experimental-ash 0.34.0 → 0.35.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.
Files changed (27) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/docs/public/auth-and-route-protection.md +9 -0
  3. package/dist/docs/public/sandbox.md +42 -19
  4. package/dist/docs/public/session-context.md +1 -1
  5. package/dist/src/compiled/.vendor-stamp.json +2 -2
  6. package/dist/src/compiled/@vercel/sandbox/index.d.ts +11 -2
  7. package/dist/src/compiled/@vercel/sandbox/index.js +3 -3
  8. package/dist/src/compiled/@vercel/sandbox/package.json +1 -1
  9. package/dist/src/compiled/_chunks/node/{auth-ZhCJAHxl.js → auth-CVVvWjaK.js} +1 -1
  10. package/dist/src/compiled/_chunks/node/{version-D4IYmfaS.js → version-nR4RSpFw.js} +1 -1
  11. package/dist/src/execution/sandbox/bindings/vercel.d.ts +1 -1
  12. package/dist/src/execution/sandbox/session.js +1 -1
  13. package/dist/src/internal/application/package.js +1 -1
  14. package/dist/src/internal/logging.js +1 -1
  15. package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
  16. package/dist/src/packages/ash-scaffold/src/web-template.js +2 -2
  17. package/dist/src/public/channels/auth.d.ts +22 -11
  18. package/dist/src/public/channels/auth.js +1 -1
  19. package/dist/src/public/definitions/sandbox.d.ts +1 -1
  20. package/dist/src/public/sandbox/index.d.ts +1 -1
  21. package/dist/src/public/sandbox/vercel-sandbox.d.ts +4 -4
  22. package/dist/src/runtime/governance/auth/oidc.js +1 -1
  23. package/dist/src/runtime/governance/auth/token-claims.d.ts +2 -0
  24. package/dist/src/runtime/governance/auth/token-claims.js +1 -1
  25. package/dist/src/runtime/governance/auth/types.d.ts +6 -0
  26. package/dist/src/shared/sandbox-session.d.ts +0 -17
  27. package/package.json +2 -2
@@ -6,9 +6,9 @@ import type { Sandbox as SdkSandbox, SandboxUpdateParams } from "#compiled/@verc
6
6
  * session-create). Skipped on resume (`Sandbox.get`) since no create
7
7
  * happens there.
8
8
  *
9
- * Framework-injected fields (`name`, `persistent`, `signal`) are
10
- * excluded — the framework owns those and overrides any author-supplied
11
- * values.
9
+ * Framework-injected fields (`name`, `onResume`, `persistent`, `signal`)
10
+ * are excluded — the framework owns those and overrides any
11
+ * author-supplied values.
12
12
  *
13
13
  * `source` is honored only on the template create at prewarm time, so
14
14
  * an author-supplied snapshot, git revision, or tarball becomes the
@@ -20,7 +20,7 @@ import type { Sandbox as SdkSandbox, SandboxUpdateParams } from "#compiled/@verc
20
20
  * snapshot, force a template rebuild (e.g. by changing the sandbox
21
21
  * definition so its template key changes).
22
22
  */
23
- export type VercelSandboxCreateOptions = Omit<NonNullable<Parameters<typeof SdkSandbox.create>[0]>, "name" | "persistent" | "signal">;
23
+ export type VercelSandboxCreateOptions = Omit<NonNullable<Parameters<typeof SdkSandbox.create>[0]>, "name" | "onResume" | "persistent" | "signal">;
24
24
  /**
25
25
  * Options accepted by the Vercel backend's `bootstrap({ use })` hook.
26
26
  * Aliases the Vercel SDK's `SandboxUpdateParams` because bootstrap
@@ -1 +1 @@
1
- import{z}from"#compiled/zod/index.js";import{createRemoteJWKSet,jwtVerify}from"#compiled/jose/index.js";import{areTokenClaimMatchersSatisfied,createJwtAuthenticatedCallerPrincipal}from"#runtime/governance/auth/token-claims.js";const oidcDiscoveryDocumentSchema=z.object({issuer:z.string().optional(),jwks_uri:z.string().url()}).passthrough(),oidcDiscoveryDocumentCache=new Map,oidcJwksCache=new Map;async function authenticateOidcStrategy(e){let t;try{t=await getOidcRemoteJwks(e.strategy)}catch(e){return{kind:`misconfigured`,message:`Failed to load OIDC discovery metadata. ${e instanceof Error?e.message:`Unknown discovery failure.`}`}}try{let a=await jwtVerify(e.token,t,{audience:[...e.strategy.audiences],clockTolerance:e.strategy.clockSkewSeconds,issuer:e.strategy.issuer});if(typeof a.payload.sub!=`string`||a.payload.sub.length===0)return{kind:`not-authenticated`};let o=e.strategy.acceptCurrentVercelProject&&isCurrentVercelProject({issuer:e.strategy.issuer,payload:a.payload}),s=o&&isCurrentVercelEnvironment({payload:a.payload});return!o&&!areTokenClaimMatchersSatisfied(a.payload,e.strategy)?{kind:`caller-not-allowed`}:{kind:`authenticated`,principal:createJwtAuthenticatedCallerPrincipal({authenticator:`oidc`,payload:a.payload,principalType:s?`runtime`:`service`})}}catch{return{kind:`not-authenticated`}}}async function getOidcRemoteJwks(e){let n=await getOidcDiscoveryDocument(e.discoveryUrl),r=oidcJwksCache.get(n.jwks_uri);if(r!==void 0)return r;let i=createRemoteJWKSet(new URL(n.jwks_uri));return oidcJwksCache.set(n.jwks_uri,i),i}async function getOidcDiscoveryDocument(e){let t=oidcDiscoveryDocumentCache.get(e);if(t!==void 0)return await t;let n=fetch(e,{headers:{accept:`application/json`}}).then(async e=>{if(!e.ok)throw Error(`Discovery route returned HTTP ${e.status}.`);return oidcDiscoveryDocumentSchema.parse(await e.json())}).catch(t=>{throw oidcDiscoveryDocumentCache.delete(e),t});return oidcDiscoveryDocumentCache.set(e,n),await n}function isCurrentVercelProject(e){if(!e.issuer.startsWith(`https://oidc.vercel.com`))return!1;let t=process.env.VERCEL_PROJECT_ID?.trim();return t===void 0||t.length===0?!1:typeof e.payload.project_id==`string`&&e.payload.project_id===t}function isCurrentVercelEnvironment(e){let t=(process.env.VERCEL_TARGET_ENV?.trim()??process.env.VERCEL_ENV?.trim())?.trim();return t===void 0||t.length===0?!1:typeof e.payload.environment==`string`&&e.payload.environment===t}export{authenticateOidcStrategy};
1
+ import{z}from"#compiled/zod/index.js";import{createRemoteJWKSet,jwtVerify}from"#compiled/jose/index.js";import{areTokenClaimMatchersSatisfied,createJwtAuthenticatedCallerPrincipal}from"#runtime/governance/auth/token-claims.js";const oidcDiscoveryDocumentSchema=z.object({issuer:z.string().optional(),jwks_uri:z.string().url()}).passthrough(),oidcDiscoveryDocumentCache=new Map,oidcJwksCache=new Map;async function authenticateOidcStrategy(e){let t;try{t=await getOidcRemoteJwks(e.strategy)}catch(e){return{kind:`misconfigured`,message:`Failed to load OIDC discovery metadata. ${e instanceof Error?e.message:`Unknown discovery failure.`}`}}try{let a=await jwtVerify(e.token,t,{audience:[...e.strategy.audiences],clockTolerance:e.strategy.clockSkewSeconds,issuer:e.strategy.issuer});if(e.strategy.acceptCurrentVercelProject&&e.strategy.issuer.startsWith(`https://oidc.vercel.com/`)&&a.payload.external_sub!==void 0)return typeof a.payload.external_sub!=`string`||a.payload.external_sub.length===0||!satisfiesCurrentVercelProjectConstraint({payload:a.payload})||!satisfiesCurrentVercelEnvironmentConstraint({payload:a.payload})?{kind:`caller-not-allowed`}:{kind:`authenticated`,principal:createJwtAuthenticatedCallerPrincipal({authenticator:`oidc`,issuerClaims:[`external_iss`,`connector_id`],payload:a.payload,principalType:`user`,subjectClaim:`external_sub`})};if(typeof a.payload.sub!=`string`||a.payload.sub.length===0)return{kind:`not-authenticated`};let o=e.strategy.acceptCurrentVercelProject&&isCurrentVercelProjectToken({issuer:e.strategy.issuer,payload:a.payload}),s=o&&isCurrentVercelEnvironmentToken({payload:a.payload});return!o&&!areTokenClaimMatchersSatisfied(a.payload,e.strategy)?{kind:`caller-not-allowed`}:{kind:`authenticated`,principal:createJwtAuthenticatedCallerPrincipal({authenticator:`oidc`,payload:a.payload,principalType:s?`runtime`:`service`})}}catch{return{kind:`not-authenticated`}}}async function getOidcRemoteJwks(e){let n=await getOidcDiscoveryDocument(e.discoveryUrl),r=oidcJwksCache.get(n.jwks_uri);if(r!==void 0)return r;let i=createRemoteJWKSet(new URL(n.jwks_uri));return oidcJwksCache.set(n.jwks_uri,i),i}async function getOidcDiscoveryDocument(e){let t=oidcDiscoveryDocumentCache.get(e);if(t!==void 0)return await t;let n=fetch(e,{headers:{accept:`application/json`}}).then(async e=>{if(!e.ok)throw Error(`Discovery route returned HTTP ${e.status}.`);return oidcDiscoveryDocumentSchema.parse(await e.json())}).catch(t=>{throw oidcDiscoveryDocumentCache.delete(e),t});return oidcDiscoveryDocumentCache.set(e,n),await n}function isCurrentVercelProjectToken(e){if(!e.issuer.startsWith(`https://oidc.vercel.com`))return!1;let t=process.env.VERCEL_PROJECT_ID?.trim();return t===void 0||t.length===0?!1:typeof e.payload.project_id==`string`&&e.payload.project_id===t}function satisfiesCurrentVercelProjectConstraint(e){let t=process.env.VERCEL_PROJECT_ID?.trim();return t===void 0||t.length===0?!0:typeof e.payload.project_id==`string`&&e.payload.project_id===t}function isCurrentVercelEnvironmentToken(e){let t=getCurrentVercelEnvironment();return t===void 0||t.length===0?!1:typeof e.payload.environment==`string`&&e.payload.environment===t}function satisfiesCurrentVercelEnvironmentConstraint(e){let t=getCurrentVercelEnvironment();return t===void 0||t.length===0?!0:typeof e.payload.environment==`string`&&e.payload.environment===t}function getCurrentVercelEnvironment(){return process.env.VERCEL_TARGET_ENV?.trim()||process.env.VERCEL_ENV?.trim()||void 0}export{authenticateOidcStrategy};
@@ -10,6 +10,8 @@ export declare function areTokenClaimMatchersSatisfied(payload: JWTPayload, matc
10
10
  */
11
11
  export declare function createJwtAuthenticatedCallerPrincipal(input: {
12
12
  readonly authenticator: AuthenticatedCallerPrincipal["authenticator"];
13
+ readonly issuerClaims?: readonly string[];
13
14
  readonly payload: JWTPayload;
14
15
  readonly principalType: AuthenticatedCallerPrincipal["principalType"];
16
+ readonly subjectClaim?: string;
15
17
  }): AuthenticatedCallerPrincipal;
@@ -1 +1 @@
1
- const STANDARD_PROJECTED_CLAIM_KEYS=new Set([`aud`,`exp`,`iat`,`iss`,`jti`,`nbf`,`sub`]);function normalizeJwtClaims(e){let t={};for(let[n,r]of Object.entries(e)){if(typeof r==`string`){t[n]=r;continue}Array.isArray(r)&&r.every(e=>typeof e==`string`)&&(t[n]=Object.freeze([...r]))}return Object.freeze(t)}function createJwtAttributeProjection(t){let n=normalizeJwtClaims(t);return Object.freeze(Object.fromEntries(Object.entries(n).filter(([t])=>!STANDARD_PROJECTED_CLAIM_KEYS.has(t))))}function areTokenClaimMatchersSatisfied(e,t){let n=normalizeJwtClaims(e);if(t.subjects!==void 0){let n=typeof e.sub==`string`?e.sub:null;if(n===null||!t.subjects.some(e=>matchesWildcardPattern(e,n)))return!1}return t.claims===void 0?!0:Object.entries(t.claims).every(([e,t])=>{let r=n[e];return r===void 0?!1:typeof r==`string`?t.includes(r):r.some(e=>t.includes(e))})}function createJwtAuthenticatedCallerPrincipal(e){let t=typeof e.payload.iss==`string`?e.payload.iss:void 0,n=typeof e.payload.sub==`string`?e.payload.sub:void 0;if(t===void 0||n===void 0)throw Error(`Expected verified JWT payloads to include string iss and sub claims.`);let r=normalizeJwtClaims(e.payload);return{attributes:createJwtAttributeProjection(e.payload),authenticator:e.authenticator,claims:r,issuer:t,principalId:`${t}:${n}`,principalType:e.principalType,subject:n}}function matchesWildcardPattern(e,t){if(!e.includes(`*`))return e===t;let n=e.replaceAll(/[.+?^${}()|[\]\\]/g,`\\$&`).replaceAll(`*`,`.*`);return RegExp(`^${n}$`).test(t)}export{areTokenClaimMatchersSatisfied,createJwtAuthenticatedCallerPrincipal};
1
+ const STANDARD_PROJECTED_CLAIM_KEYS=new Set([`aud`,`exp`,`iat`,`iss`,`jti`,`nbf`,`sub`]);function normalizeJwtClaims(e){let t={};for(let[n,r]of Object.entries(e)){if(typeof r==`string`){t[n]=r;continue}Array.isArray(r)&&r.every(e=>typeof e==`string`)&&(t[n]=Object.freeze([...r]))}return Object.freeze(t)}function createJwtAttributeProjection(t){let n=normalizeJwtClaims(t);return Object.freeze(Object.fromEntries(Object.entries(n).filter(([t])=>!STANDARD_PROJECTED_CLAIM_KEYS.has(t))))}function areTokenClaimMatchersSatisfied(e,t){let n=normalizeJwtClaims(e);if(t.subjects!==void 0){let n=typeof e.sub==`string`?e.sub:null;if(n===null||!t.subjects.some(e=>matchesWildcardPattern(e,n)))return!1}return t.claims===void 0?!0:Object.entries(t.claims).every(([e,t])=>{let r=n[e];return r===void 0?!1:typeof r==`string`?t.includes(r):r.some(e=>t.includes(e))})}function createJwtAuthenticatedCallerPrincipal(e){let t=readFirstStringClaim(e.payload,[...e.issuerClaims??[],`iss`]),n=e.subjectClaim??`sub`,r=readFirstStringClaim(e.payload,[n]);if(t===void 0||r===void 0)throw Error(`Expected verified JWT payloads to include string iss and ${n} claims.`);let i=normalizeJwtClaims(e.payload);return{attributes:createJwtAttributeProjection(e.payload),authenticator:e.authenticator,claims:i,issuer:t,principalId:`${t}:${r}`,principalType:e.principalType,subject:r}}function readFirstStringClaim(e,t){for(let n of t){let t=e[n];if(typeof t==`string`&&t.length>0)return t}}function matchesWildcardPattern(e,t){if(!e.includes(`*`))return e===t;let n=e.replaceAll(/[.+?^${}()|[\]\\]/g,`\\$&`).replaceAll(`*`,`.*`);return RegExp(`^${n}$`).test(t)}export{areTokenClaimMatchersSatisfied,createJwtAuthenticatedCallerPrincipal};
@@ -67,6 +67,12 @@ export interface ResolvedOidcAuthStrategy extends ResolvedTokenClaimMatchers {
67
67
  * `principalType: "runtime"`; other current-project tokens are
68
68
  * tagged `"service"`.
69
69
  *
70
+ * Vercel OIDC tokens with an `external_sub` claim are user tokens.
71
+ * They must satisfy the current project/environment constraints when
72
+ * those environment variables are configured, and then authenticate as
73
+ * `principalType: "user"` with `external_sub` as their subject and
74
+ * `external_iss` / `connector_id` as their issuer when present.
75
+ *
70
76
  * Set exclusively by `verifyVercelOidc`. The generic public
71
77
  * `verifyOidc` always passes `false`.
72
78
  */
@@ -19,11 +19,6 @@ export type SandboxSpawnOptions = Parameters<AiSdkSandbox["spawn"]>[0];
19
19
  * Mirrors the AI SDK `Experimental_SandboxProcess` type.
20
20
  */
21
21
  export type SandboxProcess = Awaited<ReturnType<AiSdkSandbox["spawn"]>>;
22
- /**
23
- * @deprecated Use {@link SandboxRunOptions} instead. Kept as an alias
24
- * for source compatibility; will be removed in a future release.
25
- */
26
- export type SandboxRunCommandOptions = SandboxRunOptions;
27
22
  /**
28
23
  * Options for reading one file as a stream of bytes.
29
24
  */
@@ -67,8 +62,6 @@ export type SandboxWriteTextFileOptions = Parameters<AiSdkSandbox["writeTextFile
67
62
  * Relative paths resolve from `/workspace`, the live working directory
68
63
  * for every backend. Absolute paths pass through unchanged.
69
64
  *
70
- * `runCommand` is preserved as a deprecated alias for `run`. It emits a
71
- * one-time warning per session when called.
72
65
  */
73
66
  export interface SandboxSession extends Pick<AiSdkSandbox, "run" | "spawn" | "readFile" | "readBinaryFile" | "readTextFile" | "writeFile" | "writeBinaryFile" | "writeTextFile"> {
74
67
  /**
@@ -90,16 +83,6 @@ export interface SandboxSession extends Pick<AiSdkSandbox, "run" | "spawn" | "re
90
83
  * The read and write methods already apply this internally.
91
84
  */
92
85
  resolvePath(path: string): string;
93
- /**
94
- * @deprecated Use {@link SandboxSession.run} instead. This alias wraps
95
- * `run` and emits a one-time deprecation warning per session on call.
96
- *
97
- * Marked optional on the public type so authored code that constructs
98
- * its own session-like fixtures isn't forced to mock the deprecated
99
- * path. Runtime sessions built through `buildSandboxSession` always
100
- * provide it.
101
- */
102
- runCommand?: AiSdkSandbox["run"];
103
86
  }
104
87
  /**
105
88
  * Internal sandbox session, used to construct the public {@link SandboxSession}.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "experimental-ash",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "bin": {
5
5
  "ash": "./bin/ash.js",
6
6
  "experimental-ash": "./bin/ash.js"
@@ -190,7 +190,7 @@
190
190
  "@types/react": "19.2.15",
191
191
  "@types/react-test-renderer": "19.1.0",
192
192
  "@vercel/oidc": "3.4.1",
193
- "@vercel/sandbox": "2.0.0",
193
+ "@vercel/sandbox": "2.0.1",
194
194
  "@workflow/core": "5.0.0-beta.7",
195
195
  "@workflow/errors": "5.0.0-beta.4",
196
196
  "@workflow/world-local": "5.0.0-beta.6",