@computesdk/cli 2.1.1 → 2.3.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";
@@ -1373,25 +1373,55 @@ function registerActionsCommands(program2) {
1373
1373
  import pc2 from "picocolors";
1374
1374
  var USD_RE = /^\d+(\.\d+)?([eE][+-]?\d+)?$/;
1375
1375
  var RATE_UNITS = ["second", "minute", "hour"];
1376
+ var MARKET_LANES = ["actions", "sandbox"];
1377
+ function parseMarketLane(value) {
1378
+ if (value === void 0) return "actions";
1379
+ if (!MARKET_LANES.includes(value)) {
1380
+ throw new ActionsCliError(
1381
+ "invalid_argument",
1382
+ `--use-case must be ${MARKET_LANES.join(" or ")}, got "${value}".`
1383
+ );
1384
+ }
1385
+ return value;
1386
+ }
1376
1387
  function parseRate(price, per) {
1377
1388
  if (price === void 0) {
1378
- throw new ActionsCliError("invalid_argument", "A price is required \u2014 pass --price <usd>.");
1389
+ throw new ActionsCliError("invalid_argument", "A price is required \u2014 pass --price <usd>/<unit>.");
1390
+ }
1391
+ let usdText = price;
1392
+ let unit = per;
1393
+ const slash = price.indexOf("/");
1394
+ if (slash !== -1) {
1395
+ usdText = price.slice(0, slash);
1396
+ const inline = price.slice(slash + 1);
1397
+ if (per !== void 0 && per !== inline) {
1398
+ throw new ActionsCliError(
1399
+ "invalid_argument",
1400
+ `--price "${price}" conflicts with --per "${per}".`
1401
+ );
1402
+ }
1403
+ unit = inline;
1404
+ }
1405
+ const usd2 = Number(usdText);
1406
+ if (!USD_RE.test(usdText) || !Number.isFinite(usd2) || usd2 <= 0) {
1407
+ throw new ActionsCliError(
1408
+ "invalid_argument",
1409
+ `Invalid --price "${price}". Expected a positive dollar amount (e.g. 0.12/hour).`
1410
+ );
1379
1411
  }
1380
- const usd = Number(price);
1381
- if (!USD_RE.test(price) || !Number.isFinite(usd) || usd <= 0) {
1412
+ if (unit === void 0) {
1382
1413
  throw new ActionsCliError(
1383
1414
  "invalid_argument",
1384
- `Invalid --price "${price}". Expected a positive dollar amount (e.g. 0.12).`
1415
+ "--price needs a unit \u2014 write it as <usd>/<unit> (e.g. --price 0.12/hour) or pass --per hour."
1385
1416
  );
1386
1417
  }
1387
- const unit = per ?? "second";
1388
1418
  if (!RATE_UNITS.includes(unit)) {
1389
1419
  throw new ActionsCliError(
1390
1420
  "invalid_argument",
1391
- `--per must be second, minute, or hour, got "${per}".`
1421
+ `The unit must be second, minute, or hour, got "${unit}".`
1392
1422
  );
1393
1423
  }
1394
- return { usd, per: unit };
1424
+ return { usd: usd2, per: unit };
1395
1425
  }
1396
1426
  function parseCapacity(value) {
1397
1427
  if (value === void 0) return void 0;
@@ -1427,6 +1457,7 @@ function sellBody(opts) {
1427
1457
  }
1428
1458
  return {
1429
1459
  size: opts.size,
1460
+ useCase: parseMarketLane(opts.useCase),
1430
1461
  ...opts.region !== void 0 && { region: opts.region },
1431
1462
  usd: rate.usd,
1432
1463
  per: rate.per,
@@ -1493,11 +1524,11 @@ function credentialBody(fields, values) {
1493
1524
  }
1494
1525
  return { fields: values };
1495
1526
  }
1496
- function formatUsd(usd) {
1497
- return `$${usd.toFixed(6).replace(/\.?0+$/, "")}`;
1527
+ function formatUsd(usd2) {
1528
+ return `$${usd2.toFixed(6).replace(/\.?0+$/, "")}`;
1498
1529
  }
1499
- function formatRate(usd, per) {
1500
- return `${formatUsd(usd)}/${per}`;
1530
+ function formatRate(usd2, per) {
1531
+ return `${formatUsd(usd2)}/${per}`;
1501
1532
  }
1502
1533
  function formatListingStatus(ask) {
1503
1534
  let label;
@@ -1515,6 +1546,7 @@ function formatListingRow(ask) {
1515
1546
  return [
1516
1547
  ask.id,
1517
1548
  formatListingStatus(ask),
1549
+ pc2.dim(ask.useCase === "sandbox" ? "sandbox" : "actions"),
1518
1550
  safeTerm(ask.size),
1519
1551
  safeTerm(ask.region ?? "-"),
1520
1552
  formatRate(ask.usd, ask.per),
@@ -1530,6 +1562,9 @@ function formatProviderStatus(p2) {
1530
1562
  `credential: ${p2.executorCredentialConnected ? pc2.green("connected") : pc2.yellow("not connected \u2014 your listings cannot fill until `compute market credential connect`")}`
1531
1563
  );
1532
1564
  if (p2.revokedAt) lines.push(pc2.red(`revoked: ${p2.revokedAt}`));
1565
+ lines.push(
1566
+ `lanes: ${(p2.useCases ?? ["actions"]).join(", ")}`
1567
+ );
1533
1568
  lines.push(
1534
1569
  `sizes: ${p2.sizes.map((s) => `${s.name} (${s.label})`).join(", ") || "-"}`
1535
1570
  );
@@ -1544,13 +1579,14 @@ function formatProviderStatus(p2) {
1544
1579
  }
1545
1580
  function formatBook(book) {
1546
1581
  const lines = [];
1582
+ const lane = (useCase) => pc2.dim(useCase === "sandbox" ? "sandbox" : "actions");
1547
1583
  lines.push(pc2.bold("sellers' asking prices"));
1548
1584
  if (book.asks.length === 0) lines.push(pc2.dim(" no listings are live"));
1549
1585
  for (const ask of book.asks) {
1550
1586
  const where = ask.region ? safeTerm(ask.region) : "any region";
1551
1587
  const expiry = ask.expiresAt ? pc2.dim(ask.rollover ? ` renews ${ask.expiresAt}` : ` until ${ask.expiresAt}`) : "";
1552
1588
  lines.push(
1553
- ` ${formatRate(ask.usd, ask.per)} ${safeTerm(ask.providerName)} ${safeTerm(ask.size)} ${where} up to ${ask.maxConcurrent} at once${expiry}`
1589
+ ` ${lane(ask.useCase)} ${formatRate(ask.usd, ask.per)} ${safeTerm(ask.providerName)} ${safeTerm(ask.size)} ${where} up to ${ask.maxConcurrent} at once${expiry}`
1554
1590
  );
1555
1591
  }
1556
1592
  lines.push("");
@@ -1559,7 +1595,7 @@ function formatBook(book) {
1559
1595
  for (const bid of book.bids) {
1560
1596
  const where = bid.region ? safeTerm(bid.region) : "anywhere";
1561
1597
  lines.push(
1562
- ` pays up to ${formatRate(bid.maxUsd, bid.per)} ${safeTerm(bid.size)} ${where} ${pc2.dim(`posted ${bid.createdAt}`)}`
1598
+ ` ${lane(bid.useCase)} pays up to ${formatRate(bid.maxUsd, bid.per)} ${safeTerm(bid.size)} ${where} ${pc2.dim(`posted ${bid.createdAt}`)}`
1563
1599
  );
1564
1600
  }
1565
1601
  lines.push("");
@@ -1568,7 +1604,7 @@ function formatBook(book) {
1568
1604
  for (const fill of book.fills) {
1569
1605
  const statusNote = fill.status === "replaced" ? pc2.yellow(" replaced") : fill.status === "closed" ? pc2.gray(" closed") : "";
1570
1606
  lines.push(
1571
- ` ${pc2.dim(fill.id)} ${formatRate(fill.usd, fill.per)} ${safeTerm(fill.provider)} ${safeTerm(fill.size)} ${safeTerm(fill.region ?? "-")} ${pc2.dim(fill.createdAt)}${statusNote}`
1607
+ ` ${pc2.dim(fill.id)} ${lane(fill.useCase)} ${formatRate(fill.usd, fill.per)} ${safeTerm(fill.provider)} ${safeTerm(fill.size)} ${safeTerm(fill.region ?? "-")} ${pc2.dim(fill.createdAt)}${statusNote}`
1572
1608
  );
1573
1609
  }
1574
1610
  return lines.join("\n");
@@ -1629,10 +1665,11 @@ function registerMarketCommands(program2) {
1629
1665
  }
1630
1666
  });
1631
1667
  common(
1632
- market.command("sell").description("Post a listing: sell capacity at a price").requiredOption("--price <usd>", "price per unit of time (e.g. 0.12)").option("--per <unit>", "time unit the price is per: second, minute, or hour (default: second)").option("--size <tier>", "size tier to sell (default: medium, else the provider's first tier)").option("--region <region>", "region to sell in (default: anywhere the provider runs)").option("--capacity <n>", "max simultaneous fills (default: 1)").option("--expires-in <hours>", "delist after this many hours (default: standing)").option("--renew", "re-list for the same window each time it expires (requires --expires-in)")
1668
+ market.command("sell").description("Post a listing: sell capacity at a price").requiredOption("--price <usd/unit>", "price per unit of time (e.g. 0.12/hour)").option("--per <unit>", "unit for a bare --price usd (second, minute, or hour)").option("--size <tier>", "size tier to sell (default: medium, else the provider's first tier)").option("--use-case <lane>", "lane to sell into: actions or sandbox (default: actions)").option("--region <region>", "region to sell in (default: anywhere the provider runs)").option("--capacity <n>", "max simultaneous fills (default: 1)").option("--expires-in <hours>", "delist after this many hours (default: standing)").option("--renew", "re-list for the same window each time it expires (requires --expires-in)")
1633
1669
  ).action(async (opts) => {
1634
1670
  try {
1635
1671
  const c = await client(opts);
1672
+ const useCase = parseMarketLane(opts.useCase);
1636
1673
  let size = opts.size;
1637
1674
  if (size === void 0) {
1638
1675
  const { provider } = await c.get(
@@ -1650,7 +1687,7 @@ function registerMarketCommands(program2) {
1650
1687
  }
1651
1688
  const result = await c.post(
1652
1689
  "/api/v1/market/asks",
1653
- sellBody({ ...opts, size })
1690
+ sellBody({ ...opts, size, useCase })
1654
1691
  );
1655
1692
  output(opts, result, (r) => {
1656
1693
  console.log(`listed ${pc2.cyan(r.ask.id)}`);
@@ -1661,7 +1698,7 @@ function registerMarketCommands(program2) {
1661
1698
  }
1662
1699
  });
1663
1700
  common(
1664
- market.command("price").description("Reprice a listing").argument("<listing-id>", "listing ID").requiredOption("--price <usd>", "new price per unit of time (e.g. 0.08)").option("--per <unit>", "time unit the price is per: second, minute, or hour (default: second)")
1701
+ market.command("price").description("Reprice a listing").argument("<listing-id>", "listing ID").requiredOption("--price <usd/unit>", "new price per unit of time (e.g. 0.08/hour)").option("--per <unit>", "unit for a bare --price usd (second, minute, or hour)")
1665
1702
  ).action(async (listingId, opts) => {
1666
1703
  try {
1667
1704
  const result = await (await client(opts)).post(
@@ -1742,11 +1779,12 @@ function registerMarketCommands(program2) {
1742
1779
  }
1743
1780
  );
1744
1781
  common(
1745
- market.command("book").description("Live prices: sellers' asking prices, buyer offers, recent sales")
1782
+ market.command("book").description("Live prices: sellers' asking prices, buyer offers, recent sales").option("--use-case <lane>", "only this lane: actions or sandbox (default: both)")
1746
1783
  ).action(async (opts) => {
1747
1784
  try {
1785
+ const lane = opts.useCase === void 0 ? void 0 : parseMarketLane(opts.useCase);
1748
1786
  const book = await (await client(opts)).get(
1749
- "/api/v1/market/book"
1787
+ `/api/v1/market/book${lane ? `?useCase=${lane}` : ""}`
1750
1788
  );
1751
1789
  output(opts, book, (b) => console.log(formatBook(b)));
1752
1790
  } catch (e) {
@@ -1843,6 +1881,33 @@ ${hints}`
1843
1881
 
1844
1882
  // src/sandboxes.ts
1845
1883
  import pc3 from "picocolors";
1884
+ function parseMaxPrice(value, per) {
1885
+ if (value === void 0) {
1886
+ if (per !== void 0) throw new Error("--max-price-per requires --max-price.");
1887
+ return void 0;
1888
+ }
1889
+ let usdText = value;
1890
+ let unit = per;
1891
+ const slash = value.indexOf("/");
1892
+ if (slash !== -1) {
1893
+ usdText = value.slice(0, slash);
1894
+ const inline = value.slice(slash + 1);
1895
+ if (per !== void 0 && per !== inline) {
1896
+ throw new Error(`--max-price "${value}" conflicts with --max-price-per "${per}".`);
1897
+ }
1898
+ unit = inline;
1899
+ }
1900
+ const usd2 = Number(usdText);
1901
+ if (!/^\d+(\.\d+)?([eE][+-]?\d+)?$/.test(usdText) || !Number.isFinite(usd2) || usd2 <= 0) {
1902
+ throw new Error(`Invalid --max-price "${value}". Expected a positive dollar amount like 0.12/hour.`);
1903
+ }
1904
+ if (unit !== "second" && unit !== "minute" && unit !== "hour") {
1905
+ throw new Error(
1906
+ "--max-price needs a unit \u2014 write it as <usd>/<unit> (e.g. --max-price 0.12/hour) or pass --max-price-per hour."
1907
+ );
1908
+ }
1909
+ return { usd: usd2, per: unit };
1910
+ }
1846
1911
  function shellQuote(arg) {
1847
1912
  return `'${arg.replace(/'/g, `'\\''`)}'`;
1848
1913
  }
@@ -1854,12 +1919,40 @@ function printSandbox(s) {
1854
1919
  console.log(` image: ${s.image ?? "\u2014"}${s.requestedImage !== null && s.requestedImage !== s.image ? ` ${pc3.dim(`(requested: ${s.requestedImage})`)}` : ""}`);
1855
1920
  }
1856
1921
  if (s.destroyError) console.log(` destroyError: ${pc3.red(s.destroyError)}`);
1922
+ if (s.placement) {
1923
+ const p2 = s.placement;
1924
+ const box = p2.box ? ` / ${p2.box.sizeName ?? "custom"}${p2.box.resources ? ` (${p2.box.resources.cpus ?? "?"} vCPU / ${p2.box.resources.memoryMb ?? "?"} MB)` : ""}` : "";
1925
+ console.log(
1926
+ ` provider: ${p2.box?.provider ?? s.provider ?? "\u2014"}${box} size: ${p2.size ?? "\u2014"} order: ${p2.orderType ?? "\u2014"} rate: $${p2.rate.usd}/${p2.rate.per}`
1927
+ );
1928
+ }
1857
1929
  if (attach !== void 0) {
1858
1930
  console.log(
1859
1931
  attach === null ? " attach: none (market fill or ambient provider \u2014 platform drive only)" : ` attach: ${attach.provider} / ${attach.providerSandboxId}${attach.region ? ` (${attach.region})` : ""}`
1860
1932
  );
1861
1933
  }
1862
1934
  }
1935
+ var usd = (n, suffix = "") => n === void 0 ? "\u2014" : `$${n.toFixed(4).replace(/0+$/, "").replace(/\.$/, ".00")}${suffix}`;
1936
+ function printQuote(q, timeoutMs) {
1937
+ console.log(`quote: ${q.ok ? pc3.green("ok") : pc3.red(q.reason ?? "unfillable")}`);
1938
+ const box = q.placement?.box;
1939
+ console.log(
1940
+ ` 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` : ""})` : "")
1941
+ );
1942
+ console.log(` order type: ${q.orderType} order: ${q.providerOrder.join(", ")}`);
1943
+ if (q.rateUsd) {
1944
+ console.log(` rate: ${usd(q.rateUsd.perHour, "/hour")} cheapest live: ${usd(q.cheapestLiveUsdPerHour, "/hr")} reference: ${usd(q.referenceUsdPerHour, "/hr")}`);
1945
+ } else if (q.rate) {
1946
+ console.log(` rate: $${q.rate.usd}/${q.rate.per}`);
1947
+ }
1948
+ const timeout = timeoutMs !== void 0 ? ` for ${timeoutMs}ms` : "";
1949
+ console.log(` est. cost${timeout}: ${usd(q.estimatedCostUsd)} required hold: ${usd(q.requiredHoldUsd)}`);
1950
+ console.log(
1951
+ ` cap: ${q.maxPrice ? `$${q.maxPrice.usd}/${q.maxPrice.per}` : usd(q.marketCapUsdPerHour, "/hr")} protection limit: ${usd(q.protectionLimitUsdPerHour, "/hr")} balance: ${usd(q.creditBalanceUsd)}`
1952
+ );
1953
+ if (!q.ok && q.topUpPath) console.log(` ${pc3.dim(`\u2192 ${q.topUpPath}`)}`);
1954
+ if (q.liveAskDepth !== void 0) console.log(` ${pc3.dim(`live asks: ${q.liveAskDepth}`)}`);
1955
+ }
1863
1956
  function printProcess(p2) {
1864
1957
  const exit = p2.status === "exited" ? `exit=${p2.exitCode ?? "?"}${p2.signal ? ` (${p2.signal})` : ""}` : "";
1865
1958
  const stdin = p2.stdin && p2.status === "running" ? " [stdin open]" : "";
@@ -1948,7 +2041,7 @@ function mergeLeadOpts(opts, inline) {
1948
2041
  }
1949
2042
  function registerSandboxesCommands(program2) {
1950
2043
  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) => {
2044
+ 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
2045
  try {
1953
2046
  const c = await client(opts);
1954
2047
  const resources = {};
@@ -1957,18 +2050,55 @@ function registerSandboxesCommands(program2) {
1957
2050
  if (opts.diskMb) resources.ephemeralDiskMb = Number(opts.diskMb);
1958
2051
  const body = {};
1959
2052
  if (opts.order) body.providerOrder = opts.order.split(",").map((s) => s.trim());
2053
+ if (opts.size) body.size = opts.size;
1960
2054
  if (opts.label) body.label = opts.label;
1961
2055
  if (opts.image) body.image = opts.image;
1962
2056
  if (opts.snapshotId) body.snapshotId = opts.snapshotId;
1963
2057
  if (opts.timeoutMs) body.timeoutMs = Number(opts.timeoutMs);
1964
2058
  if (opts.secret && opts.secret.length > 0) body.secrets = opts.secret;
1965
2059
  if (Object.keys(resources).length > 0) body.resources = resources;
2060
+ const maxPrice = parseMaxPrice(opts.maxPrice, opts.maxPricePer);
2061
+ if (maxPrice) body.maxPrice = { usd: maxPrice.usd, per: maxPrice.per };
2062
+ if (opts.orderType && opts.market) {
2063
+ throw new Error("--market and --order-type are mutually exclusive.");
2064
+ }
2065
+ if (opts.orderType) body.orderType = opts.orderType;
2066
+ if (opts.market) body.orderType = "market";
1966
2067
  const res = await c.post("/api/v1/sandboxes", body);
1967
2068
  output(opts, res.sandbox, printSandbox);
1968
2069
  } catch (e) {
1969
2070
  fail(e, opts);
1970
2071
  }
1971
2072
  });
2073
+ 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) => {
2074
+ try {
2075
+ const c = await client(opts);
2076
+ const params = {
2077
+ size: opts.size,
2078
+ cpus: opts.cpus,
2079
+ memoryMb: opts.memoryMb,
2080
+ ephemeralDiskMb: opts.diskMb,
2081
+ region: opts.region,
2082
+ timeoutMs: opts.timeoutMs,
2083
+ orderType: opts.market ? "market" : opts.orderType
2084
+ };
2085
+ if (opts.orderType && opts.market) {
2086
+ throw new Error("--market and --order-type are mutually exclusive.");
2087
+ }
2088
+ const maxPrice = parseMaxPrice(opts.maxPrice, opts.maxPricePer);
2089
+ if (maxPrice) {
2090
+ params.maxPriceUsd = String(maxPrice.usd);
2091
+ params.maxPricePer = maxPrice.per;
2092
+ }
2093
+ const res = await c.get(
2094
+ "/api/v1/sandboxes/quote",
2095
+ params
2096
+ );
2097
+ output(opts, res.quote, (quote) => printQuote(quote, opts.timeoutMs));
2098
+ } catch (e) {
2099
+ fail(e, opts);
2100
+ }
2101
+ });
1972
2102
  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
2103
  try {
1974
2104
  const c = await client(opts);