Remove legacy backend routes that no longer have frontend or internal
callers, including the old Claude/Codex MCP APIs, unused Cursor and Codex
helper endpoints, stale TaskMaster detection/next/initialize routes,
and unused command/project helpers.
This reduces duplicated MCP behavior now handled by the provider-based
MCP API, shrinks the exposed backend surface, and removes probe/service
code that only existed for deleted endpoints.
Add an MCP settings API audit document to capture the route-usage
analysis and explain why the legacy MCP endpoints were considered safe
to remove.
Move provider authentication status endpoints out of the legacy `/api/cli` route
namespace so auth status is exposed through the same provider module that owns
provider auth and MCP behavior.
Add `GET /api/providers/:provider/auth/status` to the provider router and route
it through the provider auth service. Remove the old `cli-auth` route file and
`/api/cli` mount now that provider auth status is handled by the unified provider
API.
Update the frontend provider auth endpoint map to call the new provider-scoped
routes and rename the endpoint constant to reflect that it is no longer CLI
specific.
Move provider-backed session history and message normalization calls out of the
generic providers service so the service name reflects the behavior it owns.
Add a dedicated sessions service for listing session-capable providers,
normalizing live provider events, and fetching persisted session history through
the provider registry. Update realtime handlers and the unified messages route to
depend on `sessionsService` instead of `providersService`.
This separates session message operations from other provider concerns such as
auth and MCP, keeping the provider services easier to navigate as the module
grows.
Rename provider auth/MCP contracts to remove the overloaded Runtime suffix so
the shared interfaces read as stable provider capabilities instead of execution
implementation details.
Add a consistent provider-first auth class naming convention by renaming
ClaudeAuthProvider, CodexAuthProvider, CursorAuthProvider, and GeminiAuthProvider
to ClaudeProviderAuth, CodexProviderAuth, CursorProviderAuth, and
GeminiProviderAuth.
This keeps the provider module API easier to scan and aligns auth naming with
the main provider ownership model.
Move provider authentication status logic out of the CLI auth route so auth checks
live with the provider implementations that understand each provider's install
and credential model.
Add provider-specific auth runtime classes for Claude, Codex, Cursor, and Gemini,
and expose them through the shared provider contract as `provider.auth`. Add a
provider auth service that resolves providers through the registry and delegates
status checks via `auth.getStatus()`.
Keep the existing `/api/cli/<provider>/status` endpoints, but make them thin route
adapters over the new provider auth service. This removes duplicated route-local
credential parsing and makes auth status a first-class provider capability beside
MCP and message handling.
Move provider-specific normalizeMessage and fetchHistory logic out of the legacy
server/providers adapters and into the refactored provider classes so callers can
depend on the main provider contract instead of parallel adapter plumbing.
Add a providers service to resolve concrete providers through the registry and
delegate message normalization/history loading from realtime handlers and the
unified messages route. Add shared TypeScript message/history types and normalized
message helpers so provider implementations and callers use the same contract.
Remove the old adapter registry/files now that Claude, Codex, Cursor, and Gemini
implement the required behavior directly.
- Add provider registry to manage LLM providers (Claude, Codex, Cursor, Gemini).
- Create provider routes for MCP server operations (list, upsert, delete, run).
- Implement MCP service for handling server operations and validations.
- Introduce abstract provider class and MCP provider base for shared functionality.
- Add tests for MCP server operations across different providers and scopes.
- Define shared interfaces and types for MCP functionality.
- Implement utility functions for handling JSON config files and API responses.
* fix: remove project dependency from settings controller and onboarding
* fix(settings): remove onClose prop from useSettingsController args
* chore: tailwind classes order
* refactor: move provider auth status management to custom hook
* refactor: rename SessionProvider to LLMProvider
* feat(frontend): support for @ alias based imports)
* fix: replace init.sql with schema.js
* fix: refactor database initialization to use schema.js for SQL statements
* feat(server): add a real backend TypeScript build and enforce module boundaries
The backend had started to grow beyond what the frontend-only tooling setup could
support safely. We were still running server code directly from /server, linting
mainly the client, and relying on path assumptions such as "../.." that only
worked in the source layout. That created three problems:
- backend alias imports were hard to resolve consistently in the editor, ESLint,
and the runtime
- server code had no enforced module boundary rules, so cross-module deep imports
could bypass intended public entry points
- building the backend into a separate output directory would break repo-level
lookups for package.json, .env, dist, and public assets because those paths
were derived from source-only relative assumptions
This change makes the backend tooling explicit and runtime-safe.
A dedicated backend TypeScript config now lives in server/tsconfig.json, with
tsconfig.server.json reduced to a compatibility shim. This gives the language
service and backend tooling a canonical project rooted in /server while still
preserving top-level compatibility for any existing references. The backend alias
mapping now resolves relative to /server, which avoids colliding with the
frontend's "@/..." -> "src/*" mapping.
The package scripts were updated so development runs through tsx with the backend
tsconfig, build now produces a compiled backend in dist-server, and typecheck/lint
cover both client and server. A new build-server.mjs script runs TypeScript and
tsc-alias and cleans dist-server first, which prevents stale compiled files from
shadowing current source files after refactors.
To make the compiled backend behave the same as the source backend, runtime path
resolution was centralized in server/utils/runtime-paths.js. Instead of assuming
fixed relative paths from each module, server entry points now resolve the actual
app root and server root at runtime. That keeps package.json, .env, dist, public,
and default database paths stable whether code is executed from /server or from
/dist-server/server.
ESLint was expanded from a frontend-only setup into a backend-aware one. The
backend now uses import resolution tied to the backend tsconfig so aliased imports
resolve correctly in linting, import ordering matches the frontend style, and
unused/duplicate imports are surfaced consistently.
Most importantly, eslint-plugin-boundaries now enforces server module boundaries.
Files under server/modules can no longer import another module's internals
directly. Cross-module imports must go through that module's barrel file
(index.ts/index.js). boundaries/no-unknown was also enabled so alias-resolution
gaps cannot silently bypass the rule.
Together, these changes make the backend buildable, keep runtime path resolution
stable after compilation, align server tooling with the client where appropriate,
and enforce a stricter modular architecture for server code.
* fix: update package.json to include dist-server in files and remove tsconfig.server.json
* refactor: remove build-server.mjs and inline its logic into package.json scripts
* fix: update paths in package.json and bin.js to use dist-server directory
* feat(eslint): add backend shared types and enforce compile-time contract for imports
* fix(eslint): update shared types pattern
---------
Co-authored-by: Haileyesus <something@gmail.com>
- The existing setup was using the text reader endpoint for downloading
files `fsPromises.readFile(..., 'utf8')` at line 801. This was incorrect
- In the old Files tab flow, the client then took that decoded string
and rebuilt it as a text blob. That UTF-8 decode/re-encode step changes
raw bytes, so the downloaded file no longer matches the original.
Folder ZIP export had the same problem for any binary file inside the
archive.
Co-authored-by: Haileyesus <something@gmail.com>
- Add provider adapter layer (server/providers/) with registry pattern
- Claude, Cursor, Codex, Gemini adapters normalize native formats to NormalizedMessage
- Shared types.js defines ProviderAdapter interface and message kinds
- Registry enables polymorphic provider lookup
- Add unified REST endpoint: GET /api/sessions/:id/messages?provider=...
- Replaces four provider-specific message endpoints with one
- Delegates to provider adapters via registry
- Add frontend session-keyed store (useSessionStore)
- Per-session Map with serverMessages/realtimeMessages/merged
- Dedup by ID, stale threshold for re-fetch, background session accumulation
- No localStorage for messages — backend JSONL is source of truth
- Add normalizedToChatMessages converter (useChatMessages)
- Converts NormalizedMessage[] to existing ChatMessage[] UI format
- Wire unified store into ChatInterface, useChatSessionState, useChatRealtimeHandlers
- Session switch uses store cache for instant render
- Background WebSocket messages routed to correct session slot
* feat: add WebSocket proxy for plugin backends
Adds /plugin-ws/:name route that proxies authenticated WebSocket
connections to plugin server subprocesses, enabling real-time
bidirectional communication for plugins like web-terminal.
* chore: update README with the plugin
* fix: remove --host from npm run server command
Running `vite --host` exposes the dev server on all interfaces. However,
we should expose it on all interfaces only when `HOST` is set to `0.0.0.0`.
Otherwise, we should assume the user wants to bind to a host of their choice
and not expose the server on the network.
* fix: use src hostname for redirecting to Vite in development
Previously, the server redirected to Vite using `localhost` as the hostname.
Even if the user was using HOST="0.0.0.0", if they connected to server from
another device on the same network using `http://<host_ip>:3001`, the
server would redirect them to `http://localhost:5173`, which would not
work since `localhost` would resolve to the client's machine instead of the server.
* fix: use shared network hosts configuration for better proxy setup
- Normalize all localhost variants to 'localhost' for consistent proxy
configuration in Vite and server setup.
- use one source of truth for network hosts functions by moving them to
a shared
- log production and development urls
* refactor: rename PORT to SERVER_PORT for clarity
* chore: add comments explaining host normalization
* fix: add legacy PORT env fallback for server port configuration
* fix: add fallback for SERVER_PORT using PORT environment variable
---------
Co-authored-by: Haileyesus <something@gmail.com>
Co-authored-by: Simos Mikelatos <simosmik@gmail.com>
- detect Claude auth from ~/.claude/settings.json env values
- treat ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN from settings as authenticated
- keep existing .credentials.json OAuth detection unchanged
* feat: introduce notification system and claude notifications
* fix(sw): prevent caching of API requests and WebSocket upgrades
* default to false for webpush notifications and translations for the button
* fix: notifications orchestrator and add a notification when first enabled
* fix: remove unused state update and dependency in settings controller hook
* fix: show notifications settings tab
* fix: add notifications for response completion for all providers
* feat: show session name in notification and don't reload tab on clicking
--- the notification
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Haileyesus <something@gmail.com>
* fix(shell): copy terminal selections from xterm buffer
The shell was delegating Cmd/Ctrl+C to document.execCommand('copy'),
which copied the rendered DOM selection instead of xterm's logical
buffer text. Wrapped values like login URLs could pick up row
whitespace or line breaks and break when pasted.
Route keyboard copy through terminal.getSelection() and the shared
clipboard helper. Also intercept native copy events on the terminal
container so mouse selection and browser copy actions use the same
normalized terminal text.
Remove the copy listener during teardown to avoid leaking handlers
across terminal reinitialization.
* fix(shell): restore terminal focus when switching to the shell tab
Pass shell activity state from MainContent through StandaloneShell and use it
inside Shell to explicitly focus the xterm instance once the terminal is both
initialized and connected.
Previously, switching to the Shell tab left focus on the tab button because
isActive was being ignored and the terminal never called focus() after the tab
activation lifecycle completed. As a result, users had to click inside the
terminal before keyboard input would be accepted.
This change wires isActive through the shell stack, removes the unused prop
handling in Shell, and adds a focus effect that runs when the shell becomes
active and ready. The effect uses both requestAnimationFrame and a zero-delay
timeout so focus is applied reliably after rendering and connection state
updates settle.
This restores immediate typing when opening the shell tab and also improves the
reconnect path by re-focusing the terminal after the shell connection is ready.
* fix(shell): remove fallback command for codex and claude session resumes
The `|| claude` and `|| codex` fallback commands were causing errors as they are not valid commands.
* fix: use fallback while resuming codex and claude sessions for linux and windows
* feat(git): add revert latest local commit action in git panel
Add a complete revert-local-commit flow so users can undo the most recent
local commit directly from the Git header, placed before the refresh icon.
Backend
- add POST /api/git/revert-local-commit endpoint in server/routes/git.js
- validate project input and repository state before executing git operations
- revert latest commit with `git reset --soft HEAD~1` to keep changes staged
- handle initial-commit edge case by deleting HEAD ref when no parent exists
- return clear success and error responses for UI consumption
Frontend
- add useRevertLocalCommit hook to encapsulate API call and loading state
- wire hook into GitPanel and refresh git data after successful revert
- add new toolbar action in GitPanelHeader before refresh icon
- route action through existing confirmation modal flow
- disable action while request is in flight and show activity indicator
Shared UI and typing updates
- extend ConfirmActionType with `revertLocalCommit`
- add confirmation title, label, and style mappings for new action
- render RotateCcw icon for revert action in ConfirmActionModal
Result
- users can safely undo the latest local commit from the UI
- reverted commit changes remain staged for immediate recommit/edit workflows
* fix: run cursor with --trust if workspace trust prompt is detected, and retry once
* fix(git): handle repositories without commits across status and remote flows
Improve git route behavior for repositories initialized with `git init` but with
no commits yet. Previously, several routes called `git rev-parse --abbrev-ref HEAD`,
which fails before the first commit and caused noisy console errors plus a broken
Git panel state.
What changed
- add `getGitErrorDetails` helper to normalize git process failure text
- add `isMissingHeadRevisionError` helper to detect no-HEAD/no-revision cases
- add `getCurrentBranchName` helper:
- uses `git symbolic-ref --short HEAD` first (works before first commit)
- falls back to `git rev-parse --abbrev-ref HEAD` for detached HEAD and edge cases
- add `repositoryHasCommits` helper using `git rev-parse --verify HEAD`
Status route improvements
- replace inline branch/HEAD error handling with shared helpers
- keep returning valid branch + `hasCommits: false` for fresh repositories
Remote status improvements
- avoid hard failure when repository has no commits
- return a safe, non-error payload with:
- `hasUpstream: false`
- `ahead: 0`, `behind: 0`
- detected remote name when remotes exist
- message: "Repository has no commits yet"
- preserve existing upstream detection behavior for repositories with commits
Consistency updates
- switch fetch/pull/push/publish branch lookup to shared `getCurrentBranchName`
to ensure the same branch-resolution behavior everywhere
Result
- `git init` repositories no longer trigger `rev-parse HEAD` ambiguity failures
- Git panel remains usable before the first commit
- backend branch detection is centralized and consistent across git operations
* fix(git): resolve file paths against repo root for paths with spaces
Fix path resolution for git file operations when project directories include spaces
or when API calls are issued from subdirectories inside a repository.
Problem
- operations like commit/discard/diff could receive file paths that were valid from
repo root but were executed from a nested cwd
- this produced pathspec errors like:
- warning: could not open directory '4/4/'
- fatal: pathspec '4/hello_world.ts' did not match any files
Root cause
- file arguments were passed directly to git commands using the project cwd
- inconsistent path forms (repo-root-relative vs cwd-relative) were not normalized
Changes
- remove unsafe fallback decode in `getActualProjectPath`; fail explicitly when the
real project path cannot be resolved
- add repository/file-path helpers:
- `getRepositoryRootPath`
- `normalizeRepositoryRelativeFilePath`
- `parseStatusFilePaths`
- `buildFilePathCandidates`
- `resolveRepositoryFilePath`
- update file-based git endpoints to resolve paths before executing commands:
- GET `/diff`
- GET `/file-with-diff`
- POST `/commit`
- POST `/generate-commit-message`
- POST `/discard`
- POST `/delete-untracked`
- stage/restore/reset operations now use `--` before pathspecs for safer argument
separation
Behavioral impact
- git operations now work reliably for repositories under directories containing spaces
- file operations are consistent even when project cwd is a subdirectory of repo root
- endpoint responses continue to preserve existing payload shapes
Verification
- syntax check: `node --check server/routes/git.js`
- typecheck: `npm run typecheck`
- reproduced failing scenario in a temp path with spaces; confirmed root-resolved
path staging succeeds where subdir-cwd pathspec previously failed
* fix(git-ui): prevent large commit diffs from freezing the history tab
Harden commit diff loading/rendering so opening a very large commit no longer hangs
the browser tab.
Problem
- commit history diff viewer rendered every diff line as a React node
- very large commits could create thousands of nodes and lock the UI thread
- backend always returned full commit patch payloads, amplifying frontend pressure
Backend safeguards
- add `COMMIT_DIFF_CHARACTER_LIMIT` (500,000 chars) in git routes
- update GET `/api/git/commit-diff` to truncate oversized diff payloads
- include `isTruncated` flag in response for observability/future UI handling
- append truncation marker text when server-side limit is applied
Frontend safeguards
- update `GitDiffViewer` to use bounded preview rendering:
- character cap: 200,000
- line cap: 1,500
- move diff preprocessing into `useMemo` for stable, one-pass preview computation
- show a clear "Large diff preview" notice when truncation is active
Impact
- commit diff expansion remains responsive even for high-change commits
- UI still shows useful diff content while avoiding tab lockups
- changes apply to shared diff viewer usage and improve resilience broadly
Validation
- `node --check server/routes/git.js`
- `npm run typecheck`
- `npx eslint src/components/git-panel/view/shared/GitDiffViewer.tsx`
* fix(cursor-chat): stabilize first-run UX and clean cursor message rendering
Fix three Cursor chat regressions observed on first message runs:
1. Full-screen UI refresh/flicker after first response.
2. Internal wrapper tags rendered in user messages.
3. Duplicate assistant message on response finalization.
Root causes
- Project refresh from chat completion used the global loading path,
toggling app-level loading UI.
- Cursor history conversion rendered raw internal wrapper payloads
as user-visible message text.
- Cursor response handling could finalize through overlapping stream/
result paths, and stdout chunk parsing could split JSON lines.
Changes
- Added non-blocking project refresh plumbing for chat/session flows.
- Introduced fetch options in useProjectsState (showLoadingState flag).
- Added refreshProjectsSilently() to update metadata without global loading UI.
- Wired window.refreshProjects to refreshProjectsSilently in AppContent.
- Added Cursor user-message sanitization during history conversion.
- Added extractCursorUserQuery() to keep only <user_query> payload.
- Added sanitizeCursorUserMessageText() to strip internal wrappers:
<user_info>, <agent_skills>, <available_skills>,
<environment_context>, <environment_info>.
- Applied sanitization only for role === 'user' in
convertCursorSessionMessages().
- Hardened Cursor backend stream parsing and finalization.
- Added line-buffered stdout parser for chunk-split JSON payloads.
- Flushed trailing unterminated stdout line on process close.
- Removed redundant content_block_stop emission on Cursor result.
- Added frontend duplicate guard in cursor-result handling.
- Skips a second assistant bubble when final result text equals
already-rendered streamed content.
Code comments
- Added focused comments describing silent refresh behavior,
tag stripping rationale, duplicate guard behavior, and line buffering.
Validation
- ESLint passes for touched files.
- Production build succeeds.
Files
- server/cursor-cli.js
- src/components/app/AppContent.tsx
- src/components/chat/hooks/useChatRealtimeHandlers.ts
- src/components/chat/utils/messageTransforms.ts
- src/hooks/useProjectsState.ts
---------
Co-authored-by: Haileyesus <something@gmail.com>
Co-authored-by: Simos Mikelatos <simosmik@gmail.com>
* fix(security): prevent shell injection in WebSocket handler and harden auth
- Replace hardcoded JWT secret with auto-generated per-installation secret
- Add database validation to WebSocket authentication
- Add token expiration (7d) with auto-refresh
- Validate projectPath and sessionId in shell handler
- Use cwd instead of shell string interpolation for project paths
- Add CORS exposedHeaders for token refresh
* fix: small fix on languages
* feat: new plugin system
* Potential fix for code scanning alert no. 312: Uncontrolled data used in path expression
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
* Update manifest.json
* feat(plugins): add SVG icon support with authenticated inline rendering
* fix: coderabbit changes and new plugin name & repo
* fix: design changes to plugins settings tab
* fix(plugins): prevent git arg injection, add repo URL detection
* fix: lint errors and deleting plugin error on windows
* fix: coderabbit nitpick comments
* fix(plugins): harden path traversal and respect enabled state
Use realpathSync to canonicalize paths before the plugin asset
boundary check, preventing symlink-based traversal bypasses that
could escape the plugin directory.
PluginTabContent now guards on plugin.enabled before mounting the
plugin module, and re-mounts when the enabled state changes so
toggling a plugin takes effect without a page reload.
PluginIcon safely handles a missing iconFile prop and skips
processing non-OK fetch responses instead of attempting to parse
error bodies as SVG.
Register 'plugins' as a known main tab so the settings router
preserves the tab on navigation.
* fix(plugins): support concurrent plugin updates
Replace single updatingPlugin string state with a Set to allow
multiple plugins to update simultaneously. Also disable the update
button and show a descriptive tooltip when a plugin has no git
remote configured.
* fix(plugins): async shutdown and asset/RPC fixes
Await stopPluginServer/stopAllPlugins in signal handlers and route
handlers so process exit and state transitions wait for clean plugin
shutdown instead of racing ahead.
Validate asset paths are regular files before streaming to prevent
directory traversal returning unexpected content; add a stream error
handler to avoid unhandled crashes on read failures.
Fix RPC proxy body detection to use the content-length header instead
of Object.keys, so falsy but valid JSON payloads (null, false, 0, {})
are forwarded correctly to plugin servers.
Track in-flight start operations via a startingPlugins map to prevent
duplicate concurrent plugin starts.
* refactor(git-panel): simplify setCommitMessage with plain function
* fix(plugins): harden input validation and scan reliability
- Validate plugin names against [a-zA-Z0-9_-] allowlist in
manifest and asset routes to prevent path traversal via URL
- Strip embedded credentials (user:pass@) from git remote URLs
before exposing them to the client
- Skip .tmp-* directories during scan to avoid partial installs
from in-progress updates appearing as broken plugins
- Deduplicate plugins sharing the same manifest name to prevent
ambiguous state
- Guard RPC proxy error handler against writing to an already-sent
response, preventing uncaught exceptions on aborted requests
* fix(git-panel): reset changes view on project switch
* refactor: move plugin content to /view folder
* fix: resolve type error in MobileNav and PluginTabContent components
---------
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Haileyesus <118998054+blackmammoth@users.noreply.github.com>
Co-authored-by: Haileyesus <something@gmail.com>
* feat: add full-text search across conversations in sidebar
Add a search mode toggle (Projects/Conversations) to the sidebar search bar.
In Conversations mode, search text content across all JSONL session files
with debounced API calls, highlighted snippets, and click-to-navigate results.
* fix: address PR review feedback - session summary tracking, search sequence invalidation, fallback navigation, SSE streaming
- Track session summaries per-session in a Map instead of file-scoped variable
- Increment searchSeqRef when clearing conversation search to invalidate in-flight requests
- Add fallback session navigation when session not loaded in sidebar paging
- Stream search results via SSE for progressive display with progress indicator
* feat(search): add Codex/Gemini search and scroll-to-message navigation
- Search now includes Codex sessions (JSONL from ~/.codex/sessions/) and
Gemini sessions (in-memory via sessionManager) in addition to Claude
- Search results include provider info and display a provider badge
- Click handler resolves the correct provider instead of hardcoding claude
- Clicking a search result loads all messages and scrolls to the matched
message with a highlight flash animation
* fix(search): Codex search path matching and scroll reliability
- Fix Codex search scanning all sessions for every project by checking
session_meta cwd match BEFORE scanning messages (was inflating match
count and hitting limit before reaching later projects)
- Fix Codex search missing user messages in response_item entries
(role=user with input_text content parts)
- Fix scroll-to-message being overridden by initial scrollToBottom
using searchScrollActiveRef to inhibit competing scroll effects
- Fix snippet matching using contiguous substring instead of
filtered words (which created non-existent phrases)
* feat(search): add Gemini CLI session support for search and history viewing
Gemini CLI sessions stored in ~/.gemini/tmp/<project>/chats/*.json are now
indexed for conversation search and can be loaded for viewing. Previously
only sessions created through the UI (sessionManager) were searchable.
* fix(search): full-word matching and longer highlight flash
- Search now uses word boundaries (\b) instead of substring matching,
so "hi" no longer matches "this"
- Highlight flash extended to 4s with thicker outline and subtle
background tint for better visibility
* Add support for ANTHROPIC_API_KEY environment variable authentication detection
This commit enhances Claude authentication detection to support both the
ANTHROPIC_API_KEY environment variable and the OAuth credentials file,
matching the authentication priority order used by the Claude Agent SDK.
- Updated checkClaudeCredentials() function in server/routes/cli-auth.js
to check ANTHROPIC_API_KEY environment variable first, then fall back
to ~/.claude/.credentials.json OAuth tokens
- Modified /api/cli-auth/claude/status endpoint to return authentication
method indicator ('api_key' or 'credentials_file')
- Added comprehensive JSDoc documentation with priority order explanation
and official Claude documentation citations
1. ANTHROPIC_API_KEY environment variable (highest priority)
2. ~/.claude/.credentials.json OAuth tokens (fallback)
This priority order matches the Claude Agent SDK's authentication behavior,
ensuring consistency between how we detect authentication and how the SDK
actually authenticates.
The /api/cli-auth/claude/status endpoint now returns:
- method: 'api_key' when using ANTHROPIC_API_KEY environment variable
- method: 'credentials_file' when using OAuth credentials file
- method: null when not authenticated
This is backward compatible as existing code checking the 'authenticated'
field will continue to work.
- https://support.claude.com/en/articles/12304248-managing-api-key-environment-variables-in-claude-code
Claude Agent SDK prioritizes environment variables over subscriptions
- https://platform.claude.com/docs/en/agent-sdk/overview
Official Claude Agent SDK authentication documentation
When ANTHROPIC_API_KEY is set, API calls are charged via pay-as-you-go
rates instead of subscription rates, even if the user is logged in with
a claude.ai subscription.
* UI: hide Claude login when API key auth is used
An API key overrides anything else with Claude SDK (and claude-code). There is no need to show this button in API key cases.
* fix: preserve pending permission requests across WebSocket reconnections
- Store WebSocketWriter reference in active sessions for reconnection
- Add reconnectSessionWriter() to swap writer when client reconnects
- Add getPendingApprovalsForSession() to query pending permissions by session
- Add get-pending-permissions WebSocket message handler on server
- Add pending-permissions-response handler on frontend
- Query pending permissions on WebSocket reconnect and session change
- Reconnect SDK output writer when client resumes an active session
* fix: address CodeRabbit review feedback for websocket-permission PR
- Use consistent session matching in pending-permissions-response handler,
checking both currentSessionId and selectedSession.id (matching the
session-status handler pattern)
- Guard get-pending-permissions with isClaudeSDKSessionActive check to
prevent returning permissions for inactive sessions
# Features
- File drag and drop upload: Support uploading files and folders via drag and drop
- Binary file handling: Detect binary files and display a friendly message instead of trying to edit them
- Folder download: Download folders as ZIP files (using JSZip library)
- Context menu integration: Full right-click context menu for file operations (rename, delete, copy path, download, new file/folder)
The modelUsage debug log ran on every streamed SDK message, but
modelUsage is only populated on result messages. This produced
repeated "Model was sent using: []" console output for every
non-result message during streaming.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
fix wrong regex replace of Claude project path
related #447, reopen due to forced-push
to reproduce error steps, let's try
Create a folder with @ in name like @test
Add this folder as new project in CloudCLI
Choose Claude tool in new Session
Star by typing sth 'hi'
In the dev tools, you will see errors ajax response said that session does not find for 'some-session-id'
The main problem is current encode path doesn't encode '@' to '-' as Claude did
I reversed code Claude-SDK, file 'cli.js' to find exactly regex (using in PR) that used to encode path under ~/.claude/projects/<encoded-project-name-path/<session-id>.jsonl
* feat: integrate Gemini AI agent provider
- Core Backend: Ported gemini-cli.js and gemini-response-handler.js to establish the CLI bridge. Registered 'gemini' as an active provider within index.js.
- Core Frontend: Extended QuickSettingsPanel.jsx, Settings.jsx, and AgentListItem.jsx to render the Gemini provider option, models (gemini-pro, gemini-flash, etc.), and handle OAuth states.
- WebSocket Pipeline: Added support for gemini-command executions in backend and payload processing of gemini-response and gemini-error streams in useChatRealtimeHandlers.ts. Resolved JSON double-stringification and sessionId stripping issues in the transmission handler.
- Platform Compatibility: Added scripts/fix-node-pty.js postinstall script and modified posix_spawnp calls with sh -c wrapper to prevent ENOEXEC and MacOS permission errors when spawning the gemini headless binary.
- UX & Design: Imported official Google Gemini branding via GeminiLogo.jsx and gemini-ai-icon.svg. Updated translations (chat.json) for en, zh-CN, and ko locales.
* fix: propagate gemini permission mode from settings to cli
- Added Gemini Permissions UI in Settings to toggle Auto Edit and YOLO modes
- Synced gemini permission mode to localStorage
- Passed permissionMode in useChatComposerState for Gemini commands
- Mapped frontend permission modes to --yolo and --approval-mode options in gemini-cli.js
* feat(gemini): Refactor Gemini CLI integration to use stream-json
- Replaced regex buffering text-system with NDJSON stream parsing
- Added fallback for restricted models like gemini-3.1-pro-preview
* feat(gemini): Render tool_use and tool_result UI bubbles
- Forwarded gemini tool NDJSON objects to the websocket
- Added React state handlers in useChatRealtimeHandlers to match Claude's tool UI behavior
* feat(gemini): Add native session resumption and UI token tracking
- Captured cliSessionId from init events to map ClaudeCodeUI's chat sessionId directly into Gemini's internal session manager.
- Updated gemini-cli.js spawn arguments to append the --resume proxy flag instead of naively dumping the accumulated chat history into the command prompt.
- Handled result stream objects by proxying total_tokens back into the frontend's claude-status tracker to natively populate the UI label.
- Eliminated gemini-3 model proxy filter entirely.
* fix(gemini): Fix static 'Claude' name rendering in chat UI header
- Added "gemini": "Gemini" translation strings to messageTypes across English, Korean, and Chinese loc dictionaries.
- Updated AssistantThinkingIndicator and MessageComponent ternary checks to identify provider === 'gemini' and render the appropriate brand label instead of statically defaulting to Claude.
* feat: Add Gemini session persistence API mapping and Sidebar UI
* fix(gemini): Watch ~/.gemini/sessions for live UI updates
Added the .gemini/sessions directory to PROVIDER_WATCH_PATHS so that Chokidar emits projects_updated websocket events when new Gemini sessions are created or modified, fixing live sidebar updates.
* fix(gemini): Fix Gemini authentication status display in Settings UI
- Injected 'checkGeminiAuthStatus' into the Settings.jsx React effect hook so that the UI can poll and render the 'geminiAuthStatus' state.
- Updated 'checkGeminiCredentials()' inside server/routes/cli-auth.js to read from '~/.gemini/oauth_creds.json' and '~/.gemini/google_accounts.json', resolving the email address correctly.
* Use logo-only icon for gemini
* feat(gemini): Add Gemini 3 preview models to UI selection list
* Fix Gemini CLI session resume bug and PR #422 review nitpicks
* Fix Gemini tool calls disappearing from UI after completion
* fix(gemini): resolve outstanding PR #422 feedback and stabilize gemini CLI timeouts
* fix(gemini): resolve resume flag and shell session initialization issues
This commit addresses the remaining PR comments for the Gemini CLI integration:
- Moves the `--resume` flag logic outside the prompt command block, ensuring Gemini sessions correctly resume even when a new prompt isn't passed.
- Updates `handleShellConnection` to correctly lookup the native `cliSessionId` from the internal `sessionId` when spawning Gemini sessions in a plain shell.
- Refactors dynamic import of `sessionManager.js` back to a native static import for code consistency.
* chore: fix TypeScript errors and remove gemini CLI dependency
* fix: use cross-spawn on Windows to resolve gemini.cmd correctly
---------
Co-authored-by: Haileyesus <118998054+blackmammoth@users.noreply.github.com>
* feat: add HOST environment variable for configurable bind address
Allow users to specify which host/IP address the servers should bind to
via the HOST environment variable. Defaults to 0.0.0.0 (all interfaces)
to maintain backward compatibility.
This enables users to restrict the server to localhost only (127.0.0.1)
for security purposes or bind to a specific network interface.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: use correct proxy host when HOST is set to specific interface
When HOST is set to a specific interface (e.g., 192.168.1.100), the Vite
proxy needs to connect to that same host, not localhost. This fix:
- Adds proxyHost logic that uses localhost when HOST=0.0.0.0, otherwise
uses the specific HOST value for proxy targets
- Adds DISPLAY_HOST to show a user-friendly URL in console output
(0.0.0.0 isn't a connectable address)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Michael Fork <mjfork@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Haileyesus <118998054+blackmammoth@users.noreply.github.com>