Files
Moltbot/src/process/supervisor/supervisor.pty-command.test.ts
Onur cd44a0d01e fix: codex and similar processes keep dying on pty, solved by refactoring process spawning (#14257)
* exec: clean up PTY resources on timeout and exit

* cli: harden resume cleanup and watchdog stalled runs

* cli: productionize PTY and resume reliability paths

* docs: add PTY process supervision architecture plan

* docs: rewrite PTY supervision plan as pre-rewrite baseline

* docs: switch PTY supervision plan to one-go execution

* docs: add one-line root cause to PTY supervision plan

* docs: add OS contracts and test matrix to PTY supervision plan

* docs: define process-supervisor package placement and scope

* docs: tie supervisor plan to existing CI lanes

* docs: place PTY supervisor plan under src/process

* refactor(process): route exec and cli runs through supervisor

* docs(process): refresh PTY supervision plan

* wip

* fix(process): harden supervisor timeout and PTY termination

* fix(process): harden supervisor adapters env and wait handling

* ci: avoid failing formal conformance on comment permissions

* test(ui): fix cron request mock argument typing

* fix(ui): remove leftover conflict marker

* fix: supervise PTY processes (#14257) (openclaw#14257) (thanks @onutc)
2026-02-16 02:32:05 +01:00

77 lines
2.1 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const { createPtyAdapterMock } = vi.hoisted(() => ({
createPtyAdapterMock: vi.fn(),
}));
vi.mock("../../agents/shell-utils.js", () => ({
getShellConfig: () => ({ shell: "sh", args: ["-c"] }),
}));
vi.mock("./adapters/pty.js", () => ({
createPtyAdapter: (...args: unknown[]) => createPtyAdapterMock(...args),
}));
function createStubPtyAdapter() {
return {
pid: 1234,
stdin: undefined,
onStdout: (_listener: (chunk: string) => void) => {
// no-op
},
onStderr: (_listener: (chunk: string) => void) => {
// no-op
},
wait: async () => ({ code: 0, signal: null }),
kill: (_signal?: NodeJS.Signals) => {
// no-op
},
dispose: () => {
// no-op
},
};
}
describe("process supervisor PTY command contract", () => {
beforeEach(() => {
createPtyAdapterMock.mockReset();
});
it("passes PTY command verbatim to shell args", async () => {
createPtyAdapterMock.mockResolvedValue(createStubPtyAdapter());
const { createProcessSupervisor } = await import("./supervisor.js");
const supervisor = createProcessSupervisor();
const command = `printf '%s\\n' "a b" && printf '%s\\n' '$HOME'`;
const run = await supervisor.spawn({
sessionId: "s1",
backendId: "test",
mode: "pty",
ptyCommand: command,
timeoutMs: 1_000,
});
const exit = await run.wait();
expect(exit.reason).toBe("exit");
expect(createPtyAdapterMock).toHaveBeenCalledTimes(1);
const params = createPtyAdapterMock.mock.calls[0]?.[0] as { args?: string[] };
expect(params.args).toEqual(["-c", command]);
});
it("rejects empty PTY command", async () => {
createPtyAdapterMock.mockResolvedValue(createStubPtyAdapter());
const { createProcessSupervisor } = await import("./supervisor.js");
const supervisor = createProcessSupervisor();
await expect(
supervisor.spawn({
sessionId: "s1",
backendId: "test",
mode: "pty",
ptyCommand: " ",
}),
).rejects.toThrow("PTY command cannot be empty");
expect(createPtyAdapterMock).not.toHaveBeenCalled();
});
});