@bitseek/hermes-webui 0.1.2 → 0.1.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/README.md +6 -0
- package/bin/hermes-webui.mjs +65 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -81,6 +81,9 @@ hermes-webui doctor
|
|
|
81
81
|
- 创建运行时状态目录。
|
|
82
82
|
- 创建并管理 Python venv。
|
|
83
83
|
- 安装 WebUI 所需的 Python 依赖。
|
|
84
|
+
- 检查当前 Python 是否能导入 Hermes Agent;如果不能,会把
|
|
85
|
+
`HERMES_WEBUI_AGENT_DIR` 指向的 Hermes Agent 以 editable 模式安装到 WebUI
|
|
86
|
+
venv。
|
|
84
87
|
- 默认启用 BitSeek 外观和扩展入口,包括 “AI 同事” 页面。
|
|
85
88
|
- 启动 BitSeek Claw WebUI 服务。
|
|
86
89
|
- 等待 `/health` 健康检查通过。
|
|
@@ -170,6 +173,9 @@ hermes-webui doctor
|
|
|
170
173
|
`HERMES_WEBUI_PYTHON`。
|
|
171
174
|
- `Hermes Agent dir: not found`:设置 `HERMES_WEBUI_AGENT_DIR` 指向包含
|
|
172
175
|
`run_agent.py` 的 Hermes Agent 目录。
|
|
176
|
+
- `AIAgent importable: fail`:当前 WebUI Python 不能导入 Hermes Agent。运行
|
|
177
|
+
`hermes-webui start` 会自动尝试修复;如果仍失败,请查看 `server.log` 和
|
|
178
|
+
`pip install -e` 的错误输出。
|
|
173
179
|
- `WebUI runtime is missing`:发布包缺少必要文件,需要维护者重新打包并发布。
|
|
174
180
|
|
|
175
181
|
## 维护者说明
|
package/bin/hermes-webui.mjs
CHANGED
|
@@ -218,6 +218,54 @@ function pythonImportsOk(python) {
|
|
|
218
218
|
return result.status === 0
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
function aiAgentImportCheck(python, agentDir) {
|
|
222
|
+
if (!python || !agentDir) {
|
|
223
|
+
return { ok: false, error: 'python or Hermes Agent dir not found' }
|
|
224
|
+
}
|
|
225
|
+
const env = {
|
|
226
|
+
...process.env,
|
|
227
|
+
HERMES_WEBUI_AGENT_DIR: agentDir,
|
|
228
|
+
PYTHONPATH: process.env.PYTHONPATH
|
|
229
|
+
? `${agentDir}${delimiter}${process.env.PYTHONPATH}`
|
|
230
|
+
: agentDir,
|
|
231
|
+
}
|
|
232
|
+
const result = spawnSync(python, ['-c', 'from run_agent import AIAgent; print("ok")'], {
|
|
233
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
234
|
+
env,
|
|
235
|
+
encoding: 'utf8',
|
|
236
|
+
})
|
|
237
|
+
return {
|
|
238
|
+
ok: result.status === 0,
|
|
239
|
+
error: (result.stderr || result.stdout || '').trim(),
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function ensureAiAgentImportable(python, agentDir) {
|
|
244
|
+
if (!agentDir) return
|
|
245
|
+
|
|
246
|
+
let check = aiAgentImportCheck(python, agentDir)
|
|
247
|
+
if (check.ok) return
|
|
248
|
+
|
|
249
|
+
const pyprojectPath = join(agentDir, 'pyproject.toml')
|
|
250
|
+
if (!existsSync(pyprojectPath)) {
|
|
251
|
+
throw new Error(`Hermes Agent cannot be imported and pyproject.toml was not found: ${pyprojectPath}`)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
console.log(`Installing Hermes Agent into WebUI Python env: ${agentDir}`)
|
|
255
|
+
const install = spawnSync(python, ['-m', 'pip', 'install', '-e', agentDir], {
|
|
256
|
+
stdio: 'inherit',
|
|
257
|
+
env: process.env,
|
|
258
|
+
})
|
|
259
|
+
if (install.status !== 0) {
|
|
260
|
+
throw new Error(`Failed to install Hermes Agent into WebUI Python env (exit ${install.status})`)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
check = aiAgentImportCheck(python, agentDir)
|
|
264
|
+
if (!check.ok) {
|
|
265
|
+
throw new Error(`Hermes Agent still cannot be imported after install:\n${check.error}`)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
221
269
|
function preparePython() {
|
|
222
270
|
if (process.env.HERMES_WEBUI_PYTHON) {
|
|
223
271
|
return resolve(expandHome(process.env.HERMES_WEBUI_PYTHON))
|
|
@@ -311,7 +359,7 @@ async function waitForHealth(host, port, child, timeoutMs = 30000) {
|
|
|
311
359
|
|
|
312
360
|
async function start(options) {
|
|
313
361
|
if (!existsSync(bootstrapPath) || !existsSync(serverPath)) {
|
|
314
|
-
throw new Error('
|
|
362
|
+
throw new Error('WebUI runtime is missing. Run npm run sync:vendor.')
|
|
315
363
|
}
|
|
316
364
|
|
|
317
365
|
const existing = readPid()
|
|
@@ -329,6 +377,7 @@ async function start(options) {
|
|
|
329
377
|
mkdirSync(paths.dir, { recursive: true })
|
|
330
378
|
const python = preparePython()
|
|
331
379
|
const env = buildEnv(python, options)
|
|
380
|
+
ensureAiAgentImportable(python, env.HERMES_WEBUI_AGENT_DIR)
|
|
332
381
|
const args = [serverPath]
|
|
333
382
|
|
|
334
383
|
if (options.foreground) {
|
|
@@ -383,7 +432,7 @@ async function start(options) {
|
|
|
383
432
|
started_at: new Date().toISOString(),
|
|
384
433
|
})
|
|
385
434
|
|
|
386
|
-
console.log(`Starting
|
|
435
|
+
console.log(`Starting BitSeek Claw WebUI (PID ${child.pid}, port ${options.port})...`)
|
|
387
436
|
const health = await waitForHealth(options.host, options.port, child)
|
|
388
437
|
if (!health.ok) {
|
|
389
438
|
console.log(`Health check failed: ${health.error || health.status || 'unknown error'}`)
|
|
@@ -391,7 +440,7 @@ async function start(options) {
|
|
|
391
440
|
process.exit(1)
|
|
392
441
|
}
|
|
393
442
|
|
|
394
|
-
console.log('
|
|
443
|
+
console.log('BitSeek Claw WebUI started')
|
|
395
444
|
console.log(`URL: http://${options.host === '127.0.0.1' ? 'localhost' : options.host}:${options.port}`)
|
|
396
445
|
console.log(`Log: ${paths.log}`)
|
|
397
446
|
if (options.open) openBrowser(options.host, options.port)
|
|
@@ -408,13 +457,13 @@ async function stop() {
|
|
|
408
457
|
process.exit(1)
|
|
409
458
|
}
|
|
410
459
|
}
|
|
411
|
-
console.log('
|
|
460
|
+
console.log('BitSeek Claw WebUI is not running')
|
|
412
461
|
return
|
|
413
462
|
}
|
|
414
463
|
|
|
415
464
|
if (!isRunning(pid)) {
|
|
416
465
|
removePid()
|
|
417
|
-
console.log(`
|
|
466
|
+
console.log(`BitSeek Claw WebUI was not running; cleaned stale PID ${pid}`)
|
|
418
467
|
return
|
|
419
468
|
}
|
|
420
469
|
|
|
@@ -427,7 +476,7 @@ async function stop() {
|
|
|
427
476
|
process.kill(pid, 'SIGKILL')
|
|
428
477
|
}
|
|
429
478
|
removePid()
|
|
430
|
-
console.log(`
|
|
479
|
+
console.log(`BitSeek Claw WebUI stopped (PID ${pid})`)
|
|
431
480
|
}
|
|
432
481
|
|
|
433
482
|
async function restart(options) {
|
|
@@ -510,11 +559,17 @@ async function doctor(options) {
|
|
|
510
559
|
const agentDir = resolveAgentDir()
|
|
511
560
|
add('Hermes Agent dir', Boolean(agentDir), agentDir || 'not found')
|
|
512
561
|
add('run_agent.py', Boolean(agentDir && existsSync(join(agentDir, 'run_agent.py'))), agentDir ? join(agentDir, 'run_agent.py') : 'not found')
|
|
562
|
+
if (python && agentDir) {
|
|
563
|
+
const importCheck = aiAgentImportCheck(python, agentDir)
|
|
564
|
+
add('AIAgent importable', importCheck.ok, importCheck.ok ? python : importCheck.error || python)
|
|
565
|
+
} else {
|
|
566
|
+
add('AIAgent importable', false, 'python or Hermes Agent dir not found')
|
|
567
|
+
}
|
|
513
568
|
add('port available', !(await portOccupied(options.host, options.port)), `${options.host}:${options.port}`)
|
|
514
|
-
add('no
|
|
515
|
-
add('no
|
|
516
|
-
add('no
|
|
517
|
-
add('no
|
|
569
|
+
add('no bundled .env', !existsSync(join(vendorRoot, '.env')), join(vendorRoot, '.env'))
|
|
570
|
+
add('no bundled .git', !existsSync(join(vendorRoot, '.git')), join(vendorRoot, '.git'))
|
|
571
|
+
add('no bundled .venv', !existsSync(join(vendorRoot, '.venv')), join(vendorRoot, '.venv'))
|
|
572
|
+
add('no bundled node_modules', !existsSync(join(vendorRoot, 'node_modules')), join(vendorRoot, 'node_modules'))
|
|
518
573
|
|
|
519
574
|
for (const check of checks) {
|
|
520
575
|
console.log(`${check.ok ? 'ok' : 'fail'} - ${check.name}${check.detail ? `: ${check.detail}` : ''}`)
|