@ai-sdk/sandbox-vercel 1.0.71 → 1.0.73

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,24 @@
1
1
  # @ai-sdk/sandbox-vercel
2
2
 
3
+ ## 1.0.73
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [62a9c2a]
8
+ - Updated dependencies [d25cae2]
9
+ - @ai-sdk/harness@1.0.73
10
+
11
+ ## 1.0.72
12
+
13
+ ### Patch Changes
14
+
15
+ - 69bb613: feat(harness): support request transformations in network sandbox abstraction and use it to apply credential brokering when available
16
+ - 52bc889: feat(harness): add `getPortEndpoint()` as more comprehensive replacement to `getPortUrl()` (now deprecated) in `HarnessV1NetworkSandboxSession`
17
+ - Updated dependencies [69bb613]
18
+ - Updated dependencies [52bc889]
19
+ - Updated dependencies [4cd4989]
20
+ - @ai-sdk/harness@1.0.72
21
+
3
22
  ## 1.0.71
4
23
 
5
24
  ### Patch Changes
package/README.md CHANGED
@@ -45,7 +45,7 @@ Credential resolution failures throw
45
45
  `HarnessSandboxAuthenticationError` from `@ai-sdk/harness` and preserve the
46
46
  underlying Vercel SDK error as `cause`.
47
47
 
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.
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`, `getPortEndpoint`, `setNetworkPolicy`, `setRequestTransformations`, `addRequestTransformations`, `stop`) that only the harness should reach for. `getPortUrl` remains available as a deprecated compatibility wrapper.
49
49
 
50
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:
51
51
 
@@ -85,3 +85,10 @@ await networkSandboxSession.setNetworkPolicy?.({
85
85
  ```
86
86
 
87
87
  `HarnessV1NetworkPolicy` is the harness-level abstraction used here. The provider translates it to `@vercel/sandbox`'s native `NetworkPolicy` for enforcement.
88
+
89
+ ### Request transformations and credential brokering
90
+
91
+ Vercel Sandbox supports outbound request transformations for use cases such as
92
+ credential brokering. `setRequestTransformations()` replaces the managed rules,
93
+ while `addRequestTransformations()` adds rules without replacing them. Network
94
+ access policies remain authoritative over which hosts can be reached.
package/dist/index.js CHANGED
@@ -1,3 +1,12 @@
1
+ var __typeError = (msg) => {
2
+ throw TypeError(msg);
3
+ };
4
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
5
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
6
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
7
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
8
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
9
+
1
10
  // src/vercel-sandbox.ts
2
11
  import {
3
12
  HarnessSandboxAuthenticationError
@@ -5,9 +14,504 @@ import {
5
14
  import { Sandbox } from "@vercel/sandbox";
6
15
 
7
16
  // src/vercel-network-sandbox-session.ts
17
+ import {
18
+ HarnessCapabilityUnsupportedError as HarnessCapabilityUnsupportedError2
19
+ } from "@ai-sdk/harness";
20
+
21
+ // src/vercel-network-policy-manager.ts
8
22
  import {
9
23
  HarnessCapabilityUnsupportedError
10
24
  } from "@ai-sdk/harness";
25
+ var VERCEL_PROVIDER_ID = "vercel-sandbox";
26
+ var _sandbox, _state, _mutationQueue, _VercelNetworkPolicyManager_instances, enqueueMutation_fn, inspectPolicy_fn, applyState_fn;
27
+ var VercelNetworkPolicyManager = class {
28
+ constructor({ sandbox }) {
29
+ __privateAdd(this, _VercelNetworkPolicyManager_instances);
30
+ __privateAdd(this, _sandbox);
31
+ __privateAdd(this, _state);
32
+ __privateAdd(this, _mutationQueue, Promise.resolve());
33
+ __privateSet(this, _sandbox, sandbox);
34
+ }
35
+ setNetworkPolicy(policy) {
36
+ return __privateMethod(this, _VercelNetworkPolicyManager_instances, enqueueMutation_fn).call(this, async () => {
37
+ var _a, _b;
38
+ const inspection = __privateMethod(this, _VercelNetworkPolicyManager_instances, inspectPolicy_fn).call(this);
39
+ if (__privateGet(this, _state) == null && inspection.requestTransformationHosts.length > 0) {
40
+ throw createPolicyConflictError(
41
+ "Cannot set the network policy because the current Vercel Sandbox policy contains request transformations whose redacted values cannot be preserved safely. Replace them explicitly with setRequestTransformations() or rehydrate them with addRequestTransformations() first."
42
+ );
43
+ }
44
+ await __privateMethod(this, _VercelNetworkPolicyManager_instances, applyState_fn).call(this, {
45
+ accessPolicy: toNetworkAccessPolicy(policy),
46
+ requestTransformations: (_b = (_a = __privateGet(this, _state)) == null ? void 0 : _a.requestTransformations.map(cloneRequestTransformation)) != null ? _b : [],
47
+ forwardRules: inspection.forwardRules.map(cloneForwardRule),
48
+ currentPolicy: inspection.currentPolicy
49
+ });
50
+ });
51
+ }
52
+ setRequestTransformations(transformations) {
53
+ return __privateMethod(this, _VercelNetworkPolicyManager_instances, enqueueMutation_fn).call(this, async () => {
54
+ const inspection = __privateMethod(this, _VercelNetworkPolicyManager_instances, inspectPolicy_fn).call(this);
55
+ await __privateMethod(this, _VercelNetworkPolicyManager_instances, applyState_fn).call(this, {
56
+ accessPolicy: inspection.accessPolicy,
57
+ requestTransformations: transformations.map(cloneRequestTransformation),
58
+ forwardRules: inspection.forwardRules.map(cloneForwardRule),
59
+ currentPolicy: inspection.currentPolicy
60
+ });
61
+ });
62
+ }
63
+ addRequestTransformations(transformations) {
64
+ return __privateMethod(this, _VercelNetworkPolicyManager_instances, enqueueMutation_fn).call(this, async () => {
65
+ const inspection = __privateMethod(this, _VercelNetworkPolicyManager_instances, inspectPolicy_fn).call(this);
66
+ const incomingTransformations = transformations.map(
67
+ cloneRequestTransformation
68
+ );
69
+ if (__privateGet(this, _state) == null) {
70
+ const incomingPolicy = composeNetworkPolicy({
71
+ accessPolicy: inspection.accessPolicy,
72
+ requestTransformations: incomingTransformations,
73
+ forwardRules: inspection.forwardRules
74
+ });
75
+ const incomingHosts = getRequestTransformationHosts(incomingPolicy);
76
+ if (!isHostSetSubset({
77
+ subset: inspection.requestTransformationHosts,
78
+ superset: incomingHosts
79
+ })) {
80
+ throw createPolicyConflictError(
81
+ "Cannot add request transformations because the current Vercel Sandbox policy contains request transformations that cannot be attributed to this call. Their header values are redacted, so preserving them safely is not possible."
82
+ );
83
+ }
84
+ }
85
+ await __privateMethod(this, _VercelNetworkPolicyManager_instances, applyState_fn).call(this, {
86
+ accessPolicy: inspection.accessPolicy,
87
+ requestTransformations: __privateGet(this, _state) == null ? incomingTransformations : [
88
+ ...__privateGet(this, _state).requestTransformations.map(
89
+ cloneRequestTransformation
90
+ ),
91
+ ...incomingTransformations
92
+ ],
93
+ forwardRules: inspection.forwardRules.map(cloneForwardRule),
94
+ currentPolicy: inspection.currentPolicy
95
+ });
96
+ });
97
+ }
98
+ };
99
+ _sandbox = new WeakMap();
100
+ _state = new WeakMap();
101
+ _mutationQueue = new WeakMap();
102
+ _VercelNetworkPolicyManager_instances = new WeakSet();
103
+ enqueueMutation_fn = function(operation) {
104
+ const result = __privateGet(this, _mutationQueue).then(operation);
105
+ __privateSet(this, _mutationQueue, result.then(
106
+ () => void 0,
107
+ () => void 0
108
+ ));
109
+ return result;
110
+ };
111
+ inspectPolicy_fn = function() {
112
+ var _a;
113
+ if (__privateGet(this, _state) != null) {
114
+ return {
115
+ accessPolicy: __privateGet(this, _state).accessPolicy,
116
+ forwardRules: __privateGet(this, _state).forwardRules,
117
+ requestTransformationHosts: [],
118
+ currentPolicy: __privateGet(this, _state).effectivePolicy
119
+ };
120
+ }
121
+ return inspectCurrentPolicy(
122
+ (_a = __privateGet(this, _sandbox).currentSession().networkPolicy) != null ? _a : "allow-all"
123
+ );
124
+ };
125
+ applyState_fn = async function({
126
+ accessPolicy,
127
+ requestTransformations,
128
+ forwardRules,
129
+ currentPolicy
130
+ }) {
131
+ const effectivePolicy = composeNetworkPolicy({
132
+ accessPolicy,
133
+ requestTransformations,
134
+ forwardRules
135
+ });
136
+ if (!areNetworkPoliciesEqual({
137
+ first: currentPolicy,
138
+ second: effectivePolicy
139
+ })) {
140
+ await __privateGet(this, _sandbox).update({ networkPolicy: effectivePolicy });
141
+ }
142
+ __privateSet(this, _state, {
143
+ accessPolicy: cloneNetworkAccessPolicy(accessPolicy),
144
+ requestTransformations: requestTransformations.map(
145
+ cloneRequestTransformation
146
+ ),
147
+ forwardRules: forwardRules.map(cloneForwardRule),
148
+ effectivePolicy
149
+ });
150
+ };
151
+ function inspectCurrentPolicy(policy) {
152
+ var _a, _b, _c, _d, _e;
153
+ if (policy === "allow-all" || policy === "deny-all") {
154
+ return {
155
+ accessPolicy: { mode: policy },
156
+ forwardRules: [],
157
+ requestTransformationHosts: [],
158
+ currentPolicy: policy
159
+ };
160
+ }
161
+ const allowedHosts = Array.isArray(policy.allow) ? [...policy.allow] : Object.keys((_a = policy.allow) != null ? _a : {});
162
+ const forwardRules = [];
163
+ const requestTransformationHosts = /* @__PURE__ */ new Set();
164
+ if (policy.allow != null && !Array.isArray(policy.allow)) {
165
+ for (const [host, rules] of Object.entries(policy.allow)) {
166
+ for (const rule of rules) {
167
+ if (hasRequestTransformation(rule)) {
168
+ requestTransformationHosts.add(host.toLowerCase());
169
+ }
170
+ if (rule.forwardURL != null) {
171
+ forwardRules.push({
172
+ host,
173
+ ...rule.match == null ? {} : { match: cloneMatch(rule.match) },
174
+ forwardURL: rule.forwardURL
175
+ });
176
+ }
177
+ }
178
+ }
179
+ }
180
+ return {
181
+ accessPolicy: {
182
+ mode: "custom",
183
+ allowedHosts: minimizeHostPatterns(allowedHosts),
184
+ allowedCIDRs: [...(_c = (_b = policy.subnets) == null ? void 0 : _b.allow) != null ? _c : []],
185
+ deniedCIDRs: [...(_e = (_d = policy.subnets) == null ? void 0 : _d.deny) != null ? _e : []]
186
+ },
187
+ forwardRules,
188
+ requestTransformationHosts: [...requestTransformationHosts],
189
+ currentPolicy: cloneNetworkPolicy(policy)
190
+ };
191
+ }
192
+ function toNetworkAccessPolicy(policy) {
193
+ var _a, _b, _c;
194
+ switch (policy.mode) {
195
+ case "allow-all":
196
+ return { mode: "allow-all" };
197
+ case "deny-all":
198
+ return { mode: "deny-all" };
199
+ case "custom": {
200
+ const allowedHosts = [...(_a = policy.allowedHosts) != null ? _a : []];
201
+ const allowedCIDRs = [...(_b = policy.allowedCIDRs) != null ? _b : []];
202
+ const deniedCIDRs = [...(_c = policy.deniedCIDRs) != null ? _c : []];
203
+ if (allowedHosts.length === 0 && allowedCIDRs.length === 0 && deniedCIDRs.length === 0) {
204
+ throw createPolicyConflictError(
205
+ "Custom network policy requires at least one of allowedHosts, allowedCIDRs, or deniedCIDRs to be non-empty."
206
+ );
207
+ }
208
+ return {
209
+ mode: "custom",
210
+ allowedHosts,
211
+ allowedCIDRs,
212
+ deniedCIDRs
213
+ };
214
+ }
215
+ }
216
+ }
217
+ function composeNetworkPolicy({
218
+ accessPolicy,
219
+ requestTransformations,
220
+ forwardRules
221
+ }) {
222
+ if (accessPolicy.mode === "deny-all") {
223
+ return "deny-all";
224
+ }
225
+ const rulesByHost = /* @__PURE__ */ new Map();
226
+ const allowedHosts = accessPolicy.mode === "allow-all" ? ["*"] : accessPolicy.allowedHosts;
227
+ for (const host of allowedHosts) {
228
+ rulesByHost.set(host, []);
229
+ }
230
+ for (const transformation of requestTransformations) {
231
+ for (const host of getActiveHostPatterns({
232
+ allowedHosts,
233
+ ruleHost: transformation.match.host
234
+ })) {
235
+ appendRule({
236
+ rulesByHost,
237
+ host,
238
+ rule: toVercelRequestTransformationRule(transformation)
239
+ });
240
+ }
241
+ }
242
+ for (const forwardRule of forwardRules) {
243
+ for (const host of getActiveHostPatterns({
244
+ allowedHosts,
245
+ ruleHost: forwardRule.host
246
+ })) {
247
+ appendRule({
248
+ rulesByHost,
249
+ host,
250
+ rule: {
251
+ ...forwardRule.match == null ? {} : { match: cloneMatch(forwardRule.match) },
252
+ forwardURL: forwardRule.forwardURL
253
+ }
254
+ });
255
+ }
256
+ }
257
+ const hasActiveRules = [...rulesByHost.values()].some(
258
+ (rules) => rules.length > 0
259
+ );
260
+ if (!hasActiveRules) {
261
+ return toVercelAccessPolicy(accessPolicy);
262
+ }
263
+ return {
264
+ allow: Object.fromEntries(rulesByHost),
265
+ ...accessPolicy.mode !== "custom" ? {} : toVercelSubnets(accessPolicy)
266
+ };
267
+ }
268
+ function toVercelAccessPolicy(accessPolicy) {
269
+ if (accessPolicy.mode === "allow-all" || accessPolicy.mode === "deny-all") {
270
+ return accessPolicy.mode;
271
+ }
272
+ return {
273
+ ...accessPolicy.allowedHosts.length === 0 ? {} : { allow: [...accessPolicy.allowedHosts] },
274
+ ...toVercelSubnets(accessPolicy)
275
+ };
276
+ }
277
+ function toVercelSubnets(accessPolicy) {
278
+ if (accessPolicy.allowedCIDRs.length === 0 && accessPolicy.deniedCIDRs.length === 0) {
279
+ return {};
280
+ }
281
+ return {
282
+ subnets: {
283
+ ...accessPolicy.allowedCIDRs.length === 0 ? {} : { allow: [...accessPolicy.allowedCIDRs] },
284
+ ...accessPolicy.deniedCIDRs.length === 0 ? {} : { deny: [...accessPolicy.deniedCIDRs] }
285
+ }
286
+ };
287
+ }
288
+ function getActiveHostPatterns({
289
+ allowedHosts,
290
+ ruleHost
291
+ }) {
292
+ return minimizeHostPatterns(
293
+ allowedHosts.flatMap((allowedHost) => {
294
+ const intersection = intersectHostPatterns({
295
+ first: allowedHost,
296
+ second: ruleHost
297
+ });
298
+ return intersection == null ? [] : [intersection];
299
+ })
300
+ );
301
+ }
302
+ function intersectHostPatterns({
303
+ first,
304
+ second
305
+ }) {
306
+ if (isHostPatternSubset({ candidate: first, container: second })) {
307
+ return first;
308
+ }
309
+ if (isHostPatternSubset({ candidate: second, container: first })) {
310
+ return second;
311
+ }
312
+ return void 0;
313
+ }
314
+ function isHostPatternSubset({
315
+ candidate,
316
+ container
317
+ }) {
318
+ const normalizedCandidate = candidate.toLowerCase();
319
+ const normalizedContainer = container.toLowerCase();
320
+ if (normalizedCandidate === normalizedContainer || normalizedContainer === "*") {
321
+ return true;
322
+ }
323
+ if (normalizedCandidate === "*" || !normalizedContainer.startsWith("*")) {
324
+ return false;
325
+ }
326
+ const containerSuffix = normalizedContainer.slice(1);
327
+ return normalizedCandidate.startsWith("*") ? normalizedCandidate.slice(1).endsWith(containerSuffix) : normalizedCandidate.endsWith(containerSuffix);
328
+ }
329
+ function minimizeHostPatterns(patterns) {
330
+ const uniquePatterns = [...new Set(patterns)];
331
+ return uniquePatterns.filter(
332
+ (pattern) => !uniquePatterns.some(
333
+ (other) => other !== pattern && isHostPatternSubset({ candidate: pattern, container: other })
334
+ )
335
+ );
336
+ }
337
+ function appendRule({
338
+ rulesByHost,
339
+ host,
340
+ rule
341
+ }) {
342
+ var _a;
343
+ const rules = (_a = rulesByHost.get(host)) != null ? _a : [];
344
+ rules.push(rule);
345
+ rulesByHost.set(host, rules);
346
+ }
347
+ function toVercelRequestTransformationRule(transformation) {
348
+ const {
349
+ host: _host,
350
+ method,
351
+ path,
352
+ queryString,
353
+ headers
354
+ } = transformation.match;
355
+ const match = {
356
+ ...path == null ? {} : { path: cloneMatcher(path) },
357
+ ...method == null ? {} : { method: [...method] },
358
+ ...queryString == null ? {} : { queryString: queryString.map(cloneKeyValueMatcher) },
359
+ ...headers == null ? {} : { headers: headers.map(cloneKeyValueMatcher) }
360
+ };
361
+ return {
362
+ ...Object.keys(match).length === 0 ? {} : { match },
363
+ transform: [{ headers: { ...transformation.transform.headers } }]
364
+ };
365
+ }
366
+ function hasRequestTransformation(rule) {
367
+ var _a, _b;
368
+ return (_b = (_a = rule.transform) == null ? void 0 : _a.some(
369
+ (transformation) => transformation.headers != null && Object.keys(transformation.headers).length > 0
370
+ )) != null ? _b : false;
371
+ }
372
+ function getRequestTransformationHosts(policy) {
373
+ if (policy === "allow-all" || policy === "deny-all" || policy.allow == null || Array.isArray(policy.allow)) {
374
+ return [];
375
+ }
376
+ const hosts = /* @__PURE__ */ new Set();
377
+ for (const [host, rules] of Object.entries(policy.allow)) {
378
+ for (const rule of rules) {
379
+ if (hasRequestTransformation(rule)) {
380
+ hosts.add(host.toLowerCase());
381
+ }
382
+ }
383
+ }
384
+ return [...hosts];
385
+ }
386
+ function isHostSetSubset({
387
+ subset,
388
+ superset
389
+ }) {
390
+ const allowedHosts = new Set(superset.map((host) => host.toLowerCase()));
391
+ return subset.every((host) => allowedHosts.has(host.toLowerCase()));
392
+ }
393
+ function areNetworkPoliciesEqual({
394
+ first,
395
+ second
396
+ }) {
397
+ return stableSerialize(first) === stableSerialize(second);
398
+ }
399
+ function stableSerialize(value) {
400
+ if (Array.isArray(value)) {
401
+ return `[${value.map(stableSerialize).join(",")}]`;
402
+ }
403
+ if (value != null && typeof value === "object") {
404
+ return `{${Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([firstKey], [secondKey]) => firstKey.localeCompare(secondKey)).map(
405
+ ([key, entryValue]) => `${JSON.stringify(key)}:${stableSerialize(entryValue)}`
406
+ ).join(",")}}`;
407
+ }
408
+ return JSON.stringify(value);
409
+ }
410
+ function cloneRequestTransformation(transformation) {
411
+ return {
412
+ match: {
413
+ host: transformation.match.host,
414
+ ...transformation.match.path == null ? {} : { path: cloneHarnessMatcher(transformation.match.path) },
415
+ ...transformation.match.method == null ? {} : { method: [...transformation.match.method] },
416
+ ...transformation.match.queryString == null ? {} : {
417
+ queryString: transformation.match.queryString.map(
418
+ cloneHarnessKeyValueMatcher
419
+ )
420
+ },
421
+ ...transformation.match.headers == null ? {} : {
422
+ headers: transformation.match.headers.map(
423
+ cloneHarnessKeyValueMatcher
424
+ )
425
+ }
426
+ },
427
+ transform: { headers: { ...transformation.transform.headers } }
428
+ };
429
+ }
430
+ function cloneForwardRule(rule) {
431
+ return {
432
+ host: rule.host,
433
+ ...rule.match == null ? {} : { match: cloneMatch(rule.match) },
434
+ forwardURL: rule.forwardURL
435
+ };
436
+ }
437
+ function cloneNetworkAccessPolicy(policy) {
438
+ if (policy.mode === "allow-all" || policy.mode === "deny-all") {
439
+ return { mode: policy.mode };
440
+ }
441
+ return {
442
+ mode: "custom",
443
+ allowedHosts: [...policy.allowedHosts],
444
+ allowedCIDRs: [...policy.allowedCIDRs],
445
+ deniedCIDRs: [...policy.deniedCIDRs]
446
+ };
447
+ }
448
+ function cloneNetworkPolicy(policy) {
449
+ if (policy === "allow-all" || policy === "deny-all") {
450
+ return policy;
451
+ }
452
+ return {
453
+ ...policy.allow == null ? {} : Array.isArray(policy.allow) ? { allow: [...policy.allow] } : {
454
+ allow: Object.fromEntries(
455
+ Object.entries(policy.allow).map(([host, rules]) => [
456
+ host,
457
+ rules.map((rule) => ({
458
+ ...rule.match == null ? {} : { match: cloneMatch(rule.match) },
459
+ ...rule.transform == null ? {} : {
460
+ transform: rule.transform.map((transform) => ({
461
+ ...transform.headers == null ? {} : { headers: { ...transform.headers } }
462
+ }))
463
+ },
464
+ ...rule.forwardURL == null ? {} : { forwardURL: rule.forwardURL }
465
+ }))
466
+ ])
467
+ )
468
+ },
469
+ ...policy.subnets == null ? {} : {
470
+ subnets: {
471
+ ...policy.subnets.allow == null ? {} : { allow: [...policy.subnets.allow] },
472
+ ...policy.subnets.deny == null ? {} : { deny: [...policy.subnets.deny] }
473
+ }
474
+ }
475
+ };
476
+ }
477
+ function cloneMatch(match) {
478
+ return {
479
+ ...match.path == null ? {} : { path: cloneMatcher(match.path) },
480
+ ...match.method == null ? {} : { method: [...match.method] },
481
+ ...match.queryString == null ? {} : { queryString: match.queryString.map(cloneKeyValueMatcher) },
482
+ ...match.headers == null ? {} : { headers: match.headers.map(cloneKeyValueMatcher) }
483
+ };
484
+ }
485
+ function cloneKeyValueMatcher(matcher) {
486
+ return {
487
+ ...matcher.key == null ? {} : { key: cloneMatcher(matcher.key) },
488
+ ...matcher.value == null ? {} : { value: cloneMatcher(matcher.value) }
489
+ };
490
+ }
491
+ function cloneMatcher(matcher) {
492
+ return { ...matcher };
493
+ }
494
+ function cloneHarnessKeyValueMatcher(matcher) {
495
+ return {
496
+ ...matcher.key == null ? {} : { key: cloneHarnessMatcher(matcher.key) },
497
+ ...matcher.value == null ? {} : { value: cloneHarnessMatcher(matcher.value) }
498
+ };
499
+ }
500
+ function cloneHarnessMatcher(matcher) {
501
+ if ("exact" in matcher) {
502
+ return { exact: matcher.exact };
503
+ }
504
+ if ("startsWith" in matcher) {
505
+ return { startsWith: matcher.startsWith };
506
+ }
507
+ return { regex: matcher.regex };
508
+ }
509
+ function createPolicyConflictError(message) {
510
+ return new HarnessCapabilityUnsupportedError({
511
+ harnessId: VERCEL_PROVIDER_ID,
512
+ message
513
+ });
514
+ }
11
515
 
12
516
  // src/vercel-sandbox-session.ts
13
517
  import { posix } from "path";
@@ -233,16 +737,18 @@ function isFileNotFoundError(error) {
233
737
  }
234
738
 
235
739
  // src/vercel-network-sandbox-session.ts
236
- var VERCEL_PROVIDER_ID = "vercel-sandbox";
740
+ var VERCEL_PROVIDER_ID2 = "vercel-sandbox";
741
+ var _networkPolicyManager;
237
742
  var VercelNetworkSandboxSession = class extends VercelSandboxSession {
238
743
  constructor(input) {
239
744
  super(input.sandbox);
240
- this.getPortUrl = async (options) => {
745
+ __privateAdd(this, _networkPolicyManager);
746
+ this.getPortEndpoint = async (options) => {
241
747
  var _a;
242
748
  const exposedPorts = this.ports;
243
749
  if (!exposedPorts.includes(options.port)) {
244
- throw new HarnessCapabilityUnsupportedError({
245
- harnessId: VERCEL_PROVIDER_ID,
750
+ throw new HarnessCapabilityUnsupportedError2({
751
+ harnessId: VERCEL_PROVIDER_ID2,
246
752
  message: `Port ${options.port} is not exposed on this sandbox. Exposed ports: [${exposedPorts.join(", ")}].`
247
753
  });
248
754
  }
@@ -260,10 +766,22 @@ var VercelNetworkSandboxSession = class extends VercelSandboxSession {
260
766
  url.protocol = isSecure ? "wss:" : "ws:";
261
767
  break;
262
768
  }
263
- return url.toString();
769
+ return { url: url.toString() };
770
+ };
771
+ /**
772
+ * @deprecated Use `getPortEndpoint` instead.
773
+ */
774
+ this.getPortUrl = async (options) => {
775
+ return (await this.getPortEndpoint(options)).url;
264
776
  };
265
777
  this.setNetworkPolicy = async (policy) => {
266
- await this.sandbox.update({ networkPolicy: toVercelPolicy(policy) });
778
+ await __privateGet(this, _networkPolicyManager).setNetworkPolicy(policy);
779
+ };
780
+ this.setRequestTransformations = async (transformations) => {
781
+ await __privateGet(this, _networkPolicyManager).setRequestTransformations(transformations);
782
+ };
783
+ this.addRequestTransformations = async (transformations) => {
784
+ await __privateGet(this, _networkPolicyManager).addRequestTransformations(transformations);
267
785
  };
268
786
  this.setPorts = async (ports, options) => {
269
787
  await this.sandbox.update(
@@ -284,6 +802,9 @@ var VercelNetworkSandboxSession = class extends VercelSandboxSession {
284
802
  this.ownsLifecycle = input.ownsLifecycle;
285
803
  this.id = input.sandbox.name;
286
804
  this.defaultWorkingDirectory = input.sandbox.currentSession().cwd;
805
+ __privateSet(this, _networkPolicyManager, new VercelNetworkPolicyManager({
806
+ sandbox: input.sandbox
807
+ }));
287
808
  }
288
809
  get ports() {
289
810
  return this.sandbox.routes.map((route) => route.port);
@@ -292,38 +813,11 @@ var VercelNetworkSandboxSession = class extends VercelSandboxSession {
292
813
  return new VercelSandboxSession(this.sandbox);
293
814
  }
294
815
  };
295
- function toVercelPolicy(policy) {
296
- switch (policy.mode) {
297
- case "allow-all":
298
- return "allow-all";
299
- case "deny-all":
300
- return "deny-all";
301
- case "custom": {
302
- const result = {};
303
- const { allowedHosts, allowedCIDRs, deniedCIDRs } = policy;
304
- if (allowedHosts != null && allowedHosts.length > 0) {
305
- result.allow = [...allowedHosts];
306
- }
307
- if (allowedCIDRs != null && allowedCIDRs.length > 0 || deniedCIDRs != null && deniedCIDRs.length > 0) {
308
- result.subnets = {
309
- ...allowedCIDRs != null && allowedCIDRs.length > 0 ? { allow: [...allowedCIDRs] } : {},
310
- ...deniedCIDRs != null && deniedCIDRs.length > 0 ? { deny: [...deniedCIDRs] } : {}
311
- };
312
- }
313
- if (result.allow == null && result.subnets == null) {
314
- throw new HarnessCapabilityUnsupportedError({
315
- harnessId: VERCEL_PROVIDER_ID,
316
- message: "Custom network policy requires at least one of allowedHosts or allowedCIDRs to be non-empty."
317
- });
318
- }
319
- return result;
320
- }
321
- }
322
- }
816
+ _networkPolicyManager = new WeakMap();
323
817
 
324
818
  // src/vercel-sandbox.ts
325
819
  var DEFAULT_SANDBOX_TIMEOUT_MS = 30 * 60 * 1e3;
326
- var VERCEL_PROVIDER_ID2 = "vercel-sandbox";
820
+ var VERCEL_PROVIDER_ID3 = "vercel-sandbox";
327
821
  var TEMPLATE_NAME_PREFIX = "ai-sdk-harness";
328
822
  var SESSION_NAME_PREFIX = "ai-sdk-harness-session";
329
823
  var SNAPSHOT_POLL_INTERVAL_MS = 500;
@@ -338,7 +832,7 @@ var VercelSandboxProvider = class {
338
832
  constructor(settings) {
339
833
  this.settings = settings;
340
834
  this.specificationVersion = "harness-sandbox-v1";
341
- this.providerId = VERCEL_PROVIDER_ID2;
835
+ this.providerId = VERCEL_PROVIDER_ID3;
342
836
  this.createSession = async (options) => {
343
837
  var _a, _b, _c;
344
838
  (_a = options == null ? void 0 : options.abortSignal) == null ? void 0 : _a.throwIfAborted();
@@ -501,7 +995,7 @@ async function withVercelSandboxAuthenticationError({
501
995
  }
502
996
  throw new HarnessSandboxAuthenticationError({
503
997
  message: VERCEL_SANDBOX_AUTHENTICATION_MESSAGE,
504
- sandboxProviderId: VERCEL_PROVIDER_ID2,
998
+ sandboxProviderId: VERCEL_PROVIDER_ID3,
505
999
  cause: error
506
1000
  });
507
1001
  }