@botbuddy/cli 1.64.14 → 1.64.16
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.mjs +19 -1
- package/src/wait-core.mjs +17 -0
- package/src/wait.mjs +4 -0
|
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.16",
|
|
3
|
+
"source_commit": "6690f94e4c3d385159ca315713a6eab3560289fb",
|
|
4
|
+
"source_sha256": "81838b2e5024a22f62c4b55a75c3113c26cfe88f0047f0048f28f5800d0693d3",
|
|
5
5
|
"target": "darwin-arm64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "9ca9db33cdee03687c26d5e162f4d936d44b7c1c23028c78566a61b90f7c40ad"
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.64.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.64.16",
|
|
3
|
+
"source_commit": "6690f94e4c3d385159ca315713a6eab3560289fb",
|
|
4
|
+
"source_sha256": "81838b2e5024a22f62c4b55a75c3113c26cfe88f0047f0048f28f5800d0693d3",
|
|
5
5
|
"target": "linux-x64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "b1a73bbd5bc7d78a13a3ec6f547436cc780163687bb5162e230d5796291e7689"
|
|
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.16",
|
|
4
|
+
"source_commit": "6690f94e4c3d385159ca315713a6eab3560289fb",
|
|
5
|
+
"source_sha256": "81838b2e5024a22f62c4b55a75c3113c26cfe88f0047f0048f28f5800d0693d3",
|
|
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": "9ca9db33cdee03687c26d5e162f4d936d44b7c1c23028c78566a61b90f7c40ad",
|
|
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": "b1a73bbd5bc7d78a13a3ec6f547436cc780163687bb5162e230d5796291e7689",
|
|
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
|
+
}
|
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) {
|
package/src/wait-core.mjs
CHANGED
|
@@ -225,6 +225,17 @@ const VALIDATORS = {
|
|
|
225
225
|
if (!CANONICAL_UUID_RE.test(jobId)) throw new Error("staging-pool-refill id must be a canonical UUID");
|
|
226
226
|
return { jobId };
|
|
227
227
|
},
|
|
228
|
+
// BOT-2888: a reporter waits for a revision after the last consumed update.
|
|
229
|
+
// The server binds the report to the registering agent and tenant.
|
|
230
|
+
feedback: (p, raw) => {
|
|
231
|
+
const match = /^\s*id\s*=\s*([0-9A-Fa-f-]+)\s*,\s*after\s*=\s*(0|[1-9][0-9]*)\s*$/.exec(raw ?? "");
|
|
232
|
+
if (!match || Object.keys(p).length !== 2 || p.id !== match[1] || String(p.after) !== match[2]) {
|
|
233
|
+
throw new Error("feedback needs exactly id=<feedback UUID>,after=<last consumed revision>");
|
|
234
|
+
}
|
|
235
|
+
const feedbackId = match[1].toLowerCase(), after = Number(match[2]);
|
|
236
|
+
if (!CANONICAL_UUID_RE.test(feedbackId) || !Number.isSafeInteger(after)) throw new Error("feedback requires a canonical UUID and safe nonnegative revision");
|
|
237
|
+
return { feedbackId, after };
|
|
238
|
+
},
|
|
228
239
|
// BOT-2629: this is deliberately a dedicated condition rather than a raw
|
|
229
240
|
// event escape hatch. The UUID is chosen before dispatch and the exact subject
|
|
230
241
|
// prevents another run on the same idle/re-adopted lease waking this waiter.
|
|
@@ -1309,6 +1320,11 @@ function conditionMatchesSignal(condition, signal, waitSessionId) {
|
|
|
1309
1320
|
signal.subject_key === `staging-pool-refill:${params.jobId}` &&
|
|
1310
1321
|
signal.payload?.job_id === params.jobId &&
|
|
1311
1322
|
["completed", "failed"].includes(signal.payload?.status);
|
|
1323
|
+
case "feedback":
|
|
1324
|
+
return signal.signal_type === "feedback_update" &&
|
|
1325
|
+
signal.subject_key === `feedback:${params.feedbackId}` &&
|
|
1326
|
+
signal.payload?.feedback_id === params.feedbackId &&
|
|
1327
|
+
Number.isSafeInteger(signal.payload?.revision) && signal.payload.revision > params.after;
|
|
1312
1328
|
case "stack-run":
|
|
1313
1329
|
return signal.signal_type === "stack_run" && signal.subject_key === `stack-run:${params.id}` &&
|
|
1314
1330
|
signal.payload?.run_id === params.id && Number.isInteger(signal.payload?.exit_code);
|
|
@@ -1523,6 +1539,7 @@ function conditionMatchesSignal(condition, signal, waitSessionId) {
|
|
|
1523
1539
|
export function signalTenantBlocked(signal, sessionTenant) {
|
|
1524
1540
|
if (sessionTenant === undefined) return false;
|
|
1525
1541
|
if (signal.tenant_id == null) return false;
|
|
1542
|
+
if (signal.signal_type === "feedback_update") return signal.tenant_id !== sessionTenant;
|
|
1526
1543
|
// Mirror the relay's precedence (recipient > owner > tenant): a signal that is
|
|
1527
1544
|
// owner- or recipient-scoped keeps THAT isolation and is never tenant-fenced, even
|
|
1528
1545
|
// though owner-scoped pr_review/ci_run also carry a tenant_id — otherwise a caller who
|
package/src/wait.mjs
CHANGED
|
@@ -175,6 +175,9 @@ CONDITIONS (TYPE:key=val,key=val — repeat for several; --any wakes on the fir
|
|
|
175
175
|
completed or failed. This is the only condition of its
|
|
176
176
|
wait and is available to each tenant agent that requested
|
|
177
177
|
the deduped job; its terminal receipt is safe to display.
|
|
178
|
+
feedback:id=<feedback UUID>,after=<revision>
|
|
179
|
+
Wait for a later update to your own feedback; use the
|
|
180
|
+
revision you last consumed and read/acknowledge updates.
|
|
178
181
|
stack-run:id=<run UUID> one named bb stack run reaching its terminal outcome
|
|
179
182
|
(BOT-2629). Choose the UUID BEFORE dispatch and pass it as
|
|
180
183
|
bb stack run --run-id <UUID>; the receipt carries exit_code,
|
|
@@ -660,6 +663,7 @@ async function registerWait(opts, conditions, deadlineIso) {
|
|
|
660
663
|
"command_run_single_condition_required",
|
|
661
664
|
"staging_pool_refill_unavailable",
|
|
662
665
|
"staging_pool_refill_single_condition_required",
|
|
666
|
+
"feedback_wait_unavailable",
|
|
663
667
|
]);
|
|
664
668
|
if (INVALID_CONDITION_CODES.has(body.error)) {
|
|
665
669
|
const err = new Error(body.detail || body.error);
|