weacpx 0.5.2 → 0.6.1

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.
Files changed (51) hide show
  1. package/README.md +15 -3
  2. package/dist/bridge/bridge-main.js +160 -4
  3. package/dist/channels/channel-scope.d.ts +8 -0
  4. package/dist/channels/types.d.ts +11 -0
  5. package/dist/channels/weixin-channel.d.ts +1 -0
  6. package/dist/cli.js +1115 -119
  7. package/dist/commands/command-hints.d.ts +11 -0
  8. package/dist/commands/command-list.d.ts +3 -0
  9. package/dist/commands/config-clone.d.ts +2 -0
  10. package/dist/commands/handlers/agent-handler.d.ts +6 -0
  11. package/dist/commands/handlers/config-handler.d.ts +5 -0
  12. package/dist/commands/handlers/later-handler.d.ts +21 -0
  13. package/dist/commands/handlers/orchestration-handler.d.ts +16 -0
  14. package/dist/commands/handlers/permission-handler.d.ts +9 -0
  15. package/dist/commands/handlers/session-handler.d.ts +37 -0
  16. package/dist/commands/handlers/workspace-handler.d.ts +8 -0
  17. package/dist/commands/help/help-registry.d.ts +4 -0
  18. package/dist/commands/help/help-types.d.ts +12 -0
  19. package/dist/commands/parse-command.d.ts +175 -0
  20. package/dist/commands/router-types.d.ts +144 -0
  21. package/dist/commands/workspace-name.d.ts +4 -0
  22. package/dist/commands/workspace-path.d.ts +4 -0
  23. package/dist/config/agent-templates.d.ts +4 -0
  24. package/dist/config/config-store.d.ts +13 -0
  25. package/dist/config/load-config.d.ts +10 -0
  26. package/dist/config/resolve-agent-command.d.ts +2 -0
  27. package/dist/formatting/render-text.d.ts +23 -0
  28. package/dist/orchestration/async-mutex.d.ts +4 -0
  29. package/dist/orchestration/build-coordinator-prompt.d.ts +66 -0
  30. package/dist/orchestration/orchestration-service.d.ts +471 -0
  31. package/dist/orchestration/progress-line-parser.d.ts +19 -0
  32. package/dist/orchestration/render-delegate-group-result.d.ts +6 -0
  33. package/dist/orchestration/render-delegate-question-package.d.ts +21 -0
  34. package/dist/orchestration/render-delegate-result.d.ts +2 -0
  35. package/dist/orchestration/task-watch-timeouts.d.ts +5 -0
  36. package/dist/plugin-api.d.ts +1 -0
  37. package/dist/scheduled/parse-later-time.d.ts +11 -0
  38. package/dist/scheduled/scheduled-render.d.ts +7 -0
  39. package/dist/scheduled/scheduled-service.d.ts +41 -0
  40. package/dist/scheduled/scheduled-types.d.ts +29 -0
  41. package/dist/sessions/session-service.d.ts +83 -0
  42. package/dist/state/state-store.d.ts +8 -0
  43. package/dist/state/types.d.ts +44 -0
  44. package/dist/transport/tool-event-mode.d.ts +14 -0
  45. package/dist/transport/types.d.ts +129 -0
  46. package/dist/util/path.d.ts +4 -0
  47. package/dist/util/sanitize.d.ts +10 -0
  48. package/dist/util/text.d.ts +3 -0
  49. package/dist/version.d.ts +2 -0
  50. package/dist/weixin/auth/accounts.d.ts +0 -1
  51. package/package.json +1 -1
package/README.md CHANGED
@@ -25,7 +25,7 @@
25
25
 
26
26
  `weacpx` 适合轻量临时使用多 Agent 办公的用户。你可以用微信、飞书或元宝盯任务、发指令、看结果,并在同一个聊天里管理多个会话。
27
27
 
28
- > `weacpx` 的会话是跟本地隔离的,它目前还不能使用 CLI 已有的会话,你在 weacpx 也无法看到本地的 CLI 会话记录。
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": "D:\\Users\\you\\.nvmd\\versions\\22.19.0\\node.exe",
455
+ "command": "C:\\Program Files\\nodejs\\node.exe",
453
456
  "args": [
454
- "E:\\projects\\weacpx\\dist\\cli.js",
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
  默认文件位置:
@@ -826,6 +826,19 @@ async function defaultRunProcessCommand(command, args) {
826
826
  }
827
827
  var init_terminate_process_tree = () => {};
828
828
 
829
+ // src/util/text.ts
830
+ function truncateText(text, maxLength, ellipsis = "…") {
831
+ if (text.length <= maxLength)
832
+ return text;
833
+ return text.slice(0, maxLength - ellipsis.length) + ellipsis;
834
+ }
835
+ function escapeForDoubleQuotes(input) {
836
+ return input.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
837
+ }
838
+ function quoteIfNeeded(input) {
839
+ return `"${escapeForDoubleQuotes(input)}"`;
840
+ }
841
+
829
842
  // src/transport/acpx-queue-owner-launcher.ts
830
843
  import { createHash } from "node:crypto";
831
844
  import { spawn as spawn3 } from "node:child_process";
@@ -1013,7 +1026,7 @@ function resolveDefaultWeacpxCommand(env) {
1013
1026
  return "weacpx";
1014
1027
  }
1015
1028
  function quoteCommandPart(value) {
1016
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
1029
+ return quoteIfNeeded(value);
1017
1030
  }
1018
1031
  var init_acpx_queue_owner_launcher = __esm(() => {
1019
1032
  init_spawn_command();
@@ -1032,6 +1045,99 @@ function permissionModeToFlag(permissionMode) {
1032
1045
  }
1033
1046
  }
1034
1047
 
1048
+ // src/util/path.ts
1049
+ import path2 from "node:path";
1050
+ import { homedir as homedir3 } from "node:os";
1051
+ function normalizePath(input) {
1052
+ const expanded = expandHome(input);
1053
+ if (isWindowsLikePath(expanded)) {
1054
+ return path2.win32.normalize(expanded).replace(/\\/g, "/");
1055
+ }
1056
+ return path2.posix.normalize(expanded.replace(/\\/g, "/"));
1057
+ }
1058
+ function basenameForPath(input) {
1059
+ const normalized = normalizePath(input);
1060
+ if (ROOT_PATH_RE.test(normalized)) {
1061
+ return normalized;
1062
+ }
1063
+ const base = path2.posix.basename(normalized);
1064
+ return base || normalized;
1065
+ }
1066
+ function isSamePath(left, right) {
1067
+ const normalizedLeft = normalizePath(left);
1068
+ const normalizedRight = normalizePath(right);
1069
+ if (isWindowsLikePath(normalizedLeft) || isWindowsLikePath(normalizedRight)) {
1070
+ return normalizedLeft.toLowerCase() === normalizedRight.toLowerCase();
1071
+ }
1072
+ return normalizedLeft === normalizedRight;
1073
+ }
1074
+ function isWindowsLikePath(input) {
1075
+ return WINDOWS_DRIVE_PATH_RE.test(input) || WINDOWS_UNC_PATH_RE.test(input);
1076
+ }
1077
+ function expandHome(input) {
1078
+ return input.startsWith("~") ? homedir3() + input.slice(1) : input;
1079
+ }
1080
+ var WINDOWS_DRIVE_PATH_RE, WINDOWS_UNC_PATH_RE, ROOT_PATH_RE;
1081
+ var init_path = __esm(() => {
1082
+ WINDOWS_DRIVE_PATH_RE = /^[a-zA-Z]:[\\/]/;
1083
+ WINDOWS_UNC_PATH_RE = /^\\\\/;
1084
+ ROOT_PATH_RE = /^(\/|[a-zA-Z]:\/?)$/;
1085
+ });
1086
+
1087
+ // src/transport/agent-session-list.ts
1088
+ function isUnknownFilterCwdOption(output) {
1089
+ return /(?:unknown|unrecognized) option/i.test(output) && output.includes("--filter-cwd");
1090
+ }
1091
+ async function runAgentSessionList(options) {
1092
+ let result = await options.runList(true);
1093
+ let filterLocally = false;
1094
+ if (result.code !== 0 && options.filterCwd && isUnknownFilterCwdOption(result.stdout + result.stderr)) {
1095
+ result = await options.runList(false);
1096
+ filterLocally = true;
1097
+ }
1098
+ if (result.code !== 0) {
1099
+ if ((result.stdout + result.stderr).includes("sessionCapabilities.list")) {
1100
+ return;
1101
+ }
1102
+ throw new Error(options.formatError(result));
1103
+ }
1104
+ return parseAgentSessionListOutput(result.stdout, filterLocally ? options.filterCwd : undefined);
1105
+ }
1106
+ function parseAgentSessionListOutput(stdout, filterCwd) {
1107
+ let parsed;
1108
+ try {
1109
+ parsed = JSON.parse(stdout);
1110
+ } catch {
1111
+ throw new Error("failed to parse acpx sessions list output");
1112
+ }
1113
+ if (!isAgentSessionListResult(parsed)) {
1114
+ return;
1115
+ }
1116
+ return filterCwd ? filterAgentSessionListByCwd(parsed, filterCwd) : parsed;
1117
+ }
1118
+ function isAgentSessionListResult(value) {
1119
+ if (!value || typeof value !== "object" || Array.isArray(value))
1120
+ return false;
1121
+ const record = value;
1122
+ if (record.source !== "agent" || !Array.isArray(record.sessions))
1123
+ return false;
1124
+ return record.sessions.every((session) => {
1125
+ if (!session || typeof session !== "object" || Array.isArray(session))
1126
+ return false;
1127
+ const item = session;
1128
+ return typeof item.sessionId === "string";
1129
+ });
1130
+ }
1131
+ function filterAgentSessionListByCwd(result, cwd) {
1132
+ return {
1133
+ ...result,
1134
+ sessions: result.sessions.filter((session) => session.cwd && isSamePath(session.cwd, cwd))
1135
+ };
1136
+ }
1137
+ var init_agent_session_list = __esm(() => {
1138
+ init_path();
1139
+ });
1140
+
1035
1141
  // src/bridge/bridge-main.ts
1036
1142
  import { createInterface } from "node:readline";
1037
1143
 
@@ -1091,7 +1197,7 @@ init_prompt_output();
1091
1197
  init_prompt_media();
1092
1198
  init_streaming_prompt();
1093
1199
  import { copyFile, readdir } from "node:fs/promises";
1094
- import { homedir as homedir3 } from "node:os";
1200
+ import { homedir as homedir4 } from "node:os";
1095
1201
  import { dirname as dirname2, join as join3, win32 } from "node:path";
1096
1202
  import { spawn as spawn4 } from "node:child_process";
1097
1203
 
@@ -1111,6 +1217,7 @@ function parseMissingOptionalDep(text) {
1111
1217
  // src/bridge/bridge-runtime.ts
1112
1218
  init_discover_parent_package_paths();
1113
1219
  init_acpx_queue_owner_launcher();
1220
+ init_agent_session_list();
1114
1221
  class EnsureSessionFailedError extends Error {
1115
1222
  kind;
1116
1223
  data;
@@ -1149,6 +1256,36 @@ class BridgeRuntime {
1149
1256
  this.options.permissionPolicy = policy.permissionPolicy;
1150
1257
  return {};
1151
1258
  }
1259
+ async listAgentSessions(input) {
1260
+ return await runAgentSessionList({
1261
+ filterCwd: input.filterCwd,
1262
+ runList: async (includeFilterCwd) => {
1263
+ const spec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
1264
+ "sessions",
1265
+ "list",
1266
+ ...includeFilterCwd && input.filterCwd ? ["--filter-cwd", input.filterCwd] : [],
1267
+ ...input.cursor ? ["--cursor", input.cursor] : []
1268
+ ], { format: "json" }));
1269
+ return await this.run(spec.command, spec.args);
1270
+ },
1271
+ formatError: (result) => result.stderr || result.stdout || `sessions list failed with exit code ${result.code}`
1272
+ });
1273
+ }
1274
+ async resumeAgentSession(input) {
1275
+ const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
1276
+ "sessions",
1277
+ "new",
1278
+ "--name",
1279
+ input.name,
1280
+ "--resume-session",
1281
+ input.agentSessionId
1282
+ ], { format: "quiet" }));
1283
+ const result = await this.runSessionCreate(spawnSpec.command, spawnSpec.args, input.cwd);
1284
+ if (result.code !== 0) {
1285
+ throw new Error(result.stderr || result.stdout || "sessions resume failed");
1286
+ }
1287
+ return {};
1288
+ }
1152
1289
  async hasSession(input) {
1153
1290
  const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
1154
1291
  "sessions",
@@ -1369,7 +1506,7 @@ class BridgeRuntime {
1369
1506
  buildSessionArgs(input, tail, options = {}) {
1370
1507
  const prefix = [
1371
1508
  "--format",
1372
- "quiet",
1509
+ options.format ?? "quiet",
1373
1510
  "--cwd",
1374
1511
  input.cwd,
1375
1512
  ...this.buildPermissionArgs()
@@ -1535,7 +1672,7 @@ async function tryRepairAcpxSessionIndex(deps = {}) {
1535
1672
  if (platform !== "win32") {
1536
1673
  return false;
1537
1674
  }
1538
- const home = deps.home ?? process.env.HOME ?? process.env.USERPROFILE ?? homedir3();
1675
+ const home = deps.home ?? process.env.HOME ?? process.env.USERPROFILE ?? homedir4();
1539
1676
  if (!home) {
1540
1677
  return false;
1541
1678
  }
@@ -1582,6 +1719,8 @@ var BRIDGE_METHODS = new Set([
1582
1719
  "hasSession",
1583
1720
  "ensureSession",
1584
1721
  "tailSessionHistory",
1722
+ "listAgentSessions",
1723
+ "resumeAgentSession",
1585
1724
  "prompt",
1586
1725
  "setMode",
1587
1726
  "cancel",
@@ -1591,6 +1730,7 @@ var SESSION_SCOPED_METHODS = new Set([
1591
1730
  "hasSession",
1592
1731
  "ensureSession",
1593
1732
  "tailSessionHistory",
1733
+ "resumeAgentSession",
1594
1734
  "prompt",
1595
1735
  "setMode",
1596
1736
  "cancel",
@@ -1673,6 +1813,14 @@ class BridgeServer {
1673
1813
  name: requireString(params, "name"),
1674
1814
  lines: requirePositiveInt(params, "lines")
1675
1815
  });
1816
+ case "listAgentSessions":
1817
+ return await this.runtime.listAgentSessions({
1818
+ agent: requireString(params, "agent"),
1819
+ agentCommand: asOptionalString(params.agentCommand),
1820
+ cwd: requireString(params, "cwd"),
1821
+ cursor: asOptionalString(params.cursor),
1822
+ filterCwd: asOptionalString(params.filterCwd)
1823
+ });
1676
1824
  case "ensureSession":
1677
1825
  return await this.runtime.ensureSession({
1678
1826
  agent: requireString(params, "agent"),
@@ -1732,6 +1880,14 @@ class BridgeServer {
1732
1880
  }));
1733
1881
  }
1734
1882
  });
1883
+ case "resumeAgentSession":
1884
+ return await this.runtime.resumeAgentSession({
1885
+ agent: requireString(params, "agent"),
1886
+ agentCommand: asOptionalString(params.agentCommand),
1887
+ cwd: requireString(params, "cwd"),
1888
+ name: requireString(params, "name"),
1889
+ agentSessionId: requireString(params, "agentSessionId")
1890
+ });
1735
1891
  case "setMode":
1736
1892
  return await this.runtime.setMode({
1737
1893
  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;
@@ -1,4 +1,5 @@
1
1
  import type { Agent as ChatAgent } from "../weixin/agent/interface.js";
2
+ import type { CommandHint } from "../commands/command-hints.js";
2
3
  import type { OrchestrationTaskRecord } from "../orchestration/orchestration-types.js";
3
4
  import type { AppLogger } from "../logging/app-logger.js";
4
5
  import type { PendingFinalChunk } from "../weixin/messaging/quota-manager.js";
@@ -50,6 +51,10 @@ export interface ChannelStartInput {
50
51
  quota: OutboundQuota;
51
52
  logger: AppLogger;
52
53
  perfTracer?: PerfTracer;
54
+ /** weacpx 内置命令目录,供支持输入框命令提示的频道使用。 */
55
+ commandHints?: CommandHint[];
56
+ /** weacpx 核心版本字符串,供需要它的频道(如命令同步元数据)使用。 */
57
+ coreVersion?: string;
53
58
  }
54
59
  export interface OrchestrationDeliveryCallbacks {
55
60
  markTaskNoticeDelivered: (taskId: string, accountId: string) => Promise<void>;
@@ -83,6 +88,12 @@ export interface MessageChannelRuntime {
83
88
  notifyTaskProgress(task: OrchestrationTaskRecord, text: string): Promise<void>;
84
89
  sendCoordinatorMessage(input: CoordinatorMessageInput): Promise<void>;
85
90
  sendScheduledMessage?(input: ScheduledChannelMessageInput): Promise<void>;
91
+ /**
92
+ * Preferred render format for `/ssn` native session lists. weixin renders
93
+ * markdown tables poorly and declares "cards"; channels that omit this are
94
+ * treated as "table".
95
+ */
96
+ nativeSessionListFormat?: "cards" | "table";
86
97
  }
87
98
  export type ToolUseStatus = "running" | "success" | "error";
88
99
  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;