@botbuddy/cli 1.36.0 → 1.36.2
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 +31 -6
- 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
|
@@ -1587,19 +1587,24 @@ export async function cmdUp(opts, {
|
|
|
1587
1587
|
}), opts, EXIT.OK);
|
|
1588
1588
|
}
|
|
1589
1589
|
|
|
1590
|
-
async function cmdStatus(leaseId, opts, { rpc = retryingStackCall() } = {}) {
|
|
1590
|
+
export async function cmdStatus(leaseId, opts, { rpc = retryingStackCall(), emitResult = emit } = {}) {
|
|
1591
1591
|
const auth = await rpc.init();
|
|
1592
|
-
if (!auth) return
|
|
1592
|
+
if (!auth) return emitResult(buildReceipt({ command: "status", outcome: "error", lease_id: leaseId, error: "not authenticated — run `botbuddy login`" }), opts, EXIT.AUTH);
|
|
1593
1593
|
const got = await rpc.call("get_stack_lease", { lease_id: leaseId });
|
|
1594
1594
|
if (!got.ok) return got.auth
|
|
1595
|
-
?
|
|
1596
|
-
:
|
|
1595
|
+
? emitResult(buildReceipt({ command: "status", outcome: "error", lease_id: leaseId, error: got.error }), opts, EXIT.AUTH)
|
|
1596
|
+
: emitResult(buildReceipt({ command: "status", outcome: "error", lease_id: leaseId, error: got.error }), opts, EXIT.BACKEND);
|
|
1597
1597
|
const g = got.data;
|
|
1598
|
-
if (!g.success) return
|
|
1599
|
-
return
|
|
1598
|
+
if (!g.success) return emitResult(buildReceipt({ command: "status", outcome: "error", lease_id: leaseId, code: g.code, error: g.code }), opts, EXIT.BACKEND);
|
|
1599
|
+
return emitResult(buildReceipt({
|
|
1600
1600
|
command: "status", outcome: g.state, lease_id: leaseId, state: g.state,
|
|
1601
1601
|
host_key: g.host_key, slot: g.slot, connection: g.connection,
|
|
1602
1602
|
queue_position: g.queue_position, idle_ttl_secs: g.idle_ttl_secs, queued: g.queued,
|
|
1603
|
+
// BOT-1960: a lease fenced in `reaping` says since when, and — once the server gave up
|
|
1604
|
+
// retrying its failed reap — that it is reap_failed and why.
|
|
1605
|
+
...(g.reap_requested_at ? { reap_requested_at: g.reap_requested_at } : {}),
|
|
1606
|
+
...(g.reap_failed_at ? { reap_failed: true, reap_failed_at: g.reap_failed_at } : {}),
|
|
1607
|
+
...(g.state === "reaping" && g.error ? { error: g.error } : {}),
|
|
1603
1608
|
}), opts, EXIT.OK);
|
|
1604
1609
|
}
|
|
1605
1610
|
|
|
@@ -1803,7 +1808,27 @@ export async function cmdDone(leaseId, opts, {
|
|
|
1803
1808
|
if (fin.ok && fin.data?.success) state = fin.data.state;
|
|
1804
1809
|
else process.stderr.write(`${yellow("⚠")} stack: finalize failed (${fin.error || fin.data?.code}); the reaper will reconcile.\n`);
|
|
1805
1810
|
}
|
|
1811
|
+
// BOT-1960: on a lease fenced in `reaping` by a FAILED reap, the server re-enqueues the
|
|
1812
|
+
// reap (the first time also resetting its retry budget) — or, once that budget is spent,
|
|
1813
|
+
// refuses. Say which, instead of a `released` that changed nothing.
|
|
1814
|
+
const retry = r.data.reap_retry;
|
|
1815
|
+
if (state === "reaping" && retry?.code === "REAP_RETRY_BUDGET_SPENT") {
|
|
1816
|
+
const since = r.data.reap_failed_at ? ` (since ${r.data.reap_failed_at})` : "";
|
|
1817
|
+
return emitResult(buildReceipt({ command: "done", outcome: "reap_failed", lease_id: leaseId, state, disposition,
|
|
1818
|
+
reap_failed_at: r.data.reap_failed_at ?? null, reap_retry: { enqueued: false, code: retry.code },
|
|
1819
|
+
error: `the reap failed on every retry${since}; no retries left — the lease stays reaping and its slot stays fenced. ` +
|
|
1820
|
+
"Remove the project's leftover containers (operator / `botbuddy docker hygiene`); the container census then finalizes the lease.",
|
|
1821
|
+
}), opts, EXIT.LEASE_FAILED);
|
|
1822
|
+
}
|
|
1823
|
+
const retried = retry?.code === "REAP_RETRY_ENQUEUED"
|
|
1824
|
+
? {
|
|
1825
|
+
reap_retry: { enqueued: true, attempt: retry.attempt, budget_reset: retry.budget_reset === true },
|
|
1826
|
+
note: `the previous reap failed; a new reap job (attempt ${retry.attempt}) was enqueued` +
|
|
1827
|
+
(retry.budget_reset === true ? " and the reap retry budget was reset" : ""),
|
|
1828
|
+
}
|
|
1829
|
+
: {};
|
|
1806
1830
|
return emitResult(buildReceipt({ command: "done", outcome: "released", lease_id: leaseId, state, disposition,
|
|
1831
|
+
...retried,
|
|
1807
1832
|
...(helperTeardown ? { teardown: "helper" } : {}),
|
|
1808
1833
|
...(legacyTargetProof ? { legacy_target_proof: legacyTargetProof } : {}) }), opts, EXIT.OK);
|
|
1809
1834
|
}
|
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() });
|