@botbuddy/cli 1.61.4 → 1.61.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.
@@ -1,7 +1,7 @@
1
1
  {
2
- "release_version": "1.61.4",
3
- "source_commit": "b6e939df36d913c84893c35c4f35a74cb8fb9675",
4
- "source_sha256": "4b2c9b81b1e46f417dd3f1b58c19f6e03c4fbb6fa697a6b2ac2db0586551614b",
2
+ "release_version": "1.61.5",
3
+ "source_commit": "f53c37db06f6fc5ab544623f2a273c4e21de1b72",
4
+ "source_sha256": "7280c8ff78ee957cc06bc7d07ed12a4b36a4c64c3a5968a710a1d371ab1b33c9",
5
5
  "target": "darwin-arm64",
6
- "sha256": "24fe172b82abb3ac4e59574516d614f1a35ec19b1fb2789793e4d877f8b0e526"
6
+ "sha256": "c57ada939dcf3d5c0ad7cefdbf27ae9995a06a14446ca8774a94c76c61083b00"
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
- "release_version": "1.61.4",
3
- "source_commit": "b6e939df36d913c84893c35c4f35a74cb8fb9675",
4
- "source_sha256": "4b2c9b81b1e46f417dd3f1b58c19f6e03c4fbb6fa697a6b2ac2db0586551614b",
2
+ "release_version": "1.61.5",
3
+ "source_commit": "f53c37db06f6fc5ab544623f2a273c4e21de1b72",
4
+ "source_sha256": "7280c8ff78ee957cc06bc7d07ed12a4b36a4c64c3a5968a710a1d371ab1b33c9",
5
5
  "target": "linux-x64",
6
- "sha256": "227d9145bedfb733e88e1456e2e8a9c989a12201b2c16427b2a7be2c27661bdf"
6
+ "sha256": "1002356a262f413e793af8779bec4208426d47bc38d6e5ef115a06b0fa4fd64c"
7
7
  }
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "schema_version": 1,
3
- "release_version": "1.61.4",
4
- "source_commit": "b6e939df36d913c84893c35c4f35a74cb8fb9675",
5
- "source_sha256": "4b2c9b81b1e46f417dd3f1b58c19f6e03c4fbb6fa697a6b2ac2db0586551614b",
3
+ "release_version": "1.61.5",
4
+ "source_commit": "f53c37db06f6fc5ab544623f2a273c4e21de1b72",
5
+ "source_sha256": "7280c8ff78ee957cc06bc7d07ed12a4b36a4c64c3a5968a710a1d371ab1b33c9",
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": "24fe172b82abb3ac4e59574516d614f1a35ec19b1fb2789793e4d877f8b0e526",
11
+ "sha256": "c57ada939dcf3d5c0ad7cefdbf27ae9995a06a14446ca8774a94c76c61083b00",
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": "227d9145bedfb733e88e1456e2e8a9c989a12201b2c16427b2a7be2c27661bdf",
18
+ "sha256": "1002356a262f413e793af8779bec4208426d47bc38d6e5ef115a06b0fa4fd64c",
19
19
  "mode": "0755"
20
20
  }
21
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbuddy/cli",
3
- "version": "1.61.4",
3
+ "version": "1.61.5",
4
4
  "description": "BotBuddy \u2014 Swarm coordination CLI for multi-agent workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,15 +4,18 @@
4
4
  // no local approval polling loop: host policy wakes only through the pushed
5
5
  // BOT-2324 raw wait condition, then this flow re-registers server-side.
6
6
  import { createHash } from "node:crypto";
7
+ import { dirname } from "node:path";
7
8
 
8
9
  import { DAEMON_PRODUCTION_MCP_SERVER, enrollDaemonDeviceProfileFromBootstrapIdentity } from "./enrollment.mjs";
9
10
  import { installRemoteService, installedBinaryPath, stageRemoteDaemonRelease } from "./installer.mjs";
10
11
  import {
11
12
  createRemoteBootstrapIdentity,
12
13
  deliverDaemonProfileFromBootstrapIdentityOverSSH,
14
+ installDaemonRuntimeConfigOverSSH,
13
15
  inspectRemoteBootstrapIdentity,
14
16
  verifyRemoteServiceIdentity,
15
17
  } from "./profile-delivery.mjs";
18
+ import { buildRuntimeConfig } from "./runtime-config.mjs";
16
19
  import { validateDaemonRelease } from "./release.mjs";
17
20
  import { renderLinuxUnit, renderMacDaemon } from "./service.mjs";
18
21
 
@@ -45,7 +48,7 @@ function exactObject(value, keys) {
45
48
  function checkedSpec(spec) {
46
49
  const keys = [
47
50
  "mode", "serverUrl", "sshHost", "serviceUser", "daemonBinary", "profilePath", "requestingHost",
48
- "runtimeEngineId", "platform", "arch", "releaseRoot", "home", "config", "approvalTimeoutSeconds",
51
+ "runtimeEngineId", "platform", "arch", "releaseRoot", "home", "config", "hostPolicy", "approvalTimeoutSeconds",
49
52
  ];
50
53
  const optional = ["caller", "callerHarness", "callerTicket"];
51
54
  if (!spec || typeof spec !== "object" || Array.isArray(spec) ||
@@ -59,7 +62,11 @@ function checkedSpec(spec) {
59
62
  typeof spec.requestingHost !== "string" || !spec.requestingHost || Buffer.byteLength(spec.requestingHost, "utf8") > 80 ||
60
63
  typeof spec.runtimeEngineId !== "string" || !ENGINE_ID_RE.test(spec.runtimeEngineId) ||
61
64
  !["darwin", "linux"].includes(spec.platform) || typeof spec.arch !== "string" || !spec.arch ||
62
- !validPath(spec.releaseRoot) || !validPath(spec.home) || !validPath(spec.config) ||
65
+ !validPath(spec.releaseRoot) || !validPath(spec.home) || !validPath(spec.config) || !spec.config.endsWith("/daemon.json") ||
66
+ dirname(spec.config) !== dirname(spec.profilePath) ||
67
+ !spec.hostPolicy || typeof spec.hostPolicy !== "object" || Array.isArray(spec.hostPolicy) ||
68
+ Object.keys(spec.hostPolicy).length !== 4 ||
69
+ !["machineId", "dockerEndpoint", "supabaseBinary", "dockerCLI"].every((key) => typeof spec.hostPolicy[key] === "string" && spec.hostPolicy[key]) ||
63
70
  !Number.isSafeInteger(spec.approvalTimeoutSeconds) || spec.approvalTimeoutSeconds < 1 || spec.approvalTimeoutSeconds > 3_600 ||
64
71
  optional.some((key) => spec[key] !== undefined && (typeof spec[key] !== "string" || !spec[key]))
65
72
  ) fail("daemon_bootstrap_spec_invalid");
@@ -215,12 +222,14 @@ export async function bootstrapFreshDaemon(input, {
215
222
  enroll = enrollDaemonDeviceProfileFromBootstrapIdentity,
216
223
  deliver = deliverDaemonProfileFromBootstrapIdentityOverSSH,
217
224
  installRemote = installRemoteService,
225
+ installRuntimeConfig = installDaemonRuntimeConfigOverSSH,
218
226
  } = {}) {
219
227
  const spec = checkedSpec(input);
220
228
  if (typeof registerHost !== "function" || typeof verifyRemoteIdentity !== "function" ||
221
229
  typeof createIdentity !== "function" || typeof inspectIdentity !== "function" ||
222
230
  typeof validateRelease !== "function" || typeof stageRemote !== "function" || typeof enroll !== "function" ||
223
231
  typeof deliver !== "function" || typeof installRemote !== "function" ||
232
+ typeof installRuntimeConfig !== "function" ||
224
233
  (awaitApproval !== null && typeof awaitApproval !== "function") ||
225
234
  (onApprovalRequired !== null && typeof onApprovalRequired !== "function") ||
226
235
  (resumeStore !== null && (typeof resumeStore !== "object" || typeof resumeStore.save !== "function" ||
@@ -355,6 +364,42 @@ export async function bootstrapFreshDaemon(input, {
355
364
  }
356
365
  }
357
366
 
367
+ let runtimeConfig;
368
+ try {
369
+ runtimeConfig = buildRuntimeConfig({
370
+ host_key: publicIdentity.hostKey,
371
+ state_dir: dirname(spec.config),
372
+ docker_endpoint: spec.hostPolicy.dockerEndpoint,
373
+ expected_engine_id: spec.runtimeEngineId,
374
+ machine_id: spec.hostPolicy.machineId,
375
+ supabase_binary: spec.hostPolicy.supabaseBinary,
376
+ docker_cli: spec.hostPolicy.dockerCLI,
377
+ host_credential_profile: spec.profilePath,
378
+ device_consent: true,
379
+ runtime_version: `go-daemon/${release.releaseVersion}`,
380
+ runtime_git_sha: release.sourceCommit,
381
+ runtime_protocol_version: 2,
382
+ });
383
+ } catch {
384
+ fail("daemon_bootstrap_runtime_config_invalid");
385
+ }
386
+ try {
387
+ const installed = await installRuntimeConfig({
388
+ config: JSON.stringify(runtimeConfig),
389
+ configPath: spec.config,
390
+ sshHost: spec.sshHost,
391
+ serviceUser: spec.serviceUser,
392
+ daemonBinary: trustedDaemonBinary,
393
+ profilePath: spec.profilePath,
394
+ expectedHostKey: publicIdentity.hostKey,
395
+ directInstall: remote.directInstall,
396
+ });
397
+ if (!installed || installed.installed !== true) fail("daemon_bootstrap_runtime_config_failed");
398
+ } catch (error) {
399
+ if (error instanceof DaemonBootstrapError) throw error;
400
+ fail("daemon_bootstrap_runtime_config_failed");
401
+ }
402
+
358
403
  const serviceSpec = {
359
404
  user: spec.serviceUser,
360
405
  home: spec.home,
@@ -29,6 +29,7 @@ import {
29
29
  createRemoteBootstrapIdentity,
30
30
  deliverDaemonProfileFromBootstrapIdentityOverSSH,
31
31
  deliverDaemonProfileFromHelperIdentityOverSSH,
32
+ installDaemonRuntimeConfigOverSSH,
32
33
  inspectRemoteBootstrapIdentity,
33
34
  verifyRemoteHelperIdentity as verifyRemoteHelperIdentityOnTarget,
34
35
  verifyRemoteServiceIdentity,
@@ -285,6 +286,7 @@ export async function cmdDaemon(args, {
285
286
  verifyRuntime = checkRuntimeConfig,
286
287
  enroll = enrollDaemonDeviceProfileFromHelperIdentity,
287
288
  deliver = deliverDaemonProfileFromHelperIdentityOverSSH,
289
+ installRuntimeConfig = installDaemonRuntimeConfigOverSSH,
288
290
  verifyRemoteIdentity = verifyRemoteServiceIdentity,
289
291
  verifyRemoteHelperIdentity = verifyRemoteHelperIdentityOnTarget,
290
292
  bootstrap = bootstrapFreshDaemon,
@@ -332,6 +334,7 @@ export async function cmdDaemon(args, {
332
334
  },
333
335
  enroll: enrollBootstrap,
334
336
  deliver: deliverBootstrap,
337
+ installRuntimeConfig,
335
338
  installRemote,
336
339
  onApprovalRequired: ({ approval_url: approvalUrl, host_key: hostKey }) => {
337
340
  notice(`bb daemon bootstrap: owner approval required for ${hostKey}: ${approvalUrl}`);
@@ -334,3 +334,32 @@ export async function deliverDaemonProfileFromBootstrapIdentityOverSSH({
334
334
  installFlag: "--install-profile-from-bootstrap-identity",
335
335
  });
336
336
  }
337
+
338
+ // Runtime policy is public host metadata, never a credential. It is still
339
+ // installed through the daemon's descriptor-pinned private root so a caller
340
+ // cannot redirect it with a symlink or broaden a prior host's policy.
341
+ export async function installDaemonRuntimeConfigOverSSH({
342
+ config,
343
+ configPath,
344
+ sshHost,
345
+ serviceUser,
346
+ daemonBinary,
347
+ profilePath,
348
+ expectedHostKey,
349
+ sshBin = "ssh",
350
+ spawnImpl = realSpawn,
351
+ env = process.env,
352
+ timeoutMs = DEFAULT_TIMEOUT_MS,
353
+ directInstall = false,
354
+ } = {}) {
355
+ safeInputs({ sshHost, serviceUser, daemonBinary, profilePath, expectedHostKey, sshBin, spawnImpl, env, timeoutMs, directInstall });
356
+ if (typeof config !== "string" || config.length === 0 || Buffer.byteLength(config, "utf8") > 64 * 1024 ||
357
+ typeof configPath !== "string" || !SAFE_ABSOLUTE_PATH.test(configPath) || !configPath.endsWith("/daemon.json")) throw invalid();
358
+ const command = remoteDaemonCommand({
359
+ serviceUser, daemonBinary, directInstall,
360
+ args: ["--install-runtime-config", "--config", configPath, "--profile", profilePath, "--expected-host-key", expectedHostKey],
361
+ });
362
+ const stdout = await fixedSsh({ sshHost, sshBin, spawnImpl, env, timeoutMs, command, stdin: Buffer.from(config, "utf8") });
363
+ if (!stdout.equals(Buffer.from('{"installed":true}\n'))) throw failed();
364
+ return { installed: true };
365
+ }