@computesdk/cli 2.0.0 → 2.1.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/README.md +21 -0
- package/dist/index.js +190 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -29,11 +29,32 @@ 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
|
+
|
|
47
|
+
For `compute sandboxes exec` and `compute sandboxes spawn`, everything after
|
|
48
|
+
the command's first word goes to the sandbox untouched — flags like `uname
|
|
49
|
+
-a` or `node -e` are not CLI options. Pass CLI options (`--json`,
|
|
50
|
+
`--timeout-ms`, `--cwd`, `-e/--env`, `--stdin`, `--org`) before the command;
|
|
51
|
+
a bare `--` before the command is optional but supported.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
compute sandboxes exec sb1 uname -a
|
|
55
|
+
compute sandboxes spawn sb1 --cwd /app -e NODE_ENV=production -- npm run dev
|
|
56
|
+
```
|
|
57
|
+
|
|
37
58
|
## Migrating from @computesdk/cli 1.x
|
|
38
59
|
|
|
39
60
|
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) => {
|
|
@@ -1872,8 +1872,82 @@ function printSnapshot(s) {
|
|
|
1872
1872
|
` provider: ${s.provider}${s.region ? ` (${s.region})` : ""} label: ${s.label ?? "\u2014"} created: ${s.createdAt}${s.expiresAt ? ` expires: ${s.expiresAt}` : ""}`
|
|
1873
1873
|
);
|
|
1874
1874
|
}
|
|
1875
|
+
var AUTH_LEAD_OPTS = [
|
|
1876
|
+
{ aliases: ["--api-key"], key: "apiKey", flag: "value" },
|
|
1877
|
+
{ aliases: ["--base-url"], key: "baseUrl", flag: "value" },
|
|
1878
|
+
{ aliases: ["--allow-untrusted-host"], key: "allowUntrustedHost", flag: "bool" },
|
|
1879
|
+
{ aliases: ["--org"], key: "org", flag: "value" },
|
|
1880
|
+
{ aliases: ["--json"], key: "json", flag: "bool" }
|
|
1881
|
+
];
|
|
1882
|
+
var EXEC_LEAD_OPTS = [
|
|
1883
|
+
...AUTH_LEAD_OPTS,
|
|
1884
|
+
{ aliases: ["--timeout-ms"], key: "timeoutMs", flag: "value" }
|
|
1885
|
+
];
|
|
1886
|
+
var SPAWN_LEAD_OPTS = [
|
|
1887
|
+
...AUTH_LEAD_OPTS,
|
|
1888
|
+
{ aliases: ["--cwd"], key: "cwd", flag: "value" },
|
|
1889
|
+
{ aliases: ["-e", "--env"], key: "env", flag: "repeat" },
|
|
1890
|
+
{ aliases: ["--stdin"], key: "stdin", flag: "bool" }
|
|
1891
|
+
];
|
|
1892
|
+
function takeLeadingOptions(argv, specs) {
|
|
1893
|
+
var _a;
|
|
1894
|
+
const byAlias = /* @__PURE__ */ new Map();
|
|
1895
|
+
for (const spec of specs) for (const alias of spec.aliases) byAlias.set(alias, spec);
|
|
1896
|
+
const inline = {};
|
|
1897
|
+
let i = 0;
|
|
1898
|
+
for (; i < argv.length; i++) {
|
|
1899
|
+
const token = argv[i];
|
|
1900
|
+
if (token === "--") {
|
|
1901
|
+
i++;
|
|
1902
|
+
break;
|
|
1903
|
+
}
|
|
1904
|
+
if (!token.startsWith("-") || token === "-") break;
|
|
1905
|
+
const eq = token.indexOf("=");
|
|
1906
|
+
const flag = eq === -1 ? token : token.slice(0, eq);
|
|
1907
|
+
let spec = byAlias.get(flag);
|
|
1908
|
+
let attached;
|
|
1909
|
+
if (!spec && !flag.startsWith("--") && flag.length > 2) {
|
|
1910
|
+
const shortSpec = byAlias.get(flag.slice(0, 2));
|
|
1911
|
+
if (shortSpec && shortSpec.flag !== "bool") {
|
|
1912
|
+
spec = shortSpec;
|
|
1913
|
+
attached = flag.slice(2);
|
|
1914
|
+
if (attached.startsWith("=")) attached = attached.slice(1);
|
|
1915
|
+
if (eq !== -1) attached += token.slice(eq);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
if (!spec) {
|
|
1919
|
+
throw new Error(
|
|
1920
|
+
`unknown option '${flag}' \u2014 pass CLI options before the command, or use -- to separate them`
|
|
1921
|
+
);
|
|
1922
|
+
}
|
|
1923
|
+
if (spec.flag === "bool") {
|
|
1924
|
+
if (eq !== -1) throw new Error(`option '${flag}' takes no value`);
|
|
1925
|
+
inline[spec.key] = true;
|
|
1926
|
+
} else {
|
|
1927
|
+
const value = attached ?? (eq !== -1 ? token.slice(eq + 1) : argv[++i]);
|
|
1928
|
+
if (value === void 0) throw new Error(`option '${flag}' requires a value`);
|
|
1929
|
+
if (spec.flag === "repeat") {
|
|
1930
|
+
(inline[_a = spec.key] ?? (inline[_a] = [])).push(value);
|
|
1931
|
+
} else {
|
|
1932
|
+
inline[spec.key] = value;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
return { command: argv.slice(i), inline };
|
|
1937
|
+
}
|
|
1938
|
+
function mergeLeadOpts(opts, inline) {
|
|
1939
|
+
const merged = { ...opts };
|
|
1940
|
+
for (const [key, value] of Object.entries(inline)) {
|
|
1941
|
+
if (Array.isArray(value)) {
|
|
1942
|
+
merged[key] = [...merged[key] ?? [], ...value];
|
|
1943
|
+
} else {
|
|
1944
|
+
merged[key] = value;
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
return merged;
|
|
1948
|
+
}
|
|
1875
1949
|
function registerSandboxesCommands(program2) {
|
|
1876
|
-
const cmd = program2.command("sandboxes").alias("sbx").description("Create and drive sandboxes through the platform").configureOutput({ outputError: usageErrorOutput });
|
|
1950
|
+
const cmd = program2.command("sandboxes").alias("sbx").description("Create and drive sandboxes through the platform").enablePositionalOptions().configureOutput({ outputError: usageErrorOutput });
|
|
1877
1951
|
cmd.command("create").description('Place a sandbox (provider order walks 1,2,3; "market" bids first)').option("--order <providers>", 'provider order, comma-separated (e.g. "market,blaxel,vercel")').option("--label <label>", "label for the sandbox").option("--image <image>", "container image to boot").option("--snapshot-id <id>", "provider snapshot to resume").option("--cpus <n>", "CPU cores").option("--memory-mb <n>", "memory in MB").option("--disk-mb <n>", "ephemeral disk in MB").option("--timeout-ms <ms>", "sandbox timeout in ms").option("--secret <name>", "vault secret name to inject (repeatable)", (v, a) => a.concat(v), []).option("--api-key <key>", "platform API key (or COMPUTE_API_KEY)").option("--base-url <url>", "platform base URL").option("--allow-untrusted-host", "allow non-computesdk.com base URLs").option("--json", "print the raw response").action(async (opts) => {
|
|
1878
1952
|
try {
|
|
1879
1953
|
const c = await client(opts);
|
|
@@ -1928,8 +2002,13 @@ function registerSandboxesCommands(program2) {
|
|
|
1928
2002
|
fail(e, opts);
|
|
1929
2003
|
}
|
|
1930
2004
|
});
|
|
1931
|
-
cmd.command("exec <sandboxId> <command...>").description("Run a one-shot command and print its result").option("--timeout-ms <ms>", "command timeout in ms").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId,
|
|
2005
|
+
cmd.command("exec <sandboxId> <command...>").description("Run a one-shot command and print its result (options before the command; -- also works)").option("--timeout-ms <ms>", "command timeout in ms").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").passThroughOptions().action(async (sandboxId, rawCommand, rawOpts) => {
|
|
2006
|
+
let opts = rawOpts;
|
|
1932
2007
|
try {
|
|
2008
|
+
const lead = takeLeadingOptions(rawCommand, EXEC_LEAD_OPTS);
|
|
2009
|
+
opts = mergeLeadOpts(rawOpts, lead.inline);
|
|
2010
|
+
const command = lead.command;
|
|
2011
|
+
if (command.length === 0) throw new Error("missing command to run");
|
|
1933
2012
|
const c = await client(opts);
|
|
1934
2013
|
const res = await c.post(
|
|
1935
2014
|
`/api/v1/sandboxes/${sandboxId}/commands`,
|
|
@@ -1948,8 +2027,13 @@ function registerSandboxesCommands(program2) {
|
|
|
1948
2027
|
fail(e, opts);
|
|
1949
2028
|
}
|
|
1950
2029
|
});
|
|
1951
|
-
cmd.command("spawn <sandboxId> <command...>").description("Start a detached process that outlives this request").option("--cwd <dir>", "working directory").option("-e, --env <key=value>", "environment variable (repeatable)", (v, a) => a.concat(v), []).option("--stdin", "keep the process's stdin pipe open for `stdin`/`close-stdin`").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId,
|
|
2030
|
+
cmd.command("spawn <sandboxId> <command...>").description("Start a detached process that outlives this request (options before the command; -- also works)").option("--cwd <dir>", "working directory").option("-e, --env <key=value>", "environment variable (repeatable)", (v, a) => a.concat(v), []).option("--stdin", "keep the process's stdin pipe open for `stdin`/`close-stdin`").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").passThroughOptions().action(async (sandboxId, rawCommand, rawOpts) => {
|
|
2031
|
+
let opts = rawOpts;
|
|
1952
2032
|
try {
|
|
2033
|
+
const lead = takeLeadingOptions(rawCommand, SPAWN_LEAD_OPTS);
|
|
2034
|
+
opts = mergeLeadOpts(rawOpts, lead.inline);
|
|
2035
|
+
const command = lead.command;
|
|
2036
|
+
if (command.length === 0) throw new Error("missing command to spawn");
|
|
1953
2037
|
const c = await client(opts);
|
|
1954
2038
|
const res = await c.post(
|
|
1955
2039
|
`/api/v1/sandboxes/${sandboxId}/processes`,
|
|
@@ -2199,6 +2283,9 @@ function registerSandboxesCommands(program2) {
|
|
|
2199
2283
|
fail(e, opts);
|
|
2200
2284
|
}
|
|
2201
2285
|
});
|
|
2286
|
+
for (const sub of cmd.commands) {
|
|
2287
|
+
sub.option("--org <slug>", "organization slug for this command (or $COMPUTE_ORG)");
|
|
2288
|
+
}
|
|
2202
2289
|
}
|
|
2203
2290
|
async function followProcess(c, sandboxId, jobId) {
|
|
2204
2291
|
let exitCode = 0;
|
|
@@ -2234,17 +2321,41 @@ var isDevBuild = import.meta.url.includes("worktrees") || import.meta.url.includ
|
|
|
2234
2321
|
var VERSION = isDevBuild ? `${packageJson.version}-dev` : packageJson.version;
|
|
2235
2322
|
var program = new Command();
|
|
2236
2323
|
program.name("compute").description("ComputeSDK CLI").version(VERSION).enablePositionalOptions().passThroughOptions();
|
|
2324
|
+
function envBaseUrl() {
|
|
2325
|
+
return process.env.COMPUTE_PLATFORM_URL ?? process.env.BENCHMARKS_PLATFORM_URL;
|
|
2326
|
+
}
|
|
2327
|
+
async function benchAuth(opts = {}) {
|
|
2328
|
+
const { resolveAuth } = await import("@benchsdk/cli");
|
|
2329
|
+
return resolveAuth({ baseUrl: opts.baseUrl ?? envBaseUrl() });
|
|
2330
|
+
}
|
|
2331
|
+
async function printSessionLine(options) {
|
|
2332
|
+
try {
|
|
2333
|
+
const { getMe } = await import("@benchsdk/cli");
|
|
2334
|
+
const me = await getMe(await benchAuth({ baseUrl: options?.baseUrl }));
|
|
2335
|
+
const active = me.organizations.find((o) => o.id === me.activeOrganizationId);
|
|
2336
|
+
const who = me.user.email ?? me.user.name ?? me.user.id;
|
|
2337
|
+
if (active) {
|
|
2338
|
+
p.log.success(
|
|
2339
|
+
`Logged in as ${who} \u2014 active org: ${active.slug}. Change with \`compute org use <slug>\`.`
|
|
2340
|
+
);
|
|
2341
|
+
} else {
|
|
2342
|
+
p.log.success(`Logged in as ${who} \u2014 no active org`);
|
|
2343
|
+
}
|
|
2344
|
+
} catch {
|
|
2345
|
+
p.log.warn("Logged in, but could not fetch session info.");
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2237
2348
|
async function runLogin(options) {
|
|
2238
2349
|
const { oauthLogin } = await import("@benchsdk/cli");
|
|
2239
2350
|
await oauthLogin({
|
|
2240
|
-
baseUrl: options?.baseUrl ??
|
|
2351
|
+
baseUrl: options?.baseUrl ?? envBaseUrl()
|
|
2241
2352
|
});
|
|
2242
2353
|
}
|
|
2243
2354
|
async function runLogout() {
|
|
2244
2355
|
const { clearCredentials } = await import("@benchsdk/cli");
|
|
2245
2356
|
await clearCredentials();
|
|
2246
2357
|
}
|
|
2247
|
-
program.option("--login", "force re-authentication").option("--logout", "clear stored credentials").action(async (opts) => {
|
|
2358
|
+
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
2359
|
if (opts.logout) {
|
|
2249
2360
|
console.log();
|
|
2250
2361
|
p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
|
|
@@ -2257,11 +2368,16 @@ program.option("--login", "force re-authentication").option("--logout", "clear s
|
|
|
2257
2368
|
console.log();
|
|
2258
2369
|
p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
|
|
2259
2370
|
await runLogin();
|
|
2371
|
+
await printSessionLine();
|
|
2260
2372
|
p.outro(pc4.green("Authenticated!"));
|
|
2261
2373
|
process.exit(0);
|
|
2262
2374
|
}
|
|
2263
2375
|
program.help();
|
|
2264
2376
|
});
|
|
2377
|
+
program.hook("preAction", (thisCommand) => {
|
|
2378
|
+
const org2 = thisCommand.opts().org;
|
|
2379
|
+
if (org2) process.env.COMPUTE_ORG = org2;
|
|
2380
|
+
});
|
|
2265
2381
|
program.command("providers").description("List all providers and their configuration status").action(async () => {
|
|
2266
2382
|
console.log();
|
|
2267
2383
|
p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
|
|
@@ -2279,6 +2395,7 @@ program.command("login").description("Authenticate with ComputeSDK (OAuth device
|
|
|
2279
2395
|
console.log();
|
|
2280
2396
|
p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
|
|
2281
2397
|
await runLogin({ baseUrl: opts.baseUrl });
|
|
2398
|
+
await printSessionLine({ baseUrl: opts.baseUrl });
|
|
2282
2399
|
p.outro(pc4.green("Authenticated!"));
|
|
2283
2400
|
});
|
|
2284
2401
|
program.command("logout").description("Clear stored credentials").action(async () => {
|
|
@@ -2288,6 +2405,57 @@ program.command("logout").description("Clear stored credentials").action(async (
|
|
|
2288
2405
|
p.log.success("Logged out. Stored credentials removed.");
|
|
2289
2406
|
p.outro(pc4.green("Done!"));
|
|
2290
2407
|
});
|
|
2408
|
+
async function printWhoami() {
|
|
2409
|
+
const { getMe } = await import("@benchsdk/cli");
|
|
2410
|
+
const me = await getMe(await benchAuth());
|
|
2411
|
+
const active = me.organizations.find((o) => o.id === me.activeOrganizationId);
|
|
2412
|
+
console.log(me.user.email ?? me.user.name ?? me.user.id);
|
|
2413
|
+
console.log(
|
|
2414
|
+
`active org: ${active ? `${active.slug} (${active.id})` : "\u2014"}${me.organizations.length > 1 ? " \u2014 change with `compute org use <slug>`" : ""}`
|
|
2415
|
+
);
|
|
2416
|
+
}
|
|
2417
|
+
var org = program.command("org").description("Manage the active organization");
|
|
2418
|
+
org.command("list").description("List your organizations").option("--json", "print machine-readable JSON").action(async (opts) => {
|
|
2419
|
+
const { listOrganizations, getMe } = await import("@benchsdk/cli");
|
|
2420
|
+
const auth = await benchAuth();
|
|
2421
|
+
const [organizations, me] = await Promise.all([listOrganizations(auth), getMe(auth)]);
|
|
2422
|
+
if (opts.json) {
|
|
2423
|
+
console.log(JSON.stringify(organizations, null, 2));
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
2426
|
+
for (const o of organizations) {
|
|
2427
|
+
const marker = o.id === me.activeOrganizationId ? pc4.green("*") : " ";
|
|
2428
|
+
console.log(`${marker} ${pc4.cyan(o.slug)} ${o.name ?? ""} ${pc4.dim(o.id)}`);
|
|
2429
|
+
}
|
|
2430
|
+
});
|
|
2431
|
+
org.command("use <slug>").description("Set the active organization (persisted to the stored login)").action(async (slug) => {
|
|
2432
|
+
const { loadCredentials, saveCredentials, createApiClient, setActiveOrganization } = await import("@benchsdk/cli");
|
|
2433
|
+
const credentials = await loadCredentials() ?? {};
|
|
2434
|
+
const { auth } = await createApiClient({ baseUrl: envBaseUrl() });
|
|
2435
|
+
const result = await setActiveOrganization(auth, slug);
|
|
2436
|
+
if (!result.organization) {
|
|
2437
|
+
throw new Error(`Could not set active organization to ${slug}`);
|
|
2438
|
+
}
|
|
2439
|
+
await saveCredentials({
|
|
2440
|
+
...credentials,
|
|
2441
|
+
baseUrl: auth.baseUrl,
|
|
2442
|
+
// An API-key login must not be rewritten as an OAuth one — keep its
|
|
2443
|
+
// key and kind, and only stamp token fields when this auth actually
|
|
2444
|
+
// is a token session.
|
|
2445
|
+
...auth.token ? {
|
|
2446
|
+
token: auth.token,
|
|
2447
|
+
refreshToken: auth.refreshToken,
|
|
2448
|
+
tokenExpiresAt: auth.tokenExpiresAt,
|
|
2449
|
+
refreshExpiresAt: auth.refreshExpiresAt,
|
|
2450
|
+
kind: "oauth"
|
|
2451
|
+
} : {},
|
|
2452
|
+
orgSlug: result.organization.slug,
|
|
2453
|
+
orgId: result.organization.id
|
|
2454
|
+
});
|
|
2455
|
+
console.log(`Active organization set to ${result.organization.slug} (${result.organization.id})`);
|
|
2456
|
+
});
|
|
2457
|
+
org.command("current").description("Show the current user and active organization").action(printWhoami);
|
|
2458
|
+
program.command("whoami").description("Show the current user and active organization").action(printWhoami);
|
|
2291
2459
|
registerActionsCommands(program);
|
|
2292
2460
|
registerMarketCommands(program);
|
|
2293
2461
|
registerSandboxesCommands(program);
|