@co0ontty/wand 4.72.2 → 4.75.0-beta.g1d477a2
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/dist/build-info.json +4 -4
- package/dist/claude-sdk-runner.d.ts +2 -3
- package/dist/config.d.ts +1 -1
- package/dist/config.js +48 -0
- package/dist/daemon-connection.d.ts +9 -0
- package/dist/daemon-connection.js +86 -0
- package/dist/distribution-manager.d.ts +5 -0
- package/dist/distribution-manager.js +79 -1
- package/dist/git-quick-commit.js +12 -0
- package/dist/message-truncator.js +9 -3
- package/dist/models.d.ts +39 -2
- package/dist/models.js +302 -9
- package/dist/native/darwin-arm64/wand-render +0 -0
- package/dist/native/darwin-arm64/wand-render.sha256 +1 -0
- package/dist/native/darwin-arm64/wand-render.version +1 -0
- package/dist/native/darwin-arm64/wand-structured-renderd +0 -0
- package/dist/native/darwin-arm64/wand-structured-renderd.sha256 +1 -0
- package/dist/native/darwin-arm64/wand-structured-renderd.version +1 -0
- package/dist/native/darwin-x64/wand-render +0 -0
- package/dist/native/darwin-x64/wand-render.sha256 +1 -0
- package/dist/native/darwin-x64/wand-render.version +1 -0
- package/dist/native/darwin-x64/wand-structured-renderd +0 -0
- package/dist/native/darwin-x64/wand-structured-renderd.sha256 +1 -0
- package/dist/native/darwin-x64/wand-structured-renderd.version +1 -0
- package/dist/native/linux-arm64/wand-render +0 -0
- package/dist/native/linux-arm64/wand-render.sha256 +1 -0
- package/dist/native/linux-arm64/wand-render.version +1 -0
- package/dist/native/linux-arm64/wand-structured-renderd +0 -0
- package/dist/native/linux-arm64/wand-structured-renderd.sha256 +1 -0
- package/dist/native/linux-arm64/wand-structured-renderd.version +1 -0
- package/dist/native/linux-x64/wand-render +0 -0
- package/dist/native/linux-x64/wand-render.sha256 +1 -0
- package/dist/native/linux-x64/wand-render.version +1 -0
- package/dist/native/linux-x64/wand-structured-renderd +0 -0
- package/dist/native/linux-x64/wand-structured-renderd.sha256 +1 -0
- package/dist/native/linux-x64/wand-structured-renderd.version +1 -0
- package/dist/process-manager.d.ts +12 -1
- package/dist/process-manager.js +88 -7
- package/dist/pty-terminal-state.d.ts +17 -0
- package/dist/pty-terminal-state.js +51 -1
- package/dist/render-binary.d.ts +61 -0
- package/dist/render-binary.js +204 -0
- package/dist/render-daemon-client.d.ts +202 -0
- package/dist/render-daemon-client.js +1164 -0
- package/dist/render-host.d.ts +88 -0
- package/dist/render-host.js +440 -0
- package/dist/render-protocol.d.ts +128 -0
- package/dist/render-protocol.js +99 -0
- package/dist/render-structured-client.d.ts +80 -0
- package/dist/render-structured-client.js +513 -0
- package/dist/render-structured-host.d.ts +31 -0
- package/dist/render-structured-host.js +218 -0
- package/dist/render-structured-protocol.d.ts +101 -0
- package/dist/render-structured-protocol.js +35 -0
- package/dist/server-session-routes.js +114 -12
- package/dist/server-task-routes.js +55 -6
- package/dist/server-update-routes.d.ts +2 -0
- package/dist/server-update-routes.js +44 -0
- package/dist/server.js +43 -13
- package/dist/session-ai-context.d.ts +1 -1
- package/dist/session-ai-context.js +15 -4
- package/dist/session-logger.d.ts +2 -0
- package/dist/session-logger.js +44 -1
- package/dist/session-transport.js +1 -1
- package/dist/storage.d.ts +2 -1
- package/dist/storage.js +14 -5
- package/dist/structured-client-protocol.d.ts +3 -1
- package/dist/structured-client-protocol.js +85 -25
- package/dist/structured-content.d.ts +20 -3
- package/dist/structured-content.js +80 -1
- package/dist/structured-exec-host.js +7 -1
- package/dist/structured-exec-pump.js +6 -2
- package/dist/structured-grok-adapter.js +22 -0
- package/dist/structured-pi-adapter.js +18 -2
- package/dist/structured-provider-common.d.ts +11 -9
- package/dist/structured-provider-common.js +60 -18
- package/dist/structured-session-manager.d.ts +2 -0
- package/dist/structured-session-manager.js +27 -3
- package/dist/task-types.d.ts +4 -1
- package/dist/terminal-daemon-client.d.ts +9 -2
- package/dist/terminal-daemon-client.js +119 -46
- package/dist/terminal-daemon-server.js +47 -8
- package/dist/terminal-host.d.ts +6 -0
- package/dist/terminal-host.js +3 -0
- package/dist/tui/commands.js +183 -10
- package/dist/tui/ipc-server.js +12 -0
- package/dist/types.d.ts +30 -10
- package/dist/unix-socket-keepalive.d.ts +3 -0
- package/dist/unix-socket-keepalive.js +94 -0
- package/dist/wand-task-sync.js +2 -1
- package/dist/web-ui/content/scripts.js +79 -73
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/content/tailwind.css +21 -0
- package/dist/web-ui/embedded-assets.js +2 -2
- package/dist/web-ui/thinking-efforts.d.ts +24 -0
- package/dist/web-ui/thinking-efforts.js +81 -0
- package/dist/ws-broadcast.d.ts +1 -1
- package/dist/ws-broadcast.js +28 -9
- package/package.json +8 -3
- package/scripts/install-render-binary.js +499 -0
- package/scripts/stage-render-binaries.js +626 -0
package/dist/models.js
CHANGED
|
@@ -57,6 +57,28 @@ const QODER_FALLBACK_MODELS = [
|
|
|
57
57
|
const PI_FALLBACK_MODELS = [
|
|
58
58
|
{ id: "default", label: "跟随 Pi 默认", alias: true },
|
|
59
59
|
];
|
|
60
|
+
const EFFORT_ID_PATTERN = /^[a-z][a-z0-9_-]{0,31}$/;
|
|
61
|
+
function effortLevels(ids) {
|
|
62
|
+
return ids.map((effort) => ({ effort }));
|
|
63
|
+
}
|
|
64
|
+
/** 探针失败时的已知档位。下一次 CLI 成功应答会把它换掉。 */
|
|
65
|
+
export const FALLBACK_PROVIDER_THINKING = {
|
|
66
|
+
claude: effortLevels(["low", "medium", "high", "xhigh", "max"]),
|
|
67
|
+
opencode: effortLevels(["low", "medium", "high", "max"]),
|
|
68
|
+
grok: effortLevels(["low", "medium", "high", "xhigh"]),
|
|
69
|
+
qoder: effortLevels(["auto", "none", "low", "medium", "high", "xhigh", "max", "ultracode"]),
|
|
70
|
+
pi: effortLevels(["minimal", "low", "medium", "high", "xhigh", "max"]),
|
|
71
|
+
};
|
|
72
|
+
function cloneThinkingEfforts(efforts) {
|
|
73
|
+
const copy = (levels) => levels.map((level) => ({ ...level }));
|
|
74
|
+
return {
|
|
75
|
+
claude: copy(efforts.claude),
|
|
76
|
+
opencode: copy(efforts.opencode),
|
|
77
|
+
grok: copy(efforts.grok),
|
|
78
|
+
qoder: copy(efforts.qoder),
|
|
79
|
+
pi: copy(efforts.pi),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
60
82
|
function cloneModels(models) {
|
|
61
83
|
return models.map((model) => ({
|
|
62
84
|
...model,
|
|
@@ -73,6 +95,7 @@ function cloneCache(cache) {
|
|
|
73
95
|
grokModels: cloneModels(cache.grokModels),
|
|
74
96
|
qoderModels: cloneModels(cache.qoderModels),
|
|
75
97
|
piModels: cloneModels(cache.piModels),
|
|
98
|
+
thinkingEfforts: cloneThinkingEfforts(cache.thinkingEfforts),
|
|
76
99
|
claudeVersion: cache.claudeVersion,
|
|
77
100
|
opencodeVersion: cache.opencodeVersion,
|
|
78
101
|
refreshedAt: cache.refreshedAt,
|
|
@@ -82,7 +105,8 @@ function defaultCommandRunner(file, args, options) {
|
|
|
82
105
|
return execFileAsync(file, args, {
|
|
83
106
|
env: options.env,
|
|
84
107
|
timeout: options.timeout,
|
|
85
|
-
maxBuffer: 1024 * 1024,
|
|
108
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
109
|
+
...(options.input !== undefined ? { input: options.input } : {}),
|
|
86
110
|
}).then(({ stdout, stderr }) => ({ stdout: String(stdout), stderr: String(stderr) }));
|
|
87
111
|
}
|
|
88
112
|
function normalizeClaudeModelId(value) {
|
|
@@ -238,6 +262,7 @@ function createInitialCache(options) {
|
|
|
238
262
|
grokModels: cloneModels(GROK_FALLBACK_MODELS),
|
|
239
263
|
qoderModels: cloneModels(QODER_FALLBACK_MODELS),
|
|
240
264
|
piModels: cloneModels(PI_FALLBACK_MODELS),
|
|
265
|
+
thinkingEfforts: cloneThinkingEfforts(FALLBACK_PROVIDER_THINKING),
|
|
241
266
|
claudeVersion: null,
|
|
242
267
|
opencodeVersion: null,
|
|
243
268
|
refreshedAt: now.toISOString(),
|
|
@@ -275,11 +300,11 @@ async function probeCodexModels(runner, env) {
|
|
|
275
300
|
}
|
|
276
301
|
async function probeOpenCode(runner, env) {
|
|
277
302
|
const [modelsResult, versionResult] = await Promise.allSettled([
|
|
278
|
-
runner("opencode", ["models"], { env, timeout: 8000 }),
|
|
303
|
+
runner("opencode", ["models", "--verbose"], { env, timeout: 8000 }),
|
|
279
304
|
runner("opencode", ["--version"], { env, timeout: 5000 }),
|
|
280
305
|
]);
|
|
281
306
|
const models = modelsResult.status === "fulfilled"
|
|
282
|
-
? { ok: true, value:
|
|
307
|
+
? { ok: true, value: parseOpenCodeModelCatalog(modelsResult.value.stdout) }
|
|
283
308
|
: { ok: false };
|
|
284
309
|
const version = versionResult.status === "fulfilled"
|
|
285
310
|
? {
|
|
@@ -308,14 +333,143 @@ async function probeQoderModels(runner, env) {
|
|
|
308
333
|
}
|
|
309
334
|
}
|
|
310
335
|
async function probePiModels(runner, env) {
|
|
336
|
+
const rpcText = await commandText(runner, "pi", ["--mode", "rpc", "--no-session"], env, 12_000, `${JSON.stringify({ id: "wand-models", type: "get_available_models" })}\n`);
|
|
337
|
+
const fromRpc = parsePiRpcModels(rpcText);
|
|
338
|
+
if (fromRpc.length > 1 || (fromRpc.length === 1 && fromRpc[0]?.id !== "default")) {
|
|
339
|
+
return { ok: true, value: fromRpc };
|
|
340
|
+
}
|
|
311
341
|
try {
|
|
312
342
|
const { stdout } = await runner("pi", ["--list-models"], { env, timeout: 8000 });
|
|
313
|
-
|
|
343
|
+
const parsed = parsePiModels(stdout);
|
|
344
|
+
return parsed.some((model) => model.id !== "default") ? { ok: true, value: parsed } : { ok: false };
|
|
314
345
|
}
|
|
315
346
|
catch {
|
|
316
347
|
return { ok: false };
|
|
317
348
|
}
|
|
318
349
|
}
|
|
350
|
+
/** 从 CLI 的 help / 非法档位报错里抽出 `low, medium, high` 这种列表。 */
|
|
351
|
+
export function parseCliEffortList(text) {
|
|
352
|
+
const patterns = [
|
|
353
|
+
/valid values(?:\s+are)?\s*:\s*([^.`\n]+)/i,
|
|
354
|
+
/use one of:\s*([^.`\n]+)/i,
|
|
355
|
+
/set thinking level:\s*([^.`\n]+)/i,
|
|
356
|
+
];
|
|
357
|
+
for (const pattern of patterns) {
|
|
358
|
+
const match = text.match(pattern);
|
|
359
|
+
if (!match?.[1])
|
|
360
|
+
continue;
|
|
361
|
+
const ids = match[1]
|
|
362
|
+
.split(/[,/]/)
|
|
363
|
+
.map((part) => part.trim().toLowerCase())
|
|
364
|
+
.filter((part) => EFFORT_ID_PATTERN.test(part));
|
|
365
|
+
if (ids.length)
|
|
366
|
+
return Array.from(new Set(ids));
|
|
367
|
+
}
|
|
368
|
+
return [];
|
|
369
|
+
}
|
|
370
|
+
function effortLevelsFromIds(ids) {
|
|
371
|
+
return ids.map((effort) => ({ effort }));
|
|
372
|
+
}
|
|
373
|
+
async function commandText(runner, file, args, env, timeout, input) {
|
|
374
|
+
try {
|
|
375
|
+
const result = await runner(file, args, { env, timeout, ...(input !== undefined ? { input } : {}) });
|
|
376
|
+
return `${result.stderr}\n${result.stdout}`;
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
const record = error;
|
|
380
|
+
return `${String(record.stderr ?? "")}\n${String(record.stdout ?? "")}\n${String(record.message ?? "")}`;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
async function probeEffortList(runner, env, file, args) {
|
|
384
|
+
const text = await commandText(runner, file, args, env, 8000);
|
|
385
|
+
const ids = parseCliEffortList(text);
|
|
386
|
+
return ids.length ? { ok: true, value: effortLevelsFromIds(ids) } : { ok: false };
|
|
387
|
+
}
|
|
388
|
+
function variantsToEfforts(value) {
|
|
389
|
+
const ids = [];
|
|
390
|
+
if (Array.isArray(value)) {
|
|
391
|
+
for (const item of value) {
|
|
392
|
+
if (typeof item === "string")
|
|
393
|
+
ids.push(item);
|
|
394
|
+
else if (isRecord(item) && typeof item.effort === "string")
|
|
395
|
+
ids.push(item.effort);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
else if (isRecord(value)) {
|
|
399
|
+
ids.push(...Object.keys(value));
|
|
400
|
+
}
|
|
401
|
+
const unique = Array.from(new Set(ids.map((id) => id.trim().toLowerCase()).filter((id) => EFFORT_ID_PATTERN.test(id))));
|
|
402
|
+
return effortLevelsFromIds(unique);
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* `opencode models --verbose` 是「一行 id + 一段 JSON」。
|
|
406
|
+
* variants 是这个模型自己的推理档位;没有 JSON 时退回一行一个 id。
|
|
407
|
+
*/
|
|
408
|
+
export function parseOpenCodeModelCatalog(stdout) {
|
|
409
|
+
const verbose = parseOpenCodeVerboseModels(stdout);
|
|
410
|
+
if (verbose.length)
|
|
411
|
+
return verbose;
|
|
412
|
+
return parseOpenCodeModels(stdout);
|
|
413
|
+
}
|
|
414
|
+
function parseOpenCodeVerboseModels(stdout) {
|
|
415
|
+
const lines = stdout.split(/\r?\n/);
|
|
416
|
+
const found = [];
|
|
417
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
418
|
+
const header = lines[index]?.trim() ?? "";
|
|
419
|
+
if (!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:/-]*$/i.test(header))
|
|
420
|
+
continue;
|
|
421
|
+
if ((lines[index + 1] ?? "").trim() !== "{")
|
|
422
|
+
continue;
|
|
423
|
+
let depth = 0;
|
|
424
|
+
let end = index + 1;
|
|
425
|
+
for (; end < lines.length; end += 1) {
|
|
426
|
+
for (const character of lines[end] ?? "") {
|
|
427
|
+
if (character === "{")
|
|
428
|
+
depth += 1;
|
|
429
|
+
else if (character === "}")
|
|
430
|
+
depth -= 1;
|
|
431
|
+
}
|
|
432
|
+
if (depth === 0)
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
if (depth !== 0)
|
|
436
|
+
continue;
|
|
437
|
+
let record = null;
|
|
438
|
+
try {
|
|
439
|
+
record = JSON.parse(lines.slice(index + 1, end + 1).join("\n"));
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
record = null;
|
|
443
|
+
}
|
|
444
|
+
const reasoningEfforts = record ? variantsToEfforts(record.variants) : [];
|
|
445
|
+
found.push({
|
|
446
|
+
id: header,
|
|
447
|
+
label: header,
|
|
448
|
+
...(reasoningEfforts.length ? { reasoningEfforts } : {}),
|
|
449
|
+
});
|
|
450
|
+
index = end;
|
|
451
|
+
}
|
|
452
|
+
if (!found.length)
|
|
453
|
+
return [];
|
|
454
|
+
return [
|
|
455
|
+
{ id: "default", label: "跟随 OpenCode 默认", alias: true },
|
|
456
|
+
...found,
|
|
457
|
+
];
|
|
458
|
+
}
|
|
459
|
+
function unionModelEfforts(models) {
|
|
460
|
+
const seen = new Set();
|
|
461
|
+
const levels = [];
|
|
462
|
+
for (const model of models) {
|
|
463
|
+
for (const level of model.reasoningEfforts ?? []) {
|
|
464
|
+
const effort = level.effort.trim().toLowerCase();
|
|
465
|
+
if (!EFFORT_ID_PATTERN.test(effort) || seen.has(effort))
|
|
466
|
+
continue;
|
|
467
|
+
seen.add(effort);
|
|
468
|
+
levels.push({ effort, ...(level.description ? { description: level.description } : {}) });
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return levels;
|
|
472
|
+
}
|
|
319
473
|
function createOfficialModelsApi(apiKey) {
|
|
320
474
|
const client = new Anthropic({ apiKey });
|
|
321
475
|
return {
|
|
@@ -400,7 +554,7 @@ export function parseGrokModels(stdout) {
|
|
|
400
554
|
defaultModel = defaultMatch[1];
|
|
401
555
|
continue;
|
|
402
556
|
}
|
|
403
|
-
const bulletMatch = line.match(
|
|
557
|
+
const bulletMatch = line.match(/^[-*•]+\s+([A-Za-z0-9][A-Za-z0-9._:-]{0,127})(?:\s*\(.*\))?\s*$/);
|
|
404
558
|
if (bulletMatch) {
|
|
405
559
|
ids.push(bulletMatch[1]);
|
|
406
560
|
continue;
|
|
@@ -460,12 +614,88 @@ export function parseQoderModels(stdout) {
|
|
|
460
614
|
}
|
|
461
615
|
return [...cloneModels(QODER_FALLBACK_MODELS), ...discovered];
|
|
462
616
|
}
|
|
617
|
+
const PI_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
618
|
+
/**
|
|
619
|
+
* Same rule as Pi's `getSupportedThinkingLevels`: no reasoning means only off;
|
|
620
|
+
* `xhigh` / `max` exist only when `thinkingLevelMap` defines them; `null` drops a level.
|
|
621
|
+
*/
|
|
622
|
+
export function piThinkingLevelsForModel(model) {
|
|
623
|
+
if (!model.reasoning)
|
|
624
|
+
return ["off"];
|
|
625
|
+
const map = model.thinkingLevelMap ?? {};
|
|
626
|
+
return PI_THINKING_LEVELS.filter((level) => {
|
|
627
|
+
if (!Object.prototype.hasOwnProperty.call(map, level)) {
|
|
628
|
+
return level !== "xhigh" && level !== "max";
|
|
629
|
+
}
|
|
630
|
+
return map[level] !== null;
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
/** `pi --mode rpc` + `get_available_models`. One JSON response holds every model's thinking map. */
|
|
634
|
+
export function parsePiRpcModels(stdout) {
|
|
635
|
+
let models = null;
|
|
636
|
+
for (const rawLine of stdout.split(/\r?\n/)) {
|
|
637
|
+
const line = rawLine.trim();
|
|
638
|
+
if (!line.startsWith("{"))
|
|
639
|
+
continue;
|
|
640
|
+
let record = null;
|
|
641
|
+
try {
|
|
642
|
+
record = JSON.parse(line);
|
|
643
|
+
}
|
|
644
|
+
catch {
|
|
645
|
+
record = null;
|
|
646
|
+
}
|
|
647
|
+
if (!record || record.type !== "response" || record.command !== "get_available_models" || record.success !== true) {
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
const data = isRecord(record.data) ? record.data : null;
|
|
651
|
+
if (data && Array.isArray(data.models))
|
|
652
|
+
models = data.models;
|
|
653
|
+
}
|
|
654
|
+
if (!models)
|
|
655
|
+
return [];
|
|
656
|
+
const discovered = [];
|
|
657
|
+
const seen = new Set();
|
|
658
|
+
for (const entry of models) {
|
|
659
|
+
if (!isRecord(entry))
|
|
660
|
+
continue;
|
|
661
|
+
const provider = typeof entry.provider === "string" ? entry.provider.trim() : "";
|
|
662
|
+
const modelId = typeof entry.id === "string" ? entry.id.trim() : "";
|
|
663
|
+
const id = provider && modelId ? `${provider}/${modelId}` : "";
|
|
664
|
+
if (!id || !PI_MODEL_ID_PATTERN.test(id) || id.length > 128 || seen.has(id))
|
|
665
|
+
continue;
|
|
666
|
+
seen.add(id);
|
|
667
|
+
const name = typeof entry.name === "string" && entry.name.trim() ? entry.name.trim() : id;
|
|
668
|
+
const reasoningEfforts = effortLevelsFromIds(piThinkingLevelsForModel({
|
|
669
|
+
reasoning: entry.reasoning === true,
|
|
670
|
+
thinkingLevelMap: isRecord(entry.thinkingLevelMap) || entry.thinkingLevelMap === null
|
|
671
|
+
? entry.thinkingLevelMap
|
|
672
|
+
: undefined,
|
|
673
|
+
}));
|
|
674
|
+
discovered.push({
|
|
675
|
+
id,
|
|
676
|
+
label: name === id ? id : `${name} · ${id}`,
|
|
677
|
+
...(reasoningEfforts.length ? { reasoningEfforts } : {}),
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
if (!discovered.length)
|
|
681
|
+
return [];
|
|
682
|
+
const union = unionModelEfforts(discovered);
|
|
683
|
+
return [
|
|
684
|
+
{
|
|
685
|
+
id: "default",
|
|
686
|
+
label: "跟随 Pi 默认",
|
|
687
|
+
alias: true,
|
|
688
|
+
...(union.length ? { reasoningEfforts: union } : {}),
|
|
689
|
+
},
|
|
690
|
+
...discovered,
|
|
691
|
+
];
|
|
692
|
+
}
|
|
463
693
|
/**
|
|
464
694
|
* Parse `pi --list-models`.
|
|
465
695
|
*
|
|
466
696
|
* Pi prints a padEnd-aligned table whose selectable `--model` value is
|
|
467
|
-
* `provider/id` (for example `xai/grok-4.6`). The
|
|
468
|
-
*
|
|
697
|
+
* `provider/id` (for example `xai/grok-4.6`). The table only says thinking
|
|
698
|
+
* yes/no, so per-model levels come from `parsePiRpcModels` instead.
|
|
469
699
|
*/
|
|
470
700
|
export function parsePiModels(stdout) {
|
|
471
701
|
const discovered = [];
|
|
@@ -566,6 +796,7 @@ function catalogRevision(cache) {
|
|
|
566
796
|
grokModels: cache.grokModels,
|
|
567
797
|
qoderModels: cache.qoderModels,
|
|
568
798
|
piModels: cache.piModels,
|
|
799
|
+
thinkingEfforts: cache.thinkingEfforts,
|
|
569
800
|
claudeVersion: cache.claudeVersion,
|
|
570
801
|
opencodeVersion: cache.opencodeVersion,
|
|
571
802
|
});
|
|
@@ -637,6 +868,35 @@ function parsePersistedModelList(value) {
|
|
|
637
868
|
}
|
|
638
869
|
return result;
|
|
639
870
|
}
|
|
871
|
+
function parsePersistedThinkingEfforts(value) {
|
|
872
|
+
const fallback = cloneThinkingEfforts(FALLBACK_PROVIDER_THINKING);
|
|
873
|
+
if (!isRecord(value))
|
|
874
|
+
return fallback;
|
|
875
|
+
const providers = ["claude", "opencode", "grok", "qoder", "pi"];
|
|
876
|
+
for (const provider of providers) {
|
|
877
|
+
const levels = parsePersistedEffortLevels(value[provider]);
|
|
878
|
+
if (levels)
|
|
879
|
+
fallback[provider] = levels;
|
|
880
|
+
}
|
|
881
|
+
return fallback;
|
|
882
|
+
}
|
|
883
|
+
function parsePersistedEffortLevels(value) {
|
|
884
|
+
if (!Array.isArray(value))
|
|
885
|
+
return null;
|
|
886
|
+
const levels = [];
|
|
887
|
+
const seen = new Set();
|
|
888
|
+
for (const entry of value) {
|
|
889
|
+
if (!isRecord(entry))
|
|
890
|
+
return null;
|
|
891
|
+
const effort = safePersistedString(entry.effort, 32)?.toLowerCase() ?? "";
|
|
892
|
+
if (!EFFORT_ID_PATTERN.test(effort) || seen.has(effort))
|
|
893
|
+
return null;
|
|
894
|
+
seen.add(effort);
|
|
895
|
+
const description = safePersistedString(entry.description);
|
|
896
|
+
levels.push({ effort, ...(description ? { description } : {}) });
|
|
897
|
+
}
|
|
898
|
+
return levels.length ? levels : null;
|
|
899
|
+
}
|
|
640
900
|
function parsePersistedModelCatalog(value) {
|
|
641
901
|
if (!isRecord(value) || value.version !== MODEL_CATALOG_CACHE_VERSION || !isRecord(value.catalog))
|
|
642
902
|
return null;
|
|
@@ -664,6 +924,7 @@ function parsePersistedModelCatalog(value) {
|
|
|
664
924
|
grokModels,
|
|
665
925
|
qoderModels,
|
|
666
926
|
piModels,
|
|
927
|
+
thinkingEfforts: parsePersistedThinkingEfforts(catalog.thinkingEfforts),
|
|
667
928
|
claudeVersion,
|
|
668
929
|
opencodeVersion,
|
|
669
930
|
refreshedAt,
|
|
@@ -703,7 +964,7 @@ async function discoverModelCache(options, previous) {
|
|
|
703
964
|
const now = options.now?.() ?? new Date();
|
|
704
965
|
const env = resolveChildEnv(options);
|
|
705
966
|
const runner = options.commandRunner ?? defaultCommandRunner;
|
|
706
|
-
const [claudeVersionProbe, codexProbe, opencodeProbe, grokProbe, qoderProbe, piProbe, apiProbe] = await Promise.all([
|
|
967
|
+
const [claudeVersionProbe, codexProbe, opencodeProbe, grokProbe, qoderProbe, piProbe, apiProbe, claudeEffortProbe, grokEffortProbe, qoderEffortProbe, piEffortProbe,] = await Promise.all([
|
|
707
968
|
probeClaudeVersion(runner, env),
|
|
708
969
|
probeCodexModels(runner, env),
|
|
709
970
|
probeOpenCode(runner, env),
|
|
@@ -711,6 +972,10 @@ async function discoverModelCache(options, previous) {
|
|
|
711
972
|
probeQoderModels(runner, env),
|
|
712
973
|
probePiModels(runner, env),
|
|
713
974
|
listClaudeModelsFromApi(options, env),
|
|
975
|
+
probeEffortList(runner, env, "claude", ["--effort", "__wand_probe__", "--help"]),
|
|
976
|
+
probeEffortList(runner, env, "grok", ["--effort", "__wand_probe__", "-p", "x", "--output-format", "streaming-json", "--max-turns", "1"]),
|
|
977
|
+
probeEffortList(runner, env, "qodercli", ["-p", "x", "--reasoning-effort", "__wand_probe__"]),
|
|
978
|
+
probeEffortList(runner, env, "pi", ["--help"]),
|
|
714
979
|
]);
|
|
715
980
|
const claudeVersion = claudeVersionProbe.ok ? claudeVersionProbe.value : previous.claudeVersion;
|
|
716
981
|
const priorVerifications = loadClaudeVerifications(options.storage);
|
|
@@ -743,6 +1008,19 @@ async function discoverModelCache(options, previous) {
|
|
|
743
1008
|
grokModels: grokProbe.ok ? grokProbe.value : cloneModels(previous.grokModels),
|
|
744
1009
|
qoderModels: qoderProbe.ok ? qoderProbe.value : cloneModels(previous.qoderModels),
|
|
745
1010
|
piModels: piProbe.ok ? piProbe.value : cloneModels(previous.piModels),
|
|
1011
|
+
thinkingEfforts: {
|
|
1012
|
+
claude: claudeEffortProbe.ok ? claudeEffortProbe.value : cloneThinkingEfforts(previous.thinkingEfforts).claude,
|
|
1013
|
+
opencode: opencodeProbe.models.ok && unionModelEfforts(opencodeProbe.models.value).length
|
|
1014
|
+
? unionModelEfforts(opencodeProbe.models.value)
|
|
1015
|
+
: cloneThinkingEfforts(previous.thinkingEfforts).opencode,
|
|
1016
|
+
grok: grokEffortProbe.ok ? grokEffortProbe.value : cloneThinkingEfforts(previous.thinkingEfforts).grok,
|
|
1017
|
+
qoder: qoderEffortProbe.ok ? qoderEffortProbe.value : cloneThinkingEfforts(previous.thinkingEfforts).qoder,
|
|
1018
|
+
pi: piProbe.ok && unionModelEfforts(piProbe.value).length
|
|
1019
|
+
? unionModelEfforts(piProbe.value)
|
|
1020
|
+
: piEffortProbe.ok
|
|
1021
|
+
? piEffortProbe.value
|
|
1022
|
+
: cloneThinkingEfforts(previous.thinkingEfforts).pi,
|
|
1023
|
+
},
|
|
746
1024
|
claudeVersion,
|
|
747
1025
|
opencodeVersion: opencodeProbe.version.ok ? opencodeProbe.version.value : previous.opencodeVersion,
|
|
748
1026
|
refreshedAt: now.toISOString(),
|
|
@@ -763,6 +1041,7 @@ export class ModelCatalogService {
|
|
|
763
1041
|
hasPersistedSnapshot;
|
|
764
1042
|
refreshPromise = null;
|
|
765
1043
|
inFlightIncludesVerification = false;
|
|
1044
|
+
changeListeners = new Set();
|
|
766
1045
|
constructor(getOptions) {
|
|
767
1046
|
this.getOptions = getOptions;
|
|
768
1047
|
const initialOptions = getOptions();
|
|
@@ -774,6 +1053,11 @@ export class ModelCatalogService {
|
|
|
774
1053
|
snapshot() {
|
|
775
1054
|
return { ...cloneCache(this.cache), revision: this.revision };
|
|
776
1055
|
}
|
|
1056
|
+
/** 目录内容真正变化时通知。客户端靠它刷新下拉,不用自己再跑一遍 CLI。 */
|
|
1057
|
+
onChanged(listener) {
|
|
1058
|
+
this.changeListeners.add(listener);
|
|
1059
|
+
return () => { this.changeListeners.delete(listener); };
|
|
1060
|
+
}
|
|
777
1061
|
refresh(request = {}) {
|
|
778
1062
|
const verifyClaudeCandidates = request.verifyClaudeCandidates === true;
|
|
779
1063
|
if (this.refreshPromise) {
|
|
@@ -811,7 +1095,16 @@ export class ModelCatalogService {
|
|
|
811
1095
|
savePersistedModelCatalog(options.storage, this.cache, this.revision);
|
|
812
1096
|
this.hasPersistedSnapshot = Boolean(options.storage);
|
|
813
1097
|
}
|
|
814
|
-
|
|
1098
|
+
const result = { ...this.snapshot(), changed, checkedAt };
|
|
1099
|
+
if (changed) {
|
|
1100
|
+
for (const listener of this.changeListeners) {
|
|
1101
|
+
try {
|
|
1102
|
+
listener(result);
|
|
1103
|
+
}
|
|
1104
|
+
catch { /* 通知失败不影响目录本身 */ }
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
return result;
|
|
815
1108
|
}
|
|
816
1109
|
}
|
|
817
1110
|
/**
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
27b881febe0177ec8c610a0e3b2884c425434adfc79ecc40e938fd8a1e375de5 wand-render
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.3
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
e0e940747fbe5af0da033763fdbb786c063443d3207aee4b2934e3fe98139776 wand-structured-renderd
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.3
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1481b8f4688e5d0bfa2e6c1e4b41da288820fcfb43a737b027401e55334bedaf wand-render
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.3
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
f67206b4d171f26172787e8184e684d61170c30646ab7d72a1f4232b737b4738 wand-structured-renderd
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.3
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
e51aee7fde515b0356c1a6919b66812ae4ff679f693847f61e816166e99d8805 wand-render
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.3
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
af342aa8e7dcde0ce2563629921c9080131ed666ba743901ecfa22c31f04bb98 wand-structured-renderd
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.3
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
27c00887d03a24867f120a67852e5d0666cf7ddae18c3d708cbcca9c45a82427 wand-render
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.3
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
52e1ec6dc79695c6af248b4b3bce160b3fb7be2c5f786172c78e13685c89ce39 wand-structured-renderd
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.3
|
|
@@ -2,7 +2,7 @@ import { EventEmitter } from "node:events";
|
|
|
2
2
|
import { WandStorage } from "./storage.js";
|
|
3
3
|
import { ExecutionMode, ProcessEventHandler, SessionProvider, SessionSnapshot, SessionSource, WandConfig } from "./types.js";
|
|
4
4
|
import { type PermissionResolution } from "./claude-pty-bridge.js";
|
|
5
|
-
import { type PtyTerminalSnapshot } from "./pty-terminal-state.js";
|
|
5
|
+
import { type PtyTerminalSnapshot, type PtyHistoryPage } from "./pty-terminal-state.js";
|
|
6
6
|
import { type TerminalHost } from "./terminal-host.js";
|
|
7
7
|
import { type ClaudeHistorySession, type CodexHistorySession, type OpenCodeHistorySession, type QoderHistorySession } from "./provider-history-scanner.js";
|
|
8
8
|
export type { ClaudeHistorySession, CodexHistorySession, OpenCodeHistorySession, QoderHistorySession, } from "./provider-history-scanner.js";
|
|
@@ -15,6 +15,11 @@ export declare class SessionInputError extends Error {
|
|
|
15
15
|
readonly sessionStatus?: SessionSnapshot["status"] | undefined;
|
|
16
16
|
constructor(message: string, code: "SESSION_NOT_FOUND" | "SESSION_NOT_RUNNING" | "SESSION_NO_PTY", sessionId: string, sessionStatus?: SessionSnapshot["status"] | undefined);
|
|
17
17
|
}
|
|
18
|
+
/** Write outcome is unknown to the caller; do not record this as accepted input. */
|
|
19
|
+
export declare class PtyInputDeliveryError extends Error {
|
|
20
|
+
readonly sessionId: string;
|
|
21
|
+
constructor(sessionId: string, cause: unknown);
|
|
22
|
+
}
|
|
18
23
|
export declare class ProcessManager extends EventEmitter {
|
|
19
24
|
private readonly config;
|
|
20
25
|
private readonly storage;
|
|
@@ -39,6 +44,7 @@ export declare class ProcessManager extends EventEmitter {
|
|
|
39
44
|
private bindRestoredTerminal;
|
|
40
45
|
private initializeClaudeBridge;
|
|
41
46
|
private bindTerminalProcess;
|
|
47
|
+
private handleTerminalResync;
|
|
42
48
|
private handleTerminalExit;
|
|
43
49
|
private handleTerminalData;
|
|
44
50
|
on(_event: "process", listener: ProcessEventHandler): this;
|
|
@@ -107,8 +113,13 @@ export declare class ProcessManager extends EventEmitter {
|
|
|
107
113
|
*/
|
|
108
114
|
setSessionMode(id: string, mode: ExecutionMode): SessionSnapshot;
|
|
109
115
|
sendInput(id: string, input: string, view?: "chat" | "terminal", shortcutKey?: string, trackUserInput?: boolean): SessionSnapshot;
|
|
116
|
+
sendInputConfirmed(id: string, input: string, view?: "chat" | "terminal", shortcutKey?: string, trackUserInput?: boolean): Promise<SessionSnapshot>;
|
|
117
|
+
private preparePtyInput;
|
|
118
|
+
private validatePtyInput;
|
|
119
|
+
private trackPtyInput;
|
|
110
120
|
resize(id: string, cols: number, rows: number): SessionSnapshot;
|
|
111
121
|
getTerminalState(id: string): PtyTerminalSnapshot | null;
|
|
122
|
+
getTerminalHistoryPage(id: string, before: number, revision: number): Promise<PtyHistoryPage | null>;
|
|
112
123
|
/**
|
|
113
124
|
* Finalize provider-only state while leaving the owning PTY shell alive. The
|
|
114
125
|
* session itself remains running so terminal input can continue normally.
|