@botbuddy/cli 1.45.2 → 1.45.4
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/package.json +1 -1
- package/src/agent-doctor.mjs +6 -0
- package/src/agent-session.mjs +0 -0
- package/src/api.mjs +7 -1
- package/src/wait-core.mjs +18 -7
- package/src/botbuddy-release-repair.json +0 -1
package/package.json
CHANGED
package/src/agent-doctor.mjs
CHANGED
|
@@ -77,6 +77,12 @@ export function repairFailureRecovery(code, error = null) {
|
|
|
77
77
|
if (code === "client_key_unavailable") {
|
|
78
78
|
return "the client key could not be read (locked or failing credential store); unlock it, then run `bb doctor --fix`";
|
|
79
79
|
}
|
|
80
|
+
// BOT-1787: this is a server-side identity conflict, not an authentication
|
|
81
|
+
// failure. Preserve the real remedy so a healthy credential is never needlessly
|
|
82
|
+
// rotated; `bb setup` clears this worktree's cache and re-registers it.
|
|
83
|
+
if (code === "agent_tenant_immutable") {
|
|
84
|
+
return "this worktree's agent identity belongs to another tenant; run `bb setup` to register this tenant, then `bb doctor --fix`";
|
|
85
|
+
}
|
|
80
86
|
// BOT-1750 (Codex R9 P2): `session_mint_unauthorized` means a locally-unexpired
|
|
81
87
|
// client key was revoked/rejected server-side — only a fresh login replaces it, so
|
|
82
88
|
// point at `bb login`, not a binding repair (the binding is valid).
|
package/src/agent-session.mjs
CHANGED
|
Binary file
|
package/src/api.mjs
CHANGED
|
@@ -445,7 +445,13 @@ export async function callToolJson(toolName, args = {}, { fetchImpl = fetch, sig
|
|
|
445
445
|
}
|
|
446
446
|
let payload;
|
|
447
447
|
try { payload = await res.json(); } catch { return { ok: false, status: res.status, error: "invalid_json" }; }
|
|
448
|
-
if (payload.error?.message)
|
|
448
|
+
if (payload.error?.message) {
|
|
449
|
+
// Typed domain errors retain their verdict inside the HTTP-200 JSON-RPC
|
|
450
|
+
// envelope. Do not infer credential failure from an identity conflict.
|
|
451
|
+
const code = payload.error.data?.code;
|
|
452
|
+
return { ok: false, status: res.status, error: payload.error.message,
|
|
453
|
+
...(typeof code === "string" && code ? { code } : {}) };
|
|
454
|
+
}
|
|
449
455
|
const text = payload.result?.content?.map((c) => c.text).filter(Boolean).join("\n") ?? "";
|
|
450
456
|
let data;
|
|
451
457
|
try { data = text ? JSON.parse(text) : {}; } catch { data = { raw: text }; }
|
package/src/wait-core.mjs
CHANGED
|
@@ -234,8 +234,9 @@ const VALIDATORS = {
|
|
|
234
234
|
// BOT-1507 — scope=next adds a fourth, LIVE-ONLY selector: it names no run or PR
|
|
235
235
|
// (they must already exist at arm time) and wakes on the first terminal CI run for
|
|
236
236
|
// the repo delivered AFTER the wait was armed — the post-merge / cron train case.
|
|
237
|
-
// Optional branch=/workflow= filter that first run;
|
|
238
|
-
// scope=next
|
|
237
|
+
// Optional branch=/workflow= filter that first run; branch is valid only with
|
|
238
|
+
// scope=next. A workflow also pins a level-triggered sha= recovery to one
|
|
239
|
+
// canonical workflow identity.
|
|
239
240
|
// BOT-1685 — scope=next also accepts an optional sha= FILTER (distinct from the
|
|
240
241
|
// level-triggered sha= selector): a wait armed BEFORE re-dispatching a run for a
|
|
241
242
|
// known commit then wakes on the NEXT terminal run for THAT commit, instead of
|
|
@@ -253,10 +254,11 @@ const VALIDATORS = {
|
|
|
253
254
|
if ((shaIsNextFilter ? selectors.length - 1 : selectors.length) !== 1) {
|
|
254
255
|
throw new Error("ci needs exactly one of scope=latest|next | run_id=<id> | sha=<sha>");
|
|
255
256
|
}
|
|
256
|
-
const
|
|
257
|
+
const hasBranchFilter = p.branch !== undefined;
|
|
258
|
+
const hasWorkflowFilter = p.workflow !== undefined;
|
|
257
259
|
if (p.scope !== undefined) {
|
|
258
260
|
if (p.scope === "latest") {
|
|
259
|
-
if (
|
|
261
|
+
if (hasBranchFilter || hasWorkflowFilter) throw new Error("ci branch=/workflow= are only valid with scope=next");
|
|
260
262
|
if (p.pr === undefined || !/^\d+$/.test(String(p.pr).trim())) {
|
|
261
263
|
throw new Error("ci scope=latest needs pr=<number>");
|
|
262
264
|
}
|
|
@@ -278,13 +280,17 @@ const VALIDATORS = {
|
|
|
278
280
|
}
|
|
279
281
|
throw new Error("ci scope must be 'latest' or 'next' (or use run_id=/sha=)");
|
|
280
282
|
}
|
|
281
|
-
if (
|
|
283
|
+
if (hasBranchFilter || (hasWorkflowFilter && p.sha === undefined)) {
|
|
284
|
+
throw new Error("ci branch= is only valid with scope=next; workflow= also accepts an exact sha selector");
|
|
285
|
+
}
|
|
282
286
|
if (p.run_id !== undefined) {
|
|
283
287
|
if (!String(p.run_id).trim()) throw new Error("ci run_id must be non-empty");
|
|
284
288
|
return { repo, scope: "run_id", runId: String(p.run_id).trim(), pr: p.pr != null ? String(p.pr).trim() : null };
|
|
285
289
|
}
|
|
286
290
|
if (!String(p.sha).trim()) throw new Error("ci sha must be non-empty");
|
|
287
|
-
|
|
291
|
+
const workflow = p.workflow === undefined ? null : String(p.workflow).trim();
|
|
292
|
+
if (p.workflow !== undefined && workflow === "") throw new Error("ci workflow= must be non-empty");
|
|
293
|
+
return { repo, scope: "sha", sha: String(p.sha).trim(), workflow, pr: p.pr != null ? String(p.pr).trim() : null };
|
|
288
294
|
},
|
|
289
295
|
"staging-green"(p) {
|
|
290
296
|
const repo = p.repo != null ? String(p.repo).trim() : null;
|
|
@@ -986,7 +992,12 @@ function conditionMatchesSignal(condition, signal, waitSessionId) {
|
|
|
986
992
|
// Repo is pinned via the payload (subject may be run:<id> for uncorrelated runs).
|
|
987
993
|
if (String(signal.payload?.repo ?? "").toLowerCase() !== params.repo.toLowerCase()) return false;
|
|
988
994
|
if (params.scope === "run_id") return String(signal.payload?.run_id) === params.runId;
|
|
989
|
-
if (params.scope === "sha")
|
|
995
|
+
if (params.scope === "sha") {
|
|
996
|
+
if (signal.payload?.head_sha !== params.sha) return false;
|
|
997
|
+
if (params.workflow == null) return true;
|
|
998
|
+
const workflow = signal.payload?.workflow_name;
|
|
999
|
+
return typeof workflow === "string" && workflow.trim().toLowerCase() === params.workflow.trim().toLowerCase();
|
|
1000
|
+
}
|
|
990
1001
|
if (params.scope === "next") {
|
|
991
1002
|
// BOT-1507 — live-only: the first TERMINAL run for the repo. Never keys on
|
|
992
1003
|
// subject_key (it is run:<id> for post-merge runs and owner/repo#n for PR
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"schema_version":1,"source_version":"1.45.1","source_identity":"2fa051c4d0ee5cb09fda8d9e15c4952af94d701a6a43e98c8831912040cb41f2"}
|