@phamvuhoang/otto-core 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 (103) hide show
  1. package/README.md +53 -0
  2. package/dist/branch.d.ts +61 -0
  3. package/dist/branch.d.ts.map +1 -0
  4. package/dist/branch.js +215 -0
  5. package/dist/branch.js.map +1 -0
  6. package/dist/cli-help.d.ts +68 -0
  7. package/dist/cli-help.d.ts.map +1 -0
  8. package/dist/cli-help.js +365 -0
  9. package/dist/cli-help.js.map +1 -0
  10. package/dist/detach.d.ts +36 -0
  11. package/dist/detach.d.ts.map +1 -0
  12. package/dist/detach.js +52 -0
  13. package/dist/detach.js.map +1 -0
  14. package/dist/gh-main.d.ts +5 -0
  15. package/dist/gh-main.d.ts.map +1 -0
  16. package/dist/gh-main.js +16 -0
  17. package/dist/gh-main.js.map +1 -0
  18. package/dist/git.d.ts +14 -0
  19. package/dist/git.d.ts.map +1 -0
  20. package/dist/git.js +50 -0
  21. package/dist/git.js.map +1 -0
  22. package/dist/index.d.ts +8 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +8 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/keepalive.d.ts +24 -0
  27. package/dist/keepalive.d.ts.map +1 -0
  28. package/dist/keepalive.js +138 -0
  29. package/dist/keepalive.js.map +1 -0
  30. package/dist/loop.d.ts +42 -0
  31. package/dist/loop.d.ts.map +1 -0
  32. package/dist/loop.js +238 -0
  33. package/dist/loop.js.map +1 -0
  34. package/dist/main.d.ts +5 -0
  35. package/dist/main.d.ts.map +1 -0
  36. package/dist/main.js +16 -0
  37. package/dist/main.js.map +1 -0
  38. package/dist/notify.d.ts +28 -0
  39. package/dist/notify.d.ts.map +1 -0
  40. package/dist/notify.js +119 -0
  41. package/dist/notify.js.map +1 -0
  42. package/dist/pacing.d.ts +8 -0
  43. package/dist/pacing.d.ts.map +1 -0
  44. package/dist/pacing.js +33 -0
  45. package/dist/pacing.js.map +1 -0
  46. package/dist/panel.d.ts +24 -0
  47. package/dist/panel.d.ts.map +1 -0
  48. package/dist/panel.js +202 -0
  49. package/dist/panel.js.map +1 -0
  50. package/dist/rate-limit.d.ts +16 -0
  51. package/dist/rate-limit.d.ts.map +1 -0
  52. package/dist/rate-limit.js +35 -0
  53. package/dist/rate-limit.js.map +1 -0
  54. package/dist/render.d.ts +8 -0
  55. package/dist/render.d.ts.map +1 -0
  56. package/dist/render.js +130 -0
  57. package/dist/render.js.map +1 -0
  58. package/dist/retry.d.ts +17 -0
  59. package/dist/retry.d.ts.map +1 -0
  60. package/dist/retry.js +34 -0
  61. package/dist/retry.js.map +1 -0
  62. package/dist/run-bin.d.ts +35 -0
  63. package/dist/run-bin.d.ts.map +1 -0
  64. package/dist/run-bin.js +241 -0
  65. package/dist/run-bin.js.map +1 -0
  66. package/dist/runner.d.ts +55 -0
  67. package/dist/runner.d.ts.map +1 -0
  68. package/dist/runner.js +297 -0
  69. package/dist/runner.js.map +1 -0
  70. package/dist/stage-exec.d.ts +16 -0
  71. package/dist/stage-exec.d.ts.map +1 -0
  72. package/dist/stage-exec.js +35 -0
  73. package/dist/stage-exec.js.map +1 -0
  74. package/dist/stages.d.ts +38 -0
  75. package/dist/stages.d.ts.map +1 -0
  76. package/dist/stages.js +38 -0
  77. package/dist/stages.js.map +1 -0
  78. package/dist/state.d.ts +25 -0
  79. package/dist/state.d.ts.map +1 -0
  80. package/dist/state.js +30 -0
  81. package/dist/state.js.map +1 -0
  82. package/dist/stream-render.d.ts +68 -0
  83. package/dist/stream-render.d.ts.map +1 -0
  84. package/dist/stream-render.js +162 -0
  85. package/dist/stream-render.js.map +1 -0
  86. package/dist/watch.d.ts +22 -0
  87. package/dist/watch.d.ts.map +1 -0
  88. package/dist/watch.js +93 -0
  89. package/dist/watch.js.map +1 -0
  90. package/package.json +67 -0
  91. package/templates/afk.md +21 -0
  92. package/templates/apply-review.md +71 -0
  93. package/templates/ghafk-issue.md +29 -0
  94. package/templates/ghafk.md +29 -0
  95. package/templates/ghprompt-workflow.md +83 -0
  96. package/templates/ghprompt.md +39 -0
  97. package/templates/prompt.md +97 -0
  98. package/templates/review-lens.md +41 -0
  99. package/templates/review-synth.md +29 -0
  100. package/templates/review-verify.md +52 -0
  101. package/templates/review.md +62 -0
  102. package/templates/superpowers.md +70 -0
  103. package/templates/verify.md +74 -0
@@ -0,0 +1,241 @@
1
+ import { existsSync, readFileSync, appendFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { dirtyTreeWarning, ensureTmpIgnored, resolveBranch, } from "./branch.js";
5
+ import { parseFlags, parseDurationMs, printConfig, printHelp, printVersion, } from "./cli-help.js";
6
+ import { detachAndExit } from "./detach.js";
7
+ import { runLoop } from "./loop.js";
8
+ /**
9
+ * Ensure .otto/state.json is listed in the workspace .gitignore.
10
+ * No-op when the workspace has no .git directory (not a git repo).
11
+ * Kept separate from branch.ts's ensureTmpIgnored: that targets the parent
12
+ * workspaceDir (.otto-tmp/), while state.json lives in the effective workspace
13
+ * (the worktree, in worktree mode) where the loop writes it.
14
+ */
15
+ function ensureStateGitignored(workspaceDir) {
16
+ if (!existsSync(join(workspaceDir, ".git")))
17
+ return;
18
+ const gitignorePath = join(workspaceDir, ".gitignore");
19
+ const entry = ".otto/state.json";
20
+ const existing = existsSync(gitignorePath)
21
+ ? readFileSync(gitignorePath, "utf8")
22
+ : "";
23
+ const alreadyPresent = existing
24
+ .split("\n")
25
+ .some((line) => line.trim() === entry);
26
+ if (!alreadyPresent) {
27
+ const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
28
+ appendFileSync(gitignorePath, `${prefix}${entry}\n`, "utf8");
29
+ }
30
+ }
31
+ /**
32
+ * Shared entry for the AFK bins: parse flags, handle --version/--help/--print-config,
33
+ * resolve the workspace / package dirs, validate the positional args,
34
+ * optionally fork into the background (--detach), then drive runLoop.
35
+ */
36
+ export async function runBin(argv, cfg) {
37
+ const flags = parseFlags(argv);
38
+ if (flags.version) {
39
+ printVersion(cfg.bin, cfg.cliVersion);
40
+ return;
41
+ }
42
+ if (flags.help) {
43
+ printHelp(cfg.bin, cfg.usage, cfg.desc);
44
+ return;
45
+ }
46
+ // run-bin.js ships in the same dist/ dir as the bin entrypoints, so ".." is
47
+ // the installed @phamvuhoang/otto-core package dir (which holds templates/).
48
+ const here = dirname(fileURLToPath(import.meta.url));
49
+ const packageDir = resolve(here, "..");
50
+ const workspaceDir = resolve(process.env.OTTO_WORKSPACE ?? process.cwd());
51
+ const envMaxWait = process.env.OTTO_MAX_WAIT?.trim();
52
+ const maxWaitMs = flags.maxWaitMs ?? (envMaxWait ? parseDurationMs(envMaxWait) : undefined);
53
+ const envBranch = process.env.OTTO_BRANCH?.trim();
54
+ const branchStrategyArg = flags.branch ??
55
+ (envBranch === "current" ||
56
+ envBranch === "branch" ||
57
+ envBranch === "worktree"
58
+ ? envBranch
59
+ : undefined);
60
+ const branchPrefixArg = flags.branchPrefix ??
61
+ (process.env.OTTO_BRANCH_PREFIX?.trim() || undefined);
62
+ const detachLogPath = flags.detach
63
+ ? (flags.log ??
64
+ join(workspaceDir, ".otto-tmp", "logs", `detached-${process.pid}.log`))
65
+ : undefined;
66
+ const DEFAULT_LENSES = ["correctness", "security", "tests"];
67
+ const envLenses = (process.env.OTTO_REVIEW_LENSES ?? "")
68
+ .split(",")
69
+ .map((s) => s.trim())
70
+ .filter(Boolean);
71
+ const reviewLenses = envLenses.length > 0
72
+ ? envLenses
73
+ : flags.reviewPanel
74
+ ? DEFAULT_LENSES
75
+ : undefined;
76
+ // Resolve the run mode before the --print-config early-return so it reflects
77
+ // the selected mode. Depends only on flags, not on the guards below.
78
+ const runMode = flags.verify
79
+ ? "verify"
80
+ : flags.applyReview != null
81
+ ? "review"
82
+ : cfg.mode;
83
+ if (flags.printConfig) {
84
+ printConfig(cfg.bin, workspaceDir, packageDir, {
85
+ cliVersion: cfg.cliVersion,
86
+ mode: runMode,
87
+ noKeepAlive: flags.noKeepAlive,
88
+ maxRetries: flags.maxRetries,
89
+ detach: flags.detach,
90
+ detachLogPath,
91
+ notify: flags.notify,
92
+ budget: flags.budget,
93
+ cooldownMs: flags.cooldownMs,
94
+ reviewLenses: reviewLenses ?? [],
95
+ watch: flags.watch,
96
+ watchIntervalSec: flags.watchIntervalSec,
97
+ issue: flags.issue,
98
+ maxWaitMs,
99
+ branchStrategy: branchStrategyArg,
100
+ branchPrefix: branchPrefixArg,
101
+ });
102
+ return;
103
+ }
104
+ if (flags.issue != null && !cfg.issueStage) {
105
+ console.error("--issue is only supported by otto-ghafk");
106
+ process.exit(1);
107
+ }
108
+ const modeCount = (flags.issue != null ? 1 : 0) +
109
+ (flags.verify ? 1 : 0) +
110
+ (flags.applyReview != null ? 1 : 0) +
111
+ (flags.watch ? 1 : 0);
112
+ if (modeCount > 1) {
113
+ console.error("--issue, --verify, --apply-review, and --watch are mutually exclusive");
114
+ process.exit(1);
115
+ }
116
+ if (flags.verify && !cfg.verifyStage) {
117
+ console.error("--verify is only supported by otto-afk");
118
+ process.exit(1);
119
+ }
120
+ if (flags.applyReview != null && !cfg.applyReviewStage) {
121
+ console.error("--apply-review is only supported by otto-afk");
122
+ process.exit(1);
123
+ }
124
+ const inputs = flags.issue != null
125
+ ? String(flags.issue)
126
+ : flags.applyReview != null
127
+ ? flags.applyReview
128
+ : cfg.takesInputArg
129
+ ? flags.rest[0]
130
+ : "";
131
+ // --verify is one-shot (iterations forced to 1 below); --apply-review takes the
132
+ // doc as its flag value, so the iterations count is the first remaining positional.
133
+ const iterationsArg = flags.applyReview != null
134
+ ? flags.rest[0]
135
+ : cfg.takesInputArg
136
+ ? flags.rest[1]
137
+ : flags.rest[0];
138
+ if (flags.verify && (!cfg.takesInputArg || !inputs)) {
139
+ console.error(`Usage: ${cfg.bin} --verify "<plan-and-prd>"`);
140
+ process.exit(1);
141
+ }
142
+ if (!flags.verify && ((cfg.takesInputArg && !inputs) || !iterationsArg)) {
143
+ console.error(`Usage: ${cfg.bin} ${cfg.usage}`);
144
+ console.error(` ${cfg.bin} --help`);
145
+ process.exit(1);
146
+ }
147
+ // --verify is one-shot regardless of any positional count.
148
+ if (flags.verify && iterationsArg) {
149
+ console.error("--verify is one-shot; ignoring the iterations argument");
150
+ }
151
+ const iterations = flags.verify ? 1 : Number.parseInt(iterationsArg, 10);
152
+ if (!flags.verify && (!Number.isFinite(iterations) || iterations < 1)) {
153
+ console.error(`Invalid iterations: ${iterationsArg}`);
154
+ process.exit(1);
155
+ }
156
+ if (flags.issue != null) {
157
+ if (flags.watch) {
158
+ console.error("--issue cannot be combined with --watch");
159
+ process.exit(1);
160
+ }
161
+ // Validated positive integer (parseIssueRef) — safe for the static
162
+ // `gh issue view "$OTTO_ISSUE"` command in ghafk-issue.md. See render.ts.
163
+ process.env.OTTO_ISSUE = String(flags.issue);
164
+ }
165
+ const stages = flags.verify
166
+ ? [cfg.verifyStage]
167
+ : flags.applyReview != null
168
+ ? [cfg.applyReviewStage, ...cfg.stages.slice(1)]
169
+ : flags.issue != null
170
+ ? [cfg.issueStage, ...cfg.stages.slice(1)]
171
+ : cfg.stages;
172
+ if (flags.detach && detachLogPath) {
173
+ detachAndExit({
174
+ logPath: detachLogPath,
175
+ argv,
176
+ binEntry: process.argv[1],
177
+ });
178
+ }
179
+ const resolved = await resolveBranch({
180
+ workspaceDir,
181
+ inputs,
182
+ isTTY: Boolean(process.stdout.isTTY),
183
+ flagStrategy: branchStrategyArg,
184
+ flagPrefix: branchPrefixArg,
185
+ });
186
+ process.stderr.write(`${resolved.summaryLine}\n`);
187
+ // Evaluate the dirty-tree warning against the user's tree BEFORE we mutate the
188
+ // workspace's .gitignore below — otherwise Otto's own .otto-tmp/ edit would
189
+ // make a tracked .gitignore "dirty" and fire a spurious warning on first run.
190
+ const dirtyWarn = dirtyTreeWarning(workspaceDir, resolved.strategy);
191
+ if (dirtyWarn)
192
+ process.stderr.write(`⚠ ${dirtyWarn}\n`);
193
+ ensureTmpIgnored(workspaceDir);
194
+ const effectiveWorkspaceDir = resolved.effectiveWorkspaceDir;
195
+ // state.json is written by the loop into effectiveWorkspaceDir (the worktree in
196
+ // worktree mode), which differs from the parent workspaceDir that
197
+ // ensureTmpIgnored targets — so this stays a separate call.
198
+ ensureStateGitignored(effectiveWorkspaceDir);
199
+ if (flags.watch) {
200
+ if (!cfg.supportsWatch) {
201
+ console.error("--watch is only supported by otto-ghafk");
202
+ process.exit(1);
203
+ }
204
+ const { runWatch } = await import("./watch.js");
205
+ await runWatch({
206
+ stages,
207
+ iterations,
208
+ workspaceDir: effectiveWorkspaceDir,
209
+ packageDir,
210
+ watchIntervalSec: flags.watchIntervalSec ?? 300,
211
+ watchLabel: process.env.OTTO_WATCH_LABEL?.trim() || "otto",
212
+ budgetUsd: flags.budget,
213
+ cooldownMs: flags.cooldownMs,
214
+ maxRetries: flags.maxRetries,
215
+ reviewLenses,
216
+ notify: flags.notify,
217
+ bin: cfg.bin,
218
+ cliVersion: cfg.cliVersion,
219
+ });
220
+ return;
221
+ }
222
+ await runLoop({
223
+ stages,
224
+ inputs: inputs ?? "",
225
+ iterations,
226
+ workspaceDir: effectiveWorkspaceDir,
227
+ packageDir,
228
+ noKeepAlive: flags.noKeepAlive,
229
+ maxRetries: flags.maxRetries,
230
+ notify: flags.notify,
231
+ bin: cfg.bin,
232
+ cliVersion: cfg.cliVersion,
233
+ budgetUsd: flags.budget,
234
+ cooldownMs: flags.cooldownMs,
235
+ reviewLenses,
236
+ mode: runMode,
237
+ maxWaitMs,
238
+ fresh: flags.fresh,
239
+ });
240
+ }
241
+ //# sourceMappingURL=run-bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-bin.js","sourceRoot":"","sources":["../src/run-bin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { Stage } from "./stages.js";
2
+ export type RunStageOptions = {
3
+ signal?: AbortSignal;
4
+ };
5
+ export type StageResult = {
6
+ result: string;
7
+ costUsd: number;
8
+ isError: boolean;
9
+ apiErrorStatus: string | null;
10
+ };
11
+ /** Pure extraction of the fields Otto tracks from a stream-json `result` event. */
12
+ export declare function resultFromEvent(ev: unknown): StageResult;
13
+ /**
14
+ * Parse `OTTO_RESULT_GRACE_MS`. Returns the configured millisecond budget,
15
+ * `0` to disable the timer entirely, or `defaultMs` for any invalid input
16
+ * (unset, empty, non-finite, negative).
17
+ */
18
+ export declare function parseGraceMs(raw: string | undefined, defaultMs?: number): number;
19
+ /**
20
+ * Resolve `OTTO_MODEL` into a `claude` argv fragment. Returns
21
+ * `["--model", trimmed]` for a non-empty value, or `[]` for unset / empty /
22
+ * whitespace-only input. Pass-through: otto never validates the model spec,
23
+ * the `claude` CLI owns that.
24
+ */
25
+ export declare function resolveModelArgs(raw: string | undefined): string[];
26
+ export type Runner = "sandbox" | "host";
27
+ /** `OTTO_RUNNER=host` → bare host run; anything else (incl. unset) → sandbox. */
28
+ export declare function resolveRunner(raw: string | undefined): Runner;
29
+ /** Parse `OTTO_SANDBOX_NET` into a domain allowlist. Empty = unrestricted. */
30
+ export declare function resolveSandboxNet(raw: string | undefined): string[];
31
+ /**
32
+ * Claude Code native-sandbox settings: confine writes to the workspace and run
33
+ * the Go-TLS CLIs unsandboxed. When `allowedDomains` is non-empty, also restrict
34
+ * network egress to that list; otherwise leave network unrestricted (filesystem
35
+ * is the blast-radius control; network commands fall back to the bypass-approved
36
+ * escape hatch).
37
+ */
38
+ export declare function buildSandboxSettings(workspaceDir: string, allowedDomains: string[]): Record<string, unknown>;
39
+ export declare function stageLogPath(workspaceDir: string, iteration: number, stageName: string): string;
40
+ /**
41
+ * Build the `claude` argv. Extracted as a pure helper so callers can unit-test
42
+ * the argv without spawning a process.
43
+ *
44
+ * @param stage - The stage configuration (name, permissionMode, etc.).
45
+ * @param promptRelPath - The workspace-relative path to the rendered prompt file.
46
+ * @param modelArgs - The `["--model", "<spec>"]` fragment from {@link resolveModelArgs},
47
+ * or `[]` when `OTTO_MODEL` is unset.
48
+ * @param settingsPath - Optional absolute path to a transient settings JSON file
49
+ * (written by `runStage` when `OTTO_RUNNER=sandbox`).
50
+ * @returns The full argv starting with `"claude"` and ending with the prompt
51
+ * instruction string.
52
+ */
53
+ export declare function buildClaudeArgs(stage: Stage, promptRelPath: string, modelArgs: string[], settingsPath?: string): string[];
54
+ export declare function runStage(stage: Stage, renderedPrompt: string, workspaceDir: string, iteration: number, spillHostDir?: string, logPathOverride?: string, options?: RunStageOptions): Promise<StageResult>;
55
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAazC,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,EAAE,EAAE,OAAO,GAAG,WAAW,CASxD;AAKD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,SAAS,GAAE,MAAgC,GAC1C,MAAM,CAQR;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAKlE;AAED,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAExC,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE7D;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAMnE;AAMD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,EAAE,GACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUzB;AAQD,wBAAgB,YAAY,CAC1B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,MAAM,CAQR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,KAAK,EACZ,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EAAE,EACnB,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,EAAE,CAmBV;AAED,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,KAAK,EACZ,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,MAAM,EACrB,eAAe,CAAC,EAAE,MAAM,EACxB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,WAAW,CAAC,CA0CtB"}
package/dist/runner.js ADDED
@@ -0,0 +1,297 @@
1
+ import { spawn } from "node:child_process";
2
+ import { appendFileSync, closeSync, mkdirSync, openSync, rmSync, writeFileSync, } from "node:fs";
3
+ import { createInterface } from "node:readline";
4
+ import { join, posix } from "node:path";
5
+ import { dim, renderEvent, } from "./stream-render.js";
6
+ import { RateLimitError, isLimitResult, resetsAtFromEvent, } from "./rate-limit.js";
7
+ /** Pure extraction of the fields Otto tracks from a stream-json `result` event. */
8
+ export function resultFromEvent(ev) {
9
+ const e = (ev ?? {});
10
+ return {
11
+ result: typeof e.result === "string" ? e.result : "",
12
+ costUsd: typeof e.total_cost_usd === "number" ? e.total_cost_usd : 0,
13
+ isError: e.is_error === true,
14
+ apiErrorStatus: typeof e.api_error_status === "string" ? e.api_error_status : null,
15
+ };
16
+ }
17
+ const STDERR_TAIL_LINES = 40;
18
+ const DEFAULT_RESULT_GRACE_MS = 30_000;
19
+ /**
20
+ * Parse `OTTO_RESULT_GRACE_MS`. Returns the configured millisecond budget,
21
+ * `0` to disable the timer entirely, or `defaultMs` for any invalid input
22
+ * (unset, empty, non-finite, negative).
23
+ */
24
+ export function parseGraceMs(raw, defaultMs = DEFAULT_RESULT_GRACE_MS) {
25
+ if (raw == null)
26
+ return defaultMs;
27
+ const trimmed = raw.trim();
28
+ if (trimmed === "")
29
+ return defaultMs;
30
+ const n = Number(trimmed);
31
+ if (!Number.isFinite(n))
32
+ return defaultMs;
33
+ if (n < 0)
34
+ return defaultMs;
35
+ return Math.floor(n);
36
+ }
37
+ /**
38
+ * Resolve `OTTO_MODEL` into a `claude` argv fragment. Returns
39
+ * `["--model", trimmed]` for a non-empty value, or `[]` for unset / empty /
40
+ * whitespace-only input. Pass-through: otto never validates the model spec,
41
+ * the `claude` CLI owns that.
42
+ */
43
+ export function resolveModelArgs(raw) {
44
+ if (raw == null)
45
+ return [];
46
+ const trimmed = raw.trim();
47
+ if (trimmed === "")
48
+ return [];
49
+ return ["--model", trimmed];
50
+ }
51
+ /** `OTTO_RUNNER=host` → bare host run; anything else (incl. unset) → sandbox. */
52
+ export function resolveRunner(raw) {
53
+ return raw?.trim() === "host" ? "host" : "sandbox";
54
+ }
55
+ /** Parse `OTTO_SANDBOX_NET` into a domain allowlist. Empty = unrestricted. */
56
+ export function resolveSandboxNet(raw) {
57
+ if (!raw)
58
+ return [];
59
+ return raw
60
+ .split(",")
61
+ .map((s) => s.trim())
62
+ .filter(Boolean);
63
+ }
64
+ // Go-based CLIs fail TLS verification under macOS Seatbelt; run them outside the
65
+ // sandbox so `gh`/`gcloud`/`terraform` keep working (otto-ghafk relies on gh).
66
+ const SANDBOX_EXCLUDED_COMMANDS = ["gh *", "gcloud *", "terraform *"];
67
+ /**
68
+ * Claude Code native-sandbox settings: confine writes to the workspace and run
69
+ * the Go-TLS CLIs unsandboxed. When `allowedDomains` is non-empty, also restrict
70
+ * network egress to that list; otherwise leave network unrestricted (filesystem
71
+ * is the blast-radius control; network commands fall back to the bypass-approved
72
+ * escape hatch).
73
+ */
74
+ export function buildSandboxSettings(workspaceDir, allowedDomains) {
75
+ const sandbox = {
76
+ enabled: true,
77
+ filesystem: { allowWrite: [workspaceDir] },
78
+ excludedCommands: SANDBOX_EXCLUDED_COMMANDS,
79
+ };
80
+ if (allowedDomains.length > 0) {
81
+ sandbox.network = { allowedDomains };
82
+ }
83
+ return { sandbox };
84
+ }
85
+ function abortError() {
86
+ const err = new Error("claude command aborted");
87
+ err.name = "AbortError";
88
+ return err;
89
+ }
90
+ export function stageLogPath(workspaceDir, iteration, stageName) {
91
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
92
+ return join(workspaceDir, ".otto-tmp", "logs", `${timestamp}-iter${iteration}-${stageName}.ndjson`);
93
+ }
94
+ /**
95
+ * Build the `claude` argv. Extracted as a pure helper so callers can unit-test
96
+ * the argv without spawning a process.
97
+ *
98
+ * @param stage - The stage configuration (name, permissionMode, etc.).
99
+ * @param promptRelPath - The workspace-relative path to the rendered prompt file.
100
+ * @param modelArgs - The `["--model", "<spec>"]` fragment from {@link resolveModelArgs},
101
+ * or `[]` when `OTTO_MODEL` is unset.
102
+ * @param settingsPath - Optional absolute path to a transient settings JSON file
103
+ * (written by `runStage` when `OTTO_RUNNER=sandbox`).
104
+ * @returns The full argv starting with `"claude"` and ending with the prompt
105
+ * instruction string.
106
+ */
107
+ export function buildClaudeArgs(stage, promptRelPath, modelArgs, settingsPath) {
108
+ const args = [
109
+ "claude",
110
+ "--verbose",
111
+ "--print",
112
+ "--output-format",
113
+ "stream-json",
114
+ ];
115
+ if (stage.permissionMode) {
116
+ args.push("--permission-mode", stage.permissionMode);
117
+ }
118
+ if (settingsPath) {
119
+ args.push("--settings", settingsPath);
120
+ }
121
+ args.push(...modelArgs);
122
+ args.push(`Read the full instructions from the file ./${promptRelPath} in the current workspace and execute them.`);
123
+ return args;
124
+ }
125
+ export async function runStage(stage, renderedPrompt, workspaceDir, iteration, spillHostDir, logPathOverride, options = {}) {
126
+ const tmpHostDir = join(workspaceDir, ".otto-tmp");
127
+ mkdirSync(tmpHostDir, { recursive: true });
128
+ const logsDir = join(tmpHostDir, "logs");
129
+ mkdirSync(logsDir, { recursive: true });
130
+ const logPath = logPathOverride ?? stageLogPath(workspaceDir, iteration, stage.name);
131
+ const promptName = `.run-${process.pid}-${iteration}-${Date.now()}.md`;
132
+ const promptHostPath = join(tmpHostDir, promptName);
133
+ const promptRelPath = posix.join(".otto-tmp", promptName);
134
+ writeFileSync(promptHostPath, renderedPrompt, "utf8");
135
+ let settingsHostPath;
136
+ if (resolveRunner(process.env.OTTO_RUNNER) === "sandbox") {
137
+ const settings = buildSandboxSettings(workspaceDir, resolveSandboxNet(process.env.OTTO_SANDBOX_NET));
138
+ settingsHostPath = join(tmpHostDir, `.sandbox-${process.pid}-${iteration}-${Date.now()}.json`);
139
+ writeFileSync(settingsHostPath, JSON.stringify(settings), "utf8");
140
+ }
141
+ process.stderr.write(`${dim("log → " + logPath)}\n`);
142
+ try {
143
+ const argv = buildClaudeArgs(stage, promptRelPath, resolveModelArgs(process.env.OTTO_MODEL), settingsHostPath);
144
+ return await streamClaude(argv, workspaceDir, logPath, options);
145
+ }
146
+ finally {
147
+ rmSync(promptHostPath, { force: true });
148
+ if (settingsHostPath)
149
+ rmSync(settingsHostPath, { force: true });
150
+ if (spillHostDir)
151
+ rmSync(spillHostDir, { recursive: true, force: true });
152
+ }
153
+ }
154
+ function streamClaude(argv, cwd, logPath, options = {}) {
155
+ if (options.signal?.aborted) {
156
+ return Promise.reject(abortError());
157
+ }
158
+ return new Promise((resolve, reject) => {
159
+ const logFd = openSync(logPath, "a");
160
+ const toolMap = new Map();
161
+ const graceMs = parseGraceMs(process.env.OTTO_RESULT_GRACE_MS);
162
+ // Spawn claude (argv[0]) on the host instead of docker.
163
+ const child = spawn(argv[0], argv.slice(1), {
164
+ cwd,
165
+ stdio: ["ignore", "pipe", "pipe"],
166
+ });
167
+ let final = {
168
+ result: "",
169
+ costUsd: 0,
170
+ isError: false,
171
+ apiErrorStatus: null,
172
+ };
173
+ let lastResetsAt = null;
174
+ const stderrTail = [];
175
+ let settled = false;
176
+ let onAbort = () => { };
177
+ let rl;
178
+ let rlErr;
179
+ let graceTimer;
180
+ const finish = (fn) => {
181
+ if (settled)
182
+ return;
183
+ settled = true;
184
+ if (graceTimer) {
185
+ clearTimeout(graceTimer);
186
+ graceTimer = undefined;
187
+ }
188
+ options.signal?.removeEventListener("abort", onAbort);
189
+ try {
190
+ rl?.close();
191
+ }
192
+ catch {
193
+ // Already closed.
194
+ }
195
+ try {
196
+ rlErr?.close();
197
+ }
198
+ catch {
199
+ // Already closed.
200
+ }
201
+ try {
202
+ closeSync(logFd);
203
+ }
204
+ catch {
205
+ // Already closed.
206
+ }
207
+ fn();
208
+ };
209
+ const rejectOnce = (err) => finish(() => reject(err));
210
+ const resolveOnce = (value) => finish(() => resolve(value));
211
+ onAbort = () => {
212
+ try {
213
+ child.kill();
214
+ }
215
+ catch {
216
+ // Already dead; close handling below will settle if needed.
217
+ }
218
+ rejectOnce(abortError());
219
+ };
220
+ rl = createInterface({ input: child.stdout });
221
+ rl.on("line", (line) => {
222
+ if (settled)
223
+ return;
224
+ if (!line.startsWith("{"))
225
+ return;
226
+ appendFileSync(logFd, line + "\n");
227
+ let parsed;
228
+ try {
229
+ parsed = JSON.parse(line);
230
+ }
231
+ catch {
232
+ return;
233
+ }
234
+ renderEvent(parsed, toolMap);
235
+ if (parsed.type === "rate_limit_event") {
236
+ const r = resetsAtFromEvent(parsed);
237
+ if (r != null)
238
+ lastResetsAt = r;
239
+ }
240
+ if (parsed.type === "result") {
241
+ final = resultFromEvent(parsed);
242
+ // Arm one-shot post-result grace timer to recover from claude-CLI
243
+ // self-deadlocks where the child emits its final NDJSON but never
244
+ // exits. See docs/prd/result-grace-timer.md.
245
+ if (!graceTimer && graceMs > 0) {
246
+ graceTimer = setTimeout(() => {
247
+ if (settled)
248
+ return;
249
+ process.stderr.write(`${dim(`grace timer fired after ${graceMs}ms post-result — killing claude child`)}\n`);
250
+ try {
251
+ child.kill();
252
+ }
253
+ catch {
254
+ // Already dead; close handler will be a no-op via settle guard.
255
+ }
256
+ if (final && isLimitResult(final)) {
257
+ rejectOnce(new RateLimitError(final.result || "rate limit", lastResetsAt));
258
+ }
259
+ else {
260
+ resolveOnce(final);
261
+ }
262
+ }, graceMs);
263
+ graceTimer.unref?.();
264
+ }
265
+ }
266
+ });
267
+ rlErr = createInterface({ input: child.stderr });
268
+ rlErr.on("line", (line) => {
269
+ if (settled)
270
+ return;
271
+ stderrTail.push(line);
272
+ if (stderrTail.length > STDERR_TAIL_LINES)
273
+ stderrTail.shift();
274
+ process.stderr.write(`${dim("claude " + line)}\n`);
275
+ });
276
+ if (options.signal?.aborted) {
277
+ onAbort();
278
+ return;
279
+ }
280
+ options.signal?.addEventListener("abort", onAbort, { once: true });
281
+ child.on("error", (err) => {
282
+ rejectOnce(err);
283
+ });
284
+ child.on("close", (code) => {
285
+ if (final && isLimitResult(final)) {
286
+ rejectOnce(new RateLimitError(final.result || "rate limit", lastResetsAt));
287
+ return;
288
+ }
289
+ if (code !== 0) {
290
+ rejectOnce(new Error(`claude exited with ${code}\n${stderrTail.join("\n")}`));
291
+ return;
292
+ }
293
+ resolveOnce(final);
294
+ });
295
+ });
296
+ }
297
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { type StageResult } from "./runner.js";
2
+ import type { Stage } from "./stages.js";
3
+ export type ExecuteStageOptions = {
4
+ stage: Stage;
5
+ vars: Record<string, string>;
6
+ workspaceDir: string;
7
+ packageDir: string;
8
+ iteration: number;
9
+ maxRetries: number;
10
+ signal?: AbortSignal;
11
+ /** Disambiguates spill/log paths when multiple sub-stages share an iteration (panel lenses). */
12
+ logLabel?: string;
13
+ };
14
+ /** Render a stage's template (inside the retry, so flaky shell tags retry) and run it. */
15
+ export declare function executeStage(opts: ExecuteStageOptions): Promise<StageResult>;
16
+ //# sourceMappingURL=stage-exec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage-exec.d.ts","sourceRoot":"","sources":["../src/stage-exec.ts"],"names":[],"mappings":"AAIA,OAAO,EAA0B,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,0FAA0F;AAC1F,wBAAsB,YAAY,CAChC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,WAAW,CAAC,CAmDtB"}