agentdev 0.1.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.
@@ -0,0 +1,926 @@
1
+ import {
2
+ getDefaultUDSPath
3
+ } from "./chunk-TSASFMRF.js";
4
+ import {
5
+ __require
6
+ } from "./chunk-BDS2QGZ5.js";
7
+
8
+ // src/core/debug-transport.ts
9
+ function resolveDebugTransportMode() {
10
+ return process.env.AGENTDEV_DEBUG_TRANSPORT === "claw" ? "claw" : "viewer-worker";
11
+ }
12
+ function getClawRuntimeUrl() {
13
+ return process.env.AGENTDEV_CLAW_RUNTIME_URL || "http://127.0.0.1:3030";
14
+ }
15
+
16
+ // src/core/debug-capabilities.ts
17
+ function getDebugCapabilities() {
18
+ const transportMode = resolveDebugTransportMode();
19
+ const isClaw = transportMode === "claw";
20
+ return {
21
+ transportMode,
22
+ interactiveInput: true,
23
+ runtimeUrl: isClaw ? getClawRuntimeUrl() : null,
24
+ viewerCompatibleApi: isClaw,
25
+ debuggerMcpMetadata: isClaw
26
+ };
27
+ }
28
+
29
+ // src/core/debug-hub.ts
30
+ import { connect } from "net";
31
+
32
+ // src/core/claw-debug-client.ts
33
+ var ClawDebugClient = class {
34
+ runtimeUrl;
35
+ processId;
36
+ projectRoot;
37
+ sessionByAgentId = /* @__PURE__ */ new Map();
38
+ pendingSessionByAgentId = /* @__PURE__ */ new Map();
39
+ constructor(options = {}) {
40
+ this.runtimeUrl = (options.runtimeUrl ?? getClawRuntimeUrl()).replace(/\/$/, "");
41
+ this.processId = options.processId ?? String(process.pid);
42
+ this.projectRoot = options.projectRoot ?? process.cwd();
43
+ }
44
+ async ping() {
45
+ await this.requestJson("/health");
46
+ }
47
+ async registerAgent(input) {
48
+ const sessionId = `${this.processId}:${input.agentId}`;
49
+ const existing = this.pendingSessionByAgentId.get(input.agentId);
50
+ if (existing) {
51
+ return existing;
52
+ }
53
+ const pending = (async () => {
54
+ const registration = {
55
+ sessionId,
56
+ runtime: "agentdev",
57
+ agentName: input.name,
58
+ projectRoot: input.projectRoot ?? this.projectRoot,
59
+ metadata: {
60
+ adapter: "agentdev-debug-hub",
61
+ agentId: input.agentId,
62
+ featureTemplates: input.featureTemplates ?? {}
63
+ },
64
+ state: {
65
+ hookInspector: input.hookInspector ?? null,
66
+ overview: input.overview ?? null
67
+ }
68
+ };
69
+ await this.requestJson("/api/sessions/register", {
70
+ method: "POST",
71
+ body: JSON.stringify(registration)
72
+ });
73
+ this.sessionByAgentId.set(input.agentId, sessionId);
74
+ await this.pushLifecycle(input.agentId, {
75
+ phase: "agent-registered",
76
+ agentId: input.agentId,
77
+ name: input.name
78
+ });
79
+ return sessionId;
80
+ })();
81
+ this.pendingSessionByAgentId.set(input.agentId, pending);
82
+ try {
83
+ return await pending;
84
+ } finally {
85
+ this.pendingSessionByAgentId.delete(input.agentId);
86
+ }
87
+ }
88
+ async unregisterAgent(agentId) {
89
+ await this.pushLifecycle(agentId, {
90
+ phase: "agent-unregistered",
91
+ agentId
92
+ });
93
+ }
94
+ async selectAgent(agentId) {
95
+ const sessionId = await this.requireSessionId(agentId);
96
+ await this.requestJson("/api/agents/current", {
97
+ method: "PUT",
98
+ body: JSON.stringify({ agentId: sessionId })
99
+ });
100
+ }
101
+ async pushMessages(agentId, messages) {
102
+ await this.pushEvent(agentId, "message", { messages });
103
+ }
104
+ async registerTools(agentId, tools) {
105
+ await this.pushEvent(agentId, "tools", { tools });
106
+ }
107
+ async updateInspector(agentId, hookInspector) {
108
+ await this.pushEvent(agentId, "snapshot", {
109
+ scope: "hookInspector",
110
+ hookInspector
111
+ });
112
+ }
113
+ async updateOverview(agentId, overview) {
114
+ await this.pushEvent(agentId, "snapshot", {
115
+ scope: "overview",
116
+ overview
117
+ });
118
+ }
119
+ async pushNotification(agentId, notification) {
120
+ await this.pushEvent(agentId, "notification", { notification });
121
+ }
122
+ async requestUserInput(_agentId, request, timeout) {
123
+ const agentId = _agentId;
124
+ const requestId = `input-${agentId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
125
+ await this.pushEvent(agentId, "input", {
126
+ requestId,
127
+ request,
128
+ timeout,
129
+ status: "pending"
130
+ });
131
+ const sessionId = await this.requireSessionId(agentId);
132
+ const startedAt = Date.now();
133
+ const pollIntervalMs = 400;
134
+ while (true) {
135
+ if (timeout !== Infinity && Date.now() - startedAt > timeout) {
136
+ throw new Error(`User input timeout after ${timeout}ms`);
137
+ }
138
+ let response;
139
+ try {
140
+ response = await fetch(
141
+ `${this.runtimeUrl}/api/agents/${encodeURIComponent(sessionId)}/input-response?requestId=${encodeURIComponent(requestId)}`
142
+ );
143
+ } catch (error) {
144
+ const message = error instanceof Error ? error.message : String(error);
145
+ throw new Error(`Unable to reach Claw runtime at ${this.runtimeUrl} while waiting for user input: ${message}`);
146
+ }
147
+ if (response.ok) {
148
+ const body = await response.json();
149
+ return body.response;
150
+ }
151
+ if (response.status !== 404) {
152
+ const text = await response.text();
153
+ throw new Error(`Input bridge request failed: ${response.status} ${response.statusText} ${text}`);
154
+ }
155
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
156
+ }
157
+ }
158
+ async pushLifecycle(agentId, payload) {
159
+ await this.pushEvent(agentId, "lifecycle", payload);
160
+ }
161
+ async pushEvent(agentId, kind, payload) {
162
+ const sessionId = await this.requireSessionId(agentId);
163
+ const event = {
164
+ sessionId,
165
+ kind,
166
+ payload
167
+ };
168
+ await this.requestJson("/api/events", {
169
+ method: "POST",
170
+ body: JSON.stringify(event)
171
+ });
172
+ }
173
+ async requireSessionId(agentId) {
174
+ const sessionId = this.sessionByAgentId.get(agentId);
175
+ if (!sessionId) {
176
+ const pending = this.pendingSessionByAgentId.get(agentId);
177
+ if (pending) {
178
+ return pending;
179
+ }
180
+ throw new Error(`No Claw session registered for agentId '${agentId}'`);
181
+ }
182
+ return sessionId;
183
+ }
184
+ async requestJson(path, init) {
185
+ let response;
186
+ try {
187
+ response = await fetch(`${this.runtimeUrl}${path}`, {
188
+ ...init,
189
+ headers: {
190
+ "content-type": "application/json",
191
+ ...init?.headers ?? {}
192
+ }
193
+ });
194
+ } catch (error) {
195
+ const message = error instanceof Error ? error.message : String(error);
196
+ throw new Error(`Unable to reach Claw runtime at ${this.runtimeUrl}: ${message}`);
197
+ }
198
+ if (!response.ok) {
199
+ const body = await response.text();
200
+ throw new Error(`Claw runtime request failed: ${response.status} ${response.statusText} ${body}`);
201
+ }
202
+ if (response.status === 204) {
203
+ return null;
204
+ }
205
+ return response.json();
206
+ }
207
+ };
208
+
209
+ // src/core/debug-hub.ts
210
+ var DebugHub = class _DebugHub {
211
+ static instance;
212
+ transportMode;
213
+ clawClient;
214
+ // ========== 状态 ==========
215
+ agents = /* @__PURE__ */ new Map();
216
+ currentAgentId = null;
217
+ nextId = 1;
218
+ processId;
219
+ // 进程唯一标识
220
+ // 输入请求回调映射:requestId → resolver
221
+ pendingInputRequests = /* @__PURE__ */ new Map();
222
+ // 活跃的输入请求元数据(用于重连恢复):agentId → requestInfo
223
+ activeInputRequests = /* @__PURE__ */ new Map();
224
+ // UDS 客户端连接
225
+ udsClient;
226
+ udsPath;
227
+ workerPort = null;
228
+ clientReady = false;
229
+ // 注册锁(防止并发竞争)
230
+ registrationLock = false;
231
+ // 待发送的消息队列(连接建立前)
232
+ messageQueue = [];
233
+ // 重连机制
234
+ reconnectTimer;
235
+ reconnectAttempts = 0;
236
+ MAX_RECONNECT_ATTEMPTS = 10;
237
+ RECONNECT_DELAY = 2e3;
238
+ // 缓存每个 Agent 的 featureTemplates(用于重连后重新注册)
239
+ agentFeatureTemplates = /* @__PURE__ */ new Map();
240
+ // ========== 单例 ==========
241
+ constructor() {
242
+ this.udsPath = process.env.AGENTDEV_UDS_PATH || getDefaultUDSPath();
243
+ this.processId = String(process.pid);
244
+ this.transportMode = resolveDebugTransportMode();
245
+ if (this.transportMode === "claw") {
246
+ this.clawClient = new ClawDebugClient({
247
+ processId: this.processId,
248
+ projectRoot: process.cwd()
249
+ });
250
+ }
251
+ }
252
+ static getInstance() {
253
+ if (!_DebugHub.instance) {
254
+ _DebugHub.instance = new _DebugHub();
255
+ }
256
+ return _DebugHub.instance;
257
+ }
258
+ // ========== 公开 API ==========
259
+ /**
260
+ * 启动调试服务器
261
+ * @param port HTTP 端口(默认 2026,仅用于显示)
262
+ * @param openBrowser 是否自动打开浏览器(默认 true,已废弃参数)
263
+ */
264
+ async start(port = 2026, openBrowser = true) {
265
+ if (this.transportMode === "claw") {
266
+ this.workerPort = port;
267
+ try {
268
+ await this.clawClient?.ping();
269
+ this.clientReady = true;
270
+ console.log(`[DebugHub] \u5DF2\u8FDE\u63A5\u5230 Claw runtime: ${getClawRuntimeUrl()}`);
271
+ } catch (err) {
272
+ console.warn(`[DebugHub] \u65E0\u6CD5\u8FDE\u63A5\u5230 Claw runtime: ${err.message}`);
273
+ console.warn("[DebugHub] \u8C03\u8BD5\u529F\u80FD\u5C06\u88AB\u7981\u7528\u3002\u8BF7\u5148\u542F\u52A8 AgentDevClaw runtime\u3002");
274
+ this.clientReady = false;
275
+ }
276
+ return;
277
+ }
278
+ if (this.udsClient) {
279
+ console.log(`[DebugHub] \u5DF2\u8FDE\u63A5\u5230 ViewerWorker`);
280
+ return;
281
+ }
282
+ this.workerPort = port;
283
+ this.openBrowser = openBrowser;
284
+ try {
285
+ await this.connectToWorker();
286
+ console.log(`[DebugHub] \u8C03\u8BD5\u670D\u52A1\u5668\u5DF2\u8FDE\u63A5: http://localhost:${port}`);
287
+ } catch (err) {
288
+ console.log(`[DebugHub] ViewerWorker \u672A\u8FD0\u884C\uFF0C\u6B63\u5728\u81EA\u52A8\u542F\u52A8...`);
289
+ try {
290
+ await this.spawnViewerWorker();
291
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
292
+ await this.connectToWorker();
293
+ console.log(`[DebugHub] \u8C03\u8BD5\u670D\u52A1\u5668\u5DF2\u8FDE\u63A5: http://localhost:${port}`);
294
+ } catch (spawnErr) {
295
+ console.warn(`[DebugHub] \u65E0\u6CD5\u542F\u52A8 ViewerWorker: ${spawnErr.message}`);
296
+ console.warn(`[DebugHub] \u8C03\u8BD5\u529F\u80FD\u5C06\u88AB\u7981\u7528\u3002\u8BF7\u624B\u52A8\u8FD0\u884C 'agentdev-viewer' \u542F\u52A8\u8C03\u8BD5\u670D\u52A1\u5668\u3002`);
297
+ this.clientReady = false;
298
+ }
299
+ }
300
+ }
301
+ /**
302
+ * 自动启动 ViewerWorker 进程
303
+ */
304
+ openBrowser = true;
305
+ viewerWorkerProcess;
306
+ async spawnViewerWorker() {
307
+ const { spawn } = await import("child_process");
308
+ const { existsSync } = await import("fs");
309
+ const { fileURLToPath } = await import("url");
310
+ const { dirname, join } = await import("path");
311
+ let viewerPath;
312
+ try {
313
+ const agentdevPath = __require.resolve("agentdev/package.json");
314
+ viewerPath = join(dirname(agentdevPath), "dist", "cli", "viewer.js");
315
+ } catch {
316
+ try {
317
+ const currentDir = dirname(fileURLToPath(import.meta.url));
318
+ viewerPath = join(currentDir, "..", "cli", "viewer.js");
319
+ } catch {
320
+ viewerPath = "agentdev-viewer";
321
+ }
322
+ }
323
+ if (viewerPath !== "agentdev-viewer" && !existsSync(viewerPath)) {
324
+ viewerPath = "agentdev-viewer";
325
+ }
326
+ return new Promise((resolve, reject) => {
327
+ try {
328
+ const env = {
329
+ ...process.env,
330
+ AGENTDEV_PORT: String(this.workerPort || 2026),
331
+ AGENTDEV_OPEN_BROWSER: this.openBrowser ? "true" : "false",
332
+ AGENTDEV_UDS_PATH: this.udsPath
333
+ };
334
+ console.log(`[DebugHub] \u542F\u52A8 ViewerWorker: ${viewerPath}`);
335
+ const isBinCommand = viewerPath === "agentdev-viewer" || viewerPath === "agentdev-server";
336
+ const command = isBinCommand ? viewerPath : "node";
337
+ const args = isBinCommand ? [] : [viewerPath];
338
+ this.viewerWorkerProcess = spawn(command, args, {
339
+ env,
340
+ stdio: ["ignore", "pipe", "pipe"],
341
+ detached: false,
342
+ shell: isBinCommand
343
+ // bin 命令需要 shell 来解析
344
+ });
345
+ this.viewerWorkerProcess.on("error", (err) => {
346
+ console.error("[DebugHub] ViewerWorker \u8FDB\u7A0B\u9519\u8BEF:", err.message);
347
+ reject(err);
348
+ });
349
+ this.viewerWorkerProcess.stdout?.on("data", (data) => {
350
+ const lines = data.toString().trim().split("\n");
351
+ for (const line of lines) {
352
+ console.log(`[ViewerWorker] ${line}`);
353
+ }
354
+ });
355
+ this.viewerWorkerProcess.stderr?.on("data", (data) => {
356
+ const lines = data.toString().trim().split("\n");
357
+ for (const line of lines) {
358
+ console.error(`[ViewerWorker] ${line}`);
359
+ }
360
+ });
361
+ setTimeout(() => {
362
+ if (this.viewerWorkerProcess && !this.viewerWorkerProcess.killed) {
363
+ resolve();
364
+ } else {
365
+ reject(new Error("ViewerWorker \u8FDB\u7A0B\u542F\u52A8\u5931\u8D25"));
366
+ }
367
+ }, 500);
368
+ } catch (err) {
369
+ reject(err);
370
+ }
371
+ });
372
+ }
373
+ /**
374
+ * 停止调试服务器
375
+ */
376
+ stop() {
377
+ if (this.transportMode === "claw") {
378
+ this.clientReady = false;
379
+ return;
380
+ }
381
+ if (this.reconnectTimer) {
382
+ clearTimeout(this.reconnectTimer);
383
+ this.reconnectTimer = void 0;
384
+ }
385
+ if (this.udsClient) {
386
+ this.sendToWorker({ type: "stop" });
387
+ this.udsClient.end();
388
+ this.udsClient = void 0;
389
+ this.clientReady = false;
390
+ }
391
+ }
392
+ /**
393
+ * 手动重连(可选)
394
+ * 如果已经连接,则不执行任何操作
395
+ */
396
+ async reconnect() {
397
+ if (this.transportMode === "claw") {
398
+ await this.start(this.workerPort ?? 2026, false);
399
+ if (!this.clientReady) {
400
+ throw new Error("Claw runtime reconnect failed");
401
+ }
402
+ return;
403
+ }
404
+ if (this.clientReady && this.udsClient) {
405
+ console.log("[DebugHub] \u5DF2\u7ECF\u8FDE\u63A5\uFF0C\u65E0\u9700\u91CD\u8FDE");
406
+ return;
407
+ }
408
+ this.reconnectAttempts = 0;
409
+ if (this.reconnectTimer) {
410
+ clearTimeout(this.reconnectTimer);
411
+ this.reconnectTimer = void 0;
412
+ }
413
+ try {
414
+ await this.connectToWorker();
415
+ console.log("[DebugHub] \u2705 \u624B\u52A8\u91CD\u8FDE\u6210\u529F");
416
+ } catch (error) {
417
+ console.error(`[DebugHub] \u624B\u52A8\u91CD\u8FDE\u5931\u8D25: ${error.message}`);
418
+ throw error;
419
+ }
420
+ }
421
+ /**
422
+ * 注册 Agent
423
+ * @param agent Agent 实例
424
+ * @param name 显示名称(可选,默认使用类名)
425
+ * @param featureTemplates Feature 模板路径映射(可选)
426
+ * @returns 分配的 agentId
427
+ */
428
+ registerAgent(agent, name, featureTemplates, hookInspector, overview) {
429
+ while (this.registrationLock) {
430
+ }
431
+ this.registrationLock = true;
432
+ try {
433
+ const id = `agent-${this.nextId++}-${this.processId}`;
434
+ const info = {
435
+ id,
436
+ name: name || agent.constructor.name,
437
+ registeredAt: Date.now()
438
+ };
439
+ this.agents.set(id, { info, agent });
440
+ if (featureTemplates) {
441
+ this.agentFeatureTemplates.set(id, featureTemplates);
442
+ }
443
+ if (this.agents.size === 1) {
444
+ this.currentAgentId = id;
445
+ }
446
+ if (this.transportMode === "claw") {
447
+ void this.clawClient?.registerAgent({
448
+ agentId: id,
449
+ name: info.name,
450
+ projectRoot: process.cwd(),
451
+ featureTemplates,
452
+ hookInspector,
453
+ overview
454
+ }).catch((error) => {
455
+ console.error(`[DebugHub] Claw registerAgent \u5931\u8D25: ${error.message}`);
456
+ });
457
+ } else {
458
+ this.sendToWorker({
459
+ type: "register-agent",
460
+ agentId: id,
461
+ name: info.name,
462
+ createdAt: info.registeredAt,
463
+ projectRoot: process.cwd(),
464
+ // 传递项目根目录,用于模板文件加载
465
+ featureTemplates,
466
+ // 传递 Feature 模板路径映射
467
+ hookInspector,
468
+ overview
469
+ });
470
+ }
471
+ console.log(`[DebugHub] Agent \u5DF2\u6CE8\u518C: ${id} (${info.name})`);
472
+ return id;
473
+ } finally {
474
+ this.registrationLock = false;
475
+ }
476
+ }
477
+ /**
478
+ * 注销 Agent
479
+ * @param agentId Agent ID
480
+ */
481
+ unregisterAgent(agentId) {
482
+ const deleted = this.agents.delete(agentId);
483
+ if (deleted) {
484
+ if (this.transportMode === "claw") {
485
+ void this.clawClient?.unregisterAgent(agentId).catch((error) => {
486
+ console.error(`[DebugHub] Claw unregisterAgent \u5931\u8D25: ${error.message}`);
487
+ });
488
+ } else {
489
+ this.sendToWorker({ type: "unregister-agent", agentId });
490
+ }
491
+ console.log(`[DebugHub] Agent \u5DF2\u6CE8\u9500: ${agentId}`);
492
+ if (this.currentAgentId === agentId) {
493
+ const remaining = Array.from(this.agents.keys());
494
+ this.currentAgentId = remaining.length > 0 ? remaining[0] : null;
495
+ if (this.currentAgentId) {
496
+ if (this.transportMode === "claw") {
497
+ void this.clawClient?.selectAgent(this.currentAgentId).catch((error) => {
498
+ console.error(`[DebugHub] Claw selectAgent \u5931\u8D25: ${error.message}`);
499
+ });
500
+ } else if (this.transportMode === "viewer-worker") {
501
+ this.sendToWorker({
502
+ type: "set-current-agent",
503
+ agentId: this.currentAgentId
504
+ });
505
+ }
506
+ }
507
+ }
508
+ }
509
+ }
510
+ /**
511
+ * 切换当前选中的 Agent
512
+ * @param agentId Agent ID
513
+ * @returns 是否成功
514
+ */
515
+ selectAgent(agentId) {
516
+ if (!this.agents.has(agentId)) {
517
+ return false;
518
+ }
519
+ this.currentAgentId = agentId;
520
+ if (this.transportMode === "claw") {
521
+ void this.clawClient?.selectAgent(agentId).catch((error) => {
522
+ console.error(`[DebugHub] Claw selectAgent \u5931\u8D25: ${error.message}`);
523
+ });
524
+ console.log(`[DebugHub] \u5F53\u524D Agent \u5DF2\u5207\u6362: ${agentId}`);
525
+ return true;
526
+ }
527
+ if (this.transportMode === "viewer-worker") {
528
+ this.sendToWorker({
529
+ type: "set-current-agent",
530
+ agentId
531
+ });
532
+ }
533
+ console.log(`[DebugHub] \u5F53\u524D Agent \u5DF2\u5207\u6362: ${agentId}`);
534
+ return true;
535
+ }
536
+ /**
537
+ * 推送 Agent 消息
538
+ * @param agentId Agent ID
539
+ * @param messages 消息数组
540
+ */
541
+ pushMessages(agentId, messages) {
542
+ if (this.transportMode === "claw") {
543
+ void this.clawClient?.pushMessages(agentId, messages).catch((error) => {
544
+ console.error(`[DebugHub] Claw pushMessages \u5931\u8D25: ${error.message}`);
545
+ });
546
+ return;
547
+ }
548
+ this.sendToWorker({
549
+ type: "push-messages",
550
+ agentId,
551
+ messages
552
+ });
553
+ }
554
+ /**
555
+ * 注册 Agent 工具
556
+ * @param agentId Agent ID
557
+ * @param tools 工具数组
558
+ */
559
+ registerAgentTools(agentId, tools) {
560
+ if (this.transportMode === "claw") {
561
+ void this.clawClient?.registerTools(agentId, tools).catch((error) => {
562
+ console.error(`[DebugHub] Claw registerTools \u5931\u8D25: ${error.message}`);
563
+ });
564
+ return;
565
+ }
566
+ this.sendToWorker({
567
+ type: "register-tools",
568
+ agentId,
569
+ tools
570
+ });
571
+ }
572
+ updateAgentInspector(agentId, hookInspector) {
573
+ if (this.transportMode === "claw") {
574
+ void this.clawClient?.updateInspector(agentId, hookInspector).catch((error) => {
575
+ console.error(`[DebugHub] Claw updateInspector \u5931\u8D25: ${error.message}`);
576
+ });
577
+ return;
578
+ }
579
+ this.sendToWorker({
580
+ type: "update-agent-inspector",
581
+ agentId,
582
+ hookInspector
583
+ });
584
+ }
585
+ updateAgentOverview(agentId, overview) {
586
+ if (this.transportMode === "claw") {
587
+ void this.clawClient?.updateOverview(agentId, overview).catch((error) => {
588
+ console.error(`[DebugHub] Claw updateOverview \u5931\u8D25: ${error.message}`);
589
+ });
590
+ return;
591
+ }
592
+ this.sendToWorker({
593
+ type: "update-agent-overview",
594
+ agentId,
595
+ overview
596
+ });
597
+ }
598
+ /**
599
+ * 获取所有已注册的 Agent 信息
600
+ */
601
+ getAgentList() {
602
+ return Array.from(this.agents.values()).map((v) => v.info);
603
+ }
604
+ /**
605
+ * 获取当前选中的 Agent ID
606
+ */
607
+ getCurrentAgentId() {
608
+ return this.currentAgentId;
609
+ }
610
+ getTransportMode() {
611
+ return this.transportMode;
612
+ }
613
+ getCapabilities() {
614
+ return getDebugCapabilities();
615
+ }
616
+ /**
617
+ * 根据 Agent 实例获取其 ID
618
+ */
619
+ getAgentId(agent) {
620
+ for (const [id, data] of this.agents) {
621
+ if (data.agent === agent) {
622
+ return id;
623
+ }
624
+ }
625
+ return void 0;
626
+ }
627
+ /**
628
+ * 获取 Worker 端口
629
+ */
630
+ getPort() {
631
+ return this.workerPort;
632
+ }
633
+ /**
634
+ * 检查是否已连接到 ViewerWorker
635
+ */
636
+ isConnected() {
637
+ if (this.transportMode === "claw") {
638
+ return this.clientReady;
639
+ }
640
+ return this.clientReady && !!this.udsClient;
641
+ }
642
+ /**
643
+ * 推送通知
644
+ * @param agentId Agent ID
645
+ * @param notification 通知对象
646
+ */
647
+ pushNotification(agentId, notification) {
648
+ if (this.transportMode === "claw") {
649
+ void this.clawClient?.pushNotification(agentId, notification).catch((error) => {
650
+ console.error(`[DebugHub] Claw pushNotification \u5931\u8D25: ${error.message}`);
651
+ });
652
+ return;
653
+ }
654
+ this.sendToWorker({
655
+ type: "push-notification",
656
+ agentId,
657
+ notification
658
+ });
659
+ }
660
+ /**
661
+ * 请求用户输入
662
+ * @param agentId Agent ID
663
+ * @param prompt 提示信息
664
+ * @param timeout 超时时间(毫秒),默认 Infinity(无限等待)
665
+ * @returns Promise<string> 用户输入内容
666
+ */
667
+ requestUserInput(agentId, prompt, timeout = Infinity) {
668
+ return this.requestUserInputEvent(agentId, { prompt }, timeout).then((response) => {
669
+ if (response.kind !== "text") {
670
+ throw new Error(`Expected text user input but received action '${response.actionId ?? "unknown"}'`);
671
+ }
672
+ return response.text ?? "";
673
+ });
674
+ }
675
+ requestUserInputEvent(agentId, request, timeout = Infinity) {
676
+ if (this.transportMode === "claw") {
677
+ return this.clawClient?.requestUserInput(agentId, request, timeout) ?? Promise.reject(new Error("Claw client is not available"));
678
+ }
679
+ const requestId = `input-${agentId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
680
+ return new Promise((resolve, reject) => {
681
+ let timer;
682
+ if (timeout !== Infinity) {
683
+ timer = setTimeout(() => {
684
+ this.pendingInputRequests.delete(requestId);
685
+ this.activeInputRequests.delete(agentId);
686
+ reject(new Error(`User input timeout after ${timeout}ms`));
687
+ }, timeout);
688
+ }
689
+ this.pendingInputRequests.set(requestId, (response) => {
690
+ if (timer) clearTimeout(timer);
691
+ this.activeInputRequests.delete(agentId);
692
+ resolve(response);
693
+ });
694
+ this.activeInputRequests.set(agentId, {
695
+ requestId,
696
+ prompt: request.prompt,
697
+ placeholder: request.placeholder,
698
+ initialValue: request.initialValue,
699
+ actions: request.actions,
700
+ timestamp: Date.now()
701
+ });
702
+ this.sendToWorker({
703
+ type: "request-input",
704
+ agentId,
705
+ requestId,
706
+ prompt: request.prompt,
707
+ placeholder: request.placeholder,
708
+ initialValue: request.initialValue,
709
+ actions: request.actions,
710
+ timeout
711
+ });
712
+ });
713
+ }
714
+ // ========== 内部方法 ==========
715
+ /**
716
+ * 连接到 UDS 服务器
717
+ */
718
+ async connectToWorker() {
719
+ return new Promise((resolve, reject) => {
720
+ this.udsClient = connect(this.udsPath);
721
+ this.udsClient.on("connect", () => {
722
+ this.clientReady = true;
723
+ this.reconnectAttempts = 0;
724
+ console.log(`[DebugHub] \u5DF2\u8FDE\u63A5\u5230 ViewerWorker: ${this.udsPath}`);
725
+ for (const msg of this.messageQueue) {
726
+ this.sendViaUDS(msg);
727
+ }
728
+ this.messageQueue = [];
729
+ this.reregisterAllAgents();
730
+ if (this.currentAgentId) {
731
+ this.sendToWorker({
732
+ type: "set-current-agent",
733
+ agentId: this.currentAgentId
734
+ });
735
+ }
736
+ resolve();
737
+ });
738
+ this.udsClient.on("data", (data) => {
739
+ const lines = data.toString().split("\n");
740
+ for (const line of lines) {
741
+ if (!line.trim()) continue;
742
+ try {
743
+ const msg = JSON.parse(line);
744
+ this.handleWorkerMessage(msg);
745
+ } catch (err) {
746
+ console.error("[DebugHub] Worker \u6D88\u606F\u89E3\u6790\u5931\u8D25:", err);
747
+ }
748
+ }
749
+ });
750
+ this.udsClient.on("error", (err) => {
751
+ reject(new Error(`\u8FDE\u63A5 ViewerWorker \u5931\u8D25 (${this.udsPath}): ${err.message}
752
+ \u8BF7\u5148\u542F\u52A8 ViewerWorker \u670D\u52A1\u5668`));
753
+ });
754
+ this.udsClient.on("close", () => {
755
+ this.clientReady = false;
756
+ console.warn("[DebugHub] \u4E0E ViewerWorker \u7684\u8FDE\u63A5\u5DF2\u65AD\u5F00");
757
+ this.scheduleReconnect();
758
+ });
759
+ });
760
+ }
761
+ /**
762
+ * 处理来自 Worker 的消息
763
+ */
764
+ handleWorkerMessage(msg) {
765
+ switch (msg.type) {
766
+ case "agent-switched":
767
+ console.log(`[DebugHub] \u5F53\u524D Agent \u5DF2\u5207\u6362: ${msg.agentId}`);
768
+ break;
769
+ // 处理用户输入响应
770
+ case "input-response":
771
+ const resolver = this.pendingInputRequests.get(msg.requestId);
772
+ if (resolver) {
773
+ resolver(msg.response ?? {
774
+ kind: "text",
775
+ text: msg.input
776
+ });
777
+ this.pendingInputRequests.delete(msg.requestId);
778
+ } else {
779
+ console.warn(`[DebugHub] \u672A\u77E5\u8F93\u5165\u54CD\u5E94: ${msg.requestId}`);
780
+ }
781
+ break;
782
+ }
783
+ }
784
+ /**
785
+ * 重新注册所有 Agent(重连后调用)
786
+ * 确保 ViewerWorker 能够恢复所有 Agent 的注册信息
787
+ */
788
+ reregisterAllAgents() {
789
+ if (this.transportMode === "claw") {
790
+ for (const [id, data] of this.agents) {
791
+ const hookInspector = data.agent.buildHookInspectorSnapshot?.() || data.agent.hookInspector;
792
+ const overview = data.agent.buildOverviewSnapshot?.();
793
+ const featureTemplates = this.agentFeatureTemplates.get(id) || {};
794
+ void this.clawClient?.registerAgent({
795
+ agentId: id,
796
+ name: data.info.name,
797
+ projectRoot: process.cwd(),
798
+ featureTemplates,
799
+ hookInspector,
800
+ overview
801
+ }).then(async () => {
802
+ const tools = data.agent.tools;
803
+ if (tools && typeof tools.getEntries === "function") {
804
+ const entries = tools.getEntries();
805
+ const toolList = entries.map((e) => e.tool);
806
+ if (toolList.length > 0) {
807
+ await this.clawClient?.registerTools(id, toolList);
808
+ }
809
+ }
810
+ const context = data.agent.getContext?.();
811
+ if (context && typeof context.getAll === "function") {
812
+ const messages = context.getAll();
813
+ if (messages.length > 0) {
814
+ await this.clawClient?.pushMessages(id, messages);
815
+ }
816
+ }
817
+ }).catch((error) => {
818
+ console.error(`[DebugHub] Claw re-register \u5931\u8D25: ${error.message}`);
819
+ });
820
+ }
821
+ return;
822
+ }
823
+ if (this.agents.size === 0) {
824
+ return;
825
+ }
826
+ console.log(`[DebugHub] \u91CD\u65B0\u6CE8\u518C ${this.agents.size} \u4E2A Agent...`);
827
+ for (const [id, data] of this.agents) {
828
+ const hookInspector = data.agent.buildHookInspectorSnapshot?.() || data.agent.hookInspector;
829
+ const overview = data.agent.buildOverviewSnapshot?.();
830
+ const featureTemplates = this.agentFeatureTemplates.get(id) || {};
831
+ const activeInputRequest = this.activeInputRequests.get(id);
832
+ if (activeInputRequest) {
833
+ console.log(`[DebugHub] \u53D1\u73B0\u6D3B\u8DC3\u8F93\u5165\u8BF7\u6C42: ${activeInputRequest.requestId}`);
834
+ }
835
+ this.sendToWorker({
836
+ type: "register-agent",
837
+ agentId: id,
838
+ name: data.info.name,
839
+ createdAt: data.info.registeredAt,
840
+ projectRoot: process.cwd(),
841
+ featureTemplates,
842
+ hookInspector,
843
+ overview,
844
+ activeInputRequest
845
+ // 携带活跃输入请求
846
+ });
847
+ const tools = data.agent.tools;
848
+ if (tools && typeof tools.getEntries === "function") {
849
+ const entries = tools.getEntries();
850
+ const toolList = entries.map((e) => e.tool);
851
+ if (toolList.length > 0) {
852
+ this.sendToWorker({
853
+ type: "register-tools",
854
+ agentId: id,
855
+ tools: toolList
856
+ });
857
+ }
858
+ }
859
+ const context = data.agent.getContext?.();
860
+ if (context && typeof context.getAll === "function") {
861
+ const messages = context.getAll();
862
+ if (messages.length > 0) {
863
+ this.sendToWorker({
864
+ type: "push-messages",
865
+ agentId: id,
866
+ messages
867
+ });
868
+ console.log(`[DebugHub] \u6062\u590D Agent ${id} \u7684 ${messages.length} \u6761\u6D88\u606F`);
869
+ }
870
+ }
871
+ }
872
+ console.log(`[DebugHub] \u2705 \u91CD\u65B0\u6CE8\u518C\u5B8C\u6210`);
873
+ }
874
+ /**
875
+ * 安排重连(指数退避)
876
+ */
877
+ scheduleReconnect() {
878
+ if (this.reconnectTimer) {
879
+ clearTimeout(this.reconnectTimer);
880
+ }
881
+ if (this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {
882
+ console.error(`[DebugHub] \u8FBE\u5230\u6700\u5927\u91CD\u8FDE\u6B21\u6570 (${this.MAX_RECONNECT_ATTEMPTS})\uFF0C\u505C\u6B62\u91CD\u8FDE`);
883
+ return;
884
+ }
885
+ this.reconnectAttempts++;
886
+ const delay = Math.min(
887
+ this.RECONNECT_DELAY * Math.pow(2, this.reconnectAttempts - 1),
888
+ 3e4
889
+ );
890
+ console.log(`[DebugHub] ${delay}ms \u540E\u5C1D\u8BD5\u7B2C ${this.reconnectAttempts} \u6B21\u91CD\u8FDE...`);
891
+ this.reconnectTimer = setTimeout(async () => {
892
+ try {
893
+ await this.connectToWorker();
894
+ console.log("[DebugHub] \u2705 \u91CD\u8FDE\u6210\u529F\uFF0C\u8C03\u8BD5\u529F\u80FD\u5DF2\u6062\u590D");
895
+ } catch (error) {
896
+ console.error(`[DebugHub] \u91CD\u8FDE\u5931\u8D25: ${error.message}`);
897
+ this.scheduleReconnect();
898
+ }
899
+ }, delay);
900
+ }
901
+ /**
902
+ * 通过 UDS 发送消息
903
+ */
904
+ sendViaUDS(msg) {
905
+ if (this.udsClient && this.clientReady) {
906
+ this.udsClient.write(JSON.stringify(msg) + "\n");
907
+ }
908
+ }
909
+ /**
910
+ * 发送消息到 Worker
911
+ */
912
+ sendToWorker(msg) {
913
+ if (!this.udsClient) {
914
+ return;
915
+ }
916
+ this.sendViaUDS(msg);
917
+ }
918
+ };
919
+
920
+ export {
921
+ resolveDebugTransportMode,
922
+ getClawRuntimeUrl,
923
+ getDebugCapabilities,
924
+ DebugHub
925
+ };
926
+ //# sourceMappingURL=chunk-3BPSNNK3.js.map