@closedrouter/cli 1.1.14 → 1.1.16

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
@@ -155,7 +155,7 @@ 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
- Cursor, Antigravity, and the hooked Claude view display the colored ClosedRouter logo beside model names. White-core neon-blue lightning bolts orbit the active chat model button in 3D for a brief pass every five seconds, and immediately on hover or keyboard focus. Reduced-motion preferences keep the mark static; hidden windows pause the effect. Codex uses its custom model catalog and provider labels. 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
+ Cursor, Antigravity, and the hooked Claude view display the colored ClosedRouter logo beside model names. White-core neon-blue lightning bolts fly around the active chat model button along a 3D orbit for a brief pass every five seconds, and immediately on hover or keyboard focus. The bolts keep their orientation; perspective enlarges the front pass and the button occludes the rear pass. Reduced-motion preferences keep the mark static; hidden windows pause the effect. Codex uses its custom model catalog and provider labels. 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
  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
161
 
@@ -1 +1 @@
1
- {"version":3,"file":"agy-desktop-account.d.ts","sourceRoot":"","sources":["../../src/commands/agy-desktop-account.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAEhE;8EAC8E;AAC9E,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,MAAM,CAa/F"}
1
+ {"version":3,"file":"agy-desktop-account.d.ts","sourceRoot":"","sources":["../../src/commands/agy-desktop-account.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAEhE;8EAC8E;AAC9E,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,MAAM,CAe/F"}
@@ -1,9 +1,12 @@
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]{position:relative;perspective:420px;transform-style:preserve-3d;overflow:visible!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:30px;height:44px;left:-15px;top:-22px;overflow:visible;filter:drop-shadow(0 0 3px #58caff) drop-shadow(0 0 7px #159aff)}\n[data-cr-lightning-bolt=\"front\"]{transform:translateX(var(--cr-lightning-radius,70px)) rotateY(30deg)}\n[data-cr-lightning-bolt=\"back\"]{transform:rotateY(180deg) translateX(var(--cr-lightning-radius,70px)) rotateZ(24deg)}\n@keyframes cr-electric-depth{0%{opacity:0;transform:rotateX(22deg) rotateY(-150deg)}2%{opacity:1}8%{opacity:1;transform:rotateX(22deg) rotateY(25deg)}16%{opacity:.85;transform:rotateX(22deg) rotateY(210deg)}20%,100%{opacity:0;transform:rotateX(22deg) rotateY(210deg)}}\n@keyframes cr-electric-hover{0%{opacity:.1;transform:rotateX(-18deg) rotateY(-150deg)}2%{opacity:1}8%{opacity:1;transform:rotateX(-18deg) rotateY(25deg)}16%{opacity:.85;transform:rotateX(-18deg) rotateY(210deg)}20%,100%{opacity:0;transform:rotateX(-18deg) rotateY(210deg)}}\n@media(prefers-reduced-motion:no-preference){\n [data-cr-lightning-orbit]{animation:cr-electric-depth 5s linear infinite}\n [data-closedrouter-electric]{opacity:1}\n button:hover>[data-closedrouter-electric] [data-cr-lightning-orbit],button:focus-visible>[data-closedrouter-electric] [data-cr-lightning-orbit]{animation-name:cr-electric-hover}\n}\nhtml[data-closedrouter-page-hidden] [data-cr-lightning-orbit]{animation-play-state:paused}\n";
3
+ export declare const CLOSEDROUTER_ELECTRIC_CSS = "\n[data-closedrouter-logo]{position:relative;isolation:isolate}\n[data-closedrouter-charged-control]{position:relative;perspective:400px;transform-style:preserve-3d;overflow:visible!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:20px;height:30px;left:-10px;top:-15px;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 in world space. Perspective belongs to
5
+ * the button; the bolt keeps its orientation as its x, y and depth change. */
6
+ export declare function lightningOrbitKeyframes(width: number, height: number, phase: number): Keyframe[];
4
7
  /** Add the provider mark to native controls without changing click handlers,
5
8
  * model IDs, keyboard behavior, or the application's typography. */
6
- export declare function installModelBranding(models: string[], artwork: string, electricCss: string): void;
9
+ export declare function installModelBranding(models: string[], artwork: string, electricCss: string, orbitKeyframes: typeof lightningOrbitKeyframes): void;
7
10
  export declare function installProviderAccountUi(account: DesktopAccountIdentity, artwork: string): void;
8
11
  export declare function modelBrandingHook(models: string[], account?: DesktopAccountIdentity): string;
9
12
  //# sourceMappingURL=model-branding.d.ts.map
@@ -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,6wDAgBrC,CAAC;AAEF;oEACoE;AACpE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CA0DjG;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAmC/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,wqBAOrC,CAAC;AAEF;8EAC8E;AAC9E,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,CAsFjJ;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
@@ -10191,7 +10191,7 @@ function patchAntigravityAccount(source, account) {
10191
10191
  if (!onboarding.test(source))
10192
10192
  throw new Error("This Antigravity build has an unsupported startup onboarding controller.");
10193
10193
  source = source.replace(onboarding, "agentOnboardingState:2");
10194
- return source;
10194
+ return "document.documentElement.dataset.closedrouterAccount='true';" + source;
10195
10195
  }
10196
10196
 
10197
10197
  // src/commands/agy-desktop-catalog.ts
@@ -10264,22 +10264,29 @@ app.on('web-contents-created', (_event, contents) => {
10264
10264
  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>';
10265
10265
  var CLOSEDROUTER_ELECTRIC_CSS = `
10266
10266
  [data-closedrouter-logo]{position:relative;isolation:isolate}
10267
- [data-closedrouter-charged-control]{position:relative;perspective:420px;transform-style:preserve-3d;overflow:visible!important}
10267
+ [data-closedrouter-charged-control]{position:relative;perspective:400px;transform-style:preserve-3d;overflow:visible!important}
10268
10268
  [data-closedrouter-electric]{position:absolute;inset:0;pointer-events:none;transform-style:preserve-3d;opacity:0}
10269
10269
  [data-cr-lightning-orbit]{position:absolute;left:50%;top:50%;width:0;height:0;transform-style:preserve-3d}
10270
- [data-cr-lightning-bolt]{position:absolute;width:30px;height:44px;left:-15px;top:-22px;overflow:visible;filter:drop-shadow(0 0 3px #58caff) drop-shadow(0 0 7px #159aff)}
10271
- [data-cr-lightning-bolt="front"]{transform:translateX(var(--cr-lightning-radius,70px)) rotateY(30deg)}
10272
- [data-cr-lightning-bolt="back"]{transform:rotateY(180deg) translateX(var(--cr-lightning-radius,70px)) rotateZ(24deg)}
10273
- @keyframes cr-electric-depth{0%{opacity:0;transform:rotateX(22deg) rotateY(-150deg)}2%{opacity:1}8%{opacity:1;transform:rotateX(22deg) rotateY(25deg)}16%{opacity:.85;transform:rotateX(22deg) rotateY(210deg)}20%,100%{opacity:0;transform:rotateX(22deg) rotateY(210deg)}}
10274
- @keyframes cr-electric-hover{0%{opacity:.1;transform:rotateX(-18deg) rotateY(-150deg)}2%{opacity:1}8%{opacity:1;transform:rotateX(-18deg) rotateY(25deg)}16%{opacity:.85;transform:rotateX(-18deg) rotateY(210deg)}20%,100%{opacity:0;transform:rotateX(-18deg) rotateY(210deg)}}
10275
- @media(prefers-reduced-motion:no-preference){
10276
- [data-cr-lightning-orbit]{animation:cr-electric-depth 5s linear infinite}
10277
- [data-closedrouter-electric]{opacity:1}
10278
- button:hover>[data-closedrouter-electric] [data-cr-lightning-orbit],button:focus-visible>[data-closedrouter-electric] [data-cr-lightning-orbit]{animation-name:cr-electric-hover}
10279
- }
10280
- html[data-closedrouter-page-hidden] [data-cr-lightning-orbit]{animation-play-state:paused}
10270
+ [data-cr-lightning-bolt]{position:absolute;width:20px;height:30px;left:-10px;top:-15px;overflow:visible;filter:drop-shadow(0 0 3px #58caff) drop-shadow(0 0 7px #159aff)}
10271
+ @media(prefers-reduced-motion:no-preference){[data-closedrouter-electric]{opacity:1}}
10281
10272
  `;
10282
- function installModelBranding(models, artwork, electricCss) {
10273
+ function lightningOrbitKeyframes(width, height, phase) {
10274
+ const frames = [];
10275
+ for (let step = 0;step <= 40; step++) {
10276
+ const progress = step / 40;
10277
+ const angle = (progress + phase) * Math.PI * 2;
10278
+ const x = (width / 2 + 12) * Math.cos(angle);
10279
+ const y = (height / 2 + 6) * Math.sin(angle) * Math.sin(Math.PI / 15);
10280
+ const z = 46 * Math.sin(angle) * Math.cos(Math.PI / 15);
10281
+ const scale = 400 / (400 - z);
10282
+ const behindButton = z < -1 && Math.abs(x * scale) < width / 2 && Math.abs(y * scale) < height / 2;
10283
+ const fade = Math.min(1, progress * 20, (1 - progress) * 20);
10284
+ frames.push({ offset: progress / 5, transform: `translate3d(${x}px,${y}px,${z}px)`, opacity: behindButton ? 0 : fade * (z < 0 ? 0.55 : 1) });
10285
+ }
10286
+ frames.push({ ...frames[0], offset: 1, opacity: 0 });
10287
+ return frames;
10288
+ }
10289
+ function installModelBranding(models, artwork, electricCss, orbitKeyframes) {
10283
10290
  if (document.querySelector("[data-closedrouter-branding]"))
10284
10291
  return;
10285
10292
  const marker = document.createElement("meta");
@@ -10288,10 +10295,36 @@ function installModelBranding(models, artwork, electricCss) {
10288
10295
  const style = document.createElement("style");
10289
10296
  style.textContent = electricCss;
10290
10297
  document.head.append(style);
10298
+ const motion = matchMedia("(prefers-reduced-motion: reduce)");
10299
+ const charged = new Map;
10300
+ const animate = (button) => {
10301
+ const previous = charged.get(button) || [];
10302
+ const time = previous[0]?.currentTime;
10303
+ previous.forEach((animation) => animation.cancel());
10304
+ const { width, height } = button.getBoundingClientRect();
10305
+ const animations = motion.matches ? [] : [...button.querySelectorAll("[data-cr-lightning-bolt]")].map((bolt, index) => {
10306
+ const animation = bolt.animate(orbitKeyframes(width, height, index / 2), { duration: 5000, iterations: Infinity, easing: "linear" });
10307
+ if (typeof time === "number")
10308
+ animation.currentTime = time;
10309
+ if (document.hidden)
10310
+ animation.pause();
10311
+ return animation;
10312
+ });
10313
+ charged.set(button, animations);
10314
+ };
10315
+ const resize = new ResizeObserver((entries) => entries.forEach((entry) => animate(entry.target)));
10316
+ motion.addEventListener("change", () => charged.forEach((_animations, button) => animate(button)));
10291
10317
  document.addEventListener("visibilitychange", () => {
10292
10318
  document.documentElement.toggleAttribute("data-closedrouter-page-hidden", document.hidden);
10319
+ charged.forEach((animations) => animations.forEach((animation) => document.hidden ? animation.pause() : animation.play()));
10293
10320
  });
10294
10321
  const paint = () => {
10322
+ for (const [button, animations] of charged)
10323
+ if (!button.isConnected) {
10324
+ animations.forEach((animation) => animation.cancel());
10325
+ resize.unobserve(button);
10326
+ charged.delete(button);
10327
+ }
10295
10328
  for (const element of document.querySelectorAll('button,[role="button"],[role="option"],[role="menuitem"],[role="menuitemradio"],[data-testid="model-selector-item"]')) {
10296
10329
  const text = element.innerText.trim().split(`
10297
10330
  `)[0];
@@ -10318,7 +10351,6 @@ function installModelBranding(models, artwork, electricCss) {
10318
10351
  if (element.matches("button") && !element.closest('[role="menu"],[role="listbox"],[role="dialog"]') && document.querySelector('[contenteditable="true"],textarea')) {
10319
10352
  logo.dataset.closedrouterSupercharged = "true";
10320
10353
  element.dataset.closedrouterChargedControl = "true";
10321
- element.style.setProperty("--cr-lightning-radius", `${Math.max(35, element.getBoundingClientRect().width / 2 + 10)}px`);
10322
10354
  const layer = document.createElement("span");
10323
10355
  layer.dataset.closedrouterElectric = "true";
10324
10356
  layer.setAttribute("aria-hidden", "true");
@@ -10329,11 +10361,26 @@ function installModelBranding(models, artwork, electricCss) {
10329
10361
  bolt.setAttribute("viewBox", "0 0 30 44");
10330
10362
  bolt.setAttribute("data-cr-lightning-bolt", position);
10331
10363
  const path = "M23 2 11 13 19 15 5 30 13 29 7 42 M11 13 3 11 M19 15 27 23";
10332
- bolt.innerHTML = `<path d="${path}" fill="none" stroke="#42bdff" stroke-width="4.2" stroke-linecap="round" stroke-linejoin="round"/><path d="${path}" fill="none" stroke="#f5fcff" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/>`;
10364
+ for (const [color, width] of [["#42bdff", "4.2"], ["#f5fcff", "1.7"]]) {
10365
+ const line = document.createElementNS("http://www.w3.org/2000/svg", "path");
10366
+ line.setAttribute("d", path);
10367
+ line.setAttribute("fill", "none");
10368
+ line.setAttribute("stroke", color);
10369
+ line.setAttribute("stroke-width", width);
10370
+ line.setAttribute("stroke-linecap", "round");
10371
+ line.setAttribute("stroke-linejoin", "round");
10372
+ bolt.append(line);
10373
+ }
10333
10374
  orbit.append(bolt);
10334
10375
  }
10335
10376
  layer.append(orbit);
10336
10377
  element.append(layer);
10378
+ const restart = () => charged.get(element)?.forEach((animation) => {
10379
+ animation.currentTime = 0;
10380
+ });
10381
+ element.addEventListener("mouseenter", restart);
10382
+ element.addEventListener("focus", restart);
10383
+ resize.observe(element);
10337
10384
  }
10338
10385
  element.prepend(logo);
10339
10386
  }
@@ -10375,7 +10422,8 @@ function installProviderAccountUi(account, artwork) {
10375
10422
  const paint = () => {
10376
10423
  let providerSession = document.documentElement.dataset.closedrouterAccount === "true";
10377
10424
  for (const button of document.querySelectorAll("button")) {
10378
- if (!button.innerText.includes(account.name) || !button.innerText.includes("Gateway"))
10425
+ const text = button.innerText;
10426
+ if (!text.includes(account.name) || !(text.includes("Gateway") || providerSession && account.email && text.includes(account.email)))
10379
10427
  continue;
10380
10428
  providerSession = true;
10381
10429
  document.documentElement.dataset.closedrouterAccount = "true";
@@ -10418,7 +10466,7 @@ function installProviderAccountUi(account, artwork) {
10418
10466
  paint();
10419
10467
  }
10420
10468
  function modelBrandingHook(models, account) {
10421
- const script = `(${installModelBranding.toString()})(${JSON.stringify(models)},${JSON.stringify(CLOSEDROUTER_LOGO)},${JSON.stringify(CLOSEDROUTER_ELECTRIC_CSS)});` + (account ? `(${installProviderAccountUi.toString()})(${JSON.stringify(account)},${JSON.stringify(CLOSEDROUTER_LOGO)})` : "");
10469
+ const script = `(${installModelBranding.toString()})(${JSON.stringify(models)},${JSON.stringify(CLOSEDROUTER_LOGO)},${JSON.stringify(CLOSEDROUTER_ELECTRIC_CSS)},${lightningOrbitKeyframes.toString()});` + (account ? `(${installProviderAccountUi.toString()})(${JSON.stringify(account)},${JSON.stringify(CLOSEDROUTER_LOGO)})` : "");
10422
10470
  return `
10423
10471
  require('electron').app.on('web-contents-created', (_event, contents) => {
10424
10472
  contents.on('did-finish-load', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@closedrouter/cli",
3
- "version": "1.1.14",
3
+ "version": "1.1.16",
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": {