autowonder 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/README.md +50 -0
- package/bin/cli.js +426 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# autowonder
|
|
2
|
+
|
|
3
|
+
Local runtime daemon for executing AI agent dispatch packages.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Connect to server (like harness)
|
|
9
|
+
npx -y autowonder connect --server-url https://autowonder.example.com --token <your-token>
|
|
10
|
+
|
|
11
|
+
# Or run locally
|
|
12
|
+
npx -y autowonder start
|
|
13
|
+
npx -y autowonder dispatch ./assignment.json
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Commands
|
|
17
|
+
|
|
18
|
+
| Command | Description |
|
|
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 |
|
|
26
|
+
|
|
27
|
+
## Options
|
|
28
|
+
|
|
29
|
+
- `--provider <name>` — Agent CLI: claude, codex, qoder (auto-detected)
|
|
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)
|
|
34
|
+
|
|
35
|
+
## Requirements
|
|
36
|
+
|
|
37
|
+
- One of: Claude Code CLI, Codex CLI, or Qoder CLI
|
|
38
|
+
- For building from source: Go 1.22+ and Git
|
|
39
|
+
|
|
40
|
+
## How It Works
|
|
41
|
+
|
|
42
|
+
1. Downloads or builds the `autowonder-daemon` binary
|
|
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
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
Apache-2.0
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { execFileSync, spawn, spawnSync } = require("child_process");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const os = require("os");
|
|
8
|
+
const https = require("https");
|
|
9
|
+
const http = require("http");
|
|
10
|
+
|
|
11
|
+
const VERSION = require("../package.json").version;
|
|
12
|
+
const AUTOWONDER_HOME = path.join(os.homedir(), ".autowonder");
|
|
13
|
+
const BIN_DIR = path.join(AUTOWONDER_HOME, "bin");
|
|
14
|
+
const DAEMON_BIN = path.join(BIN_DIR, "autowonder-daemon");
|
|
15
|
+
const CONFIG_PATH = path.join(AUTOWONDER_HOME, "config.json");
|
|
16
|
+
const PID_PATH = path.join(AUTOWONDER_HOME, "daemon.pid");
|
|
17
|
+
const LOG_PATH = path.join(AUTOWONDER_HOME, "daemon.log");
|
|
18
|
+
|
|
19
|
+
const BINARY_BASE_URL = process.env.AUTOWONDER_BINARY_URL || "https://github.com/ZichaoJin/auto-wonder-client-runtime/releases/download";
|
|
20
|
+
const SOURCE_REPO = "https://github.com/ZichaoJin/auto-wonder-client-runtime.git";
|
|
21
|
+
|
|
22
|
+
const DEFAULT_API_ADDR = "127.0.0.1:34989";
|
|
23
|
+
|
|
24
|
+
// ─── Helpers ─────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
function ensureDir(dir) {
|
|
27
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function log(msg) {
|
|
31
|
+
console.log(`\x1b[36m[autowonder]\x1b[0m ${msg}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function error(msg) {
|
|
35
|
+
console.error(`\x1b[31m[autowonder]\x1b[0m ${msg}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function detectProvider() {
|
|
39
|
+
const providers = [];
|
|
40
|
+
try { execFileSync("which", ["claude"], { stdio: "ignore" }); providers.push("claude"); } catch {}
|
|
41
|
+
try { execFileSync("which", ["codex"], { stdio: "ignore" }); providers.push("codex"); } catch {}
|
|
42
|
+
try { execFileSync("which", ["qoder"], { stdio: "ignore" }); providers.push("qoder"); } catch {}
|
|
43
|
+
return providers;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function daemonRunning() {
|
|
47
|
+
if (!fs.existsSync(PID_PATH)) return false;
|
|
48
|
+
const pid = parseInt(fs.readFileSync(PID_PATH, "utf8").trim(), 10);
|
|
49
|
+
if (!pid) return false;
|
|
50
|
+
try { process.kill(pid, 0); return true; } catch { return false; }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function readPid() {
|
|
54
|
+
if (!fs.existsSync(PID_PATH)) return null;
|
|
55
|
+
const pid = parseInt(fs.readFileSync(PID_PATH, "utf8").trim(), 10);
|
|
56
|
+
return pid || null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function loadConfig() {
|
|
60
|
+
if (!fs.existsSync(CONFIG_PATH)) return {};
|
|
61
|
+
try { return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8")); } catch { return {}; }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function saveConfig(config) {
|
|
65
|
+
ensureDir(AUTOWONDER_HOME);
|
|
66
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function binaryPlatformKey() {
|
|
70
|
+
const platform = process.platform;
|
|
71
|
+
const arch = process.arch === "arm64" ? "arm64" : "amd64";
|
|
72
|
+
return `${platform}-${arch}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function binaryExists() {
|
|
76
|
+
try {
|
|
77
|
+
fs.accessSync(DAEMON_BIN, fs.constants.X_OK);
|
|
78
|
+
return true;
|
|
79
|
+
} catch { return false; }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─── Binary Installation ─────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
async function installBinary(options = {}) {
|
|
85
|
+
if (binaryExists() && !options.force) {
|
|
86
|
+
log("Daemon binary already installed.");
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const platform = binaryPlatformKey();
|
|
91
|
+
log(`Installing autowonder-daemon for ${platform}...`);
|
|
92
|
+
|
|
93
|
+
// Try download pre-built binary
|
|
94
|
+
const url = `${BINARY_BASE_URL}/v${VERSION}/autowonder-daemon-${platform}`;
|
|
95
|
+
const downloaded = await tryDownload(url, DAEMON_BIN);
|
|
96
|
+
if (downloaded) {
|
|
97
|
+
fs.chmodSync(DAEMON_BIN, 0o755);
|
|
98
|
+
log(`Installed daemon binary: ${DAEMON_BIN}`);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Fallback: build from source
|
|
103
|
+
log("Pre-built binary not available. Building from source...");
|
|
104
|
+
return buildFromSource();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function tryDownload(url, dest) {
|
|
108
|
+
return new Promise((resolve) => {
|
|
109
|
+
ensureDir(path.dirname(dest));
|
|
110
|
+
const client = url.startsWith("https") ? https : http;
|
|
111
|
+
|
|
112
|
+
function doRequest(requestUrl, redirects) {
|
|
113
|
+
if (redirects > 5) { resolve(false); return; }
|
|
114
|
+
const parsedUrl = new URL(requestUrl);
|
|
115
|
+
const options = { hostname: parsedUrl.hostname, path: parsedUrl.pathname + parsedUrl.search, headers: { "User-Agent": "autowonder-npm" } };
|
|
116
|
+
client.get(options, (res) => {
|
|
117
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
118
|
+
doRequest(res.headers.location, redirects + 1);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (res.statusCode !== 200) { res.resume(); resolve(false); return; }
|
|
122
|
+
const file = fs.createWriteStream(dest);
|
|
123
|
+
res.pipe(file);
|
|
124
|
+
file.on("finish", () => { file.close(); resolve(true); });
|
|
125
|
+
file.on("error", () => { try { fs.unlinkSync(dest); } catch {} resolve(false); });
|
|
126
|
+
}).on("error", () => resolve(false));
|
|
127
|
+
}
|
|
128
|
+
doRequest(url, 0);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function buildFromSource() {
|
|
133
|
+
try { execFileSync("which", ["go"], { stdio: "ignore" }); } catch {
|
|
134
|
+
error("Go is not installed. Install Go 1.22+ or provide a pre-built binary.");
|
|
135
|
+
error(" macOS: brew install go");
|
|
136
|
+
error(" Linux: https://go.dev/dl/");
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "autowonder-build-"));
|
|
141
|
+
try {
|
|
142
|
+
log("Cloning source repository...");
|
|
143
|
+
execFileSync("git", ["clone", "--depth", "1", SOURCE_REPO, tmpDir], { stdio: "inherit" });
|
|
144
|
+
log("Building daemon...");
|
|
145
|
+
ensureDir(BIN_DIR);
|
|
146
|
+
execFileSync("go", ["build", "-o", DAEMON_BIN, "./cmd/autowonder-daemon"], { cwd: tmpDir, stdio: "inherit" });
|
|
147
|
+
fs.chmodSync(DAEMON_BIN, 0o755);
|
|
148
|
+
log(`Built daemon binary: ${DAEMON_BIN}`);
|
|
149
|
+
return true;
|
|
150
|
+
} catch (e) {
|
|
151
|
+
error(`Build failed: ${e.message}`);
|
|
152
|
+
return false;
|
|
153
|
+
} finally {
|
|
154
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ─── Commands ────────────────────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
async function cmdConnect(args) {
|
|
161
|
+
const flags = parseFlags(args, ["token", "server-url", "provider", "name", "max-tasks"]);
|
|
162
|
+
|
|
163
|
+
if (!flags.token && !flags["server-url"]) {
|
|
164
|
+
error("Usage: autowonder connect --server-url <url> --token <token> [--provider claude] [--name my-machine]");
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const installed = await installBinary();
|
|
169
|
+
if (!installed) process.exit(1);
|
|
170
|
+
|
|
171
|
+
const providers = detectProvider();
|
|
172
|
+
const provider = flags.provider || providers[0] || "claude";
|
|
173
|
+
if (providers.length === 0) {
|
|
174
|
+
error(`No agent CLI detected. Install one of: claude, codex, qoder`);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const config = {
|
|
179
|
+
serverUrl: flags["server-url"] || "",
|
|
180
|
+
token: flags.token || "",
|
|
181
|
+
provider,
|
|
182
|
+
name: flags.name || os.hostname(),
|
|
183
|
+
maxTasks: parseInt(flags["max-tasks"] || "2", 10),
|
|
184
|
+
};
|
|
185
|
+
saveConfig(config);
|
|
186
|
+
|
|
187
|
+
log(`Connecting to ${config.serverUrl || "local mode"}...`);
|
|
188
|
+
log(`Provider: ${provider}`);
|
|
189
|
+
log(`Name: ${config.name}`);
|
|
190
|
+
log(`Max concurrent: ${config.maxTasks}`);
|
|
191
|
+
log("");
|
|
192
|
+
|
|
193
|
+
// Start daemon in foreground (like harness connect)
|
|
194
|
+
const env = {
|
|
195
|
+
...process.env,
|
|
196
|
+
AUTOWONDER_PROVIDER: config.provider,
|
|
197
|
+
AUTOWONDER_MAX_CONCURRENT_DISPATCHES: String(config.maxTasks),
|
|
198
|
+
AUTOWONDER_DAEMON_ID: config.name,
|
|
199
|
+
};
|
|
200
|
+
if (config.serverUrl) env.AUTOWONDER_SERVER_URL = config.serverUrl;
|
|
201
|
+
if (config.token) env.AUTOWONDER_SERVER_TOKEN = config.token;
|
|
202
|
+
|
|
203
|
+
const child = spawn(DAEMON_BIN, [], { env, stdio: "inherit" });
|
|
204
|
+
child.on("exit", (code) => process.exit(code || 0));
|
|
205
|
+
process.on("SIGINT", () => { child.kill("SIGINT"); });
|
|
206
|
+
process.on("SIGTERM", () => { child.kill("SIGTERM"); });
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function cmdStart(args) {
|
|
210
|
+
const flags = parseFlags(args, ["provider", "max-tasks", "addr"]);
|
|
211
|
+
|
|
212
|
+
if (daemonRunning()) {
|
|
213
|
+
log("Daemon is already running.");
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const installed = await installBinary();
|
|
218
|
+
if (!installed) process.exit(1);
|
|
219
|
+
|
|
220
|
+
const providers = detectProvider();
|
|
221
|
+
const provider = flags.provider || providers[0] || "claude";
|
|
222
|
+
const addr = flags.addr || DEFAULT_API_ADDR;
|
|
223
|
+
const maxTasks = flags["max-tasks"] || "2";
|
|
224
|
+
|
|
225
|
+
log(`Starting daemon (provider=${provider}, addr=${addr})...`);
|
|
226
|
+
|
|
227
|
+
ensureDir(AUTOWONDER_HOME);
|
|
228
|
+
const logFd = fs.openSync(LOG_PATH, "a");
|
|
229
|
+
const child = spawn(DAEMON_BIN, [], {
|
|
230
|
+
detached: true,
|
|
231
|
+
stdio: ["ignore", logFd, logFd],
|
|
232
|
+
env: {
|
|
233
|
+
...process.env,
|
|
234
|
+
AUTOWONDER_PROVIDER: provider,
|
|
235
|
+
AUTOWONDER_LOCAL_API_ADDR: addr,
|
|
236
|
+
AUTOWONDER_MAX_CONCURRENT_DISPATCHES: maxTasks,
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
fs.writeFileSync(PID_PATH, String(child.pid) + "\n");
|
|
240
|
+
child.unref();
|
|
241
|
+
fs.closeSync(logFd);
|
|
242
|
+
|
|
243
|
+
log(`Daemon started (pid=${child.pid})`);
|
|
244
|
+
log(` API: http://${addr}`);
|
|
245
|
+
log(` Log: ${LOG_PATH}`);
|
|
246
|
+
log(` PID: ${PID_PATH}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function cmdStop() {
|
|
250
|
+
const pid = readPid();
|
|
251
|
+
if (!pid || !daemonRunning()) {
|
|
252
|
+
log("Daemon is not running.");
|
|
253
|
+
try { fs.unlinkSync(PID_PATH); } catch {}
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
process.kill(pid, "SIGTERM");
|
|
257
|
+
log(`Stopped daemon (pid=${pid})`);
|
|
258
|
+
try { fs.unlinkSync(PID_PATH); } catch {}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function cmdStatus() {
|
|
262
|
+
const running = daemonRunning();
|
|
263
|
+
const pid = readPid();
|
|
264
|
+
const config = loadConfig();
|
|
265
|
+
|
|
266
|
+
console.log("");
|
|
267
|
+
console.log(` Status: ${running ? "\x1b[32mrunning\x1b[0m" : "\x1b[31mstopped\x1b[0m"}`);
|
|
268
|
+
if (pid && running) console.log(` PID: ${pid}`);
|
|
269
|
+
console.log(` Provider: ${config.provider || "not configured"}`);
|
|
270
|
+
console.log(` Server: ${config.serverUrl || "local only"}`);
|
|
271
|
+
console.log(` Binary: ${binaryExists() ? DAEMON_BIN : "not installed"}`);
|
|
272
|
+
console.log(` Home: ${AUTOWONDER_HOME}`);
|
|
273
|
+
console.log("");
|
|
274
|
+
|
|
275
|
+
if (running) {
|
|
276
|
+
try {
|
|
277
|
+
const res = spawnSync("curl", ["-s", `http://${DEFAULT_API_ADDR}/health`], { encoding: "utf8", timeout: 3000 });
|
|
278
|
+
if (res.stdout) {
|
|
279
|
+
const health = JSON.parse(res.stdout);
|
|
280
|
+
console.log(` API: http://${DEFAULT_API_ADDR} (${health.status})`);
|
|
281
|
+
}
|
|
282
|
+
} catch {}
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
const res = spawnSync("curl", ["-s", `http://${DEFAULT_API_ADDR}/runtimes`], { encoding: "utf8", timeout: 3000 });
|
|
286
|
+
if (res.stdout) {
|
|
287
|
+
const runtimes = JSON.parse(res.stdout);
|
|
288
|
+
for (const rt of (runtimes.runtimes || runtimes)) {
|
|
289
|
+
console.log(` Runtime: ${rt.id} (${rt.provider}) ${rt.available ? "available" : "unavailable"}${rt.version ? " v" + rt.version : ""}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} catch {}
|
|
293
|
+
console.log("");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function cmdDispatch(args) {
|
|
298
|
+
if (args.length === 0) {
|
|
299
|
+
error("Usage: autowonder dispatch <assignment.json | package-url>");
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (!daemonRunning()) {
|
|
304
|
+
error("Daemon is not running. Start it first: autowonder start");
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const input = args[0];
|
|
309
|
+
let body;
|
|
310
|
+
|
|
311
|
+
if (fs.existsSync(input)) {
|
|
312
|
+
body = fs.readFileSync(input, "utf8");
|
|
313
|
+
} else {
|
|
314
|
+
error(`File not found: ${input}`);
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
log(`Submitting dispatch to http://${DEFAULT_API_ADDR}...`);
|
|
319
|
+
const res = spawnSync("curl", [
|
|
320
|
+
"-s", "-w", "\n%{http_code}",
|
|
321
|
+
"-X", "POST",
|
|
322
|
+
"-H", "Content-Type: application/json",
|
|
323
|
+
"-d", body,
|
|
324
|
+
`http://${DEFAULT_API_ADDR}/dispatches`,
|
|
325
|
+
], { encoding: "utf8", timeout: 10000 });
|
|
326
|
+
|
|
327
|
+
const lines = (res.stdout || "").trim().split("\n");
|
|
328
|
+
const statusCode = lines.pop();
|
|
329
|
+
const responseBody = lines.join("\n");
|
|
330
|
+
|
|
331
|
+
if (statusCode === "200" || statusCode === "202") {
|
|
332
|
+
log("Dispatch submitted successfully.");
|
|
333
|
+
if (responseBody) console.log(responseBody);
|
|
334
|
+
} else {
|
|
335
|
+
error(`Dispatch failed (HTTP ${statusCode})`);
|
|
336
|
+
if (responseBody) console.error(responseBody);
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async function cmdInstall(args) {
|
|
342
|
+
const flags = parseFlags(args, ["force"]);
|
|
343
|
+
const installed = await installBinary({ force: flags.force === "true" || args.includes("--force") });
|
|
344
|
+
if (!installed) process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function cmdHelp() {
|
|
348
|
+
console.log(`
|
|
349
|
+
autowonder — local runtime for AI agent dispatch execution
|
|
350
|
+
|
|
351
|
+
Usage:
|
|
352
|
+
autowonder connect --server-url <url> --token <token> Connect to server and run
|
|
353
|
+
autowonder start [--provider claude] Start daemon in background
|
|
354
|
+
autowonder stop Stop background daemon
|
|
355
|
+
autowonder status Show daemon status
|
|
356
|
+
autowonder dispatch <assignment.json> Submit a dispatch
|
|
357
|
+
autowonder install [--force] Install/update daemon binary
|
|
358
|
+
|
|
359
|
+
Options:
|
|
360
|
+
--provider <name> Agent provider: claude, codex, qoder (default: auto-detect)
|
|
361
|
+
--max-tasks <n> Max concurrent dispatches (default: 2)
|
|
362
|
+
--addr <host:port> Local API address (default: 127.0.0.1:34989)
|
|
363
|
+
--server-url <url> Server URL for remote dispatch
|
|
364
|
+
--token <token> Authentication token
|
|
365
|
+
--name <name> Runtime name (default: hostname)
|
|
366
|
+
-h, --help Show this help
|
|
367
|
+
|
|
368
|
+
Examples:
|
|
369
|
+
# Quick start — connect to server
|
|
370
|
+
npx -y autowonder connect --server-url https://autowonder.example.com --token xxx
|
|
371
|
+
|
|
372
|
+
# Local mode — start daemon, submit tasks manually
|
|
373
|
+
npx -y autowonder start
|
|
374
|
+
npx -y autowonder dispatch ./assignment.json
|
|
375
|
+
|
|
376
|
+
# Check what's happening
|
|
377
|
+
npx -y autowonder status
|
|
378
|
+
`);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// ─── Flag Parser ─────────────────────────────────────────────────────
|
|
382
|
+
|
|
383
|
+
function parseFlags(args, knownFlags) {
|
|
384
|
+
const flags = {};
|
|
385
|
+
for (let i = 0; i < args.length; i++) {
|
|
386
|
+
const arg = args[i];
|
|
387
|
+
if (arg.startsWith("--")) {
|
|
388
|
+
const key = arg.slice(2);
|
|
389
|
+
if (knownFlags.includes(key) && i + 1 < args.length && !args[i + 1].startsWith("--")) {
|
|
390
|
+
flags[key] = args[++i];
|
|
391
|
+
} else {
|
|
392
|
+
flags[key] = "true";
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return flags;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// ─── Main ────────────────────────────────────────────────────────────
|
|
400
|
+
|
|
401
|
+
async function main() {
|
|
402
|
+
const args = process.argv.slice(2);
|
|
403
|
+
const command = args[0] || "help";
|
|
404
|
+
const commandArgs = args.slice(1);
|
|
405
|
+
|
|
406
|
+
switch (command) {
|
|
407
|
+
case "connect": return cmdConnect(commandArgs);
|
|
408
|
+
case "start": return cmdStart(commandArgs);
|
|
409
|
+
case "stop": return cmdStop();
|
|
410
|
+
case "status": return cmdStatus();
|
|
411
|
+
case "dispatch": return cmdDispatch(commandArgs);
|
|
412
|
+
case "install": return cmdInstall(commandArgs);
|
|
413
|
+
case "help":
|
|
414
|
+
case "--help":
|
|
415
|
+
case "-h": return cmdHelp();
|
|
416
|
+
default:
|
|
417
|
+
error(`Unknown command: ${command}`);
|
|
418
|
+
cmdHelp();
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
main().catch((e) => {
|
|
424
|
+
error(e.message || String(e));
|
|
425
|
+
process.exit(1);
|
|
426
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "autowonder",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AutoWonder local runtime — execute AI agent dispatch packages on your machine",
|
|
5
|
+
"bin": {
|
|
6
|
+
"autowonder": "bin/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/",
|
|
10
|
+
"scripts/"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"autowonder",
|
|
14
|
+
"runtime",
|
|
15
|
+
"ai-agent",
|
|
16
|
+
"daemon",
|
|
17
|
+
"dispatch",
|
|
18
|
+
"claude",
|
|
19
|
+
"codex",
|
|
20
|
+
"qoder"
|
|
21
|
+
],
|
|
22
|
+
"author": "Alibaba Cloud",
|
|
23
|
+
"license": "Apache-2.0",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/ZichaoJin/auto-wonder-client-runtime.git"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=16"
|
|
30
|
+
}
|
|
31
|
+
}
|