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.
- package/cli/cli.js +111 -34
- 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 = {
|
|
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
|
-
|
|
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
|
|
155
|
-
|
|
156
|
-
|
|
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
|
|
201
|
-
if (!seen.has(
|
|
202
|
-
seen.add(
|
|
203
|
-
models.push(
|
|
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
|
|
221
|
-
|
|
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: '---
|
|
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
|
+
})
|