@computesdk/cli 2.1.1 → 2.2.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/dist/index.js CHANGED
@@ -835,15 +835,15 @@ function maxPriceFields(maxBid, maxBidPer) {
835
835
  throw new ActionsCliError("invalid_argument", "--max-bid-per requires --max-bid.");
836
836
  }
837
837
  if (maxBid === void 0) return void 0;
838
- const usd = Number(maxBid);
839
- if (!/^\d+(\.\d+)?([eE][+-]?\d+)?$/.test(maxBid) || !Number.isFinite(usd) || usd <= 0) {
838
+ const usd2 = Number(maxBid);
839
+ if (!/^\d+(\.\d+)?([eE][+-]?\d+)?$/.test(maxBid) || !Number.isFinite(usd2) || usd2 <= 0) {
840
840
  throw new ActionsCliError("invalid_argument", `Invalid --max-bid "${maxBid}". Expected a positive dollar amount (e.g. 0.12).`);
841
841
  }
842
842
  const per = maxBidPer ?? "second";
843
843
  if (per !== "second" && per !== "minute" && per !== "hour") {
844
844
  throw new ActionsCliError("invalid_argument", `--max-bid-per must be second, minute, or hour, got "${maxBidPer}".`);
845
845
  }
846
- return { maxPriceUsd: usd, maxPricePer: per };
846
+ return { maxPriceUsd: usd2, maxPricePer: per };
847
847
  }
848
848
  function parseVaultKind(kind) {
849
849
  if (kind === void 0 || kind === "secret" || kind === "variable") return kind ?? "secret";
@@ -1377,8 +1377,8 @@ function parseRate(price, per) {
1377
1377
  if (price === void 0) {
1378
1378
  throw new ActionsCliError("invalid_argument", "A price is required \u2014 pass --price <usd>.");
1379
1379
  }
1380
- const usd = Number(price);
1381
- if (!USD_RE.test(price) || !Number.isFinite(usd) || usd <= 0) {
1380
+ const usd2 = Number(price);
1381
+ if (!USD_RE.test(price) || !Number.isFinite(usd2) || usd2 <= 0) {
1382
1382
  throw new ActionsCliError(
1383
1383
  "invalid_argument",
1384
1384
  `Invalid --price "${price}". Expected a positive dollar amount (e.g. 0.12).`
@@ -1391,7 +1391,7 @@ function parseRate(price, per) {
1391
1391
  `--per must be second, minute, or hour, got "${per}".`
1392
1392
  );
1393
1393
  }
1394
- return { usd, per: unit };
1394
+ return { usd: usd2, per: unit };
1395
1395
  }
1396
1396
  function parseCapacity(value) {
1397
1397
  if (value === void 0) return void 0;
@@ -1493,11 +1493,11 @@ function credentialBody(fields, values) {
1493
1493
  }
1494
1494
  return { fields: values };
1495
1495
  }
1496
- function formatUsd(usd) {
1497
- return `$${usd.toFixed(6).replace(/\.?0+$/, "")}`;
1496
+ function formatUsd(usd2) {
1497
+ return `$${usd2.toFixed(6).replace(/\.?0+$/, "")}`;
1498
1498
  }
1499
- function formatRate(usd, per) {
1500
- return `${formatUsd(usd)}/${per}`;
1499
+ function formatRate(usd2, per) {
1500
+ return `${formatUsd(usd2)}/${per}`;
1501
1501
  }
1502
1502
  function formatListingStatus(ask) {
1503
1503
  let label;
@@ -1843,6 +1843,33 @@ ${hints}`
1843
1843
 
1844
1844
  // src/sandboxes.ts
1845
1845
  import pc3 from "picocolors";
1846
+ function parseMaxPrice(value, per) {
1847
+ if (value === void 0) {
1848
+ if (per !== void 0) throw new Error("--max-price-per requires --max-price.");
1849
+ return void 0;
1850
+ }
1851
+ let usdText = value;
1852
+ let unit = per;
1853
+ const slash = value.indexOf("/");
1854
+ if (slash !== -1) {
1855
+ usdText = value.slice(0, slash);
1856
+ const inline = value.slice(slash + 1);
1857
+ if (per !== void 0 && per !== inline) {
1858
+ throw new Error(`--max-price "${value}" conflicts with --max-price-per "${per}".`);
1859
+ }
1860
+ unit = inline;
1861
+ }
1862
+ const usd2 = Number(usdText);
1863
+ if (!/^\d+(\.\d+)?([eE][+-]?\d+)?$/.test(usdText) || !Number.isFinite(usd2) || usd2 <= 0) {
1864
+ throw new Error(`Invalid --max-price "${value}". Expected a positive dollar amount like 0.12/hour.`);
1865
+ }
1866
+ if (unit !== "second" && unit !== "minute" && unit !== "hour") {
1867
+ throw new Error(
1868
+ "--max-price needs a unit \u2014 write it as <usd>/<unit> (e.g. --max-price 0.12/hour) or pass --max-price-per hour."
1869
+ );
1870
+ }
1871
+ return { usd: usd2, per: unit };
1872
+ }
1846
1873
  function shellQuote(arg) {
1847
1874
  return `'${arg.replace(/'/g, `'\\''`)}'`;
1848
1875
  }
@@ -1854,12 +1881,40 @@ function printSandbox(s) {
1854
1881
  console.log(` image: ${s.image ?? "\u2014"}${s.requestedImage !== null && s.requestedImage !== s.image ? ` ${pc3.dim(`(requested: ${s.requestedImage})`)}` : ""}`);
1855
1882
  }
1856
1883
  if (s.destroyError) console.log(` destroyError: ${pc3.red(s.destroyError)}`);
1884
+ if (s.placement) {
1885
+ const p2 = s.placement;
1886
+ const box = p2.box ? ` / ${p2.box.sizeName ?? "custom"}${p2.box.resources ? ` (${p2.box.resources.cpus ?? "?"} vCPU / ${p2.box.resources.memoryMb ?? "?"} MB)` : ""}` : "";
1887
+ console.log(
1888
+ ` provider: ${p2.box?.provider ?? s.provider ?? "\u2014"}${box} size: ${p2.size ?? "\u2014"} order: ${p2.orderType ?? "\u2014"} rate: $${p2.rate.usd}/${p2.rate.per}`
1889
+ );
1890
+ }
1857
1891
  if (attach !== void 0) {
1858
1892
  console.log(
1859
1893
  attach === null ? " attach: none (market fill or ambient provider \u2014 platform drive only)" : ` attach: ${attach.provider} / ${attach.providerSandboxId}${attach.region ? ` (${attach.region})` : ""}`
1860
1894
  );
1861
1895
  }
1862
1896
  }
1897
+ var usd = (n, suffix = "") => n === void 0 ? "\u2014" : `$${n.toFixed(4).replace(/0+$/, "").replace(/\.$/, ".00")}${suffix}`;
1898
+ function printQuote(q, timeoutMs) {
1899
+ console.log(`quote: ${q.ok ? pc3.green("ok") : pc3.red(q.reason ?? "unfillable")}`);
1900
+ const box = q.placement?.box;
1901
+ console.log(
1902
+ ` size: ${q.placement?.size ?? "\u2014"} provider: ${q.placement?.provider ?? "\u2014"}` + (box ? ` box: ${box.sizeName ?? "custom"} (${box.provider}${box.resources ? `, ${box.resources.cpus ?? "?"} vCPU / ${box.resources.memoryMb ?? "?"} MB` : ""})` : "")
1903
+ );
1904
+ console.log(` order type: ${q.orderType} order: ${q.providerOrder.join(", ")}`);
1905
+ if (q.rateUsd) {
1906
+ console.log(` rate: ${usd(q.rateUsd.perHour, "/hour")} cheapest live: ${usd(q.cheapestLiveUsdPerHour, "/hr")} reference: ${usd(q.referenceUsdPerHour, "/hr")}`);
1907
+ } else if (q.rate) {
1908
+ console.log(` rate: $${q.rate.usd}/${q.rate.per}`);
1909
+ }
1910
+ const timeout = timeoutMs !== void 0 ? ` for ${timeoutMs}ms` : "";
1911
+ console.log(` est. cost${timeout}: ${usd(q.estimatedCostUsd)} required hold: ${usd(q.requiredHoldUsd)}`);
1912
+ console.log(
1913
+ ` cap: ${q.maxPrice ? `$${q.maxPrice.usd}/${q.maxPrice.per}` : usd(q.marketCapUsdPerHour, "/hr")} protection limit: ${usd(q.protectionLimitUsdPerHour, "/hr")} balance: ${usd(q.creditBalanceUsd)}`
1914
+ );
1915
+ if (!q.ok && q.topUpPath) console.log(` ${pc3.dim(`\u2192 ${q.topUpPath}`)}`);
1916
+ if (q.liveAskDepth !== void 0) console.log(` ${pc3.dim(`live asks: ${q.liveAskDepth}`)}`);
1917
+ }
1863
1918
  function printProcess(p2) {
1864
1919
  const exit = p2.status === "exited" ? `exit=${p2.exitCode ?? "?"}${p2.signal ? ` (${p2.signal})` : ""}` : "";
1865
1920
  const stdin = p2.stdin && p2.status === "running" ? " [stdin open]" : "";
@@ -1948,7 +2003,7 @@ function mergeLeadOpts(opts, inline) {
1948
2003
  }
1949
2004
  function registerSandboxesCommands(program2) {
1950
2005
  const cmd = program2.command("sandboxes").alias("sbx").description("Create and drive sandboxes through the platform").enablePositionalOptions().configureOutput({ outputError: usageErrorOutput });
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) => {
2006
+ 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("--size <size>", "platform size: small (1 vCPU/2 GB), medium, large, xlarge (8/16 GB); default medium").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 (raw resources \u2014 mutually exclusive with --size)").option("--memory-mb <n>", "memory in MB (raw resources)").option("--disk-mb <n>", "ephemeral disk in MB (raw resources)").option("--max-price <usd/unit>", "max price for a market fill (e.g. 0.12/hour); makes the create a limit order").option("--max-price-per <unit>", "unit for a bare --max-price usd (second, minute, or hour)").option("--market", 'place the create on the compute market (order type "market")').option("--order-type <type>", "explicit market order type: market or limit").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) => {
1952
2007
  try {
1953
2008
  const c = await client(opts);
1954
2009
  const resources = {};
@@ -1957,18 +2012,55 @@ function registerSandboxesCommands(program2) {
1957
2012
  if (opts.diskMb) resources.ephemeralDiskMb = Number(opts.diskMb);
1958
2013
  const body = {};
1959
2014
  if (opts.order) body.providerOrder = opts.order.split(",").map((s) => s.trim());
2015
+ if (opts.size) body.size = opts.size;
1960
2016
  if (opts.label) body.label = opts.label;
1961
2017
  if (opts.image) body.image = opts.image;
1962
2018
  if (opts.snapshotId) body.snapshotId = opts.snapshotId;
1963
2019
  if (opts.timeoutMs) body.timeoutMs = Number(opts.timeoutMs);
1964
2020
  if (opts.secret && opts.secret.length > 0) body.secrets = opts.secret;
1965
2021
  if (Object.keys(resources).length > 0) body.resources = resources;
2022
+ const maxPrice = parseMaxPrice(opts.maxPrice, opts.maxPricePer);
2023
+ if (maxPrice) body.maxPrice = { usd: maxPrice.usd, per: maxPrice.per };
2024
+ if (opts.orderType && opts.market) {
2025
+ throw new Error("--market and --order-type are mutually exclusive.");
2026
+ }
2027
+ if (opts.orderType) body.orderType = opts.orderType;
2028
+ if (opts.market) body.orderType = "market";
1966
2029
  const res = await c.post("/api/v1/sandboxes", body);
1967
2030
  output(opts, res.sandbox, printSandbox);
1968
2031
  } catch (e) {
1969
2032
  fail(e, opts);
1970
2033
  }
1971
2034
  });
2035
+ cmd.command("quote").description("Quote a sandbox create: placement, rate, caps and credits \u2014 creates nothing").option("--size <size>", "platform size (or raw resource flags; default medium)").option("--cpus <n>", "CPU cores").option("--memory-mb <n>", "memory in MB").option("--disk-mb <n>", "ephemeral disk in MB").option("--region <region>", "pin the placement region").option("--timeout-ms <ms>", "sandbox timeout in ms").option("--order-type <type>", "market order type: market or limit").option("--market", "quote a market order (fills at the live price, protection-bounded)").option("--max-price <usd/unit>", "max price for a market fill (e.g. 0.12/hour)").option("--max-price-per <unit>", "unit for a bare --max-price usd (second, minute, or hour)").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (opts) => {
2036
+ try {
2037
+ const c = await client(opts);
2038
+ const params = {
2039
+ size: opts.size,
2040
+ cpus: opts.cpus,
2041
+ memoryMb: opts.memoryMb,
2042
+ ephemeralDiskMb: opts.diskMb,
2043
+ region: opts.region,
2044
+ timeoutMs: opts.timeoutMs,
2045
+ orderType: opts.market ? "market" : opts.orderType
2046
+ };
2047
+ if (opts.orderType && opts.market) {
2048
+ throw new Error("--market and --order-type are mutually exclusive.");
2049
+ }
2050
+ const maxPrice = parseMaxPrice(opts.maxPrice, opts.maxPricePer);
2051
+ if (maxPrice) {
2052
+ params.maxPriceUsd = String(maxPrice.usd);
2053
+ params.maxPricePer = maxPrice.per;
2054
+ }
2055
+ const res = await c.get(
2056
+ "/api/v1/sandboxes/quote",
2057
+ params
2058
+ );
2059
+ output(opts, res.quote, (quote) => printQuote(quote, opts.timeoutMs));
2060
+ } catch (e) {
2061
+ fail(e, opts);
2062
+ }
2063
+ });
1972
2064
  cmd.command("list").description("List sandboxes").option("--status <status>", "creating | running | destroyed").option("--limit <n>", "page size").option("--cursor <cursor>", "page cursor from a previous response").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (opts) => {
1973
2065
  try {
1974
2066
  const c = await client(opts);