mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 21:23:07 +00:00
feat: add Claude API provider profiles for alternative endpoints
Add support for managing multiple Claude-compatible API endpoints (z.AI GLM, AWS Bedrock, etc.) through provider profiles in settings. Features: - New ClaudeApiProfile type with base URL, API key, model mappings - Pre-configured z.AI GLM template with correct model names - Profile selector in Settings > Claude > API Profiles - Clean switching between profiles and direct Anthropic API - Immediate persistence to prevent data loss on restart Profile support added to all execution paths: - Agent service (chat) - Ideation service - Auto-mode service (feature agents, enhancements) - Simple query service (title generation, descriptions, etc.) - Backlog planning, commit messages, spec generation - GitHub issue validation, suggestions Environment variables set when profile is active: - ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN/API_KEY - ANTHROPIC_DEFAULT_HAIKU/SONNET/OPUS_MODEL - API_TIMEOUT_MS, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC
This commit is contained in:
@@ -5,7 +5,12 @@
|
|||||||
import type { SettingsService } from '../services/settings-service.js';
|
import type { SettingsService } from '../services/settings-service.js';
|
||||||
import type { ContextFilesResult, ContextFileInfo } from '@automaker/utils';
|
import type { ContextFilesResult, ContextFileInfo } from '@automaker/utils';
|
||||||
import { createLogger } from '@automaker/utils';
|
import { createLogger } from '@automaker/utils';
|
||||||
import type { MCPServerConfig, McpServerConfig, PromptCustomization } from '@automaker/types';
|
import type {
|
||||||
|
MCPServerConfig,
|
||||||
|
McpServerConfig,
|
||||||
|
PromptCustomization,
|
||||||
|
ClaudeApiProfile,
|
||||||
|
} from '@automaker/types';
|
||||||
import {
|
import {
|
||||||
mergeAutoModePrompts,
|
mergeAutoModePrompts,
|
||||||
mergeAgentPrompts,
|
mergeAgentPrompts,
|
||||||
@@ -345,3 +350,47 @@ export async function getCustomSubagents(
|
|||||||
|
|
||||||
return Object.keys(merged).length > 0 ? merged : undefined;
|
return Object.keys(merged).length > 0 ? merged : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the active Claude API profile from global settings.
|
||||||
|
* Returns undefined if no profile is active (uses direct Anthropic API).
|
||||||
|
*
|
||||||
|
* @param settingsService - Optional settings service instance
|
||||||
|
* @param logPrefix - Prefix for log messages (e.g., '[AgentService]')
|
||||||
|
* @returns Promise resolving to the active profile, or undefined if none active
|
||||||
|
*/
|
||||||
|
export async function getActiveClaudeApiProfile(
|
||||||
|
settingsService?: SettingsService | null,
|
||||||
|
logPrefix = '[SettingsHelper]'
|
||||||
|
): Promise<ClaudeApiProfile | undefined> {
|
||||||
|
if (!settingsService) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const globalSettings = await settingsService.getGlobalSettings();
|
||||||
|
const profiles = globalSettings.claudeApiProfiles || [];
|
||||||
|
const activeProfileId = globalSettings.activeClaudeApiProfileId;
|
||||||
|
|
||||||
|
// No active profile selected - use direct Anthropic API
|
||||||
|
if (!activeProfileId) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the active profile by ID
|
||||||
|
const activeProfile = profiles.find((p) => p.id === activeProfileId);
|
||||||
|
|
||||||
|
if (activeProfile) {
|
||||||
|
logger.info(`${logPrefix} Using Claude API profile: ${activeProfile.name}`);
|
||||||
|
return activeProfile;
|
||||||
|
} else {
|
||||||
|
logger.warn(
|
||||||
|
`${logPrefix} Active profile ID "${activeProfileId}" not found, falling back to direct Anthropic API`
|
||||||
|
);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`${logPrefix} Failed to load Claude API profile:`, error);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ import { BaseProvider } from './base-provider.js';
|
|||||||
import { classifyError, getUserFriendlyErrorMessage, createLogger } from '@automaker/utils';
|
import { classifyError, getUserFriendlyErrorMessage, createLogger } from '@automaker/utils';
|
||||||
|
|
||||||
const logger = createLogger('ClaudeProvider');
|
const logger = createLogger('ClaudeProvider');
|
||||||
import { getThinkingTokenBudget, validateBareModelId } from '@automaker/types';
|
import {
|
||||||
|
getThinkingTokenBudget,
|
||||||
|
validateBareModelId,
|
||||||
|
type ClaudeApiProfile,
|
||||||
|
} from '@automaker/types';
|
||||||
import type {
|
import type {
|
||||||
ExecuteOptions,
|
ExecuteOptions,
|
||||||
ProviderMessage,
|
ProviderMessage,
|
||||||
@@ -21,9 +25,19 @@ import type {
|
|||||||
// Explicit allowlist of environment variables to pass to the SDK.
|
// Explicit allowlist of environment variables to pass to the SDK.
|
||||||
// Only these vars are passed - nothing else from process.env leaks through.
|
// Only these vars are passed - nothing else from process.env leaks through.
|
||||||
const ALLOWED_ENV_VARS = [
|
const ALLOWED_ENV_VARS = [
|
||||||
|
// Authentication
|
||||||
'ANTHROPIC_API_KEY',
|
'ANTHROPIC_API_KEY',
|
||||||
'ANTHROPIC_BASE_URL',
|
|
||||||
'ANTHROPIC_AUTH_TOKEN',
|
'ANTHROPIC_AUTH_TOKEN',
|
||||||
|
// Endpoint configuration
|
||||||
|
'ANTHROPIC_BASE_URL',
|
||||||
|
'API_TIMEOUT_MS',
|
||||||
|
// Model mappings
|
||||||
|
'ANTHROPIC_DEFAULT_HAIKU_MODEL',
|
||||||
|
'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
||||||
|
'ANTHROPIC_DEFAULT_OPUS_MODEL',
|
||||||
|
// Traffic control
|
||||||
|
'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC',
|
||||||
|
// System vars (always from process.env)
|
||||||
'PATH',
|
'PATH',
|
||||||
'HOME',
|
'HOME',
|
||||||
'SHELL',
|
'SHELL',
|
||||||
@@ -33,16 +47,80 @@ const ALLOWED_ENV_VARS = [
|
|||||||
'LC_ALL',
|
'LC_ALL',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// System vars are always passed from process.env regardless of profile
|
||||||
|
const SYSTEM_ENV_VARS = ['PATH', 'HOME', 'SHELL', 'TERM', 'USER', 'LANG', 'LC_ALL'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build environment for the SDK with only explicitly allowed variables
|
* Build environment for the SDK with only explicitly allowed variables.
|
||||||
|
* When a profile is provided, uses profile configuration (clean switch - don't inherit from process.env).
|
||||||
|
* When no profile is provided, uses direct Anthropic API settings from process.env.
|
||||||
|
*
|
||||||
|
* @param profile - Optional Claude API profile for alternative endpoint configuration
|
||||||
*/
|
*/
|
||||||
function buildEnv(): Record<string, string | undefined> {
|
function buildEnv(profile?: ClaudeApiProfile): Record<string, string | undefined> {
|
||||||
const env: Record<string, string | undefined> = {};
|
const env: Record<string, string | undefined> = {};
|
||||||
for (const key of ALLOWED_ENV_VARS) {
|
|
||||||
|
if (profile) {
|
||||||
|
// Use profile configuration (clean switch - don't inherit non-system vars from process.env)
|
||||||
|
logger.debug('Building environment from Claude API profile:', { name: profile.name });
|
||||||
|
|
||||||
|
// Authentication
|
||||||
|
if (profile.useAuthToken) {
|
||||||
|
env['ANTHROPIC_AUTH_TOKEN'] = profile.apiKey;
|
||||||
|
} else {
|
||||||
|
env['ANTHROPIC_API_KEY'] = profile.apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoint configuration
|
||||||
|
env['ANTHROPIC_BASE_URL'] = profile.baseUrl;
|
||||||
|
|
||||||
|
if (profile.timeoutMs) {
|
||||||
|
env['API_TIMEOUT_MS'] = String(profile.timeoutMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Model mappings
|
||||||
|
if (profile.modelMappings?.haiku) {
|
||||||
|
env['ANTHROPIC_DEFAULT_HAIKU_MODEL'] = profile.modelMappings.haiku;
|
||||||
|
}
|
||||||
|
if (profile.modelMappings?.sonnet) {
|
||||||
|
env['ANTHROPIC_DEFAULT_SONNET_MODEL'] = profile.modelMappings.sonnet;
|
||||||
|
}
|
||||||
|
if (profile.modelMappings?.opus) {
|
||||||
|
env['ANTHROPIC_DEFAULT_OPUS_MODEL'] = profile.modelMappings.opus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traffic control
|
||||||
|
if (profile.disableNonessentialTraffic) {
|
||||||
|
env['CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC'] = '1';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Use direct Anthropic API - two modes:
|
||||||
|
// 1. API Key mode: ANTHROPIC_API_KEY from credentials/env
|
||||||
|
// 2. Claude Max plan: Uses CLI OAuth auth (SDK handles this automatically)
|
||||||
|
//
|
||||||
|
// IMPORTANT: Do NOT set any profile vars (base URL, model mappings, etc.)
|
||||||
|
// This ensures clean switching - only pass through what's in process.env
|
||||||
|
if (process.env.ANTHROPIC_API_KEY) {
|
||||||
|
env['ANTHROPIC_API_KEY'] = process.env.ANTHROPIC_API_KEY;
|
||||||
|
}
|
||||||
|
// If using Claude Max plan via CLI auth, the SDK handles auth automatically
|
||||||
|
// when no API key is provided. We don't set ANTHROPIC_AUTH_TOKEN here
|
||||||
|
// unless it was explicitly set in process.env (rare edge case).
|
||||||
|
if (process.env.ANTHROPIC_AUTH_TOKEN) {
|
||||||
|
env['ANTHROPIC_AUTH_TOKEN'] = process.env.ANTHROPIC_AUTH_TOKEN;
|
||||||
|
}
|
||||||
|
// Do NOT set ANTHROPIC_BASE_URL - let SDK use default Anthropic endpoint
|
||||||
|
// Do NOT set model mappings - use standard Claude model names
|
||||||
|
// Do NOT set CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always add system vars from process.env
|
||||||
|
for (const key of SYSTEM_ENV_VARS) {
|
||||||
if (process.env[key]) {
|
if (process.env[key]) {
|
||||||
env[key] = process.env[key];
|
env[key] = process.env[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +148,7 @@ export class ClaudeProvider extends BaseProvider {
|
|||||||
conversationHistory,
|
conversationHistory,
|
||||||
sdkSessionId,
|
sdkSessionId,
|
||||||
thinkingLevel,
|
thinkingLevel,
|
||||||
|
claudeApiProfile,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
// Convert thinking level to token budget
|
// Convert thinking level to token budget
|
||||||
@@ -82,7 +161,9 @@ export class ClaudeProvider extends BaseProvider {
|
|||||||
maxTurns,
|
maxTurns,
|
||||||
cwd,
|
cwd,
|
||||||
// Pass only explicitly allowed environment variables to SDK
|
// Pass only explicitly allowed environment variables to SDK
|
||||||
env: buildEnv(),
|
// When a profile is active, uses profile settings (clean switch)
|
||||||
|
// When no profile, uses direct Anthropic API (from process.env or CLI OAuth)
|
||||||
|
env: buildEnv(claudeApiProfile),
|
||||||
// Pass through allowedTools if provided by caller (decided by sdk-options.ts)
|
// Pass through allowedTools if provided by caller (decided by sdk-options.ts)
|
||||||
...(allowedTools && { allowedTools }),
|
...(allowedTools && { allowedTools }),
|
||||||
// AUTONOMOUS MODE: Always bypass permissions for fully autonomous operation
|
// AUTONOMOUS MODE: Always bypass permissions for fully autonomous operation
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import type {
|
|||||||
ContentBlock,
|
ContentBlock,
|
||||||
ThinkingLevel,
|
ThinkingLevel,
|
||||||
ReasoningEffort,
|
ReasoningEffort,
|
||||||
|
ClaudeApiProfile,
|
||||||
} from '@automaker/types';
|
} from '@automaker/types';
|
||||||
import { stripProviderPrefix } from '@automaker/types';
|
import { stripProviderPrefix } from '@automaker/types';
|
||||||
|
|
||||||
@@ -54,6 +55,8 @@ export interface SimpleQueryOptions {
|
|||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
/** Setting sources for CLAUDE.md loading */
|
/** Setting sources for CLAUDE.md loading */
|
||||||
settingSources?: Array<'user' | 'project' | 'local'>;
|
settingSources?: Array<'user' | 'project' | 'local'>;
|
||||||
|
/** Active Claude API profile for alternative endpoint configuration */
|
||||||
|
claudeApiProfile?: ClaudeApiProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -125,6 +128,7 @@ export async function simpleQuery(options: SimpleQueryOptions): Promise<SimpleQu
|
|||||||
reasoningEffort: options.reasoningEffort,
|
reasoningEffort: options.reasoningEffort,
|
||||||
readOnly: options.readOnly,
|
readOnly: options.readOnly,
|
||||||
settingSources: options.settingSources,
|
settingSources: options.settingSources,
|
||||||
|
claudeApiProfile: options.claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
};
|
};
|
||||||
|
|
||||||
for await (const msg of provider.executeQuery(providerOptions)) {
|
for await (const msg of provider.executeQuery(providerOptions)) {
|
||||||
@@ -207,6 +211,7 @@ export async function streamingQuery(options: StreamingQueryOptions): Promise<Si
|
|||||||
reasoningEffort: options.reasoningEffort,
|
reasoningEffort: options.reasoningEffort,
|
||||||
readOnly: options.readOnly,
|
readOnly: options.readOnly,
|
||||||
settingSources: options.settingSources,
|
settingSources: options.settingSources,
|
||||||
|
claudeApiProfile: options.claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
};
|
};
|
||||||
|
|
||||||
for await (const msg of provider.executeQuery(providerOptions)) {
|
for await (const msg of provider.executeQuery(providerOptions)) {
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ import { streamingQuery } from '../../providers/simple-query-service.js';
|
|||||||
import { parseAndCreateFeatures } from './parse-and-create-features.js';
|
import { parseAndCreateFeatures } from './parse-and-create-features.js';
|
||||||
import { getAppSpecPath } from '@automaker/platform';
|
import { getAppSpecPath } from '@automaker/platform';
|
||||||
import type { SettingsService } from '../../services/settings-service.js';
|
import type { SettingsService } from '../../services/settings-service.js';
|
||||||
import { getAutoLoadClaudeMdSetting, getPromptCustomization } from '../../lib/settings-helpers.js';
|
import {
|
||||||
|
getAutoLoadClaudeMdSetting,
|
||||||
|
getPromptCustomization,
|
||||||
|
getActiveClaudeApiProfile,
|
||||||
|
} from '../../lib/settings-helpers.js';
|
||||||
import { FeatureLoader } from '../../services/feature-loader.js';
|
import { FeatureLoader } from '../../services/feature-loader.js';
|
||||||
|
|
||||||
const logger = createLogger('SpecRegeneration');
|
const logger = createLogger('SpecRegeneration');
|
||||||
@@ -123,6 +127,9 @@ Generate ${featureCount} NEW features that build on each other logically. Rememb
|
|||||||
|
|
||||||
logger.info('Using model:', model);
|
logger.info('Using model:', model);
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(settingsService, '[FeatureGeneration]');
|
||||||
|
|
||||||
// Use streamingQuery with event callbacks
|
// Use streamingQuery with event callbacks
|
||||||
const result = await streamingQuery({
|
const result = await streamingQuery({
|
||||||
prompt,
|
prompt,
|
||||||
@@ -134,6 +141,7 @@ Generate ${featureCount} NEW features that build on each other logically. Rememb
|
|||||||
thinkingLevel,
|
thinkingLevel,
|
||||||
readOnly: true, // Feature generation only reads code, doesn't write
|
readOnly: true, // Feature generation only reads code, doesn't write
|
||||||
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
onText: (text) => {
|
onText: (text) => {
|
||||||
logger.debug(`Feature text block received (${text.length} chars)`);
|
logger.debug(`Feature text block received (${text.length} chars)`);
|
||||||
events.emit('spec-regeneration:event', {
|
events.emit('spec-regeneration:event', {
|
||||||
|
|||||||
@@ -16,7 +16,11 @@ import { streamingQuery } from '../../providers/simple-query-service.js';
|
|||||||
import { generateFeaturesFromSpec } from './generate-features-from-spec.js';
|
import { generateFeaturesFromSpec } from './generate-features-from-spec.js';
|
||||||
import { ensureAutomakerDir, getAppSpecPath } from '@automaker/platform';
|
import { ensureAutomakerDir, getAppSpecPath } from '@automaker/platform';
|
||||||
import type { SettingsService } from '../../services/settings-service.js';
|
import type { SettingsService } from '../../services/settings-service.js';
|
||||||
import { getAutoLoadClaudeMdSetting, getPromptCustomization } from '../../lib/settings-helpers.js';
|
import {
|
||||||
|
getAutoLoadClaudeMdSetting,
|
||||||
|
getPromptCustomization,
|
||||||
|
getActiveClaudeApiProfile,
|
||||||
|
} from '../../lib/settings-helpers.js';
|
||||||
|
|
||||||
const logger = createLogger('SpecRegeneration');
|
const logger = createLogger('SpecRegeneration');
|
||||||
|
|
||||||
@@ -100,6 +104,9 @@ ${prompts.appSpec.structuredSpecInstructions}`;
|
|||||||
|
|
||||||
logger.info('Using model:', model);
|
logger.info('Using model:', model);
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(settingsService, '[SpecRegeneration]');
|
||||||
|
|
||||||
let responseText = '';
|
let responseText = '';
|
||||||
let structuredOutput: SpecOutput | null = null;
|
let structuredOutput: SpecOutput | null = null;
|
||||||
|
|
||||||
@@ -132,6 +139,7 @@ Your entire response should be valid JSON starting with { and ending with }. No
|
|||||||
thinkingLevel,
|
thinkingLevel,
|
||||||
readOnly: true, // Spec generation only reads code, we write the spec ourselves
|
readOnly: true, // Spec generation only reads code, we write the spec ourselves
|
||||||
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
outputFormat: useStructuredOutput
|
outputFormat: useStructuredOutput
|
||||||
? {
|
? {
|
||||||
type: 'json_schema',
|
type: 'json_schema',
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ import { resolvePhaseModel } from '@automaker/model-resolver';
|
|||||||
import { streamingQuery } from '../../providers/simple-query-service.js';
|
import { streamingQuery } from '../../providers/simple-query-service.js';
|
||||||
import { getAppSpecPath } from '@automaker/platform';
|
import { getAppSpecPath } from '@automaker/platform';
|
||||||
import type { SettingsService } from '../../services/settings-service.js';
|
import type { SettingsService } from '../../services/settings-service.js';
|
||||||
import { getAutoLoadClaudeMdSetting } from '../../lib/settings-helpers.js';
|
import {
|
||||||
|
getAutoLoadClaudeMdSetting,
|
||||||
|
getActiveClaudeApiProfile,
|
||||||
|
} from '../../lib/settings-helpers.js';
|
||||||
import { FeatureLoader } from '../../services/feature-loader.js';
|
import { FeatureLoader } from '../../services/feature-loader.js';
|
||||||
import {
|
import {
|
||||||
extractImplementedFeatures,
|
extractImplementedFeatures,
|
||||||
@@ -157,6 +160,9 @@ export async function syncSpec(
|
|||||||
settings?.phaseModels?.specGenerationModel || DEFAULT_PHASE_MODELS.specGenerationModel;
|
settings?.phaseModels?.specGenerationModel || DEFAULT_PHASE_MODELS.specGenerationModel;
|
||||||
const { model, thinkingLevel } = resolvePhaseModel(phaseModelEntry);
|
const { model, thinkingLevel } = resolvePhaseModel(phaseModelEntry);
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(settingsService, '[SpecSync]');
|
||||||
|
|
||||||
// Use AI to analyze tech stack
|
// Use AI to analyze tech stack
|
||||||
const techAnalysisPrompt = `Analyze this project and return ONLY a JSON object with the current technology stack.
|
const techAnalysisPrompt = `Analyze this project and return ONLY a JSON object with the current technology stack.
|
||||||
|
|
||||||
@@ -185,6 +191,7 @@ Return ONLY this JSON format, no other text:
|
|||||||
thinkingLevel,
|
thinkingLevel,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
onText: (text) => {
|
onText: (text) => {
|
||||||
logger.debug(`Tech analysis text: ${text.substring(0, 100)}`);
|
logger.debug(`Tech analysis text: ${text.substring(0, 100)}`);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -25,7 +25,11 @@ import {
|
|||||||
saveBacklogPlan,
|
saveBacklogPlan,
|
||||||
} from './common.js';
|
} from './common.js';
|
||||||
import type { SettingsService } from '../../services/settings-service.js';
|
import type { SettingsService } from '../../services/settings-service.js';
|
||||||
import { getAutoLoadClaudeMdSetting, getPromptCustomization } from '../../lib/settings-helpers.js';
|
import {
|
||||||
|
getAutoLoadClaudeMdSetting,
|
||||||
|
getPromptCustomization,
|
||||||
|
getActiveClaudeApiProfile,
|
||||||
|
} from '../../lib/settings-helpers.js';
|
||||||
|
|
||||||
const featureLoader = new FeatureLoader();
|
const featureLoader = new FeatureLoader();
|
||||||
|
|
||||||
@@ -161,6 +165,9 @@ ${userPrompt}`;
|
|||||||
finalSystemPrompt = undefined; // System prompt is now embedded in the user prompt
|
finalSystemPrompt = undefined; // System prompt is now embedded in the user prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(settingsService, '[BacklogPlan]');
|
||||||
|
|
||||||
// Execute the query
|
// Execute the query
|
||||||
const stream = provider.executeQuery({
|
const stream = provider.executeQuery({
|
||||||
prompt: finalPrompt,
|
prompt: finalPrompt,
|
||||||
@@ -173,6 +180,7 @@ ${userPrompt}`;
|
|||||||
settingSources: autoLoadClaudeMd ? ['user', 'project'] : undefined,
|
settingSources: autoLoadClaudeMd ? ['user', 'project'] : undefined,
|
||||||
readOnly: true, // Plan generation only generates text, doesn't write files
|
readOnly: true, // Plan generation only generates text, doesn't write files
|
||||||
thinkingLevel, // Pass thinking level for extended thinking
|
thinkingLevel, // Pass thinking level for extended thinking
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
});
|
});
|
||||||
|
|
||||||
let responseText = '';
|
let responseText = '';
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import type { SettingsService } from '../../../services/settings-service.js';
|
|||||||
import {
|
import {
|
||||||
getAutoLoadClaudeMdSetting,
|
getAutoLoadClaudeMdSetting,
|
||||||
getPromptCustomization,
|
getPromptCustomization,
|
||||||
|
getActiveClaudeApiProfile,
|
||||||
} from '../../../lib/settings-helpers.js';
|
} from '../../../lib/settings-helpers.js';
|
||||||
|
|
||||||
const logger = createLogger('DescribeFile');
|
const logger = createLogger('DescribeFile');
|
||||||
@@ -165,6 +166,9 @@ ${contentToAnalyze}`;
|
|||||||
|
|
||||||
logger.info(`Resolved model: ${model}, thinkingLevel: ${thinkingLevel}`);
|
logger.info(`Resolved model: ${model}, thinkingLevel: ${thinkingLevel}`);
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(settingsService, '[DescribeFile]');
|
||||||
|
|
||||||
// Use simpleQuery - provider abstraction handles routing to correct provider
|
// Use simpleQuery - provider abstraction handles routing to correct provider
|
||||||
const result = await simpleQuery({
|
const result = await simpleQuery({
|
||||||
prompt,
|
prompt,
|
||||||
@@ -175,6 +179,7 @@ ${contentToAnalyze}`;
|
|||||||
thinkingLevel,
|
thinkingLevel,
|
||||||
readOnly: true, // File description only reads, doesn't write
|
readOnly: true, // File description only reads, doesn't write
|
||||||
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
});
|
});
|
||||||
|
|
||||||
const description = result.text;
|
const description = result.text;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import type { SettingsService } from '../../../services/settings-service.js';
|
|||||||
import {
|
import {
|
||||||
getAutoLoadClaudeMdSetting,
|
getAutoLoadClaudeMdSetting,
|
||||||
getPromptCustomization,
|
getPromptCustomization,
|
||||||
|
getActiveClaudeApiProfile,
|
||||||
} from '../../../lib/settings-helpers.js';
|
} from '../../../lib/settings-helpers.js';
|
||||||
|
|
||||||
const logger = createLogger('DescribeImage');
|
const logger = createLogger('DescribeImage');
|
||||||
@@ -284,6 +285,9 @@ export function createDescribeImageHandler(
|
|||||||
// Get customized prompts from settings
|
// Get customized prompts from settings
|
||||||
const prompts = await getPromptCustomization(settingsService, '[DescribeImage]');
|
const prompts = await getPromptCustomization(settingsService, '[DescribeImage]');
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(settingsService, '[DescribeImage]');
|
||||||
|
|
||||||
// Build the instruction text from centralized prompts
|
// Build the instruction text from centralized prompts
|
||||||
const instructionText = prompts.contextDescription.describeImagePrompt;
|
const instructionText = prompts.contextDescription.describeImagePrompt;
|
||||||
|
|
||||||
@@ -325,6 +329,7 @@ export function createDescribeImageHandler(
|
|||||||
thinkingLevel,
|
thinkingLevel,
|
||||||
readOnly: true, // Image description only reads, doesn't write
|
readOnly: true, // Image description only reads, doesn't write
|
||||||
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(`[${requestId}] simpleQuery completed in ${Date.now() - queryStart}ms`);
|
logger.info(`[${requestId}] simpleQuery completed in ${Date.now() - queryStart}ms`);
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ import { resolveModelString } from '@automaker/model-resolver';
|
|||||||
import { CLAUDE_MODEL_MAP, type ThinkingLevel } from '@automaker/types';
|
import { CLAUDE_MODEL_MAP, type ThinkingLevel } from '@automaker/types';
|
||||||
import { simpleQuery } from '../../../providers/simple-query-service.js';
|
import { simpleQuery } from '../../../providers/simple-query-service.js';
|
||||||
import type { SettingsService } from '../../../services/settings-service.js';
|
import type { SettingsService } from '../../../services/settings-service.js';
|
||||||
import { getPromptCustomization } from '../../../lib/settings-helpers.js';
|
import {
|
||||||
|
getPromptCustomization,
|
||||||
|
getActiveClaudeApiProfile,
|
||||||
|
} from '../../../lib/settings-helpers.js';
|
||||||
import {
|
import {
|
||||||
buildUserPrompt,
|
buildUserPrompt,
|
||||||
isValidEnhancementMode,
|
isValidEnhancementMode,
|
||||||
@@ -126,6 +129,9 @@ export function createEnhanceHandler(
|
|||||||
|
|
||||||
logger.debug(`Using model: ${resolvedModel}`);
|
logger.debug(`Using model: ${resolvedModel}`);
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(settingsService, '[EnhancePrompt]');
|
||||||
|
|
||||||
// Use simpleQuery - provider abstraction handles routing to correct provider
|
// Use simpleQuery - provider abstraction handles routing to correct provider
|
||||||
// The system prompt is combined with user prompt since some providers
|
// The system prompt is combined with user prompt since some providers
|
||||||
// don't have a separate system prompt concept
|
// don't have a separate system prompt concept
|
||||||
@@ -137,6 +143,7 @@ export function createEnhanceHandler(
|
|||||||
allowedTools: [],
|
allowedTools: [],
|
||||||
thinkingLevel,
|
thinkingLevel,
|
||||||
readOnly: true, // Prompt enhancement only generates text, doesn't write files
|
readOnly: true, // Prompt enhancement only generates text, doesn't write files
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
});
|
});
|
||||||
|
|
||||||
const enhancedText = result.text;
|
const enhancedText = result.text;
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ import { createLogger } from '@automaker/utils';
|
|||||||
import { CLAUDE_MODEL_MAP } from '@automaker/model-resolver';
|
import { CLAUDE_MODEL_MAP } from '@automaker/model-resolver';
|
||||||
import { simpleQuery } from '../../../providers/simple-query-service.js';
|
import { simpleQuery } from '../../../providers/simple-query-service.js';
|
||||||
import type { SettingsService } from '../../../services/settings-service.js';
|
import type { SettingsService } from '../../../services/settings-service.js';
|
||||||
import { getPromptCustomization } from '../../../lib/settings-helpers.js';
|
import {
|
||||||
|
getPromptCustomization,
|
||||||
|
getActiveClaudeApiProfile,
|
||||||
|
} from '../../../lib/settings-helpers.js';
|
||||||
|
|
||||||
const logger = createLogger('GenerateTitle');
|
const logger = createLogger('GenerateTitle');
|
||||||
|
|
||||||
@@ -60,6 +63,9 @@ export function createGenerateTitleHandler(
|
|||||||
const prompts = await getPromptCustomization(settingsService, '[GenerateTitle]');
|
const prompts = await getPromptCustomization(settingsService, '[GenerateTitle]');
|
||||||
const systemPrompt = prompts.titleGeneration.systemPrompt;
|
const systemPrompt = prompts.titleGeneration.systemPrompt;
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(settingsService, '[GenerateTitle]');
|
||||||
|
|
||||||
const userPrompt = `Generate a concise title for this feature:\n\n${trimmedDescription}`;
|
const userPrompt = `Generate a concise title for this feature:\n\n${trimmedDescription}`;
|
||||||
|
|
||||||
// Use simpleQuery - provider abstraction handles all the streaming/extraction
|
// Use simpleQuery - provider abstraction handles all the streaming/extraction
|
||||||
@@ -69,6 +75,7 @@ export function createGenerateTitleHandler(
|
|||||||
cwd: process.cwd(),
|
cwd: process.cwd(),
|
||||||
maxTurns: 1,
|
maxTurns: 1,
|
||||||
allowedTools: [],
|
allowedTools: [],
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
});
|
});
|
||||||
|
|
||||||
const title = result.text;
|
const title = result.text;
|
||||||
|
|||||||
@@ -34,7 +34,11 @@ import {
|
|||||||
ValidationComment,
|
ValidationComment,
|
||||||
ValidationLinkedPR,
|
ValidationLinkedPR,
|
||||||
} from './validation-schema.js';
|
} from './validation-schema.js';
|
||||||
import { getPromptCustomization } from '../../../lib/settings-helpers.js';
|
import {
|
||||||
|
getPromptCustomization,
|
||||||
|
getAutoLoadClaudeMdSetting,
|
||||||
|
getActiveClaudeApiProfile,
|
||||||
|
} from '../../../lib/settings-helpers.js';
|
||||||
import {
|
import {
|
||||||
trySetValidationRunning,
|
trySetValidationRunning,
|
||||||
clearValidationStatus,
|
clearValidationStatus,
|
||||||
@@ -43,7 +47,6 @@ import {
|
|||||||
logger,
|
logger,
|
||||||
} from './validation-common.js';
|
} from './validation-common.js';
|
||||||
import type { SettingsService } from '../../../services/settings-service.js';
|
import type { SettingsService } from '../../../services/settings-service.js';
|
||||||
import { getAutoLoadClaudeMdSetting } from '../../../lib/settings-helpers.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request body for issue validation
|
* Request body for issue validation
|
||||||
@@ -166,6 +169,9 @@ ${basePrompt}`;
|
|||||||
|
|
||||||
logger.info(`Using model: ${model}`);
|
logger.info(`Using model: ${model}`);
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(settingsService, '[IssueValidation]');
|
||||||
|
|
||||||
// Use streamingQuery with event callbacks
|
// Use streamingQuery with event callbacks
|
||||||
const result = await streamingQuery({
|
const result = await streamingQuery({
|
||||||
prompt: finalPrompt,
|
prompt: finalPrompt,
|
||||||
@@ -177,6 +183,7 @@ ${basePrompt}`;
|
|||||||
reasoningEffort: effectiveReasoningEffort,
|
reasoningEffort: effectiveReasoningEffort,
|
||||||
readOnly: true, // Issue validation only reads code, doesn't write
|
readOnly: true, // Issue validation only reads code, doesn't write
|
||||||
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
outputFormat: useStructuredOutput
|
outputFormat: useStructuredOutput
|
||||||
? {
|
? {
|
||||||
type: 'json_schema',
|
type: 'json_schema',
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ import { FeatureLoader } from '../../services/feature-loader.js';
|
|||||||
import { getAppSpecPath } from '@automaker/platform';
|
import { getAppSpecPath } from '@automaker/platform';
|
||||||
import * as secureFs from '../../lib/secure-fs.js';
|
import * as secureFs from '../../lib/secure-fs.js';
|
||||||
import type { SettingsService } from '../../services/settings-service.js';
|
import type { SettingsService } from '../../services/settings-service.js';
|
||||||
import { getAutoLoadClaudeMdSetting, getPromptCustomization } from '../../lib/settings-helpers.js';
|
import {
|
||||||
|
getAutoLoadClaudeMdSetting,
|
||||||
|
getPromptCustomization,
|
||||||
|
getActiveClaudeApiProfile,
|
||||||
|
} from '../../lib/settings-helpers.js';
|
||||||
|
|
||||||
const logger = createLogger('Suggestions');
|
const logger = createLogger('Suggestions');
|
||||||
|
|
||||||
@@ -192,6 +196,9 @@ ${prompts.suggestions.baseTemplate}`;
|
|||||||
|
|
||||||
logger.info('[Suggestions] Using model:', model);
|
logger.info('[Suggestions] Using model:', model);
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(settingsService, '[Suggestions]');
|
||||||
|
|
||||||
let responseText = '';
|
let responseText = '';
|
||||||
|
|
||||||
// Determine if we should use structured output (Claude supports it, Cursor doesn't)
|
// Determine if we should use structured output (Claude supports it, Cursor doesn't)
|
||||||
@@ -223,6 +230,7 @@ Your entire response should be valid JSON starting with { and ending with }. No
|
|||||||
thinkingLevel,
|
thinkingLevel,
|
||||||
readOnly: true, // Suggestions only reads code, doesn't write
|
readOnly: true, // Suggestions only reads code, doesn't write
|
||||||
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
settingSources: autoLoadClaudeMd ? ['user', 'project', 'local'] : undefined,
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
outputFormat: useStructuredOutput
|
outputFormat: useStructuredOutput
|
||||||
? {
|
? {
|
||||||
type: 'json_schema',
|
type: 'json_schema',
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { exec } from 'child_process';
|
|||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { existsSync } from 'fs';
|
import { existsSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
||||||
import { createLogger } from '@automaker/utils';
|
import { createLogger } from '@automaker/utils';
|
||||||
import { DEFAULT_PHASE_MODELS, isCursorModel, stripProviderPrefix } from '@automaker/types';
|
import { DEFAULT_PHASE_MODELS, isCursorModel, stripProviderPrefix } from '@automaker/types';
|
||||||
import { resolvePhaseModel } from '@automaker/model-resolver';
|
import { resolvePhaseModel } from '@automaker/model-resolver';
|
||||||
@@ -18,6 +17,7 @@ import { mergeCommitMessagePrompts } from '@automaker/prompts';
|
|||||||
import { ProviderFactory } from '../../../providers/provider-factory.js';
|
import { ProviderFactory } from '../../../providers/provider-factory.js';
|
||||||
import type { SettingsService } from '../../../services/settings-service.js';
|
import type { SettingsService } from '../../../services/settings-service.js';
|
||||||
import { getErrorMessage, logError } from '../common.js';
|
import { getErrorMessage, logError } from '../common.js';
|
||||||
|
import { getActiveClaudeApiProfile } from '../../../lib/settings-helpers.js';
|
||||||
|
|
||||||
const logger = createLogger('GenerateCommitMessage');
|
const logger = createLogger('GenerateCommitMessage');
|
||||||
const execAsync = promisify(exec);
|
const execAsync = promisify(exec);
|
||||||
@@ -74,33 +74,6 @@ interface GenerateCommitMessageErrorResponse {
|
|||||||
error: string;
|
error: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function extractTextFromStream(
|
|
||||||
stream: AsyncIterable<{
|
|
||||||
type: string;
|
|
||||||
subtype?: string;
|
|
||||||
result?: string;
|
|
||||||
message?: {
|
|
||||||
content?: Array<{ type: string; text?: string }>;
|
|
||||||
};
|
|
||||||
}>
|
|
||||||
): Promise<string> {
|
|
||||||
let responseText = '';
|
|
||||||
|
|
||||||
for await (const msg of stream) {
|
|
||||||
if (msg.type === 'assistant' && msg.message?.content) {
|
|
||||||
for (const block of msg.message.content) {
|
|
||||||
if (block.type === 'text' && block.text) {
|
|
||||||
responseText += block.text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (msg.type === 'result' && msg.subtype === 'success') {
|
|
||||||
responseText = msg.result || responseText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return responseText;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createGenerateCommitMessageHandler(
|
export function createGenerateCommitMessageHandler(
|
||||||
settingsService?: SettingsService
|
settingsService?: SettingsService
|
||||||
): (req: Request, res: Response) => Promise<void> {
|
): (req: Request, res: Response) => Promise<void> {
|
||||||
@@ -195,57 +168,52 @@ export function createGenerateCommitMessageHandler(
|
|||||||
// Get the effective system prompt (custom or default)
|
// Get the effective system prompt (custom or default)
|
||||||
const systemPrompt = await getSystemPrompt(settingsService);
|
const systemPrompt = await getSystemPrompt(settingsService);
|
||||||
|
|
||||||
let message: string;
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(
|
||||||
|
settingsService,
|
||||||
|
'[GenerateCommitMessage]'
|
||||||
|
);
|
||||||
|
|
||||||
// Route to appropriate provider based on model type
|
// Get provider for the model type
|
||||||
if (isCursorModel(model)) {
|
const provider = ProviderFactory.getProviderForModel(model);
|
||||||
// Use Cursor provider for Cursor models
|
const bareModel = stripProviderPrefix(model);
|
||||||
logger.info(`Using Cursor provider for model: ${model}`);
|
|
||||||
|
|
||||||
const provider = ProviderFactory.getProviderForModel(model);
|
// For Cursor models, combine prompts since Cursor doesn't support systemPrompt separation
|
||||||
const bareModel = stripProviderPrefix(model);
|
const effectivePrompt = isCursorModel(model)
|
||||||
|
? `${systemPrompt}\n\n${userPrompt}`
|
||||||
|
: userPrompt;
|
||||||
|
const effectiveSystemPrompt = isCursorModel(model) ? undefined : systemPrompt;
|
||||||
|
|
||||||
const cursorPrompt = `${systemPrompt}\n\n${userPrompt}`;
|
logger.info(`Using ${provider.getName()} provider for model: ${model}`);
|
||||||
|
|
||||||
let responseText = '';
|
let responseText = '';
|
||||||
const cursorStream = provider.executeQuery({
|
const stream = provider.executeQuery({
|
||||||
prompt: cursorPrompt,
|
prompt: effectivePrompt,
|
||||||
model: bareModel,
|
model: bareModel,
|
||||||
cwd: worktreePath,
|
cwd: worktreePath,
|
||||||
maxTurns: 1,
|
systemPrompt: effectiveSystemPrompt,
|
||||||
allowedTools: [],
|
maxTurns: 1,
|
||||||
readOnly: true,
|
allowedTools: [],
|
||||||
});
|
readOnly: true,
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
|
});
|
||||||
|
|
||||||
// Wrap with timeout to prevent indefinite hangs
|
// Wrap with timeout to prevent indefinite hangs
|
||||||
for await (const msg of withTimeout(cursorStream, AI_TIMEOUT_MS)) {
|
for await (const msg of withTimeout(stream, AI_TIMEOUT_MS)) {
|
||||||
if (msg.type === 'assistant' && msg.message?.content) {
|
if (msg.type === 'assistant' && msg.message?.content) {
|
||||||
for (const block of msg.message.content) {
|
for (const block of msg.message.content) {
|
||||||
if (block.type === 'text' && block.text) {
|
if (block.type === 'text' && block.text) {
|
||||||
responseText += block.text;
|
responseText += block.text;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (msg.type === 'result' && msg.subtype === 'success' && msg.result) {
|
||||||
|
// Use result if available (some providers return final text here)
|
||||||
|
responseText = msg.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
message = responseText.trim();
|
|
||||||
} else {
|
|
||||||
// Use Claude SDK for Claude models
|
|
||||||
const stream = query({
|
|
||||||
prompt: userPrompt,
|
|
||||||
options: {
|
|
||||||
model,
|
|
||||||
systemPrompt,
|
|
||||||
maxTurns: 1,
|
|
||||||
allowedTools: [],
|
|
||||||
permissionMode: 'default',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wrap with timeout to prevent indefinite hangs
|
|
||||||
message = await extractTextFromStream(withTimeout(stream, AI_TIMEOUT_MS));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const message = responseText.trim();
|
||||||
|
|
||||||
if (!message || message.trim().length === 0) {
|
if (!message || message.trim().length === 0) {
|
||||||
logger.warn('Received empty response from model');
|
logger.warn('Received empty response from model');
|
||||||
const response: GenerateCommitMessageErrorResponse = {
|
const response: GenerateCommitMessageErrorResponse = {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import {
|
|||||||
getSkillsConfiguration,
|
getSkillsConfiguration,
|
||||||
getSubagentsConfiguration,
|
getSubagentsConfiguration,
|
||||||
getCustomSubagents,
|
getCustomSubagents,
|
||||||
|
getActiveClaudeApiProfile,
|
||||||
} from '../lib/settings-helpers.js';
|
} from '../lib/settings-helpers.js';
|
||||||
|
|
||||||
interface Message {
|
interface Message {
|
||||||
@@ -274,6 +275,12 @@ export class AgentService {
|
|||||||
? await getCustomSubagents(this.settingsService, effectiveWorkDir)
|
? await getCustomSubagents(this.settingsService, effectiveWorkDir)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(
|
||||||
|
this.settingsService,
|
||||||
|
'[AgentService]'
|
||||||
|
);
|
||||||
|
|
||||||
// Load project context files (CLAUDE.md, CODE_QUALITY.md, etc.) and memory files
|
// Load project context files (CLAUDE.md, CODE_QUALITY.md, etc.) and memory files
|
||||||
// Use the user's message as task context for smart memory selection
|
// Use the user's message as task context for smart memory selection
|
||||||
const contextResult = await loadContextFiles({
|
const contextResult = await loadContextFiles({
|
||||||
@@ -378,6 +385,7 @@ export class AgentService {
|
|||||||
agents: customSubagents, // Pass custom subagents for task delegation
|
agents: customSubagents, // Pass custom subagents for task delegation
|
||||||
thinkingLevel: effectiveThinkingLevel, // Pass thinking level for Claude models
|
thinkingLevel: effectiveThinkingLevel, // Pass thinking level for Claude models
|
||||||
reasoningEffort: effectiveReasoningEffort, // Pass reasoning effort for Codex models
|
reasoningEffort: effectiveReasoningEffort, // Pass reasoning effort for Codex models
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build prompt content with images
|
// Build prompt content with images
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import {
|
|||||||
filterClaudeMdFromContext,
|
filterClaudeMdFromContext,
|
||||||
getMCPServersFromSettings,
|
getMCPServersFromSettings,
|
||||||
getPromptCustomization,
|
getPromptCustomization,
|
||||||
|
getActiveClaudeApiProfile,
|
||||||
} from '../lib/settings-helpers.js';
|
} from '../lib/settings-helpers.js';
|
||||||
import { getNotificationService } from './notification-service.js';
|
import { getNotificationService } from './notification-service.js';
|
||||||
|
|
||||||
@@ -1708,6 +1709,9 @@ Format your response as a structured markdown document.`;
|
|||||||
thinkingLevel: analysisThinkingLevel,
|
thinkingLevel: analysisThinkingLevel,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(this.settingsService, '[AutoMode]');
|
||||||
|
|
||||||
const options: ExecuteOptions = {
|
const options: ExecuteOptions = {
|
||||||
prompt,
|
prompt,
|
||||||
model: sdkOptions.model ?? analysisModel,
|
model: sdkOptions.model ?? analysisModel,
|
||||||
@@ -1717,6 +1721,7 @@ Format your response as a structured markdown document.`;
|
|||||||
abortController,
|
abortController,
|
||||||
settingSources: sdkOptions.settingSources,
|
settingSources: sdkOptions.settingSources,
|
||||||
thinkingLevel: analysisThinkingLevel, // Pass thinking level
|
thinkingLevel: analysisThinkingLevel, // Pass thinking level
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
};
|
};
|
||||||
|
|
||||||
const stream = provider.executeQuery(options);
|
const stream = provider.executeQuery(options);
|
||||||
@@ -2536,6 +2541,9 @@ This mock response was generated because AUTOMAKER_MOCK_AGENT=true was set.
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(this.settingsService, '[AutoMode]');
|
||||||
|
|
||||||
const executeOptions: ExecuteOptions = {
|
const executeOptions: ExecuteOptions = {
|
||||||
prompt: promptContent,
|
prompt: promptContent,
|
||||||
model: bareModel,
|
model: bareModel,
|
||||||
@@ -2547,6 +2555,7 @@ This mock response was generated because AUTOMAKER_MOCK_AGENT=true was set.
|
|||||||
settingSources: sdkOptions.settingSources,
|
settingSources: sdkOptions.settingSources,
|
||||||
mcpServers: Object.keys(mcpServers).length > 0 ? mcpServers : undefined, // Pass MCP servers configuration
|
mcpServers: Object.keys(mcpServers).length > 0 ? mcpServers : undefined, // Pass MCP servers configuration
|
||||||
thinkingLevel: options?.thinkingLevel, // Pass thinking level for extended thinking
|
thinkingLevel: options?.thinkingLevel, // Pass thinking level for extended thinking
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
};
|
};
|
||||||
|
|
||||||
// Execute via provider
|
// Execute via provider
|
||||||
@@ -2849,6 +2858,7 @@ After generating the revised spec, output:
|
|||||||
allowedTools: allowedTools,
|
allowedTools: allowedTools,
|
||||||
abortController,
|
abortController,
|
||||||
mcpServers: Object.keys(mcpServers).length > 0 ? mcpServers : undefined,
|
mcpServers: Object.keys(mcpServers).length > 0 ? mcpServers : undefined,
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
});
|
});
|
||||||
|
|
||||||
let revisionText = '';
|
let revisionText = '';
|
||||||
@@ -2994,6 +3004,7 @@ After generating the revised spec, output:
|
|||||||
allowedTools: allowedTools,
|
allowedTools: allowedTools,
|
||||||
abortController,
|
abortController,
|
||||||
mcpServers: Object.keys(mcpServers).length > 0 ? mcpServers : undefined,
|
mcpServers: Object.keys(mcpServers).length > 0 ? mcpServers : undefined,
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
});
|
});
|
||||||
|
|
||||||
let taskOutput = '';
|
let taskOutput = '';
|
||||||
@@ -3088,6 +3099,7 @@ After generating the revised spec, output:
|
|||||||
allowedTools: allowedTools,
|
allowedTools: allowedTools,
|
||||||
abortController,
|
abortController,
|
||||||
mcpServers: Object.keys(mcpServers).length > 0 ? mcpServers : undefined,
|
mcpServers: Object.keys(mcpServers).length > 0 ? mcpServers : undefined,
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
});
|
});
|
||||||
|
|
||||||
for await (const msg of continuationStream) {
|
for await (const msg of continuationStream) {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ import type { FeatureLoader } from './feature-loader.js';
|
|||||||
import { createChatOptions, validateWorkingDirectory } from '../lib/sdk-options.js';
|
import { createChatOptions, validateWorkingDirectory } from '../lib/sdk-options.js';
|
||||||
import { resolveModelString } from '@automaker/model-resolver';
|
import { resolveModelString } from '@automaker/model-resolver';
|
||||||
import { stripProviderPrefix } from '@automaker/types';
|
import { stripProviderPrefix } from '@automaker/types';
|
||||||
import { getPromptCustomization } from '../lib/settings-helpers.js';
|
import { getPromptCustomization, getActiveClaudeApiProfile } from '../lib/settings-helpers.js';
|
||||||
|
|
||||||
const logger = createLogger('IdeationService');
|
const logger = createLogger('IdeationService');
|
||||||
|
|
||||||
@@ -223,6 +223,12 @@ export class IdeationService {
|
|||||||
// Strip provider prefix - providers need bare model IDs
|
// Strip provider prefix - providers need bare model IDs
|
||||||
const bareModel = stripProviderPrefix(modelId);
|
const bareModel = stripProviderPrefix(modelId);
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(
|
||||||
|
this.settingsService,
|
||||||
|
'[IdeationService]'
|
||||||
|
);
|
||||||
|
|
||||||
const executeOptions: ExecuteOptions = {
|
const executeOptions: ExecuteOptions = {
|
||||||
prompt: message,
|
prompt: message,
|
||||||
model: bareModel,
|
model: bareModel,
|
||||||
@@ -232,6 +238,7 @@ export class IdeationService {
|
|||||||
maxTurns: 1, // Single turn for ideation
|
maxTurns: 1, // Single turn for ideation
|
||||||
abortController: activeSession.abortController!,
|
abortController: activeSession.abortController!,
|
||||||
conversationHistory: conversationHistory.length > 0 ? conversationHistory : undefined,
|
conversationHistory: conversationHistory.length > 0 ? conversationHistory : undefined,
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
};
|
};
|
||||||
|
|
||||||
const stream = provider.executeQuery(executeOptions);
|
const stream = provider.executeQuery(executeOptions);
|
||||||
@@ -678,6 +685,12 @@ export class IdeationService {
|
|||||||
// Strip provider prefix - providers need bare model IDs
|
// Strip provider prefix - providers need bare model IDs
|
||||||
const bareModel = stripProviderPrefix(modelId);
|
const bareModel = stripProviderPrefix(modelId);
|
||||||
|
|
||||||
|
// Get active Claude API profile for alternative endpoint configuration
|
||||||
|
const claudeApiProfile = await getActiveClaudeApiProfile(
|
||||||
|
this.settingsService,
|
||||||
|
'[IdeationService]'
|
||||||
|
);
|
||||||
|
|
||||||
const executeOptions: ExecuteOptions = {
|
const executeOptions: ExecuteOptions = {
|
||||||
prompt: prompt.prompt,
|
prompt: prompt.prompt,
|
||||||
model: bareModel,
|
model: bareModel,
|
||||||
@@ -688,6 +701,7 @@ export class IdeationService {
|
|||||||
// Disable all tools - we just want text generation, not codebase analysis
|
// Disable all tools - we just want text generation, not codebase analysis
|
||||||
allowedTools: [],
|
allowedTools: [],
|
||||||
abortController: new AbortController(),
|
abortController: new AbortController(),
|
||||||
|
claudeApiProfile, // Pass active Claude API profile for alternative endpoint configuration
|
||||||
};
|
};
|
||||||
|
|
||||||
const stream = provider.executeQuery(executeOptions);
|
const stream = provider.executeQuery(executeOptions);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { ClaudeMdSettings } from '../claude/claude-md-settings';
|
|||||||
import { ClaudeUsageSection } from '../api-keys/claude-usage-section';
|
import { ClaudeUsageSection } from '../api-keys/claude-usage-section';
|
||||||
import { SkillsSection } from './claude-settings-tab/skills-section';
|
import { SkillsSection } from './claude-settings-tab/skills-section';
|
||||||
import { SubagentsSection } from './claude-settings-tab/subagents-section';
|
import { SubagentsSection } from './claude-settings-tab/subagents-section';
|
||||||
|
import { ApiProfilesSection } from './claude-settings-tab/api-profiles-section';
|
||||||
import { ProviderToggle } from './provider-toggle';
|
import { ProviderToggle } from './provider-toggle';
|
||||||
import { Info } from 'lucide-react';
|
import { Info } from 'lucide-react';
|
||||||
|
|
||||||
@@ -45,6 +46,10 @@ export function ClaudeSettingsTab() {
|
|||||||
isChecking={isCheckingClaudeCli}
|
isChecking={isCheckingClaudeCli}
|
||||||
onRefresh={handleRefreshClaudeCli}
|
onRefresh={handleRefreshClaudeCli}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* API Profiles for Claude-compatible endpoints */}
|
||||||
|
<ApiProfilesSection />
|
||||||
|
|
||||||
<ClaudeMdSettings
|
<ClaudeMdSettings
|
||||||
autoLoadClaudeMd={autoLoadClaudeMd}
|
autoLoadClaudeMd={autoLoadClaudeMd}
|
||||||
onAutoLoadClaudeMdChange={setAutoLoadClaudeMd}
|
onAutoLoadClaudeMdChange={setAutoLoadClaudeMd}
|
||||||
|
|||||||
@@ -0,0 +1,584 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { Label } from '@/components/ui/label';
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select';
|
||||||
|
import { Switch } from '@/components/ui/switch';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import {
|
||||||
|
Cloud,
|
||||||
|
Eye,
|
||||||
|
EyeOff,
|
||||||
|
ExternalLink,
|
||||||
|
MoreVertical,
|
||||||
|
Pencil,
|
||||||
|
Plus,
|
||||||
|
Server,
|
||||||
|
Trash2,
|
||||||
|
Zap,
|
||||||
|
} from 'lucide-react';
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/components/ui/dropdown-menu';
|
||||||
|
import type { ClaudeApiProfile } from '@automaker/types';
|
||||||
|
import { CLAUDE_API_PROFILE_TEMPLATES } from '@automaker/types';
|
||||||
|
|
||||||
|
// Generate unique ID for profiles
|
||||||
|
function generateProfileId(): string {
|
||||||
|
return `profile-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask API key for display (show first 4 + last 4 chars)
|
||||||
|
function maskApiKey(key: string): string {
|
||||||
|
if (key.length <= 8) return '••••••••';
|
||||||
|
return `${key.substring(0, 4)}••••${key.substring(key.length - 4)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProfileFormData {
|
||||||
|
name: string;
|
||||||
|
baseUrl: string;
|
||||||
|
apiKey: string;
|
||||||
|
useAuthToken: boolean;
|
||||||
|
timeoutMs: string; // String for input, convert to number
|
||||||
|
modelMappings: {
|
||||||
|
haiku: string;
|
||||||
|
sonnet: string;
|
||||||
|
opus: string;
|
||||||
|
};
|
||||||
|
disableNonessentialTraffic: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emptyFormData: ProfileFormData = {
|
||||||
|
name: '',
|
||||||
|
baseUrl: '',
|
||||||
|
apiKey: '',
|
||||||
|
useAuthToken: false,
|
||||||
|
timeoutMs: '',
|
||||||
|
modelMappings: {
|
||||||
|
haiku: '',
|
||||||
|
sonnet: '',
|
||||||
|
opus: '',
|
||||||
|
},
|
||||||
|
disableNonessentialTraffic: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ApiProfilesSection() {
|
||||||
|
const {
|
||||||
|
claudeApiProfiles,
|
||||||
|
activeClaudeApiProfileId,
|
||||||
|
addClaudeApiProfile,
|
||||||
|
updateClaudeApiProfile,
|
||||||
|
deleteClaudeApiProfile,
|
||||||
|
setActiveClaudeApiProfile,
|
||||||
|
} = useAppStore();
|
||||||
|
|
||||||
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
|
const [editingProfileId, setEditingProfileId] = useState<string | null>(null);
|
||||||
|
const [formData, setFormData] = useState<ProfileFormData>(emptyFormData);
|
||||||
|
const [showApiKey, setShowApiKey] = useState(false);
|
||||||
|
const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(null);
|
||||||
|
const [currentTemplate, setCurrentTemplate] = useState<
|
||||||
|
(typeof CLAUDE_API_PROFILE_TEMPLATES)[0] | null
|
||||||
|
>(null);
|
||||||
|
|
||||||
|
const handleOpenAddDialog = (templateName?: string) => {
|
||||||
|
const template = templateName
|
||||||
|
? CLAUDE_API_PROFILE_TEMPLATES.find((t) => t.name === templateName)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (template) {
|
||||||
|
setFormData({
|
||||||
|
name: template.name,
|
||||||
|
baseUrl: template.baseUrl,
|
||||||
|
apiKey: '',
|
||||||
|
useAuthToken: template.useAuthToken,
|
||||||
|
timeoutMs: template.timeoutMs?.toString() ?? '',
|
||||||
|
modelMappings: {
|
||||||
|
haiku: template.modelMappings?.haiku ?? '',
|
||||||
|
sonnet: template.modelMappings?.sonnet ?? '',
|
||||||
|
opus: template.modelMappings?.opus ?? '',
|
||||||
|
},
|
||||||
|
disableNonessentialTraffic: template.disableNonessentialTraffic ?? false,
|
||||||
|
});
|
||||||
|
setCurrentTemplate(template);
|
||||||
|
} else {
|
||||||
|
setFormData(emptyFormData);
|
||||||
|
setCurrentTemplate(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
setEditingProfileId(null);
|
||||||
|
setShowApiKey(false);
|
||||||
|
setIsDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenEditDialog = (profile: ClaudeApiProfile) => {
|
||||||
|
// Find matching template by base URL
|
||||||
|
const template = CLAUDE_API_PROFILE_TEMPLATES.find((t) => t.baseUrl === profile.baseUrl);
|
||||||
|
|
||||||
|
setFormData({
|
||||||
|
name: profile.name,
|
||||||
|
baseUrl: profile.baseUrl,
|
||||||
|
apiKey: profile.apiKey,
|
||||||
|
useAuthToken: profile.useAuthToken ?? false,
|
||||||
|
timeoutMs: profile.timeoutMs?.toString() ?? '',
|
||||||
|
modelMappings: {
|
||||||
|
haiku: profile.modelMappings?.haiku ?? '',
|
||||||
|
sonnet: profile.modelMappings?.sonnet ?? '',
|
||||||
|
opus: profile.modelMappings?.opus ?? '',
|
||||||
|
},
|
||||||
|
disableNonessentialTraffic: profile.disableNonessentialTraffic ?? false,
|
||||||
|
});
|
||||||
|
setEditingProfileId(profile.id);
|
||||||
|
setCurrentTemplate(template ?? null);
|
||||||
|
setShowApiKey(false);
|
||||||
|
setIsDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
const profileData: ClaudeApiProfile = {
|
||||||
|
id: editingProfileId ?? generateProfileId(),
|
||||||
|
name: formData.name.trim(),
|
||||||
|
baseUrl: formData.baseUrl.trim(),
|
||||||
|
apiKey: formData.apiKey,
|
||||||
|
useAuthToken: formData.useAuthToken,
|
||||||
|
timeoutMs: formData.timeoutMs ? parseInt(formData.timeoutMs, 10) : undefined,
|
||||||
|
modelMappings:
|
||||||
|
formData.modelMappings.haiku || formData.modelMappings.sonnet || formData.modelMappings.opus
|
||||||
|
? {
|
||||||
|
...(formData.modelMappings.haiku && { haiku: formData.modelMappings.haiku }),
|
||||||
|
...(formData.modelMappings.sonnet && { sonnet: formData.modelMappings.sonnet }),
|
||||||
|
...(formData.modelMappings.opus && { opus: formData.modelMappings.opus }),
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
disableNonessentialTraffic: formData.disableNonessentialTraffic || undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (editingProfileId) {
|
||||||
|
updateClaudeApiProfile(editingProfileId, profileData);
|
||||||
|
} else {
|
||||||
|
addClaudeApiProfile(profileData);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsDialogOpen(false);
|
||||||
|
setFormData(emptyFormData);
|
||||||
|
setEditingProfileId(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = (id: string) => {
|
||||||
|
deleteClaudeApiProfile(id);
|
||||||
|
setDeleteConfirmId(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isFormValid =
|
||||||
|
formData.name.trim().length > 0 &&
|
||||||
|
formData.baseUrl.trim().length > 0 &&
|
||||||
|
formData.apiKey.length > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'rounded-2xl overflow-hidden',
|
||||||
|
'border border-border/50',
|
||||||
|
'bg-linear-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
|
||||||
|
'shadow-sm shadow-black/5'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center justify-between px-6 py-4 border-b border-border/50">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="p-2 rounded-lg bg-brand-500/10">
|
||||||
|
<Server className="w-5 h-5 text-brand-500" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold text-foreground">API Profiles</h3>
|
||||||
|
<p className="text-xs text-muted-foreground">Manage Claude-compatible API endpoints</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="sm" className="gap-2">
|
||||||
|
<Plus className="w-4 h-4" />
|
||||||
|
Add Profile
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
<DropdownMenuItem onClick={() => handleOpenAddDialog()}>
|
||||||
|
<Plus className="w-4 h-4 mr-2" />
|
||||||
|
Custom Profile
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
{CLAUDE_API_PROFILE_TEMPLATES.map((template) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
key={template.name}
|
||||||
|
onClick={() => handleOpenAddDialog(template.name)}
|
||||||
|
>
|
||||||
|
<Zap className="w-4 h-4 mr-2" />
|
||||||
|
{template.name}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="p-6 space-y-4">
|
||||||
|
{/* Active Profile Selector */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label className="text-sm font-medium">Active Profile</Label>
|
||||||
|
<Select
|
||||||
|
value={activeClaudeApiProfileId ?? 'none'}
|
||||||
|
onValueChange={(value) => setActiveClaudeApiProfile(value === 'none' ? null : value)}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-full">
|
||||||
|
<SelectValue placeholder="Select active profile" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="none">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Cloud className="w-4 h-4 text-brand-500" />
|
||||||
|
Direct Anthropic API
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
{claudeApiProfiles.map((profile) => (
|
||||||
|
<SelectItem key={profile.id} value={profile.id}>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Server className="w-4 h-4 text-muted-foreground" />
|
||||||
|
{profile.name}
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{activeClaudeApiProfileId
|
||||||
|
? 'Using custom API endpoint'
|
||||||
|
: 'Using direct Anthropic API (API key or Claude Max plan)'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Profile List */}
|
||||||
|
{claudeApiProfiles.length === 0 ? (
|
||||||
|
<div className="text-center py-8 text-muted-foreground border border-dashed border-border/50 rounded-lg">
|
||||||
|
<Server className="w-12 h-12 mx-auto mb-3 opacity-50" />
|
||||||
|
<p className="text-sm">No API profiles configured</p>
|
||||||
|
<p className="text-xs mt-1">
|
||||||
|
Add a profile to use alternative Claude-compatible endpoints
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-3">
|
||||||
|
{claudeApiProfiles.map((profile) => (
|
||||||
|
<ProfileCard
|
||||||
|
key={profile.id}
|
||||||
|
profile={profile}
|
||||||
|
isActive={profile.id === activeClaudeApiProfileId}
|
||||||
|
onEdit={() => handleOpenEditDialog(profile)}
|
||||||
|
onDelete={() => setDeleteConfirmId(profile.id)}
|
||||||
|
onSetActive={() => setActiveClaudeApiProfile(profile.id)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Add/Edit Dialog */}
|
||||||
|
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
||||||
|
<DialogContent className="max-w-lg max-h-[85vh] overflow-y-auto">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>{editingProfileId ? 'Edit API Profile' : 'Add API Profile'}</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Configure a Claude-compatible API endpoint. API keys are stored locally.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="space-y-4 py-4">
|
||||||
|
{/* Name */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="profile-name">Profile Name</Label>
|
||||||
|
<Input
|
||||||
|
id="profile-name"
|
||||||
|
value={formData.name}
|
||||||
|
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||||
|
placeholder="e.g., z.AI GLM"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Base URL */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="profile-base-url">API Base URL</Label>
|
||||||
|
<Input
|
||||||
|
id="profile-base-url"
|
||||||
|
value={formData.baseUrl}
|
||||||
|
onChange={(e) => setFormData({ ...formData, baseUrl: e.target.value })}
|
||||||
|
placeholder="https://api.example.com/v1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* API Key */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="profile-api-key">API Key</Label>
|
||||||
|
<div className="relative">
|
||||||
|
<Input
|
||||||
|
id="profile-api-key"
|
||||||
|
type={showApiKey ? 'text' : 'password'}
|
||||||
|
value={formData.apiKey}
|
||||||
|
onChange={(e) => setFormData({ ...formData, apiKey: e.target.value })}
|
||||||
|
placeholder="Enter API key"
|
||||||
|
className="pr-10"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="absolute right-0 top-0 h-full px-3 text-muted-foreground hover:text-foreground hover:bg-transparent"
|
||||||
|
onClick={() => setShowApiKey(!showApiKey)}
|
||||||
|
>
|
||||||
|
{showApiKey ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{currentTemplate?.apiKeyUrl && (
|
||||||
|
<a
|
||||||
|
href={currentTemplate.apiKeyUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="inline-flex items-center gap-1 text-xs text-brand-500 hover:text-brand-400"
|
||||||
|
>
|
||||||
|
Get API Key from {currentTemplate.name} <ExternalLink className="w-3 h-3" />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Use Auth Token */}
|
||||||
|
<div className="flex items-center justify-between py-2">
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="use-auth-token" className="font-medium">
|
||||||
|
Use Auth Token
|
||||||
|
</Label>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Use ANTHROPIC_AUTH_TOKEN instead of ANTHROPIC_API_KEY
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
id="use-auth-token"
|
||||||
|
checked={formData.useAuthToken}
|
||||||
|
onCheckedChange={(checked) => setFormData({ ...formData, useAuthToken: checked })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Timeout */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="profile-timeout">Timeout (ms)</Label>
|
||||||
|
<Input
|
||||||
|
id="profile-timeout"
|
||||||
|
type="number"
|
||||||
|
value={formData.timeoutMs}
|
||||||
|
onChange={(e) => setFormData({ ...formData, timeoutMs: e.target.value })}
|
||||||
|
placeholder="Optional, e.g., 3000000"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Model Mappings */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<Label className="font-medium">Model Mappings (Optional)</Label>
|
||||||
|
<p className="text-xs text-muted-foreground -mt-1">
|
||||||
|
Map Claude model aliases to provider-specific model names
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-3 gap-3">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label htmlFor="model-haiku" className="text-xs">
|
||||||
|
Haiku
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="model-haiku"
|
||||||
|
value={formData.modelMappings.haiku}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
modelMappings: { ...formData.modelMappings, haiku: e.target.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="e.g., GLM-4.5-Flash"
|
||||||
|
className="text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label htmlFor="model-sonnet" className="text-xs">
|
||||||
|
Sonnet
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="model-sonnet"
|
||||||
|
value={formData.modelMappings.sonnet}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
modelMappings: { ...formData.modelMappings, sonnet: e.target.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="e.g., glm-4.7"
|
||||||
|
className="text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label htmlFor="model-opus" className="text-xs">
|
||||||
|
Opus
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="model-opus"
|
||||||
|
value={formData.modelMappings.opus}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
modelMappings: { ...formData.modelMappings, opus: e.target.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="e.g., glm-4.7"
|
||||||
|
className="text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Disable Non-essential Traffic */}
|
||||||
|
<div className="flex items-center justify-between py-2">
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="disable-traffic" className="font-medium">
|
||||||
|
Disable Non-essential Traffic
|
||||||
|
</Label>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Sets CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
id="disable-traffic"
|
||||||
|
checked={formData.disableNonessentialTraffic}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
setFormData({ ...formData, disableNonessentialTraffic: checked })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={() => setIsDialogOpen(false)}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleSave} disabled={!isFormValid}>
|
||||||
|
{editingProfileId ? 'Save Changes' : 'Add Profile'}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
{/* Delete Confirmation Dialog */}
|
||||||
|
<Dialog open={!!deleteConfirmId} onOpenChange={(open) => !open && setDeleteConfirmId(null)}>
|
||||||
|
<DialogContent className="max-w-sm">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Delete Profile?</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
This will permanently delete the API profile. If this profile is currently active, you
|
||||||
|
will be switched to direct Anthropic API.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={() => setDeleteConfirmId(null)}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
onClick={() => deleteConfirmId && handleDelete(deleteConfirmId)}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProfileCardProps {
|
||||||
|
profile: ClaudeApiProfile;
|
||||||
|
isActive: boolean;
|
||||||
|
onEdit: () => void;
|
||||||
|
onDelete: () => void;
|
||||||
|
onSetActive: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProfileCard({ profile, isActive, onEdit, onDelete, onSetActive }: ProfileCardProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'rounded-lg border p-4 transition-colors',
|
||||||
|
isActive
|
||||||
|
? 'border-brand-500/50 bg-brand-500/5'
|
||||||
|
: 'border-border/50 bg-card/50 hover:border-border'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between gap-4">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<h4 className="font-medium text-foreground truncate">{profile.name}</h4>
|
||||||
|
{isActive && (
|
||||||
|
<span className="px-2 py-0.5 text-xs font-medium rounded-full bg-brand-500/20 text-brand-500">
|
||||||
|
Active
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground truncate mt-1">{profile.baseUrl}</p>
|
||||||
|
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 mt-2 text-xs text-muted-foreground">
|
||||||
|
<span>Key: {maskApiKey(profile.apiKey)}</span>
|
||||||
|
{profile.useAuthToken && <span>Auth Token</span>}
|
||||||
|
{profile.timeoutMs && <span>Timeout: {(profile.timeoutMs / 1000).toFixed(0)}s</span>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="ghost" size="icon" className="shrink-0">
|
||||||
|
<MoreVertical className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
{!isActive && (
|
||||||
|
<DropdownMenuItem onClick={onSetActive}>
|
||||||
|
<Zap className="w-4 h-4 mr-2" />
|
||||||
|
Set Active
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
<DropdownMenuItem onClick={onEdit}>
|
||||||
|
<Pencil className="w-4 h-4 mr-2" />
|
||||||
|
Edit
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={onDelete}
|
||||||
|
className="text-destructive focus:text-destructive"
|
||||||
|
>
|
||||||
|
<Trash2 className="w-4 h-4 mr-2" />
|
||||||
|
Delete
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -64,6 +64,8 @@ const SETTINGS_FIELDS_TO_SYNC = [
|
|||||||
'defaultEditorCommand',
|
'defaultEditorCommand',
|
||||||
'promptCustomization',
|
'promptCustomization',
|
||||||
'eventHooks',
|
'eventHooks',
|
||||||
|
'claudeApiProfiles',
|
||||||
|
'activeClaudeApiProfileId',
|
||||||
'projects',
|
'projects',
|
||||||
'trashedProjects',
|
'trashedProjects',
|
||||||
'currentProjectId', // ID of currently open project
|
'currentProjectId', // ID of currently open project
|
||||||
@@ -510,6 +512,8 @@ export async function refreshSettingsFromServer(): Promise<boolean> {
|
|||||||
mcpServers: serverSettings.mcpServers,
|
mcpServers: serverSettings.mcpServers,
|
||||||
defaultEditorCommand: serverSettings.defaultEditorCommand ?? null,
|
defaultEditorCommand: serverSettings.defaultEditorCommand ?? null,
|
||||||
promptCustomization: serverSettings.promptCustomization ?? {},
|
promptCustomization: serverSettings.promptCustomization ?? {},
|
||||||
|
claudeApiProfiles: serverSettings.claudeApiProfiles ?? [],
|
||||||
|
activeClaudeApiProfileId: serverSettings.activeClaudeApiProfileId ?? null,
|
||||||
projects: serverSettings.projects,
|
projects: serverSettings.projects,
|
||||||
trashedProjects: serverSettings.trashedProjects,
|
trashedProjects: serverSettings.trashedProjects,
|
||||||
projectHistory: serverSettings.projectHistory,
|
projectHistory: serverSettings.projectHistory,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import type {
|
|||||||
ModelDefinition,
|
ModelDefinition,
|
||||||
ServerLogLevel,
|
ServerLogLevel,
|
||||||
EventHook,
|
EventHook,
|
||||||
|
ClaudeApiProfile,
|
||||||
} from '@automaker/types';
|
} from '@automaker/types';
|
||||||
import {
|
import {
|
||||||
getAllCursorModelIds,
|
getAllCursorModelIds,
|
||||||
@@ -742,6 +743,10 @@ export interface AppState {
|
|||||||
// Event Hooks
|
// Event Hooks
|
||||||
eventHooks: EventHook[]; // Event hooks for custom commands or webhooks
|
eventHooks: EventHook[]; // Event hooks for custom commands or webhooks
|
||||||
|
|
||||||
|
// Claude API Profiles
|
||||||
|
claudeApiProfiles: ClaudeApiProfile[]; // Claude-compatible API endpoint profiles
|
||||||
|
activeClaudeApiProfileId: string | null; // Active profile ID (null = use direct Anthropic API)
|
||||||
|
|
||||||
// Project Analysis
|
// Project Analysis
|
||||||
projectAnalysis: ProjectAnalysis | null;
|
projectAnalysis: ProjectAnalysis | null;
|
||||||
isAnalyzing: boolean;
|
isAnalyzing: boolean;
|
||||||
@@ -1172,6 +1177,13 @@ export interface AppActions {
|
|||||||
// Event Hook actions
|
// Event Hook actions
|
||||||
setEventHooks: (hooks: EventHook[]) => void;
|
setEventHooks: (hooks: EventHook[]) => void;
|
||||||
|
|
||||||
|
// Claude API Profile actions
|
||||||
|
addClaudeApiProfile: (profile: ClaudeApiProfile) => Promise<void>;
|
||||||
|
updateClaudeApiProfile: (id: string, updates: Partial<ClaudeApiProfile>) => Promise<void>;
|
||||||
|
deleteClaudeApiProfile: (id: string) => Promise<void>;
|
||||||
|
setActiveClaudeApiProfile: (id: string | null) => Promise<void>;
|
||||||
|
setClaudeApiProfiles: (profiles: ClaudeApiProfile[]) => Promise<void>;
|
||||||
|
|
||||||
// MCP Server actions
|
// MCP Server actions
|
||||||
addMCPServer: (server: Omit<MCPServerConfig, 'id'>) => void;
|
addMCPServer: (server: Omit<MCPServerConfig, 'id'>) => void;
|
||||||
updateMCPServer: (id: string, updates: Partial<MCPServerConfig>) => void;
|
updateMCPServer: (id: string, updates: Partial<MCPServerConfig>) => void;
|
||||||
@@ -1426,6 +1438,8 @@ const initialState: AppState = {
|
|||||||
subagentsSources: ['user', 'project'] as Array<'user' | 'project'>, // Load from both sources by default
|
subagentsSources: ['user', 'project'] as Array<'user' | 'project'>, // Load from both sources by default
|
||||||
promptCustomization: {}, // Empty by default - all prompts use built-in defaults
|
promptCustomization: {}, // Empty by default - all prompts use built-in defaults
|
||||||
eventHooks: [], // No event hooks configured by default
|
eventHooks: [], // No event hooks configured by default
|
||||||
|
claudeApiProfiles: [], // No Claude API profiles configured by default
|
||||||
|
activeClaudeApiProfileId: null, // Use direct Anthropic API by default
|
||||||
projectAnalysis: null,
|
projectAnalysis: null,
|
||||||
isAnalyzing: false,
|
isAnalyzing: false,
|
||||||
boardBackgroundByProject: {},
|
boardBackgroundByProject: {},
|
||||||
@@ -2428,6 +2442,51 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
|
|||||||
// Event Hook actions
|
// Event Hook actions
|
||||||
setEventHooks: (hooks) => set({ eventHooks: hooks }),
|
setEventHooks: (hooks) => set({ eventHooks: hooks }),
|
||||||
|
|
||||||
|
// Claude API Profile actions
|
||||||
|
addClaudeApiProfile: async (profile) => {
|
||||||
|
set({ claudeApiProfiles: [...get().claudeApiProfiles, profile] });
|
||||||
|
// Sync immediately to persist profile
|
||||||
|
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
|
||||||
|
await syncSettingsToServer();
|
||||||
|
},
|
||||||
|
|
||||||
|
updateClaudeApiProfile: async (id, updates) => {
|
||||||
|
set({
|
||||||
|
claudeApiProfiles: get().claudeApiProfiles.map((p) =>
|
||||||
|
p.id === id ? { ...p, ...updates } : p
|
||||||
|
),
|
||||||
|
});
|
||||||
|
// Sync immediately to persist changes
|
||||||
|
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
|
||||||
|
await syncSettingsToServer();
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteClaudeApiProfile: async (id) => {
|
||||||
|
const currentActiveId = get().activeClaudeApiProfileId;
|
||||||
|
set({
|
||||||
|
claudeApiProfiles: get().claudeApiProfiles.filter((p) => p.id !== id),
|
||||||
|
// Clear active if the deleted profile was active
|
||||||
|
activeClaudeApiProfileId: currentActiveId === id ? null : currentActiveId,
|
||||||
|
});
|
||||||
|
// Sync immediately to persist deletion
|
||||||
|
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
|
||||||
|
await syncSettingsToServer();
|
||||||
|
},
|
||||||
|
|
||||||
|
setActiveClaudeApiProfile: async (id) => {
|
||||||
|
set({ activeClaudeApiProfileId: id });
|
||||||
|
// Sync immediately to persist active profile change
|
||||||
|
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
|
||||||
|
await syncSettingsToServer();
|
||||||
|
},
|
||||||
|
|
||||||
|
setClaudeApiProfiles: async (profiles) => {
|
||||||
|
set({ claudeApiProfiles: profiles });
|
||||||
|
// Sync immediately to persist profiles
|
||||||
|
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
|
||||||
|
await syncSettingsToServer();
|
||||||
|
},
|
||||||
|
|
||||||
// MCP Server actions
|
// MCP Server actions
|
||||||
addMCPServer: (server) => {
|
addMCPServer: (server) => {
|
||||||
const id = `mcp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
const id = `mcp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||||
|
|||||||
@@ -158,6 +158,9 @@ export type {
|
|||||||
EventHookHttpAction,
|
EventHookHttpAction,
|
||||||
EventHookAction,
|
EventHookAction,
|
||||||
EventHook,
|
EventHook,
|
||||||
|
// Claude API profile types
|
||||||
|
ClaudeApiProfile,
|
||||||
|
ClaudeApiProfileTemplate,
|
||||||
} from './settings.js';
|
} from './settings.js';
|
||||||
export {
|
export {
|
||||||
DEFAULT_KEYBOARD_SHORTCUTS,
|
DEFAULT_KEYBOARD_SHORTCUTS,
|
||||||
@@ -172,6 +175,8 @@ export {
|
|||||||
getThinkingTokenBudget,
|
getThinkingTokenBudget,
|
||||||
// Event hook constants
|
// Event hook constants
|
||||||
EVENT_HOOK_TRIGGER_LABELS,
|
EVENT_HOOK_TRIGGER_LABELS,
|
||||||
|
// Claude API profile constants
|
||||||
|
CLAUDE_API_PROFILE_TEMPLATES,
|
||||||
} from './settings.js';
|
} from './settings.js';
|
||||||
|
|
||||||
// Model display constants
|
// Model display constants
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Shared types for AI model providers
|
* Shared types for AI model providers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ThinkingLevel } from './settings.js';
|
import type { ThinkingLevel, ClaudeApiProfile } from './settings.js';
|
||||||
import type { CodexSandboxMode, CodexApprovalPolicy } from './codex.js';
|
import type { CodexSandboxMode, CodexApprovalPolicy } from './codex.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -209,6 +209,12 @@ export interface ExecuteOptions {
|
|||||||
type: 'json_schema';
|
type: 'json_schema';
|
||||||
schema: Record<string, unknown>;
|
schema: Record<string, unknown>;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* Active Claude API profile for alternative endpoint configuration.
|
||||||
|
* When set, uses profile's settings (base URL, auth, model mappings) instead of direct Anthropic API.
|
||||||
|
* When undefined, uses direct Anthropic API (via API key or Claude Max CLI OAuth).
|
||||||
|
*/
|
||||||
|
claudeApiProfile?: ClaudeApiProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -101,6 +101,72 @@ export function getThinkingTokenBudget(level: ThinkingLevel | undefined): number
|
|||||||
/** ModelProvider - AI model provider for credentials and API key management */
|
/** ModelProvider - AI model provider for credentials and API key management */
|
||||||
export type ModelProvider = 'claude' | 'cursor' | 'codex' | 'opencode';
|
export type ModelProvider = 'claude' | 'cursor' | 'codex' | 'opencode';
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Claude API Profiles - Configuration for Claude-compatible API endpoints
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClaudeApiProfile - Configuration for a Claude-compatible API endpoint
|
||||||
|
*
|
||||||
|
* Allows using alternative providers like z.AI GLM, AWS Bedrock, etc.
|
||||||
|
*/
|
||||||
|
export interface ClaudeApiProfile {
|
||||||
|
/** Unique identifier (uuid) */
|
||||||
|
id: string;
|
||||||
|
/** Display name (e.g., "z.AI GLM", "AWS Bedrock") */
|
||||||
|
name: string;
|
||||||
|
/** ANTHROPIC_BASE_URL - custom API endpoint */
|
||||||
|
baseUrl: string;
|
||||||
|
/** API key value */
|
||||||
|
apiKey: string;
|
||||||
|
/** If true, use ANTHROPIC_AUTH_TOKEN instead of ANTHROPIC_API_KEY */
|
||||||
|
useAuthToken?: boolean;
|
||||||
|
/** API_TIMEOUT_MS override in milliseconds */
|
||||||
|
timeoutMs?: number;
|
||||||
|
/** Optional model name mappings */
|
||||||
|
modelMappings?: {
|
||||||
|
/** Maps to ANTHROPIC_DEFAULT_HAIKU_MODEL */
|
||||||
|
haiku?: string;
|
||||||
|
/** Maps to ANTHROPIC_DEFAULT_SONNET_MODEL */
|
||||||
|
sonnet?: string;
|
||||||
|
/** Maps to ANTHROPIC_DEFAULT_OPUS_MODEL */
|
||||||
|
opus?: string;
|
||||||
|
};
|
||||||
|
/** Set CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 */
|
||||||
|
disableNonessentialTraffic?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Known provider templates for quick setup */
|
||||||
|
export interface ClaudeApiProfileTemplate {
|
||||||
|
name: string;
|
||||||
|
baseUrl: string;
|
||||||
|
useAuthToken: boolean;
|
||||||
|
timeoutMs?: number;
|
||||||
|
modelMappings?: ClaudeApiProfile['modelMappings'];
|
||||||
|
disableNonessentialTraffic?: boolean;
|
||||||
|
description: string;
|
||||||
|
apiKeyUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Predefined templates for known Claude-compatible providers */
|
||||||
|
export const CLAUDE_API_PROFILE_TEMPLATES: ClaudeApiProfileTemplate[] = [
|
||||||
|
{
|
||||||
|
name: 'z.AI GLM',
|
||||||
|
baseUrl: 'https://api.z.ai/api/anthropic',
|
||||||
|
useAuthToken: true,
|
||||||
|
timeoutMs: 3000000,
|
||||||
|
modelMappings: {
|
||||||
|
haiku: 'GLM-4.5-Air',
|
||||||
|
sonnet: 'GLM-4.7',
|
||||||
|
opus: 'GLM-4.7',
|
||||||
|
},
|
||||||
|
disableNonessentialTraffic: true,
|
||||||
|
description: '3× usage at fraction of cost via GLM Coding Plan',
|
||||||
|
apiKeyUrl: 'https://z.ai/manage-apikey/apikey-list',
|
||||||
|
},
|
||||||
|
// Future: Add AWS Bedrock, Google Vertex, etc.
|
||||||
|
];
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Event Hooks - Custom actions triggered by system events
|
// Event Hooks - Custom actions triggered by system events
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -650,6 +716,19 @@ export interface GlobalSettings {
|
|||||||
* @see EventHook for configuration details
|
* @see EventHook for configuration details
|
||||||
*/
|
*/
|
||||||
eventHooks?: EventHook[];
|
eventHooks?: EventHook[];
|
||||||
|
|
||||||
|
// Claude API Profiles Configuration
|
||||||
|
/**
|
||||||
|
* Claude-compatible API endpoint profiles
|
||||||
|
* Allows using alternative providers like z.AI GLM, AWS Bedrock, etc.
|
||||||
|
*/
|
||||||
|
claudeApiProfiles?: ClaudeApiProfile[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Active profile ID (null/undefined = use direct Anthropic API)
|
||||||
|
* When set, the corresponding profile's settings will be used for Claude API calls
|
||||||
|
*/
|
||||||
|
activeClaudeApiProfileId?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -896,6 +975,8 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = {
|
|||||||
skillsSources: ['user', 'project'],
|
skillsSources: ['user', 'project'],
|
||||||
enableSubagents: true,
|
enableSubagents: true,
|
||||||
subagentsSources: ['user', 'project'],
|
subagentsSources: ['user', 'project'],
|
||||||
|
claudeApiProfiles: [],
|
||||||
|
activeClaudeApiProfileId: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Default credentials (empty strings - user must provide API keys) */
|
/** Default credentials (empty strings - user must provide API keys) */
|
||||||
|
|||||||
91
package-lock.json
generated
91
package-lock.json
generated
@@ -18,7 +18,6 @@
|
|||||||
"tree-kill": "1.2.2"
|
"tree-kill": "1.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"dmg-license": "^1.0.11",
|
|
||||||
"husky": "9.1.7",
|
"husky": "9.1.7",
|
||||||
"lint-staged": "16.2.7",
|
"lint-staged": "16.2.7",
|
||||||
"prettier": "3.7.4",
|
"prettier": "3.7.4",
|
||||||
@@ -26,6 +25,9 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22.0.0 <23.0.0"
|
"node": ">=22.0.0 <23.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"dmg-license": "^1.0.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"apps/server": {
|
"apps/server": {
|
||||||
@@ -6114,7 +6116,7 @@
|
|||||||
"version": "25.0.3",
|
"version": "25.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
|
||||||
"integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
|
"integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.16.0"
|
"undici-types": "~7.16.0"
|
||||||
@@ -6124,15 +6126,15 @@
|
|||||||
"version": "7.16.0",
|
"version": "7.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/plist": {
|
"node_modules/@types/plist": {
|
||||||
"version": "3.0.5",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz",
|
||||||
"integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==",
|
"integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"xmlbuilder": ">=11.0.1"
|
"xmlbuilder": ">=11.0.1"
|
||||||
@@ -6213,8 +6215,8 @@
|
|||||||
"version": "1.10.11",
|
"version": "1.10.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz",
|
||||||
"integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==",
|
"integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==",
|
||||||
"dev": true,
|
"license": "MIT",
|
||||||
"license": "MIT"
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/ws": {
|
"node_modules/@types/ws": {
|
||||||
"version": "8.18.1",
|
"version": "8.18.1",
|
||||||
@@ -6719,7 +6721,7 @@
|
|||||||
"version": "0.8.11",
|
"version": "0.8.11",
|
||||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
|
||||||
"integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==",
|
"integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
@@ -6921,7 +6923,7 @@
|
|||||||
"version": "6.12.6",
|
"version": "6.12.6",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
@@ -7003,7 +7005,7 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -7013,7 +7015,7 @@
|
|||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-convert": "^2.0.1"
|
"color-convert": "^2.0.1"
|
||||||
@@ -7237,8 +7239,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||||
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
|
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8"
|
"node": ">=0.8"
|
||||||
}
|
}
|
||||||
@@ -7289,8 +7291,8 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
|
||||||
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
|
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -7363,7 +7365,7 @@
|
|||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -7537,7 +7539,7 @@
|
|||||||
"version": "5.7.1",
|
"version": "5.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -8033,8 +8035,8 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
|
||||||
"integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
|
"integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"slice-ansi": "^3.0.0",
|
"slice-ansi": "^3.0.0",
|
||||||
"string-width": "^4.2.0"
|
"string-width": "^4.2.0"
|
||||||
@@ -8128,7 +8130,7 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-name": "~1.1.4"
|
"color-name": "~1.1.4"
|
||||||
@@ -8141,7 +8143,7 @@
|
|||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/colorette": {
|
"node_modules/colorette": {
|
||||||
@@ -8309,8 +8311,8 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||||
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
||||||
"dev": true,
|
"license": "MIT",
|
||||||
"license": "MIT"
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/cors": {
|
"node_modules/cors": {
|
||||||
"version": "2.8.5",
|
"version": "2.8.5",
|
||||||
@@ -8329,8 +8331,8 @@
|
|||||||
"version": "3.8.0",
|
"version": "3.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz",
|
||||||
"integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
|
"integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buffer": "^5.1.0"
|
"buffer": "^5.1.0"
|
||||||
}
|
}
|
||||||
@@ -8792,8 +8794,8 @@
|
|||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz",
|
||||||
"integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==",
|
"integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
@@ -9057,7 +9059,7 @@
|
|||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/encodeurl": {
|
"node_modules/encodeurl": {
|
||||||
@@ -9682,11 +9684,11 @@
|
|||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz",
|
||||||
"integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==",
|
"integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==",
|
||||||
"dev": true,
|
|
||||||
"engines": [
|
"engines": [
|
||||||
"node >=0.6.0"
|
"node >=0.6.0"
|
||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
@@ -9698,7 +9700,7 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fast-levenshtein": {
|
"node_modules/fast-levenshtein": {
|
||||||
@@ -10648,8 +10650,8 @@
|
|||||||
"version": "1.1.7",
|
"version": "1.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
|
||||||
"integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==",
|
"integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
@@ -10678,7 +10680,7 @@
|
|||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -10866,7 +10868,7 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -11132,7 +11134,7 @@
|
|||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/json-schema-typed": {
|
"node_modules/json-schema-typed": {
|
||||||
@@ -11274,7 +11276,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11296,7 +11297,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11339,7 +11339,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11361,7 +11360,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11383,7 +11381,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11405,7 +11402,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11427,7 +11423,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11449,7 +11444,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11471,7 +11465,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -13077,8 +13070,8 @@
|
|||||||
"version": "1.7.2",
|
"version": "1.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz",
|
||||||
"integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==",
|
"integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==",
|
||||||
"dev": true,
|
"license": "MIT",
|
||||||
"license": "MIT"
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/node-api-version": {
|
"node_modules/node-api-version": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
@@ -13677,7 +13670,7 @@
|
|||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
|
||||||
"integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==",
|
"integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xmldom/xmldom": "^0.8.8",
|
"@xmldom/xmldom": "^0.8.8",
|
||||||
@@ -13793,7 +13786,7 @@
|
|||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
@@ -14593,8 +14586,8 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
|
||||||
"integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
|
"integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-styles": "^4.0.0",
|
"ansi-styles": "^4.0.0",
|
||||||
"astral-regex": "^2.0.0",
|
"astral-regex": "^2.0.0",
|
||||||
@@ -14608,7 +14601,7 @@
|
|||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||||
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
|
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6.0.0",
|
"node": ">= 6.0.0",
|
||||||
@@ -14805,7 +14798,7 @@
|
|||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"emoji-regex": "^8.0.0",
|
"emoji-regex": "^8.0.0",
|
||||||
@@ -14850,7 +14843,7 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1"
|
"ansi-regex": "^5.0.1"
|
||||||
@@ -15609,7 +15602,7 @@
|
|||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
@@ -15709,8 +15702,8 @@
|
|||||||
"version": "1.10.1",
|
"version": "1.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
|
||||||
"integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
|
"integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"assert-plus": "^1.0.0",
|
"assert-plus": "^1.0.0",
|
||||||
"core-util-is": "1.0.2",
|
"core-util-is": "1.0.2",
|
||||||
@@ -16153,7 +16146,7 @@
|
|||||||
"version": "15.1.1",
|
"version": "15.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
||||||
"integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
|
"integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user