@botbuddy/cli 1.60.3 → 1.60.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/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/stack-run-outcome.mjs +2 -2
- package/src/stack.mjs +19 -3
- package/src/wait-core.mjs +17 -3
|
Binary file
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.60.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.60.5",
|
|
3
|
+
"source_commit": "a49feea0a58695a606269a3e4ccd1f94afba7114",
|
|
4
|
+
"source_sha256": "173f7953ea95f0facc3230dc993d4b64aab54037d8a89dfdafe044ad66063bb7",
|
|
5
5
|
"target": "darwin-arm64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "a6b56a02eca424bcc5dc408b90f1d831456a241d04dcc59adc2210b071698e49"
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.60.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.60.5",
|
|
3
|
+
"source_commit": "a49feea0a58695a606269a3e4ccd1f94afba7114",
|
|
4
|
+
"source_sha256": "173f7953ea95f0facc3230dc993d4b64aab54037d8a89dfdafe044ad66063bb7",
|
|
5
5
|
"target": "linux-x64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "58a715446ff176ec2b65d583e4d6a6bf3ce81ab48dc0c030da52c61a019523b4"
|
|
7
7
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
|
-
"release_version": "1.60.
|
|
4
|
-
"source_commit": "
|
|
5
|
-
"source_sha256": "
|
|
3
|
+
"release_version": "1.60.5",
|
|
4
|
+
"source_commit": "a49feea0a58695a606269a3e4ccd1f94afba7114",
|
|
5
|
+
"source_sha256": "173f7953ea95f0facc3230dc993d4b64aab54037d8a89dfdafe044ad66063bb7",
|
|
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": "a6b56a02eca424bcc5dc408b90f1d831456a241d04dcc59adc2210b071698e49",
|
|
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": "58a715446ff176ec2b65d583e4d6a6bf3ce81ab48dc0c030da52c61a019523b4",
|
|
19
19
|
"mode": "0755"
|
|
20
20
|
}
|
|
21
21
|
}
|
package/package.json
CHANGED
|
@@ -134,13 +134,13 @@ export function buildStackRunOutcomePayload({ fields = {}, exitCode, runId = ran
|
|
|
134
134
|
* credential the lease calls used); without one there is nothing to authenticate
|
|
135
135
|
* with and nothing is sent. Never throws; resolves within `timeoutMs`.
|
|
136
136
|
*/
|
|
137
|
-
export async function reportStackRunOutcome({ fields, exitCode, opts, env = process.env, durationMs = null, channel, timeoutMs = STACK_RUN_OUTCOME_TIMEOUT_MS }) {
|
|
137
|
+
export async function reportStackRunOutcome({ fields, exitCode, runId, opts, env = process.env, durationMs = null, channel, timeoutMs = STACK_RUN_OUTCOME_TIMEOUT_MS }) {
|
|
138
138
|
// Test suites opt out so a spawned real CLI never records a fixture run.
|
|
139
139
|
if (env?.[STACK_RUN_OUTCOME_OPT_OUT_ENV] === "off") return { sent: false, reason: "disabled" };
|
|
140
140
|
if (!channel?.auth || typeof channel.callTool !== "function") return { sent: false, reason: "no_credential" };
|
|
141
141
|
let payload;
|
|
142
142
|
try {
|
|
143
|
-
payload = buildStackRunOutcomePayload({ fields, exitCode, opts, env, durationMs });
|
|
143
|
+
payload = buildStackRunOutcomePayload({ fields, exitCode, runId, opts, env, durationMs });
|
|
144
144
|
} catch {
|
|
145
145
|
return { sent: false, reason: "payload_error" };
|
|
146
146
|
}
|
package/src/stack.mjs
CHANGED
|
@@ -188,6 +188,8 @@ ${bold("done OPTIONS")}
|
|
|
188
188
|
${bold("run OPTIONS")}
|
|
189
189
|
--repo <repo> Required approved repository name.
|
|
190
190
|
--ticket <BOT-123> Required ticket for the batch.
|
|
191
|
+
--run-id <UUID> Optional caller-chosen run identity. Choose it before dispatch to arm
|
|
192
|
+
bb wait 'stack-run:id=<UUID>'; the completion signal carries this run's exit code.
|
|
191
193
|
--stack-path <relative> Optional PREPARED isolated stack dir. Omit it (the norm): run
|
|
192
194
|
materializes .botbuddy/stacks/<project_id>/ from this worktree's
|
|
193
195
|
supabase/ tree with a distinct project_id + remapped ports and
|
|
@@ -358,7 +360,7 @@ export function parseStackArgs(argv) {
|
|
|
358
360
|
const childArgv = divider === -1 ? [] : rawRest.slice(divider + 1);
|
|
359
361
|
const opts = {
|
|
360
362
|
slot: null, host: null, repo: null, ticket: null, ticketUrl: null,
|
|
361
|
-
prId: null, prUrl: null, purpose: null, idleTtl: null, stackPath: ".", stackPathExplicit: false,
|
|
363
|
+
prId: null, prUrl: null, purpose: null, runId: null, idleTtl: null, stackPath: ".", stackPathExplicit: false,
|
|
362
364
|
timeout: DEFAULT_TIMEOUT_SEC, provisionTimeout: null, reapTimeout: 300, hardTtl: null,
|
|
363
365
|
connectionFile: null, noWait: false, claimOnly: false, localExec: false, keepVolumes: false, waitCapacity: null, holdOnTimeout: null,
|
|
364
366
|
dockerContext: null, dockerEndpoint: null,
|
|
@@ -390,6 +392,7 @@ export function parseStackArgs(argv) {
|
|
|
390
392
|
case "--pr": if (need(a, rest[i + 1])) opts.prId = rest[++i]; break;
|
|
391
393
|
case "--pr-url": if (need(a, rest[i + 1])) opts.prUrl = rest[++i]; break;
|
|
392
394
|
case "--purpose": if (need(a, rest[i + 1])) opts.purpose = rest[++i]; break;
|
|
395
|
+
case "--run-id": if (need(a, rest[i + 1])) opts.runId = rest[++i]; break;
|
|
393
396
|
case "--stack-path": if (need(a, rest[i + 1])) { opts.stackPath = rest[++i]; opts.stackPathExplicit = true; } break;
|
|
394
397
|
case "--idle-ttl": {
|
|
395
398
|
if (need(a, rest[i + 1])) {
|
|
@@ -518,6 +521,15 @@ export function parseStackArgs(argv) {
|
|
|
518
521
|
} else if (opts.bundleInclude.length && opts.localOnly) {
|
|
519
522
|
errors.push("--bundle-include ships files in the remote-lease bundle, and --local-only builds no bundle; drop one of them");
|
|
520
523
|
}
|
|
524
|
+
if (opts.runId != null) {
|
|
525
|
+
if (command !== "run") {
|
|
526
|
+
errors.push("--run-id is valid only with `stack run`");
|
|
527
|
+
} else if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(String(opts.runId))) {
|
|
528
|
+
errors.push("--run-id must be a canonical UUID");
|
|
529
|
+
} else {
|
|
530
|
+
opts.runId = String(opts.runId).toLowerCase();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
521
533
|
opts.bundleInclude = [...new Set(opts.bundleInclude)];
|
|
522
534
|
opts.execution = opts.remote ? "remote" : opts.localOnly ? "local" : null;
|
|
523
535
|
if (typeof opts.stackPath !== "string" || !opts.stackPath.trim() || opts.stackPath.startsWith("/") ||
|
|
@@ -4546,12 +4558,16 @@ export function runReceiptFields(result) {
|
|
|
4546
4558
|
export async function cmdRun(opts, childArgv, {
|
|
4547
4559
|
lifecycle = runStackLifecycle, report = reportStackRunOutcome, now = Date.now, env = process.env,
|
|
4548
4560
|
} = {}) {
|
|
4561
|
+
// A caller that needs a wait selects this immutable UUID before dispatch.
|
|
4562
|
+
// Ordinary foreground runs still receive a unique telemetry id, but it is not
|
|
4563
|
+
// retrospectively advertised as an armable completion contract.
|
|
4564
|
+
const runId = opts.runId ?? randomUUID();
|
|
4549
4565
|
const startedAt = now();
|
|
4550
4566
|
const result = await lifecycle(opts, childArgv);
|
|
4551
4567
|
const fields = runReceiptFields(result);
|
|
4552
4568
|
const code = emit(buildReceipt(fields), opts, result.exitCode);
|
|
4553
4569
|
try {
|
|
4554
|
-
await report({ fields: stackRunOutcomeFields(result), exitCode: code, opts, env, durationMs: now() - startedAt, channel: result?.[OUTCOME_CHANNEL] ?? null });
|
|
4570
|
+
await report({ fields: stackRunOutcomeFields(result), exitCode: code, runId, opts, env, durationMs: now() - startedAt, channel: result?.[OUTCOME_CHANNEL] ?? null });
|
|
4555
4571
|
} catch { /* best-effort: the receipt and exit code are already final */ }
|
|
4556
4572
|
return code;
|
|
4557
4573
|
}
|
|
@@ -4600,7 +4616,7 @@ export async function cmdStack(argv, { report = reportStackRunOutcome, outcomeCh
|
|
|
4600
4616
|
const code = emit(buildReceipt(fields), opts, EXIT.INVALID);
|
|
4601
4617
|
// BOT-2467: a refused `stack run` is recorded as an `invocation` outcome (no lease).
|
|
4602
4618
|
if (command === "run") {
|
|
4603
|
-
try { await report({ fields, exitCode: code, opts, env: process.env, durationMs: 0, channel: await outcomeChannel(argv) }); } catch { /* best-effort */ }
|
|
4619
|
+
try { await report({ fields, exitCode: code, runId: opts.runId ?? undefined, opts, env: process.env, durationMs: 0, channel: await outcomeChannel(argv) }); } catch { /* best-effort */ }
|
|
4604
4620
|
}
|
|
4605
4621
|
process.exitCode = code;
|
|
4606
4622
|
return code;
|
package/src/wait-core.mjs
CHANGED
|
@@ -85,9 +85,9 @@ export const WAIT_SUPERSEDED_SIGNAL_TYPE = "wait_superseded";
|
|
|
85
85
|
const HOST_APPROVAL_HOST_KEY_RE = /^bbh-[0-9a-f]{24}$/;
|
|
86
86
|
|
|
87
87
|
// The USER-ARMABLE spine signal_type allowlist, for the `event:` escape hatch. The full
|
|
88
|
-
// public.agent_signal_type enum
|
|
89
|
-
//
|
|
90
|
-
//
|
|
88
|
+
// public.agent_signal_type enum also has control-only and dedicated-grammar types (for
|
|
89
|
+
// example `wait_superseded` and BOT-2629's `stack_run`); they are intentionally excluded
|
|
90
|
+
// here so raw event conditions cannot bypass their narrower matching contracts.
|
|
91
91
|
export const SIGNAL_TYPES = Object.freeze([
|
|
92
92
|
"ci_run",
|
|
93
93
|
"pr_review",
|
|
@@ -110,6 +110,7 @@ export const SIGNAL_TYPES = Object.freeze([
|
|
|
110
110
|
]);
|
|
111
111
|
|
|
112
112
|
const LOCK_SUBTYPES = Object.freeze(["playwright_lane", "vite_port", "backend_port", "supabase_local"]);
|
|
113
|
+
const CANONICAL_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
113
114
|
|
|
114
115
|
// ---------------------------------------------------------------------------
|
|
115
116
|
// Condition grammar: `TYPE:key=val,key=val`
|
|
@@ -194,6 +195,16 @@ const VALIDATORS = {
|
|
|
194
195
|
if (!p.id) throw new Error("test-run needs id=<run_id>");
|
|
195
196
|
return { runId: String(p.id) };
|
|
196
197
|
},
|
|
198
|
+
// BOT-2629: this is deliberately a dedicated condition rather than a raw
|
|
199
|
+
// event escape hatch. The UUID is chosen before dispatch and the exact subject
|
|
200
|
+
// prevents another run on the same idle/re-adopted lease waking this waiter.
|
|
201
|
+
"stack-run": (p) => {
|
|
202
|
+
const unexpected = Object.keys(p).filter((key) => key !== "id");
|
|
203
|
+
if (unexpected.length) throw new Error(`stack-run does not accept ${unexpected.join(", ")}`);
|
|
204
|
+
const id = String(p.id ?? "").trim().toLowerCase();
|
|
205
|
+
if (!CANONICAL_UUID_RE.test(id)) throw new Error("stack-run needs id=<canonical run UUID>");
|
|
206
|
+
return { id };
|
|
207
|
+
},
|
|
197
208
|
// BOT-1063 — guidance: park on a command-post guidance RESPONSE to a request
|
|
198
209
|
// this agent raised (pairs with request_guidance). The signal is
|
|
199
210
|
// recipient-scoped to the requester and keyed by the request id.
|
|
@@ -1112,6 +1123,9 @@ function conditionMatchesSignal(condition, signal, waitSessionId) {
|
|
|
1112
1123
|
prReviewFiltersMatch(signal, params);
|
|
1113
1124
|
case "test-run":
|
|
1114
1125
|
return signal.signal_type === "test_run" && signal.subject_key === `run:${params.runId}`;
|
|
1126
|
+
case "stack-run":
|
|
1127
|
+
return signal.signal_type === "stack_run" && signal.subject_key === `stack-run:${params.id}` &&
|
|
1128
|
+
signal.payload?.run_id === params.id && Number.isInteger(signal.payload?.exit_code);
|
|
1115
1129
|
case "guidance":
|
|
1116
1130
|
return signal.signal_type === "guidance_response" && signal.subject_key === params.request;
|
|
1117
1131
|
case "lease":
|