@botbuddy/cli 1.64.15 → 1.64.17
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.
- package/daemon/bin/darwin-arm64/botbuddy-daemon +0 -0
- package/daemon/bin/linux-x64/botbuddy-daemon +0 -0
- package/daemon-release/darwin-arm64.json +4 -4
- package/daemon-release/linux-x64.json +4 -4
- package/daemon-release/manifest.json +5 -5
- package/package.json +1 -1
- package/src/stack-local-locks.mjs +25 -0
- package/src/stack-run-outcome.mjs +17 -1
- package/src/stack.mjs +25 -4
|
Binary file
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.64.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.64.17",
|
|
3
|
+
"source_commit": "d2fabc08d075fc51a591dd0ce38dc0ec9d66c168",
|
|
4
|
+
"source_sha256": "a21dcce44a33eddc1392ae4604533ee412d1c0c2bed64a26f52e99a2e2feed30",
|
|
5
5
|
"target": "darwin-arm64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "f8f98b5bc212ed676fe2e97381b5a0c548331602ed7165360a331c3e8ff309fd"
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.64.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.64.17",
|
|
3
|
+
"source_commit": "d2fabc08d075fc51a591dd0ce38dc0ec9d66c168",
|
|
4
|
+
"source_sha256": "a21dcce44a33eddc1392ae4604533ee412d1c0c2bed64a26f52e99a2e2feed30",
|
|
5
5
|
"target": "linux-x64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "f4a8fd0904f99e688e857b3674400b3e088fbca7f557bdea7cd1f14abcd84f31"
|
|
7
7
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
|
-
"release_version": "1.64.
|
|
4
|
-
"source_commit": "
|
|
5
|
-
"source_sha256": "
|
|
3
|
+
"release_version": "1.64.17",
|
|
4
|
+
"source_commit": "d2fabc08d075fc51a591dd0ce38dc0ec9d66c168",
|
|
5
|
+
"source_sha256": "a21dcce44a33eddc1392ae4604533ee412d1c0c2bed64a26f52e99a2e2feed30",
|
|
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": "f8f98b5bc212ed676fe2e97381b5a0c548331602ed7165360a331c3e8ff309fd",
|
|
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": "f4a8fd0904f99e688e857b3674400b3e088fbca7f557bdea7cd1f14abcd84f31",
|
|
19
19
|
"mode": "0755"
|
|
20
20
|
}
|
|
21
21
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
|
|
3
|
+
// Applied only to the private materialized stack, never to the source config.
|
|
4
|
+
export async function patchLocalLockCapacity(text, value) {
|
|
5
|
+
if (!Number.isInteger(value) || value < 16 || value > 1024) throw new Error("local lock capacity must be an integer from 16 to 1024");
|
|
6
|
+
if (typeof text !== "string" || Buffer.byteLength(text) > 1024 * 1024) throw new Error("local stack config unavailable or too large");
|
|
7
|
+
const { parse, stringify } = await import("./vendor/smol-toml/index.js");
|
|
8
|
+
let doc;
|
|
9
|
+
try { doc = parse(text, { integersAsBigInt: true, unsafeKeyBehaviour: "throw" }); }
|
|
10
|
+
catch { throw new Error("local stack config is not valid supported TOML"); }
|
|
11
|
+
doc.db ??= Object.create(null);
|
|
12
|
+
if (!doc.db || typeof doc.db !== "object" || Array.isArray(doc.db)) throw new Error("local db config is ambiguous");
|
|
13
|
+
doc.db.settings ??= Object.create(null);
|
|
14
|
+
const settings = doc.db.settings;
|
|
15
|
+
if (!settings || typeof settings !== "object" || Array.isArray(settings)) throw new Error("local db settings are ambiguous");
|
|
16
|
+
if (Object.hasOwn(settings, "max_locks_per_transaction") && settings.max_locks_per_transaction !== BigInt(value)) {
|
|
17
|
+
throw new Error("local lock capacity conflicts with the source configuration");
|
|
18
|
+
}
|
|
19
|
+
settings.max_locks_per_transaction = BigInt(value);
|
|
20
|
+
let patched, check;
|
|
21
|
+
try { patched = stringify(doc, { numbersAsFloat: true, strictTemporal: true }); check = parse(patched, { integersAsBigInt: true, unsafeKeyBehaviour: "throw" }); }
|
|
22
|
+
catch { throw new Error("local stack config could not roundtrip safely"); }
|
|
23
|
+
if (!isDeepStrictEqual(doc, check)) throw new Error("local stack config changed during roundtrip");
|
|
24
|
+
return patched;
|
|
25
|
+
}
|
|
@@ -185,6 +185,9 @@ export function buildStackRunOutcomePayload({ fields = {}, exitCode, runId = ran
|
|
|
185
185
|
const signal = pick(fields.stop_signal?.signal, SIGNAL_RE);
|
|
186
186
|
// BOT-2880: sent only when present, because an older server refuses an unknown key.
|
|
187
187
|
const callbackAuthorizationReason = pick(fields.callback_authorization_reason, AUTHORIZATION_REASON_RE);
|
|
188
|
+
const failureMechanism = code === "REQUESTER_ORIGIN_DIGEST_UNAVAILABLE" &&
|
|
189
|
+
["origin_config_missing", "origin_digest_missing"].includes(fields.failure_mechanism)
|
|
190
|
+
? fields.failure_mechanism : null;
|
|
188
191
|
return {
|
|
189
192
|
run_id: runId,
|
|
190
193
|
lease_id: validStackLeaseId(fields.lease_id),
|
|
@@ -208,6 +211,7 @@ export function buildStackRunOutcomePayload({ fields = {}, exitCode, runId = ran
|
|
|
208
211
|
signal_parent: pick(fields.stop_signal.parent, TOKEN_RE),
|
|
209
212
|
} : {}),
|
|
210
213
|
...(callbackAuthorizationReason ? { callback_authorization_reason: callbackAuthorizationReason } : {}),
|
|
214
|
+
...(failureMechanism ? { failure_mechanism: failureMechanism } : {}),
|
|
211
215
|
};
|
|
212
216
|
}
|
|
213
217
|
|
|
@@ -236,7 +240,19 @@ export async function reportStackRunOutcome({ fields, exitCode, runId, opts, env
|
|
|
236
240
|
timer = setTimeout(() => { ac.abort(); resolve(TIMED_OUT); }, timeoutMs);
|
|
237
241
|
});
|
|
238
242
|
try {
|
|
239
|
-
const call = Promise.resolve().then(() =>
|
|
243
|
+
const call = Promise.resolve().then(async () => {
|
|
244
|
+
const first = await channel.callTool(STACK_RUN_OUTCOME_TOOL, payload, { auth: channel.auth, signal: ac.signal });
|
|
245
|
+
// Older servers reject unknown keys before writing. Retry only that exact,
|
|
246
|
+
// explicit rejection with the same run id; never retry an uncertain send.
|
|
247
|
+
if (!ac.signal.aborted && payload.failure_mechanism && first?.ok && first?.isError &&
|
|
248
|
+
first?.data?.code === "INVALID_STACK_RUN_OUTCOME" &&
|
|
249
|
+
typeof first?.data?.message === "string" &&
|
|
250
|
+
first.data.message.startsWith("unknown key 'failure_mechanism'")) {
|
|
251
|
+
const { failure_mechanism: _omitted, ...legacyPayload } = payload;
|
|
252
|
+
return await channel.callTool(STACK_RUN_OUTCOME_TOOL, legacyPayload, { auth: channel.auth, signal: ac.signal });
|
|
253
|
+
}
|
|
254
|
+
return first;
|
|
255
|
+
});
|
|
240
256
|
const result = await Promise.race([call.catch(() => null), timeout]);
|
|
241
257
|
if (result === TIMED_OUT) return { sent: false, reason: "timeout", payload };
|
|
242
258
|
const capture = result?.data?.feedback_capture;
|
package/src/stack.mjs
CHANGED
|
@@ -237,6 +237,8 @@ ${bold("done OPTIONS")}
|
|
|
237
237
|
require exact worktree + live connection proof.
|
|
238
238
|
|
|
239
239
|
${bold("run OPTIONS")}
|
|
240
|
+
--local-max-locks N Set max_locks_per_transaction (16-1024) in the disposable stack only.
|
|
241
|
+
Refuses conflicting source settings or an explicit --stack-path.
|
|
240
242
|
--repo <repo> Required approved repository name.
|
|
241
243
|
--ticket <BOT-123> Required ticket for the batch.
|
|
242
244
|
--run-id <UUID> Optional caller-chosen run identity. Choose it before dispatch to arm
|
|
@@ -528,6 +530,15 @@ export function parseStackArgs(argv) {
|
|
|
528
530
|
case "--purpose": if (need(a, rest[i + 1])) opts.purpose = rest[++i]; break;
|
|
529
531
|
case "--run-id": if (need(a, rest[i + 1])) opts.runId = rest[++i]; break;
|
|
530
532
|
case "--stack-path": if (need(a, rest[i + 1])) { opts.stackPath = rest[++i]; opts.stackPathExplicit = true; } break;
|
|
533
|
+
case "--local-max-locks": {
|
|
534
|
+
if (need(a, rest[i + 1])) {
|
|
535
|
+
const raw = rest[++i];
|
|
536
|
+
const n = Number(raw);
|
|
537
|
+
if (!/^\d+$/.test(raw) || !Number.isInteger(n) || n < 16 || n > 1024) errors.push("--local-max-locks must be an integer from 16 to 1024");
|
|
538
|
+
else opts.localMaxLocks = n;
|
|
539
|
+
}
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
531
542
|
case "--idle-ttl": {
|
|
532
543
|
if (need(a, rest[i + 1])) {
|
|
533
544
|
const n = Number(rest[++i]);
|
|
@@ -647,6 +658,7 @@ export function parseStackArgs(argv) {
|
|
|
647
658
|
// `up` exits once the lease is active, so it can never hold a remote lease's SSH
|
|
648
659
|
// forwards and always asks for a co-located host.
|
|
649
660
|
if (opts.remote && opts.localOnly) errors.push("--remote and --local-only are mutually exclusive");
|
|
661
|
+
if (opts.localMaxLocks != null && (command !== "run" || opts.stackPathExplicit)) errors.push("--local-max-locks requires a CLI-materialized stack run without --stack-path");
|
|
650
662
|
if (opts.remote && opts.localExec) {
|
|
651
663
|
errors.push("--remote cannot be combined with --local-exec: a local-exec stack is started on THIS machine");
|
|
652
664
|
}
|
|
@@ -959,6 +971,9 @@ async function claimProjectLock(projectId, lock, subject) {
|
|
|
959
971
|
}
|
|
960
972
|
|
|
961
973
|
export async function resolveRunExecution(cwd, opts, { materialize = materializeRunStackDir, lock = acquireStackLock, identity = leasedStackIdentity, reserveBlocks = reservePortBlocks, releaseClaim = releaseLeasedStackClaim, claimHeld = leasedStackClaimHeld, stableReusable = stableRunStackReusable, markReleased = markLeasedStackReleased, idleOriginPort = idleRequesterOriginPort, stableIdle = stableRunStackIdle, recordOrigin = recordRequesterOrigin, reserveOrigin = reserveRequesterOrigin, portRegistryDir = null, warn = (line) => process.stderr.write(`${line}\n`) } = {}) {
|
|
974
|
+
if (opts.localMaxLocks != null && (opts.stackPathExplicit || (opts.stackPath && opts.stackPath !== "."))) {
|
|
975
|
+
throw new Error("local lock capacity requires a CLI-materialized stack");
|
|
976
|
+
}
|
|
962
977
|
const refuseRoot = (execution) => {
|
|
963
978
|
if (execution.stackPath === ".") {
|
|
964
979
|
throw new Error(
|
|
@@ -1095,11 +1110,14 @@ export async function resolveRunExecution(cwd, opts, { materialize = materialize
|
|
|
1095
1110
|
}
|
|
1096
1111
|
// The materialized stack stays canonical (a co-located placement serves it unchanged);
|
|
1097
1112
|
// only the remote bundle's config variant names the reserved origin.
|
|
1113
|
+
const localConfig = opts.localMaxLocks == null ? undefined : await (await import("./stack-local-locks.mjs")).patchLocalLockCapacity(
|
|
1114
|
+
readFileSync(join(worktreeRoot, "supabase", "config.toml"), "utf8"), opts.localMaxLocks,
|
|
1115
|
+
);
|
|
1098
1116
|
const execution = refuseRoot(resolveStackPath(cwd, materialize(
|
|
1099
1117
|
cwd,
|
|
1100
1118
|
{ slot: opts.slot, repo: opts.repo, ticket: opts.ticket, nonce, idleTtlSecs: opts.idleTtl ?? undefined },
|
|
1101
1119
|
// BOT-2403: a functions directory another writer replaced under this stack is reported.
|
|
1102
|
-
{ ...(portRegistryDir ? { portRegistryDir } : {}), warn },
|
|
1120
|
+
{ ...(portRegistryDir ? { portRegistryDir } : {}), ...(localConfig === undefined ? {} : { configText: localConfig }), warn },
|
|
1103
1121
|
)));
|
|
1104
1122
|
let originConfigText = null;
|
|
1105
1123
|
if (requesterOrigin) {
|
|
@@ -4232,7 +4250,7 @@ async function runStackLifecycleOnce(opts, childArgv, adapters, incidentBox) {
|
|
|
4232
4250
|
let hasDeclaration = false;
|
|
4233
4251
|
try { hasDeclaration = existsSync(declaration) && lstatSync(declaration).isFile(); } catch { hasDeclaration = false; }
|
|
4234
4252
|
if (remoteVariant && !originConfigPath) {
|
|
4235
|
-
const error = new Error("the requester-origin config variant has no materialized config to replace"); error.code = "REQUESTER_ORIGIN_DIGEST_UNAVAILABLE"; throw error;
|
|
4253
|
+
const error = new Error("the requester-origin config variant has no materialized config to replace"); error.code = "REQUESTER_ORIGIN_DIGEST_UNAVAILABLE"; error.failureMechanism = "origin_config_missing"; throw error;
|
|
4236
4254
|
}
|
|
4237
4255
|
return buildBundle(stackDirFor(execution), {
|
|
4238
4256
|
...(remoteVariant ? { readFile: (abs, ...rest) => abs === originConfigPath ? Buffer.from(execution.originConfigText, "utf8") : readFileSync(abs, ...rest) } : {}),
|
|
@@ -4270,7 +4288,8 @@ async function runStackLifecycleOnce(opts, childArgv, adapters, incidentBox) {
|
|
|
4270
4288
|
// hide it — and it happens before any lease exists.
|
|
4271
4289
|
if (executionRequest === "remote" || bundleInclude.length) {
|
|
4272
4290
|
const fixable = typeof error?.code === "string" && error.code.startsWith("BUNDLE_");
|
|
4273
|
-
return { exitCode: fixable ? EXIT.INVALID : EXIT.INTERNAL, outcome: "error", code: error?.code ?? "BUNDLE_FAILED", error: message
|
|
4291
|
+
return { exitCode: fixable ? EXIT.INVALID : EXIT.INTERNAL, outcome: "error", code: error?.code ?? "BUNDLE_FAILED", error: message,
|
|
4292
|
+
...(error?.code === "REQUESTER_ORIGIN_DIGEST_UNAVAILABLE" && error?.failureMechanism === "origin_config_missing" ? { failureMechanism: error.failureMechanism } : {}) };
|
|
4274
4293
|
}
|
|
4275
4294
|
// Codex #1077 P2: without a digest, `auto` could still pick a remote host and be
|
|
4276
4295
|
// refused BUNDLE_REQUIRED instead of queueing here, so ask for this machine.
|
|
@@ -4328,7 +4347,7 @@ async function runStackLifecycleOnce(opts, childArgv, adapters, incidentBox) {
|
|
|
4328
4347
|
let renewIdentifiedRequest = null;
|
|
4329
4348
|
if (execution.admissionRequestId) {
|
|
4330
4349
|
if (!stackDigest || !bundle?.sha256) {
|
|
4331
|
-
return { exitCode: EXIT.INTERNAL, outcome: "error", code: "REQUESTER_ORIGIN_DIGEST_UNAVAILABLE", error: "the requester-origin bundle digest could not be computed; no lease was requested" };
|
|
4350
|
+
return { exitCode: EXIT.INTERNAL, outcome: "error", code: "REQUESTER_ORIGIN_DIGEST_UNAVAILABLE", failureMechanism: "origin_digest_missing", error: "the requester-origin bundle digest could not be computed; no lease was requested" };
|
|
4332
4351
|
}
|
|
4333
4352
|
leaseArgs.request_id = execution.admissionRequestId;
|
|
4334
4353
|
// Durable request-to-context association before any request can commit (recovery key).
|
|
@@ -5305,6 +5324,8 @@ export function runReceiptFields(result) {
|
|
|
5305
5324
|
...(result.callbackCause ? { callback_cause: result.callbackCause } : {}),
|
|
5306
5325
|
// BOT-2880: which check refused a CALLBACK_AUTHORIZATION_LOST (a bounded token, never a value).
|
|
5307
5326
|
...(result.callbackAuthorizationReason ? { callback_authorization_reason: result.callbackAuthorizationReason } : {}),
|
|
5327
|
+
...(result.code === "REQUESTER_ORIGIN_DIGEST_UNAVAILABLE" && ["origin_config_missing", "origin_digest_missing"].includes(result.failureMechanism)
|
|
5328
|
+
? { failure_mechanism: result.failureMechanism } : {}),
|
|
5308
5329
|
// BOT-2957: the control-plane gaps this run's callback route rode through (never on a run that had none).
|
|
5309
5330
|
...(result.callbackOutage ? { callback_outage: result.callbackOutage } : {}),
|
|
5310
5331
|
// BOT-2780: the reserved requester origin generated auth links name (never a credential).
|