mu-agent-bridge 1.0.73 → 1.0.78

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.
Files changed (152) hide show
  1. package/README.md +12 -8
  2. package/dist/src-ts/cli.js +67 -37
  3. package/dist/src-ts/control-server.js +7 -28
  4. package/dist/src-ts/core-updater.js +141 -33
  5. package/dist/src-ts/daemon-process.js +2 -1
  6. package/dist/src-ts/dsh-agent.js +7 -2
  7. package/dist/src-ts/http-agent.js +36 -0
  8. package/dist/src-ts/plugin-manager.js +185 -58
  9. package/dist/src-ts/plugins/abweb/index.js +1 -1
  10. package/dist/src-ts/plugins/abweb/public/app.js +8 -6
  11. package/dist/src-ts/plugins/abweb/public/index.html +3 -3
  12. package/dist/src-ts/plugins/aun/attachments.js +3 -21
  13. package/dist/src-ts/plugins/aun/index.js +89 -11
  14. package/dist/src-ts/plugins/aun/messages.js +1 -1
  15. package/dist/src-ts/plugins/aun/public/app.js +2 -1
  16. package/dist/src-ts/plugins/aun/public/index.html +1 -1
  17. package/dist/src-ts/plugins/cdp-draft.js +5 -2
  18. package/dist/src-ts/plugins/chatgpt/index.js +3 -2
  19. package/dist/src-ts/plugins/claude-cli/index.js +1 -1
  20. package/dist/src-ts/plugins/desktop-launch.js +44 -3
  21. package/dist/src-ts/plugins/doubao/cdp-client.js +231 -45
  22. package/dist/src-ts/plugins/doubao/index.js +119 -7
  23. package/dist/src-ts/plugins/doubao/native-task.js +134 -0
  24. package/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
  25. package/dist/src-ts/plugins/dsh/index.js +102 -23
  26. package/dist/src-ts/plugins/dsh/session-history.js +31 -3
  27. package/dist/src-ts/plugins/feishu/index.js +1 -1
  28. package/dist/src-ts/plugins/personal-assistant/index.js +29 -8
  29. package/dist/src-ts/plugins/personal-assistant/model-client.js +20 -6
  30. package/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
  31. package/dist/src-ts/plugins/personal-assistant/tools.js +4 -3
  32. package/dist/src-ts/plugins/qwenwork/cdp-client.js +43 -7
  33. package/dist/src-ts/plugins/qwenwork/index.js +52 -18
  34. package/dist/src-ts/plugins/workbuddy/cdp-client.js +349 -83
  35. package/dist/src-ts/plugins/workbuddy/index.js +24 -12
  36. package/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
  37. package/dist/src-ts/plugins/zcode/index.js +97 -0
  38. package/dist/src-ts/session-management.js +27 -8
  39. package/dist/src-ts/version-manager.js +6 -3
  40. package/dist/tests-ts/abweb-model-config.test.js +44 -41
  41. package/dist/tests-ts/aun-attachments.test.js +19 -17
  42. package/dist/tests-ts/aun-default-owner-page.test.js +2 -1
  43. package/dist/tests-ts/aun-messages.test.js +14 -0
  44. package/dist/tests-ts/aun-plugin.test.js +207 -19
  45. package/dist/tests-ts/bundled-package.test.js +1 -1
  46. package/dist/tests-ts/cdp-draft-recovery.test.js +182 -3
  47. package/dist/tests-ts/chatgpt-plugin.test.js +19 -0
  48. package/dist/tests-ts/control-server.test.js +20 -0
  49. package/dist/tests-ts/core-updater.test.js +215 -46
  50. package/dist/tests-ts/daemon-process.test.js +126 -1
  51. package/dist/tests-ts/desktop-launch.test.js +73 -0
  52. package/dist/tests-ts/doubao-native-task.test.js +237 -0
  53. package/dist/tests-ts/doubao-plugin.test.js +688 -19
  54. package/dist/tests-ts/dsh-agent.test.js +91 -19
  55. package/dist/tests-ts/dsh-cdp.test.js +148 -0
  56. package/dist/tests-ts/dsh-history.test.js +37 -0
  57. package/dist/tests-ts/feishu-plugin.test.js +1 -1
  58. package/dist/tests-ts/http-agent.test.js +18 -0
  59. package/dist/tests-ts/local-cli.test.js +29 -2
  60. package/dist/tests-ts/model-provider-cancellation.test.js +2 -0
  61. package/dist/tests-ts/native-session-management.test.js +8 -7
  62. package/dist/tests-ts/personal-assistant-coordination.test.js +16 -0
  63. package/dist/tests-ts/personal-assistant-extensions.test.js +7 -1
  64. package/dist/tests-ts/personal-assistant-memory.test.js +117 -0
  65. package/dist/tests-ts/personal-assistant.test.js +277 -3
  66. package/dist/tests-ts/platform-integration.test.js +60 -1
  67. package/dist/tests-ts/plugin-manager.test.js +78 -6
  68. package/dist/tests-ts/plugin-upgrade-deadline.test.js +354 -0
  69. package/dist/tests-ts/publish-packages.test.js +1 -1
  70. package/dist/tests-ts/qwenwork-plugin.test.js +30 -0
  71. package/dist/tests-ts/qwenwork-reconnect.test.js +195 -0
  72. package/dist/tests-ts/qwenwork-sse.test.js +33 -0
  73. package/dist/tests-ts/session-access-defaults.test.js +112 -0
  74. package/dist/tests-ts/session-management.test.js +8 -8
  75. package/dist/tests-ts/version-manager.test.js +40 -0
  76. package/dist/tests-ts/workbuddy-cdp-recovery.test.js +311 -0
  77. package/dist/tests-ts/workbuddy-plugin.test.js +356 -13
  78. package/dist/tests-ts/zcode-cdp.test.js +894 -0
  79. package/dist/tests-ts/zcode-plugin.test.js +396 -0
  80. package/package.json +1 -1
  81. package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +1 -1
  82. package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +8 -6
  83. package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +7 -5
  84. package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +3 -3
  85. package/plugins/abweb/package.json +1 -1
  86. package/plugins/abweb/plugin.json +1 -1
  87. package/plugins/aun/config.schema.json +1 -1
  88. package/plugins/aun/dist/src-ts/plugins/aun/attachments.js +3 -21
  89. package/plugins/aun/dist/src-ts/plugins/aun/index.js +89 -11
  90. package/plugins/aun/dist/src-ts/plugins/aun/messages.js +1 -1
  91. package/plugins/aun/dist/src-ts/plugins/aun/public/app.js +2 -1
  92. package/plugins/aun/dist/src-ts/plugins/aun/public/index.html +1 -1
  93. package/plugins/aun/package.json +1 -1
  94. package/plugins/aun/plugin.json +1 -1
  95. package/plugins/chatgpt/dist/src-ts/plugins/cdp-draft.js +5 -2
  96. package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +3 -2
  97. package/plugins/chatgpt/dist/src-ts/plugins/desktop-launch.js +44 -3
  98. package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +1 -1
  99. package/plugins/claude-cli/package.json +1 -1
  100. package/plugins/claude-cli/plugin.json +1 -1
  101. package/plugins/doubao/config.schema.json +2 -1
  102. package/plugins/doubao/dist/src-ts/plugins/desktop-launch.js +44 -3
  103. package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +231 -45
  104. package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +119 -7
  105. package/plugins/doubao/dist/src-ts/plugins/doubao/native-task.js +134 -0
  106. package/plugins/doubao/package.json +1 -1
  107. package/plugins/doubao/plugin.json +1 -1
  108. package/plugins/dsh/config.schema.json +5 -2
  109. package/plugins/dsh/dist/src-ts/dsh-agent.js +7 -2
  110. package/plugins/dsh/dist/src-ts/plugins/desktop-launch.js +44 -3
  111. package/plugins/dsh/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
  112. package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +102 -23
  113. package/plugins/dsh/dist/src-ts/plugins/dsh/session-history.js +31 -3
  114. package/plugins/dsh/package.json +1 -1
  115. package/plugins/dsh/plugin.json +1 -1
  116. package/plugins/feishu/dist/src-ts/plugins/feishu/index.js +1 -1
  117. package/plugins/feishu/package.json +1 -1
  118. package/plugins/feishu/plugin.json +1 -1
  119. package/plugins/personal-assistant/config.schema.json +3 -2
  120. package/plugins/personal-assistant/dist/src-ts/attachments.js +62 -0
  121. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/index.js +29 -8
  122. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/model-client.js +20 -6
  123. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
  124. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/tools.js +4 -3
  125. package/plugins/personal-assistant/package.json +2 -2
  126. package/plugins/personal-assistant/plugin.json +1 -1
  127. package/plugins/qwenwork/dist/src-ts/plugins/cdp-draft.js +5 -2
  128. package/plugins/qwenwork/dist/src-ts/plugins/desktop-launch.js +44 -3
  129. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +43 -7
  130. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +52 -18
  131. package/plugins/qwenwork/package.json +1 -1
  132. package/plugins/qwenwork/plugin.json +1 -1
  133. package/plugins/workbuddy/config.schema.json +1 -1
  134. package/plugins/workbuddy/dist/src-ts/plugins/desktop-launch.js +44 -3
  135. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +349 -83
  136. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +24 -12
  137. package/plugins/workbuddy/package.json +1 -1
  138. package/plugins/workbuddy/plugin.json +1 -1
  139. package/plugins/zcode/config.schema.json +13 -0
  140. package/plugins/zcode/dist/src-ts/agent.js +69 -0
  141. package/plugins/zcode/dist/src-ts/attachments.js +62 -0
  142. package/plugins/zcode/dist/src-ts/plugin.js +13 -0
  143. package/plugins/zcode/dist/src-ts/plugins/desktop-launch.js +238 -0
  144. package/plugins/zcode/dist/src-ts/plugins/workbuddy/cdp-client.js +896 -0
  145. package/plugins/zcode/dist/src-ts/plugins/workbuddy/cli-client.js +39 -0
  146. package/plugins/zcode/dist/src-ts/plugins/workbuddy/desktop-launch.js +10 -0
  147. package/plugins/zcode/dist/src-ts/plugins/workbuddy/index.js +567 -0
  148. package/plugins/zcode/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
  149. package/plugins/zcode/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
  150. package/plugins/zcode/dist/src-ts/plugins/zcode/index.js +97 -0
  151. package/plugins/zcode/package.json +16 -0
  152. package/plugins/zcode/plugin.json +11 -0
@@ -17,7 +17,7 @@ const session_history_1 = require("./session-history");
17
17
  const desktop_launch_1 = require("../desktop-launch");
18
18
  const desktop_launch_2 = require("./desktop-launch");
19
19
  exports.workbuddyManifest = {
20
- id: "workbuddy", version: "1.1.34", displayName: "WorkBuddy", type: "agent",
20
+ id: "workbuddy", version: "1.1.35", displayName: "WorkBuddy", type: "agent",
21
21
  entry: "dist/index.js", apiVersion: "1", process: "in-process",
22
22
  capabilities: ["chat", "health", "model-provider"], configSchema: "config.schema.json",
23
23
  };
@@ -52,7 +52,8 @@ class WorkbuddyPlugin {
52
52
  cliPath;
53
53
  model;
54
54
  transport = "auto";
55
- timeoutMs = 3_600_000;
55
+ replyTimeoutLimitMs;
56
+ timeoutMs = cdp_client_1.WORKBUDDY_MAX_WAIT_MS;
56
57
  queue = Promise.resolve();
57
58
  pending = 0;
58
59
  activeSessionId;
@@ -76,6 +77,7 @@ class WorkbuddyPlugin {
76
77
  sessionHistory;
77
78
  constructor(options = {}) {
78
79
  this.options = options;
80
+ this.replyTimeoutLimitMs = options.replyTimeoutLimitMs ?? cdp_client_1.WORKBUDDY_MAX_WAIT_MS;
79
81
  }
80
82
  get attachmentInput() { return Boolean(this.client?.attachmentInput && this.client.complete && this.transport !== "cli" && (!this.cliPath || this.transport === "cdp")); }
81
83
  async activate(context) {
@@ -83,7 +85,7 @@ class WorkbuddyPlugin {
83
85
  if (config.transport !== undefined && !["auto", "cli", "cdp"].includes(String(config.transport)))
84
86
  throw new Error("transport 只能是 auto、cli 或 cdp");
85
87
  this.transport = config.transport ?? "auto";
86
- this.timeoutMs = typeof config.timeoutMs === "number" && config.timeoutMs > 0 ? config.timeoutMs : 3_600_000;
88
+ this.timeoutMs = Math.min(typeof config.timeoutMs === "number" && config.timeoutMs > 0 ? config.timeoutMs : 3_600_000, this.replyTimeoutLimitMs);
87
89
  this.model = typeof config.model === "string" && config.model.trim() ? config.model : undefined;
88
90
  this.cliPath = (0, cli_client_1.findCli)(typeof config.cliPath === "string" ? config.cliPath : undefined);
89
91
  this.failure = undefined;
@@ -237,18 +239,23 @@ class WorkbuddyPlugin {
237
239
  return { status: "ready", details: { transport: "cdp" } };
238
240
  if (this.transport !== "cdp" && this.cliPath && (0, node_fs_1.existsSync)(this.cliPath))
239
241
  return { status: "ready", details: { transport: "cli" } };
240
- return { status: "failed", message: this.failure ?? "WorkBuddy 未连接", details: { transport: this.transport } };
242
+ return { status: "failed", message: this.failure ?? "WorkBuddy CDP 未连接;桌面升级后请在后台使用“重启桌面”恢复连接", details: { transport: this.transport } };
241
243
  }
242
244
  async handle(request) {
243
245
  if (!request.task.trim())
244
246
  throw new Error("消息内容不能为空");
245
247
  (0, attachments_1.validateAttachments)(request.attachments);
246
248
  const run = this.enqueue(async () => {
249
+ request.signal?.throwIfAborted();
247
250
  try {
248
- return await this.complete(request);
249
- }
250
- finally {
251
+ const response = await this.complete(request);
251
252
  await this.synchronizeSession();
253
+ return response;
254
+ }
255
+ catch (error) {
256
+ // 失败时桌面可能已切换,保留原绑定并要求重新校验或恢复。
257
+ this.desktopSessionValid = false;
258
+ throw error;
252
259
  }
253
260
  });
254
261
  const response = await run;
@@ -257,7 +264,12 @@ class WorkbuddyPlugin {
257
264
  async synchronizeSession() {
258
265
  if (this.activeSessionId && this.desktopSessionValid && this.client?.conversationId) {
259
266
  try {
260
- this.activeDesktopConversationId = await this.client.conversationId();
267
+ const conversationId = await this.client.conversationId();
268
+ if (this.activeDesktopConversationId && conversationId !== this.activeDesktopConversationId) {
269
+ this.desktopSessionValid = false;
270
+ throw new Error("WorkBuddy 桌面任务已切换,已保留原会话绑定");
271
+ }
272
+ this.activeDesktopConversationId = conversationId;
261
273
  if (this.activeDesktopConversationId)
262
274
  this.sessionBindings.set(this.activeSessionId, { conversationId: this.activeDesktopConversationId, expert: this.activeExpert });
263
275
  await this.persistKnownSessions();
@@ -337,8 +349,8 @@ class WorkbuddyPlugin {
337
349
  async complete(request, modelRequest = false) {
338
350
  let stage = "连接 WorkBuddy 桌面";
339
351
  try {
340
- const timeout = request.timeoutMs ?? this.timeoutMs;
341
- const expert = modelRequest ? undefined : extractExpertQuery(request.task);
352
+ const timeout = Math.min(request.timeoutMs ?? this.timeoutMs, this.replyTimeoutLimitMs);
353
+ const expert = modelRequest || this.options.expertSelection === false ? undefined : extractExpertQuery(request.task);
342
354
  const usesCli = Boolean(this.cliPath && this.transport !== "cdp" && !expert);
343
355
  const hasAttachments = Array.isArray(request.attachments) && request.attachments.length > 0;
344
356
  if (hasAttachments && !this.attachmentInput)
@@ -372,7 +384,7 @@ class WorkbuddyPlugin {
372
384
  this.expertSession = true;
373
385
  try {
374
386
  if (this.client.complete)
375
- return await this.client.complete({ ...request, timeoutMs: timeout }, select, false);
387
+ return await this.client.complete({ ...request, timeoutMs: timeout }, select, false, request.sessionId ? this.activeDesktopConversationId ?? null : undefined);
376
388
  return { text: await this.client.direct(request.task, timeout, select) };
377
389
  }
378
390
  catch (error) {
@@ -392,7 +404,7 @@ class WorkbuddyPlugin {
392
404
  this.activeExpert = null;
393
405
  try {
394
406
  if (this.client.complete)
395
- return await this.client.complete({ ...request, timeoutMs: timeout }, undefined, reset);
407
+ return await this.client.complete({ ...request, timeoutMs: timeout }, undefined, reset, request.sessionId ? this.activeDesktopConversationId ?? null : undefined);
396
408
  return { text: await this.client.direct(request.task, timeout, undefined, reset) };
397
409
  }
398
410
  catch (error) {
@@ -0,0 +1,500 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ZcodeCdpClient = exports.zcodeReplyArtifactsScript = exports.zcodeSnapshotScript = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const node_fs_1 = require("node:fs");
6
+ const promises_1 = require("node:fs/promises");
7
+ const node_path_1 = require("node:path");
8
+ const agent_1 = require("../../agent");
9
+ const attachments_1 = require("../../attachments");
10
+ const cdp_client_1 = require("../workbuddy/cdp-client");
11
+ const editorSelector = '[data-testid="v4-composer-input"]';
12
+ const paneSelector = '[data-testid="v4-pane-shell-workspace-main"]';
13
+ const sessionSelector = '[data-testid="v4-session-pane-workspace-main"]';
14
+ const sendSelector = '[data-testid="v4-composer-send"]';
15
+ const busySelector = 'button[aria-label="停止生成"],button[aria-label="停止"],button[aria-label="Stop generating"],[data-testid="chat-loading"],[data-query-row-id][data-running="true"]';
16
+ const permissionSelector = 'button[data-permission-option-kind]';
17
+ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
18
+ // shortcut: 依据 ZCode v4 的可测试 DOM 适配,桌面改版时需重新完成真机验收。
19
+ exports.zcodeSnapshotScript = `(() => {
20
+ const root = document.querySelector(${JSON.stringify(paneSelector)});
21
+ const editor = root?.querySelector(${JSON.stringify(editorSelector)});
22
+ const running = root?.closest('[data-testid="side-pane-full-view-host"]')?.getAttribute('data-running');
23
+ const id = root?.querySelector(${JSON.stringify(sessionSelector)})?.getAttribute('data-session-id');
24
+ const error = root?.querySelector('[role="alert"],[data-testid="chat-error"],[data-testid="chat-error-banner"],[data-testid="v4-session-quota-banner"]');
25
+ const list = root?.querySelector(${JSON.stringify(permissionSelector)})?.closest('[role="listbox"]');
26
+ const interaction = list ? {
27
+ // React 以权限请求 ID 为 key 重建面板,节点标识可拒绝文案相同的新请求。
28
+ id: list.__agentBridgePermissionId ??= crypto.randomUUID(),
29
+ prompt: list.parentElement?.previousElementSibling?.innerText.trim() || 'ZCode 需要权限',
30
+ options: [...list.querySelectorAll(${JSON.stringify(permissionSelector)})].map(node => node.innerText.replace(/^\\s*\\d+[.、]\\s*/, '').trim()),
31
+ labels: [...list.querySelectorAll(${JSON.stringify(permissionSelector)})].map(node => node.getAttribute('aria-label') || ''),
32
+ } : undefined;
33
+ const rows = [...(root?.querySelectorAll('[data-row-id]') || [])].flatMap(row => {
34
+ const user = row.querySelector('[data-v4-user-input-collapsible-content]');
35
+ const assistant = row.classList.contains('group/assistant-row') && row.querySelector('[data-conversation-selectable="true"]');
36
+ const content = user || assistant;
37
+ const id = row.getAttribute('data-row-id');
38
+ let text = content ? content.innerText.trim() : '';
39
+ if (user && id) {
40
+ for (let fiber = user[Object.keys(user).find(key => key.startsWith('__reactFiber'))]; fiber; fiber = fiber.return) {
41
+ if (String(fiber.memoizedProps?.rowId) === id && typeof fiber.memoizedProps?.contentText === 'string') { text = fiber.memoizedProps.contentText.trim(); break; }
42
+ }
43
+ }
44
+ return content && id ? [{ id, role: user ? 'user' : 'assistant', text,
45
+ ...(assistant ? { finished: Boolean(root.querySelector('[data-testid="v4-feedback-like-' + id + '"]')) } : {}) }] : [];
46
+ });
47
+ return {
48
+ sessionId: id && id !== 'draft' ? id : undefined,
49
+ editor: editor?.getAttribute('contenteditable') === 'true', draft: editor?.innerText.replace(/[\\uFEFF\\u200B]/g, '').trim() || '',
50
+ running: running === 'true' || Boolean(root?.querySelector(${JSON.stringify(busySelector)})),
51
+ send: Boolean(root?.querySelector(${JSON.stringify(sendSelector)})),
52
+ error: error?.innerText.trim() || '', errorId: error ? (error.__agentBridgeErrorId ??= crypto.randomUUID()) : undefined, rows,
53
+ completed: running === 'false', interaction,
54
+ };
55
+ })()`;
56
+ exports.zcodeReplyArtifactsScript = `(expected, rowId) => {
57
+ const state = ${exports.zcodeSnapshotScript};
58
+ if (state.error || state.sessionId !== expected.sessionId || JSON.stringify(state.rows.filter(row => row.role === 'user')) !== JSON.stringify(expected.rows.filter(row => row.role === 'user'))) throw new Error('ZCode 任务或回复轮次已变化,未读取附件');
59
+ // 完成标记出现后正文仍可能重绘,同一轮先等下一次稳定快照。
60
+ if (state.running || state.interaction || !state.completed || JSON.stringify(state.rows) !== JSON.stringify(expected.rows)) return null;
61
+ const root = document.querySelector(${JSON.stringify(paneSelector)});
62
+ const row = [...(root?.querySelectorAll('[data-row-id]') || [])].find(item => item.getAttribute('data-row-id') === rowId);
63
+ const content = row?.querySelector('[data-conversation-selectable="true"]');
64
+ const nodes = content?.querySelectorAll('img[data-markdown-image][data-streamdown="image"]') || [];
65
+ if (nodes.length > ${attachments_1.MAX_ATTACHMENTS}) throw new Error('ZCode 回复图片数量超过 8 个');
66
+ let total = 0;
67
+ const images = [...nodes].map(image => {
68
+ const src = image.getAttribute('src') || '';
69
+ const size = (src.length - src.indexOf(',') - 1) / 4 * 3 - (src.endsWith('==') ? 2 : src.endsWith('=') ? 1 : 0);
70
+ if (size > ${attachments_1.MAX_ATTACHMENT_BYTES}) throw new Error('ZCode 单张图片超过 32 MiB');
71
+ total += size;
72
+ if (total > ${attachments_1.MAX_TOTAL_ATTACHMENT_BYTES}) throw new Error('ZCode 图片总大小超过 64 MiB');
73
+ return { src, title: image.closest('[data-image-thumbnail-trigger]')?.getAttribute('title') || undefined };
74
+ });
75
+ const files = new Set();
76
+ for (const node of content?.querySelectorAll('button[title]') || []) {
77
+ const key = Object.keys(node).find(key => key.startsWith('__reactFiber'));
78
+ // 只读取正式 Markdown 文件按钮的已解析路径,不扫描回复或工具输出中的任意路径。
79
+ for (let fiber = node[key]; fiber; fiber = fiber.return) {
80
+ const link = fiber.memoizedProps?.fileLink;
81
+ if (!link) continue;
82
+ if (link.workspaceRemoteSessionId) throw new Error('ZCode 远程工作区文件无法在本机读取');
83
+ if (typeof link.path !== 'string' || link.path !== node.getAttribute('title')) throw new Error('ZCode 文件链接路径已变化');
84
+ files.add(link.path);
85
+ if (files.size > ${attachments_1.MAX_ATTACHMENTS}) throw new Error('单条消息最多包含 8 个附件');
86
+ break;
87
+ }
88
+ }
89
+ return { images, files: [...files] };
90
+ }`;
91
+ class ZcodeCdpClient {
92
+ socket;
93
+ sequence = 0;
94
+ pending = new Map();
95
+ port;
96
+ timeoutMs;
97
+ fetchImpl;
98
+ pendingInteraction;
99
+ constructor(options = {}) {
100
+ this.port = options.port ?? 9231;
101
+ this.timeoutMs = options.timeoutMs ?? 30_000;
102
+ this.fetchImpl = options.fetchImpl ?? fetch;
103
+ }
104
+ connected() { return this.socket?.readyState === WebSocket.OPEN; }
105
+ pendingInteractionSessionId() { return this.pendingInteraction?.turn.sessionId; }
106
+ async connect() {
107
+ if (this.connected())
108
+ return;
109
+ this.close();
110
+ const url = `http://127.0.0.1:${this.port}/json`;
111
+ const signal = AbortSignal.timeout(this.timeoutMs);
112
+ const response = await this.fetchImpl(url, { signal }).catch(error => {
113
+ // 重载桌面后连接池可能残留旧连接,仅重试只读发现请求。
114
+ if (error.cause?.code !== "ECONNRESET")
115
+ throw error;
116
+ return this.fetchImpl(url, { signal });
117
+ });
118
+ if (!response.ok)
119
+ throw new Error(`ZCode CDP /json HTTP ${response.status}`);
120
+ const targets = await response.json();
121
+ const page = Array.isArray(targets) ? targets.find(target => target.type === "page" && /zcode/i.test(`${target.title} ${target.url}`)) : undefined;
122
+ if (!page?.webSocketDebuggerUrl)
123
+ throw new Error(`未找到 ZCode 页面 target(端口 ${this.port})`);
124
+ const socket = new WebSocket(page.webSocketDebuggerUrl);
125
+ this.socket = socket;
126
+ try {
127
+ await new Promise((resolve, reject) => {
128
+ const timer = setTimeout(() => reject(new Error("ZCode CDP WebSocket 连接超时")), this.timeoutMs);
129
+ const finish = (error) => { clearTimeout(timer); error ? reject(error) : resolve(); };
130
+ socket.onopen = () => finish();
131
+ socket.onerror = () => { finish(new Error("ZCode CDP WebSocket 连接失败")); if (this.socket === socket)
132
+ this.close(); };
133
+ socket.onclose = () => { finish(new Error("ZCode CDP 已断开")); if (this.socket === socket)
134
+ this.close(); };
135
+ socket.onmessage = event => {
136
+ let message;
137
+ try {
138
+ message = JSON.parse(String(event.data));
139
+ }
140
+ catch {
141
+ return;
142
+ }
143
+ if (!message || typeof message !== "object")
144
+ return;
145
+ const pending = this.pending.get(message.id);
146
+ if (!pending)
147
+ return;
148
+ this.pending.delete(message.id);
149
+ clearTimeout(pending.timer);
150
+ if (message.error)
151
+ pending.reject(new Error(`ZCode CDP 调用失败:${message.error.message ?? message.error.code}`));
152
+ else
153
+ pending.resolve(message.result);
154
+ };
155
+ });
156
+ }
157
+ catch (error) {
158
+ this.close();
159
+ throw error;
160
+ }
161
+ }
162
+ close() {
163
+ const socket = this.socket;
164
+ this.socket = undefined;
165
+ for (const pending of this.pending.values()) {
166
+ clearTimeout(pending.timer);
167
+ pending.reject(new Error("ZCode CDP 已关闭"));
168
+ }
169
+ this.pending.clear();
170
+ socket?.close();
171
+ }
172
+ async conversationId() { return (await this.snapshot()).sessionId; }
173
+ async createTask() {
174
+ await this.waitFor(async () => (await this.snapshot()).editor, "ZCode 输入框未就绪,请等待任务加载或先登录");
175
+ if (!await this.clickIdle('[data-testid="task-new-button"]'))
176
+ throw new Error("ZCode 新建任务入口未找到");
177
+ await this.waitFor(async () => { const state = await this.snapshot(); return state.editor && !state.sessionId && !state.rows.length && !state.draft && !state.running; }, "ZCode 新任务状态校验失败");
178
+ this.pendingInteraction = undefined;
179
+ }
180
+ async activateConversation(conversationId) {
181
+ if (!conversationId.trim())
182
+ throw new Error("ZCode 会话标识不能为空");
183
+ if ((await this.snapshot()).sessionId === conversationId)
184
+ return;
185
+ await this.waitFor(async () => (await this.snapshot()).editor, "ZCode 输入框未就绪,请等待任务加载或先登录");
186
+ const selector = `[data-testid=${JSON.stringify(`task-item-${conversationId}`)}]`;
187
+ await this.waitFor(async () => {
188
+ if (await this.clickIdle(selector))
189
+ return true;
190
+ if (!await this.clickIdle('[data-testid="conversation-section"] [data-task-list-show-more="true"]'))
191
+ throw new Error("ZCode 未找到原任务,请先在桌面任务列表中加载该任务");
192
+ return false;
193
+ }, "ZCode 原任务列表展开超时");
194
+ await this.waitFor(async () => (await this.snapshot()).sessionId === conversationId, "ZCode 原任务切换未完成");
195
+ }
196
+ async direct(text, timeoutMs, expert, newTask) {
197
+ return (await this.complete({ task: text, timeoutMs }, expert, newTask)).text;
198
+ }
199
+ async complete(request, expert, newTask = false, expectedConversationId) {
200
+ let stage = "准备 ZCode 桌面任务";
201
+ try {
202
+ if (typeof request.task !== "string" || !request.task.trim())
203
+ throw new Error("ZCode 消息不能为空");
204
+ if (request.attachments?.length)
205
+ throw new Error("ZCode 当前仅支持文本消息");
206
+ if (expert)
207
+ throw new Error("ZCode 当前不支持 WorkBuddy 专家选择");
208
+ request.signal?.throwIfAborted();
209
+ if (this.pendingInteraction) {
210
+ stage = "确认 ZCode 权限选择";
211
+ if (this.pendingInteraction.turn.sessionId !== request.sessionId)
212
+ throw new Error("ZCode 正在等待另一个会话的选择");
213
+ return await this.continueInteraction(request, expectedConversationId);
214
+ }
215
+ let before = await this.snapshot();
216
+ if (!before.editor)
217
+ await this.waitFor(async () => { before = await this.snapshot(); return before.editor; }, "ZCode 输入框未就绪,请等待任务加载或先登录", request.signal);
218
+ if (newTask) {
219
+ await this.createTask();
220
+ before = await this.snapshot();
221
+ }
222
+ this.assertIdle(before);
223
+ if (expectedConversationId !== undefined && (before.sessionId ?? null) !== expectedConversationId)
224
+ throw new Error("ZCode 桌面任务已切换,与逻辑会话预期任务不符,未输入消息");
225
+ const beforeIds = before.rows.map(row => row.id);
226
+ stage = "填写 ZCode 输入框";
227
+ await this.evaluate(`document.querySelector(${JSON.stringify(paneSelector)}).querySelector(${JSON.stringify(editorSelector)}).focus()`);
228
+ await this.cdp("Input.insertText", { text: request.task });
229
+ const filled = await this.snapshot();
230
+ if (filled.draft !== request.task.trim() || filled.sessionId !== before.sessionId)
231
+ throw new Error("ZCode 输入内容或任务校验失败,未发送消息");
232
+ request.signal?.throwIfAborted();
233
+ stage = "向 ZCode 提交任务(尚未确认发送,请勿重复提交)";
234
+ await this.waitFor(async () => Boolean(await this.evaluate(`(() => { const root = document.querySelector(${JSON.stringify(paneSelector)}); const button = root?.querySelector(${JSON.stringify(sendSelector)}); return button && !button.disabled; })()`)), "ZCode 发送按钮未就绪", request.signal);
235
+ request.signal?.throwIfAborted();
236
+ const sent = await this.evaluate(`(() => {
237
+ const root = document.querySelector(${JSON.stringify(paneSelector)});
238
+ const editor = root?.querySelector(${JSON.stringify(editorSelector)});
239
+ const button = root?.querySelector(${JSON.stringify(sendSelector)});
240
+ const id = root?.querySelector(${JSON.stringify(sessionSelector)})?.getAttribute('data-session-id');
241
+ if (!button || button.disabled || root.closest('[data-testid="side-pane-full-view-host"]')?.getAttribute('data-running') === 'true' || root.querySelector(${JSON.stringify(busySelector)}) || root.querySelector(${JSON.stringify(permissionSelector)}) || editor?.innerText.trim() !== ${JSON.stringify(request.task.trim())} || (id && id !== 'draft' ? id : undefined) !== ${JSON.stringify(before.sessionId)}) return false;
242
+ button.click(); return true;
243
+ })()`);
244
+ if (!sent)
245
+ throw new Error("ZCode 页面或输入发生变化,未发送消息");
246
+ stage = "等待 ZCode 回复(任务已提交,桌面未被停止,请勿重复提交)";
247
+ return await this.waitForReply(request, { beforeIds, task: request.task.trim(), conversationId: before.sessionId, sessionId: request.sessionId,
248
+ previousError: before.error ? { id: before.errorId, text: before.error } : undefined });
249
+ }
250
+ catch (error) {
251
+ throw new agent_1.AgentOperationError("ZCODE_TASK_FAILED", stage, error);
252
+ }
253
+ }
254
+ async continueInteraction(request, expectedConversationId) {
255
+ const { interaction, turn } = this.pendingInteraction;
256
+ const state = await this.snapshot();
257
+ if (state.sessionId !== turn.conversationId || expectedConversationId !== undefined && (state.sessionId ?? null) !== expectedConversationId)
258
+ throw new Error("ZCode 桌面任务已切换,未确认权限");
259
+ if (!state.interaction) {
260
+ this.pendingInteraction = undefined;
261
+ return this.waitForReply(request, turn);
262
+ }
263
+ if (JSON.stringify(state.interaction) !== JSON.stringify(interaction))
264
+ throw new Error("ZCode 权限面板已变化,请在桌面核对,未确认权限");
265
+ const index = (0, cdp_client_1.interactionChoice)(request.task, interaction.options) ?? (0, cdp_client_1.interactionChoice)(request.task, interaction.labels);
266
+ if (index === undefined)
267
+ return { text: (0, cdp_client_1.formatInteraction)(interaction) };
268
+ // 聚焦只改变选中项;下一次 CDP 调用等 React 更新后才确认,避免误触默认选项。
269
+ const action = (click) => `(() => {
270
+ const state = ${exports.zcodeSnapshotScript};
271
+ if (state.error || state.draft || state.sessionId !== ${JSON.stringify(turn.conversationId)} || JSON.stringify(state.interaction) !== ${JSON.stringify(JSON.stringify(interaction))}) throw new Error('ZCode 任务、草稿或权限面板已变化,未确认权限');
272
+ const user = state.rows.findIndex(row => row.role === 'user' && !${JSON.stringify(turn.beforeIds)}.includes(row.id) && row.text === ${JSON.stringify(turn.task)});
273
+ if (user < 0 || state.rows.slice(user + 1).some(row => row.role === 'user')) throw new Error('ZCode 出现人工追加的用户消息,未确认权限');
274
+ const option = document.querySelector(${JSON.stringify(paneSelector)}).querySelectorAll(${JSON.stringify(permissionSelector)})[${index}];
275
+ if (!option || option.disabled || option.getAttribute('aria-disabled') === 'true') throw new Error('ZCode 权限选项不可用');
276
+ ${click ? "if (option.getAttribute('aria-selected') !== 'true') throw new Error('ZCode 权限选项尚未选中'); option.click();" : "option.focus();"}
277
+ return true;
278
+ })()`;
279
+ request.signal?.throwIfAborted();
280
+ await this.evaluate(action(false));
281
+ request.signal?.throwIfAborted();
282
+ await this.evaluate(action(true));
283
+ this.pendingInteraction = undefined;
284
+ return this.waitForReply(request, turn, interaction.id);
285
+ }
286
+ async waitForReply(request, turn, confirmedId) {
287
+ const beforeIds = new Set(turn.beforeIds);
288
+ const deadline = Date.now() + (request.timeoutMs ?? 3_600_000);
289
+ let previous = "";
290
+ let previousSince = 0;
291
+ while (Date.now() < deadline) {
292
+ request.signal?.throwIfAborted();
293
+ await sleep(200);
294
+ const current = await this.snapshot();
295
+ request.signal?.throwIfAborted();
296
+ if (turn.conversationId && current.sessionId !== turn.conversationId)
297
+ throw new Error("ZCode 桌面任务已切换,未返回其他任务的消息");
298
+ turn.conversationId ??= current.sessionId;
299
+ if (turn.previousError && current.errorId === turn.previousError.id && current.error === turn.previousError.text)
300
+ continue;
301
+ turn.previousError = undefined;
302
+ if (current.error)
303
+ throw new Error(current.error);
304
+ const userIndex = current.rows.findIndex(row => row.role === "user" && !beforeIds.has(row.id) && row.text === turn.task);
305
+ const following = userIndex < 0 ? [] : current.rows.slice(userIndex + 1);
306
+ if (following.some(row => row.role === "user"))
307
+ throw new Error("ZCode 桌面追加了新的用户消息,未混入其他轮次回复");
308
+ const reply = following.filter(row => row.role === "assistant" && !beforeIds.has(row.id)).at(-1);
309
+ if (current.interaction) {
310
+ if (current.interaction.id !== confirmedId && userIndex >= 0 && turn.conversationId) {
311
+ this.pendingInteraction = { interaction: current.interaction, turn };
312
+ return { text: (0, cdp_client_1.formatInteraction)(current.interaction) };
313
+ }
314
+ continue;
315
+ }
316
+ const completed = current.completed && reply?.finished;
317
+ if (!current.running && current.send && completed && reply) {
318
+ const artifacts = await this.evaluate(`(${exports.zcodeReplyArtifactsScript})(${JSON.stringify(current)}, ${JSON.stringify(reply.id)})`);
319
+ request.signal?.throwIfAborted();
320
+ if (!artifacts) {
321
+ previous = "";
322
+ previousSince = 0;
323
+ continue;
324
+ }
325
+ if (reply.text || artifacts.images.length || artifacts.files.length) {
326
+ const signature = JSON.stringify({ text: reply.text, artifacts });
327
+ if (signature === previous) {
328
+ if (Date.now() - previousSince < 1_000)
329
+ continue;
330
+ const attachments = await this.replyFileAttachments(artifacts.files, this.replyAttachments(artifacts.images));
331
+ const verified = await this.evaluate(`(${exports.zcodeReplyArtifactsScript})(${JSON.stringify(current)}, ${JSON.stringify(reply.id)})`);
332
+ if (!verified) {
333
+ previous = "";
334
+ previousSince = 0;
335
+ continue;
336
+ }
337
+ if (JSON.stringify(verified) !== JSON.stringify(artifacts))
338
+ throw new Error("ZCode 回复附件已变化,未回传其他文件");
339
+ request.signal?.throwIfAborted();
340
+ return { text: reply.text || "附件已生成。", ...(attachments.length ? { attachments } : {}) };
341
+ }
342
+ previous = signature;
343
+ previousSince = Date.now();
344
+ continue;
345
+ }
346
+ }
347
+ previous = "";
348
+ previousSince = 0;
349
+ }
350
+ throw new Error("ZCode 回复超时,未确认本轮任务完成;桌面任务未被停止,请勿重复提交");
351
+ }
352
+ assertIdle(state) {
353
+ // 旧轮次的错误横幅不阻止用户重试,本轮错误在提交后回传。
354
+ if (!state.editor)
355
+ throw new Error("ZCode 输入框未就绪,请先登录并打开主任务页面");
356
+ if (state.draft)
357
+ throw new Error("ZCode 有未发送的用户草稿,请先在桌面处理");
358
+ if (state.interaction)
359
+ throw new Error("ZCode 正在等待权限确认,请先处理原任务的选择");
360
+ if (state.running)
361
+ throw new Error("ZCode 正在生成回复,请等待当前任务结束");
362
+ }
363
+ replyAttachments(images) {
364
+ if (!images?.length)
365
+ return [];
366
+ if (images.length > attachments_1.MAX_ATTACHMENTS)
367
+ throw new Error("ZCode 回复图片数量超过 8 个");
368
+ let total = 0;
369
+ const attachments = images.map((image) => {
370
+ if (image.src.length > Math.ceil(attachments_1.MAX_ATTACHMENT_BYTES / 3) * 4 + 64)
371
+ throw new Error("ZCode 单张图片超过 32 MiB");
372
+ const match = /^data:(image\/(?:png|jpeg|webp));base64,([A-Za-z0-9+/]+={0,2})$/i.exec(image.src);
373
+ if (!match)
374
+ throw new Error("ZCode 图片地址不支持");
375
+ const contentType = match[1].toLowerCase();
376
+ const size = match[2].length / 4 * 3 - (match[2].endsWith("==") ? 2 : match[2].endsWith("=") ? 1 : 0);
377
+ if (size > attachments_1.MAX_ATTACHMENT_BYTES)
378
+ throw new Error("ZCode 单张图片超过 32 MiB");
379
+ total += size;
380
+ if (total > attachments_1.MAX_TOTAL_ATTACHMENT_BYTES)
381
+ throw new Error("ZCode 图片总大小超过 64 MiB");
382
+ const data = Buffer.from(match[2], "base64");
383
+ if (data.toString("base64") !== match[2])
384
+ throw new Error("ZCode 图片编码无效");
385
+ const basename = image.title?.split(/[\\/]/).pop() || `zcode-${(0, node_crypto_1.randomUUID)()}.${contentType.slice(6)}`;
386
+ return { kind: "image", data, filename: (0, attachments_1.attachmentFilename)(basename), contentType };
387
+ });
388
+ (0, attachments_1.validateAttachments)(attachments);
389
+ return attachments;
390
+ }
391
+ async replyFileAttachments(paths, attachments) {
392
+ const mimeTypes = {
393
+ ".txt": "text/plain", ".md": "text/markdown", ".csv": "text/csv", ".json": "application/json", ".html": "text/html", ".pdf": "application/pdf",
394
+ ".doc": "application/msword", ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".zip": "application/zip",
395
+ ".mp4": "video/mp4", ".webm": "video/webm", ".mov": "video/quicktime",
396
+ };
397
+ const digest = (data) => (0, node_crypto_1.createHash)("sha256").update(data).digest("hex");
398
+ const seen = new Set();
399
+ for (const path of paths) {
400
+ if (!(0, node_path_1.isAbsolute)(path) || path.startsWith("\\") || path.startsWith("//") || process.platform === "win32" && path.startsWith("/") || /[\u0000-\u001f\u007f]/.test(path))
401
+ throw new Error("ZCode 文件链接路径无效");
402
+ const key = (0, node_path_1.normalize)(path);
403
+ if (seen.has(key))
404
+ continue;
405
+ seen.add(key);
406
+ const filename = (0, attachments_1.attachmentFilename)(path);
407
+ let data;
408
+ try {
409
+ const before = await (0, promises_1.lstat)(path);
410
+ if (!before.isFile())
411
+ throw new Error("ZCode 附件不是普通文件");
412
+ const handle = await (0, promises_1.open)(path, node_fs_1.constants.O_RDONLY | (node_fs_1.constants.O_NOFOLLOW ?? 0));
413
+ try {
414
+ const info = await handle.stat();
415
+ if (!info.isFile())
416
+ throw new Error("ZCode 附件不是普通文件");
417
+ if (before.dev !== info.dev || before.ino > 0 && before.ino !== info.ino)
418
+ throw new Error("ZCode 附件路径在读取前已变化");
419
+ if (info.size < 1 || info.size > attachments_1.MAX_ATTACHMENT_BYTES)
420
+ throw new Error("ZCode 附件大小无效,单个附件不能超过 32 MiB");
421
+ const buffer = Buffer.alloc(info.size + 1);
422
+ let offset = 0;
423
+ while (offset < buffer.length) {
424
+ const { bytesRead } = await handle.read(buffer, offset, buffer.length - offset, offset);
425
+ if (!bytesRead)
426
+ break;
427
+ offset += bytesRead;
428
+ }
429
+ const after = await handle.stat();
430
+ if (offset !== info.size || after.size !== info.size || after.mtimeMs !== info.mtimeMs)
431
+ throw new Error("ZCode 附件在读取时发生变化");
432
+ data = buffer.subarray(0, offset);
433
+ }
434
+ finally {
435
+ await handle.close();
436
+ }
437
+ }
438
+ catch (error) {
439
+ throw new Error(`ZCode 文件读取失败:${filename}(${error instanceof Error ? error.message : String(error)})`);
440
+ }
441
+ const imageType = (0, attachments_1.detectImageContentType)(data);
442
+ const contentType = imageType ?? mimeTypes[(0, node_path_1.extname)(path).toLowerCase()] ?? "application/octet-stream";
443
+ if (contentType === "video/mp4" && (data.length < 12 || data.subarray(4, 8).toString() !== "ftyp"))
444
+ throw new Error("ZCode 视频内容与类型不符");
445
+ const duplicate = imageType && attachments.find(item => item.kind === "image" && digest(item.data) === digest(data));
446
+ if (duplicate) {
447
+ duplicate.filename = filename;
448
+ continue;
449
+ }
450
+ attachments.push({ kind: imageType ? "image" : "file", data, filename, contentType });
451
+ (0, attachments_1.validateAttachments)(attachments);
452
+ }
453
+ return attachments;
454
+ }
455
+ async snapshot() { return await this.evaluate(exports.zcodeSnapshotScript); }
456
+ async clickIdle(selector) {
457
+ return await this.evaluate(`(() => {
458
+ const state = ${exports.zcodeSnapshotScript};
459
+ if (!state.editor || state.draft || state.running || state.interaction) throw new Error('ZCode 有草稿、待确认权限、正在生成或页面未就绪,未切换任务');
460
+ const item = document.querySelector(${JSON.stringify(selector)});
461
+ if (!item || item.disabled || item.getAttribute('aria-disabled') === 'true') return false;
462
+ item.click(); return true;
463
+ })()`);
464
+ }
465
+ async waitFor(check, message, signal) {
466
+ const deadline = Date.now() + this.timeoutMs;
467
+ do {
468
+ signal?.throwIfAborted();
469
+ if (await check())
470
+ return;
471
+ signal?.throwIfAborted();
472
+ await sleep(100);
473
+ } while (Date.now() < deadline);
474
+ throw new Error(message);
475
+ }
476
+ async cdp(method, params = {}) {
477
+ if (!this.connected())
478
+ throw new Error("ZCode CDP 尚未连接");
479
+ const id = ++this.sequence;
480
+ return new Promise((resolve, reject) => {
481
+ const timer = setTimeout(() => { this.pending.delete(id); reject(new Error(`ZCode CDP ${method} 超时`)); }, this.timeoutMs);
482
+ this.pending.set(id, { resolve, reject, timer });
483
+ try {
484
+ this.socket.send(JSON.stringify({ id, method, params }));
485
+ }
486
+ catch (error) {
487
+ clearTimeout(timer);
488
+ this.pending.delete(id);
489
+ reject(error);
490
+ }
491
+ });
492
+ }
493
+ async evaluate(expression) {
494
+ const result = await this.cdp("Runtime.evaluate", { expression, returnByValue: true, awaitPromise: true });
495
+ if (result.exceptionDetails)
496
+ throw new Error(`ZCode 页面 JS 异常:${result.exceptionDetails.exception?.description ?? result.exceptionDetails.text}`);
497
+ return result.result?.value;
498
+ }
499
+ }
500
+ exports.ZcodeCdpClient = ZcodeCdpClient;