@botbuddy/cli 1.36.1 → 1.36.3
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/package.json +1 -1
- package/src/agent-session.mjs +0 -0
- package/src/session-bootstrap.mjs +4 -4
- package/src/stack.mjs +50 -9
- package/src/wait.mjs +6 -5
package/package.json
CHANGED
package/src/agent-session.mjs
CHANGED
|
Binary file
|
|
@@ -43,10 +43,10 @@ export async function saveHarnessSession(session, { cwd = process.cwd() } = {})
|
|
|
43
43
|
worktree: root,
|
|
44
44
|
minted_at: new Date().toISOString(),
|
|
45
45
|
expires_at: session.expires_at,
|
|
46
|
-
// BOT-1650 (Codex round-28 P2): mark provenance
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
46
|
+
// BOT-1650 (Codex round-28 P2): mark bootstrap provenance. Since BOT-2000 a
|
|
47
|
+
// TIMER wait relays for ANY runtime cache (bootstrap or bb setup / self-heal),
|
|
48
|
+
// so this marker no longer gates timer relaying; it still distinguishes a
|
|
49
|
+
// bootstrap-issued cache for the bootstrap bridge's own reuse checks.
|
|
50
50
|
harness_origin: "bootstrap_agent_session",
|
|
51
51
|
...(session.session_token_generation ? { session_token_generation: session.session_token_generation } : {}),
|
|
52
52
|
};
|
package/src/stack.mjs
CHANGED
|
@@ -1279,21 +1279,62 @@ export const STACK_DISPOSITIONS = Object.freeze(["destroy", "stop"]);
|
|
|
1279
1279
|
* the project's data volumes (`supabase_db_/storage_/edge_runtime_<project_id>`); `--workdir`
|
|
1280
1280
|
* scopes that to the stack's own project_id, never `--all`. `stop` keeps the volumes for a
|
|
1281
1281
|
* cheap re-provision. An unknown disposition throws before Docker is touched.
|
|
1282
|
+
*
|
|
1283
|
+
* BOT-2001: when `supabase stop` fails (2026-10-02: a transient exit 1 whose real error sat
|
|
1284
|
+
* behind the CLI's deprecation WARN), log the tail of its output and fall back to a
|
|
1285
|
+
* label-scoped teardown of exactly this project (`com.supabase.cli.project=<project_id>`),
|
|
1286
|
+
* on the same validated daemon. Success requires the post-check `docker ps -a` for that
|
|
1287
|
+
* label to be empty; no readable project_id → no fallback, never an unscoped `docker rm`.
|
|
1282
1288
|
*/
|
|
1283
|
-
export function localTeardown(stackDir, dockerTarget, disposition, spawn = spawnSync, env = process.env
|
|
1289
|
+
export function localTeardown(stackDir, dockerTarget, disposition, spawn = spawnSync, env = process.env, {
|
|
1290
|
+
readProjectId = projectIdUnder, log = (line) => process.stderr.write(line),
|
|
1291
|
+
} = {}) {
|
|
1284
1292
|
if (!STACK_DISPOSITIONS.includes(disposition)) {
|
|
1285
1293
|
throw new Error(`localTeardown: unknown disposition ${JSON.stringify(disposition)} (expected one of ${STACK_DISPOSITIONS.join(", ")})`);
|
|
1286
1294
|
}
|
|
1287
1295
|
const args = ["stop", "--workdir", stackDir, ...(disposition === "destroy" ? ["--no-backup"] : [])];
|
|
1288
|
-
|
|
1289
|
-
const
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1296
|
+
log(`${yellow("⚠ LOCAL-EXEC FALLBACK")} — running ${bold(`supabase ${args.join(" ")}`)} (disposition ${disposition}).\n`);
|
|
1297
|
+
const dockerEnv = dockerEnvForTarget(dockerTarget, env);
|
|
1298
|
+
const res = spawn("supabase", args, { encoding: "utf8", env: dockerEnv });
|
|
1299
|
+
if (res.status === 0) return true;
|
|
1300
|
+
// The real error is LAST (WARN/upgrade notices lead), so keep the tail, not the head.
|
|
1301
|
+
const output = `${res.stderr || ""}${res.stdout || ""}`.trim();
|
|
1302
|
+
log(`${yellow("⚠")} stack: supabase stop returned ${res.status}:\n${output.slice(-2000)}\n`);
|
|
1303
|
+
const projectId = readProjectId(stackDir);
|
|
1304
|
+
if (!projectId) {
|
|
1305
|
+
log(`${yellow("⚠")} stack: no readable project_id under ${stackDir} — not attempting a label-scoped teardown.\n`);
|
|
1306
|
+
return false;
|
|
1307
|
+
}
|
|
1308
|
+
return labelScopedTeardown(projectId, disposition, spawn, dockerEnv, log);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
/** BOT-2001: remove exactly one Supabase project's containers, network and (for `destroy`)
|
|
1312
|
+
* volumes by the CLI's project label; true iff a re-list finds NONE of the resource classes
|
|
1313
|
+
* this disposition removes (Codex #1012: a busy volume or network fails closed too). */
|
|
1314
|
+
function labelScopedTeardown(projectId, disposition, spawn, dockerEnv, log) {
|
|
1315
|
+
const filter = ["--filter", `label=com.supabase.cli.project=${projectId}`];
|
|
1316
|
+
const docker = (args) => spawn("docker", args, { encoding: "utf8", env: dockerEnv });
|
|
1317
|
+
const ids = (res) => (res.status === 0 ? (res.stdout || "").split(/\s+/).filter(Boolean) : null);
|
|
1318
|
+
const classes = [
|
|
1319
|
+
{ name: "container", list: ["ps", "-aq", ...filter], remove: ["rm", "-f"] },
|
|
1320
|
+
...(disposition === "destroy" ? [{ name: "volume", list: ["volume", "ls", "-q", ...filter], remove: ["volume", "rm", "-f"] }] : []),
|
|
1321
|
+
{ name: "network", list: ["network", "ls", "-q", ...filter], remove: ["network", "rm"] },
|
|
1322
|
+
];
|
|
1323
|
+
log(`${yellow("⚠")} stack: falling back to a label-scoped teardown of project ${bold(projectId)}.\n`);
|
|
1324
|
+
for (const c of classes) {
|
|
1325
|
+
const found = ids(docker(c.list));
|
|
1326
|
+
if (found?.length) {
|
|
1327
|
+
const rm = docker([...c.remove, ...found]);
|
|
1328
|
+
if (rm.status !== 0) log(`${yellow("⚠")} stack: removing ${c.name}(s) of ${projectId} failed: ${`${rm.stderr || rm.stdout || ""}`.trim().slice(-500)}\n`);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
const left = classes.map((c) => ({ name: c.name, ids: ids(docker(c.list)) })).filter((c) => c.ids === null || c.ids.length);
|
|
1332
|
+
if (left.length) {
|
|
1333
|
+
log(`${yellow("⚠")} stack: project ${projectId} still has ${left.map((c) => (c.ids === null ? `unverifiable ${c.name}s` : `${c.ids.length} ${c.name}(s)`)).join(", ")} after the fallback — the slot stays fenced.\n`);
|
|
1334
|
+
return false;
|
|
1295
1335
|
}
|
|
1296
|
-
|
|
1336
|
+
log(`${yellow("⚠")} stack: label-scoped teardown of project ${projectId} succeeded.\n`);
|
|
1337
|
+
return true;
|
|
1297
1338
|
}
|
|
1298
1339
|
|
|
1299
1340
|
function emit(receipt, opts, code) {
|
package/src/wait.mjs
CHANGED
|
@@ -19,7 +19,7 @@ import { VERSION } from "./version.mjs";
|
|
|
19
19
|
import { fileURLToPath } from "node:url";
|
|
20
20
|
import { latestPublicCliCommand } from "./public-invocation.mjs";
|
|
21
21
|
import { AGENT_KEY_RE, readAgentSessionTokenEnv, legacyAgentAliasHint } from "./agent-key.mjs";
|
|
22
|
-
import { clearAgentState,
|
|
22
|
+
import { clearAgentState, hasAgentSessionRuntime, isRejectedCachedSession, resolveAgentSessionCredential, selfHealAgentSession } from "./agent-session.mjs";
|
|
23
23
|
import { touchAgentStateExpiry } from "./agent-state.mjs";
|
|
24
24
|
import { SETUP_BLOCK } from "./setup-block.mjs";
|
|
25
25
|
import { createWaitCheckpointStore, WaitCheckpointError } from "./wait-checkpoint.mjs";
|
|
@@ -1568,11 +1568,12 @@ export async function runWait(argv, { recoveryLocalWaitId = null } = {}) {
|
|
|
1568
1568
|
: new WaitCheckpointError("wait_checkpoint_write_failed", String(error?.message ?? error)));
|
|
1569
1569
|
}
|
|
1570
1570
|
|
|
1571
|
-
// BOT-1650: a
|
|
1572
|
-
// registers so
|
|
1573
|
-
//
|
|
1571
|
+
// BOT-1650 / BOT-2000: a timer from a worktree with any agent-session runtime
|
|
1572
|
+
// file (bootstrap or `bb setup`) registers, so it shows on /waits and its terminal
|
|
1573
|
+
// receipt proves the session principal. Only a credential-less worktree's timer
|
|
1574
|
+
// stays offline. Detected without a network self-heal.
|
|
1574
1575
|
const needsRelay = conditions.some((c) => c.type !== "timer")
|
|
1575
|
-
|| await
|
|
1576
|
+
|| await hasAgentSessionRuntime({ cwd: process.cwd() });
|
|
1576
1577
|
if (needsRelay) {
|
|
1577
1578
|
try {
|
|
1578
1579
|
const credential = await resolveAgentSessionCredential({ argv, env: process.env, cwd: process.cwd() });
|