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
|
@@ -25,6 +25,7 @@ const desktop_launch_1 = require("./plugins/desktop-launch");
|
|
|
25
25
|
const workbuddy_1 = require("./plugins/workbuddy");
|
|
26
26
|
const logger = { info() { }, warn() { }, error() { } };
|
|
27
27
|
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
28
|
+
const PLUGIN_UPGRADE_WAIT_MS = 2 * 60 * 60 * 1000;
|
|
28
29
|
class PluginManager {
|
|
29
30
|
options;
|
|
30
31
|
controlUrl;
|
|
@@ -220,8 +221,22 @@ class PluginManager {
|
|
|
220
221
|
if (manifest.id !== id || (0, plugin_catalog_1.resolvePluginId)(packageName, meta.agentBridge.id) !== id || manifest.version !== meta.version)
|
|
221
222
|
throw new agent_1.AgentError("npm 包插件 ID 或版本不匹配");
|
|
222
223
|
this.validateCandidate(manifest, meta.agentBridge);
|
|
223
|
-
|
|
224
|
-
|
|
224
|
+
const deadline = new AbortController();
|
|
225
|
+
const cancellation = loaded.cancellation;
|
|
226
|
+
const error = new agent_1.AgentOperationError("PLUGIN_UPGRADE_WAIT_TIMEOUT", "等待旧插件任务结束", new Error("升级已等待两小时,强制结束桥接任务并继续升级"));
|
|
227
|
+
this.log("info", `插件升级等待旧任务结束:${id}`, { waitMs: PLUGIN_UPGRADE_WAIT_MS, deadline: new Date(Date.now() + PLUGIN_UPGRADE_WAIT_MS).toISOString() }, "插件升级");
|
|
228
|
+
const timer = setTimeout(() => {
|
|
229
|
+
this.log("warn", `插件升级等待超时,强制结束桥接任务:${id}`, { code: error.code, waitMs: PLUGIN_UPGRADE_WAIT_MS }, "插件升级");
|
|
230
|
+
cancellation?.abort(error);
|
|
231
|
+
deadline.abort(error);
|
|
232
|
+
}, PLUGIN_UPGRADE_WAIT_MS);
|
|
233
|
+
try {
|
|
234
|
+
await this.stop(id, "reload", deadline.signal);
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
clearTimeout(timer);
|
|
238
|
+
stopped = !loaded.instance;
|
|
239
|
+
}
|
|
225
240
|
if (hadPrefix)
|
|
226
241
|
await (0, promises_1.rename)(prefix, backup);
|
|
227
242
|
await (0, promises_1.rename)(staging, prefix);
|
|
@@ -280,12 +295,12 @@ class PluginManager {
|
|
|
280
295
|
if (!(request.signal instanceof AbortSignal))
|
|
281
296
|
throw new agent_1.AgentError("模型来源请求缺少取消信号");
|
|
282
297
|
// 模型来源只接收最小调用契约,避免把未来扩展字段误传给桌面或 CLI 插件。
|
|
283
|
-
const result = await loaded.instance.modelProvider.complete({
|
|
298
|
+
const result = await this.runPluginTask(loaded, request.signal, signal => loaded.instance.modelProvider.complete({
|
|
284
299
|
prompt: request.prompt,
|
|
285
300
|
sessionId: request.sessionId,
|
|
286
301
|
timeoutMs: request.timeoutMs,
|
|
287
|
-
signal
|
|
288
|
-
});
|
|
302
|
+
signal,
|
|
303
|
+
}));
|
|
289
304
|
if (!result || typeof result.text !== "string" || !result.text.trim() || Buffer.byteLength(result.text) > 1024 * 1024)
|
|
290
305
|
throw new agent_1.AgentError(`模型来源插件“${loaded.record.displayName}”返回空内容或超大响应`);
|
|
291
306
|
return { text: result.text };
|
|
@@ -366,7 +381,7 @@ class PluginManager {
|
|
|
366
381
|
}
|
|
367
382
|
if (loaded.instance) {
|
|
368
383
|
try {
|
|
369
|
-
await loaded.instance.deactivate("reload");
|
|
384
|
+
await loaded.instance.deactivate("reload", this.upgrades.has(id) ? { preserveDesktop: true } : undefined);
|
|
370
385
|
}
|
|
371
386
|
catch (error) {
|
|
372
387
|
this.log("warn", `插件恢复前清理失败:${id}`, { error: error instanceof Error ? error.message : String(error) }, "插件生命周期");
|
|
@@ -396,14 +411,12 @@ class PluginManager {
|
|
|
396
411
|
let instance;
|
|
397
412
|
try {
|
|
398
413
|
instance = await this.load(loaded.record);
|
|
414
|
+
const cancellation = loaded.cancellation = new AbortController();
|
|
415
|
+
loaded.tasks = new Set();
|
|
416
|
+
const http = loaded.http = this.createHttpRegistry(id);
|
|
399
417
|
const dataDir = (0, node_path_1.join)(this.options.stateDir, id);
|
|
400
418
|
await (0, promises_1.mkdir)(dataDir, { recursive: true });
|
|
401
|
-
const
|
|
402
|
-
let config = {};
|
|
403
|
-
try {
|
|
404
|
-
config = JSON.parse(await (0, promises_1.readFile)(configPath, "utf8"));
|
|
405
|
-
}
|
|
406
|
-
catch { /* defaults */ }
|
|
419
|
+
const config = await this.config(id);
|
|
407
420
|
const pluginLogger = {
|
|
408
421
|
info: (message, fields) => { this.log("info", message, fields, id); this.options.logger?.info(message, fields); if (fields?.direction && typeof fields.text === "string")
|
|
409
422
|
this.recordMessage(id, fields.direction, fields.text, typeof fields.peer === "string" ? fields.peer : undefined, typeof fields.agentAid === "string" ? fields.agentAid : undefined); },
|
|
@@ -414,23 +427,33 @@ class PluginManager {
|
|
|
414
427
|
dataDir,
|
|
415
428
|
config,
|
|
416
429
|
logger: pluginLogger,
|
|
417
|
-
http
|
|
430
|
+
http,
|
|
418
431
|
modelInputGuard: true,
|
|
419
432
|
callPlugin: async (targetId, request) => {
|
|
420
433
|
if (targetId === id)
|
|
421
434
|
throw new agent_1.AgentError(`插件不能调用自身: ${id}`);
|
|
422
435
|
if (request.management)
|
|
423
436
|
throw new agent_1.AgentError("插件间调用不能传递管理能力");
|
|
424
|
-
|
|
437
|
+
cancellation.signal.throwIfAborted();
|
|
438
|
+
if (loaded.cancellation !== cancellation || !loaded.instance)
|
|
439
|
+
throw new agent_1.AgentError("插件实例已停止,旧调用已失效");
|
|
440
|
+
return this.handle(targetId, { ...request, signal: request.signal ? AbortSignal.any([request.signal, cancellation.signal]) : cancellation.signal });
|
|
425
441
|
},
|
|
426
442
|
callModelProvider: async (targetId, request) => {
|
|
427
443
|
if (targetId === id)
|
|
428
444
|
throw new agent_1.AgentError(`插件不能调用自身: ${id}`);
|
|
429
|
-
|
|
445
|
+
cancellation.signal.throwIfAborted();
|
|
446
|
+
if (loaded.cancellation !== cancellation || !loaded.instance)
|
|
447
|
+
throw new agent_1.AgentError("插件实例已停止,旧调用已失效");
|
|
448
|
+
return this.completeModelProvider(targetId, { ...request, signal: AbortSignal.any([request.signal, cancellation.signal]) });
|
|
430
449
|
},
|
|
431
450
|
pluginStatus: (targetId) => this.plugins.get(targetId)?.record.status,
|
|
432
451
|
ingress: {
|
|
433
|
-
publish: async (
|
|
452
|
+
publish: message => this.runPluginTask(loaded, undefined, async (signal) => {
|
|
453
|
+
if (loaded.cancellation !== cancellation || loaded.instance !== instance)
|
|
454
|
+
throw new agent_1.AgentError("通道插件实例已停止,旧调用已失效");
|
|
455
|
+
if (loaded.record.status !== "ready")
|
|
456
|
+
throw new agent_1.AgentError("通道插件正在停止或尚未就绪,不再接收新任务");
|
|
434
457
|
if (message.channel !== id)
|
|
435
458
|
throw new agent_1.AgentError("消息来源与通道插件身份不一致");
|
|
436
459
|
const trustedChannel = loaded.record.type === "channel" && (id === "aun" || id === "feishu");
|
|
@@ -444,7 +467,8 @@ class PluginManager {
|
|
|
444
467
|
throw new agent_1.AgentError("当前通道不支持会话管理");
|
|
445
468
|
const current = await this.sessions.current(message, target);
|
|
446
469
|
const result = await this.sessionManagement.command(message.text, { source: current.source, sessionId: current.logicalSessionId });
|
|
447
|
-
|
|
470
|
+
signal.throwIfAborted();
|
|
471
|
+
await (0, agent_1.abortable)(message.reply(result, signal), signal);
|
|
448
472
|
return;
|
|
449
473
|
}
|
|
450
474
|
if (trustedChannel)
|
|
@@ -457,15 +481,17 @@ class PluginManager {
|
|
|
457
481
|
try {
|
|
458
482
|
const agentInstance = this.plugins.get(target)?.instance;
|
|
459
483
|
const control = agentInstance?.sessionOperations?.includes("new")
|
|
460
|
-
? (request) => this.controlSession(target, request)
|
|
484
|
+
? (request) => this.controlSession(target, request, signal)
|
|
461
485
|
: undefined;
|
|
462
486
|
const task = !message.text.trim() && message.attachments?.length ? "请处理收到的附件" : message.text;
|
|
463
487
|
const forwarded = new Set();
|
|
464
488
|
const sent = [];
|
|
465
489
|
const onMessage = async (text) => {
|
|
490
|
+
signal.throwIfAborted();
|
|
466
491
|
if (!text.trim() || forwarded.has(text))
|
|
467
492
|
return;
|
|
468
|
-
await message.reply(text);
|
|
493
|
+
await (0, agent_1.abortable)(message.reply(text, signal), signal);
|
|
494
|
+
signal.throwIfAborted();
|
|
469
495
|
forwarded.add(text);
|
|
470
496
|
sent.push({ text });
|
|
471
497
|
this.recordMessage(target, "reply", text, message.peerId);
|
|
@@ -473,9 +499,10 @@ class PluginManager {
|
|
|
473
499
|
const response = await this.sessions.run(message, target, async (sessionId, newSession, logicalSessionId) => {
|
|
474
500
|
const targetRecord = this.plugins.get(target)?.record;
|
|
475
501
|
const config = targetRecord?.type === "agent" && targetRecord.capabilities.includes("management") ? await this.config(target) : undefined;
|
|
476
|
-
const ability = trustedChannel && id === "aun" && loaded.instance === instance && loaded.record.status === "ready" && Array.isArray(config?.allowedPeers) && (config.allowedPeers.includes("*") || config.allowedPeers.includes(message.peerId))
|
|
477
|
-
? this.createRequestManagement(target, message.peerId, source, logicalSessionId) : undefined;
|
|
478
|
-
|
|
502
|
+
const ability = trustedChannel && id === "aun" && loaded.instance === instance && loaded.record.status === "ready" && Array.isArray(config?.allowedPeers) && (config.allowedPeers.length === 0 || config.allowedPeers.includes("*") || config.allowedPeers.includes(message.peerId))
|
|
503
|
+
? this.createRequestManagement(target, message.peerId, source, logicalSessionId, { signal, onMessage, ...(message.replyAttachments ? { attachmentOutput: true } : {}) }) : undefined;
|
|
504
|
+
signal.throwIfAborted();
|
|
505
|
+
const request = { task, sessionId, signal, onMessage, ...(newSession ? { newSession: true } : {}), ...(message.attachments ? { attachments: message.attachments } : {}), ...(message.replyAttachments ? { attachmentOutput: true } : {}), ...(ability ? { management: ability } : {}) };
|
|
479
506
|
const issued = ability ? this.issuedManagement.get(ability) : undefined;
|
|
480
507
|
if (issued)
|
|
481
508
|
issued.active = true;
|
|
@@ -487,14 +514,16 @@ class PluginManager {
|
|
|
487
514
|
issued.active = false;
|
|
488
515
|
}
|
|
489
516
|
}, control);
|
|
517
|
+
signal.throwIfAborted();
|
|
490
518
|
const attachments = response.attachments?.length ? response.attachments : (0, attachments_1.asAttachments)(response.images);
|
|
491
519
|
(0, attachments_1.validateAttachments)(attachments);
|
|
492
520
|
if (attachments.length) {
|
|
493
521
|
if (!message.replyAttachments) {
|
|
494
|
-
await message.reply("当前通道不支持附件回传");
|
|
522
|
+
await (0, agent_1.abortable)(message.reply("当前通道不支持附件回传", signal), signal);
|
|
495
523
|
throw new agent_1.AgentError("当前通道不支持附件回传");
|
|
496
524
|
}
|
|
497
|
-
await message.replyAttachments(attachments, response.text);
|
|
525
|
+
await (0, agent_1.abortable)(message.replyAttachments(attachments, response.text, signal), signal);
|
|
526
|
+
signal.throwIfAborted();
|
|
498
527
|
sent.push({ text: response.text, attachments });
|
|
499
528
|
this.recordMessage(target, "reply", response.text, message.peerId);
|
|
500
529
|
}
|
|
@@ -526,7 +555,7 @@ class PluginManager {
|
|
|
526
555
|
else
|
|
527
556
|
this.activeIngress.delete(source);
|
|
528
557
|
}
|
|
529
|
-
},
|
|
558
|
+
}),
|
|
530
559
|
},
|
|
531
560
|
runtime: { controlUrl: this.controlUrl, ports: {
|
|
532
561
|
qwenwork: Number(process.env.AGENT_BRIDGE_QWEN_CDP_PORT ?? process.env.QWEN_CDP_PORT ?? "9228"),
|
|
@@ -542,13 +571,15 @@ class PluginManager {
|
|
|
542
571
|
catch (error) {
|
|
543
572
|
if (instance) {
|
|
544
573
|
try {
|
|
545
|
-
await instance.deactivate("reload");
|
|
574
|
+
await instance.deactivate("reload", this.upgrades.has(id) ? { preserveDesktop: true } : undefined);
|
|
546
575
|
}
|
|
547
576
|
catch (cleanupError) {
|
|
548
577
|
this.log("warn", `插件启动失败后的清理失败:${id}`, { error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError) }, "插件生命周期");
|
|
549
578
|
}
|
|
550
579
|
this.unregisterHttpRoutes(id);
|
|
551
580
|
}
|
|
581
|
+
loaded.http?.unregister();
|
|
582
|
+
loaded.cancellation?.abort(new agent_1.AgentError("插件启动失败,调用已取消"));
|
|
552
583
|
loaded.instance = undefined;
|
|
553
584
|
loaded.record.status = "failed";
|
|
554
585
|
loaded.record.error = error instanceof Error ? error.message : String(error);
|
|
@@ -556,17 +587,44 @@ class PluginManager {
|
|
|
556
587
|
throw error;
|
|
557
588
|
}
|
|
558
589
|
}
|
|
559
|
-
async stop(id, reason = "stop") {
|
|
590
|
+
async stop(id, reason = "stop", deadline) {
|
|
560
591
|
const loaded = this.plugins.get(id);
|
|
561
592
|
if (!loaded)
|
|
562
593
|
throw new agent_1.AgentError(`unknown plugin: ${id}`);
|
|
563
594
|
if (loaded.instance) {
|
|
564
595
|
loaded.record.status = "stopping";
|
|
596
|
+
const instance = loaded.instance;
|
|
597
|
+
const stopping = Promise.resolve().then(async () => {
|
|
598
|
+
if (deadline)
|
|
599
|
+
await Promise.allSettled([...(loaded.tasks ?? [])]);
|
|
600
|
+
await instance.deactivate(reason, deadline ? { preserveDesktop: true } : undefined);
|
|
601
|
+
});
|
|
565
602
|
try {
|
|
566
|
-
|
|
603
|
+
try {
|
|
604
|
+
await (0, agent_1.abortable)(stopping, deadline);
|
|
605
|
+
}
|
|
606
|
+
catch (error) {
|
|
607
|
+
if (!deadline?.aborted)
|
|
608
|
+
throw error;
|
|
609
|
+
let cleanupTimer;
|
|
610
|
+
try {
|
|
611
|
+
const cleaned = await Promise.race([
|
|
612
|
+
stopping.then(() => true, () => false),
|
|
613
|
+
new Promise(resolve => { cleanupTimer = setTimeout(() => resolve(false), 5_000); }),
|
|
614
|
+
]);
|
|
615
|
+
if (!cleaned)
|
|
616
|
+
this.log("warn", `旧插件未完成清理,已隔离并继续升级:${id}`, { code: "PLUGIN_UPGRADE_CLEANUP_TIMEOUT" }, "插件升级");
|
|
617
|
+
}
|
|
618
|
+
finally {
|
|
619
|
+
clearTimeout(cleanupTimer);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
567
622
|
}
|
|
568
623
|
finally {
|
|
624
|
+
loaded.http?.unregister();
|
|
569
625
|
this.unregisterHttpRoutes(id);
|
|
626
|
+
if (deadline)
|
|
627
|
+
loaded.cancellation?.abort(new agent_1.AgentError("插件已停止,旧实例调用已取消"));
|
|
570
628
|
loaded.instance = undefined;
|
|
571
629
|
}
|
|
572
630
|
}
|
|
@@ -647,13 +705,31 @@ class PluginManager {
|
|
|
647
705
|
}
|
|
648
706
|
}
|
|
649
707
|
async setAid(id, aid) { await this.saveConfig(id, { ...await this.config(id), aid }); }
|
|
650
|
-
async config(id) {
|
|
651
|
-
|
|
652
|
-
|
|
708
|
+
async config(id) {
|
|
709
|
+
const loaded = this.plugins.get(id);
|
|
710
|
+
if (!loaded)
|
|
711
|
+
throw new agent_1.AgentError(`unknown plugin: ${id}`);
|
|
712
|
+
let defaults = {};
|
|
713
|
+
if (loaded.record.manifest.configSchema) {
|
|
714
|
+
try {
|
|
715
|
+
const schema = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.resolve)(loaded.record.path, loaded.record.manifest.configSchema), "utf8"));
|
|
716
|
+
if (schema.default && typeof schema.default === "object" && !Array.isArray(schema.default))
|
|
717
|
+
defaults = schema.default;
|
|
718
|
+
}
|
|
719
|
+
catch { /* 默认声明不可读时保留已有配置 */ }
|
|
720
|
+
}
|
|
721
|
+
try {
|
|
722
|
+
const saved = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(this.options.stateDir, id, "config.json"), "utf8"));
|
|
723
|
+
if (!saved || typeof saved !== "object" || Array.isArray(saved))
|
|
724
|
+
throw new agent_1.AgentError("插件配置必须是对象");
|
|
725
|
+
return { ...defaults, ...saved };
|
|
726
|
+
}
|
|
727
|
+
catch (error) {
|
|
728
|
+
if (error.code === "ENOENT")
|
|
729
|
+
return defaults;
|
|
730
|
+
throw new agent_1.AgentError(`插件 ${id} 的配置无法读取或格式无效`);
|
|
731
|
+
}
|
|
653
732
|
}
|
|
654
|
-
catch {
|
|
655
|
-
return {};
|
|
656
|
-
} }
|
|
657
733
|
async reloadStrategy(record) { if (!record.manifest.configSchema)
|
|
658
734
|
return "reload"; try {
|
|
659
735
|
const schema = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.resolve)(record.path, record.manifest.configSchema), "utf8"));
|
|
@@ -684,26 +760,28 @@ class PluginManager {
|
|
|
684
760
|
...await (0, agent_availability_1.agentAvailability)(plugin.id, await this.config(plugin.id), plugin.status),
|
|
685
761
|
})));
|
|
686
762
|
}
|
|
687
|
-
createRequestManagement(pluginId, peerId, source, logicalSessionId) {
|
|
763
|
+
createRequestManagement(pluginId, peerId, source, logicalSessionId, output) {
|
|
688
764
|
const issued = { pluginId, instance: this.plugins.get(pluginId)?.instance, active: false };
|
|
765
|
+
const cancellation = this.plugins.get(pluginId)?.cancellation;
|
|
689
766
|
const checkActive = () => {
|
|
690
767
|
const current = this.plugins.get(pluginId);
|
|
691
768
|
if (!issued.active || !current?.instance || current.instance !== issued.instance)
|
|
692
769
|
throw new agent_1.AgentError("管理能力请求已结束或已撤销");
|
|
770
|
+
cancellation?.signal.throwIfAborted();
|
|
693
771
|
};
|
|
694
772
|
const allowed = async (agentId) => {
|
|
695
773
|
checkActive();
|
|
696
774
|
const loaded = this.plugins.get(pluginId);
|
|
697
|
-
if (!loaded?.instance || loaded.record.status !== "ready" || !loaded.record.capabilities.includes("management"))
|
|
775
|
+
if (!loaded?.instance || (loaded.record.status !== "ready" && !(loaded.record.status === "stopping" && this.upgrades.has(pluginId))) || !loaded.record.capabilities.includes("management"))
|
|
698
776
|
throw new agent_1.AgentError("当前插件未声明管理能力或未就绪");
|
|
699
777
|
const config = await this.config(pluginId);
|
|
700
778
|
checkActive();
|
|
701
|
-
const peers =
|
|
702
|
-
if (!peers.includes("*") && !peers.includes(peerId))
|
|
779
|
+
const peers = config.allowedPeers;
|
|
780
|
+
if (!Array.isArray(peers) || (peers.length > 0 && !peers.includes("*") && !peers.includes(peerId)))
|
|
703
781
|
throw new agent_1.AgentError("当前 AUN 身份无权使用管理能力");
|
|
704
782
|
if (agentId !== undefined) {
|
|
705
|
-
const agents =
|
|
706
|
-
if (!agents.includes(agentId))
|
|
783
|
+
const agents = config.allowedAgents;
|
|
784
|
+
if (!Array.isArray(agents) || (agents.length > 0 && !agents.includes("*") && !agents.includes(agentId)))
|
|
707
785
|
throw new agent_1.AgentError("目标 Agent 不在允许列表中");
|
|
708
786
|
}
|
|
709
787
|
return { loaded, config };
|
|
@@ -739,7 +817,7 @@ class PluginManager {
|
|
|
739
817
|
const aids = identitiesAvailable
|
|
740
818
|
? Object.entries(bindings).filter(([aid, bound]) => /^[a-z0-9][a-z0-9_-]{2,63}\.[a-z0-9-]+(?:\.[a-z0-9-]+)+$/.test(aid) && agentIds.has(bound) && bound === record.id).map(([aid]) => aid).sort()
|
|
741
819
|
: [];
|
|
742
|
-
const callable = Boolean(latest?.instance) && latestRecord.type === "agent" && latestRecord.id !== pluginId && latestRecord.status === "ready" && availability.available && !latestRecord.capabilities.includes("management") && (Array.isArray(config.allowedAgents) && config.allowedAgents.includes(record.id));
|
|
820
|
+
const callable = Boolean(latest?.instance) && latestRecord.type === "agent" && latestRecord.id !== pluginId && latestRecord.status === "ready" && availability.available && !latestRecord.capabilities.includes("management") && (Array.isArray(config.allowedAgents) && (config.allowedAgents.length === 0 || config.allowedAgents.includes("*") || config.allowedAgents.includes(record.id)));
|
|
743
821
|
result.push({ id: latestRecord.id, displayName: latestRecord.displayName, status: latestRecord.status, ...availability, reason: !identitiesAvailable && !availability.reason ? "AUN 身份数据不可用" : availability.reason, capabilities: [...latestRecord.capabilities], aids, callable });
|
|
744
822
|
}
|
|
745
823
|
await allowed();
|
|
@@ -759,14 +837,35 @@ class PluginManager {
|
|
|
759
837
|
const sessionId = id === "workbuddy" && target.instance?.sessionOperations?.includes("new")
|
|
760
838
|
? `management-${(0, node_crypto_1.createHash)("sha256").update(`${logicalSessionId}\0${id}`).digest("hex")}`
|
|
761
839
|
: undefined;
|
|
762
|
-
const
|
|
840
|
+
const signal = output?.signal ? AbortSignal.any([output.signal, cancellation.signal]) : cancellation.signal;
|
|
841
|
+
const response = await this.handle(id, { task, ...output, signal, ...(sessionId ? { sessionId } : {}) });
|
|
763
842
|
if (loaded.instance !== this.plugins.get(pluginId)?.instance)
|
|
764
843
|
throw new agent_1.AgentError("管理插件已重载");
|
|
765
844
|
await allowed(id);
|
|
766
845
|
return response;
|
|
767
846
|
},
|
|
768
|
-
listAunIdentities: async () => {
|
|
769
|
-
|
|
847
|
+
listAunIdentities: async () => {
|
|
848
|
+
await allowed();
|
|
849
|
+
const aun = this.plugins.get("aun")?.instance;
|
|
850
|
+
let result;
|
|
851
|
+
if (aun?.aunManagement)
|
|
852
|
+
result = await aun.aunManagement.list();
|
|
853
|
+
else if (aun?.identity) {
|
|
854
|
+
// 旧 AUN 只有身份列表和健康绑定;兼容只读查询,不开放创建或绑定操作。
|
|
855
|
+
const groups = await aun.identity.list();
|
|
856
|
+
const health = await aun.health();
|
|
857
|
+
const aidPattern = /^[A-Za-z0-9][A-Za-z0-9._-]{2,255}$/;
|
|
858
|
+
const aids = [...new Set([...groups.evolcore, ...groups.muAgentBridge, ...(groups.localMuAgentBridge ?? [])])].filter(aid => aidPattern.test(aid)).sort();
|
|
859
|
+
const raw = health.details?.bindings;
|
|
860
|
+
const bindings = raw && typeof raw === "object" && !Array.isArray(raw)
|
|
861
|
+
? Object.fromEntries(Object.entries(raw).filter(([aid, id]) => aidPattern.test(aid) && typeof id === "string" && /^[a-z0-9][a-z0-9-]{0,63}$/.test(id))) : {};
|
|
862
|
+
result = { aids, bindings };
|
|
863
|
+
}
|
|
864
|
+
else
|
|
865
|
+
throw new agent_1.AgentError("AUN 身份管理不可用");
|
|
866
|
+
await allowed();
|
|
867
|
+
return result;
|
|
868
|
+
},
|
|
770
869
|
createAunIdentity: async (input) => { await allowed(); const aun = this.plugins.get("aun")?.instance; if (!aun?.aunManagement)
|
|
771
870
|
throw new agent_1.AgentError("AUN 身份管理不可用"); const result = await aun.aunManagement.create(input); await allowed(); return result; },
|
|
772
871
|
bindAunIdentity: async (input) => { await allowed(input.agentId); const target = this.plugins.get(input.agentId); if (!target?.instance || target.record.type !== "agent" || target.record.status !== "ready")
|
|
@@ -784,6 +883,20 @@ class PluginManager {
|
|
|
784
883
|
async health(id) { const loaded = this.plugins.get(id); if (!loaded)
|
|
785
884
|
throw new agent_1.AgentError(`unknown plugin: ${id}`); if (!loaded.instance)
|
|
786
885
|
return { status: loaded.record.status }; return loaded.instance.health(); }
|
|
886
|
+
async runPluginTask(loaded, upstream, work) {
|
|
887
|
+
const local = loaded.cancellation.signal;
|
|
888
|
+
const signal = upstream ? AbortSignal.any([upstream, local]) : local;
|
|
889
|
+
signal.throwIfAborted();
|
|
890
|
+
const task = (0, agent_1.abortable)(work(signal), signal);
|
|
891
|
+
const tasks = loaded.tasks;
|
|
892
|
+
tasks.add(task);
|
|
893
|
+
try {
|
|
894
|
+
return await task;
|
|
895
|
+
}
|
|
896
|
+
finally {
|
|
897
|
+
tasks.delete(task);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
787
900
|
async handle(id, request) {
|
|
788
901
|
const loaded = this.plugins.get(id);
|
|
789
902
|
if (!loaded?.instance || loaded.record.status !== "ready") {
|
|
@@ -803,17 +916,22 @@ class PluginManager {
|
|
|
803
916
|
(0, attachments_1.validateAttachments)(request.attachments);
|
|
804
917
|
if (request.attachments?.length && !loaded.instance.attachmentInput)
|
|
805
918
|
throw new agent_1.AgentError(`插件 ${id} 不支持接收附件`);
|
|
806
|
-
|
|
919
|
+
const instance = loaded.instance;
|
|
920
|
+
return this.runPluginTask(loaded, request.signal, signal => instance.handle({
|
|
921
|
+
...request, signal,
|
|
922
|
+
...(request.onMessage ? { onMessage: async (text) => { signal.throwIfAborted(); await request.onMessage(text); } } : {}),
|
|
923
|
+
}));
|
|
807
924
|
}
|
|
808
|
-
async controlSession(id, request) {
|
|
925
|
+
async controlSession(id, request, signal) {
|
|
809
926
|
const loaded = this.plugins.get(id);
|
|
810
927
|
if (!loaded?.instance || loaded.record.status !== "ready")
|
|
811
928
|
throw new agent_1.AgentError(`插件 ${id} 未就绪,无法控制会话`);
|
|
812
929
|
if (!loaded.instance.sessionOperations?.includes(request.operation))
|
|
813
930
|
throw new agent_1.AgentError(`插件 ${id} 不支持新建会话`);
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
|
|
931
|
+
const instance = loaded.instance;
|
|
932
|
+
const response = await this.runPluginTask(loaded, signal, async () => instance.controlSession
|
|
933
|
+
? instance.controlSession(request)
|
|
934
|
+
: { text: `${loaded.record.displayName} 已创建新会话`, sessionId: request.sessionId });
|
|
817
935
|
if (typeof response.sessionId !== "string" || !response.sessionId.trim())
|
|
818
936
|
throw new agent_1.AgentError("插件未返回有效的新会话标识,原会话绑定保持不变");
|
|
819
937
|
return response;
|
|
@@ -831,18 +949,25 @@ class PluginManager {
|
|
|
831
949
|
return true;
|
|
832
950
|
}
|
|
833
951
|
createHttpRegistry(pluginId) {
|
|
952
|
+
const disposers = new Set();
|
|
953
|
+
let closed = false;
|
|
954
|
+
const check = () => { if (closed)
|
|
955
|
+
throw new agent_1.AgentError("插件 HTTP 注册表已停止"); };
|
|
956
|
+
const remember = (dispose) => { disposers.add(dispose); return () => { dispose(); disposers.delete(dispose); }; };
|
|
834
957
|
return {
|
|
835
958
|
register: (routeOrMethod, pathOrHandler, maybeHandler) => {
|
|
959
|
+
check();
|
|
836
960
|
if (typeof routeOrMethod === "object")
|
|
837
|
-
return this.registerHttpRoute(pluginId, routeOrMethod.method, routeOrMethod.path, routeOrMethod.handler);
|
|
961
|
+
return remember(this.registerHttpRoute(pluginId, routeOrMethod.method, routeOrMethod.path, routeOrMethod.handler));
|
|
838
962
|
if (typeof pathOrHandler === "string" && maybeHandler)
|
|
839
|
-
return this.registerHttpRoute(pluginId, routeOrMethod, pathOrHandler, maybeHandler);
|
|
963
|
+
return remember(this.registerHttpRoute(pluginId, routeOrMethod, pathOrHandler, maybeHandler));
|
|
840
964
|
if (typeof pathOrHandler === "function")
|
|
841
|
-
return this.registerLegacyRoute(pluginId, routeOrMethod, pathOrHandler);
|
|
965
|
+
return remember(this.registerLegacyRoute(pluginId, routeOrMethod, pathOrHandler));
|
|
842
966
|
throw new agent_1.AgentError("invalid plugin HTTP route");
|
|
843
967
|
},
|
|
844
|
-
registerRoute: (path, handler) => this.registerLegacyRoute(pluginId, path, handler),
|
|
845
|
-
unregister: () =>
|
|
968
|
+
registerRoute: (path, handler) => { check(); return remember(this.registerLegacyRoute(pluginId, path, handler)); },
|
|
969
|
+
unregister: () => { closed = true; for (const dispose of disposers)
|
|
970
|
+
dispose(); disposers.clear(); },
|
|
846
971
|
};
|
|
847
972
|
}
|
|
848
973
|
registerHttpRoute(pluginId, method, path, handler) {
|
|
@@ -854,8 +979,9 @@ class PluginManager {
|
|
|
854
979
|
const existing = this.httpRoutes.get(key);
|
|
855
980
|
if (existing)
|
|
856
981
|
throw new agent_1.AgentError(`HTTP route already registered: ${normalizedMethod} ${normalizedPath}`);
|
|
857
|
-
|
|
858
|
-
|
|
982
|
+
const route = { pluginId, method: normalizedMethod, path: normalizedPath, handler };
|
|
983
|
+
this.httpRoutes.set(key, route);
|
|
984
|
+
return () => { if (this.httpRoutes.get(key) === route)
|
|
859
985
|
this.httpRoutes.delete(key); };
|
|
860
986
|
}
|
|
861
987
|
registerLegacyRoute(pluginId, path, handler) {
|
|
@@ -876,8 +1002,9 @@ class PluginManager {
|
|
|
876
1002
|
response.setHeader(name, value);
|
|
877
1003
|
response.end(result?.body ?? "");
|
|
878
1004
|
};
|
|
879
|
-
|
|
880
|
-
|
|
1005
|
+
const route = { pluginId, method: "*", path: normalizedPath, handler: nativeHandler };
|
|
1006
|
+
this.httpRoutes.set(key, route);
|
|
1007
|
+
return () => { if (this.httpRoutes.get(key) === route)
|
|
881
1008
|
this.httpRoutes.delete(key); };
|
|
882
1009
|
}
|
|
883
1010
|
unregisterHttpRoutes(pluginId) { for (const [key, route] of this.httpRoutes)
|
|
@@ -219,7 +219,8 @@
|
|
|
219
219
|
versionsNode.replaceChildren(...entries.map((entry) => {
|
|
220
220
|
const row = document.createElement("div");
|
|
221
221
|
row.className = "version-row";
|
|
222
|
-
|
|
222
|
+
const state = entry.state === "unpublished" ? "未发布" : entry.state;
|
|
223
|
+
row.textContent = `${entry.packageName} · 当前 ${entry.currentVersion} · ${entry.latestVersion ? `最新 ${entry.latestVersion}` : "最新版本未知"} · ${state}${entry.error ? ` · ${entry.error}` : ""}`;
|
|
223
224
|
return row;
|
|
224
225
|
}));
|
|
225
226
|
if (!entries.length)
|
|
@@ -427,8 +428,8 @@
|
|
|
427
428
|
node("#assistant-api-key").value = "";
|
|
428
429
|
node("#assistant-api-key").placeholder = config.apiKey ? "已配置,留空保留" : "可选,本地免认证服务可留空";
|
|
429
430
|
node("#assistant-timeout").value = String(config.timeoutMs ?? 120000);
|
|
430
|
-
node("#assistant-allowed-peers").value = (config.allowedPeers ?? []).join("\n");
|
|
431
|
-
node("#assistant-allowed-agents").value = (config.allowedAgents ?? []).join("\n");
|
|
431
|
+
node("#assistant-allowed-peers").value = config.allowedPeers?.includes("*") ? "" : (config.allowedPeers ?? []).join("\n");
|
|
432
|
+
node("#assistant-allowed-agents").value = config.allowedAgents?.includes("*") ? "" : (config.allowedAgents ?? []).join("\n");
|
|
432
433
|
node("#assistant-config-error").hidden = true;
|
|
433
434
|
node("#assistant-config-dialog").showModal();
|
|
434
435
|
}
|
|
@@ -457,7 +458,8 @@
|
|
|
457
458
|
if (timeoutMs !== Number(assistantOriginal.timeoutMs ?? 120000))
|
|
458
459
|
body.timeoutMs = timeoutMs;
|
|
459
460
|
for (const [field, selector] of [["allowedPeers", "#assistant-allowed-peers"], ["allowedAgents", "#assistant-allowed-agents"]]) {
|
|
460
|
-
const
|
|
461
|
+
const entries = [...new Set(node(selector).value.split(/[\s,,]+/).filter(Boolean))];
|
|
462
|
+
const values = entries.includes("*") ? [] : entries;
|
|
461
463
|
if (JSON.stringify(values) !== JSON.stringify(assistantOriginal[field]))
|
|
462
464
|
body[field] = values;
|
|
463
465
|
}
|
|
@@ -519,8 +521,8 @@
|
|
|
519
521
|
node("#check-versions").onclick = () => void request("/api/versions/check", "POST", {}).then(() => load()).catch((cause) => error(String(cause)));
|
|
520
522
|
node("#save-update-policy").onclick = () => void request("/api/update-policy", "PATCH", { mode: policyNode.value }).then(() => load()).catch((cause) => error(String(cause)));
|
|
521
523
|
node("#save-plugin-trust").onclick = () => { const button = node("#save-plugin-trust"); button.disabled = true; trustStatusNode.textContent = "正在保存…"; void request("/api/plugin-trust", "PATCH", pluginTrustConfig).then(() => { pluginTrustDirty = false; trustStatusNode.textContent = "信任设置已保存"; return load(false); }).catch((cause) => { trustStatusNode.textContent = String(cause); }).finally(() => { button.disabled = false; }); };
|
|
522
|
-
node("#update-core").onclick = () => { if (confirm("
|
|
523
|
-
void request("/api/update", "POST", {}).then(() => { error("
|
|
524
|
+
node("#update-core").onclick = () => { if (confirm("确认检查后台更新?有更高版本时将升级并自动重启"))
|
|
525
|
+
void request("/api/update", "POST", {}).then(() => { error("后台正在检查更新,有更高版本时升级并重启;结果请查看运行日志"); }).catch((cause) => error(String(cause))); };
|
|
524
526
|
node("#pair-button").onclick = () => {
|
|
525
527
|
const code = pairCodeNode.value.trim();
|
|
526
528
|
if (!/^\d{6}$/.test(code))
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
<label for="assistant-endpoint">OpenAI 兼容地址</label><input id="assistant-endpoint" type="url" placeholder="http://127.0.0.1:11434/v1/chat/completions">
|
|
40
40
|
<label for="assistant-api-key">API 密钥(留空保留已有密钥)</label><input id="assistant-api-key" type="password" autocomplete="new-password">
|
|
41
41
|
<label for="assistant-timeout">模型超时(毫秒)</label><input id="assistant-timeout" type="number" min="1000" max="120000" required>
|
|
42
|
-
<label for="assistant-allowed-peers">允许使用助理的 Peer AID
|
|
43
|
-
<label for="assistant-allowed-agents">允许调用的 Agent 插件 ID
|
|
44
|
-
<p class="muted"
|
|
42
|
+
<label for="assistant-allowed-peers">允许使用助理的 Peer AID(每行一个,留空接受所有)</label><textarea id="assistant-allowed-peers" rows="3" placeholder="留空接受所有 Peer"></textarea>
|
|
43
|
+
<label for="assistant-allowed-agents">允许调用的 Agent 插件 ID(每行一个,留空允许所有)</label><textarea id="assistant-allowed-agents" rows="3" placeholder="留空允许所有已就绪 Agent"></textarea>
|
|
44
|
+
<p class="muted">默认留空,接受所有 Peer 并允许调用所有已就绪的普通 Agent;填写具体名单后才限制。插件来源沿用客户端登录及当前模型,AID 关联请在 Channel 设置中完成。</p>
|
|
45
45
|
<p id="assistant-config-error" class="error" role="alert" hidden></p>
|
|
46
46
|
<div><button id="assistant-config-save" type="submit">保存配置</button><button id="assistant-config-cancel" type="button">取消</button></div>
|
|
47
47
|
</form>
|
|
@@ -250,30 +250,12 @@ async function uploadAunAttachments(client, ownerAid, peerAid, items) {
|
|
|
250
250
|
const objectKey = `shared/${(0, node_crypto_1.randomUUID)()}/${filename.replace(/[^A-Za-z0-9._-]/g, "_")}`;
|
|
251
251
|
const sha256 = digest(item.data);
|
|
252
252
|
const contentType = item.contentType.toLowerCase();
|
|
253
|
-
//
|
|
254
|
-
const video = contentType.startsWith("video/");
|
|
253
|
+
// 会话媒体控件不携带 AID 凭据,沿用 ec msg 的标准可读对象上传方式。
|
|
255
254
|
const ref = { ownerAid, bucket: "default", objectKey, filename, contentType, sizeBytes: item.data.byteLength, sha256 };
|
|
256
|
-
const uploaded = await client.storage.writeBytes(`/${objectKey}`, item.data, { contentType, public:
|
|
255
|
+
const uploaded = await client.storage.writeBytes(`/${objectKey}`, item.data, { contentType, public: true, overwrite: true })
|
|
257
256
|
.catch(error => { throw new agent_1.AgentOperationError("AUN_FS_UPLOAD_FAILED", `上传 AUN FS 文件“${filename}”`, error); });
|
|
258
257
|
checkedMetadata({ ...uploaded, size: uploaded.size ?? uploaded.size_bytes ?? item.data.byteLength }, ref);
|
|
259
|
-
|
|
260
|
-
result.push({ owner_aid: ownerAid, object_key: objectKey, filename, content_type: contentType, size_bytes: item.data.byteLength, sha256 });
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
263
|
-
const share = await client.storage.lowlevel.createShareLink({ owner: ownerAid, bucket: "default", objectKey, allowedAids: [peerAid], expireInSeconds: 86400 })
|
|
264
|
-
.catch(error => { throw new agent_1.AgentOperationError("AUN_FS_SHARE_FAILED", `创建 AUN FS 文件“${filename}”的接收方授权`, error); });
|
|
265
|
-
if (share.owner_aid && share.owner_aid !== ownerAid || share.bucket && share.bucket !== "default" || share.object_key && share.object_key !== objectKey)
|
|
266
|
-
throw new Error("AUN 附件分享对象不一致");
|
|
267
|
-
if (share.allowed_aids !== undefined && (!Array.isArray(share.allowed_aids) || share.allowed_aids.length !== 1 || share.allowed_aids[0] !== peerAid))
|
|
268
|
-
throw new Error("AUN 附件分享授权不一致");
|
|
269
|
-
const shareId = typeof share.share_id === "string" && /^[A-Za-z0-9]{10}$/.test(share.share_id) ? share.share_id : undefined;
|
|
270
|
-
if (share.share_id !== undefined && !shareId)
|
|
271
|
-
throw new Error("AUN 附件分享标识无效");
|
|
272
|
-
const url = typeof share.path_url === "string" && share.path_url ? share.path_url : shareId ? `https://${ownerAid}/${objectKey}?t=${shareId}` : "";
|
|
273
|
-
const token = url ? pathToken(url, ownerAid, objectKey) : undefined;
|
|
274
|
-
if (!token || shareId && shareId !== token)
|
|
275
|
-
throw new Error("AUN 附件分享链接无效");
|
|
276
|
-
result.push({ owner_aid: ownerAid, bucket: "default", object_key: objectKey, url, filename, content_type: item.contentType, size_bytes: item.data.byteLength, sha256 });
|
|
258
|
+
result.push({ owner_aid: ownerAid, object_key: objectKey, filename, content_type: contentType, size_bytes: item.data.byteLength, sha256 });
|
|
277
259
|
}
|
|
278
260
|
return result;
|
|
279
261
|
}
|