@b3os/cli 0.0.0-stage → 0.0.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.
@@ -0,0 +1,146 @@
1
+ // b3os bench: a quick, client-side latency probe of SDK calls from where you run it, with the server's own phases
2
+ // from Server-Timing next to the client wall time. Sequential calls (optionally spaced to measure idle gaps); it is
3
+ // a probe, not the published harness (bench/harness, ARCHITECTURE §6 measurement rules).
4
+ import { hostname } from "node:os";
5
+ import { SDK_VERSION } from "@b3os/sdk";
6
+ import { EXIT, intFlag, parse, UsageError } from "../ctx.js";
7
+ export function percentile(sorted, p) {
8
+ if (!sorted.length)
9
+ return null;
10
+ const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil((p / 100) * sorted.length) - 1));
11
+ return sorted[idx] ?? null;
12
+ }
13
+ export function summarize(values) {
14
+ const s = [...values].sort((x, y) => x - y);
15
+ const mean = s.length ? s.reduce((a, b) => a + b, 0) / s.length : null;
16
+ return {
17
+ n: s.length,
18
+ min: s[0] ?? null,
19
+ p50: percentile(s, 50),
20
+ p95: percentile(s, 95),
21
+ p99: percentile(s, 99),
22
+ max: s[s.length - 1] ?? null,
23
+ mean,
24
+ };
25
+ }
26
+ const r3 = (v) => (v === null ? null : Math.round(v * 1000) / 1000);
27
+ export async function bench(ctx, argv) {
28
+ const a = parse(argv, {
29
+ n: { type: "string", short: "n" },
30
+ warmup: { type: "string" },
31
+ "interval-ms": { type: "string" },
32
+ url: { type: "string" },
33
+ sandbox: { type: "string" },
34
+ });
35
+ ctx.applyGlobals(a.values);
36
+ const target = a.positionals[0] ?? "ping";
37
+ if (a.values.help || !["ping", "scrape", "exec"].includes(target)) {
38
+ ctx.out("usage: b3os bench [ping|scrape|exec] [-n 30] [--warmup 3] [--interval-ms MS] [--url URL] [--sandbox sbx_…] [--json]\n" +
39
+ " ping sandboxes.listTemplates (node-served, read-only, no billing)\n" +
40
+ " scrape vision.scrape of --url (default https://example.com; bills vision.page)\n" +
41
+ " exec sandboxes.exec of /bin/true in --sandbox (or a new sandbox, deleted afterwards)\n" +
42
+ "Client-side, closed-loop, sequential; not a published benchmark (ARCHITECTURE §6).");
43
+ return a.values.help ? EXIT.OK : EXIT.USAGE;
44
+ }
45
+ const n = intFlag(a.values.n, "-n") ?? 30;
46
+ const warmup = intFlag(a.values.warmup, "--warmup") ?? 3;
47
+ const gap = intFlag(a.values["interval-ms"], "--interval-ms") ?? 0;
48
+ if (n < 1)
49
+ throw new UsageError("-n must be at least 1");
50
+ const b3 = ctx.client();
51
+ let sbx = typeof a.values.sandbox === "string" ? a.values.sandbox : undefined;
52
+ let created = false;
53
+ if (target === "exec" && !sbx) {
54
+ sbx = (await b3.sandboxes.createSandbox({ template: "base", timeout_s: 600 })).id;
55
+ created = true;
56
+ ctx.info(`created ${sbx}`);
57
+ }
58
+ const url = typeof a.values.url === "string" ? a.values.url : "https://example.com";
59
+ const call = () => {
60
+ switch (target) {
61
+ case "scrape":
62
+ return b3.vision
63
+ .scrape({ url })
64
+ .withResponse()
65
+ .then((r) => r.response);
66
+ case "exec":
67
+ return b3.sandboxes
68
+ .exec(sbx, { cmd: ["/bin/true"] })
69
+ .withResponse()
70
+ .then((r) => r.response);
71
+ default:
72
+ return b3.sandboxes
73
+ .listTemplates()
74
+ .withResponse()
75
+ .then((r) => r.response);
76
+ }
77
+ };
78
+ const samples = [];
79
+ try {
80
+ for (let i = -warmup; i < n; i++) {
81
+ if (gap > 0 && i > -warmup)
82
+ await new Promise((r) => setTimeout(r, gap));
83
+ const t0 = performance.now();
84
+ const info = await call();
85
+ const ms = performance.now() - t0;
86
+ if (i < 0)
87
+ continue;
88
+ samples.push({
89
+ i,
90
+ client_ms: ms,
91
+ server_total_ms: info.timing.total_ms,
92
+ edge_ms: info.timing.edge_ms,
93
+ status: info.status,
94
+ transport: info.transport,
95
+ phases: info.timing.phases,
96
+ });
97
+ }
98
+ }
99
+ finally {
100
+ if (created && sbx)
101
+ await b3.sandboxes.deleteSandbox(sbx).catch(() => undefined);
102
+ }
103
+ const client = summarize(samples.map((s) => s.client_ms));
104
+ const serverVals = samples.map((s) => s.server_total_ms).filter((v) => v !== null);
105
+ const server = summarize(serverVals);
106
+ const overhead = summarize(samples.filter((s) => s.server_total_ms !== null).map((s) => s.client_ms - s.server_total_ms));
107
+ const phaseNames = [...new Set(samples.flatMap((s) => Object.keys(s.phases)))];
108
+ const phases = Object.fromEntries(phaseNames.map((p) => [p, r3(summarize(samples.map((s) => s.phases[p] ?? 0)).p50)]));
109
+ const env = {
110
+ label: "dev",
111
+ side: "client",
112
+ target,
113
+ sdk: `b3os-sdk-ts/${SDK_VERSION}`,
114
+ runtime: `node/${process.versions.node}`,
115
+ host: hostname(),
116
+ route: b3.routes,
117
+ transport: samples[0]?.transport ?? null,
118
+ n,
119
+ warmup,
120
+ interval_ms: gap,
121
+ date: new Date().toISOString(),
122
+ };
123
+ const report = {
124
+ env,
125
+ client_ms: Object.fromEntries(Object.entries(client).map(([k, v]) => [k, k === "n" ? v : r3(v)])),
126
+ server_total_ms: Object.fromEntries(Object.entries(server).map(([k, v]) => [k, k === "n" ? v : r3(v)])),
127
+ client_minus_server_ms: Object.fromEntries(Object.entries(overhead).map(([k, v]) => [k, k === "n" ? v : r3(v)])),
128
+ server_phase_p50_ms: phases,
129
+ samples: ctx.json ? samples : undefined,
130
+ };
131
+ ctx.result(report, () => {
132
+ ctx.out(`${target} x${n} via ${env.transport ?? "?"} (${b3.routes.node}), client-side, ${env.date}`);
133
+ ctx.out(`client p50 ${report.client_ms.p50} ms p95 ${report.client_ms.p95} ms p99 ${report.client_ms.p99} ms max ${report.client_ms.max} ms`);
134
+ if (server.n) {
135
+ ctx.out(`server p50 ${report.server_total_ms.p50} ms p95 ${report.server_total_ms.p95} ms (Server-Timing total)`);
136
+ ctx.out(`client - server p50 ${report.client_minus_server_ms.p50} ms (network, TLS, edge and client time)`);
137
+ }
138
+ if (phaseNames.length)
139
+ ctx.out(`phases p50: ${Object.entries(phases)
140
+ .map(([k, v]) => `${k} ${v}`)
141
+ .join(", ")}`);
142
+ ctx.info("A dev probe: label numbers 'client-side, dev' if you quote them.");
143
+ });
144
+ return EXIT.OK;
145
+ }
146
+ //# sourceMappingURL=bench.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bench.js","sourceRoot":"","sources":["../../src/commands/bench.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { type Ctx } from "../ctx.ts";
2
+ export declare function keys(ctx: Ctx, argv: string[]): Promise<number>;
@@ -0,0 +1,108 @@
1
+ // b3os keys list | create | get | update | revoke (platform.yaml; served by beta-api.b3os.org).
2
+ import { EXIT, parse, UsageError } from "../ctx.js";
3
+ const USAGE = `usage: b3os keys <command>
4
+ list [--status active|revoked|expired] [--limit N]
5
+ create --name NAME --scope SCOPE[,SCOPE...] [--spend-cap USD] [--expires RFC3339] [--project prj_…]
6
+ get <key_id>
7
+ update <key_id> [--name NAME] [--spend-cap USD|none] [--expires RFC3339|none] (scopes are fixed at creation)
8
+ revoke <key_id> [--yes]`;
9
+ function scopesOf(v) {
10
+ if (v === undefined)
11
+ return undefined;
12
+ const list = (Array.isArray(v) ? v : [v]).flatMap((s) => String(s).split(/[,\s]+/)).filter(Boolean);
13
+ if (!list.length)
14
+ throw new UsageError("--scope needs at least one scope");
15
+ return list;
16
+ }
17
+ function capOf(v) {
18
+ if (v === undefined)
19
+ return undefined;
20
+ if (v === "none")
21
+ return null;
22
+ const n = Number(v);
23
+ if (!Number.isFinite(n) || n < 0)
24
+ throw new UsageError("--spend-cap takes dollars or none");
25
+ return n;
26
+ }
27
+ export async function keys(ctx, argv) {
28
+ const [sub, ...rest] = argv;
29
+ const a = parse(rest, {
30
+ name: { type: "string" },
31
+ scope: { type: "string", multiple: true },
32
+ "spend-cap": { type: "string" },
33
+ expires: { type: "string" },
34
+ project: { type: "string" },
35
+ status: { type: "string" },
36
+ limit: { type: "string" },
37
+ });
38
+ ctx.applyGlobals(a.values);
39
+ if (!sub || a.values.help) {
40
+ ctx.out(USAGE);
41
+ return sub ? EXIT.OK : EXIT.USAGE;
42
+ }
43
+ const b3 = ctx.client();
44
+ const id = a.positionals[0];
45
+ switch (sub) {
46
+ case "list":
47
+ case "ls": {
48
+ const page = await b3.keys.listKeys({
49
+ status: a.values.status,
50
+ limit: a.values.limit ? Number(a.values.limit) : undefined,
51
+ });
52
+ ctx.result(page, () => ctx.table(page.data.map((k) => ({ ...k, scopes: k.scopes.join(",") })), ["id", "name", "status", "scopes", "created_at"]));
53
+ return EXIT.OK;
54
+ }
55
+ case "create": {
56
+ if (typeof a.values.name !== "string")
57
+ throw new UsageError("keys create needs --name");
58
+ const scopes = scopesOf(a.values.scope);
59
+ if (!scopes)
60
+ throw new UsageError("keys create needs --scope (for example --scope vision:run,sandboxes:*)");
61
+ const key = await b3.keys.createKey({
62
+ name: a.values.name,
63
+ scopes: scopes,
64
+ spend_cap_usd: capOf(a.values["spend-cap"]),
65
+ expires_at: typeof a.values.expires === "string" ? a.values.expires : undefined,
66
+ project_id: typeof a.values.project === "string" ? a.values.project : undefined,
67
+ });
68
+ ctx.result(key, () => {
69
+ ctx.out(key.secret);
70
+ // stderr is what CI and agents log: name the variable, never repeat the secret there.
71
+ ctx.info(`Created ${key.id} (${key.name}). The secret above is shown once; store it, e.g. as B3OS_API_KEY.`);
72
+ });
73
+ return EXIT.OK;
74
+ }
75
+ case "get": {
76
+ if (!id)
77
+ throw new UsageError("keys get needs a key id");
78
+ const key = await b3.keys.getKey(id);
79
+ ctx.result(key, () => ctx.printJson(key));
80
+ return EXIT.OK;
81
+ }
82
+ case "update": {
83
+ if (!id)
84
+ throw new UsageError("keys update needs a key id");
85
+ const exp = a.values.expires === "none" ? null : a.values.expires;
86
+ const key = await b3.keys.updateKey(id, {
87
+ name: a.values.name,
88
+ spend_cap_usd: capOf(a.values["spend-cap"]),
89
+ expires_at: exp,
90
+ });
91
+ ctx.result(key, () => ctx.out(`Updated ${key.id}`));
92
+ return EXIT.OK;
93
+ }
94
+ case "revoke":
95
+ case "rm": {
96
+ if (!id)
97
+ throw new UsageError("keys revoke needs a key id");
98
+ if (!(await ctx.confirm(`Revoke ${id}? Requests with it fail from now on.`)))
99
+ return EXIT.OK;
100
+ const key = await b3.keys.revokeKey(id);
101
+ ctx.result(key, () => ctx.out(`Revoked ${id}`));
102
+ return EXIT.OK;
103
+ }
104
+ default:
105
+ throw new UsageError(`unknown keys command "${sub}"\n${USAGE}`);
106
+ }
107
+ }
108
+ //# sourceMappingURL=keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/commands/keys.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type Ctx } from "../ctx.ts";
2
+ /** Adds our entry to a client's JSON config. A new user-scope file is created 0600 (it may later hold others' secrets). */
3
+ export declare function mergeJsonServers(file: string, entry: Record<string, unknown>, newFileMode?: number): void;
4
+ /** Replaces (or appends) the `[mcp_servers.b3os]` table of a Codex config.toml, leaving the rest as it was. */
5
+ export declare function upsertCodexToml(text: string, table: string): string;
6
+ export declare function mcp(ctx: Ctx, argv: string[]): Promise<number>;
@@ -0,0 +1,194 @@
1
+ // b3os mcp install --client claude|cursor|codex | mcp serve. The installed entry runs the stdio server
2
+ // (`npx -y @b3os/mcp`), which reads B3OS_API_KEY or the `b3os login` token itself, so no client config ever holds
3
+ // a secret. `--remote` instead points the client at the edge's MCP endpoint with a bearer header that refers to
4
+ // B3OS_API_KEY in the client's environment (each client expands its own syntax), so the key is never written to
5
+ // a config file, printed, or passed on a command line.
6
+ import { execFileSync } from "node:child_process";
7
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
8
+ import { homedir } from "node:os";
9
+ import { dirname, join, resolve } from "node:path";
10
+ import { EXIT, parse, UsageError } from "../ctx.js";
11
+ const CLIENTS = ["claude", "cursor", "codex"];
12
+ const SERVER_NAME = "b3os";
13
+ const STDIO = { command: "npx", args: ["-y", "@b3os/mcp"] };
14
+ /** How each client names an environment variable in an MCP server entry; it expands it when it connects. */
15
+ const KEY_REF = {
16
+ claude: "${B3OS_API_KEY}",
17
+ cursor: "${env:B3OS_API_KEY}",
18
+ };
19
+ function remoteUrl(base) {
20
+ return `${base.replace(/\/+$/, "")}/mcp`;
21
+ }
22
+ function onPath(cmd) {
23
+ try {
24
+ execFileSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
25
+ return true;
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ }
31
+ /** Adds our entry to a client's JSON config. A new user-scope file is created 0600 (it may later hold others' secrets). */
32
+ export function mergeJsonServers(file, entry, newFileMode = 0o644) {
33
+ let doc = {};
34
+ if (existsSync(file)) {
35
+ try {
36
+ doc = JSON.parse(readFileSync(file, "utf8"));
37
+ }
38
+ catch (e) {
39
+ throw new UsageError(`${file} is not valid JSON (${e.message}); fix it or use --print`);
40
+ }
41
+ }
42
+ doc.mcpServers = { ...(doc.mcpServers ?? {}), [SERVER_NAME]: entry };
43
+ mkdirSync(dirname(file), { recursive: true });
44
+ // `mode` applies only when the file is created; an existing file keeps its own.
45
+ writeFileSync(file, `${JSON.stringify(doc, null, 2)}\n`, { mode: newFileMode });
46
+ }
47
+ /** Replaces (or appends) the `[mcp_servers.b3os]` table of a Codex config.toml, leaving the rest as it was. */
48
+ export function upsertCodexToml(text, table) {
49
+ const lines = text.split("\n");
50
+ const header = `[mcp_servers.${SERVER_NAME}]`;
51
+ const start = lines.findIndex((l) => l.trim() === header);
52
+ if (start < 0)
53
+ return `${text.replace(/\n*$/, "")}${text.trim() ? "\n\n" : ""}${table}\n`;
54
+ let end = start + 1;
55
+ while (end < lines.length && !/^\s*\[/.test(lines[end] ?? ""))
56
+ end++;
57
+ // Keep sub-tables of our own entry out (`[mcp_servers.b3os.env]`) by removing them too.
58
+ while (end < lines.length && (lines[end] ?? "").trim().startsWith(`[mcp_servers.${SERVER_NAME}.`)) {
59
+ end++;
60
+ while (end < lines.length && !/^\s*\[/.test(lines[end] ?? ""))
61
+ end++;
62
+ }
63
+ const out = [...lines.slice(0, start), ...table.split("\n"), ...lines.slice(end)];
64
+ return out.join("\n");
65
+ }
66
+ function tomlString(s) {
67
+ return JSON.stringify(s);
68
+ }
69
+ export async function mcp(ctx, argv) {
70
+ const [sub, ...rest] = argv;
71
+ const a = parse(rest, {
72
+ client: { type: "string" },
73
+ scope: { type: "string" },
74
+ print: { type: "boolean" },
75
+ remote: { type: "boolean" },
76
+ packs: { type: "string" },
77
+ });
78
+ ctx.applyGlobals(a.values);
79
+ const usage = `usage: b3os mcp install --client claude|cursor|codex [--scope user|project] [--packs a,b] [--remote] [--print]
80
+ b3os mcp serve [--packs github,slack]`;
81
+ if (!sub || a.values.help) {
82
+ ctx.out(usage);
83
+ return sub ? EXIT.OK : EXIT.USAGE;
84
+ }
85
+ const packs = typeof a.values.packs === "string" ? a.values.packs : undefined;
86
+ if (sub === "serve") {
87
+ const { serveStdio } = await import("@b3os/mcp");
88
+ await serveStdio({ client: ctx.client(), packs: packs ? packs.split(",").filter(Boolean) : undefined });
89
+ return EXIT.OK;
90
+ }
91
+ if (sub !== "install")
92
+ throw new UsageError(`unknown mcp command "${sub}"\n${usage}`);
93
+ const client = a.values.client;
94
+ if (!client || !CLIENTS.includes(client))
95
+ throw new UsageError(`mcp install needs --client ${CLIENTS.join("|")}`);
96
+ const scope = a.values.scope ?? "user";
97
+ if (scope !== "user" && scope !== "project")
98
+ throw new UsageError("--scope is user or project");
99
+ const cred = ctx.credential();
100
+ const args = packs ? [...STDIO.args, "--packs", packs] : STDIO.args;
101
+ let entry = { command: STDIO.command, args };
102
+ const remote = !!a.values.remote;
103
+ if (remote) {
104
+ if (client === "codex")
105
+ throw new UsageError("Codex runs stdio servers here; drop --remote");
106
+ const url = remoteUrl(cred.baseUrl) + (packs ? `?packs=${encodeURIComponent(packs)}` : "");
107
+ entry = { type: "http", url, headers: { Authorization: `Bearer ${KEY_REF[client]}` } };
108
+ }
109
+ if (a.values.print) {
110
+ if (client === "codex")
111
+ ctx.out(codexTable(entry));
112
+ else
113
+ ctx.printJson({ mcpServers: { [SERVER_NAME]: entry } });
114
+ return EXIT.OK;
115
+ }
116
+ let where;
117
+ switch (client) {
118
+ case "claude": {
119
+ if (onPath("claude")) {
120
+ const cmdArgs = remote
121
+ ? [
122
+ "mcp",
123
+ "add",
124
+ "--scope",
125
+ scope,
126
+ "--transport",
127
+ "http",
128
+ SERVER_NAME,
129
+ String(entry.url),
130
+ "--header",
131
+ `Authorization: Bearer ${KEY_REF.claude}`,
132
+ ]
133
+ : ["mcp", "add", "--scope", scope, SERVER_NAME, "--", STDIO.command, ...args];
134
+ try {
135
+ execFileSync("claude", ["mcp", "remove", "--scope", scope, SERVER_NAME], { stdio: "ignore" });
136
+ }
137
+ catch {
138
+ // Not installed yet.
139
+ }
140
+ execFileSync("claude", cmdArgs, { stdio: ctx.json ? "ignore" : ["ignore", "ignore", "inherit"] });
141
+ where = `claude mcp (${scope} scope)`;
142
+ }
143
+ else {
144
+ if (scope === "user") {
145
+ throw new UsageError("the claude CLI is not on PATH; use --scope project (writes .mcp.json here) or --print");
146
+ }
147
+ where = resolve(".mcp.json");
148
+ mergeJsonServers(where, entry);
149
+ }
150
+ break;
151
+ }
152
+ case "cursor": {
153
+ where = scope === "user" ? join(homedir(), ".cursor", "mcp.json") : resolve(".cursor", "mcp.json");
154
+ mergeJsonServers(where, entry, scope === "user" ? 0o600 : 0o644);
155
+ break;
156
+ }
157
+ case "codex": {
158
+ where =
159
+ scope === "user"
160
+ ? join(process.env.CODEX_HOME ?? join(homedir(), ".codex"), "config.toml")
161
+ : resolve(".codex", "config.toml");
162
+ const text = existsSync(where) ? readFileSync(where, "utf8") : "";
163
+ mkdirSync(dirname(where), { recursive: true });
164
+ writeFileSync(where, upsertCodexToml(text, codexTable(entry)));
165
+ break;
166
+ }
167
+ }
168
+ const summary = {
169
+ installed: true,
170
+ client,
171
+ scope,
172
+ where,
173
+ server: entry.url ? { url: entry.url } : { command: STDIO.command, args },
174
+ };
175
+ ctx.result(summary, () => {
176
+ ctx.out(`Added the ${SERVER_NAME} MCP server to ${client}: ${where}`);
177
+ if (remote) {
178
+ ctx.info(`The entry refers to B3OS_API_KEY and holds no key: start ${client} with B3OS_API_KEY set in its environment.`);
179
+ }
180
+ else if (!cred.key) {
181
+ ctx.info("The server needs credentials: run b3os login, or set B3OS_API_KEY for the client.");
182
+ }
183
+ });
184
+ return EXIT.OK;
185
+ }
186
+ function codexTable(entry) {
187
+ const args = entry.args ?? [];
188
+ return [
189
+ `[mcp_servers.${SERVER_NAME}]`,
190
+ `command = ${tomlString(String(entry.command ?? STDIO.command))}`,
191
+ `args = [${args.map(tomlString).join(", ")}]`,
192
+ ].join("\n");
193
+ }
194
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { type Ctx } from "../ctx.ts";
2
+ /** The routine's name from its definition (JSON `name`, or a top-level `name:` line of YAML). */
3
+ export declare function routineName(text: string, file: string): string;
4
+ export declare function routine(ctx: Ctx, argv: string[]): Promise<number>;
5
+ export declare function action(ctx: Ctx, argv: string[]): Promise<number>;