Skip to content

Commit 1df4340

Browse files
pi0claude
andcommitted
fix(deno): use stdin/stdout IPC and prevent deno.lock creation
Deno doesn't support Node.js IPC (`process.send`/`process.on("message")`). Switch to newline-delimited JSON over stdin/stdout for bidirectional messaging. Add `--node-modules-dir=auto --no-lock` to use existing node_modules and skip lock file. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ee3dd7c commit 1df4340

File tree

2 files changed

+60
-13
lines changed

2 files changed

+60
-13
lines changed

src/runners/deno-process/runner.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ export class DenoProcessEnvRunner extends BaseEnvRunner {
8181

8282
const child = spawn(
8383
"deno",
84-
["run", "--allow-all", "--node-modules-dir=auto", ...(execArgv || []), this._workerEntry],
84+
["run", "-A", "--node-modules-dir=auto", "--no-lock", ...(execArgv || []), this._workerEntry],
8585
{
8686
env,
87-
stdio: ["pipe", "pipe", "pipe", "ipc"],
87+
stdio: ["pipe", "pipe", "pipe"],
8888
},
8989
);
9090

@@ -95,7 +95,9 @@ export class DenoProcessEnvRunner extends BaseEnvRunner {
9595
const handle: ProcessHandle = {
9696
pid: child.pid!,
9797
kill: () => child.kill(),
98-
send: (message: unknown) => child.send(message as any),
98+
send: (message: unknown) => {
99+
child.stdin!.write(JSON.stringify(message) + "\n");
100+
},
99101
exited,
100102
_exitCode: undefined,
101103
removeAllListeners: () => child.removeAllListeners(),
@@ -113,8 +115,22 @@ export class DenoProcessEnvRunner extends BaseEnvRunner {
113115
}
114116
});
115117

116-
child.on("message", (message: any) => {
117-
this._handleMessage(message);
118+
// Parse newline-delimited JSON from stdout for IPC
119+
let buffer = "";
120+
child.stdout!.on("data", (chunk: Buffer) => {
121+
buffer += chunk.toString();
122+
let newlineIdx;
123+
while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
124+
const line = buffer.slice(0, newlineIdx);
125+
buffer = buffer.slice(newlineIdx + 1);
126+
if (line.startsWith("{")) {
127+
try {
128+
this._handleMessage(JSON.parse(line));
129+
} catch {
130+
// Not JSON, ignore
131+
}
132+
}
133+
}
118134
});
119135

120136
this.#process = handle;

src/runners/deno-process/worker.ts

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@ import { resolveEntry, reloadEntryModule, parseServerAddress } from "../../commo
44

55
const data = JSON.parse(process.env.ENV_RUNNER_DATA || "{}");
66
let entry = await resolveEntry(data.entry);
7-
const sendMessage = (message: unknown) => process.send!(message);
7+
8+
// Deno doesn't support Node.js IPC (process.send), so use stdin/stdout JSON lines
9+
const _stdout = (globalThis as any).Deno?.stdout
10+
? { write: (s: string) => (globalThis as any).Deno.stdout.writeSync(new TextEncoder().encode(s)) }
11+
: process.stdout;
12+
const sendMessage = (message: unknown) => _stdout.write(JSON.stringify(message) + "\n");
13+
14+
const _stdin = (globalThis as any).Deno?.stdin?.readable || process.stdin;
815

916
const server = serve({
1017
port: 0,
@@ -28,34 +35,58 @@ if (entry.ipc) {
2835
await entry.ipc.onOpen?.({ sendMessage });
2936
}
3037

31-
process.send!({
38+
sendMessage({
3239
address: parseServerAddress(server),
3340
});
3441

35-
process.on("message", async (message: any) => {
42+
// Read newline-delimited JSON from stdin
43+
async function readMessages() {
44+
const decoder = new TextDecoder();
45+
let buffer = "";
46+
for await (const chunk of _stdin as AsyncIterable<Uint8Array>) {
47+
buffer += decoder.decode(chunk, { stream: true });
48+
let newlineIdx;
49+
while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
50+
const line = buffer.slice(0, newlineIdx);
51+
buffer = buffer.slice(newlineIdx + 1);
52+
if (!line) continue;
53+
let message: any;
54+
try {
55+
message = JSON.parse(line);
56+
} catch {
57+
continue;
58+
}
59+
await handleMessage(message);
60+
}
61+
}
62+
}
63+
64+
async function handleMessage(message: any) {
3665
if (message?.event === "shutdown") {
3766
Promise.resolve(entry.ipc?.onClose?.())
3867
.then(() => server.close())
3968
.then(() => {
40-
process.send!({ event: "exit" });
69+
sendMessage({ event: "exit" });
4170
});
4271
return;
4372
}
4473

4574
if (message?.event === "reload-module") {
4675
try {
4776
entry = await reloadEntryModule(data.entry, entry, sendMessage);
48-
process.send!({ event: "module-reloaded" });
77+
sendMessage({ event: "module-reloaded" });
4978
} catch (error: any) {
50-
process.send!({ event: "module-reloaded", error: error?.message || String(error) });
79+
sendMessage({ event: "module-reloaded", error: error?.message || String(error) });
5180
}
5281
return;
5382
}
5483

5584
if (message?.type === "ping") {
56-
process.send!({ type: "pong", data: message.data });
85+
sendMessage({ type: "pong", data: message.data });
5786
return;
5887
}
5988

6089
entry.ipc?.onMessage?.(message);
61-
});
90+
}
91+
92+
readMessages().catch(() => {});

0 commit comments

Comments
 (0)