sandbox 3.0.0-beta.9 → 3.0.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.
@@ -15,10 +15,11 @@ import childProcess, { execFile } from "node:child_process";
15
15
  import fs, { constants, writeFile } from "node:fs/promises";
16
16
  import fs$1, { createWriteStream } from "node:fs";
17
17
  import * as Auth from "@vercel/sandbox/dist/auth/index.js";
18
- import { OAuth, getAuth, pollForToken, updateAuthConfig } from "@vercel/sandbox/dist/auth/index.js";
18
+ import { NotOk, OAuth, getAuth, inferScope, pollForToken, updateAuthConfig } from "@vercel/sandbox/dist/auth/index.js";
19
19
  import { EOL } from "os";
20
20
  import { z } from "zod/v4";
21
21
  import { z as z$1 } from "zod";
22
+ import retry from "async-retry";
22
23
  import { APIError, Sandbox, Snapshot } from "@vercel/sandbox";
23
24
  import readline from "node:readline";
24
25
  import { randomUUID } from "crypto";
@@ -295,30 +296,30 @@ var init_supports_color = __esm({ "../../node_modules/.pnpm/chalk@5.6.0/node_mod
295
296
 
296
297
  //#endregion
297
298
  //#region ../../node_modules/.pnpm/chalk@5.6.0/node_modules/chalk/source/utilities.js
298
- function stringReplaceAll(string$1, substring, replacer) {
299
- let index = string$1.indexOf(substring);
300
- if (index === -1) return string$1;
299
+ function stringReplaceAll(string$2, substring, replacer) {
300
+ let index = string$2.indexOf(substring);
301
+ if (index === -1) return string$2;
301
302
  const substringLength = substring.length;
302
303
  let endIndex = 0;
303
304
  let returnValue = "";
304
305
  do {
305
- returnValue += string$1.slice(endIndex, index) + substring + replacer;
306
+ returnValue += string$2.slice(endIndex, index) + substring + replacer;
306
307
  endIndex = index + substringLength;
307
- index = string$1.indexOf(substring, endIndex);
308
+ index = string$2.indexOf(substring, endIndex);
308
309
  } while (index !== -1);
309
- returnValue += string$1.slice(endIndex);
310
+ returnValue += string$2.slice(endIndex);
310
311
  return returnValue;
311
312
  }
312
- function stringEncaseCRLFWithFirstIndex(string$1, prefix$1, postfix, index) {
313
+ function stringEncaseCRLFWithFirstIndex(string$2, prefix$1, postfix, index) {
313
314
  let endIndex = 0;
314
315
  let returnValue = "";
315
316
  do {
316
- const gotCR = string$1[index - 1] === "\r";
317
- returnValue += string$1.slice(endIndex, gotCR ? index - 1 : index) + prefix$1 + (gotCR ? "\r\n" : "\n") + postfix;
317
+ const gotCR = string$2[index - 1] === "\r";
318
+ returnValue += string$2.slice(endIndex, gotCR ? index - 1 : index) + prefix$1 + (gotCR ? "\r\n" : "\n") + postfix;
318
319
  endIndex = index + 1;
319
- index = string$1.indexOf("\n", endIndex);
320
+ index = string$2.indexOf("\n", endIndex);
320
321
  } while (index !== -1);
321
- returnValue += string$1.slice(endIndex);
322
+ returnValue += string$2.slice(endIndex);
322
323
  return returnValue;
323
324
  }
324
325
  var init_utilities = __esm({ "../../node_modules/.pnpm/chalk@5.6.0/node_modules/chalk/source/utilities.js": (() => {}) });
@@ -454,18 +455,18 @@ var init_source = __esm({ "../../node_modules/.pnpm/chalk@5.6.0/node_modules/cha
454
455
  builder[IS_EMPTY] = _isEmpty;
455
456
  return builder;
456
457
  };
457
- applyStyle = (self, string$1) => {
458
- if (self.level <= 0 || !string$1) return self[IS_EMPTY] ? "" : string$1;
458
+ applyStyle = (self, string$2) => {
459
+ if (self.level <= 0 || !string$2) return self[IS_EMPTY] ? "" : string$2;
459
460
  let styler = self[STYLER];
460
- if (styler === void 0) return string$1;
461
+ if (styler === void 0) return string$2;
461
462
  const { openAll, closeAll } = styler;
462
- if (string$1.includes("\x1B")) while (styler !== void 0) {
463
- string$1 = stringReplaceAll(string$1, styler.close, styler.open);
463
+ if (string$2.includes("\x1B")) while (styler !== void 0) {
464
+ string$2 = stringReplaceAll(string$2, styler.close, styler.open);
464
465
  styler = styler.parent;
465
466
  }
466
- const lfIndex = string$1.indexOf("\n");
467
- if (lfIndex !== -1) string$1 = stringEncaseCRLFWithFirstIndex(string$1, closeAll, openAll, lfIndex);
468
- return openAll + string$1 + closeAll;
467
+ const lfIndex = string$2.indexOf("\n");
468
+ if (lfIndex !== -1) string$2 = stringEncaseCRLFWithFirstIndex(string$2, closeAll, openAll, lfIndex);
469
+ return openAll + string$2 + closeAll;
469
470
  };
470
471
  Object.defineProperties(createChalk.prototype, styles);
471
472
  chalk = createChalk();
@@ -689,7 +690,7 @@ var require_type = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/cmd-
689
690
  exports.identity = void 0;
690
691
  exports.typeDef = typeDef;
691
692
  exports.fromFn = fromFn;
692
- exports.extendType = extendType$1;
693
+ exports.extendType = extendType$2;
693
694
  var from_1 = require_from();
694
695
  Object.defineProperty(exports, "identity", {
695
696
  enumerable: true,
@@ -721,7 +722,7 @@ var require_type = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/cmd-
721
722
  * @param base A base type from `InputA` to `OutputA`
722
723
  * @param nextTypeOrDecodingFunction Either an entire `Type<OutputA, AnyOutput>` or just a decoding function from `OutputA` to any type
723
724
  */
724
- function extendType$1(base, nextTypeOrDecodingFunction) {
725
+ function extendType$2(base, nextTypeOrDecodingFunction) {
725
726
  const { defaultValue: _defaultValue, onMissing: _onMissing, from: _from, ...t1WithoutDefault } = base;
726
727
  const t2Object = typeDef(nextTypeOrDecodingFunction);
727
728
  const t2From = fromFn(nextTypeOrDecodingFunction);
@@ -1997,7 +1998,7 @@ var require_parser$1 = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/
1997
1998
  };
1998
1999
  Object.defineProperty(exports, "__esModule", { value: true });
1999
2000
  exports.parse = parse$5;
2000
- const debug$6 = (0, __importDefault$1(__require$1("debug")).default)("cmd-ts:parser");
2001
+ const debug$7 = (0, __importDefault$1(__require$1("debug")).default)("cmd-ts:parser");
2001
2002
  /**
2002
2003
  * Create an AST from a token list
2003
2004
  *
@@ -2005,14 +2006,14 @@ var require_parser$1 = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/
2005
2006
  * @param forceFlag Keys to force as flag. {@see ForceFlag} to read more about it.
2006
2007
  */
2007
2008
  function parse$5(tokens, forceFlag) {
2008
- if (debug$6.enabled) {
2009
+ if (debug$7.enabled) {
2009
2010
  const registered = {
2010
2011
  shortFlags: [...forceFlag.forceFlagShortNames],
2011
2012
  longFlags: [...forceFlag.forceFlagLongNames],
2012
2013
  shortOptions: [...forceFlag.forceOptionShortNames],
2013
2014
  longOptions: [...forceFlag.forceOptionLongNames]
2014
2015
  };
2015
- debug$6("Registered:", JSON.stringify(registered));
2016
+ debug$7("Registered:", JSON.stringify(registered));
2016
2017
  }
2017
2018
  const nodes = [];
2018
2019
  let index = 0;
@@ -2143,9 +2144,9 @@ var require_parser$1 = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/
2143
2144
  }
2144
2145
  index++;
2145
2146
  }
2146
- if (debug$6.enabled) {
2147
+ if (debug$7.enabled) {
2147
2148
  const objectNodes = nodes.map((node) => ({ [node.type]: node.raw }));
2148
- debug$6("Parsed items:", JSON.stringify(objectNodes));
2149
+ debug$7("Parsed items:", JSON.stringify(objectNodes));
2149
2150
  }
2150
2151
  return nodes;
2151
2152
  }
@@ -2200,8 +2201,8 @@ var require_tokenizer = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm
2200
2201
  tokens.push(token$1);
2201
2202
  overallIndex += token$1.raw.length;
2202
2203
  };
2203
- for (const [stringIndex, string$1] of (0, utils_1.enumerate)(strings)) {
2204
- const chars = [...string$1];
2204
+ for (const [stringIndex, string$2] of (0, utils_1.enumerate)(strings)) {
2205
+ const chars = [...string$2];
2205
2206
  for (let i = 0; i < chars.length; i++) if (chars[i] === "-" && chars[i + 1] === "-") {
2206
2207
  push$1({
2207
2208
  type: "longPrefix",
@@ -2997,16 +2998,17 @@ var require_cjs = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/cmd-t
2997
2998
 
2998
2999
  //#endregion
2999
3000
  //#region src/args/runtime.ts
3000
- var import_cjs$26 = /* @__PURE__ */ __toESM(require_cjs());
3001
+ var import_cjs$30 = /* @__PURE__ */ __toESM(require_cjs());
3001
3002
  const runtimeType = {
3002
- ...import_cjs$26.oneOf([
3003
+ ...import_cjs$30.oneOf([
3003
3004
  "node22",
3004
3005
  "node24",
3006
+ "node26",
3005
3007
  "python3.13"
3006
3008
  ]),
3007
3009
  displayName: "runtime"
3008
3010
  };
3009
- const runtime = import_cjs$26.option({
3011
+ const runtime = import_cjs$30.option({
3010
3012
  long: "runtime",
3011
3013
  type: runtimeType,
3012
3014
  defaultValue: () => "node24",
@@ -3100,13 +3102,13 @@ var require_ms = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/ms@2.1
3100
3102
  * @return {String}
3101
3103
  * @api private
3102
3104
  */
3103
- function fmtShort(ms$4) {
3104
- var msAbs = Math.abs(ms$4);
3105
- if (msAbs >= d) return Math.round(ms$4 / d) + "d";
3106
- if (msAbs >= h) return Math.round(ms$4 / h) + "h";
3107
- if (msAbs >= m) return Math.round(ms$4 / m) + "m";
3108
- if (msAbs >= s) return Math.round(ms$4 / s) + "s";
3109
- return ms$4 + "ms";
3105
+ function fmtShort(ms$6) {
3106
+ var msAbs = Math.abs(ms$6);
3107
+ if (msAbs >= d) return Math.round(ms$6 / d) + "d";
3108
+ if (msAbs >= h) return Math.round(ms$6 / h) + "h";
3109
+ if (msAbs >= m) return Math.round(ms$6 / m) + "m";
3110
+ if (msAbs >= s) return Math.round(ms$6 / s) + "s";
3111
+ return ms$6 + "ms";
3110
3112
  }
3111
3113
  /**
3112
3114
  * Long format for `ms`.
@@ -3115,34 +3117,34 @@ var require_ms = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/ms@2.1
3115
3117
  * @return {String}
3116
3118
  * @api private
3117
3119
  */
3118
- function fmtLong(ms$4) {
3119
- var msAbs = Math.abs(ms$4);
3120
- if (msAbs >= d) return plural(ms$4, msAbs, d, "day");
3121
- if (msAbs >= h) return plural(ms$4, msAbs, h, "hour");
3122
- if (msAbs >= m) return plural(ms$4, msAbs, m, "minute");
3123
- if (msAbs >= s) return plural(ms$4, msAbs, s, "second");
3124
- return ms$4 + " ms";
3120
+ function fmtLong(ms$6) {
3121
+ var msAbs = Math.abs(ms$6);
3122
+ if (msAbs >= d) return plural(ms$6, msAbs, d, "day");
3123
+ if (msAbs >= h) return plural(ms$6, msAbs, h, "hour");
3124
+ if (msAbs >= m) return plural(ms$6, msAbs, m, "minute");
3125
+ if (msAbs >= s) return plural(ms$6, msAbs, s, "second");
3126
+ return ms$6 + " ms";
3125
3127
  }
3126
3128
  /**
3127
3129
  * Pluralization helper.
3128
3130
  */
3129
- function plural(ms$4, msAbs, n, name) {
3131
+ function plural(ms$6, msAbs, n, name) {
3130
3132
  var isPlural = msAbs >= n * 1.5;
3131
- return Math.round(ms$4 / n) + " " + name + (isPlural ? "s" : "");
3133
+ return Math.round(ms$6 / n) + " " + name + (isPlural ? "s" : "");
3132
3134
  }
3133
3135
  }) });
3134
3136
 
3135
3137
  //#endregion
3136
3138
  //#region src/types/duration.ts
3137
- var import_cjs$25 = require_cjs();
3139
+ var import_cjs$29 = require_cjs();
3138
3140
  init_source();
3139
- const Duration = (0, import_cjs$25.extendType)(import_cjs$25.string, {
3141
+ const Duration = (0, import_cjs$29.extendType)(import_cjs$29.string, {
3140
3142
  displayName: "num UNIT",
3141
3143
  description: "A duration, e.g. 5m, 10s, 1h",
3142
- async from(string$1) {
3143
- const match$1 = string$1.match(/^(\d+) ?(ms|milliseconds?|msecs?|s(?:econds?)?|m(?:inutes?)?|h(?:ours?)?|d(?:ays?)?)?$/);
3144
+ async from(string$2) {
3145
+ const match$1 = string$2.match(/^(\d+) ?(ms|milliseconds?|msecs?|s(?:econds?)?|m(?:inutes?)?|h(?:ours?)?|d(?:ays?)?)?$/);
3144
3146
  if (!match$1) throw new Error([
3145
- `Malformed duration: "${string$1}".`,
3147
+ `Malformed duration: "${string$2}".`,
3146
3148
  `${source_default.bold("hint:")} Use a number followed by a unit: s (seconds), m (minutes), h (hours), d (days).`,
3147
3149
  "╰▶ Examples: 30s, 5m, 2h, 1d"
3148
3150
  ].join("\n"));
@@ -3152,28 +3154,28 @@ const Duration = (0, import_cjs$25.extendType)(import_cjs$25.string, {
3152
3154
 
3153
3155
  //#endregion
3154
3156
  //#region src/args/timeout.ts
3155
- var import_cjs$24 = /* @__PURE__ */ __toESM(require_cjs());
3156
- const timeout = import_cjs$24.option({
3157
+ var import_cjs$28 = /* @__PURE__ */ __toESM(require_cjs());
3158
+ const timeout = import_cjs$28.option({
3157
3159
  long: "timeout",
3158
3160
  type: Duration,
3159
- description: "The maximum duration a sandbox can run for. Example: 5m, 1h",
3161
+ description: "The maximum duration a sandbox can run for. Example: 5m, 30m",
3160
3162
  defaultValue: () => "5 minutes",
3161
3163
  defaultValueIsSerializable: true
3162
3164
  });
3163
3165
 
3164
3166
  //#endregion
3165
3167
  //#region src/args/vcpus.ts
3166
- var import_cjs$23 = /* @__PURE__ */ __toESM(require_cjs());
3167
- const vcpusType = import_cjs$23.extendType(import_cjs$23.number, {
3168
+ var import_cjs$27 = /* @__PURE__ */ __toESM(require_cjs());
3169
+ const vcpusType = import_cjs$27.extendType(import_cjs$27.number, {
3168
3170
  displayName: "COUNT",
3169
3171
  async from(n) {
3170
3172
  if (!Number.isInteger(n) || n < 1) throw new Error(`Invalid vCPU count: ${n}. Must be a positive integer.`);
3171
3173
  return n;
3172
3174
  }
3173
3175
  });
3174
- const vcpus = import_cjs$23.option({
3176
+ const vcpus = import_cjs$27.option({
3175
3177
  long: "vcpus",
3176
- type: import_cjs$23.optional(vcpusType),
3178
+ type: import_cjs$27.optional(vcpusType),
3177
3179
  description: "Number of vCPUs to allocate (each vCPU includes 2048 MB of memory)"
3178
3180
  });
3179
3181
 
@@ -3242,7 +3244,7 @@ const formatDistanceLocale = {
3242
3244
  other: "almost {{count}} years"
3243
3245
  }
3244
3246
  };
3245
- const formatDistance$1 = (token$1, count, options) => {
3247
+ const formatDistance = (token$1, count, options) => {
3246
3248
  let result;
3247
3249
  const tokenValue = formatDistanceLocale[token$1];
3248
3250
  if (typeof tokenValue === "string") result = tokenValue;
@@ -3255,10 +3257,10 @@ const formatDistance$1 = (token$1, count, options) => {
3255
3257
 
3256
3258
  //#endregion
3257
3259
  //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/locale/_lib/buildFormatLongFn.js
3258
- function buildFormatLongFn(args$4) {
3260
+ function buildFormatLongFn(args$5) {
3259
3261
  return (options = {}) => {
3260
- const width = options.width ? String(options.width) : args$4.defaultWidth;
3261
- return args$4.formats[width] || args$4.formats[args$4.defaultWidth];
3262
+ const width = options.width ? String(options.width) : args$5.defaultWidth;
3263
+ return args$5.formats[width] || args$5.formats[args$5.defaultWidth];
3262
3264
  };
3263
3265
  }
3264
3266
 
@@ -3342,20 +3344,20 @@ const formatRelative = (token$1, _date, _baseDate, _options$1) => formatRelative
3342
3344
  /**
3343
3345
  * The tuple of localized month values. The first element represents January.
3344
3346
  */
3345
- function buildLocalizeFn(args$4) {
3347
+ function buildLocalizeFn(args$5) {
3346
3348
  return (value, options) => {
3347
3349
  const context = options?.context ? String(options.context) : "standalone";
3348
3350
  let valuesArray;
3349
- if (context === "formatting" && args$4.formattingValues) {
3350
- const defaultWidth = args$4.defaultFormattingWidth || args$4.defaultWidth;
3351
+ if (context === "formatting" && args$5.formattingValues) {
3352
+ const defaultWidth = args$5.defaultFormattingWidth || args$5.defaultWidth;
3351
3353
  const width = options?.width ? String(options.width) : defaultWidth;
3352
- valuesArray = args$4.formattingValues[width] || args$4.formattingValues[defaultWidth];
3354
+ valuesArray = args$5.formattingValues[width] || args$5.formattingValues[defaultWidth];
3353
3355
  } else {
3354
- const defaultWidth = args$4.defaultWidth;
3355
- const width = options?.width ? String(options.width) : args$4.defaultWidth;
3356
- valuesArray = args$4.values[width] || args$4.values[defaultWidth];
3356
+ const defaultWidth = args$5.defaultWidth;
3357
+ const width = options?.width ? String(options.width) : args$5.defaultWidth;
3358
+ valuesArray = args$5.values[width] || args$5.values[defaultWidth];
3357
3359
  }
3358
- const index = args$4.argumentCallback ? args$4.argumentCallback(value) : value;
3360
+ const index = args$5.argumentCallback ? args$5.argumentCallback(value) : value;
3359
3361
  return valuesArray[index];
3360
3362
  };
3361
3363
  }
@@ -3572,19 +3574,19 @@ const localize = {
3572
3574
 
3573
3575
  //#endregion
3574
3576
  //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/locale/_lib/buildMatchFn.js
3575
- function buildMatchFn(args$4) {
3576
- return (string$1, options = {}) => {
3577
+ function buildMatchFn(args$5) {
3578
+ return (string$2, options = {}) => {
3577
3579
  const width = options.width;
3578
- const matchPattern = width && args$4.matchPatterns[width] || args$4.matchPatterns[args$4.defaultMatchWidth];
3579
- const matchResult = string$1.match(matchPattern);
3580
+ const matchPattern = width && args$5.matchPatterns[width] || args$5.matchPatterns[args$5.defaultMatchWidth];
3581
+ const matchResult = string$2.match(matchPattern);
3580
3582
  if (!matchResult) return null;
3581
3583
  const matchedString = matchResult[0];
3582
- const parsePatterns = width && args$4.parsePatterns[width] || args$4.parsePatterns[args$4.defaultParseWidth];
3584
+ const parsePatterns = width && args$5.parsePatterns[width] || args$5.parsePatterns[args$5.defaultParseWidth];
3583
3585
  const key = Array.isArray(parsePatterns) ? findIndex(parsePatterns, (pattern) => pattern.test(matchedString)) : findKey(parsePatterns, (pattern) => pattern.test(matchedString));
3584
3586
  let value;
3585
- value = args$4.valueCallback ? args$4.valueCallback(key) : key;
3587
+ value = args$5.valueCallback ? args$5.valueCallback(key) : key;
3586
3588
  value = options.valueCallback ? options.valueCallback(value) : value;
3587
- const rest$1 = string$1.slice(matchedString.length);
3589
+ const rest$1 = string$2.slice(matchedString.length);
3588
3590
  return {
3589
3591
  value,
3590
3592
  rest: rest$1
@@ -3600,16 +3602,16 @@ function findIndex(array$1, predicate) {
3600
3602
 
3601
3603
  //#endregion
3602
3604
  //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/locale/_lib/buildMatchPatternFn.js
3603
- function buildMatchPatternFn(args$4) {
3604
- return (string$1, options = {}) => {
3605
- const matchResult = string$1.match(args$4.matchPattern);
3605
+ function buildMatchPatternFn(args$5) {
3606
+ return (string$2, options = {}) => {
3607
+ const matchResult = string$2.match(args$5.matchPattern);
3606
3608
  if (!matchResult) return null;
3607
3609
  const matchedString = matchResult[0];
3608
- const parseResult = string$1.match(args$4.parsePattern);
3610
+ const parseResult = string$2.match(args$5.parsePattern);
3609
3611
  if (!parseResult) return null;
3610
- let value = args$4.valueCallback ? args$4.valueCallback(parseResult[0]) : parseResult[0];
3612
+ let value = args$5.valueCallback ? args$5.valueCallback(parseResult[0]) : parseResult[0];
3611
3613
  value = options.valueCallback ? options.valueCallback(value) : value;
3612
- const rest$1 = string$1.slice(matchedString.length);
3614
+ const rest$1 = string$2.slice(matchedString.length);
3613
3615
  return {
3614
3616
  value,
3615
3617
  rest: rest$1
@@ -3764,7 +3766,7 @@ const match = {
3764
3766
  */
3765
3767
  const enUS = {
3766
3768
  code: "en-US",
3767
- formatDistance: formatDistance$1,
3769
+ formatDistance,
3768
3770
  formatLong,
3769
3771
  formatRelative,
3770
3772
  localize,
@@ -3782,6 +3784,15 @@ function getDefaultOptions() {
3782
3784
  return defaultOptions;
3783
3785
  }
3784
3786
 
3787
+ //#endregion
3788
+ //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/_lib/getRoundingMethod.js
3789
+ function getRoundingMethod(method) {
3790
+ return (number) => {
3791
+ const result = (method ? Math[method] : Math.trunc)(number);
3792
+ return result === 0 ? 0 : result;
3793
+ };
3794
+ }
3795
+
3785
3796
  //#endregion
3786
3797
  //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/constants.js
3787
3798
  /**
@@ -3815,6 +3826,18 @@ const daysInYear = 365.2425;
3815
3826
  const maxTime = Math.pow(10, 8) * 24 * 60 * 60 * 1e3;
3816
3827
  /**
3817
3828
  * @constant
3829
+ * @name millisecondsInMinute
3830
+ * @summary Milliseconds in 1 minute
3831
+ */
3832
+ const millisecondsInMinute = 6e4;
3833
+ /**
3834
+ * @constant
3835
+ * @name minutesInYear
3836
+ * @summary Minutes in 1 year.
3837
+ */
3838
+ const minutesInYear = 525600;
3839
+ /**
3840
+ * @constant
3818
3841
  * @name minutesInMonth
3819
3842
  * @summary Minutes in 1 month.
3820
3843
  */
@@ -4031,277 +4054,31 @@ function compareAsc(dateLeft, dateRight) {
4031
4054
  }
4032
4055
 
4033
4056
  //#endregion
4034
- //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/differenceInCalendarMonths.js
4035
- /**
4036
- * The {@link differenceInCalendarMonths} function options.
4037
- */
4038
- /**
4039
- * @name differenceInCalendarMonths
4040
- * @category Month Helpers
4041
- * @summary Get the number of calendar months between the given dates.
4042
- *
4043
- * @description
4044
- * Get the number of calendar months between the given dates.
4045
- *
4046
- * @param laterDate - The later date
4047
- * @param earlierDate - The earlier date
4048
- * @param options - An object with options
4049
- *
4050
- * @returns The number of calendar months
4051
- *
4052
- * @example
4053
- * // How many calendar months are between 31 January 2014 and 1 September 2014?
4054
- * const result = differenceInCalendarMonths(
4055
- * new Date(2014, 8, 1),
4056
- * new Date(2014, 0, 31)
4057
- * )
4058
- * //=> 8
4059
- */
4060
- function differenceInCalendarMonths(laterDate, earlierDate, options) {
4061
- const [laterDate_, earlierDate_] = normalizeDates(options?.in, laterDate, earlierDate);
4062
- const yearsDiff = laterDate_.getFullYear() - earlierDate_.getFullYear();
4063
- const monthsDiff = laterDate_.getMonth() - earlierDate_.getMonth();
4064
- return yearsDiff * 12 + monthsDiff;
4065
- }
4066
-
4067
- //#endregion
4068
- //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/endOfDay.js
4069
- /**
4070
- * The {@link endOfDay} function options.
4071
- */
4072
- /**
4073
- * @name endOfDay
4074
- * @category Day Helpers
4075
- * @summary Return the end of a day for the given date.
4076
- *
4077
- * @description
4078
- * Return the end of a day for the given date.
4079
- * The result will be in the local timezone.
4080
- *
4081
- * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).
4082
- * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments.
4083
- *
4084
- * @param date - The original date
4085
- * @param options - An object with options
4086
- *
4087
- * @returns The end of a day
4088
- *
4089
- * @example
4090
- * // The end of a day for 2 September 2014 11:55:00:
4091
- * const result = endOfDay(new Date(2014, 8, 2, 11, 55, 0))
4092
- * //=> Tue Sep 02 2014 23:59:59.999
4093
- */
4094
- function endOfDay(date, options) {
4095
- const _date = toDate(date, options?.in);
4096
- _date.setHours(23, 59, 59, 999);
4097
- return _date;
4098
- }
4099
-
4100
- //#endregion
4101
- //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/endOfMonth.js
4102
- /**
4103
- * The {@link endOfMonth} function options.
4104
- */
4105
- /**
4106
- * @name endOfMonth
4107
- * @category Month Helpers
4108
- * @summary Return the end of a month for the given date.
4109
- *
4110
- * @description
4111
- * Return the end of a month for the given date.
4112
- * The result will be in the local timezone.
4113
- *
4114
- * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).
4115
- * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments.
4116
- *
4117
- * @param date - The original date
4118
- * @param options - An object with options
4119
- *
4120
- * @returns The end of a month
4121
- *
4122
- * @example
4123
- * // The end of a month for 2 September 2014 11:55:00:
4124
- * const result = endOfMonth(new Date(2014, 8, 2, 11, 55, 0))
4125
- * //=> Tue Sep 30 2014 23:59:59.999
4126
- */
4127
- function endOfMonth(date, options) {
4128
- const _date = toDate(date, options?.in);
4129
- const month = _date.getMonth();
4130
- _date.setFullYear(_date.getFullYear(), month + 1, 0);
4131
- _date.setHours(23, 59, 59, 999);
4132
- return _date;
4133
- }
4134
-
4135
- //#endregion
4136
- //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/isLastDayOfMonth.js
4137
- /**
4138
- * @name isLastDayOfMonth
4139
- * @category Month Helpers
4140
- * @summary Is the given date the last day of a month?
4141
- *
4142
- * @description
4143
- * Is the given date the last day of a month?
4144
- *
4145
- * @param date - The date to check
4146
- * @param options - An object with options
4147
- *
4148
- * @returns The date is the last day of a month
4149
- *
4150
- * @example
4151
- * // Is 28 February 2014 the last day of a month?
4152
- * const result = isLastDayOfMonth(new Date(2014, 1, 28))
4153
- * //=> true
4154
- */
4155
- function isLastDayOfMonth(date, options) {
4156
- const _date = toDate(date, options?.in);
4157
- return +endOfDay(_date, options) === +endOfMonth(_date, options);
4158
- }
4159
-
4160
- //#endregion
4161
- //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/differenceInMonths.js
4162
- /**
4163
- * The {@link differenceInMonths} function options.
4164
- */
4165
- /**
4166
- * @name differenceInMonths
4167
- * @category Month Helpers
4168
- * @summary Get the number of full months between the given dates.
4169
- *
4170
- * @param laterDate - The later date
4171
- * @param earlierDate - The earlier date
4172
- * @param options - An object with options
4173
- *
4174
- * @returns The number of full months
4175
- *
4176
- * @example
4177
- * // How many full months are between 31 January 2014 and 1 September 2014?
4178
- * const result = differenceInMonths(new Date(2014, 8, 1), new Date(2014, 0, 31))
4179
- * //=> 7
4180
- */
4181
- function differenceInMonths(laterDate, earlierDate, options) {
4182
- const [laterDate_, workingLaterDate, earlierDate_] = normalizeDates(options?.in, laterDate, laterDate, earlierDate);
4183
- const sign = compareAsc(workingLaterDate, earlierDate_);
4184
- const difference = Math.abs(differenceInCalendarMonths(workingLaterDate, earlierDate_));
4185
- if (difference < 1) return 0;
4186
- if (workingLaterDate.getMonth() === 1 && workingLaterDate.getDate() > 27) workingLaterDate.setDate(30);
4187
- workingLaterDate.setMonth(workingLaterDate.getMonth() - sign * difference);
4188
- let isLastMonthNotFull = compareAsc(workingLaterDate, earlierDate_) === -sign;
4189
- if (isLastDayOfMonth(laterDate_) && difference === 1 && compareAsc(laterDate_, earlierDate_) === 1) isLastMonthNotFull = false;
4190
- const result = sign * (difference - +isLastMonthNotFull);
4191
- return result === 0 ? 0 : result;
4192
- }
4193
-
4194
- //#endregion
4195
- //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/_lib/getRoundingMethod.js
4196
- function getRoundingMethod(method) {
4197
- return (number) => {
4198
- const result = (method ? Math[method] : Math.trunc)(number);
4199
- return result === 0 ? 0 : result;
4200
- };
4201
- }
4202
-
4203
- //#endregion
4204
- //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/differenceInMilliseconds.js
4205
- /**
4206
- * @name differenceInMilliseconds
4207
- * @category Millisecond Helpers
4208
- * @summary Get the number of milliseconds between the given dates.
4209
- *
4210
- * @description
4211
- * Get the number of milliseconds between the given dates.
4212
- *
4213
- * @param laterDate - The later date
4214
- * @param earlierDate - The earlier date
4215
- *
4216
- * @returns The number of milliseconds
4217
- *
4218
- * @example
4219
- * // How many milliseconds are between
4220
- * // 2 July 2014 12:30:20.600 and 2 July 2014 12:30:21.700?
4221
- * const result = differenceInMilliseconds(
4222
- * new Date(2014, 6, 2, 12, 30, 21, 700),
4223
- * new Date(2014, 6, 2, 12, 30, 20, 600)
4224
- * )
4225
- * //=> 1100
4226
- */
4227
- function differenceInMilliseconds(laterDate, earlierDate) {
4228
- return +toDate(laterDate) - +toDate(earlierDate);
4229
- }
4230
-
4231
- //#endregion
4232
- //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/differenceInSeconds.js
4057
+ //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/formatDistanceStrict.js
4233
4058
  /**
4234
- * The {@link differenceInSeconds} function options.
4059
+ * The {@link formatDistanceStrict} function options.
4235
4060
  */
4236
4061
  /**
4237
- * @name differenceInSeconds
4238
- * @category Second Helpers
4239
- * @summary Get the number of seconds between the given dates.
4240
- *
4241
- * @description
4242
- * Get the number of seconds between the given dates.
4243
- *
4244
- * @param laterDate - The later date
4245
- * @param earlierDate - The earlier date
4246
- * @param options - An object with options.
4247
- *
4248
- * @returns The number of seconds
4249
- *
4250
- * @example
4251
- * // How many seconds are between
4252
- * // 2 July 2014 12:30:07.999 and 2 July 2014 12:30:20.000?
4253
- * const result = differenceInSeconds(
4254
- * new Date(2014, 6, 2, 12, 30, 20, 0),
4255
- * new Date(2014, 6, 2, 12, 30, 7, 999)
4256
- * )
4257
- * //=> 12
4258
- */
4259
- function differenceInSeconds(laterDate, earlierDate, options) {
4260
- const diff = differenceInMilliseconds(laterDate, earlierDate) / 1e3;
4261
- return getRoundingMethod(options?.roundingMethod)(diff);
4262
- }
4263
-
4264
- //#endregion
4265
- //#region ../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/formatDistance.js
4266
- /**
4267
- * The {@link formatDistance} function options.
4062
+ * The unit used to format the distance in {@link formatDistanceStrict}.
4268
4063
  */
4269
4064
  /**
4270
- * @name formatDistance
4065
+ * @name formatDistanceStrict
4271
4066
  * @category Common Helpers
4272
4067
  * @summary Return the distance between the given dates in words.
4273
4068
  *
4274
4069
  * @description
4275
- * Return the distance between the given dates in words.
4070
+ * Return the distance between the given dates in words, using strict units.
4071
+ * This is like `formatDistance`, but does not use helpers like 'almost', 'over',
4072
+ * 'less than' and the like.
4276
4073
  *
4277
- * | Distance between dates | Result |
4278
- * |-------------------------------------------------------------------|---------------------|
4279
- * | 0 ... 30 secs | less than a minute |
4280
- * | 30 secs ... 1 min 30 secs | 1 minute |
4281
- * | 1 min 30 secs ... 44 mins 30 secs | [2..44] minutes |
4282
- * | 44 mins ... 30 secs ... 89 mins 30 secs | about 1 hour |
4283
- * | 89 mins 30 secs ... 23 hrs 59 mins 30 secs | about [2..24] hours |
4284
- * | 23 hrs 59 mins 30 secs ... 41 hrs 59 mins 30 secs | 1 day |
4285
- * | 41 hrs 59 mins 30 secs ... 29 days 23 hrs 59 mins 30 secs | [2..30] days |
4286
- * | 29 days 23 hrs 59 mins 30 secs ... 44 days 23 hrs 59 mins 30 secs | about 1 month |
4287
- * | 44 days 23 hrs 59 mins 30 secs ... 59 days 23 hrs 59 mins 30 secs | about 2 months |
4288
- * | 59 days 23 hrs 59 mins 30 secs ... 1 yr | [2..12] months |
4289
- * | 1 yr ... 1 yr 3 months | about 1 year |
4290
- * | 1 yr 3 months ... 1 yr 9 month s | over 1 year |
4291
- * | 1 yr 9 months ... 2 yrs | almost 2 years |
4292
- * | N yrs ... N yrs 3 months | about N years |
4293
- * | N yrs 3 months ... N yrs 9 months | over N years |
4294
- * | N yrs 9 months ... N+1 yrs | almost N+1 years |
4295
- *
4296
- * With `options.includeSeconds == true`:
4297
- * | Distance between dates | Result |
4298
- * |------------------------|----------------------|
4299
- * | 0 secs ... 5 secs | less than 5 seconds |
4300
- * | 5 secs ... 10 secs | less than 10 seconds |
4301
- * | 10 secs ... 20 secs | less than 20 seconds |
4302
- * | 20 secs ... 40 secs | half a minute |
4303
- * | 40 secs ... 60 secs | less than a minute |
4304
- * | 60 secs ... 90 secs | 1 minute |
4074
+ * | Distance between dates | Result |
4075
+ * |------------------------|---------------------|
4076
+ * | 0 ... 59 secs | [0..59] seconds |
4077
+ * | 1 ... 59 mins | [1..59] minutes |
4078
+ * | 1 ... 23 hrs | [1..23] hours |
4079
+ * | 1 ... 29 days | [1..29] days |
4080
+ * | 1 ... 11 months | [1..11] months |
4081
+ * | 1 ... N years | [1..N] years |
4305
4082
  *
4306
4083
  * @param laterDate - The date
4307
4084
  * @param earlierDate - The date to compare with
@@ -4311,43 +4088,59 @@ function differenceInSeconds(laterDate, earlierDate, options) {
4311
4088
  *
4312
4089
  * @throws `date` must not be Invalid Date
4313
4090
  * @throws `baseDate` must not be Invalid Date
4091
+ * @throws `options.unit` must be 'second', 'minute', 'hour', 'day', 'month' or 'year'
4314
4092
  * @throws `options.locale` must contain `formatDistance` property
4315
4093
  *
4316
4094
  * @example
4317
4095
  * // What is the distance between 2 July 2014 and 1 January 2015?
4318
- * const result = formatDistance(new Date(2014, 6, 2), new Date(2015, 0, 1))
4096
+ * const result = formatDistanceStrict(new Date(2014, 6, 2), new Date(2015, 0, 2))
4319
4097
  * //=> '6 months'
4320
4098
  *
4321
4099
  * @example
4322
4100
  * // What is the distance between 1 January 2015 00:00:15
4323
- * // and 1 January 2015 00:00:00, including seconds?
4324
- * const result = formatDistance(
4101
+ * // and 1 January 2015 00:00:00?
4102
+ * const result = formatDistanceStrict(
4325
4103
  * new Date(2015, 0, 1, 0, 0, 15),
4326
- * new Date(2015, 0, 1, 0, 0, 0),
4327
- * { includeSeconds: true }
4104
+ * new Date(2015, 0, 1, 0, 0, 0)
4328
4105
  * )
4329
- * //=> 'less than 20 seconds'
4106
+ * //=> '15 seconds'
4330
4107
  *
4331
4108
  * @example
4332
4109
  * // What is the distance from 1 January 2016
4333
4110
  * // to 1 January 2015, with a suffix?
4334
- * const result = formatDistance(new Date(2015, 0, 1), new Date(2016, 0, 1), {
4111
+ * const result = formatDistanceStrict(new Date(2015, 0, 1), new Date(2016, 0, 1), {
4335
4112
  * addSuffix: true
4336
4113
  * })
4337
- * //=> 'about 1 year ago'
4114
+ * //=> '1 year ago'
4115
+ *
4116
+ * @example
4117
+ * // What is the distance from 1 January 2016
4118
+ * // to 1 January 2015, in minutes?
4119
+ * const result = formatDistanceStrict(new Date(2016, 0, 1), new Date(2015, 0, 1), {
4120
+ * unit: 'minute'
4121
+ * })
4122
+ * //=> '525600 minutes'
4123
+ *
4124
+ * @example
4125
+ * // What is the distance from 1 January 2015
4126
+ * // to 28 January 2015, in months, rounded up?
4127
+ * const result = formatDistanceStrict(new Date(2015, 0, 28), new Date(2015, 0, 1), {
4128
+ * unit: 'month',
4129
+ * roundingMethod: 'ceil'
4130
+ * })
4131
+ * //=> '1 month'
4338
4132
  *
4339
4133
  * @example
4340
4134
  * // What is the distance between 1 August 2016 and 1 January 2015 in Esperanto?
4341
4135
  * import { eoLocale } from 'date-fns/locale/eo'
4342
- * const result = formatDistance(new Date(2016, 7, 1), new Date(2015, 0, 1), {
4136
+ * const result = formatDistanceStrict(new Date(2016, 7, 1), new Date(2015, 0, 1), {
4343
4137
  * locale: eoLocale
4344
4138
  * })
4345
- * //=> 'pli ol 1 jaro'
4139
+ * //=> '1 jaro'
4346
4140
  */
4347
- function formatDistance(laterDate, earlierDate, options) {
4141
+ function formatDistanceStrict(laterDate, earlierDate, options) {
4348
4142
  const defaultOptions$1 = getDefaultOptions();
4349
4143
  const locale = options?.locale ?? defaultOptions$1.locale ?? enUS;
4350
- const minutesInAlmostTwoDays = 2520;
4351
4144
  const comparison = compareAsc(laterDate, earlierDate);
4352
4145
  if (isNaN(comparison)) throw new RangeError("Invalid time value");
4353
4146
  const localizeOptions = Object.assign({}, options, {
@@ -4355,41 +4148,37 @@ function formatDistance(laterDate, earlierDate, options) {
4355
4148
  comparison
4356
4149
  });
4357
4150
  const [laterDate_, earlierDate_] = normalizeDates(options?.in, ...comparison > 0 ? [earlierDate, laterDate] : [laterDate, earlierDate]);
4358
- const seconds = differenceInSeconds(earlierDate_, laterDate_);
4359
- const offsetInSeconds = (getTimezoneOffsetInMilliseconds(earlierDate_) - getTimezoneOffsetInMilliseconds(laterDate_)) / 1e3;
4360
- const minutes = Math.round((seconds - offsetInSeconds) / 60);
4361
- let months;
4362
- if (minutes < 2) if (options?.includeSeconds) if (seconds < 5) return locale.formatDistance("lessThanXSeconds", 5, localizeOptions);
4363
- else if (seconds < 10) return locale.formatDistance("lessThanXSeconds", 10, localizeOptions);
4364
- else if (seconds < 20) return locale.formatDistance("lessThanXSeconds", 20, localizeOptions);
4365
- else if (seconds < 40) return locale.formatDistance("halfAMinute", 0, localizeOptions);
4366
- else if (seconds < 60) return locale.formatDistance("lessThanXMinutes", 1, localizeOptions);
4367
- else return locale.formatDistance("xMinutes", 1, localizeOptions);
4368
- else if (minutes === 0) return locale.formatDistance("lessThanXMinutes", 1, localizeOptions);
4369
- else return locale.formatDistance("xMinutes", minutes, localizeOptions);
4370
- else if (minutes < 45) return locale.formatDistance("xMinutes", minutes, localizeOptions);
4371
- else if (minutes < 90) return locale.formatDistance("aboutXHours", 1, localizeOptions);
4372
- else if (minutes < minutesInDay) {
4373
- const hours = Math.round(minutes / 60);
4374
- return locale.formatDistance("aboutXHours", hours, localizeOptions);
4375
- } else if (minutes < minutesInAlmostTwoDays) return locale.formatDistance("xDays", 1, localizeOptions);
4376
- else if (minutes < minutesInMonth) {
4377
- const days = Math.round(minutes / minutesInDay);
4151
+ const roundingMethod = getRoundingMethod(options?.roundingMethod ?? "round");
4152
+ const milliseconds = earlierDate_.getTime() - laterDate_.getTime();
4153
+ const minutes = milliseconds / millisecondsInMinute;
4154
+ const dstNormalizedMinutes = (milliseconds - (getTimezoneOffsetInMilliseconds(earlierDate_) - getTimezoneOffsetInMilliseconds(laterDate_))) / millisecondsInMinute;
4155
+ const defaultUnit = options?.unit;
4156
+ let unit;
4157
+ if (!defaultUnit) if (minutes < 1) unit = "second";
4158
+ else if (minutes < 60) unit = "minute";
4159
+ else if (minutes < minutesInDay) unit = "hour";
4160
+ else if (dstNormalizedMinutes < minutesInMonth) unit = "day";
4161
+ else if (dstNormalizedMinutes < minutesInYear) unit = "month";
4162
+ else unit = "year";
4163
+ else unit = defaultUnit;
4164
+ if (unit === "second") {
4165
+ const seconds = roundingMethod(milliseconds / 1e3);
4166
+ return locale.formatDistance("xSeconds", seconds, localizeOptions);
4167
+ } else if (unit === "minute") {
4168
+ const roundedMinutes = roundingMethod(minutes);
4169
+ return locale.formatDistance("xMinutes", roundedMinutes, localizeOptions);
4170
+ } else if (unit === "hour") {
4171
+ const hours = roundingMethod(minutes / 60);
4172
+ return locale.formatDistance("xHours", hours, localizeOptions);
4173
+ } else if (unit === "day") {
4174
+ const days = roundingMethod(dstNormalizedMinutes / minutesInDay);
4378
4175
  return locale.formatDistance("xDays", days, localizeOptions);
4379
- } else if (minutes < minutesInMonth * 2) {
4380
- months = Math.round(minutes / minutesInMonth);
4381
- return locale.formatDistance("aboutXMonths", months, localizeOptions);
4382
- }
4383
- months = differenceInMonths(earlierDate_, laterDate_);
4384
- if (months < 12) {
4385
- const nearestMonth = Math.round(minutes / minutesInMonth);
4386
- return locale.formatDistance("xMonths", nearestMonth, localizeOptions);
4176
+ } else if (unit === "month") {
4177
+ const months = roundingMethod(dstNormalizedMinutes / minutesInMonth);
4178
+ return months === 12 && defaultUnit !== "month" ? locale.formatDistance("xYears", 1, localizeOptions) : locale.formatDistance("xMonths", months, localizeOptions);
4387
4179
  } else {
4388
- const monthsSinceStartOfYear = months % 12;
4389
- const years = Math.trunc(months / 12);
4390
- if (monthsSinceStartOfYear < 3) return locale.formatDistance("aboutXYears", years, localizeOptions);
4391
- else if (monthsSinceStartOfYear < 9) return locale.formatDistance("overXYears", years, localizeOptions);
4392
- else return locale.formatDistance("almostXYears", years + 1, localizeOptions);
4180
+ const years = roundingMethod(dstNormalizedMinutes / minutesInYear);
4181
+ return locale.formatDistance("xYears", years, localizeOptions);
4393
4182
  }
4394
4183
  }
4395
4184
 
@@ -4421,7 +4210,7 @@ function formatBytes(bytes) {
4421
4210
  }
4422
4211
  function timeAgo(date) {
4423
4212
  if (date === void 0) return "-";
4424
- return formatDistance(date, /* @__PURE__ */ new Date(), { addSuffix: true }).replace("about ", "").replace("less than ", "");
4213
+ return formatDistanceStrict(date, /* @__PURE__ */ new Date(), { addSuffix: true });
4425
4214
  }
4426
4215
  function table(opts) {
4427
4216
  const titles = Object.keys(opts.columns);
@@ -4447,6 +4236,19 @@ function formatRunDuration(d$1) {
4447
4236
  if (d$1 < 1e3) return `${d$1}ms`;
4448
4237
  return `${d$1 / 1e3}s`;
4449
4238
  }
4239
+ function formatNextCursorHint(cursor) {
4240
+ const args$5 = process.argv.slice(2);
4241
+ const filtered = [];
4242
+ for (let i = 0; i < args$5.length; i++) {
4243
+ if (args$5[i] === "--cursor") {
4244
+ i++;
4245
+ continue;
4246
+ }
4247
+ if (args$5[i].startsWith("--cursor=")) continue;
4248
+ filtered.push(args$5[i]);
4249
+ }
4250
+ return `\nMore results: sandbox ${filtered.join(" ")} --cursor ${cursor}`;
4251
+ }
4450
4252
 
4451
4253
  //#endregion
4452
4254
  //#region ../../node_modules/.pnpm/is-docker@3.0.0/node_modules/is-docker/index.js
@@ -4651,7 +4453,7 @@ async function defaultBrowser$1(_execFileAsync = execFileAsync$1) {
4651
4453
  //#endregion
4652
4454
  //#region ../../node_modules/.pnpm/default-browser@5.2.1/node_modules/default-browser/index.js
4653
4455
  const execFileAsync = promisify(execFile);
4654
- const titleize = (string$1) => string$1.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
4456
+ const titleize = (string$2) => string$2.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
4655
4457
  async function defaultBrowser() {
4656
4458
  if (process$1.platform === "darwin") {
4657
4459
  const id = await defaultBrowserId();
@@ -6509,9 +6311,9 @@ function ansiRegex({ onlyFirst = false } = {}) {
6509
6311
  //#endregion
6510
6312
  //#region ../../node_modules/.pnpm/strip-ansi@7.1.0/node_modules/strip-ansi/index.js
6511
6313
  const regex = ansiRegex();
6512
- function stripAnsi(string$1) {
6513
- if (typeof string$1 !== "string") throw new TypeError(`Expected a \`string\`, got \`${typeof string$1}\``);
6514
- return string$1.replace(regex, "");
6314
+ function stripAnsi(string$2) {
6315
+ if (typeof string$2 !== "string") throw new TypeError(`Expected a \`string\`, got \`${typeof string$2}\``);
6316
+ return string$2.replace(regex, "");
6515
6317
  }
6516
6318
 
6517
6319
  //#endregion
@@ -6890,7 +6692,7 @@ function createAbortController(reason) {
6890
6692
  signal: controller.signal,
6891
6693
  ignoreInterruptions: ignoreAbortErrors(controller.signal)
6892
6694
  };
6893
- }, (c) => c.abort(reason));
6695
+ }, (c$1) => c$1.abort(reason));
6894
6696
  }
6895
6697
 
6896
6698
  //#endregion
@@ -6952,10 +6754,10 @@ function _usingCtx() {
6952
6754
 
6953
6755
  //#endregion
6954
6756
  //#region src/commands/login.ts
6955
- var import_cjs$22 = /* @__PURE__ */ __toESM(require_cjs());
6757
+ var import_cjs$26 = /* @__PURE__ */ __toESM(require_cjs());
6956
6758
  init_source();
6957
- const debug$5 = createDebugger("sandbox:login");
6958
- const login = import_cjs$22.command({
6759
+ const debug$6 = createDebugger("sandbox:login");
6760
+ const login = import_cjs$26.command({
6959
6761
  name: "login",
6960
6762
  description: "Log in to the Sandbox CLI",
6961
6763
  args: {},
@@ -6996,13 +6798,13 @@ const login = import_cjs$22.command({
6996
6798
  oauth
6997
6799
  })) switch (event._tag) {
6998
6800
  case "Timeout":
6999
- debug$5(`Connection timeout. Slowing down, polling every ${event.newInterval / 1e3}s...`);
6801
+ debug$6(`Connection timeout. Slowing down, polling every ${event.newInterval / 1e3}s...`);
7000
6802
  break;
7001
6803
  case "SlowDown":
7002
- debug$5(`Authorization server requests to slow down. Polling every ${event.newInterval / 1e3}s...`);
6804
+ debug$6(`Authorization server requests to slow down. Polling every ${event.newInterval / 1e3}s...`);
7003
6805
  break;
7004
6806
  case "Response":
7005
- debug$5("Device Access Token response:", await event.response.text());
6807
+ debug$6("Device Access Token response:", await event.response.text());
7006
6808
  break;
7007
6809
  case "Error":
7008
6810
  error = event.error;
@@ -7019,7 +6821,15 @@ const login = import_cjs$22.command({
7019
6821
  if (error) {
7020
6822
  spinner.fail(`${source_default.red("error:")} ${error.message}`);
7021
6823
  process.exitCode = 1;
7022
- } else spinner.succeed(`${source_default.cyan("Congratulations!")} You are now signed in.`);
6824
+ } else {
6825
+ spinner.succeed(`${source_default.cyan("Congratulations!")} You are now signed in.`);
6826
+ const auth = getAuth();
6827
+ if (auth?.token) try {
6828
+ const { teamId, teamSlug, projectId, projectSlug } = await inferScope({ token: auth.token });
6829
+ process.stderr.write(source_default.dim(" │ ") + "team: " + source_default.cyan(teamSlug ?? teamId) + "\n");
6830
+ process.stderr.write(source_default.dim(" ╰ ") + "project: " + source_default.cyan(projectSlug ?? projectId) + "\n");
6831
+ } catch {}
6832
+ }
7023
6833
  } catch (_) {
7024
6834
  _usingCtx$1.e = _;
7025
6835
  } finally {
@@ -7165,15 +6975,28 @@ var require_dist = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/@ver
7165
6975
 
7166
6976
  //#endregion
7167
6977
  //#region src/args/auth.ts
7168
- var import_cjs$21 = /* @__PURE__ */ __toESM(require_cjs());
6978
+ var import_cjs$25 = /* @__PURE__ */ __toESM(require_cjs());
7169
6979
  init_source();
7170
6980
  var import_dist = require_dist();
7171
- const debug$4 = createDebugger("sandbox:args:auth");
7172
- const token = import_cjs$21.option({
6981
+ const debug$5 = createDebugger("sandbox:args:auth");
6982
+ /**
6983
+ * Timestamp (ms epoch) of the most recent auto-login. Used to identify
6984
+ * tokens so that the first 401/403 against a freshly-issued token can be
6985
+ * retried instead of surfaced to the user.
6986
+ */
6987
+ let freshTokenAcquiredAt;
6988
+ const FRESH_TOKEN_WINDOW_MS = 1e4;
6989
+ function isTokenFresh() {
6990
+ return freshTokenAcquiredAt !== void 0 && Date.now() - freshTokenAcquiredAt < FRESH_TOKEN_WINDOW_MS;
6991
+ }
6992
+ function markTokenAsFresh() {
6993
+ freshTokenAcquiredAt = Date.now();
6994
+ }
6995
+ const token = import_cjs$25.option({
7173
6996
  long: "token",
7174
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.",
7175
6998
  type: {
7176
- ...import_cjs$21.string,
6999
+ ...import_cjs$25.string,
7177
7000
  displayName: "pat_or_oidc",
7178
7001
  defaultValueIsSerializable: false,
7179
7002
  onMissing: async () => {
@@ -7181,18 +7004,20 @@ const token = import_cjs$21.option({
7181
7004
  if (process.env.VERCEL_OIDC_TOKEN) try {
7182
7005
  return await (0, import_dist.getVercelOidcToken)();
7183
7006
  } catch (cause) {
7184
- debug$4(`Failed to get or refresh OIDC token: ${getMessage(cause)}`);
7007
+ debug$5(`Failed to get or refresh OIDC token: ${getMessage(cause)}`);
7185
7008
  console.warn(source_default.yellow(`${source_default.bold("warn:")} failed to get or refresh OIDC token, using personal token authentication.`));
7186
7009
  }
7187
7010
  try {
7188
7011
  return await (0, import_dist.getVercelToken)();
7189
7012
  } catch (error) {
7190
7013
  if (error instanceof import_dist.AccessTokenMissingError || error instanceof import_dist.RefreshAccessTokenFailedError) {
7191
- debug$4(`CLI token unavailable (${error.name}), prompting for login...`);
7014
+ debug$5(`CLI token unavailable (${error.name}), prompting for login...`);
7192
7015
  console.warn(source_default.yellow(`${source_default.bold("notice:")} Your session has expired. Please log in again.`));
7193
7016
  await login.handler({});
7194
7017
  try {
7195
- return await (0, import_dist.getVercelToken)();
7018
+ const refreshed = await (0, import_dist.getVercelToken)();
7019
+ markTokenAsFresh();
7020
+ return refreshed;
7196
7021
  } catch (retryError) {
7197
7022
  throw new Error([
7198
7023
  `Failed to retrieve authentication token.`,
@@ -7244,8 +7069,8 @@ const ProjectConfiguration = jsonCodec(z.object({
7244
7069
  function readProjectConfiguration(cwd) {
7245
7070
  try {
7246
7071
  const pathname = path.join(cwd, ".vercel", "project.json");
7247
- const string$1 = fs$1.readFileSync(pathname, "utf8");
7248
- return ProjectConfiguration.decode(string$1);
7072
+ const string$2 = fs$1.readFileSync(pathname, "utf8");
7073
+ return ProjectConfiguration.decode(string$2);
7249
7074
  } catch {
7250
7075
  return null;
7251
7076
  }
@@ -7253,18 +7078,18 @@ function readProjectConfiguration(cwd) {
7253
7078
 
7254
7079
  //#endregion
7255
7080
  //#region src/util/infer-scope.ts
7256
- const debug$3 = createDebugger("sandbox:scope");
7257
- async function inferScope({ token: token$1, team: team$1 }) {
7081
+ const debug$4 = createDebugger("sandbox:scope");
7082
+ async function inferScope$1({ token: token$1, team: team$1 }) {
7258
7083
  const jwt = z.jwt().safeParse(token$1);
7259
7084
  if (jwt.success) {
7260
- debug$3("trying to infer scope from OIDC JWT");
7085
+ debug$4("trying to infer scope from OIDC JWT");
7261
7086
  const data = await inferFromJwt(jwt.data);
7262
- debug$3("Using scope from OIDC JWT", data);
7087
+ debug$4("Using scope from OIDC JWT", data);
7263
7088
  return data;
7264
7089
  }
7265
7090
  const projectJson = readProjectConfiguration(process.cwd());
7266
7091
  if (projectJson) {
7267
- debug$3("Using scope from project configuration", {
7092
+ debug$4("Using scope from project configuration", {
7268
7093
  owner: projectJson.orgId,
7269
7094
  project: projectJson.projectId
7270
7095
  });
@@ -7273,12 +7098,12 @@ async function inferScope({ token: token$1, team: team$1 }) {
7273
7098
  project: projectJson.projectId
7274
7099
  };
7275
7100
  }
7276
- debug$3("trying to infer scope from API token", {
7101
+ debug$4("trying to infer scope from API token", {
7277
7102
  token: token$1,
7278
7103
  team: team$1
7279
7104
  });
7280
7105
  const fromToken = await inferFromToken(token$1, team$1);
7281
- debug$3("Using scope from API token", fromToken);
7106
+ debug$4("Using scope from API token", fromToken);
7282
7107
  return fromToken;
7283
7108
  }
7284
7109
  const JwtSchema = z.object({
@@ -7302,43 +7127,81 @@ async function inferFromJwt(jwt) {
7302
7127
  };
7303
7128
  }
7304
7129
  async function inferFromToken(token$1, requestedTeam) {
7305
- const { teamId, projectId } = await Auth.inferScope({
7130
+ const { teamId, teamSlug, projectId, projectSlug } = await Auth.inferScope({
7306
7131
  token: token$1,
7307
7132
  teamId: requestedTeam
7308
7133
  });
7309
7134
  return {
7310
7135
  owner: teamId,
7311
7136
  project: projectId,
7312
- ownerSlug: teamId,
7313
- projectSlug: projectId
7137
+ ownerSlug: teamSlug ?? teamId,
7138
+ projectSlug: projectSlug ?? projectId
7314
7139
  };
7315
7140
  }
7316
7141
 
7142
+ //#endregion
7143
+ //#region src/util/fresh-auth-retry.ts
7144
+ const debug$3 = createDebugger("sandbox:fresh-auth-retry");
7145
+ /**
7146
+ * Run an async operation, transparently retrying on 401/403 when the auth
7147
+ * token was just acquired via auto-login.
7148
+ */
7149
+ async function withFreshAuthRetry(factory) {
7150
+ return retry(async (bail, attempt) => {
7151
+ try {
7152
+ return await factory();
7153
+ } catch (error) {
7154
+ const status = getAuthFailureStatus(error);
7155
+ if (status !== void 0 && isTokenFresh()) {
7156
+ debug$3(`fresh-auth retry attempt ${attempt} (status ${status})`);
7157
+ throw error;
7158
+ }
7159
+ bail(error);
7160
+ return;
7161
+ }
7162
+ }, {
7163
+ retries: 3,
7164
+ minTimeout: 250,
7165
+ factor: 2,
7166
+ maxRetryTime: 3e3
7167
+ });
7168
+ }
7169
+ /**
7170
+ * Returns the HTTP status if the error represents a 401 or 403.
7171
+ * Returns undefined for any other error.
7172
+ */
7173
+ function getAuthFailureStatus(error) {
7174
+ let status;
7175
+ if (error instanceof APIError) status = error.response.status;
7176
+ else if (error instanceof NotOk) status = error.response.statusCode;
7177
+ return status === 401 || status === 403 ? status : void 0;
7178
+ }
7179
+
7317
7180
  //#endregion
7318
7181
  //#region src/args/scope.ts
7319
- var import_cjs$20 = /* @__PURE__ */ __toESM(require_cjs());
7182
+ var import_cjs$24 = /* @__PURE__ */ __toESM(require_cjs());
7320
7183
  init_source();
7321
- const project = import_cjs$20.option({
7184
+ const project = import_cjs$24.option({
7322
7185
  long: "project",
7323
7186
  type: {
7324
- ...import_cjs$20.optional(import_cjs$20.string),
7187
+ ...import_cjs$24.optional(import_cjs$24.string),
7325
7188
  displayName: "my-project"
7326
7189
  },
7327
7190
  description: "The project name or ID to associate with the command. Can be inferred from VERCEL_OIDC_TOKEN."
7328
7191
  });
7329
7192
  /** Parser for the `--team` option. */
7330
- const teamParser = import_cjs$20.option({
7193
+ const teamParser = import_cjs$24.option({
7331
7194
  long: "team",
7332
7195
  type: {
7333
- ...import_cjs$20.optional(import_cjs$20.string),
7196
+ ...import_cjs$24.optional(import_cjs$24.string),
7334
7197
  displayName: "my-team"
7335
7198
  }
7336
7199
  });
7337
7200
  /** Parser for the `--scope` option. */
7338
- const scopeParser = import_cjs$20.option({
7201
+ const scopeParser = import_cjs$24.option({
7339
7202
  long: "scope",
7340
7203
  type: {
7341
- ...import_cjs$20.optional(import_cjs$20.string),
7204
+ ...import_cjs$24.optional(import_cjs$24.string),
7342
7205
  displayName: "my-team"
7343
7206
  },
7344
7207
  description: "The scope/team to associate with the command. Can be inferred from VERCEL_OIDC_TOKEN. [alias: --team]"
@@ -7381,10 +7244,10 @@ const scope = {
7381
7244
  let projectSlug;
7382
7245
  let teamSlug;
7383
7246
  if (typeof projectId.value === "undefined" || typeof teamId.value === "undefined") try {
7384
- const scope$1 = await inferScope({
7247
+ const scope$1 = await withFreshAuthRetry(() => inferScope$1({
7385
7248
  token: t.value,
7386
7249
  team: teamId.value
7387
- });
7250
+ }));
7388
7251
  projectId.value ?? (projectId.value = scope$1.project);
7389
7252
  teamId.value ?? (teamId.value = scope$1.owner);
7390
7253
  projectSlug = scope$1.projectSlug;
@@ -7427,7 +7290,7 @@ const scope = {
7427
7290
 
7428
7291
  //#endregion
7429
7292
  //#region package.json
7430
- var version = "3.0.0-beta.9";
7293
+ var version = "3.0.0";
7431
7294
 
7432
7295
  //#endregion
7433
7296
  //#region src/error.ts
@@ -7446,26 +7309,34 @@ init_source();
7446
7309
  * A {@link Sandbox} wrapper that adds user-agent headers and error handling.
7447
7310
  */
7448
7311
  const sandboxClient = {
7449
- get: (params) => withErrorHandling(Sandbox.get({
7312
+ get: (params) => withErrorHandling(() => Sandbox.get({
7450
7313
  fetch: fetchWithUserAgent,
7451
7314
  resume: false,
7452
7315
  ...params
7453
7316
  })),
7454
- create: (params) => withErrorHandling(Sandbox.create({
7317
+ create: (params) => withErrorHandling(() => Sandbox.create({
7455
7318
  fetch: fetchWithUserAgent,
7456
7319
  ...params
7457
7320
  })),
7458
- list: (params) => withErrorHandling(Sandbox.list({
7321
+ fork: (params) => withErrorHandling(() => Sandbox.fork({
7322
+ fetch: fetchWithUserAgent,
7323
+ ...params
7324
+ })),
7325
+ list: (params) => withErrorHandling(() => Sandbox.list({
7459
7326
  fetch: fetchWithUserAgent,
7460
7327
  ...params
7461
7328
  }))
7462
7329
  };
7463
7330
  const snapshotClient = {
7464
- list: (params) => withErrorHandling(Snapshot.list({
7331
+ list: (params) => withErrorHandling(() => Snapshot.list({
7465
7332
  fetch: fetchWithUserAgent,
7466
7333
  ...params
7467
7334
  })),
7468
- get: (params) => withErrorHandling(Snapshot.get({ ...params }))
7335
+ get: (params) => withErrorHandling(() => Snapshot.get({ ...params })),
7336
+ tree: (params) => withErrorHandling(() => Snapshot.tree({
7337
+ fetch: fetchWithUserAgent,
7338
+ ...params
7339
+ }))
7469
7340
  };
7470
7341
  const fetchWithUserAgent = (input, init$1) => {
7471
7342
  const headers = new Headers(init$1?.headers ?? (input && typeof input === "object" && "headers" in input ? input?.headers : {}));
@@ -7478,9 +7349,9 @@ const fetchWithUserAgent = (input, init$1) => {
7478
7349
  headers
7479
7350
  });
7480
7351
  };
7481
- async function withErrorHandling(promise) {
7352
+ async function withErrorHandling(factory) {
7482
7353
  try {
7483
- return await promise;
7354
+ return await withFreshAuthRetry(factory);
7484
7355
  } catch (error) {
7485
7356
  if (error instanceof APIError) return await handleApiError(error);
7486
7357
  throw error;
@@ -7518,9 +7389,9 @@ async function writeResponseToTemp({ response, text }) {
7518
7389
 
7519
7390
  //#endregion
7520
7391
  //#region src/args/snapshot-id.ts
7521
- var import_cjs$19 = /* @__PURE__ */ __toESM(require_cjs());
7392
+ var import_cjs$23 = /* @__PURE__ */ __toESM(require_cjs());
7522
7393
  init_source();
7523
- const snapshotId = import_cjs$19.extendType(import_cjs$19.string, {
7394
+ const snapshotId = import_cjs$23.extendType(import_cjs$23.string, {
7524
7395
  displayName: "snapshot_id",
7525
7396
  description: "The ID of the snapshot",
7526
7397
  async from(s$1) {
@@ -7529,11 +7400,83 @@ const snapshotId = import_cjs$19.extendType(import_cjs$19.string, {
7529
7400
  }
7530
7401
  });
7531
7402
 
7403
+ //#endregion
7404
+ //#region src/args/ports.ts
7405
+ var import_cjs$22 = /* @__PURE__ */ __toESM(require_cjs());
7406
+ init_source();
7407
+ const publishPorts = import_cjs$22.multioption({
7408
+ long: "publish-port",
7409
+ short: "p",
7410
+ description: "Publish sandbox port(s) to DOMAIN.vercel.run",
7411
+ type: import_cjs$22.array(import_cjs$22.extendType(import_cjs$22.number, {
7412
+ displayName: "PORT",
7413
+ async from(number) {
7414
+ if (!Number.isInteger(number) || number < 1024 || number > 65535) throw new Error([
7415
+ `Invalid port: ${number}.`,
7416
+ `${source_default.bold("hint:")} Ports must be integers between 1024-65535 (privileged ports 0-1023 are reserved).`,
7417
+ "Examples: 3000, 8443"
7418
+ ].join("\n"));
7419
+ return number;
7420
+ }
7421
+ }))
7422
+ });
7423
+
7424
+ //#endregion
7425
+ //#region src/types/snapshot-expiration.ts
7426
+ var import_cjs$21 = require_cjs();
7427
+ const SnapshotExpiration = (0, import_cjs$21.extendType)(import_cjs$21.string, {
7428
+ displayName: "DURATION|none",
7429
+ description: "A duration (e.g. 7d, 30d) or \"none\" for no expiration",
7430
+ async from(value) {
7431
+ if (value === "none") return "0";
7432
+ return Duration.from(value);
7433
+ }
7434
+ });
7435
+
7436
+ //#endregion
7437
+ //#region src/args/snapshot-retention.ts
7438
+ var import_cjs$20 = /* @__PURE__ */ __toESM(require_cjs());
7439
+ const snapshotExpiration = import_cjs$20.option({
7440
+ long: "snapshot-expiration",
7441
+ type: import_cjs$20.optional(SnapshotExpiration),
7442
+ description: "Default snapshot expiration. Use \"none\" or 0 for no expiration. Example: 7d, 30d"
7443
+ });
7444
+ const keepLastSnapshots = import_cjs$20.option({
7445
+ long: "keep-last-snapshots",
7446
+ type: import_cjs$20.optional(import_cjs$20.extendType(import_cjs$20.number, {
7447
+ displayName: "COUNT",
7448
+ async from(n) {
7449
+ if (!Number.isInteger(n) || n < 1 || n > 10) throw new Error(`Invalid --keep-last-snapshots value: ${n}. Must be an integer between 1 and 10.`);
7450
+ return n;
7451
+ }
7452
+ })),
7453
+ description: "Keep only the N most recent snapshots of this sandbox (1-10)."
7454
+ });
7455
+ const keepLastSnapshotsFor = import_cjs$20.option({
7456
+ long: "keep-last-snapshots-for",
7457
+ type: import_cjs$20.optional(SnapshotExpiration),
7458
+ description: "Expiration applied to kept snapshots. Use \"none\" or 0 for no expiration. Example: 7d, 30d"
7459
+ });
7460
+ const deleteEvictedSnapshots = import_cjs$20.option({
7461
+ long: "delete-evicted-snapshots",
7462
+ type: import_cjs$20.optional({
7463
+ ...import_cjs$20.oneOf(["true", "false"]),
7464
+ displayName: "true|false"
7465
+ }),
7466
+ description: "When \"true\" (the default), evicted snapshots are deleted immediately; when \"false\", they keep the default expiration."
7467
+ });
7468
+ const snapshotRetentionArgs = {
7469
+ snapshotExpiration,
7470
+ keepLastSnapshots,
7471
+ keepLastSnapshotsFor,
7472
+ deleteEvictedSnapshots
7473
+ };
7474
+
7532
7475
  //#endregion
7533
7476
  //#region src/args/sandbox-name.ts
7534
- var import_cjs$18 = /* @__PURE__ */ __toESM(require_cjs());
7477
+ var import_cjs$19 = /* @__PURE__ */ __toESM(require_cjs());
7535
7478
  init_source();
7536
- const sandboxName = import_cjs$18.extendType(import_cjs$18.string, {
7479
+ const sandboxName = import_cjs$19.extendType(import_cjs$19.string, {
7537
7480
  displayName: "name",
7538
7481
  description: "The name of the sandbox",
7539
7482
  async from(s$1) {
@@ -11241,15 +11184,15 @@ async function waitForOpen(ws) {
11241
11184
  //#endregion
11242
11185
  //#region src/util/print-command.ts
11243
11186
  init_source();
11244
- function printCommand(command$1, args$4) {
11245
- return source_default.gray(source_default.dim("$ ") + [command$1, ...args$4].join(" "));
11187
+ function printCommand(command$1, args$5) {
11188
+ return source_default.gray(source_default.dim("$ ") + [command$1, ...args$5].join(" "));
11246
11189
  }
11247
11190
 
11248
11191
  //#endregion
11249
11192
  //#region src/interactive-shell/extend-sandbox-timeout.ts
11250
- var import_ms$3 = /* @__PURE__ */ __toESM(require_ms());
11193
+ var import_ms$5 = /* @__PURE__ */ __toESM(require_ms());
11251
11194
  const debug$2 = createDebugger("sandbox:timeout");
11252
- const BUFFER = (0, import_ms$3.default)("10 seconds");
11195
+ const BUFFER = (0, import_ms$5.default)("10 seconds");
11253
11196
  async function extendSandboxTimeoutPeriodically(sandbox, signal) {
11254
11197
  const session = sandbox.currentSession();
11255
11198
  const timeout$1 = session.timeout;
@@ -11264,7 +11207,7 @@ async function extendSandboxTimeoutPeriodically(sandbox, signal) {
11264
11207
  debug$2(`sleeping for ${sleepMs}ms until next timeout extension`);
11265
11208
  await setTimeout$1(sleepMs, null, { signal });
11266
11209
  }
11267
- await sandbox.extendTimeout((0, import_ms$3.default)("5 minutes"));
11210
+ await sandbox.extendTimeout((0, import_ms$5.default)("5 minutes"));
11268
11211
  const updatedTimeout = session.timeout;
11269
11212
  if (updatedTimeout == null) return;
11270
11213
  const nextTick$1 = session.createdAt.getTime() + updatedTimeout;
@@ -11538,9 +11481,9 @@ function getStderrStream() {
11538
11481
 
11539
11482
  //#endregion
11540
11483
  //#region src/args/key-value-pair.ts
11541
- var import_cjs$17 = /* @__PURE__ */ __toESM(require_cjs());
11484
+ var import_cjs$18 = /* @__PURE__ */ __toESM(require_cjs());
11542
11485
  init_source();
11543
- const KeyValuePair = import_cjs$17.extendType(import_cjs$17.string, {
11486
+ const KeyValuePair = import_cjs$18.extendType(import_cjs$18.string, {
11544
11487
  displayName: "key=value",
11545
11488
  async from(input) {
11546
11489
  if (!input.includes("=")) return {
@@ -11554,7 +11497,7 @@ const KeyValuePair = import_cjs$17.extendType(import_cjs$17.string, {
11554
11497
  };
11555
11498
  }
11556
11499
  });
11557
- const ObjectFromKeyValue = import_cjs$17.extendType(import_cjs$17.array(KeyValuePair), { async from(input) {
11500
+ const ObjectFromKeyValue = import_cjs$18.extendType(import_cjs$18.array(KeyValuePair), { async from(input) {
11558
11501
  const obj = Object.create(null);
11559
11502
  const missingVars = [];
11560
11503
  for (const { key, value } of input) if (value === void 0) missingVars.push(key);
@@ -11568,27 +11511,27 @@ const ObjectFromKeyValue = import_cjs$17.extendType(import_cjs$17.array(KeyValue
11568
11511
 
11569
11512
  //#endregion
11570
11513
  //#region src/commands/exec.ts
11571
- var import_cjs$16 = /* @__PURE__ */ __toESM(require_cjs());
11514
+ var import_cjs$17 = /* @__PURE__ */ __toESM(require_cjs());
11572
11515
  init_source();
11573
- const args$3 = {
11574
- sandbox: import_cjs$16.positional({ type: sandboxName }),
11575
- command: import_cjs$16.positional({
11516
+ const args$4 = {
11517
+ sandbox: import_cjs$17.positional({ type: sandboxName }),
11518
+ command: import_cjs$17.positional({
11576
11519
  displayName: "command",
11577
11520
  description: "The executable to invoke"
11578
11521
  }),
11579
- args: import_cjs$16.rest({
11522
+ args: import_cjs$17.rest({
11580
11523
  displayName: "args",
11581
11524
  description: "arguments to pass to the command"
11582
11525
  }),
11583
- asSudo: import_cjs$16.flag({
11526
+ asSudo: import_cjs$17.flag({
11584
11527
  long: "sudo",
11585
11528
  description: "Give extended privileges to the command."
11586
11529
  }),
11587
- interactive: import_cjs$16.flag({
11530
+ interactive: import_cjs$17.flag({
11588
11531
  long: "interactive",
11589
11532
  short: "i",
11590
11533
  description: "Run the command in a secure interactive shell",
11591
- type: import_cjs$16.extendType(import_cjs$16.boolean, {
11534
+ type: import_cjs$17.extendType(import_cjs$17.boolean, {
11592
11535
  defaultValue() {
11593
11536
  return false;
11594
11537
  },
@@ -11598,22 +11541,22 @@ const args$3 = {
11598
11541
  }
11599
11542
  })
11600
11543
  }),
11601
- skipExtendingTimeout: import_cjs$16.flag({
11544
+ skipExtendingTimeout: import_cjs$17.flag({
11602
11545
  long: "no-extend-timeout",
11603
11546
  description: "Do not extend the sandbox timeout while running an interactive command. Only affects interactive executions."
11604
11547
  }),
11605
- tty: import_cjs$16.flag({
11548
+ tty: import_cjs$17.flag({
11606
11549
  long: "tty",
11607
11550
  short: "t",
11608
11551
  description: "Allocate a tty for an interactive command. This is a no-op."
11609
11552
  }),
11610
- cwd: import_cjs$16.option({
11553
+ cwd: import_cjs$17.option({
11611
11554
  long: "workdir",
11612
11555
  short: "w",
11613
11556
  description: "The working directory to run the command in",
11614
- type: import_cjs$16.optional(import_cjs$16.string)
11557
+ type: import_cjs$17.optional(import_cjs$17.string)
11615
11558
  }),
11616
- envVars: import_cjs$16.multioption({
11559
+ envVars: import_cjs$17.multioption({
11617
11560
  long: "env",
11618
11561
  short: "e",
11619
11562
  type: ObjectFromKeyValue,
@@ -11621,11 +11564,11 @@ const args$3 = {
11621
11564
  }),
11622
11565
  scope
11623
11566
  };
11624
- const exec = import_cjs$16.command({
11567
+ const exec = import_cjs$17.command({
11625
11568
  name: "exec",
11626
11569
  description: "Execute a command in an existing sandbox",
11627
- args: args$3,
11628
- async handler({ command: command$1, cwd, args: args$4, asSudo, sandbox: sandboxName$1, scope: { token: token$1, team: team$1, project: project$1 }, interactive, envVars, skipExtendingTimeout }) {
11570
+ args: args$4,
11571
+ async handler({ command: command$1, cwd, args: args$5, asSudo, sandbox: sandboxName$1, scope: { token: token$1, team: team$1, project: project$1 }, interactive, envVars, skipExtendingTimeout }) {
11629
11572
  const sandbox = typeof sandboxName$1 !== "string" ? sandboxName$1 : await sandboxClient.get({
11630
11573
  name: sandboxName$1,
11631
11574
  projectId: project$1,
@@ -11634,10 +11577,10 @@ const exec = import_cjs$16.command({
11634
11577
  __includeSystemRoutes: true
11635
11578
  });
11636
11579
  if (!interactive) {
11637
- console.error(printCommand(command$1, args$4));
11580
+ console.error(printCommand(command$1, args$5));
11638
11581
  const result = await sandbox.runCommand({
11639
11582
  cmd: command$1,
11640
- args: args$4,
11583
+ args: args$5,
11641
11584
  stderr: process.stderr,
11642
11585
  stdout: process.stdout,
11643
11586
  sudo: asSudo,
@@ -11648,7 +11591,7 @@ const exec = import_cjs$16.command({
11648
11591
  } else await startInteractiveShell({
11649
11592
  sandbox,
11650
11593
  cwd,
11651
- execution: [command$1, ...args$4],
11594
+ execution: [command$1, ...args$5],
11652
11595
  envVars,
11653
11596
  sudo: asSudo,
11654
11597
  skipExtendingTimeout
@@ -11658,9 +11601,9 @@ const exec = import_cjs$16.command({
11658
11601
 
11659
11602
  //#endregion
11660
11603
  //#region src/args/network-policy.ts
11661
- var import_cjs$15 = /* @__PURE__ */ __toESM(require_cjs());
11604
+ var import_cjs$16 = /* @__PURE__ */ __toESM(require_cjs());
11662
11605
  init_source();
11663
- const networkPolicyMode = import_cjs$15.extendType(import_cjs$15.string, {
11606
+ const networkPolicyMode = import_cjs$16.extendType(import_cjs$16.string, {
11664
11607
  displayName: "MODE",
11665
11608
  async from(value) {
11666
11609
  const validModes = ["allow-all", "deny-all"];
@@ -11668,28 +11611,28 @@ const networkPolicyMode = import_cjs$15.extendType(import_cjs$15.string, {
11668
11611
  return value;
11669
11612
  }
11670
11613
  });
11671
- const networkPolicy = import_cjs$15.option({
11614
+ const networkPolicy = import_cjs$16.option({
11672
11615
  long: "network-policy",
11673
11616
  description: `Network policy mode: "allow-all" or "deny-all"
11674
11617
  - allow-all: sandbox can access any website/domain
11675
11618
  - deny-all: sandbox has no network access
11676
11619
  Omit this option and use --allowed-domain / --allowed-cidr / --denied-cidr for custom policies.`,
11677
- type: import_cjs$15.optional(networkPolicyMode)
11620
+ type: import_cjs$16.optional(networkPolicyMode)
11678
11621
  });
11679
- const allowedDomains = import_cjs$15.multioption({
11622
+ const allowedDomains = import_cjs$16.multioption({
11680
11623
  long: "allowed-domain",
11681
11624
  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.`,
11682
- type: import_cjs$15.array(import_cjs$15.string)
11625
+ type: import_cjs$16.array(import_cjs$16.string)
11683
11626
  });
11684
- const allowedCIDRs = import_cjs$15.multioption({
11627
+ const allowedCIDRs = import_cjs$16.multioption({
11685
11628
  long: "allowed-cidr",
11686
11629
  description: `CIDR to allow traffic to (creates a custom network policy). Takes precedence over 'allowed-domain'.`,
11687
- type: import_cjs$15.array(import_cjs$15.string)
11630
+ type: import_cjs$16.array(import_cjs$16.string)
11688
11631
  });
11689
- const deniedCIDRs = import_cjs$15.multioption({
11632
+ const deniedCIDRs = import_cjs$16.multioption({
11690
11633
  long: "denied-cidr",
11691
11634
  description: `CIDR to deny traffic to (creates a custom network policy). Takes precedence over allowed domains/CIDRs.`,
11692
- type: import_cjs$15.array(import_cjs$15.string)
11635
+ type: import_cjs$16.array(import_cjs$16.string)
11693
11636
  });
11694
11637
  const networkPolicyArgs = {
11695
11638
  networkPolicy,
@@ -11712,8 +11655,8 @@ function resolveMode(networkPolicy$1, mode) {
11712
11655
  /**
11713
11656
  * Builds a NetworkPolicy from CLI arguments.
11714
11657
  */
11715
- function buildNetworkPolicy(args$4) {
11716
- const { networkPolicy: networkPolicy$1, allowedDomains: allowedDomains$1, allowedCIDRs: allowedCIDRs$1, deniedCIDRs: deniedCIDRs$1 } = args$4;
11658
+ function buildNetworkPolicy(args$5) {
11659
+ const { networkPolicy: networkPolicy$1, allowedDomains: allowedDomains$1, allowedCIDRs: allowedCIDRs$1, deniedCIDRs: deniedCIDRs$1 } = args$5;
11717
11660
  const hasListOptions = allowedDomains$1.length > 0 || allowedCIDRs$1.length > 0 || deniedCIDRs$1.length > 0;
11718
11661
  if (networkPolicy$1 && hasListOptions) throw new Error([`Cannot combine --network-policy=${networkPolicy$1} with --allowed-domain, --allowed-cidr, or --denied-cidr.`, `${source_default.bold("hint:")} Use --allowed-domain / --allowed-cidr / --denied-cidr without --network-policy for custom policies.`].join("\n"));
11719
11662
  if (hasListOptions) return {
@@ -11726,84 +11669,132 @@ function buildNetworkPolicy(args$4) {
11726
11669
  return networkPolicy$1 ?? "allow-all";
11727
11670
  }
11728
11671
 
11672
+ //#endregion
11673
+ //#region src/util/keep-last-snapshots.ts
11674
+ var import_ms$4 = /* @__PURE__ */ __toESM(require_ms());
11675
+ init_source();
11676
+ /**
11677
+ * Validates the `--keep-last-snapshots*` flag combination and builds the
11678
+ * payload object that the SDK expects, or returns `undefined` when no
11679
+ * retention policy was configured.
11680
+ *
11681
+ * Throws when `--keep-last-snapshots-for` or `--delete-evicted-snapshots` are
11682
+ * passed without `--keep-last-snapshots`.
11683
+ */
11684
+ function buildKeepLastSnapshotsPayload(input) {
11685
+ const { keepLastSnapshots: keepLastSnapshots$1, keepLastSnapshotsFor: keepLastSnapshotsFor$1, deleteEvictedSnapshots: deleteEvictedSnapshots$1 } = input;
11686
+ if (keepLastSnapshots$1 === void 0 && (keepLastSnapshotsFor$1 !== void 0 || deleteEvictedSnapshots$1 !== void 0)) throw new Error(["--keep-last-snapshots-for and --delete-evicted-snapshots require --keep-last-snapshots.", `${source_default.bold("hint:")} Pass --keep-last-snapshots <count> to enable the retention policy.`].join("\n"));
11687
+ if (keepLastSnapshots$1 === void 0) return;
11688
+ return {
11689
+ count: keepLastSnapshots$1,
11690
+ expiration: keepLastSnapshotsFor$1 !== void 0 ? (0, import_ms$4.default)(keepLastSnapshotsFor$1) : void 0,
11691
+ deleteEvicted: deleteEvictedSnapshots$1 !== void 0 ? deleteEvictedSnapshots$1 === "true" : void 0
11692
+ };
11693
+ }
11694
+
11695
+ //#endregion
11696
+ //#region src/util/print-sandbox-summary.ts
11697
+ init_source();
11698
+ /**
11699
+ * Print the "✅ Sandbox <name> <action>." summary that the create and fork
11700
+ * commands emit after a successful creation, including project/team and any
11701
+ * published port routes.
11702
+ *
11703
+ * - The sandbox name is written to stdout, everything else to stderr — so
11704
+ * `sandbox create | xargs -I {} sandbox exec {} -- ...` keeps working.
11705
+ * - Pass `action: "created"` for create, or `action: \`forked from ${chalk.cyan(source)}\``
11706
+ * for fork.
11707
+ */
11708
+ function printSandboxSummary(opts) {
11709
+ const { sandbox, scope: scope$1, action } = opts;
11710
+ const teamDisplay = scope$1.teamSlug ?? scope$1.team;
11711
+ const projectDisplay = scope$1.projectSlug ?? scope$1.project;
11712
+ const routes = sandbox.routes.filter((x) => x.port !== sandbox.interactivePort);
11713
+ const hasPorts = routes.length > 0;
11714
+ process.stderr.write("✅ Sandbox ");
11715
+ process.stdout.write(source_default.cyan(sandbox.name));
11716
+ process.stderr.write(" " + action + ".\n");
11717
+ process.stderr.write(source_default.dim(" │ ") + "team: " + source_default.cyan(teamDisplay) + "\n");
11718
+ if (hasPorts) {
11719
+ process.stderr.write(source_default.dim(" │ ") + "project: " + source_default.cyan(projectDisplay) + "\n");
11720
+ process.stderr.write(source_default.dim(" │ ") + "ports:\n");
11721
+ for (let i = 0; i < routes.length; i++) {
11722
+ const route = routes[i];
11723
+ const prefix$1 = i === routes.length - 1 ? source_default.dim(" ╰ ") : source_default.dim(" │ ");
11724
+ process.stderr.write(prefix$1 + "• " + route.port + " -> " + source_default.cyan(route.url) + "\n");
11725
+ }
11726
+ } else process.stderr.write(source_default.dim(" ╰ ") + "project: " + source_default.cyan(projectDisplay) + "\n");
11727
+ }
11728
+
11729
11729
  //#endregion
11730
11730
  //#region src/commands/create.ts
11731
- var import_cjs$14 = /* @__PURE__ */ __toESM(require_cjs());
11732
- var import_ms$2 = /* @__PURE__ */ __toESM(require_ms());
11731
+ var import_cjs$15 = /* @__PURE__ */ __toESM(require_cjs());
11732
+ var import_ms$3 = /* @__PURE__ */ __toESM(require_ms());
11733
11733
  init_source();
11734
- const args$2 = {
11735
- name: import_cjs$14.option({
11734
+ const args$3 = {
11735
+ name: import_cjs$15.option({
11736
11736
  long: "name",
11737
11737
  description: "A user-chosen name for the sandbox. It must be unique per project.",
11738
- type: import_cjs$14.optional(import_cjs$14.string)
11738
+ type: import_cjs$15.optional(import_cjs$15.string)
11739
11739
  }),
11740
- nonPersistent: import_cjs$14.flag({
11740
+ nonPersistent: import_cjs$15.flag({
11741
11741
  long: "non-persistent",
11742
11742
  description: "Disable automatic restore of the filesystem between sessions."
11743
11743
  }),
11744
11744
  runtime,
11745
11745
  timeout,
11746
11746
  vcpus,
11747
- ports: import_cjs$14.multioption({
11748
- long: "publish-port",
11749
- short: "p",
11750
- description: "Publish sandbox port(s) to DOMAIN.vercel.run",
11751
- type: import_cjs$14.array(import_cjs$14.extendType(import_cjs$14.number, {
11752
- displayName: "PORT",
11753
- async from(number) {
11754
- if (number < 1024 || number > 65535) throw new Error([
11755
- `Invalid port: ${number}.`,
11756
- `${source_default.bold("hint:")} Ports must be between 1024-65535 (privileged ports 0-1023 are reserved).`,
11757
- "╰▶ Examples: 3000, 8080, 8443"
11758
- ].join("\n"));
11759
- return number;
11760
- }
11761
- }))
11762
- }),
11763
- silent: import_cjs$14.flag({
11747
+ ports: publishPorts,
11748
+ silent: import_cjs$15.flag({
11764
11749
  long: "silent",
11765
11750
  description: "Don't write sandbox name to stdout"
11766
11751
  }),
11767
- snapshot: import_cjs$14.option({
11752
+ snapshot: import_cjs$15.option({
11768
11753
  long: "snapshot",
11769
11754
  short: "s",
11770
11755
  description: "Start the sandbox from a snapshot ID",
11771
- type: import_cjs$14.optional(snapshotId)
11756
+ type: import_cjs$15.optional(snapshotId)
11772
11757
  }),
11773
- connect: import_cjs$14.flag({
11758
+ connect: import_cjs$15.flag({
11774
11759
  long: "connect",
11775
11760
  description: "Start an interactive shell session after creating the sandbox"
11776
11761
  }),
11777
- envVars: import_cjs$14.multioption({
11762
+ envVars: import_cjs$15.multioption({
11778
11763
  long: "env",
11779
11764
  short: "e",
11780
11765
  type: ObjectFromKeyValue,
11781
11766
  description: "Default environment variables for sandbox commands"
11782
11767
  }),
11783
- tags: import_cjs$14.multioption({
11768
+ tags: import_cjs$15.multioption({
11784
11769
  long: "tag",
11785
11770
  short: "t",
11786
11771
  type: ObjectFromKeyValue,
11787
11772
  description: "Key-value tags to associate with the sandbox (e.g. --tag env=staging)"
11788
11773
  }),
11774
+ ...snapshotRetentionArgs,
11789
11775
  ...networkPolicyArgs,
11790
11776
  scope
11791
11777
  };
11792
- const create = import_cjs$14.command({
11778
+ const create = import_cjs$15.command({
11793
11779
  name: "create",
11794
11780
  description: "Create a sandbox in the specified account and project.",
11795
- args: args$2,
11781
+ args: args$3,
11796
11782
  examples: [{
11797
11783
  description: "Create and connect to a sandbox without a network access",
11798
11784
  command: `sandbox run --network-policy=none --connect`
11799
11785
  }],
11800
- async handler({ name, nonPersistent, ports, scope: scope$1, runtime: runtime$1, timeout: timeout$1, vcpus: vcpus$1, silent, snapshot: snapshot$1, connect: connect$2, envVars, tags, networkPolicy: networkPolicyMode$1, allowedDomains: allowedDomains$1, allowedCIDRs: allowedCIDRs$1, deniedCIDRs: deniedCIDRs$1 }) {
11786
+ async handler({ name, nonPersistent, ports, scope: scope$1, runtime: runtime$1, timeout: timeout$1, vcpus: vcpus$1, silent, snapshot: snapshot$1, connect: connect$2, envVars, tags, snapshotExpiration: snapshotExpiration$1, keepLastSnapshots: keepLastSnapshots$1, keepLastSnapshotsFor: keepLastSnapshotsFor$1, deleteEvictedSnapshots: deleteEvictedSnapshots$1, networkPolicy: networkPolicyMode$1, allowedDomains: allowedDomains$1, allowedCIDRs: allowedCIDRs$1, deniedCIDRs: deniedCIDRs$1 }) {
11801
11787
  const networkPolicy$1 = buildNetworkPolicy({
11802
11788
  networkPolicy: networkPolicyMode$1,
11803
11789
  allowedDomains: allowedDomains$1,
11804
11790
  allowedCIDRs: allowedCIDRs$1,
11805
11791
  deniedCIDRs: deniedCIDRs$1
11806
11792
  });
11793
+ const keepLastSnapshotsPayload = buildKeepLastSnapshotsPayload({
11794
+ keepLastSnapshots: keepLastSnapshots$1,
11795
+ keepLastSnapshotsFor: keepLastSnapshotsFor$1,
11796
+ deleteEvictedSnapshots: deleteEvictedSnapshots$1
11797
+ });
11807
11798
  const persistent = !nonPersistent;
11808
11799
  const resources = vcpus$1 ? { vcpus: vcpus$1 } : void 0;
11809
11800
  const tagsObj = Object.keys(tags).length > 0 ? tags : void 0;
@@ -11818,12 +11809,14 @@ const create = import_cjs$14.command({
11818
11809
  projectId: scope$1.project,
11819
11810
  token: scope$1.token,
11820
11811
  ports,
11821
- timeout: (0, import_ms$2.default)(timeout$1),
11812
+ timeout: (0, import_ms$3.default)(timeout$1),
11822
11813
  resources,
11823
11814
  networkPolicy: networkPolicy$1,
11824
11815
  env: envVars,
11825
11816
  tags: tagsObj,
11826
11817
  persistent,
11818
+ snapshotExpiration: snapshotExpiration$1 ? (0, import_ms$3.default)(snapshotExpiration$1) : void 0,
11819
+ keepLastSnapshots: keepLastSnapshotsPayload,
11827
11820
  __interactive: true
11828
11821
  }) : await sandboxClient.create({
11829
11822
  name,
@@ -11832,12 +11825,14 @@ const create = import_cjs$14.command({
11832
11825
  token: scope$1.token,
11833
11826
  ports,
11834
11827
  runtime: runtime$1,
11835
- timeout: (0, import_ms$2.default)(timeout$1),
11828
+ timeout: (0, import_ms$3.default)(timeout$1),
11836
11829
  resources,
11837
11830
  networkPolicy: networkPolicy$1,
11838
11831
  env: envVars,
11839
11832
  tags: tagsObj,
11840
11833
  persistent,
11834
+ snapshotExpiration: snapshotExpiration$1 ? (0, import_ms$3.default)(snapshotExpiration$1) : void 0,
11835
+ keepLastSnapshots: keepLastSnapshotsPayload,
11841
11836
  __interactive: true
11842
11837
  });
11843
11838
  spinner?.stop();
@@ -11846,25 +11841,11 @@ const create = import_cjs$14.command({
11846
11841
  `${source_default.bold("hint:")} This is an internal error. Please try again.`,
11847
11842
  "╰▶ Report this issue: https://github.com/vercel/sandbox/issues"
11848
11843
  ].join("\n"));
11849
- const routes = sandbox.routes.filter((x) => x.port !== sandbox.interactivePort);
11850
- if (!silent) {
11851
- const teamDisplay = scope$1.teamSlug ?? scope$1.team;
11852
- const projectDisplay = scope$1.projectSlug ?? scope$1.project;
11853
- const hasPorts = routes.length > 0;
11854
- process.stderr.write("✅ Sandbox ");
11855
- process.stdout.write(source_default.cyan(sandbox.name));
11856
- process.stderr.write(" created.\n");
11857
- process.stderr.write(source_default.dim(" │ ") + "team: " + source_default.cyan(teamDisplay) + "\n");
11858
- if (hasPorts) {
11859
- process.stderr.write(source_default.dim(" │ ") + "project: " + source_default.cyan(projectDisplay) + "\n");
11860
- process.stderr.write(source_default.dim(" │ ") + "ports:\n");
11861
- for (let i = 0; i < routes.length; i++) {
11862
- const route = routes[i];
11863
- const prefix$1 = i === routes.length - 1 ? source_default.dim(" ╰ ") : source_default.dim(" │ ");
11864
- process.stderr.write(prefix$1 + "• " + route.port + " -> " + source_default.cyan(route.url) + "\n");
11865
- }
11866
- } else process.stderr.write(source_default.dim(" ╰ ") + "project: " + source_default.cyan(projectDisplay) + "\n");
11867
- }
11844
+ if (!silent) printSandboxSummary({
11845
+ sandbox,
11846
+ scope: scope$1,
11847
+ action: "created"
11848
+ });
11868
11849
  if (connect$2) await exec.handler({
11869
11850
  scope: scope$1,
11870
11851
  asSudo: false,
@@ -11882,22 +11863,143 @@ const create = import_cjs$14.command({
11882
11863
  });
11883
11864
 
11884
11865
  //#endregion
11885
- //#region src/util/omit.ts
11886
- function omit(obj, ...keys) {
11887
- const result = { ...obj };
11888
- for (const key of keys) delete result[key];
11889
- return result;
11890
- }
11891
-
11892
- //#endregion
11893
- //#region src/commands/run.ts
11894
- var import_cjs$13 = /* @__PURE__ */ __toESM(require_cjs());
11895
- const args$1 = {
11896
- ...args$2,
11897
- ...omit(args$3, "sandbox"),
11898
- removeAfterUse: import_cjs$13.flag({
11899
- long: "rm",
11900
- description: "Automatically remove the sandbox when the command exits."
11866
+ //#region src/commands/fork.ts
11867
+ var import_cjs$14 = /* @__PURE__ */ __toESM(require_cjs());
11868
+ var import_ms$2 = /* @__PURE__ */ __toESM(require_ms());
11869
+ init_source();
11870
+ const args$2 = {
11871
+ source: import_cjs$14.positional({
11872
+ displayName: "source",
11873
+ description: "Name of the source sandbox to fork from.",
11874
+ type: sandboxName
11875
+ }),
11876
+ name: import_cjs$14.option({
11877
+ long: "name",
11878
+ description: "A user-chosen name for the forked sandbox. Must be unique per project.",
11879
+ type: import_cjs$14.optional(import_cjs$14.string)
11880
+ }),
11881
+ nonPersistent: import_cjs$14.flag({
11882
+ long: "non-persistent",
11883
+ description: "Disable automatic restore of the filesystem between sessions."
11884
+ }),
11885
+ timeout: import_cjs$14.option({
11886
+ long: "timeout",
11887
+ type: import_cjs$14.optional(Duration),
11888
+ description: "Override the maximum sandbox runtime (inherited from source if omitted). Example: 5m, 30m"
11889
+ }),
11890
+ vcpus,
11891
+ ports: publishPorts,
11892
+ silent: import_cjs$14.flag({
11893
+ long: "silent",
11894
+ description: "Don't write sandbox name to stdout"
11895
+ }),
11896
+ connect: import_cjs$14.flag({
11897
+ long: "connect",
11898
+ description: "Start an interactive shell session after creating the forked sandbox"
11899
+ }),
11900
+ envVars: import_cjs$14.multioption({
11901
+ long: "env",
11902
+ short: "e",
11903
+ type: ObjectFromKeyValue,
11904
+ description: "Environment variables to set on the fork. Env vars from the source sandbox are not copied (encrypted server-side)."
11905
+ }),
11906
+ tags: import_cjs$14.multioption({
11907
+ long: "tag",
11908
+ short: "t",
11909
+ type: ObjectFromKeyValue,
11910
+ description: "Key-value tags to associate with the fork. When provided, fully replaces the tags copied from the source (no per-key merge)."
11911
+ }),
11912
+ ...snapshotRetentionArgs,
11913
+ ...networkPolicyArgs,
11914
+ scope
11915
+ };
11916
+ const fork = import_cjs$14.command({
11917
+ name: "fork",
11918
+ description: "Fork an existing sandbox into a new one. Copies config (cpu, timeout, network policy, tags, etc.) from the source sandbox; env vars are NOT copied and must be re-supplied via --env.",
11919
+ args: args$2,
11920
+ examples: [{
11921
+ description: "Fork a sandbox with all config copied from the source",
11922
+ command: `sandbox fork my-source`
11923
+ }, {
11924
+ description: "Fork with a specific name and overridden vcpus",
11925
+ command: `sandbox fork my-source --name my-forked-sandbox --vcpus 4`
11926
+ }],
11927
+ async handler({ source, name, nonPersistent, ports, scope: scope$1, timeout: timeout$1, vcpus: vcpus$1, silent, connect: connect$2, envVars, tags, snapshotExpiration: snapshotExpiration$1, keepLastSnapshots: keepLastSnapshots$1, keepLastSnapshotsFor: keepLastSnapshotsFor$1, deleteEvictedSnapshots: deleteEvictedSnapshots$1, networkPolicy: networkPolicyMode$1, allowedDomains: allowedDomains$1, allowedCIDRs: allowedCIDRs$1, deniedCIDRs: deniedCIDRs$1 }) {
11928
+ const networkPolicy$1 = networkPolicyMode$1 !== void 0 || allowedDomains$1.length > 0 || allowedCIDRs$1.length > 0 || deniedCIDRs$1.length > 0 ? buildNetworkPolicy({
11929
+ networkPolicy: networkPolicyMode$1,
11930
+ allowedDomains: allowedDomains$1,
11931
+ allowedCIDRs: allowedCIDRs$1,
11932
+ deniedCIDRs: deniedCIDRs$1
11933
+ }) : void 0;
11934
+ const keepLastSnapshotsPayload = buildKeepLastSnapshotsPayload({
11935
+ keepLastSnapshots: keepLastSnapshots$1,
11936
+ keepLastSnapshotsFor: keepLastSnapshotsFor$1,
11937
+ deleteEvictedSnapshots: deleteEvictedSnapshots$1
11938
+ });
11939
+ const tagsObj = Object.keys(tags).length > 0 ? tags : void 0;
11940
+ const envObj = Object.keys(envVars).length > 0 ? envVars : void 0;
11941
+ const spinner = silent ? void 0 : ora(`Forking sandbox ${source_default.cyan(source)}...`).start();
11942
+ const sandbox = await sandboxClient.fork({
11943
+ sourceSandbox: source,
11944
+ teamId: scope$1.team,
11945
+ projectId: scope$1.project,
11946
+ token: scope$1.token,
11947
+ ...name !== void 0 && { name },
11948
+ ...ports.length > 0 && { ports },
11949
+ ...timeout$1 !== void 0 && { timeout: (0, import_ms$2.default)(timeout$1) },
11950
+ ...vcpus$1 !== void 0 && { resources: { vcpus: vcpus$1 } },
11951
+ ...networkPolicy$1 !== void 0 && { networkPolicy: networkPolicy$1 },
11952
+ ...envObj !== void 0 && { env: envObj },
11953
+ ...tagsObj !== void 0 && { tags: tagsObj },
11954
+ ...nonPersistent && { persistent: false },
11955
+ ...snapshotExpiration$1 !== void 0 && { snapshotExpiration: (0, import_ms$2.default)(snapshotExpiration$1) },
11956
+ ...keepLastSnapshotsPayload !== void 0 && { keepLastSnapshots: keepLastSnapshotsPayload },
11957
+ __interactive: true
11958
+ });
11959
+ spinner?.stop();
11960
+ if (!sandbox.interactivePort) throw new Error([
11961
+ `Sandbox forked but interactive port is missing.`,
11962
+ `${source_default.bold("hint:")} This is an internal error. Please try again.`,
11963
+ "╰▶ Report this issue: https://github.com/vercel/sandbox/issues"
11964
+ ].join("\n"));
11965
+ if (!silent) printSandboxSummary({
11966
+ sandbox,
11967
+ scope: scope$1,
11968
+ action: `forked from ${source_default.cyan(source)}`
11969
+ });
11970
+ if (connect$2) await exec.handler({
11971
+ scope: scope$1,
11972
+ asSudo: false,
11973
+ args: [],
11974
+ cwd: void 0,
11975
+ skipExtendingTimeout: false,
11976
+ envVars: {},
11977
+ command: "sh",
11978
+ interactive: true,
11979
+ tty: true,
11980
+ sandbox
11981
+ });
11982
+ return sandbox;
11983
+ }
11984
+ });
11985
+
11986
+ //#endregion
11987
+ //#region src/util/omit.ts
11988
+ function omit(obj, ...keys) {
11989
+ const result = { ...obj };
11990
+ for (const key of keys) delete result[key];
11991
+ return result;
11992
+ }
11993
+
11994
+ //#endregion
11995
+ //#region src/commands/run.ts
11996
+ var import_cjs$13 = /* @__PURE__ */ __toESM(require_cjs());
11997
+ const args$1 = {
11998
+ ...args$3,
11999
+ ...omit(args$4, "sandbox"),
12000
+ removeAfterUse: import_cjs$13.flag({
12001
+ long: "rm",
12002
+ description: "Automatically remove the sandbox when the command exits."
11901
12003
  }),
11902
12004
  stopAfterUse: import_cjs$13.flag({
11903
12005
  long: "stop",
@@ -11981,31 +12083,48 @@ const list = import_cjs$12.command({
11981
12083
  description: "Filter sandboxes by tag. Format: \"key=value\"",
11982
12084
  type: ObjectFromKeyValue
11983
12085
  }),
12086
+ limit: import_cjs$12.option({
12087
+ long: "limit",
12088
+ description: "Maximum number of sandboxes per page (default 50).",
12089
+ type: import_cjs$12.optional(import_cjs$12.number)
12090
+ }),
12091
+ cursor: import_cjs$12.option({
12092
+ long: "cursor",
12093
+ description: "Pagination cursor from a previous 'More results' hint.",
12094
+ type: import_cjs$12.optional(import_cjs$12.string)
12095
+ }),
11984
12096
  scope
11985
12097
  },
11986
- async handler({ scope: { token: token$1, team: team$1, project: project$1 }, all, namePrefix, sortBy, sortOrder, tags }) {
11987
- const sandboxes = await (async () => {
12098
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, all, namePrefix, sortBy, sortOrder, tags, limit, cursor }) {
12099
+ if (namePrefix) {
12100
+ if (sortBy && sortBy !== "name") {
12101
+ console.error(source_default.red("Error: --sort-by must be 'name' when using --name-prefix"));
12102
+ return;
12103
+ }
12104
+ sortBy = "name";
12105
+ }
12106
+ const { sandboxes, pagination } = await (async () => {
11988
12107
  try {
11989
12108
  var _usingCtx$1 = _usingCtx();
11990
12109
  const _spinner$1 = _usingCtx$1.u(acquireRelease(() => ora("Fetching sandboxes...").start(), (s$1) => s$1.stop()));
11991
- let { sandboxes: sandboxes$1 } = await sandboxClient.list({
12110
+ return sandboxClient.list({
11992
12111
  token: token$1,
11993
12112
  teamId: team$1,
11994
12113
  projectId: project$1,
11995
- limit: 50,
12114
+ limit: limit ?? 50,
12115
+ ...cursor && { cursor },
11996
12116
  ...namePrefix && { namePrefix },
11997
12117
  ...sortBy && { sortBy },
11998
12118
  ...sortOrder && { sortOrder },
11999
12119
  ...Object.keys(tags).length > 0 && { tags }
12000
12120
  });
12001
- if (!all) sandboxes$1 = sandboxes$1.filter((x) => x.status === "running");
12002
- return sandboxes$1;
12003
12121
  } catch (_) {
12004
12122
  _usingCtx$1.e = _;
12005
12123
  } finally {
12006
12124
  _usingCtx$1.d();
12007
12125
  }
12008
12126
  })();
12127
+ const displayedSandboxes = all ? sandboxes : sandboxes.filter((x) => x.status === "running");
12009
12128
  const memoryFormatter = new Intl.NumberFormat(void 0, {
12010
12129
  style: "unit",
12011
12130
  unit: "megabyte"
@@ -12029,9 +12148,10 @@ const list = import_cjs$12.command({
12029
12148
  columns["NETWORK (OUT/IN)"] = { value: (s$1) => s$1.totalEgressBytes || s$1.totalIngressBytes ? `${formatBytes(s$1.totalEgressBytes ?? 0)} / ${formatBytes(s$1.totalIngressBytes ?? 0)}` : "- / -" };
12030
12149
  }
12031
12150
  console.log(table({
12032
- rows: sandboxes,
12151
+ rows: displayedSandboxes,
12033
12152
  columns
12034
12153
  }));
12154
+ if (pagination.next !== null) console.log(formatNextCursorHint(pagination.next));
12035
12155
  }
12036
12156
  });
12037
12157
  const SandboxStatusColor = {
@@ -12051,18 +12171,118 @@ const connect = import_cjs$11.command({
12051
12171
  name: "connect",
12052
12172
  aliases: ["ssh", "shell"],
12053
12173
  description: "Start an interactive shell in an existing sandbox",
12054
- args: omit(args$3, "command", "args", "interactive", "tty"),
12055
- async handler(args$4) {
12174
+ args: omit(args$4, "command", "args", "interactive", "tty"),
12175
+ async handler(args$5) {
12056
12176
  return exec.handler({
12057
12177
  command: "sh",
12058
12178
  args: [],
12059
12179
  interactive: true,
12060
12180
  tty: true,
12061
- ...args$4
12181
+ ...args$5
12062
12182
  });
12063
12183
  }
12064
12184
  });
12065
12185
 
12186
+ //#endregion
12187
+ //#region src/commands/stop.ts
12188
+ var import_cjs$10 = /* @__PURE__ */ __toESM(require_cjs());
12189
+ init_source();
12190
+ function c(label, value) {
12191
+ return {
12192
+ label,
12193
+ value
12194
+ };
12195
+ }
12196
+ /** Visible width of a cell (label + value, no ANSI). */
12197
+ function cellWidth(cell) {
12198
+ return cell ? cell.label.length + cell.value.length : 0;
12199
+ }
12200
+ /** Print rows as a tree with column-aligned label: value pairs. */
12201
+ function printTree(rows) {
12202
+ const widths = [];
12203
+ for (const row of rows) for (let i = 0; i < row.length; i++) widths[i] = Math.max(widths[i] ?? 0, cellWidth(row[i]));
12204
+ for (let r = 0; r < rows.length; r++) {
12205
+ const prefix$1 = r === rows.length - 1 ? source_default.dim(" ╰ ") : source_default.dim(" │ ");
12206
+ const line = rows[r].map((cell, i) => {
12207
+ if (!cell) return " ".repeat(widths[i]);
12208
+ const pad = widths[i] - cell.label.length - cell.value.length;
12209
+ return cell.label + source_default.cyan(cell.value) + " ".repeat(Math.max(0, pad));
12210
+ }).join(" ").trimEnd();
12211
+ process.stderr.write(prefix$1 + line + "\n");
12212
+ }
12213
+ }
12214
+ function printStopResult(name, sandbox, sessionSnapshot) {
12215
+ process.stderr.write(source_default.green("✔") + " Sandbox stopped.\n");
12216
+ const snapshot$1 = sessionSnapshot.snapshot;
12217
+ printTree([
12218
+ [
12219
+ c("sandbox: ", name),
12220
+ sandbox.totalActiveCpuDurationMs != null ? c("active cpu: ", formatRunDuration(sandbox.totalActiveCpuDurationMs)) : null,
12221
+ sandbox.memory != null ? c("mem: ", `${sandbox.memory} MB`) : null,
12222
+ sandbox.totalDurationMs != null ? c("duration: ", formatRunDuration(sandbox.totalDurationMs)) : null,
12223
+ sandbox.totalIngressBytes != null ? c("ingress: ", formatBytes(sandbox.totalIngressBytes)) : null,
12224
+ sandbox.totalEgressBytes != null ? c("egress: ", formatBytes(sandbox.totalEgressBytes)) : null
12225
+ ],
12226
+ [
12227
+ c("session: ", sessionSnapshot.id),
12228
+ sessionSnapshot.activeCpuDurationMs != null ? c("active cpu: ", formatRunDuration(sessionSnapshot.activeCpuDurationMs)) : null,
12229
+ c("mem: ", `${sessionSnapshot.memory} MB`),
12230
+ sessionSnapshot.duration != null ? c("duration: ", formatRunDuration(sessionSnapshot.duration)) : null,
12231
+ sessionSnapshot.networkTransfer ? c("ingress: ", formatBytes(sessionSnapshot.networkTransfer.ingress)) : null,
12232
+ sessionSnapshot.networkTransfer ? c("egress: ", formatBytes(sessionSnapshot.networkTransfer.egress)) : null
12233
+ ],
12234
+ ...snapshot$1 ? [[
12235
+ c("snapshot: ", snapshot$1.id),
12236
+ c("size: ", formatBytes(snapshot$1.sizeBytes)),
12237
+ c("expires: ", snapshot$1.expiresAt ? timeAgo(snapshot$1.expiresAt) : "never")
12238
+ ]] : []
12239
+ ]);
12240
+ }
12241
+ const stop = import_cjs$10.command({
12242
+ name: "stop",
12243
+ description: "Stop the current session of one or more sandboxes",
12244
+ args: {
12245
+ sandboxName: import_cjs$10.positional({
12246
+ type: sandboxName,
12247
+ description: "A sandbox name to stop"
12248
+ }),
12249
+ sandboxNames: import_cjs$10.restPositionals({
12250
+ type: sandboxName,
12251
+ description: "More sandboxes to stop"
12252
+ }),
12253
+ scope
12254
+ },
12255
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, sandboxName: sandboxName$1, sandboxNames }) {
12256
+ const names = Array.from(new Set([sandboxName$1, ...sandboxNames]));
12257
+ const spinner = ora({
12258
+ text: names.length === 1 ? `Stopping ${names[0]}` : `Stopping ${names.length} sandboxes`,
12259
+ stream: process.stderr
12260
+ }).start();
12261
+ const results = await Promise.allSettled(names.map(async (name) => {
12262
+ const sandbox = await sandboxClient.get({
12263
+ token: token$1,
12264
+ teamId: team$1,
12265
+ projectId: project$1,
12266
+ name
12267
+ });
12268
+ return {
12269
+ name,
12270
+ sandbox,
12271
+ sessionSnapshot: await sandbox.stop()
12272
+ };
12273
+ }));
12274
+ spinner.stop();
12275
+ for (const result of results) if (result.status === "fulfilled") {
12276
+ const { name, sandbox, sessionSnapshot } = result.value;
12277
+ printStopResult(name, sandbox, sessionSnapshot);
12278
+ } else {
12279
+ const error = result.reason;
12280
+ process.stderr.write(source_default.red("✖") + ` ${error.message ?? error}\n`);
12281
+ process.exitCode = 1;
12282
+ }
12283
+ }
12284
+ });
12285
+
12066
12286
  //#endregion
12067
12287
  //#region ../../node_modules/.pnpm/eventemitter3@5.0.1/node_modules/eventemitter3/index.js
12068
12288
  var require_eventemitter3 = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/eventemitter3@5.0.1/node_modules/eventemitter3/index.js": ((exports, module) => {
@@ -12185,7 +12405,7 @@ var require_eventemitter3 = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.
12185
12405
  EventEmitter$1.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
12186
12406
  var evt = prefix ? prefix + event : event;
12187
12407
  if (!this._events[evt]) return false;
12188
- var listeners = this._events[evt], len = arguments.length, args$4, i;
12408
+ var listeners = this._events[evt], len = arguments.length, args$5, i;
12189
12409
  if (listeners.fn) {
12190
12410
  if (listeners.once) this.removeListener(event, listeners.fn, void 0, true);
12191
12411
  switch (len) {
@@ -12196,8 +12416,8 @@ var require_eventemitter3 = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.
12196
12416
  case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
12197
12417
  case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
12198
12418
  }
12199
- for (i = 1, args$4 = new Array(len - 1); i < len; i++) args$4[i - 1] = arguments[i];
12200
- listeners.fn.apply(listeners.context, args$4);
12419
+ for (i = 1, args$5 = new Array(len - 1); i < len; i++) args$5[i - 1] = arguments[i];
12420
+ listeners.fn.apply(listeners.context, args$5);
12201
12421
  } else {
12202
12422
  var length = listeners.length, j;
12203
12423
  for (i = 0; i < length; i++) {
@@ -12216,8 +12436,8 @@ var require_eventemitter3 = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.
12216
12436
  listeners[i].fn.call(listeners[i].context, a1, a2, a3);
12217
12437
  break;
12218
12438
  default:
12219
- if (!args$4) for (j = 1, args$4 = new Array(len - 1); j < len; j++) args$4[j - 1] = arguments[j];
12220
- listeners[i].fn.apply(listeners[i].context, args$4);
12439
+ if (!args$5) for (j = 1, args$5 = new Array(len - 1); j < len; j++) args$5[j - 1] = arguments[j];
12440
+ listeners[i].fn.apply(listeners[i].context, args$5);
12221
12441
  }
12222
12442
  }
12223
12443
  }
@@ -12314,9 +12534,9 @@ const isDumbTerminal = env.TERM === "dumb";
12314
12534
  const isCompatibleTerminal = tty$1 && tty$1.isatty && tty$1.isatty(1) && env.TERM && !isDumbTerminal;
12315
12535
  const isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
12316
12536
  const isColorSupported = !isDisabled && (isForced || isWindows && !isDumbTerminal || isCompatibleTerminal || isCI);
12317
- const replaceClose = (index, string$1, close, replace, head = string$1.substring(0, index) + replace, tail = string$1.substring(index + close.length), next = tail.indexOf(close)) => head + (next < 0 ? tail : replaceClose(next, tail, close, replace));
12318
- const clearBleed = (index, string$1, open$1, close, replace) => index < 0 ? open$1 + string$1 + close : open$1 + replaceClose(index, string$1, close, replace) + close;
12319
- const filterEmpty = (open$1, close, replace = open$1, at = open$1.length + 1) => (string$1) => string$1 || !(string$1 === "" || string$1 === void 0) ? clearBleed(("" + string$1).indexOf(close, at), string$1, open$1, close, replace) : "";
12537
+ const replaceClose = (index, string$2, close, replace, head = string$2.substring(0, index) + replace, tail = string$2.substring(index + close.length), next = tail.indexOf(close)) => head + (next < 0 ? tail : replaceClose(next, tail, close, replace));
12538
+ const clearBleed = (index, string$2, open$1, close, replace) => index < 0 ? open$1 + string$2 + close : open$1 + replaceClose(index, string$2, close, replace) + close;
12539
+ const filterEmpty = (open$1, close, replace = open$1, at = open$1.length + 1) => (string$2) => string$2 || !(string$2 === "" || string$2 === void 0) ? clearBleed(("" + string$2).indexOf(close, at), string$2, open$1, close, replace) : "";
12320
12540
  const init = (open$1, close, replace) => filterEmpty(`\x1b[${open$1}m`, `\x1b[${close}m`, replace);
12321
12541
  const colors = {
12322
12542
  reset: init(0, 0),
@@ -12630,8 +12850,8 @@ var EventManager = class {
12630
12850
  constructor() {
12631
12851
  this.emitter = new eventemitter3_default();
12632
12852
  }
12633
- emit(dispatch, args$4) {
12634
- this.emitter.emit(dispatch, args$4);
12853
+ emit(dispatch, args$5) {
12854
+ this.emitter.emit(dispatch, args$5);
12635
12855
  }
12636
12856
  on(dispatch, handler) {
12637
12857
  this.emitter.addListener(dispatch, handler);
@@ -12675,8 +12895,8 @@ function cleanseAnsi(chunk) {
12675
12895
  * @see {@link https://www.npmjs.com/package/colorette}
12676
12896
  */
12677
12897
  const color = createColors();
12678
- function indent(string$1, count) {
12679
- return string$1.replace(/^(?!\s*$)/gm, " ".repeat(count));
12898
+ function indent(string$2, count) {
12899
+ return string$2.replace(/^(?!\s*$)/gm, " ".repeat(count));
12680
12900
  }
12681
12901
  const FIGURES_MAIN = {
12682
12902
  warning: "⚠",
@@ -12783,9 +13003,9 @@ var ListrLogger = class {
12783
13003
  if (!message) return message;
12784
13004
  return this.applyFormat(`[${message}]`, options);
12785
13005
  }
12786
- splat(...args$4) {
12787
- const message = args$4.shift() ?? "";
12788
- return args$4.length === 0 ? message : splat(message, args$4);
13006
+ splat(...args$5) {
13007
+ const message = args$5.shift() ?? "";
13008
+ return args$5.length === 0 ? message : splat(message, args$5);
12789
13009
  }
12790
13010
  suffix(message, ...suffixes) {
12791
13011
  suffixes.filter(Boolean).forEach((suffix) => {
@@ -12867,12 +13087,12 @@ var ProcessOutputBuffer = class {
12867
13087
  get length() {
12868
13088
  return this.buffer.length;
12869
13089
  }
12870
- write(data, ...args$4) {
12871
- const callback = args$4[args$4.length - 1];
13090
+ write(data, ...args$5) {
13091
+ const callback = args$5[args$5.length - 1];
12872
13092
  this.buffer.push({
12873
13093
  time: Date.now(),
12874
13094
  stream: this.options?.stream,
12875
- entry: this.decoder.write(typeof data === "string" ? Buffer.from(data, typeof args$4[0] === "string" ? args$4[0] : void 0) : Buffer.from(data))
13095
+ entry: this.decoder.write(typeof data === "string" ? Buffer.from(data, typeof args$5[0] === "string" ? args$5[0] : void 0) : Buffer.from(data))
12876
13096
  });
12877
13097
  if (this.options?.limit) this.buffer = this.buffer.slice(-this.options.limit);
12878
13098
  if (typeof callback === "function") callback();
@@ -12900,8 +13120,8 @@ var ProcessOutputStream = class {
12900
13120
  this.buffer.reset();
12901
13121
  return buffer;
12902
13122
  }
12903
- write(...args$4) {
12904
- return this.method.apply(this.stream, args$4);
13123
+ write(...args$5) {
13124
+ return this.method.apply(this.stream, args$5);
12905
13125
  }
12906
13126
  };
12907
13127
  /**
@@ -13764,8 +13984,8 @@ function getRenderer(options) {
13764
13984
  * If the value itself is a function it will evaluate it with the passed in arguments,
13765
13985
  * elsewise it will directly return itself.
13766
13986
  */
13767
- function assertFunctionOrSelf(functionOrSelf, ...args$4) {
13768
- if (typeof functionOrSelf === "function") return functionOrSelf(...args$4);
13987
+ function assertFunctionOrSelf(functionOrSelf, ...args$5) {
13988
+ if (typeof functionOrSelf === "function") return functionOrSelf(...args$5);
13769
13989
  else return functionOrSelf;
13770
13990
  }
13771
13991
  const clone = (0, import_rfdc.default)({ circles: true });
@@ -14346,45 +14566,6 @@ var Listr = class {
14346
14566
  }
14347
14567
  };
14348
14568
 
14349
- //#endregion
14350
- //#region src/commands/stop.ts
14351
- var import_cjs$10 = /* @__PURE__ */ __toESM(require_cjs());
14352
- const stop = import_cjs$10.command({
14353
- name: "stop",
14354
- description: "Stop the current session of one or more sandboxes",
14355
- args: {
14356
- sandboxName: import_cjs$10.positional({
14357
- type: sandboxName,
14358
- description: "A sandbox name to stop"
14359
- }),
14360
- sandboxNames: import_cjs$10.restPositionals({
14361
- type: sandboxName,
14362
- description: "More sandboxes to stop"
14363
- }),
14364
- scope
14365
- },
14366
- async handler({ scope: { token: token$1, team: team$1, project: project$1 }, sandboxName: sandboxName$1, sandboxNames }) {
14367
- const tasks = Array.from(new Set([sandboxName$1, ...sandboxNames]), (sandboxName$2) => {
14368
- return {
14369
- title: `Stopping active session from ${sandboxName$2}`,
14370
- async task() {
14371
- await (await sandboxClient.get({
14372
- token: token$1,
14373
- teamId: team$1,
14374
- projectId: project$1,
14375
- name: sandboxName$2
14376
- })).stop();
14377
- }
14378
- };
14379
- });
14380
- try {
14381
- await new Listr(tasks, { concurrent: true }).run();
14382
- } catch {
14383
- process.exitCode = 1;
14384
- }
14385
- }
14386
- });
14387
-
14388
14569
  //#endregion
14389
14570
  //#region src/commands/remove.ts
14390
14571
  var import_cjs$9 = /* @__PURE__ */ __toESM(require_cjs());
@@ -14579,6 +14760,75 @@ const snapshot = import_cjs$6.command({
14579
14760
  }
14580
14761
  });
14581
14762
 
14763
+ //#endregion
14764
+ //#region src/util/snapshot-tree.ts
14765
+ init_source();
14766
+ function formatExpires(expiresAt) {
14767
+ if (expiresAt === void 0) return source_default.gray("never");
14768
+ const ms$6 = expiresAt - Date.now();
14769
+ if (ms$6 <= 0) return source_default.red("expired");
14770
+ const formatted = timeAgo(expiresAt);
14771
+ return ms$6 <= 3600 * 1e3 ? source_default.red(formatted) : source_default.green(formatted);
14772
+ }
14773
+ function renderNode(id, expiresAt, isCurrent) {
14774
+ const bullet = isCurrent ? source_default.magenta.bold("●") : source_default.magenta("●");
14775
+ const suffix = isCurrent ? ` ${source_default.green("◂ current")}` : "";
14776
+ return `${bullet} ${source_default.yellow(id)} expires: ${formatExpires(expiresAt)}${suffix}`;
14777
+ }
14778
+ function renderSiblings(siblings, count) {
14779
+ const lines = [];
14780
+ const shown = siblings.slice(0, 5);
14781
+ const totalCount = parseInt(count, 10);
14782
+ const hasPlus = count.endsWith("+");
14783
+ const remaining = totalCount - 1 - shown.length;
14784
+ for (let i = 0; i < shown.length; i++) {
14785
+ const connector = i === shown.length - 1 && remaining <= 0 ? "╰──" : "├──";
14786
+ lines.push(`│ ${connector} ${source_default.gray(shown[i].sourceSessionId)}`);
14787
+ }
14788
+ if (remaining > 0) {
14789
+ const suffix = hasPlus ? "+" : "";
14790
+ lines.push(`│ ╰── ${source_default.gray(`+${remaining}${suffix} more sandboxes`)}`);
14791
+ }
14792
+ return lines;
14793
+ }
14794
+ function renderSnapshotTree(params) {
14795
+ const { ancestors, descendants } = params;
14796
+ const hideCurrent = params.hideCurrent === true;
14797
+ const currentSnapshotId = hideCurrent ? void 0 : params.currentSnapshotId;
14798
+ const lines = [];
14799
+ const pushNode = (node, isCurrent) => {
14800
+ lines.push("│");
14801
+ lines.push(renderNode(node.snapshot.id, node.snapshot.expiresAt, isCurrent));
14802
+ if (node.siblings.length > 0) lines.push(...renderSiblings(node.siblings, node.count));
14803
+ };
14804
+ const descendantNodes = descendants.snapshots.filter((n) => n.snapshot.id !== currentSnapshotId);
14805
+ if (descendantNodes.length > 0) for (const node of [...descendantNodes].reverse()) pushNode(node, false);
14806
+ if (!hideCurrent) {
14807
+ const { currentSnapshotId: id, currentSnapshotExpiresAt } = params;
14808
+ pushNode(ancestors.snapshots.find((n) => n.snapshot.id === id) ?? descendants.snapshots.find((n) => n.snapshot.id === id) ?? {
14809
+ snapshot: {
14810
+ id,
14811
+ sourceSessionId: "",
14812
+ expiresAt: currentSnapshotExpiresAt
14813
+ },
14814
+ siblings: [],
14815
+ count: "1"
14816
+ }, true);
14817
+ }
14818
+ const ancestorNodes = ancestors.snapshots.filter((n) => n.snapshot.id !== currentSnapshotId);
14819
+ for (const node of ancestorNodes) pushNode(node, false);
14820
+ if (!hideCurrent) {
14821
+ const lastAncestor = ancestorNodes[ancestorNodes.length - 1];
14822
+ if (ancestors.pagination.next === null) {
14823
+ if (lastAncestor && !lastAncestor.snapshot.parentId || ancestorNodes.length === 0) {
14824
+ lines.push("│");
14825
+ lines.push(`${source_default.white("○")} ${source_default.gray("(root)")}`);
14826
+ }
14827
+ }
14828
+ }
14829
+ return lines.join("\n");
14830
+ }
14831
+
14582
14832
  //#endregion
14583
14833
  //#region src/commands/snapshots.ts
14584
14834
  var import_cjs$4 = /* @__PURE__ */ __toESM(require_cjs());
@@ -14599,22 +14849,32 @@ const list$2 = import_cjs$4.command({
14599
14849
  long: "sort-order",
14600
14850
  description: "Sort order. Options: asc, desc (default)",
14601
14851
  type: import_cjs$4.optional(import_cjs$4.oneOf(["asc", "desc"]))
14852
+ }),
14853
+ limit: import_cjs$4.option({
14854
+ long: "limit",
14855
+ description: "Maximum number of snapshots per page (default 50).",
14856
+ type: import_cjs$4.optional(import_cjs$4.number)
14857
+ }),
14858
+ cursor: import_cjs$4.option({
14859
+ long: "cursor",
14860
+ description: "Pagination cursor from a previous 'More results' hint.",
14861
+ type: import_cjs$4.optional(import_cjs$4.string)
14602
14862
  })
14603
14863
  },
14604
- async handler({ scope: { token: token$1, team: team$1, project: project$1 }, name, sortOrder }) {
14605
- const snapshots$1 = await (async () => {
14864
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, name, sortOrder, limit, cursor }) {
14865
+ const { snapshots: snapshots$1, pagination } = await (async () => {
14606
14866
  try {
14607
14867
  var _usingCtx$1 = _usingCtx();
14608
14868
  const _spinner$1 = _usingCtx$1.u(acquireRelease(() => ora("Fetching snapshots...").start(), (s$1) => s$1.stop()));
14609
- const { snapshots: snapshots$2 } = await snapshotClient.list({
14869
+ return snapshotClient.list({
14610
14870
  token: token$1,
14611
14871
  teamId: team$1,
14612
14872
  projectId: project$1,
14613
14873
  name,
14614
- limit: 50,
14874
+ limit: limit ?? 50,
14875
+ ...cursor && { cursor },
14615
14876
  ...sortOrder && { sortOrder }
14616
14877
  });
14617
- return snapshots$2;
14618
14878
  } catch (_) {
14619
14879
  _usingCtx$1.e = _;
14620
14880
  } finally {
@@ -14635,6 +14895,7 @@ const list$2 = import_cjs$4.command({
14635
14895
  ["SOURCE SESSION"]: { value: (s$1) => s$1.sourceSessionId }
14636
14896
  }
14637
14897
  }));
14898
+ if (pagination.next !== null) console.log(formatNextCursorHint(pagination.next));
14638
14899
  }
14639
14900
  });
14640
14901
  const get = import_cjs$4.command({
@@ -14718,12 +14979,154 @@ const remove$1 = import_cjs$4.command({
14718
14979
  }
14719
14980
  }
14720
14981
  });
14982
+ const tree = import_cjs$4.command({
14983
+ name: "tree",
14984
+ description: "Show the snapshot ancestry tree for a sandbox.",
14985
+ args: {
14986
+ scope,
14987
+ sandboxName: import_cjs$4.positional({
14988
+ type: sandboxName,
14989
+ description: "Sandbox name"
14990
+ }),
14991
+ sortOrder: import_cjs$4.option({
14992
+ long: "sort-order",
14993
+ description: "Sort order. Options: asc, desc (default). 'desc' walks ancestors, 'asc' walks descendants. Requires --cursor.",
14994
+ type: import_cjs$4.optional(import_cjs$4.oneOf(["asc", "desc"]))
14995
+ }),
14996
+ limit: import_cjs$4.option({
14997
+ long: "limit",
14998
+ description: "Maximum number of snapshots per page (1–10, default 10).",
14999
+ type: import_cjs$4.optional(import_cjs$4.number)
15000
+ }),
15001
+ cursor: import_cjs$4.option({
15002
+ long: "cursor",
15003
+ description: "Pagination cursor from a previous 'More ancestors' or 'More descendants' hint.",
15004
+ type: import_cjs$4.optional(import_cjs$4.string)
15005
+ })
15006
+ },
15007
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, sandboxName: name, sortOrder, limit, cursor }) {
15008
+ if (limit !== void 0 && (limit < 1 || limit > 10)) {
15009
+ console.error(source_default.red("Error: --limit must be between 1 and 10."));
15010
+ process.exitCode = 1;
15011
+ return;
15012
+ }
15013
+ if (sortOrder !== void 0 && !cursor) {
15014
+ console.error(source_default.red("Error: --sort-order requires --cursor."));
15015
+ process.exitCode = 1;
15016
+ return;
15017
+ }
15018
+ const pageLimit = limit ?? 10;
15019
+ if (cursor) {
15020
+ const effectiveSortOrder = sortOrder ?? "desc";
15021
+ const page = await (async () => {
15022
+ try {
15023
+ var _usingCtx4 = _usingCtx();
15024
+ const _spinner$1 = _usingCtx4.u(acquireRelease(() => ora("Fetching snapshot tree...").start(), (s$1) => s$1.stop()));
15025
+ return snapshotClient.tree({
15026
+ snapshotId: cursor,
15027
+ sortOrder: effectiveSortOrder,
15028
+ limit: pageLimit,
15029
+ token: token$1,
15030
+ teamId: team$1,
15031
+ projectId: project$1
15032
+ });
15033
+ } catch (_) {
15034
+ _usingCtx4.e = _;
15035
+ } finally {
15036
+ _usingCtx4.d();
15037
+ }
15038
+ })();
15039
+ const ancestors = effectiveSortOrder === "desc" ? page : {
15040
+ snapshots: [],
15041
+ pagination: {
15042
+ count: 0,
15043
+ next: null
15044
+ }
15045
+ };
15046
+ const descendants = effectiveSortOrder === "asc" ? page : {
15047
+ snapshots: [],
15048
+ pagination: {
15049
+ count: 0,
15050
+ next: null
15051
+ }
15052
+ };
15053
+ console.log(renderSnapshotTree({
15054
+ hideCurrent: true,
15055
+ ancestors,
15056
+ descendants
15057
+ }));
15058
+ if (page.pagination.next !== null) console.log(formatNextCursorHint(page.pagination.next));
15059
+ return;
15060
+ }
15061
+ const result = await (async () => {
15062
+ try {
15063
+ var _usingCtx5 = _usingCtx();
15064
+ const _spinner$1 = _usingCtx5.u(acquireRelease(() => ora("Fetching snapshot tree...").start(), (s$1) => s$1.stop()));
15065
+ const currentSnapshotId = (await sandboxClient.get({
15066
+ name,
15067
+ token: token$1,
15068
+ teamId: team$1,
15069
+ projectId: project$1
15070
+ })).currentSnapshotId;
15071
+ if (!currentSnapshotId) return null;
15072
+ const [currentSnap, ancestors, descendants] = await Promise.all([
15073
+ snapshotClient.get({
15074
+ snapshotId: currentSnapshotId,
15075
+ token: token$1,
15076
+ teamId: team$1,
15077
+ projectId: project$1
15078
+ }),
15079
+ snapshotClient.tree({
15080
+ snapshotId: currentSnapshotId,
15081
+ sortOrder: "desc",
15082
+ limit: pageLimit,
15083
+ token: token$1,
15084
+ teamId: team$1,
15085
+ projectId: project$1
15086
+ }),
15087
+ snapshotClient.tree({
15088
+ snapshotId: currentSnapshotId,
15089
+ sortOrder: "asc",
15090
+ limit: pageLimit,
15091
+ token: token$1,
15092
+ teamId: team$1,
15093
+ projectId: project$1
15094
+ })
15095
+ ]);
15096
+ return {
15097
+ currentSnap,
15098
+ currentSnapshotId,
15099
+ ancestors,
15100
+ descendants
15101
+ };
15102
+ } catch (_) {
15103
+ _usingCtx5.e = _;
15104
+ } finally {
15105
+ _usingCtx5.d();
15106
+ }
15107
+ })();
15108
+ if (!result) {
15109
+ console.log(source_default.yellow("No snapshots found for this sandbox."));
15110
+ return;
15111
+ }
15112
+ console.log(renderSnapshotTree({
15113
+ currentSnapshotId: result.currentSnapshotId,
15114
+ currentSnapshotExpiresAt: result.currentSnap.expiresAt?.getTime(),
15115
+ ancestors: result.ancestors,
15116
+ descendants: result.descendants
15117
+ }));
15118
+ const limitArg = limit !== void 0 ? ` --limit ${limit}` : "";
15119
+ if (result.ancestors.pagination.next !== null) console.log(`\nMore ancestors: sandbox snapshots tree ${name} --sort-order desc${limitArg} --cursor ${result.ancestors.pagination.next}`);
15120
+ if (result.descendants.pagination.next !== null) console.log(`More descendants: sandbox snapshots tree ${name} --sort-order asc${limitArg} --cursor ${result.descendants.pagination.next}`);
15121
+ }
15122
+ });
14721
15123
  const snapshots = (0, import_cjs$5.subcommands)({
14722
15124
  name: "snapshots",
14723
15125
  description: "Manage sandbox snapshots",
14724
15126
  cmds: {
14725
15127
  list: list$2,
14726
15128
  get,
15129
+ tree,
14727
15130
  delete: remove$1
14728
15131
  }
14729
15132
  });
@@ -14757,27 +15160,41 @@ const list$1 = import_cjs$2.command({
14757
15160
  description: "Sort order. Options: asc, desc (default)",
14758
15161
  type: import_cjs$2.optional(import_cjs$2.oneOf(["asc", "desc"]))
14759
15162
  }),
15163
+ limit: import_cjs$2.option({
15164
+ long: "limit",
15165
+ description: "Maximum number of sessions per page (default 50).",
15166
+ type: import_cjs$2.optional(import_cjs$2.number)
15167
+ }),
15168
+ cursor: import_cjs$2.option({
15169
+ long: "cursor",
15170
+ description: "Pagination cursor from a previous 'More results' hint.",
15171
+ type: import_cjs$2.optional(import_cjs$2.string)
15172
+ }),
14760
15173
  scope
14761
15174
  },
14762
- async handler({ scope: { token: token$1, team: team$1, project: project$1 }, all, sandbox: name, sortOrder }) {
15175
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, all, sandbox: name, sortOrder, limit, cursor }) {
14763
15176
  const sandbox = await sandboxClient.get({
14764
15177
  name,
14765
15178
  projectId: project$1,
14766
15179
  teamId: team$1,
14767
15180
  token: token$1
14768
15181
  });
14769
- let { sessions: sessions$1 } = await (async () => {
15182
+ const { sessions: sessions$1, pagination } = await (async () => {
14770
15183
  try {
14771
15184
  var _usingCtx$1 = _usingCtx();
14772
15185
  const _spinner$1 = _usingCtx$1.u(acquireRelease(() => ora("Fetching sessions...").start(), (s$1) => s$1.stop()));
14773
- return sandbox.listSessions({ ...sortOrder && { sortOrder } });
15186
+ return sandbox.listSessions({
15187
+ limit: limit ?? 50,
15188
+ ...cursor && { cursor },
15189
+ ...sortOrder && { sortOrder }
15190
+ });
14774
15191
  } catch (_) {
14775
15192
  _usingCtx$1.e = _;
14776
15193
  } finally {
14777
15194
  _usingCtx$1.d();
14778
15195
  }
14779
15196
  })();
14780
- if (!all) sessions$1 = sessions$1.filter((x) => x.status === "running");
15197
+ const displayedSessions = all ? sessions$1 : sessions$1.filter((x) => x.status === "running");
14781
15198
  const memoryFormatter = new Intl.NumberFormat(void 0, {
14782
15199
  style: "unit",
14783
15200
  unit: "megabyte"
@@ -14801,9 +15218,10 @@ const list$1 = import_cjs$2.command({
14801
15218
  columns["NETWORK (OUT/IN)"] = { value: (s$1) => s$1.networkTransfer?.egress || s$1.networkTransfer?.ingress ? `${formatBytes(s$1.networkTransfer?.egress ?? 0)} / ${formatBytes(s$1.networkTransfer?.ingress ?? 0)}` : "- / -" };
14802
15219
  }
14803
15220
  console.log(table({
14804
- rows: sessions$1,
15221
+ rows: displayedSessions,
14805
15222
  columns
14806
15223
  }));
15224
+ if (pagination.next !== null) console.log(formatNextCursorHint(pagination.next));
14807
15225
  }
14808
15226
  });
14809
15227
  const sessions = (0, import_cjs$3.subcommands)({
@@ -14928,6 +15346,245 @@ const persistentCommand = import_cjs$1.command({
14928
15346
  }
14929
15347
  }
14930
15348
  });
15349
+ const snapshotExpirationCommand = import_cjs$1.command({
15350
+ name: "snapshot-expiration",
15351
+ description: "Update the default snapshot expiration of a sandbox",
15352
+ args: {
15353
+ sandbox: import_cjs$1.positional({
15354
+ type: sandboxName,
15355
+ description: "Sandbox name to update"
15356
+ }),
15357
+ duration: import_cjs$1.positional({
15358
+ type: SnapshotExpiration,
15359
+ description: "Snapshot expiration duration (e.g. 7d, 30d) or \"none\" for no expiration"
15360
+ }),
15361
+ scope
15362
+ },
15363
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, sandbox: name, duration }) {
15364
+ const sandbox = await sandboxClient.get({
15365
+ name,
15366
+ projectId: project$1,
15367
+ teamId: team$1,
15368
+ token: token$1
15369
+ });
15370
+ const spinner = ora("Updating sandbox configuration...").start();
15371
+ try {
15372
+ await sandbox.update({ snapshotExpiration: (0, import_ms.default)(duration) });
15373
+ spinner.stop();
15374
+ const display = (0, import_ms.default)(duration) === 0 ? "none" : duration;
15375
+ process.stderr.write("✅ Configuration updated for sandbox " + source_default.cyan(name) + "\n");
15376
+ process.stderr.write(source_default.dim(" ╰ ") + "snapshot-expiration: " + source_default.cyan(display) + "\n");
15377
+ } catch (error) {
15378
+ spinner.stop();
15379
+ throw error;
15380
+ }
15381
+ }
15382
+ });
15383
+ const keepLastCountType = import_cjs$1.extendType(import_cjs$1.number, {
15384
+ displayName: "COUNT",
15385
+ async from(n) {
15386
+ if (!Number.isInteger(n) || n < 0 || n > 10) throw new Error(`Invalid count: ${n}. Must be an integer between 0 and 10 (0 removes the policy).`);
15387
+ return n;
15388
+ }
15389
+ });
15390
+ const keepLastSnapshotsCommand = import_cjs$1.command({
15391
+ name: "keep-last-snapshots",
15392
+ description: "Update the snapshot retention policy (keep only the N most recent snapshots) of a sandbox",
15393
+ args: {
15394
+ sandbox: import_cjs$1.positional({
15395
+ type: sandboxName,
15396
+ description: "Sandbox name to update"
15397
+ }),
15398
+ count: import_cjs$1.positional({
15399
+ type: keepLastCountType,
15400
+ description: "Number of most recent snapshots to keep (1-10). Pass 0 to remove the policy."
15401
+ }),
15402
+ scope
15403
+ },
15404
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, sandbox: name, count }) {
15405
+ const sandbox = await sandboxClient.get({
15406
+ name,
15407
+ projectId: project$1,
15408
+ teamId: team$1,
15409
+ token: token$1
15410
+ });
15411
+ const spinner = ora("Updating sandbox configuration...").start();
15412
+ try {
15413
+ if (count === 0) await sandbox.update({ keepLastSnapshots: null });
15414
+ else {
15415
+ const current = sandbox.keepLastSnapshots;
15416
+ await sandbox.update({ keepLastSnapshots: {
15417
+ count,
15418
+ expiration: current?.expiration,
15419
+ deleteEvicted: current?.deleteEvicted
15420
+ } });
15421
+ }
15422
+ spinner.stop();
15423
+ process.stderr.write("✅ Configuration updated for sandbox " + source_default.cyan(name) + "\n");
15424
+ process.stderr.write(source_default.dim(" ╰ ") + "keep-last-snapshots: " + source_default.cyan(count === 0 ? "cleared" : `count=${count}`) + "\n");
15425
+ } catch (error) {
15426
+ spinner.stop();
15427
+ throw error;
15428
+ }
15429
+ }
15430
+ });
15431
+ const keepLastSnapshotsForCommand = import_cjs$1.command({
15432
+ name: "keep-last-snapshots-for",
15433
+ description: "Update the expiration applied to snapshots kept by the retention policy",
15434
+ args: {
15435
+ sandbox: import_cjs$1.positional({
15436
+ type: sandboxName,
15437
+ description: "Sandbox name to update"
15438
+ }),
15439
+ duration: import_cjs$1.positional({
15440
+ type: SnapshotExpiration,
15441
+ description: "Expiration for kept snapshots. Use \"none\" or 0 for no expiration. Example: 7d, 30d"
15442
+ }),
15443
+ scope
15444
+ },
15445
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, sandbox: name, duration }) {
15446
+ const sandbox = await sandboxClient.get({
15447
+ name,
15448
+ projectId: project$1,
15449
+ teamId: team$1,
15450
+ token: token$1
15451
+ });
15452
+ const current = sandbox.keepLastSnapshots;
15453
+ if (!current) throw new Error(["No keep-last-snapshots policy is set on this sandbox.", `${source_default.bold("hint:")} Set a count first with: sandbox config keep-last-snapshots ${name} <count>`].join("\n"));
15454
+ const spinner = ora("Updating sandbox configuration...").start();
15455
+ try {
15456
+ await sandbox.update({ keepLastSnapshots: {
15457
+ count: current.count,
15458
+ expiration: (0, import_ms.default)(duration),
15459
+ deleteEvicted: current.deleteEvicted
15460
+ } });
15461
+ spinner.stop();
15462
+ const display = (0, import_ms.default)(duration) === 0 ? "none" : duration;
15463
+ process.stderr.write("✅ Configuration updated for sandbox " + source_default.cyan(name) + "\n");
15464
+ process.stderr.write(source_default.dim(" ╰ ") + "keep-last-snapshots-for: " + source_default.cyan(display) + "\n");
15465
+ } catch (error) {
15466
+ spinner.stop();
15467
+ throw error;
15468
+ }
15469
+ }
15470
+ });
15471
+ const deleteEvictedSnapshotsCommand = import_cjs$1.command({
15472
+ name: "delete-evicted-snapshots",
15473
+ description: "When \"true\" (the default), snapshots evicted by the keep-last-snapshots policy are deleted immediately; when \"false\", they keep the default expiration.",
15474
+ args: {
15475
+ sandbox: import_cjs$1.positional({
15476
+ type: sandboxName,
15477
+ description: "Sandbox name to update"
15478
+ }),
15479
+ value: import_cjs$1.positional({
15480
+ type: {
15481
+ ...import_cjs$1.oneOf(["true", "false"]),
15482
+ displayName: "true|false"
15483
+ },
15484
+ description: "Whether to delete evicted snapshots immediately (\"true\") or let them keep the default expiration (\"false\")."
15485
+ }),
15486
+ scope
15487
+ },
15488
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, sandbox: name, value }) {
15489
+ const sandbox = await sandboxClient.get({
15490
+ name,
15491
+ projectId: project$1,
15492
+ teamId: team$1,
15493
+ token: token$1
15494
+ });
15495
+ const current = sandbox.keepLastSnapshots;
15496
+ if (!current) throw new Error(["No keep-last-snapshots policy is set on this sandbox.", `${source_default.bold("hint:")} Set a count first with: sandbox config keep-last-snapshots ${name} <count>`].join("\n"));
15497
+ const spinner = ora("Updating sandbox configuration...").start();
15498
+ try {
15499
+ await sandbox.update({ keepLastSnapshots: {
15500
+ count: current.count,
15501
+ expiration: current.expiration,
15502
+ deleteEvicted: value === "true"
15503
+ } });
15504
+ spinner.stop();
15505
+ process.stderr.write("✅ Configuration updated for sandbox " + source_default.cyan(name) + "\n");
15506
+ process.stderr.write(source_default.dim(" ╰ ") + "delete-evicted-snapshots: " + source_default.cyan(value) + "\n");
15507
+ } catch (error) {
15508
+ spinner.stop();
15509
+ throw error;
15510
+ }
15511
+ }
15512
+ });
15513
+ const currentSnapshotCommand = import_cjs$1.command({
15514
+ name: "current-snapshot",
15515
+ description: "Update the current snapshot of a sandbox",
15516
+ args: {
15517
+ sandbox: import_cjs$1.positional({
15518
+ type: sandboxName,
15519
+ description: "Sandbox name to update"
15520
+ }),
15521
+ snapshotId: import_cjs$1.positional({
15522
+ type: snapshotId,
15523
+ description: "Snapshot ID to set as the current snapshot"
15524
+ }),
15525
+ scope
15526
+ },
15527
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, sandbox: name, snapshotId: snapshotId$1 }) {
15528
+ const sandbox = await sandboxClient.get({
15529
+ name,
15530
+ projectId: project$1,
15531
+ teamId: team$1,
15532
+ token: token$1
15533
+ });
15534
+ const spinner = ora("Updating sandbox configuration...").start();
15535
+ try {
15536
+ await sandbox.update({ currentSnapshotId: snapshotId$1 });
15537
+ spinner.stop();
15538
+ process.stderr.write("✅ Configuration updated for sandbox " + source_default.cyan(name) + "\n");
15539
+ process.stderr.write(source_default.dim(" ╰ ") + "current-snapshot: " + source_default.cyan(snapshotId$1) + "\n");
15540
+ } catch (error) {
15541
+ spinner.stop();
15542
+ if (error instanceof APIError && error.response.status === 404) throw new StyledError(`Snapshot '${snapshotId$1}' was not found or does not belong to this project.`, error);
15543
+ throw error;
15544
+ }
15545
+ }
15546
+ });
15547
+ const portsCommand = import_cjs$1.command({
15548
+ name: "ports",
15549
+ description: "Update the published ports of a sandbox. Replaces all existing published ports.",
15550
+ args: {
15551
+ sandbox: import_cjs$1.positional({
15552
+ type: sandboxName,
15553
+ description: "Sandbox name to update"
15554
+ }),
15555
+ ports: publishPorts,
15556
+ scope
15557
+ },
15558
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, sandbox: name, ports }) {
15559
+ const sandbox = await sandboxClient.get({
15560
+ name,
15561
+ projectId: project$1,
15562
+ teamId: team$1,
15563
+ token: token$1
15564
+ });
15565
+ const spinner = ora("Updating sandbox ports...").start();
15566
+ try {
15567
+ await sandbox.update({ ports });
15568
+ spinner.stop();
15569
+ process.stderr.write("✅ Ports updated for sandbox " + source_default.cyan(name) + "\n");
15570
+ if (ports.length === 0) process.stderr.write(source_default.dim(" ╰ ") + "all ports unpublished\n");
15571
+ else {
15572
+ const routes = getPublishedRoutes(sandbox);
15573
+ process.stderr.write(source_default.dim(" │ ") + "ports:\n");
15574
+ for (let i = 0; i < ports.length; i++) {
15575
+ const port = ports[i];
15576
+ const route = routes?.find((route$1) => route$1.port === port);
15577
+ const prefix$1 = i === ports.length - 1 ? source_default.dim(" ╰ ") : source_default.dim(" │ ");
15578
+ const url = route ? " -> " + source_default.cyan(route.url) : "";
15579
+ process.stderr.write(prefix$1 + "• " + port + url + "\n");
15580
+ }
15581
+ }
15582
+ } catch (error) {
15583
+ spinner.stop();
15584
+ throw error;
15585
+ }
15586
+ }
15587
+ });
14931
15588
  const listCommand = import_cjs$1.command({
14932
15589
  name: "list",
14933
15590
  description: "Display the current configuration of a sandbox",
@@ -14956,6 +15613,7 @@ const listCommand = import_cjs$1.command({
14956
15613
  }
14957
15614
  })();
14958
15615
  const networkPolicy$1 = typeof sandbox.networkPolicy === "string" ? sandbox.networkPolicy : "restricted";
15616
+ const tagsDisplay = sandbox.tags && Object.keys(sandbox.tags).length > 0 ? Object.entries(sandbox.tags).map(([k, v]) => `${k}=${v}`).join(", ") : "-";
14959
15617
  const rows = [
14960
15618
  {
14961
15619
  field: "vCPUs",
@@ -14972,6 +15630,26 @@ const listCommand = import_cjs$1.command({
14972
15630
  {
14973
15631
  field: "Network policy",
14974
15632
  value: String(networkPolicy$1)
15633
+ },
15634
+ {
15635
+ field: "Ports",
15636
+ value: formatPorts(sandbox)
15637
+ },
15638
+ {
15639
+ field: "Snapshot expiration",
15640
+ value: sandbox.snapshotExpiration != null && sandbox.snapshotExpiration > 0 ? (0, import_ms.default)(sandbox.snapshotExpiration, { long: true }) : sandbox.snapshotExpiration === 0 ? "none" : "-"
15641
+ },
15642
+ {
15643
+ field: "Keep last snapshots",
15644
+ value: formatKeepLastSnapshots(sandbox.keepLastSnapshots)
15645
+ },
15646
+ {
15647
+ field: "Current snapshot",
15648
+ value: sandbox.currentSnapshotId ?? "-"
15649
+ },
15650
+ {
15651
+ field: "Tags",
15652
+ value: tagsDisplay
14975
15653
  }
14976
15654
  ];
14977
15655
  console.log(table({
@@ -15027,6 +15705,63 @@ const networkPolicyCommand = import_cjs$1.command({
15027
15705
  }
15028
15706
  }
15029
15707
  });
15708
+ const tagsCommand = import_cjs$1.command({
15709
+ name: "tags",
15710
+ description: "Update the tags of a sandbox. Replaces all existing tags with the provided tags.",
15711
+ args: {
15712
+ sandbox: import_cjs$1.positional({
15713
+ type: sandboxName,
15714
+ description: "Sandbox name to update"
15715
+ }),
15716
+ tags: import_cjs$1.multioption({
15717
+ long: "tag",
15718
+ short: "t",
15719
+ type: ObjectFromKeyValue,
15720
+ description: "Key-value tags to set (e.g. --tag env=staging). Omit to clear all tags."
15721
+ }),
15722
+ scope
15723
+ },
15724
+ async handler({ scope: { token: token$1, team: team$1, project: project$1 }, sandbox: name, tags }) {
15725
+ const sandbox = await sandboxClient.get({
15726
+ name,
15727
+ projectId: project$1,
15728
+ teamId: team$1,
15729
+ token: token$1
15730
+ });
15731
+ const tagsObj = Object.keys(tags).length > 0 ? tags : {};
15732
+ const spinner = ora("Updating sandbox tags...").start();
15733
+ try {
15734
+ await sandbox.update({ tags: tagsObj });
15735
+ spinner.stop();
15736
+ process.stderr.write("✅ Tags updated for sandbox " + source_default.cyan(name) + "\n");
15737
+ const entries$1 = Object.entries(tagsObj);
15738
+ if (entries$1.length === 0) process.stderr.write(source_default.dim(" ╰ ") + "all tags cleared\n");
15739
+ else for (let i = 0; i < entries$1.length; i++) {
15740
+ const [k, v] = entries$1[i];
15741
+ const prefix$1 = i === entries$1.length - 1 ? source_default.dim(" ╰ ") : source_default.dim(" │ ");
15742
+ process.stderr.write(prefix$1 + source_default.cyan(k) + "=" + source_default.cyan(v) + "\n");
15743
+ }
15744
+ } catch (error) {
15745
+ spinner.stop();
15746
+ throw error;
15747
+ }
15748
+ }
15749
+ });
15750
+ function formatKeepLastSnapshots(keepLastSnapshots$1) {
15751
+ if (!keepLastSnapshots$1) return "-";
15752
+ const parts = [`count=${keepLastSnapshots$1.count}`];
15753
+ if (keepLastSnapshots$1.expiration !== void 0) parts.push(`for=${keepLastSnapshots$1.expiration === 0 ? "none" : (0, import_ms.default)(keepLastSnapshots$1.expiration, { long: true })}`);
15754
+ if (keepLastSnapshots$1.deleteEvicted !== void 0) parts.push(`delete-evicted-snapshots=${keepLastSnapshots$1.deleteEvicted}`);
15755
+ return parts.join(", ");
15756
+ }
15757
+ function formatPorts(sandbox) {
15758
+ const ports = getPublishedRoutes(sandbox)?.map((route) => route.port) ?? [];
15759
+ if (ports.length === 0) return "-";
15760
+ return ports.join(", ");
15761
+ }
15762
+ function getPublishedRoutes(sandbox) {
15763
+ return sandbox.routes.filter((route) => route.port !== sandbox.interactivePort);
15764
+ }
15030
15765
  const config = import_cjs$1.subcommands({
15031
15766
  name: "config",
15032
15767
  description: "View and update sandbox configuration",
@@ -15035,7 +15770,14 @@ const config = import_cjs$1.subcommands({
15035
15770
  vcpus: vcpusCommand,
15036
15771
  timeout: timeoutCommand,
15037
15772
  persistent: persistentCommand,
15038
- "network-policy": networkPolicyCommand
15773
+ "network-policy": networkPolicyCommand,
15774
+ "snapshot-expiration": snapshotExpirationCommand,
15775
+ "keep-last-snapshots": keepLastSnapshotsCommand,
15776
+ "keep-last-snapshots-for": keepLastSnapshotsForCommand,
15777
+ "delete-evicted-snapshots": deleteEvictedSnapshotsCommand,
15778
+ "current-snapshot": currentSnapshotCommand,
15779
+ ports: portsCommand,
15780
+ tags: tagsCommand
15039
15781
  }
15040
15782
  });
15041
15783
 
@@ -15049,6 +15791,7 @@ const app = (opts) => (0, import_cjs.subcommands)({
15049
15791
  cmds: {
15050
15792
  list,
15051
15793
  create,
15794
+ fork,
15052
15795
  config,
15053
15796
  copy: cp,
15054
15797
  exec,
@@ -15082,4 +15825,4 @@ const app = (opts) => (0, import_cjs.subcommands)({
15082
15825
 
15083
15826
  //#endregion
15084
15827
  export { source_exports as a, init_source as i, StyledError as n, require_cjs as r, app as t };
15085
- //# sourceMappingURL=app-Bxg8uoG5.mjs.map
15828
+ //# sourceMappingURL=app-BaHpC2zy.mjs.map