@botbuddy/cli 1.6.0 → 1.6.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/botbuddy-release-repair.json +1 -0
- package/src/profile-bootstrap.mjs +2 -1
- package/src/public-invocation.mjs +7 -0
- package/src/stack.mjs +1 -1
- package/src/wait-core.mjs +50 -0
- package/src/wait.mjs +3 -2
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"schema_version":1,"source_version":"1.6.1","source_identity":"9cc6a78baeb0da589214386daad729a2e8909936a2407da801777f9902e8667f"}
|
|
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
|
|
4
4
|
import { ensureProfileCredentialBackend, readProfileIdentity, readProfileRetryIdentity, recordProfileRetryIdentity, installProfileCredential, profileCredentialEnvironment } from "./agent-credential-store.mjs";
|
|
5
5
|
import { callToolJson } from "./api.mjs";
|
|
6
|
+
import { latestPublicCliCommand } from "./public-invocation.mjs";
|
|
6
7
|
|
|
7
8
|
const PROFILES = Object.freeze({
|
|
8
9
|
"botbuddy-dev": Object.freeze({ tenant: "botbuddy" }),
|
|
@@ -92,7 +93,7 @@ export async function bootstrapProfile(profileName, {
|
|
|
92
93
|
tenant_id: profile.tenant,
|
|
93
94
|
agent_id: data.agent_id,
|
|
94
95
|
credential_source: "keychain_profile_slot",
|
|
95
|
-
shell_refresh: `source <(
|
|
96
|
+
shell_refresh: `source <(${latestPublicCliCommand(`profile env ${profileName}`)})`,
|
|
96
97
|
gui_refresh: "$HOME/.local/bin/botbuddy-mcp-env.sh",
|
|
97
98
|
};
|
|
98
99
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// BOT-1425: npm cannot infer a package executable once @botbuddy/cli exposes
|
|
2
|
+
// both botbuddy and bb-pw. Keep every installation-free command explicit.
|
|
3
|
+
export const LATEST_PUBLIC_CLI_PREFIX = "npm exec --yes --package=@botbuddy/cli@latest -- botbuddy";
|
|
4
|
+
|
|
5
|
+
export function latestPublicCliCommand(command = "") {
|
|
6
|
+
return command ? `${LATEST_PUBLIC_CLI_PREFIX} ${command}` : LATEST_PUBLIC_CLI_PREFIX;
|
|
7
|
+
}
|
package/src/stack.mjs
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// The canonical batch flow is: stack up → test → break → fix → test → stack done.
|
|
12
12
|
//
|
|
13
13
|
// Design contract (matches bb-wait):
|
|
14
|
-
// * Dependency-free (Node stdlib only) so `
|
|
14
|
+
// * Dependency-free (Node stdlib only) so an explicit `npm exec --package` invocation cold-starts fast.
|
|
15
15
|
// * Exactly ONE single-line JSON receipt to stdout; all diagnostics go to stderr.
|
|
16
16
|
// * Receipt ≤ 10 KB (documented, --receipt-max-bytes overridable).
|
|
17
17
|
// * Documented, meaningful exit codes.
|
package/src/wait-core.mjs
CHANGED
|
@@ -228,6 +228,35 @@ const VALIDATORS = {
|
|
|
228
228
|
}
|
|
229
229
|
return { repo };
|
|
230
230
|
},
|
|
231
|
+
// BOT-1458 — staging-release: wake when the first staging release train whose
|
|
232
|
+
// head commit CONTAINS this PR's merge commit reaches a terminal conclusion
|
|
233
|
+
// (success OR failure). Unlike `staging-green` this is PR-scoped and reports
|
|
234
|
+
// the train's outcome, never a health verdict. Inclusion is decided server-side
|
|
235
|
+
// by git ancestry (GitHub compare API), never by wall-clock ordering, so the
|
|
236
|
+
// condition only names the repo + PR; the relay stamps the resolved
|
|
237
|
+
// merge_commit_sha and workspace at registration. Optional
|
|
238
|
+
// `after_run=<run_id>[:<attempt>]` lets a wait re-armed from a failure receipt
|
|
239
|
+
// skip the run it already saw and park for the NEXT train.
|
|
240
|
+
"staging-release"(p) {
|
|
241
|
+
const { repo, number } = parsePrTarget(p, "staging-release");
|
|
242
|
+
if (number == null) {
|
|
243
|
+
throw new Error("staging-release needs pr=<number> (or pr=<owner/repo#number>)");
|
|
244
|
+
}
|
|
245
|
+
const out = { repo, pr: number };
|
|
246
|
+
if (p.after_run !== undefined) {
|
|
247
|
+
const raw = String(p.after_run).trim();
|
|
248
|
+
const m = /^(\d+)(?::(\d+))?$/.exec(raw);
|
|
249
|
+
if (!m) throw new Error("staging-release after_run must be <run_id>[:<attempt>]");
|
|
250
|
+
out.afterRunId = m[1];
|
|
251
|
+
if (m[2] !== undefined) out.afterRunAttempt = Number(m[2]);
|
|
252
|
+
}
|
|
253
|
+
const allowed = new Set(["repo", "pr", "number", "after_run"]);
|
|
254
|
+
const extras = Object.keys(p).filter((key) => !allowed.has(key));
|
|
255
|
+
if (extras.length > 0) {
|
|
256
|
+
throw new Error(`staging-release accepts only repo=<owner/repo>, pr=<number>, after_run=<run_id>[:<attempt>] (got: ${extras.join(", ")})`);
|
|
257
|
+
}
|
|
258
|
+
return out;
|
|
259
|
+
},
|
|
231
260
|
// BOT-1247 — unblocked: wake when a ticket's LAST open blocker clears (its
|
|
232
261
|
// open-blocker count transitions >0 → 0). Level-triggered: a ticket already
|
|
233
262
|
// unblocked (or never blocked) at registration is granted immediately with
|
|
@@ -666,6 +695,27 @@ function conditionMatchesSignal(condition, signal, waitSessionId) {
|
|
|
666
695
|
payload.gate_open === false &&
|
|
667
696
|
payload.operational_status === "healthy";
|
|
668
697
|
}
|
|
698
|
+
case "staging-release": {
|
|
699
|
+
// BOT-1458: a PR-scoped staging release train reached a terminal conclusion
|
|
700
|
+
// and its head commit contains this PR's merge (`includes_pr:true`, decided
|
|
701
|
+
// server-side by ancestry). The signal is tenant-scoped, so the central
|
|
702
|
+
// guard already fenced foreign workspaces. Subject is `<owner/repo>#<pr>`.
|
|
703
|
+
if (signal.signal_type !== "staging_release") return false;
|
|
704
|
+
const payload = signal.payload ?? {};
|
|
705
|
+
const subject = typeof signal.subject_key === "string" ? signal.subject_key.toLowerCase() : "";
|
|
706
|
+
if (subject !== `${params.repo}#${params.pr}`.toLowerCase()) return false;
|
|
707
|
+
if (payload.includes_pr !== true) return false;
|
|
708
|
+
// AC-9: a wait re-armed with after_run ignores the run it already saw — that
|
|
709
|
+
// run id, and (when an attempt was named) that attempt or lower — so a
|
|
710
|
+
// re-registration after a failure receipt parks for the NEXT train instead
|
|
711
|
+
// of instantly replaying the same failure via `--since`.
|
|
712
|
+
if (params.afterRunId != null && String(payload.workflow_run_id) === String(params.afterRunId)) {
|
|
713
|
+
if (params.afterRunAttempt == null) return false;
|
|
714
|
+
const attempt = Number(payload.workflow_run_attempt);
|
|
715
|
+
if (Number.isFinite(attempt) && attempt <= params.afterRunAttempt) return false;
|
|
716
|
+
}
|
|
717
|
+
return true;
|
|
718
|
+
}
|
|
669
719
|
case "unblocked": {
|
|
670
720
|
// Wakes on the ticket_unblocked spine signal for this ticket (the server emits both
|
|
671
721
|
// the genuine >0→0 transition and the level-triggered `already_unblocked` echo).
|
package/src/wait.mjs
CHANGED
|
@@ -17,12 +17,13 @@ import { EXIT, parseConditions, parseSseFrames, runWaitLoop, normalizeSince, tru
|
|
|
17
17
|
import { resolveAgentProfile, withPrincipalReceipt } from "./wait-profile.mjs";
|
|
18
18
|
import { VERSION } from "./version.mjs";
|
|
19
19
|
import { fileURLToPath } from "node:url";
|
|
20
|
+
import { latestPublicCliCommand } from "./public-invocation.mjs";
|
|
20
21
|
|
|
21
22
|
// A protocol is deliberately distinct from package semver: compatible pinned
|
|
22
23
|
// clients keep working until the server raises this minimum, while a stale
|
|
23
24
|
// implementation gets a typed, safe upgrade instruction.
|
|
24
25
|
export const WAIT_PROTOCOL_VERSION = 1;
|
|
25
|
-
const CLI_UPGRADE_COMMAND = "
|
|
26
|
+
const CLI_UPGRADE_COMMAND = latestPublicCliCommand("wait");
|
|
26
27
|
const MIN_RECEIPT_MAX_BYTES = 512;
|
|
27
28
|
|
|
28
29
|
const HELP = `botbuddy wait — one wait command instead of a polling loop (BOT-989)
|
|
@@ -458,7 +459,7 @@ function emit(receipt, { versioned = false, maxBytes = null } = {}) {
|
|
|
458
459
|
}
|
|
459
460
|
|
|
460
461
|
function profileRecovery(profile) {
|
|
461
|
-
return `
|
|
462
|
+
return latestPublicCliCommand(`profile setup ${profile.name}`);
|
|
462
463
|
}
|
|
463
464
|
|
|
464
465
|
function typedProfileError(errorCode) {
|