bhg-helper 1.0.1 → 1.0.3
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/api/app.ts +1 -2
- package/api/relay/protocol.ts +0 -4
- package/cli/cli.js +54 -31
- package/dist/assets/index-C4x5glYN.js +136 -0
- package/dist/index.html +1 -1
- package/package.json +17 -21
- package/scripts/start.bat +16 -6
- package/scripts/start.ps1 +15 -3
- package/src/App.tsx +2 -47
- package/src/components/ErrorBoundary.tsx +0 -1
- package/src/components/Sidebar.tsx +0 -3
- package/src/lib/api.ts +0 -17
- package/src/lib/store.ts +0 -1
- package/src/pages/ConsolePage.tsx +0 -1
- package/src/pages/Dashboard.tsx +1 -1
- package/src/pages/Relay.tsx +11 -12
- package/api/routes/install.ts +0 -156
- package/dist/assets/index-DkkgyxVG.js +0 -156
- package/src/pages/Install.tsx +0 -187
package/api/app.ts
CHANGED
|
@@ -14,7 +14,6 @@ import backupRoutes from './routes/backups.js'
|
|
|
14
14
|
import logRoutes from './routes/logs.js'
|
|
15
15
|
import providerRoutes from './routes/providers.js'
|
|
16
16
|
import relayRoutes from './routes/relay.js'
|
|
17
|
-
import installRoutes from './routes/install.js'
|
|
18
17
|
|
|
19
18
|
const app: express.Application = express()
|
|
20
19
|
|
|
@@ -35,13 +34,13 @@ app.use('/api/backups', backupRoutes)
|
|
|
35
34
|
app.use('/api/logs', logRoutes)
|
|
36
35
|
app.use('/api/providers', providerRoutes)
|
|
37
36
|
app.use('/api/relay', relayRoutes)
|
|
38
|
-
app.use('/api/install', installRoutes)
|
|
39
37
|
|
|
40
38
|
app.get('/api/health', (_req: Request, res: Response) => {
|
|
41
39
|
res.json({ ok: true, data: { bhgHelperDir: BHG_HELPER_DIR } })
|
|
42
40
|
})
|
|
43
41
|
|
|
44
42
|
app.use((error: Error, _req: Request, res: Response, _next: NextFunction) => {
|
|
43
|
+
void _next
|
|
45
44
|
log('ERR', 'http', error.message)
|
|
46
45
|
res.status(500).json({ ok: false, error: error.message })
|
|
47
46
|
})
|
package/api/relay/protocol.ts
CHANGED
|
@@ -239,8 +239,6 @@ export interface AnthropicStreamEvent {
|
|
|
239
239
|
[key: string]: unknown
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
243
|
-
|
|
244
242
|
function makeId(): string {
|
|
245
243
|
// 用 crypto.randomUUID 如果存在
|
|
246
244
|
if (typeof globalThis.crypto?.randomUUID === 'function') {
|
|
@@ -278,7 +276,6 @@ export function* openAIStreamToAnthropicEvents(
|
|
|
278
276
|
let finishReason: string | null = null
|
|
279
277
|
let hasText = false
|
|
280
278
|
let toolBlockIndex = -1
|
|
281
|
-
let toolInputBuffer = ''
|
|
282
279
|
let toolId: string | null = null
|
|
283
280
|
let toolName: string | null = null
|
|
284
281
|
const usedModel = openAIChunks[0]?.model ?? model
|
|
@@ -357,7 +354,6 @@ export function* openAIStreamToAnthropicEvents(
|
|
|
357
354
|
}
|
|
358
355
|
}
|
|
359
356
|
if (tc.function?.arguments) {
|
|
360
|
-
toolInputBuffer += tc.function.arguments
|
|
361
357
|
yield {
|
|
362
358
|
type: 'content_block_delta',
|
|
363
359
|
index: toolBlockIndex,
|
package/cli/cli.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* BHG-helper CLI — 命令行交互菜单
|
|
4
|
-
* 一级菜单:模型选择 / 工具配置 / 语言配置 /
|
|
4
|
+
* 一级菜单:模型选择 / 工具配置 / 语言配置 / 退出
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { select, confirm, input } from '@inquirer/prompts'
|
|
8
|
-
import { execSync } from 'node:child_process'
|
|
8
|
+
import { execSync, spawn } from 'node:child_process'
|
|
9
9
|
import os from 'node:os'
|
|
10
10
|
import path from 'node:path'
|
|
11
11
|
import fs from 'node:fs'
|
|
12
|
+
import { fileURLToPath } from 'node:url'
|
|
12
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' }
|
|
@@ -16,7 +17,8 @@ const theme = { prefix: `${C.cyan}?${C.r}` }
|
|
|
16
17
|
|
|
17
18
|
// ── 常量 ──────────────────────────────────────────────────────
|
|
18
19
|
const RELAY_PORT = 8787
|
|
19
|
-
const
|
|
20
|
+
const API_URL = 'http://127.0.0.1:3001'
|
|
21
|
+
const PROJECT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
20
22
|
const BHG_HELPER_DIR = path.join(os.homedir(), '.bhg-helper')
|
|
21
23
|
const RELAY_CONFIG_FILE = path.join(BHG_HELPER_DIR, 'relay.config.json')
|
|
22
24
|
const CLAUDE_SETTINGS_FILE = path.join(os.homedir(), '.claude', 'settings.json')
|
|
@@ -108,10 +110,40 @@ function checkInstalled(cmd) {
|
|
|
108
110
|
} catch { return false }
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
function
|
|
113
|
+
async function apiPost(pathname) {
|
|
114
|
+
const res = await fetch(`${API_URL}${pathname}`, { method: 'POST' })
|
|
115
|
+
return res.ok
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function apiHealth() {
|
|
112
119
|
try {
|
|
113
|
-
|
|
114
|
-
|
|
120
|
+
const controller = new AbortController()
|
|
121
|
+
const timer = setTimeout(() => controller.abort(), 800)
|
|
122
|
+
const res = await fetch(`${API_URL}/api/health`, { signal: controller.signal })
|
|
123
|
+
clearTimeout(timer)
|
|
124
|
+
return res.ok
|
|
125
|
+
} catch {
|
|
126
|
+
return false
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function ensureBackend() {
|
|
131
|
+
if (await apiHealth()) return true
|
|
132
|
+
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'
|
|
133
|
+
const child = spawn(npmCmd, ['run', 'server:raw'], {
|
|
134
|
+
cwd: PROJECT_ROOT,
|
|
135
|
+
detached: true,
|
|
136
|
+
stdio: 'ignore',
|
|
137
|
+
shell: false,
|
|
138
|
+
})
|
|
139
|
+
child.unref()
|
|
140
|
+
await new Promise(resolve => setTimeout(resolve, 1500))
|
|
141
|
+
return await apiHealth()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function ensureRelay() {
|
|
145
|
+
if (!(await ensureBackend())) return false
|
|
146
|
+
return await apiPost('/api/relay/start')
|
|
115
147
|
}
|
|
116
148
|
|
|
117
149
|
// ── 打印头部 ──────────────────────────────────────────────────
|
|
@@ -121,8 +153,7 @@ function printHeader() {
|
|
|
121
153
|
const apiOk = hasApiConfig()
|
|
122
154
|
const model = getCurrentModel()
|
|
123
155
|
console.log(` ${C.dim}API${C.r} ${apiOk ? `${C.green}已配置${C.r}` : `${C.red}未配置${C.r}`}`)
|
|
124
|
-
console.log(` ${C.dim}模型${C.r} ${C.cyan}${model}${C.r}`)
|
|
125
|
-
console.log(` ${C.dim}UI${C.r} ${UI_URL}\n`)
|
|
156
|
+
console.log(` ${C.dim}模型${C.r} ${C.cyan}${model}${C.r}\n`)
|
|
126
157
|
}
|
|
127
158
|
|
|
128
159
|
// ── 一级菜单 ──────────────────────────────────────────────────
|
|
@@ -133,7 +164,6 @@ async function mainMenu() {
|
|
|
133
164
|
{ name: '模型选择', value: 'model', description: '切换模型 / 输入 API Key' },
|
|
134
165
|
{ name: '工具配置', value: 'tools', description: '配置各 AI 编程工具' },
|
|
135
166
|
{ name: '语言配置', value: 'lang', description: '界面语言设置' },
|
|
136
|
-
{ name: `${C.yellow}BHG-helper 网页界面${C.r}`, value: 'ui', description: '打开网页控制台' },
|
|
137
167
|
{ name: `${C.dim}──────────${C.r}`, value: '---', disabled: true },
|
|
138
168
|
{ name: '退出', value: 'exit' },
|
|
139
169
|
],
|
|
@@ -224,7 +254,12 @@ async function modelMenu() {
|
|
|
224
254
|
|
|
225
255
|
const next = { ...(cfg || {}), deepseekApiKey: newKey, port: cfg?.port || RELAY_PORT, deepseekBaseUrl: cfg?.deepseekBaseUrl || 'https://api.deepseek.com', modelMap: cfg?.modelMap || { 'deepseek-v4-pro': 'deepseek-v4-pro', 'deepseek-chat': 'deepseek-v4-pro' }, spoofProvider: cfg?.spoofProvider || 'deepseek', verbose: cfg?.verbose ?? true }
|
|
226
256
|
saveConfig(next)
|
|
227
|
-
console.log(`\n ${C.green}✓ API Key 已保存${C.r}
|
|
257
|
+
console.log(`\n ${C.green}✓ API Key 已保存${C.r}`)
|
|
258
|
+
|
|
259
|
+
// 自动启动中转服务
|
|
260
|
+
console.log(` ${C.dim}正在启动中转服务...${C.r}`)
|
|
261
|
+
const relayOk = await ensureRelay()
|
|
262
|
+
console.log(` ${relayOk ? C.green + '✓ 中转服务已启动' + C.r : C.yellow + '中转服务启动失败(可稍后手动启动)' + C.r}\n`)
|
|
228
263
|
await confirm({ message: '按回车继续...', theme })
|
|
229
264
|
return 'back'
|
|
230
265
|
}
|
|
@@ -254,13 +289,6 @@ async function toolsMenu() {
|
|
|
254
289
|
if (choice === 'exit') return 'exit'
|
|
255
290
|
if (choice === 'back') return 'back'
|
|
256
291
|
|
|
257
|
-
if (choice === 'ui') {
|
|
258
|
-
openBrowser(UI_URL)
|
|
259
|
-
console.log(`\n ${C.green}✓ 浏览器已打开 → ${UI_URL}${C.r}\n`)
|
|
260
|
-
await confirm({ message: '按回车继续...', theme })
|
|
261
|
-
return 'back'
|
|
262
|
-
}
|
|
263
|
-
|
|
264
292
|
return await toolDetailMenu(choice)
|
|
265
293
|
}
|
|
266
294
|
|
|
@@ -268,18 +296,11 @@ async function toolsMenu() {
|
|
|
268
296
|
async function toolDetailMenu(toolKey) {
|
|
269
297
|
const tool = TOOLS[toolKey]
|
|
270
298
|
|
|
271
|
-
// Cursor / Continue / Trae — 无 CLI
|
|
299
|
+
// Cursor / Continue / Trae — 无 CLI,显示安装方式
|
|
272
300
|
if (!tool.cmd) {
|
|
273
301
|
console.log(`\n ${C.bold}${tool.name}${C.r} — ${tool.desc}`)
|
|
274
302
|
console.log(` ${C.dim}安装方式:${tool.install}${C.r}`)
|
|
275
|
-
console.log(` ${C.yellow}${tool.name}
|
|
276
|
-
|
|
277
|
-
const openUi = await confirm({ message: '打开 BHG-helper 网页界面?', default: true, theme })
|
|
278
|
-
if (openUi) {
|
|
279
|
-
openBrowser(UI_URL)
|
|
280
|
-
console.log(` ${C.green}✓ 浏览器已打开${C.r}`)
|
|
281
|
-
}
|
|
282
|
-
console.log()
|
|
303
|
+
console.log(` ${C.yellow}当前纯命令行版暂不自动配置 ${tool.name}${C.r}\n`)
|
|
283
304
|
await confirm({ message: '按回车继续...', theme })
|
|
284
305
|
return 'back'
|
|
285
306
|
}
|
|
@@ -330,6 +351,13 @@ async function toolDetailMenu(toolKey) {
|
|
|
330
351
|
return 'back'
|
|
331
352
|
}
|
|
332
353
|
|
|
354
|
+
const relayOk = await ensureRelay()
|
|
355
|
+
if (!relayOk) {
|
|
356
|
+
console.log(`\n ${C.red}中转服务启动失败,请重新运行 BHG-helper。${C.r}\n`)
|
|
357
|
+
await confirm({ message: '按回车继续...', theme })
|
|
358
|
+
return 'back'
|
|
359
|
+
}
|
|
360
|
+
|
|
333
361
|
// 写入 ~/.claude/settings.json(Claude Code 专用)
|
|
334
362
|
if (toolKey === 'claude') {
|
|
335
363
|
const currentModel = getCurrentModel()
|
|
@@ -435,11 +463,6 @@ async function main() {
|
|
|
435
463
|
break
|
|
436
464
|
}
|
|
437
465
|
|
|
438
|
-
case 'ui':
|
|
439
|
-
openBrowser(UI_URL)
|
|
440
|
-
console.log(`\n ${C.green}✓ 浏览器已打开 → ${UI_URL}${C.r}\n`)
|
|
441
|
-
await confirm({ message: '按回车继续...', theme })
|
|
442
|
-
break
|
|
443
466
|
}
|
|
444
467
|
}
|
|
445
468
|
}
|