cursor-openai-byok 1.1.3 → 1.1.4

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/lib/cli.js CHANGED
@@ -144,7 +144,7 @@ async function doctor(options) {
144
144
 
145
145
  try {
146
146
  const st = patcher.status(options);
147
- checks.push({ name: "cursor", ok: st.supportedVersion, ...st });
147
+ checks.push({ name: "cursor", ok: st.supportedVersion && st.patched, ...st });
148
148
  } catch (err) {
149
149
  checks.push({ name: "cursor", ok: false, error: err.message });
150
150
  }
@@ -266,7 +266,7 @@ module.exports = {
266
266
  "package.json": function(module, exports, require, __filename, __dirname) {
267
267
  module.exports = {
268
268
  "name": "cursor-openai-byok",
269
- "version": "1.1.3",
269
+ "version": "1.1.4",
270
270
  "description": "Local BYOK bridge for Cursor using OpenAI-compatible providers.",
271
271
  "displayName": "Cursor OpenAI BYOK",
272
272
  "publisher": "cursor-openai-byok",
@@ -1028,8 +1028,7 @@ function candidateCursorAppRoots() {
1028
1028
  function findCursorAppRoot(explicitRoot) {
1029
1029
  const roots = explicitRoot ? [explicitRoot] : candidateCursorAppRoots();
1030
1030
  for (const root of roots) {
1031
- const workbench = workbenchPath(root);
1032
- if (fs.existsSync(workbench)) return root;
1031
+ if (workbenchPaths(root).length > 0) return root;
1033
1032
  }
1034
1033
  throw new Error(`Cursor app root not found. Checked: ${roots.join(", ")}`);
1035
1034
  }
@@ -1038,6 +1037,13 @@ function workbenchPath(appRoot) {
1038
1037
  return path.join(appRoot, "out", "vs", "workbench", "workbench.desktop.main.js");
1039
1038
  }
1040
1039
 
1040
+ function workbenchPaths(appRoot) {
1041
+ const dir = path.join(appRoot, "out", "vs", "workbench");
1042
+ return ["workbench.desktop.main.js", "workbench.glass.main.js"]
1043
+ .map((name) => path.join(dir, name))
1044
+ .filter((file) => fs.existsSync(file));
1045
+ }
1046
+
1041
1047
  function productJsonPath(appRoot) {
1042
1048
  return path.join(appRoot, "product.json");
1043
1049
  }
@@ -1056,6 +1062,7 @@ function readCursorInfo(appRoot) {
1056
1062
  version: product.version || pkg.version || "unknown",
1057
1063
  commit: product.commit || "unknown",
1058
1064
  workbenchPath: workbenchPath(appRoot),
1065
+ workbenchPaths: workbenchPaths(appRoot),
1059
1066
  };
1060
1067
  }
1061
1068
 
@@ -1063,6 +1070,7 @@ module.exports = {
1063
1070
  candidateCursorAppRoots,
1064
1071
  findCursorAppRoot,
1065
1072
  workbenchPath,
1073
+ workbenchPaths,
1066
1074
  readCursorInfo,
1067
1075
  };
1068
1076
 
@@ -1831,16 +1839,29 @@ const MODEL_ENRICH_NEEDLE = `s=XI_(s,a.models);`;
1831
1839
  function status(options = {}) {
1832
1840
  const appRoot = findCursorAppRoot(options.appRoot);
1833
1841
  const info = readCursorInfo(appRoot);
1834
- const source = fs.readFileSync(info.workbenchPath, "utf8");
1835
- const unary = findUnaryPatchPoint(source);
1842
+ const bundles = info.workbenchPaths.map((workbenchPath) => bundleStatus(workbenchPath));
1836
1843
  return {
1837
1844
  ...info,
1838
1845
  supportedVersion: isSupportedVersion(info.version),
1846
+ patched: bundles.every((bundle) => bundle.patched),
1847
+ legacyPatched: bundles.some((bundle) => bundle.legacyPatched),
1848
+ hasRunLoopNeedle: bundles.every((bundle) => bundle.hasRunLoopNeedle),
1849
+ hasConnectNeedle: bundles.every((bundle) => bundle.hasConnectNeedle),
1850
+ hasUnaryNeedle: bundles.every((bundle) => bundle.hasUnaryNeedle),
1851
+ hasModelEnrichNeedle: bundles.some((bundle) => bundle.hasModelEnrichNeedle),
1852
+ bundles,
1853
+ };
1854
+ }
1855
+
1856
+ function bundleStatus(workbenchPath) {
1857
+ const source = fs.readFileSync(workbenchPath, "utf8");
1858
+ return {
1859
+ workbenchPath,
1839
1860
  patched: source.includes(MARKER),
1840
1861
  legacyPatched: source.includes(LEGACY_MARKER),
1841
1862
  hasRunLoopNeedle: !!findRunLoopPatchPoint(source),
1842
1863
  hasConnectNeedle: !!findConnectHelperNeedle(source),
1843
- hasUnaryNeedle: !!unary || source.includes(`await ${MARKER}().patchAvailableModels(`),
1864
+ hasUnaryNeedle: !!findUnaryPatchPoint(source) || source.includes(`await ${MARKER}().patchAvailableModels(`),
1844
1865
  hasModelEnrichNeedle: source.includes(MODEL_ENRICH_NEEDLE),
1845
1866
  };
1846
1867
  }
@@ -1851,41 +1872,58 @@ function installPatch(options = {}) {
1851
1872
  if (!isSupportedVersion(info.version) && !options.force) {
1852
1873
  throw new Error(`Unsupported Cursor version ${info.version}. Re-run with --force only after validating needles.`);
1853
1874
  }
1854
- let source = fs.readFileSync(info.workbenchPath, "utf8");
1855
- if (source.includes(MARKER)) return { ...info, changed: false, alreadyInstalled: true };
1856
- if (source.includes(LEGACY_MARKER) && !options.force) {
1857
- throw new Error("Legacy local BYOK patch is already present. Restore Cursor from backup or use --force after manual review.");
1875
+ const changes = [];
1876
+ for (const workbenchPath of info.workbenchPaths) {
1877
+ let source = fs.readFileSync(workbenchPath, "utf8");
1878
+ if (source.includes(MARKER)) continue;
1879
+ if (source.includes(LEGACY_MARKER) && !options.force) {
1880
+ throw new Error(`Legacy local BYOK patch is already present in ${workbenchPath}. Restore Cursor from backup or use --force after manual review.`);
1881
+ }
1882
+ source = patchSource(source, workbenchPath);
1883
+ changes.push({ workbenchPath, source });
1858
1884
  }
1885
+ if (changes.length === 0) return { ...info, changed: false, alreadyInstalled: true };
1886
+
1887
+ const backupPaths = changes.map(({ workbenchPath }) => backupWorkbench(workbenchPath));
1888
+ changes.forEach(({ workbenchPath, source }) => fs.writeFileSync(workbenchPath, source));
1889
+ return { ...info, changed: true, backupPath: backupPaths[0], backupPaths };
1890
+ }
1891
+
1892
+ function patchSource(source, workbenchPath) {
1859
1893
  const connectNeedle = findConnectHelperNeedle(source);
1860
1894
  const unary = findUnaryPatchPoint(source);
1861
1895
  const runLoop = findRunLoopPatchPoint(source);
1862
- if (!runLoop) throw new Error("AgentCompatService.runAgentLoop needle not found");
1863
- if (!connectNeedle) throw new Error("Connect helper needle not found");
1864
- if (!unary) throw new Error("Unary transport needle not found");
1896
+ if (!runLoop) throw new Error(`AgentCompatService.runAgentLoop needle not found in ${workbenchPath}`);
1897
+ if (!connectNeedle) throw new Error(`Connect helper needle not found in ${workbenchPath}`);
1898
+ if (!unary) throw new Error(`Unary transport needle not found in ${workbenchPath}`);
1865
1899
 
1866
- const backupPath = backupWorkbench(info.workbenchPath);
1867
1900
  source = source.replace(connectNeedle, helperSnippet() + connectNeedle);
1868
1901
  source = source.replace(unary.needle, `${unary.call};try{if(${unary.service}?.typeName==="aiserver.v1.AiService"&&${unary.method}?.name==="AvailableModels")await ${MARKER}().patchAvailableModels(${unary.response}.message,${unary.request})}catch{}${unary.returnStatement}`);
1869
1902
  if (source.includes(MODEL_ENRICH_NEEDLE)) {
1870
1903
  source = source.replace(MODEL_ENRICH_NEEDLE, `s=XI_(s,a.models);s=await ${MARKER}().enrichModels(s);`);
1871
1904
  }
1872
1905
  source = source.replace(runLoop.needle, runLoop.needle + agentLoopSnippet(runLoop.vars));
1873
- fs.writeFileSync(info.workbenchPath, source);
1874
- return { ...info, changed: true, backupPath };
1906
+ return source;
1875
1907
  }
1876
1908
 
1877
1909
  function uninstallPatch(options = {}) {
1878
1910
  const appRoot = findCursorAppRoot(options.appRoot);
1879
1911
  const info = readCursorInfo(appRoot);
1880
- const backups = findBackups(info.workbenchPath).filter((file) => {
1881
- const data = fs.readFileSync(file, "utf8");
1882
- return !data.includes(MARKER);
1912
+ const restores = info.workbenchPaths.map((workbenchPath) => {
1913
+ const backups = findBackups(workbenchPath).filter((file) => !fs.readFileSync(file, "utf8").includes(MARKER));
1914
+ if (backups.length === 0) throw new Error(`No suitable cursor-openai-byok backup found for ${workbenchPath}`);
1915
+ return { workbenchPath, restoredFrom: backups[backups.length - 1] };
1883
1916
  });
1884
- if (backups.length === 0) throw new Error("No suitable cursor-openai-byok backup found");
1885
- const latest = backups[backups.length - 1];
1886
- const currentBackup = backupWorkbench(info.workbenchPath, "pre-uninstall");
1887
- fs.copyFileSync(latest, info.workbenchPath);
1888
- return { ...info, restoredFrom: latest, currentBackup };
1917
+ restores.forEach((restore) => {
1918
+ restore.currentBackup = backupWorkbench(restore.workbenchPath, "pre-uninstall");
1919
+ fs.copyFileSync(restore.restoredFrom, restore.workbenchPath);
1920
+ });
1921
+ return {
1922
+ ...info,
1923
+ restoredFrom: restores[0].restoredFrom,
1924
+ currentBackup: restores[0].currentBackup,
1925
+ restores,
1926
+ };
1889
1927
  }
1890
1928
 
1891
1929
  function backupWorkbench(workbenchPath, label = "backup") {
@@ -1899,7 +1937,7 @@ function findBackups(workbenchPath) {
1899
1937
  const dir = path.dirname(workbenchPath);
1900
1938
  const base = path.basename(workbenchPath);
1901
1939
  return fs.readdirSync(dir)
1902
- .filter((name) => name.startsWith(`${base}.backup-cursor-openai-byok-`) || name.includes("cursor-openai-byok"))
1940
+ .filter((name) => name.startsWith(`${base}.`) && name.includes("cursor-openai-byok"))
1903
1941
  .map((name) => path.join(dir, name))
1904
1942
  .sort();
1905
1943
  }
@@ -1964,9 +2002,10 @@ function findRunLoopPatchPoint(source) {
1964
2002
  const start = Math.max(0, source.lastIndexOf("async runAgentLoop", anchor));
1965
2003
  const segment = source.slice(start, anchor + 500);
1966
2004
  const args = segment.match(/const\{userText:(\w+),richText:(\w+),mode:(\w+),selectedContext:(\w+),requestContext:(\w+),modelDetails:(\w+),generationUUID:(\w+),conversationId:(\w+),messageId:(\w+),[\s\S]{0,900}?conversationState:(\w+),/);
1967
- const context = segment.match(/const\s+\w+=\w+,\[(\w+),\w+\]=\w+\.withCancel\(\)/);
1968
- const adapter = segment.match(/\w+=\w+\(\w+\.withName\("AgentCompatService\.agentClientService\.run"\)\),(\w+)=new\s+\w+\(this\.instantiationService,[^;]+?\);/);
1969
- if (!args || !context || !adapter) return null;
2005
+ const context = segment.match(/const\s+[\w$]+=[\w$]+,\[([\w$]+),[\w$]+\]=[\w$]+\.withCancel\(\)/);
2006
+ const adapter = segment.match(/[\w$]+=[\w$]+\([\w$]+\.withName\("AgentCompatService\.agentClientService\.run"\)\),([\w$]+)=new\s+[\w$]+\(this\.instantiationService,[^;]+?\);/);
2007
+ const functionArgs = segment.match(/async runAgentLoop\([\w$]+,([\w$]+)\)/);
2008
+ if (!args || !context || !adapter || !functionArgs) return null;
1970
2009
  const eventFactory = findInteractionUpdateFactory(source);
1971
2010
  if (!eventFactory) return null;
1972
2011
  const needleStart = segment.indexOf(args[0]);
@@ -1986,7 +2025,7 @@ function findRunLoopPatchPoint(source) {
1986
2025
  conversationState: args[10],
1987
2026
  contextExpression: context[1],
1988
2027
  adapter: adapter[1],
1989
- event: "t",
2028
+ event: functionArgs[1],
1990
2029
  eventFactory,
1991
2030
  },
1992
2031
  };
@@ -488,7 +488,7 @@ module.exports = {
488
488
  "package.json": function(module, exports, require, __filename, __dirname) {
489
489
  module.exports = {
490
490
  "name": "cursor-openai-byok",
491
- "version": "1.1.3",
491
+ "version": "1.1.4",
492
492
  "description": "Local BYOK bridge for Cursor using OpenAI-compatible providers.",
493
493
  "displayName": "Cursor OpenAI BYOK",
494
494
  "publisher": "cursor-openai-byok",
@@ -115,7 +115,7 @@ module.exports = {
115
115
  "package.json": function(module, exports, require, __filename, __dirname) {
116
116
  module.exports = {
117
117
  "name": "cursor-openai-byok",
118
- "version": "1.1.3",
118
+ "version": "1.1.4",
119
119
  "description": "Local BYOK bridge for Cursor using OpenAI-compatible providers.",
120
120
  "displayName": "Cursor OpenAI BYOK",
121
121
  "publisher": "cursor-openai-byok",
@@ -510,8 +510,7 @@ function candidateCursorAppRoots() {
510
510
  function findCursorAppRoot(explicitRoot) {
511
511
  const roots = explicitRoot ? [explicitRoot] : candidateCursorAppRoots();
512
512
  for (const root of roots) {
513
- const workbench = workbenchPath(root);
514
- if (fs.existsSync(workbench)) return root;
513
+ if (workbenchPaths(root).length > 0) return root;
515
514
  }
516
515
  throw new Error(`Cursor app root not found. Checked: ${roots.join(", ")}`);
517
516
  }
@@ -520,6 +519,13 @@ function workbenchPath(appRoot) {
520
519
  return path.join(appRoot, "out", "vs", "workbench", "workbench.desktop.main.js");
521
520
  }
522
521
 
522
+ function workbenchPaths(appRoot) {
523
+ const dir = path.join(appRoot, "out", "vs", "workbench");
524
+ return ["workbench.desktop.main.js", "workbench.glass.main.js"]
525
+ .map((name) => path.join(dir, name))
526
+ .filter((file) => fs.existsSync(file));
527
+ }
528
+
523
529
  function productJsonPath(appRoot) {
524
530
  return path.join(appRoot, "product.json");
525
531
  }
@@ -538,6 +544,7 @@ function readCursorInfo(appRoot) {
538
544
  version: product.version || pkg.version || "unknown",
539
545
  commit: product.commit || "unknown",
540
546
  workbenchPath: workbenchPath(appRoot),
547
+ workbenchPaths: workbenchPaths(appRoot),
541
548
  };
542
549
  }
543
550
 
@@ -545,6 +552,7 @@ module.exports = {
545
552
  candidateCursorAppRoots,
546
553
  findCursorAppRoot,
547
554
  workbenchPath,
555
+ workbenchPaths,
548
556
  readCursorInfo,
549
557
  };
550
558
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursor-openai-byok",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Local BYOK bridge for Cursor using OpenAI-compatible providers.",
5
5
  "displayName": "Cursor OpenAI BYOK",
6
6
  "publisher": "cursor-openai-byok",