mu-agent-bridge 1.0.15 → 1.0.17
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 +18 -10
- package/dist/src-ts/cli.js +71 -3
- package/dist/src-ts/control-server.js +42 -7
- package/dist/src-ts/llprobe-qwen-bridge.js +1 -1
- package/dist/src-ts/local-aids.js +2 -2
- package/dist/src-ts/plugin-manager.js +10 -2
- package/dist/src-ts/plugin-manifest.js +4 -1
- package/dist/src-ts/plugin.js +11 -0
- package/dist/src-ts/plugins/abweb/public/app.js +10 -0
- package/dist/src-ts/plugins/aun/evolcore.js +43 -0
- package/dist/src-ts/plugins/aun/index.js +41 -23
- package/dist/src-ts/plugins/aun/public/app.js +3 -3
- package/dist/src-ts/plugins/chatgpt/cdp-client.js +295 -0
- package/dist/src-ts/plugins/chatgpt/index.js +90 -0
- package/dist/src-ts/plugins/doubao/index.js +2 -1
- package/dist/src-ts/plugins/qwenwork/cdp-client.js +9 -2
- package/dist/src-ts/plugins/qwenwork/index.js +3 -2
- package/dist/src-ts/plugins/workbuddy/index.js +2 -1
- package/dist/tests-ts/aun-plugin.test.js +60 -16
- package/dist/tests-ts/bundled-package.test.js +24 -0
- package/dist/tests-ts/chatgpt-plugin.test.js +98 -0
- package/dist/tests-ts/control-server.test.js +43 -0
- package/dist/tests-ts/platform-integration.test.js +2 -2
- package/dist/tests-ts/plugin-manager.test.js +39 -0
- package/dist/tests-ts/qwenwork-plugin.test.js +55 -1
- package/package.json +6 -6
- package/plugins/abweb/dist/src-ts/agent.js +10 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/control-client.js +91 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +132 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +366 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +227 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +25 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/style.css +55 -0
- package/plugins/abweb/plugin.json +7 -3
- package/plugins/aun/config.schema.json +18 -0
- package/plugins/aun/dist/src-ts/agent.js +10 -0
- package/plugins/aun/dist/src-ts/plugins/aun/evolcore.js +43 -0
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +434 -0
- package/plugins/aun/dist/src-ts/plugins/aun/messages.js +45 -0
- package/plugins/aun/dist/src-ts/plugins/aun/public/app.js +66 -0
- package/plugins/aun/dist/src-ts/plugins/aun/public/index.html +14 -0
- package/plugins/aun/dist/src-ts/plugins/aun/public/style.css +21 -0
- package/plugins/aun/plugin.json +18 -0
- package/plugins/chatgpt/config.schema.json +1 -0
- package/plugins/chatgpt/dist/src-ts/agent.js +10 -0
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/cdp-client.js +295 -0
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +90 -0
- package/plugins/chatgpt/plugin.json +1 -0
- package/plugins/doubao/config.schema.json +13 -0
- package/plugins/doubao/dist/src-ts/agent.js +10 -0
- package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +94 -0
- package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +85 -0
- package/plugins/doubao/plugin.json +15 -0
- package/plugins/dsh/config.schema.json +8 -0
- package/plugins/dsh/dist/src-ts/agent.js +10 -0
- package/plugins/dsh/dist/src-ts/dsh-agent.js +70 -0
- package/plugins/dsh/dist/src-ts/dsh-paths.js +15 -0
- package/plugins/dsh/dist/src-ts/headless-account.patch.yml +5 -0
- package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +61 -0
- package/plugins/dsh/dist/src-ts/plugins/qwenwork/openai-adapter.js +146 -0
- package/plugins/dsh/plugin.json +14 -0
- package/plugins/feishu/config.schema.json +68 -0
- package/plugins/feishu/dist/src-ts/agent.js +10 -0
- package/plugins/feishu/dist/src-ts/channel.js +2 -0
- package/plugins/feishu/dist/src-ts/plugins/feishu/client.js +133 -0
- package/plugins/feishu/dist/src-ts/plugins/feishu/index.js +257 -0
- package/plugins/feishu/dist/src-ts/plugins/feishu/public/app.js +147 -0
- package/plugins/feishu/dist/src-ts/plugins/feishu/public/index.html +17 -0
- package/plugins/feishu/dist/src-ts/plugins/feishu/public/style.css +30 -0
- package/plugins/feishu/plugin.json +18 -0
- package/plugins/qwenwork/config.schema.json +1 -1
- package/plugins/qwenwork/dist/src-ts/agent.js +10 -0
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +172 -0
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +149 -0
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/openai-adapter.js +146 -0
- package/plugins/qwenwork/plugin.json +8 -4
- package/plugins/workbuddy/config.schema.json +15 -0
- package/plugins/workbuddy/dist/src-ts/agent.js +10 -0
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +118 -0
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cli-client.js +39 -0
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +140 -0
- package/plugins/workbuddy/plugin.json +15 -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`,以及插件化管理后台
|
|
4
|
+
当前插件包括千问办公 `qwenwork`、DeepSeek Harness `dsh`、豆包 `doubao`、基于 CodeBuddy CLI 的 `workbuddy`、远程 ChatGPT/Codex CDP 插件 `chatgpt`,以及插件化管理后台
|
|
5
5
|
`abweb`。插件之间独立失败,管理 API 和 OpenAI 兼容接口由同一个本地控制服务提供。
|
|
6
6
|
|
|
7
7
|
## 快速开始
|
|
@@ -14,18 +14,16 @@ npm run start
|
|
|
14
14
|
|
|
15
15
|
默认监听 `http://127.0.0.1:26107`,管理页面为 `http://127.0.0.1:26107/abweb/`,管理 API 为 `/api/health`、`/api/plugins`,千问办公的
|
|
16
16
|
OpenAI 兼容 API 为 `/v1/models` 和 `/v1/chat/completions`。管理页面位于 `/abweb`。
|
|
17
|
-
千问办公插件使用 CDP `
|
|
17
|
+
千问办公插件使用 CDP `9228`;没有运行千问办公时,daemon 仍会启动,qwenwork 会显示为失败状态,
|
|
18
18
|
可以通过管理 API 重载。
|
|
19
19
|
|
|
20
20
|
安装后使用 `mab start` 启动。旧桥仍可使用 `17893`,不会与管理页面冲突。局域网访问时设置
|
|
21
21
|
`AGENT_BRIDGE_HOST=0.0.0.0` 和 `AGENT_BRIDGE_TOKEN`,然后访问
|
|
22
22
|
`http://<Windows 主机 IP>:26107/abweb/`。页面使用一次性配置码配对后管理插件;请只在可信网络使用。
|
|
23
|
-
已有 AID 的连接状态由 AUN
|
|
24
|
-
|
|
23
|
+
已有 AID 的连接状态由 AUN 插件自己建立和查询,只有当前已连接的身份才能关联 Agent。
|
|
24
|
+
AUN 插件通过 SDK 枚举 EvolCore 的本地 AID,并逐个建立连接;ABWeb 只展示已连接 AID,并按 EvolCore 与 mu-agent-bridge 分组,未连接身份不显示。状态检测不调用 `ec agent list/show`。
|
|
25
25
|
创建新 AID 必须填写 Owner AID,并安装、启动 AUN 插件;创建与通信由该插件完成。
|
|
26
|
-
|
|
27
|
-
关联 AID 到 DSH 或千问办公等 Agent 时,由 AUN 插件直接保持该身份连接并把来信转给
|
|
28
|
-
对应 Agent,回复经 AUN 发送;关联不再执行 `ec agent set/reload` 配置 EvolCore 路由。
|
|
26
|
+
本工程创建的身份由 AUN 插件保持连接并收发消息;EvolCore 来源身份只用于连接状态检测,不订阅消息,避免重复收信。将 EvolCore 身份关联到 Agent 时,AUN 插件仍执行既有 `ec agent set/reload` 路由配置;该命令不用于检测连接状态。
|
|
29
27
|
其他 Agent 插件可关联 AUN 插件自己创建的身份,由通道直接转发来信和回复。
|
|
30
28
|
Windows 可运行 `start-mab.ps1` 注册并启动登录时自动运行的计划任务;令牌保存在
|
|
31
29
|
`%USERPROFILE%\.mab\token.txt`,页面输入该令牌即可使用管理 API。
|
|
@@ -42,7 +40,14 @@ abweb 的“配置码”栏;配对码 10 分钟有效,只能使用一次,
|
|
|
42
40
|
|
|
43
41
|
## npm 插件
|
|
44
42
|
|
|
45
|
-
默认包 `mu-agent-bridge`
|
|
43
|
+
默认包 `mu-agent-bridge` 已内置 abweb、AUN、千问办公、DSH、豆包、飞书、WorkBuddy 和 ChatGPT/Codex 八个插件,直接安装即可使用:
|
|
44
|
+
|
|
45
|
+
```powershell
|
|
46
|
+
npm install -g mu-agent-bridge@latest
|
|
47
|
+
mab start
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
八个插件会从核心包的 `plugins/` 目录自动发现。插件仍分别发布为独立 npm 包,可用于单独测试、升级或替换版本。安装与发布说明见
|
|
46
51
|
[`docs/npm-publishing.md`](docs/npm-publishing.md)。
|
|
47
52
|
|
|
48
53
|
`mab start` 启动时会查询核心包和托管插件的 npm 最新版本,检查状态保存于 mab 状态目录的
|
|
@@ -50,6 +55,9 @@ abweb 的“配置码”栏;配对码 10 分钟有效,只能使用一次,
|
|
|
50
55
|
升级失败会恢复旧插件并继续启动。Windows 的 `start-mab.ps1` 登录计划任务会在启动 daemon 前
|
|
51
56
|
更新全局核心包,健康检查失败时恢复备份。全局单独安装的插件及 Linux 核心自动升级暂不受管理。
|
|
52
57
|
|
|
58
|
+
可用 `mab status` 查看后台、插件以及 AUN 已连接 AID 和关联 Agent 的状态。AUN 插件自己加载本地
|
|
59
|
+
`~/.evolcore/AIDs` 并建立连接,不要求先启动 `ec start`,也不调用 `ec agent list/show`。
|
|
60
|
+
|
|
53
61
|
插件更新策略可在 ABWeb 的“版本管理”中选择:默认“自动下载并安装”;“自动下载,手动安装”会把新版本缓存到状态目录并等待页面操作;“手动下载和安装”只检查版本。策略保存在 `state/update-policy.json`,插件下载缓存位于 `state/downloads`。
|
|
54
62
|
|
|
55
63
|
运行时会发现核心包依赖和 `AGENT_BRIDGE_PLUGIN_PACKAGES` 中声明的 npm 包。插件包在自己的
|
|
@@ -64,7 +72,7 @@ abweb 插件目录从 npm 搜索带 `mu-agent-bridge-plugin` 关键词的包,
|
|
|
64
72
|
插件仍在 daemon 的同一个 Node.js 进程内运行。修改插件入口依赖后应重启 daemon,避免模块缓存
|
|
65
73
|
影响新代码。
|
|
66
74
|
|
|
67
|
-
npm
|
|
75
|
+
npm 尚未发布时,核心包内置的八个默认插件仍会出现在插件列表。abweb 的“安装本地插件”
|
|
68
76
|
可填写 **Windows 主机上**包含 `plugin.json` 的目录路径,例如编译后的
|
|
69
77
|
`C:\path\to\agent_bridge\packages\dsh`;安装后可在下拉框中启动。已内置的千问办公无需再次安装。
|
|
70
78
|
“安装 npm 插件”只适用于包已经公开发布的情况。npm 发布令牌仅供发布脚本向 npm 官网认证,
|
|
@@ -100,7 +108,7 @@ $env:CODEBUDDY_NPM_DIR = 'C:\path\to\node_modules\@tencent-ai\codebuddy-code'
|
|
|
100
108
|
|
|
101
109
|
千问工作区默认使用 `%APPDATA%\QwenWorkCN\workspace`,可用 `$env:QWEN_WORKSPACE`
|
|
102
110
|
覆盖;AUN 数据默认使用 `$env:EVOLCORE_HOME` 或用户目录下的 `.evolcore`。
|
|
103
|
-
千问 CDP 端口默认为 `
|
|
111
|
+
千问 CDP 端口默认为 `9228`,也可用 `$env:QWEN_CDP_PORT` 或 `$env:AGENT_BRIDGE_QWEN_CDP_PORT` 覆盖。守护进程注入端口优先级最高,用来覆盖插件配置中的冲突端口;之后依次读取插件配置、环境变量和默认值。守护进程监听端口被占用时,会按 `AGENT_BRIDGE_FALLBACK_PORTS` 或系统临时端口回退,并在启动日志中报告实际端口。豆包和 WorkBuddy 分别使用 `DOUBAO_CDP_PORT`、`WORKBUDDY_CDP_PORT`,也支持对应的 `AGENT_BRIDGE_*_CDP_PORT`。
|
|
104
112
|
|
|
105
113
|
## 自检与运行
|
|
106
114
|
|
package/dist/src-ts/cli.js
CHANGED
|
@@ -37,6 +37,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
37
37
|
const node_path_1 = require("node:path");
|
|
38
38
|
const node_http_1 = require("node:http");
|
|
39
39
|
const node_fs_1 = require("node:fs");
|
|
40
|
+
const promises_1 = require("node:fs/promises");
|
|
40
41
|
const node_os_1 = require("node:os");
|
|
41
42
|
const node_child_process_1 = require("node:child_process");
|
|
42
43
|
const node_util_1 = require("node:util");
|
|
@@ -70,8 +71,17 @@ async function selfTest(task) {
|
|
|
70
71
|
console.error(`session=${response.sessionId}`);
|
|
71
72
|
return 0;
|
|
72
73
|
}
|
|
74
|
+
async function daemonPort() {
|
|
75
|
+
try {
|
|
76
|
+
const address = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.resolve)(stateDirectory(), "daemon-address.json"), "utf8"));
|
|
77
|
+
if (Number.isInteger(address.port) && address.port > 0)
|
|
78
|
+
return address.port;
|
|
79
|
+
}
|
|
80
|
+
catch { /* 后台未写入地址时使用配置端口 */ }
|
|
81
|
+
return Number(process.env.AGENT_BRIDGE_PORT ?? "26107");
|
|
82
|
+
}
|
|
73
83
|
async function pair() {
|
|
74
|
-
const port =
|
|
84
|
+
const port = await daemonPort();
|
|
75
85
|
const result = await new Promise((resolve, reject) => {
|
|
76
86
|
const req = (0, node_http_1.request)({ host: "127.0.0.1", port, path: "/api/pair-code", method: "GET" }, (res) => {
|
|
77
87
|
let body = "";
|
|
@@ -109,6 +119,50 @@ async function update() {
|
|
|
109
119
|
console.log("核心已更新,已请求 mab 计划任务重新启动");
|
|
110
120
|
return 0;
|
|
111
121
|
}
|
|
122
|
+
async function status() {
|
|
123
|
+
const port = await daemonPort();
|
|
124
|
+
const get = (path) => new Promise((resolve, reject) => {
|
|
125
|
+
const req = (0, node_http_1.request)({ host: "127.0.0.1", port, path, method: "GET" }, (res) => {
|
|
126
|
+
let body = "";
|
|
127
|
+
res.setEncoding("utf8");
|
|
128
|
+
res.on("data", (chunk) => body += chunk);
|
|
129
|
+
res.on("end", () => {
|
|
130
|
+
try {
|
|
131
|
+
const value = JSON.parse(body);
|
|
132
|
+
if ((res.statusCode ?? 500) >= 400)
|
|
133
|
+
reject(new Error(value.error ?? `HTTP ${res.statusCode}`));
|
|
134
|
+
else
|
|
135
|
+
resolve(value);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
reject(new Error("后台响应格式错误"));
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
req.setTimeout(5000, () => req.destroy(new Error("连接后台超时")));
|
|
143
|
+
req.on("error", reject);
|
|
144
|
+
req.end();
|
|
145
|
+
});
|
|
146
|
+
try {
|
|
147
|
+
const [plugins, health, aids] = await Promise.all([get("/api/plugins"), get("/api/health"), get("/api/plugins/aun/identities")]);
|
|
148
|
+
console.log(`后台:运行中(端口 ${port})`);
|
|
149
|
+
console.log(`插件:${health.plugins ?? plugins.plugins?.length ?? 0} 个`);
|
|
150
|
+
for (const plugin of plugins.plugins ?? [])
|
|
151
|
+
console.log(` ${plugin.id} ${plugin.version}:${plugin.status}`);
|
|
152
|
+
const groups = aids.groups ?? { muAgentBridge: [], evolcore: [] };
|
|
153
|
+
const all = aids.aids ?? [...(groups.muAgentBridge ?? []), ...(groups.evolcore ?? [])];
|
|
154
|
+
console.log(`AUN 已连接 AID:${all.length} 个`);
|
|
155
|
+
for (const aid of groups.muAgentBridge ?? [])
|
|
156
|
+
console.log(` ${aid}:本工程${aids.bindings?.[aid] ? ` → ${aids.bindings[aid]}` : ""}`);
|
|
157
|
+
for (const aid of groups.evolcore ?? [])
|
|
158
|
+
console.log(` ${aid}:EvolCore${aids.bindings?.[aid] ? ` → ${aids.bindings[aid]}` : ""}`);
|
|
159
|
+
return 0;
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
console.error(`后台未运行或无法访问:${error instanceof Error ? error.message : String(error)}`);
|
|
163
|
+
return 1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
112
166
|
async function serve() {
|
|
113
167
|
const manager = new plugin_manager_1.PluginManager({
|
|
114
168
|
pluginsDir: (0, node_path_1.resolve)(process.env.AGENT_BRIDGE_PLUGINS_DIR ?? (0, node_path_1.resolve)(__dirname, "..", "..", "plugins")),
|
|
@@ -125,8 +179,14 @@ async function serve() {
|
|
|
125
179
|
if (host !== "127.0.0.1" && host !== "localhost" && host !== "::1" && !process.env.AGENT_BRIDGE_TOKEN) {
|
|
126
180
|
throw new Error("局域网监听必须设置 AGENT_BRIDGE_TOKEN");
|
|
127
181
|
}
|
|
128
|
-
const
|
|
182
|
+
const fallbackPorts = (process.env.AGENT_BRIDGE_FALLBACK_PORTS ?? "").split(",").map((value) => Number(value.trim())).filter((value) => Number.isInteger(value) && value >= 0);
|
|
183
|
+
if (port > 0 && !fallbackPorts.length)
|
|
184
|
+
fallbackPorts.push(0);
|
|
185
|
+
const server = new control_server_1.ControlServer({ manager, host, port, fallbackPorts, token: process.env.AGENT_BRIDGE_TOKEN });
|
|
129
186
|
const address = await server.start();
|
|
187
|
+
const addressFile = (0, node_path_1.resolve)(stateDirectory(), "daemon-address.json");
|
|
188
|
+
await (0, promises_1.mkdir)(stateDirectory(), { recursive: true });
|
|
189
|
+
await (0, promises_1.writeFile)(addressFile, JSON.stringify({ host: address.host, port: address.port, pid: process.pid }));
|
|
130
190
|
logger.info(`daemon listening on http://${address.host}:${address.port}`);
|
|
131
191
|
const order = ["abweb", ...manager.list().map((plugin) => plugin.id).filter((id) => id !== "abweb")];
|
|
132
192
|
for (const id of order) {
|
|
@@ -155,6 +215,12 @@ async function serve() {
|
|
|
155
215
|
}
|
|
156
216
|
}
|
|
157
217
|
await server.stop();
|
|
218
|
+
try {
|
|
219
|
+
const address = JSON.parse(await (0, promises_1.readFile)(addressFile, "utf8"));
|
|
220
|
+
if (address.pid === process.pid)
|
|
221
|
+
await (0, promises_1.rm)(addressFile, { force: true });
|
|
222
|
+
}
|
|
223
|
+
catch { /* 地址文件可能已被清理 */ }
|
|
158
224
|
clearInterval(updateTimer);
|
|
159
225
|
};
|
|
160
226
|
process.once("SIGINT", () => void shutdown().finally(() => process.exit(0)));
|
|
@@ -201,6 +267,8 @@ async function main() {
|
|
|
201
267
|
const args = process.argv.slice(2);
|
|
202
268
|
if (args.includes("pair"))
|
|
203
269
|
return pair();
|
|
270
|
+
if (args[0] === "status")
|
|
271
|
+
return status();
|
|
204
272
|
if (args[0] === "update")
|
|
205
273
|
return update();
|
|
206
274
|
const selfTestIndex = args.indexOf("--self-test");
|
|
@@ -208,7 +276,7 @@ async function main() {
|
|
|
208
276
|
return selfTest(args[selfTestIndex + 1] ?? "hi");
|
|
209
277
|
if (args.includes("--serve") || args.includes("start"))
|
|
210
278
|
return serve();
|
|
211
|
-
console.error("用法:mab start | mab --self-test <任务>");
|
|
279
|
+
console.error("用法:mab start | mab status | mab --self-test <任务>");
|
|
212
280
|
return 2;
|
|
213
281
|
}
|
|
214
282
|
main().then((code) => { process.exitCode = code; }).catch((error) => {
|
|
@@ -4,9 +4,10 @@ exports.ControlServer = exports.listExistingAids = void 0;
|
|
|
4
4
|
const node_http_1 = require("node:http");
|
|
5
5
|
const node_child_process_1 = require("node:child_process");
|
|
6
6
|
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
const local_aids_1 = require("./local-aids");
|
|
7
8
|
const openai_adapter_1 = require("./plugins/qwenwork/openai-adapter");
|
|
8
|
-
var
|
|
9
|
-
Object.defineProperty(exports, "listExistingAids", { enumerable: true, get: function () { return
|
|
9
|
+
var local_aids_2 = require("./local-aids");
|
|
10
|
+
Object.defineProperty(exports, "listExistingAids", { enumerable: true, get: function () { return local_aids_2.listExistingAids; } });
|
|
10
11
|
const AID_PATTERN = /^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?){2,}$/;
|
|
11
12
|
class ControlServer {
|
|
12
13
|
options;
|
|
@@ -21,8 +22,36 @@ class ControlServer {
|
|
|
21
22
|
this.options = options;
|
|
22
23
|
this.server = (0, node_http_1.createServer)((req, res) => { void this.handle(req, res); });
|
|
23
24
|
}
|
|
24
|
-
async start() {
|
|
25
|
-
|
|
25
|
+
async start() {
|
|
26
|
+
const host = this.options.host ?? "127.0.0.1";
|
|
27
|
+
const candidates = [this.options.port ?? 0, ...(this.options.fallbackPorts ?? [])];
|
|
28
|
+
for (const port of candidates) {
|
|
29
|
+
try {
|
|
30
|
+
const address = await new Promise((resolve, reject) => {
|
|
31
|
+
const onError = (error) => { this.server.removeListener("listening", onListening); reject(error); };
|
|
32
|
+
const onListening = () => {
|
|
33
|
+
this.server.removeListener("error", onError);
|
|
34
|
+
const info = this.server.address();
|
|
35
|
+
if (!info || typeof info === "string") {
|
|
36
|
+
reject(new Error("后台监听地址无效"));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
resolve({ host, port: info.port });
|
|
40
|
+
};
|
|
41
|
+
this.server.once("error", onError);
|
|
42
|
+
this.server.once("listening", onListening);
|
|
43
|
+
this.server.listen(port, host);
|
|
44
|
+
});
|
|
45
|
+
this.address = address;
|
|
46
|
+
return address;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error.code !== "EADDRINUSE" || port === candidates.at(-1))
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
throw new Error("后台监听失败");
|
|
54
|
+
}
|
|
26
55
|
async stop() { if (!this.server.listening)
|
|
27
56
|
return; await new Promise((resolve, reject) => this.server.close((error) => error ? reject(error) : resolve())); }
|
|
28
57
|
async handle(req, res) {
|
|
@@ -102,9 +131,8 @@ class ControlServer {
|
|
|
102
131
|
if (method === "GET" && url.pathname === "/api/aids") {
|
|
103
132
|
const identity = this.identity();
|
|
104
133
|
const groups = identity ? await identity.list() : { evolcore: [], muAgentBridge: [] };
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return this.json(res, 200, { aids, groups, localAids: [], canCreate: Boolean(identity), message: identity ? "" : "请先安装并启动 AUN 插件,才能创建或关联 AID" });
|
|
134
|
+
const aids = [...new Set([...groups.evolcore, ...groups.muAgentBridge])];
|
|
135
|
+
return this.json(res, 200, { aids, groups, localAids: (await (0, local_aids_1.listExistingAids)()).filter((aid) => !aids.includes(aid)), canCreate: Boolean(identity), message: identity ? "" : "请先安装并启动 AUN 插件,才能创建或关联 AID" });
|
|
108
136
|
}
|
|
109
137
|
if (method === "POST" && url.pathname === "/api/aids") {
|
|
110
138
|
const body = await this.body(req);
|
|
@@ -189,6 +217,13 @@ class ControlServer {
|
|
|
189
217
|
}
|
|
190
218
|
if (await this.options.manager.dispatchHttp(req, res))
|
|
191
219
|
return;
|
|
220
|
+
// 根路径直接进入 abweb 管理页面,避免用户手敲 /abweb/
|
|
221
|
+
if (method === "GET" && (url.pathname === "/" || url.pathname === "")) {
|
|
222
|
+
res.statusCode = 302;
|
|
223
|
+
res.setHeader("location", "/abweb/");
|
|
224
|
+
res.end();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
192
227
|
return this.json(res, 404, { error: "not found" });
|
|
193
228
|
}
|
|
194
229
|
catch (error) {
|
|
@@ -7,7 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
*/
|
|
8
8
|
const node_http_1 = require("node:http");
|
|
9
9
|
const node_crypto_1 = require("node:crypto");
|
|
10
|
-
const PORT = Number(process.env.QWEN_CDP_PORT ?? '
|
|
10
|
+
const PORT = Number(process.env.QWEN_CDP_PORT ?? '9228');
|
|
11
11
|
const SUBCHAT = process.env.QWEN_SUBCHAT ?? 'muj55ep9426gudjw';
|
|
12
12
|
const CHAT = process.env.QWEN_CHAT ?? 'muj55ep9u6wbv0eo';
|
|
13
13
|
const CWD = process.env.QWEN_WORKSPACE ?? `${process.env.APPDATA ?? ''}\\QwenWorkCN\\workspace`;
|
|
@@ -4,8 +4,8 @@ exports.listExistingAids = listExistingAids;
|
|
|
4
4
|
const promises_1 = require("node:fs/promises");
|
|
5
5
|
const node_os_1 = require("node:os");
|
|
6
6
|
const node_path_1 = require("node:path");
|
|
7
|
-
async function listExistingAids() {
|
|
8
|
-
const root = (0, node_path_1.resolve)(process.env.EVOLCORE_HOME ?? (0, node_path_1.join)((0, node_os_1.homedir)(), ".evolcore"), "AIDs");
|
|
7
|
+
async function listExistingAids(aunRoot) {
|
|
8
|
+
const root = (0, node_path_1.resolve)(aunRoot ?? process.env.EVOLCORE_HOME ?? (0, node_path_1.join)((0, node_os_1.homedir)(), ".evolcore"), "AIDs");
|
|
9
9
|
let names;
|
|
10
10
|
try {
|
|
11
11
|
names = await (0, promises_1.readdir)(root);
|
|
@@ -138,8 +138,11 @@ class PluginManager {
|
|
|
138
138
|
this.log("info", `开始升级插件:${id}`, { packageName, version }, "插件升级");
|
|
139
139
|
await (0, promises_1.mkdir)(staging, { recursive: true });
|
|
140
140
|
try {
|
|
141
|
-
if (!cached)
|
|
142
|
-
await this.downloadPackage(packageName, version)
|
|
141
|
+
if (!cached) {
|
|
142
|
+
const downloaded = await this.downloadPackage(packageName, version);
|
|
143
|
+
await (0, promises_1.cp)(downloaded, staging, { recursive: true, force: true });
|
|
144
|
+
await (0, promises_1.rm)(downloaded, { recursive: true, force: true });
|
|
145
|
+
}
|
|
143
146
|
const candidate = (0, node_path_1.join)(staging, "node_modules", packageName);
|
|
144
147
|
const meta = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(candidate, "package.json"), "utf8"));
|
|
145
148
|
if (meta.name !== packageName || !meta.version || (version && meta.version !== version))
|
|
@@ -307,6 +310,11 @@ class PluginManager {
|
|
|
307
310
|
}
|
|
308
311
|
},
|
|
309
312
|
},
|
|
313
|
+
runtime: { ports: {
|
|
314
|
+
qwenwork: Number(process.env.AGENT_BRIDGE_QWEN_CDP_PORT ?? process.env.QWEN_CDP_PORT ?? "9228"),
|
|
315
|
+
doubao: Number(process.env.AGENT_BRIDGE_DOUBAO_CDP_PORT ?? process.env.DOUBAO_CDP_PORT ?? "9227"),
|
|
316
|
+
workbuddy: Number(process.env.AGENT_BRIDGE_WORKBUDDY_CDP_PORT ?? process.env.WORKBUDDY_CDP_PORT ?? "9229"),
|
|
317
|
+
} },
|
|
310
318
|
});
|
|
311
319
|
loaded.instance = instance;
|
|
312
320
|
loaded.record.status = "ready";
|
|
@@ -4,6 +4,7 @@ exports.validateManifest = validateManifest;
|
|
|
4
4
|
const node_path_1 = require("node:path");
|
|
5
5
|
const agent_1 = require("./agent");
|
|
6
6
|
const semver = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
7
|
+
const httpUrl = /^https?:\/\/.+$/;
|
|
7
8
|
function validateManifest(value, baseDir) {
|
|
8
9
|
if (!value || typeof value !== "object")
|
|
9
10
|
throw new agent_1.AgentError("invalid plugin manifest");
|
|
@@ -27,6 +28,8 @@ function validateManifest(value, baseDir) {
|
|
|
27
28
|
throw new agent_1.AgentError("invalid plugin process");
|
|
28
29
|
if (m.managementPage !== undefined && ((m.type ?? inferredType) !== "channel" || m.managementPage !== `/abweb/channels/${m.id}`))
|
|
29
30
|
throw new agent_1.AgentError("插件管理页面路径无效");
|
|
31
|
+
if (m.clientDownloadUrl !== undefined && (typeof m.clientDownloadUrl !== "string" || !httpUrl.test(m.clientDownloadUrl)))
|
|
32
|
+
throw new agent_1.AgentError("客户端下载地址必须是 http(s) URL");
|
|
30
33
|
if (m.dependencies && (!m.dependencies || typeof m.dependencies !== "object" || Array.isArray(m.dependencies) || Object.entries(m.dependencies).some(([id, range]) => !/^[a-z][a-z0-9-]{1,63}$/.test(id) || typeof range !== "string" || !range.trim())))
|
|
31
34
|
throw new agent_1.AgentError("插件依赖声明无效");
|
|
32
35
|
if (baseDir) {
|
|
@@ -36,5 +39,5 @@ function validateManifest(value, baseDir) {
|
|
|
36
39
|
if ((0, node_path_1.isAbsolute)(m.entry) || rel.startsWith(`..${node_path_1.sep}`) || rel === "..")
|
|
37
40
|
throw new agent_1.AgentError("plugin entry path escapes plugin directory");
|
|
38
41
|
}
|
|
39
|
-
return { id: m.id, version: m.version, displayName: m.displayName ?? m.id, type: m.type ?? inferredType, entry: m.entry, apiVersion: m.apiVersion, process: m.process ?? "in-process", capabilities: [...m.capabilities], configSchema: m.configSchema, routes: m.routes, ...(m.dependencies ? { dependencies: { ...m.dependencies } } : {}), ...(m.managementPage ? { managementPage: m.managementPage } : {}) };
|
|
42
|
+
return { id: m.id, version: m.version, displayName: m.displayName ?? m.id, type: m.type ?? inferredType, entry: m.entry, apiVersion: m.apiVersion, process: m.process ?? "in-process", capabilities: [...m.capabilities], configSchema: m.configSchema, routes: m.routes, ...(m.dependencies ? { dependencies: { ...m.dependencies } } : {}), ...(m.managementPage ? { managementPage: m.managementPage } : {}), ...(m.clientDownloadUrl ? { clientDownloadUrl: m.clientDownloadUrl } : {}) };
|
|
40
43
|
}
|
package/dist/src-ts/plugin.js
CHANGED
|
@@ -1,2 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveRuntimePort = resolveRuntimePort;
|
|
4
|
+
function resolveRuntimePort(config, runtime, pluginId, envName, fallback) {
|
|
5
|
+
const injected = runtime?.ports?.[pluginId];
|
|
6
|
+
if (typeof injected === "number" && Number.isInteger(injected) && injected > 0)
|
|
7
|
+
return injected;
|
|
8
|
+
const configured = config.cdpPort;
|
|
9
|
+
if (typeof configured === "number" && Number.isInteger(configured) && configured > 0)
|
|
10
|
+
return configured;
|
|
11
|
+
const environment = Number(process.env[envName] ?? "");
|
|
12
|
+
return Number.isInteger(environment) && environment > 0 ? environment : fallback;
|
|
13
|
+
}
|
|
@@ -239,6 +239,16 @@
|
|
|
239
239
|
detail.textContent = `依赖:${plugin.dependencyStatus.join(";")}`;
|
|
240
240
|
state.append(detail);
|
|
241
241
|
}
|
|
242
|
+
// 插件未就绪且声明了客户端下载地址时,给出获取桌面客户端的入口
|
|
243
|
+
if (plugin.manifest?.clientDownloadUrl && plugin.status !== "ready") {
|
|
244
|
+
const link = document.createElement("a");
|
|
245
|
+
link.className = "plugin-id";
|
|
246
|
+
link.href = plugin.manifest.clientDownloadUrl;
|
|
247
|
+
link.target = "_blank";
|
|
248
|
+
link.rel = "noreferrer noopener";
|
|
249
|
+
link.textContent = "下载客户端";
|
|
250
|
+
state.append(link);
|
|
251
|
+
}
|
|
242
252
|
const controls = document.createElement("td");
|
|
243
253
|
controls.className = "controls";
|
|
244
254
|
if (pluginVersions[plugin.id]?.state === "update-available" && plugin.path.includes("node_modules")) {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runEc = runEc;
|
|
4
|
+
exports.configureAidForPlugin = configureAidForPlugin;
|
|
5
|
+
const node_child_process_1 = require("node:child_process");
|
|
6
|
+
const node_util_1 = require("node:util");
|
|
7
|
+
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
8
|
+
async function runEc(args) {
|
|
9
|
+
try {
|
|
10
|
+
const { stdout } = await execFileAsync("ec", args, { shell: process.platform === "win32", timeout: 15_000, maxBuffer: 1024 * 1024 });
|
|
11
|
+
return JSON.parse(stdout);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
const stdout = error.stdout;
|
|
15
|
+
if (stdout) {
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(stdout);
|
|
18
|
+
}
|
|
19
|
+
catch { /* 保留原始命令错误 */ }
|
|
20
|
+
}
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function configureAidForPlugin(pluginId, aid, port, run = runEc) {
|
|
25
|
+
if (!aid)
|
|
26
|
+
return;
|
|
27
|
+
if (!/^[a-z0-9_-]+$/.test(pluginId))
|
|
28
|
+
throw new Error("插件 ID 格式无效");
|
|
29
|
+
const path = pluginId === "dsh" ? "/dsh/v1" : pluginId === "qwenwork" ? "/v1" : `/api/agents/${pluginId}/v1`;
|
|
30
|
+
for (const [key, value] of [
|
|
31
|
+
["baseagents.ecagent.baseUrl", `http://127.0.0.1:${port}${path}`],
|
|
32
|
+
["baseagents.ecagent.apiKey", "mab-local"],
|
|
33
|
+
["baseagents.ecagent.model", pluginId],
|
|
34
|
+
["active_baseagent", "ecagent"],
|
|
35
|
+
]) {
|
|
36
|
+
const result = await run(["agent", "set", aid, key, value, "--format", "json"]);
|
|
37
|
+
if (!result?.ok)
|
|
38
|
+
throw new Error(`配置 AID ${aid} 失败:${key};${result?.code ?? ""} ${result?.error ?? "未知错误"}`);
|
|
39
|
+
}
|
|
40
|
+
const result = await run(["agent", "reload", aid, "--format", "json"]);
|
|
41
|
+
if (!result?.ok)
|
|
42
|
+
throw new Error(`重载 AID ${aid} 失败:${result?.code ?? ""} ${result?.error ?? "未知错误"}`);
|
|
43
|
+
}
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AunPlugin = void 0;
|
|
4
4
|
const promises_1 = require("node:fs/promises");
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
5
6
|
const node_module_1 = require("node:module");
|
|
6
7
|
const node_path_1 = require("node:path");
|
|
8
|
+
const node_os_1 = require("node:os");
|
|
7
9
|
const node_url_1 = require("node:url");
|
|
8
10
|
const messages_1 = require("./messages");
|
|
11
|
+
const evolcore_1 = require("./evolcore");
|
|
9
12
|
const manifest = {
|
|
10
|
-
id: "aun", version: "1.0.
|
|
13
|
+
id: "aun", version: "1.0.10", type: "channel", displayName: "AUN 通道",
|
|
11
14
|
entry: "dist/src-ts/plugins/aun/index.js", apiVersion: "1", process: "in-process",
|
|
12
15
|
capabilities: ["channel", "channel.identity.create", "channel.receive", "channel.send", "health"],
|
|
13
16
|
configSchema: "config.schema.json",
|
|
@@ -36,6 +39,7 @@ class AunPlugin {
|
|
|
36
39
|
store;
|
|
37
40
|
clients = new Map();
|
|
38
41
|
identities = new Set();
|
|
42
|
+
legacyCreatedAids = new Set();
|
|
39
43
|
bindings = {};
|
|
40
44
|
bindingTimes = {};
|
|
41
45
|
creating = false;
|
|
@@ -53,13 +57,11 @@ class AunPlugin {
|
|
|
53
57
|
}
|
|
54
58
|
identity = {
|
|
55
59
|
list: async () => {
|
|
56
|
-
|
|
57
|
-
const local = this.listStoreAids();
|
|
60
|
+
const connected = [...this.clients.keys()];
|
|
58
61
|
return {
|
|
59
|
-
evolcore:
|
|
60
|
-
muAgentBridge:
|
|
62
|
+
evolcore: connected.filter((aid) => !this.identities.has(aid) && !this.legacyCreatedAids.has(aid)).sort(),
|
|
63
|
+
muAgentBridge: connected.filter((aid) => this.identities.has(aid) || this.legacyCreatedAids.has(aid)).sort(),
|
|
61
64
|
localMuAgentBridge: [...this.identities].filter((aid) => !this.clients.has(aid)).sort(),
|
|
62
|
-
local,
|
|
63
65
|
};
|
|
64
66
|
},
|
|
65
67
|
create: async (aid, owner) => {
|
|
@@ -94,7 +96,7 @@ class AunPlugin {
|
|
|
94
96
|
this.creating = false;
|
|
95
97
|
}
|
|
96
98
|
},
|
|
97
|
-
bind: async (pluginId, aid,
|
|
99
|
+
bind: async (pluginId, aid, port) => {
|
|
98
100
|
if (pluginId === "aun" || pluginId === "abweb")
|
|
99
101
|
throw new Error("只能关联 Agent 插件");
|
|
100
102
|
const previous = this.bindings;
|
|
@@ -107,7 +109,7 @@ class AunPlugin {
|
|
|
107
109
|
throw new Error("AID 格式无效");
|
|
108
110
|
if (this.context?.pluginStatus(pluginId) !== "ready")
|
|
109
111
|
throw new Error("目标 Agent 插件未启动");
|
|
110
|
-
if (this.identities.has(aid)) {
|
|
112
|
+
if (this.identities.has(aid) || this.legacyCreatedAids.has(aid)) {
|
|
111
113
|
if (!this.clients.has(aid))
|
|
112
114
|
await this.connectAid(aid);
|
|
113
115
|
if (!this.clients.has(aid))
|
|
@@ -120,20 +122,15 @@ class AunPlugin {
|
|
|
120
122
|
this.bindings[aid] = pluginId;
|
|
121
123
|
}
|
|
122
124
|
else {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (!loaded.ok || !loaded.data)
|
|
126
|
-
throw new Error("AID 不在本地密钥库中,无法关联");
|
|
127
|
-
await this.connectAid(aid);
|
|
128
|
-
if (!this.clients.has(aid))
|
|
125
|
+
const connected = await this.identity.list();
|
|
126
|
+
if (![...connected.evolcore, ...connected.muAgentBridge].includes(aid))
|
|
129
127
|
throw new Error("AID 当前未连接");
|
|
128
|
+
await (0, evolcore_1.configureAidForPlugin)(pluginId, aid, port);
|
|
130
129
|
for (const [identity, target] of Object.entries(this.bindings))
|
|
131
130
|
if (target === pluginId) {
|
|
132
131
|
delete this.bindings[identity];
|
|
133
132
|
delete this.bindingTimes[identity];
|
|
134
133
|
}
|
|
135
|
-
this.bindings[aid] = pluginId;
|
|
136
|
-
this.identities.add(aid);
|
|
137
134
|
}
|
|
138
135
|
}
|
|
139
136
|
else {
|
|
@@ -167,6 +164,18 @@ class AunPlugin {
|
|
|
167
164
|
async activate(context) {
|
|
168
165
|
this.context = context;
|
|
169
166
|
this.config = { ...context.config };
|
|
167
|
+
this.legacyCreatedAids.clear();
|
|
168
|
+
try {
|
|
169
|
+
const created = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(context.dataDir, "..", "created-aids.json"), "utf8"));
|
|
170
|
+
if (Array.isArray(created))
|
|
171
|
+
for (const aid of created)
|
|
172
|
+
if (typeof aid === "string" && aidPattern.test(aid))
|
|
173
|
+
this.legacyCreatedAids.add(aid);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
if (error.code !== "ENOENT")
|
|
177
|
+
context.logger.warn("读取旧版身份创建记录失败", { error: safeError(error) });
|
|
178
|
+
}
|
|
170
179
|
for (const key of ["aid", "owner_aid", "aun_path", "encryption_seed", "root_ca_path", "allow_from", "agent_plugin"]) {
|
|
171
180
|
if (this.config[key] !== undefined && typeof this.config[key] !== "string")
|
|
172
181
|
throw new Error(`${key} 必须是字符串`);
|
|
@@ -207,6 +216,10 @@ class AunPlugin {
|
|
|
207
216
|
this.identities.add(this.aid);
|
|
208
217
|
for (const aid of this.identities)
|
|
209
218
|
await this.connectAid(aid);
|
|
219
|
+
// AUN 插件直接连接本地密钥库中的所有 AID,不依赖 ec 进程或 CLI。
|
|
220
|
+
for (const aid of this.listStoreAids())
|
|
221
|
+
if (!this.clients.has(aid))
|
|
222
|
+
await this.connectAid(aid);
|
|
210
223
|
}
|
|
211
224
|
async connectAid(aid) {
|
|
212
225
|
try {
|
|
@@ -214,12 +227,14 @@ class AunPlugin {
|
|
|
214
227
|
if (!loaded.ok || !loaded.data)
|
|
215
228
|
throw new Error(loaded.error?.message ?? "本地密钥库中不存在该 AID");
|
|
216
229
|
const client = new this.sdk.AUNClient(loaded.data.aid);
|
|
217
|
-
const subscription =
|
|
230
|
+
const subscription = this.identities.has(aid) || this.legacyCreatedAids.has(aid)
|
|
231
|
+
? client.on("message.received", (payload) => this.onMessage(payload, aid))
|
|
232
|
+
: undefined;
|
|
218
233
|
try {
|
|
219
234
|
await client.connect();
|
|
220
235
|
}
|
|
221
236
|
catch (error) {
|
|
222
|
-
subscription
|
|
237
|
+
subscription?.unsubscribe();
|
|
223
238
|
await client.close();
|
|
224
239
|
throw error;
|
|
225
240
|
}
|
|
@@ -236,7 +251,7 @@ class AunPlugin {
|
|
|
236
251
|
async deactivate(_reason) {
|
|
237
252
|
this.status = "degraded";
|
|
238
253
|
for (const { client, subscription } of this.clients.values()) {
|
|
239
|
-
subscription
|
|
254
|
+
subscription?.unsubscribe();
|
|
240
255
|
await client.close();
|
|
241
256
|
}
|
|
242
257
|
this.clients.clear();
|
|
@@ -253,10 +268,13 @@ class AunPlugin {
|
|
|
253
268
|
async handle(_request) { throw new Error("AUN 是入站通道插件"); }
|
|
254
269
|
path() {
|
|
255
270
|
const configured = this.config.aun_path;
|
|
256
|
-
|
|
271
|
+
if (typeof configured === "string" && configured.trim())
|
|
272
|
+
return configured;
|
|
273
|
+
return (0, node_path_1.resolve)(process.env.AUN_HOME ?? process.env.EVOLCORE_HOME ?? (0, node_path_1.join)((0, node_os_1.homedir)(), ".evolcore"));
|
|
257
274
|
}
|
|
258
275
|
newStore() {
|
|
259
|
-
|
|
276
|
+
const rootCaPath = typeof this.config.root_ca_path === "string" && this.config.root_ca_path ? this.config.root_ca_path : (0, node_path_1.join)(this.path(), "CA", "root", "root.crt");
|
|
277
|
+
return new this.sdk.AIDStore({ aunPath: this.path(), encryptionSeed: typeof this.config.encryption_seed === "string" && this.config.encryption_seed ? this.config.encryption_seed : "evol", verifySsl: typeof this.config.verify_ssl === "boolean" ? this.config.verify_ssl : true, rootCaPath: (0, node_fs_1.existsSync)(rootCaPath) ? rootCaPath : undefined });
|
|
260
278
|
}
|
|
261
279
|
createAid = async (request, response) => {
|
|
262
280
|
let input;
|
|
@@ -301,8 +319,8 @@ class AunPlugin {
|
|
|
301
319
|
}
|
|
302
320
|
listIdentities = async (_request, response) => {
|
|
303
321
|
const groups = await this.identity.list();
|
|
304
|
-
const connected = new Set([...groups.evolcore, ...groups.muAgentBridge
|
|
305
|
-
const localAids = (
|
|
322
|
+
const connected = new Set([...groups.evolcore, ...groups.muAgentBridge]);
|
|
323
|
+
const localAids = this.listStoreAids().filter((aid) => !connected.has(aid));
|
|
306
324
|
this.json(response, 200, { groups, localAids, bindings: this.bindings, bindingTimes: this.bindingTimes });
|
|
307
325
|
};
|
|
308
326
|
saveSettings = async (request, response) => {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
configForm.elements.encryption_seed.value = "";
|
|
32
32
|
}
|
|
33
33
|
list.replaceChildren();
|
|
34
|
-
const entries = [["本工程 · 已连接", identities.groups.muAgentBridge || []], ["
|
|
34
|
+
const entries = [["本工程 · 已连接", identities.groups.muAgentBridge || []], ["EvolCore · 已连接", identities.groups.evolcore || []]]
|
|
35
35
|
.flatMap(([label, values], group) => values.map((aid) => ({ aid, label, group, bound: identities.bindings?.[aid] || agents.find((agent) => agent.id && agent.configAid === aid)?.id || "", time: Date.parse(identities.bindingTimes?.[aid] || "") || 0 })));
|
|
36
36
|
entries.sort((a, b) => Number(Boolean(b.bound)) - Number(Boolean(a.bound)) || a.group - b.group || b.time - a.time || a.aid.localeCompare(b.aid));
|
|
37
37
|
for (const { aid, label, bound } of entries) {
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
select.value = bound; select.onchange = () => { if (select.value) void bind(aid, select.value); };
|
|
43
43
|
cell.append(select); row.append(cell); list.append(row);
|
|
44
44
|
}
|
|
45
|
-
if (!list.children.length) { const row = document.createElement("tr"); const cell = document.createElement("td"); cell.colSpan = 4; cell.textContent = "
|
|
46
|
-
show(`${(identities.groups.
|
|
45
|
+
if (!list.children.length) { const row = document.createElement("tr"); const cell = document.createElement("td"); cell.colSpan = 4; cell.textContent = "暂无已连接 AID"; row.append(cell); list.append(row); }
|
|
46
|
+
show(`${(identities.groups.evolcore || []).length + (identities.groups.muAgentBridge || []).length} 个已连接 AID`);
|
|
47
47
|
} catch (error) { show(String(error), true); }
|
|
48
48
|
finally { loading = false; }
|
|
49
49
|
}
|