@morehao/dsh-github-trending 0.7.8 → 0.7.9

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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.7.9] - 2026-11-01
4
+
5
+ > 内部重构:把重复的通用实现收敛到共享底座 `@morehao/dsh-plugin-kit@^0.3.1`。对外接口与业务语义保持不变。
6
+
7
+ ### 改进 — 迁移到 dsh-plugin-kit 0.3.1
8
+
9
+ - **子进程执行**:`src/core/exec.ts` 收敛到 kit `./exec` 的 `spawnCollect` / `sanitizedEnv`,删除本地实现;握手阶段改为判 `result.error` 抛 `CORE_SPAWN_FAILED`(kit 的 `spawnCollect` 永不 reject)。
10
+ - **插件身份**:`plugin-info.ts` 的仓库地址规范化与读包改用 kit `./github` + `./meta`(`createPluginMeta`),保留 `readPluginInfo` / `normalizeRepositoryUrl` 导出面与返回形状。
11
+ - **HTTP 路由**:`routes.ts` 的 `sendJson` / `readBody` / 头校验改用 kit `./http` 的 `createRouteHandler`;业务 `dispatch`、`IpcError` 错误码与 `PluginUpdateError→409` 映射保持不变,错误码翻译仍留在插件侧。
12
+ - 对外路由响应形状、读包字段、错误码/映射均无变化(行为等价)。
13
+
3
14
  ## [v0.7.8] - 2026-11-01
4
15
 
5
16
  > 统一「订阅」口径:这个功能不再叫「发版」,运行记录、概览、设置、订阅页与提示文案一致;顺带修好运行记录里的类型胶囊样式。
@@ -1,8 +1,8 @@
1
- import {
2
- __export,
3
- execCapture,
4
- sanitizedEnv
5
- } from "./chunk-R7BK7SJK.js";
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
6
 
7
7
  // src/config.ts
8
8
  var config_exports = {};
@@ -1148,16 +1148,15 @@ __export(handshake_exports, {
1148
1148
  DEFAULT_PROVIDERS: () => DEFAULT_PROVIDERS,
1149
1149
  handshake: () => handshake
1150
1150
  });
1151
+ import { spawnCollect as execCapture } from "@morehao/dsh-plugin-kit/exec";
1151
1152
  var DEFAULT_PROVIDERS = ["deepseek", "openai-compatible"];
1152
1153
  async function handshake(core, options = {}) {
1153
- let result;
1154
- try {
1155
- result = await execCapture(core.path, ["version", "--json"], {
1156
- timeoutMs: options.timeoutMs ?? 5e3,
1157
- signal: options.signal
1158
- });
1159
- } catch (error) {
1160
- const detail = error instanceof Error ? error.message : String(error);
1154
+ const result = await execCapture(core.path, ["version", "--json"], {
1155
+ timeoutMs: options.timeoutMs ?? 5e3,
1156
+ signal: options.signal
1157
+ });
1158
+ if (result.error !== void 0) {
1159
+ const detail = result.error.message;
1161
1160
  throw new IpcError("CORE_SPAWN_FAILED", `\u65E0\u6CD5\u6267\u884C\u6838\u5FC3 ${core.path}\uFF1A${detail}`);
1162
1161
  }
1163
1162
  if (result.aborted || options.signal?.aborted === true) {
@@ -1590,6 +1589,7 @@ __export(run_exports, {
1590
1589
  runCore: () => runCore
1591
1590
  });
1592
1591
  import { spawn as spawn2 } from "child_process";
1592
+ import { sanitizedEnv } from "@morehao/dsh-plugin-kit/exec";
1593
1593
  var DEFAULT_RUN_TIMEOUT_MS = 12e4;
1594
1594
  var MAX_STDOUT = 16 * 1024 * 1024;
1595
1595
  var MAX_STDERR = 2 * 1024 * 1024;
@@ -1772,6 +1772,7 @@ __export(core_exports, {
1772
1772
  });
1773
1773
  import { accessSync as accessSync2, constants as constants3, statSync as statSync4 } from "fs";
1774
1774
  import path7 from "path";
1775
+ import { spawnCollect as execCapture2 } from "@morehao/dsh-plugin-kit/exec";
1775
1776
  function coreRepoFromSource(repo) {
1776
1777
  let ownerRepo = repo;
1777
1778
  if (ownerRepo === void 0) {
@@ -1876,7 +1877,7 @@ async function detectCoexistence(ownPath, options = {}) {
1876
1877
  }
1877
1878
  let status;
1878
1879
  try {
1879
- const result = await execCapture(found, ["version", "--json"], { timeoutMs: 3e3 });
1880
+ const result = await execCapture2(found, ["version", "--json"], { timeoutMs: 3e3 });
1880
1881
  if (result.code !== 0) {
1881
1882
  status = {
1882
1883
  found: true,
@@ -1970,8 +1971,8 @@ async function defaultGhToken(fresh = false) {
1970
1971
  if (!fresh && ghTokenCache !== void 0 && Date.now() - ghTokenCache.at < GH_TOKEN_TTL_MS) {
1971
1972
  return ghTokenCache.value;
1972
1973
  }
1973
- const { execCapture: execCapture2 } = await import("./exec-VMDINNX5.js");
1974
- const result = await execCapture2("gh", ["auth", "token"], { timeoutMs: 2e3 });
1974
+ const { spawnCollect: execCapture6 } = await import("@morehao/dsh-plugin-kit/exec");
1975
+ const result = await execCapture6("gh", ["auth", "token"], { timeoutMs: 2e3 });
1975
1976
  const token = result.code === 0 ? result.stdout.trim() : "";
1976
1977
  const value = token === "" ? void 0 : token;
1977
1978
  ghTokenCache = { value, at: Date.now() };
@@ -2660,6 +2661,7 @@ __export(subscribe_exports, {
2660
2661
  import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync2, rmSync as rmSync2 } from "fs";
2661
2662
  import os3 from "os";
2662
2663
  import path8 from "path";
2664
+ import { spawnCollect as execCapture3, sanitizedEnv as sanitizedEnv2 } from "@morehao/dsh-plugin-kit/exec";
2663
2665
  var SUBSCRIBE_TIMEOUT_MS = 8e3;
2664
2666
  var RELEASES_TIMEOUT_MS = 3e4;
2665
2667
  var REPO_PATTERN2 = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
@@ -2709,8 +2711,8 @@ async function runSubscribe(input, deps) {
2709
2711
  configPath: file
2710
2712
  };
2711
2713
  const timeoutMs = input.action === "releases" ? RELEASES_TIMEOUT_MS : SUBSCRIBE_TIMEOUT_MS;
2712
- const result = await execCapture(corePath, ["subscribe", "--json"], {
2713
- env: sanitizedEnv(env),
2714
+ const result = await execCapture3(corePath, ["subscribe", "--json"], {
2715
+ env: sanitizedEnv2(env),
2714
2716
  input: JSON.stringify(request),
2715
2717
  timeoutMs
2716
2718
  });
@@ -3411,40 +3413,17 @@ __export(plugin_info_exports, {
3411
3413
  normalizeRepositoryUrl: () => normalizeRepositoryUrl,
3412
3414
  readPluginInfo: () => readPluginInfo
3413
3415
  });
3414
- import { createRequire as createRequire2 } from "module";
3416
+ import { normalizeRepositoryUrl } from "@morehao/dsh-plugin-kit/github";
3417
+ import { createPluginMeta } from "@morehao/dsh-plugin-kit/meta";
3415
3418
  var DEFAULT_NAME = "@morehao/dsh-github-trending";
3416
- function normalizeRepositoryUrl(value) {
3417
- if (typeof value !== "string") return void 0;
3418
- let url = value.trim();
3419
- if (url === "") return void 0;
3420
- url = url.replace(/^git\+/i, "");
3421
- const ssh = /^git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/i.exec(url);
3422
- if (ssh !== null) url = `https://github.com/${ssh[1]}/${ssh[2]}`;
3423
- url = url.replace(/\.git$/i, "");
3424
- try {
3425
- const parsed = new URL(url);
3426
- if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return void 0;
3427
- if (parsed.hostname.toLowerCase() !== "github.com") return void 0;
3428
- const pathname = parsed.pathname.replace(/\/+$/, "");
3429
- if (pathname === "") return void 0;
3430
- return `https://github.com${pathname}`;
3431
- } catch {
3432
- return void 0;
3433
- }
3434
- }
3419
+ var PKG_URL = new URL("../package.json", import.meta.url);
3435
3420
  function readPluginInfo() {
3436
- try {
3437
- const require2 = createRequire2(import.meta.url);
3438
- const pkg = require2("../package.json");
3439
- const repository = typeof pkg.repository === "string" ? normalizeRepositoryUrl(pkg.repository) : typeof pkg.repository === "object" && pkg.repository !== null ? normalizeRepositoryUrl(pkg.repository.url) : void 0;
3440
- return {
3441
- name: typeof pkg.name === "string" && pkg.name !== "" ? pkg.name : DEFAULT_NAME,
3442
- version: typeof pkg.version === "string" ? pkg.version : "0.0.0",
3443
- ...repository === void 0 ? {} : { repository }
3444
- };
3445
- } catch {
3446
- return { name: DEFAULT_NAME, version: "0.0.0" };
3447
- }
3421
+ const meta = createPluginMeta({ pkgUrl: PKG_URL, defaultName: DEFAULT_NAME });
3422
+ return {
3423
+ name: meta.name,
3424
+ version: meta.version,
3425
+ ...meta.repository === void 0 ? {} : { repository: meta.repository }
3426
+ };
3448
3427
  }
3449
3428
 
3450
3429
  // src/host/github.ts
@@ -3958,6 +3937,7 @@ __export(repo_exports, {
3958
3937
  });
3959
3938
  import { statSync as statSync6 } from "fs";
3960
3939
  import path11 from "path";
3940
+ import { spawnCollect as execCapture4 } from "@morehao/dsh-plugin-kit/exec";
3961
3941
  function isDirectory(dir) {
3962
3942
  try {
3963
3943
  return statSync6(dir).isDirectory();
@@ -3967,7 +3947,7 @@ function isDirectory(dir) {
3967
3947
  }
3968
3948
  async function git(dir, args, env) {
3969
3949
  try {
3970
- const result = await execCapture("git", ["-C", dir, ...args], { env, timeoutMs: 5e3 });
3950
+ const result = await execCapture4("git", ["-C", dir, ...args], { env, timeoutMs: 5e3 });
3971
3951
  return result.code === 0 ? result.stdout.trim() : void 0;
3972
3952
  } catch {
3973
3953
  return void 0;
@@ -3998,77 +3978,53 @@ __export(routes_exports, {
3998
3978
  ROUTE_PREFIX: () => ROUTE_PREFIX,
3999
3979
  createRoutes: () => createRoutes
4000
3980
  });
3981
+ import { createRouteHandler, HttpError } from "@morehao/dsh-plugin-kit/http";
4001
3982
  import { PluginUpdateError } from "@morehao/dsh-plugin-kit/update";
4002
3983
  var ROUTE_PREFIX = "/api/morehao/github-trending";
4003
3984
  var HEADER = "x-dsh-github-trending";
4004
3985
  var MAX_BODY2 = 512 * 1024;
4005
- function sendJson2(response, status, payload) {
4006
- const body = JSON.stringify(payload);
4007
- response.writeHead(status, {
4008
- "content-type": "application/json; charset=utf-8",
4009
- "cache-control": "no-store",
4010
- "content-length": Buffer.byteLength(body)
4011
- });
4012
- response.end(body);
4013
- }
4014
- async function readBody2(request) {
4015
- const chunks = [];
4016
- let size = 0;
4017
- for await (const chunk of request) {
4018
- size += chunk.length;
4019
- if (size > MAX_BODY2) throw new IpcError("PAYLOAD_TOO_LARGE", "\u8BF7\u6C42\u4F53\u8FC7\u5927");
4020
- chunks.push(chunk);
4021
- }
4022
- if (chunks.length === 0) return {};
4023
- const text = Buffer.concat(chunks).toString("utf8").trim();
4024
- if (text === "") return {};
4025
- try {
4026
- const value = JSON.parse(text);
4027
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
4028
- throw new Error("body must be a JSON object");
4029
- }
4030
- return value;
4031
- } catch (error) {
4032
- throw new IpcError("CONFIG_INVALID", `\u8BF7\u6C42\u4F53\u4E0D\u662F JSON \u5BF9\u8C61\uFF1A${error instanceof Error ? error.message : String(error)}`);
4033
- }
4034
- }
4035
3986
  function createRoutes(deps) {
4036
- return async function handler(request, response) {
4037
- if (request.headers[HEADER] !== "1") {
4038
- sendJson2(response, 403, { ok: false, code: "FORBIDDEN", message: `\u7F3A\u5C11 ${HEADER}: 1 \u5934` });
4039
- return;
4040
- }
4041
- const url = new URL(request.url ?? "/", "http://127.0.0.1");
4042
- const pathname = url.pathname.startsWith(ROUTE_PREFIX) ? url.pathname.slice(ROUTE_PREFIX.length) || "/" : url.pathname;
4043
- const method = (request.method ?? "GET").toUpperCase();
4044
- try {
4045
- const result = await dispatch(method, pathname, url, request, deps);
4046
- if (!response.writableEnded) sendJson2(response, 200, { ok: true, ...result });
4047
- } catch (error) {
4048
- if (response.writableEnded) return;
3987
+ const logger = deps.logger;
3988
+ let current = { method: "GET", pathname: "/" };
3989
+ const handler = createRouteHandler({
3990
+ prefix: ROUTE_PREFIX,
3991
+ header: HEADER,
3992
+ maxBodyBytes: MAX_BODY2,
3993
+ dispatch: async ({ method, pathname, url, body }) => {
3994
+ current = { method, pathname };
3995
+ return dispatch(method, pathname, url, body, deps);
3996
+ },
3997
+ // 错误码翻译留在插件侧(dispatcher 不做映射):保持与原实现的响应形状逐字一致。
3998
+ onError: (error) => {
4049
3999
  if (error instanceof PluginUpdateError) {
4050
- deps.logger?.("update.failed", { code: error.code, message: error.message });
4051
- sendJson2(response, 409, {
4052
- ok: false,
4000
+ logger?.("update.failed", { code: error.code, message: error.message });
4001
+ return {
4002
+ status: 409,
4053
4003
  code: "UPDATE_FAILED",
4054
- updateCode: error.code,
4055
4004
  message: redact(error.message),
4056
- update: deps.update?.view() ?? null
4057
- });
4058
- return;
4005
+ extra: { updateCode: error.code, update: deps.update?.view() ?? null }
4006
+ };
4007
+ }
4008
+ if (error instanceof HttpError) {
4009
+ const code2 = error.code === "PAYLOAD_TOO_LARGE" ? "PAYLOAD_TOO_LARGE" : "CONFIG_INVALID";
4010
+ return { status: 400, code: code2, message: redact(error.message) };
4059
4011
  }
4060
4012
  const code = error instanceof IpcError ? error.code : "INTERNAL";
4061
4013
  const message = redact(error instanceof Error ? error.message : String(error));
4062
- deps.logger?.("route.error", { pathname, method, code, message });
4014
+ logger?.("route.error", { pathname: current.pathname, method: current.method, code, message });
4063
4015
  const status = code === "CONFIG_INVALID" ? (
4064
4016
  // 「已有一次运行在进行中」是并发冲突而非参数错误:用 409,前端据此禁用按钮并提示。
4065
4017
  message.includes("\u8FD0\u884C\u5728\u8FDB\u884C\u4E2D") ? 409 : 400
4066
4018
  ) : code === "NOT_FOUND" ? 404 : 500;
4067
- sendJson2(response, status, { ok: false, code, message });
4068
- }
4019
+ return { status, code, message };
4020
+ },
4021
+ logger
4022
+ });
4023
+ return async function routeHandler(request, response) {
4024
+ await handler(request, response);
4069
4025
  };
4070
4026
  }
4071
- async function dispatch(method, pathname, url, request, deps) {
4027
+ async function dispatch(method, pathname, url, body, deps) {
4072
4028
  const env = deps.env ?? process.env;
4073
4029
  const settings = deps.store.readSettings();
4074
4030
  if (method === "GET" && pathname === "/state") return statePayload(deps, settings);
@@ -4079,7 +4035,6 @@ async function dispatch(method, pathname, url, request, deps) {
4079
4035
  }
4080
4036
  if (method === "POST" && pathname === "/update") {
4081
4037
  if (deps.update === void 0) throw new IpcError("NOT_FOUND", "\u81EA\u66F4\u65B0\u670D\u52A1\u4E0D\u53EF\u7528");
4082
- const body = await readBody2(request);
4083
4038
  const version = typeof body.version === "string" && body.version.trim() !== "" ? body.version.trim() : void 0;
4084
4039
  const result = await deps.update.apply(version === void 0 ? {} : { version });
4085
4040
  return { result, update: deps.update.view() };
@@ -4095,7 +4050,6 @@ async function dispatch(method, pathname, url, request, deps) {
4095
4050
  return { downloaded: true, path: core.path, tag: core.tag ?? null, core: status };
4096
4051
  }
4097
4052
  if (method === "POST" && pathname === "/config") {
4098
- const body = await readBody2(request);
4099
4053
  const incoming = normalizeSettings(body.settings ?? body);
4100
4054
  const section = typeof body.section === "string" && SETTINGS_SECTIONS.includes(body.section) ? body.section : void 0;
4101
4055
  let next = section === void 0 ? incoming : mergeSettingsSection(settings, incoming, section);
@@ -4114,7 +4068,6 @@ async function dispatch(method, pathname, url, request, deps) {
4114
4068
  return { saved: true, settings: next, problems: [] };
4115
4069
  }
4116
4070
  if (method === "POST" && pathname === "/preflight") {
4117
- const body = await readBody2(request);
4118
4071
  const candidate = body.settings === void 0 ? settings : normalizeSettings(body.settings);
4119
4072
  const result = await preflightModel({
4120
4073
  settings: candidate,
@@ -4125,7 +4078,6 @@ async function dispatch(method, pathname, url, request, deps) {
4125
4078
  return { preflight: result };
4126
4079
  }
4127
4080
  if (method === "POST" && pathname === "/run") {
4128
- const body = await readBody2(request);
4129
4081
  const base = {
4130
4082
  dryRun: body.dryRun === true,
4131
4083
  ...typeof body.date === "string" && body.date !== "" ? { date: body.date } : {},
@@ -4143,7 +4095,6 @@ async function dispatch(method, pathname, url, request, deps) {
4143
4095
  return { cancelled: deps.runner.cancel() };
4144
4096
  }
4145
4097
  if (method === "POST" && pathname === "/releases/run") {
4146
- const body = await readBody2(request);
4147
4098
  let repo;
4148
4099
  if (body.repo !== void 0) {
4149
4100
  if (typeof body.repo !== "string" || !isValidRepo(body.repo.trim())) {
@@ -4191,7 +4142,6 @@ async function dispatch(method, pathname, url, request, deps) {
4191
4142
  return { ...result.doc };
4192
4143
  }
4193
4144
  if (method === "POST" && pathname === "/subscriptions") {
4194
- const body = await readBody2(request);
4195
4145
  const action = body.action === "add" || body.action === "remove" ? body.action : void 0;
4196
4146
  if (action === void 0) throw new IpcError("CONFIG_INVALID", "action \u5FC5\u987B\u662F add \u6216 remove");
4197
4147
  const repos = Array.isArray(body.repos) ? body.repos.filter((item) => typeof item === "string" && item.trim() !== "").map((item) => item.trim()) : typeof body.repo === "string" && body.repo.trim() !== "" ? [body.repo.trim()] : [];
@@ -4206,7 +4156,6 @@ async function dispatch(method, pathname, url, request, deps) {
4206
4156
  return { repos: latest.ok ? latest.repos : [], items: latest.ok ? latest.items ?? [] : [] };
4207
4157
  }
4208
4158
  if (method === "POST" && pathname === "/github/test") {
4209
- const body = await readBody2(request);
4210
4159
  const candidate = body.settings === void 0 ? settings : normalizeSettings(body.settings);
4211
4160
  const probe = await probeGithubAuth({
4212
4161
  credentials: credentialServices(deps.get),
@@ -4225,7 +4174,6 @@ async function dispatch(method, pathname, url, request, deps) {
4225
4174
  return { providers: candidates, defaultSelection: readDefaultSelection(deps.get) ?? null };
4226
4175
  }
4227
4176
  if (method === "POST" && pathname === "/quickfill") {
4228
- const body = await readBody2(request);
4229
4177
  const candidates = await listProviderCandidates({
4230
4178
  llm: llmServices(deps.get),
4231
4179
  credentials: credentialServices(deps.get),
@@ -4357,6 +4305,7 @@ __export(update_exports, {
4357
4305
  });
4358
4306
  import { normalizeProfile } from "@morehao/dsh-plugin-kit";
4359
4307
  import { createPluginUpdater } from "@morehao/dsh-plugin-kit/update";
4308
+ import { spawnCollect as execCapture5, sanitizedEnv as sanitizedEnv3 } from "@morehao/dsh-plugin-kit/exec";
4360
4309
  function pnpmViewRunner(get, env, exec) {
4361
4310
  return async (spec) => {
4362
4311
  const profile = normalizeProfile(get("profileContext"));
@@ -4367,7 +4316,7 @@ function pnpmViewRunner(get, env, exec) {
4367
4316
  try {
4368
4317
  result = await exec(command, [...prefix, "view", spec, "version", "--json"], {
4369
4318
  cwd: profile.dir,
4370
- env: sanitizedEnv(env),
4319
+ env: sanitizedEnv3(env),
4371
4320
  timeoutMs: 8e3,
4372
4321
  maxStdout: 64 * 1024,
4373
4322
  maxStderr: 16 * 1024
@@ -4389,7 +4338,7 @@ function pnpmViewRunner(get, env, exec) {
4389
4338
  };
4390
4339
  }
4391
4340
  function createUpdateService(deps) {
4392
- const exec = deps.exec ?? execCapture;
4341
+ const exec = deps.exec ?? execCapture5;
4393
4342
  return createPluginUpdater({
4394
4343
  meta: deps.meta,
4395
4344
  getPluginManager: () => deps.get("pluginManager"),
package/lib/index.js CHANGED
@@ -8,8 +8,7 @@ import {
8
8
  createTrendingRunTool,
9
9
  createUpdateService,
10
10
  readPluginInfo
11
- } from "./chunk-HYVMRVGP.js";
12
- import "./chunk-R7BK7SJK.js";
11
+ } from "./chunk-YJ5GF3MW.js";
13
12
 
14
13
  // src/index.ts
15
14
  var name = "dsh-github-trending";
package/lib/internal.d.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  import { ErrorCode, FileArtifact, Usage, ReleasesReport, SubscriptionItem, SubscriptionDoc, SubscriptionStatus, ErrorInfo, RunRequest, RunResult, State, StateRun, SubscriptionList, SubscriptionVolume } from '@morehao/trendradar-contract';
2
+ import { ExecOptions, ExecResult } from '@morehao/dsh-plugin-kit/exec';
3
+ import * as exec from '@morehao/dsh-plugin-kit/exec';
4
+ export { exec };
5
+ import { normalizeRepositoryUrl } from '@morehao/dsh-plugin-kit/github';
2
6
  import http from 'node:http';
3
7
  import { Updater, PluginMeta, Logger } from '@morehao/dsh-plugin-kit/update';
4
8
 
@@ -930,40 +934,6 @@ declare namespace channel {
930
934
  export { type channel_ChannelKind as ChannelKind, type channel_ConfigChannel as ConfigChannel, channel_WRITE_TIMEOUT_MS as WRITE_TIMEOUT_MS, channel_openConfigChannel as openConfigChannel, channel_removeChannelDir as removeChannelDir };
931
935
  }
932
936
 
933
- interface ExecResult {
934
- code: number | null;
935
- signal: NodeJS.Signals | null;
936
- stdout: string;
937
- stderr: string;
938
- timedOut: boolean;
939
- aborted: boolean;
940
- durationMs: number;
941
- }
942
- declare function sanitizedEnv(source?: NodeJS.ProcessEnv, extra?: Record<string, string | undefined>): Record<string, string>;
943
- interface ExecOptions {
944
- env?: NodeJS.ProcessEnv;
945
- cwd?: string;
946
- timeoutMs?: number;
947
- signal?: AbortSignal;
948
- maxStdout?: number;
949
- maxStderr?: number;
950
- /** 写入子进程 stdin 的内容(写完即关闭);省略则 stdin 为 ignore。 */
951
- input?: string;
952
- }
953
- /**
954
- * 执行一次短命令并收集输出(握手、共存探测都走这里)。
955
- * 超时与取消都先 SIGTERM、3 s 后 SIGKILL——核心是单进程,不需要 process group。
956
- */
957
- declare function execCapture(file: string, args: string[], options?: ExecOptions): Promise<ExecResult>;
958
-
959
- type exec_ExecOptions = ExecOptions;
960
- type exec_ExecResult = ExecResult;
961
- declare const exec_execCapture: typeof execCapture;
962
- declare const exec_sanitizedEnv: typeof sanitizedEnv;
963
- declare namespace exec {
964
- export { type exec_ExecOptions as ExecOptions, type exec_ExecResult as ExecResult, exec_execCapture as execCapture, exec_sanitizedEnv as sanitizedEnv };
965
- }
966
-
967
937
  declare const DEFAULT_RUN_TIMEOUT_MS = 120000;
968
938
  interface RunCoreOptions {
969
939
  core: CoreLocation;
@@ -1654,13 +1624,24 @@ declare namespace subscribe {
1654
1624
  export { subscribe_RELEASES_TIMEOUT_MS as RELEASES_TIMEOUT_MS, subscribe_SUBSCRIBE_TIMEOUT_MS as SUBSCRIBE_TIMEOUT_MS, type subscribe_SubscribeDeps as SubscribeDeps, subscribe_isValidRepo as isValidRepo, subscribe_runSubscribe as runSubscribe };
1655
1625
  }
1656
1626
 
1627
+ /**
1628
+ * 插件自身元数据:包名、版本、仓库地址。
1629
+ *
1630
+ * `/state.plugin` 是界面上「关于」的唯一来源:版本不能硬编码在客户端,仓库地址
1631
+ * 也不能从客户端猜。`package.json.repository.url` 常见形态是
1632
+ * `git+https://github.com/o/r.git` 或 `git@github.com:o/r.git`,统一规范化成
1633
+ * `https://github.com/o/r`;解析不了就返回 undefined,界面隐藏外链而不是猜。
1634
+ *
1635
+ * 读包与规范化仓库地址都交给 `@morehao/dsh-plugin-kit` 的 `./meta` + `./github`;
1636
+ * 这里保留 `readPluginInfo()` / `normalizeRepositoryUrl()` 两个对内部(测试与 index)
1637
+ * 的既有导出面,形状不变。
1638
+ */
1639
+
1657
1640
  interface PluginInfo {
1658
1641
  name: string;
1659
1642
  version: string;
1660
1643
  repository?: string;
1661
1644
  }
1662
- /** 只放行 GitHub 的 http(s) 地址;ssh / git+ 前缀先归一化。 */
1663
- declare function normalizeRepositoryUrl(value: unknown): string | undefined;
1664
1645
  declare function readPluginInfo(): PluginInfo;
1665
1646
 
1666
1647
  type pluginInfo_PluginInfo = PluginInfo;
@@ -2164,4 +2145,4 @@ declare namespace tools {
2164
2145
  export { tools_createTrendingHistoryTool as createTrendingHistoryTool, tools_createTrendingRunTool as createTrendingRunTool };
2165
2146
  }
2166
2147
 
2167
- export { channel, config, core, credentials, errors, exec, github, 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 };
2148
+ export { channel, config, core, credentials, errors, github, 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 };
package/lib/internal.js CHANGED
@@ -26,17 +26,17 @@ import {
26
26
  tools_exports,
27
27
  update_exports,
28
28
  version_exports
29
- } from "./chunk-HYVMRVGP.js";
30
- import {
31
- exec_exports
32
- } from "./chunk-R7BK7SJK.js";
29
+ } from "./chunk-YJ5GF3MW.js";
30
+
31
+ // src/internal.ts
32
+ import * as exec from "@morehao/dsh-plugin-kit/exec";
33
33
  export {
34
34
  channel_exports as channel,
35
35
  config_exports as config,
36
36
  core_exports as core,
37
37
  credentials_exports as credentials,
38
38
  errors_exports as errors,
39
- exec_exports as exec,
39
+ exec,
40
40
  github_exports as github,
41
41
  handshake_exports as handshake,
42
42
  import_cli_exports as importCli,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morehao/dsh-github-trending",
3
- "version": "0.7.8",
3
+ "version": "0.7.9",
4
4
  "description": "GitHub 趋势日报插件:每天定时 spawn 本地 trendradar 核心,抓取 Trending、生成中文简介与总结,提交到你的仓库并自动合并 PR。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -50,7 +50,7 @@
50
50
  }
51
51
  },
52
52
  "dependencies": {
53
- "@morehao/dsh-plugin-kit": "0.2.1",
53
+ "@morehao/dsh-plugin-kit": "^0.3.1",
54
54
  "@morehao/trendradar-contract": "0.5.0"
55
55
  },
56
56
  "dsh": {
@@ -1,121 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __export = (target, all) => {
3
- for (var name in all)
4
- __defProp(target, name, { get: all[name], enumerable: true });
5
- };
6
-
7
- // src/core/exec.ts
8
- var exec_exports = {};
9
- __export(exec_exports, {
10
- execCapture: () => execCapture,
11
- sanitizedEnv: () => sanitizedEnv
12
- });
13
- import { spawn } from "child_process";
14
- var ENV_KEYS = [
15
- "PATH",
16
- "HOME",
17
- "USER",
18
- "LOGNAME",
19
- "SHELL",
20
- "TMPDIR",
21
- "TMP",
22
- "TEMP",
23
- "LANG",
24
- "LC_ALL",
25
- "LC_CTYPE",
26
- "SSL_CERT_FILE",
27
- "SSL_CERT_DIR",
28
- "GIT_AUTHOR_NAME",
29
- "GIT_AUTHOR_EMAIL",
30
- "GIT_COMMITTER_NAME",
31
- "GIT_COMMITTER_EMAIL",
32
- "GIT_CONFIG_GLOBAL",
33
- "GIT_SSH_COMMAND"
34
- ];
35
- var PROXY_KEYS = [
36
- "HTTP_PROXY",
37
- "HTTPS_PROXY",
38
- "NO_PROXY",
39
- "ALL_PROXY",
40
- "http_proxy",
41
- "https_proxy",
42
- "no_proxy",
43
- "all_proxy"
44
- ];
45
- function sanitizedEnv(source = process.env, extra = {}) {
46
- const env = {};
47
- for (const key of [...ENV_KEYS, ...PROXY_KEYS]) {
48
- const value = source[key];
49
- if (typeof value === "string" && value !== "") env[key] = value;
50
- }
51
- for (const [key, value] of Object.entries(extra)) {
52
- if (typeof value === "string" && value !== "") env[key] = value;
53
- }
54
- return env;
55
- }
56
- function killTree(child, signal) {
57
- try {
58
- child.kill(signal);
59
- } catch {
60
- }
61
- }
62
- async function execCapture(file, args, options = {}) {
63
- const started = Date.now();
64
- const timeoutMs = options.timeoutMs ?? 1e4;
65
- const maxStdout = options.maxStdout ?? 1024 * 1024;
66
- const maxStderr = options.maxStderr ?? 1024 * 1024;
67
- const child = spawn(file, args, {
68
- cwd: options.cwd,
69
- env: options.env ?? process.env,
70
- stdio: [options.input === void 0 ? "ignore" : "pipe", "pipe", "pipe"],
71
- windowsHide: true
72
- });
73
- if (options.input !== void 0) {
74
- child.stdin?.on("error", () => void 0);
75
- child.stdin?.end(options.input);
76
- }
77
- let stdout = "";
78
- let stderr = "";
79
- let timedOut = false;
80
- let aborted = false;
81
- const hardKill = setTimeout(() => killTree(child, "SIGKILL"), timeoutMs + 3e3);
82
- const timer = setTimeout(() => {
83
- timedOut = true;
84
- killTree(child, "SIGTERM");
85
- }, timeoutMs);
86
- const onAbort = () => {
87
- aborted = true;
88
- killTree(child, "SIGTERM");
89
- };
90
- options.signal?.addEventListener("abort", onAbort, { once: true });
91
- child.stdout?.setEncoding("utf8");
92
- child.stdout?.on("data", (chunk) => {
93
- if (stdout.length < maxStdout) stdout += chunk;
94
- });
95
- child.stderr?.setEncoding("utf8");
96
- child.stderr?.on("data", (chunk) => {
97
- if (stderr.length < maxStderr) stderr += chunk;
98
- });
99
- const result = await new Promise((resolve, reject) => {
100
- child.on("error", (error) => reject(error));
101
- child.on("close", (code, signal) => resolve({ code, signal }));
102
- });
103
- clearTimeout(timer);
104
- clearTimeout(hardKill);
105
- options.signal?.removeEventListener("abort", onAbort);
106
- return {
107
- ...result,
108
- stdout,
109
- stderr,
110
- timedOut,
111
- aborted,
112
- durationMs: Date.now() - started
113
- };
114
- }
115
-
116
- export {
117
- __export,
118
- sanitizedEnv,
119
- execCapture,
120
- exec_exports
121
- };
@@ -1,8 +0,0 @@
1
- import {
2
- execCapture,
3
- sanitizedEnv
4
- } from "./chunk-R7BK7SJK.js";
5
- export {
6
- execCapture,
7
- sanitizedEnv
8
- };