@better-agent/plugins 0.1.0-canary.6 → 0.2.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +33 -76
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +281 -332
- package/dist/index.mjs.map +1 -1
- package/package.json +21 -5
package/dist/index.mjs
CHANGED
|
@@ -1,114 +1,25 @@
|
|
|
1
1
|
import { BetterAgentError } from "@better-agent/shared/errors";
|
|
2
2
|
import { defineTool } from "@better-agent/core";
|
|
3
3
|
|
|
4
|
-
//#region src/shared/json.ts
|
|
5
|
-
/** Creates a JSON response with a default content type. */
|
|
6
|
-
function jsonResponse(body, init) {
|
|
7
|
-
const headers = new Headers(init?.headers);
|
|
8
|
-
if (!headers.has("content-type")) headers.set("content-type", "application/json");
|
|
9
|
-
return new Response(JSON.stringify(body), {
|
|
10
|
-
...init,
|
|
11
|
-
headers
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
//#endregion
|
|
16
|
-
//#region src/auth/responses.ts
|
|
17
|
-
/** Creates the default unauthorized response. */
|
|
18
|
-
function createUnauthorizedResponse() {
|
|
19
|
-
return jsonResponse({
|
|
20
|
-
error: "unauthorized",
|
|
21
|
-
message: "Invalid API key."
|
|
22
|
-
}, { status: 401 });
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
//#endregion
|
|
26
|
-
//#region src/shared/validation.ts
|
|
27
|
-
/** Creates a plugin validation error. */
|
|
28
|
-
function createValidationError$1(message, at) {
|
|
29
|
-
return BetterAgentError.fromCode("VALIDATION_FAILED", message, { trace: [{ at }] });
|
|
30
|
-
}
|
|
31
|
-
/** Requires a positive finite number. */
|
|
32
|
-
function requirePositiveNumber(value, name, at) {
|
|
33
|
-
if (!Number.isFinite(value) || value <= 0) throw createValidationError$1(`\`${name}\` must be a positive number.`, at);
|
|
34
|
-
}
|
|
35
|
-
/** Requires a non-empty array. */
|
|
36
|
-
function requireNonEmptyArray(value, name, at) {
|
|
37
|
-
if (!value || value.length === 0) throw createValidationError$1(`\`${name}\` must contain at least one value.`, at);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
//#endregion
|
|
41
|
-
//#region src/auth/validate.ts
|
|
42
|
-
/** Validates `authPlugin` configuration. */
|
|
43
|
-
function validateAuthPluginConfig(config) {
|
|
44
|
-
if (!config.validate && (!config.apiKeys || config.apiKeys.length === 0)) throw createValidationError$1("`authPlugin` requires either `apiKeys` or `validate`.", "plugins.authPlugin");
|
|
45
|
-
if (config.header !== void 0 && config.header.trim().length === 0) throw createValidationError$1("`authPlugin` requires `header` to be a non-empty string when provided.", "plugins.authPlugin");
|
|
46
|
-
if (config.apiKeys) {
|
|
47
|
-
if (config.apiKeys.filter((key) => typeof key === "string").map((key) => key.trim()).filter((key) => key.length > 0).length === 0 && !config.validate) throw createValidationError$1("`authPlugin` requires `apiKeys` to contain at least one non-empty key.", "plugins.authPlugin");
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
//#endregion
|
|
52
|
-
//#region src/auth/plugin.ts
|
|
53
|
-
/**
|
|
54
|
-
* Creates an API-key auth plugin.
|
|
55
|
-
*
|
|
56
|
-
* Provide either `apiKeys` or `validate`.
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```ts
|
|
60
|
-
* const plugin = authPlugin({
|
|
61
|
-
* apiKeys: ["dev-key"],
|
|
62
|
-
* });
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
|
-
const authPlugin = (config) => {
|
|
66
|
-
validateAuthPluginConfig(config);
|
|
67
|
-
const header = config.header?.trim() || "x-api-key";
|
|
68
|
-
const apiKeys = new Set((config.apiKeys ?? []).map((key) => key.trim()).filter(Boolean));
|
|
69
|
-
return {
|
|
70
|
-
id: config.id ?? "auth",
|
|
71
|
-
guards: [async (ctx) => {
|
|
72
|
-
const keyValue = config.getKey ? await config.getKey({
|
|
73
|
-
agentName: ctx.agentName,
|
|
74
|
-
mode: ctx.mode,
|
|
75
|
-
request: ctx.request
|
|
76
|
-
}) : ctx.request.headers.get(header);
|
|
77
|
-
const key = typeof keyValue === "string" && keyValue.trim().length > 0 ? keyValue : null;
|
|
78
|
-
if (config.validate ? await config.validate({
|
|
79
|
-
key,
|
|
80
|
-
agentName: ctx.agentName,
|
|
81
|
-
mode: ctx.mode,
|
|
82
|
-
request: ctx.request
|
|
83
|
-
}) : key !== null && apiKeys.has(key)) return null;
|
|
84
|
-
return config.onUnauthorized ? await config.onUnauthorized({
|
|
85
|
-
key,
|
|
86
|
-
agentName: ctx.agentName,
|
|
87
|
-
mode: ctx.mode,
|
|
88
|
-
request: ctx.request
|
|
89
|
-
}) : createUnauthorizedResponse();
|
|
90
|
-
}]
|
|
91
|
-
};
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
//#endregion
|
|
95
4
|
//#region src/ip-allowlist/ip.ts
|
|
96
5
|
/** Parses an IPv4 string. */
|
|
97
6
|
function parseIpv4(input) {
|
|
98
7
|
const parts = input.split(".");
|
|
99
8
|
if (parts.length !== 4) return null;
|
|
100
9
|
let value = 0n;
|
|
10
|
+
const normalizedParts = [];
|
|
101
11
|
for (const part of parts) {
|
|
102
12
|
if (!/^\d+$/.test(part)) return null;
|
|
103
13
|
const octet = Number(part);
|
|
104
14
|
if (octet < 0 || octet > 255) return null;
|
|
105
15
|
value = value << 8n | BigInt(octet);
|
|
16
|
+
normalizedParts.push(String(octet));
|
|
106
17
|
}
|
|
107
18
|
return {
|
|
108
19
|
kind: "ipv4",
|
|
109
20
|
value,
|
|
110
21
|
bits: 32,
|
|
111
|
-
normalized:
|
|
22
|
+
normalized: normalizedParts.join(".")
|
|
112
23
|
};
|
|
113
24
|
}
|
|
114
25
|
/** Expands an IPv6 string into eight normalized segments. */
|
|
@@ -210,6 +121,18 @@ function parseAllowEntry(input) {
|
|
|
210
121
|
};
|
|
211
122
|
}
|
|
212
123
|
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/shared/json.ts
|
|
126
|
+
/** Creates a JSON response with a default content type. */
|
|
127
|
+
function jsonResponse(body, init) {
|
|
128
|
+
const headers = new Headers(init?.headers);
|
|
129
|
+
if (!headers.has("content-type")) headers.set("content-type", "application/json");
|
|
130
|
+
return new Response(JSON.stringify(body), {
|
|
131
|
+
...init,
|
|
132
|
+
headers
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
213
136
|
//#endregion
|
|
214
137
|
//#region src/ip-allowlist/responses.ts
|
|
215
138
|
/** Creates the default IP denied response. */
|
|
@@ -220,12 +143,27 @@ function createIpDeniedResponse() {
|
|
|
220
143
|
}, { status: 403 });
|
|
221
144
|
}
|
|
222
145
|
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/shared/validation.ts
|
|
148
|
+
/** Creates a plugin validation error. */
|
|
149
|
+
function createValidationError$1(message, at) {
|
|
150
|
+
return BetterAgentError.fromCode("VALIDATION_FAILED", message, { trace: [{ at }] });
|
|
151
|
+
}
|
|
152
|
+
/** Requires a positive finite number. */
|
|
153
|
+
function requirePositiveNumber(value, name, at) {
|
|
154
|
+
if (!Number.isFinite(value) || value <= 0) throw createValidationError$1(`\`${name}\` must be a positive number.`, at);
|
|
155
|
+
}
|
|
156
|
+
/** Requires a non-empty array. */
|
|
157
|
+
function requireNonEmptyArray(value, name, at) {
|
|
158
|
+
if (!value || value.length === 0) throw createValidationError$1(`\`${name}\` must contain at least one value.`, at);
|
|
159
|
+
}
|
|
160
|
+
|
|
223
161
|
//#endregion
|
|
224
162
|
//#region src/ip-allowlist/validate.ts
|
|
225
|
-
/** Validates `
|
|
226
|
-
function
|
|
227
|
-
requireNonEmptyArray(config.allow, "allow", "plugins.
|
|
228
|
-
for (const entry of config.allow) if (typeof entry !== "string" || !parseAllowEntry(entry)) throw createValidationError$1(`\`
|
|
163
|
+
/** Validates `ipAllowlist` configuration. */
|
|
164
|
+
function validateIpAllowlistConfig(config) {
|
|
165
|
+
requireNonEmptyArray(config.allow, "allow", "plugins.ipAllowlist");
|
|
166
|
+
for (const entry of config.allow) if (typeof entry !== "string" || !parseAllowEntry(entry)) throw createValidationError$1(`\`ipAllowlist\` received an invalid allow entry: '${String(entry)}'.`, "plugins.ipAllowlist");
|
|
229
167
|
}
|
|
230
168
|
|
|
231
169
|
//#endregion
|
|
@@ -261,13 +199,13 @@ function getDirectIp(request) {
|
|
|
261
199
|
*
|
|
262
200
|
* @example
|
|
263
201
|
* ```ts
|
|
264
|
-
* const plugin =
|
|
202
|
+
* const plugin = ipAllowlist({
|
|
265
203
|
* allow: ["127.0.0.1", "10.0.0.0/8"],
|
|
266
204
|
* });
|
|
267
205
|
* ```
|
|
268
206
|
*/
|
|
269
|
-
const
|
|
270
|
-
|
|
207
|
+
const ipAllowlist = (config) => {
|
|
208
|
+
validateIpAllowlistConfig(config);
|
|
271
209
|
const matchers = config.allow.map((entry) => {
|
|
272
210
|
const matcher = parseAllowEntry(entry);
|
|
273
211
|
if (!matcher) throw new Error(`Invalid allowlist entry: ${entry}`);
|
|
@@ -278,7 +216,6 @@ const ipAllowlistPlugin = (config) => {
|
|
|
278
216
|
guards: [async (ctx) => {
|
|
279
217
|
const resolvedIp = config.getIp ? await config.getIp({
|
|
280
218
|
agentName: ctx.agentName,
|
|
281
|
-
mode: ctx.mode,
|
|
282
219
|
request: ctx.request
|
|
283
220
|
}) : config.trustProxy ? getProxyIp(ctx.request) : getDirectIp(ctx.request);
|
|
284
221
|
const normalizedIp = typeof resolvedIp === "string" && resolvedIp.trim().length > 0 ? normalizeIp(resolvedIp) : null;
|
|
@@ -287,7 +224,6 @@ const ipAllowlistPlugin = (config) => {
|
|
|
287
224
|
return config.onDenied ? await config.onDenied({
|
|
288
225
|
ip: normalizedIp,
|
|
289
226
|
agentName: ctx.agentName,
|
|
290
|
-
mode: ctx.mode,
|
|
291
227
|
request: ctx.request
|
|
292
228
|
}) : createIpDeniedResponse();
|
|
293
229
|
}]
|
|
@@ -325,11 +261,6 @@ function redactHeaders(headers, extraHeaders) {
|
|
|
325
261
|
return result;
|
|
326
262
|
}
|
|
327
263
|
|
|
328
|
-
//#endregion
|
|
329
|
-
//#region src/logging/validate.ts
|
|
330
|
-
/** Validates `loggingPlugin` configuration. */
|
|
331
|
-
function validateLoggingPluginConfig(_config) {}
|
|
332
|
-
|
|
333
264
|
//#endregion
|
|
334
265
|
//#region src/logging/plugin.ts
|
|
335
266
|
function safeInvoke(fn, payload) {
|
|
@@ -338,6 +269,14 @@ function safeInvoke(fn, payload) {
|
|
|
338
269
|
fn(payload);
|
|
339
270
|
} catch {}
|
|
340
271
|
}
|
|
272
|
+
function safeMap(fn, input) {
|
|
273
|
+
if (!fn) return input;
|
|
274
|
+
try {
|
|
275
|
+
return fn(input);
|
|
276
|
+
} catch {
|
|
277
|
+
return input;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
341
280
|
/** Resolves the logger methods for the configured sink. */
|
|
342
281
|
function getLoggerMethods(config) {
|
|
343
282
|
return {
|
|
@@ -350,7 +289,7 @@ function getLoggerMethods(config) {
|
|
|
350
289
|
/** Emits one log entry. */
|
|
351
290
|
function emitLog(config, entry) {
|
|
352
291
|
if (!shouldLog(config.level ?? "info", entry)) return;
|
|
353
|
-
const output = config.format
|
|
292
|
+
const output = safeMap(config.format, entry);
|
|
354
293
|
safeInvoke(getLoggerMethods(config)[entry.level], output);
|
|
355
294
|
}
|
|
356
295
|
/** Maps one runtime event to a log level. */
|
|
@@ -358,16 +297,25 @@ function getEventLevel(event) {
|
|
|
358
297
|
if (event.type.endsWith("_ERROR")) return "error";
|
|
359
298
|
return "info";
|
|
360
299
|
}
|
|
361
|
-
|
|
362
|
-
function createRequestData(ctx) {
|
|
300
|
+
function createRedactedRequestData(ctx, config) {
|
|
363
301
|
return {
|
|
364
|
-
mode: ctx.mode,
|
|
365
302
|
url: ctx.request.url,
|
|
366
303
|
method: ctx.request.method,
|
|
367
|
-
headers: redactHeaders(ctx.request.headers),
|
|
304
|
+
headers: redactHeaders(ctx.request.headers, config.redactHeaders),
|
|
368
305
|
input: ctx.input
|
|
369
306
|
};
|
|
370
307
|
}
|
|
308
|
+
function redactBody(config, phase, body) {
|
|
309
|
+
if (!config.redactBody) return body;
|
|
310
|
+
try {
|
|
311
|
+
return config.redactBody({
|
|
312
|
+
body,
|
|
313
|
+
phase
|
|
314
|
+
});
|
|
315
|
+
} catch {
|
|
316
|
+
return body;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
371
319
|
/** Creates step log data. */
|
|
372
320
|
function createStepData(ctx) {
|
|
373
321
|
return {
|
|
@@ -376,49 +324,36 @@ function createStepData(ctx) {
|
|
|
376
324
|
messageCount: ctx.messages.length
|
|
377
325
|
};
|
|
378
326
|
}
|
|
379
|
-
/** Creates save log data. */
|
|
380
|
-
function createSaveData(ctx) {
|
|
381
|
-
const messageCount = ctx.items.filter((item) => item.type === "message").length;
|
|
382
|
-
return {
|
|
383
|
-
itemCount: ctx.items.length,
|
|
384
|
-
messageCount
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
327
|
/**
|
|
388
328
|
* Creates a logging plugin.
|
|
389
329
|
*
|
|
390
330
|
* @example
|
|
391
331
|
* ```ts
|
|
392
|
-
* const plugin =
|
|
332
|
+
* const plugin = logging({
|
|
393
333
|
* level: "info",
|
|
394
334
|
* include: { requests: true, toolCalls: true },
|
|
395
335
|
* });
|
|
396
336
|
* ```
|
|
397
337
|
*/
|
|
398
|
-
const
|
|
399
|
-
/* @__PURE__ */ validateLoggingPluginConfig(config);
|
|
338
|
+
const logging = (config = {}) => {
|
|
400
339
|
const include = {
|
|
401
340
|
requests: config.include?.requests ?? true,
|
|
402
341
|
events: config.include?.events ?? true,
|
|
403
342
|
steps: config.include?.steps ?? true,
|
|
404
343
|
modelCalls: config.include?.modelCalls ?? true,
|
|
405
344
|
toolCalls: config.include?.toolCalls ?? true,
|
|
406
|
-
saves: config.include?.saves ?? false,
|
|
407
345
|
errors: config.include?.errors ?? true
|
|
408
346
|
};
|
|
409
347
|
const plugin = { id: config.id ?? "logging" };
|
|
410
348
|
if (include.requests) plugin.guards = [async (ctx) => {
|
|
411
|
-
const body = config.redactBody ? config.
|
|
412
|
-
body: ctx.input,
|
|
413
|
-
phase: "request"
|
|
414
|
-
}) : ctx.input;
|
|
349
|
+
const body = config.redactBody ? redactBody(config, "request", ctx.input) : ctx.input;
|
|
415
350
|
emitLog(config, {
|
|
416
351
|
level: "info",
|
|
417
352
|
event: "request.received",
|
|
418
353
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
419
354
|
agentName: ctx.agentName,
|
|
420
355
|
data: {
|
|
421
|
-
...
|
|
356
|
+
...createRedactedRequestData(ctx, config),
|
|
422
357
|
input: body
|
|
423
358
|
}
|
|
424
359
|
});
|
|
@@ -430,10 +365,10 @@ const loggingPlugin = (config = {}) => {
|
|
|
430
365
|
emitLog(config, {
|
|
431
366
|
level,
|
|
432
367
|
event: "run.event",
|
|
433
|
-
timestamp: new Date(event.timestamp).toISOString(),
|
|
368
|
+
timestamp: new Date(event.timestamp ?? Date.now()).toISOString(),
|
|
434
369
|
agentName: ctx.agentName,
|
|
435
370
|
runId: ctx.runId,
|
|
436
|
-
conversationId: ctx.
|
|
371
|
+
conversationId: ctx.threadId,
|
|
437
372
|
data: { type: event.type }
|
|
438
373
|
});
|
|
439
374
|
};
|
|
@@ -444,7 +379,7 @@ const loggingPlugin = (config = {}) => {
|
|
|
444
379
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
445
380
|
agentName: ctx.agentName,
|
|
446
381
|
runId: ctx.runId,
|
|
447
|
-
conversationId: ctx.
|
|
382
|
+
conversationId: ctx.threadId,
|
|
448
383
|
data: createStepData(ctx)
|
|
449
384
|
});
|
|
450
385
|
};
|
|
@@ -456,10 +391,10 @@ const loggingPlugin = (config = {}) => {
|
|
|
456
391
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
457
392
|
agentName: ctx.agentName,
|
|
458
393
|
runId: ctx.runId,
|
|
459
|
-
conversationId: ctx.
|
|
394
|
+
conversationId: ctx.threadId,
|
|
460
395
|
data: {
|
|
461
396
|
stepIndex: ctx.stepIndex,
|
|
462
|
-
inputCount: ctx.
|
|
397
|
+
inputCount: ctx.messages.length,
|
|
463
398
|
toolCount: ctx.tools.length,
|
|
464
399
|
toolChoice: ctx.toolChoice
|
|
465
400
|
}
|
|
@@ -472,13 +407,10 @@ const loggingPlugin = (config = {}) => {
|
|
|
472
407
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
473
408
|
agentName: ctx.agentName,
|
|
474
409
|
runId: ctx.runId,
|
|
475
|
-
conversationId: ctx.
|
|
410
|
+
conversationId: ctx.threadId,
|
|
476
411
|
data: {
|
|
477
412
|
stepIndex: ctx.stepIndex,
|
|
478
|
-
response: config.redactBody ? config.
|
|
479
|
-
body: ctx.response,
|
|
480
|
-
phase: "response"
|
|
481
|
-
}) : ctx.response
|
|
413
|
+
response: config.redactBody ? redactBody(config, "response", ctx.response) : ctx.response
|
|
482
414
|
}
|
|
483
415
|
});
|
|
484
416
|
};
|
|
@@ -491,14 +423,11 @@ const loggingPlugin = (config = {}) => {
|
|
|
491
423
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
492
424
|
agentName: ctx.agentName,
|
|
493
425
|
runId: ctx.runId,
|
|
494
|
-
conversationId: ctx.
|
|
426
|
+
conversationId: ctx.threadId,
|
|
495
427
|
data: {
|
|
496
428
|
toolName: ctx.toolName,
|
|
497
429
|
toolCallId: ctx.toolCallId,
|
|
498
|
-
args: config.redactBody ? config.
|
|
499
|
-
body: ctx.args,
|
|
500
|
-
phase: "tool_args"
|
|
501
|
-
}) : ctx.args
|
|
430
|
+
args: config.redactBody ? redactBody(config, "tool_args", ctx.input) : ctx.input
|
|
502
431
|
}
|
|
503
432
|
});
|
|
504
433
|
};
|
|
@@ -509,36 +438,16 @@ const loggingPlugin = (config = {}) => {
|
|
|
509
438
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
510
439
|
agentName: ctx.agentName,
|
|
511
440
|
runId: ctx.runId,
|
|
512
|
-
conversationId: ctx.
|
|
441
|
+
conversationId: ctx.threadId,
|
|
513
442
|
data: {
|
|
514
443
|
toolName: ctx.toolName,
|
|
515
444
|
toolCallId: ctx.toolCallId,
|
|
516
445
|
error: ctx.error,
|
|
517
|
-
result: config.redactBody ? config.
|
|
518
|
-
body: ctx.result,
|
|
519
|
-
phase: "tool_result"
|
|
520
|
-
}) : ctx.result
|
|
446
|
+
result: config.redactBody ? redactBody(config, "tool_result", ctx.result) : ctx.result
|
|
521
447
|
}
|
|
522
448
|
});
|
|
523
449
|
};
|
|
524
450
|
}
|
|
525
|
-
if (include.saves) plugin.onBeforeSave = async (ctx) => {
|
|
526
|
-
emitLog(config, {
|
|
527
|
-
level: "debug",
|
|
528
|
-
event: "save.before",
|
|
529
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
530
|
-
agentName: ctx.agentName,
|
|
531
|
-
runId: ctx.runId,
|
|
532
|
-
conversationId: ctx.conversationId,
|
|
533
|
-
data: {
|
|
534
|
-
...createSaveData(ctx),
|
|
535
|
-
items: config.redactBody ? config.redactBody({
|
|
536
|
-
body: ctx.items,
|
|
537
|
-
phase: "save"
|
|
538
|
-
}) : ctx.items
|
|
539
|
-
}
|
|
540
|
-
});
|
|
541
|
-
};
|
|
542
451
|
return plugin;
|
|
543
452
|
};
|
|
544
453
|
|
|
@@ -562,17 +471,29 @@ function createBucket(params) {
|
|
|
562
471
|
/** Creates an in-memory CAS store for rate limiting. */
|
|
563
472
|
function createMemoryStore() {
|
|
564
473
|
const rows = /* @__PURE__ */ new Map();
|
|
474
|
+
const pruneExpiredRows = (bucket) => {
|
|
475
|
+
const nowMs = bucket.now.getTime();
|
|
476
|
+
for (const [id, row] of rows) if (row.windowEndMs <= nowMs) rows.delete(id);
|
|
477
|
+
};
|
|
478
|
+
const toStoredRow = (bucket, state) => ({
|
|
479
|
+
...state,
|
|
480
|
+
windowEndMs: bucket.windowEnd.getTime()
|
|
481
|
+
});
|
|
565
482
|
return {
|
|
566
|
-
read: async ({ bucket }) =>
|
|
483
|
+
read: async ({ bucket }) => {
|
|
484
|
+
pruneExpiredRows(bucket);
|
|
485
|
+
return rows.get(bucket.id) ?? null;
|
|
486
|
+
},
|
|
567
487
|
write: async ({ bucket, prevVersion, next }) => {
|
|
488
|
+
pruneExpiredRows(bucket);
|
|
568
489
|
const current = rows.get(bucket.id) ?? null;
|
|
569
490
|
if (prevVersion === null) {
|
|
570
491
|
if (current) return false;
|
|
571
|
-
rows.set(bucket.id, next);
|
|
492
|
+
rows.set(bucket.id, toStoredRow(bucket, next));
|
|
572
493
|
return true;
|
|
573
494
|
}
|
|
574
495
|
if (!current || current.version !== prevVersion) return false;
|
|
575
|
-
rows.set(bucket.id, next);
|
|
496
|
+
rows.set(bucket.id, toStoredRow(bucket, next));
|
|
576
497
|
return true;
|
|
577
498
|
}
|
|
578
499
|
};
|
|
@@ -609,16 +530,16 @@ function createRateLimitStorageUnavailableResponse() {
|
|
|
609
530
|
}
|
|
610
531
|
/** Creates the CAS retries exceeded error. */
|
|
611
532
|
function createCasRetriesExceededError() {
|
|
612
|
-
return BetterAgentError.fromCode("INTERNAL", "Rate limit write failed after CAS retries.", { trace: [{ at: "plugins.
|
|
533
|
+
return BetterAgentError.fromCode("INTERNAL", "Rate limit write failed after CAS retries.", { trace: [{ at: "plugins.rateLimit" }] });
|
|
613
534
|
}
|
|
614
535
|
|
|
615
536
|
//#endregion
|
|
616
537
|
//#region src/rate-limit/validate.ts
|
|
617
|
-
/** Validates `
|
|
618
|
-
function
|
|
619
|
-
requirePositiveNumber(config.windowMs, "windowMs", "plugins.
|
|
620
|
-
requirePositiveNumber(config.max, "max", "plugins.
|
|
621
|
-
requirePositiveNumber(config.casRetries ?? 8, "casRetries", "plugins.
|
|
538
|
+
/** Validates `rateLimit` configuration. */
|
|
539
|
+
function validateRateLimitConfig(config) {
|
|
540
|
+
requirePositiveNumber(config.windowMs, "windowMs", "plugins.rateLimit");
|
|
541
|
+
requirePositiveNumber(config.max, "max", "plugins.rateLimit");
|
|
542
|
+
requirePositiveNumber(config.casRetries ?? 8, "casRetries", "plugins.rateLimit");
|
|
622
543
|
}
|
|
623
544
|
|
|
624
545
|
//#endregion
|
|
@@ -630,23 +551,23 @@ function validateRateLimitPluginConfig(config) {
|
|
|
630
551
|
*
|
|
631
552
|
* @example
|
|
632
553
|
* ```ts
|
|
633
|
-
* const plugin =
|
|
554
|
+
* const plugin = rateLimit({
|
|
634
555
|
* windowMs: 60_000,
|
|
635
556
|
* max: 30,
|
|
636
557
|
* });
|
|
637
558
|
* ```
|
|
638
559
|
*/
|
|
639
|
-
const
|
|
640
|
-
|
|
560
|
+
const rateLimit = (config) => {
|
|
561
|
+
validateRateLimitConfig(config);
|
|
641
562
|
const casRetries = config.casRetries ?? 8;
|
|
642
563
|
const storage = config.storage ?? createMemoryStore();
|
|
643
564
|
return {
|
|
644
565
|
id: config.id ?? "rate-limit",
|
|
645
566
|
guards: [async (ctx) => {
|
|
646
567
|
const request = {
|
|
647
|
-
mode: ctx.mode,
|
|
648
568
|
agentName: ctx.agentName,
|
|
649
|
-
request: ctx.request
|
|
569
|
+
request: ctx.request,
|
|
570
|
+
auth: ctx.auth
|
|
650
571
|
};
|
|
651
572
|
const now = /* @__PURE__ */ new Date();
|
|
652
573
|
const nowMs = now.getTime();
|
|
@@ -973,21 +894,21 @@ function validateSandboxCreateParams(clientProvider, params) {
|
|
|
973
894
|
const provider = clientProvider?.trim().toLowerCase();
|
|
974
895
|
if (!provider) return;
|
|
975
896
|
if (provider === "daytona") {
|
|
976
|
-
if (params.lifecycle?.ttlMs !== void 0) throw createValidationError$1("`lifecycle.ttlMs` is not supported by the Daytona sandbox client. Use `startupTimeoutMs` for creation readiness and Daytona lifecycle fields like `idleStopMs`, `archiveAfterMs`, or `deleteAfterMs` instead.", "plugins.
|
|
897
|
+
if (params.lifecycle?.ttlMs !== void 0) throw createValidationError$1("`lifecycle.ttlMs` is not supported by the Daytona sandbox client. Use `startupTimeoutMs` for creation readiness and Daytona lifecycle fields like `idleStopMs`, `archiveAfterMs`, or `deleteAfterMs` instead.", "plugins.sandbox.createConfig.lifecycle.ttlMs");
|
|
977
898
|
return;
|
|
978
899
|
}
|
|
979
900
|
if (provider === "e2b") {
|
|
980
|
-
if (params.startupTimeoutMs !== void 0) throw createValidationError$1("`startupTimeoutMs` is not supported by the E2B sandbox client. Use `lifecycle.ttlMs` to control sandbox lifetime.", "plugins.
|
|
981
|
-
if (params.lifecycle?.idleStopMs !== void 0 || params.lifecycle?.archiveAfterMs !== void 0 || params.lifecycle?.deleteAfterMs !== void 0) throw createValidationError$1("`lifecycle.idleStopMs`, `lifecycle.archiveAfterMs`, and `lifecycle.deleteAfterMs` are not supported by the E2B sandbox client.", "plugins.
|
|
901
|
+
if (params.startupTimeoutMs !== void 0) throw createValidationError$1("`startupTimeoutMs` is not supported by the E2B sandbox client. Use `lifecycle.ttlMs` to control sandbox lifetime.", "plugins.sandbox.createConfig.startupTimeoutMs");
|
|
902
|
+
if (params.lifecycle?.idleStopMs !== void 0 || params.lifecycle?.archiveAfterMs !== void 0 || params.lifecycle?.deleteAfterMs !== void 0) throw createValidationError$1("`lifecycle.idleStopMs`, `lifecycle.archiveAfterMs`, and `lifecycle.deleteAfterMs` are not supported by the E2B sandbox client.", "plugins.sandbox.createConfig.lifecycle");
|
|
982
903
|
return;
|
|
983
904
|
}
|
|
984
905
|
if (hasLifecycleValues(params.lifecycle) || params.startupTimeoutMs !== void 0) return;
|
|
985
906
|
}
|
|
986
|
-
/** Validates `
|
|
987
|
-
function
|
|
907
|
+
/** Validates `sandbox` configuration. */
|
|
908
|
+
function validateSandboxConfig(config) {
|
|
988
909
|
const client = config.client;
|
|
989
|
-
if (!client || typeof client !== "object") throw createValidationError$1("`
|
|
990
|
-
if (config.prefix !== void 0 && config.prefix.trim().length === 0) throw createValidationError$1("`
|
|
910
|
+
if (!client || typeof client !== "object") throw createValidationError$1("`sandbox` requires a `client`.", "plugins.sandbox");
|
|
911
|
+
if (config.prefix !== void 0 && config.prefix.trim().length === 0) throw createValidationError$1("`sandbox` requires `prefix` to be a non-empty string when provided.", "plugins.sandbox");
|
|
991
912
|
if (config.createConfig) validateSandboxCreateParams(config.client.provider, config.createConfig);
|
|
992
913
|
if (config.createDefaults) validateSandboxCreateParams(config.client.provider, config.createDefaults);
|
|
993
914
|
}
|
|
@@ -1025,7 +946,7 @@ const resolveCreateParams = (overrides, createConfig, createDefaults) => ({
|
|
|
1025
946
|
/**
|
|
1026
947
|
* Adds sandbox tools.
|
|
1027
948
|
*
|
|
1028
|
-
* By default, the plugin reuses one sandbox per `
|
|
949
|
+
* By default, the plugin reuses one sandbox per `threadId`.
|
|
1029
950
|
* Use `sessionKey` to customize reuse, or return `null`/`undefined`
|
|
1030
951
|
* to disable reuse for a specific tool call.
|
|
1031
952
|
*
|
|
@@ -1033,9 +954,9 @@ const resolveCreateParams = (overrides, createConfig, createDefaults) => ({
|
|
|
1033
954
|
*
|
|
1034
955
|
* @example
|
|
1035
956
|
* ```ts
|
|
1036
|
-
* import {
|
|
957
|
+
* import { sandbox, createE2BSandboxClient } from "@better-agent/plugins";
|
|
1037
958
|
*
|
|
1038
|
-
* const plugin =
|
|
959
|
+
* const plugin = sandbox({
|
|
1039
960
|
* client: createE2BSandboxClient({
|
|
1040
961
|
* apiKey: process.env.E2B_API_KEY,
|
|
1041
962
|
* }),
|
|
@@ -1048,8 +969,8 @@ const resolveCreateParams = (overrides, createConfig, createDefaults) => ({
|
|
|
1048
969
|
* });
|
|
1049
970
|
* ```
|
|
1050
971
|
*/
|
|
1051
|
-
const
|
|
1052
|
-
|
|
972
|
+
const sandbox = (pluginConfig) => {
|
|
973
|
+
validateSandboxConfig(pluginConfig);
|
|
1053
974
|
const sandboxClient = pluginConfig.client;
|
|
1054
975
|
const store = pluginConfig.store ?? createMemorySandboxSessionStore();
|
|
1055
976
|
const prefix = trimToUndefined(pluginConfig.prefix) ?? "sandbox";
|
|
@@ -1058,8 +979,8 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1058
979
|
if (pluginConfig.sessionKey) {
|
|
1059
980
|
const custom = trimToUndefined(pluginConfig.sessionKey({
|
|
1060
981
|
runId: ctx.runId,
|
|
1061
|
-
agentName: ctx.agentName,
|
|
1062
|
-
...ctx.
|
|
982
|
+
agentName: ctx.agentName ?? "",
|
|
983
|
+
...ctx.threadId !== void 0 ? { threadId: ctx.threadId } : {},
|
|
1063
984
|
toolName
|
|
1064
985
|
}) ?? void 0);
|
|
1065
986
|
return custom !== void 0 ? {
|
|
@@ -1067,15 +988,15 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1067
988
|
sessionKey: custom
|
|
1068
989
|
} : { kind: "disabled" };
|
|
1069
990
|
}
|
|
1070
|
-
return ctx.
|
|
991
|
+
return ctx.threadId ? {
|
|
1071
992
|
kind: "managed",
|
|
1072
|
-
sessionKey: `
|
|
993
|
+
sessionKey: `thread:${ctx.threadId}`
|
|
1073
994
|
} : { kind: "disabled" };
|
|
1074
995
|
};
|
|
1075
996
|
const getExplicitSandboxId = (value) => {
|
|
1076
997
|
if (value === void 0) return;
|
|
1077
998
|
const trimmed = value.trim();
|
|
1078
|
-
if (!trimmed) throw createValidationError("`sandboxId` must be a non-empty string when provided.", "plugins.
|
|
999
|
+
if (!trimmed) throw createValidationError("`sandboxId` must be a non-empty string when provided.", "plugins.sandbox.sandboxId");
|
|
1079
1000
|
return trimmed;
|
|
1080
1001
|
};
|
|
1081
1002
|
const createSandbox = async (params, ctx, toolName) => {
|
|
@@ -1105,10 +1026,9 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1105
1026
|
created: false
|
|
1106
1027
|
};
|
|
1107
1028
|
}
|
|
1108
|
-
if (!params.createIfMissing) throw createValidationError(`No sandbox is available for this ${params.ctx.
|
|
1029
|
+
if (!params.createIfMissing) throw createValidationError(`No sandbox is available for this ${params.ctx.threadId ? "thread" : "run"}. Create one first with '${nameFor("create")}' or pass a sandboxId explicitly.`, "plugins.sandbox.resolveSandbox.missing", {
|
|
1109
1030
|
runId: params.ctx.runId,
|
|
1110
|
-
|
|
1111
|
-
conversationId: params.ctx.conversationId,
|
|
1031
|
+
threadId: params.ctx.threadId,
|
|
1112
1032
|
toolName: params.toolName
|
|
1113
1033
|
});
|
|
1114
1034
|
return await createSandbox(params.createParams, params.ctx, params.toolName);
|
|
@@ -1168,12 +1088,16 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1168
1088
|
type: "string",
|
|
1169
1089
|
minLength: 1
|
|
1170
1090
|
};
|
|
1171
|
-
const
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1091
|
+
const envsSchema = {
|
|
1092
|
+
type: "object",
|
|
1093
|
+
additionalProperties: { type: "string" }
|
|
1094
|
+
};
|
|
1095
|
+
const createTool = defineTool({
|
|
1096
|
+
name: nameFor("create"),
|
|
1097
|
+
description: "Create a sandbox, or reuse the current thread sandbox unless forceNew is true.",
|
|
1098
|
+
target: "server",
|
|
1099
|
+
inputSchema: createSchema,
|
|
1100
|
+
execute: async (input, ctx) => {
|
|
1177
1101
|
if (!input.forceNew) {
|
|
1178
1102
|
const sessionPolicy = getSessionPolicy(ctx, nameFor("create"));
|
|
1179
1103
|
if (sessionPolicy.kind === "managed") {
|
|
@@ -1199,33 +1123,32 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1199
1123
|
created: true,
|
|
1200
1124
|
...resolved.sessionKey !== void 0 ? { sessionKey: resolved.sessionKey } : {}
|
|
1201
1125
|
};
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
timeoutMs: {
|
|
1216
|
-
type: "number",
|
|
1217
|
-
exclusiveMinimum: 0
|
|
1218
|
-
},
|
|
1219
|
-
envs: {
|
|
1220
|
-
type: "object",
|
|
1221
|
-
additionalProperties: { type: "string" }
|
|
1222
|
-
}
|
|
1126
|
+
}
|
|
1127
|
+
});
|
|
1128
|
+
const execTool = defineTool({
|
|
1129
|
+
name: nameFor("exec"),
|
|
1130
|
+
description: "Run one shell command inside a sandbox. Creates a sandbox automatically when none exists for the thread.",
|
|
1131
|
+
target: "server",
|
|
1132
|
+
inputSchema: {
|
|
1133
|
+
type: "object",
|
|
1134
|
+
properties: {
|
|
1135
|
+
sandboxId: sandboxIdSchema,
|
|
1136
|
+
cmd: {
|
|
1137
|
+
type: "string",
|
|
1138
|
+
minLength: 1
|
|
1223
1139
|
},
|
|
1224
|
-
|
|
1225
|
-
|
|
1140
|
+
cwd: { type: "string" },
|
|
1141
|
+
timeoutMs: {
|
|
1142
|
+
type: "number",
|
|
1143
|
+
exclusiveMinimum: 0
|
|
1144
|
+
},
|
|
1145
|
+
envs: envsSchema
|
|
1226
1146
|
},
|
|
1227
|
-
|
|
1228
|
-
|
|
1147
|
+
required: ["cmd"],
|
|
1148
|
+
additionalProperties: false
|
|
1149
|
+
},
|
|
1150
|
+
approval: pluginConfig.approvals?.exec,
|
|
1151
|
+
execute: async (input, ctx) => {
|
|
1229
1152
|
const resolved = await resolveSandbox({
|
|
1230
1153
|
sandboxId: input.sandboxId,
|
|
1231
1154
|
createIfMissing: true,
|
|
@@ -1244,20 +1167,22 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1244
1167
|
createdSandbox: resolved.created,
|
|
1245
1168
|
...result
|
|
1246
1169
|
};
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1170
|
+
}
|
|
1171
|
+
});
|
|
1172
|
+
const readFileTool = defineTool({
|
|
1173
|
+
name: nameFor("read_file"),
|
|
1174
|
+
description: "Read one text file from a sandbox. Creates a sandbox automatically when none exists for the thread.",
|
|
1175
|
+
target: "server",
|
|
1176
|
+
inputSchema: {
|
|
1177
|
+
type: "object",
|
|
1178
|
+
properties: {
|
|
1179
|
+
sandboxId: sandboxIdSchema,
|
|
1180
|
+
path: pathSchema
|
|
1181
|
+
},
|
|
1182
|
+
required: ["path"],
|
|
1183
|
+
additionalProperties: false
|
|
1184
|
+
},
|
|
1185
|
+
execute: async (input, ctx) => {
|
|
1261
1186
|
const resolved = await resolveSandbox({
|
|
1262
1187
|
sandboxId: input.sandboxId,
|
|
1263
1188
|
createIfMissing: true,
|
|
@@ -1272,22 +1197,24 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1272
1197
|
path: input.path
|
|
1273
1198
|
})
|
|
1274
1199
|
};
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
additionalProperties: false
|
|
1200
|
+
}
|
|
1201
|
+
});
|
|
1202
|
+
const writeFileTool = defineTool({
|
|
1203
|
+
name: nameFor("write_file"),
|
|
1204
|
+
description: "Write one text file into a sandbox. Creates a sandbox automatically when none exists for the thread.",
|
|
1205
|
+
target: "server",
|
|
1206
|
+
inputSchema: {
|
|
1207
|
+
type: "object",
|
|
1208
|
+
properties: {
|
|
1209
|
+
sandboxId: sandboxIdSchema,
|
|
1210
|
+
path: pathSchema,
|
|
1211
|
+
content: { type: "string" }
|
|
1288
1212
|
},
|
|
1289
|
-
|
|
1290
|
-
|
|
1213
|
+
required: ["path", "content"],
|
|
1214
|
+
additionalProperties: false
|
|
1215
|
+
},
|
|
1216
|
+
approval: pluginConfig.approvals?.writeFile,
|
|
1217
|
+
execute: async (input, ctx) => {
|
|
1291
1218
|
const resolved = await resolveSandbox({
|
|
1292
1219
|
sandboxId: input.sandboxId,
|
|
1293
1220
|
createIfMissing: true,
|
|
@@ -1304,20 +1231,22 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1304
1231
|
createdSandbox: resolved.created,
|
|
1305
1232
|
path: result.path
|
|
1306
1233
|
};
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
const listFilesTool = defineTool({
|
|
1237
|
+
name: nameFor("list_files"),
|
|
1238
|
+
description: "List directory entries inside a sandbox. Creates a sandbox automatically when none exists for the thread.",
|
|
1239
|
+
target: "server",
|
|
1240
|
+
inputSchema: {
|
|
1241
|
+
type: "object",
|
|
1242
|
+
properties: {
|
|
1243
|
+
sandboxId: sandboxIdSchema,
|
|
1244
|
+
path: pathSchema
|
|
1245
|
+
},
|
|
1246
|
+
required: ["path"],
|
|
1247
|
+
additionalProperties: false
|
|
1248
|
+
},
|
|
1249
|
+
execute: async (input, ctx) => {
|
|
1321
1250
|
const resolved = await resolveSandbox({
|
|
1322
1251
|
sandboxId: input.sandboxId,
|
|
1323
1252
|
createIfMissing: true,
|
|
@@ -1332,20 +1261,22 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1332
1261
|
path: input.path
|
|
1333
1262
|
})
|
|
1334
1263
|
};
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1264
|
+
}
|
|
1265
|
+
});
|
|
1266
|
+
const makeDirTool = defineTool({
|
|
1267
|
+
name: nameFor("make_dir"),
|
|
1268
|
+
description: "Create a directory inside a sandbox. Creates a sandbox automatically when none exists for the thread.",
|
|
1269
|
+
target: "server",
|
|
1270
|
+
inputSchema: {
|
|
1271
|
+
type: "object",
|
|
1272
|
+
properties: {
|
|
1273
|
+
sandboxId: sandboxIdSchema,
|
|
1274
|
+
path: pathSchema
|
|
1275
|
+
},
|
|
1276
|
+
required: ["path"],
|
|
1277
|
+
additionalProperties: false
|
|
1278
|
+
},
|
|
1279
|
+
execute: async (input, ctx) => {
|
|
1349
1280
|
const resolved = await resolveSandbox({
|
|
1350
1281
|
sandboxId: input.sandboxId,
|
|
1351
1282
|
createIfMissing: true,
|
|
@@ -1361,21 +1292,23 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1361
1292
|
path: input.path,
|
|
1362
1293
|
created: result.created
|
|
1363
1294
|
};
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
additionalProperties: false
|
|
1295
|
+
}
|
|
1296
|
+
});
|
|
1297
|
+
const removePathTool = defineTool({
|
|
1298
|
+
name: nameFor("remove_path"),
|
|
1299
|
+
description: "Remove a file or directory from a sandbox.",
|
|
1300
|
+
target: "server",
|
|
1301
|
+
inputSchema: {
|
|
1302
|
+
type: "object",
|
|
1303
|
+
properties: {
|
|
1304
|
+
sandboxId: sandboxIdSchema,
|
|
1305
|
+
path: pathSchema
|
|
1376
1306
|
},
|
|
1377
|
-
|
|
1378
|
-
|
|
1307
|
+
required: ["path"],
|
|
1308
|
+
additionalProperties: false
|
|
1309
|
+
},
|
|
1310
|
+
approval: pluginConfig.approvals?.removePath,
|
|
1311
|
+
execute: async (input, ctx) => {
|
|
1379
1312
|
const resolved = await resolveSandbox({
|
|
1380
1313
|
sandboxId: input.sandboxId,
|
|
1381
1314
|
createIfMissing: false,
|
|
@@ -1391,24 +1324,26 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1391
1324
|
removed: true,
|
|
1392
1325
|
path: input.path
|
|
1393
1326
|
};
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1327
|
+
}
|
|
1328
|
+
});
|
|
1329
|
+
const getHostTool = defineTool({
|
|
1330
|
+
name: nameFor("get_host"),
|
|
1331
|
+
description: "Expose one sandbox port as a host URL so callers can reach an app running inside the sandbox.",
|
|
1332
|
+
target: "server",
|
|
1333
|
+
inputSchema: {
|
|
1334
|
+
type: "object",
|
|
1335
|
+
properties: {
|
|
1336
|
+
sandboxId: sandboxIdSchema,
|
|
1337
|
+
port: {
|
|
1338
|
+
type: "number",
|
|
1339
|
+
minimum: 1,
|
|
1340
|
+
maximum: 65535
|
|
1341
|
+
}
|
|
1342
|
+
},
|
|
1343
|
+
required: ["port"],
|
|
1344
|
+
additionalProperties: false
|
|
1345
|
+
},
|
|
1346
|
+
execute: async (input, ctx) => {
|
|
1412
1347
|
const resolved = await resolveSandbox({
|
|
1413
1348
|
sandboxId: input.sandboxId,
|
|
1414
1349
|
createIfMissing: false,
|
|
@@ -1427,17 +1362,19 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1427
1362
|
host,
|
|
1428
1363
|
...preview?.token !== void 0 ? { token: preview.token } : {}
|
|
1429
1364
|
};
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
},
|
|
1439
|
-
|
|
1440
|
-
}
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
const killTool = defineTool({
|
|
1368
|
+
name: nameFor("kill"),
|
|
1369
|
+
description: "Terminate a sandbox and clear the current thread sandbox when applicable.",
|
|
1370
|
+
target: "server",
|
|
1371
|
+
inputSchema: {
|
|
1372
|
+
type: "object",
|
|
1373
|
+
properties: { sandboxId: sandboxIdSchema },
|
|
1374
|
+
additionalProperties: false
|
|
1375
|
+
},
|
|
1376
|
+
approval: pluginConfig.approvals?.killSandbox,
|
|
1377
|
+
execute: async (input, ctx) => {
|
|
1441
1378
|
const resolved = await resolveSandbox({
|
|
1442
1379
|
sandboxId: input.sandboxId,
|
|
1443
1380
|
createIfMissing: false,
|
|
@@ -1451,7 +1388,19 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1451
1388
|
killed: true,
|
|
1452
1389
|
...clearedSessionKey !== void 0 ? { clearedSessionKey } : {}
|
|
1453
1390
|
};
|
|
1454
|
-
}
|
|
1391
|
+
}
|
|
1392
|
+
});
|
|
1393
|
+
const toAnyTool = (tool) => tool;
|
|
1394
|
+
const tools = [
|
|
1395
|
+
toAnyTool(createTool),
|
|
1396
|
+
toAnyTool(execTool),
|
|
1397
|
+
toAnyTool(readFileTool),
|
|
1398
|
+
toAnyTool(writeFileTool),
|
|
1399
|
+
toAnyTool(listFilesTool),
|
|
1400
|
+
toAnyTool(makeDirTool),
|
|
1401
|
+
toAnyTool(removePathTool),
|
|
1402
|
+
toAnyTool(getHostTool),
|
|
1403
|
+
toAnyTool(killTool)
|
|
1455
1404
|
];
|
|
1456
1405
|
return {
|
|
1457
1406
|
id: pluginConfig.id ?? "sandbox",
|
|
@@ -1460,5 +1409,5 @@ const sandboxPlugin = (pluginConfig) => {
|
|
|
1460
1409
|
};
|
|
1461
1410
|
|
|
1462
1411
|
//#endregion
|
|
1463
|
-
export {
|
|
1412
|
+
export { createDaytonaSandboxClient, createE2BSandboxClient, createMemorySandboxSessionStore, ipAllowlist, logging, rateLimit, sandbox };
|
|
1464
1413
|
//# sourceMappingURL=index.mjs.map
|