@morehao/dsh-github-trending 0.4.1 → 0.4.3

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.
@@ -56,7 +56,7 @@ var DEFAULT_SETTINGS = {
56
56
  github: { authMode: "gh", tokenRef: "GITHUB_TOKEN" },
57
57
  trending: { since: "daily", languages: ["all", "typescript", "python", "go", "rust"], topN: 25 },
58
58
  output: { dir: "trending" },
59
- schedule: { enabled: true, time: "08:30:00", timezone: "Asia/Shanghai", periods: ["daily"] },
59
+ schedule: { enabled: true, time: "08:30:00", timezone: "Asia/Shanghai", periods: ["daily", "weekly", "monthly"] },
60
60
  corePath: "",
61
61
  coreAutoDownload: true
62
62
  };
@@ -117,10 +117,8 @@ function strArray(value, fallback) {
117
117
  const items = value.filter((item) => typeof item === "string" && item.trim() !== "");
118
118
  return items.length > 0 ? items.map((item) => item.trim()) : fallback;
119
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));
120
+ function periodList(_value, _fallback) {
121
+ return [...PERIODS];
124
122
  }
125
123
  function normalizeSettings(input) {
126
124
  const raw = typeof input === "object" && input !== null ? input : {};
@@ -243,9 +241,6 @@ function validateSettings(settings, providers = CORE_PROVIDERS) {
243
241
  if (!isValidTimezone(schedule.timezone)) {
244
242
  problems.push({ field: "schedule.timezone", code: "CONFIG_INVALID", message: "\u65F6\u533A\u5FC5\u987B\u662F IANA \u540D\u79F0\uFF08\u5982 Asia/Shanghai\uFF09" });
245
243
  }
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
- }
249
244
  return problems;
250
245
  }
251
246
  function effectiveBaseURL(model) {
@@ -363,8 +358,8 @@ __export(state_exports, {
363
358
  });
364
359
  import { mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync, appendFileSync, existsSync } from "fs";
365
360
  import path3 from "path";
366
- var HISTORY_LIMIT = 30;
367
- var PREVIEW_LIMIT = 30;
361
+ var HISTORY_LIMIT = 90;
362
+ var PREVIEW_LIMIT = 90;
368
363
  var PREVIEW_MAX_BYTES = 512 * 1024;
369
364
  var CONTENT_HASH_ALGO_V2 = "jcs-sha256-v1";
370
365
  var V2_CORE_MIN = [0, 3, 0];
@@ -483,7 +478,7 @@ var Store = class {
483
478
  const file = readJson(this.historyPath, { records: [] });
484
479
  return Array.isArray(file.records) ? file.records : [];
485
480
  }
486
- /** 记录一次运行:历史裁剪到 30 条,state 同步更新。 */
481
+ /** 记录一次运行:历史裁剪到 HISTORY_LIMIT 条,state 同步更新。 */
487
482
  recordRun(record, next = {}) {
488
483
  const records = [record, ...this.readHistoryRaw()].slice(0, HISTORY_LIMIT);
489
484
  writeJsonAtomic(this.historyPath, { records });
@@ -1498,12 +1493,25 @@ async function runCore(options) {
1498
1493
  // src/host/core.ts
1499
1494
  var core_exports = {};
1500
1495
  __export(core_exports, {
1496
+ coreRepoFromSource: () => coreRepoFromSource,
1501
1497
  detectCoexistence: () => detectCoexistence,
1502
1498
  findOnPath: () => findOnPath,
1503
1499
  inspectCore: () => inspectCore
1504
1500
  });
1505
1501
  import { accessSync as accessSync2, constants as constants3, statSync as statSync4 } from "fs";
1506
1502
  import path7 from "path";
1503
+ function coreRepoFromSource(repo) {
1504
+ let ownerRepo = repo;
1505
+ if (ownerRepo === void 0) {
1506
+ try {
1507
+ ownerRepo = readCoreRelease().repo;
1508
+ } catch {
1509
+ return void 0;
1510
+ }
1511
+ }
1512
+ if (typeof ownerRepo !== "string" || !/^[\w.-]+\/[\w.-]+$/.test(ownerRepo)) return void 0;
1513
+ return `https://github.com/${ownerRepo}`;
1514
+ }
1507
1515
  var CACHE_TTL_MS = 3e4;
1508
1516
  function executable2(file) {
1509
1517
  try {
@@ -1544,7 +1552,9 @@ async function inspectCore(settings, options = {}) {
1544
1552
  binaryPath: view.descriptor.binaryPath,
1545
1553
  cached: view.descriptor.cached
1546
1554
  };
1555
+ const coreRepository = coreRepoFromSource(release?.repo);
1547
1556
  const base = {
1557
+ ...coreRepository === void 0 ? {} : { repository: coreRepository },
1548
1558
  ...release === void 0 ? {} : { release },
1549
1559
  ...view.override === void 0 ? {} : { override: view.override },
1550
1560
  download: view.override !== void 0 ? { state: "override" } : release === void 0 ? { state: "missing", reason: view.error ?? "release \u672A\u914D\u7F6E" } : { state: release.cached ? "cached" : "missing" }
@@ -2155,7 +2165,7 @@ var Scheduler = class {
2155
2165
  for (const key of this.attempts.keys()) {
2156
2166
  if (!key.endsWith(`:${today}`)) this.attempts.delete(key);
2157
2167
  }
2158
- const periods = schedule.periods.length > 0 ? schedule.periods : ["daily"];
2168
+ const periods = schedule.periods.length > 0 ? schedule.periods : PERIODS;
2159
2169
  let ran = false;
2160
2170
  for (const period of periods) {
2161
2171
  if (!isPeriodTriggerDay(period, now, schedule.timezone)) continue;
@@ -2200,7 +2210,7 @@ var Scheduler = class {
2200
2210
  next() {
2201
2211
  const schedule = this.readSchedule();
2202
2212
  if (!schedule.enabled) return void 0;
2203
- const periods = schedule.periods.length > 0 ? schedule.periods : ["daily"];
2213
+ const periods = schedule.periods.length > 0 ? schedule.periods : PERIODS;
2204
2214
  try {
2205
2215
  const now = /* @__PURE__ */ new Date();
2206
2216
  let earliest;
@@ -2289,6 +2299,8 @@ function periodFileBase(period, date) {
2289
2299
  }
2290
2300
  var Runner = class {
2291
2301
  active;
2302
+ /** 批量运行(一次触发跑多个周期)期间为 true,挡住外部并发触发。 */
2303
+ batching = false;
2292
2304
  deps;
2293
2305
  constructor(deps) {
2294
2306
  this.deps = deps;
@@ -2301,13 +2313,13 @@ var Runner = class {
2301
2313
  this.active.controller.abort();
2302
2314
  return true;
2303
2315
  }
2304
- async run(options) {
2316
+ async run(options, _internal = false) {
2305
2317
  const env = this.deps.env ?? process.env;
2306
2318
  const store = this.deps.store;
2307
2319
  const settings = store.readSettings();
2308
2320
  const credentials = credentialServices(this.deps.get);
2309
- if (this.active !== void 0) {
2310
- throw new IpcError("CONFIG_INVALID", `\u5DF2\u6709\u4E00\u6B21\u8FD0\u884C\u5728\u8FDB\u884C\u4E2D\uFF08${this.active.id}\uFF09\uFF0C\u8BF7\u5148\u7B49\u5F85\u6216\u53D6\u6D88`);
2321
+ if (!_internal && (this.active !== void 0 || this.batching)) {
2322
+ throw new IpcError("CONFIG_INVALID", `\u5DF2\u6709\u4E00\u6B21\u8FD0\u884C\u5728\u8FDB\u884C\u4E2D\uFF08${this.active?.id ?? "\u6279\u91CF\u8FD0\u884C"}\uFF09\uFF0C\u8BF7\u5148\u7B49\u5F85\u6216\u53D6\u6D88`);
2311
2323
  }
2312
2324
  const problems = validateSettings(settings);
2313
2325
  if (problems.length > 0) {
@@ -2429,6 +2441,28 @@ var Runner = class {
2429
2441
  this.active = void 0;
2430
2442
  }
2431
2443
  }
2444
+ /**
2445
+ * 批量运行:按顺序对多个周期各跑一次(手动/工具触发时默认产出日/周/月三榜)。
2446
+ * 每次内部独享 id / 日志 / 摘要;整个批次期间置 `batching`,挡住外部并发触发。
2447
+ * 取消只中止当前周期,批次继续跑后续周期(用户取消单次即可)。
2448
+ */
2449
+ async runMany(options) {
2450
+ if (this.active !== void 0 || this.batching) {
2451
+ throw new IpcError("CONFIG_INVALID", `\u5DF2\u6709\u4E00\u6B21\u8FD0\u884C\u5728\u8FDB\u884C\u4E2D\uFF08${this.active?.id ?? "\u6279\u91CF\u8FD0\u884C"}\uFF09\uFF0C\u8BF7\u5148\u7B49\u5F85\u6216\u53D6\u6D88`);
2452
+ }
2453
+ const periods = options.periods !== void 0 && options.periods.length > 0 ? options.periods : [...PERIODS];
2454
+ this.batching = true;
2455
+ const summaries = [];
2456
+ try {
2457
+ for (const period of periods) {
2458
+ summaries.push(await this.run({ ...options, period }, true));
2459
+ }
2460
+ } finally {
2461
+ this.batching = false;
2462
+ this.active = void 0;
2463
+ }
2464
+ return summaries;
2465
+ }
2432
2466
  summarize(input) {
2433
2467
  const { outcome } = input;
2434
2468
  const error = outcome.error === void 0 ? void 0 : (() => {
@@ -3326,13 +3360,17 @@ async function dispatch(method, pathname, url, request, deps) {
3326
3360
  }
3327
3361
  if (method === "POST" && pathname === "/run") {
3328
3362
  const body = await readBody2(request);
3329
- const summary = await deps.runner.run({
3363
+ const base = {
3330
3364
  dryRun: body.dryRun === true,
3331
3365
  ...typeof body.date === "string" && body.date !== "" ? { date: body.date } : {},
3332
- ...isPeriod(body.period) ? { period: body.period } : {},
3333
3366
  trigger: "manual"
3334
- });
3335
- return { summary };
3367
+ };
3368
+ if (isPeriod(body.period)) {
3369
+ const summary = await deps.runner.run({ ...base, period: body.period });
3370
+ return { summary, summaries: [summary] };
3371
+ }
3372
+ const summaries = await deps.runner.runMany(base);
3373
+ return { summary: summaries[summaries.length - 1], summaries };
3336
3374
  }
3337
3375
  if (method === "POST" && pathname === "/run/cancel") {
3338
3376
  return { cancelled: deps.runner.cancel() };
@@ -3602,11 +3640,11 @@ function describeRecord(record) {
3602
3640
  function createTrendingRunTool(runner) {
3603
3641
  return {
3604
3642
  name: "trending_run",
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",
3643
+ description: "\u6267\u884C GitHub \u8D8B\u52BF\u699C\u5355\uFF1A\u5B9A\u4F4D\u5E76\u63E1\u624B\u672C\u5730\u6838\u5FC3\uFF0Cspawn \u6838\u5FC3 run\uFF0C\u8FD4\u56DE\u7ED3\u6784\u5316\u6458\u8981\uFF08\u6587\u4EF6\u6570\u3001commit\u3001PR\u3001\u9519\u8BEF\u7801\uFF09\u3002\u7701\u7565 period \u65F6\u9ED8\u8BA4\u4EA7\u51FA\u65E5/\u5468/\u6708\u4E09\u699C\uFF08\u4F9D\u6B21\u8FD0\u884C\u4E09\u6B21\uFF0C\u8FD4\u56DE summaries\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",
3606
3644
  parameters: {
3607
3645
  dryRun: { type: "boolean", description: "\u53EA\u8BD5\u8DD1\uFF1A\u4E0D\u5199\u6587\u4EF6\u3001\u4E0D\u63D0\u4EA4\u3001\u4E0D\u5F00 PR" },
3608
3646
  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" }
3647
+ period: { type: "string", enum: ["daily", "weekly", "monthly"], description: "\u53EA\u8DD1\u6307\u5B9A\u5468\u671F\uFF1B\u7701\u7565\u5219\u9ED8\u8BA4\u4EA7\u51FA\u65E5/\u5468/\u6708\u4E09\u699C" }
3610
3648
  },
3611
3649
  output: {
3612
3650
  schema: RUN_OUTPUT_SCHEMA,
@@ -3614,37 +3652,51 @@ function createTrendingRunTool(runner) {
3614
3652
  },
3615
3653
  async execute(args = {}, exec = {}) {
3616
3654
  void exec;
3617
- const summary = await runner.run({
3655
+ const base = {
3618
3656
  dryRun: args.dryRun === true,
3619
3657
  ...typeof args.date === "string" && args.date !== "" ? { date: args.date } : {},
3620
- ...isPeriod(args.period) ? { period: args.period } : {},
3621
3658
  trigger: "tool"
3622
- });
3659
+ };
3660
+ const summaries = isPeriod(args.period) ? [await runner.run({ ...base, period: args.period })] : await runner.runMany(base);
3623
3661
  const lines = [];
3624
- const head = summary.ok ? summary.skipped ? "\u65E0\u53D8\u66F4\uFF08skipped\uFF09" : summary.dryRun ? "\u8BD5\u8DD1\u5B8C\u6210" : "\u8FD0\u884C\u6210\u529F" : `\u8FD0\u884C\u5931\u8D25\uFF1A${summary.error?.code ?? "INTERNAL"}`;
3625
- lines.push(`${summary.ok ? "\u2713" : "\u2717"} ${head}`);
3626
- lines.push(`\xB7 \u6838\u5FC3 ${summary.coreVersion || "(\u672A\u77E5)"} \xB7 ${formatDuration(summary.durationMs)} \xB7 ${summary.files} \u4E2A\u6587\u4EF6`);
3627
- if (summary.contentHash !== void 0) lines.push(`\xB7 contentHash ${summary.contentHash.slice(0, 12)}`);
3628
- if (summary.branch !== void 0) lines.push(`\xB7 \u5206\u652F ${summary.branch}`);
3629
- if (summary.prUrl !== void 0) lines.push(`\xB7 PR ${summary.prUrl}`);
3630
- if (summary.error !== void 0) {
3631
- lines.push(`\xB7 ${summary.error.message}`);
3632
- lines.push(`\xB7 \u4E0B\u4E00\u6B65\uFF1A${summary.error.hint}`);
3662
+ for (const summary of summaries) {
3663
+ const periodLabel = summary.period ?? "\u2014";
3664
+ const head = summary.ok ? summary.skipped ? "\u65E0\u53D8\u66F4\uFF08skipped\uFF09" : summary.dryRun ? "\u8BD5\u8DD1\u5B8C\u6210" : "\u8FD0\u884C\u6210\u529F" : `\u8FD0\u884C\u5931\u8D25\uFF1A${summary.error?.code ?? "INTERNAL"}`;
3665
+ lines.push(`${summary.ok ? "\u2713" : "\u2717"} [${periodLabel}] ${head}`);
3666
+ lines.push(`\xB7 \u6838\u5FC3 ${summary.coreVersion || "(\u672A\u77E5)"} \xB7 ${formatDuration(summary.durationMs)} \xB7 ${summary.files} \u4E2A\u6587\u4EF6`);
3667
+ if (summary.contentHash !== void 0) lines.push(`\xB7 contentHash ${summary.contentHash.slice(0, 12)}`);
3668
+ if (summary.branch !== void 0) lines.push(`\xB7 \u5206\u652F ${summary.branch}`);
3669
+ if (summary.prUrl !== void 0) lines.push(`\xB7 PR ${summary.prUrl}`);
3670
+ if (summary.error !== void 0) {
3671
+ lines.push(`\xB7 ${summary.error.message}`);
3672
+ lines.push(`\xB7 \u4E0B\u4E00\u6B65\uFF1A${summary.error.hint}`);
3673
+ }
3633
3674
  }
3634
- if (summary.dryRun) lines.push("\xB7 \u8FD9\u662F\u8BD5\u8DD1\uFF0C\u6CA1\u6709\u5199\u6587\u4EF6\u3001\u6CA1\u6709\u63D0\u4EA4");
3675
+ if (summaries.length > 1) lines.push(`\xB7 \u672C\u6B21\u5171\u8DD1 ${summaries.length} \u4E2A\u5468\u671F\uFF08\u65E5/\u5468/\u6708\uFF09`);
3676
+ if (summaries.some((summary) => summary.dryRun)) lines.push("\xB7 \u8FD9\u662F\u8BD5\u8DD1\uFF0C\u6CA1\u6709\u5199\u6587\u4EF6\u3001\u6CA1\u6709\u63D0\u4EA4");
3677
+ const ok = summaries.every((summary) => summary.ok);
3678
+ const primary = summaries[summaries.length - 1];
3635
3679
  return {
3636
- id: summary.id,
3637
- ok: summary.ok,
3638
- skipped: summary.skipped,
3639
- dryRun: summary.dryRun,
3640
- durationMs: summary.durationMs,
3641
- coreVersion: summary.coreVersion,
3642
- files: summary.files,
3643
- ...summary.contentHash === void 0 ? {} : { contentHash: summary.contentHash },
3644
- ...summary.branch === void 0 ? {} : { branch: summary.branch },
3645
- ...summary.prUrl === void 0 ? {} : { prUrl: summary.prUrl },
3646
- ...summary.error === void 0 ? {} : { errorCode: summary.error.code, errorMessage: summary.error.message, errorHint: summary.error.hint },
3647
- warnings: summary.warnings,
3680
+ ok,
3681
+ summaries: summaries.map((summary) => ({
3682
+ id: summary.id,
3683
+ period: summary.period,
3684
+ ok: summary.ok,
3685
+ skipped: summary.skipped,
3686
+ dryRun: summary.dryRun,
3687
+ durationMs: summary.durationMs,
3688
+ files: summary.files,
3689
+ ...summary.branch === void 0 ? {} : { branch: summary.branch },
3690
+ ...summary.prUrl === void 0 ? {} : { prUrl: summary.prUrl },
3691
+ ...summary.error === void 0 ? {} : { errorCode: summary.error.code, errorMessage: summary.error.message }
3692
+ })),
3693
+ id: primary?.id,
3694
+ skipped: summaries.every((summary) => summary.skipped),
3695
+ dryRun: base.dryRun,
3696
+ durationMs: summaries.reduce((sum, summary) => sum + summary.durationMs, 0),
3697
+ files: summaries.reduce((sum, summary) => sum + summary.files, 0),
3698
+ warnings: summaries.flatMap((summary) => summary.warnings),
3699
+ ...primary?.error === void 0 ? {} : { errorCode: primary.error.code, errorMessage: primary.error.message, errorHint: primary.error.hint },
3648
3700
  markdown: lines.join("\n")
3649
3701
  };
3650
3702
  }
package/lib/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  createTrendingRunTool,
9
9
  createUpdateService,
10
10
  readPluginInfo
11
- } from "./chunk-QXAKMNHQ.js";
11
+ } from "./chunk-GBXP2NGW.js";
12
12
  import "./chunk-XLIFTAVL.js";
13
13
 
14
14
  // src/index.ts
package/lib/internal.d.ts CHANGED
@@ -222,6 +222,8 @@ interface ScheduleSettings {
222
222
  /**
223
223
  * 定时自动产出的周期(插件侧调度概念,核心无此字段)。
224
224
  * 每个周期按自己的节奏触发:日=每天、周=周日、月=月末。
225
+ * 现在固定为三周期全产出(`['daily','weekly','monthly']`),不再由用户勾选;
226
+ * 保留该字段以兼容旧设置、并驱动调度与去重逻辑。
225
227
  */
226
228
  periods: Period[];
227
229
  }
@@ -373,9 +375,9 @@ declare namespace paths {
373
375
  export { paths_HISTORY_FILE as HISTORY_FILE, paths_PLUGIN_DIR_NAME as PLUGIN_DIR_NAME, paths_SETTINGS_FILE as SETTINGS_FILE, paths_STATE_FILE as STATE_FILE, paths_dataDir as dataDir, paths_dshHome as dshHome, paths_homeDir as homeDir, paths_runsDir as runsDir };
374
376
  }
375
377
 
376
- declare const HISTORY_LIMIT = 30;
377
- /** 预览副本上限:最多保留 30 份,和 history 的 30 天窗口对齐。 */
378
- declare const PREVIEW_LIMIT = 30;
378
+ declare const HISTORY_LIMIT = 90;
379
+ /** 预览副本上限:与 history 对齐,三周期混跑时也要留够 90 期。 */
380
+ declare const PREVIEW_LIMIT = 90;
379
381
  /** 一次预览只读这么多字节;超过的不复制,避免把大文件塞进状态目录。 */
380
382
  declare const PREVIEW_MAX_BYTES: number;
381
383
  /** 契约 v2 的固定内容哈希算法;旧版状态没有这个字段,据此识别 legacy。 */
@@ -486,7 +488,7 @@ declare class Store {
486
488
  readHistory(): RunRecord[];
487
489
  /** 原始历史(不做 legacy 标注):写入路径用它,避免把「读时标注」持久化。 */
488
490
  private readHistoryRaw;
489
- /** 记录一次运行:历史裁剪到 30 条,state 同步更新。 */
491
+ /** 记录一次运行:历史裁剪到 HISTORY_LIMIT 条,state 同步更新。 */
490
492
  recordRun(record: RunRecord, next?: Partial<PluginState>): void;
491
493
  newLogPath(id: string): string;
492
494
  appendLog(file: string, line: string): void;
@@ -786,6 +788,8 @@ declare namespace run {
786
788
  export { run_DEFAULT_RUN_TIMEOUT_MS as DEFAULT_RUN_TIMEOUT_MS, type run_RunCoreOptions as RunCoreOptions, type run_RunCoreResult as RunCoreResult, run_buildCoreArgs as buildCoreArgs, run_runCore as runCore };
787
789
  }
788
790
 
791
+ /** `owner/repo`(可选带前后 `v`)→ `https://github.com/owner/repo`;非 GitHub 形态返回 undefined。 */
792
+ declare function coreRepoFromSource(repo: string | undefined): string | undefined;
789
793
  /** Release 分发下的核心视图:用户一眼能看出「版本、下没下、从哪来」。 */
790
794
  interface ReleaseStatus {
791
795
  repo: string;
@@ -816,6 +820,8 @@ interface CoreStatus {
816
820
  ipcSchema?: number;
817
821
  digestSchema?: number;
818
822
  supportedProviders: string[];
823
+ /** 核心开源仓库(`https://github.com/<owner>/<repo>`),来自 package.json 的 `dsh.core.repo`。 */
824
+ repository?: string;
819
825
  code?: string;
820
826
  message?: string;
821
827
  hint?: string;
@@ -849,11 +855,12 @@ declare function detectCoexistence(ownPath: string | undefined, options?: {
849
855
  type core_CoexistenceStatus = CoexistenceStatus;
850
856
  type core_CoreStatus = CoreStatus;
851
857
  type core_ReleaseStatus = ReleaseStatus;
858
+ declare const core_coreRepoFromSource: typeof coreRepoFromSource;
852
859
  declare const core_detectCoexistence: typeof detectCoexistence;
853
860
  declare const core_findOnPath: typeof findOnPath;
854
861
  declare const core_inspectCore: typeof inspectCore;
855
862
  declare namespace core {
856
- export { type core_CoexistenceStatus as CoexistenceStatus, type core_CoreStatus as CoreStatus, type core_ReleaseStatus as ReleaseStatus, core_detectCoexistence as detectCoexistence, core_findOnPath as findOnPath, core_inspectCore as inspectCore };
863
+ export { type core_CoexistenceStatus as CoexistenceStatus, type core_CoreStatus as CoreStatus, type core_ReleaseStatus as ReleaseStatus, core_coreRepoFromSource as coreRepoFromSource, core_detectCoexistence as detectCoexistence, core_findOnPath as findOnPath, core_inspectCore as inspectCore };
857
864
  }
858
865
 
859
866
  /**
@@ -1267,6 +1274,8 @@ declare function isoWeekName(date: string): string;
1267
1274
  declare function periodFileBase(period: Period, date: string): string;
1268
1275
  declare class Runner {
1269
1276
  private active;
1277
+ /** 批量运行(一次触发跑多个周期)期间为 true,挡住外部并发触发。 */
1278
+ private batching;
1270
1279
  private readonly deps;
1271
1280
  constructor(deps: RunnerDeps);
1272
1281
  get running(): {
@@ -1274,7 +1283,15 @@ declare class Runner {
1274
1283
  startedAt: string;
1275
1284
  } | undefined;
1276
1285
  cancel(): boolean;
1277
- run(options: RunRequestOptions): Promise<RunSummary>;
1286
+ run(options: RunRequestOptions, _internal?: boolean): Promise<RunSummary>;
1287
+ /**
1288
+ * 批量运行:按顺序对多个周期各跑一次(手动/工具触发时默认产出日/周/月三榜)。
1289
+ * 每次内部独享 id / 日志 / 摘要;整个批次期间置 `batching`,挡住外部并发触发。
1290
+ * 取消只中止当前周期,批次继续跑后续周期(用户取消单次即可)。
1291
+ */
1292
+ runMany(options: RunRequestOptions & {
1293
+ periods?: Period[];
1294
+ }): Promise<RunSummary[]>;
1278
1295
  private summarize;
1279
1296
  private failureSummary;
1280
1297
  /**
@@ -1319,17 +1336,6 @@ declare namespace pluginInfo {
1319
1336
  export { type pluginInfo_PluginInfo as PluginInfo, pluginInfo_normalizeRepositoryUrl as normalizeRepositoryUrl, pluginInfo_readPluginInfo as readPluginInfo };
1320
1337
  }
1321
1338
 
1322
- /**
1323
- * 调度(FR-P-08 / 规格 §9)。
1324
- *
1325
- * 规格原本设想「复用宿主 schedule 服务」,但实测:宿主的 `ctx.schedule` 是 **会话本地**
1326
- * 的提醒(rc.5 才引入,create 需要 sessionId,冷会话不投递),而本插件的运行完全不依赖会话。
1327
- * 因此这里实现规格 §9 已经写明的兜底:宿主 `timer` 服务(或 `setInterval`)60 s tick、
1328
- * 按 IANA 时区判当日、6 h 内补跑、同日不重复。
1329
- *
1330
- * 「下一次运行」也由同一份时区算术给出,界面与工具都用它。
1331
- */
1332
-
1333
1339
  interface ScheduleConfig {
1334
1340
  enabled: boolean;
1335
1341
  time: string;
@@ -1586,21 +1592,30 @@ declare function createTrendingRunTool(runner: Runner): {
1586
1592
  }, exec?: {
1587
1593
  signal?: AbortSignal;
1588
1594
  }): Promise<{
1589
- warnings: string[];
1590
1595
  markdown: string;
1591
1596
  errorCode?: string | undefined;
1592
1597
  errorMessage?: string | undefined;
1593
1598
  errorHint?: string | undefined;
1594
- prUrl?: string | undefined;
1595
- branch?: string | undefined;
1596
- contentHash?: string | undefined;
1597
- id: string;
1598
1599
  ok: boolean;
1600
+ summaries: {
1601
+ errorCode?: string | undefined;
1602
+ errorMessage?: string | undefined;
1603
+ prUrl?: string | undefined;
1604
+ branch?: string | undefined;
1605
+ id: string;
1606
+ period: Period | undefined;
1607
+ ok: boolean;
1608
+ skipped: boolean;
1609
+ dryRun: boolean;
1610
+ durationMs: number;
1611
+ files: number;
1612
+ }[];
1613
+ id: string | undefined;
1599
1614
  skipped: boolean;
1600
1615
  dryRun: boolean;
1601
1616
  durationMs: number;
1602
- coreVersion: string;
1603
1617
  files: number;
1618
+ warnings: string[];
1604
1619
  }>;
1605
1620
  };
1606
1621
  declare function createTrendingHistoryTool(store: Store, scheduler: Scheduler): {
package/lib/internal.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  state_exports,
25
25
  tools_exports,
26
26
  update_exports
27
- } from "./chunk-QXAKMNHQ.js";
27
+ } from "./chunk-GBXP2NGW.js";
28
28
  import {
29
29
  exec_exports
30
30
  } from "./chunk-XLIFTAVL.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morehao/dsh-github-trending",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "GitHub 趋势日报插件:每天定时 spawn 本地 trendradar 核心,抓取 Trending、生成中文简介与总结,提交到你的仓库并自动合并 PR。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",