weacpx 0.1.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/LICENSE +21 -0
- package/README.md +261 -0
- package/config.example.json +30 -0
- package/dist/bridge/bridge-main.js +229 -0
- package/dist/cli.js +3224 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gadzan Mak
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# weacpx
|
|
2
|
+
|
|
3
|
+
通过微信 ClawBot 远程控制通过 `acpx` 控制 Claude Code、Codex 等 Agents。
|
|
4
|
+
|
|
5
|
+
## weacpx 是什么
|
|
6
|
+
|
|
7
|
+
`weacpx` 基于以下组件工作:
|
|
8
|
+
|
|
9
|
+
- `weixin-agent-sdk`
|
|
10
|
+
- `acpx`
|
|
11
|
+
- `acpx` 已支持的 agent driver,或自定义 ACP agent
|
|
12
|
+
|
|
13
|
+
它适合这样的场景:
|
|
14
|
+
|
|
15
|
+
- 你已经在本机使用 `acpx`
|
|
16
|
+
- 你希望通过微信远程发起或继续一个 agent 会话
|
|
17
|
+
- 你希望在手机上完成常见的会话切换、目录切换和对话操作
|
|
18
|
+
|
|
19
|
+
## 安装前准备
|
|
20
|
+
|
|
21
|
+
开始前,至少需要:
|
|
22
|
+
|
|
23
|
+
- Node.js 22+
|
|
24
|
+
- Bun
|
|
25
|
+
- 一个可用的微信登录环境
|
|
26
|
+
- 本机可以运行 `acpx` 及其目标 agent
|
|
27
|
+
|
|
28
|
+
正常情况下,不需要再额外全局安装 `acpx`。
|
|
29
|
+
|
|
30
|
+
## 安装
|
|
31
|
+
|
|
32
|
+
全局安装:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# 使用 NPM 全局安装
|
|
36
|
+
npm install -g weacpx
|
|
37
|
+
# 或使用 bun 全局安装
|
|
38
|
+
bun add -g weacpx
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
如果你是从源码仓库直接使用,请先安装依赖并构建:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bun install
|
|
45
|
+
bun run dev
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 快速开始
|
|
49
|
+
|
|
50
|
+
第一次使用建议按这个顺序:
|
|
51
|
+
|
|
52
|
+
1. 登录微信
|
|
53
|
+
2. 启动服务
|
|
54
|
+
3. 在微信里创建会话并开始对话
|
|
55
|
+
|
|
56
|
+
如果你是全局安装版本:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
weacpx login
|
|
60
|
+
weacpx start
|
|
61
|
+
weacpx status
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
如果你是在仓库里本地运行:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
bun run login
|
|
68
|
+
bun run dev
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`weacpx login` 和 `bun run login` 都会在终端里显示二维码。
|
|
72
|
+
|
|
73
|
+
启动后,在微信里先发:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
/ss codex -d /absolute/path/to/your/repo
|
|
77
|
+
|
|
78
|
+
/help
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
第一行的意思是:开启或挂在一个会话,并切换到该会话。使用 Codex,并指定工作目录为 `/absolute/path/to/your/repo`。
|
|
82
|
+
第二行的意思是:查看帮助信息。
|
|
83
|
+
|
|
84
|
+
然后就可以直接发普通消息,例如:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
hello
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
普通文本会默认发送到当前选中的 session。
|
|
91
|
+
|
|
92
|
+
## CLI 命令
|
|
93
|
+
|
|
94
|
+
常用命令:
|
|
95
|
+
|
|
96
|
+
- `weacpx login`
|
|
97
|
+
- `weacpx run`
|
|
98
|
+
- `weacpx start`
|
|
99
|
+
- `weacpx status`
|
|
100
|
+
- `weacpx stop`
|
|
101
|
+
|
|
102
|
+
说明:
|
|
103
|
+
|
|
104
|
+
- `run` 前台运行,适合调试
|
|
105
|
+
- `start` 后台启动
|
|
106
|
+
- `status` 查看后台状态、PID、配置路径和日志路径
|
|
107
|
+
- `stop` 停止后台实例
|
|
108
|
+
|
|
109
|
+
## 微信中如何使用
|
|
110
|
+
|
|
111
|
+
### Agent
|
|
112
|
+
|
|
113
|
+
内置 `codex` 与 `claude` 两个常见模板,也支持添加你自己的 agent。
|
|
114
|
+
|
|
115
|
+
- `/agents` 查看当前已添加的 agent
|
|
116
|
+
- `/agent add codex` 添加 codex agent
|
|
117
|
+
- `/agent add claude` 添加 claude agent
|
|
118
|
+
- `/agent rm <name>` 删除 agent
|
|
119
|
+
|
|
120
|
+
说明:
|
|
121
|
+
|
|
122
|
+
- 内置 `codex` 和 `claude` 走 `acpx` 的 driver alias,通常不需要额外写 `agent.command`
|
|
123
|
+
- 如果你接入的是自定义 agent,再考虑显式配置 `agent.command`
|
|
124
|
+
|
|
125
|
+
### Workspace 工作目录
|
|
126
|
+
|
|
127
|
+
- `/workspaces` 或 `/workspace` 或 `/ws` 可查看当前已添加的工作目录
|
|
128
|
+
- `/ws new <name> -d <path>` 添加工作目录,`-d` 后面接的是目录在电脑的绝对路径
|
|
129
|
+
- `/workspace rm <name>` 删除工作目录
|
|
130
|
+
|
|
131
|
+
说明:
|
|
132
|
+
|
|
133
|
+
- `/ws new` 会先校验路径是否存在
|
|
134
|
+
- Windows 路径里如果有空格,请给 `-d` 或 `--cwd` 的值加引号,例如:`/ws new backend -d "E:\my projects\backend"`
|
|
135
|
+
|
|
136
|
+
### Session 会话
|
|
137
|
+
|
|
138
|
+
- `/sessions` 或 `/session` 或 `/ss` 可查看当前已添加的会话
|
|
139
|
+
- `/ss <agent> -d <path>` 新建会话,会自动按目录名推导并创建或复用 workspace,再创建或复用 session
|
|
140
|
+
- `/ss new <agent> -d <path>` 强制新建会话
|
|
141
|
+
- `/ss new <alias> -a <name> --ws <name>` 强制新建会话,并指定 agent 和 workspace
|
|
142
|
+
- `/ss attach <alias> -a <name> --ws <name> --name <transport-session>` 恢复已存在的会话
|
|
143
|
+
- `/use <alias>` 切换当前会话
|
|
144
|
+
- `/status` 查看当前会话状态
|
|
145
|
+
- `/cancel` 取消当前会话
|
|
146
|
+
- `/stop` 停止当前会话
|
|
147
|
+
|
|
148
|
+
说明:
|
|
149
|
+
|
|
150
|
+
- `/ss <agent> -d <path>` 是最常用入口,会自动按目录名推导并创建或复用 workspace,再创建或复用 session
|
|
151
|
+
- `/ss new <agent> -d <path>` 表示强制新建 session
|
|
152
|
+
- `/use <alias>` 用来切换当前会话
|
|
153
|
+
- 非 `/` 开头的文本会发送到当前 session
|
|
154
|
+
|
|
155
|
+
### 推荐工作流
|
|
156
|
+
|
|
157
|
+
新建一个可聊天的会话:
|
|
158
|
+
|
|
159
|
+
```text
|
|
160
|
+
/ss codex -d /absolute/path/to/backend
|
|
161
|
+
修一下最近这个接口超时问题
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
在同一个聊天里切换多个会话:
|
|
165
|
+
|
|
166
|
+
```text
|
|
167
|
+
/ss new codex -d /absolute/path/to/backend
|
|
168
|
+
/ss
|
|
169
|
+
/use backend:codex
|
|
170
|
+
看一下接口日志
|
|
171
|
+
/use backend:codex-2
|
|
172
|
+
看一下前端报错
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
删除不再需要的资源:
|
|
176
|
+
|
|
177
|
+
```text
|
|
178
|
+
/agent rm claude
|
|
179
|
+
/workspace rm old-repo
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## 配置与运行文件
|
|
183
|
+
|
|
184
|
+
默认文件位置:
|
|
185
|
+
|
|
186
|
+
- 配置文件:`~/.weacpx/config.json`
|
|
187
|
+
- 状态文件:`~/.weacpx/state.json`
|
|
188
|
+
- 运行日志:`~/.weacpx/runtime/app.log`
|
|
189
|
+
|
|
190
|
+
后台运行时还会使用:
|
|
191
|
+
|
|
192
|
+
- `~/.weacpx/runtime/daemon.pid`
|
|
193
|
+
- `~/.weacpx/runtime/status.json`
|
|
194
|
+
- `~/.weacpx/runtime/stdout.log`
|
|
195
|
+
- `~/.weacpx/runtime/stderr.log`
|
|
196
|
+
|
|
197
|
+
常用环境变量:
|
|
198
|
+
|
|
199
|
+
- `WEACPX_CONFIG`
|
|
200
|
+
- `WEACPX_STATE`
|
|
201
|
+
- `WEACPX_WEIXIN_SDK`
|
|
202
|
+
|
|
203
|
+
### 日志配置
|
|
204
|
+
|
|
205
|
+
`config.json` 支持可选的 `logging` 配置:
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"logging": {
|
|
210
|
+
"level": "info",
|
|
211
|
+
"maxSizeBytes": 2097152,
|
|
212
|
+
"maxFiles": 5,
|
|
213
|
+
"retentionDays": 7
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
说明:
|
|
219
|
+
|
|
220
|
+
- `level`: `error`、`info`、`debug`
|
|
221
|
+
- `maxSizeBytes`: 单个 `app.log` 文件达到上限后会轮转
|
|
222
|
+
- `maxFiles`: 最多保留多少个轮转文件
|
|
223
|
+
- `retentionDays`: 每次启动时会清理超过保留天数的旧轮转日志
|
|
224
|
+
|
|
225
|
+
## 注意事项
|
|
226
|
+
|
|
227
|
+
### `dry-run`
|
|
228
|
+
|
|
229
|
+
`dry-run` 会复用同一套 router、session service、transport,只是把微信消息换成终端输入,适合本地排查。
|
|
230
|
+
|
|
231
|
+
示例:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
bun run dry-run --chat-key wx:test -- \
|
|
235
|
+
"/agent add codex" \
|
|
236
|
+
"/ws new backend -d /absolute/path/to/backend" \
|
|
237
|
+
"/ss new demo -a codex --ws backend" \
|
|
238
|
+
"/status"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### 如果 `/ss new` 失败
|
|
242
|
+
|
|
243
|
+
当前最常见的问题仍然是底层 `acpx` named session 的运行时恢复,不一定是 `weacpx` 本身的逻辑问题。
|
|
244
|
+
|
|
245
|
+
可以先在本地创建一个 named session,再在微信里 attach:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
./node_modules/.bin/acpx --verbose --cwd /absolute/workspace/path codex sessions new --name existing-demo
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
然后在微信里:
|
|
252
|
+
|
|
253
|
+
```text
|
|
254
|
+
/ss attach demo -a codex --ws backend --name existing-demo
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## 更多文档
|
|
258
|
+
|
|
259
|
+
- 配置参考:[docs/config-reference.md](./docs/config-reference.md)
|
|
260
|
+
- 测试说明:[docs/testing.md](./docs/testing.md)
|
|
261
|
+
- 开发与贡献:[docs/development.md](./docs/development.md)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"transport": {
|
|
3
|
+
"type": "acpx-bridge",
|
|
4
|
+
"sessionInitTimeoutMs": 120000
|
|
5
|
+
},
|
|
6
|
+
"logging": {
|
|
7
|
+
"level": "info",
|
|
8
|
+
"maxSizeBytes": 2097152,
|
|
9
|
+
"maxFiles": 5,
|
|
10
|
+
"retentionDays": 7
|
|
11
|
+
},
|
|
12
|
+
"agents": {
|
|
13
|
+
"codex": {
|
|
14
|
+
"driver": "codex"
|
|
15
|
+
},
|
|
16
|
+
"claude": {
|
|
17
|
+
"driver": "claude"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"workspaces": {
|
|
21
|
+
"backend": {
|
|
22
|
+
"cwd": "/absolute/path/to/backend",
|
|
23
|
+
"description": "backend repo"
|
|
24
|
+
},
|
|
25
|
+
"weacpx-console": {
|
|
26
|
+
"cwd": "/Users/your-name/Projects/weacpx/.worktrees/weacpx-console-v1",
|
|
27
|
+
"description": "weacpx console worktree"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
19
|
+
var __export = (target, all) => {
|
|
20
|
+
for (var name in all)
|
|
21
|
+
__defProp(target, name, {
|
|
22
|
+
get: all[name],
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
set: (newValue) => all[name] = () => newValue
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
29
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
30
|
+
|
|
31
|
+
// src/bridge/bridge-main.ts
|
|
32
|
+
import { createInterface } from "node:readline";
|
|
33
|
+
|
|
34
|
+
// src/bridge/bridge-server.ts
|
|
35
|
+
class BridgeServer {
|
|
36
|
+
runtime;
|
|
37
|
+
constructor(runtime) {
|
|
38
|
+
this.runtime = runtime;
|
|
39
|
+
}
|
|
40
|
+
async handleLine(line) {
|
|
41
|
+
const request = JSON.parse(line);
|
|
42
|
+
try {
|
|
43
|
+
const result = await this.dispatch(request.method, request.params);
|
|
44
|
+
return `${JSON.stringify({
|
|
45
|
+
id: request.id,
|
|
46
|
+
ok: true,
|
|
47
|
+
result
|
|
48
|
+
})}
|
|
49
|
+
`;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
52
|
+
return `${JSON.stringify({
|
|
53
|
+
id: request.id,
|
|
54
|
+
ok: false,
|
|
55
|
+
error: {
|
|
56
|
+
code: "BRIDGE_INTERNAL_ERROR",
|
|
57
|
+
message
|
|
58
|
+
}
|
|
59
|
+
})}
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async dispatch(method, params) {
|
|
64
|
+
switch (method) {
|
|
65
|
+
case "ping":
|
|
66
|
+
return {};
|
|
67
|
+
case "shutdown":
|
|
68
|
+
return await this.runtime.shutdown();
|
|
69
|
+
case "hasSession":
|
|
70
|
+
return await this.runtime.hasSession({
|
|
71
|
+
agent: String(params.agent),
|
|
72
|
+
agentCommand: asOptionalString(params.agentCommand),
|
|
73
|
+
cwd: String(params.cwd),
|
|
74
|
+
name: String(params.name)
|
|
75
|
+
});
|
|
76
|
+
case "ensureSession":
|
|
77
|
+
return await this.runtime.ensureSession({
|
|
78
|
+
agent: String(params.agent),
|
|
79
|
+
agentCommand: asOptionalString(params.agentCommand),
|
|
80
|
+
cwd: String(params.cwd),
|
|
81
|
+
name: String(params.name)
|
|
82
|
+
});
|
|
83
|
+
case "prompt":
|
|
84
|
+
return await this.runtime.prompt({
|
|
85
|
+
agent: String(params.agent),
|
|
86
|
+
agentCommand: asOptionalString(params.agentCommand),
|
|
87
|
+
cwd: String(params.cwd),
|
|
88
|
+
name: String(params.name),
|
|
89
|
+
text: String(params.text)
|
|
90
|
+
});
|
|
91
|
+
case "cancel":
|
|
92
|
+
return await this.runtime.cancel({
|
|
93
|
+
agent: String(params.agent),
|
|
94
|
+
agentCommand: asOptionalString(params.agentCommand),
|
|
95
|
+
cwd: String(params.cwd),
|
|
96
|
+
name: String(params.name)
|
|
97
|
+
});
|
|
98
|
+
default:
|
|
99
|
+
throw new Error(`unsupported bridge method: ${method}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function asOptionalString(value) {
|
|
104
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/bridge/bridge-runtime.ts
|
|
111
|
+
import { spawn } from "node:child_process";
|
|
112
|
+
import { fileURLToPath } from "node:url";
|
|
113
|
+
|
|
114
|
+
class BridgeRuntime {
|
|
115
|
+
command;
|
|
116
|
+
run;
|
|
117
|
+
runSessionCreate;
|
|
118
|
+
constructor(command = "acpx", run = defaultRunner, runSessionCreate = shellSessionCreateRunner) {
|
|
119
|
+
this.command = command;
|
|
120
|
+
this.run = run;
|
|
121
|
+
this.runSessionCreate = runSessionCreate;
|
|
122
|
+
}
|
|
123
|
+
async hasSession(input) {
|
|
124
|
+
const result = await this.run(this.command, this.buildSessionArgs(input, [
|
|
125
|
+
"sessions",
|
|
126
|
+
"show",
|
|
127
|
+
input.name
|
|
128
|
+
]));
|
|
129
|
+
return { exists: result.code === 0 };
|
|
130
|
+
}
|
|
131
|
+
async ensureSession(input) {
|
|
132
|
+
const ensured = await this.run(this.command, this.buildSessionArgs(input, ["sessions", "ensure", "--name", input.name]));
|
|
133
|
+
if (ensured.code === 0) {
|
|
134
|
+
return {};
|
|
135
|
+
}
|
|
136
|
+
const existing = await this.run(this.command, this.buildSessionArgs(input, ["sessions", "show", input.name]));
|
|
137
|
+
if (existing.code === 0) {
|
|
138
|
+
return {};
|
|
139
|
+
}
|
|
140
|
+
const createdWithHelper = await this.runSessionCreate(this.command, this.buildSessionArgs(input, ["sessions", "new", "--name", input.name]), input.cwd);
|
|
141
|
+
if (createdWithHelper.code !== 0) {
|
|
142
|
+
throw new Error(createdWithHelper.stderr || createdWithHelper.stdout || ensured.stderr || ensured.stdout || "failed to create session");
|
|
143
|
+
}
|
|
144
|
+
return {};
|
|
145
|
+
}
|
|
146
|
+
async prompt(input) {
|
|
147
|
+
const result = await this.run(this.command, this.buildSessionArgs(input, [
|
|
148
|
+
"prompt",
|
|
149
|
+
"-s",
|
|
150
|
+
input.name,
|
|
151
|
+
input.text
|
|
152
|
+
]));
|
|
153
|
+
if (result.code !== 0) {
|
|
154
|
+
throw new Error(result.stderr || result.stdout || "prompt failed");
|
|
155
|
+
}
|
|
156
|
+
return { text: result.stdout.trim() };
|
|
157
|
+
}
|
|
158
|
+
async cancel(input) {
|
|
159
|
+
const result = await this.run(this.command, this.buildSessionArgs(input, [
|
|
160
|
+
"cancel",
|
|
161
|
+
"-s",
|
|
162
|
+
input.name
|
|
163
|
+
]));
|
|
164
|
+
if (result.code !== 0) {
|
|
165
|
+
throw new Error(result.stderr || result.stdout || "cancel failed");
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
cancelled: true,
|
|
169
|
+
message: result.stdout.trim()
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
async shutdown() {
|
|
173
|
+
return {};
|
|
174
|
+
}
|
|
175
|
+
buildSessionArgs(input, tail) {
|
|
176
|
+
if (input.agentCommand) {
|
|
177
|
+
return ["--format", "quiet", "--cwd", input.cwd, "--agent", input.agentCommand, ...tail];
|
|
178
|
+
}
|
|
179
|
+
return ["--format", "quiet", "--cwd", input.cwd, input.agent, ...tail];
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async function defaultRunner(command, args) {
|
|
183
|
+
return await new Promise((resolve, reject) => {
|
|
184
|
+
const child = spawn(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
185
|
+
let stdout = "";
|
|
186
|
+
let stderr = "";
|
|
187
|
+
child.stdout.on("data", (chunk) => {
|
|
188
|
+
stdout += String(chunk);
|
|
189
|
+
});
|
|
190
|
+
child.stderr.on("data", (chunk) => {
|
|
191
|
+
stderr += String(chunk);
|
|
192
|
+
});
|
|
193
|
+
child.on("error", reject);
|
|
194
|
+
child.on("close", (code) => {
|
|
195
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
async function shellSessionCreateRunner(command, args, cwd) {
|
|
200
|
+
const helperPath = fileURLToPath(new URL("../../scripts/acpx-session-new-helper.sh", import.meta.url));
|
|
201
|
+
return await new Promise((resolve, reject) => {
|
|
202
|
+
const child = spawn("/bin/zsh", [helperPath, command, cwd, ...args], {
|
|
203
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
204
|
+
});
|
|
205
|
+
let stdout = "";
|
|
206
|
+
let stderr = "";
|
|
207
|
+
child.stdout.on("data", (chunk) => {
|
|
208
|
+
stdout += String(chunk);
|
|
209
|
+
});
|
|
210
|
+
child.stderr.on("data", (chunk) => {
|
|
211
|
+
stderr += String(chunk);
|
|
212
|
+
});
|
|
213
|
+
child.on("error", reject);
|
|
214
|
+
child.on("close", (code) => {
|
|
215
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// src/bridge/bridge-main.ts
|
|
221
|
+
var server = new BridgeServer(new BridgeRuntime(process.env.WEACPX_BRIDGE_ACPX_COMMAND ?? "acpx"));
|
|
222
|
+
var input = createInterface({
|
|
223
|
+
input: process.stdin,
|
|
224
|
+
crlfDelay: Infinity
|
|
225
|
+
});
|
|
226
|
+
for await (const line of input) {
|
|
227
|
+
const response = await server.handleLine(line);
|
|
228
|
+
process.stdout.write(response);
|
|
229
|
+
}
|