Skip to content

feat(agent): Cursor provider authentication — Agent Adapter Architecture step 1#56

Merged
BotCoder254 merged 1 commit into
mainfrom
feat/cursor-provider-auth
Jul 8, 2026
Merged

feat(agent): Cursor provider authentication — Agent Adapter Architecture step 1#56
BotCoder254 merged 1 commit into
mainfrom
feat/cursor-provider-auth

Conversation

@BotCoder254

@BotCoder254 BotCoder254 commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary

Adds Cursor as a second, authentication-only agent provider beside Claude Code — build-order item (1) of the Agent Adapter Architecture. Users can connect their Cursor account today (interactive CLI sign-in or an encrypted API key) so the account is ready when the runtime adapter lands; Cursor cannot run yetAGENT_MODELS deliberately has no Cursor entries, so it is structurally unselectable as the running agent.

Everything above the adapter seam is untouched: sessions, worktrees, git, terminal, memory, search, resume, and the whole agent UI stay provider-neutral.

What's new

🔐 Authentication (main process)

  • CursorAuthManager — lazy, fully local classification (not-installednot-authenticatedauthenticated-cli / authenticated-api-key), no network probes, nothing spawns at boot. Two deliberately distinct paths:
    • CLI sign-in — spawns cursor-agent login (argv-only, single-flight, timeout-killed, verified afterwards via status --format json). The CLI keeps its own credentials; Limboo never reads, copies, or exports them. Manual-browser mode (NO_OPEN_BROWSER=1) surfaces the printed login URL for Copy/Open.
    • API key — held safeStorage-encrypted by the new SecretStore (userData/secrets/, atomic 0o600 writes, names-only logging). Classification checks presence only; decryption is confined to getSpawnEnv() — the decrypt-at-spawn seam reserved for the Phase-2 runtime.
  • exec.ts — safe cursor-agent runner: execFile/spawn with argv arrays (never shell: true), secrets ride only in the child env (never argv), bounded output, Windows where.exe resolution with .cmd shims bridged through %ComSpec% only for static-literal whitelisted args, and a ~/.local/bin/{cursor-agent,agent} install-dir fallback for PATH-less GUI launches.
  • preferredAuth honored end-to-endauto (key wins), api-key (a stored CLI login never classifies; the status probe is skipped), cli-login (a stored key is kept but ignored, and getSpawnEnv() returns {}).

🛡️ Security hardening

  • API key crosses IPC exactly once (set), validated at the boundary (length caps, printable-ASCII only), never returned, logged, or echoed into an error. All broadcast state (CursorAuthState) is secret-free.
  • 7 new agent:cursor* channels all go through the trusted-sender handle() wrapper.
  • Login-URL allowlist — the captured sign-in URL must be https, credential-free, and on a Cursor-owned host (cursor.com / cursor.sh or subdomain, dot-boundary match) before the UI may open it.
  • Central redaction — crsr_… tokens and CURSOR_API_KEY=… are masked in the logger, AgentManager.redact(), and all captured CLI output before anything reaches disk or the renderer.
  • status --format json parsed defensively: whitelisted scalar fields only, length-capped, no unknown-key iteration (prototype-pollution discipline).

🎨 UI (same pure-black theme, shared components)

  • CursorProviderCard in Settings › Agent › Providers: per-state actions (sign in / cancel / retry, manual-URL copy+open, logout, re-authenticate, API key save/replace/remove, refresh, install guide, dashboard), keychain-unavailable warning, and a Preferred authentication segmented control.
  • Shared ProviderStatusRow — Claude and Cursor render the identical provider row; status pills are now icon + label (lucide CheckCircle2 Connected · KeyRound Sign in required · Download Install CLI · spinning Loader2 Checking) instead of bare text like "Not installed".
  • Shared ActionButton in settings controls; provider-neutral Connection & reliability copy (agent.connection is shared by every provider); CursorMark glyph in ProviderIcon; settings deep-search entries for every new field.
  • API-key actions link to the documented key page (cursor.com/dashboard/api).

⚙️ Config & docs

  • settings.agent.cursor (preferredAuth, manualBrowserLogin), SETTINGS_VERSION → 13, CURSOR_LIMITS bounds, AgentProvider widened to 'anthropic' | 'cursor'.
  • Docs updated: CLAUDE.md (§3 tree, §6 SecretStore, §8 adapter status), docs/reference/* (IPC channels, settings, window.limboo API), architecture security model + agent-manager subsystem, user guide.

Verified against the official Cursor docs

NO_OPEN_BROWSER=1 (print login URL), status/whoami --format json, CURSOR_API_KEY env / --api-key flag, Dashboard → API Keys URL, and the agent binary naming — cross-checked with cursor.com/docs (CLI authentication + parameters references).

Testing

  • npx vite build --config vite.renderer.config.mts
  • npm run lint
  • Forge dev boot — main / preload / voice worker bundles build and the app starts cleanly ✅
  • Manual: provider rows render the shared icon pills; card states cycle correctly across not-installed / signed-out / CLI-login / API-key; preferredAuth re-classifies live.

Out of scope (next build-order items)

Runtime adapter (@cursor/sdk / --output-format stream-json), permission bridging (.cursor/cli.json + hooks), context injection via rules files, MCP reuse, and worktree wiring — tracked in CLAUDE.md §8.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added Cursor as a new provider option alongside existing agent support.
    • Added Cursor sign-in via browser login or API key, with encrypted key storage and clear connection status in Settings.
    • Added provider-specific status indicators, icons, and refresh/logout actions for Cursor.
  • Bug Fixes

    • Improved secret handling by redacting Cursor-style keys from logs and limiting credential exposure.
    • Added safer validation for Cursor API key input and connection state checks.

…ure step 1

Adds Cursor as a second, authentication-only provider beside Claude Code
(build-order item 1 of the Agent Adapter Architecture; no run capability
yet — AGENT_MODELS deliberately has no Cursor entries).

Main process:
- CursorAuthManager: lazy, fully local auth classification (not-installed /
  not-authenticated / authenticated-cli / authenticated-api-key) honoring
  settings.agent.cursor.preferredAuth; single-flight cursor-agent login
  child with manual-browser mode (NO_OPEN_BROWSER=1) and a Cursor-host
  allowlist on the captured login URL; logout; API key set/remove;
  getSpawnEnv() decrypt-at-spawn seam for the future runtime adapter.
- exec.ts: argv-only cursor-agent runner (never shell:true), Windows
  where.exe + ComSpec-bridged .cmd shims gated by a static-literal arg
  whitelist, ~/.local/bin/{cursor-agent,agent} install-dir fallback,
  bounded output, redactCursor().
- SecretStore: safeStorage-encrypted secrets under userData/secrets/
  (atomic 0o600 writes, names-only logging, presence-only metadata).
- 7 validated agent:cursor* IPC channels via the handle() wrapper; the API
  key crosses IPC exactly once and is never returned, logged, or echoed.
- Logger + AgentManager redaction for crsr_ tokens and CURSOR_API_KEY.

Renderer:
- CursorProviderCard in Settings › Agent › Providers: CLI sign-in
  (incl. manual URL copy/open), encrypted API key, preferredAuth
  segmented control, per-state actions.
- Shared ProviderStatusRow + icon-and-label status pills (lucide icons via
  lifecycleMeta/cursorStatusMeta) used by both Claude and Cursor rows;
  shared ActionButton in settings controls; provider-neutral
  Connection & reliability copy; CursorMark glyph; catalog search fields.
- useAgentStore cursorAuth state + actions over window.limboo.agent.cursor.

Settings: agent.cursor { preferredAuth, manualBrowserLogin },
SETTINGS_VERSION 13, CURSOR_LIMITS bounds. Docs updated (CLAUDE.md §3/§6/§8,
reference + architecture + guides).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security Review Summary

Reviewed the Cursor authentication integration focusing on security-critical code paths. The implementation demonstrates strong security practices including proper secret redaction, input validation, command injection prevention, and encrypted storage.

Critical Issue Found (1)

Race condition in secret file creation - The SecretStore creates temporary files with default permissions before applying 0o600, leaving encrypted secrets briefly readable by other processes. This must be fixed before merge.

Security Strengths

The PR demonstrates excellent security design:

  • ✅ Secrets encrypted with Electron safeStorage, never plaintext
  • ✅ API keys validated at IPC boundaries with strict regex (printable ASCII only, length-capped)
  • ✅ Comprehensive redaction across logger, AgentManager, and CLI output
  • ✅ Command execution uses argv arrays, never shell: true
  • ✅ Windows batch shim arguments whitelisted via SAFE_ARG_RE
  • ✅ Login URL domain allowlist prevents CLI spoofing attacks
  • ✅ Secrets injected via child environment, never argv
  • ✅ Defensive JSON parsing with whitelisted fields (prototype pollution discipline)
  • ✅ All file operations bounded by timeouts and output limits

The architecture is well-designed with proper separation between authentication and runtime, and the decrypt-at-spawn pattern is exactly right. Once the file permission race condition is addressed, this will be a secure implementation.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

};
const tmp = `${target}.tmp`;
fs.mkdirSync(this.dir(), { recursive: true });
fs.writeFileSync(tmp, JSON.stringify(payload), { encoding: 'utf8', mode: 0o600 });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Security Vulnerability: Race condition between tmp file creation and permission setting creates a window where secrets are readable by other processes. The mode 0o600 is applied AFTER the file content is written, leaving encrypted secrets temporarily exposed with default permissions (typically 0o644).

Call fs.openSync with mode 0o600, write to that file descriptor, then close and rename. This ensures the restrictive permissions are atomic with file creation.

Suggested change
fs.writeFileSync(tmp, JSON.stringify(payload), { encoding: 'utf8', mode: 0o600 });
const fd = fs.openSync(tmp, 'w', 0o600);
try {
fs.writeSync(fd, JSON.stringify(payload), 0, 'utf8');
} finally {
fs.closeSync(fd);
}

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a Cursor authentication-only provider adapter: encrypted secret storage (SecretStore), a safe CLI exec wrapper, a CursorAuthManager for login/logout/API-key/probe flows, IPC and preload bridging, renderer store/UI (CursorProviderCard), expanded redaction, shared types/constants, and documentation updates.

Changes

Cursor Authentication Feature

Layer / File(s) Summary
Shared types and IPC contracts
src/shared/constants.ts, src/shared/types.ts, src/shared/ipc-channels.ts
Adds AgentProvider "cursor" variant, CURSOR_LIMITS, agent.cursor settings defaults, CursorAuthState/CursorAuthStatus/CursorLoginPhase types, and Cursor IPC channels/events; bumps SETTINGS_VERSION.
Encrypted SecretStore
src/main/secrets/SecretStore.ts
Implements safeStorage-backed encrypted secret persistence with atomic writes, presence/metadata checks, decrypt-on-demand with self-healing, and removal.
Safe Cursor CLI exec wrapper
src/main/managers/cursor/exec.ts
Adds executable resolution, runCursorAgent, spawnCursorLogin, and Windows ComSpec bridging gated by a strict argument allowlist and output redaction.
CursorAuthManager core logic
src/main/managers/cursor/CursorAuthManager.ts
Implements probing/classification, interactive login/logout, API-key management, spawn-env decryption, state broadcasting, status parsing, and login URL validation.
IPC wiring and main bootstrap
src/main/ipc/cursorHandlers.ts, src/main/ipc/index.ts, src/main/index.ts, src/main/managers/SettingsManager.ts
Registers Cursor IPC handlers with key validation, extends IpcDeps, wires CursorAuthManager/SecretStore into app lifecycle, and normalizes cursor settings fields.
Secret redaction hardening
src/main/logger.ts, src/main/managers/AgentManager.ts, docs/architecture/security-model.md
Extends redaction patterns/docs to cover crsr_ tokens and CURSOR_API_KEY values, plus a new encrypted-storage hardening pattern.
Preload bridge and renderer store
src/preload/index.ts, src/renderer/stores/useAgentStore.ts
Exposes agent.cursor IPC methods in preload and wires cursor auth state/actions into useAgentStore with hydration and toast feedback.
Cursor provider settings UI
src/renderer/components/brand/ProviderIcon.tsx, src/renderer/features/agent/status.ts, src/renderer/features/settings/panels/ProviderCard.tsx, src/renderer/features/settings/panels/CursorProviderCard.tsx, src/renderer/features/settings/panels/AgentPanel.tsx, src/renderer/features/settings/controls.tsx, src/renderer/features/settings/catalog.tsx
Adds CursorMark icon, ProviderStatusRow, ActionButton, lifecycle/cursor status metadata, and CursorProviderCard with login/API-key flows, integrated into AgentPanel and settings search catalog.
Documentation
CLAUDE.md, docs/architecture/..., docs/getting-started/configuration.md, docs/guides/using-the-agent.md, docs/reference/*
Documents Cursor authentication architecture, security patterns, settings, IPC channels, and user-facing setup instructions.

Estimated code review effort: 4 (Complex) | ~75 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CursorProviderCard
  participant useAgentStore
  participant preload as window.limboo.agent.cursor
  participant CursorAuthManager
  participant SecretStore
  participant CursorCLI as cursor-agent CLI

  CursorProviderCard->>useAgentStore: cursorLoginStart(manual)
  useAgentStore->>preload: loginStart(manual)
  preload->>CursorAuthManager: loginStart(manual)
  CursorAuthManager->>CursorCLI: spawn cursor-agent login
  CursorCLI-->>CursorAuthManager: stdout/stderr (login URL / exit)
  CursorAuthManager->>CursorAuthManager: probe() re-verify auth
  CursorAuthManager-->>preload: broadcast agentCursorAuthChanged
  preload-->>useAgentStore: onAuthChanged(state)
  useAgentStore-->>CursorProviderCard: cursorAuth updated

  CursorProviderCard->>useAgentStore: cursorSetApiKey(key)
  useAgentStore->>preload: setApiKey(key)
  preload->>CursorAuthManager: setApiKey(key)
  CursorAuthManager->>SecretStore: set(CURSOR_API_KEY_SECRET, key)
  SecretStore-->>CursorAuthManager: stored (encrypted)
  CursorAuthManager->>CursorAuthManager: probe()
  CursorAuthManager-->>preload: broadcast agentCursorAuthChanged
  preload-->>useAgentStore: onAuthChanged(state)
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding Cursor provider authentication as the first Agent Adapter Architecture step.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cursor-provider-auth

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The job was not started because the account is locked due to a billing issue.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/architecture/subsystems/agent-manager.md`:
- Around line 46-49: Update the Cursor API-key transport wording in the
agent-manager docs so it does not say the key is “never IPC’d”; instead,
describe that the key is sent once via cursor.setApiKey() to persist it, and
after that it is never echoed back, logged, or placed on argv. Keep the wording
aligned with the existing SecretStore and getSpawnEnv() behavior.

In `@docs/guides/using-the-agent.md`:
- Around line 26-42: The docs claim “never stores credentials” too broadly, but
the new Cursor section persists API keys encrypted in SecretStore, so narrow
that statement to Anthropic sign-in only or explicitly note Cursor as an
exception. Update the wording in the agent guide so the earlier
credential-storage claim stays accurate when describing the sign-in flow and the
Cursor API key flow, using the “Connecting Cursor (preview)” section as the
reference point.

In `@src/renderer/features/settings/panels/CursorProviderCard.tsx`:
- Around line 77-83: The CursorProviderCard save flow should not allow an empty
draft to be submitted, since saveKey still calls setApiKey(keyDraft.trim()) even
when the input is blank. Add disabled support to ActionButton in controls.tsx,
then update both “Save key” buttons in CursorProviderCard to use
disabled={!keyDraft.trim()} so saveKey is never invoked with an empty string and
the UI gives immediate feedback.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ead9959e-dc8e-4eb2-b59c-33d7962da24b

📥 Commits

Reviewing files that changed from the base of the PR and between 70e8558 and 4cedbf4.

📒 Files selected for processing (29)
  • CLAUDE.md
  • docs/architecture/security-model.md
  • docs/architecture/subsystems/agent-manager.md
  • docs/getting-started/configuration.md
  • docs/guides/using-the-agent.md
  • docs/reference/ipc-channels.md
  • docs/reference/settings.md
  • docs/reference/window-limboo-api.md
  • src/main/index.ts
  • src/main/ipc/cursorHandlers.ts
  • src/main/ipc/index.ts
  • src/main/logger.ts
  • src/main/managers/AgentManager.ts
  • src/main/managers/SettingsManager.ts
  • src/main/managers/cursor/CursorAuthManager.ts
  • src/main/managers/cursor/exec.ts
  • src/main/secrets/SecretStore.ts
  • src/preload/index.ts
  • src/renderer/components/brand/ProviderIcon.tsx
  • src/renderer/features/agent/status.ts
  • src/renderer/features/settings/catalog.tsx
  • src/renderer/features/settings/controls.tsx
  • src/renderer/features/settings/panels/AgentPanel.tsx
  • src/renderer/features/settings/panels/CursorProviderCard.tsx
  • src/renderer/features/settings/panels/ProviderCard.tsx
  • src/renderer/stores/useAgentStore.ts
  • src/shared/constants.ts
  • src/shared/ipc-channels.ts
  • src/shared/types.ts

Comment on lines +46 to +49
- `src/main/secrets/SecretStore.ts` — the app's safeStorage-backed secret store
(first consumer). The Cursor API key is encrypted at rest under
`userData/secrets/`, decrypted only at child-spawn time (`getSpawnEnv()`),
and never IPC'd, logged, or placed on argv.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Clarify the API-key transport wording.

The new Cursor section says the API key is "never IPC'd", but cursor.setApiKey() still has to cross the renderer→main boundary once to persist it. Rephrase this to say the key is never echoed back, logged, or placed on argv after that initial setter call.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/architecture/subsystems/agent-manager.md` around lines 46 - 49, Update
the Cursor API-key transport wording in the agent-manager docs so it does not
say the key is “never IPC’d”; instead, describe that the key is sent once via
cursor.setApiKey() to persist it, and after that it is never echoed back,
logged, or placed on argv. Keep the wording aligned with the existing
SecretStore and getSpawnEnv() behavior.

Comment on lines +26 to +42
### Connecting Cursor (preview)

Settings › Agent › Providers has a **Cursor** card. Running Cursor agents arrives
in a later update — connecting your account now just gets it ready. Two paths:

- **Sign in** — Limboo runs `cursor-agent login`; the CLI authenticates in your
browser and keeps its own credentials (Limboo never reads or copies them).
Enable **Manual browser login** on remote/headless machines: the login URL is
printed instead, with Copy URL / Open Browser actions.
- **API key** — paste a key from the Cursor Dashboard. It is encrypted with the
OS keychain (Electron `safeStorage`), stored outside the settings file, never
shown again, and injected only into Cursor child processes as `CURSOR_API_KEY`.
A `CURSOR_API_KEY` already present in your environment is detected and wins.

The card classifies the state locally (no network): Not installed → Sign in
required → Connected (via CLI login or API key). **Refresh** re-probes after you
install the CLI or sign in elsewhere.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Scope the "never stores credentials" claim.

This new Cursor section makes the page's earlier blanket statement inaccurate as written, because Cursor API keys are now persisted encrypted in SecretStore. Narrow the wording to Anthropic sign-in only, or explicitly call out Cursor as the exception.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/guides/using-the-agent.md` around lines 26 - 42, The docs claim “never
stores credentials” too broadly, but the new Cursor section persists API keys
encrypted in SecretStore, so narrow that statement to Anthropic sign-in only or
explicitly note Cursor as an exception. Update the wording in the agent guide so
the earlier credential-storage claim stays accurate when describing the sign-in
flow and the Cursor API key flow, using the “Connecting Cursor (preview)”
section as the reference point.

Comment on lines +77 to +83
const saveKey = async () => {
const ok = await setApiKey(keyDraft.trim());
if (ok) {
setKeyDraft('');
setShowKeyInput(false);
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Disable "Save key" when the draft is empty.

saveKey calls setApiKey(keyDraft.trim()) without guarding against an empty string, so both "Save key" buttons fire an IPC round-trip that the main process must reject. Adding a disabled prop to ActionButton and gating the buttons on !keyDraft.trim() prevents the call and gives immediate visual feedback.

Proposed fix

Add disabled support to ActionButton in controls.tsx:

 export function ActionButton({
   label,
   onClick,
   danger,
   primary,
 }: {
   label: string;
   onClick: () => void;
   danger?: boolean;
   primary?: boolean;
+  disabled?: boolean;
 }) {
   return (
     <button
       type="button"
       onClick={onClick}
+      disabled={disabled}
       className={cn(
         'rounded-md border px-2 py-1 text-[11px] transition-colors',
         primary
           ? 'border-accent/50 bg-elevated text-fg hover:border-accent'
           : danger
             ? 'border-line bg-surface-2 text-danger hover:border-danger'
             : 'border-line bg-surface-2 text-muted hover:text-fg',
       )}
     >
       {label}
     </button>
   );
 }

Then gate both save buttons in CursorProviderCard.tsx:

-            <ActionButton label="Save key" primary onClick={() => void saveKey()} />
+            <ActionButton label="Save key" primary disabled={!keyDraft.trim()} onClick={() => void saveKey()} />

Apply the same disabled={!keyDraft.trim()} to the second "Save key" button at line 242.

Also applies to: 217-217, 242-242

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/features/settings/panels/CursorProviderCard.tsx` around lines 77
- 83, The CursorProviderCard save flow should not allow an empty draft to be
submitted, since saveKey still calls setApiKey(keyDraft.trim()) even when the
input is blank. Add disabled support to ActionButton in controls.tsx, then
update both “Save key” buttons in CursorProviderCard to use
disabled={!keyDraft.trim()} so saveKey is never invoked with an empty string and
the UI gives immediate feedback.

@BotCoder254 BotCoder254 merged commit 4da94e5 into main Jul 8, 2026
4 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants