@computesdk/cli 2.0.0 → 2.1.0
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/README.md +10 -0
- package/dist/index.js +103 -19
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -29,11 +29,21 @@ compute market … # sell side of the compute market
|
|
|
29
29
|
compute sandboxes … # platform sandbox control plane
|
|
30
30
|
compute bench … # benchmarks CLI (run, auth, org, results, …)
|
|
31
31
|
compute providers # sandbox provider credential status
|
|
32
|
+
compute org list # your organizations (* = active)
|
|
33
|
+
compute org use <slug> # switch the stored login's active org
|
|
34
|
+
compute org current / whoami # current user + active org
|
|
32
35
|
```
|
|
33
36
|
|
|
34
37
|
All platform commands accept `--api-key` / `COMPUTE_API_KEY` and fall back to
|
|
35
38
|
the stored OAuth credentials.
|
|
36
39
|
|
|
40
|
+
The active org comes from the stored login (`compute org use` changes it —
|
|
41
|
+
it's per user, so every CLI on every machine sees the switch). For a one-off
|
|
42
|
+
override, pass `--org <slug>` (on the root command or any `sandboxes` /
|
|
43
|
+
`actions` / `market` subcommand) or set `COMPUTE_ORG`; it sends `X-Org-Slug`
|
|
44
|
+
on the request. Precedence: `--org` > `COMPUTE_ORG` > the stored login's org.
|
|
45
|
+
The platform ignores the header for org API keys.
|
|
46
|
+
|
|
37
47
|
## Migrating from @computesdk/cli 1.x
|
|
38
48
|
|
|
39
49
|
v2 removes the legacy console authentication and its commands:
|
package/dist/index.js
CHANGED
|
@@ -54,12 +54,6 @@ function isProviderAuthComplete(provider) {
|
|
|
54
54
|
}
|
|
55
55
|
function getProviderStatus() {
|
|
56
56
|
const statuses = [];
|
|
57
|
-
const hasGatewayKey = !!process.env.COMPUTESDK_API_KEY;
|
|
58
|
-
statuses.push({
|
|
59
|
-
name: "computesdk",
|
|
60
|
-
ready: hasGatewayKey,
|
|
61
|
-
missing: hasGatewayKey ? [] : ["COMPUTESDK_API_KEY"]
|
|
62
|
-
});
|
|
63
57
|
for (const provider of PROVIDER_NAMES) {
|
|
64
58
|
statuses.push({
|
|
65
59
|
name: provider,
|
|
@@ -262,7 +256,12 @@ var ActionsClient = class {
|
|
|
262
256
|
return new Uint8Array(await res.arrayBuffer());
|
|
263
257
|
}
|
|
264
258
|
headers() {
|
|
265
|
-
|
|
259
|
+
const headers = {
|
|
260
|
+
authorization: `Bearer ${this.auth.apiKey}`,
|
|
261
|
+
accept: "application/json"
|
|
262
|
+
};
|
|
263
|
+
if (this.auth.orgSlug) headers["X-Org-Slug"] = this.auth.orgSlug;
|
|
264
|
+
return headers;
|
|
266
265
|
}
|
|
267
266
|
async parse(res) {
|
|
268
267
|
const body = await res.json().catch(() => void 0);
|
|
@@ -336,6 +335,7 @@ var NO_CREDENTIALS_HINT = "Set COMPUTE_API_KEY, pass --api-key, or run `compute
|
|
|
336
335
|
async function resolveActionsAuth(opts, resolveStored = resolveStoredPlatformAuth) {
|
|
337
336
|
const baseUrl = (opts.baseUrl || process.env.COMPUTE_PLATFORM_URL || process.env.BENCHMARKS_PLATFORM_URL || // legacy name
|
|
338
337
|
DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
338
|
+
let orgSlug = opts.org || process.env.COMPUTE_ORG || void 0;
|
|
339
339
|
if (!opts.allowUntrustedHost && !isTrustedActionsHost(baseUrl)) {
|
|
340
340
|
throw new ActionsCliError(
|
|
341
341
|
"untrusted_host",
|
|
@@ -370,12 +370,12 @@ async function resolveActionsAuth(opts, resolveStored = resolveStoredPlatformAut
|
|
|
370
370
|
if (!apiKey) {
|
|
371
371
|
throw new ActionsCliError("no_credentials", `No API key. ${NO_CREDENTIALS_HINT}`);
|
|
372
372
|
}
|
|
373
|
-
return { apiKey, baseUrl };
|
|
373
|
+
return { apiKey, baseUrl, ...orgSlug && { orgSlug } };
|
|
374
374
|
}
|
|
375
375
|
async function resolveStoredPlatformAuth(opts) {
|
|
376
376
|
const { resolveAuth } = await import("@benchsdk/cli");
|
|
377
377
|
const auth = await resolveAuth({ baseUrl: opts.baseUrl });
|
|
378
|
-
return { apiKey: auth.apiKey, token: auth.token };
|
|
378
|
+
return { apiKey: auth.apiKey, token: auth.token, orgSlug: auth.orgSlug, orgId: auth.orgId };
|
|
379
379
|
}
|
|
380
380
|
function isLoopbackHost(host) {
|
|
381
381
|
return host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "::1";
|
|
@@ -883,7 +883,7 @@ function formatVaultRow(item) {
|
|
|
883
883
|
}
|
|
884
884
|
function registerActionsCommands(program2) {
|
|
885
885
|
const actions = program2.command("actions").alias("ci").description("Drive the benchmarks-platform Actions API").configureOutput({ outputError: usageErrorOutput });
|
|
886
|
-
const common = (cmd) => cmd.option("--api-key <key>", "API key (default: $COMPUTE_API_KEY)").option("--base-url <url>", "API base URL (default: https://platform.computesdk.com)").option("--allow-untrusted-host", "send an explicit --api-key/env key to a non-computesdk, non-localhost --base-url (stored login credentials are never sent)").option("--json", "print machine-readable JSON");
|
|
886
|
+
const common = (cmd) => cmd.option("--api-key <key>", "API key (default: $COMPUTE_API_KEY)").option("--base-url <url>", "API base URL (default: https://platform.computesdk.com)").option("--org <slug>", "organization slug for this command (or $COMPUTE_ORG)").option("--allow-untrusted-host", "send an explicit --api-key/env key to a non-computesdk, non-localhost --base-url (stored login credentials are never sent)").option("--json", "print machine-readable JSON");
|
|
887
887
|
common(
|
|
888
888
|
actions.command("dispatch").description("Dispatch a workflow_dispatch run (or any workflow with --manual)").argument("<repo>", "repository in owner/repo format").requiredOption("--workflow <path|name>", "workflow path or name").option("--ref <ref>", "git ref to run (default: the workflow's first watched ref)").option("--inputs <pairs...>", "workflow inputs as key=value (workflow_dispatch inputs only)").option("--manual", "run a workflow even if it does not declare workflow_dispatch (such runs take no --inputs)").option("--provider <id>", "place the run on one provider (e.g. namespace, vercel:sfo1) instead of the org provider order").option("--provider-region <region>", "region for --provider (same as --provider <id>:<region>)").option("--max-bid <usd>", "max price per vCPU for a market fill (e.g. 0.12); unfilled jobs fall through to the next provider").option("--max-bid-per <unit>", "time unit --max-bid is priced in: second, minute, or hour (default: second)")
|
|
889
889
|
).action(async (repo, opts) => {
|
|
@@ -906,8 +906,8 @@ function registerActionsCommands(program2) {
|
|
|
906
906
|
"/api/v1/actions/dispatch",
|
|
907
907
|
body
|
|
908
908
|
);
|
|
909
|
-
const
|
|
910
|
-
const url = `${c.baseUrl}/${
|
|
909
|
+
const org2 = await c.org();
|
|
910
|
+
const url = `${c.baseUrl}/${org2.slug}/actions/runs/${result.runId}`;
|
|
911
911
|
output(opts, { ...result, url }, (r) => {
|
|
912
912
|
console.log(`${r.created ? "dispatched" : "already running"} ${pc.cyan(r.runId)}`);
|
|
913
913
|
console.log(`sha: ${r.headSha}`);
|
|
@@ -965,8 +965,8 @@ function registerActionsCommands(program2) {
|
|
|
965
965
|
try {
|
|
966
966
|
const c = await client(opts);
|
|
967
967
|
const run = await c.get(`/api/v1/actions/runs/${runId}`);
|
|
968
|
-
const
|
|
969
|
-
const url = `${c.baseUrl}/${
|
|
968
|
+
const org2 = await c.org();
|
|
969
|
+
const url = `${c.baseUrl}/${org2.slug}/actions/runs/${run.id}`;
|
|
970
970
|
const summary = run.conclusion === "failed" ? await fetchRunSummary(c, runId) : null;
|
|
971
971
|
output(opts, { ...run, url, summary }, (r) => {
|
|
972
972
|
console.log(formatRunDetail(r, r.url));
|
|
@@ -1301,8 +1301,8 @@ function registerActionsCommands(program2) {
|
|
|
1301
1301
|
const result = await c.post(
|
|
1302
1302
|
`/api/v1/actions/runs/${runId}/rerun`
|
|
1303
1303
|
);
|
|
1304
|
-
const
|
|
1305
|
-
const url = `${c.baseUrl}/${
|
|
1304
|
+
const org2 = await c.org();
|
|
1305
|
+
const url = `${c.baseUrl}/${org2.slug}/actions/runs/${result.runId}`;
|
|
1306
1306
|
output(opts, { ...result, url }, (r) => {
|
|
1307
1307
|
console.log(`${r.created ? "rerun dispatched" : "rerun already running"} ${pc.cyan(r.runId)}`);
|
|
1308
1308
|
console.log(`url: ${r.url}`);
|
|
@@ -1609,7 +1609,7 @@ function formatSettlementRow(s) {
|
|
|
1609
1609
|
}
|
|
1610
1610
|
function registerMarketCommands(program2) {
|
|
1611
1611
|
const market = program2.command("market").description("Sell compute capacity on the compute market").configureOutput({ outputError: usageErrorOutput });
|
|
1612
|
-
const common = (cmd) => cmd.option("--api-key <key>", "API key (default: $COMPUTE_API_KEY)").option("--base-url <url>", "API base URL (default: https://platform.computesdk.com)").option("--allow-untrusted-host", "send an explicit --api-key/env key to a non-computesdk, non-localhost --base-url (stored login credentials are never sent)").option("--json", "print machine-readable JSON");
|
|
1612
|
+
const common = (cmd) => cmd.option("--api-key <key>", "API key (default: $COMPUTE_API_KEY)").option("--base-url <url>", "API base URL (default: https://platform.computesdk.com)").option("--org <slug>", "organization slug for this command (or $COMPUTE_ORG)").option("--allow-untrusted-host", "send an explicit --api-key/env key to a non-computesdk, non-localhost --base-url (stored login credentials are never sent)").option("--json", "print machine-readable JSON");
|
|
1613
1613
|
common(
|
|
1614
1614
|
market.command("listings").alias("list").description("Your listings \u2014 live, paused, expired, and withdrawn")
|
|
1615
1615
|
).action(async (opts) => {
|
|
@@ -2199,6 +2199,9 @@ function registerSandboxesCommands(program2) {
|
|
|
2199
2199
|
fail(e, opts);
|
|
2200
2200
|
}
|
|
2201
2201
|
});
|
|
2202
|
+
for (const sub of cmd.commands) {
|
|
2203
|
+
sub.option("--org <slug>", "organization slug for this command (or $COMPUTE_ORG)");
|
|
2204
|
+
}
|
|
2202
2205
|
}
|
|
2203
2206
|
async function followProcess(c, sandboxId, jobId) {
|
|
2204
2207
|
let exitCode = 0;
|
|
@@ -2234,17 +2237,41 @@ var isDevBuild = import.meta.url.includes("worktrees") || import.meta.url.includ
|
|
|
2234
2237
|
var VERSION = isDevBuild ? `${packageJson.version}-dev` : packageJson.version;
|
|
2235
2238
|
var program = new Command();
|
|
2236
2239
|
program.name("compute").description("ComputeSDK CLI").version(VERSION).enablePositionalOptions().passThroughOptions();
|
|
2240
|
+
function envBaseUrl() {
|
|
2241
|
+
return process.env.COMPUTE_PLATFORM_URL ?? process.env.BENCHMARKS_PLATFORM_URL;
|
|
2242
|
+
}
|
|
2243
|
+
async function benchAuth(opts = {}) {
|
|
2244
|
+
const { resolveAuth } = await import("@benchsdk/cli");
|
|
2245
|
+
return resolveAuth({ baseUrl: opts.baseUrl ?? envBaseUrl() });
|
|
2246
|
+
}
|
|
2247
|
+
async function printSessionLine(options) {
|
|
2248
|
+
try {
|
|
2249
|
+
const { getMe } = await import("@benchsdk/cli");
|
|
2250
|
+
const me = await getMe(await benchAuth({ baseUrl: options?.baseUrl }));
|
|
2251
|
+
const active = me.organizations.find((o) => o.id === me.activeOrganizationId);
|
|
2252
|
+
const who = me.user.email ?? me.user.name ?? me.user.id;
|
|
2253
|
+
if (active) {
|
|
2254
|
+
p.log.success(
|
|
2255
|
+
`Logged in as ${who} \u2014 active org: ${active.slug}. Change with \`compute org use <slug>\`.`
|
|
2256
|
+
);
|
|
2257
|
+
} else {
|
|
2258
|
+
p.log.success(`Logged in as ${who} \u2014 no active org`);
|
|
2259
|
+
}
|
|
2260
|
+
} catch {
|
|
2261
|
+
p.log.warn("Logged in, but could not fetch session info.");
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2237
2264
|
async function runLogin(options) {
|
|
2238
2265
|
const { oauthLogin } = await import("@benchsdk/cli");
|
|
2239
2266
|
await oauthLogin({
|
|
2240
|
-
baseUrl: options?.baseUrl ??
|
|
2267
|
+
baseUrl: options?.baseUrl ?? envBaseUrl()
|
|
2241
2268
|
});
|
|
2242
2269
|
}
|
|
2243
2270
|
async function runLogout() {
|
|
2244
2271
|
const { clearCredentials } = await import("@benchsdk/cli");
|
|
2245
2272
|
await clearCredentials();
|
|
2246
2273
|
}
|
|
2247
|
-
program.option("--login", "force re-authentication").option("--logout", "clear stored credentials").action(async (opts) => {
|
|
2274
|
+
program.option("--login", "force re-authentication").option("--logout", "clear stored credentials").option("--org <slug>", "organization slug override for this invocation (or $COMPUTE_ORG)").action(async (opts) => {
|
|
2248
2275
|
if (opts.logout) {
|
|
2249
2276
|
console.log();
|
|
2250
2277
|
p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
|
|
@@ -2257,11 +2284,16 @@ program.option("--login", "force re-authentication").option("--logout", "clear s
|
|
|
2257
2284
|
console.log();
|
|
2258
2285
|
p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
|
|
2259
2286
|
await runLogin();
|
|
2287
|
+
await printSessionLine();
|
|
2260
2288
|
p.outro(pc4.green("Authenticated!"));
|
|
2261
2289
|
process.exit(0);
|
|
2262
2290
|
}
|
|
2263
2291
|
program.help();
|
|
2264
2292
|
});
|
|
2293
|
+
program.hook("preAction", (thisCommand) => {
|
|
2294
|
+
const org2 = thisCommand.opts().org;
|
|
2295
|
+
if (org2) process.env.COMPUTE_ORG = org2;
|
|
2296
|
+
});
|
|
2265
2297
|
program.command("providers").description("List all providers and their configuration status").action(async () => {
|
|
2266
2298
|
console.log();
|
|
2267
2299
|
p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
|
|
@@ -2279,6 +2311,7 @@ program.command("login").description("Authenticate with ComputeSDK (OAuth device
|
|
|
2279
2311
|
console.log();
|
|
2280
2312
|
p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
|
|
2281
2313
|
await runLogin({ baseUrl: opts.baseUrl });
|
|
2314
|
+
await printSessionLine({ baseUrl: opts.baseUrl });
|
|
2282
2315
|
p.outro(pc4.green("Authenticated!"));
|
|
2283
2316
|
});
|
|
2284
2317
|
program.command("logout").description("Clear stored credentials").action(async () => {
|
|
@@ -2288,6 +2321,57 @@ program.command("logout").description("Clear stored credentials").action(async (
|
|
|
2288
2321
|
p.log.success("Logged out. Stored credentials removed.");
|
|
2289
2322
|
p.outro(pc4.green("Done!"));
|
|
2290
2323
|
});
|
|
2324
|
+
async function printWhoami() {
|
|
2325
|
+
const { getMe } = await import("@benchsdk/cli");
|
|
2326
|
+
const me = await getMe(await benchAuth());
|
|
2327
|
+
const active = me.organizations.find((o) => o.id === me.activeOrganizationId);
|
|
2328
|
+
console.log(me.user.email ?? me.user.name ?? me.user.id);
|
|
2329
|
+
console.log(
|
|
2330
|
+
`active org: ${active ? `${active.slug} (${active.id})` : "\u2014"}${me.organizations.length > 1 ? " \u2014 change with `compute org use <slug>`" : ""}`
|
|
2331
|
+
);
|
|
2332
|
+
}
|
|
2333
|
+
var org = program.command("org").description("Manage the active organization");
|
|
2334
|
+
org.command("list").description("List your organizations").option("--json", "print machine-readable JSON").action(async (opts) => {
|
|
2335
|
+
const { listOrganizations, getMe } = await import("@benchsdk/cli");
|
|
2336
|
+
const auth = await benchAuth();
|
|
2337
|
+
const [organizations, me] = await Promise.all([listOrganizations(auth), getMe(auth)]);
|
|
2338
|
+
if (opts.json) {
|
|
2339
|
+
console.log(JSON.stringify(organizations, null, 2));
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2342
|
+
for (const o of organizations) {
|
|
2343
|
+
const marker = o.id === me.activeOrganizationId ? pc4.green("*") : " ";
|
|
2344
|
+
console.log(`${marker} ${pc4.cyan(o.slug)} ${o.name ?? ""} ${pc4.dim(o.id)}`);
|
|
2345
|
+
}
|
|
2346
|
+
});
|
|
2347
|
+
org.command("use <slug>").description("Set the active organization (persisted to the stored login)").action(async (slug) => {
|
|
2348
|
+
const { loadCredentials, saveCredentials, createApiClient, setActiveOrganization } = await import("@benchsdk/cli");
|
|
2349
|
+
const credentials = await loadCredentials() ?? {};
|
|
2350
|
+
const { auth } = await createApiClient({ baseUrl: envBaseUrl() });
|
|
2351
|
+
const result = await setActiveOrganization(auth, slug);
|
|
2352
|
+
if (!result.organization) {
|
|
2353
|
+
throw new Error(`Could not set active organization to ${slug}`);
|
|
2354
|
+
}
|
|
2355
|
+
await saveCredentials({
|
|
2356
|
+
...credentials,
|
|
2357
|
+
baseUrl: auth.baseUrl,
|
|
2358
|
+
// An API-key login must not be rewritten as an OAuth one — keep its
|
|
2359
|
+
// key and kind, and only stamp token fields when this auth actually
|
|
2360
|
+
// is a token session.
|
|
2361
|
+
...auth.token ? {
|
|
2362
|
+
token: auth.token,
|
|
2363
|
+
refreshToken: auth.refreshToken,
|
|
2364
|
+
tokenExpiresAt: auth.tokenExpiresAt,
|
|
2365
|
+
refreshExpiresAt: auth.refreshExpiresAt,
|
|
2366
|
+
kind: "oauth"
|
|
2367
|
+
} : {},
|
|
2368
|
+
orgSlug: result.organization.slug,
|
|
2369
|
+
orgId: result.organization.id
|
|
2370
|
+
});
|
|
2371
|
+
console.log(`Active organization set to ${result.organization.slug} (${result.organization.id})`);
|
|
2372
|
+
});
|
|
2373
|
+
org.command("current").description("Show the current user and active organization").action(printWhoami);
|
|
2374
|
+
program.command("whoami").description("Show the current user and active organization").action(printWhoami);
|
|
2291
2375
|
registerActionsCommands(program);
|
|
2292
2376
|
registerMarketCommands(program);
|
|
2293
2377
|
registerSandboxesCommands(program);
|