@computesdk/cli 1.0.11 → 1.0.12
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/dist/index.js +211 -71
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -708,6 +708,76 @@ var ActionsApiError = class extends Error {
|
|
|
708
708
|
this.name = "ActionsApiError";
|
|
709
709
|
}
|
|
710
710
|
};
|
|
711
|
+
var ActionsCliError = class extends Error {
|
|
712
|
+
constructor(code, message) {
|
|
713
|
+
super(message);
|
|
714
|
+
this.code = code;
|
|
715
|
+
this.name = "ActionsCliError";
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
function codeForStatus(status) {
|
|
719
|
+
switch (status) {
|
|
720
|
+
case 400:
|
|
721
|
+
return "bad_request";
|
|
722
|
+
case 401:
|
|
723
|
+
return "unauthenticated";
|
|
724
|
+
case 403:
|
|
725
|
+
return "forbidden";
|
|
726
|
+
case 404:
|
|
727
|
+
return "not_found";
|
|
728
|
+
case 409:
|
|
729
|
+
return "conflict";
|
|
730
|
+
case 413:
|
|
731
|
+
return "payload_too_large";
|
|
732
|
+
case 429:
|
|
733
|
+
return "rate_limited";
|
|
734
|
+
default:
|
|
735
|
+
return status >= 500 ? "server_error" : "http_error";
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function errorDetails(body) {
|
|
739
|
+
if (body && typeof body === "object" && "details" in body) {
|
|
740
|
+
const details = body.details;
|
|
741
|
+
if (details && typeof details === "object" && !Array.isArray(details)) {
|
|
742
|
+
return details;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return void 0;
|
|
746
|
+
}
|
|
747
|
+
function toErrorEnvelope(error) {
|
|
748
|
+
if (error instanceof ActionsApiError) {
|
|
749
|
+
const details = errorDetails(error.body);
|
|
750
|
+
return {
|
|
751
|
+
ok: false,
|
|
752
|
+
error: {
|
|
753
|
+
code: codeForStatus(error.status),
|
|
754
|
+
message: error.message,
|
|
755
|
+
httpStatus: error.status,
|
|
756
|
+
retryable: error.status === 429 || error.status === 502 || error.status === 503 || error.status === 504,
|
|
757
|
+
...details !== void 0 && { details }
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
if (error instanceof ActionsCliError) {
|
|
762
|
+
return { ok: false, error: { code: error.code, message: error.message, retryable: false } };
|
|
763
|
+
}
|
|
764
|
+
if (error instanceof TypeError && /fetch failed/i.test(error.message)) {
|
|
765
|
+
const cause = error.cause;
|
|
766
|
+
const detail = cause instanceof Error ? `: ${cause.message}` : "";
|
|
767
|
+
return {
|
|
768
|
+
ok: false,
|
|
769
|
+
error: { code: "network", message: `${error.message}${detail}`, retryable: true }
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
return {
|
|
773
|
+
ok: false,
|
|
774
|
+
error: {
|
|
775
|
+
code: "unknown",
|
|
776
|
+
message: error instanceof Error ? error.message : String(error),
|
|
777
|
+
retryable: false
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
}
|
|
711
781
|
var ActionsClient = class {
|
|
712
782
|
constructor(auth, fetchImpl = fetch) {
|
|
713
783
|
this.auth = auth;
|
|
@@ -834,22 +904,64 @@ function encodeWatchCursor(id, jobId, step, offset) {
|
|
|
834
904
|
const stepPart = step === null ? "-" : step === "runner" ? "runner" : String(step);
|
|
835
905
|
return `${id}:${jobId}:${stepPart}:${offset}`;
|
|
836
906
|
}
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
if (!apiKey) {
|
|
840
|
-
throw new Error(
|
|
841
|
-
"No API key. Set COMPUTE_API_KEY or pass --api-key."
|
|
842
|
-
);
|
|
843
|
-
}
|
|
907
|
+
var NO_CREDENTIALS_HINT = "Set COMPUTE_API_KEY, pass --api-key, or run `compute bench auth login`.";
|
|
908
|
+
async function resolveActionsAuth(opts, resolveStored = resolveStoredPlatformAuth) {
|
|
844
909
|
const baseUrl = (opts.baseUrl || process.env.COMPUTE_PLATFORM_URL || process.env.BENCHMARKS_PLATFORM_URL || // legacy name
|
|
845
910
|
DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
846
911
|
if (!opts.allowUntrustedHost && !isTrustedActionsHost(baseUrl)) {
|
|
847
|
-
throw new
|
|
912
|
+
throw new ActionsCliError(
|
|
913
|
+
"untrusted_host",
|
|
848
914
|
`Refusing to send the API key to ${baseUrl} \u2014 it is not a computesdk.com or localhost host. If this is a self-hosted/dev deployment you trust, pass --allow-untrusted-host.`
|
|
849
915
|
);
|
|
850
916
|
}
|
|
917
|
+
if (!isSecureActionsTransport(baseUrl)) {
|
|
918
|
+
throw new ActionsCliError(
|
|
919
|
+
"insecure_transport",
|
|
920
|
+
`Refusing to send the API key over plaintext HTTP to ${baseUrl}. Use an https:// base URL; http:// is only allowed for localhost/loopback.`
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
let apiKey = opts.apiKey || process.env.COMPUTE_API_KEY || process.env.BENCHMARKS_PLATFORM_API_KEY || // legacy name
|
|
924
|
+
void 0;
|
|
925
|
+
if (!apiKey) {
|
|
926
|
+
if (!isTrustedActionsHost(baseUrl)) {
|
|
927
|
+
throw new ActionsCliError(
|
|
928
|
+
"untrusted_host_stored_auth",
|
|
929
|
+
`Refusing to use stored platform credentials with untrusted host ${baseUrl}. Pass --api-key or set COMPUTE_API_KEY to use an explicit key with --allow-untrusted-host.`
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
let stored;
|
|
933
|
+
try {
|
|
934
|
+
stored = await resolveStored({ baseUrl });
|
|
935
|
+
} catch (error) {
|
|
936
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
937
|
+
const detail = reason.startsWith("No credentials found") ? "No API key." : reason;
|
|
938
|
+
throw new ActionsCliError("no_credentials", `${detail} ${NO_CREDENTIALS_HINT}`);
|
|
939
|
+
}
|
|
940
|
+
apiKey = stored.apiKey || stored.token || void 0;
|
|
941
|
+
}
|
|
942
|
+
if (!apiKey) {
|
|
943
|
+
throw new ActionsCliError("no_credentials", `No API key. ${NO_CREDENTIALS_HINT}`);
|
|
944
|
+
}
|
|
851
945
|
return { apiKey, baseUrl };
|
|
852
946
|
}
|
|
947
|
+
async function resolveStoredPlatformAuth(opts) {
|
|
948
|
+
const { resolveAuth } = await import("@benchsdk/cli");
|
|
949
|
+
const auth = await resolveAuth({ baseUrl: opts.baseUrl });
|
|
950
|
+
return { apiKey: auth.apiKey, token: auth.token };
|
|
951
|
+
}
|
|
952
|
+
function isLoopbackHost(host) {
|
|
953
|
+
return host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "::1";
|
|
954
|
+
}
|
|
955
|
+
function isSecureActionsTransport(baseUrl) {
|
|
956
|
+
let url;
|
|
957
|
+
try {
|
|
958
|
+
url = new URL(baseUrl);
|
|
959
|
+
} catch {
|
|
960
|
+
return false;
|
|
961
|
+
}
|
|
962
|
+
if (url.protocol === "https:") return true;
|
|
963
|
+
return url.protocol === "http:" && isLoopbackHost(url.hostname);
|
|
964
|
+
}
|
|
853
965
|
function isTrustedActionsHost(baseUrl) {
|
|
854
966
|
let host;
|
|
855
967
|
try {
|
|
@@ -857,7 +969,7 @@ function isTrustedActionsHost(baseUrl) {
|
|
|
857
969
|
} catch {
|
|
858
970
|
return false;
|
|
859
971
|
}
|
|
860
|
-
return host === "computesdk.com" || host.endsWith(".computesdk.com") || host
|
|
972
|
+
return host === "computesdk.com" || host.endsWith(".computesdk.com") || isLoopbackHost(host);
|
|
861
973
|
}
|
|
862
974
|
|
|
863
975
|
// src/actions.ts
|
|
@@ -866,8 +978,8 @@ function sanitizePathPart(name) {
|
|
|
866
978
|
if (base === "" || base === "." || base === "..") return "artifact";
|
|
867
979
|
return base;
|
|
868
980
|
}
|
|
869
|
-
function client(opts) {
|
|
870
|
-
return new ActionsClient(resolveActionsAuth(opts));
|
|
981
|
+
async function client(opts) {
|
|
982
|
+
return new ActionsClient(await resolveActionsAuth(opts));
|
|
871
983
|
}
|
|
872
984
|
function output(opts, data, render) {
|
|
873
985
|
if (opts.json) {
|
|
@@ -912,14 +1024,24 @@ async function fetchRunSummary(c, runId) {
|
|
|
912
1024
|
return null;
|
|
913
1025
|
}
|
|
914
1026
|
}
|
|
915
|
-
function fail(error) {
|
|
916
|
-
if (
|
|
1027
|
+
function fail(error, opts = {}) {
|
|
1028
|
+
if (opts.json) {
|
|
1029
|
+
process.stderr.write(JSON.stringify(toErrorEnvelope(error)) + "\n");
|
|
1030
|
+
} else if (error instanceof ActionsApiError) {
|
|
917
1031
|
console.error(pc.red(`Error (${error.status}): ${error.message}`));
|
|
918
1032
|
} else {
|
|
919
|
-
console.error(pc.red(`Error: ${error.message}`));
|
|
1033
|
+
console.error(pc.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
920
1034
|
}
|
|
921
1035
|
process.exit(1);
|
|
922
1036
|
}
|
|
1037
|
+
function usageErrorOutput(str, write, argv = process.argv) {
|
|
1038
|
+
if (argv.includes("--json")) {
|
|
1039
|
+
const message = str.replace(/^error:\s*/i, "").trim();
|
|
1040
|
+
write(JSON.stringify(toErrorEnvelope(new ActionsCliError("invalid_argument", message))) + "\n");
|
|
1041
|
+
} else {
|
|
1042
|
+
write(str);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
923
1045
|
function formatDuration(ms) {
|
|
924
1046
|
if (ms === null || ms === void 0) return "-";
|
|
925
1047
|
if (ms < 1e3) return `${ms}ms`;
|
|
@@ -1085,7 +1207,7 @@ function parseInputs(pairs) {
|
|
|
1085
1207
|
for (const pair of pairs ?? []) {
|
|
1086
1208
|
const eq = pair.indexOf("=");
|
|
1087
1209
|
if (eq === -1) {
|
|
1088
|
-
throw new
|
|
1210
|
+
throw new ActionsCliError("invalid_argument", `Invalid --inputs entry "${pair}". Expected key=value.`);
|
|
1089
1211
|
}
|
|
1090
1212
|
inputs[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
1091
1213
|
}
|
|
@@ -1135,7 +1257,7 @@ function parseStep(step) {
|
|
|
1135
1257
|
if (step === "runner") return "runner";
|
|
1136
1258
|
const n = Number(step);
|
|
1137
1259
|
if (!Number.isInteger(n) || n < 0) {
|
|
1138
|
-
throw new
|
|
1260
|
+
throw new ActionsCliError("invalid_argument", `Invalid --step "${step}". Expected a step ordinal or "runner".`);
|
|
1139
1261
|
}
|
|
1140
1262
|
return n;
|
|
1141
1263
|
}
|
|
@@ -1233,14 +1355,43 @@ function printDiscovery(d) {
|
|
|
1233
1355
|
}
|
|
1234
1356
|
console.log(pc.dim(`discovery: ${d.workflowsFound ? "workflows found" : "no workflows yet"}${d.seeded ? ", cursors seeded" : ""}`));
|
|
1235
1357
|
}
|
|
1358
|
+
function dispatchBody(workflow, opts) {
|
|
1359
|
+
if (!workflow.dispatchable && !opts.manual) {
|
|
1360
|
+
throw new ActionsCliError(
|
|
1361
|
+
"invalid_argument",
|
|
1362
|
+
`Workflow "${workflow.path}" does not declare workflow_dispatch. Pass --manual to run it anyway (no inputs).`
|
|
1363
|
+
);
|
|
1364
|
+
}
|
|
1365
|
+
if (!workflow.dispatchable && opts.inputs !== void 0 && opts.inputs.length > 0) {
|
|
1366
|
+
throw new ActionsCliError(
|
|
1367
|
+
"invalid_argument",
|
|
1368
|
+
`Workflow "${workflow.path}" has no workflow_dispatch inputs; --manual runs of it take no --inputs.`
|
|
1369
|
+
);
|
|
1370
|
+
}
|
|
1371
|
+
const ref = opts.ref ?? workflow.refs[0];
|
|
1372
|
+
if (!ref) throw new ActionsCliError("invalid_argument", "No --ref given and the workflow has no watched refs.");
|
|
1373
|
+
if (opts.providerRegion !== void 0 && opts.provider === void 0) {
|
|
1374
|
+
throw new ActionsCliError("invalid_argument", "--provider-region requires --provider.");
|
|
1375
|
+
}
|
|
1376
|
+
return {
|
|
1377
|
+
workflowId: workflow.id,
|
|
1378
|
+
ref,
|
|
1379
|
+
inputs: parseInputs(opts.inputs),
|
|
1380
|
+
...opts.manual && { manual: true },
|
|
1381
|
+
...opts.provider !== void 0 && {
|
|
1382
|
+
provider: opts.provider,
|
|
1383
|
+
...opts.providerRegion !== void 0 && { providerRegion: opts.providerRegion }
|
|
1384
|
+
}
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1236
1387
|
function registerActionsCommands(program2) {
|
|
1237
|
-
const actions = program2.command("actions").alias("ci").description("Drive the benchmarks-platform Actions API");
|
|
1238
|
-
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
|
|
1388
|
+
const actions = program2.command("actions").alias("ci").description("Drive the benchmarks-platform Actions API").configureOutput({ outputError: usageErrorOutput });
|
|
1389
|
+
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");
|
|
1239
1390
|
common(
|
|
1240
|
-
actions.command("dispatch").description("Dispatch a workflow_dispatch run").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").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>)")
|
|
1391
|
+
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>)")
|
|
1241
1392
|
).action(async (repo, opts) => {
|
|
1242
1393
|
try {
|
|
1243
|
-
const c = client(opts);
|
|
1394
|
+
const c = await client(opts);
|
|
1244
1395
|
const { workflows } = await c.get(
|
|
1245
1396
|
"/api/v1/actions/workflows",
|
|
1246
1397
|
{ repo }
|
|
@@ -1248,26 +1399,15 @@ function registerActionsCommands(program2) {
|
|
|
1248
1399
|
const workflow = matchWorkflow(workflows, opts.workflow);
|
|
1249
1400
|
if (!workflow) {
|
|
1250
1401
|
const choices = workflows.map((w) => `${w.path} (${w.name})`).join(", ") || "none";
|
|
1251
|
-
throw new
|
|
1252
|
-
|
|
1402
|
+
throw new ActionsCliError(
|
|
1403
|
+
"workflow_not_found",
|
|
1404
|
+
`No workflow "${opts.workflow}" in ${repo}. Available: ${choices}`
|
|
1253
1405
|
);
|
|
1254
1406
|
}
|
|
1255
|
-
const
|
|
1256
|
-
if (!ref) throw new Error("No --ref given and the workflow has no watched refs.");
|
|
1257
|
-
if (opts.providerRegion !== void 0 && opts.provider === void 0) {
|
|
1258
|
-
throw new Error("--provider-region requires --provider.");
|
|
1259
|
-
}
|
|
1407
|
+
const body = dispatchBody(workflow, opts);
|
|
1260
1408
|
const result = await c.post(
|
|
1261
1409
|
"/api/v1/actions/dispatch",
|
|
1262
|
-
|
|
1263
|
-
workflowId: workflow.id,
|
|
1264
|
-
ref,
|
|
1265
|
-
inputs: parseInputs(opts.inputs),
|
|
1266
|
-
...opts.provider !== void 0 && {
|
|
1267
|
-
provider: opts.provider,
|
|
1268
|
-
...opts.providerRegion !== void 0 && { providerRegion: opts.providerRegion }
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1410
|
+
body
|
|
1271
1411
|
);
|
|
1272
1412
|
const org = await c.org();
|
|
1273
1413
|
const url = `${c.baseUrl}/${org.slug}/actions/runs/${result.runId}`;
|
|
@@ -1277,14 +1417,14 @@ function registerActionsCommands(program2) {
|
|
|
1277
1417
|
console.log(`url: ${r.url}`);
|
|
1278
1418
|
});
|
|
1279
1419
|
} catch (e) {
|
|
1280
|
-
fail(e);
|
|
1420
|
+
fail(e, opts);
|
|
1281
1421
|
}
|
|
1282
1422
|
});
|
|
1283
1423
|
common(
|
|
1284
1424
|
actions.command("runs").description("List recent runs for a repo").argument("<repo>", "repository in owner/repo format").option("--status <status...>", "filter by conclusion: passed, failed, running, cancelled, none").option("--branch <branch...>", "filter by branch/ref")
|
|
1285
1425
|
).action(async (repo, opts) => {
|
|
1286
1426
|
try {
|
|
1287
|
-
const { runs } = await client(opts).get(
|
|
1427
|
+
const { runs } = await (await client(opts)).get(
|
|
1288
1428
|
"/api/v1/actions/runs",
|
|
1289
1429
|
{ repo, status: opts.status, branch: opts.branch }
|
|
1290
1430
|
);
|
|
@@ -1296,14 +1436,14 @@ function registerActionsCommands(program2) {
|
|
|
1296
1436
|
for (const run of rs) console.log(formatRunRow(run));
|
|
1297
1437
|
});
|
|
1298
1438
|
} catch (e) {
|
|
1299
|
-
fail(e);
|
|
1439
|
+
fail(e, opts);
|
|
1300
1440
|
}
|
|
1301
1441
|
});
|
|
1302
1442
|
common(
|
|
1303
1443
|
actions.command("history").description("Recent run history for a workflow \u2014 per-job/step failure rates (is this failure flaky?)").argument("<repo>", "repository in owner/repo format").requiredOption("--workflow <path|name>", "workflow path or name").option("--branch <branch...>", "narrow the window to branches/refs").option("--job <job>", "narrow the rollup to one job name or workflow job id").option("--limit <n>", "runs in the window (default 10, max 50)")
|
|
1304
1444
|
).action(async (repo, opts) => {
|
|
1305
1445
|
try {
|
|
1306
|
-
const c = client(opts);
|
|
1446
|
+
const c = await client(opts);
|
|
1307
1447
|
const { workflows } = await c.get(
|
|
1308
1448
|
"/api/v1/actions/workflows",
|
|
1309
1449
|
{ repo }
|
|
@@ -1319,14 +1459,14 @@ function registerActionsCommands(program2) {
|
|
|
1319
1459
|
});
|
|
1320
1460
|
output(opts, history, (h) => console.log(formatRunHistory(h)));
|
|
1321
1461
|
} catch (e) {
|
|
1322
|
-
fail(e);
|
|
1462
|
+
fail(e, opts);
|
|
1323
1463
|
}
|
|
1324
1464
|
});
|
|
1325
1465
|
common(
|
|
1326
1466
|
actions.command("run").description("Show a run: jobs, provider:region placement, placement attempts").argument("<run-id>", "run ID")
|
|
1327
1467
|
).action(async (runId, opts) => {
|
|
1328
1468
|
try {
|
|
1329
|
-
const c = client(opts);
|
|
1469
|
+
const c = await client(opts);
|
|
1330
1470
|
const run = await c.get(`/api/v1/actions/runs/${runId}`);
|
|
1331
1471
|
const org = await c.org();
|
|
1332
1472
|
const url = `${c.baseUrl}/${org.slug}/actions/runs/${run.id}`;
|
|
@@ -1339,37 +1479,37 @@ function registerActionsCommands(program2) {
|
|
|
1339
1479
|
}
|
|
1340
1480
|
});
|
|
1341
1481
|
} catch (e) {
|
|
1342
|
-
fail(e);
|
|
1482
|
+
fail(e, opts);
|
|
1343
1483
|
}
|
|
1344
1484
|
});
|
|
1345
1485
|
common(
|
|
1346
1486
|
actions.command("summary").description("Show a run's failure digest: failed jobs, failed steps, bounded log tails").argument("<run-id>", "run ID")
|
|
1347
1487
|
).action(async (runId, opts) => {
|
|
1348
1488
|
try {
|
|
1349
|
-
const summary = await client(opts).get(`/api/v1/actions/runs/${runId}/summary`);
|
|
1489
|
+
const summary = await (await client(opts)).get(`/api/v1/actions/runs/${runId}/summary`);
|
|
1350
1490
|
output(opts, summary, (s) => console.log(formatRunSummary(s)));
|
|
1351
1491
|
} catch (e) {
|
|
1352
|
-
fail(e);
|
|
1492
|
+
fail(e, opts);
|
|
1353
1493
|
}
|
|
1354
1494
|
});
|
|
1355
1495
|
common(
|
|
1356
1496
|
actions.command("inspect").description("Inspect a run: resolved runner image, caches, secret names, concurrency, placement context").argument("<run-id>", "run ID")
|
|
1357
1497
|
).action(async (runId, opts) => {
|
|
1358
1498
|
try {
|
|
1359
|
-
const c = client(opts);
|
|
1499
|
+
const c = await client(opts);
|
|
1360
1500
|
const inspection = await c.get(
|
|
1361
1501
|
`/api/v1/actions/runs/${runId}/state`
|
|
1362
1502
|
);
|
|
1363
1503
|
output(opts, inspection, (r) => console.log(formatRunInspection(r)));
|
|
1364
1504
|
} catch (e) {
|
|
1365
|
-
fail(e);
|
|
1505
|
+
fail(e, opts);
|
|
1366
1506
|
}
|
|
1367
1507
|
});
|
|
1368
1508
|
common(
|
|
1369
1509
|
actions.command("logs").description("Print or follow a run's job logs").argument("<run-id>", "run ID").option("--job <job>", "job ID or name (default: all jobs)").option("--step <step>", 'step ordinal or "runner" (default: whole job)').option("--follow", "stream logs while the run is live")
|
|
1370
1510
|
).action(async (runId, opts) => {
|
|
1371
1511
|
try {
|
|
1372
|
-
const c = client(opts);
|
|
1512
|
+
const c = await client(opts);
|
|
1373
1513
|
let jobs = await listRunJobs(c, runId);
|
|
1374
1514
|
if (opts.job) {
|
|
1375
1515
|
const job = matchJob(jobs, opts.job);
|
|
@@ -1420,25 +1560,25 @@ function registerActionsCommands(program2) {
|
|
|
1420
1560
|
}
|
|
1421
1561
|
}
|
|
1422
1562
|
} catch (e) {
|
|
1423
|
-
fail(e);
|
|
1563
|
+
fail(e, opts);
|
|
1424
1564
|
}
|
|
1425
1565
|
});
|
|
1426
1566
|
const providers = actions.command("providers").description("List the org's registered compute providers (regions, act/usable status)");
|
|
1427
1567
|
common(providers).action(async (opts) => {
|
|
1428
1568
|
try {
|
|
1429
|
-
const data = await client(opts).get("/api/v1/actions/providers");
|
|
1569
|
+
const data = await (await client(opts)).get("/api/v1/actions/providers");
|
|
1430
1570
|
output(opts, data, (d) => {
|
|
1431
1571
|
for (const p5 of d.providers) console.log(formatProviderRow(p5));
|
|
1432
1572
|
});
|
|
1433
1573
|
} catch (e) {
|
|
1434
|
-
fail(e);
|
|
1574
|
+
fail(e, opts);
|
|
1435
1575
|
}
|
|
1436
1576
|
});
|
|
1437
1577
|
common(
|
|
1438
1578
|
providers.command("configure").description("Save the org's credential for a provider (owner/admin API key)").argument("<provider>", "provider id (e.g. tensorlake, blaxel)").option("--key <key>", "provider API key (single-field credentials)").option("--field <pair...>", "credential fields as name=value (multi-field providers)").option("--verify", "run the placement probe after saving")
|
|
1439
1579
|
).action(async (provider, opts) => {
|
|
1440
1580
|
try {
|
|
1441
|
-
const c = client(opts);
|
|
1581
|
+
const c = await client(opts);
|
|
1442
1582
|
let body;
|
|
1443
1583
|
if (opts.field !== void 0) {
|
|
1444
1584
|
if (opts.key !== void 0) throw new Error("Pass either --key or --field, not both.");
|
|
@@ -1461,45 +1601,45 @@ function registerActionsCommands(program2) {
|
|
|
1461
1601
|
});
|
|
1462
1602
|
if (verification?.verified === false) process.exitCode = 1;
|
|
1463
1603
|
} catch (e) {
|
|
1464
|
-
fail(e);
|
|
1604
|
+
fail(e, opts);
|
|
1465
1605
|
}
|
|
1466
1606
|
});
|
|
1467
1607
|
common(
|
|
1468
1608
|
providers.command("verify").description('Probe the stored provider key with a real sandbox ("Test" in Settings)').argument("<provider>", "provider id")
|
|
1469
1609
|
).action(async (provider, opts) => {
|
|
1470
1610
|
try {
|
|
1471
|
-
const result = await client(opts).post(
|
|
1611
|
+
const result = await (await client(opts)).post(
|
|
1472
1612
|
`/api/v1/actions/providers/${provider}/verify`
|
|
1473
1613
|
);
|
|
1474
1614
|
output(opts, result, (r) => console.log(formatVerifyResult(r)));
|
|
1475
1615
|
if (result.verified === false) process.exitCode = 1;
|
|
1476
1616
|
} catch (e) {
|
|
1477
|
-
fail(e);
|
|
1617
|
+
fail(e, opts);
|
|
1478
1618
|
}
|
|
1479
1619
|
});
|
|
1480
1620
|
common(
|
|
1481
1621
|
providers.command("remove").description("Delete the org's stored credential for a provider").argument("<provider>", "provider id")
|
|
1482
1622
|
).action(async (provider, opts) => {
|
|
1483
1623
|
try {
|
|
1484
|
-
const result = await client(opts).del(
|
|
1624
|
+
const result = await (await client(opts)).del(
|
|
1485
1625
|
`/api/v1/actions/providers/${provider}/key`
|
|
1486
1626
|
);
|
|
1487
1627
|
output(opts, result, (r) => {
|
|
1488
1628
|
console.log(r.deleted ? `deleted ${r.provider}` : `no key stored for ${r.provider}`);
|
|
1489
1629
|
});
|
|
1490
1630
|
} catch (e) {
|
|
1491
|
-
fail(e);
|
|
1631
|
+
fail(e, opts);
|
|
1492
1632
|
}
|
|
1493
1633
|
});
|
|
1494
1634
|
const repos = actions.command("repos").description("List the org's connected repositories");
|
|
1495
1635
|
common(repos).action(async (opts) => {
|
|
1496
1636
|
try {
|
|
1497
|
-
const data = await client(opts).get("/api/v1/actions/repos");
|
|
1637
|
+
const data = await (await client(opts)).get("/api/v1/actions/repos");
|
|
1498
1638
|
output(opts, data, (d) => {
|
|
1499
1639
|
for (const r of d.repos) console.log(formatRepoRow(r));
|
|
1500
1640
|
});
|
|
1501
1641
|
} catch (e) {
|
|
1502
|
-
fail(e);
|
|
1642
|
+
fail(e, opts);
|
|
1503
1643
|
}
|
|
1504
1644
|
});
|
|
1505
1645
|
common(
|
|
@@ -1529,7 +1669,7 @@ function registerActionsCommands(program2) {
|
|
|
1529
1669
|
if (opts.knownHostsFile !== void 0 && opts.sshKeyFile === void 0) {
|
|
1530
1670
|
throw new Error("--known-hosts-file requires --ssh-key-file.");
|
|
1531
1671
|
}
|
|
1532
|
-
const result = await client(opts).post("/api/v1/actions/repos", {
|
|
1672
|
+
const result = await (await client(opts)).post("/api/v1/actions/repos", {
|
|
1533
1673
|
cloneUrl,
|
|
1534
1674
|
...body,
|
|
1535
1675
|
...opts.name !== void 0 && { name: opts.name },
|
|
@@ -1540,14 +1680,14 @@ function registerActionsCommands(program2) {
|
|
|
1540
1680
|
printDiscovery(r.discovery);
|
|
1541
1681
|
});
|
|
1542
1682
|
} catch (e) {
|
|
1543
|
-
fail(e);
|
|
1683
|
+
fail(e, opts);
|
|
1544
1684
|
}
|
|
1545
1685
|
});
|
|
1546
1686
|
common(
|
|
1547
1687
|
repos.command("enable").description("Enable a repo the org can already see (granted by a GitHub App install, or a connected remote)").argument("<repo>", "repository in owner/repo format").option("--repo-id <id>", "scope to one row when two installations grant the same name")
|
|
1548
1688
|
).action(async (repo, opts) => {
|
|
1549
1689
|
try {
|
|
1550
|
-
const result = await client(opts).patch("/api/v1/actions/repos", {
|
|
1690
|
+
const result = await (await client(opts)).patch("/api/v1/actions/repos", {
|
|
1551
1691
|
fullName: repo,
|
|
1552
1692
|
enabled: true,
|
|
1553
1693
|
...opts.repoId !== void 0 && { repoId: opts.repoId }
|
|
@@ -1557,42 +1697,42 @@ function registerActionsCommands(program2) {
|
|
|
1557
1697
|
if (r.discovery) printDiscovery(r.discovery);
|
|
1558
1698
|
});
|
|
1559
1699
|
} catch (e) {
|
|
1560
|
-
fail(e);
|
|
1700
|
+
fail(e, opts);
|
|
1561
1701
|
}
|
|
1562
1702
|
});
|
|
1563
1703
|
common(
|
|
1564
1704
|
repos.command("disable").description("Disable a repo \u2014 it stays connected but no runs are scheduled").argument("<repo>", "repository in owner/repo format").option("--repo-id <id>", "scope to one row when two installations grant the same name")
|
|
1565
1705
|
).action(async (repo, opts) => {
|
|
1566
1706
|
try {
|
|
1567
|
-
const result = await client(opts).patch("/api/v1/actions/repos", {
|
|
1707
|
+
const result = await (await client(opts)).patch("/api/v1/actions/repos", {
|
|
1568
1708
|
fullName: repo,
|
|
1569
1709
|
enabled: false,
|
|
1570
1710
|
...opts.repoId !== void 0 && { repoId: opts.repoId }
|
|
1571
1711
|
});
|
|
1572
1712
|
output(opts, result, (r) => console.log(`disabled ${pc.cyan(safeTerm(r.fullName))}`));
|
|
1573
1713
|
} catch (e) {
|
|
1574
|
-
fail(e);
|
|
1714
|
+
fail(e, opts);
|
|
1575
1715
|
}
|
|
1576
1716
|
});
|
|
1577
1717
|
common(
|
|
1578
1718
|
actions.command("cancel").description("Cancel a run").argument("<run-id>", "run ID")
|
|
1579
1719
|
).action(async (runId, opts) => {
|
|
1580
1720
|
try {
|
|
1581
|
-
const result = await client(opts).post(
|
|
1721
|
+
const result = await (await client(opts)).post(
|
|
1582
1722
|
`/api/v1/actions/runs/${runId}/cancel`
|
|
1583
1723
|
);
|
|
1584
1724
|
output(opts, result, (r) => {
|
|
1585
1725
|
console.log(r.cancelled ? `Cancelled ${runId}` : `Run ${runId} was already finished`);
|
|
1586
1726
|
});
|
|
1587
1727
|
} catch (e) {
|
|
1588
|
-
fail(e);
|
|
1728
|
+
fail(e, opts);
|
|
1589
1729
|
}
|
|
1590
1730
|
});
|
|
1591
1731
|
common(
|
|
1592
1732
|
actions.command("rerun").description("Re-run a finished run at the same commit").argument("<run-id>", "run ID")
|
|
1593
1733
|
).action(async (runId, opts) => {
|
|
1594
1734
|
try {
|
|
1595
|
-
const c = client(opts);
|
|
1735
|
+
const c = await client(opts);
|
|
1596
1736
|
const result = await c.post(
|
|
1597
1737
|
`/api/v1/actions/runs/${runId}/rerun`
|
|
1598
1738
|
);
|
|
@@ -1603,14 +1743,14 @@ function registerActionsCommands(program2) {
|
|
|
1603
1743
|
console.log(`url: ${r.url}`);
|
|
1604
1744
|
});
|
|
1605
1745
|
} catch (e) {
|
|
1606
|
-
fail(e);
|
|
1746
|
+
fail(e, opts);
|
|
1607
1747
|
}
|
|
1608
1748
|
});
|
|
1609
1749
|
common(
|
|
1610
1750
|
actions.command("artifacts").description("List or download a run's artifacts").argument("<run-id>", "run ID").option("--job <job>", "job ID or name (default: all jobs)").option("--out <dir>", "download artifacts into this directory instead of listing")
|
|
1611
1751
|
).action(async (runId, opts) => {
|
|
1612
1752
|
try {
|
|
1613
|
-
const c = client(opts);
|
|
1753
|
+
const c = await client(opts);
|
|
1614
1754
|
let jobs = await listRunJobs(c, runId);
|
|
1615
1755
|
if (opts.job) {
|
|
1616
1756
|
const job = matchJob(jobs, opts.job);
|
|
@@ -1659,7 +1799,7 @@ function registerActionsCommands(program2) {
|
|
|
1659
1799
|
if (!any) console.log("No artifacts.");
|
|
1660
1800
|
});
|
|
1661
1801
|
} catch (e) {
|
|
1662
|
-
fail(e);
|
|
1802
|
+
fail(e, opts);
|
|
1663
1803
|
}
|
|
1664
1804
|
});
|
|
1665
1805
|
}
|