autowonder 0.3.8 → 0.3.10
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 +11 -9
- package/bin/cli.js +6 -2
- package/bin/qoder-bootstrap.js +146 -20
- package/package.json +1 -1
- 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,10 +6,10 @@ AutoWonder 本地 agent runtime。安装后启动 daemon,持续轮询本地 as
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# Qoder CLI(模型使用 provider model ID;Context Window 使用固定档位)
|
|
9
|
-
npx -y autowonder@0.3.
|
|
9
|
+
npx -y autowonder@0.3.10 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
|
# Qoder CLI CN
|
|
12
|
-
npx -y autowonder@0.3.
|
|
12
|
+
npx -y autowonder@0.3.10 connect --ws-url <wss-endpoint> --token <executor-token> --executor-id <executor-id> --provider qodercn
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
`connect` 会安装当前 npm 包内置的 daemon,并使用页面生成的 WebSocket endpoint、Token 和执行器 ID 建立连接。客户端只支持通过 Qoder 系列 CLI 启动(`qoder` 对应 `qodercli`,`qodercn` 对应 `qodercn`),其他 `--provider` 取值会在启动 daemon 前直接报错拦截并提示 `Unsupported provider`。选择 Qoder CLI 或 Qoder CLI CN 时需要 Node.js 20+;runtime 会在缺少对应 CLI 时自动通过 npm 安装,并在尚未登录时打开浏览器登录。runtime 会继承当前用户的 HOME、环境变量和 CLI 登录状态。
|
|
@@ -35,6 +35,8 @@ Qoder 的 Context Window 只能使用 `1000000`、`400000` 或 `260000`,Reason
|
|
|
35
35
|
|
|
36
36
|
daemon 会响应服务端的 `QODER_MODEL_CATALOG_REQUEST` 帧,用当前 provider 对应的本机 Qoder CLI 发现可用模型目录,只回传 `{id, name}`,让执行器页面展示这台机器真实可选的模型。该请求仅在 `executorId` 与 `provider` 都与本机一致时处理,发现超时 20 秒,并发请求合并为一次发现,`qoder` 与 `qodercn` 的发现互相隔离、不共享配置根。
|
|
37
37
|
|
|
38
|
+
daemon 还在协议能力位里声明 `TASK_MODEL_SELECTION_V1`:服务端可在派单帧中附带任务级模型选择(`modelSelection`,含目标模型、条目计数与阈值、策略版本、原因码)。runtime 只对本次派单应用该模型(覆盖派单原有的 model 取值,不修改执行器保存的默认配置),并在开始执行时把 `dispatchId`、选中模型与原因写入 daemon 日志,便于回溯某个任务因何被升级到另一个模型。该能力对执行机用户无新增参数或环境变量。
|
|
39
|
+
|
|
38
40
|
daemon 还会响应服务端的 `CONVERSATION_COMMANDS_PROBE` 帧,用独立会话抓取该会话工作目录下可用的斜杠命令,回传 `CONVERSATION_COMMANDS_RESULT` 帧,让会话面板在用户输入 `/` 时列出真实可用的命令。该探测与回合执行完全隔离:任何失败只产生 `FAILED` / `TIMEOUT` 帧,不影响进行中的会话;provider 后端不支持抓取时同样降级为 `FAILED` 帧。帧里的 `executorId` 与本机不一致或非正数时直接忽略,避免在别的执行器工作区里建目录或启动 CLI。daemon 侧给整个探测 25 秒硬上限,刻意长于 agent 侧的 20 秒预算,使 agent 自己的超时先触发并可被归类。
|
|
39
41
|
|
|
40
42
|
给 `connect` 追加 `--token-aware-enable` 可开启 qoder / qodercn 的 token 用量采集:CLI 会以 sidecar 方式启动本地 HTTPS 转发代理(`bin/token-proxy.js`),从 Qoder API 的 SSE 响应流中提取 usage 数据并按 dispatch ID 归属上报。usage 解析失败只丢失计量数据;代理启动失败时仅告警并继续。启用后模型流量经过代理,连接失败由客户端处理重试,不保证自动直连。代理端口可用 `TOKEN_PROXY_PORT` 环境变量调整(默认 19876);CA 证书位于 `~/.autowonder/token-tap/ca-cert.pem`。daemon 退出时 sidecar 一并停止。代理对 TCP 建连和两端 TLS 握手设置 15 秒期限,对无读写活动的连接设置 120 秒空闲期限;超时、报错或任一端关闭时释放两端连接。持续有数据的 SSE 流不受总时长限制,连续静默超过 120 秒的流会断开。
|
|
@@ -158,7 +160,7 @@ mv "$queue/.assignment.tmp" "$queue/assignment.json"
|
|
|
158
160
|
也可以直接提交到本地 API:
|
|
159
161
|
|
|
160
162
|
```bash
|
|
161
|
-
npx -y autowonder@0.3.
|
|
163
|
+
npx -y autowonder@0.3.10 dispatch ./assignment.json
|
|
162
164
|
```
|
|
163
165
|
|
|
164
166
|
## 上传工单附件
|
|
@@ -166,7 +168,7 @@ npx -y autowonder@0.3.8 dispatch ./assignment.json
|
|
|
166
168
|
把本地需求/设计文档直接上传到工单,避免在会话里传递大文件:
|
|
167
169
|
|
|
168
170
|
```bash
|
|
169
|
-
npx -y autowonder@0.3.
|
|
171
|
+
npx -y autowonder@0.3.10 workitem upload --server-url <autowonder-server-url> --workitem-id <id> --file <filepath-1> --file <filepath-2> --json
|
|
170
172
|
```
|
|
171
173
|
|
|
172
174
|
上传令牌通过 MCP 工具 `autowonder.workitem_cli_upload_token` 签发,经 `--token` 或 `AUTOWONDER_UPLOAD_TOKEN` 环境变量传入。`--file` 可重复指定多个文件;`--json` 输出机器可读结果。失败时按错误类型返回不同退出码(401→3、403→4、404→5、409→6、413→7、其他 4xx→8、网络/重定向→9)。
|
|
@@ -176,7 +178,7 @@ npx -y autowonder@0.3.8 workitem upload --server-url <autowonder-server-url> --w
|
|
|
176
178
|
把工单上已有的需求/设计文档下载到本地目录,再用本地文件工具阅读,避免把大文件或二进制附件搬进会话上下文:
|
|
177
179
|
|
|
178
180
|
```bash
|
|
179
|
-
npx -y autowonder@0.3.
|
|
181
|
+
npx -y autowonder@0.3.10 workitem download --server-url <autowonder-server-url> --workitem-id <id> --file <name-or-id> --output-dir <dir> --json
|
|
180
182
|
```
|
|
181
183
|
|
|
182
184
|
下载令牌通过 MCP 工具 `autowonder.workitem_cli_download_token` 签发(与上传令牌相互独立),经 `--token` 或 `AUTOWONDER_DOWNLOAD_TOKEN` 环境变量传入。`--file` 可重复指定文档名或 artifact id,省略则下载该工单的全部附件;`--output-dir` 默认为当前目录;`--json` 输出机器可读结果,含每个文件的 `id`、`name`、`path`、`size`。退出码约定与 `workitem upload` 相同(401→3、403→4、404→5、409→6、413→7、其他 4xx→8、网络/重定向→9)。
|
|
@@ -186,7 +188,7 @@ npx -y autowonder@0.3.8 workitem download --server-url <autowonder-server-url> -
|
|
|
186
188
|
把需求/设计文档上传到 7x24 定时任务,用法与工单附件一致(同一套上传令牌与预检规则):
|
|
187
189
|
|
|
188
190
|
```bash
|
|
189
|
-
npx -y autowonder@0.3.
|
|
191
|
+
npx -y autowonder@0.3.10 scheduled-task upload --server-url <autowonder-server-url> --scheduled-task-id <id> --file <filepath-1> --file <filepath-2> --json
|
|
190
192
|
```
|
|
191
193
|
|
|
192
194
|
上传令牌同样通过 `autowonder.workitem_cli_upload_token` 签发(令牌为用户级,对当前可修改的任意定时任务生效),经 `--token` 或 `AUTOWONDER_UPLOAD_TOKEN` 传入;退出码约定与 `workitem upload` 相同。
|
|
@@ -194,8 +196,8 @@ npx -y autowonder@0.3.8 scheduled-task upload --server-url <autowonder-server-ur
|
|
|
194
196
|
## 管理 daemon
|
|
195
197
|
|
|
196
198
|
```bash
|
|
197
|
-
npx -y autowonder@0.3.
|
|
198
|
-
npx -y autowonder@0.3.
|
|
199
|
+
npx -y autowonder@0.3.10 status
|
|
200
|
+
npx -y autowonder@0.3.10 stop
|
|
199
201
|
```
|
|
200
202
|
|
|
201
203
|
默认 API 是 `http://127.0.0.1:34989`,日志位于 `~/.autowonder/daemon.log`。npm 包不包含任何 agent、MCP 或服务端凭证。
|
|
@@ -203,7 +205,7 @@ npx -y autowonder@0.3.8 stop
|
|
|
203
205
|
## 调试
|
|
204
206
|
|
|
205
207
|
```bash
|
|
206
|
-
npx -y autowonder@0.3.
|
|
208
|
+
npx -y autowonder@0.3.10 connect ... --debug
|
|
207
209
|
```
|
|
208
210
|
|
|
209
211
|
`--debug` 会让 CLI 以 `AUTOWONDER_DEBUG=1` 启动 daemon,把完整的 daemon 与 agent 活动(runtime 事件流、provider 事件解码、重试退避与会话门等待、空闲时的阻塞点)同时输出到控制台和会话调试日志。调试日志不做脱敏,仅用于本机排障;不传 `--debug` 时该环境变量不会注入。
|
package/bin/cli.js
CHANGED
|
@@ -1051,10 +1051,14 @@ function parseFlags(args, knownFlags) {
|
|
|
1051
1051
|
|
|
1052
1052
|
function ensureWindowsUTF8() {
|
|
1053
1053
|
if (process.platform !== "win32") return;
|
|
1054
|
-
|
|
1054
|
+
// chcp is a .com console tool and CreateProcess resolves no PATHEXT, so it is
|
|
1055
|
+
// addressed by full path. shell:true would join the code page into a cmd.exe
|
|
1056
|
+
// command line instead, which is what emits Node's DEP0190 warning.
|
|
1057
|
+
const chcp = path.join(process.env.SystemRoot || "C:\\Windows", "System32", "chcp.com");
|
|
1058
|
+
const result = spawnSync(chcp, [], { encoding: "utf8", timeout: 2000 });
|
|
1055
1059
|
const currentCP = (result.stdout || "").match(/:\s*(\d+)/)?.[1];
|
|
1056
1060
|
if (currentCP !== "65001") {
|
|
1057
|
-
spawnSync(
|
|
1061
|
+
spawnSync(chcp, ["65001"], { stdio: "ignore" });
|
|
1058
1062
|
}
|
|
1059
1063
|
process.env.PYTHONIOENCODING = process.env.PYTHONIOENCODING || "utf-8";
|
|
1060
1064
|
process.env.PYTHONUTF8 = process.env.PYTHONUTF8 || "1";
|
package/bin/qoder-bootstrap.js
CHANGED
|
@@ -340,23 +340,136 @@ function installQoderCLIUnlocked(provider) {
|
|
|
340
340
|
const MACHINE_ID_REL_PATH = path.join(".auth", "machine_id");
|
|
341
341
|
const CREDENTIAL_REL_PATH = path.join(".auth", "user");
|
|
342
342
|
|
|
343
|
-
|
|
344
|
-
|
|
343
|
+
// Both tree runners report a timeout as exit code 124, so a bound probe stays
|
|
344
|
+
// distinguishable from a CLI that really failed to start.
|
|
345
|
+
const QODER_PROBE_TIMEOUT_STATUS = 124;
|
|
346
|
+
const QODER_PROBE_OUTPUT_LIMIT = 1000;
|
|
347
|
+
|
|
348
|
+
function classifyQoderVersionProbe(result) {
|
|
349
|
+
if (result.error) {
|
|
350
|
+
if (result.error.code === "ENOENT") return "missing";
|
|
351
|
+
if (result.error.code === "ETIMEDOUT") return "timeout";
|
|
352
|
+
return "spawn_failed";
|
|
353
|
+
}
|
|
354
|
+
if (result.signal) return "failed";
|
|
355
|
+
if (result.status === null || result.status === undefined) return "timeout";
|
|
356
|
+
if (result.status === QODER_PROBE_TIMEOUT_STATUS) return "timeout";
|
|
357
|
+
if (result.status !== 0) return "failed";
|
|
358
|
+
return "unparseable";
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// The probe keeps the raw evidence of the invocation so a permanent failure can
|
|
362
|
+
// be told apart from a transient one, and so the operator sees exit code,
|
|
363
|
+
// signal, spawn error and output instead of a single opaque "unknown".
|
|
364
|
+
function probeQoderBinaryVersion(executable, options = {}) {
|
|
365
|
+
const absent = (path.isAbsolute(executable) || executable.includes(path.sep)) && !fs.existsSync(executable);
|
|
366
|
+
const probe = {
|
|
367
|
+
executable,
|
|
368
|
+
version: absent ? "missing" : "unknown",
|
|
369
|
+
outcome: absent ? "missing" : "failed",
|
|
370
|
+
status: null,
|
|
371
|
+
signal: null,
|
|
372
|
+
spawnError: "",
|
|
373
|
+
stdout: "",
|
|
374
|
+
stderr: "",
|
|
375
|
+
};
|
|
376
|
+
if (absent) return probe;
|
|
345
377
|
const result = spawnQoderSync(executable, ["--version"], {
|
|
346
378
|
encoding: "utf8",
|
|
347
379
|
stdio: ["ignore", "pipe", "pipe"],
|
|
348
380
|
timeout: options.timeout || 20000,
|
|
349
381
|
treeTimeout: true,
|
|
350
382
|
});
|
|
383
|
+
probe.status = result.status === undefined ? null : result.status;
|
|
384
|
+
probe.signal = result.signal || null;
|
|
385
|
+
probe.spawnError = result.error
|
|
386
|
+
? `${result.error.code ? `${result.error.code}: ` : ""}${result.error.message}`
|
|
387
|
+
: "";
|
|
388
|
+
probe.stdout = result.stdout || "";
|
|
389
|
+
probe.stderr = result.stderr || "";
|
|
390
|
+
probe.outcome = classifyQoderVersionProbe(result);
|
|
351
391
|
// "missing" means the file itself is absent; "unknown" means the file is
|
|
352
392
|
// there but produced no version, so the two failure classes never collapse
|
|
353
393
|
// into one misleading answer.
|
|
354
|
-
if (
|
|
355
|
-
|
|
356
|
-
//
|
|
357
|
-
//
|
|
358
|
-
//
|
|
359
|
-
|
|
394
|
+
if (probe.outcome === "missing") probe.version = "missing";
|
|
395
|
+
// Accept only an unambiguous Qoder version line, and only from a probe that
|
|
396
|
+
// exited zero. Runtime warnings and update banners often contain
|
|
397
|
+
// unrelated/newer semvers and must never satisfy the minimum gate for an old
|
|
398
|
+
// executable.
|
|
399
|
+
if (probe.outcome === "unparseable") {
|
|
400
|
+
const line = qoderVersionLine(`${probe.stdout}\n${probe.stderr}`);
|
|
401
|
+
if (line) {
|
|
402
|
+
probe.version = line;
|
|
403
|
+
probe.outcome = "ok";
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return probe;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function qoderBinaryVersion(executable, options = {}) {
|
|
410
|
+
return probeQoderBinaryVersion(executable, options).version;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// "missing" is a definitive filesystem answer. Every other non-ok outcome can
|
|
414
|
+
// be a one-off glitch (first run, a short-lived file lock, a slow antivirus),
|
|
415
|
+
// which is exactly the field failure this retry exists for.
|
|
416
|
+
function isTransientQoderVersionProbe(probe) {
|
|
417
|
+
return probe.outcome !== "ok" && probe.outcome !== "missing";
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// A single probe must not be trusted on its own: a CLI that is already up to
|
|
421
|
+
// date can answer nothing while its installation settles, and a just-updated
|
|
422
|
+
// CLI can need a moment before it reports the version it just installed.
|
|
423
|
+
// Re-probe on this bounded schedule instead of acting on one transient answer.
|
|
424
|
+
const QODER_VERSION_RETRY_DELAYS_MS = [300, 1000, 2000];
|
|
425
|
+
|
|
426
|
+
function qoderVersionRetryDelays() {
|
|
427
|
+
if (process.env.NODE_ENV === "test" && process.env.AUTOWONDER_TEST_QODER_VERSION_RETRY_DELAY_MS) {
|
|
428
|
+
const override = Number.parseInt(process.env.AUTOWONDER_TEST_QODER_VERSION_RETRY_DELAY_MS, 10);
|
|
429
|
+
if (Number.isFinite(override) && override >= 0) return QODER_VERSION_RETRY_DELAYS_MS.map(() => override);
|
|
430
|
+
}
|
|
431
|
+
return QODER_VERSION_RETRY_DELAYS_MS;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// The bootstrap runs before any event-loop work, so the wait blocks exactly
|
|
435
|
+
// like the update lock's own queue does.
|
|
436
|
+
function sleepMs(milliseconds) {
|
|
437
|
+
if (milliseconds > 0) Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function describeQoderVersionProbe(probe) {
|
|
441
|
+
const status = probe.status === null ? "none" : probe.status;
|
|
442
|
+
return `version ${probe.version}, outcome ${probe.outcome}, exit code ${status}, signal ${probe.signal || "none"}, spawn error ${probe.spawnError || "none"}`;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// A chatty or hostile CLI must not be able to flood the operator's log.
|
|
446
|
+
function boundedQoderProbeOutput(text) {
|
|
447
|
+
const value = String(text || "").replace(/\s+/g, " ").trim();
|
|
448
|
+
if (!value) return "<empty>";
|
|
449
|
+
if (value.length <= QODER_PROBE_OUTPUT_LIMIT) return value;
|
|
450
|
+
return `${value.slice(0, QODER_PROBE_OUTPUT_LIMIT)}... (${value.length} characters total)`;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function reportQoderVersionProbe(probe) {
|
|
454
|
+
error(`probe outcome : ${probe.outcome}`);
|
|
455
|
+
error(`probe exit code : ${probe.status === null ? "none" : probe.status}`);
|
|
456
|
+
error(`probe signal : ${probe.signal || "none"}`);
|
|
457
|
+
error(`probe spawn err : ${probe.spawnError || "none"}`);
|
|
458
|
+
error(`probe stdout : ${boundedQoderProbeOutput(probe.stdout)}`);
|
|
459
|
+
error(`probe stderr : ${boundedQoderProbeOutput(probe.stderr)}`);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Re-probes until `accept` is satisfied or the bounded schedule is spent,
|
|
463
|
+
// reusing `initialProbe` when the caller has already probed once.
|
|
464
|
+
function probeQoderVersionUntil(profile, executable, accept, initialProbe) {
|
|
465
|
+
let probe = initialProbe || probeQoderBinaryVersion(executable);
|
|
466
|
+
const delays = qoderVersionRetryDelays();
|
|
467
|
+
for (let step = 0; step < delays.length && !accept(probe); step += 1) {
|
|
468
|
+
log(`${profile.displayName} version probe did not settle (${describeQoderVersionProbe(probe)}); re-probing in ${delays[step]}ms...`);
|
|
469
|
+
sleepMs(delays[step]);
|
|
470
|
+
probe = probeQoderBinaryVersion(executable);
|
|
471
|
+
}
|
|
472
|
+
return probe;
|
|
360
473
|
}
|
|
361
474
|
|
|
362
475
|
function qoderVersionLine(output) {
|
|
@@ -568,10 +681,16 @@ function ensureMinimumQoderVersion(profile, provider, executable) {
|
|
|
568
681
|
}
|
|
569
682
|
|
|
570
683
|
function ensureMinimumQoderVersionLocked(profile, provider, executable) {
|
|
571
|
-
let
|
|
572
|
-
if (
|
|
684
|
+
let probe = probeQoderBinaryVersion(executable);
|
|
685
|
+
if (isTransientQoderVersionProbe(probe)) {
|
|
686
|
+
// An up-to-date CLI answers nothing far more often than it is really too
|
|
687
|
+
// old, so confirm the failure before paying for a download.
|
|
688
|
+
log(`${profile.displayName} did not report a version on the first probe (${describeQoderVersionProbe(probe)}); re-probing before updating...`);
|
|
689
|
+
probe = probeQoderVersionUntil(profile, executable, (candidate) => candidate.outcome === "ok", probe);
|
|
690
|
+
}
|
|
691
|
+
if (qoderVersionMeetsMinimum(probe.version)) return { ok: true, version: probe.version };
|
|
573
692
|
|
|
574
|
-
log(`${profile.displayName} ${version} is older than required version 1.1.50 (or could not be parsed). Updating the current installation to latest...`);
|
|
693
|
+
log(`${profile.displayName} ${probe.version} is older than required version 1.1.50 (or could not be parsed). Updating the current installation to latest...`);
|
|
575
694
|
const update = spawnQoderSync(executable, ["update"], { stdio: "inherit", timeout: 5 * 60 * 1000, treeTimeout: true });
|
|
576
695
|
if (update.status !== 0) {
|
|
577
696
|
const managedDir = qoderPaths(profile).managedDir;
|
|
@@ -579,22 +698,26 @@ function ensureMinimumQoderVersionLocked(profile, provider, executable) {
|
|
|
579
698
|
const reinstalled = installQoderCLI(provider, { lockHeld: true });
|
|
580
699
|
if (reinstalled) {
|
|
581
700
|
executable = reinstalled;
|
|
582
|
-
|
|
583
|
-
if (qoderVersionMeetsMinimum(version)) return { ok: true, version, executable };
|
|
701
|
+
probe = probeQoderVersionUntil(profile, executable, (candidate) => qoderVersionMeetsMinimum(candidate.version));
|
|
702
|
+
if (qoderVersionMeetsMinimum(probe.version)) return { ok: true, version: probe.version, executable };
|
|
584
703
|
}
|
|
585
704
|
}
|
|
586
705
|
const detail = update.error?.message || (update.signal ? `signal ${update.signal}` : `exit status ${update.status}`);
|
|
587
706
|
error(`${profile.displayName} requires version 1.1.50 or newer, but updating ${executable} failed: ${detail}`);
|
|
588
|
-
|
|
707
|
+
reportQoderVersionProbe(probe);
|
|
708
|
+
return { ok: false, version: probe.version, executable };
|
|
589
709
|
}
|
|
590
710
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
711
|
+
// The update reported success; give the installation the bounded schedule to
|
|
712
|
+
// start answering with the version it just installed.
|
|
713
|
+
probe = probeQoderVersionUntil(profile, executable, (candidate) => qoderVersionMeetsMinimum(candidate.version));
|
|
714
|
+
if (!qoderVersionMeetsMinimum(probe.version)) {
|
|
715
|
+
error(`${profile.displayName} requires version 1.1.50 or newer, but ${executable} reports ${probe.version} after updating to latest.`);
|
|
716
|
+
reportQoderVersionProbe(probe);
|
|
717
|
+
return { ok: false, version: probe.version, executable };
|
|
595
718
|
}
|
|
596
|
-
log(`${profile.displayName} updated in place to ${version}.`);
|
|
597
|
-
return { ok: true, version, executable };
|
|
719
|
+
log(`${profile.displayName} updated in place to ${probe.version}.`);
|
|
720
|
+
return { ok: true, version: probe.version, executable };
|
|
598
721
|
}
|
|
599
722
|
|
|
600
723
|
// A missing or unreadable record means "no history", never a bootstrap failure.
|
|
@@ -1088,6 +1211,8 @@ async function ensureQoderReady(provider, options = {}) {
|
|
|
1088
1211
|
|
|
1089
1212
|
module.exports = {
|
|
1090
1213
|
QODER_FAMILY_PROFILES,
|
|
1214
|
+
QODER_VERSION_RETRY_DELAYS_MS,
|
|
1215
|
+
classifyQoderVersionProbe,
|
|
1091
1216
|
ensureMinimumQoderVersion,
|
|
1092
1217
|
ensureQoderReady,
|
|
1093
1218
|
findInInstallDirs,
|
|
@@ -1096,6 +1221,7 @@ module.exports = {
|
|
|
1096
1221
|
inspectQoderHealth,
|
|
1097
1222
|
installQoderCLIUnlocked,
|
|
1098
1223
|
isQoderFamilyProvider,
|
|
1224
|
+
probeQoderBinaryVersion,
|
|
1099
1225
|
qoderBinaryVersion,
|
|
1100
1226
|
qoderChildEnv,
|
|
1101
1227
|
qoderUpdateLockPaths,
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|