mu-agent-bridge 1.0.71 → 1.0.73
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 +9 -6
- package/dist/src-ts/agent.js +35 -0
- package/dist/src-ts/channel-sessions.js +3 -2
- package/dist/src-ts/control-server.js +5 -2
- package/dist/src-ts/dsh-agent.js +25 -12
- package/dist/src-ts/plugin-catalog.js +11 -2
- package/dist/src-ts/plugin-manager.js +183 -9
- package/dist/src-ts/plugins/abweb/index.js +1 -1
- package/dist/src-ts/plugins/abweb/public/app.js +68 -2
- package/dist/src-ts/plugins/abweb/public/index.html +15 -0
- package/dist/src-ts/plugins/abweb/public/style.css +4 -0
- package/dist/src-ts/plugins/aun/index.js +66 -10
- package/dist/src-ts/plugins/aun/messages.js +3 -0
- package/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
- package/dist/src-ts/plugins/chatgpt/index.js +77 -17
- package/dist/src-ts/plugins/claude-cli/index.js +19 -2
- package/dist/src-ts/plugins/codex-cli/index.js +19 -2
- package/dist/src-ts/plugins/doubao/cdp-client.js +5 -2
- package/dist/src-ts/plugins/doubao/index.js +36 -4
- package/dist/src-ts/plugins/dsh/index.js +78 -6
- package/dist/src-ts/plugins/local-cli/agent.js +74 -9
- package/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
- package/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
- package/dist/src-ts/plugins/personal-assistant/index.js +242 -0
- package/dist/src-ts/plugins/personal-assistant/model-client.js +167 -0
- package/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
- package/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
- package/dist/src-ts/plugins/personal-assistant/tools.js +166 -0
- package/dist/src-ts/plugins/qwenwork/cdp-client.js +47 -11
- package/dist/src-ts/plugins/qwenwork/index.js +89 -8
- package/dist/src-ts/plugins/workbuddy/cdp-client.js +9 -4
- package/dist/src-ts/plugins/workbuddy/index.js +104 -33
- package/dist/src-ts/session-management.js +105 -6
- package/dist/tests-ts/abweb-cli.test.js +10 -2
- package/dist/tests-ts/abweb-model-config.test.js +70 -0
- package/dist/tests-ts/agent.test.js +27 -0
- package/dist/tests-ts/aun-messages.test.js +6 -0
- package/dist/tests-ts/aun-plugin.test.js +85 -0
- package/dist/tests-ts/bundled-package.test.js +12 -2
- package/dist/tests-ts/channel-sessions.test.js +27 -0
- package/dist/tests-ts/chatgpt-plugin.test.js +99 -0
- package/dist/tests-ts/control-server.test.js +36 -0
- package/dist/tests-ts/doubao-plugin.test.js +92 -0
- package/dist/tests-ts/dsh-agent.test.js +77 -0
- package/dist/tests-ts/feishu-plugin.test.js +1 -1
- package/dist/tests-ts/installer-assets.test.js +1 -1
- package/dist/tests-ts/local-cli.test.js +119 -1
- package/dist/tests-ts/model-provider-cancellation.test.js +150 -0
- package/dist/tests-ts/personal-assistant-coordination.test.js +76 -0
- package/dist/tests-ts/personal-assistant-extensions.test.js +29 -0
- package/dist/tests-ts/personal-assistant-owner-context.test.js +107 -0
- package/dist/tests-ts/personal-assistant-remote-ability.test.js +123 -0
- package/dist/tests-ts/personal-assistant.test.js +389 -0
- package/dist/tests-ts/platform-integration.test.js +124 -1
- package/dist/tests-ts/plugin-catalog.test.js +12 -2
- package/dist/tests-ts/plugin-manager.test.js +280 -0
- package/dist/tests-ts/publish-packages.test.js +42 -0
- package/dist/tests-ts/qwenwork-plugin.test.js +105 -1
- package/dist/tests-ts/session-management.test.js +155 -0
- package/dist/tests-ts/workbuddy-plugin.test.js +154 -2
- package/package.json +1 -1
- package/plugins/abweb/dist/src-ts/agent.js +35 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +1 -1
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +68 -2
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +53 -3
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +15 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/style.css +4 -0
- package/plugins/abweb/package.json +1 -1
- package/plugins/abweb/plugin.json +1 -1
- package/plugins/aun/dist/src-ts/agent.js +35 -0
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +66 -10
- package/plugins/aun/dist/src-ts/plugins/aun/messages.js +3 -0
- package/plugins/chatgpt/dist/src-ts/agent.js +35 -0
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +77 -17
- package/plugins/chatgpt/package.json +1 -1
- package/plugins/chatgpt/plugin.json +1 -1
- package/plugins/claude-cli/dist/src-ts/agent.js +35 -0
- package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +19 -2
- package/plugins/claude-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
- package/plugins/claude-cli/package.json +1 -1
- package/plugins/claude-cli/plugin.json +3 -2
- package/plugins/codex-cli/dist/src-ts/agent.js +35 -0
- package/plugins/codex-cli/dist/src-ts/plugins/codex-cli/index.js +19 -2
- package/plugins/codex-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
- package/plugins/codex-cli/package.json +1 -1
- package/plugins/codex-cli/plugin.json +3 -2
- package/plugins/doubao/dist/src-ts/agent.js +35 -0
- package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +5 -2
- package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +36 -4
- package/plugins/doubao/package.json +1 -1
- package/plugins/doubao/plugin.json +3 -2
- package/plugins/dsh/dist/src-ts/agent.js +35 -0
- package/plugins/dsh/dist/src-ts/dsh-agent.js +25 -12
- package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +78 -6
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/plugin.json +3 -2
- package/plugins/feishu/dist/src-ts/agent.js +35 -0
- package/plugins/personal-assistant/config.schema.json +22 -0
- package/plugins/personal-assistant/dist/src-ts/agent.js +69 -0
- package/plugins/personal-assistant/dist/src-ts/plugin.js +13 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/index.js +242 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/model-client.js +167 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/tools.js +166 -0
- package/plugins/personal-assistant/package.json +39 -0
- package/plugins/personal-assistant/plugin.json +15 -0
- package/plugins/qwenwork/dist/src-ts/agent.js +35 -0
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +47 -11
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +89 -8
- package/plugins/qwenwork/package.json +1 -1
- package/plugins/qwenwork/plugin.json +3 -2
- package/plugins/workbuddy/dist/src-ts/agent.js +35 -0
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +9 -4
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +104 -33
- package/plugins/workbuddy/package.json +1 -1
- package/plugins/workbuddy/plugin.json +3 -2
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DoubaoPlugin = exports.doubaoManifest = void 0;
|
|
4
4
|
const promises_1 = require("node:fs/promises");
|
|
5
5
|
const node_path_1 = require("node:path");
|
|
6
|
+
const agent_1 = require("../../agent");
|
|
6
7
|
const cdp_client_1 = require("./cdp-client");
|
|
7
8
|
const session_history_1 = require("./session-history");
|
|
8
9
|
const plugin_1 = require("../../plugin");
|
|
@@ -10,17 +11,22 @@ const desktop_launch_1 = require("../desktop-launch");
|
|
|
10
11
|
const node_os_1 = require("node:os");
|
|
11
12
|
exports.doubaoManifest = {
|
|
12
13
|
id: "doubao",
|
|
13
|
-
version: "1.0.
|
|
14
|
+
version: "1.0.24",
|
|
14
15
|
displayName: "豆包",
|
|
15
16
|
entry: "dist/index.js",
|
|
16
17
|
apiVersion: "1",
|
|
17
18
|
process: "in-process",
|
|
18
|
-
capabilities: ["chat", "health"],
|
|
19
|
+
capabilities: ["chat", "health", "model-provider"],
|
|
19
20
|
configSchema: "config.schema.json",
|
|
20
21
|
};
|
|
21
22
|
class DoubaoPlugin {
|
|
22
23
|
injectedClient;
|
|
23
24
|
manifest = exports.doubaoManifest;
|
|
25
|
+
get modelProvider() {
|
|
26
|
+
if (!this.client?.complete || !this.client.createSession)
|
|
27
|
+
return undefined;
|
|
28
|
+
return { complete: (request) => this.completeModelProvider(request) };
|
|
29
|
+
}
|
|
24
30
|
attachmentInput = true;
|
|
25
31
|
sessionOperations = [];
|
|
26
32
|
sessionHistory;
|
|
@@ -145,9 +151,9 @@ class DoubaoPlugin {
|
|
|
145
151
|
if (request.attachments?.length && !client.complete)
|
|
146
152
|
throw new Error("当前豆包客户端不支持附件输入");
|
|
147
153
|
if (!client.complete)
|
|
148
|
-
return { text: await client.direct(request.task) };
|
|
154
|
+
return { text: await client.direct(request.task, request.signal) };
|
|
149
155
|
const conversationId = request.sessionId ? this.bindings.get(request.sessionId) ?? "" : undefined;
|
|
150
|
-
const response = await client.complete(request.task,
|
|
156
|
+
const response = await client.complete(request.task, request.signal, request.attachments, conversationId);
|
|
151
157
|
if (!request.sessionId)
|
|
152
158
|
return response;
|
|
153
159
|
if (!response.sessionId || !/^\d+$/.test(response.sessionId))
|
|
@@ -156,6 +162,32 @@ class DoubaoPlugin {
|
|
|
156
162
|
return { ...response, sessionId: request.sessionId };
|
|
157
163
|
});
|
|
158
164
|
}
|
|
165
|
+
async completeModelProvider(request) {
|
|
166
|
+
let stage = "豆包模型排队(queue)";
|
|
167
|
+
try {
|
|
168
|
+
request.signal.throwIfAborted();
|
|
169
|
+
const run = this.enqueue(async () => {
|
|
170
|
+
request.signal.throwIfAborted();
|
|
171
|
+
stage = "连接豆包模型(connect)";
|
|
172
|
+
const client = this.client;
|
|
173
|
+
if (this.state !== "ready" || !client?.connected() || !client.complete)
|
|
174
|
+
throw new Error("豆包 CDP 未连接,模型请求不会启动或重启桌面");
|
|
175
|
+
stage = "等待豆包模型生成(generate)";
|
|
176
|
+
// 空服务端标识会原子创建独立会话,无需切换用户桌面页面。
|
|
177
|
+
const response = await client.complete(request.prompt, request.signal, undefined, this.bindings.get(request.sessionId) ?? "");
|
|
178
|
+
if (!response.sessionId || !/^\d+$/.test(response.sessionId))
|
|
179
|
+
throw new Error("豆包未返回有效的服务端会话标识");
|
|
180
|
+
await this.remember(request.sessionId, response.sessionId);
|
|
181
|
+
return { text: response.text };
|
|
182
|
+
});
|
|
183
|
+
return await (0, agent_1.abortable)(run, request.signal);
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
if (error instanceof agent_1.AgentOperationError)
|
|
187
|
+
throw error;
|
|
188
|
+
throw new agent_1.AgentOperationError("DOUBAO_MODEL_FAILED", stage, error);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
159
191
|
enqueue(task) {
|
|
160
192
|
this.pending++;
|
|
161
193
|
const run = this.queue.then(task);
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DshPlugin = exports.dshManifest = void 0;
|
|
4
|
+
const promises_1 = require("node:fs/promises");
|
|
4
5
|
const node_path_1 = require("node:path");
|
|
6
|
+
const agent_1 = require("../../agent");
|
|
5
7
|
const dsh_agent_1 = require("../../dsh-agent");
|
|
6
8
|
const dsh_paths_1 = require("../../dsh-paths");
|
|
7
9
|
const openai_adapter_1 = require("../qwenwork/openai-adapter");
|
|
8
10
|
const session_history_1 = require("./session-history");
|
|
9
11
|
exports.dshManifest = {
|
|
10
12
|
id: "dsh",
|
|
11
|
-
version: "1.0.
|
|
13
|
+
version: "1.0.12",
|
|
12
14
|
displayName: "DeepSeek Harness",
|
|
13
15
|
entry: "dist/index.js",
|
|
14
16
|
apiVersion: "1",
|
|
15
17
|
process: "in-process",
|
|
16
|
-
capabilities: ["chat", "health"],
|
|
18
|
+
capabilities: ["chat", "health", "model-provider"],
|
|
17
19
|
configSchema: "config.schema.json",
|
|
18
20
|
};
|
|
19
21
|
class DshPlugin {
|
|
20
22
|
manifest = exports.dshManifest;
|
|
23
|
+
modelProvider = { complete: (request) => this.completeModelProvider(request) };
|
|
21
24
|
attachmentInput = true;
|
|
22
25
|
sessionOperations = ["new"];
|
|
23
26
|
sessionHistory;
|
|
@@ -26,8 +29,27 @@ class DshPlugin {
|
|
|
26
29
|
message = "插件尚未启动";
|
|
27
30
|
adapter;
|
|
28
31
|
activeRequests = 0;
|
|
32
|
+
queue = Promise.resolve();
|
|
33
|
+
modelSessions = new Map();
|
|
34
|
+
modelSessionsPath;
|
|
29
35
|
unregister = [];
|
|
30
36
|
async activate(context) {
|
|
37
|
+
this.modelSessions.clear();
|
|
38
|
+
this.modelSessionsPath = context.dataDir ? (0, node_path_1.join)(context.dataDir, "model-provider-sessions.json") : undefined;
|
|
39
|
+
if (this.modelSessionsPath) {
|
|
40
|
+
try {
|
|
41
|
+
const saved = JSON.parse(await (0, promises_1.readFile)(this.modelSessionsPath, "utf8"));
|
|
42
|
+
const valid = (value) => typeof value === "string" && value.trim() && value.length <= 512 && !/[\u0000-\u001f]/.test(value);
|
|
43
|
+
if (!saved || typeof saved !== "object" || Array.isArray(saved) || Object.entries(saved).some(([key, value]) => !valid(key) || !valid(value)))
|
|
44
|
+
throw new Error("DSH 个人助理模型会话记录无效");
|
|
45
|
+
for (const [key, value] of Object.entries(saved))
|
|
46
|
+
this.modelSessions.set(key, value);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error.code !== "ENOENT")
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
31
53
|
const configured = context.config.patchPath;
|
|
32
54
|
const patchPath = typeof configured === "string" && configured.trim()
|
|
33
55
|
? configured
|
|
@@ -52,6 +74,7 @@ class DshPlugin {
|
|
|
52
74
|
context.logger?.info?.("dsh plugin activated");
|
|
53
75
|
}
|
|
54
76
|
async deactivate() {
|
|
77
|
+
await this.queue;
|
|
55
78
|
for (const dispose of this.unregister.splice(0))
|
|
56
79
|
dispose();
|
|
57
80
|
await this.adapter?.close();
|
|
@@ -67,12 +90,61 @@ class DshPlugin {
|
|
|
67
90
|
async handle(request) {
|
|
68
91
|
if (!this.agent || this.status !== "ready")
|
|
69
92
|
throw new Error("dsh 插件未就绪");
|
|
70
|
-
this.
|
|
93
|
+
const run = this.queue.then(async () => {
|
|
94
|
+
this.activeRequests++;
|
|
95
|
+
try {
|
|
96
|
+
return await this.agent.send(request);
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
this.activeRequests--;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
this.queue = run.then(() => undefined, () => undefined);
|
|
103
|
+
return run;
|
|
104
|
+
}
|
|
105
|
+
async completeModelProvider(request) {
|
|
106
|
+
let stage = "DSH 模型排队(queue)";
|
|
71
107
|
try {
|
|
72
|
-
|
|
108
|
+
request.signal.throwIfAborted();
|
|
109
|
+
const run = this.queue.then(async () => {
|
|
110
|
+
request.signal.throwIfAborted();
|
|
111
|
+
stage = "连接 DSH 模型(connect)";
|
|
112
|
+
if (!this.agent || this.status !== "ready")
|
|
113
|
+
throw new Error("DSH 插件未就绪");
|
|
114
|
+
this.activeRequests++;
|
|
115
|
+
try {
|
|
116
|
+
const sessionId = this.modelSessions.get(request.sessionId);
|
|
117
|
+
stage = "等待 DSH 模型生成(generate)";
|
|
118
|
+
const response = await this.agent.send({
|
|
119
|
+
task: request.prompt,
|
|
120
|
+
timeoutMs: request.timeoutMs,
|
|
121
|
+
signal: request.signal,
|
|
122
|
+
...(sessionId ? { sessionId } : { newSession: true }),
|
|
123
|
+
}, true);
|
|
124
|
+
if (!sessionId) {
|
|
125
|
+
if (!response.sessionId)
|
|
126
|
+
throw new Error("DSH 未返回个人助理模型会话标识");
|
|
127
|
+
const next = new Map(this.modelSessions).set(request.sessionId, response.sessionId);
|
|
128
|
+
if (this.modelSessionsPath) {
|
|
129
|
+
await (0, promises_1.mkdir)((0, node_path_1.resolve)(this.modelSessionsPath, ".."), { recursive: true });
|
|
130
|
+
await (0, promises_1.writeFile)(`${this.modelSessionsPath}.tmp`, JSON.stringify(Object.fromEntries(next)), { mode: 0o600 });
|
|
131
|
+
await (0, promises_1.rename)(`${this.modelSessionsPath}.tmp`, this.modelSessionsPath);
|
|
132
|
+
}
|
|
133
|
+
this.modelSessions.set(request.sessionId, response.sessionId);
|
|
134
|
+
}
|
|
135
|
+
return { text: response.text };
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
this.activeRequests--;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
this.queue = run.then(() => undefined, () => undefined);
|
|
142
|
+
return await (0, agent_1.abortable)(run, request.signal);
|
|
73
143
|
}
|
|
74
|
-
|
|
75
|
-
|
|
144
|
+
catch (error) {
|
|
145
|
+
if (error instanceof agent_1.AgentOperationError)
|
|
146
|
+
throw error;
|
|
147
|
+
throw new agent_1.AgentOperationError("DSH_MODEL_FAILED", stage, error);
|
|
76
148
|
}
|
|
77
149
|
}
|
|
78
150
|
}
|
|
@@ -6,6 +6,7 @@ const node_child_process_1 = require("node:child_process");
|
|
|
6
6
|
const node_fs_1 = require("node:fs");
|
|
7
7
|
const promises_1 = require("node:fs/promises");
|
|
8
8
|
const node_path_1 = require("node:path");
|
|
9
|
+
const node_os_1 = require("node:os");
|
|
9
10
|
const node_crypto_1 = require("node:crypto");
|
|
10
11
|
const nativeId = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
|
|
11
12
|
const maximumOutput = 4 * 1024 * 1024;
|
|
@@ -22,6 +23,21 @@ function diagnostic(value) {
|
|
|
22
23
|
} })
|
|
23
24
|
.slice(0, 2000);
|
|
24
25
|
}
|
|
26
|
+
function modelDiagnostic(error) {
|
|
27
|
+
const message = String(error);
|
|
28
|
+
const code = /CLI 退出码 (?:\d+|[A-Z][A-Z0-9_]+)/.exec(message)?.[0];
|
|
29
|
+
if (code)
|
|
30
|
+
return `${code}:请检查 CLI 登录、权限和网络`;
|
|
31
|
+
if (message.includes("CLI 调用超时"))
|
|
32
|
+
return "CLI 模型调用超时,请检查登录、网络或权限审批";
|
|
33
|
+
if (message.includes("CLI 请求已取消"))
|
|
34
|
+
return "CLI 模型请求已取消";
|
|
35
|
+
if (message.includes("插件已停止"))
|
|
36
|
+
return "插件已停止,当前 CLI 模型任务已终止";
|
|
37
|
+
if (message.includes("CLI 输出超过"))
|
|
38
|
+
return "CLI 模型输出超过 4MB 限制";
|
|
39
|
+
return "CLI 模型调用或结构化输出失败,请检查 CLI 状态";
|
|
40
|
+
}
|
|
25
41
|
async function resolveWindowsCliShim(file, provider) {
|
|
26
42
|
const modules = (0, node_path_1.basename)((0, node_path_1.dirname)(file)) === ".bin" ? (0, node_path_1.dirname)((0, node_path_1.dirname)(file)) : (0, node_path_1.join)((0, node_path_1.dirname)(file), "node_modules");
|
|
27
43
|
const root = (0, node_path_1.join)(modules, ...(provider === "codex" ? ["@openai", "codex"] : ["@anthropic-ai", "claude-code"]));
|
|
@@ -76,6 +92,8 @@ class LocalCliAgent {
|
|
|
76
92
|
ready = false;
|
|
77
93
|
message = "插件尚未启动";
|
|
78
94
|
version = "";
|
|
95
|
+
modelModeSupported = false;
|
|
96
|
+
get modelInput() { return this.ready && this.modelModeSupported; }
|
|
79
97
|
sessions = new Map();
|
|
80
98
|
writes = Promise.resolve();
|
|
81
99
|
pending = new Map();
|
|
@@ -108,6 +126,9 @@ class LocalCliAgent {
|
|
|
108
126
|
const help = (await this.run(this.provider === "codex" ? ["exec", "--help"] : ["--help"], "", 10000)).stdout;
|
|
109
127
|
if (this.provider === "codex" ? !help.includes("--json") || !help.includes("resume") : !help.includes("--output-format") || !help.includes("--resume"))
|
|
110
128
|
throw new Error("CLI 版本不支持所需的结构化输出和会话恢复,请升级 CLI");
|
|
129
|
+
this.modelModeSupported = this.provider === "claude"
|
|
130
|
+
? ["--json-schema", "--tools", "--strict-mcp-config", "--mcp-config", "--no-session-persistence", "--settings", "--setting-sources"].every(flag => help.includes(flag))
|
|
131
|
+
: ["--output-schema", "--ephemeral", "--sandbox", "--disable", "--config", "--ignore-rules"].every(flag => help.includes(flag));
|
|
111
132
|
this.sessions = new Map();
|
|
112
133
|
try {
|
|
113
134
|
const data = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(context.dataDir, "cli-sessions.json"), "utf8"));
|
|
@@ -128,7 +149,9 @@ class LocalCliAgent {
|
|
|
128
149
|
throw new Error(this.message);
|
|
129
150
|
}
|
|
130
151
|
}
|
|
131
|
-
run(args, input, timeout) {
|
|
152
|
+
run(args, input, timeout, signal) {
|
|
153
|
+
if (signal?.aborted)
|
|
154
|
+
return Promise.reject(new Error("CLI 请求已取消"));
|
|
132
155
|
const command = this.command;
|
|
133
156
|
let child;
|
|
134
157
|
let stop;
|
|
@@ -153,8 +176,10 @@ class LocalCliAgent {
|
|
|
153
176
|
}
|
|
154
177
|
};
|
|
155
178
|
stop = () => terminate("插件已停止,当前 CLI 任务已终止");
|
|
179
|
+
const abort = () => terminate("CLI 请求已取消");
|
|
180
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
156
181
|
const timer = setTimeout(() => terminate(`CLI 调用超时(${timeout}ms),请检查登录、网络或权限审批`), timeout);
|
|
157
|
-
const cleanup = () => { clearTimeout(timer); this.children.delete(child); };
|
|
182
|
+
const cleanup = () => { clearTimeout(timer); signal?.removeEventListener("abort", abort); this.children.delete(child); };
|
|
158
183
|
child.stdout.setEncoding("utf8");
|
|
159
184
|
child.stderr.setEncoding("utf8");
|
|
160
185
|
const collect = (value, error) => {
|
|
@@ -227,35 +252,70 @@ class LocalCliAgent {
|
|
|
227
252
|
throw new Error(`${this.manifest.displayName} 本期不支持附件输入`);
|
|
228
253
|
if (!request.task?.trim())
|
|
229
254
|
throw new Error("CLI 任务文本不能为空");
|
|
255
|
+
return this.executeCli(request, request.modelResponseSchema);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 模型来源的独立入口。调用方只提供最小模型契约,不能通过普通 Agent handle 进入管理路径。
|
|
259
|
+
*/
|
|
260
|
+
async completeStructuredModel(request, responseSchema) {
|
|
261
|
+
if (!request.prompt?.trim())
|
|
262
|
+
throw new Error("模型请求文本不能为空");
|
|
263
|
+
if (!request.sessionId?.trim())
|
|
264
|
+
throw new Error("模型请求会话标识不能为空");
|
|
265
|
+
const response = await this.executeCli({ task: request.prompt, sessionId: request.sessionId, timeoutMs: request.timeoutMs, signal: request.signal }, responseSchema);
|
|
266
|
+
return { text: response.text };
|
|
267
|
+
}
|
|
268
|
+
async executeCli(request, modelSchema) {
|
|
269
|
+
const modelMode = modelSchema !== undefined;
|
|
270
|
+
if (modelMode && !this.modelModeSupported)
|
|
271
|
+
throw new Error("当前 CLI 不支持无工具结构化模型请求,请升级 CLI");
|
|
272
|
+
const schema = modelMode ? JSON.stringify(modelSchema) : undefined;
|
|
273
|
+
if (modelMode && (!modelSchema || typeof modelSchema !== "object" || Array.isArray(modelSchema) || !schema || Buffer.byteLength(schema) > 64_000))
|
|
274
|
+
throw new Error("模型输出 Schema 无效或过大");
|
|
230
275
|
const logical = request.sessionId || (0, node_crypto_1.randomUUID)();
|
|
231
276
|
return this.serial(logical, async () => {
|
|
232
277
|
this.requireReady();
|
|
233
|
-
const id = request.newSession ? undefined : this.sessions.get(logical);
|
|
278
|
+
const id = modelMode || request.newSession ? undefined : this.sessions.get(logical);
|
|
279
|
+
let schemaFile;
|
|
280
|
+
let schemaDirectory;
|
|
234
281
|
const args = this.provider === "codex"
|
|
235
282
|
? ["exec", ...(id ? ["resume", id] : []), "--json", "-"]
|
|
236
283
|
: ["--print", "--output-format", "json", ...(id ? ["--resume", id] : [])];
|
|
284
|
+
if (modelMode && this.provider === "claude")
|
|
285
|
+
args.push("--json-schema", schema, "--tools", "", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--settings", '{"disableAllHooks":true}', "--setting-sources", "user", "--no-session-persistence");
|
|
237
286
|
const timeout = request.timeoutMs ?? this.timeoutMs;
|
|
238
287
|
if (!Number.isInteger(timeout) || timeout <= 0 || timeout > 2147483647)
|
|
239
288
|
throw new Error("任务超时必须是有效的正整数毫秒数");
|
|
240
289
|
const started = Date.now();
|
|
241
290
|
this.context.logger.info("本地 CLI 任务开始", { plugin: this.manifest.id, sessionId: logical, resume: Boolean(id) });
|
|
242
291
|
try {
|
|
243
|
-
|
|
244
|
-
|
|
292
|
+
if (modelMode && this.provider === "codex") {
|
|
293
|
+
schemaDirectory = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-codex-model-"));
|
|
294
|
+
schemaFile = (0, node_path_1.join)(schemaDirectory, "response-schema.json");
|
|
295
|
+
await (0, promises_1.writeFile)(schemaFile, schema, { flag: "wx", mode: 0o600 });
|
|
296
|
+
args.splice(-1, 0, "--ephemeral", "--sandbox", "read-only", "--output-schema", schemaFile, "--disable", "shell_tool", "--disable", "apps", "--disable", "plugins", "--disable", "hooks", "--ignore-rules", "--config", "mcp_servers={}");
|
|
297
|
+
}
|
|
298
|
+
const output = await this.run(args, request.task, timeout, request.signal);
|
|
299
|
+
const response = this.parse(output.stdout, modelMode);
|
|
245
300
|
if (id && response.id !== id)
|
|
246
301
|
throw new Error("CLI 恢复返回的会话 ID 与指定会话不一致,保留原会话绑定");
|
|
247
|
-
|
|
302
|
+
if (!modelMode)
|
|
303
|
+
await this.save(logical, response.id);
|
|
248
304
|
this.context.logger.info("本地 CLI 任务完成", { plugin: this.manifest.id, sessionId: logical, durationMs: Date.now() - started, exitCode: 0 });
|
|
249
|
-
return { text: response.text, sessionId: logical };
|
|
305
|
+
return { text: response.text, ...(!modelMode ? { sessionId: logical } : {}) };
|
|
250
306
|
}
|
|
251
307
|
catch (error) {
|
|
252
|
-
const detail = diagnostic(String(error));
|
|
308
|
+
const detail = modelMode ? modelDiagnostic(error) : diagnostic(String(error));
|
|
253
309
|
this.context.logger.error("本地 CLI 任务失败", { plugin: this.manifest.id, sessionId: logical, durationMs: Date.now() - started, error: detail });
|
|
254
310
|
throw new Error(`${this.manifest.displayName}:${detail}`);
|
|
255
311
|
}
|
|
312
|
+
finally {
|
|
313
|
+
if (schemaFile && schemaDirectory)
|
|
314
|
+
await (0, promises_1.rm)(schemaDirectory, { recursive: true, force: true });
|
|
315
|
+
}
|
|
256
316
|
});
|
|
257
317
|
}
|
|
258
|
-
parse(stdout) {
|
|
318
|
+
parse(stdout, modelMode = false) {
|
|
259
319
|
let id = "", text = "";
|
|
260
320
|
try {
|
|
261
321
|
if (this.provider === "codex") {
|
|
@@ -287,6 +347,11 @@ class LocalCliAgent {
|
|
|
287
347
|
throw new Error(JSON.stringify(event.errors ?? event));
|
|
288
348
|
id = event.session_id;
|
|
289
349
|
text = event.result;
|
|
350
|
+
if (modelMode) {
|
|
351
|
+
if (!event.structured_output || typeof event.structured_output !== "object" || Array.isArray(event.structured_output))
|
|
352
|
+
throw new Error("缺少有效结构化模型输出");
|
|
353
|
+
text = JSON.stringify(event.structured_output);
|
|
354
|
+
}
|
|
290
355
|
}
|
|
291
356
|
if (typeof id !== "string" || !nativeId.test(id))
|
|
292
357
|
throw new Error("缺少有效原生会话 ID");
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.responseSchema = void 0;
|
|
4
|
+
exports.claudeCliCompletion = claudeCliCompletion;
|
|
5
|
+
const tools_1 = require("./tools");
|
|
6
|
+
exports.responseSchema = {
|
|
7
|
+
type: "object", additionalProperties: false, required: ["role", "content", "tool_calls"],
|
|
8
|
+
properties: {
|
|
9
|
+
role: { type: "string", enum: ["assistant"] },
|
|
10
|
+
content: { type: ["string", "null"], maxLength: 32_000 },
|
|
11
|
+
tool_calls: { type: "array", maxItems: 8, items: {
|
|
12
|
+
type: "object", additionalProperties: false, required: ["id", "type", "function"],
|
|
13
|
+
properties: {
|
|
14
|
+
id: { type: "string", minLength: 1, maxLength: 128 }, type: { type: "string", enum: ["function"] },
|
|
15
|
+
function: { type: "object", additionalProperties: false, required: ["name", "arguments"], properties: {
|
|
16
|
+
name: { type: "string", enum: [...tools_1.TOOL_NAMES] }, arguments: { type: "string", maxLength: 64_000 },
|
|
17
|
+
} },
|
|
18
|
+
},
|
|
19
|
+
} },
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
function claudeCliCompletion(callPlugin, timeoutMs) {
|
|
23
|
+
return async (messages, signal) => {
|
|
24
|
+
const response = await callPlugin("claude-cli", {
|
|
25
|
+
task: JSON.stringify({ instructions: "你是个人助理的模型推理器。按照 messages 中的系统要求处理最后一条用户消息;tools 仅描述宿主提供的管理工具。需要查询或执行任务时返回 tool_calls,待工具结果返回后再回答;不要编造结果。每次调用使用不同 id。最终回答时 tool_calls 为空数组。仅返回 Schema 规定的 JSON。", messages, tools: (0, tools_1.toolDefinitions)() }),
|
|
26
|
+
modelResponseSchema: exports.responseSchema, timeoutMs, signal,
|
|
27
|
+
});
|
|
28
|
+
return response.text;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createCoordinator = createCoordinator;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
const node_util_1 = require("node:util");
|
|
6
|
+
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
7
|
+
const isAid = (value) => typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{2,255}$/.test(value);
|
|
8
|
+
const isAgentId = (value) => typeof value === "string" && /^[a-z0-9][a-z0-9-]{0,63}$/.test(value);
|
|
9
|
+
const isThreadId = (value) => typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/.test(value);
|
|
10
|
+
function createCoordinator(options) {
|
|
11
|
+
if (!isAid(options.selfAid))
|
|
12
|
+
throw new Error("selfAid 无效");
|
|
13
|
+
const allowed = new Map();
|
|
14
|
+
for (const agent of options.allowedAgents) {
|
|
15
|
+
if (!isAgentId(agent.id) || !Array.isArray(agent.aids) || agent.aids.some((aid) => !isAid(aid)))
|
|
16
|
+
throw new Error("Agent 白名单无效");
|
|
17
|
+
allowed.set(agent.id, new Set(agent.aids));
|
|
18
|
+
}
|
|
19
|
+
const run = options.execFile ?? (async (file, args) => execFileAsync(file, args, { timeout: 30_000, maxBuffer: 1024 * 1024, encoding: "utf8", windowsHide: true }));
|
|
20
|
+
const command = async (args) => {
|
|
21
|
+
const { stdout } = await run(options.ecPath ?? "ec", args);
|
|
22
|
+
let value;
|
|
23
|
+
try {
|
|
24
|
+
value = JSON.parse(stdout);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new Error("EvolCore 返回了无效 JSON");
|
|
28
|
+
}
|
|
29
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || value.ok === false)
|
|
30
|
+
throw new Error("EvolCore 命令失败");
|
|
31
|
+
return value;
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
async dispatch({ agentId, aid, task }) {
|
|
35
|
+
if (!isAgentId(agentId) || !isAid(aid) || !allowed.get(agentId)?.has(aid))
|
|
36
|
+
throw new Error("目标 Agent/AID 不在白名单中");
|
|
37
|
+
if (typeof task !== "string" || !task.trim() || task.length > 20_000)
|
|
38
|
+
throw new Error("任务长度无效");
|
|
39
|
+
const thread = await command(["thread", "create", aid, "--name", `个人助理派活-${agentId}`, "--format", "json"]);
|
|
40
|
+
const threadId = thread.thread_id ?? thread.threadId ?? thread.id;
|
|
41
|
+
if (!isThreadId(threadId))
|
|
42
|
+
throw new Error("EvolCore 未返回有效 threadId");
|
|
43
|
+
await command(["msg", "send", options.selfAid, aid, "--text", task, "--thread", threadId, "--return", "required", "--format", "json"]);
|
|
44
|
+
return { status: "pending", agentId, aid, threadId };
|
|
45
|
+
},
|
|
46
|
+
async getStatus({ aid, threadId }) {
|
|
47
|
+
if (!isAid(aid))
|
|
48
|
+
throw new Error("AID 无效");
|
|
49
|
+
if (!isThreadId(threadId))
|
|
50
|
+
throw new Error("threadId 无效");
|
|
51
|
+
const history = await command(["thread", "history", threadId, "--format", "json"]);
|
|
52
|
+
const state = history.status ?? history.state;
|
|
53
|
+
const status = state === "completed" || state === "failed" || state === "cancelled" ? state : "pending";
|
|
54
|
+
return { status, aid, threadId, history };
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|