@morehao/dsh-github-trending 0.3.2 → 0.4.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/CHANGELOG.md +17 -0
- package/README.md +7 -6
- package/client.js +216 -95
- package/lib/{chunk-NMINPPWO.js → chunk-QXAKMNHQ.js} +143 -35
- package/lib/index.js +4 -4
- package/lib/internal.d.ts +66 -16
- package/lib/internal.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v0.4.1] - 2026-10-31
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- **ui**: move navigation to top with identity bar (#25)
|
|
8
|
+
|
|
9
|
+
**Full Changelog**: https://github.com/morehao/dsh-github-trending/compare/v0.4.0...v0.4.1
|
|
10
|
+
|
|
11
|
+
## 0.4.0 - 2026-10-31
|
|
12
|
+
|
|
13
|
+
### 变更
|
|
14
|
+
|
|
15
|
+
- **适配核心 0.4.0 的三周期产物布局**:`dsh.core.version` 升到 `0.4.0`。核心产物改为 `trending/<period>/<bucket>/<name>.{md,json}`(daily/weekly/monthly),分支为 `trending/<period>/<name>`;插件每次运行按标的周期传 `run --period daily|weekly|monthly`,保证「抓取窗口 = 标注周期」。
|
|
16
|
+
- **预览按周期选取**:从本次 artifact 里挑本期 digest 的 `.md`(daily=`YYYY-MM-DD.md`、weekly=`YYYY-Wnn.md`、monthly=`YYYY-MM.md`),并排除 `index.md`;日榜预览行为不变。
|
|
17
|
+
- **调度按周期**:新增 `schedule.periods`(日/周/月多选,默认 `daily`)。日榜每天、周榜周日(ISO 周末)、月榜月末触发;去重键含周期与本地日期,同一周期当天只成功一次;「下次运行」取所有已启用周期里最近的一次。
|
|
18
|
+
- `trending_run` 工具与 `POST /run` 增加可选 `period` 参数(缺省用 `trending.since`)。
|
|
19
|
+
|
|
3
20
|
## 0.3.2 - 2026-10-31
|
|
4
21
|
|
|
5
22
|
### 改进
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://github.com/morehao/dsh-github-trending/actions/workflows/check.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/@morehao/dsh-github-trending)
|
|
5
5
|
|
|
6
|
-
> GitHub
|
|
6
|
+
> GitHub 趋势榜的 **DSH 入口**:按日/周/月周期定时 spawn 本地 [`trendradar`](https://github.com/morehao/trendradar) 核心,抓取 Trending、生成中文简介与周期总结,提交到你自己的仓库并自动合并 PR。
|
|
7
7
|
|
|
8
8
|
**状态:0.3.0。** 插件侧(配置、调度、核心调用、状态与工作台、两个工具)已完成。核心二进制走 **GitHub Release**:首次运行按 `os/arch` 下载、用 `checksums.txt` 校验 sha256、缓存到插件数据目录,之后离线复用。核心仓库打 tag 出资产之前,运行会明确报 `CORE_NOT_DOWNLOADED` / `CORE_UNAVAILABLE` 并给出下载地址——不会写空文件,也不会退回旧内容。
|
|
9
9
|
|
|
@@ -40,11 +40,11 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
|
|
|
40
40
|
- `GET /preview?id=<runId>` 只接受历史里出现过的 id、不接路径参数;返回 `{ text, truncated }`。客户端渲染 Markdown 子集(标题 / 段落 / 列表 / 引用 / 代码块 / 表格(GFM 管道表,含列对齐)/ 粗体 / 行内代码 / 链接 / 分隔线),**只生成 React 文本节点**,链接仅放行 http(s),不渲染图片或原始 HTML;也可以切「原文」。
|
|
41
41
|
- 日报只列最近 30 天**有记录**的日期,每天一条;默认打开最近一期有预览的日报。失败 / 无变更 / 试跑 / 旧记录 / 副本被清理都有各自的空状态,不用示例内容兜底。
|
|
42
42
|
- 预览是运行结束时的派生副本:你在仓库里事后编辑 canonical 文件,不会改变这次记录。
|
|
43
|
-
-
|
|
43
|
+
- 工作台顶部第一行是插件身份条:名称 / 版本 / 完整包名(点击复制)/ GitHub 外链 / 检查更新(就地状态);第二行是横向 tab(概览 / 日报 / 运行记录 / 设置),右侧常驻下次运行与就绪 N/4。点版本号可打开「关于」弹层。包名 / 版本 / GitHub 来自 `/state.plugin`(`repository` 由 `package.json` 规范化),仓库缺失时隐藏外链。
|
|
44
44
|
|
|
45
45
|
## 插件更新
|
|
46
46
|
|
|
47
|
-
-
|
|
47
|
+
- 顶部身份条的「检查更新」与「关于」弹层都会显示当前版本与 npm 上的最新版:首次挂载静默检查一次(Host 侧缓存 5 分钟),也可以点「检查更新」强制重查;有新版时身份条就地显示「有新版本 vX」+「更新到 vX」。只跟随 npm 的 `latest` 稳定版。
|
|
48
48
|
- 确认后由宿主 `pluginManager` 重装本插件(`installBundle`;已装同 spec 时走 `removeBundle → installBundle`,失败会尝试装回原版本),完成后需要重启 DSH 生效。
|
|
49
49
|
- 本地安装(`link:` / `file:` / `workspace:`)**不提供一键更新**:那会把开发链接换成 npm 版。此时只显示 `dsh plugin --profile <name> add @morehao/dsh-github-trending@latest` 供复制。
|
|
50
50
|
- 更新引擎来自共享包 [`@morehao/dsh-plugin-kit`](https://www.npmjs.com/package/@morehao/dsh-plugin-kit)(`./update` + `./update/client`),本仓只做上下文与 `execCapture` 接线;`GET /update` / `POST /update` 与其它路由一样要求自定义头、`no-store`,业务失败返回 409 + `UPDATE_FAILED` + `updateCode`。
|
|
@@ -94,9 +94,10 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
|
|
|
94
94
|
| `pr.titleTemplate` | `chore(trending): add {date} digest` | 默认规则,界面不展示 |
|
|
95
95
|
| `github.authMode` | `gh` | `gh`(本机 gh 登录态)/ `pat` |
|
|
96
96
|
| `github.tokenRef` | `GITHUB_TOKEN` | `ctx.credentials` 引用名(`pat` 用) |
|
|
97
|
-
| `trending.since` / `languages` / `topN` | `daily` / `all,typescript,python,go,rust` / `25` |
|
|
97
|
+
| `trending.since` / `languages` / `topN` | `daily` / `all,typescript,python,go,rust` / `25` | `since` 是单次运行周期(`daily`/`weekly`/`monthly`,抓取窗口=标的周期由核心保证);实际支持范围由核心决定 |
|
|
98
98
|
| `output.dir` | `trending` | 仓库内相对目录 |
|
|
99
99
|
| `schedule.enabled` / `time` / `timezone` | `true` / `08:30:00` / `Asia/Shanghai` | 插件自己的 60 s tick 驱动 |
|
|
100
|
+
| `schedule.periods` | `daily` | 定时自动产出的周期(`daily`/`weekly`/`monthly` 多选):日=每天、周=周日、月=月末触发 |
|
|
100
101
|
| `corePath` | 空 | 等价于 `TRENDING_DIGEST_CORE`(仅开发/排障,界面不提供入口,用环境变量或直接改 `settings.json`;设了就完全不下载) |
|
|
101
102
|
| `coreAutoDownload` | `true` | 缓存缺失时是否自动下载核心;关掉后只报 `CORE_NOT_DOWNLOADED`,由你在设置页点「下载核心」 |
|
|
102
103
|
|
|
@@ -104,7 +105,7 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
|
|
|
104
105
|
|
|
105
106
|
| 工具 | 参数 | 返回 |
|
|
106
107
|
|---|---|---|
|
|
107
|
-
| `trending_run` | `{ dryRun?: boolean, date?: string }` | 结构化摘要:文件数、commit/分支、PR、内容哈希、错误码与下一步 |
|
|
108
|
+
| `trending_run` | `{ dryRun?: boolean, date?: string, period?: 'daily'\|'weekly'\|'monthly' }` | 结构化摘要:文件数、commit/分支、PR、内容哈希、错误码与下一步 |
|
|
108
109
|
| `trending_history` | `{ limit?: number }` | 上次/下次运行、最近 N 条记录、核心版本、模型、未合并 PR |
|
|
109
110
|
|
|
110
111
|
工具名是稳定对外接口:**只新增不改名**。
|
|
@@ -141,7 +142,7 @@ dsh plugin --profile <profile> add @morehao/dsh-github-trending
|
|
|
141
142
|
1. **结果信封**:核心 `run --config <path> --json` 输出契约 v2 `RunResult`(`ipcSchema=2`,可选 `branch/commit/prUrl/contentHash`);插件只解析这一种形状,旧核心的 `runOutput` 不再兼容。
|
|
142
143
|
2. **密钥传输**:POSIX 上配置经 **FIFO** 交给核心(文件系统里不出现明文);Windows 无 FIFO,退化为 0600 临时文件并在读完后立即删除。
|
|
143
144
|
3. **GitHub token** 经子进程环境变量传给核心(核心只认 `GITHUB_TOKEN`/`GH_TOKEN`);不进 argv、不进配置、不进日志。
|
|
144
|
-
4. **调度**用插件自己的 60 s tick(宿主 `schedule`
|
|
145
|
+
4. **调度**用插件自己的 60 s tick(宿主 `schedule` 是会话本地提醒,覆盖不到无人值守场景)。按 `schedule.periods` 分别触发:日榜每天、周榜周日(ISO 周末)、月榜月末;去重键含周期与本地日期,同一周期当天只成功一次。
|
|
145
146
|
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>` 写入)。
|
|
146
147
|
6. **核心版本固定**:`package.json` 的 `dsh.core.version` 是首次运行下载哪个核心 Release 的唯一来源(用 `node scripts/set-core-version.mjs <x.y.z>` 写入);下载失败会给出下载地址与错误码,不会退回旧内容。
|
|
147
148
|
7. **预览副本是派生数据**:不是第二份真相;仓库里的 canonical 文件才是。旧记录没有 `preview` 元数据,日报里只能看到对应的空状态,不承诺回填。
|
package/client.js
CHANGED
|
@@ -137,10 +137,12 @@ var DICT = {
|
|
|
137
137
|
"scope.period": "周期",
|
|
138
138
|
"scope.daily": "每日",
|
|
139
139
|
"scope.weekly": "每周",
|
|
140
|
+
"scope.monthly": "每月",
|
|
140
141
|
"scope.languages": "语言",
|
|
141
142
|
"scope.topN": "每语言条数",
|
|
142
143
|
"card.schedule.title": "调度",
|
|
143
144
|
"schedule.enabled": "定时运行",
|
|
145
|
+
"schedule.periods": "自动产出周期",
|
|
144
146
|
"schedule.time": "时间",
|
|
145
147
|
"schedule.timezone": "时区",
|
|
146
148
|
"schedule.next": "下次运行",
|
|
@@ -239,6 +241,22 @@ var DICT = {
|
|
|
239
241
|
"shell.ready": "就绪 {ready} / {total}",
|
|
240
242
|
"shell.github": "GitHub",
|
|
241
243
|
"shell.package.copy": "复制完整包名",
|
|
244
|
+
"shell.update.check": "检查更新",
|
|
245
|
+
"shell.update.checking": "正在检查…",
|
|
246
|
+
"shell.update.applying": "正在更新到 v{version}…",
|
|
247
|
+
"shell.update.applyingShort": "更新中…",
|
|
248
|
+
"shell.update.done": "已更新,重启 DSH 生效",
|
|
249
|
+
"shell.update.failed": "更新失败",
|
|
250
|
+
"shell.update.checkFailed": "检查失败,可稍后重试",
|
|
251
|
+
"shell.update.available": "有新版本 v{latest}",
|
|
252
|
+
"shell.update.latest": "已是最新 v{version}",
|
|
253
|
+
"shell.update.apply": "更新到 v{version}",
|
|
254
|
+
"shell.update.confirmHint": "更新会重装插件,需重启 DSH 生效",
|
|
255
|
+
"shell.update.confirmYes": "确认更新",
|
|
256
|
+
"shell.update.cancel": "取消",
|
|
257
|
+
"shell.update.copyCmd": "复制更新命令",
|
|
258
|
+
"shell.update.copied": "已复制",
|
|
259
|
+
"shell.update.manual": "本地安装,需手动更新",
|
|
242
260
|
"time.in.minutes": "{minutes} 分钟后",
|
|
243
261
|
"time.in.hours": "{hours} 小时 {minutes} 分后",
|
|
244
262
|
"time.in.days": "{days} 天后",
|
|
@@ -452,10 +470,12 @@ var DICT = {
|
|
|
452
470
|
"scope.period": "Period",
|
|
453
471
|
"scope.daily": "Daily",
|
|
454
472
|
"scope.weekly": "Weekly",
|
|
473
|
+
"scope.monthly": "Monthly",
|
|
455
474
|
"scope.languages": "Languages",
|
|
456
475
|
"scope.topN": "Per language",
|
|
457
476
|
"card.schedule.title": "Schedule",
|
|
458
477
|
"schedule.enabled": "Scheduled runs",
|
|
478
|
+
"schedule.periods": "Auto periods",
|
|
459
479
|
"schedule.time": "Time",
|
|
460
480
|
"schedule.timezone": "Time zone",
|
|
461
481
|
"schedule.next": "Next run",
|
|
@@ -554,6 +574,22 @@ var DICT = {
|
|
|
554
574
|
"shell.ready": "Ready {ready} / {total}",
|
|
555
575
|
"shell.github": "GitHub",
|
|
556
576
|
"shell.package.copy": "Copy full package name",
|
|
577
|
+
"shell.update.check": "Check for updates",
|
|
578
|
+
"shell.update.checking": "Checking…",
|
|
579
|
+
"shell.update.applying": "Updating to v{version}…",
|
|
580
|
+
"shell.update.applyingShort": "Updating…",
|
|
581
|
+
"shell.update.done": "Updated · restart DSH",
|
|
582
|
+
"shell.update.failed": "Update failed",
|
|
583
|
+
"shell.update.checkFailed": "Check failed, try again later",
|
|
584
|
+
"shell.update.available": "v{latest} available",
|
|
585
|
+
"shell.update.latest": "Up to date · v{version}",
|
|
586
|
+
"shell.update.apply": "Update to v{version}",
|
|
587
|
+
"shell.update.confirmHint": "Reinstalls the plugin; restart DSH to apply",
|
|
588
|
+
"shell.update.confirmYes": "Confirm",
|
|
589
|
+
"shell.update.cancel": "Cancel",
|
|
590
|
+
"shell.update.copyCmd": "Copy update command",
|
|
591
|
+
"shell.update.copied": "Copied",
|
|
592
|
+
"shell.update.manual": "Local install · update manually",
|
|
557
593
|
"time.in.minutes": "in {minutes} min",
|
|
558
594
|
"time.in.hours": "in {hours} h {minutes} min",
|
|
559
595
|
"time.in.days": "in {days} d",
|
|
@@ -764,7 +800,7 @@ var CSS = `
|
|
|
764
800
|
|
|
765
801
|
/* ——— 01 工作台外壳 ——— */
|
|
766
802
|
.dshgt-shell{gap:0;height:100%;min-height:0;overflow:hidden;}
|
|
767
|
-
.dshgt-app{flex:1;min-height:0;display:
|
|
803
|
+
.dshgt-app{flex:1;min-height:0;display:flex;flex-direction:column;background:var(--gt-bg);}
|
|
768
804
|
.dshgt-rail{background:color-mix(in srgb,var(--gt-fg) 3%,var(--gt-bg));border-right:1px solid var(--gt-border);display:flex;flex-direction:column;padding:14px 10px 12px;min-height:0;overflow:hidden;}
|
|
769
805
|
.dshgt-brand{display:flex;align-items:center;gap:9px;padding:2px 6px 16px;}
|
|
770
806
|
.dshgt-brand svg{flex:none;border-radius:8px;}
|
|
@@ -793,7 +829,7 @@ var CSS = `
|
|
|
793
829
|
.dshgt-pkg-copy{width:22px;height:22px;margin-left:auto;display:grid;place-items:center;border:0;background:none;border-radius:4px;color:var(--gt-muted);cursor:pointer;}
|
|
794
830
|
.dshgt-pkg-copy:hover{background:color-mix(in srgb,var(--gt-fg) 7%,var(--gt-bg));color:var(--gt-fg);}
|
|
795
831
|
.dshgt-pkg-copy:disabled{opacity:.4;cursor:not-allowed;}
|
|
796
|
-
.dshgt-main{display:flex;flex-direction:column;min-width:0;min-height:0;background:var(--gt-bg);}
|
|
832
|
+
.dshgt-main{flex:1;display:flex;flex-direction:column;min-width:0;min-height:0;background:var(--gt-bg);}
|
|
797
833
|
.dshgt-page-head{display:flex;align-items:flex-start;gap:16px;padding:20px 30px 14px;border-bottom:1px solid color-mix(in srgb,var(--gt-border) 70%,transparent);flex:none;}
|
|
798
834
|
.dshgt-page-heading{min-width:0;}
|
|
799
835
|
.dshgt-page-head h1{margin:0;font-size:21px;line-height:1.25;font-weight:650;letter-spacing:-.012em;}
|
|
@@ -809,6 +845,43 @@ var CSS = `
|
|
|
809
845
|
.dshgt-tag{display:inline-flex;align-items:center;height:20px;padding:0 7px;border:1px solid var(--gt-border);border-radius:999px;font-size:11px;color:var(--gt-muted);background:var(--gt-bg);white-space:nowrap;}
|
|
810
846
|
.dshgt-inline-tag{margin-left:6px;vertical-align:1px;}
|
|
811
847
|
|
|
848
|
+
/* ——— 顶部身份条(第一行):名称 / 版本 / 完整包名 / GitHub / 检查更新 ——— */
|
|
849
|
+
.dshgt-idbar{display:flex;align-items:center;gap:10px;height:48px;padding:0 18px;border-bottom:1px solid var(--gt-border);flex:none;background:var(--gt-bg);}
|
|
850
|
+
.dshgt-idbar .dshgt-brand{display:flex;align-items:center;gap:9px;padding:0;flex:none;}
|
|
851
|
+
.dshgt-idbar .dshgt-brand svg{width:22px;height:22px;flex:none;border-radius:6px;}
|
|
852
|
+
.dshgt-brand-name{font-size:13.5px;font-weight:650;letter-spacing:-.01em;white-space:nowrap;line-height:1.3;}
|
|
853
|
+
.dshgt-id-ver{appearance:none;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;color:var(--gt-fg);background:var(--gt-card);border:1px solid var(--gt-border);border-radius:999px;padding:1px 7px;line-height:16px;cursor:pointer;flex:none;}
|
|
854
|
+
.dshgt-id-ver:hover{border-color:color-mix(in srgb,var(--gt-accent) 45%,var(--gt-border));}
|
|
855
|
+
.dshgt-id-sep{width:1px;height:16px;background:var(--gt-border);flex:none;}
|
|
856
|
+
.dshgt-id-pkg{appearance:none;border:0;background:none;padding:2px 4px;border-radius:5px;color:var(--gt-muted);cursor:pointer;font-size:12px;max-width:44vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:none;}
|
|
857
|
+
.dshgt-id-pkg:hover{color:var(--gt-fg);background:color-mix(in srgb,var(--gt-fg) 5%,var(--gt-bg));}
|
|
858
|
+
.dshgt-idbar-right{margin-left:auto;display:flex;align-items:center;gap:10px;flex:none;}
|
|
859
|
+
.dshgt-id-gh{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:7px;color:var(--gt-muted);border:1px solid transparent;}
|
|
860
|
+
.dshgt-id-gh:hover{color:var(--gt-fg);background:color-mix(in srgb,var(--gt-fg) 6%,var(--gt-bg));border-color:var(--gt-border);}
|
|
861
|
+
.dshgt-id-upd{display:flex;align-items:center;gap:10px;}
|
|
862
|
+
.dshgt-id-status{font-size:12px;color:var(--gt-muted);white-space:nowrap;display:inline-flex;align-items:center;gap:6px;}
|
|
863
|
+
.dshgt-id-status.ok{color:var(--gt-ok);}
|
|
864
|
+
.dshgt-id-status.new{color:var(--gt-warn);}
|
|
865
|
+
.dshgt-id-status.err{color:var(--gt-err);}
|
|
866
|
+
.dshgt-id-actions{display:flex;align-items:center;gap:8px;}
|
|
867
|
+
|
|
868
|
+
/* ——— 横向 tab(第二行)+ 右侧状态 ——— */
|
|
869
|
+
.dshgt-tabsbar{display:flex;align-items:stretch;gap:16px;height:46px;padding:0 24px;border-bottom:1px solid var(--gt-border);flex:none;background:var(--gt-bg);}
|
|
870
|
+
.dshgt-tabnav{display:flex;align-items:stretch;gap:2px;height:100%;}
|
|
871
|
+
.dshgt-tab{display:flex;align-items:center;gap:7px;padding:0 12px;border:0;background:none;color:var(--gt-muted);cursor:pointer;font:inherit;font-size:13.5px;position:relative;border-bottom:2px solid transparent;margin-bottom:-1px;}
|
|
872
|
+
.dshgt-tab svg{opacity:.8;}
|
|
873
|
+
.dshgt-tab:hover{color:var(--gt-fg);}
|
|
874
|
+
.dshgt-tab.on{color:var(--gt-fg);font-weight:600;border-bottom-color:var(--gt-accent);}
|
|
875
|
+
.dshgt-tab.on svg{opacity:1;color:var(--gt-accent);}
|
|
876
|
+
.dshgt-tab-tail{font-size:10.5px;color:var(--gt-muted);background:var(--gt-card);border:1px solid var(--gt-border);border-radius:999px;padding:0 5px;line-height:15px;}
|
|
877
|
+
.dshgt-tabsbar-status{margin-left:auto;display:flex;align-items:center;gap:16px;flex:none;}
|
|
878
|
+
.dshgt-status-item{display:inline-flex;align-items:center;gap:7px;font-size:12px;color:var(--gt-muted);white-space:nowrap;}
|
|
879
|
+
.dshgt-status-key{color:var(--gt-muted);}
|
|
880
|
+
.dshgt-status-val{color:var(--gt-fg);font-weight:600;}
|
|
881
|
+
.dshgt-status-sub{color:var(--gt-muted);}
|
|
882
|
+
@media (max-width:1100px){ .dshgt-id-pkg{max-width:26vw;} }
|
|
883
|
+
@media (max-width:860px){ .dshgt-id-pkg{display:none;} .dshgt-status-item.dshgt-next{display:none;} }
|
|
884
|
+
|
|
812
885
|
/* ——— 概览 ——— */
|
|
813
886
|
.dshgt-delivery{border:1px solid var(--gt-border);border-radius:12px;padding:18px 20px 16px;background:var(--gt-card);display:flex;flex-direction:column;gap:6px;}
|
|
814
887
|
.dshgt-delivery-top{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
|
|
@@ -1663,10 +1736,11 @@ function SettingsView(props) {
|
|
|
1663
1736
|
value: draft.trending.since,
|
|
1664
1737
|
options: [
|
|
1665
1738
|
{ value: "daily", label: t("scope.daily") },
|
|
1666
|
-
{ value: "weekly", label: t("scope.weekly") }
|
|
1739
|
+
{ value: "weekly", label: t("scope.weekly") },
|
|
1740
|
+
{ value: "monthly", label: t("scope.monthly") }
|
|
1667
1741
|
],
|
|
1668
1742
|
onChange: (value) => update((next) => {
|
|
1669
|
-
next.trending.since = value === "weekly" ? "weekly" : "daily";
|
|
1743
|
+
next.trending.since = value === "weekly" ? "weekly" : value === "monthly" ? "monthly" : "daily";
|
|
1670
1744
|
})
|
|
1671
1745
|
}),
|
|
1672
1746
|
h("span", { className: "dshgt-hint" }, t("scope.topN")),
|
|
@@ -1704,6 +1778,23 @@ function SettingsView(props) {
|
|
|
1704
1778
|
next.schedule.timezone = value;
|
|
1705
1779
|
}) })
|
|
1706
1780
|
),
|
|
1781
|
+
h(
|
|
1782
|
+
Row,
|
|
1783
|
+
{ label: t("schedule.periods") },
|
|
1784
|
+
...["daily", "weekly", "monthly"].map(
|
|
1785
|
+
(period) => h(Toggle, {
|
|
1786
|
+
key: period,
|
|
1787
|
+
checked: draft.schedule.periods.includes(period),
|
|
1788
|
+
onChange: (on) => update((next) => {
|
|
1789
|
+
const set = new Set(next.schedule.periods);
|
|
1790
|
+
if (on) set.add(period);
|
|
1791
|
+
else set.delete(period);
|
|
1792
|
+
next.schedule.periods = ["daily", "weekly", "monthly"].filter((p) => set.has(p));
|
|
1793
|
+
}),
|
|
1794
|
+
label: t(`scope.${period}`)
|
|
1795
|
+
})
|
|
1796
|
+
)
|
|
1797
|
+
),
|
|
1707
1798
|
h("p", { className: "dshgt-hint" }, t("schedule.source"))
|
|
1708
1799
|
);
|
|
1709
1800
|
const currentCard = section === "source" ? sourceCard : section === "model" ? modelCard : section === "repo" ? repoCard : section === "auth" ? authCard : section === "scope" ? scopeCard : scheduleCard;
|
|
@@ -2107,11 +2198,6 @@ function usePluginUpdate() {
|
|
|
2107
2198
|
}, [controller]);
|
|
2108
2199
|
return [state, controller];
|
|
2109
2200
|
}
|
|
2110
|
-
function UpdateDot(props) {
|
|
2111
|
-
const [state] = usePluginUpdate();
|
|
2112
|
-
if (state.view?.updateAvailable !== true) return null;
|
|
2113
|
-
return h("span", { className: "dshgt-update-dot", title: props.t("update.badge") });
|
|
2114
|
-
}
|
|
2115
2201
|
function PluginUpdateSection(props) {
|
|
2116
2202
|
const { t } = props;
|
|
2117
2203
|
const [state, controller] = usePluginUpdate();
|
|
@@ -3440,6 +3526,109 @@ function AboutDialog(props) {
|
|
|
3440
3526
|
return h("div", { className: "dshgt-root" }, dialog);
|
|
3441
3527
|
}
|
|
3442
3528
|
|
|
3529
|
+
// client/identity-bar.ts
|
|
3530
|
+
function logo() {
|
|
3531
|
+
return h(
|
|
3532
|
+
"svg",
|
|
3533
|
+
{ width: 22, height: 22, viewBox: "0 0 64 64", "aria-hidden": "true" },
|
|
3534
|
+
h("rect", { x: 4, y: 4, width: 56, height: 56, rx: 14, fill: "#0d1117" }),
|
|
3535
|
+
h("path", { d: "M18 46V22l14 12 14-12v24", fill: "none", stroke: "#58a6ff", strokeWidth: 4, strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
3536
|
+
h("circle", { cx: 32, cy: 45, r: 3, fill: "#3fb950" })
|
|
3537
|
+
);
|
|
3538
|
+
}
|
|
3539
|
+
function githubIcon() {
|
|
3540
|
+
return h("svg", { width: 16, height: 16, viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true" }, h("path", {
|
|
3541
|
+
d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"
|
|
3542
|
+
}));
|
|
3543
|
+
}
|
|
3544
|
+
function IdentityBar(props) {
|
|
3545
|
+
const { t, plugin } = props;
|
|
3546
|
+
const [copied, setCopied] = import_react.default.useState(false);
|
|
3547
|
+
const [confirming, setConfirming] = import_react.default.useState(false);
|
|
3548
|
+
const [cmdCopied, setCmdCopied] = import_react.default.useState(false);
|
|
3549
|
+
const [updateState, controller] = usePluginUpdate();
|
|
3550
|
+
const view = updateState.view;
|
|
3551
|
+
const status = updateState.status;
|
|
3552
|
+
import_react.default.useEffect(() => {
|
|
3553
|
+
if (status === "applying" || status === "restart-required" || status === "applied") setConfirming(false);
|
|
3554
|
+
}, [status]);
|
|
3555
|
+
const copyPkg = () => {
|
|
3556
|
+
void copyText(plugin.name).then((ok) => {
|
|
3557
|
+
if (!ok) return;
|
|
3558
|
+
setCopied(true);
|
|
3559
|
+
setTimeout(() => setCopied(false), 1400);
|
|
3560
|
+
});
|
|
3561
|
+
};
|
|
3562
|
+
const busy = status === "checking" || status === "applying";
|
|
3563
|
+
const settled = status === "restart-required" || status === "applied";
|
|
3564
|
+
const latestVersion = view === null ? "" : view.latestVersion ?? "";
|
|
3565
|
+
const updateAvailable = view !== null && view.updateAvailable;
|
|
3566
|
+
const actionable = updateAvailable && view !== null && view.canApply && !settled && !busy;
|
|
3567
|
+
const manual = view !== null && (!view.canApply || view.checkFailed) ? view.manualCommand ?? null : null;
|
|
3568
|
+
const currentVersion = view === null ? plugin.version : view.currentVersion ?? plugin.version;
|
|
3569
|
+
const copyCmd = () => {
|
|
3570
|
+
if (manual === void 0 || manual === null) return;
|
|
3571
|
+
void copyText(manual).then((ok) => {
|
|
3572
|
+
if (!ok) return;
|
|
3573
|
+
setCmdCopied(true);
|
|
3574
|
+
setTimeout(() => setCmdCopied(false), 1400);
|
|
3575
|
+
});
|
|
3576
|
+
};
|
|
3577
|
+
function statusLine() {
|
|
3578
|
+
if (status === "checking" || view === null) return { text: t("shell.update.checking"), kind: "muted" };
|
|
3579
|
+
if (status === "applying") return { text: t("shell.update.applying", { version: latestVersion }), kind: "muted" };
|
|
3580
|
+
if (status === "restart-required" || status === "applied") return { text: t("shell.update.done", { version: latestVersion }), kind: "ok" };
|
|
3581
|
+
if (status === "failed") return { text: updateState.error?.code === "CHECK_FAILED" ? t("shell.update.checkFailed") : t("shell.update.failed"), kind: "err" };
|
|
3582
|
+
if (view.checkFailed) return { text: t("shell.update.checkFailed"), kind: "err" };
|
|
3583
|
+
if (view.updateAvailable) return { text: t("shell.update.available", { latest: view.latestVersion ?? "" }), kind: "new" };
|
|
3584
|
+
return { text: t("shell.update.latest", { version: currentVersion }), kind: "ok" };
|
|
3585
|
+
}
|
|
3586
|
+
function action() {
|
|
3587
|
+
if (status === "applying") return h(Btn, { small: true, disabled: true }, t("shell.update.applyingShort"));
|
|
3588
|
+
if (actionable) {
|
|
3589
|
+
if (confirming) {
|
|
3590
|
+
return [
|
|
3591
|
+
h(Btn, { key: "yes", small: true, variant: "danger", onClick: () => {
|
|
3592
|
+
setConfirming(false);
|
|
3593
|
+
void controller.apply();
|
|
3594
|
+
} }, t("shell.update.confirmYes")),
|
|
3595
|
+
h(Btn, { key: "no", small: true, onClick: () => setConfirming(false) }, t("shell.update.cancel"))
|
|
3596
|
+
];
|
|
3597
|
+
}
|
|
3598
|
+
return h(Btn, { small: true, variant: "primary", onClick: () => setConfirming(true) }, t("shell.update.apply", { version: latestVersion }));
|
|
3599
|
+
}
|
|
3600
|
+
if (manual !== null) return h(Btn, { small: true, onClick: copyCmd }, cmdCopied ? t("shell.update.copied") : t("shell.update.copyCmd"));
|
|
3601
|
+
return h(Btn, { small: true, disabled: busy, onClick: () => void controller.check({ fresh: true }) }, t("shell.update.check"));
|
|
3602
|
+
}
|
|
3603
|
+
const line = statusLine();
|
|
3604
|
+
return h(
|
|
3605
|
+
"div",
|
|
3606
|
+
{ className: "dshgt-idbar" },
|
|
3607
|
+
h("div", { className: "dshgt-brand" }, logo(), h("span", { className: "dshgt-brand-name" }, t("section.title"))),
|
|
3608
|
+
h("button", { type: "button", className: "dshgt-id-ver", title: t("about.title"), onClick: () => props.onOpenAbout() }, `v${plugin.version}`),
|
|
3609
|
+
h("span", { className: "dshgt-id-sep" }),
|
|
3610
|
+
h(
|
|
3611
|
+
"button",
|
|
3612
|
+
{ type: "button", className: "dshgt-id-pkg dshgt-mono", title: updatedCopyTitle(t, copied), onClick: copyPkg },
|
|
3613
|
+
copied ? t("about.copied") : plugin.name
|
|
3614
|
+
),
|
|
3615
|
+
h(
|
|
3616
|
+
"div",
|
|
3617
|
+
{ className: "dshgt-idbar-right" },
|
|
3618
|
+
plugin.repository === void 0 ? null : h("a", { className: "dshgt-id-gh", href: plugin.repository, target: "_blank", rel: "noreferrer", title: t("about.repo"), "aria-label": t("about.repo") }, githubIcon()),
|
|
3619
|
+
h(
|
|
3620
|
+
"span",
|
|
3621
|
+
{ className: "dshgt-id-upd" },
|
|
3622
|
+
confirming ? h("span", { className: "dshgt-id-status new" }, t("shell.update.confirmHint")) : h("span", { className: `dshgt-id-status ${line.kind}` }, line.text),
|
|
3623
|
+
h("span", { className: "dshgt-id-actions" }, action())
|
|
3624
|
+
)
|
|
3625
|
+
)
|
|
3626
|
+
);
|
|
3627
|
+
}
|
|
3628
|
+
function updatedCopyTitle(t, copied) {
|
|
3629
|
+
return copied ? t("about.copied") : t("about.copy");
|
|
3630
|
+
}
|
|
3631
|
+
|
|
3443
3632
|
// client/panel-icon.ts
|
|
3444
3633
|
var TREND_LINE = "3 16.5 9 10.5 13 14.5 21 6.5";
|
|
3445
3634
|
var TREND_ARROW = "15.5 6.5 21 6.5 21 12";
|
|
@@ -3520,22 +3709,6 @@ function navIcon(name, size = 16) {
|
|
|
3520
3709
|
h("circle", { cx: 10, cy: 17, r: 2.2 })
|
|
3521
3710
|
);
|
|
3522
3711
|
}
|
|
3523
|
-
function brandLogo() {
|
|
3524
|
-
return h(
|
|
3525
|
-
"svg",
|
|
3526
|
-
{ width: 28, height: 28, viewBox: "0 0 64 64", "aria-hidden": "true" },
|
|
3527
|
-
h("rect", { x: 3, y: 3, width: 58, height: 58, rx: 15, fill: "#0d1117", stroke: "rgba(255,255,255,.14)", strokeWidth: 1 }),
|
|
3528
|
-
h("path", {
|
|
3529
|
-
d: "M18 46V22l14 12 14-12v24",
|
|
3530
|
-
fill: "none",
|
|
3531
|
-
stroke: "#58a6ff",
|
|
3532
|
-
strokeWidth: 4,
|
|
3533
|
-
strokeLinecap: "round",
|
|
3534
|
-
strokeLinejoin: "round"
|
|
3535
|
-
}),
|
|
3536
|
-
h("circle", { cx: 32, cy: 45, r: 3, fill: "#3fb950" })
|
|
3537
|
-
);
|
|
3538
|
-
}
|
|
3539
3712
|
function relativeFuture(iso, t) {
|
|
3540
3713
|
if (iso === void 0) return void 0;
|
|
3541
3714
|
const target = new Date(iso).getTime();
|
|
@@ -3581,6 +3754,7 @@ function apply(ctx) {
|
|
|
3581
3754
|
}, []);
|
|
3582
3755
|
const openRuns = import_react.default.useCallback(() => setView("runs"), []);
|
|
3583
3756
|
const openSettings = import_react.default.useCallback(() => setView("settings"), []);
|
|
3757
|
+
const openAbout = import_react.default.useCallback(() => setAboutOpen(true), []);
|
|
3584
3758
|
const state = value.state;
|
|
3585
3759
|
const plugin = state?.plugin;
|
|
3586
3760
|
const history = state?.status.history ?? [];
|
|
@@ -3591,9 +3765,10 @@ function apply(ctx) {
|
|
|
3591
3765
|
state.settings.repo.path.trim() !== "",
|
|
3592
3766
|
state.github.configured
|
|
3593
3767
|
].filter(Boolean).length;
|
|
3594
|
-
const fullName = plugin === void 0 ? void 0 : `${plugin.name}@v${plugin.version}`;
|
|
3595
3768
|
const digestTail = digestDate ?? state?.status.lastResult?.date ?? state?.status.history[0]?.date;
|
|
3596
3769
|
const showActions = view === "overview" || view === "runs";
|
|
3770
|
+
const nextRun = schedule === void 0 || !schedule.enabled ? t("shell.scheduleOff") : schedule.nextRunAt === void 0 ? `${schedule.time} · ${schedule.timezone}` : nextRunText(schedule.nextRunAt);
|
|
3771
|
+
const relNext = schedule !== void 0 && schedule.enabled && schedule.nextRunAt !== void 0 ? relativeFuture(schedule.nextRunAt, t) : void 0;
|
|
3597
3772
|
const viewNode = view === "overview" ? h(OverviewView, {
|
|
3598
3773
|
t,
|
|
3599
3774
|
lang,
|
|
@@ -3616,103 +3791,49 @@ function apply(ctx) {
|
|
|
3616
3791
|
h(
|
|
3617
3792
|
"div",
|
|
3618
3793
|
{ className: "dshgt-app" },
|
|
3794
|
+
plugin === void 0 ? null : h(IdentityBar, { t, plugin, onOpenAbout: openAbout }),
|
|
3619
3795
|
h(
|
|
3620
|
-
"
|
|
3621
|
-
{ className: "dshgt-
|
|
3622
|
-
h(
|
|
3623
|
-
"div",
|
|
3624
|
-
{ className: "dshgt-brand" },
|
|
3625
|
-
brandLogo(),
|
|
3626
|
-
h(
|
|
3627
|
-
"div",
|
|
3628
|
-
null,
|
|
3629
|
-
h("div", { className: "dshgt-brand-name" }, t("section.title")),
|
|
3630
|
-
h("div", { className: "dshgt-brand-ver" }, plugin === void 0 ? "" : `v${plugin.version}`)
|
|
3631
|
-
)
|
|
3632
|
-
),
|
|
3796
|
+
"div",
|
|
3797
|
+
{ className: "dshgt-tabsbar" },
|
|
3633
3798
|
h(
|
|
3634
3799
|
"nav",
|
|
3635
|
-
{ className: "dshgt-
|
|
3800
|
+
{ className: "dshgt-tabnav", "aria-label": t("shell.nav") },
|
|
3636
3801
|
NAV.map(
|
|
3637
3802
|
(item) => h(
|
|
3638
3803
|
"button",
|
|
3639
3804
|
{
|
|
3640
3805
|
key: item.id,
|
|
3641
3806
|
type: "button",
|
|
3642
|
-
className: `dshgt-
|
|
3807
|
+
className: `dshgt-tab${view === item.id ? " on" : ""}`,
|
|
3643
3808
|
"aria-current": view === item.id ? "page" : void 0,
|
|
3644
3809
|
onClick: () => setView(item.id)
|
|
3645
3810
|
},
|
|
3646
3811
|
navIcon(item.icon),
|
|
3647
3812
|
h("span", null, t(item.label)),
|
|
3648
|
-
item.id === "digest" && digestTail !== void 0 ? h("span", { className: "dshgt-
|
|
3813
|
+
item.id === "digest" && digestTail !== void 0 ? h("span", { className: "dshgt-tab-tail dshgt-mono" }, digestTail.slice(5)) : item.id === "runs" && history.length > 0 ? h("span", { className: "dshgt-tab-tail dshgt-mono" }, String(history.length)) : null
|
|
3649
3814
|
)
|
|
3650
3815
|
)
|
|
3651
3816
|
),
|
|
3652
|
-
h("div", { className: "dshgt-rail-spacer" }),
|
|
3653
3817
|
h(
|
|
3654
3818
|
"div",
|
|
3655
|
-
{ className: "dshgt-
|
|
3656
|
-
h("div", { className: "dshgt-rail-cap" }, t("shell.nextRun")),
|
|
3657
|
-
h(
|
|
3658
|
-
"div",
|
|
3659
|
-
{ className: "dshgt-rail-time" },
|
|
3660
|
-
schedule === void 0 || !schedule.enabled ? t("shell.scheduleOff") : schedule.nextRunAt === void 0 ? `${schedule.time} · ${schedule.timezone}` : nextRunText(schedule.nextRunAt)
|
|
3661
|
-
),
|
|
3662
|
-
h(
|
|
3663
|
-
"div",
|
|
3664
|
-
{ className: "dshgt-rail-count" },
|
|
3665
|
-
schedule !== void 0 && schedule.enabled && schedule.nextRunAt !== void 0 ? relativeFuture(schedule.nextRunAt, t) ?? "" : schedule === void 0 ? "" : `${schedule.time} · ${schedule.timezone}`
|
|
3666
|
-
),
|
|
3819
|
+
{ className: "dshgt-tabsbar-status" },
|
|
3667
3820
|
h(
|
|
3668
|
-
"
|
|
3669
|
-
{ className: "dshgt-
|
|
3821
|
+
"span",
|
|
3822
|
+
{ className: "dshgt-status-item" },
|
|
3670
3823
|
h("span", { className: `dshgt-lamp ${ready === 4 ? "ok" : ready > 0 ? "warn" : "err"}` }),
|
|
3671
3824
|
t("shell.ready", { ready, total: 4 })
|
|
3672
|
-
)
|
|
3673
|
-
),
|
|
3674
|
-
h(
|
|
3675
|
-
"div",
|
|
3676
|
-
{ className: "dshgt-rail-pkg" },
|
|
3677
|
-
h(
|
|
3678
|
-
"button",
|
|
3679
|
-
{
|
|
3680
|
-
type: "button",
|
|
3681
|
-
className: "dshgt-pkg-name",
|
|
3682
|
-
onClick: () => setAboutOpen(true),
|
|
3683
|
-
title: plugin === void 0 ? "" : fullName
|
|
3684
|
-
},
|
|
3685
|
-
plugin?.name ?? t("section.title"),
|
|
3686
|
-
h(UpdateDot, { t })
|
|
3687
3825
|
),
|
|
3688
3826
|
h(
|
|
3689
|
-
"
|
|
3690
|
-
{ className: "dshgt-
|
|
3691
|
-
h("span", { className: "dshgt-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
{ href: plugin.repository, target: "_blank", rel: "noreferrer" },
|
|
3695
|
-
`${t("shell.github")} ↗`
|
|
3696
|
-
),
|
|
3697
|
-
h(
|
|
3698
|
-
"button",
|
|
3699
|
-
{
|
|
3700
|
-
type: "button",
|
|
3701
|
-
className: "dshgt-pkg-copy",
|
|
3702
|
-
title: t("shell.package.copy"),
|
|
3703
|
-
"aria-label": t("shell.package.copy"),
|
|
3704
|
-
disabled: fullName === void 0,
|
|
3705
|
-
onClick: () => {
|
|
3706
|
-
if (fullName !== void 0) void copyText(fullName);
|
|
3707
|
-
}
|
|
3708
|
-
},
|
|
3709
|
-
navIcon("doc", 13)
|
|
3710
|
-
)
|
|
3827
|
+
"span",
|
|
3828
|
+
{ className: "dshgt-status-item dshgt-next" },
|
|
3829
|
+
h("span", { className: "dshgt-status-key" }, t("shell.nextRun")),
|
|
3830
|
+
h("span", { className: "dshgt-status-val dshgt-mono" }, nextRun),
|
|
3831
|
+
relNext === void 0 ? null : h("span", { className: "dshgt-status-sub" }, relNext)
|
|
3711
3832
|
)
|
|
3712
3833
|
)
|
|
3713
3834
|
),
|
|
3714
3835
|
h(
|
|
3715
|
-
"
|
|
3836
|
+
"div",
|
|
3716
3837
|
{ className: "dshgt-main" },
|
|
3717
3838
|
h(
|
|
3718
3839
|
"header",
|
|
@@ -9,9 +9,11 @@ var config_exports = {};
|
|
|
9
9
|
__export(config_exports, {
|
|
10
10
|
CORE_PROVIDERS: () => CORE_PROVIDERS,
|
|
11
11
|
DEFAULT_SETTINGS: () => DEFAULT_SETTINGS,
|
|
12
|
+
PERIODS: () => PERIODS,
|
|
12
13
|
PROVIDER_BASE_URLS: () => PROVIDER_BASE_URLS,
|
|
13
14
|
SETTINGS_SECTIONS: () => SETTINGS_SECTIONS,
|
|
14
15
|
effectiveBaseURL: () => effectiveBaseURL,
|
|
16
|
+
isPeriod: () => isPeriod,
|
|
15
17
|
isValidTimezone: () => isValidTimezone,
|
|
16
18
|
mergeSettingsSection: () => mergeSettingsSection,
|
|
17
19
|
normalizeSettings: () => normalizeSettings,
|
|
@@ -21,6 +23,10 @@ __export(config_exports, {
|
|
|
21
23
|
validateSettings: () => validateSettings
|
|
22
24
|
});
|
|
23
25
|
import path from "path";
|
|
26
|
+
var PERIODS = ["daily", "weekly", "monthly"];
|
|
27
|
+
function isPeriod(value) {
|
|
28
|
+
return value === "daily" || value === "weekly" || value === "monthly";
|
|
29
|
+
}
|
|
24
30
|
var DEFAULT_SETTINGS = {
|
|
25
31
|
model: {
|
|
26
32
|
provider: "deepseek",
|
|
@@ -50,7 +56,7 @@ var DEFAULT_SETTINGS = {
|
|
|
50
56
|
github: { authMode: "gh", tokenRef: "GITHUB_TOKEN" },
|
|
51
57
|
trending: { since: "daily", languages: ["all", "typescript", "python", "go", "rust"], topN: 25 },
|
|
52
58
|
output: { dir: "trending" },
|
|
53
|
-
schedule: { enabled: true, time: "08:30:00", timezone: "Asia/Shanghai" },
|
|
59
|
+
schedule: { enabled: true, time: "08:30:00", timezone: "Asia/Shanghai", periods: ["daily"] },
|
|
54
60
|
corePath: "",
|
|
55
61
|
coreAutoDownload: true
|
|
56
62
|
};
|
|
@@ -111,6 +117,11 @@ function strArray(value, fallback) {
|
|
|
111
117
|
const items = value.filter((item) => typeof item === "string" && item.trim() !== "");
|
|
112
118
|
return items.length > 0 ? items.map((item) => item.trim()) : fallback;
|
|
113
119
|
}
|
|
120
|
+
function periodList(value, fallback) {
|
|
121
|
+
if (value === void 0 || !Array.isArray(value)) return [...fallback];
|
|
122
|
+
const set = new Set(value.filter(isPeriod));
|
|
123
|
+
return PERIODS.filter((period) => set.has(period));
|
|
124
|
+
}
|
|
114
125
|
function normalizeSettings(input) {
|
|
115
126
|
const raw = typeof input === "object" && input !== null ? input : {};
|
|
116
127
|
const model = raw.model ?? {};
|
|
@@ -155,7 +166,7 @@ function normalizeSettings(input) {
|
|
|
155
166
|
tokenRef: str(github.tokenRef, DEFAULT_SETTINGS.github.tokenRef).trim() || DEFAULT_SETTINGS.github.tokenRef
|
|
156
167
|
},
|
|
157
168
|
trending: {
|
|
158
|
-
since: since
|
|
169
|
+
since: isPeriod(since) ? since : "daily",
|
|
159
170
|
languages: strArray(trending.languages, DEFAULT_SETTINGS.trending.languages),
|
|
160
171
|
topN: Math.round(num(trending.topN, DEFAULT_SETTINGS.trending.topN))
|
|
161
172
|
},
|
|
@@ -165,7 +176,8 @@ function normalizeSettings(input) {
|
|
|
165
176
|
schedule: {
|
|
166
177
|
enabled: bool(schedule.enabled, DEFAULT_SETTINGS.schedule.enabled),
|
|
167
178
|
time: str(schedule.time, DEFAULT_SETTINGS.schedule.time),
|
|
168
|
-
timezone: str(schedule.timezone, DEFAULT_SETTINGS.schedule.timezone)
|
|
179
|
+
timezone: str(schedule.timezone, DEFAULT_SETTINGS.schedule.timezone),
|
|
180
|
+
periods: periodList(schedule.periods, DEFAULT_SETTINGS.schedule.periods)
|
|
169
181
|
},
|
|
170
182
|
corePath: str(raw.corePath, DEFAULT_SETTINGS.corePath).trim(),
|
|
171
183
|
coreAutoDownload: bool(raw.coreAutoDownload, DEFAULT_SETTINGS.coreAutoDownload)
|
|
@@ -231,12 +243,15 @@ function validateSettings(settings, providers = CORE_PROVIDERS) {
|
|
|
231
243
|
if (!isValidTimezone(schedule.timezone)) {
|
|
232
244
|
problems.push({ field: "schedule.timezone", code: "CONFIG_INVALID", message: "\u65F6\u533A\u5FC5\u987B\u662F IANA \u540D\u79F0\uFF08\u5982 Asia/Shanghai\uFF09" });
|
|
233
245
|
}
|
|
246
|
+
if (schedule.enabled && schedule.periods.length === 0) {
|
|
247
|
+
problems.push({ field: "schedule.periods", code: "CONFIG_INVALID", message: "\u5B9A\u65F6\u8FD0\u884C\u5F00\u542F\u65F6\u81F3\u5C11\u8981\u9009\u62E9\u4E00\u4E2A\u4EA7\u51FA\u5468\u671F\uFF08\u65E5/\u5468/\u6708\uFF09" });
|
|
248
|
+
}
|
|
234
249
|
return problems;
|
|
235
250
|
}
|
|
236
251
|
function effectiveBaseURL(model) {
|
|
237
252
|
return model.baseURL !== "" ? model.baseURL : PROVIDER_BASE_URLS[model.provider] ?? "";
|
|
238
253
|
}
|
|
239
|
-
function toCoreConfig(settings, apiKey, baseURLOverride) {
|
|
254
|
+
function toCoreConfig(settings, apiKey, baseURLOverride, period) {
|
|
240
255
|
const config = {
|
|
241
256
|
model: {
|
|
242
257
|
provider: settings.model.provider,
|
|
@@ -262,7 +277,7 @@ function toCoreConfig(settings, apiKey, baseURLOverride) {
|
|
|
262
277
|
},
|
|
263
278
|
trending: {
|
|
264
279
|
languages: settings.trending.languages,
|
|
265
|
-
since: settings.trending.since,
|
|
280
|
+
since: period ?? settings.trending.since,
|
|
266
281
|
perLanguage: settings.trending.topN
|
|
267
282
|
},
|
|
268
283
|
output: {
|
|
@@ -1339,6 +1354,7 @@ function buildCoreArgs(channelPath, options) {
|
|
|
1339
1354
|
const args = ["run", "--config", channelPath, "--json"];
|
|
1340
1355
|
if (options.dryRun) args.push("--dry-run");
|
|
1341
1356
|
if (options.date !== void 0 && options.date !== "") args.push("--date", options.date);
|
|
1357
|
+
if (options.period !== void 0) args.push("--period", options.period);
|
|
1342
1358
|
return args;
|
|
1343
1359
|
}
|
|
1344
1360
|
async function runCore(options) {
|
|
@@ -1346,10 +1362,10 @@ async function runCore(options) {
|
|
|
1346
1362
|
const startedAt = nowIso();
|
|
1347
1363
|
const started = Date.now();
|
|
1348
1364
|
const channel = await openConfigChannel();
|
|
1349
|
-
const yaml = `${toYaml(toCoreConfig(options.settings, options.apiKey, options.baseURLOverride))}
|
|
1365
|
+
const yaml = `${toYaml(toCoreConfig(options.settings, options.apiKey, options.baseURLOverride, options.period))}
|
|
1350
1366
|
`;
|
|
1351
1367
|
const secrets = [options.apiKey, options.token, ...options.secrets ?? []];
|
|
1352
|
-
const child = spawn2(options.core.path, buildCoreArgs(channel.path, { dryRun: options.dryRun, date: options.date }), {
|
|
1368
|
+
const child = spawn2(options.core.path, buildCoreArgs(channel.path, { dryRun: options.dryRun, date: options.date, period: options.period }), {
|
|
1353
1369
|
env: sanitizedEnv(process.env, {
|
|
1354
1370
|
GITHUB_TOKEN: options.token,
|
|
1355
1371
|
GH_TOKEN: options.token,
|
|
@@ -1985,8 +2001,11 @@ function authorized(request, token) {
|
|
|
1985
2001
|
var schedule_exports = {};
|
|
1986
2002
|
__export(schedule_exports, {
|
|
1987
2003
|
Scheduler: () => Scheduler,
|
|
2004
|
+
daysInMonth: () => daysInMonth,
|
|
1988
2005
|
isDue: () => isDue,
|
|
2006
|
+
isPeriodTriggerDay: () => isPeriodTriggerDay,
|
|
1989
2007
|
nextOccurrence: () => nextOccurrence,
|
|
2008
|
+
nextOccurrenceFor: () => nextOccurrenceFor,
|
|
1990
2009
|
parseClock: () => parseClock,
|
|
1991
2010
|
zonedDateString: () => zonedDateString,
|
|
1992
2011
|
zonedTimeToUtc: () => zonedTimeToUtc
|
|
@@ -2066,6 +2085,35 @@ function isDue(now, schedule) {
|
|
|
2066
2085
|
const late = nowMinutes - targetMinutes;
|
|
2067
2086
|
return late >= 0 && late <= CATCH_UP_MINUTES;
|
|
2068
2087
|
}
|
|
2088
|
+
function daysInMonth(year, month) {
|
|
2089
|
+
return new Date(Date.UTC(year, month, 0)).getUTCDate();
|
|
2090
|
+
}
|
|
2091
|
+
function isPeriodTriggerDate(period, year, month, day) {
|
|
2092
|
+
if (period === "weekly") {
|
|
2093
|
+
return new Date(Date.UTC(year, month - 1, day)).getUTCDay() === 0;
|
|
2094
|
+
}
|
|
2095
|
+
if (period === "monthly") return day === daysInMonth(year, month);
|
|
2096
|
+
return true;
|
|
2097
|
+
}
|
|
2098
|
+
function isPeriodTriggerDay(period, now, timezone) {
|
|
2099
|
+
const parts = partsOf(now, timezone);
|
|
2100
|
+
return isPeriodTriggerDate(period, parts.year, parts.month, parts.day);
|
|
2101
|
+
}
|
|
2102
|
+
function nextOccurrenceFor(period, now, schedule) {
|
|
2103
|
+
const { hour, minute, second } = parseClock(schedule.time);
|
|
2104
|
+
const today = partsOf(now, schedule.timezone);
|
|
2105
|
+
const start = Date.UTC(today.year, today.month - 1, today.day);
|
|
2106
|
+
for (let i = 0; i < 400; i += 1) {
|
|
2107
|
+
const d = new Date(start + i * 864e5);
|
|
2108
|
+
const year = d.getUTCFullYear();
|
|
2109
|
+
const month = d.getUTCMonth() + 1;
|
|
2110
|
+
const day = d.getUTCDate();
|
|
2111
|
+
if (!isPeriodTriggerDate(period, year, month, day)) continue;
|
|
2112
|
+
const candidate = zonedTimeToUtc(year, month, day, hour, minute, second, schedule.timezone);
|
|
2113
|
+
if (candidate.getTime() > now.getTime()) return candidate;
|
|
2114
|
+
}
|
|
2115
|
+
return nextOccurrence(now, schedule);
|
|
2116
|
+
}
|
|
2069
2117
|
var Scheduler = class {
|
|
2070
2118
|
deps;
|
|
2071
2119
|
timerHandle;
|
|
@@ -2104,41 +2152,63 @@ var Scheduler = class {
|
|
|
2104
2152
|
if (!schedule.enabled) return "disabled";
|
|
2105
2153
|
if (!isDue(now, schedule)) return "skipped";
|
|
2106
2154
|
const today = zonedDateString(now, schedule.timezone);
|
|
2107
|
-
const state = this.deps.store.readState();
|
|
2108
|
-
const lastSuccess = state.lastSuccessAt === void 0 ? void 0 : zonedDateString(new Date(state.lastSuccessAt), schedule.timezone);
|
|
2109
|
-
if (lastSuccess === today) return "skipped";
|
|
2110
|
-
const attempt = this.attempts.get(today);
|
|
2111
|
-
if (attempt !== void 0) {
|
|
2112
|
-
if (attempt.count >= MAX_ATTEMPTS_PER_DAY) return "skipped";
|
|
2113
|
-
if (now.getTime() - attempt.lastAt < RETRY_GAP_MS) return "skipped";
|
|
2114
|
-
}
|
|
2115
|
-
if (this.deps.isRunning()) return "busy";
|
|
2116
2155
|
for (const key of this.attempts.keys()) {
|
|
2117
|
-
if (key
|
|
2156
|
+
if (!key.endsWith(`:${today}`)) this.attempts.delete(key);
|
|
2157
|
+
}
|
|
2158
|
+
const periods = schedule.periods.length > 0 ? schedule.periods : ["daily"];
|
|
2159
|
+
let ran = false;
|
|
2160
|
+
for (const period of periods) {
|
|
2161
|
+
if (!isPeriodTriggerDay(period, now, schedule.timezone)) continue;
|
|
2162
|
+
const state = this.deps.store.readState();
|
|
2163
|
+
if (this.succeededToday(state, period, today, schedule.timezone)) continue;
|
|
2164
|
+
const key = `${period}:${today}`;
|
|
2165
|
+
const attempt = this.attempts.get(key);
|
|
2166
|
+
if (attempt !== void 0) {
|
|
2167
|
+
if (attempt.count >= MAX_ATTEMPTS_PER_DAY) continue;
|
|
2168
|
+
if (now.getTime() - attempt.lastAt < RETRY_GAP_MS) continue;
|
|
2169
|
+
}
|
|
2170
|
+
if (this.deps.isRunning()) return "busy";
|
|
2171
|
+
this.attempts.set(key, { count: (attempt?.count ?? 0) + 1, lastAt: now.getTime() });
|
|
2172
|
+
try {
|
|
2173
|
+
await this.deps.run("schedule", period);
|
|
2174
|
+
ran = true;
|
|
2175
|
+
} catch (error) {
|
|
2176
|
+
this.deps.onError?.(error);
|
|
2177
|
+
}
|
|
2118
2178
|
}
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2179
|
+
return ran ? "ran" : "skipped";
|
|
2180
|
+
}
|
|
2181
|
+
/** 该周期今天是否已经成功过(含无变更跳过)。 */
|
|
2182
|
+
succeededToday(state, period, today, timezone) {
|
|
2183
|
+
const stamp = state.periodSuccess?.[period];
|
|
2184
|
+
if (stamp !== void 0 && zonedDateString(new Date(stamp), timezone) === today) return true;
|
|
2185
|
+
if (period === "daily" && state.lastSuccessAt !== void 0 && zonedDateString(new Date(state.lastSuccessAt), timezone) === today) {
|
|
2186
|
+
return true;
|
|
2126
2187
|
}
|
|
2188
|
+
return false;
|
|
2127
2189
|
}
|
|
2128
2190
|
readSchedule() {
|
|
2129
2191
|
const settings = this.deps.store.readSettings();
|
|
2130
2192
|
return {
|
|
2131
2193
|
enabled: settings.schedule.enabled,
|
|
2132
2194
|
time: settings.schedule.time,
|
|
2133
|
-
timezone: settings.schedule.timezone
|
|
2195
|
+
timezone: settings.schedule.timezone,
|
|
2196
|
+
periods: settings.schedule.periods
|
|
2134
2197
|
};
|
|
2135
2198
|
}
|
|
2136
|
-
/**
|
|
2199
|
+
/** 下一次运行(取所有已启用周期里最近的一次;禁用时为 undefined)。 */
|
|
2137
2200
|
next() {
|
|
2138
2201
|
const schedule = this.readSchedule();
|
|
2139
2202
|
if (!schedule.enabled) return void 0;
|
|
2203
|
+
const periods = schedule.periods.length > 0 ? schedule.periods : ["daily"];
|
|
2140
2204
|
try {
|
|
2141
|
-
|
|
2205
|
+
const now = /* @__PURE__ */ new Date();
|
|
2206
|
+
let earliest;
|
|
2207
|
+
for (const period of periods) {
|
|
2208
|
+
const occurrence = nextOccurrenceFor(period, now, schedule);
|
|
2209
|
+
if (earliest === void 0 || occurrence.getTime() < earliest.getTime()) earliest = occurrence;
|
|
2210
|
+
}
|
|
2211
|
+
return earliest;
|
|
2142
2212
|
} catch {
|
|
2143
2213
|
return void 0;
|
|
2144
2214
|
}
|
|
@@ -2150,6 +2220,7 @@ var Scheduler = class {
|
|
|
2150
2220
|
enabled: schedule.enabled,
|
|
2151
2221
|
time: schedule.time,
|
|
2152
2222
|
timezone: schedule.timezone,
|
|
2223
|
+
periods: schedule.periods,
|
|
2153
2224
|
...next === void 0 ? {} : { nextRunAt: next.toISOString() },
|
|
2154
2225
|
...state.lastRunAt === void 0 ? {} : { lastRunDate: zonedDateString(new Date(state.lastRunAt), schedule.timezone) },
|
|
2155
2226
|
catchUpMinutes: CATCH_UP_MINUTES,
|
|
@@ -2161,7 +2232,9 @@ var Scheduler = class {
|
|
|
2161
2232
|
// src/host/runner.ts
|
|
2162
2233
|
var runner_exports = {};
|
|
2163
2234
|
__export(runner_exports, {
|
|
2164
|
-
Runner: () => Runner
|
|
2235
|
+
Runner: () => Runner,
|
|
2236
|
+
isoWeekName: () => isoWeekName,
|
|
2237
|
+
periodFileBase: () => periodFileBase
|
|
2165
2238
|
});
|
|
2166
2239
|
import { createHash as createHash2 } from "crypto";
|
|
2167
2240
|
import { existsSync as existsSync4, readFileSync as readFileSync3, realpathSync, statSync as statSync5 } from "fs";
|
|
@@ -2195,6 +2268,25 @@ function normalizeSha256(value) {
|
|
|
2195
2268
|
const hex = value.trim().replace(/^sha256:/i, "").toLowerCase();
|
|
2196
2269
|
return /^[0-9a-f]{64}$/.test(hex) ? hex : void 0;
|
|
2197
2270
|
}
|
|
2271
|
+
function isoWeekName(date) {
|
|
2272
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date);
|
|
2273
|
+
if (match === null) return date;
|
|
2274
|
+
const d = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])));
|
|
2275
|
+
if (Number.isNaN(d.getTime())) return date;
|
|
2276
|
+
const dayIdx = (d.getUTCDay() + 6) % 7;
|
|
2277
|
+
d.setUTCDate(d.getUTCDate() - dayIdx + 3);
|
|
2278
|
+
const isoYear = d.getUTCFullYear();
|
|
2279
|
+
const firstThursday = new Date(Date.UTC(isoYear, 0, 4));
|
|
2280
|
+
const firstIdx = (firstThursday.getUTCDay() + 6) % 7;
|
|
2281
|
+
firstThursday.setUTCDate(firstThursday.getUTCDate() - firstIdx + 3);
|
|
2282
|
+
const week = 1 + Math.round((d.getTime() - firstThursday.getTime()) / (7 * 864e5));
|
|
2283
|
+
return `${isoYear}-W${String(week).padStart(2, "0")}`;
|
|
2284
|
+
}
|
|
2285
|
+
function periodFileBase(period, date) {
|
|
2286
|
+
if (period === "monthly") return date.slice(0, 7);
|
|
2287
|
+
if (period === "weekly") return isoWeekName(date);
|
|
2288
|
+
return date;
|
|
2289
|
+
}
|
|
2198
2290
|
var Runner = class {
|
|
2199
2291
|
active;
|
|
2200
2292
|
deps;
|
|
@@ -2226,6 +2318,7 @@ var Runner = class {
|
|
|
2226
2318
|
if (repoIssue !== void 0) throw new IpcError(repoIssue.code, repoIssue.message);
|
|
2227
2319
|
const dryRun = options.dryRun === true;
|
|
2228
2320
|
const date = options.date !== void 0 && options.date !== "" ? options.date : runDate(settings);
|
|
2321
|
+
const period = options.period ?? settings.trending.since;
|
|
2229
2322
|
const id = newRunId();
|
|
2230
2323
|
const controller = new AbortController();
|
|
2231
2324
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -2282,6 +2375,7 @@ var Runner = class {
|
|
|
2282
2375
|
apiKey,
|
|
2283
2376
|
...github.token === void 0 ? {} : { token: github.token },
|
|
2284
2377
|
date,
|
|
2378
|
+
period,
|
|
2285
2379
|
dryRun,
|
|
2286
2380
|
trigger: options.trigger === "schedule" ? "schedule" : "manual",
|
|
2287
2381
|
...baseURLOverride === void 0 ? {} : { baseURLOverride },
|
|
@@ -2295,6 +2389,7 @@ var Runner = class {
|
|
|
2295
2389
|
outcome,
|
|
2296
2390
|
dryRun,
|
|
2297
2391
|
date,
|
|
2392
|
+
period,
|
|
2298
2393
|
logFile,
|
|
2299
2394
|
channel: result.channelKind,
|
|
2300
2395
|
proxy: proxy !== void 0,
|
|
@@ -2304,6 +2399,7 @@ var Runner = class {
|
|
|
2304
2399
|
const preview = this.capturePreview({
|
|
2305
2400
|
id,
|
|
2306
2401
|
date,
|
|
2402
|
+
period,
|
|
2307
2403
|
settings,
|
|
2308
2404
|
outcome,
|
|
2309
2405
|
secrets: [apiKey, github.token]
|
|
@@ -2318,6 +2414,7 @@ var Runner = class {
|
|
|
2318
2414
|
error: cancelled && !(error instanceof IpcError && error.code === "CORE_CANCELLED") ? new IpcError("CORE_CANCELLED", "\u8FD0\u884C\u5DF2\u53D6\u6D88\uFF1A\u6838\u5FC3\u8FDB\u7A0B\u5DF2\u7EC8\u6B62\uFF0C\u672A\u63D0\u4EA4\u4EFB\u4F55\u72B6\u6001") : error,
|
|
2319
2415
|
dryRun,
|
|
2320
2416
|
date,
|
|
2417
|
+
period,
|
|
2321
2418
|
logFile,
|
|
2322
2419
|
durationMs: Date.now() - startedMs
|
|
2323
2420
|
});
|
|
@@ -2353,6 +2450,7 @@ var Runner = class {
|
|
|
2353
2450
|
skipped: outcome.skipped,
|
|
2354
2451
|
dryRun: input.dryRun,
|
|
2355
2452
|
...input.date === void 0 ? {} : { date: input.date },
|
|
2453
|
+
...input.period === void 0 ? {} : { period: input.period },
|
|
2356
2454
|
durationMs: outcome.durationMs,
|
|
2357
2455
|
coreVersion: outcome.coreVersion,
|
|
2358
2456
|
files: outcome.files.length,
|
|
@@ -2379,6 +2477,7 @@ var Runner = class {
|
|
|
2379
2477
|
skipped: false,
|
|
2380
2478
|
dryRun: input.dryRun,
|
|
2381
2479
|
...input.date === void 0 ? {} : { date: input.date },
|
|
2480
|
+
...input.period === void 0 ? {} : { period: input.period },
|
|
2382
2481
|
durationMs: input.durationMs ?? 0,
|
|
2383
2482
|
coreVersion: "",
|
|
2384
2483
|
files: 0,
|
|
@@ -2396,7 +2495,7 @@ var Runner = class {
|
|
|
2396
2495
|
* - 任何一步失败都只记日志,返回 undefined;运行结果不受影响。
|
|
2397
2496
|
*/
|
|
2398
2497
|
capturePreview(input) {
|
|
2399
|
-
const { outcome, settings, date, id } = input;
|
|
2498
|
+
const { outcome, settings, date, period, id } = input;
|
|
2400
2499
|
if (!outcome.ok || outcome.dryRun || outcome.skipped || date === void 0) return void 0;
|
|
2401
2500
|
const skip = (reason, extra = {}) => {
|
|
2402
2501
|
this.deps.logger?.("preview.skip", { id, reason, ...extra });
|
|
@@ -2404,12 +2503,14 @@ var Runner = class {
|
|
|
2404
2503
|
};
|
|
2405
2504
|
const markdown = outcome.files.filter((file) => {
|
|
2406
2505
|
const ext = path8.extname(file.path).toLowerCase();
|
|
2407
|
-
|
|
2506
|
+
const base = path8.basename(file.path).toLowerCase();
|
|
2507
|
+
return ext === ".md" && base !== "readme.md" && base !== "index.md";
|
|
2408
2508
|
});
|
|
2409
2509
|
if (markdown.length === 0) return skip("no-markdown");
|
|
2410
|
-
const
|
|
2411
|
-
const
|
|
2412
|
-
|
|
2510
|
+
const expected = period === void 0 ? `${date}.md` : `${periodFileBase(period, date)}.md`;
|
|
2511
|
+
const named = markdown.filter((file) => path8.basename(file.path) === expected);
|
|
2512
|
+
const chosen = named.length > 0 ? named[0] : markdown.length === 1 ? markdown[0] : void 0;
|
|
2513
|
+
if (chosen === void 0) return skip("ambiguous", { candidates: markdown.length, expected, period: period ?? null });
|
|
2413
2514
|
const repoPath = settings.repo.path;
|
|
2414
2515
|
const resolved = path8.resolve(repoPath, chosen.path);
|
|
2415
2516
|
if (!isInside(repoPath, resolved)) return skip("outside-repo", { path: chosen.path });
|
|
@@ -2467,6 +2568,7 @@ var Runner = class {
|
|
|
2467
2568
|
durationMs: summary.durationMs,
|
|
2468
2569
|
files: summary.files,
|
|
2469
2570
|
...summary.date === void 0 ? {} : { date: summary.date },
|
|
2571
|
+
...summary.period === void 0 ? {} : { period: summary.period },
|
|
2470
2572
|
...summary.contentHash === void 0 ? {} : { contentHash: summary.contentHash },
|
|
2471
2573
|
...summary.branch === void 0 ? {} : { branch: summary.branch },
|
|
2472
2574
|
...summary.commit === void 0 ? {} : { commit: summary.commit },
|
|
@@ -2479,9 +2581,12 @@ var Runner = class {
|
|
|
2479
2581
|
logFile: summary.logFile
|
|
2480
2582
|
};
|
|
2481
2583
|
const state = store.readState();
|
|
2584
|
+
const periodSuccess = { ...state.periodSuccess ?? {} };
|
|
2585
|
+
if (summary.ok && summary.period !== void 0) periodSuccess[summary.period] = record.at;
|
|
2482
2586
|
const next = {
|
|
2483
2587
|
lastRunAt: record.at,
|
|
2484
2588
|
...summary.ok ? { lastSuccessAt: record.at } : {},
|
|
2589
|
+
...summary.ok && summary.period !== void 0 ? { periodSuccess } : {},
|
|
2485
2590
|
...summary.ok && summary.prUrl !== void 0 ? { pendingPr: { url: summary.prUrl, createdAt: record.at, ...summary.date === void 0 ? {} : { date: summary.date } } } : {},
|
|
2486
2591
|
...summary.ok && !summary.skipped && summary.contentHash !== void 0 ? {
|
|
2487
2592
|
lastDigest: {
|
|
@@ -3224,6 +3329,7 @@ async function dispatch(method, pathname, url, request, deps) {
|
|
|
3224
3329
|
const summary = await deps.runner.run({
|
|
3225
3330
|
dryRun: body.dryRun === true,
|
|
3226
3331
|
...typeof body.date === "string" && body.date !== "" ? { date: body.date } : {},
|
|
3332
|
+
...isPeriod(body.period) ? { period: body.period } : {},
|
|
3227
3333
|
trigger: "manual"
|
|
3228
3334
|
});
|
|
3229
3335
|
return { summary };
|
|
@@ -3496,10 +3602,11 @@ function describeRecord(record) {
|
|
|
3496
3602
|
function createTrendingRunTool(runner) {
|
|
3497
3603
|
return {
|
|
3498
3604
|
name: "trending_run",
|
|
3499
|
-
description: "\u6267\u884C\u4E00\u6B21 GitHub \u8D8B\u52BF\
|
|
3605
|
+
description: "\u6267\u884C\u4E00\u6B21 GitHub \u8D8B\u52BF\u699C\u5355\uFF1A\u5B9A\u4F4D\u5E76\u63E1\u624B\u672C\u5730\u6838\u5FC3\uFF0Cspawn \u4E00\u6B21 run\uFF0C\u8FD4\u56DE\u7ED3\u6784\u5316\u6458\u8981\uFF08\u6587\u4EF6\u6570\u3001commit\u3001PR\u3001\u9519\u8BEF\u7801\uFF09\u3002dryRun=true \u53EA\u6293\u53D6\u4E0E\u6E32\u67D3\u3001\u4E0D\u5199\u6587\u4EF6\u4E5F\u4E0D\u63D0\u4EA4\u3002\u540C\u65E5\u540C\u5185\u5BB9\u91CD\u8DD1\u4F1A\u8FD4\u56DE skipped=true\uFF08\u96F6\u65B0\u589E commit\uFF09\u3002",
|
|
3500
3606
|
parameters: {
|
|
3501
3607
|
dryRun: { type: "boolean", description: "\u53EA\u8BD5\u8DD1\uFF1A\u4E0D\u5199\u6587\u4EF6\u3001\u4E0D\u63D0\u4EA4\u3001\u4E0D\u5F00 PR" },
|
|
3502
|
-
date: { type: "string", description: "\u6307\u5B9A\u65E5\u671F\uFF08YYYY-MM-DD\uFF09\uFF1B\u7701\u7565\u5219\u7528\u8C03\u5EA6\u65F6\u533A\u7684\u4ECA\u5929" }
|
|
3608
|
+
date: { type: "string", description: "\u6307\u5B9A\u65E5\u671F\uFF08YYYY-MM-DD\uFF09\uFF1B\u7701\u7565\u5219\u7528\u8C03\u5EA6\u65F6\u533A\u7684\u4ECA\u5929" },
|
|
3609
|
+
period: { type: "string", enum: ["daily", "weekly", "monthly"], description: "\u4EA7\u51FA\u5468\u671F\uFF1B\u7701\u7565\u5219\u7528\u8BBE\u7F6E\u91CC\u7684\u9ED8\u8BA4\u5468\u671F" }
|
|
3503
3610
|
},
|
|
3504
3611
|
output: {
|
|
3505
3612
|
schema: RUN_OUTPUT_SCHEMA,
|
|
@@ -3510,6 +3617,7 @@ function createTrendingRunTool(runner) {
|
|
|
3510
3617
|
const summary = await runner.run({
|
|
3511
3618
|
dryRun: args.dryRun === true,
|
|
3512
3619
|
...typeof args.date === "string" && args.date !== "" ? { date: args.date } : {},
|
|
3620
|
+
...isPeriod(args.period) ? { period: args.period } : {},
|
|
3513
3621
|
trigger: "tool"
|
|
3514
3622
|
});
|
|
3515
3623
|
const lines = [];
|
package/lib/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
createTrendingRunTool,
|
|
9
9
|
createUpdateService,
|
|
10
10
|
readPluginInfo
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-QXAKMNHQ.js";
|
|
12
12
|
import "./chunk-XLIFTAVL.js";
|
|
13
13
|
|
|
14
14
|
// src/index.ts
|
|
@@ -53,9 +53,9 @@ function apply(ctx, config = {}) {
|
|
|
53
53
|
store,
|
|
54
54
|
isRunning: () => runner.running !== void 0,
|
|
55
55
|
getTimer: () => get("timer"),
|
|
56
|
-
run: async () => {
|
|
57
|
-
const summary = await runner.run({ trigger: "schedule" });
|
|
58
|
-
log("schedule.run", { ok: summary.ok, skipped: summary.skipped, error: summary.error?.code });
|
|
56
|
+
run: async (_trigger, period) => {
|
|
57
|
+
const summary = await runner.run({ trigger: "schedule", period });
|
|
58
|
+
log("schedule.run", { period, ok: summary.ok, skipped: summary.skipped, error: summary.error?.code });
|
|
59
59
|
},
|
|
60
60
|
onError: (error) => ctx.logger?.warn?.(`[dsh-github-trending] \u5B9A\u65F6\u8FD0\u884C\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`)
|
|
61
61
|
});
|
package/lib/internal.d.ts
CHANGED
|
@@ -164,7 +164,16 @@ type Provider = string;
|
|
|
164
164
|
type CommitMode = 'branch' | 'direct';
|
|
165
165
|
/** 只保留两种真正可用的方式:本机 `gh` 登录态、手动 PAT。历史的 `device` 在归一化时迁移为 `gh`。 */
|
|
166
166
|
type AuthMode = 'pat' | 'gh';
|
|
167
|
-
|
|
167
|
+
/**
|
|
168
|
+
* 榜单周期(core 0.4.0 的 `--period` 三取值)。
|
|
169
|
+
* `trending.since` 复用同一枚举:单次运行「抓取窗口 = 标注周期」由核心保证
|
|
170
|
+
* (`run --period` 会覆盖 `config.trending.since`)。
|
|
171
|
+
*/
|
|
172
|
+
type Period = 'daily' | 'weekly' | 'monthly';
|
|
173
|
+
type Since = Period;
|
|
174
|
+
/** 规范顺序(索引分组、去重、UI 展示都以它为准)。 */
|
|
175
|
+
declare const PERIODS: readonly Period[];
|
|
176
|
+
declare function isPeriod(value: unknown): value is Period;
|
|
168
177
|
interface ModelSettings {
|
|
169
178
|
provider: Provider;
|
|
170
179
|
/** `direct` = 密钥经配置通道直传核心;`proxy` = 宿主 provider 无可导出密钥,走回环代理。 */
|
|
@@ -210,6 +219,11 @@ interface ScheduleSettings {
|
|
|
210
219
|
/** `HH:MM:SS`(本地墙钟,按 timezone 解释)。 */
|
|
211
220
|
time: string;
|
|
212
221
|
timezone: string;
|
|
222
|
+
/**
|
|
223
|
+
* 定时自动产出的周期(插件侧调度概念,核心无此字段)。
|
|
224
|
+
* 每个周期按自己的节奏触发:日=每天、周=周日、月=月末。
|
|
225
|
+
*/
|
|
226
|
+
periods: Period[];
|
|
213
227
|
}
|
|
214
228
|
interface Settings {
|
|
215
229
|
model: ModelSettings;
|
|
@@ -293,9 +307,10 @@ interface CoreConfig {
|
|
|
293
307
|
}
|
|
294
308
|
/**
|
|
295
309
|
* 生成核心配置。`apiKey` 单独传入:它只能来自宿主凭据或用户当次输入,
|
|
296
|
-
* 绝不来自 settings.json
|
|
310
|
+
* 绝不来自 settings.json。`period` 是本次运行的标的周期;核心 `run --period`
|
|
311
|
+
* 会用它覆盖 `trending.since`,这里同步写入以免旧核心/调试时口径不一致。
|
|
297
312
|
*/
|
|
298
|
-
declare function toCoreConfig(settings: Settings, apiKey: string, baseURLOverride?: string): CoreConfig;
|
|
313
|
+
declare function toCoreConfig(settings: Settings, apiKey: string, baseURLOverride?: string, period?: Period): CoreConfig;
|
|
299
314
|
/**
|
|
300
315
|
* 极简 YAML 序列化:只覆盖「对象/数组/字符串/数字/布尔」。
|
|
301
316
|
* 字符串一律用 JSON 双引号形式——JSON 字符串是合法 YAML 标量,且能避免
|
|
@@ -311,8 +326,10 @@ declare const config_DEFAULT_SETTINGS: typeof DEFAULT_SETTINGS;
|
|
|
311
326
|
type config_GithubSettings = GithubSettings;
|
|
312
327
|
type config_ModelSettings = ModelSettings;
|
|
313
328
|
type config_OutputSettings = OutputSettings;
|
|
329
|
+
declare const config_PERIODS: typeof PERIODS;
|
|
314
330
|
declare const config_PROVIDER_BASE_URLS: typeof PROVIDER_BASE_URLS;
|
|
315
331
|
type config_PRSettings = PRSettings;
|
|
332
|
+
type config_Period = Period;
|
|
316
333
|
type config_Problem = Problem;
|
|
317
334
|
type config_Provider = Provider;
|
|
318
335
|
type config_RepoSettings = RepoSettings;
|
|
@@ -323,6 +340,7 @@ type config_SettingsSection = SettingsSection;
|
|
|
323
340
|
type config_Since = Since;
|
|
324
341
|
type config_TrendingSettings = TrendingSettings;
|
|
325
342
|
declare const config_effectiveBaseURL: typeof effectiveBaseURL;
|
|
343
|
+
declare const config_isPeriod: typeof isPeriod;
|
|
326
344
|
declare const config_isValidTimezone: typeof isValidTimezone;
|
|
327
345
|
declare const config_mergeSettingsSection: typeof mergeSettingsSection;
|
|
328
346
|
declare const config_normalizeSettings: typeof normalizeSettings;
|
|
@@ -331,7 +349,7 @@ declare const config_toCoreConfig: typeof toCoreConfig;
|
|
|
331
349
|
declare const config_toYaml: typeof toYaml;
|
|
332
350
|
declare const config_validateSettings: typeof validateSettings;
|
|
333
351
|
declare namespace config {
|
|
334
|
-
export { type config_AuthMode as AuthMode, config_CORE_PROVIDERS as CORE_PROVIDERS, type config_CommitMode as CommitMode, type config_CoreConfig as CoreConfig, config_DEFAULT_SETTINGS as DEFAULT_SETTINGS, type config_GithubSettings as GithubSettings, type config_ModelSettings as ModelSettings, type config_OutputSettings as OutputSettings, config_PROVIDER_BASE_URLS as PROVIDER_BASE_URLS, type config_PRSettings as PRSettings, type config_Problem as Problem, type config_Provider as Provider, type config_RepoSettings as RepoSettings, config_SETTINGS_SECTIONS as SETTINGS_SECTIONS, type config_ScheduleSettings as ScheduleSettings, type config_Settings as Settings, type config_SettingsSection as SettingsSection, type config_Since as Since, type config_TrendingSettings as TrendingSettings, config_effectiveBaseURL as effectiveBaseURL, config_isValidTimezone as isValidTimezone, config_mergeSettingsSection as mergeSettingsSection, config_normalizeSettings as normalizeSettings, config_sectionOfField as sectionOfField, config_toCoreConfig as toCoreConfig, config_toYaml as toYaml, config_validateSettings as validateSettings };
|
|
352
|
+
export { type config_AuthMode as AuthMode, config_CORE_PROVIDERS as CORE_PROVIDERS, type config_CommitMode as CommitMode, type config_CoreConfig as CoreConfig, config_DEFAULT_SETTINGS as DEFAULT_SETTINGS, type config_GithubSettings as GithubSettings, type config_ModelSettings as ModelSettings, type config_OutputSettings as OutputSettings, config_PERIODS as PERIODS, config_PROVIDER_BASE_URLS as PROVIDER_BASE_URLS, type config_PRSettings as PRSettings, type config_Period as Period, type config_Problem as Problem, type config_Provider as Provider, type config_RepoSettings as RepoSettings, config_SETTINGS_SECTIONS as SETTINGS_SECTIONS, type config_ScheduleSettings as ScheduleSettings, type config_Settings as Settings, type config_SettingsSection as SettingsSection, type config_Since as Since, type config_TrendingSettings as TrendingSettings, config_effectiveBaseURL as effectiveBaseURL, config_isPeriod as isPeriod, config_isValidTimezone as isValidTimezone, config_mergeSettingsSection as mergeSettingsSection, config_normalizeSettings as normalizeSettings, config_sectionOfField as sectionOfField, config_toCoreConfig as toCoreConfig, config_toYaml as toYaml, config_validateSettings as validateSettings };
|
|
335
353
|
}
|
|
336
354
|
|
|
337
355
|
declare const PLUGIN_DIR_NAME = "github-trending";
|
|
@@ -397,6 +415,8 @@ interface RunRecord {
|
|
|
397
415
|
files: number;
|
|
398
416
|
/** 运行所属的调度时区日期(YYYY-MM-DD);旧记录可能没有。 */
|
|
399
417
|
date?: string;
|
|
418
|
+
/** 本次产出周期(daily|weekly|monthly);旧记录可能没有。 */
|
|
419
|
+
period?: Period;
|
|
400
420
|
contentHash?: string;
|
|
401
421
|
branch?: string;
|
|
402
422
|
commit?: string;
|
|
@@ -421,8 +441,10 @@ interface PendingPr {
|
|
|
421
441
|
}
|
|
422
442
|
interface PluginState {
|
|
423
443
|
lastRunAt?: string;
|
|
424
|
-
/** 最近一次成功(ok=true,含 skipped
|
|
444
|
+
/** 最近一次成功(ok=true,含 skipped)的运行时间;工具与界面读它。 */
|
|
425
445
|
lastSuccessAt?: string;
|
|
446
|
+
/** 各周期最近一次成功的 ISO 时间;调度按「周期 + 本地日期」判重。 */
|
|
447
|
+
periodSuccess?: Partial<Record<Period, string>>;
|
|
426
448
|
lastDigest?: DigestIdentity;
|
|
427
449
|
lastResult?: RunRecord;
|
|
428
450
|
pendingPr?: PendingPr;
|
|
@@ -729,6 +751,8 @@ interface RunCoreOptions {
|
|
|
729
751
|
/** GitHub token;核心只从 env 读(GITHUB_TOKEN/GH_TOKEN)。 */
|
|
730
752
|
token?: string;
|
|
731
753
|
date?: string;
|
|
754
|
+
/** 本次运行的标的周期(daily|weekly|monthly);核心 `--period` 覆盖抓取 since。 */
|
|
755
|
+
period: Period;
|
|
732
756
|
dryRun: boolean;
|
|
733
757
|
/** 谁触发的运行:核心用它在定时运行时尊重「人工关闭的 PR」。 */
|
|
734
758
|
trigger?: 'manual' | 'schedule';
|
|
@@ -749,6 +773,7 @@ interface RunCoreResult {
|
|
|
749
773
|
declare function buildCoreArgs(channelPath: string, options: {
|
|
750
774
|
dryRun: boolean;
|
|
751
775
|
date?: string;
|
|
776
|
+
period?: Period;
|
|
752
777
|
}): string[];
|
|
753
778
|
declare function runCore(options: RunCoreOptions): Promise<RunCoreResult>;
|
|
754
779
|
|
|
@@ -1187,6 +1212,8 @@ interface RunSummary {
|
|
|
1187
1212
|
skipped: boolean;
|
|
1188
1213
|
dryRun: boolean;
|
|
1189
1214
|
date?: string;
|
|
1215
|
+
/** 本次产出周期(daily|weekly|monthly)。 */
|
|
1216
|
+
period?: Period;
|
|
1190
1217
|
durationMs: number;
|
|
1191
1218
|
coreVersion: string;
|
|
1192
1219
|
files: number;
|
|
@@ -1218,6 +1245,8 @@ interface RunSummary {
|
|
|
1218
1245
|
interface RunRequestOptions {
|
|
1219
1246
|
dryRun?: boolean;
|
|
1220
1247
|
date?: string;
|
|
1248
|
+
/** 标的周期;缺省取 settings.trending.since(手动/工具运行)。 */
|
|
1249
|
+
period?: Period;
|
|
1221
1250
|
trigger: 'manual' | 'schedule' | 'tool';
|
|
1222
1251
|
}
|
|
1223
1252
|
interface RunnerDeps {
|
|
@@ -1229,6 +1258,13 @@ interface RunnerDeps {
|
|
|
1229
1258
|
/** 预览副本这类「可失败但不阻断运行」的事件走这里,不写进 warnings。 */
|
|
1230
1259
|
logger?: (event: string, data?: Record<string, unknown>) => void;
|
|
1231
1260
|
}
|
|
1261
|
+
/**
|
|
1262
|
+
* 与 core `render.NameOf` 对齐的产物基名(不含扩展名):
|
|
1263
|
+
* daily → YYYY-MM-DD;weekly → YYYY-Wnn(ISO 周年/周);monthly → YYYY-MM。
|
|
1264
|
+
* 用于在 outcome.files 里按周期精确挑出本期 digest 的 `.md`(而不是猜)。
|
|
1265
|
+
*/
|
|
1266
|
+
declare function isoWeekName(date: string): string;
|
|
1267
|
+
declare function periodFileBase(period: Period, date: string): string;
|
|
1232
1268
|
declare class Runner {
|
|
1233
1269
|
private active;
|
|
1234
1270
|
private readonly deps;
|
|
@@ -1261,8 +1297,10 @@ type runner_RunSummary = RunSummary;
|
|
|
1261
1297
|
type runner_Runner = Runner;
|
|
1262
1298
|
declare const runner_Runner: typeof Runner;
|
|
1263
1299
|
type runner_RunnerDeps = RunnerDeps;
|
|
1300
|
+
declare const runner_isoWeekName: typeof isoWeekName;
|
|
1301
|
+
declare const runner_periodFileBase: typeof periodFileBase;
|
|
1264
1302
|
declare namespace runner {
|
|
1265
|
-
export { type runner_RunRequestOptions as RunRequestOptions, type runner_RunSummary as RunSummary, runner_Runner as Runner, type runner_RunnerDeps as RunnerDeps };
|
|
1303
|
+
export { type runner_RunRequestOptions as RunRequestOptions, type runner_RunSummary as RunSummary, runner_Runner as Runner, type runner_RunnerDeps as RunnerDeps, runner_isoWeekName as isoWeekName, runner_periodFileBase as periodFileBase };
|
|
1266
1304
|
}
|
|
1267
1305
|
|
|
1268
1306
|
interface PluginInfo {
|
|
@@ -1296,6 +1334,8 @@ interface ScheduleConfig {
|
|
|
1296
1334
|
enabled: boolean;
|
|
1297
1335
|
time: string;
|
|
1298
1336
|
timezone: string;
|
|
1337
|
+
/** 自动产出的周期;每个周期只在「它自己的触发日」跑(日=每天、周=周日、月=月末)。 */
|
|
1338
|
+
periods: Period[];
|
|
1299
1339
|
}
|
|
1300
1340
|
/** 目标时区的墙上时间 → UTC 时刻(按 DST 修正一次,够用且可测)。 */
|
|
1301
1341
|
declare function zonedTimeToUtc(year: number, month: number, day: number, hour: number, minute: number, second: number, timezone: string): Date;
|
|
@@ -1310,9 +1350,14 @@ declare function zonedDateString(date: Date, timezone: string): string;
|
|
|
1310
1350
|
declare function nextOccurrence(now: Date, schedule: ScheduleConfig): Date;
|
|
1311
1351
|
/** 今天这一次是否已过点且在补跑窗口内。 */
|
|
1312
1352
|
declare function isDue(now: Date, schedule: ScheduleConfig): boolean;
|
|
1353
|
+
declare function daysInMonth(year: number, month: number): number;
|
|
1354
|
+
/** 该时刻是否落在某个周期的结算日(按 timezone 的本地日历)。 */
|
|
1355
|
+
declare function isPeriodTriggerDay(period: Period, now: Date, timezone: string): boolean;
|
|
1356
|
+
/** 某周期下一次触发时刻(触发日的墙钟 time,严格晚于 now)。 */
|
|
1357
|
+
declare function nextOccurrenceFor(period: Period, now: Date, schedule: ScheduleConfig): Date;
|
|
1313
1358
|
interface SchedulerDeps {
|
|
1314
1359
|
store: Store;
|
|
1315
|
-
run: (trigger: 'schedule') => Promise<unknown>;
|
|
1360
|
+
run: (trigger: 'schedule', period: Period) => Promise<unknown>;
|
|
1316
1361
|
isRunning: () => boolean;
|
|
1317
1362
|
getTimer: () => {
|
|
1318
1363
|
interval?: (fn: () => void, ms: number) => unknown;
|
|
@@ -1331,13 +1376,16 @@ declare class Scheduler {
|
|
|
1331
1376
|
stop(): void;
|
|
1332
1377
|
/** 公开给测试:判断此刻是否该跑,并按需触发一次。 */
|
|
1333
1378
|
tick(now?: Date): Promise<'skipped' | 'ran' | 'disabled' | 'busy'>;
|
|
1379
|
+
/** 该周期今天是否已经成功过(含无变更跳过)。 */
|
|
1380
|
+
private succeededToday;
|
|
1334
1381
|
private readSchedule;
|
|
1335
|
-
/**
|
|
1382
|
+
/** 下一次运行(取所有已启用周期里最近的一次;禁用时为 undefined)。 */
|
|
1336
1383
|
next(): Date | undefined;
|
|
1337
1384
|
describe(state: PluginState, now?: Date): {
|
|
1338
1385
|
enabled: boolean;
|
|
1339
1386
|
time: string;
|
|
1340
1387
|
timezone: string;
|
|
1388
|
+
periods: Period[];
|
|
1341
1389
|
nextRunAt?: string;
|
|
1342
1390
|
lastRunDate?: string;
|
|
1343
1391
|
catchUpMinutes: number;
|
|
@@ -1349,13 +1397,16 @@ type schedule_ScheduleConfig = ScheduleConfig;
|
|
|
1349
1397
|
type schedule_Scheduler = Scheduler;
|
|
1350
1398
|
declare const schedule_Scheduler: typeof Scheduler;
|
|
1351
1399
|
type schedule_SchedulerDeps = SchedulerDeps;
|
|
1400
|
+
declare const schedule_daysInMonth: typeof daysInMonth;
|
|
1352
1401
|
declare const schedule_isDue: typeof isDue;
|
|
1402
|
+
declare const schedule_isPeriodTriggerDay: typeof isPeriodTriggerDay;
|
|
1353
1403
|
declare const schedule_nextOccurrence: typeof nextOccurrence;
|
|
1404
|
+
declare const schedule_nextOccurrenceFor: typeof nextOccurrenceFor;
|
|
1354
1405
|
declare const schedule_parseClock: typeof parseClock;
|
|
1355
1406
|
declare const schedule_zonedDateString: typeof zonedDateString;
|
|
1356
1407
|
declare const schedule_zonedTimeToUtc: typeof zonedTimeToUtc;
|
|
1357
1408
|
declare namespace schedule {
|
|
1358
|
-
export { type schedule_ScheduleConfig as ScheduleConfig, schedule_Scheduler as Scheduler, type schedule_SchedulerDeps as SchedulerDeps, schedule_isDue as isDue, schedule_nextOccurrence as nextOccurrence, schedule_parseClock as parseClock, schedule_zonedDateString as zonedDateString, schedule_zonedTimeToUtc as zonedTimeToUtc };
|
|
1409
|
+
export { type schedule_ScheduleConfig as ScheduleConfig, schedule_Scheduler as Scheduler, type schedule_SchedulerDeps as SchedulerDeps, schedule_daysInMonth as daysInMonth, schedule_isDue as isDue, schedule_isPeriodTriggerDay as isPeriodTriggerDay, schedule_nextOccurrence as nextOccurrence, schedule_nextOccurrenceFor as nextOccurrenceFor, schedule_parseClock as parseClock, schedule_zonedDateString as zonedDateString, schedule_zonedTimeToUtc as zonedTimeToUtc };
|
|
1359
1410
|
}
|
|
1360
1411
|
|
|
1361
1412
|
/**
|
|
@@ -1447,13 +1498,6 @@ declare namespace importCli {
|
|
|
1447
1498
|
export { type importCli_ImportCandidate as ImportCandidate, type importCli_ImportResult as ImportResult, importCli_cliConfigCandidates as cliConfigCandidates, importCli_importFirstAvailable as importFirstAvailable, importCli_importFromCli as importFromCli, importCli_parseSimpleYaml as parseSimpleYaml };
|
|
1448
1499
|
}
|
|
1449
1500
|
|
|
1450
|
-
/**
|
|
1451
|
-
* 两个对外工具:`trending_run` / `trending_history`(规格 §8)。
|
|
1452
|
-
*
|
|
1453
|
-
* 工具名是**稳定对外接口**:一旦发布不再改名;需要新能力时新增工具。
|
|
1454
|
-
* 参数方言用宿主 `dsh-tools` 的「内联 required: true」,输出用标准 JSON Schema。
|
|
1455
|
-
*/
|
|
1456
|
-
|
|
1457
1501
|
declare function createTrendingRunTool(runner: Runner): {
|
|
1458
1502
|
name: string;
|
|
1459
1503
|
description: string;
|
|
@@ -1466,6 +1510,11 @@ declare function createTrendingRunTool(runner: Runner): {
|
|
|
1466
1510
|
type: string;
|
|
1467
1511
|
description: string;
|
|
1468
1512
|
};
|
|
1513
|
+
period: {
|
|
1514
|
+
type: string;
|
|
1515
|
+
enum: string[];
|
|
1516
|
+
description: string;
|
|
1517
|
+
};
|
|
1469
1518
|
};
|
|
1470
1519
|
output: {
|
|
1471
1520
|
schema: {
|
|
@@ -1533,6 +1582,7 @@ declare function createTrendingRunTool(runner: Runner): {
|
|
|
1533
1582
|
execute(args?: {
|
|
1534
1583
|
dryRun?: boolean;
|
|
1535
1584
|
date?: string;
|
|
1585
|
+
period?: string;
|
|
1536
1586
|
}, exec?: {
|
|
1537
1587
|
signal?: AbortSignal;
|
|
1538
1588
|
}): Promise<{
|
package/lib/internal.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morehao/dsh-github-trending",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "GitHub 趋势日报插件:每天定时 spawn 本地 trendradar 核心,抓取 Trending、生成中文简介与总结,提交到你的仓库并自动合并 PR。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
},
|
|
60
60
|
"core": {
|
|
61
61
|
"repo": "morehao/trendradar",
|
|
62
|
-
"version": "0.
|
|
62
|
+
"version": "0.4.0"
|
|
63
63
|
},
|
|
64
64
|
"client": {
|
|
65
65
|
"platform": "web",
|