Skip to content

feat(compute): insta compute exec — run a one-shot command in a compute service - #105

Merged
jwfing merged 2 commits into
mainfrom
feat/compute-exec
Aug 19, 2026
Merged

feat(compute): insta compute exec — run a one-shot command in a compute service#105
jwfing merged 2 commits into
mainfrom
feat/compute-exec

Conversation

@jwfing

@jwfing jwfing commented Aug 19, 2026

Copy link
Copy Markdown
Member

What

New command: insta compute exec [service] -- <command> [args...] — runs a one-shot command inside a compute service's machine via the new platform route POST /projects/:id/services/:serviceId/exec.

Options: --branch <b> (default: current), --timeout <sec> (1–180, default 30), --json.

Semantics (each backed by a test)

  • Remote stdout→stdout, stderr→stderr verbatim (process.stdout.write, no added newlines).
  • The CLI's exit code is the remote command's exit code — scripts/agents can branch on it. Codes outside 0–255 (e.g. the platform's -1 unknown-exit sentinel) map to exit 1 with a stderr note (avoids Node's DEP0164).
  • 202 approval-pending exits 1 (not 0 — a pending approval must not read as success in && chains); --json prints the raw 202 envelope.
  • Output is capped by the platform at 1 MiB per stream; a truncation notice goes to stderr.
  • One-shot only — no interactive shell, no stdin. Wakes a scaled-to-zero machine first (a few seconds; billed as uptime).
  • Argv split: exec needs a literal -- boundary (optional [service] + variadic command is otherwise ambiguous in commander); isolated in a pure, unit-tested splitExecArgs.

Tests

npm run typecheck clean; npm test 330/330 (19 exec tests: split/validation/timeout bounds + applyExecResult exit/clamp/202/json paths); npm run build clean. Manual smoke of 200/202/400/truncation against a fake server (transcript in the PR branch's review notes).

Ordering

Requires the platform route (InsForge/insta-platform PR feat/compute-exec — link in comments once open). Do not cut a CLI release with this until the platform change is deployed, or the command 404s against prod.

🤖 Generated with Claude Code


Summary by cubic

Adds insta compute exec to run a one-shot command inside a compute service and exit with the remote command’s code. Pending approvals now exit 1 (was 0) and out-of-range remote exit codes clamp to 1 to avoid Node’s 255 fallback.

  • Non-interactive execution: insta compute exec [service] -- <command> [args…]. Requires a literal --; no shell, stdin, or PTY. Wakes scaled-to-zero machines; latency and billing as normal uptime.
  • Output and exit codes: stdout/stderr are written verbatim (no added newlines). Each stream is capped at 1 MiB; truncation prints a stderr note. CLI exit code equals the remote exit code; 202 approval-pending exits 1; codes outside 0–255 clamp to 1 with a stderr note.
  • Options: --branch <b> (default: current), --timeout <sec> (1–180; omitted uses platform default 30), --json (prints the raw response, including 202 envelopes).
  • Implementation touchpoints: pre-parse argv via splitExecArgs before commander; new applyExecResult handles 200/202 and exit code mapping; request body omits timeoutSec when not provided. Unit tests cover argv splitting, timeout bounds, request body, and result handling.

Rollout

  • Requires platform route POST /projects/:id/services/:serviceId/exec. Do not ship a CLI release until the platform change is live.
  • Automation: ensure scripts pass the literal -- before the command and expect exit 1 on approval-pending.

Written for commit 3c94968. Summary will update on new commits.

Review in cubic

jwfing and others added 2 commits August 18, 2026 17:18
…te service

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ps --timeout at 180

A 202 previously left process.exitCode at 0, so `insta compute exec db
migrate && next` proceeded as if the migration had run. Extracted
applyExecResult(res, json) so the 202 and exit-code handling is a pure,
directly-testable function of the response: a 202 now exits 1 (raw
envelope on --json, the usual human hint otherwise); an out-of-range
remote exit code (the platform's -1 unknown-exit sentinel, or anything
outside 0-255) is clamped to 1 with a one-line stderr note instead of
hitting Node's own DEP0164/255 behavior. --timeout's bound drops from
300 to 180 to match the platform.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jwfing

jwfing commented Aug 19, 2026

Copy link
Copy Markdown
Member Author

Platform route PR: InsForge/insta-platform#241 (merge/deploy that first; full ordering in its description).

@jwfing jwfing left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: feat(compute): insta compute exec

Summary: A clean, well-tested addition of a one-shot insta compute exec command that maps neatly onto existing CLI conventions; no blocking issues found.

Requirements context

No /docs/superpowers/ (or docs/specs/) spec/plan exists in this repo — insta-cli keeps feature specs in the companion platform PR, not here. Assessed against the PR description, the platform route contract it names (POST /projects/:id/services/:serviceId/exec), and the surrounding command code.


Findings

Critical

(none)

Suggestion

  • [functionality] splitExecArgs scans the whole argv for adjacent compute exec tokens rather than anchoring to the actual command positionsrc/commands/compute.ts:87-93. The split is applied globally to process.argv in src/index.ts:177 before commander parses, and keys only on any 'compute' immediately followed by 'exec', then the first -- after it. insta run <cmd> [args…] (src/index.ts:80-83, .passThroughOptions().allowUnknownOption()) is explicitly designed to run arbitrary passthrough commands, so an invocation like insta run compute exec -- --flag would match this heuristic and silently truncate the tokens after -- from the run passthrough. This is low-probability (it needs the literal adjacent tokens compute exec and a following -- in a non-exec invocation), and the normal case insta run cargo test -- --nocapture is unaffected because the tokens aren't present — but the guard comment's claim that "nothing else is affected" is slightly overstated. Anchoring the split to the resolved top-level command being compute exec (or only splitting when the tokens are the first two operands) would remove the ambiguity entirely.
  • [software engineering] No regression test for the insta run collision abovetest/compute-exec.test.ts:21-24 covers "leaves argv untouched for any other invocation" only for compute start; a case asserting that a run-style argv containing compute exec … -- … is left intact would pin the seam the PR itself calls "most likely to regress."

Information

  • [functionality] applyExecResult destructures res.body without a null guardsrc/commands/compute.ts:128. On a hypothetical 200 with an absent/null body, exitCode is undefined; both range checks are false, so process.exitCode = undefined → exit 0. The platform contract guarantees the body on a non-202 success, so this is informational, not a bug; a ?? {} or explicit shape check would just make it defensive.
  • [software engineering] --branch <b> on the exec subcommand lacks the descriptive help string its siblings carry — src/index.ts:201 uses bare .option('--branch <b>') where start/stop/status/etc. use .option('--branch <branch>', 'branch (default: current)'). Cosmetic help-text inconsistency only.
  • [software engineering] Flag-surface mirror — AGENTS.md non-negotiable #4 requires command/flag changes to be mirrored in the superproject's skills/insta/cli-reference.md (agent-facing surface doc). That file lives outside this repo, so it can't be verified from this diff — flagging as a reminder for the merge.

Security

No security-relevant concerns. The command is sent to the platform as an argv array (no shell invoked client-side, so no local shell-injection surface — the help text correctly documents ["sh","-c","…"] for shell features), no new dependencies are added, auth is unchanged (existing bearer flow), and the only new output is the remote command's own stdout/stderr written verbatim — that is the user's own data, not newly-exposed secrets. Server-side execution remains gated (the 202 approval path is handled).

Performance

No concerns. computeExec issues one GET …/services (to resolve the service id — identical to every other compute subcommand) plus one POST …/exec; no N+1, no unbounded loops. Output is capped at 1 MiB/stream server-side and buffered as two strings, which is acceptable for a one-shot command.


Notable strengths

  • Argv-split, timeout parsing, request-body mapping, and result rendering are all extracted as pure, exported functions with direct unit tests (19 cases) — mirrors the established parseCpu/execRequestBody/applyExecResult-style pattern in this suite.
  • Exit-code semantics are careful and correct: remote code passthrough, out-of-range (incl. the -1 sentinel) clamped to 1 with a visible note to sidestep Node's DEP0164, and 202 approval_required → exit 1 so it can't read as success in && chains. Consistent with the rawRequest throws-on-≥400 / returns-{status,body}-for-2xx contract, so 400/404/timeout errors correctly flow through the top-level guard.

Verdict

approved (informational — the GitHub green-check is a separate human action). No Critical findings; the Suggestions above are worth a look but none block merge. Note the PR's own ordering caveat still stands: don't cut a CLI release until the platform exec route is deployed.

@Fermionic-Lyu Fermionic-Lyu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, Approved.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/commands/compute.ts">

<violation number="1" location="src/commands/compute.ts:87">
P2: splitExecArgs scans the whole argv for a `compute` followed by `exec` and strips everything after the first `--`, without anchoring to the actually-invoked `compute exec` subcommand. A different subcommand that legitimately takes a free-form command with a literal `--` — namely `insta run compute exec -- echo hi` (run is `run <cmd> [args...]` with passThroughOptions) — gets its `--` and trailing args silently removed, so it runs the wrong command. Guard the split so it only fires when the invocation is genuinely `compute exec` (e.g. verify argv starts [node, insta, compute, exec] / there is no earlier subcommand token).</violation>

<violation number="2" location="src/commands/compute.ts:129">
P2: When `--json` is used with truncated output, this branch prints only the JSON envelope and skips the required stderr notice. Emit the truncation notice after the JSON/non-JSON split so both modes report capped output.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/commands/compute.ts
Comment on lines +129 to +135
if (json) {
printJson(res.body)
} else {
process.stdout.write(stdout)
process.stderr.write(stderr)
if (truncated) process.stderr.write('note: output truncated — the platform caps stdout/stderr at 1 MiB each\n')
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When --json is used with truncated output, this branch prints only the JSON envelope and skips the required stderr notice. Emit the truncation notice after the JSON/non-JSON split so both modes report capped output.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/commands/compute.ts, line 129:

<comment>When `--json` is used with truncated output, this branch prints only the JSON envelope and skips the required stderr notice. Emit the truncation notice after the JSON/non-JSON split so both modes report capped output.</comment>

<file context>
@@ -74,6 +74,95 @@ export async function computeStatus(serviceName: string | undefined, opts: LifeO
+    return
+  }
+  const { exitCode, stdout, stderr, truncated } = res.body
+  if (json) {
+    printJson(res.body)
+  } else {
</file context>
Suggested change
if (json) {
printJson(res.body)
} else {
process.stdout.write(stdout)
process.stderr.write(stderr)
if (truncated) process.stderr.write('note: output truncated — the platform caps stdout/stderr at 1 MiB each\n')
}
if (json) {
printJson(res.body)
} else {
process.stdout.write(stdout)
process.stderr.write(stderr)
}
if (truncated) process.stderr.write('note: output truncated — the platform caps stdout/stderr at 1 MiB each\n')

Comment thread src/commands/compute.ts
// ourselves, before commander ever parses it, removes the ambiguity; this is the only place in the
// whole CLI a bare `--` has this meaning, so nothing else is affected. Exported for a direct,
// network-free unit test — this split is the seam most likely to regress.
export function splitExecArgs(argv: string[]): { argv: string[]; command?: string[] } {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: splitExecArgs scans the whole argv for a compute followed by exec and strips everything after the first --, without anchoring to the actually-invoked compute exec subcommand. A different subcommand that legitimately takes a free-form command with a literal -- — namely insta run compute exec -- echo hi (run is run <cmd> [args...] with passThroughOptions) — gets its -- and trailing args silently removed, so it runs the wrong command. Guard the split so it only fires when the invocation is genuinely compute exec (e.g. verify argv starts [node, insta, compute, exec] / there is no earlier subcommand token).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/commands/compute.ts, line 87:

<comment>splitExecArgs scans the whole argv for a `compute` followed by `exec` and strips everything after the first `--`, without anchoring to the actually-invoked `compute exec` subcommand. A different subcommand that legitimately takes a free-form command with a literal `--` — namely `insta run compute exec -- echo hi` (run is `run <cmd> [args...]` with passThroughOptions) — gets its `--` and trailing args silently removed, so it runs the wrong command. Guard the split so it only fires when the invocation is genuinely `compute exec` (e.g. verify argv starts [node, insta, compute, exec] / there is no earlier subcommand token).</comment>

<file context>
@@ -74,6 +74,95 @@ export async function computeStatus(serviceName: string | undefined, opts: LifeO
+// ourselves, before commander ever parses it, removes the ambiguity; this is the only place in the
+// whole CLI a bare `--` has this meaning, so nothing else is affected. Exported for a direct,
+// network-free unit test — this split is the seam most likely to regress.
+export function splitExecArgs(argv: string[]): { argv: string[]; command?: string[] } {
+  const i = argv.findIndex((a, idx) => a === 'compute' && argv[idx + 1] === 'exec')
+  if (i === -1) return { argv }
</file context>

@jwfing
jwfing merged commit dd33673 into main Aug 19, 2026
2 checks passed
@jwfing jwfing mentioned this pull request Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants