zeitlich 0.2.13 → 0.2.14

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 (135) hide show
  1. package/README.md +49 -38
  2. package/dist/adapters/sandbox/daytona/index.cjs +205 -0
  3. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -0
  4. package/dist/adapters/sandbox/daytona/index.d.cts +86 -0
  5. package/dist/adapters/sandbox/daytona/index.d.ts +86 -0
  6. package/dist/adapters/sandbox/daytona/index.js +202 -0
  7. package/dist/adapters/sandbox/daytona/index.js.map +1 -0
  8. package/dist/adapters/sandbox/inmemory/index.cjs +174 -0
  9. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -0
  10. package/dist/adapters/sandbox/inmemory/index.d.cts +28 -0
  11. package/dist/adapters/sandbox/inmemory/index.d.ts +28 -0
  12. package/dist/adapters/sandbox/inmemory/index.js +172 -0
  13. package/dist/adapters/sandbox/inmemory/index.js.map +1 -0
  14. package/dist/adapters/sandbox/virtual/index.cjs +405 -0
  15. package/dist/adapters/sandbox/virtual/index.cjs.map +1 -0
  16. package/dist/adapters/sandbox/virtual/index.d.cts +85 -0
  17. package/dist/adapters/sandbox/virtual/index.d.ts +85 -0
  18. package/dist/adapters/sandbox/virtual/index.js +400 -0
  19. package/dist/adapters/sandbox/virtual/index.js.map +1 -0
  20. package/dist/adapters/thread/google-genai/index.cjs +284 -0
  21. package/dist/adapters/thread/google-genai/index.cjs.map +1 -0
  22. package/dist/adapters/thread/google-genai/index.d.cts +145 -0
  23. package/dist/adapters/thread/google-genai/index.d.ts +145 -0
  24. package/dist/adapters/thread/google-genai/index.js +278 -0
  25. package/dist/adapters/thread/google-genai/index.js.map +1 -0
  26. package/dist/adapters/{langchain → thread/langchain}/index.cjs +7 -9
  27. package/dist/adapters/thread/langchain/index.cjs.map +1 -0
  28. package/dist/adapters/{langchain → thread/langchain}/index.d.cts +17 -21
  29. package/dist/adapters/{langchain → thread/langchain}/index.d.ts +17 -21
  30. package/dist/adapters/{langchain → thread/langchain}/index.js +7 -9
  31. package/dist/adapters/thread/langchain/index.js.map +1 -0
  32. package/dist/index.cjs +816 -545
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +235 -74
  35. package/dist/index.d.ts +235 -74
  36. package/dist/index.js +804 -540
  37. package/dist/index.js.map +1 -1
  38. package/dist/types-B4C9txdq.d.ts +389 -0
  39. package/dist/{thread-manager-qc0g5Rvd.d.cts → types-B9ljZewB.d.cts} +1 -6
  40. package/dist/{thread-manager-qc0g5Rvd.d.ts → types-B9ljZewB.d.ts} +1 -6
  41. package/dist/types-BMXzv7TN.d.cts +476 -0
  42. package/dist/types-BMXzv7TN.d.ts +476 -0
  43. package/dist/types-BVP87m_W.d.cts +121 -0
  44. package/dist/types-CDubRtad.d.cts +115 -0
  45. package/dist/types-CDubRtad.d.ts +115 -0
  46. package/dist/types-CwwgQ_9H.d.ts +121 -0
  47. package/dist/types-GpMU4b0w.d.cts +389 -0
  48. package/dist/workflow.cjs +444 -318
  49. package/dist/workflow.cjs.map +1 -1
  50. package/dist/workflow.d.cts +271 -222
  51. package/dist/workflow.d.ts +271 -222
  52. package/dist/workflow.js +440 -316
  53. package/dist/workflow.js.map +1 -1
  54. package/package.json +59 -6
  55. package/src/adapters/sandbox/daytona/filesystem.ts +136 -0
  56. package/src/adapters/sandbox/daytona/index.ts +149 -0
  57. package/src/adapters/sandbox/daytona/types.ts +34 -0
  58. package/src/adapters/sandbox/inmemory/index.ts +213 -0
  59. package/src/adapters/sandbox/virtual/filesystem.ts +345 -0
  60. package/src/adapters/sandbox/virtual/index.ts +88 -0
  61. package/src/adapters/sandbox/virtual/mutations.ts +38 -0
  62. package/src/adapters/sandbox/virtual/provider.ts +101 -0
  63. package/src/adapters/sandbox/virtual/tree.ts +82 -0
  64. package/src/adapters/sandbox/virtual/types.ts +127 -0
  65. package/src/adapters/sandbox/virtual/virtual-sandbox.test.ts +523 -0
  66. package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +91 -0
  67. package/src/adapters/thread/google-genai/activities.ts +121 -0
  68. package/src/adapters/thread/google-genai/index.ts +41 -0
  69. package/src/adapters/thread/google-genai/model-invoker.ts +154 -0
  70. package/src/adapters/thread/google-genai/thread-manager.ts +169 -0
  71. package/src/adapters/{langchain → thread/langchain}/activities.ts +11 -15
  72. package/src/adapters/{langchain → thread/langchain}/index.ts +1 -1
  73. package/src/adapters/{langchain → thread/langchain}/model-invoker.ts +15 -18
  74. package/src/adapters/{langchain → thread/langchain}/thread-manager.ts +1 -1
  75. package/src/index.ts +32 -24
  76. package/src/lib/activity.ts +87 -0
  77. package/src/lib/hooks/index.ts +11 -0
  78. package/src/lib/hooks/types.ts +98 -0
  79. package/src/lib/model/helpers.ts +6 -0
  80. package/src/lib/model/index.ts +13 -0
  81. package/src/lib/{model-invoker.ts → model/types.ts} +18 -1
  82. package/src/lib/sandbox/index.ts +19 -0
  83. package/src/lib/sandbox/manager.ts +76 -0
  84. package/src/lib/sandbox/sandbox.test.ts +158 -0
  85. package/src/lib/{fs.ts → sandbox/tree.ts} +6 -6
  86. package/src/lib/sandbox/types.ts +164 -0
  87. package/src/lib/session/index.ts +11 -0
  88. package/src/lib/{session.ts → session/session.ts} +76 -48
  89. package/src/lib/session/types.ts +93 -0
  90. package/src/lib/skills/fs-provider.ts +16 -15
  91. package/src/lib/skills/handler.ts +31 -0
  92. package/src/lib/skills/index.ts +5 -1
  93. package/src/lib/skills/register.ts +20 -0
  94. package/src/lib/skills/tool.ts +47 -0
  95. package/src/lib/state/index.ts +9 -0
  96. package/src/lib/{state-manager.ts → state/manager.ts} +10 -147
  97. package/src/lib/state/types.ts +134 -0
  98. package/src/lib/subagent/define.ts +71 -0
  99. package/src/lib/subagent/handler.ts +99 -0
  100. package/src/lib/subagent/index.ts +13 -0
  101. package/src/lib/subagent/register.ts +53 -0
  102. package/src/lib/subagent/tool.ts +80 -0
  103. package/src/lib/subagent/types.ts +92 -0
  104. package/src/lib/thread/index.ts +7 -0
  105. package/src/lib/{thread-manager.ts → thread/manager.ts} +1 -33
  106. package/src/lib/thread/types.ts +33 -0
  107. package/src/lib/tool-router/auto-append.ts +55 -0
  108. package/src/lib/tool-router/index.ts +41 -0
  109. package/src/lib/tool-router/router.ts +462 -0
  110. package/src/lib/tool-router/types.ts +478 -0
  111. package/src/lib/tool-router/with-sandbox.ts +70 -0
  112. package/src/lib/types.ts +5 -382
  113. package/src/tools/bash/bash.test.ts +53 -55
  114. package/src/tools/bash/handler.ts +23 -51
  115. package/src/tools/edit/handler.ts +67 -81
  116. package/src/tools/glob/handler.ts +60 -17
  117. package/src/tools/read-file/handler.ts +67 -0
  118. package/src/tools/read-skill/handler.ts +1 -31
  119. package/src/tools/read-skill/tool.ts +5 -47
  120. package/src/tools/subagent/handler.ts +1 -100
  121. package/src/tools/subagent/tool.ts +5 -93
  122. package/src/tools/task-create/handler.ts +1 -1
  123. package/src/tools/task-get/handler.ts +1 -1
  124. package/src/tools/task-list/handler.ts +1 -1
  125. package/src/tools/task-update/handler.ts +1 -1
  126. package/src/tools/write-file/handler.ts +47 -0
  127. package/src/workflow.ts +88 -47
  128. package/tsup.config.ts +8 -1
  129. package/dist/adapters/langchain/index.cjs.map +0 -1
  130. package/dist/adapters/langchain/index.js.map +0 -1
  131. package/dist/model-invoker-y_zlyMqu.d.cts +0 -892
  132. package/dist/model-invoker-y_zlyMqu.d.ts +0 -892
  133. package/src/lib/tool-router.ts +0 -977
  134. package/src/lib/workflow-helpers.ts +0 -50
  135. /package/src/lib/{thread-id.ts → thread/id.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zeitlich",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "[EXPERIMENTAL] An opinionated AI agent implementation for Temporal",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -27,14 +27,54 @@
27
27
  "default": "./dist/workflow.js"
28
28
  }
29
29
  },
30
- "./adapters/langchain": {
30
+ "./adapters/thread/langchain": {
31
31
  "import": {
32
- "types": "./dist/adapters/langchain/index.d.ts",
33
- "default": "./dist/adapters/langchain/index.js"
32
+ "types": "./dist/adapters/thread/langchain/index.d.ts",
33
+ "default": "./dist/adapters/thread/langchain/index.js"
34
34
  },
35
35
  "require": {
36
- "types": "./dist/adapters/langchain/index.d.ts",
37
- "default": "./dist/adapters/langchain/index.js"
36
+ "types": "./dist/adapters/thread/langchain/index.d.ts",
37
+ "default": "./dist/adapters/thread/langchain/index.js"
38
+ }
39
+ },
40
+ "./adapters/thread/google-genai": {
41
+ "import": {
42
+ "types": "./dist/adapters/thread/google-genai/index.d.ts",
43
+ "default": "./dist/adapters/thread/google-genai/index.js"
44
+ },
45
+ "require": {
46
+ "types": "./dist/adapters/thread/google-genai/index.d.ts",
47
+ "default": "./dist/adapters/thread/google-genai/index.js"
48
+ }
49
+ },
50
+ "./adapters/sandbox/inmemory": {
51
+ "import": {
52
+ "types": "./dist/adapters/sandbox/inmemory/index.d.ts",
53
+ "default": "./dist/adapters/sandbox/inmemory/index.js"
54
+ },
55
+ "require": {
56
+ "types": "./dist/adapters/sandbox/inmemory/index.d.ts",
57
+ "default": "./dist/adapters/sandbox/inmemory/index.js"
58
+ }
59
+ },
60
+ "./adapters/sandbox/virtual": {
61
+ "import": {
62
+ "types": "./dist/adapters/sandbox/virtual/index.d.ts",
63
+ "default": "./dist/adapters/sandbox/virtual/index.js"
64
+ },
65
+ "require": {
66
+ "types": "./dist/adapters/sandbox/virtual/index.d.ts",
67
+ "default": "./dist/adapters/sandbox/virtual/index.js"
68
+ }
69
+ },
70
+ "./adapters/sandbox/daytona": {
71
+ "import": {
72
+ "types": "./dist/adapters/sandbox/daytona/index.d.ts",
73
+ "default": "./dist/adapters/sandbox/daytona/index.js"
74
+ },
75
+ "require": {
76
+ "types": "./dist/adapters/sandbox/daytona/index.d.ts",
77
+ "default": "./dist/adapters/sandbox/daytona/index.js"
38
78
  }
39
79
  }
40
80
  },
@@ -52,6 +92,8 @@
52
92
  "format:check": "prettier --check .",
53
93
  "typecheck": "tsc --noEmit",
54
94
  "prepublishOnly": "npm run build",
95
+ "test": "vitest run",
96
+ "test:watch": "vitest",
55
97
  "clean": "rm -rf dist",
56
98
  "prepare": "husky",
57
99
  "prepack": "npm run build",
@@ -78,7 +120,10 @@
78
120
  "node": ">=18"
79
121
  },
80
122
  "devDependencies": {
123
+ "@daytonaio/sdk": "^0.149.0",
81
124
  "@eslint/js": "^10.0.1",
125
+ "@google/genai": "^1.44.0",
126
+ "@langchain/core": "^1.1.30",
82
127
  "@temporalio/envconfig": "^1.15.0",
83
128
  "@temporalio/worker": "^1.15.0",
84
129
  "@types/node": "^25.3.3",
@@ -92,10 +137,18 @@
92
137
  "vitest": "^4.0.18"
93
138
  },
94
139
  "peerDependencies": {
140
+ "@daytonaio/sdk": ">=0.100.0",
141
+ "@google/genai": "^1.43.0",
95
142
  "@langchain/core": ">=1.0.0",
96
143
  "ioredis": ">=5.0.0"
97
144
  },
98
145
  "peerDependenciesMeta": {
146
+ "@daytonaio/sdk": {
147
+ "optional": true
148
+ },
149
+ "@google/genai": {
150
+ "optional": true
151
+ },
99
152
  "@langchain/core": {
100
153
  "optional": true
101
154
  }
@@ -0,0 +1,136 @@
1
+ import type { Sandbox as DaytonaSdkSandbox } from "@daytonaio/sdk";
2
+ import type {
3
+ SandboxFileSystem,
4
+ DirentEntry,
5
+ FileStat,
6
+ } from "../../../lib/sandbox/types";
7
+ import { SandboxNotSupportedError } from "../../../lib/sandbox/types";
8
+ import { posix } from "node:path";
9
+
10
+ /**
11
+ * {@link SandboxFileSystem} backed by a Daytona SDK sandbox.
12
+ *
13
+ * Maps zeitlich's filesystem interface to Daytona's `sandbox.fs` and
14
+ * `sandbox.process` APIs. Operations that have no direct Daytona equivalent
15
+ * (e.g. `appendFile`, `cp`) are composed from primitives.
16
+ */
17
+ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
18
+ constructor(private sandbox: DaytonaSdkSandbox) {}
19
+
20
+ async readFile(path: string): Promise<string> {
21
+ const buf = await this.sandbox.fs.downloadFile(path);
22
+ return buf.toString("utf-8");
23
+ }
24
+
25
+ async readFileBuffer(path: string): Promise<Uint8Array> {
26
+ const buf = await this.sandbox.fs.downloadFile(path);
27
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
28
+ }
29
+
30
+ async writeFile(path: string, content: string | Uint8Array): Promise<void> {
31
+ const buf =
32
+ typeof content === "string"
33
+ ? Buffer.from(content, "utf-8")
34
+ : Buffer.from(content);
35
+ await this.sandbox.fs.uploadFile(buf, path);
36
+ }
37
+
38
+ async appendFile(
39
+ path: string,
40
+ content: string | Uint8Array,
41
+ ): Promise<void> {
42
+ let existing: Buffer;
43
+ try {
44
+ existing = await this.sandbox.fs.downloadFile(path);
45
+ } catch {
46
+ return this.writeFile(path, content);
47
+ }
48
+
49
+ const addition =
50
+ typeof content === "string" ? Buffer.from(content, "utf-8") : content;
51
+ const merged = Buffer.concat([existing, Buffer.from(addition)]);
52
+ await this.sandbox.fs.uploadFile(merged, path);
53
+ }
54
+
55
+ async exists(path: string): Promise<boolean> {
56
+ try {
57
+ await this.sandbox.fs.getFileDetails(path);
58
+ return true;
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+
64
+ async stat(path: string): Promise<FileStat> {
65
+ const info = await this.sandbox.fs.getFileDetails(path);
66
+ return {
67
+ isFile: !info.isDir,
68
+ isDirectory: info.isDir,
69
+ isSymbolicLink: false,
70
+ size: info.size,
71
+ mtime: new Date(info.modTime),
72
+ };
73
+ }
74
+
75
+ async mkdir(
76
+ path: string,
77
+ _options?: { recursive?: boolean },
78
+ ): Promise<void> {
79
+ await this.sandbox.fs.createFolder(path, "755");
80
+ }
81
+
82
+ async readdir(path: string): Promise<string[]> {
83
+ const entries = await this.sandbox.fs.listFiles(path);
84
+ return entries.map((e) => e.name);
85
+ }
86
+
87
+ async readdirWithFileTypes(path: string): Promise<DirentEntry[]> {
88
+ const entries = await this.sandbox.fs.listFiles(path);
89
+ return entries.map((e) => ({
90
+ name: e.name,
91
+ isFile: !e.isDir,
92
+ isDirectory: e.isDir,
93
+ isSymbolicLink: false,
94
+ }));
95
+ }
96
+
97
+ async rm(
98
+ path: string,
99
+ options?: { recursive?: boolean; force?: boolean },
100
+ ): Promise<void> {
101
+ try {
102
+ await this.sandbox.fs.deleteFile(path, options?.recursive);
103
+ } catch (err) {
104
+ if (!options?.force) throw err;
105
+ }
106
+ }
107
+
108
+ async cp(
109
+ src: string,
110
+ dest: string,
111
+ options?: { recursive?: boolean },
112
+ ): Promise<void> {
113
+ const info = await this.sandbox.fs.getFileDetails(src);
114
+ if (info.isDir) {
115
+ if (!options?.recursive) {
116
+ throw new Error(`EISDIR: is a directory (use recursive): ${src}`);
117
+ }
118
+ await this.sandbox.process.executeCommand(`cp -r "${src}" "${dest}"`);
119
+ } else {
120
+ await this.sandbox.process.executeCommand(`cp "${src}" "${dest}"`);
121
+ }
122
+ }
123
+
124
+ async mv(src: string, dest: string): Promise<void> {
125
+ await this.sandbox.fs.moveFiles(src, dest);
126
+ }
127
+
128
+ async readlink(_path: string): Promise<string> {
129
+ throw new SandboxNotSupportedError("readlink");
130
+ }
131
+
132
+ resolvePath(base: string, path: string): string {
133
+ if (posix.isAbsolute(path)) return posix.normalize(path);
134
+ return posix.resolve(base, path);
135
+ }
136
+ }
@@ -0,0 +1,149 @@
1
+ import {
2
+ Daytona,
3
+ type Sandbox as DaytonaSdkSandbox,
4
+ } from "@daytonaio/sdk";
5
+ import type {
6
+ Sandbox,
7
+ SandboxCapabilities,
8
+ SandboxCreateResult,
9
+ SandboxProvider,
10
+ SandboxSnapshot,
11
+ ExecOptions,
12
+ ExecResult,
13
+ } from "../../../lib/sandbox/types";
14
+ import {
15
+ SandboxNotFoundError,
16
+ SandboxNotSupportedError,
17
+ } from "../../../lib/sandbox/types";
18
+ import { DaytonaSandboxFileSystem } from "./filesystem";
19
+ import type {
20
+ DaytonaSandbox,
21
+ DaytonaSandboxConfig,
22
+ DaytonaSandboxCreateOptions,
23
+ } from "./types";
24
+
25
+ // ============================================================================
26
+ // DaytonaSandbox
27
+ // ============================================================================
28
+
29
+ class DaytonaSandboxImpl implements Sandbox {
30
+ readonly capabilities: SandboxCapabilities = {
31
+ filesystem: true,
32
+ execution: true,
33
+ persistence: false,
34
+ };
35
+
36
+ readonly fs: DaytonaSandboxFileSystem;
37
+
38
+ constructor(
39
+ readonly id: string,
40
+ private sdkSandbox: DaytonaSdkSandbox,
41
+ ) {
42
+ this.fs = new DaytonaSandboxFileSystem(sdkSandbox);
43
+ }
44
+
45
+ async exec(command: string, options?: ExecOptions): Promise<ExecResult> {
46
+ const response = await this.sdkSandbox.process.executeCommand(
47
+ command,
48
+ options?.cwd,
49
+ options?.env,
50
+ options?.timeout,
51
+ );
52
+
53
+ return {
54
+ exitCode: response.exitCode ?? 0,
55
+ stdout: response.result ?? "",
56
+ stderr: "",
57
+ };
58
+ }
59
+
60
+ async destroy(): Promise<void> {
61
+ await this.sdkSandbox.delete(60);
62
+ }
63
+ }
64
+
65
+ // ============================================================================
66
+ // DaytonaSandboxProvider
67
+ // ============================================================================
68
+
69
+ export class DaytonaSandboxProvider
70
+ implements SandboxProvider<DaytonaSandboxCreateOptions, DaytonaSandbox>
71
+ {
72
+ readonly id = "daytona";
73
+ readonly capabilities: SandboxCapabilities = {
74
+ filesystem: true,
75
+ execution: true,
76
+ persistence: false,
77
+ };
78
+
79
+ private client: Daytona;
80
+
81
+ constructor(config?: DaytonaSandboxConfig) {
82
+ this.client = new Daytona(config);
83
+ }
84
+
85
+ async create(
86
+ options?: DaytonaSandboxCreateOptions,
87
+ ): Promise<SandboxCreateResult> {
88
+ const sdkSandbox = await this.client.create(
89
+ {
90
+ language: options?.language,
91
+ snapshot: options?.snapshot,
92
+ envVars: options?.env,
93
+ labels: options?.labels,
94
+ autoStopInterval: options?.autoStopInterval,
95
+ autoArchiveInterval: options?.autoArchiveInterval,
96
+ autoDeleteInterval: options?.autoDeleteInterval,
97
+ },
98
+ { timeout: options?.timeout ?? 60 },
99
+ );
100
+
101
+ const sandbox = new DaytonaSandboxImpl(sdkSandbox.id, sdkSandbox);
102
+
103
+ if (options?.initialFiles) {
104
+ for (const [path, content] of Object.entries(options.initialFiles)) {
105
+ await sandbox.fs.writeFile(path, content);
106
+ }
107
+ }
108
+
109
+ return { sandbox };
110
+ }
111
+
112
+ async get(sandboxId: string): Promise<DaytonaSandbox> {
113
+ try {
114
+ const sdkSandbox = await this.client.get(sandboxId);
115
+ return new DaytonaSandboxImpl(sdkSandbox.id, sdkSandbox);
116
+ } catch {
117
+ throw new SandboxNotFoundError(sandboxId);
118
+ }
119
+ }
120
+
121
+ async destroy(sandboxId: string): Promise<void> {
122
+ try {
123
+ const sdkSandbox = await this.client.get(sandboxId);
124
+ await this.client.delete(sdkSandbox);
125
+ } catch {
126
+ // Already gone
127
+ }
128
+ }
129
+
130
+ async snapshot(_sandboxId: string): Promise<SandboxSnapshot> {
131
+ throw new SandboxNotSupportedError(
132
+ "snapshot (use Daytona's native snapshot API directly)",
133
+ );
134
+ }
135
+
136
+ async restore(_snapshot: SandboxSnapshot): Promise<never> {
137
+ throw new SandboxNotSupportedError(
138
+ "restore (use Daytona's native snapshot API directly)",
139
+ );
140
+ }
141
+ }
142
+
143
+ // Re-exports
144
+ export { DaytonaSandboxFileSystem } from "./filesystem";
145
+ export type {
146
+ DaytonaSandbox,
147
+ DaytonaSandboxConfig,
148
+ DaytonaSandboxCreateOptions,
149
+ } from "./types";
@@ -0,0 +1,34 @@
1
+ import type { Sandbox, SandboxCreateOptions } from "../../../lib/sandbox/types";
2
+ import type { DaytonaSandboxFileSystem } from "./filesystem";
3
+
4
+ /**
5
+ * A Daytona-backed {@link Sandbox} with its typed filesystem.
6
+ */
7
+ export type DaytonaSandbox = Sandbox & { fs: DaytonaSandboxFileSystem };
8
+
9
+ export interface DaytonaSandboxConfig {
10
+ apiKey?: string;
11
+ apiUrl?: string;
12
+ target?: string;
13
+ }
14
+
15
+ export interface DaytonaSandboxCreateOptions extends SandboxCreateOptions {
16
+ /** Programming language runtime. Defaults to "python". */
17
+ language?: string;
18
+ /** Daytona snapshot name to create the sandbox from. */
19
+ snapshot?: string;
20
+ /** Custom Docker image to use. */
21
+ image?: string;
22
+ /** Resource allocation. */
23
+ resources?: { cpu?: number; memory?: number; disk?: number };
24
+ /** Auto-stop interval in minutes (0 = disabled). Default 15. */
25
+ autoStopInterval?: number;
26
+ /** Auto-archive interval in minutes (0 = max interval). Default 7 days. */
27
+ autoArchiveInterval?: number;
28
+ /** Auto-delete interval in minutes (negative = disabled). */
29
+ autoDeleteInterval?: number;
30
+ /** Custom labels for the sandbox. */
31
+ labels?: Record<string, string>;
32
+ /** Timeout in seconds for sandbox creation. Default 60. */
33
+ timeout?: number;
34
+ }
@@ -0,0 +1,213 @@
1
+ import {
2
+ Bash,
3
+ InMemoryFs,
4
+ type BashOptions,
5
+ type IFileSystem,
6
+ type InitialFiles,
7
+ } from "just-bash";
8
+ import type {
9
+ Sandbox,
10
+ SandboxCapabilities,
11
+ SandboxCreateOptions,
12
+ SandboxCreateResult,
13
+ SandboxFileSystem,
14
+ SandboxProvider,
15
+ SandboxSnapshot,
16
+ ExecOptions,
17
+ ExecResult,
18
+ DirentEntry,
19
+ FileStat,
20
+ } from "../../../lib/sandbox/types";
21
+ import { SandboxNotFoundError } from "../../../lib/sandbox/types";
22
+ import { getShortId } from "../../../lib/thread/id";
23
+
24
+ // ============================================================================
25
+ // Adapter: IFileSystem → SandboxFileSystem
26
+ // ============================================================================
27
+
28
+ function toSandboxFs(fs: IFileSystem): SandboxFileSystem {
29
+ return {
30
+ readFile: (path) => fs.readFile(path),
31
+ readFileBuffer: (path) => fs.readFileBuffer(path),
32
+ writeFile: (path, content) => fs.writeFile(path, content),
33
+ appendFile: (path, content) => fs.appendFile(path, content),
34
+ exists: (path) => fs.exists(path),
35
+ stat: async (path): Promise<FileStat> => {
36
+ const s = await fs.stat(path);
37
+ return {
38
+ isFile: s.isFile,
39
+ isDirectory: s.isDirectory,
40
+ isSymbolicLink: s.isSymbolicLink,
41
+ size: s.size,
42
+ mtime: s.mtime,
43
+ };
44
+ },
45
+ mkdir: (path, opts) => fs.mkdir(path, opts),
46
+ readdir: (path) => fs.readdir(path),
47
+ readdirWithFileTypes: async (path): Promise<DirentEntry[]> => {
48
+ if (!fs.readdirWithFileTypes) {
49
+ const names = await fs.readdir(path);
50
+ return Promise.all(
51
+ names.map(async (name) => {
52
+ const s = await fs.stat(`${path}/${name}`);
53
+ return {
54
+ name,
55
+ isFile: s.isFile,
56
+ isDirectory: s.isDirectory,
57
+ isSymbolicLink: s.isSymbolicLink,
58
+ };
59
+ })
60
+ );
61
+ }
62
+ return fs.readdirWithFileTypes(path);
63
+ },
64
+ rm: (path, opts) => fs.rm(path, opts),
65
+ cp: (src, dest, opts) => fs.cp(src, dest, opts),
66
+ mv: (src, dest) => fs.mv(src, dest),
67
+ readlink: (path) => fs.readlink(path),
68
+ resolvePath: (base, p) => fs.resolvePath(base, p),
69
+ };
70
+ }
71
+
72
+ // ============================================================================
73
+ // InMemorySandbox
74
+ // ============================================================================
75
+
76
+ export interface InMemorySandboxOptions {
77
+ /** Options forwarded to `just-bash` `Bash` (minus `fs` which is managed) */
78
+ bashOptions?: Omit<BashOptions, "fs">;
79
+ }
80
+
81
+ /**
82
+ * An in-memory {@link Sandbox} backed by `just-bash`.
83
+ */
84
+ export type InMemorySandbox = Sandbox & { fs: SandboxFileSystem };
85
+
86
+ class InMemorySandboxImpl implements Sandbox {
87
+ readonly capabilities: SandboxCapabilities = {
88
+ filesystem: true,
89
+ execution: true,
90
+ persistence: true,
91
+ };
92
+
93
+ readonly fs: SandboxFileSystem;
94
+ private bashOptions: Omit<BashOptions, "fs">;
95
+
96
+ constructor(
97
+ readonly id: string,
98
+ private justBashFs: IFileSystem,
99
+ options?: InMemorySandboxOptions
100
+ ) {
101
+ this.fs = toSandboxFs(justBashFs);
102
+ this.bashOptions = {
103
+ executionLimits: { maxStringLength: 52_428_800 },
104
+ ...options?.bashOptions,
105
+ };
106
+ }
107
+
108
+ async exec(command: string, _options?: ExecOptions): Promise<ExecResult> {
109
+ const bash = new Bash({ ...this.bashOptions, fs: this.justBashFs });
110
+ const { exitCode, stderr, stdout } = await bash.exec(command);
111
+ return { exitCode, stdout, stderr };
112
+ }
113
+
114
+ async destroy(): Promise<void> {
115
+ // In-memory: nothing to clean up
116
+ }
117
+
118
+ /** Expose the underlying IFileSystem for snapshot serialisation */
119
+ _getJustBashFs(): IFileSystem {
120
+ return this.justBashFs;
121
+ }
122
+ }
123
+
124
+ // ============================================================================
125
+ // InMemorySandboxProvider
126
+ // ============================================================================
127
+
128
+ export class InMemorySandboxProvider implements SandboxProvider {
129
+ readonly id = "inmemory";
130
+ readonly capabilities: SandboxCapabilities = {
131
+ filesystem: true,
132
+ execution: true,
133
+ persistence: true,
134
+ };
135
+
136
+ private sandboxes = new Map<string, InMemorySandboxImpl>();
137
+
138
+ constructor(private defaultOptions?: InMemorySandboxOptions) {}
139
+
140
+ async get(id: string): Promise<Sandbox> {
141
+ const sandbox = this.sandboxes.get(id);
142
+ if (!sandbox) throw new SandboxNotFoundError(id);
143
+ return sandbox;
144
+ }
145
+
146
+ async destroy(id: string): Promise<void> {
147
+ const sandbox = this.sandboxes.get(id);
148
+ if (sandbox) {
149
+ await sandbox.destroy();
150
+ this.sandboxes.delete(id);
151
+ }
152
+ }
153
+
154
+ async create(options?: SandboxCreateOptions): Promise<SandboxCreateResult> {
155
+ const id = options?.id ?? getShortId();
156
+ const initialFiles: InitialFiles = {};
157
+
158
+ if (options?.initialFiles) {
159
+ for (const [path, content] of Object.entries(options.initialFiles)) {
160
+ initialFiles[path] = content;
161
+ }
162
+ }
163
+
164
+ const fs = new InMemoryFs(initialFiles);
165
+ const sandbox = new InMemorySandboxImpl(id, fs, this.defaultOptions);
166
+ this.sandboxes.set(id, sandbox);
167
+ return { sandbox };
168
+ }
169
+
170
+ async snapshot(sandboxId: string): Promise<SandboxSnapshot> {
171
+ const sandbox = this.sandboxes.get(sandboxId);
172
+ if (!sandbox) throw new SandboxNotFoundError(sandboxId);
173
+
174
+ const fs = sandbox._getJustBashFs();
175
+ const paths = fs.getAllPaths();
176
+ const files: Record<string, string> = {};
177
+
178
+ for (const p of paths) {
179
+ try {
180
+ const stat = await fs.stat(p);
181
+ if (stat.isFile) {
182
+ files[p] = await fs.readFile(p);
183
+ }
184
+ } catch {
185
+ // skip entries that can't be read (e.g. broken symlinks)
186
+ }
187
+ }
188
+
189
+ return {
190
+ sandboxId,
191
+ providerId: this.id,
192
+ data: { files },
193
+ createdAt: new Date().toISOString(),
194
+ };
195
+ }
196
+
197
+ async restore(snapshot: SandboxSnapshot): Promise<Sandbox> {
198
+ const { files } = snapshot.data as { files: Record<string, string> };
199
+ const initialFiles: InitialFiles = {};
200
+ for (const [path, content] of Object.entries(files)) {
201
+ initialFiles[path] = content;
202
+ }
203
+
204
+ const fs = new InMemoryFs(initialFiles);
205
+ const sandbox = new InMemorySandboxImpl(
206
+ snapshot.sandboxId,
207
+ fs,
208
+ this.defaultOptions
209
+ );
210
+ this.sandboxes.set(sandbox.id, sandbox);
211
+ return sandbox;
212
+ }
213
+ }