noumen 0.3.0 → 0.5.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 +4 -2
- package/dist/acp/index.d.ts +5 -3
- package/dist/{agent-1nFVUP9E.d.ts → agent-C3eDRsxs.d.ts} +19 -508
- package/dist/chunk-I5SBSOS6.js +40 -0
- package/dist/chunk-I5SBSOS6.js.map +1 -0
- package/dist/{chunk-4HW6LN6D.js → chunk-WPCYGZOE.js} +58 -1228
- package/dist/chunk-WPCYGZOE.js.map +1 -0
- package/dist/{chunk-5JN4SPI7.js → chunk-WTLK2ZAR.js} +1 -1
- package/dist/{chunk-HL6JCRZJ.js → chunk-XZN4QZLK.js} +4 -4
- package/dist/cli/index.js +10 -10
- 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 +9 -201
- package/dist/index.js +24 -48
- package/dist/lsp/index.d.ts +3 -2
- package/dist/mcp/index.d.ts +4 -3
- package/dist/mcp/index.js +2 -2
- package/dist/{provider-factory-KCLIF34X.js → provider-factory-KI7OZUY3.js} +2 -2
- package/dist/{resolve-4JA2BBDA.js → resolve-GDSHNMG6.js} +2 -2
- package/dist/sandbox-9qeMTNrD.d.ts +126 -0
- package/dist/server/index.d.ts +4 -2
- package/dist/{server-CHMxuWKq.d.ts → server-Cu9gv1dk.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-BA87bHPV.d.ts} +2 -88
- package/package.json +28 -1
- package/dist/chunk-4HW6LN6D.js.map +0 -1
- /package/dist/{chunk-5JN4SPI7.js.map → chunk-WTLK2ZAR.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-KI7OZUY3.js.map} +0 -0
- /package/dist/{resolve-4JA2BBDA.js.map → resolve-GDSHNMG6.js.map} +0 -0
package/dist/ssh.js
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createComputerProxy,
|
|
3
|
+
createFsProxy
|
|
4
|
+
} from "./chunk-I5SBSOS6.js";
|
|
5
|
+
import "./chunk-DGUM43GV.js";
|
|
6
|
+
|
|
7
|
+
// src/virtual/ssh-fs.ts
|
|
8
|
+
import * as pathMod from "path";
|
|
9
|
+
var SshFs = class {
|
|
10
|
+
client;
|
|
11
|
+
workingDir;
|
|
12
|
+
sftpSession = null;
|
|
13
|
+
sftpPromise = null;
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
this.client = opts.client;
|
|
16
|
+
this.workingDir = opts.workingDir ?? "/";
|
|
17
|
+
}
|
|
18
|
+
getSftp() {
|
|
19
|
+
if (this.sftpSession) return Promise.resolve(this.sftpSession);
|
|
20
|
+
if (this.sftpPromise) return this.sftpPromise;
|
|
21
|
+
this.sftpPromise = new Promise((resolve2, reject) => {
|
|
22
|
+
this.client.sftp((err, sftp) => {
|
|
23
|
+
if (err) {
|
|
24
|
+
this.sftpPromise = null;
|
|
25
|
+
reject(err);
|
|
26
|
+
} else {
|
|
27
|
+
this.sftpSession = sftp;
|
|
28
|
+
resolve2(sftp);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
return this.sftpPromise;
|
|
33
|
+
}
|
|
34
|
+
resolvePath(p) {
|
|
35
|
+
if (p.includes("\0")) {
|
|
36
|
+
throw new Error("Path contains null bytes");
|
|
37
|
+
}
|
|
38
|
+
const normalizedBase = this.workingDir.endsWith("/") ? this.workingDir : this.workingDir + "/";
|
|
39
|
+
if (p.startsWith("/")) {
|
|
40
|
+
const normalized = pathMod.normalize(p);
|
|
41
|
+
if (normalized !== this.workingDir && !normalized.startsWith(normalizedBase)) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Absolute path "${p}" is outside working directory "${this.workingDir}"`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return normalized;
|
|
47
|
+
}
|
|
48
|
+
const resolved = pathMod.resolve(this.workingDir, p);
|
|
49
|
+
if (resolved !== this.workingDir && !resolved.startsWith(normalizedBase)) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Path "${p}" escapes working directory "${this.workingDir}"`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return resolved;
|
|
55
|
+
}
|
|
56
|
+
async readFile(path, _opts) {
|
|
57
|
+
const resolved = this.resolvePath(path);
|
|
58
|
+
const sftp = await this.getSftp();
|
|
59
|
+
return new Promise((resolve2, reject) => {
|
|
60
|
+
sftp.readFile(resolved, { encoding: "utf8" }, (err, data) => {
|
|
61
|
+
if (err) reject(new Error(`SshFs readFile failed: ${err.message}`));
|
|
62
|
+
else resolve2(data);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
async readFileBytes(path, maxBytes) {
|
|
67
|
+
const resolved = this.resolvePath(path);
|
|
68
|
+
const sftp = await this.getSftp();
|
|
69
|
+
if (maxBytes === void 0) {
|
|
70
|
+
return new Promise((resolve2, reject) => {
|
|
71
|
+
sftp.readFile(resolved, (err, data) => {
|
|
72
|
+
if (err) reject(new Error(`SshFs readFileBytes failed: ${err.message}`));
|
|
73
|
+
else resolve2(data);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return new Promise((resolve2, reject) => {
|
|
78
|
+
sftp.open(resolved, "r", (err, handle) => {
|
|
79
|
+
if (err) {
|
|
80
|
+
reject(new Error(`SshFs readFileBytes failed: ${err.message}`));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const buf = Buffer.alloc(maxBytes);
|
|
84
|
+
sftp.read(handle, buf, 0, maxBytes, 0, (readErr, bytesRead, readBuf) => {
|
|
85
|
+
sftp.close(handle, () => {
|
|
86
|
+
});
|
|
87
|
+
if (readErr) {
|
|
88
|
+
reject(new Error(`SshFs readFileBytes failed: ${readErr.message}`));
|
|
89
|
+
} else {
|
|
90
|
+
resolve2(readBuf.subarray(0, bytesRead));
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async writeFile(path, content) {
|
|
97
|
+
const resolved = this.resolvePath(path);
|
|
98
|
+
const sftp = await this.getSftp();
|
|
99
|
+
const dir = pathMod.dirname(resolved);
|
|
100
|
+
await this.mkdirRecursive(sftp, dir);
|
|
101
|
+
return new Promise((resolve2, reject) => {
|
|
102
|
+
sftp.writeFile(resolved, content, (err) => {
|
|
103
|
+
if (err) reject(new Error(`SshFs writeFile failed: ${err.message}`));
|
|
104
|
+
else resolve2();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async appendFile(path, content) {
|
|
109
|
+
const resolved = this.resolvePath(path);
|
|
110
|
+
const sftp = await this.getSftp();
|
|
111
|
+
const dir = pathMod.dirname(resolved);
|
|
112
|
+
await this.mkdirRecursive(sftp, dir);
|
|
113
|
+
return new Promise((resolve2, reject) => {
|
|
114
|
+
sftp.appendFile(resolved, content, (err) => {
|
|
115
|
+
if (err) reject(new Error(`SshFs appendFile failed: ${err.message}`));
|
|
116
|
+
else resolve2();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async deleteFile(path, opts) {
|
|
121
|
+
const resolved = this.resolvePath(path);
|
|
122
|
+
if (opts?.recursive) {
|
|
123
|
+
const sftp2 = await this.getSftp();
|
|
124
|
+
await this.rmRecursive(sftp2, resolved);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const sftp = await this.getSftp();
|
|
128
|
+
return new Promise((resolve2, reject) => {
|
|
129
|
+
sftp.unlink(resolved, (err) => {
|
|
130
|
+
if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));
|
|
131
|
+
else resolve2();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
async mkdir(path, opts) {
|
|
136
|
+
const resolved = this.resolvePath(path);
|
|
137
|
+
const sftp = await this.getSftp();
|
|
138
|
+
if (opts?.recursive) {
|
|
139
|
+
await this.mkdirRecursive(sftp, resolved);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
return new Promise((resolve2, reject) => {
|
|
143
|
+
sftp.mkdir(resolved, (err) => {
|
|
144
|
+
if (err) reject(new Error(`SshFs mkdir failed: ${err.message}`));
|
|
145
|
+
else resolve2();
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
async readdir(path, _opts) {
|
|
150
|
+
const resolved = this.resolvePath(path);
|
|
151
|
+
const sftp = await this.getSftp();
|
|
152
|
+
return new Promise((resolve2, reject) => {
|
|
153
|
+
sftp.readdir(resolved, (err, list) => {
|
|
154
|
+
if (err) {
|
|
155
|
+
reject(new Error(`SshFs readdir failed: ${err.message}`));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const entries = list.map((item) => ({
|
|
159
|
+
name: item.filename,
|
|
160
|
+
path: pathMod.join(resolved, item.filename),
|
|
161
|
+
isDirectory: item.attrs.isDirectory(),
|
|
162
|
+
isFile: item.attrs.isFile()
|
|
163
|
+
}));
|
|
164
|
+
resolve2(entries);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
async exists(path) {
|
|
169
|
+
const resolved = this.resolvePath(path);
|
|
170
|
+
const sftp = await this.getSftp();
|
|
171
|
+
return new Promise((resolve2) => {
|
|
172
|
+
sftp.stat(resolved, (err) => {
|
|
173
|
+
resolve2(!err);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
async stat(path) {
|
|
178
|
+
const resolved = this.resolvePath(path);
|
|
179
|
+
const sftp = await this.getSftp();
|
|
180
|
+
return new Promise((resolve2, reject) => {
|
|
181
|
+
sftp.stat(resolved, (err, stats) => {
|
|
182
|
+
if (err) {
|
|
183
|
+
reject(new Error(`SshFs stat failed: ${err.message}`));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
resolve2({
|
|
187
|
+
size: stats.size,
|
|
188
|
+
isDirectory: stats.isDirectory(),
|
|
189
|
+
isFile: stats.isFile(),
|
|
190
|
+
modifiedAt: stats.mtime > 0 ? new Date(stats.mtime * 1e3) : void 0
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
async mkdirRecursive(sftp, dir) {
|
|
196
|
+
const parts = dir.split("/").filter(Boolean);
|
|
197
|
+
let current = "/";
|
|
198
|
+
for (const part of parts) {
|
|
199
|
+
current = pathMod.join(current, part);
|
|
200
|
+
await new Promise((resolve2) => {
|
|
201
|
+
sftp.mkdir(current, () => {
|
|
202
|
+
resolve2();
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async rmRecursive(sftp, target) {
|
|
208
|
+
const isDir = await new Promise((resolve2) => {
|
|
209
|
+
sftp.stat(target, (err, stats) => {
|
|
210
|
+
if (err) resolve2(false);
|
|
211
|
+
else resolve2(stats.isDirectory());
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
if (!isDir) {
|
|
215
|
+
return new Promise((resolve2, reject) => {
|
|
216
|
+
sftp.unlink(target, (err) => {
|
|
217
|
+
if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));
|
|
218
|
+
else resolve2();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
const entries = await new Promise(
|
|
223
|
+
(resolve2, reject) => {
|
|
224
|
+
sftp.readdir(target, (err, list) => {
|
|
225
|
+
if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));
|
|
226
|
+
else resolve2(list);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
);
|
|
230
|
+
for (const entry of entries) {
|
|
231
|
+
await this.rmRecursive(sftp, pathMod.join(target, entry.filename));
|
|
232
|
+
}
|
|
233
|
+
return new Promise((resolve2, reject) => {
|
|
234
|
+
sftp.rmdir(target, (err) => {
|
|
235
|
+
if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));
|
|
236
|
+
else resolve2();
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// src/virtual/ssh-computer.ts
|
|
243
|
+
var SshComputer = class {
|
|
244
|
+
client;
|
|
245
|
+
defaultCwd;
|
|
246
|
+
defaultTimeout;
|
|
247
|
+
constructor(opts) {
|
|
248
|
+
this.client = opts.client;
|
|
249
|
+
this.defaultCwd = opts.defaultCwd ?? "/";
|
|
250
|
+
this.defaultTimeout = opts.defaultTimeout ?? 3e4;
|
|
251
|
+
}
|
|
252
|
+
async executeCommand(command, opts) {
|
|
253
|
+
const cwd = opts?.cwd ?? this.defaultCwd;
|
|
254
|
+
const timeout = opts?.timeout ?? this.defaultTimeout;
|
|
255
|
+
let envPrefix = "";
|
|
256
|
+
if (opts?.env) {
|
|
257
|
+
envPrefix = Object.entries(opts.env).map(([k, v]) => `${k}=${shellEscape(v)}`).join(" ") + " ";
|
|
258
|
+
}
|
|
259
|
+
const fullCommand = `cd ${shellEscape(cwd)} && ${envPrefix}${command}`;
|
|
260
|
+
return new Promise((resolve2, reject) => {
|
|
261
|
+
this.client.exec(fullCommand, (err, channel) => {
|
|
262
|
+
if (err) {
|
|
263
|
+
reject(err);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const stdoutBufs = [];
|
|
267
|
+
const stderrBufs = [];
|
|
268
|
+
const timer = setTimeout(() => {
|
|
269
|
+
channel.destroy?.();
|
|
270
|
+
resolve2({
|
|
271
|
+
exitCode: 1,
|
|
272
|
+
stdout: Buffer.concat(stdoutBufs).toString("utf-8"),
|
|
273
|
+
stderr: Buffer.concat(stderrBufs).toString("utf-8") + "\n[timeout after " + timeout + "ms]"
|
|
274
|
+
});
|
|
275
|
+
}, timeout);
|
|
276
|
+
channel.on("data", (chunk) => {
|
|
277
|
+
stdoutBufs.push(Buffer.from(chunk));
|
|
278
|
+
});
|
|
279
|
+
channel.stderr.on("data", (chunk) => {
|
|
280
|
+
stderrBufs.push(Buffer.from(chunk));
|
|
281
|
+
});
|
|
282
|
+
channel.on("close", (code) => {
|
|
283
|
+
clearTimeout(timer);
|
|
284
|
+
resolve2({
|
|
285
|
+
exitCode: code ?? 0,
|
|
286
|
+
stdout: Buffer.concat(stdoutBufs).toString("utf-8"),
|
|
287
|
+
stderr: Buffer.concat(stderrBufs).toString("utf-8")
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
channel.on("error", (channelErr) => {
|
|
291
|
+
clearTimeout(timer);
|
|
292
|
+
reject(channelErr);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
function shellEscape(s) {
|
|
299
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/virtual/ssh-sandbox.ts
|
|
303
|
+
function SshSandbox(opts) {
|
|
304
|
+
if (opts.client) {
|
|
305
|
+
const c = opts.client;
|
|
306
|
+
return {
|
|
307
|
+
fs: new SshFs({ client: c, workingDir: opts.cwd }),
|
|
308
|
+
computer: new SshComputer({
|
|
309
|
+
client: c,
|
|
310
|
+
defaultCwd: opts.cwd,
|
|
311
|
+
defaultTimeout: opts.defaultTimeout
|
|
312
|
+
}),
|
|
313
|
+
sandboxId: () => opts.host ? `${opts.host}:${opts.port ?? 22}` : void 0
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
if (!opts.host) {
|
|
317
|
+
throw new Error("SshSandbox requires either `client` or `host`");
|
|
318
|
+
}
|
|
319
|
+
const fsProxy = createFsProxy();
|
|
320
|
+
const computerProxy = createComputerProxy();
|
|
321
|
+
const identifier = `${opts.host}:${opts.port ?? 22}`;
|
|
322
|
+
let clientRef = null;
|
|
323
|
+
let autoCreated = false;
|
|
324
|
+
let initPromise = null;
|
|
325
|
+
async function doInit() {
|
|
326
|
+
const modName = "ssh2";
|
|
327
|
+
const ssh2 = await import(
|
|
328
|
+
/* webpackIgnore: true */
|
|
329
|
+
modName
|
|
330
|
+
);
|
|
331
|
+
const ClientClass = ssh2.Client ?? ssh2.default?.Client;
|
|
332
|
+
if (!ClientClass) {
|
|
333
|
+
throw new Error(
|
|
334
|
+
"Could not resolve Client class from 'ssh2' package"
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
const client = new ClientClass();
|
|
338
|
+
await new Promise((resolve2, reject) => {
|
|
339
|
+
client.on("ready", () => resolve2());
|
|
340
|
+
client.on("error", (err) => reject(err));
|
|
341
|
+
client.connect({
|
|
342
|
+
host: opts.host,
|
|
343
|
+
port: opts.port ?? 22,
|
|
344
|
+
username: opts.username ?? "root",
|
|
345
|
+
password: opts.password,
|
|
346
|
+
privateKey: opts.privateKey,
|
|
347
|
+
passphrase: opts.passphrase
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
clientRef = client;
|
|
351
|
+
autoCreated = true;
|
|
352
|
+
fsProxy.setTarget(new SshFs({ client, workingDir: opts.cwd }));
|
|
353
|
+
computerProxy.setTarget(
|
|
354
|
+
new SshComputer({
|
|
355
|
+
client,
|
|
356
|
+
defaultCwd: opts.cwd,
|
|
357
|
+
defaultTimeout: opts.defaultTimeout
|
|
358
|
+
})
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
fs: fsProxy,
|
|
363
|
+
computer: computerProxy,
|
|
364
|
+
sandboxId: () => identifier,
|
|
365
|
+
init() {
|
|
366
|
+
if (!initPromise) {
|
|
367
|
+
initPromise = doInit().catch((err) => {
|
|
368
|
+
initPromise = null;
|
|
369
|
+
throw err;
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
return initPromise;
|
|
373
|
+
},
|
|
374
|
+
async dispose() {
|
|
375
|
+
if (initPromise) {
|
|
376
|
+
await initPromise.catch(() => {
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
if (!autoCreated || !clientRef) return;
|
|
380
|
+
try {
|
|
381
|
+
clientRef.end();
|
|
382
|
+
} catch {
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
export {
|
|
388
|
+
SshComputer,
|
|
389
|
+
SshFs,
|
|
390
|
+
SshSandbox
|
|
391
|
+
};
|
|
392
|
+
//# sourceMappingURL=ssh.js.map
|
package/dist/ssh.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/virtual/ssh-fs.ts","../src/virtual/ssh-computer.ts","../src/virtual/ssh-sandbox.ts"],"sourcesContent":["import * as pathMod from \"node:path\";\nimport type { VirtualFs, FileEntry, FileStat, ReadOptions } from \"./fs.js\";\nimport type { SshClient, SshSftpSession } from \"./ssh-computer.js\";\n\nexport interface SshFsOptions {\n /** A connected ssh2 Client instance. */\n client: SshClient;\n /** Working directory for relative path resolution (default: /). */\n workingDir?: string;\n}\n\n/**\n * VirtualFs backed by SFTP file operations over SSH.\n *\n * Uses the ssh2 Client's SFTP subsystem for all file I/O. The SFTP session\n * is opened lazily on the first operation and reused for the lifetime of\n * the SshFs instance.\n *\n * Requires `ssh2` as an optional peer dependency.\n * The caller is responsible for the Client lifecycle.\n */\nexport class SshFs implements VirtualFs {\n private client: SshClient;\n private workingDir: string;\n private sftpSession: SshSftpSession | null = null;\n private sftpPromise: Promise<SshSftpSession> | null = null;\n\n constructor(opts: SshFsOptions) {\n this.client = opts.client;\n this.workingDir = opts.workingDir ?? \"/\";\n }\n\n private getSftp(): Promise<SshSftpSession> {\n if (this.sftpSession) return Promise.resolve(this.sftpSession);\n if (this.sftpPromise) return this.sftpPromise;\n this.sftpPromise = new Promise<SshSftpSession>((resolve, reject) => {\n this.client.sftp((err, sftp) => {\n if (err) {\n this.sftpPromise = null;\n reject(err);\n } else {\n this.sftpSession = sftp;\n resolve(sftp);\n }\n });\n });\n return this.sftpPromise;\n }\n\n private resolvePath(p: string): string {\n if (p.includes(\"\\0\")) {\n throw new Error(\"Path contains null bytes\");\n }\n const normalizedBase = this.workingDir.endsWith(\"/\")\n ? this.workingDir\n : this.workingDir + \"/\";\n if (p.startsWith(\"/\")) {\n const normalized = pathMod.normalize(p);\n if (\n normalized !== this.workingDir &&\n !normalized.startsWith(normalizedBase)\n ) {\n throw new Error(\n `Absolute path \"${p}\" is outside working directory \"${this.workingDir}\"`,\n );\n }\n return normalized;\n }\n const resolved = pathMod.resolve(this.workingDir, p);\n if (\n resolved !== this.workingDir &&\n !resolved.startsWith(normalizedBase)\n ) {\n throw new Error(\n `Path \"${p}\" escapes working directory \"${this.workingDir}\"`,\n );\n }\n return resolved;\n }\n\n async readFile(path: string, _opts?: ReadOptions): Promise<string> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n return new Promise<string>((resolve, reject) => {\n sftp.readFile(resolved, { encoding: \"utf8\" }, (err, data) => {\n if (err) reject(new Error(`SshFs readFile failed: ${err.message}`));\n else resolve(data as string);\n });\n });\n }\n\n async readFileBytes(path: string, maxBytes?: number): Promise<Buffer> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n\n if (maxBytes === undefined) {\n return new Promise<Buffer>((resolve, reject) => {\n sftp.readFile(resolved, (err, data) => {\n if (err) reject(new Error(`SshFs readFileBytes failed: ${err.message}`));\n else resolve(data);\n });\n });\n }\n\n return new Promise<Buffer>((resolve, reject) => {\n sftp.open(resolved, \"r\", (err, handle) => {\n if (err) {\n reject(new Error(`SshFs readFileBytes failed: ${err.message}`));\n return;\n }\n const buf = Buffer.alloc(maxBytes);\n sftp.read(handle, buf, 0, maxBytes, 0, (readErr, bytesRead, readBuf) => {\n sftp.close(handle, () => {});\n if (readErr) {\n reject(new Error(`SshFs readFileBytes failed: ${readErr.message}`));\n } else {\n resolve(readBuf.subarray(0, bytesRead));\n }\n });\n });\n });\n }\n\n async writeFile(path: string, content: string): Promise<void> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n const dir = pathMod.dirname(resolved);\n await this.mkdirRecursive(sftp, dir);\n return new Promise<void>((resolve, reject) => {\n sftp.writeFile(resolved, content, (err) => {\n if (err) reject(new Error(`SshFs writeFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n async appendFile(path: string, content: string): Promise<void> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n const dir = pathMod.dirname(resolved);\n await this.mkdirRecursive(sftp, dir);\n return new Promise<void>((resolve, reject) => {\n sftp.appendFile(resolved, content, (err) => {\n if (err) reject(new Error(`SshFs appendFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n async deleteFile(\n path: string,\n opts?: { recursive?: boolean },\n ): Promise<void> {\n const resolved = this.resolvePath(path);\n if (opts?.recursive) {\n const sftp = await this.getSftp();\n await this.rmRecursive(sftp, resolved);\n return;\n }\n const sftp = await this.getSftp();\n return new Promise<void>((resolve, reject) => {\n sftp.unlink(resolved, (err) => {\n if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n async mkdir(path: string, opts?: { recursive?: boolean }): Promise<void> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n if (opts?.recursive) {\n await this.mkdirRecursive(sftp, resolved);\n return;\n }\n return new Promise<void>((resolve, reject) => {\n sftp.mkdir(resolved, (err) => {\n if (err) reject(new Error(`SshFs mkdir failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n async readdir(\n path: string,\n _opts?: { recursive?: boolean },\n ): Promise<FileEntry[]> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n return new Promise<FileEntry[]>((resolve, reject) => {\n sftp.readdir(resolved, (err, list) => {\n if (err) {\n reject(new Error(`SshFs readdir failed: ${err.message}`));\n return;\n }\n const entries: FileEntry[] = list.map((item) => ({\n name: item.filename,\n path: pathMod.join(resolved, item.filename),\n isDirectory: item.attrs.isDirectory(),\n isFile: item.attrs.isFile(),\n }));\n resolve(entries);\n });\n });\n }\n\n async exists(path: string): Promise<boolean> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n return new Promise<boolean>((resolve) => {\n sftp.stat(resolved, (err) => {\n resolve(!err);\n });\n });\n }\n\n async stat(path: string): Promise<FileStat> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n return new Promise<FileStat>((resolve, reject) => {\n sftp.stat(resolved, (err, stats) => {\n if (err) {\n reject(new Error(`SshFs stat failed: ${err.message}`));\n return;\n }\n resolve({\n size: stats.size,\n isDirectory: stats.isDirectory(),\n isFile: stats.isFile(),\n modifiedAt: stats.mtime > 0 ? new Date(stats.mtime * 1000) : undefined,\n });\n });\n });\n }\n\n private async mkdirRecursive(\n sftp: SshSftpSession,\n dir: string,\n ): Promise<void> {\n const parts = dir.split(\"/\").filter(Boolean);\n let current = \"/\";\n for (const part of parts) {\n current = pathMod.join(current, part);\n await new Promise<void>((resolve) => {\n sftp.mkdir(current, () => {\n resolve();\n });\n });\n }\n }\n\n private async rmRecursive(\n sftp: SshSftpSession,\n target: string,\n ): Promise<void> {\n const isDir = await new Promise<boolean>((resolve) => {\n sftp.stat(target, (err, stats) => {\n if (err) resolve(false);\n else resolve(stats.isDirectory());\n });\n });\n\n if (!isDir) {\n return new Promise<void>((resolve, reject) => {\n sftp.unlink(target, (err) => {\n if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n const entries = await new Promise<Array<{ filename: string; attrs: { isDirectory(): boolean } }>>(\n (resolve, reject) => {\n sftp.readdir(target, (err, list) => {\n if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));\n else resolve(list);\n });\n },\n );\n\n for (const entry of entries) {\n await this.rmRecursive(sftp, pathMod.join(target, entry.filename));\n }\n\n return new Promise<void>((resolve, reject) => {\n sftp.rmdir(target, (err) => {\n if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n}\n","import type { VirtualComputer, ExecOptions, CommandResult } from \"./computer.js\";\n\n/**\n * Minimal subset of the ssh2 Channel interface used by SshComputer / SshFs.\n * Avoids a hard import of ssh2 at the module level.\n */\nexport interface SshChannel {\n on(event: string, listener: (...args: any[]) => void): SshChannel;\n stderr: { on(event: string, listener: (...args: any[]) => void): unknown };\n destroy?(): void;\n}\n\n/**\n * Minimal subset of the ssh2 SFTPWrapper interface used by SshFs.\n */\nexport interface SshSftpSession {\n readFile(\n path: string,\n callback: (err: Error | undefined, data: Buffer) => void,\n ): void;\n readFile(\n path: string,\n opts: { encoding: string },\n callback: (err: Error | undefined, data: string) => void,\n ): void;\n writeFile(\n path: string,\n data: string | Buffer,\n callback: (err: Error | undefined) => void,\n ): void;\n appendFile(\n path: string,\n data: string | Buffer,\n callback: (err: Error | undefined) => void,\n ): void;\n unlink(path: string, callback: (err: Error | undefined) => void): void;\n rmdir(path: string, callback: (err: Error | undefined) => void): void;\n mkdir(path: string, callback: (err: Error | undefined) => void): void;\n readdir(\n path: string,\n callback: (\n err: Error | undefined,\n list: Array<{\n filename: string;\n longname: string;\n attrs: { size: number; isDirectory(): boolean; isFile(): boolean; mtime: number; atime: number };\n }>,\n ) => void,\n ): void;\n stat(\n path: string,\n callback: (\n err: Error | undefined,\n stats: { size: number; isDirectory(): boolean; isFile(): boolean; mtime: number; atime: number },\n ) => void,\n ): void;\n open(\n path: string,\n flags: string,\n callback: (err: Error | undefined, handle: Buffer) => void,\n ): void;\n read(\n handle: Buffer,\n buffer: Buffer,\n offset: number,\n length: number,\n position: number,\n callback: (err: Error | undefined, bytesRead: number, buf: Buffer) => void,\n ): void;\n close(handle: Buffer, callback: (err: Error | undefined) => void): void;\n end(): void;\n}\n\n/**\n * Minimal subset of the ssh2 Client interface used by SshComputer and SshFs.\n * Avoids a hard import of ssh2 at the module level.\n */\nexport interface SshClient {\n exec(\n command: string,\n callback: (err: Error | undefined, channel: SshChannel) => void,\n ): void;\n exec(\n command: string,\n opts: Record<string, unknown>,\n callback: (err: Error | undefined, channel: SshChannel) => void,\n ): void;\n sftp(\n callback: (err: Error | undefined, sftp: SshSftpSession) => void,\n ): void;\n end(): void;\n on(event: string, listener: (...args: any[]) => void): this;\n}\n\nexport interface SshComputerOptions {\n /** A connected ssh2 Client instance. */\n client: SshClient;\n /** Default working directory for commands (default: /). */\n defaultCwd?: string;\n /** Default timeout in ms for commands (default: 30000). */\n defaultTimeout?: number;\n}\n\n/**\n * VirtualComputer backed by command execution over SSH.\n *\n * Requires `ssh2` as an optional peer dependency.\n * The caller is responsible for the Client lifecycle (connect, end).\n */\nexport class SshComputer implements VirtualComputer {\n private client: SshClient;\n private defaultCwd: string;\n private defaultTimeout: number;\n\n constructor(opts: SshComputerOptions) {\n this.client = opts.client;\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 cwd = opts?.cwd ?? this.defaultCwd;\n const timeout = opts?.timeout ?? this.defaultTimeout;\n\n let envPrefix = \"\";\n if (opts?.env) {\n envPrefix = Object.entries(opts.env)\n .map(([k, v]) => `${k}=${shellEscape(v)}`)\n .join(\" \") + \" \";\n }\n\n const fullCommand = `cd ${shellEscape(cwd)} && ${envPrefix}${command}`;\n\n return new Promise<CommandResult>((resolve, reject) => {\n this.client.exec(fullCommand, (err, channel) => {\n if (err) {\n reject(err);\n return;\n }\n\n const stdoutBufs: Buffer[] = [];\n const stderrBufs: Buffer[] = [];\n\n const timer = setTimeout(() => {\n channel.destroy?.();\n resolve({\n exitCode: 1,\n stdout: Buffer.concat(stdoutBufs).toString(\"utf-8\"),\n stderr:\n Buffer.concat(stderrBufs).toString(\"utf-8\") +\n \"\\n[timeout after \" + timeout + \"ms]\",\n });\n }, timeout);\n\n channel.on(\"data\", (chunk: Buffer) => {\n stdoutBufs.push(Buffer.from(chunk));\n });\n\n channel.stderr.on(\"data\", (chunk: Buffer) => {\n stderrBufs.push(Buffer.from(chunk));\n });\n\n channel.on(\"close\", (code: number) => {\n clearTimeout(timer);\n resolve({\n exitCode: code ?? 0,\n stdout: Buffer.concat(stdoutBufs).toString(\"utf-8\"),\n stderr: Buffer.concat(stderrBufs).toString(\"utf-8\"),\n });\n });\n\n channel.on(\"error\", (channelErr: Error) => {\n clearTimeout(timer);\n reject(channelErr);\n });\n });\n });\n }\n}\n\nfunction shellEscape(s: string): string {\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n}\n","import type { Sandbox } from \"./sandbox.js\";\nimport { SshFs } from \"./ssh-fs.js\";\nimport { SshComputer, type SshClient } from \"./ssh-computer.js\";\nimport { createFsProxy, createComputerProxy } from \"./proxy.js\";\n\nexport interface SshSandboxOptions {\n /**\n * A pre-connected ssh2 Client instance. When provided the sandbox uses\n * this client directly — no auto-connect occurs and `dispose()` will\n * **not** call `client.end()`.\n *\n * When omitted, a new ssh2 Client is created and connected on the first\n * `init()` call using `host`, `port`, `username`, and the provided\n * credentials. The client is ended when `dispose()` is called.\n */\n client?: SshClient;\n /**\n * SSH hostname. Required when `client` is omitted; ignored when `client`\n * is provided.\n */\n host?: string;\n /** SSH port (default: 22). Only used during auto-connect. */\n port?: number;\n /** SSH username (default: \"root\"). Only used during auto-connect. */\n username?: string;\n /** Password for password-based authentication. Only used during auto-connect. */\n password?: string;\n /** Private key for key-based authentication (PEM string or Buffer). Only used during auto-connect. */\n privateKey?: string | Buffer;\n /** Passphrase for an encrypted private key. Only used during auto-connect. */\n passphrase?: string;\n /** Working directory on the remote host. */\n cwd?: string;\n /** Default timeout (ms) for shell commands. */\n defaultTimeout?: number;\n}\n\n/**\n * Create a `Sandbox` backed by a remote host over SSH.\n * Requires `ssh2` as an optional peer dependency.\n *\n * **Auto-connect:** When `client` is omitted and `host` is provided,\n * an ssh2 Client is created and connected lazily on the first `init()`\n * call. The connection identifier (`host:port`) is available through\n * `sandboxId()` for session persistence.\n *\n * **Explicit client:** When `client` is provided, `init()` binds it\n * immediately. `dispose()` is a no-op — the caller owns the client's\n * lifecycle.\n *\n * @example\n * ```ts\n * // Auto-connect with private key\n * const sandbox = SshSandbox({\n * host: \"dev.example.com\",\n * username: \"deploy\",\n * privateKey: fs.readFileSync(\"~/.ssh/id_ed25519\"),\n * cwd: \"/home/deploy/project\",\n * });\n *\n * // Explicit client (lifecycle managed externally)\n * const sandbox = SshSandbox({ client: myConnectedClient, cwd: \"/workspace\" });\n * ```\n */\nexport function SshSandbox(opts: SshSandboxOptions): Sandbox {\n if (opts.client) {\n const c = opts.client;\n return {\n fs: new SshFs({ client: c, workingDir: opts.cwd }),\n computer: new SshComputer({\n client: c,\n defaultCwd: opts.cwd,\n defaultTimeout: opts.defaultTimeout,\n }),\n sandboxId: () =>\n opts.host ? `${opts.host}:${opts.port ?? 22}` : undefined,\n };\n }\n\n if (!opts.host) {\n throw new Error(\"SshSandbox requires either `client` or `host`\");\n }\n\n const fsProxy = createFsProxy();\n const computerProxy = createComputerProxy();\n const identifier = `${opts.host}:${opts.port ?? 22}`;\n let clientRef: SshClient | null = null;\n let autoCreated = false;\n let initPromise: Promise<void> | null = null;\n\n async function doInit(): Promise<void> {\n const modName = \"ssh2\";\n const ssh2 = await import(/* webpackIgnore: true */ modName);\n const ClientClass =\n (ssh2 as any).Client ?? (ssh2 as any).default?.Client;\n if (!ClientClass) {\n throw new Error(\n \"Could not resolve Client class from 'ssh2' package\",\n );\n }\n\n const client: SshClient = new ClientClass();\n await new Promise<void>((resolve, reject) => {\n client.on(\"ready\", () => resolve());\n client.on(\"error\", (err: Error) => reject(err));\n (client as any).connect({\n host: opts.host,\n port: opts.port ?? 22,\n username: opts.username ?? \"root\",\n password: opts.password,\n privateKey: opts.privateKey,\n passphrase: opts.passphrase,\n });\n });\n\n clientRef = client;\n autoCreated = true;\n fsProxy.setTarget(new SshFs({ client, workingDir: opts.cwd }));\n computerProxy.setTarget(\n new SshComputer({\n client,\n defaultCwd: opts.cwd,\n defaultTimeout: opts.defaultTimeout,\n }),\n );\n }\n\n return {\n fs: fsProxy,\n computer: computerProxy,\n sandboxId: () => identifier,\n\n init(): Promise<void> {\n if (!initPromise) {\n initPromise = doInit().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 || !clientRef) return;\n try {\n clientRef.end();\n } catch {\n /* best-effort */\n }\n },\n };\n}\n"],"mappings":"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","names":["resolve","sftp","resolve","resolve"]}
|
|
@@ -1,93 +1,7 @@
|
|
|
1
1
|
import { c as CheckpointConfig, F as FileCheckpointState, D as DiffStats, d as FileCheckpointSnapshot, S as StreamEvent, e as ContentPart } from './types-LrU4LRmX.js';
|
|
2
|
+
import { a as VirtualFs, V as VirtualComputer } from './computer-BPdxSo6X.js';
|
|
2
3
|
import { a as PermissionMode, b as PermissionResult } from './types-CD0rUKKT.js';
|
|
3
4
|
|
|
4
|
-
interface FileEntry {
|
|
5
|
-
name: string;
|
|
6
|
-
path: string;
|
|
7
|
-
isDirectory: boolean;
|
|
8
|
-
isFile: boolean;
|
|
9
|
-
size?: number;
|
|
10
|
-
}
|
|
11
|
-
interface FileStat {
|
|
12
|
-
size: number;
|
|
13
|
-
isDirectory: boolean;
|
|
14
|
-
isFile: boolean;
|
|
15
|
-
createdAt?: Date;
|
|
16
|
-
modifiedAt?: Date;
|
|
17
|
-
}
|
|
18
|
-
interface ReadOptions {
|
|
19
|
-
encoding?: BufferEncoding;
|
|
20
|
-
/**
|
|
21
|
-
* Maximum number of bytes to read. When set, only the first `maxBytes`
|
|
22
|
-
* bytes are returned (decoded as a string). Implementations that do not
|
|
23
|
-
* support this option may ignore it and return the full content.
|
|
24
|
-
*/
|
|
25
|
-
maxBytes?: number;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Sandboxed filesystem interface.
|
|
29
|
-
*
|
|
30
|
-
* `VirtualFs` is noumen's primary isolation boundary for file I/O. Every
|
|
31
|
-
* built-in tool that touches the filesystem (ReadFile, WriteFile, EditFile)
|
|
32
|
-
* delegates to this interface — the agent never accesses `node:fs` directly.
|
|
33
|
-
*
|
|
34
|
-
* Swap implementations to control where files live and what the agent can reach:
|
|
35
|
-
* - `LocalFs` — reads/writes on the host filesystem (no isolation, for local dev)
|
|
36
|
-
* - `SpritesFs` — reads/writes inside a remote sprites.dev container (full sandbox)
|
|
37
|
-
* - Custom — implement this interface for Docker volumes, E2B, S3, in-memory, etc.
|
|
38
|
-
*/
|
|
39
|
-
interface VirtualFs {
|
|
40
|
-
readFile(path: string, opts?: ReadOptions): Promise<string>;
|
|
41
|
-
/**
|
|
42
|
-
* Read raw bytes from a file. Used for binary content (images, PDFs).
|
|
43
|
-
* Implementations SHOULD cap the read at `maxBytes` to prevent OOM on
|
|
44
|
-
* very large files. When `maxBytes` is omitted, the entire file is read.
|
|
45
|
-
*
|
|
46
|
-
* Returns a Buffer (Node.js) or Uint8Array.
|
|
47
|
-
*/
|
|
48
|
-
readFileBytes?(path: string, maxBytes?: number): Promise<Buffer>;
|
|
49
|
-
writeFile(path: string, content: string): Promise<void>;
|
|
50
|
-
appendFile(path: string, content: string): Promise<void>;
|
|
51
|
-
deleteFile(path: string, opts?: {
|
|
52
|
-
recursive?: boolean;
|
|
53
|
-
}): Promise<void>;
|
|
54
|
-
mkdir(path: string, opts?: {
|
|
55
|
-
recursive?: boolean;
|
|
56
|
-
}): Promise<void>;
|
|
57
|
-
readdir(path: string, opts?: {
|
|
58
|
-
recursive?: boolean;
|
|
59
|
-
}): Promise<FileEntry[]>;
|
|
60
|
-
exists(path: string): Promise<boolean>;
|
|
61
|
-
stat(path: string): Promise<FileStat>;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
interface ExecOptions {
|
|
65
|
-
timeout?: number;
|
|
66
|
-
cwd?: string;
|
|
67
|
-
env?: Record<string, string>;
|
|
68
|
-
signal?: AbortSignal;
|
|
69
|
-
}
|
|
70
|
-
interface CommandResult {
|
|
71
|
-
exitCode: number;
|
|
72
|
-
stdout: string;
|
|
73
|
-
stderr: string;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Sandboxed shell execution interface.
|
|
77
|
-
*
|
|
78
|
-
* `VirtualComputer` is noumen's primary isolation boundary for command
|
|
79
|
-
* execution. Every built-in tool that runs shell commands (Bash, Glob, Grep)
|
|
80
|
-
* delegates to this interface — the agent never spawns processes directly.
|
|
81
|
-
*
|
|
82
|
-
* Swap implementations to control where and how commands run:
|
|
83
|
-
* - `LocalComputer` — runs on the host machine (no isolation, for local dev)
|
|
84
|
-
* - `SpritesComputer` — runs in a remote sprites.dev container (full sandbox)
|
|
85
|
-
* - Custom — implement this interface for Docker, E2B, Daytona, etc.
|
|
86
|
-
*/
|
|
87
|
-
interface VirtualComputer {
|
|
88
|
-
executeCommand(command: string, opts?: ExecOptions): Promise<CommandResult>;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
5
|
/**
|
|
92
6
|
* Zod integration utilities. Users bring their own `zod` dependency — these
|
|
93
7
|
* helpers accept duck-typed schemas so we don't force a hard dependency.
|
|
@@ -642,4 +556,4 @@ interface Tool {
|
|
|
642
556
|
call(args: Record<string, unknown>, ctx: ToolContext): Promise<ToolResult>;
|
|
643
557
|
}
|
|
644
558
|
|
|
645
|
-
export { type
|
|
559
|
+
export { type SessionEndHookInput as A, type SessionStartHookInput as B, type SubagentStartHookInput as C, DiagnosticRegistry as D, type SubagentStopHookInput as E, FileCheckpointManager as F, type Task as G, type HookDefinition as H, type TaskCreateInput as I, type JsonSchemaType as J, type TaskStatus as K, type LspServerConfig as L, type MemoryUpdateHookInput as M, type NotificationHookInput as N, type TaskUpdateInput as O, type PostToolUseFailureHookInput as P, type ToolParameters as Q, type RetryAttemptHookInput as R, type SubagentConfig as S, type Tool as T, formatZodValidationError as U, registerZodToJsonSchema as V, zodToJsonSchema as W, type ZodLikeSchema as Z, type LspServerState as a, type LspDiagnostic as b, type LspLocation as c, type LspOperation as d, LspServerManager as e, type LspSymbol as f, type ToolResult as g, type ToolContext as h, type SubagentRun as i, TaskStore as j, type FileStateCacheConfig as k, type HookEvent as l, type HookInput as m, type PostToolUseFailureHookOutput as n, type PostToolUseHookInput as o, type PostToolUseHookOutput as p, type PreToolUseHookInput as q, type PreToolUseHookOutput as r, type FileState as s, FileStateCache as t, type FileWriteHookInput as u, type HookOutput as v, type ModelSwitchHookInput as w, type PermissionDeniedHookInput as x, type PermissionRequestHookInput as y, type SafeParseResult as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "noumen",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Programmatic AI agent runtime with pluggable providers and sandboxed virtual infrastructure",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -69,6 +69,26 @@
|
|
|
69
69
|
"./jsonrpc": {
|
|
70
70
|
"import": "./dist/jsonrpc/index.js",
|
|
71
71
|
"types": "./dist/jsonrpc/index.d.ts"
|
|
72
|
+
},
|
|
73
|
+
"./docker": {
|
|
74
|
+
"import": "./dist/docker.js",
|
|
75
|
+
"types": "./dist/docker.d.ts"
|
|
76
|
+
},
|
|
77
|
+
"./e2b": {
|
|
78
|
+
"import": "./dist/e2b.js",
|
|
79
|
+
"types": "./dist/e2b.d.ts"
|
|
80
|
+
},
|
|
81
|
+
"./freestyle": {
|
|
82
|
+
"import": "./dist/freestyle.js",
|
|
83
|
+
"types": "./dist/freestyle.d.ts"
|
|
84
|
+
},
|
|
85
|
+
"./ssh": {
|
|
86
|
+
"import": "./dist/ssh.js",
|
|
87
|
+
"types": "./dist/ssh.d.ts"
|
|
88
|
+
},
|
|
89
|
+
"./sprites": {
|
|
90
|
+
"import": "./dist/sprites.js",
|
|
91
|
+
"types": "./dist/sprites.d.ts"
|
|
72
92
|
}
|
|
73
93
|
},
|
|
74
94
|
"files": [
|
|
@@ -97,6 +117,7 @@
|
|
|
97
117
|
"google-auth-library": ">=9.0.0",
|
|
98
118
|
"openai": "^4.85.0",
|
|
99
119
|
"sharp": ">=0.33.0",
|
|
120
|
+
"ssh2": ">=1.0.0",
|
|
100
121
|
"vscode-jsonrpc": "^8.2.1",
|
|
101
122
|
"ws": ">=8.0.0"
|
|
102
123
|
},
|
|
@@ -142,6 +163,9 @@
|
|
|
142
163
|
},
|
|
143
164
|
"sharp": {
|
|
144
165
|
"optional": true
|
|
166
|
+
},
|
|
167
|
+
"ssh2": {
|
|
168
|
+
"optional": true
|
|
145
169
|
}
|
|
146
170
|
},
|
|
147
171
|
"devDependencies": {
|
|
@@ -170,6 +194,9 @@
|
|
|
170
194
|
"test": "vitest run",
|
|
171
195
|
"dev": "concurrently -n lib,web -c blue,magenta \"tsup --watch\" \"pnpm --filter noumen-docs dev\"",
|
|
172
196
|
"cli": "tsx src/cli/index.ts",
|
|
197
|
+
"version:major": "npm version major --no-git-tag-version",
|
|
198
|
+
"version:minor": "npm version minor --no-git-tag-version",
|
|
199
|
+
"version:patch": "npm version patch --no-git-tag-version",
|
|
173
200
|
"publish:lib": "pnpm publish --access public --no-git-checks"
|
|
174
201
|
}
|
|
175
202
|
}
|