ocx-cursor 1.0.2 → 1.0.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/README.md +1 -1
- package/package.json +1 -1
- package/src/catalog.mjs +27 -2
- package/src/cursor-patch.mjs +16 -2
- package/src/install.mjs +3 -3
- package/src/service.mjs +4 -7
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
Use your active [OpenCodex](https://github.com/lidge-jun/opencodex) models in Cursor through an authenticated HTTPS endpoint on your Mac.
|
|
7
7
|
|
|
8
8
|
> [!WARNING]
|
|
9
|
-
> The installer edits two private workbench files inside `Cursor.app`. This invalidates Cursor's vendor signature, so the bridge removes the app's quarantine attribute after each patch to prevent macOS from reporting a corrupt installation. The bridge saves the original files under `~/.opencodex/cursor-bridge/cursor-app-backups`. Reinstall Cursor to restore a vendor-signed app.
|
|
9
|
+
> The installer edits two private workbench files inside `Cursor.app`. This invalidates Cursor's vendor signature, so the bridge applies a local ad-hoc seal and removes the app's quarantine attribute after each patch to prevent macOS from reporting a corrupt installation. The bridge saves the original files under `~/.opencodex/cursor-bridge/cursor-app-backups`. Reinstall Cursor to restore a vendor-signed app.
|
|
10
10
|
|
|
11
11
|
## Requirements
|
|
12
12
|
|
package/package.json
CHANGED
package/src/catalog.mjs
CHANGED
|
@@ -73,6 +73,30 @@ export function priorityModelIds(catalogFile = configuredCodexCatalogFile()) {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
export function codexNativeModels(catalogFile = configuredCodexCatalogFile()) {
|
|
77
|
+
try {
|
|
78
|
+
const payload = JSON.parse(readFileSync(catalogFile, "utf8"));
|
|
79
|
+
return payload.models
|
|
80
|
+
.filter((model) => typeof model?.slug === "string"
|
|
81
|
+
&& !model.slug.includes("/")
|
|
82
|
+
&& model.visibility === "list"
|
|
83
|
+
&& model.supported_in_api === true
|
|
84
|
+
&& model.opencodex_capability_provenance === undefined)
|
|
85
|
+
.map((model) => ({
|
|
86
|
+
id: model.slug,
|
|
87
|
+
owned_by: "openai",
|
|
88
|
+
capabilities: {
|
|
89
|
+
input_modalities: model.input_modalities,
|
|
90
|
+
context_length: model.context_window,
|
|
91
|
+
max_output_tokens: model.max_output_tokens,
|
|
92
|
+
reasoning_effort: model.supported_reasoning_levels?.map(({ effort }) => effort),
|
|
93
|
+
},
|
|
94
|
+
}));
|
|
95
|
+
} catch {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
76
100
|
export function normalizeActiveCatalog(configured, active, fastModelIds = new Set()) {
|
|
77
101
|
const configuredById = new Map(configured
|
|
78
102
|
.filter((model) => typeof model.provider === "string" && typeof model.model === "string")
|
|
@@ -141,9 +165,10 @@ export async function activeModels(fetchImpl = fetch) {
|
|
|
141
165
|
}
|
|
142
166
|
|
|
143
167
|
export async function buildActiveCatalog(options = {}) {
|
|
168
|
+
const codexCatalogFile = options.codexCatalogFile || configuredCodexCatalogFile();
|
|
144
169
|
return normalizeActiveCatalog(
|
|
145
170
|
configuredModels(options.ocxBin),
|
|
146
|
-
await activeModels(options.fetchImpl),
|
|
147
|
-
options.fastModelIds || priorityModelIds(
|
|
171
|
+
[...await activeModels(options.fetchImpl), ...codexNativeModels(codexCatalogFile)],
|
|
172
|
+
options.fastModelIds || priorityModelIds(codexCatalogFile),
|
|
148
173
|
);
|
|
149
174
|
}
|
package/src/cursor-patch.mjs
CHANGED
|
@@ -45,6 +45,14 @@ export function clearCursorAppQuarantine(options = {}) {
|
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
export function finalizeCursorAppPatch(options = {}) {
|
|
49
|
+
const execute = options.execFileSync || execFileSync;
|
|
50
|
+
const appPath = options.appPath || cursorAppPath;
|
|
51
|
+
execute("/usr/bin/codesign", ["--force", "--sign", "-", appPath], { stdio: "ignore" });
|
|
52
|
+
execute("/usr/bin/codesign", ["--verify", "--deep", "--strict", appPath], { stdio: "ignore" });
|
|
53
|
+
clearCursorAppQuarantine({ ...options, appPath, execFileSync: execute });
|
|
54
|
+
}
|
|
55
|
+
|
|
48
56
|
function replaceSingleMatch(source, pattern, replacement, label) {
|
|
49
57
|
const matches = [...source.matchAll(pattern)];
|
|
50
58
|
if (matches.length !== 1) throw new Error(`Expected one Cursor ${label}, found ${matches.length}`);
|
|
@@ -433,8 +441,14 @@ export function startCursorPatchMonitor(options = {}) {
|
|
|
433
441
|
}));
|
|
434
442
|
if (retryAfterStabilityDelay) resetStability();
|
|
435
443
|
const patched = results.filter((result) => result?.status === "patched");
|
|
436
|
-
|
|
437
|
-
|
|
444
|
+
try {
|
|
445
|
+
if (patched.length > 0) await options.onPatched?.(patched);
|
|
446
|
+
if (results.length === files.length && results.every(Boolean)) await options.onReady?.(results);
|
|
447
|
+
} catch (error) {
|
|
448
|
+
resetStability();
|
|
449
|
+
lastSignatures.clear();
|
|
450
|
+
options.onError?.(error, options.appPath || cursorAppPath);
|
|
451
|
+
}
|
|
438
452
|
return results;
|
|
439
453
|
})().finally(() => {
|
|
440
454
|
patchPromise = null;
|
package/src/install.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { access, chmod, copyFile, cp, lstat, mkdir, mkdtemp, readFile, readlink,
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
-
import {
|
|
7
|
+
import { ensureCursorModelMetadataPatched, finalizeCursorAppPatch } from "./cursor-patch.mjs";
|
|
8
8
|
import { cursorIsRunning } from "./cursor-state.mjs";
|
|
9
9
|
import {
|
|
10
10
|
cliLinkFile,
|
|
@@ -210,8 +210,8 @@ export async function repairCursorMetadataPatch(options = {}) {
|
|
|
210
210
|
file,
|
|
211
211
|
...await patchFile({ file }),
|
|
212
212
|
})));
|
|
213
|
-
const
|
|
214
|
-
|
|
213
|
+
const finalizePatch = options.finalizeCursorAppPatch || finalizeCursorAppPatch;
|
|
214
|
+
finalizePatch({ appPath });
|
|
215
215
|
return patches;
|
|
216
216
|
}
|
|
217
217
|
|
package/src/service.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { buildActiveCatalog } from "./catalog.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { finalizeCursorAppPatch, startCursorModelMetadataPatchMonitor } from "./cursor-patch.mjs";
|
|
5
5
|
import {
|
|
6
6
|
cursorIsRunning,
|
|
7
7
|
cursorUpdateIsRunning,
|
|
@@ -72,13 +72,10 @@ export async function runService(options = {}) {
|
|
|
72
72
|
if (result.status === "patched") process.stdout.write(`Patched Cursor app file ${result.file} (backup: ${result.backupPath})\n`);
|
|
73
73
|
},
|
|
74
74
|
onPatched: () => {
|
|
75
|
-
|
|
76
|
-
clearCursorAppQuarantine();
|
|
77
|
-
} catch (error) {
|
|
78
|
-
process.stderr.write(`Cursor quarantine removal failed: ${error.message}\n`);
|
|
79
|
-
}
|
|
75
|
+
finalizeCursorAppPatch();
|
|
80
76
|
},
|
|
81
|
-
onReady: () => {
|
|
77
|
+
onReady: (results) => {
|
|
78
|
+
if (results.every(({ status }) => status === "already-patched")) finalizeCursorAppPatch();
|
|
82
79
|
cursorPatchReady = true;
|
|
83
80
|
wasCursorRunning = true;
|
|
84
81
|
},
|