bilisum 1.20.0-alpha.1 → 1.20.0-alpha.2
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 +34 -3
- package/bin/bilisum.js +160 -22
- package/lib/api.js +12 -1
- package/lib/args.js +15 -0
- package/lib/env.js +138 -0
- package/lib/output.js +36 -9
- package/lib/runtime.js +125 -26
- package/lib/server.js +30 -12
- package/lib/token.js +5 -0
- package/package.json +5 -2
- package/runtime/VERSION +1 -0
- package/runtime/apps/service/pyproject.toml +32 -0
- package/runtime/apps/service/src/video_sum_service/__init__.py +1 -0
- package/runtime/apps/service/src/video_sum_service/__main__.py +5 -0
- package/runtime/apps/service/src/video_sum_service/app.py +543 -0
- package/runtime/apps/service/src/video_sum_service/assets/asr_test_zh.wav +0 -0
- package/runtime/apps/service/src/video_sum_service/auth.py +132 -0
- package/runtime/apps/service/src/video_sum_service/bilibili_cookies.py +201 -0
- package/runtime/apps/service/src/video_sum_service/cli_idle.py +92 -0
- package/runtime/apps/service/src/video_sum_service/context.py +26 -0
- package/runtime/apps/service/src/video_sum_service/integrations.py +791 -0
- package/runtime/apps/service/src/video_sum_service/knowledge/__init__.py +5 -0
- package/runtime/apps/service/src/video_sum_service/knowledge/index_service.py +523 -0
- package/runtime/apps/service/src/video_sum_service/knowledge/local_llm.py +357 -0
- package/runtime/apps/service/src/video_sum_service/knowledge/rag_service.py +492 -0
- package/runtime/apps/service/src/video_sum_service/knowledge/tag_service.py +242 -0
- package/runtime/apps/service/src/video_sum_service/main.py +27 -0
- package/runtime/apps/service/src/video_sum_service/probe_script.py +199 -0
- package/runtime/apps/service/src/video_sum_service/repository.py +1426 -0
- package/runtime/apps/service/src/video_sum_service/routers/__init__.py +1 -0
- package/runtime/apps/service/src/video_sum_service/routers/knowledge.py +283 -0
- package/runtime/apps/service/src/video_sum_service/routers/system.py +664 -0
- package/runtime/apps/service/src/video_sum_service/routers/tasks.py +410 -0
- package/runtime/apps/service/src/video_sum_service/routers/videos.py +952 -0
- package/runtime/apps/service/src/video_sum_service/runtime_startup.py +296 -0
- package/runtime/apps/service/src/video_sum_service/runtime_support.py +3207 -0
- package/runtime/apps/service/src/video_sum_service/schemas.py +538 -0
- package/runtime/apps/service/src/video_sum_service/settings_manager.py +227 -0
- package/runtime/apps/service/src/video_sum_service/task_artifacts.py +262 -0
- package/runtime/apps/service/src/video_sum_service/task_exports.py +312 -0
- package/runtime/apps/service/src/video_sum_service/transcribe_worker.py +9 -0
- package/runtime/apps/service/src/video_sum_service/video_assets.py +686 -0
- package/runtime/apps/service/src/video_sum_service/worker.py +653 -0
- package/runtime/apps/web/static/apple-touch-icon.png +0 -0
- package/runtime/apps/web/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/runtime/apps/web/static/assets/icons/icon-180.png +0 -0
- package/runtime/apps/web/static/assets/icons/icon-512.png +0 -0
- package/runtime/apps/web/static/assets/icons/icon.svg +17 -0
- package/runtime/apps/web/static/assets/index-POgEkQWf.js +357 -0
- package/runtime/apps/web/static/assets/index-S9QooflZ.css +1 -0
- package/runtime/apps/web/static/favicon-32x32.png +0 -0
- package/runtime/apps/web/static/favicon.ico +0 -0
- package/runtime/apps/web/static/favicon.svg +17 -0
- package/runtime/apps/web/static/index.html +37 -0
- package/runtime/apps/web/static/js/api.js +123 -0
- package/runtime/apps/web/static/js/main.js +952 -0
- package/runtime/apps/web/static/js/state.js +28 -0
- package/runtime/apps/web/static/js/utils.js +81 -0
- package/runtime/apps/web/static/js/views/home.js +604 -0
- package/runtime/apps/web/static/js/views/settings.js +550 -0
- package/runtime/apps/web/static/styles.css +2206 -0
- package/runtime/packages/core/pyproject.toml +18 -0
- package/runtime/packages/core/src/video_sum_core/__init__.py +1 -0
- package/runtime/packages/core/src/video_sum_core/errors.py +30 -0
- package/runtime/packages/core/src/video_sum_core/markdown_exports.py +167 -0
- package/runtime/packages/core/src/video_sum_core/models/__init__.py +1 -0
- package/runtime/packages/core/src/video_sum_core/models/tasks.py +82 -0
- package/runtime/packages/core/src/video_sum_core/pipeline/__init__.py +1 -0
- package/runtime/packages/core/src/video_sum_core/pipeline/base.py +46 -0
- package/runtime/packages/core/src/video_sum_core/pipeline/real.py +5765 -0
- package/runtime/packages/core/src/video_sum_core/transcribe_funasr_subprocess.py +469 -0
- package/runtime/packages/core/src/video_sum_core/transcribe_subprocess.py +173 -0
- package/runtime/packages/core/src/video_sum_core/twelvelabs.py +167 -0
- package/runtime/packages/core/src/video_sum_core/utils.py +127 -0
- package/runtime/packages/infra/pyproject.toml +16 -0
- package/runtime/packages/infra/src/video_sum_infra/__init__.py +1 -0
- package/runtime/packages/infra/src/video_sum_infra/app.py +39 -0
- package/runtime/packages/infra/src/video_sum_infra/config.py +725 -0
- package/runtime/packages/infra/src/video_sum_infra/db.py +34 -0
- package/runtime/packages/infra/src/video_sum_infra/llm.py +166 -0
- package/runtime/packages/infra/src/video_sum_infra/logging.py +54 -0
- package/runtime/packages/infra/src/video_sum_infra/paths.py +6 -0
- package/runtime/packages/infra/src/video_sum_infra/prompt_library.py +367 -0
- package/runtime/packages/infra/src/video_sum_infra/runtime.py +724 -0
package/README.md
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# BiliSum CLI
|
|
2
2
|
|
|
3
|
-
`bilisum` 是 BiliSum
|
|
3
|
+
`bilisum` 是 BiliSum 的命令行控制面:**控制已安装的 BiliSum(桌面端 / 已运行的服务),或完全独立运行**(未安装桌面端也能用),让 Agent / 脚本做视频理解(转写 + 摘要)。
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
# 摘要一个视频(默认输出:元数据 + 总结内容,不含字幕)
|
|
7
7
|
bilisum summarize "https://www.bilibili.com/video/BV1xxxx"
|
|
8
8
|
|
|
9
|
+
# 快速摘要卡片(跳过知识笔记 LLM 调用,更快)
|
|
10
|
+
bilisum brief "https://www.bilibili.com/video/BV1xxxx"
|
|
11
|
+
|
|
9
12
|
# 输出 JSON 给 Agent 解析(stdout 只含 JSON,进度在 stderr)
|
|
10
13
|
bilisum summarize "https://www.bilibili.com/video/BV1xxxx" --format json
|
|
11
14
|
|
|
@@ -26,12 +29,35 @@ bilisum --help
|
|
|
26
29
|
bilisum summarize --help
|
|
27
30
|
```
|
|
28
31
|
|
|
32
|
+
## 环境(Environment)
|
|
33
|
+
|
|
34
|
+
四种环境决定连接哪个服务、用哪套数据:
|
|
35
|
+
|
|
36
|
+
| 环境 | 说明 | 地址 |
|
|
37
|
+
|------|------|------|
|
|
38
|
+
| `desktop` | 连接桌面端服务(控制现有程序,同库同知识库) | `127.0.0.1:3838` |
|
|
39
|
+
| `cli` | **独立环境**:自带运行时与配置,无需桌面端 | `127.0.0.1:3839` |
|
|
40
|
+
| `custom` | 任意 host/port/token(Docker、远程) | 自定 |
|
|
41
|
+
| `auto` | 默认:桌面端可达则 desktop,否则 cli | 自动 |
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bilisum env # 查看环境状态
|
|
45
|
+
bilisum env use cli # 切换到独立环境
|
|
46
|
+
bilisum env setup # 初始化独立环境(内置运行时,需 Python 3.12)
|
|
47
|
+
bilisum --setting # 打开当前环境的网页设置(cli 环境无需桌面端)
|
|
48
|
+
bilisum summarize <url> --environment cli # 单次使用某环境
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> 完整文档见 [BiliSum CLI 使用指南](https://github.com/lycohana/BiliSum/blob/master/docs/cli.md)。
|
|
52
|
+
|
|
29
53
|
## 输出格式
|
|
30
54
|
|
|
31
|
-
- **`markdown`(默认)**:YAML 元数据头(task_id / status / title / video_id / source)+
|
|
55
|
+
- **`markdown`(默认)**:YAML 元数据头(task_id / status / title / video_id / source)+ 总结内容(`summarize` 为知识笔记,`brief` 为摘要卡片),**不含字幕**;加 `--with-transcript` 会追加转写全文。
|
|
32
56
|
- **`json`**:完整任务对象(含 `result`,字幕在 `result.transcript_text`)。
|
|
33
57
|
- **`transcript`**:仅转写全文(`bilisum transcribe` 的默认输出)。
|
|
34
58
|
|
|
59
|
+
任务等待期间进度显示在 stderr(`[task_id] 12% [stage] 消息`),stdout 保持干净。
|
|
60
|
+
|
|
35
61
|
## 它如何"控制现有程序"
|
|
36
62
|
|
|
37
63
|
- 默认连接桌面端服务 `http://127.0.0.1:3838`,任务、结果、知识库都与桌面端**同一个数据库**。
|
|
@@ -41,13 +67,15 @@ bilisum summarize --help
|
|
|
41
67
|
|
|
42
68
|
## 服务不在线时
|
|
43
69
|
|
|
44
|
-
`summarize / transcribe / status / tasks` 会先探活;如果 `127.0.0.1:3838` 没有服务,就**用桌面端数据目录自动后台拉起一个**(优先用桌面端自带的托管 Python 运行时,其次系统 Python 3.12),等 `/health` 就绪后继续。CLI 拉起的服务进程记录在 `{数据根}\cli-runtime
|
|
70
|
+
`summarize / brief / transcribe / status / tasks` 会先探活;如果 `127.0.0.1:3838` 没有服务,就**用桌面端数据目录自动后台拉起一个**(优先用桌面端自带的托管 Python 运行时,其次系统 Python 3.12),等 `/health` 就绪后继续。CLI 拉起的服务进程记录在 `{数据根}\cli-runtime-<port>.json`:
|
|
45
71
|
|
|
46
72
|
```bash
|
|
47
73
|
bilisum start --no-open # 前台启动(可选,一般不用)
|
|
48
74
|
bilisum stop # 停止 CLI 拉起的服务
|
|
49
75
|
```
|
|
50
76
|
|
|
77
|
+
**无任务自动关闭**:CLI 后台拉起的服务默认空闲 10 分钟(且无排队/运行中任务)自动退出,可用 `--idle-timeout <sec>` 或 `BILISUM_CLI_IDLE_TIMEOUT` 环境变量调整。该机制只作用于 CLI 自己拉起的服务(通过 `VIDEO_SUM_CLI_MANAGED` 标记),**桌面端启动的服务绝不会被自动关闭**。
|
|
78
|
+
|
|
51
79
|
> 注意:桌面端自己运行的服务不受 `bilisum stop` 影响(没有 cli-runtime 记录时会提示)。
|
|
52
80
|
|
|
53
81
|
## 安装
|
|
@@ -71,11 +99,14 @@ npm install -g bilisum # 或 npx bilisum ...
|
|
|
71
99
|
| `--page <n> / --all-pages` | 多 P 视频分 P 选择 |
|
|
72
100
|
| `--visual-note <mode>` | `text` / `frame_insert` / `vlm_integrated` |
|
|
73
101
|
| `--no-wait` | 只创建任务,立即返回 task_id |
|
|
102
|
+
| `--idle-timeout <sec>` | CLI 后台服务空闲自动关闭秒数(默认 600) |
|
|
74
103
|
| `--timeout <sec>` | 等待完成超时(默认 3600) |
|
|
75
104
|
| `--quiet` | 抑制 stderr 进度输出 |
|
|
76
105
|
|
|
77
106
|
> 说明:任务转写与摘要在服务端一体执行;`transcribe` 输出转写全文,任务仍会顺带生成摘要。语言 / 摘要模式等参数跟随服务端设置。
|
|
78
107
|
|
|
108
|
+
> 完整文档见 [BiliSum CLI 使用指南](https://github.com/lycohana/BiliSum/blob/master/docs/cli.md)。
|
|
109
|
+
|
|
79
110
|
## 桌面端内置
|
|
80
111
|
|
|
81
112
|
桌面版 BiliSum 打包时会把 CLI 一并放进安装目录的 `resources/cli`,可用:
|
package/bin/bilisum.js
CHANGED
|
@@ -8,6 +8,7 @@ const { existsSync } = require("node:fs");
|
|
|
8
8
|
const {
|
|
9
9
|
baseUrlFor,
|
|
10
10
|
getTask,
|
|
11
|
+
getTaskProgress,
|
|
11
12
|
listTasks,
|
|
12
13
|
probeVideo,
|
|
13
14
|
uploadLocalMedia,
|
|
@@ -16,13 +17,20 @@ const {
|
|
|
16
17
|
} = require("../lib/api");
|
|
17
18
|
const { UsageError, parseTaskCommandArgs } = require("../lib/args");
|
|
18
19
|
const { formatTasks, emit } = require("../lib/output");
|
|
19
|
-
const { readVersion, locateBilisumDataRoot, findPython } = require("../lib/runtime");
|
|
20
|
+
const { readVersion, locateBilisumDataRoot, findPython, cliHomeDir, ensureCliVenv, cliVenvPython, findSystemPython } = require("../lib/runtime");
|
|
20
21
|
const { readToken } = require("../lib/token");
|
|
21
22
|
const {
|
|
22
23
|
ensureService,
|
|
23
24
|
stopService,
|
|
24
25
|
spawnService,
|
|
25
26
|
} = require("../lib/server");
|
|
27
|
+
const {
|
|
28
|
+
resolveEnvironment,
|
|
29
|
+
readConfig,
|
|
30
|
+
writeConfig,
|
|
31
|
+
ENV_NAMES,
|
|
32
|
+
isDesktopReachable,
|
|
33
|
+
} = require("../lib/env");
|
|
26
34
|
|
|
27
35
|
const RELEASES_URL = "https://github.com/lycohana/BiliSum/releases/latest";
|
|
28
36
|
const REPO_URL = "https://github.com/lycohana/BiliSum";
|
|
@@ -37,26 +45,32 @@ function printHelp() {
|
|
|
37
45
|
console.log("AI 视频总结与知识库工具:CLI 直接控制已安装的 BiliSum(桌面端/服务)。");
|
|
38
46
|
console.log("");
|
|
39
47
|
console.log("Usage:");
|
|
40
|
-
console.log(" npx bilisum summarize <url|file> [options] Summarize a video (
|
|
48
|
+
console.log(" npx bilisum summarize <url|file> [options] Summarize a video (knowledge note)");
|
|
49
|
+
console.log(" npx bilisum brief <url|file> [options] Fast summary card only (skips note)");
|
|
41
50
|
console.log(" npx bilisum transcribe <url|file> [options] Print the transcript of a video");
|
|
42
51
|
console.log(" npx bilisum status <task-id> [--json] Show task status");
|
|
43
52
|
console.log(" npx bilisum tasks [--limit N] [--json] List tasks");
|
|
44
|
-
console.log(" npx bilisum
|
|
53
|
+
console.log(" npx bilisum env [use <name>|setup] Environment status / switch / setup");
|
|
54
|
+
console.log(" npx bilisum --setting Open the web settings page");
|
|
55
|
+
console.log(" npx bilisum start [options] Start the service");
|
|
45
56
|
console.log(" npx bilisum stop Stop the CLI-started BiliSum service");
|
|
46
|
-
console.log(" npx bilisum doctor Check service/Python setup");
|
|
57
|
+
console.log(" npx bilisum doctor Check environment/service/Python setup");
|
|
47
58
|
console.log(" npx bilisum --version Print package version");
|
|
48
59
|
console.log(" npx bilisum release Open the latest GitHub release");
|
|
49
60
|
console.log("");
|
|
61
|
+
console.log("环境(bilisum env):desktop 连桌面端服务 / cli 独立环境(自带运行时)/ custom 自定义 / auto 自动");
|
|
62
|
+
console.log("");
|
|
50
63
|
console.log("默认连接桌面端服务 http://127.0.0.1:3838;未运行时自动用桌面端数据目录后台拉起。");
|
|
51
64
|
console.log("");
|
|
52
65
|
console.log("Options (all commands):");
|
|
53
66
|
console.log(" --host <host> Host, default 127.0.0.1");
|
|
54
67
|
console.log(" --port <port> Port, default 3838");
|
|
55
|
-
console.log(" --data <path>
|
|
56
|
-
console.log(" --token <token> Access token (default:
|
|
68
|
+
console.log(" --data <path> Data root (default: env-dependent)");
|
|
69
|
+
console.log(" --token <token> Access token (default: env-dependent)");
|
|
70
|
+
console.log(" --environment <name> desktop | cli | custom | auto (one-shot)");
|
|
57
71
|
console.log(" --env KEY=VALUE Extra env for a CLI-started service");
|
|
58
72
|
console.log("");
|
|
59
|
-
console.log("Options (summarize/transcribe):");
|
|
73
|
+
console.log("Options (summarize/brief/transcribe):");
|
|
60
74
|
console.log(" --page <n> Process a specific P for multi-P videos");
|
|
61
75
|
console.log(" --all-pages Process every P of a multi-P video");
|
|
62
76
|
console.log(" --visual-note <mode> text | frame_insert | vlm_integrated");
|
|
@@ -65,11 +79,13 @@ function printHelp() {
|
|
|
65
79
|
console.log(" --output <path> / -o <path> Write result to a file instead of stdout");
|
|
66
80
|
console.log(" --with-transcript Append the full transcript to markdown output");
|
|
67
81
|
console.log(" --no-wait Create the task and exit immediately");
|
|
82
|
+
console.log(" --idle-timeout <sec> CLI 后台服务无任务自动关闭秒数,默认 600");
|
|
68
83
|
console.log(" --timeout <sec> Max wait for completion, default 3600");
|
|
69
84
|
console.log(" --startup-timeout <sec> Max wait for service startup, default 300");
|
|
70
85
|
console.log(" --quiet / -q Suppress progress output (stderr)");
|
|
71
86
|
console.log("");
|
|
72
87
|
console.log("默认输出为元数据 + 总结内容(不含字幕),需要字幕请加 --with-transcript。");
|
|
88
|
+
console.log("brief 只生成摘要卡片(跳过知识笔记 LLM 调用,更快)。");
|
|
73
89
|
console.log("Options (start):");
|
|
74
90
|
console.log(" --no-open Do not open the browser");
|
|
75
91
|
console.log("");
|
|
@@ -85,6 +101,9 @@ function parseConnectionOptions(args, { allowTaskId = false } = {}) {
|
|
|
85
101
|
data: process.env.BILISUM_DATA || locateBilisumDataRoot(),
|
|
86
102
|
token: process.env.BILISUM_TOKEN || "",
|
|
87
103
|
env: [],
|
|
104
|
+
environment: "",
|
|
105
|
+
_hostSet: false,
|
|
106
|
+
_portSet: false,
|
|
88
107
|
json: false,
|
|
89
108
|
limit: 20,
|
|
90
109
|
help: false,
|
|
@@ -102,14 +121,22 @@ function parseConnectionOptions(args, { allowTaskId = false } = {}) {
|
|
|
102
121
|
const arg = args[index];
|
|
103
122
|
if (arg === "--host") {
|
|
104
123
|
options.host = readValue(++index, arg);
|
|
124
|
+
options._hostSet = true;
|
|
105
125
|
} else if (arg === "--port" || arg === "-p") {
|
|
106
126
|
options.port = readValue(++index, arg);
|
|
127
|
+
options._portSet = true;
|
|
107
128
|
} else if (arg === "--python") {
|
|
108
129
|
options.python = readValue(++index, arg);
|
|
109
130
|
} else if (arg === "--data") {
|
|
110
131
|
options.data = readValue(++index, arg);
|
|
111
132
|
} else if (arg === "--token") {
|
|
112
133
|
options.token = readValue(++index, arg);
|
|
134
|
+
} else if (arg === "--environment" || arg === "--env-mode") {
|
|
135
|
+
const name = readValue(++index, arg);
|
|
136
|
+
if (!ENV_NAMES.includes(name)) {
|
|
137
|
+
throw new UsageError(`--environment 仅支持 ${ENV_NAMES.join(" / ")},收到:${name}`);
|
|
138
|
+
}
|
|
139
|
+
options.environment = name;
|
|
113
140
|
} else if (arg === "--env" || arg === "-e") {
|
|
114
141
|
options.env.push(readValue(++index, arg));
|
|
115
142
|
} else if (arg === "--json") {
|
|
@@ -157,6 +184,12 @@ async function ensureAuthenticatedService(options, log) {
|
|
|
157
184
|
// Resolve the token first (desktop token / auth.json / --token / env) and
|
|
158
185
|
// inject it into a service the CLI spawns, so both always agree.
|
|
159
186
|
let token = readToken(options);
|
|
187
|
+
if (!token && options.environment === "cli") {
|
|
188
|
+
// Standalone cli environment: generate a token and inject it into the
|
|
189
|
+
// service (same pattern as the desktop app), so we never depend on
|
|
190
|
+
// auth.json creation timing.
|
|
191
|
+
token = require("node:crypto").randomBytes(32).toString("hex");
|
|
192
|
+
}
|
|
160
193
|
await ensureService(options, { log, accessToken: token });
|
|
161
194
|
if (!token) {
|
|
162
195
|
// The CLI just spawned the service, which may have created {data}/auth.json.
|
|
@@ -220,6 +253,7 @@ async function resolveAndCreateTasks(source, options, token, baseUrl, log) {
|
|
|
220
253
|
pageNumber,
|
|
221
254
|
visualNoteMode: options.visualNote,
|
|
222
255
|
promptPresetId: options.promptPreset,
|
|
256
|
+
summaryScope: options.brief ? "summary" : undefined,
|
|
223
257
|
});
|
|
224
258
|
created.push(task);
|
|
225
259
|
log(`任务已创建:${task.task_id}`);
|
|
@@ -242,20 +276,25 @@ async function waitForTasks(taskIds, options, token, baseUrl, log) {
|
|
|
242
276
|
}
|
|
243
277
|
let progress = null;
|
|
244
278
|
try {
|
|
245
|
-
progress = await
|
|
279
|
+
progress = await getTaskProgress(baseUrl, token, taskId);
|
|
246
280
|
} catch (error) {
|
|
247
281
|
if (error instanceof ApiError && error.status === 404) {
|
|
248
282
|
throw new Error(`任务不存在:${taskId}`);
|
|
249
283
|
}
|
|
250
|
-
|
|
284
|
+
// Fall back to the detail endpoint if progress is unavailable.
|
|
285
|
+
progress = await getTask(baseUrl, token, taskId);
|
|
251
286
|
}
|
|
252
287
|
state.status = progress.status;
|
|
253
288
|
states.set(taskId, state);
|
|
254
289
|
if (!TERMINAL_STATUSES.has(progress.status)) {
|
|
255
|
-
const marker = `${taskId}:${progress.status}`;
|
|
290
|
+
const marker = `${taskId}:${progress.status}:${progress.progress ?? ""}:${progress.latest_stage ?? ""}`;
|
|
256
291
|
if (!seenProgress.has(marker)) {
|
|
257
292
|
seenProgress.add(marker);
|
|
258
|
-
|
|
293
|
+
const pct = Number(progress.progress);
|
|
294
|
+
const pctText = Number.isFinite(pct) ? `${String(Math.max(0, Math.min(100, Math.round(pct)))).padStart(3)}%` : " -%";
|
|
295
|
+
const stage = progress.latest_stage ? ` [${progress.latest_stage}]` : "";
|
|
296
|
+
const message = progress.latest_message ? ` ${progress.latest_message}` : "";
|
|
297
|
+
log(`[${taskId}] ${pctText}${stage}${message}`);
|
|
259
298
|
}
|
|
260
299
|
}
|
|
261
300
|
allTerminal.push(TERMINAL_STATUSES.has(progress.status));
|
|
@@ -277,8 +316,9 @@ async function waitForTasks(taskIds, options, token, baseUrl, log) {
|
|
|
277
316
|
}
|
|
278
317
|
}
|
|
279
318
|
|
|
280
|
-
async function runTaskCommand(args, { defaultFormat }) {
|
|
319
|
+
async function runTaskCommand(args, { defaultFormat, brief = false }) {
|
|
281
320
|
const { source, options } = parseTaskCommandArgs(args, { defaultFormat });
|
|
321
|
+
options.brief = brief;
|
|
282
322
|
if (options.help) {
|
|
283
323
|
printHelp();
|
|
284
324
|
return;
|
|
@@ -288,6 +328,7 @@ async function runTaskCommand(args, { defaultFormat }) {
|
|
|
288
328
|
throw new UsageError(`本地文件不存在:${source}`);
|
|
289
329
|
}
|
|
290
330
|
const log = options.quiet ? () => {} : console.error;
|
|
331
|
+
await resolveEnvironment(options);
|
|
291
332
|
const baseUrl = baseUrlFor(options.host, options.port);
|
|
292
333
|
const token = await ensureAuthenticatedService(options, log);
|
|
293
334
|
const tasks = await resolveAndCreateTasks(source, options, token, baseUrl, log);
|
|
@@ -322,7 +363,7 @@ async function runTaskCommand(args, { defaultFormat }) {
|
|
|
322
363
|
throw new Error(`${failed.length} 个任务失败。`);
|
|
323
364
|
}
|
|
324
365
|
const written = emit(
|
|
325
|
-
formatTasks(completed, options.format, { includeTranscript: options.withTranscript }),
|
|
366
|
+
formatTasks(completed, options.format, { includeTranscript: options.withTranscript, brief }),
|
|
326
367
|
options,
|
|
327
368
|
);
|
|
328
369
|
if (written) {
|
|
@@ -340,6 +381,7 @@ async function statusCommand(args) {
|
|
|
340
381
|
throw new UsageError("缺少 task-id。用法:bilisum status <task-id> [--json]");
|
|
341
382
|
}
|
|
342
383
|
const log = console.error;
|
|
384
|
+
await resolveEnvironment(options);
|
|
343
385
|
const baseUrl = baseUrlFor(options.host, options.port);
|
|
344
386
|
const token = await ensureAuthenticatedService(options, log);
|
|
345
387
|
const taskId = positional[0];
|
|
@@ -379,6 +421,7 @@ async function tasksCommand(args) {
|
|
|
379
421
|
return;
|
|
380
422
|
}
|
|
381
423
|
const log = console.error;
|
|
424
|
+
await resolveEnvironment(options);
|
|
382
425
|
const baseUrl = baseUrlFor(options.host, options.port);
|
|
383
426
|
const token = await ensureAuthenticatedService(options, log);
|
|
384
427
|
const tasks = await listTasks(baseUrl, token);
|
|
@@ -400,10 +443,12 @@ async function tasksCommand(args) {
|
|
|
400
443
|
}
|
|
401
444
|
}
|
|
402
445
|
|
|
403
|
-
function startService(args) {
|
|
446
|
+
async function startService(args) {
|
|
404
447
|
const { options } = parseConnectionOptions(args);
|
|
448
|
+
await resolveEnvironment(options);
|
|
405
449
|
const url = `http://${options.host}:${options.port}`;
|
|
406
450
|
console.log(`Starting BiliSum at ${url}`);
|
|
451
|
+
console.log(`Environment: ${options.environment}`);
|
|
407
452
|
console.log(`Data root: ${options.data}`);
|
|
408
453
|
console.log("");
|
|
409
454
|
const { child } = spawnService(options, { background: false });
|
|
@@ -419,16 +464,96 @@ function startService(args) {
|
|
|
419
464
|
});
|
|
420
465
|
}
|
|
421
466
|
|
|
422
|
-
function doctor(args) {
|
|
467
|
+
async function doctor(args) {
|
|
423
468
|
const { options } = parseConnectionOptions(args);
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
:
|
|
469
|
+
await resolveEnvironment(options);
|
|
470
|
+
const python = options.environment === "cli"
|
|
471
|
+
? { command: cliVenvPython(), args: [] }
|
|
472
|
+
: options.python
|
|
473
|
+
? { command: options.python, args: [] }
|
|
474
|
+
: findPython(options.data);
|
|
427
475
|
console.log(`BiliSum package: ${readVersion()}`);
|
|
428
|
-
console.log(`
|
|
429
|
-
console.log(`Python: ${python ? [python.command, ...python.args].join(" ") : "未找到(请安装桌面版 BiliSum 或 Python 3.12)"}`);
|
|
476
|
+
console.log(`Environment: ${options.environment}`);
|
|
430
477
|
console.log(`Service: http://${options.host}:${options.port}`);
|
|
431
|
-
console.log(`
|
|
478
|
+
console.log(`Data root: ${options.data}`);
|
|
479
|
+
console.log(`Python: ${python && python.command ? [python.command, ...python.args].join(" ") : "未找到(请安装桌面版 BiliSum、Python 3.12,或运行 bilisum env setup)"}`);
|
|
480
|
+
console.log(`Token: ${readToken(options) ? "已找到" : "未找到(desktop 环境请先启动桌面端;cli 环境运行后自动生成)"}`);
|
|
481
|
+
if (options.environment === "cli") {
|
|
482
|
+
console.log(`CLI env status: ${require("node:fs").existsSync(cliVenvPython()) ? "就绪" : "未初始化(运行 bilisum env setup)"}`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
async function settingsCommand(args) {
|
|
487
|
+
const { options } = parseConnectionOptions(args);
|
|
488
|
+
if (options.help) {
|
|
489
|
+
printHelp();
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
const log = console.error;
|
|
493
|
+
await resolveEnvironment(options);
|
|
494
|
+
await ensureService(options, { log });
|
|
495
|
+
const url = `http://${options.host}:${options.port}/settings`;
|
|
496
|
+
log(`打开设置页:${url}`);
|
|
497
|
+
openUrl(url);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
async function envCommand(args) {
|
|
501
|
+
const sub = (args[0] || "").toLowerCase();
|
|
502
|
+
const config = readConfig();
|
|
503
|
+
|
|
504
|
+
if (sub === "use") {
|
|
505
|
+
const name = String(args[1] || "").toLowerCase();
|
|
506
|
+
if (!ENV_NAMES.includes(name) || name === "auto") {
|
|
507
|
+
throw new UsageError(`用法:bilisum env use ${ENV_NAMES.filter((item) => item !== "auto").join("|")}`);
|
|
508
|
+
}
|
|
509
|
+
writeConfig({ ...config, env: name });
|
|
510
|
+
console.log(`已切换到 ${name} 环境。`);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (sub === "setup") {
|
|
515
|
+
console.log(`初始化 CLI 独立环境(${cliHomeDir()})...`);
|
|
516
|
+
const python = ensureCliVenv({ python: process.env.BILISUM_PYTHON || "", environment: "cli" }, console.error);
|
|
517
|
+
console.log(`CLI 独立环境就绪:${python}`);
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (sub === "help" || sub === "--help" || sub === "-h") {
|
|
522
|
+
printEnvHelp();
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// default: status
|
|
527
|
+
const current = await resolveEnvironment({ environment: config.env || "auto" });
|
|
528
|
+
const desktopUp = await isDesktopReachable();
|
|
529
|
+
console.log(`当前环境: ${current.env}`);
|
|
530
|
+
console.log(`服务地址: http://${current.host}:${current.port}`);
|
|
531
|
+
console.log(`数据根: ${current.dataRoot}`);
|
|
532
|
+
console.log("");
|
|
533
|
+
console.log("可用环境:");
|
|
534
|
+
console.log(` desktop ${desktopUp ? "● 桌面端服务在运行" : "○ 未检测到桌面端服务(127.0.0.1:3838)"}`);
|
|
535
|
+
const venvReady = require("node:fs").existsSync(cliVenvPython());
|
|
536
|
+
console.log(` cli ${venvReady ? "● 独立环境已就绪" : "○ 未初始化(bilisum env setup)"} CLI_HOME=${cliHomeDir()}`);
|
|
537
|
+
console.log(` custom ${config.custom ? "● 已配置" : "○ 未配置(--host/--port/--token)"}`);
|
|
538
|
+
console.log(` auto desktop 优先,否则 cli`);
|
|
539
|
+
console.log("");
|
|
540
|
+
console.log("切换:bilisum env use desktop|cli|custom");
|
|
541
|
+
if (config.env && config.env !== "auto") {
|
|
542
|
+
console.log(`已持久化选择:${config.env}`);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function printEnvHelp() {
|
|
547
|
+
console.log("Usage:");
|
|
548
|
+
console.log(" bilisum env Show environment status");
|
|
549
|
+
console.log(" bilisum env use <name> Switch environment (desktop|cli|custom)");
|
|
550
|
+
console.log(" bilisum env setup Initialize the standalone cli environment");
|
|
551
|
+
console.log("");
|
|
552
|
+
console.log("Environments:");
|
|
553
|
+
console.log(" desktop Connect to the installed desktop app service (127.0.0.1:3838)");
|
|
554
|
+
console.log(" cli Standalone: own data root, own venv, own settings page (127.0.0.1:3839)");
|
|
555
|
+
console.log(" custom Any host/port/token (Docker, remote)");
|
|
556
|
+
console.log(" auto Desktop if reachable, otherwise cli (default)");
|
|
432
557
|
}
|
|
433
558
|
|
|
434
559
|
function openUrl(url) {
|
|
@@ -463,6 +588,10 @@ async function main() {
|
|
|
463
588
|
await runTaskCommand(args, { defaultFormat: "markdown" });
|
|
464
589
|
return;
|
|
465
590
|
}
|
|
591
|
+
if (command === "brief") {
|
|
592
|
+
await runTaskCommand(args, { defaultFormat: "markdown", brief: true });
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
466
595
|
if (command === "transcribe" || command === "trans") {
|
|
467
596
|
await runTaskCommand(args, { defaultFormat: "transcript" });
|
|
468
597
|
return;
|
|
@@ -475,13 +604,22 @@ async function main() {
|
|
|
475
604
|
await tasksCommand(args);
|
|
476
605
|
return;
|
|
477
606
|
}
|
|
607
|
+
if (command === "env") {
|
|
608
|
+
await envCommand(args);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (command === "settings" || command === "--setting" || command === "config") {
|
|
612
|
+
await settingsCommand(args);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
478
615
|
if (command === "stop") {
|
|
479
616
|
const { options } = parseConnectionOptions(args);
|
|
617
|
+
await resolveEnvironment(options);
|
|
480
618
|
await stopService(options);
|
|
481
619
|
return;
|
|
482
620
|
}
|
|
483
621
|
if (command === "doctor") {
|
|
484
|
-
doctor(args);
|
|
622
|
+
await doctor(args);
|
|
485
623
|
return;
|
|
486
624
|
}
|
|
487
625
|
if (command === "help" || command === "-h" || command === "--help") {
|
package/lib/api.js
CHANGED
|
@@ -122,7 +122,7 @@ function uploadLocalMedia(baseUrl, token, filePath, { timeoutMs = 30 * 60_000 }
|
|
|
122
122
|
});
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
function createVideoTask(baseUrl, token, videoId, { pageNumber, visualNoteMode, promptPresetId, timeoutMs = 30_000 } = {}) {
|
|
125
|
+
function createVideoTask(baseUrl, token, videoId, { pageNumber, visualNoteMode, promptPresetId, summaryScope, timeoutMs = 30_000 } = {}) {
|
|
126
126
|
const payload = {};
|
|
127
127
|
if (pageNumber !== undefined && pageNumber !== null) {
|
|
128
128
|
payload.page_number = pageNumber;
|
|
@@ -133,6 +133,9 @@ function createVideoTask(baseUrl, token, videoId, { pageNumber, visualNoteMode,
|
|
|
133
133
|
if (promptPresetId) {
|
|
134
134
|
payload.prompt_preset_id = promptPresetId;
|
|
135
135
|
}
|
|
136
|
+
if (summaryScope) {
|
|
137
|
+
payload.summary_scope = summaryScope;
|
|
138
|
+
}
|
|
136
139
|
return apiRequest(baseUrl, `/api/v1/videos/${encodeURIComponent(videoId)}/tasks`, {
|
|
137
140
|
method: "POST",
|
|
138
141
|
token,
|
|
@@ -141,6 +144,13 @@ function createVideoTask(baseUrl, token, videoId, { pageNumber, visualNoteMode,
|
|
|
141
144
|
});
|
|
142
145
|
}
|
|
143
146
|
|
|
147
|
+
function getTaskProgress(baseUrl, token, taskId, { timeoutMs = 30_000 } = {}) {
|
|
148
|
+
return apiRequest(baseUrl, `/api/v1/tasks/${encodeURIComponent(taskId)}/progress`, {
|
|
149
|
+
token,
|
|
150
|
+
timeoutMs,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
144
154
|
function getTask(baseUrl, token, taskId, { timeoutMs = 30_000 } = {}) {
|
|
145
155
|
return apiRequest(baseUrl, `/api/v1/tasks/${encodeURIComponent(taskId)}`, {
|
|
146
156
|
token,
|
|
@@ -169,6 +179,7 @@ module.exports = {
|
|
|
169
179
|
uploadLocalMedia,
|
|
170
180
|
createVideoTask,
|
|
171
181
|
getTask,
|
|
182
|
+
getTaskProgress,
|
|
172
183
|
listTasks,
|
|
173
184
|
deleteTask,
|
|
174
185
|
};
|
package/lib/args.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { locateBilisumDataRoot } = require("./runtime");
|
|
8
|
+
const { ENV_NAMES } = require("./env");
|
|
8
9
|
|
|
9
10
|
class UsageError extends Error {
|
|
10
11
|
constructor(message) {
|
|
@@ -44,6 +45,9 @@ function parseTaskCommandArgs(args, { defaultFormat = "markdown" } = {}) {
|
|
|
44
45
|
data: process.env.BILISUM_DATA || locateBilisumDataRoot(),
|
|
45
46
|
token: process.env.BILISUM_TOKEN || "",
|
|
46
47
|
env: [],
|
|
48
|
+
environment: "",
|
|
49
|
+
_hostSet: false,
|
|
50
|
+
_portSet: false,
|
|
47
51
|
format: defaultFormat,
|
|
48
52
|
output: "",
|
|
49
53
|
wait: true,
|
|
@@ -53,6 +57,7 @@ function parseTaskCommandArgs(args, { defaultFormat = "markdown" } = {}) {
|
|
|
53
57
|
visualNote: "",
|
|
54
58
|
promptPreset: "",
|
|
55
59
|
withTranscript: false,
|
|
60
|
+
idleTimeout: Number.parseInt(process.env.BILISUM_CLI_IDLE_TIMEOUT || "600", 10) || 600,
|
|
56
61
|
timeoutSeconds: 3600,
|
|
57
62
|
startupTimeoutSeconds: 300,
|
|
58
63
|
};
|
|
@@ -62,10 +67,18 @@ function parseTaskCommandArgs(args, { defaultFormat = "markdown" } = {}) {
|
|
|
62
67
|
const arg = args[index];
|
|
63
68
|
if (arg === "--host") {
|
|
64
69
|
options.host = readValue(args, ++index, arg);
|
|
70
|
+
options._hostSet = true;
|
|
65
71
|
} else if (arg === "--port" || arg === "-p") {
|
|
66
72
|
options.port = readValue(args, ++index, arg);
|
|
73
|
+
options._portSet = true;
|
|
67
74
|
} else if (arg === "--python") {
|
|
68
75
|
options.python = readValue(args, ++index, arg);
|
|
76
|
+
} else if (arg === "--environment" || arg === "--env-mode") {
|
|
77
|
+
const name = readValue(args, ++index, arg);
|
|
78
|
+
if (!ENV_NAMES.includes(name)) {
|
|
79
|
+
throw new UsageError(`--environment 仅支持 ${ENV_NAMES.join(" / ")},收到:${name}`);
|
|
80
|
+
}
|
|
81
|
+
options.environment = name;
|
|
69
82
|
} else if (arg === "--data") {
|
|
70
83
|
options.data = readValue(args, ++index, arg);
|
|
71
84
|
} else if (arg === "--token") {
|
|
@@ -98,6 +111,8 @@ function parseTaskCommandArgs(args, { defaultFormat = "markdown" } = {}) {
|
|
|
98
111
|
options.visualNote = mode;
|
|
99
112
|
} else if (arg === "--prompt-preset") {
|
|
100
113
|
options.promptPreset = readValue(args, ++index, arg);
|
|
114
|
+
} else if (arg === "--idle-timeout") {
|
|
115
|
+
options.idleTimeout = parsePositiveInt(readValue(args, ++index, arg), "--idle-timeout");
|
|
101
116
|
} else if (arg === "--timeout") {
|
|
102
117
|
options.timeoutSeconds = parsePositiveInt(readValue(args, ++index, arg), "--timeout");
|
|
103
118
|
} else if (arg === "--startup-timeout") {
|
package/lib/env.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Environment resolution for the BiliSum CLI.
|
|
5
|
+
*
|
|
6
|
+
* Environments:
|
|
7
|
+
* - desktop: talk to an installed desktop app service (127.0.0.1:3838, desktop
|
|
8
|
+
* data root + desktop token). This is the "control the existing program" mode.
|
|
9
|
+
* - cli: standalone mode — the CLI provides its own service (own data root
|
|
10
|
+
* under CLI_HOME, own managed venv, own settings page). Works without any
|
|
11
|
+
* installed BiliSum.
|
|
12
|
+
* - custom: any host/port/token (Docker, remote, ...).
|
|
13
|
+
* - auto (default): probe 127.0.0.1:3838 — reachable => desktop, else cli.
|
|
14
|
+
*
|
|
15
|
+
* Selection precedence:
|
|
16
|
+
* 1. --environment desktop|cli|custom (one-shot override)
|
|
17
|
+
* 2. explicit --host/--port (treated as a custom target)
|
|
18
|
+
* 3. config.env persisted by `bilisum env use <name>`
|
|
19
|
+
* 4. auto
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const { existsSync, mkdirSync, readFileSync, writeFileSync } = require("node:fs");
|
|
23
|
+
const { join } = require("node:path");
|
|
24
|
+
|
|
25
|
+
const { health } = require("./api");
|
|
26
|
+
const { locateBilisumDataRoot, cliHomeDir } = require("./runtime");
|
|
27
|
+
|
|
28
|
+
const ENV_NAMES = ["auto", "desktop", "cli", "custom"];
|
|
29
|
+
const DESKTOP_HOST = "127.0.0.1";
|
|
30
|
+
const DESKTOP_PORT = "3838";
|
|
31
|
+
const CLI_PORT = "3839";
|
|
32
|
+
|
|
33
|
+
function configPath() {
|
|
34
|
+
return join(cliHomeDir(), "config.json");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function readConfig() {
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(readFileSync(configPath(), "utf8"));
|
|
40
|
+
} catch {
|
|
41
|
+
return { env: "auto" };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function writeConfig(config) {
|
|
46
|
+
mkdirSync(cliHomeDir(), { recursive: true });
|
|
47
|
+
writeFileSync(configPath(), JSON.stringify(config, null, 2), "utf8");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function normalizeEnvName(name) {
|
|
51
|
+
const normalized = String(name || "").trim().toLowerCase();
|
|
52
|
+
return ENV_NAMES.includes(normalized) ? normalized : null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function currentEnvName(config) {
|
|
56
|
+
return normalizeEnvName((config && config.env) || "auto") || "auto";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function isDesktopReachable(port = DESKTOP_PORT) {
|
|
60
|
+
try {
|
|
61
|
+
const payload = await health(`http://${DESKTOP_HOST}:${port}`);
|
|
62
|
+
return Boolean(payload && String(payload.service || "").trim() === "BiliSum");
|
|
63
|
+
} catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Resolve which environment a command should talk to and return concrete
|
|
70
|
+
* { env, host, port, dataRoot, token }. Mutates `options.host/port/data/token`
|
|
71
|
+
* to the resolved values (explicit flags win).
|
|
72
|
+
*/
|
|
73
|
+
async function resolveEnvironment(options = {}) {
|
|
74
|
+
const config = readConfig();
|
|
75
|
+
const explicitHost = Boolean(options._hostSet);
|
|
76
|
+
const explicitPort = Boolean(options._portSet);
|
|
77
|
+
const flag = normalizeEnvName(options.environment);
|
|
78
|
+
|
|
79
|
+
let env;
|
|
80
|
+
if (explicitHost || explicitPort) {
|
|
81
|
+
// Explicit connection target always wins (custom).
|
|
82
|
+
env = "custom";
|
|
83
|
+
} else if (flag && flag !== "auto") {
|
|
84
|
+
env = flag;
|
|
85
|
+
} else {
|
|
86
|
+
env = currentEnvName(config);
|
|
87
|
+
if (env === "auto") {
|
|
88
|
+
const probe = options._desktopProbe || isDesktopReachable;
|
|
89
|
+
env = (await probe()) ? "desktop" : "cli";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let host;
|
|
94
|
+
let port;
|
|
95
|
+
let dataRoot;
|
|
96
|
+
let token;
|
|
97
|
+
|
|
98
|
+
if (env === "desktop") {
|
|
99
|
+
host = explicitHost ? options.host : DESKTOP_HOST;
|
|
100
|
+
port = explicitPort ? options.port : DESKTOP_PORT;
|
|
101
|
+
dataRoot = options.data || locateBilisumDataRoot();
|
|
102
|
+
token = options.token || "";
|
|
103
|
+
} else if (env === "cli") {
|
|
104
|
+
const cliPort = String((config.cli && config.cli.port) || CLI_PORT);
|
|
105
|
+
host = DESKTOP_HOST;
|
|
106
|
+
port = explicitPort ? options.port : cliPort;
|
|
107
|
+
dataRoot = cliHomeDir();
|
|
108
|
+
token = options.token || "";
|
|
109
|
+
} else {
|
|
110
|
+
const custom = (config.custom && typeof config.custom === "object") ? config.custom : {};
|
|
111
|
+
host = explicitHost ? options.host : String(custom.host || DESKTOP_HOST);
|
|
112
|
+
port = explicitPort ? options.port : String(custom.port || DESKTOP_PORT);
|
|
113
|
+
dataRoot = cliHomeDir();
|
|
114
|
+
token = options.token || String(custom.token || "");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
options.host = host;
|
|
118
|
+
options.port = port;
|
|
119
|
+
options.data = dataRoot;
|
|
120
|
+
options.token = token;
|
|
121
|
+
options.environment = env;
|
|
122
|
+
return { env, host, port, dataRoot, token };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = {
|
|
126
|
+
ENV_NAMES,
|
|
127
|
+
DESKTOP_HOST,
|
|
128
|
+
DESKTOP_PORT,
|
|
129
|
+
CLI_PORT,
|
|
130
|
+
cliHomeDir,
|
|
131
|
+
configPath,
|
|
132
|
+
readConfig,
|
|
133
|
+
writeConfig,
|
|
134
|
+
normalizeEnvName,
|
|
135
|
+
currentEnvName,
|
|
136
|
+
isDesktopReachable,
|
|
137
|
+
resolveEnvironment,
|
|
138
|
+
};
|