@lamplitisles/dsh-nanocodex 0.1.0-beta.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/LICENSE +65 -0
- package/NOTICE +2 -0
- package/README.md +182 -0
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/cordis.patch.yml +52 -0
- package/dist/index.d.ts +435 -0
- package/dist/index.js +3405 -0
- package/dist/standard-preset.d.ts +7 -0
- package/dist/standard-preset.js +25 -0
- package/engine-release.json +14 -0
- package/package.json +119 -0
- package/presets/standard/agent.cordis.yml +6 -0
- package/presets/standard/preset.yml +2 -0
- package/vendor/nanocodex/README.md +1344 -0
- package/vendor/nanocodex/actions/events.d.mts +10 -0
- package/vendor/nanocodex/actions/events.mjs +163 -0
- package/vendor/nanocodex/actions/index.d.mts +13 -0
- package/vendor/nanocodex/actions/index.mjs +35 -0
- package/vendor/nanocodex/actions/session.d.mts +66 -0
- package/vendor/nanocodex/actions/session.mjs +59 -0
- package/vendor/nanocodex/actions/turn.d.mts +59 -0
- package/vendor/nanocodex/actions/turn.mjs +37 -0
- package/vendor/nanocodex/actions/voice.d.mts +22 -0
- package/vendor/nanocodex/actions/voice.mjs +50 -0
- package/vendor/nanocodex/browser/Agent.d.mts +55 -0
- package/vendor/nanocodex/browser/Agent.mjs +11 -0
- package/vendor/nanocodex/browser/ChatGptSubscription.d.mts +7 -0
- package/vendor/nanocodex/browser/ChatGptSubscription.mjs +19 -0
- package/vendor/nanocodex/browser/InlineAgent.mjs +266 -0
- package/vendor/nanocodex/browser/Transport.d.mts +74 -0
- package/vendor/nanocodex/browser/Transport.mjs +61 -0
- package/vendor/nanocodex/browser/Voice.d.mts +71 -0
- package/vendor/nanocodex/browser/Voice.mjs +291 -0
- package/vendor/nanocodex/browser/VoiceSession.mjs +688 -0
- package/vendor/nanocodex/browser/WorkerAgent.d.mts +65 -0
- package/vendor/nanocodex/browser/WorkerAgent.mjs +1472 -0
- package/vendor/nanocodex/browser/agent.worker.mjs +3 -0
- package/vendor/nanocodex/browser/config.d.mts +36 -0
- package/vendor/nanocodex/browser/config.mjs +439 -0
- package/vendor/nanocodex/browser/engine.mjs +13 -0
- package/vendor/nanocodex/browser/harness.mjs +55 -0
- package/vendor/nanocodex/browser/host.d.mts +83 -0
- package/vendor/nanocodex/browser/host.mjs +555 -0
- package/vendor/nanocodex/browser/hostManagedWebSocket.d.mts +14 -0
- package/vendor/nanocodex/browser/hostManagedWebSocket.mjs +110 -0
- package/vendor/nanocodex/browser/index.d.mts +92 -0
- package/vendor/nanocodex/browser/index.mjs +23 -0
- package/vendor/nanocodex/browser/indexeddb-durability-store.mjs +197 -0
- package/vendor/nanocodex/browser/workspace.d.mts +16 -0
- package/vendor/nanocodex/browser/workspace.mjs +107 -0
- package/vendor/nanocodex/cloud/Client.d.mts +83 -0
- package/vendor/nanocodex/cloud/Client.mjs +231 -0
- package/vendor/nanocodex/cloud/Decorator.d.mts +31 -0
- package/vendor/nanocodex/cloud/Decorator.mjs +37 -0
- package/vendor/nanocodex/cloud/Dialog.d.mts +113 -0
- package/vendor/nanocodex/cloud/Dialog.mjs +366 -0
- package/vendor/nanocodex/cloud/Errors.d.mts +22 -0
- package/vendor/nanocodex/cloud/Errors.mjs +39 -0
- package/vendor/nanocodex/cloud/Principal.d.mts +26 -0
- package/vendor/nanocodex/cloud/Principal.mjs +88 -0
- package/vendor/nanocodex/cloud/RemoteProvider.mjs +88 -0
- package/vendor/nanocodex/cloud/Transport.d.mts +47 -0
- package/vendor/nanocodex/cloud/Transport.mjs +454 -0
- package/vendor/nanocodex/cloud/actions/account.d.mts +9 -0
- package/vendor/nanocodex/cloud/actions/account.mjs +19 -0
- package/vendor/nanocodex/cloud/actions/agent.d.mts +18 -0
- package/vendor/nanocodex/cloud/actions/agent.mjs +428 -0
- package/vendor/nanocodex/cloud/actions/connection.d.mts +133 -0
- package/vendor/nanocodex/cloud/actions/connection.mjs +580 -0
- package/vendor/nanocodex/cloud/actions/grant.d.mts +10 -0
- package/vendor/nanocodex/cloud/actions/grant.mjs +12 -0
- package/vendor/nanocodex/cloud/actions/index.d.mts +7 -0
- package/vendor/nanocodex/cloud/actions/index.mjs +7 -0
- package/vendor/nanocodex/cloud/actions/machineUsd.d.mts +23 -0
- package/vendor/nanocodex/cloud/actions/machineUsd.mjs +37 -0
- package/vendor/nanocodex/cloud/actions/model.d.mts +12 -0
- package/vendor/nanocodex/cloud/actions/model.mjs +50 -0
- package/vendor/nanocodex/cloud/actions/mpp.d.mts +28 -0
- package/vendor/nanocodex/cloud/actions/mpp.mjs +39 -0
- package/vendor/nanocodex/cloud/index.d.mts +30 -0
- package/vendor/nanocodex/cloud/index.mjs +9 -0
- package/vendor/nanocodex/cloud/internal.mjs +533 -0
- package/vendor/nanocodex/cloud/mercator.mjs +108 -0
- package/vendor/nanocodex/cloud/server/HostPrincipal.d.mts +34 -0
- package/vendor/nanocodex/cloud/server/HostPrincipal.mjs +202 -0
- package/vendor/nanocodex/cloud/server/index.d.mts +1 -0
- package/vendor/nanocodex/cloud/server/index.mjs +1 -0
- package/vendor/nanocodex/cloud/types.d.mts +211 -0
- package/vendor/nanocodex/cloudflare/Agent.d.mts +133 -0
- package/vendor/nanocodex/cloudflare/Agent.mjs +811 -0
- package/vendor/nanocodex/cloudflare/egress-subject.mjs +19 -0
- package/vendor/nanocodex/cloudflare/egress.d.mts +41 -0
- package/vendor/nanocodex/cloudflare/egress.mjs +119 -0
- package/vendor/nanocodex/cloudflare/event-socket.mjs +316 -0
- package/vendor/nanocodex/cloudflare/index.d.mts +7 -0
- package/vendor/nanocodex/cloudflare/index.mjs +6 -0
- package/vendor/nanocodex/host/Agent.d.mts +55 -0
- package/vendor/nanocodex/host/Agent.mjs +1 -0
- package/vendor/nanocodex/host/index.d.mts +82 -0
- package/vendor/nanocodex/host/index.mjs +10 -0
- package/vendor/nanocodex/index.d.mts +81 -0
- package/vendor/nanocodex/index.mjs +12 -0
- package/vendor/nanocodex/internal.mjs +1275 -0
- package/vendor/nanocodex/managed/Agent.d.mts +380 -0
- package/vendor/nanocodex/managed/Agent.mjs +1694 -0
- package/vendor/nanocodex/managed/ManagedError.d.mts +9 -0
- package/vendor/nanocodex/managed/ManagedError.mjs +8 -0
- package/vendor/nanocodex/managed/README.md +241 -0
- package/vendor/nanocodex/managed/Voice.mjs +202 -0
- package/vendor/nanocodex/managed/index.d.mts +20 -0
- package/vendor/nanocodex/managed/index.mjs +2 -0
- package/vendor/nanocodex/managed/internal.mjs +119 -0
- package/vendor/nanocodex/node/Agent.d.mts +50 -0
- package/vendor/nanocodex/node/Agent.mjs +203 -0
- package/vendor/nanocodex/node/ChatGptSubscription.d.mts +9 -0
- package/vendor/nanocodex/node/ChatGptSubscription.mjs +17 -0
- package/vendor/nanocodex/node/Transport.d.mts +44 -0
- package/vendor/nanocodex/node/Transport.mjs +46 -0
- package/vendor/nanocodex/node/host.mjs +731 -0
- package/vendor/nanocodex/node/index.d.mts +73 -0
- package/vendor/nanocodex/node/index.mjs +17 -0
- package/vendor/nanocodex/node/workspace.d.mts +10 -0
- package/vendor/nanocodex/node/workspace.mjs +198 -0
- package/vendor/nanocodex/package.json +200 -0
- package/vendor/nanocodex/pkg-node/nanocodex.d.ts +675 -0
- package/vendor/nanocodex/pkg-node/nanocodex.js +2628 -0
- package/vendor/nanocodex/pkg-node/nanocodex_bg.wasm.d.ts +105 -0
- package/vendor/nanocodex/pkg-node/package.json +1 -0
- package/vendor/nanocodex/pkg-web/.nanocodex-bindgen-stamp +6 -0
- package/vendor/nanocodex/pkg-web/nanocodex-build.json +1 -0
- package/vendor/nanocodex/pkg-web/nanocodex.d.ts +805 -0
- package/vendor/nanocodex/pkg-web/nanocodex.js +2714 -0
- package/vendor/nanocodex/pkg-web/nanocodex_bg.js +2615 -0
- package/vendor/nanocodex/pkg-web/nanocodex_bg.wasm +0 -0
- package/vendor/nanocodex/pkg-web/nanocodex_bg.wasm.d.ts +105 -0
- package/vendor/nanocodex/pkg-web/nanocodex_worker.js +9 -0
- package/vendor/nanocodex/pkg-web/package.json +1 -0
- package/vendor/nanocodex/runtime/chatgpt-subscription.mjs +212 -0
- package/vendor/nanocodex/runtime/cloudflare-durability-store.d.mts +26 -0
- package/vendor/nanocodex/runtime/cloudflare-durability-store.mjs +168 -0
- package/vendor/nanocodex/runtime/code-evaluator.worker.mjs +137 -0
- package/vendor/nanocodex/runtime/code-runtime.mjs +1 -0
- package/vendor/nanocodex/runtime/durability-store.d.mts +85 -0
- package/vendor/nanocodex/runtime/durability-store.mjs +631 -0
- package/vendor/nanocodex/runtime/durability.mjs +153 -0
- package/vendor/nanocodex/runtime/managed-transport.mjs +460 -0
- package/vendor/nanocodex/runtime/mcp-runtime.mjs +534 -0
- package/vendor/nanocodex/runtime/postgres-durability-store.d.mts +64 -0
- package/vendor/nanocodex/runtime/postgres-durability-store.mjs +532 -0
- package/vendor/nanocodex/runtime/quickjs-evaluator.d.mts +17 -0
- package/vendor/nanocodex/runtime/quickjs-evaluator.mjs +236 -0
- package/vendor/nanocodex/runtime/responses-transport.mjs +22 -0
- package/vendor/nanocodex/runtime/subagents.d.mts +107 -0
- package/vendor/nanocodex/runtime/subagents.mjs +54 -0
- package/vendor/nanocodex/runtime/subscription-store.d.mts +8 -0
- package/vendor/nanocodex/runtime/subscription-store.mjs +42 -0
- package/vendor/nanocodex/runtime/tempo-provider.d.mts +125 -0
- package/vendor/nanocodex/runtime/tempo-provider.mjs +264 -0
- package/vendor/nanocodex/runtime/tool-configuration.mjs +1 -0
- package/vendor/nanocodex/runtime/tool-router.mjs +1 -0
- package/vendor/nanocodex/runtime/utf8.mjs +1 -0
- package/vendor/nanocodex/runtime/worker-evaluator.mjs +162 -0
- package/vendor/nanocodex/runtime/workspace.d.mts +1 -0
- package/vendor/nanocodex/runtime/workspace.mjs +1 -0
- package/vendor/nanocodex/tools/Tools.d.mts +56 -0
- package/vendor/nanocodex/tools/Tools.mjs +136 -0
- package/vendor/nanocodex/tools/artifact.d.mts +1 -0
- package/vendor/nanocodex/tools/artifact.mjs +1 -0
- package/vendor/nanocodex/tools/attachment.mjs +1 -0
- package/vendor/nanocodex/tools/bash.d.mts +1 -0
- package/vendor/nanocodex/tools/bash.mjs +1 -0
- package/vendor/nanocodex/tools/browser/accountInfo.mjs +795 -0
- package/vendor/nanocodex/tools/browser/browserBuffer.mjs +18 -0
- package/vendor/nanocodex/tools/browser/browserCompiler.mjs +155 -0
- package/vendor/nanocodex/tools/browser/browserEgress.mjs +161 -0
- package/vendor/nanocodex/tools/browser/browserPython.mjs +133 -0
- package/vendor/nanocodex/tools/browser/browserShell.mjs +1075 -0
- package/vendor/nanocodex/tools/browser/browserSprintf.mjs +4 -0
- package/vendor/nanocodex/tools/browser/browserSsh.mjs +201 -0
- package/vendor/nanocodex/tools/browser/browserZlib.mjs +136 -0
- package/vendor/nanocodex/tools/browser/compiler.worker.mjs +160 -0
- package/vendor/nanocodex/tools/browser/devTunnelsSshBrowser.mjs +13 -0
- package/vendor/nanocodex/tools/browser/index.d.mts +209 -0
- package/vendor/nanocodex/tools/browser/index.mjs +174 -0
- package/vendor/nanocodex/tools/browser/opfsGit.mjs +248 -0
- package/vendor/nanocodex/tools/browser/python.worker.mjs +114 -0
- package/vendor/nanocodex/tools/browser/threadGit.mjs +197 -0
- package/vendor/nanocodex/tools/browser/unsupportedNodeRsa.mjs +5 -0
- package/vendor/nanocodex/tools/browser/workspace.mjs +83 -0
- package/vendor/nanocodex/tools/dataset.d.mts +1 -0
- package/vendor/nanocodex/tools/dataset.mjs +1 -0
- package/vendor/nanocodex/tools/datasetContract.mjs +1 -0
- package/vendor/nanocodex/tools/datasetEngine.mjs +1 -0
- package/vendor/nanocodex/tools/hostedCatalog.d.mts +1 -0
- package/vendor/nanocodex/tools/hostedCatalog.mjs +1 -0
- package/vendor/nanocodex/tools/index.d.mts +49 -0
- package/vendor/nanocodex/tools/index.mjs +19 -0
- package/vendor/nanocodex/tools/namedTool.mjs +1 -0
- package/vendor/nanocodex/tools/repository-workspace.d.mts +1 -0
- package/vendor/nanocodex/tools/repository-workspace.mjs +1 -0
- package/vendor/nanocodex/tools/ssh.d.mts +1 -0
- package/vendor/nanocodex/tools/ssh.mjs +1 -0
- package/vendor/nanocodex/tools/standard.mjs +1 -0
- package/vendor/nanocodex/tools/standardDescriptions.mjs +1 -0
- package/vendor/nanocodex/types.d.mts +663 -0
- package/vendor/nanocodex/wasm.d.mts +4 -0
- package/vendor/nanocodex/worker/ChatGptSubscription.d.mts +9 -0
- package/vendor/nanocodex/worker/ChatGptSubscription.mjs +29 -0
- package/vendor/nanocodex/worker/index.d.mts +18 -0
- package/vendor/nanocodex/worker/index.mjs +5 -0
- package/vendor/nanocodex-tools/README.md +65 -0
- package/vendor/nanocodex-tools/dist/hosted/app-tool-catalog.d.ts +7 -0
- package/vendor/nanocodex-tools/dist/hosted/app-tool-catalog.js +14 -0
- package/vendor/nanocodex-tools/dist/hosted/broker-core.d.ts +172 -0
- package/vendor/nanocodex-tools/dist/hosted/broker-core.js +1278 -0
- package/vendor/nanocodex-tools/dist/hosted/index.d.ts +3 -0
- package/vendor/nanocodex-tools/dist/hosted/index.js +3 -0
- package/vendor/nanocodex-tools/dist/hosted/protocol.d.ts +120 -0
- package/vendor/nanocodex-tools/dist/hosted/protocol.js +415 -0
- package/vendor/nanocodex-tools/dist/index.d.ts +19 -0
- package/vendor/nanocodex-tools/dist/index.js +14 -0
- package/vendor/nanocodex-tools/dist/memory.d.ts +204 -0
- package/vendor/nanocodex-tools/dist/memory.js +411 -0
- package/vendor/nanocodex-tools/dist/namespace.d.ts +87 -0
- package/vendor/nanocodex-tools/dist/namespace.js +338 -0
- package/vendor/nanocodex-tools/dist/repository-archive.d.ts +4 -0
- package/vendor/nanocodex-tools/dist/repository-archive.js +104 -0
- package/vendor/nanocodex-tools/dist/runtime.d.ts +30 -0
- package/vendor/nanocodex-tools/dist/runtime.js +37 -0
- package/vendor/nanocodex-tools/dist/session.d.ts +167 -0
- package/vendor/nanocodex-tools/dist/session.js +286 -0
- package/vendor/nanocodex-tools/dist/shell.d.ts +51 -0
- package/vendor/nanocodex-tools/dist/shell.js +553 -0
- package/vendor/nanocodex-tools/dist/ssh.d.ts +29 -0
- package/vendor/nanocodex-tools/dist/ssh.js +66 -0
- package/vendor/nanocodex-tools/dist/workspace.d.ts +45 -0
- package/vendor/nanocodex-tools/dist/workspace.js +237 -0
- package/vendor/nanocodex-tools/package.json +122 -0
- package/vendor/nanocodex-tools/runtime/code-runtime.mjs +817 -0
- package/vendor/nanocodex-tools/runtime/tool-configuration.mjs +50 -0
- package/vendor/nanocodex-tools/runtime/tool-router.mjs +730 -0
- package/vendor/nanocodex-tools/runtime/utf8.d.mts +2 -0
- package/vendor/nanocodex-tools/runtime/utf8.mjs +25 -0
- package/vendor/nanocodex-tools/tools/artifact.d.mts +69 -0
- package/vendor/nanocodex-tools/tools/artifact.mjs +270 -0
- package/vendor/nanocodex-tools/tools/attachment.mjs +603 -0
- package/vendor/nanocodex-tools/tools/bash.d.mts +90 -0
- package/vendor/nanocodex-tools/tools/bash.mjs +742 -0
- package/vendor/nanocodex-tools/tools/dataset.d.mts +9 -0
- package/vendor/nanocodex-tools/tools/dataset.mjs +47 -0
- package/vendor/nanocodex-tools/tools/datasetContract.mjs +54 -0
- package/vendor/nanocodex-tools/tools/datasetEngine.mjs +1167 -0
- package/vendor/nanocodex-tools/tools/execution-contract.d.mts +5 -0
- package/vendor/nanocodex-tools/tools/execution-contract.mjs +63 -0
- package/vendor/nanocodex-tools/tools/hostedCatalog.d.mts +20 -0
- package/vendor/nanocodex-tools/tools/hostedCatalog.mjs +67 -0
- package/vendor/nanocodex-tools/tools/hostedMachine.d.mts +10 -0
- package/vendor/nanocodex-tools/tools/hostedMachine.mjs +56 -0
- package/vendor/nanocodex-tools/tools/just-bash-browser.d.mts +1 -0
- package/vendor/nanocodex-tools/tools/just-bash-browser.mjs +1 -0
- package/vendor/nanocodex-tools/tools/namedTool.d.mts +3 -0
- package/vendor/nanocodex-tools/tools/namedTool.mjs +3 -0
- package/vendor/nanocodex-tools/tools/nodeProcess.d.mts +10 -0
- package/vendor/nanocodex-tools/tools/nodeProcess.mjs +314 -0
- package/vendor/nanocodex-tools/tools/processOutput.mjs +56 -0
- package/vendor/nanocodex-tools/tools/repository-workspace.d.mts +55 -0
- package/vendor/nanocodex-tools/tools/repository-workspace.mjs +397 -0
- package/vendor/nanocodex-tools/tools/ssh.d.mts +56 -0
- package/vendor/nanocodex-tools/tools/ssh.mjs +503 -0
- package/vendor/nanocodex-tools/tools/standard.mjs +458 -0
- package/vendor/nanocodex-tools/tools/standardDescriptions.mjs +3 -0
- package/vendor/nanocodex-tools/tools/types.d.mts +83 -0
- package/vendor/nanocodex-tools/tools/workspace.d.mts +25 -0
- package/vendor/nanocodex-tools/tools/workspace.mjs +282 -0
- package/vendor/nanocodex-tools/tools/x.d.mts +30 -0
- package/vendor/nanocodex-tools/tools/x.mjs +91 -0
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
import { Console } from "node:console";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import WebSocket from "ws";
|
|
5
|
+
import packageMetadata from "../package.json" with { type: "json" };
|
|
6
|
+
|
|
7
|
+
import { createCodeRuntime } from "../runtime/code-runtime.mjs";
|
|
8
|
+
import { createMcpRuntime } from "../runtime/mcp-runtime.mjs";
|
|
9
|
+
import {
|
|
10
|
+
settleCleanup,
|
|
11
|
+
toolRouterBrand,
|
|
12
|
+
toolRouterRuntime,
|
|
13
|
+
toolRuntimeLifecycle,
|
|
14
|
+
} from "../runtime/tool-router.mjs";
|
|
15
|
+
import { utf8ByteLength } from "../runtime/utf8.mjs";
|
|
16
|
+
|
|
17
|
+
const RESPONSES_WEBSOCKETS_BETA = "responses_websockets=2026-02-06";
|
|
18
|
+
const USER_AGENT = `nanocodex-wasm/${packageMetadata.version}`;
|
|
19
|
+
const DEFAULT_MAX_QUEUED_MESSAGES = 4_096;
|
|
20
|
+
const DEFAULT_MAX_QUEUED_BYTES = 32 * 1024 * 1024;
|
|
21
|
+
const DEFAULT_MAX_FRAME_BYTES = 16 * 1024 * 1024;
|
|
22
|
+
const DEFAULT_CONNECT_TIMEOUT_MS = 15_000;
|
|
23
|
+
const MAX_HTTP_ERROR_BYTES = 64 * 1024;
|
|
24
|
+
const MPP_CLIENT_PROTOCOL_ERROR_CLOSE_CODE = 3008;
|
|
25
|
+
|
|
26
|
+
export function createNodeHost(options = {}) {
|
|
27
|
+
if (options.resolveCompactionInstruction !== undefined
|
|
28
|
+
&& typeof options.resolveCompactionInstruction !== "function") {
|
|
29
|
+
throw new TypeError("resolveCompactionInstruction must be a function");
|
|
30
|
+
}
|
|
31
|
+
const toolMode = options.toolMode ?? "code";
|
|
32
|
+
if (toolMode !== "code" && toolMode !== "direct") {
|
|
33
|
+
throw new TypeError("toolMode must be code or direct");
|
|
34
|
+
}
|
|
35
|
+
const toolsRouter = options.tools?.[toolRouterBrand]
|
|
36
|
+
? options.tools[toolRouterRuntime]
|
|
37
|
+
: undefined;
|
|
38
|
+
const toolsMcp = toolsRouter?.hasSourceKind("mcp") === true;
|
|
39
|
+
if (toolsRouter?.hasSource("workspace") && options.filesystem) {
|
|
40
|
+
throw new TypeError("workspace is already configured in Tools");
|
|
41
|
+
}
|
|
42
|
+
if (toolsMcp && options.mcpServers) {
|
|
43
|
+
throw new TypeError("MCP is already configured in Tools");
|
|
44
|
+
}
|
|
45
|
+
if ((toolsMcp || options.mcpServers) && toolMode !== "code") {
|
|
46
|
+
throw new TypeError("remote MCP requires Code Mode");
|
|
47
|
+
}
|
|
48
|
+
const toolsLifecycle = options.tools?.[toolRuntimeLifecycle];
|
|
49
|
+
toolsLifecycle?.available();
|
|
50
|
+
const connections = new Map();
|
|
51
|
+
const code = createCodeRuntime(options.tools, {
|
|
52
|
+
require: createRequire(resolve(options.workspace ?? process.cwd(), ".nanocodex-code-mode.cjs")),
|
|
53
|
+
console: new Console({ stdout: process.stderr, stderr: process.stderr }),
|
|
54
|
+
evaluate: options.codeEvaluator,
|
|
55
|
+
});
|
|
56
|
+
const filesystem = options.filesystem
|
|
57
|
+
? import("../runtime/workspace.mjs")
|
|
58
|
+
.then(({ tools }) => code.addTools(tools(options.filesystem)))
|
|
59
|
+
: undefined;
|
|
60
|
+
const mcp = options.mcpServers
|
|
61
|
+
? createMcpRuntime(options.mcpServers, { clientName: "nanocodex-node" })
|
|
62
|
+
: undefined;
|
|
63
|
+
let disposal;
|
|
64
|
+
const mcpInstalled = mcp?.then(async (provider) => {
|
|
65
|
+
if (disposal) {
|
|
66
|
+
await provider.close();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
try { code.addProvider(provider, { id: "mcp", kind: "mcp" }); }
|
|
70
|
+
catch (error) {
|
|
71
|
+
try { await provider.close(); }
|
|
72
|
+
catch (cleanupError) {
|
|
73
|
+
throw new AggregateError([error, cleanupError], "MCP installation and cleanup failed");
|
|
74
|
+
}
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
const onEvent = options.onEvent || (() => {});
|
|
79
|
+
const connectTimeoutMs = Math.min(
|
|
80
|
+
options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS,
|
|
81
|
+
DEFAULT_CONNECT_TIMEOUT_MS,
|
|
82
|
+
);
|
|
83
|
+
const sendTimeoutMs = options.sendTimeoutMs ?? 30_000;
|
|
84
|
+
const maxQueuedMessages = options.maxQueuedMessages ?? DEFAULT_MAX_QUEUED_MESSAGES;
|
|
85
|
+
const maxQueuedBytes = options.maxQueuedBytes ?? DEFAULT_MAX_QUEUED_BYTES;
|
|
86
|
+
const maxFrameBytes = options.maxFrameBytes ?? DEFAULT_MAX_FRAME_BYTES;
|
|
87
|
+
let nextHandle = 1;
|
|
88
|
+
let references = 0;
|
|
89
|
+
const httpConnections = new Map();
|
|
90
|
+
|
|
91
|
+
function connect(endpoint, apiKey, sessionId, metadata = {}) {
|
|
92
|
+
if (options.mpp) return connectMpp(endpoint);
|
|
93
|
+
let cancelConnection = () => {};
|
|
94
|
+
const pending = new Promise((resolve, reject) => {
|
|
95
|
+
let settled = false;
|
|
96
|
+
let upgradeResponse;
|
|
97
|
+
let upgradeRequest;
|
|
98
|
+
let deadline;
|
|
99
|
+
let rejectionResponse;
|
|
100
|
+
let rejectionSnapshot;
|
|
101
|
+
const threadId = metadata.threadId ?? sessionId;
|
|
102
|
+
const headers = {
|
|
103
|
+
Authorization: `Bearer ${apiKey}`,
|
|
104
|
+
"OpenAI-Beta": RESPONSES_WEBSOCKETS_BETA,
|
|
105
|
+
"x-openai-internal-codex-responses-lite": "true",
|
|
106
|
+
"session-id": sessionId,
|
|
107
|
+
"thread-id": threadId,
|
|
108
|
+
"x-client-request-id": threadId,
|
|
109
|
+
"x-responsesapi-include-timing-metrics": "true",
|
|
110
|
+
"User-Agent": USER_AGENT,
|
|
111
|
+
};
|
|
112
|
+
if (metadata.accountId) headers["ChatGPT-Account-ID"] = metadata.accountId;
|
|
113
|
+
if (metadata.fedramp) headers["X-OpenAI-Fedramp"] = "true";
|
|
114
|
+
if (metadata.turnState) headers["x-codex-turn-state"] = metadata.turnState;
|
|
115
|
+
const socket = new WebSocket(endpoint, {
|
|
116
|
+
maxPayload: maxFrameBytes,
|
|
117
|
+
headers,
|
|
118
|
+
});
|
|
119
|
+
const handle = nextHandle++;
|
|
120
|
+
const connection = queueState(socket);
|
|
121
|
+
connection.connecting = true;
|
|
122
|
+
connections.set(handle, connection);
|
|
123
|
+
|
|
124
|
+
const fail = (error, reset = false) => {
|
|
125
|
+
if (settled) return;
|
|
126
|
+
settled = true;
|
|
127
|
+
clearTimeout(deadline);
|
|
128
|
+
connections.delete(handle);
|
|
129
|
+
connection.intentionallyClosed = true;
|
|
130
|
+
destroyWebSocket(socket, upgradeRequest, rejectionResponse, reset);
|
|
131
|
+
reject(error);
|
|
132
|
+
};
|
|
133
|
+
const rejectAsClosed = () => {
|
|
134
|
+
const error = new Error("WebSocket connection was closed by the host");
|
|
135
|
+
error.reconnectable = false;
|
|
136
|
+
fail(error, true);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
socket.on("upgrade", (response) => { upgradeResponse = response; });
|
|
140
|
+
socket.on("unexpected-response", (request, response) => {
|
|
141
|
+
if (settled) return;
|
|
142
|
+
upgradeRequest = request;
|
|
143
|
+
rejectionResponse = response;
|
|
144
|
+
rejectionSnapshot = readHandshakeRejection(response, (error) => fail(error));
|
|
145
|
+
});
|
|
146
|
+
socket.on("open", () => {
|
|
147
|
+
if (settled) {
|
|
148
|
+
destroyWebSocket(socket);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
settled = true;
|
|
152
|
+
clearTimeout(deadline);
|
|
153
|
+
connection.connecting = false;
|
|
154
|
+
const headers = upgradeResponse?.headers || {};
|
|
155
|
+
resolve(JSON.stringify({
|
|
156
|
+
handle,
|
|
157
|
+
status: upgradeResponse?.statusCode || 101,
|
|
158
|
+
request_id: header(headers, "x-request-id"),
|
|
159
|
+
server_model: header(headers, "openai-model"),
|
|
160
|
+
reasoning_included: header(headers, "x-reasoning-included") !== undefined,
|
|
161
|
+
turn_state: header(headers, "x-codex-turn-state"),
|
|
162
|
+
}));
|
|
163
|
+
});
|
|
164
|
+
socket.on("message", (data, isBinary) => {
|
|
165
|
+
enqueue(connection, isBinary
|
|
166
|
+
? { kind: "binary" }
|
|
167
|
+
: { kind: "text", text: data.toString("utf8") });
|
|
168
|
+
});
|
|
169
|
+
socket.on("close", (status, reason) => {
|
|
170
|
+
if (!settled) {
|
|
171
|
+
const suffix = reason.length ? `: ${reason.toString("utf8")}` : "";
|
|
172
|
+
fail(new Error(`WebSocket connection closed during handshake with code ${status}${suffix}`));
|
|
173
|
+
} else if (!connection.intentionallyClosed && !connection.overflowed) {
|
|
174
|
+
const suffix = reason.length ? `: ${reason.toString("utf8")}` : "";
|
|
175
|
+
enqueue(connection, { kind: "closed", detail: `with code ${status}${suffix}` });
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
socket.on("error", (error) => {
|
|
179
|
+
if (!settled) {
|
|
180
|
+
fail(error, true);
|
|
181
|
+
} else {
|
|
182
|
+
enqueue(connection, { kind: "error", detail: errorMessage(error) });
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
deadline = setTimeout(() => {
|
|
186
|
+
const error = rejectionSnapshot?.(
|
|
187
|
+
new Error(`WebSocket handshake exceeded ${connectTimeoutMs} milliseconds`),
|
|
188
|
+
) || new Error(`WebSocket handshake exceeded ${connectTimeoutMs} milliseconds`);
|
|
189
|
+
error.reconnectable = true;
|
|
190
|
+
fail(error, true);
|
|
191
|
+
}, connectTimeoutMs);
|
|
192
|
+
connection.reject = rejectAsClosed;
|
|
193
|
+
cancelConnection = rejectAsClosed;
|
|
194
|
+
});
|
|
195
|
+
pending.cancel = () => cancelConnection();
|
|
196
|
+
return pending;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function connectMpp(endpoint) {
|
|
200
|
+
if (typeof options.mpp.ws !== "function") {
|
|
201
|
+
throw new TypeError("mpp must provide ws(endpoint)");
|
|
202
|
+
}
|
|
203
|
+
const socket = await options.mpp.ws(endpoint);
|
|
204
|
+
if (!socket || typeof socket.addEventListener !== "function") {
|
|
205
|
+
throw new TypeError("mpp.ws(endpoint) must return a WebSocket");
|
|
206
|
+
}
|
|
207
|
+
const handle = nextHandle++;
|
|
208
|
+
const connection = queueState(socket);
|
|
209
|
+
connection.managed = true;
|
|
210
|
+
connections.set(handle, connection);
|
|
211
|
+
socket.addEventListener("message", (event) => {
|
|
212
|
+
enqueue(connection, typeof event.data === "string"
|
|
213
|
+
? { kind: "text", text: event.data }
|
|
214
|
+
: { kind: "binary" });
|
|
215
|
+
});
|
|
216
|
+
socket.addEventListener("close", (event) => {
|
|
217
|
+
if (!connection.intentionallyClosed && !connection.overflowed) {
|
|
218
|
+
const code = event.code ?? 1000;
|
|
219
|
+
const suffix = event.reason ? `: ${event.reason}` : "";
|
|
220
|
+
enqueue(connection, code === MPP_CLIENT_PROTOCOL_ERROR_CLOSE_CODE
|
|
221
|
+
? {
|
|
222
|
+
kind: "error",
|
|
223
|
+
detail: `MPP WebSocket payment flow failed with code ${code}${suffix}`,
|
|
224
|
+
reconnectable: false,
|
|
225
|
+
}
|
|
226
|
+
: { kind: "closed", detail: `with code ${code}${suffix}` });
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
socket.addEventListener("error", () => {
|
|
230
|
+
enqueue(connection, { kind: "error", detail: "MPP WebSocket connection failed" });
|
|
231
|
+
});
|
|
232
|
+
return JSON.stringify({ handle, status: 101, reasoning_included: false });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function httpOpen(endpoint, apiKey, sessionId, body, metadata = {}) {
|
|
236
|
+
if (options.mpp) {
|
|
237
|
+
throw new Error("MPP transport does not support HTTPS Responses fallback");
|
|
238
|
+
}
|
|
239
|
+
if (typeof fetch !== "function") {
|
|
240
|
+
throw new Error("the Node host requires a global fetch implementation for HTTPS Responses");
|
|
241
|
+
}
|
|
242
|
+
const controller = new AbortController();
|
|
243
|
+
const timer = setTimeout(() => controller.abort(), connectTimeoutMs);
|
|
244
|
+
const threadId = metadata.threadId ?? sessionId;
|
|
245
|
+
const headers = {
|
|
246
|
+
Authorization: `Bearer ${apiKey}`,
|
|
247
|
+
"Content-Type": "application/json",
|
|
248
|
+
Accept: "text/event-stream",
|
|
249
|
+
"x-openai-internal-codex-responses-lite": "true",
|
|
250
|
+
"session-id": sessionId,
|
|
251
|
+
"thread-id": threadId,
|
|
252
|
+
"x-client-request-id": threadId,
|
|
253
|
+
"User-Agent": USER_AGENT,
|
|
254
|
+
};
|
|
255
|
+
if (metadata.accountId) headers["ChatGPT-Account-ID"] = metadata.accountId;
|
|
256
|
+
if (metadata.fedramp) headers["X-OpenAI-Fedramp"] = "true";
|
|
257
|
+
if (metadata.turnState) headers["x-codex-turn-state"] = metadata.turnState;
|
|
258
|
+
let responsePromise;
|
|
259
|
+
try {
|
|
260
|
+
responsePromise = fetch(endpoint, {
|
|
261
|
+
method: "POST",
|
|
262
|
+
headers,
|
|
263
|
+
body,
|
|
264
|
+
signal: controller.signal,
|
|
265
|
+
});
|
|
266
|
+
} catch (error) {
|
|
267
|
+
controller.abort();
|
|
268
|
+
clearTimeout(timer);
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
const handle = nextHandle++;
|
|
272
|
+
const connection = {
|
|
273
|
+
response: undefined,
|
|
274
|
+
responsePromise: undefined,
|
|
275
|
+
controller,
|
|
276
|
+
reader: undefined,
|
|
277
|
+
decoder: new TextDecoder("utf-8", { fatal: true }),
|
|
278
|
+
reading: false,
|
|
279
|
+
closed: false,
|
|
280
|
+
};
|
|
281
|
+
connection.responsePromise = Promise.resolve(responsePromise).then((response) => {
|
|
282
|
+
clearTimeout(timer);
|
|
283
|
+
if (connection.closed) {
|
|
284
|
+
void response.body?.cancel().catch(() => {});
|
|
285
|
+
} else {
|
|
286
|
+
connection.response = response;
|
|
287
|
+
}
|
|
288
|
+
return response;
|
|
289
|
+
}, (error) => {
|
|
290
|
+
clearTimeout(timer);
|
|
291
|
+
const timedOut = error?.name === "AbortError";
|
|
292
|
+
const failure = new Error(timedOut
|
|
293
|
+
? `HTTPS Responses request headers exceeded ${connectTimeoutMs} milliseconds`
|
|
294
|
+
: errorMessage(error));
|
|
295
|
+
failure.reconnectable = true;
|
|
296
|
+
failure.timeout = timedOut;
|
|
297
|
+
throw failure;
|
|
298
|
+
});
|
|
299
|
+
// A caller may close the handle before awaiting headers. Keep the
|
|
300
|
+
// rejection observed while preserving it for the header caller.
|
|
301
|
+
connection.responsePromise.catch(() => {});
|
|
302
|
+
httpConnections.set(handle, connection);
|
|
303
|
+
return JSON.stringify({ handle });
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async function httpHeaders(handle) {
|
|
307
|
+
const connection = httpConnections.get(handle);
|
|
308
|
+
if (!connection || connection.closed) {
|
|
309
|
+
throw new Error("unknown HTTPS handle");
|
|
310
|
+
}
|
|
311
|
+
let response;
|
|
312
|
+
try {
|
|
313
|
+
response = await connection.responsePromise;
|
|
314
|
+
} catch (error) {
|
|
315
|
+
closeHttp(connection, handle);
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
if (connection.closed) throw new Error("HTTPS Responses handle was closed");
|
|
319
|
+
if (!response.ok) {
|
|
320
|
+
try {
|
|
321
|
+
const body = await readHttpErrorBody(connection);
|
|
322
|
+
const error = new Error(
|
|
323
|
+
`HTTPS Responses request was rejected with HTTP ${response.status}`,
|
|
324
|
+
);
|
|
325
|
+
error.status = response.status;
|
|
326
|
+
error.body = body;
|
|
327
|
+
const retryAfter = Number(response.headers.get("retry-after"));
|
|
328
|
+
if (Number.isFinite(retryAfter) && retryAfter >= 0) error.retryAfter = retryAfter;
|
|
329
|
+
throw error;
|
|
330
|
+
} finally {
|
|
331
|
+
closeHttp(connection, handle);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return JSON.stringify({
|
|
335
|
+
status: response.status,
|
|
336
|
+
request_id: response.headers.get("x-request-id") ?? undefined,
|
|
337
|
+
server_model: response.headers.get("openai-model") ?? undefined,
|
|
338
|
+
reasoning_included: response.headers.has("x-reasoning-included"),
|
|
339
|
+
turn_state: response.headers.get("x-codex-turn-state") ?? undefined,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function send(handle, message) {
|
|
344
|
+
if (httpConnections.has(handle)) {
|
|
345
|
+
return Promise.resolve(JSON.stringify({
|
|
346
|
+
ok: false,
|
|
347
|
+
reconnectable: false,
|
|
348
|
+
error: "HTTPS Responses connections do not accept WebSocket frames",
|
|
349
|
+
}));
|
|
350
|
+
}
|
|
351
|
+
const connection = connections.get(handle);
|
|
352
|
+
if (!connection || connection.socket.readyState !== WebSocket.OPEN) {
|
|
353
|
+
return Promise.resolve(JSON.stringify({
|
|
354
|
+
ok: false,
|
|
355
|
+
reconnectable: true,
|
|
356
|
+
error: "WebSocket is no longer open",
|
|
357
|
+
}));
|
|
358
|
+
}
|
|
359
|
+
if (connection.managed) {
|
|
360
|
+
try {
|
|
361
|
+
connection.socket.send(JSON.stringify({ mpp: "message", data: message }));
|
|
362
|
+
return Promise.resolve(JSON.stringify({ ok: true }));
|
|
363
|
+
} catch (error) {
|
|
364
|
+
return Promise.resolve(JSON.stringify({
|
|
365
|
+
ok: false,
|
|
366
|
+
reconnectable: connection.socket.readyState !== WebSocket.OPEN,
|
|
367
|
+
error: errorMessage(error),
|
|
368
|
+
}));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return new Promise((resolve) => {
|
|
372
|
+
let completed = false;
|
|
373
|
+
const timer = setTimeout(() => finish({
|
|
374
|
+
ok: false,
|
|
375
|
+
reconnectable: false,
|
|
376
|
+
error: `sending a WebSocket frame exceeded ${sendTimeoutMs} milliseconds`,
|
|
377
|
+
}), sendTimeoutMs);
|
|
378
|
+
function finish(result) {
|
|
379
|
+
if (completed) return;
|
|
380
|
+
completed = true;
|
|
381
|
+
clearTimeout(timer);
|
|
382
|
+
resolve(JSON.stringify(result));
|
|
383
|
+
}
|
|
384
|
+
connection.socket.send(message, (error) => finish(error ? {
|
|
385
|
+
ok: false,
|
|
386
|
+
reconnectable: connection.socket.readyState !== WebSocket.OPEN,
|
|
387
|
+
error: errorMessage(error),
|
|
388
|
+
} : { ok: true }));
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function next(handle, timeoutMs) {
|
|
393
|
+
if (httpConnections.has(handle)) return nextHttp(handle, timeoutMs);
|
|
394
|
+
const connection = connections.get(handle);
|
|
395
|
+
if (!connection) {
|
|
396
|
+
return Promise.resolve(JSON.stringify({ kind: "closed", detail: "before the next frame" }));
|
|
397
|
+
}
|
|
398
|
+
if (connection.queue.length) {
|
|
399
|
+
const entry = connection.queue.shift();
|
|
400
|
+
connection.queuedBytes -= entry.bytes;
|
|
401
|
+
return Promise.resolve(JSON.stringify(entry.message));
|
|
402
|
+
}
|
|
403
|
+
if (connection.waiter) return Promise.reject(new Error("concurrent reads are unsupported"));
|
|
404
|
+
return new Promise((resolve) => {
|
|
405
|
+
const timer = setTimeout(() => {
|
|
406
|
+
connection.waiter = undefined;
|
|
407
|
+
resolve(JSON.stringify({ kind: "timeout" }));
|
|
408
|
+
}, timeoutMs);
|
|
409
|
+
connection.waiter = (message) => {
|
|
410
|
+
clearTimeout(timer);
|
|
411
|
+
connection.waiter = undefined;
|
|
412
|
+
resolve(JSON.stringify(message));
|
|
413
|
+
};
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function close(handle) {
|
|
418
|
+
const httpConnection = httpConnections.get(handle);
|
|
419
|
+
if (httpConnection) {
|
|
420
|
+
httpConnections.delete(handle);
|
|
421
|
+
closeHttp(httpConnection, handle);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const connection = connections.get(handle);
|
|
425
|
+
if (!connection) return;
|
|
426
|
+
if (connection.connecting) {
|
|
427
|
+
connection.reject?.();
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
connections.delete(handle);
|
|
431
|
+
connection.intentionallyClosed = true;
|
|
432
|
+
connection.waiter?.({ kind: "closed", detail: "by the WASM runtime" });
|
|
433
|
+
connection.socket.close();
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async function nextHttp(handle, timeoutMs) {
|
|
437
|
+
const connection = httpConnections.get(handle);
|
|
438
|
+
if (!connection || connection.closed) {
|
|
439
|
+
return JSON.stringify({ kind: "closed", detail: "before the next HTTPS chunk" });
|
|
440
|
+
}
|
|
441
|
+
if (connection.reading) {
|
|
442
|
+
throw new Error("concurrent HTTPS reads are unsupported");
|
|
443
|
+
}
|
|
444
|
+
connection.reading = true;
|
|
445
|
+
try {
|
|
446
|
+
if (!connection.response) {
|
|
447
|
+
closeHttp(connection, handle);
|
|
448
|
+
return JSON.stringify({ kind: "error", detail: "HTTPS headers were not consumed" });
|
|
449
|
+
}
|
|
450
|
+
if (!connection.reader) {
|
|
451
|
+
if (!connection.response.body) {
|
|
452
|
+
httpConnections.delete(handle);
|
|
453
|
+
closeHttp(connection, handle, false);
|
|
454
|
+
return JSON.stringify({ kind: "eof" });
|
|
455
|
+
}
|
|
456
|
+
connection.reader = connection.response.body.getReader();
|
|
457
|
+
}
|
|
458
|
+
const read = connection.reader.read();
|
|
459
|
+
let timeout;
|
|
460
|
+
let result;
|
|
461
|
+
try {
|
|
462
|
+
result = await Promise.race([
|
|
463
|
+
read,
|
|
464
|
+
new Promise((resolve) => {
|
|
465
|
+
timeout = setTimeout(() => resolve({ timedOut: true }), timeoutMs);
|
|
466
|
+
}),
|
|
467
|
+
]);
|
|
468
|
+
} finally {
|
|
469
|
+
clearTimeout(timeout);
|
|
470
|
+
}
|
|
471
|
+
if (result.timedOut) {
|
|
472
|
+
read.catch(() => {});
|
|
473
|
+
closeHttp(connection, handle);
|
|
474
|
+
return JSON.stringify({ kind: "timeout" });
|
|
475
|
+
}
|
|
476
|
+
if (result.done) {
|
|
477
|
+
let tail;
|
|
478
|
+
try {
|
|
479
|
+
tail = connection.decoder.decode();
|
|
480
|
+
} catch (error) {
|
|
481
|
+
closeHttp(connection, handle);
|
|
482
|
+
return JSON.stringify({
|
|
483
|
+
kind: "error",
|
|
484
|
+
detail: `invalid HTTPS response UTF-8: ${errorMessage(error)}`,
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
if (tail) {
|
|
488
|
+
return JSON.stringify({ kind: "chunk", text: tail });
|
|
489
|
+
}
|
|
490
|
+
httpConnections.delete(handle);
|
|
491
|
+
closeHttp(connection, handle, false);
|
|
492
|
+
return JSON.stringify({ kind: "eof" });
|
|
493
|
+
}
|
|
494
|
+
let text;
|
|
495
|
+
try {
|
|
496
|
+
text = connection.decoder.decode(result.value, { stream: true });
|
|
497
|
+
} catch (error) {
|
|
498
|
+
closeHttp(connection, handle);
|
|
499
|
+
return JSON.stringify({
|
|
500
|
+
kind: "error",
|
|
501
|
+
detail: `invalid HTTPS response UTF-8: ${errorMessage(error)}`,
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
return JSON.stringify({ kind: "chunk", text });
|
|
505
|
+
} catch (error) {
|
|
506
|
+
closeHttp(connection, handle);
|
|
507
|
+
throw error;
|
|
508
|
+
} finally {
|
|
509
|
+
connection.reading = false;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
async function readHttpErrorBody(connection) {
|
|
514
|
+
if (!connection.response.body) return "";
|
|
515
|
+
const reader = connection.reader || connection.response.body.getReader();
|
|
516
|
+
connection.reader = reader;
|
|
517
|
+
const decoder = new TextDecoder();
|
|
518
|
+
const parts = [];
|
|
519
|
+
let length = 0;
|
|
520
|
+
let truncated = false;
|
|
521
|
+
try {
|
|
522
|
+
while (true) {
|
|
523
|
+
const { done, value } = await reader.read();
|
|
524
|
+
if (done) break;
|
|
525
|
+
const remaining = MAX_HTTP_ERROR_BYTES - length;
|
|
526
|
+
if (remaining <= 0) {
|
|
527
|
+
truncated = true;
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
const chunk = value.subarray(0, remaining);
|
|
531
|
+
parts.push(decoder.decode(chunk, { stream: true }));
|
|
532
|
+
length += chunk.byteLength;
|
|
533
|
+
if (chunk.byteLength < value.byteLength) {
|
|
534
|
+
truncated = true;
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
parts.push(decoder.decode());
|
|
539
|
+
} finally {
|
|
540
|
+
if (truncated) void reader.cancel().catch(() => {});
|
|
541
|
+
}
|
|
542
|
+
return parts.join("") + (truncated ? "…" : "");
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function closeHttp(connection, handle, abort = true) {
|
|
546
|
+
if (connection.closed) return;
|
|
547
|
+
connection.closed = true;
|
|
548
|
+
if (abort) connection.controller.abort();
|
|
549
|
+
if (connection.reader) void connection.reader.cancel().catch(() => {});
|
|
550
|
+
if (!connection.response && connection.responsePromise) {
|
|
551
|
+
connection.responsePromise.then((response) => response.body?.cancel()).catch(() => {});
|
|
552
|
+
}
|
|
553
|
+
if (httpConnections.get(handle) === connection) httpConnections.delete(handle);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function enqueue(connection, message) {
|
|
557
|
+
if (connection.overflowed) return;
|
|
558
|
+
if (connection.waiter) {
|
|
559
|
+
connection.waiter(message);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
const bytes = messageBytes(message);
|
|
563
|
+
if (connection.queue.length >= maxQueuedMessages || connection.queuedBytes + bytes > maxQueuedBytes) {
|
|
564
|
+
connection.queue.length = 0;
|
|
565
|
+
connection.queuedBytes = 0;
|
|
566
|
+
connection.overflowed = true;
|
|
567
|
+
const error = {
|
|
568
|
+
kind: "error",
|
|
569
|
+
detail: `receive queue exceeded ${maxQueuedMessages} messages or ${maxQueuedBytes} bytes`,
|
|
570
|
+
};
|
|
571
|
+
connection.queue.push({ message: error, bytes: messageBytes(error) });
|
|
572
|
+
if (typeof connection.socket.terminate === "function") connection.socket.terminate();
|
|
573
|
+
else connection.socket.close(1009, "receive queue exceeded configured bounds");
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
connection.queue.push({ message, bytes });
|
|
577
|
+
connection.queuedBytes += bytes;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function dispose() {
|
|
581
|
+
if (disposal) return disposal;
|
|
582
|
+
disposal = Promise.resolve().then(() => settleCleanup([
|
|
583
|
+
...[...connections.keys()].map((handle) => () => close(handle)),
|
|
584
|
+
...[...httpConnections.keys()].map((handle) => () => close(handle)),
|
|
585
|
+
() => code.reset(),
|
|
586
|
+
() => mcpInstalled,
|
|
587
|
+
() => toolsLifecycle?.close(),
|
|
588
|
+
() => options.onDispose?.(),
|
|
589
|
+
], "Nanocodex host disposal failed", disposal));
|
|
590
|
+
return disposal;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
toolsLifecycle?.claim();
|
|
594
|
+
return Object.freeze({
|
|
595
|
+
ready: async () => { await Promise.all([filesystem, mcpInstalled]); },
|
|
596
|
+
retain() {
|
|
597
|
+
if (disposal) throw new Error("Nanocodex host is already disposed");
|
|
598
|
+
references += 1;
|
|
599
|
+
},
|
|
600
|
+
release() {
|
|
601
|
+
if (references > 0) references -= 1;
|
|
602
|
+
return references === 0 ? dispose() : Promise.resolve();
|
|
603
|
+
},
|
|
604
|
+
connect,
|
|
605
|
+
httpOpen,
|
|
606
|
+
httpHeaders,
|
|
607
|
+
send,
|
|
608
|
+
next,
|
|
609
|
+
close,
|
|
610
|
+
sleep: (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
|
|
611
|
+
executeCode: code.executeCodeObserved,
|
|
612
|
+
waitCode: code.waitCodeObserved,
|
|
613
|
+
beginCodeTurn: code.beginTurn,
|
|
614
|
+
cancelCodeTurn: code.cancelTurn,
|
|
615
|
+
nextCodeUpdate: code.nextCodeUpdate,
|
|
616
|
+
executeTool: code.executeTool,
|
|
617
|
+
bindSubagentSession: code.bindSubagentSession,
|
|
618
|
+
cancelCode: code.cancel,
|
|
619
|
+
toolMode: () => toolMode,
|
|
620
|
+
toolDefinitions: code.toolDefinitions,
|
|
621
|
+
resolveCompactionInstruction: (context, signal) => {
|
|
622
|
+
if (typeof options.resolveCompactionInstruction !== "function") {
|
|
623
|
+
throw new Error("resolveCompactionInstruction is not configured");
|
|
624
|
+
}
|
|
625
|
+
return options.resolveCompactionInstruction(context, signal);
|
|
626
|
+
},
|
|
627
|
+
releaseSession: code.releaseSession,
|
|
628
|
+
emitEvent: onEvent,
|
|
629
|
+
reset: code.reset,
|
|
630
|
+
dispose,
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
function queueState(socket) {
|
|
635
|
+
return {
|
|
636
|
+
socket,
|
|
637
|
+
connecting: false,
|
|
638
|
+
reject: undefined,
|
|
639
|
+
queue: [],
|
|
640
|
+
queuedBytes: 0,
|
|
641
|
+
waiter: undefined,
|
|
642
|
+
intentionallyClosed: false,
|
|
643
|
+
overflowed: false,
|
|
644
|
+
managed: false,
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function readHandshakeRejection(response, finish) {
|
|
649
|
+
const chunks = [];
|
|
650
|
+
let bytes = 0;
|
|
651
|
+
let truncated = false;
|
|
652
|
+
let settled = false;
|
|
653
|
+
const rejectionError = (reason, partial = false) => {
|
|
654
|
+
const body = Buffer.concat(chunks).toString("utf8")
|
|
655
|
+
+ (truncated || partial ? "…" : "");
|
|
656
|
+
const detail = reason ? `: ${errorMessage(reason)}` : "";
|
|
657
|
+
const rejection = new Error(
|
|
658
|
+
`WebSocket handshake was rejected with HTTP ${response.statusCode}${detail}`,
|
|
659
|
+
);
|
|
660
|
+
rejection.status = response.statusCode;
|
|
661
|
+
rejection.body = body || "empty response body";
|
|
662
|
+
const retryAfter = Number(header(response.headers, "retry-after"));
|
|
663
|
+
if (Number.isFinite(retryAfter) && retryAfter >= 0) rejection.retryAfter = retryAfter;
|
|
664
|
+
return rejection;
|
|
665
|
+
};
|
|
666
|
+
const complete = (error) => {
|
|
667
|
+
if (settled) return;
|
|
668
|
+
settled = true;
|
|
669
|
+
finish(rejectionError(error, Boolean(error)));
|
|
670
|
+
};
|
|
671
|
+
response.on("data", (chunk) => {
|
|
672
|
+
if (settled || truncated) return;
|
|
673
|
+
const value = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
674
|
+
const remaining = MAX_HTTP_ERROR_BYTES - bytes;
|
|
675
|
+
if (remaining <= 0) {
|
|
676
|
+
truncated = true;
|
|
677
|
+
complete();
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
const retained = value.subarray(0, remaining);
|
|
681
|
+
chunks.push(retained);
|
|
682
|
+
bytes += retained.byteLength;
|
|
683
|
+
if (retained.byteLength < value.byteLength) {
|
|
684
|
+
truncated = true;
|
|
685
|
+
complete();
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
response.once("end", () => complete());
|
|
689
|
+
response.once("aborted", () => complete(new Error("WebSocket handshake response was aborted")));
|
|
690
|
+
response.once("error", (error) => complete(error));
|
|
691
|
+
response.once("close", () => {
|
|
692
|
+
if (!response.complete) complete(new Error("WebSocket handshake response closed early"));
|
|
693
|
+
});
|
|
694
|
+
return (reason) => rejectionError(reason, true);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
function destroyWebSocket(socket, ...httpObjects) {
|
|
698
|
+
const reset = httpObjects.at(-1) === true;
|
|
699
|
+
if (reset) httpObjects = httpObjects.slice(0, -1);
|
|
700
|
+
const sockets = new Set([
|
|
701
|
+
socket?._socket,
|
|
702
|
+
socket?._req?.socket,
|
|
703
|
+
...httpObjects.map((object) => object?.socket),
|
|
704
|
+
]);
|
|
705
|
+
for (const underlying of sockets) {
|
|
706
|
+
if (!underlying || underlying.destroyed) continue;
|
|
707
|
+
try {
|
|
708
|
+
if (reset && typeof underlying.resetAndDestroy === "function") underlying.resetAndDestroy();
|
|
709
|
+
else underlying.destroy();
|
|
710
|
+
} catch {}
|
|
711
|
+
}
|
|
712
|
+
if (!reset) {
|
|
713
|
+
try {
|
|
714
|
+
if (typeof socket.terminate === "function") socket.terminate();
|
|
715
|
+
else socket.close();
|
|
716
|
+
} catch {}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function header(headers, name) {
|
|
721
|
+
const value = headers[name];
|
|
722
|
+
return Array.isArray(value) ? value[0] : value;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function messageBytes(message) {
|
|
726
|
+
return utf8ByteLength(message.kind === "text" ? message.text : JSON.stringify(message));
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function errorMessage(error) {
|
|
730
|
+
return error && (error.stack || error.message) || String(error);
|
|
731
|
+
}
|