@morehao/dsh-github-trending 0.8.5 → 0.8.6
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 +12 -0
- package/README.md +1 -1
- package/client.js +140 -90
- package/lib/{chunk-QOYTABJH.js → chunk-OMQY3T3V.js} +15 -267
- package/lib/index.d.ts +0 -1
- package/lib/index.js +2 -5
- package/lib/internal.d.ts +26 -96
- package/lib/internal.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v0.8.6] - 2026-11-02
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- **auth**: device-code sign-in modal with prominent code (#82)
|
|
8
|
+
|
|
9
|
+
### Refactors
|
|
10
|
+
|
|
11
|
+
- **auth**: drop loopback web flow, keep device flow only (#81)
|
|
12
|
+
|
|
13
|
+
**Full Changelog**: https://github.com/morehao/dsh-github-trending/compare/v0.8.5...v0.8.6
|
|
14
|
+
|
|
3
15
|
## [v0.8.5] - 2026-11-02
|
|
4
16
|
|
|
5
17
|
### Fixes
|
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ GitHub 趋势榜的 **DeepSeek Harness 插件**:按日 / 周 / 月周期定时
|
|
|
59
59
|
|
|
60
60
|
认证分区支持三种方式(可随时切换):
|
|
61
61
|
|
|
62
|
-
-
|
|
62
|
+
- **浏览器登录(推荐)**:点「用 GitHub 登录」在浏览器完成授权,无需手工粘贴 token,token 由 DSH 凭据记录安全保存、不落 `settings.json`。插件内置了公开的 OAuth App client_id,开箱即用。GitHub 的 web flow 换 token 必须带 client_secret(客户端不能内置机密),所以走**设备码**:点一下打开 GitHub 验证页,把页面上的验证码(设置页可一键复制)输入并确认即可。
|
|
63
63
|
- **本机 gh**:复用你终端里 `gh auth login` 的登录态。
|
|
64
64
|
- **PAT**:手工填 Personal Access Token(存到凭据引用 / 环境变量)。
|
|
65
65
|
|
package/client.js
CHANGED
|
@@ -127,19 +127,22 @@ var DICT = {
|
|
|
127
127
|
"auth.gh.hint": "使用本机 gh 登录态:终端执行 `gh auth login` 完成登录,再点「测试认证」。",
|
|
128
128
|
"auth.browser.hint": "在浏览器里完成 GitHub 授权,token 由 DSH 安全保存,无需手工粘贴。点一下会打开 GitHub 验证页,把下方验证码输入并确认即可。",
|
|
129
129
|
"auth.browser.signin": "用 GitHub 登录",
|
|
130
|
-
"auth.browser.waiting": "等待浏览器授权…",
|
|
131
|
-
"auth.browser.code": "验证码:",
|
|
132
130
|
"auth.browser.copyCode": "复制验证码",
|
|
133
131
|
"auth.browser.codeCopied": "验证码已复制",
|
|
134
132
|
"auth.browser.codeCopyFailed": "复制失败,请手动记下验证码",
|
|
135
133
|
"auth.browser.cancel": "取消",
|
|
136
134
|
"auth.browser.done": "已登录",
|
|
137
|
-
"auth.browser.device": "用设备码登录",
|
|
138
135
|
"auth.browser.unavailable": "宿主的 authorization 服务不可用(或 GitHub 授权流程没注册成功):请重试一次;仍不行就改用「本机 gh」或 PAT。",
|
|
139
|
-
"auth.
|
|
140
|
-
"auth.
|
|
141
|
-
"auth.
|
|
142
|
-
"auth.
|
|
136
|
+
"auth.modal.title": "用 GitHub 登录",
|
|
137
|
+
"auth.modal.lead": "在浏览器中打开 GitHub,输入下面的验证码完成授权。",
|
|
138
|
+
"auth.modal.openPage": "打开 GitHub 验证页",
|
|
139
|
+
"auth.modal.waiting": "等待授权中…",
|
|
140
|
+
"auth.modal.exchanging": "授权成功,正在换取令牌…",
|
|
141
|
+
"auth.modal.requesting": "正在向 GitHub 申请验证码…",
|
|
142
|
+
"auth.modal.retry": "重试",
|
|
143
|
+
"auth.modal.close": "关闭",
|
|
144
|
+
"auth.modal.closeHint": "关窗不取消,可随时回来查看。",
|
|
145
|
+
"auth.modal.viewCode": "查看验证码",
|
|
143
146
|
"auth.browser.failed": "登录失败",
|
|
144
147
|
"auth.browser.failed.detail": "登录失败:{message}",
|
|
145
148
|
"auth.browser.failed.hint": "请重试一次;仍失败可改用「本机 gh」或 PAT。",
|
|
@@ -675,19 +678,22 @@ var DICT = {
|
|
|
675
678
|
"auth.gh.hint": "Uses your local gh login: run `gh auth login` in a terminal, then click Test authentication.",
|
|
676
679
|
"auth.browser.hint": "Authorize GitHub in your browser; DSH stores the token securely, no manual paste. Clicking opens the GitHub verification page — enter the code below and confirm.",
|
|
677
680
|
"auth.browser.signin": "Sign in with GitHub",
|
|
678
|
-
"auth.browser.waiting": "Waiting for browser authorization…",
|
|
679
|
-
"auth.browser.code": "Verification code:",
|
|
680
681
|
"auth.browser.copyCode": "Copy code",
|
|
681
682
|
"auth.browser.codeCopied": "Code copied",
|
|
682
683
|
"auth.browser.codeCopyFailed": "Copy failed — please note the code manually",
|
|
683
684
|
"auth.browser.cancel": "Cancel",
|
|
684
685
|
"auth.browser.done": "Signed in",
|
|
685
|
-
"auth.browser.device": "Use a device code",
|
|
686
686
|
"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.",
|
|
687
|
-
"auth.
|
|
688
|
-
"auth.
|
|
689
|
-
"auth.
|
|
690
|
-
"auth.
|
|
687
|
+
"auth.modal.title": "Sign in with GitHub",
|
|
688
|
+
"auth.modal.lead": "Open GitHub in your browser and enter the code below to authorize.",
|
|
689
|
+
"auth.modal.openPage": "Open GitHub verification page",
|
|
690
|
+
"auth.modal.waiting": "Waiting for authorization…",
|
|
691
|
+
"auth.modal.exchanging": "Authorized — exchanging for token…",
|
|
692
|
+
"auth.modal.requesting": "Requesting a device code from GitHub…",
|
|
693
|
+
"auth.modal.retry": "Retry",
|
|
694
|
+
"auth.modal.close": "Close",
|
|
695
|
+
"auth.modal.closeHint": "Closing keeps the attempt running; reopen anytime.",
|
|
696
|
+
"auth.modal.viewCode": "View code",
|
|
691
697
|
"auth.browser.failed": "Sign-in failed",
|
|
692
698
|
"auth.browser.failed.detail": "Sign-in failed: {message}",
|
|
693
699
|
"auth.browser.failed.hint": "Retry once; if it keeps failing, use Local gh or a PAT instead.",
|
|
@@ -1238,6 +1244,20 @@ var CSS = `
|
|
|
1238
1244
|
.dshgt-modal-mask{position:fixed;inset:0;background:var(--dsw-alias-bg-mask-1,rgba(15,23,42,.38));display:flex;align-items:center;justify-content:center;z-index:2147483000;}
|
|
1239
1245
|
.dshgt-modal{background:var(--gt-bg);border-radius:12px;border:1px solid var(--gt-border);max-width:640px;width:calc(100% - 32px);max-height:80vh;overflow:auto;padding:16px;display:flex;flex-direction:column;gap:10px;box-shadow:0 20px 50px rgba(15,23,42,.25);}
|
|
1240
1246
|
|
|
1247
|
+
/* ——— GitHub 设备码登录弹窗 ——— */
|
|
1248
|
+
.dshgt-authmodal{max-width:420px;gap:14px;}
|
|
1249
|
+
.dshgt-authmodal-head{display:flex;align-items:center;justify-content:space-between;gap:8px;}
|
|
1250
|
+
.dshgt-authmodal-x{border:0;background:transparent;color:var(--gt-muted);font-size:14px;line-height:1;cursor:pointer;padding:4px 6px;border-radius:6px;}
|
|
1251
|
+
.dshgt-authmodal-x:hover{background:color-mix(in srgb,var(--gt-fg) 8%,transparent);color:var(--gt-fg);}
|
|
1252
|
+
.dshgt-authmodal-body{display:flex;flex-direction:column;gap:12px;}
|
|
1253
|
+
.dshgt-authcode{display:flex;align-items:center;justify-content:center;padding:16px 12px;border:1px dashed color-mix(in srgb,var(--gt-accent) 45%,var(--gt-border));border-radius:12px;background:color-mix(in srgb,var(--gt-accent) 6%,var(--gt-bg));}
|
|
1254
|
+
.dshgt-authcode-value{font-family:var(--gt-mono,ui-monospace,SFMono-Regular,Menlo,monospace);font-size:30px;font-weight:700;letter-spacing:6px;line-height:1.1;color:var(--gt-fg);user-select:all;}
|
|
1255
|
+
.dshgt-authcode-actions{display:flex;gap:8px;flex-wrap:wrap;justify-content:center;}
|
|
1256
|
+
.dshgt-authwait{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--gt-muted);}
|
|
1257
|
+
.dshgt-authwait-dot{width:8px;height:8px;border-radius:50%;background:var(--gt-accent);animation:dshgt-pulse 1.1s ease-in-out infinite;}
|
|
1258
|
+
@keyframes dshgt-pulse{0%,100%{opacity:.25;transform:scale(.8);}50%{opacity:1;transform:scale(1);}}
|
|
1259
|
+
.dshgt-authmodal-footer{display:flex;align-items:center;justify-content:space-between;gap:8px;border-top:1px solid var(--gt-border);padding-top:10px;}
|
|
1260
|
+
|
|
1241
1261
|
/* ——— 01 工作台外壳 ——— */
|
|
1242
1262
|
.dshgt-shell{gap:0;height:100%;min-height:0;overflow:hidden;}
|
|
1243
1263
|
.dshgt-app{flex:1;min-height:0;display:flex;flex-direction:column;background:var(--gt-bg);}
|
|
@@ -1891,8 +1911,8 @@ async function apiCall(method, path, body, options = {}) {
|
|
|
1891
1911
|
}
|
|
1892
1912
|
return json;
|
|
1893
1913
|
}
|
|
1894
|
-
async function beginGithubSignIn(
|
|
1895
|
-
const payload = await apiCall("POST", "/github/browser/begin", {
|
|
1914
|
+
async function beginGithubSignIn() {
|
|
1915
|
+
const payload = await apiCall("POST", "/github/browser/begin", {});
|
|
1896
1916
|
return payload.attempt;
|
|
1897
1917
|
}
|
|
1898
1918
|
async function getGithubSignInStatus(signal) {
|
|
@@ -2136,6 +2156,7 @@ function SettingsView(props) {
|
|
|
2136
2156
|
const [testing, setTesting] = import_react.default.useState(false);
|
|
2137
2157
|
const [githubTest, setGithubTest] = import_react.default.useState(null);
|
|
2138
2158
|
const [signIn, setSignIn] = import_react.default.useState({ phase: "idle" });
|
|
2159
|
+
const [signInModal, setSignInModal] = import_react.default.useState(false);
|
|
2139
2160
|
const anyDirty = SECTIONS.some((id) => dirtySections[id]);
|
|
2140
2161
|
const sectionEditable = section !== "source";
|
|
2141
2162
|
import_react.default.useEffect(() => {
|
|
@@ -2263,50 +2284,43 @@ function SettingsView(props) {
|
|
|
2263
2284
|
setTesting(false);
|
|
2264
2285
|
}
|
|
2265
2286
|
};
|
|
2266
|
-
const
|
|
2287
|
+
const openVerifyPage = (url) => {
|
|
2288
|
+
if (url === void 0) return;
|
|
2289
|
+
try {
|
|
2290
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
2291
|
+
} catch {
|
|
2292
|
+
}
|
|
2293
|
+
};
|
|
2294
|
+
const startSignIn = async () => {
|
|
2295
|
+
setSignInModal(true);
|
|
2296
|
+
setSignIn({ phase: "idle" });
|
|
2267
2297
|
try {
|
|
2268
|
-
let view = await beginGithubSignIn(
|
|
2298
|
+
let view = await beginGithubSignIn();
|
|
2269
2299
|
if (view.phase === "idle") view = await getGithubSignInStatus();
|
|
2270
2300
|
setSignIn(view);
|
|
2271
|
-
if (view.phase === "
|
|
2272
|
-
notify("err", t("auth.browser.failed.detail", { message: view.error ?? "" }));
|
|
2273
|
-
return;
|
|
2274
|
-
}
|
|
2275
|
-
if (view.phase === "waiting-browser" && view.authorizeUrl !== void 0) {
|
|
2276
|
-
try {
|
|
2277
|
-
window.open(view.authorizeUrl, "_blank", "noopener,noreferrer");
|
|
2278
|
-
} catch {
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2281
|
-
const deviceUrl = view.verificationUriComplete ?? view.verificationUri;
|
|
2282
|
-
if (view.phase === "waiting-device" && deviceUrl !== void 0) {
|
|
2283
|
-
try {
|
|
2284
|
-
window.open(deviceUrl, "_blank", "noopener,noreferrer");
|
|
2285
|
-
} catch {
|
|
2286
|
-
}
|
|
2287
|
-
}
|
|
2301
|
+
if (view.phase === "waiting-device") openVerifyPage(view.verificationUri);
|
|
2288
2302
|
} catch (error) {
|
|
2289
|
-
|
|
2303
|
+
setSignIn({ phase: "error", error: error instanceof Error ? error.message : String(error) });
|
|
2290
2304
|
}
|
|
2291
2305
|
};
|
|
2306
|
+
const openSignInModal = () => setSignInModal(true);
|
|
2307
|
+
const closeSignInModal = () => setSignInModal(false);
|
|
2292
2308
|
const cancelSignIn = async () => {
|
|
2293
2309
|
try {
|
|
2294
2310
|
setSignIn(await cancelGithubSignIn());
|
|
2295
2311
|
} catch (error) {
|
|
2296
2312
|
notify("err", error instanceof Error ? error.message : String(error));
|
|
2313
|
+
} finally {
|
|
2314
|
+
setSignInModal(false);
|
|
2297
2315
|
}
|
|
2298
2316
|
};
|
|
2299
|
-
const copyAuthorizeLink = () => {
|
|
2300
|
-
if (signIn.authorizeUrl === void 0) return;
|
|
2301
|
-
void navigator.clipboard?.writeText(signIn.authorizeUrl).then(() => notify("ok", t("auth.browser.copied")));
|
|
2302
|
-
};
|
|
2303
2317
|
const copyDeviceCode = () => {
|
|
2304
2318
|
if (signIn.userCode === void 0) return;
|
|
2305
2319
|
void copyText(signIn.userCode).then(
|
|
2306
2320
|
(ok) => notify(ok ? "ok" : "err", ok ? t("auth.browser.codeCopied") : t("auth.browser.codeCopyFailed"))
|
|
2307
2321
|
);
|
|
2308
2322
|
};
|
|
2309
|
-
const signInActive = signIn.phase === "waiting-
|
|
2323
|
+
const signInActive = signIn.phase === "waiting-device" || signIn.phase === "exchanging";
|
|
2310
2324
|
import_react.default.useEffect(() => {
|
|
2311
2325
|
if (!signInActive) return void 0;
|
|
2312
2326
|
let alive = true;
|
|
@@ -2367,7 +2381,6 @@ function SettingsView(props) {
|
|
|
2367
2381
|
const isGh = draft.github.authMode === "gh";
|
|
2368
2382
|
const isBrowser = draft.github.authMode === "browser";
|
|
2369
2383
|
const browserAvailable = state.github.browserAvailable === true;
|
|
2370
|
-
const loopbackAvailable = state.github.loopbackAvailable === true;
|
|
2371
2384
|
const sections = [
|
|
2372
2385
|
{ id: "source", label: t("st.source") },
|
|
2373
2386
|
{ id: "model", label: t("st.model") },
|
|
@@ -2562,13 +2575,10 @@ function SettingsView(props) {
|
|
|
2562
2575
|
{
|
|
2563
2576
|
small: true,
|
|
2564
2577
|
variant: "primary",
|
|
2565
|
-
onClick: () => void
|
|
2566
|
-
disabled: signInActive
|
|
2578
|
+
onClick: () => void (signInActive ? openSignInModal() : startSignIn())
|
|
2567
2579
|
},
|
|
2568
|
-
signIn.phase === "done" && signIn.login !== void 0 ? `${t("auth.browser.done")} · @${signIn.login}` :
|
|
2569
|
-
) : h("span", { className: "dshgt-hint" }, t("auth.browser.unavailable"))
|
|
2570
|
-
browserAvailable && loopbackAvailable ? h(Btn, { small: true, variant: "ghost", onClick: () => void startSignIn("device"), disabled: signInActive }, t("auth.browser.device")) : null,
|
|
2571
|
-
signInActive ? h(Btn, { small: true, variant: "ghost", onClick: () => void cancelSignIn() }, t("auth.browser.cancel")) : null
|
|
2580
|
+
signInActive ? t("auth.modal.viewCode") : signIn.phase === "done" && signIn.login !== void 0 ? `${t("auth.browser.done")} · @${signIn.login}` : t("auth.browser.signin")
|
|
2581
|
+
) : h("span", { className: "dshgt-hint" }, t("auth.browser.unavailable"))
|
|
2572
2582
|
) : h(
|
|
2573
2583
|
Row,
|
|
2574
2584
|
{ label: t("auth.tokenRef") },
|
|
@@ -2577,47 +2587,6 @@ function SettingsView(props) {
|
|
|
2577
2587
|
}) }),
|
|
2578
2588
|
h("span", { className: "dshgt-hint" }, t("auth.tokenRef.hint"))
|
|
2579
2589
|
),
|
|
2580
|
-
// 浏览器登录失败:必须显式说出来,否则「点了没反应」又回来了。
|
|
2581
|
-
isBrowser && signIn.phase === "error" ? h(Alert, { kind: "err", title: t("auth.browser.failed") }, signIn.error ?? t("auth.browser.failed.hint")) : null,
|
|
2582
|
-
// 浏览器登录进行中的状态行:可点的授权页链接 + 复制兜底 + 设备码。
|
|
2583
|
-
isBrowser && (signIn.phase === "waiting-browser" || signIn.phase === "waiting-device" || signIn.phase === "exchanging") ? h(
|
|
2584
|
-
"div",
|
|
2585
|
-
{ className: "dshgt-hint" },
|
|
2586
|
-
signIn.authorizeUrl !== void 0 ? h(
|
|
2587
|
-
"div",
|
|
2588
|
-
{ className: "dshgt-desc" },
|
|
2589
|
-
h("span", null, t("auth.browser.openHint")),
|
|
2590
|
-
" ",
|
|
2591
|
-
h(
|
|
2592
|
-
"a",
|
|
2593
|
-
{ className: "dshgt-link", href: signIn.authorizeUrl, target: "_blank", rel: "noreferrer" },
|
|
2594
|
-
t("auth.browser.open")
|
|
2595
|
-
),
|
|
2596
|
-
" ",
|
|
2597
|
-
h(Btn, { small: true, variant: "ghost", onClick: () => copyAuthorizeLink() }, t("auth.browser.copy"))
|
|
2598
|
-
) : null,
|
|
2599
|
-
signIn.verificationUriComplete !== void 0 || signIn.verificationUri !== void 0 ? h(
|
|
2600
|
-
"div",
|
|
2601
|
-
{ className: "dshgt-desc" },
|
|
2602
|
-
h(
|
|
2603
|
-
"a",
|
|
2604
|
-
{
|
|
2605
|
-
className: "dshgt-link",
|
|
2606
|
-
href: signIn.verificationUriComplete ?? signIn.verificationUri,
|
|
2607
|
-
target: "_blank",
|
|
2608
|
-
rel: "noreferrer"
|
|
2609
|
-
},
|
|
2610
|
-
t("auth.browser.open")
|
|
2611
|
-
)
|
|
2612
|
-
) : null,
|
|
2613
|
-
signIn.userCode !== void 0 ? h(
|
|
2614
|
-
"div",
|
|
2615
|
-
{ className: "dshgt-desc", style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" } },
|
|
2616
|
-
h("span", null, t("auth.browser.code")),
|
|
2617
|
-
h("span", { className: "dshgt-mono", style: { fontSize: "1.1em", fontWeight: "bold", letterSpacing: "1px" } }, signIn.userCode),
|
|
2618
|
-
h(Btn, { small: true, variant: "ghost", onClick: () => copyDeviceCode() }, t("auth.browser.copyCode"))
|
|
2619
|
-
) : null
|
|
2620
|
-
) : null,
|
|
2621
2590
|
h(
|
|
2622
2591
|
Row,
|
|
2623
2592
|
{ label: t("auth.test") },
|
|
@@ -2891,7 +2860,88 @@ function SettingsView(props) {
|
|
|
2891
2860
|
setDirtySections({ ...EMPTY_DIRTY });
|
|
2892
2861
|
setSavedSection(null);
|
|
2893
2862
|
void store.refresh({ quiet: true });
|
|
2894
|
-
} }) : null
|
|
2863
|
+
} }) : null,
|
|
2864
|
+
signInModal ? h(AuthSignInModal, {
|
|
2865
|
+
t,
|
|
2866
|
+
view: signIn,
|
|
2867
|
+
onClose: closeSignInModal,
|
|
2868
|
+
onCancel: () => void cancelSignIn(),
|
|
2869
|
+
onRetry: () => void startSignIn(),
|
|
2870
|
+
onCopyCode: copyDeviceCode,
|
|
2871
|
+
onOpenPage: () => openVerifyPage(signIn.verificationUri)
|
|
2872
|
+
}) : null
|
|
2873
|
+
);
|
|
2874
|
+
}
|
|
2875
|
+
function AuthSignInModal(props) {
|
|
2876
|
+
const { t, view } = props;
|
|
2877
|
+
const active = view.phase === "waiting-device" || view.phase === "exchanging";
|
|
2878
|
+
const body = view.phase === "waiting-device" || view.phase === "exchanging" ? h(
|
|
2879
|
+
"div",
|
|
2880
|
+
{ className: "dshgt-authmodal-body" },
|
|
2881
|
+
h("p", { className: "dshgt-hint", style: { margin: 0 } }, t("auth.modal.lead")),
|
|
2882
|
+
view.userCode !== void 0 ? h(
|
|
2883
|
+
"div",
|
|
2884
|
+
{ className: "dshgt-authcode" },
|
|
2885
|
+
h("span", { className: "dshgt-authcode-value" }, view.userCode)
|
|
2886
|
+
) : null,
|
|
2887
|
+
view.userCode !== void 0 ? h(
|
|
2888
|
+
"div",
|
|
2889
|
+
{ className: "dshgt-authcode-actions" },
|
|
2890
|
+
h(Btn, { small: true, variant: "ghost", onClick: props.onCopyCode }, t("auth.browser.copyCode")),
|
|
2891
|
+
view.verificationUri !== void 0 ? h(Btn, { small: true, variant: "primary", onClick: props.onOpenPage }, `${t("auth.modal.openPage")} ↗`) : null
|
|
2892
|
+
) : null,
|
|
2893
|
+
h(
|
|
2894
|
+
"div",
|
|
2895
|
+
{ className: "dshgt-authwait" },
|
|
2896
|
+
h("span", { className: "dshgt-authwait-dot" }),
|
|
2897
|
+
h("span", null, view.phase === "exchanging" ? t("auth.modal.exchanging") : t("auth.modal.waiting"))
|
|
2898
|
+
)
|
|
2899
|
+
) : view.phase === "done" ? h(
|
|
2900
|
+
"div",
|
|
2901
|
+
{ className: "dshgt-authmodal-body" },
|
|
2902
|
+
h(Alert, { kind: "ok" }, view.login === void 0 ? t("auth.browser.done") : `${t("auth.browser.done")} · @${view.login}`)
|
|
2903
|
+
) : view.phase === "error" ? h(
|
|
2904
|
+
"div",
|
|
2905
|
+
{ className: "dshgt-authmodal-body" },
|
|
2906
|
+
h(Alert, { kind: "err", title: t("auth.browser.failed") }, view.error ?? t("auth.browser.failed.hint"))
|
|
2907
|
+
) : h(
|
|
2908
|
+
"div",
|
|
2909
|
+
{ className: "dshgt-authmodal-body dshgt-authwait" },
|
|
2910
|
+
h("span", { className: "dshgt-authwait-dot" }),
|
|
2911
|
+
h("span", null, t("auth.modal.requesting"))
|
|
2912
|
+
);
|
|
2913
|
+
const footer = view.phase === "error" ? h(
|
|
2914
|
+
"div",
|
|
2915
|
+
{ className: "dshgt-authmodal-footer" },
|
|
2916
|
+
h(Btn, { small: true, variant: "primary", onClick: props.onRetry }, t("auth.modal.retry")),
|
|
2917
|
+
h(Btn, { small: true, variant: "ghost", onClick: props.onClose }, t("auth.modal.close"))
|
|
2918
|
+
) : view.phase === "done" ? h(
|
|
2919
|
+
"div",
|
|
2920
|
+
{ className: "dshgt-authmodal-footer" },
|
|
2921
|
+
h(Btn, { small: true, variant: "primary", onClick: props.onClose }, t("auth.modal.close"))
|
|
2922
|
+
) : h(
|
|
2923
|
+
"div",
|
|
2924
|
+
{ className: "dshgt-authmodal-footer" },
|
|
2925
|
+
h("span", { className: "dshgt-hint" }, t("auth.modal.closeHint")),
|
|
2926
|
+
h(Btn, { small: true, variant: "ghost", onClick: props.onCancel, disabled: !active }, t("auth.browser.cancel"))
|
|
2927
|
+
);
|
|
2928
|
+
return h(
|
|
2929
|
+
"div",
|
|
2930
|
+
{ className: "dshgt-modal-mask", onClick: (event) => {
|
|
2931
|
+
if (event.target === event.currentTarget) props.onClose();
|
|
2932
|
+
} },
|
|
2933
|
+
h(
|
|
2934
|
+
"div",
|
|
2935
|
+
{ className: "dshgt-modal dshgt-authmodal", role: "dialog", "aria-modal": "true", "aria-label": t("auth.modal.title") },
|
|
2936
|
+
h(
|
|
2937
|
+
"div",
|
|
2938
|
+
{ className: "dshgt-authmodal-head" },
|
|
2939
|
+
h("h3", { style: { margin: 0, fontSize: 14 } }, t("auth.modal.title")),
|
|
2940
|
+
h("button", { type: "button", className: "dshgt-authmodal-x", "aria-label": t("auth.modal.close"), onClick: props.onClose }, "✕")
|
|
2941
|
+
),
|
|
2942
|
+
body,
|
|
2943
|
+
footer
|
|
2944
|
+
)
|
|
2895
2945
|
);
|
|
2896
2946
|
}
|
|
2897
2947
|
function QuickFillModal(props) {
|
|
@@ -3529,48 +3529,26 @@ async function probeGithubAuth(options) {
|
|
|
3529
3529
|
// src/host/github-auth.ts
|
|
3530
3530
|
var github_auth_exports = {};
|
|
3531
3531
|
__export(github_auth_exports, {
|
|
3532
|
-
CALLBACK_PATH: () => CALLBACK_PATH,
|
|
3533
3532
|
DEFAULT_GITHUB_CLIENT_ID: () => DEFAULT_GITHUB_CLIENT_ID,
|
|
3534
3533
|
DEFAULT_SCOPE: () => DEFAULT_SCOPE,
|
|
3535
3534
|
GITHUB_RECORD_KEY: () => GITHUB_RECORD_KEY2,
|
|
3536
3535
|
attemptView: () => attemptView,
|
|
3537
3536
|
beginGithubSignIn: () => beginGithubSignIn,
|
|
3538
|
-
buildAuthorizeUrl: () => buildAuthorizeUrl,
|
|
3539
3537
|
cancelGithubSignIn: () => cancelGithubSignIn,
|
|
3540
|
-
createPkce: () => createPkce,
|
|
3541
3538
|
ensureGithubFlow: () => ensureGithubFlow,
|
|
3542
3539
|
isGithubFlowRegistered: () => isGithubFlowRegistered,
|
|
3543
|
-
isHttpUrl: () => isHttpUrl,
|
|
3544
|
-
isLoopbackSignInAvailable: () => isLoopbackSignInAvailable,
|
|
3545
3540
|
registerGithubFlow: () => registerGithubFlow,
|
|
3546
|
-
renderCallbackPage: () => renderCallbackPage,
|
|
3547
3541
|
resetAttemptForTest: () => resetAttemptForTest,
|
|
3548
|
-
resolveClientId: () => resolveClientId
|
|
3549
|
-
resolveClientSecret: () => resolveClientSecret,
|
|
3550
|
-
resolveMethod: () => resolveMethod,
|
|
3551
|
-
verifyState: () => verifyState
|
|
3542
|
+
resolveClientId: () => resolveClientId
|
|
3552
3543
|
});
|
|
3553
|
-
import { createHash as createHash3, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
3554
3544
|
var GITHUB_RECORD_KEY2 = "github/default";
|
|
3555
|
-
var CALLBACK_PATH = "/api/morehao/github-trending/oauth/github/callback";
|
|
3556
3545
|
var DEFAULT_GITHUB_CLIENT_ID = "Ov23li7rhp0EKoqbkJKY";
|
|
3557
3546
|
function resolveClientId(deps) {
|
|
3558
3547
|
const fromConfig = deps.clientId;
|
|
3559
3548
|
return fromConfig !== void 0 && fromConfig !== "" ? fromConfig : DEFAULT_GITHUB_CLIENT_ID;
|
|
3560
3549
|
}
|
|
3561
|
-
function resolveClientSecret(deps) {
|
|
3562
|
-
const secret = deps.clientSecret;
|
|
3563
|
-
return secret !== void 0 && secret !== "" ? secret : void 0;
|
|
3564
|
-
}
|
|
3565
|
-
function isLoopbackSignInAvailable(deps) {
|
|
3566
|
-
return resolveClientSecret(deps) !== void 0;
|
|
3567
|
-
}
|
|
3568
|
-
function resolveMethod(deps, requested) {
|
|
3569
|
-
return requested === "browser" && !isLoopbackSignInAvailable(deps) ? "device" : requested;
|
|
3570
|
-
}
|
|
3571
|
-
var AUTHORIZE_URL = "https://github.com/login/oauth/authorize";
|
|
3572
|
-
var ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
3573
3550
|
var DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
3551
|
+
var ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
3574
3552
|
var GITHUB_USER_URL = "https://api.github.com/user";
|
|
3575
3553
|
var DEFAULT_SCOPE = "repo";
|
|
3576
3554
|
var DEFAULT_ATTEMPT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
@@ -3580,105 +3558,6 @@ function attemptView() {
|
|
|
3580
3558
|
const { controller: _c, done: _d, ...view } = current;
|
|
3581
3559
|
return view;
|
|
3582
3560
|
}
|
|
3583
|
-
function base64url(buf) {
|
|
3584
|
-
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
3585
|
-
}
|
|
3586
|
-
function isHttpUrl(value) {
|
|
3587
|
-
if (typeof value !== "string" || value === "") return false;
|
|
3588
|
-
let url;
|
|
3589
|
-
try {
|
|
3590
|
-
url = new URL(value);
|
|
3591
|
-
} catch {
|
|
3592
|
-
return false;
|
|
3593
|
-
}
|
|
3594
|
-
return url.protocol === "https:" || url.protocol === "http:";
|
|
3595
|
-
}
|
|
3596
|
-
function createPkce() {
|
|
3597
|
-
const verifier = base64url(randomBytes2(32));
|
|
3598
|
-
const challenge = base64url(createHash3("sha256").update(verifier).digest());
|
|
3599
|
-
return { verifier, challenge };
|
|
3600
|
-
}
|
|
3601
|
-
function verifyState(received, expected) {
|
|
3602
|
-
const a = Buffer.from(received);
|
|
3603
|
-
const b = Buffer.from(expected);
|
|
3604
|
-
return a.length === b.length && timingSafeEqual2(a, b);
|
|
3605
|
-
}
|
|
3606
|
-
function renderCallbackPage(kind) {
|
|
3607
|
-
const ok = kind === "success";
|
|
3608
|
-
const title = ok ? "\u767B\u5F55\u5B8C\u6210" : "\u767B\u5F55\u672A\u5B8C\u6210";
|
|
3609
|
-
const heading = ok ? "GitHub \u6388\u6743\u6210\u529F" : "GitHub \u6388\u6743\u5931\u8D25";
|
|
3610
|
-
const body = ok ? "\u5DF2\u5B8C\u6210\u767B\u5F55\uFF0C\u53EF\u4EE5\u5173\u95ED\u6B64\u9875\u9762\u3002" : "\u6388\u6743\u6821\u9A8C\u672A\u901A\u8FC7\uFF0C\u8BF7\u56DE\u5230 DSH \u91CD\u65B0\u53D1\u8D77\u767B\u5F55\u3002";
|
|
3611
|
-
const accent = ok ? "#1f883d" : "#cf222e";
|
|
3612
|
-
const icon = ok ? '<path d="M20 6 9 17l-5-5"/>' : '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>';
|
|
3613
|
-
const autoClose = ok ? "<script>(function(){try{setTimeout(function(){window.close()},1200)}catch(e){}})()</script>" : "";
|
|
3614
|
-
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8">
|
|
3615
|
-
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
3616
|
-
<meta name="color-scheme" content="light dark">
|
|
3617
|
-
<title>${title}</title>
|
|
3618
|
-
<style>
|
|
3619
|
-
:root{--bg:#f6f8fa;--card:#ffffff;--fg:#1f2328;--muted:#59636e;--border:#d1d9e0;--accent:${accent}}
|
|
3620
|
-
@media (prefers-color-scheme:dark){:root{--bg:#0d1117;--card:#151b23;--fg:#e6edf3;--muted:#9198a1;--border:#3d444d}}
|
|
3621
|
-
*{box-sizing:border-box}
|
|
3622
|
-
html,body{height:100%}
|
|
3623
|
-
body{margin:0;display:flex;align-items:center;justify-content:center;padding:24px;
|
|
3624
|
-
background:var(--bg);color:var(--fg);
|
|
3625
|
-
font:400 15px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif}
|
|
3626
|
-
main{width:100%;max-width:400px;background:var(--card);border:1px solid var(--border);
|
|
3627
|
-
border-radius:14px;padding:40px 32px;text-align:center;
|
|
3628
|
-
box-shadow:0 8px 24px rgba(0,0,0,.06)}
|
|
3629
|
-
.mark{width:64px;height:64px;margin:0 auto 20px;border-radius:50%;display:flex;
|
|
3630
|
-
align-items:center;justify-content:center;background:color-mix(in srgb,var(--accent) 14%,transparent);color:var(--accent)}
|
|
3631
|
-
.mark svg{width:30px;height:30px;fill:none;stroke:currentColor;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
|
|
3632
|
-
h1{margin:0 0 8px;font-size:19px;font-weight:600;letter-spacing:.2px}
|
|
3633
|
-
p{margin:0;color:var(--muted);font-size:14px}
|
|
3634
|
-
</style></head>
|
|
3635
|
-
<body><main role="status">
|
|
3636
|
-
<div class="mark"><svg viewBox="0 0 24 24" aria-hidden="true">${icon}</svg></div>
|
|
3637
|
-
<h1>${heading}</h1>
|
|
3638
|
-
<p>${body}</p>
|
|
3639
|
-
</main>${autoClose}</body></html>`;
|
|
3640
|
-
}
|
|
3641
|
-
function buildAuthorizeUrl(params) {
|
|
3642
|
-
const url = new URL(params.authorizeEndpoint);
|
|
3643
|
-
url.searchParams.set("client_id", params.clientId);
|
|
3644
|
-
url.searchParams.set("redirect_uri", params.redirectUri);
|
|
3645
|
-
url.searchParams.set("scope", params.scope);
|
|
3646
|
-
url.searchParams.set("state", params.state);
|
|
3647
|
-
url.searchParams.set("code_challenge", params.challenge);
|
|
3648
|
-
url.searchParams.set("code_challenge_method", "S256");
|
|
3649
|
-
return url.toString();
|
|
3650
|
-
}
|
|
3651
|
-
async function exchangeCode(fetchImpl, endpoint, params, signal) {
|
|
3652
|
-
const form = new URLSearchParams({
|
|
3653
|
-
client_id: params.clientId,
|
|
3654
|
-
code: params.code,
|
|
3655
|
-
code_verifier: params.verifier,
|
|
3656
|
-
redirect_uri: params.redirectUri
|
|
3657
|
-
});
|
|
3658
|
-
if (params.clientSecret !== void 0) form.set("client_secret", params.clientSecret);
|
|
3659
|
-
const response = await fetchImpl(endpoint, {
|
|
3660
|
-
method: "POST",
|
|
3661
|
-
headers: { accept: "application/json", "content-type": "application/x-www-form-urlencoded" },
|
|
3662
|
-
body: form.toString(),
|
|
3663
|
-
signal
|
|
3664
|
-
});
|
|
3665
|
-
if (!response.ok) throw new Error(`GitHub access_token HTTP ${response.status}`);
|
|
3666
|
-
const body = await response.json();
|
|
3667
|
-
if (typeof body.access_token !== "string" || body.access_token === "") {
|
|
3668
|
-
const error = typeof body.error === "string" ? body.error : void 0;
|
|
3669
|
-
if (error === "incorrect_client_credentials") {
|
|
3670
|
-
throw new Error(
|
|
3671
|
-
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"
|
|
3672
|
-
);
|
|
3673
|
-
}
|
|
3674
|
-
throw new Error(`GitHub access_token \u672A\u8FD4\u56DE token${error === void 0 ? "" : `\uFF08${error}\uFF09`}`);
|
|
3675
|
-
}
|
|
3676
|
-
return {
|
|
3677
|
-
token: body.access_token,
|
|
3678
|
-
...typeof body.token_type === "string" ? { tokenType: body.token_type } : {},
|
|
3679
|
-
...typeof body.scope === "string" ? { scope: body.scope } : {}
|
|
3680
|
-
};
|
|
3681
|
-
}
|
|
3682
3561
|
async function fetchLogin(fetchImpl, endpoint, token, signal) {
|
|
3683
3562
|
try {
|
|
3684
3563
|
const response = await fetchImpl(endpoint, {
|
|
@@ -3692,87 +3571,6 @@ async function fetchLogin(fetchImpl, endpoint, token, signal) {
|
|
|
3692
3571
|
return void 0;
|
|
3693
3572
|
}
|
|
3694
3573
|
}
|
|
3695
|
-
async function runBrowser(deps, session) {
|
|
3696
|
-
const webServer = deps.get("webServer");
|
|
3697
|
-
if (webServer === void 0 || typeof webServer.register !== "function") {
|
|
3698
|
-
throw new Error("webServer \u670D\u52A1\u4E0D\u53EF\u7528\uFF1A\u6D4F\u89C8\u5668\u767B\u5F55\u9700\u8981\u5B83\u627F\u8F7D OAuth \u56DE\u8C03");
|
|
3699
|
-
}
|
|
3700
|
-
const clientId = resolveClientId(deps);
|
|
3701
|
-
if (clientId === void 0 || clientId === "") throw new Error("\u7F3A\u5C11 GitHub OAuth client_id");
|
|
3702
|
-
const clientSecret = resolveClientSecret(deps);
|
|
3703
|
-
if (clientSecret === void 0) {
|
|
3704
|
-
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");
|
|
3705
|
-
}
|
|
3706
|
-
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
3707
|
-
const endpoints = normalizeEndpoints(deps.endpoints);
|
|
3708
|
-
const scope = deps.scope ?? DEFAULT_SCOPE;
|
|
3709
|
-
const state = base64url(randomBytes2(32));
|
|
3710
|
-
const { verifier, challenge } = createPkce();
|
|
3711
|
-
let resolveCode;
|
|
3712
|
-
let rejectCode;
|
|
3713
|
-
const codePromise = new Promise((resolve, reject) => {
|
|
3714
|
-
resolveCode = resolve;
|
|
3715
|
-
rejectCode = reject;
|
|
3716
|
-
});
|
|
3717
|
-
codePromise.catch(() => void 0);
|
|
3718
|
-
const dispose = webServer.register({
|
|
3719
|
-
kind: "exact",
|
|
3720
|
-
path: CALLBACK_PATH,
|
|
3721
|
-
handler: (req, res) => {
|
|
3722
|
-
let url;
|
|
3723
|
-
try {
|
|
3724
|
-
url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
3725
|
-
} catch {
|
|
3726
|
-
res.writeHead(400, { "cache-control": "no-store" }).end();
|
|
3727
|
-
return;
|
|
3728
|
-
}
|
|
3729
|
-
const code = url.searchParams.get("code") ?? "";
|
|
3730
|
-
const gotState = url.searchParams.get("state") ?? "";
|
|
3731
|
-
const single = url.searchParams.getAll("state").length === 1 && url.searchParams.getAll("code").length === 1;
|
|
3732
|
-
const bad = req.method !== "GET" || url.pathname !== CALLBACK_PATH || !single || code === "" || !verifyState(gotState, state);
|
|
3733
|
-
if (bad) {
|
|
3734
|
-
res.writeHead(400, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" }).end(renderCallbackPage("error"));
|
|
3735
|
-
return;
|
|
3736
|
-
}
|
|
3737
|
-
res.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" }).end(renderCallbackPage("success"));
|
|
3738
|
-
resolveCode(code);
|
|
3739
|
-
}
|
|
3740
|
-
});
|
|
3741
|
-
try {
|
|
3742
|
-
const origin = localOrigin(webServer);
|
|
3743
|
-
const redirectUri = `${origin}${CALLBACK_PATH}`;
|
|
3744
|
-
const authorizeUrl = buildAuthorizeUrl({
|
|
3745
|
-
authorizeEndpoint: endpoints.authorize,
|
|
3746
|
-
clientId,
|
|
3747
|
-
redirectUri,
|
|
3748
|
-
scope,
|
|
3749
|
-
state,
|
|
3750
|
-
challenge
|
|
3751
|
-
});
|
|
3752
|
-
updateCurrent({ phase: "waiting-browser", authorizeUrl, expiresAt: Date.now() + attemptTimeout(deps) });
|
|
3753
|
-
session.notify?.({ message: "Continue in your browser to sign in to GitHub.", url: authorizeUrl });
|
|
3754
|
-
const code = await withAbort(codePromise, session.signal, () => rejectCode(new Error("authorization cancelled")));
|
|
3755
|
-
updateCurrent({ phase: "exchanging" });
|
|
3756
|
-
const exchanged = await exchangeCode(
|
|
3757
|
-
fetchImpl,
|
|
3758
|
-
endpoints.accessToken,
|
|
3759
|
-
{ clientId, clientSecret, code, verifier, redirectUri },
|
|
3760
|
-
session.signal
|
|
3761
|
-
);
|
|
3762
|
-
const login = await fetchLogin(fetchImpl, endpoints.user, exchanged.token, session.signal);
|
|
3763
|
-
const payload = {
|
|
3764
|
-
version: 1,
|
|
3765
|
-
token: exchanged.token,
|
|
3766
|
-
...login === void 0 ? {} : { login },
|
|
3767
|
-
tokenType: exchanged.tokenType ?? "bearer",
|
|
3768
|
-
scope: exchanged.scope ?? scope
|
|
3769
|
-
};
|
|
3770
|
-
if (typeof session.commit === "function") await session.commit({ kind: "grant", payload });
|
|
3771
|
-
updateCurrent({ phase: "done", ...login === void 0 ? {} : { login } });
|
|
3772
|
-
} finally {
|
|
3773
|
-
dispose();
|
|
3774
|
-
}
|
|
3775
|
-
}
|
|
3776
3574
|
async function runDevice(deps, session) {
|
|
3777
3575
|
const clientId = resolveClientId(deps);
|
|
3778
3576
|
if (clientId === void 0 || clientId === "") throw new Error("\u7F3A\u5C11 GitHub OAuth client_id");
|
|
@@ -3797,17 +3595,15 @@ async function runDevice(deps, session) {
|
|
|
3797
3595
|
const intervalSec = typeof start.interval === "number" && start.interval > 0 ? start.interval : 5;
|
|
3798
3596
|
const expiresInSec = typeof start.expires_in === "number" && start.expires_in > 0 ? start.expires_in : 900;
|
|
3799
3597
|
const deadline = Date.now() + expiresInSec * 1e3;
|
|
3800
|
-
const verificationUriComplete = isHttpUrl(start.verification_uri_complete) ? start.verification_uri_complete : void 0;
|
|
3801
3598
|
updateCurrent({
|
|
3802
3599
|
phase: "waiting-device",
|
|
3803
3600
|
verificationUri: start.verification_uri,
|
|
3804
|
-
...verificationUriComplete === void 0 ? {} : { verificationUriComplete },
|
|
3805
3601
|
userCode: start.user_code,
|
|
3806
3602
|
expiresAt: deadline
|
|
3807
3603
|
});
|
|
3808
3604
|
session.notify?.({
|
|
3809
3605
|
message: "Enter this code on the verification page to finish signing in.",
|
|
3810
|
-
url:
|
|
3606
|
+
url: start.verification_uri,
|
|
3811
3607
|
code: start.user_code
|
|
3812
3608
|
});
|
|
3813
3609
|
let waitMs = intervalSec * 1e3;
|
|
@@ -3852,11 +3648,8 @@ function flowDefinition(deps) {
|
|
|
3852
3648
|
return {
|
|
3853
3649
|
key: GITHUB_RECORD_KEY2,
|
|
3854
3650
|
label: "GitHub",
|
|
3855
|
-
methods: [
|
|
3856
|
-
|
|
3857
|
-
{ id: "device", label: "Use a device code" }
|
|
3858
|
-
],
|
|
3859
|
-
run: (session) => session.method === "device" ? runDevice(deps, session) : runBrowser(deps, session)
|
|
3651
|
+
methods: [{ id: "device", label: "Use a device code" }],
|
|
3652
|
+
run: (session) => runDevice(deps, session)
|
|
3860
3653
|
};
|
|
3861
3654
|
}
|
|
3862
3655
|
var registeredOn = /* @__PURE__ */ new WeakSet();
|
|
@@ -3900,21 +3693,17 @@ function registerGithubFlow(deps) {
|
|
|
3900
3693
|
}
|
|
3901
3694
|
var START_TIMEOUT_MS = 1500;
|
|
3902
3695
|
var START_POLL_MS = 10;
|
|
3903
|
-
async function beginGithubSignIn(deps
|
|
3696
|
+
async function beginGithubSignIn(deps) {
|
|
3904
3697
|
const authorization = deps.get("authorization");
|
|
3905
3698
|
if (authorization === void 0 || typeof authorization.begin !== "function") {
|
|
3906
|
-
throw new Error("authorization \u670D\u52A1\u4E0D\u53EF\u7528\uFF1A\u65E0\u6CD5\u8FDB\u884C\
|
|
3699
|
+
throw new Error("authorization \u670D\u52A1\u4E0D\u53EF\u7528\uFF1A\u65E0\u6CD5\u8FDB\u884C GitHub \u767B\u5F55");
|
|
3907
3700
|
}
|
|
3908
3701
|
if (!ensureGithubFlow(deps)) {
|
|
3909
3702
|
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");
|
|
3910
3703
|
}
|
|
3911
|
-
if (current !== void 0 && (current.phase === "waiting-
|
|
3704
|
+
if (current !== void 0 && (current.phase === "waiting-device" || current.phase === "exchanging")) {
|
|
3912
3705
|
throw new Error("already-in-flight");
|
|
3913
3706
|
}
|
|
3914
|
-
const effectiveMethod = resolveMethod(deps, method);
|
|
3915
|
-
if (effectiveMethod !== method) {
|
|
3916
|
-
deps.logger?.("auth.method.fallback", { from: method, to: effectiveMethod, reason: "no-client-secret" });
|
|
3917
|
-
}
|
|
3918
3707
|
const controller = new AbortController();
|
|
3919
3708
|
const timer = setTimeout(() => controller.abort(), attemptTimeout(deps));
|
|
3920
3709
|
const attempt = { phase: "idle", controller, done: Promise.resolve() };
|
|
@@ -3923,15 +3712,13 @@ async function beginGithubSignIn(deps, method = "browser") {
|
|
|
3923
3712
|
try {
|
|
3924
3713
|
started = authorization.begin({
|
|
3925
3714
|
key: GITHUB_RECORD_KEY2,
|
|
3926
|
-
method:
|
|
3715
|
+
method: "device",
|
|
3927
3716
|
signal: controller.signal,
|
|
3928
|
-
//
|
|
3717
|
+
// 设备码:notify 只更新内存视图;prompt 不支持(拒绝)。
|
|
3929
3718
|
interaction: {
|
|
3930
3719
|
notify: (notice) => {
|
|
3931
|
-
if (notice.code !== void 0
|
|
3720
|
+
if (notice.code !== void 0) {
|
|
3932
3721
|
updateCurrent({ phase: "waiting-device", userCode: notice.code });
|
|
3933
|
-
} else if (notice.url !== void 0) {
|
|
3934
|
-
updateCurrent({ phase: "waiting-browser", authorizeUrl: notice.url });
|
|
3935
3722
|
}
|
|
3936
3723
|
},
|
|
3937
3724
|
prompt: () => Promise.reject(new Error("interactive prompt unsupported in this surface"))
|
|
@@ -3968,7 +3755,6 @@ function resetAttemptForTest() {
|
|
|
3968
3755
|
}
|
|
3969
3756
|
function normalizeEndpoints(endpoints = {}) {
|
|
3970
3757
|
return {
|
|
3971
|
-
authorize: endpoints?.authorize ?? AUTHORIZE_URL,
|
|
3972
3758
|
accessToken: endpoints?.accessToken ?? ACCESS_TOKEN_URL,
|
|
3973
3759
|
deviceCode: endpoints?.deviceCode ?? DEVICE_CODE_URL,
|
|
3974
3760
|
user: endpoints?.user ?? GITHUB_USER_URL
|
|
@@ -3977,37 +3763,6 @@ function normalizeEndpoints(endpoints = {}) {
|
|
|
3977
3763
|
function attemptTimeout(deps) {
|
|
3978
3764
|
return typeof deps.attemptTimeoutMs === "number" && deps.attemptTimeoutMs > 0 ? deps.attemptTimeoutMs : DEFAULT_ATTEMPT_TIMEOUT_MS;
|
|
3979
3765
|
}
|
|
3980
|
-
function localOrigin(webServer) {
|
|
3981
|
-
const port = webServer.port;
|
|
3982
|
-
if (typeof port !== "number" || port <= 0) throw new Error("webServer \u5C1A\u672A\u76D1\u542C\uFF0C\u65E0\u6CD5\u786E\u5B9A\u56DE\u8C03\u5730\u5740");
|
|
3983
|
-
const host = typeof webServer.host === "string" && webServer.host !== "" ? webServer.host : "127.0.0.1";
|
|
3984
|
-
const loopback = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
|
|
3985
|
-
return `http://${loopback}:${port}`;
|
|
3986
|
-
}
|
|
3987
|
-
function withAbort(promise, signal, onAbort) {
|
|
3988
|
-
if (signal === void 0) return promise;
|
|
3989
|
-
if (signal.aborted) {
|
|
3990
|
-
onAbort();
|
|
3991
|
-
return Promise.reject(new Error("authorization cancelled"));
|
|
3992
|
-
}
|
|
3993
|
-
return new Promise((resolve, reject) => {
|
|
3994
|
-
const abort = () => {
|
|
3995
|
-
onAbort();
|
|
3996
|
-
reject(new Error("authorization cancelled"));
|
|
3997
|
-
};
|
|
3998
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
3999
|
-
promise.then(
|
|
4000
|
-
(value) => {
|
|
4001
|
-
signal.removeEventListener("abort", abort);
|
|
4002
|
-
resolve(value);
|
|
4003
|
-
},
|
|
4004
|
-
(error) => {
|
|
4005
|
-
signal.removeEventListener("abort", abort);
|
|
4006
|
-
reject(error);
|
|
4007
|
-
}
|
|
4008
|
-
);
|
|
4009
|
-
});
|
|
4010
|
-
}
|
|
4011
3766
|
function sleep(ms, signal) {
|
|
4012
3767
|
return new Promise((resolve, reject) => {
|
|
4013
3768
|
if (signal?.aborted === true) {
|
|
@@ -4541,7 +4296,6 @@ function githubAuthDeps(deps) {
|
|
|
4541
4296
|
get: deps.get,
|
|
4542
4297
|
...deps.fetchImpl === void 0 ? {} : { fetchImpl: deps.fetchImpl },
|
|
4543
4298
|
...deps.githubClientId === void 0 ? {} : { clientId: deps.githubClientId },
|
|
4544
|
-
...deps.githubClientSecret === void 0 ? {} : { clientSecret: deps.githubClientSecret },
|
|
4545
4299
|
...deps.logger === void 0 ? {} : { logger: deps.logger }
|
|
4546
4300
|
};
|
|
4547
4301
|
}
|
|
@@ -4728,9 +4482,8 @@ async function dispatch(method, pathname, url, body, deps) {
|
|
|
4728
4482
|
return { github: probe };
|
|
4729
4483
|
}
|
|
4730
4484
|
if (method === "POST" && pathname === "/github/browser/begin") {
|
|
4731
|
-
const method2 = body.method === "device" ? "device" : "browser";
|
|
4732
4485
|
try {
|
|
4733
|
-
const view = await beginGithubSignIn(githubAuthDeps(deps)
|
|
4486
|
+
const view = await beginGithubSignIn(githubAuthDeps(deps));
|
|
4734
4487
|
if (view.phase === "error") throw new IpcError("GITHUB_AUTH_UNAVAILABLE", redact(view.error ?? "GitHub \u767B\u5F55\u5931\u8D25"));
|
|
4735
4488
|
return { attempt: view };
|
|
4736
4489
|
} catch (error) {
|
|
@@ -4878,15 +4631,10 @@ async function statePayload(deps, settings) {
|
|
|
4878
4631
|
authMode: settings.github.authMode,
|
|
4879
4632
|
configured: github.token !== void 0,
|
|
4880
4633
|
source: github.source,
|
|
4881
|
-
/**
|
|
4634
|
+
/** GitHub 登录 attempt 视图(idle / waiting-device / exchanging / done / error)。 */
|
|
4882
4635
|
attempt: attemptView(),
|
|
4883
|
-
/**
|
|
4884
|
-
browserAvailable: browserSignInAvailable(deps)
|
|
4885
|
-
/**
|
|
4886
|
-
* 回环 web flow 是否可用:GitHub 换 token 必须有 client_secret,内置 client_id 没有 → 只能设备码。
|
|
4887
|
-
* 前端据此决定主按钮启动哪个 method(false 时直接用 device,避免点了必然失败的 web flow)。
|
|
4888
|
-
*/
|
|
4889
|
-
loopbackAvailable: isLoopbackSignInAvailable(githubAuthDeps(deps))
|
|
4636
|
+
/** 登录是否可用:authorization 服务在、且 GitHub flow 真的注册上了(client_id 有内置默认)。 */
|
|
4637
|
+
browserAvailable: browserSignInAvailable(deps)
|
|
4890
4638
|
}
|
|
4891
4639
|
};
|
|
4892
4640
|
}
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
createUpdateService,
|
|
10
10
|
ensureGithubFlow,
|
|
11
11
|
readPluginInfo
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OMQY3T3V.js";
|
|
13
13
|
|
|
14
14
|
// src/index.ts
|
|
15
15
|
var name = "dsh-github-trending";
|
|
@@ -20,7 +20,6 @@ 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();
|
|
24
23
|
const get = (key) => typeof ctx.get === "function" ? ctx.get(key) : void 0;
|
|
25
24
|
const log = (event, data) => {
|
|
26
25
|
const suffix = data === void 0 ? "" : ` ${JSON.stringify(data)}`;
|
|
@@ -68,8 +67,7 @@ function apply(ctx, config = {}) {
|
|
|
68
67
|
const githubAuth = (resolve) => ({
|
|
69
68
|
get: resolve,
|
|
70
69
|
logger: log,
|
|
71
|
-
...config.githubClientId === void 0 ? {} : { clientId: config.githubClientId }
|
|
72
|
-
...githubClientSecret === void 0 || githubClientSecret === "" ? {} : { clientSecret: githubClientSecret }
|
|
70
|
+
...config.githubClientId === void 0 ? {} : { clientId: config.githubClientId }
|
|
73
71
|
});
|
|
74
72
|
const registerAuth = (resolve) => {
|
|
75
73
|
if (!ensureGithubFlow(githubAuth(resolve ?? get))) log("auth.flow.deferred", { reason: "authorization-not-ready" });
|
|
@@ -102,7 +100,6 @@ function apply(ctx, config = {}) {
|
|
|
102
100
|
pluginName: pluginInfo.name,
|
|
103
101
|
...pluginInfo.repository === void 0 ? {} : { repository: pluginInfo.repository },
|
|
104
102
|
...config.githubClientId === void 0 ? {} : { githubClientId: config.githubClientId },
|
|
105
|
-
...githubClientSecret === void 0 || githubClientSecret === "" ? {} : { githubClientSecret },
|
|
106
103
|
logger: log
|
|
107
104
|
});
|
|
108
105
|
const register = () => {
|
package/lib/internal.d.ts
CHANGED
|
@@ -1157,39 +1157,36 @@ declare namespace github {
|
|
|
1157
1157
|
export { type github_GithubProbeResult as GithubProbeResult, github_probeGithubAuth as probeGithubAuth };
|
|
1158
1158
|
}
|
|
1159
1159
|
|
|
1160
|
+
/**
|
|
1161
|
+
* GitHub 浏览器登录(薄 adapter)。
|
|
1162
|
+
*
|
|
1163
|
+
* 设计原则:**只补核心缺的那一层,其余全用核心轮子**(见
|
|
1164
|
+
* `.dsh/docs/plans/2026-11-01-github-browser-auth-plan.md`)。
|
|
1165
|
+
* - 协议生命周期 / attempt 单飞 / 取消 / 结算 → `ctx.authorization`(核心)
|
|
1166
|
+
* - 回环回调 HTTP 载体 → `ctx.webServer.register({ kind:'exact' })`(核心)
|
|
1167
|
+
* - token 落盘 / 原子写 / 0600 / record 事件 → `ctx.credentials`(核心)
|
|
1168
|
+
* - 交互词汇 notify/prompt → `AuthorizationSession`(核心)
|
|
1169
|
+
*
|
|
1170
|
+
* 本文件只写 GitHub provider 专有的:client_id、端点、PKCE 三行、state 校验、exchange、
|
|
1171
|
+
* grant payload,以及「进程内 begin + 内存 attempt 视图」这点胶水(核心没有给第三方 key
|
|
1172
|
+
* 的 authorization HTTP 投影,见阶段 0 核实 R4)。
|
|
1173
|
+
*
|
|
1174
|
+
* 不 import 任何 `@deepseek-ai/*` 运行时模块:全部走 `get(...)` 的鸭子类型,故不需要新增
|
|
1175
|
+
* peerDependency。record 键用字符串 `'github/default'`(与核心包提案同名同构,便于迁移)。
|
|
1176
|
+
*/
|
|
1160
1177
|
/** 与核心 `@deepseek-ai/dsh-github-auth` 提案保持同 key;迁移时消费端无需改。 */
|
|
1161
1178
|
declare const GITHUB_RECORD_KEY = "github/default";
|
|
1162
|
-
/** 本地回调路径(带集成前缀,避免多 provider 撞 webServer 的 `(kind,path)` 唯一约束)。 */
|
|
1163
|
-
declare const CALLBACK_PATH = "/api/morehao/github-trending/oauth/github/callback";
|
|
1164
1179
|
/**
|
|
1165
1180
|
* 内置的 GitHub OAuth App 公开 client_id(**非机密**:OAuth 的 client_id 设计上就是公开的,
|
|
1166
1181
|
* 会出现在授权 URL 里,与 `gh`/VS Code 内置自己的 client_id 同理)。
|
|
1167
1182
|
*
|
|
1168
|
-
*
|
|
1169
|
-
*
|
|
1170
|
-
*
|
|
1171
|
-
* - 内置 client_id → 只能走 **device flow**(`resolveMethod` 自动降级),开箱即用;
|
|
1172
|
-
* - 用户自带 OAuth App(`githubClientId` + `githubClientSecret`)→ 才启用回环 web flow。
|
|
1173
|
-
* 且 device flow 需要在 OAuth App 设置里勾选启用(本插件内置的 App 已启用)。
|
|
1174
|
-
*
|
|
1175
|
-
* OAuth App 侧登记的回调:`http://127.0.0.1/api/morehao/github-trending/oauth/github/callback`
|
|
1176
|
-
* (loopback 按 RFC 8252 忽略端口,运行时随机端口可正常回跳)。
|
|
1183
|
+
* 只走 **device flow**:GitHub 的 web application flow 换 token 必须带 client_secret,
|
|
1184
|
+
* 而客户端不能内置机密,故本插件不实现回环 web flow(也不支持自带 OAuth App 的 secret)。
|
|
1185
|
+
* device flow 需要在 OAuth App 设置里勾选启用(本插件内置的 App 已启用)。
|
|
1177
1186
|
*/
|
|
1178
1187
|
declare const DEFAULT_GITHUB_CLIENT_ID = "Ov23li7rhp0EKoqbkJKY";
|
|
1179
|
-
/** 解析生效的 client_id:显式 config
|
|
1188
|
+
/** 解析生效的 client_id:显式 config 优先,否则用内置默认(device flow 允许自定义 client_id)。 */
|
|
1180
1189
|
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';
|
|
1193
1190
|
/** 本插件运行需要 push / 建 PR,`read:user` 不够;一次授权到 `repo`。 */
|
|
1194
1191
|
declare const DEFAULT_SCOPE = "repo";
|
|
1195
1192
|
interface AuthorizationFlow {
|
|
@@ -1224,15 +1221,6 @@ interface AuthorizationLike {
|
|
|
1224
1221
|
/** 核心 0.2.x 有:按 key 查已注册的 flow;判定「真的注册上了」靠它。 */
|
|
1225
1222
|
describe?: (key: string) => unknown;
|
|
1226
1223
|
}
|
|
1227
|
-
interface WebServerLike {
|
|
1228
|
-
register?: (route: {
|
|
1229
|
-
kind: 'exact';
|
|
1230
|
-
path: string;
|
|
1231
|
-
handler: (req: any, res: any) => void;
|
|
1232
|
-
}) => () => void;
|
|
1233
|
-
port?: number;
|
|
1234
|
-
host?: string;
|
|
1235
|
-
}
|
|
1236
1224
|
type FetchLike = typeof fetch;
|
|
1237
1225
|
interface GithubAuthDeps {
|
|
1238
1226
|
get: (name: string) => unknown;
|
|
@@ -1240,70 +1228,25 @@ interface GithubAuthDeps {
|
|
|
1240
1228
|
logger?: (event: string, data?: Record<string, unknown>) => void;
|
|
1241
1229
|
/** 覆盖端点(测试用);生产留空走 GitHub 官方。 */
|
|
1242
1230
|
endpoints?: {
|
|
1243
|
-
authorize?: string;
|
|
1244
1231
|
accessToken?: string;
|
|
1245
1232
|
deviceCode?: string;
|
|
1246
1233
|
user?: string;
|
|
1247
1234
|
};
|
|
1248
|
-
/** GitHub OAuth App 的 public client_id
|
|
1235
|
+
/** GitHub OAuth App 的 public client_id(device flow 用;无需 secret)。 */
|
|
1249
1236
|
clientId?: string;
|
|
1250
|
-
/**
|
|
1251
|
-
* GitHub OAuth App 的 client_secret。只有自带 OAuth App 的回环 web flow 才需要;
|
|
1252
|
-
* 缺省时 `browser` 方法自动降级为 device flow。不进 `settings.json`、不进日志。
|
|
1253
|
-
*/
|
|
1254
|
-
clientSecret?: string;
|
|
1255
1237
|
scope?: string;
|
|
1256
1238
|
attemptTimeoutMs?: number;
|
|
1257
1239
|
}
|
|
1258
|
-
type GithubAttemptPhase = 'idle' | 'waiting-
|
|
1240
|
+
type GithubAttemptPhase = 'idle' | 'waiting-device' | 'exchanging' | 'done' | 'error';
|
|
1259
1241
|
interface GithubAttemptView {
|
|
1260
1242
|
phase: GithubAttemptPhase;
|
|
1261
|
-
authorizeUrl?: string;
|
|
1262
1243
|
verificationUri?: string;
|
|
1263
|
-
/**
|
|
1264
|
-
* device flow 的预填验证码地址(`verification_uri_complete`),**可选**:若 provider 返回就透出,
|
|
1265
|
-
* 前端优先用它打开(码已预填)。注意 GitHub 官方 device flow 实测不返回该字段,此时走
|
|
1266
|
-
* `verificationUri` + 手动输入;保留此字段只为兼容会返回它的 provider。
|
|
1267
|
-
*/
|
|
1268
|
-
verificationUriComplete?: string;
|
|
1269
1244
|
userCode?: string;
|
|
1270
1245
|
expiresAt?: number;
|
|
1271
1246
|
login?: string;
|
|
1272
1247
|
error?: string;
|
|
1273
1248
|
}
|
|
1274
1249
|
declare function attemptView(): GithubAttemptView;
|
|
1275
|
-
/** 判断一个未知值是否是可直接打开的 http(s) URL(用于校验 GitHub 返回的验证页地址)。 */
|
|
1276
|
-
declare function isHttpUrl(value: unknown): boolean;
|
|
1277
|
-
/** PKCE:返回 verifier 与 S256 challenge。 */
|
|
1278
|
-
declare function createPkce(): {
|
|
1279
|
-
verifier: string;
|
|
1280
|
-
challenge: string;
|
|
1281
|
-
};
|
|
1282
|
-
/** 常量时间比较 state(长度不等直接 false,避免 timingSafeEqual 抛错)。 */
|
|
1283
|
-
declare function verifyState(received: string, expected: string): boolean;
|
|
1284
|
-
/** 回调落地页的两种结果(纯展示,无外部资源,离线可测)。 */
|
|
1285
|
-
type CallbackPageKind = 'success' | 'error';
|
|
1286
|
-
/**
|
|
1287
|
-
* 回环回调的落地页 HTML。自包含、无外链资源(CSP 友好、离线可用),
|
|
1288
|
-
* 跟随系统深浅色;成功页会尝试自动关闭本窗口(脚本失败时文字兜底)。
|
|
1289
|
-
*/
|
|
1290
|
-
declare function renderCallbackPage(kind: CallbackPageKind): string;
|
|
1291
|
-
/** 拼授权页 URL。 */
|
|
1292
|
-
declare function buildAuthorizeUrl(params: {
|
|
1293
|
-
authorizeEndpoint: string;
|
|
1294
|
-
clientId: string;
|
|
1295
|
-
redirectUri: string;
|
|
1296
|
-
scope: string;
|
|
1297
|
-
state: string;
|
|
1298
|
-
challenge: string;
|
|
1299
|
-
}): string;
|
|
1300
|
-
interface GithubGrantPayload {
|
|
1301
|
-
version: 1;
|
|
1302
|
-
token: string;
|
|
1303
|
-
login?: string;
|
|
1304
|
-
tokenType?: string;
|
|
1305
|
-
scope?: string;
|
|
1306
|
-
}
|
|
1307
1250
|
/** 这个 key 现在是否**真的**有 flow 在注册表里。 */
|
|
1308
1251
|
declare function isGithubFlowRegistered(deps: GithubAuthDeps): boolean;
|
|
1309
1252
|
/**
|
|
@@ -1334,7 +1277,7 @@ declare function registerGithubFlow(deps: GithubAuthDeps): void;
|
|
|
1334
1277
|
*
|
|
1335
1278
|
* @throws 服务不可用、flow 未注册、已有 attempt 在飞时抛错。
|
|
1336
1279
|
*/
|
|
1337
|
-
declare function beginGithubSignIn(deps: GithubAuthDeps
|
|
1280
|
+
declare function beginGithubSignIn(deps: GithubAuthDeps): Promise<GithubAttemptView>;
|
|
1338
1281
|
/** 取消进行中的 attempt。 */
|
|
1339
1282
|
declare function cancelGithubSignIn(deps: GithubAuthDeps): void;
|
|
1340
1283
|
/** 仅测试用:清空内存 attempt。 */
|
|
@@ -1343,33 +1286,22 @@ declare function resetAttemptForTest(): void;
|
|
|
1343
1286
|
type githubAuth_AuthorizationFlow = AuthorizationFlow;
|
|
1344
1287
|
type githubAuth_AuthorizationLike = AuthorizationLike;
|
|
1345
1288
|
type githubAuth_AuthorizationSessionLike = AuthorizationSessionLike;
|
|
1346
|
-
declare const githubAuth_CALLBACK_PATH: typeof CALLBACK_PATH;
|
|
1347
1289
|
declare const githubAuth_DEFAULT_GITHUB_CLIENT_ID: typeof DEFAULT_GITHUB_CLIENT_ID;
|
|
1348
1290
|
declare const githubAuth_DEFAULT_SCOPE: typeof DEFAULT_SCOPE;
|
|
1349
1291
|
declare const githubAuth_GITHUB_RECORD_KEY: typeof GITHUB_RECORD_KEY;
|
|
1350
1292
|
type githubAuth_GithubAttemptPhase = GithubAttemptPhase;
|
|
1351
1293
|
type githubAuth_GithubAttemptView = GithubAttemptView;
|
|
1352
1294
|
type githubAuth_GithubAuthDeps = GithubAuthDeps;
|
|
1353
|
-
type githubAuth_GithubGrantPayload = GithubGrantPayload;
|
|
1354
|
-
type githubAuth_WebServerLike = WebServerLike;
|
|
1355
1295
|
declare const githubAuth_attemptView: typeof attemptView;
|
|
1356
1296
|
declare const githubAuth_beginGithubSignIn: typeof beginGithubSignIn;
|
|
1357
|
-
declare const githubAuth_buildAuthorizeUrl: typeof buildAuthorizeUrl;
|
|
1358
1297
|
declare const githubAuth_cancelGithubSignIn: typeof cancelGithubSignIn;
|
|
1359
|
-
declare const githubAuth_createPkce: typeof createPkce;
|
|
1360
1298
|
declare const githubAuth_ensureGithubFlow: typeof ensureGithubFlow;
|
|
1361
1299
|
declare const githubAuth_isGithubFlowRegistered: typeof isGithubFlowRegistered;
|
|
1362
|
-
declare const githubAuth_isHttpUrl: typeof isHttpUrl;
|
|
1363
|
-
declare const githubAuth_isLoopbackSignInAvailable: typeof isLoopbackSignInAvailable;
|
|
1364
1300
|
declare const githubAuth_registerGithubFlow: typeof registerGithubFlow;
|
|
1365
|
-
declare const githubAuth_renderCallbackPage: typeof renderCallbackPage;
|
|
1366
1301
|
declare const githubAuth_resetAttemptForTest: typeof resetAttemptForTest;
|
|
1367
1302
|
declare const githubAuth_resolveClientId: typeof resolveClientId;
|
|
1368
|
-
declare const githubAuth_resolveClientSecret: typeof resolveClientSecret;
|
|
1369
|
-
declare const githubAuth_resolveMethod: typeof resolveMethod;
|
|
1370
|
-
declare const githubAuth_verifyState: typeof verifyState;
|
|
1371
1303
|
declare namespace githubAuth {
|
|
1372
|
-
export { type githubAuth_AuthorizationFlow as AuthorizationFlow, type githubAuth_AuthorizationLike as AuthorizationLike, type githubAuth_AuthorizationSessionLike as AuthorizationSessionLike,
|
|
1304
|
+
export { type githubAuth_AuthorizationFlow as AuthorizationFlow, type githubAuth_AuthorizationLike as AuthorizationLike, type githubAuth_AuthorizationSessionLike as AuthorizationSessionLike, 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, githubAuth_attemptView as attemptView, githubAuth_beginGithubSignIn as beginGithubSignIn, githubAuth_cancelGithubSignIn as cancelGithubSignIn, githubAuth_ensureGithubFlow as ensureGithubFlow, githubAuth_isGithubFlowRegistered as isGithubFlowRegistered, githubAuth_registerGithubFlow as registerGithubFlow, githubAuth_resetAttemptForTest as resetAttemptForTest, githubAuth_resolveClientId as resolveClientId };
|
|
1373
1305
|
}
|
|
1374
1306
|
|
|
1375
1307
|
interface DefaultBranchResult {
|
|
@@ -2053,10 +1985,8 @@ interface RouteDeps {
|
|
|
2053
1985
|
/** 包名与仓库地址都来自 package.json,由 src/index.ts 传入。 */
|
|
2054
1986
|
pluginName?: string;
|
|
2055
1987
|
repository?: string;
|
|
2056
|
-
/**
|
|
1988
|
+
/** GitHub 登录用的 public client_id(由 apply 的 config 传入;缺省时用内置默认)。 */
|
|
2057
1989
|
githubClientId?: string;
|
|
2058
|
-
/** 自带 OAuth App 时的 client_secret(只用于回环 web flow;缺省时该 method 自动降级为设备码)。 */
|
|
2059
|
-
githubClientSecret?: string;
|
|
2060
1990
|
logger?: (event: string, data?: Record<string, unknown>) => void;
|
|
2061
1991
|
}
|
|
2062
1992
|
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.6",
|
|
4
4
|
"description": "GitHub 趋势日报的 DeepSeek Harness 插件:按日 / 周 / 月周期定时 spawn 本地 trendradar 核心,抓取 Trending、生成中文简介与周期总结,并可选把订阅仓库的新 Release 合成中文摘要,一起提交到你的仓库、自动开 PR 合并;内置概览 / 榜单 / 运行记录 / 订阅 / 设置工作台。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|