cursor-keys 1.0.0 → 1.2.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 CHANGED
@@ -1,27 +1,42 @@
1
- # cursor-keys
1
+ <div align="center">
2
2
 
3
- > Per-project Cursor profiles so every window automatically uses the right BYOK API key. Two windows, two projects, two keys, zero switching.
3
+ ```
4
+ ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██╗ ██╗███████╗██╗ ██╗███████╗
5
+ ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ ██║ ██╔╝██╔════╝╚██╗ ██╔╝██╔════╝
6
+ ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ █████╔╝ █████╗ ╚████╔╝ ███████╗
7
+ ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ ██╔═██╗ ██╔══╝ ╚██╔╝ ╚════██║
8
+ ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ ██║ ██╗███████╗ ██║ ███████║
9
+ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚══════╝
10
+ ```
4
11
 
5
- Cursor stores your OpenAI-compatible API key (Bonzai, LiteLLM, AI Gateway, ...) **once per profile**, not per repo. If you bill five client projects, you either babysit Settings all day or bill the wrong project. `cursor-keys` gives each project its own isolated profile and makes opening, switching, and checking keys automatic.
12
+ **Per-project Cursor profiles so every window uses the right BYOK API key.**
6
13
 
7
- ```
8
- ╔═╗┬ ┬┬─┐┌─┐┌─┐┬─┐ ┬┌─┌─┐┬ ┬┌─┐
9
- │ │ │├┬┘└─┐│ │├┬┘ ├┴┐├┤ └┬┘└─┐
10
- └─┘└─┘┴└─└─┘└─┘┴└─ ┴ ┴└─┘ ┴ └─┘ per-project Cursor API keys
14
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
15
+ [![Node](https://img.shields.io/badge/node-%E2%89%A518-5FA04E.svg)](https://nodejs.org)
16
+ [![Runtime dependencies](https://img.shields.io/badge/runtime%20deps-0-brightgreen.svg)](#development)
17
+
18
+ [Website](https://l-ubu.github.io/cursor-keys/) · [Install](#install) · [Commands](#commands) · [Config](#config)
19
+
20
+ </div>
21
+
22
+ ---
11
23
 
24
+ Cursor stores your OpenAI-compatible API key (LiteLLM, Vercel AI Gateway, corporate proxy, ...) **once per profile**, not per repo. If you bill five client projects, you either babysit Settings all day or bill the wrong project. `cursor-keys` gives each project its own isolated profile and makes opening, switching, and checking keys automatic.
25
+
26
+ ```
12
27
  $ cursor-keys status
13
28
 
14
- Project | Color | Windows | Key | Repo
15
- --------------------+----------+---------+-----+-------------------------
16
- famework-platform-1 | #339af0 | 1 | yes | ~/Projects/famework-platform-1
17
- jlr_mss | #4f8cff | 0 | no | ~/Projects/jlr_mss
18
- nokia | #845ef7 | 1 | yes | ~/Projects/nokia-main-cs
29
+ Project | Color | Windows | Key | Repo
30
+ ----------+----------+---------+-----+------------------
31
+ acme | #4f8cff | 1 | yes | ~/Projects/acme
32
+ client-a | #51cf66 | 0 | no | ~/Projects/client-a
33
+ client-b | #845ef7 | 1 | yes | ~/Projects/client-b
19
34
 
20
- $ cursor-keys doctor nokia
35
+ $ cursor-keys doctor acme
21
36
 
22
- ✓ OpenAI/Bonzai API key row present
37
+ ✓ OpenAI API key row present
23
38
  ✓ Explore subagent overrides present
24
- ✓ Base URL (connect.iobonzai.com/cursor) in profile state
39
+ ✓ Base URL (gateway.example.com/v1) in profile state
25
40
  ```
26
41
 
27
42
  ## Install
@@ -36,8 +51,8 @@ Requires Node ≥18, the Cursor CLI (`Shell Command: Install 'cursor' in PATH`),
36
51
  ## Quick start
37
52
 
38
53
  ```bash
39
- # one-time, only if your gateway is not the Bonzai default
40
- cursor-keys config baseUrl https://your-gateway.example.com/v1
54
+ # one-time: point at your OpenAI-compatible gateway
55
+ cursor-keys config baseUrl https://gateway.example.com/v1
41
56
 
42
57
  # one-time per project
43
58
  cursor-keys add acme --path ~/Projects/acme # register + create profile
@@ -64,7 +79,7 @@ One window, one profile, one key. Each project gets an isolated `--user-data-dir
64
79
 
65
80
  Settings flow one way, main to profiles: `sync` re-copies your main `settings.json` into every profile and re-applies that profile's title bar color on top. `open` does this automatically before every launch, and open windows hot-reload the file, so tweaks in your main Cursor show up everywhere. Changes made inside a profile window are overwritten on the next sync.
66
81
 
67
- `clone` copies your main Cursor state (SQLite backup) so profiles open signed-in with your theme and model config. `set-key` encrypts your project key with Cursor's own safeStorage (Chromium OSCrypt, key from the "Cursor Safe Storage" Keychain item) and writes it into the profile's secret store, round-trip verified. macOS asks for Keychain permission once; click Always Allow.
82
+ `clone` copies your main Cursor state (SQLite backup) so profiles open signed-in with your theme and model config. `set-key` encrypts your project key with Cursor's own safeStorage (Chromium OSCrypt, key from the "Cursor Safe Storage" macOS Keychain item) and writes it into the profile's secret store, round-trip verified. macOS asks for Keychain permission once; click Always Allow.
68
83
 
69
84
  ## "Which key am I on?" in three layers
70
85
 
@@ -83,7 +98,7 @@ Settings flow one way, main to profiles: `sync` re-copies your main `settings.js
83
98
  | `open [name]` | Launch Cursor with the profile (auto-detect from cwd) |
84
99
  | `status` | Projects, colors, running windows, key configured |
85
100
  | `current` | Mapping for the current directory |
86
- | `config [baseUrl [URL]]` | Show or set the gateway base URL |
101
+ | `config [get <key> · baseUrl [URL]]` | Show summary, print one value, or set the gateway base URL |
87
102
  | `sync [name]` | Re-copy main Cursor settings into profiles |
88
103
  | `doctor [name]` | Health checks: key row, base URL, subagent overrides |
89
104
  | `wrap [--install]` | zsh function so plain `cursor <path>` auto-picks the profile |
@@ -98,14 +113,21 @@ Settings flow one way, main to profiles: `sync` re-copies your main `settings.js
98
113
  ```json
99
114
  {
100
115
  "profilesRoot": "~/.cursor-profiles",
101
- "baseUrl": "https://connect.iobonzai.com/cursor",
116
+ "baseUrl": "https://gateway.example.com/v1",
102
117
  "projects": {
103
118
  "acme": { "path": "~/Projects/acme", "color": "#4f8cff" }
104
119
  }
105
120
  }
106
121
  ```
107
122
 
108
- `baseUrl` is your gateway's OpenAI-compatible endpoint (Bonzai default; point it at LiteLLM, Vercel AI Gateway, ...). `doctor` verifies it per profile.
123
+ `baseUrl` is your gateway's OpenAI-compatible endpoint. `doctor` verifies it per profile.
124
+
125
+ Read a single value for scripts (stdout only, exit 1 on unknown keys):
126
+
127
+ ```bash
128
+ cursor-keys config get baseUrl
129
+ cursor-keys config get profilesRoot
130
+ ```
109
131
 
110
132
  ## Security
111
133
 
@@ -1,12 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { runCommand } from "../src/commands.mjs";
4
- import { bold, cyan, dim, reset } from "../src/ansi.mjs";
4
+ import { bold, dim, reset, yellow } from "../src/ansi.mjs";
5
5
 
6
6
  const BANNER = `
7
- ${cyan}╔═╗┬ ┬┬─┐┌─┐┌─┐┬─┐ ┬┌─┌─┐┬ ┬┌─┐
8
- ${cyan}│ │ │├┬┘└─┐│ │├┬┘ ├┴┐├┤ └┬┘└─┐
9
- ${cyan}└─┘└─┘┴└─└─┘└─┘┴└─ ┴ ┴└─┘ ┴ └─┘${dim} per-project Cursor API keys
7
+ ${yellow} ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██╗ ██╗███████╗██╗ ██╗███████╗
8
+ ${yellow}██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ ██║ ██╔╝██╔════╝╚██╗ ██╔╝██╔════╝
9
+ ${yellow}██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ █████╔╝ █████╗ ╚████╔╝ ███████╗
10
+ ${yellow}██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ ██╔═██╗ ██╔══╝ ╚██╔╝ ╚════██║
11
+ ${yellow}╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ ██║ ██╗███████╗ ██║ ███████║
12
+ ${yellow} ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚══════╝${reset}${dim} per-project Cursor API keys
10
13
  `;
11
14
 
12
15
  function printHelp() {
@@ -17,6 +20,12 @@ function printHelp() {
17
20
  " init [--root DIR] [--all] Scan for git repos and register projects",
18
21
  );
19
22
  console.log(" add <name> [options] Create profile for a project");
23
+ console.log(
24
+ " clone <name> [--from main] Copy sign-in + settings from a donor profile",
25
+ );
26
+ console.log(
27
+ " set-key <name> Inject the project API key (encrypted)",
28
+ );
20
29
  console.log(
21
30
  " open [name] Open Cursor with this project's profile",
22
31
  );
@@ -25,7 +34,7 @@ function printHelp() {
25
34
  );
26
35
  console.log(" current Show mapping for cwd");
27
36
  console.log(
28
- " config [baseUrl [URL]] Show or set config (gateway base URL)",
37
+ " config [get <key>|baseUrl [URL]] Show, read, or set config values",
29
38
  );
30
39
  console.log(
31
40
  " sync [name] Re-copy main Cursor settings into profiles",
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 L-ubu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursor-keys",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Per-project Cursor profiles so each window uses the correct BYOK API key. Zero dependencies.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,7 +19,6 @@
19
19
  "cursor",
20
20
  "cli",
21
21
  "api-key",
22
- "bonzai",
23
22
  "byok",
24
23
  "developer-tools",
25
24
  "productivity"
package/src/commands.mjs CHANGED
@@ -8,11 +8,14 @@ import {
8
8
  baseUrlFragment,
9
9
  collapseHome,
10
10
  CONFIG_FILE,
11
+ CONFIG_GETTABLE_KEYS,
11
12
  expandHome,
12
13
  findProjectByPath,
14
+ getConfigValue,
13
15
  getProject,
14
16
  loadConfig,
15
17
  pickColorForName,
18
+ pickUnusedColor,
16
19
  saveConfig,
17
20
  profileStateDb,
18
21
  } from "./config.mjs";
@@ -32,8 +35,10 @@ import {
32
35
  findCursorCli,
33
36
  listRunningCursorProfiles,
34
37
  mainCursorProfileRoot,
38
+ mainCursorUserDir,
35
39
  stateDbHasKey,
36
40
  stateDbValueContains,
41
+ stateDbValuesMatch,
37
42
  hasSqlite3,
38
43
  } from "./detect.mjs";
39
44
  import {
@@ -174,12 +179,30 @@ function ask(question) {
174
179
  async function cmdConfig(args) {
175
180
  const { flags, positional } = parseFlags(args);
176
181
  if (flags.help) {
177
- console.log("Usage: cursor-keys config [baseUrl [URL]]");
182
+ console.log(
183
+ "Usage: cursor-keys config Show config summary",
184
+ );
185
+ console.log(
186
+ " cursor-keys config get <key> Print one value (baseUrl, profilesRoot)",
187
+ );
188
+ console.log(
189
+ " cursor-keys config baseUrl <URL> Set gateway base URL",
190
+ );
178
191
  return;
179
192
  }
180
193
  const config = loadConfig();
181
194
  const [key, value] = positional;
182
195
 
196
+ if (key === "get") {
197
+ if (!value) {
198
+ throw new Error(
199
+ `Missing config key. Usage: cursor-keys config get <${CONFIG_GETTABLE_KEYS.join("|")}>`,
200
+ );
201
+ }
202
+ console.log(getConfigValue(config, value));
203
+ return;
204
+ }
205
+
183
206
  if (!key) {
184
207
  const count = Object.keys(config.projects).length;
185
208
  console.log(
@@ -292,10 +315,17 @@ async function cmdInit(args) {
292
315
  }
293
316
  saveConfig(config);
294
317
  console.log(
295
- `\nNext: cursor-keys add <name> --clone main (creates profile + copies Bonzai settings)`,
318
+ `\nNext: cursor-keys add <name> --clone main (creates profile + copies main Cursor settings)`,
296
319
  );
297
320
  }
298
321
 
322
+ function shouldShowFirstRunChecklist(profileDir) {
323
+ if (!needsFirstRunChecklist(profileDir)) return false;
324
+ const db = profileStateDb(profileDir);
325
+ // A cloned profile already carries a key row: setup is effectively done.
326
+ return !(fs.existsSync(db) && stateDbHasKey(db, SECRET_OPENAI_KEY));
327
+ }
328
+
299
329
  async function cmdAdd(args) {
300
330
  const { flags, positional } = parseFlags(args);
301
331
  if (flags.help) {
@@ -319,8 +349,13 @@ async function cmdAdd(args) {
319
349
  throw new Error(`Repo path does not exist: ${repoPath}`);
320
350
  }
321
351
 
352
+ const usedColors = Object.entries(config.projects)
353
+ .filter(([other]) => other !== name)
354
+ .map(([, entry]) => entry.color);
322
355
  const color =
323
- flags.color || config.projects[name]?.color || pickColorForName(name);
356
+ flags.color ||
357
+ config.projects[name]?.color ||
358
+ pickUnusedColor(name, usedColors);
324
359
  config.projects[name] = { path: collapseHome(repoPath), color };
325
360
  saveConfig(config);
326
361
 
@@ -348,7 +383,7 @@ async function cmdAdd(args) {
348
383
  console.log(ok(`Profile ready: ${collapseHome(project.profileDir)}`));
349
384
  console.log(` Repo: ${collapseHome(project.path)}`);
350
385
  console.log(` Color: ${color} ${blockSwatch(color)}`);
351
- if (needsFirstRunChecklist(project.profileDir)) {
386
+ if (shouldShowFirstRunChecklist(project.profileDir)) {
352
387
  printFirstRunChecklist(name, config.baseUrl);
353
388
  }
354
389
  }
@@ -405,7 +440,7 @@ async function cmdOpen(args) {
405
440
  console.log(` ${dim}${collapseHome(project.path)}${reset}`);
406
441
  console.log(` Profile ${blockSwatch(project.color)} ${project.color}`);
407
442
 
408
- if (needsFirstRunChecklist(project.profileDir)) {
443
+ if (shouldShowFirstRunChecklist(project.profileDir)) {
409
444
  printFirstRunChecklist(name, config.baseUrl);
410
445
  markSetupDone(project.profileDir);
411
446
  }
@@ -499,9 +534,22 @@ async function cmdDoctor(args) {
499
534
  }
500
535
  const db = profileStateDb(p.profileDir);
501
536
  if (fs.existsSync(db)) {
537
+ const hasKey = stateDbHasKey(db, SECRET_OPENAI_KEY);
502
538
  console.log(
503
- ` ${stateDbHasKey(db, SECRET_OPENAI_KEY) ? ok("✓") : warn("✗")} OpenAI/Bonzai API key row present`,
539
+ ` ${hasKey ? ok("✓") : warn("✗")} OpenAI API key row present`,
504
540
  );
541
+ if (hasKey) {
542
+ const mainDb = path.join(
543
+ mainCursorUserDir(),
544
+ "globalStorage",
545
+ "state.vscdb",
546
+ );
547
+ if (stateDbValuesMatch(db, mainDb, SECRET_OPENAI_KEY)) {
548
+ console.log(
549
+ ` ${warn("!")} Key is identical to the main profile (inherited via clone); run: cursor-keys set-key ${name}`,
550
+ );
551
+ }
552
+ }
505
553
  console.log(
506
554
  ` ${stateDbHasKey(db, SUBAGENT_OVERRIDES_KEY) ? ok("✓") : warn("✗")} Explore subagent overrides present`,
507
555
  );
@@ -747,7 +795,19 @@ async function cmdSetKey(args) {
747
795
  throw new Error(`Close the "${name}" Cursor window first, then retry.`);
748
796
  }
749
797
 
750
- const key = flags.key || (await promptHidden(`API key for ${name}: `));
798
+ let key = flags.key;
799
+ if (!key) {
800
+ console.log(
801
+ `\nPaste the API key for ${bold}${name}${reset} below, then press Enter.`,
802
+ );
803
+ console.log(
804
+ `${dim}Input is hidden: nothing echoes while you paste or type.${reset}`,
805
+ );
806
+ key = await promptHidden("API key: ");
807
+ }
808
+ if (!key) {
809
+ throw new Error("No key entered. Aborted, nothing was written.");
810
+ }
751
811
  const result = setProfileApiKey(db, key);
752
812
  console.log(
753
813
  ok(`Key set for ${name}`) +
package/src/config.mjs CHANGED
@@ -98,25 +98,49 @@ export function baseUrlFragment(config) {
98
98
  return String(config.baseUrl || "").replace(/^https?:\/\//, "");
99
99
  }
100
100
 
101
+ export const CONFIG_GETTABLE_KEYS = ["baseUrl", "profilesRoot"];
102
+
103
+ /**
104
+ * @param {ReturnType<typeof loadConfig>} config
105
+ * @param {string} key
106
+ */
107
+ export function getConfigValue(config, key) {
108
+ if (!CONFIG_GETTABLE_KEYS.includes(key)) {
109
+ throw new Error(
110
+ `Unknown config key: ${key}. Known keys: ${CONFIG_GETTABLE_KEYS.join(", ")}`,
111
+ );
112
+ }
113
+ return config[key];
114
+ }
115
+
116
+ const COLOR_PALETTE = [
117
+ "#4f8cff",
118
+ "#ff6b6b",
119
+ "#51cf66",
120
+ "#ffd43b",
121
+ "#845ef7",
122
+ "#20c997",
123
+ "#ff922b",
124
+ "#e64980",
125
+ "#339af0",
126
+ "#94d82d",
127
+ ];
128
+
101
129
  /** Deterministic palette color from project name */
102
130
  export function pickColorForName(name) {
103
- const palette = [
104
- "#4f8cff",
105
- "#ff6b6b",
106
- "#51cf66",
107
- "#ffd43b",
108
- "#845ef7",
109
- "#20c997",
110
- "#ff922b",
111
- "#e64980",
112
- "#339af0",
113
- "#94d82d",
114
- ];
115
131
  let hash = 0;
116
132
  for (let i = 0; i < name.length; i++) {
117
133
  hash = (hash * 31 + name.charCodeAt(i)) >>> 0;
118
134
  }
119
- return palette[hash % palette.length];
135
+ return COLOR_PALETTE[hash % COLOR_PALETTE.length];
136
+ }
137
+
138
+ /** Like pickColorForName, but avoids colors already used by other projects */
139
+ export function pickUnusedColor(name, usedColors) {
140
+ const used = new Set(usedColors);
141
+ const preferred = pickColorForName(name);
142
+ if (!used.has(preferred)) return preferred;
143
+ return COLOR_PALETTE.find((c) => !used.has(c)) || preferred;
120
144
  }
121
145
 
122
146
  /**
package/src/detect.mjs CHANGED
@@ -183,6 +183,28 @@ export function stateDbValueContains(stateDbPath, urlFragment) {
183
183
  }
184
184
  }
185
185
 
186
+ /**
187
+ * True if the value for `key` is byte-identical in both state DBs.
188
+ * Comparison happens inside SQL; the value never enters this process.
189
+ * @param {string} dbA
190
+ * @param {string} dbB
191
+ * @param {string} key
192
+ */
193
+ export function stateDbValuesMatch(dbA, dbB, key) {
194
+ if (!fs.existsSync(dbA) || !fs.existsSync(dbB) || !hasSqlite3()) {
195
+ return false;
196
+ }
197
+ try {
198
+ const out = execSync(
199
+ `sqlite3 ${shellQuote(dbA)} "ATTACH DATABASE ${shellQuote(dbB)} AS other; SELECT CASE WHEN (SELECT value FROM ItemTable WHERE key = ${shellQuote(key)}) IS NOT NULL AND (SELECT value FROM ItemTable WHERE key = ${shellQuote(key)}) = (SELECT value FROM other.ItemTable WHERE key = ${shellQuote(key)}) THEN 1 ELSE 0 END;"`,
200
+ { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] },
201
+ );
202
+ return out.trim() === "1";
203
+ } catch {
204
+ return false;
205
+ }
206
+ }
207
+
186
208
  function shellQuote(s) {
187
209
  return `'${String(s).replace(/'/g, `'\\''`)}'`;
188
210
  }
package/src/profile.mjs CHANGED
@@ -116,7 +116,14 @@ export function cloneStateDb(donorProfileDir, targetProfileDir) {
116
116
  ensureProfileStructure(targetProfileDir);
117
117
  fs.mkdirSync(path.dirname(dest), { recursive: true });
118
118
 
119
- execSync(`sqlite3 ${shellQuote(src)} ".backup ${shellQuote(dest)}"`, {
119
+ // VACUUM INTO requires a fresh destination.
120
+ for (const suffix of ["", "-wal", "-shm", "-journal"]) {
121
+ fs.rmSync(dest + suffix, { force: true });
122
+ }
123
+
124
+ // VACUUM INTO snapshots a consistent copy even while the donor Cursor is
125
+ // running; `.backup` can spin at full CPU against a write-heavy donor.
126
+ execSync(`sqlite3 ${shellQuote(src)} "VACUUM INTO ${shellQuote(dest)}"`, {
120
127
  stdio: "ignore",
121
128
  });
122
129
  }
@@ -138,17 +145,16 @@ export function needsFirstRunChecklist(profileDir) {
138
145
 
139
146
  export function printFirstRunChecklist(
140
147
  projectName,
141
- baseUrl = "https://connect.iobonzai.com/cursor",
148
+ baseUrl = "https://gateway.example.com/v1",
142
149
  ) {
143
150
  console.log(`
144
151
  First-time setup for "${projectName}" in this Cursor profile:
145
152
 
146
153
  1. Cursor Settings → Models → API keys
147
- 2. Paste your project Bonzai API key under OpenAI API Key
154
+ 2. Paste your project API key under OpenAI API Key (or run: cursor-keys set-key ${projectName})
148
155
  3. Enable "Use OpenAI API Key" and "Override OpenAI Base URL"
149
156
  4. Base URL: ${baseUrl}
150
- 5. Explore Subagent Model: Composer 2.5
151
- 6. Add custom Bonzai models and disable non-Bonzai models
157
+ 5. Add your gateway models and disable models you do not use
152
158
 
153
159
  Then run: cursor-keys doctor ${projectName}
154
160
  `);
Binary file