bilisum 1.20.0-alpha.3 → 1.20.0-beta.1
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 +25 -5
- package/bin/bilisum.js +30 -9
- package/lib/args.js +3 -2
- package/lib/env.js +21 -12
- package/lib/runtime.js +42 -3
- package/lib/server.js +18 -4
- package/lib/skill.js +223 -0
- package/package.json +2 -1
- package/runtime/VERSION +1 -1
- package/runtime/apps/service/pyproject.toml +1 -1
- package/runtime/apps/web/static/assets/{index-S9QooflZ.css → index-D2xs2cji.css} +1 -1
- package/runtime/apps/web/static/assets/{index-DHuJPoLi.js → index-Lt_d2NnY.js} +1 -1
- package/runtime/apps/web/static/index.html +2 -2
- package/runtime/packages/core/pyproject.toml +1 -1
- package/runtime/packages/infra/pyproject.toml +1 -1
- package/skill/bilisum-video-understanding/SKILL.md +81 -0
- package/skill/bilisum-video-understanding/agents/openai.yaml +4 -0
package/README.md
CHANGED
|
@@ -43,6 +43,8 @@ bilisum summarize --help
|
|
|
43
43
|
```bash
|
|
44
44
|
bilisum env # 查看环境状态
|
|
45
45
|
bilisum env use cli # 切换到独立环境
|
|
46
|
+
bilisum desktop # 快捷切换到桌面端环境
|
|
47
|
+
bilisum auto # 恢复自动选择
|
|
46
48
|
bilisum env setup # 初始化独立环境(内置运行时,需 Python 3.12)
|
|
47
49
|
bilisum --setting # 打开当前环境的网页设置(cli 环境无需桌面端)
|
|
48
50
|
bilisum summarize <url> --environment cli # 单次使用某环境
|
|
@@ -60,14 +62,14 @@ bilisum summarize <url> --environment cli # 单次使用某环境
|
|
|
60
62
|
|
|
61
63
|
## 它如何"控制现有程序"
|
|
62
64
|
|
|
63
|
-
-
|
|
65
|
+
- `desktop` 环境连接桌面端服务 `http://127.0.0.1:3838`,任务、结果、知识库都与桌面端**同一个数据库**;默认 `auto` 会优先探测 desktop,不可达时使用 cli 独立环境。
|
|
64
66
|
- 访问令牌自动获取:`--token` → `VIDEO_SUM_ACCESS_TOKEN` → 桌面端自己的 token 文件(`%APPDATA%\BiliSum\access-token.json` 等)→ 服务侧 `{数据根}\data\auth.json`。桌面端正在运行时通常无需任何配置。
|
|
65
|
-
- 数据根默认与桌面端一致(Windows `%LOCALAPPDATA%\bilisum`,macOS `~/Library/Application Support/bilisum
|
|
67
|
+
- desktop 数据根默认与桌面端一致(Windows `%LOCALAPPDATA%\bilisum`,macOS `~/Library/Application Support/bilisum`);cli 使用独立的 `CLI_HOME`。
|
|
66
68
|
- 配置(LLM / ASR Key 等)跟随服务端设置,桌面端里配好了,CLI 直接用。
|
|
67
69
|
|
|
68
70
|
## 服务不在线时
|
|
69
71
|
|
|
70
|
-
`summarize / brief / transcribe / status / tasks`
|
|
72
|
+
`summarize / brief / transcribe / status / tasks` 会先探活当前环境。desktop 不可达时用桌面端数据目录拉起服务,cli 不可达时用 CLI 独立数据目录和 venv 拉起服务;custom 只连接指定地址,不会在本机拉起替代服务。CLI 拉起的服务进程记录在 `{数据根}\cli-runtime-<port>.json`:
|
|
71
73
|
|
|
72
74
|
```bash
|
|
73
75
|
bilisum start --no-open # 前台启动(可选,一般不用)
|
|
@@ -84,13 +86,30 @@ bilisum stop # 停止 CLI 拉起的服务
|
|
|
84
86
|
npm install -g bilisum # 或 npx bilisum ...
|
|
85
87
|
```
|
|
86
88
|
|
|
87
|
-
|
|
89
|
+
包内包含构建独立环境所需的 Python 源码;首次执行 `bilisum env setup` 仍要求系统已有 Python 3.12。
|
|
90
|
+
## Agent 视频理解 Skill
|
|
88
91
|
|
|
92
|
+
安装 CLI 后,可以把随包提供的 `bilisum-video-understanding` skill 安装给 Agent:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# 从 GitHub 安装到当前项目或指定 Agent
|
|
96
|
+
npx skills add https://github.com/lycohana/BiliSum --skill bilisum-video-understanding
|
|
97
|
+
|
|
98
|
+
# 使用 BiliSum CLI 自带的交互式安装器
|
|
99
|
+
bilisum skill install
|
|
100
|
+
|
|
101
|
+
# 非交互环境下显式指定目标
|
|
102
|
+
bilisum skill install --project
|
|
103
|
+
bilisum skill install --global
|
|
104
|
+
bilisum skill install --path ./agent-skills/bilisum-video-understanding
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`bilisum skill install` 在终端中会让你选择当前项目、Codex 全局目录或自定义目录;被 Agent/CI 调用且未指定目标时只打印路径,不会写入文件。已有 skill 需要更新时加 `--force`。安装后,Agent 可以根据用户请求调用 `summarize`、`brief`、`transcribe` 和 `status` 理解视频。
|
|
89
108
|
## 常用选项
|
|
90
109
|
|
|
91
110
|
| 选项 | 说明 |
|
|
92
111
|
|------|------|
|
|
93
|
-
| `--host / --port` |
|
|
112
|
+
| `--host / --port` | 自定义服务地址(显式指定时进入 custom 环境) |
|
|
94
113
|
| `--data <path>` | 桌面端数据根(默认 `%LOCALAPPDATA%\bilisum`) |
|
|
95
114
|
| `--token <token>` | 访问令牌(默认自动读桌面端 token) |
|
|
96
115
|
| `--format json\|markdown\|transcript` | 输出格式,默认 markdown |
|
|
@@ -120,4 +139,5 @@ node "<安装目录>\resources\cli\bin\bilisum.js" summarize "https://..."
|
|
|
120
139
|
```bash
|
|
121
140
|
npm test --prefix packages/npx # node:test 单元测试
|
|
122
141
|
npm run npx:test # 版本 + 单测 + npm pack 校验(根目录)
|
|
142
|
+
node packages/npx/bin/bilisum.js --setting # 可直接从仓库运行;自动使用仓库内 runtime/web 资源
|
|
123
143
|
```
|
package/bin/bilisum.js
CHANGED
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
const { UsageError, parseTaskCommandArgs } = require("../lib/args");
|
|
19
19
|
const { formatTasks, emit } = require("../lib/output");
|
|
20
20
|
const { readVersion, locateBilisumDataRoot, findPython, cliHomeDir, ensureCliVenv, cliVenvPython, findSystemPython } = require("../lib/runtime");
|
|
21
|
+
const { runSkillCommand } = require("../lib/skill");
|
|
21
22
|
const { readToken } = require("../lib/token");
|
|
22
23
|
const {
|
|
23
24
|
ensureService,
|
|
@@ -51,16 +52,18 @@ function printHelp() {
|
|
|
51
52
|
console.log(" npx bilisum status <task-id> [--json] Show task status");
|
|
52
53
|
console.log(" npx bilisum tasks [--limit N] [--json] List tasks");
|
|
53
54
|
console.log(" npx bilisum env [use <name>|setup] Environment status / switch / setup");
|
|
55
|
+
console.log(" npx bilisum desktop|cli|custom|auto Switch environment (shortcut)");
|
|
54
56
|
console.log(" npx bilisum --setting Open the web settings page");
|
|
55
57
|
console.log(" npx bilisum start [options] Start the service");
|
|
56
58
|
console.log(" npx bilisum stop Stop the CLI-started BiliSum service");
|
|
57
59
|
console.log(" npx bilisum doctor Check environment/service/Python setup");
|
|
60
|
+
console.log(" npx bilisum skill [path|install] Install the Agent video-understanding skill");
|
|
58
61
|
console.log(" npx bilisum --version Print package version");
|
|
59
62
|
console.log(" npx bilisum release Open the latest GitHub release");
|
|
60
63
|
console.log("");
|
|
61
64
|
console.log("环境(bilisum env):desktop 连桌面端服务 / cli 独立环境(自带运行时)/ custom 自定义 / auto 自动");
|
|
62
65
|
console.log("");
|
|
63
|
-
console.log("
|
|
66
|
+
console.log("默认按已保存的环境连接;auto 模式优先桌面端,否则使用 CLI 独立环境。");
|
|
64
67
|
console.log("");
|
|
65
68
|
console.log("Options (all commands):");
|
|
66
69
|
console.log(" --host <host> Host, default 127.0.0.1");
|
|
@@ -132,7 +135,7 @@ function parseConnectionOptions(args, { allowTaskId = false } = {}) {
|
|
|
132
135
|
} else if (arg === "--token") {
|
|
133
136
|
options.token = readValue(++index, arg);
|
|
134
137
|
} else if (arg === "--environment" || arg === "--env-mode") {
|
|
135
|
-
const name = readValue(++index, arg);
|
|
138
|
+
const name = readValue(++index, arg).toLowerCase();
|
|
136
139
|
if (!ENV_NAMES.includes(name)) {
|
|
137
140
|
throw new UsageError(`--environment 仅支持 ${ENV_NAMES.join(" / ")},收到:${name}`);
|
|
138
141
|
}
|
|
@@ -204,7 +207,8 @@ async function ensureAuthenticatedService(options, log) {
|
|
|
204
207
|
if (!token) {
|
|
205
208
|
throw new UsageError(
|
|
206
209
|
"无法获取访问令牌:请用 --token 指定,或设置 VIDEO_SUM_ACCESS_TOKEN," +
|
|
207
|
-
"或先启动一次桌面端 BiliSum
|
|
210
|
+
"或先启动一次桌面端 BiliSum 生成令牌;没有桌面端时可使用 --environment cli。",
|
|
211
|
+
{ showHelp: false },
|
|
208
212
|
);
|
|
209
213
|
}
|
|
210
214
|
return token;
|
|
@@ -514,11 +518,12 @@ async function envCommand(args) {
|
|
|
514
518
|
|
|
515
519
|
if (sub === "use") {
|
|
516
520
|
const name = String(args[1] || "").toLowerCase();
|
|
517
|
-
if (!ENV_NAMES.includes(name) ||
|
|
518
|
-
throw new UsageError(`用法:bilisum env use ${ENV_NAMES.
|
|
521
|
+
if (!ENV_NAMES.includes(name) || args.length !== 2) {
|
|
522
|
+
throw new UsageError(`用法:bilisum env use ${ENV_NAMES.join("|")}`);
|
|
519
523
|
}
|
|
520
524
|
writeConfig({ ...config, env: name });
|
|
521
|
-
|
|
525
|
+
const suffix = name === "auto" ? "(桌面端可用时优先 desktop,否则 cli)" : "";
|
|
526
|
+
console.log(`已切换到 ${name} 环境${suffix}。`);
|
|
522
527
|
return;
|
|
523
528
|
}
|
|
524
529
|
|
|
@@ -534,6 +539,10 @@ async function envCommand(args) {
|
|
|
534
539
|
return;
|
|
535
540
|
}
|
|
536
541
|
|
|
542
|
+
if (sub) {
|
|
543
|
+
throw new UsageError(`未知 env 子命令:${sub}`);
|
|
544
|
+
}
|
|
545
|
+
|
|
537
546
|
// default: status
|
|
538
547
|
const current = await resolveEnvironment({ environment: config.env || "auto" });
|
|
539
548
|
const desktopUp = await isDesktopReachable();
|
|
@@ -548,7 +557,7 @@ async function envCommand(args) {
|
|
|
548
557
|
console.log(` custom ${config.custom ? "● 已配置" : "○ 未配置(--host/--port/--token)"}`);
|
|
549
558
|
console.log(` auto desktop 优先,否则 cli`);
|
|
550
559
|
console.log("");
|
|
551
|
-
console.log("切换:bilisum env use desktop|cli|custom");
|
|
560
|
+
console.log("切换:bilisum env use desktop|cli|custom|auto(也可直接 bilisum desktop|cli|custom|auto)");
|
|
552
561
|
if (config.env && config.env !== "auto") {
|
|
553
562
|
console.log(`已持久化选择:${config.env}`);
|
|
554
563
|
}
|
|
@@ -557,7 +566,8 @@ async function envCommand(args) {
|
|
|
557
566
|
function printEnvHelp() {
|
|
558
567
|
console.log("Usage:");
|
|
559
568
|
console.log(" bilisum env Show environment status");
|
|
560
|
-
console.log(" bilisum env use <name> Switch environment (desktop|cli|custom)");
|
|
569
|
+
console.log(" bilisum env use <name> Switch environment (desktop|cli|custom|auto)");
|
|
570
|
+
console.log(" bilisum <name> Shortcut for `bilisum env use <name>`");
|
|
561
571
|
console.log(" bilisum env setup Initialize the standalone cli environment");
|
|
562
572
|
console.log("");
|
|
563
573
|
console.log("Environments:");
|
|
@@ -619,6 +629,13 @@ async function main() {
|
|
|
619
629
|
await envCommand(args);
|
|
620
630
|
return;
|
|
621
631
|
}
|
|
632
|
+
if (ENV_NAMES.includes(command)) {
|
|
633
|
+
if (args.length > 0) {
|
|
634
|
+
throw new UsageError(`用法:bilisum ${command}`);
|
|
635
|
+
}
|
|
636
|
+
await envCommand(["use", command]);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
622
639
|
if (command === "settings" || command === "--setting" || command === "config") {
|
|
623
640
|
await settingsCommand(args);
|
|
624
641
|
return;
|
|
@@ -633,6 +650,10 @@ async function main() {
|
|
|
633
650
|
await doctor(args);
|
|
634
651
|
return;
|
|
635
652
|
}
|
|
653
|
+
if (command === "skill") {
|
|
654
|
+
await runSkillCommand(args);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
636
657
|
if (command === "help" || command === "-h" || command === "--help") {
|
|
637
658
|
printHelp();
|
|
638
659
|
return;
|
|
@@ -653,7 +674,7 @@ async function main() {
|
|
|
653
674
|
} catch (error) {
|
|
654
675
|
const message = error instanceof Error ? error.message : String(error);
|
|
655
676
|
console.error(message);
|
|
656
|
-
if (error instanceof UsageError) {
|
|
677
|
+
if (error instanceof UsageError && error.showHelp) {
|
|
657
678
|
console.error("");
|
|
658
679
|
printHelp();
|
|
659
680
|
}
|
package/lib/args.js
CHANGED
|
@@ -8,9 +8,10 @@ const { locateBilisumDataRoot } = require("./runtime");
|
|
|
8
8
|
const { ENV_NAMES } = require("./env");
|
|
9
9
|
|
|
10
10
|
class UsageError extends Error {
|
|
11
|
-
constructor(message) {
|
|
11
|
+
constructor(message, { showHelp = true } = {}) {
|
|
12
12
|
super(message);
|
|
13
13
|
this.name = "UsageError";
|
|
14
|
+
this.showHelp = showHelp;
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -74,7 +75,7 @@ function parseTaskCommandArgs(args, { defaultFormat = "markdown" } = {}) {
|
|
|
74
75
|
} else if (arg === "--python") {
|
|
75
76
|
options.python = readValue(args, ++index, arg);
|
|
76
77
|
} else if (arg === "--environment" || arg === "--env-mode") {
|
|
77
|
-
const name = readValue(args, ++index, arg);
|
|
78
|
+
const name = readValue(args, ++index, arg).toLowerCase();
|
|
78
79
|
if (!ENV_NAMES.includes(name)) {
|
|
79
80
|
throw new UsageError(`--environment 仅支持 ${ENV_NAMES.join(" / ")},收到:${name}`);
|
|
80
81
|
}
|
package/lib/env.js
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* - auto (default): probe 127.0.0.1:3838 — reachable => desktop, else cli.
|
|
14
14
|
*
|
|
15
15
|
* Selection precedence:
|
|
16
|
-
* 1. --
|
|
17
|
-
* 2.
|
|
16
|
+
* 1. explicit --host/--port (treated as a custom target)
|
|
17
|
+
* 2. --environment desktop|cli|custom|auto (one-shot override)
|
|
18
18
|
* 3. config.env persisted by `bilisum env use <name>`
|
|
19
19
|
* 4. auto
|
|
20
20
|
*/
|
|
@@ -74,20 +74,29 @@ async function resolveEnvironment(options = {}) {
|
|
|
74
74
|
const config = readConfig();
|
|
75
75
|
const explicitHost = Boolean(options._hostSet);
|
|
76
76
|
const explicitPort = Boolean(options._portSet);
|
|
77
|
-
const
|
|
77
|
+
const rawFlag = String(options.environment || "").trim();
|
|
78
|
+
const flag = normalizeEnvName(rawFlag);
|
|
78
79
|
|
|
79
|
-
|
|
80
|
+
if (rawFlag && !flag) {
|
|
81
|
+
throw new Error(`未知环境:${rawFlag}。可选值:${ENV_NAMES.join(" / ")}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let requestedEnv;
|
|
80
85
|
if (explicitHost || explicitPort) {
|
|
81
86
|
// Explicit connection target always wins (custom).
|
|
82
|
-
|
|
83
|
-
} else if (flag
|
|
84
|
-
|
|
87
|
+
requestedEnv = "custom";
|
|
88
|
+
} else if (flag) {
|
|
89
|
+
// An explicit `auto` is a real one-shot override. It must not fall back to
|
|
90
|
+
// a persisted desktop/cli/custom selection.
|
|
91
|
+
requestedEnv = flag;
|
|
85
92
|
} else {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
requestedEnv = currentEnvName(config);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let env = requestedEnv;
|
|
97
|
+
if (env === "auto") {
|
|
98
|
+
const probe = options._desktopProbe || isDesktopReachable;
|
|
99
|
+
env = (await probe()) ? "desktop" : "cli";
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
let host;
|
package/lib/runtime.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const { existsSync, mkdirSync, readFileSync, writeFileSync } = require("node:fs");
|
|
15
|
-
const { join } = require("node:path");
|
|
15
|
+
const { join, resolve } = require("node:path");
|
|
16
16
|
const { spawnSync } = require("node:child_process");
|
|
17
17
|
|
|
18
18
|
const PACKAGE_ROOT = join(__dirname, "..");
|
|
@@ -139,9 +139,47 @@ function findPython(dataRoot) {
|
|
|
139
139
|
return findSystemPython();
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
function isRuntimeSourceRoot(path) {
|
|
143
|
+
return existsSync(join(path, "apps", "service", "pyproject.toml"));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Python/runtime sources for both supported layouts:
|
|
148
|
+
* - published package: packages/npx/runtime (created by prepack)
|
|
149
|
+
* - repository checkout: repository root (runtime is removed by postpack)
|
|
150
|
+
*/
|
|
143
151
|
function runtimeSourceDir() {
|
|
144
|
-
|
|
152
|
+
const packagedRoot = join(PACKAGE_ROOT, "runtime");
|
|
153
|
+
if (isRuntimeSourceRoot(packagedRoot)) {
|
|
154
|
+
return packagedRoot;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const repositoryRoot = resolve(PACKAGE_ROOT, "..", "..");
|
|
158
|
+
if (isRuntimeSourceRoot(repositoryRoot)) {
|
|
159
|
+
return repositoryRoot;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Keep the packaged path in error messages for an incomplete npm install.
|
|
163
|
+
return packagedRoot;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Resolve the installable Agent Skill from the published package or the
|
|
168
|
+
* repository checkout used during development.
|
|
169
|
+
*/
|
|
170
|
+
function skillSourceDir() {
|
|
171
|
+
const packagedSkill = join(PACKAGE_ROOT, "skill", "bilisum-video-understanding");
|
|
172
|
+
if (existsSync(join(packagedSkill, "SKILL.md"))) {
|
|
173
|
+
return packagedSkill;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const repositoryRoot = resolve(PACKAGE_ROOT, "..", "..");
|
|
177
|
+
const repositorySkill = join(repositoryRoot, ".agents", "skills", "bilisum-video-understanding");
|
|
178
|
+
if (existsSync(join(repositorySkill, "SKILL.md"))) {
|
|
179
|
+
return repositorySkill;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return packagedSkill;
|
|
145
183
|
}
|
|
146
184
|
|
|
147
185
|
/** CLI venv lives under CLI_HOME/venv. */
|
|
@@ -225,6 +263,7 @@ module.exports = {
|
|
|
225
263
|
findSystemPython,
|
|
226
264
|
findPython,
|
|
227
265
|
runtimeSourceDir,
|
|
266
|
+
skillSourceDir,
|
|
228
267
|
cliVenvDir,
|
|
229
268
|
cliVenvPython,
|
|
230
269
|
ensureRuntime,
|
package/lib/server.js
CHANGED
|
@@ -275,10 +275,17 @@ function buildStartupFailureHint(logPath) {
|
|
|
275
275
|
return `详细日志:${logPath}`;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
+
function buildAutoStartMessage(options) {
|
|
279
|
+
const url = baseUrlFor(options.host, options.port);
|
|
280
|
+
const dataRootLabel = options.environment === "cli" ? "CLI 独立数据目录" : "桌面端数据目录";
|
|
281
|
+
return `BiliSum 服务未在 ${url} 运行,正在用 ${dataRootLabel} 后台启动...`;
|
|
282
|
+
}
|
|
283
|
+
|
|
278
284
|
/**
|
|
279
|
-
* Make sure a BiliSum service is reachable. If none is running, start
|
|
280
|
-
*
|
|
281
|
-
*
|
|
285
|
+
* Make sure a BiliSum service is reachable. If none is running, start a local
|
|
286
|
+
* service for the desktop/cli environments. A custom target is connection-only
|
|
287
|
+
* and is never replaced with an unrelated local process. When `accessToken` is
|
|
288
|
+
* known it is injected into the spawned service so CLI and service always agree.
|
|
282
289
|
* Returns { started, payload }.
|
|
283
290
|
*/
|
|
284
291
|
async function ensureService(options, { startupTimeoutMs = DEFAULT_STARTUP_TIMEOUT_MS, log = console.error, accessToken = null } = {}) {
|
|
@@ -287,7 +294,13 @@ async function ensureService(options, { startupTimeoutMs = DEFAULT_STARTUP_TIMEO
|
|
|
287
294
|
return { started: false, payload: await health(url) };
|
|
288
295
|
}
|
|
289
296
|
|
|
290
|
-
|
|
297
|
+
if (options.environment === "custom") {
|
|
298
|
+
throw new Error(
|
|
299
|
+
`无法连接自定义 BiliSum 服务(${url})。请确认服务已启动,并检查 --host/--port 配置。`,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
log(buildAutoStartMessage(options));
|
|
291
304
|
const { child, logPath } = spawnService(options, { background: true, accessToken });
|
|
292
305
|
const controller = new AbortController();
|
|
293
306
|
const earlyExit = new Promise((resolve) => {
|
|
@@ -396,4 +409,5 @@ module.exports = {
|
|
|
396
409
|
cliServiceLogPath,
|
|
397
410
|
readServiceSettings,
|
|
398
411
|
assertSpawnablePort,
|
|
412
|
+
buildAutoStartMessage,
|
|
399
413
|
};
|
package/lib/skill.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { copyFileSync, existsSync, mkdirSync, statSync } = require("node:fs");
|
|
4
|
+
const { homedir } = require("node:os");
|
|
5
|
+
const { createInterface } = require("node:readline/promises");
|
|
6
|
+
const { dirname, join, resolve } = require("node:path");
|
|
7
|
+
|
|
8
|
+
const { UsageError } = require("./args");
|
|
9
|
+
const { skillSourceDir } = require("./runtime");
|
|
10
|
+
|
|
11
|
+
const SKILL_NAME = "bilisum-video-understanding";
|
|
12
|
+
const MANAGED_FILES = ["SKILL.md", join("agents", "openai.yaml")];
|
|
13
|
+
|
|
14
|
+
function projectSkillDir(cwd = process.cwd()) {
|
|
15
|
+
return resolve(cwd, ".agents", "skills", SKILL_NAME);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function globalSkillDir() {
|
|
19
|
+
const codexHome = String(process.env.CODEX_HOME || "").trim();
|
|
20
|
+
const codexRoot = codexHome || join(homedir(), ".codex");
|
|
21
|
+
return resolve(codexRoot, "skills", SKILL_NAME);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function destinationForMode(mode, cwd = process.cwd()) {
|
|
25
|
+
if (mode === "project") {
|
|
26
|
+
return projectSkillDir(cwd);
|
|
27
|
+
}
|
|
28
|
+
if (mode === "global") {
|
|
29
|
+
return globalSkillDir();
|
|
30
|
+
}
|
|
31
|
+
throw new UsageError(`未知 skill 安装范围:${mode}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseInstallArgs(args) {
|
|
35
|
+
const options = {
|
|
36
|
+
mode: "",
|
|
37
|
+
path: "",
|
|
38
|
+
force: false,
|
|
39
|
+
help: false,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
43
|
+
const arg = args[index];
|
|
44
|
+
if (arg === "--project") {
|
|
45
|
+
if (options.mode || options.path) {
|
|
46
|
+
throw new UsageError("--project、--global 与 --path 只能选择一个。");
|
|
47
|
+
}
|
|
48
|
+
options.mode = "project";
|
|
49
|
+
} else if (arg === "--global") {
|
|
50
|
+
if (options.mode || options.path) {
|
|
51
|
+
throw new UsageError("--project、--global 与 --path 只能选择一个。");
|
|
52
|
+
}
|
|
53
|
+
options.mode = "global";
|
|
54
|
+
} else if (arg === "--path") {
|
|
55
|
+
const value = args[++index];
|
|
56
|
+
if (!value || value.startsWith("--")) {
|
|
57
|
+
throw new UsageError("--path requires a value");
|
|
58
|
+
}
|
|
59
|
+
if (options.mode || options.path) {
|
|
60
|
+
throw new UsageError("--project、--global 与 --path 只能选择一个。");
|
|
61
|
+
}
|
|
62
|
+
options.path = value;
|
|
63
|
+
} else if (arg === "--force") {
|
|
64
|
+
options.force = true;
|
|
65
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
66
|
+
options.help = true;
|
|
67
|
+
} else {
|
|
68
|
+
throw new UsageError(`未知 skill 安装选项:${arg}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return options;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function printSkillHelp() {
|
|
76
|
+
console.log("Usage:");
|
|
77
|
+
console.log(" bilisum skill path");
|
|
78
|
+
console.log(" bilisum skill install [--project | --global | --path <dir>] [--force]");
|
|
79
|
+
console.log("");
|
|
80
|
+
console.log("Commands:");
|
|
81
|
+
console.log(" path Print the bundled video-understanding skill directory");
|
|
82
|
+
console.log(" install Install the skill interactively or copy to an explicit target");
|
|
83
|
+
console.log("");
|
|
84
|
+
console.log("Install behavior:");
|
|
85
|
+
console.log(" TTY Choose project, Codex global, or a custom path interactively");
|
|
86
|
+
console.log(" non-TTY Preview source and destinations without writing files");
|
|
87
|
+
console.log(" --project Install to ./.agents/skills/ in the current project");
|
|
88
|
+
console.log(" --global Install to $CODEX_HOME/skills/ or ~/.codex/skills/");
|
|
89
|
+
console.log(" --path Install to the exact directory supplied");
|
|
90
|
+
console.log(" --force Overwrite managed files in an existing skill directory");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function assertSkillSource(source) {
|
|
94
|
+
if (!existsSync(join(source, "SKILL.md"))) {
|
|
95
|
+
throw new Error(`Bundled BiliSum skill is missing: ${source}`);
|
|
96
|
+
}
|
|
97
|
+
for (const relativePath of MANAGED_FILES) {
|
|
98
|
+
if (!existsSync(join(source, relativePath))) {
|
|
99
|
+
throw new Error(`Bundled BiliSum skill file is missing: ${join(source, relativePath)}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function installSkill(target, { source = skillSourceDir(), force = false } = {}) {
|
|
105
|
+
assertSkillSource(source);
|
|
106
|
+
const destination = resolve(target);
|
|
107
|
+
if (existsSync(destination)) {
|
|
108
|
+
if (!statSync(destination).isDirectory()) {
|
|
109
|
+
throw new UsageError(`skill 安装目标不是目录:${destination}`);
|
|
110
|
+
}
|
|
111
|
+
if (!force) {
|
|
112
|
+
throw new UsageError(`skill 安装目标已存在:${destination}。如需更新请加 --force。`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
for (const relativePath of MANAGED_FILES) {
|
|
117
|
+
const targetPath = join(destination, relativePath);
|
|
118
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
119
|
+
copyFileSync(join(source, relativePath), targetPath);
|
|
120
|
+
}
|
|
121
|
+
return destination;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function printInstallPreview(source) {
|
|
125
|
+
console.log(`Bundled skill: ${source}`);
|
|
126
|
+
console.log(`Project target: ${projectSkillDir()}`);
|
|
127
|
+
console.log(`Global target: ${globalSkillDir()}`);
|
|
128
|
+
console.log("");
|
|
129
|
+
console.log("Non-interactive mode: no files were written.");
|
|
130
|
+
console.log("Use --project, --global, or --path <dir> to install explicitly.");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function chooseInteractiveTarget() {
|
|
134
|
+
const readline = createInterface({ input: process.stdin, output: process.stdout });
|
|
135
|
+
try {
|
|
136
|
+
console.log("选择 BiliSum 视频理解 skill 的安装位置:");
|
|
137
|
+
console.log(` 1) 当前项目 ${projectSkillDir()}`);
|
|
138
|
+
console.log(` 2) Codex 全局 ${globalSkillDir()}`);
|
|
139
|
+
console.log(" 3) 自定义目录");
|
|
140
|
+
console.log(" q) 取消");
|
|
141
|
+
const choice = (await readline.question("请输入选项 [1/2/3/q]: ")).trim().toLowerCase();
|
|
142
|
+
if (choice === "1") {
|
|
143
|
+
return projectSkillDir();
|
|
144
|
+
}
|
|
145
|
+
if (choice === "2") {
|
|
146
|
+
return globalSkillDir();
|
|
147
|
+
}
|
|
148
|
+
if (choice === "3") {
|
|
149
|
+
const customPath = (await readline.question("请输入 skill 目标目录: ")).trim();
|
|
150
|
+
if (!customPath) {
|
|
151
|
+
throw new UsageError("自定义 skill 目录不能为空。");
|
|
152
|
+
}
|
|
153
|
+
return resolve(customPath);
|
|
154
|
+
}
|
|
155
|
+
if (choice === "q" || choice === "quit" || choice === "cancel") {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
throw new UsageError("无效的 skill 安装选项。");
|
|
159
|
+
} finally {
|
|
160
|
+
readline.close();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function installCommand(args) {
|
|
165
|
+
const options = parseInstallArgs(args);
|
|
166
|
+
if (options.help) {
|
|
167
|
+
printSkillHelp();
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const source = resolve(skillSourceDir());
|
|
172
|
+
let destination = "";
|
|
173
|
+
if (options.mode) {
|
|
174
|
+
destination = destinationForMode(options.mode);
|
|
175
|
+
} else if (options.path) {
|
|
176
|
+
destination = resolve(options.path);
|
|
177
|
+
} else if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
178
|
+
destination = await chooseInteractiveTarget();
|
|
179
|
+
} else {
|
|
180
|
+
printInstallPreview(source);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!destination) {
|
|
185
|
+
console.log("已取消 skill 安装。");
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const installed = installSkill(destination, { source, force: options.force });
|
|
190
|
+
console.log(`BiliSum 视频理解 skill 已安装到:${installed}`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function runSkillCommand(args) {
|
|
194
|
+
const [subcommand, ...rest] = args;
|
|
195
|
+
if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
|
|
196
|
+
printSkillHelp();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (subcommand === "path") {
|
|
200
|
+
if (rest.length > 0) {
|
|
201
|
+
throw new UsageError("用法:bilisum skill path");
|
|
202
|
+
}
|
|
203
|
+
console.log(resolve(skillSourceDir()));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (subcommand === "install") {
|
|
207
|
+
await installCommand(rest);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
throw new UsageError(`未知 skill 子命令:${subcommand}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
module.exports = {
|
|
214
|
+
SKILL_NAME,
|
|
215
|
+
MANAGED_FILES,
|
|
216
|
+
projectSkillDir,
|
|
217
|
+
globalSkillDir,
|
|
218
|
+
destinationForMode,
|
|
219
|
+
parseInstallArgs,
|
|
220
|
+
installSkill,
|
|
221
|
+
printSkillHelp,
|
|
222
|
+
runSkillCommand,
|
|
223
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bilisum",
|
|
3
|
-
"version": "1.20.0-
|
|
3
|
+
"version": "1.20.0-beta.1",
|
|
4
4
|
"description": "BiliSum CLI: control the installed BiliSum app, or run standalone with its own runtime.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"bilisum": "bin/bilisum.js"
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"bin",
|
|
10
10
|
"lib",
|
|
11
11
|
"runtime",
|
|
12
|
+
"skill",
|
|
12
13
|
"README.md"
|
|
13
14
|
],
|
|
14
15
|
"scripts": {
|
package/runtime/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.20.0-
|
|
1
|
+
1.20.0-beta.1
|