ltcraft-ai-auto 1.5.2 → 1.6.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.
- package/.claude/settings.local.json +6 -0
- package/bin/index.js +166 -3
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -91,13 +91,59 @@ function configureClaudeCode(apiKey) {
|
|
|
91
91
|
...(settings.env || {}),
|
|
92
92
|
ANTHROPIC_BASE_URL: API_BASE_URL,
|
|
93
93
|
API_TIMEOUT_MS: 3000000,
|
|
94
|
-
ANTHROPIC_AUTH_TOKEN: apiKey
|
|
94
|
+
ANTHROPIC_AUTH_TOKEN: apiKey,
|
|
95
|
+
ANTHROPIC_API_KEY: apiKey
|
|
95
96
|
};
|
|
96
97
|
writeJsonFile(settingsPath, settings);
|
|
97
98
|
console.log(`✓ 已配置: ${settingsPath}`);
|
|
98
99
|
console.log('\n🎉 Claude Code 配置完成!');
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
function readTomlValue(content, key) {
|
|
103
|
+
const match = content.match(new RegExp(`^${key}\\s*=\\s*"([^"]*)"`, 'm'));
|
|
104
|
+
return match ? match[1] : null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function mergeTomlConfig(existing, name, baseUrl, apiKey) {
|
|
108
|
+
let content = existing || '';
|
|
109
|
+
|
|
110
|
+
// 更新或插入顶层 model_provider
|
|
111
|
+
if (/^model_provider\s*=/m.test(content)) {
|
|
112
|
+
content = content.replace(/^model_provider\s*=.*/m, `model_provider = "ltcraftai"`);
|
|
113
|
+
} else {
|
|
114
|
+
content = `model_provider = "ltcraftai"\n` + content;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 移除已有的 [model_providers.ltcraftai] 节(到下一个节或文件末尾)
|
|
118
|
+
content = content.replace(/\[model_providers\.ltcraftai\][\s\S]*?(?=\n\[|\s*$)/, '');
|
|
119
|
+
|
|
120
|
+
// 追加新节
|
|
121
|
+
content = content.trimEnd() + `\n\n[model_providers.ltcraftai]\nname = "${name}"\nbase_url = "${baseUrl}"\napi_key = "${apiKey}"\n`;
|
|
122
|
+
return content;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function configureCodex(apiKey) {
|
|
126
|
+
const homeDir = getHomeDir();
|
|
127
|
+
const codexDir = path.join(homeDir, '.codex');
|
|
128
|
+
const configPath = path.join(codexDir, 'config.toml');
|
|
129
|
+
const authPath = path.join(codexDir, 'auth.json');
|
|
130
|
+
|
|
131
|
+
backupFile(configPath);
|
|
132
|
+
backupFile(authPath);
|
|
133
|
+
|
|
134
|
+
ensureDir(codexDir);
|
|
135
|
+
|
|
136
|
+
const existingToml = fs.existsSync(configPath) ? fs.readFileSync(configPath, 'utf-8') : '';
|
|
137
|
+
const merged = mergeTomlConfig(existingToml, 'LTCraftAI', `${API_BASE_URL}/v1`, apiKey);
|
|
138
|
+
fs.writeFileSync(configPath, merged, 'utf-8');
|
|
139
|
+
console.log(`✓ 已配置: ${configPath}`);
|
|
140
|
+
|
|
141
|
+
const existingAuth = readJsonFile(authPath);
|
|
142
|
+
writeJsonFile(authPath, { ...existingAuth, auth_mode: 'apikey', OPENAI_API_KEY: apiKey });
|
|
143
|
+
console.log(`✓ 已配置: ${authPath}`);
|
|
144
|
+
console.log('\n🎉 Codex 配置完成!');
|
|
145
|
+
}
|
|
146
|
+
|
|
101
147
|
function configureOpenCode(apiKey) {
|
|
102
148
|
const homeDir = getHomeDir();
|
|
103
149
|
const configPath = path.join(homeDir, '.config', 'opencode', 'opencode.json');
|
|
@@ -432,6 +478,113 @@ function checkOpenClaw() {
|
|
|
432
478
|
}
|
|
433
479
|
}
|
|
434
480
|
|
|
481
|
+
function checkCodex() {
|
|
482
|
+
printSection('Codex 配置检测');
|
|
483
|
+
const homeDir = getHomeDir();
|
|
484
|
+
const cwd = process.cwd();
|
|
485
|
+
const isHomeDir = path.resolve(cwd) === path.resolve(homeDir);
|
|
486
|
+
|
|
487
|
+
if (isHomeDir) {
|
|
488
|
+
console.log(colorize('\n⚠️ 当前工作目录为用户主目录,跳过项目级配置检测(与全局配置路径相同)', 'yellow'));
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const globalConfigPath = path.join(homeDir, '.codex', 'config.toml');
|
|
492
|
+
const globalAuthPath = path.join(homeDir, '.codex', 'auth.json');
|
|
493
|
+
const projectConfigPath = path.join(cwd, '.codex', 'config.toml');
|
|
494
|
+
const projectAuthPath = path.join(cwd, '.codex', 'auth.json');
|
|
495
|
+
|
|
496
|
+
console.log(colorize('\n📁 配置文件状态:', 'blue'));
|
|
497
|
+
printFileStatus('全局 config.toml', globalConfigPath, fileExists(globalConfigPath));
|
|
498
|
+
printFileStatus('全局 auth.json', globalAuthPath, fileExists(globalAuthPath));
|
|
499
|
+
if (!isHomeDir) {
|
|
500
|
+
printFileStatus('项目 config.toml', projectConfigPath, fileExists(projectConfigPath));
|
|
501
|
+
printFileStatus('项目 auth.json', projectAuthPath, fileExists(projectAuthPath));
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const readToml = (p) => fileExists(p) ? fs.readFileSync(p, 'utf-8') : '';
|
|
505
|
+
const readAuth = (p) => readJsonFile(p);
|
|
506
|
+
|
|
507
|
+
const globalToml = readToml(globalConfigPath);
|
|
508
|
+
const projectToml = isHomeDir ? '' : readToml(projectConfigPath);
|
|
509
|
+
const globalAuth = readAuth(globalAuthPath);
|
|
510
|
+
const projectAuth = isHomeDir ? {} : readAuth(projectAuthPath);
|
|
511
|
+
|
|
512
|
+
const envBaseUrl = process.env.OPENAI_BASE_URL;
|
|
513
|
+
const envApiKey = process.env.OPENAI_API_KEY;
|
|
514
|
+
|
|
515
|
+
// 从 TOML 中提取值
|
|
516
|
+
const globalProvider = readTomlValue(globalToml, 'model_provider');
|
|
517
|
+
const globalBaseUrl = readTomlValue(globalToml, 'base_url');
|
|
518
|
+
const globalApiKeyToml = readTomlValue(globalToml, 'api_key');
|
|
519
|
+
const projectProvider = readTomlValue(projectToml, 'model_provider');
|
|
520
|
+
const projectBaseUrl = readTomlValue(projectToml, 'base_url');
|
|
521
|
+
const projectApiKeyToml = readTomlValue(projectToml, 'api_key');
|
|
522
|
+
|
|
523
|
+
const effectiveBaseUrl = envBaseUrl || projectBaseUrl || globalBaseUrl;
|
|
524
|
+
const effectiveApiKey = envApiKey || projectAuth.OPENAI_API_KEY || globalAuth.OPENAI_API_KEY || projectApiKeyToml || globalApiKeyToml;
|
|
525
|
+
const baseUrlSource = envBaseUrl ? '环境变量' : (projectBaseUrl ? '项目配置' : (globalBaseUrl ? '全局配置' : null));
|
|
526
|
+
const apiKeySource = envApiKey ? '环境变量' : (projectAuth.OPENAI_API_KEY ? '项目auth.json' : (globalAuth.OPENAI_API_KEY ? '全局auth.json' : (projectApiKeyToml ? '项目config.toml' : (globalApiKeyToml ? '全局config.toml' : null))));
|
|
527
|
+
|
|
528
|
+
console.log(colorize('\n🔧 关键配置项 (按优先级合并后):', 'blue'));
|
|
529
|
+
printConfigItem('model_provider', effectiveBaseUrl ? (projectProvider || globalProvider) : null, baseUrlSource ? '配置文件' : null);
|
|
530
|
+
printConfigItem('base_url', effectiveBaseUrl, baseUrlSource);
|
|
531
|
+
printConfigItem('OPENAI_API_KEY', effectiveApiKey, apiKeySource);
|
|
532
|
+
|
|
533
|
+
console.log(colorize('\n📊 各级配置详情:', 'blue'));
|
|
534
|
+
|
|
535
|
+
console.log(colorize('\n [环境变量]', 'yellow'));
|
|
536
|
+
console.log(` OPENAI_BASE_URL: ${envBaseUrl ? colorize(envBaseUrl, 'green') : colorize('未设置', 'gray')}`);
|
|
537
|
+
console.log(` OPENAI_API_KEY: ${envApiKey ? colorize(maskSecret(envApiKey), 'green') : colorize('未设置', 'gray')}`);
|
|
538
|
+
|
|
539
|
+
if (!isHomeDir) {
|
|
540
|
+
console.log(colorize('\n [项目配置] .codex/config.toml', 'yellow'));
|
|
541
|
+
if (fileExists(projectConfigPath)) {
|
|
542
|
+
console.log(` model_provider: ${projectProvider ? colorize(projectProvider, 'green') : colorize('未设置', 'gray')}`);
|
|
543
|
+
console.log(` base_url: ${projectBaseUrl ? colorize(projectBaseUrl, 'green') : colorize('未设置', 'gray')}`);
|
|
544
|
+
console.log(` api_key: ${projectApiKeyToml ? colorize(maskSecret(projectApiKeyToml), 'green') : colorize('未设置', 'gray')}`);
|
|
545
|
+
} else {
|
|
546
|
+
console.log(colorize(' (文件不存在)', 'gray'));
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
console.log(colorize('\n [项目配置] .codex/auth.json', 'yellow'));
|
|
550
|
+
if (fileExists(projectAuthPath)) {
|
|
551
|
+
console.log(` auth_mode: ${projectAuth.auth_mode ? colorize(projectAuth.auth_mode, 'green') : colorize('未设置', 'gray')}`);
|
|
552
|
+
console.log(` OPENAI_API_KEY: ${projectAuth.OPENAI_API_KEY ? colorize(maskSecret(projectAuth.OPENAI_API_KEY), 'green') : colorize('未设置', 'gray')}`);
|
|
553
|
+
} else {
|
|
554
|
+
console.log(colorize(' (文件不存在)', 'gray'));
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
console.log(colorize('\n [全局配置] ~/.codex/config.toml', 'yellow'));
|
|
559
|
+
if (fileExists(globalConfigPath)) {
|
|
560
|
+
console.log(` model_provider: ${globalProvider ? colorize(globalProvider, 'green') : colorize('未设置', 'gray')}`);
|
|
561
|
+
console.log(` base_url: ${globalBaseUrl ? colorize(globalBaseUrl, 'green') : colorize('未设置', 'gray')}`);
|
|
562
|
+
console.log(` api_key: ${globalApiKeyToml ? colorize(maskSecret(globalApiKeyToml), 'green') : colorize('未设置', 'gray')}`);
|
|
563
|
+
} else {
|
|
564
|
+
console.log(colorize(' (文件不存在)', 'gray'));
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
console.log(colorize('\n [全局配置] ~/.codex/auth.json', 'yellow'));
|
|
568
|
+
if (fileExists(globalAuthPath)) {
|
|
569
|
+
console.log(` auth_mode: ${globalAuth.auth_mode ? colorize(globalAuth.auth_mode, 'green') : colorize('未设置', 'gray')}`);
|
|
570
|
+
console.log(` OPENAI_API_KEY: ${globalAuth.OPENAI_API_KEY ? colorize(maskSecret(globalAuth.OPENAI_API_KEY), 'green') : colorize('未设置', 'gray')}`);
|
|
571
|
+
} else {
|
|
572
|
+
console.log(colorize(' (文件不存在)', 'gray'));
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
console.log(colorize('\n🎯 LTCraft API 配置状态:', 'blue'));
|
|
576
|
+
if (effectiveBaseUrl === `${API_BASE_URL}/v1` && effectiveApiKey) {
|
|
577
|
+
console.log(colorize(' ✓ LTCraft API 已正确配置', 'green'));
|
|
578
|
+
} else {
|
|
579
|
+
if (effectiveBaseUrl !== `${API_BASE_URL}/v1`) {
|
|
580
|
+
console.log(colorize(` ✗ base_url 不是 LTCraft (当前: ${effectiveBaseUrl || '未设置'})`, 'red'));
|
|
581
|
+
}
|
|
582
|
+
if (!effectiveApiKey) {
|
|
583
|
+
console.log(colorize(' ✗ OPENAI_API_KEY 未设置', 'red'));
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
435
588
|
async function runCheck() {
|
|
436
589
|
console.log('╔════════════════════════════════════════╗');
|
|
437
590
|
console.log('║ LTCraft AI 配置检测工具 ║');
|
|
@@ -445,7 +598,8 @@ async function runCheck() {
|
|
|
445
598
|
{ name: '全部检测', value: 'all' },
|
|
446
599
|
{ name: 'Claude Code', value: 'claude-code' },
|
|
447
600
|
{ name: 'OpenCode', value: 'opencode' },
|
|
448
|
-
{ name: 'OpenClaw', value: 'openclaw' }
|
|
601
|
+
{ name: 'OpenClaw', value: 'openclaw' },
|
|
602
|
+
{ name: 'Codex', value: 'codex' }
|
|
449
603
|
],
|
|
450
604
|
default: 'all'
|
|
451
605
|
});
|
|
@@ -455,6 +609,7 @@ async function runCheck() {
|
|
|
455
609
|
checkClaudeCode();
|
|
456
610
|
checkOpenCode();
|
|
457
611
|
checkOpenClaw();
|
|
612
|
+
checkCodex();
|
|
458
613
|
break;
|
|
459
614
|
case 'claude-code':
|
|
460
615
|
checkClaudeCode();
|
|
@@ -465,6 +620,9 @@ async function runCheck() {
|
|
|
465
620
|
case 'openclaw':
|
|
466
621
|
checkOpenClaw();
|
|
467
622
|
break;
|
|
623
|
+
case 'codex':
|
|
624
|
+
checkCodex();
|
|
625
|
+
break;
|
|
468
626
|
}
|
|
469
627
|
|
|
470
628
|
console.log(colorize('\n' + '━'.repeat(50), 'cyan'));
|
|
@@ -472,6 +630,7 @@ async function runCheck() {
|
|
|
472
630
|
console.log(colorize(' Claude Code: 环境变量 > 项目本地配置 > 项目配置 > 全局配置', 'gray'));
|
|
473
631
|
console.log(colorize(' OpenCode: 环境变量 > 项目配置 > 全局配置', 'gray'));
|
|
474
632
|
console.log(colorize(' OpenClaw: 全局配置 (暂无项目级配置支持)', 'gray'));
|
|
633
|
+
console.log(colorize(' Codex: 环境变量 > 项目配置(.codex/) > 全局配置(~/.codex/)', 'gray'));
|
|
475
634
|
console.log();
|
|
476
635
|
}
|
|
477
636
|
|
|
@@ -486,7 +645,8 @@ async function main() {
|
|
|
486
645
|
choices: [
|
|
487
646
|
{ name: 'Claude Code', value: 'claude-code' },
|
|
488
647
|
{ name: 'OpenCode', value: 'opencode' },
|
|
489
|
-
{ name: 'OpenClaw', value: 'openclaw' }
|
|
648
|
+
{ name: 'OpenClaw', value: 'openclaw' },
|
|
649
|
+
{ name: 'Codex', value: 'codex' }
|
|
490
650
|
],
|
|
491
651
|
default: 'claude-code'
|
|
492
652
|
});
|
|
@@ -514,6 +674,9 @@ async function main() {
|
|
|
514
674
|
case 'openclaw':
|
|
515
675
|
configureOpenClaw(trimmedKey);
|
|
516
676
|
break;
|
|
677
|
+
case 'codex':
|
|
678
|
+
configureCodex(trimmedKey);
|
|
679
|
+
break;
|
|
517
680
|
}
|
|
518
681
|
}
|
|
519
682
|
|