-
Notifications
You must be signed in to change notification settings - Fork 0
feat(claude): Integrate CLI subscription authentication with dual auth support #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,8 @@ | ||
| /** | ||
| * Claude Provider - Executes queries using Claude Agent SDK | ||
| * Claude Provider - Executes queries using Claude Agent SDK or CLI | ||
| * | ||
| * Wraps the @anthropic-ai/claude-agent-sdk for seamless integration | ||
| * with the provider architecture. | ||
| * Routes through unified client when CLI auth is configured. | ||
| * Falls back to SDK for API key auth. | ||
| */ | ||
|
|
||
| import { query, type Options, type SDKUserMessage } from '@anthropic-ai/claude-agent-sdk'; | ||
|
|
@@ -14,14 +14,17 @@ import type { | |
| ModelDefinition, | ||
| ContentBlock, | ||
| } from './types.js'; | ||
| import { getAuthStatus } from '../lib/claude-auth-manager.js'; | ||
|
|
||
| export class ClaudeProvider extends BaseProvider { | ||
| getName(): string { | ||
| return 'claude'; | ||
| } | ||
|
|
||
| /** | ||
| * Execute a query using Claude Agent SDK | ||
| * Execute a query using Claude Agent SDK or CLI (if configured) | ||
| * | ||
| * Routes through unified client when CLI auth is configured. | ||
| */ | ||
| async *executeQuery(options: ExecuteOptions): AsyncGenerator<ProviderMessage> { | ||
| const { | ||
|
|
@@ -36,6 +39,36 @@ export class ClaudeProvider extends BaseProvider { | |
| sdkSessionId, | ||
| } = options; | ||
|
|
||
| // Check if we should use CLI auth | ||
| const authStatus = await getAuthStatus(); | ||
| const useCLI = | ||
| authStatus.method === 'cli' || | ||
| (authStatus.method === 'auto' && | ||
| authStatus.cli?.installed && | ||
|
Comment on lines
+43
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In CLI-only mode, Useful? React with 👍 / 👎. |
||
| authStatus.cli?.authenticated); | ||
|
|
||
| if (useCLI) { | ||
| // Use unified client for CLI mode | ||
| console.log('[ClaudeProvider] Using CLI authentication'); | ||
| const { executeUnifiedQuery } = await import('../lib/unified-claude-client.js'); | ||
| yield* executeUnifiedQuery({ | ||
| prompt, | ||
| model, | ||
| cwd, | ||
| systemPrompt, | ||
| maxTurns, | ||
| allowedTools, | ||
| abortController, | ||
| conversationHistory, | ||
| sdkSessionId, | ||
| forceAuthMethod: 'cli', | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| // Use SDK for API key mode (original behavior) | ||
| console.log('[ClaudeProvider] Using API key authentication'); | ||
|
|
||
| // Build Claude SDK options | ||
| const defaultTools = ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash', 'WebSearch', 'WebFetch']; | ||
| const toolsToUse = allowedTools || defaultTools; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| /** | ||
| * GET /api/settings/auth - Get Claude authentication status | ||
| * | ||
| * Returns current auth configuration including: | ||
| * - Configured auth method (api_key, cli, auto) | ||
| * - CLI detection status | ||
| * - API key status | ||
| * - Effective auth method (what will actually be used) | ||
| */ | ||
|
|
||
| import type { Request, Response } from 'express'; | ||
| import type { SettingsService } from '../../../services/settings-service.js'; | ||
| import { getAuthStatus } from '../../../lib/claude-auth-manager.js'; | ||
|
|
||
| export function createAuthStatusHandler(settingsService: SettingsService) { | ||
| return async (req: Request, res: Response): Promise<void> => { | ||
| try { | ||
| const authStatus = await getAuthStatus(); | ||
| const globalSettings = await settingsService.getGlobalSettings(); | ||
|
|
||
| res.json({ | ||
| success: true, | ||
| config: { | ||
| method: globalSettings.claudeAuthMethod || 'auto', | ||
| }, | ||
| status: authStatus, | ||
| }); | ||
| } catch (error) { | ||
| console.error('[Settings] Auth status error:', error); | ||
| res.status(500).json({ | ||
| success: false, | ||
| error: error instanceof Error ? error.message : 'Failed to get auth status', | ||
| }); | ||
| } | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| /** | ||
| * PUT /api/settings/auth - Update Claude authentication method | ||
| * | ||
| * Updates the preferred authentication method and persists to settings. | ||
| * | ||
| * Body: { claudeAuthMethod: 'api_key' | 'cli' | 'auto' } | ||
| */ | ||
|
|
||
| import type { Request, Response } from 'express'; | ||
| import type { SettingsService } from '../../../services/settings-service.js'; | ||
| import { setAuthConfig, getAuthStatus } from '../../../lib/claude-auth-manager.js'; | ||
|
|
||
| export function createUpdateAuthMethodHandler(settingsService: SettingsService) { | ||
| return async (req: Request, res: Response): Promise<void> => { | ||
| try { | ||
| const { claudeAuthMethod } = req.body as { | ||
| claudeAuthMethod?: 'api_key' | 'cli' | 'auto'; | ||
| }; | ||
|
|
||
| // Validate | ||
| if ( | ||
| !claudeAuthMethod || | ||
| !['api_key', 'cli', 'auto'].includes(claudeAuthMethod) | ||
| ) { | ||
|
Comment on lines
+16
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The type assertion on const claudeAuthMethod = req.body?.claudeAuthMethod;
// Validate
if (
typeof claudeAuthMethod !== 'string' ||
!['api_key', 'cli', 'auto'].includes(claudeAuthMethod)
) { |
||
| res.status(400).json({ | ||
| success: false, | ||
| error: | ||
| 'Invalid auth method. Must be api_key, cli, or auto', | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| // Update in-memory config | ||
| setAuthConfig({ method: claudeAuthMethod }); | ||
|
|
||
| // Persist to settings | ||
| await settingsService.updateGlobalSettings({ claudeAuthMethod }); | ||
|
|
||
| // Return updated status | ||
| const authStatus = await getAuthStatus(); | ||
|
|
||
| res.json({ | ||
| success: true, | ||
| config: { | ||
| method: claudeAuthMethod, | ||
| }, | ||
| status: authStatus, | ||
| }); | ||
| } catch (error) { | ||
| console.error('[Settings] Update auth method error:', error); | ||
| res.status(500).json({ | ||
| success: false, | ||
| error: | ||
| error instanceof Error ? error.message : 'Failed to update auth method', | ||
| }); | ||
| } | ||
| }; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The create issue validator now only accepts title/description/type/priority/labels. That means any client that sends
parentIssueId(whichCreateBeadsIssueInputandBeadsService.createIssuestill support for--parent) will now get a 400 validation error, so creating subtasks/child issues via the API is silently broken. This is a regression for any UI or API consumer that relies on hierarchical Beads issues.Useful? React with 👍 / 👎.