@vidit19sharma/opencode-cursor-auth 0.3.1 → 0.3.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
4
4
 
5
+ ## 0.3.2 — 2026-10-16
6
+
7
+ ### Added
8
+
9
+ - `npx @vidit19sharma/opencode-cursor-auth setup` — adds the plugin to opencode config idempotently (`--dry-run`, `--config`).
10
+
11
+ ### Fixed
12
+
13
+ - The first request before `opencode auth login` hung in opencode's connect loop: without a `cursor` auth entry opencode skips the plugin's auth loader and dials the dummy `http://cursor.local/v1`. The plugin now seeds its auth entry (only when none exists), so the loader runs and returns a clear 401 telling you to run `opencode auth login`.
14
+
15
+ ### Changed
16
+
17
+ - Docs no longer claim the Cursor CLI (`cursor-agent`) is required; `@cursor/sdk` talks to Cursor's backend directly.
18
+
5
19
  ## 0.3.1 — 2026-10-16
6
20
 
7
21
  ### Fixed
package/README.md CHANGED
@@ -23,18 +23,22 @@ The plugin bridges opencode's OpenAI-style chat requests to the official [`@curs
23
23
  ## Requirements
24
24
 
25
25
  - Node >= 22.13 (opencode runs plugins on Bun, which also works)
26
- - [`cursor-agent` CLI](https://cursor.com/docs/cli) installed (`curl https://cursor.com/install -fsS | bash`) — the SDK's local executor uses it
27
26
  - A Cursor subscription
28
27
 
29
28
  ## Install
30
29
 
31
- For AI agents — paste this into any LLM agent:
30
+ All three methods end with `opencode auth login` (next section).
32
31
 
33
- ```
34
- Install the @vidit19sharma/opencode-cursor-auth plugin and configure it by following: https://raw.githubusercontent.com/vidit19sharma/opencode-cursor-auth/main/installation.md
32
+ ### 1. One command (npx)
33
+
34
+ ```sh
35
+ npx @vidit19sharma/opencode-cursor-auth setup
35
36
  ```
36
37
 
37
- See [installation.md](installation.md) for the step-by-step agent instructions.
38
+ Adds the plugin to the `plugin` array of your opencode config (`~/.config/opencode/opencode.json[c]`, or creates `opencode.json`); running it again is a no-op.
39
+ `--dry-run` shows the change without writing; `--config <path>` targets a specific file. A `.jsonc` file with comments is never rewritten — you get the one-line manual edit instead.
40
+
41
+ ### 2. Manual
38
42
 
39
43
  Add the plugin to `~/.config/opencode/opencode.json`:
40
44
 
@@ -44,7 +48,17 @@ Add the plugin to `~/.config/opencode/opencode.json`:
44
48
  }
45
49
  ```
46
50
 
47
- The plugin auto-registers a `cursor` provider — no manual provider config needed.
51
+ The plugin auto-registers the `cursor` provider — no manual provider config needed.
52
+
53
+ ### 3. Let an AI agent do it
54
+
55
+ Paste this into any LLM agent:
56
+
57
+ ```
58
+ Install the @vidit19sharma/opencode-cursor-auth plugin and configure it by following: https://raw.githubusercontent.com/vidit19sharma/opencode-cursor-auth/main/installation.md
59
+ ```
60
+
61
+ See [installation.md](installation.md) for the step-by-step agent instructions.
48
62
 
49
63
  ## Authenticate
50
64
 
@@ -115,7 +129,7 @@ The models cache is refetched once on upgrade.
115
129
  ## How it works
116
130
 
117
131
  ```
118
- opencode ──(OpenAI chat JSON)──> plugin fetch() ──> @cursor/sdk Agent ──> cursor-agent ──> Cursor backend
132
+ opencode ──(OpenAI chat JSON)──> plugin fetch() ──> @cursor/sdk Agent ──> Cursor backend
119
133
  <─(SSE stream)──────── bridge <──(typed deltas: text / thinking / tool calls)
120
134
  ```
121
135
 
@@ -147,6 +161,8 @@ bun install
147
161
  bun run build # tsc → dist/
148
162
  bun run test # end-to-end bridge tests (requires Cursor login, uses real Cursor usage)
149
163
  bun run spike # raw @cursor/sdk validation
164
+ bun run test:cli # exercises the npx setup command against temp config dirs (7 cases)
165
+ bun run smoke # packs the plugin and loads it in a pristine HOME (startup, auth login, first request before login)
150
166
  ```
151
167
 
152
168
  The test suite runs 13 scenarios: plain streaming, a full tool loop on one held run, in-memory and disk-persisted session resume, AGENTS.md system-prompt delivery, mid-stream abort, error statuses, session scoping, context limits and key checks, model variants, the concurrency cap, retry semantics, and blocked models.
@@ -154,7 +170,6 @@ The test suite runs 13 scenarios: plain streaming, a full tool loop on one held
154
170
  ## Known limitations
155
171
 
156
172
  - `systemPrompt` as a true harness replacement is feature-gated server-side by Cursor; system text is applied via `AGENTS.md` as a project rule (merged under Cursor's harness prompt).
157
- - The SDK spawns a `cursor-agent` worker per turn (~1–2 s startup on resume).
158
173
  - Cursor's catalog has no entitlement info, so models your plan or team blocks only disappear after the first 403.
159
174
  - `limit.context` is per model, not per variant: picking a smaller-context variant (e.g. gpt-5.5 `272k`) keeps the base model's 1M limit in opencode's context indicator and auto-compaction.
160
175
  - A run parked waiting for a tool result keeps its slot. Cursor-model sessions that spawn Cursor-model sub-agents nested deeper than `OPENCODE_CURSOR_MAX_ACTIVE` will stall; raise the limit if you fan out.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import process from "node:process";
6
+ const PKG = "@vidit19sharma/opencode-cursor-auth";
7
+ const ENTRY = `${PKG}@latest`;
8
+ const HELP = `Usage: npx ${PKG} [setup] [--dry-run] [--config <path>]
9
+
10
+ Adds "${ENTRY}" to the "plugin" array of your opencode config.
11
+ Idempotent: does nothing if any ${PKG} entry is already present.
12
+
13
+ --dry-run print what would change, write nothing
14
+ --config <path> edit this file instead of the auto-detected one
15
+ --help show this help
16
+
17
+ Config lookup: --config, $OPENCODE_CONFIG, then opencode.jsonc / opencode.json in
18
+ $OPENCODE_CONFIG_DIR, $XDG_CONFIG_HOME/opencode or ~/.config/opencode.
19
+
20
+ Exit codes: 0 ok or already installed, 2 cannot edit safely (manual edit printed), 1 error.`;
21
+ const NEXT = `Next steps:
22
+ 1. Restart opencode
23
+ 2. opencode auth login -> Cursor -> Log in with Cursor (browser)
24
+ 3. opencode models | grep cursor/`;
25
+ const MANUAL = `Add this to the top-level "plugin" array of your opencode config (create it if missing):
26
+ "plugin": ["${ENTRY}"]`;
27
+ // Strips // and /* */ comments and trailing commas outside double-quoted strings; does not handle single-quoted strings or other JSON5 syntax.
28
+ const stripJsonc = (s) => s
29
+ .replace(/("(?:\\.|[^"\\])*")|\/\/[^\n]*|\/\*[\s\S]*?\*\//g, (_m, str) => str ?? "")
30
+ .replace(/("(?:\\.|[^"\\])*")|,(\s*[}\]])/g, (_m, str, close) => str ?? close);
31
+ function tryParse(s) {
32
+ try {
33
+ return JSON.parse(s);
34
+ }
35
+ catch {
36
+ return undefined;
37
+ }
38
+ }
39
+ function main(argv) {
40
+ let dryRun = false;
41
+ let explicit;
42
+ for (let i = 0; i < argv.length; i++) {
43
+ const a = argv[i];
44
+ if (a === "--help" || a === "-h")
45
+ return console.log(HELP), 0;
46
+ else if (a === "--dry-run")
47
+ dryRun = true;
48
+ else if (a === "--config")
49
+ explicit = argv[++i];
50
+ else if (a !== "setup")
51
+ return console.error(`Unknown argument: ${a}\n\n${HELP}`), 1;
52
+ }
53
+ if (argv.includes("--config") && !explicit)
54
+ return console.error("--config needs a path"), 1;
55
+ const env = process.env;
56
+ const dir = env.OPENCODE_CONFIG_DIR || join(env.XDG_CONFIG_HOME || join(homedir(), ".config"), "opencode");
57
+ const file = resolve(explicit ||
58
+ env.OPENCODE_CONFIG ||
59
+ ["opencode.jsonc", "opencode.json"].map((f) => join(dir, f)).find((f) => existsSync(f)) ||
60
+ join(dir, "opencode.json"));
61
+ const exists = existsSync(file);
62
+ const original = exists ? readFileSync(file, "utf8") : "";
63
+ const strict = exists ? tryParse(original) : { $schema: "https://opencode.ai/config.json" };
64
+ const config = (strict ?? tryParse(stripJsonc(original)));
65
+ console.log(`Config: ${file}${exists ? "" : " (will be created)"}`);
66
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
67
+ console.error(`Cannot parse ${file}; not editing it.\n${MANUAL}`);
68
+ return 2;
69
+ }
70
+ const leftovers = [];
71
+ const provider = config.provider;
72
+ if (provider && typeof provider === "object" && "cursor-acp" in provider)
73
+ leftovers.push(` - remove the "cursor-acp" block under "provider" in ${file}`);
74
+ const acp = join(dir, "plugin", "cursor-acp.js");
75
+ if (existsSync(acp))
76
+ leftovers.push(` - rm ${JSON.stringify(acp)}`);
77
+ if (leftovers.length)
78
+ console.log(`Notice: old open-cursor setup found (nothing was deleted). To remove it:\n${leftovers.join("\n")}\n - npm uninstall -g @rama_nigg/open-cursor`);
79
+ const plugins = config.plugin ?? [];
80
+ if (!Array.isArray(plugins)) {
81
+ console.error(`"plugin" in ${file} is not an array; not editing it.\n${MANUAL}`);
82
+ return 2;
83
+ }
84
+ if (plugins.some((p) => typeof p === "string" && p.startsWith(PKG))) {
85
+ console.log(`Already installed: ${PKG} is in "plugin". File left untouched.\n\n${NEXT}`);
86
+ return 0;
87
+ }
88
+ if (strict === undefined) {
89
+ console.error(`${file} has comments or trailing commas; not rewriting it (that would drop them).\n${MANUAL}`);
90
+ return 2;
91
+ }
92
+ config.plugin = [...plugins, ENTRY];
93
+ const next = JSON.stringify(config, null, 2) + "\n";
94
+ if (dryRun) {
95
+ console.log(`Dry run: would ${exists ? "update" : "create"} ${file} with:\n${next}`);
96
+ return 0;
97
+ }
98
+ mkdirSync(dirname(file), { recursive: true });
99
+ if (exists)
100
+ writeFileSync(`${file}.bak-cursor-auth`, original);
101
+ writeFileSync(`${file}.tmp`, next);
102
+ renameSync(`${file}.tmp`, file);
103
+ console.log(`Added "${ENTRY}" to "plugin"${exists ? ` (backup: ${file}.bak-cursor-auth)` : ""}.\n\n${NEXT}`);
104
+ return 0;
105
+ }
106
+ try {
107
+ process.exitCode = main(process.argv.slice(2));
108
+ }
109
+ catch (e) {
110
+ console.error(`Unexpected error: ${e instanceof Error ? e.message : e}`);
111
+ process.exitCode = 1;
112
+ }
package/dist/plugin.js CHANGED
@@ -1,6 +1,9 @@
1
1
  import { Cursor } from "@cursor/sdk";
2
2
  import { errorResponse, handleChatCompletion } from "./bridge.js";
3
3
  import { currentModels } from "./models.js";
4
+ import { readFile } from "node:fs/promises";
5
+ import { homedir } from "node:os";
6
+ import { join } from "node:path";
4
7
  export { parseContext } from "./models.js";
5
8
  const BASE_URL = "http://cursor.local/v1";
6
9
  // Cursor publishes no output limit and the bridge never passes max_tokens to Cursor, so output is only an accounting reserve for opencode.
@@ -67,7 +70,17 @@ async function cursorFetch(input, init) {
67
70
  }
68
71
  return Response.json({ error: { message: `unsupported endpoint: ${url}` } }, { status: 404 });
69
72
  }
70
- export const CursorAuthPlugin = async () => {
73
+ async function seedAuthEntry(client) {
74
+ if (!client?.auth?.set)
75
+ return;
76
+ const file = join(process.env.XDG_DATA_HOME || join(homedir(), ".local", "share"), "opencode", "auth.json");
77
+ const entries = await readFile(file, "utf8").then(JSON.parse, (err) => (err.code === "ENOENT" ? {} : undefined));
78
+ if (!entries || typeof entries !== "object" || "cursor" in entries)
79
+ return;
80
+ await client.auth.set({ path: { id: "cursor" }, body: { type: "api", key: "cursor-sdk-store" } });
81
+ }
82
+ export const CursorAuthPlugin = async (input) => {
83
+ seedAuthEntry(input?.client).catch(() => { });
71
84
  return {
72
85
  config: async (config) => {
73
86
  const providers = (config.provider ??= {});
package/installation.md CHANGED
@@ -20,23 +20,9 @@ You should see `1.19` or newer. If older or missing, ask the user to install or
20
20
  node --version
21
21
  ```
22
22
 
23
- You should see `v22.13` or newer. opencode installs plugins with Bun, but `@cursor/sdk` spawns the `cursor-agent` binary and requires Node >= 22.13. If older, ask the user to upgrade Node.
23
+ You should see `v22.13` or newer. `@cursor/sdk` requires Node >= 22.13. If older, ask the user to upgrade Node.
24
24
 
25
- ### Check the Cursor CLI
26
-
27
- ```bash
28
- cursor-agent --version
29
- ```
30
-
31
- If this prints a version, continue. If it fails with `command not found`, install it:
32
-
33
- ```bash
34
- curl https://cursor.com/install -fsS | bash
35
- ```
36
-
37
- Then open a new shell, or add `~/.local/bin` to `PATH`, and re-run `cursor-agent --version`.
38
-
39
- A Cursor subscription (Pro, Business, or team) is required. `cursor-agent login` is not required (the plugin has its own login) but is harmless.
25
+ A Cursor subscription (Pro, Business, or team) is required.
40
26
 
41
27
  ## Installation
42
28
 
@@ -52,7 +38,15 @@ Skip this step if none of these exist.
52
38
 
53
39
  ### Step 2: Add the plugin to the opencode config
54
40
 
55
- The config file is `~/.config/opencode/opencode.json`. Some users have `~/.config/opencode/opencode.jsonc` instead; edit whichever exists.
41
+ **Preferred, if you can run shell commands:**
42
+
43
+ ```bash
44
+ npx @vidit19sharma/opencode-cursor-auth setup
45
+ ```
46
+
47
+ It adds the plugin idempotently (exit 0 = added or already present) and lists any open-cursor leftovers from Step 1 without deleting them. Exit 2 means it could not edit the file safely (e.g. a `.jsonc` with comments); it prints the exact edit, which you then make by hand as described below. Use `--dry-run` to preview.
48
+
49
+ **Fallback: edit the file by hand.** The config file is `~/.config/opencode/opencode.json`. Some users have `~/.config/opencode/opencode.jsonc` instead; edit whichever exists.
56
50
 
57
51
  If neither exists, create `~/.config/opencode/opencode.json`:
58
52
 
@@ -123,9 +117,8 @@ The plugin is installed. Every model the user's Cursor plan includes is availabl
123
117
  - **401 `Cursor SDK key missing/expired`**: run `opencode auth login`.
124
118
  - **403 `permission_error` "Model Blocked"**: a team admin disabled that model for the account. It is hidden from `opencode models` from then on; delete `~/.cache/opencode-cursor-auth/models.json` to re-check.
125
119
  - **400 `Cannot use this model`**: the model id is not in Cursor's catalog for this account.
126
- - **`cursor-agent: command not found` at request time**: install the Cursor CLI (see Prerequisites) and make sure it is on `PATH` for the shell that launches opencode.
120
+ - **Request hangs with no error**: `opencode auth login` was never run for Cursor; run it (0.3.2+ seeds the entry automatically so you get a 401 message instead).
127
121
  - **Model list empty or only `cursor/auto`**: the catalog fetch failed (offline or not logged in). Fix auth, delete `~/.cache/opencode-cursor-auth/models.json`, restart opencode.
128
- - **Stray `cursor-agent` processes**: check with `pgrep -fl cursor-agent`. A single `worker-server` is the SDK's idle singleton and is normal.
129
122
 
130
123
  ### Paths
131
124
 
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@vidit19sharma/opencode-cursor-auth",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "OpenCode plugin that uses your Cursor subscription via the official @cursor/sdk \u2014 no proxy server, no separate login.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "opencode-cursor-auth": "dist/cli.js"
10
+ },
8
11
  "files": [
9
12
  "dist",
10
13
  "README.md",
@@ -31,10 +34,12 @@
31
34
  "auth"
32
35
  ],
33
36
  "scripts": {
34
- "build": "tsc",
37
+ "build": "tsc && chmod +x dist/cli.js",
35
38
  "test": "bun spike/e2e-bridge.ts",
36
39
  "spike": "bun spike/validate-sdk.ts",
37
- "prepublishOnly": "tsc"
40
+ "smoke": "bash scripts/fresh-home-smoke.sh",
41
+ "prepublishOnly": "npm run build",
42
+ "test:cli": "npm run build && bash spike/cli-setup.test.sh"
38
43
  },
39
44
  "dependencies": {
40
45
  "@cursor/sdk": "^1.0.36"