weacpx 0.1.3 → 0.1.5
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 +75 -10
- package/dist/bridge/bridge-main.js +75 -24
- package/dist/cli.js +2817 -252
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -28,17 +28,10 @@ bun add -g weacpx
|
|
|
28
28
|
|
|
29
29
|
第一次使用建议按这个顺序:
|
|
30
30
|
|
|
31
|
-
1. 登录微信
|
|
32
|
-
2. 启动服务
|
|
31
|
+
1. 登录微信 `weacpx login`
|
|
32
|
+
2. 启动服务 `weacpx start`
|
|
33
33
|
3. 在微信里创建会话并开始对话
|
|
34
34
|
|
|
35
|
-
如果你是全局安装版本:
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
weacpx login
|
|
39
|
-
weacpx start
|
|
40
|
-
```
|
|
41
|
-
|
|
42
35
|
`weacpx login` 会在终端里显示二维码,使用微信扫描登录。`weacpx start` 启动后,在微信里发:
|
|
43
36
|
|
|
44
37
|
```text
|
|
@@ -57,6 +50,8 @@ weacpx start
|
|
|
57
50
|
hello
|
|
58
51
|
```
|
|
59
52
|
|
|
53
|
+
如果任务比较长,`weacpx` 会优先把 Agent 的中间回复分段发回微信,而不是一直等到最后一条结果。
|
|
54
|
+
|
|
60
55
|
如果你是从源码仓库直接使用:
|
|
61
56
|
|
|
62
57
|
```bash
|
|
@@ -75,6 +70,7 @@ bun run dev
|
|
|
75
70
|
常用命令:
|
|
76
71
|
|
|
77
72
|
- `weacpx login`
|
|
73
|
+
- `weacpx logout`
|
|
78
74
|
- `weacpx run`
|
|
79
75
|
- `weacpx start`
|
|
80
76
|
- `weacpx status`
|
|
@@ -86,6 +82,12 @@ bun run dev
|
|
|
86
82
|
- `start` 后台启动
|
|
87
83
|
- `status` 查看后台状态、PID、配置路径和日志路径
|
|
88
84
|
- `stop` 停止后台实例
|
|
85
|
+
- `logout` 清除本机已保存的微信登录凭证;如果当前没有已登录账号,会直接提示
|
|
86
|
+
|
|
87
|
+
说明:
|
|
88
|
+
|
|
89
|
+
- `weacpx logout` 只清理已保存的微信账号凭证
|
|
90
|
+
- 它不会停止当前 daemon,也不会删除 `weacpx` 的 session/state 配置
|
|
89
91
|
|
|
90
92
|
## 微信中使用说明
|
|
91
93
|
|
|
@@ -153,16 +155,42 @@ bun run dev
|
|
|
153
155
|
| `/ss attach <alias> -a <name> --ws <name> --name <transport-session>` | 恢复已存在的会话 |
|
|
154
156
|
| `/use <alias>` | 切换当前会话 |
|
|
155
157
|
| `/status` | 查看当前会话状态 |
|
|
158
|
+
| `/session reset` | 重置当前会话上下文,保留 alias/agent/workspace,但重新绑定到一个新的后端 session |
|
|
159
|
+
| `/clear` | `/session reset` 的快捷别名 |
|
|
156
160
|
| `/cancel` | 取消当前会话 |
|
|
157
|
-
| `/stop` |
|
|
161
|
+
| `/stop` | `/cancel` 的别名,用于取消当前会话 |
|
|
158
162
|
|
|
159
163
|
说明:
|
|
160
164
|
|
|
161
165
|
- `/ss <agent> -d <path>` 是最常用入口,会自动按目录名推导并创建或复用 workspace,再创建或复用 session
|
|
162
166
|
- `/ss new <agent> -d <path>` 表示强制新建 session
|
|
163
167
|
- `/use <alias>` 用来切换当前会话
|
|
168
|
+
- `/session reset` 和 `/clear` 会保留当前逻辑会话名,但重新创建一个新的后端 session,从空上下文重新开始
|
|
164
169
|
- 非 `/` 开头的文本会发送到当前 session
|
|
165
170
|
|
|
171
|
+
### 权限策略
|
|
172
|
+
|
|
173
|
+
`weacpx` 支持直接在微信里查看和切换 `acpx` 的权限策略。
|
|
174
|
+
|
|
175
|
+
| 命令 | 说明 |
|
|
176
|
+
|------|------|
|
|
177
|
+
| `/pm` / `/permission` | 查看当前权限模式 |
|
|
178
|
+
| `/pm set allow` | 切到 `approve-all` |
|
|
179
|
+
| `/pm set read` | 切到 `approve-reads` |
|
|
180
|
+
| `/pm set deny` | 切到 `deny-all` |
|
|
181
|
+
| `/pm auto` | 查看当前非交互策略 |
|
|
182
|
+
| `/pm auto allow` | 切到 `allow` |
|
|
183
|
+
| `/pm auto deny` | 切到 `deny` |
|
|
184
|
+
| `/pm auto fail` | 切到 `fail` |
|
|
185
|
+
|
|
186
|
+
说明:
|
|
187
|
+
|
|
188
|
+
- `allow` 对应 `approve-all`
|
|
189
|
+
- `read` 对应 `approve-reads`
|
|
190
|
+
- `deny` 对应 `deny-all`
|
|
191
|
+
- `/pm auto ...` 修改的是 `transport.nonInteractivePermissions`
|
|
192
|
+
- 这些命令会把结果写回 `config.json`
|
|
193
|
+
|
|
166
194
|
### 推荐工作流
|
|
167
195
|
|
|
168
196
|
新建一个可聊天的会话:
|
|
@@ -190,6 +218,21 @@ bun run dev
|
|
|
190
218
|
/workspace rm old-repo
|
|
191
219
|
```
|
|
192
220
|
|
|
221
|
+
### 微信内置登录相关指令
|
|
222
|
+
|
|
223
|
+
除了 `weacpx login` / `weacpx logout` 这类 CLI 命令外,微信通道里还支持少量内置 slash 指令:
|
|
224
|
+
|
|
225
|
+
| 命令 | 说明 |
|
|
226
|
+
|------|------|
|
|
227
|
+
| `/clear` | 重置当前聊天绑定的会话上下文,效果等同于 `/session reset` |
|
|
228
|
+
| `/logout` | 清除当前机器上已保存的所有微信账号凭证 |
|
|
229
|
+
|
|
230
|
+
说明:
|
|
231
|
+
|
|
232
|
+
- `/logout` 的语义和 CLI 的 `weacpx logout` 一致,都是清凭证
|
|
233
|
+
- 如果当前没有已登录账号,会提示“当前没有已登录的账号”
|
|
234
|
+
- `/logout` 不会停止后台服务,也不会删除 `weacpx` 的工作区、agent、session 状态
|
|
235
|
+
|
|
193
236
|
## 配置与运行文件
|
|
194
237
|
|
|
195
238
|
默认文件位置:
|
|
@@ -211,6 +254,28 @@ bun run dev
|
|
|
211
254
|
- `WEACPX_STATE`
|
|
212
255
|
- `WEACPX_WEIXIN_SDK`
|
|
213
256
|
|
|
257
|
+
### Transport 权限配置
|
|
258
|
+
|
|
259
|
+
`config.json` 中的 `transport` 支持以下权限字段:
|
|
260
|
+
|
|
261
|
+
```json
|
|
262
|
+
{
|
|
263
|
+
"transport": {
|
|
264
|
+
"type": "acpx-bridge",
|
|
265
|
+
"sessionInitTimeoutMs": 120000,
|
|
266
|
+
"permissionMode": "approve-all",
|
|
267
|
+
"nonInteractivePermissions": "fail"
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
说明:
|
|
273
|
+
|
|
274
|
+
- `permissionMode`: `approve-all`、`approve-reads`、`deny-all`
|
|
275
|
+
- `nonInteractivePermissions`: `allow`、`deny`、`fail`
|
|
276
|
+
- 默认值分别是 `approve-all` 和 `fail`
|
|
277
|
+
- 也可以直接在微信里通过 `/pm` 和 `/pm auto` 修改
|
|
278
|
+
|
|
214
279
|
### 日志配置
|
|
215
280
|
|
|
216
281
|
`config.json` 支持可选的 `logging` 配置:
|
|
@@ -28,21 +28,6 @@ var __export = (target, all) => {
|
|
|
28
28
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
29
29
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
30
30
|
|
|
31
|
-
// src/process/spawn-command.ts
|
|
32
|
-
function resolveSpawnCommand(command, args) {
|
|
33
|
-
if (SCRIPT_FILE_PATTERN.test(command)) {
|
|
34
|
-
return {
|
|
35
|
-
command: process.execPath,
|
|
36
|
-
args: [command, ...args]
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
return { command, args };
|
|
40
|
-
}
|
|
41
|
-
var SCRIPT_FILE_PATTERN;
|
|
42
|
-
var init_spawn_command = __esm(() => {
|
|
43
|
-
SCRIPT_FILE_PATTERN = /\.(c|m)?js$/i;
|
|
44
|
-
});
|
|
45
|
-
|
|
46
31
|
// src/transport/prompt-output.ts
|
|
47
32
|
function getPromptText(result) {
|
|
48
33
|
const stdoutOutput = extractPromptOutput(result.stdout);
|
|
@@ -51,14 +36,14 @@ function getPromptText(result) {
|
|
|
51
36
|
}
|
|
52
37
|
const preferredError = extractPromptFailureMessage(result);
|
|
53
38
|
if (preferredError) {
|
|
54
|
-
throw new
|
|
39
|
+
throw new PromptCommandError(preferredError, result);
|
|
55
40
|
}
|
|
56
41
|
const stderrOutput = extractPromptOutput(result.stderr);
|
|
57
42
|
const partialReply = [stdoutOutput, stderrOutput].filter((output) => output.hasAgentMessage && output.text.length > 0).map((output) => sanitizePromptText(output.text)).find((text) => text.length > 0);
|
|
58
43
|
if (partialReply) {
|
|
59
44
|
return partialReply;
|
|
60
45
|
}
|
|
61
|
-
throw new
|
|
46
|
+
throw new PromptCommandError(`command failed with exit code ${result.code}`, result);
|
|
62
47
|
}
|
|
63
48
|
function normalizeCommandError(result) {
|
|
64
49
|
const preferredError = extractPromptFailureMessage(result);
|
|
@@ -159,11 +144,43 @@ function extractJsonRpcErrorMessages(output) {
|
|
|
159
144
|
return [];
|
|
160
145
|
});
|
|
161
146
|
}
|
|
147
|
+
var PromptCommandError;
|
|
148
|
+
var init_prompt_output = __esm(() => {
|
|
149
|
+
PromptCommandError = class PromptCommandError extends Error {
|
|
150
|
+
exitCode;
|
|
151
|
+
stdout;
|
|
152
|
+
stderr;
|
|
153
|
+
constructor(message, result) {
|
|
154
|
+
super(message);
|
|
155
|
+
this.name = "PromptCommandError";
|
|
156
|
+
this.exitCode = result.code;
|
|
157
|
+
this.stdout = result.stdout;
|
|
158
|
+
this.stderr = result.stderr;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// src/process/spawn-command.ts
|
|
164
|
+
function resolveSpawnCommand(command, args) {
|
|
165
|
+
if (SCRIPT_FILE_PATTERN.test(command)) {
|
|
166
|
+
return {
|
|
167
|
+
command: process.execPath,
|
|
168
|
+
args: [command, ...args]
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return { command, args };
|
|
172
|
+
}
|
|
173
|
+
var SCRIPT_FILE_PATTERN;
|
|
174
|
+
var init_spawn_command = __esm(() => {
|
|
175
|
+
SCRIPT_FILE_PATTERN = /\.(c|m)?js$/i;
|
|
176
|
+
});
|
|
162
177
|
|
|
163
178
|
// src/bridge/bridge-main.ts
|
|
164
179
|
import { createInterface } from "node:readline";
|
|
165
180
|
|
|
166
181
|
// src/bridge/bridge-server.ts
|
|
182
|
+
init_prompt_output();
|
|
183
|
+
|
|
167
184
|
class BridgeServer {
|
|
168
185
|
runtime;
|
|
169
186
|
constructor(runtime) {
|
|
@@ -186,7 +203,14 @@ class BridgeServer {
|
|
|
186
203
|
ok: false,
|
|
187
204
|
error: {
|
|
188
205
|
code: "BRIDGE_INTERNAL_ERROR",
|
|
189
|
-
message
|
|
206
|
+
message,
|
|
207
|
+
...error instanceof PromptCommandError ? {
|
|
208
|
+
details: {
|
|
209
|
+
exitCode: error.exitCode,
|
|
210
|
+
stdout: error.stdout,
|
|
211
|
+
stderr: error.stderr
|
|
212
|
+
}
|
|
213
|
+
} : {}
|
|
190
214
|
}
|
|
191
215
|
})}
|
|
192
216
|
`;
|
|
@@ -241,6 +265,7 @@ function asOptionalString(value) {
|
|
|
241
265
|
|
|
242
266
|
// src/bridge/bridge-runtime.ts
|
|
243
267
|
init_spawn_command();
|
|
268
|
+
init_prompt_output();
|
|
244
269
|
import { spawn } from "node:child_process";
|
|
245
270
|
import { fileURLToPath } from "node:url";
|
|
246
271
|
|
|
@@ -248,10 +273,12 @@ class BridgeRuntime {
|
|
|
248
273
|
command;
|
|
249
274
|
run;
|
|
250
275
|
runSessionCreate;
|
|
251
|
-
|
|
276
|
+
options;
|
|
277
|
+
constructor(command = "acpx", run = defaultRunner, runSessionCreate = shellSessionCreateRunner, options = {}) {
|
|
252
278
|
this.command = command;
|
|
253
279
|
this.run = run;
|
|
254
280
|
this.runSessionCreate = runSessionCreate;
|
|
281
|
+
this.options = options;
|
|
255
282
|
}
|
|
256
283
|
async hasSession(input) {
|
|
257
284
|
const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
|
|
@@ -314,16 +341,37 @@ class BridgeRuntime {
|
|
|
314
341
|
return {};
|
|
315
342
|
}
|
|
316
343
|
buildSessionArgs(input, tail) {
|
|
344
|
+
const prefix = [
|
|
345
|
+
"--format",
|
|
346
|
+
"quiet",
|
|
347
|
+
"--cwd",
|
|
348
|
+
input.cwd,
|
|
349
|
+
...this.buildPermissionArgs()
|
|
350
|
+
];
|
|
317
351
|
if (input.agentCommand) {
|
|
318
|
-
return [
|
|
352
|
+
return [...prefix, "--agent", input.agentCommand, ...tail];
|
|
319
353
|
}
|
|
320
|
-
return [
|
|
354
|
+
return [...prefix, input.agent, ...tail];
|
|
321
355
|
}
|
|
322
356
|
buildPromptArgs(input, tail) {
|
|
357
|
+
const prefix = [
|
|
358
|
+
"--format",
|
|
359
|
+
"json",
|
|
360
|
+
"--json-strict",
|
|
361
|
+
"--cwd",
|
|
362
|
+
input.cwd,
|
|
363
|
+
...this.buildPermissionArgs()
|
|
364
|
+
];
|
|
323
365
|
if (input.agentCommand) {
|
|
324
|
-
return [
|
|
366
|
+
return [...prefix, "--agent", input.agentCommand, ...tail];
|
|
325
367
|
}
|
|
326
|
-
return [
|
|
368
|
+
return [...prefix, input.agent, ...tail];
|
|
369
|
+
}
|
|
370
|
+
buildPermissionArgs() {
|
|
371
|
+
const permissionMode = this.options.permissionMode ?? "approve-all";
|
|
372
|
+
const nonInteractivePermissions = this.options.nonInteractivePermissions ?? "fail";
|
|
373
|
+
const modeFlag = permissionMode === "approve-reads" ? "--approve-reads" : permissionMode === "deny-all" ? "--deny-all" : "--approve-all";
|
|
374
|
+
return [modeFlag, "--non-interactive-permissions", nonInteractivePermissions];
|
|
327
375
|
}
|
|
328
376
|
}
|
|
329
377
|
async function defaultRunner(command, args) {
|
|
@@ -365,7 +413,10 @@ async function shellSessionCreateRunner(command, args, cwd) {
|
|
|
365
413
|
}
|
|
366
414
|
|
|
367
415
|
// src/bridge/bridge-main.ts
|
|
368
|
-
var server = new BridgeServer(new BridgeRuntime(process.env.WEACPX_BRIDGE_ACPX_COMMAND ?? "acpx"
|
|
416
|
+
var server = new BridgeServer(new BridgeRuntime(process.env.WEACPX_BRIDGE_ACPX_COMMAND ?? "acpx", undefined, undefined, {
|
|
417
|
+
permissionMode: process.env.WEACPX_BRIDGE_PERMISSION_MODE === "approve-reads" || process.env.WEACPX_BRIDGE_PERMISSION_MODE === "deny-all" ? process.env.WEACPX_BRIDGE_PERMISSION_MODE : "approve-all",
|
|
418
|
+
nonInteractivePermissions: process.env.WEACPX_BRIDGE_NON_INTERACTIVE_PERMISSIONS === "deny" ? "deny" : "fail"
|
|
419
|
+
}));
|
|
369
420
|
var input = createInterface({
|
|
370
421
|
input: process.stdin,
|
|
371
422
|
crlfDelay: Infinity
|