ltcraft-ai-auto 1.5.3 → 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/bin/index.js +164 -2
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -99,6 +99,51 @@ function configureClaudeCode(apiKey) {
|
|
|
99
99
|
console.log('\n🎉 Claude Code 配置完成!');
|
|
100
100
|
}
|
|
101
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
|
+
|
|
102
147
|
function configureOpenCode(apiKey) {
|
|
103
148
|
const homeDir = getHomeDir();
|
|
104
149
|
const configPath = path.join(homeDir, '.config', 'opencode', 'opencode.json');
|
|
@@ -433,6 +478,113 @@ function checkOpenClaw() {
|
|
|
433
478
|
}
|
|
434
479
|
}
|
|
435
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
|
+
|
|
436
588
|
async function runCheck() {
|
|
437
589
|
console.log('╔════════════════════════════════════════╗');
|
|
438
590
|
console.log('║ LTCraft AI 配置检测工具 ║');
|
|
@@ -446,7 +598,8 @@ async function runCheck() {
|
|
|
446
598
|
{ name: '全部检测', value: 'all' },
|
|
447
599
|
{ name: 'Claude Code', value: 'claude-code' },
|
|
448
600
|
{ name: 'OpenCode', value: 'opencode' },
|
|
449
|
-
{ name: 'OpenClaw', value: 'openclaw' }
|
|
601
|
+
{ name: 'OpenClaw', value: 'openclaw' },
|
|
602
|
+
{ name: 'Codex', value: 'codex' }
|
|
450
603
|
],
|
|
451
604
|
default: 'all'
|
|
452
605
|
});
|
|
@@ -456,6 +609,7 @@ async function runCheck() {
|
|
|
456
609
|
checkClaudeCode();
|
|
457
610
|
checkOpenCode();
|
|
458
611
|
checkOpenClaw();
|
|
612
|
+
checkCodex();
|
|
459
613
|
break;
|
|
460
614
|
case 'claude-code':
|
|
461
615
|
checkClaudeCode();
|
|
@@ -466,6 +620,9 @@ async function runCheck() {
|
|
|
466
620
|
case 'openclaw':
|
|
467
621
|
checkOpenClaw();
|
|
468
622
|
break;
|
|
623
|
+
case 'codex':
|
|
624
|
+
checkCodex();
|
|
625
|
+
break;
|
|
469
626
|
}
|
|
470
627
|
|
|
471
628
|
console.log(colorize('\n' + '━'.repeat(50), 'cyan'));
|
|
@@ -473,6 +630,7 @@ async function runCheck() {
|
|
|
473
630
|
console.log(colorize(' Claude Code: 环境变量 > 项目本地配置 > 项目配置 > 全局配置', 'gray'));
|
|
474
631
|
console.log(colorize(' OpenCode: 环境变量 > 项目配置 > 全局配置', 'gray'));
|
|
475
632
|
console.log(colorize(' OpenClaw: 全局配置 (暂无项目级配置支持)', 'gray'));
|
|
633
|
+
console.log(colorize(' Codex: 环境变量 > 项目配置(.codex/) > 全局配置(~/.codex/)', 'gray'));
|
|
476
634
|
console.log();
|
|
477
635
|
}
|
|
478
636
|
|
|
@@ -487,7 +645,8 @@ async function main() {
|
|
|
487
645
|
choices: [
|
|
488
646
|
{ name: 'Claude Code', value: 'claude-code' },
|
|
489
647
|
{ name: 'OpenCode', value: 'opencode' },
|
|
490
|
-
{ name: 'OpenClaw', value: 'openclaw' }
|
|
648
|
+
{ name: 'OpenClaw', value: 'openclaw' },
|
|
649
|
+
{ name: 'Codex', value: 'codex' }
|
|
491
650
|
],
|
|
492
651
|
default: 'claude-code'
|
|
493
652
|
});
|
|
@@ -515,6 +674,9 @@ async function main() {
|
|
|
515
674
|
case 'openclaw':
|
|
516
675
|
configureOpenClaw(trimmedKey);
|
|
517
676
|
break;
|
|
677
|
+
case 'codex':
|
|
678
|
+
configureCodex(trimmedKey);
|
|
679
|
+
break;
|
|
518
680
|
}
|
|
519
681
|
}
|
|
520
682
|
|