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.
- package/README.md +49 -38
- package/dist/adapters/sandbox/daytona/index.cjs +205 -0
- package/dist/adapters/sandbox/daytona/index.cjs.map +1 -0
- package/dist/adapters/sandbox/daytona/index.d.cts +86 -0
- package/dist/adapters/sandbox/daytona/index.d.ts +86 -0
- package/dist/adapters/sandbox/daytona/index.js +202 -0
- package/dist/adapters/sandbox/daytona/index.js.map +1 -0
- package/dist/adapters/sandbox/inmemory/index.cjs +174 -0
- package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -0
- package/dist/adapters/sandbox/inmemory/index.d.cts +28 -0
- package/dist/adapters/sandbox/inmemory/index.d.ts +28 -0
- package/dist/adapters/sandbox/inmemory/index.js +172 -0
- package/dist/adapters/sandbox/inmemory/index.js.map +1 -0
- package/dist/adapters/sandbox/virtual/index.cjs +405 -0
- package/dist/adapters/sandbox/virtual/index.cjs.map +1 -0
- package/dist/adapters/sandbox/virtual/index.d.cts +85 -0
- package/dist/adapters/sandbox/virtual/index.d.ts +85 -0
- package/dist/adapters/sandbox/virtual/index.js +400 -0
- package/dist/adapters/sandbox/virtual/index.js.map +1 -0
- package/dist/adapters/thread/google-genai/index.cjs +284 -0
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -0
- package/dist/adapters/thread/google-genai/index.d.cts +145 -0
- package/dist/adapters/thread/google-genai/index.d.ts +145 -0
- package/dist/adapters/thread/google-genai/index.js +278 -0
- package/dist/adapters/thread/google-genai/index.js.map +1 -0
- package/dist/adapters/{langchain → thread/langchain}/index.cjs +7 -9
- package/dist/adapters/thread/langchain/index.cjs.map +1 -0
- package/dist/adapters/{langchain → thread/langchain}/index.d.cts +17 -21
- package/dist/adapters/{langchain → thread/langchain}/index.d.ts +17 -21
- package/dist/adapters/{langchain → thread/langchain}/index.js +7 -9
- package/dist/adapters/thread/langchain/index.js.map +1 -0
- package/dist/index.cjs +816 -545
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +235 -74
- package/dist/index.d.ts +235 -74
- package/dist/index.js +804 -540
- package/dist/index.js.map +1 -1
- package/dist/types-B4C9txdq.d.ts +389 -0
- package/dist/{thread-manager-qc0g5Rvd.d.cts → types-B9ljZewB.d.cts} +1 -6
- package/dist/{thread-manager-qc0g5Rvd.d.ts → types-B9ljZewB.d.ts} +1 -6
- package/dist/types-BMXzv7TN.d.cts +476 -0
- package/dist/types-BMXzv7TN.d.ts +476 -0
- package/dist/types-BVP87m_W.d.cts +121 -0
- package/dist/types-CDubRtad.d.cts +115 -0
- package/dist/types-CDubRtad.d.ts +115 -0
- package/dist/types-CwwgQ_9H.d.ts +121 -0
- package/dist/types-GpMU4b0w.d.cts +389 -0
- package/dist/workflow.cjs +444 -318
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +271 -222
- package/dist/workflow.d.ts +271 -222
- package/dist/workflow.js +440 -316
- package/dist/workflow.js.map +1 -1
- package/package.json +59 -6
- package/src/adapters/sandbox/daytona/filesystem.ts +136 -0
- package/src/adapters/sandbox/daytona/index.ts +149 -0
- package/src/adapters/sandbox/daytona/types.ts +34 -0
- package/src/adapters/sandbox/inmemory/index.ts +213 -0
- package/src/adapters/sandbox/virtual/filesystem.ts +345 -0
- package/src/adapters/sandbox/virtual/index.ts +88 -0
- package/src/adapters/sandbox/virtual/mutations.ts +38 -0
- package/src/adapters/sandbox/virtual/provider.ts +101 -0
- package/src/adapters/sandbox/virtual/tree.ts +82 -0
- package/src/adapters/sandbox/virtual/types.ts +127 -0
- package/src/adapters/sandbox/virtual/virtual-sandbox.test.ts +523 -0
- package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +91 -0
- package/src/adapters/thread/google-genai/activities.ts +121 -0
- package/src/adapters/thread/google-genai/index.ts +41 -0
- package/src/adapters/thread/google-genai/model-invoker.ts +154 -0
- package/src/adapters/thread/google-genai/thread-manager.ts +169 -0
- package/src/adapters/{langchain → thread/langchain}/activities.ts +11 -15
- package/src/adapters/{langchain → thread/langchain}/index.ts +1 -1
- package/src/adapters/{langchain → thread/langchain}/model-invoker.ts +15 -18
- package/src/adapters/{langchain → thread/langchain}/thread-manager.ts +1 -1
- package/src/index.ts +32 -24
- package/src/lib/activity.ts +87 -0
- package/src/lib/hooks/index.ts +11 -0
- package/src/lib/hooks/types.ts +98 -0
- package/src/lib/model/helpers.ts +6 -0
- package/src/lib/model/index.ts +13 -0
- package/src/lib/{model-invoker.ts → model/types.ts} +18 -1
- package/src/lib/sandbox/index.ts +19 -0
- package/src/lib/sandbox/manager.ts +76 -0
- package/src/lib/sandbox/sandbox.test.ts +158 -0
- package/src/lib/{fs.ts → sandbox/tree.ts} +6 -6
- package/src/lib/sandbox/types.ts +164 -0
- package/src/lib/session/index.ts +11 -0
- package/src/lib/{session.ts → session/session.ts} +76 -48
- package/src/lib/session/types.ts +93 -0
- package/src/lib/skills/fs-provider.ts +16 -15
- package/src/lib/skills/handler.ts +31 -0
- package/src/lib/skills/index.ts +5 -1
- package/src/lib/skills/register.ts +20 -0
- package/src/lib/skills/tool.ts +47 -0
- package/src/lib/state/index.ts +9 -0
- package/src/lib/{state-manager.ts → state/manager.ts} +10 -147
- package/src/lib/state/types.ts +134 -0
- package/src/lib/subagent/define.ts +71 -0
- package/src/lib/subagent/handler.ts +99 -0
- package/src/lib/subagent/index.ts +13 -0
- package/src/lib/subagent/register.ts +53 -0
- package/src/lib/subagent/tool.ts +80 -0
- package/src/lib/subagent/types.ts +92 -0
- package/src/lib/thread/index.ts +7 -0
- package/src/lib/{thread-manager.ts → thread/manager.ts} +1 -33
- package/src/lib/thread/types.ts +33 -0
- package/src/lib/tool-router/auto-append.ts +55 -0
- package/src/lib/tool-router/index.ts +41 -0
- package/src/lib/tool-router/router.ts +462 -0
- package/src/lib/tool-router/types.ts +478 -0
- package/src/lib/tool-router/with-sandbox.ts +70 -0
- package/src/lib/types.ts +5 -382
- package/src/tools/bash/bash.test.ts +53 -55
- package/src/tools/bash/handler.ts +23 -51
- package/src/tools/edit/handler.ts +67 -81
- package/src/tools/glob/handler.ts +60 -17
- package/src/tools/read-file/handler.ts +67 -0
- package/src/tools/read-skill/handler.ts +1 -31
- package/src/tools/read-skill/tool.ts +5 -47
- package/src/tools/subagent/handler.ts +1 -100
- package/src/tools/subagent/tool.ts +5 -93
- package/src/tools/task-create/handler.ts +1 -1
- package/src/tools/task-get/handler.ts +1 -1
- package/src/tools/task-list/handler.ts +1 -1
- package/src/tools/task-update/handler.ts +1 -1
- package/src/tools/write-file/handler.ts +47 -0
- package/src/workflow.ts +88 -47
- package/tsup.config.ts +8 -1
- package/dist/adapters/langchain/index.cjs.map +0 -1
- package/dist/adapters/langchain/index.js.map +0 -1
- package/dist/model-invoker-y_zlyMqu.d.cts +0 -892
- package/dist/model-invoker-y_zlyMqu.d.ts +0 -892
- package/src/lib/tool-router.ts +0 -977
- package/src/lib/workflow-helpers.ts +0 -50
- /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.
|
|
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
|
+
}
|