Compare commits

..

5 Commits

Author SHA1 Message Date
simosmik
b0d8ea402c fix: base url config 2026-04-21 16:23:49 +00:00
Simos Mikelatos
2611a2744c Merge branch 'main' into feature/upgrade-claude-agent-sdk 2026-04-21 16:56:08 +02:00
simosmik
a20f6b8af4 feat: add "claude" as fallback in the cli path 2026-04-21 14:54:33 +00:00
Menny Even Danan
09dd407648 fix missing curly (#683) 2026-04-21 16:45:29 +02:00
simosmik
be08149e4a feat: introduce opus 4.7
- Bump claude-agent-sdk from 0.2.59 to 0.2.116
- Forward process.env to SDK subprocess so ANTHROPIC_BASE_URL and other env vars work
- Add claude-opus-4-6 as a distinct mode
2026-04-21 14:16:15 +00:00
7 changed files with 718 additions and 562 deletions

1024
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -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",

View File

@@ -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) {

View File

@@ -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.

View File

@@ -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.

View File

@@ -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]" },

View File

@@ -168,6 +168,7 @@ i18n
chat: trChat,
codeEditor: trCodeEditor,
tasks: trTasks,
},
it: {
common: itCommon,
settings: itSettings,