@morehao/dsh-github-trending 0.8.2 → 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 +8 -0
- package/README.md +2 -2
- package/client.js +13 -6
- package/lib/{chunk-5D2VRWPJ.js → chunk-VO72OOVK.js} +54 -11
- package/lib/index.d.ts +1 -0
- package/lib/index.js +5 -2
- package/lib/internal.d.ts +31 -3
- package/lib/internal.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v0.8.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
|
+
|
|
3
11
|
## [v0.8.2] - 2026-11-02
|
|
4
12
|
|
|
5
13
|
### Fixes
|
package/README.md
CHANGED
|
@@ -59,13 +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
66
|
> 浏览器登录需要核心提供 `authorization` 服务,并且 GitHub flow 注册成功才算可用;两者任一不满足时,设置页直接提示不可用并引导改用 gh / PAT,而不是给一个点了没反应的按钮。宿主启动时服务可能晚于插件就绪,插件会等服务就绪再注册,并在读状态时再兜底一次。
|
|
67
67
|
>
|
|
68
|
-
>
|
|
68
|
+
> 设备码 / 授权页默认尝试自动打开;若被浏览器的弹窗拦截,状态行里始终有可点的「打开授权页」链接、验证码与「复制链接」。
|
|
69
69
|
|
|
70
70
|
### 订阅
|
|
71
71
|
|
package/client.js
CHANGED
|
@@ -125,7 +125,7 @@ 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": "在打开的页面输入验证码:",
|
|
@@ -139,7 +139,7 @@ var DICT = {
|
|
|
139
139
|
"auth.browser.copied": "授权链接已复制",
|
|
140
140
|
"auth.browser.failed": "登录失败",
|
|
141
141
|
"auth.browser.failed.detail": "登录失败:{message}",
|
|
142
|
-
"auth.browser.failed.hint": "
|
|
142
|
+
"auth.browser.failed.hint": "请重试一次;仍失败可改用「本机 gh」或 PAT。",
|
|
143
143
|
"auth.pat.hint": "在 GitHub → Settings → Developer settings 生成 Personal Access Token(勾选 public_repo),填到下方引用或环境变量。",
|
|
144
144
|
"auth.tokenRef": "凭据引用",
|
|
145
145
|
"auth.tokenRef.hint": "凭据引用名 / 环境变量名",
|
|
@@ -670,7 +670,7 @@ 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. Uses a device code by default: enter the code on the GitHub page after clicking.",
|
|
674
674
|
"auth.browser.signin": "Sign in with GitHub",
|
|
675
675
|
"auth.browser.waiting": "Waiting for browser authorization…",
|
|
676
676
|
"auth.browser.code": "Enter this code on the opened page:",
|
|
@@ -684,7 +684,7 @@ var DICT = {
|
|
|
684
684
|
"auth.browser.copied": "Authorization link copied",
|
|
685
685
|
"auth.browser.failed": "Sign-in failed",
|
|
686
686
|
"auth.browser.failed.detail": "Sign-in failed: {message}",
|
|
687
|
-
"auth.browser.failed.hint": "
|
|
687
|
+
"auth.browser.failed.hint": "Retry once; if it keeps failing, use Local gh or a PAT instead.",
|
|
688
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.",
|
|
689
689
|
"auth.tokenRef": "Credential ref",
|
|
690
690
|
"auth.tokenRef.hint": "credential ref / env var name",
|
|
@@ -2272,6 +2272,12 @@ function SettingsView(props) {
|
|
|
2272
2272
|
} catch {
|
|
2273
2273
|
}
|
|
2274
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
|
+
}
|
|
2275
2281
|
} catch (error) {
|
|
2276
2282
|
notify("err", error instanceof Error ? error.message : String(error));
|
|
2277
2283
|
}
|
|
@@ -2348,6 +2354,7 @@ function SettingsView(props) {
|
|
|
2348
2354
|
const isGh = draft.github.authMode === "gh";
|
|
2349
2355
|
const isBrowser = draft.github.authMode === "browser";
|
|
2350
2356
|
const browserAvailable = state.github.browserAvailable === true;
|
|
2357
|
+
const loopbackAvailable = state.github.loopbackAvailable === true;
|
|
2351
2358
|
const sections = [
|
|
2352
2359
|
{ id: "source", label: t("st.source") },
|
|
2353
2360
|
{ id: "model", label: t("st.model") },
|
|
@@ -2542,12 +2549,12 @@ function SettingsView(props) {
|
|
|
2542
2549
|
{
|
|
2543
2550
|
small: true,
|
|
2544
2551
|
variant: "primary",
|
|
2545
|
-
onClick: () => void startSignIn("browser"),
|
|
2552
|
+
onClick: () => void startSignIn(loopbackAvailable ? "browser" : "device"),
|
|
2546
2553
|
disabled: signInActive
|
|
2547
2554
|
},
|
|
2548
2555
|
signIn.phase === "done" && signIn.login !== void 0 ? `${t("auth.browser.done")} · @${signIn.login}` : signInActive ? t("auth.browser.waiting") : t("auth.browser.signin")
|
|
2549
2556
|
) : h("span", { className: "dshgt-hint" }, t("auth.browser.unavailable")),
|
|
2550
|
-
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,
|
|
2551
2558
|
signInActive ? h(Btn, { small: true, variant: "ghost", onClick: () => void cancelSignIn() }, t("auth.browser.cancel")) : null
|
|
2552
2559
|
) : h(
|
|
2553
2560
|
Row,
|
|
@@ -3540,9 +3540,12 @@ __export(github_auth_exports, {
|
|
|
3540
3540
|
createPkce: () => createPkce,
|
|
3541
3541
|
ensureGithubFlow: () => ensureGithubFlow,
|
|
3542
3542
|
isGithubFlowRegistered: () => isGithubFlowRegistered,
|
|
3543
|
+
isLoopbackSignInAvailable: () => isLoopbackSignInAvailable,
|
|
3543
3544
|
registerGithubFlow: () => registerGithubFlow,
|
|
3544
3545
|
resetAttemptForTest: () => resetAttemptForTest,
|
|
3545
3546
|
resolveClientId: () => resolveClientId,
|
|
3547
|
+
resolveClientSecret: () => resolveClientSecret,
|
|
3548
|
+
resolveMethod: () => resolveMethod,
|
|
3546
3549
|
verifyState: () => verifyState
|
|
3547
3550
|
});
|
|
3548
3551
|
import { createHash as createHash3, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
@@ -3553,6 +3556,16 @@ function resolveClientId(deps) {
|
|
|
3553
3556
|
const fromConfig = deps.clientId;
|
|
3554
3557
|
return fromConfig !== void 0 && fromConfig !== "" ? fromConfig : DEFAULT_GITHUB_CLIENT_ID;
|
|
3555
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
|
+
}
|
|
3556
3569
|
var AUTHORIZE_URL = "https://github.com/login/oauth/authorize";
|
|
3557
3570
|
var ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
3558
3571
|
var DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
@@ -3589,21 +3602,29 @@ function buildAuthorizeUrl(params) {
|
|
|
3589
3602
|
return url.toString();
|
|
3590
3603
|
}
|
|
3591
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);
|
|
3592
3612
|
const response = await fetchImpl(endpoint, {
|
|
3593
3613
|
method: "POST",
|
|
3594
3614
|
headers: { accept: "application/json", "content-type": "application/x-www-form-urlencoded" },
|
|
3595
|
-
body:
|
|
3596
|
-
client_id: params.clientId,
|
|
3597
|
-
code: params.code,
|
|
3598
|
-
code_verifier: params.verifier,
|
|
3599
|
-
redirect_uri: params.redirectUri
|
|
3600
|
-
}).toString(),
|
|
3615
|
+
body: form.toString(),
|
|
3601
3616
|
signal
|
|
3602
3617
|
});
|
|
3603
3618
|
if (!response.ok) throw new Error(`GitHub access_token HTTP ${response.status}`);
|
|
3604
3619
|
const body = await response.json();
|
|
3605
3620
|
if (typeof body.access_token !== "string" || body.access_token === "") {
|
|
3606
|
-
|
|
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`}`);
|
|
3607
3628
|
}
|
|
3608
3629
|
return {
|
|
3609
3630
|
token: body.access_token,
|
|
@@ -3631,6 +3652,10 @@ async function runBrowser(deps, session) {
|
|
|
3631
3652
|
}
|
|
3632
3653
|
const clientId = resolveClientId(deps);
|
|
3633
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
|
+
}
|
|
3634
3659
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
3635
3660
|
const endpoints = normalizeEndpoints(deps.endpoints);
|
|
3636
3661
|
const scope = deps.scope ?? DEFAULT_SCOPE;
|
|
@@ -3681,7 +3706,12 @@ async function runBrowser(deps, session) {
|
|
|
3681
3706
|
session.notify?.({ message: "Continue in your browser to sign in to GitHub.", url: authorizeUrl });
|
|
3682
3707
|
const code = await withAbort(codePromise, session.signal, () => rejectCode(new Error("authorization cancelled")));
|
|
3683
3708
|
updateCurrent({ phase: "exchanging" });
|
|
3684
|
-
const exchanged = await exchangeCode(
|
|
3709
|
+
const exchanged = await exchangeCode(
|
|
3710
|
+
fetchImpl,
|
|
3711
|
+
endpoints.accessToken,
|
|
3712
|
+
{ clientId, clientSecret, code, verifier, redirectUri },
|
|
3713
|
+
session.signal
|
|
3714
|
+
);
|
|
3685
3715
|
const login = await fetchLogin(fetchImpl, endpoints.user, exchanged.token, session.signal);
|
|
3686
3716
|
const payload = {
|
|
3687
3717
|
version: 1,
|
|
@@ -3711,7 +3741,10 @@ async function runDevice(deps, session) {
|
|
|
3711
3741
|
if (!startResponse.ok) throw new Error(`GitHub device/code HTTP ${startResponse.status}`);
|
|
3712
3742
|
const start = await startResponse.json();
|
|
3713
3743
|
if (typeof start.device_code !== "string" || typeof start.user_code !== "string" || typeof start.verification_uri !== "string") {
|
|
3714
|
-
|
|
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
|
+
);
|
|
3715
3748
|
}
|
|
3716
3749
|
const deviceCode = start.device_code;
|
|
3717
3750
|
const intervalSec = typeof start.interval === "number" && start.interval > 0 ? start.interval : 5;
|
|
@@ -3829,6 +3862,10 @@ async function beginGithubSignIn(deps, method = "browser") {
|
|
|
3829
3862
|
if (current !== void 0 && (current.phase === "waiting-browser" || current.phase === "waiting-device" || current.phase === "exchanging")) {
|
|
3830
3863
|
throw new Error("already-in-flight");
|
|
3831
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
|
+
}
|
|
3832
3869
|
const controller = new AbortController();
|
|
3833
3870
|
const timer = setTimeout(() => controller.abort(), attemptTimeout(deps));
|
|
3834
3871
|
const attempt = { phase: "idle", controller, done: Promise.resolve() };
|
|
@@ -3837,7 +3874,7 @@ async function beginGithubSignIn(deps, method = "browser") {
|
|
|
3837
3874
|
try {
|
|
3838
3875
|
started = authorization.begin({
|
|
3839
3876
|
key: GITHUB_RECORD_KEY2,
|
|
3840
|
-
method,
|
|
3877
|
+
method: effectiveMethod,
|
|
3841
3878
|
signal: controller.signal,
|
|
3842
3879
|
// 浏览器往返靠 authorizeUrl + 回调;notify 只更新内存视图,prompt 不支持(拒绝)。
|
|
3843
3880
|
interaction: {
|
|
@@ -4455,6 +4492,7 @@ function githubAuthDeps(deps) {
|
|
|
4455
4492
|
get: deps.get,
|
|
4456
4493
|
...deps.fetchImpl === void 0 ? {} : { fetchImpl: deps.fetchImpl },
|
|
4457
4494
|
...deps.githubClientId === void 0 ? {} : { clientId: deps.githubClientId },
|
|
4495
|
+
...deps.githubClientSecret === void 0 ? {} : { clientSecret: deps.githubClientSecret },
|
|
4458
4496
|
...deps.logger === void 0 ? {} : { logger: deps.logger }
|
|
4459
4497
|
};
|
|
4460
4498
|
}
|
|
@@ -4794,7 +4832,12 @@ async function statePayload(deps, settings) {
|
|
|
4794
4832
|
/** 浏览器登录 attempt 视图(idle / waiting-browser / waiting-device / exchanging / done / error)。 */
|
|
4795
4833
|
attempt: attemptView(),
|
|
4796
4834
|
/** 浏览器登录是否可用:authorization 服务在、且 GitHub flow 真的注册上了(client_id 有内置默认)。 */
|
|
4797
|
-
browserAvailable: browserSignInAvailable(deps)
|
|
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))
|
|
4798
4841
|
}
|
|
4799
4842
|
};
|
|
4800
4843
|
}
|
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-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)}`;
|
|
@@ -67,7 +68,8 @@ function apply(ctx, config = {}) {
|
|
|
67
68
|
const githubAuth = (resolve) => ({
|
|
68
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
|
});
|
|
72
74
|
const registerAuth = (resolve) => {
|
|
73
75
|
if (!ensureGithubFlow(githubAuth(resolve ?? get))) log("auth.flow.deferred", { reason: "authorization-not-ready" });
|
|
@@ -100,6 +102,7 @@ function apply(ctx, config = {}) {
|
|
|
100
102
|
pluginName: pluginInfo.name,
|
|
101
103
|
...pluginInfo.repository === void 0 ? {} : { repository: pluginInfo.repository },
|
|
102
104
|
...config.githubClientId === void 0 ? {} : { githubClientId: config.githubClientId },
|
|
105
|
+
...githubClientSecret === void 0 || githubClientSecret === "" ? {} : { githubClientSecret },
|
|
103
106
|
logger: log
|
|
104
107
|
});
|
|
105
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 {
|
|
@@ -1229,6 +1247,11 @@ interface GithubAuthDeps {
|
|
|
1229
1247
|
};
|
|
1230
1248
|
/** GitHub OAuth App 的 public client_id(无需 secret)。 */
|
|
1231
1249
|
clientId?: string;
|
|
1250
|
+
/**
|
|
1251
|
+
* GitHub OAuth App 的 client_secret。只有自带 OAuth App 的回环 web flow 才需要;
|
|
1252
|
+
* 缺省时 `browser` 方法自动降级为 device flow。不进 `settings.json`、不进日志。
|
|
1253
|
+
*/
|
|
1254
|
+
clientSecret?: string;
|
|
1232
1255
|
scope?: string;
|
|
1233
1256
|
attemptTimeoutMs?: number;
|
|
1234
1257
|
}
|
|
@@ -1321,12 +1344,15 @@ declare const githubAuth_cancelGithubSignIn: typeof cancelGithubSignIn;
|
|
|
1321
1344
|
declare const githubAuth_createPkce: typeof createPkce;
|
|
1322
1345
|
declare const githubAuth_ensureGithubFlow: typeof ensureGithubFlow;
|
|
1323
1346
|
declare const githubAuth_isGithubFlowRegistered: typeof isGithubFlowRegistered;
|
|
1347
|
+
declare const githubAuth_isLoopbackSignInAvailable: typeof isLoopbackSignInAvailable;
|
|
1324
1348
|
declare const githubAuth_registerGithubFlow: typeof registerGithubFlow;
|
|
1325
1349
|
declare const githubAuth_resetAttemptForTest: typeof resetAttemptForTest;
|
|
1326
1350
|
declare const githubAuth_resolveClientId: typeof resolveClientId;
|
|
1351
|
+
declare const githubAuth_resolveClientSecret: typeof resolveClientSecret;
|
|
1352
|
+
declare const githubAuth_resolveMethod: typeof resolveMethod;
|
|
1327
1353
|
declare const githubAuth_verifyState: typeof verifyState;
|
|
1328
1354
|
declare namespace githubAuth {
|
|
1329
|
-
export { type githubAuth_AuthorizationFlow as AuthorizationFlow, type githubAuth_AuthorizationLike as AuthorizationLike, type githubAuth_AuthorizationSessionLike as AuthorizationSessionLike, githubAuth_CALLBACK_PATH as CALLBACK_PATH, githubAuth_DEFAULT_GITHUB_CLIENT_ID as DEFAULT_GITHUB_CLIENT_ID, githubAuth_DEFAULT_SCOPE as DEFAULT_SCOPE, githubAuth_GITHUB_RECORD_KEY as GITHUB_RECORD_KEY, type githubAuth_GithubAttemptPhase as GithubAttemptPhase, type githubAuth_GithubAttemptView as GithubAttemptView, type githubAuth_GithubAuthDeps as GithubAuthDeps, type githubAuth_GithubGrantPayload as GithubGrantPayload, type githubAuth_WebServerLike as WebServerLike, githubAuth_attemptView as attemptView, githubAuth_beginGithubSignIn as beginGithubSignIn, githubAuth_buildAuthorizeUrl as buildAuthorizeUrl, githubAuth_cancelGithubSignIn as cancelGithubSignIn, githubAuth_createPkce as createPkce, githubAuth_ensureGithubFlow as ensureGithubFlow, githubAuth_isGithubFlowRegistered as isGithubFlowRegistered, githubAuth_registerGithubFlow as registerGithubFlow, githubAuth_resetAttemptForTest as resetAttemptForTest, githubAuth_resolveClientId as resolveClientId, githubAuth_verifyState as verifyState };
|
|
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 };
|
|
1330
1356
|
}
|
|
1331
1357
|
|
|
1332
1358
|
interface DefaultBranchResult {
|
|
@@ -2010,8 +2036,10 @@ interface RouteDeps {
|
|
|
2010
2036
|
/** 包名与仓库地址都来自 package.json,由 src/index.ts 传入。 */
|
|
2011
2037
|
pluginName?: string;
|
|
2012
2038
|
repository?: string;
|
|
2013
|
-
/** 浏览器 GitHub 登录用的 public client_id(由 apply 的 config
|
|
2039
|
+
/** 浏览器 GitHub 登录用的 public client_id(由 apply 的 config 传入;缺省时用内置默认)。 */
|
|
2014
2040
|
githubClientId?: string;
|
|
2041
|
+
/** 自带 OAuth App 时的 client_secret(只用于回环 web flow;缺省时该 method 自动降级为设备码)。 */
|
|
2042
|
+
githubClientSecret?: string;
|
|
2015
2043
|
logger?: (event: string, data?: Record<string, unknown>) => void;
|
|
2016
2044
|
}
|
|
2017
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",
|