autowonder 0.2.115 → 0.2.116
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 +6 -6
- package/bin/cli.js +101 -47
- package/package.json +1 -1
- package/scripts/build-and-publish.sh +9 -5
- package/vendor/autowonder-daemon-darwin-amd64 +0 -0
- package/vendor/autowonder-daemon-darwin-arm64 +0 -0
- package/vendor/autowonder-daemon-linux-amd64 +0 -0
- package/vendor/autowonder-daemon-linux-arm64 +0 -0
- package/vendor/autowonder-daemon-win32-amd64.exe +0 -0
- package/vendor/autowonder-daemon-win32-arm64.exe +0 -0
package/README.md
CHANGED
|
@@ -6,13 +6,13 @@ AutoWonder 本地 agent runtime。安装后启动 daemon,持续轮询本地 as
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# Qoder CLI(模型使用 provider model ID;Context Window 使用固定档位)
|
|
9
|
-
npx -y autowonder@
|
|
9
|
+
npx -y autowonder@0.2.116 connect --ws-url <wss-endpoint> --token <executor-token> --executor-id <executor-id> --provider qoder --model qmodel_latest --reasoning-effort medium --context-window 260000
|
|
10
10
|
|
|
11
11
|
# Claude Code
|
|
12
|
-
npx -y autowonder@0.2.
|
|
12
|
+
npx -y autowonder@0.2.116 connect --ws-url <wss-endpoint> --token <executor-token> --executor-id <executor-id> --provider claude
|
|
13
13
|
|
|
14
14
|
# Codex CLI
|
|
15
|
-
npx -y autowonder@0.2.
|
|
15
|
+
npx -y autowonder@0.2.116 connect --ws-url <wss-endpoint> --token <executor-token> --executor-id <executor-id> --provider codex --model gpt-5.5 --reasoning-effort medium
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
`connect` 会安装当前 npm 包内置的 daemon,并使用页面生成的 WebSocket endpoint、Token 和执行器 ID 建立连接。选择 Qoder 时需要 Node.js 20+;runtime 会在缺少 `qodercli` 时自动通过 npm 安装,并在尚未登录时打开 Qoder 浏览器登录。Claude Code 和 Codex CLI 仍需提前安装并登录。runtime 会继承当前用户的 HOME、环境变量和 CLI 登录状态。
|
|
@@ -57,14 +57,14 @@ mv "$queue/.assignment.tmp" "$queue/assignment.json"
|
|
|
57
57
|
也可以直接提交到本地 API:
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
npx -y autowonder@0.2.
|
|
60
|
+
npx -y autowonder@0.2.116 dispatch ./assignment.json
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
## 管理 daemon
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
|
-
npx -y autowonder@0.2.
|
|
67
|
-
npx -y autowonder@0.2.
|
|
66
|
+
npx -y autowonder@0.2.116 status
|
|
67
|
+
npx -y autowonder@0.2.116 stop
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
默认 API 是 `http://127.0.0.1:34989`,日志位于 `~/.autowonder/daemon.log`。npm 包不包含任何 agent、MCP 或服务端凭证。
|
package/bin/cli.js
CHANGED
|
@@ -12,7 +12,10 @@ const http = require("http");
|
|
|
12
12
|
const VERSION = require("../package.json").version;
|
|
13
13
|
const AUTOWONDER_HOME = path.join(os.homedir(), ".autowonder");
|
|
14
14
|
const BIN_DIR = path.join(AUTOWONDER_HOME, "bin");
|
|
15
|
-
|
|
15
|
+
function daemonBinPath() {
|
|
16
|
+
return path.join(BIN_DIR, process.platform === "win32" ? "autowonder-daemon.exe" : "autowonder-daemon");
|
|
17
|
+
}
|
|
18
|
+
const DAEMON_BIN = daemonBinPath();
|
|
16
19
|
const CONFIG_PATH = path.join(AUTOWONDER_HOME, "config.json");
|
|
17
20
|
const PID_PATH = path.join(AUTOWONDER_HOME, "daemon.pid");
|
|
18
21
|
const STATE_PATH = path.join(AUTOWONDER_HOME, "daemon-state.json");
|
|
@@ -23,7 +26,12 @@ const BINARY_BASE_URL = process.env.AUTOWONDER_BINARY_URL || "";
|
|
|
23
26
|
const SOURCE_REPO = process.env.AUTOWONDER_SOURCE_REPO || "";
|
|
24
27
|
const BUNDLED_BIN_DIR = path.join(__dirname, "..", "vendor");
|
|
25
28
|
|
|
26
|
-
const DEFAULT_API_ADDR = "127.0.0.1:34989";
|
|
29
|
+
const DEFAULT_API_ADDR = process.env.AUTOWONDER_API_ADDR || "127.0.0.1:34989";
|
|
30
|
+
const IS_WIN32 = process.platform === "win32";
|
|
31
|
+
|
|
32
|
+
function safeChmod(filePath, mode) {
|
|
33
|
+
try { fs.chmodSync(filePath, mode); } catch {}
|
|
34
|
+
}
|
|
27
35
|
|
|
28
36
|
// ─── Helpers ─────────────────────────────────────────────────────────
|
|
29
37
|
|
|
@@ -104,7 +112,14 @@ function qoderNodeVersion() {
|
|
|
104
112
|
return process.versions.node;
|
|
105
113
|
}
|
|
106
114
|
|
|
107
|
-
const
|
|
115
|
+
const QODER_AUTH_RECHECK_TIMEOUT_MS = 3 * 60 * 1000;
|
|
116
|
+
|
|
117
|
+
function qoderAuthRecheckTimeoutMs() {
|
|
118
|
+
if (process.env.NODE_ENV === "test" && process.env.AUTOWONDER_TEST_QODER_AUTH_RECHECK_TIMEOUT_MS) {
|
|
119
|
+
return Number.parseInt(process.env.AUTOWONDER_TEST_QODER_AUTH_RECHECK_TIMEOUT_MS, 10) || 1;
|
|
120
|
+
}
|
|
121
|
+
return QODER_AUTH_RECHECK_TIMEOUT_MS;
|
|
122
|
+
}
|
|
108
123
|
|
|
109
124
|
function qoderAuthRecheckDelayMs() {
|
|
110
125
|
if (process.env.NODE_ENV === "test" && process.env.AUTOWONDER_TEST_QODER_AUTH_RECHECK_DELAY_MS) {
|
|
@@ -116,14 +131,19 @@ function qoderAuthRecheckDelayMs() {
|
|
|
116
131
|
// Credentials written by `qodercli login` may not be readable immediately,
|
|
117
132
|
// so the post-login authentication check retries before declaring failure.
|
|
118
133
|
async function qoderAuthenticatedAfterLogin(executable) {
|
|
119
|
-
|
|
134
|
+
const deadline = Date.now() + qoderAuthRecheckTimeoutMs();
|
|
135
|
+
while (true) {
|
|
120
136
|
if (qoderAuthenticated(executable)) return true;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
137
|
+
const remainingMs = deadline - Date.now();
|
|
138
|
+
if (remainingMs <= 0) return false;
|
|
139
|
+
log("Waiting for Qoder login to finish...");
|
|
140
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(qoderAuthRecheckDelayMs(), remainingMs)));
|
|
125
141
|
}
|
|
126
|
-
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function qoderLoginCommand(executable) {
|
|
145
|
+
const quoted = `'${executable.replace(/'/g, "''")}'`;
|
|
146
|
+
return IS_WIN32 ? `& ${quoted} login` : `${quoted} login`;
|
|
127
147
|
}
|
|
128
148
|
|
|
129
149
|
async function ensureQoderReady() {
|
|
@@ -141,7 +161,7 @@ async function ensureQoderReady() {
|
|
|
141
161
|
log("Qoder login is required. Opening browser login...");
|
|
142
162
|
const login = spawnSync(executable, ["login"], { stdio: "inherit" });
|
|
143
163
|
if (login.status !== 0 || !(await qoderAuthenticatedAfterLogin(executable))) {
|
|
144
|
-
error(
|
|
164
|
+
error(`Qoder login did not complete. Run ${qoderLoginCommand(executable)} and retry.`);
|
|
145
165
|
return false;
|
|
146
166
|
}
|
|
147
167
|
}
|
|
@@ -193,6 +213,12 @@ function processRunning(pid) {
|
|
|
193
213
|
}
|
|
194
214
|
|
|
195
215
|
function processFingerprint(pid) {
|
|
216
|
+
if (IS_WIN32) {
|
|
217
|
+
const result = spawnSync("wmic", [
|
|
218
|
+
"process", "where", `ProcessId=${pid}`, "get", "CreationDate,CommandLine", "/format:list",
|
|
219
|
+
], { encoding: "utf8", timeout: 3000 });
|
|
220
|
+
return result.status === 0 ? (result.stdout || "").trim() : "";
|
|
221
|
+
}
|
|
196
222
|
const result = spawnSync("ps", ["-p", String(pid), "-o", "lstart=", "-o", "command="], {
|
|
197
223
|
encoding: "utf8",
|
|
198
224
|
timeout: 2000,
|
|
@@ -201,6 +227,15 @@ function processFingerprint(pid) {
|
|
|
201
227
|
}
|
|
202
228
|
|
|
203
229
|
function legacyDaemonProcess(pid) {
|
|
230
|
+
if (IS_WIN32) {
|
|
231
|
+
const result = spawnSync("wmic", [
|
|
232
|
+
"process", "where", `ProcessId=${pid}`, "get", "ExecutablePath", "/format:list",
|
|
233
|
+
], { encoding: "utf8", timeout: 3000 });
|
|
234
|
+
if (result.status !== 0) return false;
|
|
235
|
+
const output = (result.stdout || "").trim();
|
|
236
|
+
const normalized = DAEMON_BIN.replace(/\//g, "\\");
|
|
237
|
+
return output.includes(DAEMON_BIN) || output.includes(normalized);
|
|
238
|
+
}
|
|
204
239
|
const result = spawnSync("ps", ["-p", String(pid), "-o", "command="], {
|
|
205
240
|
encoding: "utf8",
|
|
206
241
|
timeout: 2000,
|
|
@@ -309,7 +344,7 @@ function installBundledBinary(source, destination) {
|
|
|
309
344
|
const temporary = `${destination}.tmp-${process.pid}-${crypto.randomUUID()}`;
|
|
310
345
|
try {
|
|
311
346
|
fs.copyFileSync(source, temporary);
|
|
312
|
-
|
|
347
|
+
safeChmod(temporary, 0o755);
|
|
313
348
|
fs.renameSync(temporary, destination);
|
|
314
349
|
} finally {
|
|
315
350
|
try { fs.unlinkSync(temporary); } catch {}
|
|
@@ -329,7 +364,8 @@ async function installBinary(options = {}) {
|
|
|
329
364
|
ensureDir(BIN_DIR);
|
|
330
365
|
|
|
331
366
|
// Priority 1: bundled binary in npm package (vendor/)
|
|
332
|
-
const
|
|
367
|
+
const ext = IS_WIN32 ? ".exe" : "";
|
|
368
|
+
const bundledPath = path.join(BUNDLED_BIN_DIR, `autowonder-daemon-${platform}${ext}`);
|
|
333
369
|
if (fs.existsSync(bundledPath)) {
|
|
334
370
|
installBundledBinary(bundledPath, DAEMON_BIN);
|
|
335
371
|
log(`Installed from bundled binary: ${DAEMON_BIN}`);
|
|
@@ -338,10 +374,10 @@ async function installBinary(options = {}) {
|
|
|
338
374
|
|
|
339
375
|
// Priority 2: download from URL (OSS or CDN)
|
|
340
376
|
if (BINARY_BASE_URL) {
|
|
341
|
-
const url = `${BINARY_BASE_URL}/v${VERSION}/autowonder-daemon-${platform}`;
|
|
377
|
+
const url = `${BINARY_BASE_URL}/v${VERSION}/autowonder-daemon-${platform}${ext}`;
|
|
342
378
|
const downloaded = await tryDownload(url, DAEMON_BIN);
|
|
343
379
|
if (downloaded) {
|
|
344
|
-
|
|
380
|
+
safeChmod(DAEMON_BIN, 0o755);
|
|
345
381
|
log(`Downloaded daemon binary: ${DAEMON_BIN}`);
|
|
346
382
|
return true;
|
|
347
383
|
}
|
|
@@ -389,7 +425,7 @@ function buildFromSource() {
|
|
|
389
425
|
return false;
|
|
390
426
|
}
|
|
391
427
|
|
|
392
|
-
try { execFileSync("which", ["go"], { stdio: "ignore" }); } catch {
|
|
428
|
+
try { execFileSync(IS_WIN32 ? "where" : "which", ["go"], { stdio: "ignore" }); } catch {
|
|
393
429
|
error("Go is not installed. Install Go 1.22+ or provide a pre-built binary.");
|
|
394
430
|
error(" macOS: brew install go");
|
|
395
431
|
error(" Linux: https://go.dev/dl/");
|
|
@@ -403,7 +439,7 @@ function buildFromSource() {
|
|
|
403
439
|
log("Building daemon...");
|
|
404
440
|
ensureDir(BIN_DIR);
|
|
405
441
|
execFileSync("go", ["build", "-o", DAEMON_BIN, "./cmd/autowonder-daemon"], { cwd: tmpDir, stdio: "inherit" });
|
|
406
|
-
|
|
442
|
+
safeChmod(DAEMON_BIN, 0o755);
|
|
407
443
|
log(`Built daemon binary: ${DAEMON_BIN}`);
|
|
408
444
|
return true;
|
|
409
445
|
} catch (e) {
|
|
@@ -601,6 +637,40 @@ async function cmdStop(options = {}) {
|
|
|
601
637
|
return true;
|
|
602
638
|
}
|
|
603
639
|
|
|
640
|
+
function httpGetJson(addr, urlPath, timeoutMs = 3000) {
|
|
641
|
+
return new Promise((resolve) => {
|
|
642
|
+
const request = http.get({ hostname: addr.split(":")[0], port: addr.split(":")[1], path: urlPath, timeout: timeoutMs }, (response) => {
|
|
643
|
+
let body = "";
|
|
644
|
+
response.setEncoding("utf8");
|
|
645
|
+
response.on("data", (chunk) => { body += chunk; });
|
|
646
|
+
response.on("end", () => {
|
|
647
|
+
try { resolve({ statusCode: response.statusCode, body: JSON.parse(body) }); } catch { resolve(null); }
|
|
648
|
+
});
|
|
649
|
+
});
|
|
650
|
+
request.on("timeout", () => request.destroy());
|
|
651
|
+
request.on("error", () => resolve(null));
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function httpPostJson(addr, urlPath, body, timeoutMs = 10000) {
|
|
656
|
+
return new Promise((resolve) => {
|
|
657
|
+
const data = typeof body === "string" ? body : JSON.stringify(body);
|
|
658
|
+
const request = http.request({
|
|
659
|
+
hostname: addr.split(":")[0], port: addr.split(":")[1], path: urlPath, method: "POST", timeout: timeoutMs,
|
|
660
|
+
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(data) },
|
|
661
|
+
}, (response) => {
|
|
662
|
+
let responseBody = "";
|
|
663
|
+
response.setEncoding("utf8");
|
|
664
|
+
response.on("data", (chunk) => { responseBody += chunk; });
|
|
665
|
+
response.on("end", () => resolve({ statusCode: response.statusCode, body: responseBody }));
|
|
666
|
+
});
|
|
667
|
+
request.on("timeout", () => request.destroy());
|
|
668
|
+
request.on("error", (e) => resolve({ statusCode: 0, body: e.message }));
|
|
669
|
+
request.write(data);
|
|
670
|
+
request.end();
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
|
|
604
674
|
async function cmdStatus() {
|
|
605
675
|
const running = daemonRunning();
|
|
606
676
|
const pid = readPid();
|
|
@@ -616,23 +686,17 @@ async function cmdStatus() {
|
|
|
616
686
|
console.log("");
|
|
617
687
|
|
|
618
688
|
if (running) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
console.log(` API: http://${DEFAULT_API_ADDR} (${health.status})`);
|
|
624
|
-
}
|
|
625
|
-
} catch {}
|
|
689
|
+
const health = await httpGetJson(DEFAULT_API_ADDR, "/health");
|
|
690
|
+
if (health && health.statusCode === 200) {
|
|
691
|
+
console.log(` API: http://${DEFAULT_API_ADDR} (${health.body.status})`);
|
|
692
|
+
}
|
|
626
693
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
for (const rt of (runtimes.runtimes || runtimes)) {
|
|
632
|
-
console.log(` Runtime: ${rt.id} (${rt.provider}) ${rt.available ? "available" : "unavailable"}${rt.version ? " v" + rt.version : ""}`);
|
|
633
|
-
}
|
|
694
|
+
const runtimes = await httpGetJson(DEFAULT_API_ADDR, "/runtimes");
|
|
695
|
+
if (runtimes && runtimes.statusCode === 200) {
|
|
696
|
+
for (const rt of (runtimes.body.runtimes || runtimes.body)) {
|
|
697
|
+
console.log(` Runtime: ${rt.id} (${rt.provider}) ${rt.available ? "available" : "unavailable"}${rt.version ? " v" + rt.version : ""}`);
|
|
634
698
|
}
|
|
635
|
-
}
|
|
699
|
+
}
|
|
636
700
|
console.log("");
|
|
637
701
|
}
|
|
638
702
|
}
|
|
@@ -659,24 +723,14 @@ async function cmdDispatch(args) {
|
|
|
659
723
|
}
|
|
660
724
|
|
|
661
725
|
log(`Submitting dispatch to http://${DEFAULT_API_ADDR}...`);
|
|
662
|
-
const res =
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
"-H", "Content-Type: application/json",
|
|
666
|
-
"-d", body,
|
|
667
|
-
`http://${DEFAULT_API_ADDR}/dispatches/local`,
|
|
668
|
-
], { encoding: "utf8", timeout: 10000 });
|
|
669
|
-
|
|
670
|
-
const lines = (res.stdout || "").trim().split("\n");
|
|
671
|
-
const statusCode = lines.pop();
|
|
672
|
-
const responseBody = lines.join("\n");
|
|
673
|
-
|
|
674
|
-
if (statusCode === "200" || statusCode === "202") {
|
|
726
|
+
const res = await httpPostJson(DEFAULT_API_ADDR, "/dispatches/local", body);
|
|
727
|
+
|
|
728
|
+
if (res.statusCode === 200 || res.statusCode === 202) {
|
|
675
729
|
log("Dispatch submitted successfully.");
|
|
676
|
-
if (
|
|
730
|
+
if (res.body) console.log(res.body);
|
|
677
731
|
} else {
|
|
678
|
-
error(`Dispatch failed (HTTP ${statusCode})`);
|
|
679
|
-
if (
|
|
732
|
+
error(`Dispatch failed (HTTP ${res.statusCode})`);
|
|
733
|
+
if (res.body) console.error(res.body);
|
|
680
734
|
process.exit(1);
|
|
681
735
|
}
|
|
682
736
|
}
|
package/package.json
CHANGED
|
@@ -88,19 +88,23 @@ echo "Go toolchain: $(go env GOVERSION)"
|
|
|
88
88
|
# Build for all platforms
|
|
89
89
|
STAGING_VENDOR_DIR="$(mktemp -d "$PKG_DIR/.vendor-staging.XXXXXX")"
|
|
90
90
|
|
|
91
|
-
platforms=("darwin-arm64" "darwin-amd64" "linux-amd64" "linux-arm64")
|
|
92
|
-
goarch_map=("arm64" "amd64" "amd64" "arm64")
|
|
93
|
-
goos_map=("darwin" "darwin" "linux" "linux")
|
|
91
|
+
platforms=("darwin-arm64" "darwin-amd64" "linux-amd64" "linux-arm64" "windows-amd64" "windows-arm64")
|
|
92
|
+
goarch_map=("arm64" "amd64" "amd64" "arm64" "amd64" "arm64")
|
|
93
|
+
goos_map=("darwin" "darwin" "linux" "linux" "windows" "windows")
|
|
94
|
+
filename_map=("autowonder-daemon-darwin-arm64" "autowonder-daemon-darwin-amd64" "autowonder-daemon-linux-amd64" "autowonder-daemon-linux-arm64" "autowonder-daemon-win32-amd64.exe" "autowonder-daemon-win32-arm64.exe")
|
|
94
95
|
|
|
95
96
|
for i in "${!platforms[@]}"; do
|
|
96
97
|
platform="${platforms[$i]}"
|
|
97
98
|
goos="${goos_map[$i]}"
|
|
98
99
|
goarch="${goarch_map[$i]}"
|
|
99
|
-
|
|
100
|
+
filename="${filename_map[$i]}"
|
|
101
|
+
output="$STAGING_VENDOR_DIR/$filename"
|
|
100
102
|
|
|
101
103
|
echo "Building $platform..."
|
|
102
104
|
(cd "$BUILD_SOURCE_DIR" && CGO_ENABLED=0 GOOS="$goos" GOARCH="$goarch" go build -trimpath -buildvcs=true -o "$output" -ldflags="$ldflags" ./cmd/autowonder-daemon)
|
|
103
|
-
|
|
105
|
+
if [[ "$goos" != "windows" ]]; then
|
|
106
|
+
chmod +x "$output"
|
|
107
|
+
fi
|
|
104
108
|
verify_go_binary_provenance "$output" "$release_commit" "$goos" "$goarch"
|
|
105
109
|
echo " -> $output ($(du -h "$output" | cut -f1))"
|
|
106
110
|
done
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|