@morehao/dsh-github-trending 0.1.0 → 0.1.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/CHANGELOG.md +35 -0
- package/README.md +16 -8
- package/client.js +38 -6
- package/contract/UPSTREAM.json +2 -2
- package/lib/{chunk-CWB5L7RW.js → chunk-RXYA5WFW.js} +326 -100
- package/lib/index.js +1 -1
- package/lib/internal.d.ts +147 -20
- package/lib/internal.js +3 -1
- package/package.json +7 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
> 0.1.1 从未发布到 npm(tag 推上去后 CI 在 `npm ci` 就失败),0.1.2 是它的第一次真实发布。
|
|
6
|
+
|
|
7
|
+
### 修复
|
|
8
|
+
|
|
9
|
+
- lockfile 里 57 个 `resolved` 指向 `registry.npmmirror.com`,npm 11 在 CI 拒绝拉取(`EALLOWREMOTE`);统一为 `registry.npmjs.org`,并加 `scripts/check-lockfile.mjs`(`npm run check` 会跑),把这类只在发版暴露的错误前移到 PR。
|
|
10
|
+
|
|
11
|
+
### 发布链路
|
|
12
|
+
|
|
13
|
+
- 新增 tag 触发的发布:`.github/workflows/release.yml`(OIDC Trusted Publishing,无 NPM_TOKEN)+ `scripts/ci-publish.sh`(版本守卫 / 幂等 / 失败回读后重试)+ `scripts/release-notes.mjs`(只投影注记 tag 正文)+ `docs/publishing.md`。
|
|
14
|
+
- `check.yml` 在 PR/main 上跑 `npm run check` 与 AC 机械检查。
|
|
15
|
+
|
|
16
|
+
## 0.1.1
|
|
17
|
+
|
|
18
|
+
分发方式变更:核心二进制不再走 npm 平台分包,改为 **GitHub Release 下载 + sha256 校验 + 本地缓存**(2026-10-30 决策 B)。
|
|
19
|
+
|
|
20
|
+
### 变更
|
|
21
|
+
|
|
22
|
+
- **获取核心**:`TRENDING_DIGEST_CORE` / 设置里的 `corePath`(覆盖,仅排障)→ 缓存 `$DSH_HOME/github-trending/core/v<version>/` → 按 `os/arch` 下载 `trendradar_<goos>_<goarch>`,用同目录 `checksums.txt` 校验(流式写入 + 原子 rename + 0755),失败不留半个文件;缓存命中后完全离线。
|
|
23
|
+
- **版本来源**:`package.json` 的 `dsh.core.{repo,version}`;`scripts/sync-contract.mjs --ref vX.Y.Z` 会同步写入,保证「契约来源 tag」与「下载的核心版本」是同一个值。
|
|
24
|
+
- **新增配置**:`coreAutoDownload`(默认 `true`);关掉后只报 `CORE_NOT_DOWNLOADED`,由设置页显式下载。
|
|
25
|
+
- **设置页**:显示 Release 版本 / 资产名 / 缓存状态 / 下载 URL,并提供「下载核心 / 重新下载」按钮(`POST /core/download`)。
|
|
26
|
+
- **新增错误码**:`CORE_NOT_DOWNLOADED`(未下载,附下载地址)、`CORE_CHECKSUM_MISMATCH`(校验失败且已丢弃);`CORE_UNAVAILABLE` 现在覆盖 404(版本未发布)、网络失败与平台不在矩阵。
|
|
27
|
+
- **移除**:`optionalDependencies` 里的 6 个 `@morehao/trendradar-core-*`(从未发布,安装时只是 6 次无效探测)。
|
|
28
|
+
|
|
29
|
+
### 修复(实测 v0.1.0 Release 后发现)
|
|
30
|
+
|
|
31
|
+
- Windows 资产名带 `.exe`(goreleaser `formats: [binary]` 保留后缀):`assetName()` 按平台补后缀,避免 Windows 上下载 404。
|
|
32
|
+
|
|
33
|
+
### 测试
|
|
34
|
+
|
|
35
|
+
- 新增 `test/release.test.mjs`(命名映射 / 下载校验 / 缓存复用 / 篡改重下 / 校验失败无残留 / 404 提示 / force)与 `test/core-status.test.mjs`(未下载 / 已缓存 / 覆盖路径三种状态)。
|
|
36
|
+
- AC 脚本 AC-P-11 判据改为「环境变量优先 / 未设走 Release」,全套 71 条测试、11 条 AC 机械检查通过。
|
|
37
|
+
|
|
3
38
|
## 0.1.0
|
|
4
39
|
|
|
5
40
|
首个可用版本:插件侧完整落地,内容仍全部由 `trendradar` 核心产出。
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> GitHub 趋势日报的 **DSH 入口**:每天定时 spawn 本地 [`trendradar`](https://github.com/morehao/trendradar) 核心,抓取 Trending、生成中文简介与当日总结,提交到你自己的仓库并自动合并 PR。
|
|
4
4
|
|
|
5
|
-
**状态:0.1.
|
|
5
|
+
**状态:0.1.1。** 插件侧(配置、调度、核心调用、状态与界面、两个工具)已完成。核心二进制走 **GitHub Release**:首次运行按 `os/arch` 下载、用 `checksums.txt` 校验 sha256、缓存到插件数据目录,之后离线复用。核心仓库打 tag 出资产之前,运行会明确报 `CORE_NOT_DOWNLOADED` / `CORE_UNAVAILABLE` 并给出下载地址——不会写空文件,也不会退回旧内容。
|
|
6
6
|
|
|
7
7
|
## 它做什么
|
|
8
8
|
|
|
@@ -35,11 +35,12 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
|
|
|
35
35
|
- 一个本地 **git 仓库**(提交目标)与可写的远端
|
|
36
36
|
- 模型:provider + 模型 ID + 密钥(或用宿主已配置的 provider;OAuth 类凭据不需要密钥)
|
|
37
37
|
- Git 与 GitHub 凭据(PAT / Device Flow / 本机 `gh`),仅 push 与开 PR 需要
|
|
38
|
-
-
|
|
38
|
+
- 核心二进制:首次运行从 `github.com/morehao/trendradar/releases` 下载(按 `os/arch`,sha256 校验后缓存);之后离线复用。开发/排障时可用 `TRENDING_DIGEST_CORE`(或设置里的「核心路径」)指向一个可执行的 `trendradar`,此时完全不下载
|
|
39
39
|
|
|
40
40
|
**访问哪里**
|
|
41
41
|
|
|
42
42
|
- 本地核心进程(一次性的 `run` 子进程)
|
|
43
|
+
- `github.com/morehao/trendradar/releases`:仅首次运行(或点「下载核心」)时下载二进制与其 `checksums.txt`;之后不再联网
|
|
43
44
|
- 你配置的模型端点(由**核心**发起,插件不代发)
|
|
44
45
|
- `api.github.com`(由**核心**发起 push/PR;插件本身不发 GitHub 请求)
|
|
45
46
|
- 本机 `127.0.0.1` 上的临时回环代理(仅当宿主 provider 的凭据不可导出时,端口随机、一次性 token、运行结束即关闭)
|
|
@@ -72,7 +73,8 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
|
|
|
72
73
|
| `trending.since` / `languages` / `topN` | `daily` / `all,typescript,python,go,rust` / `25` | 实际支持范围由核心决定 |
|
|
73
74
|
| `output.dir` | `trending` | 仓库内相对目录 |
|
|
74
75
|
| `schedule.enabled` / `time` / `timezone` | `true` / `08:30:00` / `Asia/Shanghai` | 插件自己的 60 s tick 驱动 |
|
|
75
|
-
| `corePath` | 空 | 等价于 `TRENDING_DIGEST_CORE
|
|
76
|
+
| `corePath` | 空 | 等价于 `TRENDING_DIGEST_CORE`(仅开发/排障;设了就完全不下载) |
|
|
77
|
+
| `coreAutoDownload` | `true` | 缓存缺失时是否自动下载核心;关掉后只报 `CORE_NOT_DOWNLOADED`,由你在设置页点「下载核心」 |
|
|
76
78
|
|
|
77
79
|
## 两个工具
|
|
78
80
|
|
|
@@ -87,7 +89,9 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
|
|
|
87
89
|
|
|
88
90
|
| 码 | 含义 | 下一步 |
|
|
89
91
|
|---|---|---|
|
|
90
|
-
| `
|
|
92
|
+
| `CORE_NOT_DOWNLOADED` | 核心还没下载(缓存为空,且自动下载被关或尚未触发) | 设置页点「下载核心」,或指向可执行的 `trendradar` |
|
|
93
|
+
| `CORE_CHECKSUM_MISMATCH` | 下载内容的 sha256 与 `checksums.txt` 不符(已被丢弃) | 重试;仍失败则手动下载放到缓存目录(代理可能改写内容) |
|
|
94
|
+
| `CORE_UNAVAILABLE` | 下载失败 / 版本还没发布(404)/ 平台不在矩阵 / 覆盖路径无效 | 按提示里的 URL 与发布顺序处理 |
|
|
91
95
|
| `CORE_INCOMPATIBLE` | 核心 `ipcSchema`/`digestSchema` 与插件不一致 | 升级插件(含平台包)到同一版本 |
|
|
92
96
|
| `CORE_TIMEOUT` / `CORE_CANCELLED` | 超时 / 用户取消 | 不提交任何状态,也留不下半成品 |
|
|
93
97
|
| `IPC_INVALID` | 核心 stdout 不是约定的结果信封 | 升级插件与核心 |
|
|
@@ -105,15 +109,16 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
|
|
|
105
109
|
- 首次可在设置页「从本机 CLI 导入」一次性导入非敏感字段;**密钥不导入**
|
|
106
110
|
- 别让两套定时同时开着:内容侧靠 `contentHash` 与 PR 复用兜底,模型消耗无法兜底
|
|
107
111
|
|
|
108
|
-
## 已知偏差(0.1.
|
|
112
|
+
## 已知偏差(0.1.1)
|
|
109
113
|
|
|
110
114
|
1. **核心尚无 stdin 信封**:核心当前的 `run` 只接受 `--config <path> --json`,插件据此传配置;`ok/startedAt/error.code` 形状的 `RunResult` 已兼容,核心补上后插件无需改。
|
|
111
115
|
2. **密钥传输**:POSIX 上配置经 **FIFO** 交给核心(文件系统里不出现明文);Windows 无 FIFO,退化为 0600 临时文件并在读完后立即删除。
|
|
112
116
|
3. **GitHub token** 经子进程环境变量传给核心(核心只认 `GITHUB_TOKEN`/`GH_TOKEN`);不进 argv、不进配置、不进日志。
|
|
113
117
|
4. **调度**用插件自己的 60 s tick(宿主 `schedule` 是会话本地提醒,覆盖不到无人值守场景)。
|
|
114
|
-
5.
|
|
118
|
+
5. **二进制改为下载**(2026-10-30 决策 B):不再随包发 npm 平台分包。首次运行需要能访问 GitHub Release;断网环境请用 `TRENDING_DIGEST_CORE` 指向本地核心。核心版本由 `package.json` 的 `dsh.core.version` 固定(`sync-contract --ref vX.Y.Z` 会同步写入)。
|
|
119
|
+
6. **核心尚未打 tag**:`trendradar` 仓库还没有 Release,因此当前下载会得到「还没有发布」的明确提示;打上 `v0.1.0` 后无需改插件。
|
|
115
120
|
|
|
116
|
-
##
|
|
121
|
+
## 开发与发版
|
|
117
122
|
|
|
118
123
|
```bash
|
|
119
124
|
npm install
|
|
@@ -121,7 +126,10 @@ npm run check # typecheck + 契约校验 + 构建 + 全部测试
|
|
|
121
126
|
node scripts/ac/run.mjs # AC-P-01…13 的机械部分
|
|
122
127
|
```
|
|
123
128
|
|
|
124
|
-
|
|
129
|
+
发版走 **tag 触发的 CI**(Trusted Publishing / OIDC,不需要 npm token):合并 PR → 在 `main` 打注记 tag `vX.Y.Z` → CI 发 npm 并用注记正文建 Release。细节见 [docs/publishing.md](docs/publishing.md)。
|
|
130
|
+
|
|
131
|
+
- 契约快照:`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` 校验哈希与生成类型
|
|
132
|
+
- 核心下载与缓存:`src/core/release.ts`(`ensureCore`),缓存位置 `$DSH_HOME/github-trending/core/v<version>/`
|
|
125
133
|
- 宿主半 `src/`(tsup → `lib/`),客户端半 `client/`(esbuild → `client.js`,`window.__ModuleLoader__.load`)
|
|
126
134
|
|
|
127
135
|
## 许可
|
package/client.js
CHANGED
|
@@ -61,10 +61,15 @@ var DICT = {
|
|
|
61
61
|
"core.ready": "可用",
|
|
62
62
|
"core.unavailable": "不可用",
|
|
63
63
|
"core.incompatible": "版本不兼容",
|
|
64
|
-
"core.
|
|
64
|
+
"core.cached": "已缓存",
|
|
65
|
+
"core.notDownloaded": "未下载",
|
|
66
|
+
"core.download": "下载核心",
|
|
67
|
+
"core.downloading": "下载中…",
|
|
68
|
+
"core.redownload": "重新下载",
|
|
69
|
+
"core.downloaded": "核心已下载并校验通过",
|
|
65
70
|
"core.installed": "已安装",
|
|
66
71
|
"core.missing": "未安装",
|
|
67
|
-
"core.order": "解析顺序:TRENDING_DIGEST_CORE(仅开发/排障)→
|
|
72
|
+
"core.order": "解析顺序:TRENDING_DIGEST_CORE(仅开发/排障)→ Release 资产(缓存命中直接用,否则下载并校验);都不可用时报 CORE_UNAVAILABLE。",
|
|
68
73
|
"coexist.title": "检测到本机另有 CLI",
|
|
69
74
|
"coexist.hint": "插件仍使用自带核心,不切换、不接管它的定时。",
|
|
70
75
|
"card.model.title": "模型",
|
|
@@ -201,10 +206,15 @@ var DICT = {
|
|
|
201
206
|
"core.ready": "ready",
|
|
202
207
|
"core.unavailable": "unavailable",
|
|
203
208
|
"core.incompatible": "incompatible",
|
|
204
|
-
"core.package": "Platform package",
|
|
205
209
|
"core.installed": "installed",
|
|
206
210
|
"core.missing": "missing",
|
|
207
|
-
"core.
|
|
211
|
+
"core.cached": "cached",
|
|
212
|
+
"core.notDownloaded": "not downloaded",
|
|
213
|
+
"core.download": "Download core",
|
|
214
|
+
"core.downloading": "Downloading…",
|
|
215
|
+
"core.redownload": "Re-download",
|
|
216
|
+
"core.downloaded": "Core downloaded and verified",
|
|
217
|
+
"core.order": "Resolution order: TRENDING_DIGEST_CORE (dev/troubleshooting only) → Release asset (cache first, otherwise download and verify); otherwise CORE_UNAVAILABLE.",
|
|
208
218
|
"coexist.title": "Another CLI found on this machine",
|
|
209
219
|
"coexist.hint": "The plugin still uses its bundled core; it never switches or takes over that CLI schedule.",
|
|
210
220
|
"card.model.title": "Model",
|
|
@@ -630,6 +640,7 @@ function SettingsView(props) {
|
|
|
630
640
|
const [preflight, setPreflight] = import_react.default.useState(null);
|
|
631
641
|
const [preflighting, setPreflighting] = import_react.default.useState(false);
|
|
632
642
|
const [rechecking, setRechecking] = import_react.default.useState(false);
|
|
643
|
+
const [downloading, setDownloading] = import_react.default.useState(false);
|
|
633
644
|
const [quickOpen, setQuickOpen] = import_react.default.useState(false);
|
|
634
645
|
import_react.default.useEffect(() => {
|
|
635
646
|
if (state !== null && !dirty) setDraft(state.settings);
|
|
@@ -690,6 +701,20 @@ function SettingsView(props) {
|
|
|
690
701
|
setRechecking(false);
|
|
691
702
|
}
|
|
692
703
|
};
|
|
704
|
+
const downloadCore = async () => {
|
|
705
|
+
setDownloading(true);
|
|
706
|
+
setProblems([]);
|
|
707
|
+
setMessage(null);
|
|
708
|
+
try {
|
|
709
|
+
await apiCall("POST", "/core/download", {});
|
|
710
|
+
setMessage(t("core.downloaded"));
|
|
711
|
+
await store.refresh({ quiet: true });
|
|
712
|
+
} catch (error) {
|
|
713
|
+
setMessage(error instanceof Error ? error.message : String(error));
|
|
714
|
+
} finally {
|
|
715
|
+
setDownloading(false);
|
|
716
|
+
}
|
|
717
|
+
};
|
|
693
718
|
const importCli = async () => {
|
|
694
719
|
try {
|
|
695
720
|
const result = await apiCall(
|
|
@@ -755,12 +780,19 @@ function SettingsView(props) {
|
|
|
755
780
|
{ label: t("core.label") },
|
|
756
781
|
h("span", { className: "dshgt-pill" }, h("span", { className: `dshgt-dot ${coreKind}` }), state.core.version ?? t(`core.${state.core.state}`)),
|
|
757
782
|
state.core.ipcSchema === void 0 ? null : h("span", { className: "dshgt-hint dshgt-mono" }, `ipc ${state.core.ipcSchema} · digest ${state.core.digestSchema ?? "?"}`),
|
|
783
|
+
state.core.release === void 0 ? null : h("span", { className: "dshgt-hint dshgt-mono" }, `${state.core.release.tag} · ${state.core.release.asset}`),
|
|
758
784
|
h(
|
|
759
785
|
"span",
|
|
760
|
-
{ className: "dshgt-hint
|
|
761
|
-
state.core.
|
|
786
|
+
{ className: "dshgt-hint" },
|
|
787
|
+
state.core.download.state === "override" ? `覆盖路径:${state.core.override?.path ?? ""}` : state.core.release?.cached === true ? t("core.cached") : t("core.notDownloaded")
|
|
788
|
+
),
|
|
789
|
+
state.core.download.state === "override" ? null : h(
|
|
790
|
+
Btn,
|
|
791
|
+
{ small: true, onClick: () => void downloadCore(), disabled: downloading },
|
|
792
|
+
downloading ? t("core.downloading") : state.core.release?.cached === true ? t("core.redownload") : t("core.download")
|
|
762
793
|
)
|
|
763
794
|
),
|
|
795
|
+
state.core.release === void 0 ? null : h("p", { className: "dshgt-hint dshgt-mono", style: { fontSize: 11 } }, `${state.core.release.url} → ${state.core.release.binaryPath}`),
|
|
764
796
|
state.core.state === "ready" ? null : h(Alert, { kind: "err", title: t("core.label") }, h("div", null, state.core.message ?? ""), h("div", { className: "dshgt-hint" }, t("core.order"))),
|
|
765
797
|
h("p", { className: "dshgt-hint" }, t("core.order")),
|
|
766
798
|
state.coexistence.found ? h(Alert, { kind: "warn", title: t("coexist.title") }, h("div", { className: "dshgt-mono", style: { fontSize: 12 } }, `${state.coexistence.path ?? ""} ${state.coexistence.version ?? ""}`), h("div", null, state.coexistence.message ?? ""), h("div", { className: "dshgt-hint" }, t("coexist.hint"))) : null,
|
package/contract/UPSTREAM.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"repo": "https://github.com/morehao/trendradar.git",
|
|
3
|
-
"ref": "
|
|
3
|
+
"ref": "v0.1.0",
|
|
4
4
|
"commit": "d3bf720a869732e7a2ee04fa39d32dd50868fd61",
|
|
5
5
|
"aggregate": "eb7f1ac9e117a709d15d87de89cf30e309c479e62dfa3d320cb8f154d3f1dc1e",
|
|
6
|
-
"syncedAt": "2026-10-
|
|
6
|
+
"syncedAt": "2026-10-30T14:06:16.766Z",
|
|
7
7
|
"files": [
|
|
8
8
|
{
|
|
9
9
|
"path": "schema/api.v1.yaml",
|
|
@@ -48,7 +48,8 @@ var DEFAULT_SETTINGS = {
|
|
|
48
48
|
trending: { since: "daily", languages: ["all", "typescript", "python", "go", "rust"], topN: 25 },
|
|
49
49
|
output: { dir: "trending" },
|
|
50
50
|
schedule: { enabled: true, time: "08:30:00", timezone: "Asia/Shanghai" },
|
|
51
|
-
corePath: ""
|
|
51
|
+
corePath: "",
|
|
52
|
+
coreAutoDownload: true
|
|
52
53
|
};
|
|
53
54
|
var PROVIDER_BASE_URLS = {
|
|
54
55
|
deepseek: "https://api.deepseek.com/v1",
|
|
@@ -125,7 +126,8 @@ function normalizeSettings(input) {
|
|
|
125
126
|
time: str(schedule.time, DEFAULT_SETTINGS.schedule.time),
|
|
126
127
|
timezone: str(schedule.timezone, DEFAULT_SETTINGS.schedule.timezone)
|
|
127
128
|
},
|
|
128
|
-
corePath: str(raw.corePath, DEFAULT_SETTINGS.corePath).trim()
|
|
129
|
+
corePath: str(raw.corePath, DEFAULT_SETTINGS.corePath).trim(),
|
|
130
|
+
coreAutoDownload: bool(raw.coreAutoDownload, DEFAULT_SETTINGS.coreAutoDownload)
|
|
129
131
|
};
|
|
130
132
|
}
|
|
131
133
|
var TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d))?$/;
|
|
@@ -403,6 +405,8 @@ var PLUGIN_CODES = [
|
|
|
403
405
|
"CORE_TIMEOUT",
|
|
404
406
|
"CORE_CANCELLED",
|
|
405
407
|
"CORE_CONFIG_CHANNEL_FAILED",
|
|
408
|
+
"CORE_NOT_DOWNLOADED",
|
|
409
|
+
"CORE_CHECKSUM_MISMATCH",
|
|
406
410
|
"IPC_INVALID",
|
|
407
411
|
"MODEL_KEY_MISSING",
|
|
408
412
|
"MODEL_NOT_FOUND",
|
|
@@ -435,6 +439,8 @@ var RETRIABLE = {
|
|
|
435
439
|
CORE_TIMEOUT: true,
|
|
436
440
|
CORE_CANCELLED: false,
|
|
437
441
|
CORE_CONFIG_CHANNEL_FAILED: true,
|
|
442
|
+
CORE_NOT_DOWNLOADED: false,
|
|
443
|
+
CORE_CHECKSUM_MISMATCH: true,
|
|
438
444
|
IPC_INVALID: false,
|
|
439
445
|
MODEL_KEY_MISSING: false,
|
|
440
446
|
MODEL_NOT_FOUND: false,
|
|
@@ -464,6 +470,10 @@ function hintOf(code) {
|
|
|
464
470
|
return "\u8FD0\u884C\u5DF2\u53D6\u6D88\uFF1A\u6838\u5FC3\u5DF2\u7EC8\u6B62\uFF0C\u672A\u63D0\u4EA4\u72B6\u6001\uFF0C\u4E5F\u672A\u7559\u534A\u6210\u54C1\u3002";
|
|
465
471
|
case "CORE_CONFIG_CHANNEL_FAILED":
|
|
466
472
|
return "\u914D\u7F6E\u901A\u9053\u5EFA\u7ACB\u5931\u8D25\uFF08FIFO \u4E0D\u53EF\u7528\uFF09\uFF1A\u6539\u7528\u4E34\u65F6\u6587\u4EF6\u6A21\u5F0F\u91CD\u8BD5\uFF0C\u6216\u68C0\u67E5\u76EE\u6807\u6587\u4EF6\u7CFB\u7EDF\u3002";
|
|
473
|
+
case "CORE_NOT_DOWNLOADED":
|
|
474
|
+
return "\u6838\u5FC3\u8FD8\u6CA1\u4E0B\u8F7D\uFF1A\u5728\u8BBE\u7F6E\u9875\u70B9\u300C\u4E0B\u8F7D\u6838\u5FC3\u300D\uFF08\u6216\u7528 TRENDING_DIGEST_CORE \u6307\u5411\u672C\u5730\u6838\u5FC3\uFF09\u3002\u9996\u6B21\u8FD0\u884C\u9700\u8981\u8054\u7F51\u3002";
|
|
475
|
+
case "CORE_CHECKSUM_MISMATCH":
|
|
476
|
+
return "\u6838\u5FC3\u4E8C\u8FDB\u5236\u6821\u9A8C\u5931\u8D25\u4E14\u5DF2\u4E22\u5F03\uFF1A\u7F51\u7EDC\u4EE3\u7406\u53EF\u80FD\u6539\u5199\u4E86\u5185\u5BB9\uFF0C\u91CD\u8BD5\u4E00\u6B21\uFF1B\u4ECD\u5931\u8D25\u8BF7\u624B\u52A8\u4E0B\u8F7D\u540E\u653E\u5230\u7F13\u5B58\u76EE\u5F55\u3002";
|
|
467
477
|
case "IPC_INVALID":
|
|
468
478
|
return "\u6838\u5FC3\u8F93\u51FA\u4E0D\u662F\u7EA6\u5B9A\u7684\u7ED3\u679C\u4FE1\u5C01\uFF1A\u5347\u7EA7\u63D2\u4EF6\u4E0E\u6838\u5FC3\u5230\u540C\u4E00\u7248\u672C\u3002";
|
|
469
479
|
case "MODEL_KEY_MISSING":
|
|
@@ -731,105 +741,289 @@ async function handshake(core, options = {}) {
|
|
|
731
741
|
return { version, corePath: core.path, durationMs: result.durationMs, supportedProviders };
|
|
732
742
|
}
|
|
733
743
|
|
|
734
|
-
// src/core/
|
|
735
|
-
var
|
|
736
|
-
__export(
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
744
|
+
// src/core/release.ts
|
|
745
|
+
var release_exports = {};
|
|
746
|
+
__export(release_exports, {
|
|
747
|
+
assetName: () => assetName,
|
|
748
|
+
describeRelease: () => describeRelease,
|
|
749
|
+
ensureCore: () => ensureCore,
|
|
750
|
+
goTarget: () => goTarget,
|
|
751
|
+
readCoreRelease: () => readCoreRelease,
|
|
752
|
+
verifyCached: () => verifyCached
|
|
741
753
|
});
|
|
754
|
+
import { createHash } from "crypto";
|
|
755
|
+
import { chmodSync, createWriteStream, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, rmSync, statSync, unlinkSync } from "fs";
|
|
742
756
|
import { createRequire } from "module";
|
|
743
|
-
import {
|
|
757
|
+
import { pipeline } from "stream/promises";
|
|
758
|
+
import { Readable } from "stream";
|
|
744
759
|
import path4 from "path";
|
|
745
|
-
var
|
|
746
|
-
function
|
|
747
|
-
|
|
760
|
+
var DOWNLOAD_TIMEOUT_MS = 18e4;
|
|
761
|
+
function requireJson() {
|
|
762
|
+
const require2 = createRequire(import.meta.url);
|
|
763
|
+
return require2("../package.json");
|
|
748
764
|
}
|
|
749
|
-
function
|
|
765
|
+
function readCoreRelease(pkg) {
|
|
766
|
+
const source = pkg ?? requireJson();
|
|
767
|
+
const repo = source.dsh?.core?.repo;
|
|
768
|
+
const version = source.dsh?.core?.version;
|
|
769
|
+
if (typeof repo !== "string" || !/^[\w.-]+\/[\w.-]+$/.test(repo)) {
|
|
770
|
+
throw new IpcError("CORE_UNAVAILABLE", "package.json \u7F3A\u5C11 dsh.core.repo\uFF08owner/repo\uFF09\uFF0C\u65E0\u6CD5\u5B9A\u4F4D\u6838\u5FC3 Release");
|
|
771
|
+
}
|
|
772
|
+
if (typeof version !== "string" || !/^\d+\.\d+\.\d+(?:-[\w.]+)?$/.test(version)) {
|
|
773
|
+
throw new IpcError("CORE_UNAVAILABLE", "package.json \u7F3A\u5C11 dsh.core.version\uFF08\u5F62\u5982 0.1.0\uFF09\uFF0C\u65E0\u6CD5\u5B9A\u4F4D\u6838\u5FC3 Release");
|
|
774
|
+
}
|
|
775
|
+
return { repo, version };
|
|
776
|
+
}
|
|
777
|
+
function goTarget(platform = process.platform, arch = process.arch) {
|
|
778
|
+
const goos = platform === "win32" ? "windows" : platform === "darwin" ? "darwin" : platform === "linux" ? "linux" : platform;
|
|
779
|
+
const goarch = arch === "x64" ? "amd64" : arch === "arm64" ? "arm64" : arch === "ia32" ? "386" : arch;
|
|
780
|
+
return { goos, goarch };
|
|
781
|
+
}
|
|
782
|
+
function assetName(platform = process.platform, arch = process.arch) {
|
|
783
|
+
const { goos, goarch } = goTarget(platform, arch);
|
|
784
|
+
const suffix = platform === "win32" ? ".exe" : "";
|
|
785
|
+
return `trendradar_${goos}_${goarch}${suffix}`;
|
|
786
|
+
}
|
|
787
|
+
function binaryName(platform = process.platform) {
|
|
788
|
+
return platform === "win32" ? "trendradar.exe" : "trendradar";
|
|
789
|
+
}
|
|
790
|
+
function describeRelease(release, options = {}) {
|
|
791
|
+
const env = options.env ?? process.env;
|
|
792
|
+
const platform = options.platform ?? process.platform;
|
|
793
|
+
const arch = options.arch ?? process.arch;
|
|
794
|
+
const { goos, goarch } = goTarget(platform, arch);
|
|
795
|
+
const asset = assetName(platform, arch);
|
|
796
|
+
const tag = `v${release.version}`;
|
|
797
|
+
const base = `https://github.com/${release.repo}/releases/download/${tag}`;
|
|
798
|
+
const cacheDir = path4.join(dataDir(env), "core", tag);
|
|
799
|
+
const binaryPath = path4.join(cacheDir, binaryName(platform));
|
|
800
|
+
return {
|
|
801
|
+
...release,
|
|
802
|
+
tag,
|
|
803
|
+
goos,
|
|
804
|
+
goarch,
|
|
805
|
+
asset,
|
|
806
|
+
url: `${base}/${asset}`,
|
|
807
|
+
checksumsUrl: `${base}/checksums.txt`,
|
|
808
|
+
cacheDir,
|
|
809
|
+
binaryPath,
|
|
810
|
+
cached: existsSync2(binaryPath)
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
function sha256File(file) {
|
|
814
|
+
return createHash("sha256").update(readFileSync2(file)).digest("hex");
|
|
815
|
+
}
|
|
816
|
+
function markerPath(binaryPath) {
|
|
817
|
+
return `${binaryPath}.sha256`;
|
|
818
|
+
}
|
|
819
|
+
function verifyCached(binaryPath) {
|
|
820
|
+
let info;
|
|
750
821
|
try {
|
|
751
|
-
|
|
752
|
-
accessSync(file, constants.X_OK);
|
|
753
|
-
return true;
|
|
822
|
+
info = statSync(binaryPath);
|
|
754
823
|
} catch {
|
|
755
|
-
return false;
|
|
824
|
+
return { ok: false, reason: "missing" };
|
|
825
|
+
}
|
|
826
|
+
if (!info.isFile()) return { ok: false, reason: "not-a-file" };
|
|
827
|
+
if (process.platform !== "win32" && (info.mode & 73) === 0) return { ok: false, reason: "not-executable" };
|
|
828
|
+
const marker = markerPath(binaryPath);
|
|
829
|
+
if (!existsSync2(marker)) return { ok: true };
|
|
830
|
+
const expected = readFileSync2(marker, "utf8").trim();
|
|
831
|
+
if (expected === "") return { ok: true };
|
|
832
|
+
try {
|
|
833
|
+
return sha256File(binaryPath) === expected ? { ok: true } : { ok: false, reason: "checksum-mismatch" };
|
|
834
|
+
} catch {
|
|
835
|
+
return { ok: false, reason: "unreadable" };
|
|
756
836
|
}
|
|
757
837
|
}
|
|
758
|
-
function
|
|
759
|
-
|
|
838
|
+
function fetchOf(options) {
|
|
839
|
+
if (options.fetchImpl !== void 0) return options.fetchImpl;
|
|
840
|
+
if (typeof fetch !== "function") {
|
|
841
|
+
throw new IpcError("CORE_UNAVAILABLE", "\u5F53\u524D Node \u6CA1\u6709\u5168\u5C40 fetch\uFF08\u9700\u8981 Node \u2265 18\uFF09\uFF0C\u65E0\u6CD5\u4E0B\u8F7D\u6838\u5FC3");
|
|
842
|
+
}
|
|
843
|
+
return fetch;
|
|
844
|
+
}
|
|
845
|
+
async function fetchChecksum(descriptor, options) {
|
|
846
|
+
options.onProgress?.({ phase: "checksums" });
|
|
847
|
+
const response = await fetchOf(options)(descriptor.checksumsUrl, { signal: AbortSignal.timeout(3e4) }).catch((error) => {
|
|
848
|
+
throw new IpcError(
|
|
849
|
+
"CORE_UNAVAILABLE",
|
|
850
|
+
`\u65E0\u6CD5\u83B7\u53D6\u6838\u5FC3\u6821\u9A8C\u6587\u4EF6\uFF1A${descriptor.checksumsUrl}\uFF08${error instanceof Error ? error.message : String(error)}\uFF09\u3002\u82E5\u5728\u516C\u53F8\u7F51\u7EDC\u5185\uFF0C\u8BF7\u786E\u8BA4\u4EE3\u7406\u53EF\u7528\uFF1B\u4E5F\u53EF\u7528 TRENDING_DIGEST_CORE \u6307\u5411\u672C\u5730\u5DF2\u7F16\u8BD1\u7684\u6838\u5FC3\u3002`
|
|
851
|
+
);
|
|
852
|
+
});
|
|
853
|
+
if (!response.ok) {
|
|
854
|
+
throw new IpcError(
|
|
855
|
+
"CORE_UNAVAILABLE",
|
|
856
|
+
`\u6838\u5FC3 ${descriptor.tag} \u8FD8\u6CA1\u6709\u53D1\u5E03\uFF08${descriptor.checksumsUrl} \u8FD4\u56DE HTTP ${response.status}\uFF09\u3002\u53D1\u5E03\u987A\u5E8F\u662F\u5148\u5728 trendradar \u6253 tag \u5E76\u4E0A\u4F20 Release \u8D44\u4EA7\uFF0C\u63D2\u4EF6\u968F\u540E\u624D\u80FD\u4E0B\u8F7D\u3002`
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
const text = await response.text();
|
|
860
|
+
for (const line of text.split("\n")) {
|
|
861
|
+
const match = /^([0-9a-f]{64})\s+\*?(.+)$/.exec(line.trim());
|
|
862
|
+
if (match !== null && match[2] === descriptor.asset) return match[1];
|
|
863
|
+
}
|
|
864
|
+
throw new IpcError(
|
|
865
|
+
"CORE_UNAVAILABLE",
|
|
866
|
+
`checksums.txt \u91CC\u6CA1\u6709 ${descriptor.asset}\uFF08\u5F53\u524D\u5E73\u53F0 ${descriptor.goos}/${descriptor.goarch} \u53EF\u80FD\u4E0D\u5728\u53D1\u5E03\u77E9\u9635\u5185\uFF09`
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
async function download(descriptor, options) {
|
|
870
|
+
const expected = await fetchChecksum(descriptor, options);
|
|
871
|
+
options.onProgress?.({ phase: "download" });
|
|
872
|
+
const response = await fetchOf(options)(descriptor.url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) }).catch((error) => {
|
|
873
|
+
throw new IpcError(
|
|
874
|
+
"CORE_UNAVAILABLE",
|
|
875
|
+
`\u4E0B\u8F7D\u6838\u5FC3\u5931\u8D25\uFF1A${descriptor.url}\uFF08${error instanceof Error ? error.message : String(error)}\uFF09\u3002\u53EF\u624B\u52A8\u4E0B\u8F7D\u540E\u653E\u5230 ${descriptor.binaryPath}\uFF0C\u6216\u7528 TRENDING_DIGEST_CORE \u6307\u5B9A\u8DEF\u5F84\u3002`
|
|
876
|
+
);
|
|
877
|
+
});
|
|
878
|
+
if (!response.ok || response.body === null) {
|
|
879
|
+
throw new IpcError(
|
|
880
|
+
"CORE_UNAVAILABLE",
|
|
881
|
+
`\u4E0B\u8F7D\u6838\u5FC3\u5931\u8D25\uFF1A${descriptor.url} \u8FD4\u56DE HTTP ${response.status}\uFF08\u7248\u672C ${descriptor.tag} / ${descriptor.asset}\uFF09`
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
mkdirSync2(descriptor.cacheDir, { recursive: true, mode: 448 });
|
|
885
|
+
const temp = `${descriptor.binaryPath}.${process.pid}.${Date.now()}.part`;
|
|
886
|
+
const hash = createHash("sha256");
|
|
887
|
+
const total = Number(response.headers.get("content-length") ?? "0") || void 0;
|
|
888
|
+
let bytes = 0;
|
|
760
889
|
try {
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
890
|
+
await pipeline(
|
|
891
|
+
Readable.fromWeb(response.body),
|
|
892
|
+
async function* (source) {
|
|
893
|
+
for await (const chunk of source) {
|
|
894
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
895
|
+
hash.update(buf);
|
|
896
|
+
bytes += buf.length;
|
|
897
|
+
yield buf;
|
|
898
|
+
}
|
|
899
|
+
},
|
|
900
|
+
createWriteStream(temp, { mode: 384 })
|
|
901
|
+
);
|
|
902
|
+
const actual = hash.digest("hex");
|
|
903
|
+
options.onProgress?.({ phase: "verify", bytes, total });
|
|
904
|
+
if (actual !== expected) {
|
|
905
|
+
throw new IpcError(
|
|
906
|
+
"CORE_CHECKSUM_MISMATCH",
|
|
907
|
+
`\u6838\u5FC3\u4E8C\u8FDB\u5236\u6821\u9A8C\u5931\u8D25\uFF1A\u671F\u671B ${expected}\uFF0C\u5B9E\u9645 ${actual}\uFF08${descriptor.url}\uFF09\u3002\u5DF2\u4E22\u5F03\u4E0B\u8F7D\u7ED3\u679C\u3002`
|
|
908
|
+
);
|
|
767
909
|
}
|
|
768
|
-
|
|
910
|
+
chmodSync(temp, 493);
|
|
911
|
+
renameSync2(temp, descriptor.binaryPath);
|
|
912
|
+
const fs = await import("fs");
|
|
913
|
+
fs.writeFileSync(markerPath(descriptor.binaryPath), `${actual}
|
|
914
|
+
`, { mode: 384 });
|
|
915
|
+
return { path: descriptor.binaryPath, sha256: actual };
|
|
916
|
+
} catch (error) {
|
|
917
|
+
rmSync(temp, { force: true });
|
|
918
|
+
throw error;
|
|
769
919
|
}
|
|
770
|
-
candidates.push(path4.join(packageDir, "bin", "trendradar"));
|
|
771
|
-
candidates.push(path4.join(packageDir, "trendradar"));
|
|
772
|
-
return candidates;
|
|
773
920
|
}
|
|
774
|
-
function
|
|
775
|
-
const
|
|
776
|
-
const
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
921
|
+
async function ensureCore(options = {}) {
|
|
922
|
+
const release = options.release ?? readCoreRelease();
|
|
923
|
+
const descriptor = describeRelease(release, options);
|
|
924
|
+
if (!options.force && existsSync2(descriptor.binaryPath)) {
|
|
925
|
+
const verified = verifyCached(descriptor.binaryPath);
|
|
926
|
+
if (verified.ok) {
|
|
927
|
+
return { path: descriptor.binaryPath, source: "cache", descriptor };
|
|
928
|
+
}
|
|
929
|
+
if (options.allowDownload === false) {
|
|
782
930
|
throw new IpcError(
|
|
783
931
|
"CORE_UNAVAILABLE",
|
|
784
|
-
|
|
932
|
+
`\u672C\u5730\u6838\u5FC3\u7F13\u5B58\u4E0D\u53EF\u7528\uFF08${verified.reason ?? "unknown"}\uFF09\uFF1A${descriptor.binaryPath}\u3002\u5220\u9664\u8BE5\u6587\u4EF6\u540E\u91CD\u8BD5\uFF0C\u6216\u5728\u8BBE\u7F6E\u9875\u70B9\u300C\u91CD\u65B0\u4E0B\u8F7D\u6838\u5FC3\u300D\u3002`
|
|
785
933
|
);
|
|
786
934
|
}
|
|
787
|
-
|
|
788
|
-
|
|
935
|
+
try {
|
|
936
|
+
unlinkSync(descriptor.binaryPath);
|
|
937
|
+
} catch {
|
|
789
938
|
}
|
|
790
|
-
return {
|
|
791
|
-
path: resolved,
|
|
792
|
-
source: options.settings.corePath !== "" ? "settings" : "env",
|
|
793
|
-
searched
|
|
794
|
-
};
|
|
795
939
|
}
|
|
796
|
-
|
|
797
|
-
const arch = options.arch ?? process.arch;
|
|
798
|
-
const packageName = platformPackageName(platform, arch);
|
|
799
|
-
const resolve = options.resolve ?? createRequire(import.meta.url);
|
|
800
|
-
try {
|
|
801
|
-
const packageJson = resolve(`${packageName}/package.json`);
|
|
802
|
-
const packageDir = path4.dirname(packageJson);
|
|
803
|
-
for (const candidate of binCandidates(packageJson, packageDir)) {
|
|
804
|
-
searched.push(candidate);
|
|
805
|
-
if (executable(candidate)) {
|
|
806
|
-
return { path: candidate, source: "package", packageName, searched };
|
|
807
|
-
}
|
|
808
|
-
}
|
|
940
|
+
if (options.allowDownload === false) {
|
|
809
941
|
throw new IpcError(
|
|
810
|
-
"
|
|
811
|
-
`\
|
|
812
|
-
);
|
|
813
|
-
} catch (error) {
|
|
814
|
-
if (error instanceof IpcError) throw error;
|
|
815
|
-
throw new IpcError(
|
|
816
|
-
"CORE_UNAVAILABLE",
|
|
817
|
-
`\u627E\u4E0D\u5230\u6838\u5FC3\u4E8C\u8FDB\u5236\uFF1A\u672A\u8BBE\u7F6E TRENDING_DIGEST_CORE\uFF0C\u5E73\u53F0\u5305 ${packageName} \u4E5F\u672A\u5B89\u88C5\u3002\u8BF7\u91CD\u88C5\u63D2\u4EF6\uFF08\u4F1A\u5E26\u4E0A\u5BF9\u5E94\u5E73\u53F0\u7684\u5305\uFF09\uFF1B\u652F\u6301\u7684\u5E73\u53F0\uFF1Adarwin/linux/win32 \xD7 x64/arm64\u3002`
|
|
942
|
+
"CORE_NOT_DOWNLOADED",
|
|
943
|
+
`\u6838\u5FC3 ${descriptor.tag} \u5C1A\u672A\u4E0B\u8F7D\uFF08\u7F13\u5B58\u76EE\u5F55 ${descriptor.cacheDir}\uFF09\u3002\u4E0B\u8F7D\u5730\u5740\uFF1A${descriptor.url}\uFF1B\u6216\u5728\u8BBE\u7F6E\u9875\u70B9\u300C\u4E0B\u8F7D\u6838\u5FC3\u300D\uFF0C\u6216\u8BBE\u7F6E TRENDING_DIGEST_CORE \u6307\u5411\u672C\u5730\u6838\u5FC3\u3002`
|
|
818
944
|
);
|
|
819
945
|
}
|
|
946
|
+
const downloaded = await download(descriptor, options);
|
|
947
|
+
return { path: downloaded.path, source: "download", descriptor, sha256: downloaded.sha256 };
|
|
820
948
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
949
|
+
|
|
950
|
+
// src/core/locate.ts
|
|
951
|
+
var locate_exports = {};
|
|
952
|
+
__export(locate_exports, {
|
|
953
|
+
inspectRelease: () => inspectRelease,
|
|
954
|
+
locateCore: () => locateCore,
|
|
955
|
+
overridePath: () => overridePath
|
|
956
|
+
});
|
|
957
|
+
import { accessSync, constants, existsSync as existsSync3, statSync as statSync2 } from "fs";
|
|
958
|
+
import path5 from "path";
|
|
959
|
+
function executable(file) {
|
|
826
960
|
try {
|
|
827
|
-
|
|
961
|
+
if (!statSync2(file).isFile()) return false;
|
|
962
|
+
accessSync(file, constants.X_OK);
|
|
828
963
|
return true;
|
|
829
964
|
} catch {
|
|
830
965
|
return false;
|
|
831
966
|
}
|
|
832
967
|
}
|
|
968
|
+
function overridePath(settings, env = process.env) {
|
|
969
|
+
const fromSettings = settings.corePath.trim();
|
|
970
|
+
if (fromSettings !== "") {
|
|
971
|
+
return { path: path5.isAbsolute(fromSettings) ? fromSettings : path5.resolve(fromSettings), source: "settings" };
|
|
972
|
+
}
|
|
973
|
+
const fromEnv2 = env.TRENDING_DIGEST_CORE?.trim() ?? "";
|
|
974
|
+
if (fromEnv2 !== "") return { path: path5.isAbsolute(fromEnv2) ? fromEnv2 : path5.resolve(fromEnv2), source: "env" };
|
|
975
|
+
return void 0;
|
|
976
|
+
}
|
|
977
|
+
async function locateCore(options) {
|
|
978
|
+
const env = options.env ?? process.env;
|
|
979
|
+
const override = overridePath(options.settings, env);
|
|
980
|
+
if (override !== void 0) {
|
|
981
|
+
if (!existsSync3(override.path)) {
|
|
982
|
+
throw new IpcError(
|
|
983
|
+
"CORE_UNAVAILABLE",
|
|
984
|
+
`${override.source === "settings" ? "\u8BBE\u7F6E\u91CC\u7684 corePath" : "TRENDING_DIGEST_CORE"} \u6307\u5411\u7684\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF1A${override.path}\u3002\u8BE5\u8986\u76D6\u53EA\u7528\u4E8E\u5F00\u53D1/\u6392\u969C\uFF0C\u6B63\u5E38\u4F7F\u7528\u8BF7\u6E05\u7A7A\u5B83\u3002`
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
if (!executable(override.path)) {
|
|
988
|
+
throw new IpcError("CORE_UNAVAILABLE", `${override.path} \u4E0D\u53EF\u6267\u884C\uFF08\u7F3A\u6267\u884C\u4F4D\u6216\u4E0D\u662F\u6587\u4EF6\uFF09`);
|
|
989
|
+
}
|
|
990
|
+
return { path: override.path, source: override.source };
|
|
991
|
+
}
|
|
992
|
+
const result = await ensureCore({
|
|
993
|
+
...options.release === void 0 ? {} : { release: options.release },
|
|
994
|
+
env,
|
|
995
|
+
...options.platform === void 0 ? {} : { platform: options.platform },
|
|
996
|
+
...options.arch === void 0 ? {} : { arch: options.arch },
|
|
997
|
+
...options.allowDownload === void 0 ? {} : { allowDownload: options.allowDownload },
|
|
998
|
+
...options.forceDownload === void 0 ? {} : { force: options.forceDownload },
|
|
999
|
+
...options.fetchImpl === void 0 ? {} : { fetchImpl: options.fetchImpl }
|
|
1000
|
+
});
|
|
1001
|
+
return {
|
|
1002
|
+
path: result.path,
|
|
1003
|
+
source: result.source,
|
|
1004
|
+
release: { repo: result.descriptor.repo, version: result.descriptor.version },
|
|
1005
|
+
tag: result.descriptor.tag,
|
|
1006
|
+
downloaded: result.source === "download",
|
|
1007
|
+
...result.sha256 === void 0 ? {} : { sha256: result.sha256 }
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
function inspectRelease(settings, options = {}) {
|
|
1011
|
+
const env = options.env ?? process.env;
|
|
1012
|
+
const override = overridePath(settings, env);
|
|
1013
|
+
if (override !== void 0) return { override };
|
|
1014
|
+
try {
|
|
1015
|
+
const release = options.release ?? readCoreRelease();
|
|
1016
|
+
return {
|
|
1017
|
+
descriptor: describeRelease(release, {
|
|
1018
|
+
env,
|
|
1019
|
+
...options.platform === void 0 ? {} : { platform: options.platform },
|
|
1020
|
+
...options.arch === void 0 ? {} : { arch: options.arch }
|
|
1021
|
+
})
|
|
1022
|
+
};
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
833
1027
|
|
|
834
1028
|
// src/core/channel.ts
|
|
835
1029
|
var channel_exports = {};
|
|
@@ -843,10 +1037,10 @@ import { constants as constants2 } from "fs";
|
|
|
843
1037
|
import { open, rm, stat, unlink, writeFile } from "fs/promises";
|
|
844
1038
|
import { mkdtempSync } from "fs";
|
|
845
1039
|
import os2 from "os";
|
|
846
|
-
import
|
|
1040
|
+
import path6 from "path";
|
|
847
1041
|
var WRITE_TIMEOUT_MS = 15e3;
|
|
848
1042
|
function tempDir() {
|
|
849
|
-
return mkdtempSync(
|
|
1043
|
+
return mkdtempSync(path6.join(os2.tmpdir(), "dsh-trending-"));
|
|
850
1044
|
}
|
|
851
1045
|
async function mkfifo(file) {
|
|
852
1046
|
await new Promise((resolve, reject) => {
|
|
@@ -864,7 +1058,7 @@ async function mkfifo(file) {
|
|
|
864
1058
|
});
|
|
865
1059
|
}
|
|
866
1060
|
async function createFifoChannel(dir, writeTimeoutMs) {
|
|
867
|
-
const file =
|
|
1061
|
+
const file = path6.join(dir, "core.yml");
|
|
868
1062
|
await mkfifo(file);
|
|
869
1063
|
let disposed = false;
|
|
870
1064
|
return {
|
|
@@ -905,7 +1099,7 @@ async function createFifoChannel(dir, writeTimeoutMs) {
|
|
|
905
1099
|
};
|
|
906
1100
|
}
|
|
907
1101
|
async function createTmpfileChannel(dir) {
|
|
908
|
-
const file =
|
|
1102
|
+
const file = path6.join(dir, "core.yml");
|
|
909
1103
|
let disposed = false;
|
|
910
1104
|
return {
|
|
911
1105
|
kind: "tmpfile",
|
|
@@ -938,7 +1132,7 @@ async function removeChannelDir(channel) {
|
|
|
938
1132
|
if (info.isFIFO()) return;
|
|
939
1133
|
} catch {
|
|
940
1134
|
}
|
|
941
|
-
await rm(
|
|
1135
|
+
await rm(path6.dirname(channel.path), { recursive: true, force: true }).catch(() => void 0);
|
|
942
1136
|
}
|
|
943
1137
|
|
|
944
1138
|
// src/core/run.ts
|
|
@@ -1111,12 +1305,12 @@ __export(core_exports, {
|
|
|
1111
1305
|
findOnPath: () => findOnPath,
|
|
1112
1306
|
inspectCore: () => inspectCore
|
|
1113
1307
|
});
|
|
1114
|
-
import { accessSync as accessSync2, constants as constants3, statSync as
|
|
1115
|
-
import
|
|
1308
|
+
import { accessSync as accessSync2, constants as constants3, statSync as statSync3 } from "fs";
|
|
1309
|
+
import path7 from "path";
|
|
1116
1310
|
var CACHE_TTL_MS = 3e4;
|
|
1117
1311
|
function executable2(file) {
|
|
1118
1312
|
try {
|
|
1119
|
-
if (!
|
|
1313
|
+
if (!statSync3(file).isFile()) return false;
|
|
1120
1314
|
accessSync2(file, constants3.X_OK);
|
|
1121
1315
|
return true;
|
|
1122
1316
|
} catch {
|
|
@@ -1126,10 +1320,10 @@ function executable2(file) {
|
|
|
1126
1320
|
function findOnPath(command, env = process.env) {
|
|
1127
1321
|
const raw = env.PATH ?? "";
|
|
1128
1322
|
const exts = process.platform === "win32" ? [".exe", ".cmd", ".bat", ""] : [""];
|
|
1129
|
-
for (const dir of raw.split(
|
|
1323
|
+
for (const dir of raw.split(path7.delimiter)) {
|
|
1130
1324
|
if (dir.trim() === "") continue;
|
|
1131
1325
|
for (const ext of exts) {
|
|
1132
|
-
const candidate =
|
|
1326
|
+
const candidate = path7.join(dir, `${command}${ext}`);
|
|
1133
1327
|
if (executable2(candidate)) return candidate;
|
|
1134
1328
|
}
|
|
1135
1329
|
}
|
|
@@ -1142,14 +1336,25 @@ async function inspectCore(settings, options = {}) {
|
|
|
1142
1336
|
if (!options.fresh && cached !== void 0 && cached.key === key && Date.now() - cached.at < CACHE_TTL_MS) {
|
|
1143
1337
|
return cached.status;
|
|
1144
1338
|
}
|
|
1145
|
-
const
|
|
1339
|
+
const view = inspectRelease(settings, { env });
|
|
1340
|
+
const release = view.descriptor === void 0 ? void 0 : {
|
|
1341
|
+
repo: view.descriptor.repo,
|
|
1342
|
+
tag: view.descriptor.tag,
|
|
1343
|
+
asset: view.descriptor.asset,
|
|
1344
|
+
url: view.descriptor.url,
|
|
1345
|
+
checksumsUrl: view.descriptor.checksumsUrl,
|
|
1346
|
+
cacheDir: view.descriptor.cacheDir,
|
|
1347
|
+
binaryPath: view.descriptor.binaryPath,
|
|
1348
|
+
cached: view.descriptor.cached
|
|
1349
|
+
};
|
|
1146
1350
|
const base = {
|
|
1147
|
-
|
|
1148
|
-
|
|
1351
|
+
...release === void 0 ? {} : { release },
|
|
1352
|
+
...view.override === void 0 ? {} : { override: view.override },
|
|
1353
|
+
download: view.override !== void 0 ? { state: "override" } : release === void 0 ? { state: "missing", reason: view.error ?? "release \u672A\u914D\u7F6E" } : { state: release.cached ? "cached" : "missing" }
|
|
1149
1354
|
};
|
|
1150
1355
|
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1151
1356
|
try {
|
|
1152
|
-
const core = locateCore({ settings, env });
|
|
1357
|
+
const core = await locateCore({ settings, env, allowDownload: false });
|
|
1153
1358
|
const result = await handshake(core, { timeoutMs: 5e3 });
|
|
1154
1359
|
const status = {
|
|
1155
1360
|
...base,
|
|
@@ -1186,7 +1391,7 @@ async function detectCoexistence(ownPath, options = {}) {
|
|
|
1186
1391
|
const env = options.env ?? process.env;
|
|
1187
1392
|
const found = findOnPath("trendradar", env);
|
|
1188
1393
|
if (found === void 0) return { found: false };
|
|
1189
|
-
if (ownPath !== void 0 &&
|
|
1394
|
+
if (ownPath !== void 0 && path7.resolve(found) === path7.resolve(ownPath)) return { found: false };
|
|
1190
1395
|
if (!options.fresh && coexistCache !== void 0 && coexistCache.key === found && Date.now() - coexistCache.at < CACHE_TTL_MS) {
|
|
1191
1396
|
return coexistCache.status;
|
|
1192
1397
|
}
|
|
@@ -1563,17 +1768,17 @@ var runner_exports = {};
|
|
|
1563
1768
|
__export(runner_exports, {
|
|
1564
1769
|
Runner: () => Runner
|
|
1565
1770
|
});
|
|
1566
|
-
import { existsSync as
|
|
1567
|
-
import
|
|
1771
|
+
import { existsSync as existsSync4, statSync as statSync4 } from "fs";
|
|
1772
|
+
import path8 from "path";
|
|
1568
1773
|
function repoProblem(repoPath) {
|
|
1569
1774
|
if (repoPath.trim() === "") return { code: "CONFIG_INVALID", message: "\u8FD8\u6CA1\u6709\u9009\u62E9\u5F52\u6863\u4ED3\u5E93" };
|
|
1570
|
-
if (!
|
|
1775
|
+
if (!existsSync4(repoPath)) return { code: "REPO_NOT_GIT", message: `\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${repoPath}` };
|
|
1571
1776
|
try {
|
|
1572
|
-
if (!
|
|
1777
|
+
if (!statSync4(repoPath).isDirectory()) return { code: "REPO_NOT_GIT", message: `\u4E0D\u662F\u76EE\u5F55\uFF1A${repoPath}` };
|
|
1573
1778
|
} catch {
|
|
1574
1779
|
return { code: "REPO_NOT_GIT", message: `\u65E0\u6CD5\u8BBF\u95EE\uFF1A${repoPath}` };
|
|
1575
1780
|
}
|
|
1576
|
-
if (!
|
|
1781
|
+
if (!existsSync4(path8.join(repoPath, ".git"))) {
|
|
1577
1782
|
return { code: "REPO_NOT_GIT", message: `\u4E0D\u662F git \u4ED3\u5E93\uFF1A${repoPath}\uFF08\u63D2\u4EF6\u4E0D\u4F1A\u66FF\u4F60 git init\uFF09` };
|
|
1578
1783
|
}
|
|
1579
1784
|
return void 0;
|
|
@@ -1616,7 +1821,10 @@ var Runner = class {
|
|
|
1616
1821
|
const startedMs = Date.now();
|
|
1617
1822
|
let proxy;
|
|
1618
1823
|
try {
|
|
1619
|
-
const core = locateCore({ settings, env });
|
|
1824
|
+
const core = await locateCore({ settings, env, allowDownload: settings.coreAutoDownload });
|
|
1825
|
+
if (core.downloaded === true) {
|
|
1826
|
+
store.appendLog(logFile, `[core] \u5DF2\u4E0B\u8F7D\u6838\u5FC3 ${core.tag ?? ""} \u2192 ${core.path}`);
|
|
1827
|
+
}
|
|
1620
1828
|
const handshakeResult = await handshake(core, { signal: controller.signal });
|
|
1621
1829
|
if (!handshakeResult.supportedProviders.includes(settings.model.provider)) {
|
|
1622
1830
|
throw new IpcError(
|
|
@@ -1666,7 +1874,17 @@ var Runner = class {
|
|
|
1666
1874
|
onStderr: (line) => store.appendLog(logFile, line)
|
|
1667
1875
|
});
|
|
1668
1876
|
const outcome = result.outcome;
|
|
1669
|
-
const summary = this.summarize({
|
|
1877
|
+
const summary = this.summarize({
|
|
1878
|
+
id,
|
|
1879
|
+
outcome,
|
|
1880
|
+
dryRun,
|
|
1881
|
+
...options.date === void 0 ? {} : { date: options.date },
|
|
1882
|
+
logFile,
|
|
1883
|
+
channel: result.channelKind,
|
|
1884
|
+
proxy: proxy !== void 0,
|
|
1885
|
+
coreSource: core.source,
|
|
1886
|
+
...core.tag === void 0 ? {} : { coreTag: core.tag }
|
|
1887
|
+
});
|
|
1670
1888
|
this.record(summary, settings);
|
|
1671
1889
|
return summary;
|
|
1672
1890
|
} catch (error) {
|
|
@@ -1716,7 +1934,9 @@ var Runner = class {
|
|
|
1716
1934
|
warnings: outcome.warnings,
|
|
1717
1935
|
logFile: input.logFile,
|
|
1718
1936
|
...input.channel === void 0 ? {} : { channel: input.channel },
|
|
1719
|
-
proxy: input.proxy
|
|
1937
|
+
proxy: input.proxy,
|
|
1938
|
+
...input.coreSource === void 0 ? {} : { coreSource: input.coreSource },
|
|
1939
|
+
...input.coreTag === void 0 ? {} : { coreTag: input.coreTag }
|
|
1720
1940
|
};
|
|
1721
1941
|
}
|
|
1722
1942
|
failureSummary(input) {
|
|
@@ -1957,14 +2177,14 @@ __export(import_cli_exports, {
|
|
|
1957
2177
|
});
|
|
1958
2178
|
import { readFileSync as readFileSync3 } from "fs";
|
|
1959
2179
|
import os3 from "os";
|
|
1960
|
-
import
|
|
2180
|
+
import path9 from "path";
|
|
1961
2181
|
function cliConfigCandidates(env = process.env, home = os3.homedir()) {
|
|
1962
2182
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
1963
|
-
const base = xdg !== void 0 && xdg !== "" ?
|
|
2183
|
+
const base = xdg !== void 0 && xdg !== "" ? path9.resolve(xdg) : path9.join(home, ".config");
|
|
1964
2184
|
return [
|
|
1965
2185
|
env.TRENDradar_CONFIG?.trim() ?? "",
|
|
1966
|
-
|
|
1967
|
-
|
|
2186
|
+
path9.join(base, "trendradar", "config.yaml"),
|
|
2187
|
+
path9.join(home, ".trendradar", "config.yaml")
|
|
1968
2188
|
].filter((item) => item !== "");
|
|
1969
2189
|
}
|
|
1970
2190
|
function unquote(raw) {
|
|
@@ -2376,6 +2596,11 @@ async function dispatch(method, pathname, url, request, deps) {
|
|
|
2376
2596
|
const coexistence = await detectCoexistence(core.path, { env });
|
|
2377
2597
|
return { core, coexistence };
|
|
2378
2598
|
}
|
|
2599
|
+
if (method === "POST" && pathname === "/core/download") {
|
|
2600
|
+
const core = await locateCore({ settings, env, forceDownload: true });
|
|
2601
|
+
const status = await inspectCore(settings, { env, fresh: true });
|
|
2602
|
+
return { downloaded: true, path: core.path, tag: core.tag ?? null, core: status };
|
|
2603
|
+
}
|
|
2379
2604
|
if (method === "POST" && pathname === "/config") {
|
|
2380
2605
|
const body = await readBody2(request);
|
|
2381
2606
|
const next = normalizeSettings(body.settings ?? body);
|
|
@@ -2661,6 +2886,7 @@ export {
|
|
|
2661
2886
|
errors_exports,
|
|
2662
2887
|
ipc_exports,
|
|
2663
2888
|
handshake_exports,
|
|
2889
|
+
release_exports,
|
|
2664
2890
|
locate_exports,
|
|
2665
2891
|
channel_exports,
|
|
2666
2892
|
run_exports,
|
package/lib/index.js
CHANGED
package/lib/internal.d.ts
CHANGED
|
@@ -64,7 +64,7 @@ declare const IPC_SCHEMA_VERSION: 1;
|
|
|
64
64
|
*/
|
|
65
65
|
|
|
66
66
|
/** 插件在核心之外新增的码。前缀统一避免与核心码冲突。 */
|
|
67
|
-
declare const PLUGIN_CODES: readonly ["CORE_UNAVAILABLE", "CORE_INCOMPATIBLE", "CORE_SPAWN_FAILED", "CORE_TIMEOUT", "CORE_CANCELLED", "CORE_CONFIG_CHANNEL_FAILED", "IPC_INVALID", "MODEL_KEY_MISSING", "MODEL_NOT_FOUND", "MODEL_PROVIDER_UNSUPPORTED", "GITHUB_AUTH", "PUSH_FAILED", "REPO_DIRTY"];
|
|
67
|
+
declare const PLUGIN_CODES: readonly ["CORE_UNAVAILABLE", "CORE_INCOMPATIBLE", "CORE_SPAWN_FAILED", "CORE_TIMEOUT", "CORE_CANCELLED", "CORE_CONFIG_CHANNEL_FAILED", "CORE_NOT_DOWNLOADED", "CORE_CHECKSUM_MISMATCH", "IPC_INVALID", "MODEL_KEY_MISSING", "MODEL_NOT_FOUND", "MODEL_PROVIDER_UNSUPPORTED", "GITHUB_AUTH", "PUSH_FAILED", "REPO_DIRTY"];
|
|
68
68
|
type PluginCode = (typeof PLUGIN_CODES)[number];
|
|
69
69
|
type AnyCode = ErrorCode | PluginCode;
|
|
70
70
|
interface ClassifiedError {
|
|
@@ -264,6 +264,8 @@ interface Settings {
|
|
|
264
264
|
schedule: ScheduleSettings;
|
|
265
265
|
/** 核心二进制覆盖;只在开发/排障时用,对应 TRENDING_DIGEST_CORE。 */
|
|
266
266
|
corePath: string;
|
|
267
|
+
/** 缓存缺失时是否自动从 Release 下载核心(首次运行需要联网)。 */
|
|
268
|
+
coreAutoDownload: boolean;
|
|
267
269
|
}
|
|
268
270
|
declare const DEFAULT_SETTINGS: Settings;
|
|
269
271
|
/** 核心支持的 provider 默认端点(空字符串 = 必须由用户填)。 */
|
|
@@ -470,35 +472,137 @@ declare namespace state {
|
|
|
470
472
|
export { type state_DigestIdentity as DigestIdentity, state_HISTORY_LIMIT as HISTORY_LIMIT, type state_HistoryFile as HistoryFile, type state_PendingPr as PendingPr, type state_PluginState as PluginState, type state_RunRecord as RunRecord, state_Store as Store, type state_StoreOptions as StoreOptions, state_newRunId as newRunId, state_writeJsonAtomic as writeJsonAtomic };
|
|
471
473
|
}
|
|
472
474
|
|
|
473
|
-
|
|
475
|
+
interface CoreRelease {
|
|
476
|
+
/** `owner/repo`(GitHub)。 */
|
|
477
|
+
repo: string;
|
|
478
|
+
/** 不带 `v` 的版本号,例如 `0.1.0`;对应 tag `v0.1.0`。 */
|
|
479
|
+
version: string;
|
|
480
|
+
}
|
|
481
|
+
interface ReleaseDescriptor extends CoreRelease {
|
|
482
|
+
tag: string;
|
|
483
|
+
goos: string;
|
|
484
|
+
goarch: string;
|
|
485
|
+
asset: string;
|
|
486
|
+
url: string;
|
|
487
|
+
checksumsUrl: string;
|
|
488
|
+
cacheDir: string;
|
|
489
|
+
binaryPath: string;
|
|
490
|
+
cached: boolean;
|
|
491
|
+
}
|
|
492
|
+
/** 从本包的 package.json 读核心版本(`sync-contract --ref vX` 会同时更新它)。 */
|
|
493
|
+
declare function readCoreRelease(pkg?: {
|
|
494
|
+
dsh?: {
|
|
495
|
+
core?: {
|
|
496
|
+
repo?: unknown;
|
|
497
|
+
version?: unknown;
|
|
498
|
+
};
|
|
499
|
+
};
|
|
500
|
+
}): CoreRelease;
|
|
501
|
+
/** Node 的 platform/arch → Go 的 GOOS/GOARCH(两个生态词汇不同,映射必须显式)。 */
|
|
502
|
+
declare function goTarget(platform?: NodeJS.Platform, arch?: string): {
|
|
503
|
+
goos: string;
|
|
504
|
+
goarch: string;
|
|
505
|
+
};
|
|
506
|
+
declare function assetName(platform?: NodeJS.Platform, arch?: string): string;
|
|
507
|
+
declare function describeRelease(release: CoreRelease, options?: {
|
|
508
|
+
env?: NodeJS.ProcessEnv;
|
|
509
|
+
platform?: NodeJS.Platform;
|
|
510
|
+
arch?: string;
|
|
511
|
+
}): ReleaseDescriptor;
|
|
512
|
+
/** 缓存里的二进制是否可信:可执行 + 与下载时记录的哈希一致。 */
|
|
513
|
+
declare function verifyCached(binaryPath: string): {
|
|
514
|
+
ok: boolean;
|
|
515
|
+
reason?: string;
|
|
516
|
+
};
|
|
517
|
+
interface EnsureOptions {
|
|
518
|
+
release?: CoreRelease;
|
|
519
|
+
env?: NodeJS.ProcessEnv;
|
|
520
|
+
platform?: NodeJS.Platform;
|
|
521
|
+
arch?: string;
|
|
522
|
+
/** false 时只查缓存,不联网(设置页探测、离线诊断用)。 */
|
|
523
|
+
allowDownload?: boolean;
|
|
524
|
+
/** true 时忽略缓存重新下载(「重新下载核心」按钮)。 */
|
|
525
|
+
force?: boolean;
|
|
526
|
+
fetchImpl?: typeof fetch;
|
|
527
|
+
onProgress?: (event: {
|
|
528
|
+
phase: 'checksums' | 'download' | 'verify';
|
|
529
|
+
bytes?: number;
|
|
530
|
+
total?: number;
|
|
531
|
+
}) => void;
|
|
532
|
+
}
|
|
533
|
+
interface EnsureResult {
|
|
534
|
+
path: string;
|
|
535
|
+
source: 'cache' | 'download';
|
|
536
|
+
descriptor: ReleaseDescriptor;
|
|
537
|
+
sha256?: string;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* 确保本地有一份可用的核心二进制。
|
|
541
|
+
* 缓存命中就不联网;`allowDownload=false` 时缓存缺失直接报错(不带网络副作用)。
|
|
542
|
+
*/
|
|
543
|
+
declare function ensureCore(options?: EnsureOptions): Promise<EnsureResult>;
|
|
544
|
+
|
|
545
|
+
type release_CoreRelease = CoreRelease;
|
|
546
|
+
type release_EnsureOptions = EnsureOptions;
|
|
547
|
+
type release_EnsureResult = EnsureResult;
|
|
548
|
+
type release_ReleaseDescriptor = ReleaseDescriptor;
|
|
549
|
+
declare const release_assetName: typeof assetName;
|
|
550
|
+
declare const release_describeRelease: typeof describeRelease;
|
|
551
|
+
declare const release_ensureCore: typeof ensureCore;
|
|
552
|
+
declare const release_goTarget: typeof goTarget;
|
|
553
|
+
declare const release_readCoreRelease: typeof readCoreRelease;
|
|
554
|
+
declare const release_verifyCached: typeof verifyCached;
|
|
555
|
+
declare namespace release {
|
|
556
|
+
export { type release_CoreRelease as CoreRelease, type release_EnsureOptions as EnsureOptions, type release_EnsureResult as EnsureResult, type release_ReleaseDescriptor as ReleaseDescriptor, release_assetName as assetName, release_describeRelease as describeRelease, release_ensureCore as ensureCore, release_goTarget as goTarget, release_readCoreRelease as readCoreRelease, release_verifyCached as verifyCached };
|
|
557
|
+
}
|
|
558
|
+
|
|
474
559
|
interface CoreLocation {
|
|
475
560
|
path: string;
|
|
476
|
-
source: 'settings' | 'env' | '
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
561
|
+
source: 'settings' | 'env' | 'cache' | 'download';
|
|
562
|
+
release?: CoreRelease;
|
|
563
|
+
tag?: string;
|
|
564
|
+
/** 本次调用是否真的下载了(用于日志与状态提示)。 */
|
|
565
|
+
downloaded?: boolean;
|
|
566
|
+
sha256?: string;
|
|
480
567
|
}
|
|
481
|
-
declare function platformPackageName(platform?: NodeJS.Platform, arch?: string): string;
|
|
482
568
|
interface LocateOptions {
|
|
483
569
|
settings: Settings;
|
|
484
570
|
env?: NodeJS.ProcessEnv;
|
|
485
571
|
platform?: NodeJS.Platform;
|
|
486
572
|
arch?: string;
|
|
487
|
-
/**
|
|
488
|
-
|
|
573
|
+
/** 默认 true;false 时只查缓存(设置页探测用)。 */
|
|
574
|
+
allowDownload?: boolean;
|
|
575
|
+
forceDownload?: boolean;
|
|
576
|
+
fetchImpl?: typeof fetch;
|
|
577
|
+
release?: CoreRelease;
|
|
489
578
|
}
|
|
490
|
-
declare function
|
|
491
|
-
|
|
492
|
-
|
|
579
|
+
declare function overridePath(settings: Settings, env?: NodeJS.ProcessEnv): {
|
|
580
|
+
path: string;
|
|
581
|
+
source: 'settings' | 'env';
|
|
582
|
+
} | undefined;
|
|
583
|
+
declare function locateCore(options: LocateOptions): Promise<CoreLocation>;
|
|
584
|
+
/** 只读的 Release 视图:不联网、不创建目录,供设置页展示「有没有、从哪来」。 */
|
|
585
|
+
declare function inspectRelease(settings: Settings, options?: {
|
|
586
|
+
env?: NodeJS.ProcessEnv;
|
|
587
|
+
platform?: NodeJS.Platform;
|
|
588
|
+
arch?: string;
|
|
589
|
+
release?: CoreRelease;
|
|
590
|
+
}): {
|
|
591
|
+
descriptor?: ReleaseDescriptor;
|
|
592
|
+
override?: {
|
|
593
|
+
path: string;
|
|
594
|
+
source: 'settings' | 'env';
|
|
595
|
+
};
|
|
596
|
+
error?: string;
|
|
597
|
+
};
|
|
493
598
|
|
|
494
599
|
type locate_CoreLocation = CoreLocation;
|
|
495
600
|
type locate_LocateOptions = LocateOptions;
|
|
496
|
-
declare const
|
|
601
|
+
declare const locate_inspectRelease: typeof inspectRelease;
|
|
497
602
|
declare const locate_locateCore: typeof locateCore;
|
|
498
|
-
declare const
|
|
499
|
-
declare const locate_platformPackageName: typeof platformPackageName;
|
|
603
|
+
declare const locate_overridePath: typeof overridePath;
|
|
500
604
|
declare namespace locate {
|
|
501
|
-
export { type locate_CoreLocation as CoreLocation, type locate_LocateOptions as LocateOptions,
|
|
605
|
+
export { type locate_CoreLocation as CoreLocation, type locate_LocateOptions as LocateOptions, locate_inspectRelease as inspectRelease, locate_locateCore as locateCore, locate_overridePath as overridePath };
|
|
502
606
|
}
|
|
503
607
|
|
|
504
608
|
interface HandshakeResult {
|
|
@@ -622,12 +726,31 @@ declare namespace run {
|
|
|
622
726
|
export { run_DEFAULT_RUN_TIMEOUT_MS as DEFAULT_RUN_TIMEOUT_MS, type run_RunCoreOptions as RunCoreOptions, type run_RunCoreResult as RunCoreResult, run_buildCoreArgs as buildCoreArgs, run_runCore as runCore };
|
|
623
727
|
}
|
|
624
728
|
|
|
729
|
+
/** Release 分发下的核心视图:用户一眼能看出「版本、下没下、从哪来」。 */
|
|
730
|
+
interface ReleaseStatus {
|
|
731
|
+
repo: string;
|
|
732
|
+
tag: string;
|
|
733
|
+
asset: string;
|
|
734
|
+
url: string;
|
|
735
|
+
checksumsUrl: string;
|
|
736
|
+
cacheDir: string;
|
|
737
|
+
binaryPath: string;
|
|
738
|
+
cached: boolean;
|
|
739
|
+
}
|
|
625
740
|
interface CoreStatus {
|
|
626
741
|
state: 'ready' | 'unavailable' | 'incompatible';
|
|
627
742
|
path?: string;
|
|
628
743
|
source?: CoreLocation['source'];
|
|
629
|
-
|
|
630
|
-
|
|
744
|
+
/** Release 分发状态(有覆盖路径时只有 override,没有 release)。 */
|
|
745
|
+
release?: ReleaseStatus;
|
|
746
|
+
override?: {
|
|
747
|
+
path: string;
|
|
748
|
+
source: 'settings' | 'env';
|
|
749
|
+
};
|
|
750
|
+
download: {
|
|
751
|
+
state: 'cached' | 'missing' | 'override';
|
|
752
|
+
reason?: string;
|
|
753
|
+
};
|
|
631
754
|
version?: string;
|
|
632
755
|
commit?: string;
|
|
633
756
|
ipcSchema?: number;
|
|
@@ -665,11 +788,12 @@ declare function detectCoexistence(ownPath: string | undefined, options?: {
|
|
|
665
788
|
|
|
666
789
|
type core_CoexistenceStatus = CoexistenceStatus;
|
|
667
790
|
type core_CoreStatus = CoreStatus;
|
|
791
|
+
type core_ReleaseStatus = ReleaseStatus;
|
|
668
792
|
declare const core_detectCoexistence: typeof detectCoexistence;
|
|
669
793
|
declare const core_findOnPath: typeof findOnPath;
|
|
670
794
|
declare const core_inspectCore: typeof inspectCore;
|
|
671
795
|
declare namespace core {
|
|
672
|
-
export { type core_CoexistenceStatus as CoexistenceStatus, type core_CoreStatus as CoreStatus, core_detectCoexistence as detectCoexistence, core_findOnPath as findOnPath, core_inspectCore as inspectCore };
|
|
796
|
+
export { type core_CoexistenceStatus as CoexistenceStatus, type core_CoreStatus as CoreStatus, type core_ReleaseStatus as ReleaseStatus, core_detectCoexistence as detectCoexistence, core_findOnPath as findOnPath, core_inspectCore as inspectCore };
|
|
673
797
|
}
|
|
674
798
|
|
|
675
799
|
/**
|
|
@@ -926,6 +1050,9 @@ interface RunSummary {
|
|
|
926
1050
|
logFile?: string;
|
|
927
1051
|
channel?: 'fifo' | 'tmpfile';
|
|
928
1052
|
proxy?: boolean;
|
|
1053
|
+
/** 核心从哪来:env/settings 覆盖、缓存、还是本次下载。 */
|
|
1054
|
+
coreSource?: 'settings' | 'env' | 'cache' | 'download';
|
|
1055
|
+
coreTag?: string;
|
|
929
1056
|
}
|
|
930
1057
|
interface RunRequestOptions {
|
|
931
1058
|
dryRun?: boolean;
|
|
@@ -1313,4 +1440,4 @@ declare namespace tools {
|
|
|
1313
1440
|
export { tools_createTrendingHistoryTool as createTrendingHistoryTool, tools_createTrendingRunTool as createTrendingRunTool };
|
|
1314
1441
|
}
|
|
1315
1442
|
|
|
1316
|
-
export { channel, config, core, credentials, errors, exec, handshake$1 as handshake, importCli, ipc, locate, paths, preflight, proxy, quickfill, routes, run, runner, schedule, state, tools };
|
|
1443
|
+
export { channel, config, core, credentials, errors, exec, handshake$1 as handshake, importCli, ipc, locate, paths, preflight, proxy, quickfill, release, routes, run, runner, schedule, state, tools };
|
package/lib/internal.js
CHANGED
|
@@ -12,13 +12,14 @@ import {
|
|
|
12
12
|
preflight_exports,
|
|
13
13
|
proxy_exports,
|
|
14
14
|
quickfill_exports,
|
|
15
|
+
release_exports,
|
|
15
16
|
routes_exports,
|
|
16
17
|
run_exports,
|
|
17
18
|
runner_exports,
|
|
18
19
|
schedule_exports,
|
|
19
20
|
state_exports,
|
|
20
21
|
tools_exports
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-RXYA5WFW.js";
|
|
22
23
|
import {
|
|
23
24
|
exec_exports
|
|
24
25
|
} from "./chunk-XLIFTAVL.js";
|
|
@@ -37,6 +38,7 @@ export {
|
|
|
37
38
|
preflight_exports as preflight,
|
|
38
39
|
proxy_exports as proxy,
|
|
39
40
|
quickfill_exports as quickfill,
|
|
41
|
+
release_exports as release,
|
|
40
42
|
routes_exports as routes,
|
|
41
43
|
run_exports as run,
|
|
42
44
|
runner_exports as runner,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morehao/dsh-github-trending",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "GitHub 趋势日报插件:每天定时 spawn 本地 trendradar 核心,抓取 Trending、生成中文简介与总结,提交到你的仓库并自动合并 PR。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -43,14 +43,6 @@
|
|
|
43
43
|
"node": ">=22.20",
|
|
44
44
|
"dsh": ">=0.2.1-rc.3 <0.3.0-0"
|
|
45
45
|
},
|
|
46
|
-
"optionalDependencies": {
|
|
47
|
-
"@morehao/trendradar-core-darwin-arm64": "0.1.0",
|
|
48
|
-
"@morehao/trendradar-core-darwin-x64": "0.1.0",
|
|
49
|
-
"@morehao/trendradar-core-linux-arm64": "0.1.0",
|
|
50
|
-
"@morehao/trendradar-core-linux-x64": "0.1.0",
|
|
51
|
-
"@morehao/trendradar-core-win32-arm64": "0.1.0",
|
|
52
|
-
"@morehao/trendradar-core-win32-x64": "0.1.0"
|
|
53
|
-
},
|
|
54
46
|
"peerDependencies": {
|
|
55
47
|
"@deepseek-ai/dsh-tools": ">=0.2.1-rc.3 <0.3.0-0"
|
|
56
48
|
},
|
|
@@ -63,6 +55,10 @@
|
|
|
63
55
|
"bundle": {
|
|
64
56
|
"patch": "./cordis.patch.yml"
|
|
65
57
|
},
|
|
58
|
+
"core": {
|
|
59
|
+
"repo": "morehao/trendradar",
|
|
60
|
+
"version": "0.1.0"
|
|
61
|
+
},
|
|
66
62
|
"client": {
|
|
67
63
|
"platform": "web",
|
|
68
64
|
"immediately": true,
|
|
@@ -80,9 +76,10 @@
|
|
|
80
76
|
"typecheck": "tsc --noEmit",
|
|
81
77
|
"gen:ipc": "node scripts/gen-ipc-types.mjs",
|
|
82
78
|
"check:contract": "node scripts/sync-contract.mjs --check && node scripts/gen-ipc-types.mjs --check",
|
|
79
|
+
"check:lockfile": "node scripts/check-lockfile.mjs",
|
|
83
80
|
"test": "node --test \"test/*.test.mjs\"",
|
|
84
81
|
"pretest": "npm run build",
|
|
85
|
-
"check": "npm run typecheck && npm run check:contract && npm run build && npm test",
|
|
82
|
+
"check": "npm run typecheck && npm run check:lockfile && npm run check:contract && npm run build && npm test",
|
|
86
83
|
"prepublishOnly": "npm run check"
|
|
87
84
|
},
|
|
88
85
|
"devDependencies": {
|