weacpx 0.1.5 → 0.1.6
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 +24 -0
- package/config.example.json +3 -1
- package/dist/bridge/bridge-main.js +41 -2
- package/dist/cli.js +237 -50
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -155,6 +155,8 @@ bun run dev
|
|
|
155
155
|
| `/ss attach <alias> -a <name> --ws <name> --name <transport-session>` | 恢复已存在的会话 |
|
|
156
156
|
| `/use <alias>` | 切换当前会话 |
|
|
157
157
|
| `/status` | 查看当前会话状态 |
|
|
158
|
+
| `/mode` | 查看当前会话已保存的 mode |
|
|
159
|
+
| `/mode <id>` | 设置当前会话 mode,例如 `/mode plan` |
|
|
158
160
|
| `/session reset` | 重置当前会话上下文,保留 alias/agent/workspace,但重新绑定到一个新的后端 session |
|
|
159
161
|
| `/clear` | `/session reset` 的快捷别名 |
|
|
160
162
|
| `/cancel` | 取消当前会话 |
|
|
@@ -165,6 +167,8 @@ bun run dev
|
|
|
165
167
|
- `/ss <agent> -d <path>` 是最常用入口,会自动按目录名推导并创建或复用 workspace,再创建或复用 session
|
|
166
168
|
- `/ss new <agent> -d <path>` 表示强制新建 session
|
|
167
169
|
- `/use <alias>` 用来切换当前会话
|
|
170
|
+
- `/mode` 会显示当前逻辑会话里保存的 mode;如果还没设置过,会显示“未设置”
|
|
171
|
+
- `/mode <id>` 会把 mode 透传给底层 `acpx set-mode`,成功后再写回当前逻辑会话
|
|
168
172
|
- `/session reset` 和 `/clear` 会保留当前逻辑会话名,但重新创建一个新的后端 session,从空上下文重新开始
|
|
169
173
|
- 非 `/` 开头的文本会发送到当前 session
|
|
170
174
|
|
|
@@ -330,6 +334,26 @@ bun run dry-run --chat-key wx:test -- \
|
|
|
330
334
|
/ss attach demo -a codex --ws backend --name existing-demo
|
|
331
335
|
```
|
|
332
336
|
|
|
337
|
+
### Adapter mode 参考
|
|
338
|
+
|
|
339
|
+
`acpx set-mode` / 计划中的 `/mode <id>` 本质上都是给底层 ACP session 发送 `session/set_mode`。
|
|
340
|
+
这里的 `<id>` 不是 `weacpx` 或 `acpx` 统一规定的枚举,而是**各 adapter 自己定义**的值;填错时通常会收到 adapter 返回的 `Invalid params` 一类错误。
|
|
341
|
+
|
|
342
|
+
基于 `acpx` 内置 adapter 文档和各上游公开文档,当前能确认的信息如下:
|
|
343
|
+
|
|
344
|
+
| adapter | 已确认可用的 mode id | 说明 |
|
|
345
|
+
|------|------|------|
|
|
346
|
+
| `codex` | `plan` | `acpx` 自身示例明确使用过 `acpx codex set-mode plan`。`codex-acp` 还暴露了 `mode` 运行时配置项,但上游目前没有公开一份完整、稳定的 mode id 列表。 |
|
|
347
|
+
| `cursor` | `agent`、`plan`、`ask` | Cursor 官方文档/更新日志公开提到 `Plan mode`、`Ask mode`;Cursor 官方论坛在 ACP `session/configure` 示例中展示过 `availableModes` 为 `agent` / `plan` / `ask`。 |
|
|
348
|
+
| 其他内置 adapter | 暂无公开、稳定的 mode id 列表 | 包括 `claude`、`copilot`、`gemini`、`qoder`、`qwen`、`kimi`、`kiro`、`iflow`、`opencode`、`trae`、`droid`、`kilocode` 等。即使某些产品本身有“Ask / Agent / Plan”之类概念,其 ACP `set-mode` 可接受的精确字符串也往往没有在官方文档中写死。 |
|
|
349
|
+
|
|
350
|
+
建议:
|
|
351
|
+
|
|
352
|
+
- 对 `codex`,优先把 `plan` 当作已知可用值。
|
|
353
|
+
- 对 `cursor`,优先使用 `agent`、`plan`、`ask`。
|
|
354
|
+
- 对其他 adapter,不要在 `weacpx` 里写死候选值;最好把 `/mode <id>` 设计成透传,由 adapter 自己决定是否接受。
|
|
355
|
+
- 如果某个 adapter 后续补充了官方 mode 文档,再把它们补进这里。
|
|
356
|
+
|
|
333
357
|
## 更多文档
|
|
334
358
|
|
|
335
359
|
- 配置参考:[docs/config-reference.md](./docs/config-reference.md)
|
package/config.example.json
CHANGED
|
@@ -4,25 +4,43 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
7
12
|
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
8
20
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
21
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
22
|
for (let key of __getOwnPropNames(mod))
|
|
11
23
|
if (!__hasOwnProp.call(to, key))
|
|
12
24
|
__defProp(to, key, {
|
|
13
|
-
get: (
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
14
26
|
enumerable: true
|
|
15
27
|
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
16
30
|
return to;
|
|
17
31
|
};
|
|
18
32
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __returnValue = (v) => v;
|
|
34
|
+
function __exportSetter(name, newValue) {
|
|
35
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
36
|
+
}
|
|
19
37
|
var __export = (target, all) => {
|
|
20
38
|
for (var name in all)
|
|
21
39
|
__defProp(target, name, {
|
|
22
40
|
get: all[name],
|
|
23
41
|
enumerable: true,
|
|
24
42
|
configurable: true,
|
|
25
|
-
set: (
|
|
43
|
+
set: __exportSetter.bind(all, name)
|
|
26
44
|
});
|
|
27
45
|
};
|
|
28
46
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -244,6 +262,14 @@ class BridgeServer {
|
|
|
244
262
|
name: String(params.name),
|
|
245
263
|
text: String(params.text)
|
|
246
264
|
});
|
|
265
|
+
case "setMode":
|
|
266
|
+
return await this.runtime.setMode({
|
|
267
|
+
agent: String(params.agent),
|
|
268
|
+
agentCommand: asOptionalString(params.agentCommand),
|
|
269
|
+
cwd: String(params.cwd),
|
|
270
|
+
name: String(params.name),
|
|
271
|
+
modeId: String(params.modeId)
|
|
272
|
+
});
|
|
247
273
|
case "cancel":
|
|
248
274
|
return await this.runtime.cancel({
|
|
249
275
|
agent: String(params.agent),
|
|
@@ -322,6 +348,19 @@ class BridgeRuntime {
|
|
|
322
348
|
const result = await this.run(spawnSpec.command, spawnSpec.args);
|
|
323
349
|
return { text: getPromptText(result) };
|
|
324
350
|
}
|
|
351
|
+
async setMode(input) {
|
|
352
|
+
const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
|
|
353
|
+
"set-mode",
|
|
354
|
+
"-s",
|
|
355
|
+
input.name,
|
|
356
|
+
input.modeId
|
|
357
|
+
]));
|
|
358
|
+
const result = await this.run(spawnSpec.command, spawnSpec.args);
|
|
359
|
+
if (result.code !== 0) {
|
|
360
|
+
throw new Error(result.stderr || result.stdout || "set-mode failed");
|
|
361
|
+
}
|
|
362
|
+
return {};
|
|
363
|
+
}
|
|
325
364
|
async cancel(input) {
|
|
326
365
|
const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
|
|
327
366
|
"cancel",
|
package/dist/cli.js
CHANGED
|
@@ -5,25 +5,43 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
8
13
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
9
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
22
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
23
|
for (let key of __getOwnPropNames(mod))
|
|
12
24
|
if (!__hasOwnProp.call(to, key))
|
|
13
25
|
__defProp(to, key, {
|
|
14
|
-
get: (
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
15
27
|
enumerable: true
|
|
16
28
|
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
17
31
|
return to;
|
|
18
32
|
};
|
|
19
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
+
var __returnValue = (v) => v;
|
|
35
|
+
function __exportSetter(name, newValue) {
|
|
36
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
37
|
+
}
|
|
20
38
|
var __export = (target, all) => {
|
|
21
39
|
for (var name in all)
|
|
22
40
|
__defProp(target, name, {
|
|
23
41
|
get: all[name],
|
|
24
42
|
enumerable: true,
|
|
25
43
|
configurable: true,
|
|
26
|
-
set: (
|
|
44
|
+
set: __exportSetter.bind(all, name)
|
|
27
45
|
});
|
|
28
46
|
};
|
|
29
47
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -3338,26 +3356,41 @@ var init_agent_templates = __esm(() => {
|
|
|
3338
3356
|
function renderHelpText() {
|
|
3339
3357
|
return [
|
|
3340
3358
|
"可用命令:",
|
|
3341
|
-
"
|
|
3342
|
-
"
|
|
3343
|
-
"/agent
|
|
3344
|
-
"/
|
|
3345
|
-
"/
|
|
3346
|
-
"
|
|
3347
|
-
"
|
|
3348
|
-
"/
|
|
3349
|
-
"/
|
|
3350
|
-
"/
|
|
3351
|
-
"
|
|
3352
|
-
"
|
|
3353
|
-
"/
|
|
3354
|
-
"/
|
|
3355
|
-
"/
|
|
3356
|
-
"/
|
|
3357
|
-
"
|
|
3358
|
-
"
|
|
3359
|
-
"/
|
|
3360
|
-
"/session
|
|
3359
|
+
"",
|
|
3360
|
+
"先看这 3 个:",
|
|
3361
|
+
"/ss new <agent> -d <path> - 新建会话",
|
|
3362
|
+
"/use <alias> - 切会话",
|
|
3363
|
+
"/status - 看状态",
|
|
3364
|
+
"",
|
|
3365
|
+
"Agent:",
|
|
3366
|
+
"/agents - 看 Agent",
|
|
3367
|
+
"/agent add <codex|claude> - 加 Agent",
|
|
3368
|
+
"/agent rm <name> - 删 Agent",
|
|
3369
|
+
"",
|
|
3370
|
+
"工作区:",
|
|
3371
|
+
"/workspaces - 看工作区",
|
|
3372
|
+
"/workspace 或 /ws - 工作区命令",
|
|
3373
|
+
"/ws new <name> -d <path> - 加工作区",
|
|
3374
|
+
"/workspace rm <name> - 删工作区",
|
|
3375
|
+
"",
|
|
3376
|
+
"会话:",
|
|
3377
|
+
"/sessions - 看会话",
|
|
3378
|
+
"/session 或 /ss - 会话命令",
|
|
3379
|
+
"/ss <agent> -d <path> - 快速新建",
|
|
3380
|
+
"/ss new <agent> -d <path> - 新建会话",
|
|
3381
|
+
"/ss new <alias> -a <name> --ws <name> - 指定配置新建",
|
|
3382
|
+
"/ss attach <alias> -a <name> --ws <name> --name <transport-session> - 挂已有会话",
|
|
3383
|
+
"/use <alias> - 切会话",
|
|
3384
|
+
"/session reset 或 /clear - 清上下文",
|
|
3385
|
+
"",
|
|
3386
|
+
"权限:",
|
|
3387
|
+
"/pm 或 /permission - 权限设置",
|
|
3388
|
+
"/pm set <allow|read|deny> - 设审批级别",
|
|
3389
|
+
"/pm auto [allow|deny|fail] - 设自动处理",
|
|
3390
|
+
"",
|
|
3391
|
+
"常用:",
|
|
3392
|
+
"/status - 看状态",
|
|
3393
|
+
"/cancel 或 /stop - 停当前任务"
|
|
3361
3394
|
].join(`
|
|
3362
3395
|
`);
|
|
3363
3396
|
}
|
|
@@ -3498,6 +3531,27 @@ var init_app_logger = __esm(() => {
|
|
|
3498
3531
|
};
|
|
3499
3532
|
});
|
|
3500
3533
|
|
|
3534
|
+
// src/transport/acpx-session-index.ts
|
|
3535
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
3536
|
+
import { homedir } from "node:os";
|
|
3537
|
+
import { resolve } from "node:path";
|
|
3538
|
+
async function resolveSessionAgentCommandFromIndex(session) {
|
|
3539
|
+
const home = process.env.HOME ?? homedir();
|
|
3540
|
+
if (!home) {
|
|
3541
|
+
return;
|
|
3542
|
+
}
|
|
3543
|
+
try {
|
|
3544
|
+
const raw = await readFile3(resolve(home, ".acpx", "sessions", "index.json"), "utf8");
|
|
3545
|
+
const parsed = JSON.parse(raw);
|
|
3546
|
+
const targetCwd = resolve(session.cwd);
|
|
3547
|
+
const match = parsed.entries?.find((entry) => entry.name === session.transportSession && entry.cwd === targetCwd && typeof entry.agentCommand === "string" && entry.agentCommand.trim().length > 0);
|
|
3548
|
+
return match?.agentCommand?.trim();
|
|
3549
|
+
} catch {
|
|
3550
|
+
return;
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
var init_acpx_session_index = () => {};
|
|
3554
|
+
|
|
3501
3555
|
// src/transport/prompt-output.ts
|
|
3502
3556
|
function getPromptText(result) {
|
|
3503
3557
|
const stdoutOutput = extractPromptOutput(result.stdout);
|
|
@@ -3652,6 +3706,8 @@ function parseCommand(input) {
|
|
|
3652
3706
|
return { kind: "cancel" };
|
|
3653
3707
|
if (command === "/clear")
|
|
3654
3708
|
return { kind: "session.reset" };
|
|
3709
|
+
if (command === "/mode" && parts.length === 1)
|
|
3710
|
+
return { kind: "mode.show" };
|
|
3655
3711
|
if (command === "/permission" && parts.length === 1)
|
|
3656
3712
|
return { kind: "permission.status" };
|
|
3657
3713
|
if (command === "/session" && parts.length === 1)
|
|
@@ -3678,6 +3734,9 @@ function parseCommand(input) {
|
|
|
3678
3734
|
if (command === "/use" && parts[1]) {
|
|
3679
3735
|
return { kind: "session.use", alias: parts[1] };
|
|
3680
3736
|
}
|
|
3737
|
+
if (command === "/mode" && parts[1]) {
|
|
3738
|
+
return { kind: "mode.set", modeId: parts[1] };
|
|
3739
|
+
}
|
|
3681
3740
|
if (command === "/agent" && parts[1] === "add" && parts[2]) {
|
|
3682
3741
|
return { kind: "agent.add", template: parts[2] };
|
|
3683
3742
|
}
|
|
@@ -3830,6 +3889,7 @@ var init_parse_command = __esm(() => {
|
|
|
3830
3889
|
"/status",
|
|
3831
3890
|
"/cancel",
|
|
3832
3891
|
"/clear",
|
|
3892
|
+
"/mode",
|
|
3833
3893
|
"/permission",
|
|
3834
3894
|
"/session",
|
|
3835
3895
|
"/workspace",
|
|
@@ -3841,19 +3901,21 @@ var init_parse_command = __esm(() => {
|
|
|
3841
3901
|
// src/commands/command-router.ts
|
|
3842
3902
|
import { access } from "node:fs/promises";
|
|
3843
3903
|
import { basename as basename2, normalize } from "node:path";
|
|
3844
|
-
import { homedir } from "node:os";
|
|
3904
|
+
import { homedir as homedir2 } from "node:os";
|
|
3845
3905
|
|
|
3846
3906
|
class CommandRouter {
|
|
3847
3907
|
sessions;
|
|
3848
3908
|
transport;
|
|
3849
3909
|
config;
|
|
3850
3910
|
configStore;
|
|
3911
|
+
resolveSessionAgentCommand;
|
|
3851
3912
|
logger;
|
|
3852
|
-
constructor(sessions, transport, config, configStore, logger2) {
|
|
3913
|
+
constructor(sessions, transport, config, configStore, logger2, resolveSessionAgentCommand = resolveSessionAgentCommandFromIndex) {
|
|
3853
3914
|
this.sessions = sessions;
|
|
3854
3915
|
this.transport = transport;
|
|
3855
3916
|
this.config = config;
|
|
3856
3917
|
this.configStore = configStore;
|
|
3918
|
+
this.resolveSessionAgentCommand = resolveSessionAgentCommand;
|
|
3857
3919
|
this.logger = logger2 ?? createNoopAppLogger();
|
|
3858
3920
|
}
|
|
3859
3921
|
async handle(chatKey, input, reply) {
|
|
@@ -3976,6 +4038,7 @@ class CommandRouter {
|
|
|
3976
4038
|
return this.renderSessionCreationError(session, error);
|
|
3977
4039
|
}
|
|
3978
4040
|
await this.sessions.attachSession(command.alias, command.agent, command.workspace, session.transportSession);
|
|
4041
|
+
await this.refreshSessionTransportAgentCommand(command.alias);
|
|
3979
4042
|
await this.sessions.useSession(chatKey, command.alias);
|
|
3980
4043
|
await this.logger.info("session.created", "created and selected logical session", {
|
|
3981
4044
|
alias: command.alias,
|
|
@@ -4001,6 +4064,7 @@ class CommandRouter {
|
|
|
4001
4064
|
};
|
|
4002
4065
|
}
|
|
4003
4066
|
await this.sessions.attachSession(command.alias, command.agent, command.workspace, command.transportSession);
|
|
4067
|
+
await this.refreshSessionTransportAgentCommand(command.alias);
|
|
4004
4068
|
await this.sessions.useSession(chatKey, command.alias);
|
|
4005
4069
|
await this.logger.info("session.attached", "attached existing transport session", {
|
|
4006
4070
|
alias: command.alias,
|
|
@@ -4017,6 +4081,29 @@ class CommandRouter {
|
|
|
4017
4081
|
chatKey
|
|
4018
4082
|
});
|
|
4019
4083
|
return { text: `已切换到会话「${command.alias}」` };
|
|
4084
|
+
case "mode.show": {
|
|
4085
|
+
const session = await this.sessions.getCurrentSession(chatKey);
|
|
4086
|
+
if (!session) {
|
|
4087
|
+
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
4088
|
+
}
|
|
4089
|
+
return {
|
|
4090
|
+
text: [
|
|
4091
|
+
"当前 mode:",
|
|
4092
|
+
`- 会话:${session.alias}`,
|
|
4093
|
+
`- mode:${session.modeId ?? "未设置"}`
|
|
4094
|
+
].join(`
|
|
4095
|
+
`)
|
|
4096
|
+
};
|
|
4097
|
+
}
|
|
4098
|
+
case "mode.set": {
|
|
4099
|
+
const session = await this.sessions.getCurrentSession(chatKey);
|
|
4100
|
+
if (!session) {
|
|
4101
|
+
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
4102
|
+
}
|
|
4103
|
+
await this.setModeTransportSession(session, command.modeId);
|
|
4104
|
+
await this.sessions.setCurrentSessionMode(chatKey, command.modeId);
|
|
4105
|
+
return { text: `已设置当前会话 mode:${command.modeId}` };
|
|
4106
|
+
}
|
|
4020
4107
|
case "status": {
|
|
4021
4108
|
const session = await this.sessions.getCurrentSession(chatKey);
|
|
4022
4109
|
if (!session) {
|
|
@@ -4055,6 +4142,11 @@ class CommandRouter {
|
|
|
4055
4142
|
const result = await this.promptTransportSession(session, command.text, reply);
|
|
4056
4143
|
return { text: result.text };
|
|
4057
4144
|
} catch (error) {
|
|
4145
|
+
const recovered = await this.tryRecoverMissingSession(session, error);
|
|
4146
|
+
if (recovered) {
|
|
4147
|
+
const result = await this.promptTransportSession(recovered, command.text, reply);
|
|
4148
|
+
return { text: result.text };
|
|
4149
|
+
}
|
|
4058
4150
|
return this.renderTransportError(session, error);
|
|
4059
4151
|
}
|
|
4060
4152
|
}
|
|
@@ -4107,6 +4199,7 @@ class CommandRouter {
|
|
|
4107
4199
|
return this.renderShortcutSessionCreationError(workspace, alias);
|
|
4108
4200
|
}
|
|
4109
4201
|
await this.sessions.attachSession(alias, agent, workspace.name, session.transportSession);
|
|
4202
|
+
await this.refreshSessionTransportAgentCommand(alias);
|
|
4110
4203
|
await this.sessions.useSession(chatKey, alias);
|
|
4111
4204
|
await this.logger.info("session.shortcut.created", "created new logical session from shortcut", {
|
|
4112
4205
|
alias,
|
|
@@ -4274,6 +4367,7 @@ class CommandRouter {
|
|
|
4274
4367
|
return this.renderTransportError(resetSession, error);
|
|
4275
4368
|
}
|
|
4276
4369
|
await this.sessions.attachSession(resetSession.alias, resetSession.agent, resetSession.workspace, resetSession.transportSession);
|
|
4370
|
+
await this.refreshSessionTransportAgentCommand(resetSession.alias);
|
|
4277
4371
|
await this.sessions.useSession(chatKey, resetSession.alias);
|
|
4278
4372
|
await this.logger.info("session.reset", "reset current logical session", {
|
|
4279
4373
|
alias: resetSession.alias,
|
|
@@ -4293,9 +4387,35 @@ class CommandRouter {
|
|
|
4293
4387
|
async promptTransportSession(session, text, reply) {
|
|
4294
4388
|
return await this.measureTransportCall("prompt", session, () => this.transport.prompt(session, text, reply));
|
|
4295
4389
|
}
|
|
4390
|
+
async setModeTransportSession(session, modeId) {
|
|
4391
|
+
return await this.measureTransportCall("set_mode", session, () => this.transport.setMode(session, modeId));
|
|
4392
|
+
}
|
|
4296
4393
|
async cancelTransportSession(session) {
|
|
4297
4394
|
return await this.measureTransportCall("cancel", session, () => this.transport.cancel(session));
|
|
4298
4395
|
}
|
|
4396
|
+
async refreshSessionTransportAgentCommand(alias) {
|
|
4397
|
+
const session = await this.sessions.getSession(alias);
|
|
4398
|
+
if (!session) {
|
|
4399
|
+
return;
|
|
4400
|
+
}
|
|
4401
|
+
const transportAgentCommand = await this.resolveSessionAgentCommand(session);
|
|
4402
|
+
if (!transportAgentCommand) {
|
|
4403
|
+
return;
|
|
4404
|
+
}
|
|
4405
|
+
await this.sessions.setSessionTransportAgentCommand(alias, transportAgentCommand);
|
|
4406
|
+
}
|
|
4407
|
+
async tryRecoverMissingSession(session, error) {
|
|
4408
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4409
|
+
if (!message.includes("No acpx session found")) {
|
|
4410
|
+
return null;
|
|
4411
|
+
}
|
|
4412
|
+
const transportAgentCommand = await this.resolveSessionAgentCommand(session);
|
|
4413
|
+
if (!transportAgentCommand || transportAgentCommand === session.agentCommand) {
|
|
4414
|
+
return null;
|
|
4415
|
+
}
|
|
4416
|
+
await this.sessions.setSessionTransportAgentCommand(session.alias, transportAgentCommand);
|
|
4417
|
+
return await this.sessions.getSession(session.alias);
|
|
4418
|
+
}
|
|
4299
4419
|
async measureTransportCall(operation, session, callback) {
|
|
4300
4420
|
const startedAt = Date.now();
|
|
4301
4421
|
try {
|
|
@@ -4342,7 +4462,7 @@ async function pathExists(path11) {
|
|
|
4342
4462
|
}
|
|
4343
4463
|
}
|
|
4344
4464
|
function normalizePathForWorkspace(path11) {
|
|
4345
|
-
const expanded = path11.startsWith("~") ?
|
|
4465
|
+
const expanded = path11.startsWith("~") ? homedir2() + path11.slice(1) : path11;
|
|
4346
4466
|
return normalize(expanded);
|
|
4347
4467
|
}
|
|
4348
4468
|
function sameWorkspacePath(left, right) {
|
|
@@ -4415,6 +4535,7 @@ function isPartialPromptOutputError(message) {
|
|
|
4415
4535
|
var init_command_router = __esm(() => {
|
|
4416
4536
|
init_agent_templates();
|
|
4417
4537
|
init_app_logger();
|
|
4538
|
+
init_acpx_session_index();
|
|
4418
4539
|
init_prompt_output();
|
|
4419
4540
|
init_parse_command();
|
|
4420
4541
|
});
|
|
@@ -4435,12 +4556,12 @@ function isLegacyCodexCommand(command) {
|
|
|
4435
4556
|
}
|
|
4436
4557
|
|
|
4437
4558
|
// src/config/load-config.ts
|
|
4438
|
-
import { readFile as
|
|
4559
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
4439
4560
|
function isRecord(value) {
|
|
4440
4561
|
return typeof value === "object" && value !== null;
|
|
4441
4562
|
}
|
|
4442
4563
|
async function loadConfig(path11, options = {}) {
|
|
4443
|
-
const raw = JSON.parse(await
|
|
4564
|
+
const raw = JSON.parse(await readFile4(path11, "utf8"));
|
|
4444
4565
|
return parseConfig(raw, options);
|
|
4445
4566
|
}
|
|
4446
4567
|
function parseConfig(raw, options = {}) {
|
|
@@ -4608,7 +4729,7 @@ var init_config_store = __esm(() => {
|
|
|
4608
4729
|
});
|
|
4609
4730
|
|
|
4610
4731
|
// src/config/ensure-config.ts
|
|
4611
|
-
import { readFile as
|
|
4732
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
4612
4733
|
async function ensureConfigExists(path11) {
|
|
4613
4734
|
try {
|
|
4614
4735
|
await loadConfig(path11);
|
|
@@ -4622,7 +4743,7 @@ async function ensureConfigExists(path11) {
|
|
|
4622
4743
|
}
|
|
4623
4744
|
async function loadDefaultConfigTemplate() {
|
|
4624
4745
|
const templatePath = new URL("../../config.example.json", import.meta.url);
|
|
4625
|
-
const template = JSON.parse(await
|
|
4746
|
+
const template = JSON.parse(await readFile5(templatePath, "utf8"));
|
|
4626
4747
|
return {
|
|
4627
4748
|
...template,
|
|
4628
4749
|
agents: Object.fromEntries(Object.entries(template.agents).map(([name, agent]) => [
|
|
@@ -4725,12 +4846,20 @@ class SessionService {
|
|
|
4725
4846
|
agent,
|
|
4726
4847
|
workspace,
|
|
4727
4848
|
transport_session: transportSession,
|
|
4849
|
+
transport_agent_command: this.state.sessions[alias]?.transport_agent_command,
|
|
4728
4850
|
created_at: this.state.sessions[alias]?.created_at ?? new Date().toISOString(),
|
|
4729
4851
|
last_used_at: new Date().toISOString()
|
|
4730
4852
|
});
|
|
4731
4853
|
}
|
|
4732
|
-
async attachSession(alias, agent, workspace, transportSession) {
|
|
4733
|
-
return await this.createLogicalSession(alias, agent, workspace, transportSession);
|
|
4854
|
+
async attachSession(alias, agent, workspace, transportSession, transportAgentCommand) {
|
|
4855
|
+
return await this.createLogicalSession(alias, agent, workspace, transportSession, transportAgentCommand);
|
|
4856
|
+
}
|
|
4857
|
+
async getSession(alias) {
|
|
4858
|
+
const session = this.state.sessions[alias];
|
|
4859
|
+
if (!session) {
|
|
4860
|
+
return null;
|
|
4861
|
+
}
|
|
4862
|
+
return this.toResolvedSession(session);
|
|
4734
4863
|
}
|
|
4735
4864
|
async useSession(chatKey, alias) {
|
|
4736
4865
|
const session = this.state.sessions[alias];
|
|
@@ -4741,6 +4870,24 @@ class SessionService {
|
|
|
4741
4870
|
this.state.chat_contexts[chatKey] = { current_session: alias };
|
|
4742
4871
|
await this.persist();
|
|
4743
4872
|
}
|
|
4873
|
+
async setCurrentSessionMode(chatKey, modeId) {
|
|
4874
|
+
const currentAlias = this.state.chat_contexts[chatKey]?.current_session;
|
|
4875
|
+
if (!currentAlias) {
|
|
4876
|
+
throw new Error("no current session selected");
|
|
4877
|
+
}
|
|
4878
|
+
const session = this.state.sessions[currentAlias];
|
|
4879
|
+
if (!session) {
|
|
4880
|
+
throw new Error("no current session selected");
|
|
4881
|
+
}
|
|
4882
|
+
const normalizedModeId = modeId?.trim();
|
|
4883
|
+
if (normalizedModeId) {
|
|
4884
|
+
session.mode_id = normalizedModeId;
|
|
4885
|
+
} else {
|
|
4886
|
+
delete session.mode_id;
|
|
4887
|
+
}
|
|
4888
|
+
session.last_used_at = new Date().toISOString();
|
|
4889
|
+
await this.persist();
|
|
4890
|
+
}
|
|
4744
4891
|
async getCurrentSession(chatKey) {
|
|
4745
4892
|
const currentAlias = this.state.chat_contexts[chatKey]?.current_session;
|
|
4746
4893
|
if (!currentAlias) {
|
|
@@ -4768,24 +4915,42 @@ class SessionService {
|
|
|
4768
4915
|
return {
|
|
4769
4916
|
alias: session.alias,
|
|
4770
4917
|
agent: session.agent,
|
|
4771
|
-
agentCommand: resolveAgentCommand(agentConfig.driver, agentConfig.command),
|
|
4918
|
+
agentCommand: session.transport_agent_command ?? resolveAgentCommand(agentConfig.driver, agentConfig.command),
|
|
4772
4919
|
workspace: session.workspace,
|
|
4773
4920
|
transportSession: session.transport_session,
|
|
4921
|
+
modeId: session.mode_id,
|
|
4774
4922
|
cwd: this.config.workspaces[session.workspace].cwd
|
|
4775
4923
|
};
|
|
4776
4924
|
}
|
|
4925
|
+
async setSessionTransportAgentCommand(alias, transportAgentCommand) {
|
|
4926
|
+
const session = this.state.sessions[alias];
|
|
4927
|
+
if (!session) {
|
|
4928
|
+
throw new Error(`session "${alias}" does not exist`);
|
|
4929
|
+
}
|
|
4930
|
+
const normalized = transportAgentCommand?.trim();
|
|
4931
|
+
if (normalized) {
|
|
4932
|
+
session.transport_agent_command = normalized;
|
|
4933
|
+
} else {
|
|
4934
|
+
delete session.transport_agent_command;
|
|
4935
|
+
}
|
|
4936
|
+
session.last_used_at = new Date().toISOString();
|
|
4937
|
+
await this.persist();
|
|
4938
|
+
}
|
|
4777
4939
|
async persist() {
|
|
4778
4940
|
await this.stateStore.save(this.state);
|
|
4779
4941
|
}
|
|
4780
|
-
async createLogicalSession(alias, agent, workspace, transportSession) {
|
|
4942
|
+
async createLogicalSession(alias, agent, workspace, transportSession, transportAgentCommand) {
|
|
4781
4943
|
this.validateSession(alias, agent, workspace);
|
|
4782
4944
|
const existingSession = this.state.sessions[alias];
|
|
4783
4945
|
const now = new Date().toISOString();
|
|
4946
|
+
const normalizedTransportAgentCommand = transportAgentCommand?.trim();
|
|
4784
4947
|
const session = {
|
|
4785
4948
|
alias,
|
|
4786
4949
|
agent,
|
|
4787
4950
|
workspace,
|
|
4788
4951
|
transport_session: transportSession,
|
|
4952
|
+
...normalizedTransportAgentCommand ? { transport_agent_command: normalizedTransportAgentCommand } : existingSession?.transport_agent_command ? { transport_agent_command: existingSession.transport_agent_command } : {},
|
|
4953
|
+
mode_id: existingSession?.mode_id,
|
|
4789
4954
|
created_at: existingSession?.created_at ?? now,
|
|
4790
4955
|
last_used_at: now
|
|
4791
4956
|
};
|
|
@@ -4813,7 +4978,7 @@ function createEmptyState() {
|
|
|
4813
4978
|
}
|
|
4814
4979
|
|
|
4815
4980
|
// src/state/state-store.ts
|
|
4816
|
-
import { mkdir as mkdir7, readFile as
|
|
4981
|
+
import { mkdir as mkdir7, readFile as readFile6, writeFile as writeFile5 } from "node:fs/promises";
|
|
4817
4982
|
import { dirname as dirname6 } from "node:path";
|
|
4818
4983
|
|
|
4819
4984
|
class StateStore {
|
|
@@ -4823,7 +4988,7 @@ class StateStore {
|
|
|
4823
4988
|
}
|
|
4824
4989
|
async load() {
|
|
4825
4990
|
try {
|
|
4826
|
-
const content = await
|
|
4991
|
+
const content = await readFile6(this.path, "utf8");
|
|
4827
4992
|
if (content.trim() === "") {
|
|
4828
4993
|
return createEmptyState();
|
|
4829
4994
|
}
|
|
@@ -4918,9 +5083,9 @@ class AcpxBridgeClient {
|
|
|
4918
5083
|
request(method, params) {
|
|
4919
5084
|
const id = String(this.nextId);
|
|
4920
5085
|
this.nextId += 1;
|
|
4921
|
-
return awaitable((
|
|
5086
|
+
return awaitable((resolve2, reject) => {
|
|
4922
5087
|
this.pending.set(id, {
|
|
4923
|
-
resolve: (value) =>
|
|
5088
|
+
resolve: (value) => resolve2(value),
|
|
4924
5089
|
reject
|
|
4925
5090
|
});
|
|
4926
5091
|
this.writeLine(encodeBridgeRequest({
|
|
@@ -5019,8 +5184,8 @@ async function spawnAcpxBridgeClient(options = {}) {
|
|
|
5019
5184
|
return client;
|
|
5020
5185
|
}
|
|
5021
5186
|
function awaitable(executor) {
|
|
5022
|
-
return new Promise((
|
|
5023
|
-
executor(
|
|
5187
|
+
return new Promise((resolve2, reject) => {
|
|
5188
|
+
executor(resolve2, reject);
|
|
5024
5189
|
});
|
|
5025
5190
|
}
|
|
5026
5191
|
var init_acpx_bridge_client = __esm(() => {
|
|
@@ -5042,6 +5207,12 @@ class AcpxBridgeTransport {
|
|
|
5042
5207
|
text
|
|
5043
5208
|
});
|
|
5044
5209
|
}
|
|
5210
|
+
async setMode(session, modeId) {
|
|
5211
|
+
await this.client.request("setMode", {
|
|
5212
|
+
...this.toParams(session),
|
|
5213
|
+
modeId
|
|
5214
|
+
});
|
|
5215
|
+
}
|
|
5045
5216
|
async cancel(session) {
|
|
5046
5217
|
return await this.client.request("cancel", this.toParams(session));
|
|
5047
5218
|
}
|
|
@@ -5167,7 +5338,7 @@ import { createRequire as createRequire3 } from "node:module";
|
|
|
5167
5338
|
import { spawn as spawn3 } from "node:child_process";
|
|
5168
5339
|
import { spawn as spawnPty } from "node-pty";
|
|
5169
5340
|
async function defaultRunner(command, args, options) {
|
|
5170
|
-
return await new Promise((
|
|
5341
|
+
return await new Promise((resolve2, reject) => {
|
|
5171
5342
|
const spawnSpec = resolveSpawnCommand(command, args);
|
|
5172
5343
|
const child = spawn3(spawnSpec.command, spawnSpec.args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
5173
5344
|
let stdout = "";
|
|
@@ -5190,14 +5361,14 @@ async function defaultRunner(command, args, options) {
|
|
|
5190
5361
|
child.on("close", (code) => {
|
|
5191
5362
|
if (timeoutId)
|
|
5192
5363
|
clearTimeout(timeoutId);
|
|
5193
|
-
|
|
5364
|
+
resolve2({ code: code ?? 1, stdout, stderr });
|
|
5194
5365
|
});
|
|
5195
5366
|
});
|
|
5196
5367
|
}
|
|
5197
5368
|
async function defaultPtyRunner(command, args, options) {
|
|
5198
5369
|
const helperPath = resolveNodePtyHelperPath(require3.resolve("node-pty/package.json"), process.platform, process.arch);
|
|
5199
5370
|
await ensureNodePtyHelperExecutable(helperPath);
|
|
5200
|
-
return await new Promise((
|
|
5371
|
+
return await new Promise((resolve2, reject) => {
|
|
5201
5372
|
const spawnSpec = resolveSpawnCommand(command, args);
|
|
5202
5373
|
const child = spawnPty(spawnSpec.command, spawnSpec.args, {
|
|
5203
5374
|
name: "xterm-color",
|
|
@@ -5217,7 +5388,7 @@ async function defaultPtyRunner(command, args, options) {
|
|
|
5217
5388
|
child.onExit(({ exitCode }) => {
|
|
5218
5389
|
if (timeoutId)
|
|
5219
5390
|
clearTimeout(timeoutId);
|
|
5220
|
-
|
|
5391
|
+
resolve2({ code: exitCode, stdout: output, stderr: "" });
|
|
5221
5392
|
});
|
|
5222
5393
|
});
|
|
5223
5394
|
}
|
|
@@ -5258,6 +5429,14 @@ class AcpxCliTransport {
|
|
|
5258
5429
|
const result = await this.runCommand(this.command, args);
|
|
5259
5430
|
return { text: getPromptText(result) };
|
|
5260
5431
|
}
|
|
5432
|
+
async setMode(session, modeId) {
|
|
5433
|
+
await this.run(this.buildArgs(session, [
|
|
5434
|
+
"set-mode",
|
|
5435
|
+
"-s",
|
|
5436
|
+
session.transportSession,
|
|
5437
|
+
modeId
|
|
5438
|
+
]));
|
|
5439
|
+
}
|
|
5261
5440
|
async cancel(session) {
|
|
5262
5441
|
const output = await this.run(this.buildArgs(session, [
|
|
5263
5442
|
"cancel",
|
|
@@ -5315,7 +5494,7 @@ class AcpxCliTransport {
|
|
|
5315
5494
|
]);
|
|
5316
5495
|
}
|
|
5317
5496
|
async runStreamingPrompt(command, args, reply, maxSegmentWaitMs = 30000) {
|
|
5318
|
-
return await new Promise((
|
|
5497
|
+
return await new Promise((resolve2, reject) => {
|
|
5319
5498
|
const spawnSpec = resolveSpawnCommand(command, args);
|
|
5320
5499
|
const child = spawn3(spawnSpec.command, spawnSpec.args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
5321
5500
|
let stdout = "";
|
|
@@ -5357,7 +5536,7 @@ class AcpxCliTransport {
|
|
|
5357
5536
|
if (remaining.length > 0) {
|
|
5358
5537
|
reply(remaining).catch(() => {});
|
|
5359
5538
|
}
|
|
5360
|
-
|
|
5539
|
+
resolve2({ code: code ?? 1, stdout, stderr });
|
|
5361
5540
|
});
|
|
5362
5541
|
});
|
|
5363
5542
|
}
|
|
@@ -5428,7 +5607,7 @@ __export(exports_main, {
|
|
|
5428
5607
|
main: () => main2,
|
|
5429
5608
|
buildApp: () => buildApp
|
|
5430
5609
|
});
|
|
5431
|
-
import { homedir as
|
|
5610
|
+
import { homedir as homedir3 } from "node:os";
|
|
5432
5611
|
import { dirname as dirname8, join as join4 } from "node:path";
|
|
5433
5612
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
5434
5613
|
async function buildApp(paths, deps = {}) {
|
|
@@ -5491,7 +5670,7 @@ async function main2() {
|
|
|
5491
5670
|
}
|
|
5492
5671
|
}
|
|
5493
5672
|
function resolveRuntimePaths() {
|
|
5494
|
-
const home = process.env.HOME ??
|
|
5673
|
+
const home = process.env.HOME ?? homedir3();
|
|
5495
5674
|
if (!home) {
|
|
5496
5675
|
throw new Error("Unable to resolve the current user home directory");
|
|
5497
5676
|
}
|
|
@@ -5528,7 +5707,7 @@ var init_main = __esm(async () => {
|
|
|
5528
5707
|
});
|
|
5529
5708
|
|
|
5530
5709
|
// src/cli.ts
|
|
5531
|
-
import { homedir as
|
|
5710
|
+
import { homedir as homedir4 } from "node:os";
|
|
5532
5711
|
import { sep } from "node:path";
|
|
5533
5712
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
5534
5713
|
|
|
@@ -5924,7 +6103,15 @@ class DaemonRuntime {
|
|
|
5924
6103
|
}
|
|
5925
6104
|
|
|
5926
6105
|
// src/cli.ts
|
|
5927
|
-
var HELP_LINES = [
|
|
6106
|
+
var HELP_LINES = [
|
|
6107
|
+
"用法:",
|
|
6108
|
+
"weacpx login - 微信登录",
|
|
6109
|
+
"weacpx logout - 退出登录",
|
|
6110
|
+
"weacpx run - 前台运行",
|
|
6111
|
+
"weacpx start - 后台启动",
|
|
6112
|
+
"weacpx status - 查看状态",
|
|
6113
|
+
"weacpx stop - 停止服务"
|
|
6114
|
+
];
|
|
5928
6115
|
async function runCli(args, deps = {}) {
|
|
5929
6116
|
const command = args[0];
|
|
5930
6117
|
const print = deps.print ?? ((line) => console.log(line));
|
|
@@ -6018,7 +6205,7 @@ function createDefaultController() {
|
|
|
6018
6205
|
});
|
|
6019
6206
|
}
|
|
6020
6207
|
function requireHome() {
|
|
6021
|
-
const home = process.env.HOME ??
|
|
6208
|
+
const home = process.env.HOME ?? homedir4();
|
|
6022
6209
|
if (!home) {
|
|
6023
6210
|
throw new Error("Unable to resolve the current user home directory");
|
|
6024
6211
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "weacpx",
|
|
3
3
|
"description": "用微信远程控制 `acpx` 会话的控制台, 底层基于 `weixin-agent-sdk` 与 `acpx`",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.6",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
7
7
|
"doc": "docs",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"test:smoke": "node ./scripts/run-tests.mjs tests/smoke"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"acpx": "^0.
|
|
49
|
+
"acpx": "^0.4.0",
|
|
50
50
|
"node-pty": "^1.1.0",
|
|
51
51
|
"qrcode-terminal": "^0.12.0"
|
|
52
52
|
},
|
|
@@ -55,5 +55,10 @@
|
|
|
55
55
|
},
|
|
56
56
|
"engines": {
|
|
57
57
|
"node": ">=22"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/bun": "^1.3.11",
|
|
61
|
+
"bun-types": "^1.3.11",
|
|
62
|
+
"typescript": "^6.0.2"
|
|
58
63
|
}
|
|
59
64
|
}
|