@botbuddy/cli 1.21.1 → 1.22.0

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 +19 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbuddy/cli",
3
- "version": "1.21.1",
3
+ "version": "1.22.0",
4
4
  "description": "BotBuddy — Swarm coordination CLI for multi-agent workflows",
5
5
  "type": "module",
6
6
  "bin": {
package/src/stack.mjs CHANGED
@@ -31,6 +31,7 @@ import { callToolJson } from "./api.mjs";
31
31
  import { SERVER_URL, getConfig } from "./config.mjs";
32
32
  import { resolveOwnerToken, resolveAgentKey } from "./cli-credentials.mjs";
33
33
  import { runDockerCommand, runDockerWorkflow } from "./docker-hygiene.mjs";
34
+ import { machineUuid } from "./machine-id.mjs";
34
35
  import { bold, dim, yellow } from "./utils.mjs";
35
36
 
36
37
  export const STACK_SCHEMA_VERSION = 1;
@@ -667,6 +668,7 @@ export async function cmdUp(opts, {
667
668
  localProvisionFn = localProvision,
668
669
  waitFn = waitForLease,
669
670
  emitResult = emit,
671
+ machineUuidFn = machineUuid,
670
672
  } = {}) {
671
673
  let slot;
672
674
  try { slot = deriveSlot(opts); } catch (e) {
@@ -696,6 +698,13 @@ export async function cmdUp(opts, {
696
698
  const auth = await authProvider();
697
699
  if (!auth) return emitResult(buildReceipt({ command: "up", outcome: "error", error: "not authenticated — run `botbuddy login`" }), opts, EXIT.AUTH);
698
700
 
701
+ // BOT-1585: co-location dispatches the lease to the Helper enrolled for THIS
702
+ // physical machine (hardware id), since a hostname is not machine-unique. The
703
+ // server requires it; fail fast with an actionable message if it can't be read.
704
+ const hardwareUuid = machineUuidFn();
705
+ if (!hardwareUuid) {
706
+ return emitResult(buildReceipt({ command: "up", outcome: "error", code: "MACHINE_UUID_REQUIRED", error: "could not determine this machine's hardware id (needed to dispatch the stack lease to the right machine)" }), opts, EXIT.BACKEND);
707
+ }
699
708
  const req = await callTool("request_stack_lease", {
700
709
  slot, host_key: opts.host || undefined, repo: opts.repo || undefined,
701
710
  ticket_id: opts.ticket || undefined, ticket_url: opts.ticketUrl || undefined,
@@ -703,6 +712,7 @@ export async function cmdUp(opts, {
703
712
  purpose: opts.purpose || undefined, idle_ttl_secs: opts.idleTtl ?? undefined,
704
713
  stack_path: execution.stackPath,
705
714
  worktree_root: execution.worktreeRoot,
715
+ machine_uuid: hardwareUuid,
706
716
  });
707
717
  if (!req.ok) {
708
718
  return req.auth
@@ -1046,6 +1056,7 @@ export async function runStackLifecycle(opts, childArgv, adapters = {}) {
1046
1056
  release: (leaseId, signal) => callToolJson("release_stack_lease", { lease_id: leaseId, disposition: "destroy" }, { signal }),
1047
1057
  };
1048
1058
  const auth = adapters.auth ?? await stackAuthHeader();
1059
+ const machineUuidFn = adapters.machineUuidFn ?? machineUuid;
1049
1060
  const wait = adapters.wait ?? ((leaseId, done, failed, options) => waitForLease(leaseId, done, failed, options));
1050
1061
  // nosemgrep: javascript.lang.security.detect-child-process.detect-child-process -- validated executable + argv only; shell is never used.
1051
1062
  const startChild = adapters.startChild ?? ((argv, env, cwd) => spawn(argv[0], argv.slice(1), { cwd, env, stdio: "inherit", detached: true }));
@@ -1122,6 +1133,13 @@ export async function runStackLifecycle(opts, childArgv, adapters = {}) {
1122
1133
 
1123
1134
  try {
1124
1135
  if (!auth) return { exitCode: EXIT.AUTH, outcome: "error", error: "not authenticated — run botbuddy profile setup botbuddy-dev" };
1136
+ // BOT-1585: request_stack_lease requires this machine's hardware id so the lease
1137
+ // dispatches to the machine the worktree was registered on. `stack run` builds its
1138
+ // own payload (separate from `cmdUp`), so it must send it too.
1139
+ const hardwareUuid = machineUuidFn();
1140
+ if (!hardwareUuid) {
1141
+ return { exitCode: EXIT.BACKEND, outcome: "error", error: "could not determine this machine's hardware id (needed to dispatch the stack lease to the right machine)" };
1142
+ }
1125
1143
  const slot = deriveSlot(opts);
1126
1144
  const execution = resolveStackPath(process.cwd(), opts.stackPath);
1127
1145
  const request = await api.request({
@@ -1129,6 +1147,7 @@ export async function runStackLifecycle(opts, childArgv, adapters = {}) {
1129
1147
  ticket_url: opts.ticketUrl || undefined, pr_id: opts.prId || undefined, pr_url: opts.prUrl || undefined,
1130
1148
  purpose: opts.purpose || "stack run", idle_ttl_secs: opts.idleTtl ?? undefined,
1131
1149
  stack_path: execution.stackPath, worktree_root: execution.worktreeRoot,
1150
+ machine_uuid: hardwareUuid,
1132
1151
  });
1133
1152
  if (!request?.ok) return { exitCode: request?.auth ? EXIT.AUTH : EXIT.BACKEND, outcome: "error", error: request?.error || "lease request failed" };
1134
1153
  if (!request.data?.success) return { exitCode: EXIT.BACKEND, outcome: "error", error: request.data?.message || request.data?.code || "lease request refused" };