@morehao/dsh-github-trending 0.8.3 → 0.8.4
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 +9 -0
- package/client.js +18 -8
- package/lib/{chunk-VO72OOVK.js → chunk-QOYTABJH.js} +53 -4
- package/lib/index.js +1 -1
- package/lib/internal.d.ts +14 -1
- package/lib/internal.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v0.8.4] - 2026-11-02
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- **auth**: open device-code page with prefilled code
|
|
8
|
+
- **auth**: 美化 GitHub OAuth 回调落地页 (#78)
|
|
9
|
+
|
|
10
|
+
**Full Changelog**: https://github.com/morehao/dsh-github-trending/compare/v0.8.3...v0.8.4
|
|
11
|
+
|
|
3
12
|
## [v0.8.3] - 2026-11-02
|
|
4
13
|
|
|
5
14
|
### Fixes
|
package/client.js
CHANGED
|
@@ -125,10 +125,10 @@ 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
|
-
"auth.browser.code": "
|
|
131
|
+
"auth.browser.code": "如未自动填入,请手动输入验证码:",
|
|
132
132
|
"auth.browser.cancel": "取消",
|
|
133
133
|
"auth.browser.done": "已登录",
|
|
134
134
|
"auth.browser.device": "用设备码登录",
|
|
@@ -670,10 +670,10 @@ var DICT = {
|
|
|
670
670
|
"auth.gh": "Local gh",
|
|
671
671
|
"auth.browser": "Browser login",
|
|
672
672
|
"auth.gh.hint": "Uses your local gh login: run `gh auth login` in a terminal, then click Test authentication.",
|
|
673
|
-
"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. Clicking opens the authorization page with the code usually pre-filled — just confirm.",
|
|
674
674
|
"auth.browser.signin": "Sign in with GitHub",
|
|
675
675
|
"auth.browser.waiting": "Waiting for browser authorization…",
|
|
676
|
-
"auth.browser.code": "
|
|
676
|
+
"auth.browser.code": "If it is not filled in automatically, enter this code:",
|
|
677
677
|
"auth.browser.cancel": "Cancel",
|
|
678
678
|
"auth.browser.done": "Signed in",
|
|
679
679
|
"auth.browser.device": "Use a device code",
|
|
@@ -2272,9 +2272,10 @@ function SettingsView(props) {
|
|
|
2272
2272
|
} catch {
|
|
2273
2273
|
}
|
|
2274
2274
|
}
|
|
2275
|
-
|
|
2275
|
+
const deviceUrl = view.verificationUriComplete ?? view.verificationUri;
|
|
2276
|
+
if (view.phase === "waiting-device" && deviceUrl !== void 0) {
|
|
2276
2277
|
try {
|
|
2277
|
-
window.open(
|
|
2278
|
+
window.open(deviceUrl, "_blank", "noopener,noreferrer");
|
|
2278
2279
|
} catch {
|
|
2279
2280
|
}
|
|
2280
2281
|
}
|
|
@@ -2583,10 +2584,19 @@ function SettingsView(props) {
|
|
|
2583
2584
|
" ",
|
|
2584
2585
|
h(Btn, { small: true, variant: "ghost", onClick: () => copyAuthorizeLink() }, t("auth.browser.copy"))
|
|
2585
2586
|
) : null,
|
|
2586
|
-
signIn.verificationUri !== void 0 ? h(
|
|
2587
|
+
signIn.verificationUriComplete !== void 0 || signIn.verificationUri !== void 0 ? h(
|
|
2587
2588
|
"div",
|
|
2588
2589
|
{ className: "dshgt-desc" },
|
|
2589
|
-
h(
|
|
2590
|
+
h(
|
|
2591
|
+
"a",
|
|
2592
|
+
{
|
|
2593
|
+
className: "dshgt-link",
|
|
2594
|
+
href: signIn.verificationUriComplete ?? signIn.verificationUri,
|
|
2595
|
+
target: "_blank",
|
|
2596
|
+
rel: "noreferrer"
|
|
2597
|
+
},
|
|
2598
|
+
t("auth.browser.open")
|
|
2599
|
+
)
|
|
2590
2600
|
) : null,
|
|
2591
2601
|
signIn.userCode !== void 0 ? h("div", { className: "dshgt-mono" }, `${t("auth.browser.code")} ${signIn.userCode}`) : null
|
|
2592
2602
|
) : null,
|
|
@@ -3540,8 +3540,10 @@ __export(github_auth_exports, {
|
|
|
3540
3540
|
createPkce: () => createPkce,
|
|
3541
3541
|
ensureGithubFlow: () => ensureGithubFlow,
|
|
3542
3542
|
isGithubFlowRegistered: () => isGithubFlowRegistered,
|
|
3543
|
+
isHttpUrl: () => isHttpUrl,
|
|
3543
3544
|
isLoopbackSignInAvailable: () => isLoopbackSignInAvailable,
|
|
3544
3545
|
registerGithubFlow: () => registerGithubFlow,
|
|
3546
|
+
renderCallbackPage: () => renderCallbackPage,
|
|
3545
3547
|
resetAttemptForTest: () => resetAttemptForTest,
|
|
3546
3548
|
resolveClientId: () => resolveClientId,
|
|
3547
3549
|
resolveClientSecret: () => resolveClientSecret,
|
|
@@ -3581,6 +3583,16 @@ function attemptView() {
|
|
|
3581
3583
|
function base64url(buf) {
|
|
3582
3584
|
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
3583
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
|
+
}
|
|
3584
3596
|
function createPkce() {
|
|
3585
3597
|
const verifier = base64url(randomBytes2(32));
|
|
3586
3598
|
const challenge = base64url(createHash3("sha256").update(verifier).digest());
|
|
@@ -3591,6 +3603,41 @@ function verifyState(received, expected) {
|
|
|
3591
3603
|
const b = Buffer.from(expected);
|
|
3592
3604
|
return a.length === b.length && timingSafeEqual2(a, b);
|
|
3593
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
|
+
}
|
|
3594
3641
|
function buildAuthorizeUrl(params) {
|
|
3595
3642
|
const url = new URL(params.authorizeEndpoint);
|
|
3596
3643
|
url.searchParams.set("client_id", params.clientId);
|
|
@@ -3684,10 +3731,10 @@ async function runBrowser(deps, session) {
|
|
|
3684
3731
|
const single = url.searchParams.getAll("state").length === 1 && url.searchParams.getAll("code").length === 1;
|
|
3685
3732
|
const bad = req.method !== "GET" || url.pathname !== CALLBACK_PATH || !single || code === "" || !verifyState(gotState, state);
|
|
3686
3733
|
if (bad) {
|
|
3687
|
-
res.writeHead(400, { "cache-control": "no-store" }).end();
|
|
3734
|
+
res.writeHead(400, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" }).end(renderCallbackPage("error"));
|
|
3688
3735
|
return;
|
|
3689
3736
|
}
|
|
3690
|
-
res.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" }).end(
|
|
3737
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" }).end(renderCallbackPage("success"));
|
|
3691
3738
|
resolveCode(code);
|
|
3692
3739
|
}
|
|
3693
3740
|
});
|
|
@@ -3750,15 +3797,17 @@ async function runDevice(deps, session) {
|
|
|
3750
3797
|
const intervalSec = typeof start.interval === "number" && start.interval > 0 ? start.interval : 5;
|
|
3751
3798
|
const expiresInSec = typeof start.expires_in === "number" && start.expires_in > 0 ? start.expires_in : 900;
|
|
3752
3799
|
const deadline = Date.now() + expiresInSec * 1e3;
|
|
3800
|
+
const verificationUriComplete = isHttpUrl(start.verification_uri_complete) ? start.verification_uri_complete : void 0;
|
|
3753
3801
|
updateCurrent({
|
|
3754
3802
|
phase: "waiting-device",
|
|
3755
3803
|
verificationUri: start.verification_uri,
|
|
3804
|
+
...verificationUriComplete === void 0 ? {} : { verificationUriComplete },
|
|
3756
3805
|
userCode: start.user_code,
|
|
3757
3806
|
expiresAt: deadline
|
|
3758
3807
|
});
|
|
3759
3808
|
session.notify?.({
|
|
3760
3809
|
message: "Enter this code on the verification page to finish signing in.",
|
|
3761
|
-
url: start.verification_uri,
|
|
3810
|
+
url: verificationUriComplete ?? start.verification_uri,
|
|
3762
3811
|
code: start.user_code
|
|
3763
3812
|
});
|
|
3764
3813
|
let waitMs = intervalSec * 1e3;
|
|
@@ -3880,7 +3929,7 @@ async function beginGithubSignIn(deps, method = "browser") {
|
|
|
3880
3929
|
interaction: {
|
|
3881
3930
|
notify: (notice) => {
|
|
3882
3931
|
if (notice.code !== void 0 && notice.url !== void 0) {
|
|
3883
|
-
updateCurrent({ phase: "waiting-device",
|
|
3932
|
+
updateCurrent({ phase: "waiting-device", userCode: notice.code });
|
|
3884
3933
|
} else if (notice.url !== void 0) {
|
|
3885
3934
|
updateCurrent({ phase: "waiting-browser", authorizeUrl: notice.url });
|
|
3886
3935
|
}
|
package/lib/index.js
CHANGED
package/lib/internal.d.ts
CHANGED
|
@@ -1260,12 +1260,16 @@ interface GithubAttemptView {
|
|
|
1260
1260
|
phase: GithubAttemptPhase;
|
|
1261
1261
|
authorizeUrl?: string;
|
|
1262
1262
|
verificationUri?: string;
|
|
1263
|
+
/** device flow 预填验证码的地址(GitHub 返回的 `verification_uri_complete`):打开只需点授权。 */
|
|
1264
|
+
verificationUriComplete?: string;
|
|
1263
1265
|
userCode?: string;
|
|
1264
1266
|
expiresAt?: number;
|
|
1265
1267
|
login?: string;
|
|
1266
1268
|
error?: string;
|
|
1267
1269
|
}
|
|
1268
1270
|
declare function attemptView(): GithubAttemptView;
|
|
1271
|
+
/** 判断一个未知值是否是可直接打开的 http(s) URL(用于校验 GitHub 返回的验证页地址)。 */
|
|
1272
|
+
declare function isHttpUrl(value: unknown): boolean;
|
|
1269
1273
|
/** PKCE:返回 verifier 与 S256 challenge。 */
|
|
1270
1274
|
declare function createPkce(): {
|
|
1271
1275
|
verifier: string;
|
|
@@ -1273,6 +1277,13 @@ declare function createPkce(): {
|
|
|
1273
1277
|
};
|
|
1274
1278
|
/** 常量时间比较 state(长度不等直接 false,避免 timingSafeEqual 抛错)。 */
|
|
1275
1279
|
declare function verifyState(received: string, expected: string): boolean;
|
|
1280
|
+
/** 回调落地页的两种结果(纯展示,无外部资源,离线可测)。 */
|
|
1281
|
+
type CallbackPageKind = 'success' | 'error';
|
|
1282
|
+
/**
|
|
1283
|
+
* 回环回调的落地页 HTML。自包含、无外链资源(CSP 友好、离线可用),
|
|
1284
|
+
* 跟随系统深浅色;成功页会尝试自动关闭本窗口(脚本失败时文字兜底)。
|
|
1285
|
+
*/
|
|
1286
|
+
declare function renderCallbackPage(kind: CallbackPageKind): string;
|
|
1276
1287
|
/** 拼授权页 URL。 */
|
|
1277
1288
|
declare function buildAuthorizeUrl(params: {
|
|
1278
1289
|
authorizeEndpoint: string;
|
|
@@ -1344,15 +1355,17 @@ declare const githubAuth_cancelGithubSignIn: typeof cancelGithubSignIn;
|
|
|
1344
1355
|
declare const githubAuth_createPkce: typeof createPkce;
|
|
1345
1356
|
declare const githubAuth_ensureGithubFlow: typeof ensureGithubFlow;
|
|
1346
1357
|
declare const githubAuth_isGithubFlowRegistered: typeof isGithubFlowRegistered;
|
|
1358
|
+
declare const githubAuth_isHttpUrl: typeof isHttpUrl;
|
|
1347
1359
|
declare const githubAuth_isLoopbackSignInAvailable: typeof isLoopbackSignInAvailable;
|
|
1348
1360
|
declare const githubAuth_registerGithubFlow: typeof registerGithubFlow;
|
|
1361
|
+
declare const githubAuth_renderCallbackPage: typeof renderCallbackPage;
|
|
1349
1362
|
declare const githubAuth_resetAttemptForTest: typeof resetAttemptForTest;
|
|
1350
1363
|
declare const githubAuth_resolveClientId: typeof resolveClientId;
|
|
1351
1364
|
declare const githubAuth_resolveClientSecret: typeof resolveClientSecret;
|
|
1352
1365
|
declare const githubAuth_resolveMethod: typeof resolveMethod;
|
|
1353
1366
|
declare const githubAuth_verifyState: typeof verifyState;
|
|
1354
1367
|
declare namespace githubAuth {
|
|
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 };
|
|
1368
|
+
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_isHttpUrl as isHttpUrl, githubAuth_isLoopbackSignInAvailable as isLoopbackSignInAvailable, githubAuth_registerGithubFlow as registerGithubFlow, githubAuth_renderCallbackPage as renderCallbackPage, githubAuth_resetAttemptForTest as resetAttemptForTest, githubAuth_resolveClientId as resolveClientId, githubAuth_resolveClientSecret as resolveClientSecret, githubAuth_resolveMethod as resolveMethod, githubAuth_verifyState as verifyState };
|
|
1356
1369
|
}
|
|
1357
1370
|
|
|
1358
1371
|
interface DefaultBranchResult {
|
package/lib/internal.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morehao/dsh-github-trending",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
4
4
|
"description": "GitHub 趋势日报的 DeepSeek Harness 插件:按日 / 周 / 月周期定时 spawn 本地 trendradar 核心,抓取 Trending、生成中文简介与周期总结,并可选把订阅仓库的新 Release 合成中文摘要,一起提交到你的仓库、自动开 PR 合并;内置概览 / 榜单 / 运行记录 / 订阅 / 设置工作台。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|