@botbuddy/cli 1.31.1 → 1.31.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-key.mjs +56 -0
- package/src/agent-session.mjs +0 -0
- package/src/credential-kinds.mjs +42 -0
- package/src/pw/run.mjs +21 -2
- package/src/run.mjs +7 -2
- package/src/setup-block.mjs +18 -25
- package/src/test-lane.mjs +6 -2
- package/src/wait.mjs +11 -1
package/package.json
CHANGED
package/src/agent-key.mjs
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
// (supabase/functions/_shared/sessionToken.ts). The prior `bb_agent_` shape is
|
|
10
10
|
// accepted as a legacy alias for one release (its hash is already on `sessions`).
|
|
11
11
|
// This is a SHAPE check only — authorization is the server's hashed lookup.
|
|
12
|
+
import { TIER3_LEGACY, credentialTier } from "./credential-kinds.mjs";
|
|
13
|
+
|
|
12
14
|
export const AGENT_KEY_RE = /^bb_(agent|sess)_[0-9a-f]{64}$/;
|
|
13
15
|
|
|
14
16
|
// BOT-1649: BOTBUDDY_AGENT_SESSION_TOKEN names the short-lived credential
|
|
@@ -32,3 +34,57 @@ export function readAgentSessionTokenEnv(env = process.env) {
|
|
|
32
34
|
|
|
33
35
|
// Deprecated exported alias for downstream CLI integrations.
|
|
34
36
|
export const readAgentKeyEnv = readAgentSessionTokenEnv;
|
|
37
|
+
|
|
38
|
+
// BOT-1701 (AC-6) — one canonical stderr nudge when a tier-3 legacy alias is used
|
|
39
|
+
// for a wait/run: a legacy CLI flag (--agent-key / --session-token), a legacy env
|
|
40
|
+
// var (BOTBUDDY_AGENT_KEY / BOTBUDDY_SESSION_TOKEN) that actually supplied the
|
|
41
|
+
// token, or a bb_agent_-prefixed token value. Returns a SINGLE line (never more)
|
|
42
|
+
// naming the canonical flag + env, or null when only canonical names were used.
|
|
43
|
+
// The caller writes it to STDERR only, so a machine-parseable stdout receipt is
|
|
44
|
+
// byte-identical to a canonical-flag run. Precedence matches the CLI: a token
|
|
45
|
+
// flag overrides the env, so the env is only "used" when no token flag was given.
|
|
46
|
+
// The value passed after any tier-3 token flag (canonical or legacy), or null.
|
|
47
|
+
function tokenAfterFlag(argv, flags) {
|
|
48
|
+
for (let i = 0; i < argv.length; i++) {
|
|
49
|
+
if (flags.includes(argv[i])) return argv[i + 1] ?? null;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function legacyAgentAliasHint({ argv = [], env = process.env, token = null } = {}) {
|
|
55
|
+
const tier3 = credentialTier(3);
|
|
56
|
+
const canonicalFlag = tier3.flag; // --agent-session-token
|
|
57
|
+
const canonicalEnv = tier3.env; // BOTBUDDY_AGENT_SESSION_TOKEN
|
|
58
|
+
const tokenFlags = [canonicalFlag, ...TIER3_LEGACY.flags];
|
|
59
|
+
|
|
60
|
+
// Only the CLI's own args count. `bb run/test … -- <workload> --agent-key …`
|
|
61
|
+
// forwards a workload after `--` whose identically named flags are NOT BotBuddy
|
|
62
|
+
// credentials, so never scan past the separator (Codex R6).
|
|
63
|
+
const sep = argv.indexOf("--");
|
|
64
|
+
const cliArgs = sep === -1 ? argv : argv.slice(0, sep);
|
|
65
|
+
|
|
66
|
+
const legacyFlagUsed = TIER3_LEGACY.flags.some((f) => cliArgs.includes(f));
|
|
67
|
+
const flagToken = tokenAfterFlag(cliArgs, tokenFlags);
|
|
68
|
+
const anyTokenFlag = flagToken !== null || legacyFlagUsed || cliArgs.includes(canonicalFlag);
|
|
69
|
+
|
|
70
|
+
// The value the env would supply (canonical wins), so a bb_agent_ VALUE behind
|
|
71
|
+
// the canonical flag/env is nudged even though the NAME is canonical.
|
|
72
|
+
let legacyEnvUsed = false;
|
|
73
|
+
let envToken = null;
|
|
74
|
+
if (!anyTokenFlag) {
|
|
75
|
+
const canonical = typeof env[canonicalEnv] === "string" ? env[canonicalEnv].trim() : "";
|
|
76
|
+
if (canonical) {
|
|
77
|
+
envToken = env[canonicalEnv];
|
|
78
|
+
} else {
|
|
79
|
+
for (const e of TIER3_LEGACY.envs) {
|
|
80
|
+
if (typeof env[e] === "string" && env[e].trim() !== "") { legacyEnvUsed = true; envToken = env[e]; break; }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const effectiveToken = token ?? flagToken ?? envToken;
|
|
86
|
+
const legacyPrefixUsed = typeof effectiveToken === "string" && effectiveToken.startsWith(TIER3_LEGACY.prefix);
|
|
87
|
+
|
|
88
|
+
if (!legacyFlagUsed && !legacyEnvUsed && !legacyPrefixUsed) return null;
|
|
89
|
+
return `note: --agent-key / --session-token / $BOTBUDDY_AGENT_KEY / ${TIER3_LEGACY.prefix} are one-release legacy aliases — use ${canonicalFlag} or $${canonicalEnv} (docs/agent-connectivity.md)`;
|
|
90
|
+
}
|
package/src/agent-session.mjs
CHANGED
|
Binary file
|
package/src/credential-kinds.mjs
CHANGED
|
@@ -30,6 +30,48 @@ export const CREDENTIAL_PREFIXES = [
|
|
|
30
30
|
{ prefix: "bb_", kind: "legacy", label: "legacy personal key" },
|
|
31
31
|
];
|
|
32
32
|
|
|
33
|
+
// BOT-1701 — the ONE canonical per-tier credential contract. The same noun,
|
|
34
|
+
// prefix, CLI flag, env var, and `credential_kind` for each tier, named the same
|
|
35
|
+
// way EVERYWHERE it is met: the setup-block help text, the connectivity guide's
|
|
36
|
+
// tier table, the `bb wait/run/test/pw --help` flags, the connectivity error
|
|
37
|
+
// strings, and the server's `whoami credential_kind`. A drift test
|
|
38
|
+
// (credential-naming-contract.test.mjs) fails when any of those surfaces names a
|
|
39
|
+
// tier-3 credential by anything other than the canonical noun/flag/env below.
|
|
40
|
+
// Decided by BOT-1649 — do not re-decide here. Additive only: legacy aliases
|
|
41
|
+
// (TIER3_LEGACY) keep working for one release and are listed separately so a
|
|
42
|
+
// surface can name them ONLY on a line that says "alias"/"legacy".
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {{ tier: 1|2|3, noun: string, prefix: string, flag: string|null, env: string, kind: string }} CredentialTier
|
|
45
|
+
* @type {ReadonlyArray<CredentialTier>}
|
|
46
|
+
*/
|
|
47
|
+
export const CREDENTIAL_TIERS = Object.freeze([
|
|
48
|
+
Object.freeze({ tier: 1, noun: "client key", prefix: "bb_cli_", flag: null, env: "BOTBUDDY_CLIENT_KEY", kind: "cli" }),
|
|
49
|
+
Object.freeze({ tier: 2, noun: "MCP key", prefix: "bb_mcp_", flag: null, env: "BOTBUDDY_MCP_KEY", kind: "mcp" }),
|
|
50
|
+
Object.freeze({ tier: 3, noun: "agent session token", prefix: "bb_sess_", flag: "--agent-session-token", env: "BOTBUDDY_AGENT_SESSION_TOKEN", kind: "agent_session" }),
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The tier-3 legacy aliases, accepted for one release. Kept apart from the
|
|
55
|
+
* canonical table so a surface can name them only on an "alias"/"legacy" line and
|
|
56
|
+
* the CLI can nudge an operator toward the canonical flag/env (BOT-1701 AC-6).
|
|
57
|
+
*/
|
|
58
|
+
export const TIER3_LEGACY = Object.freeze({
|
|
59
|
+
flags: Object.freeze(["--agent-key", "--session-token"]),
|
|
60
|
+
envs: Object.freeze(["BOTBUDDY_AGENT_KEY", "BOTBUDDY_SESSION_TOKEN"]),
|
|
61
|
+
prefix: "bb_agent_",
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
/** The canonical tier row for a tier number (1|2|3), or undefined. */
|
|
65
|
+
export function credentialTier(tier) {
|
|
66
|
+
return CREDENTIAL_TIERS.find((t) => t.tier === tier);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The canonical tier row whose prefix a secret carries, or undefined. */
|
|
70
|
+
export function credentialTierByPrefix(secret) {
|
|
71
|
+
if (typeof secret !== "string") return undefined;
|
|
72
|
+
return CREDENTIAL_TIERS.find((t) => secret.startsWith(t.prefix));
|
|
73
|
+
}
|
|
74
|
+
|
|
33
75
|
/**
|
|
34
76
|
* Classify a raw secret by its prefix.
|
|
35
77
|
* @param {unknown} secret
|
package/src/pw/run.mjs
CHANGED
|
@@ -7,13 +7,13 @@ import { canonicalizeHostString } from "./host.mjs";
|
|
|
7
7
|
import { resolveAgentBinding } from "../wait-profile.mjs";
|
|
8
8
|
import { loadConfig, getConfig } from "../config.mjs";
|
|
9
9
|
import { VERSION } from "../version.mjs";
|
|
10
|
-
import { readAgentSessionTokenEnv, AGENT_KEY_RE } from "../agent-key.mjs";
|
|
10
|
+
import { readAgentSessionTokenEnv, AGENT_KEY_RE, legacyAgentAliasHint } from "../agent-key.mjs";
|
|
11
11
|
import { clearAgentState, isRejectedCachedMcpSession, resolveAgentSessionCredential } from "../agent-session.mjs";
|
|
12
12
|
// BOT-1488: canonicalize the raw hostname the SAME way acquire_resources does
|
|
13
13
|
// server-side, so the lane name bb-pw builds/matches/prints is the one the lock
|
|
14
14
|
// kernel actually stored ("jonos-mbp:8", not "Jonos-MBP.localdomain:8").
|
|
15
15
|
const hostFor = (env) => canonicalizeHostString(env.PLAYWRIGHT_MCP_HOST || env.HOSTNAME || os.hostname());
|
|
16
|
-
function help(out) { out.write("Usage: pw [--tenant <slug>] [--session-id <id>] <lane> <verb> [args…]\n\nAliases: bb-pw <lane> <verb> [args…] · botbuddy pw <lane> <verb> [args…]\n (all three drive the same lock-gated Playwright lane)\n\n--session-id <id> accept a lane held by this arming-session agent id (from\n register_agent); defaults to $BOTBUDDY_SESSION_ID, then the\n id saved by `botbuddy register`.\n--tenant <slug> override the worktree .botbuddy-agent.json tenant when falling\n back to the .mcp.json ($BOTBUDDY_MCP_KEY) credential.\n"); }
|
|
16
|
+
function help(out) { out.write("Usage: pw [--tenant <slug>] [--session-id <id>] [--agent-session-token <token>] <lane> <verb> [args…]\n\nAliases: bb-pw <lane> <verb> [args…] · botbuddy pw <lane> <verb> [args…]\n (all three drive the same lock-gated Playwright lane)\n\n--session-id <id> accept a lane held by this arming-session agent id (from\n register_agent); defaults to $BOTBUDDY_SESSION_ID, then the\n id saved by `botbuddy register`.\n--agent-session-token <token> session credential (env $BOTBUDDY_AGENT_SESSION_TOKEN);\n --agent-key / --session-token are one-release legacy aliases.\n--tenant <slug> override the worktree .botbuddy-agent.json tenant when falling\n back to the .mcp.json ($BOTBUDDY_MCP_KEY) credential.\n"); }
|
|
17
17
|
function redact(value, secretValues = []) { return secretValues.reduce((text, secret) => secret ? text.split(secret).join("[redacted]") : text, String(value ?? "")); }
|
|
18
18
|
// BOT-1488: the register_agent identity for this machine, persisted by
|
|
19
19
|
// `botbuddy register` into ~/.botbuddy/config.json. This is the SESSION agent
|
|
@@ -99,9 +99,28 @@ export async function runPw(argv, deps = {}) {
|
|
|
99
99
|
if (code !== 0) stderr.write(`${versionLine()} — if your checkout's cli/package.json is newer, this global is stale: npm i -g @botbuddy/cli@latest, or run node cli/bin/bb-pw.mjs from the repo.\n`);
|
|
100
100
|
return code;
|
|
101
101
|
}
|
|
102
|
+
// The leading option/value pairs bb pw consumes before the lane/verb (mirrors the
|
|
103
|
+
// while-loop in runPwInner). Everything after the first non-option (the lane) is a
|
|
104
|
+
// Playwright workload argument and is never a BotBuddy credential.
|
|
105
|
+
const PW_LEADING_FLAGS = ["--tenant", "--session-id", "--agent-session-token", "--agent-key", "--session-token"];
|
|
106
|
+
function leadingPwOptions(args) {
|
|
107
|
+
const lead = [];
|
|
108
|
+
for (let i = 0; i < args.length && PW_LEADING_FLAGS.includes(args[i]); i += 2) {
|
|
109
|
+
lead.push(args[i]);
|
|
110
|
+
if (i + 1 < args.length) lead.push(args[i + 1]);
|
|
111
|
+
}
|
|
112
|
+
return lead;
|
|
113
|
+
}
|
|
114
|
+
|
|
102
115
|
async function runPwInner(argv, deps = {}) {
|
|
103
116
|
const env = deps.env ?? process.env, stdout = deps.stdout ?? process.stdout, stderr = deps.stderr ?? process.stderr; let args = [...argv]; if (["--help", "-h"].includes(args[0])) { help(stdout); return 0; }
|
|
104
117
|
if (["--version", "-v"].includes(args[0])) { stdout.write(`${versionLine()}\n`); return 0; }
|
|
118
|
+
// BOT-1701 (AC-6): one stderr nudge for a tier-3 legacy flag/env alias. bb pw
|
|
119
|
+
// only recognizes credential flags as LEADING options (the while-loop below);
|
|
120
|
+
// a `--agent-key` after the lane/verb is a Playwright workload argument, not a
|
|
121
|
+
// BotBuddy credential, so scan only the leading options (Codex R9).
|
|
122
|
+
const aliasHint = legacyAgentAliasHint({ argv: leadingPwOptions(args), env });
|
|
123
|
+
if (aliasHint) stderr.write(`bb pw: ${aliasHint}\n`);
|
|
105
124
|
// BOT-1649: --agent-session-token / $BOTBUDDY_AGENT_SESSION_TOKEN is canonical;
|
|
106
125
|
// legacy flags and env aliases remain accepted as a
|
|
107
126
|
// session identity alongside --session-id, so a token-armed session need not
|
package/src/run.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import { spawn } from "node:child_process";
|
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
import { callToolJson } from "./api.mjs";
|
|
16
16
|
import { parseLaneEvents, laneCaseCounts, flushLaneCases, buildLaneSummary, laneSummaryFilename } from "./test-lane-events.mjs";
|
|
17
|
-
import { AGENT_KEY_RE, readAgentSessionTokenEnv } from "./agent-key.mjs";
|
|
17
|
+
import { AGENT_KEY_RE, readAgentSessionTokenEnv, legacyAgentAliasHint } from "./agent-key.mjs";
|
|
18
18
|
import { clearAgentState, isRejectedCachedMcpSession, resolveAgentSessionCredential } from "./agent-session.mjs";
|
|
19
19
|
import { createTelemetryOutbox } from "./telemetry-outbox.mjs";
|
|
20
20
|
import { deliverExecutionEvent } from "./telemetry-delivery.mjs";
|
|
@@ -532,9 +532,14 @@ export async function cmdRun(args) {
|
|
|
532
532
|
return;
|
|
533
533
|
}
|
|
534
534
|
if (args[0] === "help" || args[0] === "--help") {
|
|
535
|
-
console.log("botbuddy run --session-id <id> --environment <local|preview|staging|production|none> [--timeout <sec>] [--rerun-reason <json>] -- <command> [args...]\nbotbuddy run --foreground --kind <test|wait|browser|ci|hook|stack> --environment <local|preview|staging|production|none> -- <command> [args...]\nbotbuddy run cancel <run_id>");
|
|
535
|
+
console.log("botbuddy run --session-id <id> --environment <local|preview|staging|production|none> [--timeout <sec>] [--rerun-reason <json>] -- <command> [args...]\nbotbuddy run --foreground --kind <test|wait|browser|ci|hook|stack> --environment <local|preview|staging|production|none> -- <command> [args...]\nbotbuddy run cancel <run_id>\n --agent-session-token <token> session credential (env $BOTBUDDY_AGENT_SESSION_TOKEN); --agent-key / --session-token are one-release legacy aliases");
|
|
536
536
|
return;
|
|
537
537
|
}
|
|
538
|
+
// BOT-1701 (AC-6): one stderr nudge when a tier-3 legacy flag/env alias is used
|
|
539
|
+
// (stderr only, so the stdout receipt stays byte-identical). A cached bb_agent_
|
|
540
|
+
// token is nudged inside resolveAgentSessionCredential.
|
|
541
|
+
const aliasHint = legacyAgentAliasHint({ argv: args, env: process.env });
|
|
542
|
+
if (aliasHint) process.stderr.write(`botbuddy run: ${aliasHint}\n`);
|
|
538
543
|
if (args.includes("--foreground")) {
|
|
539
544
|
let identity;
|
|
540
545
|
try { identity = await loadTelemetryIdentity(); }
|
package/src/setup-block.mjs
CHANGED
|
@@ -8,46 +8,39 @@
|
|
|
8
8
|
// session persistence; normal operator output must never turn that into a
|
|
9
9
|
// multi-command credential handoff.
|
|
10
10
|
|
|
11
|
+
import { credentialTier } from "./credential-kinds.mjs";
|
|
12
|
+
|
|
11
13
|
/** The canonical guide, referenced from every SETUP block and recovery. */
|
|
12
14
|
export const CONNECTIVITY_GUIDE = "docs/agent-connectivity.md";
|
|
13
15
|
|
|
14
16
|
// The three connectivity credential tiers, named consistently everywhere. A
|
|
15
17
|
// connectivity error names the MISSING tier and the exact next command; the
|
|
16
18
|
// guide's tiered table and the help SETUP block use these same labels.
|
|
19
|
+
//
|
|
20
|
+
// BOT-1701: the noun, prefix, flag, env, and kind for each tier are OWNED by
|
|
21
|
+
// credential-kinds.mjs (CREDENTIAL_TIERS) — the single source of truth. This map
|
|
22
|
+
// only adds the setup-specific fields (the tier number, the `setup` command, and
|
|
23
|
+
// the scope). A drift test asserts every user-facing surface agrees with the
|
|
24
|
+
// canonical table, so `label`/`prefix`/`env`/`flag` here can never fork from it.
|
|
17
25
|
export const TIERS = Object.freeze({
|
|
18
26
|
// Tier 1 — per-machine client key. `bb login`, USER-scoped (reaches every
|
|
19
27
|
// tenant you belong to; each request pins one). Never a wait credential.
|
|
20
|
-
client: Object.freeze({
|
|
21
|
-
n: 1,
|
|
22
|
-
label: "client key",
|
|
23
|
-
prefix: "bb_cli_",
|
|
24
|
-
env: "BOTBUDDY_CLIENT_KEY",
|
|
25
|
-
setup: "bb login",
|
|
26
|
-
scope: "user",
|
|
27
|
-
}),
|
|
28
|
+
client: Object.freeze({ ...tierFields(1), setup: "bb login", scope: "user" }),
|
|
28
29
|
// Tier 2 — per-tenant MCP config key. `bb mcp setup`, TENANT-bound, lives in
|
|
29
30
|
// .mcp.json under $BOTBUDDY_MCP_KEY. Authenticates MCP calls + register_agent.
|
|
30
|
-
mcp: Object.freeze({
|
|
31
|
-
|
|
32
|
-
label: "MCP key",
|
|
33
|
-
prefix: "bb_mcp_",
|
|
34
|
-
env: "BOTBUDDY_MCP_KEY",
|
|
35
|
-
setup: "bb mcp setup",
|
|
36
|
-
scope: "tenant",
|
|
37
|
-
}),
|
|
38
|
-
// Tier 3 — per-session agent token, tenant-bound, 8 h TTL, revoked on
|
|
31
|
+
mcp: Object.freeze({ ...tierFields(2), setup: "bb mcp setup", scope: "tenant" }),
|
|
32
|
+
// Tier 3 — per-session agent session token, tenant-bound, 8 h TTL, revoked on
|
|
39
33
|
// re-register. The CLI caches it in .botbuddy/agent-state.json and restores
|
|
40
34
|
// it automatically for wait/run/test/pw.
|
|
41
|
-
session: Object.freeze({
|
|
42
|
-
n: 3,
|
|
43
|
-
label: "session token",
|
|
44
|
-
prefix: "bb_sess_",
|
|
45
|
-
env: "BOTBUDDY_AGENT_SESSION_TOKEN",
|
|
46
|
-
setup: "register_agent",
|
|
47
|
-
scope: "session",
|
|
48
|
-
}),
|
|
35
|
+
session: Object.freeze({ ...tierFields(3), setup: "register_agent", scope: "session" }),
|
|
49
36
|
});
|
|
50
37
|
|
|
38
|
+
/** Project a canonical CREDENTIAL_TIERS row onto the TIERS shape (n/label/…). */
|
|
39
|
+
function tierFields(tier) {
|
|
40
|
+
const t = credentialTier(tier);
|
|
41
|
+
return { n: t.tier, label: t.noun, prefix: t.prefix, env: t.env, flag: t.flag };
|
|
42
|
+
}
|
|
43
|
+
|
|
51
44
|
/**
|
|
52
45
|
* The shared SETUP block reproduced verbatim in every `--help` output. Plain
|
|
53
46
|
* text (no ANSI) so the snapshot test can match it byte-for-byte across surfaces.
|
package/src/test-lane.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import { execFileSync } from "node:child_process";
|
|
|
21
21
|
|
|
22
22
|
import { EXIT, launchRun, receiptPath, DEFAULT_TIMEOUT_SECONDS } from "./run.mjs";
|
|
23
23
|
import { callToolJson } from "./api.mjs";
|
|
24
|
-
import { readAgentSessionTokenEnv } from "./agent-key.mjs";
|
|
24
|
+
import { readAgentSessionTokenEnv, legacyAgentAliasHint } from "./agent-key.mjs";
|
|
25
25
|
import { clearAgentState, isRejectedCachedMcpSession, resolveAgentSessionCredential } from "./agent-session.mjs";
|
|
26
26
|
|
|
27
27
|
// EXIT.{OK,INVALID,BACKEND,INTERNAL} plus a --wait timeout code (AC-9).
|
|
@@ -338,7 +338,8 @@ function laneList(cwd) {
|
|
|
338
338
|
}
|
|
339
339
|
|
|
340
340
|
function testHelp() {
|
|
341
|
-
console.log(`botbuddy test <lane> [--session-id <uuid>] [--environment local] [--ticket <KEY>] [--pr <n>] [--repo <owner/repo>] [--lane-kind <${LANE_KINDS.join("|")}>] [--wait] [--json] [-- <extra args>]
|
|
341
|
+
console.log(`botbuddy test <lane> [--session-id <uuid>] [--agent-session-token <token>] [--environment local] [--ticket <KEY>] [--pr <n>] [--repo <owner/repo>] [--lane-kind <${LANE_KINDS.join("|")}>] [--wait] [--json] [-- <extra args>]
|
|
342
|
+
--agent-session-token <token> session credential (env $BOTBUDDY_AGENT_SESSION_TOKEN); --agent-key / --session-token are one-release legacy aliases
|
|
342
343
|
botbuddy test list List the lanes configured in .botbuddy/lanes.json
|
|
343
344
|
botbuddy test help Show this help
|
|
344
345
|
|
|
@@ -351,6 +352,9 @@ export async function cmdTest(args, { cwd = process.cwd() } = {}) {
|
|
|
351
352
|
const { subcommand } = parseTestArgs(args);
|
|
352
353
|
if (subcommand === "help" || args[0] === "--help" || args[0] === "-h") { testHelp(); return; }
|
|
353
354
|
if (subcommand === "list") { process.exitCode = laneList(cwd); return; }
|
|
355
|
+
// BOT-1701 (AC-6): one stderr nudge for a tier-3 legacy flag/env alias.
|
|
356
|
+
const aliasHint = legacyAgentAliasHint({ argv: args, env: process.env });
|
|
357
|
+
if (aliasHint) process.stderr.write(`botbuddy test: ${aliasHint}\n`);
|
|
354
358
|
const result = await launchTestLane(args, { cwd });
|
|
355
359
|
process.stdout.write(result.line + "\n");
|
|
356
360
|
// --wait (AC-9): after launch, block until the run completes and exit with the
|
package/src/wait.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import { readAgentBinding, withPrincipalReceipt } from "./wait-profile.mjs";
|
|
|
18
18
|
import { VERSION } from "./version.mjs";
|
|
19
19
|
import { fileURLToPath } from "node:url";
|
|
20
20
|
import { latestPublicCliCommand } from "./public-invocation.mjs";
|
|
21
|
-
import { AGENT_KEY_RE, readAgentSessionTokenEnv } from "./agent-key.mjs";
|
|
21
|
+
import { AGENT_KEY_RE, readAgentSessionTokenEnv, legacyAgentAliasHint } from "./agent-key.mjs";
|
|
22
22
|
import { clearAgentState, hasBootstrapAgentSession, isRejectedCachedSession, resolveAgentSessionCredential, selfHealAgentSession } from "./agent-session.mjs";
|
|
23
23
|
import { touchAgentStateExpiry } from "./agent-state.mjs";
|
|
24
24
|
import { SETUP_BLOCK } from "./setup-block.mjs";
|
|
@@ -1344,6 +1344,16 @@ async function acknowledgeSavedWait(id) {
|
|
|
1344
1344
|
|
|
1345
1345
|
export async function runWait(argv, { recoveryLocalWaitId = null } = {}) {
|
|
1346
1346
|
if (argv.length === 0 || argv[0] === "--offset") return inspectSavedWaits(argv);
|
|
1347
|
+
// BOT-1701 (AC-6): one stderr nudge for a tier-3 legacy alias (flag, env, or a
|
|
1348
|
+
// bb_agent_ token value), emitted BEFORE any subcommand dispatch so the
|
|
1349
|
+
// credentialed recovery actions (resume/cancel/acknowledge) warn too (Codex R7).
|
|
1350
|
+
// STDERR only, so a machine-parseable stdout receipt is byte-identical. Skipped
|
|
1351
|
+
// on the internal recovery re-entry (resumeSavedWait recurses into runWait with
|
|
1352
|
+
// recoveryLocalWaitId set) so a resume never nudges twice (Codex R8).
|
|
1353
|
+
if (recoveryLocalWaitId == null) {
|
|
1354
|
+
const aliasHint = legacyAgentAliasHint({ argv, env: process.env });
|
|
1355
|
+
if (aliasHint) process.stderr.write(`botbuddy wait: ${aliasHint}\n`);
|
|
1356
|
+
}
|
|
1347
1357
|
if (argv[0] === "resume") {
|
|
1348
1358
|
const id = argv[1];
|
|
1349
1359
|
if (!id || argv.length > 2) return emitSavedWaitError(new WaitCheckpointError("wait_not_resumable", "select exactly one saved wait: bb wait resume <local_wait_id>"));
|