@botbuddy/cli 1.45.3 → 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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/wait-core.mjs +18 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbuddy/cli",
3
- "version": "1.45.3",
3
+ "version": "1.45.4",
4
4
  "description": "BotBuddy — Swarm coordination CLI for multi-agent workflows",
5
5
  "type": "module",
6
6
  "bin": {
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; both are valid ONLY with
238
- // scope=next (a run_id/sha/latest wait already names its run).
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 hasFilter = p.branch !== undefined || p.workflow !== undefined;
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 (hasFilter) throw new Error("ci branch=/workflow= are only valid with scope=next");
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 (hasFilter) throw new Error("ci branch=/workflow= are only valid with scope=next");
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
- return { repo, scope: "sha", sha: String(p.sha).trim(), pr: p.pr != null ? String(p.pr).trim() : null };
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") return signal.payload?.head_sha === params.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