@ganglion/xacpx 0.9.1 → 0.9.3
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 +4 -2
- package/dist/bridge/bridge-main.js +39 -17
- package/dist/cli.js +415 -409
- package/dist/commands/command-hints.d.ts +1 -1
- package/dist/commands/command-list.d.ts +3 -3
- package/dist/commands/handlers/resolve-reply-mode.d.ts +13 -0
- package/dist/config/types.d.ts +1 -0
- package/dist/i18n/types.d.ts +6 -2
- package/dist/orchestration/coordinator-identity.d.ts +27 -0
- package/dist/orchestration/orchestration-service.d.ts +0 -23
- package/dist/plugin-api.d.ts +1 -1
- package/dist/plugin-api.js +27 -13
- package/dist/plugins/compatibility.d.ts +4 -1
- package/dist/plugins/types.d.ts +2 -2
- package/dist/state/types.d.ts +1 -1
- package/dist/transport/types.d.ts +9 -1
- package/dist/version.d.ts +2 -2
- package/dist/weixin/agent/interface.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ English · **[中文](./docs/zh/README_zh.md)**
|
|
|
13
13
|
|
|
14
14
|
`xacpx` is a tool that lets you control ACP agents such as Codex / Claude Code / Gemini / OpenCode directly from WeChat, Feishu, or Yuanbao. It connects chat messages to your agent CLI sessions through `acpx`, so you can, right from your phone:
|
|
15
15
|
|
|
16
|
+
[](https://imgchr.com/i/pmZXIv6)
|
|
17
|
+
|
|
16
18
|
- Create and switch between sessions
|
|
17
19
|
- Have the agent keep working in a specific project directory
|
|
18
20
|
- View streaming replies, final results, and tool-call summaries
|
|
@@ -427,7 +429,7 @@ Notes:
|
|
|
427
429
|
|
|
428
430
|
If you want to first understand when to delegate and when to dispatch multiple subtasks in parallel, see:
|
|
429
431
|
|
|
430
|
-
- [docs/
|
|
432
|
+
- [docs/xacpx-group-usage-guide.md](./docs/xacpx-group-usage-guide.md)
|
|
431
433
|
|
|
432
434
|
|
|
433
435
|
### MCP integration: external coordinator
|
|
@@ -579,7 +581,7 @@ If what you're about to do is one of the following, you can continue from here:
|
|
|
579
581
|
|
|
580
582
|
- Want the full chat-command reference: [docs/commands.md](./docs/commands.md)
|
|
581
583
|
- Want to schedule a one-time future message with scheduled tasks (`/later`): [docs/later-command.md](./docs/later-command.md)
|
|
582
|
-
- Want to understand when to delegate and when to open a group: [docs/
|
|
584
|
+
- Want to understand when to delegate and when to open a group: [docs/xacpx-group-usage-guide.md](./docs/xacpx-group-usage-guide.md)
|
|
583
585
|
|
|
584
586
|
### Troubleshooting and verification
|
|
585
587
|
|
|
@@ -880,10 +880,11 @@ var init_session = __esm(() => {
|
|
|
880
880
|
replyModeHeader: "Current reply mode:",
|
|
881
881
|
replyModeSessionLabel: (alias) => `- Session: ${alias}`,
|
|
882
882
|
replyModeGlobalDefault: (value) => `- Global default: ${value}`,
|
|
883
|
+
replyModeChannelDefault: (value) => `- Channel default: ${value}`,
|
|
883
884
|
replyModeSessionOverride: (value) => `- Session override: ${value}`,
|
|
884
885
|
replyModeEffective: (value) => `- Effective: ${value}`,
|
|
885
886
|
replyModeSet: (replyMode) => `Current session reply mode set to: ${replyMode}`,
|
|
886
|
-
replyModeReset: (
|
|
887
|
+
replyModeReset: (effective) => `Session reply mode reset. Now effective: ${effective}`,
|
|
887
888
|
statusHeader: "Current session:",
|
|
888
889
|
statusNameLabel: (alias) => `- Name: ${alias}`,
|
|
889
890
|
statusAgentLabel: (agent) => `- Agent: ${agent}`,
|
|
@@ -1426,6 +1427,8 @@ var init_config = __esm(() => {
|
|
|
1426
1427
|
mustBePositiveNumber: (path2) => `${path2} must be a positive number.`,
|
|
1427
1428
|
channelTypeDisabled: "channel.type is a legacy single-channel field; /config set writes are disabled. Use `xacpx channel ...` to manage channels[], then restart xacpx.",
|
|
1428
1429
|
channelReplyModeInvalid: "channel.replyMode only supports: stream, final, verbose",
|
|
1430
|
+
channelRuntimeNotFound: (id) => `Channel "${id}" does not exist; add it first with \`xacpx channel add ${id}\`.`,
|
|
1431
|
+
channelRuntimeReplyModeInvalid: (id) => `channels.${id}.replyMode only supports: stream, final, verbose`,
|
|
1429
1432
|
wechatReplyModeInvalid: "wechat.replyMode only supports: stream, final, verbose",
|
|
1430
1433
|
wechatReplyModeMapped: (value) => `${value} (mapped to channel.replyMode)`,
|
|
1431
1434
|
agentNotFound: (name) => `Agent "${name}" does not exist. Create it first.`,
|
|
@@ -1615,7 +1618,6 @@ var init_cli_update = __esm(() => {
|
|
|
1615
1618
|
updateFailed: (name, error) => `${name} update failed: ${error}`,
|
|
1616
1619
|
targetNotFound: (name) => `Update target not found: ${name}`,
|
|
1617
1620
|
targetVersionUnknown: (name) => `${name}: cannot check latest version; skipped.`,
|
|
1618
|
-
targetNotPinned: (name) => `${name} has no recorded version; use \`xacpx plugin update ${name}\` or specify a version explicitly.`,
|
|
1619
1621
|
multiTargetNonInteractive: "Installed plugins detected; in non-interactive mode use `xacpx update --all` or `xacpx update <name>`.",
|
|
1620
1622
|
selectionPrompt: "Select items to update (numbers, comma-separated; a=all; Enter to cancel): ",
|
|
1621
1623
|
selectionInvalid: (part) => `Invalid selection: ${part}`,
|
|
@@ -1646,6 +1648,8 @@ var init_channel_cli = __esm(() => {
|
|
|
1646
1648
|
channelRemoved: (id) => `Channel ${id} removed`,
|
|
1647
1649
|
cannotDisableLastEnabled: "Cannot disable the last enabled channel.",
|
|
1648
1650
|
channelEnabledToggled: (id, enabled) => `Channel ${id} ${enabled ? "enabled" : "disabled"}`,
|
|
1651
|
+
channelReplyModeSet: (id, mode) => `Channel ${id} default reply mode set to: ${mode}`,
|
|
1652
|
+
channelReplyModeInvalid: (mode) => `reply mode must be stream / final / verbose, got: ${mode}`,
|
|
1649
1653
|
channelAccountAlreadyExists: (type, accountId) => `Account ${accountId} already exists on channel ${type}; run xacpx channel rm ${type} --account ${accountId} first`,
|
|
1650
1654
|
channelAccountAdded: (type, accountId) => `Channel ${type} account ${accountId} added`,
|
|
1651
1655
|
channelReEnabled: (type) => `Channel ${type} was disabled; it has been automatically re-enabled.`,
|
|
@@ -1946,10 +1950,11 @@ var init_session2 = __esm(() => {
|
|
|
1946
1950
|
replyModeHeader: "当前 reply mode:",
|
|
1947
1951
|
replyModeSessionLabel: (alias) => `- 会话:${alias}`,
|
|
1948
1952
|
replyModeGlobalDefault: (value) => `- 全局默认:${value}`,
|
|
1953
|
+
replyModeChannelDefault: (value) => `- 频道默认:${value}`,
|
|
1949
1954
|
replyModeSessionOverride: (value) => `- 当前会话覆盖:${value}`,
|
|
1950
1955
|
replyModeEffective: (value) => `- 当前生效:${value}`,
|
|
1951
1956
|
replyModeSet: (replyMode) => `已设置当前会话 reply mode:${replyMode}`,
|
|
1952
|
-
replyModeReset: (
|
|
1957
|
+
replyModeReset: (effective) => `已重置当前会话 reply mode,当前生效:${effective}`,
|
|
1953
1958
|
statusHeader: "当前会话:",
|
|
1954
1959
|
statusNameLabel: (alias) => `- 名称:${alias}`,
|
|
1955
1960
|
statusAgentLabel: (agent2) => `- Agent:${agent2}`,
|
|
@@ -2492,6 +2497,8 @@ var init_config2 = __esm(() => {
|
|
|
2492
2497
|
mustBePositiveNumber: (path2) => `${path2} 必须是正数。`,
|
|
2493
2498
|
channelTypeDisabled: "channel.type 是旧单频道字段,/config set 已禁用写入;请使用 `xacpx channel ...` 管理 channels[],然后重启 xacpx。",
|
|
2494
2499
|
channelReplyModeInvalid: "channel.replyMode 只支持:stream、final、verbose",
|
|
2500
|
+
channelRuntimeNotFound: (id) => `频道「${id}」不存在;请先用 \`xacpx channel add ${id}\` 添加。`,
|
|
2501
|
+
channelRuntimeReplyModeInvalid: (id) => `channels.${id}.replyMode 只支持:stream、final、verbose`,
|
|
2495
2502
|
wechatReplyModeInvalid: "wechat.replyMode 只支持:stream、final、verbose",
|
|
2496
2503
|
wechatReplyModeMapped: (value) => `${value}(已映射到 channel.replyMode)`,
|
|
2497
2504
|
agentNotFound: (name) => `Agent「${name}」不存在,请先创建。`,
|
|
@@ -2681,7 +2688,6 @@ var init_cli_update2 = __esm(() => {
|
|
|
2681
2688
|
updateFailed: (name, error) => `${name} 更新失败:${error}`,
|
|
2682
2689
|
targetNotFound: (name) => `没有找到更新项:${name}`,
|
|
2683
2690
|
targetVersionUnknown: (name) => `${name} 无法检查最新版本,已跳过。`,
|
|
2684
|
-
targetNotPinned: (name) => `${name} 未记录当前版本;请先使用 \`xacpx plugin update ${name}\` 或显式选择版本。`,
|
|
2685
2691
|
multiTargetNonInteractive: "检测到已安装插件;非交互模式请使用 `xacpx update --all` 或 `xacpx update <name>`。",
|
|
2686
2692
|
selectionPrompt: "请选择要更新的项目(数字,逗号分隔,a=全部,回车取消):",
|
|
2687
2693
|
selectionInvalid: (part) => `无效选择:${part}`,
|
|
@@ -2712,6 +2718,8 @@ var init_channel_cli2 = __esm(() => {
|
|
|
2712
2718
|
channelRemoved: (id) => `频道 ${id} 已删除`,
|
|
2713
2719
|
cannotDisableLastEnabled: "不能禁用最后一个启用的频道。",
|
|
2714
2720
|
channelEnabledToggled: (id, enabled) => `频道 ${id} 已${enabled ? "启用" : "禁用"}`,
|
|
2721
|
+
channelReplyModeSet: (id, mode) => `频道 ${id} 的默认 reply mode 已设置为:${mode}`,
|
|
2722
|
+
channelReplyModeInvalid: (mode) => `reply mode 只支持 stream / final / verbose,收到:${mode}`,
|
|
2715
2723
|
channelAccountAlreadyExists: (type, accountId) => `频道 ${type} 的账号 ${accountId} 已存在;先 xacpx channel rm ${type} --account ${accountId}`,
|
|
2716
2724
|
channelAccountAdded: (type, accountId) => `频道 ${type} 账号 ${accountId} 已添加`,
|
|
2717
2725
|
channelReEnabled: (type) => `频道 ${type} 此前是 disabled 状态,已自动启用。`,
|
|
@@ -3030,10 +3038,10 @@ import { spawn as spawn3 } from "node:child_process";
|
|
|
3030
3038
|
import { readFile, unlink } from "node:fs/promises";
|
|
3031
3039
|
import { homedir as homedir2 } from "node:os";
|
|
3032
3040
|
import { join as join2 } from "node:path";
|
|
3033
|
-
function
|
|
3034
|
-
const { command, args } = splitCommandLine(input.
|
|
3041
|
+
function buildXacpxMcpServerSpec(input) {
|
|
3042
|
+
const { command, args } = splitCommandLine(input.xacpxCommand);
|
|
3035
3043
|
return {
|
|
3036
|
-
name: "
|
|
3044
|
+
name: "xacpx",
|
|
3037
3045
|
type: "stdio",
|
|
3038
3046
|
command,
|
|
3039
3047
|
args: [
|
|
@@ -3060,7 +3068,7 @@ function buildQueueOwnerPayload(input) {
|
|
|
3060
3068
|
|
|
3061
3069
|
class AcpxQueueOwnerLauncher {
|
|
3062
3070
|
acpxCommand;
|
|
3063
|
-
|
|
3071
|
+
xacpxCommand;
|
|
3064
3072
|
spawnOwner;
|
|
3065
3073
|
terminateOwner;
|
|
3066
3074
|
baseEnv;
|
|
@@ -3069,7 +3077,7 @@ class AcpxQueueOwnerLauncher {
|
|
|
3069
3077
|
launchLocks = new Map;
|
|
3070
3078
|
constructor(options) {
|
|
3071
3079
|
this.acpxCommand = options.acpxCommand;
|
|
3072
|
-
this.
|
|
3080
|
+
this.xacpxCommand = options.xacpxCommand ?? resolveDefaultXacpxCommand(options.baseEnv ?? process.env);
|
|
3073
3081
|
this.spawnOwner = options.spawnOwner ?? defaultQueueOwnerSpawner;
|
|
3074
3082
|
this.terminateOwner = options.terminateOwner ?? createDefaultQueueOwnerTerminator(options.acpxCommand);
|
|
3075
3083
|
this.baseEnv = options.baseEnv ?? process.env;
|
|
@@ -3097,8 +3105,8 @@ class AcpxQueueOwnerLauncher {
|
|
|
3097
3105
|
nonInteractivePermissions: input.nonInteractivePermissions,
|
|
3098
3106
|
ttlMs: this.ttlMs,
|
|
3099
3107
|
maxQueueDepth: this.maxQueueDepth,
|
|
3100
|
-
mcpServers: [
|
|
3101
|
-
|
|
3108
|
+
mcpServers: [buildXacpxMcpServerSpec({
|
|
3109
|
+
xacpxCommand: this.xacpxCommand,
|
|
3102
3110
|
coordinatorSession: input.coordinatorSession,
|
|
3103
3111
|
...input.sourceHandle ? { sourceHandle: input.sourceHandle } : {}
|
|
3104
3112
|
})]
|
|
@@ -3153,13 +3161,13 @@ function splitCommandLine(value) {
|
|
|
3153
3161
|
current += "\\";
|
|
3154
3162
|
}
|
|
3155
3163
|
if (quote) {
|
|
3156
|
-
throw new Error("
|
|
3164
|
+
throw new Error("xacpx MCP command has an unterminated quote");
|
|
3157
3165
|
}
|
|
3158
3166
|
if (current.length > 0) {
|
|
3159
3167
|
parts.push(current);
|
|
3160
3168
|
}
|
|
3161
3169
|
if (parts.length === 0) {
|
|
3162
|
-
throw new Error("
|
|
3170
|
+
throw new Error("xacpx MCP command must not be empty");
|
|
3163
3171
|
}
|
|
3164
3172
|
return { command: parts[0], args: parts.slice(1) };
|
|
3165
3173
|
}
|
|
@@ -3205,7 +3213,7 @@ function queueLockFilePath(sessionId) {
|
|
|
3205
3213
|
function shortHash(value, length) {
|
|
3206
3214
|
return createHash("sha256").update(value).digest("hex").slice(0, length);
|
|
3207
3215
|
}
|
|
3208
|
-
function
|
|
3216
|
+
function resolveDefaultXacpxCommand(env) {
|
|
3209
3217
|
const cliCommand = coreEnv("CLI_COMMAND", env);
|
|
3210
3218
|
if (cliCommand?.trim()) {
|
|
3211
3219
|
return cliCommand.trim();
|
|
@@ -3641,8 +3649,9 @@ class BridgeRuntime {
|
|
|
3641
3649
|
} else if (typeof parsed.id === "string") {
|
|
3642
3650
|
acpxRecordId = parsed.id;
|
|
3643
3651
|
}
|
|
3652
|
+
const agentSessionId = typeof parsed.agentSessionId === "string" ? parsed.agentSessionId : undefined;
|
|
3644
3653
|
if (acpxRecordId) {
|
|
3645
|
-
return { acpxRecordId };
|
|
3654
|
+
return { acpxRecordId, agentSessionId };
|
|
3646
3655
|
}
|
|
3647
3656
|
} catch {
|
|
3648
3657
|
const firstLine = result.stdout.trim().split(/\r?\n/, 1)[0];
|
|
@@ -3652,6 +3661,10 @@ class BridgeRuntime {
|
|
|
3652
3661
|
}
|
|
3653
3662
|
throw new Error("failed to resolve acpx session record id");
|
|
3654
3663
|
}
|
|
3664
|
+
async getAgentSessionId(input) {
|
|
3665
|
+
const record = await this.readSessionRecord(input);
|
|
3666
|
+
return { agentSessionId: record.agentSessionId };
|
|
3667
|
+
}
|
|
3655
3668
|
async setMode(input) {
|
|
3656
3669
|
const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
|
|
3657
3670
|
"set-mode",
|
|
@@ -3919,7 +3932,8 @@ var BRIDGE_METHODS = new Set([
|
|
|
3919
3932
|
"prompt",
|
|
3920
3933
|
"setMode",
|
|
3921
3934
|
"cancel",
|
|
3922
|
-
"removeSession"
|
|
3935
|
+
"removeSession",
|
|
3936
|
+
"getAgentSessionId"
|
|
3923
3937
|
]);
|
|
3924
3938
|
var SESSION_SCOPED_METHODS = new Set([
|
|
3925
3939
|
"hasSession",
|
|
@@ -3929,7 +3943,8 @@ var SESSION_SCOPED_METHODS = new Set([
|
|
|
3929
3943
|
"prompt",
|
|
3930
3944
|
"setMode",
|
|
3931
3945
|
"cancel",
|
|
3932
|
-
"removeSession"
|
|
3946
|
+
"removeSession",
|
|
3947
|
+
"getAgentSessionId"
|
|
3933
3948
|
]);
|
|
3934
3949
|
|
|
3935
3950
|
class BridgeServer {
|
|
@@ -4105,6 +4120,13 @@ class BridgeServer {
|
|
|
4105
4120
|
cwd: requireString(params, "cwd"),
|
|
4106
4121
|
name: requireString(params, "name")
|
|
4107
4122
|
});
|
|
4123
|
+
case "getAgentSessionId":
|
|
4124
|
+
return await this.runtime.getAgentSessionId({
|
|
4125
|
+
agent: requireString(params, "agent"),
|
|
4126
|
+
agentCommand: asOptionalString(params.agentCommand),
|
|
4127
|
+
cwd: requireString(params, "cwd"),
|
|
4128
|
+
name: requireString(params, "name")
|
|
4129
|
+
});
|
|
4108
4130
|
default:
|
|
4109
4131
|
throw new Error(`unsupported bridge method: ${method}`);
|
|
4110
4132
|
}
|