ltcraft-ai-auto 1.5.3 → 1.6.1
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 +171 -2
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -99,6 +99,58 @@ 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
|
+
const newSection = `[model_providers.ltcraftai]\nname = "${name}"\nbase_url = "${baseUrl}"\napi_key = "${apiKey}"`;
|
|
110
|
+
|
|
111
|
+
// 更新或插入顶层 model_provider
|
|
112
|
+
if (/^model_provider\s*=/m.test(content)) {
|
|
113
|
+
content = content.replace(/^model_provider\s*=.*/m, `model_provider = "ltcraftai"`);
|
|
114
|
+
} else {
|
|
115
|
+
content = `model_provider = "ltcraftai"\n` + content;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 原地替换 [model_providers.ltcraftai] 节;不存在则追加
|
|
119
|
+
const sectionStart = content.indexOf('[model_providers.ltcraftai]');
|
|
120
|
+
if (sectionStart !== -1) {
|
|
121
|
+
const afterSection = content.slice(sectionStart + 1);
|
|
122
|
+
const nextSection = afterSection.match(/\n\[/);
|
|
123
|
+
const sectionEnd = nextSection ? sectionStart + 1 + nextSection.index + 1 : content.length;
|
|
124
|
+
content = content.slice(0, sectionStart) + newSection + '\n' + content.slice(sectionEnd);
|
|
125
|
+
} else {
|
|
126
|
+
content = content.trimEnd() + `\n\n${newSection}\n`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return content;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function configureCodex(apiKey) {
|
|
133
|
+
const homeDir = getHomeDir();
|
|
134
|
+
const codexDir = path.join(homeDir, '.codex');
|
|
135
|
+
const configPath = path.join(codexDir, 'config.toml');
|
|
136
|
+
const authPath = path.join(codexDir, 'auth.json');
|
|
137
|
+
|
|
138
|
+
backupFile(configPath);
|
|
139
|
+
backupFile(authPath);
|
|
140
|
+
|
|
141
|
+
ensureDir(codexDir);
|
|
142
|
+
|
|
143
|
+
const existingToml = fs.existsSync(configPath) ? fs.readFileSync(configPath, 'utf-8') : '';
|
|
144
|
+
const merged = mergeTomlConfig(existingToml, 'LTCraftAI', `${API_BASE_URL}/v1`, apiKey);
|
|
145
|
+
fs.writeFileSync(configPath, merged, 'utf-8');
|
|
146
|
+
console.log(`✓ 已配置: ${configPath}`);
|
|
147
|
+
|
|
148
|
+
const existingAuth = readJsonFile(authPath);
|
|
149
|
+
writeJsonFile(authPath, { ...existingAuth, auth_mode: 'apikey', OPENAI_API_KEY: apiKey });
|
|
150
|
+
console.log(`✓ 已配置: ${authPath}`);
|
|
151
|
+
console.log('\n🎉 Codex 配置完成!');
|
|
152
|
+
}
|
|
153
|
+
|
|
102
154
|
function configureOpenCode(apiKey) {
|
|
103
155
|
const homeDir = getHomeDir();
|
|
104
156
|
const configPath = path.join(homeDir, '.config', 'opencode', 'opencode.json');
|
|
@@ -433,6 +485,113 @@ function checkOpenClaw() {
|
|
|
433
485
|
}
|
|
434
486
|
}
|
|
435
487
|
|
|
488
|
+
function checkCodex() {
|
|
489
|
+
printSection('Codex 配置检测');
|
|
490
|
+
const homeDir = getHomeDir();
|
|
491
|
+
const cwd = process.cwd();
|
|
492
|
+
const isHomeDir = path.resolve(cwd) === path.resolve(homeDir);
|
|
493
|
+
|
|
494
|
+
if (isHomeDir) {
|
|
495
|
+
console.log(colorize('\n⚠️ 当前工作目录为用户主目录,跳过项目级配置检测(与全局配置路径相同)', 'yellow'));
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const globalConfigPath = path.join(homeDir, '.codex', 'config.toml');
|
|
499
|
+
const globalAuthPath = path.join(homeDir, '.codex', 'auth.json');
|
|
500
|
+
const projectConfigPath = path.join(cwd, '.codex', 'config.toml');
|
|
501
|
+
const projectAuthPath = path.join(cwd, '.codex', 'auth.json');
|
|
502
|
+
|
|
503
|
+
console.log(colorize('\n📁 配置文件状态:', 'blue'));
|
|
504
|
+
printFileStatus('全局 config.toml', globalConfigPath, fileExists(globalConfigPath));
|
|
505
|
+
printFileStatus('全局 auth.json', globalAuthPath, fileExists(globalAuthPath));
|
|
506
|
+
if (!isHomeDir) {
|
|
507
|
+
printFileStatus('项目 config.toml', projectConfigPath, fileExists(projectConfigPath));
|
|
508
|
+
printFileStatus('项目 auth.json', projectAuthPath, fileExists(projectAuthPath));
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const readToml = (p) => fileExists(p) ? fs.readFileSync(p, 'utf-8') : '';
|
|
512
|
+
const readAuth = (p) => readJsonFile(p);
|
|
513
|
+
|
|
514
|
+
const globalToml = readToml(globalConfigPath);
|
|
515
|
+
const projectToml = isHomeDir ? '' : readToml(projectConfigPath);
|
|
516
|
+
const globalAuth = readAuth(globalAuthPath);
|
|
517
|
+
const projectAuth = isHomeDir ? {} : readAuth(projectAuthPath);
|
|
518
|
+
|
|
519
|
+
const envBaseUrl = process.env.OPENAI_BASE_URL;
|
|
520
|
+
const envApiKey = process.env.OPENAI_API_KEY;
|
|
521
|
+
|
|
522
|
+
// 从 TOML 中提取值
|
|
523
|
+
const globalProvider = readTomlValue(globalToml, 'model_provider');
|
|
524
|
+
const globalBaseUrl = readTomlValue(globalToml, 'base_url');
|
|
525
|
+
const globalApiKeyToml = readTomlValue(globalToml, 'api_key');
|
|
526
|
+
const projectProvider = readTomlValue(projectToml, 'model_provider');
|
|
527
|
+
const projectBaseUrl = readTomlValue(projectToml, 'base_url');
|
|
528
|
+
const projectApiKeyToml = readTomlValue(projectToml, 'api_key');
|
|
529
|
+
|
|
530
|
+
const effectiveBaseUrl = envBaseUrl || projectBaseUrl || globalBaseUrl;
|
|
531
|
+
const effectiveApiKey = envApiKey || projectAuth.OPENAI_API_KEY || globalAuth.OPENAI_API_KEY || projectApiKeyToml || globalApiKeyToml;
|
|
532
|
+
const baseUrlSource = envBaseUrl ? '环境变量' : (projectBaseUrl ? '项目配置' : (globalBaseUrl ? '全局配置' : null));
|
|
533
|
+
const apiKeySource = envApiKey ? '环境变量' : (projectAuth.OPENAI_API_KEY ? '项目auth.json' : (globalAuth.OPENAI_API_KEY ? '全局auth.json' : (projectApiKeyToml ? '项目config.toml' : (globalApiKeyToml ? '全局config.toml' : null))));
|
|
534
|
+
|
|
535
|
+
console.log(colorize('\n🔧 关键配置项 (按优先级合并后):', 'blue'));
|
|
536
|
+
printConfigItem('model_provider', effectiveBaseUrl ? (projectProvider || globalProvider) : null, baseUrlSource ? '配置文件' : null);
|
|
537
|
+
printConfigItem('base_url', effectiveBaseUrl, baseUrlSource);
|
|
538
|
+
printConfigItem('OPENAI_API_KEY', effectiveApiKey, apiKeySource);
|
|
539
|
+
|
|
540
|
+
console.log(colorize('\n📊 各级配置详情:', 'blue'));
|
|
541
|
+
|
|
542
|
+
console.log(colorize('\n [环境变量]', 'yellow'));
|
|
543
|
+
console.log(` OPENAI_BASE_URL: ${envBaseUrl ? colorize(envBaseUrl, 'green') : colorize('未设置', 'gray')}`);
|
|
544
|
+
console.log(` OPENAI_API_KEY: ${envApiKey ? colorize(maskSecret(envApiKey), 'green') : colorize('未设置', 'gray')}`);
|
|
545
|
+
|
|
546
|
+
if (!isHomeDir) {
|
|
547
|
+
console.log(colorize('\n [项目配置] .codex/config.toml', 'yellow'));
|
|
548
|
+
if (fileExists(projectConfigPath)) {
|
|
549
|
+
console.log(` model_provider: ${projectProvider ? colorize(projectProvider, 'green') : colorize('未设置', 'gray')}`);
|
|
550
|
+
console.log(` base_url: ${projectBaseUrl ? colorize(projectBaseUrl, 'green') : colorize('未设置', 'gray')}`);
|
|
551
|
+
console.log(` api_key: ${projectApiKeyToml ? colorize(maskSecret(projectApiKeyToml), 'green') : colorize('未设置', 'gray')}`);
|
|
552
|
+
} else {
|
|
553
|
+
console.log(colorize(' (文件不存在)', 'gray'));
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
console.log(colorize('\n [项目配置] .codex/auth.json', 'yellow'));
|
|
557
|
+
if (fileExists(projectAuthPath)) {
|
|
558
|
+
console.log(` auth_mode: ${projectAuth.auth_mode ? colorize(projectAuth.auth_mode, 'green') : colorize('未设置', 'gray')}`);
|
|
559
|
+
console.log(` OPENAI_API_KEY: ${projectAuth.OPENAI_API_KEY ? colorize(maskSecret(projectAuth.OPENAI_API_KEY), 'green') : colorize('未设置', 'gray')}`);
|
|
560
|
+
} else {
|
|
561
|
+
console.log(colorize(' (文件不存在)', 'gray'));
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
console.log(colorize('\n [全局配置] ~/.codex/config.toml', 'yellow'));
|
|
566
|
+
if (fileExists(globalConfigPath)) {
|
|
567
|
+
console.log(` model_provider: ${globalProvider ? colorize(globalProvider, 'green') : colorize('未设置', 'gray')}`);
|
|
568
|
+
console.log(` base_url: ${globalBaseUrl ? colorize(globalBaseUrl, 'green') : colorize('未设置', 'gray')}`);
|
|
569
|
+
console.log(` api_key: ${globalApiKeyToml ? colorize(maskSecret(globalApiKeyToml), 'green') : colorize('未设置', 'gray')}`);
|
|
570
|
+
} else {
|
|
571
|
+
console.log(colorize(' (文件不存在)', 'gray'));
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
console.log(colorize('\n [全局配置] ~/.codex/auth.json', 'yellow'));
|
|
575
|
+
if (fileExists(globalAuthPath)) {
|
|
576
|
+
console.log(` auth_mode: ${globalAuth.auth_mode ? colorize(globalAuth.auth_mode, 'green') : colorize('未设置', 'gray')}`);
|
|
577
|
+
console.log(` OPENAI_API_KEY: ${globalAuth.OPENAI_API_KEY ? colorize(maskSecret(globalAuth.OPENAI_API_KEY), 'green') : colorize('未设置', 'gray')}`);
|
|
578
|
+
} else {
|
|
579
|
+
console.log(colorize(' (文件不存在)', 'gray'));
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
console.log(colorize('\n🎯 LTCraft API 配置状态:', 'blue'));
|
|
583
|
+
if (effectiveBaseUrl === `${API_BASE_URL}/v1` && effectiveApiKey) {
|
|
584
|
+
console.log(colorize(' ✓ LTCraft API 已正确配置', 'green'));
|
|
585
|
+
} else {
|
|
586
|
+
if (effectiveBaseUrl !== `${API_BASE_URL}/v1`) {
|
|
587
|
+
console.log(colorize(` ✗ base_url 不是 LTCraft (当前: ${effectiveBaseUrl || '未设置'})`, 'red'));
|
|
588
|
+
}
|
|
589
|
+
if (!effectiveApiKey) {
|
|
590
|
+
console.log(colorize(' ✗ OPENAI_API_KEY 未设置', 'red'));
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
436
595
|
async function runCheck() {
|
|
437
596
|
console.log('╔════════════════════════════════════════╗');
|
|
438
597
|
console.log('║ LTCraft AI 配置检测工具 ║');
|
|
@@ -446,7 +605,8 @@ async function runCheck() {
|
|
|
446
605
|
{ name: '全部检测', value: 'all' },
|
|
447
606
|
{ name: 'Claude Code', value: 'claude-code' },
|
|
448
607
|
{ name: 'OpenCode', value: 'opencode' },
|
|
449
|
-
{ name: 'OpenClaw', value: 'openclaw' }
|
|
608
|
+
{ name: 'OpenClaw', value: 'openclaw' },
|
|
609
|
+
{ name: 'Codex', value: 'codex' }
|
|
450
610
|
],
|
|
451
611
|
default: 'all'
|
|
452
612
|
});
|
|
@@ -456,6 +616,7 @@ async function runCheck() {
|
|
|
456
616
|
checkClaudeCode();
|
|
457
617
|
checkOpenCode();
|
|
458
618
|
checkOpenClaw();
|
|
619
|
+
checkCodex();
|
|
459
620
|
break;
|
|
460
621
|
case 'claude-code':
|
|
461
622
|
checkClaudeCode();
|
|
@@ -466,6 +627,9 @@ async function runCheck() {
|
|
|
466
627
|
case 'openclaw':
|
|
467
628
|
checkOpenClaw();
|
|
468
629
|
break;
|
|
630
|
+
case 'codex':
|
|
631
|
+
checkCodex();
|
|
632
|
+
break;
|
|
469
633
|
}
|
|
470
634
|
|
|
471
635
|
console.log(colorize('\n' + '━'.repeat(50), 'cyan'));
|
|
@@ -473,6 +637,7 @@ async function runCheck() {
|
|
|
473
637
|
console.log(colorize(' Claude Code: 环境变量 > 项目本地配置 > 项目配置 > 全局配置', 'gray'));
|
|
474
638
|
console.log(colorize(' OpenCode: 环境变量 > 项目配置 > 全局配置', 'gray'));
|
|
475
639
|
console.log(colorize(' OpenClaw: 全局配置 (暂无项目级配置支持)', 'gray'));
|
|
640
|
+
console.log(colorize(' Codex: 环境变量 > 项目配置(.codex/) > 全局配置(~/.codex/)', 'gray'));
|
|
476
641
|
console.log();
|
|
477
642
|
}
|
|
478
643
|
|
|
@@ -487,7 +652,8 @@ async function main() {
|
|
|
487
652
|
choices: [
|
|
488
653
|
{ name: 'Claude Code', value: 'claude-code' },
|
|
489
654
|
{ name: 'OpenCode', value: 'opencode' },
|
|
490
|
-
{ name: 'OpenClaw', value: 'openclaw' }
|
|
655
|
+
{ name: 'OpenClaw', value: 'openclaw' },
|
|
656
|
+
{ name: 'Codex', value: 'codex' }
|
|
491
657
|
],
|
|
492
658
|
default: 'claude-code'
|
|
493
659
|
});
|
|
@@ -515,6 +681,9 @@ async function main() {
|
|
|
515
681
|
case 'openclaw':
|
|
516
682
|
configureOpenClaw(trimmedKey);
|
|
517
683
|
break;
|
|
684
|
+
case 'codex':
|
|
685
|
+
configureCodex(trimmedKey);
|
|
686
|
+
break;
|
|
518
687
|
}
|
|
519
688
|
}
|
|
520
689
|
|