noumen 0.4.0 → 0.6.0
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 +63 -8
- package/dist/a2a/index.d.ts +6 -4
- package/dist/acp/index.d.ts +7 -5
- package/dist/{agent-1nFVUP9E.d.ts → agent-DWE4_P5X.d.ts} +169 -485
- package/dist/{cache-DsRqxx6v.d.ts → cache-BlBwXXPS.d.ts} +1 -1
- package/dist/{chunk-4HW6LN6D.js → chunk-6MMYCGJQ.js} +366 -1227
- package/dist/chunk-6MMYCGJQ.js.map +1 -0
- package/dist/{chunk-5JN4SPI7.js → chunk-7IQCQI2G.js} +1 -1
- package/dist/{chunk-L3L3FG5T.js → chunk-CCM2AXZG.js} +1 -1
- package/dist/{chunk-L3L3FG5T.js.map → chunk-CCM2AXZG.js.map} +1 -1
- package/dist/{chunk-CS6WNDCF.js → chunk-I3JTUFPK.js} +2 -2
- package/dist/chunk-I3JTUFPK.js.map +1 -0
- package/dist/chunk-I5SBSOS6.js +40 -0
- package/dist/chunk-I5SBSOS6.js.map +1 -0
- package/dist/{chunk-HL6JCRZJ.js → chunk-XZN4QZLK.js} +4 -4
- package/dist/{chunk-EKOGVTBT.js → chunk-ZXSDKBYB.js} +4 -2
- package/dist/chunk-ZXSDKBYB.js.map +1 -0
- package/dist/cli/index.js +11 -11
- package/dist/client/index.d.ts +1 -1
- package/dist/computer-BPdxSo6X.d.ts +88 -0
- package/dist/docker.d.ts +129 -0
- package/dist/docker.js +401 -0
- package/dist/docker.js.map +1 -0
- package/dist/e2b.d.ts +157 -0
- package/dist/e2b.js +202 -0
- package/dist/e2b.js.map +1 -0
- package/dist/freestyle.d.ts +174 -0
- package/dist/freestyle.js +240 -0
- package/dist/freestyle.js.map +1 -0
- package/dist/index.d.ts +19 -204
- package/dist/index.js +38 -48
- package/dist/lsp/index.d.ts +4 -3
- package/dist/mcp/index.d.ts +5 -4
- package/dist/mcp/index.js +2 -2
- package/dist/{provider-factory-KCLIF34X.js → provider-factory-TUHU3DIG.js} +2 -2
- package/dist/providers/anthropic.d.ts +3 -3
- package/dist/providers/anthropic.js +4 -3
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/bedrock.d.ts +3 -3
- package/dist/providers/bedrock.js +2 -2
- package/dist/providers/bedrock.js.map +1 -1
- package/dist/providers/gemini.d.ts +2 -2
- package/dist/providers/gemini.js +1 -1
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/ollama.d.ts +1 -1
- package/dist/providers/ollama.js +2 -2
- package/dist/providers/openai.d.ts +2 -2
- package/dist/providers/openai.js +2 -2
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +2 -2
- package/dist/providers/vertex.d.ts +3 -3
- package/dist/providers/vertex.js +4 -3
- package/dist/providers/vertex.js.map +1 -1
- package/dist/{resolve-4JA2BBDA.js → resolve-6KUZNEYW.js} +2 -2
- package/dist/sandbox-9qeMTNrD.d.ts +126 -0
- package/dist/server/index.d.ts +6 -4
- package/dist/{server-CHMxuWKq.d.ts → server-BzNGKTP6.d.ts} +1 -1
- package/dist/sprites.d.ts +136 -0
- package/dist/sprites.js +334 -0
- package/dist/sprites.js.map +1 -0
- package/dist/ssh.d.ts +187 -0
- package/dist/ssh.js +392 -0
- package/dist/ssh.js.map +1 -0
- package/dist/{types-RPKUTu1k.d.ts → types-DhXwOQwD.d.ts} +3 -89
- package/dist/{types-LrU4LRmX.d.ts → types-kiGBF35b.d.ts} +40 -2
- package/package.json +25 -1
- package/dist/chunk-4HW6LN6D.js.map +0 -1
- package/dist/chunk-CS6WNDCF.js.map +0 -1
- package/dist/chunk-EKOGVTBT.js.map +0 -1
- /package/dist/{chunk-5JN4SPI7.js.map → chunk-7IQCQI2G.js.map} +0 -0
- /package/dist/{chunk-HL6JCRZJ.js.map → chunk-XZN4QZLK.js.map} +0 -0
- /package/dist/{provider-factory-KCLIF34X.js.map → provider-factory-TUHU3DIG.js.map} +0 -0
- /package/dist/{resolve-4JA2BBDA.js.map → resolve-6KUZNEYW.js.map} +0 -0
package/dist/e2b.js
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createComputerProxy,
|
|
3
|
+
createFsProxy
|
|
4
|
+
} from "./chunk-I5SBSOS6.js";
|
|
5
|
+
import "./chunk-DGUM43GV.js";
|
|
6
|
+
|
|
7
|
+
// src/virtual/e2b-fs.ts
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
var E2BFs = class {
|
|
10
|
+
sandbox;
|
|
11
|
+
workingDir;
|
|
12
|
+
constructor(opts) {
|
|
13
|
+
this.sandbox = opts.sandbox;
|
|
14
|
+
this.workingDir = opts.workingDir;
|
|
15
|
+
}
|
|
16
|
+
resolvePath(p) {
|
|
17
|
+
if (!this.workingDir) return p;
|
|
18
|
+
const normalizedBase = this.workingDir.endsWith("/") ? this.workingDir : this.workingDir + "/";
|
|
19
|
+
if (p.startsWith("/")) {
|
|
20
|
+
if (p !== this.workingDir && !p.startsWith(normalizedBase)) {
|
|
21
|
+
throw new Error(`Absolute path "${p}" is outside working directory "${this.workingDir}"`);
|
|
22
|
+
}
|
|
23
|
+
return p;
|
|
24
|
+
}
|
|
25
|
+
const resolved = path.resolve(this.workingDir, p);
|
|
26
|
+
if (resolved !== this.workingDir && !resolved.startsWith(normalizedBase)) {
|
|
27
|
+
throw new Error(`Path "${p}" escapes working directory "${this.workingDir}"`);
|
|
28
|
+
}
|
|
29
|
+
return resolved;
|
|
30
|
+
}
|
|
31
|
+
async readFile(path2, _opts) {
|
|
32
|
+
return this.sandbox.files.read(this.resolvePath(path2), {
|
|
33
|
+
format: "text"
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async readFileBytes(path2, maxBytes) {
|
|
37
|
+
const data = await this.sandbox.files.read(this.resolvePath(path2), {
|
|
38
|
+
format: "bytes"
|
|
39
|
+
});
|
|
40
|
+
const buf = Buffer.from(data);
|
|
41
|
+
if (maxBytes !== void 0 && buf.length > maxBytes) {
|
|
42
|
+
return buf.subarray(0, maxBytes);
|
|
43
|
+
}
|
|
44
|
+
return buf;
|
|
45
|
+
}
|
|
46
|
+
async writeFile(path2, content) {
|
|
47
|
+
await this.sandbox.files.write(this.resolvePath(path2), content);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* @warning Not atomic. Concurrent appends may lose data due to
|
|
51
|
+
* read-then-write TOCTOU. The E2B SDK does not expose an append primitive.
|
|
52
|
+
*/
|
|
53
|
+
async appendFile(path2, content) {
|
|
54
|
+
let existing = "";
|
|
55
|
+
try {
|
|
56
|
+
existing = await this.readFile(path2);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
await this.writeFile(path2, existing + content);
|
|
60
|
+
}
|
|
61
|
+
async deleteFile(path2, _opts) {
|
|
62
|
+
await this.sandbox.files.remove(this.resolvePath(path2));
|
|
63
|
+
}
|
|
64
|
+
async mkdir(path2, _opts) {
|
|
65
|
+
await this.sandbox.files.makeDir(this.resolvePath(path2));
|
|
66
|
+
}
|
|
67
|
+
async readdir(path2, _opts) {
|
|
68
|
+
const entries = await this.sandbox.files.list(this.resolvePath(path2));
|
|
69
|
+
return entries.map((entry) => ({
|
|
70
|
+
name: entry.name,
|
|
71
|
+
path: entry.path,
|
|
72
|
+
isDirectory: entry.type === "dir",
|
|
73
|
+
isFile: entry.type === "file",
|
|
74
|
+
size: entry.size
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
async exists(path2) {
|
|
78
|
+
return this.sandbox.files.exists(this.resolvePath(path2));
|
|
79
|
+
}
|
|
80
|
+
async stat(path2) {
|
|
81
|
+
const info = await this.sandbox.files.getInfo(this.resolvePath(path2));
|
|
82
|
+
return {
|
|
83
|
+
size: info.size ?? 0,
|
|
84
|
+
isDirectory: info.type === "dir",
|
|
85
|
+
isFile: info.type === "file",
|
|
86
|
+
modifiedAt: info.modifiedTime
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// src/virtual/e2b-computer.ts
|
|
92
|
+
var E2BComputer = class {
|
|
93
|
+
sandbox;
|
|
94
|
+
defaultCwd;
|
|
95
|
+
defaultTimeout;
|
|
96
|
+
constructor(opts) {
|
|
97
|
+
this.sandbox = opts.sandbox;
|
|
98
|
+
this.defaultCwd = opts.defaultCwd;
|
|
99
|
+
this.defaultTimeout = opts.defaultTimeout ?? 3e4;
|
|
100
|
+
}
|
|
101
|
+
async executeCommand(command, opts) {
|
|
102
|
+
const result = await this.sandbox.commands.run(command, {
|
|
103
|
+
cwd: opts?.cwd ?? this.defaultCwd,
|
|
104
|
+
timeout: opts?.timeout ?? this.defaultTimeout,
|
|
105
|
+
envs: opts?.env
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
exitCode: result.exitCode,
|
|
109
|
+
stdout: result.stdout ?? "",
|
|
110
|
+
stderr: result.stderr ?? ""
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// src/virtual/e2b-sandbox.ts
|
|
116
|
+
function E2BSandbox(opts) {
|
|
117
|
+
if (opts.sandbox) {
|
|
118
|
+
const s = opts.sandbox;
|
|
119
|
+
return {
|
|
120
|
+
fs: new E2BFs({ sandbox: s, workingDir: opts.cwd }),
|
|
121
|
+
computer: new E2BComputer({
|
|
122
|
+
sandbox: s,
|
|
123
|
+
defaultCwd: opts.cwd,
|
|
124
|
+
defaultTimeout: opts.defaultTimeout
|
|
125
|
+
}),
|
|
126
|
+
sandboxId: () => s.sandboxId
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const fsProxy = createFsProxy();
|
|
130
|
+
const computerProxy = createComputerProxy();
|
|
131
|
+
let resolvedId;
|
|
132
|
+
let sandboxRef;
|
|
133
|
+
let autoCreated = false;
|
|
134
|
+
let initPromise = null;
|
|
135
|
+
async function doInit(reconnectId) {
|
|
136
|
+
const e2b = await import("e2b");
|
|
137
|
+
const SandboxClass = e2b.Sandbox ?? e2b.default?.Sandbox;
|
|
138
|
+
if (!SandboxClass) {
|
|
139
|
+
throw new Error("Could not resolve Sandbox class from 'e2b' package");
|
|
140
|
+
}
|
|
141
|
+
let sandbox;
|
|
142
|
+
if (reconnectId) {
|
|
143
|
+
try {
|
|
144
|
+
sandbox = await SandboxClass.connect(reconnectId, {
|
|
145
|
+
apiKey: opts.apiKey
|
|
146
|
+
});
|
|
147
|
+
} catch {
|
|
148
|
+
sandbox = await SandboxClass.create({
|
|
149
|
+
template: opts.template ?? "base",
|
|
150
|
+
apiKey: opts.apiKey,
|
|
151
|
+
timeoutMs: opts.timeoutMs
|
|
152
|
+
});
|
|
153
|
+
autoCreated = true;
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
sandbox = await SandboxClass.create({
|
|
157
|
+
template: opts.template ?? "base",
|
|
158
|
+
apiKey: opts.apiKey,
|
|
159
|
+
timeoutMs: opts.timeoutMs
|
|
160
|
+
});
|
|
161
|
+
autoCreated = true;
|
|
162
|
+
}
|
|
163
|
+
sandboxRef = sandbox;
|
|
164
|
+
resolvedId = sandbox.sandboxId ?? reconnectId;
|
|
165
|
+
fsProxy.setTarget(new E2BFs({ sandbox, workingDir: opts.cwd }));
|
|
166
|
+
computerProxy.setTarget(new E2BComputer({
|
|
167
|
+
sandbox,
|
|
168
|
+
defaultCwd: opts.cwd,
|
|
169
|
+
defaultTimeout: opts.defaultTimeout
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
fs: fsProxy,
|
|
174
|
+
computer: computerProxy,
|
|
175
|
+
sandboxId: () => resolvedId,
|
|
176
|
+
init(sandboxId) {
|
|
177
|
+
if (!initPromise) {
|
|
178
|
+
initPromise = doInit(sandboxId).catch((err) => {
|
|
179
|
+
initPromise = null;
|
|
180
|
+
throw err;
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return initPromise;
|
|
184
|
+
},
|
|
185
|
+
async dispose() {
|
|
186
|
+
if (initPromise) {
|
|
187
|
+
await initPromise.catch(() => {
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (!autoCreated || !sandboxRef) return;
|
|
191
|
+
if (typeof sandboxRef.kill === "function") {
|
|
192
|
+
await sandboxRef.kill();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
export {
|
|
198
|
+
E2BComputer,
|
|
199
|
+
E2BFs,
|
|
200
|
+
E2BSandbox
|
|
201
|
+
};
|
|
202
|
+
//# sourceMappingURL=e2b.js.map
|
package/dist/e2b.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/virtual/e2b-fs.ts","../src/virtual/e2b-computer.ts","../src/virtual/e2b-sandbox.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport type { VirtualFs, FileEntry, FileStat, ReadOptions } from \"./fs.js\";\nimport type { E2BSandboxInstance } from \"./e2b-computer.js\";\n\nexport interface E2BFsOptions {\n /** An E2B Sandbox instance created via `Sandbox.create()`. */\n sandbox: E2BSandboxInstance;\n /** Working directory for relative path resolution. */\n workingDir?: string;\n}\n\n/**\n * VirtualFs backed by the E2B cloud sandbox filesystem.\n *\n * Requires `e2b` as an optional peer dependency.\n * The user is responsible for sandbox lifecycle (create, close).\n */\nexport class E2BFs implements VirtualFs {\n private sandbox: E2BSandboxInstance;\n private workingDir: string | undefined;\n\n constructor(opts: E2BFsOptions) {\n this.sandbox = opts.sandbox;\n this.workingDir = opts.workingDir;\n }\n\n private resolvePath(p: string): string {\n if (!this.workingDir) return p;\n const normalizedBase = this.workingDir.endsWith(\"/\") ? this.workingDir : this.workingDir + \"/\";\n if (p.startsWith(\"/\")) {\n if (p !== this.workingDir && !p.startsWith(normalizedBase)) {\n throw new Error(`Absolute path \"${p}\" is outside working directory \"${this.workingDir}\"`);\n }\n return p;\n }\n const resolved = path.resolve(this.workingDir, p);\n if (resolved !== this.workingDir && !resolved.startsWith(normalizedBase)) {\n throw new Error(`Path \"${p}\" escapes working directory \"${this.workingDir}\"`);\n }\n return resolved;\n }\n\n async readFile(path: string, _opts?: ReadOptions): Promise<string> {\n return this.sandbox.files.read(this.resolvePath(path), {\n format: \"text\",\n });\n }\n\n async readFileBytes(path: string, maxBytes?: number): Promise<Buffer> {\n const data = await this.sandbox.files.read(this.resolvePath(path), {\n format: \"bytes\",\n } as Record<string, unknown>);\n const buf = Buffer.from(data as unknown as ArrayBuffer);\n if (maxBytes !== undefined && buf.length > maxBytes) {\n return buf.subarray(0, maxBytes);\n }\n return buf;\n }\n\n async writeFile(path: string, content: string): Promise<void> {\n await this.sandbox.files.write(this.resolvePath(path), content);\n }\n\n /**\n * @warning Not atomic. Concurrent appends may lose data due to\n * read-then-write TOCTOU. The E2B SDK does not expose an append primitive.\n */\n async appendFile(path: string, content: string): Promise<void> {\n let existing = \"\";\n try {\n existing = await this.readFile(path);\n } catch {\n // file may not exist yet\n }\n await this.writeFile(path, existing + content);\n }\n\n async deleteFile(\n path: string,\n _opts?: { recursive?: boolean },\n ): Promise<void> {\n await this.sandbox.files.remove(this.resolvePath(path));\n }\n\n async mkdir(path: string, _opts?: { recursive?: boolean }): Promise<void> {\n await this.sandbox.files.makeDir(this.resolvePath(path));\n }\n\n async readdir(\n path: string,\n _opts?: { recursive?: boolean },\n ): Promise<FileEntry[]> {\n const entries = await this.sandbox.files.list(this.resolvePath(path));\n return entries.map((entry) => ({\n name: entry.name,\n path: entry.path,\n isDirectory: entry.type === \"dir\",\n isFile: entry.type === \"file\",\n size: entry.size,\n }));\n }\n\n async exists(path: string): Promise<boolean> {\n return this.sandbox.files.exists(this.resolvePath(path));\n }\n\n async stat(path: string): Promise<FileStat> {\n const info = await this.sandbox.files.getInfo(this.resolvePath(path));\n return {\n size: info.size ?? 0,\n isDirectory: info.type === \"dir\",\n isFile: info.type === \"file\",\n modifiedAt: info.modifiedTime,\n };\n }\n}\n","import type { VirtualComputer, ExecOptions, CommandResult } from \"./computer.js\";\n\n/**\n * Minimal subset of the E2B Sandbox interface used by E2BComputer and E2BFs.\n * Avoids a hard import of `e2b` at the module level.\n */\nexport interface E2BSandboxInstance {\n commands: {\n run(\n cmd: string,\n opts?: {\n cwd?: string;\n timeout?: number;\n envs?: Record<string, string>;\n },\n ): Promise<{\n exitCode: number;\n stdout: string;\n stderr: string;\n }>;\n };\n files: {\n read(path: string, opts?: { format?: string }): Promise<string>;\n write(path: string, data: string): Promise<unknown>;\n remove(path: string): Promise<void>;\n makeDir(path: string): Promise<unknown>;\n list(path: string): Promise<\n Array<{\n name: string;\n path: string;\n type?: string;\n size?: number;\n modifiedTime?: Date;\n }>\n >;\n exists(path: string): Promise<boolean>;\n getInfo(path: string): Promise<{\n name: string;\n path: string;\n type?: string;\n size?: number;\n modifiedTime?: Date;\n }>;\n };\n}\n\nexport interface E2BComputerOptions {\n /** An E2B Sandbox instance created via `Sandbox.create()`. */\n sandbox: E2BSandboxInstance;\n /** Default working directory for commands. */\n defaultCwd?: string;\n /** Default timeout in ms for commands (default: 30000). */\n defaultTimeout?: number;\n}\n\n/**\n * VirtualComputer backed by command execution in an E2B cloud sandbox.\n *\n * Requires `e2b` as an optional peer dependency.\n * The user is responsible for sandbox lifecycle (create, close).\n */\nexport class E2BComputer implements VirtualComputer {\n private sandbox: E2BSandboxInstance;\n private defaultCwd: string | undefined;\n private defaultTimeout: number;\n\n constructor(opts: E2BComputerOptions) {\n this.sandbox = opts.sandbox;\n this.defaultCwd = opts.defaultCwd;\n this.defaultTimeout = opts.defaultTimeout ?? 30_000;\n }\n\n async executeCommand(\n command: string,\n opts?: ExecOptions,\n ): Promise<CommandResult> {\n const result = await this.sandbox.commands.run(command, {\n cwd: opts?.cwd ?? this.defaultCwd,\n timeout: opts?.timeout ?? this.defaultTimeout,\n envs: opts?.env,\n });\n\n return {\n exitCode: result.exitCode,\n stdout: result.stdout ?? \"\",\n stderr: result.stderr ?? \"\",\n };\n }\n}\n","import type { Sandbox } from \"./sandbox.js\";\nimport { E2BFs } from \"./e2b-fs.js\";\nimport { E2BComputer, type E2BSandboxInstance } from \"./e2b-computer.js\";\nimport { createFsProxy, createComputerProxy } from \"./proxy.js\";\n\nexport interface E2BSandboxOptions {\n /**\n * A pre-existing E2B Sandbox instance (e.g. from `Sandbox.create()`).\n * When provided the sandbox attaches to this instance — no auto-creation\n * occurs and `dispose()` will **not** kill it.\n *\n * When omitted, a new E2B sandbox is created on the first `init()` call\n * via a dynamic import of the `e2b` package. The auto-created sandbox\n * is killed when `dispose()` is called.\n */\n sandbox?: E2BSandboxInstance;\n /**\n * E2B template to use for auto-creation (default: `\"base\"`).\n * Only used when `sandbox` is omitted.\n */\n template?: string;\n /**\n * E2B API key. Falls back to the `E2B_API_KEY` environment variable\n * when omitted. Only used during auto-creation.\n */\n apiKey?: string;\n /** Timeout (ms) for the auto-created E2B sandbox (default: E2B SDK default). */\n timeoutMs?: number;\n /** Working directory inside the sandbox. */\n cwd?: string;\n /** Default timeout (ms) for shell commands. */\n defaultTimeout?: number;\n}\n\n/**\n * Create a `Sandbox` backed by an E2B cloud sandbox.\n * Requires `e2b` as an optional peer dependency.\n *\n * **Auto-creation:** When `sandbox` is omitted the E2B sandbox is\n * provisioned lazily on the first `init()` call via the E2B SDK.\n * The sandbox ID is available through `sandboxId()` for session\n * persistence. Pass the stored ID back through `init(storedId)` to\n * reconnect to the same sandbox on resume (via `Sandbox.connect()`).\n *\n * **Explicit instance:** When `sandbox` is provided, `init()` binds\n * it immediately. `dispose()` is a no-op — the caller owns the\n * sandbox's lifecycle.\n *\n * @example\n * ```ts\n * // Auto-create — sandbox provisioned on first init()\n * const sandbox = E2BSandbox({ template: \"base\" });\n *\n * // Explicit — attach to pre-existing instance\n * const sandbox = E2BSandbox({ sandbox: await E2BSdk.Sandbox.create() });\n * ```\n */\nexport function E2BSandbox(opts: E2BSandboxOptions): Sandbox {\n if (opts.sandbox) {\n const s = opts.sandbox;\n return {\n fs: new E2BFs({ sandbox: s, workingDir: opts.cwd }),\n computer: new E2BComputer({\n sandbox: s,\n defaultCwd: opts.cwd,\n defaultTimeout: opts.defaultTimeout,\n }),\n sandboxId: () => (s as any).sandboxId as string | undefined,\n };\n }\n\n const fsProxy = createFsProxy();\n const computerProxy = createComputerProxy();\n let resolvedId: string | undefined;\n let sandboxRef: any;\n let autoCreated = false;\n let initPromise: Promise<void> | null = null;\n\n async function doInit(reconnectId?: string): Promise<void> {\n const e2b = await import(\"e2b\");\n const SandboxClass = (e2b as any).Sandbox ?? (e2b as any).default?.Sandbox;\n if (!SandboxClass) {\n throw new Error(\"Could not resolve Sandbox class from 'e2b' package\");\n }\n\n let sandbox: E2BSandboxInstance;\n if (reconnectId) {\n try {\n sandbox = await SandboxClass.connect(reconnectId, {\n apiKey: opts.apiKey,\n });\n } catch {\n sandbox = await SandboxClass.create({\n template: opts.template ?? \"base\",\n apiKey: opts.apiKey,\n timeoutMs: opts.timeoutMs,\n });\n autoCreated = true;\n }\n } else {\n sandbox = await SandboxClass.create({\n template: opts.template ?? \"base\",\n apiKey: opts.apiKey,\n timeoutMs: opts.timeoutMs,\n });\n autoCreated = true;\n }\n\n sandboxRef = sandbox;\n resolvedId = (sandbox as any).sandboxId ?? reconnectId;\n fsProxy.setTarget(new E2BFs({ sandbox, workingDir: opts.cwd }));\n computerProxy.setTarget(new E2BComputer({\n sandbox,\n defaultCwd: opts.cwd,\n defaultTimeout: opts.defaultTimeout,\n }));\n }\n\n return {\n fs: fsProxy,\n computer: computerProxy,\n sandboxId: () => resolvedId,\n\n init(sandboxId?: string): Promise<void> {\n if (!initPromise) {\n initPromise = doInit(sandboxId).catch((err) => {\n initPromise = null;\n throw err;\n });\n }\n return initPromise;\n },\n\n async dispose(): Promise<void> {\n if (initPromise) {\n await initPromise.catch(() => {});\n }\n if (!autoCreated || !sandboxRef) return;\n if (typeof sandboxRef.kill === \"function\") {\n await sandboxRef.kill();\n }\n },\n };\n}\n"],"mappings":";;;;;;;AAAA,YAAY,UAAU;AAiBf,IAAM,QAAN,MAAiC;AAAA,EAC9B;AAAA,EACA;AAAA,EAER,YAAY,MAAoB;AAC9B,SAAK,UAAU,KAAK;AACpB,SAAK,aAAa,KAAK;AAAA,EACzB;AAAA,EAEQ,YAAY,GAAmB;AACrC,QAAI,CAAC,KAAK,WAAY,QAAO;AAC7B,UAAM,iBAAiB,KAAK,WAAW,SAAS,GAAG,IAAI,KAAK,aAAa,KAAK,aAAa;AAC3F,QAAI,EAAE,WAAW,GAAG,GAAG;AACrB,UAAI,MAAM,KAAK,cAAc,CAAC,EAAE,WAAW,cAAc,GAAG;AAC1D,cAAM,IAAI,MAAM,kBAAkB,CAAC,mCAAmC,KAAK,UAAU,GAAG;AAAA,MAC1F;AACA,aAAO;AAAA,IACT;AACA,UAAM,WAAgB,aAAQ,KAAK,YAAY,CAAC;AAChD,QAAI,aAAa,KAAK,cAAc,CAAC,SAAS,WAAW,cAAc,GAAG;AACxE,YAAM,IAAI,MAAM,SAAS,CAAC,gCAAgC,KAAK,UAAU,GAAG;AAAA,IAC9E;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAASA,OAAc,OAAsC;AACjE,WAAO,KAAK,QAAQ,MAAM,KAAK,KAAK,YAAYA,KAAI,GAAG;AAAA,MACrD,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,cAAcA,OAAc,UAAoC;AACpE,UAAM,OAAO,MAAM,KAAK,QAAQ,MAAM,KAAK,KAAK,YAAYA,KAAI,GAAG;AAAA,MACjE,QAAQ;AAAA,IACV,CAA4B;AAC5B,UAAM,MAAM,OAAO,KAAK,IAA8B;AACtD,QAAI,aAAa,UAAa,IAAI,SAAS,UAAU;AACnD,aAAO,IAAI,SAAS,GAAG,QAAQ;AAAA,IACjC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,UAAUA,OAAc,SAAgC;AAC5D,UAAM,KAAK,QAAQ,MAAM,MAAM,KAAK,YAAYA,KAAI,GAAG,OAAO;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAAWA,OAAc,SAAgC;AAC7D,QAAI,WAAW;AACf,QAAI;AACF,iBAAW,MAAM,KAAK,SAASA,KAAI;AAAA,IACrC,QAAQ;AAAA,IAER;AACA,UAAM,KAAK,UAAUA,OAAM,WAAW,OAAO;AAAA,EAC/C;AAAA,EAEA,MAAM,WACJA,OACA,OACe;AACf,UAAM,KAAK,QAAQ,MAAM,OAAO,KAAK,YAAYA,KAAI,CAAC;AAAA,EACxD;AAAA,EAEA,MAAM,MAAMA,OAAc,OAAgD;AACxE,UAAM,KAAK,QAAQ,MAAM,QAAQ,KAAK,YAAYA,KAAI,CAAC;AAAA,EACzD;AAAA,EAEA,MAAM,QACJA,OACA,OACsB;AACtB,UAAM,UAAU,MAAM,KAAK,QAAQ,MAAM,KAAK,KAAK,YAAYA,KAAI,CAAC;AACpE,WAAO,QAAQ,IAAI,CAAC,WAAW;AAAA,MAC7B,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,aAAa,MAAM,SAAS;AAAA,MAC5B,QAAQ,MAAM,SAAS;AAAA,MACvB,MAAM,MAAM;AAAA,IACd,EAAE;AAAA,EACJ;AAAA,EAEA,MAAM,OAAOA,OAAgC;AAC3C,WAAO,KAAK,QAAQ,MAAM,OAAO,KAAK,YAAYA,KAAI,CAAC;AAAA,EACzD;AAAA,EAEA,MAAM,KAAKA,OAAiC;AAC1C,UAAM,OAAO,MAAM,KAAK,QAAQ,MAAM,QAAQ,KAAK,YAAYA,KAAI,CAAC;AACpE,WAAO;AAAA,MACL,MAAM,KAAK,QAAQ;AAAA,MACnB,aAAa,KAAK,SAAS;AAAA,MAC3B,QAAQ,KAAK,SAAS;AAAA,MACtB,YAAY,KAAK;AAAA,IACnB;AAAA,EACF;AACF;;;ACtDO,IAAM,cAAN,MAA6C;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,MAA0B;AACpC,SAAK,UAAU,KAAK;AACpB,SAAK,aAAa,KAAK;AACvB,SAAK,iBAAiB,KAAK,kBAAkB;AAAA,EAC/C;AAAA,EAEA,MAAM,eACJ,SACA,MACwB;AACxB,UAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,IAAI,SAAS;AAAA,MACtD,KAAK,MAAM,OAAO,KAAK;AAAA,MACvB,SAAS,MAAM,WAAW,KAAK;AAAA,MAC/B,MAAM,MAAM;AAAA,IACd,CAAC;AAED,WAAO;AAAA,MACL,UAAU,OAAO;AAAA,MACjB,QAAQ,OAAO,UAAU;AAAA,MACzB,QAAQ,OAAO,UAAU;AAAA,IAC3B;AAAA,EACF;AACF;;;AC/BO,SAAS,WAAW,MAAkC;AAC3D,MAAI,KAAK,SAAS;AAChB,UAAM,IAAI,KAAK;AACf,WAAO;AAAA,MACL,IAAI,IAAI,MAAM,EAAE,SAAS,GAAG,YAAY,KAAK,IAAI,CAAC;AAAA,MAClD,UAAU,IAAI,YAAY;AAAA,QACxB,SAAS;AAAA,QACT,YAAY,KAAK;AAAA,QACjB,gBAAgB,KAAK;AAAA,MACvB,CAAC;AAAA,MACD,WAAW,MAAO,EAAU;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,UAAU,cAAc;AAC9B,QAAM,gBAAgB,oBAAoB;AAC1C,MAAI;AACJ,MAAI;AACJ,MAAI,cAAc;AAClB,MAAI,cAAoC;AAExC,iBAAe,OAAO,aAAqC;AACzD,UAAM,MAAM,MAAM,OAAO,KAAK;AAC9B,UAAM,eAAgB,IAAY,WAAY,IAAY,SAAS;AACnE,QAAI,CAAC,cAAc;AACjB,YAAM,IAAI,MAAM,oDAAoD;AAAA,IACtE;AAEA,QAAI;AACJ,QAAI,aAAa;AACf,UAAI;AACF,kBAAU,MAAM,aAAa,QAAQ,aAAa;AAAA,UAChD,QAAQ,KAAK;AAAA,QACf,CAAC;AAAA,MACH,QAAQ;AACN,kBAAU,MAAM,aAAa,OAAO;AAAA,UAClC,UAAU,KAAK,YAAY;AAAA,UAC3B,QAAQ,KAAK;AAAA,UACb,WAAW,KAAK;AAAA,QAClB,CAAC;AACD,sBAAc;AAAA,MAChB;AAAA,IACF,OAAO;AACL,gBAAU,MAAM,aAAa,OAAO;AAAA,QAClC,UAAU,KAAK,YAAY;AAAA,QAC3B,QAAQ,KAAK;AAAA,QACb,WAAW,KAAK;AAAA,MAClB,CAAC;AACD,oBAAc;AAAA,IAChB;AAEA,iBAAa;AACb,iBAAc,QAAgB,aAAa;AAC3C,YAAQ,UAAU,IAAI,MAAM,EAAE,SAAS,YAAY,KAAK,IAAI,CAAC,CAAC;AAC9D,kBAAc,UAAU,IAAI,YAAY;AAAA,MACtC;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,gBAAgB,KAAK;AAAA,IACvB,CAAC,CAAC;AAAA,EACJ;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,WAAW,MAAM;AAAA,IAEjB,KAAK,WAAmC;AACtC,UAAI,CAAC,aAAa;AAChB,sBAAc,OAAO,SAAS,EAAE,MAAM,CAAC,QAAQ;AAC7C,wBAAc;AACd,gBAAM;AAAA,QACR,CAAC;AAAA,MACH;AACA,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,UAAyB;AAC7B,UAAI,aAAa;AACf,cAAM,YAAY,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAClC;AACA,UAAI,CAAC,eAAe,CAAC,WAAY;AACjC,UAAI,OAAO,WAAW,SAAS,YAAY;AACzC,cAAM,WAAW,KAAK;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AACF;","names":["path"]}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { S as Sandbox } from './sandbox-9qeMTNrD.js';
|
|
2
|
+
import { V as VirtualComputer, E as ExecOptions, C as CommandResult, a as VirtualFs, R as ReadOptions, F as FileEntry, b as FileStat } from './computer-BPdxSo6X.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Minimal subset of the Freestyle VM interface used by FreestyleComputer and
|
|
6
|
+
* FreestyleFs. Avoids a hard import of `freestyle-sandboxes` at the module
|
|
7
|
+
* level — the real SDK is only loaded dynamically during `FreestyleSandbox`
|
|
8
|
+
* auto-creation.
|
|
9
|
+
*/
|
|
10
|
+
interface FreestyleVmInstance {
|
|
11
|
+
exec(command: string, opts?: {
|
|
12
|
+
cwd?: string;
|
|
13
|
+
timeout?: number;
|
|
14
|
+
}): Promise<{
|
|
15
|
+
stdout: string | null;
|
|
16
|
+
stderr: string | null;
|
|
17
|
+
statusCode: number | null;
|
|
18
|
+
}>;
|
|
19
|
+
fs: {
|
|
20
|
+
readTextFile(path: string): Promise<string>;
|
|
21
|
+
writeTextFile(path: string, content: string): Promise<void>;
|
|
22
|
+
readDir(path: string): Promise<Array<{
|
|
23
|
+
name: string;
|
|
24
|
+
kind: string;
|
|
25
|
+
}>>;
|
|
26
|
+
};
|
|
27
|
+
suspend(): Promise<unknown>;
|
|
28
|
+
start(): Promise<unknown>;
|
|
29
|
+
}
|
|
30
|
+
interface FreestyleComputerOptions {
|
|
31
|
+
/** A Freestyle VM instance. */
|
|
32
|
+
vm: FreestyleVmInstance;
|
|
33
|
+
/** Default working directory for commands. */
|
|
34
|
+
defaultCwd?: string;
|
|
35
|
+
/** Default timeout in ms for commands (default: 30000). */
|
|
36
|
+
defaultTimeout?: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* VirtualComputer backed by command execution in a Freestyle VM.
|
|
40
|
+
*
|
|
41
|
+
* Requires `freestyle-sandboxes` as an optional peer dependency.
|
|
42
|
+
* The user is responsible for VM lifecycle when using explicit mode.
|
|
43
|
+
*/
|
|
44
|
+
declare class FreestyleComputer implements VirtualComputer {
|
|
45
|
+
private vm;
|
|
46
|
+
private defaultCwd;
|
|
47
|
+
private defaultTimeout;
|
|
48
|
+
constructor(opts: FreestyleComputerOptions);
|
|
49
|
+
executeCommand(command: string, opts?: ExecOptions): Promise<CommandResult>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface FreestyleSandboxOptions {
|
|
53
|
+
/**
|
|
54
|
+
* A pre-existing Freestyle VM instance. When provided the sandbox
|
|
55
|
+
* attaches to this VM directly — no auto-creation occurs and
|
|
56
|
+
* `dispose()` will **not** suspend or delete it.
|
|
57
|
+
*
|
|
58
|
+
* When omitted, a new VM is created on the first `init()` call via
|
|
59
|
+
* a dynamic import of the `freestyle-sandboxes` package.
|
|
60
|
+
*/
|
|
61
|
+
vm?: FreestyleVmInstance;
|
|
62
|
+
/**
|
|
63
|
+
* Freestyle API key. Falls back to the `FREESTYLE_API_KEY` environment
|
|
64
|
+
* variable when omitted. Only used during auto-creation.
|
|
65
|
+
*/
|
|
66
|
+
apiKey?: string;
|
|
67
|
+
/** Snapshot ID to create the VM from. Only used during auto-creation. */
|
|
68
|
+
snapshotId?: string;
|
|
69
|
+
/**
|
|
70
|
+
* A `VmSpec` instance or configuration object passed through to
|
|
71
|
+
* `freestyle.vms.create()`. Only used during auto-creation.
|
|
72
|
+
*/
|
|
73
|
+
spec?: unknown;
|
|
74
|
+
/**
|
|
75
|
+
* Idle timeout in seconds for the auto-created VM (default: 600).
|
|
76
|
+
* The VM auto-suspends after this many seconds of network inactivity.
|
|
77
|
+
*/
|
|
78
|
+
idleTimeoutSeconds?: number;
|
|
79
|
+
/** Working directory inside the VM. */
|
|
80
|
+
cwd?: string;
|
|
81
|
+
/** Default timeout (ms) for shell commands. */
|
|
82
|
+
defaultTimeout?: number;
|
|
83
|
+
/** Files to provision at creation time. */
|
|
84
|
+
additionalFiles?: Record<string, {
|
|
85
|
+
content: string;
|
|
86
|
+
encoding?: string;
|
|
87
|
+
}>;
|
|
88
|
+
/** Git repos to clone at creation time. */
|
|
89
|
+
gitRepos?: Array<{
|
|
90
|
+
repo: string;
|
|
91
|
+
path: string;
|
|
92
|
+
rev?: string;
|
|
93
|
+
}>;
|
|
94
|
+
/**
|
|
95
|
+
* What to do with auto-created VMs on `dispose()`:
|
|
96
|
+
* - `"suspend"` (default) — suspends the VM, preserving full memory
|
|
97
|
+
* state for near-instant resume on reconnect.
|
|
98
|
+
* - `"delete"` — permanently deletes the VM and frees all resources.
|
|
99
|
+
*/
|
|
100
|
+
disposeStrategy?: "suspend" | "delete";
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Create a `Sandbox` backed by a Freestyle VM.
|
|
104
|
+
* Requires `freestyle-sandboxes` as an optional peer dependency.
|
|
105
|
+
*
|
|
106
|
+
* **Auto-creation:** When `vm` is omitted a new Freestyle VM is
|
|
107
|
+
* provisioned lazily on the first `init()` call. The VM ID is available
|
|
108
|
+
* through `sandboxId()` for session persistence. Pass the stored ID back
|
|
109
|
+
* through `init(storedId)` to reconnect (this also wakes suspended VMs).
|
|
110
|
+
*
|
|
111
|
+
* By default, auto-created VMs are **suspended** on `dispose()` rather
|
|
112
|
+
* than deleted. This preserves full memory state and allows near-instant
|
|
113
|
+
* resume. Set `disposeStrategy: "delete"` for full cleanup.
|
|
114
|
+
*
|
|
115
|
+
* **Explicit instance:** When `vm` is provided, `init()` binds it
|
|
116
|
+
* immediately. `dispose()` is a no-op — the caller owns the VM's
|
|
117
|
+
* lifecycle.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* // Auto-create — VM provisioned on first init()
|
|
122
|
+
* const sandbox = FreestyleSandbox({ cwd: "/workspace" });
|
|
123
|
+
*
|
|
124
|
+
* // Auto-create from a snapshot
|
|
125
|
+
* const sandbox = FreestyleSandbox({
|
|
126
|
+
* snapshotId: "abc123",
|
|
127
|
+
* cwd: "/workspace",
|
|
128
|
+
* });
|
|
129
|
+
*
|
|
130
|
+
* // Explicit — attach to pre-existing VM
|
|
131
|
+
* const sandbox = FreestyleSandbox({ vm: existingVm });
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
declare function FreestyleSandbox(opts: FreestyleSandboxOptions): Sandbox;
|
|
135
|
+
|
|
136
|
+
interface FreestyleFsOptions {
|
|
137
|
+
/** A Freestyle VM instance. */
|
|
138
|
+
vm: FreestyleVmInstance;
|
|
139
|
+
/** Working directory for relative path resolution. */
|
|
140
|
+
workingDir?: string;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* VirtualFs backed by a Freestyle VM.
|
|
144
|
+
*
|
|
145
|
+
* Uses `vm.fs.*` for operations with native SDK support (readTextFile,
|
|
146
|
+
* writeTextFile, readDir) and falls back to `vm.exec()` for the rest
|
|
147
|
+
* (stat, exists, mkdir, deleteFile, appendFile, readFileBytes).
|
|
148
|
+
*
|
|
149
|
+
* Requires `freestyle-sandboxes` as an optional peer dependency.
|
|
150
|
+
* The user is responsible for VM lifecycle when using explicit mode.
|
|
151
|
+
*/
|
|
152
|
+
declare class FreestyleFs implements VirtualFs {
|
|
153
|
+
private vm;
|
|
154
|
+
private workingDir;
|
|
155
|
+
constructor(opts: FreestyleFsOptions);
|
|
156
|
+
private resolvePath;
|
|
157
|
+
readFile(filePath: string, _opts?: ReadOptions): Promise<string>;
|
|
158
|
+
readFileBytes(filePath: string, maxBytes?: number): Promise<Buffer>;
|
|
159
|
+
writeFile(filePath: string, content: string): Promise<void>;
|
|
160
|
+
appendFile(filePath: string, content: string): Promise<void>;
|
|
161
|
+
deleteFile(filePath: string, opts?: {
|
|
162
|
+
recursive?: boolean;
|
|
163
|
+
}): Promise<void>;
|
|
164
|
+
mkdir(filePath: string, opts?: {
|
|
165
|
+
recursive?: boolean;
|
|
166
|
+
}): Promise<void>;
|
|
167
|
+
readdir(dirPath: string, _opts?: {
|
|
168
|
+
recursive?: boolean;
|
|
169
|
+
}): Promise<FileEntry[]>;
|
|
170
|
+
exists(filePath: string): Promise<boolean>;
|
|
171
|
+
stat(filePath: string): Promise<FileStat>;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export { FreestyleComputer, type FreestyleComputerOptions, FreestyleFs, type FreestyleFsOptions, FreestyleSandbox, type FreestyleSandboxOptions, type FreestyleVmInstance };
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createComputerProxy,
|
|
3
|
+
createFsProxy
|
|
4
|
+
} from "./chunk-I5SBSOS6.js";
|
|
5
|
+
import "./chunk-DGUM43GV.js";
|
|
6
|
+
|
|
7
|
+
// src/virtual/freestyle-fs.ts
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
var FreestyleFs = class {
|
|
10
|
+
vm;
|
|
11
|
+
workingDir;
|
|
12
|
+
constructor(opts) {
|
|
13
|
+
this.vm = opts.vm;
|
|
14
|
+
this.workingDir = opts.workingDir;
|
|
15
|
+
}
|
|
16
|
+
resolvePath(p) {
|
|
17
|
+
if (p.includes("\0")) {
|
|
18
|
+
throw new Error("Path contains null bytes");
|
|
19
|
+
}
|
|
20
|
+
if (!this.workingDir) return p;
|
|
21
|
+
const normalizedBase = this.workingDir.endsWith("/") ? this.workingDir : this.workingDir + "/";
|
|
22
|
+
if (p.startsWith("/")) {
|
|
23
|
+
const normalized = path.normalize(p);
|
|
24
|
+
if (normalized !== this.workingDir && !normalized.startsWith(normalizedBase)) {
|
|
25
|
+
throw new Error(`Absolute path "${p}" is outside working directory "${this.workingDir}"`);
|
|
26
|
+
}
|
|
27
|
+
return normalized;
|
|
28
|
+
}
|
|
29
|
+
const resolved = path.resolve(this.workingDir, p);
|
|
30
|
+
if (resolved !== this.workingDir && !resolved.startsWith(normalizedBase)) {
|
|
31
|
+
throw new Error(`Path "${p}" escapes working directory "${this.workingDir}"`);
|
|
32
|
+
}
|
|
33
|
+
return resolved;
|
|
34
|
+
}
|
|
35
|
+
async readFile(filePath, _opts) {
|
|
36
|
+
return this.vm.fs.readTextFile(this.resolvePath(filePath));
|
|
37
|
+
}
|
|
38
|
+
async readFileBytes(filePath, maxBytes) {
|
|
39
|
+
const resolved = this.resolvePath(filePath);
|
|
40
|
+
const cmd = maxBytes !== void 0 ? `head -c ${maxBytes} ${shellEscape(resolved)} | base64` : `base64 ${shellEscape(resolved)}`;
|
|
41
|
+
const { statusCode, stdout, stderr } = await this.vm.exec(cmd);
|
|
42
|
+
if (statusCode !== 0) {
|
|
43
|
+
throw new Error(`FreestyleFs readFileBytes failed: ${stderr?.trim() || `exit code ${statusCode}`}`);
|
|
44
|
+
}
|
|
45
|
+
return Buffer.from((stdout ?? "").trim(), "base64");
|
|
46
|
+
}
|
|
47
|
+
async writeFile(filePath, content) {
|
|
48
|
+
await this.vm.fs.writeTextFile(this.resolvePath(filePath), content);
|
|
49
|
+
}
|
|
50
|
+
async appendFile(filePath, content) {
|
|
51
|
+
const resolved = this.resolvePath(filePath);
|
|
52
|
+
const encoded = Buffer.from(content, "utf-8").toString("base64");
|
|
53
|
+
const { statusCode, stderr } = await this.vm.exec(
|
|
54
|
+
`echo ${shellEscape(encoded)} | base64 -d >> ${shellEscape(resolved)}`
|
|
55
|
+
);
|
|
56
|
+
if (statusCode !== 0) {
|
|
57
|
+
throw new Error(`FreestyleFs appendFile failed: ${stderr?.trim() || `exit code ${statusCode}`}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async deleteFile(filePath, opts) {
|
|
61
|
+
const resolved = this.resolvePath(filePath);
|
|
62
|
+
const flag = opts?.recursive ? "-rf" : "-f";
|
|
63
|
+
await this.vm.exec(`rm ${flag} ${shellEscape(resolved)}`);
|
|
64
|
+
}
|
|
65
|
+
async mkdir(filePath, opts) {
|
|
66
|
+
const resolved = this.resolvePath(filePath);
|
|
67
|
+
const flag = opts?.recursive ? "-p " : "";
|
|
68
|
+
await this.vm.exec(`mkdir ${flag}${shellEscape(resolved)}`);
|
|
69
|
+
}
|
|
70
|
+
async readdir(dirPath, _opts) {
|
|
71
|
+
const resolved = this.resolvePath(dirPath);
|
|
72
|
+
const items = await this.vm.fs.readDir(resolved);
|
|
73
|
+
return items.map((entry) => ({
|
|
74
|
+
name: entry.name,
|
|
75
|
+
path: resolved === "/" ? `/${entry.name}` : `${resolved}/${entry.name}`,
|
|
76
|
+
isDirectory: entry.kind === "dir" || entry.kind === "directory",
|
|
77
|
+
isFile: entry.kind === "file"
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
async exists(filePath) {
|
|
81
|
+
const resolved = this.resolvePath(filePath);
|
|
82
|
+
const { statusCode } = await this.vm.exec(`test -e ${shellEscape(resolved)}`);
|
|
83
|
+
return statusCode === 0;
|
|
84
|
+
}
|
|
85
|
+
async stat(filePath) {
|
|
86
|
+
const resolved = this.resolvePath(filePath);
|
|
87
|
+
const { statusCode, stdout, stderr } = await this.vm.exec(
|
|
88
|
+
`stat -c '%s %F %W %Y' ${shellEscape(resolved)}`
|
|
89
|
+
);
|
|
90
|
+
if (statusCode !== 0) {
|
|
91
|
+
throw new Error(`FreestyleFs stat failed: ${stderr?.trim() || `exit code ${statusCode}`}`);
|
|
92
|
+
}
|
|
93
|
+
const parts = (stdout ?? "").trim().split(" ");
|
|
94
|
+
const size = parseInt(parts[0], 10);
|
|
95
|
+
const fileType = parts[1];
|
|
96
|
+
const createdEpoch = parseInt(parts[2], 10);
|
|
97
|
+
const modifiedEpoch = parseInt(parts[3], 10);
|
|
98
|
+
return {
|
|
99
|
+
size,
|
|
100
|
+
isDirectory: fileType === "directory",
|
|
101
|
+
isFile: fileType.startsWith("regular"),
|
|
102
|
+
createdAt: createdEpoch > 0 ? new Date(createdEpoch * 1e3) : void 0,
|
|
103
|
+
modifiedAt: modifiedEpoch > 0 ? new Date(modifiedEpoch * 1e3) : void 0
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
function shellEscape(s) {
|
|
108
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/virtual/freestyle-computer.ts
|
|
112
|
+
var FreestyleComputer = class {
|
|
113
|
+
vm;
|
|
114
|
+
defaultCwd;
|
|
115
|
+
defaultTimeout;
|
|
116
|
+
constructor(opts) {
|
|
117
|
+
this.vm = opts.vm;
|
|
118
|
+
this.defaultCwd = opts.defaultCwd;
|
|
119
|
+
this.defaultTimeout = opts.defaultTimeout ?? 3e4;
|
|
120
|
+
}
|
|
121
|
+
async executeCommand(command, opts) {
|
|
122
|
+
const result = await this.vm.exec(command, {
|
|
123
|
+
cwd: opts?.cwd ?? this.defaultCwd,
|
|
124
|
+
timeout: opts?.timeout ?? this.defaultTimeout
|
|
125
|
+
});
|
|
126
|
+
return {
|
|
127
|
+
exitCode: result.statusCode ?? 1,
|
|
128
|
+
stdout: result.stdout ?? "",
|
|
129
|
+
stderr: result.stderr ?? ""
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// src/virtual/freestyle-sandbox.ts
|
|
135
|
+
function FreestyleSandbox(opts) {
|
|
136
|
+
if (opts.vm) {
|
|
137
|
+
const v = opts.vm;
|
|
138
|
+
return {
|
|
139
|
+
fs: new FreestyleFs({ vm: v, workingDir: opts.cwd }),
|
|
140
|
+
computer: new FreestyleComputer({
|
|
141
|
+
vm: v,
|
|
142
|
+
defaultCwd: opts.cwd,
|
|
143
|
+
defaultTimeout: opts.defaultTimeout
|
|
144
|
+
}),
|
|
145
|
+
sandboxId: () => v.vmId
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const fsProxy = createFsProxy();
|
|
149
|
+
const computerProxy = createComputerProxy();
|
|
150
|
+
let resolvedId;
|
|
151
|
+
let vmRef = null;
|
|
152
|
+
let autoCreated = false;
|
|
153
|
+
let initPromise = null;
|
|
154
|
+
async function doInit(reconnectId) {
|
|
155
|
+
const mod = await import("freestyle-sandboxes");
|
|
156
|
+
const freestyle = mod.freestyle ?? mod.default?.freestyle;
|
|
157
|
+
if (!freestyle?.vms) {
|
|
158
|
+
throw new Error("Could not resolve freestyle client from 'freestyle-sandboxes' package");
|
|
159
|
+
}
|
|
160
|
+
let vm;
|
|
161
|
+
if (reconnectId) {
|
|
162
|
+
try {
|
|
163
|
+
const result = await freestyle.vms.get({ vmId: reconnectId });
|
|
164
|
+
vm = result.vm;
|
|
165
|
+
resolvedId = reconnectId;
|
|
166
|
+
} catch {
|
|
167
|
+
const result = await freestyle.vms.create({
|
|
168
|
+
...opts.spec ? { spec: opts.spec } : {},
|
|
169
|
+
snapshotId: opts.snapshotId,
|
|
170
|
+
workdir: opts.cwd,
|
|
171
|
+
idleTimeoutSeconds: opts.idleTimeoutSeconds ?? 600,
|
|
172
|
+
additionalFiles: opts.additionalFiles,
|
|
173
|
+
gitRepos: opts.gitRepos
|
|
174
|
+
});
|
|
175
|
+
vm = result.vm;
|
|
176
|
+
resolvedId = result.vmId ?? result.id;
|
|
177
|
+
autoCreated = true;
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
const result = await freestyle.vms.create({
|
|
181
|
+
...opts.spec ? { spec: opts.spec } : {},
|
|
182
|
+
snapshotId: opts.snapshotId,
|
|
183
|
+
workdir: opts.cwd,
|
|
184
|
+
idleTimeoutSeconds: opts.idleTimeoutSeconds ?? 600,
|
|
185
|
+
additionalFiles: opts.additionalFiles,
|
|
186
|
+
gitRepos: opts.gitRepos
|
|
187
|
+
});
|
|
188
|
+
vm = result.vm;
|
|
189
|
+
resolvedId = result.vmId ?? result.id;
|
|
190
|
+
autoCreated = true;
|
|
191
|
+
}
|
|
192
|
+
vmRef = vm;
|
|
193
|
+
fsProxy.setTarget(new FreestyleFs({ vm, workingDir: opts.cwd }));
|
|
194
|
+
computerProxy.setTarget(new FreestyleComputer({
|
|
195
|
+
vm,
|
|
196
|
+
defaultCwd: opts.cwd,
|
|
197
|
+
defaultTimeout: opts.defaultTimeout
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
fs: fsProxy,
|
|
202
|
+
computer: computerProxy,
|
|
203
|
+
sandboxId: () => resolvedId,
|
|
204
|
+
init(sandboxId) {
|
|
205
|
+
if (!initPromise) {
|
|
206
|
+
initPromise = doInit(sandboxId).catch((err) => {
|
|
207
|
+
initPromise = null;
|
|
208
|
+
throw err;
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return initPromise;
|
|
212
|
+
},
|
|
213
|
+
async dispose() {
|
|
214
|
+
if (initPromise) {
|
|
215
|
+
await initPromise.catch(() => {
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
if (!autoCreated || !vmRef || !resolvedId) return;
|
|
219
|
+
try {
|
|
220
|
+
const strategy = opts.disposeStrategy ?? "suspend";
|
|
221
|
+
if (strategy === "suspend") {
|
|
222
|
+
await vmRef.suspend();
|
|
223
|
+
} else {
|
|
224
|
+
const mod = await import("freestyle-sandboxes");
|
|
225
|
+
const freestyle = mod.freestyle ?? mod.default?.freestyle;
|
|
226
|
+
if (freestyle?.vms) {
|
|
227
|
+
await freestyle.vms.delete({ vmId: resolvedId });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
} catch {
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
export {
|
|
236
|
+
FreestyleComputer,
|
|
237
|
+
FreestyleFs,
|
|
238
|
+
FreestyleSandbox
|
|
239
|
+
};
|
|
240
|
+
//# sourceMappingURL=freestyle.js.map
|