dsh-vidlens 0.0.0-stage → 1.2.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/LICENSE +21 -0
- package/README.md +240 -2
- package/RELEASING.md +77 -0
- package/bridge/vidlens_bridge.py +492 -0
- package/cordis.patch.yml +16 -0
- package/docs/AUDIO-EVENTS.md +127 -0
- package/docs/DISTRIBUTION.md +169 -0
- package/docs/ISSUES.md +41 -0
- package/icon.svg +48 -0
- package/index.js +641 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +53 -4
- package/tools/fetch_sound_model.py +117 -0
- package/tools/selftest.mjs +188 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 vidlens contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,241 @@
|
|
|
1
|
-
#
|
|
1
|
+
# VidLens for DeepSeek Harness
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/cctv2333/dsh-vidlens/actions/workflows/ci.yml)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
|
|
6
|
+
把 [vidlens](https://github.com/cctv2333/vidlens) 从一个 stdio MCP 外壳变成 **DSH 原生插件**:18 个 `vidlens_*` 工具、图片直接进对话、按工具设超时、外加三个零下载的缓存工具。
|
|
7
|
+
|
|
8
|
+
工程本体(Python)没有改一行逻辑:插件通过一个常驻桥进程调用 `vl_mcp.py` 里**已经测过**的工具函数,所以 MCP 外壳和插件的行为不会漂移。
|
|
9
|
+
|
|
10
|
+
- 工程仓库(Python + MCP 服务器):<https://github.com/cctv2333/vidlens>
|
|
11
|
+
- 本仓库(DSH 插件):<https://github.com/cctv2333/dsh-vidlens>
|
|
12
|
+
- 插件要求:`vidlens >= 1.4.0`(`video_soundscape` 起)、Node ≥ 20、DSH `>=0.2.1-rc.2 <0.3.0`
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
用户: 这个视频讲了什么? <url>
|
|
16
|
+
→ vidlens_meta 0 下载:值不值得看
|
|
17
|
+
→ vidlens_plan 0 下载:切章节,挑 2–3 段
|
|
18
|
+
→ vidlens_window 只下这一窗的音频(或直接读缓存/字幕)
|
|
19
|
+
→ vidlens_sheet 只下这 20 秒,拼图带时间码,图直接附在结果里
|
|
20
|
+
→ vidlens_brief 平台字幕优先;无字幕才下音频做本地 ASR
|
|
21
|
+
→ vidlens_soundscape 台词之外的声音(吃饭/敲击/水声),带概率
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 相对 MCP 外壳的增强
|
|
25
|
+
|
|
26
|
+
| 能力 | MCP 外壳 | 本插件 |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| 工具名 | `mcp__vidlens__video_meta` | `vidlens_meta`(短、可读) |
|
|
29
|
+
| 超时 | 客户端一个全局 60s | 每个工具自带预算(`vidlens_brief` 15 分钟,`vidlens_scan` 15 分钟) |
|
|
30
|
+
| 图片 | 取决于客户端是否支持 MCP image content | 走宿主 `attachments.saveImage`,成为**持久图片附件**,同轮就能看 |
|
|
31
|
+
| 缓存意识 | 无 | `vidlens_library`(零网络列缓存)、`vidlens_seek`(零网络查原话) |
|
|
32
|
+
| 批量筛选 | 无 | `vidlens_compare`(最多 8 个链接,0 媒体下载) |
|
|
33
|
+
| 行为约束 | 只在 MCP instructions 里 | 独立的 system-prompt 小节:先 meta→plan→抽样,绝不下整片 |
|
|
34
|
+
| 取消 | 断开连接 | `exec.signal` 取消时重启桥进程,不留下卡死的下载 |
|
|
35
|
+
| 整片下载 | `video_full(confirm=true)` 存在 | **故意不提供**:需要画面就用 sheet/frames/scan |
|
|
36
|
+
|
|
37
|
+
## 安装
|
|
38
|
+
|
|
39
|
+
**前置:先有一份 vidlens。** 二选一:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pipx install "vidlens-mcp[all]" # 推荐:无 checkout,命令叫 vidlens / vidlens-mcp
|
|
43
|
+
# 或
|
|
44
|
+
git clone https://github.com/cctv2333/vidlens && cd vidlens && install.cmd
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
然后装本插件(三选一):
|
|
48
|
+
|
|
49
|
+
```powershell
|
|
50
|
+
# A. 本地目录(开发)
|
|
51
|
+
plugin_manager action=install_bundle target=D:\vidlens\vidlens-mcp-dsh
|
|
52
|
+
|
|
53
|
+
# B. GitHub 直装(本包无 build/prepare 脚本,不需要 allowBuilds 权限)
|
|
54
|
+
dsh plugin --profile desktop add github:cctv2333/dsh-vidlens#<sha>
|
|
55
|
+
|
|
56
|
+
# C. npm(发布后)
|
|
57
|
+
dsh plugin --profile desktop add dsh-vidlens
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
插件会自动找 Python 工程:`<本包>/../vidlens` → `D:\vidlens\vidlens` → `$VIDLENS_PROJECT`;
|
|
61
|
+
找不到时按 `$VIDLENS_PYTHON` / `.venv` / `python` 依次尝试。PyPI 安装的形态下,
|
|
62
|
+
把 `projectDir` 指到 `pipx` 的 venv 或直接设 `VIDLENS_PROJECT` 都行(模块名不变)。
|
|
63
|
+
|
|
64
|
+
安装后这个 bundle 会插入一行 Host 插件:
|
|
65
|
+
|
|
66
|
+
```yaml
|
|
67
|
+
- insert:
|
|
68
|
+
- id: dsh-vidlens
|
|
69
|
+
name: 'dsh-vidlens'
|
|
70
|
+
config: {}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
想改成别的工程位置(例如换机器)就在 `config` 里写:
|
|
74
|
+
|
|
75
|
+
```yaml
|
|
76
|
+
- insert:
|
|
77
|
+
- id: dsh-vidlens
|
|
78
|
+
name: 'dsh-vidlens'
|
|
79
|
+
config:
|
|
80
|
+
projectDir: D:\path\to\vidlens
|
|
81
|
+
pythonPath: D:\path\to\vidlens\.venv\Scripts\python.exe
|
|
82
|
+
warm: true
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
字段都可省略:`projectDir` 默认依次取 `<本包>/../vidlens`、`D:\vidlens\vidlens`、`$VIDLENS_PROJECT`;`pythonPath` 默认取工程 `.venv` 里的解释器。
|
|
86
|
+
|
|
87
|
+
## 工具(18 个)
|
|
88
|
+
|
|
89
|
+
**基础四件(0 下载)**
|
|
90
|
+
|
|
91
|
+
| 工具 | 用途 |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `vidlens_meta(url)` | 标题/UP主/时长/播放/平台字幕/平台标注音乐 + 注入风险扫描 |
|
|
94
|
+
| `vidlens_plan(url, window=300)` | 按切点/长静音分章节 + 建议顺序 |
|
|
95
|
+
| `vidlens_subs(url, lang=zh)` | 平台字幕(人工优先,其次自动/AI) |
|
|
96
|
+
| `vidlens_library(limit=20, query='')` | **新增**:本地缓存清单,避免重复下载 |
|
|
97
|
+
|
|
98
|
+
**读内容**
|
|
99
|
+
|
|
100
|
+
| 工具 | 下载量 | 用途 |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| `vidlens_window(url, start, end)` | 0(有缓存/字幕)或该窗音频 | 某时间窗的台词与停顿 |
|
|
103
|
+
| `vidlens_brief(url, ...)` | 平台字幕 0;无字幕才下音频 | 完整证据报告(结构/停顿/低置信词/广告段落/风险) |
|
|
104
|
+
| `vidlens_seek(query, url='')` | 0(**新增**) | 在缓存转写里按关键词查原话 → 时间码 |
|
|
105
|
+
| `vidlens_audio(url, start, end)` | 该段音频 | SenseVoice 语种/情感/事件 + 节拍与能量 |
|
|
106
|
+
| `vidlens_soundscape(url, start, end)` | 该段音频 | **新增**:AudioSet 527 类声音事件(吃饭/敲击/水声/碰撞…)+ 概率 + 时间码 |
|
|
107
|
+
|
|
108
|
+
**看画面(图片直接进对话)**
|
|
109
|
+
|
|
110
|
+
| 工具 | 下载量 | 用途 |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| `vidlens_sheet(url, start, end, cols, rows)` | 该段(20s≈0.9MB) | 带绝对时间码的拼图 |
|
|
113
|
+
| `vidlens_frames(url, at=[...])` | 该段 | 最多 6 张精确帧(读小字用) |
|
|
114
|
+
| `vidlens_scan(url, n=24)` | 最低画质整片(7min≈14-21MB,用完即删) | 全片抽样拼图(无音频视频) |
|
|
115
|
+
|
|
116
|
+
**判断 / 长视频 / 自检**
|
|
117
|
+
|
|
118
|
+
| 工具 | 用途 |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `vidlens_comments(url, limit=40)` | 评论区互动比、正负面、争议度、高赞评论 + 评论配图 |
|
|
121
|
+
| `vidlens_compare(urls=[...])` | **新增**:批量元数据对比(0 媒体下载,最多 8 个) |
|
|
122
|
+
| `vidlens_job_start(url, ..., full=False)` | 长视频后台任务,立即返回 `job_id` |
|
|
123
|
+
| `vidlens_job(job_id, wait_seconds=20)` | 轮询阶段/百分比,完成返回报告 |
|
|
124
|
+
| `vidlens_models(action=list)` | 本地模型目录与硬件建议(只读,不自动下载) |
|
|
125
|
+
| `vidlens_doctor()` | 自检 python/ffmpeg/yt-dlp/依赖/模型/磁盘/网络 |
|
|
126
|
+
|
|
127
|
+
## 架构
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
DSH Host (index.js)
|
|
131
|
+
├─ 18 × ctx.tools.register(...) 原生工具 + 每工具 timeoutMs
|
|
132
|
+
├─ ctx.systemPrompt.section('vidlens') 取样协议(meta→plan→抽样)
|
|
133
|
+
└─ Bridge (常驻) ──stdin/stdout JSONL──▶ python -u bridge/vidlens_bridge.py
|
|
134
|
+
│
|
|
135
|
+
├─ import vl_mcp (复用工具函数)
|
|
136
|
+
├─ import vidlens_web / _extra / _sanitize
|
|
137
|
+
└─ out/<id>/ (缓存,进程重启不丢)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**为什么不是直接把 MCP 服务器挂上来**:MCP 客户端只有一个全局工具超时,长视频 ASR 会被误杀;图片是否进模型取决于客户端实现;也拿不到「缓存清单 / 关键词定位」这类只在本地才有答案的能力。桥进程把「已验证的 Python 逻辑」和「宿主的原生能力」接在一起,两边都不用重写。
|
|
141
|
+
|
|
142
|
+
**协议**(每行一个 JSON)
|
|
143
|
+
|
|
144
|
+
```jsonc
|
|
145
|
+
// 启动
|
|
146
|
+
{"op":"ready","ok":true,"pid":1234,"project":"D:\\vidlens\\vidlens","python":"3.12.10","tools":["meta",...]}
|
|
147
|
+
// 请求 → 响应
|
|
148
|
+
{"id":7,"tool":"sheet","args":{"url":"...","start":"0","end":"20"}}
|
|
149
|
+
{"id":7,"ok":true,"text":"...","images":[{"path":"...jpg","mediaType":"image/jpeg","name":"sheet_0_20000.jpg"}]}
|
|
150
|
+
{"id":8,"ok":false,"error":"ValueError: ...","trace":"..."}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`sys.stdout` 在导入任何库之前就被指向 stderr,所以依赖里的杂散 `print` 不可能污染协议;桥只把帧写进保存下来的真实 stdout。
|
|
154
|
+
|
|
155
|
+
**多进程安全**:DSH 会话、MCP 服务器、插件桥是**不同进程**,进程内 `threading.Lock` 互相看不见。
|
|
156
|
+
工程侧用 `vidlens_web.CrossProcessLock`(进程内 RLock + `out/.media.lock` 文件锁,msvcrt/flock)
|
|
157
|
+
把「一个下载口」扩展到整台机器;超时可用 `VIDLENS_LOCK_TIMEOUT`(默认 900s)调整。
|
|
158
|
+
实测两个进程同时调 `video_sheet`:第二个的 acq 时间落在第一个 end 之后(串行,不互相踩)。
|
|
159
|
+
|
|
160
|
+
## 验证
|
|
161
|
+
|
|
162
|
+
**离线(不联网、不下媒体)**
|
|
163
|
+
|
|
164
|
+
```powershell
|
|
165
|
+
node D:\vidlens\vidlens-mcp-dsh\tools\selftest.mjs
|
|
166
|
+
# 96/96 checks passed(工具表 + 桥握手 + library/models/seek + 强制杀进程后自愈)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**带真实链路(约 1MB 媒体)**
|
|
170
|
+
|
|
171
|
+
```powershell
|
|
172
|
+
node D:\vidlens\vidlens-mcp-dsh\tools\selftest.mjs --live
|
|
173
|
+
# 98/98 checks passed
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**安装后在 DSH 里的实测**(本机 2026-10-25,全部走原生工具通道)
|
|
177
|
+
|
|
178
|
+
| 调用 | 结果 |
|
|
179
|
+
|---|---|
|
|
180
|
+
| `vidlens_library(limit=6)` | 9 个缓存视频,标题/时长/段数/来源/占用/时间齐全(`out/` 里的 `bili/`、`comment_pics/` 等非视频目录已自动排除) |
|
|
181
|
+
| `vidlens_doctor()` | 11/11:python 3.12.10、ffmpeg 7.1、yt-dlp 2026.10.21、4 个依赖、whisper 461MB、磁盘 332GB、B站 API 200 |
|
|
182
|
+
| `vidlens_meta(BV1PinG6fEPX)` | 真实标题/UP主/139514 播放;正确报出「字幕需要登录」 |
|
|
183
|
+
| `vidlens_plan(window=120)` | 3 章节,0 下载 |
|
|
184
|
+
| `vidlens_window(02:10–02:25)` | `asr(cached)`,0 下载直接读缓存 |
|
|
185
|
+
| `vidlens_seek("宪法")` | 3 条原文 + 时间码(`00:20.460`、`02:17.720`…),0 网络 |
|
|
186
|
+
| `vidlens_sheet(60–72s, 3×2)` | 下载 12 秒,产出 1440×540 拼图,**图片作为持久附件进入对话**(时间码 `00:00:59.995 … 00:01:09.594`) |
|
|
187
|
+
| `vidlens_frames([63, 68])` | 2 张 960×540 帧,同一条结果里两张图都在 |
|
|
188
|
+
| `vidlens_compare(2 个链接)` | 一张 markdown 表,0 媒体下载 |
|
|
189
|
+
| `vidlens_job_start` → `vidlens_job` | 起任务拿到 `job_id=6652ac4e`,轮询后返回完整 brief(`asr(cached)`,含低置信词/停顿/广告段) |
|
|
190
|
+
| `vidlens_meta(错误 URL)` | 可读错误:`ERROR: meta failed rc=1: ... HTTP Error 404`,工具不崩 |
|
|
191
|
+
|
|
192
|
+
拼图实测(时间码烧在每格左上角):
|
|
193
|
+
|
|
194
|
+

|
|
195
|
+
|
|
196
|
+
## 发布(维护者)
|
|
197
|
+
|
|
198
|
+
- CI:`.github/workflows/ci.yml` —— 会 checkout 两个仓库(本插件 + `cctv2333/vidlens` 作为兄弟目录),
|
|
199
|
+
装 Python 核心后跑离线自检;另外两个 job 校验 manifest 可发布、tarball 含 `bridge/` 与 `LICENSE`。
|
|
200
|
+
- 发版:打 tag 即发布 npm。
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
git tag v1.2.0 && git push origin v1.2.0
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`release.yml` 会 `npm pack`、`npm publish --access public`,走 **Trusted Publishing(OIDC)**——
|
|
207
|
+
CI 里没有任何长期 token。npm 的 publisher 只能在包存在后配置,所以**首次发布要在本地做一次**
|
|
208
|
+
(`npm stage publish` → `npm stage approve`,全程不需要把 token 交给 CI,见 `RELEASING.md`),之后打 tag 即可。
|
|
209
|
+
|
|
210
|
+
> 注意:PowerShell 5.1 不支持 `&&`,用 `;` 或分行。
|
|
211
|
+
|
|
212
|
+
## 实现笔记
|
|
213
|
+
|
|
214
|
+
- **`output.schema` 不能用 `{type:'json'}`**:宿主对工具输出 schema 做严格校验,只接受
|
|
215
|
+
object/array/string/number/integer/boolean/null。第一版用了 `{type:'json'}`,安装直接
|
|
216
|
+
`application: failed`(`JsonSchemaError: unsupported JSON schema`)。现在写成显式的
|
|
217
|
+
object schema(`text` + `images[]`)。
|
|
218
|
+
- **被杀掉的旧进程不能污染新进程**:超时/取消会 `kill()` 工作进程并在同一 tick 重建。旧
|
|
219
|
+
进程的 `exit` 事件是异步到的,必须先判 `this.child !== child` 再清理,否则它会把**新**
|
|
220
|
+
进程上排队的请求全部 reject(这个 bug 是自检里的「强制杀进程后自愈」用例抓到的)。
|
|
221
|
+
- **本插件不导出 `Config`**:`Config.listConfigs` 里这一行显示 `status: absent`(没有配置
|
|
222
|
+
schema),但 `config:` 里的 `projectDir` / `pythonPath` / `warm` 仍会原样传给 `apply()`。
|
|
223
|
+
不导出 schema 是刻意的:schema 需要 schemastery,而插件的解析链未必能找到宿主那份
|
|
224
|
+
`@deepseek-ai/schemastery`;宁可少一个设置表单,也不要一个「载入期 import 失败 → 18
|
|
225
|
+
个工具整体消失」的失败模式。
|
|
226
|
+
- **图标**:`icon.svg` 是原创矢量图(2042 字节,well-formed XML,256×256,边缘圆角 +
|
|
227
|
+
时间码刻度 + 镜头环 + 播放三角)。结构校验通过;Plugin Manager 里的实际渲染效果
|
|
228
|
+
没有在浏览器里验证(无头浏览器对 `file:` / `data:` 导航超时),这一项留待用户目视确认。
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
## 边界与已知限制
|
|
232
|
+
|
|
233
|
+
- **job 状态在进程内存**:`vidlens_job_start` 起的任务,桥进程重启(超时/取消/宿主重启)后会消失;已下的缓存不丢,重跑即可。
|
|
234
|
+
- **取消 = 杀桥进程**:Python 侧没有协作取消,所以被取消/超时的调用会重启工作进程(缓存在磁盘上,代价很小)。
|
|
235
|
+
- **需要 cookie 的站点**(抖音、B 站 AI 字幕)默认不碰:按报错提示设 `VIDLENS_COOKIES_FROM_BROWSER=edge` 或 `VIDLENS_COOKIES=path\cookies.txt`。
|
|
236
|
+
- **不下整片**:`video_full` 故意没有移植。整片画面需求用 `vidlens_scan`(最低画质、用完即删)。
|
|
237
|
+
- **PyPI 形态也能接**:工程装成 wheel 后没有 `vidlens.py` 旁边的 checkout 标记,插件的
|
|
238
|
+
目录嗅探找不到它;把 `config.projectDir` 指到 `pipx` 的 venv 目录(或任意含 `vidlens.py`
|
|
239
|
+
的目录),或设 `VIDLENS_PROJECT` 即可。安装形态下 `out/` 会落在 `%LOCALAPPDATA%\vidlens\out`。
|
|
240
|
+
- **Windows 优先**:工程默认路径与 `.venv\Scripts\python.exe` 是 Windows 习惯;Linux/macOS 用 `projectDir` + `pythonPath` 显式指定即可(跨进程锁在 POSIX 用 `flock`)。
|
|
241
|
+
- 工程本体仍无 `LICENSE`,公开分发前需项目所有者选定;本包 `license` 标为 `UNLICENSED`。
|
package/RELEASING.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# 发布(维护者)
|
|
2
|
+
|
|
3
|
+
首次发布用 **staged publishing**(`npm stage publish` **不需要 2FA**,批准时才要);
|
|
4
|
+
之后全部走 **OIDC Trusted Publishing**,CI 里没有任何长期 token。
|
|
5
|
+
|
|
6
|
+
> 别用 `npm publish` 做首次发布:npm 现在对它强制 2FA,CLI 的 `--otp` 也常被同样拒绝
|
|
7
|
+
> (`403 ... bypass 2fa enabled is required`)。
|
|
8
|
+
|
|
9
|
+
## 1. 首次发布:stage(不需要 2FA)
|
|
10
|
+
|
|
11
|
+
```powershell
|
|
12
|
+
npm --version # 需要 >= 11.15.0,低了就 npm i -g npm@latest
|
|
13
|
+
cd D:\vidlens\vidlens-mcp-dsh
|
|
14
|
+
npm stage publish --access public
|
|
15
|
+
npm stage list # 记下 id,等 status 变成 staged
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
新包会先在 registry 上生成一个 `0.0.0-stage` 占位版本(公开可见),
|
|
19
|
+
真正要发的版本留在 staging 区,**批准后才上线**。
|
|
20
|
+
|
|
21
|
+
## 2. 批准(要 2FA,只有你能做)
|
|
22
|
+
|
|
23
|
+
**用命令行,最可靠**(网页的 Staged Packages 入口时有时无,`/package/<name>/staged` 可能 404):
|
|
24
|
+
|
|
25
|
+
```powershell
|
|
26
|
+
npm stage approve <stage-id> # 会提示输入 6 位 OTP
|
|
27
|
+
npm view dsh-vidlens versions # 批准后应能看到目标版本
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
> OTP 只有 30 秒有效,输慢了就重来。
|
|
31
|
+
> 如果 2FA 是「安全密钥(WebAuthn)」而不是验证器 App,CLI 无法输入 OTP——
|
|
32
|
+
> 先把 npm 账号的 2FA 改成 TOTP(Authenticator app)再重试。
|
|
33
|
+
|
|
34
|
+
## 3. 配置 Trusted Publisher(一次性)
|
|
35
|
+
|
|
36
|
+
1. 打开 <https://www.npmjs.com/package/dsh-vidlens/access>
|
|
37
|
+
(或包页 → **Settings** → **Trusted Publisher**)
|
|
38
|
+
2. **Add trusted publisher** → **GitHub Actions**,填:
|
|
39
|
+
|
|
40
|
+
| 字段 | 值 |
|
|
41
|
+
|---|---|
|
|
42
|
+
| Organization or user | `cctv2333` ← **GitHub** 用户名(不是 npm 的 `ghost120`) |
|
|
43
|
+
| Repository | `dsh-vidlens` |
|
|
44
|
+
| Workflow filename | `release.yml`(只写文件名,带 `.yml`) |
|
|
45
|
+
| Environment name | 留空 |
|
|
46
|
+
| Allowed actions | 勾上 **npm publish** |
|
|
47
|
+
|
|
48
|
+
3. 保存。
|
|
49
|
+
**注意**:新配置必须在 **2 天内成功发布一次**,否则会过期作废。
|
|
50
|
+
|
|
51
|
+
## 3. 之后发版(CI,无 token)
|
|
52
|
+
|
|
53
|
+
```powershell
|
|
54
|
+
cd D:\vidlens\vidlens-mcp-dsh
|
|
55
|
+
git tag v1.2.1
|
|
56
|
+
git push origin v1.2.1
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`.github/workflows/release.yml` 用 `id-token: write` + Node 24(npm ≥ 11.5.1),
|
|
60
|
+
`npm publish` 自动用 OIDC 换短期凭证,并自动生成 provenance(来源证明)。
|
|
61
|
+
|
|
62
|
+
## 4. 收尾(安全)
|
|
63
|
+
|
|
64
|
+
- GitHub → Settings → Secrets and variables → Actions → 删除 `NPM_TOKEN`
|
|
65
|
+
- <https://www.npmjs.com/settings/~/tokens> → 删掉之前建的 granular token
|
|
66
|
+
- 可选强化:包 Settings → **Publishing access** 选
|
|
67
|
+
“Require two-factor authentication and disallow tokens”——Trusted Publisher 不受影响
|
|
68
|
+
|
|
69
|
+
## 常见失败
|
|
70
|
+
|
|
71
|
+
| 现象 | 原因 | 处理 |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| `ENEEDAUTH` / `Unable to authenticate` | workflow 文件名、repo、environment 任一不符;或没给 `id-token: write` | 逐字核对;workflow 必须叫 `release.yml` |
|
|
74
|
+
| 提示 repository 不匹配 | `package.json` 的 `repository.url` 与 GitHub 仓库不一致 | 保持 `git+https://github.com/cctv2333/dsh-vidlens.git` |
|
|
75
|
+
| `E409` / `cannot publish over` | 同版本号已存在 | 改 `package.json` version 再打新 tag |
|
|
76
|
+
| `403 ... bypass 2fa` | 还在用 token 路径 | 本仓库已切 OIDC;确认 workflow 里没有 `NODE_AUTH_TOKEN` |
|
|
77
|
+
| 配置 2 天后失效 | 首次发布没在 2 天内完成 | 删掉重新添加 publisher |
|