ocx-cursor 0.5.2 → 1.0.0
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 +101 -44
- package/bin/ocx-cursor.mjs +19 -6
- package/package.json +2 -2
- package/src/cursor-patch.mjs +23 -0
- package/src/cursor-state.mjs +9 -1
- package/src/install.mjs +8 -2
- package/src/paths.mjs +1 -1
- package/src/service.mjs +2 -2
- package/src/setup.mjs +1 -1
package/README.md
CHANGED
|
@@ -3,16 +3,29 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/ocx-cursor)
|
|
4
4
|
[](https://github.com/hiddenest/opencodex-cursor-bridge/actions/workflows/ci.yml)
|
|
5
5
|
|
|
6
|
-
Use your active [OpenCodex](https://github.com/lidge-jun/opencodex) models in Cursor through
|
|
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
|
-
>
|
|
9
|
+
> The installer edits two private workbench files inside `Cursor.app`. This invalidates Cursor's vendor signature. Cursor may report a corrupt installation. The bridge saves the original files under `~/.opencodex/cursor-bridge/cursor-app-backups`. Reinstall Cursor to restore a signed app.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Requirements
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
- macOS with Cursor installed at `/Applications/Cursor.app`
|
|
14
|
+
- Node.js 22.5 or newer
|
|
15
|
+
- OpenCodex with at least one active model
|
|
16
|
+
- An HTTPS hostname that forwards to `http://127.0.0.1:10101`
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
Use [Cloudflare Tunnel](https://developers.cloudflare.com/tunnel/) for the HTTPS hostname, or choose another reverse proxy that preserves the `Authorization` header.
|
|
19
|
+
|
|
20
|
+
## Upgrading from 0.5.x
|
|
21
|
+
|
|
22
|
+
Version 0.5.x enabled Cursor's local-agent mode and edited ten app files. Version 1.0.0 edits two workbench files and leaves local-agent mode disabled.
|
|
23
|
+
|
|
24
|
+
Reinstall Cursor before upgrading so macOS replaces the old app files. Quit the new Cursor installation, then follow the setup below. Your Cursor settings remain under `~/Library/Application Support/Cursor`.
|
|
25
|
+
|
|
26
|
+
## Getting started
|
|
27
|
+
|
|
28
|
+
Install OpenCodex and start its login service:
|
|
16
29
|
|
|
17
30
|
```bash
|
|
18
31
|
npm install --global @bitkyc08/opencodex
|
|
@@ -21,98 +34,142 @@ ocx service install
|
|
|
21
34
|
ocx service status
|
|
22
35
|
```
|
|
23
36
|
|
|
24
|
-
Check
|
|
37
|
+
OpenCodex listens on `http://127.0.0.1:10100`. Check its active models:
|
|
25
38
|
|
|
26
39
|
```bash
|
|
27
40
|
ocx models --json
|
|
28
41
|
```
|
|
29
42
|
|
|
30
|
-
|
|
43
|
+
Install `cloudflared`, sign in, and create a named tunnel:
|
|
31
44
|
|
|
32
45
|
```bash
|
|
33
|
-
|
|
34
|
-
|
|
46
|
+
brew install cloudflared
|
|
47
|
+
cloudflared tunnel login
|
|
48
|
+
cloudflared tunnel create ocx-cursor
|
|
49
|
+
cloudflared tunnel list
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Create `~/.cloudflared/config.yml`. Replace the UUID, username, and hostname:
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
tunnel: YOUR_TUNNEL_UUID
|
|
56
|
+
credentials-file: /Users/YOUR_USERNAME/.cloudflared/YOUR_TUNNEL_UUID.json
|
|
57
|
+
|
|
58
|
+
ingress:
|
|
59
|
+
- hostname: cursor-api.example.com
|
|
60
|
+
service: http://127.0.0.1:10101
|
|
61
|
+
- service: http_status:404
|
|
35
62
|
```
|
|
36
63
|
|
|
37
|
-
|
|
64
|
+
Create the DNS record and check the ingress rule:
|
|
38
65
|
|
|
39
|
-
|
|
66
|
+
```bash
|
|
67
|
+
cloudflared tunnel route dns ocx-cursor cursor-api.example.com
|
|
68
|
+
cloudflared tunnel ingress validate
|
|
69
|
+
cloudflared tunnel ingress rule https://cursor-api.example.com
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Start the tunnel at login with Cloudflare's [macOS service command](https://developers.cloudflare.com/tunnel/advanced/local-management/as-a-service/macos/):
|
|
40
73
|
|
|
41
74
|
```bash
|
|
42
|
-
|
|
75
|
+
cloudflared service install
|
|
43
76
|
```
|
|
44
77
|
|
|
45
|
-
|
|
78
|
+
This command reads `~/.cloudflared/config.yml`. `sudo cloudflared service install` starts the tunnel at boot and reads `/etc/cloudflared/config.yml` instead.
|
|
46
79
|
|
|
47
|
-
|
|
48
|
-
| --- | --- |
|
|
49
|
-
| Cursor settings | Stores the gateway key with macOS Safe Storage and sets the local base URL. |
|
|
50
|
-
| Cursor model state | Adds active models under `opencodex/*` with names, effort choices, and Fast metadata. |
|
|
51
|
-
| `Cursor.app` | Enables local-agent mode in eight bundles and patches two local-agent runtimes. |
|
|
52
|
-
| Login service | Installs `com.opencodex.cursor-bridge`, refreshes models, and reapplies patches after Cursor updates. |
|
|
80
|
+
Launch Cursor once and sign in, then quit it. Install the bridge:
|
|
53
81
|
|
|
54
|
-
|
|
82
|
+
```bash
|
|
83
|
+
npx ocx-cursor init --base-url https://cursor-api.example.com/v1
|
|
84
|
+
~/.local/bin/ocx-cursor status
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Open Cursor after `status` reports a running service and a healthy gateway.
|
|
88
|
+
|
|
89
|
+
## How requests move
|
|
55
90
|
|
|
56
91
|
```text
|
|
57
|
-
Cursor
|
|
92
|
+
Cursor
|
|
93
|
+
-> https://cursor-api.example.com/v1
|
|
94
|
+
-> Cloudflare Tunnel
|
|
95
|
+
-> OpenCodex Cursor Bridge on 127.0.0.1:10101
|
|
96
|
+
-> OpenCodex on 127.0.0.1:10100
|
|
97
|
+
-> your provider
|
|
58
98
|
```
|
|
59
99
|
|
|
60
|
-
The
|
|
100
|
+
The bridge generates a bearer token during `init`, stores it in Cursor with macOS Safe Storage, and checks it on each `/v1/*` request. The gateway binds to `127.0.0.1` unless you set `OCX_CURSOR_HOST`.
|
|
61
101
|
|
|
62
|
-
|
|
102
|
+
The package does not create, edit, or remove your Cloudflare Tunnel.
|
|
63
103
|
|
|
64
|
-
##
|
|
104
|
+
## Models and Cursor metadata
|
|
65
105
|
|
|
66
|
-
The bridge reads
|
|
106
|
+
The bridge reads the active OpenCodex catalog and adds stable aliases to Cursor:
|
|
67
107
|
|
|
68
108
|
```text
|
|
69
109
|
anthropic/claude-sonnet-5 -> opencodex/claude-sonnet-5
|
|
70
110
|
gpt-5.6-sol -> opencodex/gpt-5.6-sol
|
|
71
111
|
```
|
|
72
112
|
|
|
73
|
-
Cursor
|
|
113
|
+
Cursor displays provider prefixes where model names overlap. Examples include `Cursor Kimi K3` and `OpenCode Go Kimi K3`. Claude and GPT names stay short.
|
|
114
|
+
|
|
115
|
+
Reasoning models receive an effort selector. Models that advertise OpenCodex's `priority` service tier receive a Fast toggle. The gateway maps those choices to `reasoning_effort` and `service_tier: priority`.
|
|
116
|
+
|
|
117
|
+
The installer patches these files:
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
/Applications/Cursor.app/Contents/Resources/app/out/vs/workbench/workbench.desktop.main.js
|
|
121
|
+
/Applications/Cursor.app/Contents/Resources/app/out/vs/workbench/workbench.glass.main.js
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The patch keeps the model metadata that Cursor would discard. It leaves Cursor's local-agent mode disabled and does not edit either local-agent runtime.
|
|
74
125
|
|
|
75
|
-
|
|
126
|
+
The login service refreshes the catalog every 15 seconds. It waits for Cursor to quit before writing the state database or reapplying a patch after a Cursor update.
|
|
76
127
|
|
|
77
|
-
## Commands
|
|
128
|
+
## Commands
|
|
78
129
|
|
|
79
|
-
| Command |
|
|
130
|
+
| Command | Action |
|
|
80
131
|
| --- | --- |
|
|
81
|
-
| `ocx-cursor init` | Install the service,
|
|
82
|
-
| `ocx-cursor install` | Reinstall the service and check
|
|
83
|
-
| `ocx-cursor update` | Install the
|
|
84
|
-
| `ocx-cursor sync` |
|
|
85
|
-
| `ocx-cursor status` |
|
|
132
|
+
| `ocx-cursor init --base-url URL` | Install the service, test the HTTPS endpoint, configure Cursor, and sync models. |
|
|
133
|
+
| `ocx-cursor install` | Reinstall the service and check the two metadata patches. |
|
|
134
|
+
| `ocx-cursor update` | Install the current npm release and restart the service. |
|
|
135
|
+
| `ocx-cursor sync` | Read the active OpenCodex catalog and update Cursor. |
|
|
136
|
+
| `ocx-cursor status` | Print service, gateway, catalog, and pending-sync status. |
|
|
86
137
|
| `ocx-cursor uninstall` | Remove the service, command link, and bridge state. |
|
|
87
138
|
|
|
88
|
-
`uninstall`
|
|
139
|
+
`uninstall` leaves Cursor's endpoint setting and patched workbench files in place. Reinstall Cursor if you want a signed app without the metadata patch.
|
|
140
|
+
|
|
141
|
+
## Configuration
|
|
89
142
|
|
|
90
143
|
| Variable | Default |
|
|
91
144
|
| --- | --- |
|
|
92
|
-
| `OCX_CURSOR_BASE_URL` |
|
|
145
|
+
| `OCX_CURSOR_BASE_URL` | Cursor's stored endpoint |
|
|
93
146
|
| `OCX_CURSOR_HOME` | `~/.opencodex/cursor-bridge` |
|
|
94
147
|
| `OCX_CURSOR_HOST` | `127.0.0.1` |
|
|
95
148
|
| `OCX_CURSOR_PORT` | `10101` |
|
|
96
149
|
| `OCX_BIN` | `~/.local/bin/ocx` |
|
|
97
150
|
|
|
98
|
-
The
|
|
151
|
+
The gateway exposes `GET /v1/models`, `POST /v1/chat/completions`, `POST /v1/responses`, `POST /v1/responses/compact`, and `POST /v1/messages`.
|
|
152
|
+
|
|
153
|
+
The bridge reads `~/.opencodex/service-api-token` when OpenCodex uses service authentication.
|
|
99
154
|
|
|
100
155
|
## Troubleshooting
|
|
101
156
|
|
|
102
|
-
| Symptom |
|
|
157
|
+
| Symptom | Check |
|
|
103
158
|
| --- | --- |
|
|
104
|
-
| Cursor returns 401 | Quit Cursor and rerun `npx ocx-cursor init`. |
|
|
105
|
-
|
|
|
106
|
-
|
|
|
107
|
-
|
|
|
108
|
-
|
|
|
159
|
+
| Cursor returns 401 | Quit Cursor and rerun `npx ocx-cursor init --base-url https://YOUR_HOST/v1`. |
|
|
160
|
+
| The hostname returns 502 | Run `ocx-cursor status`, then check that the tunnel points to port `10101`. |
|
|
161
|
+
| Cloudflare returns 1016 | Run `cloudflared tunnel info ocx-cursor` and check the service. |
|
|
162
|
+
| Models or controls are missing | Quit Cursor and run `ocx-cursor sync`. |
|
|
163
|
+
| A Cursor update removed the patch | Quit Cursor and run `ocx-cursor install`. |
|
|
164
|
+
|
|
165
|
+
Read the service logs:
|
|
109
166
|
|
|
110
167
|
```bash
|
|
111
168
|
tail -n 100 ~/.opencodex/cursor-bridge/service.log
|
|
112
169
|
tail -n 100 ~/.opencodex/cursor-bridge/service.error.log
|
|
113
170
|
```
|
|
114
171
|
|
|
115
|
-
The patcher stops
|
|
172
|
+
The patcher stops if a Cursor update removes its known catalog hook. It records the file and error without replacing that file.
|
|
116
173
|
|
|
117
174
|
## Development
|
|
118
175
|
|
package/bin/ocx-cursor.mjs
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
4
|
import process from "node:process";
|
|
5
|
-
import {
|
|
5
|
+
import { createInterface } from "node:readline/promises";
|
|
6
|
+
import { configureCursorOpenAI, storedCursorOpenAIBaseUrl } from "../src/cursor-config.mjs";
|
|
6
7
|
import { cursorIsRunning } from "../src/cursor-state.mjs";
|
|
7
8
|
import { installService, prepareInstallSecret, serviceStatus, uninstallService, updateService } from "../src/install.mjs";
|
|
8
|
-
import {
|
|
9
|
+
import { cursorOpenAIBaseUrl, pendingFile } from "../src/paths.mjs";
|
|
9
10
|
import { runService } from "../src/service.mjs";
|
|
10
11
|
import { normalizeBaseUrl, testEndpoint } from "../src/setup.mjs";
|
|
11
12
|
import { loadCatalogSnapshot, syncNow } from "../src/sync.mjs";
|
|
@@ -36,7 +37,19 @@ function argumentValue(name) {
|
|
|
36
37
|
|
|
37
38
|
async function requestedBaseUrl() {
|
|
38
39
|
const supplied = argumentValue("--base-url");
|
|
39
|
-
|
|
40
|
+
const fallback = cursorOpenAIBaseUrl || storedCursorOpenAIBaseUrl();
|
|
41
|
+
if (supplied) return normalizeBaseUrl(supplied);
|
|
42
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return normalizeBaseUrl(fallback);
|
|
43
|
+
|
|
44
|
+
const prompt = fallback
|
|
45
|
+
? `HTTPS endpoint [${fallback}]: `
|
|
46
|
+
: "HTTPS endpoint (for example, https://cursor-api.example.com): ";
|
|
47
|
+
const readline = createInterface({ input: process.stdin, output: process.stdout });
|
|
48
|
+
try {
|
|
49
|
+
return normalizeBaseUrl((await readline.question(prompt)).trim() || fallback);
|
|
50
|
+
} finally {
|
|
51
|
+
readline.close();
|
|
52
|
+
}
|
|
40
53
|
}
|
|
41
54
|
|
|
42
55
|
async function pendingCount() {
|
|
@@ -63,7 +76,7 @@ function printCursorPatches(result) {
|
|
|
63
76
|
return;
|
|
64
77
|
}
|
|
65
78
|
const changed = result.cursorPatches.filter(({ status }) => status === "patched").length;
|
|
66
|
-
process.stdout.write(`Verified ${result.cursorPatches.length} Cursor
|
|
79
|
+
process.stdout.write(`Verified ${result.cursorPatches.length} Cursor metadata patches (${changed} changed).\n`);
|
|
67
80
|
}
|
|
68
81
|
|
|
69
82
|
async function main() {
|
|
@@ -83,7 +96,7 @@ async function main() {
|
|
|
83
96
|
process.stdout.write(`Installed ${installed.launchAgentFile} (API key ${installed.secretStatus}).\nCLI: ${installed.cliLinkFile}\n`);
|
|
84
97
|
printCursorPatches(installed);
|
|
85
98
|
printSync(await syncNow());
|
|
86
|
-
process.stdout.write("
|
|
99
|
+
process.stdout.write("Local gateway: http://127.0.0.1:10101/v1\n");
|
|
87
100
|
return;
|
|
88
101
|
}
|
|
89
102
|
if (command === "init") {
|
|
@@ -100,7 +113,7 @@ async function main() {
|
|
|
100
113
|
try {
|
|
101
114
|
endpoint = await testEndpoint(baseUrl, prepared.secret);
|
|
102
115
|
} catch (error) {
|
|
103
|
-
throw new Error(`${error.message}\nThe bridge service is running locally. Fix the endpoint and rerun ocx-cursor init.`);
|
|
116
|
+
throw new Error(`${error.message}\nThe bridge service is running locally. Fix the HTTPS endpoint and rerun ocx-cursor init.`);
|
|
104
117
|
}
|
|
105
118
|
process.stdout.write(`Endpoint is ready (${endpoint.modelCount} models reachable).\n`);
|
|
106
119
|
const configured = await configureCursorOpenAI({
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ocx-cursor",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "OpenCodex
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Use OpenCodex models in Cursor through an authenticated HTTPS gateway",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"opencodex",
|
|
7
7
|
"cursor",
|
package/src/cursor-patch.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
cursorBundleFiles,
|
|
6
6
|
cursorGlassWorkbenchFile,
|
|
7
7
|
cursorLocalRuntimeFiles,
|
|
8
|
+
cursorModelMetadataFiles,
|
|
8
9
|
cursorPatchBackupDirectory,
|
|
9
10
|
cursorWorkbenchFile,
|
|
10
11
|
} from "./paths.mjs";
|
|
@@ -229,6 +230,13 @@ export async function ensureCursorWorkbenchPatched(options = {}) {
|
|
|
229
230
|
return await writePatchedFile(file, source, patched, backupDirectory);
|
|
230
231
|
}
|
|
231
232
|
|
|
233
|
+
export async function ensureCursorModelMetadataPatched(options = {}) {
|
|
234
|
+
const file = options.file || cursorWorkbenchFile;
|
|
235
|
+
const backupDirectory = options.backupDirectory || cursorPatchBackupDirectory;
|
|
236
|
+
const source = await readFile(file, "utf8");
|
|
237
|
+
return await writePatchedFile(file, source, patchCursorWorkbenchSource(source), backupDirectory);
|
|
238
|
+
}
|
|
239
|
+
|
|
232
240
|
export async function ensureCursorLocalRuntimesPatched(options = {}) {
|
|
233
241
|
const files = options.files || cursorLocalRuntimeFiles;
|
|
234
242
|
const backupDirectory = options.backupDirectory || cursorPatchBackupDirectory;
|
|
@@ -268,6 +276,12 @@ export async function ensureCursorAppPatched(options = {}) {
|
|
|
268
276
|
}
|
|
269
277
|
|
|
270
278
|
async function ensureCursorPatchFile(file, localRuntimeFiles, options) {
|
|
279
|
+
if (options.metadataOnly) {
|
|
280
|
+
return {
|
|
281
|
+
file,
|
|
282
|
+
...await ensureCursorModelMetadataPatched({ ...options, file }),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
271
285
|
if (localRuntimeFiles.has(file)) {
|
|
272
286
|
const [result] = await ensureCursorLocalRuntimesPatched({
|
|
273
287
|
...options,
|
|
@@ -285,6 +299,15 @@ async function ensureCursorPatchFile(file, localRuntimeFiles, options) {
|
|
|
285
299
|
};
|
|
286
300
|
}
|
|
287
301
|
|
|
302
|
+
export function startCursorModelMetadataPatchMonitor(options = {}) {
|
|
303
|
+
return startCursorPatchMonitor({
|
|
304
|
+
...options,
|
|
305
|
+
bundleFiles: options.bundleFiles || cursorModelMetadataFiles,
|
|
306
|
+
localRuntimeFiles: [],
|
|
307
|
+
metadataOnly: true,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
288
311
|
export function startCursorPatchMonitor(options = {}) {
|
|
289
312
|
const intervalMs = options.intervalMs || 250;
|
|
290
313
|
const bundleFiles = options.file ? [options.file] : (options.bundleFiles || options.files || cursorBundleFiles);
|
package/src/cursor-state.mjs
CHANGED
|
@@ -29,6 +29,11 @@ const displayWords = new Map([
|
|
|
29
29
|
["spark", "Spark"],
|
|
30
30
|
["terra", "Terra"],
|
|
31
31
|
]);
|
|
32
|
+
const providerDisplayNames = new Map([
|
|
33
|
+
["cursor", "Cursor"],
|
|
34
|
+
["opencode-go", "OpenCode Go"],
|
|
35
|
+
]);
|
|
36
|
+
const unprefixedProviders = new Set(["anthropic", "openai"]);
|
|
32
37
|
|
|
33
38
|
function displayWord(value) {
|
|
34
39
|
const known = displayWords.get(value);
|
|
@@ -42,8 +47,11 @@ function displayWord(value) {
|
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
export function displayNameFor(model) {
|
|
50
|
+
const provider = model.provider || (model.sourceId.includes("/") ? model.sourceId.split("/", 1)[0] : "openai");
|
|
51
|
+
const providerDisplayName = providerDisplayNames.get(provider)
|
|
52
|
+
|| provider.split(/[-_]/).map(displayWord).join(" ");
|
|
45
53
|
const displayName = model.sourceId.split("/").at(-1).split("-").map(displayWord).join(" ");
|
|
46
|
-
return
|
|
54
|
+
return unprefixedProviders.has(provider) ? displayName : `${providerDisplayName} ${displayName}`;
|
|
47
55
|
}
|
|
48
56
|
|
|
49
57
|
export function cursorIsRunning() {
|
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 } from "./cursor-patch.mjs";
|
|
8
8
|
import { cursorIsRunning } from "./cursor-state.mjs";
|
|
9
9
|
import {
|
|
10
10
|
cliLinkFile,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
launchAgentFile,
|
|
14
14
|
legacyLaunchAgentFile,
|
|
15
15
|
legacyServiceLabel,
|
|
16
|
+
cursorModelMetadataFiles,
|
|
16
17
|
secretFile,
|
|
17
18
|
serviceLabel,
|
|
18
19
|
stderrFile,
|
|
@@ -167,7 +168,12 @@ function launchAgent(nodePath) {
|
|
|
167
168
|
export async function installService() {
|
|
168
169
|
await mkdir(dirname(launchAgentFile), { recursive: true });
|
|
169
170
|
const { legacyPlist, secretStatus } = await prepareInstallSecret();
|
|
170
|
-
const cursorPatches = cursorIsRunning()
|
|
171
|
+
const cursorPatches = cursorIsRunning()
|
|
172
|
+
? null
|
|
173
|
+
: await Promise.all(cursorModelMetadataFiles.map(async (file) => ({
|
|
174
|
+
file,
|
|
175
|
+
...await ensureCursorModelMetadataPatched({ file }),
|
|
176
|
+
})));
|
|
171
177
|
await copyPackage();
|
|
172
178
|
await installCliLink();
|
|
173
179
|
|
package/src/paths.mjs
CHANGED
|
@@ -17,6 +17,7 @@ export const cursorDatabaseFile = join(homedir(), "Library", "Application Suppor
|
|
|
17
17
|
export const cursorAppOutDirectory = "/Applications/Cursor.app/Contents/Resources/app/out";
|
|
18
18
|
export const cursorWorkbenchFile = join(cursorAppOutDirectory, "vs", "workbench", "workbench.desktop.main.js");
|
|
19
19
|
export const cursorGlassWorkbenchFile = join(cursorAppOutDirectory, "vs", "workbench", "workbench.glass.main.js");
|
|
20
|
+
export const cursorModelMetadataFiles = [cursorWorkbenchFile, cursorGlassWorkbenchFile];
|
|
20
21
|
export const cursorLocalRuntimeFiles = [
|
|
21
22
|
"/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-local-agent-runtime/dist/main.js",
|
|
22
23
|
"/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-agent-exec/dist/main.js",
|
|
@@ -39,6 +40,5 @@ export const opencodexConfigFile = join(homedir(), ".opencodex", "config.json");
|
|
|
39
40
|
export const opencodexServiceTokenFile = join(homedir(), ".opencodex", "service-api-token");
|
|
40
41
|
export const gatewayPort = Number(process.env.OCX_CURSOR_PORT || "10101");
|
|
41
42
|
export const gatewayHost = process.env.OCX_CURSOR_HOST || "127.0.0.1";
|
|
42
|
-
export const cursorLocalBaseUrl = `http://127.0.0.1:${gatewayPort}/v1`;
|
|
43
43
|
export const cursorOpenAIBaseUrl = process.env.OCX_CURSOR_BASE_URL || "";
|
|
44
44
|
export const managedPrefix = "opencodex/";
|
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 { startCursorModelMetadataPatchMonitor } from "./cursor-patch.mjs";
|
|
5
5
|
import {
|
|
6
6
|
cursorIsRunning,
|
|
7
7
|
readPendingCatalog,
|
|
@@ -59,7 +59,7 @@ export async function runService(options = {}) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
const server = startGateway({ secret, getCatalog: () => catalog, host: options.host, port: options.port });
|
|
62
|
-
const patchMonitor =
|
|
62
|
+
const patchMonitor = startCursorModelMetadataPatchMonitor({
|
|
63
63
|
intervalMs: options.cursorPatchIntervalMs,
|
|
64
64
|
shouldPatch: () => !cursorIsRunning(),
|
|
65
65
|
onResult: (result) => {
|
package/src/setup.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export function normalizeBaseUrl(value) {
|
|
2
2
|
if (!value) {
|
|
3
|
-
throw new Error("Enter an endpoint
|
|
3
|
+
throw new Error("Enter an HTTPS endpoint or pass --base-url https://cursor-api.example.com/v1");
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
const trimmed = value.trim();
|