zeitlich 0.2.23 → 0.2.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +44 -8
  2. package/dist/adapters/sandbox/bedrock/index.cjs +452 -0
  3. package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -0
  4. package/dist/adapters/sandbox/bedrock/index.d.cts +23 -0
  5. package/dist/adapters/sandbox/bedrock/index.d.ts +23 -0
  6. package/dist/adapters/sandbox/bedrock/index.js +449 -0
  7. package/dist/adapters/sandbox/bedrock/index.js.map +1 -0
  8. package/dist/adapters/sandbox/bedrock/workflow.cjs +33 -0
  9. package/dist/adapters/sandbox/bedrock/workflow.cjs.map +1 -0
  10. package/dist/adapters/sandbox/bedrock/workflow.d.cts +29 -0
  11. package/dist/adapters/sandbox/bedrock/workflow.d.ts +29 -0
  12. package/dist/adapters/sandbox/bedrock/workflow.js +31 -0
  13. package/dist/adapters/sandbox/bedrock/workflow.js.map +1 -0
  14. package/dist/adapters/sandbox/virtual/index.cjs +12 -2
  15. package/dist/adapters/sandbox/virtual/index.cjs.map +1 -1
  16. package/dist/adapters/sandbox/virtual/index.d.cts +4 -4
  17. package/dist/adapters/sandbox/virtual/index.d.ts +4 -4
  18. package/dist/adapters/sandbox/virtual/index.js +12 -2
  19. package/dist/adapters/sandbox/virtual/index.js.map +1 -1
  20. package/dist/adapters/sandbox/virtual/workflow.d.cts +2 -2
  21. package/dist/adapters/sandbox/virtual/workflow.d.ts +2 -2
  22. package/dist/adapters/thread/google-genai/index.d.cts +2 -2
  23. package/dist/adapters/thread/google-genai/index.d.ts +2 -2
  24. package/dist/adapters/thread/google-genai/workflow.d.cts +2 -2
  25. package/dist/adapters/thread/google-genai/workflow.d.ts +2 -2
  26. package/dist/adapters/thread/langchain/index.d.cts +2 -2
  27. package/dist/adapters/thread/langchain/index.d.ts +2 -2
  28. package/dist/adapters/thread/langchain/workflow.d.cts +2 -2
  29. package/dist/adapters/thread/langchain/workflow.d.ts +2 -2
  30. package/dist/index.cjs +202 -19
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +63 -10
  33. package/dist/index.d.ts +63 -10
  34. package/dist/index.js +203 -21
  35. package/dist/index.js.map +1 -1
  36. package/dist/{queries-DModcWRy.d.cts → queries-BYGBImeC.d.cts} +1 -1
  37. package/dist/{queries-byD0jr1Y.d.ts → queries-DwBe2CAA.d.ts} +1 -1
  38. package/dist/{types-DQW8l7pY.d.cts → types-7PeMi1bD.d.cts} +9 -2
  39. package/dist/types-BdCdR41N.d.ts +74 -0
  40. package/dist/{types-BuXdFhaZ.d.cts → types-Bf8KV0Ci.d.cts} +1 -1
  41. package/dist/{types-Bll19FZJ.d.ts → types-D_igp10o.d.cts} +4 -0
  42. package/dist/{types-Bll19FZJ.d.cts → types-D_igp10o.d.ts} +4 -0
  43. package/dist/{types-GZ76HZSj.d.ts → types-LVKmCNds.d.ts} +1 -1
  44. package/dist/types-ZHs2v9Ap.d.cts +74 -0
  45. package/dist/{types-B50pBPEV.d.ts → types-hmferhc2.d.ts} +9 -2
  46. package/dist/workflow.cjs +73 -11
  47. package/dist/workflow.cjs.map +1 -1
  48. package/dist/workflow.d.cts +12 -7
  49. package/dist/workflow.d.ts +12 -7
  50. package/dist/workflow.js +73 -11
  51. package/dist/workflow.js.map +1 -1
  52. package/package.json +26 -1
  53. package/src/adapters/sandbox/bedrock/filesystem.ts +346 -0
  54. package/src/adapters/sandbox/bedrock/index.ts +259 -0
  55. package/src/adapters/sandbox/bedrock/proxy.ts +56 -0
  56. package/src/adapters/sandbox/bedrock/types.ts +24 -0
  57. package/src/adapters/sandbox/virtual/filesystem.ts +5 -3
  58. package/src/adapters/sandbox/virtual/provider.ts +9 -0
  59. package/src/adapters/sandbox/virtual/virtual-sandbox.test.ts +26 -0
  60. package/src/index.ts +2 -1
  61. package/src/lib/lifecycle.ts +1 -1
  62. package/src/lib/sandbox/node-fs.ts +115 -0
  63. package/src/lib/session/session.integration.test.ts +97 -0
  64. package/src/lib/session/session.ts +33 -2
  65. package/src/lib/session/types.ts +1 -1
  66. package/src/lib/skills/fs-provider.ts +65 -4
  67. package/src/lib/skills/handler.ts +43 -1
  68. package/src/lib/skills/index.ts +0 -1
  69. package/src/lib/skills/register.ts +17 -1
  70. package/src/lib/skills/skills.integration.test.ts +308 -24
  71. package/src/lib/skills/types.ts +6 -0
  72. package/src/lib/subagent/handler.ts +10 -4
  73. package/src/lib/subagent/subagent.integration.test.ts +36 -4
  74. package/src/lib/tool-router/router.ts +6 -3
  75. package/src/lib/tool-router/types.ts +4 -0
  76. package/tsup.config.ts +3 -0
package/README.md CHANGED
@@ -61,6 +61,7 @@ A sandbox adapter provides filesystem access for tools like `Bash`, `Read`, `Wri
61
61
  | Virtual | `zeitlich/adapters/sandbox/virtual` | Custom resolvers with path-only ops |
62
62
  | Daytona | `zeitlich/adapters/sandbox/daytona` | Remote Daytona workspaces |
63
63
  | E2B | `zeitlich/adapters/sandbox/e2b` | E2B cloud sandboxes |
64
+ | Bedrock | `zeitlich/adapters/sandbox/bedrock` | AWS Bedrock AgentCore Code Interpreter |
64
65
 
65
66
  ### Example: LangChain Adapter
66
67
 
@@ -95,6 +96,7 @@ npm install zeitlich ioredis
95
96
  - `ioredis` >= 5.0.0
96
97
  - `@langchain/core` >= 1.0.0 (optional — only when using the LangChain adapter)
97
98
  - `@google/genai` >= 1.0.0 (optional — only when using the Google GenAI adapter)
99
+ - `@aws-sdk/client-bedrock-agentcore` >= 3.900.0 (optional — only when using the Bedrock adapter)
98
100
 
99
101
  **Required infrastructure:**
100
102
 
@@ -483,14 +485,18 @@ Zeitlich has first-class support for the [agentskills.io](https://agentskills.io
483
485
 
484
486
  #### Defining a Skill
485
487
 
486
- Each skill lives in its own directory as a `SKILL.md` file with YAML frontmatter:
488
+ Each skill lives in its own directory as a `SKILL.md` file with YAML frontmatter. A skill directory can also contain **resource files** — supporting documents, templates, or data that the agent can read from the sandbox filesystem:
487
489
 
488
490
  ```
489
491
  skills/
490
492
  ├── code-review/
491
- └── SKILL.md
493
+ ├── SKILL.md
494
+ │ └── resources/
495
+ │ └── checklist.md
492
496
  ├── pdf-processing/
493
- └── SKILL.md
497
+ ├── SKILL.md
498
+ │ └── templates/
499
+ │ └── extraction-prompt.txt
494
500
  ```
495
501
 
496
502
  ```markdown
@@ -506,14 +512,17 @@ license: MIT
506
512
  When reviewing code, follow these steps:
507
513
  1. Read the diff with `Bash`
508
514
  2. Search for related tests with `Grep`
509
- 3. ...
515
+ 3. Read the checklist from `resources/checklist.md`
516
+ 4. ...
510
517
  ```
511
518
 
512
519
  Required fields: `name` and `description`. Optional: `license`, `compatibility`, `allowed-tools` (space-delimited), `metadata` (key-value map).
513
520
 
521
+ Resource files are any non-`SKILL.md` files inside the skill directory (discovered recursively). When loaded via `FileSystemSkillProvider`, their contents are stored in `skill.resourceContents` — a `Record<string, string>` keyed by relative path (e.g. `"resources/checklist.md"`).
522
+
514
523
  #### Loading Skills
515
524
 
516
- Use `FileSystemSkillProvider` to load skills from a directory (works with any sandbox filesystem):
525
+ Use `FileSystemSkillProvider` to load skills from a directory. It accepts any `SandboxFileSystem` implementation. `loadAll()` eagerly reads `SKILL.md` instructions **and** all resource file contents into each `Skill` object:
517
526
 
518
527
  ```typescript
519
528
  import { FileSystemSkillProvider } from "zeitlich";
@@ -524,6 +533,28 @@ const { sandbox } = await provider.create({});
524
533
 
525
534
  const skillProvider = new FileSystemSkillProvider(sandbox.fs, "/skills");
526
535
  const skills = await skillProvider.loadAll();
536
+ // Each skill has: { name, description, instructions, resourceContents }
537
+ // resourceContents: { "resources/checklist.md": "...", ... }
538
+ ```
539
+
540
+ **Loading from the local filesystem (activity-side):** Use `NodeFsSandboxFileSystem` to read skills from the worker's disk. This is the simplest option when skill files are bundled alongside your application code:
541
+
542
+ ```typescript
543
+ import { NodeFsSandboxFileSystem, FileSystemSkillProvider } from "zeitlich";
544
+ import { fileURLToPath } from "node:url";
545
+ import { dirname, join } from "node:path";
546
+
547
+ const __dirname = dirname(fileURLToPath(import.meta.url));
548
+ const fs = new NodeFsSandboxFileSystem(join(__dirname, "skills"));
549
+ const skillProvider = new FileSystemSkillProvider(fs, "/");
550
+ const skills = await skillProvider.loadAll();
551
+ ```
552
+
553
+ For lightweight discovery without reading file contents, use `listSkills()`:
554
+
555
+ ```typescript
556
+ const metadata = await skillProvider.listSkills();
557
+ // SkillMetadata[] — name, description, location only
527
558
  ```
528
559
 
529
560
  Or parse a single file directly:
@@ -537,7 +568,10 @@ const { frontmatter, body } = parseSkillFile(rawMarkdown);
537
568
 
538
569
  #### Passing Skills to a Session
539
570
 
540
- Pass loaded skills to `createSession`. Zeitlich automatically registers a `ReadSkill` tool whose description lists all available skills — the agent discovers them through the tool definition and loads instructions on demand:
571
+ Pass loaded skills to `createSession`. Zeitlich automatically:
572
+
573
+ 1. Registers a `ReadSkill` tool whose description lists all available skills — the agent discovers them through the tool definition and loads instructions on demand.
574
+ 2. Seeds `resourceContents` into the sandbox as `initialFiles` (when `sandboxOps` is configured), so the agent can read resource files with its `Read` tool without any extra setup.
541
575
 
542
576
  ```typescript
543
577
  import { createSession } from "zeitlich/workflow";
@@ -548,7 +582,7 @@ const session = await createSession({
548
582
  });
549
583
  ```
550
584
 
551
- The `ReadSkill` tool accepts a `skill_name` parameter (constrained to an enum of available names) and returns the full instruction body. The handler runs directly in the workflow — no activity needed.
585
+ The `ReadSkill` tool accepts a `skill_name` parameter (constrained to an enum of available names) and returns the full instruction body plus a list of available resource file paths. The handler runs directly in the workflow — no activity needed. Resource file contents are not included in the `ReadSkill` response (progressive disclosure); the agent reads them from the sandbox filesystem on demand.
552
586
 
553
587
  #### Building Skills Manually
554
588
 
@@ -867,6 +901,7 @@ Framework-agnostic utilities for activities, worker setup, and Node.js code:
867
901
  | `createThreadManager` | Generic Redis-backed thread manager factory |
868
902
  | `toTree` | Generate file tree string from an `IFileSystem` instance |
869
903
  | `withSandbox` | Wraps a handler to auto-resolve sandbox from context (pairs with `withAutoAppend`) |
904
+ | `NodeFsSandboxFileSystem` | `node:fs` adapter for `SandboxFileSystem` — read skills from the worker's local disk |
870
905
  | `FileSystemSkillProvider` | Load skills from a directory following the agentskills.io layout |
871
906
  | Tool handlers | `bashHandler`, `editHandler`, `globHandler`, `readFileHandler`, `writeFileHandler`, `createAskUserQuestionHandler` |
872
907
 
@@ -926,6 +961,7 @@ Framework-agnostic utilities for activities, worker setup, and Node.js code:
926
961
  │ │ │ • Turns │ │ • Tool routing & hooks │ │ │
927
962
  │ │ │ • Custom state │ │ • Prompts (system, context) │ │ │
928
963
  │ │ └────────────────┘ │ • Subagent coordination │ │ │
964
+ │ │ │ • Skills (progressive load) │ │ │
929
965
  │ │ └───────────────────────────────┘ │ │
930
966
  │ └──────────────────────────────────────────────────────────┘ │
931
967
  │ │ │
@@ -942,7 +978,7 @@ Framework-agnostic utilities for activities, worker setup, and Node.js code:
942
978
  │ └──────────────────────────────────────────────────────────┘ │
943
979
  │ ┌──────────────────────────────────────────────────────────┐ │
944
980
  │ │ Sandbox Adapter (zeitlich/adapters/sandbox/*) │ │
945
- │ │ • In-memory, Virtual, Daytona, E2B, or custom │ │
981
+ │ │ • In-memory, Virtual, Daytona, E2B, Bedrock, or custom │ │
946
982
  │ │ • Filesystem ops for agent tools │ │
947
983
  │ └──────────────────────────────────────────────────────────┘ │
948
984
  └─────────────────────────────────────────────────────────────────┘
@@ -0,0 +1,452 @@
1
+ 'use strict';
2
+
3
+ var clientBedrockAgentcore = require('@aws-sdk/client-bedrock-agentcore');
4
+ var common = require('@temporalio/common');
5
+ var path = require('path');
6
+
7
+ // src/adapters/sandbox/bedrock/index.ts
8
+ var SandboxNotSupportedError = class extends common.ApplicationFailure {
9
+ constructor(operation) {
10
+ super(
11
+ `Sandbox does not support: ${operation}`,
12
+ "SandboxNotSupportedError",
13
+ true
14
+ );
15
+ }
16
+ };
17
+ var SandboxNotFoundError = class extends common.ApplicationFailure {
18
+ constructor(sandboxId) {
19
+ super(`Sandbox not found: ${sandboxId}`, "SandboxNotFoundError", true);
20
+ }
21
+ };
22
+ async function consumeStream(stream) {
23
+ for await (const event of stream) {
24
+ if ("result" in event && event.result) return event.result;
25
+ if ("accessDeniedException" in event && event.accessDeniedException)
26
+ throw new Error(event.accessDeniedException.message ?? "Access denied");
27
+ if ("resourceNotFoundException" in event && event.resourceNotFoundException)
28
+ throw new Error(
29
+ event.resourceNotFoundException.message ?? "Resource not found"
30
+ );
31
+ if ("validationException" in event && event.validationException)
32
+ throw new Error(
33
+ event.validationException.message ?? "Validation error"
34
+ );
35
+ if ("internalServerException" in event && event.internalServerException)
36
+ throw new Error(
37
+ event.internalServerException.message ?? "Internal server error"
38
+ );
39
+ if ("throttlingException" in event && event.throttlingException)
40
+ throw new Error(event.throttlingException.message ?? "Throttled");
41
+ if ("serviceQuotaExceededException" in event && event.serviceQuotaExceededException)
42
+ throw new Error(
43
+ event.serviceQuotaExceededException.message ?? "Quota exceeded"
44
+ );
45
+ if ("conflictException" in event && event.conflictException)
46
+ throw new Error(event.conflictException.message ?? "Conflict");
47
+ }
48
+ throw new Error("No result received from code interpreter stream");
49
+ }
50
+ var BedrockSandboxFileSystem = class {
51
+ constructor(client, codeInterpreterIdentifier, sessionId, workspaceBase = "/home/user") {
52
+ this.client = client;
53
+ this.codeInterpreterIdentifier = codeInterpreterIdentifier;
54
+ this.sessionId = sessionId;
55
+ this.workspaceBase = path.posix.resolve("/", workspaceBase);
56
+ }
57
+ workspaceBase;
58
+ /**
59
+ * Resolve a caller-supplied path to an absolute path within the workspace.
60
+ * Used for shell commands that need full paths.
61
+ */
62
+ normalisePath(path$1) {
63
+ if (path.posix.isAbsolute(path$1) && !path$1.startsWith(this.workspaceBase + "/") && path$1 !== this.workspaceBase) {
64
+ path$1 = path$1.replace(/^\/+/, "");
65
+ }
66
+ const resolved = path.posix.resolve(this.workspaceBase, path$1);
67
+ if (!resolved.startsWith(this.workspaceBase + "/") && resolved !== this.workspaceBase) {
68
+ throw new Error(
69
+ `Invalid file path: "${resolved}" escapes workspace "${this.workspaceBase}"`
70
+ );
71
+ }
72
+ return resolved;
73
+ }
74
+ /**
75
+ * Return a workspace-relative path for Bedrock tool invocations
76
+ * (`writeFiles`, `readFiles`, `listFiles`, `removeFiles`), which
77
+ * reject absolute paths as "path traversal".
78
+ */
79
+ toToolPath(path) {
80
+ const abs = this.normalisePath(path);
81
+ const prefix = this.workspaceBase + "/";
82
+ return abs.startsWith(prefix) ? abs.slice(prefix.length) : abs;
83
+ }
84
+ async invoke(name, args) {
85
+ const resp = await this.client.send(
86
+ new clientBedrockAgentcore.InvokeCodeInterpreterCommand({
87
+ codeInterpreterIdentifier: this.codeInterpreterIdentifier,
88
+ sessionId: this.sessionId,
89
+ name,
90
+ arguments: args
91
+ })
92
+ );
93
+ if (!resp.stream) throw new Error("No stream in code interpreter response");
94
+ return consumeStream(resp.stream);
95
+ }
96
+ async execShell(command) {
97
+ const result = await this.invoke("executeCommand", {
98
+ command
99
+ });
100
+ return {
101
+ stdout: result.structuredContent?.stdout ?? "",
102
+ stderr: result.structuredContent?.stderr ?? "",
103
+ exitCode: result.structuredContent?.exitCode ?? 0
104
+ };
105
+ }
106
+ async readFile(path) {
107
+ const rel = this.toToolPath(path);
108
+ const result = await this.invoke("readFiles", {
109
+ paths: [rel]
110
+ });
111
+ for (const block of result.content ?? []) {
112
+ if (block.resource?.text != null) return block.resource.text;
113
+ if (block.text != null) return block.text;
114
+ }
115
+ return "";
116
+ }
117
+ async readFileBuffer(path) {
118
+ const rel = this.toToolPath(path);
119
+ const result = await this.invoke("readFiles", {
120
+ paths: [rel]
121
+ });
122
+ for (const block of result.content ?? []) {
123
+ if (block.resource?.blob) return block.resource.blob;
124
+ if (block.data) return block.data;
125
+ if (block.resource?.text != null)
126
+ return new TextEncoder().encode(block.resource.text);
127
+ if (block.text != null) return new TextEncoder().encode(block.text);
128
+ }
129
+ return new Uint8Array(0);
130
+ }
131
+ async writeFile(path, content) {
132
+ const rel = this.toToolPath(path);
133
+ const isText = typeof content === "string";
134
+ const result = await this.invoke("writeFiles", {
135
+ content: [
136
+ {
137
+ path: rel,
138
+ ...isText ? { text: content } : { blob: content }
139
+ }
140
+ ]
141
+ });
142
+ if (result.isError) {
143
+ const msg = result.content?.map((b) => b.text).join("") ?? "writeFile failed";
144
+ throw new Error(msg);
145
+ }
146
+ }
147
+ async appendFile(path, content) {
148
+ const norm = this.normalisePath(path);
149
+ const addition = typeof content === "string" ? content : new TextDecoder().decode(content);
150
+ const escaped = addition.replace(/'/g, "'\\''");
151
+ const { exitCode, stderr } = await this.execShell(
152
+ `printf '%s' '${escaped}' >> "${norm}"`
153
+ );
154
+ if (exitCode !== 0) throw new Error(`appendFile failed: ${stderr}`);
155
+ }
156
+ async exists(path) {
157
+ const norm = this.normalisePath(path);
158
+ const { exitCode } = await this.execShell(`test -e "${norm}"`);
159
+ return exitCode === 0;
160
+ }
161
+ async stat(path) {
162
+ const norm = this.normalisePath(path);
163
+ const { stdout, exitCode, stderr } = await this.execShell(
164
+ `stat -c '%F %s %Y' "${norm}" 2>&1`
165
+ );
166
+ if (exitCode !== 0) throw new Error(`stat failed: ${stderr || stdout}`);
167
+ const parts = stdout.trim().split(" ");
168
+ const fileType = parts.slice(0, -2).join(" ");
169
+ const sizeStr = parts[parts.length - 2] ?? "0";
170
+ const mtimeStr = parts[parts.length - 1] ?? "0";
171
+ const size = parseInt(sizeStr, 10);
172
+ const mtimeEpoch = parseInt(mtimeStr, 10);
173
+ return {
174
+ isFile: fileType === "regular file" || fileType === "regular empty file",
175
+ isDirectory: fileType === "directory",
176
+ isSymbolicLink: fileType === "symbolic link",
177
+ size: isNaN(size) ? 0 : size,
178
+ mtime: new Date(isNaN(mtimeEpoch) ? 0 : mtimeEpoch * 1e3)
179
+ };
180
+ }
181
+ async mkdir(path, options) {
182
+ const norm = this.normalisePath(path);
183
+ const flag = options?.recursive ? "-p " : "";
184
+ const { exitCode, stderr } = await this.execShell(
185
+ `mkdir ${flag}"${norm}"`
186
+ );
187
+ if (exitCode !== 0) throw new Error(`mkdir failed: ${stderr}`);
188
+ }
189
+ async readdir(path) {
190
+ const rel = this.toToolPath(path);
191
+ const result = await this.invoke("listFiles", {
192
+ directoryPath: rel
193
+ });
194
+ const names = [];
195
+ for (const block of result.content ?? []) {
196
+ if (block.name) names.push(block.name);
197
+ }
198
+ if (names.length > 0) return names;
199
+ const norm = this.normalisePath(path);
200
+ const { stdout, exitCode, stderr } = await this.execShell(
201
+ `ls -1A "${norm}"`
202
+ );
203
+ if (exitCode !== 0) throw new Error(`readdir failed: ${stderr}`);
204
+ return stdout.trim().split("\n").filter((l) => l.length > 0);
205
+ }
206
+ async readdirWithFileTypes(path) {
207
+ const norm = this.normalisePath(path);
208
+ const { stdout, exitCode, stderr } = await this.execShell(
209
+ `find "${norm}" -maxdepth 1 -mindepth 1 -printf '%y %f\\n'`
210
+ );
211
+ if (exitCode !== 0)
212
+ throw new Error(`readdirWithFileTypes failed: ${stderr}`);
213
+ return stdout.trim().split("\n").filter((l) => l.length > 0).map((line) => {
214
+ const type = line.charAt(0);
215
+ const name = line.slice(2);
216
+ return {
217
+ name,
218
+ isFile: type === "f",
219
+ isDirectory: type === "d",
220
+ isSymbolicLink: type === "l"
221
+ };
222
+ });
223
+ }
224
+ async rm(path, options) {
225
+ const norm = this.normalisePath(path);
226
+ if (options?.recursive || options?.force) {
227
+ const flags = `${options?.recursive ? "-r" : ""} ${options?.force ? "-f" : ""}`.trim();
228
+ const { exitCode, stderr } = await this.execShell(
229
+ `rm ${flags} "${norm}"`
230
+ );
231
+ if (exitCode !== 0 && !options?.force)
232
+ throw new Error(`rm failed: ${stderr}`);
233
+ return;
234
+ }
235
+ const rel = this.toToolPath(path);
236
+ const result = await this.invoke("removeFiles", {
237
+ paths: [rel]
238
+ });
239
+ if (result.isError) {
240
+ const msg = result.content?.map((b) => b.text).join("") ?? "rm failed";
241
+ throw new Error(msg);
242
+ }
243
+ }
244
+ async cp(src, dest, options) {
245
+ const normSrc = this.normalisePath(src);
246
+ const normDest = this.normalisePath(dest);
247
+ const flag = options?.recursive ? "-r " : "";
248
+ const { exitCode, stderr } = await this.execShell(
249
+ `cp ${flag}"${normSrc}" "${normDest}"`
250
+ );
251
+ if (exitCode !== 0) throw new Error(`cp failed: ${stderr}`);
252
+ }
253
+ async mv(src, dest) {
254
+ const normSrc = this.normalisePath(src);
255
+ const normDest = this.normalisePath(dest);
256
+ const { exitCode, stderr } = await this.execShell(
257
+ `mv "${normSrc}" "${normDest}"`
258
+ );
259
+ if (exitCode !== 0) throw new Error(`mv failed: ${stderr}`);
260
+ }
261
+ async readlink(path) {
262
+ const norm = this.normalisePath(path);
263
+ const { stdout, exitCode, stderr } = await this.execShell(
264
+ `readlink "${norm}"`
265
+ );
266
+ if (exitCode !== 0)
267
+ throw new SandboxNotSupportedError(`readlink: ${stderr}`);
268
+ return stdout.trim();
269
+ }
270
+ resolvePath(base, path$1) {
271
+ return path.posix.resolve(this.normalisePath(base), path$1);
272
+ }
273
+ };
274
+
275
+ // src/adapters/sandbox/bedrock/index.ts
276
+ async function consumeExecStream(stream) {
277
+ for await (const event of stream) {
278
+ if ("result" in event && event.result) {
279
+ const sc = event.result.structuredContent;
280
+ return {
281
+ exitCode: sc?.exitCode ?? 0,
282
+ stdout: sc?.stdout ?? "",
283
+ stderr: sc?.stderr ?? ""
284
+ };
285
+ }
286
+ if ("accessDeniedException" in event && event.accessDeniedException)
287
+ throw new Error(event.accessDeniedException.message ?? "Access denied");
288
+ if ("resourceNotFoundException" in event && event.resourceNotFoundException)
289
+ throw new Error(
290
+ event.resourceNotFoundException.message ?? "Resource not found"
291
+ );
292
+ if ("validationException" in event && event.validationException)
293
+ throw new Error(
294
+ event.validationException.message ?? "Validation error"
295
+ );
296
+ if ("internalServerException" in event && event.internalServerException)
297
+ throw new Error(
298
+ event.internalServerException.message ?? "Internal server error"
299
+ );
300
+ if ("throttlingException" in event && event.throttlingException)
301
+ throw new Error(event.throttlingException.message ?? "Throttled");
302
+ if ("serviceQuotaExceededException" in event && event.serviceQuotaExceededException)
303
+ throw new Error(
304
+ event.serviceQuotaExceededException.message ?? "Quota exceeded"
305
+ );
306
+ if ("conflictException" in event && event.conflictException)
307
+ throw new Error(event.conflictException.message ?? "Conflict");
308
+ }
309
+ return { exitCode: 0, stdout: "", stderr: "" };
310
+ }
311
+ var BedrockSandboxImpl = class {
312
+ constructor(id, client, codeInterpreterIdentifier, sessionId, workspaceBase = "/home/user") {
313
+ this.id = id;
314
+ this.client = client;
315
+ this.codeInterpreterIdentifier = codeInterpreterIdentifier;
316
+ this.sessionId = sessionId;
317
+ this.fs = new BedrockSandboxFileSystem(
318
+ client,
319
+ codeInterpreterIdentifier,
320
+ sessionId,
321
+ workspaceBase
322
+ );
323
+ }
324
+ capabilities = {
325
+ filesystem: true,
326
+ execution: true,
327
+ persistence: false
328
+ };
329
+ fs;
330
+ async exec(command, options) {
331
+ let cmd = command;
332
+ if (options?.cwd) cmd = `cd "${options.cwd}" && ${cmd}`;
333
+ if (options?.env) {
334
+ const exports$1 = Object.entries(options.env).map(([k, v]) => `export ${k}="${v.replace(/"/g, '\\"')}"`).join(" && ");
335
+ cmd = `${exports$1} && ${cmd}`;
336
+ }
337
+ const resp = await this.client.send(
338
+ new clientBedrockAgentcore.InvokeCodeInterpreterCommand({
339
+ codeInterpreterIdentifier: this.codeInterpreterIdentifier,
340
+ sessionId: this.sessionId,
341
+ name: "executeCommand",
342
+ arguments: { command: cmd }
343
+ })
344
+ );
345
+ if (!resp.stream)
346
+ throw new Error("No stream in code interpreter response");
347
+ return consumeExecStream(resp.stream);
348
+ }
349
+ async destroy() {
350
+ await this.client.send(
351
+ new clientBedrockAgentcore.StopCodeInterpreterSessionCommand({
352
+ codeInterpreterIdentifier: this.codeInterpreterIdentifier,
353
+ sessionId: this.sessionId
354
+ })
355
+ );
356
+ }
357
+ };
358
+ var BedrockSandboxProvider = class {
359
+ id = "bedrock";
360
+ capabilities = {
361
+ filesystem: true,
362
+ execution: true,
363
+ persistence: false
364
+ };
365
+ client;
366
+ codeInterpreterIdentifier;
367
+ defaultWorkspaceBase;
368
+ constructor(config) {
369
+ this.client = new clientBedrockAgentcore.BedrockAgentCoreClient(config.clientConfig ?? {});
370
+ this.codeInterpreterIdentifier = config.codeInterpreterIdentifier;
371
+ this.defaultWorkspaceBase = config.workspaceBase ?? "/home/user";
372
+ }
373
+ async create(options) {
374
+ const resp = await this.client.send(
375
+ new clientBedrockAgentcore.StartCodeInterpreterSessionCommand({
376
+ codeInterpreterIdentifier: this.codeInterpreterIdentifier,
377
+ name: options?.name,
378
+ sessionTimeoutSeconds: options?.sessionTimeoutSeconds
379
+ })
380
+ );
381
+ const sessionId = resp.sessionId ?? "";
382
+ if (!sessionId) throw new Error("No sessionId returned from Bedrock");
383
+ const sandbox = new BedrockSandboxImpl(
384
+ sessionId,
385
+ this.client,
386
+ this.codeInterpreterIdentifier,
387
+ sessionId,
388
+ this.defaultWorkspaceBase
389
+ );
390
+ if (options?.initialFiles) {
391
+ for (const [path, content] of Object.entries(options.initialFiles)) {
392
+ await sandbox.fs.writeFile(path, content);
393
+ }
394
+ }
395
+ if (options?.env) {
396
+ const exports$1 = Object.entries(options.env).map(([k, v]) => `${k}="${v.replace(/"/g, '\\"')}"`).join(" ");
397
+ await sandbox.exec(`echo '${exports$1}' >> ~/.bashrc`);
398
+ }
399
+ return { sandbox };
400
+ }
401
+ async get(sandboxId) {
402
+ try {
403
+ const resp = await this.client.send(
404
+ new clientBedrockAgentcore.GetCodeInterpreterSessionCommand({
405
+ codeInterpreterIdentifier: this.codeInterpreterIdentifier,
406
+ sessionId: sandboxId
407
+ })
408
+ );
409
+ if (resp.status === "TERMINATED") {
410
+ throw new SandboxNotFoundError(sandboxId);
411
+ }
412
+ return new BedrockSandboxImpl(
413
+ sandboxId,
414
+ this.client,
415
+ this.codeInterpreterIdentifier,
416
+ sandboxId,
417
+ this.defaultWorkspaceBase
418
+ );
419
+ } catch (err) {
420
+ if (err instanceof SandboxNotFoundError) throw err;
421
+ throw new SandboxNotFoundError(sandboxId);
422
+ }
423
+ }
424
+ async destroy(sandboxId) {
425
+ try {
426
+ await this.client.send(
427
+ new clientBedrockAgentcore.StopCodeInterpreterSessionCommand({
428
+ codeInterpreterIdentifier: this.codeInterpreterIdentifier,
429
+ sessionId: sandboxId
430
+ })
431
+ );
432
+ } catch {
433
+ }
434
+ }
435
+ async pause(_sandboxId, _ttlSeconds) {
436
+ throw new SandboxNotSupportedError("pause");
437
+ }
438
+ async snapshot(_sandboxId) {
439
+ throw new SandboxNotSupportedError("snapshot");
440
+ }
441
+ async restore(_snapshot) {
442
+ throw new SandboxNotSupportedError("restore");
443
+ }
444
+ async fork(_sandboxId) {
445
+ throw new SandboxNotSupportedError("fork");
446
+ }
447
+ };
448
+
449
+ exports.BedrockSandboxFileSystem = BedrockSandboxFileSystem;
450
+ exports.BedrockSandboxProvider = BedrockSandboxProvider;
451
+ //# sourceMappingURL=index.cjs.map
452
+ //# sourceMappingURL=index.cjs.map