@botbuddy/cli 1.63.23 → 1.63.24
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.
|
Binary file
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.63.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.63.24",
|
|
3
|
+
"source_commit": "9da358c3b61afaf3c70fcbf9e73cc71500137b65",
|
|
4
|
+
"source_sha256": "f187bf83378e0de983fa1ab3e97928d784f08d8149598889de8d080b8221512f",
|
|
5
5
|
"target": "darwin-arm64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "29947b82499d5e2db4048530bf203f52546aef31625c59e017d2c2666b6ee5d5"
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.63.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.63.24",
|
|
3
|
+
"source_commit": "9da358c3b61afaf3c70fcbf9e73cc71500137b65",
|
|
4
|
+
"source_sha256": "f187bf83378e0de983fa1ab3e97928d784f08d8149598889de8d080b8221512f",
|
|
5
5
|
"target": "linux-x64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "855a97c0028f1dbd208d7ead972b6ffbc195a945b22e14ba356fdc7365e730b5"
|
|
7
7
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
|
-
"release_version": "1.63.
|
|
4
|
-
"source_commit": "
|
|
5
|
-
"source_sha256": "
|
|
3
|
+
"release_version": "1.63.24",
|
|
4
|
+
"source_commit": "9da358c3b61afaf3c70fcbf9e73cc71500137b65",
|
|
5
|
+
"source_sha256": "f187bf83378e0de983fa1ab3e97928d784f08d8149598889de8d080b8221512f",
|
|
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": "
|
|
11
|
+
"sha256": "29947b82499d5e2db4048530bf203f52546aef31625c59e017d2c2666b6ee5d5",
|
|
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": "
|
|
18
|
+
"sha256": "855a97c0028f1dbd208d7ead972b6ffbc195a945b22e14ba356fdc7365e730b5",
|
|
19
19
|
"mode": "0755"
|
|
20
20
|
}
|
|
21
21
|
}
|
package/package.json
CHANGED
package/src/stack.mjs
CHANGED
|
@@ -65,16 +65,17 @@ export const STACK_SCHEMA_VERSION = 1;
|
|
|
65
65
|
* BOT-2780 / BOT-2788: a `stack run` that may be served by another host reserves a requester
|
|
66
66
|
* origin and allocates through BOT-2499's identified admission. Stable (the default ring):
|
|
67
67
|
* auto or remote placement, with or without `--reap` (an idle stack is continued on the same
|
|
68
|
-
* logical origin by a later run)
|
|
69
|
-
*
|
|
70
|
-
*
|
|
68
|
+
* logical origin by a later run), callback runs included (BOT-2775 stable activation: they ask
|
|
69
|
+
* for callback_policy auto, see runStackLifecycle); runs that pin a host, a prepared stack
|
|
70
|
+
* path, a fixed nonce or a co-located placement keep the legacy path unchanged. Beta:
|
|
71
|
+
* BOT-2780's `--remote --reap` gate, which refuses those contexts instead.
|
|
71
72
|
*/
|
|
72
73
|
export const requesterOriginEnabled = (opts) => {
|
|
73
74
|
if (opts.execution === "local" || opts.localOnly || opts.localExec) return false;
|
|
74
75
|
// Beta keeps BOT-2780's gate (remote + --reap); its idle/callback runs stay on BOT-2775's path.
|
|
75
76
|
if (opts.executionRing === "beta") return opts.execution === "remote" && opts.reap === true;
|
|
76
77
|
if (opts.execution != null && opts.execution !== "auto" && opts.execution !== "remote") return false;
|
|
77
|
-
return opts.host == null && opts.stackNonce == null && (opts.stackPath == null || opts.stackPath === "" || opts.stackPath === ".")
|
|
78
|
+
return opts.host == null && opts.stackNonce == null && (opts.stackPath == null || opts.stackPath === "" || opts.stackPath === ".");
|
|
78
79
|
};
|
|
79
80
|
// Exact replays of one identified request after an uncertain answer (lost response, 5xx,
|
|
80
81
|
// ADMISSION_UNCERTAIN, pending delivery). The same request_id can never create a second lease.
|
|
@@ -277,16 +278,20 @@ ${bold("run OPTIONS")}
|
|
|
277
278
|
is requested through the identified admission (exact replay after a lost
|
|
278
279
|
response, no fallback).
|
|
279
280
|
--callback-port N The caller runs a listener the stack calls back through
|
|
280
|
-
host.docker.internal:N (e.g. 4600): sent as callback_port.
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
281
|
+
host.docker.internal:N (e.g. 4600): sent as callback_port. A remote
|
|
282
|
+
callback is served by a Linux Go runtime with native callbacks (protocol
|
|
283
|
+
5): a private SSH Unix-socket route to a fixed requester gateway, renewed
|
|
284
|
+
against current owned authorization (BOT-2775). An unpinned stable/auto run
|
|
285
|
+
asks for callback_policy auto: it runs co-located or on a host with native
|
|
286
|
+
callbacks, and Swift Helper hosts no longer take it remotely. With --remote
|
|
287
|
+
--execution-ring beta the native transport is required explicitly. A remote
|
|
288
|
+
lease the backend does not acknowledge with it fails
|
|
289
|
+
HOST_CALLBACK_TRANSPORT_UNSUPPORTED (never a legacy -R); the child never
|
|
290
|
+
receives the route capability. A run pinned with --host, --stack-path or a
|
|
291
|
+
fixed nonce keeps the legacy -R forward: it first probes the host with a
|
|
292
|
+
throwaway reverse forward and opens -R 127.0.0.1:N:127.0.0.1:N only when
|
|
293
|
+
the host keeps it on loopback (sshd GatewayPorts no/clientspecified);
|
|
294
|
+
otherwise exit 5 CALLBACK_NOT_LOOPBACK (BOT-2184).
|
|
290
295
|
--bundle-max-mb N Remote-lease bundle size cap in MB (default 50, compressed). The bundle
|
|
291
296
|
excludes .bb-stack.json, node_modules, supabase/.temp|.branches and
|
|
292
297
|
untracked-secret dotenv files (.env, .env.local, .env.*.local).
|
|
@@ -331,9 +336,11 @@ ${bold("IDLE LEASES")} (BOT-1803)
|
|
|
331
336
|
|
|
332
337
|
${bold("REMOTE LEASES")} (BOT-2142, stack run)
|
|
333
338
|
The receipt carries execution_mode (local|remote), execution_host_key, requester_host,
|
|
334
|
-
transport (none|ssh-forward), and for a remote lease forwards [{name, remote, local}] + bundle
|
|
339
|
+
transport (none|ssh-forward), and for a remote lease forwards [{name, remote, local}] + bundle,
|
|
340
|
+
plus callback_transport "native_origin_v1" when --callback-port used the native route (BOT-2775).
|
|
335
341
|
The child also gets BOTBUDDY_STACK_EXECUTION_MODE and BOTBUDDY_STACK_TRANSPORT.
|
|
336
|
-
Requeue (BOT-2207/BOT-2334): under auto, including --callback-port, a lease that QUEUES on this machine uploads its bundle at once
|
|
342
|
+
Requeue (BOT-2207/BOT-2334): under auto, including --callback-port, a lease that QUEUES on this machine uploads its bundle at once
|
|
343
|
+
(a callback lease moves only to a host with native callbacks, BOT-2775);
|
|
337
344
|
every minute the control plane re-offers it to this owner's other hosts and it may move there
|
|
338
345
|
(placement_reason moved_remote_on_requeue), and run then forwards to the new host. While queued,
|
|
339
346
|
run prints one \`considered: <host> — <verdict>\` line per host weighed (e.g. "census stale 131 s");
|
|
@@ -347,11 +354,13 @@ ${bold("REMOTE LEASES")} (BOT-2142, stack run)
|
|
|
347
354
|
4 STACK_EXECUTION_MODE_MISMATCH the slot already holds a lease in another mode/host — stack done it first
|
|
348
355
|
(the four codes above create NO lease)
|
|
349
356
|
5 BUNDLE_UPLOAD_FAILED the signed bundle upload failed (the lease is released first)
|
|
350
|
-
5 CALLBACK_NOT_LOOPBACK --callback-port (
|
|
357
|
+
5 CALLBACK_NOT_LOOPBACK --callback-port (pinned legacy -R): the GatewayPorts probe (before any real -R)
|
|
351
358
|
or the post-forward check found the host's -R listener not loopback-only
|
|
352
359
|
(sshd GatewayPorts yes), or could not check it (no lsof, no listener)
|
|
353
|
-
5 HOST_CALLBACK_TRANSPORT_UNSUPPORTED --callback-port (
|
|
360
|
+
5 HOST_CALLBACK_TRANSPORT_UNSUPPORTED --callback-port (remote native): native callback transport is
|
|
354
361
|
unavailable; needs the installed Linux Go runtime (protocol 5) and backend
|
|
362
|
+
4 REQUESTER_ORIGIN_EXPLICIT_CONFLICT an unpinned stable --callback-port run in a repo whose
|
|
363
|
+
supabase/config.toml sets its own [auth] external_url (no lease requested)
|
|
355
364
|
5 CALLBACK_NOT_READY / CALLBACK_CONSUMING_NAMESPACE_UNREACHABLE the native route did not
|
|
356
365
|
answer from the stack's own network before the child (lease reaped)
|
|
357
366
|
6 CALLBACK_TRANSPORT_LOST / CALLBACK_AUTHORIZATION_LOST the native route or its current
|
|
@@ -1040,7 +1049,9 @@ export async function resolveRunExecution(cwd, opts, { materialize = materialize
|
|
|
1040
1049
|
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal -- canonical realpath of the current worktree; fixed own source config, never a request-supplied path.
|
|
1041
1050
|
await patchRequesterAuthOrigin(readFileSync(join(worktreeRoot, "supabase", "config.toml"), "utf8"), requesterOrigin.origin);
|
|
1042
1051
|
} catch (error) {
|
|
1043
|
-
|
|
1052
|
+
// BOT-2775: a stable callback run is refused too. The legacy allocator would place it
|
|
1053
|
+
// on any remote host with the legacy -R callback, which Linux Go hosts cannot serve.
|
|
1054
|
+
if (error?.code !== "REQUESTER_ORIGIN_EXPLICIT_CONFLICT" || opts.executionRing === "beta" || opts.callbackPort != null) throw error;
|
|
1044
1055
|
warn("stack: supabase/config.toml sets its own [auth] external_url, so this run keeps the legacy (non-requester-origin) path");
|
|
1045
1056
|
await requesterOrigin.close();
|
|
1046
1057
|
requesterOrigin = null;
|
|
@@ -4128,8 +4139,14 @@ async function runStackLifecycleOnce(opts, childArgv, adapters, incidentBox) {
|
|
|
4128
4139
|
"an idle stack on this slot will be re-provisioned rather than reused.");
|
|
4129
4140
|
}
|
|
4130
4141
|
idleLease.stackDigest = stackDigest;
|
|
4131
|
-
//
|
|
4142
|
+
// BOT-2775: an explicit beta caller requires the native transport up front. A stable/default
|
|
4143
|
+
// run with a callback asks for callback_policy auto on its identified admission (BOT-2779):
|
|
4144
|
+
// the backend places it on a host that serves native callbacks (Linux Go protocol 5) or
|
|
4145
|
+
// co-located, and the transport the lease answers with decides the route (useNativeCallback).
|
|
4146
|
+
// A remote answer without the native transport fails closed; it never falls back to -R.
|
|
4132
4147
|
const requireNativeCallback = opts.callbackPort != null && opts.executionRing === "beta" && executionRequest !== "local";
|
|
4148
|
+
const autoCallbackPolicy = opts.callbackPort != null && opts.executionRing !== "beta" && executionRequest !== "local" && Boolean(execution.admissionRequestId);
|
|
4149
|
+
let useNativeCallback = requireNativeCallback;
|
|
4133
4150
|
const leaseArgs = {
|
|
4134
4151
|
slot, host_key: opts.host || undefined, repo: opts.repo, ticket_id: opts.ticket,
|
|
4135
4152
|
ticket_url: opts.ticketUrl || undefined, pr_id: opts.prId || undefined, pr_url: opts.prUrl || undefined,
|
|
@@ -4144,7 +4161,8 @@ async function runStackLifecycleOnce(opts, childArgv, adapters, incidentBox) {
|
|
|
4144
4161
|
require_stack_digest: true,
|
|
4145
4162
|
// BOT-2269: what the bundle carries beyond the defaults (paths, sha256, key NAMES).
|
|
4146
4163
|
...(bundle?.included?.length ? { bundle_included: bundle.included } : {}),
|
|
4147
|
-
...(opts.callbackPort != null ? { callback_port: opts.callbackPort,
|
|
4164
|
+
...(opts.callbackPort != null ? { callback_port: opts.callbackPort,
|
|
4165
|
+
...(requireNativeCallback ? { callback_transport: NATIVE_CALLBACK_TRANSPORT } : autoCallbackPolicy ? { callback_policy: "auto" } : {}) } : {}),
|
|
4148
4166
|
};
|
|
4149
4167
|
// BOT-2780: the identified admission key and a digest of the immutable origin context.
|
|
4150
4168
|
// The intent (including the bundle digest that names the reserved origin) is fingerprinted
|
|
@@ -4508,7 +4526,7 @@ async function runStackLifecycleOnce(opts, childArgv, adapters, incidentBox) {
|
|
|
4508
4526
|
placement.mode = request.data.execution_mode ?? null;
|
|
4509
4527
|
placement.hostKey = request.data.host_key ?? null;
|
|
4510
4528
|
placement.requesterHost = request.data.requester_host ?? null;
|
|
4511
|
-
if (remoteLease && requireNativeCallback && request.data.callback_transport !== NATIVE_CALLBACK_TRANSPORT) {
|
|
4529
|
+
if (remoteLease && (requireNativeCallback || autoCallbackPolicy) && request.data.callback_transport !== NATIVE_CALLBACK_TRANSPORT) {
|
|
4512
4530
|
const cleanup = await requestCleanup();
|
|
4513
4531
|
return { exitCode: cleanup.ok ? EXIT.BACKEND : EXIT.CLEANUP_FAILED, outcome: "error", code: "HOST_CALLBACK_TRANSPORT_UNSUPPORTED", leaseId, error: "backend did not acknowledge required remote callback transport", cleanup };
|
|
4514
4532
|
}
|
|
@@ -4779,17 +4797,27 @@ async function runStackLifecycleOnce(opts, childArgv, adapters, incidentBox) {
|
|
|
4779
4797
|
stderrLine(`stack: lease ${leaseId} moved to ${placement.hostKey ?? "another host"} while queued (${placement.placementReason ?? "moved"}) — forwarding to it`);
|
|
4780
4798
|
}
|
|
4781
4799
|
let childConnection = current.data.connection;
|
|
4800
|
+
// BOT-2775: a callback_policy auto lease serves its callback natively whenever it is remote,
|
|
4801
|
+
// including one that moved to a remote host while queued. Its current lease must name the
|
|
4802
|
+
// native transport; anything else is refused before any forward opens.
|
|
4803
|
+
if (servedRemote && autoCallbackPolicy) {
|
|
4804
|
+
if (current.data.callback_transport !== NATIVE_CALLBACK_TRANSPORT) {
|
|
4805
|
+
const cleanup = await requestCleanup();
|
|
4806
|
+
return { exitCode: cleanup.ok ? EXIT.BACKEND : EXIT.CLEANUP_FAILED, outcome: "error", code: "HOST_CALLBACK_TRANSPORT_UNSUPPORTED", leaseId, error: "the remote lease does not carry the native callback transport its callback policy requires", cleanup };
|
|
4807
|
+
}
|
|
4808
|
+
useNativeCallback = true;
|
|
4809
|
+
}
|
|
4782
4810
|
if (servedRemote) {
|
|
4783
4811
|
// BOT-2142: forward every stack port over ONE ssh and hand the child the local ports.
|
|
4784
4812
|
sshTarget = current.data.connection?.ssh_target ?? current.data.ssh_target ?? sshTarget;
|
|
4785
4813
|
try {
|
|
4786
|
-
tunnel = await openTunnel({ connection: current.data.connection, sshTarget, hostKey: placement.hostKey, callbackPort:
|
|
4787
|
-
...(
|
|
4814
|
+
tunnel = await openTunnel({ connection: current.data.connection, sshTarget, hostKey: placement.hostKey, callbackPort: useNativeCallback ? null : opts.callbackPort,
|
|
4815
|
+
...(useNativeCallback ? { reserveLocalPorts: [opts.callbackPort] } : {}) });
|
|
4788
4816
|
} catch (error) {
|
|
4789
4817
|
const cleanup = await requestCleanup();
|
|
4790
4818
|
return { exitCode: EXIT.BACKEND, outcome: "error", code: error?.code ?? "REMOTE_UNREACHABLE", leaseId, error: String(error?.message ?? error), cleanup };
|
|
4791
4819
|
}
|
|
4792
|
-
if (
|
|
4820
|
+
if (useNativeCallback) {
|
|
4793
4821
|
try {
|
|
4794
4822
|
nativeCallback = await openCallback({ current: current.data, leaseId, hostKey: placement.hostKey, callbackPort: opts.callbackPort, requesterMachineId: machineUuidFn(), sshTarget,
|
|
4795
4823
|
readCurrent: async (signal) => { let observed; try { observed = await api.get(leaseId, { signal }); } catch (error) { throw Object.assign(new Error(`current callback status unreadable (${error?.message ?? error})`), { transient: true }); } if (!observed?.ok) {
|
|
@@ -4802,6 +4830,7 @@ async function runStackLifecycleOnce(opts, childArgv, adapters, incidentBox) {
|
|
|
4802
4830
|
return observed.data; },
|
|
4803
4831
|
});
|
|
4804
4832
|
nativeCallback.onExit((exit) => { if (childResult) return; callbackLost = exit; fencing = true; stopChild("SIGTERM", EXIT.LEASE_FAILED); });
|
|
4833
|
+
placement.callbackTransport = NATIVE_CALLBACK_TRANSPORT;
|
|
4805
4834
|
} catch (error) {
|
|
4806
4835
|
await closeTunnel(); const cleanup = await requestCleanup();
|
|
4807
4836
|
return { exitCode: cleanup.ok && !callbackCleanupError && error?.code !== "CALLBACK_CLEANUP_UNCERTAIN" ? EXIT.BACKEND : EXIT.CLEANUP_FAILED, outcome: "error", code: error?.code ?? "CALLBACK_NOT_READY", leaseId, error: String(error?.message ?? "callback transport unavailable"), cleanup };
|
|
@@ -5061,6 +5090,8 @@ export function runReceiptFields(result) {
|
|
|
5061
5090
|
execution_host_key: result.execution?.hostKey ?? null,
|
|
5062
5091
|
requester_host: result.execution?.requesterHost ?? null,
|
|
5063
5092
|
transport: result.execution?.transport ?? "none",
|
|
5093
|
+
// BOT-2775: present only when the callback was served over the native route.
|
|
5094
|
+
...(result.execution?.callbackTransport ? { callback_transport: result.execution.callbackTransport } : {}),
|
|
5064
5095
|
// BOT-2780: the reserved requester origin generated auth links name (never a credential).
|
|
5065
5096
|
...(result.execution?.requesterOrigin ? { requester_origin: result.execution.requesterOrigin } : {}),
|
|
5066
5097
|
// BOT-2788: "previous" (an idle stack's origin re-reserved), "previous_port_busy" or "new".
|