@mhingston5/conduit 1.1.1 → 1.1.3
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 +29 -5
- package/dist/assets/deno-shim.ts +93 -0
- package/dist/assets/python-shim.py +21 -0
- package/dist/executors/pyodide.worker.d.ts +2 -0
- package/dist/executors/pyodide.worker.js +163 -0
- package/dist/executors/pyodide.worker.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3250 -0
- package/dist/index.js.map +1 -0
- package/docs/ARCHITECTURE.md +2 -2
- package/docs/CODE_MODE.md +1 -1
- package/docs/SECURITY.md +1 -1
- package/package.json +6 -1
- package/src/assets/deno-shim.ts +2 -2
- package/src/auth.cmd.ts +95 -0
- package/src/core/config.service.ts +5 -2
- package/src/core/middleware/auth.middleware.ts +1 -1
- package/src/core/request.controller.ts +25 -9
- package/src/core/security.service.ts +3 -3
- package/src/executors/pyodide.worker.ts +2 -2
- package/src/gateway/auth.service.ts +19 -18
- package/src/gateway/gateway.service.ts +3 -3
- package/src/index.ts +52 -8
- package/src/transport/socket.transport.ts +8 -3
- package/tests/auth.service.test.ts +8 -13
- package/tests/config.service.test.ts +31 -0
- package/tests/contract.test.ts +2 -2
- package/tests/dynamic.tool.test.ts +5 -5
- package/tests/e2e_stdio_upstream.test.ts +3 -3
- package/tests/gateway.service.test.ts +5 -5
- package/tests/hardening.test.ts +3 -3
- package/tests/routing.test.ts +24 -4
- package/tests/socket.transport.test.ts +3 -3
- package/tests/vscode_e2e.test.ts +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ Conduit is a **secure Code Mode execution substrate** for [MCP](https://modelcon
|
|
|
21
21
|
|
|
22
22
|
It lets agents:
|
|
23
23
|
- generate **real TypeScript or Python code**
|
|
24
|
-
- call tools via **language-native APIs** (`tools.github.
|
|
24
|
+
- call tools via **language-native APIs** (`tools.github.create_issue()`)
|
|
25
25
|
- run that code in **isolated, resource-governed sandboxes**
|
|
26
26
|
- without exposing credentials or the host environment
|
|
27
27
|
|
|
@@ -66,6 +66,15 @@ upstreams:
|
|
|
66
66
|
- id: github
|
|
67
67
|
type: http
|
|
68
68
|
url: "http://localhost:3000/mcp"
|
|
69
|
+
- id: slack
|
|
70
|
+
type: http
|
|
71
|
+
url: "https://your-mcp-server/mcp"
|
|
72
|
+
credentials:
|
|
73
|
+
type: oauth2
|
|
74
|
+
clientId: ${SLACK_CLIENT_ID}
|
|
75
|
+
clientSecret: ${SLACK_CLIENT_SECRET}
|
|
76
|
+
tokenUrl: "https://slack.com/api/oauth.v2.access"
|
|
77
|
+
refreshToken: ${SLACK_REFRESH_TOKEN}
|
|
69
78
|
# Or use local stdio for testing:
|
|
70
79
|
- id: filesystem
|
|
71
80
|
type: stdio
|
|
@@ -73,9 +82,24 @@ upstreams:
|
|
|
73
82
|
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
|
|
74
83
|
```
|
|
75
84
|
|
|
76
|
-
### 3.
|
|
85
|
+
### 3. OAuth Setup Helper
|
|
86
|
+
|
|
87
|
+
If your upstream requires an initial OAuth flow (like Jira), you can use the built-in helper to obtain a refresh token:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npx conduit auth \
|
|
91
|
+
--client-id <id> \
|
|
92
|
+
--client-secret <secret> \
|
|
93
|
+
--auth-url <url> \
|
|
94
|
+
--token-url <url> \
|
|
95
|
+
--scopes <scopes>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This will start a temporary local server, open your browser for authorization, and print the generated `credentials` block for your `conduit.yaml`.
|
|
99
|
+
|
|
100
|
+
### 4. Execute TypeScript
|
|
77
101
|
|
|
78
|
-
Using any [MCP Client](https://modelcontextprotocol.io/clients) (Claude Desktop, etc.), call `
|
|
102
|
+
Using any [MCP Client](https://modelcontextprotocol.io/clients) (Claude Desktop, etc.), call `mcp_execute_typescript`:
|
|
79
103
|
|
|
80
104
|
```ts
|
|
81
105
|
// The agent writes this code:
|
|
@@ -83,7 +107,7 @@ const result = await tools.filesystem.list_allowed_directories();
|
|
|
83
107
|
console.log("Files:", result);
|
|
84
108
|
```
|
|
85
109
|
|
|
86
|
-
###
|
|
110
|
+
### 5. Result
|
|
87
111
|
|
|
88
112
|
Conduit runs the code, handles the tool call securely, and returns:
|
|
89
113
|
|
|
@@ -182,7 +206,7 @@ To get the most out of Conduit, you should guide your LLM (agent) to prefer mult
|
|
|
182
206
|
**Recommended System Prompt Additions:**
|
|
183
207
|
> You are equipped with a "Code Mode" execution environment via Conduit.
|
|
184
208
|
>
|
|
185
|
-
> - **Prefer `
|
|
209
|
+
> - **Prefer `mcp_execute_typescript`**: Use this for any task requiring multiple steps, data transformation, or logical branching.
|
|
186
210
|
> - **Use `tools.*` SDK**: Within your scripts, access all upstream tools via the `tools` namespace (e.g., `await tools.github.create_issue(...)`).
|
|
187
211
|
> - **Avoid JSON tool-calling overhead**: Instead of making 5 separate tool calls and waiting for 5 round-trips, write one script that orchestrates the entire operation.
|
|
188
212
|
> - **Data Transformation**: Perform loops, filters, and aggregations directly in your code rather than asking the user (or yourself) to process large datasets in the chat context.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Deno Shim for Conduit - Code Mode SDK
|
|
3
|
+
const IPC_ADDRESS = '__CONDUIT_IPC_ADDRESS__';
|
|
4
|
+
const IPC_TOKEN = '__CONDUIT_IPC_TOKEN__';
|
|
5
|
+
|
|
6
|
+
async function sendIPCRequest(method: string, params: any) {
|
|
7
|
+
if (!IPC_ADDRESS) throw new Error('Conduit IPC address not configured');
|
|
8
|
+
|
|
9
|
+
let conn: any;
|
|
10
|
+
try {
|
|
11
|
+
if (IPC_ADDRESS.includes(':')) {
|
|
12
|
+
const lastColon = IPC_ADDRESS.lastIndexOf(':');
|
|
13
|
+
const hostname = IPC_ADDRESS.substring(0, lastColon);
|
|
14
|
+
const port = IPC_ADDRESS.substring(lastColon + 1);
|
|
15
|
+
|
|
16
|
+
// Normalize hostname for Deno connect
|
|
17
|
+
let targetHost = hostname.replace(/[\[\]]/g, '');
|
|
18
|
+
if (targetHost === '0.0.0.0' || targetHost === '::' || targetHost === '::1' || targetHost === '') {
|
|
19
|
+
targetHost = '127.0.0.1';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
conn = await (Deno as any).connect({
|
|
23
|
+
hostname: targetHost,
|
|
24
|
+
port: Number(port)
|
|
25
|
+
});
|
|
26
|
+
} else {
|
|
27
|
+
conn = await (Deno as any).connect({ transport: 'unix', path: IPC_ADDRESS });
|
|
28
|
+
}
|
|
29
|
+
} catch (err: any) {
|
|
30
|
+
throw new Error(`Failed to connect to Conduit IPC (${IPC_ADDRESS}): ${err.message}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const id = Math.random().toString(36).substring(7);
|
|
35
|
+
const request = {
|
|
36
|
+
jsonrpc: '2.0',
|
|
37
|
+
id,
|
|
38
|
+
method,
|
|
39
|
+
params: params || {},
|
|
40
|
+
auth: { bearerToken: IPC_TOKEN }
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const encoder = new TextEncoder();
|
|
44
|
+
await conn.write(encoder.encode(JSON.stringify(request) + '\n'));
|
|
45
|
+
|
|
46
|
+
const decoder = new TextDecoder();
|
|
47
|
+
let buffer = '';
|
|
48
|
+
const chunk = new Uint8Array(2 * 1024 * 1024); // 2MB buffer for large tool returns
|
|
49
|
+
|
|
50
|
+
while (true) {
|
|
51
|
+
const n = await conn.read(chunk);
|
|
52
|
+
if (n === null) throw new Error('IPC connection closed by host before receiving response');
|
|
53
|
+
|
|
54
|
+
buffer += decoder.decode(chunk.subarray(0, n));
|
|
55
|
+
const lines = buffer.split('\n');
|
|
56
|
+
buffer = lines.pop() || ''; // Keep partial line
|
|
57
|
+
|
|
58
|
+
for (const line of lines) {
|
|
59
|
+
if (!line.trim()) continue;
|
|
60
|
+
try {
|
|
61
|
+
const response = JSON.parse(line);
|
|
62
|
+
if (response.id === id) {
|
|
63
|
+
if (response.error) {
|
|
64
|
+
const error = new Error(response.error.message);
|
|
65
|
+
(error as any).code = response.error.code;
|
|
66
|
+
(error as any).data = response.error.data;
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
return response.result;
|
|
70
|
+
}
|
|
71
|
+
} catch (e: any) {
|
|
72
|
+
if (e.message.includes('JSON')) continue;
|
|
73
|
+
throw e;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} finally {
|
|
78
|
+
conn.close();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Internal tool call function - used by generated SDK
|
|
83
|
+
const __internalCallTool = async (name: string, params: any) => {
|
|
84
|
+
return await sendIPCRequest('mcp_call_tool', { name, arguments: params });
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Tool discovery - still available for dynamic scenarios
|
|
88
|
+
(globalThis as any).discoverMCPTools = async (options: any) => {
|
|
89
|
+
const result = await sendIPCRequest('mcp_discover_tools', options);
|
|
90
|
+
return result.tools || [];
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// __CONDUIT_SDK_INJECTION__
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Python Shim for Conduit - Code Mode SDK
|
|
2
|
+
import asyncio
|
|
3
|
+
|
|
4
|
+
async def discover_mcp_tools(options=None):
|
|
5
|
+
"""Discover available MCP tools from the gateway."""
|
|
6
|
+
# These functions are injected into the Python global scope by the executor
|
|
7
|
+
res = await discover_mcp_tools_js(options)
|
|
8
|
+
# Pyodide's JS proxy handles conversion broadly, but we might need to convert the tools list
|
|
9
|
+
if hasattr(res, 'to_py'):
|
|
10
|
+
data = res.to_py()
|
|
11
|
+
return data.get('tools', []) if isinstance(data, dict) else []
|
|
12
|
+
return []
|
|
13
|
+
|
|
14
|
+
async def _internal_call_tool(name, arguments):
|
|
15
|
+
"""Internal tool call function - used by generated SDK."""
|
|
16
|
+
res = await call_mcp_tool_js(name, arguments)
|
|
17
|
+
if hasattr(res, 'to_py'):
|
|
18
|
+
return res.to_py()
|
|
19
|
+
return res
|
|
20
|
+
|
|
21
|
+
# __CONDUIT_SDK_INJECTION__
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// src/executors/pyodide.worker.ts
|
|
2
|
+
import { parentPort } from "worker_threads";
|
|
3
|
+
import { loadPyodide } from "pyodide";
|
|
4
|
+
import net from "net";
|
|
5
|
+
var pyodide = null;
|
|
6
|
+
var currentStdout = "";
|
|
7
|
+
var currentStderr = "";
|
|
8
|
+
var totalOutputBytes = 0;
|
|
9
|
+
var totalLogEntries = 0;
|
|
10
|
+
var currentLimits = null;
|
|
11
|
+
async function init() {
|
|
12
|
+
if (pyodide) return pyodide;
|
|
13
|
+
pyodide = await loadPyodide({
|
|
14
|
+
stdout: (text) => {
|
|
15
|
+
if (currentLimits && (totalOutputBytes > (currentLimits.maxOutputBytes || 1024 * 1024) || totalLogEntries > (currentLimits.maxLogEntries || 1e4))) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
currentStdout += text + "\n";
|
|
19
|
+
totalOutputBytes += text.length + 1;
|
|
20
|
+
totalLogEntries++;
|
|
21
|
+
},
|
|
22
|
+
stderr: (text) => {
|
|
23
|
+
if (currentLimits && (totalOutputBytes > (currentLimits.maxOutputBytes || 1024 * 1024) || totalLogEntries > (currentLimits.maxLogEntries || 1e4))) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
currentStderr += text + "\n";
|
|
27
|
+
totalOutputBytes += text.length + 1;
|
|
28
|
+
totalLogEntries++;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return pyodide;
|
|
32
|
+
}
|
|
33
|
+
async function handleTask(data) {
|
|
34
|
+
const { code, limits, ipcInfo, shim } = data;
|
|
35
|
+
currentStdout = "";
|
|
36
|
+
currentStderr = "";
|
|
37
|
+
totalOutputBytes = 0;
|
|
38
|
+
totalLogEntries = 0;
|
|
39
|
+
currentLimits = limits;
|
|
40
|
+
try {
|
|
41
|
+
const p = await init();
|
|
42
|
+
const sendIPCRequest = async (method, params) => {
|
|
43
|
+
if (!ipcInfo?.ipcAddress) throw new Error("Conduit IPC address not configured");
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
let client;
|
|
46
|
+
if (ipcInfo.ipcAddress.includes(":")) {
|
|
47
|
+
const lastColon = ipcInfo.ipcAddress.lastIndexOf(":");
|
|
48
|
+
const host = ipcInfo.ipcAddress.substring(0, lastColon);
|
|
49
|
+
const port = ipcInfo.ipcAddress.substring(lastColon + 1);
|
|
50
|
+
let targetHost = host.replace(/[\[\]]/g, "");
|
|
51
|
+
if (targetHost === "0.0.0.0" || targetHost === "::" || targetHost === "::1" || targetHost === "") {
|
|
52
|
+
targetHost = "127.0.0.1";
|
|
53
|
+
}
|
|
54
|
+
client = net.createConnection({
|
|
55
|
+
host: targetHost,
|
|
56
|
+
port: parseInt(port)
|
|
57
|
+
});
|
|
58
|
+
} else {
|
|
59
|
+
client = net.createConnection({ path: ipcInfo.ipcAddress });
|
|
60
|
+
}
|
|
61
|
+
const id = Math.random().toString(36).substring(7);
|
|
62
|
+
const request = {
|
|
63
|
+
jsonrpc: "2.0",
|
|
64
|
+
id,
|
|
65
|
+
method,
|
|
66
|
+
params: params || {},
|
|
67
|
+
auth: { bearerToken: ipcInfo.ipcToken }
|
|
68
|
+
};
|
|
69
|
+
client.on("error", (err) => {
|
|
70
|
+
reject(err);
|
|
71
|
+
client.destroy();
|
|
72
|
+
});
|
|
73
|
+
client.write(JSON.stringify(request) + "\n");
|
|
74
|
+
let buffer = "";
|
|
75
|
+
client.on("data", (data2) => {
|
|
76
|
+
buffer += data2.toString();
|
|
77
|
+
const lines = buffer.split("\n");
|
|
78
|
+
buffer = lines.pop() || "";
|
|
79
|
+
for (const line of lines) {
|
|
80
|
+
if (!line.trim()) continue;
|
|
81
|
+
try {
|
|
82
|
+
const response = JSON.parse(line);
|
|
83
|
+
if (response.id === id) {
|
|
84
|
+
if (response.error) {
|
|
85
|
+
reject(new Error(response.error.message));
|
|
86
|
+
} else {
|
|
87
|
+
resolve(response.result);
|
|
88
|
+
}
|
|
89
|
+
client.end();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
} catch (e) {
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
client.on("end", () => {
|
|
97
|
+
if (buffer.trim()) {
|
|
98
|
+
try {
|
|
99
|
+
const response = JSON.parse(buffer);
|
|
100
|
+
if (response.id === id) {
|
|
101
|
+
if (response.error) {
|
|
102
|
+
reject(new Error(response.error.message));
|
|
103
|
+
} else {
|
|
104
|
+
resolve(response.result);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch (e) {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
p.globals.set("discover_mcp_tools_js", (options) => {
|
|
114
|
+
return sendIPCRequest("mcp_discover_tools", options);
|
|
115
|
+
});
|
|
116
|
+
p.globals.set("call_mcp_tool_js", (name, args) => {
|
|
117
|
+
return sendIPCRequest("mcp_call_tool", { name, arguments: args });
|
|
118
|
+
});
|
|
119
|
+
if (shim) {
|
|
120
|
+
await p.runPythonAsync(shim);
|
|
121
|
+
}
|
|
122
|
+
const result = await p.runPythonAsync(code);
|
|
123
|
+
if (totalOutputBytes > (limits.maxOutputBytes || 1024 * 1024)) {
|
|
124
|
+
throw new Error("[LIMIT_OUTPUT]");
|
|
125
|
+
}
|
|
126
|
+
if (totalLogEntries > (limits.maxLogEntries || 1e4)) {
|
|
127
|
+
throw new Error("[LIMIT_LOG]");
|
|
128
|
+
}
|
|
129
|
+
parentPort?.postMessage({
|
|
130
|
+
stdout: currentStdout,
|
|
131
|
+
stderr: currentStderr,
|
|
132
|
+
result: String(result),
|
|
133
|
+
success: true
|
|
134
|
+
});
|
|
135
|
+
} catch (err) {
|
|
136
|
+
let isOutput = err.message.includes("[LIMIT_OUTPUT]");
|
|
137
|
+
let isLog = err.message.includes("[LIMIT_LOG]");
|
|
138
|
+
if (!isOutput && !isLog && currentLimits) {
|
|
139
|
+
if (totalOutputBytes > (currentLimits.maxOutputBytes || 1024 * 1024)) {
|
|
140
|
+
isOutput = true;
|
|
141
|
+
}
|
|
142
|
+
if (totalLogEntries > (currentLimits.maxLogEntries || 1e4)) {
|
|
143
|
+
isLog = true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
parentPort?.postMessage({
|
|
147
|
+
stdout: currentStdout,
|
|
148
|
+
stderr: currentStderr,
|
|
149
|
+
error: err.message,
|
|
150
|
+
limitBreached: isOutput ? "output" : isLog ? "log" : void 0,
|
|
151
|
+
success: false
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
parentPort?.on("message", async (msg) => {
|
|
156
|
+
if (msg.type === "execute") {
|
|
157
|
+
await handleTask(msg.data);
|
|
158
|
+
} else if (msg.type === "ping") {
|
|
159
|
+
parentPort?.postMessage({ type: "pong" });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
parentPort?.postMessage({ type: "ready" });
|
|
163
|
+
//# sourceMappingURL=pyodide.worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/executors/pyodide.worker.ts"],"sourcesContent":["import { parentPort, workerData } from 'node:worker_threads';\nimport { loadPyodide, type PyodideInterface } from 'pyodide';\nimport net from 'node:net';\n\nlet pyodide: PyodideInterface | null = null;\nlet currentStdout = '';\nlet currentStderr = '';\nlet totalOutputBytes = 0;\nlet totalLogEntries = 0;\nlet currentLimits: any = null;\n\nasync function init() {\n if (pyodide) return pyodide;\n\n pyodide = await loadPyodide({\n stdout: (text) => {\n if (currentLimits && (totalOutputBytes > (currentLimits.maxOutputBytes || 1024 * 1024) || totalLogEntries > (currentLimits.maxLogEntries || 10000))) {\n return; // Stop processing logs once limit breached\n }\n currentStdout += text + '\\n';\n totalOutputBytes += text.length + 1;\n totalLogEntries++;\n },\n stderr: (text) => {\n if (currentLimits && (totalOutputBytes > (currentLimits.maxOutputBytes || 1024 * 1024) || totalLogEntries > (currentLimits.maxLogEntries || 10000))) {\n return; // Stop processing logs once limit breached\n }\n currentStderr += text + '\\n';\n totalOutputBytes += text.length + 1;\n totalLogEntries++;\n },\n });\n\n return pyodide;\n}\n\nasync function handleTask(data: any) {\n const { code, limits, ipcInfo, shim } = data;\n currentStdout = '';\n currentStderr = '';\n totalOutputBytes = 0;\n totalLogEntries = 0;\n currentLimits = limits;\n\n try {\n const p = await init();\n\n const sendIPCRequest = async (method: string, params: any) => {\n if (!ipcInfo?.ipcAddress) throw new Error('Conduit IPC address not configured');\n\n return new Promise((resolve, reject) => {\n let client: net.Socket;\n\n if (ipcInfo.ipcAddress.includes(':')) {\n const lastColon = ipcInfo.ipcAddress.lastIndexOf(':');\n const host = ipcInfo.ipcAddress.substring(0, lastColon);\n const port = ipcInfo.ipcAddress.substring(lastColon + 1);\n\n let targetHost = host.replace(/[\\[\\]]/g, '');\n if (targetHost === '0.0.0.0' || targetHost === '::' || targetHost === '::1' || targetHost === '') {\n targetHost = '127.0.0.1';\n }\n\n client = net.createConnection({\n host: targetHost,\n port: parseInt(port)\n });\n } else {\n client = net.createConnection({ path: ipcInfo.ipcAddress });\n }\n\n const id = Math.random().toString(36).substring(7);\n const request = {\n jsonrpc: '2.0',\n id,\n method,\n params: params || {},\n auth: { bearerToken: ipcInfo.ipcToken }\n };\n\n client.on('error', (err) => {\n reject(err);\n client.destroy();\n });\n\n client.write(JSON.stringify(request) + '\\n');\n\n let buffer = '';\n client.on('data', (data) => {\n buffer += data.toString();\n // Robust framing: read until we find a complete JSON object on a line\n const lines = buffer.split('\\n');\n buffer = lines.pop() || ''; // Keep the last partial line\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const response = JSON.parse(line);\n if (response.id === id) {\n if (response.error) {\n reject(new Error(response.error.message));\n } else {\n resolve(response.result);\n }\n client.end();\n return;\n }\n } catch (e) {\n // If parse fails, it might be a partial line that we haven't seen the end of yet\n // but since we split by \\n, this shouldn't happen unless the \\n was inside the JSON.\n // However, Conduit ensures JSON-RPC is one line.\n }\n }\n });\n\n client.on('end', () => {\n if (buffer.trim()) {\n try {\n const response = JSON.parse(buffer);\n if (response.id === id) {\n if (response.error) {\n reject(new Error(response.error.message));\n } else {\n resolve(response.result);\n }\n }\n } catch (e) { }\n }\n });\n });\n };\n\n (p as any).globals.set('discover_mcp_tools_js', (options: any) => {\n return sendIPCRequest('mcp_discover_tools', options);\n });\n\n (p as any).globals.set('call_mcp_tool_js', (name: string, args: any) => {\n return sendIPCRequest('mcp_call_tool', { name, arguments: args });\n });\n\n if (shim) {\n await p.runPythonAsync(shim);\n }\n\n const result = await p.runPythonAsync(code);\n\n if (totalOutputBytes > (limits.maxOutputBytes || 1024 * 1024)) {\n throw new Error('[LIMIT_OUTPUT]');\n }\n if (totalLogEntries > (limits.maxLogEntries || 10000)) {\n throw new Error('[LIMIT_LOG]');\n }\n\n parentPort?.postMessage({\n stdout: currentStdout,\n stderr: currentStderr,\n result: String(result),\n success: true,\n });\n } catch (err: any) {\n let isOutput = err.message.includes('[LIMIT_OUTPUT]');\n let isLog = err.message.includes('[LIMIT_LOG]');\n\n // Fallback: check counters if message doesn't match (e.g. wrapped in OSError)\n if (!isOutput && !isLog && currentLimits) {\n if (totalOutputBytes > (currentLimits.maxOutputBytes || 1024 * 1024)) {\n isOutput = true;\n }\n // Check specific log limit breach\n if (totalLogEntries > (currentLimits.maxLogEntries || 10000)) {\n isLog = true;\n }\n }\n\n parentPort?.postMessage({\n stdout: currentStdout,\n stderr: currentStderr,\n error: err.message,\n limitBreached: isOutput ? 'output' : (isLog ? 'log' : undefined),\n success: false,\n });\n }\n}\n\nparentPort?.on('message', async (msg) => {\n if (msg.type === 'execute') {\n await handleTask(msg.data);\n } else if (msg.type === 'ping') {\n parentPort?.postMessage({ type: 'pong' });\n }\n});\n\n// Signal ready\nparentPort?.postMessage({ type: 'ready' });\n\n"],"mappings":"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","names":["data"]}
|
package/dist/index.d.ts
ADDED