weacpx 0.1.1 → 0.1.2
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 +70 -59
- package/dist/bridge/bridge-main.js +33 -6
- package/dist/cli.js +41 -48
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,31 +1,17 @@
|
|
|
1
1
|
# weacpx
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
- 你希望在手机上完成常见的会话切换、目录切换和对话操作
|
|
3
|
+
使用微信 ClawBot 随时随地通过 `acpx` 控制 Claude Code、Codex 等 Agents。
|
|
18
4
|
|
|
19
5
|
## 安装前准备
|
|
20
6
|
|
|
21
7
|
开始前,至少需要:
|
|
22
8
|
|
|
23
|
-
- Node.js 22+
|
|
24
|
-
- Bun
|
|
9
|
+
- Node.js 22+ 或 Bun
|
|
25
10
|
- 一个可用的微信登录环境
|
|
26
|
-
-
|
|
11
|
+
- Claude Code 或 Codex
|
|
27
12
|
|
|
28
|
-
|
|
13
|
+
> `weacpx` 基于 `weixin-agent-sdk` 与 `acpx` 实现。
|
|
14
|
+
> 正常情况下,不需要再额外全局安装 `acpx`。
|
|
29
15
|
|
|
30
16
|
## 安装
|
|
31
17
|
|
|
@@ -34,17 +20,10 @@
|
|
|
34
20
|
```bash
|
|
35
21
|
# 使用 NPM 全局安装
|
|
36
22
|
npm install -g weacpx
|
|
37
|
-
# 或使用
|
|
23
|
+
# 或使用 Bun 全局安装
|
|
38
24
|
bun add -g weacpx
|
|
39
25
|
```
|
|
40
26
|
|
|
41
|
-
如果你是从源码仓库直接使用,请先安装依赖并构建:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
bun install
|
|
45
|
-
bun run dev
|
|
46
|
-
```
|
|
47
|
-
|
|
48
27
|
## 快速开始
|
|
49
28
|
|
|
50
29
|
第一次使用建议按这个顺序:
|
|
@@ -58,19 +37,9 @@ bun run dev
|
|
|
58
37
|
```bash
|
|
59
38
|
weacpx login
|
|
60
39
|
weacpx start
|
|
61
|
-
weacpx status
|
|
62
40
|
```
|
|
63
41
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
bun run login
|
|
68
|
-
bun run dev
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
`weacpx login` 和 `bun run login` 都会在终端里显示二维码。
|
|
72
|
-
|
|
73
|
-
启动后,在微信里先发:
|
|
42
|
+
`weacpx login` 会在终端里显示二维码,使用微信扫描登录。`weacpx start` 启动后,在微信里发:
|
|
74
43
|
|
|
75
44
|
```text
|
|
76
45
|
/ss codex -d /absolute/path/to/your/repo
|
|
@@ -78,8 +47,9 @@ bun run dev
|
|
|
78
47
|
/help
|
|
79
48
|
```
|
|
80
49
|
|
|
81
|
-
|
|
82
|
-
|
|
50
|
+
`/ss codex -d /absolute/path/to/your/repo`:开启或挂在一个会话,并切换到该会话。使用 Codex,并指定工作目录为 `/absolute/path/to/your/repo`。
|
|
51
|
+
|
|
52
|
+
`/help` 查看帮助信息。
|
|
83
53
|
|
|
84
54
|
然后就可以直接发普通消息,例如:
|
|
85
55
|
|
|
@@ -87,6 +57,17 @@ bun run dev
|
|
|
87
57
|
hello
|
|
88
58
|
```
|
|
89
59
|
|
|
60
|
+
如果你是从源码仓库直接使用:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# 先安装依赖
|
|
64
|
+
bun install
|
|
65
|
+
# 登录微信
|
|
66
|
+
bun run login
|
|
67
|
+
# 启动服务
|
|
68
|
+
bun run dev
|
|
69
|
+
```
|
|
70
|
+
|
|
90
71
|
普通文本会默认发送到当前选中的 session。
|
|
91
72
|
|
|
92
73
|
## CLI 命令
|
|
@@ -106,27 +87,55 @@ hello
|
|
|
106
87
|
- `status` 查看后台状态、PID、配置路径和日志路径
|
|
107
88
|
- `stop` 停止后台实例
|
|
108
89
|
|
|
109
|
-
##
|
|
90
|
+
## 微信中使用说明
|
|
110
91
|
|
|
111
|
-
### Agent
|
|
92
|
+
### 管理 Agent
|
|
112
93
|
|
|
113
|
-
内置 `codex` 与 `claude`
|
|
94
|
+
内置 `codex` 与 `claude` 两个常见 agent,也支持添加你自己的 agents。
|
|
114
95
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
96
|
+
| 命令 | 说明 |
|
|
97
|
+
|------|------|
|
|
98
|
+
| `/agents` | 查看当前已添加的 agent |
|
|
99
|
+
| `/agent add codex` | 添加 codex agent |
|
|
100
|
+
| `/agent add claude` | 添加 claude agent |
|
|
101
|
+
| `/agent rm <name>` | 删除 agent |
|
|
119
102
|
|
|
120
103
|
说明:
|
|
121
104
|
|
|
122
105
|
- 内置 `codex` 和 `claude` 走 `acpx` 的 driver alias,通常不需要额外写 `agent.command`
|
|
123
106
|
- 如果你接入的是自定义 agent,再考虑显式配置 `agent.command`
|
|
124
107
|
|
|
108
|
+
`config.json` 中的 `agent.command` 用于显式指定 agent 的原始启动命令,完整字段如下:
|
|
109
|
+
|
|
110
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
111
|
+
|------|------|------|------|
|
|
112
|
+
| `driver` | `string` | 是 | agent 驱动类型,传递给 acpx 的第一位置参数 |
|
|
113
|
+
| `command` | `string` | 否 | 显式指定自定义 agent 的原始命令。不填则使用 acpx 默认行为 |
|
|
114
|
+
|
|
115
|
+
示例 — 配置一个自定义 agent:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"agents": {
|
|
120
|
+
"my-agent": {
|
|
121
|
+
"driver": "codex",
|
|
122
|
+
"command": "/path/to/acpx codex --arg1 value1"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- 内置 `codex` 和 `claude` 建议只写 `driver`,让 `acpx` 自己解析对应 alias
|
|
129
|
+
- `command` 主要用于自定义 agent,不建议给内置 driver 手写原始命令
|
|
130
|
+
- 旧版 `codex` raw command 配置会被自动忽略,回退为 `acpx codex ...`
|
|
131
|
+
|
|
125
132
|
### Workspace 工作目录
|
|
126
133
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
134
|
+
| 命令 | 说明 |
|
|
135
|
+
|------|------|
|
|
136
|
+
| `/workspaces` / `/workspace` / `/ws` | 查看当前已添加的工作目录 |
|
|
137
|
+
| `/ws new <name> -d <path>` | 添加工作目录,`-d` 后面接的是目录在电脑的绝对路径 |
|
|
138
|
+
| `/workspace rm <name>` | 删除工作目录 |
|
|
130
139
|
|
|
131
140
|
说明:
|
|
132
141
|
|
|
@@ -135,15 +144,17 @@ hello
|
|
|
135
144
|
|
|
136
145
|
### Session 会话
|
|
137
146
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
| 命令 | 说明 |
|
|
148
|
+
|------|------|
|
|
149
|
+
| `/sessions` / `/session` / `/ss` | 查看当前已添加的会话 |
|
|
150
|
+
| `/ss <agent> -d <path>` | 新建会话(自动按目录名推导并创建或复用 workspace,再创建或复用 session) |
|
|
151
|
+
| `/ss new <agent> -d <path>` | 强制新建会话 |
|
|
152
|
+
| `/ss new <alias> -a <name> --ws <name>` | 强制新建会话,并指定 agent 和 workspace |
|
|
153
|
+
| `/ss attach <alias> -a <name> --ws <name> --name <transport-session>` | 恢复已存在的会话 |
|
|
154
|
+
| `/use <alias>` | 切换当前会话 |
|
|
155
|
+
| `/status` | 查看当前会话状态 |
|
|
156
|
+
| `/cancel` | 取消当前会话 |
|
|
157
|
+
| `/stop` | 停止当前会话 |
|
|
147
158
|
|
|
148
159
|
说明:
|
|
149
160
|
|
|
@@ -28,6 +28,21 @@ 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
|
+
|
|
31
46
|
// src/bridge/bridge-main.ts
|
|
32
47
|
import { createInterface } from "node:readline";
|
|
33
48
|
|
|
@@ -108,6 +123,7 @@ function asOptionalString(value) {
|
|
|
108
123
|
}
|
|
109
124
|
|
|
110
125
|
// src/bridge/bridge-runtime.ts
|
|
126
|
+
init_spawn_command();
|
|
111
127
|
import { spawn } from "node:child_process";
|
|
112
128
|
import { fileURLToPath } from "node:url";
|
|
113
129
|
|
|
@@ -121,46 +137,57 @@ class BridgeRuntime {
|
|
|
121
137
|
this.runSessionCreate = runSessionCreate;
|
|
122
138
|
}
|
|
123
139
|
async hasSession(input) {
|
|
124
|
-
const
|
|
140
|
+
const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
|
|
125
141
|
"sessions",
|
|
126
142
|
"show",
|
|
127
143
|
input.name
|
|
128
144
|
]));
|
|
145
|
+
const result = await this.run(spawnSpec.command, spawnSpec.args);
|
|
129
146
|
return { exists: result.code === 0 };
|
|
130
147
|
}
|
|
131
148
|
async ensureSession(input) {
|
|
132
|
-
const
|
|
149
|
+
const ensuredSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
|
|
150
|
+
"sessions",
|
|
151
|
+
"ensure",
|
|
152
|
+
"--name",
|
|
153
|
+
input.name
|
|
154
|
+
]));
|
|
155
|
+
const ensured = await this.run(ensuredSpec.command, ensuredSpec.args);
|
|
133
156
|
if (ensured.code === 0) {
|
|
134
157
|
return {};
|
|
135
158
|
}
|
|
136
|
-
const
|
|
159
|
+
const existingSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, ["sessions", "show", input.name]));
|
|
160
|
+
const existing = await this.run(existingSpec.command, existingSpec.args);
|
|
137
161
|
if (existing.code === 0) {
|
|
138
162
|
return {};
|
|
139
163
|
}
|
|
140
|
-
const
|
|
164
|
+
const createSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, ["sessions", "new", "--name", input.name]));
|
|
165
|
+
const createdWithHelper = await this.runSessionCreate(createSpec.command, createSpec.args, input.cwd);
|
|
141
166
|
if (createdWithHelper.code !== 0) {
|
|
142
167
|
throw new Error(createdWithHelper.stderr || createdWithHelper.stdout || ensured.stderr || ensured.stdout || "failed to create session");
|
|
143
168
|
}
|
|
144
169
|
return {};
|
|
145
170
|
}
|
|
146
171
|
async prompt(input) {
|
|
147
|
-
const
|
|
172
|
+
const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
|
|
148
173
|
"prompt",
|
|
149
174
|
"-s",
|
|
150
175
|
input.name,
|
|
151
176
|
input.text
|
|
152
177
|
]));
|
|
178
|
+
const result = await this.run(spawnSpec.command, spawnSpec.args);
|
|
153
179
|
if (result.code !== 0) {
|
|
154
180
|
throw new Error(result.stderr || result.stdout || "prompt failed");
|
|
155
181
|
}
|
|
156
182
|
return { text: result.stdout.trim() };
|
|
157
183
|
}
|
|
158
184
|
async cancel(input) {
|
|
159
|
-
const
|
|
185
|
+
const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
|
|
160
186
|
"cancel",
|
|
161
187
|
"-s",
|
|
162
188
|
input.name
|
|
163
189
|
]));
|
|
190
|
+
const result = await this.run(spawnSpec.command, spawnSpec.args);
|
|
164
191
|
if (result.code !== 0) {
|
|
165
192
|
throw new Error(result.stderr || result.stdout || "cancel failed");
|
|
166
193
|
}
|
package/dist/cli.js
CHANGED
|
@@ -2108,7 +2108,7 @@ var init_ensure_config = __esm(() => {
|
|
|
2108
2108
|
|
|
2109
2109
|
// src/config/resolve-acpx-command.ts
|
|
2110
2110
|
import { readFileSync } from "node:fs";
|
|
2111
|
-
import {
|
|
2111
|
+
import { posix, win32 } from "node:path";
|
|
2112
2112
|
import { createRequire as createRequire2 } from "node:module";
|
|
2113
2113
|
function resolveAcpxCommand(options = {}) {
|
|
2114
2114
|
if (options.configuredCommand) {
|
|
@@ -2120,12 +2120,11 @@ function resolveAcpxCommand(options = {}) {
|
|
|
2120
2120
|
try {
|
|
2121
2121
|
const packageJsonPath = resolvePackageJson("acpx/package.json");
|
|
2122
2122
|
const pkg = readPackageJson(packageJsonPath);
|
|
2123
|
+
const pathApi = platform === "win32" ? win32 : posix;
|
|
2124
|
+
const packageDir = pathApi.dirname(packageJsonPath);
|
|
2123
2125
|
const binPath = typeof pkg.bin === "string" ? pkg.bin : pkg.bin && typeof pkg.bin.acpx === "string" ? pkg.bin.acpx : null;
|
|
2124
2126
|
if (binPath) {
|
|
2125
|
-
|
|
2126
|
-
return resolve(dirname6(packageJsonPath), "../.bin/acpx.exe");
|
|
2127
|
-
}
|
|
2128
|
-
return resolve(dirname6(packageJsonPath), binPath);
|
|
2127
|
+
return pathApi.resolve(packageDir, binPath);
|
|
2129
2128
|
}
|
|
2130
2129
|
} catch {}
|
|
2131
2130
|
return "acpx";
|
|
@@ -2275,7 +2274,7 @@ function createEmptyState() {
|
|
|
2275
2274
|
|
|
2276
2275
|
// src/state/state-store.ts
|
|
2277
2276
|
import { mkdir as mkdir7, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
|
|
2278
|
-
import { dirname as
|
|
2277
|
+
import { dirname as dirname6 } from "node:path";
|
|
2279
2278
|
|
|
2280
2279
|
class StateStore {
|
|
2281
2280
|
path;
|
|
@@ -2297,7 +2296,7 @@ class StateStore {
|
|
|
2297
2296
|
}
|
|
2298
2297
|
}
|
|
2299
2298
|
async save(state) {
|
|
2300
|
-
await mkdir7(
|
|
2299
|
+
await mkdir7(dirname6(this.path), { recursive: true });
|
|
2301
2300
|
await writeFile5(this.path, JSON.stringify(state, null, 2));
|
|
2302
2301
|
}
|
|
2303
2302
|
}
|
|
@@ -2355,8 +2354,8 @@ class AcpxBridgeClient {
|
|
|
2355
2354
|
request(method, params) {
|
|
2356
2355
|
const id = String(this.nextId);
|
|
2357
2356
|
this.nextId += 1;
|
|
2358
|
-
return awaitable((
|
|
2359
|
-
this.pending.set(id, { resolve
|
|
2357
|
+
return awaitable((resolve, reject) => {
|
|
2358
|
+
this.pending.set(id, { resolve, reject });
|
|
2360
2359
|
this.writeLine(encodeBridgeRequest({
|
|
2361
2360
|
id,
|
|
2362
2361
|
method,
|
|
@@ -2432,8 +2431,8 @@ async function spawnAcpxBridgeClient(options = {}) {
|
|
|
2432
2431
|
return client;
|
|
2433
2432
|
}
|
|
2434
2433
|
function awaitable(executor) {
|
|
2435
|
-
return new Promise((
|
|
2436
|
-
executor(
|
|
2434
|
+
return new Promise((resolve, reject) => {
|
|
2435
|
+
executor(resolve, reject);
|
|
2437
2436
|
});
|
|
2438
2437
|
}
|
|
2439
2438
|
var init_acpx_bridge_client = () => {};
|
|
@@ -2476,14 +2475,29 @@ class AcpxBridgeTransport {
|
|
|
2476
2475
|
}
|
|
2477
2476
|
}
|
|
2478
2477
|
|
|
2478
|
+
// src/process/spawn-command.ts
|
|
2479
|
+
function resolveSpawnCommand(command, args) {
|
|
2480
|
+
if (SCRIPT_FILE_PATTERN.test(command)) {
|
|
2481
|
+
return {
|
|
2482
|
+
command: process.execPath,
|
|
2483
|
+
args: [command, ...args]
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2486
|
+
return { command, args };
|
|
2487
|
+
}
|
|
2488
|
+
var SCRIPT_FILE_PATTERN;
|
|
2489
|
+
var init_spawn_command = __esm(() => {
|
|
2490
|
+
SCRIPT_FILE_PATTERN = /\.(c|m)?js$/i;
|
|
2491
|
+
});
|
|
2492
|
+
|
|
2479
2493
|
// src/transport/acpx-cli/node-pty-helper.ts
|
|
2480
2494
|
import { chmod as chmodFs } from "node:fs/promises";
|
|
2481
|
-
import { dirname as
|
|
2495
|
+
import { dirname as dirname7, join as join3 } from "node:path";
|
|
2482
2496
|
function resolveNodePtyHelperPath(packageJsonPath, platform, arch) {
|
|
2483
2497
|
if (platform === "win32") {
|
|
2484
2498
|
return null;
|
|
2485
2499
|
}
|
|
2486
|
-
return join3(
|
|
2500
|
+
return join3(dirname7(packageJsonPath), "prebuilds", `${platform}-${arch}`, "spawn-helper");
|
|
2487
2501
|
}
|
|
2488
2502
|
async function ensureNodePtyHelperExecutable(helperPath, chmod = chmodFs) {
|
|
2489
2503
|
if (!helperPath) {
|
|
@@ -2505,8 +2519,9 @@ import { createRequire as createRequire3 } from "node:module";
|
|
|
2505
2519
|
import { spawn as spawn3 } from "node:child_process";
|
|
2506
2520
|
import { spawn as spawnPty } from "node-pty";
|
|
2507
2521
|
async function defaultRunner(command, args, options) {
|
|
2508
|
-
return await new Promise((
|
|
2509
|
-
const
|
|
2522
|
+
return await new Promise((resolve, reject) => {
|
|
2523
|
+
const spawnSpec = resolveSpawnCommand(command, args);
|
|
2524
|
+
const child = spawn3(spawnSpec.command, spawnSpec.args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
2510
2525
|
let stdout = "";
|
|
2511
2526
|
let stderr = "";
|
|
2512
2527
|
const timeoutId = options?.timeoutMs ? setTimeout(() => {
|
|
@@ -2527,15 +2542,16 @@ async function defaultRunner(command, args, options) {
|
|
|
2527
2542
|
child.on("close", (code) => {
|
|
2528
2543
|
if (timeoutId)
|
|
2529
2544
|
clearTimeout(timeoutId);
|
|
2530
|
-
|
|
2545
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
2531
2546
|
});
|
|
2532
2547
|
});
|
|
2533
2548
|
}
|
|
2534
2549
|
async function defaultPtyRunner(command, args, options) {
|
|
2535
2550
|
const helperPath = resolveNodePtyHelperPath(require3.resolve("node-pty/package.json"), process.platform, process.arch);
|
|
2536
2551
|
await ensureNodePtyHelperExecutable(helperPath);
|
|
2537
|
-
return await new Promise((
|
|
2538
|
-
const
|
|
2552
|
+
return await new Promise((resolve, reject) => {
|
|
2553
|
+
const spawnSpec = resolveSpawnCommand(command, args);
|
|
2554
|
+
const child = spawnPty(spawnSpec.command, spawnSpec.args, {
|
|
2539
2555
|
name: "xterm-color",
|
|
2540
2556
|
cols: 80,
|
|
2541
2557
|
rows: 24,
|
|
@@ -2553,7 +2569,7 @@ async function defaultPtyRunner(command, args, options) {
|
|
|
2553
2569
|
child.onExit(({ exitCode }) => {
|
|
2554
2570
|
if (timeoutId)
|
|
2555
2571
|
clearTimeout(timeoutId);
|
|
2556
|
-
|
|
2572
|
+
resolve({ code: exitCode, stdout: output, stderr: "" });
|
|
2557
2573
|
});
|
|
2558
2574
|
});
|
|
2559
2575
|
}
|
|
@@ -2624,12 +2640,13 @@ class AcpxCliTransport {
|
|
|
2624
2640
|
return result.stdout;
|
|
2625
2641
|
}
|
|
2626
2642
|
async runCommandWithTimeout(runner, args, options) {
|
|
2643
|
+
const spawnSpec = resolveSpawnCommand(this.command, args);
|
|
2627
2644
|
if (!options?.timeoutMs) {
|
|
2628
|
-
return await runner(
|
|
2645
|
+
return await runner(spawnSpec.command, spawnSpec.args, undefined);
|
|
2629
2646
|
}
|
|
2630
2647
|
let timeoutId;
|
|
2631
2648
|
return await Promise.race([
|
|
2632
|
-
runner(
|
|
2649
|
+
runner(spawnSpec.command, spawnSpec.args, options).finally(() => {
|
|
2633
2650
|
if (timeoutId)
|
|
2634
2651
|
clearTimeout(timeoutId);
|
|
2635
2652
|
}),
|
|
@@ -2754,6 +2771,7 @@ function extractJsonRpcErrorMessages(output) {
|
|
|
2754
2771
|
}
|
|
2755
2772
|
var require3;
|
|
2756
2773
|
var init_acpx_cli_transport = __esm(() => {
|
|
2774
|
+
init_spawn_command();
|
|
2757
2775
|
init_node_pty_helper();
|
|
2758
2776
|
require3 = createRequire3(import.meta.url);
|
|
2759
2777
|
});
|
|
@@ -2766,7 +2784,7 @@ __export(exports_main, {
|
|
|
2766
2784
|
buildApp: () => buildApp
|
|
2767
2785
|
});
|
|
2768
2786
|
import { homedir } from "node:os";
|
|
2769
|
-
import { dirname as
|
|
2787
|
+
import { dirname as dirname8, join as join4 } from "node:path";
|
|
2770
2788
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2771
2789
|
async function buildApp(paths, deps = {}) {
|
|
2772
2790
|
await ensureConfigExists(paths.configPath);
|
|
@@ -2842,7 +2860,7 @@ function resolveBridgeEntryPath() {
|
|
|
2842
2860
|
return fileURLToPath2(new URL("./bridge/bridge-main.ts", import.meta.url));
|
|
2843
2861
|
}
|
|
2844
2862
|
function resolveAppLogPath(configPath) {
|
|
2845
|
-
const rootDir =
|
|
2863
|
+
const rootDir = dirname8(configPath);
|
|
2846
2864
|
const runtimeDir = join4(rootDir, "runtime");
|
|
2847
2865
|
return join4(runtimeDir, "app.log");
|
|
2848
2866
|
}
|
|
@@ -2911,18 +2929,10 @@ class DaemonController {
|
|
|
2911
2929
|
paths;
|
|
2912
2930
|
deps;
|
|
2913
2931
|
statusStore;
|
|
2914
|
-
startupPollIntervalMs;
|
|
2915
|
-
startupTimeoutMs;
|
|
2916
|
-
onStartupPoll;
|
|
2917
2932
|
constructor(paths, deps) {
|
|
2918
2933
|
this.paths = paths;
|
|
2919
2934
|
this.deps = deps;
|
|
2920
2935
|
this.statusStore = new DaemonStatusStore(paths.statusFile);
|
|
2921
|
-
this.startupPollIntervalMs = deps.startupPollIntervalMs ?? 50;
|
|
2922
|
-
this.startupTimeoutMs = deps.startupTimeoutMs ?? 5000;
|
|
2923
|
-
this.onStartupPoll = deps.onStartupPoll ?? (async () => {
|
|
2924
|
-
await new Promise((resolve) => setTimeout(resolve, this.startupPollIntervalMs));
|
|
2925
|
-
});
|
|
2926
2936
|
}
|
|
2927
2937
|
async getStatus() {
|
|
2928
2938
|
const pid = await this.loadPid();
|
|
@@ -2948,10 +2958,8 @@ class DaemonController {
|
|
|
2948
2958
|
if (current.state === "running") {
|
|
2949
2959
|
return { state: "already-running", pid: current.pid };
|
|
2950
2960
|
}
|
|
2951
|
-
await this.statusStore.clear();
|
|
2952
2961
|
const pid = await this.deps.spawnDetached();
|
|
2953
2962
|
await this.writePid(pid);
|
|
2954
|
-
await this.waitForStartupMetadata(pid);
|
|
2955
2963
|
return { state: "started", pid };
|
|
2956
2964
|
}
|
|
2957
2965
|
async stop() {
|
|
@@ -2986,21 +2994,6 @@ class DaemonController {
|
|
|
2986
2994
|
await rm2(this.paths.pidFile, { force: true });
|
|
2987
2995
|
await this.statusStore.clear();
|
|
2988
2996
|
}
|
|
2989
|
-
async waitForStartupMetadata(pid) {
|
|
2990
|
-
const deadline = Date.now() + this.startupTimeoutMs;
|
|
2991
|
-
while (Date.now() < deadline) {
|
|
2992
|
-
const status = await this.statusStore.load();
|
|
2993
|
-
if (status?.pid === pid) {
|
|
2994
|
-
return;
|
|
2995
|
-
}
|
|
2996
|
-
if (!this.deps.isProcessRunning(pid)) {
|
|
2997
|
-
await this.clearRuntimeFiles();
|
|
2998
|
-
throw new Error(`weacpx daemon exited before reporting ready state (pid ${pid})`);
|
|
2999
|
-
}
|
|
3000
|
-
await this.onStartupPoll();
|
|
3001
|
-
}
|
|
3002
|
-
throw new Error(`weacpx daemon did not report ready state within ${this.startupTimeoutMs}ms (pid ${pid})`);
|
|
3003
|
-
}
|
|
3004
2997
|
}
|
|
3005
2998
|
|
|
3006
2999
|
// src/daemon/create-daemon-controller.ts
|