@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. 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.
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;AAKhE,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,IAAI,CA2E/I"}
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: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";
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,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"}
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:14px;height:22px;left:-7px;top:-11px;overflow:visible;filter:drop-shadow(0 0 3px #58caff) drop-shadow(0 0 7px #159aff)}
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
- for (let step = 0;step <= 40; step++) {
10299
- const progress = step / 40;
10300
- const angle = (progress + phase) * Math.PI * 2;
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);
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 behindButton = z < -1 && Math.abs(x * scale) < width / 2 && Math.abs(y * scale) < height / 2;
10307
- const fade = Math.min(1, progress * 20, (1 - progress) * 20);
10308
- frames.push({ offset: progress / 5, transform: `translate3d(${x}px,${y}px,${z}px)`, opacity: behindButton ? 0 : fade * (z < 0 ? 0.55 : 1) });
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 { width = 16, height = 16 } = button.querySelector("[data-closedrouter-supercharged]")?.getBoundingClientRect() || {};
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 animation = bolt.animate(orbitKeyframes(width, height, index / 2), { duration: 5000, iterations: Infinity, easing: "linear" });
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 (const position of ["front", "back"]) {
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 30 44");
10386
- bolt.setAttribute("data-cr-lightning-bolt", position);
10387
- const path = "M23 2 11 13 19 15 5 30 13 29 7 42 M11 13 3 11 M19 15 27 23";
10388
- for (const [color, width] of [["#42bdff", "4.2"], ["#f5fcff", "1.7"]]) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@closedrouter/cli",
3
- "version": "1.1.21",
3
+ "version": "1.1.23",
4
4
  "description": "ClosedRouter CLI: Onboard and import local AI subscriptions and API keys into your shared token pool",
5
5
  "author": "ClosedRouter Contributors",
6
6
  "repository": {