@aiaiai-pt/martha-cli 0.45.0 → 0.48.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.
- package/CHANGELOG.md +20 -0
- package/dist/index.js +51 -224
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,26 @@ All notable changes to `@aiaiai-pt/martha-cli`. Format: [Keep a Changelog](https
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.48.0] — 2026-07-13
|
|
8
|
+
|
|
9
|
+
### Changed — BREAKING: `martha runner` means ONE thing (#714 runner-journey unification)
|
|
10
|
+
- Bare `martha runner` now attaches this machine as a **cloud agent's hands** (the
|
|
11
|
+
previous `--tools` mode): it holds the per-agent channel open and executes
|
|
12
|
+
`tool.invoke` host-tool calls the agent's Martha-hosted reasoning loop parks on
|
|
13
|
+
(chat gates and assigned-task parks, #910). `--tools` is kept as an accepted
|
|
14
|
+
no-op so printed `setup-host-runner` commands stay valid.
|
|
15
|
+
- The startup line now states the model plainly ("this machine is the agent's
|
|
16
|
+
hands … the agent reasons in Martha's cloud loop") — the old "reasoning stays
|
|
17
|
+
Martha-hosted regardless" line was false for the removed task mode.
|
|
18
|
+
|
|
19
|
+
### Removed — the goal-as-`bash -c` task-executor mode
|
|
20
|
+
- `--tasks` (and the pre-#822 `--channel` alias), `--once`, `--team`, `--agent`,
|
|
21
|
+
and `--poll-interval` are **removed**. They ran a claimed task's goal as a raw
|
|
22
|
+
`bash -c` — the runner impersonating an external agent. Invoking any of them
|
|
23
|
+
now fails with migration guidance. **Nothing is stranded:** a self-hosted
|
|
24
|
+
agent is your own program driving the task REST — `martha tasks
|
|
25
|
+
poll/claim/heartbeat/complete` are unchanged.
|
|
26
|
+
|
|
7
27
|
## [0.30.0] — 2026-07-03
|
|
8
28
|
|
|
9
29
|
### Added — #761 runner heartbeat + poll-based cancel
|
package/dist/index.js
CHANGED
|
@@ -11119,7 +11119,7 @@ init_config();
|
|
|
11119
11119
|
init_errors();
|
|
11120
11120
|
|
|
11121
11121
|
// src/version.ts
|
|
11122
|
-
var CLI_VERSION = "0.
|
|
11122
|
+
var CLI_VERSION = "0.48.0";
|
|
11123
11123
|
|
|
11124
11124
|
// src/commands/sessions.ts
|
|
11125
11125
|
function relativeTime(iso) {
|
|
@@ -12249,6 +12249,7 @@ function parseLocalAgentRequests(data) {
|
|
|
12249
12249
|
|
|
12250
12250
|
// src/lib/local-tool-runner.ts
|
|
12251
12251
|
import { createHash as createHash2 } from "node:crypto";
|
|
12252
|
+
init_errors();
|
|
12252
12253
|
|
|
12253
12254
|
// src/lib/local-exec.ts
|
|
12254
12255
|
import { spawn } from "node:child_process";
|
|
@@ -12336,68 +12337,6 @@ async function runHostCommand(command, toolCallId, opts) {
|
|
|
12336
12337
|
function safeName(s) {
|
|
12337
12338
|
return s.replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 120) || "task";
|
|
12338
12339
|
}
|
|
12339
|
-
async function startHostCommand(command, toolCallId, opts) {
|
|
12340
|
-
const dir = runnerDir(opts.cwd);
|
|
12341
|
-
await fs5.mkdir(dir, { recursive: true });
|
|
12342
|
-
const logPath = path4.join(dir, `${safeName(toolCallId)}.log`);
|
|
12343
|
-
await fs5.writeFile(logPath, "");
|
|
12344
|
-
const fd = openSync(logPath, "a");
|
|
12345
|
-
const cap = opts.maxOutputBytes ?? MAX_OUTPUT;
|
|
12346
|
-
const readLog = async () => {
|
|
12347
|
-
try {
|
|
12348
|
-
const buf = await fs5.readFile(logPath);
|
|
12349
|
-
return buf.subarray(-cap).toString("utf8");
|
|
12350
|
-
} catch {
|
|
12351
|
-
return "";
|
|
12352
|
-
}
|
|
12353
|
-
};
|
|
12354
|
-
const child = spawn(command, {
|
|
12355
|
-
shell: true,
|
|
12356
|
-
cwd: opts.cwd,
|
|
12357
|
-
detached: true,
|
|
12358
|
-
stdio: ["ignore", fd, fd]
|
|
12359
|
-
});
|
|
12360
|
-
closeSync(fd);
|
|
12361
|
-
let exited = false;
|
|
12362
|
-
const done = new Promise((resolve, reject) => {
|
|
12363
|
-
let settled = false;
|
|
12364
|
-
child.on("error", (e) => {
|
|
12365
|
-
if (settled)
|
|
12366
|
-
return;
|
|
12367
|
-
settled = true;
|
|
12368
|
-
exited = true;
|
|
12369
|
-
reject(e);
|
|
12370
|
-
});
|
|
12371
|
-
child.on("close", async (code) => {
|
|
12372
|
-
if (settled)
|
|
12373
|
-
return;
|
|
12374
|
-
settled = true;
|
|
12375
|
-
exited = true;
|
|
12376
|
-
resolve({
|
|
12377
|
-
stdout: await readLog(),
|
|
12378
|
-
stderr: "",
|
|
12379
|
-
exit_code: code ?? -1,
|
|
12380
|
-
log_path: logPath
|
|
12381
|
-
});
|
|
12382
|
-
});
|
|
12383
|
-
});
|
|
12384
|
-
const kill = (graceMs = 5000) => {
|
|
12385
|
-
if (exited || !child.pid)
|
|
12386
|
-
return;
|
|
12387
|
-
try {
|
|
12388
|
-
process.kill(-child.pid, "SIGTERM");
|
|
12389
|
-
} catch {}
|
|
12390
|
-
const escalate = setTimeout(() => {
|
|
12391
|
-
if (exited || !child.pid)
|
|
12392
|
-
return;
|
|
12393
|
-
try {
|
|
12394
|
-
process.kill(-child.pid, "SIGKILL");
|
|
12395
|
-
} catch {}
|
|
12396
|
-
}, graceMs);
|
|
12397
|
-
escalate.unref?.();
|
|
12398
|
-
};
|
|
12399
|
-
return { pid: child.pid, done, kill };
|
|
12400
|
-
}
|
|
12401
12340
|
function mintExecSecret() {
|
|
12402
12341
|
return randomBytes2(32).toString("hex");
|
|
12403
12342
|
}
|
|
@@ -13393,7 +13332,7 @@ async function resolveLocalAgent(sessionId, toolCallId, req, state) {
|
|
|
13393
13332
|
}
|
|
13394
13333
|
async function claimGate(sessionId, toolCallId, state, transport) {
|
|
13395
13334
|
if (state.claimed.has(toolCallId))
|
|
13396
|
-
return;
|
|
13335
|
+
return true;
|
|
13397
13336
|
const secret = state.secrets.get(toolCallId) ?? mintExecSecret();
|
|
13398
13337
|
const secretHash = createHash2("sha256").update(secret).digest("hex");
|
|
13399
13338
|
state.secrets.set(toolCallId, secret);
|
|
@@ -13401,9 +13340,16 @@ async function claimGate(sessionId, toolCallId, state, transport) {
|
|
|
13401
13340
|
try {
|
|
13402
13341
|
await transport.claim(toolCallId, secretHash);
|
|
13403
13342
|
state.claimed.add(toolCallId);
|
|
13343
|
+
return true;
|
|
13404
13344
|
} catch (e) {
|
|
13345
|
+
if (e instanceof MarthaAPIError && e.status === 409) {
|
|
13346
|
+
process.stderr.write(source_default.yellow(`[local-exec] gate ${toolCallId} is not pending (already resolved or timed out) — skipping execution
|
|
13347
|
+
`));
|
|
13348
|
+
return false;
|
|
13349
|
+
}
|
|
13405
13350
|
process.stderr.write(source_default.yellow(`[local-exec] failed to claim gate for ${toolCallId}: ${String(e)}
|
|
13406
13351
|
`));
|
|
13352
|
+
return true;
|
|
13407
13353
|
}
|
|
13408
13354
|
}
|
|
13409
13355
|
async function postToolResult(toolCallId, result, label, transport, execSecret) {
|
|
@@ -13420,7 +13366,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
|
|
|
13420
13366
|
if (state.handled.has(id))
|
|
13421
13367
|
continue;
|
|
13422
13368
|
state.handled.add(id);
|
|
13423
|
-
await claimGate(sessionId, id, state, transport)
|
|
13369
|
+
if (!await claimGate(sessionId, id, state, transport))
|
|
13370
|
+
continue;
|
|
13424
13371
|
const result = await resolveHostExec(sessionId, id, req, state);
|
|
13425
13372
|
await postToolResult(id, result, "host_exec", transport, state.secrets.get(id));
|
|
13426
13373
|
}
|
|
@@ -13429,7 +13376,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
|
|
|
13429
13376
|
if (state.handled.has(id))
|
|
13430
13377
|
continue;
|
|
13431
13378
|
state.handled.add(id);
|
|
13432
|
-
await claimGate(sessionId, id, state, transport)
|
|
13379
|
+
if (!await claimGate(sessionId, id, state, transport))
|
|
13380
|
+
continue;
|
|
13433
13381
|
const result = await resolveHostOutput(req, state);
|
|
13434
13382
|
await postToolResult(id, result, "host_output", transport, state.secrets.get(id));
|
|
13435
13383
|
}
|
|
@@ -13438,7 +13386,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
|
|
|
13438
13386
|
if (state.handled.has(id))
|
|
13439
13387
|
continue;
|
|
13440
13388
|
state.handled.add(id);
|
|
13441
|
-
await claimGate(sessionId, id, state, transport)
|
|
13389
|
+
if (!await claimGate(sessionId, id, state, transport))
|
|
13390
|
+
continue;
|
|
13442
13391
|
const result = await resolveHostKill(req, state);
|
|
13443
13392
|
await postToolResult(id, result, "host_kill", transport, state.secrets.get(id));
|
|
13444
13393
|
}
|
|
@@ -13447,7 +13396,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
|
|
|
13447
13396
|
if (state.handled.has(id))
|
|
13448
13397
|
continue;
|
|
13449
13398
|
state.handled.add(id);
|
|
13450
|
-
await claimGate(sessionId, id, state, transport)
|
|
13399
|
+
if (!await claimGate(sessionId, id, state, transport))
|
|
13400
|
+
continue;
|
|
13451
13401
|
const result = await resolveHostScreenshot(ctx, sessionId, req, state);
|
|
13452
13402
|
await postToolResult(id, result, "host_screenshot", transport, state.secrets.get(id));
|
|
13453
13403
|
}
|
|
@@ -13456,7 +13406,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
|
|
|
13456
13406
|
if (state.handled.has(id))
|
|
13457
13407
|
continue;
|
|
13458
13408
|
state.handled.add(id);
|
|
13459
|
-
await claimGate(sessionId, id, state, transport)
|
|
13409
|
+
if (!await claimGate(sessionId, id, state, transport))
|
|
13410
|
+
continue;
|
|
13460
13411
|
const result = await resolveHostBrowser(ctx, sessionId, req, state);
|
|
13461
13412
|
await postToolResult(id, result, "host_browser", transport, state.secrets.get(id));
|
|
13462
13413
|
}
|
|
@@ -13465,7 +13416,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
|
|
|
13465
13416
|
if (state.handled.has(id))
|
|
13466
13417
|
continue;
|
|
13467
13418
|
state.handled.add(id);
|
|
13468
|
-
await claimGate(sessionId, id, state, transport)
|
|
13419
|
+
if (!await claimGate(sessionId, id, state, transport))
|
|
13420
|
+
continue;
|
|
13469
13421
|
const result = await resolveHostFile(req, state);
|
|
13470
13422
|
await postToolResult(id, result, req.action, transport, state.secrets.get(id));
|
|
13471
13423
|
}
|
|
@@ -13474,7 +13426,8 @@ async function handleToolStatusData(ctx, sessionId, data, state, transport) {
|
|
|
13474
13426
|
if (state.handled.has(id))
|
|
13475
13427
|
continue;
|
|
13476
13428
|
state.handled.add(id);
|
|
13477
|
-
await claimGate(sessionId, id, state, transport)
|
|
13429
|
+
if (!await claimGate(sessionId, id, state, transport))
|
|
13430
|
+
continue;
|
|
13478
13431
|
const result = await resolveLocalAgent(sessionId, id, req, state);
|
|
13479
13432
|
await postToolResult(id, result, "local_agent", transport, state.secrets.get(id));
|
|
13480
13433
|
}
|
|
@@ -17718,9 +17671,6 @@ ${items.length} task(s) available`));
|
|
|
17718
17671
|
// src/commands/runner.ts
|
|
17719
17672
|
import os3 from "node:os";
|
|
17720
17673
|
init_errors();
|
|
17721
|
-
function sleep5(ms) {
|
|
17722
|
-
return new Promise((r) => setTimeout(r, ms));
|
|
17723
|
-
}
|
|
17724
17674
|
function hostLabel() {
|
|
17725
17675
|
try {
|
|
17726
17676
|
return os3.hostname() || undefined;
|
|
@@ -17734,92 +17684,24 @@ function sleepUnref(ms) {
|
|
|
17734
17684
|
t.unref?.();
|
|
17735
17685
|
});
|
|
17736
17686
|
}
|
|
17737
|
-
var DEFAULT_LEASE_S = 300;
|
|
17738
17687
|
var ACK_INTERVAL_MS = 5000;
|
|
17739
|
-
function heartbeatIntervalMs(leaseTimeoutS) {
|
|
17740
|
-
const lease = typeof leaseTimeoutS === "number" && leaseTimeoutS > 0 ? leaseTimeoutS : DEFAULT_LEASE_S;
|
|
17741
|
-
return Math.min(lease / 3, 60) * 1000;
|
|
17742
|
-
}
|
|
17743
|
-
async function runOnce(ctx, opts, handled) {
|
|
17744
|
-
const params = { limit: String(opts.pollLimit ?? 5) };
|
|
17745
|
-
if (opts.team)
|
|
17746
|
-
params.team_id = opts.team;
|
|
17747
|
-
const tasks = await ctx.api.get("/api/tasks/poll", { params });
|
|
17748
|
-
const candidates = Array.isArray(tasks) ? tasks : [];
|
|
17749
|
-
const matchesAgent = (t) => !opts.agent || t.assigned_agent_id === opts.agent || t.agent_definition_id === opts.agent;
|
|
17750
|
-
const task = candidates.find((t) => (t.status ?? "open") === "open" && !handled?.has(t.id) && matchesAgent(t));
|
|
17751
|
-
if (!task)
|
|
17752
|
-
return { ran: false };
|
|
17753
|
-
if (!opts.allowHostExec) {
|
|
17754
|
-
process.stderr.write(source_default.yellow(`
|
|
17755
|
-
[runner] refused task ${task.id} — re-run with --allow-host-exec to allow host execution
|
|
17756
|
-
`));
|
|
17757
|
-
return { ran: false, taskId: task.id, status: "refused" };
|
|
17758
|
-
}
|
|
17759
|
-
handled?.add(task.id);
|
|
17760
|
-
const claimed = await ctx.api.post(`/api/tasks/${encodeURIComponent(task.id)}/claim`, {});
|
|
17761
|
-
const goal = claimed.goal ?? task.goal ?? "";
|
|
17762
|
-
const intervalMs = heartbeatIntervalMs(claimed.lease_timeout_s ?? DEFAULT_LEASE_S);
|
|
17763
|
-
process.stderr.write(source_default.dim(`
|
|
17764
|
-
[runner] claimed ${task.id} — running: ${goal} (heartbeat every ${Math.round(intervalMs / 1000)}s)
|
|
17765
|
-
`));
|
|
17766
|
-
let result;
|
|
17767
|
-
let cancelled = false;
|
|
17768
|
-
try {
|
|
17769
|
-
const handle = await startHostCommand(goal, `task-${task.id}`, {
|
|
17770
|
-
cwd: opts.cwd
|
|
17771
|
-
});
|
|
17772
|
-
const done = handle.done.catch((e) => ({
|
|
17773
|
-
stdout: "",
|
|
17774
|
-
stderr: String(e),
|
|
17775
|
-
exit_code: -1
|
|
17776
|
-
}));
|
|
17777
|
-
for (;; ) {
|
|
17778
|
-
const raced = await Promise.race([
|
|
17779
|
-
done,
|
|
17780
|
-
sleepUnref(intervalMs).then(() => null)
|
|
17781
|
-
]);
|
|
17782
|
-
if (raced !== null) {
|
|
17783
|
-
result = raced;
|
|
17784
|
-
break;
|
|
17785
|
-
}
|
|
17786
|
-
let reply;
|
|
17787
|
-
try {
|
|
17788
|
-
reply = await ctx.api.post(`/api/tasks/${encodeURIComponent(task.id)}/heartbeat`, {});
|
|
17789
|
-
} catch (e) {
|
|
17790
|
-
process.stderr.write(source_default.yellow(`[runner] heartbeat failed for ${task.id} (job unaffected): ${String(e)}
|
|
17791
|
-
`));
|
|
17792
|
-
continue;
|
|
17793
|
-
}
|
|
17794
|
-
if (reply && reply.signal === "cancel") {
|
|
17795
|
-
process.stderr.write(source_default.yellow(`[runner] cancel signal for ${task.id} — killing process tree
|
|
17796
|
-
`));
|
|
17797
|
-
handle.kill();
|
|
17798
|
-
result = await done;
|
|
17799
|
-
cancelled = true;
|
|
17800
|
-
break;
|
|
17801
|
-
}
|
|
17802
|
-
}
|
|
17803
|
-
} catch (e) {
|
|
17804
|
-
result = { stdout: "", stderr: String(e), exit_code: -1 };
|
|
17805
|
-
}
|
|
17806
|
-
const exit_code = result.exit_code;
|
|
17807
|
-
const stdout = result.stdout || result.stderr || "";
|
|
17808
|
-
const status = cancelled ? "cancelled" : exit_code === 0 ? "completed" : "failed";
|
|
17809
|
-
await ctx.api.post(`/api/tasks/${encodeURIComponent(task.id)}/complete`, {
|
|
17810
|
-
outcome: cancelled ? { stdout, exit_code, cancelled: true } : { stdout, exit_code },
|
|
17811
|
-
status
|
|
17812
|
-
});
|
|
17813
|
-
process.stderr.write(source_default.dim(`[runner] completed ${task.id} — ${status} (exit ${exit_code})
|
|
17814
|
-
`));
|
|
17815
|
-
return { ran: true, taskId: task.id, status, exitCode: exit_code };
|
|
17816
|
-
}
|
|
17817
17688
|
function startupEchoLine(who, opts) {
|
|
17818
17689
|
const identity2 = who ? who.kind === "agent" ? `agent ${who.agent_name ?? "?"} (${(who.agent_id ?? "").slice(0, 8)}…)` : `${who.kind} ${who.sub ?? ""}`.trim() : "identity unknown (whoami unavailable)";
|
|
17819
|
-
const scope = opts.team ? `team:${opts.team}` : "direct";
|
|
17820
17690
|
const exec = opts.allowHostExec ? "host exec allowed" : "host exec refused (pass --allow-host-exec)";
|
|
17821
|
-
|
|
17822
|
-
|
|
17691
|
+
return `[runner] attached as ${identity2} — this machine is the agent's hands (host executor; the agent reasons in Martha's cloud loop), cwd ${opts.cwd}, ${exec}`;
|
|
17692
|
+
}
|
|
17693
|
+
function rejectRemovedTaskModeFlags(opts) {
|
|
17694
|
+
const used = [
|
|
17695
|
+
opts.tasks && "--tasks",
|
|
17696
|
+
opts.channel && "--channel",
|
|
17697
|
+
opts.once && "--once",
|
|
17698
|
+
opts.team && "--team",
|
|
17699
|
+
opts.agent && "--agent",
|
|
17700
|
+
opts.pollInterval && "--poll-interval"
|
|
17701
|
+
].filter(Boolean);
|
|
17702
|
+
if (used.length === 0)
|
|
17703
|
+
return;
|
|
17704
|
+
throw new CLIError(`${used.join(", ")} ${used.length > 1 ? "were" : "was"} removed: ` + "`martha runner` now means ONE thing — attach this machine as a cloud " + "agent's hands (host executor). It no longer polls or runs tasks. A " + "self-hosted agent is your own program driving the task REST " + "(`martha tasks poll/claim/heartbeat/complete`) and the peer channel.", 4 /* Validation */);
|
|
17823
17705
|
}
|
|
17824
17706
|
function shouldFlushAck(pendingAckId, lastAckedId, lastAckAt, now, force, intervalMs) {
|
|
17825
17707
|
if (!pendingAckId || pendingAckId === lastAckedId)
|
|
@@ -17879,7 +17761,7 @@ async function runChannelLoop(ctx, state, abort) {
|
|
|
17879
17761
|
if (abort.signal.aborted)
|
|
17880
17762
|
break;
|
|
17881
17763
|
if (err instanceof MarthaAPIError && err.status === 404) {
|
|
17882
|
-
process.stderr.write(source_default.dim(`[runner] channel unavailable (server
|
|
17764
|
+
process.stderr.write(source_default.dim(`[runner] channel unavailable (server CHANNEL_ENABLED off, or old server) — nothing to attach to; exiting
|
|
17883
17765
|
`));
|
|
17884
17766
|
return;
|
|
17885
17767
|
}
|
|
@@ -17945,59 +17827,25 @@ async function runChannelLoop(ctx, state, abort) {
|
|
|
17945
17827
|
}
|
|
17946
17828
|
}
|
|
17947
17829
|
function registerRunnerCommand(program2) {
|
|
17948
|
-
program2.command("runner").description("Attach this machine as
|
|
17830
|
+
program2.command("runner").description("Attach this machine as a cloud agent's hands (#714): hold the per-agent " + "channel open and execute host-tool calls the agent's Martha-hosted " + "reasoning loop parks on (chat gates and assigned-task parks). Identity " + "comes from the API token (the agent's runner key). The runner never " + "reasons and never runs tasks — self-hosted agents drive the task REST " + "(`martha tasks poll/claim/complete`) with their own code.").option("--tools", "Accepted no-op: hands mode is the only mode (kept so printed " + "setup-host-runner commands stay valid).").option("--cwd <dir>", "Scoped workspace for host execution", process.cwd()).option("--allow-host-exec", "Consent to run host commands (required to execute)").option("--yes", "Deprecated alias for --allow-host-exec").option("--local-agent <name>", "Local coding agent for local_agent delegation over the channel", DEFAULT_LOCAL_AGENT).option("--tasks", "REMOVED (#714): the runner no longer runs tasks").option("--channel", "REMOVED (#714): deprecated pre-#822 alias").option("--once", "REMOVED (#714): belonged to the task-executor mode").option("--team <name>", "REMOVED (#714): belonged to the task-executor mode").option("--agent <id>", "REMOVED (#714): identity comes from the API token").option("--poll-interval <ms>", "REMOVED (#714): the runner no longer polls").action(async (opts) => {
|
|
17831
|
+
rejectRemovedTaskModeFlags(opts);
|
|
17949
17832
|
const ctx = createContext({
|
|
17950
17833
|
profileOverride: program2.opts().profile,
|
|
17951
17834
|
verbose: program2.opts().verbose,
|
|
17952
17835
|
apiUrlOverride: program2.opts().apiUrl
|
|
17953
17836
|
});
|
|
17954
|
-
const
|
|
17955
|
-
if (isNaN(pollInterval) || pollInterval < 250) {
|
|
17956
|
-
throw new CLIError("--poll-interval must be >= 250 (ms)", 4 /* Validation */);
|
|
17957
|
-
}
|
|
17958
|
-
if (opts.agent) {
|
|
17959
|
-
process.stderr.write(source_default.yellow(`[runner] --agent is deprecated — identity comes from the API token; the value is applied as a client-side filter only
|
|
17960
|
-
`));
|
|
17961
|
-
}
|
|
17962
|
-
const runOpts = {
|
|
17963
|
-
agent: opts.agent,
|
|
17964
|
-
team: opts.team,
|
|
17965
|
-
cwd: opts.cwd,
|
|
17966
|
-
allowHostExec: !!(opts.allowHostExec || opts.yes)
|
|
17967
|
-
};
|
|
17968
|
-
let runTasks;
|
|
17969
|
-
let runChannel;
|
|
17970
|
-
if (opts.tools || opts.tasks) {
|
|
17971
|
-
runTasks = !!opts.tasks;
|
|
17972
|
-
runChannel = !!opts.tools;
|
|
17973
|
-
} else if (opts.channel) {
|
|
17974
|
-
runTasks = true;
|
|
17975
|
-
runChannel = true;
|
|
17976
|
-
} else {
|
|
17977
|
-
runTasks = true;
|
|
17978
|
-
runChannel = false;
|
|
17979
|
-
}
|
|
17980
|
-
const mode = runTasks && runChannel ? "tools+tasks" : runChannel ? "tools" : "tasks";
|
|
17837
|
+
const allowHostExec = !!(opts.allowHostExec || opts.yes);
|
|
17981
17838
|
let who;
|
|
17982
17839
|
try {
|
|
17983
17840
|
who = await ctx.api.get("/api/tasks/whoami");
|
|
17984
17841
|
} catch {}
|
|
17985
|
-
process.stderr.write(source_default.dim(`${startupEchoLine(who, {
|
|
17842
|
+
process.stderr.write(source_default.dim(`${startupEchoLine(who, { cwd: opts.cwd, allowHostExec })}
|
|
17986
17843
|
`));
|
|
17987
|
-
if (
|
|
17988
|
-
throw new CLIError("
|
|
17844
|
+
if (!allowHostExec) {
|
|
17845
|
+
throw new CLIError("the runner executes host-tool calls; re-run with --allow-host-exec to consent", 4 /* Validation */);
|
|
17989
17846
|
}
|
|
17990
|
-
if (opts.once) {
|
|
17991
|
-
if (!runTasks) {
|
|
17992
|
-
throw new CLIError("--once runs a single TASK; it has no meaning in --tools (host-runner) mode", 4 /* Validation */);
|
|
17993
|
-
}
|
|
17994
|
-
await runOnce(ctx, runOpts);
|
|
17995
|
-
return;
|
|
17996
|
-
}
|
|
17997
|
-
let stop = false;
|
|
17998
17847
|
const abort = new AbortController;
|
|
17999
17848
|
const onSignal = () => {
|
|
18000
|
-
stop = true;
|
|
18001
17849
|
abort.abort();
|
|
18002
17850
|
process.stderr.write(`
|
|
18003
17851
|
[runner] stopping…
|
|
@@ -18005,34 +17853,13 @@ function registerRunnerCommand(program2) {
|
|
|
18005
17853
|
};
|
|
18006
17854
|
process.on("SIGINT", onSignal);
|
|
18007
17855
|
process.on("SIGTERM", onSignal);
|
|
18008
|
-
const
|
|
18009
|
-
|
|
18010
|
-
|
|
18011
|
-
|
|
18012
|
-
|
|
18013
|
-
r = await runOnce(ctx, runOpts, handled);
|
|
18014
|
-
} catch (e) {
|
|
18015
|
-
process.stderr.write(source_default.red(`[runner] error: ${String(e)}
|
|
18016
|
-
`));
|
|
18017
|
-
r = { ran: false };
|
|
18018
|
-
}
|
|
18019
|
-
if (!r.ran)
|
|
18020
|
-
await sleep5(pollInterval);
|
|
18021
|
-
}
|
|
18022
|
-
};
|
|
18023
|
-
const loops = [];
|
|
18024
|
-
if (runTasks)
|
|
18025
|
-
loops.push(pollLoop());
|
|
18026
|
-
if (runChannel) {
|
|
18027
|
-
const state = createLocalExecState({
|
|
18028
|
-
cwd: opts.cwd,
|
|
18029
|
-
autoYes: runOpts.allowHostExec,
|
|
18030
|
-
localAgent: opts.localAgent ?? DEFAULT_LOCAL_AGENT
|
|
18031
|
-
});
|
|
18032
|
-
loops.push(runChannelLoop(ctx, state, abort));
|
|
18033
|
-
}
|
|
17856
|
+
const state = createLocalExecState({
|
|
17857
|
+
cwd: opts.cwd,
|
|
17858
|
+
autoYes: allowHostExec,
|
|
17859
|
+
localAgent: opts.localAgent ?? DEFAULT_LOCAL_AGENT
|
|
17860
|
+
});
|
|
18034
17861
|
try {
|
|
18035
|
-
await
|
|
17862
|
+
await runChannelLoop(ctx, state, abort);
|
|
18036
17863
|
} finally {
|
|
18037
17864
|
process.off("SIGINT", onSignal);
|
|
18038
17865
|
process.off("SIGTERM", onSignal);
|