weacpx 0.5.1 → 0.6.0
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 +15 -3
- package/dist/bridge/bridge-main.js +122 -2
- package/dist/channels/channel-scope.d.ts +8 -0
- package/dist/channels/types.d.ts +6 -0
- package/dist/channels/weixin-channel.d.ts +1 -0
- package/dist/cli.js +882 -50
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
`weacpx` 适合轻量临时使用多 Agent 办公的用户。你可以用微信、飞书或元宝盯任务、发指令、看结果,并在同一个聊天里管理多个会话。
|
|
27
27
|
|
|
28
|
-
>
|
|
28
|
+
> 日常使用优先记 `/ss`:它负责创建或复用 weacpx 逻辑会话。如果你想接入本地 Codex 等 Agent 已有的原生会话,再用 `/ssn`;进阶说明见 [docs/native-sessions.md](./docs/native-sessions.md)。
|
|
29
29
|
|
|
30
30
|
## 5 分钟快速开始
|
|
31
31
|
|
|
@@ -305,6 +305,7 @@ opencode, qoder, qwen, trae
|
|
|
305
305
|
| `/sessions` / `/session` / `/ss` | 查看会话列表 |
|
|
306
306
|
| `/ss <agent> (-d <path> \| --ws <name>)` | 创建或复用当前最常用的会话 |
|
|
307
307
|
| `/ss new <agent> (-d <path> \| --ws <name>)` | 强制新建会话 |
|
|
308
|
+
| `/ssn <agent> (-d <path> \| --ws <name>)` | 接入本地已有的 Agent 原生会话,详见 [native sessions](./docs/native-sessions.md) |
|
|
308
309
|
| `/use <alias>` | 切换当前会话 |
|
|
309
310
|
| `/status` | 查看当前会话状态 |
|
|
310
311
|
| `/mode` / `/mode <id>` | 查看或设置底层 `acpx` mode |
|
|
@@ -325,6 +326,8 @@ opencode, qoder, qwen, trae
|
|
|
325
326
|
/cancel
|
|
326
327
|
```
|
|
327
328
|
|
|
329
|
+
如果要接入本地 Codex 等 Agent 已有的原生会话,用 `/ssn codex -d /absolute/path/to/repo`;完整语义见 [docs/native-sessions.md](./docs/native-sessions.md)。
|
|
330
|
+
|
|
328
331
|
### 定时任务(/later)
|
|
329
332
|
|
|
330
333
|
让 agent 在未来某个时间自动收到一条消息。**默认在一个为该任务新建的临时会话里执行**(沿用创建时当前会话的 agent 与工作区,对话历史全新,跑完即销毁);加 `--bind` 则发送到创建时绑定的当前会话。到点后把消息作为普通 prompt 投递,结果推回原聊天。
|
|
@@ -449,9 +452,9 @@ Windows 上如果 MCP host 不会帮你解析带参数的 `command`,把 `node.
|
|
|
449
452
|
```json
|
|
450
453
|
{
|
|
451
454
|
"type": "stdio",
|
|
452
|
-
"command": "
|
|
455
|
+
"command": "C:\\Program Files\\nodejs\\node.exe",
|
|
453
456
|
"args": [
|
|
454
|
-
"
|
|
457
|
+
"C:\\path\\to\\weacpx\\dist\\cli.js",
|
|
455
458
|
"mcp-stdio"
|
|
456
459
|
]
|
|
457
460
|
}
|
|
@@ -478,6 +481,15 @@ Windows 上如果 MCP host 不会帮你解析带参数的 `command`,把 `node.
|
|
|
478
481
|
/use frontend:codex
|
|
479
482
|
```
|
|
480
483
|
|
|
484
|
+
### 接入本地已有 Codex 原生会话
|
|
485
|
+
|
|
486
|
+
```text
|
|
487
|
+
/ssn codex -d /absolute/path/to/backend
|
|
488
|
+
/ssn 1
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
更多筛选、别名和故障排查见 [docs/native-sessions.md](./docs/native-sessions.md)。
|
|
492
|
+
|
|
481
493
|
## 配置与运行文件
|
|
482
494
|
|
|
483
495
|
默认文件位置:
|
|
@@ -835,7 +835,7 @@ import { join as join2 } from "node:path";
|
|
|
835
835
|
function buildWeacpxMcpServerSpec(input) {
|
|
836
836
|
const { command, args } = splitCommandLine(input.weacpxCommand);
|
|
837
837
|
return {
|
|
838
|
-
name: "weacpx
|
|
838
|
+
name: "weacpx",
|
|
839
839
|
type: "stdio",
|
|
840
840
|
command,
|
|
841
841
|
args: [
|
|
@@ -1032,6 +1032,76 @@ function permissionModeToFlag(permissionMode) {
|
|
|
1032
1032
|
}
|
|
1033
1033
|
}
|
|
1034
1034
|
|
|
1035
|
+
// src/transport/agent-session-list.ts
|
|
1036
|
+
import path2 from "node:path";
|
|
1037
|
+
function isUnknownFilterCwdOption(output) {
|
|
1038
|
+
return /(?:unknown|unrecognized) option/i.test(output) && output.includes("--filter-cwd");
|
|
1039
|
+
}
|
|
1040
|
+
async function runAgentSessionList(options) {
|
|
1041
|
+
let result = await options.runList(true);
|
|
1042
|
+
let filterLocally = false;
|
|
1043
|
+
if (result.code !== 0 && options.filterCwd && isUnknownFilterCwdOption(result.stdout + result.stderr)) {
|
|
1044
|
+
result = await options.runList(false);
|
|
1045
|
+
filterLocally = true;
|
|
1046
|
+
}
|
|
1047
|
+
if (result.code !== 0) {
|
|
1048
|
+
if ((result.stdout + result.stderr).includes("sessionCapabilities.list")) {
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
throw new Error(options.formatError(result));
|
|
1052
|
+
}
|
|
1053
|
+
return parseAgentSessionListOutput(result.stdout, filterLocally ? options.filterCwd : undefined);
|
|
1054
|
+
}
|
|
1055
|
+
function parseAgentSessionListOutput(stdout, filterCwd) {
|
|
1056
|
+
let parsed;
|
|
1057
|
+
try {
|
|
1058
|
+
parsed = JSON.parse(stdout);
|
|
1059
|
+
} catch {
|
|
1060
|
+
throw new Error("failed to parse acpx sessions list output");
|
|
1061
|
+
}
|
|
1062
|
+
if (!isAgentSessionListResult(parsed)) {
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
return filterCwd ? filterAgentSessionListByCwd(parsed, filterCwd) : parsed;
|
|
1066
|
+
}
|
|
1067
|
+
function isAgentSessionListResult(value) {
|
|
1068
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
1069
|
+
return false;
|
|
1070
|
+
const record = value;
|
|
1071
|
+
if (record.source !== "agent" || !Array.isArray(record.sessions))
|
|
1072
|
+
return false;
|
|
1073
|
+
return record.sessions.every((session) => {
|
|
1074
|
+
if (!session || typeof session !== "object" || Array.isArray(session))
|
|
1075
|
+
return false;
|
|
1076
|
+
const item = session;
|
|
1077
|
+
return typeof item.sessionId === "string";
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
function filterAgentSessionListByCwd(result, cwd) {
|
|
1081
|
+
return {
|
|
1082
|
+
...result,
|
|
1083
|
+
sessions: result.sessions.filter((session) => session.cwd && sameAgentSessionCwd(session.cwd, cwd))
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
function sameAgentSessionCwd(left, right) {
|
|
1087
|
+
const normalizedLeft = normalizeAgentSessionCwd(left);
|
|
1088
|
+
const normalizedRight = normalizeAgentSessionCwd(right);
|
|
1089
|
+
if (isWindowsLikePath(normalizedLeft) || isWindowsLikePath(normalizedRight)) {
|
|
1090
|
+
return normalizedLeft.toLowerCase() === normalizedRight.toLowerCase();
|
|
1091
|
+
}
|
|
1092
|
+
return normalizedLeft === normalizedRight;
|
|
1093
|
+
}
|
|
1094
|
+
function normalizeAgentSessionCwd(input) {
|
|
1095
|
+
if (isWindowsLikePath(input)) {
|
|
1096
|
+
return path2.win32.normalize(input).replace(/\\/g, "/");
|
|
1097
|
+
}
|
|
1098
|
+
return path2.posix.normalize(input.replace(/\\/g, "/"));
|
|
1099
|
+
}
|
|
1100
|
+
function isWindowsLikePath(input) {
|
|
1101
|
+
return /^[a-zA-Z]:[\\/]/.test(input) || input.startsWith("\\\\");
|
|
1102
|
+
}
|
|
1103
|
+
var init_agent_session_list = () => {};
|
|
1104
|
+
|
|
1035
1105
|
// src/bridge/bridge-main.ts
|
|
1036
1106
|
import { createInterface } from "node:readline";
|
|
1037
1107
|
|
|
@@ -1111,6 +1181,7 @@ function parseMissingOptionalDep(text) {
|
|
|
1111
1181
|
// src/bridge/bridge-runtime.ts
|
|
1112
1182
|
init_discover_parent_package_paths();
|
|
1113
1183
|
init_acpx_queue_owner_launcher();
|
|
1184
|
+
init_agent_session_list();
|
|
1114
1185
|
class EnsureSessionFailedError extends Error {
|
|
1115
1186
|
kind;
|
|
1116
1187
|
data;
|
|
@@ -1149,6 +1220,36 @@ class BridgeRuntime {
|
|
|
1149
1220
|
this.options.permissionPolicy = policy.permissionPolicy;
|
|
1150
1221
|
return {};
|
|
1151
1222
|
}
|
|
1223
|
+
async listAgentSessions(input) {
|
|
1224
|
+
return await runAgentSessionList({
|
|
1225
|
+
filterCwd: input.filterCwd,
|
|
1226
|
+
runList: async (includeFilterCwd) => {
|
|
1227
|
+
const spec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
|
|
1228
|
+
"sessions",
|
|
1229
|
+
"list",
|
|
1230
|
+
...includeFilterCwd && input.filterCwd ? ["--filter-cwd", input.filterCwd] : [],
|
|
1231
|
+
...input.cursor ? ["--cursor", input.cursor] : []
|
|
1232
|
+
], { format: "json" }));
|
|
1233
|
+
return await this.run(spec.command, spec.args);
|
|
1234
|
+
},
|
|
1235
|
+
formatError: (result) => result.stderr || result.stdout || `sessions list failed with exit code ${result.code}`
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
async resumeAgentSession(input) {
|
|
1239
|
+
const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
|
|
1240
|
+
"sessions",
|
|
1241
|
+
"new",
|
|
1242
|
+
"--name",
|
|
1243
|
+
input.name,
|
|
1244
|
+
"--resume-session",
|
|
1245
|
+
input.agentSessionId
|
|
1246
|
+
], { format: "quiet" }));
|
|
1247
|
+
const result = await this.runSessionCreate(spawnSpec.command, spawnSpec.args, input.cwd);
|
|
1248
|
+
if (result.code !== 0) {
|
|
1249
|
+
throw new Error(result.stderr || result.stdout || "sessions resume failed");
|
|
1250
|
+
}
|
|
1251
|
+
return {};
|
|
1252
|
+
}
|
|
1152
1253
|
async hasSession(input) {
|
|
1153
1254
|
const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
|
|
1154
1255
|
"sessions",
|
|
@@ -1369,7 +1470,7 @@ class BridgeRuntime {
|
|
|
1369
1470
|
buildSessionArgs(input, tail, options = {}) {
|
|
1370
1471
|
const prefix = [
|
|
1371
1472
|
"--format",
|
|
1372
|
-
"quiet",
|
|
1473
|
+
options.format ?? "quiet",
|
|
1373
1474
|
"--cwd",
|
|
1374
1475
|
input.cwd,
|
|
1375
1476
|
...this.buildPermissionArgs()
|
|
@@ -1582,6 +1683,8 @@ var BRIDGE_METHODS = new Set([
|
|
|
1582
1683
|
"hasSession",
|
|
1583
1684
|
"ensureSession",
|
|
1584
1685
|
"tailSessionHistory",
|
|
1686
|
+
"listAgentSessions",
|
|
1687
|
+
"resumeAgentSession",
|
|
1585
1688
|
"prompt",
|
|
1586
1689
|
"setMode",
|
|
1587
1690
|
"cancel",
|
|
@@ -1591,6 +1694,7 @@ var SESSION_SCOPED_METHODS = new Set([
|
|
|
1591
1694
|
"hasSession",
|
|
1592
1695
|
"ensureSession",
|
|
1593
1696
|
"tailSessionHistory",
|
|
1697
|
+
"resumeAgentSession",
|
|
1594
1698
|
"prompt",
|
|
1595
1699
|
"setMode",
|
|
1596
1700
|
"cancel",
|
|
@@ -1673,6 +1777,14 @@ class BridgeServer {
|
|
|
1673
1777
|
name: requireString(params, "name"),
|
|
1674
1778
|
lines: requirePositiveInt(params, "lines")
|
|
1675
1779
|
});
|
|
1780
|
+
case "listAgentSessions":
|
|
1781
|
+
return await this.runtime.listAgentSessions({
|
|
1782
|
+
agent: requireString(params, "agent"),
|
|
1783
|
+
agentCommand: asOptionalString(params.agentCommand),
|
|
1784
|
+
cwd: requireString(params, "cwd"),
|
|
1785
|
+
cursor: asOptionalString(params.cursor),
|
|
1786
|
+
filterCwd: asOptionalString(params.filterCwd)
|
|
1787
|
+
});
|
|
1676
1788
|
case "ensureSession":
|
|
1677
1789
|
return await this.runtime.ensureSession({
|
|
1678
1790
|
agent: requireString(params, "agent"),
|
|
@@ -1732,6 +1844,14 @@ class BridgeServer {
|
|
|
1732
1844
|
}));
|
|
1733
1845
|
}
|
|
1734
1846
|
});
|
|
1847
|
+
case "resumeAgentSession":
|
|
1848
|
+
return await this.runtime.resumeAgentSession({
|
|
1849
|
+
agent: requireString(params, "agent"),
|
|
1850
|
+
agentCommand: asOptionalString(params.agentCommand),
|
|
1851
|
+
cwd: requireString(params, "cwd"),
|
|
1852
|
+
name: requireString(params, "name"),
|
|
1853
|
+
agentSessionId: requireString(params, "agentSessionId")
|
|
1854
|
+
});
|
|
1735
1855
|
case "setMode":
|
|
1736
1856
|
return await this.runtime.setMode({
|
|
1737
1857
|
agent: requireString(params, "agent"),
|
|
@@ -6,4 +6,12 @@ export declare function toInternalSessionAlias(channelId: string, displayAlias:
|
|
|
6
6
|
export declare function toDisplaySessionAlias(internalAlias: string): string;
|
|
7
7
|
export declare function isSessionAliasVisibleInChannel(alias: string, channelId: string): boolean;
|
|
8
8
|
export declare function resolveSessionAliasForInput(channelId: string, displayAlias: string, existingAliases: Iterable<string>): string;
|
|
9
|
+
/**
|
|
10
|
+
* Internal alias for a display alias entered in `channelId`. The default
|
|
11
|
+
* channel (weixin) stays unprefixed for backwards compatibility; every other
|
|
12
|
+
* channel is namespaced as `channelId:alias`. Idempotent — an already-scoped
|
|
13
|
+
* alias is not double-prefixed. This is the single home for the rule that
|
|
14
|
+
* handlers must not re-implement inline.
|
|
15
|
+
*/
|
|
16
|
+
export declare function scopeDisplayAliasToInternal(channelId: string, displayAlias: string): string;
|
|
9
17
|
export declare function buildDefaultTransportSession(channelId: string, displayAlias: string): string;
|
package/dist/channels/types.d.ts
CHANGED
|
@@ -83,6 +83,12 @@ export interface MessageChannelRuntime {
|
|
|
83
83
|
notifyTaskProgress(task: OrchestrationTaskRecord, text: string): Promise<void>;
|
|
84
84
|
sendCoordinatorMessage(input: CoordinatorMessageInput): Promise<void>;
|
|
85
85
|
sendScheduledMessage?(input: ScheduledChannelMessageInput): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Preferred render format for `/ssn` native session lists. weixin renders
|
|
88
|
+
* markdown tables poorly and declares "cards"; channels that omit this are
|
|
89
|
+
* treated as "table".
|
|
90
|
+
*/
|
|
91
|
+
nativeSessionListFormat?: "cards" | "table";
|
|
86
92
|
}
|
|
87
93
|
export type ToolUseStatus = "running" | "success" | "error";
|
|
88
94
|
export type ToolUseKind = "read" | "search" | "execute" | "edit" | "think" | "other";
|
|
@@ -3,6 +3,7 @@ import type { RuntimeMediaStore } from "./media-store.js";
|
|
|
3
3
|
import type { OrchestrationTaskRecord } from "../orchestration/orchestration-types.js";
|
|
4
4
|
export declare class WeixinChannel implements MessageChannelRuntime {
|
|
5
5
|
readonly id = "weixin";
|
|
6
|
+
readonly nativeSessionListFormat: "cards" | "table";
|
|
6
7
|
private agent;
|
|
7
8
|
private quota;
|
|
8
9
|
private logger;
|