@closedrouter/cli 1.1.15 → 1.1.17

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,7 +149,7 @@ 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. 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. 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
 
@@ -0,0 +1,6 @@
1
+ import type { DesktopAccountIdentity } from "./desktop-account";
2
+ /** Provider identity belongs to this isolated launch, not to Cursor's OAuth
3
+ * store. Use the native account area without manufacturing a vendor login. */
4
+ export declare function installCursorProviderAccountUi(account: DesktopAccountIdentity, artwork: string): void;
5
+ export declare function cursorProviderAccountHook(account: DesktopAccountIdentity): string;
6
+ //# sourceMappingURL=cursor-desktop-account.d.ts.map
@@ -0,0 +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,CAuDrG;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CAKjF"}
@@ -1,4 +1,5 @@
1
1
  import type { DesktopLaunchPlan } from "./desktop-launch";
2
2
  import type { LaunchCatalog } from "./launch-catalog";
3
- export declare function addCursorDesktopLocalHook(plan: DesktopLaunchPlan, app: string, catalog?: LaunchCatalog): void;
3
+ import type { DesktopAccountIdentity } from "./desktop-account";
4
+ export declare function addCursorDesktopLocalHook(plan: DesktopLaunchPlan, app: string, catalog?: LaunchCatalog, account?: DesktopAccountIdentity): void;
4
5
  //# sourceMappingURL=cursor-desktop-hook.d.ts.map
@@ -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;AAKtD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CA2E7G"}
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,3 +1,4 @@
1
1
  export declare const CURSOR_APPLICATION_STORAGE = "src.vs.platform.reactivestorage.browser.reactiveStorageServiceImpl.persistentStorage.applicationUser";
2
+ export declare function cursorNativeAccountExists(sourceDb?: string): Promise<boolean>;
2
3
  export declare function configureCursorDesktop(profileDir: string, baseUrl: string, apiKey: string, model: string, sourceDb?: string, models?: string[]): Promise<string>;
3
4
  //# sourceMappingURL=cursor-desktop.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cursor-desktop.d.ts","sourceRoot":"","sources":["../../src/commands/cursor-desktop.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,0BAA0B,yGAAyG,CAAC;AAyCjJ,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAClE,QAAQ,SAAqE,EAC7E,MAAM,GAAE,MAAM,EAAY,GACzB,OAAO,CAAC,MAAM,CAAC,CA2CjB"}
1
+ {"version":3,"file":"cursor-desktop.d.ts","sourceRoot":"","sources":["../../src/commands/cursor-desktop.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,0BAA0B,yGAAyG,CAAC;AAyCjJ,wBAAsB,yBAAyB,CAAC,QAAQ,SAAqE,GAAG,OAAO,CAAC,OAAO,CAAC,CAI/I;AAED,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAClE,QAAQ,SAAqE,EAC7E,MAAM,GAAE,MAAM,EAAY,GACzB,OAAO,CAAC,MAAM,CAAC,CA2CjB"}
@@ -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,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,CAkFjJ;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
@@ -3920,7 +3920,7 @@ var require_websocket = __commonJS(function(exports, module) {
3920
3920
  NOOP
3921
3921
  } = require_constants();
3922
3922
  var {
3923
- EventTarget: { addEventListener, removeEventListener }
3923
+ EventTarget: { addEventListener: addEventListener2, removeEventListener }
3924
3924
  } = require_event_target();
3925
3925
  var { format, parse } = require_extension();
3926
3926
  var { toBuffer } = require_buffer_util();
@@ -4262,7 +4262,7 @@ var require_websocket = __commonJS(function(exports, module) {
4262
4262
  }
4263
4263
  });
4264
4264
  });
4265
- WebSocket.prototype.addEventListener = addEventListener;
4265
+ WebSocket.prototype.addEventListener = addEventListener2;
4266
4266
  WebSocket.prototype.removeEventListener = removeEventListener;
4267
4267
  module.exports = WebSocket;
4268
4268
  function initAsClient(websocket, address, protocols, options) {
@@ -10121,6 +10121,12 @@ function defaultCursorDatabase() {
10121
10121
  const root = process.platform === "darwin" ? join12(homedir11(), "Library", "Application Support") : process.platform === "win32" ? process.env.APPDATA || join12(homedir11(), "AppData", "Roaming") : join12(homedir11(), ".config");
10122
10122
  return join12(root, "Cursor", "User", "globalStorage", "state.vscdb");
10123
10123
  }
10124
+ async function cursorNativeAccountExists(sourceDb = process.env.CLOSEDROUTER_CURSOR_AUTH_DB ?? defaultCursorDatabase()) {
10125
+ if (!existsSync12(sourceDb))
10126
+ return false;
10127
+ const auth = await querySqliteKeyValue(sourceDb, "cursorAuth/");
10128
+ return Boolean(auth.get("cursorAuth/accessToken") && auth.get("cursorAuth/refreshToken"));
10129
+ }
10124
10130
  async function configureCursorDesktop(profileDir, baseUrl, apiKey, model, sourceDb = process.env.CLOSEDROUTER_CURSOR_AUTH_DB ?? defaultCursorDatabase(), models = [model]) {
10125
10131
  const cursorModel = model;
10126
10132
  const catalog = [...new Set([model, ...models])];
@@ -10361,7 +10367,16 @@ function installModelBranding(models, artwork, electricCss, orbitKeyframes) {
10361
10367
  bolt.setAttribute("viewBox", "0 0 30 44");
10362
10368
  bolt.setAttribute("data-cr-lightning-bolt", position);
10363
10369
  const path = "M23 2 11 13 19 15 5 30 13 29 7 42 M11 13 3 11 M19 15 27 23";
10364
- 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"/>`;
10370
+ for (const [color, width] of [["#42bdff", "4.2"], ["#f5fcff", "1.7"]]) {
10371
+ const line = document.createElementNS("http://www.w3.org/2000/svg", "path");
10372
+ line.setAttribute("d", path);
10373
+ line.setAttribute("fill", "none");
10374
+ line.setAttribute("stroke", color);
10375
+ line.setAttribute("stroke-width", width);
10376
+ line.setAttribute("stroke-linecap", "round");
10377
+ line.setAttribute("stroke-linejoin", "round");
10378
+ bolt.append(line);
10379
+ }
10365
10380
  orbit.append(bolt);
10366
10381
  }
10367
10382
  layer.append(orbit);
@@ -10413,7 +10428,8 @@ function installProviderAccountUi(account, artwork) {
10413
10428
  const paint = () => {
10414
10429
  let providerSession = document.documentElement.dataset.closedrouterAccount === "true";
10415
10430
  for (const button of document.querySelectorAll("button")) {
10416
- if (!button.innerText.includes(account.name) || !button.innerText.includes("Gateway"))
10431
+ const text = button.innerText;
10432
+ if (!text.includes(account.name) || !(text.includes("Gateway") || providerSession && account.email && text.includes(account.email)))
10417
10433
  continue;
10418
10434
  providerSession = true;
10419
10435
  document.documentElement.dataset.closedrouterAccount = "true";
@@ -10840,8 +10856,152 @@ if (typeof moduleHooks.registerHooks === 'function') moduleHooks.registerHooks({
10840
10856
  // src/commands/cursor-desktop-hook.ts
10841
10857
  import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
10842
10858
  import { dirname as dirname4, join as join15 } from "node:path";
10859
+
10860
+ // src/commands/cursor-desktop-account.ts
10861
+ function installCursorProviderAccountUi(account, artwork) {
10862
+ if (document.querySelector("[data-closedrouter-cursor-account-hook]"))
10863
+ return;
10864
+ const marker = document.createElement("meta");
10865
+ marker.dataset.closedrouterCursorAccountHook = "true";
10866
+ document.head.append(marker);
10867
+ document.documentElement.dataset.closedrouterAccount = "true";
10868
+ const style = document.createElement("style");
10869
+ style.textContent = "[data-closedrouter-replaced-account]{display:none!important}";
10870
+ document.head.append(style);
10871
+ const image = (size) => {
10872
+ const logo = document.createElement("img");
10873
+ logo.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(artwork)}`;
10874
+ logo.alt = "ClosedRouter";
10875
+ logo.width = size;
10876
+ logo.height = size;
10877
+ logo.style.flex = `0 0 ${size}px`;
10878
+ return logo;
10879
+ };
10880
+ const button = document.createElement("button");
10881
+ button.type = "button";
10882
+ button.dataset.closedrouterCursorAccount = "true";
10883
+ button.setAttribute("aria-label", `ClosedRouter account: ${account.name}`);
10884
+ button.setAttribute("aria-haspopup", "menu");
10885
+ button.setAttribute("aria-expanded", "false");
10886
+ button.style.cssText = "display:flex;align-items:center;gap:8px;width:100%;padding:8px;border:0;border-radius:6px;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer";
10887
+ const mark = image(24);
10888
+ mark.dataset.closedrouterAccountLogo = "true";
10889
+ const label = document.createElement("span");
10890
+ label.style.cssText = "flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
10891
+ label.textContent = account.name;
10892
+ const provider = document.createElement("span");
10893
+ provider.textContent = "ClosedRouter";
10894
+ provider.style.cssText = "font-size:11px;opacity:.7";
10895
+ button.append(mark, label, provider);
10896
+ let menu;
10897
+ const close = (focus = false) => {
10898
+ menu?.remove();
10899
+ menu = undefined;
10900
+ button.setAttribute("aria-expanded", "false");
10901
+ button.removeAttribute("aria-controls");
10902
+ if (focus)
10903
+ button.focus();
10904
+ };
10905
+ const open = () => {
10906
+ if (menu) {
10907
+ close();
10908
+ return;
10909
+ }
10910
+ const bounds = button.getBoundingClientRect();
10911
+ menu = document.createElement("div");
10912
+ menu.id = "closedrouter-cursor-account-menu";
10913
+ menu.setAttribute("role", "menu");
10914
+ menu.setAttribute("aria-label", "ClosedRouter account");
10915
+ menu.style.cssText = `position:fixed;z-index:10000;left:${Math.max(8, Math.min(bounds.left, innerWidth - 300))}px;bottom:${Math.max(8, innerHeight - bounds.top + 6)}px;width:280px;max-width:calc(100vw - 16px);padding:8px;border:1px solid var(--vscode-menu-border,rgba(128,128,128,.25));border-radius:8px;background:var(--vscode-menu-background,var(--vscode-sideBar-background,#202020));color:var(--vscode-menu-foreground,var(--vscode-foreground,inherit));box-shadow:0 6px 20px #0004;font:inherit`;
10916
+ const card = document.createElement("section");
10917
+ card.id = "closedrouter-provider-account";
10918
+ card.setAttribute("aria-label", "ClosedRouter account");
10919
+ card.style.cssText = "display:flex;gap:12px;align-items:center;padding:12px 8px;margin:4px 0;border-bottom:1px solid color-mix(in srgb,currentColor 15%,transparent)";
10920
+ const details = document.createElement("div");
10921
+ details.style.cssText = "min-width:0;overflow-wrap:anywhere";
10922
+ for (const text of [account.name, account.email, "Connected through ClosedRouter"])
10923
+ if (text) {
10924
+ const line = document.createElement("div");
10925
+ line.textContent = text;
10926
+ details.append(line);
10927
+ }
10928
+ card.append(image(28), details);
10929
+ const link = document.createElement("a");
10930
+ link.href = `${account.serverOrigin}/settings`;
10931
+ link.target = "_blank";
10932
+ link.rel = "noopener noreferrer";
10933
+ link.setAttribute("role", "menuitem");
10934
+ link.textContent = "Open ClosedRouter account";
10935
+ link.style.cssText = "display:block;padding:8px;color:inherit;text-decoration:none;border-radius:4px";
10936
+ menu.append(card, link);
10937
+ document.body.append(menu);
10938
+ button.setAttribute("aria-controls", menu.id);
10939
+ button.setAttribute("aria-expanded", "true");
10940
+ link.focus();
10941
+ };
10942
+ button.addEventListener("click", (event) => {
10943
+ event.preventDefault();
10944
+ event.stopPropagation();
10945
+ open();
10946
+ });
10947
+ button.addEventListener("keydown", (event) => {
10948
+ if (event.key === "ArrowDown") {
10949
+ event.preventDefault();
10950
+ event.stopPropagation();
10951
+ if (!menu)
10952
+ open();
10953
+ }
10954
+ });
10955
+ document.addEventListener("pointerdown", (event) => {
10956
+ if (menu && !menu.contains(event.target) && !button.contains(event.target))
10957
+ close();
10958
+ });
10959
+ document.addEventListener("keydown", (event) => {
10960
+ if (menu && event.key === "Escape") {
10961
+ event.preventDefault();
10962
+ event.stopPropagation();
10963
+ close(true);
10964
+ }
10965
+ });
10966
+ addEventListener("resize", () => close());
10967
+ const paint = () => {
10968
+ const upgrade = document.querySelector('[aria-label="Upgrade to a Pro account"]');
10969
+ if (upgrade)
10970
+ upgrade.dataset.closedrouterReplacedAccount = "true";
10971
+ 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
+ if (!target?.parentElement)
10973
+ return;
10974
+ if (button.isConnected && button.nextElementSibling === target)
10975
+ return;
10976
+ close();
10977
+ const compact = target.parentElement.getBoundingClientRect().width < 70;
10978
+ label.style.display = provider.style.display = compact ? "none" : "";
10979
+ button.style.padding = compact ? "4px" : "8px";
10980
+ target.dataset.closedrouterReplacedAccount = "true";
10981
+ target.parentElement.insertBefore(button, target);
10982
+ };
10983
+ let queued = false;
10984
+ new MutationObserver(() => {
10985
+ if (!queued) {
10986
+ queued = true;
10987
+ queueMicrotask(() => {
10988
+ queued = false;
10989
+ paint();
10990
+ });
10991
+ }
10992
+ }).observe(document.documentElement, { subtree: true, childList: true });
10993
+ paint();
10994
+ }
10995
+ function cursorProviderAccountHook(account) {
10996
+ const script = `(${installCursorProviderAccountUi.toString()})(${JSON.stringify(account)},${JSON.stringify(CLOSEDROUTER_LOGO)});`;
10997
+ return `require('electron').app.on('web-contents-created',(_event,contents)=>{
10998
+ contents.on('did-finish-load',()=>contents.executeJavaScript(${JSON.stringify(script)}).catch(()=>{}));
10999
+ });`;
11000
+ }
11001
+
11002
+ // src/commands/cursor-desktop-hook.ts
10843
11003
  var FLAGS = /([\w$]+)=\{extensionIsDev:[^{}]{1,1000}localMode:(?:!1|false)\}/;
10844
- function addCursorDesktopLocalHook(plan, app, catalog) {
11004
+ function addCursorDesktopLocalHook(plan, app, catalog, account) {
10845
11005
  const resources = app.endsWith(".app") ? join15(app, "Contents/Resources/app") : join15(dirname4(app), "resources/app");
10846
11006
  const source = join15(resources, "out/main.js");
10847
11007
  const mainSource = readFileSync13(source, "utf8");
@@ -10920,7 +11080,7 @@ function installProtocolHook() {
10920
11080
  }
10921
11081
  if (app.isReady()) installProtocolHook(); else app.once('ready', installProtocolHook);
10922
11082
  require('node:fs').writeFileSync(${JSON.stringify(join15(plan.profileDir, "hook-loaded"))}, 'ready', {mode: 0o600});
10923
- ` + (catalog ? modelBrandingHook(catalog.models) : "") });
11083
+ ` + (catalog ? modelBrandingHook(catalog.models) : "") + (account ? cursorProviderAccountHook(account) : "") });
10924
11084
  const point = mainSource.slice(0, mainMatch.index + mainMatch[0].length).split(`
10925
11085
  `);
10926
11086
  const logPath = join15(plan.profileDir, "inspector.log");
@@ -11291,9 +11451,9 @@ async function startCommand(options = {}) {
11291
11451
  const relay = await startLaunchProxy(serverUrl, apiKey, "cursor-desktop", localKey, catalog);
11292
11452
  try {
11293
11453
  const plan = buildDesktopLaunchPlan("cursor", executablePath, createDesktopProfile("cursor"), undefined, undefined, undefined, options.toolArgs);
11294
- addCursorDesktopLocalHook(plan, executablePath, catalog);
11454
+ const cursorDb = await configureCursorDesktop(plan.profileDir, relay.baseUrl, localKey, model, undefined, catalog.models);
11455
+ addCursorDesktopLocalHook(plan, executablePath, catalog, await cursorNativeAccountExists(cursorDb) ? undefined : account);
11295
11456
  writeDesktopLaunchPlan(plan);
11296
- await configureCursorDesktop(plan.profileDir, relay.baseUrl, localKey, model, undefined, catalog.models);
11297
11457
  M2.info(`Starting Cursor desktop local provider mode with ${model}. Model requests use ClosedRouter, without a paid Cursor subscription. Profile: ${plan.profileDir}. Keep cr running until you close this app instance.`);
11298
11458
  process.exitCode = await runDesktopLaunch(plan);
11299
11459
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@closedrouter/cli",
3
- "version": "1.1.15",
3
+ "version": "1.1.17",
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": {