@botbuddy/cli 1.40.5 → 1.40.6

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/stack.mjs +24 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbuddy/cli",
3
- "version": "1.40.5",
3
+ "version": "1.40.6",
4
4
  "description": "BotBuddy — Swarm coordination CLI for multi-agent workflows",
5
5
  "type": "module",
6
6
  "bin": {
package/src/stack.mjs CHANGED
@@ -88,6 +88,8 @@ ${bold("up OPTIONS")}
88
88
  --idle-ttl <seconds> Idle seconds before the reaper STOPS an unused stack (default 1800).
89
89
  --timeout <seconds> Max seconds to park for capacity before giving up (default ${DEFAULT_TIMEOUT_SEC}).
90
90
  --no-wait If the host is full, print the queue position and exit (don't park).
91
+ --claim-only LOCAL-EXEC only: wait for and hold a provisioning lease without starting
92
+ Supabase. Resume with the identical \`stack up --local-exec\` command.
91
93
  --wait-capacity <dur> Opt-in (BOT-2024): on a TRANSIENT HOST_CAPACITY_SOURCE_STALE refusal, park
92
94
  on the server's \`bb wait 'capacity:…'\` up to <dur> (90s, 10m, 1h) and
93
95
  re-attempt admission once when it matches. Sustained refusals, the
@@ -189,7 +191,7 @@ export function parseStackArgs(argv) {
189
191
  slot: null, host: null, repo: null, ticket: null, ticketUrl: null,
190
192
  prId: null, prUrl: null, purpose: null, idleTtl: null, stackPath: ".", stackPathExplicit: false,
191
193
  timeout: DEFAULT_TIMEOUT_SEC, provisionTimeout: null, reapTimeout: 300, hardTtl: null,
192
- connectionFile: null, noWait: false, localExec: false, waitCapacity: null,
194
+ connectionFile: null, noWait: false, claimOnly: false, localExec: false, waitCapacity: null,
193
195
  dockerContext: null, dockerEndpoint: null,
194
196
  disposition: "destroy", json: false, receiptMaxBytes: DEFAULT_RECEIPT_MAX_BYTES,
195
197
  };
@@ -271,6 +273,7 @@ export function parseStackArgs(argv) {
271
273
  break;
272
274
  }
273
275
  case "--no-wait": opts.noWait = true; break;
276
+ case "--claim-only": opts.claimOnly = true; break;
274
277
  case "--local-exec": opts.localExec = true; break;
275
278
  case "--docker-context": if (need(a, rest[i + 1])) opts.dockerContext = rest[++i]; break;
276
279
  case "--docker-endpoint": if (need(a, rest[i + 1])) opts.dockerEndpoint = rest[++i]; break;
@@ -305,6 +308,9 @@ export function parseStackArgs(argv) {
305
308
  if (opts.waitCapacity != null && !["up", "run"].includes(command)) {
306
309
  errors.push("--wait-capacity is valid only with `stack up` or `stack run`");
307
310
  }
311
+ if (opts.claimOnly && (command !== "up" || !opts.localExec)) {
312
+ errors.push("--claim-only is valid only with `stack up --local-exec`");
313
+ }
308
314
  if ((opts.dockerContext || opts.dockerEndpoint) && !(["up", "done"].includes(command) && opts.localExec)) {
309
315
  errors.push("--docker-context and --docker-endpoint are valid only with `stack up --local-exec` or `stack done --local-exec`");
310
316
  }
@@ -1696,6 +1702,23 @@ export async function cmdUp(opts, {
1696
1702
  }
1697
1703
  reportLaneStatus({ phase: "testing", reason: "provisioning" });
1698
1704
 
1705
+ // BOT-2089: an expensive hermetic preparation (template restore) must not lose
1706
+ // an admission slot after the queue grants it. Keep this deliberately narrow:
1707
+ // only a local-exec caller may hold an unreserved provisioning lease, and it
1708
+ // must resume through the normal `stack up` path that reserves before start.
1709
+ if (opts.claimOnly) {
1710
+ if (state !== "provisioning") {
1711
+ return emitResult(buildReceipt({
1712
+ command: "up", outcome: "error", lease_id: leaseId, state, slot,
1713
+ error: `claim-only lease is ${state}, not provisioning`,
1714
+ }), opts, EXIT.LEASE_FAILED);
1715
+ }
1716
+ return emitResult(buildReceipt({
1717
+ command: "up", outcome: "claimed", lease_id: leaseId, state, host_key: d.host_key, slot,
1718
+ idle_ttl_secs: d.idle_ttl_secs, resource_name: d.resource_name,
1719
+ }), opts, EXIT.OK);
1720
+ }
1721
+
1699
1722
  // Reach `active`: local-exec provisions itself; otherwise wait for the Helper.
1700
1723
  if (state !== "active") {
1701
1724
  if (opts.localExec) {