sandbox 3.0.0-beta.23 → 3.0.0-beta.25

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.
@@ -2998,9 +2998,9 @@ var require_cjs = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/cmd-t
2998
2998
 
2999
2999
  //#endregion
3000
3000
  //#region src/args/runtime.ts
3001
- var import_cjs$27 = /* @__PURE__ */ __toESM(require_cjs());
3001
+ var import_cjs$28 = /* @__PURE__ */ __toESM(require_cjs());
3002
3002
  const runtimeType = {
3003
- ...import_cjs$27.oneOf([
3003
+ ...import_cjs$28.oneOf([
3004
3004
  "node22",
3005
3005
  "node24",
3006
3006
  "node26",
@@ -3008,7 +3008,7 @@ const runtimeType = {
3008
3008
  ]),
3009
3009
  displayName: "runtime"
3010
3010
  };
3011
- const runtime = import_cjs$27.option({
3011
+ const runtime = import_cjs$28.option({
3012
3012
  long: "runtime",
3013
3013
  type: runtimeType,
3014
3014
  defaultValue: () => "node24",
@@ -3136,9 +3136,9 @@ var require_ms = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/ms@2.1
3136
3136
 
3137
3137
  //#endregion
3138
3138
  //#region src/types/duration.ts
3139
- var import_cjs$26 = require_cjs();
3139
+ var import_cjs$27 = require_cjs();
3140
3140
  init_source();
3141
- const Duration = (0, import_cjs$26.extendType)(import_cjs$26.string, {
3141
+ const Duration = (0, import_cjs$27.extendType)(import_cjs$27.string, {
3142
3142
  displayName: "num UNIT",
3143
3143
  description: "A duration, e.g. 5m, 10s, 1h",
3144
3144
  async from(string$2) {
@@ -3154,8 +3154,8 @@ const Duration = (0, import_cjs$26.extendType)(import_cjs$26.string, {
3154
3154
 
3155
3155
  //#endregion
3156
3156
  //#region src/args/timeout.ts
3157
- var import_cjs$25 = /* @__PURE__ */ __toESM(require_cjs());
3158
- const timeout = import_cjs$25.option({
3157
+ var import_cjs$26 = /* @__PURE__ */ __toESM(require_cjs());
3158
+ const timeout = import_cjs$26.option({
3159
3159
  long: "timeout",
3160
3160
  type: Duration,
3161
3161
  description: "The maximum duration a sandbox can run for. Example: 5m, 30m",
@@ -3165,17 +3165,17 @@ const timeout = import_cjs$25.option({
3165
3165
 
3166
3166
  //#endregion
3167
3167
  //#region src/args/vcpus.ts
3168
- var import_cjs$24 = /* @__PURE__ */ __toESM(require_cjs());
3169
- const vcpusType = import_cjs$24.extendType(import_cjs$24.number, {
3168
+ var import_cjs$25 = /* @__PURE__ */ __toESM(require_cjs());
3169
+ const vcpusType = import_cjs$25.extendType(import_cjs$25.number, {
3170
3170
  displayName: "COUNT",
3171
3171
  async from(n) {
3172
3172
  if (!Number.isInteger(n) || n < 1) throw new Error(`Invalid vCPU count: ${n}. Must be a positive integer.`);
3173
3173
  return n;
3174
3174
  }
3175
3175
  });
3176
- const vcpus = import_cjs$24.option({
3176
+ const vcpus = import_cjs$25.option({
3177
3177
  long: "vcpus",
3178
- type: import_cjs$24.optional(vcpusType),
3178
+ type: import_cjs$25.optional(vcpusType),
3179
3179
  description: "Number of vCPUs to allocate (each vCPU includes 2048 MB of memory)"
3180
3180
  });
3181
3181
 
@@ -6754,10 +6754,10 @@ function _usingCtx() {
6754
6754
 
6755
6755
  //#endregion
6756
6756
  //#region src/commands/login.ts
6757
- var import_cjs$23 = /* @__PURE__ */ __toESM(require_cjs());
6757
+ var import_cjs$24 = /* @__PURE__ */ __toESM(require_cjs());
6758
6758
  init_source();
6759
6759
  const debug$6 = createDebugger("sandbox:login");
6760
- const login = import_cjs$23.command({
6760
+ const login = import_cjs$24.command({
6761
6761
  name: "login",
6762
6762
  description: "Log in to the Sandbox CLI",
6763
6763
  args: {},
@@ -6975,7 +6975,7 @@ var require_dist = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/@ver
6975
6975
 
6976
6976
  //#endregion
6977
6977
  //#region src/args/auth.ts
6978
- var import_cjs$22 = /* @__PURE__ */ __toESM(require_cjs());
6978
+ var import_cjs$23 = /* @__PURE__ */ __toESM(require_cjs());
6979
6979
  init_source();
6980
6980
  var import_dist = require_dist();
6981
6981
  const debug$5 = createDebugger("sandbox:args:auth");
@@ -6992,11 +6992,11 @@ function isTokenFresh() {
6992
6992
  function markTokenAsFresh() {
6993
6993
  freshTokenAcquiredAt = Date.now();
6994
6994
  }
6995
- const token = import_cjs$22.option({
6995
+ const token = import_cjs$23.option({
6996
6996
  long: "token",
6997
6997
  description: "A Vercel authentication token. If not provided, will use the token stored in your system from `VERCEL_AUTH_TOKEN` or will start a log in process.",
6998
6998
  type: {
6999
- ...import_cjs$22.string,
6999
+ ...import_cjs$23.string,
7000
7000
  displayName: "pat_or_oidc",
7001
7001
  defaultValueIsSerializable: false,
7002
7002
  onMissing: async () => {
@@ -7179,29 +7179,29 @@ function getAuthFailureStatus(error) {
7179
7179
 
7180
7180
  //#endregion
7181
7181
  //#region src/args/scope.ts
7182
- var import_cjs$21 = /* @__PURE__ */ __toESM(require_cjs());
7182
+ var import_cjs$22 = /* @__PURE__ */ __toESM(require_cjs());
7183
7183
  init_source();
7184
- const project = import_cjs$21.option({
7184
+ const project = import_cjs$22.option({
7185
7185
  long: "project",
7186
7186
  type: {
7187
- ...import_cjs$21.optional(import_cjs$21.string),
7187
+ ...import_cjs$22.optional(import_cjs$22.string),
7188
7188
  displayName: "my-project"
7189
7189
  },
7190
7190
  description: "The project name or ID to associate with the command. Can be inferred from VERCEL_OIDC_TOKEN."
7191
7191
  });
7192
7192
  /** Parser for the `--team` option. */
7193
- const teamParser = import_cjs$21.option({
7193
+ const teamParser = import_cjs$22.option({
7194
7194
  long: "team",
7195
7195
  type: {
7196
- ...import_cjs$21.optional(import_cjs$21.string),
7196
+ ...import_cjs$22.optional(import_cjs$22.string),
7197
7197
  displayName: "my-team"
7198
7198
  }
7199
7199
  });
7200
7200
  /** Parser for the `--scope` option. */
7201
- const scopeParser = import_cjs$21.option({
7201
+ const scopeParser = import_cjs$22.option({
7202
7202
  long: "scope",
7203
7203
  type: {
7204
- ...import_cjs$21.optional(import_cjs$21.string),
7204
+ ...import_cjs$22.optional(import_cjs$22.string),
7205
7205
  displayName: "my-team"
7206
7206
  },
7207
7207
  description: "The scope/team to associate with the command. Can be inferred from VERCEL_OIDC_TOKEN. [alias: --team]"
@@ -7290,7 +7290,7 @@ const scope = {
7290
7290
 
7291
7291
  //#endregion
7292
7292
  //#region package.json
7293
- var version = "3.0.0-beta.23";
7293
+ var version = "3.0.0-beta.25";
7294
7294
 
7295
7295
  //#endregion
7296
7296
  //#region src/error.ts
@@ -7332,6 +7332,10 @@ const snapshotClient = {
7332
7332
  fromSandbox: (name, opts) => withErrorHandling(() => Snapshot.fromSandbox(name, {
7333
7333
  fetch: fetchWithUserAgent,
7334
7334
  ...opts
7335
+ })),
7336
+ tree: (params) => withErrorHandling(() => Snapshot.tree({
7337
+ fetch: fetchWithUserAgent,
7338
+ ...params
7335
7339
  }))
7336
7340
  };
7337
7341
  const fetchWithUserAgent = (input, init$1) => {
@@ -7385,9 +7389,9 @@ async function writeResponseToTemp({ response, text }) {
7385
7389
 
7386
7390
  //#endregion
7387
7391
  //#region src/args/snapshot-id.ts
7388
- var import_cjs$20 = /* @__PURE__ */ __toESM(require_cjs());
7392
+ var import_cjs$21 = /* @__PURE__ */ __toESM(require_cjs());
7389
7393
  init_source();
7390
- const snapshotId = import_cjs$20.extendType(import_cjs$20.string, {
7394
+ const snapshotId = import_cjs$21.extendType(import_cjs$21.string, {
7391
7395
  displayName: "snapshot_id",
7392
7396
  description: "The ID of the snapshot",
7393
7397
  async from(s$1) {
@@ -7398,9 +7402,9 @@ const snapshotId = import_cjs$20.extendType(import_cjs$20.string, {
7398
7402
 
7399
7403
  //#endregion
7400
7404
  //#region src/args/sandbox-name.ts
7401
- var import_cjs$19 = /* @__PURE__ */ __toESM(require_cjs());
7405
+ var import_cjs$20 = /* @__PURE__ */ __toESM(require_cjs());
7402
7406
  init_source();
7403
- const sandboxName = import_cjs$19.extendType(import_cjs$19.string, {
7407
+ const sandboxName = import_cjs$20.extendType(import_cjs$20.string, {
7404
7408
  displayName: "name",
7405
7409
  description: "The name of the sandbox",
7406
7410
  async from(s$1) {
@@ -11405,9 +11409,9 @@ function getStderrStream() {
11405
11409
 
11406
11410
  //#endregion
11407
11411
  //#region src/args/key-value-pair.ts
11408
- var import_cjs$18 = /* @__PURE__ */ __toESM(require_cjs());
11412
+ var import_cjs$19 = /* @__PURE__ */ __toESM(require_cjs());
11409
11413
  init_source();
11410
- const KeyValuePair = import_cjs$18.extendType(import_cjs$18.string, {
11414
+ const KeyValuePair = import_cjs$19.extendType(import_cjs$19.string, {
11411
11415
  displayName: "key=value",
11412
11416
  async from(input) {
11413
11417
  if (!input.includes("=")) return {
@@ -11421,7 +11425,7 @@ const KeyValuePair = import_cjs$18.extendType(import_cjs$18.string, {
11421
11425
  };
11422
11426
  }
11423
11427
  });
11424
- const ObjectFromKeyValue = import_cjs$18.extendType(import_cjs$18.array(KeyValuePair), { async from(input) {
11428
+ const ObjectFromKeyValue = import_cjs$19.extendType(import_cjs$19.array(KeyValuePair), { async from(input) {
11425
11429
  const obj = Object.create(null);
11426
11430
  const missingVars = [];
11427
11431
  for (const { key, value } of input) if (value === void 0) missingVars.push(key);
@@ -11435,27 +11439,27 @@ const ObjectFromKeyValue = import_cjs$18.extendType(import_cjs$18.array(KeyValue
11435
11439
 
11436
11440
  //#endregion
11437
11441
  //#region src/commands/exec.ts
11438
- var import_cjs$17 = /* @__PURE__ */ __toESM(require_cjs());
11442
+ var import_cjs$18 = /* @__PURE__ */ __toESM(require_cjs());
11439
11443
  init_source();
11440
11444
  const args$3 = {
11441
- sandbox: import_cjs$17.positional({ type: sandboxName }),
11442
- command: import_cjs$17.positional({
11445
+ sandbox: import_cjs$18.positional({ type: sandboxName }),
11446
+ command: import_cjs$18.positional({
11443
11447
  displayName: "command",
11444
11448
  description: "The executable to invoke"
11445
11449
  }),
11446
- args: import_cjs$17.rest({
11450
+ args: import_cjs$18.rest({
11447
11451
  displayName: "args",
11448
11452
  description: "arguments to pass to the command"
11449
11453
  }),
11450
- asSudo: import_cjs$17.flag({
11454
+ asSudo: import_cjs$18.flag({
11451
11455
  long: "sudo",
11452
11456
  description: "Give extended privileges to the command."
11453
11457
  }),
11454
- interactive: import_cjs$17.flag({
11458
+ interactive: import_cjs$18.flag({
11455
11459
  long: "interactive",
11456
11460
  short: "i",
11457
11461
  description: "Run the command in a secure interactive shell",
11458
- type: import_cjs$17.extendType(import_cjs$17.boolean, {
11462
+ type: import_cjs$18.extendType(import_cjs$18.boolean, {
11459
11463
  defaultValue() {
11460
11464
  return false;
11461
11465
  },
@@ -11465,22 +11469,22 @@ const args$3 = {
11465
11469
  }
11466
11470
  })
11467
11471
  }),
11468
- skipExtendingTimeout: import_cjs$17.flag({
11472
+ skipExtendingTimeout: import_cjs$18.flag({
11469
11473
  long: "no-extend-timeout",
11470
11474
  description: "Do not extend the sandbox timeout while running an interactive command. Only affects interactive executions."
11471
11475
  }),
11472
- tty: import_cjs$17.flag({
11476
+ tty: import_cjs$18.flag({
11473
11477
  long: "tty",
11474
11478
  short: "t",
11475
11479
  description: "Allocate a tty for an interactive command. This is a no-op."
11476
11480
  }),
11477
- cwd: import_cjs$17.option({
11481
+ cwd: import_cjs$18.option({
11478
11482
  long: "workdir",
11479
11483
  short: "w",
11480
11484
  description: "The working directory to run the command in",
11481
- type: import_cjs$17.optional(import_cjs$17.string)
11485
+ type: import_cjs$18.optional(import_cjs$18.string)
11482
11486
  }),
11483
- envVars: import_cjs$17.multioption({
11487
+ envVars: import_cjs$18.multioption({
11484
11488
  long: "env",
11485
11489
  short: "e",
11486
11490
  type: ObjectFromKeyValue,
@@ -11488,7 +11492,7 @@ const args$3 = {
11488
11492
  }),
11489
11493
  scope
11490
11494
  };
11491
- const exec = import_cjs$17.command({
11495
+ const exec = import_cjs$18.command({
11492
11496
  name: "exec",
11493
11497
  description: "Execute a command in an existing sandbox",
11494
11498
  args: args$3,
@@ -11525,9 +11529,9 @@ const exec = import_cjs$17.command({
11525
11529
 
11526
11530
  //#endregion
11527
11531
  //#region src/args/network-policy.ts
11528
- var import_cjs$16 = /* @__PURE__ */ __toESM(require_cjs());
11532
+ var import_cjs$17 = /* @__PURE__ */ __toESM(require_cjs());
11529
11533
  init_source();
11530
- const networkPolicyMode = import_cjs$16.extendType(import_cjs$16.string, {
11534
+ const networkPolicyMode = import_cjs$17.extendType(import_cjs$17.string, {
11531
11535
  displayName: "MODE",
11532
11536
  async from(value) {
11533
11537
  const validModes = ["allow-all", "deny-all"];
@@ -11535,28 +11539,28 @@ const networkPolicyMode = import_cjs$16.extendType(import_cjs$16.string, {
11535
11539
  return value;
11536
11540
  }
11537
11541
  });
11538
- const networkPolicy = import_cjs$16.option({
11542
+ const networkPolicy = import_cjs$17.option({
11539
11543
  long: "network-policy",
11540
11544
  description: `Network policy mode: "allow-all" or "deny-all"
11541
11545
  - allow-all: sandbox can access any website/domain
11542
11546
  - deny-all: sandbox has no network access
11543
11547
  Omit this option and use --allowed-domain / --allowed-cidr / --denied-cidr for custom policies.`,
11544
- type: import_cjs$16.optional(networkPolicyMode)
11548
+ type: import_cjs$17.optional(networkPolicyMode)
11545
11549
  });
11546
- const allowedDomains = import_cjs$16.multioption({
11550
+ const allowedDomains = import_cjs$17.multioption({
11547
11551
  long: "allowed-domain",
11548
11552
  description: `Domain to allow traffic to (creates a custom network policy). Supports "*" for wildcards for a segment (e.g. '*.vercel.com', 'www.*.com'). If used as the first segment, will match any subdomain.`,
11549
- type: import_cjs$16.array(import_cjs$16.string)
11553
+ type: import_cjs$17.array(import_cjs$17.string)
11550
11554
  });
11551
- const allowedCIDRs = import_cjs$16.multioption({
11555
+ const allowedCIDRs = import_cjs$17.multioption({
11552
11556
  long: "allowed-cidr",
11553
11557
  description: `CIDR to allow traffic to (creates a custom network policy). Takes precedence over 'allowed-domain'.`,
11554
- type: import_cjs$16.array(import_cjs$16.string)
11558
+ type: import_cjs$17.array(import_cjs$17.string)
11555
11559
  });
11556
- const deniedCIDRs = import_cjs$16.multioption({
11560
+ const deniedCIDRs = import_cjs$17.multioption({
11557
11561
  long: "denied-cidr",
11558
11562
  description: `CIDR to deny traffic to (creates a custom network policy). Takes precedence over allowed domains/CIDRs.`,
11559
- type: import_cjs$16.array(import_cjs$16.string)
11563
+ type: import_cjs$17.array(import_cjs$17.string)
11560
11564
  });
11561
11565
  const networkPolicyArgs = {
11562
11566
  networkPolicy,
@@ -11595,8 +11599,8 @@ function buildNetworkPolicy(args$4) {
11595
11599
 
11596
11600
  //#endregion
11597
11601
  //#region src/types/snapshot-expiration.ts
11598
- var import_cjs$15 = require_cjs();
11599
- const SnapshotExpiration = (0, import_cjs$15.extendType)(import_cjs$15.string, {
11602
+ var import_cjs$16 = require_cjs();
11603
+ const SnapshotExpiration = (0, import_cjs$16.extendType)(import_cjs$16.string, {
11600
11604
  displayName: "DURATION|none",
11601
11605
  description: "A duration (e.g. 7d, 30d) or \"none\" for no expiration",
11602
11606
  async from(value) {
@@ -11605,6 +11609,27 @@ const SnapshotExpiration = (0, import_cjs$15.extendType)(import_cjs$15.string, {
11605
11609
  }
11606
11610
  });
11607
11611
 
11612
+ //#endregion
11613
+ //#region src/args/ports.ts
11614
+ var import_cjs$15 = /* @__PURE__ */ __toESM(require_cjs());
11615
+ init_source();
11616
+ const publishPorts = import_cjs$15.multioption({
11617
+ long: "publish-port",
11618
+ short: "p",
11619
+ description: "Publish sandbox port(s) to DOMAIN.vercel.run",
11620
+ type: import_cjs$15.array(import_cjs$15.extendType(import_cjs$15.number, {
11621
+ displayName: "PORT",
11622
+ async from(number) {
11623
+ if (!Number.isInteger(number) || number < 1024 || number > 65535) throw new Error([
11624
+ `Invalid port: ${number}.`,
11625
+ `${source_default.bold("hint:")} Ports must be integers between 1024-65535 (privileged ports 0-1023 are reserved).`,
11626
+ "Examples: 3000, 8443"
11627
+ ].join("\n"));
11628
+ return number;
11629
+ }
11630
+ }))
11631
+ });
11632
+
11608
11633
  //#endregion
11609
11634
  //#region src/commands/create.ts
11610
11635
  var import_cjs$14 = /* @__PURE__ */ __toESM(require_cjs());
@@ -11623,22 +11648,7 @@ const args$2 = {
11623
11648
  runtime,
11624
11649
  timeout,
11625
11650
  vcpus,
11626
- ports: import_cjs$14.multioption({
11627
- long: "publish-port",
11628
- short: "p",
11629
- description: "Publish sandbox port(s) to DOMAIN.vercel.run",
11630
- type: import_cjs$14.array(import_cjs$14.extendType(import_cjs$14.number, {
11631
- displayName: "PORT",
11632
- async from(number) {
11633
- if (number < 1024 || number > 65535) throw new Error([
11634
- `Invalid port: ${number}.`,
11635
- `${source_default.bold("hint:")} Ports must be between 1024-65535 (privileged ports 0-1023 are reserved).`,
11636
- "╰▶ Examples: 3000, 8080, 8443"
11637
- ].join("\n"));
11638
- return number;
11639
- }
11640
- }))
11641
- }),
11651
+ ports: publishPorts,
11642
11652
  silent: import_cjs$14.flag({
11643
11653
  long: "silent",
11644
11654
  description: "Don't write sandbox name to stdout"
@@ -14587,6 +14597,75 @@ const snapshot = import_cjs$6.command({
14587
14597
  }
14588
14598
  });
14589
14599
 
14600
+ //#endregion
14601
+ //#region src/util/snapshot-tree.ts
14602
+ init_source();
14603
+ function formatExpires(expiresAt) {
14604
+ if (expiresAt === void 0) return source_default.gray("never");
14605
+ const ms$4 = expiresAt - Date.now();
14606
+ if (ms$4 <= 0) return source_default.red("expired");
14607
+ const formatted = timeAgo(expiresAt);
14608
+ return ms$4 <= 3600 * 1e3 ? source_default.red(formatted) : source_default.green(formatted);
14609
+ }
14610
+ function renderNode(id, expiresAt, isCurrent) {
14611
+ const bullet = isCurrent ? source_default.magenta.bold("●") : source_default.magenta("●");
14612
+ const suffix = isCurrent ? ` ${source_default.green("◂ current")}` : "";
14613
+ return `${bullet} ${source_default.yellow(id)} expires: ${formatExpires(expiresAt)}${suffix}`;
14614
+ }
14615
+ function renderSiblings(siblings, count) {
14616
+ const lines = [];
14617
+ const shown = siblings.slice(0, 5);
14618
+ const totalCount = parseInt(count, 10);
14619
+ const hasPlus = count.endsWith("+");
14620
+ const remaining = totalCount - 1 - shown.length;
14621
+ for (let i = 0; i < shown.length; i++) {
14622
+ const connector = i === shown.length - 1 && remaining <= 0 ? "╰──" : "├──";
14623
+ lines.push(`│ ${connector} ${source_default.gray(shown[i].sourceSessionId)}`);
14624
+ }
14625
+ if (remaining > 0) {
14626
+ const suffix = hasPlus ? "+" : "";
14627
+ lines.push(`│ ╰── ${source_default.gray(`+${remaining}${suffix} more sandboxes`)}`);
14628
+ }
14629
+ return lines;
14630
+ }
14631
+ function renderSnapshotTree(params) {
14632
+ const { ancestors, descendants } = params;
14633
+ const hideCurrent = params.hideCurrent === true;
14634
+ const currentSnapshotId = hideCurrent ? void 0 : params.currentSnapshotId;
14635
+ const lines = [];
14636
+ const pushNode = (node, isCurrent) => {
14637
+ lines.push("│");
14638
+ lines.push(renderNode(node.snapshot.id, node.snapshot.expiresAt, isCurrent));
14639
+ if (node.siblings.length > 0) lines.push(...renderSiblings(node.siblings, node.count));
14640
+ };
14641
+ const descendantNodes = descendants.snapshots.filter((n) => n.snapshot.id !== currentSnapshotId);
14642
+ if (descendantNodes.length > 0) for (const node of [...descendantNodes].reverse()) pushNode(node, false);
14643
+ if (!hideCurrent) {
14644
+ const { currentSnapshotId: id, currentSnapshotExpiresAt } = params;
14645
+ pushNode(ancestors.snapshots.find((n) => n.snapshot.id === id) ?? descendants.snapshots.find((n) => n.snapshot.id === id) ?? {
14646
+ snapshot: {
14647
+ id,
14648
+ sourceSessionId: "",
14649
+ expiresAt: currentSnapshotExpiresAt
14650
+ },
14651
+ siblings: [],
14652
+ count: "1"
14653
+ }, true);
14654
+ }
14655
+ const ancestorNodes = ancestors.snapshots.filter((n) => n.snapshot.id !== currentSnapshotId);
14656
+ for (const node of ancestorNodes) pushNode(node, false);
14657
+ if (!hideCurrent) {
14658
+ const lastAncestor = ancestorNodes[ancestorNodes.length - 1];
14659
+ if (ancestors.pagination.next === null) {
14660
+ if (lastAncestor && !lastAncestor.snapshot.parentId || ancestorNodes.length === 0) {
14661
+ lines.push("│");
14662
+ lines.push(`${source_default.white("○")} ${source_default.gray("(root)")}`);
14663
+ }
14664
+ }
14665
+ }
14666
+ return lines.join("\n");
14667
+ }
14668
+
14590
14669
  //#endregion
14591
14670
  //#region src/commands/snapshots.ts
14592
14671
  var import_cjs$4 = /* @__PURE__ */ __toESM(require_cjs());
@@ -14737,12 +14816,154 @@ const remove$1 = import_cjs$4.command({
14737
14816
  }
14738
14817
  }
14739
14818
  });
14819
+ const tree = import_cjs$4.command({
14820
+ name: "tree",
14821
+ description: "Show the snapshot ancestry tree for a sandbox.",
14822
+ args: {
14823
+ scope,
14824
+ sandboxName: import_cjs$4.positional({
14825
+ type: sandboxName,
14826
+ description: "Sandbox name"
14827
+ }),
14828
+ sortOrder: import_cjs$4.option({
14829
+ long: "sort-order",
14830
+ description: "Sort order. Options: asc, desc (default). 'desc' walks ancestors, 'asc' walks descendants. Requires --cursor.",
14831
+ type: import_cjs$4.optional(import_cjs$4.oneOf(["asc", "desc"]))
14832
+ }),
14833
+ limit: import_cjs$4.option({
14834
+ long: "limit",
14835
+ description: "Maximum number of snapshots per page (1–10, default 10).",
14836
+ type: import_cjs$4.optional(import_cjs$4.number)
14837
+ }),
14838
+ cursor: import_cjs$4.option({
14839
+ long: "cursor",
14840
+ description: "Pagination cursor from a previous 'More ancestors' or 'More descendants' hint.",
14841
+ type: import_cjs$4.optional(import_cjs$4.string)
14842
+ })
14843
+ },
14844
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, sandboxName: name, sortOrder, limit, cursor }) {
14845
+ if (limit !== void 0 && (limit < 1 || limit > 10)) {
14846
+ console.error(source_default.red("Error: --limit must be between 1 and 10."));
14847
+ process.exitCode = 1;
14848
+ return;
14849
+ }
14850
+ if (sortOrder !== void 0 && !cursor) {
14851
+ console.error(source_default.red("Error: --sort-order requires --cursor."));
14852
+ process.exitCode = 1;
14853
+ return;
14854
+ }
14855
+ const pageLimit = limit ?? 10;
14856
+ if (cursor) {
14857
+ const effectiveSortOrder = sortOrder ?? "desc";
14858
+ const page = await (async () => {
14859
+ try {
14860
+ var _usingCtx4 = _usingCtx();
14861
+ const _spinner$1 = _usingCtx4.u(acquireRelease(() => ora("Fetching snapshot tree...").start(), (s$1) => s$1.stop()));
14862
+ return snapshotClient.tree({
14863
+ snapshotId: cursor,
14864
+ sortOrder: effectiveSortOrder,
14865
+ limit: pageLimit,
14866
+ token: token$1,
14867
+ teamId: team$1,
14868
+ projectId: project$1
14869
+ });
14870
+ } catch (_) {
14871
+ _usingCtx4.e = _;
14872
+ } finally {
14873
+ _usingCtx4.d();
14874
+ }
14875
+ })();
14876
+ const ancestors = effectiveSortOrder === "desc" ? page : {
14877
+ snapshots: [],
14878
+ pagination: {
14879
+ count: 0,
14880
+ next: null
14881
+ }
14882
+ };
14883
+ const descendants = effectiveSortOrder === "asc" ? page : {
14884
+ snapshots: [],
14885
+ pagination: {
14886
+ count: 0,
14887
+ next: null
14888
+ }
14889
+ };
14890
+ console.log(renderSnapshotTree({
14891
+ hideCurrent: true,
14892
+ ancestors,
14893
+ descendants
14894
+ }));
14895
+ if (page.pagination.next !== null) console.log(formatNextCursorHint(page.pagination.next));
14896
+ return;
14897
+ }
14898
+ const result = await (async () => {
14899
+ try {
14900
+ var _usingCtx5 = _usingCtx();
14901
+ const _spinner$1 = _usingCtx5.u(acquireRelease(() => ora("Fetching snapshot tree...").start(), (s$1) => s$1.stop()));
14902
+ const currentSnapshotId = (await sandboxClient.get({
14903
+ name,
14904
+ token: token$1,
14905
+ teamId: team$1,
14906
+ projectId: project$1
14907
+ })).currentSnapshotId;
14908
+ if (!currentSnapshotId) return null;
14909
+ const [currentSnap, ancestors, descendants] = await Promise.all([
14910
+ snapshotClient.get({
14911
+ snapshotId: currentSnapshotId,
14912
+ token: token$1,
14913
+ teamId: team$1,
14914
+ projectId: project$1
14915
+ }),
14916
+ snapshotClient.tree({
14917
+ snapshotId: currentSnapshotId,
14918
+ sortOrder: "desc",
14919
+ limit: pageLimit,
14920
+ token: token$1,
14921
+ teamId: team$1,
14922
+ projectId: project$1
14923
+ }),
14924
+ snapshotClient.tree({
14925
+ snapshotId: currentSnapshotId,
14926
+ sortOrder: "asc",
14927
+ limit: pageLimit,
14928
+ token: token$1,
14929
+ teamId: team$1,
14930
+ projectId: project$1
14931
+ })
14932
+ ]);
14933
+ return {
14934
+ currentSnap,
14935
+ currentSnapshotId,
14936
+ ancestors,
14937
+ descendants
14938
+ };
14939
+ } catch (_) {
14940
+ _usingCtx5.e = _;
14941
+ } finally {
14942
+ _usingCtx5.d();
14943
+ }
14944
+ })();
14945
+ if (!result) {
14946
+ console.log(source_default.yellow("No snapshots found for this sandbox."));
14947
+ return;
14948
+ }
14949
+ console.log(renderSnapshotTree({
14950
+ currentSnapshotId: result.currentSnapshotId,
14951
+ currentSnapshotExpiresAt: result.currentSnap.expiresAt?.getTime(),
14952
+ ancestors: result.ancestors,
14953
+ descendants: result.descendants
14954
+ }));
14955
+ const limitArg = limit !== void 0 ? ` --limit ${limit}` : "";
14956
+ if (result.ancestors.pagination.next !== null) console.log(`\nMore ancestors: sandbox snapshots tree ${name} --sort-order desc${limitArg} --cursor ${result.ancestors.pagination.next}`);
14957
+ if (result.descendants.pagination.next !== null) console.log(`More descendants: sandbox snapshots tree ${name} --sort-order asc${limitArg} --cursor ${result.descendants.pagination.next}`);
14958
+ }
14959
+ });
14740
14960
  const snapshots = (0, import_cjs$5.subcommands)({
14741
14961
  name: "snapshots",
14742
14962
  description: "Manage sandbox snapshots",
14743
14963
  cmds: {
14744
14964
  list: list$2,
14745
14965
  get,
14966
+ tree,
14746
14967
  delete: remove$1
14747
14968
  }
14748
14969
  });
@@ -15160,6 +15381,47 @@ const currentSnapshotCommand = import_cjs$1.command({
15160
15381
  }
15161
15382
  }
15162
15383
  });
15384
+ const portsCommand = import_cjs$1.command({
15385
+ name: "ports",
15386
+ description: "Update the published ports of a sandbox. Replaces all existing published ports.",
15387
+ args: {
15388
+ sandbox: import_cjs$1.positional({
15389
+ type: sandboxName,
15390
+ description: "Sandbox name to update"
15391
+ }),
15392
+ ports: publishPorts,
15393
+ scope
15394
+ },
15395
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, sandbox: name, ports }) {
15396
+ const sandbox = await sandboxClient.get({
15397
+ name,
15398
+ projectId: project$1,
15399
+ teamId: team$1,
15400
+ token: token$1
15401
+ });
15402
+ const spinner = ora("Updating sandbox ports...").start();
15403
+ try {
15404
+ await sandbox.update({ ports });
15405
+ spinner.stop();
15406
+ process.stderr.write("✅ Ports updated for sandbox " + source_default.cyan(name) + "\n");
15407
+ if (ports.length === 0) process.stderr.write(source_default.dim(" ╰ ") + "all ports unpublished\n");
15408
+ else {
15409
+ const routes = getPublishedRoutes(sandbox);
15410
+ process.stderr.write(source_default.dim(" │ ") + "ports:\n");
15411
+ for (let i = 0; i < ports.length; i++) {
15412
+ const port = ports[i];
15413
+ const route = routes?.find((route$1) => route$1.port === port);
15414
+ const prefix$1 = i === ports.length - 1 ? source_default.dim(" ╰ ") : source_default.dim(" │ ");
15415
+ const url = route ? " -> " + source_default.cyan(route.url) : "";
15416
+ process.stderr.write(prefix$1 + "• " + port + url + "\n");
15417
+ }
15418
+ }
15419
+ } catch (error) {
15420
+ spinner.stop();
15421
+ throw error;
15422
+ }
15423
+ }
15424
+ });
15163
15425
  const listCommand = import_cjs$1.command({
15164
15426
  name: "list",
15165
15427
  description: "Display the current configuration of a sandbox",
@@ -15206,6 +15468,10 @@ const listCommand = import_cjs$1.command({
15206
15468
  field: "Network policy",
15207
15469
  value: String(networkPolicy$1)
15208
15470
  },
15471
+ {
15472
+ field: "Ports",
15473
+ value: formatPorts(sandbox)
15474
+ },
15209
15475
  {
15210
15476
  field: "Snapshot expiration",
15211
15477
  value: sandbox.snapshotExpiration != null && sandbox.snapshotExpiration > 0 ? (0, import_ms.default)(sandbox.snapshotExpiration, { long: true }) : sandbox.snapshotExpiration === 0 ? "none" : "-"
@@ -15325,6 +15591,14 @@ function formatKeepLastSnapshots(keepLastSnapshots) {
15325
15591
  if (keepLastSnapshots.deleteEvicted !== void 0) parts.push(`delete-evicted-snapshots=${keepLastSnapshots.deleteEvicted}`);
15326
15592
  return parts.join(", ");
15327
15593
  }
15594
+ function formatPorts(sandbox) {
15595
+ const ports = getPublishedRoutes(sandbox)?.map((route) => route.port) ?? [];
15596
+ if (ports.length === 0) return "-";
15597
+ return ports.join(", ");
15598
+ }
15599
+ function getPublishedRoutes(sandbox) {
15600
+ return sandbox.routes.filter((route) => route.port !== sandbox.interactivePort);
15601
+ }
15328
15602
  const config = import_cjs$1.subcommands({
15329
15603
  name: "config",
15330
15604
  description: "View and update sandbox configuration",
@@ -15339,6 +15613,7 @@ const config = import_cjs$1.subcommands({
15339
15613
  "keep-last-snapshots-for": keepLastSnapshotsForCommand,
15340
15614
  "delete-evicted-snapshots": deleteEvictedSnapshotsCommand,
15341
15615
  "current-snapshot": currentSnapshotCommand,
15616
+ ports: portsCommand,
15342
15617
  tags: tagsCommand
15343
15618
  }
15344
15619
  });
@@ -15386,4 +15661,4 @@ const app = (opts) => (0, import_cjs.subcommands)({
15386
15661
 
15387
15662
  //#endregion
15388
15663
  export { source_exports as a, init_source as i, StyledError as n, require_cjs as r, app as t };
15389
- //# sourceMappingURL=app-BjVyEIk1.mjs.map
15664
+ //# sourceMappingURL=app-BdOGNXsw.mjs.map