@sideboard-ai/core 0.1.232 → 0.1.234

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.
Files changed (42) hide show
  1. package/dist/agents/cursor-runner.cjs +207 -62
  2. package/dist/agents/cursor-runner.js +24 -10
  3. package/dist/{agents-TCTEAK6M.js → agents-BTXL6R5I.js} +17 -7
  4. package/dist/{agents-ENAQHQRZ.js → agents-WMN7C322.js} +16 -6
  5. package/dist/{chunk-PO4M4JAJ.js → chunk-2P4XKWV2.js} +1 -1
  6. package/dist/{chunk-FA4CPYCY.js → chunk-2UBVFL2X.js} +81 -41
  7. package/dist/{chunk-ABOBTSO6.js → chunk-2UG25RMK.js} +18 -5
  8. package/dist/{chunk-4CZ25DO2.js → chunk-4VFOJZPS.js} +2 -2
  9. package/dist/{chunk-N5TX66CD.js → chunk-7H2CVVLF.js} +2 -2
  10. package/dist/{chunk-SZJZEFTY.js → chunk-DD67JWTN.js} +1 -1
  11. package/dist/{chunk-LLMG6OVS.js → chunk-LEYY54BN.js} +19 -19
  12. package/dist/{chunk-6L25K7BE.js → chunk-NOIWXANG.js} +224 -73
  13. package/dist/{chunk-HVUVQXZ2.js → chunk-QVN2WOKJ.js} +18 -5
  14. package/dist/{chunk-7BZ7DSBN.js → chunk-RKKTCKVX.js} +17 -17
  15. package/dist/{chunk-HZFLKVGC.js → chunk-S2OEFXST.js} +1 -1
  16. package/dist/{chunk-SXFRXNBM.js → chunk-SPAMHHJH.js} +1 -1
  17. package/dist/{chunk-ZXJMAKEP.js → chunk-TNVJPFHM.js} +5 -5
  18. package/dist/{chunk-UNTOZA3C.js → chunk-TWXSZYR2.js} +1 -1
  19. package/dist/{chunk-TQBUXKIB.js → chunk-WPD7BR6C.js} +135 -11
  20. package/dist/{connected-teams-PIBQTOJU.js → connected-teams-JUJAQZZE.js} +2 -2
  21. package/dist/{connected-teams-RVD3PFAG.js → connected-teams-KQUDU7LZ.js} +2 -2
  22. package/dist/{coordinator-prompt-YKLXQTWH.js → coordinator-prompt-CE7MN6QY.js} +4 -4
  23. package/dist/{coordinator-prompt-DFXYTM6D.js → coordinator-prompt-IGDMFKCD.js} +4 -4
  24. package/dist/{global-workspace-EYZKYHAO.js → global-workspace-7WH24UFO.js} +4 -4
  25. package/dist/{global-workspace-4SPANNJT.js → global-workspace-NCIC2Y2U.js} +4 -4
  26. package/dist/index.cjs +519 -326
  27. package/dist/index.d.cts +19 -3
  28. package/dist/index.d.ts +19 -3
  29. package/dist/index.js +27 -8
  30. package/dist/mcp/run-stdio.cjs +447 -302
  31. package/dist/mcp/run-stdio.js +7 -7
  32. package/dist/{orchestrator-RJX4OKYC.js → orchestrator-7ZEGFPK4.js} +7 -7
  33. package/dist/{orchestrator-FTHJKPIX.js → orchestrator-R2WCTNJI.js} +8 -8
  34. package/dist/{run-UEQM4JMZ.js → run-PCX5QWR4.js} +1 -1
  35. package/dist/{run-QDZCTP4Y.js → run-TB7RBII2.js} +1 -1
  36. package/dist/{workspaces-73Y7IJY2.js → workspaces-H2TRFDE2.js} +4 -4
  37. package/dist/{workspaces-FQCSCTNH.js → workspaces-NSQZSDQD.js} +4 -4
  38. package/dist/{worktree-DOPRKG7K.js → worktree-FMIX4BRV.js} +3 -3
  39. package/dist/{worktree-36VEFUJN.js → worktree-W7BSI4GU.js} +3 -3
  40. package/dist/{worktree-exclude-K7QK4RI5.js → worktree-exclude-5N2KTYIT.js} +2 -2
  41. package/dist/{worktree-exclude-L32WEBX6.js → worktree-exclude-RXZPTCNH.js} +2 -2
  42. package/package.json +2 -2
@@ -2,13 +2,13 @@
2
2
 
3
3
  import {
4
4
  isOrchestratorThread
5
- } from "./chunk-UNTOZA3C.js";
5
+ } from "./chunk-TWXSZYR2.js";
6
6
  import {
7
7
  applyConnectedTeamToCli,
8
8
  brightsyMcpServerName,
9
9
  ensureCliTeamTracked,
10
10
  ensureConnectedBrightsyTeamTokens
11
- } from "./chunk-SXFRXNBM.js";
11
+ } from "./chunk-SPAMHHJH.js";
12
12
  import {
13
13
  loadBrightsyConfig
14
14
  } from "./chunk-P2BSMNHB.js";
@@ -20,14 +20,15 @@ import {
20
20
  mergeAgentGitAuthEnv,
21
21
  resolveAgentGitAuthEnv,
22
22
  resolveCodexGitWritableRoots
23
- } from "./chunk-4CZ25DO2.js";
23
+ } from "./chunk-4VFOJZPS.js";
24
24
  import {
25
25
  enrichPathWithNpmGlobalBin,
26
26
  isConductorBundledCli,
27
+ npmGlobalNodeModules,
27
28
  resolveCommandBinarySync,
28
29
  run,
29
30
  withExportedPath
30
- } from "./chunk-ABOBTSO6.js";
31
+ } from "./chunk-2UG25RMK.js";
31
32
  import {
32
33
  claudeChromeEnabled,
33
34
  fromInclusiveInputUsage,
@@ -2548,7 +2549,7 @@ var claudeAdapter = {
2548
2549
  );
2549
2550
  }
2550
2551
  const mode = permissionMode(thread);
2551
- const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-EYZKYHAO.js");
2552
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-7WH24UFO.js");
2552
2553
  const isOrchestrator = isOrchestratorThread2(thread);
2553
2554
  const injectedServers = await buildInjectedMcpServers({
2554
2555
  includeSideboard: true,
@@ -3161,11 +3162,120 @@ var codexAdapter = {
3161
3162
  };
3162
3163
 
3163
3164
  // src/agents/cursor.ts
3164
- import { existsSync as existsSync11 } from "fs";
3165
- import { createRequire as createRequire3 } from "module";
3166
- import { dirname as dirname5, join as join9 } from "path";
3167
- import { fileURLToPath as fileURLToPath2 } from "url";
3168
- import { Cursor } from "@cursor/sdk";
3165
+ import { existsSync as existsSync12 } from "fs";
3166
+ import { createRequire as createRequire4 } from "module";
3167
+ import { dirname as dirname6, join as join10 } from "path";
3168
+ import { fileURLToPath as fileURLToPath3 } from "url";
3169
+
3170
+ // src/agents/cursor-sdk-resolve.ts
3171
+ import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
3172
+ import { createRequire as createRequire2 } from "module";
3173
+ import { basename, dirname as dirname4, join as join8 } from "path";
3174
+ import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
3175
+ var CURSOR_SDK_INSTALL_HINT = "Cursor SDK is not installed. Settings \u2192 Agents \u2192 Cursor \u2192 Install (`npm i -g @cursor/sdk`). Sideboard does not ship the SDK \u2014 update it anytime without a new Sideboard build.";
3176
+ function thisModuleFile() {
3177
+ const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
3178
+ if (cjsDir) return join8(cjsDir, "cursor-sdk-resolve.js");
3179
+ try {
3180
+ return fileURLToPath2(import.meta.url);
3181
+ } catch {
3182
+ return join8(process.cwd(), "package.json");
3183
+ }
3184
+ }
3185
+ function packageRootLooksLikeSdk(dir) {
3186
+ try {
3187
+ const raw = readFileSync5(join8(dir, "package.json"), "utf8");
3188
+ const pkg = JSON.parse(raw);
3189
+ return pkg.name === "@cursor/sdk" && existsSync10(dir);
3190
+ } catch {
3191
+ return false;
3192
+ }
3193
+ }
3194
+ function resolvedPackageRoot(fromFile) {
3195
+ try {
3196
+ const req = createRequire2(fromFile);
3197
+ const pkgJson = req.resolve("@cursor/sdk/package.json");
3198
+ const root = dirname4(pkgJson);
3199
+ return packageRootLooksLikeSdk(root) ? root : null;
3200
+ } catch {
3201
+ return null;
3202
+ }
3203
+ }
3204
+ function resolveCursorSdkRoot(opts) {
3205
+ const env = opts?.env ?? process.env;
3206
+ const override = env.SIDEBOARD_CURSOR_SDK?.trim();
3207
+ if (override) {
3208
+ return packageRootLooksLikeSdk(override) ? override : null;
3209
+ }
3210
+ const globalNm = npmGlobalNodeModules(env);
3211
+ if (globalNm) {
3212
+ const globalRoot = join8(globalNm, "@cursor/sdk");
3213
+ if (packageRootLooksLikeSdk(globalRoot)) return globalRoot;
3214
+ }
3215
+ const cwdPkg = join8(process.cwd(), "package.json");
3216
+ return resolvedPackageRoot(thisModuleFile()) ?? resolvedPackageRoot(cwdPkg);
3217
+ }
3218
+ function isCursorSdkInstalled(opts) {
3219
+ return Boolean(resolveCursorSdkRoot(opts));
3220
+ }
3221
+ function exportTarget(value) {
3222
+ if (typeof value === "string") return value;
3223
+ if (value && typeof value === "object") {
3224
+ const v = value;
3225
+ return exportTarget(v.import) ?? exportTarget(v.default);
3226
+ }
3227
+ return null;
3228
+ }
3229
+ function cursorSdkEsmEntry(root) {
3230
+ try {
3231
+ const pkg = JSON.parse(readFileSync5(join8(root, "package.json"), "utf8"));
3232
+ const exp = pkg.exports;
3233
+ const dot = exp && typeof exp === "object" && !Array.isArray(exp) && "." in exp ? exp["."] : exp;
3234
+ const rel = exportTarget(dot) ?? (typeof pkg.module === "string" ? pkg.module : null);
3235
+ if (!rel) return null;
3236
+ const entry = join8(root, rel);
3237
+ return existsSync10(entry) ? entry : null;
3238
+ } catch {
3239
+ return null;
3240
+ }
3241
+ }
3242
+ function unwrapCursorSdkModule(mod) {
3243
+ const m = mod;
3244
+ if (m && !m.Agent && m.default?.Agent) return m.default;
3245
+ return m;
3246
+ }
3247
+ async function importCursorSdk(opts) {
3248
+ const root = resolveCursorSdkRoot(opts);
3249
+ if (!root) return null;
3250
+ try {
3251
+ let entry = cursorSdkEsmEntry(root);
3252
+ if (!entry) {
3253
+ const req = createRequire2(join8(root, "package.json"));
3254
+ entry = req.resolve("@cursor/sdk");
3255
+ }
3256
+ return unwrapCursorSdkModule(await import(pathToFileURL(entry).href));
3257
+ } catch {
3258
+ return null;
3259
+ }
3260
+ }
3261
+ function cursorSdkPlatformRipgrepPackage() {
3262
+ return `@cursor/sdk-${process.platform}-${process.arch}`;
3263
+ }
3264
+ function nodeModulesDirForPackageRoot(packageRoot) {
3265
+ const parent = dirname4(packageRoot);
3266
+ return basename(parent).startsWith("@") ? dirname4(parent) : parent;
3267
+ }
3268
+ function cursorSdkRipgrepCandidate(opts) {
3269
+ const root = resolveCursorSdkRoot(opts);
3270
+ if (!root) return null;
3271
+ const nm = nodeModulesDirForPackageRoot(root);
3272
+ const pkg = cursorSdkPlatformRipgrepPackage();
3273
+ const bin = process.platform === "win32" ? "rg.exe" : "rg";
3274
+ const sibling = join8(nm, pkg, "bin", bin);
3275
+ if (existsSync10(sibling)) return sibling;
3276
+ const nested = join8(root, "node_modules", pkg, "bin", bin);
3277
+ return existsSync10(nested) ? nested : null;
3278
+ }
3169
3279
 
3170
3280
  // src/agents/cursor-events.ts
3171
3281
  function usageFromCursor(usage) {
@@ -3379,9 +3489,9 @@ function parseCursorRunnerLine(line) {
3379
3489
  }
3380
3490
 
3381
3491
  // src/agents/cursor-ripgrep.ts
3382
- import { existsSync as existsSync10 } from "fs";
3383
- import { createRequire as createRequire2 } from "module";
3384
- import { dirname as dirname4, isAbsolute, join as join8, parse, resolve as resolvePath } from "path";
3492
+ import { existsSync as existsSync11 } from "fs";
3493
+ import { createRequire as createRequire3 } from "module";
3494
+ import { dirname as dirname5, isAbsolute, join as join9, parse, resolve as resolvePath } from "path";
3385
3495
  var RIPGREP_ENV = "CURSOR_RIPGREP_PATH";
3386
3496
  function rgBinaryName() {
3387
3497
  return process.platform === "win32" ? "rg.exe" : "rg";
@@ -3393,19 +3503,19 @@ function usableRipgrepPath(candidate) {
3393
3503
  const raw = candidate?.trim();
3394
3504
  if (!raw || !isAbsolute(raw)) return null;
3395
3505
  const readable = nodeReadableScriptPath(raw);
3396
- if (!existsSync10(readable) || isAsarPath(readable)) return null;
3506
+ if (!existsSync11(readable) || isAsarPath(readable)) return null;
3397
3507
  return readable;
3398
3508
  }
3399
3509
  function walkForBundledRipgrep(startFile) {
3400
3510
  if (!startFile) return null;
3401
3511
  const pkg = platformRipgrepPackage();
3402
3512
  const name = rgBinaryName();
3403
- let dir = dirname4(resolvePath(startFile));
3513
+ let dir = dirname5(resolvePath(startFile));
3404
3514
  const root = parse(dir).root;
3405
3515
  while (dir !== root) {
3406
- const hit = usableRipgrepPath(join8(dir, "node_modules", pkg, "bin", name));
3516
+ const hit = usableRipgrepPath(join9(dir, "node_modules", pkg, "bin", name));
3407
3517
  if (hit) return hit;
3408
- const next = dirname4(dir);
3518
+ const next = dirname5(dir);
3409
3519
  if (next === dir) break;
3410
3520
  dir = next;
3411
3521
  }
@@ -3413,9 +3523,9 @@ function walkForBundledRipgrep(startFile) {
3413
3523
  }
3414
3524
  function requireResolveBundledRipgrep(fromFile) {
3415
3525
  try {
3416
- const req = createRequire2(fromFile);
3526
+ const req = createRequire3(fromFile);
3417
3527
  const pkgJson = req.resolve(`${platformRipgrepPackage()}/package.json`);
3418
- return usableRipgrepPath(join8(dirname4(pkgJson), "bin", rgBinaryName()));
3528
+ return usableRipgrepPath(join9(dirname5(pkgJson), "bin", rgBinaryName()));
3419
3529
  } catch {
3420
3530
  return null;
3421
3531
  }
@@ -3431,7 +3541,7 @@ function resolveCursorRipgrepPath(opts) {
3431
3541
  const start = opts?.startFile?.trim() || process.argv[1] || // CJS bundle only — ESM has no __filename; argv[1] is the running script.
3432
3542
  // eslint-disable-next-line camelcase
3433
3543
  (typeof __filename !== "undefined" ? __filename : "");
3434
- return walkForBundledRipgrep(start) ?? requireResolveBundledRipgrep(start);
3544
+ return walkForBundledRipgrep(start) ?? usableRipgrepPath(cursorSdkRipgrepCandidate({ env })) ?? requireResolveBundledRipgrep(start);
3435
3545
  }
3436
3546
  function cursorRipgrepEnv(opts) {
3437
3547
  const path = resolveCursorRipgrepPath(opts);
@@ -3469,8 +3579,10 @@ async function listCursorModels() {
3469
3579
  }
3470
3580
  const apiKey = resolveCursorApiKey();
3471
3581
  if (!apiKey) return FALLBACK_CURSOR_MODELS;
3582
+ const sdk = await importCursorSdk();
3583
+ if (!sdk) return FALLBACK_CURSOR_MODELS;
3472
3584
  try {
3473
- const listed = await Cursor.models.list({ apiKey });
3585
+ const listed = await sdk.Cursor.models.list({ apiKey });
3474
3586
  const models = listed.map((m) => ({
3475
3587
  id: m.id,
3476
3588
  displayName: m.displayName || m.id,
@@ -3487,11 +3599,11 @@ function entryDir() {
3487
3599
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
3488
3600
  if (cjsDir) return cjsDir;
3489
3601
  try {
3490
- return dirname5(fileURLToPath2(import.meta.url));
3602
+ return dirname6(fileURLToPath3(import.meta.url));
3491
3603
  } catch {
3492
3604
  try {
3493
- const req = createRequire3(process.cwd() + "/");
3494
- return dirname5(req.resolve("@sideboard-ai/core"));
3605
+ const req = createRequire4(process.cwd() + "/");
3606
+ return dirname6(req.resolve("@sideboard-ai/core"));
3495
3607
  } catch {
3496
3608
  return process.cwd();
3497
3609
  }
@@ -3502,23 +3614,33 @@ function cursorRunnerPath() {
3502
3614
  if (packaged) return packaged;
3503
3615
  const root = entryDir();
3504
3616
  const candidates = [
3505
- join9(root, "agents", "cursor-runner.js"),
3506
- join9(root, "agents", "cursor-runner.cjs"),
3617
+ join10(root, "agents", "cursor-runner.js"),
3618
+ join10(root, "agents", "cursor-runner.cjs"),
3507
3619
  // If somehow resolved from package root instead of dist/
3508
- join9(root, "dist", "agents", "cursor-runner.js"),
3509
- join9(root, "dist", "agents", "cursor-runner.cjs"),
3620
+ join10(root, "dist", "agents", "cursor-runner.js"),
3621
+ join10(root, "dist", "agents", "cursor-runner.cjs"),
3510
3622
  // Source tree (dev): packages/core/src/agents/cursor-runner.ts
3511
- join9(root, "cursor-runner.ts"),
3512
- join9(root, "src", "agents", "cursor-runner.ts")
3623
+ join10(root, "cursor-runner.ts"),
3624
+ join10(root, "src", "agents", "cursor-runner.ts")
3513
3625
  ];
3514
3626
  for (const candidate of candidates) {
3515
- if (existsSync11(candidate)) return candidate;
3627
+ if (existsSync12(candidate)) return candidate;
3516
3628
  }
3517
3629
  return candidates[0];
3518
3630
  }
3519
3631
  var cursorAdapter = {
3520
3632
  kind: "cursor",
3521
3633
  async detect() {
3634
+ if (!isCursorSdkInstalled()) {
3635
+ return {
3636
+ agent: "cursor",
3637
+ installed: false,
3638
+ authenticated: false,
3639
+ linearMcp: false,
3640
+ warnings: [],
3641
+ reason: CURSOR_SDK_INSTALL_HINT
3642
+ };
3643
+ }
3522
3644
  const apiKey = resolveCursorApiKey();
3523
3645
  if (!apiKey) {
3524
3646
  return {
@@ -3600,7 +3722,7 @@ var cursorAdapter = {
3600
3722
  };
3601
3723
 
3602
3724
  // src/agents/opencode.ts
3603
- import { existsSync as existsSync12 } from "fs";
3725
+ import { existsSync as existsSync13 } from "fs";
3604
3726
  var FALLBACK_OPENCODE_MODELS = [
3605
3727
  { id: "opencode/big-pickle", displayName: "opencode \xB7 big-pickle" },
3606
3728
  {
@@ -3659,7 +3781,7 @@ async function listOpencodeModels() {
3659
3781
  if (opencode === "opencode") {
3660
3782
  const which = await run("which", ["opencode"], { reject: false });
3661
3783
  if (which.exitCode !== 0) return FALLBACK_OPENCODE_MODELS;
3662
- } else if (!existsSync12(opencode)) {
3784
+ } else if (!existsSync13(opencode)) {
3663
3785
  return FALLBACK_OPENCODE_MODELS;
3664
3786
  }
3665
3787
  const listed = await run(opencode, ["models"], { reject: false });
@@ -3694,7 +3816,7 @@ var opencodeAdapter = {
3694
3816
  async detect() {
3695
3817
  const opencode = resolveAgentExecutable("opencode");
3696
3818
  if (opencode !== "opencode") {
3697
- if (!existsSync12(opencode)) {
3819
+ if (!existsSync13(opencode)) {
3698
3820
  return {
3699
3821
  agent: "opencode",
3700
3822
  installed: false,
@@ -4371,9 +4493,9 @@ function formatClaudeUsageOverLimitWait(windows, now = /* @__PURE__ */ new Date(
4371
4493
  }
4372
4494
 
4373
4495
  // src/agents/claude-usage-client.ts
4374
- import { existsSync as existsSync13, readFileSync as readFileSync5 } from "fs";
4496
+ import { existsSync as existsSync14, readFileSync as readFileSync6 } from "fs";
4375
4497
  import { homedir as homedir5 } from "os";
4376
- import { join as join10 } from "path";
4498
+ import { join as join11 } from "path";
4377
4499
  var CLAUDE_OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
4378
4500
  var CLAUDE_USAGE_TTL_MS = 9e4;
4379
4501
  var CLAUDE_USAGE_ERROR_TTL_MS = 5 * 6e4;
@@ -4401,14 +4523,14 @@ function accessTokenFromCredentialsJson(raw) {
4401
4523
  }
4402
4524
  }
4403
4525
  function claudeCredentialsPath() {
4404
- const root = process.env.CLAUDE_CONFIG_DIR?.trim() || join10(homedir5(), ".claude");
4405
- return join10(root, ".credentials.json");
4526
+ const root = process.env.CLAUDE_CONFIG_DIR?.trim() || join11(homedir5(), ".claude");
4527
+ return join11(root, ".credentials.json");
4406
4528
  }
4407
4529
  async function readFileAccessToken() {
4408
4530
  const path = claudeCredentialsPath();
4409
- if (!existsSync13(path)) return null;
4531
+ if (!existsSync14(path)) return null;
4410
4532
  try {
4411
- return accessTokenFromCredentialsJson(readFileSync5(path, "utf8"));
4533
+ return accessTokenFromCredentialsJson(readFileSync6(path, "utf8"));
4412
4534
  } catch {
4413
4535
  return null;
4414
4536
  }
@@ -4514,6 +4636,7 @@ async function getClaudePlanUsage(opts = {}) {
4514
4636
  }
4515
4637
 
4516
4638
  // src/agents/install.ts
4639
+ import { existsSync as existsSync15 } from "fs";
4517
4640
  var SETUP = {
4518
4641
  claude: {
4519
4642
  agent: "claude",
@@ -4522,7 +4645,8 @@ var SETUP = {
4522
4645
  docsUrl: "https://code.claude.com/docs/en/install",
4523
4646
  installCommand: "npm install -g @anthropic-ai/claude-code",
4524
4647
  loginCommand: "claude auth login",
4525
- npmPackage: "@anthropic-ai/claude-code"
4648
+ npmPackage: "@anthropic-ai/claude-code",
4649
+ updateArgs: ["update"]
4526
4650
  },
4527
4651
  codex: {
4528
4652
  agent: "codex",
@@ -4531,7 +4655,8 @@ var SETUP = {
4531
4655
  docsUrl: "https://github.com/openai/codex",
4532
4656
  installCommand: "npm install -g @openai/codex",
4533
4657
  loginCommand: "codex login",
4534
- npmPackage: "@openai/codex"
4658
+ npmPackage: "@openai/codex",
4659
+ updateArgs: ["update"]
4535
4660
  },
4536
4661
  opencode: {
4537
4662
  agent: "opencode",
@@ -4541,20 +4666,22 @@ var SETUP = {
4541
4666
  // Prefer the official installer; npm package name is opencode-ai.
4542
4667
  installCommand: "curl -fsSL https://opencode.ai/install | bash",
4543
4668
  loginCommand: "opencode auth login",
4544
- npmPackage: "opencode-ai@latest"
4669
+ npmPackage: "opencode-ai@latest",
4670
+ updateArgs: ["upgrade"]
4545
4671
  },
4546
4672
  cursor: {
4547
4673
  agent: "cursor",
4548
- kind: "bundled-sdk",
4549
- summary: "No CLI install \u2014 Sideboard ships the Cursor SDK. Add a CURSOR_API_KEY from the Cursor dashboard.",
4550
- docsUrl: "https://cursor.com/dashboard/integrations",
4551
- installCommand: null,
4552
- loginCommand: null
4674
+ kind: "cli",
4675
+ summary: "Install (or update) @cursor/sdk with npm \u2014 Sideboard does not pin it in the app. Then add a CURSOR_API_KEY.",
4676
+ docsUrl: "https://www.npmjs.com/package/@cursor/sdk",
4677
+ installCommand: "npm install -g @cursor/sdk",
4678
+ loginCommand: null,
4679
+ npmPackage: "@cursor/sdk"
4553
4680
  },
4554
4681
  brightsy: {
4555
4682
  agent: "brightsy",
4556
4683
  kind: "cli",
4557
- summary: "Install the Brightsy CLI, then run `brightsy login`.",
4684
+ summary: "Install (or update) the Brightsy CLI with npm, then run `brightsy login`.",
4558
4685
  docsUrl: "https://www.npmjs.com/package/@brightsy/cli",
4559
4686
  installCommand: "npm install -g @brightsy/cli",
4560
4687
  loginCommand: "brightsy login",
@@ -4650,16 +4777,6 @@ async function whichOnPath(bin) {
4650
4777
  }
4651
4778
  async function installNpmGlobalPackage(opts) {
4652
4779
  enrichPathWithNpmGlobalBin();
4653
- if (opts.cliBin) {
4654
- const existing = await whichOnPath(opts.cliBin);
4655
- if (existing) {
4656
- return {
4657
- ok: true,
4658
- command: existing,
4659
- message: isConductorBundledCli(existing) ? `Using Conductor\u2019s ${opts.cliBin} at ${existing}. No extra install \u2014 Log in only if auth is missing.` : `Already on PATH: ${existing}`
4660
- };
4661
- }
4662
- }
4663
4780
  const result = await run("npm", ["install", "-g", opts.npmPackage], { reject: false });
4664
4781
  const ok = result.exitCode === 0;
4665
4782
  if (!ok) {
@@ -4696,6 +4813,33 @@ async function installNpmGlobalPackage(opts) {
4696
4813
  message: binPath ? `Installed ${opts.npmPackage} \u2192 ${binPath}` : `Installed ${opts.npmPackage} (CLI not yet on PATH)`
4697
4814
  };
4698
4815
  }
4816
+ async function resolveExistingCli(agent) {
4817
+ if (agent === "cursor") return null;
4818
+ const exe = resolveAgentExecutable(agent);
4819
+ if ((exe.includes("/") || exe.includes("\\")) && existsSync15(exe)) return exe;
4820
+ enrichPathWithNpmGlobalBin();
4821
+ return whichOnPath(CLI_BIN[agent]);
4822
+ }
4823
+ async function runVendorUpdate(existing, updateArgs) {
4824
+ const result = await run(existing, updateArgs, {
4825
+ reject: false,
4826
+ timeoutMs: 12e4
4827
+ });
4828
+ const command = `${existing} ${updateArgs.join(" ")}`;
4829
+ const ok = result.exitCode === 0;
4830
+ const detail = result.stdout.trim() || result.stderr.trim() || `exit ${result.exitCode}`;
4831
+ return {
4832
+ ok,
4833
+ command,
4834
+ exitCode: result.exitCode,
4835
+ stdout: result.stdout,
4836
+ stderr: result.stderr,
4837
+ message: ok ? isConductorBundledCli(existing) ? `Updated Conductor\u2019s CLI at ${existing}. ${detail}` : `Updated ${command}. ${detail}` : `Update failed (${command}): ${detail}`
4838
+ };
4839
+ }
4840
+ function isCurlInstaller(command) {
4841
+ return /curl\s| \|\s*bash/.test(command);
4842
+ }
4699
4843
  async function installAgent(agent) {
4700
4844
  const info = getAgentSetupInfo(agent);
4701
4845
  if (info.kind === "bundled-sdk" || info.kind === "api-key") {
@@ -4707,21 +4851,25 @@ async function installAgent(agent) {
4707
4851
  if (!info.installCommand) {
4708
4852
  return { ok: false, message: `No install command for ${agent}` };
4709
4853
  }
4710
- if (/curl\s| \|\s*bash/.test(info.installCommand) || !info.npmPackage) {
4711
- await openInSystemTerminal(info.installCommand);
4712
- return {
4713
- ok: true,
4714
- openedTerminal: true,
4715
- command: info.installCommand,
4716
- message: `Opened Terminal to run: ${info.installCommand}`
4717
- };
4854
+ const existing = await resolveExistingCli(agent);
4855
+ if (existing && info.updateArgs?.length) {
4856
+ return runVendorUpdate(existing, info.updateArgs);
4718
4857
  }
4719
- const cliBin = agent === "cursor" ? null : CLI_BIN[agent];
4720
- return installNpmGlobalPackage({
4721
- npmPackage: info.npmPackage,
4722
- installCommand: info.installCommand,
4723
- cliBin
4724
- });
4858
+ if (info.npmPackage && !isCurlInstaller(info.installCommand)) {
4859
+ const cliBin = agent === "cursor" ? null : CLI_BIN[agent];
4860
+ return installNpmGlobalPackage({
4861
+ npmPackage: info.npmPackage,
4862
+ installCommand: info.installCommand,
4863
+ cliBin
4864
+ });
4865
+ }
4866
+ await openInSystemTerminal(info.installCommand);
4867
+ return {
4868
+ ok: true,
4869
+ openedTerminal: true,
4870
+ command: info.installCommand,
4871
+ message: `Opened Terminal to run: ${info.installCommand}`
4872
+ };
4725
4873
  }
4726
4874
  async function loginAgent(agent) {
4727
4875
  const info = getAgentSetupInfo(agent);
@@ -4800,6 +4948,9 @@ export {
4800
4948
  claudeAdapter,
4801
4949
  listCodexModels,
4802
4950
  codexAdapter,
4951
+ CURSOR_SDK_INSTALL_HINT,
4952
+ resolveCursorSdkRoot,
4953
+ isCursorSdkInstalled,
4803
4954
  preferredCursorCostCents,
4804
4955
  turnCostUsdFromCursorUsage,
4805
4956
  cursorSdkMessageToEvents,
@@ -63,6 +63,14 @@ function ensureAgentPath(env = process.env) {
63
63
  }
64
64
  function enrichPathWithNpmGlobalBin(env = process.env) {
65
65
  ensureAgentPath(env);
66
+ const prefix = npmGlobalPrefix(env);
67
+ if (prefix) {
68
+ const binDir = process.platform === "win32" ? prefix : join(prefix, "bin");
69
+ prependPathDir(env, binDir);
70
+ }
71
+ return env.PATH ?? "";
72
+ }
73
+ function npmGlobalPrefix(env = process.env) {
66
74
  try {
67
75
  const prefix = execFileSync("npm", ["prefix", "-g"], {
68
76
  encoding: "utf8",
@@ -70,13 +78,16 @@ function enrichPathWithNpmGlobalBin(env = process.env) {
70
78
  timeout: 8e3,
71
79
  stdio: ["ignore", "pipe", "ignore"]
72
80
  }).trim().split(/\r?\n/).find(Boolean);
73
- if (prefix) {
74
- const binDir = process.platform === "win32" ? prefix : join(prefix, "bin");
75
- prependPathDir(env, binDir);
76
- }
81
+ return prefix || null;
77
82
  } catch {
83
+ return null;
78
84
  }
79
- return env.PATH ?? "";
85
+ }
86
+ function npmGlobalNodeModules(env = process.env) {
87
+ const prefix = npmGlobalPrefix(env);
88
+ if (!prefix) return null;
89
+ const dir = process.platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
90
+ return existsSync(dir) ? dir : null;
80
91
  }
81
92
  function posixShellSingleQuote(value) {
82
93
  return `'${value.replace(/'/g, `'\\''`)}'`;
@@ -307,6 +318,8 @@ export {
307
318
  isConductorBundledCli,
308
319
  ensureAgentPath,
309
320
  enrichPathWithNpmGlobalBin,
321
+ npmGlobalPrefix,
322
+ npmGlobalNodeModules,
310
323
  posixShellSingleQuote,
311
324
  resolveCommandBinarySync,
312
325
  withExportedPath,