@morehao/dsh-github-trending 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +24 -11
- package/client.js +1987 -186
- package/lib/{chunk-HIJIQJCZ.js → chunk-X4PBXIZE.js} +376 -80
- package/lib/index.js +29 -11
- package/lib/internal.d.ts +137 -86
- package/lib/internal.js +3 -1
- package/package.json +7 -6
- package/contract/CONTRACT.sha256 +0 -10
- package/contract/UPSTREAM.json +0 -37
- package/contract/fixtures/digest-degraded.json +0 -39
- package/contract/fixtures/digest-empty-lang.json +0 -39
- package/contract/fixtures/digest-normal.json +0 -91
- package/contract/schema/api.v1.yaml +0 -70
- package/contract/schema/digest.v1.json +0 -24
- package/contract/schema/ipc.v1.json +0 -104
- package/contract/schema/model.v1.json +0 -46
|
@@ -338,14 +338,21 @@ var HISTORY_FILE = "history.json";
|
|
|
338
338
|
// src/state.ts
|
|
339
339
|
var state_exports = {};
|
|
340
340
|
__export(state_exports, {
|
|
341
|
+
CONTENT_HASH_ALGO_V2: () => CONTENT_HASH_ALGO_V2,
|
|
341
342
|
HISTORY_LIMIT: () => HISTORY_LIMIT,
|
|
343
|
+
PREVIEW_LIMIT: () => PREVIEW_LIMIT,
|
|
344
|
+
PREVIEW_MAX_BYTES: () => PREVIEW_MAX_BYTES,
|
|
342
345
|
Store: () => Store,
|
|
343
346
|
newRunId: () => newRunId,
|
|
344
347
|
writeJsonAtomic: () => writeJsonAtomic
|
|
345
348
|
});
|
|
346
|
-
import { mkdirSync, readFileSync, renameSync, writeFileSync, appendFileSync, existsSync } from "fs";
|
|
349
|
+
import { mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync, appendFileSync, existsSync } from "fs";
|
|
347
350
|
import path3 from "path";
|
|
348
351
|
var HISTORY_LIMIT = 30;
|
|
352
|
+
var PREVIEW_LIMIT = 30;
|
|
353
|
+
var PREVIEW_MAX_BYTES = 512 * 1024;
|
|
354
|
+
var CONTENT_HASH_ALGO_V2 = "jcs-sha256-v1";
|
|
355
|
+
var V2_CORE_MIN = [0, 3, 0];
|
|
349
356
|
function readJson(file, fallback) {
|
|
350
357
|
try {
|
|
351
358
|
const text = readFileSync(file, "utf8");
|
|
@@ -359,6 +366,32 @@ function readJson(file, fallback) {
|
|
|
359
366
|
function ensureDir(dir) {
|
|
360
367
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
361
368
|
}
|
|
369
|
+
function coreVersionIsV2(coreVersion) {
|
|
370
|
+
if (typeof coreVersion !== "string") return false;
|
|
371
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(coreVersion.trim());
|
|
372
|
+
if (match === null) return false;
|
|
373
|
+
const major = Number(match[1]);
|
|
374
|
+
const minor = Number(match[2]);
|
|
375
|
+
const patch = Number(match[3]);
|
|
376
|
+
const [minMajor, minMinor, minPatch] = V2_CORE_MIN;
|
|
377
|
+
if (major !== minMajor) return major > minMajor;
|
|
378
|
+
if (minor !== minMinor) return minor > minMinor;
|
|
379
|
+
return patch >= minPatch;
|
|
380
|
+
}
|
|
381
|
+
function digestIsV2(digest) {
|
|
382
|
+
return digest !== void 0 && digest.contentHashAlgo === CONTENT_HASH_ALGO_V2 && coreVersionIsV2(digest.coreVersion);
|
|
383
|
+
}
|
|
384
|
+
function normalizeState(state) {
|
|
385
|
+
const next = { ...state };
|
|
386
|
+
if (next.lastDigest !== void 0 && !digestIsV2(next.lastDigest)) {
|
|
387
|
+
delete next.lastDigest;
|
|
388
|
+
delete next.pendingPr;
|
|
389
|
+
}
|
|
390
|
+
if (next.lastResult !== void 0 && next.lastResult.legacy !== true && !coreVersionIsV2(next.lastResult.coreVersion)) {
|
|
391
|
+
next.lastResult = { ...next.lastResult, legacy: true };
|
|
392
|
+
}
|
|
393
|
+
return next;
|
|
394
|
+
}
|
|
362
395
|
function writeJsonAtomic(file, value) {
|
|
363
396
|
ensureDir(path3.dirname(file));
|
|
364
397
|
const temp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
@@ -385,6 +418,9 @@ var Store = class {
|
|
|
385
418
|
get logsPath() {
|
|
386
419
|
return path3.join(this.dir, "runs");
|
|
387
420
|
}
|
|
421
|
+
get previewsPath() {
|
|
422
|
+
return path3.join(this.dir, "previews");
|
|
423
|
+
}
|
|
388
424
|
readSettings() {
|
|
389
425
|
return normalizeSettings(readJson(this.settingsPath, {}));
|
|
390
426
|
}
|
|
@@ -392,18 +428,49 @@ var Store = class {
|
|
|
392
428
|
writeJsonAtomic(this.settingsPath, settings);
|
|
393
429
|
}
|
|
394
430
|
readState() {
|
|
395
|
-
return readJson(this.statePath, {});
|
|
431
|
+
return normalizeState(readJson(this.statePath, {}));
|
|
396
432
|
}
|
|
397
433
|
writeState(state) {
|
|
398
434
|
writeJsonAtomic(this.statePath, state);
|
|
399
435
|
}
|
|
436
|
+
/**
|
|
437
|
+
* 把 v1 时代遗留的 state / history 一次性清洗落盘:清掉不可信的 lastDigest 与
|
|
438
|
+
* 与之绑定的 pendingPr,并给旧历史记录打 legacy 标。插件启动时调用一次。
|
|
439
|
+
*/
|
|
440
|
+
migrateLegacyState() {
|
|
441
|
+
const rawState = readJson(this.statePath, {});
|
|
442
|
+
const nextState = normalizeState(rawState);
|
|
443
|
+
const stateChanged = JSON.stringify(nextState) !== JSON.stringify(rawState);
|
|
444
|
+
if (stateChanged) this.writeState(nextState);
|
|
445
|
+
const rawHistory = readJson(this.historyPath, { records: [] });
|
|
446
|
+
const records = Array.isArray(rawHistory.records) ? rawHistory.records : [];
|
|
447
|
+
let historyMarked = 0;
|
|
448
|
+
const nextRecords = records.map((record) => {
|
|
449
|
+
if (record.legacy === true || coreVersionIsV2(record.coreVersion)) return record;
|
|
450
|
+
historyMarked += 1;
|
|
451
|
+
return { ...record, legacy: true };
|
|
452
|
+
});
|
|
453
|
+
if (historyMarked > 0) writeJsonAtomic(this.historyPath, { records: nextRecords });
|
|
454
|
+
return {
|
|
455
|
+
stateChanged,
|
|
456
|
+
clearedDigest: rawState.lastDigest !== void 0 && nextState.lastDigest === void 0,
|
|
457
|
+
clearedPendingPr: rawState.pendingPr !== void 0 && nextState.pendingPr === void 0,
|
|
458
|
+
historyMarked
|
|
459
|
+
};
|
|
460
|
+
}
|
|
400
461
|
readHistory() {
|
|
462
|
+
return this.readHistoryRaw().map(
|
|
463
|
+
(record) => record.legacy === true || coreVersionIsV2(record.coreVersion) ? record : { ...record, legacy: true }
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
/** 原始历史(不做 legacy 标注):写入路径用它,避免把「读时标注」持久化。 */
|
|
467
|
+
readHistoryRaw() {
|
|
401
468
|
const file = readJson(this.historyPath, { records: [] });
|
|
402
469
|
return Array.isArray(file.records) ? file.records : [];
|
|
403
470
|
}
|
|
404
471
|
/** 记录一次运行:历史裁剪到 30 条,state 同步更新。 */
|
|
405
472
|
recordRun(record, next = {}) {
|
|
406
|
-
const records = [record, ...this.
|
|
473
|
+
const records = [record, ...this.readHistoryRaw()].slice(0, HISTORY_LIMIT);
|
|
407
474
|
writeJsonAtomic(this.historyPath, { records });
|
|
408
475
|
const state = this.readState();
|
|
409
476
|
this.writeState({ ...state, ...next, lastResult: record });
|
|
@@ -428,6 +495,61 @@ var Store = class {
|
|
|
428
495
|
return "";
|
|
429
496
|
}
|
|
430
497
|
}
|
|
498
|
+
/** 预览副本路径;写入前才建目录,读接口不会凭空创建目录。 */
|
|
499
|
+
previewPath(id) {
|
|
500
|
+
return path3.join(this.previewsPath, `${id}.md`);
|
|
501
|
+
}
|
|
502
|
+
/** 写预览副本:同目录临时文件 + rename、0600;写完裁到 30 份。 */
|
|
503
|
+
writePreview(id, text) {
|
|
504
|
+
ensureDir(this.previewsPath);
|
|
505
|
+
const file = this.previewPath(id);
|
|
506
|
+
const temp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
507
|
+
writeFileSync(temp, text, { mode: 384 });
|
|
508
|
+
renameSync(temp, file);
|
|
509
|
+
this.prunePreviews();
|
|
510
|
+
}
|
|
511
|
+
/** 读预览副本;不存在/不可读返回 undefined,超过上限时截断。 */
|
|
512
|
+
readPreview(id, maxBytes = PREVIEW_MAX_BYTES) {
|
|
513
|
+
const file = this.previewPath(id);
|
|
514
|
+
try {
|
|
515
|
+
const stat2 = statSync(file);
|
|
516
|
+
if (!stat2.isFile()) return void 0;
|
|
517
|
+
const truncated = stat2.size > maxBytes;
|
|
518
|
+
const buffer = readFileSync(file);
|
|
519
|
+
return {
|
|
520
|
+
text: (truncated ? buffer.subarray(0, maxBytes) : buffer).toString("utf8"),
|
|
521
|
+
truncated
|
|
522
|
+
};
|
|
523
|
+
} catch {
|
|
524
|
+
return void 0;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
/** 只保留最近 `keep` 份预览副本;按 mtime 倒序,任何错误都忽略。 */
|
|
528
|
+
prunePreviews(keep = PREVIEW_LIMIT) {
|
|
529
|
+
let names;
|
|
530
|
+
try {
|
|
531
|
+
names = readdirSync(this.previewsPath);
|
|
532
|
+
} catch {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
const entries = [];
|
|
536
|
+
for (const name of names) {
|
|
537
|
+
if (!name.endsWith(".md")) continue;
|
|
538
|
+
const file = path3.join(this.previewsPath, name);
|
|
539
|
+
try {
|
|
540
|
+
const stat2 = statSync(file);
|
|
541
|
+
if (stat2.isFile()) entries.push({ file, mtimeMs: stat2.mtimeMs });
|
|
542
|
+
} catch {
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
entries.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
546
|
+
for (const entry of entries.slice(keep)) {
|
|
547
|
+
try {
|
|
548
|
+
unlinkSync(entry.file);
|
|
549
|
+
} catch {
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
431
553
|
};
|
|
432
554
|
function newRunId(now = /* @__PURE__ */ new Date()) {
|
|
433
555
|
const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\..+$/, "").replace("T", "-");
|
|
@@ -443,6 +565,7 @@ __export(errors_exports, {
|
|
|
443
565
|
classifyStderr: () => classifyStderr,
|
|
444
566
|
hintOf: () => hintOf,
|
|
445
567
|
isRetriable: () => isRetriable,
|
|
568
|
+
reasonOf: () => reasonOf,
|
|
446
569
|
redact: () => redact
|
|
447
570
|
});
|
|
448
571
|
var PLUGIN_CODES = [
|
|
@@ -505,9 +628,37 @@ function areaOf(code) {
|
|
|
505
628
|
if (code === "GITHUB_AUTH") return "auth";
|
|
506
629
|
return "run";
|
|
507
630
|
}
|
|
508
|
-
function
|
|
509
|
-
|
|
510
|
-
|
|
631
|
+
function reasonOf(code, message, details) {
|
|
632
|
+
const structured = typeof details?.reason === "string" && details.reason !== "" ? details.reason : void 0;
|
|
633
|
+
if (structured !== void 0) return structured;
|
|
634
|
+
if (code !== "GIT_OPERATION_FAILED" || typeof message !== "string") return void 0;
|
|
635
|
+
if (/stale info/i.test(message)) return "stale-lease";
|
|
636
|
+
if (/non-fast-forward|fetch first/i.test(message)) return "non-fast-forward";
|
|
637
|
+
if (/permission denied|authentication failed|could not read Username|invalid username or password/i.test(message)) return "auth";
|
|
638
|
+
if (/protected branch|GH006/i.test(message)) return "protected";
|
|
639
|
+
if (/hook declined|pre-receive hook/i.test(message)) return "hook";
|
|
640
|
+
if (/pathspec/i.test(message)) return "pathspec";
|
|
641
|
+
return "unknown";
|
|
642
|
+
}
|
|
643
|
+
function hintOf(code, message, reason) {
|
|
644
|
+
const gitReason = reason ?? reasonOf(code, message);
|
|
645
|
+
if (code === "GIT_OPERATION_FAILED" && gitReason !== void 0) {
|
|
646
|
+
switch (gitReason) {
|
|
647
|
+
case "stale-lease":
|
|
648
|
+
return "\u63A8\u9001\u88AB\u62D2\uFF08stale info\uFF09\uFF1A\u672C\u5730\u5BF9\u8FDC\u7AEF\u5206\u652F\u7684\u8BB0\u5F55\u8FC7\u671F\uFF08\u5206\u652F\u88AB\u5220\u6216\u88AB\u6539\uFF09\uFF0C\u4E0D\u662F\u6709\u4EBA\u62A2\u5148\u63A8\u9001\u3002\u5728\u4ED3\u5E93\u91CC\u6267\u884C git fetch --prune origin \u540E\u91CD\u8BD5\uFF1B\u5347\u7EA7\u6838\u5FC3\u540E\u4E0D\u4F1A\u518D\u51FA\u73B0\u3002";
|
|
649
|
+
case "non-fast-forward":
|
|
650
|
+
return "\u63A8\u9001\u88AB\u62D2\uFF08non-fast-forward\uFF09\uFF1A\u8FDC\u7AEF\u5206\u652F\u5DF2\u524D\u8FDB\u3002\u5148 git fetch \u5E76 rebase \u5230\u8FDC\u7AEF\u5206\u652F\uFF0C\u6216\u5220\u9664\u8BE5\u8FDC\u7AEF\u5206\u652F\u540E\u91CD\u8BD5\u3002";
|
|
651
|
+
case "auth":
|
|
652
|
+
return "\u63A8\u9001\u8BA4\u8BC1\u5931\u8D25\uFF1A\u66F4\u65B0 GitHub token / gh \u767B\u5F55\u540E\u91CD\u8BD5\uFF1B\u672C\u5730 commit \u5DF2\u4FDD\u7559\u3002";
|
|
653
|
+
case "protected":
|
|
654
|
+
return "\u76EE\u6807\u5206\u652F\u53D7\u4FDD\u62A4\uFF1A\u6539\u4E3A\u901A\u8FC7 PR \u63D0\u4EA4\uFF0C\u6216\u8C03\u6574\u5206\u652F\u4FDD\u62A4\u89C4\u5219\u3002";
|
|
655
|
+
case "hook":
|
|
656
|
+
return "\u4ED3\u5E93 hook \u62D2\u7EDD\u4E86\u64CD\u4F5C\uFF1A\u6309 hook \u8F93\u51FA\u4FEE\u6B63\u540E\u91CD\u8BD5\uFF1B\u672C\u5730 commit \u5DF2\u4FDD\u7559\u3002";
|
|
657
|
+
case "pathspec":
|
|
658
|
+
return "\u6682\u5B58\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF1A\u68C0\u67E5\u8BBE\u7F6E\u91CC\u7684\u8F93\u51FA\u76EE\u5F55\uFF08output.dir\uFF09\u662F\u5426\u4E0E\u4ED3\u5E93\u5B9E\u9645\u76EE\u5F55\u4E00\u81F4\u3002";
|
|
659
|
+
default:
|
|
660
|
+
return "\u63A8\u9001\u5931\u8D25\uFF1A\u672C\u5730 commit \u5DF2\u4FDD\u7559\uFF0C\u67E5\u770B\u8FD0\u884C\u65E5\u5FD7\u91CC\u7684 git \u8F93\u51FA\u540E\u91CD\u8BD5\u3002";
|
|
661
|
+
}
|
|
511
662
|
}
|
|
512
663
|
switch (code) {
|
|
513
664
|
case "CORE_UNAVAILABLE":
|
|
@@ -636,18 +787,17 @@ var ipc_exports = {};
|
|
|
636
787
|
__export(ipc_exports, {
|
|
637
788
|
IPC_SCHEMA_VERSION: () => IPC_SCHEMA_VERSION,
|
|
638
789
|
IpcError: () => IpcError,
|
|
790
|
+
SUPPORTED_CONTRACT_MAJOR: () => SUPPORTED_CONTRACT_MAJOR,
|
|
639
791
|
SUPPORTED_DIGEST_SCHEMA: () => SUPPORTED_DIGEST_SCHEMA,
|
|
640
792
|
isVersionInfo: () => isVersionInfo,
|
|
641
793
|
parseJsonObject: () => parseJsonObject,
|
|
642
794
|
parseRunResult: () => parseRunResult,
|
|
643
795
|
parseVersionInfo: () => parseVersionInfo
|
|
644
796
|
});
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
var
|
|
648
|
-
|
|
649
|
-
// src/ipc.ts
|
|
650
|
-
var SUPPORTED_DIGEST_SCHEMA = 1;
|
|
797
|
+
import { validators } from "@morehao/trendradar-contract";
|
|
798
|
+
var IPC_SCHEMA_VERSION = 2;
|
|
799
|
+
var SUPPORTED_DIGEST_SCHEMA = 2;
|
|
800
|
+
var SUPPORTED_CONTRACT_MAJOR = 0;
|
|
651
801
|
var IpcError = class extends Error {
|
|
652
802
|
code;
|
|
653
803
|
constructor(code, message) {
|
|
@@ -683,19 +833,6 @@ function parseVersionInfo(text) {
|
|
|
683
833
|
}
|
|
684
834
|
return value;
|
|
685
835
|
}
|
|
686
|
-
function normalizeFiles(value) {
|
|
687
|
-
if (!Array.isArray(value)) return [];
|
|
688
|
-
const files = [];
|
|
689
|
-
for (const item of value) {
|
|
690
|
-
if (!isRecord(item) || typeof item.path !== "string") continue;
|
|
691
|
-
files.push({
|
|
692
|
-
path: item.path,
|
|
693
|
-
bytes: typeof item.bytes === "number" ? item.bytes : 0,
|
|
694
|
-
sha256: typeof item.sha256 === "string" ? item.sha256 : ""
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
return files;
|
|
698
|
-
}
|
|
699
836
|
function normalizeError(value) {
|
|
700
837
|
if (!isRecord(value)) return void 0;
|
|
701
838
|
if (typeof value.code !== "string" || typeof value.message !== "string") return void 0;
|
|
@@ -708,37 +845,24 @@ function normalizeError(value) {
|
|
|
708
845
|
}
|
|
709
846
|
function parseRunResult(text, options) {
|
|
710
847
|
const value = parseJsonObject(text);
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
const result = value;
|
|
714
|
-
return {
|
|
715
|
-
ok: result.ok,
|
|
716
|
-
skipped: result.skipped === true,
|
|
717
|
-
dryRun: options.dryRun,
|
|
718
|
-
files: normalizeFiles(result.files),
|
|
719
|
-
contentHash: typeof value.contentHash === "string" ? value.contentHash : void 0,
|
|
720
|
-
branch: typeof value.branch === "string" ? value.branch : void 0,
|
|
721
|
-
commit: typeof value.commit === "string" ? value.commit : void 0,
|
|
722
|
-
prUrl: typeof value.prUrl === "string" ? value.prUrl : void 0,
|
|
723
|
-
error: normalizeError(result.error),
|
|
724
|
-
warnings: Array.isArray(result.warnings) ? result.warnings.filter((item) => typeof item === "string") : [],
|
|
725
|
-
...timing(options),
|
|
726
|
-
coreVersion: options.coreVersion
|
|
727
|
-
};
|
|
848
|
+
if (value.command !== "run" || value.ipcSchema !== IPC_SCHEMA_VERSION || !validators.runResult(value)) {
|
|
849
|
+
throw new IpcError("IPC_INVALID", "\u6838\u5FC3 run --json \u4E0D\u662F\u5951\u7EA6 RunResult\uFF08ipcSchema=2 / command=run\uFF09");
|
|
728
850
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
const digest = isRecord(value.digest) ? value.digest : void 0;
|
|
851
|
+
const result = value;
|
|
852
|
+
const error = normalizeError(result.error);
|
|
853
|
+
const skipReason = value.skipReason;
|
|
733
854
|
return {
|
|
734
|
-
ok:
|
|
735
|
-
skipped:
|
|
855
|
+
ok: result.ok,
|
|
856
|
+
skipped: result.skipped === true,
|
|
736
857
|
dryRun: options.dryRun,
|
|
737
|
-
files:
|
|
738
|
-
contentHash: typeof
|
|
739
|
-
branch: typeof
|
|
740
|
-
|
|
741
|
-
|
|
858
|
+
files: Array.isArray(result.files) ? result.files : [],
|
|
859
|
+
contentHash: typeof result.contentHash === "string" ? result.contentHash : void 0,
|
|
860
|
+
branch: typeof result.branch === "string" ? result.branch : void 0,
|
|
861
|
+
commit: typeof result.commit === "string" ? result.commit : void 0,
|
|
862
|
+
prUrl: typeof result.prUrl === "string" ? result.prUrl : void 0,
|
|
863
|
+
skipReason: typeof skipReason === "string" && skipReason !== "" ? skipReason : void 0,
|
|
864
|
+
error,
|
|
865
|
+
warnings: Array.isArray(result.warnings) ? result.warnings.filter((item) => typeof item === "string") : [],
|
|
742
866
|
...timing(options),
|
|
743
867
|
coreVersion: options.coreVersion
|
|
744
868
|
};
|
|
@@ -782,10 +906,11 @@ async function handshake(core, options = {}) {
|
|
|
782
906
|
const detail = error instanceof Error ? error.message : String(error);
|
|
783
907
|
throw new IpcError("CORE_INCOMPATIBLE", `\u6838\u5FC3 version --json \u8F93\u51FA\u65E0\u6CD5\u8BC6\u522B\uFF1A${detail}`);
|
|
784
908
|
}
|
|
785
|
-
|
|
909
|
+
const contractMajor = typeof version.contractVersion === "string" ? Number(version.contractVersion.split(".")[0]) : void 0;
|
|
910
|
+
if (version.ipcSchema !== IPC_SCHEMA_VERSION || version.digestSchema !== SUPPORTED_DIGEST_SCHEMA || contractMajor !== SUPPORTED_CONTRACT_MAJOR) {
|
|
786
911
|
throw new IpcError(
|
|
787
912
|
"CORE_INCOMPATIBLE",
|
|
788
|
-
`\u6838\u5FC3\u534F\u8BAE\u4E0D\u5339\u914D\uFF1A\u6838\u5FC3 ipcSchema=${version.ipcSchema} digestSchema=${version.digestSchema}\uFF0C\u63D2\u4EF6\u652F\u6301 ipcSchema=${IPC_SCHEMA_VERSION} digestSchema=${SUPPORTED_DIGEST_SCHEMA}\u3002\u8BF7\u628A\u63D2\u4EF6\u4E0E\u6838\u5FC3\u5347\u7EA7\u5230\u540C\u4E00\u7248\u672C\uFF08\u63D2\u4EF6\u5E26\u5E73\u53F0\u5305\uFF0C\u5347\u7EA7\u63D2\u4EF6\u5373\u53EF\uFF09\u3002`
|
|
913
|
+
`\u6838\u5FC3\u534F\u8BAE\u4E0D\u5339\u914D\uFF1A\u6838\u5FC3 ipcSchema=${version.ipcSchema} digestSchema=${version.digestSchema} contractVersion=${version.contractVersion ?? "(\u7F3A\u5931)"}\uFF0C\u63D2\u4EF6\u652F\u6301 ipcSchema=${IPC_SCHEMA_VERSION} digestSchema=${SUPPORTED_DIGEST_SCHEMA} contractVersion \u4E3B\u7248\u672C=${SUPPORTED_CONTRACT_MAJOR}\u3002\u8BF7\u628A\u63D2\u4EF6\u4E0E\u6838\u5FC3\u5347\u7EA7\u5230\u540C\u4E00\u7248\u672C\uFF08\u63D2\u4EF6\u5E26\u5E73\u53F0\u5305\uFF0C\u5347\u7EA7\u63D2\u4EF6\u5373\u53EF\uFF09\u3002`
|
|
789
914
|
);
|
|
790
915
|
}
|
|
791
916
|
const supportedProviders = Array.isArray(version.supportedProviders) && version.supportedProviders.length > 0 ? version.supportedProviders : [...DEFAULT_PROVIDERS];
|
|
@@ -803,7 +928,7 @@ __export(release_exports, {
|
|
|
803
928
|
verifyCached: () => verifyCached
|
|
804
929
|
});
|
|
805
930
|
import { createHash } from "crypto";
|
|
806
|
-
import { chmodSync, createWriteStream, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, rmSync, statSync, unlinkSync } from "fs";
|
|
931
|
+
import { chmodSync, createWriteStream, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, rmSync, statSync as statSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
807
932
|
import { createRequire } from "module";
|
|
808
933
|
import { pipeline } from "stream/promises";
|
|
809
934
|
import { Readable } from "stream";
|
|
@@ -870,7 +995,7 @@ function markerPath(binaryPath) {
|
|
|
870
995
|
function verifyCached(binaryPath) {
|
|
871
996
|
let info;
|
|
872
997
|
try {
|
|
873
|
-
info =
|
|
998
|
+
info = statSync2(binaryPath);
|
|
874
999
|
} catch {
|
|
875
1000
|
return { ok: false, reason: "missing" };
|
|
876
1001
|
}
|
|
@@ -984,7 +1109,7 @@ async function ensureCore(options = {}) {
|
|
|
984
1109
|
);
|
|
985
1110
|
}
|
|
986
1111
|
try {
|
|
987
|
-
|
|
1112
|
+
unlinkSync2(descriptor.binaryPath);
|
|
988
1113
|
} catch {
|
|
989
1114
|
}
|
|
990
1115
|
}
|
|
@@ -1005,11 +1130,11 @@ __export(locate_exports, {
|
|
|
1005
1130
|
locateCore: () => locateCore,
|
|
1006
1131
|
overridePath: () => overridePath
|
|
1007
1132
|
});
|
|
1008
|
-
import { accessSync, constants, existsSync as existsSync3, statSync as
|
|
1133
|
+
import { accessSync, constants, existsSync as existsSync3, statSync as statSync3 } from "fs";
|
|
1009
1134
|
import path5 from "path";
|
|
1010
1135
|
function executable(file) {
|
|
1011
1136
|
try {
|
|
1012
|
-
if (!
|
|
1137
|
+
if (!statSync3(file).isFile()) return false;
|
|
1013
1138
|
accessSync(file, constants.X_OK);
|
|
1014
1139
|
return true;
|
|
1015
1140
|
} catch {
|
|
@@ -1225,7 +1350,12 @@ async function runCore(options) {
|
|
|
1225
1350
|
`;
|
|
1226
1351
|
const secrets = [options.apiKey, options.token, ...options.secrets ?? []];
|
|
1227
1352
|
const child = spawn2(options.core.path, buildCoreArgs(channel.path, { dryRun: options.dryRun, date: options.date }), {
|
|
1228
|
-
env: sanitizedEnv(process.env, {
|
|
1353
|
+
env: sanitizedEnv(process.env, {
|
|
1354
|
+
GITHUB_TOKEN: options.token,
|
|
1355
|
+
GH_TOKEN: options.token,
|
|
1356
|
+
// 环境变量通道:旧核心会忽略它,不会因为多一个未知参数而失败。
|
|
1357
|
+
TRENDRADAR_TRIGGER: options.trigger ?? "manual"
|
|
1358
|
+
}),
|
|
1229
1359
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1230
1360
|
windowsHide: true
|
|
1231
1361
|
});
|
|
@@ -1356,12 +1486,12 @@ __export(core_exports, {
|
|
|
1356
1486
|
findOnPath: () => findOnPath,
|
|
1357
1487
|
inspectCore: () => inspectCore
|
|
1358
1488
|
});
|
|
1359
|
-
import { accessSync as accessSync2, constants as constants3, statSync as
|
|
1489
|
+
import { accessSync as accessSync2, constants as constants3, statSync as statSync4 } from "fs";
|
|
1360
1490
|
import path7 from "path";
|
|
1361
1491
|
var CACHE_TTL_MS = 3e4;
|
|
1362
1492
|
function executable2(file) {
|
|
1363
1493
|
try {
|
|
1364
|
-
if (!
|
|
1494
|
+
if (!statSync4(file).isFile()) return false;
|
|
1365
1495
|
accessSync2(file, constants3.X_OK);
|
|
1366
1496
|
return true;
|
|
1367
1497
|
} catch {
|
|
@@ -1457,7 +1587,7 @@ async function detectCoexistence(ownPath, options = {}) {
|
|
|
1457
1587
|
};
|
|
1458
1588
|
} else {
|
|
1459
1589
|
const info = JSON.parse(result.stdout);
|
|
1460
|
-
const compatible = info.ipcSchema ===
|
|
1590
|
+
const compatible = info.ipcSchema === IPC_SCHEMA_VERSION && info.digestSchema === SUPPORTED_DIGEST_SCHEMA;
|
|
1461
1591
|
status = {
|
|
1462
1592
|
found: true,
|
|
1463
1593
|
path: found,
|
|
@@ -1863,6 +1993,8 @@ __export(schedule_exports, {
|
|
|
1863
1993
|
});
|
|
1864
1994
|
var MINUTE = 6e4;
|
|
1865
1995
|
var CATCH_UP_MINUTES = 6 * 60;
|
|
1996
|
+
var MAX_ATTEMPTS_PER_DAY = 2;
|
|
1997
|
+
var RETRY_GAP_MS = 5 * MINUTE;
|
|
1866
1998
|
function partsOf(date, timezone) {
|
|
1867
1999
|
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
1868
2000
|
timeZone: timezone,
|
|
@@ -1938,7 +2070,8 @@ var Scheduler = class {
|
|
|
1938
2070
|
deps;
|
|
1939
2071
|
timerHandle;
|
|
1940
2072
|
stopped = false;
|
|
1941
|
-
|
|
2073
|
+
/** 当天已尝试次数与时间;只在内存里,进程重启后重新计数。 */
|
|
2074
|
+
attempts = /* @__PURE__ */ new Map();
|
|
1942
2075
|
constructor(deps) {
|
|
1943
2076
|
this.deps = deps;
|
|
1944
2077
|
}
|
|
@@ -1972,11 +2105,18 @@ var Scheduler = class {
|
|
|
1972
2105
|
if (!isDue(now, schedule)) return "skipped";
|
|
1973
2106
|
const today = zonedDateString(now, schedule.timezone);
|
|
1974
2107
|
const state = this.deps.store.readState();
|
|
1975
|
-
const
|
|
1976
|
-
if (
|
|
1977
|
-
|
|
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
|
+
}
|
|
1978
2115
|
if (this.deps.isRunning()) return "busy";
|
|
1979
|
-
this.
|
|
2116
|
+
for (const key of this.attempts.keys()) {
|
|
2117
|
+
if (key !== today) this.attempts.delete(key);
|
|
2118
|
+
}
|
|
2119
|
+
this.attempts.set(today, { count: (attempt?.count ?? 0) + 1, lastAt: now.getTime() });
|
|
1980
2120
|
try {
|
|
1981
2121
|
await this.deps.run("schedule");
|
|
1982
2122
|
return "ran";
|
|
@@ -2023,13 +2163,14 @@ var runner_exports = {};
|
|
|
2023
2163
|
__export(runner_exports, {
|
|
2024
2164
|
Runner: () => Runner
|
|
2025
2165
|
});
|
|
2026
|
-
import {
|
|
2166
|
+
import { createHash as createHash2 } from "crypto";
|
|
2167
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, realpathSync, statSync as statSync5 } from "fs";
|
|
2027
2168
|
import path8 from "path";
|
|
2028
2169
|
function repoProblem(repoPath) {
|
|
2029
2170
|
if (repoPath.trim() === "") return { code: "CONFIG_INVALID", message: "\u8FD8\u6CA1\u6709\u9009\u62E9\u5F52\u6863\u4ED3\u5E93" };
|
|
2030
2171
|
if (!existsSync4(repoPath)) return { code: "REPO_NOT_GIT", message: `\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${repoPath}` };
|
|
2031
2172
|
try {
|
|
2032
|
-
if (!
|
|
2173
|
+
if (!statSync5(repoPath).isDirectory()) return { code: "REPO_NOT_GIT", message: `\u4E0D\u662F\u76EE\u5F55\uFF1A${repoPath}` };
|
|
2033
2174
|
} catch {
|
|
2034
2175
|
return { code: "REPO_NOT_GIT", message: `\u65E0\u6CD5\u8BBF\u95EE\uFF1A${repoPath}` };
|
|
2035
2176
|
}
|
|
@@ -2045,6 +2186,15 @@ function runDate(settings, now = /* @__PURE__ */ new Date()) {
|
|
|
2045
2186
|
return now.toISOString().slice(0, 10);
|
|
2046
2187
|
}
|
|
2047
2188
|
}
|
|
2189
|
+
function isInside(root, target) {
|
|
2190
|
+
const rel = path8.relative(root, target);
|
|
2191
|
+
return rel !== "" && !rel.startsWith("..") && !path8.isAbsolute(rel);
|
|
2192
|
+
}
|
|
2193
|
+
function normalizeSha256(value) {
|
|
2194
|
+
if (typeof value !== "string") return void 0;
|
|
2195
|
+
const hex = value.trim().replace(/^sha256:/i, "").toLowerCase();
|
|
2196
|
+
return /^[0-9a-f]{64}$/.test(hex) ? hex : void 0;
|
|
2197
|
+
}
|
|
2048
2198
|
var Runner = class {
|
|
2049
2199
|
active;
|
|
2050
2200
|
deps;
|
|
@@ -2133,6 +2283,7 @@ var Runner = class {
|
|
|
2133
2283
|
...github.token === void 0 ? {} : { token: github.token },
|
|
2134
2284
|
date,
|
|
2135
2285
|
dryRun,
|
|
2286
|
+
trigger: options.trigger === "schedule" ? "schedule" : "manual",
|
|
2136
2287
|
...baseURLOverride === void 0 ? {} : { baseURLOverride },
|
|
2137
2288
|
signal: controller.signal,
|
|
2138
2289
|
secrets: [apiKey, github.token],
|
|
@@ -2150,6 +2301,14 @@ var Runner = class {
|
|
|
2150
2301
|
coreSource: core.source,
|
|
2151
2302
|
...core.tag === void 0 ? {} : { coreTag: core.tag }
|
|
2152
2303
|
});
|
|
2304
|
+
const preview = this.capturePreview({
|
|
2305
|
+
id,
|
|
2306
|
+
date,
|
|
2307
|
+
settings,
|
|
2308
|
+
outcome,
|
|
2309
|
+
secrets: [apiKey, github.token]
|
|
2310
|
+
});
|
|
2311
|
+
if (preview !== void 0) summary.preview = preview;
|
|
2153
2312
|
this.record(summary, settings);
|
|
2154
2313
|
return summary;
|
|
2155
2314
|
} catch (error) {
|
|
@@ -2177,10 +2336,12 @@ var Runner = class {
|
|
|
2177
2336
|
const { outcome } = input;
|
|
2178
2337
|
const error = outcome.error === void 0 ? void 0 : (() => {
|
|
2179
2338
|
const message = redact(outcome.error.message);
|
|
2339
|
+
const reason = reasonOf(outcome.error.code, message, outcome.error.details);
|
|
2180
2340
|
return {
|
|
2181
2341
|
code: outcome.error.code,
|
|
2182
2342
|
message,
|
|
2183
|
-
|
|
2343
|
+
...reason === void 0 ? {} : { reason },
|
|
2344
|
+
hint: hintOf(outcome.error.code, message, reason),
|
|
2184
2345
|
area: areaOf(outcome.error.code),
|
|
2185
2346
|
retriable: outcome.error.retriable,
|
|
2186
2347
|
inferred: outcome.error.details?.inferred === true
|
|
@@ -2197,7 +2358,9 @@ var Runner = class {
|
|
|
2197
2358
|
files: outcome.files.length,
|
|
2198
2359
|
...outcome.contentHash === void 0 ? {} : { contentHash: outcome.contentHash },
|
|
2199
2360
|
...outcome.branch === void 0 ? {} : { branch: outcome.branch },
|
|
2361
|
+
...outcome.commit === void 0 ? {} : { commit: outcome.commit },
|
|
2200
2362
|
...outcome.prUrl === void 0 ? {} : { prUrl: outcome.prUrl },
|
|
2363
|
+
...outcome.skipReason === void 0 ? {} : { skipReason: outcome.skipReason },
|
|
2201
2364
|
...error === void 0 ? {} : { error },
|
|
2202
2365
|
warnings: outcome.warnings,
|
|
2203
2366
|
logFile: input.logFile,
|
|
@@ -2224,6 +2387,68 @@ var Runner = class {
|
|
|
2224
2387
|
logFile: input.logFile
|
|
2225
2388
|
};
|
|
2226
2389
|
}
|
|
2390
|
+
/**
|
|
2391
|
+
* 从本次核心返回的 artifact 清单里选当天的 `.md`,只读复制到 previews/。
|
|
2392
|
+
*
|
|
2393
|
+
* 这是插件**唯一**读取用户仓库的场景,而且只读核心自己刚写出的那一份:
|
|
2394
|
+
* - 路径必须在 `repo.path` 内(词法 + realpath 各判一次,挡 `..`/绝对路径/符号链接);
|
|
2395
|
+
* - 上限 512 KB、拒绝 NUL、sha256 必须与契约声明一致;
|
|
2396
|
+
* - 任何一步失败都只记日志,返回 undefined;运行结果不受影响。
|
|
2397
|
+
*/
|
|
2398
|
+
capturePreview(input) {
|
|
2399
|
+
const { outcome, settings, date, id } = input;
|
|
2400
|
+
if (!outcome.ok || outcome.dryRun || outcome.skipped || date === void 0) return void 0;
|
|
2401
|
+
const skip = (reason, extra = {}) => {
|
|
2402
|
+
this.deps.logger?.("preview.skip", { id, reason, ...extra });
|
|
2403
|
+
return void 0;
|
|
2404
|
+
};
|
|
2405
|
+
const markdown = outcome.files.filter((file) => {
|
|
2406
|
+
const ext = path8.extname(file.path).toLowerCase();
|
|
2407
|
+
return ext === ".md" && path8.basename(file.path).toLowerCase() !== "readme.md";
|
|
2408
|
+
});
|
|
2409
|
+
if (markdown.length === 0) return skip("no-markdown");
|
|
2410
|
+
const dated = markdown.filter((file) => path8.basename(file.path) === `${date}.md`);
|
|
2411
|
+
const chosen = dated.length > 0 ? dated[0] : markdown.length === 1 ? markdown[0] : void 0;
|
|
2412
|
+
if (chosen === void 0) return skip("ambiguous", { candidates: markdown.length });
|
|
2413
|
+
const repoPath = settings.repo.path;
|
|
2414
|
+
const resolved = path8.resolve(repoPath, chosen.path);
|
|
2415
|
+
if (!isInside(repoPath, resolved)) return skip("outside-repo", { path: chosen.path });
|
|
2416
|
+
let fileReal;
|
|
2417
|
+
try {
|
|
2418
|
+
const repoReal = realpathSync(repoPath);
|
|
2419
|
+
fileReal = realpathSync(resolved);
|
|
2420
|
+
if (!isInside(repoReal, fileReal)) return skip("symlink-outside-repo", { path: chosen.path });
|
|
2421
|
+
} catch {
|
|
2422
|
+
return skip("missing-file", { path: chosen.path });
|
|
2423
|
+
}
|
|
2424
|
+
let buffer;
|
|
2425
|
+
try {
|
|
2426
|
+
const stat2 = statSync5(fileReal);
|
|
2427
|
+
if (!stat2.isFile()) return skip("not-a-file", { path: chosen.path });
|
|
2428
|
+
if (stat2.size > PREVIEW_MAX_BYTES) return skip("too-large", { bytes: stat2.size });
|
|
2429
|
+
buffer = readFileSync3(fileReal);
|
|
2430
|
+
} catch {
|
|
2431
|
+
return skip("read-failed", { path: chosen.path });
|
|
2432
|
+
}
|
|
2433
|
+
if (buffer.includes(0)) return skip("contains-nul", { path: chosen.path });
|
|
2434
|
+
const declared = normalizeSha256(chosen.sha256);
|
|
2435
|
+
const actual = createHash2("sha256").update(buffer).digest("hex");
|
|
2436
|
+
if (declared === void 0 || declared !== actual) {
|
|
2437
|
+
return skip("sha-mismatch", { path: chosen.path, declared: declared ?? null, actual });
|
|
2438
|
+
}
|
|
2439
|
+
const text = redact(buffer.toString("utf8"), input.secrets);
|
|
2440
|
+
const stored = Buffer.from(text, "utf8");
|
|
2441
|
+
try {
|
|
2442
|
+
this.deps.store.writePreview(id, text);
|
|
2443
|
+
} catch {
|
|
2444
|
+
return skip("write-failed", { path: chosen.path });
|
|
2445
|
+
}
|
|
2446
|
+
return {
|
|
2447
|
+
file: path8.relative(repoPath, resolved).split(path8.sep).join("/"),
|
|
2448
|
+
bytes: stored.length,
|
|
2449
|
+
sha256: createHash2("sha256").update(stored).digest("hex")
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2227
2452
|
/** 写历史与状态;同一天的 skipped 运行不覆盖 lastDigest。 */
|
|
2228
2453
|
record(summary, settings) {
|
|
2229
2454
|
const store = this.deps.store;
|
|
@@ -2233,12 +2458,20 @@ var Runner = class {
|
|
|
2233
2458
|
kind: summary.dryRun ? "dry-run" : "run",
|
|
2234
2459
|
ok: summary.ok,
|
|
2235
2460
|
skipped: summary.skipped,
|
|
2236
|
-
...summary.error === void 0 ? {} : {
|
|
2461
|
+
...summary.error === void 0 ? {} : {
|
|
2462
|
+
errorCode: summary.error.code,
|
|
2463
|
+
errorMessage: summary.error.message,
|
|
2464
|
+
...summary.error.reason === void 0 ? {} : { errorReason: summary.error.reason }
|
|
2465
|
+
},
|
|
2466
|
+
...summary.skipReason === void 0 ? {} : { skipReason: summary.skipReason },
|
|
2237
2467
|
durationMs: summary.durationMs,
|
|
2238
2468
|
files: summary.files,
|
|
2469
|
+
...summary.date === void 0 ? {} : { date: summary.date },
|
|
2239
2470
|
...summary.contentHash === void 0 ? {} : { contentHash: summary.contentHash },
|
|
2240
2471
|
...summary.branch === void 0 ? {} : { branch: summary.branch },
|
|
2472
|
+
...summary.commit === void 0 ? {} : { commit: summary.commit },
|
|
2241
2473
|
...summary.prUrl === void 0 ? {} : { prUrl: summary.prUrl },
|
|
2474
|
+
...summary.preview === void 0 ? {} : { preview: summary.preview },
|
|
2242
2475
|
coreVersion: summary.coreVersion,
|
|
2243
2476
|
model: { provider: settings.model.provider, model: settings.model.model },
|
|
2244
2477
|
languages: settings.trending.languages,
|
|
@@ -2248,11 +2481,13 @@ var Runner = class {
|
|
|
2248
2481
|
const state = store.readState();
|
|
2249
2482
|
const next = {
|
|
2250
2483
|
lastRunAt: record.at,
|
|
2484
|
+
...summary.ok ? { lastSuccessAt: record.at } : {},
|
|
2251
2485
|
...summary.ok && summary.prUrl !== void 0 ? { pendingPr: { url: summary.prUrl, createdAt: record.at, ...summary.date === void 0 ? {} : { date: summary.date } } } : {},
|
|
2252
2486
|
...summary.ok && !summary.skipped && summary.contentHash !== void 0 ? {
|
|
2253
2487
|
lastDigest: {
|
|
2254
2488
|
date: summary.date ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
2255
2489
|
contentHash: summary.contentHash,
|
|
2490
|
+
contentHashAlgo: CONTENT_HASH_ALGO_V2,
|
|
2256
2491
|
coreVersion: summary.coreVersion
|
|
2257
2492
|
},
|
|
2258
2493
|
...state.pendingPr !== void 0 && summary.prUrl === void 0 ? { pendingPr: state.pendingPr } : {}
|
|
@@ -2268,6 +2503,48 @@ var Runner = class {
|
|
|
2268
2503
|
}
|
|
2269
2504
|
};
|
|
2270
2505
|
|
|
2506
|
+
// src/host/plugin-info.ts
|
|
2507
|
+
var plugin_info_exports = {};
|
|
2508
|
+
__export(plugin_info_exports, {
|
|
2509
|
+
normalizeRepositoryUrl: () => normalizeRepositoryUrl,
|
|
2510
|
+
readPluginInfo: () => readPluginInfo
|
|
2511
|
+
});
|
|
2512
|
+
import { createRequire as createRequire2 } from "module";
|
|
2513
|
+
var DEFAULT_NAME = "@morehao/dsh-github-trending";
|
|
2514
|
+
function normalizeRepositoryUrl(value) {
|
|
2515
|
+
if (typeof value !== "string") return void 0;
|
|
2516
|
+
let url = value.trim();
|
|
2517
|
+
if (url === "") return void 0;
|
|
2518
|
+
url = url.replace(/^git\+/i, "");
|
|
2519
|
+
const ssh = /^git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/i.exec(url);
|
|
2520
|
+
if (ssh !== null) url = `https://github.com/${ssh[1]}/${ssh[2]}`;
|
|
2521
|
+
url = url.replace(/\.git$/i, "");
|
|
2522
|
+
try {
|
|
2523
|
+
const parsed = new URL(url);
|
|
2524
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return void 0;
|
|
2525
|
+
if (parsed.hostname.toLowerCase() !== "github.com") return void 0;
|
|
2526
|
+
const pathname = parsed.pathname.replace(/\/+$/, "");
|
|
2527
|
+
if (pathname === "") return void 0;
|
|
2528
|
+
return `https://github.com${pathname}`;
|
|
2529
|
+
} catch {
|
|
2530
|
+
return void 0;
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
function readPluginInfo() {
|
|
2534
|
+
try {
|
|
2535
|
+
const require2 = createRequire2(import.meta.url);
|
|
2536
|
+
const pkg = require2("../package.json");
|
|
2537
|
+
const repository = typeof pkg.repository === "string" ? normalizeRepositoryUrl(pkg.repository) : typeof pkg.repository === "object" && pkg.repository !== null ? normalizeRepositoryUrl(pkg.repository.url) : void 0;
|
|
2538
|
+
return {
|
|
2539
|
+
name: typeof pkg.name === "string" && pkg.name !== "" ? pkg.name : DEFAULT_NAME,
|
|
2540
|
+
version: typeof pkg.version === "string" ? pkg.version : "0.0.0",
|
|
2541
|
+
...repository === void 0 ? {} : { repository }
|
|
2542
|
+
};
|
|
2543
|
+
} catch {
|
|
2544
|
+
return { name: DEFAULT_NAME, version: "0.0.0" };
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2271
2548
|
// src/host/github.ts
|
|
2272
2549
|
var github_exports = {};
|
|
2273
2550
|
__export(github_exports, {
|
|
@@ -2335,7 +2612,7 @@ __export(import_cli_exports, {
|
|
|
2335
2612
|
importFromCli: () => importFromCli,
|
|
2336
2613
|
parseSimpleYaml: () => parseSimpleYaml
|
|
2337
2614
|
});
|
|
2338
|
-
import { readFileSync as
|
|
2615
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
2339
2616
|
import os3 from "os";
|
|
2340
2617
|
import path9 from "path";
|
|
2341
2618
|
function cliConfigCandidates(env = process.env, home = os3.homedir()) {
|
|
@@ -2427,7 +2704,7 @@ function isSecretKey(key) {
|
|
|
2427
2704
|
function importFromCli(filePath, base = DEFAULT_SETTINGS) {
|
|
2428
2705
|
let text;
|
|
2429
2706
|
try {
|
|
2430
|
-
text =
|
|
2707
|
+
text = readFileSync4(filePath, "utf8");
|
|
2431
2708
|
} catch (error) {
|
|
2432
2709
|
return {
|
|
2433
2710
|
ok: false,
|
|
@@ -2777,11 +3054,11 @@ var repo_exports = {};
|
|
|
2777
3054
|
__export(repo_exports, {
|
|
2778
3055
|
readDefaultBranch: () => readDefaultBranch
|
|
2779
3056
|
});
|
|
2780
|
-
import { statSync as
|
|
3057
|
+
import { statSync as statSync6 } from "fs";
|
|
2781
3058
|
import path10 from "path";
|
|
2782
3059
|
function isDirectory(dir) {
|
|
2783
3060
|
try {
|
|
2784
|
-
return
|
|
3061
|
+
return statSync6(dir).isDirectory();
|
|
2785
3062
|
} catch {
|
|
2786
3063
|
return false;
|
|
2787
3064
|
}
|
|
@@ -2869,7 +3146,7 @@ function createRoutes(deps) {
|
|
|
2869
3146
|
const code = error instanceof IpcError ? error.code : "INTERNAL";
|
|
2870
3147
|
const message = redact(error instanceof Error ? error.message : String(error));
|
|
2871
3148
|
deps.logger?.("route.error", { pathname, method, code, message });
|
|
2872
|
-
const status = code === "CONFIG_INVALID" ? 400 : 500;
|
|
3149
|
+
const status = code === "CONFIG_INVALID" ? 400 : code === "NOT_FOUND" ? 404 : 500;
|
|
2873
3150
|
sendJson2(response, status, { ok: false, code, message });
|
|
2874
3151
|
}
|
|
2875
3152
|
};
|
|
@@ -2986,6 +3263,19 @@ async function dispatch(method, pathname, url, request, deps) {
|
|
|
2986
3263
|
if (result.ok && result.settings !== void 0) deps.store.writeSettings(result.settings);
|
|
2987
3264
|
return { import: { ...result, settings: result.settings === void 0 ? void 0 : result.settings } };
|
|
2988
3265
|
}
|
|
3266
|
+
if (method === "GET" && pathname === "/preview") {
|
|
3267
|
+
const id = url.searchParams.get("id") ?? "";
|
|
3268
|
+
if (!/^[A-Za-z0-9-]+$/.test(id)) throw new IpcError("CONFIG_INVALID", "\u975E\u6CD5\u7684\u8FD0\u884C id");
|
|
3269
|
+
const state = deps.store.readState();
|
|
3270
|
+
const history = deps.store.readHistory();
|
|
3271
|
+
const record = history.find((item) => item.id === id) ?? (state.lastResult?.id === id ? state.lastResult : void 0);
|
|
3272
|
+
if (record === void 0 || record.preview === void 0) {
|
|
3273
|
+
throw new IpcError("NOT_FOUND", "\u6CA1\u6709\u8FD9\u6B21\u8FD0\u884C\u7684\u9884\u89C8\u526F\u672C");
|
|
3274
|
+
}
|
|
3275
|
+
const preview = deps.store.readPreview(id);
|
|
3276
|
+
if (preview === void 0) throw new IpcError("NOT_FOUND", "\u9884\u89C8\u526F\u672C\u4E0D\u5B58\u5728\u6216\u5DF2\u88AB\u6E05\u7406");
|
|
3277
|
+
return { id, preview };
|
|
3278
|
+
}
|
|
2989
3279
|
if (method === "GET" && pathname === "/log") {
|
|
2990
3280
|
const id = url.searchParams.get("id") ?? "";
|
|
2991
3281
|
if (!/^[A-Za-z0-9-]+$/.test(id)) throw new IpcError("CONFIG_INVALID", "\u975E\u6CD5\u7684\u8FD0\u884C id");
|
|
@@ -2999,7 +3289,7 @@ async function statePayload(deps, settings) {
|
|
|
2999
3289
|
const env = deps.env ?? process.env;
|
|
3000
3290
|
const credentials = credentialServices(deps.get);
|
|
3001
3291
|
const state = deps.store.readState();
|
|
3002
|
-
const history = deps.store.readHistory()
|
|
3292
|
+
const history = deps.store.readHistory();
|
|
3003
3293
|
const core = await inspectCore(settings, { env });
|
|
3004
3294
|
const coexistence = await detectCoexistence(core.path, { env });
|
|
3005
3295
|
const modelCredential = await describeRef(credentials, settings.model.apiKeyRef, env);
|
|
@@ -3021,7 +3311,11 @@ async function statePayload(deps, settings) {
|
|
|
3021
3311
|
}
|
|
3022
3312
|
}
|
|
3023
3313
|
return {
|
|
3024
|
-
plugin: {
|
|
3314
|
+
plugin: {
|
|
3315
|
+
name: deps.pluginName ?? "@morehao/dsh-github-trending",
|
|
3316
|
+
version: deps.version,
|
|
3317
|
+
...deps.repository === void 0 ? {} : { repository: deps.repository }
|
|
3318
|
+
},
|
|
3025
3319
|
settings,
|
|
3026
3320
|
core,
|
|
3027
3321
|
coexistence,
|
|
@@ -3236,6 +3530,8 @@ export {
|
|
|
3236
3530
|
schedule_exports,
|
|
3237
3531
|
Runner,
|
|
3238
3532
|
runner_exports,
|
|
3533
|
+
readPluginInfo,
|
|
3534
|
+
plugin_info_exports,
|
|
3239
3535
|
github_exports,
|
|
3240
3536
|
import_cli_exports,
|
|
3241
3537
|
pending_pr_exports,
|