cursor-openai-byok 1.0.2 → 1.1.3
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 +5 -46
- package/TROUBLESHOOTING.md +7 -3
- package/lib/cli.js +299 -115
- package/lib/daemon-entry.js +30 -11
- package/lib/watcher-entry.js +1540 -0
- package/package.json +5 -6
package/lib/cli.js
CHANGED
|
@@ -109,16 +109,18 @@ function importCcursorConfig() {
|
|
|
109
109
|
function install(options) {
|
|
110
110
|
writeDefaultConfig();
|
|
111
111
|
const patch = patcher.installPatch(options);
|
|
112
|
-
const extension = service.installExtension();
|
|
112
|
+
const extension = service.installExtension(options);
|
|
113
|
+
const lifecycle = service.installLifecycle(options);
|
|
113
114
|
const storage = syncCursorStorage(listModels(loadConfig()));
|
|
114
|
-
print({ config: configPath(), patch, extension, storage });
|
|
115
|
+
print({ config: configPath(), patch, extension, lifecycle, storage });
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
function uninstall(options) {
|
|
118
119
|
const stopped = service.stopBackground();
|
|
119
|
-
const
|
|
120
|
+
const lifecycle = service.uninstallLifecycle();
|
|
121
|
+
const extension = service.uninstallExtension(options);
|
|
120
122
|
const patch = patcher.uninstallPatch(options);
|
|
121
|
-
print({ stopped, extension, patch });
|
|
123
|
+
print({ stopped, lifecycle, extension, patch });
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
async function doctor(options) {
|
|
@@ -147,14 +149,17 @@ async function doctor(options) {
|
|
|
147
149
|
checks.push({ name: "cursor", ok: false, error: err.message });
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
|
|
152
|
+
const extension = service.extensionStatus(options);
|
|
153
|
+
checks.push({ name: "extension", ok: extension.installed, ...extension });
|
|
154
|
+
const lifecycle = service.lifecycleStatus(options);
|
|
155
|
+
checks.push({ name: "lifecycle", ok: lifecycle.installed, ...lifecycle });
|
|
151
156
|
const runningPid = service.isRunning();
|
|
152
157
|
checks.push({
|
|
153
158
|
name: "daemon-process",
|
|
154
159
|
ok: true,
|
|
155
160
|
running: !!runningPid,
|
|
156
161
|
pid: runningPid || undefined,
|
|
157
|
-
note: runningPid ? "server is running" : "server is expected to run only while Cursor
|
|
162
|
+
note: runningPid ? "server is running" : "server is expected to run only while Cursor lifecycle is active",
|
|
158
163
|
});
|
|
159
164
|
|
|
160
165
|
try {
|
|
@@ -170,7 +175,7 @@ async function doctor(options) {
|
|
|
170
175
|
ok: !runningPid,
|
|
171
176
|
running: false,
|
|
172
177
|
error: err.message,
|
|
173
|
-
note: runningPid ? "server pid exists but health check failed" : "health is skipped when Cursor is
|
|
178
|
+
note: runningPid ? "server pid exists but health check failed" : "health is skipped when Cursor lifecycle is inactive",
|
|
174
179
|
});
|
|
175
180
|
}
|
|
176
181
|
|
|
@@ -214,6 +219,10 @@ function pidPath() {
|
|
|
214
219
|
return path.join(configDir(), "daemon.pid");
|
|
215
220
|
}
|
|
216
221
|
|
|
222
|
+
function watcherPidPath() {
|
|
223
|
+
return path.join(configDir(), "watcher.pid");
|
|
224
|
+
}
|
|
225
|
+
|
|
217
226
|
function runtimePath(name) {
|
|
218
227
|
return path.join(configDir(), name);
|
|
219
228
|
}
|
|
@@ -245,6 +254,7 @@ module.exports = {
|
|
|
245
254
|
configPath,
|
|
246
255
|
logPath,
|
|
247
256
|
pidPath,
|
|
257
|
+
watcherPidPath,
|
|
248
258
|
runtimePath,
|
|
249
259
|
platformName,
|
|
250
260
|
cursorExtensionsDir,
|
|
@@ -256,16 +266,15 @@ module.exports = {
|
|
|
256
266
|
"package.json": function(module, exports, require, __filename, __dirname) {
|
|
257
267
|
module.exports = {
|
|
258
268
|
"name": "cursor-openai-byok",
|
|
259
|
-
"version": "1.
|
|
269
|
+
"version": "1.1.3",
|
|
260
270
|
"description": "Local BYOK bridge for Cursor using OpenAI-compatible providers.",
|
|
261
271
|
"displayName": "Cursor OpenAI BYOK",
|
|
262
272
|
"publisher": "cursor-openai-byok",
|
|
263
273
|
"type": "commonjs",
|
|
274
|
+
"extensionKind": [
|
|
275
|
+
"ui"
|
|
276
|
+
],
|
|
264
277
|
"main": "./lib/extension.js",
|
|
265
|
-
"repository": {
|
|
266
|
-
"type": "git",
|
|
267
|
-
"url": "git+https://git.3weijia.com/base/ai/cursor-openai-byok.git"
|
|
268
|
-
},
|
|
269
278
|
"keywords": [
|
|
270
279
|
"cursor",
|
|
271
280
|
"byok",
|
|
@@ -289,7 +298,7 @@ module.exports = {
|
|
|
289
298
|
"registry": "https://registry.npmjs.org/"
|
|
290
299
|
},
|
|
291
300
|
"activationEvents": [
|
|
292
|
-
"
|
|
301
|
+
"onStartupFinished"
|
|
293
302
|
],
|
|
294
303
|
"contributes": {
|
|
295
304
|
"commands": [
|
|
@@ -458,12 +467,17 @@ function listModels(config) {
|
|
|
458
467
|
return models;
|
|
459
468
|
}
|
|
460
469
|
|
|
461
|
-
function resolveModel(config, requestedModel) {
|
|
470
|
+
function resolveModel(config, requestedModel, options = {}) {
|
|
462
471
|
const models = listModels(config);
|
|
463
472
|
const fallback = config.defaults && config.defaults.model;
|
|
464
473
|
const target = requestedModel || fallback || (models[0] && models[0].displayName);
|
|
465
474
|
const found = models.find((m) => m.displayName === target || m.apiModel === target);
|
|
466
475
|
if (!found) {
|
|
476
|
+
if (options.allowUnconfigured && typeof target === "string" && target) {
|
|
477
|
+
const base = models.find((m) => m.displayName === fallback || m.apiModel === fallback) || models[0];
|
|
478
|
+
const provider = base && config.providers.find((p) => p.id === base.providerId);
|
|
479
|
+
if (provider) return { provider, model: { ...base, displayName: target, apiModel: target } };
|
|
480
|
+
}
|
|
467
481
|
throw new Error(`Model ${target || "<empty>"} is not configured`);
|
|
468
482
|
}
|
|
469
483
|
const provider = config.providers.find((p) => p.id === found.providerId);
|
|
@@ -560,7 +574,7 @@ function toCursorModel(model) {
|
|
|
560
574
|
vendorName: providerName,
|
|
561
575
|
vendor: { id: 9000, displayName: providerName },
|
|
562
576
|
tagline: "OpenAI-compatible BYOK",
|
|
563
|
-
visibleInRoutedModelView:
|
|
577
|
+
visibleInRoutedModelView: false,
|
|
564
578
|
isUserAdded: true,
|
|
565
579
|
byokModel: true,
|
|
566
580
|
};
|
|
@@ -698,10 +712,13 @@ module.exports = { log, tailLog, redactText };
|
|
|
698
712
|
"use strict";
|
|
699
713
|
|
|
700
714
|
const fs = require("fs");
|
|
715
|
+
const os = require("os");
|
|
701
716
|
const path = require("path");
|
|
702
717
|
const childProcess = require("child_process");
|
|
718
|
+
const { findCursorAppRoot, readCursorInfo } = require("../patcher/cursor-paths");
|
|
703
719
|
const {
|
|
704
720
|
pidPath,
|
|
721
|
+
watcherPidPath,
|
|
705
722
|
logPath,
|
|
706
723
|
installedExtensionDir,
|
|
707
724
|
cursorExtensionsDir,
|
|
@@ -759,7 +776,7 @@ function stopBackground() {
|
|
|
759
776
|
return { stopped: true, pid };
|
|
760
777
|
}
|
|
761
778
|
|
|
762
|
-
function installExtension() {
|
|
779
|
+
function installExtension(options = {}) {
|
|
763
780
|
const sourceRoot = path.resolve(__dirname, "..", "..");
|
|
764
781
|
const target = installedExtensionDir();
|
|
765
782
|
ensureDir(cursorExtensionsDir());
|
|
@@ -773,7 +790,7 @@ function installExtension() {
|
|
|
773
790
|
return { installed: true, extensionDir: target };
|
|
774
791
|
}
|
|
775
792
|
|
|
776
|
-
function uninstallExtension() {
|
|
793
|
+
function uninstallExtension(options = {}) {
|
|
777
794
|
const target = installedExtensionDir();
|
|
778
795
|
removeInstalledExtensionVersions();
|
|
779
796
|
removeFromCursorExtensionIndex();
|
|
@@ -781,9 +798,97 @@ function uninstallExtension() {
|
|
|
781
798
|
return { uninstalled: true, extensionDir: target };
|
|
782
799
|
}
|
|
783
800
|
|
|
784
|
-
function extensionStatus() {
|
|
801
|
+
function extensionStatus(options = {}) {
|
|
785
802
|
const target = installedExtensionDir();
|
|
786
|
-
return { installed: fs.existsSync(path.join(target, "extension", "extension.js")), extensionDir: target };
|
|
803
|
+
return { installed: fs.existsSync(path.join(target, "lib", "extension.js")) || fs.existsSync(path.join(target, "extension", "extension.js")), extensionDir: target };
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
function installLifecycle(options = {}) {
|
|
807
|
+
const appRoot = findCursorAppRoot(options.appRoot);
|
|
808
|
+
if (!/^3\.11\./.test(readCursorInfo(appRoot).version)) return { installed: false, required: false };
|
|
809
|
+
if (process.platform === "darwin") return installLaunchAgent();
|
|
810
|
+
if (process.platform === "win32") return installWindowsWatcher();
|
|
811
|
+
return { installed: false, required: true, error: `Unsupported platform ${process.platform}` };
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
function uninstallLifecycle() {
|
|
815
|
+
if (process.platform === "darwin") {
|
|
816
|
+
const plist = launchAgentPath();
|
|
817
|
+
childProcess.spawnSync("launchctl", ["bootout", `gui/${process.getuid()}`, plist], { encoding: "utf8" });
|
|
818
|
+
fs.rmSync(plist, { force: true });
|
|
819
|
+
} else if (process.platform === "win32") {
|
|
820
|
+
childProcess.spawnSync("reg.exe", ["delete", "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", "/v", "CursorOpenAIByok", "/f"], { encoding: "utf8" });
|
|
821
|
+
}
|
|
822
|
+
stopWatcherProcess();
|
|
823
|
+
return { uninstalled: true };
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function lifecycleStatus(options = {}) {
|
|
827
|
+
try {
|
|
828
|
+
const appRoot = findCursorAppRoot(options.appRoot);
|
|
829
|
+
if (!/^3\.11\./.test(readCursorInfo(appRoot).version)) return { installed: true, required: false, mode: "extension" };
|
|
830
|
+
} catch {}
|
|
831
|
+
if (process.platform === "darwin") return { installed: fs.existsSync(launchAgentPath()), required: true, mode: "launch-agent" };
|
|
832
|
+
if (process.platform === "win32") return { installed: !!watcherProcess(), required: true, mode: "startup-watcher" };
|
|
833
|
+
return { installed: false, required: true, mode: "unsupported" };
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
function installLaunchAgent() {
|
|
837
|
+
ensureConfigDir();
|
|
838
|
+
const plist = launchAgentPath();
|
|
839
|
+
const watcher = path.join(installedExtensionDir(), "lib", "watcher-entry.js");
|
|
840
|
+
fs.mkdirSync(path.dirname(plist), { recursive: true });
|
|
841
|
+
fs.writeFileSync(plist, launchAgentPlist(watcher));
|
|
842
|
+
childProcess.spawnSync("launchctl", ["bootout", `gui/${process.getuid()}`, plist], { encoding: "utf8" });
|
|
843
|
+
const result = childProcess.spawnSync("launchctl", ["bootstrap", `gui/${process.getuid()}`, plist], { encoding: "utf8" });
|
|
844
|
+
if (result.status !== 0) throw new Error(result.stderr || result.stdout || "Failed to install lifecycle LaunchAgent");
|
|
845
|
+
return { installed: true, required: true, mode: "launch-agent", file: plist };
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
function installWindowsWatcher() {
|
|
849
|
+
const watcher = path.join(installedExtensionDir(), "lib", "watcher-entry.js");
|
|
850
|
+
const command = `\"${nodeBin()}\" \"${watcher}\"`;
|
|
851
|
+
const result = childProcess.spawnSync("reg.exe", ["add", "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", "/v", "CursorOpenAIByok", "/t", "REG_SZ", "/d", command, "/f"], { encoding: "utf8" });
|
|
852
|
+
if (result.status !== 0) throw new Error(result.stderr || result.stdout || "Failed to install lifecycle startup entry");
|
|
853
|
+
childProcess.spawn(nodeBin(), [watcher], { detached: true, stdio: "ignore" }).unref();
|
|
854
|
+
return { installed: true, required: true, mode: "startup-watcher" };
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
function launchAgentPath() {
|
|
858
|
+
return path.join(os.homedir(), "Library", "LaunchAgents", "cursor-openai-byok.lifecycle.plist");
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
function launchAgentPlist(watcher) {
|
|
862
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
863
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
864
|
+
<plist version="1.0"><dict>
|
|
865
|
+
<key>Label</key><string>cursor-openai-byok.lifecycle</string>
|
|
866
|
+
<key>ProgramArguments</key><array><string>${escapeXml(nodeBin())}</string><string>${escapeXml(watcher)}</string></array>
|
|
867
|
+
<key>RunAtLoad</key><true/><key>KeepAlive</key><true/>
|
|
868
|
+
<key>StandardOutPath</key><string>${escapeXml(logPath())}</string>
|
|
869
|
+
<key>StandardErrorPath</key><string>${escapeXml(logPath())}</string>
|
|
870
|
+
</dict></plist>
|
|
871
|
+
`;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
function escapeXml(value) {
|
|
875
|
+
return String(value).replace(/[&<>"']/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[ch]);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
function watcherProcess() {
|
|
879
|
+
try {
|
|
880
|
+
const pid = Number(fs.readFileSync(watcherPidPath(), "utf8"));
|
|
881
|
+
process.kill(pid, 0);
|
|
882
|
+
return pid;
|
|
883
|
+
} catch {
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function stopWatcherProcess() {
|
|
889
|
+
const pid = watcherProcess();
|
|
890
|
+
if (pid) try { process.kill(pid, "SIGTERM"); } catch {}
|
|
891
|
+
try { fs.unlinkSync(watcherPidPath()); } catch {}
|
|
787
892
|
}
|
|
788
893
|
|
|
789
894
|
function copyTree(src, dst, options = {}) {
|
|
@@ -830,6 +935,16 @@ function writeCursorExtensionIndex(target) {
|
|
|
830
935
|
scheme: "file",
|
|
831
936
|
},
|
|
832
937
|
relativeLocation: path.basename(target),
|
|
938
|
+
metadata: {
|
|
939
|
+
isApplicationScoped: true,
|
|
940
|
+
installedTimestamp: Date.now(),
|
|
941
|
+
source: "vsix",
|
|
942
|
+
targetPlatform: "undefined",
|
|
943
|
+
updated: false,
|
|
944
|
+
private: false,
|
|
945
|
+
isPreReleaseVersion: false,
|
|
946
|
+
hasPreReleaseVersion: false,
|
|
947
|
+
},
|
|
833
948
|
});
|
|
834
949
|
fs.writeFileSync(file, JSON.stringify(entries, null, 2));
|
|
835
950
|
}
|
|
@@ -879,6 +994,76 @@ module.exports = {
|
|
|
879
994
|
installExtension,
|
|
880
995
|
uninstallExtension,
|
|
881
996
|
extensionStatus,
|
|
997
|
+
installLifecycle,
|
|
998
|
+
uninstallLifecycle,
|
|
999
|
+
lifecycleStatus,
|
|
1000
|
+
watcherProcess,
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
},
|
|
1004
|
+
"src/patcher/cursor-paths.js": function(module, exports, require, __filename, __dirname) {
|
|
1005
|
+
"use strict";
|
|
1006
|
+
|
|
1007
|
+
const fs = require("fs");
|
|
1008
|
+
const path = require("path");
|
|
1009
|
+
const os = require("os");
|
|
1010
|
+
|
|
1011
|
+
function candidateCursorAppRoots() {
|
|
1012
|
+
if (process.platform === "darwin") {
|
|
1013
|
+
return [
|
|
1014
|
+
"/Applications/Cursor.app/Contents/Resources/app",
|
|
1015
|
+
path.join(os.homedir(), "Applications/Cursor.app/Contents/Resources/app"),
|
|
1016
|
+
];
|
|
1017
|
+
}
|
|
1018
|
+
if (process.platform === "win32") {
|
|
1019
|
+
const local = process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local");
|
|
1020
|
+
return [
|
|
1021
|
+
path.join(local, "Programs", "Cursor", "resources", "app"),
|
|
1022
|
+
path.join(local, "Cursor", "resources", "app"),
|
|
1023
|
+
];
|
|
1024
|
+
}
|
|
1025
|
+
return [];
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
function findCursorAppRoot(explicitRoot) {
|
|
1029
|
+
const roots = explicitRoot ? [explicitRoot] : candidateCursorAppRoots();
|
|
1030
|
+
for (const root of roots) {
|
|
1031
|
+
const workbench = workbenchPath(root);
|
|
1032
|
+
if (fs.existsSync(workbench)) return root;
|
|
1033
|
+
}
|
|
1034
|
+
throw new Error(`Cursor app root not found. Checked: ${roots.join(", ")}`);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
function workbenchPath(appRoot) {
|
|
1038
|
+
return path.join(appRoot, "out", "vs", "workbench", "workbench.desktop.main.js");
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
function productJsonPath(appRoot) {
|
|
1042
|
+
return path.join(appRoot, "product.json");
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
function packageJsonPath(appRoot) {
|
|
1046
|
+
return path.join(appRoot, "package.json");
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
function readCursorInfo(appRoot) {
|
|
1050
|
+
const productPath = productJsonPath(appRoot);
|
|
1051
|
+
const packagePath = packageJsonPath(appRoot);
|
|
1052
|
+
const product = fs.existsSync(productPath) ? JSON.parse(fs.readFileSync(productPath, "utf8")) : {};
|
|
1053
|
+
const pkg = fs.existsSync(packagePath) ? JSON.parse(fs.readFileSync(packagePath, "utf8")) : {};
|
|
1054
|
+
return {
|
|
1055
|
+
appRoot,
|
|
1056
|
+
version: product.version || pkg.version || "unknown",
|
|
1057
|
+
commit: product.commit || "unknown",
|
|
1058
|
+
workbenchPath: workbenchPath(appRoot),
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
module.exports = {
|
|
1063
|
+
candidateCursorAppRoots,
|
|
1064
|
+
findCursorAppRoot,
|
|
1065
|
+
workbenchPath,
|
|
1066
|
+
readCursorInfo,
|
|
882
1067
|
};
|
|
883
1068
|
|
|
884
1069
|
},
|
|
@@ -983,9 +1168,9 @@ async function sendNdjson(res, iterator) {
|
|
|
983
1168
|
res.end();
|
|
984
1169
|
}
|
|
985
1170
|
|
|
986
|
-
async function* runCursorAgentLoop(config, payload) {
|
|
1171
|
+
async function* runCursorAgentLoop(config, payload, customModels = new Set()) {
|
|
987
1172
|
const requestedModel = payload.model || payload.modelName || payload?.runRequest?.requestedModel?.modelId;
|
|
988
|
-
const route = resolveModel(config, requestedModel);
|
|
1173
|
+
const route = resolveModel(config, requestedModel, { allowUnconfigured: customModels.has(requestedModel) });
|
|
989
1174
|
const openaiBody = cursorPayloadToOpenAI(payload, route);
|
|
990
1175
|
log("cursor-run", {
|
|
991
1176
|
model: route.model.displayName,
|
|
@@ -1026,6 +1211,7 @@ async function* runCursorAgentLoop(config, payload) {
|
|
|
1026
1211
|
}
|
|
1027
1212
|
|
|
1028
1213
|
function createServer() {
|
|
1214
|
+
const customModels = new Set();
|
|
1029
1215
|
return http.createServer(async (req, res) => {
|
|
1030
1216
|
try {
|
|
1031
1217
|
if (req.method === "OPTIONS") return sendJson(res, 204, {});
|
|
@@ -1047,16 +1233,25 @@ function createServer() {
|
|
|
1047
1233
|
if (req.method === "GET" && url.pathname === "/cursor/v1/should-handle") {
|
|
1048
1234
|
const model = url.searchParams.get("model");
|
|
1049
1235
|
try {
|
|
1050
|
-
const route = resolveModel(config, model);
|
|
1236
|
+
const route = resolveModel(config, model, { allowUnconfigured: customModels.has(model) });
|
|
1051
1237
|
return sendJson(res, 200, { ok: true, handle: true, model: route.model });
|
|
1052
1238
|
} catch {
|
|
1053
1239
|
return sendJson(res, 200, { ok: true, handle: false });
|
|
1054
1240
|
}
|
|
1055
1241
|
}
|
|
1056
1242
|
|
|
1243
|
+
if (req.method === "POST" && url.pathname === "/cursor/v1/custom-models") {
|
|
1244
|
+
const payload = await readJson(req);
|
|
1245
|
+
customModels.clear();
|
|
1246
|
+
for (const model of payload.models || []) {
|
|
1247
|
+
if (typeof model === "string" && model.length <= 128 && !/[\x00-\x1f\x7f]/.test(model)) customModels.add(model);
|
|
1248
|
+
}
|
|
1249
|
+
return sendJson(res, 200, { ok: true, models: [...customModels] });
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1057
1252
|
if (req.method === "POST" && url.pathname === "/cursor/v1/run-agent-loop") {
|
|
1058
1253
|
const payload = await readJson(req);
|
|
1059
|
-
return sendNdjson(res, runCursorAgentLoop(config, payload));
|
|
1254
|
+
return sendNdjson(res, runCursorAgentLoop(config, payload, customModels));
|
|
1060
1255
|
}
|
|
1061
1256
|
|
|
1062
1257
|
if (req.method === "POST" && url.pathname === "/cursor/v1/tool-result") {
|
|
@@ -1631,13 +1826,13 @@ const { MARKER, LEGACY_MARKER, helperSnippet, agentLoopSnippet } = require("./te
|
|
|
1631
1826
|
|
|
1632
1827
|
const RUN_LOOP_NEEDLE = `_e=SC(n.withName("AgentCompatService.agentClientService.run")),oe=new Q$t(this.instantiationService,z,re,f,ue);`;
|
|
1633
1828
|
const CONNECT_HELPER_NEEDLE = `function E5_(n,e){return vp1(n,t=>{switch(t.kind){case rn.Unary:return wp1(e,n,t);case rn.ServerStreaming:return Sp1(e,n,t);case rn.ClientStreaming:return kp1(e,n,t);case rn.BiDiStreaming:return Cp1(e,n,t);default:return null}})}`;
|
|
1634
|
-
const UNARY_NEEDLE = `const s=await n.unary(e,t,r?.signal,r?.timeoutMs,r?.headers,i,r?.contextValues);return r?.onHeader?.(s.header),r?.onTrailer?.(s.trailer),s.message`;
|
|
1635
1829
|
const MODEL_ENRICH_NEEDLE = `s=XI_(s,a.models);`;
|
|
1636
1830
|
|
|
1637
1831
|
function status(options = {}) {
|
|
1638
1832
|
const appRoot = findCursorAppRoot(options.appRoot);
|
|
1639
1833
|
const info = readCursorInfo(appRoot);
|
|
1640
1834
|
const source = fs.readFileSync(info.workbenchPath, "utf8");
|
|
1835
|
+
const unary = findUnaryPatchPoint(source);
|
|
1641
1836
|
return {
|
|
1642
1837
|
...info,
|
|
1643
1838
|
supportedVersion: isSupportedVersion(info.version),
|
|
@@ -1645,6 +1840,7 @@ function status(options = {}) {
|
|
|
1645
1840
|
legacyPatched: source.includes(LEGACY_MARKER),
|
|
1646
1841
|
hasRunLoopNeedle: !!findRunLoopPatchPoint(source),
|
|
1647
1842
|
hasConnectNeedle: !!findConnectHelperNeedle(source),
|
|
1843
|
+
hasUnaryNeedle: !!unary || source.includes(`await ${MARKER}().patchAvailableModels(`),
|
|
1648
1844
|
hasModelEnrichNeedle: source.includes(MODEL_ENRICH_NEEDLE),
|
|
1649
1845
|
};
|
|
1650
1846
|
}
|
|
@@ -1661,19 +1857,18 @@ function installPatch(options = {}) {
|
|
|
1661
1857
|
throw new Error("Legacy local BYOK patch is already present. Restore Cursor from backup or use --force after manual review.");
|
|
1662
1858
|
}
|
|
1663
1859
|
const connectNeedle = findConnectHelperNeedle(source);
|
|
1860
|
+
const unary = findUnaryPatchPoint(source);
|
|
1664
1861
|
const runLoop = findRunLoopPatchPoint(source);
|
|
1665
1862
|
if (!runLoop) throw new Error("AgentCompatService.runAgentLoop needle not found");
|
|
1666
1863
|
if (!connectNeedle) throw new Error("Connect helper needle not found");
|
|
1667
|
-
if (!
|
|
1668
|
-
if (!source.includes(MODEL_ENRICH_NEEDLE)) throw new Error("Model enrichment needle not found");
|
|
1864
|
+
if (!unary) throw new Error("Unary transport needle not found");
|
|
1669
1865
|
|
|
1670
1866
|
const backupPath = backupWorkbench(info.workbenchPath);
|
|
1671
1867
|
source = source.replace(connectNeedle, helperSnippet() + connectNeedle);
|
|
1672
|
-
source = source.replace(
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
source = source.replace(MODEL_ENRICH_NEEDLE, `s=XI_(s,a.models);s=await ${MARKER}().enrichModels(s);`);
|
|
1868
|
+
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
|
+
if (source.includes(MODEL_ENRICH_NEEDLE)) {
|
|
1870
|
+
source = source.replace(MODEL_ENRICH_NEEDLE, `s=XI_(s,a.models);s=await ${MARKER}().enrichModels(s);`);
|
|
1871
|
+
}
|
|
1677
1872
|
source = source.replace(runLoop.needle, runLoop.needle + agentLoopSnippet(runLoop.vars));
|
|
1678
1873
|
fs.writeFileSync(info.workbenchPath, source);
|
|
1679
1874
|
return { ...info, changed: true, backupPath };
|
|
@@ -1710,15 +1905,31 @@ function findBackups(workbenchPath) {
|
|
|
1710
1905
|
}
|
|
1711
1906
|
|
|
1712
1907
|
function isSupportedVersion(version) {
|
|
1713
|
-
return /^3\.8\./.test(version);
|
|
1908
|
+
return /^(?:3\.8|3\.11)\./.test(version);
|
|
1714
1909
|
}
|
|
1715
1910
|
|
|
1716
1911
|
function findConnectHelperNeedle(source) {
|
|
1717
1912
|
if (source.includes(CONNECT_HELPER_NEEDLE)) return CONNECT_HELPER_NEEDLE;
|
|
1718
|
-
const match = source.match(/function\s+\w+\(
|
|
1913
|
+
const match = source.match(/function\s+\w+\((\w+),(\w+)\)\{return\s+\w+\(\1,(\w+)=>\{switch\(\3\.kind\)\{case\s+\w+\.Unary:return\s+\w+\(\2,\1,\3\);case\s+\w+\.ServerStreaming:return\s+\w+\(\2,\1,\3\);case\s+\w+\.ClientStreaming:return\s+\w+\(\2,\1,\3\);case\s+\w+\.BiDiStreaming:return\s+\w+\(\2,\1,\3\);default:return null\}\}\)\}/);
|
|
1719
1914
|
return match ? match[0] : null;
|
|
1720
1915
|
}
|
|
1721
1916
|
|
|
1917
|
+
function findUnaryPatchPoint(source) {
|
|
1918
|
+
const re = /const\s+(\w+)=await\s+\w+\.unary\((\w+),(\w+),(\w+)\?\.signal,\4\?\.timeoutMs,\4\?\.headers,(\w+),\4\?\.contextValues\);return\s+\4\?\.onHeader\?\.\(\1\.header\),\4\?\.onTrailer\?\.\(\1\.trailer\),\1\.message/;
|
|
1919
|
+
const match = source.match(re);
|
|
1920
|
+
if (!match) return null;
|
|
1921
|
+
const split = match[0].indexOf(";return");
|
|
1922
|
+
return {
|
|
1923
|
+
needle: match[0],
|
|
1924
|
+
call: match[0].slice(0, split),
|
|
1925
|
+
returnStatement: match[0].slice(split + 1),
|
|
1926
|
+
response: match[1],
|
|
1927
|
+
service: match[2],
|
|
1928
|
+
method: match[3],
|
|
1929
|
+
request: match[5],
|
|
1930
|
+
};
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1722
1933
|
function findRunLoopPatchPoint(source) {
|
|
1723
1934
|
if (source.includes(RUN_LOOP_NEEDLE)) {
|
|
1724
1935
|
return {
|
|
@@ -1728,94 +1939,65 @@ function findRunLoopPatchPoint(source) {
|
|
|
1728
1939
|
}
|
|
1729
1940
|
const re = /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+),[\s\S]{0,1600}?(\w+)=_b\(n\.withName\("AgentCompatService\.agentClientService\.run"\)\),(\w+)=new\s+\w+\(this\.instantiationService,[^;]+?\);/;
|
|
1730
1941
|
const match = source.match(re);
|
|
1731
|
-
if (
|
|
1942
|
+
if (match) {
|
|
1943
|
+
return {
|
|
1944
|
+
needle: match[0],
|
|
1945
|
+
vars: {
|
|
1946
|
+
userText: match[1],
|
|
1947
|
+
richText: match[2],
|
|
1948
|
+
mode: match[3],
|
|
1949
|
+
selectedContext: match[4],
|
|
1950
|
+
requestContext: match[5],
|
|
1951
|
+
model: match[6],
|
|
1952
|
+
generationUUID: match[7],
|
|
1953
|
+
conversationId: match[8],
|
|
1954
|
+
messageId: match[9],
|
|
1955
|
+
conversationState: match[10],
|
|
1956
|
+
ctx: match[11],
|
|
1957
|
+
adapter: match[12],
|
|
1958
|
+
event: "e",
|
|
1959
|
+
},
|
|
1960
|
+
};
|
|
1961
|
+
}
|
|
1962
|
+
const anchor = source.indexOf('"AgentCompatService.agentClientService.run"');
|
|
1963
|
+
if (anchor < 0) return null;
|
|
1964
|
+
const start = Math.max(0, source.lastIndexOf("async runAgentLoop", anchor));
|
|
1965
|
+
const segment = source.slice(start, anchor + 500);
|
|
1966
|
+
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;
|
|
1970
|
+
const eventFactory = findInteractionUpdateFactory(source);
|
|
1971
|
+
if (!eventFactory) return null;
|
|
1972
|
+
const needleStart = segment.indexOf(args[0]);
|
|
1973
|
+
const needleEnd = adapter.index + adapter[0].length;
|
|
1732
1974
|
return {
|
|
1733
|
-
needle:
|
|
1975
|
+
needle: segment.slice(needleStart, needleEnd),
|
|
1734
1976
|
vars: {
|
|
1735
|
-
userText:
|
|
1736
|
-
richText:
|
|
1737
|
-
mode:
|
|
1738
|
-
selectedContext:
|
|
1739
|
-
requestContext:
|
|
1740
|
-
model:
|
|
1741
|
-
generationUUID:
|
|
1742
|
-
conversationId:
|
|
1743
|
-
messageId:
|
|
1744
|
-
conversationState:
|
|
1745
|
-
|
|
1746
|
-
adapter:
|
|
1747
|
-
event: "
|
|
1977
|
+
userText: args[1],
|
|
1978
|
+
richText: args[2],
|
|
1979
|
+
mode: args[3],
|
|
1980
|
+
selectedContext: args[4],
|
|
1981
|
+
requestContext: args[5],
|
|
1982
|
+
model: args[6],
|
|
1983
|
+
generationUUID: args[7],
|
|
1984
|
+
conversationId: args[8],
|
|
1985
|
+
messageId: args[9],
|
|
1986
|
+
conversationState: args[10],
|
|
1987
|
+
contextExpression: context[1],
|
|
1988
|
+
adapter: adapter[1],
|
|
1989
|
+
event: "t",
|
|
1990
|
+
eventFactory,
|
|
1748
1991
|
},
|
|
1749
1992
|
};
|
|
1750
1993
|
}
|
|
1751
1994
|
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
"src/patcher/cursor-paths.js": function(module, exports, require, __filename, __dirname) {
|
|
1756
|
-
"use strict";
|
|
1757
|
-
|
|
1758
|
-
const fs = require("fs");
|
|
1759
|
-
const path = require("path");
|
|
1760
|
-
const os = require("os");
|
|
1761
|
-
|
|
1762
|
-
function candidateCursorAppRoots() {
|
|
1763
|
-
if (process.platform === "darwin") {
|
|
1764
|
-
return [
|
|
1765
|
-
"/Applications/Cursor.app/Contents/Resources/app",
|
|
1766
|
-
path.join(os.homedir(), "Applications/Cursor.app/Contents/Resources/app"),
|
|
1767
|
-
];
|
|
1768
|
-
}
|
|
1769
|
-
if (process.platform === "win32") {
|
|
1770
|
-
const local = process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local");
|
|
1771
|
-
return [
|
|
1772
|
-
path.join(local, "Programs", "Cursor", "resources", "app"),
|
|
1773
|
-
path.join(local, "Cursor", "resources", "app"),
|
|
1774
|
-
];
|
|
1775
|
-
}
|
|
1776
|
-
return [];
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
function findCursorAppRoot(explicitRoot) {
|
|
1780
|
-
const roots = explicitRoot ? [explicitRoot] : candidateCursorAppRoots();
|
|
1781
|
-
for (const root of roots) {
|
|
1782
|
-
const workbench = workbenchPath(root);
|
|
1783
|
-
if (fs.existsSync(workbench)) return root;
|
|
1784
|
-
}
|
|
1785
|
-
throw new Error(`Cursor app root not found. Checked: ${roots.join(", ")}`);
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1788
|
-
function workbenchPath(appRoot) {
|
|
1789
|
-
return path.join(appRoot, "out", "vs", "workbench", "workbench.desktop.main.js");
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
function productJsonPath(appRoot) {
|
|
1793
|
-
return path.join(appRoot, "product.json");
|
|
1794
|
-
}
|
|
1795
|
-
|
|
1796
|
-
function packageJsonPath(appRoot) {
|
|
1797
|
-
return path.join(appRoot, "package.json");
|
|
1798
|
-
}
|
|
1799
|
-
|
|
1800
|
-
function readCursorInfo(appRoot) {
|
|
1801
|
-
const productPath = productJsonPath(appRoot);
|
|
1802
|
-
const packagePath = packageJsonPath(appRoot);
|
|
1803
|
-
const product = fs.existsSync(productPath) ? JSON.parse(fs.readFileSync(productPath, "utf8")) : {};
|
|
1804
|
-
const pkg = fs.existsSync(packagePath) ? JSON.parse(fs.readFileSync(packagePath, "utf8")) : {};
|
|
1805
|
-
return {
|
|
1806
|
-
appRoot,
|
|
1807
|
-
version: product.version || pkg.version || "unknown",
|
|
1808
|
-
commit: product.commit || "unknown",
|
|
1809
|
-
workbenchPath: workbenchPath(appRoot),
|
|
1810
|
-
};
|
|
1995
|
+
function findInteractionUpdateFactory(source) {
|
|
1996
|
+
const match = source.match(/(\w+)=\{textDelta\(\w+\)\{return new\s+\w+\(\{message:\{case:"textDelta",[\s\S]{0,2500}?turnEnded\(/);
|
|
1997
|
+
return match ? match[1] : null;
|
|
1811
1998
|
}
|
|
1812
1999
|
|
|
1813
|
-
module.exports = {
|
|
1814
|
-
candidateCursorAppRoots,
|
|
1815
|
-
findCursorAppRoot,
|
|
1816
|
-
workbenchPath,
|
|
1817
|
-
readCursorInfo,
|
|
1818
|
-
};
|
|
2000
|
+
module.exports = { status, installPatch, uninstallPatch, isSupportedVersion, findConnectHelperNeedle, findUnaryPatchPoint, findRunLoopPatchPoint };
|
|
1819
2001
|
|
|
1820
2002
|
},
|
|
1821
2003
|
"src/patcher/templates.js": function(module, exports, require, __filename, __dirname) {
|
|
@@ -1826,7 +2008,7 @@ const LEGACY_MARKER = "__LOCAL_CURSOR_BYOK_V0";
|
|
|
1826
2008
|
const ENDPOINT = "http://127.0.0.1:39832";
|
|
1827
2009
|
|
|
1828
2010
|
function helperSnippet() {
|
|
1829
|
-
return `function ${MARKER}(){if(globalThis.${MARKER})return globalThis.${MARKER};const endpoint="${ENDPOINT}";const sleep=ms=>new Promise(r=>setTimeout(r,ms));const tip=t=>({primaryText:"",secondaryText:"",secondaryWarningText:false,icon:"",tertiaryText:"",tertiaryTextUrl:"",markdownContent:t});const esc=v=>String(v??"").replace(/[\\\\*_\\[\\]\`]/g,"\\\\$&");const byok={async models(){const started=Date.now();for(;;){try{const r=await fetch(endpoint+"/cursor/v1/models");const j=await r.json();return j&&j.ok?j.models||[]:[]}catch(e){if(Date.now()-started>2e3)return[];await sleep(120)}}},toCursorModel(m){const name=m.displayName,provider=m.providerName||m.providerId||"BYOK",ctx=m.contextTokenLimit||128000,md="**"+esc(name)+"**<br />OpenAI-compatible BYOK model via "+esc(provider)+".<br /><br />"+ctx.toLocaleString()+" context window";return{name,defaultOn:true,parameterDefinitions:[],variants:[{parameterValues:[],displayName:name,isMaxMode:false,isDefaultMaxConfig:true,isDefaultNonMaxConfig:true,tooltipData:tip(md),displayNameOutsidePicker:name,variantStringRepresentation:name+"[]",legacySlug:name}],legacySlugs:[name],idAliases:[name],cloudAgentEffortModes:[],supportsAgent:true,degradationStatus:0,tooltipData:tip(md),supportsThinking:false,supportsImages:m.supportsImages!==false,supportsMaxMode:true,clientDisplayName:name,serverModelName:name,supportsNonMaxMode:true,tooltipDataForMaxMode:tip(md),isRecommendedForBackgroundComposer:false,supportsPlanMode:true,inputboxShortModelName:name,supportsSandboxing:true,supportsCmdK:true,contextTokenLimit:ctx,contextTokenLimitForMaxMode:ctx,maxOutputTokens:m.maxOutputTokens||8192,namedModelSectionIndex:1,vendorName:provider,vendor:{id:9000,displayName:provider},tagline:"OpenAI-compatible BYOK",visibleInRoutedModelView:
|
|
2011
|
+
return `function ${MARKER}(){if(globalThis.${MARKER})return globalThis.${MARKER};const endpoint="${ENDPOINT}";const sleep=ms=>new Promise(r=>setTimeout(r,ms));const tip=t=>({primaryText:"",secondaryText:"",secondaryWarningText:false,icon:"",tertiaryText:"",tertiaryTextUrl:"",markdownContent:t});const esc=v=>String(v??"").replace(/[\\\\*_\\[\\]\`]/g,"\\\\$&");const byok={async models(){const started=Date.now();for(;;){try{const r=await fetch(endpoint+"/cursor/v1/models");const j=await r.json();return j&&j.ok?j.models||[]:[]}catch(e){if(Date.now()-started>2e3)return[];await sleep(120)}}},toCursorModel(m){const name=m.displayName,provider=m.providerName||m.providerId||"BYOK",ctx=m.contextTokenLimit||128000,md="**"+esc(name)+"**<br />OpenAI-compatible BYOK model via "+esc(provider)+".<br /><br />"+ctx.toLocaleString()+" context window";return{name,defaultOn:true,parameterDefinitions:[],variants:[{parameterValues:[],displayName:name,isMaxMode:false,isDefaultMaxConfig:true,isDefaultNonMaxConfig:true,tooltipData:tip(md),displayNameOutsidePicker:name,variantStringRepresentation:name+"[]",legacySlug:name}],legacySlugs:[name],idAliases:[name],cloudAgentEffortModes:[],supportsAgent:true,degradationStatus:0,tooltipData:tip(md),supportsThinking:false,supportsImages:m.supportsImages!==false,supportsMaxMode:true,clientDisplayName:name,serverModelName:name,supportsNonMaxMode:true,tooltipDataForMaxMode:tip(md),isRecommendedForBackgroundComposer:false,supportsPlanMode:true,inputboxShortModelName:name,supportsSandboxing:true,supportsCmdK:true,contextTokenLimit:ctx,contextTokenLimitForMaxMode:ctx,maxOutputTokens:m.maxOutputTokens||8192,namedModelSectionIndex:1,vendorName:provider,vendor:{id:9000,displayName:provider},tagline:"OpenAI-compatible BYOK",visibleInRoutedModelView:false,isUserAdded:true,byokModel:true}},async shouldHandle(model){try{if(!model)return false;const r=await fetch(endpoint+"/cursor/v1/should-handle?model="+encodeURIComponent(model));const j=await r.json();return !!(j&&j.ok&&j.handle)}catch{return false}},async enrichModels(list,models){try{models=models||await this.models();if(!models.length)return list;const out=Array.isArray(list)?list:[];for(const m of models){const cm=this.toCursorModel(m),i=out.findIndex(x=>x&&(x.name===m.displayName||x.clientDisplayName===m.displayName||x.serverModelName===m.displayName));i>=0?out[i]={...out[i],...cm}:out.push(cm)}return out}catch(e){console.warn("cursor-openai-byok model enrich failed",e);return list}},async registerCustomModels(models){try{await fetch(endpoint+"/cursor/v1/custom-models",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({models})})}catch{}},async patchAvailableModels(msg,req){try{if(!msg)return;const models=await this.models(),known=new Set((msg.models||[]).flatMap(m=>[m?.name,m?.clientDisplayName,m?.serverModelName]).filter(Boolean)),names=Array.isArray(req?.additionalModelNames)?req.additionalModelNames:[],custom=[...new Set(names.filter(n=>typeof n==="string"&&n&&!known.has(n)&&!models.some(m=>m.displayName===n)))];await this.registerCustomModels(custom);const all=[...models,...custom.map(displayName=>({displayName,providerName:"BYOK"}))];if(!all.length)return;msg.modelNames=msg.modelNames||[];msg.models=await this.enrichModels(msg.models||[],all);for(const m of all)if(!msg.modelNames.includes(m.displayName))msg.modelNames.push(m.displayName)}catch(e){console.warn("cursor-openai-byok model patch failed",e)}},async runAgent(payload,onEvent){const res=await fetch(endpoint+"/cursor/v1/run-agent-loop",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(payload)});if(!res.ok)throw new Error("BYOK HTTP "+res.status);const reader=res.body.getReader();const decoder=new TextDecoder();let buf="";for(;;){const item=await reader.read();if(item.done)break;buf+=decoder.decode(item.value,{stream:true});let idx;while((idx=buf.indexOf("\\n"))>=0){const line=buf.slice(0,idx).trim();buf=buf.slice(idx+1);if(line){try{await onEvent(JSON.parse(line))}catch(e){console.warn("cursor-openai-byok event failed",e)}}}}}};globalThis.${MARKER}=byok;return byok}`;
|
|
1830
2012
|
}
|
|
1831
2013
|
|
|
1832
2014
|
function agentLoopSnippet(vars) {
|
|
@@ -1845,7 +2027,9 @@ function agentLoopSnippet(vars) {
|
|
|
1845
2027
|
messageId: "b",
|
|
1846
2028
|
conversationState: "W",
|
|
1847
2029
|
};
|
|
1848
|
-
|
|
2030
|
+
const context = v.contextExpression || `${v.ctx}.ctx`;
|
|
2031
|
+
const events = v.eventFactory || "E0c";
|
|
2032
|
+
return `if(!${v.event}.subagentTypeName&&await ${MARKER}().shouldHandle(${v.model}?.modelName)){const sendText=async text=>{if(text)await ${v.adapter}.sendUpdate(${context},${events}.textDelta(text))};const sendDone=async()=>{await ${v.adapter}.sendUpdate(${context},${events}.turnEnded())};try{await ${MARKER}().runAgent({model:${v.model}?.modelName,userText:${v.userText},richText:${v.richText},mode:${v.mode},selectedContext:${v.selectedContext},requestContext:${v.requestContext},generationUUID:${v.generationUUID},conversationId:${v.conversationId},messageId:${v.messageId},conversationState:${v.conversationState},event:${v.event},tools:${v.event}.tools},async ev=>{if(ev.type==="text_delta")await sendText(ev.text||"");else if(ev.type==="tool_calls_ready")await sendText("\\n[BYOK tool calls requested: "+(ev.toolCalls||[]).map(t=>t.name||t.id).join(", ")+"]\\n");else if(ev.type==="error")await sendText("Local BYOK error: "+(ev.error||"unknown"));});await sendDone();return}catch(err){await sendText("Local BYOK error: "+(err&&err.message?err.message:String(err)));await sendDone();return}}`;
|
|
1849
2033
|
}
|
|
1850
2034
|
|
|
1851
2035
|
module.exports = { MARKER, LEGACY_MARKER, helperSnippet, agentLoopSnippet };
|