@morehao/dsh-github-trending 0.1.4 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 - 2026-10-31
4
+
5
+ ### 变更(含破坏性)
6
+
7
+ - **契约改用独立 npm 包**:类型与运行时校验全部来自 `@morehao/trendradar-contract@0.2.0`(`dependencies` 精确锁定),删除仓库内 `contract/` 快照、`scripts/sync-contract.mjs`、`scripts/gen-ipc-types.mjs` 与 `src/ipc.gen.ts`。契约升级从此只改依赖版本。
8
+ - **wire 升级到 v2**:`ipcSchema=2` / `digestSchema=2`,握手额外校验 `contractVersion` 主版本(0)。与核心 0.3.0 配套;混搭旧核心会被 `CORE_INCOMPATIBLE` 明确拦下。
9
+ - **单一 RunResult 解析**:`parseRunResult` 只接受契约 v2 `RunResult`(用契约包自带 ajv 校验器),删除对旧核心 `runOutput` 的兼容分支;`branch` / `commit` / `prUrl` / `contentHash` 为可选字段。
10
+ - **旧状态迁移**:读取时若 `lastDigest` 缺 `contentHashAlgo` 或 `coreVersion` 早于 0.3.0,判定为 legacy,清空 `lastDigest` 与绑定的 `pendingPr`;旧历史记录打 `legacy` 标。插件启动时(`migrateLegacyState`)把清洗结果落盘。
11
+ - 新增 `scripts/set-core-version.mjs` 写 `dsh.core.version`(本版为 `0.3.1`);`check:naming` 改为校验契约包依赖与 wire 版本。
12
+ - **核心版本锁定 `0.3.1`**(`dsh.core.version`):首次运行下载带 append-only 推送与 `TRENDRADAR_TRIGGER` 支持的核心。
13
+
14
+ ### 变更(运行与调度)
15
+
16
+ - **推送失败可自愈**:核心改为日期分支追加提交 + 普通推送,`stale info` / `non-fast-forward` 不再需要人工进仓库处理;插件把 push 失败细分为 `stale-lease` / `non-fast-forward` / `auth` / `protected` / `hook` / `pathspec`,并按原因给出下一步(zh/en)。
17
+ - **跳过原因可见**:结果与历史记录新增 `skipReason`(`merged` / `pr-closed` / `unchanged`),运行页直接说明为什么没跑或没提交。
18
+ - **定时运行尊重已关闭的 PR**:插件经 `TRENDRADAR_TRIGGER` 把触发来源传给核心;定时运行遇到 closed-unmerged 的 PR 会跳过,手动运行才会重建并重开。
19
+ - **调度改为「成功才锁当天」**:失败不再吃掉一整天;同一 catch-up 窗口内最多重试 2 次、间隔至少 5 分钟。
20
+ - 历史记录保存 `errorReason` / `skipReason`,重开界面仍能看到原因。
21
+
22
+ ### 新增(工作台 / 日报预览 / 最近 30 天)
23
+
24
+ - **01 工作台**:侧栏入口进入「概览 / 日报 / 运行记录 / 设置」四视图;概览回答下次运行、就绪检查(核心 / 模型 / 仓库 / GitHub)、上次交付与最近 7 天;运行记录支持筛选、展开交付物、pendingPr/legacy 状态与只读日志抽屉;设置继续沿用 #13 的六个 tab,本轮不重排。
25
+ - **日报预览**:运行成功后只读核心本次 `outcome.files` 里的当天 `.md`(优先 `YYYY-MM-DD.md`,否则只有一个 `.md` 时用它),路径必须在 `repo.path` 内(词法 + realpath)、≤512 KB、无 NUL、sha256 与契约一致,才复制到 `$DSH_HOME/github-trending/previews/<runId>.md`;0600、临时文件 + rename、最多 30 份。预览失败只记日志,不阻断运行;`state.json` / `history.json` 只存元数据。
26
+ - **最近 30 天日期切换**:日报按调度时区把 history 按日期分组,每天一条(优先当天成功且有预览的最近一条);默认打开最近一期有预览的日期;失败 / 无变更 / 试跑 / 旧记录 / 副本缺失各有空状态;切换用 React state + AbortController,不写 URL hash。
27
+ - **渲染安全**:Markdown 子集(标题 / 段落 / 列表 / 引用 / 粗体 / 行内代码 / 链接 / 分隔线 / frontmatter 元信息),只生成 React 文本节点,链接仅 http(s),不渲染 HTML / 图片;可切「原文」。
28
+ - **关于与仓库入口**:`/state.plugin` 增加 `repository`(由 `package.json.repository` 规范化),左轨包名点击打开关于弹层;完整包名可复制,外链 `target=_blank rel=noreferrer`。
29
+ - **Host 数据**:`RunRecord` / `RunSummary` 补 `date` / `commit` / `preview`,`/state` history 从 10 条改为 30;新增 `GET /preview?id=<runId>`(只接受历史里出现过的 id、不接路径、自定义头鉴权、`no-store`)。
30
+ - **测试**:新增 state / runner / routes / plugin-info / markdown-core / digest-core 用例;`npm run check` 145 条单测 + AC-P 11 条全绿。
31
+
32
+ ### 修复
33
+
34
+ - **删掉「高级设置」开关**:它是设置页底部的全局按钮,只在「内容来源 / 模型」两个 tab 插入字段,且插在卡片顶部——用户滚到底部点它看不到任何变化(4 个 tab 更是完全无反应)。现在移除该按钮与两个仅它能编辑的字段(`TRENDING_DIGEST_CORE`、手动 `凭据引用`;前者继续支持环境变量与 `settings.json`,后者由「沿用 DSH 配置」自动带入)。「内容来源」没有可编辑项,因此该 tab 不再显示「保存本页」。
35
+ - **凭据状态按密钥来源判断**:回环代理模式下密钥由宿主自己带上(已经复用 DSH 凭据),不再显示红色的「未配置」,改为「沿用 DSH 凭据」并说明本页无需填写;直传模式仍如实区分「已配置 · 来源」与「未配置(引用为空 / 解析不到)」。设置页、概览就绪检查与外壳就绪数统一走同一套判定。
36
+
37
+ ## 0.2.0 - 2026-10-31
38
+
39
+ ### 变更
40
+
41
+ - **设置页只出现在左侧菜单栏**:不再同时注册 `settings.section`,改为左侧栏一个入口、内含「设置 / 运行」两个 tab,避免同一插件在左侧栏与设置里各出现一份。
42
+ - **跟随宿主明暗主题**:样式表从宿主并不存在的 `--dsh-fg / --dsh-bg / ...` 换成设计系统 token `--dsw-alias-*`,暗色桌面端不再强行渲染亮色。
43
+ - **设置页改为左侧纵向 tab + 右侧滚动**:六个分区(内容来源 / 模型 / 仓库 / 认证 / 榜单 / 调度)纵向排列,右栏独立滚动;面板根补上 `height/overflow`,修复宿主 `main` 面板裁掉内容且不出纵向滚动条的问题(运行页同样受益)。
44
+ - **沿用 DSH 配置更省心**:改为读取宿主 `ctx.agentDefaultModel.currentSelection()` 的当前默认模型一键带入(默认置顶),并只列**实际生效路由**(`listProviders`)及其模型目录(`listModels`)供按需展开选择;不再把 `listConfigurableProviders` 的能力目录当成「已配置」,也不会再把「支持 OAuth」误判为可用。`凭据引用` 默认隐藏、随选择自动带入。
45
+ - **首屏只留普通用户字段**:`TRENDING_DIGEST_CORE` 与手动 `凭据引用` 收进「高级设置」;仓库路径改为目录选择;目标分支只读展示仓库默认分支(`git symbolic-ref` 读 `origin/HEAD`,只读不写仓库);`远端`、`PR 标题模板` 不再露出,用默认规则。
46
+ - **GitHub 认证收敛**:只保留真正可用的「本机 gh」与「PAT」两种,各自给出可操作步骤,并新增「测试认证」按钮(`POST /github/test` 调 `api.github.com/user` 验证并回显账号)。删除从未实现的 Device Flow(旧配置里的 `device` 自动迁移为 `gh`)。
47
+ - **侧栏入口不再是一整张卡片**:`sidebar.panellist` 只渲染 16–18px 图标(`SidebarPanelIcon`),状态摘要与「立即运行 / 试跑」留在面板页。
48
+ - **设置按 tab 分段保存**:每个 tab 独立提交与校验(`POST /config` 新增 `section`),保存某个分区不会顺带提交或覆盖其它 tab 里尚未保存的改动;左栏 tab 用黄点标记未保存改动。
49
+
50
+ ### 修复
51
+
52
+ - **运行日志写到真实 `~/.dsh`**:`Store.logsPath` 之前忽略 `dir` 选项,测试/自定义数据目录也会把 `runs/*.log` 写进默认数据目录;改为跟随 `this.dir`。
53
+ - **代理模式下保存被 baseURL 校验误拦**:`mode=proxy` 的端点由运行时回环代理注入、quickfill 也会清空 `baseURL`,但 `/config` 仍强制其非空,导致「模型预检通过、保存却报 `model.baseURL:该 provider 需要显式 baseURL`」。现在代理模式豁免该必填校验(仍校验非空时的 http(s) 格式)。
54
+
3
55
  ## 0.1.4 - 2026-10-30
4
56
 
5
57
  > 0.1.3 从未发布到 npm(tag 推上去后 `release.yml` 在「发布前校验」失败)。0.1.4 是这批改动的第一次真实发布,内容 = 0.1.3 小节 + 本节的修复。
package/README.md CHANGED
@@ -5,14 +5,15 @@
5
5
 
6
6
  > GitHub 趋势日报的 **DSH 入口**:每天定时 spawn 本地 [`trendradar`](https://github.com/morehao/trendradar) 核心,抓取 Trending、生成中文简介与当日总结,提交到你自己的仓库并自动合并 PR。
7
7
 
8
- **状态:0.1.2。** 插件侧(配置、调度、核心调用、状态与界面、两个工具)已完成。核心二进制走 **GitHub Release**:首次运行按 `os/arch` 下载、用 `checksums.txt` 校验 sha256、缓存到插件数据目录,之后离线复用。核心仓库打 tag 出资产之前,运行会明确报 `CORE_NOT_DOWNLOADED` / `CORE_UNAVAILABLE` 并给出下载地址——不会写空文件,也不会退回旧内容。
8
+ **状态:0.3.0。** 插件侧(配置、调度、核心调用、状态与工作台、两个工具)已完成。核心二进制走 **GitHub Release**:首次运行按 `os/arch` 下载、用 `checksums.txt` 校验 sha256、缓存到插件数据目录,之后离线复用。核心仓库打 tag 出资产之前,运行会明确报 `CORE_NOT_DOWNLOADED` / `CORE_UNAVAILABLE` 并给出下载地址——不会写空文件,也不会退回旧内容。
9
9
 
10
10
  ## 它做什么
11
11
 
12
- - 按你设定的时间(IANA 时区)每天跑一次;也可以在设置页或侧栏卡片点「立即运行」
12
+ - 按你设定的时间(IANA 时区)每天跑一次;从侧栏入口进入工作台,点「立即运行」
13
13
  - 一次运行 = **spawn 一次本地核心**:抓取 → 模型总结 → 渲染 → commit → push → PR → 合并,全部由核心完成
14
14
  - 插件只负责:收集配置、调度、传配置与密钥、展示结果与错误、记录最近 30 次运行
15
15
  - 内容按核心产出的成品**原样**落地(canonical),插件不改写、不重排、不二次调用模型
16
+ - 工作台分「概览 / 日报 / 运行记录 / 设置」:概览回答下次运行、就绪检查与上次交付;日报按日期回看最近 30 天;运行记录可展开交付物并看只读日志
16
17
 
17
18
  **它不做什么**:不抓 GitHub、不解析 HTML、不调模型(回环代理模式除外)、不渲染内容、不执行 git。这些只有核心一处实现,避免两份实现漂移。
18
19
 
@@ -30,6 +31,17 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
30
31
  2. **填模型**:点「沿用 DSH 配置」从宿主 provider 目录里挑 provider 与模型;可导出密钥的直接传给核心,OAuth 类凭据走本机回环代理。
31
32
  3. **试跑一次**:「试跑(不提交)」只抓取与渲染,不写文件、不提交。
32
33
 
34
+ > 设置页分「内容来源 / 模型 / 仓库 / 认证 / 榜单 / 调度」六个 tab;**除「内容来源」(只有状态与核心操作,没有可保存字段)外,每个 tab 独立保存与校验**;某个 tab 有未保存改动时,左侧对应 tab 会显示黄点。
35
+
36
+ ## 日报预览与最近 30 天
37
+
38
+ - 运行成功后,Host 从核心本次返回的 `outcome.files` 里挑当天的 `.md`(优先 `YYYY-MM-DD.md`,否则只有一个 `.md` 时用它),**只读**复制到 `$DSH_HOME/github-trending/previews/<runId>.md`:路径必须在 `repo.path` 内(词法 + realpath 各判一次)、不超过 512 KB、sha256 与契约声明一致;失败不阻断运行,只是这次没有预览。
39
+ - 预览副本 0600、临时文件 + rename、最多保留 30 份;`state.json` / `history.json` 只存 `file` / `bytes` / `sha256` 元数据,不存正文。
40
+ - `GET /preview?id=<runId>` 只接受历史里出现过的 id、不接路径参数;返回 `{ text, truncated }`。客户端渲染 Markdown 子集(标题 / 段落 / 列表 / 引用 / 粗体 / 行内代码 / 链接 / 分隔线),**只生成 React 文本节点**,链接仅放行 http(s),不渲染图片或原始 HTML;也可以切「原文」。
41
+ - 日报只列最近 30 天**有记录**的日期,每天一条;默认打开最近一期有预览的日报。失败 / 无变更 / 试跑 / 旧记录 / 副本被清理都有各自的空状态,不用示例内容兜底。
42
+ - 预览是运行结束时的派生副本:你在仓库里事后编辑 canonical 文件,不会改变这次记录。
43
+ - 关于入口在左轨底部:包名 / 版本 / GitHub 来自 `/state.plugin`(`repository` 由 `package.json` 规范化),仓库缺失时隐藏外链。
44
+
33
45
  ## 它需要什么 / 它访问哪里 / 它不上传什么
34
46
 
35
47
  **需要什么**
@@ -37,7 +49,7 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
37
49
  - DSH `0.2.1-rc.3` 以上(`engines.dsh`),Node.js `^22.20.0 || >=24.0.0`(`.nvmrc` 钉 22 线;CI 与发布跑 22)
38
50
  - 一个本地 **git 仓库**(提交目标)与可写的远端
39
51
  - 模型:provider + 模型 ID + 密钥(或用宿主已配置的 provider;OAuth 类凭据不需要密钥)
40
- - Git 与 GitHub 凭据(PAT / Device Flow / 本机 `gh`),仅 push 与开 PR 需要
52
+ - Git 与 GitHub 凭据(本机 `gh` 登录态 / PAT),仅 push 与开 PR 需要
41
53
  - 核心二进制:首次运行从 `github.com/morehao/trendradar/releases` 下载(按 `os/arch`,sha256 校验后缓存);之后离线复用。开发/排障时可用 `TRENDING_DIGEST_CORE`(或设置里的「核心路径」)指向一个可执行的 `trendradar`,此时完全不下载
42
54
 
43
55
  **访问哪里**
@@ -51,7 +63,7 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
51
63
  **不上传什么**
52
64
 
53
65
  - 不向作者控制的任何服务器发送数据;插件没有遥测、没有云端账号
54
- - 不读取、不上传与你配置无关的仓库内容;核心只在目标仓库里写 `output.dir` 下的文件
66
+ - 不读取、不上传与你配置无关的仓库内容;核心只在目标仓库里写 `output.dir` 下的文件;插件唯一的仓库读取是运行成功后按清单复制当天 `.md` 预览副本(见上节),只读、不 commit、不改工作树
55
67
  - 密钥与 token 只经 `ctx.credentials` 解析、只经运行时通道交给核心:**不写配置文件、不进日志、不进 argv**
56
68
 
57
69
  ## 配置
@@ -68,15 +80,17 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
68
80
  | `model.temperature` / `maxTokens` | `0.2` / `2048` | 单次调用参数 |
69
81
  | `model.allowDegraded` | `true` | 模型失败时核心是否退回原文(会标记 degraded) |
70
82
  | `repo.path` | 空 | 本地仓库绝对路径,必填 |
71
- | `repo.baseBranch` / `remote` | `main` / `origin` | |
83
+ | `repo.baseBranch` | 读取仓库默认分支 | 只读展示,不允许手改(以 `origin/HEAD` 为准) |
84
+ | `repo.remote` | `origin` | 固定 `origin`,界面不展示 |
72
85
  | `repo.commitMode` | `branch` | `branch`(分支 + PR)/ `direct` |
73
- | `pr.enabled` / `autoMerge` / `titleTemplate` | `true` / `true` / `chore(trending): add {date} digest` | |
74
- | `github.authMode` | `device` | `device` / `pat` / `gh` |
75
- | `github.tokenRef` | `GITHUB_TOKEN` | `ctx.credentials` 引用名 |
86
+ | `pr.enabled` / `autoMerge` | `true` / `true` | |
87
+ | `pr.titleTemplate` | `chore(trending): add {date} digest` | 默认规则,界面不展示 |
88
+ | `github.authMode` | `gh` | `gh`(本机 gh 登录态)/ `pat` |
89
+ | `github.tokenRef` | `GITHUB_TOKEN` | `ctx.credentials` 引用名(`pat` 用) |
76
90
  | `trending.since` / `languages` / `topN` | `daily` / `all,typescript,python,go,rust` / `25` | 实际支持范围由核心决定 |
77
91
  | `output.dir` | `trending` | 仓库内相对目录 |
78
92
  | `schedule.enabled` / `time` / `timezone` | `true` / `08:30:00` / `Asia/Shanghai` | 插件自己的 60 s tick 驱动 |
79
- | `corePath` | 空 | 等价于 `TRENDING_DIGEST_CORE`(仅开发/排障;设了就完全不下载) |
93
+ | `corePath` | 空 | 等价于 `TRENDING_DIGEST_CORE`(仅开发/排障,界面不提供入口,用环境变量或直接改 `settings.json`;设了就完全不下载) |
80
94
  | `coreAutoDownload` | `true` | 缓存缺失时是否自动下载核心;关掉后只报 `CORE_NOT_DOWNLOADED`,由你在设置页点「下载核心」 |
81
95
 
82
96
  ## 两个工具
@@ -100,11 +114,14 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
100
114
  | `IPC_INVALID` | 核心 stdout 不是约定的结果信封 | 升级插件与核心 |
101
115
  | `MODEL_KEY_MISSING` / `MODEL_UNAUTHORIZED` / `MODEL_NOT_FOUND` / `MODEL_RATE_LIMITED` | 模型侧 | 设置页「模型」区红字 + 重新选择 |
102
116
  | `GIT_NOT_INSTALLED` / `REPO_NOT_GIT` / `REPO_DIRTY` | 本机 git / 仓库状态 | 装 git ≥ 2.30、换仓库、先提交改动 |
117
+ | `GIT_OPERATION_FAILED` | 核心执行 git 失败(含 push 被拒) | 核心对 `trending/` 日期分支只追加提交、普通 fast-forward 推送;被拒时自动 fetch + rebase 重试一次。若报 `stale info`:在仓库执行 `git fetch --prune origin` 后重试(升级核心后不再出现) |
103
118
  | `GITHUB_AUTH` / `PUSH_FAILED` / `PR_FAILED` / `NOT_MERGEABLE` | 推送与 PR | 本地 commit 已保留,修好后重试即可复用 |
104
119
  | `FETCH_FAILED` / `FETCH_RATE_LIMITED` | 抓取 | 检查网络/代理或稍后重试 |
105
120
 
106
121
  核心只在 stderr 给出 `error: <CODE>: ...` 文本;插件优先取核心自己的码,取不到时按稳定文本特征归类,并在结果里标 `inferred: true`(界面会注明「错误码由插件推断」)。
107
122
 
123
+ 运行结果还会带 `skipReason`(`merged` / `pr-closed` / `unchanged`)与 push 失败的 `reason`(`stale-lease` / `non-fast-forward` / `auth` / `protected` / `hook` / `pathspec`)。定时运行(`TRENDRADAR_TRIGGER=schedule`)尊重人工关闭的 PR,不重建分支;手动运行才会重开。
124
+
108
125
  ## 与本地 CLI 共存
109
126
 
110
127
  - 插件**永远使用自带的平台包**,不从 `PATH` 解析核心
@@ -112,14 +129,15 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
112
129
  - 首次可在设置页「从本机 CLI 导入」一次性导入非敏感字段;**密钥不导入**
113
130
  - 别让两套定时同时开着:内容侧靠 `contentHash` 与 PR 复用兜底,模型消耗无法兜底
114
131
 
115
- ## 已知偏差(0.1.1)
132
+ ## 实现说明(0.3.0)
116
133
 
117
- 1. **核心尚无 stdin 信封**:核心当前的 `run` 只接受 `--config <path> --json`,插件据此传配置;`ok/startedAt/error.code` 形状的 `RunResult` 已兼容,核心补上后插件无需改。
134
+ 1. **结果信封**:核心 `run --config <path> --json` 输出契约 v2 `RunResult`(`ipcSchema=2`,可选 `branch/commit/prUrl/contentHash`);插件只解析这一种形状,旧核心的 `runOutput` 不再兼容。
118
135
  2. **密钥传输**:POSIX 上配置经 **FIFO** 交给核心(文件系统里不出现明文);Windows 无 FIFO,退化为 0600 临时文件并在读完后立即删除。
119
136
  3. **GitHub token** 经子进程环境变量传给核心(核心只认 `GITHUB_TOKEN`/`GH_TOKEN`);不进 argv、不进配置、不进日志。
120
137
  4. **调度**用插件自己的 60 s tick(宿主 `schedule` 是会话本地提醒,覆盖不到无人值守场景)。
121
- 5. **二进制改为下载**(2026-10-30 决策 B):不再随包发 npm 平台分包。首次运行需要能访问 GitHub Release;断网环境请用 `TRENDING_DIGEST_CORE` 指向本地核心。核心版本由 `package.json` 的 `dsh.core.version` 固定(`sync-contract --ref vX.Y.Z` 会同步写入)。
122
- 6. **核心尚未打 tag**:`trendradar` 仓库还没有 Release,因此当前下载会得到「还没有发布」的明确提示;打上 `v0.1.0` 后无需改插件。
138
+ 5. **二进制改为下载**(2026-10-30 决策 B):不再随包发 npm 平台分包。首次运行需要能访问 GitHub Release;断网环境请用 `TRENDING_DIGEST_CORE` 指向本地核心。核心版本由 `package.json` 的 `dsh.core.version` 固定(用 `node scripts/set-core-version.mjs <x.y.z>` 写入)。
139
+ 6. **核心版本固定**:`package.json` 的 `dsh.core.version` 是首次运行下载哪个核心 Release 的唯一来源(用 `node scripts/set-core-version.mjs <x.y.z>` 写入);下载失败会给出下载地址与错误码,不会退回旧内容。
140
+ 7. **预览副本是派生数据**:不是第二份真相;仓库里的 canonical 文件才是。旧记录没有 `preview` 元数据,日报里只能看到对应的空状态,不承诺回填。
123
141
 
124
142
  ## 开发与发版
125
143
 
@@ -131,7 +149,7 @@ node scripts/ac/run.mjs # AC-P-01…13 的机械部分
131
149
 
132
150
  发版走 **tag 触发的 CI**(Trusted Publishing / OIDC,不需要 npm token):合并 PR → 在 `main` 打注记 tag `vX.Y.Z` → CI 发 npm 并用注记正文建 Release。细节见 [docs/publishing.md](docs/publishing.md)。
133
151
 
134
- - 契约快照:`contract/`(schema + fixtures + `UPSTREAM.json`),由 `node scripts/sync-contract.mjs --ref <sha|vX.Y.Z>` 从核心仓库同步;ref 是 tag 时会同时把版本写进 `package.json` 的 `dsh.core.version`;`npm run check:contract` 校验哈希与生成类型
152
+ - 契约来源:类型与校验器来自 npm 包 `@morehao/trendradar-contract`(`dependencies` 精确锁定;wire `ipcSchema=2` / `digestSchema=2`);本地不再保留 `contract/` 快照与生成器;`npm run check:contract` 校验包版本与 wire 版本
135
153
  - 核心下载与缓存:`src/core/release.ts`(`ensureCore`),缓存位置 `$DSH_HOME/github-trending/core/v<version>/`
136
154
  - 宿主半 `src/`(tsup → `lib/`),客户端半 `client/`(esbuild → `client.js`,`window.__ModuleLoader__.load`)
137
155