mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-04-23 22:11:33 +00:00
Compare commits
4 Commits
docs/add-r
...
v1.30.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6200e3e95 | ||
|
|
fa5a23897c | ||
|
|
c5e55adc89 | ||
|
|
09dd407648 |
51
.github/workflows/docker.yml
vendored
Normal file
51
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Docker
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
extra_tag:
|
||||
description: 'Additional tag to push alongside the template tag (e.g. v1.2.3, leave empty for none)'
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
template: [claude-code, codex, gemini]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Compute tags
|
||||
id: tags
|
||||
run: |
|
||||
TAGS="docker.io/cloudcliai/sandbox:${{ matrix.template }}"
|
||||
if [ -n "${{ inputs.extra_tag }}" ]; then
|
||||
TAGS="$TAGS,docker.io/cloudcliai/sandbox:${{ matrix.template }}-${{ inputs.extra_tag }}"
|
||||
fi
|
||||
echo "tags=$TAGS" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./docker
|
||||
file: ./docker/${{ matrix.template }}/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.tags.outputs.tags }}
|
||||
cache-from: type=gha,scope=${{ matrix.template }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.template }}
|
||||
27
CHANGELOG.md
27
CHANGELOG.md
@@ -3,6 +3,33 @@
|
||||
All notable changes to CloudCLI UI will be documented in this file.
|
||||
|
||||
|
||||
## [1.30.0](https://github.com/siteboon/claudecodeui/compare/v1.29.5...v1.30.0) (2026-04-21)
|
||||
|
||||
### New Features
|
||||
|
||||
* **i18n:** add Italian language support ([#677](https://github.com/siteboon/claudecodeui/issues/677)) ([86b6545](https://github.com/siteboon/claudecodeui/commit/86b6545c3505475ac2de0cec75cc8f86ab22aceb))
|
||||
* **i18n:** add Turkish (tr) language support ([#678](https://github.com/siteboon/claudecodeui/issues/678)) ([89b754d](https://github.com/siteboon/claudecodeui/commit/89b754d186b68f3df8aa439a2d535644406066f0)), closes [#384](https://github.com/siteboon/claudecodeui/issues/384) [#514](https://github.com/siteboon/claudecodeui/issues/514) [#525](https://github.com/siteboon/claudecodeui/issues/525) [#534](https://github.com/siteboon/claudecodeui/issues/534)
|
||||
* introduce opus 4.7 ([#682](https://github.com/siteboon/claudecodeui/issues/682)) ([c5e55ad](https://github.com/siteboon/claudecodeui/commit/c5e55adc89d0316675f90a927aa40d115958ae9f))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* iOS scrolling main chat area ([3969135](https://github.com/siteboon/claudecodeui/commit/3969135bd427fbf48f29bb3dbfedb47791ca78dc))
|
||||
* migrate PlanDisplay raw params from native details to Collapsible primitive ([fc3504e](https://github.com/siteboon/claudecodeui/commit/fc3504eaed8ca7ed9214838d148ea385b8352c31))
|
||||
* precise Claude SDK denial message detection in deriveToolStatus ([09dcea0](https://github.com/siteboon/claudecodeui/commit/09dcea05fbc8c208d931aa1f08618f0e8087392f))
|
||||
* reduce size of permission mode button tap target and provider selector on mobile ([457ca0d](https://github.com/siteboon/claudecodeui/commit/457ca0daabcaa8397f4375ee8aa2671336b648ff))
|
||||
* small mobile respnosive fixes ([25820ed](https://github.com/siteboon/claudecodeui/commit/25820ed995c1b813b1f9ed073097b08eb1d902ec))
|
||||
* small mobile respnosive fixes ([c471b5d](https://github.com/siteboon/claudecodeui/commit/c471b5d3fa6ce1968adb4cf87a15ac0e18febd20))
|
||||
|
||||
### Refactoring
|
||||
|
||||
* add primitives, plan mode display, and new session model selector ([7763e60](https://github.com/siteboon/claudecodeui/commit/7763e60fb32e34742058c055c57664a503a34d1d))
|
||||
* chat composer new design ([5758bee](https://github.com/siteboon/claudecodeui/commit/5758bee8a038ed50073dba882108617959dda82c))
|
||||
* queue primitive, tool status badges, and tool display cleanup ([ec0ff97](https://github.com/siteboon/claudecodeui/commit/ec0ff974cba213a1100b2a071b8ba533e812fe82))
|
||||
|
||||
### Maintenance
|
||||
|
||||
* add docker sandbox action ([fa5a238](https://github.com/siteboon/claudecodeui/commit/fa5a23897c086bcacf1cf5d926c650f98a0f2222))
|
||||
|
||||
## [1.29.5](https://github.com/siteboon/claudecodeui/compare/v1.29.4...v1.29.5) (2026-04-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
1028
package-lock.json
generated
1028
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@cloudcli-ai/cloudcli",
|
||||
"version": "1.29.5",
|
||||
"version": "1.30.0",
|
||||
"description": "A web-based UI for Claude Code CLI",
|
||||
"type": "module",
|
||||
"main": "dist-server/server/index.js",
|
||||
@@ -65,7 +65,7 @@
|
||||
"author": "CloudCLI UI Contributors",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.59",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.116",
|
||||
"@codemirror/lang-css": "^6.3.1",
|
||||
"@codemirror/lang-html": "^6.4.9",
|
||||
"@codemirror/lang-javascript": "^6.2.4",
|
||||
|
||||
@@ -149,9 +149,15 @@ function mapCliOptionsToSDK(options = {}) {
|
||||
|
||||
const sdkOptions = {};
|
||||
|
||||
if (process.env.CLAUDE_CLI_PATH) {
|
||||
sdkOptions.pathToClaudeCodeExecutable = process.env.CLAUDE_CLI_PATH;
|
||||
}
|
||||
// Forward all host env vars (e.g. ANTHROPIC_BASE_URL) to the subprocess.
|
||||
// Since SDK 0.2.113, options.env replaces process.env instead of overlaying it.
|
||||
sdkOptions.env = { ...process.env };
|
||||
|
||||
// Use CLAUDE_CLI_PATH if explicitly set, otherwise fall back to 'claude' on PATH.
|
||||
// The SDK 0.2.113+ looks for a bundled native binary optional dep by default;
|
||||
// this fallback ensures users who installed via the official installer still work
|
||||
// even when npm prune --production has removed those optional deps.
|
||||
sdkOptions.pathToClaudeCodeExecutable = process.env.CLAUDE_CLI_PATH || 'claude';
|
||||
|
||||
// Map working directory
|
||||
if (cwd) {
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
# Providers Module: How To Add a New Provider
|
||||
|
||||
This guide is the canonical checklist for adding a provider to the unified provider system.
|
||||
|
||||
The goal is to make provider onboarding deterministic for both humans and AI agents.
|
||||
|
||||
## Architecture Summary
|
||||
|
||||
Each provider is composed of 3 sub-capabilities behind one wrapper:
|
||||
|
||||
- `auth` (`IProviderAuth`): install/auth status
|
||||
- `mcp` (`IProviderMcp`): MCP server read/write/list for provider-native config files
|
||||
- `sessions` (`IProviderSessions`): normalize live events and fetch persisted history
|
||||
|
||||
Main interfaces:
|
||||
|
||||
- `server/shared/interfaces.ts`
|
||||
- `server/shared/types.ts`
|
||||
- `server/modules/providers/shared/base/abstract.provider.ts`
|
||||
- `server/modules/providers/shared/mcp/mcp.provider.ts`
|
||||
|
||||
Main registry/services:
|
||||
|
||||
- `server/modules/providers/provider.registry.ts`
|
||||
- `server/modules/providers/services/provider-auth.service.ts`
|
||||
- `server/modules/providers/services/mcp.service.ts`
|
||||
- `server/modules/providers/services/sessions.service.ts`
|
||||
|
||||
## Files You Must Add
|
||||
|
||||
Create `server/modules/providers/list/<provider>/` with:
|
||||
|
||||
- `<provider>.provider.ts`
|
||||
- `<provider>-auth.provider.ts`
|
||||
- `<provider>-mcp.provider.ts`
|
||||
- `<provider>-sessions.provider.ts`
|
||||
|
||||
Follow the existing structure in `claude`, `codex`, `cursor`, or `gemini`.
|
||||
|
||||
## Step-by-Step Checklist
|
||||
|
||||
1. Add provider id to shared union types.
|
||||
|
||||
- Update `server/shared/types.ts` `LLMProvider`.
|
||||
- Also update `src/types/app.ts` `LLMProvider` (frontend type).
|
||||
|
||||
2. Implement the provider wrapper.
|
||||
|
||||
- Extend `AbstractProvider`.
|
||||
- Expose `readonly auth`, `readonly mcp`, and `readonly sessions`.
|
||||
- Call `super('<provider>')`.
|
||||
|
||||
3. Implement auth provider (`<provider>-auth.provider.ts`).
|
||||
|
||||
- Implement `IProviderAuth#getStatus()`.
|
||||
- Return `{ installed, provider, authenticated, email, method, error? }`.
|
||||
- Use existing helpers from `server/shared/utils.ts` (`readObjectRecord`, `readOptionalString`, etc.) where relevant.
|
||||
|
||||
4. Implement MCP provider (`<provider>-mcp.provider.ts`).
|
||||
|
||||
- Extend `McpProvider`.
|
||||
- Define supported scopes/transports in `super('<provider>', scopes, transports)`.
|
||||
- Implement:
|
||||
- `readScopedServers(...)`
|
||||
- `writeScopedServers(...)`
|
||||
- `buildServerConfig(...)`
|
||||
- `normalizeServerConfig(...)`
|
||||
- Reuse shared validation behavior in `McpProvider` (scope/transport checks).
|
||||
|
||||
5. Implement sessions provider (`<provider>-sessions.provider.ts`).
|
||||
|
||||
- Implement `IProviderSessions`:
|
||||
- `normalizeMessage(raw, sessionId)`
|
||||
- `fetchHistory(sessionId, options)`
|
||||
- Normalize to `NormalizedMessage` using `createNormalizedMessage(...)`.
|
||||
- For filesystem-backed sessions, sanitize path inputs (`sessionId`, workspace paths) before reading files/databases.
|
||||
- Keep pagination semantics consistent:
|
||||
- `limit: null` means unbounded
|
||||
- `limit: 0` means empty page
|
||||
- include `total`, `hasMore`, `offset`, `limit` correctly
|
||||
- Ensure normalized message ids are unique per output message.
|
||||
|
||||
6. Register provider in backend registry/router.
|
||||
|
||||
- `server/modules/providers/provider.registry.ts`:
|
||||
- import the new provider class
|
||||
- add it to the `providers` map
|
||||
- `server/modules/providers/provider.routes.ts`:
|
||||
- update `parseProvider(...)` whitelist
|
||||
|
||||
7. Wire runtime execution path (outside this module).
|
||||
|
||||
If the provider should run live chat commands, also update runtime routing:
|
||||
|
||||
- `server/routes/agent.js` provider validation and dispatch
|
||||
- `server/index.js` provider routing/command handling/valid provider lists
|
||||
- Add or wire provider runtime implementation module (similar to `claude-sdk.js`, `cursor-cli.js`, `openai-codex.js`, `gemini-cli.js`)
|
||||
|
||||
8. Add model constants and UI integration (outside this module).
|
||||
|
||||
- `shared/modelConstants.js` provider model list + default
|
||||
- Provider selection and state hooks:
|
||||
- `src/components/chat/hooks/useChatProviderState.ts`
|
||||
- `src/components/chat/view/subcomponents/ProviderSelectionEmptyState.tsx`
|
||||
- Auth/login modal command text:
|
||||
- `src/components/provider-auth/view/ProviderLoginModal.tsx`
|
||||
|
||||
## Minimal Templates
|
||||
|
||||
Use these as a starting point.
|
||||
|
||||
```ts
|
||||
// <provider>.provider.ts
|
||||
import { AbstractProvider } from '@/modules/providers/shared/base/abstract.provider.js';
|
||||
import { <Provider>AuthProvider } from './<provider>-auth.provider.js';
|
||||
import { <Provider>McpProvider } from './<provider>-mcp.provider.js';
|
||||
import { <Provider>SessionsProvider } from './<provider>-sessions.provider.js';
|
||||
import type { IProviderAuth, IProviderSessions } from '@/shared/interfaces.js';
|
||||
|
||||
export class <Provider>Provider extends AbstractProvider {
|
||||
readonly mcp = new <Provider>McpProvider();
|
||||
readonly auth: IProviderAuth = new <Provider>AuthProvider();
|
||||
readonly sessions: IProviderSessions = new <Provider>SessionsProvider();
|
||||
|
||||
constructor() {
|
||||
super('<provider>');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
// <provider>-sessions.provider.ts
|
||||
import type { IProviderSessions } from '@/shared/interfaces.js';
|
||||
import type { FetchHistoryOptions, FetchHistoryResult, NormalizedMessage } from '@/shared/types.js';
|
||||
import { createNormalizedMessage, readObjectRecord } from '@/shared/utils.js';
|
||||
|
||||
const PROVIDER = '<provider>';
|
||||
|
||||
export class <Provider>SessionsProvider implements IProviderSessions {
|
||||
normalizeMessage(rawMessage: unknown, sessionId: string | null): NormalizedMessage[] {
|
||||
const raw = readObjectRecord(rawMessage);
|
||||
if (!raw) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [createNormalizedMessage({
|
||||
provider: PROVIDER,
|
||||
kind: 'text',
|
||||
role: 'assistant',
|
||||
sessionId,
|
||||
content: String(raw.content ?? ''),
|
||||
})];
|
||||
}
|
||||
|
||||
async fetchHistory(
|
||||
sessionId: string,
|
||||
options: FetchHistoryOptions = {},
|
||||
): Promise<FetchHistoryResult> {
|
||||
const { limit = null, offset = 0 } = options;
|
||||
const all: NormalizedMessage[] = [];
|
||||
|
||||
if (limit === null) {
|
||||
return { messages: all.slice(offset), total: all.length, hasMore: false, offset, limit: null };
|
||||
}
|
||||
|
||||
const start = Math.max(0, offset);
|
||||
const safeLimit = Math.max(0, limit);
|
||||
const page = safeLimit === 0 ? [] : all.slice(start, start + safeLimit);
|
||||
return {
|
||||
messages: page,
|
||||
total: all.length,
|
||||
hasMore: safeLimit === 0 ? start < all.length : start + safeLimit < all.length,
|
||||
offset: start,
|
||||
limit: safeLimit,
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AI Prompt Template
|
||||
|
||||
Use this prompt for AI-assisted implementation:
|
||||
|
||||
```text
|
||||
Add a new provider "<provider>" using the provider module architecture.
|
||||
|
||||
Requirements:
|
||||
1) Create:
|
||||
- server/modules/providers/list/<provider>/<provider>.provider.ts
|
||||
- server/modules/providers/list/<provider>/<provider>-auth.provider.ts
|
||||
- server/modules/providers/list/<provider>/<provider>-mcp.provider.ts
|
||||
- server/modules/providers/list/<provider>/<provider>-sessions.provider.ts
|
||||
2) Register in:
|
||||
- server/modules/providers/provider.registry.ts
|
||||
- server/modules/providers/provider.routes.ts (parseProvider whitelist)
|
||||
- server/shared/types.ts LLMProvider
|
||||
- src/types/app.ts LLMProvider
|
||||
3) Reuse helper utilities and follow existing style from codex/claude/cursor/gemini.
|
||||
4) Ensure sessions:
|
||||
- unique normalized message IDs
|
||||
- safe path handling for disk/db session sources
|
||||
- correct pagination for limit=null and limit=0
|
||||
5) Run:
|
||||
- npx eslint <touched server files>
|
||||
- npx tsc --noEmit -p server/tsconfig.json
|
||||
```
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
Run these after implementation:
|
||||
|
||||
```bash
|
||||
npx eslint server/modules/providers/**/*.ts server/shared/types.ts server/shared/interfaces.ts
|
||||
npx tsc --noEmit -p server/tsconfig.json
|
||||
```
|
||||
|
||||
Quick API smoke tests:
|
||||
|
||||
- `GET /api/providers/<provider>/auth/status`
|
||||
- `GET /api/providers/<provider>/mcp/servers`
|
||||
- `POST /api/providers/<provider>/mcp/servers`
|
||||
- `GET /api/sessions/<sessionId>/messages?provider=<provider>&limit=50&offset=0`
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
- Adding provider files but forgetting `provider.registry.ts`.
|
||||
- Updating backend `LLMProvider` but not frontend `src/types/app.ts`.
|
||||
- Hardcoding provider whitelists in routes and missing one location.
|
||||
- Returning duplicate message ids in `normalizeMessage`.
|
||||
- Treating `limit === 0` as unbounded instead of empty page.
|
||||
- Building file paths from raw `sessionId` without validation.
|
||||
@@ -4,10 +4,13 @@
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"lib": ["ES2022"],
|
||||
"baseUrl": ".",
|
||||
// baseUrl is the project root (one level above this config file) so that tsc-alias
|
||||
// resolves @/ imports relative to the compiled output structure in dist-server/server/.
|
||||
// With rootDir ".." tsc emits server files under dist-server/server/, so paths must
|
||||
// include the "server/" prefix to match that layout.
|
||||
"baseUrl": "..",
|
||||
"paths": {
|
||||
// In the backend config, "@" maps to the /server directory itself.
|
||||
"@/*": ["*"]
|
||||
"@/*": ["server/*"]
|
||||
},
|
||||
// The backend is still mostly JavaScript today, so allowJs lets us add a real
|
||||
// TypeScript build without forcing a large rename before the tooling is usable.
|
||||
|
||||
@@ -16,6 +16,7 @@ export const CLAUDE_MODELS = {
|
||||
{ value: "sonnet", label: "Sonnet" },
|
||||
{ value: "opus", label: "Opus" },
|
||||
{ value: "haiku", label: "Haiku" },
|
||||
{ value: "claude-opus-4-6", label: "Opus 4.6" },
|
||||
{ value: "opusplan", label: "Opus Plan" },
|
||||
{ value: "sonnet[1m]", label: "Sonnet [1M]" },
|
||||
{ value: "opus[1m]", label: "Opus [1M]" },
|
||||
|
||||
@@ -168,6 +168,7 @@ i18n
|
||||
chat: trChat,
|
||||
codeEditor: trCodeEditor,
|
||||
tasks: trTasks,
|
||||
},
|
||||
it: {
|
||||
common: itCommon,
|
||||
settings: itSettings,
|
||||
|
||||
Reference in New Issue
Block a user