noumen 0.5.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-C3eDRsxs.d.ts → agent-D0gl-qYi.d.ts} +259 -31
- package/dist/{chunk-WPCYGZOE.js → chunk-5HY4IYNT.js} +2062 -2545
- 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 +358 -72
- package/dist/index.js +68 -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-KI7OZUY3.js → provider-factory-KNBSHXJ6.js} +3 -3
- package/dist/{render-GRN4ZSSW.js → render-4VEODRK7.js} +1 -2
- package/dist/{resolve-GDSHNMG6.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-Cu9gv1dk.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-BA87bHPV.d.ts → types-BX4ALqoN.d.ts} +76 -4
- package/dist/{types-LrU4LRmX.d.ts → types-DLZNyF5t.d.ts} +164 -2
- 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-DsRqxx6v.d.ts +0 -38
- package/dist/chunk-5GEX6ZSB.js.map +0 -1
- package/dist/chunk-CS6WNDCF.js +0 -171
- package/dist/chunk-CS6WNDCF.js.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-EKOGVTBT.js +0 -472
- package/dist/chunk-EKOGVTBT.js.map +0 -1
- package/dist/chunk-HEQQQGK5.js +0 -131
- package/dist/chunk-HEQQQGK5.js.map +0 -1
- package/dist/chunk-L3L3FG5T.js +0 -16
- package/dist/chunk-L3L3FG5T.js.map +0 -1
- package/dist/chunk-WPCYGZOE.js.map +0 -1
- package/dist/chunk-WTLK2ZAR.js +0 -94
- package/dist/chunk-WTLK2ZAR.js.map +0 -1
- package/dist/chunk-XZN4QZLK.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 -35
- 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 -67
- 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-KI7OZUY3.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-GDSHNMG6.js.map → uuid-CVTNAPEB.js.map} +0 -0
- /package/dist/{spinner-OJNR6NFO.js.map → zod-VKURGPRT.js.map} +0 -0
|
@@ -4,11 +4,10 @@ import {
|
|
|
4
4
|
promptPermission,
|
|
5
5
|
renderEvent
|
|
6
6
|
} from "./chunk-OGXNFXFA.js";
|
|
7
|
-
import "./chunk-DGUM43GV.js";
|
|
8
7
|
export {
|
|
9
8
|
createRenderState,
|
|
10
9
|
isVisibleEvent,
|
|
11
10
|
promptPermission,
|
|
12
11
|
renderEvent
|
|
13
12
|
};
|
|
14
|
-
//# sourceMappingURL=render-
|
|
13
|
+
//# sourceMappingURL=render-4VEODRK7.js.map
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
SUPPORTED_PROVIDERS,
|
|
4
4
|
detectProvider,
|
|
5
5
|
resolveProvider
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-XZPAA5TO.js";
|
|
7
|
+
import "./chunk-OPFFLQZL.js";
|
|
8
8
|
export {
|
|
9
9
|
DEFAULT_MODELS,
|
|
10
10
|
SUPPORTED_PROVIDERS,
|
|
11
11
|
detectProvider,
|
|
12
12
|
resolveProvider
|
|
13
13
|
};
|
|
14
|
-
//# sourceMappingURL=resolve-
|
|
14
|
+
//# sourceMappingURL=resolve-AGQZFMKD.js.map
|
|
@@ -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-BA87bHPV.js';
|
|
8
|
-
import '../cache-DsRqxx6v.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. */
|