@botbuddy/cli 1.40.6 → 1.40.9
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/commands.mjs +56 -19
- package/src/stack.mjs +2 -1
package/package.json
CHANGED
package/src/commands.mjs
CHANGED
|
@@ -57,14 +57,24 @@ export async function run(argv, {
|
|
|
57
57
|
loadConfig: load = loadConfig,
|
|
58
58
|
warnStale = maybeWarnStale,
|
|
59
59
|
errorLog = (line) => console.error(line),
|
|
60
|
+
bootstrap = cmdBootstrap,
|
|
60
61
|
} = {}) {
|
|
61
|
-
await load();
|
|
62
62
|
const [command, ...args] = argv;
|
|
63
|
+
// BOT-2069: `bootstrap close` can retire a live server session. Parse its
|
|
64
|
+
// own arguments before loading config, checking npm, reading the runtime
|
|
65
|
+
// cache, or resolving any credential. Help and malformed invocations return
|
|
66
|
+
// immediately; a valid plain close retains its established config/update
|
|
67
|
+
// setup before the generation-confirmed cleanup transaction.
|
|
68
|
+
if (command === "bootstrap" && args[0] === "close" && parseBootstrapCloseArgs(args).action !== "close") {
|
|
69
|
+
return bootstrap(args);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
await load();
|
|
63
73
|
if (shouldCheckForUpdates(argv)) await warnStale({ version: VERSION });
|
|
64
74
|
|
|
65
75
|
switch (command) {
|
|
66
76
|
case "start": return cmdStart(args);
|
|
67
|
-
case "bootstrap": return
|
|
77
|
+
case "bootstrap": return bootstrap(args);
|
|
68
78
|
case "login": return cmdLogin(args, { errorLog });
|
|
69
79
|
case "setup": return cmdSetup(args, { errorLog });
|
|
70
80
|
case "logout": return cmdLogout();
|
|
@@ -812,28 +822,55 @@ export async function closeStoredHarnessSession({
|
|
|
812
822
|
return result;
|
|
813
823
|
}
|
|
814
824
|
|
|
815
|
-
|
|
825
|
+
const BOOTSTRAP_CLOSE_USAGE = "Usage: bb bootstrap close [--help]";
|
|
826
|
+
|
|
827
|
+
function parseBootstrapCloseArgs(args) {
|
|
828
|
+
const rest = args.slice(1);
|
|
829
|
+
if (rest.length === 0) return { action: "close" };
|
|
830
|
+
if (rest.length === 1 && (rest[0] === "--help" || rest[0] === "-h")) return { action: "help" };
|
|
831
|
+
return { action: "invalid" };
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
export async function cmdBootstrap(args, {
|
|
835
|
+
withHarnessBootstrapLock: withLock = withHarnessBootstrapLock,
|
|
836
|
+
loadHarnessSession: loadSession = loadHarnessSession,
|
|
837
|
+
resolveOwnerToken: resolveOwner = resolveOwnerToken,
|
|
838
|
+
resolveAgentKey: resolveAgent = resolveAgentKey,
|
|
839
|
+
closeStoredHarnessSession: closeSession = closeStoredHarnessSession,
|
|
840
|
+
clearHarnessSession: clearSession = clearHarnessSession,
|
|
841
|
+
bootstrapHarnessSession: bootstrapSession = bootstrapHarnessSession,
|
|
842
|
+
getConfig: getCfg = getConfig,
|
|
843
|
+
log = (line) => console.log(line),
|
|
844
|
+
die: fail = die,
|
|
845
|
+
} = {}) {
|
|
816
846
|
if (args[0] === "close") {
|
|
817
|
-
|
|
818
|
-
|
|
847
|
+
const closeArguments = parseBootstrapCloseArgs(args);
|
|
848
|
+
if (closeArguments.action === "help") {
|
|
849
|
+
log(`${BOOTSTRAP_CLOSE_USAGE}\n\nClose this worktree's cached harness session after generation-confirmed server cleanup.`);
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
if (closeArguments.action === "invalid") return fail(BOOTSTRAP_CLOSE_USAGE);
|
|
853
|
+
|
|
854
|
+
return withLock({}, async () => {
|
|
855
|
+
const session = await loadSession();
|
|
819
856
|
if (!session) {
|
|
820
|
-
|
|
857
|
+
log(JSON.stringify({ outcome: "no_session" }));
|
|
821
858
|
return;
|
|
822
859
|
}
|
|
823
|
-
const owner = await
|
|
860
|
+
const owner = await resolveOwner({ getConfig: getCfg });
|
|
824
861
|
// Match resolveCallAuth: a stored but expired owner token must never mask
|
|
825
862
|
// a usable MCP key or the exact session credential that needs cleanup.
|
|
826
863
|
const activeOwner = owner && !(owner.expiresAt && Date.now() >= owner.expiresAt) ? owner : null;
|
|
827
|
-
const agentKey = activeOwner ? null : await
|
|
864
|
+
const agentKey = activeOwner ? null : await resolveAgent();
|
|
828
865
|
const durableAuth = activeOwner
|
|
829
866
|
? { Authorization: `Bearer ${activeOwner.token}` }
|
|
830
867
|
: agentKey
|
|
831
868
|
? { "x-agent-api-key": agentKey }
|
|
832
869
|
: { "x-agent-api-key": session.session_token };
|
|
833
|
-
const result = await
|
|
834
|
-
if (!result?.ok || result.isError)
|
|
835
|
-
await
|
|
836
|
-
|
|
870
|
+
const result = await closeSession({ session, durableAuth });
|
|
871
|
+
if (!result?.ok || result.isError) fail(result?.error || "bb bootstrap close failed");
|
|
872
|
+
await clearSession(session.session_id, { sessionToken: session.session_token, lock: false }); // already inside withHarnessBootstrapLock (non-reentrant)
|
|
873
|
+
log(JSON.stringify({ outcome: "closed", session_id: session.session_id }));
|
|
837
874
|
});
|
|
838
875
|
}
|
|
839
876
|
let harnessSessionId = null;
|
|
@@ -848,7 +885,7 @@ async function cmdBootstrap(args) {
|
|
|
848
885
|
let prUrl = null;
|
|
849
886
|
let model = null;
|
|
850
887
|
let effort = null;
|
|
851
|
-
let name =
|
|
888
|
+
let name = getCfg()?.agent_name || `botbuddy-${os.hostname()}`;
|
|
852
889
|
let nameExplicit = false;
|
|
853
890
|
for (let i = 0; i < args.length; i += 1) {
|
|
854
891
|
if (args[i] === "--session-id") harnessSessionId = args[++i] || null;
|
|
@@ -864,15 +901,15 @@ async function cmdBootstrap(args) {
|
|
|
864
901
|
else if (args[i] === "--pr-url") prUrl = args[++i] || null;
|
|
865
902
|
else if (args[i] === "--model") model = args[++i] || null;
|
|
866
903
|
else if (args[i] === "--effort") effort = args[++i] || null;
|
|
867
|
-
else
|
|
904
|
+
else fail("Usage: bb bootstrap --session-id <harness UUID> (--ticket-id <ticket> | --no-ticket-reason <reason>) [--type codex|claude|gpt|custom] [--codex-thread-id <UUID>] [--name <technical-name>] [--branch <branch>] [--ticket-url <url>] [--model <model>] [--effort <effort>]");
|
|
868
905
|
}
|
|
869
|
-
if (!harnessSessionId)
|
|
906
|
+
if (!harnessSessionId) fail("bb bootstrap requires --session-id <harness UUID>");
|
|
870
907
|
const linkageError = validateBootstrapLinkage(ticketId, noTicketReason);
|
|
871
|
-
if (linkageError)
|
|
908
|
+
if (linkageError) fail(linkageError);
|
|
872
909
|
ticketId = ticketId?.trim() || null;
|
|
873
910
|
noTicketReason = noTicketReason?.trim() || null;
|
|
874
911
|
if (!nameExplicit) name = `${name}-${harnessSessionId.slice(0, 8)}`;
|
|
875
|
-
const state = await
|
|
912
|
+
const state = await bootstrapSession({
|
|
876
913
|
name,
|
|
877
914
|
type,
|
|
878
915
|
harnessSessionId,
|
|
@@ -888,13 +925,13 @@ async function cmdBootstrap(args) {
|
|
|
888
925
|
effort,
|
|
889
926
|
harness: `botbuddy-${type}-launcher`,
|
|
890
927
|
});
|
|
891
|
-
if (state.linkage?.status === "gap")
|
|
928
|
+
if (state.linkage?.status === "gap") fail(state.linkage.warning || "bb bootstrap requires ticket attribution or --no-ticket-reason.");
|
|
892
929
|
// BOT-1945: name (never the bearer of) an unowned cached session this bootstrap
|
|
893
930
|
// replaced, so a leftover live holder is visible instead of silent.
|
|
894
931
|
// BOT-2058: the launcher must see a known-holder warning before it starts a
|
|
895
932
|
// new harness. Keep it out of the credential cache, but preserve the
|
|
896
933
|
// non-secret concurrency payload in this command receipt.
|
|
897
|
-
|
|
934
|
+
log(JSON.stringify({ agent_id: state.agent_id, display_name: state.display_name, session_id: state.session_id, expires_at: state.expires_at, ...(state.ticket_signals?.concurrency ? { ticket_session_warning: state.ticket_signals.concurrency } : {}), ...(state.replaced_cached_session ? { replaced_cached_session: state.replaced_cached_session } : {}) }));
|
|
898
935
|
}
|
|
899
936
|
|
|
900
937
|
// BOT-1566 A4: after the local-metadata lines, ask the SERVER whether the stored
|
package/src/stack.mjs
CHANGED
|
@@ -2186,7 +2186,8 @@ function waitForChild(child) {
|
|
|
2186
2186
|
function isDenoIntegrationLane(argv) {
|
|
2187
2187
|
// `test:all` is the supported package wrapper that transitively invokes
|
|
2188
2188
|
// `test:integration`; both must receive the leased runner configuration.
|
|
2189
|
-
return argv.includes("test:integration") || argv.includes("test:integration:coverage") || argv.includes("test:all") || argv.includes("scripts/run-deno-integration.sh")
|
|
2189
|
+
return argv.includes("test:integration") || argv.includes("test:integration:coverage") || argv.includes("test:all") || argv.includes("scripts/run-deno-integration.sh") ||
|
|
2190
|
+
(argv.includes("scripts/run-deno-hermetic.mjs") && argv.includes("database-checks"));
|
|
2190
2191
|
}
|
|
2191
2192
|
|
|
2192
2193
|
function requireConnectionText(connection, key) {
|