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.
- package/README.md +12 -8
- package/dist/src-ts/cli.js +67 -37
- package/dist/src-ts/control-server.js +7 -28
- package/dist/src-ts/core-updater.js +141 -33
- package/dist/src-ts/daemon-process.js +2 -1
- package/dist/src-ts/dsh-agent.js +7 -2
- package/dist/src-ts/http-agent.js +36 -0
- package/dist/src-ts/plugin-manager.js +185 -58
- package/dist/src-ts/plugins/abweb/index.js +1 -1
- package/dist/src-ts/plugins/abweb/public/app.js +8 -6
- package/dist/src-ts/plugins/abweb/public/index.html +3 -3
- package/dist/src-ts/plugins/aun/attachments.js +3 -21
- package/dist/src-ts/plugins/aun/index.js +89 -11
- package/dist/src-ts/plugins/aun/messages.js +1 -1
- package/dist/src-ts/plugins/aun/public/app.js +2 -1
- package/dist/src-ts/plugins/aun/public/index.html +1 -1
- package/dist/src-ts/plugins/cdp-draft.js +5 -2
- package/dist/src-ts/plugins/chatgpt/index.js +3 -2
- package/dist/src-ts/plugins/claude-cli/index.js +1 -1
- package/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/dist/src-ts/plugins/doubao/cdp-client.js +231 -45
- package/dist/src-ts/plugins/doubao/index.js +119 -7
- package/dist/src-ts/plugins/doubao/native-task.js +134 -0
- package/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
- package/dist/src-ts/plugins/dsh/index.js +102 -23
- package/dist/src-ts/plugins/dsh/session-history.js +31 -3
- package/dist/src-ts/plugins/feishu/index.js +1 -1
- package/dist/src-ts/plugins/personal-assistant/index.js +29 -8
- package/dist/src-ts/plugins/personal-assistant/model-client.js +20 -6
- package/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
- package/dist/src-ts/plugins/personal-assistant/tools.js +4 -3
- package/dist/src-ts/plugins/qwenwork/cdp-client.js +43 -7
- package/dist/src-ts/plugins/qwenwork/index.js +52 -18
- package/dist/src-ts/plugins/workbuddy/cdp-client.js +349 -83
- package/dist/src-ts/plugins/workbuddy/index.js +24 -12
- package/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
- package/dist/src-ts/plugins/zcode/index.js +97 -0
- package/dist/src-ts/session-management.js +27 -8
- package/dist/src-ts/version-manager.js +6 -3
- package/dist/tests-ts/abweb-model-config.test.js +44 -41
- package/dist/tests-ts/aun-attachments.test.js +19 -17
- package/dist/tests-ts/aun-default-owner-page.test.js +2 -1
- package/dist/tests-ts/aun-messages.test.js +14 -0
- package/dist/tests-ts/aun-plugin.test.js +207 -19
- package/dist/tests-ts/bundled-package.test.js +1 -1
- package/dist/tests-ts/cdp-draft-recovery.test.js +182 -3
- package/dist/tests-ts/chatgpt-plugin.test.js +19 -0
- package/dist/tests-ts/control-server.test.js +20 -0
- package/dist/tests-ts/core-updater.test.js +215 -46
- package/dist/tests-ts/daemon-process.test.js +126 -1
- package/dist/tests-ts/desktop-launch.test.js +73 -0
- package/dist/tests-ts/doubao-native-task.test.js +237 -0
- package/dist/tests-ts/doubao-plugin.test.js +688 -19
- package/dist/tests-ts/dsh-agent.test.js +91 -19
- package/dist/tests-ts/dsh-cdp.test.js +148 -0
- package/dist/tests-ts/dsh-history.test.js +37 -0
- package/dist/tests-ts/feishu-plugin.test.js +1 -1
- package/dist/tests-ts/http-agent.test.js +18 -0
- package/dist/tests-ts/local-cli.test.js +29 -2
- package/dist/tests-ts/model-provider-cancellation.test.js +2 -0
- package/dist/tests-ts/native-session-management.test.js +8 -7
- package/dist/tests-ts/personal-assistant-coordination.test.js +16 -0
- package/dist/tests-ts/personal-assistant-extensions.test.js +7 -1
- package/dist/tests-ts/personal-assistant-memory.test.js +117 -0
- package/dist/tests-ts/personal-assistant.test.js +277 -3
- package/dist/tests-ts/platform-integration.test.js +60 -1
- package/dist/tests-ts/plugin-manager.test.js +78 -6
- package/dist/tests-ts/plugin-upgrade-deadline.test.js +354 -0
- package/dist/tests-ts/publish-packages.test.js +1 -1
- package/dist/tests-ts/qwenwork-plugin.test.js +30 -0
- package/dist/tests-ts/qwenwork-reconnect.test.js +195 -0
- package/dist/tests-ts/qwenwork-sse.test.js +33 -0
- package/dist/tests-ts/session-access-defaults.test.js +112 -0
- package/dist/tests-ts/session-management.test.js +8 -8
- package/dist/tests-ts/version-manager.test.js +40 -0
- package/dist/tests-ts/workbuddy-cdp-recovery.test.js +311 -0
- package/dist/tests-ts/workbuddy-plugin.test.js +356 -13
- package/dist/tests-ts/zcode-cdp.test.js +894 -0
- package/dist/tests-ts/zcode-plugin.test.js +396 -0
- package/package.json +1 -1
- package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +1 -1
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +8 -6
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +7 -5
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +3 -3
- package/plugins/abweb/package.json +1 -1
- package/plugins/abweb/plugin.json +1 -1
- package/plugins/aun/config.schema.json +1 -1
- package/plugins/aun/dist/src-ts/plugins/aun/attachments.js +3 -21
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +89 -11
- package/plugins/aun/dist/src-ts/plugins/aun/messages.js +1 -1
- package/plugins/aun/dist/src-ts/plugins/aun/public/app.js +2 -1
- package/plugins/aun/dist/src-ts/plugins/aun/public/index.html +1 -1
- package/plugins/aun/package.json +1 -1
- package/plugins/aun/plugin.json +1 -1
- package/plugins/chatgpt/dist/src-ts/plugins/cdp-draft.js +5 -2
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +3 -2
- package/plugins/chatgpt/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +1 -1
- package/plugins/claude-cli/package.json +1 -1
- package/plugins/claude-cli/plugin.json +1 -1
- package/plugins/doubao/config.schema.json +2 -1
- package/plugins/doubao/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +231 -45
- package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +119 -7
- package/plugins/doubao/dist/src-ts/plugins/doubao/native-task.js +134 -0
- package/plugins/doubao/package.json +1 -1
- package/plugins/doubao/plugin.json +1 -1
- package/plugins/dsh/config.schema.json +5 -2
- package/plugins/dsh/dist/src-ts/dsh-agent.js +7 -2
- package/plugins/dsh/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/dsh/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
- package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +102 -23
- package/plugins/dsh/dist/src-ts/plugins/dsh/session-history.js +31 -3
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/plugin.json +1 -1
- package/plugins/feishu/dist/src-ts/plugins/feishu/index.js +1 -1
- package/plugins/feishu/package.json +1 -1
- package/plugins/feishu/plugin.json +1 -1
- package/plugins/personal-assistant/config.schema.json +3 -2
- package/plugins/personal-assistant/dist/src-ts/attachments.js +62 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/index.js +29 -8
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/model-client.js +20 -6
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/tools.js +4 -3
- package/plugins/personal-assistant/package.json +2 -2
- package/plugins/personal-assistant/plugin.json +1 -1
- package/plugins/qwenwork/dist/src-ts/plugins/cdp-draft.js +5 -2
- package/plugins/qwenwork/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +43 -7
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +52 -18
- package/plugins/qwenwork/package.json +1 -1
- package/plugins/qwenwork/plugin.json +1 -1
- package/plugins/workbuddy/config.schema.json +1 -1
- package/plugins/workbuddy/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +349 -83
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +24 -12
- package/plugins/workbuddy/package.json +1 -1
- package/plugins/workbuddy/plugin.json +1 -1
- package/plugins/zcode/config.schema.json +13 -0
- package/plugins/zcode/dist/src-ts/agent.js +69 -0
- package/plugins/zcode/dist/src-ts/attachments.js +62 -0
- package/plugins/zcode/dist/src-ts/plugin.js +13 -0
- package/plugins/zcode/dist/src-ts/plugins/desktop-launch.js +238 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/cdp-client.js +896 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/cli-client.js +39 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/desktop-launch.js +10 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/index.js +567 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
- package/plugins/zcode/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
- package/plugins/zcode/dist/src-ts/plugins/zcode/index.js +97 -0
- package/plugins/zcode/package.json +16 -0
- package/plugins/zcode/plugin.json +11 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DoubaoPlugin = exports.doubaoManifest = void 0;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
4
5
|
const promises_1 = require("node:fs/promises");
|
|
5
6
|
const node_path_1 = require("node:path");
|
|
6
7
|
const agent_1 = require("../../agent");
|
|
@@ -11,7 +12,7 @@ const desktop_launch_1 = require("../desktop-launch");
|
|
|
11
12
|
const node_os_1 = require("node:os");
|
|
12
13
|
exports.doubaoManifest = {
|
|
13
14
|
id: "doubao",
|
|
14
|
-
version: "1.0.
|
|
15
|
+
version: "1.0.35",
|
|
15
16
|
displayName: "豆包",
|
|
16
17
|
entry: "dist/index.js",
|
|
17
18
|
apiVersion: "1",
|
|
@@ -19,6 +20,95 @@ exports.doubaoManifest = {
|
|
|
19
20
|
capabilities: ["chat", "health", "model-provider"],
|
|
20
21
|
configSchema: "config.schema.json",
|
|
21
22
|
};
|
|
23
|
+
const doubaoLoginEntries = [
|
|
24
|
+
"Local State",
|
|
25
|
+
"saman_app_state",
|
|
26
|
+
"saman_shell_db_storage",
|
|
27
|
+
"saman_shell_persistent_storage_v1",
|
|
28
|
+
"Default/Network",
|
|
29
|
+
"Default/Local Storage",
|
|
30
|
+
"Default/IndexedDB",
|
|
31
|
+
"Default/Session Storage",
|
|
32
|
+
"Default/WebStorage",
|
|
33
|
+
"Default/DoubaoStorage",
|
|
34
|
+
"Default/Preferences",
|
|
35
|
+
"Default/Secure Preferences",
|
|
36
|
+
"Default/Remote_State",
|
|
37
|
+
"Default/Remote_State_Aha_Chromium",
|
|
38
|
+
];
|
|
39
|
+
function runRobocopy(args) {
|
|
40
|
+
return new Promise((resolve, reject) => (0, node_child_process_1.execFile)("robocopy.exe", args, { windowsHide: true }, error => {
|
|
41
|
+
if (!error)
|
|
42
|
+
return resolve();
|
|
43
|
+
const rawCode = error.code;
|
|
44
|
+
const code = typeof rawCode === "number" ? rawCode : Number(rawCode);
|
|
45
|
+
if (Number.isFinite(code) && code <= 7)
|
|
46
|
+
resolve();
|
|
47
|
+
else
|
|
48
|
+
reject(error);
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
async function copyWithWindowsBackup(from, staged) {
|
|
52
|
+
const sourceInfo = await (0, promises_1.stat)(from);
|
|
53
|
+
const robocopyTarget = `${staged}.robocopy`;
|
|
54
|
+
await (0, promises_1.rm)(robocopyTarget, { recursive: true, force: true });
|
|
55
|
+
await (0, promises_1.mkdir)(robocopyTarget, { recursive: true });
|
|
56
|
+
const args = [
|
|
57
|
+
sourceInfo.isDirectory() ? from : (0, node_path_1.dirname)(from),
|
|
58
|
+
robocopyTarget,
|
|
59
|
+
sourceInfo.isDirectory() ? "*" : (0, node_path_1.basename)(from),
|
|
60
|
+
...(sourceInfo.isDirectory() ? ["/E"] : []),
|
|
61
|
+
"/B", "/COPY:DAT", "/DCOPY:DAT", "/R:0", "/W:0", "/NFL", "/NDL", "/NJH", "/NJS", "/NP", "/XF", "LOCK", "LOG", "LOG.old", "lockfile",
|
|
62
|
+
];
|
|
63
|
+
try {
|
|
64
|
+
await runRobocopy(args);
|
|
65
|
+
const copied = sourceInfo.isDirectory() ? robocopyTarget : (0, node_path_1.join)(robocopyTarget, (0, node_path_1.basename)(from));
|
|
66
|
+
await (0, promises_1.rm)(staged, { recursive: true, force: true });
|
|
67
|
+
await (0, promises_1.rename)(copied, staged);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
await (0, promises_1.rm)(robocopyTarget, { recursive: true, force: true }).catch(() => undefined);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function copyDoubaoLoginState(target) {
|
|
74
|
+
const source = (0, node_path_1.join)(process.env.LOCALAPPDATA ?? (0, node_path_1.join)((0, node_os_1.homedir)(), "AppData", "Local"), "Doubao", "User Data");
|
|
75
|
+
await (0, promises_1.mkdir)(target, { recursive: true });
|
|
76
|
+
for (const entry of doubaoLoginEntries) {
|
|
77
|
+
const from = (0, node_path_1.join)(source, entry);
|
|
78
|
+
const to = (0, node_path_1.join)(target, entry);
|
|
79
|
+
const staged = `${to}.mab-copy`;
|
|
80
|
+
try {
|
|
81
|
+
await (0, promises_1.rm)(staged, { recursive: true, force: true });
|
|
82
|
+
await (0, promises_1.mkdir)((0, node_path_1.dirname)(staged), { recursive: true });
|
|
83
|
+
try {
|
|
84
|
+
await (0, promises_1.cp)(from, staged, {
|
|
85
|
+
recursive: true,
|
|
86
|
+
force: true,
|
|
87
|
+
filter: value => !new Set(["LOCK", "LOG", "LOG.old", "lockfile"]).has(value.split(/[\\/]/).at(-1) ?? ""),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (error.code !== "EBUSY")
|
|
92
|
+
throw error;
|
|
93
|
+
try {
|
|
94
|
+
await copyWithWindowsBackup(from, staged);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
await (0, promises_1.rm)(staged, { recursive: true, force: true }).catch(() => undefined);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
await (0, promises_1.rm)(to, { recursive: true, force: true });
|
|
102
|
+
await (0, promises_1.rename)(staged, to);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
await (0, promises_1.rm)(staged, { recursive: true, force: true }).catch(() => undefined);
|
|
106
|
+
const code = error.code;
|
|
107
|
+
if (code !== "ENOENT" && code !== "EBUSY")
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
22
112
|
class DoubaoPlugin {
|
|
23
113
|
injectedClient;
|
|
24
114
|
manifest = exports.doubaoManifest;
|
|
@@ -63,7 +153,14 @@ class DoubaoPlugin {
|
|
|
63
153
|
conversationId: typeof context.config.conversationId === "string" ? context.config.conversationId : undefined,
|
|
64
154
|
botId: typeof context.config.botId === "string" ? context.config.botId : undefined,
|
|
65
155
|
pluginList: Array.isArray(context.config.pluginList) ? context.config.pluginList.filter((value) => typeof value === "string") : undefined,
|
|
156
|
+
mode: context.config.mode === "chat" ? "chat" : "local-computer",
|
|
66
157
|
timeoutMs: typeof context.config.timeoutMs === "number" ? context.config.timeoutMs : undefined,
|
|
158
|
+
logger: { warn: message => context.logger?.warn?.(message) },
|
|
159
|
+
recoverCdp: async () => {
|
|
160
|
+
if (!this.reconnect)
|
|
161
|
+
throw new Error("豆包插件尚未配置 CDP 恢复流程");
|
|
162
|
+
await this.reconnect();
|
|
163
|
+
},
|
|
67
164
|
});
|
|
68
165
|
this.sessionHistory = new session_history_1.DoubaoSessionHistoryProvider({
|
|
69
166
|
scope: `doubao:${(0, plugin_1.resolveRuntimePort)(context.config, context.runtime, "doubao", "DOUBAO_CDP_PORT", 9227)}`,
|
|
@@ -76,17 +173,30 @@ class DoubaoPlugin {
|
|
|
76
173
|
}
|
|
77
174
|
catch (first) {
|
|
78
175
|
if (context.config.autoStart !== false && !this.injectedClient && process.platform === "win32") {
|
|
176
|
+
if (!(0, agent_1.errorDetails)(first).includes("ECONNREFUSED"))
|
|
177
|
+
throw first;
|
|
79
178
|
const executable = await (0, desktop_launch_1.findWindowsExecutable)("Doubao.exe", typeof context.config.executablePath === "string" ? context.config.executablePath : undefined);
|
|
80
179
|
if (!executable)
|
|
81
180
|
throw new Error("未找到 Doubao.exe,请配置 executablePath");
|
|
82
181
|
const port = (0, plugin_1.resolveRuntimePort)(context.config, context.runtime, "doubao", "DOUBAO_CDP_PORT", 9227);
|
|
83
182
|
context.logger?.warn?.(`正在恢复豆包:${executable},等待 CDP ${port}`);
|
|
84
183
|
try {
|
|
85
|
-
|
|
184
|
+
// 只重启桥接持有的进程;人工实例占用默认目录时使用下方独立目录。
|
|
185
|
+
await (0, desktop_launch_1.restartOwnedAndWait)(this.ownedProcess, executable, [`--remote-debugging-port=${port}`], undefined, () => this.client.connect(), typeof context.config.startupTimeoutMs === "number" ? context.config.startupTimeoutMs : 30_000, child => { this.ownedProcess = child; });
|
|
86
186
|
return;
|
|
87
187
|
}
|
|
88
188
|
catch (error) {
|
|
89
189
|
first = new Error(`已启动豆包,但 CDP ${port} 未就绪:${error instanceof Error ? error.message : String(error)}`);
|
|
190
|
+
const profile = (0, node_path_1.join)(this.dataDir ?? (0, node_path_1.join)((0, node_os_1.homedir)(), ".agent-bridge", "doubao"), "desktop-profile");
|
|
191
|
+
try {
|
|
192
|
+
await copyDoubaoLoginState(profile);
|
|
193
|
+
context.logger?.warn?.(`默认豆包用户目录不可用,改用登录状态副本:${profile}`);
|
|
194
|
+
await (0, desktop_launch_1.restartOwnedAndWait)(this.ownedProcess, executable, [`--remote-debugging-port=${port}`], profile, () => this.client.connect(), typeof context.config.startupTimeoutMs === "number" ? context.config.startupTimeoutMs : 30_000, child => { this.ownedProcess = child; });
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
catch (fallbackError) {
|
|
198
|
+
first = new Error(`已启动豆包,但 CDP ${port} 未就绪:${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`);
|
|
199
|
+
}
|
|
90
200
|
}
|
|
91
201
|
}
|
|
92
202
|
throw first;
|
|
@@ -106,13 +216,14 @@ class DoubaoPlugin {
|
|
|
106
216
|
throw error;
|
|
107
217
|
}
|
|
108
218
|
}
|
|
109
|
-
async deactivate() {
|
|
219
|
+
async deactivate(_reason = "stop", options) {
|
|
110
220
|
await this.queue;
|
|
111
221
|
this.disposer?.();
|
|
112
222
|
this.disposer = undefined;
|
|
113
223
|
this.reconnect = undefined;
|
|
114
224
|
await this.client?.close();
|
|
115
|
-
|
|
225
|
+
if (!options?.preserveDesktop)
|
|
226
|
+
await (0, desktop_launch_1.terminateOwnedProcess)(this.ownedProcess).catch(() => undefined);
|
|
116
227
|
this.ownedProcess = undefined;
|
|
117
228
|
this.client = undefined;
|
|
118
229
|
this.state = "stopped";
|
|
@@ -142,7 +253,7 @@ class DoubaoPlugin {
|
|
|
142
253
|
if (this.state !== "ready" || !this.client)
|
|
143
254
|
throw new Error("豆包插件未就绪");
|
|
144
255
|
if (!this.client.connected())
|
|
145
|
-
await this.reconnect
|
|
256
|
+
await this.reconnect();
|
|
146
257
|
}
|
|
147
258
|
async handle(request) {
|
|
148
259
|
return this.enqueue(async () => {
|
|
@@ -169,9 +280,10 @@ class DoubaoPlugin {
|
|
|
169
280
|
const run = this.enqueue(async () => {
|
|
170
281
|
request.signal.throwIfAborted();
|
|
171
282
|
stage = "连接豆包模型(connect)";
|
|
283
|
+
await this.ensureConnected();
|
|
172
284
|
const client = this.client;
|
|
173
|
-
if (
|
|
174
|
-
throw new Error("
|
|
285
|
+
if (!client?.complete)
|
|
286
|
+
throw new Error("豆包客户端不支持模型调用");
|
|
175
287
|
stage = "等待豆包模型生成(generate)";
|
|
176
288
|
// 空服务端标识会原子创建独立会话,无需切换用户桌面页面。
|
|
177
289
|
const response = await client.complete(request.prompt, request.signal, undefined, this.bindings.get(request.sessionId) ?? "");
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.nativeTaskScript = void 0;
|
|
4
|
+
// shortcut: 依赖桌面端原生模块导出,豆包升级接口后需重新校验;接口缺失时拒绝发送。
|
|
5
|
+
exports.nativeTaskScript = String.raw `(body, readTexts, readArtifacts, getRequestUrl) => {
|
|
6
|
+
window.__mab_doubao_controller?.abort();
|
|
7
|
+
const controller = new AbortController();
|
|
8
|
+
window.__mab_doubao_controller = controller;
|
|
9
|
+
window.__mab_doubao_read_artifacts = undefined;
|
|
10
|
+
window.__mab_doubao_request_id = body.option.unique_key;
|
|
11
|
+
let progressSequence = 0;
|
|
12
|
+
window.__mab_doubao_progress = { requestId: body.option.unique_key, sequence: progressSequence };
|
|
13
|
+
window.__mab_doubao_result = null;
|
|
14
|
+
const finish = result => {
|
|
15
|
+
if (window.__mab_doubao_controller === controller) window.__mab_doubao_result = JSON.stringify(result);
|
|
16
|
+
};
|
|
17
|
+
(async () => {
|
|
18
|
+
window.__mab_doubao_request_url = getRequestUrl().href;
|
|
19
|
+
const chunks = window['@flow-web/desktop:stable'];
|
|
20
|
+
if (!Array.isArray(chunks)) throw Error('豆包本地电脑原生接口不可用,请检查桌面端版本');
|
|
21
|
+
let requireNative;
|
|
22
|
+
chunks.push([['mab_task_' + body.option.unique_key], {}, value => { requireNative = value; }]);
|
|
23
|
+
const factories = new Map(chunks.flatMap(chunk => Object.entries(chunk[1] || {})));
|
|
24
|
+
const load = (marker, extra = '') => {
|
|
25
|
+
for (const [id, factory] of factories) if (String(factory).includes(marker) && String(factory).includes(extra)) return requireNative(id);
|
|
26
|
+
throw Error('豆包本地电脑接口缺失:' + marker);
|
|
27
|
+
};
|
|
28
|
+
const named = name => load(name + ':()=>')[name];
|
|
29
|
+
const only = (module, predicate) => {
|
|
30
|
+
const matches = Object.values(module).filter(value => typeof value === 'function' && predicate(value));
|
|
31
|
+
if (matches.length !== 1) throw Error('豆包本地电脑原生接口不兼容');
|
|
32
|
+
return matches[0];
|
|
33
|
+
};
|
|
34
|
+
const environment = await named('sandboxEnvironmentController').query();
|
|
35
|
+
if (!environment.isReady || !environment.environmentId) throw Error('豆包本地电脑环境未就绪,请在当前桌面登录态中检查本地电脑功能');
|
|
36
|
+
const getStore = only(load('chat-ioc-manager: cannot bind empty store'), value => String(value).includes('it is async store'));
|
|
37
|
+
const state = getStore('chatInputStore').getState();
|
|
38
|
+
const agentMode = named('AgentMode').MOA;
|
|
39
|
+
const mode = state.modeSelectConfigs?.menu_item_list?.find(item => Number(item.agent_mode) === agentMode);
|
|
40
|
+
if (!mode) throw Error('当前豆包登录态未提供任务模式,不能使用本地电脑');
|
|
41
|
+
const modeId = state.modeSelectConfigsV2?.mode_list?.item_list?.find(item => Number(item.agent_mode) === agentMode)?.mode_id || mode.default_mode;
|
|
42
|
+
if (!modeId) throw Error('豆包本地电脑任务模式标识缺失');
|
|
43
|
+
const getAuthorization = only(load('new WeakMap', 'sandboxAuthType:'), value => value.length === 0);
|
|
44
|
+
const authorization = getAuthorization();
|
|
45
|
+
if (!authorization || typeof authorization.sandboxAuthType !== 'number') throw Error('无法读取豆包本地电脑授权设置');
|
|
46
|
+
const getService = only(load('chat-ioc-manager: cannot bind empty service'), value => value.constructor.name === 'AsyncFunction');
|
|
47
|
+
const service = await getService('chatIMService');
|
|
48
|
+
const messageService = await service?.getMessageService();
|
|
49
|
+
if (!messageService?.sendMessage) throw Error('豆包本地电脑消息服务未就绪');
|
|
50
|
+
const scope = body.client_meta.conversation_id || body.client_meta.local_conversation_id;
|
|
51
|
+
const taskApi = load('buildGeneralAgentCompletionTaskParam:()=>');
|
|
52
|
+
const clientOption = await taskApi.buildGeneralAgentCompletionTaskParam(scope);
|
|
53
|
+
if (!clientOption.sandbox_id || !clientOption.workspace) throw Error('豆包本地电脑工作区或运行环境不可用');
|
|
54
|
+
const workspace = clientOption.workspace;
|
|
55
|
+
const runtimeType = taskApi.GeneralAgentRuntimeType.LocalPC;
|
|
56
|
+
const modify = only(load('general_agent_conversation_modify_params'), value => value.constructor.name === 'AsyncFunction');
|
|
57
|
+
const changes = await modify({ completionTaskParam: clientOption, messageConversationId: scope, primaryWorkspace: clientOption.workspace, runtimeType, sandboxAuthType: authorization.sandboxAuthType });
|
|
58
|
+
const general = { ...changes, runtime_type: runtimeType, server_do_compare: true, client_option: {
|
|
59
|
+
...clientOption, sandbox_id: undefined, sandbox_env_id: clientOption.sandbox_id,
|
|
60
|
+
shared_folder_path: [clientOption.workspace], sandbox_auth_type: authorization.sandboxAuthType,
|
|
61
|
+
} };
|
|
62
|
+
const connector = await named('getFeishuConnectorEnabledForConversation')(scope);
|
|
63
|
+
const connectorInfo = only(load('connector_info_list', 'wrapped:!0'), value => {
|
|
64
|
+
const source = String(value); return source.includes('connector_id:') && source.includes('switch:') && source.includes('connector_vendor:');
|
|
65
|
+
});
|
|
66
|
+
const extra = {
|
|
67
|
+
agent_mode: String(agentMode), use_deep_think: String(mode.menu_type ?? mode.item_id), mode_id: String(modeId),
|
|
68
|
+
model_item_key: mode.model_item_key, model_extra_params: JSON.stringify(mode.model_extra_params || {}),
|
|
69
|
+
reasoning_effort: String(mode.reasoning_effort_config?.default_level ?? body.option.model_config.reasoning_effort),
|
|
70
|
+
general_task_param: JSON.stringify(general), mention_plugin_list: body.option.aggregate_params.mention_plugin_list,
|
|
71
|
+
...(connector ? { connector_info_list: JSON.stringify([connectorInfo(connector.enabled, connector.id)]) } : {}),
|
|
72
|
+
...(!body.client_meta.conversation_id ? {
|
|
73
|
+
conversation_init_option: JSON.stringify({ need_ack_conversation: true }),
|
|
74
|
+
conversation_init_ext: JSON.stringify({ mode_id: String(modeId), agent_task_param: JSON.stringify(changes.agent_task_param) }),
|
|
75
|
+
} : {}),
|
|
76
|
+
};
|
|
77
|
+
const create = named('createSendMessage');
|
|
78
|
+
const messages = body.messages.map(item => create({ conversationContext: {
|
|
79
|
+
conversation_id: body.client_meta.conversation_id || undefined,
|
|
80
|
+
local_conversation_id: body.client_meta.conversation_id ? undefined : scope, bot_id: body.client_meta.bot_id,
|
|
81
|
+
}, message: { content_blocks_v2: item.content_block, unique_id: item.local_message_id, ext: extra } }));
|
|
82
|
+
controller.signal.throwIfAborted();
|
|
83
|
+
const handle = messageService.sendMessage({ messages, sessionId: body.option.unique_key, session: { reportParams: { is_local_computer_on: 1 } }, sendMode: 'normal' });
|
|
84
|
+
controller.signal.addEventListener('abort', () => { Promise.resolve().then(() => handle.cancel()).catch(() => {}); }, { once: true });
|
|
85
|
+
const localIds = new Set(body.messages.map(item => item.local_message_id)), replies = new Map();
|
|
86
|
+
const questionIds = new Set(), messageIds = new Set();
|
|
87
|
+
let expectVideo = false, conversationId = body.client_meta.conversation_id;
|
|
88
|
+
const collect = message => {
|
|
89
|
+
const statuses = Object.values(message.final_status || {});
|
|
90
|
+
if (statuses.some(status => ['Broken', 'CancelError', 'Cancelled', 'Canceled'].includes(status))) throw Error('豆包本地电脑任务已中止');
|
|
91
|
+
if (message.error_details?.has_error || statuses.some(status => ['Error', 'PanicError', 'ExpectError'].includes(status))) throw Error(message.error_details?.error_msg || '豆包本地电脑任务执行失败');
|
|
92
|
+
if (message.ext?.has_video_gen === '1' || message.ext?.has_video_gen === 1) expectVideo = true;
|
|
93
|
+
if (message.message_id) {
|
|
94
|
+
messageIds.add(message.message_id);
|
|
95
|
+
replies.set(message.message_id, message.content_blocks_v2 || message.content_blocks || message.content_block || []);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
for await (const message of handle.subscribe()) {
|
|
99
|
+
if (window.__mab_doubao_controller === controller) window.__mab_doubao_progress = { requestId: body.option.unique_key, sequence: ++progressSequence };
|
|
100
|
+
if (message.conversation_id) conversationId = message.conversation_id;
|
|
101
|
+
if (localIds.has(message.local_message_id) || localIds.has(message.unique_id)) {
|
|
102
|
+
if (message.error_details?.has_error) throw Error('豆包本地电脑任务发送失败');
|
|
103
|
+
if (message.message_id) questionIds.add(message.message_id);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (message.reply_id) questionIds.add(message.reply_id);
|
|
107
|
+
collect(message);
|
|
108
|
+
}
|
|
109
|
+
const mediaSnapshot = () => {
|
|
110
|
+
const media = readArtifacts([...replies.values()], workspace);
|
|
111
|
+
return { ...media, pending: media.pending || (expectVideo && !media.artifacts.some(item => item.kind === 'video')) };
|
|
112
|
+
};
|
|
113
|
+
// 异步媒体按本轮引用读取原生消息列表,不依赖人工当前打开的会话。
|
|
114
|
+
if (window.__mab_doubao_controller !== controller) throw Error('豆包本地电脑原任务上下文已变化');
|
|
115
|
+
window.__mab_doubao_read_artifacts = async requestId => {
|
|
116
|
+
if (requestId !== body.option.unique_key || window.__mab_doubao_controller !== controller) throw Error('豆包本地电脑原任务上下文已变化');
|
|
117
|
+
controller.signal.throwIfAborted();
|
|
118
|
+
if (typeof messageService.fetchAndUpdateMessageList !== 'function') throw Error('豆包本地电脑异步产物读取接口不可用');
|
|
119
|
+
const page = await messageService.fetchAndUpdateMessageList({ conversationId, options: { limit: 50 } });
|
|
120
|
+
for (const message of page?.messages || []) {
|
|
121
|
+
if (message.conversation_id !== conversationId) continue;
|
|
122
|
+
const reference = message.ext?.reference_info;
|
|
123
|
+
const match = typeof reference === 'string' ? reference.match(/"referenced_message_id"\s*:\s*(?:"([^"\n]+)"|(\d+))/) : null;
|
|
124
|
+
const referenceId = match?.[1] || match?.[2];
|
|
125
|
+
if (messageIds.has(message.message_id) || questionIds.has(message.reply_id) || questionIds.has(referenceId)) collect(message);
|
|
126
|
+
}
|
|
127
|
+
return mediaSnapshot();
|
|
128
|
+
};
|
|
129
|
+
const media = mediaSnapshot();
|
|
130
|
+
const reply = readTexts([...replies.values()].map(blocks => blocks.filter(block => !block.parent_id && block.control_info?.is_visible !== false))).join('\n').trim();
|
|
131
|
+
if (!reply && !media.artifacts.length && !media.pending) throw Error('豆包本地电脑任务未返回结果');
|
|
132
|
+
finish({ ok: true, reply, conversationId, ...media, requestId: body.option.unique_key, questionIds: [...questionIds], messageIds: [...messageIds] });
|
|
133
|
+
})().catch(error => { finish({ failed: true, requestId: body.option.unique_key, reason: error?.message || String(error) }); });
|
|
134
|
+
}`;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "object",
|
|
3
3
|
"properties": {
|
|
4
|
-
"executablePath": { "type": "string", "description": "
|
|
4
|
+
"executablePath": { "type": "string", "description": "旧配置兼容字段;CDP 插件不启动客户端" },
|
|
5
5
|
"aid": { "type": "string", "description": "复用的 EvolCore 已有 AID" },
|
|
6
|
-
"patchPath": { "type": "string", "description": "
|
|
6
|
+
"patchPath": { "type": "string", "description": "旧配置兼容字段;CDP 桌面请求不读取" },
|
|
7
|
+
"cdpPort": { "type": "integer", "minimum": 1, "maximum": 65535, "default": 9226, "description": "DSH 桌面调试端口" },
|
|
8
|
+
"timeoutMs": { "type": "integer", "minimum": 1000, "default": 240000, "description": "等待 DSH 桌面回复的超时毫秒数" },
|
|
9
|
+
"startupTimeoutMs": { "type": "integer", "minimum": 1000, "default": 10000, "description": "连接 DSH 桌面调试端口的超时毫秒数" },
|
|
7
10
|
"sessionsRoot": { "type": "string", "description": "DSH 官方持久化会话目录;默认使用 DSH_HOME/sessions 或用户目录下的 .dsh/sessions" }
|
|
8
11
|
},
|
|
9
12
|
"additionalProperties": false
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DshAgent = void 0;
|
|
4
|
+
exports.readPresentedFiles = readPresentedFiles;
|
|
4
5
|
const node_child_process_1 = require("node:child_process");
|
|
5
6
|
const node_crypto_1 = require("node:crypto");
|
|
6
7
|
const node_fs_1 = require("node:fs");
|
|
@@ -19,7 +20,7 @@ function digest(data) { return (0, node_crypto_1.createHash)("sha256").update(da
|
|
|
19
20
|
function outputContentType(filename, data) {
|
|
20
21
|
return (0, attachments_1.detectImageContentType)(data) ?? outputMimeTypes[(0, node_path_1.extname)(filename).toLowerCase()] ?? "application/octet-stream";
|
|
21
22
|
}
|
|
22
|
-
async function readPresentedFiles(directory, paths, cwd, inputs, returnPaths) {
|
|
23
|
+
async function readPresentedFiles(directory, paths, cwd, inputs = [], returnPaths = []) {
|
|
23
24
|
if (paths.length > attachments_1.MAX_ATTACHMENTS)
|
|
24
25
|
throw new Error("单条消息最多包含 8 个附件");
|
|
25
26
|
const result = [];
|
|
@@ -88,7 +89,11 @@ class DshAgent {
|
|
|
88
89
|
const inputPaths = (request.attachments ?? []).map((item, index) => ({ path: (0, node_path_1.join)(workDirectory, `${index}-${(0, attachments_1.attachmentFilename)(item.filename)}`), returnPath: (0, node_path_1.join)(outputDirectory, `${index}-${(0, attachments_1.attachmentFilename)(item.filename)}`), filename: item.filename, contentType: item.contentType }));
|
|
89
90
|
for (const [index, item] of (request.attachments ?? []).entries())
|
|
90
91
|
await (0, promises_1.writeFile)(inputPaths[index].returnPath, item.data, { mode: 0o600 });
|
|
91
|
-
const task = modelRequest ? request.task :
|
|
92
|
+
const task = modelRequest ? request.task : inputPaths.length
|
|
93
|
+
? `${request.task}\n\n请读取输入附件;如需回传文件,将文件写入 outputDirectory 的直接子文件并调用 present。原附件可直接使用 returnPath。\n附件传输:\n${JSON.stringify({ inputs: inputPaths, outputDirectory })}`
|
|
94
|
+
: request.attachmentOutput
|
|
95
|
+
? `${request.task}\n\n如需回传文件,将文件写入 outputDirectory 的直接子文件并调用 present。\n附件传输:\n${JSON.stringify({ outputDirectory })}`
|
|
96
|
+
: request.task;
|
|
92
97
|
const args = ["--expose-internals", paths.cli, "--profile", "headless", "--patch", paths.patch, "--json"];
|
|
93
98
|
if (request.sessionId && !request.newSession)
|
|
94
99
|
args.push("--session-id", request.sessionId);
|
|
@@ -16,8 +16,8 @@ const node_path_1 = require("node:path");
|
|
|
16
16
|
const node_util_1 = require("node:util");
|
|
17
17
|
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
18
18
|
const versionDirectory = /^(?:app[-_ ]?)?v?\d+(?:[.-]\d+)+$/i;
|
|
19
|
-
/**
|
|
20
|
-
async function findWindowsExecutable(fileName, configured) {
|
|
19
|
+
/** 先从 Windows 注册信息和开始菜单解析,失败时再查常见安装目录。 */
|
|
20
|
+
async function findWindowsExecutable(fileName, configured, run = execFileAsync) {
|
|
21
21
|
if (configured?.trim()) {
|
|
22
22
|
const configuredPath = (0, node_path_1.resolve)(configured);
|
|
23
23
|
if (fileName.toLowerCase() === "doubao.exe") {
|
|
@@ -42,12 +42,16 @@ async function findWindowsExecutable(fileName, configured) {
|
|
|
42
42
|
}
|
|
43
43
|
if (process.platform !== "win32")
|
|
44
44
|
return undefined;
|
|
45
|
-
const
|
|
45
|
+
const registered = await findRegisteredWindowsExecutable(fileName, run);
|
|
46
|
+
if (registered)
|
|
47
|
+
return registered;
|
|
48
|
+
const pattern = /doubao|豆包|qwen|qwwork|千问|deepseek|harness|dsh|zcode/i;
|
|
46
49
|
const roots = [...new Set([
|
|
47
50
|
process.env.DSH_INSTALL_DIR,
|
|
48
51
|
"D:\\Programs\\DeepSeek Harness",
|
|
49
52
|
"D:\\DSH",
|
|
50
53
|
process.env.LOCALAPPDATA && (0, node_path_1.join)(process.env.LOCALAPPDATA, "Programs"),
|
|
54
|
+
process.env.LOCALAPPDATA,
|
|
51
55
|
process.env.ProgramFiles,
|
|
52
56
|
process.env["ProgramFiles(x86)"],
|
|
53
57
|
...Array.from({ length: 24 }, (_, index) => `${String.fromCharCode(67 + index)}:\\`),
|
|
@@ -59,6 +63,36 @@ async function findWindowsExecutable(fileName, configured) {
|
|
|
59
63
|
}
|
|
60
64
|
return undefined;
|
|
61
65
|
}
|
|
66
|
+
async function findRegisteredWindowsExecutable(fileName, run) {
|
|
67
|
+
const target = fileName.replace(/'/g, "''");
|
|
68
|
+
const script = `$ErrorActionPreference='SilentlyContinue';[Console]::OutputEncoding=[Text.Encoding]::UTF8;$target='${target}'
|
|
69
|
+
function Emit-Path($value){if(-not $value){return};$value=[Environment]::ExpandEnvironmentVariables(([string]$value).Trim());if($value.StartsWith('"')){$end=$value.IndexOf('"',1);if($end -gt 1){$value=$value.Substring(1,$end-1)}}elseif($value -match '^(\S+?\.exe)(?:\s|$)'){$value=$Matches[1]};if($value -match '(?i)\.exe$'){[Console]::WriteLine($value)}}
|
|
70
|
+
$keys=@("HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\$target","HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\$target","HKLM:\\Software\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths\\$target","HKCU:\\Software\\Classes\\Applications\\$target\\shell\\open\\command","HKLM:\\Software\\Classes\\Applications\\$target\\shell\\open\\command")
|
|
71
|
+
foreach($key in $keys){$item=Get-Item $key -ErrorAction SilentlyContinue;if($item){Emit-Path $item.GetValue('')}}
|
|
72
|
+
$dirs=@();if($env:APPDATA){$dirs+=[IO.Path]::Combine($env:APPDATA,'Microsoft\\Windows\\Start Menu\\Programs')};if($env:ProgramData){$dirs+=[IO.Path]::Combine($env:ProgramData,'Microsoft\\Windows\\Start Menu\\Programs')};$shell=New-Object -ComObject WScript.Shell
|
|
73
|
+
foreach($dir in $dirs){Get-ChildItem $dir -Filter '*.lnk' -File -Recurse -ErrorAction SilentlyContinue|ForEach-Object{$link=$shell.CreateShortcut($_.FullName);if($link.TargetPath -and [IO.Path]::GetFileName($link.TargetPath) -ieq $target){Emit-Path $link.TargetPath}}}`;
|
|
74
|
+
try {
|
|
75
|
+
const { stdout } = await run("powershell.exe", ["-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(script, "utf16le").toString("base64")], { timeout: 10_000, windowsHide: true });
|
|
76
|
+
for (const candidate of stdout.split(/\r?\n/).map(value => value.trim()).filter(Boolean)) {
|
|
77
|
+
const found = await resolveRegisteredCandidate(fileName, candidate);
|
|
78
|
+
if (found)
|
|
79
|
+
return found;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch { /* 注册表或开始菜单不可读时继续使用目录兜底 */ }
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
async function resolveRegisteredCandidate(fileName, candidate) {
|
|
86
|
+
const candidatePath = (0, node_path_1.resolve)(candidate.replace(/^"|"$/g, ""));
|
|
87
|
+
if (fileName.toLowerCase() === "doubao.exe" && (0, node_path_1.basename)((0, node_path_1.dirname)(candidatePath)).toLowerCase() === "application") {
|
|
88
|
+
const client = (0, node_path_1.join)((0, node_path_1.dirname)(candidatePath), "app", fileName);
|
|
89
|
+
if ((0, node_fs_1.existsSync)(client))
|
|
90
|
+
return client;
|
|
91
|
+
}
|
|
92
|
+
if ((0, node_fs_1.existsSync)(candidatePath) && (0, node_path_1.basename)(candidatePath).toLowerCase() === fileName.toLowerCase())
|
|
93
|
+
return candidatePath;
|
|
94
|
+
return findExecutableInTree((0, node_path_1.dirname)(candidatePath), fileName, /^/, 3);
|
|
95
|
+
}
|
|
62
96
|
async function findExecutableInTree(directory, fileName, pattern, depth) {
|
|
63
97
|
return search(directory, fileName, pattern, depth, pattern.test(directory));
|
|
64
98
|
}
|
|
@@ -104,9 +138,16 @@ async function launchManagedAndWait(executable, args, profileDirectory, connect,
|
|
|
104
138
|
if (profileDirectory)
|
|
105
139
|
await (0, promises_1.mkdir)(profileDirectory, { recursive: true });
|
|
106
140
|
const child = launch(executable, profileDirectory ? [...args, `--user-data-dir=${profileDirectory}`] : args);
|
|
141
|
+
// 先等待 spawn,避免异步 error 终止 daemon 或在启动失败后继续轮询。
|
|
142
|
+
const events = child;
|
|
143
|
+
const started = typeof events.once === "function" ? new Promise((resolve, reject) => {
|
|
144
|
+
events.once("error", reject);
|
|
145
|
+
events.once("spawn", resolve);
|
|
146
|
+
}) : Promise.resolve();
|
|
107
147
|
child.unref();
|
|
108
148
|
onProcess(child);
|
|
109
149
|
try {
|
|
150
|
+
await started;
|
|
110
151
|
return await waitFor(connect, timeoutMs, 250);
|
|
111
152
|
}
|
|
112
153
|
catch (error) {
|