bhg-helper 1.0.3 → 1.0.5

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.
Files changed (2) hide show
  1. package/cli/cli.js +118 -34
  2. package/package.json +1 -1
package/cli/cli.js CHANGED
@@ -12,10 +12,19 @@ import fs from 'node:fs'
12
12
  import { fileURLToPath } from 'node:url'
13
13
 
14
14
  // ── 色彩 ──────────────────────────────────────────────────────
15
- const C = { r: '\x1b[0m', bold: '\x1b[1m', cyan: '\x1b[36m', green: '\x1b[32m', yellow: '\x1b[33m', dim: '\x1b[2m', red: '\x1b[31m' }
15
+ const C = {
16
+ r: '\x1b[0m',
17
+ bold: '\x1b[1m',
18
+ cyan: '\x1b[36m',
19
+ green: '\x1b[32m',
20
+ yellow: '\x1b[33m',
21
+ dim: '\x1b[2m',
22
+ red: '\x1b[31m',
23
+ }
16
24
  const theme = { prefix: `${C.cyan}?${C.r}` }
17
25
 
18
26
  // ── 常量 ──────────────────────────────────────────────────────
27
+ const VERSION = '1.0.2'
19
28
  const RELAY_PORT = 8787
20
29
  const API_URL = 'http://127.0.0.1:3001'
21
30
  const PROJECT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
@@ -23,6 +32,66 @@ const BHG_HELPER_DIR = path.join(os.homedir(), '.bhg-helper')
23
32
  const RELAY_CONFIG_FILE = path.join(BHG_HELPER_DIR, 'relay.config.json')
24
33
  const CLAUDE_SETTINGS_FILE = path.join(os.homedir(), '.claude', 'settings.json')
25
34
 
35
+ const TOOLS_LIST = [
36
+ { name: 'Claude Code', desc: 'Anthropic 官方 CLI 编程助手' },
37
+ { name: 'OpenCode', desc: '开源 AI 编程助手' },
38
+ { name: 'Gemini CLI', desc: 'Google 官方 CLI 编程助手' },
39
+ { name: 'OpenAI Codex CLI', desc: 'OpenAI 官方 CLI 编程助手' },
40
+ ]
41
+
42
+ // ── ANSI 24 位色渐变 Logo ──────────────────────────────────────
43
+ function rgb(r, g, b) {
44
+ return `\x1b[38;2;${r};${g};${b}m`
45
+ }
46
+
47
+ // 蓝 → 紫 → 粉 渐变 10 行
48
+ function colorLine(row, totalRows = 10) {
49
+ const t = row / (totalRows - 1)
50
+ if (t < 0.5) {
51
+ const p = t * 2
52
+ return rgb(
53
+ Math.round(80 + p * 70),
54
+ Math.round(180 - p * 40),
55
+ Math.round(255)
56
+ )
57
+ } else {
58
+ const p = (t - 0.5) * 2
59
+ return rgb(
60
+ Math.round(150 + p * 95),
61
+ Math.round(140 - p * 70),
62
+ Math.round(255)
63
+ )
64
+ }
65
+ }
66
+
67
+ const BHG_LOGO = [
68
+ '██████╗ ██╗ ██╗ ███████╗',
69
+ '██╔══██╗ ██║ ██║ ██╔════╝',
70
+ '██████╔╝ ███████║ ███████╗',
71
+ '██╔══██╗ ██╔══██║ ╚════██║',
72
+ '██████╔╝ ██║ ██║ ███████║',
73
+ '╚═════╝ ╚═╝ ╚═╝ ╚══════╝',
74
+ ]
75
+
76
+ const HELPER_LOGO = [
77
+ '██╗ ██╗ ███████╗ ██╗ ██████╗ ███████╗ ██████╗ ',
78
+ '██║ ██║ ██╔════╝ ██║ ██╔══██╗ ██╔════╝ ██╔══██╗',
79
+ '███████║ █████╗ ██║ ██████╔╝ █████╗ ██████╔╝',
80
+ '██╔══██║ ██╔══╝ ██║ ██╔═══╝ ██╔══╝ ██╔══██╗',
81
+ '██║ ██║ ███████╗ ███████╗ ██║ ███████╗ ██║ ██║',
82
+ '╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝',
83
+ ]
84
+
85
+ function printLogo() {
86
+ const gap = ' '
87
+ for (let i = 0; i < BHG_LOGO.length; i++) {
88
+ console.log(
89
+ `${colorLine(i)}${C.bold}${BHG_LOGO[i]}${gap}${HELPER_LOGO[i]}${C.r}`
90
+ )
91
+ }
92
+ console.log()
93
+ }
94
+
26
95
  // ── 工具定义 ──────────────────────────────────────────────────
27
96
  const TOOLS = {
28
97
  claude: {
@@ -97,7 +166,6 @@ function hasApiConfig() {
97
166
  function getCurrentModel() {
98
167
  const cfg = loadConfig()
99
168
  if (!cfg) return 'deepseek-v4-pro'
100
- // 优先读取当前激活的模型
101
169
  return cfg.currentModel || 'deepseek-v4-pro'
102
170
  }
103
171
 
@@ -115,6 +183,19 @@ async function apiPost(pathname) {
115
183
  return res.ok
116
184
  }
117
185
 
186
+ async function apiGet(pathname) {
187
+ try {
188
+ const controller = new AbortController()
189
+ const timer = setTimeout(() => controller.abort(), 800)
190
+ const res = await fetch(`${API_URL}${pathname}`, { signal: controller.signal })
191
+ clearTimeout(timer)
192
+ if (!res.ok) return null
193
+ return await res.json().catch(() => null)
194
+ } catch {
195
+ return null
196
+ }
197
+ }
198
+
118
199
  async function apiHealth() {
119
200
  try {
120
201
  const controller = new AbortController()
@@ -146,14 +227,33 @@ async function ensureRelay() {
146
227
  return await apiPost('/api/relay/start')
147
228
  }
148
229
 
149
- // ── 打印头部 ──────────────────────────────────────────────────
150
- function printHeader() {
230
+ // ── 打印头部(ArkHelper 风格)─────────────────────────────────
231
+ async function printHeader() {
151
232
  console.clear()
152
- console.log(`${C.bold}${C.cyan} ◆ BHG-helper${C.r}`)
233
+ printLogo()
234
+
235
+ console.log(` v${VERSION} · DeepSeek LLM bridge for your AI coding tools`)
236
+ console.log()
237
+ console.log(` ${C.bold}一键配置${C.r}`)
238
+ for (const t of TOOLS_LIST) {
239
+ console.log(` ${C.cyan}◆${C.r} ${t.name} ${C.dim}— ${t.desc}${C.r}`)
240
+ }
241
+ console.log()
242
+
153
243
  const apiOk = hasApiConfig()
154
- const model = getCurrentModel()
155
- console.log(` ${C.dim}API${C.r} ${apiOk ? `${C.green}已配置${C.r}` : `${C.red}未配置${C.r}`}`)
156
- console.log(` ${C.dim}模型${C.r} ${C.cyan}${model}${C.r}\n`)
244
+ const backendOk = await apiHealth()
245
+ const relayJson = await apiGet('/api/relay/status')
246
+ const relayOk = !!(relayJson && relayJson.data?.running)
247
+
248
+ console.log(
249
+ ` ${C.dim}API:${C.r} ${apiOk ? C.green + '✓' + C.r : C.red + '✗' + C.r}`
250
+ + ` ${C.dim}Backend:${C.r} ${backendOk ? C.green + '✓' + C.r : C.red + '✗' + C.r}`
251
+ + ` ${C.dim}Relay:${C.r} ${relayOk ? C.green + '✓' + C.r : C.red + '✗' + C.r}`
252
+ )
253
+
254
+ console.log()
255
+ console.log(` ${C.dim}powered by BH6BHG${C.r}`)
256
+ console.log()
157
257
  }
158
258
 
159
259
  // ── 一级菜单 ──────────────────────────────────────────────────
@@ -193,36 +293,27 @@ async function modelMenu() {
193
293
  if (choice === 'back') return 'back'
194
294
 
195
295
  if (choice === 'switch') {
196
- // 从 relay config 读取模型列表
197
296
  const models = []
198
297
  if (cfg && cfg.modelMap) {
199
298
  const seen = new Set()
200
- for (const [from, to] of Object.entries(cfg.modelMap)) {
201
- if (!seen.has(to)) {
202
- seen.add(to)
203
- models.push({ from, to })
299
+ for (const [from] of Object.entries(cfg.modelMap)) {
300
+ if (!seen.has(from)) {
301
+ seen.add(from)
302
+ models.push(from)
204
303
  }
205
304
  }
206
305
  } else {
207
- // 默认列表
208
- models.push(
209
- { from: 'deepseek-v4-pro', to: 'deepseek-v4-pro' },
210
- { from: 'deepseek-v4-flash', to: 'deepseek-v4-flash' },
211
- { from: 'deepseek-chat', to: 'deepseek-v4-pro' },
212
- { from: 'deepseek-reasoner', to: 'deepseek-v4-pro' },
213
- )
306
+ models.push('deepseek-v4-pro', 'deepseek-v4-flash', 'deepseek-chat')
214
307
  }
215
308
 
216
309
  const modelChoice = await select({
217
310
  message: '选择模型',
218
311
  choices: [
219
312
  ...models.map(m => ({
220
- name: m.from === currentModel
221
- ? `${C.green}● ${m.from}${C.r} ${C.dim}→ ${m.to}${C.r}`
222
- : ` ${m.from} ${C.dim}→ ${m.to}${C.r}`,
223
- value: m.from,
313
+ name: m === currentModel ? `${C.green}● ${m}${C.r}` : ` ${m}`,
314
+ value: m,
224
315
  })),
225
- { name: `${C.dim}──────────${C.r}`, value: '---2', disabled: true },
316
+ { name: `${C.dim}──────────${C.r}`, value: '---', disabled: true },
226
317
  { name: '返回', value: 'back' },
227
318
  ],
228
319
  theme,
@@ -256,7 +347,6 @@ async function modelMenu() {
256
347
  saveConfig(next)
257
348
  console.log(`\n ${C.green}✓ API Key 已保存${C.r}`)
258
349
 
259
- // 自动启动中转服务
260
350
  console.log(` ${C.dim}正在启动中转服务...${C.r}`)
261
351
  const relayOk = await ensureRelay()
262
352
  console.log(` ${relayOk ? C.green + '✓ 中转服务已启动' + C.r : C.yellow + '中转服务启动失败(可稍后手动启动)' + C.r}\n`)
@@ -296,7 +386,6 @@ async function toolsMenu() {
296
386
  async function toolDetailMenu(toolKey) {
297
387
  const tool = TOOLS[toolKey]
298
388
 
299
- // Cursor / Continue / Trae — 无 CLI,显示安装方式
300
389
  if (!tool.cmd) {
301
390
  console.log(`\n ${C.bold}${tool.name}${C.r} — ${tool.desc}`)
302
391
  console.log(` ${C.dim}安装方式:${tool.install}${C.r}`)
@@ -305,7 +394,6 @@ async function toolDetailMenu(toolKey) {
305
394
  return 'back'
306
395
  }
307
396
 
308
- // 有 CLI 的工具
309
397
  const installed = checkInstalled(tool.cmd)
310
398
 
311
399
  const choices = []
@@ -344,7 +432,6 @@ async function toolDetailMenu(toolKey) {
344
432
  }
345
433
 
346
434
  if (action === 'launch') {
347
- // 启动前检查 API 配置
348
435
  if (!hasApiConfig()) {
349
436
  console.log(`\n ${C.red}API 未配置!请先在「模型选择」→「输入 API」中配置。${C.r}\n`)
350
437
  await confirm({ message: '按回车继续...', theme })
@@ -358,7 +445,6 @@ async function toolDetailMenu(toolKey) {
358
445
  return 'back'
359
446
  }
360
447
 
361
- // 写入 ~/.claude/settings.json(Claude Code 专用)
362
448
  if (toolKey === 'claude') {
363
449
  const currentModel = getCurrentModel()
364
450
  const dir = path.dirname(CLAUDE_SETTINGS_FILE)
@@ -383,7 +469,6 @@ async function toolDetailMenu(toolKey) {
383
469
  console.log(` ${C.green}✓ Claude Code 配置已写入${C.r}`)
384
470
  }
385
471
 
386
- // 写入 opencode 配置
387
472
  if (toolKey === 'opencode') {
388
473
  const file = TOOLS.opencode.configFile
389
474
  const dir = path.dirname(file)
@@ -436,7 +521,7 @@ async function langMenu() {
436
521
  async function main() {
437
522
  let running = true
438
523
  while (running) {
439
- printHeader()
524
+ await printHeader()
440
525
  const choice = await mainMenu()
441
526
 
442
527
  switch (choice) {
@@ -462,7 +547,6 @@ async function main() {
462
547
  if (result === 'exit') { console.log(`\n ${C.dim}再见。${C.r}\n`); running = false }
463
548
  break
464
549
  }
465
-
466
550
  }
467
551
  }
468
552
  }
@@ -474,4 +558,4 @@ main().catch((err) => {
474
558
  }
475
559
  console.error('Error:', err.message)
476
560
  process.exit(1)
477
- })
561
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bhg-helper",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "BHG-helper:AI 编程工具 DeepSeek 中转 & 可视化配置工具",
5
5
  "type": "module",
6
6
  "bin": {