@ai-sdk/sandbox-vercel 1.0.70 → 1.0.72
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 +18 -0
- package/README.md +8 -1
- package/dist/index.js +531 -37
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/vercel-network-policy-manager.ts +760 -0
- package/src/vercel-network-sandbox-session.ts +34 -42
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @ai-sdk/sandbox-vercel
|
|
2
2
|
|
|
3
|
+
## 1.0.72
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 69bb613: feat(harness): support request transformations in network sandbox abstraction and use it to apply credential brokering when available
|
|
8
|
+
- 52bc889: feat(harness): add `getPortEndpoint()` as more comprehensive replacement to `getPortUrl()` (now deprecated) in `HarnessV1NetworkSandboxSession`
|
|
9
|
+
- Updated dependencies [69bb613]
|
|
10
|
+
- Updated dependencies [52bc889]
|
|
11
|
+
- Updated dependencies [4cd4989]
|
|
12
|
+
- @ai-sdk/harness@1.0.72
|
|
13
|
+
|
|
14
|
+
## 1.0.71
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated dependencies [8d717b3]
|
|
19
|
+
- @ai-sdk/harness@1.0.71
|
|
20
|
+
|
|
3
21
|
## 1.0.70
|
|
4
22
|
|
|
5
23
|
### 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`, `
|
|
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
|
|
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
|
|
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
|
|
245
|
-
harnessId:
|
|
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.
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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:
|
|
998
|
+
sandboxProviderId: VERCEL_PROVIDER_ID3,
|
|
505
999
|
cause: error
|
|
506
1000
|
});
|
|
507
1001
|
}
|