@computesdk/cli 1.0.12 → 1.0.13

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 CHANGED
@@ -981,6 +981,19 @@ function sanitizePathPart(name) {
981
981
  async function client(opts) {
982
982
  return new ActionsClient(await resolveActionsAuth(opts));
983
983
  }
984
+ function assertVaultValueHost(auth) {
985
+ if (!isTrustedActionsHost(auth.baseUrl)) {
986
+ throw new ActionsCliError(
987
+ "untrusted_host",
988
+ `Refusing to send or read vault values at ${auth.baseUrl} \u2014 only computesdk.com and localhost hosts are allowed, even with --allow-untrusted-host.`
989
+ );
990
+ }
991
+ }
992
+ async function vaultValueClient(opts) {
993
+ const auth = await resolveActionsAuth(opts);
994
+ assertVaultValueHost(auth);
995
+ return new ActionsClient(auth);
996
+ }
984
997
  function output(opts, data, render) {
985
998
  if (opts.json) {
986
999
  process.stdout.write(JSON.stringify(data, null, 2) + "\n");
@@ -1384,6 +1397,42 @@ function dispatchBody(workflow, opts) {
1384
1397
  }
1385
1398
  };
1386
1399
  }
1400
+ function parseVaultKind(kind) {
1401
+ if (kind === void 0 || kind === "secret" || kind === "variable") return kind ?? "secret";
1402
+ throw new ActionsCliError("invalid_argument", `--kind must be secret or variable, got "${kind}".`);
1403
+ }
1404
+ function vaultPath(path5, params) {
1405
+ const search = new URLSearchParams();
1406
+ for (const [key, value] of Object.entries(params)) {
1407
+ if (value !== void 0) search.set(key, value);
1408
+ }
1409
+ const query = search.toString();
1410
+ return query ? `${path5}?${query}` : path5;
1411
+ }
1412
+ function vaultSetBody(name, value, opts) {
1413
+ if (value === "") throw new ActionsCliError("invalid_argument", "The value is empty.");
1414
+ if (opts.revealable && opts.kind === "variable") {
1415
+ throw new ActionsCliError("invalid_argument", "--revealable applies to secrets; variables are always readable.");
1416
+ }
1417
+ return {
1418
+ name,
1419
+ kind: opts.kind,
1420
+ value,
1421
+ ...opts.revealable && { revealable: true },
1422
+ ...opts.description !== void 0 && { description: opts.description },
1423
+ ...opts.labels !== void 0 && opts.labels.length > 0 && { labels: opts.labels }
1424
+ };
1425
+ }
1426
+ function formatVaultRow(item) {
1427
+ const flags = [
1428
+ item.kind,
1429
+ ...item.revealable && item.kind === "secret" ? ["revealable"] : [],
1430
+ `v${item.version}`,
1431
+ ...item.source === "organization" ? ["inherited"] : [],
1432
+ ...item.overridesOrganization ? ["overrides org"] : []
1433
+ ];
1434
+ return `${pc.cyan(safeTerm(item.name))} ${pc.dim(flags.join(", "))} ${pc.dim(`updated ${safeTerm(item.updatedAt)}`)}`;
1435
+ }
1387
1436
  function registerActionsCommands(program2) {
1388
1437
  const actions = program2.command("actions").alias("ci").description("Drive the benchmarks-platform Actions API").configureOutput({ outputError: usageErrorOutput });
1389
1438
  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");
@@ -1714,6 +1763,74 @@ function registerActionsCommands(program2) {
1714
1763
  fail(e, opts);
1715
1764
  }
1716
1765
  });
1766
+ const vault = actions.command("vault").description("The org vault: secrets and variables (owner/admin key)");
1767
+ const vaultScope = (cmd) => common(cmd).option("--repo <owner/repo>", "scope to a repo's own items instead of the org's").option("--kind <kind>", "secret or variable", "secret");
1768
+ vaultScope(
1769
+ vault.command("ls").description("List names and metadata (never values)")
1770
+ ).action(async (opts) => {
1771
+ try {
1772
+ const path5 = vaultPath("/api/v1/vault", { repo: opts.repo, kind: parseVaultKind(opts.kind) });
1773
+ const data = await (await client(opts)).get(path5);
1774
+ output(opts, data, (d) => {
1775
+ for (const item of d.items) console.log(formatVaultRow(item));
1776
+ });
1777
+ } catch (e) {
1778
+ fail(e, opts);
1779
+ }
1780
+ });
1781
+ vaultScope(
1782
+ vault.command("set").description("Store a value, read from stdin (or --from-file) and sent exactly as read").argument("<name>", "item name, e.g. NPM_TOKEN").option("--from-file <path>", "read the value from a file instead of stdin").option("--revealable", "let the value be read back with `vault get` (secrets; fixed at creation)").option("--description <text>", "what the item is for").option("--labels <labels...>", "labels (fixed at creation)")
1783
+ ).action(async (name, opts) => {
1784
+ try {
1785
+ if (opts.fromFile === void 0 && process.stdin.isTTY) {
1786
+ throw new ActionsCliError(
1787
+ "invalid_argument",
1788
+ 'Pipe the value on stdin (e.g. printf %s "$VALUE" | compute actions vault set NAME) or pass --from-file.'
1789
+ );
1790
+ }
1791
+ const value = readFileSync(opts.fromFile ?? 0, "utf8");
1792
+ const kind = parseVaultKind(opts.kind);
1793
+ const body = vaultSetBody(name, value, { ...opts, kind });
1794
+ const result = await (await vaultValueClient(opts)).put(
1795
+ vaultPath("/api/v1/vault", { repo: opts.repo }),
1796
+ body
1797
+ );
1798
+ output(opts, result, (r) => {
1799
+ const where = opts.repo ? safeTerm(opts.repo) : "org";
1800
+ console.log(`saved ${pc.cyan(safeTerm(r.item.name))} ${pc.dim(`(${r.item.kind}, v${r.item.version}, ${where})`)}`);
1801
+ if (/\r?\n$/.test(value)) {
1802
+ console.error(pc.yellow("note: the stored value ends with a newline; use printf %s rather than echo to avoid one."));
1803
+ }
1804
+ });
1805
+ } catch (e) {
1806
+ fail(e, opts);
1807
+ }
1808
+ });
1809
+ vaultScope(
1810
+ vault.command("get").description("Print a value: any variable, or a secret created --revealable").argument("<name>", "item name")
1811
+ ).action(async (name, opts) => {
1812
+ try {
1813
+ const kind = parseVaultKind(opts.kind);
1814
+ const result = await (await vaultValueClient(opts)).post(
1815
+ vaultPath("/api/v1/vault/reveal", { repo: opts.repo }),
1816
+ { name, kind }
1817
+ );
1818
+ output(opts, result, (r) => process.stdout.write(r.value));
1819
+ } catch (e) {
1820
+ fail(e, opts);
1821
+ }
1822
+ });
1823
+ vaultScope(
1824
+ vault.command("rm").description("Delete an item (with --repo, only the repo's override)").argument("<name>", "item name")
1825
+ ).action(async (name, opts) => {
1826
+ try {
1827
+ const path5 = vaultPath("/api/v1/vault", { repo: opts.repo, kind: parseVaultKind(opts.kind), name });
1828
+ const result = await (await client(opts)).del(path5);
1829
+ output(opts, result, (r) => console.log(`deleted ${pc.cyan(safeTerm(r.name))} ${pc.dim(`(${r.kind})`)}`));
1830
+ } catch (e) {
1831
+ fail(e, opts);
1832
+ }
1833
+ });
1717
1834
  common(
1718
1835
  actions.command("cancel").description("Cancel a run").argument("<run-id>", "run ID")
1719
1836
  ).action(async (runId, opts) => {