sandbox 3.2.0 → 3.2.2

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.
@@ -8,7 +8,7 @@ import tty, { isatty } from "node:tty";
8
8
  import createDebugger from "debug";
9
9
  import { promisify, stripVTControlCharacters } from "node:util";
10
10
  import { Buffer as Buffer$1 } from "node:buffer";
11
- import path from "node:path";
11
+ import Path from "node:path";
12
12
  import { fileURLToPath } from "node:url";
13
13
  import childProcess, { execFile } from "node:child_process";
14
14
  import fs, { constants, writeFile } from "node:fs/promises";
@@ -19,7 +19,7 @@ import { EOL } from "os";
19
19
  import { z } from "zod/v4";
20
20
  import { z as z$1 } from "zod";
21
21
  import retry from "async-retry";
22
- import { APIError, Sandbox, Snapshot } from "@vercel/sandbox";
22
+ import { APIError, Sandbox, Snapshot, StreamError } from "@vercel/sandbox";
23
23
  import { WebSocket } from "ws";
24
24
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
25
25
  import * as tty$1 from "tty";
@@ -1994,7 +1994,7 @@ var require_parser = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/cmd-
1994
1994
  };
1995
1995
  Object.defineProperty(exports, "__esModule", { value: true });
1996
1996
  exports.parse = parse$2;
1997
- const debug$7 = (0, __importDefault$1(__require("debug")).default)("cmd-ts:parser");
1997
+ const debug$9 = (0, __importDefault$1(__require("debug")).default)("cmd-ts:parser");
1998
1998
  /**
1999
1999
  * Create an AST from a token list
2000
2000
  *
@@ -2002,14 +2002,14 @@ var require_parser = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/cmd-
2002
2002
  * @param forceFlag Keys to force as flag. {@see ForceFlag} to read more about it.
2003
2003
  */
2004
2004
  function parse$2(tokens, forceFlag) {
2005
- if (debug$7.enabled) {
2005
+ if (debug$9.enabled) {
2006
2006
  const registered = {
2007
2007
  shortFlags: [...forceFlag.forceFlagShortNames],
2008
2008
  longFlags: [...forceFlag.forceFlagLongNames],
2009
2009
  shortOptions: [...forceFlag.forceOptionShortNames],
2010
2010
  longOptions: [...forceFlag.forceOptionLongNames]
2011
2011
  };
2012
- debug$7("Registered:", JSON.stringify(registered));
2012
+ debug$9("Registered:", JSON.stringify(registered));
2013
2013
  }
2014
2014
  const nodes = [];
2015
2015
  let index = 0;
@@ -2140,9 +2140,9 @@ var require_parser = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/cmd-
2140
2140
  }
2141
2141
  index++;
2142
2142
  }
2143
- if (debug$7.enabled) {
2143
+ if (debug$9.enabled) {
2144
2144
  const objectNodes = nodes.map((node) => ({ [node.type]: node.raw }));
2145
- debug$7("Parsed items:", JSON.stringify(objectNodes));
2145
+ debug$9("Parsed items:", JSON.stringify(objectNodes));
2146
2146
  }
2147
2147
  return nodes;
2148
2148
  }
@@ -4477,8 +4477,8 @@ async function defaultBrowser() {
4477
4477
  //#endregion
4478
4478
  //#region ../../node_modules/.pnpm/open@10.2.0/node_modules/open/index.js
4479
4479
  const execFile$1 = promisify(childProcess.execFile);
4480
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
4481
- const localXdgOpenPath = path.join(__dirname, "xdg-open");
4480
+ const __dirname = Path.dirname(fileURLToPath(import.meta.url));
4481
+ const localXdgOpenPath = Path.join(__dirname, "xdg-open");
4482
4482
  const { platform: platform$1, arch } = process$1;
4483
4483
  /**
4484
4484
  Get the default browser name in Windows from WSL.
@@ -6752,7 +6752,7 @@ function _usingCtx() {
6752
6752
  //#region src/commands/login.ts
6753
6753
  var import_cjs$26 = /* @__PURE__ */ __toESM(require_cjs());
6754
6754
  init_source();
6755
- const debug$6 = createDebugger("sandbox:login");
6755
+ const debug$8 = createDebugger("sandbox:login");
6756
6756
  const login = import_cjs$26.command({
6757
6757
  name: "login",
6758
6758
  description: "Log in to the Sandbox CLI",
@@ -6794,13 +6794,13 @@ const login = import_cjs$26.command({
6794
6794
  oauth
6795
6795
  })) switch (event._tag) {
6796
6796
  case "Timeout":
6797
- debug$6(`Connection timeout. Slowing down, polling every ${event.newInterval / 1e3}s...`);
6797
+ debug$8(`Connection timeout. Slowing down, polling every ${event.newInterval / 1e3}s...`);
6798
6798
  break;
6799
6799
  case "SlowDown":
6800
- debug$6(`Authorization server requests to slow down. Polling every ${event.newInterval / 1e3}s...`);
6800
+ debug$8(`Authorization server requests to slow down. Polling every ${event.newInterval / 1e3}s...`);
6801
6801
  break;
6802
6802
  case "Response":
6803
- debug$6("Device Access Token response:", await event.response.text());
6803
+ debug$8("Device Access Token response:", await event.response.text());
6804
6804
  break;
6805
6805
  case "Error":
6806
6806
  error = event.error;
@@ -6974,7 +6974,7 @@ var require_dist = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@verce
6974
6974
  var import_cjs$25 = /* @__PURE__ */ __toESM(require_cjs());
6975
6975
  init_source();
6976
6976
  var import_dist = require_dist();
6977
- const debug$5 = createDebugger("sandbox:args:auth");
6977
+ const debug$7 = createDebugger("sandbox:args:auth");
6978
6978
  /**
6979
6979
  * Timestamp (ms epoch) of the most recent auto-login. Used to identify
6980
6980
  * tokens so that the first 401/403 against a freshly-issued token can be
@@ -7000,14 +7000,14 @@ const token = import_cjs$25.option({
7000
7000
  if (process.env.VERCEL_OIDC_TOKEN) try {
7001
7001
  return await (0, import_dist.getVercelOidcToken)();
7002
7002
  } catch (cause) {
7003
- debug$5(`Failed to get or refresh OIDC token: ${getMessage(cause)}`);
7003
+ debug$7(`Failed to get or refresh OIDC token: ${getMessage(cause)}`);
7004
7004
  console.warn(source_default.yellow(`${source_default.bold("warn:")} failed to get or refresh OIDC token, using personal token authentication.`));
7005
7005
  }
7006
7006
  try {
7007
7007
  return await (0, import_dist.getVercelToken)();
7008
7008
  } catch (error) {
7009
7009
  if (error instanceof import_dist.AccessTokenMissingError || error instanceof import_dist.RefreshAccessTokenFailedError) {
7010
- debug$5(`CLI token unavailable (${error.name}), prompting for login...`);
7010
+ debug$7(`CLI token unavailable (${error.name}), prompting for login...`);
7011
7011
  console.warn(source_default.yellow(`${source_default.bold("notice:")} Your session has expired. Please log in again.`));
7012
7012
  await login.handler({});
7013
7013
  try {
@@ -7064,7 +7064,7 @@ const ProjectConfiguration = jsonCodec(z.object({
7064
7064
  }));
7065
7065
  function readProjectConfiguration(cwd) {
7066
7066
  try {
7067
- const pathname = path.join(cwd, ".vercel", "project.json");
7067
+ const pathname = Path.join(cwd, ".vercel", "project.json");
7068
7068
  const string$2 = fs$1.readFileSync(pathname, "utf8");
7069
7069
  return ProjectConfiguration.decode(string$2);
7070
7070
  } catch {
@@ -7074,18 +7074,18 @@ function readProjectConfiguration(cwd) {
7074
7074
 
7075
7075
  //#endregion
7076
7076
  //#region src/util/infer-scope.ts
7077
- const debug$4 = createDebugger("sandbox:scope");
7077
+ const debug$6 = createDebugger("sandbox:scope");
7078
7078
  async function inferScope$1({ token: token$1, team: team$1 }) {
7079
7079
  const jwt = z.jwt().safeParse(token$1);
7080
7080
  if (jwt.success) {
7081
- debug$4("trying to infer scope from OIDC JWT");
7081
+ debug$6("trying to infer scope from OIDC JWT");
7082
7082
  const data = await inferFromJwt(jwt.data);
7083
- debug$4("Using scope from OIDC JWT", data);
7083
+ debug$6("Using scope from OIDC JWT", data);
7084
7084
  return data;
7085
7085
  }
7086
7086
  const projectJson = readProjectConfiguration(process.cwd());
7087
7087
  if (projectJson) {
7088
- debug$4("Using scope from project configuration", {
7088
+ debug$6("Using scope from project configuration", {
7089
7089
  owner: projectJson.orgId,
7090
7090
  project: projectJson.projectId
7091
7091
  });
@@ -7094,12 +7094,12 @@ async function inferScope$1({ token: token$1, team: team$1 }) {
7094
7094
  project: projectJson.projectId
7095
7095
  };
7096
7096
  }
7097
- debug$4("trying to infer scope from API token", {
7097
+ debug$6("trying to infer scope from API token", {
7098
7098
  token: token$1,
7099
7099
  team: team$1
7100
7100
  });
7101
7101
  const fromToken = await inferFromToken(token$1, team$1);
7102
- debug$4("Using scope from API token", fromToken);
7102
+ debug$6("Using scope from API token", fromToken);
7103
7103
  return fromToken;
7104
7104
  }
7105
7105
  const JwtSchema = z.object({
@@ -7137,7 +7137,7 @@ async function inferFromToken(token$1, requestedTeam) {
7137
7137
 
7138
7138
  //#endregion
7139
7139
  //#region src/util/fresh-auth-retry.ts
7140
- const debug$3 = createDebugger("sandbox:fresh-auth-retry");
7140
+ const debug$5 = createDebugger("sandbox:fresh-auth-retry");
7141
7141
  /**
7142
7142
  * Run an async operation, transparently retrying on 401/403 when the auth
7143
7143
  * token was just acquired via auto-login.
@@ -7149,7 +7149,7 @@ async function withFreshAuthRetry(factory) {
7149
7149
  } catch (error) {
7150
7150
  const status = getAuthFailureStatus(error);
7151
7151
  if (status !== void 0 && isTokenFresh()) {
7152
- debug$3(`fresh-auth retry attempt ${attempt} (status ${status})`);
7152
+ debug$5(`fresh-auth retry attempt ${attempt} (status ${status})`);
7153
7153
  throw error;
7154
7154
  }
7155
7155
  bail(error);
@@ -7177,6 +7177,7 @@ function getAuthFailureStatus(error) {
7177
7177
  //#region src/args/scope.ts
7178
7178
  var import_cjs$24 = /* @__PURE__ */ __toESM(require_cjs());
7179
7179
  init_source();
7180
+ const debug$4 = createDebugger("sandbox:scope");
7180
7181
  const project = import_cjs$24.option({
7181
7182
  long: "project",
7182
7183
  type: {
@@ -7249,12 +7250,13 @@ const scope = {
7249
7250
  projectSlug = scope$1.projectSlug;
7250
7251
  teamSlug = scope$1.ownerSlug;
7251
7252
  } catch (err$1) {
7253
+ debug$4("scope inference failed: %O", err$1);
7252
7254
  return {
7253
7255
  _tag: "error",
7254
7256
  error: { errors: [{
7255
7257
  nodes,
7256
7258
  message: [
7257
- `Could not determine team/project scope: ${err$1.message}.`,
7259
+ `Could not determine team/project scope.${err$1 instanceof NotOk ? ` ${err$1.response.responseText}` : ""}`,
7258
7260
  `${source_default.bold("hint:")} Specify explicitly with --scope TEAM_SLUG --project PROJECT_NAME, or set VERCEL_OIDC_TOKEN.`,
7259
7261
  "╰▶ Docs: https://vercel.com/docs/sandbox"
7260
7262
  ].join("\n")
@@ -7286,7 +7288,7 @@ const scope = {
7286
7288
 
7287
7289
  //#endregion
7288
7290
  //#region package.json
7289
- var version = "3.2.0";
7291
+ var version = "3.2.2";
7290
7292
 
7291
7293
  //#endregion
7292
7294
  //#region src/error.ts
@@ -7299,8 +7301,90 @@ var StyledError = class extends Error {
7299
7301
  };
7300
7302
 
7301
7303
  //#endregion
7302
- //#region src/client.ts
7304
+ //#region src/util/format-error.ts
7303
7305
  init_source();
7306
+ const debug$3 = createDebugger("sandbox:errors");
7307
+ const ApiErrorResponse = z$1.object({ error: z$1.object({ message: z$1.string() }) });
7308
+ /**
7309
+ * Formats an {@link APIError} into a {@link StyledError} with the pretty
7310
+ * multi-line layout used across the CLI.
7311
+ */
7312
+ async function formatApiError(error) {
7313
+ const tmpPath = await writeResponseToTemp(error);
7314
+ const status = error.response.status;
7315
+ return new StyledError([
7316
+ ApiErrorResponse.safeParse(error.json).data?.error.message ?? getErrorMessage(status),
7317
+ `├▶ requested url: ${error.response.url}`,
7318
+ `├▶ status code: ${status} ${error.response.statusText}`,
7319
+ `╰▶ ${source_default.bold("hint:")} the full response buffer is stored in ${source_default.italic(tmpPath)}`
7320
+ ].join("\n"), error);
7321
+ }
7322
+ function getErrorMessage(status) {
7323
+ if (status === 400) return "Sandbox API request failed: the request was invalid (400). Check the command arguments and try again.";
7324
+ if (status === 401 || status === 403) return "Sandbox API request failed due to authentication. Check your token or run `sandbox login`.";
7325
+ if (status === 404) return "Sandbox API request failed: resource not found.";
7326
+ if (status === 429) return "Sandbox API rate limit exceeded (429). Please wait and try again.";
7327
+ if (status >= 500) return "Sandbox API responded with a server error. Please try again.";
7328
+ return "Sandbox API request failed.";
7329
+ }
7330
+ async function writeResponseToTemp({ response, text }) {
7331
+ const unique = [process.pid, process.hrtime.bigint()].map((x) => x.toString(36)).join("");
7332
+ const tmpPath = Path.join(tmpdir(), `sandbox-cli-response-${unique}.http`);
7333
+ const buffers = [];
7334
+ buffers.push(Buffer.from(`${response.url}\r\n`));
7335
+ buffers.push(Buffer.from(`${response.status} ${response.statusText}\r\n`));
7336
+ for (const [key, value] of response.headers) buffers.push(Buffer.from(`${key}: ${value}\r\n`));
7337
+ buffers.push(Buffer.from(`\r\n`));
7338
+ if (text) buffers.push(Buffer.from(text));
7339
+ await writeFile(tmpPath, Buffer.concat(buffers));
7340
+ return tmpPath;
7341
+ }
7342
+ /**
7343
+ * Detects network-level timeouts when talking to the Sandbox API.
7344
+ */
7345
+ function isApiTimeoutError(error) {
7346
+ if (!error || typeof error !== "object") return false;
7347
+ const e = error;
7348
+ if (e.name === "TimeoutError" || e.name === "AbortError") return true;
7349
+ if (e.code === "ETIMEDOUT") return true;
7350
+ const causeCode = e.cause?.code;
7351
+ if (causeCode === "UND_ERR_CONNECT_TIMEOUT" || causeCode === "UND_ERR_HEADERS_TIMEOUT") return true;
7352
+ if (e.name === "TypeError" && e.message === "fetch failed") return isApiTimeoutError(e.cause);
7353
+ return false;
7354
+ }
7355
+ function formatApiTimeoutError(error) {
7356
+ return new StyledError(["The request to the Sandbox API timed out.", `╰▶ ${source_default.bold("hint:")} check your network connection and try again.`].join("\n"), error);
7357
+ }
7358
+ function formatStreamError(error) {
7359
+ return new StyledError([
7360
+ "The sandbox stream was interrupted.",
7361
+ `├▶ code: ${error.code}`,
7362
+ `├▶ session: ${error.sessionId}`,
7363
+ `╰▶ ${source_default.bold("hint:")} the sandbox may have stopped. Resume or recreate it and try again.`
7364
+ ].join("\n"), error);
7365
+ }
7366
+ /**
7367
+ * Single funnel for every error that reaches the CLI's top-level catch.
7368
+ */
7369
+ async function printTopLevelError(e) {
7370
+ const styled = await toStyledError(e);
7371
+ console.error();
7372
+ if (styled) console.error(styled.message);
7373
+ else {
7374
+ console.error(source_default.red(e instanceof Error ? e.message : String(e)));
7375
+ if (debug$3.enabled) console.error(e);
7376
+ }
7377
+ }
7378
+ async function toStyledError(e) {
7379
+ if (e instanceof StyledError) return e;
7380
+ if (e instanceof APIError) return formatApiError(e);
7381
+ if (e instanceof StreamError) return formatStreamError(e);
7382
+ if (isApiTimeoutError(e)) return formatApiTimeoutError(e);
7383
+ return null;
7384
+ }
7385
+
7386
+ //#endregion
7387
+ //#region src/client.ts
7304
7388
  /**
7305
7389
  * A {@link Sandbox} wrapper that adds user-agent headers and error handling.
7306
7390
  */
@@ -7349,39 +7433,10 @@ async function withErrorHandling(factory) {
7349
7433
  try {
7350
7434
  return await withFreshAuthRetry(factory);
7351
7435
  } catch (error) {
7352
- if (error instanceof APIError) return await handleApiError(error);
7436
+ if (error instanceof APIError) throw await formatApiError(error);
7353
7437
  throw error;
7354
7438
  }
7355
7439
  }
7356
- async function handleApiError(error) {
7357
- const tmpPath = await writeResponseToTemp(error);
7358
- const status = error.response.status;
7359
- throw new StyledError([
7360
- ApiErrorResponse.safeParse(error.json).data?.error.message ?? getErrorMessage(status),
7361
- `├▶ requested url: ${error.response.url}`,
7362
- `├▶ status code: ${status} ${error.response.statusText}`,
7363
- `╰▶ ${source_default.bold("hint:")} the full response buffer is stored in ${source_default.italic(tmpPath)}`
7364
- ].join("\n"), error);
7365
- }
7366
- const ApiErrorResponse = z$1.object({ error: z$1.object({ message: z$1.string() }) });
7367
- function getErrorMessage(status) {
7368
- if (status === 401 || status === 403) return "Sandbox API request failed due to authentication. Check your token or run `sandbox login`.";
7369
- if (status === 404) return "Sandbox API request failed: resource not found.";
7370
- if (status >= 500) return "Sandbox API responded with a server error. Please try again.";
7371
- return "Sandbox API request failed.";
7372
- }
7373
- async function writeResponseToTemp({ response, text }) {
7374
- const unique = [process.pid, process.hrtime.bigint()].map((x) => x.toString(36)).join("");
7375
- const tmpPath = path.join(tmpdir(), `sandbox-cli-response-${unique}.http`);
7376
- const buffers = [];
7377
- buffers.push(Buffer.from(`${response.url}\r\n`));
7378
- buffers.push(Buffer.from(`${response.status} ${response.statusText}\r\n`));
7379
- for (const [key, value] of response.headers) buffers.push(Buffer.from(`${key}: ${value}\r\n`));
7380
- buffers.push(Buffer.from(`\r\n`));
7381
- if (text) buffers.push(Buffer.from(text));
7382
- await writeFile(tmpPath, Buffer.concat(buffers));
7383
- return tmpPath;
7384
- }
7385
7440
 
7386
7441
  //#endregion
7387
7442
  //#region src/args/snapshot-id.ts
@@ -10781,8 +10836,8 @@ init_source();
10781
10836
  const parseLocalOrRemotePath = async (input) => {
10782
10837
  const parts = input.split(":");
10783
10838
  if (parts.length === 2) {
10784
- const [id, path$1] = parts;
10785
- if (!id || !path$1) throw new Error([
10839
+ const [id, path] = parts;
10840
+ if (!id || !path) throw new Error([
10786
10841
  `Invalid copy path format: "${input}".`,
10787
10842
  `${source_default.bold("hint:")} Expected format: SANDBOX_NAME:PATH (e.g., my-sandbox:/home/user/file.txt).`,
10788
10843
  "╰▶ Local paths should not contain colons."
@@ -10790,7 +10845,7 @@ const parseLocalOrRemotePath = async (input) => {
10790
10845
  return {
10791
10846
  type: "remote",
10792
10847
  sandboxName: await sandboxName.from(id),
10793
- path: path$1
10848
+ path
10794
10849
  };
10795
10850
  }
10796
10851
  return {
@@ -10834,7 +10889,7 @@ const cp = import_cjs$8.command({
10834
10889
  }
10835
10890
  if (!sourceFile) {
10836
10891
  if (source.type === "remote") {
10837
- const dir = path.dirname(source.path);
10892
+ const dir = Path.dirname(source.path);
10838
10893
  spinner.fail([`File not found: ${source.path} in sandbox ${source.sandboxName}.`, `${source_default.bold("hint:")} Verify the file path exists using \`sandbox exec ${source.sandboxName} ls ${dir}\`.`].join("\n"));
10839
10894
  } else spinner.fail(`Source file (${source.path}) not found.`);
10840
10895
  return;
@@ -11996,5 +12051,5 @@ const app = (opts) => (0, import_cjs.subcommands)({
11996
12051
  });
11997
12052
 
11998
12053
  //#endregion
11999
- export { source_exports as a, init_source as i, StyledError as n, require_cjs as r, app as t };
12000
- //# sourceMappingURL=app-Cil_fM4x.mjs.map
12054
+ export { source_exports as a, init_source as i, printTopLevelError as n, require_cjs as r, app as t };
12055
+ //# sourceMappingURL=app-COo3Oifc.mjs.map