@morehao/dsh-github-trending 0.8.0 → 0.8.2
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 +8 -0
- package/README.md +12 -0
- package/client.js +35 -7
- package/lib/{chunk-6PVBPJXO.js → chunk-5D2VRWPJ.js} +101 -30
- package/lib/index.d.ts +2 -0
- package/lib/index.js +15 -5
- package/lib/internal.d.ts +41 -3
- package/lib/internal.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v0.8.2] - 2026-11-02
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
- **auth**: register github flow after service is ready (#72)
|
|
8
|
+
|
|
9
|
+
**Full Changelog**: https://github.com/morehao/dsh-github-trending/compare/v0.8.1...v0.8.2
|
|
10
|
+
|
|
3
11
|
## [v0.7.11] - 2026-11-01
|
|
4
12
|
|
|
5
13
|
### Refactors
|
package/README.md
CHANGED
|
@@ -55,6 +55,18 @@ GitHub 趋势榜的 **DeepSeek Harness 插件**:按日 / 周 / 月周期定时
|
|
|
55
55
|
|
|
56
56
|

|
|
57
57
|
|
|
58
|
+
### GitHub 认证
|
|
59
|
+
|
|
60
|
+
认证分区支持三种方式(可随时切换):
|
|
61
|
+
|
|
62
|
+
- **浏览器登录(默认推荐)**:点「用 GitHub 登录」在浏览器完成授权,无需手工粘贴 token。走 OAuth PKCE + 本机回环回调(或设备码),token 由 DSH 凭据记录安全保存,不落 `settings.json`。插件内置了公开的 OAuth App client_id(client_id 属公开标识、非机密;纯 PKCE 不使用 client_secret),开箱即用。
|
|
63
|
+
- **本机 gh**:复用你终端里 `gh auth login` 的登录态。
|
|
64
|
+
- **PAT**:手工填 Personal Access Token(存到凭据引用 / 环境变量)。
|
|
65
|
+
|
|
66
|
+
> 浏览器登录需要核心提供 `authorization` 服务,并且 GitHub flow 注册成功才算可用;两者任一不满足时,设置页直接提示不可用并引导改用 gh / PAT,而不是给一个点了没反应的按钮。宿主启动时服务可能晚于插件就绪,插件会等服务就绪再注册,并在读状态时再兜底一次。
|
|
67
|
+
>
|
|
68
|
+
> 授权页默认尝试自动打开;若被浏览器的弹窗拦截,状态行里始终有可点的「打开授权页」链接与「复制链接」。
|
|
69
|
+
|
|
58
70
|
### 订阅
|
|
59
71
|
|
|
60
72
|
(可选)订阅一组仓库,每周把每个仓库当周新增的 Release 合成一段中文摘要,与榜单同分支、同 PR 一起提交;订阅列表随数据仓版本化,可审计、可复现。
|
package/client.js
CHANGED
|
@@ -132,10 +132,14 @@ var DICT = {
|
|
|
132
132
|
"auth.browser.cancel": "取消",
|
|
133
133
|
"auth.browser.done": "已登录",
|
|
134
134
|
"auth.browser.device": "用设备码登录",
|
|
135
|
-
"auth.browser.unavailable": "
|
|
136
|
-
"auth.browser.openHint": "
|
|
135
|
+
"auth.browser.unavailable": "宿主的 authorization 服务不可用(或 GitHub 授权流程没注册成功):请重试一次;仍不行就改用「本机 gh」或 PAT。",
|
|
136
|
+
"auth.browser.openHint": "若浏览器没有自动打开,点下面的链接完成授权,或复制链接:",
|
|
137
|
+
"auth.browser.open": "打开授权页",
|
|
137
138
|
"auth.browser.copy": "复制链接",
|
|
138
139
|
"auth.browser.copied": "授权链接已复制",
|
|
140
|
+
"auth.browser.failed": "登录失败",
|
|
141
|
+
"auth.browser.failed.detail": "登录失败:{message}",
|
|
142
|
+
"auth.browser.failed.hint": "宿主没有完成 GitHub 授权流程的注册,请重试;仍失败请改用「本机 gh」或 PAT。",
|
|
139
143
|
"auth.pat.hint": "在 GitHub → Settings → Developer settings 生成 Personal Access Token(勾选 public_repo),填到下方引用或环境变量。",
|
|
140
144
|
"auth.tokenRef": "凭据引用",
|
|
141
145
|
"auth.tokenRef.hint": "凭据引用名 / 环境变量名",
|
|
@@ -673,10 +677,14 @@ var DICT = {
|
|
|
673
677
|
"auth.browser.cancel": "Cancel",
|
|
674
678
|
"auth.browser.done": "Signed in",
|
|
675
679
|
"auth.browser.device": "Use a device code",
|
|
676
|
-
"auth.browser.unavailable": "
|
|
677
|
-
"auth.browser.openHint": "If the browser did not open,
|
|
680
|
+
"auth.browser.unavailable": "The host authorization service is unavailable (or the GitHub flow did not register): retry once, then fall back to Local gh or a PAT.",
|
|
681
|
+
"auth.browser.openHint": "If the browser did not open, use the link below to finish signing in, or copy the link:",
|
|
682
|
+
"auth.browser.open": "Open authorization page",
|
|
678
683
|
"auth.browser.copy": "Copy link",
|
|
679
684
|
"auth.browser.copied": "Authorization link copied",
|
|
685
|
+
"auth.browser.failed": "Sign-in failed",
|
|
686
|
+
"auth.browser.failed.detail": "Sign-in failed: {message}",
|
|
687
|
+
"auth.browser.failed.hint": "The host did not register the GitHub authorization flow. Retry, or use Local gh / a PAT instead.",
|
|
680
688
|
"auth.pat.hint": "Create a Personal Access Token under GitHub → Settings → Developer settings (scope public_repo) and put it in the ref below or an env var.",
|
|
681
689
|
"auth.tokenRef": "Credential ref",
|
|
682
690
|
"auth.tokenRef.hint": "credential ref / env var name",
|
|
@@ -1340,6 +1348,8 @@ var CSS = `
|
|
|
1340
1348
|
.dshgt-ready-extra{color:var(--gt-muted);font-size:12px;margin-left:7px;}
|
|
1341
1349
|
.dshgt-linkbtn{border:0;background:none;color:var(--gt-accent);font-family:inherit;font-size:12.5px;padding:6px 2px;border-radius:4px;cursor:pointer;}
|
|
1342
1350
|
.dshgt-linkbtn:hover{text-decoration:underline;}
|
|
1351
|
+
.dshgt-link{color:var(--gt-accent);text-decoration:none;font-size:12.5px;word-break:break-all;}
|
|
1352
|
+
.dshgt-link:hover{text-decoration:underline;}
|
|
1343
1353
|
.dshgt-week{display:flex;gap:6px;align-items:stretch;}
|
|
1344
1354
|
.dshgt-day{flex:1;min-width:0;border:1px solid var(--gt-border);border-radius:8px;padding:8px;background:var(--gt-card);display:flex;flex-direction:column;gap:5px;text-align:left;font-family:inherit;color:inherit;}
|
|
1345
1355
|
.dshgt-day-clickable{cursor:pointer;}
|
|
@@ -2249,8 +2259,13 @@ function SettingsView(props) {
|
|
|
2249
2259
|
};
|
|
2250
2260
|
const startSignIn = async (method) => {
|
|
2251
2261
|
try {
|
|
2252
|
-
|
|
2262
|
+
let view = await beginGithubSignIn(method);
|
|
2263
|
+
if (view.phase === "idle") view = await getGithubSignInStatus();
|
|
2253
2264
|
setSignIn(view);
|
|
2265
|
+
if (view.phase === "error") {
|
|
2266
|
+
notify("err", t("auth.browser.failed.detail", { message: view.error ?? "" }));
|
|
2267
|
+
return;
|
|
2268
|
+
}
|
|
2254
2269
|
if (view.phase === "waiting-browser" && view.authorizeUrl !== void 0) {
|
|
2255
2270
|
try {
|
|
2256
2271
|
window.open(view.authorizeUrl, "_blank", "noopener,noreferrer");
|
|
@@ -2542,8 +2557,10 @@ function SettingsView(props) {
|
|
|
2542
2557
|
}) }),
|
|
2543
2558
|
h("span", { className: "dshgt-hint" }, t("auth.tokenRef.hint"))
|
|
2544
2559
|
),
|
|
2545
|
-
//
|
|
2546
|
-
isBrowser && signIn.phase
|
|
2560
|
+
// 浏览器登录失败:必须显式说出来,否则「点了没反应」又回来了。
|
|
2561
|
+
isBrowser && signIn.phase === "error" ? h(Alert, { kind: "err", title: t("auth.browser.failed") }, signIn.error ?? t("auth.browser.failed.hint")) : null,
|
|
2562
|
+
// 浏览器登录进行中的状态行:可点的授权页链接 + 复制兜底 + 设备码。
|
|
2563
|
+
isBrowser && (signIn.phase === "waiting-browser" || signIn.phase === "waiting-device" || signIn.phase === "exchanging") ? h(
|
|
2547
2564
|
"div",
|
|
2548
2565
|
{ className: "dshgt-hint" },
|
|
2549
2566
|
signIn.authorizeUrl !== void 0 ? h(
|
|
@@ -2551,8 +2568,19 @@ function SettingsView(props) {
|
|
|
2551
2568
|
{ className: "dshgt-desc" },
|
|
2552
2569
|
h("span", null, t("auth.browser.openHint")),
|
|
2553
2570
|
" ",
|
|
2571
|
+
h(
|
|
2572
|
+
"a",
|
|
2573
|
+
{ className: "dshgt-link", href: signIn.authorizeUrl, target: "_blank", rel: "noreferrer" },
|
|
2574
|
+
t("auth.browser.open")
|
|
2575
|
+
),
|
|
2576
|
+
" ",
|
|
2554
2577
|
h(Btn, { small: true, variant: "ghost", onClick: () => copyAuthorizeLink() }, t("auth.browser.copy"))
|
|
2555
2578
|
) : null,
|
|
2579
|
+
signIn.verificationUri !== void 0 ? h(
|
|
2580
|
+
"div",
|
|
2581
|
+
{ className: "dshgt-desc" },
|
|
2582
|
+
h("a", { className: "dshgt-link", href: signIn.verificationUri, target: "_blank", rel: "noreferrer" }, signIn.verificationUri)
|
|
2583
|
+
) : null,
|
|
2556
2584
|
signIn.userCode !== void 0 ? h("div", { className: "dshgt-mono" }, `${t("auth.browser.code")} ${signIn.userCode}`) : null
|
|
2557
2585
|
) : null,
|
|
2558
2586
|
h(
|
|
@@ -3530,6 +3530,7 @@ async function probeGithubAuth(options) {
|
|
|
3530
3530
|
var github_auth_exports = {};
|
|
3531
3531
|
__export(github_auth_exports, {
|
|
3532
3532
|
CALLBACK_PATH: () => CALLBACK_PATH,
|
|
3533
|
+
DEFAULT_GITHUB_CLIENT_ID: () => DEFAULT_GITHUB_CLIENT_ID,
|
|
3533
3534
|
DEFAULT_SCOPE: () => DEFAULT_SCOPE,
|
|
3534
3535
|
GITHUB_RECORD_KEY: () => GITHUB_RECORD_KEY2,
|
|
3535
3536
|
attemptView: () => attemptView,
|
|
@@ -3537,13 +3538,21 @@ __export(github_auth_exports, {
|
|
|
3537
3538
|
buildAuthorizeUrl: () => buildAuthorizeUrl,
|
|
3538
3539
|
cancelGithubSignIn: () => cancelGithubSignIn,
|
|
3539
3540
|
createPkce: () => createPkce,
|
|
3541
|
+
ensureGithubFlow: () => ensureGithubFlow,
|
|
3542
|
+
isGithubFlowRegistered: () => isGithubFlowRegistered,
|
|
3540
3543
|
registerGithubFlow: () => registerGithubFlow,
|
|
3541
3544
|
resetAttemptForTest: () => resetAttemptForTest,
|
|
3545
|
+
resolveClientId: () => resolveClientId,
|
|
3542
3546
|
verifyState: () => verifyState
|
|
3543
3547
|
});
|
|
3544
3548
|
import { createHash as createHash3, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
3545
3549
|
var GITHUB_RECORD_KEY2 = "github/default";
|
|
3546
3550
|
var CALLBACK_PATH = "/api/morehao/github-trending/oauth/github/callback";
|
|
3551
|
+
var DEFAULT_GITHUB_CLIENT_ID = "Ov23li7rhp0EKoqbkJKY";
|
|
3552
|
+
function resolveClientId(deps) {
|
|
3553
|
+
const fromConfig = deps.clientId;
|
|
3554
|
+
return fromConfig !== void 0 && fromConfig !== "" ? fromConfig : DEFAULT_GITHUB_CLIENT_ID;
|
|
3555
|
+
}
|
|
3547
3556
|
var AUTHORIZE_URL = "https://github.com/login/oauth/authorize";
|
|
3548
3557
|
var ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
3549
3558
|
var DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
@@ -3620,7 +3629,7 @@ async function runBrowser(deps, session) {
|
|
|
3620
3629
|
if (webServer === void 0 || typeof webServer.register !== "function") {
|
|
3621
3630
|
throw new Error("webServer \u670D\u52A1\u4E0D\u53EF\u7528\uFF1A\u6D4F\u89C8\u5668\u767B\u5F55\u9700\u8981\u5B83\u627F\u8F7D OAuth \u56DE\u8C03");
|
|
3622
3631
|
}
|
|
3623
|
-
const clientId = deps
|
|
3632
|
+
const clientId = resolveClientId(deps);
|
|
3624
3633
|
if (clientId === void 0 || clientId === "") throw new Error("\u7F3A\u5C11 GitHub OAuth client_id");
|
|
3625
3634
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
3626
3635
|
const endpoints = normalizeEndpoints(deps.endpoints);
|
|
@@ -3688,7 +3697,7 @@ async function runBrowser(deps, session) {
|
|
|
3688
3697
|
}
|
|
3689
3698
|
}
|
|
3690
3699
|
async function runDevice(deps, session) {
|
|
3691
|
-
const clientId = deps
|
|
3700
|
+
const clientId = resolveClientId(deps);
|
|
3692
3701
|
if (clientId === void 0 || clientId === "") throw new Error("\u7F3A\u5C11 GitHub OAuth client_id");
|
|
3693
3702
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
3694
3703
|
const endpoints = normalizeEndpoints(deps.endpoints);
|
|
@@ -3757,13 +3766,8 @@ async function runDevice(deps, session) {
|
|
|
3757
3766
|
}
|
|
3758
3767
|
throw new Error("GitHub device flow \u8D85\u65F6");
|
|
3759
3768
|
}
|
|
3760
|
-
function
|
|
3761
|
-
|
|
3762
|
-
if (authorization === void 0 || typeof authorization.registerFlow !== "function") {
|
|
3763
|
-
deps.logger?.("auth.flow.skipped", { reason: "no-authorization-service" });
|
|
3764
|
-
return;
|
|
3765
|
-
}
|
|
3766
|
-
authorization.registerFlow({
|
|
3769
|
+
function flowDefinition(deps) {
|
|
3770
|
+
return {
|
|
3767
3771
|
key: GITHUB_RECORD_KEY2,
|
|
3768
3772
|
label: "GitHub",
|
|
3769
3773
|
methods: [
|
|
@@ -3771,14 +3775,57 @@ function registerGithubFlow(deps) {
|
|
|
3771
3775
|
{ id: "device", label: "Use a device code" }
|
|
3772
3776
|
],
|
|
3773
3777
|
run: (session) => session.method === "device" ? runDevice(deps, session) : runBrowser(deps, session)
|
|
3774
|
-
}
|
|
3778
|
+
};
|
|
3779
|
+
}
|
|
3780
|
+
var registeredOn = /* @__PURE__ */ new WeakSet();
|
|
3781
|
+
var failedOn = /* @__PURE__ */ new WeakSet();
|
|
3782
|
+
function isGithubFlowRegistered(deps) {
|
|
3783
|
+
const authorization = deps.get("authorization");
|
|
3784
|
+
if (authorization === void 0) return false;
|
|
3785
|
+
if (typeof authorization.describe === "function") {
|
|
3786
|
+
try {
|
|
3787
|
+
return authorization.describe(GITHUB_RECORD_KEY2) !== void 0;
|
|
3788
|
+
} catch {
|
|
3789
|
+
return registeredOn.has(authorization);
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
return registeredOn.has(authorization);
|
|
3793
|
+
}
|
|
3794
|
+
function ensureGithubFlow(deps) {
|
|
3795
|
+
const authorization = deps.get("authorization");
|
|
3796
|
+
if (authorization === void 0 || typeof authorization.registerFlow !== "function") return false;
|
|
3797
|
+
if (isGithubFlowRegistered(deps)) {
|
|
3798
|
+
registeredOn.add(authorization);
|
|
3799
|
+
return true;
|
|
3800
|
+
}
|
|
3801
|
+
if (failedOn.has(authorization)) return false;
|
|
3802
|
+
try {
|
|
3803
|
+
authorization.registerFlow(flowDefinition(deps));
|
|
3804
|
+
} catch (error) {
|
|
3805
|
+
failedOn.add(authorization);
|
|
3806
|
+
deps.logger?.("auth.flow.failed", {
|
|
3807
|
+
key: GITHUB_RECORD_KEY2,
|
|
3808
|
+
message: error instanceof Error ? error.message : String(error)
|
|
3809
|
+
});
|
|
3810
|
+
return isGithubFlowRegistered(deps);
|
|
3811
|
+
}
|
|
3812
|
+
registeredOn.add(authorization);
|
|
3775
3813
|
deps.logger?.("auth.flow.registered", { key: GITHUB_RECORD_KEY2 });
|
|
3814
|
+
return true;
|
|
3815
|
+
}
|
|
3816
|
+
function registerGithubFlow(deps) {
|
|
3817
|
+
if (!ensureGithubFlow(deps)) deps.logger?.("auth.flow.skipped", { reason: "no-authorization-service" });
|
|
3776
3818
|
}
|
|
3819
|
+
var START_TIMEOUT_MS = 1500;
|
|
3820
|
+
var START_POLL_MS = 10;
|
|
3777
3821
|
async function beginGithubSignIn(deps, method = "browser") {
|
|
3778
3822
|
const authorization = deps.get("authorization");
|
|
3779
3823
|
if (authorization === void 0 || typeof authorization.begin !== "function") {
|
|
3780
3824
|
throw new Error("authorization \u670D\u52A1\u4E0D\u53EF\u7528\uFF1A\u65E0\u6CD5\u8FDB\u884C\u6D4F\u89C8\u5668\u767B\u5F55");
|
|
3781
3825
|
}
|
|
3826
|
+
if (!ensureGithubFlow(deps)) {
|
|
3827
|
+
throw new Error("authorization \u670D\u52A1\u5C1A\u672A\u5C31\u7EEA\uFF0CGitHub \u6388\u6743\u6D41\u7A0B\u6CA1\u6709\u6CE8\u518C\u6210\u529F\uFF1A\u8BF7\u7A0D\u540E\u91CD\u8BD5");
|
|
3828
|
+
}
|
|
3782
3829
|
if (current !== void 0 && (current.phase === "waiting-browser" || current.phase === "waiting-device" || current.phase === "exchanging")) {
|
|
3783
3830
|
throw new Error("already-in-flight");
|
|
3784
3831
|
}
|
|
@@ -3786,29 +3833,43 @@ async function beginGithubSignIn(deps, method = "browser") {
|
|
|
3786
3833
|
const timer = setTimeout(() => controller.abort(), attemptTimeout(deps));
|
|
3787
3834
|
const attempt = { phase: "idle", controller, done: Promise.resolve() };
|
|
3788
3835
|
current = attempt;
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
notify
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3836
|
+
let started;
|
|
3837
|
+
try {
|
|
3838
|
+
started = authorization.begin({
|
|
3839
|
+
key: GITHUB_RECORD_KEY2,
|
|
3840
|
+
method,
|
|
3841
|
+
signal: controller.signal,
|
|
3842
|
+
// 浏览器往返靠 authorizeUrl + 回调;notify 只更新内存视图,prompt 不支持(拒绝)。
|
|
3843
|
+
interaction: {
|
|
3844
|
+
notify: (notice) => {
|
|
3845
|
+
if (notice.code !== void 0 && notice.url !== void 0) {
|
|
3846
|
+
updateCurrent({ phase: "waiting-device", verificationUri: notice.url, userCode: notice.code });
|
|
3847
|
+
} else if (notice.url !== void 0) {
|
|
3848
|
+
updateCurrent({ phase: "waiting-browser", authorizeUrl: notice.url });
|
|
3849
|
+
}
|
|
3850
|
+
},
|
|
3851
|
+
prompt: () => Promise.reject(new Error("interactive prompt unsupported in this surface"))
|
|
3852
|
+
}
|
|
3853
|
+
});
|
|
3854
|
+
} catch (error) {
|
|
3855
|
+
clearTimeout(timer);
|
|
3856
|
+
updateCurrent({ phase: "error", error: error instanceof Error ? error.message : String(error) });
|
|
3857
|
+
return attemptView();
|
|
3858
|
+
}
|
|
3859
|
+
attempt.done = started.then((outcome) => {
|
|
3805
3860
|
if (outcome.status === "cancelled") updateCurrent({ phase: "idle" });
|
|
3806
3861
|
}).catch((error) => {
|
|
3807
3862
|
updateCurrent({ phase: "error", error: error instanceof Error ? error.message : String(error) });
|
|
3808
3863
|
}).finally(() => clearTimeout(timer));
|
|
3809
|
-
await
|
|
3864
|
+
await waitForStart(attempt);
|
|
3810
3865
|
return attemptView();
|
|
3811
3866
|
}
|
|
3867
|
+
async function waitForStart(attempt, timeoutMs = START_TIMEOUT_MS) {
|
|
3868
|
+
const deadline = Date.now() + timeoutMs;
|
|
3869
|
+
while (attempt.phase === "idle" && Date.now() < deadline) {
|
|
3870
|
+
await new Promise((resolve) => setTimeout(resolve, START_POLL_MS));
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3812
3873
|
function cancelGithubSignIn(deps) {
|
|
3813
3874
|
const authorization = deps.get("authorization");
|
|
3814
3875
|
if (authorization !== void 0 && typeof authorization.cancel === "function") {
|
|
@@ -4381,6 +4442,14 @@ import { PluginUpdateError } from "@morehao/dsh-plugin-kit/update";
|
|
|
4381
4442
|
var ROUTE_PREFIX = "/api/morehao/github-trending";
|
|
4382
4443
|
var HEADER = "x-dsh-github-trending";
|
|
4383
4444
|
var MAX_BODY2 = 512 * 1024;
|
|
4445
|
+
function isAuthorizationAvailable(get) {
|
|
4446
|
+
const authorization = get("authorization");
|
|
4447
|
+
return authorization !== void 0 && typeof authorization.begin === "function";
|
|
4448
|
+
}
|
|
4449
|
+
function browserSignInAvailable(deps) {
|
|
4450
|
+
if (!isAuthorizationAvailable(deps.get)) return false;
|
|
4451
|
+
return ensureGithubFlow(githubAuthDeps(deps));
|
|
4452
|
+
}
|
|
4384
4453
|
function githubAuthDeps(deps) {
|
|
4385
4454
|
return {
|
|
4386
4455
|
get: deps.get,
|
|
@@ -4575,8 +4644,10 @@ async function dispatch(method, pathname, url, body, deps) {
|
|
|
4575
4644
|
const method2 = body.method === "device" ? "device" : "browser";
|
|
4576
4645
|
try {
|
|
4577
4646
|
const view = await beginGithubSignIn(githubAuthDeps(deps), method2);
|
|
4647
|
+
if (view.phase === "error") throw new IpcError("GITHUB_AUTH_UNAVAILABLE", redact(view.error ?? "GitHub \u767B\u5F55\u5931\u8D25"));
|
|
4578
4648
|
return { attempt: view };
|
|
4579
4649
|
} catch (error) {
|
|
4650
|
+
if (error instanceof IpcError) throw error;
|
|
4580
4651
|
const message = error instanceof Error ? error.message : String(error);
|
|
4581
4652
|
if (message === "already-in-flight") {
|
|
4582
4653
|
return { attempt: attemptView(), alreadyInFlight: true };
|
|
@@ -4722,8 +4793,8 @@ async function statePayload(deps, settings) {
|
|
|
4722
4793
|
source: github.source,
|
|
4723
4794
|
/** 浏览器登录 attempt 视图(idle / waiting-browser / waiting-device / exchanging / done / error)。 */
|
|
4724
4795
|
attempt: attemptView(),
|
|
4725
|
-
/**
|
|
4726
|
-
browserAvailable: deps
|
|
4796
|
+
/** 浏览器登录是否可用:authorization 服务在、且 GitHub flow 真的注册上了(client_id 有内置默认)。 */
|
|
4797
|
+
browserAvailable: browserSignInAvailable(deps)
|
|
4727
4798
|
}
|
|
4728
4799
|
};
|
|
4729
4800
|
}
|
|
@@ -5002,7 +5073,7 @@ export {
|
|
|
5002
5073
|
readPluginInfo,
|
|
5003
5074
|
plugin_info_exports,
|
|
5004
5075
|
github_exports,
|
|
5005
|
-
|
|
5076
|
+
ensureGithubFlow,
|
|
5006
5077
|
github_auth_exports,
|
|
5007
5078
|
import_cli_exports,
|
|
5008
5079
|
pending_pr_exports,
|
package/lib/index.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ interface PluginLogger {
|
|
|
10
10
|
interface PluginContext {
|
|
11
11
|
get?: (name: string) => unknown;
|
|
12
12
|
effect?: (factory: () => unknown, label?: string) => unknown;
|
|
13
|
+
/** cordis 的服务就绪钩子:依赖被提供(或重提供)时回调,用于「晚到的服务」注册。 */
|
|
14
|
+
inject?: (deps: string[], callback: (scope: PluginContext) => unknown) => unknown;
|
|
13
15
|
logger?: PluginLogger;
|
|
14
16
|
webServer?: {
|
|
15
17
|
register: (options: {
|
package/lib/index.js
CHANGED
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
createTrendingHistoryTool,
|
|
8
8
|
createTrendingRunTool,
|
|
9
9
|
createUpdateService,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} from "./chunk-
|
|
10
|
+
ensureGithubFlow,
|
|
11
|
+
readPluginInfo
|
|
12
|
+
} from "./chunk-5D2VRWPJ.js";
|
|
13
13
|
|
|
14
14
|
// src/index.ts
|
|
15
15
|
var name = "dsh-github-trending";
|
|
@@ -64,11 +64,21 @@ function apply(ctx, config = {}) {
|
|
|
64
64
|
onError: (error) => ctx.logger?.warn?.(`[dsh-github-trending] \u5B9A\u65F6\u8FD0\u884C\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`)
|
|
65
65
|
});
|
|
66
66
|
const pluginInfo = readPluginInfo();
|
|
67
|
-
|
|
68
|
-
get,
|
|
67
|
+
const githubAuth = (resolve) => ({
|
|
68
|
+
get: resolve,
|
|
69
69
|
logger: log,
|
|
70
70
|
...config.githubClientId === void 0 ? {} : { clientId: config.githubClientId }
|
|
71
71
|
});
|
|
72
|
+
const registerAuth = (resolve) => {
|
|
73
|
+
if (!ensureGithubFlow(githubAuth(resolve ?? get))) log("auth.flow.deferred", { reason: "authorization-not-ready" });
|
|
74
|
+
};
|
|
75
|
+
if (typeof ctx.inject === "function") {
|
|
76
|
+
ctx.inject(["authorization"], (scope) => {
|
|
77
|
+
registerAuth((name2) => typeof scope.get === "function" ? scope.get(name2) : void 0);
|
|
78
|
+
});
|
|
79
|
+
} else {
|
|
80
|
+
registerAuth();
|
|
81
|
+
}
|
|
72
82
|
const update = createUpdateService({
|
|
73
83
|
get,
|
|
74
84
|
env,
|
package/lib/internal.d.ts
CHANGED
|
@@ -1161,6 +1161,17 @@ declare namespace github {
|
|
|
1161
1161
|
declare const GITHUB_RECORD_KEY = "github/default";
|
|
1162
1162
|
/** 本地回调路径(带集成前缀,避免多 provider 撞 webServer 的 `(kind,path)` 唯一约束)。 */
|
|
1163
1163
|
declare const CALLBACK_PATH = "/api/morehao/github-trending/oauth/github/callback";
|
|
1164
|
+
/**
|
|
1165
|
+
* 内置的 GitHub OAuth App 公开 client_id(**非机密**:OAuth 的 client_id 设计上就是公开的,
|
|
1166
|
+
* 会出现在授权 URL 里,与 `gh`/VS Code 内置自己的 client_id 同理)。
|
|
1167
|
+
* 纯 PKCE public client,无需 client_secret。用户/部署可在 `apply(config.githubClientId)` 覆盖。
|
|
1168
|
+
*
|
|
1169
|
+
* OAuth App 侧登记的回调:`http://127.0.0.1/api/morehao/github-trending/oauth/github/callback`
|
|
1170
|
+
* (loopback 按 RFC 8252 忽略端口,运行时随机端口可正常回跳)。
|
|
1171
|
+
*/
|
|
1172
|
+
declare const DEFAULT_GITHUB_CLIENT_ID = "Ov23li7rhp0EKoqbkJKY";
|
|
1173
|
+
/** 解析生效的 client_id:显式 config 优先,否则用内置默认。 */
|
|
1174
|
+
declare function resolveClientId(deps: GithubAuthDeps): string | undefined;
|
|
1164
1175
|
/** 本插件运行需要 push / 建 PR,`read:user` 不够;一次授权到 `repo`。 */
|
|
1165
1176
|
declare const DEFAULT_SCOPE = "repo";
|
|
1166
1177
|
interface AuthorizationFlow {
|
|
@@ -1192,6 +1203,8 @@ interface AuthorizationLike {
|
|
|
1192
1203
|
status: 'authorized' | 'cancelled';
|
|
1193
1204
|
}>;
|
|
1194
1205
|
cancel?: (key: string) => void;
|
|
1206
|
+
/** 核心 0.2.x 有:按 key 查已注册的 flow;判定「真的注册上了」靠它。 */
|
|
1207
|
+
describe?: (key: string) => unknown;
|
|
1195
1208
|
}
|
|
1196
1209
|
interface WebServerLike {
|
|
1197
1210
|
register?: (route: {
|
|
@@ -1253,14 +1266,35 @@ interface GithubGrantPayload {
|
|
|
1253
1266
|
tokenType?: string;
|
|
1254
1267
|
scope?: string;
|
|
1255
1268
|
}
|
|
1269
|
+
/** 这个 key 现在是否**真的**有 flow 在注册表里。 */
|
|
1270
|
+
declare function isGithubFlowRegistered(deps: GithubAuthDeps): boolean;
|
|
1271
|
+
/**
|
|
1272
|
+
* 幂等注册 GitHub flow;`authorization` 还没就绪时返回 false,调用方可稍后重试。
|
|
1273
|
+
*
|
|
1274
|
+
* 为什么不能只在 `apply()` 里试一次:宿主按「服务可用性」激活插件,本插件只
|
|
1275
|
+
* `inject: ['webServer']`,apply 完全可能早于 `authorization`——后者要等
|
|
1276
|
+
* `credentials` 的异步 `[Service.init]`(启动时读 `.credentials.yaml`)走完才
|
|
1277
|
+
* 可见,而 `ctx.get()` 对未 active 的 provider 返回 undefined。那一刻若放弃注册,
|
|
1278
|
+
* 就永久没有 flow;而可用性又是按请求现算的,于是出现「按钮亮着、点了必然
|
|
1279
|
+
* NO_FLOW、界面毫无反馈」。所以注册做成可重入的自愈入口:apply 用
|
|
1280
|
+
* `ctx.inject(['authorization'])` 等服务就绪,HTTP 面每次读状态再兜底一次。
|
|
1281
|
+
*
|
|
1282
|
+
* @returns 注册后是否可用(服务缺失 / 注册失败时为 false)。
|
|
1283
|
+
*/
|
|
1284
|
+
declare function ensureGithubFlow(deps: GithubAuthDeps): boolean;
|
|
1256
1285
|
/**
|
|
1257
1286
|
* 向 `ctx.authorization` 注册 GitHub flow(若核心没有该服务则降级 no-op)。
|
|
1258
|
-
*
|
|
1287
|
+
* 等价于 {@link ensureGithubFlow},保留旧名字给 `apply()` 与测试用。
|
|
1259
1288
|
*/
|
|
1260
1289
|
declare function registerGithubFlow(deps: GithubAuthDeps): void;
|
|
1261
1290
|
/**
|
|
1262
1291
|
* 启动一次浏览器登录 attempt(进程内 `authorization.begin`,不阻塞请求)。
|
|
1263
|
-
*
|
|
1292
|
+
*
|
|
1293
|
+
* 返回的视图保证**不是 `idle`**:要么已经进入 waiting-*,要么已经落成 `error`
|
|
1294
|
+
* (供路由转成 HTTP 错误)。`idle` 只可能来自「flow 没注册」这类抢跑,前端拿到
|
|
1295
|
+
* 它就会以为什么都没发生——那正是「点了没反应」的形态,所以这里把它堵掉。
|
|
1296
|
+
*
|
|
1297
|
+
* @throws 服务不可用、flow 未注册、已有 attempt 在飞时抛错。
|
|
1264
1298
|
*/
|
|
1265
1299
|
declare function beginGithubSignIn(deps: GithubAuthDeps, method?: 'browser' | 'device'): Promise<GithubAttemptView>;
|
|
1266
1300
|
/** 取消进行中的 attempt。 */
|
|
@@ -1272,6 +1306,7 @@ type githubAuth_AuthorizationFlow = AuthorizationFlow;
|
|
|
1272
1306
|
type githubAuth_AuthorizationLike = AuthorizationLike;
|
|
1273
1307
|
type githubAuth_AuthorizationSessionLike = AuthorizationSessionLike;
|
|
1274
1308
|
declare const githubAuth_CALLBACK_PATH: typeof CALLBACK_PATH;
|
|
1309
|
+
declare const githubAuth_DEFAULT_GITHUB_CLIENT_ID: typeof DEFAULT_GITHUB_CLIENT_ID;
|
|
1275
1310
|
declare const githubAuth_DEFAULT_SCOPE: typeof DEFAULT_SCOPE;
|
|
1276
1311
|
declare const githubAuth_GITHUB_RECORD_KEY: typeof GITHUB_RECORD_KEY;
|
|
1277
1312
|
type githubAuth_GithubAttemptPhase = GithubAttemptPhase;
|
|
@@ -1284,11 +1319,14 @@ declare const githubAuth_beginGithubSignIn: typeof beginGithubSignIn;
|
|
|
1284
1319
|
declare const githubAuth_buildAuthorizeUrl: typeof buildAuthorizeUrl;
|
|
1285
1320
|
declare const githubAuth_cancelGithubSignIn: typeof cancelGithubSignIn;
|
|
1286
1321
|
declare const githubAuth_createPkce: typeof createPkce;
|
|
1322
|
+
declare const githubAuth_ensureGithubFlow: typeof ensureGithubFlow;
|
|
1323
|
+
declare const githubAuth_isGithubFlowRegistered: typeof isGithubFlowRegistered;
|
|
1287
1324
|
declare const githubAuth_registerGithubFlow: typeof registerGithubFlow;
|
|
1288
1325
|
declare const githubAuth_resetAttemptForTest: typeof resetAttemptForTest;
|
|
1326
|
+
declare const githubAuth_resolveClientId: typeof resolveClientId;
|
|
1289
1327
|
declare const githubAuth_verifyState: typeof verifyState;
|
|
1290
1328
|
declare namespace githubAuth {
|
|
1291
|
-
export { type githubAuth_AuthorizationFlow as AuthorizationFlow, type githubAuth_AuthorizationLike as AuthorizationLike, type githubAuth_AuthorizationSessionLike as AuthorizationSessionLike, githubAuth_CALLBACK_PATH as CALLBACK_PATH, githubAuth_DEFAULT_SCOPE as DEFAULT_SCOPE, githubAuth_GITHUB_RECORD_KEY as GITHUB_RECORD_KEY, type githubAuth_GithubAttemptPhase as GithubAttemptPhase, type githubAuth_GithubAttemptView as GithubAttemptView, type githubAuth_GithubAuthDeps as GithubAuthDeps, type githubAuth_GithubGrantPayload as GithubGrantPayload, type githubAuth_WebServerLike as WebServerLike, githubAuth_attemptView as attemptView, githubAuth_beginGithubSignIn as beginGithubSignIn, githubAuth_buildAuthorizeUrl as buildAuthorizeUrl, githubAuth_cancelGithubSignIn as cancelGithubSignIn, githubAuth_createPkce as createPkce, githubAuth_registerGithubFlow as registerGithubFlow, githubAuth_resetAttemptForTest as resetAttemptForTest, githubAuth_verifyState as verifyState };
|
|
1329
|
+
export { type githubAuth_AuthorizationFlow as AuthorizationFlow, type githubAuth_AuthorizationLike as AuthorizationLike, type githubAuth_AuthorizationSessionLike as AuthorizationSessionLike, githubAuth_CALLBACK_PATH as CALLBACK_PATH, githubAuth_DEFAULT_GITHUB_CLIENT_ID as DEFAULT_GITHUB_CLIENT_ID, githubAuth_DEFAULT_SCOPE as DEFAULT_SCOPE, githubAuth_GITHUB_RECORD_KEY as GITHUB_RECORD_KEY, type githubAuth_GithubAttemptPhase as GithubAttemptPhase, type githubAuth_GithubAttemptView as GithubAttemptView, type githubAuth_GithubAuthDeps as GithubAuthDeps, type githubAuth_GithubGrantPayload as GithubGrantPayload, type githubAuth_WebServerLike as WebServerLike, githubAuth_attemptView as attemptView, githubAuth_beginGithubSignIn as beginGithubSignIn, githubAuth_buildAuthorizeUrl as buildAuthorizeUrl, githubAuth_cancelGithubSignIn as cancelGithubSignIn, githubAuth_createPkce as createPkce, githubAuth_ensureGithubFlow as ensureGithubFlow, githubAuth_isGithubFlowRegistered as isGithubFlowRegistered, githubAuth_registerGithubFlow as registerGithubFlow, githubAuth_resetAttemptForTest as resetAttemptForTest, githubAuth_resolveClientId as resolveClientId, githubAuth_verifyState as verifyState };
|
|
1292
1330
|
}
|
|
1293
1331
|
|
|
1294
1332
|
interface DefaultBranchResult {
|
package/lib/internal.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morehao/dsh-github-trending",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "GitHub 趋势日报的 DeepSeek Harness 插件:按日 / 周 / 月周期定时 spawn 本地 trendradar 核心,抓取 Trending、生成中文简介与周期总结,并可选把订阅仓库的新 Release 合成中文摘要,一起提交到你的仓库、自动开 PR 合并;内置概览 / 榜单 / 运行记录 / 订阅 / 设置工作台。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|