@morehao/dsh-github-trending 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +4 -2
- package/client.js +46 -11
- package/lib/{chunk-LLTW4YHJ.js → chunk-VO72OOVK.js} +140 -37
- package/lib/index.d.ts +3 -0
- package/lib/index.js +19 -6
- package/lib/internal.d.ts +58 -5
- package/lib/internal.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v0.8.3] - 2026-11-02
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
- **auth**: fall back to device flow when client_secret is missing (#75)
|
|
8
|
+
|
|
9
|
+
**Full Changelog**: https://github.com/morehao/dsh-github-trending/compare/v0.8.2...v0.8.3
|
|
10
|
+
|
|
11
|
+
## [v0.8.2] - 2026-11-02
|
|
12
|
+
|
|
13
|
+
### Fixes
|
|
14
|
+
|
|
15
|
+
- **auth**: register github flow after service is ready (#72)
|
|
16
|
+
|
|
17
|
+
**Full Changelog**: https://github.com/morehao/dsh-github-trending/compare/v0.8.1...v0.8.2
|
|
18
|
+
|
|
3
19
|
## [v0.7.11] - 2026-11-01
|
|
4
20
|
|
|
5
21
|
### Refactors
|
package/README.md
CHANGED
|
@@ -59,11 +59,13 @@ GitHub 趋势榜的 **DeepSeek Harness 插件**:按日 / 周 / 月周期定时
|
|
|
59
59
|
|
|
60
60
|
认证分区支持三种方式(可随时切换):
|
|
61
61
|
|
|
62
|
-
- **浏览器登录(默认推荐)**:点「用 GitHub 登录」在浏览器完成授权,无需手工粘贴 token
|
|
62
|
+
- **浏览器登录(默认推荐)**:点「用 GitHub 登录」在浏览器完成授权,无需手工粘贴 token,token 由 DSH 凭据记录安全保存、不落 `settings.json`。插件内置了公开的 OAuth App client_id,开箱即用;GitHub 的 web flow 换 token 仍然**必须带 client_secret**(PKCE 只是叠加保护),而客户端不能内置机密,所以默认走**设备码**:点一下后在打开的 GitHub 页面输入验证码即可。若你自带 OAuth App,可在插件配置里同时给 `githubClientId` + `githubClientSecret`,就会改用本机回环回调(不用手输验证码)。
|
|
63
63
|
- **本机 gh**:复用你终端里 `gh auth login` 的登录态。
|
|
64
64
|
- **PAT**:手工填 Personal Access Token(存到凭据引用 / 环境变量)。
|
|
65
65
|
|
|
66
|
-
> 浏览器登录需要核心提供 `authorization`
|
|
66
|
+
> 浏览器登录需要核心提供 `authorization` 服务,并且 GitHub flow 注册成功才算可用;两者任一不满足时,设置页直接提示不可用并引导改用 gh / PAT,而不是给一个点了没反应的按钮。宿主启动时服务可能晚于插件就绪,插件会等服务就绪再注册,并在读状态时再兜底一次。
|
|
67
|
+
>
|
|
68
|
+
> 设备码 / 授权页默认尝试自动打开;若被浏览器的弹窗拦截,状态行里始终有可点的「打开授权页」链接、验证码与「复制链接」。
|
|
67
69
|
|
|
68
70
|
### 订阅
|
|
69
71
|
|
package/client.js
CHANGED
|
@@ -125,17 +125,21 @@ var DICT = {
|
|
|
125
125
|
"auth.gh": "本机 gh",
|
|
126
126
|
"auth.browser": "浏览器登录",
|
|
127
127
|
"auth.gh.hint": "使用本机 gh 登录态:终端执行 `gh auth login` 完成登录,再点「测试认证」。",
|
|
128
|
-
"auth.browser.hint": "在浏览器里完成 GitHub 授权,token 由 DSH
|
|
128
|
+
"auth.browser.hint": "在浏览器里完成 GitHub 授权,token 由 DSH 安全保存,无需手工粘贴。默认走设备码:点一下后按提示输入验证码。",
|
|
129
129
|
"auth.browser.signin": "用 GitHub 登录",
|
|
130
130
|
"auth.browser.waiting": "等待浏览器授权…",
|
|
131
131
|
"auth.browser.code": "在打开的页面输入验证码:",
|
|
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": "请重试一次;仍失败可改用「本机 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": "凭据引用名 / 环境变量名",
|
|
@@ -666,17 +670,21 @@ var DICT = {
|
|
|
666
670
|
"auth.gh": "Local gh",
|
|
667
671
|
"auth.browser": "Browser login",
|
|
668
672
|
"auth.gh.hint": "Uses your local gh login: run `gh auth login` in a terminal, then click Test authentication.",
|
|
669
|
-
"auth.browser.hint": "Authorize GitHub in your browser; DSH stores the token securely, no manual paste.",
|
|
673
|
+
"auth.browser.hint": "Authorize GitHub in your browser; DSH stores the token securely, no manual paste. Uses a device code by default: enter the code on the GitHub page after clicking.",
|
|
670
674
|
"auth.browser.signin": "Sign in with GitHub",
|
|
671
675
|
"auth.browser.waiting": "Waiting for browser authorization…",
|
|
672
676
|
"auth.browser.code": "Enter this code on the opened page:",
|
|
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": "Retry once; if it keeps failing, use Local gh or 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,14 +2259,25 @@ 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");
|
|
2257
2272
|
} catch {
|
|
2258
2273
|
}
|
|
2259
2274
|
}
|
|
2275
|
+
if (view.phase === "waiting-device" && view.verificationUri !== void 0) {
|
|
2276
|
+
try {
|
|
2277
|
+
window.open(view.verificationUri, "_blank", "noopener,noreferrer");
|
|
2278
|
+
} catch {
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2260
2281
|
} catch (error) {
|
|
2261
2282
|
notify("err", error instanceof Error ? error.message : String(error));
|
|
2262
2283
|
}
|
|
@@ -2333,6 +2354,7 @@ function SettingsView(props) {
|
|
|
2333
2354
|
const isGh = draft.github.authMode === "gh";
|
|
2334
2355
|
const isBrowser = draft.github.authMode === "browser";
|
|
2335
2356
|
const browserAvailable = state.github.browserAvailable === true;
|
|
2357
|
+
const loopbackAvailable = state.github.loopbackAvailable === true;
|
|
2336
2358
|
const sections = [
|
|
2337
2359
|
{ id: "source", label: t("st.source") },
|
|
2338
2360
|
{ id: "model", label: t("st.model") },
|
|
@@ -2527,12 +2549,12 @@ function SettingsView(props) {
|
|
|
2527
2549
|
{
|
|
2528
2550
|
small: true,
|
|
2529
2551
|
variant: "primary",
|
|
2530
|
-
onClick: () => void startSignIn("browser"),
|
|
2552
|
+
onClick: () => void startSignIn(loopbackAvailable ? "browser" : "device"),
|
|
2531
2553
|
disabled: signInActive
|
|
2532
2554
|
},
|
|
2533
2555
|
signIn.phase === "done" && signIn.login !== void 0 ? `${t("auth.browser.done")} · @${signIn.login}` : signInActive ? t("auth.browser.waiting") : t("auth.browser.signin")
|
|
2534
2556
|
) : h("span", { className: "dshgt-hint" }, t("auth.browser.unavailable")),
|
|
2535
|
-
browserAvailable ? h(Btn, { small: true, variant: "ghost", onClick: () => void startSignIn("device"), disabled: signInActive }, t("auth.browser.device")) : null,
|
|
2557
|
+
browserAvailable && loopbackAvailable ? h(Btn, { small: true, variant: "ghost", onClick: () => void startSignIn("device"), disabled: signInActive }, t("auth.browser.device")) : null,
|
|
2536
2558
|
signInActive ? h(Btn, { small: true, variant: "ghost", onClick: () => void cancelSignIn() }, t("auth.browser.cancel")) : null
|
|
2537
2559
|
) : h(
|
|
2538
2560
|
Row,
|
|
@@ -2542,8 +2564,10 @@ function SettingsView(props) {
|
|
|
2542
2564
|
}) }),
|
|
2543
2565
|
h("span", { className: "dshgt-hint" }, t("auth.tokenRef.hint"))
|
|
2544
2566
|
),
|
|
2545
|
-
//
|
|
2546
|
-
isBrowser && signIn.phase
|
|
2567
|
+
// 浏览器登录失败:必须显式说出来,否则「点了没反应」又回来了。
|
|
2568
|
+
isBrowser && signIn.phase === "error" ? h(Alert, { kind: "err", title: t("auth.browser.failed") }, signIn.error ?? t("auth.browser.failed.hint")) : null,
|
|
2569
|
+
// 浏览器登录进行中的状态行:可点的授权页链接 + 复制兜底 + 设备码。
|
|
2570
|
+
isBrowser && (signIn.phase === "waiting-browser" || signIn.phase === "waiting-device" || signIn.phase === "exchanging") ? h(
|
|
2547
2571
|
"div",
|
|
2548
2572
|
{ className: "dshgt-hint" },
|
|
2549
2573
|
signIn.authorizeUrl !== void 0 ? h(
|
|
@@ -2551,8 +2575,19 @@ function SettingsView(props) {
|
|
|
2551
2575
|
{ className: "dshgt-desc" },
|
|
2552
2576
|
h("span", null, t("auth.browser.openHint")),
|
|
2553
2577
|
" ",
|
|
2578
|
+
h(
|
|
2579
|
+
"a",
|
|
2580
|
+
{ className: "dshgt-link", href: signIn.authorizeUrl, target: "_blank", rel: "noreferrer" },
|
|
2581
|
+
t("auth.browser.open")
|
|
2582
|
+
),
|
|
2583
|
+
" ",
|
|
2554
2584
|
h(Btn, { small: true, variant: "ghost", onClick: () => copyAuthorizeLink() }, t("auth.browser.copy"))
|
|
2555
2585
|
) : null,
|
|
2586
|
+
signIn.verificationUri !== void 0 ? h(
|
|
2587
|
+
"div",
|
|
2588
|
+
{ className: "dshgt-desc" },
|
|
2589
|
+
h("a", { className: "dshgt-link", href: signIn.verificationUri, target: "_blank", rel: "noreferrer" }, signIn.verificationUri)
|
|
2590
|
+
) : null,
|
|
2556
2591
|
signIn.userCode !== void 0 ? h("div", { className: "dshgt-mono" }, `${t("auth.browser.code")} ${signIn.userCode}`) : null
|
|
2557
2592
|
) : null,
|
|
2558
2593
|
h(
|
|
@@ -3538,9 +3538,14 @@ __export(github_auth_exports, {
|
|
|
3538
3538
|
buildAuthorizeUrl: () => buildAuthorizeUrl,
|
|
3539
3539
|
cancelGithubSignIn: () => cancelGithubSignIn,
|
|
3540
3540
|
createPkce: () => createPkce,
|
|
3541
|
+
ensureGithubFlow: () => ensureGithubFlow,
|
|
3542
|
+
isGithubFlowRegistered: () => isGithubFlowRegistered,
|
|
3543
|
+
isLoopbackSignInAvailable: () => isLoopbackSignInAvailable,
|
|
3541
3544
|
registerGithubFlow: () => registerGithubFlow,
|
|
3542
3545
|
resetAttemptForTest: () => resetAttemptForTest,
|
|
3543
3546
|
resolveClientId: () => resolveClientId,
|
|
3547
|
+
resolveClientSecret: () => resolveClientSecret,
|
|
3548
|
+
resolveMethod: () => resolveMethod,
|
|
3544
3549
|
verifyState: () => verifyState
|
|
3545
3550
|
});
|
|
3546
3551
|
import { createHash as createHash3, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
@@ -3551,6 +3556,16 @@ function resolveClientId(deps) {
|
|
|
3551
3556
|
const fromConfig = deps.clientId;
|
|
3552
3557
|
return fromConfig !== void 0 && fromConfig !== "" ? fromConfig : DEFAULT_GITHUB_CLIENT_ID;
|
|
3553
3558
|
}
|
|
3559
|
+
function resolveClientSecret(deps) {
|
|
3560
|
+
const secret = deps.clientSecret;
|
|
3561
|
+
return secret !== void 0 && secret !== "" ? secret : void 0;
|
|
3562
|
+
}
|
|
3563
|
+
function isLoopbackSignInAvailable(deps) {
|
|
3564
|
+
return resolveClientSecret(deps) !== void 0;
|
|
3565
|
+
}
|
|
3566
|
+
function resolveMethod(deps, requested) {
|
|
3567
|
+
return requested === "browser" && !isLoopbackSignInAvailable(deps) ? "device" : requested;
|
|
3568
|
+
}
|
|
3554
3569
|
var AUTHORIZE_URL = "https://github.com/login/oauth/authorize";
|
|
3555
3570
|
var ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
3556
3571
|
var DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
@@ -3587,21 +3602,29 @@ function buildAuthorizeUrl(params) {
|
|
|
3587
3602
|
return url.toString();
|
|
3588
3603
|
}
|
|
3589
3604
|
async function exchangeCode(fetchImpl, endpoint, params, signal) {
|
|
3605
|
+
const form = new URLSearchParams({
|
|
3606
|
+
client_id: params.clientId,
|
|
3607
|
+
code: params.code,
|
|
3608
|
+
code_verifier: params.verifier,
|
|
3609
|
+
redirect_uri: params.redirectUri
|
|
3610
|
+
});
|
|
3611
|
+
if (params.clientSecret !== void 0) form.set("client_secret", params.clientSecret);
|
|
3590
3612
|
const response = await fetchImpl(endpoint, {
|
|
3591
3613
|
method: "POST",
|
|
3592
3614
|
headers: { accept: "application/json", "content-type": "application/x-www-form-urlencoded" },
|
|
3593
|
-
body:
|
|
3594
|
-
client_id: params.clientId,
|
|
3595
|
-
code: params.code,
|
|
3596
|
-
code_verifier: params.verifier,
|
|
3597
|
-
redirect_uri: params.redirectUri
|
|
3598
|
-
}).toString(),
|
|
3615
|
+
body: form.toString(),
|
|
3599
3616
|
signal
|
|
3600
3617
|
});
|
|
3601
3618
|
if (!response.ok) throw new Error(`GitHub access_token HTTP ${response.status}`);
|
|
3602
3619
|
const body = await response.json();
|
|
3603
3620
|
if (typeof body.access_token !== "string" || body.access_token === "") {
|
|
3604
|
-
|
|
3621
|
+
const error = typeof body.error === "string" ? body.error : void 0;
|
|
3622
|
+
if (error === "incorrect_client_credentials") {
|
|
3623
|
+
throw new Error(
|
|
3624
|
+
params.clientSecret === void 0 ? "GitHub \u7F51\u9875\u6388\u6743\u9700\u8981 client_secret\uFF08incorrect_client_credentials\uFF09\uFF0C\u5185\u7F6E\u7684\u516C\u5F00 client_id \u65E0\u6CD5\u5B8C\u6210\u8FD9\u4E00\u6B65\uFF1A\u8BF7\u6539\u7528\u8BBE\u5907\u7801\u767B\u5F55\uFF0C\u6216\u914D\u7F6E\u81EA\u5DF1\u7684 OAuth App client_id + client_secret\u3002" : "GitHub \u8BA4\u4E3A\u8FD9\u4E2A OAuth App \u7684 client_id / client_secret \u4E0D\u6B63\u786E\uFF08incorrect_client_credentials\uFF09\uFF1A\u8BF7\u68C0\u67E5\u63D2\u4EF6\u914D\u7F6E\uFF0C\u6216\u6539\u7528\u8BBE\u5907\u7801\u767B\u5F55\u3002"
|
|
3625
|
+
);
|
|
3626
|
+
}
|
|
3627
|
+
throw new Error(`GitHub access_token \u672A\u8FD4\u56DE token${error === void 0 ? "" : `\uFF08${error}\uFF09`}`);
|
|
3605
3628
|
}
|
|
3606
3629
|
return {
|
|
3607
3630
|
token: body.access_token,
|
|
@@ -3629,6 +3652,10 @@ async function runBrowser(deps, session) {
|
|
|
3629
3652
|
}
|
|
3630
3653
|
const clientId = resolveClientId(deps);
|
|
3631
3654
|
if (clientId === void 0 || clientId === "") throw new Error("\u7F3A\u5C11 GitHub OAuth client_id");
|
|
3655
|
+
const clientSecret = resolveClientSecret(deps);
|
|
3656
|
+
if (clientSecret === void 0) {
|
|
3657
|
+
throw new Error("\u56DE\u73AF\u767B\u5F55\u4E0D\u53EF\u7528\uFF1A\u6CA1\u6709\u914D\u7F6E GitHub OAuth App \u7684 client_secret\uFF0C\u8BF7\u6539\u7528\u8BBE\u5907\u7801\u767B\u5F55");
|
|
3658
|
+
}
|
|
3632
3659
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
3633
3660
|
const endpoints = normalizeEndpoints(deps.endpoints);
|
|
3634
3661
|
const scope = deps.scope ?? DEFAULT_SCOPE;
|
|
@@ -3679,7 +3706,12 @@ async function runBrowser(deps, session) {
|
|
|
3679
3706
|
session.notify?.({ message: "Continue in your browser to sign in to GitHub.", url: authorizeUrl });
|
|
3680
3707
|
const code = await withAbort(codePromise, session.signal, () => rejectCode(new Error("authorization cancelled")));
|
|
3681
3708
|
updateCurrent({ phase: "exchanging" });
|
|
3682
|
-
const exchanged = await exchangeCode(
|
|
3709
|
+
const exchanged = await exchangeCode(
|
|
3710
|
+
fetchImpl,
|
|
3711
|
+
endpoints.accessToken,
|
|
3712
|
+
{ clientId, clientSecret, code, verifier, redirectUri },
|
|
3713
|
+
session.signal
|
|
3714
|
+
);
|
|
3683
3715
|
const login = await fetchLogin(fetchImpl, endpoints.user, exchanged.token, session.signal);
|
|
3684
3716
|
const payload = {
|
|
3685
3717
|
version: 1,
|
|
@@ -3709,7 +3741,10 @@ async function runDevice(deps, session) {
|
|
|
3709
3741
|
if (!startResponse.ok) throw new Error(`GitHub device/code HTTP ${startResponse.status}`);
|
|
3710
3742
|
const start = await startResponse.json();
|
|
3711
3743
|
if (typeof start.device_code !== "string" || typeof start.user_code !== "string" || typeof start.verification_uri !== "string") {
|
|
3712
|
-
|
|
3744
|
+
const error = typeof start.error === "string" ? start.error : void 0;
|
|
3745
|
+
throw new Error(
|
|
3746
|
+
error === "device_flow_disabled" ? "GitHub \u6CA1\u6709\u4E3A\u8FD9\u4E2A OAuth App \u542F\u7528\u8BBE\u5907\u7801\u767B\u5F55\uFF1A\u8BF7\u5230 OAuth App \u8BBE\u7F6E\u52FE\u9009 Device flow\uFF0C\u6216\u6539\u7528\u300C\u672C\u673A gh\u300D/ PAT\u3002" : `GitHub device/code \u8FD4\u56DE\u4E0D\u5B8C\u6574${error === void 0 ? "" : `\uFF08${error}\uFF09`}`
|
|
3747
|
+
);
|
|
3713
3748
|
}
|
|
3714
3749
|
const deviceCode = start.device_code;
|
|
3715
3750
|
const intervalSec = typeof start.interval === "number" && start.interval > 0 ? start.interval : 5;
|
|
@@ -3764,13 +3799,8 @@ async function runDevice(deps, session) {
|
|
|
3764
3799
|
}
|
|
3765
3800
|
throw new Error("GitHub device flow \u8D85\u65F6");
|
|
3766
3801
|
}
|
|
3767
|
-
function
|
|
3768
|
-
|
|
3769
|
-
if (authorization === void 0 || typeof authorization.registerFlow !== "function") {
|
|
3770
|
-
deps.logger?.("auth.flow.skipped", { reason: "no-authorization-service" });
|
|
3771
|
-
return;
|
|
3772
|
-
}
|
|
3773
|
-
authorization.registerFlow({
|
|
3802
|
+
function flowDefinition(deps) {
|
|
3803
|
+
return {
|
|
3774
3804
|
key: GITHUB_RECORD_KEY2,
|
|
3775
3805
|
label: "GitHub",
|
|
3776
3806
|
methods: [
|
|
@@ -3778,44 +3808,105 @@ function registerGithubFlow(deps) {
|
|
|
3778
3808
|
{ id: "device", label: "Use a device code" }
|
|
3779
3809
|
],
|
|
3780
3810
|
run: (session) => session.method === "device" ? runDevice(deps, session) : runBrowser(deps, session)
|
|
3781
|
-
}
|
|
3811
|
+
};
|
|
3812
|
+
}
|
|
3813
|
+
var registeredOn = /* @__PURE__ */ new WeakSet();
|
|
3814
|
+
var failedOn = /* @__PURE__ */ new WeakSet();
|
|
3815
|
+
function isGithubFlowRegistered(deps) {
|
|
3816
|
+
const authorization = deps.get("authorization");
|
|
3817
|
+
if (authorization === void 0) return false;
|
|
3818
|
+
if (typeof authorization.describe === "function") {
|
|
3819
|
+
try {
|
|
3820
|
+
return authorization.describe(GITHUB_RECORD_KEY2) !== void 0;
|
|
3821
|
+
} catch {
|
|
3822
|
+
return registeredOn.has(authorization);
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
return registeredOn.has(authorization);
|
|
3826
|
+
}
|
|
3827
|
+
function ensureGithubFlow(deps) {
|
|
3828
|
+
const authorization = deps.get("authorization");
|
|
3829
|
+
if (authorization === void 0 || typeof authorization.registerFlow !== "function") return false;
|
|
3830
|
+
if (isGithubFlowRegistered(deps)) {
|
|
3831
|
+
registeredOn.add(authorization);
|
|
3832
|
+
return true;
|
|
3833
|
+
}
|
|
3834
|
+
if (failedOn.has(authorization)) return false;
|
|
3835
|
+
try {
|
|
3836
|
+
authorization.registerFlow(flowDefinition(deps));
|
|
3837
|
+
} catch (error) {
|
|
3838
|
+
failedOn.add(authorization);
|
|
3839
|
+
deps.logger?.("auth.flow.failed", {
|
|
3840
|
+
key: GITHUB_RECORD_KEY2,
|
|
3841
|
+
message: error instanceof Error ? error.message : String(error)
|
|
3842
|
+
});
|
|
3843
|
+
return isGithubFlowRegistered(deps);
|
|
3844
|
+
}
|
|
3845
|
+
registeredOn.add(authorization);
|
|
3782
3846
|
deps.logger?.("auth.flow.registered", { key: GITHUB_RECORD_KEY2 });
|
|
3847
|
+
return true;
|
|
3848
|
+
}
|
|
3849
|
+
function registerGithubFlow(deps) {
|
|
3850
|
+
if (!ensureGithubFlow(deps)) deps.logger?.("auth.flow.skipped", { reason: "no-authorization-service" });
|
|
3783
3851
|
}
|
|
3852
|
+
var START_TIMEOUT_MS = 1500;
|
|
3853
|
+
var START_POLL_MS = 10;
|
|
3784
3854
|
async function beginGithubSignIn(deps, method = "browser") {
|
|
3785
3855
|
const authorization = deps.get("authorization");
|
|
3786
3856
|
if (authorization === void 0 || typeof authorization.begin !== "function") {
|
|
3787
3857
|
throw new Error("authorization \u670D\u52A1\u4E0D\u53EF\u7528\uFF1A\u65E0\u6CD5\u8FDB\u884C\u6D4F\u89C8\u5668\u767B\u5F55");
|
|
3788
3858
|
}
|
|
3859
|
+
if (!ensureGithubFlow(deps)) {
|
|
3860
|
+
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");
|
|
3861
|
+
}
|
|
3789
3862
|
if (current !== void 0 && (current.phase === "waiting-browser" || current.phase === "waiting-device" || current.phase === "exchanging")) {
|
|
3790
3863
|
throw new Error("already-in-flight");
|
|
3791
3864
|
}
|
|
3865
|
+
const effectiveMethod = resolveMethod(deps, method);
|
|
3866
|
+
if (effectiveMethod !== method) {
|
|
3867
|
+
deps.logger?.("auth.method.fallback", { from: method, to: effectiveMethod, reason: "no-client-secret" });
|
|
3868
|
+
}
|
|
3792
3869
|
const controller = new AbortController();
|
|
3793
3870
|
const timer = setTimeout(() => controller.abort(), attemptTimeout(deps));
|
|
3794
3871
|
const attempt = { phase: "idle", controller, done: Promise.resolve() };
|
|
3795
3872
|
current = attempt;
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
notify
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3873
|
+
let started;
|
|
3874
|
+
try {
|
|
3875
|
+
started = authorization.begin({
|
|
3876
|
+
key: GITHUB_RECORD_KEY2,
|
|
3877
|
+
method: effectiveMethod,
|
|
3878
|
+
signal: controller.signal,
|
|
3879
|
+
// 浏览器往返靠 authorizeUrl + 回调;notify 只更新内存视图,prompt 不支持(拒绝)。
|
|
3880
|
+
interaction: {
|
|
3881
|
+
notify: (notice) => {
|
|
3882
|
+
if (notice.code !== void 0 && notice.url !== void 0) {
|
|
3883
|
+
updateCurrent({ phase: "waiting-device", verificationUri: notice.url, userCode: notice.code });
|
|
3884
|
+
} else if (notice.url !== void 0) {
|
|
3885
|
+
updateCurrent({ phase: "waiting-browser", authorizeUrl: notice.url });
|
|
3886
|
+
}
|
|
3887
|
+
},
|
|
3888
|
+
prompt: () => Promise.reject(new Error("interactive prompt unsupported in this surface"))
|
|
3889
|
+
}
|
|
3890
|
+
});
|
|
3891
|
+
} catch (error) {
|
|
3892
|
+
clearTimeout(timer);
|
|
3893
|
+
updateCurrent({ phase: "error", error: error instanceof Error ? error.message : String(error) });
|
|
3894
|
+
return attemptView();
|
|
3895
|
+
}
|
|
3896
|
+
attempt.done = started.then((outcome) => {
|
|
3812
3897
|
if (outcome.status === "cancelled") updateCurrent({ phase: "idle" });
|
|
3813
3898
|
}).catch((error) => {
|
|
3814
3899
|
updateCurrent({ phase: "error", error: error instanceof Error ? error.message : String(error) });
|
|
3815
3900
|
}).finally(() => clearTimeout(timer));
|
|
3816
|
-
await
|
|
3901
|
+
await waitForStart(attempt);
|
|
3817
3902
|
return attemptView();
|
|
3818
3903
|
}
|
|
3904
|
+
async function waitForStart(attempt, timeoutMs = START_TIMEOUT_MS) {
|
|
3905
|
+
const deadline = Date.now() + timeoutMs;
|
|
3906
|
+
while (attempt.phase === "idle" && Date.now() < deadline) {
|
|
3907
|
+
await new Promise((resolve) => setTimeout(resolve, START_POLL_MS));
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3819
3910
|
function cancelGithubSignIn(deps) {
|
|
3820
3911
|
const authorization = deps.get("authorization");
|
|
3821
3912
|
if (authorization !== void 0 && typeof authorization.cancel === "function") {
|
|
@@ -4392,11 +4483,16 @@ function isAuthorizationAvailable(get) {
|
|
|
4392
4483
|
const authorization = get("authorization");
|
|
4393
4484
|
return authorization !== void 0 && typeof authorization.begin === "function";
|
|
4394
4485
|
}
|
|
4486
|
+
function browserSignInAvailable(deps) {
|
|
4487
|
+
if (!isAuthorizationAvailable(deps.get)) return false;
|
|
4488
|
+
return ensureGithubFlow(githubAuthDeps(deps));
|
|
4489
|
+
}
|
|
4395
4490
|
function githubAuthDeps(deps) {
|
|
4396
4491
|
return {
|
|
4397
4492
|
get: deps.get,
|
|
4398
4493
|
...deps.fetchImpl === void 0 ? {} : { fetchImpl: deps.fetchImpl },
|
|
4399
4494
|
...deps.githubClientId === void 0 ? {} : { clientId: deps.githubClientId },
|
|
4495
|
+
...deps.githubClientSecret === void 0 ? {} : { clientSecret: deps.githubClientSecret },
|
|
4400
4496
|
...deps.logger === void 0 ? {} : { logger: deps.logger }
|
|
4401
4497
|
};
|
|
4402
4498
|
}
|
|
@@ -4586,8 +4682,10 @@ async function dispatch(method, pathname, url, body, deps) {
|
|
|
4586
4682
|
const method2 = body.method === "device" ? "device" : "browser";
|
|
4587
4683
|
try {
|
|
4588
4684
|
const view = await beginGithubSignIn(githubAuthDeps(deps), method2);
|
|
4685
|
+
if (view.phase === "error") throw new IpcError("GITHUB_AUTH_UNAVAILABLE", redact(view.error ?? "GitHub \u767B\u5F55\u5931\u8D25"));
|
|
4589
4686
|
return { attempt: view };
|
|
4590
4687
|
} catch (error) {
|
|
4688
|
+
if (error instanceof IpcError) throw error;
|
|
4591
4689
|
const message = error instanceof Error ? error.message : String(error);
|
|
4592
4690
|
if (message === "already-in-flight") {
|
|
4593
4691
|
return { attempt: attemptView(), alreadyInFlight: true };
|
|
@@ -4733,8 +4831,13 @@ async function statePayload(deps, settings) {
|
|
|
4733
4831
|
source: github.source,
|
|
4734
4832
|
/** 浏览器登录 attempt 视图(idle / waiting-browser / waiting-device / exchanging / done / error)。 */
|
|
4735
4833
|
attempt: attemptView(),
|
|
4736
|
-
/**
|
|
4737
|
-
browserAvailable:
|
|
4834
|
+
/** 浏览器登录是否可用:authorization 服务在、且 GitHub flow 真的注册上了(client_id 有内置默认)。 */
|
|
4835
|
+
browserAvailable: browserSignInAvailable(deps),
|
|
4836
|
+
/**
|
|
4837
|
+
* 回环 web flow 是否可用:GitHub 换 token 必须有 client_secret,内置 client_id 没有 → 只能设备码。
|
|
4838
|
+
* 前端据此决定主按钮启动哪个 method(false 时直接用 device,避免点了必然失败的 web flow)。
|
|
4839
|
+
*/
|
|
4840
|
+
loopbackAvailable: isLoopbackSignInAvailable(githubAuthDeps(deps))
|
|
4738
4841
|
}
|
|
4739
4842
|
};
|
|
4740
4843
|
}
|
|
@@ -5013,7 +5116,7 @@ export {
|
|
|
5013
5116
|
readPluginInfo,
|
|
5014
5117
|
plugin_info_exports,
|
|
5015
5118
|
github_exports,
|
|
5016
|
-
|
|
5119
|
+
ensureGithubFlow,
|
|
5017
5120
|
github_auth_exports,
|
|
5018
5121
|
import_cli_exports,
|
|
5019
5122
|
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: {
|
|
@@ -25,6 +27,7 @@ declare function apply(ctx: PluginContext, config?: {
|
|
|
25
27
|
stateDir?: string;
|
|
26
28
|
corePath?: string;
|
|
27
29
|
githubClientId?: string;
|
|
30
|
+
githubClientSecret?: string;
|
|
28
31
|
}): void;
|
|
29
32
|
declare const _default: {
|
|
30
33
|
name: string;
|
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-VO72OOVK.js";
|
|
13
13
|
|
|
14
14
|
// src/index.ts
|
|
15
15
|
var name = "dsh-github-trending";
|
|
@@ -20,6 +20,7 @@ function readVersion() {
|
|
|
20
20
|
var VERSION = readVersion();
|
|
21
21
|
function apply(ctx, config = {}) {
|
|
22
22
|
const env = process.env;
|
|
23
|
+
const githubClientSecret = (config.githubClientSecret ?? env.DSH_GITHUB_CLIENT_SECRET)?.trim();
|
|
23
24
|
const get = (key) => typeof ctx.get === "function" ? ctx.get(key) : void 0;
|
|
24
25
|
const log = (event, data) => {
|
|
25
26
|
const suffix = data === void 0 ? "" : ` ${JSON.stringify(data)}`;
|
|
@@ -64,11 +65,22 @@ function apply(ctx, config = {}) {
|
|
|
64
65
|
onError: (error) => ctx.logger?.warn?.(`[dsh-github-trending] \u5B9A\u65F6\u8FD0\u884C\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`)
|
|
65
66
|
});
|
|
66
67
|
const pluginInfo = readPluginInfo();
|
|
67
|
-
|
|
68
|
-
get,
|
|
68
|
+
const githubAuth = (resolve) => ({
|
|
69
|
+
get: resolve,
|
|
69
70
|
logger: log,
|
|
70
|
-
...config.githubClientId === void 0 ? {} : { clientId: config.githubClientId }
|
|
71
|
+
...config.githubClientId === void 0 ? {} : { clientId: config.githubClientId },
|
|
72
|
+
...githubClientSecret === void 0 || githubClientSecret === "" ? {} : { clientSecret: githubClientSecret }
|
|
71
73
|
});
|
|
74
|
+
const registerAuth = (resolve) => {
|
|
75
|
+
if (!ensureGithubFlow(githubAuth(resolve ?? get))) log("auth.flow.deferred", { reason: "authorization-not-ready" });
|
|
76
|
+
};
|
|
77
|
+
if (typeof ctx.inject === "function") {
|
|
78
|
+
ctx.inject(["authorization"], (scope) => {
|
|
79
|
+
registerAuth((name2) => typeof scope.get === "function" ? scope.get(name2) : void 0);
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
registerAuth();
|
|
83
|
+
}
|
|
72
84
|
const update = createUpdateService({
|
|
73
85
|
get,
|
|
74
86
|
env,
|
|
@@ -90,6 +102,7 @@ function apply(ctx, config = {}) {
|
|
|
90
102
|
pluginName: pluginInfo.name,
|
|
91
103
|
...pluginInfo.repository === void 0 ? {} : { repository: pluginInfo.repository },
|
|
92
104
|
...config.githubClientId === void 0 ? {} : { githubClientId: config.githubClientId },
|
|
105
|
+
...githubClientSecret === void 0 || githubClientSecret === "" ? {} : { githubClientSecret },
|
|
93
106
|
logger: log
|
|
94
107
|
});
|
|
95
108
|
const register = () => {
|
package/lib/internal.d.ts
CHANGED
|
@@ -1164,7 +1164,13 @@ declare const CALLBACK_PATH = "/api/morehao/github-trending/oauth/github/callbac
|
|
|
1164
1164
|
/**
|
|
1165
1165
|
* 内置的 GitHub OAuth App 公开 client_id(**非机密**:OAuth 的 client_id 设计上就是公开的,
|
|
1166
1166
|
* 会出现在授权 URL 里,与 `gh`/VS Code 内置自己的 client_id 同理)。
|
|
1167
|
-
*
|
|
1167
|
+
*
|
|
1168
|
+
* ⚠️ 只有 client_id 不够:GitHub 的 web application flow 在换 token 时**仍然要求
|
|
1169
|
+
* client_secret**(PKCE 是叠加保护,不能替代,2025-07 起官方也只说「推荐」而非「可免 secret」)。
|
|
1170
|
+
* 因此:
|
|
1171
|
+
* - 内置 client_id → 只能走 **device flow**(`resolveMethod` 自动降级),开箱即用;
|
|
1172
|
+
* - 用户自带 OAuth App(`githubClientId` + `githubClientSecret`)→ 才启用回环 web flow。
|
|
1173
|
+
* 且 device flow 需要在 OAuth App 设置里勾选启用(本插件内置的 App 已启用)。
|
|
1168
1174
|
*
|
|
1169
1175
|
* OAuth App 侧登记的回调:`http://127.0.0.1/api/morehao/github-trending/oauth/github/callback`
|
|
1170
1176
|
* (loopback 按 RFC 8252 忽略端口,运行时随机端口可正常回跳)。
|
|
@@ -1172,6 +1178,18 @@ declare const CALLBACK_PATH = "/api/morehao/github-trending/oauth/github/callbac
|
|
|
1172
1178
|
declare const DEFAULT_GITHUB_CLIENT_ID = "Ov23li7rhp0EKoqbkJKY";
|
|
1173
1179
|
/** 解析生效的 client_id:显式 config 优先,否则用内置默认。 */
|
|
1174
1180
|
declare function resolveClientId(deps: GithubAuthDeps): string | undefined;
|
|
1181
|
+
/** 解析生效的 client_secret(没有就返回 undefined,绝不以空串形式发出去)。 */
|
|
1182
|
+
declare function resolveClientSecret(deps: GithubAuthDeps): string | undefined;
|
|
1183
|
+
/**
|
|
1184
|
+
* 回环(浏览器 web flow)是否可用:GitHub 换 token 必须带 client_secret,
|
|
1185
|
+
* 内置的公开 client_id 没有 secret,只能退到设备码。见文件头的说明。
|
|
1186
|
+
*/
|
|
1187
|
+
declare function isLoopbackSignInAvailable(deps: GithubAuthDeps): boolean;
|
|
1188
|
+
/**
|
|
1189
|
+
* 实际要跑的方法:`browser` 需要 client_secret,缺省时自动降级为 `device`(开箱即用)。
|
|
1190
|
+
* 显式选择 `device` 的调用不受影响。
|
|
1191
|
+
*/
|
|
1192
|
+
declare function resolveMethod(deps: GithubAuthDeps, requested: 'browser' | 'device'): 'browser' | 'device';
|
|
1175
1193
|
/** 本插件运行需要 push / 建 PR,`read:user` 不够;一次授权到 `repo`。 */
|
|
1176
1194
|
declare const DEFAULT_SCOPE = "repo";
|
|
1177
1195
|
interface AuthorizationFlow {
|
|
@@ -1203,6 +1221,8 @@ interface AuthorizationLike {
|
|
|
1203
1221
|
status: 'authorized' | 'cancelled';
|
|
1204
1222
|
}>;
|
|
1205
1223
|
cancel?: (key: string) => void;
|
|
1224
|
+
/** 核心 0.2.x 有:按 key 查已注册的 flow;判定「真的注册上了」靠它。 */
|
|
1225
|
+
describe?: (key: string) => unknown;
|
|
1206
1226
|
}
|
|
1207
1227
|
interface WebServerLike {
|
|
1208
1228
|
register?: (route: {
|
|
@@ -1227,6 +1247,11 @@ interface GithubAuthDeps {
|
|
|
1227
1247
|
};
|
|
1228
1248
|
/** GitHub OAuth App 的 public client_id(无需 secret)。 */
|
|
1229
1249
|
clientId?: string;
|
|
1250
|
+
/**
|
|
1251
|
+
* GitHub OAuth App 的 client_secret。只有自带 OAuth App 的回环 web flow 才需要;
|
|
1252
|
+
* 缺省时 `browser` 方法自动降级为 device flow。不进 `settings.json`、不进日志。
|
|
1253
|
+
*/
|
|
1254
|
+
clientSecret?: string;
|
|
1230
1255
|
scope?: string;
|
|
1231
1256
|
attemptTimeoutMs?: number;
|
|
1232
1257
|
}
|
|
@@ -1264,14 +1289,35 @@ interface GithubGrantPayload {
|
|
|
1264
1289
|
tokenType?: string;
|
|
1265
1290
|
scope?: string;
|
|
1266
1291
|
}
|
|
1292
|
+
/** 这个 key 现在是否**真的**有 flow 在注册表里。 */
|
|
1293
|
+
declare function isGithubFlowRegistered(deps: GithubAuthDeps): boolean;
|
|
1294
|
+
/**
|
|
1295
|
+
* 幂等注册 GitHub flow;`authorization` 还没就绪时返回 false,调用方可稍后重试。
|
|
1296
|
+
*
|
|
1297
|
+
* 为什么不能只在 `apply()` 里试一次:宿主按「服务可用性」激活插件,本插件只
|
|
1298
|
+
* `inject: ['webServer']`,apply 完全可能早于 `authorization`——后者要等
|
|
1299
|
+
* `credentials` 的异步 `[Service.init]`(启动时读 `.credentials.yaml`)走完才
|
|
1300
|
+
* 可见,而 `ctx.get()` 对未 active 的 provider 返回 undefined。那一刻若放弃注册,
|
|
1301
|
+
* 就永久没有 flow;而可用性又是按请求现算的,于是出现「按钮亮着、点了必然
|
|
1302
|
+
* NO_FLOW、界面毫无反馈」。所以注册做成可重入的自愈入口:apply 用
|
|
1303
|
+
* `ctx.inject(['authorization'])` 等服务就绪,HTTP 面每次读状态再兜底一次。
|
|
1304
|
+
*
|
|
1305
|
+
* @returns 注册后是否可用(服务缺失 / 注册失败时为 false)。
|
|
1306
|
+
*/
|
|
1307
|
+
declare function ensureGithubFlow(deps: GithubAuthDeps): boolean;
|
|
1267
1308
|
/**
|
|
1268
1309
|
* 向 `ctx.authorization` 注册 GitHub flow(若核心没有该服务则降级 no-op)。
|
|
1269
|
-
*
|
|
1310
|
+
* 等价于 {@link ensureGithubFlow},保留旧名字给 `apply()` 与测试用。
|
|
1270
1311
|
*/
|
|
1271
1312
|
declare function registerGithubFlow(deps: GithubAuthDeps): void;
|
|
1272
1313
|
/**
|
|
1273
1314
|
* 启动一次浏览器登录 attempt(进程内 `authorization.begin`,不阻塞请求)。
|
|
1274
|
-
*
|
|
1315
|
+
*
|
|
1316
|
+
* 返回的视图保证**不是 `idle`**:要么已经进入 waiting-*,要么已经落成 `error`
|
|
1317
|
+
* (供路由转成 HTTP 错误)。`idle` 只可能来自「flow 没注册」这类抢跑,前端拿到
|
|
1318
|
+
* 它就会以为什么都没发生——那正是「点了没反应」的形态,所以这里把它堵掉。
|
|
1319
|
+
*
|
|
1320
|
+
* @throws 服务不可用、flow 未注册、已有 attempt 在飞时抛错。
|
|
1275
1321
|
*/
|
|
1276
1322
|
declare function beginGithubSignIn(deps: GithubAuthDeps, method?: 'browser' | 'device'): Promise<GithubAttemptView>;
|
|
1277
1323
|
/** 取消进行中的 attempt。 */
|
|
@@ -1296,12 +1342,17 @@ declare const githubAuth_beginGithubSignIn: typeof beginGithubSignIn;
|
|
|
1296
1342
|
declare const githubAuth_buildAuthorizeUrl: typeof buildAuthorizeUrl;
|
|
1297
1343
|
declare const githubAuth_cancelGithubSignIn: typeof cancelGithubSignIn;
|
|
1298
1344
|
declare const githubAuth_createPkce: typeof createPkce;
|
|
1345
|
+
declare const githubAuth_ensureGithubFlow: typeof ensureGithubFlow;
|
|
1346
|
+
declare const githubAuth_isGithubFlowRegistered: typeof isGithubFlowRegistered;
|
|
1347
|
+
declare const githubAuth_isLoopbackSignInAvailable: typeof isLoopbackSignInAvailable;
|
|
1299
1348
|
declare const githubAuth_registerGithubFlow: typeof registerGithubFlow;
|
|
1300
1349
|
declare const githubAuth_resetAttemptForTest: typeof resetAttemptForTest;
|
|
1301
1350
|
declare const githubAuth_resolveClientId: typeof resolveClientId;
|
|
1351
|
+
declare const githubAuth_resolveClientSecret: typeof resolveClientSecret;
|
|
1352
|
+
declare const githubAuth_resolveMethod: typeof resolveMethod;
|
|
1302
1353
|
declare const githubAuth_verifyState: typeof verifyState;
|
|
1303
1354
|
declare namespace githubAuth {
|
|
1304
|
-
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_registerGithubFlow as registerGithubFlow, githubAuth_resetAttemptForTest as resetAttemptForTest, githubAuth_resolveClientId as resolveClientId, githubAuth_verifyState as verifyState };
|
|
1355
|
+
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_isLoopbackSignInAvailable as isLoopbackSignInAvailable, githubAuth_registerGithubFlow as registerGithubFlow, githubAuth_resetAttemptForTest as resetAttemptForTest, githubAuth_resolveClientId as resolveClientId, githubAuth_resolveClientSecret as resolveClientSecret, githubAuth_resolveMethod as resolveMethod, githubAuth_verifyState as verifyState };
|
|
1305
1356
|
}
|
|
1306
1357
|
|
|
1307
1358
|
interface DefaultBranchResult {
|
|
@@ -1985,8 +2036,10 @@ interface RouteDeps {
|
|
|
1985
2036
|
/** 包名与仓库地址都来自 package.json,由 src/index.ts 传入。 */
|
|
1986
2037
|
pluginName?: string;
|
|
1987
2038
|
repository?: string;
|
|
1988
|
-
/** 浏览器 GitHub 登录用的 public client_id(由 apply 的 config
|
|
2039
|
+
/** 浏览器 GitHub 登录用的 public client_id(由 apply 的 config 传入;缺省时用内置默认)。 */
|
|
1989
2040
|
githubClientId?: string;
|
|
2041
|
+
/** 自带 OAuth App 时的 client_secret(只用于回环 web flow;缺省时该 method 自动降级为设备码)。 */
|
|
2042
|
+
githubClientSecret?: string;
|
|
1990
2043
|
logger?: (event: string, data?: Record<string, unknown>) => void;
|
|
1991
2044
|
}
|
|
1992
2045
|
declare function createRoutes(deps: RouteDeps): (request: http.IncomingMessage, response: http.ServerResponse) => Promise<void>;
|
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.3",
|
|
4
4
|
"description": "GitHub 趋势日报的 DeepSeek Harness 插件:按日 / 周 / 月周期定时 spawn 本地 trendradar 核心,抓取 Trending、生成中文简介与周期总结,并可选把订阅仓库的新 Release 合成中文摘要,一起提交到你的仓库、自动开 PR 合并;内置概览 / 榜单 / 运行记录 / 订阅 / 设置工作台。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|