@abitat_reece/host-daemon 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/cli/conversation-runtime.d.ts +23 -0
  2. package/dist/cli/conversation-runtime.d.ts.map +1 -0
  3. package/dist/cli/conversation-runtime.js +73 -0
  4. package/dist/cli/conversation-runtime.js.map +1 -0
  5. package/dist/cli/daemon-connection.d.ts +14 -0
  6. package/dist/cli/daemon-connection.d.ts.map +1 -0
  7. package/dist/cli/daemon-connection.js +17 -0
  8. package/dist/cli/daemon-connection.js.map +1 -0
  9. package/dist/cli/heartbeat-loop.d.ts +2 -0
  10. package/dist/cli/heartbeat-loop.d.ts.map +1 -0
  11. package/dist/cli/heartbeat-loop.js +10 -0
  12. package/dist/cli/heartbeat-loop.js.map +1 -0
  13. package/dist/cli/index.d.ts +3 -0
  14. package/dist/cli/index.d.ts.map +1 -0
  15. package/dist/cli/index.js +371 -0
  16. package/dist/cli/index.js.map +1 -0
  17. package/dist/cli/retryable-task.d.ts +5 -0
  18. package/dist/cli/retryable-task.d.ts.map +1 -0
  19. package/dist/cli/retryable-task.js +16 -0
  20. package/dist/cli/retryable-task.js.map +1 -0
  21. package/dist/cli/start-options.d.ts +5 -0
  22. package/dist/cli/start-options.d.ts.map +1 -0
  23. package/dist/cli/start-options.js +6 -0
  24. package/dist/cli/start-options.js.map +1 -0
  25. package/dist/config/host-config.d.ts +10 -0
  26. package/dist/config/host-config.d.ts.map +1 -0
  27. package/dist/config/host-config.js +15 -0
  28. package/dist/config/host-config.js.map +1 -0
  29. package/dist/git/changeset.d.ts +9 -0
  30. package/dist/git/changeset.d.ts.map +1 -0
  31. package/dist/git/changeset.js +78 -0
  32. package/dist/git/changeset.js.map +1 -0
  33. package/dist/git/paths.d.ts +12 -0
  34. package/dist/git/paths.d.ts.map +1 -0
  35. package/dist/git/paths.js +72 -0
  36. package/dist/git/paths.js.map +1 -0
  37. package/dist/git/publish.d.ts +24 -0
  38. package/dist/git/publish.d.ts.map +1 -0
  39. package/dist/git/publish.js +23 -0
  40. package/dist/git/publish.js.map +1 -0
  41. package/dist/git/repo.d.ts +21 -0
  42. package/dist/git/repo.d.ts.map +1 -0
  43. package/dist/git/repo.js +113 -0
  44. package/dist/git/repo.js.map +1 -0
  45. package/dist/git/worktree.d.ts +25 -0
  46. package/dist/git/worktree.d.ts.map +1 -0
  47. package/dist/git/worktree.js +33 -0
  48. package/dist/git/worktree.js.map +1 -0
  49. package/dist/remote-control/manager.d.ts +46 -0
  50. package/dist/remote-control/manager.d.ts.map +1 -0
  51. package/dist/remote-control/manager.js +86 -0
  52. package/dist/remote-control/manager.js.map +1 -0
  53. package/dist/runtime/adapter.d.ts +27 -0
  54. package/dist/runtime/adapter.d.ts.map +1 -0
  55. package/dist/runtime/adapter.js +2 -0
  56. package/dist/runtime/adapter.js.map +1 -0
  57. package/dist/runtime/cli.d.ts +41 -0
  58. package/dist/runtime/cli.d.ts.map +1 -0
  59. package/dist/runtime/cli.js +540 -0
  60. package/dist/runtime/cli.js.map +1 -0
  61. package/dist/runtime/index.d.ts +10 -0
  62. package/dist/runtime/index.d.ts.map +1 -0
  63. package/dist/runtime/index.js +39 -0
  64. package/dist/runtime/index.js.map +1 -0
  65. package/dist/runtime/mock.d.ts +3 -0
  66. package/dist/runtime/mock.d.ts.map +1 -0
  67. package/dist/runtime/mock.js +34 -0
  68. package/dist/runtime/mock.js.map +1 -0
  69. package/dist/runtime/pty.d.ts +32 -0
  70. package/dist/runtime/pty.d.ts.map +1 -0
  71. package/dist/runtime/pty.js +197 -0
  72. package/dist/runtime/pty.js.map +1 -0
  73. package/dist/tools/scanner.d.ts +14 -0
  74. package/dist/tools/scanner.d.ts.map +1 -0
  75. package/dist/tools/scanner.js +53 -0
  76. package/dist/tools/scanner.js.map +1 -0
  77. package/dist/transport/api-client.d.ts +153 -0
  78. package/dist/transport/api-client.d.ts.map +1 -0
  79. package/dist/transport/api-client.js +93 -0
  80. package/dist/transport/api-client.js.map +1 -0
  81. package/package.json +41 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/remote-control/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { RunEventType, Runtime } from "@abitat_reece/shared";
2
+ export interface RuntimeEvent {
3
+ type: Extract<RunEventType, "error" | "status" | "stdout" | "stderr" | "summary">;
4
+ content: string;
5
+ }
6
+ export interface RuntimeRunInput {
7
+ worktreePath: string;
8
+ prompt: string;
9
+ model?: string;
10
+ instructions: string;
11
+ conversationId?: string;
12
+ allowedTools?: string[];
13
+ resumeSessionId?: string;
14
+ skipGitRepoCheck?: boolean;
15
+ }
16
+ export interface RuntimeAvailability {
17
+ installed: boolean;
18
+ version?: string;
19
+ path?: string;
20
+ reason?: string;
21
+ }
22
+ export interface RuntimeAdapter {
23
+ name: Runtime;
24
+ isAvailable(): Promise<RuntimeAvailability>;
25
+ run(input: RuntimeRunInput, emit: (event: RuntimeEvent) => Promise<void>): Promise<void>;
26
+ }
27
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/runtime/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAElE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC,YAAY,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC,CAAC;IAClF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC5C,GAAG,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1F"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/runtime/adapter.ts"],"names":[],"mappings":""}
@@ -0,0 +1,41 @@
1
+ import type { RuntimeAdapter, RuntimeAvailability } from "./adapter.js";
2
+ export declare const terminalShellPidMarker = "__ABITAT_SHELL_PID__";
3
+ type CliRuntimeName = "codex" | "claude";
4
+ type CliOutput = {
5
+ type: "stdout" | "stderr" | "summary";
6
+ content: string;
7
+ };
8
+ interface CliRunOptions {
9
+ cwd: string;
10
+ stdin: string;
11
+ }
12
+ export interface TerminalLaunchInput {
13
+ scriptPath: string;
14
+ logPath: string;
15
+ exitMarker: string;
16
+ }
17
+ interface TerminalCliRuntimeRunnerInput {
18
+ check?: (command: string) => Promise<RuntimeAvailability>;
19
+ launch?: (input: TerminalLaunchInput) => Promise<void>;
20
+ isProcessAlive?: (pid: number) => Promise<boolean>;
21
+ startupTimeoutMs?: number;
22
+ }
23
+ export interface CliRuntimeRunner {
24
+ interactive: boolean;
25
+ check(command: string): Promise<RuntimeAvailability>;
26
+ run(command: string, args: string[], options: CliRunOptions, emit: (event: CliOutput) => Promise<void>): Promise<number>;
27
+ }
28
+ interface CliRuntimeAdapterInput {
29
+ name: CliRuntimeName;
30
+ command: string;
31
+ runner?: CliRuntimeRunner;
32
+ }
33
+ export declare function createCodexRuntimeAdapter(): RuntimeAdapter;
34
+ export declare function createClaudeRuntimeAdapter(): RuntimeAdapter;
35
+ export declare function createCliRuntimeAdapter(input: CliRuntimeAdapterInput): RuntimeAdapter;
36
+ export declare const defaultCliRuntimeRunner: CliRuntimeRunner;
37
+ export declare function createTerminalCliRuntimeRunner(input?: TerminalCliRuntimeRunnerInput): CliRuntimeRunner;
38
+ type ExecFileAsync = (file: string, args: string[]) => Promise<unknown>;
39
+ export declare function launchITerm2Script(input: TerminalLaunchInput, execFileAsyncImpl?: ExecFileAsync): Promise<void>;
40
+ export {};
41
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/runtime/cli.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAmB,MAAM,cAAc,CAAC;AAczF,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAE7D,KAAK,cAAc,GAAG,OAAO,GAAG,QAAQ,CAAC;AACzC,KAAK,SAAS,GAAG;IAAE,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,6BAA6B;IACrC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC1D,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACrD,GAAG,CACD,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,GACxC,OAAO,CAAC,MAAM,CAAC,CAAC;CACpB;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAED,wBAAgB,yBAAyB,mBAMxC;AAED,wBAAgB,0BAA0B,mBAMzC;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,sBAAsB,GAAG,cAAc,CAyCrF;AAED,eAAO,MAAM,uBAAuB,EAAE,gBAiDrC,CAAC;AAEF,wBAAgB,8BAA8B,CAC5C,KAAK,GAAE,6BAAkC,GACxC,gBAAgB,CA4DlB;AAwBD,KAAK,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAExE,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,mBAAmB,EAC1B,iBAAiB,GAAE,aAA6B,iBAoBjD"}
@@ -0,0 +1,540 @@
1
+ import { execFile, spawn } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import { chmod, mkdtemp, readdir, readFile, stat, writeFile } from "node:fs/promises";
4
+ import { homedir, tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { promisify } from "node:util";
7
+ const execFileAsync = promisify(execFile);
8
+ const ESCAPE_CHARACTER = String.fromCharCode(27);
9
+ const BELL_CHARACTER = String.fromCharCode(7);
10
+ const CONTROL_CHARACTER_PATTERN = new RegExp(`[${String.fromCharCode(0)}-${String.fromCharCode(8)}${String.fromCharCode(11)}${String.fromCharCode(12)}${String.fromCharCode(14)}-${String.fromCharCode(31)}${String.fromCharCode(127)}]`, "g");
11
+ const ANSI_CSI_PATTERN = new RegExp(`${ESCAPE_CHARACTER}\\[[0-?]*[ -/]*[@-~]`, "g");
12
+ const ANSI_OSC_PATTERN = new RegExp(`${ESCAPE_CHARACTER}\\][^${BELL_CHARACTER}]*(?:${BELL_CHARACTER}|${ESCAPE_CHARACTER}\\\\)`, "g");
13
+ export const terminalShellPidMarker = "__ABITAT_SHELL_PID__";
14
+ export function createCodexRuntimeAdapter() {
15
+ return createCliRuntimeAdapter({
16
+ name: "codex",
17
+ command: "codex",
18
+ runner: defaultRunnerForAgentCli()
19
+ });
20
+ }
21
+ export function createClaudeRuntimeAdapter() {
22
+ return createCliRuntimeAdapter({
23
+ name: "claude",
24
+ command: "claude",
25
+ runner: defaultRunnerForAgentCli()
26
+ });
27
+ }
28
+ export function createCliRuntimeAdapter(input) {
29
+ const runner = input.runner ?? defaultCliRuntimeRunner;
30
+ return {
31
+ name: input.name,
32
+ isAvailable() {
33
+ return runner.check(input.command);
34
+ },
35
+ async run(runInput, emit) {
36
+ const availability = await runner.check(input.command);
37
+ let lastOutput = "";
38
+ if (!availability.installed) {
39
+ throw new Error(availability.reason ?? `${input.name} CLI is unavailable`);
40
+ }
41
+ await emit({ type: "status", content: `${input.name} runtime starting` });
42
+ const exitCode = await runner.run(input.command, runtimeArgs(input.name, runInput, runner.interactive), {
43
+ cwd: runInput.worktreePath,
44
+ stdin: runtimePrompt(runInput)
45
+ }, async (event) => {
46
+ if (event.content.trim().length > 0) {
47
+ lastOutput = event.content.trim();
48
+ }
49
+ await emit(event);
50
+ });
51
+ if (exitCode !== 0) {
52
+ const details = lastOutput ? `: ${lastOutput}` : "";
53
+ throw new Error(`${input.name} exited with code ${exitCode}${details}`);
54
+ }
55
+ await emit({ type: "status", content: `${input.name} runtime completed` });
56
+ }
57
+ };
58
+ }
59
+ export const defaultCliRuntimeRunner = {
60
+ interactive: false,
61
+ async check(command) {
62
+ try {
63
+ const { stdout: path } = await execFileAsync("which", [command]);
64
+ const version = await readVersion(command);
65
+ return {
66
+ installed: true,
67
+ path: path.trim(),
68
+ ...(version ? { version } : {})
69
+ };
70
+ }
71
+ catch {
72
+ return {
73
+ installed: false,
74
+ reason: `${command} CLI is unavailable on the host.`
75
+ };
76
+ }
77
+ },
78
+ run(command, args, options, emit) {
79
+ return new Promise((resolve, reject) => {
80
+ const child = spawn(command, args, {
81
+ cwd: options.cwd,
82
+ stdio: ["pipe", "pipe", "pipe"]
83
+ });
84
+ const flushStdout = createLineEmitter("stdout", emit);
85
+ const flushStderr = createLineEmitter("stderr", emit);
86
+ const queue = createAsyncQueue();
87
+ child.stdout.on("data", (chunk) => {
88
+ queue.add(() => flushStdout.write(chunk.toString("utf8")));
89
+ });
90
+ child.stderr.on("data", (chunk) => {
91
+ queue.add(() => flushStderr.write(chunk.toString("utf8")));
92
+ });
93
+ child.on("error", reject);
94
+ child.on("close", (code) => {
95
+ queue
96
+ .add(async () => {
97
+ await flushStdout.close();
98
+ await flushStderr.close();
99
+ })
100
+ .then(() => resolve(code ?? 1))
101
+ .catch(reject);
102
+ });
103
+ child.stdin.end(options.stdin);
104
+ });
105
+ }
106
+ };
107
+ export function createTerminalCliRuntimeRunner(input = {}) {
108
+ const check = input.check ?? defaultCliRuntimeRunner.check;
109
+ const launch = input.launch ?? launchITerm2Script;
110
+ const isProcessAlive = input.isProcessAlive ?? defaultIsProcessAlive;
111
+ const startupTimeoutMs = input.startupTimeoutMs ?? 15_000;
112
+ return {
113
+ interactive: true,
114
+ check,
115
+ async run(command, args, options, emit) {
116
+ const availability = await check(command);
117
+ const startedAt = Date.now();
118
+ if (!availability.installed) {
119
+ return 127;
120
+ }
121
+ const runDir = await mkdtemp(join(tmpdir(), "abitat-terminal-runtime-"));
122
+ const logPath = join(runDir, "runtime.log");
123
+ const promptPath = join(runDir, "prompt.txt");
124
+ const scriptPath = join(runDir, `${command}.command`);
125
+ const exitMarker = `__ABITAT_EXIT_${randomUUID()}__`;
126
+ const commandPath = availability.path ?? command;
127
+ await writeFile(promptPath, options.stdin, "utf8");
128
+ await writeFile(logPath, "", "utf8");
129
+ await writeFile(scriptPath, terminalScript({
130
+ args,
131
+ commandName: command,
132
+ commandPath,
133
+ cwd: options.cwd,
134
+ exitMarker,
135
+ logPath,
136
+ promptPath
137
+ }), "utf8");
138
+ await chmod(scriptPath, 0o700);
139
+ const watch = watchTerminalLog(logPath, exitMarker, emit, {
140
+ isProcessAlive,
141
+ startupTimeoutMs
142
+ });
143
+ await launch({ scriptPath, logPath, exitMarker });
144
+ const { exitCode, sawSessionId } = await watch;
145
+ const sessionId = command === "codex" && isInteractiveCodexArgs(args)
146
+ ? await findLatestCodexSessionId(options.cwd, startedAt)
147
+ : null;
148
+ if (sessionId && !sawSessionId) {
149
+ await emit({ type: "stdout", content: `session id: ${sessionId}` });
150
+ }
151
+ return exitCode;
152
+ }
153
+ };
154
+ }
155
+ function defaultRunnerForAgentCli() {
156
+ if (process.env.ABITAT_CLI_RUNTIME_MODE === "inline" || process.platform !== "darwin") {
157
+ console.log("runtime runner: inline (hidden)");
158
+ return defaultCliRuntimeRunner;
159
+ }
160
+ console.log("runtime runner: terminal (visible window)");
161
+ return createTerminalCliRuntimeRunner();
162
+ }
163
+ function createAsyncQueue() {
164
+ let current = Promise.resolve();
165
+ return {
166
+ add(task) {
167
+ const next = current.then(task, task);
168
+ current = next.catch(() => undefined);
169
+ return next;
170
+ }
171
+ };
172
+ }
173
+ export async function launchITerm2Script(input, execFileAsyncImpl = execFileAsync) {
174
+ const command = shellQuote(input.scriptPath);
175
+ const script = [
176
+ 'tell application "iTerm2"',
177
+ "activate",
178
+ "if (count of windows) = 0 then",
179
+ "set newWindow to create window with default profile",
180
+ "set targetSession to current session of newWindow",
181
+ "else",
182
+ "tell current window",
183
+ "set newTab to create tab with default profile",
184
+ "set targetSession to current session of newTab",
185
+ "end tell",
186
+ "end if",
187
+ `tell targetSession to write text ${appleScriptString(command)}`,
188
+ "end tell"
189
+ ].join("\n");
190
+ await execFileAsyncImpl("osascript", ["-e", script]);
191
+ }
192
+ function terminalScript(input) {
193
+ const usesPrompt = usesPromptFile(input.commandName, input.args);
194
+ const command = terminalCommand(input.commandName, input.commandPath, input.args, input.promptPath);
195
+ const holdOpen = process.env.ABITAT_TERMINAL_HOLD_OPEN !== "0";
196
+ const capturesOutput = capturesTerminalOutput(input.commandName, input.args);
197
+ const lines = [
198
+ "#!/bin/bash",
199
+ "set +e",
200
+ `cd ${shellQuote(input.cwd)}`,
201
+ `echo "${terminalShellPidMarker}:$$" >> ${shellQuote(input.logPath)}`,
202
+ terminalLogLine("Abitat Workspace terminal runtime", input.logPath, capturesOutput),
203
+ terminalLogLine(`cwd: ${input.cwd}`, input.logPath, capturesOutput),
204
+ terminalLogLine(`command: ${command}`, input.logPath, capturesOutput),
205
+ capturesOutput ? `echo ""` : "clear",
206
+ ...(usesPrompt
207
+ ? [`echo "--- Task ---"`, `cat ${shellQuote(input.promptPath)}`, `echo ""`]
208
+ : []),
209
+ ...(capturesOutput ? [`echo "--- Starting Codex CLI ---"`, `echo ""`] : []),
210
+ capturesOutput
211
+ ? // Use `script` to give noninteractive CLI runs a real pseudo-terminal
212
+ // while capturing output for the daemon to stream to the web UI.
213
+ `script -q -a ${shellQuote(input.logPath)} /bin/bash -lc ${shellQuote(command)}`
214
+ : command,
215
+ "status=$?",
216
+ `echo "${input.exitMarker}:$status" >> ${shellQuote(input.logPath)}`,
217
+ 'echo ""',
218
+ 'echo "--- Agent finished (exit $status) ---"',
219
+ ...(holdOpen ? ['read -p "Press enter to close this window..."'] : []),
220
+ "exit $status",
221
+ ""
222
+ ];
223
+ return lines.join("\n");
224
+ }
225
+ function terminalLogLine(line, logPath, echoToTerminal) {
226
+ const escaped = escapeDoubleQuoted(line);
227
+ const log = shellQuote(logPath);
228
+ return echoToTerminal ? `echo "${escaped}" | tee -a ${log}` : `echo "${escaped}" >> ${log}`;
229
+ }
230
+ function terminalCommand(commandName, commandPath, args, promptPath) {
231
+ if (commandName === "codex" && args[0] === "exec") {
232
+ const promptArgs = args.at(-1) === "-" ? args.slice(0, -1) : args;
233
+ return [
234
+ shellQuote(commandPath),
235
+ ...promptArgs.map(shellQuote),
236
+ `"$(cat ${shellQuote(promptPath)})"`
237
+ ].join(" ");
238
+ }
239
+ if (commandName === "codex") {
240
+ return [commandPath, ...args].map(shellQuote).join(" ");
241
+ }
242
+ return `${[commandPath, ...args].map(shellQuote).join(" ")} < ${shellQuote(promptPath)}`;
243
+ }
244
+ function usesPromptFile(commandName, args) {
245
+ return commandName !== "codex" || args[0] === "exec";
246
+ }
247
+ function capturesTerminalOutput(commandName, args) {
248
+ return commandName !== "codex" || args[0] === "exec";
249
+ }
250
+ async function watchTerminalLog(logPath, exitMarker, emit, options) {
251
+ let offset = 0;
252
+ let buffered = "";
253
+ let shellPid = null;
254
+ let sawSessionId = false;
255
+ const startedAt = Date.now();
256
+ const emittedLines = [];
257
+ while (true) {
258
+ const text = await readFile(logPath, "utf8").catch(() => "");
259
+ const chunk = text.slice(offset);
260
+ offset = text.length;
261
+ if (chunk) {
262
+ buffered += chunk;
263
+ const lines = buffered.split(/\r?\n/);
264
+ buffered = lines.pop() ?? "";
265
+ for (const line of lines) {
266
+ if (line.startsWith(`${terminalShellPidMarker}:`)) {
267
+ shellPid = Number(line.slice(terminalShellPidMarker.length + 1)) || null;
268
+ continue;
269
+ }
270
+ if (line.startsWith(`${exitMarker}:`)) {
271
+ const summary = summarizeTerminalLines(emittedLines);
272
+ if (summary) {
273
+ await emit({ type: "summary", content: summary });
274
+ }
275
+ return {
276
+ exitCode: Number(line.slice(exitMarker.length + 1)) || 0,
277
+ sawSessionId
278
+ };
279
+ }
280
+ if (line.length > 0) {
281
+ emittedLines.push(line);
282
+ await emit({ type: "stdout", content: line });
283
+ const sessionId = parseTerminalSessionId(line);
284
+ if (sessionId) {
285
+ sawSessionId = true;
286
+ if (!/^session id:/i.test(cleanTerminalLine(line))) {
287
+ await emit({ type: "stdout", content: `session id: ${sessionId}` });
288
+ }
289
+ }
290
+ }
291
+ }
292
+ }
293
+ if (shellPid !== null && !(await options.isProcessAlive(shellPid))) {
294
+ const summary = summarizeTerminalLines(emittedLines);
295
+ if (summary) {
296
+ await emit({ type: "summary", content: summary });
297
+ }
298
+ return {
299
+ exitCode: 130,
300
+ sawSessionId
301
+ };
302
+ }
303
+ if (shellPid === null && Date.now() - startedAt > options.startupTimeoutMs) {
304
+ const summary = summarizeTerminalLines(emittedLines);
305
+ if (summary) {
306
+ await emit({ type: "summary", content: summary });
307
+ }
308
+ return {
309
+ exitCode: 124,
310
+ sawSessionId
311
+ };
312
+ }
313
+ await new Promise((resolve) => setTimeout(resolve, 250));
314
+ }
315
+ }
316
+ async function readVersion(command) {
317
+ try {
318
+ const { stdout } = await execFileAsync(command, ["--version"]);
319
+ return stdout.trim().split("\n")[0] || undefined;
320
+ }
321
+ catch {
322
+ return undefined;
323
+ }
324
+ }
325
+ function isInteractiveCodexArgs(args) {
326
+ return args[0] !== "exec";
327
+ }
328
+ async function findLatestCodexSessionId(cwd, startedAt) {
329
+ const sessionsRoot = join(process.env.CODEX_HOME ?? join(homedir(), ".codex"), "sessions");
330
+ const files = await listFiles(sessionsRoot).catch(() => []);
331
+ let latest = null;
332
+ for (const file of files) {
333
+ if (!file.endsWith(".jsonl")) {
334
+ continue;
335
+ }
336
+ const details = await stat(file).catch(() => null);
337
+ if (!details || details.mtimeMs < startedAt - 60_000) {
338
+ continue;
339
+ }
340
+ const firstLine = (await readFile(file, "utf8").catch(() => "")).split(/\r?\n/)[0];
341
+ const session = parseCodexSessionMeta(firstLine, cwd);
342
+ if (session && (!latest || details.mtimeMs > latest.mtimeMs)) {
343
+ latest = {
344
+ id: session,
345
+ mtimeMs: details.mtimeMs
346
+ };
347
+ }
348
+ }
349
+ return latest?.id ?? null;
350
+ }
351
+ async function listFiles(root) {
352
+ const entries = await readdir(root, { withFileTypes: true });
353
+ const files = await Promise.all(entries.map(async (entry) => {
354
+ const path = join(root, entry.name);
355
+ if (entry.isDirectory()) {
356
+ return listFiles(path);
357
+ }
358
+ return entry.isFile() ? [path] : [];
359
+ }));
360
+ return files.flat();
361
+ }
362
+ function parseCodexSessionMeta(line, cwd) {
363
+ try {
364
+ const parsed = JSON.parse(line);
365
+ if (parsed.type !== "session_meta") {
366
+ return null;
367
+ }
368
+ if (parsed.payload?.cwd !== cwd || typeof parsed.payload.id !== "string") {
369
+ return null;
370
+ }
371
+ return parsed.payload.id;
372
+ }
373
+ catch {
374
+ return null;
375
+ }
376
+ }
377
+ function summarizeTerminalLines(lines) {
378
+ const recent = lines.map(cleanTerminalLine).map(cleanSummaryLine).filter(isSummaryLine).slice(-6);
379
+ if (recent.length === 0) {
380
+ return "Codex terminal session ended.";
381
+ }
382
+ return [
383
+ "Codex terminal session ended.",
384
+ "",
385
+ "Recent activity:",
386
+ ...recent.map((line) => `- ${line}`)
387
+ ].join("\n");
388
+ }
389
+ function cleanTerminalLine(line) {
390
+ return line
391
+ .replace(ANSI_CSI_PATTERN, "")
392
+ .replace(ANSI_OSC_PATTERN, "")
393
+ .replace(CONTROL_CHARACTER_PATTERN, "")
394
+ .replace(/\s+/g, " ")
395
+ .trim();
396
+ }
397
+ function parseTerminalSessionId(line) {
398
+ const cleaned = cleanTerminalLine(line);
399
+ const direct = /^session id:\s*(\S+)/i.exec(cleaned);
400
+ if (direct) {
401
+ return direct[1];
402
+ }
403
+ const resume = /\bcodex\s+resume\s+(\S+)/i.exec(cleaned);
404
+ return resume?.[1]?.replace(/[.,;:]+$/u, "");
405
+ }
406
+ function cleanSummaryLine(line) {
407
+ let cleaned = line
408
+ .replace(/^[\u2022\-\s]+/u, "")
409
+ .replace(/^[\u2713\u2714]\s*/u, "")
410
+ .trim();
411
+ for (const marker of [" › ", " Token usage:"]) {
412
+ const markerIndex = cleaned.indexOf(marker);
413
+ if (markerIndex > 0) {
414
+ cleaned = cleaned.slice(0, markerIndex).trim();
415
+ }
416
+ }
417
+ return cleaned;
418
+ }
419
+ function isSummaryLine(line) {
420
+ if (!line) {
421
+ return false;
422
+ }
423
+ if (/^session id:/i.test(line)) {
424
+ return false;
425
+ }
426
+ if (/to continue this session/i.test(line)) {
427
+ return false;
428
+ }
429
+ if (["Abitat Workspace terminal runtime", "--- Starting Codex CLI ---", "--- Task ---"].some((prefix) => line.startsWith(prefix)) ||
430
+ line.startsWith("cwd:") ||
431
+ line.startsWith("command:")) {
432
+ return false;
433
+ }
434
+ if (/^[\u256d\u2570\u2502\u2500\s]+$/u.test(line) || /^\d+\s+[+-]?/u.test(line)) {
435
+ return false;
436
+ }
437
+ return /^(?:added|changed|completed|created|deleted|edited|fixed|generated|implemented|modified|updated|wrote)\b/iu.test(line);
438
+ }
439
+ function shellQuote(value) {
440
+ return `'${value.replace(/'/g, "'\\''")}'`;
441
+ }
442
+ function appleScriptString(value) {
443
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
444
+ }
445
+ function escapeDoubleQuoted(value) {
446
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\$/g, "\\$");
447
+ }
448
+ async function defaultIsProcessAlive(pid) {
449
+ try {
450
+ process.kill(pid, 0);
451
+ return true;
452
+ }
453
+ catch (error) {
454
+ return !(typeof error === "object" &&
455
+ error !== null &&
456
+ "code" in error &&
457
+ error.code === "ESRCH");
458
+ }
459
+ }
460
+ function runtimeArgs(name, input, interactive) {
461
+ if (name === "codex") {
462
+ const localFolderArgs = input.skipGitRepoCheck ? ["--skip-git-repo-check"] : [];
463
+ const vpnStableArgs = ["--disable", "plugins", "--disable", "general_analytics"];
464
+ if (interactive) {
465
+ const interactiveArgs = [...vpnStableArgs, ...codexModelArgs(input.model), "--full-auto"];
466
+ if (input.resumeSessionId) {
467
+ return [...interactiveArgs, "resume", input.resumeSessionId];
468
+ }
469
+ return interactiveArgs;
470
+ }
471
+ if (input.resumeSessionId) {
472
+ return [
473
+ "exec",
474
+ "resume",
475
+ ...localFolderArgs,
476
+ ...vpnStableArgs,
477
+ ...codexModelArgs(input.model),
478
+ "--full-auto",
479
+ input.resumeSessionId,
480
+ "-"
481
+ ];
482
+ }
483
+ return [
484
+ "exec",
485
+ ...localFolderArgs,
486
+ ...vpnStableArgs,
487
+ ...codexModelArgs(input.model),
488
+ "--full-auto"
489
+ ];
490
+ }
491
+ if (interactive) {
492
+ return [];
493
+ }
494
+ return [...plainModelArgs(input.model), "--print"];
495
+ }
496
+ function codexModelArgs(model) {
497
+ const trimmed = model?.trim();
498
+ return trimmed ? ["--model", resolveCodexModel(trimmed)] : [];
499
+ }
500
+ function plainModelArgs(model) {
501
+ const trimmed = model?.trim();
502
+ return trimmed ? ["--model", trimmed] : [];
503
+ }
504
+ function resolveCodexModel(model) {
505
+ return /^\d+(?:\.\d+)+$/.test(model) ? `gpt-${model}` : model;
506
+ }
507
+ function runtimePrompt(input) {
508
+ return [
509
+ "# Instructions",
510
+ input.instructions,
511
+ "",
512
+ "# Task",
513
+ input.prompt,
514
+ "",
515
+ "# Allowed Tools",
516
+ (input.allowedTools ?? []).join(", ")
517
+ ].join("\n");
518
+ }
519
+ function createLineEmitter(type, emit) {
520
+ let buffered = "";
521
+ return {
522
+ async write(text) {
523
+ buffered += text;
524
+ const lines = buffered.split(/\r?\n/);
525
+ buffered = lines.pop() ?? "";
526
+ for (const line of lines) {
527
+ if (line.length > 0) {
528
+ await emit({ type, content: line });
529
+ }
530
+ }
531
+ },
532
+ async close() {
533
+ if (buffered.length > 0) {
534
+ await emit({ type, content: buffered });
535
+ buffered = "";
536
+ }
537
+ }
538
+ };
539
+ }
540
+ //# sourceMappingURL=cli.js.map