@better-agent/plugins 0.1.0-beta.1 → 0.1.0-beta.3

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.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { BetterAgentError } from "@better-agent/shared/errors";
2
+ import { defineTool } from "@better-agent/core";
2
3
 
3
4
  //#region src/shared/json.ts
4
5
  /** Creates a JSON response with a default content type. */
@@ -24,26 +25,26 @@ function createUnauthorizedResponse() {
24
25
  //#endregion
25
26
  //#region src/shared/validation.ts
26
27
  /** Creates a plugin validation error. */
27
- function createValidationError(message, at) {
28
+ function createValidationError$1(message, at) {
28
29
  return BetterAgentError.fromCode("VALIDATION_FAILED", message, { trace: [{ at }] });
29
30
  }
30
31
  /** Requires a positive finite number. */
31
32
  function requirePositiveNumber(value, name, at) {
32
- if (!Number.isFinite(value) || value <= 0) throw createValidationError(`\`${name}\` must be a positive number.`, at);
33
+ if (!Number.isFinite(value) || value <= 0) throw createValidationError$1(`\`${name}\` must be a positive number.`, at);
33
34
  }
34
35
  /** Requires a non-empty array. */
35
36
  function requireNonEmptyArray(value, name, at) {
36
- if (!value || value.length === 0) throw createValidationError(`\`${name}\` must contain at least one value.`, at);
37
+ if (!value || value.length === 0) throw createValidationError$1(`\`${name}\` must contain at least one value.`, at);
37
38
  }
38
39
 
39
40
  //#endregion
40
41
  //#region src/auth/validate.ts
41
42
  /** Validates `authPlugin` configuration. */
42
43
  function validateAuthPluginConfig(config) {
43
- if (!config.validate && (!config.apiKeys || config.apiKeys.length === 0)) throw createValidationError("`authPlugin` requires either `apiKeys` or `validate`.", "plugins.authPlugin");
44
- if (config.header !== void 0 && config.header.trim().length === 0) throw createValidationError("`authPlugin` requires `header` to be a non-empty string when provided.", "plugins.authPlugin");
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");
45
46
  if (config.apiKeys) {
46
- if (config.apiKeys.filter((key) => typeof key === "string").map((key) => key.trim()).filter((key) => key.length > 0).length === 0 && !config.validate) throw createValidationError("`authPlugin` requires `apiKeys` to contain at least one non-empty key.", "plugins.authPlugin");
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");
47
48
  }
48
49
  }
49
50
 
@@ -224,7 +225,7 @@ function createIpDeniedResponse() {
224
225
  /** Validates `ipAllowlistPlugin` configuration. */
225
226
  function validateIpAllowlistPluginConfig(config) {
226
227
  requireNonEmptyArray(config.allow, "allow", "plugins.ipAllowlistPlugin");
227
- for (const entry of config.allow) if (typeof entry !== "string" || !parseAllowEntry(entry)) throw createValidationError(`\`ipAllowlistPlugin\` received an invalid allow entry: '${String(entry)}'.`, "plugins.ipAllowlistPlugin");
228
+ for (const entry of config.allow) if (typeof entry !== "string" || !parseAllowEntry(entry)) throw createValidationError$1(`\`ipAllowlistPlugin\` received an invalid allow entry: '${String(entry)}'.`, "plugins.ipAllowlistPlugin");
228
229
  }
229
230
 
230
231
  //#endregion
@@ -702,5 +703,701 @@ const rateLimitPlugin = (config) => {
702
703
  };
703
704
 
704
705
  //#endregion
705
- export { authPlugin, ipAllowlistPlugin, loggingPlugin, rateLimitPlugin };
706
+ //#region src/sandbox/daytona.ts
707
+ const loadDaytona = async () => {
708
+ try {
709
+ return await import("@daytonaio/sdk");
710
+ } catch (error) {
711
+ throw BetterAgentError.fromCode("INTERNAL", "The Daytona sandbox client requires the `@daytonaio/sdk` package to be installed in the host app.", {
712
+ cause: error,
713
+ trace: [{ at: "plugins.sandbox.createDaytonaSandboxClient.loadDaytona" }]
714
+ });
715
+ }
716
+ };
717
+ const removeUndefined$1 = (value) => Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
718
+ const toSeconds = (value) => value === void 0 ? void 0 : Math.max(1, Math.ceil(value / 1e3));
719
+ const toUtf8 = (value) => typeof value === "string" ? value : new TextDecoder().decode(value);
720
+ const normalizeFileEntries = (files) => files.map((entry) => ({
721
+ name: entry.name ?? entry.path?.split("/").filter(Boolean).at(-1) ?? "",
722
+ path: entry.path ?? entry.name ?? "",
723
+ type: entry.type ?? (entry.isDir ? "directory" : "file")
724
+ }));
725
+ /** Creates a sandbox client backed by the Daytona SDK. */
726
+ function createDaytonaSandboxClient(config = {}) {
727
+ const clientConfig = removeUndefined$1({
728
+ apiKey: config.apiKey,
729
+ apiUrl: config.apiUrl,
730
+ target: config.target
731
+ });
732
+ const buildCreateParams = (overrides) => {
733
+ const template = overrides?.template ?? config.template;
734
+ const templateKind = config.templateKind ?? "snapshot";
735
+ return removeUndefined$1({
736
+ language: config.language,
737
+ envVars: overrides?.envs ?? config.envs,
738
+ labels: overrides?.metadata ?? config.metadata,
739
+ public: config.public,
740
+ autoStopInterval: config.autoStopInterval,
741
+ autoArchiveInterval: config.autoArchiveInterval,
742
+ autoDeleteInterval: config.autoDeleteInterval,
743
+ ...template !== void 0 ? templateKind === "image" ? { image: template } : { snapshot: template } : {},
744
+ ...config.snapshot !== void 0 ? { snapshot: config.snapshot } : {},
745
+ ...config.image !== void 0 ? { image: config.image } : {}
746
+ });
747
+ };
748
+ const getClient = async () => {
749
+ const { Daytona } = await loadDaytona();
750
+ return new Daytona(clientConfig);
751
+ };
752
+ const getSandbox = async (sandboxId) => {
753
+ const client = await getClient();
754
+ return {
755
+ sandbox: await client.get(sandboxId),
756
+ client
757
+ };
758
+ };
759
+ const toCommandResult = (response) => ({
760
+ exitCode: response.exitCode,
761
+ stdout: response.result
762
+ });
763
+ return {
764
+ provider: "daytona",
765
+ capabilities: {
766
+ commands: true,
767
+ filesystem: true,
768
+ preview: true,
769
+ pty: true,
770
+ desktop: true,
771
+ git: true,
772
+ snapshots: true,
773
+ volumes: true,
774
+ lifecycle: {
775
+ start: true,
776
+ stop: true,
777
+ archive: true,
778
+ resume: true
779
+ }
780
+ },
781
+ async createSandbox(params) {
782
+ const client = await getClient();
783
+ const timeout = toSeconds(params?.timeoutMs ?? config.timeoutMs);
784
+ return { sandboxId: (await client.create(buildCreateParams(params), { ...timeout !== void 0 ? { timeout } : {} })).id };
785
+ },
786
+ async runCommand(params) {
787
+ const { sandbox } = await getSandbox(params.sandboxId);
788
+ return toCommandResult(await sandbox.process.executeCommand(params.cmd, params.cwd, params.envs, toSeconds(params.timeoutMs)));
789
+ },
790
+ async readFile(params) {
791
+ const { sandbox } = await getSandbox(params.sandboxId);
792
+ return toUtf8(await sandbox.fs.downloadFile(params.path));
793
+ },
794
+ async writeFile(params) {
795
+ const { sandbox } = await getSandbox(params.sandboxId);
796
+ await sandbox.fs.uploadFile(Buffer.from(params.content), params.path);
797
+ return { path: params.path };
798
+ },
799
+ async listFiles(params) {
800
+ const { sandbox } = await getSandbox(params.sandboxId);
801
+ return normalizeFileEntries(await sandbox.fs.listFiles(params.path));
802
+ },
803
+ async makeDir(params) {
804
+ const { sandbox } = await getSandbox(params.sandboxId);
805
+ await sandbox.fs.createFolder(params.path, "755");
806
+ return { created: true };
807
+ },
808
+ async removePath(params) {
809
+ const { sandbox } = await getSandbox(params.sandboxId);
810
+ await sandbox.fs.deleteFile(params.path, true);
811
+ },
812
+ async getHost(params) {
813
+ const { sandbox } = await getSandbox(params.sandboxId);
814
+ return await sandbox.getPreviewLink(params.port);
815
+ },
816
+ async killSandbox(params) {
817
+ const { client, sandbox } = await getSandbox(params.sandboxId);
818
+ if (typeof sandbox.delete === "function") {
819
+ await sandbox.delete(60);
820
+ return;
821
+ }
822
+ if (typeof client.delete === "function") {
823
+ await client.delete(sandbox, 60);
824
+ return;
825
+ }
826
+ throw BetterAgentError.fromCode("NOT_IMPLEMENTED", "The active Daytona SDK instance does not expose sandbox deletion.", {
827
+ context: { sandboxId: params.sandboxId },
828
+ trace: [{ at: "plugins.sandbox.createDaytonaSandboxClient.killSandbox" }]
829
+ });
830
+ },
831
+ lifecycle: {
832
+ async startSandbox(params) {
833
+ const { client, sandbox } = await getSandbox(params.sandboxId);
834
+ const timeout = toSeconds(params.timeoutMs);
835
+ if (typeof sandbox.start === "function") {
836
+ await sandbox.start(timeout);
837
+ return;
838
+ }
839
+ if (typeof client.start === "function") await client.start(sandbox, timeout);
840
+ },
841
+ async stopSandbox(params) {
842
+ const { client, sandbox } = await getSandbox(params.sandboxId);
843
+ if (typeof sandbox.stop === "function") {
844
+ await sandbox.stop(toSeconds(params.timeoutMs));
845
+ return;
846
+ }
847
+ if (typeof client.stop === "function") await client.stop(sandbox);
848
+ },
849
+ async archiveSandbox(params) {
850
+ const { sandbox } = await getSandbox(params.sandboxId);
851
+ if (typeof sandbox.archive === "function") {
852
+ await sandbox.archive();
853
+ return;
854
+ }
855
+ throw BetterAgentError.fromCode("NOT_IMPLEMENTED", "The active Daytona SDK instance does not expose sandbox archiving.", {
856
+ context: { sandboxId: params.sandboxId },
857
+ trace: [{ at: "plugins.sandbox.createDaytonaSandboxClient.archiveSandbox" }]
858
+ });
859
+ }
860
+ }
861
+ };
862
+ }
863
+
864
+ //#endregion
865
+ //#region src/sandbox/e2b.ts
866
+ const removeUndefined = (value) => Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
867
+ const loadE2B = async () => {
868
+ try {
869
+ return await import("e2b");
870
+ } catch (error) {
871
+ throw BetterAgentError.fromCode("INTERNAL", "The built-in E2B sandbox client requires the `e2b` package to be installed in the host app.", {
872
+ cause: error,
873
+ trace: [{ at: "plugins.sandbox.createE2BSandboxClient.loadE2B" }]
874
+ });
875
+ }
876
+ };
877
+ /** Creates a sandbox client backed by the E2B SDK. */
878
+ function createE2BSandboxClient(config = {}) {
879
+ const connectionOptions = removeUndefined({
880
+ apiKey: config.apiKey,
881
+ accessToken: config.accessToken,
882
+ domain: config.domain,
883
+ requestTimeoutMs: config.requestTimeoutMs
884
+ });
885
+ const toCreateOptions = (overrides) => removeUndefined({
886
+ ...connectionOptions,
887
+ timeoutMs: overrides?.timeoutMs ?? config.timeoutMs,
888
+ envs: overrides?.envs ?? config.envs,
889
+ metadata: overrides?.metadata ?? config.metadata
890
+ });
891
+ const connectSandbox = async (sandboxId) => {
892
+ const { Sandbox } = await loadE2B();
893
+ return await Sandbox.connect(sandboxId, connectionOptions);
894
+ };
895
+ const mapCommandResult = (result) => ({
896
+ exitCode: result.exitCode,
897
+ stdout: result.stdout,
898
+ stderr: result.stderr,
899
+ pid: result.pid
900
+ });
901
+ return {
902
+ provider: "e2b",
903
+ capabilities: {
904
+ commands: true,
905
+ filesystem: true,
906
+ preview: true,
907
+ mcp: true,
908
+ pty: true,
909
+ desktop: true
910
+ },
911
+ async createSandbox(params) {
912
+ const { Sandbox } = await loadE2B();
913
+ const template = params?.template ?? config.template;
914
+ const options = toCreateOptions(params);
915
+ return { sandboxId: (template !== void 0 ? await Sandbox.create(template, options) : await Sandbox.create(options)).sandboxId };
916
+ },
917
+ async runCommand(params) {
918
+ return mapCommandResult(await (await connectSandbox(params.sandboxId)).commands.run(params.cmd, removeUndefined({
919
+ cwd: params.cwd,
920
+ envs: params.envs,
921
+ timeoutMs: params.timeoutMs
922
+ })));
923
+ },
924
+ async readFile(params) {
925
+ return await (await connectSandbox(params.sandboxId)).files.read(params.path);
926
+ },
927
+ async writeFile(params) {
928
+ return { path: (await (await connectSandbox(params.sandboxId)).files.write(params.path, params.content)).path ?? params.path };
929
+ },
930
+ async listFiles(params) {
931
+ return await (await connectSandbox(params.sandboxId)).files.list(params.path);
932
+ },
933
+ async makeDir(params) {
934
+ return { created: await (await connectSandbox(params.sandboxId)).files.makeDir(params.path) };
935
+ },
936
+ async removePath(params) {
937
+ await (await connectSandbox(params.sandboxId)).files.remove(params.path);
938
+ },
939
+ async getHost(params) {
940
+ return (await connectSandbox(params.sandboxId)).getHost(params.port);
941
+ },
942
+ async killSandbox(params) {
943
+ await (await connectSandbox(params.sandboxId)).kill();
944
+ }
945
+ };
946
+ }
947
+
948
+ //#endregion
949
+ //#region src/sandbox/memory-store.ts
950
+ /** Creates an in-memory sandbox session store. */
951
+ function createMemorySandboxSessionStore() {
952
+ const sessions = /* @__PURE__ */ new Map();
953
+ return {
954
+ async get(key) {
955
+ return sessions.get(key) ?? null;
956
+ },
957
+ async set(key, sandboxId) {
958
+ sessions.set(key, sandboxId);
959
+ },
960
+ async delete(key) {
961
+ sessions.delete(key);
962
+ }
963
+ };
964
+ }
965
+
966
+ //#endregion
967
+ //#region src/sandbox/validate.ts
968
+ /** Validates `sandboxPlugin` configuration. */
969
+ function validateSandboxPluginConfig(config) {
970
+ const client = config.client;
971
+ if (!client || typeof client !== "object") throw createValidationError$1("`sandboxPlugin` requires a `client`.", "plugins.sandboxPlugin");
972
+ if (config.prefix !== void 0 && config.prefix.trim().length === 0) throw createValidationError$1("`sandboxPlugin` requires `prefix` to be a non-empty string when provided.", "plugins.sandboxPlugin");
973
+ }
974
+
975
+ //#endregion
976
+ //#region src/sandbox/plugin.ts
977
+ const trimToUndefined = (value) => {
978
+ const trimmed = value?.trim();
979
+ return trimmed ? trimmed : void 0;
980
+ };
981
+ const normalizeHostUrl = (value) => /^[a-z][a-z0-9+.-]*:\/\//i.test(value) ? value : `https://${value}`;
982
+ const createValidationError = (message, at, context) => BetterAgentError.fromCode("VALIDATION_FAILED", message, {
983
+ ...context !== void 0 ? { context } : {},
984
+ trace: [{ at }]
985
+ });
986
+ /**
987
+ * Adds sandbox tools.
988
+ *
989
+ * By default, the plugin reuses one sandbox per `conversationId`.
990
+ * Use `sessionKey` to customize reuse, or return `null`/`undefined`
991
+ * to disable reuse for a specific tool call.
992
+ *
993
+ * Uses an in-memory store by default.
994
+ *
995
+ * @example
996
+ * ```ts
997
+ * import { sandboxPlugin, createE2BSandboxClient } from "@better-agent/plugins";
998
+ *
999
+ * const plugin = sandboxPlugin({
1000
+ * client: createE2BSandboxClient({
1001
+ * apiKey: process.env.E2B_API_KEY,
1002
+ * }),
1003
+ * defaults: {
1004
+ * template: "base",
1005
+ * timeoutMs: 10 * 60_000,
1006
+ * },
1007
+ * });
1008
+ * ```
1009
+ */
1010
+ const sandboxPlugin = (config) => {
1011
+ validateSandboxPluginConfig(config);
1012
+ const sandboxClient = config.client;
1013
+ const store = config.store ?? createMemorySandboxSessionStore();
1014
+ const prefix = trimToUndefined(config.prefix) ?? "sandbox";
1015
+ const nameFor = (suffix) => `${prefix}_${suffix}`;
1016
+ const getSessionPolicy = (ctx, toolName) => {
1017
+ if (config.sessionKey) {
1018
+ const custom = trimToUndefined(config.sessionKey({
1019
+ runId: ctx.runId,
1020
+ agentName: ctx.agentName,
1021
+ ...ctx.conversationId !== void 0 ? { conversationId: ctx.conversationId } : {},
1022
+ toolName
1023
+ }) ?? void 0);
1024
+ return custom !== void 0 ? {
1025
+ kind: "managed",
1026
+ sessionKey: custom
1027
+ } : { kind: "disabled" };
1028
+ }
1029
+ return ctx.conversationId ? {
1030
+ kind: "managed",
1031
+ sessionKey: `conversation:${ctx.conversationId}`
1032
+ } : { kind: "disabled" };
1033
+ };
1034
+ const getExplicitSandboxId = (value) => {
1035
+ if (value === void 0) return;
1036
+ const trimmed = value.trim();
1037
+ if (!trimmed) throw createValidationError("`sandboxId` must be a non-empty string when provided.", "plugins.sandboxPlugin.sandboxId");
1038
+ return trimmed;
1039
+ };
1040
+ const createSandbox = async (params, ctx, toolName) => {
1041
+ const sessionPolicy = getSessionPolicy(ctx, toolName);
1042
+ const created = await sandboxClient.createSandbox({
1043
+ template: params?.template ?? config.defaults?.template,
1044
+ timeoutMs: params?.timeoutMs ?? config.defaults?.timeoutMs,
1045
+ envs: params?.envs ?? config.defaults?.envs,
1046
+ metadata: params?.metadata ?? config.defaults?.metadata
1047
+ });
1048
+ if (sessionPolicy.kind === "managed") await store.set(sessionPolicy.sessionKey, created.sandboxId);
1049
+ return {
1050
+ sandboxId: created.sandboxId,
1051
+ ...sessionPolicy.kind === "managed" ? { sessionKey: sessionPolicy.sessionKey } : {},
1052
+ created: true
1053
+ };
1054
+ };
1055
+ const resolveSandbox = async (params) => {
1056
+ const explicitSandboxId = getExplicitSandboxId(params.sandboxId);
1057
+ if (explicitSandboxId) return {
1058
+ sandboxId: explicitSandboxId,
1059
+ created: false
1060
+ };
1061
+ const sessionPolicy = getSessionPolicy(params.ctx, params.toolName);
1062
+ if (sessionPolicy.kind === "managed") {
1063
+ const existing = trimToUndefined(await store.get(sessionPolicy.sessionKey) ?? void 0);
1064
+ if (existing) return {
1065
+ sandboxId: existing,
1066
+ sessionKey: sessionPolicy.sessionKey,
1067
+ created: false
1068
+ };
1069
+ }
1070
+ if (!params.createIfMissing) throw createValidationError(`No sandbox is available for this ${params.ctx.conversationId ? "conversation" : "run"}. Create one first with '${nameFor("create")}' or pass a sandboxId explicitly.`, "plugins.sandboxPlugin.resolveSandbox.missing", {
1071
+ runId: params.ctx.runId,
1072
+ agentName: params.ctx.agentName,
1073
+ conversationId: params.ctx.conversationId,
1074
+ toolName: params.toolName
1075
+ });
1076
+ return await createSandbox(params.createParams, params.ctx, params.toolName);
1077
+ };
1078
+ const clearManagedSessionIfMatches = async (ctx, toolName, sandboxId) => {
1079
+ const sessionPolicy = getSessionPolicy(ctx, toolName);
1080
+ if (sessionPolicy.kind !== "managed") return;
1081
+ if (await store.get(sessionPolicy.sessionKey) === sandboxId) {
1082
+ await store.delete(sessionPolicy.sessionKey);
1083
+ return sessionPolicy.sessionKey;
1084
+ }
1085
+ };
1086
+ const createSchema = {
1087
+ type: "object",
1088
+ properties: {
1089
+ forceNew: { type: "boolean" },
1090
+ template: { type: "string" },
1091
+ timeoutMs: {
1092
+ type: "number",
1093
+ exclusiveMinimum: 0
1094
+ },
1095
+ envs: {
1096
+ type: "object",
1097
+ additionalProperties: { type: "string" }
1098
+ },
1099
+ metadata: {
1100
+ type: "object",
1101
+ additionalProperties: { type: "string" }
1102
+ }
1103
+ },
1104
+ additionalProperties: false
1105
+ };
1106
+ const sandboxIdSchema = { type: "string" };
1107
+ const pathSchema = {
1108
+ type: "string",
1109
+ minLength: 1
1110
+ };
1111
+ const tools = [
1112
+ defineTool({
1113
+ name: nameFor("create"),
1114
+ description: "Create a sandbox, or reuse the current conversation sandbox unless forceNew is true.",
1115
+ schema: createSchema
1116
+ }).server(async (input, ctx) => {
1117
+ if (!input.forceNew) {
1118
+ const sessionPolicy = getSessionPolicy(ctx, nameFor("create"));
1119
+ if (sessionPolicy.kind === "managed") {
1120
+ const existing = trimToUndefined(await store.get(sessionPolicy.sessionKey) ?? void 0);
1121
+ if (existing) return {
1122
+ sandboxId: existing,
1123
+ reused: true,
1124
+ created: false,
1125
+ sessionKey: sessionPolicy.sessionKey
1126
+ };
1127
+ }
1128
+ }
1129
+ const resolved = await createSandbox({
1130
+ template: input.template,
1131
+ timeoutMs: input.timeoutMs,
1132
+ envs: input.envs,
1133
+ metadata: input.metadata
1134
+ }, ctx, nameFor("create"));
1135
+ return {
1136
+ sandboxId: resolved.sandboxId,
1137
+ reused: false,
1138
+ created: true,
1139
+ ...resolved.sessionKey !== void 0 ? { sessionKey: resolved.sessionKey } : {}
1140
+ };
1141
+ }),
1142
+ defineTool({
1143
+ name: nameFor("exec"),
1144
+ description: "Run one shell command inside a sandbox. Creates a sandbox automatically when none exists for the conversation.",
1145
+ schema: {
1146
+ type: "object",
1147
+ properties: {
1148
+ sandboxId: sandboxIdSchema,
1149
+ cmd: {
1150
+ type: "string",
1151
+ minLength: 1
1152
+ },
1153
+ cwd: { type: "string" },
1154
+ timeoutMs: {
1155
+ type: "number",
1156
+ exclusiveMinimum: 0
1157
+ },
1158
+ envs: {
1159
+ type: "object",
1160
+ additionalProperties: { type: "string" }
1161
+ }
1162
+ },
1163
+ required: ["cmd"],
1164
+ additionalProperties: false
1165
+ },
1166
+ approval: config.approvals?.exec
1167
+ }).server(async (input, ctx) => {
1168
+ const resolved = await resolveSandbox({
1169
+ sandboxId: input.sandboxId,
1170
+ createIfMissing: true,
1171
+ ctx,
1172
+ toolName: nameFor("exec")
1173
+ });
1174
+ const result = await sandboxClient.runCommand({
1175
+ sandboxId: resolved.sandboxId,
1176
+ cmd: input.cmd,
1177
+ cwd: input.cwd,
1178
+ timeoutMs: input.timeoutMs,
1179
+ envs: input.envs
1180
+ });
1181
+ return {
1182
+ sandboxId: resolved.sandboxId,
1183
+ createdSandbox: resolved.created,
1184
+ ...result
1185
+ };
1186
+ }),
1187
+ defineTool({
1188
+ name: nameFor("read_file"),
1189
+ description: "Read one text file from a sandbox. Creates a sandbox automatically when none exists for the conversation.",
1190
+ schema: {
1191
+ type: "object",
1192
+ properties: {
1193
+ sandboxId: sandboxIdSchema,
1194
+ path: pathSchema
1195
+ },
1196
+ required: ["path"],
1197
+ additionalProperties: false
1198
+ }
1199
+ }).server(async (input, ctx) => {
1200
+ const resolved = await resolveSandbox({
1201
+ sandboxId: input.sandboxId,
1202
+ createIfMissing: true,
1203
+ ctx,
1204
+ toolName: nameFor("read_file")
1205
+ });
1206
+ return {
1207
+ sandboxId: resolved.sandboxId,
1208
+ path: input.path,
1209
+ content: await sandboxClient.readFile({
1210
+ sandboxId: resolved.sandboxId,
1211
+ path: input.path
1212
+ })
1213
+ };
1214
+ }),
1215
+ defineTool({
1216
+ name: nameFor("write_file"),
1217
+ description: "Write one text file into a sandbox. Creates a sandbox automatically when none exists for the conversation.",
1218
+ schema: {
1219
+ type: "object",
1220
+ properties: {
1221
+ sandboxId: sandboxIdSchema,
1222
+ path: pathSchema,
1223
+ content: { type: "string" }
1224
+ },
1225
+ required: ["path", "content"],
1226
+ additionalProperties: false
1227
+ },
1228
+ approval: config.approvals?.writeFile
1229
+ }).server(async (input, ctx) => {
1230
+ const resolved = await resolveSandbox({
1231
+ sandboxId: input.sandboxId,
1232
+ createIfMissing: true,
1233
+ ctx,
1234
+ toolName: nameFor("write_file")
1235
+ });
1236
+ const result = await sandboxClient.writeFile({
1237
+ sandboxId: resolved.sandboxId,
1238
+ path: input.path,
1239
+ content: input.content
1240
+ });
1241
+ return {
1242
+ sandboxId: resolved.sandboxId,
1243
+ createdSandbox: resolved.created,
1244
+ path: result.path
1245
+ };
1246
+ }),
1247
+ defineTool({
1248
+ name: nameFor("list_files"),
1249
+ description: "List directory entries inside a sandbox. Creates a sandbox automatically when none exists for the conversation.",
1250
+ schema: {
1251
+ type: "object",
1252
+ properties: {
1253
+ sandboxId: sandboxIdSchema,
1254
+ path: pathSchema
1255
+ },
1256
+ required: ["path"],
1257
+ additionalProperties: false
1258
+ }
1259
+ }).server(async (input, ctx) => {
1260
+ const resolved = await resolveSandbox({
1261
+ sandboxId: input.sandboxId,
1262
+ createIfMissing: true,
1263
+ ctx,
1264
+ toolName: nameFor("list_files")
1265
+ });
1266
+ return {
1267
+ sandboxId: resolved.sandboxId,
1268
+ path: input.path,
1269
+ entries: await sandboxClient.listFiles({
1270
+ sandboxId: resolved.sandboxId,
1271
+ path: input.path
1272
+ })
1273
+ };
1274
+ }),
1275
+ defineTool({
1276
+ name: nameFor("make_dir"),
1277
+ description: "Create a directory inside a sandbox. Creates a sandbox automatically when none exists for the conversation.",
1278
+ schema: {
1279
+ type: "object",
1280
+ properties: {
1281
+ sandboxId: sandboxIdSchema,
1282
+ path: pathSchema
1283
+ },
1284
+ required: ["path"],
1285
+ additionalProperties: false
1286
+ }
1287
+ }).server(async (input, ctx) => {
1288
+ const resolved = await resolveSandbox({
1289
+ sandboxId: input.sandboxId,
1290
+ createIfMissing: true,
1291
+ ctx,
1292
+ toolName: nameFor("make_dir")
1293
+ });
1294
+ const result = await sandboxClient.makeDir({
1295
+ sandboxId: resolved.sandboxId,
1296
+ path: input.path
1297
+ });
1298
+ return {
1299
+ sandboxId: resolved.sandboxId,
1300
+ path: input.path,
1301
+ created: result.created
1302
+ };
1303
+ }),
1304
+ defineTool({
1305
+ name: nameFor("remove_path"),
1306
+ description: "Remove a file or directory from a sandbox.",
1307
+ schema: {
1308
+ type: "object",
1309
+ properties: {
1310
+ sandboxId: sandboxIdSchema,
1311
+ path: pathSchema
1312
+ },
1313
+ required: ["path"],
1314
+ additionalProperties: false
1315
+ },
1316
+ approval: config.approvals?.removePath
1317
+ }).server(async (input, ctx) => {
1318
+ const resolved = await resolveSandbox({
1319
+ sandboxId: input.sandboxId,
1320
+ createIfMissing: false,
1321
+ ctx,
1322
+ toolName: nameFor("remove_path")
1323
+ });
1324
+ await sandboxClient.removePath({
1325
+ sandboxId: resolved.sandboxId,
1326
+ path: input.path
1327
+ });
1328
+ return {
1329
+ sandboxId: resolved.sandboxId,
1330
+ removed: true,
1331
+ path: input.path
1332
+ };
1333
+ }),
1334
+ defineTool({
1335
+ name: nameFor("get_host"),
1336
+ description: "Expose one sandbox port as a host URL so callers can reach an app running inside the sandbox.",
1337
+ schema: {
1338
+ type: "object",
1339
+ properties: {
1340
+ sandboxId: sandboxIdSchema,
1341
+ port: {
1342
+ type: "number",
1343
+ minimum: 1,
1344
+ maximum: 65535
1345
+ }
1346
+ },
1347
+ required: ["port"],
1348
+ additionalProperties: false
1349
+ }
1350
+ }).server(async (input, ctx) => {
1351
+ const resolved = await resolveSandbox({
1352
+ sandboxId: input.sandboxId,
1353
+ createIfMissing: false,
1354
+ ctx,
1355
+ toolName: nameFor("get_host")
1356
+ });
1357
+ const hostResult = await sandboxClient.getHost({
1358
+ sandboxId: resolved.sandboxId,
1359
+ port: input.port
1360
+ });
1361
+ const preview = typeof hostResult === "string" ? void 0 : hostResult;
1362
+ const host = normalizeHostUrl(typeof hostResult === "string" ? hostResult : hostResult.url);
1363
+ return {
1364
+ sandboxId: resolved.sandboxId,
1365
+ port: input.port,
1366
+ host,
1367
+ ...preview?.token !== void 0 ? { token: preview.token } : {}
1368
+ };
1369
+ }),
1370
+ defineTool({
1371
+ name: nameFor("kill"),
1372
+ description: "Terminate a sandbox and clear the current conversation sandbox when applicable.",
1373
+ schema: {
1374
+ type: "object",
1375
+ properties: { sandboxId: sandboxIdSchema },
1376
+ additionalProperties: false
1377
+ },
1378
+ approval: config.approvals?.killSandbox
1379
+ }).server(async (input, ctx) => {
1380
+ const resolved = await resolveSandbox({
1381
+ sandboxId: input.sandboxId,
1382
+ createIfMissing: false,
1383
+ ctx,
1384
+ toolName: nameFor("kill")
1385
+ });
1386
+ await sandboxClient.killSandbox({ sandboxId: resolved.sandboxId });
1387
+ const clearedSessionKey = await clearManagedSessionIfMatches(ctx, nameFor("kill"), resolved.sandboxId);
1388
+ return {
1389
+ sandboxId: resolved.sandboxId,
1390
+ killed: true,
1391
+ ...clearedSessionKey !== void 0 ? { clearedSessionKey } : {}
1392
+ };
1393
+ })
1394
+ ];
1395
+ return {
1396
+ id: config.id ?? "sandbox",
1397
+ tools
1398
+ };
1399
+ };
1400
+
1401
+ //#endregion
1402
+ export { authPlugin, createDaytonaSandboxClient, createE2BSandboxClient, createMemorySandboxSessionStore, ipAllowlistPlugin, loggingPlugin, rateLimitPlugin, sandboxPlugin };
706
1403
  //# sourceMappingURL=index.mjs.map