dsh-baleen 0.1.0 → 0.2.0

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/AGENTS.md CHANGED
@@ -41,6 +41,10 @@ Baleen 是一个 DSH(DeepSeek Harness 桌面版)插件:定时把信息源
41
41
  - **`client.js` 是生成产物,别手改**:源在 `src/client/`,改完跑 `node tools/build-client.mjs`(`npm run build:check` 只验证新旧)。`test/build.test.js` 有两条守卫:产物逐字节比对、以及"`src/client` 下每个 `.js` 都在 `PARTS` 里"——新加视图忘了登记 `PARTS`,面板不会有任何变化,也不会报错。
42
42
  - 生成方式是把多个源文件**内联进同一个 factory 作用域**(只剥 `import`/`export` 行),所以**两个源文件里的同名 `const` 会直接 `SyntaxError`**。节点工厂统一用 `src/client/vnode.js` 的 `vnode()`,别在视图里再造同名局部函数。
43
43
  - 视图返回的是纯对象 `{type, props, children}`,不是 React 元素;装配层用 `toElement()` 桥给 `createElement`。测试桩的 `flattenText` 会**调用函数组件**(桩不 reconcile),所以面板断言看得见内容——但也因此看不见真 React 的键/形状问题,那一层只能在预览门和真机上看。
44
+ - **重启 DSH 前先看两件事**:会话树里有没有"进行中"的任务、插件页有没有"正在安装 / 排队中"。宿主在装卸插件期间会重写 profile 的 `cordis.patch.yml`,这时候重启等于拿装配条目赌运气(2026-11-04 实测撞上"安装 dsh-openwiki + 一条 Issue #557 的 agent 正在跑",探针因此改成不重启的那条路)。
45
+ - **真机探针的三个坑**(侧栏落点这类看不见的效果):① 停靠的 DevTools 正压在窗口底部,而 `openTab` 不带 `path`/`url` 时的落点恰恰是底部工作台——先关 DevTools 再判有没有开出来;② console 求值的 document 与应用 UI 不是同一棵渲染树(`document.body.innerText` 在几百到几千字符之间乱跳、`querySelectorAll('iframe')` 恒为 0),**DOM 判据不可信,只能看截图**;③ `document.title` 会被应用的标题同步覆写,回读结果用 `position:fixed` 覆盖层;**别照旧的无障碍序号点 DevTools 的元素**——一次渲染后序号会漂到应用自己的导航上,实测把宿主界面导到了「插件」页。
46
+
47
+ - **宿主 web boot 对插件 slot 回调的抛错零容忍**:任何插件注册进 slot 的回调(如 `sidebar.panellist` 的 `label`)一抛错,宿主自家的 `dsh-client-ui-sidebar` 会卡在 loading,web boot 直接 abort——窗口永远出不来,但进程全活着。日志在 `%APPDATA%@deepseek-ai/dsh-desktop/logs/crash-*-web-boot.log`(文件头有 "N entries did not activate")。诊断手法:关掉后用 `DeepSeek Harness.exe --remote-debugging-port=9222` 重启,CDP(`/json` 拿 target → Runtime.exceptionThrown 的结构化 callFrames)能看到抛错函数与 bundle 内行号;bundle 是按批拼接的第三方 client.js,用 scriptParsed 时的**当前 rev** 在页面内 fetch 原文即可逐行定位是哪个插件哪一行。(2026-11-10 实证:dsh-artifacts 0.1.0 的 `label: () => t('panel')` 引用了不存在的 `t`,一行修复。)
44
48
 
45
49
  ## 界面协作约定
46
50
 
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  >
5
5
  > 名字来自鲸须——须鲸的滤食结构。这个产品做的同一件事。
6
6
 
7
- **当前状态:M0–M5 全部完成并已装载进 DSH 真机验收。** 摄取(RSS / B 站 / YouTube 三通道,读正文与字幕)→ 本地预筛 → 面板六格导航与收藏页 → 判断轮(五个只读工具 + 唯一写工具 save_verdicts,三档枚举锁死)→ 每轮增量汇报 → 设置页四页签,全链路真机跑过。小红书通道未做(需先做代码审查)。
7
+ **当前状态:M0–M7 全部完成并已装载进 DSH 真机验收。** 摄取(RSS / B 站 / YouTube / 小红书四通道,读正文与字幕;RSS 摘要之外还有零依赖的正文补抓)→ 本地预筛 → 面板六格导航与收藏页 → 判断轮(五个只读工具 + 唯一写工具 save_verdicts,三档枚举锁死)→ 每轮增量汇报 → 设置页四页签,全链路真机跑过。视频在面板里直接看:抽屉内嵌播放(B 站完整页 / YouTube embed,裁掉推荐流),或送到 dsh-better-sidebar 的右侧栏页签;文章有护眼排版。
8
8
 
9
9
  ## 看面板长什么样(不装 DSH、不联网)
10
10
 
@@ -20,13 +20,24 @@ open out/preview-today.html # 浏览器直接打开即可
20
20
 
21
21
  ```bash
22
22
  npm install # 唯一依赖:yaml(纯 JS,无 install 脚本)
23
- npm test # 578 个测试,node --test,不需要网络
23
+ npm test # 661 个测试,node --test,不需要网络
24
24
  cp preference.example.yaml ~/baleen-trial/preference.yaml # 先自己建目录
25
25
  BALEEN_HOME=~/baleen-trial node tools/live-ingest.mjs --reset # 真抓一轮公开 feed
26
26
  ```
27
27
 
28
28
  `tools/live-ingest.mjs` 不经 DSH、用假宿主上下文直接跑一轮,`BALEEN_HOME` 指到临时目录就不会碰你真实的 `~/.dsh/baleen`。
29
29
 
30
+ ## 装进 DSH
31
+
32
+ ```bash
33
+ npm i -g dsh-baleen # 或 npm i dsh-baleen 装进 profile
34
+ dsh plugin --profile desktop add dsh-baleen
35
+ ```
36
+
37
+ 装完**重启 DSH** 才会装配(link 挂载不等于热加载)。重启前用 `dsh --profile desktop --dump-config` 确认插件在装配列表里——宿主运行中重写 `cordis.patch.yml` 会把条目丢掉。
38
+
39
+ 首次启动会在 `~/.dsh/baleen/` 生成 `preference.yaml` 与空库;改偏好走面板设置页,或直接编辑那个 YAML(面板写的是同一份真源)。
40
+
30
41
  ## 设计原则(改代码前先读这几条)
31
42
 
32
43
  - **只读世界**:不注册任何平台写操作。没有评论、私信、点赞、关注、发布——不是"暂时没做",是产品红线。
@@ -36,6 +47,7 @@ BALEEN_HOME=~/baleen-trial node tools/live-ingest.mjs --reset # 真抓一轮
36
47
  - **首轮收存货**:某个源还没有游标时不看发布日期——低频作者的 feed 里全是几个月的旧文,而刚订阅的你没读过,那正是首轮最值钱的部分。之后才用 `max_age_days`,且**可以按源覆盖**(慢更源 365 天,高频源 14 天)。
37
48
  - **成本三道闸**:本地预筛在前 → `max_per_run` → `max_per_day`;工具失败两次就记跳过,不许反复重跑。
38
49
  - **默认按偏好过滤**:`require_theme_hit: true` 时,没命中任何主题且不是关注 feed 的内容不进候选。关掉它就退回"启用一个源=它全部内容都进候选"。
50
+ - **封面与原文都是真链接**:卡片上的封面图直接读库里 `items.thumb`(平台原样地址,只认 http/https,坏值退回灰占位而不是空 `src` 的裂图标);抽屉里视频直接内嵌播放(V5,裁切窗把 B 站的推荐流和页头推出可视区,原生控制条完整可见),「侧栏播放」把它送到 dsh-better-sidebar 的右侧栏页签(面板在前台时先排队——排队落地时序在 better-sidebar 0.30.1 上有上游缺口,不保证立刻出现),「浏览器打开」走你自己的系统浏览器。侧栏浏览器每次启动都是全新分区(没有登录态),内嵌 iframe 同样无登录态——B 站不登录也能播,约 30 秒后弹一次登录浮层。
39
51
 
40
52
  完整约束见 [AGENTS.md](AGENTS.md),设计见 [docs/superpowers/specs/2026-10-23-baleen-design.md](docs/superpowers/specs/2026-10-23-baleen-design.md),实测记录见 [docs/verification.md](docs/verification.md)。
41
53
 
@@ -61,6 +73,8 @@ run.lock 单实例锁(带 pid,进程被强杀后陈旧锁会被
61
73
  | `lib/adapters/rss.js` | RSS/Atom 适配器;失败时抛 `status` / `empty` 供上层分类 |
62
74
  | `lib/adapters/bili.js` `lib/adapters/bili-wbi.js` | B 站:纯 fetch 直连 + 自研 wbi 签名 + 风控指纹;字幕走 player/wbi/v2 |
63
75
  | `lib/adapters/yt.js` `lib/adapters/yt-captions.js` | YouTube:yt-dlp 直连取列表与字幕轨(轨间回退),已入库的 external_id 跳过重抓 |
76
+ | `lib/adapters/xhs.js` `lib/mcp/xhs-client.js` | 小红书:官方 MCP 子进程 + 只读白名单,搜索关键词 → 笔记列表 → 逐条详情 |
77
+ | `lib/fulltext.js` | 零依赖正文抽取:RSS 只给摘要时回原文抓正文,宁可抽不到也不把评论区当正文 |
64
78
  | `lib/feed.js` | RSS2 + Atom 容错解析(CDATA、坏日期、双重转义、时长三种写法) |
65
79
  | `lib/pool.js` | 并发 4、单 job 超时 20s、**重试固定 1 次**、错误分类 |
66
80
  | `lib/normalize.js` | RawItem → items 行:`kind` / `body_state` / 命中理由与得分 |
@@ -73,8 +87,9 @@ run.lock 单实例锁(带 pid,进程被强杀后陈旧锁会被
73
87
 
74
88
  ## 还没做
75
89
 
76
- - 小红书通道(扫码 MCP 方案,需先做代码审查)
77
90
  - 汇报的图文排版(当前是 Markdown,图片位留白)
91
+ - 播放器自绘控制条(音量/进度在跨源 iframe 里面够不着,现在是明说"用画面里的原播放器控件")
92
+ - 侧栏播放页签的排队落地时序:better-sidebar 0.30.1 上排队路径不保证落地(0.28.2 同路径可用),是上游升级引入的集成缺口,待上游排查或降级验证——tab 一旦落地渲染全对
78
93
 
79
94
  ## 名字提示
80
95