dtc-mcp 0.2.0 → 1.0.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 +169 -402
- package/data/docs.json +4338 -0
- package/dist/docs/loader.d.ts +40 -0
- package/dist/docs/loader.js +110 -0
- package/dist/docs/loader.js.map +1 -0
- package/dist/docs/search.d.ts +47 -0
- package/dist/docs/search.js +101 -0
- package/dist/docs/search.js.map +1 -0
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/sandbox/bridge.d.ts +2 -0
- package/dist/sandbox/bridge.js +101 -0
- package/dist/sandbox/bridge.js.map +1 -0
- package/dist/sandbox/node-discovery.d.ts +7 -0
- package/dist/sandbox/node-discovery.js +228 -0
- package/dist/sandbox/node-discovery.js.map +1 -0
- package/dist/sandbox/protocol.d.ts +76 -0
- package/dist/sandbox/protocol.js +20 -0
- package/dist/sandbox/protocol.js.map +1 -0
- package/dist/sandbox/proxy-template.d.ts +19 -0
- package/dist/sandbox/proxy-template.js +83 -0
- package/dist/sandbox/proxy-template.js.map +1 -0
- package/dist/sandbox/runner.d.ts +20 -0
- package/dist/sandbox/runner.js +99 -0
- package/dist/sandbox/runner.js.map +1 -0
- package/dist/sandbox/sandbox-helpers.d.ts +14 -0
- package/dist/sandbox/sandbox-helpers.js +98 -0
- package/dist/sandbox/sandbox-helpers.js.map +1 -0
- package/dist/sandbox/sidecar/index.d.ts +16 -0
- package/dist/sandbox/sidecar/index.js +346 -0
- package/dist/sandbox/sidecar/index.js.map +1 -0
- package/dist/sandbox/sidecar-runner.d.ts +32 -0
- package/dist/sandbox/sidecar-runner.js +325 -0
- package/dist/sandbox/sidecar-runner.js.map +1 -0
- package/dist/sandbox/timeout.d.ts +16 -0
- package/dist/sandbox/timeout.js +38 -0
- package/dist/sandbox/timeout.js.map +1 -0
- package/dist/sandbox/vm-runner.d.ts +35 -0
- package/dist/sandbox/vm-runner.js +182 -0
- package/dist/sandbox/vm-runner.js.map +1 -0
- package/dist/sdk/klaviyo/host.d.ts +43 -0
- package/dist/sdk/klaviyo/host.js +218 -0
- package/dist/sdk/klaviyo/host.js.map +1 -0
- package/dist/sdk/shopify/host.d.ts +23 -0
- package/dist/sdk/shopify/host.js +175 -0
- package/dist/sdk/shopify/host.js.map +1 -0
- package/dist/server.js +7 -7
- package/dist/server.js.map +1 -1
- package/dist/shared/errors.d.ts +0 -14
- package/dist/shared/errors.js +0 -73
- package/dist/shared/errors.js.map +1 -1
- package/dist/{platforms/klaviyo/tools.d.ts → tools/execute_code.d.ts} +1 -1
- package/dist/tools/execute_code.js +70 -0
- package/dist/tools/execute_code.js.map +1 -0
- package/dist/{platforms/shopify/tools.d.ts → tools/read_doc.d.ts} +1 -1
- package/dist/tools/read_doc.js +70 -0
- package/dist/tools/read_doc.js.map +1 -0
- package/dist/tools/search_docs.d.ts +2 -0
- package/dist/tools/search_docs.js +55 -0
- package/dist/tools/search_docs.js.map +1 -0
- package/package.json +16 -5
- package/dist/cross-platform/correlator.d.ts +0 -10
- package/dist/cross-platform/correlator.js +0 -166
- package/dist/cross-platform/correlator.js.map +0 -1
- package/dist/cross-platform/tools.d.ts +0 -2
- package/dist/cross-platform/tools.js +0 -30
- package/dist/cross-platform/tools.js.map +0 -1
- package/dist/platforms/klaviyo/client.d.ts +0 -91
- package/dist/platforms/klaviyo/client.js +0 -389
- package/dist/platforms/klaviyo/client.js.map +0 -1
- package/dist/platforms/klaviyo/tools.js +0 -363
- package/dist/platforms/klaviyo/tools.js.map +0 -1
- package/dist/platforms/klaviyo/transforms.d.ts +0 -59
- package/dist/platforms/klaviyo/transforms.js +0 -326
- package/dist/platforms/klaviyo/transforms.js.map +0 -1
- package/dist/platforms/shopify/client.d.ts +0 -51
- package/dist/platforms/shopify/client.js +0 -352
- package/dist/platforms/shopify/client.js.map +0 -1
- package/dist/platforms/shopify/tools.js +0 -368
- package/dist/platforms/shopify/tools.js.map +0 -1
- package/dist/platforms/shopify/transforms.d.ts +0 -83
- package/dist/platforms/shopify/transforms.js +0 -308
- package/dist/platforms/shopify/transforms.js.map +0 -1
- package/dist/shared/pagination.d.ts +0 -21
- package/dist/shared/pagination.js +0 -36
- package/dist/shared/pagination.js.map +0 -1
- package/dist/shared/types.d.ts +0 -318
- package/dist/shared/types.js +0 -3
- package/dist/shared/types.js.map +0 -1
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { access, readdir } from "node:fs/promises";
|
|
3
|
+
import { constants as FS } from "node:fs";
|
|
4
|
+
import { homedir, platform } from "node:os";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
6
|
+
/**
|
|
7
|
+
* Discover a usable Node binary on the user's system, scanning common
|
|
8
|
+
* install locations and version managers. Returns the first one whose
|
|
9
|
+
* `--version` reports >= minMajor. Null if none found.
|
|
10
|
+
*
|
|
11
|
+
* Order of preference:
|
|
12
|
+
* 1. DTC_MCP_NODE_PATH env var (explicit override)
|
|
13
|
+
* 2. PATH (via `which` / `where`)
|
|
14
|
+
* 3. Homebrew (macOS Intel + Apple Silicon)
|
|
15
|
+
* 4. Standard system locations
|
|
16
|
+
* 5. nvm (POSIX + Windows)
|
|
17
|
+
* 6. Volta
|
|
18
|
+
* 7. fnm (POSIX + Windows)
|
|
19
|
+
* 8. asdf
|
|
20
|
+
*
|
|
21
|
+
* Why exhaustive: a developer who installed Node via nvm or fnm has it
|
|
22
|
+
* available in their shell but NOT in the GUI app's PATH (Claude Desktop
|
|
23
|
+
* launches without a login shell). Walking the common per-version-manager
|
|
24
|
+
* directories catches those.
|
|
25
|
+
*/
|
|
26
|
+
const MIN_MAJOR = 20;
|
|
27
|
+
const MAX_CANDIDATES_TO_PROBE = 12;
|
|
28
|
+
export async function discoverNode() {
|
|
29
|
+
const candidates = await collectCandidates();
|
|
30
|
+
let probed = 0;
|
|
31
|
+
for (const candidate of candidates) {
|
|
32
|
+
if (probed >= MAX_CANDIDATES_TO_PROBE)
|
|
33
|
+
break;
|
|
34
|
+
if (!candidate.path)
|
|
35
|
+
continue;
|
|
36
|
+
if (!(await exists(candidate.path)))
|
|
37
|
+
continue;
|
|
38
|
+
probed++;
|
|
39
|
+
const version = await probeNodeVersion(candidate.path);
|
|
40
|
+
if (!version)
|
|
41
|
+
continue;
|
|
42
|
+
const major = parseInt(version.replace(/^v/, "").split(".")[0], 10);
|
|
43
|
+
if (!Number.isFinite(major) || major < MIN_MAJOR)
|
|
44
|
+
continue;
|
|
45
|
+
return {
|
|
46
|
+
path: candidate.path,
|
|
47
|
+
version,
|
|
48
|
+
major,
|
|
49
|
+
source: candidate.source,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
async function collectCandidates() {
|
|
55
|
+
const out = [];
|
|
56
|
+
const isWindows = platform() === "win32";
|
|
57
|
+
const home = homedir();
|
|
58
|
+
// 1. Explicit override
|
|
59
|
+
const override = process.env.DTC_MCP_NODE_PATH;
|
|
60
|
+
if (override)
|
|
61
|
+
out.push({ path: override, source: "DTC_MCP_NODE_PATH" });
|
|
62
|
+
// 2. PATH lookup (via shell)
|
|
63
|
+
const fromPath = await whichNode();
|
|
64
|
+
if (fromPath)
|
|
65
|
+
out.push({ path: fromPath, source: "PATH" });
|
|
66
|
+
if (isWindows) {
|
|
67
|
+
// 3. Windows common locations
|
|
68
|
+
const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
|
|
69
|
+
const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
|
|
70
|
+
const localAppData = process.env.LOCALAPPDATA ?? join(home, "AppData", "Local");
|
|
71
|
+
const userProfile = process.env.USERPROFILE ?? home;
|
|
72
|
+
out.push({ path: join(programFiles, "nodejs", "node.exe"), source: "Program Files" }, { path: join(programFilesX86, "nodejs", "node.exe"), source: "Program Files (x86)" }, { path: join(userProfile, ".volta", "bin", "node.exe"), source: "volta" });
|
|
73
|
+
// nvm-windows: %LOCALAPPDATA%\nvm\v22.x.x\node.exe — scan dir
|
|
74
|
+
out.push(...(await scanDir(join(localAppData, "nvm"), "node.exe", "nvm-windows")));
|
|
75
|
+
// fnm: %LOCALAPPDATA%\fnm_multishells\<pid>_<ts>\node.exe — scan dir
|
|
76
|
+
out.push(...(await scanDir(join(localAppData, "fnm_multishells"), "node.exe", "fnm-windows")));
|
|
77
|
+
// fnm node-versions: %LOCALAPPDATA%\fnm\node-versions\v22.x.x\installation\node.exe
|
|
78
|
+
out.push(...(await scanFnmNodeVersions(join(localAppData, "fnm", "node-versions"), true)));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// 3. POSIX common locations
|
|
82
|
+
out.push({ path: "/opt/homebrew/bin/node", source: "homebrew-arm64" }, { path: "/usr/local/bin/node", source: "homebrew-x64" }, { path: "/usr/bin/node", source: "system" });
|
|
83
|
+
// 4. nvm (~/.nvm/versions/node/v22.x.x/bin/node) — pick latest
|
|
84
|
+
out.push(...(await scanNvm(join(home, ".nvm", "versions", "node"))));
|
|
85
|
+
// 5. Volta
|
|
86
|
+
out.push({ path: join(home, ".volta", "bin", "node"), source: "volta" });
|
|
87
|
+
// 6. fnm (~/.fnm/node-versions/v22.x.x/installation/bin/node)
|
|
88
|
+
out.push(...(await scanFnmNodeVersions(join(home, ".fnm", "node-versions"), false)));
|
|
89
|
+
// fnm alternate: ~/.local/share/fnm/node-versions/v22.x.x/installation/bin/node
|
|
90
|
+
out.push(...(await scanFnmNodeVersions(join(home, ".local", "share", "fnm", "node-versions"), false)));
|
|
91
|
+
// 7. asdf (~/.asdf/installs/nodejs/22.x.x/bin/node)
|
|
92
|
+
out.push(...(await scanAsdf(join(home, ".asdf", "installs", "nodejs"))));
|
|
93
|
+
}
|
|
94
|
+
return dedupe(out);
|
|
95
|
+
}
|
|
96
|
+
function dedupe(items) {
|
|
97
|
+
const seen = new Set();
|
|
98
|
+
const out = [];
|
|
99
|
+
for (const item of items) {
|
|
100
|
+
const key = resolve(item.path);
|
|
101
|
+
if (seen.has(key))
|
|
102
|
+
continue;
|
|
103
|
+
seen.add(key);
|
|
104
|
+
out.push({ ...item, path: key });
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
async function exists(p) {
|
|
109
|
+
try {
|
|
110
|
+
await access(p, FS.X_OK);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async function whichNode() {
|
|
118
|
+
const cmd = platform() === "win32" ? "where" : "which";
|
|
119
|
+
return new Promise((resolveP) => {
|
|
120
|
+
const proc = spawn(cmd, ["node"], { stdio: ["ignore", "pipe", "ignore"] });
|
|
121
|
+
let out = "";
|
|
122
|
+
proc.stdout.on("data", (d) => {
|
|
123
|
+
out += d.toString("utf8");
|
|
124
|
+
});
|
|
125
|
+
proc.on("close", () => {
|
|
126
|
+
// `where` on Windows can return multiple lines; take the first.
|
|
127
|
+
const first = out.split(/\r?\n/).map((s) => s.trim()).find(Boolean);
|
|
128
|
+
resolveP(first || null);
|
|
129
|
+
});
|
|
130
|
+
proc.on("error", () => resolveP(null));
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
async function probeNodeVersion(nodePath) {
|
|
134
|
+
return new Promise((resolveP) => {
|
|
135
|
+
const proc = spawn(nodePath, ["--version"], {
|
|
136
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
137
|
+
timeout: 3_000,
|
|
138
|
+
});
|
|
139
|
+
let out = "";
|
|
140
|
+
proc.stdout.on("data", (d) => {
|
|
141
|
+
out += d.toString("utf8");
|
|
142
|
+
});
|
|
143
|
+
proc.on("close", (code) => {
|
|
144
|
+
if (code !== 0)
|
|
145
|
+
return resolveP(null);
|
|
146
|
+
const version = out.trim();
|
|
147
|
+
resolveP(version.startsWith("v") ? version : null);
|
|
148
|
+
});
|
|
149
|
+
proc.on("error", () => resolveP(null));
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
async function scanDir(dir, binaryName, source) {
|
|
153
|
+
try {
|
|
154
|
+
const entries = await readdir(dir);
|
|
155
|
+
// Largest version-looking dir wins, so a user with multiple installed
|
|
156
|
+
// versions tries the most recent first.
|
|
157
|
+
const sorted = entries
|
|
158
|
+
.filter((e) => /v?\d/.test(e))
|
|
159
|
+
.sort((a, b) => semverDescend(a, b));
|
|
160
|
+
return sorted.slice(0, 5).map((entry) => ({
|
|
161
|
+
path: join(dir, entry, binaryName),
|
|
162
|
+
source: `${source}:${entry}`,
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async function scanNvm(nvmDir) {
|
|
170
|
+
try {
|
|
171
|
+
const entries = await readdir(nvmDir);
|
|
172
|
+
const sorted = entries
|
|
173
|
+
.filter((e) => /^v?\d/.test(e))
|
|
174
|
+
.sort((a, b) => semverDescend(a, b));
|
|
175
|
+
return sorted.slice(0, 5).map((entry) => ({
|
|
176
|
+
path: join(nvmDir, entry, "bin", "node"),
|
|
177
|
+
source: `nvm:${entry}`,
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async function scanFnmNodeVersions(versionsDir, isWindows) {
|
|
185
|
+
try {
|
|
186
|
+
const entries = await readdir(versionsDir);
|
|
187
|
+
const sorted = entries
|
|
188
|
+
.filter((e) => /^v?\d/.test(e))
|
|
189
|
+
.sort((a, b) => semverDescend(a, b));
|
|
190
|
+
const segments = isWindows
|
|
191
|
+
? ["installation", "node.exe"]
|
|
192
|
+
: ["installation", "bin", "node"];
|
|
193
|
+
return sorted.slice(0, 5).map((entry) => ({
|
|
194
|
+
path: join(versionsDir, entry, ...segments),
|
|
195
|
+
source: `fnm:${entry}`,
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function scanAsdf(asdfDir) {
|
|
203
|
+
try {
|
|
204
|
+
const entries = await readdir(asdfDir);
|
|
205
|
+
const sorted = entries
|
|
206
|
+
.filter((e) => /^\d/.test(e))
|
|
207
|
+
.sort((a, b) => semverDescend(a, b));
|
|
208
|
+
return sorted.slice(0, 5).map((entry) => ({
|
|
209
|
+
path: join(asdfDir, entry, "bin", "node"),
|
|
210
|
+
source: `asdf:${entry}`,
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return [];
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function semverDescend(a, b) {
|
|
218
|
+
const ax = a.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
|
|
219
|
+
const bx = b.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
|
|
220
|
+
for (let i = 0; i < 3; i++) {
|
|
221
|
+
const av = ax[i] ?? 0;
|
|
222
|
+
const bv = bx[i] ?? 0;
|
|
223
|
+
if (av !== bv)
|
|
224
|
+
return bv - av;
|
|
225
|
+
}
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=node-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-discovery.js","sourceRoot":"","sources":["../../src/sandbox/node-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Newline-delimited JSON-RPC protocol shared between the main MCP server
|
|
3
|
+
* and the spawned sidecar Node process. Each message is one JSON object
|
|
4
|
+
* per line on stdin/stdout.
|
|
5
|
+
*
|
|
6
|
+
* Flow:
|
|
7
|
+
* 1. Sidecar starts, tries to `require('isolated-vm')`.
|
|
8
|
+
* - On failure → sends FatalMessage and exits 1.
|
|
9
|
+
* - On success → sends ReadyMessage with isolated-vm version.
|
|
10
|
+
* 2. Main sends InitMessage with the method-path registry.
|
|
11
|
+
* 3. Main sends ExecuteRequestMessage to run user code.
|
|
12
|
+
* 4. During execution, the sandbox's bridge calls round-trip:
|
|
13
|
+
* sidecar → HostCallRequestMessage → main → HostCallResponseMessage → sidecar.
|
|
14
|
+
* 5. Sidecar sends ExecuteResponseMessage when done.
|
|
15
|
+
* 6. On shutdown, main sends ShutdownMessage (or just closes stdin).
|
|
16
|
+
*/
|
|
17
|
+
export interface ReadyMessage {
|
|
18
|
+
type: "ready";
|
|
19
|
+
/** isolated-vm package version, for diagnostics. */
|
|
20
|
+
ivmVersion: string;
|
|
21
|
+
}
|
|
22
|
+
export interface FatalMessage {
|
|
23
|
+
type: "fatal";
|
|
24
|
+
reason: string;
|
|
25
|
+
}
|
|
26
|
+
export interface LogMessage {
|
|
27
|
+
type: "log";
|
|
28
|
+
level: "debug" | "info" | "warn" | "error";
|
|
29
|
+
message: string;
|
|
30
|
+
}
|
|
31
|
+
export interface InitMessage {
|
|
32
|
+
type: "init";
|
|
33
|
+
/** Method-path registry to mirror as the in-isolate SDK surface. */
|
|
34
|
+
methodPaths: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface ExecuteRequestMessage {
|
|
37
|
+
type: "execute";
|
|
38
|
+
/** Correlation ID set by the main process. */
|
|
39
|
+
id: string;
|
|
40
|
+
code: string;
|
|
41
|
+
timeoutMs: number;
|
|
42
|
+
}
|
|
43
|
+
export interface HostCallRequestMessage {
|
|
44
|
+
type: "host-call";
|
|
45
|
+
/** ID of the in-flight execute that owns this call. */
|
|
46
|
+
execId: string;
|
|
47
|
+
/** Unique per host call within a single execute. */
|
|
48
|
+
callId: string;
|
|
49
|
+
path: string;
|
|
50
|
+
argsJson: string;
|
|
51
|
+
}
|
|
52
|
+
export interface HostCallResponseMessage {
|
|
53
|
+
type: "host-result";
|
|
54
|
+
execId: string;
|
|
55
|
+
callId: string;
|
|
56
|
+
/** JSON-encoded result, or `__ERROR__<msg>` sentinel for thrown errors. */
|
|
57
|
+
resultJson: string;
|
|
58
|
+
}
|
|
59
|
+
export interface ExecuteResponseMessage {
|
|
60
|
+
type: "execute-result";
|
|
61
|
+
id: string;
|
|
62
|
+
ok: boolean;
|
|
63
|
+
/** JSON-encoded user return value, only meaningful when ok=true. */
|
|
64
|
+
resultJson?: string;
|
|
65
|
+
stdout: string[];
|
|
66
|
+
error?: string;
|
|
67
|
+
durationMs: number;
|
|
68
|
+
/** True when the underlying isolate was recreated since the last call. */
|
|
69
|
+
sessionReset?: boolean;
|
|
70
|
+
}
|
|
71
|
+
export interface ShutdownMessage {
|
|
72
|
+
type: "shutdown";
|
|
73
|
+
}
|
|
74
|
+
export type MainToSidecar = InitMessage | ExecuteRequestMessage | HostCallResponseMessage | ShutdownMessage;
|
|
75
|
+
export type SidecarToMain = ReadyMessage | FatalMessage | LogMessage | ExecuteResponseMessage | HostCallRequestMessage;
|
|
76
|
+
export declare function encodeLine(msg: MainToSidecar | SidecarToMain): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Newline-delimited JSON-RPC protocol shared between the main MCP server
|
|
3
|
+
* and the spawned sidecar Node process. Each message is one JSON object
|
|
4
|
+
* per line on stdin/stdout.
|
|
5
|
+
*
|
|
6
|
+
* Flow:
|
|
7
|
+
* 1. Sidecar starts, tries to `require('isolated-vm')`.
|
|
8
|
+
* - On failure → sends FatalMessage and exits 1.
|
|
9
|
+
* - On success → sends ReadyMessage with isolated-vm version.
|
|
10
|
+
* 2. Main sends InitMessage with the method-path registry.
|
|
11
|
+
* 3. Main sends ExecuteRequestMessage to run user code.
|
|
12
|
+
* 4. During execution, the sandbox's bridge calls round-trip:
|
|
13
|
+
* sidecar → HostCallRequestMessage → main → HostCallResponseMessage → sidecar.
|
|
14
|
+
* 5. Sidecar sends ExecuteResponseMessage when done.
|
|
15
|
+
* 6. On shutdown, main sends ShutdownMessage (or just closes stdin).
|
|
16
|
+
*/
|
|
17
|
+
export function encodeLine(msg) {
|
|
18
|
+
return JSON.stringify(msg) + "\n";
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/sandbox/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAiFH,MAAM,UAAU,UAAU,CAAC,GAAkC;IAC3D,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the JavaScript source that runs INSIDE the `node:vm` context to
|
|
3
|
+
* reconstruct the `klaviyo` and `shopify` namespace trees as thin async stubs.
|
|
4
|
+
*
|
|
5
|
+
* Each leaf method serializes its args to JSON, calls the host bridge function
|
|
6
|
+
* (`__host_invoke`) injected into the context, and awaits the host's promise.
|
|
7
|
+
* The host re-runs the request with real rate limiting, auth, and caching.
|
|
8
|
+
*
|
|
9
|
+
* JSON-encoded args are used (rather than passing the raw array) because:
|
|
10
|
+
* - `node:vm` shares the V8 heap with the host, so objects could leak the
|
|
11
|
+
* prototype chain across the boundary if passed directly. JSON normalizes
|
|
12
|
+
* everything to plain data.
|
|
13
|
+
* - Behavior matches what we'd do across a worker boundary if we later
|
|
14
|
+
* swap the runtime for a stricter sandbox.
|
|
15
|
+
*
|
|
16
|
+
* The bootstrap also installs the output-discipline helpers (`pick`,
|
|
17
|
+
* `summarize`, `topN`) — see ./sandbox-helpers.ts.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildProxyScript(methodPaths: string[]): string;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { SANDBOX_HELPERS_SOURCE } from "./sandbox-helpers.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build the JavaScript source that runs INSIDE the `node:vm` context to
|
|
4
|
+
* reconstruct the `klaviyo` and `shopify` namespace trees as thin async stubs.
|
|
5
|
+
*
|
|
6
|
+
* Each leaf method serializes its args to JSON, calls the host bridge function
|
|
7
|
+
* (`__host_invoke`) injected into the context, and awaits the host's promise.
|
|
8
|
+
* The host re-runs the request with real rate limiting, auth, and caching.
|
|
9
|
+
*
|
|
10
|
+
* JSON-encoded args are used (rather than passing the raw array) because:
|
|
11
|
+
* - `node:vm` shares the V8 heap with the host, so objects could leak the
|
|
12
|
+
* prototype chain across the boundary if passed directly. JSON normalizes
|
|
13
|
+
* everything to plain data.
|
|
14
|
+
* - Behavior matches what we'd do across a worker boundary if we later
|
|
15
|
+
* swap the runtime for a stricter sandbox.
|
|
16
|
+
*
|
|
17
|
+
* The bootstrap also installs the output-discipline helpers (`pick`,
|
|
18
|
+
* `summarize`, `topN`) — see ./sandbox-helpers.ts.
|
|
19
|
+
*/
|
|
20
|
+
export function buildProxyScript(methodPaths) {
|
|
21
|
+
const tree = {};
|
|
22
|
+
for (const path of methodPaths) {
|
|
23
|
+
const segments = path.split(".");
|
|
24
|
+
let cursor = tree;
|
|
25
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
26
|
+
const key = segments[i];
|
|
27
|
+
if (typeof cursor[key] !== "object")
|
|
28
|
+
cursor[key] = {};
|
|
29
|
+
cursor = cursor[key];
|
|
30
|
+
}
|
|
31
|
+
cursor[segments[segments.length - 1]] = path;
|
|
32
|
+
}
|
|
33
|
+
function emit(node) {
|
|
34
|
+
const parts = [];
|
|
35
|
+
for (const [key, value] of Object.entries(node)) {
|
|
36
|
+
if (typeof value === "string") {
|
|
37
|
+
parts.push(`${JSON.stringify(key)}: (...args) => __invoke(${JSON.stringify(value)}, args)`);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
parts.push(`${JSON.stringify(key)}: ${emit(value)}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return `{${parts.join(",")}}`;
|
|
44
|
+
}
|
|
45
|
+
return `
|
|
46
|
+
(function () {
|
|
47
|
+
const __invoke = async function (path, args) {
|
|
48
|
+
const argsJson = JSON.stringify(args);
|
|
49
|
+
const resultJson = await __host_invoke(path, argsJson);
|
|
50
|
+
if (typeof resultJson === 'string' && resultJson.startsWith('__ERROR__')) {
|
|
51
|
+
throw new Error(resultJson.slice(9));
|
|
52
|
+
}
|
|
53
|
+
return resultJson ? JSON.parse(resultJson) : undefined;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const __stdout = [];
|
|
57
|
+
globalThis.console = {
|
|
58
|
+
log: (...args) => {
|
|
59
|
+
__stdout.push(args.map((a) => typeof a === 'string' ? a : JSON.stringify(a)).join(' '));
|
|
60
|
+
},
|
|
61
|
+
error: (...args) => {
|
|
62
|
+
__stdout.push('[err] ' + args.map((a) => typeof a === 'string' ? a : JSON.stringify(a)).join(' '));
|
|
63
|
+
},
|
|
64
|
+
warn: (...args) => {
|
|
65
|
+
__stdout.push('[warn] ' + args.map((a) => typeof a === 'string' ? a : JSON.stringify(a)).join(' '));
|
|
66
|
+
},
|
|
67
|
+
info: (...args) => {
|
|
68
|
+
__stdout.push(args.map((a) => typeof a === 'string' ? a : JSON.stringify(a)).join(' '));
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
globalThis.__getStdout = () => __stdout;
|
|
72
|
+
globalThis.__resetStdout = () => { __stdout.length = 0; };
|
|
73
|
+
|
|
74
|
+
const __sdk = ${emit(tree)};
|
|
75
|
+
for (const k of Object.keys(__sdk)) {
|
|
76
|
+
globalThis[k] = __sdk[k];
|
|
77
|
+
}
|
|
78
|
+
})();
|
|
79
|
+
|
|
80
|
+
${SANDBOX_HELPERS_SOURCE}
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=proxy-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-template.js","sourceRoot":"","sources":["../../src/sandbox/proxy-template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE9D;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAqB;IAEpD,MAAM,IAAI,GAAS,EAAE,CAAC;IACtB,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,GAAG,IAAI,CAAC;QAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7C,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,QAAQ;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;YACtD,MAAM,GAAG,MAAM,CAAC,GAAG,CAAS,CAAC;QAC/B,CAAC;QACD,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC/C,CAAC;IAED,SAAS,IAAI,CAAC,IAAU;QACtB,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,KAAK,CAAC,IAAI,CACR,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,2BAA2B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAChF,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;QACD,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAChC,CAAC;IAED,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA6BS,IAAI,CAAC,IAAI,CAAC;;;;;;EAM1B,sBAAsB;CACvB,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type RunResult } from "./vm-runner.js";
|
|
2
|
+
/**
|
|
3
|
+
* Public sandbox runner. Routes between the sidecar (isolated-vm) and the
|
|
4
|
+
* in-process vm-runner (node:vm) based on availability.
|
|
5
|
+
*
|
|
6
|
+
* Selection logic:
|
|
7
|
+
* - DTC_MCP_SANDBOX=vm → always use node:vm
|
|
8
|
+
* - DTC_MCP_SANDBOX=sidecar → require sidecar; error if unavailable
|
|
9
|
+
* - DTC_MCP_SANDBOX=auto → default: prefer sidecar, fall back to vm
|
|
10
|
+
*
|
|
11
|
+
* The active mode is logged once at first use so users can see in the debug
|
|
12
|
+
* log which sandbox is actually executing their code.
|
|
13
|
+
*/
|
|
14
|
+
export type { RunResult } from "./vm-runner.js";
|
|
15
|
+
export type SandboxMode = "sidecar" | "vm";
|
|
16
|
+
export declare function runSandbox(code: string, options: {
|
|
17
|
+
timeoutMs: number;
|
|
18
|
+
}): Promise<RunResult & {
|
|
19
|
+
sandbox: SandboxMode;
|
|
20
|
+
}>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { runSandboxVm } from "./vm-runner.js";
|
|
2
|
+
import { getSidecarStatus, runSandboxSidecar } from "./sidecar-runner.js";
|
|
3
|
+
import { log } from "../config.js";
|
|
4
|
+
let activeMode = null;
|
|
5
|
+
async function pickMode() {
|
|
6
|
+
const override = (process.env.DTC_MCP_SANDBOX ?? "auto").toLowerCase();
|
|
7
|
+
if (override === "vm")
|
|
8
|
+
return "vm";
|
|
9
|
+
const status = await getSidecarStatus();
|
|
10
|
+
if (override === "sidecar") {
|
|
11
|
+
if (!status.available) {
|
|
12
|
+
// Caller asked for sidecar specifically; throwing here would break
|
|
13
|
+
// execute_code. We log the failure and fall back so the tool stays
|
|
14
|
+
// usable — but the log makes it obvious.
|
|
15
|
+
log("error", "DTC_MCP_SANDBOX=sidecar but sidecar unavailable", {
|
|
16
|
+
reason: status.reason,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return status.available ? "sidecar" : "vm";
|
|
20
|
+
}
|
|
21
|
+
// auto
|
|
22
|
+
return status.available ? "sidecar" : "vm";
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Cap user code's return payload so an over-eager LLM that returns a raw
|
|
26
|
+
* 380 KB JSON blob doesn't burn through the conversation's context window.
|
|
27
|
+
* Stainless's own benchmark caps factuality at 53% across all code-mode
|
|
28
|
+
* MCPs because "models tend toward verbose responses beyond what's strictly
|
|
29
|
+
* necessary." This is the host-side enforcement; the in-sandbox `pick` /
|
|
30
|
+
* `summarize` / `topN` helpers are the LLM-friendly remediation.
|
|
31
|
+
*/
|
|
32
|
+
const MAX_RESPONSE_BYTES = (() => {
|
|
33
|
+
const raw = process.env.DTC_MCP_MAX_RESPONSE_KB;
|
|
34
|
+
if (raw) {
|
|
35
|
+
const kb = parseInt(raw, 10);
|
|
36
|
+
if (Number.isFinite(kb) && kb > 0)
|
|
37
|
+
return kb * 1024;
|
|
38
|
+
}
|
|
39
|
+
return 100 * 1024;
|
|
40
|
+
})();
|
|
41
|
+
function applyResponseCap(result) {
|
|
42
|
+
if (!result.ok || result.result === undefined || result.result === null) {
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
let serialized;
|
|
46
|
+
try {
|
|
47
|
+
serialized = JSON.stringify(result.result);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// If the value isn't JSON-serializable, leave it for the upstream
|
|
51
|
+
// JSON.stringify (in execute_code.ts) to surface the error.
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
const bytes = Buffer.byteLength(serialized, "utf8");
|
|
55
|
+
if (bytes <= MAX_RESPONSE_BYTES)
|
|
56
|
+
return result;
|
|
57
|
+
// Capture a leading slice as a preview so the LLM can still see the shape.
|
|
58
|
+
// Truncate at ~90% of the cap to leave room for the truncation envelope.
|
|
59
|
+
const previewLimit = Math.floor(MAX_RESPONSE_BYTES * 0.9);
|
|
60
|
+
const preview = serialized.slice(0, previewLimit);
|
|
61
|
+
log("warn", "Response capped", {
|
|
62
|
+
originalBytes: bytes,
|
|
63
|
+
cap: MAX_RESPONSE_BYTES,
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
...result,
|
|
67
|
+
result: {
|
|
68
|
+
truncated: true,
|
|
69
|
+
originalBytes: bytes,
|
|
70
|
+
cap: MAX_RESPONSE_BYTES,
|
|
71
|
+
preview,
|
|
72
|
+
instructions: "Output exceeded the response cap. Use `pick(value, schema)` to project specific fields, `topN(arr, n, key)` for top items, or `summarize(arr, { by, topN })` for aggregate stats. See search_docs('output discipline') for examples.",
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export async function runSandbox(code, options) {
|
|
77
|
+
if (!activeMode) {
|
|
78
|
+
activeMode = await pickMode();
|
|
79
|
+
log("info", "Sandbox mode selected", { mode: activeMode });
|
|
80
|
+
}
|
|
81
|
+
const result = activeMode === "sidecar"
|
|
82
|
+
? await runSandboxSidecar(code, options)
|
|
83
|
+
: await runSandboxVm(code, options);
|
|
84
|
+
// If the sidecar died mid-session (e.g., child crashed), gracefully drop
|
|
85
|
+
// to vm-runner for THIS call and every future call.
|
|
86
|
+
if (activeMode === "sidecar" &&
|
|
87
|
+
!result.ok &&
|
|
88
|
+
typeof result.error === "string" &&
|
|
89
|
+
result.error.startsWith("Sidecar unavailable")) {
|
|
90
|
+
log("warn", "Sidecar dropped; falling back to vm-runner", {
|
|
91
|
+
error: result.error,
|
|
92
|
+
});
|
|
93
|
+
activeMode = "vm";
|
|
94
|
+
const fallback = await runSandboxVm(code, options);
|
|
95
|
+
return { ...applyResponseCap(fallback), sandbox: "vm" };
|
|
96
|
+
}
|
|
97
|
+
return { ...applyResponseCap(result), sandbox: activeMode };
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/sandbox/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JS source for the output-discipline helpers that get injected into the
|
|
3
|
+
* sandbox global scope. Same code runs in both runners (node:vm and
|
|
4
|
+
* isolated-vm) — keeping this as a single string source-of-truth means the
|
|
5
|
+
* behavior the LLM observes is identical regardless of which runner is
|
|
6
|
+
* actually executing.
|
|
7
|
+
*
|
|
8
|
+
* Why these helpers exist: Stainless's published benchmark caps factuality
|
|
9
|
+
* at 53% across all code-mode MCPs, with the model "tending toward verbose
|
|
10
|
+
* responses beyond what's strictly necessary." These helpers give the LLM
|
|
11
|
+
* a vocabulary to project / aggregate / trim raw API responses before
|
|
12
|
+
* returning, and the docs explicitly point at them.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SANDBOX_HELPERS_SOURCE: string;
|