bhg-helper 1.0.3 → 1.0.4

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 +111 -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,12 @@ async function apiPost(pathname) {
115
183
  return res.ok
116
184
  }
117
185
 
186
+ async function apiGet(pathname) {
187
+ const res = await fetch(`${API_URL}${pathname}`)
188
+ if (!res.ok) return null
189
+ return await res.json().catch(() => null)
190
+ }
191
+
118
192
  async function apiHealth() {
119
193
  try {
120
194
  const controller = new AbortController()
@@ -146,14 +220,33 @@ async function ensureRelay() {
146
220
  return await apiPost('/api/relay/start')
147
221
  }
148
222
 
149
- // ── 打印头部 ──────────────────────────────────────────────────
150
- function printHeader() {
223
+ // ── 打印头部(ArkHelper 风格)─────────────────────────────────
224
+ async function printHeader() {
151
225
  console.clear()
152
- console.log(`${C.bold}${C.cyan} ◆ BHG-helper${C.r}`)
226
+ printLogo()
227
+
228
+ console.log(` v${VERSION} · DeepSeek LLM bridge for your AI coding tools`)
229
+ console.log()
230
+ console.log(` ${C.bold}一键配置${C.r}`)
231
+ for (const t of TOOLS_LIST) {
232
+ console.log(` ${C.cyan}◆${C.r} ${t.name} ${C.dim}— ${t.desc}${C.r}`)
233
+ }
234
+ console.log()
235
+
153
236
  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`)
237
+ const backendOk = await apiHealth()
238
+ const relayJson = await apiGet('/api/relay/status')
239
+ const relayOk = !!(relayJson && relayJson.data?.running)
240
+
241
+ console.log(
242
+ ` ${C.dim}API:${C.r} ${apiOk ? C.green + '✓' + C.r : C.red + '✗' + C.r}`
243
+ + ` ${C.dim}Backend:${C.r} ${backendOk ? C.green + '✓' + C.r : C.red + '✗' + C.r}`
244
+ + ` ${C.dim}Relay:${C.r} ${relayOk ? C.green + '✓' + C.r : C.red + '✗' + C.r}`
245
+ )
246
+
247
+ console.log()
248
+ console.log(` ${C.dim}powered by BH6BHG${C.r}`)
249
+ console.log()
157
250
  }
158
251
 
159
252
  // ── 一级菜单 ──────────────────────────────────────────────────
@@ -193,36 +286,27 @@ async function modelMenu() {
193
286
  if (choice === 'back') return 'back'
194
287
 
195
288
  if (choice === 'switch') {
196
- // 从 relay config 读取模型列表
197
289
  const models = []
198
290
  if (cfg && cfg.modelMap) {
199
291
  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 })
292
+ for (const [from] of Object.entries(cfg.modelMap)) {
293
+ if (!seen.has(from)) {
294
+ seen.add(from)
295
+ models.push(from)
204
296
  }
205
297
  }
206
298
  } 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
- )
299
+ models.push('deepseek-v4-pro', 'deepseek-v4-flash', 'deepseek-chat')
214
300
  }
215
301
 
216
302
  const modelChoice = await select({
217
303
  message: '选择模型',
218
304
  choices: [
219
305
  ...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,
306
+ name: m === currentModel ? `${C.green}● ${m}${C.r}` : ` ${m}`,
307
+ value: m,
224
308
  })),
225
- { name: `${C.dim}──────────${C.r}`, value: '---2', disabled: true },
309
+ { name: `${C.dim}──────────${C.r}`, value: '---', disabled: true },
226
310
  { name: '返回', value: 'back' },
227
311
  ],
228
312
  theme,
@@ -256,7 +340,6 @@ async function modelMenu() {
256
340
  saveConfig(next)
257
341
  console.log(`\n ${C.green}✓ API Key 已保存${C.r}`)
258
342
 
259
- // 自动启动中转服务
260
343
  console.log(` ${C.dim}正在启动中转服务...${C.r}`)
261
344
  const relayOk = await ensureRelay()
262
345
  console.log(` ${relayOk ? C.green + '✓ 中转服务已启动' + C.r : C.yellow + '中转服务启动失败(可稍后手动启动)' + C.r}\n`)
@@ -296,7 +379,6 @@ async function toolsMenu() {
296
379
  async function toolDetailMenu(toolKey) {
297
380
  const tool = TOOLS[toolKey]
298
381
 
299
- // Cursor / Continue / Trae — 无 CLI,显示安装方式
300
382
  if (!tool.cmd) {
301
383
  console.log(`\n ${C.bold}${tool.name}${C.r} — ${tool.desc}`)
302
384
  console.log(` ${C.dim}安装方式:${tool.install}${C.r}`)
@@ -305,7 +387,6 @@ async function toolDetailMenu(toolKey) {
305
387
  return 'back'
306
388
  }
307
389
 
308
- // 有 CLI 的工具
309
390
  const installed = checkInstalled(tool.cmd)
310
391
 
311
392
  const choices = []
@@ -344,7 +425,6 @@ async function toolDetailMenu(toolKey) {
344
425
  }
345
426
 
346
427
  if (action === 'launch') {
347
- // 启动前检查 API 配置
348
428
  if (!hasApiConfig()) {
349
429
  console.log(`\n ${C.red}API 未配置!请先在「模型选择」→「输入 API」中配置。${C.r}\n`)
350
430
  await confirm({ message: '按回车继续...', theme })
@@ -358,7 +438,6 @@ async function toolDetailMenu(toolKey) {
358
438
  return 'back'
359
439
  }
360
440
 
361
- // 写入 ~/.claude/settings.json(Claude Code 专用)
362
441
  if (toolKey === 'claude') {
363
442
  const currentModel = getCurrentModel()
364
443
  const dir = path.dirname(CLAUDE_SETTINGS_FILE)
@@ -383,7 +462,6 @@ async function toolDetailMenu(toolKey) {
383
462
  console.log(` ${C.green}✓ Claude Code 配置已写入${C.r}`)
384
463
  }
385
464
 
386
- // 写入 opencode 配置
387
465
  if (toolKey === 'opencode') {
388
466
  const file = TOOLS.opencode.configFile
389
467
  const dir = path.dirname(file)
@@ -436,7 +514,7 @@ async function langMenu() {
436
514
  async function main() {
437
515
  let running = true
438
516
  while (running) {
439
- printHeader()
517
+ await printHeader()
440
518
  const choice = await mainMenu()
441
519
 
442
520
  switch (choice) {
@@ -462,7 +540,6 @@ async function main() {
462
540
  if (result === 'exit') { console.log(`\n ${C.dim}再见。${C.r}\n`); running = false }
463
541
  break
464
542
  }
465
-
466
543
  }
467
544
  }
468
545
  }
@@ -474,4 +551,4 @@ main().catch((err) => {
474
551
  }
475
552
  console.error('Error:', err.message)
476
553
  process.exit(1)
477
- })
554
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bhg-helper",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "BHG-helper:AI 编程工具 DeepSeek 中转 & 可视化配置工具",
5
5
  "type": "module",
6
6
  "bin": {