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
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# mu-agent-bridge 动态插件平台
|
|
2
2
|
|
|
3
3
|
运行时使用 TypeScript/Node.js,实现一个可动态发现、启动、停止、重载和安装插件的 daemon。
|
|
4
|
-
当前插件包括千问办公 `qwenwork`、DeepSeek Harness `dsh`、豆包 `doubao`、基于 CodeBuddy CLI 的 `workbuddy`、远程 ChatGPT/Codex CDP 插件 `chatgpt`、个人助理管理 Agent `personal-assistant`,以及插件化管理后台 `abweb`。插件之间独立失败,管理 API 和 OpenAI 兼容接口由同一个本地控制服务提供。
|
|
4
|
+
当前插件包括千问办公 `qwenwork`、DeepSeek Harness `dsh`、豆包 `doubao`、基于 CodeBuddy CLI 的 `workbuddy`、ZCode 桌面端 CDP 插件 `zcode`、远程 ChatGPT/Codex CDP 插件 `chatgpt`、个人助理管理 Agent `personal-assistant`,以及插件化管理后台 `abweb`。插件之间独立失败,管理 API 和 OpenAI 兼容接口由同一个本地控制服务提供。
|
|
5
5
|
|
|
6
6
|
## 快速开始
|
|
7
7
|
|
|
@@ -22,7 +22,7 @@ OpenAI 兼容 API 为 `/v1/models` 和 `/v1/chat/completions`。管理页面位
|
|
|
22
22
|
`AGENT_BRIDGE_HOST=0.0.0.0` 和 `AGENT_BRIDGE_TOKEN`,然后访问
|
|
23
23
|
`http://<Windows 主机 IP>:26107/abweb/`。页面使用一次性配置码配对后管理插件;请只在可信网络使用。
|
|
24
24
|
如果 26107 被旧的 mab 进程占用,可执行 `mab restart`;Windows 会扫描并结束同一 CLI 安装路径的全部旧 daemon,并重新启动。`mab stop` 只停止 daemon,
|
|
25
|
-
`mab update`
|
|
25
|
+
`mab update` 和 ABWeb 的后台升级按钮使用相同规则:只有 npmjs 的 `mu-agent-bridge@latest` 高于当前版本时才升级,并启动新版本。全局安装、测试目录和源码目录按同一版本规则处理;测试时请设置高于 npmjs 的版本,避免被正式版覆盖。不会把自己的旧进程误当成新的 daemon,也不会因此默认改用临时端口。
|
|
26
26
|
已有 AID 的连接状态由 AUN 插件查询,只有当前已连接的身份才能关联 Agent。
|
|
27
27
|
本机 AID 来自 AUN 插件自身的密钥库,不依赖 `ec` 命令,`ec stop` 后仍会显示。
|
|
28
28
|
未配置 `aun_path` 时密钥库默认使用共享 AUN 目录(`AUN_HOME`/`EVOLCORE_HOME` 或
|
|
@@ -43,24 +43,26 @@ abweb 的“配置码”栏;配对码 10 分钟有效,只能使用一次,
|
|
|
43
43
|
`AGENT_BRIDGE_PLUGINS_DIR`、`AGENT_BRIDGE_STATE_DIR`。默认插件目录是仓库的 `plugins/`,状态目录
|
|
44
44
|
是 Windows 的 `%APPDATA%\agent-bridge\state`。
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
跨会话读取默认 `allowFrom: []`(白名单留空),允许所有 Peer 查询本机各来源的 AUN/飞书归档和插件已提供的原生历史,可通过独立无工具端点总结。归档采集默认关闭;读取范围仍受各插件历史能力限制。个人助理也默认接受所有 Peer、允许调用所有已就绪的普通 Agent;需要限制时可配置名单。配置与 `/sessions` 命令见[跨会话读取说明](docs/cross-session-reading.md)。
|
|
47
47
|
|
|
48
48
|
## npm 插件
|
|
49
49
|
|
|
50
|
-
默认包 `mu-agent-bridge` 已内置 abweb、AUN、千问办公、DSH、豆包、飞书、WorkBuddy、ChatGPT/Codex、Codex CLI、Claude CLI
|
|
50
|
+
默认包 `mu-agent-bridge` 已内置 abweb、AUN、千问办公、DSH、豆包、飞书、WorkBuddy、ZCode、ChatGPT/Codex、Codex CLI、Claude CLI 和个人助理共十二个插件,直接安装即可使用:
|
|
51
51
|
|
|
52
52
|
```powershell
|
|
53
53
|
npm install -g mu-agent-bridge@latest
|
|
54
54
|
mab start
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
十二个插件会从核心包的 `plugins/` 目录自动发现。插件仍分别发布为独立 npm 包,可用于单独测试、升级或替换版本。安装与发布说明见
|
|
58
58
|
[`docs/npm-publishing.md`](docs/npm-publishing.md)。
|
|
59
59
|
|
|
60
60
|
`mab start` 启动时会查询核心包和托管插件的 npm 最新版本,检查状态保存于 mab 状态目录的
|
|
61
61
|
`versions.json`,abweb 的“版本管理”区域可查看。托管插件发现新版本后会自动下载、验证并替换;
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
升级失败会恢复旧插件并继续启动。核心在启动及运行中定时检查 npmjs,仅升级较低版本,不降级高版本测试包。
|
|
63
|
+
核心先暂存并验证新包与依赖,再备份和更新当前启动目录中的发布文件;保留其他本地文件,新 daemon 健康检查失败时恢复旧文件。Windows 的 `start-mab.ps1` 登录任务、命令行和 ABWeb 共用核心升级器,计划任务不必更换启动路径。全局单独安装的插件暂不自动升级。
|
|
64
|
+
|
|
65
|
+
插件候选包校验通过后,最多等待旧任务两小时;到期以 `PLUGIN_UPGRADE_WAIT_TIMEOUT` 结束桥接任务,最多再给旧插件五秒清理,然后继续升级,失败仍回滚。升级清理会要求保留桌面进程;千问、豆包和 ChatGPT 需使用支持该选项的配套插件。桌面原生生成可能继续,核心会拦截迟到回复;普通停止和重载不套用此期限。详见[升级等待设计](docs/superpowers/specs/2026-10-30-plugin-upgrade-deadline-design.md)。
|
|
64
66
|
|
|
65
67
|
可用 `mab status` 查看后台、插件以及 AUN 已连接 AID 和关联 Agent 的状态。AUN 插件自己加载本地
|
|
66
68
|
`~/.evolcore/AIDs` 并建立连接,不要求先启动 `ec start`,也不调用 `ec agent list/show`。
|
|
@@ -82,10 +84,12 @@ abweb 插件目录从 npm 搜索带 `mu-agent-bridge-plugin` 关键词的包,
|
|
|
82
84
|
|
|
83
85
|
`workbuddy` 插件包名为 `agent-bridge-workbuddy`,显式关联后按对端保持会话。默认问答使用独立的 `@tencent-ai/codebuddy-code` CLI,须单独登录;没有 CLI 时使用桌面端 CDP。CLI 未登录时不会自动切换到桌面账号。个人助理选择 WorkBuddy 桌面模型来源时请设置 `transport: "cdp"`。
|
|
84
86
|
|
|
87
|
+
`zcode` 插件包名为 `agent-bridge-zcode`,通过 ZCode v4 桌面端 CDP 收发文本。Windows 后台启动或重载插件时,会自动从注册信息和开始菜单查找并带 CDP 参数启动 ZCode;默认端口为 `9231`,可在插件配置设置 `cdpPort`,或使用 `ZCODE_CDP_PORT` 覆盖。插件保护未发送的草稿;已人工启动但未开启 CDP 时,请先从托盘退出再重载。启动参数和会话说明见 [插件文档](packages/zcode/README.md)。
|
|
88
|
+
|
|
85
89
|
插件仍在 daemon 的同一个 Node.js 进程内运行。修改插件入口依赖后应重启 daemon,避免模块缓存
|
|
86
90
|
影响新代码。
|
|
87
91
|
|
|
88
|
-
npm
|
|
92
|
+
npm 尚未发布时,核心包内置的十二个默认插件仍会出现在插件列表。abweb 的“安装本地插件”
|
|
89
93
|
可填写 **Windows 主机上**包含 `plugin.json` 的目录路径,例如编译后的
|
|
90
94
|
`C:\path\to\agent_bridge\packages\dsh`;安装后可在下拉框中启动。已内置的千问办公无需再次安装。
|
|
91
95
|
“安装 npm 插件”只适用于包已经公开发布的情况。npm 发布令牌仅供发布脚本向 npm 官网认证,
|
package/dist/src-ts/cli.js
CHANGED
|
@@ -79,7 +79,7 @@ async function daemonAddress() {
|
|
|
79
79
|
try {
|
|
80
80
|
const address = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.resolve)(stateDirectory(), "daemon-address.json"), "utf8"));
|
|
81
81
|
if (Number.isInteger(address.port) && address.port > 0)
|
|
82
|
-
return { host: typeof address.host === "string" ? address.host : "127.0.0.1", port: address.port };
|
|
82
|
+
return { host: typeof address.host === "string" ? address.host : "127.0.0.1", port: address.port, pid: address.pid };
|
|
83
83
|
}
|
|
84
84
|
catch { /* 后台未写入地址时使用配置端口 */ }
|
|
85
85
|
return { host: process.env.AGENT_BRIDGE_HOST ?? "127.0.0.1", port: Number(process.env.AGENT_BRIDGE_PORT ?? "26107") };
|
|
@@ -114,17 +114,28 @@ async function pair() {
|
|
|
114
114
|
return 0;
|
|
115
115
|
}
|
|
116
116
|
async function update() {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return 2;
|
|
120
|
-
}
|
|
121
|
-
await stopDaemon();
|
|
122
|
-
await execFileAsync("npm.cmd", ["install", "-g", "mu-agent-bridge@latest", "--no-fund", "--no-audit"], { timeout: 120_000, shell: true, windowsHide: true });
|
|
123
|
-
await startDaemon();
|
|
124
|
-
console.log("核心已更新,旧 daemon 已结束并启动新版本");
|
|
117
|
+
const updated = await checkCoreUpdate();
|
|
118
|
+
console.log(updated ? "核心已更新,新版本健康检查通过" : "核心未升级,版本检查结果及失败原因见后台日志");
|
|
125
119
|
return 0;
|
|
126
120
|
}
|
|
127
|
-
async function
|
|
121
|
+
async function checkCoreUpdate() {
|
|
122
|
+
const logs = new log_store_1.LogStore((0, node_path_1.resolve)(stateDirectory(), "logs"));
|
|
123
|
+
let launched;
|
|
124
|
+
try {
|
|
125
|
+
return await (0, core_updater_1.updateCoreAtStartup)({
|
|
126
|
+
packageRoot: (0, node_path_1.resolve)(__dirname, "..", ".."),
|
|
127
|
+
stateDir: stateDirectory(),
|
|
128
|
+
stopDaemon: (additionalPid) => stopDaemon(false, additionalPid),
|
|
129
|
+
startDaemon: async () => { launched = await startUpdatedDaemon(); return launched; },
|
|
130
|
+
waitForHealth: async () => launched?.healthy() ?? false,
|
|
131
|
+
log: (level, message, fields) => logs.append({ time: new Date().toISOString(), level, source: "核心升级", message, fields }),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
await logs.flush();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function stopDaemon(stopTask = true, additionalPid) {
|
|
128
139
|
let pid;
|
|
129
140
|
try {
|
|
130
141
|
const address = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.resolve)(stateDirectory(), "daemon-address.json"), "utf8"));
|
|
@@ -144,11 +155,42 @@ async function stopDaemon(stopTask = true) {
|
|
|
144
155
|
console.log(`已结束旧 daemon:${old.ProcessId}`);
|
|
145
156
|
}
|
|
146
157
|
}
|
|
147
|
-
else
|
|
148
|
-
|
|
149
|
-
|
|
158
|
+
else {
|
|
159
|
+
for (const targetPid of new Set([pid, additionalPid].filter((value) => Boolean(value && value !== process.pid)))) {
|
|
160
|
+
let command;
|
|
161
|
+
try {
|
|
162
|
+
command = process.platform === "linux"
|
|
163
|
+
? (await (0, promises_1.readFile)(`/proc/${targetPid}/cmdline`, "utf8")).split("\0").filter(Boolean)
|
|
164
|
+
: (await execFileAsync("ps", ["-ww", "-p", String(targetPid), "-o", "command="], { timeout: 5000 })).stdout;
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
if (["ENOENT", "ESRCH", 1].includes(error.code))
|
|
168
|
+
continue;
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
if (!(0, daemon_process_1.isDaemonCommand)(command, (0, node_path_1.resolve)(__dirname, "cli.js")))
|
|
172
|
+
continue;
|
|
173
|
+
try {
|
|
174
|
+
process.kill(targetPid);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
if (error.code !== "ESRCH")
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
for (let attempt = 0;; attempt++) {
|
|
181
|
+
try {
|
|
182
|
+
process.kill(targetPid, 0);
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
if (error.code === "ESRCH")
|
|
186
|
+
break;
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
if (attempt >= 150)
|
|
190
|
+
throw new Error(`等待旧 daemon 退出超时:${targetPid}`);
|
|
191
|
+
await new Promise(done => setTimeout(done, 100));
|
|
192
|
+
}
|
|
150
193
|
}
|
|
151
|
-
catch { /* 进程已退出 */ }
|
|
152
194
|
}
|
|
153
195
|
if (stopTask && process.platform === "win32") {
|
|
154
196
|
try {
|
|
@@ -193,19 +235,21 @@ async function startUpdatedDaemon() {
|
|
|
193
235
|
return false;
|
|
194
236
|
const address = await daemonAddress();
|
|
195
237
|
try {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
238
|
+
if (address.pid === child?.pid) {
|
|
239
|
+
const response = await fetch(`http://127.0.0.1:${address.port}/api/health`, {
|
|
240
|
+
headers: env.AGENT_BRIDGE_TOKEN ? { authorization: `Bearer ${env.AGENT_BRIDGE_TOKEN}` } : undefined,
|
|
241
|
+
signal: AbortSignal.timeout(1000),
|
|
242
|
+
});
|
|
243
|
+
if (response.ok && (await response.json()).status === "ok")
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
202
246
|
}
|
|
203
247
|
catch { /* daemon 尚未就绪 */ }
|
|
204
248
|
await new Promise((done) => setTimeout(done, 1000));
|
|
205
249
|
}
|
|
206
250
|
return false;
|
|
207
251
|
};
|
|
208
|
-
return { healthy };
|
|
252
|
+
return { pid: child.pid, healthy };
|
|
209
253
|
}
|
|
210
254
|
let coreUpdateCheckRunning = false;
|
|
211
255
|
function scheduleCoreUpdateCheck() {
|
|
@@ -399,24 +443,10 @@ async function main() {
|
|
|
399
443
|
delete process.env.AGENT_BRIDGE_CORE_UPDATE_CHILD;
|
|
400
444
|
return serve();
|
|
401
445
|
}
|
|
402
|
-
|
|
403
|
-
let launched;
|
|
404
|
-
const updated = await (0, core_updater_1.updateCoreAtStartup)({
|
|
405
|
-
packageRoot: (0, node_path_1.resolve)(__dirname, "..", ".."),
|
|
406
|
-
stateDir: stateDirectory(),
|
|
407
|
-
stopDaemon: () => stopDaemon(false),
|
|
408
|
-
startDaemon: async () => { launched = await startUpdatedDaemon(); },
|
|
409
|
-
waitForHealth: async () => launched?.healthy() ?? false,
|
|
410
|
-
log: (level, message, fields) => logs.append({ time: new Date().toISOString(), level, source: "核心升级", message, fields }),
|
|
411
|
-
});
|
|
412
|
-
if (updated) {
|
|
413
|
-
await logs.flush();
|
|
446
|
+
if (await checkCoreUpdate())
|
|
414
447
|
return 0;
|
|
415
|
-
|
|
416
|
-
if (process.env.AGENT_BRIDGE_CORE_UPDATE_ONLY === "1") {
|
|
417
|
-
await logs.flush();
|
|
448
|
+
if (process.env.AGENT_BRIDGE_CORE_UPDATE_ONLY === "1")
|
|
418
449
|
return 0;
|
|
419
|
-
}
|
|
420
450
|
await stopDaemon(false);
|
|
421
451
|
return serve();
|
|
422
452
|
}
|
|
@@ -199,34 +199,13 @@ class ControlServer {
|
|
|
199
199
|
if (method === "POST" && url.pathname === "/api/versions/check")
|
|
200
200
|
return this.json(res, 200, { versions: await this.options.manager.checkPackageUpdates() });
|
|
201
201
|
if (method === "POST" && url.pathname === "/api/update") {
|
|
202
|
-
this.options.manager.log("info", "
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
"New-Item -ItemType Directory -Force $dir | Out-Null",
|
|
210
|
-
"function Write-UpdateLog($text) { \"$(Get-Date -Format o) $text\" | Out-File -FilePath $log -Append -Encoding utf8 }",
|
|
211
|
-
"Start-Sleep -Seconds 3",
|
|
212
|
-
"Write-UpdateLog '停止旧 daemon'",
|
|
213
|
-
"schtasks.exe /End /TN mab 2>&1 | ForEach-Object { Write-UpdateLog $_ }",
|
|
214
|
-
"Start-Sleep -Seconds 2",
|
|
215
|
-
"Write-UpdateLog '开始安装 mu-agent-bridge@latest'",
|
|
216
|
-
"npm.cmd install -g mu-agent-bridge@latest --no-fund --no-audit 2>&1 | ForEach-Object { Write-UpdateLog $_ }",
|
|
217
|
-
"Write-UpdateLog '重新启动 mab 计划任务'",
|
|
218
|
-
"schtasks.exe /Run /TN mab 2>&1 | ForEach-Object { Write-UpdateLog $_ }",
|
|
219
|
-
"Write-UpdateLog '后台升级脚本结束'",
|
|
220
|
-
].join('; ');
|
|
221
|
-
setTimeout(() => {
|
|
222
|
-
const child = (0, node_child_process_1.spawn)("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script], { detached: true, stdio: "ignore", windowsHide: true });
|
|
223
|
-
child.unref();
|
|
224
|
-
}, 1500).unref();
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
return this.json(res, 501, { error: "当前平台未配置后台升级器" });
|
|
228
|
-
}
|
|
229
|
-
return this.json(res, 202, { ok: true, message: "后台正在升级,服务将自动重启" });
|
|
202
|
+
this.options.manager.log("info", "已请求检查后台更新,有更高版本时自动升级", undefined, "后台升级");
|
|
203
|
+
setTimeout(() => {
|
|
204
|
+
const child = (0, node_child_process_1.spawn)(process.execPath, [(0, node_path_1.resolve)(__dirname, "cli.js"), "update"], { detached: true, stdio: "ignore", windowsHide: true });
|
|
205
|
+
child.once("error", error => this.options.manager.log("error", "后台升级进程启动失败", { error: error.message }, "后台升级"));
|
|
206
|
+
child.unref();
|
|
207
|
+
}, 1500).unref();
|
|
208
|
+
return this.json(res, 202, { ok: true, message: "后台正在检查更新,有更高版本时升级并重启" });
|
|
230
209
|
}
|
|
231
210
|
if (method === "GET" && url.pathname === "/api/messages")
|
|
232
211
|
return this.json(res, 200, { messages: this.options.manager.recentMessages() });
|
|
@@ -7,72 +7,180 @@ const node_child_process_1 = require("node:child_process");
|
|
|
7
7
|
const node_util_1 = require("node:util");
|
|
8
8
|
const version_manager_1 = require("./version-manager");
|
|
9
9
|
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
10
|
-
function normalizePath(value) {
|
|
11
|
-
const path = (0, node_path_1.resolve)(value).replace(/[\\/]+$/, "");
|
|
12
|
-
return process.platform === "win32" ? path.toLowerCase() : path;
|
|
13
|
-
}
|
|
14
|
-
async function sameDirectory(left, right) {
|
|
15
|
-
try {
|
|
16
|
-
return normalizePath(await (0, promises_1.realpath)(left)) === normalizePath(await (0, promises_1.realpath)(right));
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
return normalizePath(left) === normalizePath(right);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
10
|
async function runNpm(args) {
|
|
23
|
-
const
|
|
24
|
-
|
|
11
|
+
const options = { timeout: 180_000, maxBuffer: 4 * 1024 * 1024, windowsHide: true };
|
|
12
|
+
let command = "npm";
|
|
13
|
+
if (process.platform === "win32") {
|
|
14
|
+
// 直接调用 npm 的 Node 入口,保证带空格的暂存路径不会被 cmd 拆分。
|
|
15
|
+
const { stdout } = await execFileAsync("where.exe", ["npm.cmd"], options);
|
|
16
|
+
args = [(0, node_path_1.join)((0, node_path_1.dirname)(stdout.trim().split(/\r?\n/)[0]), "node_modules", "npm", "bin", "npm-cli.js"), ...args];
|
|
17
|
+
command = process.execPath;
|
|
18
|
+
}
|
|
19
|
+
const { stdout } = await execFileAsync(command, args, options);
|
|
25
20
|
return stdout.trim();
|
|
26
21
|
}
|
|
27
22
|
async function updateCoreAtStartup(options) {
|
|
28
23
|
const npm = options.npm ?? runNpm;
|
|
24
|
+
let workRoot;
|
|
25
|
+
let lockPath;
|
|
26
|
+
let lockCandidate;
|
|
27
|
+
let locked = false;
|
|
28
|
+
let busy = false;
|
|
29
|
+
let keepBackup = false;
|
|
29
30
|
try {
|
|
30
|
-
const globalRoot = options.globalRoot ?? await npm(["root", "-g"]);
|
|
31
31
|
const packageRoot = (0, node_path_1.resolve)(options.packageRoot);
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
await (0, promises_1.mkdir)(options.stateDir, { recursive: true });
|
|
33
|
+
lockPath = (0, node_path_1.join)((0, node_path_1.resolve)(options.stateDir), "core-update.lock");
|
|
34
|
+
try {
|
|
35
|
+
lockCandidate = await (0, promises_1.mkdtemp)((0, node_path_1.join)(options.stateDir, "core-update-lock-"));
|
|
36
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(lockCandidate, String(process.pid)), "");
|
|
37
|
+
await (0, promises_1.rename)(lockCandidate, lockPath);
|
|
38
|
+
lockCandidate = undefined;
|
|
39
|
+
locked = true;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (!["EEXIST", "ENOTEMPTY", "EPERM"].includes(error.code ?? ""))
|
|
43
|
+
throw error;
|
|
44
|
+
const owners = await (0, promises_1.readdir)(lockPath);
|
|
45
|
+
for (const owner of owners.filter(value => /^\d+$/.test(value) && Number(value) > 0)) {
|
|
46
|
+
try {
|
|
47
|
+
process.kill(Number(owner), 0);
|
|
48
|
+
}
|
|
49
|
+
catch (ownerError) {
|
|
50
|
+
if (ownerError.code === "ESRCH")
|
|
51
|
+
await (0, promises_1.rm)((0, node_path_1.join)(lockPath, owner), { force: true });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// 只删除空锁目录,避免并发清理误删新执行者的锁。
|
|
55
|
+
if (!await (0, promises_1.readdir)(lockPath).then(entries => entries.length)) {
|
|
56
|
+
await (0, promises_1.rmdir)(lockPath).catch(() => undefined);
|
|
57
|
+
return updateCoreAtStartup(options);
|
|
58
|
+
}
|
|
59
|
+
busy = true;
|
|
60
|
+
throw new Error("已有核心升级检查正在运行,请等待完成后重试");
|
|
61
|
+
}
|
|
34
62
|
const metadata = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(packageRoot, "package.json"), "utf8"));
|
|
35
63
|
if (metadata.name !== "mu-agent-bridge" || !metadata.version)
|
|
36
|
-
throw new Error("
|
|
37
|
-
const rawVersion = await npm(["view", "mu-agent-bridge", "version", "--json"]);
|
|
64
|
+
throw new Error("当前核心包信息无效");
|
|
65
|
+
const rawVersion = await npm(["view", "mu-agent-bridge@latest", "version", "--json", "--registry=https://registry.npmjs.org"]);
|
|
38
66
|
const latestVersion = (() => { try {
|
|
39
67
|
return String(JSON.parse(rawVersion));
|
|
40
68
|
}
|
|
41
69
|
catch {
|
|
42
70
|
return rawVersion.replace(/^['\"]|['\"]$/g, "");
|
|
43
71
|
} })();
|
|
44
|
-
if ((0, version_manager_1.compareVersions)(latestVersion, metadata.version) <= 0)
|
|
72
|
+
if ((0, version_manager_1.compareVersions)(latestVersion, metadata.version) <= 0) {
|
|
73
|
+
options.log?.("info", "当前核心版本不低于 npmjs 最新版本,保留当前版本", { currentVersion: metadata.version, latestVersion });
|
|
45
74
|
return false;
|
|
75
|
+
}
|
|
46
76
|
options.log?.("info", "发现 daemon 新版本,开始自动升级", { currentVersion: metadata.version, latestVersion });
|
|
47
|
-
await (0, promises_1.
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
77
|
+
workRoot = await (0, promises_1.mkdtemp)((0, node_path_1.join)(options.stateDir, "mab-core-update-"));
|
|
78
|
+
const staging = (0, node_path_1.join)(workRoot, "staging");
|
|
79
|
+
const candidate = (0, node_path_1.join)(staging, "node_modules", "mu-agent-bridge");
|
|
80
|
+
const backup = (0, node_path_1.join)(workRoot, "backup");
|
|
81
|
+
await npm(["install", "--prefix", staging, "--install-strategy=nested", `mu-agent-bridge@${latestVersion}`, "--registry=https://registry.npmjs.org", "--no-fund", "--no-audit"]);
|
|
82
|
+
const downloaded = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(candidate, "package.json"), "utf8"));
|
|
83
|
+
if (downloaded.name !== "mu-agent-bridge" || downloaded.version !== latestVersion)
|
|
84
|
+
throw new Error(`核心包名称或版本不匹配:${downloaded.version ?? "未知版本"}`);
|
|
85
|
+
await (0, promises_1.readFile)((0, node_path_1.join)(candidate, "dist", "src-ts", "cli.js"));
|
|
86
|
+
const entries = (await (0, promises_1.readdir)(candidate)).filter(entry => entry !== "plugins" && entry !== "node_modules");
|
|
87
|
+
for (const entry of await (0, promises_1.readdir)((0, node_path_1.join)(candidate, "node_modules"), { withFileTypes: true }).catch(error => {
|
|
88
|
+
if (error.code === "ENOENT")
|
|
89
|
+
return [];
|
|
90
|
+
throw error;
|
|
91
|
+
})) {
|
|
92
|
+
if (entry.name === ".package-lock.json")
|
|
93
|
+
continue;
|
|
94
|
+
if (entry.name === ".bin" && entry.isDirectory()) {
|
|
95
|
+
for (const binary of await (0, promises_1.readdir)((0, node_path_1.join)(candidate, "node_modules", entry.name)))
|
|
96
|
+
entries.push((0, node_path_1.join)("node_modules", entry.name, binary));
|
|
97
|
+
}
|
|
98
|
+
else if (entry.name.startsWith("@") && entry.isDirectory()) {
|
|
99
|
+
for (const scoped of await (0, promises_1.readdir)((0, node_path_1.join)(candidate, "node_modules", entry.name)))
|
|
100
|
+
entries.push((0, node_path_1.join)("node_modules", entry.name, scoped));
|
|
101
|
+
}
|
|
102
|
+
else
|
|
103
|
+
entries.push((0, node_path_1.join)("node_modules", entry.name));
|
|
104
|
+
}
|
|
105
|
+
// 本地安装的插件及其高版本也在 plugins 下,只替换发布包管理的文件。
|
|
106
|
+
for (const plugin of await (0, promises_1.readdir)((0, node_path_1.join)(candidate, "plugins"), { withFileTypes: true }).catch(error => {
|
|
107
|
+
if (error.code === "ENOENT")
|
|
108
|
+
return [];
|
|
109
|
+
throw error;
|
|
110
|
+
})) {
|
|
111
|
+
const pluginPath = (0, node_path_1.join)("plugins", plugin.name);
|
|
112
|
+
if (plugin.isDirectory()) {
|
|
113
|
+
for (const entry of await (0, promises_1.readdir)((0, node_path_1.join)(candidate, pluginPath)))
|
|
114
|
+
entries.push((0, node_path_1.join)(pluginPath, entry));
|
|
115
|
+
}
|
|
116
|
+
else
|
|
117
|
+
entries.push(pluginPath);
|
|
118
|
+
}
|
|
119
|
+
const previousEntries = new Set();
|
|
120
|
+
await (0, promises_1.mkdir)(backup);
|
|
121
|
+
for (const entry of entries) {
|
|
122
|
+
const exists = await (0, promises_1.lstat)((0, node_path_1.join)(packageRoot, entry)).then(() => true, error => {
|
|
123
|
+
if (error.code === "ENOENT")
|
|
124
|
+
return false;
|
|
125
|
+
throw error;
|
|
126
|
+
});
|
|
127
|
+
if (exists) {
|
|
128
|
+
previousEntries.add(entry);
|
|
129
|
+
await (0, promises_1.mkdir)((0, node_path_1.dirname)((0, node_path_1.join)(backup, entry)), { recursive: true });
|
|
130
|
+
await (0, promises_1.cp)((0, node_path_1.join)(packageRoot, entry), (0, node_path_1.join)(backup, entry), { recursive: true, verbatimSymlinks: true });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
let stopped = false;
|
|
134
|
+
let startedPid;
|
|
51
135
|
try {
|
|
52
136
|
await options.stopDaemon();
|
|
53
|
-
|
|
137
|
+
stopped = true;
|
|
138
|
+
for (const entry of entries) {
|
|
139
|
+
await (0, promises_1.rm)((0, node_path_1.join)(packageRoot, entry), { recursive: true, force: true });
|
|
140
|
+
await (0, promises_1.mkdir)((0, node_path_1.dirname)((0, node_path_1.join)(packageRoot, entry)), { recursive: true });
|
|
141
|
+
await (0, promises_1.cp)((0, node_path_1.join)(candidate, entry), (0, node_path_1.join)(packageRoot, entry), { recursive: true, verbatimSymlinks: true });
|
|
142
|
+
}
|
|
54
143
|
const installed = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(packageRoot, "package.json"), "utf8"));
|
|
55
144
|
if (installed.name !== "mu-agent-bridge" || installed.version !== latestVersion)
|
|
56
145
|
throw new Error(`核心安装结果不匹配:${installed.version ?? "未知版本"}`);
|
|
57
|
-
await options.startDaemon();
|
|
146
|
+
startedPid = (await options.startDaemon())?.pid;
|
|
58
147
|
if (!await options.waitForHealth())
|
|
59
148
|
throw new Error("新 daemon 健康检查失败");
|
|
60
149
|
options.log?.("info", "daemon 自动升级成功", { version: latestVersion });
|
|
61
|
-
await (0, promises_1.rm)(backupRoot, { recursive: true, force: true });
|
|
62
150
|
return true;
|
|
63
151
|
}
|
|
64
152
|
catch (error) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
153
|
+
if (!stopped)
|
|
154
|
+
throw error;
|
|
155
|
+
keepBackup = true;
|
|
156
|
+
await options.stopDaemon(startedPid);
|
|
157
|
+
for (const entry of entries) {
|
|
158
|
+
await (0, promises_1.rm)((0, node_path_1.join)(packageRoot, entry), { recursive: true, force: true });
|
|
159
|
+
if (previousEntries.has(entry))
|
|
160
|
+
await (0, promises_1.cp)((0, node_path_1.join)(backup, entry), (0, node_path_1.join)(packageRoot, entry), { recursive: true, verbatimSymlinks: true });
|
|
161
|
+
}
|
|
68
162
|
options.log?.("error", "daemon 自动升级失败,已恢复旧版本", { error: error instanceof Error ? error.message : String(error), version: metadata.version });
|
|
69
|
-
await options.startDaemon()
|
|
70
|
-
|
|
163
|
+
startedPid = (await options.startDaemon())?.pid;
|
|
164
|
+
if (!await options.waitForHealth()) {
|
|
165
|
+
await options.stopDaemon(startedPid);
|
|
166
|
+
throw new Error("旧 daemon 恢复后的健康检查失败");
|
|
167
|
+
}
|
|
168
|
+
keepBackup = false;
|
|
71
169
|
return false;
|
|
72
170
|
}
|
|
73
171
|
}
|
|
74
172
|
catch (error) {
|
|
75
|
-
options.log?.("warn", "daemon 自动升级检查失败,继续使用当前版本", { error: error instanceof Error ? error.message : String(error) });
|
|
173
|
+
options.log?.("warn", keepBackup ? "daemon 恢复失败,升级备份已保留" : "daemon 自动升级检查失败,继续使用当前版本", { error: error instanceof Error ? error.message : String(error), ...(keepBackup ? { backup: workRoot } : {}) });
|
|
174
|
+
if (keepBackup || busy)
|
|
175
|
+
throw error;
|
|
76
176
|
return false;
|
|
77
177
|
}
|
|
178
|
+
finally {
|
|
179
|
+
if (workRoot && !keepBackup)
|
|
180
|
+
await (0, promises_1.rm)(workRoot, { recursive: true, force: true });
|
|
181
|
+
if (locked && lockPath)
|
|
182
|
+
await (0, promises_1.rm)(lockPath, { recursive: true, force: true });
|
|
183
|
+
if (lockCandidate)
|
|
184
|
+
await (0, promises_1.rm)(lockCandidate, { recursive: true, force: true });
|
|
185
|
+
}
|
|
78
186
|
}
|
|
@@ -11,6 +11,7 @@ function isDaemonCommand(commandLine, cli) {
|
|
|
11
11
|
catch { /* 不存在的路径仍按原始路径比较。 */ }
|
|
12
12
|
return value.replace(/[\\/]+/g, "/").toLowerCase();
|
|
13
13
|
};
|
|
14
|
-
const
|
|
14
|
+
const values = Array.isArray(commandLine) ? commandLine : [...commandLine.matchAll(/"([^"]*)"|(\S+)/g)].map(match => match[1] ?? match[2]);
|
|
15
|
+
const args = values.map(normalize);
|
|
15
16
|
return args[1] === normalize(cli) && ["start", "--serve"].includes(args[2]);
|
|
16
17
|
}
|
package/dist/src-ts/dsh-agent.js
CHANGED
|
@@ -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);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HttpAgent = void 0;
|
|
4
|
+
const agent_1 = require("./agent");
|
|
5
|
+
// 仅供独立 HTTP 通信验收使用,不接入桌面插件的生产调用链。
|
|
6
|
+
class HttpAgent {
|
|
7
|
+
endpoint;
|
|
8
|
+
model;
|
|
9
|
+
name = "dsh-http";
|
|
10
|
+
constructor(endpoint, model = "qwenwork") {
|
|
11
|
+
this.endpoint = endpoint;
|
|
12
|
+
this.model = model;
|
|
13
|
+
}
|
|
14
|
+
async send(request) {
|
|
15
|
+
const response = await fetch(`${this.endpoint.replace(/\/$/, "")}/chat/completions`, {
|
|
16
|
+
method: "POST",
|
|
17
|
+
headers: { "content-type": "application/json" },
|
|
18
|
+
body: JSON.stringify({ model: this.model, messages: [{ role: "user", content: request.task }], stream: false }),
|
|
19
|
+
signal: AbortSignal.timeout(request.timeoutMs ?? 300_000),
|
|
20
|
+
});
|
|
21
|
+
const body = await response.text();
|
|
22
|
+
if (!response.ok)
|
|
23
|
+
throw new agent_1.AgentError(`HTTP Agent 请求失败 (${response.status}): ${body.slice(0, 500)}`);
|
|
24
|
+
try {
|
|
25
|
+
const value = JSON.parse(body);
|
|
26
|
+
const text = value.choices?.[0]?.message?.content?.trim();
|
|
27
|
+
if (!text)
|
|
28
|
+
throw new Error("响应 choices[0].message.content 为空");
|
|
29
|
+
return { text };
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw new agent_1.AgentError("HTTP Agent 返回了无效响应", { cause: error });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.HttpAgent = HttpAgent;
|