@closedrouter/cli 1.1.21 → 1.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -149,13 +149,13 @@ Desktop launches follow the isolation and gateway configuration in `reference_re
|
|
|
149
149
|
|
|
150
150
|
Desktop clients use a streaming loopback relay with a per-launch local credential. The platform key stays in the launcher process. Cursor's local engine uses the relay directly, so the gateway does not need to be publicly reachable from Cursor's servers. Generated files and profile directories start with private permissions. Keep `cr` running and close the launched app instance to end the session. Interrupting `cr` stops local routing; macOS may leave the app open. Start a new session with `cr start` to reconnect.
|
|
151
151
|
|
|
152
|
-
Cursor's startup hook enables local provider mode in memory and serves app-owned scripts for both desktop views. The installed app files and signature are preserved. Its picker lists all tool-capable pool models, and changing the selection changes the routed model. Native model and login controllers are checked before injection; unsupported app builds fail with an update instruction. A valid existing login retains its normal authentication policy checks and account UI. Without a native login, the account area shows the ClosedRouter name and colored logo; its menu shows the email and a link to the ClosedRouter account. Set `CLOSEDROUTER_CURSOR_AUTH_DB` to a different SQLite source, or a nonexistent path to test the no-login case.
|
|
152
|
+
Cursor's startup hook enables local provider mode in memory and serves app-owned scripts for both desktop views. Managed launches skip native onboarding, welcome, and release-note screens and open the normal workspace. The installed app files and signature are preserved. Its picker lists all tool-capable pool models, and changing the selection changes the routed model. Native model and login controllers are checked before injection; unsupported app builds fail with an update instruction. A valid existing login retains its normal authentication policy checks and account UI. Without a native login, the account area shows the ClosedRouter name and colored logo; its menu shows the email and a link to the ClosedRouter account. Set `CLOSEDROUTER_CURSOR_AUTH_DB` to a different SQLite source, or a nonexistent path to test the no-login case.
|
|
153
153
|
|
|
154
154
|
On macOS, Claude uses a compatible Electron runtime installed by Cockpit, or the executable supplied through `CLOSEDROUTER_CLAUDE_ELECTRON`, when one is available. The launcher shares its immutable framework files by hard link and creates private copies of the small executable stubs before signing them with the virtualization entitlement Cowork requires. This added about 860 KB in the tested runtime. The installed Claude app and original Electron runtime are not modified. LaunchServices starts the owned runtime with Claude's original archive and resources. The startup inspector binds to loopback and closes after injection. The runtime and ClosedRouter config directory must be on the same filesystem. Without a compatible runtime, Claude uses its native gateway configuration and text model labels; `--desktop-inspector` requires a compatible runtime.
|
|
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
|
-
Supported desktop views display the colored ClosedRouter logo beside model names.
|
|
158
|
+
Supported desktop views display the colored ClosedRouter logo beside model names. Twelve miniature white-core neon-blue lightning bolts surround the active chat-box logo on four intersecting 3D orbits, with a brief burst 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
159
|
|
|
160
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. Malformed model-generated tool arguments leave a check unverified and can be retried without hiding the model. 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. The [October 3 acceptance report](../docs/acceptance/desktop-harnesses-2026-10-03.json) records the protocol sweep, native checks, and remaining limits.
|
|
161
161
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor-desktop-hook.d.ts","sourceRoot":"","sources":["../../src/commands/cursor-desktop-hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"cursor-desktop-hook.d.ts","sourceRoot":"","sources":["../../src/commands/cursor-desktop-hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAMhE,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAmF/I"}
|
|
@@ -1,9 +1,9 @@
|
|
|
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]{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:
|
|
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;margin-inline-end:9px!important}\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:4.5px;height:8px;left:-2.25px;top:-4px;overflow:visible;filter:drop-shadow(0 0 1.5px #58caff) drop-shadow(0 0 3px #159aff)}\n@media(prefers-reduced-motion:no-preference){[data-closedrouter-electric]{opacity:1}}\n";
|
|
4
4
|
/** Position a bolt along a tilted orbit around the provider logo in world
|
|
5
5
|
* space. The bolt keeps its orientation as its x, y and depth change. */
|
|
6
|
-
export declare function lightningOrbitKeyframes(width: number, height: number, phase: number): Keyframe[];
|
|
6
|
+
export declare function lightningOrbitKeyframes(width: number, height: number, phase: number, plane?: 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. */
|
|
9
9
|
export declare function installModelBranding(models: string[], artwork: string, electricCss: string, orbitKeyframes: typeof lightningOrbitKeyframes): void;
|
|
@@ -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,2vBAQrC,CAAC;AAEF;yEACyE;AACzE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,QAAQ,EAAE,CAwB3G;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,CA+FjJ;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/index.js
CHANGED
|
@@ -10287,25 +10287,30 @@ var CLOSEDROUTER_LOGO = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24
|
|
|
10287
10287
|
var CLOSEDROUTER_ELECTRIC_CSS = `
|
|
10288
10288
|
[data-closedrouter-logo]{position:relative;isolation:isolate}
|
|
10289
10289
|
[data-closedrouter-charged-control]{overflow:visible!important}
|
|
10290
|
-
[data-closedrouter-supercharged]{perspective:80px;transform-style:preserve-3d;isolation:auto;overflow:visible}
|
|
10290
|
+
[data-closedrouter-supercharged]{perspective:80px;transform-style:preserve-3d;isolation:auto;overflow:visible;margin-inline-end:9px!important}
|
|
10291
10291
|
[data-closedrouter-electric]{position:absolute;inset:0;pointer-events:none;transform-style:preserve-3d;opacity:0}
|
|
10292
10292
|
[data-cr-lightning-orbit]{position:absolute;left:50%;top:50%;width:0;height:0;transform-style:preserve-3d}
|
|
10293
|
-
[data-cr-lightning-bolt]{position:absolute;width:
|
|
10293
|
+
[data-cr-lightning-bolt]{position:absolute;width:4.5px;height:8px;left:-2.25px;top:-4px;overflow:visible;filter:drop-shadow(0 0 1.5px #58caff) drop-shadow(0 0 3px #159aff)}
|
|
10294
10294
|
@media(prefers-reduced-motion:no-preference){[data-closedrouter-electric]{opacity:1}}
|
|
10295
10295
|
`;
|
|
10296
|
-
function lightningOrbitKeyframes(width, height, phase) {
|
|
10296
|
+
function lightningOrbitKeyframes(width, height, phase, plane = 0) {
|
|
10297
10297
|
const frames = [];
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
const
|
|
10303
|
-
const
|
|
10304
|
-
const
|
|
10298
|
+
const inclination = [Math.PI / 2.7, Math.PI / 2.2, Math.PI / 3.2, Math.PI / 2.5][plane % 4];
|
|
10299
|
+
const bearing = [-Math.PI / 7, Math.PI / 2.7, -Math.PI / 2.5, Math.PI / 10][plane % 4];
|
|
10300
|
+
const radius = Math.max(width, height) / 2 + 4;
|
|
10301
|
+
for (let step = 0;step <= 64; step++) {
|
|
10302
|
+
const progress = step / 64;
|
|
10303
|
+
const angle = (phase + progress * (plane % 2 ? -1 : 1) * (1 + plane * 0.12)) * Math.PI * 2;
|
|
10304
|
+
const localX = radius * Math.cos(angle);
|
|
10305
|
+
const localY = radius * Math.sin(angle) * Math.cos(inclination);
|
|
10306
|
+
const x = localX * Math.cos(bearing) - localY * Math.sin(bearing);
|
|
10307
|
+
const y = localX * Math.sin(bearing) + localY * Math.cos(bearing);
|
|
10308
|
+
const z = radius * Math.sin(angle) * Math.sin(inclination);
|
|
10305
10309
|
const scale = 80 / (80 - z);
|
|
10306
|
-
const
|
|
10307
|
-
const fade = Math.min(1, progress *
|
|
10308
|
-
|
|
10310
|
+
const behindLogo = z < -1 && Math.abs(x * scale) < width / 2 && Math.abs(y * scale) < height / 2;
|
|
10311
|
+
const fade = Math.min(1, progress * 12, (1 - progress) * 12);
|
|
10312
|
+
const shimmer = 0.8 + 0.2 * Math.sin((progress * 2 + phase) * Math.PI) ** 2;
|
|
10313
|
+
frames.push({ offset: progress * 0.28, transform: `translate3d(${x}px,${y}px,${z}px)`, opacity: behindLogo ? 0 : fade * shimmer * (z < 0 ? 0.38 : 0.95) });
|
|
10309
10314
|
}
|
|
10310
10315
|
frames.push({ ...frames[0], offset: 1, opacity: 0 });
|
|
10311
10316
|
return frames;
|
|
@@ -10325,9 +10330,12 @@ function installModelBranding(models, artwork, electricCss, orbitKeyframes) {
|
|
|
10325
10330
|
const previous = charged.get(button) || [];
|
|
10326
10331
|
const time = previous[0]?.currentTime;
|
|
10327
10332
|
previous.forEach((animation) => animation.cancel());
|
|
10328
|
-
const
|
|
10333
|
+
const mark = button.querySelector("[data-closedrouter-supercharged]");
|
|
10334
|
+
const width = mark?.offsetWidth || 16, height = mark?.offsetHeight || 16;
|
|
10329
10335
|
const animations = motion.matches ? [] : [...button.querySelectorAll("[data-cr-lightning-bolt]")].map((bolt, index) => {
|
|
10330
|
-
const
|
|
10336
|
+
const plane = index % 4;
|
|
10337
|
+
const phase = Math.floor(index / 4) / 3 + plane * 0.07;
|
|
10338
|
+
const animation = bolt.animate(orbitKeyframes(width, height, phase, plane), { duration: 5000, iterations: Infinity, easing: "linear" });
|
|
10331
10339
|
if (typeof time === "number")
|
|
10332
10340
|
animation.currentTime = time;
|
|
10333
10341
|
if (document.hidden)
|
|
@@ -10380,12 +10388,17 @@ function installModelBranding(models, artwork, electricCss, orbitKeyframes) {
|
|
|
10380
10388
|
layer.setAttribute("aria-hidden", "true");
|
|
10381
10389
|
const orbit = document.createElement("span");
|
|
10382
10390
|
orbit.dataset.crLightningOrbit = "true";
|
|
10383
|
-
for (
|
|
10391
|
+
for (let index = 0;index < 12; index++) {
|
|
10384
10392
|
const bolt = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
10385
|
-
bolt.setAttribute("viewBox", "0 0
|
|
10386
|
-
bolt.setAttribute("data-cr-lightning-bolt",
|
|
10387
|
-
const
|
|
10388
|
-
|
|
10393
|
+
bolt.setAttribute("viewBox", "0 0 20 34");
|
|
10394
|
+
bolt.setAttribute("data-cr-lightning-bolt", String(index));
|
|
10395
|
+
const size = [0.82, 1, 0.9][Math.floor(index / 4)];
|
|
10396
|
+
bolt.style.width = `${4.5 * size}px`;
|
|
10397
|
+
bolt.style.height = `${8 * size}px`;
|
|
10398
|
+
bolt.style.left = `${-2.25 * size}px`;
|
|
10399
|
+
bolt.style.top = `${-4 * size}px`;
|
|
10400
|
+
const path = index % 2 ? "M15 2 6 13 13 14 4 31 M6 13 2 10" : "M14 2 5 15 12 16 4 32 M12 16 18 22";
|
|
10401
|
+
for (const [color, width] of [["#42bdff", "3.5"], ["#f5fcff", "1.3"]]) {
|
|
10389
10402
|
const line = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
10390
10403
|
line.setAttribute("d", path);
|
|
10391
10404
|
line.setAttribute("fill", "none");
|
|
@@ -11024,6 +11037,7 @@ function cursorProviderAccountHook(account) {
|
|
|
11024
11037
|
|
|
11025
11038
|
// src/commands/cursor-desktop-hook.ts
|
|
11026
11039
|
var FLAGS = /([\w$]+)=\{extensionIsDev:[^{}]{1,1000}localMode:(?:!1|false)\}/;
|
|
11040
|
+
var PROVIDER_STARTUP_FLAGS = ["--skip-onboarding", "--skip-welcome", "--skip-release-notes"];
|
|
11027
11041
|
function addCursorDesktopLocalHook(plan, app, catalog, account) {
|
|
11028
11042
|
const resources = app.endsWith(".app") ? join15(app, "Contents/Resources/app") : join15(dirname4(app), "resources/app");
|
|
11029
11043
|
const source = join15(resources, "out/main.js");
|
|
@@ -11031,6 +11045,9 @@ function addCursorDesktopLocalHook(plan, app, catalog, account) {
|
|
|
11031
11045
|
const mainMatch = FLAGS.exec(mainSource);
|
|
11032
11046
|
if (!mainMatch)
|
|
11033
11047
|
throw new Error("This Cursor Desktop build does not include the supported local agent runtime. Update Cursor or use cr start cursor with Cursor Agent Local.");
|
|
11048
|
+
if (PROVIDER_STARTUP_FLAGS.some((flag) => !mainSource.includes(JSON.stringify(flag.slice(2))))) {
|
|
11049
|
+
throw new Error("This Cursor Desktop build does not support starting provider sessions without onboarding. Update Cursor before launching through ClosedRouter.");
|
|
11050
|
+
}
|
|
11034
11051
|
const nativeKey = mainMatch[1];
|
|
11035
11052
|
const redirects = {};
|
|
11036
11053
|
for (const variant of ["desktop", "glass"]) {
|
|
@@ -11041,6 +11058,9 @@ function addCursorDesktopLocalHook(plan, app, catalog, account) {
|
|
|
11041
11058
|
const rendererMatch = FLAGS.exec(rendererSource);
|
|
11042
11059
|
if (!rendererMatch || !rendererSource.includes("runLocalAgentInExtensionHost"))
|
|
11043
11060
|
throw new Error("This Cursor Desktop build does not include the supported local agent runtime. Update Cursor or use cr start cursor with Cursor Agent Local.");
|
|
11061
|
+
if (!rendererSource.includes('this.args["skip-onboarding"]') || !rendererSource.includes("this.environmentService.skipOnboarding")) {
|
|
11062
|
+
throw new Error("This Cursor Desktop build has an unsupported onboarding controller. Update the ClosedRouter launcher before using provider mode.");
|
|
11063
|
+
}
|
|
11044
11064
|
const authMethod = /isAuthenticated\(\)\{const ([\w$]+)=this\.accessToken\(\),([\w$]+)=this\.refreshToken\(\);return this\.scheduleTeamPolicyCheck\(\),!!\(\1&&\2\)\}/;
|
|
11045
11065
|
if (!authMethod.test(rendererSource))
|
|
11046
11066
|
throw new Error("This Cursor Desktop build has an unsupported login controller. Update the ClosedRouter launcher before using local provider mode.");
|
|
@@ -11118,7 +11138,7 @@ require('node:fs').writeFileSync(${JSON.stringify(join15(plan.profileDir, "hook-
|
|
|
11118
11138
|
},
|
|
11119
11139
|
readyPath: marker
|
|
11120
11140
|
};
|
|
11121
|
-
plan.args.push("--inspect-brk=127.0.0.1:0");
|
|
11141
|
+
plan.args.push(...PROVIDER_STARTUP_FLAGS, "--inspect-brk=127.0.0.1:0");
|
|
11122
11142
|
if (plan.command === "/usr/bin/open")
|
|
11123
11143
|
plan.args.splice(plan.args.indexOf("--args"), 0, "--stderr", logPath);
|
|
11124
11144
|
}
|
package/package.json
CHANGED