autowonder 0.2.0 → 0.2.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 +28 -34
- package/bin/cli.js +169 -35
- package/package.json +5 -1
- package/scripts/build-and-publish.sh +27 -7
- 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/README.md
CHANGED
|
@@ -1,50 +1,44 @@
|
|
|
1
1
|
# autowonder
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
AutoWonder 本地 agent runtime。安装后启动 daemon,持续轮询本地 assignment 队列并执行任务包。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 连接 AutoWonder 服务
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
#
|
|
9
|
-
npx -y autowonder connect --
|
|
8
|
+
# Qoder CLI
|
|
9
|
+
npx -y autowonder@0.2.2 connect --ws-url <wss-endpoint> --token <executor-token> --executor-id <executor-id> --provider qoder
|
|
10
10
|
|
|
11
|
-
#
|
|
12
|
-
npx -y autowonder
|
|
13
|
-
npx -y autowonder dispatch ./assignment.json
|
|
14
|
-
```
|
|
11
|
+
# Claude Code
|
|
12
|
+
npx -y autowonder@0.2.2 connect --ws-url <wss-endpoint> --token <executor-token> --executor-id <executor-id> --provider claude
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
# Codex CLI
|
|
15
|
+
npx -y autowonder@0.2.2 connect --ws-url <wss-endpoint> --token <executor-token> --executor-id <executor-id> --provider codex
|
|
16
|
+
```
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|---------|-------------|
|
|
20
|
-
| `autowonder connect` | Connect to server and run in foreground |
|
|
21
|
-
| `autowonder start` | Start daemon in background |
|
|
22
|
-
| `autowonder stop` | Stop background daemon |
|
|
23
|
-
| `autowonder status` | Show daemon status and runtime info |
|
|
24
|
-
| `autowonder dispatch <file>` | Submit a dispatch assignment |
|
|
25
|
-
| `autowonder install` | Install/update daemon binary |
|
|
18
|
+
`connect` 会安装当前 npm 包内置的 daemon,并使用页面生成的 WebSocket endpoint、Token 和执行器 ID 建立连接。目标机器必须提前安装并登录对应的 Qoder CLI、Claude Code 或 Codex CLI;runtime 会继承当前用户的 HOME、环境变量和 CLI 登录状态。
|
|
26
19
|
|
|
27
|
-
##
|
|
20
|
+
## 提交 assignment
|
|
28
21
|
|
|
29
|
-
|
|
30
|
-
- `--max-tasks <n>` — Max concurrent dispatches (default: 2)
|
|
31
|
-
- `--server-url <url>` — Server URL for remote task polling
|
|
32
|
-
- `--token <token>` — Authentication token for server
|
|
33
|
-
- `--name <name>` — Runtime instance name (default: hostname)
|
|
22
|
+
daemon 默认轮询 `~/autowonder_workspaces/assignments/*.json`。队列里放 assignment JSON,不放 package zip;JSON 的 `taskPackageRef.downloadUrl` 应指向可下载的 OSS、HTTP 或本地 package 地址。
|
|
34
23
|
|
|
35
|
-
|
|
24
|
+
```bash
|
|
25
|
+
queue="$HOME/autowonder_workspaces/assignments"
|
|
26
|
+
mkdir -p "$queue"
|
|
27
|
+
cp ./assignment.json "$queue/.assignment.tmp"
|
|
28
|
+
mv "$queue/.assignment.tmp" "$queue/assignment.json"
|
|
29
|
+
```
|
|
36
30
|
|
|
37
|
-
|
|
38
|
-
- For building from source: Go 1.22+ and Git
|
|
31
|
+
也可以直接提交到本地 API:
|
|
39
32
|
|
|
40
|
-
|
|
33
|
+
```bash
|
|
34
|
+
npx -y autowonder@0.2.2 dispatch ./assignment.json
|
|
35
|
+
```
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
2. Starts a local HTTP API on `127.0.0.1:34989`
|
|
44
|
-
3. Accepts dispatch assignments via file queue, API, or server polling
|
|
45
|
-
4. Executes packages using the configured agent provider
|
|
46
|
-
5. Produces structured artifacts, results, and observability
|
|
37
|
+
## 管理 daemon
|
|
47
38
|
|
|
48
|
-
|
|
39
|
+
```bash
|
|
40
|
+
npx -y autowonder@0.2.2 status
|
|
41
|
+
npx -y autowonder@0.2.2 stop
|
|
42
|
+
```
|
|
49
43
|
|
|
50
|
-
|
|
44
|
+
默认 API 是 `http://127.0.0.1:34989`,日志位于 `~/.autowonder/daemon.log`。npm 包不包含任何 agent、MCP 或服务端凭证。
|
package/bin/cli.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
const { execFileSync, spawn, spawnSync } = require("child_process");
|
|
5
|
+
const crypto = require("crypto");
|
|
5
6
|
const path = require("path");
|
|
6
7
|
const fs = require("fs");
|
|
7
8
|
const os = require("os");
|
|
@@ -14,6 +15,7 @@ const BIN_DIR = path.join(AUTOWONDER_HOME, "bin");
|
|
|
14
15
|
const DAEMON_BIN = path.join(BIN_DIR, "autowonder-daemon");
|
|
15
16
|
const CONFIG_PATH = path.join(AUTOWONDER_HOME, "config.json");
|
|
16
17
|
const PID_PATH = path.join(AUTOWONDER_HOME, "daemon.pid");
|
|
18
|
+
const STATE_PATH = path.join(AUTOWONDER_HOME, "daemon-state.json");
|
|
17
19
|
const LOG_PATH = path.join(AUTOWONDER_HOME, "daemon.log");
|
|
18
20
|
|
|
19
21
|
const BINARY_BASE_URL = process.env.AUTOWONDER_BINARY_URL || "";
|
|
@@ -40,17 +42,105 @@ function detectProvider() {
|
|
|
40
42
|
const providers = [];
|
|
41
43
|
try { execFileSync("which", ["claude"], { stdio: "ignore" }); providers.push("claude"); } catch {}
|
|
42
44
|
try { execFileSync("which", ["codex"], { stdio: "ignore" }); providers.push("codex"); } catch {}
|
|
43
|
-
try { execFileSync("which", ["
|
|
45
|
+
try { execFileSync("which", ["qodercli"], { stdio: "ignore" }); providers.push("qoder"); } catch {
|
|
46
|
+
try { execFileSync("which", ["qoder"], { stdio: "ignore" }); providers.push("qoder"); } catch {}
|
|
47
|
+
}
|
|
44
48
|
return providers;
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
function daemonRunning() {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
const pid = readPid();
|
|
53
|
+
return Boolean(pid && processRunning(pid) && ownsDaemonProcess(pid));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function processRunning(pid) {
|
|
51
57
|
try { process.kill(pid, 0); return true; } catch { return false; }
|
|
52
58
|
}
|
|
53
59
|
|
|
60
|
+
function processFingerprint(pid) {
|
|
61
|
+
const result = spawnSync("ps", ["-p", String(pid), "-o", "lstart=", "-o", "command="], {
|
|
62
|
+
encoding: "utf8",
|
|
63
|
+
timeout: 2000,
|
|
64
|
+
});
|
|
65
|
+
return result.status === 0 ? (result.stdout || "").trim() : "";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function legacyDaemonProcess(pid) {
|
|
69
|
+
const result = spawnSync("ps", ["-p", String(pid), "-o", "command="], {
|
|
70
|
+
encoding: "utf8",
|
|
71
|
+
timeout: 2000,
|
|
72
|
+
});
|
|
73
|
+
if (result.status !== 0) return false;
|
|
74
|
+
const command = (result.stdout || "").trim();
|
|
75
|
+
return command === DAEMON_BIN || command.startsWith(`${DAEMON_BIN} `);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function readDaemonState() {
|
|
79
|
+
if (!fs.existsSync(STATE_PATH)) return null;
|
|
80
|
+
try { return JSON.parse(fs.readFileSync(STATE_PATH, "utf8")); } catch { return null; }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function ownsDaemonProcess(pid) {
|
|
84
|
+
const state = readDaemonState();
|
|
85
|
+
if (state && state.pid === pid) {
|
|
86
|
+
const fingerprint = processFingerprint(pid);
|
|
87
|
+
return Boolean(fingerprint && fingerprint === state.fingerprint);
|
|
88
|
+
}
|
|
89
|
+
return legacyDaemonProcess(pid);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function clearDaemonState() {
|
|
93
|
+
try { fs.unlinkSync(PID_PATH); } catch {}
|
|
94
|
+
try { fs.unlinkSync(STATE_PATH); } catch {}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function saveDaemonState(pid, instanceId) {
|
|
98
|
+
const fingerprint = processFingerprint(pid);
|
|
99
|
+
if (!fingerprint) return false;
|
|
100
|
+
fs.writeFileSync(PID_PATH, String(pid) + "\n");
|
|
101
|
+
fs.writeFileSync(STATE_PATH, JSON.stringify({ pid, fingerprint, instanceId }) + "\n", { mode: 0o600 });
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function waitForProcessExit(pid, timeoutMs = 5000) {
|
|
106
|
+
const deadline = Date.now() + timeoutMs;
|
|
107
|
+
while (Date.now() < deadline) {
|
|
108
|
+
if (!processRunning(pid)) return true;
|
|
109
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
110
|
+
}
|
|
111
|
+
return !processRunning(pid);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function checkDaemonHealth(addr, instanceId) {
|
|
115
|
+
return new Promise((resolve) => {
|
|
116
|
+
const request = http.get(`http://${addr}/health`, { timeout: 500 }, (response) => {
|
|
117
|
+
let body = "";
|
|
118
|
+
response.setEncoding("utf8");
|
|
119
|
+
response.on("data", (chunk) => { body += chunk; });
|
|
120
|
+
response.on("end", () => {
|
|
121
|
+
try {
|
|
122
|
+
const health = JSON.parse(body);
|
|
123
|
+
resolve(response.statusCode === 200 && health.status === "ok" && health.instanceId === instanceId);
|
|
124
|
+
} catch {
|
|
125
|
+
resolve(false);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
request.on("timeout", () => request.destroy());
|
|
130
|
+
request.on("error", () => resolve(false));
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function waitForDaemonHealth(addr, pid, instanceId, timeoutMs = 5000) {
|
|
135
|
+
const deadline = Date.now() + timeoutMs;
|
|
136
|
+
while (Date.now() < deadline) {
|
|
137
|
+
if (!processRunning(pid)) return false;
|
|
138
|
+
if (await checkDaemonHealth(addr, instanceId)) return true;
|
|
139
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
54
144
|
function readPid() {
|
|
55
145
|
if (!fs.existsSync(PID_PATH)) return null;
|
|
56
146
|
const pid = parseInt(fs.readFileSync(PID_PATH, "utf8").trim(), 10);
|
|
@@ -125,7 +215,12 @@ function tryDownload(url, dest) {
|
|
|
125
215
|
function doRequest(requestUrl, redirects) {
|
|
126
216
|
if (redirects > 5) { resolve(false); return; }
|
|
127
217
|
const parsedUrl = new URL(requestUrl);
|
|
128
|
-
const options = {
|
|
218
|
+
const options = {
|
|
219
|
+
hostname: parsedUrl.hostname,
|
|
220
|
+
port: parsedUrl.port || undefined,
|
|
221
|
+
path: parsedUrl.pathname + parsedUrl.search,
|
|
222
|
+
headers: { "User-Agent": "autowonder-npm" },
|
|
223
|
+
};
|
|
129
224
|
client.get(options, (res) => {
|
|
130
225
|
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
131
226
|
doRequest(res.headers.location, redirects + 1);
|
|
@@ -177,28 +272,32 @@ function buildFromSource() {
|
|
|
177
272
|
// ─── Commands ────────────────────────────────────────────────────────
|
|
178
273
|
|
|
179
274
|
async function cmdConnect(args) {
|
|
180
|
-
const flags = parseFlags(args, ["token", "
|
|
275
|
+
const flags = parseFlags(args, ["ws-url", "token", "executor-id", "provider", "name", "max-tasks"]);
|
|
181
276
|
|
|
182
|
-
if (!flags.token
|
|
183
|
-
error("Usage: autowonder connect --
|
|
277
|
+
if (!flags["ws-url"] || !flags.token || !flags["executor-id"]) {
|
|
278
|
+
error("Usage: autowonder connect --ws-url <url> --token <token> --executor-id <id> [--provider qoder]");
|
|
184
279
|
process.exit(1);
|
|
185
280
|
}
|
|
186
281
|
|
|
187
|
-
|
|
282
|
+
// A version-pinned npx command must always run the binary bundled with that package.
|
|
283
|
+
const installed = await installBinary({ force: true });
|
|
188
284
|
if (!installed) process.exit(1);
|
|
189
285
|
|
|
190
286
|
const providers = detectProvider();
|
|
191
|
-
const provider = flags.provider || providers[0]
|
|
192
|
-
if (
|
|
193
|
-
error(`No agent CLI detected. Install one of: claude, codex,
|
|
287
|
+
const provider = flags.provider || providers[0];
|
|
288
|
+
if (!provider) {
|
|
289
|
+
error(`No agent CLI detected. Install one of: claude, codex, qodercli`);
|
|
194
290
|
process.exit(1);
|
|
195
291
|
}
|
|
196
292
|
|
|
293
|
+
const executorId = flags["executor-id"];
|
|
294
|
+
|
|
197
295
|
const config = {
|
|
198
|
-
serverUrl: flags["
|
|
199
|
-
token: flags.token
|
|
296
|
+
serverUrl: flags["ws-url"],
|
|
297
|
+
token: flags.token,
|
|
298
|
+
executorId,
|
|
200
299
|
provider,
|
|
201
|
-
name: flags.name ||
|
|
300
|
+
name: flags.name || `daemon_${provider}_${executorId}`,
|
|
202
301
|
maxTasks: parseInt(flags["max-tasks"] || "2", 10),
|
|
203
302
|
};
|
|
204
303
|
saveConfig(config);
|
|
@@ -215,9 +314,13 @@ async function cmdConnect(args) {
|
|
|
215
314
|
AUTOWONDER_PROVIDER: config.provider,
|
|
216
315
|
AUTOWONDER_MAX_CONCURRENT_DISPATCHES: String(config.maxTasks),
|
|
217
316
|
AUTOWONDER_DAEMON_ID: config.name,
|
|
317
|
+
AUTOWONDER_RUNTIME_ID: `rt_${provider}_${executorId}`,
|
|
318
|
+
AUTOWONDER_WORKSPACE_ROOT: path.join(os.homedir(), `autowonder_workspaces_${provider}_${executorId}`),
|
|
319
|
+
AUTOWONDER_LOCAL_API_ADDR: "127.0.0.1:0",
|
|
320
|
+
AUTOWONDER_SERVER_WS_URL: config.serverUrl,
|
|
321
|
+
AUTOWONDER_EXECUTOR_TOKEN: config.token,
|
|
322
|
+
AUTOWONDER_EXECUTOR_ID: executorId,
|
|
218
323
|
};
|
|
219
|
-
if (config.serverUrl) env.AUTOWONDER_SERVER_URL = config.serverUrl;
|
|
220
|
-
if (config.token) env.AUTOWONDER_SERVER_TOKEN = config.token;
|
|
221
324
|
|
|
222
325
|
const child = spawn(DAEMON_BIN, [], { env, stdio: "inherit" });
|
|
223
326
|
child.on("exit", (code) => process.exit(code || 0));
|
|
@@ -230,7 +333,7 @@ async function cmdStart(args) {
|
|
|
230
333
|
|
|
231
334
|
if (daemonRunning()) {
|
|
232
335
|
log("Daemon is already running.");
|
|
233
|
-
return;
|
|
336
|
+
return true;
|
|
234
337
|
}
|
|
235
338
|
|
|
236
339
|
const installed = await installBinary();
|
|
@@ -245,6 +348,7 @@ async function cmdStart(args) {
|
|
|
245
348
|
|
|
246
349
|
ensureDir(AUTOWONDER_HOME);
|
|
247
350
|
const logFd = fs.openSync(LOG_PATH, "a");
|
|
351
|
+
const instanceId = crypto.randomUUID();
|
|
248
352
|
const child = spawn(DAEMON_BIN, [], {
|
|
249
353
|
detached: true,
|
|
250
354
|
stdio: ["ignore", logFd, logFd],
|
|
@@ -253,28 +357,52 @@ async function cmdStart(args) {
|
|
|
253
357
|
AUTOWONDER_PROVIDER: provider,
|
|
254
358
|
AUTOWONDER_LOCAL_API_ADDR: addr,
|
|
255
359
|
AUTOWONDER_MAX_CONCURRENT_DISPATCHES: maxTasks,
|
|
360
|
+
AUTOWONDER_DAEMON_INSTANCE_ID: instanceId,
|
|
256
361
|
},
|
|
257
362
|
});
|
|
363
|
+
fs.closeSync(logFd);
|
|
258
364
|
fs.writeFileSync(PID_PATH, String(child.pid) + "\n");
|
|
365
|
+
|
|
366
|
+
if (!await waitForDaemonHealth(addr, child.pid, instanceId) || !saveDaemonState(child.pid, instanceId)) {
|
|
367
|
+
if (processRunning(child.pid)) {
|
|
368
|
+
try { child.kill("SIGTERM"); } catch {}
|
|
369
|
+
await waitForProcessExit(child.pid, 2000);
|
|
370
|
+
}
|
|
371
|
+
clearDaemonState();
|
|
372
|
+
error(`Daemon failed to become healthy at http://${addr}/health.`);
|
|
373
|
+
process.exitCode = 1;
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
|
|
259
377
|
child.unref();
|
|
260
|
-
fs.closeSync(logFd);
|
|
261
378
|
|
|
262
379
|
log(`Daemon started (pid=${child.pid})`);
|
|
263
380
|
log(` API: http://${addr}`);
|
|
264
381
|
log(` Log: ${LOG_PATH}`);
|
|
265
382
|
log(` PID: ${PID_PATH}`);
|
|
383
|
+
return true;
|
|
266
384
|
}
|
|
267
385
|
|
|
268
|
-
function cmdStop() {
|
|
386
|
+
async function cmdStop(options = {}) {
|
|
269
387
|
const pid = readPid();
|
|
270
|
-
if (!pid || !
|
|
271
|
-
log("Daemon is not running.");
|
|
272
|
-
|
|
273
|
-
return;
|
|
388
|
+
if (!pid || !processRunning(pid)) {
|
|
389
|
+
if (!options.quiet) log("Daemon is not running.");
|
|
390
|
+
clearDaemonState();
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
if (!ownsDaemonProcess(pid)) {
|
|
394
|
+
if (!options.quiet) log(`Ignoring stale daemon PID ${pid}; it belongs to another process.`);
|
|
395
|
+
clearDaemonState();
|
|
396
|
+
return true;
|
|
274
397
|
}
|
|
275
398
|
process.kill(pid, "SIGTERM");
|
|
276
|
-
|
|
277
|
-
|
|
399
|
+
if (!await waitForProcessExit(pid)) {
|
|
400
|
+
error(`Daemon did not stop within 5 seconds (pid=${pid}).`);
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
if (!options.quiet) log(`Stopped daemon (pid=${pid})`);
|
|
404
|
+
clearDaemonState();
|
|
405
|
+
return true;
|
|
278
406
|
}
|
|
279
407
|
|
|
280
408
|
async function cmdStatus() {
|
|
@@ -340,7 +468,7 @@ async function cmdDispatch(args) {
|
|
|
340
468
|
"-X", "POST",
|
|
341
469
|
"-H", "Content-Type: application/json",
|
|
342
470
|
"-d", body,
|
|
343
|
-
`http://${DEFAULT_API_ADDR}/dispatches`,
|
|
471
|
+
`http://${DEFAULT_API_ADDR}/dispatches/local`,
|
|
344
472
|
], { encoding: "utf8", timeout: 10000 });
|
|
345
473
|
|
|
346
474
|
const lines = (res.stdout || "").trim().split("\n");
|
|
@@ -358,9 +486,14 @@ async function cmdDispatch(args) {
|
|
|
358
486
|
}
|
|
359
487
|
|
|
360
488
|
async function cmdInstall(args) {
|
|
361
|
-
const flags = parseFlags(args, ["force"]);
|
|
362
|
-
const
|
|
489
|
+
const flags = parseFlags(args, ["force", "provider", "max-tasks", "addr"]);
|
|
490
|
+
const force = flags.force === "true" || args.includes("--force");
|
|
491
|
+
if (force && daemonRunning() && !await cmdStop({ quiet: true })) {
|
|
492
|
+
process.exit(1);
|
|
493
|
+
}
|
|
494
|
+
const installed = await installBinary({ force });
|
|
363
495
|
if (!installed) process.exit(1);
|
|
496
|
+
if (!await cmdStart(args)) process.exit(1);
|
|
364
497
|
}
|
|
365
498
|
|
|
366
499
|
function cmdHelp() {
|
|
@@ -368,25 +501,26 @@ function cmdHelp() {
|
|
|
368
501
|
autowonder — local runtime for AI agent dispatch execution
|
|
369
502
|
|
|
370
503
|
Usage:
|
|
371
|
-
autowonder connect --
|
|
504
|
+
autowonder connect --ws-url <url> --token <token> --executor-id <id> Connect to server and run
|
|
372
505
|
autowonder start [--provider claude] Start daemon in background
|
|
373
506
|
autowonder stop Stop background daemon
|
|
374
507
|
autowonder status Show daemon status
|
|
375
508
|
autowonder dispatch <assignment.json> Submit a dispatch
|
|
376
|
-
autowonder install [--force] Install/update daemon
|
|
509
|
+
autowonder install [--force] Install/update and start daemon
|
|
377
510
|
|
|
378
511
|
Options:
|
|
379
512
|
--provider <name> Agent provider: claude, codex, qoder (default: auto-detect)
|
|
380
513
|
--max-tasks <n> Max concurrent dispatches (default: 2)
|
|
381
514
|
--addr <host:port> Local API address (default: 127.0.0.1:34989)
|
|
382
|
-
--
|
|
383
|
-
--token <token>
|
|
384
|
-
--
|
|
515
|
+
--ws-url <url> Executor WebSocket endpoint
|
|
516
|
+
--token <token> Executor authentication token
|
|
517
|
+
--executor-id <id> Executor ID
|
|
518
|
+
--name <name> Runtime name (default: provider + executor ID)
|
|
385
519
|
-h, --help Show this help
|
|
386
520
|
|
|
387
521
|
Examples:
|
|
388
522
|
# Quick start — connect to server
|
|
389
|
-
npx -y autowonder connect --
|
|
523
|
+
npx -y autowonder connect --ws-url wss://autowonder.example.com/ws/executor --token xxx --executor-id 10000 --provider qoder
|
|
390
524
|
|
|
391
525
|
# Local mode — start daemon, submit tasks manually
|
|
392
526
|
npx -y autowonder start
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autowonder",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "AutoWonder local runtime — execute AI agent dispatch packages on your machine",
|
|
5
5
|
"bin": {
|
|
6
6
|
"autowonder": "bin/cli.js"
|
|
@@ -28,5 +28,9 @@
|
|
|
28
28
|
},
|
|
29
29
|
"engines": {
|
|
30
30
|
"node": ">=16"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "./scripts/build-and-publish.sh --build",
|
|
34
|
+
"test": "node --test test/*.test.js"
|
|
31
35
|
}
|
|
32
36
|
}
|
|
@@ -18,6 +18,18 @@ PKG_DIR="$(dirname "$SCRIPT_DIR")"
|
|
|
18
18
|
VENDOR_DIR="$PKG_DIR/vendor"
|
|
19
19
|
SOURCE_DIR="${AUTOWONDER_SOURCE_DIR:-}"
|
|
20
20
|
SOURCE_REPO="${AUTOWONDER_SOURCE_REPO:-}"
|
|
21
|
+
TEMP_SOURCE_DIR=""
|
|
22
|
+
NPM_CONFIG_FILE=""
|
|
23
|
+
|
|
24
|
+
cleanup() {
|
|
25
|
+
if [[ -n "$TEMP_SOURCE_DIR" ]]; then
|
|
26
|
+
rm -rf "$TEMP_SOURCE_DIR"
|
|
27
|
+
fi
|
|
28
|
+
if [[ -n "$NPM_CONFIG_FILE" ]]; then
|
|
29
|
+
rm -f "$NPM_CONFIG_FILE"
|
|
30
|
+
fi
|
|
31
|
+
}
|
|
32
|
+
trap cleanup EXIT
|
|
21
33
|
|
|
22
34
|
BUILD_ONLY=false
|
|
23
35
|
if [[ "${1:-}" == "--build" ]]; then
|
|
@@ -27,11 +39,19 @@ fi
|
|
|
27
39
|
echo "━━━ autowonder npm build ━━━"
|
|
28
40
|
|
|
29
41
|
# Get source code
|
|
42
|
+
if [[ -z "$SOURCE_DIR" && -f "$PKG_DIR/../go.mod" ]]; then
|
|
43
|
+
SOURCE_DIR="$(cd "$PKG_DIR/.." && pwd)"
|
|
44
|
+
fi
|
|
45
|
+
|
|
30
46
|
if [[ -n "$SOURCE_DIR" && -d "$SOURCE_DIR" ]]; then
|
|
31
47
|
echo "Using local source: $SOURCE_DIR"
|
|
32
48
|
else
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
if [[ -z "$SOURCE_REPO" ]]; then
|
|
50
|
+
echo "Error: set AUTOWONDER_SOURCE_DIR or AUTOWONDER_SOURCE_REPO"
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
TEMP_SOURCE_DIR="$(mktemp -d)"
|
|
54
|
+
SOURCE_DIR="$TEMP_SOURCE_DIR"
|
|
35
55
|
echo "Cloning from $SOURCE_REPO..."
|
|
36
56
|
git clone --depth 1 "$SOURCE_REPO" "$SOURCE_DIR"
|
|
37
57
|
fi
|
|
@@ -51,7 +71,7 @@ for i in "${!platforms[@]}"; do
|
|
|
51
71
|
output="$VENDOR_DIR/autowonder-daemon-${goos}-${goarch}"
|
|
52
72
|
|
|
53
73
|
echo "Building $platform..."
|
|
54
|
-
(cd "$SOURCE_DIR" && GOOS="$goos" GOARCH="$goarch" go build -o "$output" -ldflags="-s -w" ./cmd/autowonder-daemon)
|
|
74
|
+
(cd "$SOURCE_DIR" && CGO_ENABLED=0 GOOS="$goos" GOARCH="$goarch" go build -o "$output" -ldflags="-s -w" ./cmd/autowonder-daemon)
|
|
55
75
|
chmod +x "$output"
|
|
56
76
|
echo " -> $output ($(du -h "$output" | cut -f1))"
|
|
57
77
|
done
|
|
@@ -76,10 +96,10 @@ if [[ -z "${NPM_TOKEN:-}" ]]; then
|
|
|
76
96
|
exit 1
|
|
77
97
|
fi
|
|
78
98
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
99
|
+
umask 077
|
|
100
|
+
NPM_CONFIG_FILE="$(mktemp)"
|
|
101
|
+
printf '//registry.npmjs.org/:_authToken=%s\n' "$NPM_TOKEN" > "$NPM_CONFIG_FILE"
|
|
102
|
+
npm publish --access public --userconfig "$NPM_CONFIG_FILE"
|
|
83
103
|
|
|
84
104
|
echo ""
|
|
85
105
|
echo "Published $(node -p "require('./package.json').name + '@' + require('./package.json').version")"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|