@botbuddy/cli 1.63.3 → 1.63.5
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/bin/botbuddy.mjs +10 -2
- package/daemon/bin/darwin-arm64/botbuddy-daemon +0 -0
- package/daemon/bin/linux-x64/botbuddy-daemon +0 -0
- package/daemon-release/darwin-arm64.json +4 -4
- package/daemon-release/linux-x64.json +4 -4
- package/daemon-release/manifest.json +5 -5
- package/package.json +1 -1
- package/src/run.mjs +50 -3
- package/src/stack.mjs +38 -0
package/bin/botbuddy.mjs
CHANGED
|
@@ -4,11 +4,19 @@ import { ensureRunDelivery } from "../src/run-delivery.mjs";
|
|
|
4
4
|
|
|
5
5
|
const argv = process.argv.slice(2);
|
|
6
6
|
|
|
7
|
-
// BOT-2600:
|
|
7
|
+
// BOT-2600: normal `bb` invocations start the single per-machine delivery process
|
|
8
8
|
// when finished `bb run` commands still have an unsent terminal update. A stat
|
|
9
9
|
// and two small reads when nothing changed; it never fails the command.
|
|
10
10
|
const deliveryProcessArgs = argv[0] === "run" && (argv[1] === "--worker" || argv[1] === "--deliver");
|
|
11
|
-
|
|
11
|
+
// BOT-2766: help and inspection must remain read-only through the launcher,
|
|
12
|
+
// not just inside their handlers. Child help after -- still belongs to work.
|
|
13
|
+
const separator = argv.indexOf("--");
|
|
14
|
+
const flags = separator === -1 ? argv : argv.slice(0, separator);
|
|
15
|
+
const readOnly = flags.some((flag) => flag === "--help" || flag === "-h")
|
|
16
|
+
|| argv[0] === "help"
|
|
17
|
+
|| (argv[0] === "stack" && argv[1] === "help")
|
|
18
|
+
|| (argv[0] === "run" && (argv[1] === "inspect" || argv[1] === "help"));
|
|
19
|
+
const delivery = deliveryProcessArgs || readOnly ? Promise.resolve() : ensureRunDelivery().catch(() => {});
|
|
12
20
|
|
|
13
21
|
delivery.then(() => run(argv)).then((code) => {
|
|
14
22
|
if (typeof code === "number") process.exitCode = code;
|
|
Binary file
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.63.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.63.5",
|
|
3
|
+
"source_commit": "fc9bbe22c3fe8840724c30eae63515cfd70cce9d",
|
|
4
|
+
"source_sha256": "bed3488d03f054f8c30c49d28bb3d5783d081363f2d9c69063164c79cc70feee",
|
|
5
5
|
"target": "darwin-arm64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "e6e438fc3e98628a4e5f04a0100c96ba694f75d78210fbebe3623af7b0f085d7"
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.63.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.63.5",
|
|
3
|
+
"source_commit": "fc9bbe22c3fe8840724c30eae63515cfd70cce9d",
|
|
4
|
+
"source_sha256": "bed3488d03f054f8c30c49d28bb3d5783d081363f2d9c69063164c79cc70feee",
|
|
5
5
|
"target": "linux-x64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "b224066b8aa6ae1831282fbfd7e60135864dea69847a7187df81bd171674456a"
|
|
7
7
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
|
-
"release_version": "1.63.
|
|
4
|
-
"source_commit": "
|
|
5
|
-
"source_sha256": "
|
|
3
|
+
"release_version": "1.63.5",
|
|
4
|
+
"source_commit": "fc9bbe22c3fe8840724c30eae63515cfd70cce9d",
|
|
5
|
+
"source_sha256": "bed3488d03f054f8c30c49d28bb3d5783d081363f2d9c69063164c79cc70feee",
|
|
6
6
|
"targets": {
|
|
7
7
|
"darwin-arm64": {
|
|
8
8
|
"goos": "darwin",
|
|
9
9
|
"goarch": "arm64",
|
|
10
10
|
"binary": "daemon/bin/darwin-arm64/botbuddy-daemon",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "e6e438fc3e98628a4e5f04a0100c96ba694f75d78210fbebe3623af7b0f085d7",
|
|
12
12
|
"mode": "0755"
|
|
13
13
|
},
|
|
14
14
|
"linux-x64": {
|
|
15
15
|
"goos": "linux",
|
|
16
16
|
"goarch": "amd64",
|
|
17
17
|
"binary": "daemon/bin/linux-x64/botbuddy-daemon",
|
|
18
|
-
"sha256": "
|
|
18
|
+
"sha256": "b224066b8aa6ae1831282fbfd7e60135864dea69847a7187df81bd171674456a",
|
|
19
19
|
"mode": "0755"
|
|
20
20
|
}
|
|
21
21
|
}
|
package/package.json
CHANGED
package/src/run.mjs
CHANGED
|
@@ -280,7 +280,7 @@ function terminalOutcomesAgree(receipt, remote) {
|
|
|
280
280
|
// Reconcile only evidence the current OS user and owning BotBuddy agent can
|
|
281
281
|
// read. This is intentionally advisory: an inspector must never terminate a
|
|
282
282
|
// worker, replay a callback, or make a retry safe merely by observing a file.
|
|
283
|
-
|
|
283
|
+
async function inspectRunEvidence(runId, {
|
|
284
284
|
call = callToolJson,
|
|
285
285
|
read = readFile,
|
|
286
286
|
now = Date.now(),
|
|
@@ -292,8 +292,12 @@ export async function inspectRun(runId, {
|
|
|
292
292
|
return { schema_version: RUN_SCHEMA_VERSION, classification: "rejected", rerun_safe: false, cancellation_safe: false, error: "run_id_required" };
|
|
293
293
|
}
|
|
294
294
|
const id = runId.trim();
|
|
295
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(id)) {
|
|
296
|
+
return { schema_version: RUN_SCHEMA_VERSION, classification: "rejected", rerun_safe: false, cancellation_safe: false, error: "run_id_invalid" };
|
|
297
|
+
}
|
|
298
|
+
const localReceiptPath = receiptFile(id);
|
|
295
299
|
const [receiptRaw, contextRaw] = await Promise.all([
|
|
296
|
-
read(
|
|
300
|
+
read(localReceiptPath, "utf8").catch(() => null),
|
|
297
301
|
read(contextFile(id), "utf8").catch(() => null),
|
|
298
302
|
]);
|
|
299
303
|
const receipt = typeof receiptRaw === "string" ? parseOwnedRunFile(receiptRaw, id) : null;
|
|
@@ -339,7 +343,21 @@ export async function inspectRun(runId, {
|
|
|
339
343
|
rerun_safe: false,
|
|
340
344
|
cancellation_safe: false,
|
|
341
345
|
source_bound: sourceBound,
|
|
342
|
-
local: {
|
|
346
|
+
local: {
|
|
347
|
+
status: receipt.status,
|
|
348
|
+
exit_code: receipt.exit_code ?? null,
|
|
349
|
+
...(sourceBound ? {
|
|
350
|
+
receipt_path: localReceiptPath,
|
|
351
|
+
source_sha: receipt.source_sha,
|
|
352
|
+
command_hash: receipt.command_hash,
|
|
353
|
+
} : {}),
|
|
354
|
+
},
|
|
355
|
+
...(sourceBound ? {
|
|
356
|
+
recovery: {
|
|
357
|
+
action: "read_local_receipt",
|
|
358
|
+
message: "Read local.receipt_path for the original workload evidence and bounded_log. Preserve its source identity and exit code; do not rerun or cancel to repair missing telemetry. Preserve and investigate any conflicting remote result.",
|
|
359
|
+
},
|
|
360
|
+
} : {}),
|
|
343
361
|
remote,
|
|
344
362
|
};
|
|
345
363
|
}
|
|
@@ -369,6 +387,35 @@ export async function inspectRun(runId, {
|
|
|
369
387
|
remote,
|
|
370
388
|
};
|
|
371
389
|
}
|
|
390
|
+
|
|
391
|
+
/** Add advice only from the inspector's public evidence; never from raw output. */
|
|
392
|
+
export async function inspectRun(runId, options = {}) {
|
|
393
|
+
const result = await inspectRunEvidence(runId, options);
|
|
394
|
+
const terminal = result.source_bound === true;
|
|
395
|
+
const running = result.classification === "running";
|
|
396
|
+
return {
|
|
397
|
+
...result,
|
|
398
|
+
recovery: {
|
|
399
|
+
schema_version: 1,
|
|
400
|
+
...(terminal ? result.recovery : {}),
|
|
401
|
+
action: terminal ? "read_local_receipt" : running ? "await_original_result" : "preserve_and_escalate",
|
|
402
|
+
started: terminal && result.local.status === "success" ? "yes" : "unknown",
|
|
403
|
+
workload: terminal && ["success", "failure"].includes(result.local.status) ? "exited" : "unknown",
|
|
404
|
+
retry: "unsafe",
|
|
405
|
+
receipt_available: terminal,
|
|
406
|
+
evidence: terminal ? "source_bound_terminal_receipt" : running ? "fresh_local_owner_observation" : "insufficient_or_conflicting_evidence",
|
|
407
|
+
// An original pending tool handle is not a shell command. Do not invent
|
|
408
|
+
// a waiter or encourage re-inspection as a polling loop.
|
|
409
|
+
recovery_argv: null,
|
|
410
|
+
do_not: ["rerun", "cancel", "poll"],
|
|
411
|
+
...(!terminal ? {
|
|
412
|
+
message: running
|
|
413
|
+
? "Keep the original command result pending. A live owner does not prove that its child started or stalled. Consume that owner's terminal receipt; do not rerun or cancel to repair telemetry."
|
|
414
|
+
: "Preserve the original command and identifiers. Evidence is unknown or conflicts; report the gap without rerunning, cancelling, or polling. No safe recovery command can be derived from this observation.",
|
|
415
|
+
} : {}),
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
}
|
|
372
419
|
function cancellationPath(contextFile) { return `${contextFile}.cancelled`; }
|
|
373
420
|
|
|
374
421
|
async function markCancellation(contextFile, runId) {
|
package/src/stack.mjs
CHANGED
|
@@ -105,6 +105,15 @@ export const EXIT = Object.freeze({
|
|
|
105
105
|
CLEANUP_FAILED: 8, // child completed but signed physical reap was not proven
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
+
const STACK_READINESS_HELP = `${bold("READINESS")}
|
|
109
|
+
An active lease proves infrastructure activation. It does not prove application readiness:
|
|
110
|
+
the child may still be initializing its schema, runtime configuration, or worktree locks.
|
|
111
|
+
Consume the application's acknowledged ready receipt before starting another consumer.
|
|
112
|
+
--callback-port provides callback transport; the application must configure its callback URLs.
|
|
113
|
+
A retained idle lease is still active; idle_since/cleanup.idle distinguish reuse from live work.
|
|
114
|
+
Preserve the original stack-run result. Do not rerun or release a live workload to recover output.
|
|
115
|
+
Use bb run inspect <run_id> for a workload launched by bb run; its run ID differs from a lease ID.`;
|
|
116
|
+
|
|
108
117
|
export const STACK_HELP = `${bold("botbuddy stack")} — one command for a batch-scoped local stack lease (BOT-1218)
|
|
109
118
|
|
|
110
119
|
${bold("USAGE")}
|
|
@@ -311,10 +320,33 @@ ${bold("HOST_CAPACITY_SOURCE_STALE")} (BOT-2024)
|
|
|
311
320
|
sustained (no beacon ever or >= 10 min) → report BLOCKED quoting \`stale_since\`; a Helper
|
|
312
321
|
check/restart is the right ask. A single refusal is never evidence of an outage.
|
|
313
322
|
|
|
323
|
+
${STACK_READINESS_HELP}
|
|
324
|
+
|
|
314
325
|
${bold("EXAMPLE")}
|
|
315
326
|
# request a per-worktree stack for this batch, run a lane against it, then reap it
|
|
316
327
|
botbuddy stack run --repo botbuddy-web --ticket BOT-1346 -- pnpm test:integration`;
|
|
317
328
|
|
|
329
|
+
const STACK_COMMANDS = new Set(["up", "run", "status", "touch", "done", "cancel"]);
|
|
330
|
+
|
|
331
|
+
/** Scoped help is assembled from the canonical text, so flags cannot drift. */
|
|
332
|
+
export function stackCommandHelp(command) {
|
|
333
|
+
const headings = {
|
|
334
|
+
up: ["up OPTIONS", "GLOBAL", "EXIT CODES", "IDLE LEASES", "HOST_CAPACITY_SOURCE_STALE"],
|
|
335
|
+
run: ["run OPTIONS", "GLOBAL", "EXIT CODES", "IDLE LEASES", "REMOTE LEASES", "HOST_CAPACITY_SOURCE_STALE"],
|
|
336
|
+
done: ["done OPTIONS", "GLOBAL", "EXIT CODES", "IDLE LEASES"],
|
|
337
|
+
cancel: ["GLOBAL", "EXIT CODES"],
|
|
338
|
+
status: ["GLOBAL", "EXIT CODES"],
|
|
339
|
+
touch: ["GLOBAL", "EXIT CODES", "IDLE LEASES"],
|
|
340
|
+
};
|
|
341
|
+
// The OPTIONS headings intentionally use lower-case command names.
|
|
342
|
+
const all = STACK_HELP.split(/\n(?=\u001b\[1m)/);
|
|
343
|
+
const selected = all.filter((section) => headings[command]?.some((heading) => section.startsWith(bold(heading))));
|
|
344
|
+
const usage = command === "run" ? "[options] -- <executable> [args...]"
|
|
345
|
+
: command === "up" ? "[options]" : "<lease_id> [options]";
|
|
346
|
+
const detail = command === "cancel" ? "\n Cancel gives up a queued lease's place (equivalent to done --reap).\n" : "";
|
|
347
|
+
return `${bold(`botbuddy stack ${command}`)} ${usage}${detail}\n\n${selected.join("\n")}\n\n${STACK_READINESS_HELP}\n Use bb stack --help for all commands and options.`;
|
|
348
|
+
}
|
|
349
|
+
|
|
318
350
|
// ── pure helpers (unit-tested) ───────────────────────────────────────────────
|
|
319
351
|
|
|
320
352
|
/** Derive the event-stream base URL from the MCP server URL. */
|
|
@@ -4658,6 +4690,12 @@ export async function cmdStack(argv, { report = reportStackRunOutcome, outcomeCh
|
|
|
4658
4690
|
process.stdout.write(STACK_HELP + "\n");
|
|
4659
4691
|
return EXIT.OK;
|
|
4660
4692
|
}
|
|
4693
|
+
const divider = argv.indexOf("--");
|
|
4694
|
+
const flags = divider === -1 ? argv.slice(1) : argv.slice(1, divider);
|
|
4695
|
+
if (STACK_COMMANDS.has(argv[0]) && flags.some((flag) => flag === "--help" || flag === "-h")) {
|
|
4696
|
+
process.stdout.write(stackCommandHelp(argv[0]) + "\n");
|
|
4697
|
+
return EXIT.OK;
|
|
4698
|
+
}
|
|
4661
4699
|
const { command, leaseId, opts, errors, childArgv } = parseStackArgs(argv);
|
|
4662
4700
|
if (errors.length) {
|
|
4663
4701
|
for (const e of errors) process.stderr.write(`${yellow("⚠")} stack: ${e}\n`);
|