autowonder 0.2.85 → 0.2.87

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 CHANGED
@@ -5,17 +5,19 @@ AutoWonder 本地 agent runtime。安装后启动 daemon,持续轮询本地 as
5
5
  ## 连接 AutoWonder 服务
6
6
 
7
7
  ```bash
8
- # Qoder CLI
9
- npx -y autowonder@0.2.85 connect --ws-url <wss-endpoint> --token <executor-token> --executor-id <executor-id> --provider qoder --model Qwen3.7-Max --reasoning-effort medium --context-window 131072
8
+ # Qoder CLI(模型使用 provider model ID;Context Window 使用固定档位)
9
+ npx -y autowonder@latest 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.85 connect --ws-url <wss-endpoint> --token <executor-token> --executor-id <executor-id> --provider claude
12
+ npx -y autowonder@0.2.87 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.85 connect --ws-url <wss-endpoint> --token <executor-token> --executor-id <executor-id> --provider codex --model gpt-5.5 --reasoning-effort medium
15
+ npx -y autowonder@0.2.87 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
- `connect` 会安装当前 npm 包内置的 daemon,并使用页面生成的 WebSocket endpoint、Token 和执行器 ID 建立连接。目标机器必须提前安装并登录对应的 Qoder CLI、Claude Code Codex CLIruntime 会继承当前用户的 HOME、环境变量和 CLI 登录状态。
18
+ `connect` 会安装当前 npm 包内置的 daemon,并使用页面生成的 WebSocket endpoint、Token 和执行器 ID 建立连接。选择 Qoder 时需要 Node.js 20+;runtime 会在缺少 `qodercli` 时自动通过 npm 安装,并在尚未登录时打开 Qoder 浏览器登录。Claude Code Codex CLI 仍需提前安装并登录。runtime 会继承当前用户的 HOME、环境变量和 CLI 登录状态。
19
+
20
+ Qoder 的 Context Window 只能使用 `1000000`、`400000` 或 `260000`,Reasoning Effort 只能使用 `none`、`low`、`medium`、`high`、`xhigh` 或 `max`。建议直接复制 AutoWonder 执行器页面生成的命令,避免把模型展示名称误当成 provider model ID。
19
21
 
20
22
  ## 自动更新
21
23
 
@@ -55,14 +57,14 @@ mv "$queue/.assignment.tmp" "$queue/assignment.json"
55
57
  也可以直接提交到本地 API:
56
58
 
57
59
  ```bash
58
- npx -y autowonder@0.2.85 dispatch ./assignment.json
60
+ npx -y autowonder@0.2.87 dispatch ./assignment.json
59
61
  ```
60
62
 
61
63
  ## 管理 daemon
62
64
 
63
65
  ```bash
64
- npx -y autowonder@0.2.85 status
65
- npx -y autowonder@0.2.85 stop
66
+ npx -y autowonder@0.2.87 status
67
+ npx -y autowonder@0.2.87 stop
66
68
  ```
67
69
 
68
70
  默认 API 是 `http://127.0.0.1:34989`,日志位于 `~/.autowonder/daemon.log`。npm 包不包含任何 agent、MCP 或服务端凭证。
package/bin/cli.js CHANGED
@@ -17,6 +17,7 @@ const CONFIG_PATH = path.join(AUTOWONDER_HOME, "config.json");
17
17
  const PID_PATH = path.join(AUTOWONDER_HOME, "daemon.pid");
18
18
  const STATE_PATH = path.join(AUTOWONDER_HOME, "daemon-state.json");
19
19
  const LOG_PATH = path.join(AUTOWONDER_HOME, "daemon.log");
20
+ const MANAGED_QODER_DIR = path.join(AUTOWONDER_HOME, "qoder-cli");
20
21
 
21
22
  const BINARY_BASE_URL = process.env.AUTOWONDER_BINARY_URL || "";
22
23
  const SOURCE_REPO = process.env.AUTOWONDER_SOURCE_REPO || "";
@@ -40,14 +41,126 @@ function error(msg) {
40
41
 
41
42
  function detectProvider() {
42
43
  const providers = [];
43
- try { execFileSync("which", ["claude"], { stdio: "ignore" }); providers.push("claude"); } catch {}
44
- try { execFileSync("which", ["codex"], { stdio: "ignore" }); providers.push("codex"); } catch {}
45
- try { execFileSync("which", ["qodercli"], { stdio: "ignore" }); providers.push("qoder"); } catch {
46
- try { execFileSync("which", ["qoder"], { stdio: "ignore" }); providers.push("qoder"); } catch {}
47
- }
44
+ if (findExecutable("claude")) providers.push("claude");
45
+ if (findExecutable("codex")) providers.push("codex");
46
+ if (findExecutable("qodercli")) providers.push("qoder");
48
47
  return providers;
49
48
  }
50
49
 
50
+ function findExecutable(name) {
51
+ if (name === "qodercli" && process.env.AUTOWONDER_QODER_CLI) {
52
+ const configured = resolveUserPath(process.env.AUTOWONDER_QODER_CLI);
53
+ try { fs.accessSync(configured, fs.constants.X_OK); return configured; } catch {}
54
+ }
55
+ const finder = process.platform === "win32" ? "where" : "which";
56
+ const found = spawnSync(finder, [name], { encoding: "utf8", timeout: 3000 });
57
+ if (found.status === 0) {
58
+ const first = (found.stdout || "").split(/\r?\n/).map((value) => value.trim()).find(Boolean);
59
+ if (first) return first;
60
+ }
61
+ if (name === "qodercli" && process.platform !== "win32") {
62
+ for (const candidate of [
63
+ path.join(MANAGED_QODER_DIR, "node_modules", ".bin", "qodercli"),
64
+ path.join(os.homedir(), ".local", "bin", "qodercli"),
65
+ path.join(os.homedir(), ".qoder", "bin", "qodercli", "qodercli"),
66
+ ]) {
67
+ try { fs.accessSync(candidate, fs.constants.X_OK); return candidate; } catch {}
68
+ }
69
+ }
70
+ if (name === "qodercli" && process.platform === "win32") {
71
+ const candidate = path.join(MANAGED_QODER_DIR, "node_modules", ".bin", "qodercli.cmd");
72
+ try { fs.accessSync(candidate, fs.constants.F_OK); return candidate; } catch {}
73
+ }
74
+ return "";
75
+ }
76
+
77
+ function qoderAuthenticated(executable) {
78
+ const result = spawnSync(executable, ["--list-models"], {
79
+ encoding: "utf8",
80
+ stdio: ["ignore", "pipe", "pipe"],
81
+ timeout: 20000,
82
+ });
83
+ return result.status === 0 && Boolean((result.stdout || "").trim());
84
+ }
85
+
86
+ function installQoderCLI() {
87
+ log("Qoder CLI is not installed. Installing @qoder-ai/qodercli...");
88
+ const result = spawnSync(process.platform === "win32" ? "npm.cmd" : "npm", [
89
+ "install", "--prefix", MANAGED_QODER_DIR, "@qoder-ai/qodercli@latest", "--no-audit", "--no-fund",
90
+ ], { stdio: "inherit" });
91
+ if (result.status !== 0) {
92
+ error(`Failed to install Qoder CLI into ${MANAGED_QODER_DIR}.`);
93
+ return "";
94
+ }
95
+ const executable = findExecutable("qodercli");
96
+ if (!executable) error("Qoder CLI was installed but qodercli is not available on PATH.");
97
+ return executable;
98
+ }
99
+
100
+ function qoderNodeVersion() {
101
+ if (process.env.NODE_ENV === "test" && process.env.AUTOWONDER_TEST_NODE_VERSION) {
102
+ return process.env.AUTOWONDER_TEST_NODE_VERSION;
103
+ }
104
+ return process.versions.node;
105
+ }
106
+
107
+ function ensureQoderReady() {
108
+ if (process.env.AUTOWONDER_SKIP_QODER_BOOTSTRAP === "1") return true;
109
+ const nodeVersion = qoderNodeVersion();
110
+ const nodeMajor = Number.parseInt(nodeVersion.split(".")[0], 10);
111
+ if (!Number.isInteger(nodeMajor) || nodeMajor < 20) {
112
+ error(`Qoder CLI requires Node.js 20 or newer; current version is ${nodeVersion}. Upgrade Node.js and retry.`);
113
+ return false;
114
+ }
115
+ let executable = findExecutable("qodercli");
116
+ if (!executable) executable = installQoderCLI();
117
+ if (!executable) return false;
118
+ if (!qoderAuthenticated(executable)) {
119
+ log("Qoder login is required. Opening browser login...");
120
+ const login = spawnSync(executable, ["login"], { stdio: "inherit" });
121
+ if (login.status !== 0 || !qoderAuthenticated(executable)) {
122
+ error("Qoder login did not complete. Run `qodercli login` and retry.");
123
+ return false;
124
+ }
125
+ }
126
+ process.env.AUTOWONDER_QODER_CLI = executable;
127
+ const executableDir = path.dirname(executable);
128
+ const pathEntries = (process.env.PATH || "").split(path.delimiter);
129
+ if (!pathEntries.includes(executableDir)) {
130
+ process.env.PATH = `${executableDir}${path.delimiter}${process.env.PATH || ""}`;
131
+ }
132
+ log("Qoder CLI is installed and authenticated.");
133
+ return true;
134
+ }
135
+
136
+ const QODER_MODELS = new Set([
137
+ "auto", "ultimate", "performance", "efficient", "lite", "cmodel",
138
+ "qmodel_preview", "qmodel_latest", "qmodel", "kmodel_latest", "kmodel",
139
+ "gm51model", "dmodel", "dfmodel", "mmodel", "qwen3.8-max-preview",
140
+ ]);
141
+ const QODER_CONTEXT_WINDOWS = new Set(["1000000", "400000", "260000"]);
142
+ const QODER_REASONING_EFFORTS = new Set(["none", "low", "medium", "high", "xhigh", "max"]);
143
+
144
+ function validateQoderOptions(flags) {
145
+ if (flags.model && !QODER_MODELS.has(flags.model)) {
146
+ error(`Unsupported Qoder model ID: ${flags.model}. Use a provider model ID shown in the executor form.`);
147
+ return false;
148
+ }
149
+ if (flags["reasoning-effort"] && !QODER_REASONING_EFFORTS.has(flags["reasoning-effort"])) {
150
+ error("--reasoning-effort must be one of: none, low, medium, high, xhigh, max");
151
+ return false;
152
+ }
153
+ if (flags["context-window"] && !/^[1-9][0-9]*$/.test(flags["context-window"])) {
154
+ error("--context-window must be a positive integer");
155
+ return false;
156
+ }
157
+ if (flags["context-window"] && !QODER_CONTEXT_WINDOWS.has(flags["context-window"])) {
158
+ error("--context-window must be one of: 1000000, 400000, 260000");
159
+ return false;
160
+ }
161
+ return true;
162
+ }
163
+
51
164
  function daemonRunning() {
52
165
  const pid = readPid();
53
166
  return Boolean(pid && processRunning(pid) && ownsDaemonProcess(pid));
@@ -289,16 +402,20 @@ async function cmdConnect(args) {
289
402
  process.exit(1);
290
403
  }
291
404
 
292
- // A version-pinned npx command must always run the binary bundled with that package.
293
- const installed = await installBinary({ force: true });
294
- if (!installed) process.exit(1);
405
+ const requestedProvider = flags.provider;
406
+ if (requestedProvider === "qoder" && !validateQoderOptions(flags)) process.exit(1);
295
407
 
296
408
  const providers = detectProvider();
297
- const provider = flags.provider || providers[0];
409
+ const provider = requestedProvider || providers[0];
298
410
  if (!provider) {
299
411
  error(`No agent CLI detected. Install one of: claude, codex, qodercli`);
300
412
  process.exit(1);
301
413
  }
414
+ if (provider === "qoder" && !ensureQoderReady()) process.exit(1);
415
+
416
+ // A version-pinned npx command must always run the binary bundled with that package.
417
+ const installed = await installBinary({ force: true });
418
+ if (!installed) process.exit(1);
302
419
 
303
420
  const hasProviderSettings = Boolean(flags.model || flags["reasoning-effort"]);
304
421
  if (hasProviderSettings && provider !== "codex" && provider !== "qoder") {
@@ -309,10 +426,7 @@ async function cmdConnect(args) {
309
426
  error("--context-window is supported only for qoder");
310
427
  process.exit(1);
311
428
  }
312
- if (flags["context-window"] && !/^[1-9][0-9]*$/.test(flags["context-window"])) {
313
- error("--context-window must be a positive integer");
314
- process.exit(1);
315
- }
429
+ if (provider === "qoder" && !validateQoderOptions(flags)) process.exit(1);
316
430
  let claudeSettingsPath = "";
317
431
  if (flags.settings) {
318
432
  if (provider !== "claude") {
@@ -577,7 +691,7 @@ function cmdHelp() {
577
691
  --executor-id <id> Executor ID
578
692
  --model <model> Default model for Codex or Qoder
579
693
  --reasoning-effort <level> Default reasoning effort for Codex or Qoder
580
- --context-window <n> Qoder context window (positive integer)
694
+ --context-window <n> Qoder context window: 1000000, 400000, or 260000
581
695
  --settings <file> Claude settings JSON file
582
696
  --memory-mode <mode> Memory: platform, provider-local, or none (default: platform)
583
697
  --name <name> Runtime name (default: provider + executor ID)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autowonder",
3
- "version": "0.2.85",
3
+ "version": "0.2.87",
4
4
  "description": "AutoWonder local runtime — execute AI agent dispatch packages on your machine",
5
5
  "bin": {
6
6
  "autowonder": "bin/cli.js"