noumen 0.6.0 → 0.8.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 +237 -93
- package/dist/a2a/index.d.ts +5 -7
- package/dist/a2a/index.js +3 -4
- package/dist/a2a/index.js.map +1 -1
- package/dist/acp/index.d.ts +5 -7
- package/dist/acp/index.js +0 -1
- package/dist/acp/index.js.map +1 -1
- package/dist/{agent-DWE4_P5X.d.ts → agent-D0gl-qYi.d.ts} +89 -34
- package/dist/{chunk-6MMYCGJQ.js → chunk-5HY4IYNT.js} +1529 -2321
- package/dist/chunk-5HY4IYNT.js.map +1 -0
- package/dist/chunk-BC5BLWBC.js +21 -0
- package/dist/chunk-BC5BLWBC.js.map +1 -0
- package/dist/{chunk-XZN4QZLK.js → chunk-CX4BL6PC.js} +25 -15
- package/dist/chunk-CX4BL6PC.js.map +1 -0
- package/dist/{chunk-5GEX6ZSB.js → chunk-HQISH4D7.js} +60 -1
- package/dist/chunk-HQISH4D7.js.map +1 -0
- package/dist/{chunk-Y45R3PQL.js → chunk-NUCJXOUV.js} +32 -18
- package/dist/{chunk-Y45R3PQL.js.map → chunk-NUCJXOUV.js.map} +1 -1
- package/dist/chunk-OPFFLQZL.js +40 -0
- package/dist/chunk-OPFFLQZL.js.map +1 -0
- package/dist/chunk-PDEAJ272.js +660 -0
- package/dist/chunk-PDEAJ272.js.map +1 -0
- package/dist/chunk-PKHLGGEC.js +115 -0
- package/dist/chunk-PKHLGGEC.js.map +1 -0
- package/dist/chunk-XQTNXRE7.js +176 -0
- package/dist/chunk-XQTNXRE7.js.map +1 -0
- package/dist/chunk-XZPAA5TO.js +817 -0
- package/dist/chunk-XZPAA5TO.js.map +1 -0
- package/dist/cli/index.js +77 -42
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -2
- package/dist/client/index.js +0 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client-JJFLE6RT.js +9 -0
- package/dist/{computer-BPdxSo6X.d.ts → computer-DzMR92tK.d.ts} +1 -1
- package/dist/docker.d.ts +2 -2
- package/dist/docker.js +0 -1
- package/dist/docker.js.map +1 -1
- package/dist/e2b.d.ts +2 -2
- package/dist/e2b.js +0 -1
- package/dist/e2b.js.map +1 -1
- package/dist/freestyle.d.ts +2 -2
- package/dist/freestyle.js +0 -1
- package/dist/freestyle.js.map +1 -1
- package/dist/{headless-FFU2DESQ.js → headless-25DU4MJQ.js} +1 -3
- package/dist/{headless-FFU2DESQ.js.map → headless-25DU4MJQ.js.map} +1 -1
- package/dist/{history-snip-64GYP4ZL.js → history-snip-HAWNAYKY.js} +1 -2
- package/dist/index.d.ts +351 -72
- package/dist/index.js +54 -55
- package/dist/jsonrpc/index.js +0 -1
- package/dist/local.d.ts +168 -0
- package/dist/local.js +40 -0
- package/dist/local.js.map +1 -0
- package/dist/lsp/index.d.ts +4 -5
- package/dist/lsp/index.js +0 -1
- package/dist/{lsp-PS3BWIHC.js → lsp-3APWNKB2.js} +1 -2
- package/dist/{manager-DLXK63XC.js → manager-Z5EQ7YYV.js} +1 -2
- package/dist/mcp/index.d.ts +16 -8
- package/dist/mcp/index.js +5 -6
- package/dist/mcp/index.js.map +1 -1
- package/dist/{mcp-auth-AEI2R4ZC.js → mcp-auth-NOIQPF7W.js} +1 -2
- package/dist/{provider-factory-TUHU3DIG.js → provider-factory-KNBSHXJ6.js} +3 -3
- package/dist/{render-GRN4ZSSW.js → render-4VEODRK7.js} +1 -2
- package/dist/{resolve-6KUZNEYW.js → resolve-AGQZFMKD.js} +3 -3
- package/dist/sandbox-DAqQo0Tj.d.ts +49 -0
- package/dist/sandbox-index-ODNREIFA.js +32 -0
- package/dist/sandbox-index-ODNREIFA.js.map +1 -0
- package/dist/server/index.d.ts +18 -7
- package/dist/server/index.js +9 -5
- package/dist/server/index.js.map +1 -1
- package/dist/{server-BzNGKTP6.d.ts → server-DFXdlqyX.d.ts} +1 -1
- package/dist/{spinner-OJNR6NFO.js → spinner-72JEISPK.js} +1 -2
- package/dist/sprites.d.ts +2 -2
- package/dist/sprites.js +0 -1
- package/dist/sprites.js.map +1 -1
- package/dist/ssh.d.ts +2 -2
- package/dist/ssh.js +0 -1
- package/dist/ssh.js.map +1 -1
- package/dist/{types-DhXwOQwD.d.ts → types-BX4ALqoN.d.ts} +76 -4
- package/dist/{types-kiGBF35b.d.ts → types-DLZNyF5t.d.ts} +125 -1
- package/dist/unsandboxed.d.ts +59 -0
- package/dist/unsandboxed.js +32 -0
- package/dist/unsandboxed.js.map +1 -0
- package/dist/{uuid-RVN2T26F.js → uuid-CVTNAPEB.js} +1 -2
- package/dist/{zod-7YXKWYMC.js → zod-VKURGPRT.js} +1 -2
- package/package.json +35 -50
- package/dist/cache-BlBwXXPS.d.ts +0 -38
- package/dist/chunk-5GEX6ZSB.js.map +0 -1
- package/dist/chunk-6MMYCGJQ.js.map +0 -1
- package/dist/chunk-7IQCQI2G.js +0 -94
- package/dist/chunk-7IQCQI2G.js.map +0 -1
- package/dist/chunk-CCM2AXZG.js +0 -16
- package/dist/chunk-CCM2AXZG.js.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-HEQQQGK5.js +0 -131
- package/dist/chunk-HEQQQGK5.js.map +0 -1
- package/dist/chunk-I3JTUFPK.js +0 -171
- package/dist/chunk-I3JTUFPK.js.map +0 -1
- package/dist/chunk-XZN4QZLK.js.map +0 -1
- package/dist/chunk-ZXSDKBYB.js +0 -474
- package/dist/chunk-ZXSDKBYB.js.map +0 -1
- package/dist/client-CRRO2376.js +0 -10
- package/dist/providers/anthropic.d.ts +0 -19
- package/dist/providers/anthropic.js +0 -36
- package/dist/providers/anthropic.js.map +0 -1
- package/dist/providers/bedrock.d.ts +0 -39
- package/dist/providers/bedrock.js +0 -56
- package/dist/providers/bedrock.js.map +0 -1
- package/dist/providers/gemini.d.ts +0 -17
- package/dist/providers/gemini.js +0 -262
- package/dist/providers/gemini.js.map +0 -1
- package/dist/providers/ollama.d.ts +0 -13
- package/dist/providers/ollama.js +0 -20
- package/dist/providers/ollama.js.map +0 -1
- package/dist/providers/openai.d.ts +0 -21
- package/dist/providers/openai.js +0 -9
- package/dist/providers/openrouter.d.ts +0 -16
- package/dist/providers/openrouter.js +0 -24
- package/dist/providers/openrouter.js.map +0 -1
- package/dist/providers/vertex.d.ts +0 -42
- package/dist/providers/vertex.js +0 -68
- package/dist/providers/vertex.js.map +0 -1
- package/dist/sandbox-9qeMTNrD.d.ts +0 -126
- package/dist/types-CD0rUKKT.d.ts +0 -109
- package/dist/uuid-RVN2T26F.js.map +0 -1
- package/dist/zod-7YXKWYMC.js.map +0 -1
- /package/dist/{chunk-DGUM43GV.js.map → client-JJFLE6RT.js.map} +0 -0
- /package/dist/{client-CRRO2376.js.map → history-snip-HAWNAYKY.js.map} +0 -0
- /package/dist/{history-snip-64GYP4ZL.js.map → lsp-3APWNKB2.js.map} +0 -0
- /package/dist/{lsp-PS3BWIHC.js.map → manager-Z5EQ7YYV.js.map} +0 -0
- /package/dist/{manager-DLXK63XC.js.map → mcp-auth-NOIQPF7W.js.map} +0 -0
- /package/dist/{mcp-auth-AEI2R4ZC.js.map → provider-factory-KNBSHXJ6.js.map} +0 -0
- /package/dist/{provider-factory-TUHU3DIG.js.map → render-4VEODRK7.js.map} +0 -0
- /package/dist/{providers/openai.js.map → resolve-AGQZFMKD.js.map} +0 -0
- /package/dist/{render-GRN4ZSSW.js.map → spinner-72JEISPK.js.map} +0 -0
- /package/dist/{resolve-6KUZNEYW.js.map → uuid-CVTNAPEB.js.map} +0 -0
- /package/dist/{spinner-OJNR6NFO.js.map → zod-VKURGPRT.js.map} +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { V as VirtualFs, a as VirtualComputer } from './computer-DzMR92tK.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bundled sandbox: a `VirtualFs` and `VirtualComputer` paired together.
|
|
5
|
+
*
|
|
6
|
+
* Every concrete backend lives on its own subpath so that importing `noumen`
|
|
7
|
+
* never pulls a backend's optional peer deps into the module graph. Pick the
|
|
8
|
+
* one you want:
|
|
9
|
+
*
|
|
10
|
+
* import { LocalSandbox } from "noumen/local" // OS-level sandboxing
|
|
11
|
+
* import { UnsandboxedLocal } from "noumen/unsandboxed" // raw host access
|
|
12
|
+
* import { DockerSandbox } from "noumen/docker" // requires `dockerode`
|
|
13
|
+
* import { E2BSandbox } from "noumen/e2b" // requires `e2b`
|
|
14
|
+
* import { FreestyleSandbox } from "noumen/freestyle" // requires `freestyle-sandboxes`
|
|
15
|
+
* import { SshSandbox } from "noumen/ssh" // requires `ssh2`
|
|
16
|
+
* import { SpritesSandbox } from "noumen/sprites" // no peer dep
|
|
17
|
+
*
|
|
18
|
+
* You can also supply any object that satisfies this shape for custom
|
|
19
|
+
* sandboxes (in-memory, custom cloud backends, etc.).
|
|
20
|
+
*/
|
|
21
|
+
interface Sandbox {
|
|
22
|
+
fs: VirtualFs;
|
|
23
|
+
computer: VirtualComputer;
|
|
24
|
+
/** Optional cleanup — called by Agent.close() to tear down OS-level sandbox state. */
|
|
25
|
+
dispose?(): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Lazily provision the underlying sandbox resource. Idempotent — repeated
|
|
28
|
+
* calls return the same single-flight promise.
|
|
29
|
+
*
|
|
30
|
+
* When `sandboxId` is provided the sandbox reconnects to an existing
|
|
31
|
+
* resource instead of creating a new one. This is used during session
|
|
32
|
+
* resume: the stored sandbox identifier is read from session metadata
|
|
33
|
+
* and passed here so the agent reattaches to its previous container.
|
|
34
|
+
*
|
|
35
|
+
* When omitted a fresh resource is provisioned (for factories that
|
|
36
|
+
* support auto-creation) or the call is a no-op (for factories that
|
|
37
|
+
* were given a pre-created resource up front).
|
|
38
|
+
*/
|
|
39
|
+
init?(sandboxId?: string): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Return the opaque identifier for this sandbox instance so it can be
|
|
42
|
+
* persisted in session metadata and used to reconnect later via `init()`.
|
|
43
|
+
* Returns `undefined` before `init()` has resolved or for sandboxes
|
|
44
|
+
* that don't support reconnection.
|
|
45
|
+
*/
|
|
46
|
+
sandboxId?(): string | undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type { Sandbox as S };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/session/sandbox-index.ts
|
|
2
|
+
import * as nodeFs from "fs/promises";
|
|
3
|
+
import * as nodePath from "path";
|
|
4
|
+
function indexPath(cwd, sessionDir) {
|
|
5
|
+
return nodePath.resolve(cwd, sessionDir, ".sandbox-index.json");
|
|
6
|
+
}
|
|
7
|
+
async function loadSandboxId(cwd, sessionDir, sessionId) {
|
|
8
|
+
try {
|
|
9
|
+
const content = await nodeFs.readFile(indexPath(cwd, sessionDir), "utf-8");
|
|
10
|
+
const index = JSON.parse(content);
|
|
11
|
+
return index[sessionId];
|
|
12
|
+
} catch {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function storeSandboxId(cwd, sessionDir, sessionId, sandboxId) {
|
|
17
|
+
const path = indexPath(cwd, sessionDir);
|
|
18
|
+
let index = {};
|
|
19
|
+
try {
|
|
20
|
+
const content = await nodeFs.readFile(path, "utf-8");
|
|
21
|
+
index = JSON.parse(content);
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
index[sessionId] = sandboxId;
|
|
25
|
+
await nodeFs.mkdir(nodePath.dirname(path), { recursive: true });
|
|
26
|
+
await nodeFs.writeFile(path, JSON.stringify(index, null, 2));
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
loadSandboxId,
|
|
30
|
+
storeSandboxId
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=sandbox-index-ODNREIFA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/session/sandbox-index.ts"],"sourcesContent":["/**\n * Local host file mapping `sessionId -> sandboxId` so the agent can\n * reconnect to auto-created containers (Docker / E2B / Freestyle /\n * Sprites) when a session is resumed — the sandbox's own filesystem\n * may be unreachable at that point.\n *\n * Quarantined in its own module so it can be lazy-loaded from\n * `src/agent.ts`. Keeping `node:fs/promises` + `node:path` out of the\n * Agent's static import graph matters for bundlers that trace\n * dependencies (Next.js NFT, serverless-webpack, etc.) — a top-level\n * `path.resolve(cwd, ...)` with a non-constant `cwd` is the classic\n * \"whole project traced unintentionally\" trigger.\n */\nimport * as nodeFs from \"node:fs/promises\";\nimport * as nodePath from \"node:path\";\n\nfunction indexPath(cwd: string, sessionDir: string): string {\n return nodePath.resolve(cwd, sessionDir, \".sandbox-index.json\");\n}\n\nexport async function loadSandboxId(\n cwd: string,\n sessionDir: string,\n sessionId: string,\n): Promise<string | undefined> {\n try {\n const content = await nodeFs.readFile(indexPath(cwd, sessionDir), \"utf-8\");\n const index = JSON.parse(content) as Record<string, string>;\n return index[sessionId];\n } catch {\n return undefined;\n }\n}\n\nexport async function storeSandboxId(\n cwd: string,\n sessionDir: string,\n sessionId: string,\n sandboxId: string,\n): Promise<void> {\n const path = indexPath(cwd, sessionDir);\n let index: Record<string, string> = {};\n try {\n const content = await nodeFs.readFile(path, \"utf-8\");\n index = JSON.parse(content) as Record<string, string>;\n } catch {\n /* file doesn't exist yet */\n }\n index[sessionId] = sandboxId;\n await nodeFs.mkdir(nodePath.dirname(path), { recursive: true });\n await nodeFs.writeFile(path, JSON.stringify(index, null, 2));\n}\n"],"mappings":";AAaA,YAAY,YAAY;AACxB,YAAY,cAAc;AAE1B,SAAS,UAAU,KAAa,YAA4B;AAC1D,SAAgB,iBAAQ,KAAK,YAAY,qBAAqB;AAChE;AAEA,eAAsB,cACpB,KACA,YACA,WAC6B;AAC7B,MAAI;AACF,UAAM,UAAU,MAAa,gBAAS,UAAU,KAAK,UAAU,GAAG,OAAO;AACzE,UAAM,QAAQ,KAAK,MAAM,OAAO;AAChC,WAAO,MAAM,SAAS;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,eACpB,KACA,YACA,WACA,WACe;AACf,QAAM,OAAO,UAAU,KAAK,UAAU;AACtC,MAAI,QAAgC,CAAC;AACrC,MAAI;AACF,UAAM,UAAU,MAAa,gBAAS,MAAM,OAAO;AACnD,YAAQ,KAAK,MAAM,OAAO;AAAA,EAC5B,QAAQ;AAAA,EAER;AACA,QAAM,SAAS,IAAI;AACnB,QAAa,aAAe,iBAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9D,QAAa,iBAAU,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAC7D;","names":[]}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { ServerResponse, IncomingMessage } from 'node:http';
|
|
2
|
-
import {
|
|
3
|
-
import { S as StreamEvent } from '../types-
|
|
4
|
-
import
|
|
5
|
-
import '../
|
|
6
|
-
import '../
|
|
7
|
-
import '../types-DhXwOQwD.js';
|
|
8
|
-
import '../cache-BlBwXXPS.js';
|
|
2
|
+
import { a as Agent } from '../agent-D0gl-qYi.js';
|
|
3
|
+
import { S as StreamEvent, P as PermissionResponse } from '../types-DLZNyF5t.js';
|
|
4
|
+
import '../sandbox-DAqQo0Tj.js';
|
|
5
|
+
import '../computer-DzMR92tK.js';
|
|
6
|
+
import '../types-BX4ALqoN.js';
|
|
9
7
|
import '../types-2kTLUCnD.js';
|
|
10
8
|
import '@modelcontextprotocol/sdk/client/index.js';
|
|
11
9
|
import '@modelcontextprotocol/sdk/client/auth.js';
|
|
@@ -56,6 +54,18 @@ interface ServerOptions {
|
|
|
56
54
|
cors?: boolean;
|
|
57
55
|
/** Timeout for pending permission/input responses before rejecting (ms). Default 120000. */
|
|
58
56
|
pendingTimeoutMs?: number;
|
|
57
|
+
/**
|
|
58
|
+
* How long `stop()` waits for in-flight requests to drain after aborting
|
|
59
|
+
* active sessions (ms). Default 500. Set to 0 in tests to make teardown
|
|
60
|
+
* instant. The drain is skipped entirely when no sessions are active.
|
|
61
|
+
*/
|
|
62
|
+
shutdownDrainMs?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Minimum interval for the idle-session reaper (ms). Default 1000. Lower
|
|
65
|
+
* values let tests observe reaping quickly; production should use the
|
|
66
|
+
* default to avoid burning CPU on a short idle timeout.
|
|
67
|
+
*/
|
|
68
|
+
idleReaperMinIntervalMs?: number;
|
|
59
69
|
}
|
|
60
70
|
/**
|
|
61
71
|
* Options for `createRequestHandler()` — same as `ServerOptions` but without
|
|
@@ -69,6 +79,7 @@ interface RequestHandlerOptions {
|
|
|
69
79
|
onError?: (err: Error) => void;
|
|
70
80
|
cors?: boolean;
|
|
71
81
|
pendingTimeoutMs?: number;
|
|
82
|
+
idleReaperMinIntervalMs?: number;
|
|
72
83
|
}
|
|
73
84
|
type AuthConfig = {
|
|
74
85
|
type: "bearer";
|
package/dist/server/index.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import "../chunk-DGUM43GV.js";
|
|
2
|
-
|
|
3
1
|
// src/server/index.ts
|
|
4
2
|
import { createServer as createHttpServer } from "http";
|
|
5
3
|
|
|
@@ -203,7 +201,8 @@ function wsSend(ws, data) {
|
|
|
203
201
|
// src/server/index.ts
|
|
204
202
|
var SSE_KEEPALIVE_INTERVAL_MS = 15e3;
|
|
205
203
|
var MAX_BODY_BYTES = 1048576;
|
|
206
|
-
var
|
|
204
|
+
var DEFAULT_SHUTDOWN_DRAIN_MS = 500;
|
|
205
|
+
var DEFAULT_IDLE_REAPER_MIN_INTERVAL_MS = 1e3;
|
|
207
206
|
var WS_PING_INTERVAL_MS = 3e4;
|
|
208
207
|
var NoumenServer = class {
|
|
209
208
|
code;
|
|
@@ -233,10 +232,14 @@ var NoumenServer = class {
|
|
|
233
232
|
clearInterval(this.idleTimer);
|
|
234
233
|
this.idleTimer = null;
|
|
235
234
|
}
|
|
235
|
+
const hadActiveSessions = this.sessions.size > 0;
|
|
236
236
|
for (const session of this.sessions.values()) {
|
|
237
237
|
session.abortController.abort();
|
|
238
238
|
}
|
|
239
|
-
|
|
239
|
+
const drainMs = this.options.shutdownDrainMs ?? DEFAULT_SHUTDOWN_DRAIN_MS;
|
|
240
|
+
if (hadActiveSessions && drainMs > 0) {
|
|
241
|
+
await new Promise((resolve) => setTimeout(resolve, drainMs));
|
|
242
|
+
}
|
|
240
243
|
for (const session of this.sessions.values()) {
|
|
241
244
|
destroySession(this.sessions, session);
|
|
242
245
|
}
|
|
@@ -297,7 +300,8 @@ var NoumenServer = class {
|
|
|
297
300
|
ensureIdleReaper() {
|
|
298
301
|
if (this.idleReaperStarted || !this.options.idleTimeoutMs) return;
|
|
299
302
|
this.idleReaperStarted = true;
|
|
300
|
-
const
|
|
303
|
+
const minInterval = this.options.idleReaperMinIntervalMs ?? DEFAULT_IDLE_REAPER_MIN_INTERVAL_MS;
|
|
304
|
+
const interval = Math.max(this.options.idleTimeoutMs / 2, minInterval);
|
|
301
305
|
this.idleTimer = setInterval(() => reapIdleSessions(this.sessions, this.options.idleTimeoutMs), interval);
|
|
302
306
|
this.idleTimer.unref();
|
|
303
307
|
}
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/index.ts","../../src/server/session-state.ts","../../src/server/event-buffer.ts","../../src/server/ws-dispatch.ts"],"sourcesContent":["import { createServer as createHttpServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport type { Agent } from \"../agent.js\";\nimport type { StreamEvent } from \"../session/types.js\";\nimport type { PermissionResponse } from \"../permissions/types.js\";\n\nimport {\n type SessionState,\n type ConnectionOverrides,\n DEFAULT_PENDING_TIMEOUT_MS,\n createSessionState,\n destroySession,\n reapIdleSessions,\n bridgePermission,\n bridgeUserInput,\n clearPendingPermissionTimer,\n clearPendingInputTimer,\n clearSseKeepalive,\n} from \"./session-state.js\";\n\nimport {\n MAX_EVENT_BUFFER,\n serializeEvent,\n pushEvent,\n getBufferedEventsAfter,\n writeSseEventRaw,\n} from \"./event-buffer.js\";\n\nimport {\n type WsWebSocket,\n type WsDispatchCallbacks,\n handleWsMessage as dispatchWsMessage,\n parseWsMessage,\n wsSend,\n} from \"./ws-dispatch.js\";\n\ntype MaybePromise<T> = T | Promise<T>;\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst SSE_KEEPALIVE_INTERVAL_MS = 15_000;\nconst MAX_BODY_BYTES = 1_048_576; // 1 MB\nconst SHUTDOWN_DRAIN_MS = 500;\nconst WS_PING_INTERVAL_MS = 30_000;\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport interface ServerOptions {\n port: number;\n host?: string;\n /** Enable WebSocket transport (default true). Requires `ws` peer dependency. */\n ws?: boolean;\n auth?: AuthConfig;\n /** Maximum number of concurrent sessions (default unlimited). */\n maxSessions?: number;\n /** Automatically clean up sessions idle longer than this (ms). No timeout by default. */\n idleTimeoutMs?: number;\n /** Called on every new connection; return overrides for the session. */\n onConnection?: (info: ConnectionInfo) => MaybePromise<ConnectionOverrides>;\n onError?: (err: Error) => void;\n /** Enable CORS for browser clients (default true). */\n cors?: boolean;\n /** Timeout for pending permission/input responses before rejecting (ms). Default 120000. */\n pendingTimeoutMs?: number;\n}\n\n/**\n * Options for `createRequestHandler()` — same as `ServerOptions` but without\n * `port` / `host` / `ws` since the caller owns the HTTP server.\n */\nexport interface RequestHandlerOptions {\n auth?: AuthConfig;\n maxSessions?: number;\n idleTimeoutMs?: number;\n onConnection?: (info: ConnectionInfo) => MaybePromise<ConnectionOverrides>;\n onError?: (err: Error) => void;\n cors?: boolean;\n pendingTimeoutMs?: number;\n}\n\nexport type AuthConfig =\n | { type: \"bearer\"; token: string }\n | { type: \"custom\"; verify: (req: IncomingMessage) => MaybePromise<AuthResult | null> };\n\nexport interface AuthResult {\n [key: string]: unknown;\n}\n\nexport interface ConnectionInfo {\n auth: AuthResult;\n remoteAddress?: string;\n}\n\n// Re-export types that consumers might need\nexport type { ConnectionOverrides, SessionState, BufferedEvent, PromiseResolver } from \"./session-state.js\";\n\ntype WsServer = {\n on(event: \"connection\", cb: (ws: WsWebSocket, req: IncomingMessage) => void): void;\n close(cb?: () => void): void;\n};\n\n// ---------------------------------------------------------------------------\n// NoumenServer\n// ---------------------------------------------------------------------------\n\nexport class NoumenServer {\n private code: Agent;\n private options: ServerOptions;\n private httpServer: ReturnType<typeof createHttpServer> | null = null;\n private wss: WsServer | null = null;\n private sessions = new Map<string, SessionState>();\n private idleTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(code: Agent, options: ServerOptions) {\n this.code = code;\n this.options = options;\n }\n\n async start(): Promise<void> {\n this.httpServer = createHttpServer((req, res) => this.handleRequest(req, res));\n\n if (this.options.ws !== false) {\n await this.initWebSocket();\n }\n\n this.ensureIdleReaper();\n\n return new Promise<void>((resolve, reject) => {\n const host = this.options.host ?? \"127.0.0.1\";\n this.httpServer!.listen(this.options.port, host, () => resolve());\n this.httpServer!.once(\"error\", reject);\n });\n }\n\n async stop(): Promise<void> {\n if (this.idleTimer) {\n clearInterval(this.idleTimer);\n this.idleTimer = null;\n }\n\n for (const session of this.sessions.values()) {\n session.abortController.abort();\n }\n await new Promise<void>((resolve) => setTimeout(resolve, SHUTDOWN_DRAIN_MS));\n\n for (const session of this.sessions.values()) {\n destroySession(this.sessions, session);\n }\n\n if (this.wss) {\n await new Promise<void>((resolve) => this.wss!.close(() => resolve()));\n this.wss = null;\n }\n\n if (this.httpServer) {\n if (typeof (this.httpServer as any).closeAllConnections === \"function\") {\n (this.httpServer as any).closeAllConnections();\n }\n await new Promise<void>((resolve, reject) =>\n this.httpServer!.close((err) => (err ? reject(err) : resolve())),\n );\n this.httpServer = null;\n }\n }\n\n getActiveSessions(): Map<string, { id: string; lastActivity: number; done: boolean }> {\n const result = new Map<string, { id: string; lastActivity: number; done: boolean }>();\n for (const [id, s] of this.sessions) {\n result.set(id, { id: s.id, lastActivity: s.lastActivity, done: s.done });\n }\n return result;\n }\n\n // -------------------------------------------------------------------------\n // WebSocket setup\n // -------------------------------------------------------------------------\n\n private async initWebSocket(): Promise<void> {\n let WsServerCtor: new (opts: { server: ReturnType<typeof createHttpServer> }) => WsServer;\n try {\n const ws = await import(\"ws\");\n WsServerCtor = (ws as any).WebSocketServer ?? (ws as any).default?.WebSocketServer;\n } catch {\n throw new Error(\n \"noumen/server: WebSocket support requires the 'ws' package. \" +\n \"Install it with: npm install ws\\n\" +\n \"Or disable WebSocket with { ws: false } in ServerOptions.\",\n );\n }\n\n this.wss = new WsServerCtor({ server: this.httpServer! });\n this.wss.on(\"connection\", (ws, req) => {\n this.handleWsConnection(ws, req).catch((err) =>\n this.options.onError?.(err instanceof Error ? err : new Error(String(err))),\n );\n });\n }\n\n /**\n * Handle an HTTP request. Used internally by `start()` and exposed for\n * `createRequestHandler()` so the same logic can be mounted on an\n * external Express / Fastify / Hono server.\n */\n async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n this.ensureIdleReaper();\n return this.handleHttp(req, res).catch((err) => {\n this.options.onError?.(err instanceof Error ? err : new Error(String(err)));\n if (!res.headersSent) jsonResponse(res, 500, { error: \"Internal server error\" });\n });\n }\n\n private idleReaperStarted = false;\n\n private ensureIdleReaper(): void {\n if (this.idleReaperStarted || !this.options.idleTimeoutMs) return;\n this.idleReaperStarted = true;\n const interval = Math.max(this.options.idleTimeoutMs / 2, 1000);\n this.idleTimer = setInterval(() => reapIdleSessions(this.sessions, this.options.idleTimeoutMs), interval);\n this.idleTimer.unref();\n }\n\n // -------------------------------------------------------------------------\n // HTTP routing\n // -------------------------------------------------------------------------\n\n private async handleHttp(req: IncomingMessage, res: ServerResponse): Promise<void> {\n if (this.options.cors !== false) {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, DELETE, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization, Last-Event-ID\");\n }\n\n const method = req.method ?? \"GET\";\n\n if (method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n const path = url.pathname;\n\n if (path === \"/health\" && method === \"GET\") {\n return jsonResponse(res, 200, { status: \"ok\", sessions: this.sessions.size });\n }\n\n if (this.options.auth) {\n const authResult = await this.authenticate(req);\n if (!authResult) {\n return jsonResponse(res, 401, { error: \"Unauthorized\" });\n }\n }\n\n if (path === \"/sessions\" && method === \"POST\") {\n return this.handleCreateSession(req, res);\n }\n\n if (path === \"/sessions\" && method === \"GET\") {\n return this.handleListSessions(res);\n }\n\n const sessionMatch = path.match(/^\\/sessions\\/([^/]+)(?:\\/(.+))?$/);\n if (sessionMatch) {\n const sessionId = sessionMatch[1];\n const sub = sessionMatch[2] ?? \"\";\n\n if (sub === \"events\" && method === \"GET\") return this.handleSseStream(sessionId, req, res);\n if (sub === \"permissions\" && method === \"POST\") return this.handlePermissionResponse(sessionId, req, res);\n if (sub === \"input\" && method === \"POST\") return this.handleInputResponse(sessionId, req, res);\n if (sub === \"messages\" && method === \"POST\") return this.handleSendMessage(sessionId, req, res);\n if (sub === \"\" && method === \"DELETE\") return this.handleDeleteSession(sessionId, res);\n }\n\n jsonResponse(res, 404, { error: \"Not found\" });\n }\n\n // -------------------------------------------------------------------------\n // Auth\n // -------------------------------------------------------------------------\n\n private async authenticate(req: IncomingMessage): Promise<AuthResult | null> {\n const auth = this.options.auth;\n if (!auth) return {};\n\n if (auth.type === \"bearer\") {\n const header = req.headers.authorization;\n if (header === `Bearer ${auth.token}`) return {};\n return null;\n }\n\n return auth.verify(req);\n }\n\n // -------------------------------------------------------------------------\n // REST handlers\n // -------------------------------------------------------------------------\n\n private async handleCreateSession(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const body = await readBody(req);\n const { prompt, sessionId: requestedId } = body as { prompt?: string; sessionId?: string };\n\n if (!prompt || typeof prompt !== \"string\") {\n return jsonResponse(res, 400, { error: \"Missing required field: prompt\" });\n }\n\n if (this.options.maxSessions && this.sessions.size >= this.options.maxSessions) {\n return jsonResponse(res, 429, { error: \"Maximum sessions reached\" });\n }\n\n const overrides = await this.resolveConnectionOverrides(req);\n const session = createSessionState(this.sessions, requestedId, overrides);\n\n this.runAgentSse(session, prompt, false);\n\n jsonResponse(res, 201, {\n sessionId: session.id,\n eventsUrl: `/sessions/${session.id}/events`,\n });\n }\n\n private handleListSessions(res: ServerResponse): void {\n const sessions = Array.from(this.sessions.values()).map((s) => ({\n id: s.id,\n lastActivity: s.lastActivity,\n done: s.done,\n }));\n jsonResponse(res, 200, sessions);\n }\n\n private handleSseStream(sessionId: string, req: IncomingMessage, res: ServerResponse): void {\n const session = this.sessions.get(sessionId);\n if (!session) return jsonResponse(res, 404, { error: \"Session not found\" });\n\n if (session.sseResponse) {\n const oldRes = session.sseResponse;\n writeSseEventRaw(oldRes, session.sequenceNum + 1, { type: \"subscriber_replaced\" });\n oldRes.end();\n clearSseKeepalive(session);\n session.sseResponse = null;\n }\n\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"Connection\": \"keep-alive\",\n \"X-Accel-Buffering\": \"no\",\n });\n\n const lastEventId = req.headers[\"last-event-id\"] as string | undefined;\n const resumeAfterSeq = lastEventId ? parseInt(lastEventId, 10) : 0;\n\n const eventsToReplay = getBufferedEventsAfter(session.eventBuffer, resumeAfterSeq);\n for (const buffered of eventsToReplay) {\n writeSseEventRaw(res, buffered.seq, serializeEvent(buffered.event));\n }\n session.eventBuffer = [];\n session.sseResponse = res;\n\n this.startSseKeepalive(session);\n\n res.on(\"close\", () => {\n if (session.sseResponse === res) {\n clearSseKeepalive(session);\n session.sseResponse = null;\n }\n });\n }\n\n private async handlePermissionResponse(\n sessionId: string,\n req: IncomingMessage,\n res: ServerResponse,\n ): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (!session) return jsonResponse(res, 404, { error: \"Session not found\" });\n if (!session.pendingPermission) return jsonResponse(res, 409, { error: \"No pending permission request\" });\n\n const body = (await readBody(req)) as PermissionResponse;\n clearPendingPermissionTimer(session);\n session.pendingPermission.resolve(body);\n session.pendingPermission = null;\n jsonResponse(res, 200, { ok: true });\n }\n\n private async handleInputResponse(\n sessionId: string,\n req: IncomingMessage,\n res: ServerResponse,\n ): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (!session) return jsonResponse(res, 404, { error: \"Session not found\" });\n if (!session.pendingInput) return jsonResponse(res, 409, { error: \"No pending input request\" });\n\n const body = (await readBody(req)) as { answer?: string };\n if (typeof body.answer !== \"string\") {\n return jsonResponse(res, 400, { error: \"Missing required field: answer\" });\n }\n\n clearPendingInputTimer(session);\n session.pendingInput.resolve(body.answer);\n session.pendingInput = null;\n jsonResponse(res, 200, { ok: true });\n }\n\n private async handleSendMessage(\n sessionId: string,\n req: IncomingMessage,\n res: ServerResponse,\n ): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (!session) return jsonResponse(res, 404, { error: \"Session not found\" });\n if (!session.done) return jsonResponse(res, 409, { error: \"Session is still running\" });\n\n const body = (await readBody(req)) as { prompt?: string };\n if (!body.prompt || typeof body.prompt !== \"string\") {\n return jsonResponse(res, 400, { error: \"Missing required field: prompt\" });\n }\n\n session.done = false;\n session.abortController = new AbortController();\n this.runAgentSse(session, body.prompt, true);\n jsonResponse(res, 200, { ok: true });\n }\n\n private handleDeleteSession(sessionId: string, res: ServerResponse): void {\n const session = this.sessions.get(sessionId);\n if (!session) return jsonResponse(res, 404, { error: \"Session not found\" });\n destroySession(this.sessions, session);\n jsonResponse(res, 200, { ok: true });\n }\n\n // -------------------------------------------------------------------------\n // WebSocket handling\n // -------------------------------------------------------------------------\n\n private async handleWsConnection(ws: WsWebSocket, req: IncomingMessage): Promise<void> {\n if (this.options.auth) {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n const tokenParam = url.searchParams.get(\"token\");\n if (tokenParam && this.options.auth.type === \"bearer\") {\n if (tokenParam !== this.options.auth.token) {\n ws.close();\n return;\n }\n } else {\n const authResult = await this.authenticate(req);\n if (!authResult) {\n ws.close();\n return;\n }\n }\n }\n\n const wsSessions = new Set<string>();\n\n let pongReceived = true;\n const pingTimer = setInterval(() => {\n if (!pongReceived) {\n ws.close();\n return;\n }\n pongReceived = false;\n try { ws.ping(); } catch { /* connection may already be closing */ }\n }, WS_PING_INTERVAL_MS);\n\n ws.on(\"pong\", () => { pongReceived = true; });\n\n const callbacks: WsDispatchCallbacks = {\n onRun: async (prompt, requestedSessionId) => {\n const overrides = await this.resolveConnectionOverrides(req);\n const session = createSessionState(this.sessions, requestedSessionId, overrides);\n wsSessions.add(session.id);\n this.runAgentWs(session, prompt, ws, false);\n return session.id;\n },\n onMessage: (sessionId, prompt) => {\n const session = this.sessions.get(sessionId);\n if (!session) { wsSend(ws, { type: \"error\", error: \"Session not found\" }); return; }\n if (!session.done) { wsSend(ws, { type: \"error\", error: \"Session is still running\" }); return; }\n session.done = false;\n session.abortController = new AbortController();\n this.runAgentWs(session, prompt, ws, true);\n },\n onPermissionResponse: (sessionId, response) => {\n const session = this.sessions.get(sessionId);\n if (!session?.pendingPermission) return;\n clearPendingPermissionTimer(session);\n session.pendingPermission.resolve(response);\n session.pendingPermission = null;\n },\n onInputResponse: (sessionId, answer) => {\n const session = this.sessions.get(sessionId);\n if (!session?.pendingInput) return;\n clearPendingInputTimer(session);\n session.pendingInput.resolve(answer);\n session.pendingInput = null;\n },\n onAbort: (sessionId) => {\n const session = this.sessions.get(sessionId);\n if (session) destroySession(this.sessions, session);\n },\n };\n\n ws.on(\"message\", async (raw) => {\n try {\n const msg = parseWsMessage(raw);\n if (!msg) { wsSend(ws, { type: \"error\", error: \"Invalid JSON\" }); return; }\n const result = await dispatchWsMessage(msg, {\n maxSessions: this.options.maxSessions,\n currentSessionCount: this.sessions.size,\n }, callbacks);\n if (result.type === \"error\") {\n wsSend(ws, { type: \"error\", error: result.error });\n } else if (result.type === \"session_created\") {\n wsSend(ws, { type: \"session_created\", sessionId: result.sessionId });\n }\n } catch (err) {\n wsSend(ws, { type: \"error\", error: String(err) });\n }\n });\n\n ws.on(\"close\", () => {\n clearInterval(pingTimer);\n for (const sid of wsSessions) {\n const session = this.sessions.get(sid);\n if (session) destroySession(this.sessions, session);\n }\n });\n\n ws.on(\"error\", () => {\n clearInterval(pingTimer);\n });\n }\n\n // -------------------------------------------------------------------------\n // Agent runners\n // -------------------------------------------------------------------------\n\n private async makeThread(session: SessionState, resume: boolean) {\n const timeoutMs = this.options.pendingTimeoutMs ?? DEFAULT_PENDING_TIMEOUT_MS;\n const handlers = {\n cwd: session.cwd,\n permissionHandler: (_req: import(\"../permissions/types.js\").PermissionRequest) =>\n bridgePermission(this.sessions, session.id, timeoutMs),\n userInputHandler: (_q: string) =>\n bridgeUserInput(this.sessions, session.id, timeoutMs),\n };\n\n return resume\n ? this.code.resumeThread(session.id, handlers)\n : this.code.createThread({ sessionId: session.id, ...handlers });\n }\n\n private runAgentSse(session: SessionState, prompt: string, resume: boolean): void {\n const run = async () => {\n try {\n const thread = await this.makeThread(session, resume);\n for await (const event of thread.run(prompt, { signal: session.abortController.signal })) {\n pushEvent(session, event);\n session.lastActivity = Date.now();\n }\n } catch (err) {\n if ((err as Error).name !== \"AbortError\") {\n pushEvent(session, {\n type: \"error\",\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n } finally {\n session.done = true;\n }\n };\n run().catch((err) => this.options.onError?.(err instanceof Error ? err : new Error(String(err))));\n }\n\n private runAgentWs(session: SessionState, prompt: string, ws: WsWebSocket, resume: boolean): void {\n const run = async () => {\n try {\n const thread = await this.makeThread(session, resume);\n for await (const event of thread.run(prompt, { signal: session.abortController.signal })) {\n session.sequenceNum++;\n wsSend(ws, { ...serializeEvent(event), sessionId: session.id, seq: session.sequenceNum });\n session.lastActivity = Date.now();\n }\n } catch (err) {\n if ((err as Error).name !== \"AbortError\") {\n wsSend(ws, { type: \"error\", sessionId: session.id, error: String(err) });\n }\n } finally {\n session.done = true;\n }\n };\n run().catch((err) => this.options.onError?.(err instanceof Error ? err : new Error(String(err))));\n }\n\n private startSseKeepalive(session: SessionState): void {\n clearSseKeepalive(session);\n session.sseKeepaliveTimer = setInterval(() => {\n if (session.sseResponse && !session.sseResponse.destroyed) {\n session.sseResponse.write(\":keepalive\\n\\n\");\n }\n }, SSE_KEEPALIVE_INTERVAL_MS);\n session.sseKeepaliveTimer.unref();\n }\n\n private async resolveConnectionOverrides(req: IncomingMessage): Promise<ConnectionOverrides> {\n if (!this.options.onConnection) return {};\n const auth = (await this.authenticate(req)) ?? {};\n return this.options.onConnection({ auth, remoteAddress: req.socket.remoteAddress });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport function createServer(code: Agent, options: ServerOptions): NoumenServer {\n return new NoumenServer(code, options);\n}\n\n/**\n * Create a `(req, res)` handler that can be mounted on any Node HTTP\n * framework (Express, Fastify, Hono, bare `http.createServer`, etc.).\n *\n * ```ts\n * import express from \"express\";\n * import { createRequestHandler } from \"noumen/server\";\n *\n * const app = express();\n * app.use(\"/agent\", createRequestHandler(code, { auth: { type: \"bearer\", token: \"...\" } }));\n * ```\n *\n * WebSocket is not supported in middleware mode — use `createServer()` for WS.\n */\nexport function createRequestHandler(\n code: Agent,\n options?: RequestHandlerOptions,\n): (req: IncomingMessage, res: ServerResponse) => void {\n const serverOpts: ServerOptions = {\n port: 0,\n ws: false,\n ...options,\n };\n const server = new NoumenServer(code, serverOpts);\n return (req, res) => { server.handleRequest(req, res); };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction jsonResponse(res: ServerResponse, status: number, body: unknown): void {\n const json = JSON.stringify(body);\n res.writeHead(status, {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(json),\n });\n res.end(json);\n}\n\nfunction readBody(req: IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let totalBytes = 0;\n let rejected = false;\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => {\n if (rejected) return;\n totalBytes += chunk.length;\n if (totalBytes > MAX_BODY_BYTES) {\n rejected = true;\n req.destroy();\n reject(new Error(\"Request body too large\"));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (rejected) return;\n try {\n const raw = Buffer.concat(chunks).toString();\n resolve(raw ? JSON.parse(raw) : {});\n } catch (err) {\n reject(err);\n }\n });\n req.on(\"error\", (err) => {\n if (!rejected) reject(err);\n });\n });\n}\n","import { randomUUID } from \"node:crypto\";\nimport type { ServerResponse } from \"node:http\";\nimport type { StreamEvent } from \"../session/types.js\";\nimport type { PermissionResponse } from \"../permissions/types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface PromiseResolver<T> {\n resolve: (value: T) => void;\n reject: (err: Error) => void;\n}\n\nexport interface BufferedEvent {\n seq: number;\n event: StreamEvent;\n}\n\nexport interface SessionState {\n id: string;\n abortController: AbortController;\n pendingPermission: PromiseResolver<PermissionResponse> | null;\n pendingInput: PromiseResolver<string> | null;\n pendingPermissionTimer: ReturnType<typeof setTimeout> | null;\n pendingInputTimer: ReturnType<typeof setTimeout> | null;\n lastActivity: number;\n sseResponse: ServerResponse | null;\n sseKeepaliveTimer: ReturnType<typeof setInterval> | null;\n eventBuffer: BufferedEvent[];\n sequenceNum: number;\n done: boolean;\n cwd?: string;\n}\n\nexport interface ConnectionOverrides {\n cwd?: string;\n}\n\nexport const DEFAULT_PENDING_TIMEOUT_MS = 120_000; // 2 minutes\n\n// ---------------------------------------------------------------------------\n// Session lifecycle\n// ---------------------------------------------------------------------------\n\nexport function createSessionState(\n sessions: Map<string, SessionState>,\n requestedId: string | undefined,\n overrides: ConnectionOverrides,\n): SessionState {\n if (requestedId && sessions.has(requestedId)) {\n throw new Error(`Session ${requestedId} already exists`);\n }\n const sessionId = requestedId ?? randomUUID();\n\n const session: SessionState = {\n id: sessionId,\n abortController: new AbortController(),\n pendingPermission: null,\n pendingInput: null,\n pendingPermissionTimer: null,\n pendingInputTimer: null,\n lastActivity: Date.now(),\n sseResponse: null,\n sseKeepaliveTimer: null,\n eventBuffer: [],\n sequenceNum: 0,\n done: false,\n cwd: overrides.cwd,\n };\n\n sessions.set(sessionId, session);\n return session;\n}\n\nexport function destroySession(\n sessions: Map<string, SessionState>,\n session: SessionState,\n): void {\n session.abortController.abort();\n clearSseKeepalive(session);\n clearPendingPermissionTimer(session);\n clearPendingInputTimer(session);\n if (session.pendingPermission) {\n session.pendingPermission.reject(new Error(\"Session aborted\"));\n session.pendingPermission = null;\n }\n if (session.pendingInput) {\n session.pendingInput.reject(new Error(\"Session aborted\"));\n session.pendingInput = null;\n }\n if (session.sseResponse) {\n session.sseResponse.end();\n session.sseResponse = null;\n }\n sessions.delete(session.id);\n}\n\nexport function reapIdleSessions(\n sessions: Map<string, SessionState>,\n timeoutMs: number | undefined,\n): void {\n if (!timeoutMs) return;\n const now = Date.now();\n for (const session of sessions.values()) {\n if (now - session.lastActivity > timeoutMs) {\n destroySession(sessions, session);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Permission / input bridging\n// ---------------------------------------------------------------------------\n\nexport function bridgePermission(\n sessions: Map<string, SessionState>,\n sessionId: string,\n timeoutMs: number,\n): Promise<PermissionResponse> {\n const session = sessions.get(sessionId);\n if (!session) return Promise.reject(new Error(\"Session not found\"));\n return new Promise<PermissionResponse>((resolve, reject) => {\n session.pendingPermission = { resolve, reject };\n session.pendingPermissionTimer = setTimeout(() => {\n session.pendingPermissionTimer = null;\n if (session.pendingPermission) {\n session.pendingPermission.reject(new Error(\"Permission request timed out\"));\n session.pendingPermission = null;\n }\n }, timeoutMs);\n });\n}\n\nexport function bridgeUserInput(\n sessions: Map<string, SessionState>,\n sessionId: string,\n timeoutMs: number,\n): Promise<string> {\n const session = sessions.get(sessionId);\n if (!session) return Promise.reject(new Error(\"Session not found\"));\n return new Promise<string>((resolve, reject) => {\n session.pendingInput = { resolve, reject };\n session.pendingInputTimer = setTimeout(() => {\n session.pendingInputTimer = null;\n if (session.pendingInput) {\n session.pendingInput.reject(new Error(\"User input request timed out\"));\n session.pendingInput = null;\n }\n }, timeoutMs);\n });\n}\n\n// ---------------------------------------------------------------------------\n// Timer helpers\n// ---------------------------------------------------------------------------\n\nexport function clearPendingPermissionTimer(session: SessionState): void {\n if (session.pendingPermissionTimer) {\n clearTimeout(session.pendingPermissionTimer);\n session.pendingPermissionTimer = null;\n }\n}\n\nexport function clearPendingInputTimer(session: SessionState): void {\n if (session.pendingInputTimer) {\n clearTimeout(session.pendingInputTimer);\n session.pendingInputTimer = null;\n }\n}\n\nexport function clearSseKeepalive(session: SessionState): void {\n if (session.sseKeepaliveTimer) {\n clearInterval(session.sseKeepaliveTimer);\n session.sseKeepaliveTimer = null;\n }\n}\n","import type { ServerResponse } from \"node:http\";\nimport type { StreamEvent } from \"../session/types.js\";\nimport type { SessionState, BufferedEvent } from \"./session-state.js\";\n\nexport const MAX_EVENT_BUFFER = 1000;\n\n/**\n * Serialize a StreamEvent to a JSON-safe object. Error instances are\n * converted to `{ message, name }` since `JSON.stringify(new Error())`\n * produces `{}`.\n */\nexport function serializeEvent(event: StreamEvent): Record<string, unknown> {\n if (event.type === \"error\") {\n return { type: \"error\", error: { message: event.error.message, name: event.error.name } };\n }\n if (event.type === \"retry_exhausted\") {\n return { ...event, error: { message: event.error.message, name: event.error.name } };\n }\n if (event.type === \"retry_attempt\") {\n return { ...event, error: { message: event.error.message, name: event.error.name } };\n }\n return event as unknown as Record<string, unknown>;\n}\n\n/**\n * Push a stream event into the session's buffer, incrementing the sequence\n * number and writing to the live SSE response if one is attached.\n */\nexport function pushEvent(session: SessionState, event: StreamEvent): void {\n session.sequenceNum++;\n const seq = session.sequenceNum;\n\n if (session.eventBuffer.length >= MAX_EVENT_BUFFER) {\n session.eventBuffer.shift();\n }\n session.eventBuffer.push({ seq, event });\n\n if (session.sseResponse) {\n writeSseEventRaw(session.sseResponse, seq, serializeEvent(event));\n }\n}\n\n/**\n * Return buffered events whose sequence number is greater than `afterSeq`.\n */\nexport function getBufferedEventsAfter(\n buffer: BufferedEvent[],\n afterSeq: number,\n): BufferedEvent[] {\n if (!afterSeq) return [...buffer];\n return buffer.filter((e) => e.seq > afterSeq);\n}\n\nexport function writeSseEventRaw(\n res: ServerResponse,\n seq: number,\n data: Record<string, unknown>,\n): void {\n res.write(`id: ${seq}\\ndata: ${JSON.stringify(data)}\\n\\n`);\n}\n","import type { PermissionResponse } from \"../permissions/types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type WsWebSocket = {\n on(event: \"message\", cb: (data: Buffer | string) => void): void;\n on(event: \"close\", cb: () => void): void;\n on(event: \"error\", cb: (err: Error) => void): void;\n on(event: \"pong\", cb: () => void): void;\n send(data: string): void;\n ping(): void;\n close(): void;\n readyState: number;\n};\n\nexport interface WsDispatchCallbacks {\n /** Start a new session with the given prompt, optional requested session ID. */\n onRun: (prompt: string, requestedSessionId: string | undefined) => Promise<string>;\n /** Send a follow-up message to an existing session. */\n onMessage: (sessionId: string, prompt: string) => void;\n /** Forward a permission response to a session. */\n onPermissionResponse: (sessionId: string, response: PermissionResponse) => void;\n /** Forward a user input response to a session. */\n onInputResponse: (sessionId: string, answer: string) => void;\n /** Abort/destroy a session. */\n onAbort: (sessionId: string) => void;\n}\n\nexport interface WsDispatchContext {\n maxSessions: number | undefined;\n currentSessionCount: number;\n}\n\nexport type WsDispatchResult =\n | { type: \"ok\" }\n | { type: \"error\"; error: string }\n | { type: \"session_created\"; sessionId: string }\n\n// ---------------------------------------------------------------------------\n// Parsing\n// ---------------------------------------------------------------------------\n\nexport function parseWsMessage(raw: Buffer | string): Record<string, unknown> | null {\n try {\n return JSON.parse(typeof raw === \"string\" ? raw : raw.toString());\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Dispatch\n// ---------------------------------------------------------------------------\n\nexport async function handleWsMessage(\n msg: Record<string, unknown>,\n ctx: WsDispatchContext,\n callbacks: WsDispatchCallbacks,\n): Promise<WsDispatchResult> {\n const msgType = msg.type as string;\n\n if (msgType === \"run\") {\n if (ctx.maxSessions && ctx.currentSessionCount >= ctx.maxSessions) {\n return { type: \"error\", error: \"Maximum sessions reached\" };\n }\n if (typeof msg.prompt !== \"string\" || !msg.prompt.trim()) {\n return { type: \"error\", error: \"Missing or empty prompt\" };\n }\n try {\n const sessionId = await callbacks.onRun(\n msg.prompt,\n msg.sessionId as string | undefined,\n );\n return { type: \"session_created\", sessionId };\n } catch (err) {\n return { type: \"error\", error: err instanceof Error ? err.message : String(err) };\n }\n }\n\n if (msgType === \"message\") {\n if (typeof msg.prompt !== \"string\" || !msg.prompt.trim()) {\n return { type: \"error\", error: \"Missing or empty prompt\" };\n }\n const sessionId = msg.sessionId as string;\n if (!sessionId) {\n return { type: \"error\", error: \"Missing sessionId\" };\n }\n callbacks.onMessage(sessionId, msg.prompt);\n return { type: \"ok\" };\n }\n\n if (msgType === \"permission_response\") {\n const sessionId = msg.sessionId as string;\n const { sessionId: _sid, type: _type, ...response } = msg;\n callbacks.onPermissionResponse(sessionId, response as unknown as PermissionResponse);\n return { type: \"ok\" };\n }\n\n if (msgType === \"input_response\") {\n const sessionId = msg.sessionId as string;\n callbacks.onInputResponse(sessionId, (msg.answer as string) ?? \"\");\n return { type: \"ok\" };\n }\n\n if (msgType === \"abort\") {\n const sessionId = msg.sessionId as string;\n if (sessionId) callbacks.onAbort(sessionId);\n return { type: \"ok\" };\n }\n\n return { type: \"ok\" };\n}\n\nexport function wsSend(ws: WsWebSocket, data: unknown): void {\n if (ws.readyState === 1) ws.send(JSON.stringify(data));\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/index.ts","../../src/server/session-state.ts","../../src/server/event-buffer.ts","../../src/server/ws-dispatch.ts"],"sourcesContent":["import { createServer as createHttpServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport type { Agent } from \"../agent.js\";\nimport type { StreamEvent } from \"../session/types.js\";\nimport type { PermissionResponse } from \"../permissions/types.js\";\n\nimport {\n type SessionState,\n type ConnectionOverrides,\n DEFAULT_PENDING_TIMEOUT_MS,\n createSessionState,\n destroySession,\n reapIdleSessions,\n bridgePermission,\n bridgeUserInput,\n clearPendingPermissionTimer,\n clearPendingInputTimer,\n clearSseKeepalive,\n} from \"./session-state.js\";\n\nimport {\n MAX_EVENT_BUFFER,\n serializeEvent,\n pushEvent,\n getBufferedEventsAfter,\n writeSseEventRaw,\n} from \"./event-buffer.js\";\n\nimport {\n type WsWebSocket,\n type WsDispatchCallbacks,\n handleWsMessage as dispatchWsMessage,\n parseWsMessage,\n wsSend,\n} from \"./ws-dispatch.js\";\n\ntype MaybePromise<T> = T | Promise<T>;\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst SSE_KEEPALIVE_INTERVAL_MS = 15_000;\nconst MAX_BODY_BYTES = 1_048_576; // 1 MB\nconst DEFAULT_SHUTDOWN_DRAIN_MS = 500;\nconst DEFAULT_IDLE_REAPER_MIN_INTERVAL_MS = 1000;\nconst WS_PING_INTERVAL_MS = 30_000;\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport interface ServerOptions {\n port: number;\n host?: string;\n /** Enable WebSocket transport (default true). Requires `ws` peer dependency. */\n ws?: boolean;\n auth?: AuthConfig;\n /** Maximum number of concurrent sessions (default unlimited). */\n maxSessions?: number;\n /** Automatically clean up sessions idle longer than this (ms). No timeout by default. */\n idleTimeoutMs?: number;\n /** Called on every new connection; return overrides for the session. */\n onConnection?: (info: ConnectionInfo) => MaybePromise<ConnectionOverrides>;\n onError?: (err: Error) => void;\n /** Enable CORS for browser clients (default true). */\n cors?: boolean;\n /** Timeout for pending permission/input responses before rejecting (ms). Default 120000. */\n pendingTimeoutMs?: number;\n /**\n * How long `stop()` waits for in-flight requests to drain after aborting\n * active sessions (ms). Default 500. Set to 0 in tests to make teardown\n * instant. The drain is skipped entirely when no sessions are active.\n */\n shutdownDrainMs?: number;\n /**\n * Minimum interval for the idle-session reaper (ms). Default 1000. Lower\n * values let tests observe reaping quickly; production should use the\n * default to avoid burning CPU on a short idle timeout.\n */\n idleReaperMinIntervalMs?: number;\n}\n\n/**\n * Options for `createRequestHandler()` — same as `ServerOptions` but without\n * `port` / `host` / `ws` since the caller owns the HTTP server.\n */\nexport interface RequestHandlerOptions {\n auth?: AuthConfig;\n maxSessions?: number;\n idleTimeoutMs?: number;\n onConnection?: (info: ConnectionInfo) => MaybePromise<ConnectionOverrides>;\n onError?: (err: Error) => void;\n cors?: boolean;\n pendingTimeoutMs?: number;\n idleReaperMinIntervalMs?: number;\n}\n\nexport type AuthConfig =\n | { type: \"bearer\"; token: string }\n | { type: \"custom\"; verify: (req: IncomingMessage) => MaybePromise<AuthResult | null> };\n\nexport interface AuthResult {\n [key: string]: unknown;\n}\n\nexport interface ConnectionInfo {\n auth: AuthResult;\n remoteAddress?: string;\n}\n\n// Re-export types that consumers might need\nexport type { ConnectionOverrides, SessionState, BufferedEvent, PromiseResolver } from \"./session-state.js\";\n\ntype WsServer = {\n on(event: \"connection\", cb: (ws: WsWebSocket, req: IncomingMessage) => void): void;\n close(cb?: () => void): void;\n};\n\n// ---------------------------------------------------------------------------\n// NoumenServer\n// ---------------------------------------------------------------------------\n\nexport class NoumenServer {\n private code: Agent;\n private options: ServerOptions;\n private httpServer: ReturnType<typeof createHttpServer> | null = null;\n private wss: WsServer | null = null;\n private sessions = new Map<string, SessionState>();\n private idleTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(code: Agent, options: ServerOptions) {\n this.code = code;\n this.options = options;\n }\n\n async start(): Promise<void> {\n this.httpServer = createHttpServer((req, res) => this.handleRequest(req, res));\n\n if (this.options.ws !== false) {\n await this.initWebSocket();\n }\n\n this.ensureIdleReaper();\n\n return new Promise<void>((resolve, reject) => {\n const host = this.options.host ?? \"127.0.0.1\";\n this.httpServer!.listen(this.options.port, host, () => resolve());\n this.httpServer!.once(\"error\", reject);\n });\n }\n\n async stop(): Promise<void> {\n if (this.idleTimer) {\n clearInterval(this.idleTimer);\n this.idleTimer = null;\n }\n\n const hadActiveSessions = this.sessions.size > 0;\n for (const session of this.sessions.values()) {\n session.abortController.abort();\n }\n\n const drainMs = this.options.shutdownDrainMs ?? DEFAULT_SHUTDOWN_DRAIN_MS;\n if (hadActiveSessions && drainMs > 0) {\n await new Promise<void>((resolve) => setTimeout(resolve, drainMs));\n }\n\n for (const session of this.sessions.values()) {\n destroySession(this.sessions, session);\n }\n\n if (this.wss) {\n await new Promise<void>((resolve) => this.wss!.close(() => resolve()));\n this.wss = null;\n }\n\n if (this.httpServer) {\n if (typeof (this.httpServer as any).closeAllConnections === \"function\") {\n (this.httpServer as any).closeAllConnections();\n }\n await new Promise<void>((resolve, reject) =>\n this.httpServer!.close((err) => (err ? reject(err) : resolve())),\n );\n this.httpServer = null;\n }\n }\n\n getActiveSessions(): Map<string, { id: string; lastActivity: number; done: boolean }> {\n const result = new Map<string, { id: string; lastActivity: number; done: boolean }>();\n for (const [id, s] of this.sessions) {\n result.set(id, { id: s.id, lastActivity: s.lastActivity, done: s.done });\n }\n return result;\n }\n\n // -------------------------------------------------------------------------\n // WebSocket setup\n // -------------------------------------------------------------------------\n\n private async initWebSocket(): Promise<void> {\n let WsServerCtor: new (opts: { server: ReturnType<typeof createHttpServer> }) => WsServer;\n try {\n const ws = await import(\"ws\");\n WsServerCtor = (ws as any).WebSocketServer ?? (ws as any).default?.WebSocketServer;\n } catch {\n throw new Error(\n \"noumen/server: WebSocket support requires the 'ws' package. \" +\n \"Install it with: npm install ws\\n\" +\n \"Or disable WebSocket with { ws: false } in ServerOptions.\",\n );\n }\n\n this.wss = new WsServerCtor({ server: this.httpServer! });\n this.wss.on(\"connection\", (ws, req) => {\n this.handleWsConnection(ws, req).catch((err) =>\n this.options.onError?.(err instanceof Error ? err : new Error(String(err))),\n );\n });\n }\n\n /**\n * Handle an HTTP request. Used internally by `start()` and exposed for\n * `createRequestHandler()` so the same logic can be mounted on an\n * external Express / Fastify / Hono server.\n */\n async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n this.ensureIdleReaper();\n return this.handleHttp(req, res).catch((err) => {\n this.options.onError?.(err instanceof Error ? err : new Error(String(err)));\n if (!res.headersSent) jsonResponse(res, 500, { error: \"Internal server error\" });\n });\n }\n\n private idleReaperStarted = false;\n\n private ensureIdleReaper(): void {\n if (this.idleReaperStarted || !this.options.idleTimeoutMs) return;\n this.idleReaperStarted = true;\n const minInterval = this.options.idleReaperMinIntervalMs ?? DEFAULT_IDLE_REAPER_MIN_INTERVAL_MS;\n const interval = Math.max(this.options.idleTimeoutMs / 2, minInterval);\n this.idleTimer = setInterval(() => reapIdleSessions(this.sessions, this.options.idleTimeoutMs), interval);\n this.idleTimer.unref();\n }\n\n // -------------------------------------------------------------------------\n // HTTP routing\n // -------------------------------------------------------------------------\n\n private async handleHttp(req: IncomingMessage, res: ServerResponse): Promise<void> {\n if (this.options.cors !== false) {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, DELETE, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization, Last-Event-ID\");\n }\n\n const method = req.method ?? \"GET\";\n\n if (method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n const path = url.pathname;\n\n if (path === \"/health\" && method === \"GET\") {\n return jsonResponse(res, 200, { status: \"ok\", sessions: this.sessions.size });\n }\n\n if (this.options.auth) {\n const authResult = await this.authenticate(req);\n if (!authResult) {\n return jsonResponse(res, 401, { error: \"Unauthorized\" });\n }\n }\n\n if (path === \"/sessions\" && method === \"POST\") {\n return this.handleCreateSession(req, res);\n }\n\n if (path === \"/sessions\" && method === \"GET\") {\n return this.handleListSessions(res);\n }\n\n const sessionMatch = path.match(/^\\/sessions\\/([^/]+)(?:\\/(.+))?$/);\n if (sessionMatch) {\n const sessionId = sessionMatch[1];\n const sub = sessionMatch[2] ?? \"\";\n\n if (sub === \"events\" && method === \"GET\") return this.handleSseStream(sessionId, req, res);\n if (sub === \"permissions\" && method === \"POST\") return this.handlePermissionResponse(sessionId, req, res);\n if (sub === \"input\" && method === \"POST\") return this.handleInputResponse(sessionId, req, res);\n if (sub === \"messages\" && method === \"POST\") return this.handleSendMessage(sessionId, req, res);\n if (sub === \"\" && method === \"DELETE\") return this.handleDeleteSession(sessionId, res);\n }\n\n jsonResponse(res, 404, { error: \"Not found\" });\n }\n\n // -------------------------------------------------------------------------\n // Auth\n // -------------------------------------------------------------------------\n\n private async authenticate(req: IncomingMessage): Promise<AuthResult | null> {\n const auth = this.options.auth;\n if (!auth) return {};\n\n if (auth.type === \"bearer\") {\n const header = req.headers.authorization;\n if (header === `Bearer ${auth.token}`) return {};\n return null;\n }\n\n return auth.verify(req);\n }\n\n // -------------------------------------------------------------------------\n // REST handlers\n // -------------------------------------------------------------------------\n\n private async handleCreateSession(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const body = await readBody(req);\n const { prompt, sessionId: requestedId } = body as { prompt?: string; sessionId?: string };\n\n if (!prompt || typeof prompt !== \"string\") {\n return jsonResponse(res, 400, { error: \"Missing required field: prompt\" });\n }\n\n if (this.options.maxSessions && this.sessions.size >= this.options.maxSessions) {\n return jsonResponse(res, 429, { error: \"Maximum sessions reached\" });\n }\n\n const overrides = await this.resolveConnectionOverrides(req);\n const session = createSessionState(this.sessions, requestedId, overrides);\n\n this.runAgentSse(session, prompt, false);\n\n jsonResponse(res, 201, {\n sessionId: session.id,\n eventsUrl: `/sessions/${session.id}/events`,\n });\n }\n\n private handleListSessions(res: ServerResponse): void {\n const sessions = Array.from(this.sessions.values()).map((s) => ({\n id: s.id,\n lastActivity: s.lastActivity,\n done: s.done,\n }));\n jsonResponse(res, 200, sessions);\n }\n\n private handleSseStream(sessionId: string, req: IncomingMessage, res: ServerResponse): void {\n const session = this.sessions.get(sessionId);\n if (!session) return jsonResponse(res, 404, { error: \"Session not found\" });\n\n if (session.sseResponse) {\n const oldRes = session.sseResponse;\n writeSseEventRaw(oldRes, session.sequenceNum + 1, { type: \"subscriber_replaced\" });\n oldRes.end();\n clearSseKeepalive(session);\n session.sseResponse = null;\n }\n\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"Connection\": \"keep-alive\",\n \"X-Accel-Buffering\": \"no\",\n });\n\n const lastEventId = req.headers[\"last-event-id\"] as string | undefined;\n const resumeAfterSeq = lastEventId ? parseInt(lastEventId, 10) : 0;\n\n const eventsToReplay = getBufferedEventsAfter(session.eventBuffer, resumeAfterSeq);\n for (const buffered of eventsToReplay) {\n writeSseEventRaw(res, buffered.seq, serializeEvent(buffered.event));\n }\n session.eventBuffer = [];\n session.sseResponse = res;\n\n this.startSseKeepalive(session);\n\n res.on(\"close\", () => {\n if (session.sseResponse === res) {\n clearSseKeepalive(session);\n session.sseResponse = null;\n }\n });\n }\n\n private async handlePermissionResponse(\n sessionId: string,\n req: IncomingMessage,\n res: ServerResponse,\n ): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (!session) return jsonResponse(res, 404, { error: \"Session not found\" });\n if (!session.pendingPermission) return jsonResponse(res, 409, { error: \"No pending permission request\" });\n\n const body = (await readBody(req)) as PermissionResponse;\n clearPendingPermissionTimer(session);\n session.pendingPermission.resolve(body);\n session.pendingPermission = null;\n jsonResponse(res, 200, { ok: true });\n }\n\n private async handleInputResponse(\n sessionId: string,\n req: IncomingMessage,\n res: ServerResponse,\n ): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (!session) return jsonResponse(res, 404, { error: \"Session not found\" });\n if (!session.pendingInput) return jsonResponse(res, 409, { error: \"No pending input request\" });\n\n const body = (await readBody(req)) as { answer?: string };\n if (typeof body.answer !== \"string\") {\n return jsonResponse(res, 400, { error: \"Missing required field: answer\" });\n }\n\n clearPendingInputTimer(session);\n session.pendingInput.resolve(body.answer);\n session.pendingInput = null;\n jsonResponse(res, 200, { ok: true });\n }\n\n private async handleSendMessage(\n sessionId: string,\n req: IncomingMessage,\n res: ServerResponse,\n ): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (!session) return jsonResponse(res, 404, { error: \"Session not found\" });\n if (!session.done) return jsonResponse(res, 409, { error: \"Session is still running\" });\n\n const body = (await readBody(req)) as { prompt?: string };\n if (!body.prompt || typeof body.prompt !== \"string\") {\n return jsonResponse(res, 400, { error: \"Missing required field: prompt\" });\n }\n\n session.done = false;\n session.abortController = new AbortController();\n this.runAgentSse(session, body.prompt, true);\n jsonResponse(res, 200, { ok: true });\n }\n\n private handleDeleteSession(sessionId: string, res: ServerResponse): void {\n const session = this.sessions.get(sessionId);\n if (!session) return jsonResponse(res, 404, { error: \"Session not found\" });\n destroySession(this.sessions, session);\n jsonResponse(res, 200, { ok: true });\n }\n\n // -------------------------------------------------------------------------\n // WebSocket handling\n // -------------------------------------------------------------------------\n\n private async handleWsConnection(ws: WsWebSocket, req: IncomingMessage): Promise<void> {\n if (this.options.auth) {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n const tokenParam = url.searchParams.get(\"token\");\n if (tokenParam && this.options.auth.type === \"bearer\") {\n if (tokenParam !== this.options.auth.token) {\n ws.close();\n return;\n }\n } else {\n const authResult = await this.authenticate(req);\n if (!authResult) {\n ws.close();\n return;\n }\n }\n }\n\n const wsSessions = new Set<string>();\n\n let pongReceived = true;\n const pingTimer = setInterval(() => {\n if (!pongReceived) {\n ws.close();\n return;\n }\n pongReceived = false;\n try { ws.ping(); } catch { /* connection may already be closing */ }\n }, WS_PING_INTERVAL_MS);\n\n ws.on(\"pong\", () => { pongReceived = true; });\n\n const callbacks: WsDispatchCallbacks = {\n onRun: async (prompt, requestedSessionId) => {\n const overrides = await this.resolveConnectionOverrides(req);\n const session = createSessionState(this.sessions, requestedSessionId, overrides);\n wsSessions.add(session.id);\n this.runAgentWs(session, prompt, ws, false);\n return session.id;\n },\n onMessage: (sessionId, prompt) => {\n const session = this.sessions.get(sessionId);\n if (!session) { wsSend(ws, { type: \"error\", error: \"Session not found\" }); return; }\n if (!session.done) { wsSend(ws, { type: \"error\", error: \"Session is still running\" }); return; }\n session.done = false;\n session.abortController = new AbortController();\n this.runAgentWs(session, prompt, ws, true);\n },\n onPermissionResponse: (sessionId, response) => {\n const session = this.sessions.get(sessionId);\n if (!session?.pendingPermission) return;\n clearPendingPermissionTimer(session);\n session.pendingPermission.resolve(response);\n session.pendingPermission = null;\n },\n onInputResponse: (sessionId, answer) => {\n const session = this.sessions.get(sessionId);\n if (!session?.pendingInput) return;\n clearPendingInputTimer(session);\n session.pendingInput.resolve(answer);\n session.pendingInput = null;\n },\n onAbort: (sessionId) => {\n const session = this.sessions.get(sessionId);\n if (session) destroySession(this.sessions, session);\n },\n };\n\n ws.on(\"message\", async (raw) => {\n try {\n const msg = parseWsMessage(raw);\n if (!msg) { wsSend(ws, { type: \"error\", error: \"Invalid JSON\" }); return; }\n const result = await dispatchWsMessage(msg, {\n maxSessions: this.options.maxSessions,\n currentSessionCount: this.sessions.size,\n }, callbacks);\n if (result.type === \"error\") {\n wsSend(ws, { type: \"error\", error: result.error });\n } else if (result.type === \"session_created\") {\n wsSend(ws, { type: \"session_created\", sessionId: result.sessionId });\n }\n } catch (err) {\n wsSend(ws, { type: \"error\", error: String(err) });\n }\n });\n\n ws.on(\"close\", () => {\n clearInterval(pingTimer);\n for (const sid of wsSessions) {\n const session = this.sessions.get(sid);\n if (session) destroySession(this.sessions, session);\n }\n });\n\n ws.on(\"error\", () => {\n clearInterval(pingTimer);\n });\n }\n\n // -------------------------------------------------------------------------\n // Agent runners\n // -------------------------------------------------------------------------\n\n private async makeThread(session: SessionState, resume: boolean) {\n const timeoutMs = this.options.pendingTimeoutMs ?? DEFAULT_PENDING_TIMEOUT_MS;\n const handlers = {\n cwd: session.cwd,\n permissionHandler: (_req: import(\"../permissions/types.js\").PermissionRequest) =>\n bridgePermission(this.sessions, session.id, timeoutMs),\n userInputHandler: (_q: string) =>\n bridgeUserInput(this.sessions, session.id, timeoutMs),\n };\n\n return resume\n ? this.code.resumeThread(session.id, handlers)\n : this.code.createThread({ sessionId: session.id, ...handlers });\n }\n\n private runAgentSse(session: SessionState, prompt: string, resume: boolean): void {\n const run = async () => {\n try {\n const thread = await this.makeThread(session, resume);\n for await (const event of thread.run(prompt, { signal: session.abortController.signal })) {\n pushEvent(session, event);\n session.lastActivity = Date.now();\n }\n } catch (err) {\n if ((err as Error).name !== \"AbortError\") {\n pushEvent(session, {\n type: \"error\",\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n } finally {\n session.done = true;\n }\n };\n run().catch((err) => this.options.onError?.(err instanceof Error ? err : new Error(String(err))));\n }\n\n private runAgentWs(session: SessionState, prompt: string, ws: WsWebSocket, resume: boolean): void {\n const run = async () => {\n try {\n const thread = await this.makeThread(session, resume);\n for await (const event of thread.run(prompt, { signal: session.abortController.signal })) {\n session.sequenceNum++;\n wsSend(ws, { ...serializeEvent(event), sessionId: session.id, seq: session.sequenceNum });\n session.lastActivity = Date.now();\n }\n } catch (err) {\n if ((err as Error).name !== \"AbortError\") {\n wsSend(ws, { type: \"error\", sessionId: session.id, error: String(err) });\n }\n } finally {\n session.done = true;\n }\n };\n run().catch((err) => this.options.onError?.(err instanceof Error ? err : new Error(String(err))));\n }\n\n private startSseKeepalive(session: SessionState): void {\n clearSseKeepalive(session);\n session.sseKeepaliveTimer = setInterval(() => {\n if (session.sseResponse && !session.sseResponse.destroyed) {\n session.sseResponse.write(\":keepalive\\n\\n\");\n }\n }, SSE_KEEPALIVE_INTERVAL_MS);\n session.sseKeepaliveTimer.unref();\n }\n\n private async resolveConnectionOverrides(req: IncomingMessage): Promise<ConnectionOverrides> {\n if (!this.options.onConnection) return {};\n const auth = (await this.authenticate(req)) ?? {};\n return this.options.onConnection({ auth, remoteAddress: req.socket.remoteAddress });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport function createServer(code: Agent, options: ServerOptions): NoumenServer {\n return new NoumenServer(code, options);\n}\n\n/**\n * Create a `(req, res)` handler that can be mounted on any Node HTTP\n * framework (Express, Fastify, Hono, bare `http.createServer`, etc.).\n *\n * ```ts\n * import express from \"express\";\n * import { createRequestHandler } from \"noumen/server\";\n *\n * const app = express();\n * app.use(\"/agent\", createRequestHandler(code, { auth: { type: \"bearer\", token: \"...\" } }));\n * ```\n *\n * WebSocket is not supported in middleware mode — use `createServer()` for WS.\n */\nexport function createRequestHandler(\n code: Agent,\n options?: RequestHandlerOptions,\n): (req: IncomingMessage, res: ServerResponse) => void {\n const serverOpts: ServerOptions = {\n port: 0,\n ws: false,\n ...options,\n };\n const server = new NoumenServer(code, serverOpts);\n return (req, res) => { server.handleRequest(req, res); };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction jsonResponse(res: ServerResponse, status: number, body: unknown): void {\n const json = JSON.stringify(body);\n res.writeHead(status, {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(json),\n });\n res.end(json);\n}\n\nfunction readBody(req: IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let totalBytes = 0;\n let rejected = false;\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => {\n if (rejected) return;\n totalBytes += chunk.length;\n if (totalBytes > MAX_BODY_BYTES) {\n rejected = true;\n req.destroy();\n reject(new Error(\"Request body too large\"));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (rejected) return;\n try {\n const raw = Buffer.concat(chunks).toString();\n resolve(raw ? JSON.parse(raw) : {});\n } catch (err) {\n reject(err);\n }\n });\n req.on(\"error\", (err) => {\n if (!rejected) reject(err);\n });\n });\n}\n","import { randomUUID } from \"node:crypto\";\nimport type { ServerResponse } from \"node:http\";\nimport type { StreamEvent } from \"../session/types.js\";\nimport type { PermissionResponse } from \"../permissions/types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface PromiseResolver<T> {\n resolve: (value: T) => void;\n reject: (err: Error) => void;\n}\n\nexport interface BufferedEvent {\n seq: number;\n event: StreamEvent;\n}\n\nexport interface SessionState {\n id: string;\n abortController: AbortController;\n pendingPermission: PromiseResolver<PermissionResponse> | null;\n pendingInput: PromiseResolver<string> | null;\n pendingPermissionTimer: ReturnType<typeof setTimeout> | null;\n pendingInputTimer: ReturnType<typeof setTimeout> | null;\n lastActivity: number;\n sseResponse: ServerResponse | null;\n sseKeepaliveTimer: ReturnType<typeof setInterval> | null;\n eventBuffer: BufferedEvent[];\n sequenceNum: number;\n done: boolean;\n cwd?: string;\n}\n\nexport interface ConnectionOverrides {\n cwd?: string;\n}\n\nexport const DEFAULT_PENDING_TIMEOUT_MS = 120_000; // 2 minutes\n\n// ---------------------------------------------------------------------------\n// Session lifecycle\n// ---------------------------------------------------------------------------\n\nexport function createSessionState(\n sessions: Map<string, SessionState>,\n requestedId: string | undefined,\n overrides: ConnectionOverrides,\n): SessionState {\n if (requestedId && sessions.has(requestedId)) {\n throw new Error(`Session ${requestedId} already exists`);\n }\n const sessionId = requestedId ?? randomUUID();\n\n const session: SessionState = {\n id: sessionId,\n abortController: new AbortController(),\n pendingPermission: null,\n pendingInput: null,\n pendingPermissionTimer: null,\n pendingInputTimer: null,\n lastActivity: Date.now(),\n sseResponse: null,\n sseKeepaliveTimer: null,\n eventBuffer: [],\n sequenceNum: 0,\n done: false,\n cwd: overrides.cwd,\n };\n\n sessions.set(sessionId, session);\n return session;\n}\n\nexport function destroySession(\n sessions: Map<string, SessionState>,\n session: SessionState,\n): void {\n session.abortController.abort();\n clearSseKeepalive(session);\n clearPendingPermissionTimer(session);\n clearPendingInputTimer(session);\n if (session.pendingPermission) {\n session.pendingPermission.reject(new Error(\"Session aborted\"));\n session.pendingPermission = null;\n }\n if (session.pendingInput) {\n session.pendingInput.reject(new Error(\"Session aborted\"));\n session.pendingInput = null;\n }\n if (session.sseResponse) {\n session.sseResponse.end();\n session.sseResponse = null;\n }\n sessions.delete(session.id);\n}\n\nexport function reapIdleSessions(\n sessions: Map<string, SessionState>,\n timeoutMs: number | undefined,\n): void {\n if (!timeoutMs) return;\n const now = Date.now();\n for (const session of sessions.values()) {\n if (now - session.lastActivity > timeoutMs) {\n destroySession(sessions, session);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Permission / input bridging\n// ---------------------------------------------------------------------------\n\nexport function bridgePermission(\n sessions: Map<string, SessionState>,\n sessionId: string,\n timeoutMs: number,\n): Promise<PermissionResponse> {\n const session = sessions.get(sessionId);\n if (!session) return Promise.reject(new Error(\"Session not found\"));\n return new Promise<PermissionResponse>((resolve, reject) => {\n session.pendingPermission = { resolve, reject };\n session.pendingPermissionTimer = setTimeout(() => {\n session.pendingPermissionTimer = null;\n if (session.pendingPermission) {\n session.pendingPermission.reject(new Error(\"Permission request timed out\"));\n session.pendingPermission = null;\n }\n }, timeoutMs);\n });\n}\n\nexport function bridgeUserInput(\n sessions: Map<string, SessionState>,\n sessionId: string,\n timeoutMs: number,\n): Promise<string> {\n const session = sessions.get(sessionId);\n if (!session) return Promise.reject(new Error(\"Session not found\"));\n return new Promise<string>((resolve, reject) => {\n session.pendingInput = { resolve, reject };\n session.pendingInputTimer = setTimeout(() => {\n session.pendingInputTimer = null;\n if (session.pendingInput) {\n session.pendingInput.reject(new Error(\"User input request timed out\"));\n session.pendingInput = null;\n }\n }, timeoutMs);\n });\n}\n\n// ---------------------------------------------------------------------------\n// Timer helpers\n// ---------------------------------------------------------------------------\n\nexport function clearPendingPermissionTimer(session: SessionState): void {\n if (session.pendingPermissionTimer) {\n clearTimeout(session.pendingPermissionTimer);\n session.pendingPermissionTimer = null;\n }\n}\n\nexport function clearPendingInputTimer(session: SessionState): void {\n if (session.pendingInputTimer) {\n clearTimeout(session.pendingInputTimer);\n session.pendingInputTimer = null;\n }\n}\n\nexport function clearSseKeepalive(session: SessionState): void {\n if (session.sseKeepaliveTimer) {\n clearInterval(session.sseKeepaliveTimer);\n session.sseKeepaliveTimer = null;\n }\n}\n","import type { ServerResponse } from \"node:http\";\nimport type { StreamEvent } from \"../session/types.js\";\nimport type { SessionState, BufferedEvent } from \"./session-state.js\";\n\nexport const MAX_EVENT_BUFFER = 1000;\n\n/**\n * Serialize a StreamEvent to a JSON-safe object. Error instances are\n * converted to `{ message, name }` since `JSON.stringify(new Error())`\n * produces `{}`.\n */\nexport function serializeEvent(event: StreamEvent): Record<string, unknown> {\n if (event.type === \"error\") {\n return { type: \"error\", error: { message: event.error.message, name: event.error.name } };\n }\n if (event.type === \"retry_exhausted\") {\n return { ...event, error: { message: event.error.message, name: event.error.name } };\n }\n if (event.type === \"retry_attempt\") {\n return { ...event, error: { message: event.error.message, name: event.error.name } };\n }\n return event as unknown as Record<string, unknown>;\n}\n\n/**\n * Push a stream event into the session's buffer, incrementing the sequence\n * number and writing to the live SSE response if one is attached.\n */\nexport function pushEvent(session: SessionState, event: StreamEvent): void {\n session.sequenceNum++;\n const seq = session.sequenceNum;\n\n if (session.eventBuffer.length >= MAX_EVENT_BUFFER) {\n session.eventBuffer.shift();\n }\n session.eventBuffer.push({ seq, event });\n\n if (session.sseResponse) {\n writeSseEventRaw(session.sseResponse, seq, serializeEvent(event));\n }\n}\n\n/**\n * Return buffered events whose sequence number is greater than `afterSeq`.\n */\nexport function getBufferedEventsAfter(\n buffer: BufferedEvent[],\n afterSeq: number,\n): BufferedEvent[] {\n if (!afterSeq) return [...buffer];\n return buffer.filter((e) => e.seq > afterSeq);\n}\n\nexport function writeSseEventRaw(\n res: ServerResponse,\n seq: number,\n data: Record<string, unknown>,\n): void {\n res.write(`id: ${seq}\\ndata: ${JSON.stringify(data)}\\n\\n`);\n}\n","import type { PermissionResponse } from \"../permissions/types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type WsWebSocket = {\n on(event: \"message\", cb: (data: Buffer | string) => void): void;\n on(event: \"close\", cb: () => void): void;\n on(event: \"error\", cb: (err: Error) => void): void;\n on(event: \"pong\", cb: () => void): void;\n send(data: string): void;\n ping(): void;\n close(): void;\n readyState: number;\n};\n\nexport interface WsDispatchCallbacks {\n /** Start a new session with the given prompt, optional requested session ID. */\n onRun: (prompt: string, requestedSessionId: string | undefined) => Promise<string>;\n /** Send a follow-up message to an existing session. */\n onMessage: (sessionId: string, prompt: string) => void;\n /** Forward a permission response to a session. */\n onPermissionResponse: (sessionId: string, response: PermissionResponse) => void;\n /** Forward a user input response to a session. */\n onInputResponse: (sessionId: string, answer: string) => void;\n /** Abort/destroy a session. */\n onAbort: (sessionId: string) => void;\n}\n\nexport interface WsDispatchContext {\n maxSessions: number | undefined;\n currentSessionCount: number;\n}\n\nexport type WsDispatchResult =\n | { type: \"ok\" }\n | { type: \"error\"; error: string }\n | { type: \"session_created\"; sessionId: string }\n\n// ---------------------------------------------------------------------------\n// Parsing\n// ---------------------------------------------------------------------------\n\nexport function parseWsMessage(raw: Buffer | string): Record<string, unknown> | null {\n try {\n return JSON.parse(typeof raw === \"string\" ? raw : raw.toString());\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Dispatch\n// ---------------------------------------------------------------------------\n\nexport async function handleWsMessage(\n msg: Record<string, unknown>,\n ctx: WsDispatchContext,\n callbacks: WsDispatchCallbacks,\n): Promise<WsDispatchResult> {\n const msgType = msg.type as string;\n\n if (msgType === \"run\") {\n if (ctx.maxSessions && ctx.currentSessionCount >= ctx.maxSessions) {\n return { type: \"error\", error: \"Maximum sessions reached\" };\n }\n if (typeof msg.prompt !== \"string\" || !msg.prompt.trim()) {\n return { type: \"error\", error: \"Missing or empty prompt\" };\n }\n try {\n const sessionId = await callbacks.onRun(\n msg.prompt,\n msg.sessionId as string | undefined,\n );\n return { type: \"session_created\", sessionId };\n } catch (err) {\n return { type: \"error\", error: err instanceof Error ? err.message : String(err) };\n }\n }\n\n if (msgType === \"message\") {\n if (typeof msg.prompt !== \"string\" || !msg.prompt.trim()) {\n return { type: \"error\", error: \"Missing or empty prompt\" };\n }\n const sessionId = msg.sessionId as string;\n if (!sessionId) {\n return { type: \"error\", error: \"Missing sessionId\" };\n }\n callbacks.onMessage(sessionId, msg.prompt);\n return { type: \"ok\" };\n }\n\n if (msgType === \"permission_response\") {\n const sessionId = msg.sessionId as string;\n const { sessionId: _sid, type: _type, ...response } = msg;\n callbacks.onPermissionResponse(sessionId, response as unknown as PermissionResponse);\n return { type: \"ok\" };\n }\n\n if (msgType === \"input_response\") {\n const sessionId = msg.sessionId as string;\n callbacks.onInputResponse(sessionId, (msg.answer as string) ?? \"\");\n return { type: \"ok\" };\n }\n\n if (msgType === \"abort\") {\n const sessionId = msg.sessionId as string;\n if (sessionId) callbacks.onAbort(sessionId);\n return { type: \"ok\" };\n }\n\n return { type: \"ok\" };\n}\n\nexport function wsSend(ws: WsWebSocket, data: unknown): void {\n if (ws.readyState === 1) ws.send(JSON.stringify(data));\n}\n"],"mappings":"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","names":[]}
|
package/dist/sprites.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Sandbox } from './sandbox-
|
|
2
|
-
import {
|
|
1
|
+
import { S as Sandbox } from './sandbox-DAqQo0Tj.js';
|
|
2
|
+
import { V as VirtualFs, R as ReadOptions, F as FileEntry, b as FileStat, a as VirtualComputer, E as ExecOptions, C as CommandResult } from './computer-DzMR92tK.js';
|
|
3
3
|
|
|
4
4
|
interface SpritesSandboxOptions {
|
|
5
5
|
/** sprites.dev API token. */
|
package/dist/sprites.js
CHANGED
package/dist/sprites.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/virtual/sprites-fs.ts","../src/virtual/sprites-computer.ts","../src/virtual/sprites-sandbox.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport type { VirtualFs, FileEntry, FileStat, ReadOptions } from \"./fs.js\";\n\nexport interface SpritesFsOptions {\n /** sprites.dev API token */\n token: string;\n /** Name of the sprite container */\n spriteName: string;\n /** Base URL for sprites API (default: https://api.sprites.dev) */\n baseURL?: string;\n /** Working directory inside the sprite (default: /home/sprite) */\n workingDir?: string;\n}\n\n/**\n * Sandboxed VirtualFs backed by a remote sprites.dev container. All file\n * operations are executed over the sprites.dev HTTP API — the agent has no\n * access to the host filesystem. This is the recommended VirtualFs for\n * production deployments and untrusted agents. See `LocalFs` for an\n * unsandboxed local alternative.\n */\nexport class SpritesFs implements VirtualFs {\n private token: string;\n private spriteName: string;\n private baseURL: string;\n private workingDir: string;\n\n constructor(opts: SpritesFsOptions) {\n this.token = opts.token;\n this.spriteName = opts.spriteName;\n this.baseURL = (opts.baseURL ?? \"https://api.sprites.dev\").replace(\n /\\/$/,\n \"\",\n );\n this.workingDir = opts.workingDir ?? \"/home/sprite\";\n }\n\n private fsUrl(endpoint: string, params?: Record<string, string>): string {\n const url = new URL(\n `${this.baseURL}/v1/sprites/${this.spriteName}/fs${endpoint}`,\n );\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n url.searchParams.set(k, v);\n }\n }\n return url.toString();\n }\n\n private resolvePath(p: string): string {\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 private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.token}`,\n };\n }\n\n async readFile(filePath: string, _opts?: ReadOptions): Promise<string> {\n const url = this.fsUrl(\"/read\", { path: this.resolvePath(filePath) });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs readFile failed (${res.status}): ${await res.text()}`,\n );\n }\n return res.text();\n }\n\n async readFileBytes(filePath: string, maxBytes?: number): Promise<Buffer> {\n const url = this.fsUrl(\"/read\", {\n path: this.resolvePath(filePath),\n binary: \"true\",\n });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs readFileBytes failed (${res.status}): ${await res.text()}`,\n );\n }\n const arrayBuf = await res.arrayBuffer();\n const buf = Buffer.from(arrayBuf);\n if (maxBytes !== undefined && buf.length > maxBytes) {\n return buf.subarray(0, maxBytes);\n }\n return buf;\n }\n\n async writeFile(filePath: string, content: string): Promise<void> {\n const url = this.fsUrl(\"/write\");\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n path: this.resolvePath(filePath),\n content,\n }),\n });\n if (!res.ok) {\n throw new Error(\n `SpritesFs writeFile failed (${res.status}): ${await res.text()}`,\n );\n }\n }\n\n /**\n * @warning Not atomic. Concurrent appends may lose data due to\n * read-then-write TOCTOU. The Sprites API does not expose an append primitive.\n */\n async appendFile(filePath: string, content: string): Promise<void> {\n let existing = \"\";\n try {\n existing = await this.readFile(filePath);\n } catch {\n // file may not exist yet\n }\n await this.writeFile(filePath, existing + content);\n }\n\n async deleteFile(\n filePath: string,\n opts?: { recursive?: boolean },\n ): Promise<void> {\n const url = this.fsUrl(\"/remove\");\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n path: this.resolvePath(filePath),\n recursive: opts?.recursive ?? false,\n }),\n });\n if (!res.ok) {\n throw new Error(\n `SpritesFs deleteFile failed (${res.status}): ${await res.text()}`,\n );\n }\n }\n\n async mkdir(\n dirPath: string,\n opts?: { recursive?: boolean },\n ): Promise<void> {\n const url = this.fsUrl(\"/mkdir\");\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n path: this.resolvePath(dirPath),\n recursive: opts?.recursive ?? false,\n }),\n });\n if (!res.ok) {\n throw new Error(\n `SpritesFs mkdir failed (${res.status}): ${await res.text()}`,\n );\n }\n }\n\n async readdir(\n dirPath: string,\n _opts?: { recursive?: boolean },\n ): Promise<FileEntry[]> {\n const url = this.fsUrl(\"/readdir\", { path: this.resolvePath(dirPath) });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs readdir failed (${res.status}): ${await res.text()}`,\n );\n }\n const data = (await res.json()) as Array<{\n name: string;\n path: string;\n is_dir: boolean;\n size?: number;\n }>;\n return data.map((entry) => ({\n name: entry.name,\n path: entry.path,\n isDirectory: entry.is_dir,\n isFile: !entry.is_dir,\n size: entry.size,\n }));\n }\n\n async exists(filePath: string): Promise<boolean> {\n try {\n await this.stat(filePath);\n return true;\n } catch {\n return false;\n }\n }\n\n async stat(filePath: string): Promise<FileStat> {\n const url = this.fsUrl(\"/stat\", { path: this.resolvePath(filePath) });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs stat failed (${res.status}): ${await res.text()}`,\n );\n }\n const data = (await res.json()) as {\n size: number;\n is_dir: boolean;\n created_at?: string;\n modified_at?: string;\n };\n return {\n size: data.size,\n isDirectory: data.is_dir,\n isFile: !data.is_dir,\n createdAt: data.created_at ? new Date(data.created_at) : undefined,\n modifiedAt: data.modified_at ? new Date(data.modified_at) : undefined,\n };\n }\n}\n","import type {\n VirtualComputer,\n ExecOptions,\n CommandResult,\n} from \"./computer.js\";\n\nexport interface SpritesComputerOptions {\n /** sprites.dev API token */\n token: string;\n /** Name of the sprite container */\n spriteName: string;\n /** Base URL for sprites API (default: https://api.sprites.dev) */\n baseURL?: string;\n /** Working directory inside the sprite (default: /home/sprite) */\n workingDir?: string;\n}\n\n/**\n * Sandboxed VirtualComputer that executes commands inside a remote\n * sprites.dev container. All shell execution is fully isolated — the agent\n * has no access to the host machine's processes, filesystem, or network.\n *\n * This is the recommended VirtualComputer for production deployments and\n * untrusted agents. See `LocalComputer` for an unsandboxed local alternative.\n *\n * Uses the non-interactive exec REST endpoint (POST command, receive\n * stdout/stderr/exit_code). The WebSocket exec endpoint can be used for\n * streaming/TTY use cases, but REST is sufficient for tool calls.\n */\nexport class SpritesComputer implements VirtualComputer {\n private token: string;\n private spriteName: string;\n private baseURL: string;\n private workingDir: string;\n\n constructor(opts: SpritesComputerOptions) {\n this.token = opts.token;\n this.spriteName = opts.spriteName;\n this.baseURL = (opts.baseURL ?? \"https://api.sprites.dev\").replace(\n /\\/$/,\n \"\",\n );\n this.workingDir = opts.workingDir ?? \"/home/sprite\";\n }\n\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.token}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n async executeCommand(\n command: string,\n opts?: ExecOptions,\n ): Promise<CommandResult> {\n const cwd = opts?.cwd ?? this.workingDir;\n const wrappedCommand = `cd ${this.shellEscape(cwd)} && ${command}`;\n\n const url = `${this.baseURL}/v1/sprites/${this.spriteName}/exec`;\n\n const res = await fetch(url, {\n method: \"POST\",\n headers: this.headers(),\n body: JSON.stringify({\n command: [\"bash\", \"-c\", wrappedCommand],\n timeout: opts?.timeout ?? 30_000,\n env: opts?.env,\n }),\n });\n\n if (!res.ok) {\n const text = await res.text();\n return {\n exitCode: 1,\n stdout: \"\",\n stderr: `Sprites exec failed (${res.status}): ${text}`,\n };\n }\n\n const data = (await res.json()) as {\n exit_code: number;\n stdout: string;\n stderr: string;\n };\n\n return {\n exitCode: data.exit_code,\n stdout: data.stdout ?? \"\",\n stderr: data.stderr ?? \"\",\n };\n }\n\n private shellEscape(s: string): string {\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n }\n}\n","import type { Sandbox } from \"./sandbox.js\";\nimport { SpritesFs } from \"./sprites-fs.js\";\nimport { SpritesComputer } from \"./sprites-computer.js\";\nimport { createFsProxy, createComputerProxy } from \"./proxy.js\";\n\nexport interface SpritesSandboxOptions {\n /** sprites.dev API token. */\n token: string;\n /**\n * Name of an existing sprite container. When provided the sandbox\n * attaches to this sprite directly — no auto-creation occurs and\n * `dispose()` will **not** delete it (lifecycle is yours to manage).\n *\n * When omitted a new sprite is provisioned on the first `init()` call\n * (via `POST /v1/sprites`). The auto-created sprite is deleted when\n * `dispose()` is called, and its name is available via `sandboxId()`\n * for session persistence.\n */\n spriteName?: string;\n /** Base URL for sprites API (default: https://api.sprites.dev). */\n baseURL?: string;\n /** Working directory inside the sprite (default: /home/sprite). */\n workingDir?: string;\n /**\n * Optional prefix for auto-generated sprite names (default: \"noumen-\").\n * Only used when `spriteName` is omitted.\n */\n namePrefix?: string;\n}\n\n/**\n * Create a `Sandbox` backed by a remote sprites.dev container.\n * Full isolation — the agent has no access to the host machine.\n *\n * **Auto-creation:** When `spriteName` is omitted the sandbox is created\n * lazily on the first `init()` call via the Sprites REST API. The sprite\n * name is available through `sandboxId()` so callers can persist it in\n * session metadata for reconnection on resume. Pass the stored name back\n * through `init(storedId)` to reattach instead of creating a new sprite.\n *\n * **Explicit ID:** When `spriteName` is provided the sandbox attaches to\n * that sprite immediately on `init()`. `dispose()` is a no-op in this\n * case — the caller owns the sprite's lifecycle.\n *\n * @example\n * ```ts\n * // Auto-create — sprite provisioned on first init()\n * const sandbox = SpritesSandbox({ token: process.env.SPRITES_TOKEN! });\n *\n * // Explicit — attach to pre-existing sprite, no auto-lifecycle\n * const sandbox = SpritesSandbox({\n * token: process.env.SPRITES_TOKEN!,\n * spriteName: \"my-sprite\",\n * });\n * ```\n */\nexport function SpritesSandbox(opts: SpritesSandboxOptions): Sandbox {\n const baseURL = (opts.baseURL ?? \"https://api.sprites.dev\").replace(/\\/$/, \"\");\n const userProvidedName = opts.spriteName;\n\n if (userProvidedName) {\n const fsOpts = { ...opts, spriteName: userProvidedName };\n return {\n fs: new SpritesFs(fsOpts),\n computer: new SpritesComputer(fsOpts),\n sandboxId: () => userProvidedName,\n };\n }\n\n const fsProxy = createFsProxy();\n const computerProxy = createComputerProxy();\n let resolvedName: string | undefined;\n let autoCreated = false;\n let initPromise: Promise<void> | null = null;\n\n async function doInit(reconnectId?: string): Promise<void> {\n let name = reconnectId ?? `${opts.namePrefix ?? \"noumen-\"}${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n let needsCreate = !reconnectId;\n\n if (reconnectId) {\n const check = await fetch(`${baseURL}/v1/sprites/${reconnectId}`, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${opts.token}` },\n });\n if (!check.ok) {\n name = `${opts.namePrefix ?? \"noumen-\"}${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n needsCreate = true;\n }\n }\n\n if (needsCreate) {\n const res = await fetch(`${baseURL}/v1/sprites`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${opts.token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ name }),\n });\n if (!res.ok) {\n throw new Error(`Sprites auto-create failed (${res.status}): ${await res.text()}`);\n }\n autoCreated = true;\n }\n\n resolvedName = name;\n const childOpts = { ...opts, spriteName: name };\n fsProxy.setTarget(new SpritesFs(childOpts));\n computerProxy.setTarget(new SpritesComputer(childOpts));\n }\n\n return {\n fs: fsProxy,\n computer: computerProxy,\n sandboxId: () => resolvedName,\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 || !resolvedName) return;\n try {\n const res = await fetch(`${baseURL}/v1/sprites/${resolvedName}`, {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${opts.token}` },\n });\n if (!res.ok && res.status !== 404) {\n throw new Error(`Sprites dispose failed (${res.status}): ${await res.text()}`);\n }\n } catch {\n // Best-effort cleanup — network errors during dispose are non-fatal\n }\n },\n };\n}\n"],"mappings":";;;;;;;AAAA,YAAY,UAAU;AAqBf,IAAM,YAAN,MAAqC;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,MAAwB;AAClC,SAAK,QAAQ,KAAK;AAClB,SAAK,aAAa,KAAK;AACvB,SAAK,WAAW,KAAK,WAAW,2BAA2B;AAAA,MACzD;AAAA,MACA;AAAA,IACF;AACA,SAAK,aAAa,KAAK,cAAc;AAAA,EACvC;AAAA,EAEQ,MAAM,UAAkB,QAAyC;AACvE,UAAM,MAAM,IAAI;AAAA,MACd,GAAG,KAAK,OAAO,eAAe,KAAK,UAAU,MAAM,QAAQ;AAAA,IAC7D;AACA,QAAI,QAAQ;AACV,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC3C,YAAI,aAAa,IAAI,GAAG,CAAC;AAAA,MAC3B;AAAA,IACF;AACA,WAAO,IAAI,SAAS;AAAA,EACtB;AAAA,EAEQ,YAAY,GAAmB;AACrC,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,EAEQ,UAAkC;AACxC,WAAO;AAAA,MACL,eAAe,UAAU,KAAK,KAAK;AAAA,IACrC;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,UAAkB,OAAsC;AACrE,UAAM,MAAM,KAAK,MAAM,SAAS,EAAE,MAAM,KAAK,YAAY,QAAQ,EAAE,CAAC;AACpE,UAAM,MAAM,MAAM,MAAM,KAAK,EAAE,SAAS,KAAK,QAAQ,EAAE,CAAC;AACxD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,8BAA8B,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,MAChE;AAAA,IACF;AACA,WAAO,IAAI,KAAK;AAAA,EAClB;AAAA,EAEA,MAAM,cAAc,UAAkB,UAAoC;AACxE,UAAM,MAAM,KAAK,MAAM,SAAS;AAAA,MAC9B,MAAM,KAAK,YAAY,QAAQ;AAAA,MAC/B,QAAQ;AAAA,IACV,CAAC;AACD,UAAM,MAAM,MAAM,MAAM,KAAK,EAAE,SAAS,KAAK,QAAQ,EAAE,CAAC;AACxD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,mCAAmC,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,MACrE;AAAA,IACF;AACA,UAAM,WAAW,MAAM,IAAI,YAAY;AACvC,UAAM,MAAM,OAAO,KAAK,QAAQ;AAChC,QAAI,aAAa,UAAa,IAAI,SAAS,UAAU;AACnD,aAAO,IAAI,SAAS,GAAG,QAAQ;AAAA,IACjC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,UAAU,UAAkB,SAAgC;AAChE,UAAM,MAAM,KAAK,MAAM,QAAQ;AAC/B,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,GAAG,KAAK,QAAQ;AAAA,QAChB,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,MAAM,KAAK,YAAY,QAAQ;AAAA,QAC/B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,+BAA+B,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAAW,UAAkB,SAAgC;AACjE,QAAI,WAAW;AACf,QAAI;AACF,iBAAW,MAAM,KAAK,SAAS,QAAQ;AAAA,IACzC,QAAQ;AAAA,IAER;AACA,UAAM,KAAK,UAAU,UAAU,WAAW,OAAO;AAAA,EACnD;AAAA,EAEA,MAAM,WACJ,UACA,MACe;AACf,UAAM,MAAM,KAAK,MAAM,SAAS;AAChC,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,GAAG,KAAK,QAAQ;AAAA,QAChB,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,MAAM,KAAK,YAAY,QAAQ;AAAA,QAC/B,WAAW,MAAM,aAAa;AAAA,MAChC,CAAC;AAAA,IACH,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,gCAAgC,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,MAClE;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,MACJ,SACA,MACe;AACf,UAAM,MAAM,KAAK,MAAM,QAAQ;AAC/B,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,GAAG,KAAK,QAAQ;AAAA,QAChB,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,MAAM,KAAK,YAAY,OAAO;AAAA,QAC9B,WAAW,MAAM,aAAa;AAAA,MAChC,CAAC;AAAA,IACH,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,2BAA2B,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,MAC7D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,QACJ,SACA,OACsB;AACtB,UAAM,MAAM,KAAK,MAAM,YAAY,EAAE,MAAM,KAAK,YAAY,OAAO,EAAE,CAAC;AACtE,UAAM,MAAM,MAAM,MAAM,KAAK,EAAE,SAAS,KAAK,QAAQ,EAAE,CAAC;AACxD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,6BAA6B,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,MAC/D;AAAA,IACF;AACA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAM7B,WAAO,KAAK,IAAI,CAAC,WAAW;AAAA,MAC1B,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,aAAa,MAAM;AAAA,MACnB,QAAQ,CAAC,MAAM;AAAA,MACf,MAAM,MAAM;AAAA,IACd,EAAE;AAAA,EACJ;AAAA,EAEA,MAAM,OAAO,UAAoC;AAC/C,QAAI;AACF,YAAM,KAAK,KAAK,QAAQ;AACxB,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,UAAqC;AAC9C,UAAM,MAAM,KAAK,MAAM,SAAS,EAAE,MAAM,KAAK,YAAY,QAAQ,EAAE,CAAC;AACpE,UAAM,MAAM,MAAM,MAAM,KAAK,EAAE,SAAS,KAAK,QAAQ,EAAE,CAAC;AACxD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,0BAA0B,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,MAC5D;AAAA,IACF;AACA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAM7B,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,aAAa,KAAK;AAAA,MAClB,QAAQ,CAAC,KAAK;AAAA,MACd,WAAW,KAAK,aAAa,IAAI,KAAK,KAAK,UAAU,IAAI;AAAA,MACzD,YAAY,KAAK,cAAc,IAAI,KAAK,KAAK,WAAW,IAAI;AAAA,IAC9D;AAAA,EACF;AACF;;;AChNO,IAAM,kBAAN,MAAiD;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,MAA8B;AACxC,SAAK,QAAQ,KAAK;AAClB,SAAK,aAAa,KAAK;AACvB,SAAK,WAAW,KAAK,WAAW,2BAA2B;AAAA,MACzD;AAAA,MACA;AAAA,IACF;AACA,SAAK,aAAa,KAAK,cAAc;AAAA,EACvC;AAAA,EAEQ,UAAkC;AACxC,WAAO;AAAA,MACL,eAAe,UAAU,KAAK,KAAK;AAAA,MACnC,gBAAgB;AAAA,IAClB;AAAA,EACF;AAAA,EAEA,MAAM,eACJ,SACA,MACwB;AACxB,UAAM,MAAM,MAAM,OAAO,KAAK;AAC9B,UAAM,iBAAiB,MAAM,KAAK,YAAY,GAAG,CAAC,OAAO,OAAO;AAEhE,UAAM,MAAM,GAAG,KAAK,OAAO,eAAe,KAAK,UAAU;AAEzD,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS,KAAK,QAAQ;AAAA,MACtB,MAAM,KAAK,UAAU;AAAA,QACnB,SAAS,CAAC,QAAQ,MAAM,cAAc;AAAA,QACtC,SAAS,MAAM,WAAW;AAAA,QAC1B,KAAK,MAAM;AAAA,MACb,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,aAAO;AAAA,QACL,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ,wBAAwB,IAAI,MAAM,MAAM,IAAI;AAAA,MACtD;AAAA,IACF;AAEA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAM7B,WAAO;AAAA,MACL,UAAU,KAAK;AAAA,MACf,QAAQ,KAAK,UAAU;AAAA,MACvB,QAAQ,KAAK,UAAU;AAAA,IACzB;AAAA,EACF;AAAA,EAEQ,YAAY,GAAmB;AACrC,WAAO,IAAI,EAAE,QAAQ,MAAM,OAAO,CAAC;AAAA,EACrC;AACF;;;ACxCO,SAAS,eAAe,MAAsC;AACnE,QAAM,WAAW,KAAK,WAAW,2BAA2B,QAAQ,OAAO,EAAE;AAC7E,QAAM,mBAAmB,KAAK;AAE9B,MAAI,kBAAkB;AACpB,UAAM,SAAS,EAAE,GAAG,MAAM,YAAY,iBAAiB;AACvD,WAAO;AAAA,MACL,IAAI,IAAI,UAAU,MAAM;AAAA,MACxB,UAAU,IAAI,gBAAgB,MAAM;AAAA,MACpC,WAAW,MAAM;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,UAAU,cAAc;AAC9B,QAAM,gBAAgB,oBAAoB;AAC1C,MAAI;AACJ,MAAI,cAAc;AAClB,MAAI,cAAoC;AAExC,iBAAe,OAAO,aAAqC;AACzD,QAAI,OAAO,eAAe,GAAG,KAAK,cAAc,SAAS,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAChH,QAAI,cAAc,CAAC;AAEnB,QAAI,aAAa;AACf,YAAM,QAAQ,MAAM,MAAM,GAAG,OAAO,eAAe,WAAW,IAAI;AAAA,QAChE,QAAQ;AAAA,QACR,SAAS,EAAE,eAAe,UAAU,KAAK,KAAK,GAAG;AAAA,MACnD,CAAC;AACD,UAAI,CAAC,MAAM,IAAI;AACb,eAAO,GAAG,KAAK,cAAc,SAAS,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC7F,sBAAc;AAAA,MAChB;AAAA,IACF;AAEA,QAAI,aAAa;AACf,YAAM,MAAM,MAAM,MAAM,GAAG,OAAO,eAAe;AAAA,QAC/C,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,KAAK,KAAK;AAAA,UACnC,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;AAAA,MAC/B,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,+BAA+B,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC,EAAE;AAAA,MACnF;AACA,oBAAc;AAAA,IAChB;AAEA,mBAAe;AACf,UAAM,YAAY,EAAE,GAAG,MAAM,YAAY,KAAK;AAC9C,YAAQ,UAAU,IAAI,UAAU,SAAS,CAAC;AAC1C,kBAAc,UAAU,IAAI,gBAAgB,SAAS,CAAC;AAAA,EACxD;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,aAAc;AACnC,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,GAAG,OAAO,eAAe,YAAY,IAAI;AAAA,UAC/D,QAAQ;AAAA,UACR,SAAS,EAAE,eAAe,UAAU,KAAK,KAAK,GAAG;AAAA,QACnD,CAAC;AACD,YAAI,CAAC,IAAI,MAAM,IAAI,WAAW,KAAK;AACjC,gBAAM,IAAI,MAAM,2BAA2B,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,CAAC,EAAE;AAAA,QAC/E;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/virtual/sprites-fs.ts","../src/virtual/sprites-computer.ts","../src/virtual/sprites-sandbox.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport type { VirtualFs, FileEntry, FileStat, ReadOptions } from \"./fs.js\";\n\nexport interface SpritesFsOptions {\n /** sprites.dev API token */\n token: string;\n /** Name of the sprite container */\n spriteName: string;\n /** Base URL for sprites API (default: https://api.sprites.dev) */\n baseURL?: string;\n /** Working directory inside the sprite (default: /home/sprite) */\n workingDir?: string;\n}\n\n/**\n * Sandboxed VirtualFs backed by a remote sprites.dev container. All file\n * operations are executed over the sprites.dev HTTP API — the agent has no\n * access to the host filesystem. This is the recommended VirtualFs for\n * production deployments and untrusted agents. See `LocalFs` for an\n * unsandboxed local alternative.\n */\nexport class SpritesFs implements VirtualFs {\n private token: string;\n private spriteName: string;\n private baseURL: string;\n private workingDir: string;\n\n constructor(opts: SpritesFsOptions) {\n this.token = opts.token;\n this.spriteName = opts.spriteName;\n this.baseURL = (opts.baseURL ?? \"https://api.sprites.dev\").replace(\n /\\/$/,\n \"\",\n );\n this.workingDir = opts.workingDir ?? \"/home/sprite\";\n }\n\n private fsUrl(endpoint: string, params?: Record<string, string>): string {\n const url = new URL(\n `${this.baseURL}/v1/sprites/${this.spriteName}/fs${endpoint}`,\n );\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n url.searchParams.set(k, v);\n }\n }\n return url.toString();\n }\n\n private resolvePath(p: string): string {\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 private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.token}`,\n };\n }\n\n async readFile(filePath: string, _opts?: ReadOptions): Promise<string> {\n const url = this.fsUrl(\"/read\", { path: this.resolvePath(filePath) });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs readFile failed (${res.status}): ${await res.text()}`,\n );\n }\n return res.text();\n }\n\n async readFileBytes(filePath: string, maxBytes?: number): Promise<Buffer> {\n const url = this.fsUrl(\"/read\", {\n path: this.resolvePath(filePath),\n binary: \"true\",\n });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs readFileBytes failed (${res.status}): ${await res.text()}`,\n );\n }\n const arrayBuf = await res.arrayBuffer();\n const buf = Buffer.from(arrayBuf);\n if (maxBytes !== undefined && buf.length > maxBytes) {\n return buf.subarray(0, maxBytes);\n }\n return buf;\n }\n\n async writeFile(filePath: string, content: string): Promise<void> {\n const url = this.fsUrl(\"/write\");\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n path: this.resolvePath(filePath),\n content,\n }),\n });\n if (!res.ok) {\n throw new Error(\n `SpritesFs writeFile failed (${res.status}): ${await res.text()}`,\n );\n }\n }\n\n /**\n * @warning Not atomic. Concurrent appends may lose data due to\n * read-then-write TOCTOU. The Sprites API does not expose an append primitive.\n */\n async appendFile(filePath: string, content: string): Promise<void> {\n let existing = \"\";\n try {\n existing = await this.readFile(filePath);\n } catch {\n // file may not exist yet\n }\n await this.writeFile(filePath, existing + content);\n }\n\n async deleteFile(\n filePath: string,\n opts?: { recursive?: boolean },\n ): Promise<void> {\n const url = this.fsUrl(\"/remove\");\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n path: this.resolvePath(filePath),\n recursive: opts?.recursive ?? false,\n }),\n });\n if (!res.ok) {\n throw new Error(\n `SpritesFs deleteFile failed (${res.status}): ${await res.text()}`,\n );\n }\n }\n\n async mkdir(\n dirPath: string,\n opts?: { recursive?: boolean },\n ): Promise<void> {\n const url = this.fsUrl(\"/mkdir\");\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n path: this.resolvePath(dirPath),\n recursive: opts?.recursive ?? false,\n }),\n });\n if (!res.ok) {\n throw new Error(\n `SpritesFs mkdir failed (${res.status}): ${await res.text()}`,\n );\n }\n }\n\n async readdir(\n dirPath: string,\n _opts?: { recursive?: boolean },\n ): Promise<FileEntry[]> {\n const url = this.fsUrl(\"/readdir\", { path: this.resolvePath(dirPath) });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs readdir failed (${res.status}): ${await res.text()}`,\n );\n }\n const data = (await res.json()) as Array<{\n name: string;\n path: string;\n is_dir: boolean;\n size?: number;\n }>;\n return data.map((entry) => ({\n name: entry.name,\n path: entry.path,\n isDirectory: entry.is_dir,\n isFile: !entry.is_dir,\n size: entry.size,\n }));\n }\n\n async exists(filePath: string): Promise<boolean> {\n try {\n await this.stat(filePath);\n return true;\n } catch {\n return false;\n }\n }\n\n async stat(filePath: string): Promise<FileStat> {\n const url = this.fsUrl(\"/stat\", { path: this.resolvePath(filePath) });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs stat failed (${res.status}): ${await res.text()}`,\n );\n }\n const data = (await res.json()) as {\n size: number;\n is_dir: boolean;\n created_at?: string;\n modified_at?: string;\n };\n return {\n size: data.size,\n isDirectory: data.is_dir,\n isFile: !data.is_dir,\n createdAt: data.created_at ? new Date(data.created_at) : undefined,\n modifiedAt: data.modified_at ? new Date(data.modified_at) : undefined,\n };\n }\n}\n","import type {\n VirtualComputer,\n ExecOptions,\n CommandResult,\n} from \"./computer.js\";\n\nexport interface SpritesComputerOptions {\n /** sprites.dev API token */\n token: string;\n /** Name of the sprite container */\n spriteName: string;\n /** Base URL for sprites API (default: https://api.sprites.dev) */\n baseURL?: string;\n /** Working directory inside the sprite (default: /home/sprite) */\n workingDir?: string;\n}\n\n/**\n * Sandboxed VirtualComputer that executes commands inside a remote\n * sprites.dev container. All shell execution is fully isolated — the agent\n * has no access to the host machine's processes, filesystem, or network.\n *\n * This is the recommended VirtualComputer for production deployments and\n * untrusted agents. See `LocalComputer` for an unsandboxed local alternative.\n *\n * Uses the non-interactive exec REST endpoint (POST command, receive\n * stdout/stderr/exit_code). The WebSocket exec endpoint can be used for\n * streaming/TTY use cases, but REST is sufficient for tool calls.\n */\nexport class SpritesComputer implements VirtualComputer {\n private token: string;\n private spriteName: string;\n private baseURL: string;\n private workingDir: string;\n\n constructor(opts: SpritesComputerOptions) {\n this.token = opts.token;\n this.spriteName = opts.spriteName;\n this.baseURL = (opts.baseURL ?? \"https://api.sprites.dev\").replace(\n /\\/$/,\n \"\",\n );\n this.workingDir = opts.workingDir ?? \"/home/sprite\";\n }\n\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.token}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n async executeCommand(\n command: string,\n opts?: ExecOptions,\n ): Promise<CommandResult> {\n const cwd = opts?.cwd ?? this.workingDir;\n const wrappedCommand = `cd ${this.shellEscape(cwd)} && ${command}`;\n\n const url = `${this.baseURL}/v1/sprites/${this.spriteName}/exec`;\n\n const res = await fetch(url, {\n method: \"POST\",\n headers: this.headers(),\n body: JSON.stringify({\n command: [\"bash\", \"-c\", wrappedCommand],\n timeout: opts?.timeout ?? 30_000,\n env: opts?.env,\n }),\n });\n\n if (!res.ok) {\n const text = await res.text();\n return {\n exitCode: 1,\n stdout: \"\",\n stderr: `Sprites exec failed (${res.status}): ${text}`,\n };\n }\n\n const data = (await res.json()) as {\n exit_code: number;\n stdout: string;\n stderr: string;\n };\n\n return {\n exitCode: data.exit_code,\n stdout: data.stdout ?? \"\",\n stderr: data.stderr ?? \"\",\n };\n }\n\n private shellEscape(s: string): string {\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n }\n}\n","import type { Sandbox } from \"./sandbox.js\";\nimport { SpritesFs } from \"./sprites-fs.js\";\nimport { SpritesComputer } from \"./sprites-computer.js\";\nimport { createFsProxy, createComputerProxy } from \"./proxy.js\";\n\nexport interface SpritesSandboxOptions {\n /** sprites.dev API token. */\n token: string;\n /**\n * Name of an existing sprite container. When provided the sandbox\n * attaches to this sprite directly — no auto-creation occurs and\n * `dispose()` will **not** delete it (lifecycle is yours to manage).\n *\n * When omitted a new sprite is provisioned on the first `init()` call\n * (via `POST /v1/sprites`). The auto-created sprite is deleted when\n * `dispose()` is called, and its name is available via `sandboxId()`\n * for session persistence.\n */\n spriteName?: string;\n /** Base URL for sprites API (default: https://api.sprites.dev). */\n baseURL?: string;\n /** Working directory inside the sprite (default: /home/sprite). */\n workingDir?: string;\n /**\n * Optional prefix for auto-generated sprite names (default: \"noumen-\").\n * Only used when `spriteName` is omitted.\n */\n namePrefix?: string;\n}\n\n/**\n * Create a `Sandbox` backed by a remote sprites.dev container.\n * Full isolation — the agent has no access to the host machine.\n *\n * **Auto-creation:** When `spriteName` is omitted the sandbox is created\n * lazily on the first `init()` call via the Sprites REST API. The sprite\n * name is available through `sandboxId()` so callers can persist it in\n * session metadata for reconnection on resume. Pass the stored name back\n * through `init(storedId)` to reattach instead of creating a new sprite.\n *\n * **Explicit ID:** When `spriteName` is provided the sandbox attaches to\n * that sprite immediately on `init()`. `dispose()` is a no-op in this\n * case — the caller owns the sprite's lifecycle.\n *\n * @example\n * ```ts\n * // Auto-create — sprite provisioned on first init()\n * const sandbox = SpritesSandbox({ token: process.env.SPRITES_TOKEN! });\n *\n * // Explicit — attach to pre-existing sprite, no auto-lifecycle\n * const sandbox = SpritesSandbox({\n * token: process.env.SPRITES_TOKEN!,\n * spriteName: \"my-sprite\",\n * });\n * ```\n */\nexport function SpritesSandbox(opts: SpritesSandboxOptions): Sandbox {\n const baseURL = (opts.baseURL ?? \"https://api.sprites.dev\").replace(/\\/$/, \"\");\n const userProvidedName = opts.spriteName;\n\n if (userProvidedName) {\n const fsOpts = { ...opts, spriteName: userProvidedName };\n return {\n fs: new SpritesFs(fsOpts),\n computer: new SpritesComputer(fsOpts),\n sandboxId: () => userProvidedName,\n };\n }\n\n const fsProxy = createFsProxy();\n const computerProxy = createComputerProxy();\n let resolvedName: string | undefined;\n let autoCreated = false;\n let initPromise: Promise<void> | null = null;\n\n async function doInit(reconnectId?: string): Promise<void> {\n let name = reconnectId ?? `${opts.namePrefix ?? \"noumen-\"}${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n let needsCreate = !reconnectId;\n\n if (reconnectId) {\n const check = await fetch(`${baseURL}/v1/sprites/${reconnectId}`, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${opts.token}` },\n });\n if (!check.ok) {\n name = `${opts.namePrefix ?? \"noumen-\"}${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n needsCreate = true;\n }\n }\n\n if (needsCreate) {\n const res = await fetch(`${baseURL}/v1/sprites`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${opts.token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ name }),\n });\n if (!res.ok) {\n throw new Error(`Sprites auto-create failed (${res.status}): ${await res.text()}`);\n }\n autoCreated = true;\n }\n\n resolvedName = name;\n const childOpts = { ...opts, spriteName: name };\n fsProxy.setTarget(new SpritesFs(childOpts));\n computerProxy.setTarget(new SpritesComputer(childOpts));\n }\n\n return {\n fs: fsProxy,\n computer: computerProxy,\n sandboxId: () => resolvedName,\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 || !resolvedName) return;\n try {\n const res = await fetch(`${baseURL}/v1/sprites/${resolvedName}`, {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${opts.token}` },\n });\n if (!res.ok && res.status !== 404) {\n throw new Error(`Sprites dispose failed (${res.status}): ${await res.text()}`);\n }\n } catch {\n // Best-effort cleanup — network errors during dispose are non-fatal\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/ssh.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Sandbox } from './sandbox-
|
|
2
|
-
import {
|
|
1
|
+
import { S as Sandbox } from './sandbox-DAqQo0Tj.js';
|
|
2
|
+
import { a as VirtualComputer, E as ExecOptions, C as CommandResult, V as VirtualFs, R as ReadOptions, F as FileEntry, b as FileStat } from './computer-DzMR92tK.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Minimal subset of the ssh2 Channel interface used by SshComputer / SshFs.
|