@ai-sdk/sandbox-vercel 1.0.54 → 1.0.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @ai-sdk/sandbox-vercel
2
2
 
3
+ ## 1.0.55
4
+
5
+ ### Patch Changes
6
+
7
+ - 861d423: feat(harness): improve harness sandbox auth error handling and expose `getHarnessErrorMessage()` helper
8
+ - Updated dependencies [2c0a8aa]
9
+ - Updated dependencies [861d423]
10
+ - Updated dependencies [8f10600]
11
+ - @ai-sdk/harness@1.0.55
12
+
3
13
  ## 1.0.54
4
14
 
5
15
  ### Patch Changes
package/README.md CHANGED
@@ -34,6 +34,17 @@ console.log(stdout); // "hi"
34
34
  await networkSandboxSession.stop();
35
35
  ```
36
36
 
37
+ ## Authentication
38
+
39
+ Vercel Sandbox accepts `VERCEL_OIDC_TOKEN`, or explicit `token`, `teamId`, and
40
+ `projectId` settings. For local OIDC authentication, link the application with
41
+ `vercel link`, run `vercel env pull`, and load the generated `.env.local` before
42
+ starting it.
43
+
44
+ Credential resolution failures throw
45
+ `HarnessSandboxAuthenticationError` from `@ai-sdk/harness` and preserve the
46
+ underlying Vercel SDK error as `cause`.
47
+
37
48
  `networkSandboxSession.restricted()` is typed as `Experimental_SandboxSession`, so it's safe to pass to AI SDK tools that accept `experimental_sandbox`. The network sandbox session itself carries the infra surface (`ports`, `getPortUrl`, `setNetworkPolicy`, `stop`) that only the harness should reach for.
38
49
 
39
50
  The flat-field settings are aliased directly from `@vercel/sandbox`'s `Sandbox.create` parameters, so every option Vercel supports — including its native `NetworkPolicy` — is available without re-declaration:
package/dist/index.js CHANGED
@@ -1,4 +1,7 @@
1
1
  // src/vercel-sandbox.ts
2
+ import {
3
+ HarnessSandboxAuthenticationError
4
+ } from "@ai-sdk/harness";
2
5
  import { Sandbox } from "@vercel/sandbox";
3
6
 
4
7
  // src/vercel-network-sandbox-session.ts
@@ -337,7 +340,7 @@ var VercelSandboxProvider = class {
337
340
  this.specificationVersion = "harness-sandbox-v1";
338
341
  this.providerId = VERCEL_PROVIDER_ID2;
339
342
  this.createSession = async (options) => {
340
- var _a, _b, _c, _d;
343
+ var _a, _b, _c;
341
344
  (_a = options == null ? void 0 : options.abortSignal) == null ? void 0 : _a.throwIfAborted();
342
345
  if ("sandbox" in this.settings && this.settings.sandbox != null) {
343
346
  return new VercelNetworkSandboxSession({
@@ -359,10 +362,13 @@ var VercelSandboxProvider = class {
359
362
  const onFirstCreate = options == null ? void 0 : options.onFirstCreate;
360
363
  const sessionNameOverride = (options == null ? void 0 : options.sessionId) ? { name: sessionSandboxName(options.sessionId) } : {};
361
364
  if (identity == null || onFirstCreate == null) {
362
- const sandbox = await Sandbox.create({
363
- ...baseParams,
364
- ...sessionNameOverride,
365
- ...(options == null ? void 0 : options.abortSignal) ? { signal: options.abortSignal } : {}
365
+ const sandbox = await withVercelSandboxAuthenticationError({
366
+ settings: this.settings,
367
+ operation: () => Sandbox.create({
368
+ ...baseParams,
369
+ ...sessionNameOverride,
370
+ ...(options == null ? void 0 : options.abortSignal) ? { signal: options.abortSignal } : {}
371
+ })
366
372
  });
367
373
  return new VercelNetworkSandboxSession({ sandbox, ownsLifecycle: true });
368
374
  }
@@ -370,29 +376,41 @@ var VercelSandboxProvider = class {
370
376
  const cache = getSnapshotCache();
371
377
  let snapshotId = cache.get(templateName);
372
378
  if (snapshotId == null) {
373
- const template = await Sandbox.getOrCreate({
374
- ...baseParams,
375
- name: templateName,
376
- persistent: true,
377
- snapshotExpiration: (_c = baseParams.snapshotExpiration) != null ? _c : 0,
378
- onCreate: async (sbx) => {
379
- await onFirstCreate(new VercelSandboxSession(sbx), {
380
- abortSignal: options == null ? void 0 : options.abortSignal
379
+ const template = await withVercelSandboxAuthenticationError({
380
+ settings: this.settings,
381
+ operation: () => {
382
+ var _a2;
383
+ return Sandbox.getOrCreate({
384
+ ...baseParams,
385
+ name: templateName,
386
+ persistent: true,
387
+ snapshotExpiration: (_a2 = baseParams.snapshotExpiration) != null ? _a2 : 0,
388
+ onCreate: async (sbx) => {
389
+ await onFirstCreate(new VercelSandboxSession(sbx), {
390
+ abortSignal: options == null ? void 0 : options.abortSignal
391
+ });
392
+ },
393
+ ...(options == null ? void 0 : options.abortSignal) ? { signal: options.abortSignal } : {}
381
394
  });
382
- },
383
- ...(options == null ? void 0 : options.abortSignal) ? { signal: options.abortSignal } : {}
395
+ }
384
396
  });
385
397
  let resolvedId = template.currentSnapshotId;
386
398
  if (resolvedId == null) {
387
- const stopResult = await template.stop(
388
- (options == null ? void 0 : options.abortSignal) ? { signal: options.abortSignal } : void 0
389
- );
390
- resolvedId = (_d = stopResult.snapshot) == null ? void 0 : _d.id;
399
+ const stopResult = await withVercelSandboxAuthenticationError({
400
+ settings: this.settings,
401
+ operation: () => template.stop(
402
+ (options == null ? void 0 : options.abortSignal) ? { signal: options.abortSignal } : void 0
403
+ )
404
+ });
405
+ resolvedId = (_c = stopResult.snapshot) == null ? void 0 : _c.id;
391
406
  if (resolvedId == null) {
392
- resolvedId = await pollForTemplateSnapshot({
393
- name: templateName,
394
- lookupParams: getSandboxLookupParams(baseParams),
395
- abortSignal: options == null ? void 0 : options.abortSignal
407
+ resolvedId = await withVercelSandboxAuthenticationError({
408
+ settings: this.settings,
409
+ operation: () => pollForTemplateSnapshot({
410
+ name: templateName,
411
+ lookupParams: getSandboxLookupParams(baseParams),
412
+ abortSignal: options == null ? void 0 : options.abortSignal
413
+ })
396
414
  });
397
415
  }
398
416
  }
@@ -405,11 +423,14 @@ var VercelSandboxProvider = class {
405
423
  persistent: _ignoredPersistent,
406
424
  ...forkParams
407
425
  } = baseParams;
408
- const fork = await Sandbox.create({
409
- ...forkParams,
410
- source: { type: "snapshot", snapshotId },
411
- ...sessionNameOverride,
412
- ...(options == null ? void 0 : options.abortSignal) ? { signal: options.abortSignal } : {}
426
+ const fork = await withVercelSandboxAuthenticationError({
427
+ settings: this.settings,
428
+ operation: () => Sandbox.create({
429
+ ...forkParams,
430
+ source: { type: "snapshot", snapshotId },
431
+ ...sessionNameOverride,
432
+ ...(options == null ? void 0 : options.abortSignal) ? { signal: options.abortSignal } : {}
433
+ })
413
434
  });
414
435
  return new VercelNetworkSandboxSession({
415
436
  sandbox: fork,
@@ -425,10 +446,13 @@ var VercelSandboxProvider = class {
425
446
  ownsLifecycle: false
426
447
  });
427
448
  }
428
- const sandbox = await Sandbox.get({
429
- ...getSandboxLookupParams(this.settings),
430
- name: sessionSandboxName(options.sessionId),
431
- ...options.abortSignal ? { signal: options.abortSignal } : {}
449
+ const sandbox = await withVercelSandboxAuthenticationError({
450
+ settings: this.settings,
451
+ operation: () => Sandbox.get({
452
+ ...getSandboxLookupParams(this.settings),
453
+ name: sessionSandboxName(options.sessionId),
454
+ ...options.abortSignal ? { signal: options.abortSignal } : {}
455
+ })
432
456
  });
433
457
  return new VercelNetworkSandboxSession({ sandbox, ownsLifecycle: true });
434
458
  };
@@ -461,6 +485,72 @@ function getSnapshotCache() {
461
485
  }
462
486
  return cache;
463
487
  }
488
+ var VERCEL_SANDBOX_AUTHENTICATION_MESSAGE = "Vercel Sandbox authentication failed. Set VERCEL_OIDC_TOKEN, or pass token, teamId, and projectId to createVercelSandbox(), then verify that they can access Vercel Sandbox.";
489
+ async function withVercelSandboxAuthenticationError({
490
+ settings,
491
+ operation
492
+ }) {
493
+ try {
494
+ return await operation();
495
+ } catch (error) {
496
+ if (!isVercelSandboxAuthenticationFailure({
497
+ error,
498
+ hasConfiguredCredentials: hasConfiguredCredentials(settings)
499
+ })) {
500
+ throw error;
501
+ }
502
+ throw new HarnessSandboxAuthenticationError({
503
+ message: VERCEL_SANDBOX_AUTHENTICATION_MESSAGE,
504
+ sandboxProviderId: VERCEL_PROVIDER_ID2,
505
+ cause: error
506
+ });
507
+ }
508
+ }
509
+ function hasConfiguredCredentials(settings) {
510
+ if (process.env.VERCEL_OIDC_TOKEN) return true;
511
+ if ("sandbox" in settings && settings.sandbox != null) return true;
512
+ const { token, teamId, projectId } = getSandboxLookupParams(settings);
513
+ return Boolean(token && teamId && projectId);
514
+ }
515
+ function isVercelSandboxAuthenticationFailure({
516
+ error,
517
+ hasConfiguredCredentials: hasConfiguredCredentials2
518
+ }) {
519
+ var _a, _b, _c, _d;
520
+ const seen = /* @__PURE__ */ new Set();
521
+ let current = error;
522
+ while (current != null && !seen.has(current)) {
523
+ seen.add(current);
524
+ if (typeof current === "object") {
525
+ const candidate = current;
526
+ if (((_a = candidate.response) == null ? void 0 : _a.status) === 401 || ((_b = candidate.response) == null ? void 0 : _b.status) === 403 || ((_c = candidate.response) == null ? void 0 : _c.statusCode) === 401 || ((_d = candidate.response) == null ? void 0 : _d.statusCode) === 403) {
527
+ return true;
528
+ }
529
+ if (typeof candidate.name === "string" && VERCEL_AUTHENTICATION_ERROR_NAMES.has(candidate.name)) {
530
+ return true;
531
+ }
532
+ if (typeof candidate.message === "string" && VERCEL_AUTHENTICATION_ERROR_MESSAGE.test(candidate.message)) {
533
+ return true;
534
+ }
535
+ if (!hasConfiguredCredentials2 && candidate.code === "ERR_INVALID_ARG_TYPE" && typeof candidate.message === "string" && candidate.message.includes('"path" argument') && candidate.message.includes("Received undefined")) {
536
+ return true;
537
+ }
538
+ current = candidate.cause;
539
+ continue;
540
+ }
541
+ break;
542
+ }
543
+ return false;
544
+ }
545
+ var VERCEL_AUTHENTICATION_ERROR_NAMES = /* @__PURE__ */ new Set([
546
+ "AccessTokenMissingError",
547
+ "LocalOidcContextError",
548
+ "OAuthError",
549
+ "RefreshAccessTokenFailedError",
550
+ "VercelOidcContextError",
551
+ "VercelOidcTokenError"
552
+ ]);
553
+ var VERCEL_AUTHENTICATION_ERROR_MESSAGE = /Could not get credentials from OIDC context|No authentication found|Failed to (?:retrieve|refresh) authentication token|Missing credentials parameters to access the Vercel API|Authentication failed/i;
464
554
  async function pollForTemplateSnapshot({
465
555
  name,
466
556
  lookupParams,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/vercel-sandbox.ts","../src/vercel-network-sandbox-session.ts","../src/vercel-sandbox-session.ts"],"sourcesContent":["import type {\n HarnessV1NetworkSandboxSession,\n HarnessV1SandboxProvider,\n} from '@ai-sdk/harness';\nimport type { Experimental_SandboxSession as SandboxSession } from '@ai-sdk/provider-utils';\nimport { Sandbox } from '@vercel/sandbox';\nimport { VercelNetworkSandboxSession } from './vercel-network-sandbox-session';\nimport { VercelSandboxSession } from './vercel-sandbox-session';\n\n/**\n * Flattens an intersection of object types into a single object type so the\n * resolved shape displays as its named properties rather than a chain of\n * `A & B & C`.\n */\ntype Prettify<T> = { [K in keyof T]: T[K] } & {};\n\n/**\n * Distributes `Omit` across each member of a union instead of collapsing the\n * union to its common keys. `Sandbox.create`'s parameter is a union (a\n * git/tarball/no-source create variant and a snapshot-source create variant),\n * so a plain `Omit` would discard keys absent from any one member (e.g.\n * `runtime`, which the snapshot variant lacks) and merge the `source` shapes.\n * Applying `Omit` per-member preserves every variant intact; the `Prettify`\n * wrapper collapses each member's intersections into a readable object shape.\n */\ntype DistributiveOmit<T, K extends keyof any> = T extends unknown\n ? Prettify<Omit<T, K>>\n : never;\n\n/**\n * Parameters forwarded to `@vercel/sandbox`'s `Sandbox.create` when creating\n * a sandbox from scratch. Aliased directly from the underlying SDK so the\n * full surface — every option Vercel supports, including its native\n * `NetworkPolicy` — is available without us re-declaring it.\n */\ntype VercelSandboxCreateParams = DistributiveOmit<\n NonNullable<Parameters<typeof Sandbox.create>[0]>,\n 'onResume'\n>;\n\n/**\n * Settings for {@link createVercelSandbox}. Two mutually-exclusive shapes:\n *\n * - `{ sandbox }` — wrap an already-created `@vercel/sandbox` `Sandbox`. The\n * caller owns its lifecycle; the provider's `stop()` and `destroy()` are\n * no-ops. Optionally declare `bridgePorts` to give the harness a port pool to\n * lease from for concurrent sessions on the same provided sandbox.\n * - {@link VercelSandboxCreateParams} fields — provider creates the underlying\n * sandbox. When the adapter declares a bootstrap recipe the provider uses\n * `Sandbox.getOrCreate` to maintain a persistent named template snapshot\n * keyed by the recipe identity, and forks an ephemeral sandbox per session\n * from the snapshot. Use `name` to override the auto-derived template name.\n */\nexport type VercelSandboxSettings =\n | {\n sandbox: Sandbox;\n bridgePorts?: ReadonlyArray<number>;\n }\n | (VercelSandboxCreateParams & {\n sandbox?: never;\n name?: string;\n });\n\n/**\n * 30 minutes. The `@vercel/sandbox` SDK defaults to 5 minutes which is\n * too short for multi-step workflows — the VM expires between steps.\n */\nconst DEFAULT_SANDBOX_TIMEOUT_MS = 30 * 60 * 1_000;\n\nconst VERCEL_PROVIDER_ID = 'vercel-sandbox';\nconst TEMPLATE_NAME_PREFIX = 'ai-sdk-harness';\nconst SESSION_NAME_PREFIX = 'ai-sdk-harness-session';\nconst SNAPSHOT_POLL_INTERVAL_MS = 500;\nconst SNAPSHOT_POLL_TIMEOUT_MS = 30_000;\n\nfunction sessionSandboxName(sessionId: string): string {\n return `${SESSION_NAME_PREFIX}-${sessionId}`;\n}\n\nexport function createVercelSandbox(\n settings: VercelSandboxSettings = {} as VercelSandboxSettings,\n): HarnessV1SandboxProvider {\n return new VercelSandboxProvider(settings);\n}\n\n/**\n * `HarnessV1SandboxProvider` implementation backed by `@vercel/sandbox`.\n * Construct one via {@link createVercelSandbox} at module scope and pass it\n * to a `HarnessAgent` (or call `createSession()` directly if you want raw\n * access to a network sandbox session).\n */\nexport class VercelSandboxProvider implements HarnessV1SandboxProvider {\n readonly specificationVersion = 'harness-sandbox-v1' as const;\n readonly providerId = VERCEL_PROVIDER_ID;\n readonly bridgePorts?: ReadonlyArray<number>;\n\n constructor(private readonly settings: VercelSandboxSettings) {\n if (\n 'sandbox' in settings &&\n settings.sandbox != null &&\n settings.bridgePorts != null &&\n settings.bridgePorts.length > 0\n ) {\n this.bridgePorts = [...settings.bridgePorts];\n }\n }\n\n createSession = async (options?: {\n sessionId?: string;\n abortSignal?: AbortSignal;\n identity?: string;\n onFirstCreate?: (\n session: SandboxSession,\n opts: { abortSignal?: AbortSignal },\n ) => Promise<void>;\n }): Promise<HarnessV1NetworkSandboxSession> => {\n options?.abortSignal?.throwIfAborted();\n\n if ('sandbox' in this.settings && this.settings.sandbox != null) {\n return new VercelNetworkSandboxSession({\n sandbox: this.settings.sandbox,\n ownsLifecycle: false,\n });\n }\n\n type CreateNewBranch = BaseCreateSandboxParams & {\n sandbox?: never;\n name?: string;\n };\n const settings = this.settings as CreateNewBranch;\n const {\n sandbox: _ignoredSandbox,\n name: explicitName,\n ...createParams\n } = settings;\n const baseParams: BaseCreateSandboxParams = {\n ...createParams,\n timeout: createParams.timeout ?? DEFAULT_SANDBOX_TIMEOUT_MS,\n };\n\n const identity = options?.identity;\n const onFirstCreate = options?.onFirstCreate;\n\n // When sessionId is supplied, name the per-session sandbox deterministically\n // so a future `resumeSession({ sessionId })` can locate it via\n // `Sandbox.get({ name })`. Absent sessionId (e.g. prewarm), fall back to\n // Vercel's auto-naming.\n const sessionNameOverride = options?.sessionId\n ? { name: sessionSandboxName(options.sessionId) }\n : {};\n\n if (identity == null || onFirstCreate == null) {\n const sandbox = await Sandbox.create({\n ...baseParams,\n ...sessionNameOverride,\n ...(options?.abortSignal ? { signal: options.abortSignal } : {}),\n });\n return new VercelNetworkSandboxSession({ sandbox, ownsLifecycle: true });\n }\n\n const templateName = explicitName ?? `${TEMPLATE_NAME_PREFIX}-${identity}`;\n const cache = getSnapshotCache();\n let snapshotId = cache.get(templateName);\n\n if (snapshotId == null) {\n const template = await Sandbox.getOrCreate({\n ...baseParams,\n name: templateName,\n persistent: true,\n snapshotExpiration: baseParams.snapshotExpiration ?? 0,\n onCreate: async sbx => {\n await onFirstCreate(new VercelSandboxSession(sbx), {\n abortSignal: options?.abortSignal,\n });\n },\n ...(options?.abortSignal ? { signal: options.abortSignal } : {}),\n });\n\n let resolvedId: string | undefined = template.currentSnapshotId;\n if (resolvedId == null) {\n const stopResult = await template.stop(\n options?.abortSignal ? { signal: options.abortSignal } : undefined,\n );\n resolvedId = stopResult.snapshot?.id;\n if (resolvedId == null) {\n resolvedId = await pollForTemplateSnapshot({\n name: templateName,\n lookupParams: getSandboxLookupParams(baseParams),\n abortSignal: options?.abortSignal,\n });\n }\n }\n\n cache.set(templateName, resolvedId);\n snapshotId = resolvedId;\n }\n\n const {\n runtime: _ignoredRuntime,\n source: _ignoredSource,\n persistent: _ignoredPersistent,\n ...forkParams\n } = baseParams;\n\n const fork = await Sandbox.create({\n ...forkParams,\n source: { type: 'snapshot', snapshotId },\n ...sessionNameOverride,\n ...(options?.abortSignal ? { signal: options.abortSignal } : {}),\n });\n return new VercelNetworkSandboxSession({\n sandbox: fork,\n ownsLifecycle: true,\n });\n };\n\n resumeSession = async (options: {\n sessionId: string;\n abortSignal?: AbortSignal;\n }): Promise<HarnessV1NetworkSandboxSession> => {\n options.abortSignal?.throwIfAborted();\n\n // Wrap-existing case: caller owns the sandbox. Same session as createSession.\n if ('sandbox' in this.settings && this.settings.sandbox != null) {\n return new VercelNetworkSandboxSession({\n sandbox: this.settings.sandbox,\n ownsLifecycle: false,\n });\n }\n\n const sandbox = await Sandbox.get({\n ...getSandboxLookupParams(this.settings),\n name: sessionSandboxName(options.sessionId),\n ...(options.abortSignal ? { signal: options.abortSignal } : {}),\n });\n return new VercelNetworkSandboxSession({ sandbox, ownsLifecycle: true });\n };\n}\n\n/**\n * Base shape of `Sandbox.create` params extracted from the union (excludes\n * the `source: { type: 'snapshot' }` variant) so all create-time fields\n * are typed as present.\n */\ntype BaseCreateSandboxParams = Exclude<\n VercelSandboxCreateParams,\n { source: { type: 'snapshot'; snapshotId: string } }\n>;\n\nconst SNAPSHOT_CACHE_KEY = Symbol.for(\n 'ai-sdk.harness.vercel-template-snapshots',\n);\n\ntype SnapshotCache = Map<string, string>;\ntype SandboxLookupParams = {\n fetch?: typeof fetch;\n projectId?: string;\n teamId?: string;\n token?: string;\n};\n\nfunction getSandboxLookupParams(\n settings: VercelSandboxSettings,\n): SandboxLookupParams {\n if ('sandbox' in settings && settings.sandbox != null) {\n return {};\n }\n\n const { fetch, projectId, teamId, token } = settings as SandboxLookupParams;\n return {\n ...(fetch ? { fetch } : {}),\n ...(projectId ? { projectId } : {}),\n ...(teamId ? { teamId } : {}),\n ...(token ? { token } : {}),\n };\n}\n\nfunction getSnapshotCache(): SnapshotCache {\n const globals = globalThis as {\n [SNAPSHOT_CACHE_KEY]?: SnapshotCache;\n };\n let cache = globals[SNAPSHOT_CACHE_KEY];\n if (cache == null) {\n cache = new Map();\n globals[SNAPSHOT_CACHE_KEY] = cache;\n }\n return cache;\n}\n\nasync function pollForTemplateSnapshot({\n name,\n lookupParams,\n abortSignal,\n}: {\n name: string;\n lookupParams: ReturnType<typeof getSandboxLookupParams>;\n abortSignal: AbortSignal | undefined;\n}): Promise<string> {\n const deadline = Date.now() + SNAPSHOT_POLL_TIMEOUT_MS;\n while (Date.now() < deadline) {\n abortSignal?.throwIfAborted();\n const refreshed = await Sandbox.get({\n ...lookupParams,\n name,\n resume: false,\n ...(abortSignal ? { signal: abortSignal } : {}),\n });\n if (refreshed.currentSnapshotId) {\n return refreshed.currentSnapshotId;\n }\n await new Promise<void>(resolve =>\n setTimeout(resolve, SNAPSHOT_POLL_INTERVAL_MS),\n );\n }\n throw new Error(\n `Timed out waiting for snapshot of template \"${name}\" to publish.`,\n );\n}\n","import {\n HarnessCapabilityUnsupportedError,\n type HarnessV1NetworkPolicy,\n type HarnessV1NetworkSandboxSession,\n} from '@ai-sdk/harness';\nimport type { Experimental_SandboxSession as SandboxSession } from '@ai-sdk/provider-utils';\nimport type { Sandbox, NetworkPolicy } from '@vercel/sandbox';\nimport { VercelSandboxSession } from './vercel-sandbox-session';\n\nconst VERCEL_PROVIDER_ID = 'vercel-sandbox';\n\n/**\n * `HarnessV1NetworkSandboxSession` backed by a `@vercel/sandbox` `Sandbox`. The\n * provider's `create()` returns one of these. It extends\n * {@link VercelSandboxSession} with the infra surface (ports, lifecycle,\n * network policy). It owns the sandbox's lifecycle only when the provider\n * created it; when the provider was given an existing sandbox, `stop()` and\n * `destroy()` are no-ops (caller retains ownership).\n */\nexport class VercelNetworkSandboxSession\n extends VercelSandboxSession\n implements HarnessV1NetworkSandboxSession\n{\n readonly id: string;\n readonly defaultWorkingDirectory: string;\n private readonly ownsLifecycle: boolean;\n\n constructor(input: { sandbox: Sandbox; ownsLifecycle: boolean }) {\n super(input.sandbox);\n this.ownsLifecycle = input.ownsLifecycle;\n this.id = input.sandbox.name;\n this.defaultWorkingDirectory = input.sandbox.currentSession().cwd;\n }\n\n get ports(): ReadonlyArray<number> {\n return this.sandbox.routes.map(route => route.port);\n }\n\n restricted(): SandboxSession {\n return new VercelSandboxSession(this.sandbox);\n }\n\n getPortUrl = async (options: {\n port: number;\n protocol?: 'http' | 'https' | 'ws';\n }): Promise<string> => {\n const exposedPorts = this.ports;\n if (!exposedPorts.includes(options.port)) {\n throw new HarnessCapabilityUnsupportedError({\n harnessId: VERCEL_PROVIDER_ID,\n message: `Port ${options.port} is not exposed on this sandbox. Exposed ports: [${exposedPorts.join(', ')}].`,\n });\n }\n const protocol = options.protocol ?? 'https';\n const url = new URL(this.sandbox.domain(options.port));\n const isSecure = url.protocol === 'https:';\n switch (protocol) {\n case 'http':\n url.protocol = isSecure ? 'https:' : 'http:';\n break;\n case 'https':\n url.protocol = 'https:';\n break;\n case 'ws':\n url.protocol = isSecure ? 'wss:' : 'ws:';\n break;\n }\n return url.toString();\n };\n\n setNetworkPolicy = async (policy: HarnessV1NetworkPolicy): Promise<void> => {\n await this.sandbox.update({ networkPolicy: toVercelPolicy(policy) });\n };\n\n setPorts = async (\n ports: ReadonlyArray<number>,\n options?: { abortSignal?: AbortSignal },\n ): Promise<void> => {\n await this.sandbox.update(\n { ports: [...ports] },\n options?.abortSignal ? { signal: options.abortSignal } : undefined,\n );\n };\n\n stop = async (): Promise<void> => {\n if (!this.ownsLifecycle) return;\n await this.sandbox.stop();\n };\n\n destroy = async (): Promise<void> => {\n if (!this.ownsLifecycle) return;\n await this.sandbox.stop().catch(() => {});\n await this.sandbox.delete();\n };\n}\n\nexport function toVercelPolicy(policy: HarnessV1NetworkPolicy): NetworkPolicy {\n switch (policy.mode) {\n case 'allow-all':\n return 'allow-all';\n case 'deny-all':\n return 'deny-all';\n case 'custom': {\n const result: Extract<NetworkPolicy, { allow?: unknown }> = {};\n const { allowedHosts, allowedCIDRs, deniedCIDRs } = policy;\n if (allowedHosts != null && allowedHosts.length > 0) {\n result.allow = [...allowedHosts];\n }\n if (\n (allowedCIDRs != null && allowedCIDRs.length > 0) ||\n (deniedCIDRs != null && deniedCIDRs.length > 0)\n ) {\n result.subnets = {\n ...(allowedCIDRs != null && allowedCIDRs.length > 0\n ? { allow: [...allowedCIDRs] }\n : {}),\n ...(deniedCIDRs != null && deniedCIDRs.length > 0\n ? { deny: [...deniedCIDRs] }\n : {}),\n };\n }\n if (result.allow == null && result.subnets == null) {\n throw new HarnessCapabilityUnsupportedError({\n harnessId: VERCEL_PROVIDER_ID,\n message:\n 'Custom network policy requires at least one of allowedHosts or allowedCIDRs to be non-empty.',\n });\n }\n return result;\n }\n }\n}\n","import { posix } from 'node:path';\nimport {\n extractLines,\n type Experimental_SandboxSession,\n type Experimental_SandboxProcess,\n} from '@ai-sdk/provider-utils';\nimport type { Sandbox, Command } from '@vercel/sandbox';\n\n/**\n * `Experimental_SandboxSession` implementation backed by a `@vercel/sandbox`\n * `Sandbox` instance. This is the tool-safe surface (file I/O, exec, spawn);\n * it is what `VercelNetworkSandboxSession.restricted()` returns and is not\n * constructed directly by consumers. The network sandbox session owns the\n * lifetime of the underlying sandbox.\n */\nexport class VercelSandboxSession implements Experimental_SandboxSession {\n constructor(protected readonly sandbox: Sandbox) {}\n\n get description(): string {\n return [\n `Vercel Sandbox (name: ${this.sandbox.name}).`,\n 'Filesystem changes persist for the lifetime of the sandbox.',\n ].join('\\n');\n }\n\n async run({\n command,\n workingDirectory,\n env,\n abortSignal,\n }: {\n command: string;\n workingDirectory?: string;\n env?: Record<string, string>;\n abortSignal?: AbortSignal;\n }): Promise<{ exitCode: number; stdout: string; stderr: string }> {\n abortSignal?.throwIfAborted();\n\n const finished = await this.sandbox.runCommand({\n cmd: 'bash',\n args: ['-c', command],\n ...(workingDirectory !== undefined ? { cwd: workingDirectory } : {}),\n ...(env !== undefined ? { env } : {}),\n ...(abortSignal !== undefined ? { signal: abortSignal } : {}),\n });\n\n const [stdout, stderr] = await Promise.all([\n finished.stdout(),\n finished.stderr(),\n ]);\n\n return {\n exitCode: finished.exitCode,\n stdout,\n stderr,\n };\n }\n\n async spawn({\n command,\n workingDirectory,\n env,\n abortSignal,\n }: {\n command: string;\n workingDirectory?: string;\n env?: Record<string, string>;\n abortSignal?: AbortSignal;\n }): Promise<Experimental_SandboxProcess> {\n abortSignal?.throwIfAborted();\n\n const live = await this.sandbox.runCommand({\n cmd: 'bash',\n args: ['-c', command],\n detached: true,\n ...(workingDirectory !== undefined ? { cwd: workingDirectory } : {}),\n ...(env !== undefined ? { env } : {}),\n ...(abortSignal !== undefined ? { signal: abortSignal } : {}),\n });\n\n return createSandboxProcess(live, abortSignal);\n }\n\n async readFile({\n path,\n abortSignal,\n }: {\n path: string;\n abortSignal?: AbortSignal;\n }): Promise<ReadableStream<Uint8Array> | null> {\n const bytes = await this.readBinaryFile({ path, abortSignal });\n if (bytes == null) return null;\n return bytesToStream(bytes);\n }\n\n async readBinaryFile({\n path,\n abortSignal,\n }: {\n path: string;\n abortSignal?: AbortSignal;\n }): Promise<Uint8Array | null> {\n abortSignal?.throwIfAborted();\n try {\n const buffer = await this.sandbox.readFileToBuffer(\n { path },\n abortSignal ? { signal: abortSignal } : undefined,\n );\n if (buffer == null) return null;\n return new Uint8Array(\n buffer.buffer,\n buffer.byteOffset,\n buffer.byteLength,\n );\n } catch (error) {\n if (isFileNotFoundError(error)) return null;\n throw error;\n }\n }\n\n async readTextFile({\n path,\n encoding = 'utf-8',\n startLine,\n endLine,\n abortSignal,\n }: {\n path: string;\n encoding?: string;\n startLine?: number;\n endLine?: number;\n abortSignal?: AbortSignal;\n }): Promise<string | null> {\n const bytes = await this.readBinaryFile({ path, abortSignal });\n if (bytes == null) return null;\n const text = Buffer.from(bytes).toString(encoding as BufferEncoding);\n return extractLines({ text, startLine, endLine });\n }\n\n async writeFile({\n path,\n content,\n abortSignal,\n }: {\n path: string;\n content: ReadableStream<Uint8Array>;\n abortSignal?: AbortSignal;\n }): Promise<void> {\n const bytes = await collectStream(content);\n await this.writeBinaryFile({ path, content: bytes, abortSignal });\n }\n\n async writeBinaryFile({\n path,\n content,\n abortSignal,\n }: {\n path: string;\n content: Uint8Array;\n abortSignal?: AbortSignal;\n }): Promise<void> {\n abortSignal?.throwIfAborted();\n const parent = posix.dirname(path);\n if (parent && parent !== '.' && parent !== '/') {\n await this.sandbox.runCommand({\n cmd: 'mkdir',\n args: ['-p', parent],\n ...(abortSignal !== undefined ? { signal: abortSignal } : {}),\n });\n }\n await this.sandbox.writeFiles(\n [{ path, content }],\n abortSignal ? { signal: abortSignal } : undefined,\n );\n }\n\n async writeTextFile({\n path,\n content,\n encoding = 'utf-8',\n abortSignal,\n }: {\n path: string;\n content: string;\n encoding?: string;\n abortSignal?: AbortSignal;\n }): Promise<void> {\n const buffer = Buffer.from(content, encoding as BufferEncoding);\n await this.writeBinaryFile({\n path,\n content: new Uint8Array(\n buffer.buffer,\n buffer.byteOffset,\n buffer.byteLength,\n ),\n abortSignal,\n });\n }\n}\n\nfunction createSandboxProcess(\n command: Command,\n abortSignal: AbortSignal | undefined,\n): Experimental_SandboxProcess {\n const encoder = new TextEncoder();\n const controllers: {\n stdout?: ReadableStreamDefaultController<Uint8Array>;\n stderr?: ReadableStreamDefaultController<Uint8Array>;\n } = {};\n\n const stdout = new ReadableStream<Uint8Array>({\n start(controller) {\n controllers.stdout = controller;\n },\n });\n\n const stderr = new ReadableStream<Uint8Array>({\n start(controller) {\n controllers.stderr = controller;\n },\n });\n\n const drained = (async () => {\n try {\n const iterator = abortSignal\n ? command.logs({ signal: abortSignal })\n : command.logs();\n for await (const message of iterator) {\n const target =\n message.stream === 'stdout' ? controllers.stdout : controllers.stderr;\n target?.enqueue(encoder.encode(message.data));\n }\n controllers.stdout?.close();\n controllers.stderr?.close();\n } catch (error) {\n controllers.stdout?.error(error);\n controllers.stderr?.error(error);\n }\n })();\n\n return {\n stdout,\n stderr,\n async wait(): Promise<{ exitCode: number }> {\n const finished = await command.wait();\n await drained;\n if (abortSignal?.aborted) {\n throw abortSignal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n return { exitCode: finished.exitCode };\n },\n async kill(): Promise<void> {\n await command.kill();\n },\n };\n}\n\nfunction bytesToStream(bytes: Uint8Array): ReadableStream<Uint8Array> {\n return new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(bytes);\n controller.close();\n },\n });\n}\n\nasync function collectStream(\n stream: ReadableStream<Uint8Array>,\n): Promise<Uint8Array> {\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n if (value) {\n chunks.push(value);\n total += value.byteLength;\n }\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n\nfunction isFileNotFoundError(error: unknown): boolean {\n if (error == null || typeof error !== 'object') return false;\n const code = (error as { code?: unknown }).code;\n if (code === 'ENOENT') return true;\n const message = (error as { message?: unknown }).message;\n return (\n typeof message === 'string' &&\n /no such file|not found|does not exist|ENOENT/i.test(message)\n );\n}\n"],"mappings":"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","names":["VERCEL_PROVIDER_ID"]}
1
+ {"version":3,"sources":["../src/vercel-sandbox.ts","../src/vercel-network-sandbox-session.ts","../src/vercel-sandbox-session.ts"],"sourcesContent":["import {\n HarnessSandboxAuthenticationError,\n type HarnessV1NetworkSandboxSession,\n type HarnessV1SandboxProvider,\n} from '@ai-sdk/harness';\nimport type { Experimental_SandboxSession as SandboxSession } from '@ai-sdk/provider-utils';\nimport { Sandbox } from '@vercel/sandbox';\nimport { VercelNetworkSandboxSession } from './vercel-network-sandbox-session';\nimport { VercelSandboxSession } from './vercel-sandbox-session';\n\n/**\n * Flattens an intersection of object types into a single object type so the\n * resolved shape displays as its named properties rather than a chain of\n * `A & B & C`.\n */\ntype Prettify<T> = { [K in keyof T]: T[K] } & {};\n\n/**\n * Distributes `Omit` across each member of a union instead of collapsing the\n * union to its common keys. `Sandbox.create`'s parameter is a union (a\n * git/tarball/no-source create variant and a snapshot-source create variant),\n * so a plain `Omit` would discard keys absent from any one member (e.g.\n * `runtime`, which the snapshot variant lacks) and merge the `source` shapes.\n * Applying `Omit` per-member preserves every variant intact; the `Prettify`\n * wrapper collapses each member's intersections into a readable object shape.\n */\ntype DistributiveOmit<T, K extends keyof any> = T extends unknown\n ? Prettify<Omit<T, K>>\n : never;\n\n/**\n * Parameters forwarded to `@vercel/sandbox`'s `Sandbox.create` when creating\n * a sandbox from scratch. Aliased directly from the underlying SDK so the\n * full surface — every option Vercel supports, including its native\n * `NetworkPolicy` — is available without us re-declaring it.\n */\ntype VercelSandboxCreateParams = DistributiveOmit<\n NonNullable<Parameters<typeof Sandbox.create>[0]>,\n 'onResume'\n>;\n\n/**\n * Settings for {@link createVercelSandbox}. Two mutually-exclusive shapes:\n *\n * - `{ sandbox }` — wrap an already-created `@vercel/sandbox` `Sandbox`. The\n * caller owns its lifecycle; the provider's `stop()` and `destroy()` are\n * no-ops. Optionally declare `bridgePorts` to give the harness a port pool to\n * lease from for concurrent sessions on the same provided sandbox.\n * - {@link VercelSandboxCreateParams} fields — provider creates the underlying\n * sandbox. When the adapter declares a bootstrap recipe the provider uses\n * `Sandbox.getOrCreate` to maintain a persistent named template snapshot\n * keyed by the recipe identity, and forks an ephemeral sandbox per session\n * from the snapshot. Use `name` to override the auto-derived template name.\n */\nexport type VercelSandboxSettings =\n | {\n sandbox: Sandbox;\n bridgePorts?: ReadonlyArray<number>;\n }\n | (VercelSandboxCreateParams & {\n sandbox?: never;\n name?: string;\n });\n\n/**\n * 30 minutes. The `@vercel/sandbox` SDK defaults to 5 minutes which is\n * too short for multi-step workflows — the VM expires between steps.\n */\nconst DEFAULT_SANDBOX_TIMEOUT_MS = 30 * 60 * 1_000;\n\nconst VERCEL_PROVIDER_ID = 'vercel-sandbox';\nconst TEMPLATE_NAME_PREFIX = 'ai-sdk-harness';\nconst SESSION_NAME_PREFIX = 'ai-sdk-harness-session';\nconst SNAPSHOT_POLL_INTERVAL_MS = 500;\nconst SNAPSHOT_POLL_TIMEOUT_MS = 30_000;\n\nfunction sessionSandboxName(sessionId: string): string {\n return `${SESSION_NAME_PREFIX}-${sessionId}`;\n}\n\nexport function createVercelSandbox(\n settings: VercelSandboxSettings = {} as VercelSandboxSettings,\n): HarnessV1SandboxProvider {\n return new VercelSandboxProvider(settings);\n}\n\n/**\n * `HarnessV1SandboxProvider` implementation backed by `@vercel/sandbox`.\n * Construct one via {@link createVercelSandbox} at module scope and pass it\n * to a `HarnessAgent` (or call `createSession()` directly if you want raw\n * access to a network sandbox session).\n */\nexport class VercelSandboxProvider implements HarnessV1SandboxProvider {\n readonly specificationVersion = 'harness-sandbox-v1' as const;\n readonly providerId = VERCEL_PROVIDER_ID;\n readonly bridgePorts?: ReadonlyArray<number>;\n\n constructor(private readonly settings: VercelSandboxSettings) {\n if (\n 'sandbox' in settings &&\n settings.sandbox != null &&\n settings.bridgePorts != null &&\n settings.bridgePorts.length > 0\n ) {\n this.bridgePorts = [...settings.bridgePorts];\n }\n }\n\n createSession = async (options?: {\n sessionId?: string;\n abortSignal?: AbortSignal;\n identity?: string;\n onFirstCreate?: (\n session: SandboxSession,\n opts: { abortSignal?: AbortSignal },\n ) => Promise<void>;\n }): Promise<HarnessV1NetworkSandboxSession> => {\n options?.abortSignal?.throwIfAborted();\n\n if ('sandbox' in this.settings && this.settings.sandbox != null) {\n return new VercelNetworkSandboxSession({\n sandbox: this.settings.sandbox,\n ownsLifecycle: false,\n });\n }\n\n type CreateNewBranch = BaseCreateSandboxParams & {\n sandbox?: never;\n name?: string;\n };\n const settings = this.settings as CreateNewBranch;\n const {\n sandbox: _ignoredSandbox,\n name: explicitName,\n ...createParams\n } = settings;\n const baseParams: BaseCreateSandboxParams = {\n ...createParams,\n timeout: createParams.timeout ?? DEFAULT_SANDBOX_TIMEOUT_MS,\n };\n\n const identity = options?.identity;\n const onFirstCreate = options?.onFirstCreate;\n\n // When sessionId is supplied, name the per-session sandbox deterministically\n // so a future `resumeSession({ sessionId })` can locate it via\n // `Sandbox.get({ name })`. Absent sessionId (e.g. prewarm), fall back to\n // Vercel's auto-naming.\n const sessionNameOverride = options?.sessionId\n ? { name: sessionSandboxName(options.sessionId) }\n : {};\n\n if (identity == null || onFirstCreate == null) {\n const sandbox = await withVercelSandboxAuthenticationError({\n settings: this.settings,\n operation: () =>\n Sandbox.create({\n ...baseParams,\n ...sessionNameOverride,\n ...(options?.abortSignal ? { signal: options.abortSignal } : {}),\n }),\n });\n return new VercelNetworkSandboxSession({ sandbox, ownsLifecycle: true });\n }\n\n const templateName = explicitName ?? `${TEMPLATE_NAME_PREFIX}-${identity}`;\n const cache = getSnapshotCache();\n let snapshotId = cache.get(templateName);\n\n if (snapshotId == null) {\n const template = await withVercelSandboxAuthenticationError({\n settings: this.settings,\n operation: () =>\n Sandbox.getOrCreate({\n ...baseParams,\n name: templateName,\n persistent: true,\n snapshotExpiration: baseParams.snapshotExpiration ?? 0,\n onCreate: async sbx => {\n await onFirstCreate(new VercelSandboxSession(sbx), {\n abortSignal: options?.abortSignal,\n });\n },\n ...(options?.abortSignal ? { signal: options.abortSignal } : {}),\n }),\n });\n\n let resolvedId: string | undefined = template.currentSnapshotId;\n if (resolvedId == null) {\n const stopResult = await withVercelSandboxAuthenticationError({\n settings: this.settings,\n operation: () =>\n template.stop(\n options?.abortSignal\n ? { signal: options.abortSignal }\n : undefined,\n ),\n });\n resolvedId = stopResult.snapshot?.id;\n if (resolvedId == null) {\n resolvedId = await withVercelSandboxAuthenticationError({\n settings: this.settings,\n operation: () =>\n pollForTemplateSnapshot({\n name: templateName,\n lookupParams: getSandboxLookupParams(baseParams),\n abortSignal: options?.abortSignal,\n }),\n });\n }\n }\n\n cache.set(templateName, resolvedId);\n snapshotId = resolvedId;\n }\n\n const {\n runtime: _ignoredRuntime,\n source: _ignoredSource,\n persistent: _ignoredPersistent,\n ...forkParams\n } = baseParams;\n\n const fork = await withVercelSandboxAuthenticationError({\n settings: this.settings,\n operation: () =>\n Sandbox.create({\n ...forkParams,\n source: { type: 'snapshot', snapshotId },\n ...sessionNameOverride,\n ...(options?.abortSignal ? { signal: options.abortSignal } : {}),\n }),\n });\n return new VercelNetworkSandboxSession({\n sandbox: fork,\n ownsLifecycle: true,\n });\n };\n\n resumeSession = async (options: {\n sessionId: string;\n abortSignal?: AbortSignal;\n }): Promise<HarnessV1NetworkSandboxSession> => {\n options.abortSignal?.throwIfAborted();\n\n // Wrap-existing case: caller owns the sandbox. Same session as createSession.\n if ('sandbox' in this.settings && this.settings.sandbox != null) {\n return new VercelNetworkSandboxSession({\n sandbox: this.settings.sandbox,\n ownsLifecycle: false,\n });\n }\n\n const sandbox = await withVercelSandboxAuthenticationError({\n settings: this.settings,\n operation: () =>\n Sandbox.get({\n ...getSandboxLookupParams(this.settings),\n name: sessionSandboxName(options.sessionId),\n ...(options.abortSignal ? { signal: options.abortSignal } : {}),\n }),\n });\n return new VercelNetworkSandboxSession({ sandbox, ownsLifecycle: true });\n };\n}\n\n/**\n * Base shape of `Sandbox.create` params extracted from the union (excludes\n * the `source: { type: 'snapshot' }` variant) so all create-time fields\n * are typed as present.\n */\ntype BaseCreateSandboxParams = Exclude<\n VercelSandboxCreateParams,\n { source: { type: 'snapshot'; snapshotId: string } }\n>;\n\nconst SNAPSHOT_CACHE_KEY = Symbol.for(\n 'ai-sdk.harness.vercel-template-snapshots',\n);\n\ntype SnapshotCache = Map<string, string>;\ntype SandboxLookupParams = {\n fetch?: typeof fetch;\n projectId?: string;\n teamId?: string;\n token?: string;\n};\n\nfunction getSandboxLookupParams(\n settings: VercelSandboxSettings,\n): SandboxLookupParams {\n if ('sandbox' in settings && settings.sandbox != null) {\n return {};\n }\n\n const { fetch, projectId, teamId, token } = settings as SandboxLookupParams;\n return {\n ...(fetch ? { fetch } : {}),\n ...(projectId ? { projectId } : {}),\n ...(teamId ? { teamId } : {}),\n ...(token ? { token } : {}),\n };\n}\n\nfunction getSnapshotCache(): SnapshotCache {\n const globals = globalThis as {\n [SNAPSHOT_CACHE_KEY]?: SnapshotCache;\n };\n let cache = globals[SNAPSHOT_CACHE_KEY];\n if (cache == null) {\n cache = new Map();\n globals[SNAPSHOT_CACHE_KEY] = cache;\n }\n return cache;\n}\n\nconst VERCEL_SANDBOX_AUTHENTICATION_MESSAGE =\n 'Vercel Sandbox authentication failed. Set VERCEL_OIDC_TOKEN, or pass token, teamId, and projectId to createVercelSandbox(), then verify that they can access Vercel Sandbox.';\n\nasync function withVercelSandboxAuthenticationError<T>({\n settings,\n operation,\n}: {\n settings: VercelSandboxSettings;\n operation: () => Promise<T>;\n}): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (\n !isVercelSandboxAuthenticationFailure({\n error,\n hasConfiguredCredentials: hasConfiguredCredentials(settings),\n })\n ) {\n throw error;\n }\n throw new HarnessSandboxAuthenticationError({\n message: VERCEL_SANDBOX_AUTHENTICATION_MESSAGE,\n sandboxProviderId: VERCEL_PROVIDER_ID,\n cause: error,\n });\n }\n}\n\nfunction hasConfiguredCredentials(settings: VercelSandboxSettings): boolean {\n if (process.env.VERCEL_OIDC_TOKEN) return true;\n if ('sandbox' in settings && settings.sandbox != null) return true;\n const { token, teamId, projectId } = getSandboxLookupParams(settings);\n return Boolean(token && teamId && projectId);\n}\n\nfunction isVercelSandboxAuthenticationFailure({\n error,\n hasConfiguredCredentials,\n}: {\n error: unknown;\n hasConfiguredCredentials: boolean;\n}): boolean {\n const seen = new Set<unknown>();\n let current = error;\n while (current != null && !seen.has(current)) {\n seen.add(current);\n if (typeof current === 'object') {\n const candidate = current as {\n name?: unknown;\n message?: unknown;\n code?: unknown;\n cause?: unknown;\n response?: { status?: unknown; statusCode?: unknown };\n };\n if (\n candidate.response?.status === 401 ||\n candidate.response?.status === 403 ||\n candidate.response?.statusCode === 401 ||\n candidate.response?.statusCode === 403\n ) {\n return true;\n }\n if (\n typeof candidate.name === 'string' &&\n VERCEL_AUTHENTICATION_ERROR_NAMES.has(candidate.name)\n ) {\n return true;\n }\n if (\n typeof candidate.message === 'string' &&\n VERCEL_AUTHENTICATION_ERROR_MESSAGE.test(candidate.message)\n ) {\n return true;\n }\n if (\n !hasConfiguredCredentials &&\n candidate.code === 'ERR_INVALID_ARG_TYPE' &&\n typeof candidate.message === 'string' &&\n candidate.message.includes('\"path\" argument') &&\n candidate.message.includes('Received undefined')\n ) {\n return true;\n }\n current = candidate.cause;\n continue;\n }\n break;\n }\n return false;\n}\n\nconst VERCEL_AUTHENTICATION_ERROR_NAMES = new Set([\n 'AccessTokenMissingError',\n 'LocalOidcContextError',\n 'OAuthError',\n 'RefreshAccessTokenFailedError',\n 'VercelOidcContextError',\n 'VercelOidcTokenError',\n]);\n\nconst VERCEL_AUTHENTICATION_ERROR_MESSAGE =\n /Could not get credentials from OIDC context|No authentication found|Failed to (?:retrieve|refresh) authentication token|Missing credentials parameters to access the Vercel API|Authentication failed/i;\n\nasync function pollForTemplateSnapshot({\n name,\n lookupParams,\n abortSignal,\n}: {\n name: string;\n lookupParams: ReturnType<typeof getSandboxLookupParams>;\n abortSignal: AbortSignal | undefined;\n}): Promise<string> {\n const deadline = Date.now() + SNAPSHOT_POLL_TIMEOUT_MS;\n while (Date.now() < deadline) {\n abortSignal?.throwIfAborted();\n const refreshed = await Sandbox.get({\n ...lookupParams,\n name,\n resume: false,\n ...(abortSignal ? { signal: abortSignal } : {}),\n });\n if (refreshed.currentSnapshotId) {\n return refreshed.currentSnapshotId;\n }\n await new Promise<void>(resolve =>\n setTimeout(resolve, SNAPSHOT_POLL_INTERVAL_MS),\n );\n }\n throw new Error(\n `Timed out waiting for snapshot of template \"${name}\" to publish.`,\n );\n}\n","import {\n HarnessCapabilityUnsupportedError,\n type HarnessV1NetworkPolicy,\n type HarnessV1NetworkSandboxSession,\n} from '@ai-sdk/harness';\nimport type { Experimental_SandboxSession as SandboxSession } from '@ai-sdk/provider-utils';\nimport type { Sandbox, NetworkPolicy } from '@vercel/sandbox';\nimport { VercelSandboxSession } from './vercel-sandbox-session';\n\nconst VERCEL_PROVIDER_ID = 'vercel-sandbox';\n\n/**\n * `HarnessV1NetworkSandboxSession` backed by a `@vercel/sandbox` `Sandbox`. The\n * provider's `create()` returns one of these. It extends\n * {@link VercelSandboxSession} with the infra surface (ports, lifecycle,\n * network policy). It owns the sandbox's lifecycle only when the provider\n * created it; when the provider was given an existing sandbox, `stop()` and\n * `destroy()` are no-ops (caller retains ownership).\n */\nexport class VercelNetworkSandboxSession\n extends VercelSandboxSession\n implements HarnessV1NetworkSandboxSession\n{\n readonly id: string;\n readonly defaultWorkingDirectory: string;\n private readonly ownsLifecycle: boolean;\n\n constructor(input: { sandbox: Sandbox; ownsLifecycle: boolean }) {\n super(input.sandbox);\n this.ownsLifecycle = input.ownsLifecycle;\n this.id = input.sandbox.name;\n this.defaultWorkingDirectory = input.sandbox.currentSession().cwd;\n }\n\n get ports(): ReadonlyArray<number> {\n return this.sandbox.routes.map(route => route.port);\n }\n\n restricted(): SandboxSession {\n return new VercelSandboxSession(this.sandbox);\n }\n\n getPortUrl = async (options: {\n port: number;\n protocol?: 'http' | 'https' | 'ws';\n }): Promise<string> => {\n const exposedPorts = this.ports;\n if (!exposedPorts.includes(options.port)) {\n throw new HarnessCapabilityUnsupportedError({\n harnessId: VERCEL_PROVIDER_ID,\n message: `Port ${options.port} is not exposed on this sandbox. Exposed ports: [${exposedPorts.join(', ')}].`,\n });\n }\n const protocol = options.protocol ?? 'https';\n const url = new URL(this.sandbox.domain(options.port));\n const isSecure = url.protocol === 'https:';\n switch (protocol) {\n case 'http':\n url.protocol = isSecure ? 'https:' : 'http:';\n break;\n case 'https':\n url.protocol = 'https:';\n break;\n case 'ws':\n url.protocol = isSecure ? 'wss:' : 'ws:';\n break;\n }\n return url.toString();\n };\n\n setNetworkPolicy = async (policy: HarnessV1NetworkPolicy): Promise<void> => {\n await this.sandbox.update({ networkPolicy: toVercelPolicy(policy) });\n };\n\n setPorts = async (\n ports: ReadonlyArray<number>,\n options?: { abortSignal?: AbortSignal },\n ): Promise<void> => {\n await this.sandbox.update(\n { ports: [...ports] },\n options?.abortSignal ? { signal: options.abortSignal } : undefined,\n );\n };\n\n stop = async (): Promise<void> => {\n if (!this.ownsLifecycle) return;\n await this.sandbox.stop();\n };\n\n destroy = async (): Promise<void> => {\n if (!this.ownsLifecycle) return;\n await this.sandbox.stop().catch(() => {});\n await this.sandbox.delete();\n };\n}\n\nexport function toVercelPolicy(policy: HarnessV1NetworkPolicy): NetworkPolicy {\n switch (policy.mode) {\n case 'allow-all':\n return 'allow-all';\n case 'deny-all':\n return 'deny-all';\n case 'custom': {\n const result: Extract<NetworkPolicy, { allow?: unknown }> = {};\n const { allowedHosts, allowedCIDRs, deniedCIDRs } = policy;\n if (allowedHosts != null && allowedHosts.length > 0) {\n result.allow = [...allowedHosts];\n }\n if (\n (allowedCIDRs != null && allowedCIDRs.length > 0) ||\n (deniedCIDRs != null && deniedCIDRs.length > 0)\n ) {\n result.subnets = {\n ...(allowedCIDRs != null && allowedCIDRs.length > 0\n ? { allow: [...allowedCIDRs] }\n : {}),\n ...(deniedCIDRs != null && deniedCIDRs.length > 0\n ? { deny: [...deniedCIDRs] }\n : {}),\n };\n }\n if (result.allow == null && result.subnets == null) {\n throw new HarnessCapabilityUnsupportedError({\n harnessId: VERCEL_PROVIDER_ID,\n message:\n 'Custom network policy requires at least one of allowedHosts or allowedCIDRs to be non-empty.',\n });\n }\n return result;\n }\n }\n}\n","import { posix } from 'node:path';\nimport {\n extractLines,\n type Experimental_SandboxSession,\n type Experimental_SandboxProcess,\n} from '@ai-sdk/provider-utils';\nimport type { Sandbox, Command } from '@vercel/sandbox';\n\n/**\n * `Experimental_SandboxSession` implementation backed by a `@vercel/sandbox`\n * `Sandbox` instance. This is the tool-safe surface (file I/O, exec, spawn);\n * it is what `VercelNetworkSandboxSession.restricted()` returns and is not\n * constructed directly by consumers. The network sandbox session owns the\n * lifetime of the underlying sandbox.\n */\nexport class VercelSandboxSession implements Experimental_SandboxSession {\n constructor(protected readonly sandbox: Sandbox) {}\n\n get description(): string {\n return [\n `Vercel Sandbox (name: ${this.sandbox.name}).`,\n 'Filesystem changes persist for the lifetime of the sandbox.',\n ].join('\\n');\n }\n\n async run({\n command,\n workingDirectory,\n env,\n abortSignal,\n }: {\n command: string;\n workingDirectory?: string;\n env?: Record<string, string>;\n abortSignal?: AbortSignal;\n }): Promise<{ exitCode: number; stdout: string; stderr: string }> {\n abortSignal?.throwIfAborted();\n\n const finished = await this.sandbox.runCommand({\n cmd: 'bash',\n args: ['-c', command],\n ...(workingDirectory !== undefined ? { cwd: workingDirectory } : {}),\n ...(env !== undefined ? { env } : {}),\n ...(abortSignal !== undefined ? { signal: abortSignal } : {}),\n });\n\n const [stdout, stderr] = await Promise.all([\n finished.stdout(),\n finished.stderr(),\n ]);\n\n return {\n exitCode: finished.exitCode,\n stdout,\n stderr,\n };\n }\n\n async spawn({\n command,\n workingDirectory,\n env,\n abortSignal,\n }: {\n command: string;\n workingDirectory?: string;\n env?: Record<string, string>;\n abortSignal?: AbortSignal;\n }): Promise<Experimental_SandboxProcess> {\n abortSignal?.throwIfAborted();\n\n const live = await this.sandbox.runCommand({\n cmd: 'bash',\n args: ['-c', command],\n detached: true,\n ...(workingDirectory !== undefined ? { cwd: workingDirectory } : {}),\n ...(env !== undefined ? { env } : {}),\n ...(abortSignal !== undefined ? { signal: abortSignal } : {}),\n });\n\n return createSandboxProcess(live, abortSignal);\n }\n\n async readFile({\n path,\n abortSignal,\n }: {\n path: string;\n abortSignal?: AbortSignal;\n }): Promise<ReadableStream<Uint8Array> | null> {\n const bytes = await this.readBinaryFile({ path, abortSignal });\n if (bytes == null) return null;\n return bytesToStream(bytes);\n }\n\n async readBinaryFile({\n path,\n abortSignal,\n }: {\n path: string;\n abortSignal?: AbortSignal;\n }): Promise<Uint8Array | null> {\n abortSignal?.throwIfAborted();\n try {\n const buffer = await this.sandbox.readFileToBuffer(\n { path },\n abortSignal ? { signal: abortSignal } : undefined,\n );\n if (buffer == null) return null;\n return new Uint8Array(\n buffer.buffer,\n buffer.byteOffset,\n buffer.byteLength,\n );\n } catch (error) {\n if (isFileNotFoundError(error)) return null;\n throw error;\n }\n }\n\n async readTextFile({\n path,\n encoding = 'utf-8',\n startLine,\n endLine,\n abortSignal,\n }: {\n path: string;\n encoding?: string;\n startLine?: number;\n endLine?: number;\n abortSignal?: AbortSignal;\n }): Promise<string | null> {\n const bytes = await this.readBinaryFile({ path, abortSignal });\n if (bytes == null) return null;\n const text = Buffer.from(bytes).toString(encoding as BufferEncoding);\n return extractLines({ text, startLine, endLine });\n }\n\n async writeFile({\n path,\n content,\n abortSignal,\n }: {\n path: string;\n content: ReadableStream<Uint8Array>;\n abortSignal?: AbortSignal;\n }): Promise<void> {\n const bytes = await collectStream(content);\n await this.writeBinaryFile({ path, content: bytes, abortSignal });\n }\n\n async writeBinaryFile({\n path,\n content,\n abortSignal,\n }: {\n path: string;\n content: Uint8Array;\n abortSignal?: AbortSignal;\n }): Promise<void> {\n abortSignal?.throwIfAborted();\n const parent = posix.dirname(path);\n if (parent && parent !== '.' && parent !== '/') {\n await this.sandbox.runCommand({\n cmd: 'mkdir',\n args: ['-p', parent],\n ...(abortSignal !== undefined ? { signal: abortSignal } : {}),\n });\n }\n await this.sandbox.writeFiles(\n [{ path, content }],\n abortSignal ? { signal: abortSignal } : undefined,\n );\n }\n\n async writeTextFile({\n path,\n content,\n encoding = 'utf-8',\n abortSignal,\n }: {\n path: string;\n content: string;\n encoding?: string;\n abortSignal?: AbortSignal;\n }): Promise<void> {\n const buffer = Buffer.from(content, encoding as BufferEncoding);\n await this.writeBinaryFile({\n path,\n content: new Uint8Array(\n buffer.buffer,\n buffer.byteOffset,\n buffer.byteLength,\n ),\n abortSignal,\n });\n }\n}\n\nfunction createSandboxProcess(\n command: Command,\n abortSignal: AbortSignal | undefined,\n): Experimental_SandboxProcess {\n const encoder = new TextEncoder();\n const controllers: {\n stdout?: ReadableStreamDefaultController<Uint8Array>;\n stderr?: ReadableStreamDefaultController<Uint8Array>;\n } = {};\n\n const stdout = new ReadableStream<Uint8Array>({\n start(controller) {\n controllers.stdout = controller;\n },\n });\n\n const stderr = new ReadableStream<Uint8Array>({\n start(controller) {\n controllers.stderr = controller;\n },\n });\n\n const drained = (async () => {\n try {\n const iterator = abortSignal\n ? command.logs({ signal: abortSignal })\n : command.logs();\n for await (const message of iterator) {\n const target =\n message.stream === 'stdout' ? controllers.stdout : controllers.stderr;\n target?.enqueue(encoder.encode(message.data));\n }\n controllers.stdout?.close();\n controllers.stderr?.close();\n } catch (error) {\n controllers.stdout?.error(error);\n controllers.stderr?.error(error);\n }\n })();\n\n return {\n stdout,\n stderr,\n async wait(): Promise<{ exitCode: number }> {\n const finished = await command.wait();\n await drained;\n if (abortSignal?.aborted) {\n throw abortSignal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n return { exitCode: finished.exitCode };\n },\n async kill(): Promise<void> {\n await command.kill();\n },\n };\n}\n\nfunction bytesToStream(bytes: Uint8Array): ReadableStream<Uint8Array> {\n return new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(bytes);\n controller.close();\n },\n });\n}\n\nasync function collectStream(\n stream: ReadableStream<Uint8Array>,\n): Promise<Uint8Array> {\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n if (value) {\n chunks.push(value);\n total += value.byteLength;\n }\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n\nfunction isFileNotFoundError(error: unknown): boolean {\n if (error == null || typeof error !== 'object') return false;\n const code = (error as { code?: unknown }).code;\n if (code === 'ENOENT') return true;\n const message = (error as { message?: unknown }).message;\n return (\n typeof message === 'string' &&\n /no such file|not found|does not exist|ENOENT/i.test(message)\n );\n}\n"],"mappings":"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","names":["VERCEL_PROVIDER_ID","_a","hasConfiguredCredentials"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/sandbox-vercel",
3
- "version": "1.0.54",
3
+ "version": "1.0.55",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@vercel/sandbox": "^2.0.1",
30
- "@ai-sdk/harness": "1.0.54",
30
+ "@ai-sdk/harness": "1.0.55",
31
31
  "@ai-sdk/provider-utils": "5.0.18"
32
32
  },
33
33
  "devDependencies": {
@@ -1,6 +1,7 @@
1
- import type {
2
- HarnessV1NetworkSandboxSession,
3
- HarnessV1SandboxProvider,
1
+ import {
2
+ HarnessSandboxAuthenticationError,
3
+ type HarnessV1NetworkSandboxSession,
4
+ type HarnessV1SandboxProvider,
4
5
  } from '@ai-sdk/harness';
5
6
  import type { Experimental_SandboxSession as SandboxSession } from '@ai-sdk/provider-utils';
6
7
  import { Sandbox } from '@vercel/sandbox';
@@ -150,10 +151,14 @@ export class VercelSandboxProvider implements HarnessV1SandboxProvider {
150
151
  : {};
151
152
 
152
153
  if (identity == null || onFirstCreate == null) {
153
- const sandbox = await Sandbox.create({
154
- ...baseParams,
155
- ...sessionNameOverride,
156
- ...(options?.abortSignal ? { signal: options.abortSignal } : {}),
154
+ const sandbox = await withVercelSandboxAuthenticationError({
155
+ settings: this.settings,
156
+ operation: () =>
157
+ Sandbox.create({
158
+ ...baseParams,
159
+ ...sessionNameOverride,
160
+ ...(options?.abortSignal ? { signal: options.abortSignal } : {}),
161
+ }),
157
162
  });
158
163
  return new VercelNetworkSandboxSession({ sandbox, ownsLifecycle: true });
159
164
  }
@@ -163,30 +168,44 @@ export class VercelSandboxProvider implements HarnessV1SandboxProvider {
163
168
  let snapshotId = cache.get(templateName);
164
169
 
165
170
  if (snapshotId == null) {
166
- const template = await Sandbox.getOrCreate({
167
- ...baseParams,
168
- name: templateName,
169
- persistent: true,
170
- snapshotExpiration: baseParams.snapshotExpiration ?? 0,
171
- onCreate: async sbx => {
172
- await onFirstCreate(new VercelSandboxSession(sbx), {
173
- abortSignal: options?.abortSignal,
174
- });
175
- },
176
- ...(options?.abortSignal ? { signal: options.abortSignal } : {}),
171
+ const template = await withVercelSandboxAuthenticationError({
172
+ settings: this.settings,
173
+ operation: () =>
174
+ Sandbox.getOrCreate({
175
+ ...baseParams,
176
+ name: templateName,
177
+ persistent: true,
178
+ snapshotExpiration: baseParams.snapshotExpiration ?? 0,
179
+ onCreate: async sbx => {
180
+ await onFirstCreate(new VercelSandboxSession(sbx), {
181
+ abortSignal: options?.abortSignal,
182
+ });
183
+ },
184
+ ...(options?.abortSignal ? { signal: options.abortSignal } : {}),
185
+ }),
177
186
  });
178
187
 
179
188
  let resolvedId: string | undefined = template.currentSnapshotId;
180
189
  if (resolvedId == null) {
181
- const stopResult = await template.stop(
182
- options?.abortSignal ? { signal: options.abortSignal } : undefined,
183
- );
190
+ const stopResult = await withVercelSandboxAuthenticationError({
191
+ settings: this.settings,
192
+ operation: () =>
193
+ template.stop(
194
+ options?.abortSignal
195
+ ? { signal: options.abortSignal }
196
+ : undefined,
197
+ ),
198
+ });
184
199
  resolvedId = stopResult.snapshot?.id;
185
200
  if (resolvedId == null) {
186
- resolvedId = await pollForTemplateSnapshot({
187
- name: templateName,
188
- lookupParams: getSandboxLookupParams(baseParams),
189
- abortSignal: options?.abortSignal,
201
+ resolvedId = await withVercelSandboxAuthenticationError({
202
+ settings: this.settings,
203
+ operation: () =>
204
+ pollForTemplateSnapshot({
205
+ name: templateName,
206
+ lookupParams: getSandboxLookupParams(baseParams),
207
+ abortSignal: options?.abortSignal,
208
+ }),
190
209
  });
191
210
  }
192
211
  }
@@ -202,11 +221,15 @@ export class VercelSandboxProvider implements HarnessV1SandboxProvider {
202
221
  ...forkParams
203
222
  } = baseParams;
204
223
 
205
- const fork = await Sandbox.create({
206
- ...forkParams,
207
- source: { type: 'snapshot', snapshotId },
208
- ...sessionNameOverride,
209
- ...(options?.abortSignal ? { signal: options.abortSignal } : {}),
224
+ const fork = await withVercelSandboxAuthenticationError({
225
+ settings: this.settings,
226
+ operation: () =>
227
+ Sandbox.create({
228
+ ...forkParams,
229
+ source: { type: 'snapshot', snapshotId },
230
+ ...sessionNameOverride,
231
+ ...(options?.abortSignal ? { signal: options.abortSignal } : {}),
232
+ }),
210
233
  });
211
234
  return new VercelNetworkSandboxSession({
212
235
  sandbox: fork,
@@ -228,10 +251,14 @@ export class VercelSandboxProvider implements HarnessV1SandboxProvider {
228
251
  });
229
252
  }
230
253
 
231
- const sandbox = await Sandbox.get({
232
- ...getSandboxLookupParams(this.settings),
233
- name: sessionSandboxName(options.sessionId),
234
- ...(options.abortSignal ? { signal: options.abortSignal } : {}),
254
+ const sandbox = await withVercelSandboxAuthenticationError({
255
+ settings: this.settings,
256
+ operation: () =>
257
+ Sandbox.get({
258
+ ...getSandboxLookupParams(this.settings),
259
+ name: sessionSandboxName(options.sessionId),
260
+ ...(options.abortSignal ? { signal: options.abortSignal } : {}),
261
+ }),
235
262
  });
236
263
  return new VercelNetworkSandboxSession({ sandbox, ownsLifecycle: true });
237
264
  };
@@ -287,6 +314,110 @@ function getSnapshotCache(): SnapshotCache {
287
314
  return cache;
288
315
  }
289
316
 
317
+ const VERCEL_SANDBOX_AUTHENTICATION_MESSAGE =
318
+ 'Vercel Sandbox authentication failed. Set VERCEL_OIDC_TOKEN, or pass token, teamId, and projectId to createVercelSandbox(), then verify that they can access Vercel Sandbox.';
319
+
320
+ async function withVercelSandboxAuthenticationError<T>({
321
+ settings,
322
+ operation,
323
+ }: {
324
+ settings: VercelSandboxSettings;
325
+ operation: () => Promise<T>;
326
+ }): Promise<T> {
327
+ try {
328
+ return await operation();
329
+ } catch (error) {
330
+ if (
331
+ !isVercelSandboxAuthenticationFailure({
332
+ error,
333
+ hasConfiguredCredentials: hasConfiguredCredentials(settings),
334
+ })
335
+ ) {
336
+ throw error;
337
+ }
338
+ throw new HarnessSandboxAuthenticationError({
339
+ message: VERCEL_SANDBOX_AUTHENTICATION_MESSAGE,
340
+ sandboxProviderId: VERCEL_PROVIDER_ID,
341
+ cause: error,
342
+ });
343
+ }
344
+ }
345
+
346
+ function hasConfiguredCredentials(settings: VercelSandboxSettings): boolean {
347
+ if (process.env.VERCEL_OIDC_TOKEN) return true;
348
+ if ('sandbox' in settings && settings.sandbox != null) return true;
349
+ const { token, teamId, projectId } = getSandboxLookupParams(settings);
350
+ return Boolean(token && teamId && projectId);
351
+ }
352
+
353
+ function isVercelSandboxAuthenticationFailure({
354
+ error,
355
+ hasConfiguredCredentials,
356
+ }: {
357
+ error: unknown;
358
+ hasConfiguredCredentials: boolean;
359
+ }): boolean {
360
+ const seen = new Set<unknown>();
361
+ let current = error;
362
+ while (current != null && !seen.has(current)) {
363
+ seen.add(current);
364
+ if (typeof current === 'object') {
365
+ const candidate = current as {
366
+ name?: unknown;
367
+ message?: unknown;
368
+ code?: unknown;
369
+ cause?: unknown;
370
+ response?: { status?: unknown; statusCode?: unknown };
371
+ };
372
+ if (
373
+ candidate.response?.status === 401 ||
374
+ candidate.response?.status === 403 ||
375
+ candidate.response?.statusCode === 401 ||
376
+ candidate.response?.statusCode === 403
377
+ ) {
378
+ return true;
379
+ }
380
+ if (
381
+ typeof candidate.name === 'string' &&
382
+ VERCEL_AUTHENTICATION_ERROR_NAMES.has(candidate.name)
383
+ ) {
384
+ return true;
385
+ }
386
+ if (
387
+ typeof candidate.message === 'string' &&
388
+ VERCEL_AUTHENTICATION_ERROR_MESSAGE.test(candidate.message)
389
+ ) {
390
+ return true;
391
+ }
392
+ if (
393
+ !hasConfiguredCredentials &&
394
+ candidate.code === 'ERR_INVALID_ARG_TYPE' &&
395
+ typeof candidate.message === 'string' &&
396
+ candidate.message.includes('"path" argument') &&
397
+ candidate.message.includes('Received undefined')
398
+ ) {
399
+ return true;
400
+ }
401
+ current = candidate.cause;
402
+ continue;
403
+ }
404
+ break;
405
+ }
406
+ return false;
407
+ }
408
+
409
+ const VERCEL_AUTHENTICATION_ERROR_NAMES = new Set([
410
+ 'AccessTokenMissingError',
411
+ 'LocalOidcContextError',
412
+ 'OAuthError',
413
+ 'RefreshAccessTokenFailedError',
414
+ 'VercelOidcContextError',
415
+ 'VercelOidcTokenError',
416
+ ]);
417
+
418
+ const VERCEL_AUTHENTICATION_ERROR_MESSAGE =
419
+ /Could not get credentials from OIDC context|No authentication found|Failed to (?:retrieve|refresh) authentication token|Missing credentials parameters to access the Vercel API|Authentication failed/i;
420
+
290
421
  async function pollForTemplateSnapshot({
291
422
  name,
292
423
  lookupParams,