@botbuddy/cli 1.2.3 → 1.4.1
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/bin/botbuddy.mjs +5 -1
- package/package.json +1 -1
- package/src/agent-credential-store.mjs +208 -0
- package/src/api.mjs +39 -0
- package/src/auth.mjs +169 -70
- package/src/auth.test.mjs +404 -0
- package/src/codex-bridge.mjs +2 -1
- package/src/commands.mjs +206 -30
- package/src/config.mjs +5 -1
- package/src/discovery.mjs +141 -0
- package/src/discovery.test.mjs +195 -0
- package/src/locks.mjs +154 -0
- package/src/locks.test.mjs +60 -0
- package/src/oauth-loopback.mjs +228 -0
- package/src/profile-bootstrap.mjs +104 -0
- package/src/profile-bootstrap.test.mjs +205 -0
- package/src/publish-equal.mjs +207 -0
- package/src/publish-equal.test.mjs +176 -0
- package/src/publish-workflow.test.mjs +122 -0
- package/src/quiet-runner.mjs +134 -0
- package/src/quiet-runner.test.mjs +109 -0
- package/src/run.mjs +239 -0
- package/src/run.test.mjs +173 -0
- package/src/stack.mjs +572 -0
- package/src/stack.test.mjs +196 -0
- package/src/wait-core.mjs +1266 -0
- package/src/wait-profile.mjs +84 -0
- package/src/wait-profile.test.mjs +30 -0
- package/src/wait.mjs +727 -0
- package/src/wait.test.mjs +266 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join, parse } from "node:path";
|
|
3
|
+
import { readProfileCredential } from "./agent-credential-store.mjs";
|
|
4
|
+
|
|
5
|
+
// Public CLI profiles remain tenant-bound machine-principal contracts.
|
|
6
|
+
export const PROFILE_FILE = ".botbuddy-agent.json";
|
|
7
|
+
|
|
8
|
+
const PROFILES = Object.freeze({
|
|
9
|
+
"botbuddy-dev": Object.freeze({
|
|
10
|
+
tenant: "botbuddy",
|
|
11
|
+
tokenEnv: "BOTBUDDY_BB_AGENT_KEY",
|
|
12
|
+
}),
|
|
13
|
+
"supplyguard-dev": Object.freeze({
|
|
14
|
+
tenant: "supply-guard",
|
|
15
|
+
tokenEnv: "BOTBUDDY_SG_AGENT_KEY",
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export function getAgentProfile(name) {
|
|
20
|
+
return PROFILES[name] ?? null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function findProfileName(cwd) {
|
|
24
|
+
let dir = cwd;
|
|
25
|
+
const root = parse(dir).root;
|
|
26
|
+
while (true) {
|
|
27
|
+
try {
|
|
28
|
+
const raw = await readFile(join(dir, PROFILE_FILE), "utf8");
|
|
29
|
+
const config = JSON.parse(raw);
|
|
30
|
+
if (config?.schema_version !== 1 || typeof config?.profile !== "string") {
|
|
31
|
+
throw new Error(`${PROFILE_FILE} must contain schema_version 1 and a profile`);
|
|
32
|
+
}
|
|
33
|
+
return config.profile;
|
|
34
|
+
} catch (err) {
|
|
35
|
+
if (err?.code !== "ENOENT") throw err;
|
|
36
|
+
}
|
|
37
|
+
if (dir === root) return null;
|
|
38
|
+
dir = dirname(dir);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function resolveAgentProfile({
|
|
43
|
+
cwd = process.cwd(),
|
|
44
|
+
env = process.env,
|
|
45
|
+
explicitProfile = null,
|
|
46
|
+
explicitToken = null,
|
|
47
|
+
readCredential = readProfileCredential,
|
|
48
|
+
} = {}) {
|
|
49
|
+
const name = explicitProfile || await findProfileName(cwd);
|
|
50
|
+
if (!name) {
|
|
51
|
+
const err = new Error(
|
|
52
|
+
`no BotBuddy agent profile found (add ${PROFILE_FILE} or pass --profile)`,
|
|
53
|
+
);
|
|
54
|
+
err.code = "profile_required";
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
const profile = getAgentProfile(name);
|
|
58
|
+
if (!profile) {
|
|
59
|
+
const err = new Error(`unknown BotBuddy agent profile '${name}'`);
|
|
60
|
+
err.code = "unknown_profile";
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
name,
|
|
65
|
+
tenant: profile.tenant,
|
|
66
|
+
tokenEnv: profile.tokenEnv,
|
|
67
|
+
// A freshly bootstrapped credential must take effect even if a calling shell
|
|
68
|
+
// still has a stale profile variable. `--token` remains the explicit escape
|
|
69
|
+
// hatch for a one-off credential.
|
|
70
|
+
token: explicitToken || await readCredential(name) || env[profile.tokenEnv] || null,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function withPrincipalReceipt(receipt, profile, registration = {}) {
|
|
75
|
+
if (!profile) return receipt;
|
|
76
|
+
return {
|
|
77
|
+
...receipt,
|
|
78
|
+
principal: {
|
|
79
|
+
profile: profile.name,
|
|
80
|
+
tenant_id: registration.sessionTenant ?? profile.tenant,
|
|
81
|
+
agent_id: registration.agentId ?? null,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import { resolveAgentProfile } from "./wait-profile.mjs";
|
|
5
|
+
|
|
6
|
+
test("BOT-1344: a profiled public wait reads its tenant-bound Keychain credential without a shell export", async () => {
|
|
7
|
+
const profile = await resolveAgentProfile({
|
|
8
|
+
explicitProfile: "botbuddy-dev",
|
|
9
|
+
env: {},
|
|
10
|
+
readCredential: async (name) => {
|
|
11
|
+
assert.equal(name, "botbuddy-dev");
|
|
12
|
+
return "fixture-keychain-agent-credential";
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
assert.equal(profile.token, "fixture-keychain-agent-credential");
|
|
17
|
+
assert.equal(profile.tenant, "botbuddy");
|
|
18
|
+
assert(!JSON.stringify(profile).includes("BOTBUDDY_AGENT_KEY"));
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("BOT-1344: a one-off wait token overrides the Keychain profile credential", async () => {
|
|
22
|
+
const profile = await resolveAgentProfile({
|
|
23
|
+
explicitProfile: "botbuddy-dev",
|
|
24
|
+
explicitToken: "fixture-diagnostic-token",
|
|
25
|
+
env: { BOTBUDDY_BB_AGENT_KEY: "fixture-ci-token" },
|
|
26
|
+
readCredential: async () => "fixture-keychain-agent-credential",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
assert.equal(profile.token, "fixture-diagnostic-token");
|
|
30
|
+
});
|