@dev-anywhere/proxy 0.1.9 → 0.2.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/dist/{chunk-BMVYMCKF.js → chunk-3ZUZ22V6.js} +2 -2
- package/dist/{chunk-7XMJMVIL.js → chunk-4YQ2JUM7.js} +41 -6
- package/dist/chunk-4YQ2JUM7.js.map +1 -0
- package/dist/chunk-7UOPAMX7.js +220 -0
- package/dist/chunk-7UOPAMX7.js.map +1 -0
- package/dist/chunk-NBRBO5GS.js +1032 -0
- package/dist/chunk-NBRBO5GS.js.map +1 -0
- package/dist/chunk-NQDJ6QAM.js +18 -0
- package/dist/chunk-NQDJ6QAM.js.map +1 -0
- package/dist/chunk-OBYEKZWC.js +104 -0
- package/dist/chunk-OBYEKZWC.js.map +1 -0
- package/dist/chunk-PWG6K5QB.js +204 -0
- package/dist/chunk-PWG6K5QB.js.map +1 -0
- package/dist/{chunk-DCDXAM76.js → chunk-RIQ6OL7X.js} +9 -6
- package/dist/chunk-RIQ6OL7X.js.map +1 -0
- package/dist/{chunk-6O6JTF24.js → chunk-WUBRUO3G.js} +1 -1
- package/dist/index.js +5 -5
- package/dist/{relay-token-Z4JZFPQ5.js → relay-token-RKAVVQHE.js} +5 -4
- package/dist/{relay-token-Z4JZFPQ5.js.map → relay-token-RKAVVQHE.js.map} +1 -1
- package/dist/serve.js +538 -431
- package/dist/serve.js.map +1 -1
- package/dist/session-worker.js +99 -32
- package/dist/session-worker.js.map +1 -1
- package/dist/{terminal-FJAIRC73.js → terminal-YO2D2OJU.js} +194 -151
- package/dist/terminal-YO2D2OJU.js.map +1 -0
- package/package.json +3 -3
- package/dist/chunk-2JUB4LDU.js +0 -84
- package/dist/chunk-2JUB4LDU.js.map +0 -1
- package/dist/chunk-7XMJMVIL.js.map +0 -1
- package/dist/chunk-DCDXAM76.js.map +0 -1
- package/dist/chunk-ORZTFYXR.js +0 -123
- package/dist/chunk-ORZTFYXR.js.map +0 -1
- package/dist/chunk-QFYI6AMN.js +0 -870
- package/dist/chunk-QFYI6AMN.js.map +0 -1
- package/dist/chunk-U5T7ZYXT.js +0 -346
- package/dist/chunk-U5T7ZYXT.js.map +0 -1
- package/dist/terminal-FJAIRC73.js.map +0 -1
- /package/dist/{chunk-BMVYMCKF.js.map → chunk-3ZUZ22V6.js.map} +0 -0
- /package/dist/{chunk-6O6JTF24.js.map → chunk-WUBRUO3G.js.map} +0 -0
package/dist/session-worker.js
CHANGED
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
import {
|
|
3
3
|
ControlRequestEventSchema,
|
|
4
4
|
SeqCounter
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-4YQ2JUM7.js";
|
|
6
6
|
import {
|
|
7
7
|
CLAUDE_PROVIDER
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-WUBRUO3G.js";
|
|
9
9
|
import {
|
|
10
10
|
LineBuffer,
|
|
11
11
|
createWorkerReader,
|
|
12
12
|
serializeWorkerMsg
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-NBRBO5GS.js";
|
|
14
|
+
import "./chunk-NQDJ6QAM.js";
|
|
15
|
+
import "./chunk-PWG6K5QB.js";
|
|
15
16
|
|
|
16
17
|
// src/session-worker.ts
|
|
17
18
|
import { createServer } from "net";
|
|
@@ -161,8 +162,27 @@ var JsonSession = class {
|
|
|
161
162
|
}
|
|
162
163
|
setupExitHandler() {
|
|
163
164
|
if (!this.child) return;
|
|
165
|
+
let stdoutEnded = false;
|
|
166
|
+
let exitCode = null;
|
|
167
|
+
let exited = false;
|
|
168
|
+
let fired = false;
|
|
169
|
+
const fireOnce = () => {
|
|
170
|
+
if (fired || !exited || !stdoutEnded) return;
|
|
171
|
+
fired = true;
|
|
172
|
+
this.onExitCb?.(exitCode ?? 1);
|
|
173
|
+
};
|
|
174
|
+
this.child.stdout?.on("end", () => {
|
|
175
|
+
stdoutEnded = true;
|
|
176
|
+
fireOnce();
|
|
177
|
+
});
|
|
164
178
|
this.child.on("exit", (code) => {
|
|
165
|
-
|
|
179
|
+
exitCode = code;
|
|
180
|
+
exited = true;
|
|
181
|
+
setTimeout(() => {
|
|
182
|
+
stdoutEnded = true;
|
|
183
|
+
fireOnce();
|
|
184
|
+
}, 1e3).unref();
|
|
185
|
+
fireOnce();
|
|
166
186
|
});
|
|
167
187
|
}
|
|
168
188
|
handleControlRequest(event) {
|
|
@@ -191,27 +211,59 @@ var JsonSession = class {
|
|
|
191
211
|
}
|
|
192
212
|
};
|
|
193
213
|
this.writeToStdin(JSON.stringify(response));
|
|
214
|
+
}).catch((err) => {
|
|
215
|
+
console.error(
|
|
216
|
+
"[json-session] approval strategy rejected, fallback to deny",
|
|
217
|
+
requestId,
|
|
218
|
+
err instanceof Error ? err.message : err
|
|
219
|
+
);
|
|
220
|
+
this.writeToStdin(
|
|
221
|
+
JSON.stringify({
|
|
222
|
+
type: "control_response",
|
|
223
|
+
response: {
|
|
224
|
+
subtype: "success",
|
|
225
|
+
request_id: requestId,
|
|
226
|
+
response: {
|
|
227
|
+
behavior: "deny",
|
|
228
|
+
message: "Approval strategy failed; denied as fallback."
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
);
|
|
194
233
|
});
|
|
195
234
|
}
|
|
196
235
|
writeToStdin(data) {
|
|
197
|
-
this.writeQueue = this.writeQueue.then(
|
|
198
|
-
|
|
236
|
+
this.writeQueue = this.writeQueue.then(
|
|
237
|
+
() => new Promise((resolve, reject) => {
|
|
199
238
|
if (!this.child?.stdin?.writable) {
|
|
200
239
|
reject(new Error("stdin not writable"));
|
|
201
240
|
return;
|
|
202
241
|
}
|
|
203
242
|
this.child.stdin.write(data + "\n", (err) => {
|
|
204
|
-
if (err)
|
|
205
|
-
|
|
206
|
-
} else {
|
|
207
|
-
resolve();
|
|
208
|
-
}
|
|
243
|
+
if (err) reject(err);
|
|
244
|
+
else resolve();
|
|
209
245
|
});
|
|
210
|
-
})
|
|
246
|
+
})
|
|
247
|
+
).catch((err) => {
|
|
248
|
+
console.error(
|
|
249
|
+
"[json-session] writeToStdin failed",
|
|
250
|
+
err instanceof Error ? err.message : err
|
|
251
|
+
);
|
|
211
252
|
});
|
|
212
253
|
}
|
|
213
254
|
};
|
|
214
255
|
|
|
256
|
+
// src/worker/serve-socket-takeover.ts
|
|
257
|
+
function takeoverServeSocket(prev, next) {
|
|
258
|
+
if (prev && prev !== next) {
|
|
259
|
+
try {
|
|
260
|
+
prev.destroy();
|
|
261
|
+
} catch {
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return next;
|
|
265
|
+
}
|
|
266
|
+
|
|
215
267
|
// src/session-worker.ts
|
|
216
268
|
var sessionId = process.argv[2];
|
|
217
269
|
var sockPath = process.argv[3];
|
|
@@ -295,7 +347,7 @@ var session = new JsonSession({
|
|
|
295
347
|
}
|
|
296
348
|
});
|
|
297
349
|
function handleServeConnection(socket) {
|
|
298
|
-
serveSocket = socket;
|
|
350
|
+
serveSocket = takeoverServeSocket(serveSocket, socket);
|
|
299
351
|
for (const [requestId, pending] of pendingApprovals) {
|
|
300
352
|
sendToServe({
|
|
301
353
|
type: "worker_approval_request",
|
|
@@ -304,34 +356,49 @@ function handleServeConnection(socket) {
|
|
|
304
356
|
input: pending.input
|
|
305
357
|
});
|
|
306
358
|
}
|
|
307
|
-
createWorkerReader(
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
pending
|
|
319
|
-
|
|
359
|
+
createWorkerReader(
|
|
360
|
+
socket,
|
|
361
|
+
(msg) => {
|
|
362
|
+
switch (msg.type) {
|
|
363
|
+
case "worker_input":
|
|
364
|
+
session.sendMessage(msg.content);
|
|
365
|
+
break;
|
|
366
|
+
case "worker_stop":
|
|
367
|
+
session.stop();
|
|
368
|
+
break;
|
|
369
|
+
case "worker_approval_response": {
|
|
370
|
+
const pending = pendingApprovals.get(msg.requestId);
|
|
371
|
+
if (pending) {
|
|
372
|
+
pending.resolve({ behavior: msg.behavior, message: msg.message });
|
|
373
|
+
pendingApprovals.delete(msg.requestId);
|
|
374
|
+
}
|
|
375
|
+
break;
|
|
320
376
|
}
|
|
321
|
-
|
|
377
|
+
case "worker_whitelist_add":
|
|
378
|
+
whitelist.add(msg.toolName);
|
|
379
|
+
break;
|
|
322
380
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
381
|
+
},
|
|
382
|
+
(err) => {
|
|
383
|
+
console.error(`[worker] serve IPC message dropped: ${err.message}`);
|
|
326
384
|
}
|
|
327
|
-
|
|
385
|
+
);
|
|
328
386
|
socket.on("close", () => {
|
|
329
387
|
serveSocket = null;
|
|
388
|
+
rejectAllPendingApprovals("Serve connection closed");
|
|
330
389
|
});
|
|
331
390
|
socket.on("error", () => {
|
|
332
391
|
serveSocket = null;
|
|
392
|
+
rejectAllPendingApprovals("Serve connection error");
|
|
333
393
|
});
|
|
334
394
|
}
|
|
395
|
+
function rejectAllPendingApprovals(reason) {
|
|
396
|
+
if (pendingApprovals.size === 0) return;
|
|
397
|
+
for (const [, pending] of pendingApprovals) {
|
|
398
|
+
pending.resolve({ behavior: "deny", message: reason });
|
|
399
|
+
}
|
|
400
|
+
pendingApprovals.clear();
|
|
401
|
+
}
|
|
335
402
|
var sockDir = sockPath.substring(0, sockPath.lastIndexOf("/"));
|
|
336
403
|
mkdirSync(sockDir, { recursive: true });
|
|
337
404
|
if (existsSync(sockPath)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/session-worker.ts","../src/worker/json-session.ts"],"sourcesContent":["import { createServer, type Socket } from \"node:net\";\nimport { mkdirSync, unlinkSync, existsSync, chmodSync } from \"node:fs\";\nimport {\n JsonSession,\n ToolWhitelist,\n createRelayApprovalStrategy,\n type StreamJsonEvent,\n type ClaudePermissionMode,\n} from \"./worker/json-session.js\";\nimport { SeqCounter } from \"./common/seq-counter.js\";\nimport { createWorkerReader, serializeWorkerMsg, type WorkerMessage } from \"./ipc/ipc-protocol.js\";\nimport type { ProviderHookContext } from \"./providers/index.js\";\n\n// 参数格式: session-worker.ts <sessionId> <socketPath> [--cwd <dir>] [--resume <id>] [-- claude args...]\nconst sessionId = process.argv[2];\nconst sockPath = process.argv[3];\nconst separatorIdx = process.argv.indexOf(\"--\");\nconst claudeArgs = separatorIdx >= 0 ? process.argv.slice(separatorIdx + 1) : [];\n\n// 解析 -- 之前的可选参数\nconst preArgs = process.argv.slice(4, separatorIdx >= 0 ? separatorIdx : undefined);\nfunction getArg(name: string): string | undefined {\n const idx = preArgs.indexOf(name);\n return idx >= 0 && idx + 1 < preArgs.length ? preArgs[idx + 1] : undefined;\n}\nfunction hasFlag(name: string): boolean {\n return preArgs.includes(name);\n}\nconst workerCwd = getArg(\"--cwd\");\nconst workerResume = getArg(\"--resume\");\nconst workerPermissionMode = getArg(\"--permission-mode\") as ClaudePermissionMode | undefined;\nconst workerStreamDelta = hasFlag(\"--stream-delta\");\nconst workerHookUrl = getArg(\"--hook-url\");\nconst workerHookMarker = getArg(\"--hook-marker\");\nconst workerHookToken = process.env.DEV_ANYWHERE_HOOK_TOKEN;\nconst workerHookProvider = getArg(\"--hook-provider\") as ProviderHookContext[\"provider\"] | undefined;\n\nif (!sessionId || !sockPath) {\n console.error(\"Usage: session-worker <sessionId> <socketPath> [-- claudeArgs...]\");\n process.exit(1);\n}\n\nconst workerHook: ProviderHookContext | undefined =\n workerHookUrl && workerHookMarker && workerHookToken && workerHookProvider\n ? {\n provider: workerHookProvider,\n sessionId,\n hookUrl: workerHookUrl,\n marker: workerHookMarker,\n token: workerHookToken,\n }\n : undefined;\n\nlet serveSocket: Socket | null = null;\nconst seqCounter = new SeqCounter(sessionId);\nconst whitelist = new ToolWhitelist();\n\nconst pendingApprovals = new Map<\n string,\n {\n resolve: (decision: { behavior: \"allow\" | \"deny\"; message?: string }) => void;\n toolName: string;\n input: Record<string, unknown>;\n }\n>();\n\nfunction sendToServe(msg: WorkerMessage): void {\n if (serveSocket?.writable) {\n serveSocket.write(serializeWorkerMsg(msg));\n }\n}\n\n// 转发审批请求到 serve 进程,由 serve 进程通过 relay 转发到小程序\nconst forwardToRelay = async (\n toolName: string,\n input: Record<string, unknown>,\n): Promise<{ behavior: \"allow\" | \"deny\"; message?: string }> => {\n return new Promise((resolve) => {\n const requestId = `${sessionId}-${Date.now()}`;\n pendingApprovals.set(requestId, { resolve, toolName, input });\n sendToServe({\n type: \"worker_approval_request\",\n requestId,\n toolName,\n input,\n });\n });\n};\n\nconst session = new JsonSession({\n claudeArgs,\n cwd: workerCwd,\n resumeSessionId: workerResume,\n permissionMode: workerPermissionMode,\n includePartialMessages: workerStreamDelta,\n hook: workerHook,\n approvalStrategy: createRelayApprovalStrategy(whitelist, forwardToRelay),\n onEvent: (event: StreamJsonEvent) => {\n // 从 system 事件中捕获 Claude 会话 ID 并通知 serve\n if (event.type === \"system\" && typeof event.session_id === \"string\") {\n sendToServe({\n type: \"worker_claude_session_id\",\n sessionId: event.session_id,\n });\n }\n\n const seq = seqCounter.next();\n sendToServe({\n type: \"worker_event\",\n seq,\n event: event as Record<string, unknown>,\n });\n },\n onExit: (code: number) => {\n whitelist.clear();\n sendToServe({ type: \"worker_exit\", code });\n cleanup();\n process.exit(0);\n },\n});\n\nfunction handleServeConnection(socket: Socket): void {\n serveSocket = socket;\n\n for (const [requestId, pending] of pendingApprovals) {\n sendToServe({\n type: \"worker_approval_request\",\n requestId,\n toolName: pending.toolName,\n input: pending.input,\n });\n }\n\n createWorkerReader(socket, (msg: WorkerMessage) => {\n switch (msg.type) {\n case \"worker_input\":\n session.sendMessage(msg.content);\n break;\n case \"worker_stop\":\n session.stop();\n break;\n case \"worker_approval_response\": {\n const pending = pendingApprovals.get(msg.requestId);\n if (pending) {\n pending.resolve({ behavior: msg.behavior, message: msg.message });\n pendingApprovals.delete(msg.requestId);\n }\n break;\n }\n case \"worker_whitelist_add\":\n whitelist.add(msg.toolName);\n break;\n }\n });\n\n socket.on(\"close\", () => {\n serveSocket = null;\n });\n socket.on(\"error\", () => {\n serveSocket = null;\n });\n}\n\nconst sockDir = sockPath.substring(0, sockPath.lastIndexOf(\"/\"));\nmkdirSync(sockDir, { recursive: true });\n\nif (existsSync(sockPath)) {\n unlinkSync(sockPath);\n}\n\nconst server = createServer((socket) => {\n handleServeConnection(socket);\n});\n\nfunction cleanup(): void {\n server.close();\n try {\n unlinkSync(sockPath);\n } catch {\n // socket 文件可能已被删除\n }\n}\n\nprocess.on(\"SIGTERM\", () => {\n session.stop();\n});\n\nserver.listen(sockPath, () => {\n chmodSync(sockPath, 0o600);\n const pid = session.start();\n sendToServe({ type: \"worker_ready\", pid });\n});\n","import { spawn, type ChildProcess } from \"node:child_process\";\nimport type { z } from \"zod\";\nimport { LineBuffer } from \"../ipc/line-buffer.js\";\nimport { ControlRequestEventSchema } from \"../common/stream-json-schema.js\";\nimport {\n CLAUDE_PROVIDER,\n buildClaudeArgs,\n filterClaudeEnvVars,\n type ClaudePermissionMode,\n} from \"../providers/index.js\";\nimport type { ProviderHookContext } from \"../providers/index.js\";\n\nexport { buildClaudeArgs, filterClaudeEnvVars };\nexport type { ClaudePermissionMode };\n\n// stream-json event types observed from provider output.\nexport type StreamJsonEventType =\n | \"system\"\n | \"assistant\"\n | \"user\"\n | \"result\"\n | \"control_request\"\n | \"control_cancel_request\"\n | \"stream_event\";\n\nexport interface StreamJsonEvent {\n type: StreamJsonEventType;\n [key: string]: unknown;\n}\n\nexport type ApprovalStrategy = (\n toolName: string,\n input: Record<string, unknown>,\n) => Promise<{ behavior: \"allow\" | \"deny\"; message?: string }>;\n\ninterface JsonSessionOptions {\n workDir?: string;\n claudeArgs?: string[];\n approvalStrategy?: ApprovalStrategy;\n onEvent?: (event: StreamJsonEvent) => void;\n onExit?: (code: number) => void;\n cwd?: string;\n resumeSessionId?: string;\n permissionMode?: ClaudePermissionMode;\n includePartialMessages?: boolean;\n hook?: ProviderHookContext;\n}\n\n// 默认拒绝所有工具调用,远程审批未配置前的安全兜底\nconst denyAllStrategy: ApprovalStrategy = async () => ({\n behavior: \"deny\" as const,\n message: \"Tool use denied by default policy. Remote approval not yet configured.\",\n});\n\n// 会话级别的工具白名单,用户点击\"全部允许\"后同名工具自动审批\nexport class ToolWhitelist {\n private allowed = new Set<string>();\n\n has(toolName: string): boolean {\n return this.allowed.has(toolName);\n }\n\n add(toolName: string): void {\n this.allowed.add(toolName);\n }\n\n clear(): void {\n this.allowed.clear();\n }\n}\n\n// 创建中继转发审批策略,先检查白名单再转发到 relay\nexport function createRelayApprovalStrategy(\n whitelist: ToolWhitelist,\n forwardToRelay: (\n toolName: string,\n input: Record<string, unknown>,\n ) => Promise<{ behavior: \"allow\" | \"deny\"; message?: string }>,\n): ApprovalStrategy {\n return async (toolName, input) => {\n if (whitelist.has(toolName)) {\n return { behavior: \"allow\", message: \"Auto-approved by session whitelist\" };\n }\n return forwardToRelay(toolName, input);\n };\n}\n\nexport class JsonSession {\n private child: ChildProcess | null = null;\n private stderrChunks: string[] = [];\n private writeQueue: Promise<void> = Promise.resolve();\n private claudeSessionId: string | null = null;\n private readonly workDir: string;\n private readonly claudeArgs: string[];\n private readonly approvalStrategy: ApprovalStrategy;\n private readonly onEvent?: (event: StreamJsonEvent) => void;\n private readonly onExitCb?: (code: number) => void;\n private readonly resumeSessionId?: string;\n private readonly permissionMode?: ClaudePermissionMode;\n private readonly includePartialMessages: boolean;\n private readonly hook?: ProviderHookContext;\n\n constructor(options: JsonSessionOptions = {}) {\n this.workDir = options.cwd ?? options.workDir ?? process.cwd();\n this.claudeArgs = options.claudeArgs ?? [];\n this.approvalStrategy = options.approvalStrategy ?? denyAllStrategy;\n this.onEvent = options.onEvent;\n this.onExitCb = options.onExit;\n this.resumeSessionId = options.resumeSessionId;\n this.permissionMode = options.permissionMode;\n this.includePartialMessages = options.includePartialMessages ?? false;\n this.hook = options.hook;\n }\n\n getClaudeSessionId(): string | null {\n return this.claudeSessionId;\n }\n\n start(): number {\n const command = CLAUDE_PROVIDER.buildJsonCommand(\n {\n extraArgs: this.claudeArgs,\n permissionMode: this.permissionMode,\n resumeSessionId: this.resumeSessionId,\n includePartialMessages: this.includePartialMessages,\n hook: this.hook,\n },\n process.env,\n );\n\n this.child = spawn(command.command, command.args, {\n cwd: this.workDir,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: command.env,\n });\n\n this.setupStdoutParsing();\n this.setupStderrCollection();\n this.setupExitHandler();\n\n return this.child.pid!;\n }\n\n sendMessage(content: string): void {\n const message = {\n type: \"user\",\n message: { role: \"user\", content },\n };\n this.writeToStdin(JSON.stringify(message));\n }\n\n async stop(gracePeriodMs = 5000): Promise<void> {\n if (!this.child || !this.isAlive()) return;\n\n this.child.kill(\"SIGTERM\");\n\n const start = Date.now();\n while (Date.now() - start < gracePeriodMs) {\n if (!this.isAlive()) return;\n await new Promise((r) => setTimeout(r, 200));\n }\n\n if (this.isAlive()) {\n this.child.kill(\"SIGKILL\");\n }\n }\n\n isAlive(): boolean {\n if (!this.child || !this.child.pid) return false;\n try {\n process.kill(this.child.pid, 0);\n return true;\n } catch {\n return false;\n }\n }\n\n getStderr(): string {\n return this.stderrChunks.join(\"\");\n }\n\n private setupStdoutParsing(): void {\n if (!this.child?.stdout) return;\n\n const lineBuffer = new LineBuffer();\n this.child.stdout.pipe(lineBuffer);\n\n lineBuffer.on(\"data\", (line: Buffer | string) => {\n const str = typeof line === \"string\" ? line : line.toString();\n let event: StreamJsonEvent;\n try {\n event = JSON.parse(str) as StreamJsonEvent;\n } catch {\n // 非 JSON 行直接跳过,verbose 模式会输出调试日志\n return;\n }\n\n // 从 system 事件中捕获 Claude 会话 ID 用于后续 resume\n if (event.type === \"system\" && typeof event.session_id === \"string\") {\n this.claudeSessionId = event.session_id;\n }\n\n if (event.type === \"control_request\") {\n // schema parse 失败说明 CLI 协议漂移,调用方必须感知而不是静默吃掉\n const parsed = ControlRequestEventSchema.safeParse(event);\n if (!parsed.success) {\n console.error(\n \"[json-session] control_request shape mismatch; skipping approval\",\n parsed.error.issues.slice(0, 3),\n );\n return;\n }\n this.handleControlRequest(parsed.data);\n return;\n }\n\n this.onEvent?.(event);\n });\n }\n\n private setupStderrCollection(): void {\n if (!this.child?.stderr) return;\n this.child.stderr.on(\"data\", (chunk: Buffer | string) => {\n this.stderrChunks.push(typeof chunk === \"string\" ? chunk : chunk.toString());\n });\n }\n\n private setupExitHandler(): void {\n if (!this.child) return;\n this.child.on(\"exit\", (code: number | null) => {\n this.onExitCb?.(code ?? 1);\n });\n }\n\n private handleControlRequest(event: z.infer<typeof ControlRequestEventSchema>): void {\n const requestId = event.request_id;\n const request = event.request;\n\n this.approvalStrategy(request.tool_name, request.input).then((decision) => {\n const response =\n decision.behavior === \"deny\"\n ? {\n type: \"control_response\",\n response: {\n subtype: \"success\",\n request_id: requestId,\n response: {\n behavior: \"deny\",\n message: decision.message ?? \"Tool use denied by default policy.\",\n },\n },\n }\n : {\n type: \"control_response\",\n response: {\n subtype: \"success\",\n request_id: requestId,\n response: {\n behavior: \"allow\",\n updatedInput: {},\n },\n },\n };\n\n this.writeToStdin(JSON.stringify(response));\n });\n }\n\n private writeToStdin(data: string): void {\n this.writeQueue = this.writeQueue.then(() => {\n return new Promise<void>((resolve, reject) => {\n if (!this.child?.stdin?.writable) {\n reject(new Error(\"stdin not writable\"));\n return;\n }\n this.child.stdin.write(data + \"\\n\", (err) => {\n if (err) {\n reject(err);\n } else {\n resolve();\n }\n });\n });\n });\n }\n}\n"],"mappings":"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","names":["whitelist","forwardToRelay"]}
|
|
1
|
+
{"version":3,"sources":["../src/session-worker.ts","../src/worker/json-session.ts","../src/worker/serve-socket-takeover.ts"],"sourcesContent":["import { createServer, type Socket } from \"node:net\";\nimport { mkdirSync, unlinkSync, existsSync, chmodSync } from \"node:fs\";\nimport {\n JsonSession,\n ToolWhitelist,\n createRelayApprovalStrategy,\n type StreamJsonEvent,\n type ClaudePermissionMode,\n} from \"./worker/json-session.js\";\nimport { SeqCounter } from \"./common/seq-counter.js\";\nimport { createWorkerReader, serializeWorkerMsg, type WorkerMessage } from \"./ipc/ipc-protocol.js\";\nimport { takeoverServeSocket } from \"./worker/serve-socket-takeover.js\";\nimport type { ProviderHookContext } from \"./providers/index.js\";\n\n// 参数格式: session-worker.ts <sessionId> <socketPath> [--cwd <dir>] [--resume <id>] [-- claude args...]\nconst sessionId = process.argv[2];\nconst sockPath = process.argv[3];\nconst separatorIdx = process.argv.indexOf(\"--\");\nconst claudeArgs = separatorIdx >= 0 ? process.argv.slice(separatorIdx + 1) : [];\n\n// 解析 -- 之前的可选参数\nconst preArgs = process.argv.slice(4, separatorIdx >= 0 ? separatorIdx : undefined);\nfunction getArg(name: string): string | undefined {\n const idx = preArgs.indexOf(name);\n return idx >= 0 && idx + 1 < preArgs.length ? preArgs[idx + 1] : undefined;\n}\nfunction hasFlag(name: string): boolean {\n return preArgs.includes(name);\n}\nconst workerCwd = getArg(\"--cwd\");\nconst workerResume = getArg(\"--resume\");\nconst workerPermissionMode = getArg(\"--permission-mode\") as ClaudePermissionMode | undefined;\nconst workerStreamDelta = hasFlag(\"--stream-delta\");\nconst workerHookUrl = getArg(\"--hook-url\");\nconst workerHookMarker = getArg(\"--hook-marker\");\nconst workerHookToken = process.env.DEV_ANYWHERE_HOOK_TOKEN;\nconst workerHookProvider = getArg(\"--hook-provider\") as ProviderHookContext[\"provider\"] | undefined;\n\nif (!sessionId || !sockPath) {\n console.error(\"Usage: session-worker <sessionId> <socketPath> [-- claudeArgs...]\");\n process.exit(1);\n}\n\nconst workerHook: ProviderHookContext | undefined =\n workerHookUrl && workerHookMarker && workerHookToken && workerHookProvider\n ? {\n provider: workerHookProvider,\n sessionId,\n hookUrl: workerHookUrl,\n marker: workerHookMarker,\n token: workerHookToken,\n }\n : undefined;\n\nlet serveSocket: Socket | null = null;\nconst seqCounter = new SeqCounter(sessionId);\nconst whitelist = new ToolWhitelist();\n\nconst pendingApprovals = new Map<\n string,\n {\n resolve: (decision: { behavior: \"allow\" | \"deny\"; message?: string }) => void;\n toolName: string;\n input: Record<string, unknown>;\n }\n>();\n\nfunction sendToServe(msg: WorkerMessage): void {\n if (serveSocket?.writable) {\n serveSocket.write(serializeWorkerMsg(msg));\n }\n}\n\n// 转发审批请求到 serve 进程,由 serve 进程通过 relay 转发到 web 客户端\nconst forwardToRelay = async (\n toolName: string,\n input: Record<string, unknown>,\n): Promise<{ behavior: \"allow\" | \"deny\"; message?: string }> => {\n return new Promise((resolve) => {\n const requestId = `${sessionId}-${Date.now()}`;\n pendingApprovals.set(requestId, { resolve, toolName, input });\n sendToServe({\n type: \"worker_approval_request\",\n requestId,\n toolName,\n input,\n });\n });\n};\n\nconst session = new JsonSession({\n claudeArgs,\n cwd: workerCwd,\n resumeSessionId: workerResume,\n permissionMode: workerPermissionMode,\n includePartialMessages: workerStreamDelta,\n hook: workerHook,\n approvalStrategy: createRelayApprovalStrategy(whitelist, forwardToRelay),\n onEvent: (event: StreamJsonEvent) => {\n // 从 system 事件中捕获 Claude 会话 ID 并通知 serve\n if (event.type === \"system\" && typeof event.session_id === \"string\") {\n sendToServe({\n type: \"worker_claude_session_id\",\n sessionId: event.session_id,\n });\n }\n\n const seq = seqCounter.next();\n sendToServe({\n type: \"worker_event\",\n seq,\n event: event as Record<string, unknown>,\n });\n },\n onExit: (code: number) => {\n whitelist.clear();\n sendToServe({ type: \"worker_exit\", code });\n cleanup();\n process.exit(0);\n },\n});\n\nfunction handleServeConnection(socket: Socket): void {\n serveSocket = takeoverServeSocket(serveSocket, socket);\n\n for (const [requestId, pending] of pendingApprovals) {\n sendToServe({\n type: \"worker_approval_request\",\n requestId,\n toolName: pending.toolName,\n input: pending.input,\n });\n }\n\n createWorkerReader(\n socket,\n (msg: WorkerMessage) => {\n switch (msg.type) {\n case \"worker_input\":\n session.sendMessage(msg.content);\n break;\n case \"worker_stop\":\n session.stop();\n break;\n case \"worker_approval_response\": {\n const pending = pendingApprovals.get(msg.requestId);\n if (pending) {\n pending.resolve({ behavior: msg.behavior, message: msg.message });\n pendingApprovals.delete(msg.requestId);\n }\n break;\n }\n case \"worker_whitelist_add\":\n whitelist.add(msg.toolName);\n break;\n }\n },\n (err) => {\n // worker 进程没有 pino logger,console.error 经 ipc-protocol 捕获到 stderr。\n // 同样不让单条 schema 错误升级成 socket close。\n console.error(`[worker] serve IPC message dropped: ${err.message}`);\n },\n );\n\n socket.on(\"close\", () => {\n serveSocket = null;\n rejectAllPendingApprovals(\"Serve connection closed\");\n });\n socket.on(\"error\", () => {\n serveSocket = null;\n rejectAllPendingApprovals(\"Serve connection error\");\n });\n}\n\n// serve socket 断开时:所有未决 approval 立即按 deny 落盘。deny 是安全默认值(不执行操作),\n// 防止 worker 在 approvalStrategy 里永久 await 一个永不 resolve 的 Promise,从而把 claude\n// 进程拖入死锁状态直到 60s reaper。\nfunction rejectAllPendingApprovals(reason: string): void {\n if (pendingApprovals.size === 0) return;\n for (const [, pending] of pendingApprovals) {\n pending.resolve({ behavior: \"deny\", message: reason });\n }\n pendingApprovals.clear();\n}\n\nconst sockDir = sockPath.substring(0, sockPath.lastIndexOf(\"/\"));\nmkdirSync(sockDir, { recursive: true });\n\nif (existsSync(sockPath)) {\n unlinkSync(sockPath);\n}\n\nconst server = createServer((socket) => {\n handleServeConnection(socket);\n});\n\nfunction cleanup(): void {\n server.close();\n try {\n unlinkSync(sockPath);\n } catch {\n // socket 文件可能已被删除\n }\n}\n\nprocess.on(\"SIGTERM\", () => {\n session.stop();\n});\n\nserver.listen(sockPath, () => {\n chmodSync(sockPath, 0o600);\n const pid = session.start();\n sendToServe({ type: \"worker_ready\", pid });\n});\n","import { spawn, type ChildProcess } from \"node:child_process\";\nimport type { z } from \"zod\";\nimport { LineBuffer } from \"../ipc/line-buffer.js\";\nimport { ControlRequestEventSchema } from \"../common/stream-json-schema.js\";\nimport { CLAUDE_PROVIDER, type ClaudePermissionMode } from \"../providers/index.js\";\nimport type { ProviderHookContext } from \"../providers/index.js\";\n\nexport type { ClaudePermissionMode };\n\n// stream-json event types observed from provider output.\nexport type StreamJsonEventType =\n | \"system\"\n | \"assistant\"\n | \"user\"\n | \"result\"\n | \"control_request\"\n | \"control_cancel_request\"\n | \"stream_event\";\n\nexport interface StreamJsonEvent {\n type: StreamJsonEventType;\n [key: string]: unknown;\n}\n\nexport type ApprovalStrategy = (\n toolName: string,\n input: Record<string, unknown>,\n) => Promise<{ behavior: \"allow\" | \"deny\"; message?: string }>;\n\ninterface JsonSessionOptions {\n workDir?: string;\n claudeArgs?: string[];\n approvalStrategy?: ApprovalStrategy;\n onEvent?: (event: StreamJsonEvent) => void;\n onExit?: (code: number) => void;\n cwd?: string;\n resumeSessionId?: string;\n permissionMode?: ClaudePermissionMode;\n includePartialMessages?: boolean;\n hook?: ProviderHookContext;\n}\n\n// 默认拒绝所有工具调用,远程审批未配置前的安全兜底\nconst denyAllStrategy: ApprovalStrategy = async () => ({\n behavior: \"deny\" as const,\n message: \"Tool use denied by default policy. Remote approval not yet configured.\",\n});\n\n// 会话级别的工具白名单,用户点击\"全部允许\"后同名工具自动审批\nexport class ToolWhitelist {\n private allowed = new Set<string>();\n\n has(toolName: string): boolean {\n return this.allowed.has(toolName);\n }\n\n add(toolName: string): void {\n this.allowed.add(toolName);\n }\n\n clear(): void {\n this.allowed.clear();\n }\n}\n\n// 创建中继转发审批策略,先检查白名单再转发到 relay\nexport function createRelayApprovalStrategy(\n whitelist: ToolWhitelist,\n forwardToRelay: (\n toolName: string,\n input: Record<string, unknown>,\n ) => Promise<{ behavior: \"allow\" | \"deny\"; message?: string }>,\n): ApprovalStrategy {\n return async (toolName, input) => {\n if (whitelist.has(toolName)) {\n return { behavior: \"allow\", message: \"Auto-approved by session whitelist\" };\n }\n return forwardToRelay(toolName, input);\n };\n}\n\nexport class JsonSession {\n private child: ChildProcess | null = null;\n private stderrChunks: string[] = [];\n private writeQueue: Promise<void> = Promise.resolve();\n private claudeSessionId: string | null = null;\n private readonly workDir: string;\n private readonly claudeArgs: string[];\n private readonly approvalStrategy: ApprovalStrategy;\n private readonly onEvent?: (event: StreamJsonEvent) => void;\n private readonly onExitCb?: (code: number) => void;\n private readonly resumeSessionId?: string;\n private readonly permissionMode?: ClaudePermissionMode;\n private readonly includePartialMessages: boolean;\n private readonly hook?: ProviderHookContext;\n\n constructor(options: JsonSessionOptions = {}) {\n this.workDir = options.cwd ?? options.workDir ?? process.cwd();\n this.claudeArgs = options.claudeArgs ?? [];\n this.approvalStrategy = options.approvalStrategy ?? denyAllStrategy;\n this.onEvent = options.onEvent;\n this.onExitCb = options.onExit;\n this.resumeSessionId = options.resumeSessionId;\n this.permissionMode = options.permissionMode;\n this.includePartialMessages = options.includePartialMessages ?? false;\n this.hook = options.hook;\n }\n\n getClaudeSessionId(): string | null {\n return this.claudeSessionId;\n }\n\n start(): number {\n const command = CLAUDE_PROVIDER.buildJsonCommand(\n {\n extraArgs: this.claudeArgs,\n permissionMode: this.permissionMode,\n resumeSessionId: this.resumeSessionId,\n includePartialMessages: this.includePartialMessages,\n hook: this.hook,\n },\n process.env,\n );\n\n this.child = spawn(command.command, command.args, {\n cwd: this.workDir,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: command.env,\n });\n\n this.setupStdoutParsing();\n this.setupStderrCollection();\n this.setupExitHandler();\n\n return this.child.pid!;\n }\n\n sendMessage(content: string): void {\n const message = {\n type: \"user\",\n message: { role: \"user\", content },\n };\n this.writeToStdin(JSON.stringify(message));\n }\n\n async stop(gracePeriodMs = 5000): Promise<void> {\n if (!this.child || !this.isAlive()) return;\n\n this.child.kill(\"SIGTERM\");\n\n const start = Date.now();\n while (Date.now() - start < gracePeriodMs) {\n if (!this.isAlive()) return;\n await new Promise((r) => setTimeout(r, 200));\n }\n\n if (this.isAlive()) {\n this.child.kill(\"SIGKILL\");\n }\n }\n\n isAlive(): boolean {\n if (!this.child || !this.child.pid) return false;\n try {\n process.kill(this.child.pid, 0);\n return true;\n } catch {\n return false;\n }\n }\n\n getStderr(): string {\n return this.stderrChunks.join(\"\");\n }\n\n private setupStdoutParsing(): void {\n if (!this.child?.stdout) return;\n\n const lineBuffer = new LineBuffer();\n this.child.stdout.pipe(lineBuffer);\n\n lineBuffer.on(\"data\", (line: Buffer | string) => {\n const str = typeof line === \"string\" ? line : line.toString();\n let event: StreamJsonEvent;\n try {\n event = JSON.parse(str) as StreamJsonEvent;\n } catch {\n // 非 JSON 行直接跳过,verbose 模式会输出调试日志\n return;\n }\n\n // 从 system 事件中捕获 Claude 会话 ID 用于后续 resume\n if (event.type === \"system\" && typeof event.session_id === \"string\") {\n this.claudeSessionId = event.session_id;\n }\n\n if (event.type === \"control_request\") {\n // schema parse 失败说明 CLI 协议漂移,调用方必须感知而不是静默吃掉\n const parsed = ControlRequestEventSchema.safeParse(event);\n if (!parsed.success) {\n console.error(\n \"[json-session] control_request shape mismatch; skipping approval\",\n parsed.error.issues.slice(0, 3),\n );\n return;\n }\n this.handleControlRequest(parsed.data);\n return;\n }\n\n this.onEvent?.(event);\n });\n }\n\n private setupStderrCollection(): void {\n if (!this.child?.stderr) return;\n this.child.stderr.on(\"data\", (chunk: Buffer | string) => {\n this.stderrChunks.push(typeof chunk === \"string\" ? chunk : chunk.toString());\n });\n }\n\n private setupExitHandler(): void {\n if (!this.child) return;\n // child 'exit' 触发时, stdout pipe 里可能还堆着最后几行 stream-json (含 'result'\n // event → turn_complete 信号)。直接 fire onExit → caller process.exit(0) 会切断\n // pipe, 这些行永远不解析, session 永卡 WORKING。等 stdout 'end' (所有 chunk 流过\n // LineBuffer) + child 'exit' 都到, 才 fire onExit。\n let stdoutEnded = false;\n let exitCode: number | null = null;\n let exited = false;\n let fired = false;\n const fireOnce = (): void => {\n if (fired || !exited || !stdoutEnded) return;\n fired = true;\n this.onExitCb?.(exitCode ?? 1);\n };\n this.child.stdout?.on(\"end\", () => {\n stdoutEnded = true;\n fireOnce();\n });\n this.child.on(\"exit\", (code: number | null) => {\n exitCode = code;\n exited = true;\n // 兜底: child 异常退出且 stdout 卡住时 'end' 永不到, 1s 后强制 fire 防 session 永挂\n setTimeout(() => {\n stdoutEnded = true;\n fireOnce();\n }, 1000).unref();\n fireOnce();\n });\n }\n\n private handleControlRequest(event: z.infer<typeof ControlRequestEventSchema>): void {\n const requestId = event.request_id;\n const request = event.request;\n\n this.approvalStrategy(request.tool_name, request.input)\n .then((decision) => {\n const response =\n decision.behavior === \"deny\"\n ? {\n type: \"control_response\",\n response: {\n subtype: \"success\",\n request_id: requestId,\n response: {\n behavior: \"deny\",\n message: decision.message ?? \"Tool use denied by default policy.\",\n },\n },\n }\n : {\n type: \"control_response\",\n response: {\n subtype: \"success\",\n request_id: requestId,\n response: {\n behavior: \"allow\",\n updatedInput: {},\n },\n },\n };\n\n this.writeToStdin(JSON.stringify(response));\n })\n .catch((err) => {\n // approvalStrategy 失败时若无应答,claude 会无限等待 control_response 卡死整个 turn。\n // 兜底回 deny 让 turn 继续推进;具体失败原因记到 stderr 由调用方/日志收敛。\n console.error(\n \"[json-session] approval strategy rejected, fallback to deny\",\n requestId,\n err instanceof Error ? err.message : err,\n );\n this.writeToStdin(\n JSON.stringify({\n type: \"control_response\",\n response: {\n subtype: \"success\",\n request_id: requestId,\n response: {\n behavior: \"deny\",\n message: \"Approval strategy failed; denied as fallback.\",\n },\n },\n }),\n );\n });\n }\n\n private writeToStdin(data: string): void {\n // writeQueue 只承诺单调推进:单次写入失败不能让 queue 永久 rejected,否则后续 sendMessage\n // 与 control_response 全部走在 rejected promise 上立即失败,worker 表现为 stdin 静默死锁。\n this.writeQueue = this.writeQueue\n .then(\n () =>\n new Promise<void>((resolve, reject) => {\n if (!this.child?.stdin?.writable) {\n reject(new Error(\"stdin not writable\"));\n return;\n }\n this.child.stdin.write(data + \"\\n\", (err) => {\n if (err) reject(err);\n else resolve();\n });\n }),\n )\n .catch((err) => {\n console.error(\n \"[json-session] writeToStdin failed\",\n err instanceof Error ? err.message : err,\n );\n });\n }\n}\n","import type { Socket } from \"node:net\";\n\n// serve 进程快速 stop+start 时,旧 socket 的 close 事件可能还没触发,新 serve 已经连上来。\n// session-worker 只允许一条活跃的 serveSocket:到达新连接时显式 destroy 旧的,避免两条\n// socket 同时各跑一份 createWorkerReader / pendingApprovals 重发逻辑造成状态不一致。\nexport function takeoverServeSocket(prev: Socket | null, next: Socket): Socket {\n if (prev && prev !== next) {\n try {\n prev.destroy();\n } catch {\n // 旧 socket 可能已半关闭\n }\n }\n return next;\n}\n"],"mappings":"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","names":["whitelist","forwardToRelay"]}
|