@morehao/dsh-github-trending 0.8.22 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/client.js CHANGED
@@ -457,6 +457,14 @@ var DICT = {
457
457
  "summarize.readmeChars": "README 摘录长度",
458
458
  "summarize.readmeChars.help": "enrich 为 readme 时,每个仓库 README 摘录的最大字符数(超出截断)。",
459
459
  "summarize.enrichNote": "外部增强需要只读 token:enrich=repo/readme 建议配置 GITHUB_READ_TOKEN(独立只读);readme 模式在网络慢时可能因预算耗尽跳过部分仓库(自动降级,不影响运行)。",
460
+ "card.observe.title": "本地观测库",
461
+ "observe.enabled": "启用观测库",
462
+ "observe.enabled.help": "每日记录候选仓库的 star 快照,用本地差分算出「今日新增」,不再依赖趋势页那行数字;关闭即回到「增量只来自趋势页」的旧行为。数据存在本机 $DSH_HOME,不进归档仓库。",
463
+ "observe.backfillDays": "回填天数",
464
+ "observe.backfillDays.help": "首次见到的仓库从 GitHub 历史接口回填多少天(0=不回填,从今天开始攒;上限 365)。",
465
+ "observe.maxCandidates": "候选上限",
466
+ "observe.maxCandidates.help": "单次运行最多记多少个候选仓库(越多覆盖越广、请求越多;建议 300,约 3 个批量请求/天)。",
467
+ "observe.disabledNote": "观测库已关闭:本次运行的「今日新增」仍来自趋势页/搜索,可能为 0。",
460
468
  "card.releases.title": "订阅",
461
469
  "card.releases.hint": "对应核心 releases.*(core 0.5.0+)",
462
470
  "releases.includePrerelease": "含预发布",
@@ -1013,6 +1021,14 @@ var DICT = {
1013
1021
  "summarize.readmeChars": "README excerpt chars",
1014
1022
  "summarize.readmeChars.help": "Max README characters excerpted per repo when enrich=readme (longer text is truncated).",
1015
1023
  "summarize.enrichNote": "Enrich needs a read-only token: enrich=repo/readme is best paired with GITHUB_READ_TOKEN (separate, read-only). In readme mode slow networks may skip some repos when the budget runs out (auto-degrades; the run is unaffected).",
1024
+ "card.observe.title": "Local observation store",
1025
+ "observe.enabled": "Enable observation store",
1026
+ "observe.enabled.help": 'Records a daily star snapshot of candidate repos and derives "stars today" from local diffs instead of the trending-page number; turn it off to fall back to the old "increment only from the trending page" behaviour. Data lives in $DSH_HOME, never in the archive repo.',
1027
+ "observe.backfillDays": "Backfill days",
1028
+ "observe.backfillDays.help": "How many days to backfill a first-seen repo from GitHub history (0 = no backfill, accumulate from today; max 365).",
1029
+ "observe.maxCandidates": "Candidate cap",
1030
+ "observe.maxCandidates.help": "Max candidate repos recorded per run (more = broader coverage and more requests; 300 ≈ 3 batch requests/day is recommended).",
1031
+ "observe.disabledNote": `Observation store is off: this run's "stars today" still comes from the trending page/search and may be 0.`,
1016
1032
  "card.releases.title": "Subscriptions",
1017
1033
  "card.releases.hint": "Maps to core releases.* (core 0.5.0+)",
1018
1034
  "releases.includePrerelease": "Include pre-releases",
@@ -2242,7 +2258,7 @@ function sectionSlice(settings, section) {
2242
2258
  case "auth":
2243
2259
  return { github: settings.github };
2244
2260
  case "scope":
2245
- return { trending: settings.trending, schedule: settings.schedule, summarize: settings.summarize };
2261
+ return { trending: settings.trending, schedule: settings.schedule, summarize: settings.summarize, observe: settings.observe };
2246
2262
  case "releases":
2247
2263
  return { releases: settings.releases };
2248
2264
  }
@@ -2730,6 +2746,8 @@ function SettingsView(props) {
2730
2746
  )
2731
2747
  )
2732
2748
  );
2749
+ const observeOf = (s) => s.observe ?? { enabled: true, backfillDays: 30, maxCandidates: 300 };
2750
+ const observe = observeOf(draft);
2733
2751
  const scopeCard = h(
2734
2752
  Card,
2735
2753
  {
@@ -2822,7 +2840,35 @@ function SettingsView(props) {
2822
2840
  next.summarize.readmeChars = Math.round(Number(value) || 0);
2823
2841
  }) })
2824
2842
  ),
2825
- draft.summarize.enrich === "none" ? null : h(Alert, { kind: "warn" }, h("div", null, t("summarize.enrichNote")))
2843
+ draft.summarize.enrich === "none" ? null : h(Alert, { kind: "warn" }, h("div", null, t("summarize.enrichNote"))),
2844
+ // 本地 star 观测库(核心 observe.*):旧设置里可能还没有该字段,先取默认再编辑。
2845
+ // 观测库让「今日新增」来自本地每日快照的差分;关闭即回到「增量只来自趋势页」的旧行为。
2846
+ h("h4", { className: "dshgt-subhead" }, t("card.observe.title")),
2847
+ h(
2848
+ Row,
2849
+ { label: t("observe.enabled"), help: t("observe.enabled.help") },
2850
+ h(Toggle, {
2851
+ checked: observe.enabled,
2852
+ onChange: (value) => update((next) => {
2853
+ next.observe = { ...observeOf(next), enabled: value };
2854
+ })
2855
+ })
2856
+ ),
2857
+ h(
2858
+ Row,
2859
+ { label: t("observe.backfillDays"), help: t("observe.backfillDays.help") },
2860
+ h(Input, { mono: true, width: 90, value: String(observe.backfillDays), onInput: (value) => update((next) => {
2861
+ next.observe = { ...observeOf(next), backfillDays: Math.round(Number(value) || 0) };
2862
+ }) })
2863
+ ),
2864
+ h(
2865
+ Row,
2866
+ { label: t("observe.maxCandidates"), help: t("observe.maxCandidates.help") },
2867
+ h(Input, { mono: true, width: 90, value: String(observe.maxCandidates), onInput: (value) => update((next) => {
2868
+ next.observe = { ...observeOf(next), maxCandidates: Math.round(Number(value) || 0) };
2869
+ }) })
2870
+ ),
2871
+ observe.enabled ? null : h(Alert, { kind: "info" }, h("div", null, t("observe.disabledNote")))
2826
2872
  );
2827
2873
  const releaseSchedule = draft.releases.schedule ?? { enabled: false, weekday: 1, time: "08:00:00", timezone: "Asia/Shanghai" };
2828
2874
  const releaseConcurrency = draft.releases.concurrency ?? "auto";
@@ -69,6 +69,7 @@ var DEFAULT_SETTINGS = {
69
69
  enrichTimeoutSeconds: 5,
70
70
  enrichBudgetSeconds: 8
71
71
  },
72
+ observe: { enabled: true, backfillDays: 30, maxCandidates: 300 },
72
73
  releases: {
73
74
  includePrerelease: false,
74
75
  includeDrafts: false,
@@ -102,6 +103,7 @@ function sectionOfField(field) {
102
103
  if (field.startsWith("github.")) return "auth";
103
104
  if (field.startsWith("trending.")) return "scope";
104
105
  if (field.startsWith("summarize.")) return "summarize";
106
+ if (field.startsWith("observe.")) return "scope";
105
107
  if (field.startsWith("releases.")) return "releases";
106
108
  if (field.startsWith("schedule.")) return "schedule";
107
109
  if (field === "corePath" || field === "coreAutoDownload" || field === "network.proxy") return "source";
@@ -128,6 +130,7 @@ function mergeSettingsSection(stored, incoming, section) {
128
130
  break;
129
131
  case "scope":
130
132
  next.trending = incoming.trending;
133
+ next.observe = incoming.observe;
131
134
  break;
132
135
  case "summarize":
133
136
  next.summarize = incoming.summarize;
@@ -171,6 +174,7 @@ function normalizeSettings(input) {
171
174
  const github = raw.github ?? {};
172
175
  const trending = raw.trending ?? {};
173
176
  const summarize = raw.summarize ?? {};
177
+ const observe = raw.observe ?? {};
174
178
  const releases = raw.releases ?? {};
175
179
  const releasesSchedule = releases.schedule ?? {};
176
180
  const output = raw.output ?? {};
@@ -225,6 +229,11 @@ function normalizeSettings(input) {
225
229
  enrichTimeoutSeconds: Math.round(num(summarize.enrichTimeoutSeconds, DEFAULT_SETTINGS.summarize.enrichTimeoutSeconds)),
226
230
  enrichBudgetSeconds: Math.round(num(summarize.enrichBudgetSeconds, DEFAULT_SETTINGS.summarize.enrichBudgetSeconds))
227
231
  },
232
+ observe: {
233
+ enabled: bool(observe.enabled, DEFAULT_SETTINGS.observe.enabled),
234
+ backfillDays: Math.round(num(observe.backfillDays, DEFAULT_SETTINGS.observe.backfillDays)),
235
+ maxCandidates: Math.round(num(observe.maxCandidates, DEFAULT_SETTINGS.observe.maxCandidates))
236
+ },
228
237
  releases: {
229
238
  includePrerelease: bool(releases.includePrerelease, DEFAULT_SETTINGS.releases.includePrerelease),
230
239
  includeDrafts: bool(releases.includeDrafts, DEFAULT_SETTINGS.releases.includeDrafts),
@@ -332,6 +341,13 @@ function validateSettings(settings, providers = CORE_PROVIDERS) {
332
341
  if (!Number.isInteger(summarize.readmeChars) || summarize.readmeChars <= 0) {
333
342
  problems.push({ field: "summarize.readmeChars", code: "CONFIG_INVALID", message: "readmeChars \u5FC5\u987B\u662F\u6B63\u6574\u6570" });
334
343
  }
344
+ const { observe } = settings;
345
+ if (!Number.isInteger(observe.backfillDays) || observe.backfillDays < 0 || observe.backfillDays > 365) {
346
+ problems.push({ field: "observe.backfillDays", code: "CONFIG_INVALID", message: "backfillDays \u5FC5\u987B\u662F 0\u2013365 \u7684\u6574\u6570" });
347
+ }
348
+ if (!Number.isInteger(observe.maxCandidates) || observe.maxCandidates < 1 || observe.maxCandidates > 1e3) {
349
+ problems.push({ field: "observe.maxCandidates", code: "CONFIG_INVALID", message: "maxCandidates \u5FC5\u987B\u662F 1\u20131000 \u7684\u6574\u6570" });
350
+ }
335
351
  if (releases.period !== "weekly") {
336
352
  problems.push({
337
353
  field: "releases.period",
@@ -422,6 +438,11 @@ function toCoreConfig(settings, apiKey, baseURLOverride, period, repoCount = 0)
422
438
  enrichTimeoutSeconds: settings.summarize.enrichTimeoutSeconds,
423
439
  enrichBudgetSeconds: settings.summarize.enrichBudgetSeconds
424
440
  },
441
+ observe: {
442
+ enabled: settings.observe.enabled,
443
+ backfillDays: settings.observe.backfillDays,
444
+ maxCandidates: settings.observe.maxCandidates
445
+ },
425
446
  releases: {
426
447
  // 发版订阅常开:插件侧不再提供开关,是否产出由订阅列表决定。
427
448
  enabled: true,
@@ -1031,8 +1052,9 @@ __export(ipc_exports, {
1031
1052
  parseSubscribeResult: () => parseSubscribeResult,
1032
1053
  parseVersionInfo: () => parseVersionInfo
1033
1054
  });
1055
+ import { createHash } from "crypto";
1034
1056
  import { validators } from "@morehao/trendradar-contract";
1035
- var IPC_SCHEMA_VERSION = 2;
1057
+ var IPC_SCHEMA_VERSION = 3;
1036
1058
  var SUPPORTED_DIGEST_SCHEMA = 2;
1037
1059
  var SUPPORTED_CONTRACT_MAJOR = 0;
1038
1060
  var SUBSCRIPTION_STATUSES = ["baseline", "ok", "no_change", "rate_limited", "failed"];
@@ -1091,20 +1113,50 @@ function normalizeError(value) {
1091
1113
  ...isRecord(value.details) ? { details: value.details } : {}
1092
1114
  };
1093
1115
  }
1116
+ function parseDigest(value, files) {
1117
+ if (value === void 0 || value === null) return void 0;
1118
+ if (!isRecord(value)) throw new IpcError("IPC_INVALID", "\u6838\u5FC3 run --json \u7684 digest \u4E0D\u662F\u5BF9\u8C61");
1119
+ const file = value.path;
1120
+ const bytes = value.bytes;
1121
+ const sha256 = value.sha256;
1122
+ const text = value.text;
1123
+ if (typeof file !== "string" || file === "" || typeof text !== "string" || typeof bytes !== "number" || typeof sha256 !== "string") {
1124
+ throw new IpcError("IPC_INVALID", "\u6838\u5FC3 run --json \u7684 digest \u7F3A path/bytes/sha256/text");
1125
+ }
1126
+ const buffer = Buffer.from(text, "utf8");
1127
+ if (buffer.length !== bytes) {
1128
+ throw new IpcError("IPC_INVALID", `digest.bytes=${bytes} \u4E0E text \u5B9E\u9645 ${buffer.length} \u5B57\u8282\u4E0D\u4E00\u81F4`);
1129
+ }
1130
+ const actual = `sha256:${createHash("sha256").update(buffer).digest("hex")}`;
1131
+ if (actual !== sha256) {
1132
+ throw new IpcError("IPC_INVALID", `digest.sha256 \u4E0E text \u4E0D\u4E00\u81F4\uFF1A\u58F0\u660E ${sha256}\uFF0C\u5B9E\u9645 ${actual}`);
1133
+ }
1134
+ const listed = files.find((item) => item.path === file);
1135
+ if (listed === void 0) {
1136
+ throw new IpcError("IPC_INVALID", `digest.path=${file} \u4E0D\u5728 files \u91CC`);
1137
+ }
1138
+ if (listed.sha256 !== sha256 || listed.bytes !== bytes) {
1139
+ throw new IpcError("IPC_INVALID", `digest \u4E0E files \u91CC ${file} \u7684 bytes/sha256 \u4E0D\u4E00\u81F4`);
1140
+ }
1141
+ return { path: file, bytes, sha256, text };
1142
+ }
1094
1143
  function parseRunResult(text, options) {
1095
1144
  const value = parseJsonObject(text);
1096
1145
  if (value.files === null || value.files === void 0) value.files = [];
1097
1146
  if (value.command !== "run" || value.ipcSchema !== IPC_SCHEMA_VERSION || !validators.runResult(value)) {
1098
- throw new IpcError("IPC_INVALID", "\u6838\u5FC3 run --json \u4E0D\u662F\u5951\u7EA6 RunResult\uFF08ipcSchema=2 / command=run\uFF09");
1147
+ throw new IpcError("IPC_INVALID", "\u6838\u5FC3 run --json \u4E0D\u662F\u5951\u7EA6 RunResult\uFF08ipcSchema=3 / command=run\uFF09");
1099
1148
  }
1100
1149
  const result = value;
1101
1150
  const error = normalizeError(result.error);
1102
1151
  const skipReason = value.skipReason;
1152
+ const files = Array.isArray(result.files) ? result.files : [];
1153
+ const digest = parseDigest(value.digest, files);
1103
1154
  return {
1104
1155
  ok: result.ok,
1105
1156
  skipped: result.skipped === true,
1106
1157
  dryRun: options.dryRun,
1107
- files: Array.isArray(result.files) ? result.files : [],
1158
+ files,
1159
+ ...digest === void 0 ? {} : { digest },
1108
1160
  contentHash: typeof result.contentHash === "string" ? result.contentHash : void 0,
1109
1161
  branch: typeof result.branch === "string" ? result.branch : void 0,
1110
1162
  commit: typeof result.commit === "string" ? result.commit : void 0,
@@ -1225,7 +1277,7 @@ __export(release_exports, {
1225
1277
  readCoreRelease: () => readCoreRelease,
1226
1278
  verifyCached: () => verifyCached
1227
1279
  });
1228
- import { createHash } from "crypto";
1280
+ import { createHash as createHash2 } from "crypto";
1229
1281
  import { chmodSync, createWriteStream, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, rmSync, statSync as statSync2, unlinkSync as unlinkSync2 } from "fs";
1230
1282
  import { createRequire } from "module";
1231
1283
  import { pipeline } from "stream/promises";
@@ -1285,7 +1337,7 @@ function describeRelease(release, options = {}) {
1285
1337
  };
1286
1338
  }
1287
1339
  function sha256File(file) {
1288
- return createHash("sha256").update(readFileSync2(file)).digest("hex");
1340
+ return createHash2("sha256").update(readFileSync2(file)).digest("hex");
1289
1341
  }
1290
1342
  function markerPath(binaryPath) {
1291
1343
  return `${binaryPath}.sha256`;
@@ -1357,7 +1409,7 @@ async function download(descriptor, options) {
1357
1409
  }
1358
1410
  mkdirSync2(descriptor.cacheDir, { recursive: true, mode: 448 });
1359
1411
  const temp = `${descriptor.binaryPath}.${process.pid}.${Date.now()}.part`;
1360
- const hash = createHash("sha256");
1412
+ const hash = createHash2("sha256");
1361
1413
  const total = Number(response.headers.get("content-length") ?? "0") || void 0;
1362
1414
  let bytes = 0;
1363
1415
  try {
@@ -2795,44 +2847,15 @@ async function runSubscribe(input, deps) {
2795
2847
  }
2796
2848
  }
2797
2849
 
2798
- // src/host/version.ts
2799
- var version_exports = {};
2800
- __export(version_exports, {
2801
- versionAtLeast: () => versionAtLeast
2802
- });
2803
- function parseTriplet(value) {
2804
- const match = /^v?(\d+)\.(\d+)\.(\d+)/.exec(value.trim());
2805
- if (match === null) return void 0;
2806
- return [Number(match[1]), Number(match[2]), Number(match[3])];
2807
- }
2808
- function versionAtLeast(a, b) {
2809
- const left = parseTriplet(a);
2810
- const right = parseTriplet(b);
2811
- if (left === void 0 || right === void 0) return false;
2812
- for (let i = 0; i < 3; i += 1) {
2813
- const l = left[i];
2814
- const r = right[i];
2815
- if (l > r) return true;
2816
- if (l < r) return false;
2817
- }
2818
- return true;
2819
- }
2820
-
2821
2850
  // src/host/runner.ts
2822
2851
  var runner_exports = {};
2823
2852
  __export(runner_exports, {
2824
- FORCE_MIN_CORE_VERSION: () => FORCE_MIN_CORE_VERSION,
2825
- REPO_MIN_CORE_VERSION: () => REPO_MIN_CORE_VERSION,
2826
2853
  Runner: () => Runner,
2827
- isoWeekName: () => isoWeekName,
2828
- periodFileBase: () => periodFileBase,
2829
2854
  projectUsage: () => projectUsage
2830
2855
  });
2831
- import { createHash as createHash2 } from "crypto";
2832
- import { existsSync as existsSync4, readFileSync as readFileSync3, realpathSync, statSync as statSync5 } from "fs";
2856
+ import { createHash as createHash3 } from "crypto";
2857
+ import { existsSync as existsSync4, statSync as statSync5 } from "fs";
2833
2858
  import path9 from "path";
2834
- var FORCE_MIN_CORE_VERSION = "0.7.1";
2835
- var REPO_MIN_CORE_VERSION = "0.8.0";
2836
2859
  function repoProblem(repoPath) {
2837
2860
  if (repoPath.trim() === "") return { code: "CONFIG_INVALID", message: "\u8FD8\u6CA1\u6709\u9009\u62E9\u5F52\u6863\u4ED3\u5E93" };
2838
2861
  if (!existsSync4(repoPath)) return { code: "REPO_NOT_GIT", message: `\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${repoPath}` };
@@ -2853,30 +2876,9 @@ function runDate(settings, now = /* @__PURE__ */ new Date()) {
2853
2876
  return now.toISOString().slice(0, 10);
2854
2877
  }
2855
2878
  }
2856
- function isInside(root, target) {
2857
- const rel = path9.relative(root, target);
2858
- return rel !== "" && !rel.startsWith("..") && !path9.isAbsolute(rel);
2859
- }
2860
- function normalizeSha256(value) {
2861
- if (typeof value !== "string") return void 0;
2862
- const hex = value.trim().replace(/^sha256:/i, "").toLowerCase();
2863
- return /^[0-9a-f]{64}$/.test(hex) ? hex : void 0;
2864
- }
2865
2879
  function counterOf(value) {
2866
2880
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
2867
2881
  }
2868
- function isoWeekName(date) {
2869
- const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date);
2870
- if (match === null) return date;
2871
- const d = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])));
2872
- if (Number.isNaN(d.getTime())) return date;
2873
- return isoWeekKey(d, "UTC");
2874
- }
2875
- function periodFileBase(period, date) {
2876
- if (period === "monthly") return date.slice(0, 7);
2877
- if (period === "weekly") return isoWeekName(date);
2878
- return date;
2879
- }
2880
2882
  function projectUsage(outcome) {
2881
2883
  const usageTokens = outcome.usage?.totalTokens;
2882
2884
  const releasesTokens = outcome.releases?.usage?.totalTokens;
@@ -2951,15 +2953,7 @@ var Runner = class {
2951
2953
  `\u6838\u5FC3\u4E0D\u652F\u6301 provider\u300C${settings.model.provider}\u300D\uFF1B\u53EF\u7528\uFF1A${handshakeResult.supportedProviders.join(" / ")}`
2952
2954
  );
2953
2955
  }
2954
- const forceRequested = options.force === true;
2955
- const forceSupported = versionAtLeast(handshakeResult.version.version, FORCE_MIN_CORE_VERSION);
2956
- const effectiveForce = forceRequested && forceSupported;
2957
- if (forceRequested && !forceSupported) {
2958
- store.appendLog(
2959
- logFile,
2960
- `[force] \u6838\u5FC3 ${handshakeResult.version.version} \u4F4E\u4E8E ${FORCE_MIN_CORE_VERSION}\uFF0C\u4E0D\u652F\u6301 --force\uFF0C\u5DF2\u964D\u7EA7\u4E3A\u666E\u901A\u91CD\u8DD1`
2961
- );
2962
- }
2956
+ const effectiveForce = options.force === true;
2963
2957
  const model = await this.openModelChannel({ settings, credentials, env, logFile });
2964
2958
  const apiKey = model.apiKey;
2965
2959
  proxy = model.proxy;
@@ -2997,15 +2991,7 @@ var Runner = class {
2997
2991
  ...core.tag === void 0 ? {} : { coreTag: core.tag },
2998
2992
  ...effectiveForce ? { forced: true } : {}
2999
2993
  });
3000
- const preview = this.capturePreview({
3001
- id,
3002
- date,
3003
- period,
3004
- settings,
3005
- outcome,
3006
- secrets: [apiKey, github.token],
3007
- ...effectiveForce ? { force: true } : {}
3008
- });
2994
+ const preview = this.capturePreview({ id, outcome, secrets: [apiKey, github.token] });
3009
2995
  if (preview !== void 0) summary.preview = preview;
3010
2996
  this.record(summary, settings);
3011
2997
  return summary;
@@ -3096,14 +3082,7 @@ var Runner = class {
3096
3082
  `\u6838\u5FC3\u4E0D\u652F\u6301 provider\u300C${settings.model.provider}\u300D\uFF1B\u53EF\u7528\uFF1A${handshakeResult.supportedProviders.join(" / ")}`
3097
3083
  );
3098
3084
  }
3099
- let effectiveRepo = requestedRepo;
3100
- if (requestedRepo !== void 0 && !versionAtLeast(handshakeResult.version.version, REPO_MIN_CORE_VERSION)) {
3101
- store.appendLog(
3102
- logFile,
3103
- `[release] \u6838\u5FC3 ${handshakeResult.version.version} \u4F4E\u4E8E ${REPO_MIN_CORE_VERSION}\uFF0C\u5355\u4ED3\u5206\u6790\u964D\u7EA7\u4E3A\u5168\u91CF`
3104
- );
3105
- effectiveRepo = void 0;
3106
- }
3085
+ const effectiveRepo = requestedRepo;
3107
3086
  const model = await this.openModelChannel({ settings, credentials, env, logFile });
3108
3087
  proxy = model.proxy;
3109
3088
  const github = await resolveGithubToken(credentials, settings.github.authMode, settings.github.tokenRef, { env });
@@ -3344,70 +3323,38 @@ var Runner = class {
3344
3323
  };
3345
3324
  }
3346
3325
  /**
3347
- * 从本次核心返回的 artifact 清单里选当天的 `.md`,只读复制到 previews/。
3326
+ * 把本次运行渲染的 digest 原文写进 previews/(ipc v3 起由核心随 RunResult 回传)。
3348
3327
  *
3349
- * 这是插件**唯一**读取用户仓库的场景,而且只读核心自己刚写出的那一份:
3350
- * - 路径必须在 `repo.path` 内(词法 + realpath 各判一次,挡 `..`/绝对路径/符号链接);
3351
- * - 上限 512 KB、拒绝 NUL、sha256 必须与契约声明一致;
3328
+ * 这里**不再读用户仓库**:分支可能在运行结束前就被合并、切回 base 并删除,
3329
+ * 任何「事后去工作树里找文件」的做法都会读到旧版或空。文本、字节数与 sha256
3330
+ * 的跨字段一致性已在 `parseRunResult` 里校验过;这里只做展示侧的防御:
3331
+ * - 上限 512 KB(与预览读取上限一致)、拒绝 NUL;
3332
+ * - 落盘前统一 `redact()`,避免把密钥写进预览文件;
3352
3333
  * - 任何一步失败都只记日志,返回 undefined;运行结果不受影响。
3353
3334
  */
3354
3335
  capturePreview(input) {
3355
- const { outcome, settings, date, period, id } = input;
3356
- if (!outcome.ok || outcome.dryRun || date === void 0) return void 0;
3357
- if (outcome.skipped && input.force !== true) return void 0;
3336
+ const { outcome, id } = input;
3337
+ if (!outcome.ok || outcome.dryRun) return void 0;
3358
3338
  const skip = (reason, extra = {}) => {
3359
3339
  this.deps.logger?.("preview.skip", { id, reason, ...extra });
3360
3340
  return void 0;
3361
3341
  };
3362
- const markdown = outcome.files.filter((file) => {
3363
- const ext = path9.extname(file.path).toLowerCase();
3364
- const base = path9.basename(file.path).toLowerCase();
3365
- const normalized = file.path.replace(/\\/g, "/");
3366
- const inReleases = normalized === "releases" || normalized.startsWith("releases/");
3367
- return ext === ".md" && base !== "readme.md" && base !== "index.md" && !inReleases;
3368
- });
3369
- if (markdown.length === 0) return skip("no-markdown");
3370
- const expected = period === void 0 ? `${date}.md` : `${periodFileBase(period, date)}.md`;
3371
- const named = markdown.filter((file) => path9.basename(file.path) === expected);
3372
- const chosen = named.length > 0 ? named[0] : markdown.length === 1 ? markdown[0] : void 0;
3373
- if (chosen === void 0) return skip("ambiguous", { candidates: markdown.length, expected, period: period ?? null });
3374
- const repoPath = settings.repo.path;
3375
- const resolved = path9.resolve(repoPath, chosen.path);
3376
- if (!isInside(repoPath, resolved)) return skip("outside-repo", { path: chosen.path });
3377
- let fileReal;
3378
- try {
3379
- const repoReal = realpathSync(repoPath);
3380
- fileReal = realpathSync(resolved);
3381
- if (!isInside(repoReal, fileReal)) return skip("symlink-outside-repo", { path: chosen.path });
3382
- } catch {
3383
- return skip("missing-file", { path: chosen.path });
3384
- }
3385
- let buffer;
3386
- try {
3387
- const stat2 = statSync5(fileReal);
3388
- if (!stat2.isFile()) return skip("not-a-file", { path: chosen.path });
3389
- if (stat2.size > PREVIEW_MAX_BYTES) return skip("too-large", { bytes: stat2.size });
3390
- buffer = readFileSync3(fileReal);
3391
- } catch {
3392
- return skip("read-failed", { path: chosen.path });
3393
- }
3394
- if (buffer.includes(0)) return skip("contains-nul", { path: chosen.path });
3395
- const declared = normalizeSha256(chosen.sha256);
3396
- const actual = createHash2("sha256").update(buffer).digest("hex");
3397
- if (declared === void 0 || declared !== actual) {
3398
- return skip("sha-mismatch", { path: chosen.path, declared: declared ?? null, actual });
3399
- }
3400
- const text = redact(buffer.toString("utf8"), input.secrets);
3342
+ const digest = outcome.digest;
3343
+ if (digest === void 0) return skip("no-digest");
3344
+ const buffer = Buffer.from(digest.text, "utf8");
3345
+ if (buffer.length > PREVIEW_MAX_BYTES) return skip("too-large", { path: digest.path, bytes: buffer.length });
3346
+ if (buffer.includes(0)) return skip("contains-nul", { path: digest.path });
3347
+ const text = redact(digest.text, input.secrets);
3401
3348
  const stored = Buffer.from(text, "utf8");
3402
3349
  try {
3403
3350
  this.deps.store.writePreview(id, text);
3404
3351
  } catch {
3405
- return skip("write-failed", { path: chosen.path });
3352
+ return skip("write-failed", { path: digest.path });
3406
3353
  }
3407
3354
  return {
3408
- file: path9.relative(repoPath, resolved).split(path9.sep).join("/"),
3355
+ file: digest.path,
3409
3356
  bytes: stored.length,
3410
- sha256: createHash2("sha256").update(stored).digest("hex")
3357
+ sha256: createHash3("sha256").update(stored).digest("hex")
3411
3358
  };
3412
3359
  }
3413
3360
  /** 写历史与状态;同一天的 skipped 运行不覆盖 lastDigest。 */
@@ -3818,7 +3765,7 @@ __export(import_cli_exports, {
3818
3765
  importFromCli: () => importFromCli,
3819
3766
  parseSimpleYaml: () => parseSimpleYaml
3820
3767
  });
3821
- import { readFileSync as readFileSync4 } from "fs";
3768
+ import { readFileSync as readFileSync3 } from "fs";
3822
3769
  import os4 from "os";
3823
3770
  import path10 from "path";
3824
3771
  function cliConfigCandidates(env = process.env, home = os4.homedir()) {
@@ -3910,7 +3857,7 @@ function isSecretKey(key) {
3910
3857
  function importFromCli(filePath, base = DEFAULT_SETTINGS) {
3911
3858
  let text;
3912
3859
  try {
3913
- text = readFileSync4(filePath, "utf8");
3860
+ text = readFileSync3(filePath, "utf8");
3914
3861
  } catch (error) {
3915
3862
  return {
3916
3863
  ok: false,
@@ -4929,7 +4876,6 @@ export {
4929
4876
  Scheduler,
4930
4877
  schedule_exports,
4931
4878
  subscribe_exports,
4932
- version_exports,
4933
4879
  Runner,
4934
4880
  runner_exports,
4935
4881
  readPluginInfo,
package/lib/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  createUpdateService,
10
10
  ensureGithubFlow,
11
11
  readPluginInfo
12
- } from "./chunk-SLY2RKCZ.js";
12
+ } from "./chunk-JMIWABMY.js";
13
13
 
14
14
  // src/index.ts
15
15
  var name = "dsh-github-trending";
package/lib/internal.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ErrorCode, FileArtifact, Usage, ReleasesReport, SubscriptionItem, SubscriptionDoc, SubscriptionStatus, ErrorInfo, RunRequest, RunResult, State, StateRun, SubscriptionList, SubscriptionVolume } from '@morehao/trendradar-contract';
1
+ import { ErrorCode, FileArtifact, DigestArtifact, Usage, ReleasesReport, SubscriptionItem, SubscriptionDoc, SubscriptionStatus, ErrorInfo, RunRequest, RunResult, State, StateRun, SubscriptionList, SubscriptionVolume } from '@morehao/trendradar-contract';
2
2
  import { ExecOptions, ExecResult } from '@morehao/dsh-plugin-kit/exec';
3
3
  import * as exec from '@morehao/dsh-plugin-kit/exec';
4
4
  export { exec };
@@ -71,8 +71,8 @@ declare namespace errors {
71
71
  export { type errors_AnyCode as AnyCode, type errors_ClassifiedError as ClassifiedError, type errors_ErrorArea as ErrorArea, type errors_GitReason as GitReason, errors_PLUGIN_CODES as PLUGIN_CODES, type errors_PluginCode as PluginCode, errors_areaOf as areaOf, errors_classifyStderr as classifyStderr, errors_hintOf as hintOf, errors_isRetriable as isRetriable, errors_reasonOf as reasonOf, errors_redact as redact };
72
72
  }
73
73
 
74
- /** 插件实现所对应的 IPC 契约版本(契约包 `ipc.v2.json` 的 wire 整数)。 */
75
- declare const IPC_SCHEMA_VERSION: 2;
74
+ /** 插件实现所对应的 IPC 契约版本(契约包 `ipc.v3.json` 的 wire 整数)。 */
75
+ declare const IPC_SCHEMA_VERSION: 3;
76
76
  /** 插件能接受的 digest 契约版本(与核心 `version --json` 的 digestSchema 比对)。 */
77
77
  declare const SUPPORTED_DIGEST_SCHEMA: 2;
78
78
  /** 契约包 `contractVersion` 的主版本;0.x 期间同主版本即视为同一契约线。 */
@@ -99,6 +99,8 @@ interface RunOutcome {
99
99
  skipped: boolean;
100
100
  dryRun: boolean;
101
101
  files: FileArtifact[];
102
+ /** 本次渲染的 digest 原文(ipc v3);没有渲染(dry-run / 纯发版 / 渲染前失败)时缺省。 */
103
+ digest?: DigestArtifact;
102
104
  contentHash?: string;
103
105
  branch?: string;
104
106
  commit?: string;
@@ -173,6 +175,7 @@ type SubscribeAction = 'list' | 'add' | 'remove' | 'releases';
173
175
  */
174
176
  declare function parseSubscribeResult(text: string): SubscribeOutcome;
175
177
 
178
+ declare const ipc_DigestArtifact: typeof DigestArtifact;
176
179
  declare const ipc_ErrorCode: typeof ErrorCode;
177
180
  declare const ipc_ErrorInfo: typeof ErrorInfo;
178
181
  declare const ipc_FileArtifact: typeof FileArtifact;
@@ -206,7 +209,7 @@ declare const ipc_parseRunResult: typeof parseRunResult;
206
209
  declare const ipc_parseSubscribeResult: typeof parseSubscribeResult;
207
210
  declare const ipc_parseVersionInfo: typeof parseVersionInfo;
208
211
  declare namespace ipc {
209
- export { ipc_ErrorCode as ErrorCode, ipc_ErrorInfo as ErrorInfo, ipc_FileArtifact as FileArtifact, ipc_IPC_SCHEMA_VERSION as IPC_SCHEMA_VERSION, ipc_IpcError as IpcError, type ipc_ParseRunOptions as ParseRunOptions, ipc_ReleasesReport as ReleasesReport, type ipc_RunError as RunError, type ipc_RunOutcome as RunOutcome, ipc_RunRequest as RunRequest, ipc_RunResult as RunResult, ipc_SUPPORTED_CONTRACT_MAJOR as SUPPORTED_CONTRACT_MAJOR, ipc_SUPPORTED_DIGEST_SCHEMA as SUPPORTED_DIGEST_SCHEMA, ipc_State as State, ipc_StateRun as StateRun, type ipc_SubscribeAction as SubscribeAction, type ipc_SubscribeOutcome as SubscribeOutcome, ipc_SubscriptionDoc as SubscriptionDoc, ipc_SubscriptionItem as SubscriptionItem, ipc_SubscriptionList as SubscriptionList, ipc_SubscriptionStatus as SubscriptionStatus, ipc_SubscriptionVolume as SubscriptionVolume, ipc_Usage as Usage, type ipc_VersionInfo as VersionInfo, ipc_isSubscriptionDoc as isSubscriptionDoc, ipc_isSubscriptionStatus as isSubscriptionStatus, ipc_isVersionInfo as isVersionInfo, ipc_parseJsonObject as parseJsonObject, ipc_parseRunResult as parseRunResult, ipc_parseSubscribeResult as parseSubscribeResult, ipc_parseVersionInfo as parseVersionInfo };
212
+ export { ipc_DigestArtifact as DigestArtifact, ipc_ErrorCode as ErrorCode, ipc_ErrorInfo as ErrorInfo, ipc_FileArtifact as FileArtifact, ipc_IPC_SCHEMA_VERSION as IPC_SCHEMA_VERSION, ipc_IpcError as IpcError, type ipc_ParseRunOptions as ParseRunOptions, ipc_ReleasesReport as ReleasesReport, type ipc_RunError as RunError, type ipc_RunOutcome as RunOutcome, ipc_RunRequest as RunRequest, ipc_RunResult as RunResult, ipc_SUPPORTED_CONTRACT_MAJOR as SUPPORTED_CONTRACT_MAJOR, ipc_SUPPORTED_DIGEST_SCHEMA as SUPPORTED_DIGEST_SCHEMA, ipc_State as State, ipc_StateRun as StateRun, type ipc_SubscribeAction as SubscribeAction, type ipc_SubscribeOutcome as SubscribeOutcome, ipc_SubscriptionDoc as SubscriptionDoc, ipc_SubscriptionItem as SubscriptionItem, ipc_SubscriptionList as SubscriptionList, ipc_SubscriptionStatus as SubscriptionStatus, ipc_SubscriptionVolume as SubscriptionVolume, ipc_Usage as Usage, type ipc_VersionInfo as VersionInfo, ipc_isSubscriptionDoc as isSubscriptionDoc, ipc_isSubscriptionStatus as isSubscriptionStatus, ipc_isVersionInfo as isVersionInfo, ipc_parseJsonObject as parseJsonObject, ipc_parseRunResult as parseRunResult, ipc_parseSubscribeResult as parseSubscribeResult, ipc_parseVersionInfo as parseVersionInfo };
210
213
  }
211
214
 
212
215
  type Provider = string;
@@ -282,6 +285,21 @@ interface SummarizeSettings {
282
285
  enrichTimeoutSeconds: number;
283
286
  enrichBudgetSeconds: number;
284
287
  }
288
+ /**
289
+ * 本地 star 观测库(核心 `observe.*`,core ≥ 0.8.8)。
290
+ * 观测库让「今日新增」来自本地每日快照的差分,而不是趋势页上的一行字;
291
+ * 默认开启,`enabled=false` 是回滚开关(回到「增量只来自趋势页」的旧行为)。
292
+ * 存储位置由核心自己解析到 `$DSH_HOME`(计划 D4),**不**在这里暴露,避免用户
293
+ * 把观测库指进归档仓库。
294
+ */
295
+ interface ObserveSettings {
296
+ /** 是否启用观测库;关闭即回到旧行为(`starsInPeriod` 只来自趋势页/搜索)。 */
297
+ enabled: boolean;
298
+ /** 首次见到的仓库从历史接口回填多少天(0=不回填,从今天开始攒)。 */
299
+ backfillDays: number;
300
+ /** 单次运行最多记多少个候选仓库(上限 300,约 3 个 GraphQL 批量/天)。 */
301
+ maxCandidates: number;
302
+ }
285
303
  /**
286
304
  * 每周发版运行的插件侧节奏(核心没有这个字段,**绝不**写进核心 YAML)。
287
305
  * 与榜单调度(`schedule.*`)完全独立:两者各有自己的开关与时间点。
@@ -345,6 +363,7 @@ interface Settings {
345
363
  github: GithubSettings;
346
364
  trending: TrendingSettings;
347
365
  summarize: SummarizeSettings;
366
+ observe: ObserveSettings;
348
367
  releases: ReleasesSettings;
349
368
  output: OutputSettings;
350
369
  schedule: ScheduleSettings;
@@ -432,6 +451,11 @@ interface CoreConfig {
432
451
  enrichTimeoutSeconds: number;
433
452
  enrichBudgetSeconds: number;
434
453
  };
454
+ observe: {
455
+ enabled: boolean;
456
+ backfillDays: number;
457
+ maxCandidates: number;
458
+ };
435
459
  releases: {
436
460
  enabled: boolean;
437
461
  includePrerelease: boolean;
@@ -480,6 +504,7 @@ type config_Enrich = Enrich;
480
504
  type config_GithubSettings = GithubSettings;
481
505
  type config_ModelSettings = ModelSettings;
482
506
  type config_NetworkSettings = NetworkSettings;
507
+ type config_ObserveSettings = ObserveSettings;
483
508
  type config_OutputSettings = OutputSettings;
484
509
  declare const config_PERIODS: typeof PERIODS;
485
510
  declare const config_PROVIDER_BASE_URLS: typeof PROVIDER_BASE_URLS;
@@ -508,7 +533,7 @@ declare const config_toCoreConfig: typeof toCoreConfig;
508
533
  declare const config_toYaml: typeof toYaml;
509
534
  declare const config_validateSettings: typeof validateSettings;
510
535
  declare namespace config {
511
- 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_Enrich as Enrich, type config_GithubSettings as GithubSettings, type config_ModelSettings as ModelSettings, type config_NetworkSettings as NetworkSettings, 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_ReleaseScheduleSettings as ReleaseScheduleSettings, type config_ReleasesSettings as ReleasesSettings, 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_SummarizeSettings as SummarizeSettings, type config_TrendingSettings as TrendingSettings, config_effectiveBaseURL as effectiveBaseURL, config_isEnrich as isEnrich, 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 };
536
+ 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_Enrich as Enrich, type config_GithubSettings as GithubSettings, type config_ModelSettings as ModelSettings, type config_NetworkSettings as NetworkSettings, type config_ObserveSettings as ObserveSettings, 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_ReleaseScheduleSettings as ReleaseScheduleSettings, type config_ReleasesSettings as ReleasesSettings, 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_SummarizeSettings as SummarizeSettings, type config_TrendingSettings as TrendingSettings, config_effectiveBaseURL as effectiveBaseURL, config_isEnrich as isEnrich, 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 };
512
537
  }
513
538
 
514
539
  declare const PLUGIN_DIR_NAME = "github-trending";
@@ -1595,16 +1620,6 @@ declare namespace pendingPr {
1595
1620
  export { type pendingPr_CheckPendingPrOptions as CheckPendingPrOptions, type pendingPr_ParsedPullUrl as ParsedPullUrl, type pendingPr_PendingPrStatus as PendingPrStatus, type pendingPr_PullState as PullState, pendingPr_checkPendingPr as checkPendingPr, pendingPr_clearPendingPrCache as clearPendingPrCache, pendingPr_parsePullUrl as parsePullUrl };
1596
1621
  }
1597
1622
 
1598
- /**
1599
- * 支持 `run --force`(忽略摘要缓存重新生成)的最低核心版本。
1600
- * 与 core patch 发版 `0.7.1` 对齐;`dsh.core.version` 应 >= 它,dev override 指向更旧核心时靠闸门降级。
1601
- */
1602
- declare const FORCE_MIN_CORE_VERSION = "0.7.1";
1603
- /**
1604
- * 支持 `run --stages releases --repo`(单仓发版分析)的最低核心版本。
1605
- * 与 core v0.8.0 对齐;核心更旧时单仓触发降级为全量(不传 --repo)。
1606
- */
1607
- declare const REPO_MIN_CORE_VERSION = "0.8.0";
1608
1623
  interface RunSummary {
1609
1624
  id: string;
1610
1625
  ok: boolean;
@@ -1665,13 +1680,6 @@ interface RunnerDeps {
1665
1680
  /** 预览副本这类「可失败但不阻断运行」的事件走这里,不写进 warnings。 */
1666
1681
  logger?: (event: string, data?: Record<string, unknown>) => void;
1667
1682
  }
1668
- /**
1669
- * 与 core `render.NameOf` 对齐的产物基名(不含扩展名):
1670
- * daily → YYYY-MM-DD;weekly → YYYY-Wnn(ISO 周年/周);monthly → YYYY-MM。
1671
- * 用于在 outcome.files 里按周期精确挑出本期 digest 的 `.md`(而不是猜)。
1672
- */
1673
- declare function isoWeekName(date: string): string;
1674
- declare function periodFileBase(period: Period, date: string): string;
1675
1683
  /**
1676
1684
  * 从契约 `RunOutcome` 投影出「token 总量」与「发版摘要」:
1677
1685
  * - `tokens` = usage.totalTokens + releases.usage.totalTokens(含订阅阶段);
@@ -1739,11 +1747,13 @@ declare class Runner {
1739
1747
  private summarize;
1740
1748
  private failureSummary;
1741
1749
  /**
1742
- * 从本次核心返回的 artifact 清单里选当天的 `.md`,只读复制到 previews/。
1750
+ * 把本次运行渲染的 digest 原文写进 previews/(ipc v3 起由核心随 RunResult 回传)。
1743
1751
  *
1744
- * 这是插件**唯一**读取用户仓库的场景,而且只读核心自己刚写出的那一份:
1745
- * - 路径必须在 `repo.path` 内(词法 + realpath 各判一次,挡 `..`/绝对路径/符号链接);
1746
- * - 上限 512 KB、拒绝 NUL、sha256 必须与契约声明一致;
1752
+ * 这里**不再读用户仓库**:分支可能在运行结束前就被合并、切回 base 并删除,
1753
+ * 任何「事后去工作树里找文件」的做法都会读到旧版或空。文本、字节数与 sha256
1754
+ * 的跨字段一致性已在 `parseRunResult` 里校验过;这里只做展示侧的防御:
1755
+ * - 上限 512 KB(与预览读取上限一致)、拒绝 NUL;
1756
+ * - 落盘前统一 `redact()`,避免把密钥写进预览文件;
1747
1757
  * - 任何一步失败都只记日志,返回 undefined;运行结果不受影响。
1748
1758
  */
1749
1759
  private capturePreview;
@@ -1753,18 +1763,14 @@ declare class Runner {
1753
1763
  coreStatus(fresh?: boolean): Promise<CoreStatus>;
1754
1764
  }
1755
1765
 
1756
- declare const runner_FORCE_MIN_CORE_VERSION: typeof FORCE_MIN_CORE_VERSION;
1757
- declare const runner_REPO_MIN_CORE_VERSION: typeof REPO_MIN_CORE_VERSION;
1758
1766
  type runner_RunRequestOptions = RunRequestOptions;
1759
1767
  type runner_RunSummary = RunSummary;
1760
1768
  type runner_Runner = Runner;
1761
1769
  declare const runner_Runner: typeof Runner;
1762
1770
  type runner_RunnerDeps = RunnerDeps;
1763
- declare const runner_isoWeekName: typeof isoWeekName;
1764
- declare const runner_periodFileBase: typeof periodFileBase;
1765
1771
  declare const runner_projectUsage: typeof projectUsage;
1766
1772
  declare namespace runner {
1767
- export { runner_FORCE_MIN_CORE_VERSION as FORCE_MIN_CORE_VERSION, runner_REPO_MIN_CORE_VERSION as REPO_MIN_CORE_VERSION, 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, runner_projectUsage as projectUsage };
1773
+ export { type runner_RunRequestOptions as RunRequestOptions, type runner_RunSummary as RunSummary, runner_Runner as Runner, type runner_RunnerDeps as RunnerDeps, runner_projectUsage as projectUsage };
1768
1774
  }
1769
1775
 
1770
1776
  /** 订阅命令超时:核心只做一次 GitHub 请求/本地文件读写,8 s 足够。 */
@@ -1958,23 +1964,6 @@ declare namespace schedule {
1958
1964
  export { schedule_RELEASE_CATCH_UP_MINUTES as RELEASE_CATCH_UP_MINUTES, type schedule_ReleaseScheduleConfig as ReleaseScheduleConfig, 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_isReleaseDue as isReleaseDue, schedule_isoWeekKey as isoWeekKey, schedule_nextOccurrence as nextOccurrence, schedule_nextOccurrenceFor as nextOccurrenceFor, schedule_nextReleaseOccurrence as nextReleaseOccurrence, schedule_parseClock as parseClock, schedule_zonedDateString as zonedDateString, schedule_zonedTimeToUtc as zonedTimeToUtc };
1959
1965
  }
1960
1966
 
1961
- /**
1962
- * 版本比较:只用于「核心是否足够新到支持某个 CLI flag」这类能力闸门。
1963
- *
1964
- * 核心的 `version --json` 只给 `x.y.z`(偶带预发布后缀),这里按三段数字比较,
1965
- * 后缀忽略。不做完整 semver 区间——项目只需要「>= 阈值」这一种判断,够用且不易错。
1966
- */
1967
- /**
1968
- * `a >= b` 时返回 true。任一方不可解析时返回 false(保守:当作「不满足」,
1969
- * 让调用方降级为不带新 flag 的行为,避免向旧核心传未知参数)。
1970
- */
1971
- declare function versionAtLeast(a: string, b: string): boolean;
1972
-
1973
- declare const version_versionAtLeast: typeof versionAtLeast;
1974
- declare namespace version {
1975
- export { version_versionAtLeast as versionAtLeast };
1976
- }
1977
-
1978
1967
  /**
1979
1968
  * Host HTTP 面:`/api/morehao/github-trending/*`。
1980
1969
  *
@@ -2324,4 +2313,4 @@ declare namespace tools {
2324
2313
  export { tools_createTrendingHistoryTool as createTrendingHistoryTool, tools_createTrendingRunTool as createTrendingRunTool };
2325
2314
  }
2326
2315
 
2327
- export { channel, config, core, credentials, errors, github, githubAuth, handshake$1 as handshake, importCli, ipc, locate, paths, pendingPr, pluginInfo, preflight, proxy, quickfill, release, repo, routes, run, runner, schedule, state, subscribe, tools, update as updateService, version };
2316
+ export { channel, config, core, credentials, errors, github, githubAuth, handshake$1 as handshake, importCli, ipc, locate, paths, pendingPr, pluginInfo, preflight, proxy, quickfill, release, repo, routes, run, runner, schedule, state, subscribe, tools, update as updateService };
package/lib/internal.js CHANGED
@@ -25,9 +25,8 @@ import {
25
25
  state_exports,
26
26
  subscribe_exports,
27
27
  tools_exports,
28
- update_exports,
29
- version_exports
30
- } from "./chunk-SLY2RKCZ.js";
28
+ update_exports
29
+ } from "./chunk-JMIWABMY.js";
31
30
 
32
31
  // src/internal.ts
33
32
  import * as exec from "@morehao/dsh-plugin-kit/exec";
@@ -59,6 +58,5 @@ export {
59
58
  state_exports as state,
60
59
  subscribe_exports as subscribe,
61
60
  tools_exports as tools,
62
- update_exports as updateService,
63
- version_exports as version
61
+ update_exports as updateService
64
62
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morehao/dsh-github-trending",
3
- "version": "0.8.22",
3
+ "version": "0.9.0",
4
4
  "description": "GitHub 趋势日报的 DeepSeek Harness 插件:按日 / 周 / 月周期定时 spawn 本地 trendradar 核心,抓取 Trending、生成中文简介与周期总结,并可选把订阅仓库的新 Release 合成中文摘要,一起提交到你的仓库、自动开 PR 合并;内置概览 / 榜单 / 运行记录 / 订阅 / 设置工作台。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -52,7 +52,7 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@morehao/dsh-plugin-kit": "^0.4.1",
55
- "@morehao/trendradar-contract": "0.5.0"
55
+ "@morehao/trendradar-contract": "0.6.0"
56
56
  },
57
57
  "dsh": {
58
58
  "bundle": {
@@ -60,7 +60,7 @@
60
60
  },
61
61
  "core": {
62
62
  "repo": "morehao/trendradar",
63
- "version": "0.8.7"
63
+ "version": "0.9.0"
64
64
  },
65
65
  "client": {
66
66
  "platform": "web",