@x-all-in-one/coding-helper 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,75 @@
1
+ /**
2
+ * API Key 和网络验证模块
3
+ * 通过调用模型列表 API 来验证 API Key 的有效性和网络连通性
4
+ */
5
+ const VALIDATION_URL = 'https://code-api.x-aio.com/v1/models';
6
+ /**
7
+ * 验证 API Key 的有效性和网络连通性
8
+ *
9
+ * @param apiKey - API Key
10
+ * @returns 验证结果,包含 valid 状态和可选的错误信息
11
+ */
12
+ export async function validateApiKey(apiKey) {
13
+ try {
14
+ const controller = new AbortController();
15
+ const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 秒超时
16
+ const response = await fetch(VALIDATION_URL, {
17
+ method: 'GET',
18
+ headers: {
19
+ 'Authorization': `Bearer ${apiKey}`,
20
+ 'Content-Type': 'application/json'
21
+ },
22
+ signal: controller.signal
23
+ });
24
+ clearTimeout(timeoutId);
25
+ if (response.status === 401) {
26
+ return {
27
+ valid: false,
28
+ error: 'invalid_api_key',
29
+ message: 'API Key is invalid or expired'
30
+ };
31
+ }
32
+ if (response.ok) {
33
+ // 尝试解析响应以确保是有效的 JSON
34
+ try {
35
+ const data = await response.json();
36
+ if (data && data.object === 'list') {
37
+ return { valid: true };
38
+ }
39
+ }
40
+ catch {
41
+ // JSON 解析失败但响应成功,仍然视为有效
42
+ return { valid: true };
43
+ }
44
+ return { valid: true };
45
+ }
46
+ // 其他 HTTP 错误
47
+ return {
48
+ valid: false,
49
+ error: 'unknown_error',
50
+ message: `HTTP ${response.status}: ${response.statusText}`
51
+ };
52
+ }
53
+ catch (error) {
54
+ // 网络错误或超时
55
+ if (error instanceof Error) {
56
+ if (error.name === 'AbortError') {
57
+ return {
58
+ valid: false,
59
+ error: 'network_error',
60
+ message: 'Request timeout'
61
+ };
62
+ }
63
+ return {
64
+ valid: false,
65
+ error: 'network_error',
66
+ message: error.message
67
+ };
68
+ }
69
+ return {
70
+ valid: false,
71
+ error: 'network_error',
72
+ message: 'Network connection failed'
73
+ };
74
+ }
75
+ }
@@ -0,0 +1,79 @@
1
+ export declare const DEFAULT_CONFIG: {
2
+ ANTHROPIC_BASE_URL: string;
3
+ ANTHROPIC_API_KEY: string;
4
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: string;
5
+ ANTHROPIC_DEFAULT_SONNET_MODEL: string;
6
+ ANTHROPIC_DEFAULT_OPUS_MODEL: string;
7
+ };
8
+ export interface ModelConfig {
9
+ apiKey: string;
10
+ haikuModel: string;
11
+ sonnetModel: string;
12
+ opusModel: string;
13
+ baseUrl?: string;
14
+ }
15
+ export interface ClaudeCodeSettingsConfig {
16
+ env?: {
17
+ ANTHROPIC_API_KEY?: string;
18
+ ANTHROPIC_BASE_URL?: string;
19
+ ANTHROPIC_DEFAULT_HAIKU_MODEL?: string;
20
+ ANTHROPIC_DEFAULT_SONNET_MODEL?: string;
21
+ ANTHROPIC_DEFAULT_OPUS_MODEL?: string;
22
+ API_TIMEOUT_MS?: string;
23
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC?: number;
24
+ [key: string]: any;
25
+ };
26
+ [key: string]: any;
27
+ }
28
+ export interface ClaudeCodeOnboardingConfig {
29
+ hasCompletedOnboarding?: boolean;
30
+ [key: string]: any;
31
+ }
32
+ export declare class ClaudeCodeManager {
33
+ private static instance;
34
+ private settingsPath;
35
+ private onboardingConfigPath;
36
+ private constructor();
37
+ static getInstance(): ClaudeCodeManager;
38
+ /**
39
+ * 确保配置目录存在
40
+ */
41
+ private ensureConfigDir;
42
+ /**
43
+ * 读取 settings.json 配置
44
+ */
45
+ getSettings(): ClaudeCodeSettingsConfig;
46
+ /**
47
+ * 保存 settings.json 配置
48
+ */
49
+ saveSettings(config: ClaudeCodeSettingsConfig): void;
50
+ /**
51
+ * 读取 onboarding 配置 (~/.claude.json)
52
+ */
53
+ private getOnboardingConfig;
54
+ /**
55
+ * 保存 onboarding 配置 (~/.claude.json)
56
+ */
57
+ private saveOnboardingConfig;
58
+ /**
59
+ * 确保 .claude.json 中有 hasCompletedOnboarding: true
60
+ */
61
+ private ensureOnboardingCompleted;
62
+ /**
63
+ * 从 API 获取可用的模型列表
64
+ */
65
+ fetchAvailableModels(apiKey: string): Promise<string[]>;
66
+ /**
67
+ * 保存模型配置到 Claude Code
68
+ */
69
+ saveModelConfig(config: ModelConfig): void;
70
+ /**
71
+ * 获取当前模型配置
72
+ */
73
+ getModelConfig(): ModelConfig | null;
74
+ /**
75
+ * 清除模型配置
76
+ */
77
+ clearModelConfig(): void;
78
+ }
79
+ export declare const claudeCodeManager: ClaudeCodeManager;
@@ -0,0 +1,201 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { homedir } from 'os';
4
+ // 默认配置
5
+ export const DEFAULT_CONFIG = {
6
+ ANTHROPIC_BASE_URL: 'https://code-api.x-aio.com/anthropic',
7
+ ANTHROPIC_API_KEY: 'sk-xxx',
8
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: 'Qwen3-Coder-30B-A3B-Instruct',
9
+ ANTHROPIC_DEFAULT_SONNET_MODEL: 'MiniMax-M2',
10
+ ANTHROPIC_DEFAULT_OPUS_MODEL: 'Qwen3-Coder-480B-A35B-Instruct'
11
+ };
12
+ export class ClaudeCodeManager {
13
+ static instance;
14
+ settingsPath;
15
+ onboardingConfigPath;
16
+ constructor() {
17
+ // Claude Code 配置文件路径(跨平台支持)
18
+ // - macOS/Linux: ~/.claude/settings.json 和 ~/.claude.json
19
+ // - Windows: %USERPROFILE%\.claude\settings.json 和 %USERPROFILE%\.claude.json
20
+ // (例如: C:\Users\username\.claude\settings.json)
21
+ this.settingsPath = join(homedir(), '.claude', 'settings.json');
22
+ this.onboardingConfigPath = join(homedir(), '.claude.json');
23
+ }
24
+ static getInstance() {
25
+ if (!ClaudeCodeManager.instance) {
26
+ ClaudeCodeManager.instance = new ClaudeCodeManager();
27
+ }
28
+ return ClaudeCodeManager.instance;
29
+ }
30
+ /**
31
+ * 确保配置目录存在
32
+ */
33
+ ensureConfigDir(filePath) {
34
+ const dir = dirname(filePath);
35
+ if (!existsSync(dir)) {
36
+ mkdirSync(dir, { recursive: true });
37
+ }
38
+ }
39
+ /**
40
+ * 读取 settings.json 配置
41
+ */
42
+ getSettings() {
43
+ try {
44
+ if (existsSync(this.settingsPath)) {
45
+ const content = readFileSync(this.settingsPath, 'utf-8');
46
+ return JSON.parse(content);
47
+ }
48
+ }
49
+ catch (error) {
50
+ console.warn('Failed to read Claude Code settings:', error);
51
+ }
52
+ return {};
53
+ }
54
+ /**
55
+ * 保存 settings.json 配置
56
+ */
57
+ saveSettings(config) {
58
+ try {
59
+ this.ensureConfigDir(this.settingsPath);
60
+ writeFileSync(this.settingsPath, JSON.stringify(config, null, 2), 'utf-8');
61
+ }
62
+ catch (error) {
63
+ throw new Error(`Failed to save Claude Code settings: ${error}`);
64
+ }
65
+ }
66
+ /**
67
+ * 读取 onboarding 配置 (~/.claude.json)
68
+ */
69
+ getOnboardingConfig() {
70
+ try {
71
+ if (existsSync(this.onboardingConfigPath)) {
72
+ const content = readFileSync(this.onboardingConfigPath, 'utf-8');
73
+ return JSON.parse(content);
74
+ }
75
+ }
76
+ catch (error) {
77
+ console.warn('Failed to read Claude Code onboarding config:', error);
78
+ }
79
+ return {};
80
+ }
81
+ /**
82
+ * 保存 onboarding 配置 (~/.claude.json)
83
+ */
84
+ saveOnboardingConfig(config) {
85
+ try {
86
+ this.ensureConfigDir(this.onboardingConfigPath);
87
+ writeFileSync(this.onboardingConfigPath, JSON.stringify(config, null, 2), 'utf-8');
88
+ }
89
+ catch (error) {
90
+ throw new Error(`Failed to save Claude Code onboarding config: ${error}`);
91
+ }
92
+ }
93
+ /**
94
+ * 确保 .claude.json 中有 hasCompletedOnboarding: true
95
+ */
96
+ ensureOnboardingCompleted() {
97
+ try {
98
+ const config = this.getOnboardingConfig();
99
+ if (!config.hasCompletedOnboarding) {
100
+ this.saveOnboardingConfig({ ...config, hasCompletedOnboarding: true });
101
+ }
102
+ }
103
+ catch (error) {
104
+ console.warn('Failed to ensure onboarding completed:', error);
105
+ }
106
+ }
107
+ /**
108
+ * 从 API 获取可用的模型列表
109
+ */
110
+ async fetchAvailableModels(apiKey) {
111
+ try {
112
+ const controller = new AbortController();
113
+ const timeoutId = setTimeout(() => controller.abort(), 30000);
114
+ const response = await fetch('https://code-api.x-aio.com/v1/models', {
115
+ method: 'GET',
116
+ headers: {
117
+ 'Authorization': `Bearer ${apiKey}`,
118
+ 'Content-Type': 'application/json'
119
+ },
120
+ signal: controller.signal
121
+ });
122
+ clearTimeout(timeoutId);
123
+ if (!response.ok) {
124
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
125
+ }
126
+ const data = await response.json();
127
+ if (data && data.data && Array.isArray(data.data)) {
128
+ return data.data.map((model) => model.id);
129
+ }
130
+ return [];
131
+ }
132
+ catch (error) {
133
+ console.warn('Failed to fetch available models:', error);
134
+ return [];
135
+ }
136
+ }
137
+ /**
138
+ * 保存模型配置到 Claude Code
139
+ */
140
+ saveModelConfig(config) {
141
+ // 确保 onboarding 已完成
142
+ this.ensureOnboardingCompleted();
143
+ const currentSettings = this.getSettings();
144
+ // 移除旧的 ANTHROPIC_AUTH_TOKEN(如果存在)
145
+ const currentEnv = currentSettings.env || {};
146
+ const { ANTHROPIC_AUTH_TOKEN: _, ...cleanedEnv } = currentEnv;
147
+ const newSettings = {
148
+ ...currentSettings,
149
+ env: {
150
+ ...cleanedEnv,
151
+ ANTHROPIC_BASE_URL: config.baseUrl || DEFAULT_CONFIG.ANTHROPIC_BASE_URL,
152
+ ANTHROPIC_API_KEY: config.apiKey,
153
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: config.haikuModel,
154
+ ANTHROPIC_DEFAULT_SONNET_MODEL: config.sonnetModel,
155
+ ANTHROPIC_DEFAULT_OPUS_MODEL: config.opusModel,
156
+ API_TIMEOUT_MS: '3000000',
157
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: 1
158
+ }
159
+ };
160
+ this.saveSettings(newSettings);
161
+ }
162
+ /**
163
+ * 获取当前模型配置
164
+ */
165
+ getModelConfig() {
166
+ const settings = this.getSettings();
167
+ if (!settings.env) {
168
+ return null;
169
+ }
170
+ const apiKey = settings.env.ANTHROPIC_API_KEY;
171
+ if (!apiKey) {
172
+ return null;
173
+ }
174
+ return {
175
+ apiKey,
176
+ haikuModel: settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL || "",
177
+ sonnetModel: settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL || "",
178
+ opusModel: settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL || "",
179
+ baseUrl: settings.env.ANTHROPIC_BASE_URL || ""
180
+ };
181
+ }
182
+ /**
183
+ * 清除模型配置
184
+ */
185
+ clearModelConfig() {
186
+ const currentSettings = this.getSettings();
187
+ if (!currentSettings.env) {
188
+ return;
189
+ }
190
+ const { ANTHROPIC_BASE_URL: _1, ANTHROPIC_API_KEY: _2, ANTHROPIC_DEFAULT_HAIKU_MODEL: _3, ANTHROPIC_DEFAULT_SONNET_MODEL: _4, ANTHROPIC_DEFAULT_OPUS_MODEL: _5, API_TIMEOUT_MS: _6, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: _7, ...otherEnv } = currentSettings.env;
191
+ const newSettings = {
192
+ ...currentSettings,
193
+ env: otherEnv
194
+ };
195
+ if (newSettings.env && Object.keys(newSettings.env).length === 0) {
196
+ delete newSettings.env;
197
+ }
198
+ this.saveSettings(newSettings);
199
+ }
200
+ }
201
+ export const claudeCodeManager = ClaudeCodeManager.getInstance();
@@ -0,0 +1,10 @@
1
+ import { Command as Commander } from 'commander';
2
+ export declare class Command {
3
+ private program;
4
+ constructor();
5
+ private getVersion;
6
+ private setupProgram;
7
+ private handleInitCommand;
8
+ execute(args: string[]): Promise<void>;
9
+ getProgram(): Commander;
10
+ }
@@ -0,0 +1,174 @@
1
+ import { Command as Commander } from 'commander';
2
+ import { i18n } from './i18n.js';
3
+ import { configManager } from './config.js';
4
+ import { wizard } from './wizard.js';
5
+ import { langCommand, authCommand, doctorCommand, configCommand } from '../commands/index.js';
6
+ import chalk from 'chalk';
7
+ import { readFileSync } from 'fs';
8
+ import { join, dirname } from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ export class Command {
11
+ program;
12
+ constructor() {
13
+ // Load language from config
14
+ const lang = configManager.getLang();
15
+ i18n.loadFromConfig(lang);
16
+ this.program = new Commander();
17
+ this.setupProgram();
18
+ }
19
+ getVersion() {
20
+ try {
21
+ const __filename = fileURLToPath(import.meta.url);
22
+ const __dirname = dirname(__filename);
23
+ const packagePath = join(__dirname, '../../package.json');
24
+ const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));
25
+ return packageJson.version;
26
+ }
27
+ catch {
28
+ return '1.0.0';
29
+ }
30
+ }
31
+ setupProgram() {
32
+ this.program
33
+ .name('chelper')
34
+ .description(i18n.t('cli.title'))
35
+ .version(this.getVersion(), '-v, --version', i18n.t('commands.version'))
36
+ .helpOption('-h, --help', i18n.t('commands.help'));
37
+ // Init command - interactive wizard
38
+ this.program
39
+ .command('init')
40
+ .description(i18n.t('commands.init'))
41
+ .action(async () => {
42
+ await this.handleInitCommand();
43
+ });
44
+ // Lang command - language management
45
+ const langCmd = this.program
46
+ .command('lang')
47
+ .description(i18n.t('commands.lang'));
48
+ langCmd
49
+ .command('show')
50
+ .description(i18n.t('lang.show_usage'))
51
+ .action(async () => {
52
+ await langCommand(['show']);
53
+ });
54
+ langCmd
55
+ .command('set <locale>')
56
+ .description(i18n.t('lang.set_usage'))
57
+ .action(async (locale) => {
58
+ await langCommand(['set', locale]);
59
+ });
60
+ // Auth command - API key management
61
+ const authCmd = this.program
62
+ .command('auth')
63
+ .description(i18n.t('commands.auth'));
64
+ authCmd
65
+ .argument('[token]', 'API token')
66
+ .action(async (token) => {
67
+ const args = [];
68
+ if (token)
69
+ args.push(token);
70
+ await authCommand(args);
71
+ });
72
+ authCmd
73
+ .command('revoke')
74
+ .description('Revoke saved API key')
75
+ .action(async () => {
76
+ await authCommand(['revoke']);
77
+ });
78
+ authCmd
79
+ .command('reload <tool>')
80
+ .description('Reload plan configuration to the specified tool (e.g., claude)')
81
+ .action(async (tool) => {
82
+ await authCommand(['reload', tool]);
83
+ });
84
+ // Doctor command - health check
85
+ this.program
86
+ .command('doctor')
87
+ .description(i18n.t('commands.doctor'))
88
+ .action(async () => {
89
+ await doctorCommand();
90
+ });
91
+ // Config command - tool configuration
92
+ const enterCmd = this.program
93
+ .command('enter [option]')
94
+ .description(i18n.t('commands.enter'));
95
+ // config 子命令
96
+ enterCmd
97
+ .action(async (option) => {
98
+ // 如果没有参数,显示主菜单
99
+ if (!option) {
100
+ await wizard.showMainMenu();
101
+ return;
102
+ }
103
+ // 根据参数执行对应操作
104
+ switch (option) {
105
+ case 'lang':
106
+ case 'language':
107
+ await wizard.configLanguage();
108
+ break;
109
+ case 'models':
110
+ await wizard.configModels();
111
+ break;
112
+ case 'apikey':
113
+ case 'api-key':
114
+ await wizard.configApiKey();
115
+ break;
116
+ default: {
117
+ // 尝试作为工具名处理
118
+ const args = [option];
119
+ await configCommand(args);
120
+ break;
121
+ }
122
+ }
123
+ });
124
+ this.program.action(async () => {
125
+ if (configManager.isFirstRun()) {
126
+ console.log(chalk.cyan(i18n.t('messages.first_run')));
127
+ await wizard.runFirstTimeSetup();
128
+ }
129
+ else {
130
+ await wizard.showMainMenu();
131
+ }
132
+ });
133
+ // Custom help
134
+ this.program.configureHelp({
135
+ sortSubcommands: true,
136
+ subcommandTerm: (cmd) => cmd.name() + ' ' + cmd.usage()
137
+ });
138
+ // Add examples to help
139
+ this.program.addHelpText('after', `
140
+ ${chalk.bold(i18n.t('cli.examples'))}:
141
+ ${chalk.gray('$ chelper # Interactive main menu')}
142
+ ${chalk.gray('$ chelper init # Run first-time setup wizard')}
143
+ ${chalk.gray('$ chelper enter # Interactive main menu')}
144
+ ${chalk.gray('$ chelper enter lang # Interactive language configuration')}
145
+ ${chalk.gray('$ chelper enter models # Interactive models configuration')}
146
+ ${chalk.gray('$ chelper enter apikey # Interactive API key configuration')}
147
+ ${chalk.gray('$ chelper enter claude-code # Interactive Configure Claude Code tool')}
148
+ ${chalk.gray('$ chelper lang show # Show current language')}
149
+ ${chalk.gray('$ chelper lang set zh_CN')}
150
+ ${chalk.gray('$ chelper auth # Interactive auth setup')}
151
+ ${chalk.gray('$ chelper auth <token> # Set API key directly')}
152
+ ${chalk.gray('$ chelper auth revoke')}
153
+ ${chalk.gray('$ chelper auth reload claude # Reload config to Claude Code')}
154
+ ${chalk.gray('$ chelper doctor # Health check')}
155
+ `);
156
+ }
157
+ async handleInitCommand() {
158
+ await wizard.runFirstTimeSetup();
159
+ }
160
+ async execute(args) {
161
+ try {
162
+ await this.program.parseAsync(args, { from: 'user' });
163
+ }
164
+ catch (error) {
165
+ if (error instanceof Error) {
166
+ console.error(chalk.red(i18n.t('cli.error_general')), error.message);
167
+ }
168
+ process.exit(1);
169
+ }
170
+ }
171
+ getProgram() {
172
+ return this.program;
173
+ }
174
+ }
@@ -0,0 +1,37 @@
1
+ export interface ChelperConfig {
2
+ lang: string;
3
+ api_key?: string;
4
+ haikuModel?: string;
5
+ sonnetModel?: string;
6
+ opusModel?: string;
7
+ }
8
+ export declare class ConfigManager {
9
+ private static instance;
10
+ private configDir;
11
+ private configPath;
12
+ private config;
13
+ private constructor();
14
+ static getInstance(): ConfigManager;
15
+ private ensureConfigDir;
16
+ private loadConfig;
17
+ saveConfig(config?: ChelperConfig): void;
18
+ getConfig(): ChelperConfig;
19
+ updateConfig(updates: Partial<ChelperConfig>): void;
20
+ isFirstRun(): boolean;
21
+ getLang(): string;
22
+ setLang(lang: string): void;
23
+ getApiKey(): string | undefined;
24
+ setApiKey(apiKey: string): void;
25
+ revokeApiKey(): void;
26
+ getModels(): {
27
+ haikuModel?: string;
28
+ sonnetModel?: string;
29
+ opusModel?: string;
30
+ };
31
+ setModels(models: {
32
+ haikuModel?: string;
33
+ sonnetModel?: string;
34
+ opusModel?: string;
35
+ }): void;
36
+ }
37
+ export declare const configManager: ConfigManager;
@@ -0,0 +1,92 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { homedir } from 'os';
4
+ import * as yaml from 'js-yaml';
5
+ export class ConfigManager {
6
+ static instance;
7
+ configDir;
8
+ configPath;
9
+ config;
10
+ constructor() {
11
+ // chelper 配置文件路径(跨平台支持)
12
+ // - macOS/Linux: ~/.chelper/config.yaml
13
+ // - Windows: %USERPROFILE%\.chelper\config.yaml
14
+ // (例如: C:\Users\username\.chelper\config.yaml)
15
+ this.configDir = join(homedir(), '.chelper');
16
+ this.configPath = join(this.configDir, 'config.yaml');
17
+ this.config = this.loadConfig();
18
+ }
19
+ static getInstance() {
20
+ if (!ConfigManager.instance) {
21
+ ConfigManager.instance = new ConfigManager();
22
+ }
23
+ return ConfigManager.instance;
24
+ }
25
+ ensureConfigDir() {
26
+ if (!existsSync(this.configDir)) {
27
+ mkdirSync(this.configDir, { recursive: true });
28
+ }
29
+ }
30
+ loadConfig() {
31
+ try {
32
+ if (existsSync(this.configPath)) {
33
+ const fileContent = readFileSync(this.configPath, 'utf-8');
34
+ const config = yaml.load(fileContent);
35
+ return config || { lang: 'en_US' };
36
+ }
37
+ }
38
+ catch (error) {
39
+ console.warn('Failed to load config, using defaults:', error);
40
+ }
41
+ return { lang: 'en_US' };
42
+ }
43
+ saveConfig(config) {
44
+ try {
45
+ this.ensureConfigDir();
46
+ const configToSave = config || this.config;
47
+ const yamlContent = yaml.dump(configToSave);
48
+ writeFileSync(this.configPath, yamlContent, 'utf-8');
49
+ this.config = configToSave;
50
+ }
51
+ catch (error) {
52
+ console.error('Failed to save config:', error);
53
+ throw error;
54
+ }
55
+ }
56
+ getConfig() {
57
+ return { ...this.config };
58
+ }
59
+ updateConfig(updates) {
60
+ this.config = { ...this.config, ...updates };
61
+ this.saveConfig();
62
+ }
63
+ isFirstRun() {
64
+ return !existsSync(this.configPath);
65
+ }
66
+ getLang() {
67
+ return this.config.lang || 'en_US';
68
+ }
69
+ setLang(lang) {
70
+ this.updateConfig({ lang });
71
+ }
72
+ getApiKey() {
73
+ return this.config.api_key;
74
+ }
75
+ setApiKey(apiKey) {
76
+ this.updateConfig({ api_key: apiKey });
77
+ }
78
+ revokeApiKey() {
79
+ this.updateConfig({ api_key: undefined });
80
+ }
81
+ getModels() {
82
+ return {
83
+ haikuModel: this.config.haikuModel,
84
+ sonnetModel: this.config.sonnetModel,
85
+ opusModel: this.config.opusModel
86
+ };
87
+ }
88
+ setModels(models) {
89
+ this.updateConfig(models);
90
+ }
91
+ }
92
+ export const configManager = ConfigManager.getInstance();
@@ -0,0 +1,20 @@
1
+ declare class I18n {
2
+ private static instance;
3
+ private currentLocale;
4
+ private translations;
5
+ private fallbackLocale;
6
+ private localesDir;
7
+ private constructor();
8
+ static getInstance(): I18n;
9
+ private loadTranslations;
10
+ setLocale(locale: string): void;
11
+ getLocale(): string;
12
+ getAvailableLocales(): string[];
13
+ translate(key: string, params?: Record<string, string>): string;
14
+ t(key: string, params?: Record<string, string>): string;
15
+ private saveLocale;
16
+ private loadSavedLocale;
17
+ loadFromConfig(locale: string): void;
18
+ }
19
+ export declare const i18n: I18n;
20
+ export { i18n as I18n };