@dev-anywhere/proxy 0.1.9 → 0.2.1
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
|
@@ -0,0 +1,1032 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ../../packages/shared/dist/schemas/envelope.js
|
|
4
|
+
import { z as z6 } from "zod";
|
|
5
|
+
|
|
6
|
+
// ../../packages/shared/dist/schemas/id.js
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
var MAX_ID_LENGTH = 256;
|
|
9
|
+
var IdSchema = z.string().min(1).max(MAX_ID_LENGTH);
|
|
10
|
+
|
|
11
|
+
// ../../packages/shared/dist/schemas/chat.js
|
|
12
|
+
import { z as z2 } from "zod";
|
|
13
|
+
var UserInputPayloadSchema = z2.object({
|
|
14
|
+
text: z2.string().min(1),
|
|
15
|
+
messageId: z2.string().min(1).optional()
|
|
16
|
+
});
|
|
17
|
+
var AssistantMessagePayloadSchema = z2.object({
|
|
18
|
+
text: z2.string(),
|
|
19
|
+
isPartial: z2.boolean()
|
|
20
|
+
});
|
|
21
|
+
var ThinkingPayloadSchema = z2.object({
|
|
22
|
+
text: z2.string()
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// ../../packages/shared/dist/schemas/tool.js
|
|
26
|
+
import { z as z3 } from "zod";
|
|
27
|
+
var ToolUseRequestPayloadSchema = z3.object({
|
|
28
|
+
toolName: z3.string(),
|
|
29
|
+
toolId: IdSchema,
|
|
30
|
+
parameters: z3.record(z3.string(), z3.unknown())
|
|
31
|
+
});
|
|
32
|
+
var ToolApprovePayloadSchema = z3.object({
|
|
33
|
+
toolId: IdSchema,
|
|
34
|
+
whitelistTool: z3.boolean().optional()
|
|
35
|
+
});
|
|
36
|
+
var ToolDenyPayloadSchema = z3.object({
|
|
37
|
+
toolId: IdSchema,
|
|
38
|
+
reason: z3.string().optional()
|
|
39
|
+
});
|
|
40
|
+
var ToolResultPayloadSchema = z3.object({
|
|
41
|
+
toolId: IdSchema,
|
|
42
|
+
result: z3.unknown(),
|
|
43
|
+
isError: z3.boolean()
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// ../../packages/shared/dist/schemas/session.js
|
|
47
|
+
import { z as z4 } from "zod";
|
|
48
|
+
|
|
49
|
+
// ../../packages/shared/dist/constants/enums.js
|
|
50
|
+
var providerValues = ["claude", "codex"];
|
|
51
|
+
var ptyOwnerValues = ["local-terminal", "proxy-hosted"];
|
|
52
|
+
var sessionModeValues = ["pty", "json"];
|
|
53
|
+
|
|
54
|
+
// ../../packages/shared/dist/constants/pty.js
|
|
55
|
+
var PtySemanticState = {
|
|
56
|
+
WORKING: "working",
|
|
57
|
+
TURN_COMPLETE: "turn_complete",
|
|
58
|
+
APPROVAL_WAIT: "approval_wait"
|
|
59
|
+
};
|
|
60
|
+
var ptySemanticStateValues = [
|
|
61
|
+
PtySemanticState.WORKING,
|
|
62
|
+
PtySemanticState.TURN_COMPLETE,
|
|
63
|
+
PtySemanticState.APPROVAL_WAIT
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
// ../../packages/shared/dist/schemas/session.js
|
|
67
|
+
var sessionStateValues = [
|
|
68
|
+
"idle",
|
|
69
|
+
"working",
|
|
70
|
+
"waiting_approval",
|
|
71
|
+
"error",
|
|
72
|
+
"terminated"
|
|
73
|
+
];
|
|
74
|
+
var agentStatusPhaseValues = [
|
|
75
|
+
"idle",
|
|
76
|
+
"thinking",
|
|
77
|
+
"tool_use",
|
|
78
|
+
"outputting",
|
|
79
|
+
"waiting_permission",
|
|
80
|
+
"error"
|
|
81
|
+
];
|
|
82
|
+
var SessionInfoSchema = z4.object({
|
|
83
|
+
sessionId: IdSchema,
|
|
84
|
+
name: z4.string().optional(),
|
|
85
|
+
state: z4.enum(sessionStateValues),
|
|
86
|
+
mode: z4.enum(sessionModeValues).optional(),
|
|
87
|
+
provider: z4.enum(providerValues),
|
|
88
|
+
// PTY 尺寸所有权:
|
|
89
|
+
// - local-terminal: 本地 terminal 进程持有真实 PTY,Web 只按原始 cols/rows 展示
|
|
90
|
+
// - proxy-hosted: serve 内托管 PTY,Web 可按视口请求 resize
|
|
91
|
+
ptyOwner: z4.enum(ptyOwnerValues).optional(),
|
|
92
|
+
lastActive: z4.number().optional()
|
|
93
|
+
});
|
|
94
|
+
var SessionCreatePayloadSchema = z4.object({
|
|
95
|
+
name: z4.string().optional(),
|
|
96
|
+
cwd: z4.string().optional(),
|
|
97
|
+
streamDelta: z4.boolean().optional()
|
|
98
|
+
});
|
|
99
|
+
var SessionListPayloadSchema = z4.object({
|
|
100
|
+
sessions: z4.array(SessionInfoSchema)
|
|
101
|
+
});
|
|
102
|
+
var SessionSwitchPayloadSchema = z4.object({
|
|
103
|
+
sessionId: IdSchema
|
|
104
|
+
});
|
|
105
|
+
var SessionTerminatePayloadSchema = z4.object({
|
|
106
|
+
sessionId: IdSchema
|
|
107
|
+
});
|
|
108
|
+
var SessionStatusPayloadSchema = z4.object({
|
|
109
|
+
sessionId: IdSchema,
|
|
110
|
+
state: z4.enum(sessionStateValues),
|
|
111
|
+
lastActive: z4.number()
|
|
112
|
+
});
|
|
113
|
+
var PtyStatePayloadSchema = z4.object({
|
|
114
|
+
state: z4.enum(ptySemanticStateValues),
|
|
115
|
+
title: z4.string().optional(),
|
|
116
|
+
tool: z4.string().optional()
|
|
117
|
+
});
|
|
118
|
+
var AgentStatusPayloadSchema = z4.object({
|
|
119
|
+
provider: z4.enum(providerValues),
|
|
120
|
+
phase: z4.enum(agentStatusPhaseValues),
|
|
121
|
+
seq: z4.number().int().nonnegative(),
|
|
122
|
+
updatedAt: z4.number(),
|
|
123
|
+
toolName: z4.string().optional(),
|
|
124
|
+
toolInput: z4.record(z4.string(), z4.unknown()).optional(),
|
|
125
|
+
permissionRequest: z4.object({
|
|
126
|
+
requestId: IdSchema,
|
|
127
|
+
toolName: z4.string(),
|
|
128
|
+
input: z4.record(z4.string(), z4.unknown())
|
|
129
|
+
}).optional(),
|
|
130
|
+
permissionResolution: z4.object({
|
|
131
|
+
requestId: IdSchema,
|
|
132
|
+
outcome: z4.enum(["allow", "deny"])
|
|
133
|
+
}).optional(),
|
|
134
|
+
summary: z4.string().optional()
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// ../../packages/shared/dist/schemas/system.js
|
|
138
|
+
import { z as z5 } from "zod";
|
|
139
|
+
var HeartbeatPayloadSchema = z5.object({});
|
|
140
|
+
var AuthPayloadSchema = z5.object({
|
|
141
|
+
pairingCode: z5.string().optional(),
|
|
142
|
+
token: z5.string().optional()
|
|
143
|
+
});
|
|
144
|
+
var SyncRequestPayloadSchema = z5.object({
|
|
145
|
+
lastSeq: z5.number().int().nonnegative()
|
|
146
|
+
});
|
|
147
|
+
var SyncResponsePayloadSchema = z5.object({
|
|
148
|
+
messages: z5.array(z5.record(z5.string(), z5.unknown()))
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// ../../packages/shared/dist/schemas/envelope.js
|
|
152
|
+
var BaseEnvelopeFields = {
|
|
153
|
+
seq: z6.number().int().nonnegative(),
|
|
154
|
+
timestamp: z6.number(),
|
|
155
|
+
source: z6.enum(["proxy", "client"]),
|
|
156
|
+
version: z6.string()
|
|
157
|
+
};
|
|
158
|
+
var SessionedEnvelopeFields = {
|
|
159
|
+
...BaseEnvelopeFields,
|
|
160
|
+
sessionId: IdSchema
|
|
161
|
+
};
|
|
162
|
+
var MessageEnvelopeSchema = z6.discriminatedUnion("type", [
|
|
163
|
+
// chat (3)
|
|
164
|
+
z6.object({
|
|
165
|
+
...SessionedEnvelopeFields,
|
|
166
|
+
type: z6.literal("user_input"),
|
|
167
|
+
payload: UserInputPayloadSchema
|
|
168
|
+
}),
|
|
169
|
+
z6.object({
|
|
170
|
+
...SessionedEnvelopeFields,
|
|
171
|
+
type: z6.literal("assistant_message"),
|
|
172
|
+
payload: AssistantMessagePayloadSchema
|
|
173
|
+
}),
|
|
174
|
+
z6.object({
|
|
175
|
+
...SessionedEnvelopeFields,
|
|
176
|
+
type: z6.literal("thinking"),
|
|
177
|
+
payload: ThinkingPayloadSchema
|
|
178
|
+
}),
|
|
179
|
+
// tool (4): 工具审批决策属于 relay control,不进入会话消息信封。
|
|
180
|
+
// tool_use_request: 审批流请求(proxy → client),toolId 是 approval requestId
|
|
181
|
+
z6.object({
|
|
182
|
+
...SessionedEnvelopeFields,
|
|
183
|
+
type: z6.literal("tool_use_request"),
|
|
184
|
+
payload: ToolUseRequestPayloadSchema
|
|
185
|
+
}),
|
|
186
|
+
// tool_result: 工具执行结果(proxy → client),toolId 对应 assistant_tool_use / tool_use_request 的 toolId
|
|
187
|
+
z6.object({
|
|
188
|
+
...SessionedEnvelopeFields,
|
|
189
|
+
type: z6.literal("tool_result"),
|
|
190
|
+
payload: ToolResultPayloadSchema
|
|
191
|
+
}),
|
|
192
|
+
// assistant_tool_use: 纯展示型工具调用(proxy → client),区别于 tool_use_request 无审批语义
|
|
193
|
+
// payload 结构复用 ToolUseRequestPayloadSchema;toolId 是 Claude 分配的 tool_use id
|
|
194
|
+
z6.object({
|
|
195
|
+
...SessionedEnvelopeFields,
|
|
196
|
+
type: z6.literal("assistant_tool_use"),
|
|
197
|
+
payload: ToolUseRequestPayloadSchema
|
|
198
|
+
}),
|
|
199
|
+
// session (5)
|
|
200
|
+
z6.object({
|
|
201
|
+
...SessionedEnvelopeFields,
|
|
202
|
+
type: z6.literal("session_create"),
|
|
203
|
+
payload: SessionCreatePayloadSchema
|
|
204
|
+
}),
|
|
205
|
+
// session_list 是全局广播 (列出所有 session), 不绑定具体 sessionId, 不携带该字段。
|
|
206
|
+
z6.object({
|
|
207
|
+
...BaseEnvelopeFields,
|
|
208
|
+
type: z6.literal("session_list"),
|
|
209
|
+
payload: SessionListPayloadSchema
|
|
210
|
+
}),
|
|
211
|
+
z6.object({
|
|
212
|
+
...SessionedEnvelopeFields,
|
|
213
|
+
type: z6.literal("session_switch"),
|
|
214
|
+
payload: SessionSwitchPayloadSchema
|
|
215
|
+
}),
|
|
216
|
+
z6.object({
|
|
217
|
+
...SessionedEnvelopeFields,
|
|
218
|
+
type: z6.literal("session_terminate"),
|
|
219
|
+
payload: SessionTerminatePayloadSchema
|
|
220
|
+
}),
|
|
221
|
+
z6.object({
|
|
222
|
+
...SessionedEnvelopeFields,
|
|
223
|
+
type: z6.literal("session_status"),
|
|
224
|
+
payload: SessionStatusPayloadSchema
|
|
225
|
+
}),
|
|
226
|
+
// system (5): 心跳 / 认证 / 同步——全局, 无 sessionId
|
|
227
|
+
z6.object({
|
|
228
|
+
...BaseEnvelopeFields,
|
|
229
|
+
type: z6.literal("heartbeat"),
|
|
230
|
+
payload: HeartbeatPayloadSchema
|
|
231
|
+
}),
|
|
232
|
+
z6.object({
|
|
233
|
+
...BaseEnvelopeFields,
|
|
234
|
+
type: z6.literal("auth"),
|
|
235
|
+
payload: AuthPayloadSchema
|
|
236
|
+
}),
|
|
237
|
+
z6.object({
|
|
238
|
+
...BaseEnvelopeFields,
|
|
239
|
+
type: z6.literal("sync_request"),
|
|
240
|
+
payload: SyncRequestPayloadSchema
|
|
241
|
+
}),
|
|
242
|
+
z6.object({
|
|
243
|
+
...BaseEnvelopeFields,
|
|
244
|
+
type: z6.literal("sync_response"),
|
|
245
|
+
payload: SyncResponsePayloadSchema
|
|
246
|
+
})
|
|
247
|
+
]);
|
|
248
|
+
|
|
249
|
+
// ../../packages/shared/dist/builders/index.js
|
|
250
|
+
function buildMessage(type, sessionId, seq, payload, source) {
|
|
251
|
+
const envelope = {
|
|
252
|
+
seq,
|
|
253
|
+
...sessionId !== null ? { sessionId } : {},
|
|
254
|
+
type,
|
|
255
|
+
payload,
|
|
256
|
+
timestamp: Date.now(),
|
|
257
|
+
source,
|
|
258
|
+
version: "1.0"
|
|
259
|
+
};
|
|
260
|
+
return MessageEnvelopeSchema.parse(envelope);
|
|
261
|
+
}
|
|
262
|
+
function serializeControl(msg) {
|
|
263
|
+
return JSON.stringify(msg);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ../../packages/shared/dist/state-machine.js
|
|
267
|
+
function computeAbsorbingSet(transitions) {
|
|
268
|
+
const absorbing = /* @__PURE__ */ new Set();
|
|
269
|
+
const entries = Object.entries(transitions);
|
|
270
|
+
for (const [s, outs] of entries) {
|
|
271
|
+
if (outs.length === 0)
|
|
272
|
+
absorbing.add(s);
|
|
273
|
+
}
|
|
274
|
+
let changed = true;
|
|
275
|
+
while (changed) {
|
|
276
|
+
changed = false;
|
|
277
|
+
for (const [s, outs] of entries) {
|
|
278
|
+
if (absorbing.has(s))
|
|
279
|
+
continue;
|
|
280
|
+
if (outs.length > 0 && outs.every((t) => absorbing.has(t))) {
|
|
281
|
+
absorbing.add(s);
|
|
282
|
+
changed = true;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return absorbing;
|
|
287
|
+
}
|
|
288
|
+
function createFSM(def) {
|
|
289
|
+
let state = def.initial;
|
|
290
|
+
const absorbing = computeAbsorbingSet(def.transitions);
|
|
291
|
+
const tryTransitionTo = (to) => {
|
|
292
|
+
const allowed = def.transitions[state];
|
|
293
|
+
if (!allowed?.includes(to)) {
|
|
294
|
+
def.onRejected?.(state, to, absorbing.has(state));
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
const from = state;
|
|
298
|
+
state = to;
|
|
299
|
+
def.onTransition?.(from, to);
|
|
300
|
+
return true;
|
|
301
|
+
};
|
|
302
|
+
return {
|
|
303
|
+
current: () => state,
|
|
304
|
+
is: (s) => state === s,
|
|
305
|
+
isIn: (ss) => ss.includes(state),
|
|
306
|
+
canTransitionTo: (to) => def.transitions[state]?.includes(to) ?? false,
|
|
307
|
+
transitionTo: (to) => {
|
|
308
|
+
if (!tryTransitionTo(to)) {
|
|
309
|
+
throw new Error(`Invalid FSM transition: ${state} -> ${to}`);
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
tryTransitionTo,
|
|
313
|
+
isInAbsorbingState: () => absorbing.has(state)
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
function defineFSM(transitions) {
|
|
317
|
+
const absorbing = computeAbsorbingSet(transitions);
|
|
318
|
+
return {
|
|
319
|
+
canTransition: (from, to) => transitions[from]?.includes(to) ?? false,
|
|
320
|
+
isAbsorbing: (state) => absorbing.has(state)
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ../../packages/shared/dist/schemas/relay-control.js
|
|
325
|
+
import { z as z7 } from "zod";
|
|
326
|
+
|
|
327
|
+
// ../../packages/shared/dist/constants/relay-errors.js
|
|
328
|
+
var RelayErrorCode = {
|
|
329
|
+
NOT_REGISTERED: "NOT_REGISTERED",
|
|
330
|
+
NOT_BOUND: "NOT_BOUND",
|
|
331
|
+
PROXY_OFFLINE: "PROXY_OFFLINE",
|
|
332
|
+
INVALID_MESSAGE: "INVALID_MESSAGE",
|
|
333
|
+
UNSUPPORTED: "UNSUPPORTED",
|
|
334
|
+
INVALID_RANGE: "INVALID_RANGE"
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// ../../packages/shared/dist/constants/control-errors.js
|
|
338
|
+
var ControlErrorCode = {
|
|
339
|
+
INVALID_PATH: "INVALID_PATH",
|
|
340
|
+
PATH_NOT_FOUND: "PATH_NOT_FOUND",
|
|
341
|
+
PATH_NOT_DIRECTORY: "PATH_NOT_DIRECTORY",
|
|
342
|
+
PATH_ACCESS_DENIED: "PATH_ACCESS_DENIED",
|
|
343
|
+
PROXY_OFFLINE: "PROXY_OFFLINE",
|
|
344
|
+
SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
|
|
345
|
+
PROVIDER_UNSUPPORTED: "PROVIDER_UNSUPPORTED",
|
|
346
|
+
WORKER_START_FAILED: "WORKER_START_FAILED",
|
|
347
|
+
PROCESS_START_FAILED: "PROCESS_START_FAILED",
|
|
348
|
+
UNKNOWN: "UNKNOWN"
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// ../../packages/shared/dist/schemas/relay-control.js
|
|
352
|
+
var ProxyInfoSchema = z7.object({
|
|
353
|
+
proxyId: IdSchema,
|
|
354
|
+
name: z7.string().optional(),
|
|
355
|
+
online: z7.boolean(),
|
|
356
|
+
sessions: z7.array(z7.string()).optional()
|
|
357
|
+
});
|
|
358
|
+
var AgentCliAvailabilitySchema = z7.object({
|
|
359
|
+
available: z7.boolean(),
|
|
360
|
+
command: z7.string().optional(),
|
|
361
|
+
error: z7.string().optional(),
|
|
362
|
+
suggestions: z7.array(z7.string()).optional()
|
|
363
|
+
});
|
|
364
|
+
var AgentCliStatusSchema = z7.object({
|
|
365
|
+
claude: AgentCliAvailabilitySchema,
|
|
366
|
+
codex: AgentCliAvailabilitySchema
|
|
367
|
+
});
|
|
368
|
+
var DirEntrySchema = z7.object({ name: z7.string(), isDir: z7.boolean() });
|
|
369
|
+
var FileTreeGroupSchema = z7.object({
|
|
370
|
+
path: z7.string(),
|
|
371
|
+
entries: z7.array(DirEntrySchema)
|
|
372
|
+
});
|
|
373
|
+
var CommandEntrySchema = z7.object({
|
|
374
|
+
name: z7.string(),
|
|
375
|
+
description: z7.string(),
|
|
376
|
+
argumentHint: z7.string().optional(),
|
|
377
|
+
source: z7.string()
|
|
378
|
+
});
|
|
379
|
+
var HistorySessionSchema = z7.object({
|
|
380
|
+
id: z7.string(),
|
|
381
|
+
title: z7.string(),
|
|
382
|
+
projectDir: z7.string(),
|
|
383
|
+
updatedAt: z7.number(),
|
|
384
|
+
provider: z7.enum(providerValues).optional()
|
|
385
|
+
});
|
|
386
|
+
var SessionHistoryMessageSchema = z7.object({
|
|
387
|
+
role: z7.enum(["user", "assistant"]),
|
|
388
|
+
text: z7.string(),
|
|
389
|
+
timestamp: z7.number().optional(),
|
|
390
|
+
cursor: z7.string().optional()
|
|
391
|
+
});
|
|
392
|
+
var RequestIdShape = { requestId: IdSchema.optional() };
|
|
393
|
+
var ControlErrorCodeSchema = z7.enum(Object.values(ControlErrorCode));
|
|
394
|
+
var RequestErrorShape = {
|
|
395
|
+
error: z7.string().optional(),
|
|
396
|
+
errorCode: ControlErrorCodeSchema.optional()
|
|
397
|
+
};
|
|
398
|
+
var ClipboardImageMimeTypeSchema = z7.enum(["image/png", "image/jpeg", "image/webp", "image/gif"]);
|
|
399
|
+
function control(type, shape, directions) {
|
|
400
|
+
return {
|
|
401
|
+
type,
|
|
402
|
+
directions: new Set(Array.isArray(directions) ? directions : directions ? [directions] : []),
|
|
403
|
+
schema: z7.object({
|
|
404
|
+
type: z7.literal(type),
|
|
405
|
+
...shape ?? {}
|
|
406
|
+
})
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
var relayControlDefinitions = [
|
|
410
|
+
control("proxy_register", {
|
|
411
|
+
proxyId: IdSchema,
|
|
412
|
+
name: z7.string().optional()
|
|
413
|
+
}),
|
|
414
|
+
control("proxy_register_response", {
|
|
415
|
+
status: z7.enum(["new", "reconnected"])
|
|
416
|
+
}),
|
|
417
|
+
control("proxy_list_request", RequestIdShape),
|
|
418
|
+
control("proxy_list_response", {
|
|
419
|
+
...RequestIdShape,
|
|
420
|
+
proxies: z7.array(ProxyInfoSchema)
|
|
421
|
+
}),
|
|
422
|
+
control("proxy_select", { ...RequestIdShape, proxyId: IdSchema }),
|
|
423
|
+
control("proxy_select_response", {
|
|
424
|
+
...RequestIdShape,
|
|
425
|
+
success: z7.boolean(),
|
|
426
|
+
proxyId: IdSchema.optional(),
|
|
427
|
+
...RequestErrorShape
|
|
428
|
+
}),
|
|
429
|
+
control("relay_error", {
|
|
430
|
+
code: z7.enum(Object.values(RelayErrorCode)),
|
|
431
|
+
message: z7.string()
|
|
432
|
+
}),
|
|
433
|
+
// 客户端注册协议
|
|
434
|
+
control("client_register", {
|
|
435
|
+
clientId: IdSchema
|
|
436
|
+
}),
|
|
437
|
+
control("client_register_response", {
|
|
438
|
+
status: z7.enum(["restored", "proxy_offline", "new"]),
|
|
439
|
+
proxyId: IdSchema.optional()
|
|
440
|
+
}),
|
|
441
|
+
// Proxy 离线通知
|
|
442
|
+
control("proxy_offline", {
|
|
443
|
+
proxyId: IdSchema
|
|
444
|
+
}),
|
|
445
|
+
// Proxy 主动断开,relay 立即清理资源
|
|
446
|
+
control("proxy_disconnect", {
|
|
447
|
+
proxyId: IdSchema
|
|
448
|
+
}),
|
|
449
|
+
// Proxy 重连后通知 client 恢复
|
|
450
|
+
control("proxy_online", {
|
|
451
|
+
proxyId: IdSchema
|
|
452
|
+
}),
|
|
453
|
+
// 目录列表请求与响应
|
|
454
|
+
control("dir_list_request", {
|
|
455
|
+
proxyId: IdSchema.optional(),
|
|
456
|
+
...RequestIdShape,
|
|
457
|
+
path: z7.string()
|
|
458
|
+
}, "client_to_proxy"),
|
|
459
|
+
control("dir_list_response", { ...RequestIdShape, ...RequestErrorShape, entries: z7.array(DirEntrySchema), path: z7.string() }, "proxy_to_client"),
|
|
460
|
+
// 目录创建请求与响应
|
|
461
|
+
control("dir_create_request", { ...RequestIdShape, path: z7.string() }, "client_to_proxy"),
|
|
462
|
+
control("dir_create_response", {
|
|
463
|
+
...RequestIdShape,
|
|
464
|
+
...RequestErrorShape,
|
|
465
|
+
path: z7.string(),
|
|
466
|
+
success: z7.boolean()
|
|
467
|
+
}, "proxy_to_client"),
|
|
468
|
+
// 命令列表推送,proxy 将可用命令列表推给 client
|
|
469
|
+
control("command_list_push", { commands: z7.array(CommandEntrySchema) }, "proxy_to_client"),
|
|
470
|
+
// 文件树推送: 按目录分组, 首组 path 即为 session cwd
|
|
471
|
+
// 前端直接把每组写入 tree[path], 与 dir_list_response 共享 cache slot
|
|
472
|
+
control("file_tree_push", {
|
|
473
|
+
groups: z7.array(FileTreeGroupSchema)
|
|
474
|
+
}, "proxy_to_client"),
|
|
475
|
+
// 会话列表请求与权限模式变更
|
|
476
|
+
control("session_list", void 0, ["client_to_proxy", "proxy_to_client"]),
|
|
477
|
+
control("permission_mode_change", {
|
|
478
|
+
mode: z7.enum(["default", "auto_accept", "plan"]),
|
|
479
|
+
// sessionId 可选:传入时 proxy 按该会话的 mode 分叉(PTY 发 Tab ANSI),未传走全局日志行为
|
|
480
|
+
sessionId: IdSchema.optional()
|
|
481
|
+
}, "client_to_proxy"),
|
|
482
|
+
// 会话历史浏览
|
|
483
|
+
control("session_history_request", RequestIdShape, "client_to_proxy"),
|
|
484
|
+
control("session_history_response", { ...RequestIdShape, sessions: z7.array(HistorySessionSchema) }, "proxy_to_client"),
|
|
485
|
+
// PTY 语义状态,从 Envelope 迁移到 Control 层
|
|
486
|
+
control("pty_state", { sessionId: IdSchema, payload: PtyStatePayloadSchema }, "proxy_to_client"),
|
|
487
|
+
// Provider 语义状态,来自 Claude/Codex hook 等结构化事件,不从 PTY 字节推断
|
|
488
|
+
control("agent_status", { sessionId: IdSchema, payload: AgentStatusPayloadSchema }, "proxy_to_client"),
|
|
489
|
+
// 终端标题变化,proxy -> client
|
|
490
|
+
control("terminal_title", { sessionId: IdSchema, title: z7.string() }, "proxy_to_client"),
|
|
491
|
+
// 终端尺寸变化,proxy -> client
|
|
492
|
+
control("terminal_resize", { sessionId: IdSchema, cols: z7.number().int().positive(), rows: z7.number().int().positive() }, "proxy_to_client"),
|
|
493
|
+
control("terminal_resize_request", { sessionId: IdSchema, cols: z7.number().int().positive(), rows: z7.number().int().positive() }, "client_to_proxy"),
|
|
494
|
+
// 远程终止 JSON 会话,client -> proxy
|
|
495
|
+
control("session_terminate", { sessionId: IdSchema }, "client_to_proxy"),
|
|
496
|
+
// 中断当前 turn,client -> proxy,SIGINT 到 worker 进程让 claude CLI abort 当前流
|
|
497
|
+
control("session_worker_abort", { sessionId: IdSchema }, "client_to_proxy"),
|
|
498
|
+
// turn 完成信号,proxy -> client,对应 claude stream-json 的 result 事件
|
|
499
|
+
control("turn_result", {
|
|
500
|
+
sessionId: IdSchema,
|
|
501
|
+
success: z7.boolean(),
|
|
502
|
+
isError: z7.boolean(),
|
|
503
|
+
// stream-json result.result 是本轮最终文本。assistant_message 流丢失或 CLI 未发增量时,
|
|
504
|
+
// Web 用它作为 JSON 模式兜底展示,避免 turn 已结束但界面空白。
|
|
505
|
+
result: z7.string().optional()
|
|
506
|
+
}, "proxy_to_client"),
|
|
507
|
+
// 客户端发送到 PTY 的原始字节(ANSI 序列),不追加换行
|
|
508
|
+
control("remote_input_raw", { sessionId: IdSchema, data: z7.string() }, "client_to_proxy"),
|
|
509
|
+
control("clipboard_image_upload", {
|
|
510
|
+
...RequestIdShape,
|
|
511
|
+
sessionId: IdSchema,
|
|
512
|
+
mimeType: ClipboardImageMimeTypeSchema,
|
|
513
|
+
dataBase64: z7.string().min(1),
|
|
514
|
+
fileName: z7.string().optional()
|
|
515
|
+
}, "client_to_proxy"),
|
|
516
|
+
control("clipboard_image_upload_response", {
|
|
517
|
+
...RequestIdShape,
|
|
518
|
+
...RequestErrorShape,
|
|
519
|
+
sessionId: IdSchema,
|
|
520
|
+
success: z7.boolean(),
|
|
521
|
+
// success=false 时 proxy 没有有效 path 可填;保持 optional 以避免占位空字符串通过校验。
|
|
522
|
+
path: z7.string().optional()
|
|
523
|
+
}, "proxy_to_client"),
|
|
524
|
+
control("image_preview_request", {
|
|
525
|
+
...RequestIdShape,
|
|
526
|
+
sessionId: IdSchema,
|
|
527
|
+
path: z7.string().min(1)
|
|
528
|
+
}, "client_to_proxy"),
|
|
529
|
+
control("image_preview_response", {
|
|
530
|
+
...RequestIdShape,
|
|
531
|
+
...RequestErrorShape,
|
|
532
|
+
sessionId: IdSchema,
|
|
533
|
+
success: z7.boolean(),
|
|
534
|
+
// 同 clipboard_image_upload_response:失败时 proxy 不一定有路径。
|
|
535
|
+
path: z7.string().optional(),
|
|
536
|
+
mimeType: ClipboardImageMimeTypeSchema.optional(),
|
|
537
|
+
dataBase64: z7.string().optional(),
|
|
538
|
+
size: z7.number().int().nonnegative().optional()
|
|
539
|
+
}, "proxy_to_client"),
|
|
540
|
+
// 客户端询问 proxy 的环境信息 (home 路径等), client -> proxy -> response
|
|
541
|
+
// FilePathPicker 用 homePath 作为 select 模式下的默认起点, 新建会话时打开即可浏览
|
|
542
|
+
control("proxy_info_request", RequestIdShape, "client_to_proxy"),
|
|
543
|
+
control("proxy_info", { ...RequestIdShape, homePath: z7.string(), agentCli: AgentCliStatusSchema }, "proxy_to_client"),
|
|
544
|
+
control("agent_cli_config_update", { ...RequestIdShape, provider: z7.enum(providerValues), path: z7.string().min(1) }, "client_to_proxy"),
|
|
545
|
+
control("agent_cli_config_update_response", {
|
|
546
|
+
...RequestIdShape,
|
|
547
|
+
provider: z7.enum(providerValues),
|
|
548
|
+
agentCli: AgentCliStatusSchema.optional(),
|
|
549
|
+
...RequestErrorShape
|
|
550
|
+
}, "proxy_to_client"),
|
|
551
|
+
// 远程创建 JSON 会话,client -> proxy -> response
|
|
552
|
+
control("session_create", {
|
|
553
|
+
...RequestIdShape,
|
|
554
|
+
cwd: z7.string(),
|
|
555
|
+
provider: z7.enum(providerValues),
|
|
556
|
+
mode: z7.enum(sessionModeValues).optional(),
|
|
557
|
+
resumeSessionId: z7.string().optional(),
|
|
558
|
+
// 透传给 claude CLI 的 --permission-mode, undefined 时 proxy 兜底为 "default"
|
|
559
|
+
permissionMode: z7.enum(["default", "auto", "acceptEdits", "plan", "bypassPermissions", "dontAsk"]).optional()
|
|
560
|
+
}, "client_to_proxy"),
|
|
561
|
+
control("session_create_response", {
|
|
562
|
+
...RequestIdShape,
|
|
563
|
+
// 失败路径只送 errorCode/error, sessionId 此时无语义。成功路径才有 id。
|
|
564
|
+
sessionId: IdSchema.optional(),
|
|
565
|
+
mode: z7.enum(sessionModeValues).optional(),
|
|
566
|
+
provider: z7.enum(providerValues).optional(),
|
|
567
|
+
ptyOwner: z7.enum(ptyOwnerValues).optional(),
|
|
568
|
+
...RequestErrorShape
|
|
569
|
+
}, "proxy_to_client"),
|
|
570
|
+
// 客户端请求会话历史消息,client -> proxy
|
|
571
|
+
control("session_messages_request", {
|
|
572
|
+
...RequestIdShape,
|
|
573
|
+
sessionId: IdSchema,
|
|
574
|
+
limit: z7.number().int().min(1).max(200).optional(),
|
|
575
|
+
before: z7.string().optional()
|
|
576
|
+
}, "client_to_proxy"),
|
|
577
|
+
// 客户端请求会话资源(命令列表 + 文件树),client -> proxy
|
|
578
|
+
control("session_resources_request", { ...RequestIdShape, sessionId: IdSchema }, "client_to_proxy"),
|
|
579
|
+
control("session_resources_response", {
|
|
580
|
+
...RequestIdShape,
|
|
581
|
+
...RequestErrorShape,
|
|
582
|
+
sessionId: IdSchema,
|
|
583
|
+
commands: z7.array(CommandEntrySchema),
|
|
584
|
+
groups: z7.array(FileTreeGroupSchema)
|
|
585
|
+
}, "proxy_to_client"),
|
|
586
|
+
// 客户端请求当前 provider 语义状态;不经 relay 缓存,由 proxy 返回当前值
|
|
587
|
+
control("agent_status_request", { ...RequestIdShape, sessionId: IdSchema.optional() }, "client_to_proxy"),
|
|
588
|
+
control("agent_status_response", {
|
|
589
|
+
...RequestIdShape,
|
|
590
|
+
statuses: z7.array(z7.object({ sessionId: IdSchema, payload: AgentStatusPayloadSchema }))
|
|
591
|
+
}, "proxy_to_client"),
|
|
592
|
+
// 客户端确认已收到审批请求;proxy 只记录送达状态,不把它当成用户决策
|
|
593
|
+
control("permission_request_delivered", { sessionId: IdSchema, requestId: IdSchema }, "client_to_proxy"),
|
|
594
|
+
control("tool_approve", { sessionId: IdSchema, payload: ToolApprovePayloadSchema }, "client_to_proxy"),
|
|
595
|
+
control("tool_deny", { sessionId: IdSchema, payload: ToolDenyPayloadSchema }, "client_to_proxy"),
|
|
596
|
+
// proxy 确认用户决策已进入 provider/worker 路径;web 用它更新审批卡片状态
|
|
597
|
+
control("permission_decision_result", {
|
|
598
|
+
sessionId: IdSchema,
|
|
599
|
+
requestId: IdSchema,
|
|
600
|
+
outcome: z7.enum(["allow", "deny"]),
|
|
601
|
+
delivered: z7.boolean(),
|
|
602
|
+
message: z7.string().optional()
|
|
603
|
+
}, "proxy_to_client"),
|
|
604
|
+
// proxy 推送当前 pending 的工具审批列表,client 据此恢复审批卡片
|
|
605
|
+
control("pending_approvals_push", {
|
|
606
|
+
sessionId: IdSchema,
|
|
607
|
+
approvals: z7.array(z7.object({
|
|
608
|
+
requestId: IdSchema,
|
|
609
|
+
toolName: z7.string(),
|
|
610
|
+
input: z7.record(z7.string(), z7.unknown())
|
|
611
|
+
}))
|
|
612
|
+
}, "proxy_to_client"),
|
|
613
|
+
// 恢复会话时推送历史消息,proxy -> client
|
|
614
|
+
control("session_history_messages", {
|
|
615
|
+
...RequestIdShape,
|
|
616
|
+
sessionId: IdSchema,
|
|
617
|
+
before: z7.string().optional(),
|
|
618
|
+
messages: z7.array(SessionHistoryMessageSchema),
|
|
619
|
+
hasMore: z7.boolean().optional(),
|
|
620
|
+
nextBefore: z7.string().optional()
|
|
621
|
+
}, "proxy_to_client"),
|
|
622
|
+
// proxy 重连后同步活跃 session 列表给 relay。session_sync 由 relay 自消费(更新 proxy-session
|
|
623
|
+
// 关联)不转发给 client,因此**没有** direction 标注——RelayControlDirection 只描述转发流。
|
|
624
|
+
control("session_sync", {
|
|
625
|
+
sessions: z7.array(z7.object({
|
|
626
|
+
id: z7.string(),
|
|
627
|
+
mode: z7.enum(sessionModeValues),
|
|
628
|
+
provider: z7.enum(providerValues),
|
|
629
|
+
ptyOwner: z7.enum(ptyOwnerValues).optional(),
|
|
630
|
+
state: z7.enum(sessionStateValues)
|
|
631
|
+
}))
|
|
632
|
+
}),
|
|
633
|
+
// PTY 会话订阅,client -> proxy,触发 terminal serialize() 返回当前状态
|
|
634
|
+
control("session_subscribe", { sessionId: IdSchema, requestId: IdSchema.optional() }, "client_to_proxy"),
|
|
635
|
+
// PTY 会话快照,proxy -> client,serialize() 的全量终端状态
|
|
636
|
+
control("session_snapshot", {
|
|
637
|
+
sessionId: IdSchema,
|
|
638
|
+
cols: z7.number().int().positive(),
|
|
639
|
+
rows: z7.number().int().positive(),
|
|
640
|
+
data: z7.string(),
|
|
641
|
+
outputSeq: z7.number().int().nonnegative(),
|
|
642
|
+
requestId: IdSchema.optional()
|
|
643
|
+
}, "proxy_to_client")
|
|
644
|
+
];
|
|
645
|
+
var relayControlSchemas = relayControlDefinitions.map((definition) => definition.schema);
|
|
646
|
+
var RelayControlSchema = z7.discriminatedUnion("type", relayControlSchemas);
|
|
647
|
+
var ProxyToClientRelayControlTypes = new Set(relayControlDefinitions.filter((definition) => definition.directions.has("proxy_to_client")).map((definition) => definition.type));
|
|
648
|
+
var ClientToProxyRelayControlTypes = new Set(relayControlDefinitions.filter((definition) => definition.directions.has("client_to_proxy")).map((definition) => definition.type));
|
|
649
|
+
|
|
650
|
+
// ../../packages/shared/dist/constants/session.js
|
|
651
|
+
var SessionState = {
|
|
652
|
+
IDLE: "idle",
|
|
653
|
+
WORKING: "working",
|
|
654
|
+
WAITING_APPROVAL: "waiting_approval",
|
|
655
|
+
ERROR: "error",
|
|
656
|
+
TERMINATED: "terminated"
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
// ../../packages/shared/dist/binary-frame.js
|
|
660
|
+
var SID_LEN_BYTES = 1;
|
|
661
|
+
var SEQ_BYTES = 4;
|
|
662
|
+
var HEADER_FIXED_BYTES = SID_LEN_BYTES + SEQ_BYTES;
|
|
663
|
+
function encodeBinaryFrame(sessionId, outputSeq, data) {
|
|
664
|
+
const sidBytes = new TextEncoder().encode(sessionId);
|
|
665
|
+
if (sidBytes.length === 0 || sidBytes.length > 255) {
|
|
666
|
+
throw new RangeError(`sessionId byte length must be 1-255, got ${sidBytes.length} (sessionId=${sessionId})`);
|
|
667
|
+
}
|
|
668
|
+
if (!Number.isInteger(outputSeq) || outputSeq < 0 || outputSeq > 4294967295) {
|
|
669
|
+
throw new RangeError(`outputSeq must be a uint32, got ${outputSeq}`);
|
|
670
|
+
}
|
|
671
|
+
const frame = new Uint8Array(SID_LEN_BYTES + sidBytes.length + SEQ_BYTES + data.length);
|
|
672
|
+
frame[0] = sidBytes.length;
|
|
673
|
+
frame.set(sidBytes, SID_LEN_BYTES);
|
|
674
|
+
const seqOffset = SID_LEN_BYTES + sidBytes.length;
|
|
675
|
+
new DataView(frame.buffer, frame.byteOffset + seqOffset, SEQ_BYTES).setUint32(0, outputSeq, true);
|
|
676
|
+
frame.set(data, seqOffset + SEQ_BYTES);
|
|
677
|
+
return frame;
|
|
678
|
+
}
|
|
679
|
+
function decodeBinaryFrame(view) {
|
|
680
|
+
if (view.length < SID_LEN_BYTES + SEQ_BYTES)
|
|
681
|
+
return null;
|
|
682
|
+
const sidLen = view[0];
|
|
683
|
+
if (sidLen === 0)
|
|
684
|
+
return null;
|
|
685
|
+
if (view.length < SID_LEN_BYTES + sidLen + SEQ_BYTES)
|
|
686
|
+
return null;
|
|
687
|
+
const sessionId = new TextDecoder().decode(view.subarray(SID_LEN_BYTES, SID_LEN_BYTES + sidLen));
|
|
688
|
+
const seqOffset = SID_LEN_BYTES + sidLen;
|
|
689
|
+
const outputSeq = new DataView(view.buffer, view.byteOffset + seqOffset, SEQ_BYTES).getUint32(0, true);
|
|
690
|
+
const data = view.subarray(seqOffset + SEQ_BYTES);
|
|
691
|
+
return { sessionId, outputSeq, data };
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// src/ipc/ipc-protocol.ts
|
|
695
|
+
import { z as z8 } from "zod";
|
|
696
|
+
|
|
697
|
+
// src/ipc/line-buffer.ts
|
|
698
|
+
import { Transform } from "stream";
|
|
699
|
+
import { StringDecoder } from "string_decoder";
|
|
700
|
+
var LineBuffer = class extends Transform {
|
|
701
|
+
buffer = "";
|
|
702
|
+
decoder = new StringDecoder("utf8");
|
|
703
|
+
_transform(chunk, _encoding, callback) {
|
|
704
|
+
this.buffer += typeof chunk === "string" ? chunk : this.decoder.write(chunk);
|
|
705
|
+
const segments = this.buffer.split("\n");
|
|
706
|
+
this.buffer = segments.pop();
|
|
707
|
+
for (const segment of segments) {
|
|
708
|
+
if (segment.length > 0) {
|
|
709
|
+
this.push(segment);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
callback();
|
|
713
|
+
}
|
|
714
|
+
_flush(callback) {
|
|
715
|
+
const tail = this.decoder.end();
|
|
716
|
+
if (tail.length > 0) this.buffer += tail;
|
|
717
|
+
if (this.buffer.length > 0) {
|
|
718
|
+
this.push(this.buffer);
|
|
719
|
+
this.buffer = "";
|
|
720
|
+
}
|
|
721
|
+
callback();
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
// src/ipc/ipc-protocol.ts
|
|
726
|
+
var IPC_BINARY_MARKER = 0;
|
|
727
|
+
function encodeBinaryIpcFrame(sessionId, data, outputSeq) {
|
|
728
|
+
const inner = encodeBinaryFrame(sessionId, outputSeq, data);
|
|
729
|
+
const frame = Buffer.alloc(1 + 4 + inner.length);
|
|
730
|
+
frame[0] = IPC_BINARY_MARKER;
|
|
731
|
+
frame.writeUInt32LE(inner.length, 1);
|
|
732
|
+
frame.set(inner, 5);
|
|
733
|
+
return frame;
|
|
734
|
+
}
|
|
735
|
+
var sessionStateValues2 = Object.values(SessionState);
|
|
736
|
+
var ProviderHookContextSchema = z8.object({
|
|
737
|
+
provider: z8.enum(["claude", "codex"]),
|
|
738
|
+
sessionId: z8.string(),
|
|
739
|
+
hookUrl: z8.string(),
|
|
740
|
+
marker: z8.string(),
|
|
741
|
+
token: z8.string()
|
|
742
|
+
});
|
|
743
|
+
var IpcMessageSchema = z8.discriminatedUnion("type", [
|
|
744
|
+
// 客户端请求创建新会话,sessionId 可选用于重连时复用
|
|
745
|
+
z8.object({
|
|
746
|
+
type: z8.literal("session_create_request"),
|
|
747
|
+
name: z8.string().optional(),
|
|
748
|
+
mode: z8.enum(["pty", "json"]),
|
|
749
|
+
provider: z8.enum(["claude", "codex"]),
|
|
750
|
+
cwd: z8.string(),
|
|
751
|
+
pid: z8.number(),
|
|
752
|
+
sessionId: z8.string().optional()
|
|
753
|
+
}),
|
|
754
|
+
// 服务端响应创建会话
|
|
755
|
+
z8.object({
|
|
756
|
+
type: z8.literal("session_create_response"),
|
|
757
|
+
sessionId: z8.string(),
|
|
758
|
+
error: z8.string().optional(),
|
|
759
|
+
hook: ProviderHookContextSchema.optional()
|
|
760
|
+
}),
|
|
761
|
+
// 客户端请求终止会话
|
|
762
|
+
z8.object({
|
|
763
|
+
type: z8.literal("session_terminate_request"),
|
|
764
|
+
sessionId: z8.string()
|
|
765
|
+
}),
|
|
766
|
+
// 服务端响应终止会话
|
|
767
|
+
z8.object({
|
|
768
|
+
type: z8.literal("session_terminate_response"),
|
|
769
|
+
sessionId: z8.string(),
|
|
770
|
+
success: z8.boolean()
|
|
771
|
+
}),
|
|
772
|
+
// 客户端向服务端注册 PTY 会话
|
|
773
|
+
z8.object({
|
|
774
|
+
type: z8.literal("pty_register"),
|
|
775
|
+
sessionId: z8.string(),
|
|
776
|
+
pid: z8.number()
|
|
777
|
+
}),
|
|
778
|
+
// 客户端取消注册 PTY 会话
|
|
779
|
+
z8.object({
|
|
780
|
+
type: z8.literal("pty_deregister"),
|
|
781
|
+
sessionId: z8.string()
|
|
782
|
+
}),
|
|
783
|
+
// 输入,从服务端转发到客户端的 PTY stdin(手机远程输入注入)
|
|
784
|
+
z8.object({
|
|
785
|
+
type: z8.literal("pty_input"),
|
|
786
|
+
sessionId: z8.string(),
|
|
787
|
+
data: z8.string()
|
|
788
|
+
}),
|
|
789
|
+
// serve → terminal:Web 端移除本地终端会话时,只断开远程视图,不杀本地 CLI。
|
|
790
|
+
z8.object({
|
|
791
|
+
type: z8.literal("pty_detach"),
|
|
792
|
+
sessionId: z8.string()
|
|
793
|
+
}),
|
|
794
|
+
// 服务端广播会话状态变更
|
|
795
|
+
z8.object({
|
|
796
|
+
type: z8.literal("session_status_update"),
|
|
797
|
+
sessionId: z8.string(),
|
|
798
|
+
state: z8.enum(sessionStateValues2)
|
|
799
|
+
}),
|
|
800
|
+
// 错误响应
|
|
801
|
+
z8.object({
|
|
802
|
+
type: z8.literal("error"),
|
|
803
|
+
message: z8.string(),
|
|
804
|
+
code: z8.string().optional()
|
|
805
|
+
}),
|
|
806
|
+
// 客户端请求服务状态(含 relay 连接信息和 worker 状态)
|
|
807
|
+
z8.object({
|
|
808
|
+
type: z8.literal("service_status_request")
|
|
809
|
+
}),
|
|
810
|
+
// 服务端响应增强版服务状态
|
|
811
|
+
z8.object({
|
|
812
|
+
type: z8.literal("service_status_response"),
|
|
813
|
+
config: z8.object({
|
|
814
|
+
profile: z8.string().optional(),
|
|
815
|
+
relayName: z8.string(),
|
|
816
|
+
relayNameSource: z8.enum(["cli", "profile"]),
|
|
817
|
+
relayUrl: z8.string().optional(),
|
|
818
|
+
relayUrlSource: z8.enum(["env", "file", "none"]),
|
|
819
|
+
relayTokenSource: z8.enum(["env", "file", "none"]),
|
|
820
|
+
hookPort: z8.number(),
|
|
821
|
+
hookPortSource: z8.enum(["env", "file", "default"])
|
|
822
|
+
}),
|
|
823
|
+
relay: z8.object({
|
|
824
|
+
connected: z8.boolean(),
|
|
825
|
+
proxyId: z8.string(),
|
|
826
|
+
reconnectAttempt: z8.number(),
|
|
827
|
+
queueDepth: z8.number()
|
|
828
|
+
}).nullable(),
|
|
829
|
+
sessions: z8.array(
|
|
830
|
+
z8.object({
|
|
831
|
+
id: z8.string(),
|
|
832
|
+
mode: z8.enum(["pty", "json"]),
|
|
833
|
+
state: z8.enum(sessionStateValues2),
|
|
834
|
+
createdAt: z8.string(),
|
|
835
|
+
name: z8.string().optional(),
|
|
836
|
+
hasWorker: z8.boolean()
|
|
837
|
+
})
|
|
838
|
+
)
|
|
839
|
+
}),
|
|
840
|
+
// terminal → serve:终端标题变化,由 xterm onTitleChange 触发
|
|
841
|
+
z8.object({
|
|
842
|
+
type: z8.literal("pty_title_change"),
|
|
843
|
+
sessionId: z8.string(),
|
|
844
|
+
title: z8.string()
|
|
845
|
+
}),
|
|
846
|
+
// terminal → serve:local runtime 观察到的 PTY 语义事件。
|
|
847
|
+
z8.object({
|
|
848
|
+
type: z8.literal("pty_semantic_event"),
|
|
849
|
+
sessionId: z8.string(),
|
|
850
|
+
state: z8.enum(ptySemanticStateValues),
|
|
851
|
+
title: z8.string().optional(),
|
|
852
|
+
tool: z8.string().optional()
|
|
853
|
+
}),
|
|
854
|
+
// terminal → serve:终端尺寸变化
|
|
855
|
+
z8.object({
|
|
856
|
+
type: z8.literal("pty_resize"),
|
|
857
|
+
sessionId: z8.string(),
|
|
858
|
+
cols: z8.number(),
|
|
859
|
+
rows: z8.number()
|
|
860
|
+
}),
|
|
861
|
+
// serve → terminal:请求 HeadlessTerminal serialize() 快照
|
|
862
|
+
z8.object({
|
|
863
|
+
type: z8.literal("pty_subscribe"),
|
|
864
|
+
sessionId: z8.string(),
|
|
865
|
+
requestId: z8.string().optional()
|
|
866
|
+
}),
|
|
867
|
+
// terminal → serve:serialize() 结果
|
|
868
|
+
z8.object({
|
|
869
|
+
type: z8.literal("pty_snapshot"),
|
|
870
|
+
sessionId: z8.string(),
|
|
871
|
+
cols: z8.number(),
|
|
872
|
+
rows: z8.number(),
|
|
873
|
+
data: z8.string(),
|
|
874
|
+
outputSeq: z8.number().int().nonnegative(),
|
|
875
|
+
requestId: z8.string().optional()
|
|
876
|
+
}),
|
|
877
|
+
// serve → terminal:relay 连接状态变更,供终端给用户显示 remote viewing 是否通畅
|
|
878
|
+
z8.object({
|
|
879
|
+
type: z8.literal("bridge_status"),
|
|
880
|
+
connected: z8.boolean()
|
|
881
|
+
})
|
|
882
|
+
]);
|
|
883
|
+
var WorkerMessageSchema = z8.discriminatedUnion("type", [
|
|
884
|
+
// serve → worker: 发送用户输入给 claude
|
|
885
|
+
z8.object({
|
|
886
|
+
type: z8.literal("worker_input"),
|
|
887
|
+
content: z8.string()
|
|
888
|
+
}),
|
|
889
|
+
// serve → worker: 停止 claude 进程
|
|
890
|
+
z8.object({
|
|
891
|
+
type: z8.literal("worker_stop")
|
|
892
|
+
}),
|
|
893
|
+
// serve → worker: 工具审批响应
|
|
894
|
+
z8.object({
|
|
895
|
+
type: z8.literal("worker_approval_response"),
|
|
896
|
+
requestId: z8.string(),
|
|
897
|
+
behavior: z8.enum(["allow", "deny"]),
|
|
898
|
+
message: z8.string().optional()
|
|
899
|
+
}),
|
|
900
|
+
// worker → serve: claude 输出事件(带序列号)
|
|
901
|
+
z8.object({
|
|
902
|
+
type: z8.literal("worker_event"),
|
|
903
|
+
seq: z8.number(),
|
|
904
|
+
event: z8.record(z8.string(), z8.unknown())
|
|
905
|
+
}),
|
|
906
|
+
// worker → serve: claude 进程退出
|
|
907
|
+
z8.object({
|
|
908
|
+
type: z8.literal("worker_exit"),
|
|
909
|
+
code: z8.number()
|
|
910
|
+
}),
|
|
911
|
+
// worker → serve: 工具审批请求
|
|
912
|
+
z8.object({
|
|
913
|
+
type: z8.literal("worker_approval_request"),
|
|
914
|
+
requestId: z8.string(),
|
|
915
|
+
toolName: z8.string(),
|
|
916
|
+
input: z8.record(z8.string(), z8.unknown())
|
|
917
|
+
}),
|
|
918
|
+
// worker → serve: worker 就绪,claude 已启动
|
|
919
|
+
z8.object({
|
|
920
|
+
type: z8.literal("worker_ready"),
|
|
921
|
+
pid: z8.number()
|
|
922
|
+
}),
|
|
923
|
+
// worker → serve: 从 stream-json 的 system.init 事件捕获 Claude CLI 侧的 session ID
|
|
924
|
+
// proxy 拿它来读 ~/.claude/projects/.../<id>.jsonl 历史或后续 --resume
|
|
925
|
+
z8.object({
|
|
926
|
+
type: z8.literal("worker_claude_session_id"),
|
|
927
|
+
sessionId: z8.string()
|
|
928
|
+
}),
|
|
929
|
+
// serve → worker: 将指定工具加入会话白名单,后续同名工具自动审批
|
|
930
|
+
z8.object({
|
|
931
|
+
type: z8.literal("worker_whitelist_add"),
|
|
932
|
+
toolName: z8.string()
|
|
933
|
+
})
|
|
934
|
+
]);
|
|
935
|
+
function serializeWorkerMsg(msg) {
|
|
936
|
+
return JSON.stringify(msg) + "\n";
|
|
937
|
+
}
|
|
938
|
+
function createWorkerReader(stream, onMessage, onProtocolError) {
|
|
939
|
+
const lineBuffer = new LineBuffer();
|
|
940
|
+
lineBuffer.on("data", (line) => {
|
|
941
|
+
const str = typeof line === "string" ? line : line.toString();
|
|
942
|
+
if (str.length === 0) return;
|
|
943
|
+
try {
|
|
944
|
+
const raw = JSON.parse(str);
|
|
945
|
+
const result = WorkerMessageSchema.safeParse(raw);
|
|
946
|
+
if (result.success) {
|
|
947
|
+
onMessage(result.data);
|
|
948
|
+
} else {
|
|
949
|
+
onProtocolError?.(
|
|
950
|
+
new Error(`Worker message validation failed: ${result.error.message}`),
|
|
951
|
+
str
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
} catch (err) {
|
|
955
|
+
onProtocolError?.(new Error("Worker message parse error", { cause: err }), str);
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
stream.pipe(lineBuffer);
|
|
959
|
+
}
|
|
960
|
+
function serializeIpc(msg) {
|
|
961
|
+
return JSON.stringify(msg) + "\n";
|
|
962
|
+
}
|
|
963
|
+
function createIpcReader(stream, onMessage, onBinaryFrame, onProtocolError) {
|
|
964
|
+
let buf = Buffer.alloc(0);
|
|
965
|
+
let disposed = false;
|
|
966
|
+
function drain() {
|
|
967
|
+
while (buf.length > 0) {
|
|
968
|
+
if (buf[0] === IPC_BINARY_MARKER) {
|
|
969
|
+
if (buf.length < 5) return;
|
|
970
|
+
const payloadLen = buf.readUInt32LE(1);
|
|
971
|
+
const totalFrameLen = 1 + 4 + payloadLen;
|
|
972
|
+
if (buf.length < totalFrameLen) return;
|
|
973
|
+
const decoded = decodeBinaryFrame(buf.subarray(5, totalFrameLen));
|
|
974
|
+
if (decoded && onBinaryFrame) {
|
|
975
|
+
onBinaryFrame(decoded.sessionId, Buffer.from(decoded.data), decoded.outputSeq);
|
|
976
|
+
}
|
|
977
|
+
buf = buf.subarray(totalFrameLen);
|
|
978
|
+
} else {
|
|
979
|
+
const newlineIdx = buf.indexOf(10);
|
|
980
|
+
if (newlineIdx === -1) return;
|
|
981
|
+
const line = buf.subarray(0, newlineIdx).toString("utf-8");
|
|
982
|
+
buf = buf.subarray(newlineIdx + 1);
|
|
983
|
+
if (line.length === 0) continue;
|
|
984
|
+
try {
|
|
985
|
+
const raw = JSON.parse(line);
|
|
986
|
+
const result = IpcMessageSchema.safeParse(raw);
|
|
987
|
+
if (result.success) {
|
|
988
|
+
onMessage(result.data);
|
|
989
|
+
} else {
|
|
990
|
+
onProtocolError?.(
|
|
991
|
+
new Error(`IPC message validation failed: ${result.error.message}`),
|
|
992
|
+
line
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
} catch (err) {
|
|
996
|
+
onProtocolError?.(new Error("IPC message parse error", { cause: err }), line);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
function onData(chunk) {
|
|
1002
|
+
if (disposed) return;
|
|
1003
|
+
const incoming = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
1004
|
+
buf = Buffer.concat([buf, incoming]);
|
|
1005
|
+
drain();
|
|
1006
|
+
}
|
|
1007
|
+
stream.on("data", onData);
|
|
1008
|
+
return () => {
|
|
1009
|
+
disposed = true;
|
|
1010
|
+
stream.off("data", onData);
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
export {
|
|
1015
|
+
providerValues,
|
|
1016
|
+
MessageEnvelopeSchema,
|
|
1017
|
+
buildMessage,
|
|
1018
|
+
serializeControl,
|
|
1019
|
+
ControlErrorCode,
|
|
1020
|
+
RelayControlSchema,
|
|
1021
|
+
SessionState,
|
|
1022
|
+
encodeBinaryFrame,
|
|
1023
|
+
createFSM,
|
|
1024
|
+
defineFSM,
|
|
1025
|
+
LineBuffer,
|
|
1026
|
+
encodeBinaryIpcFrame,
|
|
1027
|
+
serializeWorkerMsg,
|
|
1028
|
+
createWorkerReader,
|
|
1029
|
+
serializeIpc,
|
|
1030
|
+
createIpcReader
|
|
1031
|
+
};
|
|
1032
|
+
//# sourceMappingURL=chunk-NBRBO5GS.js.map
|