@closedrouter/cli 1.1.17 → 1.1.19
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 +7 -3
- package/dist/asar.d.ts +3 -0
- package/dist/asar.d.ts.map +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/commands/codex-desktop.d.ts +6 -0
- package/dist/commands/codex-desktop.d.ts.map +1 -0
- package/dist/commands/cursor-desktop-account.d.ts.map +1 -1
- package/dist/commands/desktop-launch.d.ts.map +1 -1
- package/dist/commands/model-branding.d.ts +3 -3
- package/dist/commands/model-branding.d.ts.map +1 -1
- package/dist/commands/start.d.ts +2 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/verify.d.ts +9 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/compatibility.d.ts +32 -0
- package/dist/compatibility.d.ts.map +1 -0
- package/dist/index.js +414 -44
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -126,7 +126,7 @@ cr logout
|
|
|
126
126
|
|
|
127
127
|
### `cr start` / `cr launch`
|
|
128
128
|
|
|
129
|
-
Run `cr start` to choose a CLI or desktop app from the tool selector. Desktop entries are labeled Codex Desktop, Antigravity Desktop, Cursor Desktop, and Claude Desktop. Use `cr start --desktop` to show only desktop apps. Explicit tool names such as `cr start codex` still default to the CLI; add `--desktop` to launch the app. The names `codex-desktop`, `claude-desktop`, `cursor-desktop`, and `antigravity-desktop` also work.
|
|
129
|
+
Run `cr start` to choose a CLI or desktop app from the tool selector. Grok Build is available as `cr start grok` or `cr start grok-build`. Desktop entries are labeled Codex Desktop, Antigravity Desktop, Cursor Desktop, and Claude Desktop. Use `cr start --desktop` to show only desktop apps. Explicit tool names such as `cr start codex` still default to the CLI; add `--desktop` to launch the app. The names `codex-desktop`, `claude-desktop`, `cursor-desktop`, and `antigravity-desktop` also work.
|
|
130
130
|
|
|
131
131
|
```bash
|
|
132
132
|
cr start codex --desktop --model gpt-4.1
|
|
@@ -140,7 +140,7 @@ cr start claude --desktop --model claude-sonnet-4-6 --path /Applications/Claude.
|
|
|
140
140
|
|
|
141
141
|
| Desktop app | Support |
|
|
142
142
|
| --- | --- |
|
|
143
|
-
| Codex | Configures
|
|
143
|
+
| Codex | Configures an authenticated Responses provider in an isolated `CODEX_HOME`, supplies the model catalog, disables unsupported WebSocket transport, and adds the colored provider logo. An OpenAI login is not required. |
|
|
144
144
|
| Claude | Configures its native `3p` gateway profile and the supported model catalog. Local transport aliases resolve to the actual selected pool model. No Claude subscription or login is required for gateway sessions. |
|
|
145
145
|
| Cursor | Enables the bundled local agent engine in the launched process and routes requests directly through the ClosedRouter relay. Existing logins are reused. Without a login, the UI accepts the configured provider key and skips the login screen. No Cursor subscription is used for inference. |
|
|
146
146
|
| Antigravity | On macOS, installs a process-local request hook and translates Cloud Code generation envelopes into Gemini gateway requests for the selected pool model. Account and model-discovery traffic uses the existing `antigravity-mods` proxy. Requires its daemon running on ports 59997 and 59998. |
|
|
@@ -155,7 +155,11 @@ On macOS, Claude uses a compatible Electron runtime installed by Cockpit, or the
|
|
|
155
155
|
|
|
156
156
|
Antigravity uses the CCPA envelope conversion documented in `~/Projects/antigravity-mods`. The launcher briefly opens a loopback Node inspector and installs its language-server hook. A browser debugger replaces the launched renderer's model catalog with native custom-model entries, then detaches. Each entry carries a distinct routing alias and a positive context and output budget. The picker lists the supported pool models, and selecting one changes inference routing. App bundles and global hook configuration are not modified. Unsupported launcher builds fail before opening; `--open-only` remains available.
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
Supported desktop views display the colored ClosedRouter logo beside model names. White-core neon-blue lightning bolts fly around the active chat-box logo along a 3D orbit for a brief pass every five seconds, and immediately on hover or keyboard focus. The orbit measures the logo, so model-label width does not move or enlarge it. The bolts keep their orientation; perspective enlarges the front pass and the logo occludes the rear pass. Reduced-motion preferences keep the mark static; hidden windows pause the effect. Launch requests also carry the configured model ID in system context so native client identity prompts do not misidentify the underlying model. The relay resolves the requested model on each turn and preserves tools and streamed responses. Unsupported model IDs are rejected before forwarding. Catalogs are snapshots of the models available to the platform key at launch; provider quotas can change during a session.
|
|
159
|
+
|
|
160
|
+
`cr start` checks the selected model's actual tool call, tool-result continuation, and streamed completion before opening a client. Successful checks are cached for six hours by gateway and platform key. Confirmed protocol rejections are removed from that harness's selector; quota, authorization, and connection failures are reported as temporary availability failures. Use `cr verify --all-models --output compatibility.json` to audit every available tool-capable model across Responses, Chat Completions, Anthropic Messages, and Gemini. `cr verify codex --model <id>` checks one harness protocol. Reports distinguish gateway protocol checks from native client tests. `--no-verify-model` skips this check for a previously verified private gateway.
|
|
161
|
+
|
|
162
|
+
Grok Build uses its documented custom-model configuration in an isolated `GROK_HOME`, with the full pool catalog and credentials supplied through the child environment. Install Grok from its official installer, or pass `--path`. Grok Bot is a separate desktop product; its native bot/account services are not interchangeable with Grok Build's custom inference endpoints.
|
|
159
163
|
|
|
160
164
|
macOS app bundles are discovered in `/Applications` and `~/Applications`. Common Windows and Linux installation paths are checked, with `--path` available for other installations. Live tests verified Cursor, Claude's Code and Cowork views, and Antigravity inference through ClosedRouter. Cursor and Antigravity switched between `gpt-5-nano` and `gpt-4.1-nano` in their native pickers. Cowork could answer through the gateway, but its workspace VM reported insufficient disk space on the test Mac; workspace actions were not verified. Gemma's full agent request previously received an upstream quota error. Desktop hooks fail closed when expected controllers change. See the [Codex provider configuration](https://developers.openai.com/codex/config-advanced) and [Electron fuse documentation](https://www.electronjs.org/docs/latest/tutorial/fuses) for configuration and inspector behavior.
|
|
161
165
|
|
package/dist/asar.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asar.d.ts","sourceRoot":"","sources":["../src/asar.ts"],"names":[],"mappings":"AAIA,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBzE"}
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DesktopLaunchPlan } from "./desktop-launch";
|
|
2
|
+
import type { DesktopAccountIdentity } from "./desktop-account";
|
|
3
|
+
/** Configure the supported app-server provider first; the startup hook only
|
|
4
|
+
* adds the provider mark to its native model controls. */
|
|
5
|
+
export declare function addCodexDesktopHook(plan: DesktopLaunchPlan, app: string, models: string[], account?: DesktopAccountIdentity): void;
|
|
6
|
+
//# sourceMappingURL=codex-desktop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-desktop.d.ts","sourceRoot":"","sources":["../../src/commands/codex-desktop.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAIhE;0DAC0D;AAC1D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAUlI"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor-desktop-account.d.ts","sourceRoot":"","sources":["../../src/commands/cursor-desktop-account.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAGhE;8EAC8E;AAC9E,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"cursor-desktop-account.d.ts","sourceRoot":"","sources":["../../src/commands/cursor-desktop-account.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAGhE;8EAC8E;AAC9E,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAyDrG;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CAKjF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"desktop-launch.d.ts","sourceRoot":"","sources":["../../src/commands/desktop-launch.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;AAMhE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE;QACd,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAClD,iBAAiB,CAAC,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,CAAC;KACvG,CAAC;CACH;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,WAAW,CAE/D;AAED,iEAAiE;AACjE,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,kBAAmB,EAAE,IAAI,SAAY,GACtF,MAAM,CAoBR;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAIxD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAI9D;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EACzD,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EACnD,QAAQ,GAAE,MAAM,EAAO,EAAE,QAAQ,kBAAmB,EACpD,MAAM,GAAE,MAAM,EAAyB,GACtC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"desktop-launch.d.ts","sourceRoot":"","sources":["../../src/commands/desktop-launch.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;AAMhE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE;QACd,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAClD,iBAAiB,CAAC,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,CAAC;KACvG,CAAC;CACH;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,WAAW,CAE/D;AAED,iEAAiE;AACjE,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,kBAAmB,EAAE,IAAI,SAAY,GACtF,MAAM,CAoBR;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAIxD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAI9D;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EACzD,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EACnD,QAAQ,GAAE,MAAM,EAAO,EAAE,QAAQ,kBAAmB,EACpD,MAAM,GAAE,MAAM,EAAyB,GACtC,iBAAiB,CAmDnB;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAQpE;AAED,2EAA2E;AAC3E,wBAAgB,iCAAiC,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAO1E;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB/E"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export declare const CLOSEDROUTER_LOGO = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><rect width=\"24\" height=\"24\" rx=\"6\" fill=\"#D4FE51\"/><path d=\"M16.68 7.82A6.5 6.5 0 1 0 16.68 16.18\" fill=\"none\" stroke=\"#04080A\" stroke-width=\"2.6\" stroke-linecap=\"round\"/><circle cx=\"18.2\" cy=\"12\" r=\"1.9\" fill=\"#04080A\"/></svg>";
|
|
2
2
|
import type { DesktopAccountIdentity } from "./desktop-account";
|
|
3
|
-
export declare const CLOSEDROUTER_ELECTRIC_CSS = "\n[data-closedrouter-logo]{position:relative;isolation:isolate}\n[data-closedrouter-charged-control]{
|
|
4
|
-
/** Position a bolt along a tilted orbit
|
|
5
|
-
*
|
|
3
|
+
export declare const CLOSEDROUTER_ELECTRIC_CSS = "\n[data-closedrouter-logo]{position:relative;isolation:isolate}\n[data-closedrouter-charged-control]{overflow:visible!important}\n[data-closedrouter-supercharged]{perspective:80px;transform-style:preserve-3d;isolation:auto;overflow:visible}\n[data-closedrouter-electric]{position:absolute;inset:0;pointer-events:none;transform-style:preserve-3d;opacity:0}\n[data-cr-lightning-orbit]{position:absolute;left:50%;top:50%;width:0;height:0;transform-style:preserve-3d}\n[data-cr-lightning-bolt]{position:absolute;width:14px;height:22px;left:-7px;top:-11px;overflow:visible;filter:drop-shadow(0 0 3px #58caff) drop-shadow(0 0 7px #159aff)}\n@media(prefers-reduced-motion:no-preference){[data-closedrouter-electric]{opacity:1}}\n";
|
|
4
|
+
/** Position a bolt along a tilted orbit around the provider logo in world
|
|
5
|
+
* space. The bolt keeps its orientation as its x, y and depth change. */
|
|
6
6
|
export declare function lightningOrbitKeyframes(width: number, height: number, phase: number): Keyframe[];
|
|
7
7
|
/** Add the provider mark to native controls without changing click handlers,
|
|
8
8
|
* model IDs, keyboard behavior, or the application's typography. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-branding.d.ts","sourceRoot":"","sources":["../../src/commands/model-branding.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB,mUAAqS,CAAC;AACpU,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAEhE,eAAO,MAAM,yBAAyB,
|
|
1
|
+
{"version":3,"file":"model-branding.d.ts","sourceRoot":"","sources":["../../src/commands/model-branding.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB,mUAAqS,CAAC;AACpU,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAEhE,eAAO,MAAM,yBAAyB,utBAQrC,CAAC;AAEF;yEACyE;AACzE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE,CAiBhG;AAED;oEACoE;AACpE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,uBAAuB,GAAG,IAAI,CAuFjJ;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAoC/F;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM,CAS5F"}
|
package/dist/commands/start.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { type GatewayModel } from "../client";
|
|
7
7
|
import { type LaunchCatalog } from "./launch-catalog";
|
|
8
|
-
export type StartTool = "claude" | "codex" | "gemini" | "agy" | "aider" | "opencode" | "cursor";
|
|
8
|
+
export type StartTool = "claude" | "codex" | "gemini" | "agy" | "aider" | "opencode" | "cursor" | "grok";
|
|
9
9
|
type CliTool = StartTool;
|
|
10
10
|
export interface StartCommandOptions {
|
|
11
11
|
tool?: string;
|
|
@@ -18,6 +18,7 @@ export interface StartCommandOptions {
|
|
|
18
18
|
desktop?: boolean;
|
|
19
19
|
openOnly?: boolean;
|
|
20
20
|
desktopInspector?: boolean;
|
|
21
|
+
verifyModel?: boolean;
|
|
21
22
|
}
|
|
22
23
|
interface ConfigFile {
|
|
23
24
|
path: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAelE,OAAO,EAA6F,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGjJ,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AACzG,KAAK,OAAO,GAAG,SAAS,CAAC;AAEzB,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,wBAAgB,uBAAuB,CAAC,eAAe,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEzF;AAoBD,wBAAgB,mBAAmB,CAAC,WAAW,UAAQ;;;;IAEtD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAUxG;AAqBD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAM,EAAO,EACvB,OAAO,SAAY,EACnB,cAAc,CAAC,EAAE,MAAM,EACvB,UAAU,CAAC,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,EACzD,OAAO,CAAC,EAAE,aAAa,GACtB,UAAU,CAiJZ;AAeD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAKlF;AAuED,wBAAsB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiJnF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"AAMA,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE;IAAE,MAAM,CAAC,EAAC,MAAM,CAAC;IAAA,GAAG,CAAC,EAAC,MAAM,CAAC;IAAA,KAAK,CAAC,EAAC,MAAM,CAAC;IAAA,SAAS,CAAC,EAAC,OAAO,CAAC;IAAA,MAAM,CAAC,EAAC,MAAM,CAAC;IAAA,WAAW,CAAC,EAAC,MAAM,CAAA;CAAE,GAAE,OAAO,CAAC,IAAI,CAAC,CAqBvL"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare const PROTOCOLS: readonly ["responses", "chat", "messages", "gemini"];
|
|
2
|
+
export type HarnessProtocol = typeof PROTOCOLS[number];
|
|
3
|
+
export type CompatibilityStatus = "passed" | "incompatible" | "unavailable" | "unverified";
|
|
4
|
+
export interface CompatibilityResult {
|
|
5
|
+
model: string;
|
|
6
|
+
protocol: HarnessProtocol;
|
|
7
|
+
status: CompatibilityStatus;
|
|
8
|
+
checkedAt: string;
|
|
9
|
+
latencyMs: number;
|
|
10
|
+
stage: "tool-call" | "tool-result";
|
|
11
|
+
httpStatus?: number;
|
|
12
|
+
code?: string;
|
|
13
|
+
reason?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface CompatibilityReport {
|
|
16
|
+
version: 2;
|
|
17
|
+
results: CompatibilityResult[];
|
|
18
|
+
}
|
|
19
|
+
export declare function harnessProtocol(tool: string): HarnessProtocol;
|
|
20
|
+
export declare function loadCompatibility(server: string, key: string): CompatibilityReport;
|
|
21
|
+
export declare function saveCompatibility(server: string, key: string, results: CompatibilityResult[]): CompatibilityReport;
|
|
22
|
+
export declare function recentCompatibility(report: CompatibilityReport, model: string, protocol: HarnessProtocol): CompatibilityResult | undefined;
|
|
23
|
+
/** A quota failure is not evidence that a harness cannot use the model. */
|
|
24
|
+
export declare function classifyProbeHttp(status: number): CompatibilityStatus;
|
|
25
|
+
export declare function streamedText(raw: string, protocol: HarnessProtocol): {
|
|
26
|
+
text: string;
|
|
27
|
+
complete: boolean;
|
|
28
|
+
};
|
|
29
|
+
/** Check a real tool call, then return a new nonce only in the tool result.
|
|
30
|
+
* Successful HTTP status or a plain greeting alone does not pass this check. */
|
|
31
|
+
export declare function probeCompatibility(server: string, key: string, model: string, protocol: HarnessProtocol): Promise<CompatibilityResult>;
|
|
32
|
+
//# sourceMappingURL=compatibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compatibility.d.ts","sourceRoot":"","sources":["../src/compatibility.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,SAAS,YAAI,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,CAAU,CAAC;AAC9E,MAAM,MAAM,eAAe,GAAG,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC;AACvD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,cAAc,GAAG,aAAa,GAAG,YAAY,CAAC;AAC3F,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAC;IACtE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,GAAG,aAAa,CAAC;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACrD;AACD,MAAM,WAAW,mBAAmB;IAAG,OAAO,EAAE,CAAC,CAAC;IAAC,OAAO,EAAE,mBAAmB,EAAE,CAAA;CAAE;AAEnF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAI7D;AAOD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,mBAAmB,CASlF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,GAAG,mBAAmB,CAQlH;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,mBAAmB,GAAG,SAAS,CAG1I;AAMD,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAErE;AAyBD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CA6BxG;AAED;gFACgF;AAChF,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAqC5I"}
|
package/dist/index.js
CHANGED
|
@@ -5204,7 +5204,7 @@ var {
|
|
|
5204
5204
|
} = exports_commander;
|
|
5205
5205
|
|
|
5206
5206
|
// src/index.ts
|
|
5207
|
-
import { readFileSync as
|
|
5207
|
+
import { readFileSync as readFileSync17 } from "node:fs";
|
|
5208
5208
|
|
|
5209
5209
|
// ../node_modules/.bun/@clack+prompts@0.11.0/node_modules/@clack/prompts/dist/index.mjs
|
|
5210
5210
|
import { stripVTControlCharacters as S2 } from "node:util";
|
|
@@ -6182,10 +6182,24 @@ class ClosedRouterClient {
|
|
|
6182
6182
|
return data.data || data.api_keys || data.keys || [];
|
|
6183
6183
|
}
|
|
6184
6184
|
async listGatewayModels(options = {}) {
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6185
|
+
let res;
|
|
6186
|
+
for (let attempt = 0;attempt < 3; attempt++) {
|
|
6187
|
+
try {
|
|
6188
|
+
res = await fetch(`${this.baseUrl}/v1/models`, {
|
|
6189
|
+
headers: this.getHeaders({}, options.preferApiKey === true),
|
|
6190
|
+
signal: AbortSignal.timeout(1e4)
|
|
6191
|
+
});
|
|
6192
|
+
if (![502, 503, 504].includes(res.status) || attempt === 2)
|
|
6193
|
+
break;
|
|
6194
|
+
await res.body?.cancel();
|
|
6195
|
+
} catch (error) {
|
|
6196
|
+
if (attempt === 2)
|
|
6197
|
+
throw error;
|
|
6198
|
+
}
|
|
6199
|
+
await new Promise((resolve) => setTimeout(resolve, 250 * (attempt + 1)));
|
|
6200
|
+
}
|
|
6201
|
+
if (!res)
|
|
6202
|
+
throw new Error("Could not connect to the gateway model catalog.");
|
|
6189
6203
|
if (!res.ok) {
|
|
6190
6204
|
throw new Error(`Failed to list gateway models (HTTP ${res.status})`);
|
|
6191
6205
|
}
|
|
@@ -8656,12 +8670,12 @@ to scan and import local subscriptions!`, "Pool Empty");
|
|
|
8656
8670
|
|
|
8657
8671
|
// src/commands/start.ts
|
|
8658
8672
|
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
8659
|
-
import { randomUUID as
|
|
8673
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
8660
8674
|
import { spawn as spawn5 } from "node:child_process";
|
|
8661
8675
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
8662
|
-
import { mkdirSync as
|
|
8676
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync16, writeFileSync as writeFileSync6 } from "node:fs";
|
|
8663
8677
|
import { homedir as homedir15 } from "node:os";
|
|
8664
|
-
import { dirname as dirname5, join as
|
|
8678
|
+
import { dirname as dirname5, join as join19 } from "node:path";
|
|
8665
8679
|
|
|
8666
8680
|
// src/model-picker.ts
|
|
8667
8681
|
import { emitKeypressEvents as emitKeypressEvents2 } from "node:readline";
|
|
@@ -9435,7 +9449,7 @@ function createLaunchCatalog(models, selected) {
|
|
|
9435
9449
|
[transportModelId(id, "claude"), id],
|
|
9436
9450
|
[transportModelId(id, "gemini"), id]
|
|
9437
9451
|
])),
|
|
9438
|
-
fallbacks: ["claude-sonnet-4-6", "claude-haiku-4-5-20251001", "gemini-3.8-flash", "gemini-3.8-flash-medium", "gemini-3.8-flash-high", "gemini-2.5-flash", "gemini-2.5-flash-lite"]
|
|
9452
|
+
fallbacks: ["claude-sonnet-4-6", "claude-haiku-4-5-20251001", "gemini-3.8-flash", "gemini-3.8-flash-medium", "gemini-3.8-flash-high", "gemini-2.5-flash", "gemini-2.5-flash-lite", "grok-4.6", "grok-4.7", "grok-build"]
|
|
9439
9453
|
};
|
|
9440
9454
|
}
|
|
9441
9455
|
function resolveLaunchModel(catalog, requested) {
|
|
@@ -9480,8 +9494,8 @@ function codexModelCatalog(models) {
|
|
|
9480
9494
|
support_verbosity: false,
|
|
9481
9495
|
prefer_websockets: false,
|
|
9482
9496
|
supports_image_detail_original: false,
|
|
9483
|
-
context_window:
|
|
9484
|
-
auto_compact_token_limit:
|
|
9497
|
+
context_window: 32768,
|
|
9498
|
+
auto_compact_token_limit: 28000,
|
|
9485
9499
|
truncation_policy: { mode: "tokens", limit: 6000 }
|
|
9486
9500
|
})) };
|
|
9487
9501
|
}
|
|
@@ -9990,7 +10004,9 @@ function buildDesktopLaunchPlan(tool, executable, profileDir, baseUrl, localKey,
|
|
|
9990
10004
|
'name = "ClosedRouter"',
|
|
9991
10005
|
`base_url = ${JSON.stringify(`${baseUrl.replace(/\/+$/, "")}/v1`)}`,
|
|
9992
10006
|
'wire_api = "responses"',
|
|
9993
|
-
"requires_openai_auth =
|
|
10007
|
+
"requires_openai_auth = false",
|
|
10008
|
+
"supports_websockets = false",
|
|
10009
|
+
`experimental_bearer_token = ${JSON.stringify(localKey)}`,
|
|
9994
10010
|
""
|
|
9995
10011
|
].join(`
|
|
9996
10012
|
`) });
|
|
@@ -10270,10 +10286,11 @@ app.on('web-contents-created', (_event, contents) => {
|
|
|
10270
10286
|
var CLOSEDROUTER_LOGO = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect width="24" height="24" rx="6" fill="#D4FE51"/><path d="M16.68 7.82A6.5 6.5 0 1 0 16.68 16.18" fill="none" stroke="#04080A" stroke-width="2.6" stroke-linecap="round"/><circle cx="18.2" cy="12" r="1.9" fill="#04080A"/></svg>';
|
|
10271
10287
|
var CLOSEDROUTER_ELECTRIC_CSS = `
|
|
10272
10288
|
[data-closedrouter-logo]{position:relative;isolation:isolate}
|
|
10273
|
-
[data-closedrouter-charged-control]{
|
|
10289
|
+
[data-closedrouter-charged-control]{overflow:visible!important}
|
|
10290
|
+
[data-closedrouter-supercharged]{perspective:80px;transform-style:preserve-3d;isolation:auto;overflow:visible}
|
|
10274
10291
|
[data-closedrouter-electric]{position:absolute;inset:0;pointer-events:none;transform-style:preserve-3d;opacity:0}
|
|
10275
10292
|
[data-cr-lightning-orbit]{position:absolute;left:50%;top:50%;width:0;height:0;transform-style:preserve-3d}
|
|
10276
|
-
[data-cr-lightning-bolt]{position:absolute;width:
|
|
10293
|
+
[data-cr-lightning-bolt]{position:absolute;width:14px;height:22px;left:-7px;top:-11px;overflow:visible;filter:drop-shadow(0 0 3px #58caff) drop-shadow(0 0 7px #159aff)}
|
|
10277
10294
|
@media(prefers-reduced-motion:no-preference){[data-closedrouter-electric]{opacity:1}}
|
|
10278
10295
|
`;
|
|
10279
10296
|
function lightningOrbitKeyframes(width, height, phase) {
|
|
@@ -10281,10 +10298,11 @@ function lightningOrbitKeyframes(width, height, phase) {
|
|
|
10281
10298
|
for (let step = 0;step <= 40; step++) {
|
|
10282
10299
|
const progress = step / 40;
|
|
10283
10300
|
const angle = (progress + phase) * Math.PI * 2;
|
|
10284
|
-
const
|
|
10285
|
-
const
|
|
10286
|
-
const
|
|
10287
|
-
const
|
|
10301
|
+
const radius = Math.max(width, height) / 2 + 8;
|
|
10302
|
+
const x = (width / 2 + 8) * Math.cos(angle);
|
|
10303
|
+
const y = (height / 2 + 4) * Math.sin(angle) * Math.sin(Math.PI / 15);
|
|
10304
|
+
const z = Math.min(radius, 32) * Math.sin(angle) * Math.cos(Math.PI / 15);
|
|
10305
|
+
const scale = 80 / (80 - z);
|
|
10288
10306
|
const behindButton = z < -1 && Math.abs(x * scale) < width / 2 && Math.abs(y * scale) < height / 2;
|
|
10289
10307
|
const fade = Math.min(1, progress * 20, (1 - progress) * 20);
|
|
10290
10308
|
frames.push({ offset: progress / 5, transform: `translate3d(${x}px,${y}px,${z}px)`, opacity: behindButton ? 0 : fade * (z < 0 ? 0.55 : 1) });
|
|
@@ -10307,7 +10325,7 @@ function installModelBranding(models, artwork, electricCss, orbitKeyframes) {
|
|
|
10307
10325
|
const previous = charged.get(button) || [];
|
|
10308
10326
|
const time = previous[0]?.currentTime;
|
|
10309
10327
|
previous.forEach((animation) => animation.cancel());
|
|
10310
|
-
const { width, height } = button.getBoundingClientRect();
|
|
10328
|
+
const { width = 16, height = 16 } = button.querySelector("[data-closedrouter-supercharged]")?.getBoundingClientRect() || {};
|
|
10311
10329
|
const animations = motion.matches ? [] : [...button.querySelectorAll("[data-cr-lightning-bolt]")].map((bolt, index) => {
|
|
10312
10330
|
const animation = bolt.animate(orbitKeyframes(width, height, index / 2), { duration: 5000, iterations: Infinity, easing: "linear" });
|
|
10313
10331
|
if (typeof time === "number")
|
|
@@ -10380,7 +10398,7 @@ function installModelBranding(models, artwork, electricCss, orbitKeyframes) {
|
|
|
10380
10398
|
orbit.append(bolt);
|
|
10381
10399
|
}
|
|
10382
10400
|
layer.append(orbit);
|
|
10383
|
-
|
|
10401
|
+
logo.append(layer);
|
|
10384
10402
|
const restart = () => charged.get(element)?.forEach((animation) => {
|
|
10385
10403
|
animation.currentTime = 0;
|
|
10386
10404
|
});
|
|
@@ -10389,6 +10407,8 @@ function installModelBranding(models, artwork, electricCss, orbitKeyframes) {
|
|
|
10389
10407
|
resize.observe(element);
|
|
10390
10408
|
}
|
|
10391
10409
|
element.prepend(logo);
|
|
10410
|
+
if (logo.dataset.closedrouterSupercharged)
|
|
10411
|
+
animate(element);
|
|
10392
10412
|
}
|
|
10393
10413
|
};
|
|
10394
10414
|
let scheduled = false;
|
|
@@ -10971,6 +10991,9 @@ function installCursorProviderAccountUi(account, artwork) {
|
|
|
10971
10991
|
const target = document.querySelector('[aria-label="Loading account information"],[aria-label="Accounts"]') || upgrade || [...document.querySelectorAll("button,[role=button]")].find((node) => /^(Sign in|Sign In|Accounts)$/.test(node.innerText.trim()));
|
|
10972
10992
|
if (!target?.parentElement)
|
|
10973
10993
|
return;
|
|
10994
|
+
const nativeAvatar = target.parentElement.querySelector(":scope > .ui-sidebar-menu-button-icon-wrapper");
|
|
10995
|
+
if (nativeAvatar)
|
|
10996
|
+
nativeAvatar.dataset.closedrouterReplacedAccount = "true";
|
|
10974
10997
|
if (button.isConnected && button.nextElementSibling === target)
|
|
10975
10998
|
return;
|
|
10976
10999
|
close();
|
|
@@ -11100,10 +11123,65 @@ require('node:fs').writeFileSync(${JSON.stringify(join15(plan.profileDir, "hook-
|
|
|
11100
11123
|
plan.args.splice(plan.args.indexOf("--args"), 0, "--stderr", logPath);
|
|
11101
11124
|
}
|
|
11102
11125
|
|
|
11126
|
+
// src/commands/codex-desktop.ts
|
|
11127
|
+
import { join as join16 } from "node:path";
|
|
11128
|
+
|
|
11129
|
+
// src/asar.ts
|
|
11130
|
+
import { closeSync as closeSync2, openSync as openSync2, readSync, statSync as statSync3 } from "node:fs";
|
|
11131
|
+
function readAsarSources(archive, prefix) {
|
|
11132
|
+
const fd = openSync2(archive, "r");
|
|
11133
|
+
try {
|
|
11134
|
+
const size = Buffer.alloc(8);
|
|
11135
|
+
readSync(fd, size, 0, 8, 0);
|
|
11136
|
+
const length = size.readUInt32LE(4);
|
|
11137
|
+
if (length < 8 || length > 32 * 1024 * 1024)
|
|
11138
|
+
throw new Error("Unsupported Electron archive header.");
|
|
11139
|
+
const header = Buffer.alloc(length);
|
|
11140
|
+
readSync(fd, header, 0, length, 8);
|
|
11141
|
+
const tree = JSON.parse(header.subarray(8, 8 + header.readUInt32LE(4)).toString("utf8"));
|
|
11142
|
+
const sources = [];
|
|
11143
|
+
const archiveSize = statSync3(archive).size;
|
|
11144
|
+
const visit = (entry, path) => {
|
|
11145
|
+
if (entry.files) {
|
|
11146
|
+
for (const [name, child] of Object.entries(entry.files))
|
|
11147
|
+
visit(child, path ? `${path}/${name}` : name);
|
|
11148
|
+
return;
|
|
11149
|
+
}
|
|
11150
|
+
if (!path.startsWith(prefix) || !path.endsWith(".js") || entry.offset === undefined || entry.size === undefined)
|
|
11151
|
+
return;
|
|
11152
|
+
const offset = 8 + length + Number(entry.offset);
|
|
11153
|
+
if (!Number.isSafeInteger(offset) || offset < 8 + length || entry.size > 32 * 1024 * 1024 || offset + entry.size > archiveSize)
|
|
11154
|
+
throw new Error("Invalid Electron source member.");
|
|
11155
|
+
const bytes = Buffer.alloc(entry.size);
|
|
11156
|
+
readSync(fd, bytes, 0, entry.size, offset);
|
|
11157
|
+
sources.push(bytes.toString("utf8"));
|
|
11158
|
+
};
|
|
11159
|
+
visit(tree, "");
|
|
11160
|
+
return sources;
|
|
11161
|
+
} finally {
|
|
11162
|
+
closeSync2(fd);
|
|
11163
|
+
}
|
|
11164
|
+
}
|
|
11165
|
+
|
|
11166
|
+
// src/commands/codex-desktop.ts
|
|
11167
|
+
function addCodexDesktopHook(plan, app, models, account) {
|
|
11168
|
+
const archive = app.endsWith(".app") ? join16(app, "Contents/Resources/app.asar") : join16(app, "../resources/app.asar");
|
|
11169
|
+
if (!readAsarSources(archive, ".vite/build/bootstrap-").some((source) => source.includes("CODEX_ELECTRON_USER_DATA_PATH")))
|
|
11170
|
+
throw new Error("This Codex app build does not support isolated provider profiles. Update the app before launching through ClosedRouter.");
|
|
11171
|
+
const hook = join16(plan.profileDir, "codex-hook.cjs");
|
|
11172
|
+
const marker = join16(plan.profileDir, "codex-hook-loaded");
|
|
11173
|
+
const log = join16(plan.profileDir, "inspector.log");
|
|
11174
|
+
plan.files.push({ path: hook, contents: modelBrandingHook(models, account) + `require('node:fs').writeFileSync(${JSON.stringify(marker)},'ready',{mode:0o600});` });
|
|
11175
|
+
plan.inspectorHook = { path: hook, logPath: log, readyPath: marker };
|
|
11176
|
+
plan.args.push("--inspect-brk=127.0.0.1:0");
|
|
11177
|
+
if (plan.command === "/usr/bin/open")
|
|
11178
|
+
plan.args.splice(plan.args.indexOf("--args"), 0, "--stderr", log);
|
|
11179
|
+
}
|
|
11180
|
+
|
|
11103
11181
|
// src/commands/desktop-account.ts
|
|
11104
11182
|
import { existsSync as existsSync16, readFileSync as readFileSync14 } from "node:fs";
|
|
11105
11183
|
import { homedir as homedir14 } from "node:os";
|
|
11106
|
-
import { join as
|
|
11184
|
+
import { join as join17 } from "node:path";
|
|
11107
11185
|
function desktopAccountIdentity(config, server, key) {
|
|
11108
11186
|
const sameAccount = config.apiKey === key && config.serverUrl.replace(/\/+$/, "") === server.replace(/\/+$/, "");
|
|
11109
11187
|
const user = sameAccount ? config.user : undefined;
|
|
@@ -11115,8 +11193,8 @@ function desktopAccountIdentity(config, server, key) {
|
|
|
11115
11193
|
serverOrigin: new URL(server).origin
|
|
11116
11194
|
};
|
|
11117
11195
|
}
|
|
11118
|
-
function antigravityNativeAccountExists(geminiDir = process.env.CLOSEDROUTER_ANTIGRAVITY_NATIVE_DIR ||
|
|
11119
|
-
const path =
|
|
11196
|
+
function antigravityNativeAccountExists(geminiDir = process.env.CLOSEDROUTER_ANTIGRAVITY_NATIVE_DIR || join17(homedir14(), ".gemini")) {
|
|
11197
|
+
const path = join17(geminiDir, "jetski-standalone-oauth-token");
|
|
11120
11198
|
if (!existsSync16(path))
|
|
11121
11199
|
return false;
|
|
11122
11200
|
try {
|
|
@@ -11127,6 +11205,194 @@ function antigravityNativeAccountExists(geminiDir = process.env.CLOSEDROUTER_ANT
|
|
|
11127
11205
|
}
|
|
11128
11206
|
}
|
|
11129
11207
|
|
|
11208
|
+
// src/compatibility.ts
|
|
11209
|
+
import { createHash as createHash4, randomUUID as randomUUID5 } from "node:crypto";
|
|
11210
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync15, writeFileSync as writeFileSync5 } from "node:fs";
|
|
11211
|
+
import { join as join18 } from "node:path";
|
|
11212
|
+
var PROTOCOLS = ["responses", "chat", "messages", "gemini"];
|
|
11213
|
+
function harnessProtocol(tool) {
|
|
11214
|
+
const mapping = { codex: "responses", grok: "responses", "grok-build": "responses", claude: "messages", agy: "gemini", antigravity: "gemini", gemini: "gemini", cursor: "chat", aider: "chat", opencode: "chat" };
|
|
11215
|
+
if (!Object.hasOwn(mapping, tool))
|
|
11216
|
+
throw new Error(`Unsupported harness ${JSON.stringify(tool)}.`);
|
|
11217
|
+
return mapping[tool];
|
|
11218
|
+
}
|
|
11219
|
+
function reportPath(server, key) {
|
|
11220
|
+
const scope = createHash4("sha256").update(`${server.replace(/\/+$/, "")}\x00${key}`).digest("hex");
|
|
11221
|
+
return join18(getConfigDir(), "compatibility", `${scope}.json`);
|
|
11222
|
+
}
|
|
11223
|
+
function loadCompatibility(server, key) {
|
|
11224
|
+
try {
|
|
11225
|
+
const report = JSON.parse(readFileSync15(reportPath(server, key), "utf8"));
|
|
11226
|
+
if (report.version === 2 && Array.isArray(report.results) && report.results.every((item) => item && typeof item.model === "string" && PROTOCOLS.includes(item.protocol) && ["passed", "incompatible", "unavailable", "unverified"].includes(item.status) && Number.isFinite(Date.parse(item.checkedAt))))
|
|
11227
|
+
return report;
|
|
11228
|
+
} catch {}
|
|
11229
|
+
return { version: 2, results: [] };
|
|
11230
|
+
}
|
|
11231
|
+
function saveCompatibility(server, key, results) {
|
|
11232
|
+
const report = loadCompatibility(server, key);
|
|
11233
|
+
for (const result of results) {
|
|
11234
|
+
report.results = report.results.filter((item) => item.model !== result.model || item.protocol !== result.protocol);
|
|
11235
|
+
report.results.push(result);
|
|
11236
|
+
}
|
|
11237
|
+
const path = reportPath(server, key);
|
|
11238
|
+
mkdirSync5(join18(getConfigDir(), "compatibility"), { recursive: true, mode: 448 });
|
|
11239
|
+
writeFileSync5(path, JSON.stringify(report, null, 2) + `
|
|
11240
|
+
`, { mode: 384 });
|
|
11241
|
+
return report;
|
|
11242
|
+
}
|
|
11243
|
+
function recentCompatibility(report, model, protocol) {
|
|
11244
|
+
return report.results.find((result) => result.model === model && result.protocol === protocol && Date.now() - Date.parse(result.checkedAt) < (result.status === "unavailable" ? 60000 : 6 * 60 * 60 * 1000));
|
|
11245
|
+
}
|
|
11246
|
+
|
|
11247
|
+
class ProbeFailure extends Error {
|
|
11248
|
+
status;
|
|
11249
|
+
code;
|
|
11250
|
+
httpStatus;
|
|
11251
|
+
constructor(status, code, message, httpStatus) {
|
|
11252
|
+
super(message);
|
|
11253
|
+
this.status = status;
|
|
11254
|
+
this.code = code;
|
|
11255
|
+
this.httpStatus = httpStatus;
|
|
11256
|
+
}
|
|
11257
|
+
}
|
|
11258
|
+
function classifyProbeHttp(status) {
|
|
11259
|
+
return status === 400 || status === 422 ? "incompatible" : "unavailable";
|
|
11260
|
+
}
|
|
11261
|
+
var TOOL = "closedrouter_probe";
|
|
11262
|
+
var PARAMETERS = { type: "object", properties: { ticket: { type: "string" } }, required: ["ticket"], additionalProperties: false };
|
|
11263
|
+
function toolCall(data, protocol) {
|
|
11264
|
+
const call = protocol === "responses" ? data.output?.find((item) => item.type === "function_call" && item.name === TOOL) : protocol === "messages" ? data.content?.find((item) => item.type === "tool_use" && item.name === TOOL) : protocol === "gemini" ? data.candidates?.[0]?.content?.parts?.map((part) => part.functionCall).find((call) => call?.name === TOOL) : data.choices?.[0]?.message?.tool_calls?.find((item) => item.function?.name === TOOL);
|
|
11265
|
+
if (!call)
|
|
11266
|
+
throw new ProbeFailure("unverified", "missing_tool_call", "The model did not produce the required tool call.");
|
|
11267
|
+
let args;
|
|
11268
|
+
try {
|
|
11269
|
+
args = protocol === "chat" ? JSON.parse(call.function.arguments) : protocol === "responses" ? JSON.parse(call.arguments) : protocol === "messages" ? call.input : call.args;
|
|
11270
|
+
} catch {
|
|
11271
|
+
throw new ProbeFailure("incompatible", "invalid_tool_arguments", "The tool call arguments were not valid JSON.");
|
|
11272
|
+
}
|
|
11273
|
+
return { id: call.call_id || call.id || TOOL, ticket: args?.ticket };
|
|
11274
|
+
}
|
|
11275
|
+
function textFrom(data, protocol) {
|
|
11276
|
+
if (protocol === "responses")
|
|
11277
|
+
return data.output_text || (data.output || []).flatMap((item) => item.content || []).map((part) => part.text || "").join("");
|
|
11278
|
+
if (protocol === "messages")
|
|
11279
|
+
return (data.content || []).map((part) => part.text || "").join("");
|
|
11280
|
+
if (protocol === "gemini")
|
|
11281
|
+
return (data.candidates?.[0]?.content?.parts || []).map((part) => part.text || "").join("");
|
|
11282
|
+
return data.choices?.[0]?.message?.content || "";
|
|
11283
|
+
}
|
|
11284
|
+
function streamedText(raw, protocol) {
|
|
11285
|
+
if (!raw.includes("data:")) {
|
|
11286
|
+
try {
|
|
11287
|
+
return { text: textFrom(JSON.parse(raw), protocol), complete: true };
|
|
11288
|
+
} catch {
|
|
11289
|
+
return { text: "", complete: false };
|
|
11290
|
+
}
|
|
11291
|
+
}
|
|
11292
|
+
let text = "", complete = false, lastSequence = -1;
|
|
11293
|
+
for (const record of raw.replace(/\r\n/g, `
|
|
11294
|
+
`).split(`
|
|
11295
|
+
|
|
11296
|
+
`)) {
|
|
11297
|
+
const payload = record.split(`
|
|
11298
|
+
`).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).join(`
|
|
11299
|
+
`);
|
|
11300
|
+
if (!payload)
|
|
11301
|
+
continue;
|
|
11302
|
+
if (payload === "[DONE]") {
|
|
11303
|
+
complete = true;
|
|
11304
|
+
continue;
|
|
11305
|
+
}
|
|
11306
|
+
let event;
|
|
11307
|
+
try {
|
|
11308
|
+
event = JSON.parse(payload);
|
|
11309
|
+
} catch {
|
|
11310
|
+
continue;
|
|
11311
|
+
}
|
|
11312
|
+
if (event.error || event.type === "error" || event.type === "response.failed")
|
|
11313
|
+
throw new ProbeFailure("unavailable", "stream_error", "The gateway returned an error during streaming.");
|
|
11314
|
+
if (protocol === "responses") {
|
|
11315
|
+
if (typeof event.type === "string" && event.type.startsWith("response.")) {
|
|
11316
|
+
if (!Number.isInteger(event.sequence_number) || event.sequence_number <= lastSequence)
|
|
11317
|
+
throw new ProbeFailure("incompatible", "invalid_stream_sequence", "Responses events lack an increasing sequence number required by strict clients.");
|
|
11318
|
+
lastSequence = event.sequence_number;
|
|
11319
|
+
}
|
|
11320
|
+
if (event.type === "response.output_text.delta")
|
|
11321
|
+
text += event.delta || "";
|
|
11322
|
+
if (event.type === "response.completed") {
|
|
11323
|
+
complete = true;
|
|
11324
|
+
if (!text)
|
|
11325
|
+
text = textFrom(event.response || {}, protocol);
|
|
11326
|
+
}
|
|
11327
|
+
} else if (protocol === "messages") {
|
|
11328
|
+
if (event.type === "content_block_delta" && event.delta?.type === "text_delta")
|
|
11329
|
+
text += event.delta.text || "";
|
|
11330
|
+
if (event.type === "message_stop")
|
|
11331
|
+
complete = true;
|
|
11332
|
+
} else if (protocol === "gemini") {
|
|
11333
|
+
text += textFrom(event, protocol);
|
|
11334
|
+
if (event.candidates?.[0]?.finishReason)
|
|
11335
|
+
complete = true;
|
|
11336
|
+
} else {
|
|
11337
|
+
text += event.choices?.[0]?.delta?.content || "";
|
|
11338
|
+
if (event.choices?.[0]?.finish_reason)
|
|
11339
|
+
complete = true;
|
|
11340
|
+
}
|
|
11341
|
+
}
|
|
11342
|
+
return { text, complete };
|
|
11343
|
+
}
|
|
11344
|
+
async function probeCompatibility(server, key, model, protocol) {
|
|
11345
|
+
const started = Date.now(), ticket = randomUUID5(), proof = `CR_TOOL_OK_${randomUUID5().replace(/-/g, "")}`;
|
|
11346
|
+
let stage = "tool-call";
|
|
11347
|
+
const result = (status, extra = {}) => ({ model, protocol, status, stage, checkedAt: new Date().toISOString(), latencyMs: Date.now() - started, ...extra });
|
|
11348
|
+
const prompt = `Call the ${TOOL} tool exactly once with ticket ${ticket}. After its result, reply with only the exact text returned by the tool. Do not invent a tool result.`;
|
|
11349
|
+
const base = server.replace(/\/+$/, "");
|
|
11350
|
+
const url = protocol === "gemini" ? `${base}/v1beta/models/${encodeURIComponent(model)}:generateContent` : `${base}/v1/${protocol === "chat" ? "chat/completions" : protocol}`;
|
|
11351
|
+
const send = async (payload, stream = false) => {
|
|
11352
|
+
const endpoint = stream && protocol === "gemini" ? url.replace(":generateContent", ":streamGenerateContent") + "?alt=sse" : url;
|
|
11353
|
+
const response = await fetch(endpoint, { method: "POST", headers: { authorization: `Bearer ${key}`, "content-type": "application/json", "x-closedrouter-harness": `compatibility-${protocol}` }, body: JSON.stringify(payload), signal: AbortSignal.timeout(90000) });
|
|
11354
|
+
const body = await response.text();
|
|
11355
|
+
if (!response.ok)
|
|
11356
|
+
throw new ProbeFailure(classifyProbeHttp(response.status), `http_${response.status}`, `The gateway rejected the ${stage} check (HTTP ${response.status}).`, response.status);
|
|
11357
|
+
return body;
|
|
11358
|
+
};
|
|
11359
|
+
try {
|
|
11360
|
+
let first;
|
|
11361
|
+
if (protocol === "responses")
|
|
11362
|
+
first = { model, input: [{ role: "user", content: prompt }], tools: [{ type: "function", name: TOOL, parameters: PARAMETERS, strict: true }], tool_choice: { type: "function", name: TOOL }, max_output_tokens: 2048, store: false };
|
|
11363
|
+
else if (protocol === "messages")
|
|
11364
|
+
first = { model, messages: [{ role: "user", content: prompt }], tools: [{ name: TOOL, input_schema: PARAMETERS }], tool_choice: { type: "tool", name: TOOL }, max_tokens: 2048 };
|
|
11365
|
+
else if (protocol === "gemini")
|
|
11366
|
+
first = { contents: [{ role: "user", parts: [{ text: prompt }] }], tools: [{ functionDeclarations: [{ name: TOOL, parameters: PARAMETERS }] }], toolConfig: { functionCallingConfig: { mode: "ANY", allowedFunctionNames: [TOOL] } }, generationConfig: { maxOutputTokens: 2048 } };
|
|
11367
|
+
else
|
|
11368
|
+
first = { model, messages: [{ role: "user", content: prompt }], tools: [{ type: "function", function: { name: TOOL, parameters: PARAMETERS } }], tool_choice: { type: "function", function: { name: TOOL } }, max_tokens: 2048 };
|
|
11369
|
+
const data = JSON.parse(await send(first));
|
|
11370
|
+
const call = toolCall(data, protocol);
|
|
11371
|
+
if (call.ticket !== ticket)
|
|
11372
|
+
throw new ProbeFailure("unverified", "wrong_tool_arguments", "The model did not preserve the requested tool argument.");
|
|
11373
|
+
stage = "tool-result";
|
|
11374
|
+
let next;
|
|
11375
|
+
if (protocol === "responses")
|
|
11376
|
+
next = { ...first, input: [...first.input, ...data.output || [], { type: "function_call_output", call_id: call.id, output: proof }], tool_choice: "none", stream: true };
|
|
11377
|
+
else if (protocol === "messages")
|
|
11378
|
+
next = { ...first, messages: [...first.messages, { role: "assistant", content: data.content }, { role: "user", content: [{ type: "tool_result", tool_use_id: call.id, content: proof }] }], tool_choice: { type: "auto" }, stream: true };
|
|
11379
|
+
else if (protocol === "gemini")
|
|
11380
|
+
next = { ...first, contents: [...first.contents, data.candidates[0].content, { role: "user", parts: [{ functionResponse: { name: TOOL, response: { result: proof } } }] }], toolConfig: { functionCallingConfig: { mode: "NONE" } } };
|
|
11381
|
+
else
|
|
11382
|
+
next = { ...first, messages: [...first.messages, data.choices[0].message, { role: "tool", tool_call_id: call.id, content: proof }], tool_choice: "none", stream: true };
|
|
11383
|
+
const final = streamedText(await send(next, true), protocol);
|
|
11384
|
+
if (!final.complete)
|
|
11385
|
+
throw new ProbeFailure("incompatible", "incomplete_stream", "The response stream did not reach its completion event.");
|
|
11386
|
+
if (!final.text.includes(proof))
|
|
11387
|
+
throw new ProbeFailure("unverified", "missing_tool_result", "The model did not return the supplied tool result.");
|
|
11388
|
+
return result("passed");
|
|
11389
|
+
} catch (error) {
|
|
11390
|
+
if (error instanceof ProbeFailure)
|
|
11391
|
+
return result(error.status, { code: error.code, reason: error.message, httpStatus: error.httpStatus });
|
|
11392
|
+
return result("unavailable", { code: "connection_or_response_error", reason: "The model check could not complete. Check the gateway connection and provider availability." });
|
|
11393
|
+
}
|
|
11394
|
+
}
|
|
11395
|
+
|
|
11130
11396
|
// src/commands/start.ts
|
|
11131
11397
|
function isGatewayModelAvailable(availableModels, model) {
|
|
11132
11398
|
return availableModels.includes(model);
|
|
@@ -11138,7 +11404,8 @@ var TOOL_OPTIONS = [
|
|
|
11138
11404
|
{ value: "agy", label: "Antigravity CLI", hint: "Google Gemini API via your ClosedRouter pool" },
|
|
11139
11405
|
{ value: "aider", label: "Aider", hint: "OpenAI-compatible API" },
|
|
11140
11406
|
{ value: "opencode", label: "OpenCode", hint: "OpenAI-compatible API" },
|
|
11141
|
-
{ value: "cursor", label: "Cursor Agent Local", hint: "OpenAI-compatible API; separate preview package required" }
|
|
11407
|
+
{ value: "cursor", label: "Cursor Agent Local", hint: "OpenAI-compatible API; separate preview package required" },
|
|
11408
|
+
{ value: "grok", label: "Grok Build", hint: "Custom provider using your ClosedRouter pool" }
|
|
11142
11409
|
];
|
|
11143
11410
|
var DESKTOP_TOOL_OPTIONS = [
|
|
11144
11411
|
{ value: "codex-desktop", label: "Codex Desktop", hint: "Responses API with an isolated app profile" },
|
|
@@ -11153,7 +11420,7 @@ function resolveStartTarget(value, desktop = false) {
|
|
|
11153
11420
|
const normalized = value.toLowerCase();
|
|
11154
11421
|
desktop ||= normalized.endsWith("-desktop");
|
|
11155
11422
|
const name = normalized.replace(/-desktop$/, "");
|
|
11156
|
-
const tool = name === "antigravity" ? "agy" : name;
|
|
11423
|
+
const tool = name === "antigravity" ? "agy" : name === "grok-build" ? "grok" : name;
|
|
11157
11424
|
if (!TOOL_OPTIONS.some((option) => option.value === tool)) {
|
|
11158
11425
|
throw new Error(`Unsupported tool '${value}'. Choose: ${getStartToolOptions().map((option) => option.value).join(", ")}.`);
|
|
11159
11426
|
}
|
|
@@ -11199,10 +11466,11 @@ function buildLaunchPlan(tool, serverUrl, apiKey, model, toolArgs = [], homeDir
|
|
|
11199
11466
|
}
|
|
11200
11467
|
if (tool === "codex") {
|
|
11201
11468
|
assertNoManagedCodexArgs(toolArgs);
|
|
11202
|
-
const codexHome =
|
|
11469
|
+
const codexHome = join19(homeDir, ".closedrouter", "cli", "codex", `session-${randomUUID6()}`);
|
|
11470
|
+
env.CODEX_HOME = codexHome;
|
|
11203
11471
|
const profileName = "closedrouter-launch";
|
|
11204
|
-
const configPath =
|
|
11205
|
-
const catalogPath =
|
|
11472
|
+
const configPath = join19(codexHome, "config.toml");
|
|
11473
|
+
const catalogPath = join19(codexHome, "closedrouter-models.json");
|
|
11206
11474
|
const configLines = [
|
|
11207
11475
|
`model = ${tomlString(model)}`,
|
|
11208
11476
|
`model_provider = ${tomlString(profileName)}`,
|
|
@@ -11213,25 +11481,59 @@ function buildLaunchPlan(tool, serverUrl, apiKey, model, toolArgs = [], homeDir
|
|
|
11213
11481
|
`base_url = ${tomlString(`${baseUrl}/v1/`)}`,
|
|
11214
11482
|
'env_key = "OPENAI_API_KEY"',
|
|
11215
11483
|
'wire_api = "responses"',
|
|
11484
|
+
"requires_openai_auth = false",
|
|
11485
|
+
"supports_websockets = false",
|
|
11216
11486
|
""
|
|
11217
11487
|
];
|
|
11218
11488
|
env.OPENAI_API_KEY = apiKey;
|
|
11219
11489
|
return {
|
|
11220
11490
|
tool,
|
|
11221
11491
|
command,
|
|
11222
|
-
args: [
|
|
11492
|
+
args: [...toolArgs],
|
|
11223
11493
|
env,
|
|
11224
11494
|
configFile: { path: configPath, contents: configLines.join(`
|
|
11225
11495
|
`) },
|
|
11226
11496
|
...catalog ? { extraFiles: [{ path: catalogPath, contents: JSON.stringify(codexModelCatalog(catalog.models)) }] } : {}
|
|
11227
11497
|
};
|
|
11228
11498
|
}
|
|
11499
|
+
if (tool === "grok") {
|
|
11500
|
+
const home = join19(homeDir, ".closedrouter", "cli", "grok", `session-${randomUUID6()}`);
|
|
11501
|
+
env.GROK_HOME = home;
|
|
11502
|
+
env.CLOSEDROUTER_API_KEY = apiKey;
|
|
11503
|
+
delete env.XAI_API_KEY;
|
|
11504
|
+
const ids = catalog?.models || [model];
|
|
11505
|
+
const config = ["[models]", `default = ${tomlString(model)}`, `web_search = ${tomlString(model)}`, ...ids.flatMap((id) => [
|
|
11506
|
+
"",
|
|
11507
|
+
`[model.${tomlString(id)}]`,
|
|
11508
|
+
`model = ${tomlString(id)}`,
|
|
11509
|
+
`name = ${tomlString(modelLabel(id))}`,
|
|
11510
|
+
'description = "Via your ClosedRouter pool"',
|
|
11511
|
+
`base_url = ${tomlString(`${baseUrl}/v1`)}`,
|
|
11512
|
+
'env_key = "CLOSEDROUTER_API_KEY"',
|
|
11513
|
+
'api_backend = "responses"',
|
|
11514
|
+
"supports_backend_search = false",
|
|
11515
|
+
"context_window = 32768"
|
|
11516
|
+
])].join(`
|
|
11517
|
+
`);
|
|
11518
|
+
const candidates = [join19(homeDir, ".grok", "bin", "grok"), join19(homeDir, ".local", "bin", "grok")];
|
|
11519
|
+
return {
|
|
11520
|
+
tool,
|
|
11521
|
+
command: executablePath || candidates.find((path) => existsSync17(path)) || "grok",
|
|
11522
|
+
args: [...toolArgs],
|
|
11523
|
+
env,
|
|
11524
|
+
configFile: { path: join19(home, "config.toml"), contents: config }
|
|
11525
|
+
};
|
|
11526
|
+
}
|
|
11229
11527
|
if (tool === "gemini") {
|
|
11528
|
+
delete env.GOOGLE_API_KEY;
|
|
11529
|
+
delete env.GOOGLE_GENAI_USE_VERTEXAI;
|
|
11230
11530
|
env.GEMINI_API_KEY = apiKey;
|
|
11231
11531
|
env.GEMINI_MODEL = model;
|
|
11232
11532
|
env.GOOGLE_GEMINI_BASE_URL = baseUrl;
|
|
11233
|
-
const
|
|
11234
|
-
env.
|
|
11533
|
+
const geminiHome = join19(homeDir, ".closedrouter", "cli", "gemini", `session-${randomUUID6()}`);
|
|
11534
|
+
env.GEMINI_CLI_HOME = geminiHome;
|
|
11535
|
+
const configPath = join19(geminiHome, ".gemini", "settings.json");
|
|
11536
|
+
delete env.GEMINI_CLI_SYSTEM_SETTINGS_PATH;
|
|
11235
11537
|
return {
|
|
11236
11538
|
tool,
|
|
11237
11539
|
command,
|
|
@@ -11265,9 +11567,9 @@ function buildLaunchPlan(tool, serverUrl, apiKey, model, toolArgs = [], homeDir
|
|
|
11265
11567
|
return {
|
|
11266
11568
|
tool,
|
|
11267
11569
|
command,
|
|
11268
|
-
args: ["--model", `openai/${catalog ? "closedrouter/" : ""}${model}`, ...catalog ? ["--model-metadata-file",
|
|
11570
|
+
args: ["--model", `openai/${catalog ? "closedrouter/" : ""}${model}`, ...catalog ? ["--model-metadata-file", join19(homeDir, ".closedrouter", "aider-models.json")] : [], ...toolArgs],
|
|
11269
11571
|
env,
|
|
11270
|
-
...catalog ? { configFile: { path:
|
|
11572
|
+
...catalog ? { configFile: { path: join19(homeDir, ".closedrouter", "aider-models.json"), contents: JSON.stringify(Object.fromEntries(catalog.models.map((id) => [`openai/closedrouter/${id}`, { litellm_provider: "openai", mode: "chat", max_input_tokens: 8192, max_output_tokens: 4096, supports_function_calling: true }]))) } } : {}
|
|
11271
11573
|
};
|
|
11272
11574
|
}
|
|
11273
11575
|
const config = {
|
|
@@ -11294,9 +11596,9 @@ function buildLaunchPlan(tool, serverUrl, apiKey, model, toolArgs = [], homeDir
|
|
|
11294
11596
|
};
|
|
11295
11597
|
}
|
|
11296
11598
|
function writeOwnedConfig(file) {
|
|
11297
|
-
|
|
11599
|
+
mkdirSync6(dirname5(file.path), { recursive: true, mode: 448 });
|
|
11298
11600
|
try {
|
|
11299
|
-
const existing =
|
|
11601
|
+
const existing = readFileSync16(file.path, "utf8");
|
|
11300
11602
|
if (file.path.endsWith(".config.toml") && !existing.includes('name = "ClosedRouter"')) {
|
|
11301
11603
|
throw new Error(`Refusing to replace an existing Codex profile at ${file.path}.`);
|
|
11302
11604
|
}
|
|
@@ -11304,7 +11606,7 @@ function writeOwnedConfig(file) {
|
|
|
11304
11606
|
if (error instanceof Error && error.message.startsWith("Refusing to replace"))
|
|
11305
11607
|
throw error;
|
|
11306
11608
|
}
|
|
11307
|
-
|
|
11609
|
+
writeFileSync6(file.path, file.contents, { encoding: "utf8", mode: 384 });
|
|
11308
11610
|
}
|
|
11309
11611
|
function modelSupportsHarness(model, tool) {
|
|
11310
11612
|
const parameters = model.supported_parameters;
|
|
@@ -11333,9 +11635,9 @@ function assertAgyCustomModel(command, label, apiKey) {
|
|
|
11333
11635
|
throw new Error(`This version of agy did not recognize '${label}'. Update agy to use ClosedRouter's accurate model display.`);
|
|
11334
11636
|
}
|
|
11335
11637
|
function assertAgyGeminiMode(homeDir) {
|
|
11336
|
-
const settingsPath =
|
|
11638
|
+
const settingsPath = join19(homeDir, ".gemini", "antigravity-cli", "settings.json");
|
|
11337
11639
|
try {
|
|
11338
|
-
const settings = JSON.parse(
|
|
11640
|
+
const settings = JSON.parse(readFileSync16(settingsPath, "utf8"));
|
|
11339
11641
|
if (settings?.modelProvider === "gemini")
|
|
11340
11642
|
return;
|
|
11341
11643
|
} catch {}
|
|
@@ -11386,6 +11688,19 @@ async function startCommand(options = {}) {
|
|
|
11386
11688
|
if (desktop)
|
|
11387
11689
|
validateDesktopArgs(options.toolArgs || []);
|
|
11388
11690
|
let executablePath = desktop && isDesktopTool(tool) ? resolveDesktopExecutable(tool, options.executablePath) : tool === "cursor" ? resolveCursorLocalExecutable(options.executablePath) : options.executablePath;
|
|
11691
|
+
if (!desktop && tool === "grok") {
|
|
11692
|
+
const candidates = executablePath ? [executablePath] : ["grok", join19(homedir15(), ".grok/bin/grok"), join19(homedir15(), ".local/bin/grok")];
|
|
11693
|
+
executablePath = candidates.find((command) => {
|
|
11694
|
+
try {
|
|
11695
|
+
execFileSync3(command, ["--version"], { timeout: 5000, stdio: "ignore" });
|
|
11696
|
+
return true;
|
|
11697
|
+
} catch {
|
|
11698
|
+
return false;
|
|
11699
|
+
}
|
|
11700
|
+
});
|
|
11701
|
+
if (!executablePath)
|
|
11702
|
+
throw new Error("Grok Build was not found. Install it from https://grok.com/build or pass --path <grok-executable>.");
|
|
11703
|
+
}
|
|
11389
11704
|
const sourceApp = executablePath;
|
|
11390
11705
|
let sharedClaudeElectron;
|
|
11391
11706
|
if (desktop && isDesktopTool(tool) && options.openOnly) {
|
|
@@ -11419,7 +11734,9 @@ async function startCommand(options = {}) {
|
|
|
11419
11734
|
const apiKey = await ensurePlatformKey(tool, client, options);
|
|
11420
11735
|
client.setApiKey(apiKey);
|
|
11421
11736
|
const models = await client.listGatewayModels({ preferApiKey: true });
|
|
11422
|
-
const
|
|
11737
|
+
const protocol = harnessProtocol(tool);
|
|
11738
|
+
const compatibility = loadCompatibility(serverUrl, apiKey);
|
|
11739
|
+
const eligibleModels = models.filter((item) => modelSupportsHarness(item, tool) && recentCompatibility(compatibility, gatewayModelId(item), protocol)?.status !== "incompatible");
|
|
11423
11740
|
const availableModels = [...new Set(eligibleModels.map(gatewayModelId).filter(Boolean))].sort();
|
|
11424
11741
|
let model = options.model?.trim();
|
|
11425
11742
|
if (!model) {
|
|
@@ -11444,10 +11761,20 @@ async function startCommand(options = {}) {
|
|
|
11444
11761
|
if (!isGatewayModelAvailable(availableModels, model)) {
|
|
11445
11762
|
throw new Error(`Model '${model}' is not available for ${tool} with this platform key. Agent clients require a provider that supports gateway function tools. Choose another model from ClosedRouter's model catalog.`);
|
|
11446
11763
|
}
|
|
11764
|
+
if (options.verifyModel !== false) {
|
|
11765
|
+
let checked = recentCompatibility(compatibility, model, protocol);
|
|
11766
|
+
if (checked?.status !== "passed") {
|
|
11767
|
+
M2.info(`Checking tool use and streaming for ${model}.`);
|
|
11768
|
+
checked = await probeCompatibility(serverUrl, apiKey, model, protocol);
|
|
11769
|
+
saveCompatibility(serverUrl, apiKey, [checked]);
|
|
11770
|
+
}
|
|
11771
|
+
if (checked.status !== "passed")
|
|
11772
|
+
throw new Error(`${model} did not pass the ${protocol} compatibility check: ${checked.reason || checked.code}. Run cr verify ${tool} --model ${model} to check again. Provider quota and connection failures are temporary and do not mark the model incompatible.`);
|
|
11773
|
+
}
|
|
11447
11774
|
const catalog = createLaunchCatalog(availableModels, model);
|
|
11448
11775
|
const account = desktopAccountIdentity(config, serverUrl, apiKey);
|
|
11449
11776
|
if (desktop && tool === "cursor") {
|
|
11450
|
-
const localKey =
|
|
11777
|
+
const localKey = randomUUID6();
|
|
11451
11778
|
const relay = await startLaunchProxy(serverUrl, apiKey, "cursor-desktop", localKey, catalog);
|
|
11452
11779
|
try {
|
|
11453
11780
|
const plan = buildDesktopLaunchPlan("cursor", executablePath, createDesktopProfile("cursor"), undefined, undefined, undefined, options.toolArgs);
|
|
@@ -11462,11 +11789,13 @@ async function startCommand(options = {}) {
|
|
|
11462
11789
|
return;
|
|
11463
11790
|
}
|
|
11464
11791
|
if (desktop && (tool === "codex" || tool === "claude" || tool === "agy")) {
|
|
11465
|
-
const localKey =
|
|
11792
|
+
const localKey = randomUUID6();
|
|
11466
11793
|
const relay = await startLaunchProxy(serverUrl, apiKey, `${tool}-desktop`, localKey, catalog);
|
|
11467
11794
|
let agyDesktopProxy;
|
|
11468
11795
|
try {
|
|
11469
11796
|
const plan = buildDesktopLaunchPlan(tool, executablePath, createDesktopProfile(tool), relay.baseUrl, localKey, model, options.toolArgs, process.platform, catalog.models);
|
|
11797
|
+
if (tool === "codex" && executablePath.endsWith(".app"))
|
|
11798
|
+
addCodexDesktopHook(plan, executablePath, catalog.models, account);
|
|
11470
11799
|
if (sharedClaudeElectron && tool === "claude") {
|
|
11471
11800
|
if (sharedClaudeElectron)
|
|
11472
11801
|
useClaudeSharedElectron(plan, sharedClaudeElectron, sourceApp, options.toolArgs, true);
|
|
@@ -11509,6 +11838,45 @@ async function startCommand(options = {}) {
|
|
|
11509
11838
|
}
|
|
11510
11839
|
}
|
|
11511
11840
|
|
|
11841
|
+
// src/commands/verify.ts
|
|
11842
|
+
import { writeFileSync as writeFileSync7 } from "node:fs";
|
|
11843
|
+
async function verifyCommand(tool, options) {
|
|
11844
|
+
const config = loadConfig(), server = options.server || config.serverUrl, key = options.key || config.apiKey;
|
|
11845
|
+
if (!key)
|
|
11846
|
+
throw new Error("Sign in with cr login or supply a platform API key before checking models.");
|
|
11847
|
+
const client = new ClosedRouterClient({ baseUrl: server, apiKey: key });
|
|
11848
|
+
const catalog = await client.listGatewayModels({ preferApiKey: true });
|
|
11849
|
+
const models = catalog.filter((model) => (model.status === undefined || model.status === "available") && (!model.supported_parameters || model.supported_parameters.includes("tools"))).map(gatewayModelId).filter(Boolean);
|
|
11850
|
+
const ids = options.model ? [options.model] : options.allModels ? models : models.slice(0, 1);
|
|
11851
|
+
if (ids.some((id) => !models.includes(id)))
|
|
11852
|
+
throw new Error("Choose an available tool-capable model from the gateway catalog.");
|
|
11853
|
+
const protocols = tool ? [harnessProtocol(tool.replace(/-desktop$/, ""))] : PROTOCOLS;
|
|
11854
|
+
const concurrency = Number(options.concurrency || 2);
|
|
11855
|
+
if (!Number.isInteger(concurrency) || concurrency < 1 || concurrency > 4)
|
|
11856
|
+
throw new Error("Use a concurrency between 1 and 4.");
|
|
11857
|
+
const queue = ids.flatMap((model) => protocols.map((protocol) => ({ model, protocol }))), results = [];
|
|
11858
|
+
let cursor = 0;
|
|
11859
|
+
console.log(`Checking ${queue.length} model/protocol combinations with tool calls, tool results, and streaming.`);
|
|
11860
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
|
|
11861
|
+
while (cursor < queue.length) {
|
|
11862
|
+
const item = queue[cursor++];
|
|
11863
|
+
if (!item)
|
|
11864
|
+
break;
|
|
11865
|
+
const result = await probeCompatibility(server, key, item.model, item.protocol);
|
|
11866
|
+
results.push(result);
|
|
11867
|
+
saveCompatibility(server, key, [result]);
|
|
11868
|
+
console.log(`${results.length}/${queue.length} ${result.model} ${result.protocol}: ${result.status}${result.code ? ` (${result.code}, ${result.stage})` : ""}`);
|
|
11869
|
+
}
|
|
11870
|
+
}));
|
|
11871
|
+
const report = { checkedAt: new Date().toISOString(), scope: "gateway protocol checks; native harness checks are separate", results };
|
|
11872
|
+
if (options.output)
|
|
11873
|
+
writeFileSync7(options.output, JSON.stringify(report, null, 2) + `
|
|
11874
|
+
`, { mode: 384 });
|
|
11875
|
+
console.log(JSON.stringify({ passed: results.filter((r) => r.status === "passed").length, incompatible: results.filter((r) => r.status === "incompatible").length, unavailable: results.filter((r) => r.status === "unavailable").length, unverified: results.filter((r) => r.status === "unverified").length }));
|
|
11876
|
+
if (results.some((r) => r.status !== "passed"))
|
|
11877
|
+
process.exitCode = 1;
|
|
11878
|
+
}
|
|
11879
|
+
|
|
11512
11880
|
// src/commands/whoami.ts
|
|
11513
11881
|
var import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
11514
11882
|
async function whoamiCommand() {
|
|
@@ -11554,7 +11922,7 @@ process.on("warning", (warning) => {
|
|
|
11554
11922
|
console.warn(warning.name, warning.message);
|
|
11555
11923
|
});
|
|
11556
11924
|
var program2 = new Command2;
|
|
11557
|
-
var cliVersion = JSON.parse(
|
|
11925
|
+
var cliVersion = JSON.parse(readFileSync17(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
11558
11926
|
program2.name("closedrouter").description("ClosedRouter CLI: Import local AI subscriptions & API keys into your shared token pool").version(cliVersion).option("-s, --server <url>", "ClosedRouter server base URL (default: http://localhost:8000)").action(async (options) => {
|
|
11559
11927
|
await onboardCommand({ server: options.server });
|
|
11560
11928
|
});
|
|
@@ -11605,7 +11973,8 @@ program2.command("gateway").alias("daemon").alias("bridge").description("Start t
|
|
|
11605
11973
|
program2.command("settings").description("Configure the CLI model selector").option("--model-grouping <grouping>", "Group models by model-provider, account-provider, or none").action(async (options) => {
|
|
11606
11974
|
await settingsCommand({ modelGrouping: options.modelGrouping });
|
|
11607
11975
|
});
|
|
11608
|
-
program2.command("
|
|
11976
|
+
program2.command("verify [tool]").description("Check model tool calls, tool-result continuation, and streaming through the gateway").option("-s, --server <url>", "ClosedRouter server base URL").option("-k, --key <key>", "Platform API key; otherwise use the saved CLI key").option("-m, --model <model>", "Check one model").option("--all-models", "Check every available tool-capable model").option("--concurrency <count>", "Concurrent checks, between 1 and 4", "2").option("--output <path>", "Write a JSON compatibility report").action(async (tool, options) => verifyCommand(tool, { ...options, server: options.server || program2.opts().server }));
|
|
11977
|
+
program2.command("start [tool] [tool-args...]").alias("launch").description("Configure and start a coding CLI or desktop app through ClosedRouter").option("--desktop", "Launch the desktop app for codex, claude, cursor, or antigravity").option("--open-only", "Open an isolated desktop app without gateway configuration").option("--desktop-inspector", "Enable Claude's startup hook using an installed shared Electron runtime").option("--no-verify-model", "Skip the selected-model protocol check for a previously verified private gateway").option("-s, --server <url>", "ClosedRouter server base URL").option("-k, --key <key>", "Platform API key; otherwise use the saved CLI key").option("-m, --model <model>", "Gateway model to use").option("--group-by <grouping>", "Override model grouping for this launch: model-provider, account-provider, or none").option("--path <path>", "Path to the installed CLI, desktop executable, or macOS app bundle").allowUnknownOption().action(async (tool, toolArgs, options) => {
|
|
11609
11978
|
await startCommand({
|
|
11610
11979
|
tool,
|
|
11611
11980
|
toolArgs: toolArgs || [],
|
|
@@ -11616,7 +11985,8 @@ program2.command("start [tool] [tool-args...]").alias("launch").description("Con
|
|
|
11616
11985
|
executablePath: options.path,
|
|
11617
11986
|
desktop: options.desktop,
|
|
11618
11987
|
openOnly: options.openOnly,
|
|
11619
|
-
desktopInspector: options.desktopInspector
|
|
11988
|
+
desktopInspector: options.desktopInspector,
|
|
11989
|
+
verifyModel: options.verifyModel
|
|
11620
11990
|
});
|
|
11621
11991
|
});
|
|
11622
11992
|
program2.parseAsync(process.argv).catch((error) => {
|
package/package.json
CHANGED