github-router 0.3.41 → 0.3.43
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/browser-ext/background.js +824 -32
- package/dist/{lifecycle-CpnAVVQ_.js → lifecycle-DU0UI2t5.js} +2 -2
- package/dist/{lifecycle-CpnAVVQ_.js.map → lifecycle-DU0UI2t5.js.map} +1 -1
- package/dist/{lifecycle-DpnTmHCo.js → lifecycle-zr19Ot-e.js} +2 -2
- package/dist/main.js +459 -55
- package/dist/main.js.map +1 -1
- package/dist/{paths-cZle37Jp.js → paths-lwEqM5-i.js} +293 -2
- package/dist/paths-lwEqM5-i.js.map +1 -0
- package/dist/{paths-B7jmIPYq.js → paths-nd-94lLq.js} +1 -1
- package/package.json +1 -1
- package/dist/paths-cZle37Jp.js.map +0 -1
package/dist/main.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { c as writeRuntimeFileSecure, i as removeOwnClaudeConfigMirror, n as ensureClaudeConfigMirror, r as ensurePaths, t as PATHS } from "./paths-
|
|
3
|
-
import { a as sweepRegistry, i as registerExitHandlers, n as getInstanceUuid, r as recordWorkerRepo, t as WorktreeRegistry } from "./lifecycle-
|
|
2
|
+
import { c as writeRuntimeFileSecure, i as removeOwnClaudeConfigMirror, n as ensureClaudeConfigMirror, r as ensurePaths, t as PATHS } from "./paths-lwEqM5-i.js";
|
|
3
|
+
import { a as sweepRegistry, i as registerExitHandlers, n as getInstanceUuid, r as recordWorkerRepo, t as WorktreeRegistry } from "./lifecycle-DU0UI2t5.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { defineCommand, runMain } from "citty";
|
|
6
6
|
import consola from "consola";
|
|
@@ -447,8 +447,8 @@ function normalizeModelId(id) {
|
|
|
447
447
|
* 6. Return as-is with a warning
|
|
448
448
|
*/
|
|
449
449
|
function resolveModel(modelId) {
|
|
450
|
-
const models = state.models?.data;
|
|
451
|
-
if (!models) return modelId;
|
|
450
|
+
const models$1 = state.models?.data;
|
|
451
|
+
if (!models$1) return modelId;
|
|
452
452
|
const oneMMatch = modelId.match(/^(.*)\[1m\]$/i);
|
|
453
453
|
if (oneMMatch) {
|
|
454
454
|
const stripped = oneMMatch[1];
|
|
@@ -456,31 +456,31 @@ function resolveModel(modelId) {
|
|
|
456
456
|
if (!/-1m(?:$|-)/.test(resolved)) consola.warn(`Model "${modelId}" requested 1M context but no -1m backend is in Copilot's catalog for this tier/family; downgrading upstream to "${resolved}" (200K). Claude Code's local context accounting will still assume 1M — expect premature auto-compact. Drop the [1m] suffix (or unset CLAUDE_CODE_DISABLE_1M_CONTEXT if you set it) to silence.`);
|
|
457
457
|
return resolved;
|
|
458
458
|
}
|
|
459
|
-
if (models.some((m) => m.id === modelId)) return modelId;
|
|
459
|
+
if (models$1.some((m) => m.id === modelId)) return modelId;
|
|
460
460
|
const lower = modelId.toLowerCase();
|
|
461
|
-
const ciMatch = models.find((m) => m.id.toLowerCase() === lower);
|
|
461
|
+
const ciMatch = models$1.find((m) => m.id.toLowerCase() === lower);
|
|
462
462
|
if (ciMatch) return ciMatch.id;
|
|
463
463
|
if (lower.includes("opus")) {
|
|
464
|
-
const oneMs = models.filter((m) => m.id.includes("opus") && /-1m(?:$|-)/.test(m.id));
|
|
464
|
+
const oneMs = models$1.filter((m) => m.id.includes("opus") && /-1m(?:$|-)/.test(m.id));
|
|
465
465
|
const versionMatch = lower.match(/opus-(\d+)[.-](\d+)/);
|
|
466
466
|
const requestedVersion = versionMatch ? `${versionMatch[1]}.${versionMatch[2]}` : void 0;
|
|
467
467
|
const oneM = (requestedVersion ? oneMs.find((m) => m.id.includes(`opus-${requestedVersion}-`)) : void 0) ?? (requestedVersion ? void 0 : oneMs[0]);
|
|
468
468
|
if (oneM) return oneM.id;
|
|
469
469
|
}
|
|
470
470
|
if (lower.includes("codex")) {
|
|
471
|
-
const codexModels = models.filter((m) => m.id.includes("codex") && !m.id.includes("mini"));
|
|
471
|
+
const codexModels = models$1.filter((m) => m.id.includes("codex") && !m.id.includes("mini"));
|
|
472
472
|
if (codexModels.length > 0) {
|
|
473
473
|
codexModels.sort((a, b) => b.id.localeCompare(a.id));
|
|
474
474
|
return codexModels[0].id;
|
|
475
475
|
}
|
|
476
476
|
}
|
|
477
477
|
const normalized = normalizeModelId(modelId);
|
|
478
|
-
const normMatch = models.find((m) => normalizeModelId(m.id) === normalized);
|
|
478
|
+
const normMatch = models$1.find((m) => normalizeModelId(m.id) === normalized);
|
|
479
479
|
if (normMatch) return normMatch.id;
|
|
480
480
|
const dateStripped = modelId.replace(/^(claude-[\w.-]+)-20\d{6}$/i, "$1");
|
|
481
481
|
if (dateStripped !== modelId) {
|
|
482
482
|
const retried = resolveModel(dateStripped);
|
|
483
|
-
if (retried !== dateStripped || models.some((m) => m.id === dateStripped)) {
|
|
483
|
+
if (retried !== dateStripped || models$1.some((m) => m.id === dateStripped)) {
|
|
484
484
|
consola.info(`Resolved Anthropic dated slug "${modelId}" → "${retried}" (stripped -YYYYMMDD; pass an explicit catalog id to pin a snapshot)`);
|
|
485
485
|
return retried;
|
|
486
486
|
}
|
|
@@ -490,7 +490,7 @@ function resolveModel(modelId) {
|
|
|
490
490
|
const matchHaiku = /(?:^|-)haiku(?:-|$)/.test(lower);
|
|
491
491
|
if (matchSonnet || matchHaiku) {
|
|
492
492
|
const family = matchSonnet ? "sonnet" : "haiku";
|
|
493
|
-
const familyMembers = models.filter((m) => (/* @__PURE__ */ new RegExp(`(?:^|-)${family}(?:-|$|\\.)`)).test(m.id));
|
|
493
|
+
const familyMembers = models$1.filter((m) => (/* @__PURE__ */ new RegExp(`(?:^|-)${family}(?:-|$|\\.)`)).test(m.id));
|
|
494
494
|
if (familyMembers.length > 0) {
|
|
495
495
|
familyMembers.sort((a, b) => b.id.localeCompare(a.id, void 0, { numeric: true }));
|
|
496
496
|
const best = familyMembers[0].id;
|
|
@@ -499,7 +499,7 @@ function resolveModel(modelId) {
|
|
|
499
499
|
}
|
|
500
500
|
}
|
|
501
501
|
}
|
|
502
|
-
consola.warn(`Model "${modelId}" not found in Copilot model list. Available: ${models.map((m) => m.id).join(", ")}`);
|
|
502
|
+
consola.warn(`Model "${modelId}" not found in Copilot model list. Available: ${models$1.map((m) => m.id).join(", ")}`);
|
|
503
503
|
return modelId;
|
|
504
504
|
}
|
|
505
505
|
/**
|
|
@@ -508,10 +508,10 @@ function resolveModel(modelId) {
|
|
|
508
508
|
*/
|
|
509
509
|
function resolveCodexModel(modelId) {
|
|
510
510
|
const resolved = resolveModel(modelId);
|
|
511
|
-
const models = state.models?.data;
|
|
512
|
-
if (!models) return resolved;
|
|
513
|
-
if (models.some((m) => m.id === resolved)) return resolved;
|
|
514
|
-
const candidates = models.filter((m) => {
|
|
511
|
+
const models$1 = state.models?.data;
|
|
512
|
+
if (!models$1) return resolved;
|
|
513
|
+
if (models$1.some((m) => m.id === resolved)) return resolved;
|
|
514
|
+
const candidates = models$1.filter((m) => {
|
|
515
515
|
const endpoints = m.supported_endpoints ?? [];
|
|
516
516
|
if (m.id.includes("mini") || m.id.includes("nano")) return false;
|
|
517
517
|
return endpoints.length === 0 || endpoints.includes("/responses");
|
|
@@ -971,9 +971,9 @@ function pickClaudeDefault(opusFamily = DEFAULT_OPUS_FAMILY) {
|
|
|
971
971
|
const versionPattern = dotted.replace(/\./g, "[.-]");
|
|
972
972
|
const oneMRegex = new RegExp(`opus-${versionPattern}-1m(?:$|-)`, "i");
|
|
973
973
|
const familyRegex = new RegExp(`opus-${versionPattern}(?:$|[-.])`, "i");
|
|
974
|
-
const models = state.models?.data ?? [];
|
|
975
|
-
const has1m = models.some((m) => oneMRegex.test(m.id));
|
|
976
|
-
if (opusFamily !== DEFAULT_OPUS_FAMILY && state.models && models.length > 0 && !models.some((m) => familyRegex.test(m.id))) consola.warn(`Requested Opus family "${dotted}" not found in Copilot catalog; using "${bareSlug}" anyway (resolveModel may not find a backend for it).`);
|
|
974
|
+
const models$1 = state.models?.data ?? [];
|
|
975
|
+
const has1m = models$1.some((m) => oneMRegex.test(m.id));
|
|
976
|
+
if (opusFamily !== DEFAULT_OPUS_FAMILY && state.models && models$1.length > 0 && !models$1.some((m) => familyRegex.test(m.id))) consola.warn(`Requested Opus family "${dotted}" not found in Copilot catalog; using "${bareSlug}" anyway (resolveModel may not find a backend for it).`);
|
|
977
977
|
if (has1m) {
|
|
978
978
|
consola.info(`Catalog contains opus-${dotted}-1m variant; defaulting ANTHROPIC_MODEL to "${bareSlug}[1m]" so Claude Code accounts for 1M context locally. Set CLAUDE_CODE_DISABLE_1M_CONTEXT=1 to opt out (HIPAA), or pass --model ${bareSlug} to pin 200K.`);
|
|
979
979
|
return `${bareSlug}[1m]`;
|
|
@@ -3015,7 +3015,7 @@ const PER_TOOL_TIMEOUTS = {
|
|
|
3015
3015
|
},
|
|
3016
3016
|
browser_scroll: {
|
|
3017
3017
|
defaultMs: 5e3,
|
|
3018
|
-
maxMs:
|
|
3018
|
+
maxMs: 15e3
|
|
3019
3019
|
},
|
|
3020
3020
|
browser_keyboard: {
|
|
3021
3021
|
defaultMs: 5e3,
|
|
@@ -3040,6 +3040,22 @@ const PER_TOOL_TIMEOUTS = {
|
|
|
3040
3040
|
browser_network_log: {
|
|
3041
3041
|
defaultMs: 5e3,
|
|
3042
3042
|
maxMs: 1e4
|
|
3043
|
+
},
|
|
3044
|
+
browser_mouse: {
|
|
3045
|
+
defaultMs: 1e4,
|
|
3046
|
+
maxMs: 3e4
|
|
3047
|
+
},
|
|
3048
|
+
browser_drag: {
|
|
3049
|
+
defaultMs: 15e3,
|
|
3050
|
+
maxMs: 3e4
|
|
3051
|
+
},
|
|
3052
|
+
browser_type: {
|
|
3053
|
+
defaultMs: 15e3,
|
|
3054
|
+
maxMs: 21e4
|
|
3055
|
+
},
|
|
3056
|
+
browser_locate: {
|
|
3057
|
+
defaultMs: 5e3,
|
|
3058
|
+
maxMs: 1e4
|
|
3043
3059
|
}
|
|
3044
3060
|
};
|
|
3045
3061
|
function pickTimeout(tool) {
|
|
@@ -3197,7 +3213,7 @@ function logAudit$1(record) {
|
|
|
3197
3213
|
try {
|
|
3198
3214
|
const fs$2 = await import("node:fs/promises");
|
|
3199
3215
|
const path$2 = await import("node:path");
|
|
3200
|
-
const { PATHS: PATHS$1 } = await import("./paths-
|
|
3216
|
+
const { PATHS: PATHS$1 } = await import("./paths-nd-94lLq.js");
|
|
3201
3217
|
const dir = path$2.join(PATHS$1.APP_DIR, "browser-mcp");
|
|
3202
3218
|
await fs$2.mkdir(dir, { recursive: true });
|
|
3203
3219
|
const line = JSON.stringify({
|
|
@@ -3224,7 +3240,7 @@ function logAudit$1(record) {
|
|
|
3224
3240
|
* call-time when the operator hasn't opted in via `--browse` or
|
|
3225
3241
|
* `GH_ROUTER_ENABLE_BROWSE=1`.
|
|
3226
3242
|
*
|
|
3227
|
-
* v1 surface:
|
|
3243
|
+
* v1 surface: 19 tools (Phases 3 + 4a + 4b + humanlike input v2).
|
|
3228
3244
|
*/
|
|
3229
3245
|
const BROWSER_TOOLS = Object.freeze([
|
|
3230
3246
|
{
|
|
@@ -3344,7 +3360,7 @@ const BROWSER_TOOLS = Object.freeze([
|
|
|
3344
3360
|
},
|
|
3345
3361
|
{
|
|
3346
3362
|
toolNameHttp: "browser_read_page",
|
|
3347
|
-
description: "Extract rendered page text plus
|
|
3363
|
+
description: "Extract rendered page text plus interactive elements (refs, roles, names, bounding boxes) plus viewport metadata. Each element entry carries bbox: [x, y, w, h] in CSS viewport pixels — the same coordinate space used by browser_mouse / browser_drag / browser_scroll(at-pointer). Element refs returned here are intended as the primary input to follow-up tool calls — preferred over CSS selectors because refs are stable across dynamic class names. The viewport block {width, height, devicePixelRatio, scrollX, scrollY} lets you map a CSS-px bbox to a device-px pixel in browser_screenshot (device_px = css_px * devicePixelRatio). Text is capped at 256 KiB; elements at the first 200 interactive nodes.",
|
|
3348
3364
|
inputSchema: {
|
|
3349
3365
|
type: "object",
|
|
3350
3366
|
required: ["tabId"],
|
|
@@ -3427,7 +3443,7 @@ const BROWSER_TOOLS = Object.freeze([
|
|
|
3427
3443
|
},
|
|
3428
3444
|
{
|
|
3429
3445
|
toolNameHttp: "browser_scroll",
|
|
3430
|
-
description: "Scroll a tab
|
|
3446
|
+
description: "Scroll a tab. Five modes: top / bottom of the page, by an absolute pixel delta, to a specific element (by ref), or wheel-scroll a sub-region at a pointer location ('at-pointer' — the path that works for chat windows / infinite-scroll lists / modal bodies that don't respond to window.scrollTo because they have their own scroll container).",
|
|
3431
3447
|
inputSchema: {
|
|
3432
3448
|
type: "object",
|
|
3433
3449
|
required: ["tabId", "target"],
|
|
@@ -3440,7 +3456,8 @@ const BROWSER_TOOLS = Object.freeze([
|
|
|
3440
3456
|
"top",
|
|
3441
3457
|
"bottom",
|
|
3442
3458
|
"pixels",
|
|
3443
|
-
"element"
|
|
3459
|
+
"element",
|
|
3460
|
+
"at-pointer"
|
|
3444
3461
|
],
|
|
3445
3462
|
description: "Scroll target type."
|
|
3446
3463
|
},
|
|
@@ -3450,7 +3467,31 @@ const BROWSER_TOOLS = Object.freeze([
|
|
|
3450
3467
|
},
|
|
3451
3468
|
ref: {
|
|
3452
3469
|
type: "string",
|
|
3453
|
-
description: "Element ref
|
|
3470
|
+
description: "Element ref. For target=element, scrolls so the element is centered. For target=at-pointer, resolves to the bbox center as the wheel position."
|
|
3471
|
+
},
|
|
3472
|
+
selector: {
|
|
3473
|
+
type: "string",
|
|
3474
|
+
description: "CSS selector. For target=at-pointer, fallback when no ref. Resolves to bbox center."
|
|
3475
|
+
},
|
|
3476
|
+
x: {
|
|
3477
|
+
type: "number",
|
|
3478
|
+
description: "Pointer x (CSS viewport px) for target=at-pointer. Pair with y. Exactly one of (ref, selector, or x+y) is required for at-pointer."
|
|
3479
|
+
},
|
|
3480
|
+
y: {
|
|
3481
|
+
type: "number",
|
|
3482
|
+
description: "Pointer y (CSS viewport px) for target=at-pointer. Pair with x."
|
|
3483
|
+
},
|
|
3484
|
+
deltaX: {
|
|
3485
|
+
type: "number",
|
|
3486
|
+
description: "Wheel delta x (CSS px) for target=at-pointer. Default 0. Clamped to |10000|."
|
|
3487
|
+
},
|
|
3488
|
+
deltaY: {
|
|
3489
|
+
type: "number",
|
|
3490
|
+
description: "Wheel delta y (CSS px) for target=at-pointer. Positive scrolls down. Default 0. Clamped to |10000|. At least one of deltaX/deltaY must be non-zero."
|
|
3491
|
+
},
|
|
3492
|
+
force: {
|
|
3493
|
+
type: "boolean",
|
|
3494
|
+
description: "Skip the pre-wheel elementFromPoint hit-test for target=at-pointer. Default false. Set true when an overlay covers the target but forwards wheel events."
|
|
3454
3495
|
}
|
|
3455
3496
|
}
|
|
3456
3497
|
},
|
|
@@ -3613,6 +3654,192 @@ const BROWSER_TOOLS = Object.freeze([
|
|
|
3613
3654
|
async handler(args, signal) {
|
|
3614
3655
|
return dispatchBrowserTool("browser_network_log", args, signal);
|
|
3615
3656
|
}
|
|
3657
|
+
},
|
|
3658
|
+
{
|
|
3659
|
+
toolNameHttp: "browser_mouse",
|
|
3660
|
+
description: "Move / click / hover / press / release the mouse via real CDP input events (Input.dispatchMouseEvent). Use this when you need behavior that synthetic .click() can't trigger: hover-to-reveal menus, canvas / map / image-map clicks, sites that check event.isTrusted, or precise coordinate targeting. Target with ref (from browser_read_page), CSS selector, or (x, y) in CSS viewport pixels — exactly one. action='move' is the hover (single mouseMoved fires :hover and pointerover reliably). action='dblclick' sends two press/release cycles with incrementing clickCount (a real double-click, not one cycle with clickCount=2). By default the target is hit-tested with elementFromPoint and the call fails with `target_obscured` if the topmost element isn't the target or a descendant — pass force:true to bypass when you know an overlay forwards events.",
|
|
3661
|
+
inputSchema: {
|
|
3662
|
+
type: "object",
|
|
3663
|
+
required: ["tabId", "action"],
|
|
3664
|
+
additionalProperties: false,
|
|
3665
|
+
properties: {
|
|
3666
|
+
tabId: { type: "number" },
|
|
3667
|
+
action: {
|
|
3668
|
+
type: "string",
|
|
3669
|
+
enum: [
|
|
3670
|
+
"move",
|
|
3671
|
+
"click",
|
|
3672
|
+
"dblclick",
|
|
3673
|
+
"down",
|
|
3674
|
+
"up"
|
|
3675
|
+
],
|
|
3676
|
+
description: "What to do. move=position cursor (hover). click=press+release. dblclick=two press+release with clickCount 1 then 2. down=press only. up=release only."
|
|
3677
|
+
},
|
|
3678
|
+
ref: {
|
|
3679
|
+
type: "string",
|
|
3680
|
+
description: "Element ref from browser_read_page (preferred). Resolves to bbox center. Exactly one of ref / selector / (x+y) required."
|
|
3681
|
+
},
|
|
3682
|
+
selector: {
|
|
3683
|
+
type: "string",
|
|
3684
|
+
description: "CSS selector (fallback). Resolves to bbox center."
|
|
3685
|
+
},
|
|
3686
|
+
x: {
|
|
3687
|
+
type: "number",
|
|
3688
|
+
description: "Target x in CSS viewport pixels. Pair with y. Use when working from a screenshot or eval_js output."
|
|
3689
|
+
},
|
|
3690
|
+
y: {
|
|
3691
|
+
type: "number",
|
|
3692
|
+
description: "Target y in CSS viewport pixels. Pair with x."
|
|
3693
|
+
},
|
|
3694
|
+
button: {
|
|
3695
|
+
type: "string",
|
|
3696
|
+
enum: [
|
|
3697
|
+
"left",
|
|
3698
|
+
"right",
|
|
3699
|
+
"middle"
|
|
3700
|
+
],
|
|
3701
|
+
description: "Mouse button for click / dblclick / down / up. Default 'left'. Ignored for action=move."
|
|
3702
|
+
},
|
|
3703
|
+
steps: {
|
|
3704
|
+
type: "number",
|
|
3705
|
+
description: "Humanlike trajectory. >1 interpolates the cursor approach over N mouseMoved events. Default 1 (teleport). Clamped to [1, 100]."
|
|
3706
|
+
},
|
|
3707
|
+
stepDelayMs: {
|
|
3708
|
+
type: "number",
|
|
3709
|
+
description: "Pause between interpolated mouseMoved events when steps > 1. Default 8. Clamped to [0, 50]."
|
|
3710
|
+
},
|
|
3711
|
+
force: {
|
|
3712
|
+
type: "boolean",
|
|
3713
|
+
description: "Skip the pre-click elementFromPoint hit-test (ref/selector mode only). Default false."
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3716
|
+
},
|
|
3717
|
+
capability: "browser",
|
|
3718
|
+
async handler(args, signal) {
|
|
3719
|
+
return dispatchBrowserTool("browser_mouse", args, signal);
|
|
3720
|
+
}
|
|
3721
|
+
},
|
|
3722
|
+
{
|
|
3723
|
+
toolNameHttp: "browser_drag",
|
|
3724
|
+
description: "Drag from a source to a destination. Auto-detects whether to use HTML5 native DnD (for elements with draggable='true', via CDP Input.setInterceptDrags + Input.dispatchDragEvent — the only path that triggers Chromium's native dragstart pipeline) or pointer-based DnD (for react-dnd / Sortable.js / mouse-event-based drag handlers — via CDP mouse events with buttons:1 held throughout). Each of from/to can be a ref (preferred), a CSS selector, or x+y coordinates. Returns { ok: true, mode_used: 'pointer'|'html5' } so you can verify which path ran.",
|
|
3725
|
+
inputSchema: {
|
|
3726
|
+
type: "object",
|
|
3727
|
+
required: ["tabId"],
|
|
3728
|
+
additionalProperties: false,
|
|
3729
|
+
properties: {
|
|
3730
|
+
tabId: { type: "number" },
|
|
3731
|
+
fromRef: {
|
|
3732
|
+
type: "string",
|
|
3733
|
+
description: "Source ref from browser_read_page (preferred)."
|
|
3734
|
+
},
|
|
3735
|
+
fromSelector: {
|
|
3736
|
+
type: "string",
|
|
3737
|
+
description: "Source CSS selector (fallback)."
|
|
3738
|
+
},
|
|
3739
|
+
fromX: {
|
|
3740
|
+
type: "number",
|
|
3741
|
+
description: "Source x in CSS viewport pixels. Pair with fromY."
|
|
3742
|
+
},
|
|
3743
|
+
fromY: {
|
|
3744
|
+
type: "number",
|
|
3745
|
+
description: "Source y in CSS viewport pixels. Pair with fromX."
|
|
3746
|
+
},
|
|
3747
|
+
toRef: {
|
|
3748
|
+
type: "string",
|
|
3749
|
+
description: "Destination ref from browser_read_page (preferred)."
|
|
3750
|
+
},
|
|
3751
|
+
toSelector: {
|
|
3752
|
+
type: "string",
|
|
3753
|
+
description: "Destination CSS selector (fallback)."
|
|
3754
|
+
},
|
|
3755
|
+
toX: {
|
|
3756
|
+
type: "number",
|
|
3757
|
+
description: "Destination x in CSS viewport pixels. Pair with toY."
|
|
3758
|
+
},
|
|
3759
|
+
toY: {
|
|
3760
|
+
type: "number",
|
|
3761
|
+
description: "Destination y in CSS viewport pixels. Pair with toX."
|
|
3762
|
+
},
|
|
3763
|
+
button: {
|
|
3764
|
+
type: "string",
|
|
3765
|
+
enum: ["left", "middle"],
|
|
3766
|
+
description: "Mouse button held during drag. Default 'left'."
|
|
3767
|
+
},
|
|
3768
|
+
steps: {
|
|
3769
|
+
type: "number",
|
|
3770
|
+
description: "Intermediate mouseMoved events from→to with the button held. Drag-detect libraries need a trajectory to fire. Default 15. Clamped to [1, 100]."
|
|
3771
|
+
},
|
|
3772
|
+
stepDelayMs: {
|
|
3773
|
+
type: "number",
|
|
3774
|
+
description: "Pause between intermediate moves. Default 12. Clamped to [0, 50]."
|
|
3775
|
+
},
|
|
3776
|
+
mode: {
|
|
3777
|
+
type: "string",
|
|
3778
|
+
enum: [
|
|
3779
|
+
"auto",
|
|
3780
|
+
"pointer",
|
|
3781
|
+
"html5"
|
|
3782
|
+
],
|
|
3783
|
+
description: "Drag mode. 'auto' (default) picks html5 if the source has draggable='true', else pointer. Override only when auto detection misses."
|
|
3784
|
+
},
|
|
3785
|
+
force: {
|
|
3786
|
+
type: "boolean",
|
|
3787
|
+
description: "Skip the pre-press elementFromPoint hit-test on the source. Default false."
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
},
|
|
3791
|
+
capability: "browser",
|
|
3792
|
+
async handler(args, signal) {
|
|
3793
|
+
return dispatchBrowserTool("browser_drag", args, signal);
|
|
3794
|
+
}
|
|
3795
|
+
},
|
|
3796
|
+
{
|
|
3797
|
+
toolNameHttp: "browser_type",
|
|
3798
|
+
description: "Type a string into the currently-focused element per-keystroke via CDP Input.dispatchKeyEvent. Each character fires keydown + keypress + input — this is the tool for keystroke-driven autocomplete, chips, search-as-you-type, and any site whose handlers listen on keydown rather than just reading element.value. For plain form-value entry use browser_fill (faster, sets value directly). For chord shortcuts (Control+L, etc) use browser_keyboard. Special characters in text: \\n→Enter, \\t→Tab, \\b→Backspace (dispatched as the named key, not as a literal control char). Other control chars (< 0x20) are rejected with an actionable error. Uppercase letters come from the natural code point — event.shiftKey is false but the typed value is correct.",
|
|
3799
|
+
inputSchema: {
|
|
3800
|
+
type: "object",
|
|
3801
|
+
required: ["tabId", "text"],
|
|
3802
|
+
additionalProperties: false,
|
|
3803
|
+
properties: {
|
|
3804
|
+
tabId: { type: "number" },
|
|
3805
|
+
text: {
|
|
3806
|
+
type: "string",
|
|
3807
|
+
description: "The text to type. Max 4096 chars. Iterates as Unicode code points (surrogate pairs handled correctly)."
|
|
3808
|
+
},
|
|
3809
|
+
delayMs: {
|
|
3810
|
+
type: "number",
|
|
3811
|
+
description: "Pause between characters. Default 0. Clamped to [0, 50]. Set > 0 when typing into search-as-you-type inputs that debounce."
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
},
|
|
3815
|
+
capability: "browser",
|
|
3816
|
+
async handler(args, signal) {
|
|
3817
|
+
return dispatchBrowserTool("browser_type", args, signal);
|
|
3818
|
+
}
|
|
3819
|
+
},
|
|
3820
|
+
{
|
|
3821
|
+
toolNameHttp: "browser_locate",
|
|
3822
|
+
description: "Resolve a single ref or selector to bounding box + hit-test metadata, without a full browser_read_page snapshot. Cheap — one in-page script call. Returns bbox (CSS viewport px), center, inView (bbox intersects viewport), visible (display/visibility/opacity > 0 and bbox > 0), computed pointer-events, viewport metadata, and topmostAtCenter (is the element at the bbox center actually this target, or is it occluded by an overlay?). Use this before browser_mouse / browser_drag to detect overlay-occluded targets, or to check whether something scrolled out of view.",
|
|
3823
|
+
inputSchema: {
|
|
3824
|
+
type: "object",
|
|
3825
|
+
required: ["tabId"],
|
|
3826
|
+
additionalProperties: false,
|
|
3827
|
+
properties: {
|
|
3828
|
+
tabId: { type: "number" },
|
|
3829
|
+
ref: {
|
|
3830
|
+
type: "string",
|
|
3831
|
+
description: "Element ref from browser_read_page (preferred). Exactly one of ref / selector required."
|
|
3832
|
+
},
|
|
3833
|
+
selector: {
|
|
3834
|
+
type: "string",
|
|
3835
|
+
description: "CSS selector (fallback)."
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
},
|
|
3839
|
+
capability: "browser",
|
|
3840
|
+
async handler(args, signal) {
|
|
3841
|
+
return dispatchBrowserTool("browser_locate", args, signal);
|
|
3842
|
+
}
|
|
3616
3843
|
}
|
|
3617
3844
|
]);
|
|
3618
3845
|
|
|
@@ -3651,9 +3878,9 @@ const MODELS = {};
|
|
|
3651
3878
|
//#endregion
|
|
3652
3879
|
//#region src/vendor/pi/ai/models.ts
|
|
3653
3880
|
const modelRegistry = /* @__PURE__ */ new Map();
|
|
3654
|
-
for (const [provider, models] of Object.entries(MODELS)) {
|
|
3881
|
+
for (const [provider, models$1] of Object.entries(MODELS)) {
|
|
3655
3882
|
const providerModels = /* @__PURE__ */ new Map();
|
|
3656
|
-
for (const [id, model] of Object.entries(models)) providerModels.set(id, model);
|
|
3883
|
+
for (const [id, model] of Object.entries(models$1)) providerModels.set(id, model);
|
|
3657
3884
|
modelRegistry.set(provider, providerModels);
|
|
3658
3885
|
}
|
|
3659
3886
|
|
|
@@ -6338,9 +6565,9 @@ function checkAuth(c) {
|
|
|
6338
6565
|
return { ok: true };
|
|
6339
6566
|
}
|
|
6340
6567
|
function geminiAvailable() {
|
|
6341
|
-
const models = state.models?.data;
|
|
6342
|
-
if (!models) return false;
|
|
6343
|
-
return models.some((m) => /^gemini-3\..*pro/i.test(m.id));
|
|
6568
|
+
const models$1 = state.models?.data;
|
|
6569
|
+
if (!models$1) return false;
|
|
6570
|
+
return models$1.some((m) => /^gemini-3\..*pro/i.test(m.id));
|
|
6344
6571
|
}
|
|
6345
6572
|
/**
|
|
6346
6573
|
* Gate for the `stand_in` tool.
|
|
@@ -6364,11 +6591,11 @@ function geminiAvailable() {
|
|
|
6364
6591
|
* land under the dotted slug, so we match by Copilot's actual id shape.
|
|
6365
6592
|
*/
|
|
6366
6593
|
function standInToolEnabled() {
|
|
6367
|
-
const models = state.models?.data;
|
|
6368
|
-
if (!models) return false;
|
|
6369
|
-
const hasGpt55 = models.some((m) => m.id === "gpt-5.5");
|
|
6370
|
-
const hasOpus = models.some((m) => m.id === "claude-opus-4-7" || m.id === "claude-opus-4.7");
|
|
6371
|
-
const hasGeminiPro = models.some((m) => /^gemini-3\..*pro/i.test(m.id));
|
|
6594
|
+
const models$1 = state.models?.data;
|
|
6595
|
+
if (!models$1) return false;
|
|
6596
|
+
const hasGpt55 = models$1.some((m) => m.id === "gpt-5.5");
|
|
6597
|
+
const hasOpus = models$1.some((m) => m.id === "claude-opus-4-7" || m.id === "claude-opus-4.7");
|
|
6598
|
+
const hasGeminiPro = models$1.some((m) => /^gemini-3\..*pro/i.test(m.id));
|
|
6372
6599
|
return hasGpt55 && hasOpus && hasGeminiPro;
|
|
6373
6600
|
}
|
|
6374
6601
|
/**
|
|
@@ -6398,9 +6625,9 @@ function standInToolEnabled() {
|
|
|
6398
6625
|
*/
|
|
6399
6626
|
function workerToolsEnabled() {
|
|
6400
6627
|
if (process.env.GH_ROUTER_DISABLE_WORKER_TOOLS === "1") return false;
|
|
6401
|
-
const models = state.models?.data;
|
|
6402
|
-
if (!models) return false;
|
|
6403
|
-
const found = models.find((m) => m.id === DEFAULT_MODEL);
|
|
6628
|
+
const models$1 = state.models?.data;
|
|
6629
|
+
if (!models$1) return false;
|
|
6630
|
+
const found = models$1.find((m) => m.id === DEFAULT_MODEL);
|
|
6404
6631
|
if (!found) return false;
|
|
6405
6632
|
return found.capabilities?.supports?.tool_calls === true;
|
|
6406
6633
|
}
|
|
@@ -9073,9 +9300,9 @@ function lookupPersona(critic) {
|
|
|
9073
9300
|
return persona;
|
|
9074
9301
|
}
|
|
9075
9302
|
function geminiInCatalog() {
|
|
9076
|
-
const models = state.models?.data;
|
|
9077
|
-
if (!models) return false;
|
|
9078
|
-
return models.some((m) => /^gemini-3\..*pro/i.test(m.id));
|
|
9303
|
+
const models$1 = state.models?.data;
|
|
9304
|
+
if (!models$1) return false;
|
|
9305
|
+
return models$1.some((m) => /^gemini-3\..*pro/i.test(m.id));
|
|
9079
9306
|
}
|
|
9080
9307
|
const ADVISOR_PARAMS = Type.Object({ concern: Type.String({
|
|
9081
9308
|
description: "What you want a second pair of eyes on — your current approach, the blocker you're stuck on, or the decision you're about to commit. Required: the advisor needs a focal point.",
|
|
@@ -10578,6 +10805,10 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
|
|
|
10578
10805
|
"xhigh"
|
|
10579
10806
|
],
|
|
10580
10807
|
description: "Optional reasoning depth (default high). Silently clamped to the model's allowed range; \"off\" drops the parameter entirely."
|
|
10808
|
+
},
|
|
10809
|
+
workspace: {
|
|
10810
|
+
type: "string",
|
|
10811
|
+
description: "Optional absolute path to the workspace the worker operates in. Defaults to the proxy's launch cwd. Use this when the parent agent has multiple workspaces open and the worker must operate in a specific one. Must be absolute (relative paths rejected)."
|
|
10581
10812
|
}
|
|
10582
10813
|
}
|
|
10583
10814
|
},
|
|
@@ -10621,6 +10852,10 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
|
|
|
10621
10852
|
"xhigh"
|
|
10622
10853
|
],
|
|
10623
10854
|
description: "Optional reasoning depth (default high). Silently clamped to the model's allowed range; \"off\" drops the parameter entirely."
|
|
10855
|
+
},
|
|
10856
|
+
workspace: {
|
|
10857
|
+
type: "string",
|
|
10858
|
+
description: "Optional absolute path to the workspace the worker operates in. Defaults to the proxy's launch cwd. Use this when the parent agent has multiple workspaces open and the worker must operate in a specific one. Must be absolute (relative paths rejected). For worktree:true, must be inside a git repo."
|
|
10624
10859
|
}
|
|
10625
10860
|
}
|
|
10626
10861
|
},
|
|
@@ -10685,11 +10920,13 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
|
|
|
10685
10920
|
/**
|
|
10686
10921
|
* Shared closure body for the two worker MCP tools. Validates the
|
|
10687
10922
|
* minimal arg shape (prompt required + optional knobs typed), then
|
|
10688
|
-
* forwards to `runWorkerAgent
|
|
10689
|
-
*
|
|
10690
|
-
*
|
|
10691
|
-
*
|
|
10692
|
-
*
|
|
10923
|
+
* forwards to `runWorkerAgent`. `workspace` defaults to the proxy's
|
|
10924
|
+
* launch cwd; callers can override via the optional `workspace` arg
|
|
10925
|
+
* (absolute paths only — enforced here). The engine performs every
|
|
10926
|
+
* deeper validation (model existence, thinking clamp, worktree
|
|
10927
|
+
* provisioning, semaphore acquisition, workspace realpath +
|
|
10928
|
+
* accessibility) and never throws — its `{text, isError?}` envelope
|
|
10929
|
+
* is forwarded verbatim into the MCP `tool result` shape.
|
|
10693
10930
|
*
|
|
10694
10931
|
* Arg-validation policy mirrors `web_search`'s pattern: shape errors
|
|
10695
10932
|
* surface as `isError: true` tool-result envelopes (NOT JSON-RPC -32602
|
|
@@ -10746,10 +10983,28 @@ async function runWorkerToolCall(call) {
|
|
|
10746
10983
|
};
|
|
10747
10984
|
worktree = args.worktree;
|
|
10748
10985
|
}
|
|
10986
|
+
let workspace = process.cwd();
|
|
10987
|
+
if (args.workspace !== void 0) {
|
|
10988
|
+
if (typeof args.workspace !== "string" || args.workspace.length === 0) return {
|
|
10989
|
+
content: [{
|
|
10990
|
+
type: "text",
|
|
10991
|
+
text: `worker_${mode}: arguments.workspace must be a non-empty string when provided`
|
|
10992
|
+
}],
|
|
10993
|
+
isError: true
|
|
10994
|
+
};
|
|
10995
|
+
if (!path.isAbsolute(args.workspace)) return {
|
|
10996
|
+
content: [{
|
|
10997
|
+
type: "text",
|
|
10998
|
+
text: `worker_${mode}: arguments.workspace must be an absolute path (got "${args.workspace}")`
|
|
10999
|
+
}],
|
|
11000
|
+
isError: true
|
|
11001
|
+
};
|
|
11002
|
+
workspace = args.workspace;
|
|
11003
|
+
}
|
|
10749
11004
|
const result = await runWorkerAgent({
|
|
10750
11005
|
mode,
|
|
10751
11006
|
prompt,
|
|
10752
|
-
workspace
|
|
11007
|
+
workspace,
|
|
10753
11008
|
model,
|
|
10754
11009
|
thinking,
|
|
10755
11010
|
worktree,
|
|
@@ -11459,7 +11714,7 @@ function initProxyFromEnv() {
|
|
|
11459
11714
|
//#endregion
|
|
11460
11715
|
//#region package.json
|
|
11461
11716
|
var name = "github-router";
|
|
11462
|
-
var version = "0.3.
|
|
11717
|
+
var version = "0.3.43";
|
|
11463
11718
|
|
|
11464
11719
|
//#endregion
|
|
11465
11720
|
//#region src/lib/approval.ts
|
|
@@ -11518,7 +11773,7 @@ async function doCheck(state$1, ticket) {
|
|
|
11518
11773
|
/**
|
|
11519
11774
|
* Format a number with K/M suffix for compact display.
|
|
11520
11775
|
*/
|
|
11521
|
-
function formatTokens(n) {
|
|
11776
|
+
function formatTokens$1(n) {
|
|
11522
11777
|
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
11523
11778
|
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
11524
11779
|
return String(n);
|
|
@@ -11532,9 +11787,9 @@ function formatTokenInfo(inputTokens, outputTokens, model) {
|
|
|
11532
11787
|
const maxPrompt = model?.capabilities?.limits?.max_prompt_tokens;
|
|
11533
11788
|
if (maxPrompt) {
|
|
11534
11789
|
const pct = (inputTokens / maxPrompt * 100).toFixed(1);
|
|
11535
|
-
parts.push(`in:${formatTokens(inputTokens)}/${formatTokens(maxPrompt)} (${pct}%)`);
|
|
11536
|
-
} else parts.push(`in:${formatTokens(inputTokens)}`);
|
|
11537
|
-
if (outputTokens !== void 0) parts.push(`out:${formatTokens(outputTokens)}`);
|
|
11790
|
+
parts.push(`in:${formatTokens$1(inputTokens)}/${formatTokens$1(maxPrompt)} (${pct}%)`);
|
|
11791
|
+
} else parts.push(`in:${formatTokens$1(inputTokens)}`);
|
|
11792
|
+
if (outputTokens !== void 0) parts.push(`out:${formatTokens$1(outputTokens)}`);
|
|
11538
11793
|
return parts.join(" ");
|
|
11539
11794
|
}
|
|
11540
11795
|
/**
|
|
@@ -12703,7 +12958,7 @@ const modelRoutes = new Hono();
|
|
|
12703
12958
|
modelRoutes.get("/", async (c) => {
|
|
12704
12959
|
try {
|
|
12705
12960
|
if (!state.models) await cacheModels();
|
|
12706
|
-
const models = state.models?.data.map((model) => {
|
|
12961
|
+
const models$1 = state.models?.data.map((model) => {
|
|
12707
12962
|
const { requestHeaders,...rest } = model;
|
|
12708
12963
|
return {
|
|
12709
12964
|
...rest,
|
|
@@ -12717,7 +12972,7 @@ modelRoutes.get("/", async (c) => {
|
|
|
12717
12972
|
});
|
|
12718
12973
|
return c.json({
|
|
12719
12974
|
object: "list",
|
|
12720
|
-
data: models,
|
|
12975
|
+
data: models$1,
|
|
12721
12976
|
has_more: false
|
|
12722
12977
|
});
|
|
12723
12978
|
} catch (error) {
|
|
@@ -13673,6 +13928,154 @@ const debug = defineCommand({
|
|
|
13673
13928
|
}
|
|
13674
13929
|
});
|
|
13675
13930
|
|
|
13931
|
+
//#endregion
|
|
13932
|
+
//#region src/models.ts
|
|
13933
|
+
const models = defineCommand({
|
|
13934
|
+
meta: {
|
|
13935
|
+
name: "models",
|
|
13936
|
+
description: "List available GitHub Copilot models and their capabilities. Pass an optional pattern to filter (case-insensitive substring match on id, name, vendor, family)."
|
|
13937
|
+
},
|
|
13938
|
+
args: {
|
|
13939
|
+
pattern: {
|
|
13940
|
+
type: "positional",
|
|
13941
|
+
required: false,
|
|
13942
|
+
description: "Substring to filter models by (matches id, name, vendor, or family)."
|
|
13943
|
+
},
|
|
13944
|
+
json: {
|
|
13945
|
+
type: "boolean",
|
|
13946
|
+
default: false,
|
|
13947
|
+
description: "Emit raw JSON instead of the pretty layout."
|
|
13948
|
+
}
|
|
13949
|
+
},
|
|
13950
|
+
async run({ args }) {
|
|
13951
|
+
await ensurePaths();
|
|
13952
|
+
await setupGitHubToken();
|
|
13953
|
+
try {
|
|
13954
|
+
await setupCopilotToken();
|
|
13955
|
+
} catch (err) {
|
|
13956
|
+
consola.error("Failed to obtain Copilot token:", err);
|
|
13957
|
+
process.exit(1);
|
|
13958
|
+
}
|
|
13959
|
+
let catalog;
|
|
13960
|
+
try {
|
|
13961
|
+
catalog = await getModels();
|
|
13962
|
+
} catch (err) {
|
|
13963
|
+
consola.error("Failed to fetch Copilot model catalog:", err);
|
|
13964
|
+
process.exit(1);
|
|
13965
|
+
}
|
|
13966
|
+
const all = catalog.data;
|
|
13967
|
+
const pattern = args.pattern?.toString().trim();
|
|
13968
|
+
const filtered = pattern ? filterModels(all, pattern) : all;
|
|
13969
|
+
if (args.json) {
|
|
13970
|
+
process.stdout.write(`${JSON.stringify(filtered, null, 2)}\n`);
|
|
13971
|
+
return;
|
|
13972
|
+
}
|
|
13973
|
+
if (filtered.length === 0) {
|
|
13974
|
+
consola.warn(`No models matched "${pattern}". ${all.length} models available — try a different substring or run without an argument to list everything.`);
|
|
13975
|
+
process.exit(1);
|
|
13976
|
+
}
|
|
13977
|
+
const grouped = groupByVendor(filtered);
|
|
13978
|
+
const lines = [];
|
|
13979
|
+
const header = pattern ? `${filtered.length}/${all.length} models match "${pattern}"` : `${all.length} models available`;
|
|
13980
|
+
lines.push(header);
|
|
13981
|
+
lines.push("");
|
|
13982
|
+
for (const [vendor, list] of grouped) {
|
|
13983
|
+
lines.push(`▾ ${vendor} (${list.length})`);
|
|
13984
|
+
for (const model of list) lines.push(...formatModel(model));
|
|
13985
|
+
lines.push("");
|
|
13986
|
+
}
|
|
13987
|
+
process.stdout.write(lines.join("\n"));
|
|
13988
|
+
}
|
|
13989
|
+
});
|
|
13990
|
+
function filterModels(models$1, pattern) {
|
|
13991
|
+
const needle = pattern.toLowerCase();
|
|
13992
|
+
return models$1.filter((m) => {
|
|
13993
|
+
return [
|
|
13994
|
+
m.id,
|
|
13995
|
+
m.name,
|
|
13996
|
+
m.vendor,
|
|
13997
|
+
m.capabilities.family,
|
|
13998
|
+
m.capabilities.type,
|
|
13999
|
+
m.model_picker_category ?? ""
|
|
14000
|
+
].join(" ").toLowerCase().includes(needle);
|
|
14001
|
+
});
|
|
14002
|
+
}
|
|
14003
|
+
function groupByVendor(models$1) {
|
|
14004
|
+
const map = /* @__PURE__ */ new Map();
|
|
14005
|
+
for (const m of models$1) {
|
|
14006
|
+
const key = m.vendor || "(unknown vendor)";
|
|
14007
|
+
const bucket = map.get(key);
|
|
14008
|
+
if (bucket) bucket.push(m);
|
|
14009
|
+
else map.set(key, [m]);
|
|
14010
|
+
}
|
|
14011
|
+
return [...map.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
14012
|
+
}
|
|
14013
|
+
function formatModel(model) {
|
|
14014
|
+
const lines = [];
|
|
14015
|
+
const tags = [];
|
|
14016
|
+
if (model.preview) tags.push("preview");
|
|
14017
|
+
if (model.is_chat_default) tags.push("chat-default");
|
|
14018
|
+
if (model.is_chat_fallback) tags.push("chat-fallback");
|
|
14019
|
+
if (model.billing?.is_premium) tags.push("premium");
|
|
14020
|
+
if (model.billing?.restricted_to?.length) tags.push(`restricted:${model.billing.restricted_to.join("/")}`);
|
|
14021
|
+
if (model.policy && model.policy.state !== "enabled") tags.push(`policy:${model.policy.state}`);
|
|
14022
|
+
const tagStr = tags.length > 0 ? ` [${tags.join(", ")}]` : "";
|
|
14023
|
+
lines.push(` • ${model.id}${tagStr}`);
|
|
14024
|
+
if (model.name && model.name !== model.id) lines.push(` name: ${model.name}`);
|
|
14025
|
+
const meta = [`family: ${model.capabilities.family}`, `type: ${model.capabilities.type}`];
|
|
14026
|
+
if (model.capabilities.tokenizer) meta.push(`tokenizer: ${model.capabilities.tokenizer}`);
|
|
14027
|
+
if (model.version) meta.push(`version: ${model.version}`);
|
|
14028
|
+
lines.push(` ${meta.join(" · ")}`);
|
|
14029
|
+
const limits = model.capabilities.limits;
|
|
14030
|
+
const limitParts = [];
|
|
14031
|
+
if (limits.max_context_window_tokens) limitParts.push(`ctx ${formatTokens(limits.max_context_window_tokens)}`);
|
|
14032
|
+
else if (limits.max_prompt_tokens) limitParts.push(`prompt ${formatTokens(limits.max_prompt_tokens)}`);
|
|
14033
|
+
if (limits.max_output_tokens) limitParts.push(`out ${formatTokens(limits.max_output_tokens)}`);
|
|
14034
|
+
if (limits.max_non_streaming_output_tokens && limits.max_non_streaming_output_tokens !== limits.max_output_tokens) limitParts.push(`out-non-stream ${formatTokens(limits.max_non_streaming_output_tokens)}`);
|
|
14035
|
+
if (limits.max_inputs) limitParts.push(`inputs ${limits.max_inputs}`);
|
|
14036
|
+
if (limits.vision?.max_prompt_images) limitParts.push(`images ${limits.vision.max_prompt_images}`);
|
|
14037
|
+
if (limitParts.length > 0) lines.push(` limits: ${limitParts.join(" · ")}`);
|
|
14038
|
+
const supports = model.capabilities.supports;
|
|
14039
|
+
const supportFlags = [];
|
|
14040
|
+
if (supports.tool_calls) supportFlags.push("tools");
|
|
14041
|
+
if (supports.parallel_tool_calls) supportFlags.push("parallel-tools");
|
|
14042
|
+
if (supports.streaming) supportFlags.push("streaming");
|
|
14043
|
+
if (supports.vision) supportFlags.push("vision");
|
|
14044
|
+
if (supports.structured_outputs) supportFlags.push("structured-outputs");
|
|
14045
|
+
if (supports.dimensions) supportFlags.push("dimensions");
|
|
14046
|
+
if (supports.adaptive_thinking) {
|
|
14047
|
+
const min = supports.min_thinking_budget;
|
|
14048
|
+
const max = supports.max_thinking_budget;
|
|
14049
|
+
const range = min !== void 0 && max !== void 0 ? `(${formatTokens(min)}-${formatTokens(max)})` : "";
|
|
14050
|
+
supportFlags.push(`adaptive-thinking${range}`);
|
|
14051
|
+
}
|
|
14052
|
+
if (supports.reasoning_effort && supports.reasoning_effort.length > 0) supportFlags.push(`reasoning:${supports.reasoning_effort.join("/")}`);
|
|
14053
|
+
if (supportFlags.length > 0) lines.push(` supports: ${supportFlags.join(", ")}`);
|
|
14054
|
+
if (model.supported_endpoints && model.supported_endpoints.length > 0) lines.push(` endpoints: ${model.supported_endpoints.join(", ")}`);
|
|
14055
|
+
if (model.billing) {
|
|
14056
|
+
const billParts = [];
|
|
14057
|
+
if (model.billing.is_premium) billParts.push("premium");
|
|
14058
|
+
if (typeof model.billing.multiplier === "number") billParts.push(`×${model.billing.multiplier}`);
|
|
14059
|
+
if (billParts.length > 0) lines.push(` billing: ${billParts.join(" ")}`);
|
|
14060
|
+
}
|
|
14061
|
+
return lines;
|
|
14062
|
+
}
|
|
14063
|
+
/**
|
|
14064
|
+
* Format a token count in a compact human-readable form: `1024` →
|
|
14065
|
+
* `1k`, `4096` → `4k`, `131072` → `128k`, `1048576` → `1M`. Prefer
|
|
14066
|
+
* binary multiples (mebi, kibi) since Claude Code / Copilot context
|
|
14067
|
+
* windows are reported in binary units (`1M context` = 1024 × 1024
|
|
14068
|
+
* tokens). Fall back to decimal (`64k` for `64000`) when the value
|
|
14069
|
+
* is a clean decimal multiple but not binary.
|
|
14070
|
+
*/
|
|
14071
|
+
function formatTokens(n) {
|
|
14072
|
+
if (n >= 1048576 && n % 1048576 === 0) return `${n / 1048576}M`;
|
|
14073
|
+
if (n >= 1024 && n % 1024 === 0) return `${n / 1024}k`;
|
|
14074
|
+
if (n >= 1e6 && n % 1e6 === 0) return `${n / 1e6}M`;
|
|
14075
|
+
if (n >= 1e3 && n % 1e3 === 0) return `${n / 1e3}k`;
|
|
14076
|
+
return `${n}`;
|
|
14077
|
+
}
|
|
14078
|
+
|
|
13676
14079
|
//#endregion
|
|
13677
14080
|
//#region src/lib/shell.ts
|
|
13678
14081
|
function getShell() {
|
|
@@ -13809,6 +14212,7 @@ await runMain(defineCommand({
|
|
|
13809
14212
|
start,
|
|
13810
14213
|
claude,
|
|
13811
14214
|
codex,
|
|
14215
|
+
models,
|
|
13812
14216
|
"check-usage": checkUsage,
|
|
13813
14217
|
debug
|
|
13814
14218
|
}
|