@dependfix/engine 0.1.3 → 0.4.0

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/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # @dependfix/engine
2
2
 
3
+ <!-- i18n: switch -->
4
+ [简体中文](./README.md) | <!-- English version pending -->
5
+
3
6
  > dependfix 执行引擎包:编排、采集、修复与研判核心。由 CLI(dependfix)、MCP Server(@dependfix/mcp)与平台(apps/platform)共同依赖,应用层不再互相依赖。
4
7
 
5
8
  ## 内容
@@ -25,3 +28,11 @@
25
28
  pnpm --filter @dependfix/engine build # 构建 dist
26
29
  pnpm --filter @dependfix/engine test # 测试
27
30
  ```
31
+
32
+ ## 相关包
33
+
34
+ - [dependfix 主项目](https://github.com/dependfix/dependfix) — 本包源码与完整文档
35
+ - [@dependfix/core](https://github.com/dependfix/dependfix/blob/master/packages/core/README.md) — 核心领域模型库(本包依赖)
36
+ - [dependfix](https://github.com/dependfix/dependfix/blob/master/packages/cli/README.md) — CLI 应用入口(依赖本包)
37
+ - [@dependfix/mcp](https://github.com/dependfix/dependfix/blob/master/packages/mcp/README.md) — MCP Server(依赖本包)
38
+ - [@dependfix/skills](https://github.com/dependfix/dependfix/blob/master/packages/skills/README.md) — 产品 Agent Skill 权威源
@@ -0,0 +1,82 @@
1
+ import { Octokit } from "@octokit/rest";
2
+ //#region src/auth/auth-provider.d.ts
3
+ /**
4
+ * GitHub 认证抽象层。
5
+ *
6
+ * 支持 PAT(classic / fine-grained)与 GitHub App installation token 两种认证路径。
7
+ * 调用方通过 `getOctokit()` 获取已认证的 Octokit 实例;通过 `getGitCredential()`
8
+ * 获取推送/clone 用的 Git 凭据;通过 `getCommitAuthor()` 获取 commit author 信息。
9
+ *
10
+ * `authProvider` 字段用于审计与调试(区分 PAT vs App 路径)。
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * // 推荐:从 fromPat / fromApp 工厂构造
15
+ * const auth = fromPat('ghp_xxxx')
16
+ * const octokit = auth.getOctokit()
17
+ *
18
+ * // git push 凭据
19
+ * const { username, token } = auth.getGitCredential()
20
+ *
21
+ * // commit author(PAT 路径固定;App 路径动态)
22
+ * const { name, email } = auth.getCommitAuthor()
23
+ * ```
24
+ */
25
+ interface AuthProvider {
26
+ /** 获取已认证的 Octokit 实例 */
27
+ getOctokit(): Octokit;
28
+ /** 获取推送 / clone 用的 Git 凭据(用户名 + token) */
29
+ getGitCredential(): {
30
+ username: string;
31
+ token: string;
32
+ };
33
+ /**
34
+ * 获取 commit author 信息。
35
+ *
36
+ * - PAT 路径:固定返回 `{ name: 'dependfix[bot]', email: 'dependfix[bot]@users.noreply.github.com' }`(保持现有行为零变化)
37
+ * - GitHub App 路径:动态生成 `{app_id}+{bot_login}[bot]@users.noreply.github.com`(GitHub App 协议要求)
38
+ */
39
+ getCommitAuthor(): {
40
+ name: string;
41
+ email: string;
42
+ };
43
+ /** 认证路径标识(用于审计 + 调试) */
44
+ readonly authProvider: 'pat' | 'github-app';
45
+ }
46
+ /** 工厂函数统一选项(与 OctokitClientOptions.retry 字段对齐) */
47
+ interface AuthProviderOptions {
48
+ /** API 限流 / 次要限流指数退避重试策略。默认 maxRetries=3。 */
49
+ retry?: {
50
+ maxRetries?: number;
51
+ baseDelayMs?: number;
52
+ maxBackoffMs?: number;
53
+ };
54
+ }
55
+ /** PAT 工厂参数(仅 token;retry 在 options 注入) */
56
+ type FromPatParams = string;
57
+ /** GitHub App 工厂参数 */
58
+ interface FromAppParams {
59
+ /** GitHub App ID */
60
+ appId: string;
61
+ /** PEM 格式私钥 */
62
+ privateKey: string;
63
+ /** Installation ID */
64
+ installationId: string;
65
+ /** Bot 用户名(用于 commit author 动态生成;M18.1 commit 4 实施) */
66
+ botLogin?: string;
67
+ }
68
+ /**
69
+ * PAT 路径固定 commit author(保持现有行为零变化)。
70
+ *
71
+ * 注:现有 PAT 路径硬编码 `dependfix[bot]@users.noreply.github.com`,虽非真实 bot 身份(字符串约定),
72
+ * 但保持行为不变以确保 PAT 用户无感升级。已知缺陷由 C22 范围之外的后续阶段修复。
73
+ */
74
+ declare const PAT_COMMIT_AUTHOR: {
75
+ readonly name: "dependfix[bot]";
76
+ readonly email: "dependfix[bot]@users.noreply.github.com";
77
+ };
78
+ /** PAT 路径固定 Git 用户名(用于 git push 时的 Basic Auth) */
79
+ declare const PAT_GIT_USERNAME = "x-access-token";
80
+ //#endregion
81
+ export { PAT_COMMIT_AUTHOR as a, FromPatParams as i, AuthProviderOptions as n, PAT_GIT_USERNAME as o, FromAppParams as r, AuthProvider as t };
82
+ //# sourceMappingURL=auth-provider.d.mts.map
@@ -0,0 +1,184 @@
1
+ import { a as PAT_COMMIT_AUTHOR, i as FromPatParams, n as AuthProviderOptions, o as PAT_GIT_USERNAME, r as FromAppParams, t as AuthProvider } from "./auth-provider.mjs";
2
+ import { Octokit } from "@octokit/rest";
3
+ //#region src/auth/pat-provider.d.ts
4
+ /**
5
+ * PAT 工厂函数。委托给 `PatAuthProvider` 类。
6
+ *
7
+ * 保留 `fromPat` 命名习惯便于与 `fromApp` 对齐(统一工厂语义)。
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const auth = fromPat('ghp_xxxx')
12
+ * const octokit = auth.getOctokit()
13
+ * const { username, token } = auth.getGitCredential()
14
+ * ```
15
+ */
16
+ export declare function fromPat(token: FromPatParams, options?: AuthProviderOptions): AuthProvider;
17
+ /**
18
+ * PAT 认证路径实现。
19
+ *
20
+ * `getOctokit()` 委托给 [`createGitHubClient`](../github/client.ts) 以复用现有 retry policy 与限流重试实现;
21
+ * Octokit 实例缓存以避免重复构造(每次 `new Octokit()` + `hook.wrap()` 都有开销)。
22
+ *
23
+ * 行为与原 `createGitHubClient({ token })` 等价:
24
+ * - `getOctokit()` 返回通过 PAT 认证的 Octokit 实例(含限流重试 hook)
25
+ * - `getGitCredential()` 返回推送用的 Basic Auth(username = `'x-access-token'`)
26
+ * - `getCommitAuthor()` 返回固定 `dependfix[bot]`(PAT 路径用户行为零变化)
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const auth = new PatAuthProvider('ghp_xxxx', { retry: { maxRetries: 3 } })
31
+ * const octokit = auth.getOctokit()
32
+ * ```
33
+ */
34
+ export declare class PatAuthProvider implements AuthProvider {
35
+ readonly authProvider: "pat";
36
+ private readonly token;
37
+ private readonly retry?;
38
+ private cachedOctokit;
39
+ constructor(token: FromPatParams, options?: AuthProviderOptions);
40
+ /**
41
+ * 获取已认证的 Octokit 实例(首次调用构造并缓存)。
42
+ */
43
+ getOctokit(): Octokit;
44
+ /** 获取推送 / clone 用的 Git 凭据(username = `'x-access-token'`,token = PAT) */
45
+ getGitCredential(): {
46
+ username: string;
47
+ token: string;
48
+ };
49
+ /** 获取 commit author(PAT 路径固定 `dependfix[bot]`) */
50
+ getCommitAuthor(): {
51
+ name: string;
52
+ email: string;
53
+ };
54
+ }
55
+ //#endregion
56
+ //#region src/auth/app-provider.d.ts
57
+ /**
58
+ * GitHub App 工厂函数(**M18.1 commit 4 完整实施**)。
59
+ *
60
+ * 委托给 [`AppAuthProvider`] 类。
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const auth = fromApp({
65
+ * appId: '123456',
66
+ * privateKey: '-----BEGIN RSA PRIVATE KEY-----\n...',
67
+ * installationId: '7890123',
68
+ * botLogin: 'dependfix-bot[bot]',
69
+ * })
70
+ * const octokit = auth.getOctokit()
71
+ * ```
72
+ */
73
+ export declare function fromApp(params: FromAppParams, options?: AuthProviderOptions): AuthProvider;
74
+ /**
75
+ * GitHub App 认证路径实现。
76
+ *
77
+ * 使用 [`@octokit/auth-app`](https://github.com/octokit/auth-app.js) 提供:
78
+ * - JWT signing(App 身份认证)
79
+ * - `getInstallationOctokit`(installation token 获取;库内置 59 分钟 LRU TTL 缓存)
80
+ * - 自动 installation token 轮换
81
+ *
82
+ * 注意:
83
+ * - `getGitCredential()` 返回的 `token` 字段为占位符(installation token 不能直接从 AppAuthProvider 获取;
84
+ * 由 [`@octokit/auth-app`](https://github.com/octokit/auth-app.js) 内部管理);调用方应通过 `getOctokit()`
85
+ * 拿到的 Octokit 实例进行 API 调用
86
+ * - `getCommitAuthor()` 按 GitHub App 协议动态生成 `{app_id}+{bot_login}[bot]@users.noreply.github.com`
87
+ * - Octokit 实例缓存以避免重复构造
88
+ *
89
+ * @see [C22 PAT 无感升级评估 §4.2 AuthProvider 接口设计](../../../../docs/design/governance/c22-pat-backward-compat.md)
90
+ */
91
+ export declare class AppAuthProvider implements AuthProvider {
92
+ readonly authProvider: "github-app";
93
+ private readonly params;
94
+ private readonly _options?;
95
+ private cachedOctokit;
96
+ constructor(params: FromAppParams, options?: AuthProviderOptions);
97
+ /**
98
+ * 获取已认证的 Octokit 实例。
99
+ *
100
+ * 首次调用时构造并缓存;后续调用复用同一实例。
101
+ * installation token 由 `@octokit/auth-app` 内部管理(59 分钟 LRU TTL 缓存)。
102
+ *
103
+ * **构造方式**:使用 `authStrategy: createAppAuth` + `auth: {appId, privateKey, installationId}`
104
+ * 双字段组合(`@octokit/auth-app` v8.x README 标准用法),**而非** `auth: createAppAuth(...)`
105
+ * 单字段调用——后者被 `@octokit/core` 视作字符串 token 路径抛 `Token passed to createTokenAuth is not a string`,
106
+ * 或被 `authStrategy` 字段调用后命中 `auth(state, authOptions)` 的 `default` 分支抛 `Invalid auth type: undefined`
107
+ * (todo.md §M18.4 e2e 验证 + audit quick depth Reject 后补修发现)。
108
+ *
109
+ * @see [@octokit/auth-app README §installation authentication](https://github.com/octokit/auth-app.js#installation-authentication)
110
+ */
111
+ getOctokit(): Octokit;
112
+ /**
113
+ * 获取推送 / clone 用的 Git 凭据。
114
+ *
115
+ * 注意:installation token 由 `@octokit/auth-app` 内部管理,**不**能从 AppAuthProvider 直接获取;
116
+ * 本方法返回的 `token` 字段为占位符(`'installation-token-managed-by-octokit-auth-app'`)。
117
+ * 调用方应通过 `getOctokit()` 拿到的 Octokit 实例进行 API 调用(含 git push / clone 等写操作)。
118
+ */
119
+ getGitCredential(): {
120
+ username: string;
121
+ token: string;
122
+ };
123
+ /**
124
+ * 获取 commit author。
125
+ *
126
+ * 按 GitHub App 协议动态生成 `{app_id}+{bot_login}[bot]@users.noreply.github.com`:
127
+ * - `name`:`{app_id}[bot]`(GitHub 自动识别 bot 身份)
128
+ * - `email`:`{app_id}+{bot_login}[bot]@users.noreply.github.com`(GitHub noreply email 格式)
129
+ *
130
+ * 当 `params.botLogin` 未提供时,使用 `dependfix[bot]` 作为 fallback(保持与 PAT 路径一致的格式);
131
+ * 但建议调用方显式提供 `botLogin` 以确保 commit author 真实 bot 身份归属。
132
+ */
133
+ getCommitAuthor(): {
134
+ name: string;
135
+ email: string;
136
+ };
137
+ }
138
+ //#endregion
139
+ //#region src/auth/installation-token-cache.d.ts
140
+ /**
141
+ * Installation token 缓存层(**M18.1 commit 4 实施**)。
142
+ *
143
+ * 当前实现:**依赖 [@octokit/auth-app](https://github.com/octokit/auth-app.js) 内置缓存**
144
+ * (59 分钟 LRU TTL;GitHub token 有效期 60 分钟 - 1 分钟缓冲)。
145
+ *
146
+ * 本类作为未来扩展点:
147
+ * - 缓存统计(命中率 / 大小 / 清理 API)
148
+ * - 失败重试 + 报警(M18.2 集成时实施)
149
+ * - 多 worker 共享(Redis-backed;超出 M18.1 范围)
150
+ *
151
+ * 注意:
152
+ * - `getOrRefresh` 当前委托给 `createAppAuthentication` + `getInstallationOctokit`
153
+ * - 当 [@octokit/auth-app](https://github.com/octokit/auth-app.js) 内部缓存命中时不会触发 API 调用
154
+ * - 缓存未命中时自动调用 `POST /app/installations/{installation_id}/access_tokens` 获取新 token
155
+ *
156
+ * @see [C22 PAT 无感升级评估 §4.7 commit 拆分纪律](../../../../docs/design/governance/c22-pat-backward-compat.md)
157
+ */
158
+ export declare class InstallationTokenCache {
159
+ private readonly params;
160
+ private readonly ttlMs;
161
+ constructor(params: FromAppParams, options?: {
162
+ ttlMs?: number;
163
+ refreshThresholdMs?: number;
164
+ });
165
+ /**
166
+ * 获取或刷新 installation token。
167
+ *
168
+ * 当前为占位实现:直接返回 `'installation-token-managed-by-octokit-auth-app'` 占位字符串。
169
+ * 调用方应使用 `fromApp(params).getOctokit()` 获取真实 Octokit 实例(由 `@octokit/auth-app`
170
+ * 自动管理 installation token 缓存)。
171
+ *
172
+ * @throws 永不抛错(占位实现)
173
+ */
174
+ getOrRefresh(): Promise<string>;
175
+ /** 清理过期缓存项(占位实现) */
176
+ clear(): void;
177
+ /** 获取缓存 TTL 配置(毫秒) */
178
+ getTtlMs(): number;
179
+ /** 获取 cache key(基于 installationId) */
180
+ static getCacheKey(params: FromAppParams): string;
181
+ }
182
+ //#endregion
183
+ export { AuthProvider, AuthProviderOptions, FromAppParams, FromPatParams, PAT_COMMIT_AUTHOR, PAT_GIT_USERNAME };
184
+ //# sourceMappingURL=auth.d.mts.map
package/dist/auth.mjs ADDED
@@ -0,0 +1,3 @@
1
+ import { a as fromPat, i as PatAuthProvider, n as AppAuthProvider, o as PAT_COMMIT_AUTHOR, r as fromApp, s as PAT_GIT_USERNAME, t as InstallationTokenCache } from "./auth2.mjs";
2
+
3
+ export { AppAuthProvider, InstallationTokenCache, PAT_COMMIT_AUTHOR, PAT_GIT_USERNAME, PatAuthProvider, fromApp, fromPat };
package/dist/auth2.mjs ADDED
@@ -0,0 +1,361 @@
1
+ import { Octokit } from "@octokit/rest";
2
+ import { RequestError } from "@octokit/request-error";
3
+ import { createAppAuth } from "@octokit/auth-app";
4
+
5
+ //#region src/github/client.ts
6
+ /**
7
+ * 创建已认证的 Octokit 实例。
8
+ *
9
+ * 调用方直接使用 `octokit.rest.*` 访问所有已类型化的 GitHub REST API。
10
+ * 分页使用 `octokit.paginate()` 自动合并多页结果。
11
+ *
12
+ * 配置 `retry` 时通过 `hook.wrap('request')` 统一包装所有请求
13
+ * (含 paginate 内部请求),对限流响应做指数退避重试。
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // 推荐:使用 auth 抽象层(M18.1 实施后)
18
+ * import { fromPat } from '@dependfix/engine/auth'
19
+ * const octokit = createGitHubClient({ auth: fromPat('ghp_xxxx') })
20
+ *
21
+ * // 向后兼容:使用 token 字段(deprecated)
22
+ * const octokit = createGitHubClient({ token: 'ghp_xxxx' })
23
+ *
24
+ * // 仓库信息
25
+ * const { data: repo } = await octokit.rest.repos.get({ owner: 'foo', repo: 'bar' })
26
+ *
27
+ * // Dependabot 告警(自动分页)
28
+ * const alerts = await octokit.paginate(
29
+ * octokit.rest.dependabot.listAlertsForRepo,
30
+ * { owner: 'foo', repo: 'bar', state: 'open', per_page: 100 },
31
+ * )
32
+ * ```
33
+ */
34
+ function createGitHubClient(options) {
35
+ if (options.auth) return options.auth.getOctokit();
36
+ if (!options.token) throw new Error("createGitHubClient: must provide either `auth` (AuthProvider) or `token` (deprecated)");
37
+ const client = new Octokit({
38
+ auth: options.token,
39
+ baseUrl: options.baseUrl ?? "https://api.github.com"
40
+ });
41
+ const maxRetries = options.retry?.maxRetries ?? 3;
42
+ if (maxRetries > 0) applyRetryPolicy(client, {
43
+ maxRetries,
44
+ baseDelayMs: options.retry?.baseDelayMs ?? 1e3,
45
+ maxBackoffMs: options.retry?.maxBackoffMs ?? MAX_BACKOFF_MS_DEFAULT
46
+ });
47
+ return client;
48
+ }
49
+ const SECONDARY_RATE_LIMIT_RE = /secondary rate limit|abuse|retry later|retry-after/i;
50
+ /** 指数退避单次等待默认上限(30s),防止长时间空转(可经 retry.maxBackoffMs 覆盖) */
51
+ const MAX_BACKOFF_MS_DEFAULT = 3e4;
52
+ function applyRetryPolicy(client, retry) {
53
+ const maxRetries = retry.maxRetries ?? 3;
54
+ const baseDelayMs = retry.baseDelayMs ?? 1e3;
55
+ const maxBackoffMs = retry.maxBackoffMs ?? MAX_BACKOFF_MS_DEFAULT;
56
+ client.hook.wrap("request", async (request, options) => {
57
+ const method = (options.method ?? "GET").toUpperCase();
58
+ if (method !== "GET" && method !== "HEAD") return request(options);
59
+ let attempt = 0;
60
+ for (;;) try {
61
+ return await request(options);
62
+ } catch (error) {
63
+ const delayMs = computeRetryDelayMs(error, attempt, baseDelayMs, maxBackoffMs);
64
+ if (delayMs === null || attempt >= maxRetries) throw error;
65
+ attempt += 1;
66
+ await sleep(delayMs);
67
+ }
68
+ });
69
+ }
70
+ /**
71
+ * 计算限流重试等待时间(纯函数,可单测)。
72
+ *
73
+ * 可重试(返回等待毫秒):
74
+ * - 429(Too Many Requests / secondary rate limit)
75
+ * - 403 + `x-ratelimit-remaining: 0`(primary rate limit)
76
+ * - 403/429 + message 含 secondary rate limit / abuse / retry 特征
77
+ *
78
+ * 等待策略(Retry-After 优先):
79
+ * 1. `retry-after` 头(秒)→ 等待其秒数(受 maxBackoffMs 上限约束)
80
+ * 2. `x-ratelimit-reset`(unix 秒)→ 等待到 reset + 1s 缓冲
81
+ * 3. 否则 → 指数退避 `baseDelayMs * 2^attempt`
82
+ * 所有等待均受 maxBackoffMs 上限约束(默认 30s)。
83
+ *
84
+ * 不重试(返回 null):
85
+ * - 非 RequestError(网络错误等由上层 AppError 语义处理)
86
+ * - 权限类 403(无限流特征)
87
+ * - 其他状态码
88
+ */
89
+ function computeRetryDelayMs(error, attempt, baseDelayMs = 1e3, maxBackoffMs = MAX_BACKOFF_MS_DEFAULT) {
90
+ if (!(error instanceof RequestError)) return null;
91
+ const status = error.status;
92
+ const headers = error.response?.headers ?? {};
93
+ const remaining = headers["x-ratelimit-remaining"];
94
+ const message = error.message;
95
+ let retryable = false;
96
+ if (status === 429) retryable = true;
97
+ else if (status === 403) {
98
+ if (remaining !== void 0 && remaining === "0") retryable = true;
99
+ else if (SECONDARY_RATE_LIMIT_RE.test(message)) retryable = true;
100
+ }
101
+ if (!retryable) return null;
102
+ const retryAfter = headers["retry-after"];
103
+ if (retryAfter !== void 0) {
104
+ const seconds = Number(retryAfter);
105
+ if (Number.isFinite(seconds) && seconds > 0) return Math.min(seconds * 1e3, maxBackoffMs);
106
+ }
107
+ const reset = headers["x-ratelimit-reset"];
108
+ if (reset !== void 0) {
109
+ const resetMs = Number(reset) * 1e3;
110
+ if (Number.isFinite(resetMs) && resetMs > 0) {
111
+ const waitMs = resetMs - Date.now() + 1e3;
112
+ if (waitMs > 0) return Math.min(waitMs, maxBackoffMs);
113
+ }
114
+ }
115
+ return Math.min(baseDelayMs * 2 ** attempt, maxBackoffMs);
116
+ }
117
+ function sleep(ms) {
118
+ return new Promise((resolve) => setTimeout(resolve, ms));
119
+ }
120
+
121
+ //#endregion
122
+ //#region src/auth/auth-provider.ts
123
+ /**
124
+ * PAT 路径固定 commit author(保持现有行为零变化)。
125
+ *
126
+ * 注:现有 PAT 路径硬编码 `dependfix[bot]@users.noreply.github.com`,虽非真实 bot 身份(字符串约定),
127
+ * 但保持行为不变以确保 PAT 用户无感升级。已知缺陷由 C22 范围之外的后续阶段修复。
128
+ */
129
+ const PAT_COMMIT_AUTHOR = {
130
+ name: "dependfix[bot]",
131
+ email: "dependfix[bot]@users.noreply.github.com"
132
+ };
133
+ /** PAT 路径固定 Git 用户名(用于 git push 时的 Basic Auth) */
134
+ const PAT_GIT_USERNAME = "x-access-token";
135
+
136
+ //#endregion
137
+ //#region src/auth/pat-provider.ts
138
+ /**
139
+ * PAT 工厂函数。委托给 `PatAuthProvider` 类。
140
+ *
141
+ * 保留 `fromPat` 命名习惯便于与 `fromApp` 对齐(统一工厂语义)。
142
+ *
143
+ * @example
144
+ * ```typescript
145
+ * const auth = fromPat('ghp_xxxx')
146
+ * const octokit = auth.getOctokit()
147
+ * const { username, token } = auth.getGitCredential()
148
+ * ```
149
+ */
150
+ function fromPat(token, options) {
151
+ return new PatAuthProvider(token, options);
152
+ }
153
+ /**
154
+ * PAT 认证路径实现。
155
+ *
156
+ * `getOctokit()` 委托给 [`createGitHubClient`](../github/client.ts) 以复用现有 retry policy 与限流重试实现;
157
+ * Octokit 实例缓存以避免重复构造(每次 `new Octokit()` + `hook.wrap()` 都有开销)。
158
+ *
159
+ * 行为与原 `createGitHubClient({ token })` 等价:
160
+ * - `getOctokit()` 返回通过 PAT 认证的 Octokit 实例(含限流重试 hook)
161
+ * - `getGitCredential()` 返回推送用的 Basic Auth(username = `'x-access-token'`)
162
+ * - `getCommitAuthor()` 返回固定 `dependfix[bot]`(PAT 路径用户行为零变化)
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * const auth = new PatAuthProvider('ghp_xxxx', { retry: { maxRetries: 3 } })
167
+ * const octokit = auth.getOctokit()
168
+ * ```
169
+ */
170
+ var PatAuthProvider = class {
171
+ authProvider = "pat";
172
+ token;
173
+ retry;
174
+ cachedOctokit = null;
175
+ constructor(token, options) {
176
+ this.token = token;
177
+ this.retry = options?.retry;
178
+ }
179
+ /**
180
+ * 获取已认证的 Octokit 实例(首次调用构造并缓存)。
181
+ */
182
+ getOctokit() {
183
+ if (!this.cachedOctokit) this.cachedOctokit = createGitHubClient({
184
+ token: this.token,
185
+ retry: this.retry
186
+ });
187
+ return this.cachedOctokit;
188
+ }
189
+ /** 获取推送 / clone 用的 Git 凭据(username = `'x-access-token'`,token = PAT) */
190
+ getGitCredential() {
191
+ return {
192
+ username: PAT_GIT_USERNAME,
193
+ token: this.token
194
+ };
195
+ }
196
+ /** 获取 commit author(PAT 路径固定 `dependfix[bot]`) */
197
+ getCommitAuthor() {
198
+ return { ...PAT_COMMIT_AUTHOR };
199
+ }
200
+ };
201
+
202
+ //#endregion
203
+ //#region src/auth/app-provider.ts
204
+ /**
205
+ * GitHub App 工厂函数(**M18.1 commit 4 完整实施**)。
206
+ *
207
+ * 委托给 [`AppAuthProvider`] 类。
208
+ *
209
+ * @example
210
+ * ```typescript
211
+ * const auth = fromApp({
212
+ * appId: '123456',
213
+ * privateKey: '-----BEGIN RSA PRIVATE KEY-----\n...',
214
+ * installationId: '7890123',
215
+ * botLogin: 'dependfix-bot[bot]',
216
+ * })
217
+ * const octokit = auth.getOctokit()
218
+ * ```
219
+ */
220
+ function fromApp(params, options) {
221
+ return new AppAuthProvider(params, options);
222
+ }
223
+ /**
224
+ * GitHub App 认证路径实现。
225
+ *
226
+ * 使用 [`@octokit/auth-app`](https://github.com/octokit/auth-app.js) 提供:
227
+ * - JWT signing(App 身份认证)
228
+ * - `getInstallationOctokit`(installation token 获取;库内置 59 分钟 LRU TTL 缓存)
229
+ * - 自动 installation token 轮换
230
+ *
231
+ * 注意:
232
+ * - `getGitCredential()` 返回的 `token` 字段为占位符(installation token 不能直接从 AppAuthProvider 获取;
233
+ * 由 [`@octokit/auth-app`](https://github.com/octokit/auth-app.js) 内部管理);调用方应通过 `getOctokit()`
234
+ * 拿到的 Octokit 实例进行 API 调用
235
+ * - `getCommitAuthor()` 按 GitHub App 协议动态生成 `{app_id}+{bot_login}[bot]@users.noreply.github.com`
236
+ * - Octokit 实例缓存以避免重复构造
237
+ *
238
+ * @see [C22 PAT 无感升级评估 §4.2 AuthProvider 接口设计](../../../../docs/design/governance/c22-pat-backward-compat.md)
239
+ */
240
+ var AppAuthProvider = class {
241
+ authProvider = "github-app";
242
+ params;
243
+ _options;
244
+ cachedOctokit = null;
245
+ constructor(params, options) {
246
+ this.params = params;
247
+ this._options = options;
248
+ }
249
+ /**
250
+ * 获取已认证的 Octokit 实例。
251
+ *
252
+ * 首次调用时构造并缓存;后续调用复用同一实例。
253
+ * installation token 由 `@octokit/auth-app` 内部管理(59 分钟 LRU TTL 缓存)。
254
+ *
255
+ * **构造方式**:使用 `authStrategy: createAppAuth` + `auth: {appId, privateKey, installationId}`
256
+ * 双字段组合(`@octokit/auth-app` v8.x README 标准用法),**而非** `auth: createAppAuth(...)`
257
+ * 单字段调用——后者被 `@octokit/core` 视作字符串 token 路径抛 `Token passed to createTokenAuth is not a string`,
258
+ * 或被 `authStrategy` 字段调用后命中 `auth(state, authOptions)` 的 `default` 分支抛 `Invalid auth type: undefined`
259
+ * (todo.md §M18.4 e2e 验证 + audit quick depth Reject 后补修发现)。
260
+ *
261
+ * @see [@octokit/auth-app README §installation authentication](https://github.com/octokit/auth-app.js#installation-authentication)
262
+ */
263
+ getOctokit() {
264
+ if (!this.cachedOctokit) this.cachedOctokit = new Octokit({
265
+ authStrategy: createAppAuth,
266
+ auth: {
267
+ appId: this.params.appId,
268
+ privateKey: this.params.privateKey,
269
+ installationId: this.params.installationId
270
+ },
271
+ baseUrl: "https://api.github.com"
272
+ });
273
+ return this.cachedOctokit;
274
+ }
275
+ /**
276
+ * 获取推送 / clone 用的 Git 凭据。
277
+ *
278
+ * 注意:installation token 由 `@octokit/auth-app` 内部管理,**不**能从 AppAuthProvider 直接获取;
279
+ * 本方法返回的 `token` 字段为占位符(`'installation-token-managed-by-octokit-auth-app'`)。
280
+ * 调用方应通过 `getOctokit()` 拿到的 Octokit 实例进行 API 调用(含 git push / clone 等写操作)。
281
+ */
282
+ getGitCredential() {
283
+ return {
284
+ username: "x-access-token",
285
+ token: "installation-token-managed-by-octokit-auth-app"
286
+ };
287
+ }
288
+ /**
289
+ * 获取 commit author。
290
+ *
291
+ * 按 GitHub App 协议动态生成 `{app_id}+{bot_login}[bot]@users.noreply.github.com`:
292
+ * - `name`:`{app_id}[bot]`(GitHub 自动识别 bot 身份)
293
+ * - `email`:`{app_id}+{bot_login}[bot]@users.noreply.github.com`(GitHub noreply email 格式)
294
+ *
295
+ * 当 `params.botLogin` 未提供时,使用 `dependfix[bot]` 作为 fallback(保持与 PAT 路径一致的格式);
296
+ * 但建议调用方显式提供 `botLogin` 以确保 commit author 真实 bot 身份归属。
297
+ */
298
+ getCommitAuthor() {
299
+ const botLogin = this.params.botLogin ?? "dependfix[bot]";
300
+ const appId = this.params.appId;
301
+ return {
302
+ name: `${appId}[bot]`,
303
+ email: `${appId}+${botLogin}@users.noreply.github.com`
304
+ };
305
+ }
306
+ };
307
+
308
+ //#endregion
309
+ //#region src/auth/installation-token-cache.ts
310
+ /**
311
+ * Installation token 缓存层(**M18.1 commit 4 实施**)。
312
+ *
313
+ * 当前实现:**依赖 [@octokit/auth-app](https://github.com/octokit/auth-app.js) 内置缓存**
314
+ * (59 分钟 LRU TTL;GitHub token 有效期 60 分钟 - 1 分钟缓冲)。
315
+ *
316
+ * 本类作为未来扩展点:
317
+ * - 缓存统计(命中率 / 大小 / 清理 API)
318
+ * - 失败重试 + 报警(M18.2 集成时实施)
319
+ * - 多 worker 共享(Redis-backed;超出 M18.1 范围)
320
+ *
321
+ * 注意:
322
+ * - `getOrRefresh` 当前委托给 `createAppAuthentication` + `getInstallationOctokit`
323
+ * - 当 [@octokit/auth-app](https://github.com/octokit/auth-app.js) 内部缓存命中时不会触发 API 调用
324
+ * - 缓存未命中时自动调用 `POST /app/installations/{installation_id}/access_tokens` 获取新 token
325
+ *
326
+ * @see [C22 PAT 无感升级评估 §4.7 commit 拆分纪律](../../../../docs/design/governance/c22-pat-backward-compat.md)
327
+ */
328
+ var InstallationTokenCache = class {
329
+ params;
330
+ ttlMs;
331
+ constructor(params, options) {
332
+ this.params = params;
333
+ this.ttlMs = options?.ttlMs ?? 354e4;
334
+ }
335
+ /**
336
+ * 获取或刷新 installation token。
337
+ *
338
+ * 当前为占位实现:直接返回 `'installation-token-managed-by-octokit-auth-app'` 占位字符串。
339
+ * 调用方应使用 `fromApp(params).getOctokit()` 获取真实 Octokit 实例(由 `@octokit/auth-app`
340
+ * 自动管理 installation token 缓存)。
341
+ *
342
+ * @throws 永不抛错(占位实现)
343
+ */
344
+ async getOrRefresh() {
345
+ return "installation-token-managed-by-octokit-auth-app";
346
+ }
347
+ /** 清理过期缓存项(占位实现) */
348
+ clear() {}
349
+ /** 获取缓存 TTL 配置(毫秒) */
350
+ getTtlMs() {
351
+ return this.ttlMs;
352
+ }
353
+ /** 获取 cache key(基于 installationId) */
354
+ static getCacheKey(params) {
355
+ return `installation:${params.installationId}`;
356
+ }
357
+ };
358
+
359
+ //#endregion
360
+ export { fromPat as a, computeRetryDelayMs as c, PatAuthProvider as i, createGitHubClient as l, AppAuthProvider as n, PAT_COMMIT_AUTHOR as o, fromApp as r, PAT_GIT_USERNAME as s, InstallationTokenCache as t };
361
+ //# sourceMappingURL=auth2.mjs.map