dependfix 0.0.0 → 0.1.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 +59 -78
- package/dist/bin.cjs +14 -0
- package/dist/bin.cjs.map +1 -0
- package/dist/bin.d.cts +1 -0
- package/dist/bin.d.mts +1 -0
- package/dist/bin.mjs +15 -0
- package/dist/bin.mjs.map +1 -0
- package/dist/cli.cjs +2812 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.mjs +2530 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.cjs +66 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +667 -7
- package/dist/index.d.mts +667 -7
- package/dist/index.mjs +18 -38
- package/dist/index.mjs.map +1 -1
- package/package.json +53 -67
package/dist/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["RequestError","rollback","isRuntimeMode","isSeverityThreshold"],"sources":["../src/github/client.ts","../../../node_modules/.pnpm/deprecation@2.3.1/node_modules/deprecation/dist-node/index.js","../../../node_modules/.pnpm/wrappy@1.0.2/node_modules/wrappy/wrappy.js","../../../node_modules/.pnpm/once@1.4.0/node_modules/once/once.js","../../../node_modules/.pnpm/@octokit+request-error@5.1.1/node_modules/@octokit/request-error/dist-node/index.js","../src/github/errors.ts","../src/github/dependabot-fetcher.ts","../src/github/pr-creator.ts","../src/fixers/dependency/index.ts","../src/fixers/pnpm/index.ts","../src/runners/verification-runner.ts","../src/app-helpers.ts","../src/app.ts","../src/github/repo-selector.ts","../src/config/index.ts","../src/cli/index.ts"],"sourcesContent":["import { Octokit } from '@octokit/rest'\n\nexport interface OctokitClientOptions {\n /** GitHub Personal Access Token */\n token: string\n /**\n * API 基地址。\n * 默认 `https://api.github.com`。\n * 测试时指向 nock 拦截的同一地址。\n */\n baseUrl?: string\n}\n\n/**\n * 创建已认证的 Octokit 实例。\n *\n * 调用方直接使用 `octokit.rest.*` 访问所有已类型化的 GitHub REST API。\n * 分页使用 `octokit.paginate()` 自动合并多页结果。\n *\n * @example\n * ```typescript\n * const octokit = createGitHubClient({ token: 'ghp_xxxx' })\n *\n * // 仓库信息\n * const { data: repo } = await octokit.rest.repos.get({ owner: 'foo', repo: 'bar' })\n *\n * // Dependabot 告警(自动分页)\n * const alerts = await octokit.paginate(\n * octokit.rest.dependabot.listAlertsForRepo,\n * { owner: 'foo', repo: 'bar', state: 'open', per_page: 100 },\n * )\n * ```\n */\nexport function createGitHubClient(options: OctokitClientOptions): Octokit {\n return new Octokit({\n auth: options.token,\n baseUrl: options.baseUrl ?? 'https://api.github.com',\n })\n}\n","'use strict';\n\nObject.defineProperty(exports, '__esModule', { value: true });\n\nclass Deprecation extends Error {\n constructor(message) {\n super(message); // Maintains proper stack trace (only available on V8)\n\n /* istanbul ignore next */\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n\n this.name = 'Deprecation';\n }\n\n}\n\nexports.Deprecation = Deprecation;\n","// Returns a wrapper function that returns a wrapped callback\n// The wrapper function should do some stuff, and return a\n// presumably different callback function.\n// This makes sure that own properties are retained, so that\n// decorations and such are not lost along the way.\nmodule.exports = wrappy\nfunction wrappy (fn, cb) {\n if (fn && cb) return wrappy(fn)(cb)\n\n if (typeof fn !== 'function')\n throw new TypeError('need wrapper function')\n\n Object.keys(fn).forEach(function (k) {\n wrapper[k] = fn[k]\n })\n\n return wrapper\n\n function wrapper() {\n var args = new Array(arguments.length)\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i]\n }\n var ret = fn.apply(this, args)\n var cb = args[args.length-1]\n if (typeof ret === 'function' && ret !== cb) {\n Object.keys(cb).forEach(function (k) {\n ret[k] = cb[k]\n })\n }\n return ret\n }\n}\n","var wrappy = require('wrappy')\nmodule.exports = wrappy(once)\nmodule.exports.strict = wrappy(onceStrict)\n\nonce.proto = once(function () {\n Object.defineProperty(Function.prototype, 'once', {\n value: function () {\n return once(this)\n },\n configurable: true\n })\n\n Object.defineProperty(Function.prototype, 'onceStrict', {\n value: function () {\n return onceStrict(this)\n },\n configurable: true\n })\n})\n\nfunction once (fn) {\n var f = function () {\n if (f.called) return f.value\n f.called = true\n return f.value = fn.apply(this, arguments)\n }\n f.called = false\n return f\n}\n\nfunction onceStrict (fn) {\n var f = function () {\n if (f.called)\n throw new Error(f.onceError)\n f.called = true\n return f.value = fn.apply(this, arguments)\n }\n var name = fn.name || 'Function wrapped with `once`'\n f.onceError = name + \" shouldn't be called more than once\"\n f.called = false\n return f\n}\n","\"use strict\";\nvar __create = Object.create;\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(\n // If the importer is in node compatibility mode or this is not an ESM\n // file that has been converted to a CommonJS file using a Babel-\n // compatible transform (i.e. \"__esModule\" has not been set), then set\n // \"default\" to the CommonJS \"module.exports\" for node compatibility.\n isNodeMode || !mod || !mod.__esModule ? __defProp(target, \"default\", { value: mod, enumerable: true }) : target,\n mod\n));\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// pkg/dist-src/index.js\nvar dist_src_exports = {};\n__export(dist_src_exports, {\n RequestError: () => RequestError\n});\nmodule.exports = __toCommonJS(dist_src_exports);\nvar import_deprecation = require(\"deprecation\");\nvar import_once = __toESM(require(\"once\"));\nvar logOnceCode = (0, import_once.default)((deprecation) => console.warn(deprecation));\nvar logOnceHeaders = (0, import_once.default)((deprecation) => console.warn(deprecation));\nvar RequestError = class extends Error {\n constructor(message, statusCode, options) {\n super(message);\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n this.name = \"HttpError\";\n this.status = statusCode;\n let headers;\n if (\"headers\" in options && typeof options.headers !== \"undefined\") {\n headers = options.headers;\n }\n if (\"response\" in options) {\n this.response = options.response;\n headers = options.response.headers;\n }\n const requestCopy = Object.assign({}, options.request);\n if (options.request.headers.authorization) {\n requestCopy.headers = Object.assign({}, options.request.headers, {\n authorization: options.request.headers.authorization.replace(\n /(?<! ) .*$/,\n \" [REDACTED]\"\n )\n });\n }\n requestCopy.url = requestCopy.url.replace(/\\bclient_secret=\\w+/g, \"client_secret=[REDACTED]\").replace(/\\baccess_token=\\w+/g, \"access_token=[REDACTED]\");\n this.request = requestCopy;\n Object.defineProperty(this, \"code\", {\n get() {\n logOnceCode(\n new import_deprecation.Deprecation(\n \"[@octokit/request-error] `error.code` is deprecated, use `error.status`.\"\n )\n );\n return statusCode;\n }\n });\n Object.defineProperty(this, \"headers\", {\n get() {\n logOnceHeaders(\n new import_deprecation.Deprecation(\n \"[@octokit/request-error] `error.headers` is deprecated, use `error.response.headers`.\"\n )\n );\n return headers || {};\n }\n });\n }\n};\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n RequestError\n});\n","import { AppError, toErrorMessage, type GitHubErrorCode } from '@dependfix/core'\nimport { RequestError } from '@octokit/request-error'\n\n/**\n * 将 Octokit 请求错误映射为 `AppError`。\n *\n * @param error - 原始错误(`RequestError` 或网络异常)\n * @param context - 调用上下文描述(如 `'fetch repo info for foo/bar'`),会附加到错误消息中\n */\nexport function mapGitHubError(error: unknown, context: string): AppError {\n if (error instanceof RequestError) {\n const code: GitHubErrorCode = resolveErrorCode(error)\n const details = collectErrorDetails(error)\n\n return new AppError(code, `${context}: ${error.message}`, { cause: error, details })\n }\n\n // 网络 / DNS 等非 RequestError 异常\n if (error instanceof Error) {\n return new AppError('NETWORK_ERROR', `${context}: ${error.message}`, { cause: error })\n }\n\n return new AppError('NETWORK_ERROR', `${context}: unknown error`, { details: { raw: toErrorMessage(error) } })\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction resolveErrorCode(error: RequestError): GitHubErrorCode {\n switch (error.status) {\n case 401:\n return 'AUTHENTICATION_FAILED'\n case 403: {\n const remaining = error.response?.headers['x-ratelimit-remaining']\n if (remaining !== undefined && remaining === '0') {\n return 'RATE_LIMITED'\n }\n return 'PERMISSION_DENIED'\n }\n case 404:\n return 'REPO_NOT_FOUND'\n default:\n return 'GITHUB_API_ERROR'\n }\n}\n\nfunction collectErrorDetails(error: RequestError): Record<string, unknown> {\n const details: Record<string, unknown> = {\n status: error.status,\n requestUrl: error.request?.url,\n requestMethod: error.request?.method,\n }\n\n // 限流时附带重置时间\n const resetAt = error.response?.headers['x-ratelimit-reset']\n if (resetAt) {\n details.rateLimitReset = resetAt\n }\n\n return details\n}\n","import type { Octokit, RestEndpointMethodTypes } from '@octokit/rest'\nimport type { NormalizedSecurityAlert, DependencyType } from '@dependfix/core'\nimport { mapGitHubError } from './errors'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface FetchDependabotAlertsParams {\n /** 仓库所属组织或用户 */\n owner: string\n /** 仓库名称 */\n repo: string\n /**\n * 告警状态过滤。\n * M1 只拉 `open`(默认)。\n */\n state?: 'open' | 'fixed' | 'dismissed' | 'auto_dismissed'\n /** 每页数量,默认 100(GitHub API 最大值) */\n perPage?: number\n}\n\n/**\n * Dependabot API 返回的单条原始告警类型。\n * 从 `@octokit/rest` 的 `RestEndpointMethodTypes` 推导,无需手写。\n */\ntype DependabotAlertItem =\n RestEndpointMethodTypes['dependabot']['listAlertsForRepo']['response']['data'][number]\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * 拉取指定仓库的 Dependabot 告警并映射为标准化模型。\n *\n * - 自动分页(`octokit.paginate()`),返回所有匹配的告警\n * - 仅处理 `state=open` 的告警(可通过 `params.state` 覆盖)\n * - 异常通过 `mapGitHubError` 转为 `AppError`\n * - 空仓库返回 `[]`,不抛异常\n *\n * @param client - 已认证的 Octokit 实例(来自 `createGitHubClient`)\n * @param params - 仓库标识与过滤参数\n * @returns 标准化告警列表\n *\n * @example\n * ```typescript\n * const octokit = createGitHubClient({ token: 'ghp_xxxx' })\n * const alerts = await fetchDependabotAlerts(octokit, { owner: 'foo', repo: 'bar' })\n * ```\n */\nexport async function fetchDependabotAlerts(\n client: Octokit,\n params: FetchDependabotAlertsParams,\n): Promise<NormalizedSecurityAlert[]> {\n const { owner, repo, state = 'open', perPage = 100 } = params\n\n try {\n const rawAlerts = await client.paginate(\n client.rest.dependabot.listAlertsForRepo,\n { owner, repo, state, per_page: perPage },\n )\n return rawAlerts.map((alert) => normalizeAlert(alert, owner, repo))\n } catch (error: unknown) {\n const context = `fetch dependabot alerts for ${owner}/${repo}`\n throw mapGitHubError(error, context)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * 将单条 Dependabot API 原始告警转换为标准化模型。\n *\n * 映射逻辑:\n * - severity 直接透传(Dependabot 的 `low|medium|high|critical` 与 `AlertSeverity` 对齐)\n * - fixable 判定:`security_vulnerability.first_patched_version !== null`\n * - fixStrategy:有修复版本 → `'upgrade'`,否则 `null`\n * - recommendedVersion 取自 `first_patched_version.identifier`\n * - 可选字段兜底:`packageName`/`packageEcosystem` → `'unknown'`,`manifestPath` → `''`\n * - `defaultBranch` 不在此层填充(由上层调用方获取后注入)\n */\nfunction normalizeAlert(\n alert: DependabotAlertItem,\n owner: string,\n repo: string,\n): NormalizedSecurityAlert {\n const firstPatched = alert.security_vulnerability.first_patched_version\n const fixable = firstPatched !== null\n\n return {\n id: alert.number,\n source: 'dependabot',\n repository: `${owner}/${repo}`,\n defaultBranch: '', // 由上层调用 octokit.rest.repos.get 后填入\n severity: alert.security_advisory.severity,\n packageEcosystem: alert.dependency.package?.ecosystem ?? 'unknown',\n packageName: alert.dependency.package?.name ?? 'unknown',\n manifestPath: alert.dependency.manifest_path ?? '',\n ruleId: alert.security_advisory.ghsa_id,\n summary: alert.security_advisory.summary,\n htmlUrl: alert.html_url,\n fixable,\n fixStrategy: fixable ? 'upgrade' : null,\n recommendedVersion: firstPatched?.identifier ?? '',\n dependencyType: normalizeDependencyRelationship(alert.dependency.relationship),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers (continued)\n// ---------------------------------------------------------------------------\n\n/**\n * 将 Dependabot API 的 `dependency.relationship` 映射为 `DependencyType`。\n *\n * GitHub API 返回的合法值:\n * - `'direct'` → 直接依赖\n * - `'indirect'` → 间接依赖(transitive)\n * - `null`/`undefined` → 未知\n */\nfunction normalizeDependencyRelationship(\n relationship: string | null | undefined,\n): DependencyType | undefined {\n if (relationship === 'direct') {\n return 'direct'\n }\n if (relationship === 'indirect') {\n return 'transitive'\n }\n return undefined\n}\n","import { execSync } from 'node:child_process'\nimport { createHash } from 'node:crypto'\nimport type { Octokit } from '@octokit/rest'\nimport type { FixAction, RunResult } from '@dependfix/core'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface CreatePullRequestParams {\n /** 已认证的 Octokit 实例 */\n octokit: Octokit\n /** 仓库 owner */\n owner: string\n /** 仓库名 */\n repo: string\n /** 修复分支名 */\n headBranch: string\n /** 目标分支(默认 master/main,从 API 获取) */\n baseBranch: string\n /** PR 标题 */\n title: string\n /** PR 内容(Markdown) */\n body: string\n}\n\nexport interface PullRequestResult {\n /** PR 编号 */\n number: number\n /** PR HTML URL */\n htmlUrl: string\n}\n\nexport interface FixBranchResult {\n /** 分支名 */\n branchName: string\n /** 是否为新创建 */\n created: boolean\n}\n\n/** dependfix 自动修复 PR 的公开信息(用于查重/取代判定) */\nexport interface DependfixOpenPR {\n /** PR 编号 */\n number: number\n /** PR HTML URL */\n htmlUrl: string\n /** PR head 分支名 */\n headRef: string\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst BOT_NAME = 'dependfix[bot]'\nconst BOT_EMAIL = 'dependfix[bot]@users.noreply.github.com'\n\n/** 自动修复分支统一前缀(T210 起分支名 = 前缀 + 内容指纹 8 位) */\nconst BRANCH_PREFIX = 'dependfix/auto-fix-'\n\n// ---------------------------------------------------------------------------\n// Fingerprint\n// ---------------------------------------------------------------------------\n\n/**\n * 计算修复内容指纹(sha256 前 8 位)。\n *\n * 指纹基于**结构化修复结果**而非 git diff:\n * - 成功升级集:`pkg@toVersion`(排序拼接)\n * - 修复失败集:失败包名(排序拼接)\n * - lockfile 修复状态(成功/失败)\n *\n * 同告警集 → 同修复结果 → 同指纹 → 幂等跳过;\n * 内容变化(升级集/失败集/锁文件修复变化)→ 指纹变化 → 关旧开新。\n * 不用 diff hash 是因为 pnpm 版本等非确定性因素会使锁文件内容漂移,\n * 导致\"告警没变但指纹变\"。\n */\nexport function computeFixFingerprint(actions: FixAction[]): string {\n // 排除 PR 记录 action(target 为 \"PR #N\"),它们不代表修复内容本身\n const isUpgrade = (a: FixAction): boolean => a.type === 'dependency-upgrade' && !!a.target && !a.target.startsWith('PR #')\n\n const upgrades = actions\n .filter((a) => isUpgrade(a) && a.success && a.toVersion)\n .map((a) => `${a.target}@${a.toVersion}`)\n .sort()\n\n const failures = actions\n .filter((a) => isUpgrade(a) && !a.success)\n .map((a) => a.target)\n .sort()\n\n const repairs = actions\n .filter((a) => a.type === 'lockfile-repair')\n .map((a) => `${a.target}:${a.success}`)\n .sort()\n\n const payload = JSON.stringify([upgrades, failures, repairs])\n return createHash('sha256').update(payload).digest('hex').slice(0, 8)\n}\n\n/**\n * 从分支名提取内容指纹(`dependfix/auto-fix-{fp8}` → `{fp8}`)。\n * 指纹格式为 8 位 hex;分支名不含 `-` 后的内容、前缀不匹配或格式不合法时返回 null\n * (如旧版 runId 分支,会被 supersede)。\n */\nexport function extractFingerprintFromBranch(branchName: string): string | null {\n if (!branchName.startsWith(BRANCH_PREFIX)) {\n return null\n }\n const fingerprint = branchName.slice(BRANCH_PREFIX.length)\n return /^[0-9a-f]{8}$/.test(fingerprint) ? fingerprint : null\n}\n\n/** fix-and-pr 去重决策结果 */\nexport interface FixAndPrPlan {\n /** skip = 已有同内容 PR,不重复提交;create = 需要新建 PR */\n action: 'skip' | 'create'\n /** action=skip 时命中的同内容 PR */\n sameContentPR?: DependfixOpenPR\n /** 内容不同、需要关闭(supersede)的旧 PR 列表 */\n supersedePRs: DependfixOpenPR[]\n}\n\n/**\n * 计算 fix-and-pr 去重决策:\n * - 存在同指纹 open PR → skip(仅需关闭其余异指纹 PR)\n * - 否则 → create(关闭全部旧 PR,先建新后关旧由调用方保证)\n */\nexport function computeFixAndPrPlan(existingPRs: DependfixOpenPR[], fingerprint: string): FixAndPrPlan {\n const sameContentPR = existingPRs.find(\n (pr) => extractFingerprintFromBranch(pr.headRef) === fingerprint,\n )\n\n if (sameContentPR) {\n return {\n action: 'skip',\n sameContentPR,\n supersedePRs: existingPRs.filter((pr) => pr !== sameContentPR),\n }\n }\n\n return { action: 'create', supersedePRs: existingPRs }\n}\n\n// ---------------------------------------------------------------------------\n// Git Operations\n// ---------------------------------------------------------------------------\n\n/**\n * 在工作目录创建修复分支。\n *\n * - 分支名由调用方传入(T210 起为 `dependfix/auto-fix-{内容指纹}`,不再依赖 runId)\n * - 如果分支已存在(如重跑),切换到该分支\n *\n * @returns 分支名和是否为新创建\n */\nexport function createFixBranch(branchName: string, workDir: string): FixBranchResult {\n const exists = branchExists(branchName, workDir)\n if (exists) {\n execSync(`git checkout ${branchName}`, { cwd: workDir, stdio: 'pipe' })\n return { branchName, created: false }\n }\n\n execSync(`git checkout -b ${branchName}`, { cwd: workDir, stdio: 'pipe' })\n return { branchName, created: true }\n}\n\n/**\n * 暂存所有变更并提交。\n * 自动设置 git user.name / user.email(如未设置)。\n */\nexport function stageAndCommit(message: string, workDir: string): void {\n ensureGitConfig(workDir)\n execSync('git add .', { cwd: workDir, stdio: 'pipe' })\n execSync(`git commit -m \"${escapeShell(message)}\"`, { cwd: workDir, stdio: 'pipe' })\n}\n\n/**\n * 推送分支到远程 origin。\n */\nexport function pushBranch(branchName: string, workDir: string): void {\n execSync(`git push origin ${branchName}`, { cwd: workDir, stdio: 'pipe' })\n}\n\n// ---------------------------------------------------------------------------\n// GitHub API\n// ---------------------------------------------------------------------------\n\n/**\n * 查找目标仓库中所有 open 状态的 dependfix 自动修复 PR。\n *\n * GitHub 的 `pulls.list` 的 `head` 参数不支持通配符,因此拉取 open PR 列表后\n * 按 head 分支前缀 `dependfix/auto-fix-` 过滤。假设 PR 数量不多(单页 100 足够),\n * 未来 PR 数量增大时可引入 label 索引(见 backlog B1)。\n */\nexport async function findDependfixOpenPR(\n octokit: Octokit,\n owner: string,\n repo: string,\n): Promise<DependfixOpenPR[]> {\n const { data } = await octokit.rest.pulls.list({\n owner,\n repo,\n state: 'open',\n per_page: 100,\n })\n\n return data\n .filter((pr) => pr.head.ref.startsWith(BRANCH_PREFIX))\n .map((pr) => ({\n number: pr.number,\n htmlUrl: pr.html_url,\n headRef: pr.head.ref,\n }))\n}\n\n/**\n * 关闭 Pull Request(用于 supersede 旧 PR)。\n */\nexport async function closePullRequest(\n octokit: Octokit,\n owner: string,\n repo: string,\n pullNumber: number,\n): Promise<void> {\n await octokit.rest.pulls.update({\n owner,\n repo,\n pull_number: pullNumber,\n state: 'closed',\n })\n}\n\n/**\n * 通过 GitHub API 创建 Pull Request。\n */\nexport async function createPullRequest(params: CreatePullRequestParams): Promise<PullRequestResult> {\n const { octokit, owner, repo, headBranch, baseBranch, title, body } = params\n\n const { data } = await octokit.rest.pulls.create({\n owner,\n repo,\n title,\n body,\n head: headBranch,\n base: baseBranch,\n })\n\n return {\n number: data.number,\n htmlUrl: data.html_url,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Branch cleanup API\n// ---------------------------------------------------------------------------\n\n/** 分支清理状态(用于 cleanup-branches 清单分类) */\nexport interface DependfixBranchStatus {\n /** 分支名 */\n branch: string\n /** 关联 PR 编号(无 PR 记录时为 null) */\n prNumber: number | null\n /** PR 是否已合并 */\n merged: boolean\n /** PR 是否已关闭(含合并) */\n closed: boolean\n}\n\n/**\n * 列出远端所有 `dependfix/` 前缀分支(git.listMatchingRefs 精确前缀匹配)。\n */\nexport async function listDependfixBranches(\n octokit: Octokit,\n owner: string,\n repo: string,\n): Promise<string[]> {\n const { data } = await octokit.rest.git.listMatchingRefs({\n owner,\n repo,\n ref: 'heads/dependfix',\n per_page: 100,\n })\n\n return data\n .map((ref) => ref.ref.replace(/^refs\\/heads\\//, ''))\n .filter((name) => name.startsWith('dependfix/'))\n}\n\n/**\n * 查询分支对应的最近 PR 状态(pulls.list head 精确匹配,按最近更新取 1 条)。\n */\nexport async function getBranchPrStatus(\n octokit: Octokit,\n owner: string,\n repo: string,\n branch: string,\n): Promise<DependfixBranchStatus> {\n const { data } = await octokit.rest.pulls.list({\n owner,\n repo,\n head: `${owner}:${branch}`,\n state: 'all',\n sort: 'updated',\n direction: 'desc',\n per_page: 1,\n })\n\n const pr = data[0]\n if (!pr) {\n return { branch, prNumber: null, merged: false, closed: false }\n }\n\n // pulls.list 摘要不含 `merged` 字段,用 `state=closed + merged_at 非空` 判定已合并\n return {\n branch,\n prNumber: pr.number,\n merged: pr.state === 'closed' && pr.merged_at !== null,\n closed: pr.state === 'closed',\n }\n}\n\n/**\n * 删除远端分支(git ref delete)。失败(分支保护、不存在)由调用方捕获处理。\n */\nexport async function deleteRemoteBranch(\n octokit: Octokit,\n owner: string,\n repo: string,\n branch: string,\n): Promise<void> {\n await octokit.rest.git.deleteRef({\n owner,\n repo,\n ref: `heads/${branch}`,\n })\n}\n\n/**\n * 判定交互确认回答是否为\"是\"(y/yes,大小写不敏感;空输入默认拒绝)。\n */\nexport function isConfirmAnswer(answer: string): boolean {\n return /^y(es)?$/i.test(answer.trim())\n}\n\n// ---------------------------------------------------------------------------\n// Report Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * 从 RunResult 生成 PR body(Markdown)。\n *\n * @param supersededNumbers 被本 PR 取代并已关闭的旧 PR 编号列表(用于 Supersedes 声明)\n */\nexport function generatePRBody(result: RunResult, supersededNumbers?: number[]): string {\n const { summary, actions, errors } = result\n\n const lines: string[] = [\n '## 🔒 Dependfix Auto Fix',\n '',\n `**Run ID**: \\`${result.runId}\\``,\n `**Mode**: ${result.config.mode}`,\n `**Time**: ${result.startedAt}`,\n '',\n '### 📊 Summary',\n '',\n '| Metric | Value |',\n '|--------|-------|',\n `| Repositories scanned | ${summary.repositoriesScanned} |`,\n `| Alerts found | ${summary.alertsFound} |`,\n `| Alerts fixable | ${summary.alertsFixable} |`,\n `| Dependencies upgraded | ${summary.alertsFixed} |`,\n `| Upgrades failed | ${summary.alertsFailed} |`,\n `| Lockfile repairs | ${summary.lockfileRepairs} |`,\n `| Verifications passed | ${summary.verificationsPassed} |`,\n `| Verifications failed | ${summary.verificationsFailed} |`,\n '',\n ]\n\n // Upgrade actions\n const upgrades = actions.filter((a) => a.type === 'dependency-upgrade' && a.success)\n if (upgrades.length > 0) {\n lines.push('### 📦 Upgraded Dependencies', '')\n lines.push('| Package | From | To | Major |')\n lines.push('|---------|------|----|-------|')\n for (const a of upgrades) {\n const major = a.isMajor ? '⚠️ Yes' : 'No'\n lines.push(`| \\`${a.target}\\` | ${a.fromVersion ?? '-'} | ${a.toVersion ?? '-'} | ${major} |`)\n }\n lines.push('')\n }\n\n // Failed upgrades\n const failures = actions.filter((a) => a.type === 'dependency-upgrade' && !a.success)\n if (failures.length > 0) {\n lines.push('### ⚠️ Failed Upgrades', '')\n for (const a of failures) {\n lines.push(`- **\\`${a.target}\\`**: ${a.error ?? 'unknown error'}`)\n }\n lines.push('')\n }\n\n // Verification\n const verifications = actions.filter((a) => a.type === 'verification')\n if (verifications.length > 0) {\n lines.push('### ✅ Verification', '')\n for (const v of verifications) {\n const icon = v.success ? '✅' : '❌'\n lines.push(`- ${icon} \\`${v.target}\\` ${v.error ? `(${v.error})` : ''}`)\n }\n lines.push('')\n }\n\n // Errors\n if (errors.length > 0) {\n lines.push('### 🚨 Errors', '')\n for (const e of errors) {\n lines.push(`- **${e.stage}**: ${e.message}`)\n }\n lines.push('')\n }\n\n lines.push('---', '', '*This PR was automatically created by [dependfix](https://github.com/dependfix/dependfix).*')\n\n if (supersededNumbers && supersededNumbers.length > 0) {\n lines.push('', `> **Supersedes**: ${supersededNumbers.map((n) => `#${n}`).join(', ')}(内容已更新,将取代旧 PR)`)\n }\n\n return lines.join('\\n')\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction branchExists(branchName: string, workDir: string): boolean {\n try {\n execSync(`git rev-parse --verify ${branchName}`, {\n cwd: workDir,\n stdio: 'pipe',\n })\n return true\n } catch {\n return false\n }\n}\n\nfunction ensureGitConfig(workDir: string): void {\n const hasName = gitConfigExists('user.name', workDir)\n const hasEmail = gitConfigExists('user.email', workDir)\n\n if (!hasName) {\n execSync(`git config user.name \"${BOT_NAME}\"`, { cwd: workDir, stdio: 'pipe' })\n }\n if (!hasEmail) {\n execSync(`git config user.email \"${BOT_EMAIL}\"`, { cwd: workDir, stdio: 'pipe' })\n }\n}\n\nfunction gitConfigExists(key: string, workDir: string): boolean {\n try {\n execSync(`git config ${key}`, { cwd: workDir, stdio: 'pipe' })\n return true\n } catch {\n return false\n }\n}\n\nfunction escapeShell(str: string): string {\n return str.replace(/\"/g, '\\\\\"')\n}\n","import { execSync } from 'node:child_process'\nimport { copyFileSync, existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport YAML from 'yaml'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface UpgradeDependencyParams {\n /** 包名(如 `'lodash'`, `'@babel/traverse'`) */\n packageName: string\n /** 目标精确版本(如 `'4.17.21'`) */\n targetVersion: string\n /** 工作目录(包含 `package.json` 和 `pnpm-lock.yaml`) */\n workDir: string\n}\n\nexport interface DependencyFixResult {\n packageName: string\n /** 升级前版本声明(保留原始 range 格式,如 `^4.17.20`) */\n fromVersion: string\n /** 升级后版本声明(保留原始前缀,如 `^4.17.21`) */\n toVersion: string\n /** 是否为 major 版本升级 */\n isMajor: boolean\n /** 升级是否成功 */\n success: boolean\n /** 失败原因(仅 `success=false` 时有值) */\n error?: string\n}\n\ninterface PackageJson {\n name?: string\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n optionalDependencies?: Record<string, string>\n peerDependencies?: Record<string, string>\n pnpm?: {\n overrides?: Record<string, string>\n [key: string]: unknown\n }\n [key: string]: unknown\n}\n\ninterface DependencyInfo {\n group: 'dependencies' | 'devDependencies' | 'optionalDependencies'\n version: string\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * 升级单个依赖到指定版本。\n *\n * - 在 `workDir` 中查找 `package.json` 并修改目标包的版本声明(保留原始前缀)\n * - 执行 `pnpm install --no-frozen-lockfile` 更新 lockfile\n * - 失败时自动回滚 `package.json` 和 `pnpm-lock.yaml`\n * - 不执行验证(由 T107 负责)\n *\n * @param params - 包名、目标版本、工作目录\n * @returns 修复结果\n */\nexport async function upgradeDependency(\n params: UpgradeDependencyParams,\n): Promise<DependencyFixResult> {\n const { packageName, targetVersion, workDir } = params\n const pkgPath = join(workDir, 'package.json')\n const lockfilePath = join(workDir, 'pnpm-lock.yaml')\n\n // ---- 1. 读取 package.json ----\n if (!existsSync(pkgPath)) {\n return failResult(packageName, targetVersion, '', `${pkgPath}: file not found`)\n }\n\n let pkg: PackageJson\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as PackageJson\n } catch {\n return failResult(packageName, targetVersion, '', `${pkgPath}: invalid JSON`)\n }\n\n // ---- 2. 查找目标包 ----\n const dep = findDependencyVersion(pkg, packageName)\n if (!dep) {\n return failResult(\n packageName,\n targetVersion,\n '',\n `package \"${packageName}\" not found in dependencies / devDependencies / optionalDependencies`,\n )\n }\n\n const fromVersion = dep.version\n const prefix = extractPrefix(fromVersion)\n const toVersion = `${prefix}${targetVersion}`\n const isMajor = parseMajorVersion(fromVersion) !== parseMajorVersion(toVersion)\n && parseMajorVersion(fromVersion) !== -1\n && parseMajorVersion(toVersion) !== -1\n\n // ---- 3. 备份 ----\n const pkgBackup = `${pkgPath}.bak`\n const lockBackup = `${lockfilePath}.bak`\n\n try {\n copyFileSync(pkgPath, pkgBackup)\n if (existsSync(lockfilePath)) {\n copyFileSync(lockfilePath, lockBackup)\n }\n } catch {\n return failResult(packageName, targetVersion, fromVersion, 'failed to create backup files')\n }\n\n // ---- 4. 修改 package.json ----\n const deps = pkg[dep.group]\n if (!deps) {\n // 不应发生: findDependencyVersion 已确认该组存在且包含目标包\n return failResult(packageName, targetVersion, fromVersion, `dependency group \"${dep.group}\" is missing`)\n }\n deps[packageName] = toVersion\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`, 'utf-8')\n\n // ---- 5. 执行 pnpm install ----\n try {\n await execPnpmInstall(workDir)\n } catch (installErr: unknown) {\n // 回滚\n rollback(pkgPath, pkgBackup, lockfilePath, lockBackup)\n\n // 回滚后清理备份\n cleanupBackups({ pkgBackup, lockBackup })\n\n const stderr = getStderr(installErr)\n return {\n packageName,\n fromVersion,\n toVersion,\n isMajor,\n success: false,\n error: `pnpm install failed: ${stderr}`,\n }\n }\n\n // 成功 — 清理备份文件\n cleanupBackups({ pkgBackup, lockBackup })\n\n return {\n packageName,\n fromVersion,\n toVersion,\n isMajor,\n success: true,\n }\n}\n\n/**\n * 通过 pnpm `overrides` 升级间接(transitive)依赖。\n *\n * 间接依赖不在 `package.json` 的 `dependencies` / `devDependencies` 中列出,\n * 因此无法通过常规 `upgradeDependency` 处理。本函数根据项目类型选择覆盖写入位置:\n *\n * - **有 `pnpm-workspace.yaml`**(monorepo / workspace)→ 写入其中的 `overrides` 字段(pnpm v10+ 推荐)\n * - **无 `pnpm-workspace.yaml`**(单包项目)→ 写入 `package.json` 的 `pnpm.overrides`\n *\n * 失败时自动回滚被修改的文件和 `pnpm-lock.yaml`。不执行验证(由 T107 负责)。\n *\n * @param params - 包名、目标版本、工作目录\n * @returns 修复结果\n */\nexport async function overrideTransitiveDependency(\n params: UpgradeDependencyParams,\n): Promise<DependencyFixResult> {\n const { packageName, targetVersion, workDir } = params\n const pkgPath = join(workDir, 'package.json')\n const workspaceYamlPath = join(workDir, 'pnpm-workspace.yaml')\n const lockfilePath = join(workDir, 'pnpm-lock.yaml')\n\n const usesWorkspaceYaml = existsSync(workspaceYamlPath)\n\n // ---- 1. 读取 package.json ----\n if (!existsSync(pkgPath)) {\n return failResult(packageName, targetVersion, '', `${pkgPath}: file not found`)\n }\n\n let pkg: PackageJson\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as PackageJson\n } catch {\n return failResult(packageName, targetVersion, '', `${pkgPath}: invalid JSON`)\n }\n\n // ---- 2. 确认是间接依赖 ----\n const directDep = findDependencyVersion(pkg, packageName)\n if (directDep) {\n return failResult(\n packageName,\n targetVersion,\n directDep.version,\n `package \"${packageName}\" is a direct dependency (${directDep.group}), use upgradeDependency instead`,\n )\n }\n\n // ---- 3. 从 lockfile 提取当前版本 ----\n const fromVersion = readLockfileVersion(lockfilePath, packageName) ?? 'unknown'\n\n // ---- 4. 构建目标版本声明 ----\n const prefix = extractPrefix(fromVersion)\n const toVersion = `${prefix}${targetVersion}`\n const isMajor = parseMajorVersion(fromVersion) !== parseMajorVersion(toVersion)\n && parseMajorVersion(fromVersion) !== -1\n && parseMajorVersion(toVersion) !== -1\n\n // ---- 5. 备份 ----\n const pkgBackup = `${pkgPath}.bak`\n const workspaceBackup = usesWorkspaceYaml ? `${workspaceYamlPath}.bak` : null\n const lockBackup = `${lockfilePath}.bak`\n\n try {\n copyFileSync(pkgPath, pkgBackup)\n if (usesWorkspaceYaml) {\n copyFileSync(workspaceYamlPath, workspaceBackup)\n }\n if (existsSync(lockfilePath)) {\n copyFileSync(lockfilePath, lockBackup)\n }\n } catch {\n return failResult(packageName, targetVersion, fromVersion, 'failed to create backup files')\n }\n\n // ---- 6. 写入 overrides ----\n let oldOverride: string | undefined\n\n if (usesWorkspaceYaml) {\n oldOverride = writeWorkspaceOverride(workspaceYamlPath, packageName, toVersion)\n } else {\n const overrides = ensurePnpmOverrides(pkg)\n oldOverride = overrides[packageName]\n overrides[packageName] = toVersion\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`, 'utf-8')\n }\n\n // ---- 7. 执行 pnpm install ----\n try {\n await execPnpmInstall(workDir)\n } catch (installErr: unknown) {\n // 回滚\n rollbackOverrides({\n usesWorkspaceYaml,\n workspaceYamlPath,\n packageName,\n oldOverride,\n pkg,\n pkgPath,\n })\n rollback(pkgPath, pkgBackup, lockfilePath, lockBackup)\n if (usesWorkspaceYaml) {\n rollback(workspaceYamlPath, workspaceBackup, lockfilePath, null)\n }\n\n // 回滚后清理备份\n cleanupBackups({ pkgBackup, lockBackup, workspaceBackup })\n\n const stderr = getStderr(installErr)\n return {\n packageName,\n fromVersion,\n toVersion,\n isMajor,\n success: false,\n error: `pnpm install failed: ${stderr}`,\n }\n }\n\n // 成功 — 清理备份文件\n cleanupBackups({\n pkgBackup,\n lockBackup,\n workspaceBackup,\n })\n\n return {\n packageName,\n fromVersion,\n toVersion,\n isMajor,\n success: true,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Exported helpers (for unit testing)\n// ---------------------------------------------------------------------------\n\n/**\n * 从版本声明中提取前缀(^ / ~ / 空)。\n *\n * @example\n * extractPrefix('^4.17.20') // '^'\n * extractPrefix('~4.17.0') // '~'\n * extractPrefix('4.17.20') // ''\n * extractPrefix('*') // '^' (默认)\n */\nexport function extractPrefix(version: string): string {\n const trimmed = version.trim()\n if (/^\\d/.test(trimmed)) {\n return ''\n }\n if (trimmed.startsWith('^')) {\n return '^'\n }\n if (trimmed.startsWith('~')) {\n return '~'\n }\n // 复杂 range / * / latest → 默认 ^\n return '^'\n}\n\n/**\n * 从版本声明中提取主版本号(major)。\n * 去除前缀后匹配第一个 semver 段。\n *\n * @example\n * parseMajorVersion('^4.17.20') // 4\n * parseMajorVersion('~2.0.0') // 2\n * parseMajorVersion('1.2.3') // 1\n * parseMajorVersion('*') // -1\n */\nexport function parseMajorVersion(version: string): number {\n const cleaned = version.replace(/^\\s*[\\^~>=<]*\\s*/, '')\n const match = /^(\\d+)\\.\\d+\\.\\d+/.exec(cleaned)\n return match ? Number.parseInt(match[1], 10) : -1\n}\n\n/**\n * 在 `package.json` 的 `dependencies` / `devDependencies` / `optionalDependencies` 中按顺序查找包。\n */\nexport function findDependencyVersion(\n pkg: PackageJson,\n packageName: string,\n): DependencyInfo | null {\n const groups = ['dependencies', 'devDependencies', 'optionalDependencies'] as const\n for (const group of groups) {\n const deps = pkg[group]\n if (deps && typeof deps === 'object' && packageName in deps) {\n return { group, version: deps[packageName] }\n }\n }\n return null\n}\n\n/**\n * 确保 `package.json` 中存在 `pnpm.overrides` 对象,若不存在则创建。\n * 返回 `overrides` 对象的引用,调用方可直接写入。\n */\nexport function ensurePnpmOverrides(pkg: PackageJson): Record<string, string> {\n if (!pkg.pnpm) {\n pkg.pnpm = {}\n }\n if (!pkg.pnpm.overrides) {\n pkg.pnpm.overrides = {}\n }\n return pkg.pnpm.overrides\n}\n\n/**\n * 从 `pnpm-lock.yaml` 中读取指定包的当前锁定版本。\n * 通过简单的正则匹配查找,不解析完整 YAML(性能优先)。\n *\n * pnpm v9+ lockfile 格式(v9.0 起):\n * /package-name/version:\n * /@scope/package-name/version:\n *\n * @returns 锁定版本字符串(如 `'5.0.0'`),未找到返回 `null`\n */\nexport function readLockfileVersion(lockfilePath: string, packageName: string): string | null {\n if (!existsSync(lockfilePath)) {\n return null\n }\n\n try {\n const content = readFileSync(lockfilePath, 'utf-8')\n // 匹配 `/packageName/version:` 行(v9 格式)\n // 包名不转义:fast-uri → /fast-uri/5.0.0:;@babel/traverse → /@babel/traverse/7.26.0:\n const escapedName = escapeRegExp(packageName)\n const pattern = new RegExp(`^/${escapedName}/(\\\\d+(?:\\\\.\\\\d+(?:\\\\.\\\\d+)?(?:-[a-zA-Z0-9.]+)?)):`, 'm')\n const match = pattern.exec(content)\n return match ? match[1] : null\n } catch {\n return null\n }\n}\n\n/**\n * 转义正则表达式特殊字符。\n */\nfunction escapeRegExp(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * 执行 `pnpm install --no-frozen-lockfile`。\n * 将同步 `execSync` 包装为 Promise,使上层 `upgradeDependency` 保持 async 语义。\n */\nfunction execPnpmInstall(workDir: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n try {\n execSync('pnpm install --no-frozen-lockfile', {\n cwd: workDir,\n encoding: 'utf-8',\n stdio: 'pipe',\n timeout: 120_000, // 2 min\n })\n resolve()\n } catch (err: unknown) {\n reject(err instanceof Error ? err : new Error(String(err)))\n }\n })\n}\n\nfunction failResult(\n packageName: string,\n targetVersion: string,\n fromVersion: string,\n error: string,\n): DependencyFixResult {\n return {\n packageName,\n fromVersion,\n toVersion: targetVersion, // 无原始前缀上下文\n isMajor: false,\n success: false,\n error,\n }\n}\n\nfunction rollback(\n pkgPath: string,\n pkgBackup: string,\n lockfilePath: string,\n lockBackup: string | null,\n): void {\n try {\n if (existsSync(pkgBackup)) {\n copyFileSync(pkgBackup, pkgPath)\n }\n if (lockBackup !== null && existsSync(lockBackup)) {\n copyFileSync(lockBackup, lockfilePath)\n }\n } catch {\n // 回滚失败 → 已写入 result.error,不在此层二次抛异常\n }\n}\n\n/**\n * 安全删除备份文件,失败静默忽略。\n */\nfunction safeUnlink(filePath: string): void {\n try {\n if (existsSync(filePath)) {\n unlinkSync(filePath)\n }\n } catch {\n // 静默降级\n }\n}\n\n/**\n * 清理所有备份文件。\n */\nfunction cleanupBackups(paths: { pkgBackup: string, lockBackup: string | null, workspaceBackup?: string | null }): void {\n safeUnlink(paths.pkgBackup)\n if (paths.lockBackup !== null) {\n safeUnlink(paths.lockBackup)\n }\n if (paths.workspaceBackup) {\n safeUnlink(paths.workspaceBackup)\n }\n}\n\n/**\n * 在 `pnpm-workspace.yaml` 中写入一条 override。\n *\n * 读取现有 YAML → 设置 `overrides[packageName] = version` → 原样写回。\n *\n * @returns 写入前该包的旧值(`undefined` 表示原先不存在该 override)\n */\nfunction writeWorkspaceOverride(\n workspaceYamlPath: string,\n packageName: string,\n version: string,\n): string | undefined {\n let doc: Record<string, unknown>\n\n try {\n const raw = readFileSync(workspaceYamlPath, 'utf-8')\n doc = YAML.parse(raw) as Record<string, unknown> ?? {}\n } catch {\n doc = {}\n }\n\n const overrides = (doc.overrides ?? {}) as Record<string, string>\n const old = overrides[packageName]\n overrides[packageName] = version\n doc.overrides = overrides\n\n writeFileSync(workspaceYamlPath, YAML.stringify(doc), 'utf-8')\n return old\n}\n\n/**\n * 回滚 override 写入操作。\n *\n * - `pnpm-workspace.yaml` 模式:删除刚写入的条目,或恢复旧值\n * - `package.json` 模式:同理操作 `pkg.pnpm.overrides`\n */\nfunction rollbackOverrides(params: {\n usesWorkspaceYaml: boolean\n workspaceYamlPath: string\n packageName: string\n oldOverride: string | undefined\n pkg: PackageJson\n pkgPath: string\n}): void {\n const { usesWorkspaceYaml, workspaceYamlPath, packageName, oldOverride, pkg, pkgPath } = params\n\n if (usesWorkspaceYaml) {\n try {\n const raw = readFileSync(workspaceYamlPath, 'utf-8')\n const doc = YAML.parse(raw) as Record<string, unknown> ?? {}\n const overrides = (doc.overrides ?? {}) as Record<string, string>\n\n if (oldOverride === undefined) {\n const next = Object.fromEntries(\n Object.entries(overrides).filter(([key]) => key !== packageName),\n )\n if (Object.keys(next).length === 0) {\n delete doc.overrides\n } else {\n doc.overrides = next\n }\n } else {\n overrides[packageName] = oldOverride\n }\n\n writeFileSync(workspaceYamlPath, YAML.stringify(doc), 'utf-8')\n } catch {\n // 回滚失败 → 已被 backup 机制覆盖\n }\n } else {\n if (pkg.pnpm?.overrides) {\n const overrides = pkg.pnpm.overrides\n if (oldOverride === undefined) {\n const next = Object.fromEntries(\n Object.entries(overrides).filter(([key]) => key !== packageName),\n )\n if (Object.keys(next).length === 0) {\n delete pkg.pnpm.overrides\n if (Object.keys(pkg.pnpm).length === 0) {\n delete pkg.pnpm\n }\n } else {\n pkg.pnpm.overrides = next\n }\n } else {\n overrides[packageName] = oldOverride\n }\n }\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`, 'utf-8')\n }\n}\n\nfunction getStderr(err: unknown): string {\n if (err instanceof Error && 'stderr' in err) {\n const stderr = (err as Error & { stderr: unknown }).stderr\n if (typeof stderr === 'string') {\n return stderr.trim()\n }\n if (Buffer.isBuffer(stderr)) {\n return stderr.toString('utf-8').trim()\n }\n return typeof stderr === 'object' && stderr !== null ? JSON.stringify(stderr) : String(stderr)\n }\n if (err instanceof Error) {\n return err.message\n }\n return typeof err === 'object' && err !== null ? JSON.stringify(err) : String(err)\n}\n","import { execSync } from 'node:child_process'\nimport { copyFileSync, existsSync, readFileSync, rmSync } from 'node:fs'\nimport { join } from 'node:path'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type LockfileFailureCategory =\n | 'LOCKFILE_NOT_FOUND'\n | 'MANIFEST_MISMATCH'\n | 'LOCKFILE_VERSION_MISMATCH'\n | 'CORRUPTED_LOCKFILE'\n | 'CREDENTIAL_ERROR'\n | 'RESOLVE_ERROR'\n | 'UNKNOWN'\n\nexport type RepairStrategy =\n | 'REGENERATE'\n | 'FIX_ENTRIES'\n | 'PIN_TOOLCHAIN'\n | 'REINSTALL'\n | 'SKIP'\n\nexport interface RepairLockfileParams {\n /** 工作目录(必须包含 package.json 和 pnpm-lock.yaml) */\n workDir: string\n /** 可选:期望的 pnpm 版本(优先于 packageManager 字段) */\n toolchain?: {\n pnpmVersion?: string\n }\n}\n\nexport interface LockfileDiff {\n /** lockfile 行数变化(正=增加) */\n linesChanged: number\n /** packages 条数变化(正=增加) */\n packagesChanged: number\n /** 简要文字摘要 */\n summary: string\n}\n\nexport interface RepairAttempt {\n strategy: RepairStrategy\n command: string\n success: boolean\n error?: string\n durationMs: number\n}\n\nexport interface LockfileRepairResult {\n success: boolean\n failureCategory?: LockfileFailureCategory\n failureDetail?: string\n strategy?: RepairStrategy\n diff?: LockfileDiff | null\n attemptHistory: RepairAttempt[]\n}\n\ninterface ClassificationResult {\n ok: boolean\n category?: LockfileFailureCategory\n stderr?: string\n}\n\n// ---------------------------------------------------------------------------\n// Diagnostics\n// ---------------------------------------------------------------------------\n\n/**\n * 执行 `pnpm i --frozen-lockfile` 并解析失败分类。\n * 成功时返回 { ok: true }。\n */\nexport function classifyLockfileFailure(workDir: string): ClassificationResult {\n try {\n execSync('pnpm install --frozen-lockfile', {\n cwd: workDir,\n stdio: 'pipe',\n timeout: 120_000,\n })\n return { ok: true }\n } catch (err) {\n const execError = err as ExecError\n const stderr = getStderrText(execError)\n if (!stderr) {\n return { ok: false, category: 'UNKNOWN', stderr: '' }\n }\n const category = parseFailureCategory(stderr)\n return { ok: false, category, stderr }\n }\n}\n\nconst FAILURE_PATTERNS: [LockfileFailureCategory, RegExp][] = [\n ['LOCKFILE_NOT_FOUND', /no lockfile|Cannot find.*pnpm-lock\\.yaml|ENOENT.*lockfile/i],\n ['MANIFEST_MISMATCH', /ERR_PNPM_OUTDATED_LOCKFILE|out of sync|needs update/i],\n ['CREDENTIAL_ERROR', /E401|ERR_PNPM_FETCH_401|ERR_PNPM_FETCH_403|authentication failed|Authorization/i],\n ['CORRUPTED_LOCKFILE', /\\b(broken|corrupted|Cannot read).*lockfile|ERR_PNPM_BROKEN_LOCKFILE/i],\n ['LOCKFILE_VERSION_MISMATCH', /lockfileVersion.*incompatible|unsupported lockfile version|lockfile had been generated with/i],\n ['RESOLVE_ERROR', /ERR_PNPM_NO_MATCHING_VERSION|resolution|resolve.*failed|ERR_PNPM_PEER_DEP_ISSUES/i],\n]\n\n/**\n * 按优先级匹配 stderr,返回首个命中的 FailureCategory。\n * 不匹配任何特征时返回 'UNKNOWN'。\n */\nfunction parseFailureCategory(stderr: string): LockfileFailureCategory {\n for (const [category, pattern] of FAILURE_PATTERNS) {\n if (pattern.test(stderr)) {\n return category\n }\n }\n return 'UNKNOWN'\n}\n\n// ---------------------------------------------------------------------------\n// Repair Strategies\n// ---------------------------------------------------------------------------\n\ninterface ExecError extends Error {\n stderr?: Buffer | string\n stdout?: Buffer | string\n status?: number\n}\n\nfunction getStderrText(err: ExecError): string {\n if (!err.stderr) {\n return (err as Error).message ?? ''\n }\n if (typeof err.stderr === 'string') {\n return err.stderr\n }\n return err.stderr.toString('utf-8')\n}\n\nfunction execRepair(command: string, workDir: string, timeoutMs = 180_000): void {\n execSync(command, { cwd: workDir, stdio: 'pipe', timeout: timeoutMs })\n}\n\n/**\n * 确定某个分类下适用的修复策略链(从首到末)。\n * CREDENTIAL_ERROR 返回空链(不可修复)。\n */\nfunction getStrategyChain(category: LockfileFailureCategory): RepairStrategy[] {\n switch (category) {\n case 'LOCKFILE_NOT_FOUND':\n case 'MANIFEST_MISMATCH':\n return ['REGENERATE', 'REINSTALL']\n case 'CORRUPTED_LOCKFILE':\n return ['FIX_ENTRIES', 'REGENERATE', 'REINSTALL']\n case 'LOCKFILE_VERSION_MISMATCH':\n return ['PIN_TOOLCHAIN', 'REGENERATE', 'REINSTALL']\n case 'RESOLVE_ERROR':\n return ['REGENERATE', 'FIX_ENTRIES', 'REINSTALL']\n case 'UNKNOWN':\n return ['REGENERATE', 'REINSTALL']\n case 'CREDENTIAL_ERROR':\n return []\n }\n}\n\nfunction getStrategyCommand(strategy: RepairStrategy): string {\n switch (strategy) {\n case 'REGENERATE':\n return 'pnpm install --lockfile-only'\n case 'FIX_ENTRIES':\n return 'pnpm install --fix-lockfile --lockfile-only'\n case 'REINSTALL':\n return 'pnpm install --no-frozen-lockfile'\n case 'PIN_TOOLCHAIN':\n return 'pnpm install --lockfile-only'\n default:\n return ''\n }\n}\n\n// ---------------------------------------------------------------------------\n// Diff\n// ---------------------------------------------------------------------------\n\n/**\n * 比较修复前后 lockfile,返回行数与包数量变化摘要。\n */\nexport function computeLockfileDiff(beforePath: string, afterPath: string): LockfileDiff {\n const beforeLines = countLockfileLines(beforePath)\n const afterLines = countLockfileLines(afterPath)\n const beforePackages = countLockfilePackages(beforePath)\n const afterPackages = countLockfilePackages(afterPath)\n\n const linesChanged = afterLines - beforeLines\n const packagesChanged = afterPackages - beforePackages\n\n const parts: string[] = []\n if (linesChanged !== 0) {\n parts.push(`${linesChanged > 0 ? '+' : ''}${linesChanged} lines`)\n }\n if (packagesChanged !== 0) {\n parts.push(`${packagesChanged > 0 ? '+' : ''}${packagesChanged} packages`)\n }\n const summary = parts.length > 0 ? `lockfile updated: ${parts.join(', ')}` : 'lockfile unchanged'\n\n return { linesChanged, packagesChanged, summary }\n}\n\nfunction countLockfileLines(path: string): number {\n if (!existsSync(path)) {\n return 0\n }\n return readFileSync(path, 'utf-8').split('\\n').length\n}\n\n/**\n * 统计 pnpm-lock.yaml 中 `packages:` 块下的条目数。\n * packages 条目以缩进 + `/` 开头(如 ` /lodash@4.17.21:`)。\n */\nfunction countLockfilePackages(path: string): number {\n if (!existsSync(path)) {\n return 0\n }\n const content = readFileSync(path, 'utf-8')\n const lines = content.split('\\n')\n let inPackages = false\n let count = 0\n for (const line of lines) {\n if (!inPackages) {\n if (/^packages:\\s*$/.test(line)) {\n inPackages = true\n }\n continue\n }\n // packages 块结束:下一个顶级 key(无缩进)\n if (/^[a-zA-Z]/.test(line)) {\n break\n }\n // 包条目: 以缩进 + '/' 开头\n if (/^\\s+\\//.test(line)) {\n count++\n }\n }\n return count\n}\n\n// ---------------------------------------------------------------------------\n// Version Resolution\n// ---------------------------------------------------------------------------\n\n/**\n * 从 package.json 的 `packageManager` 字段解析 pnpm 版本。\n * 优先级: toolchain.pnpmVersion > packageManager > null\n */\nexport function resolvePnpmVersion(\n workDir: string,\n toolchain?: { pnpmVersion?: string },\n): string | null {\n if (toolchain?.pnpmVersion) {\n return toolchain.pnpmVersion\n }\n\n const pkgPath = join(workDir, 'package.json')\n if (!existsSync(pkgPath)) {\n return null\n }\n\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as Record<string, unknown>\n const pm = pkg.packageManager\n if (typeof pm === 'string') {\n const match = /pnpm@(.+)$/.exec(pm)\n if (match?.[1]) {\n return match[1]\n }\n }\n } catch {\n // ignore parse errors\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Main\n// ---------------------------------------------------------------------------\n\nconst LOCKFILE_NAME = 'pnpm-lock.yaml'\n\n/**\n * 在 workDir 中使用 `pnpm install --frozen-lockfile` 验证 lockfile 合规性。\n * 验证通过 → 返回 ret:0;失败 → 返回 ret:1 + stderr。\n * 不抛异常——调用方通过 ret 判断结果。\n */\nfunction verifyFrozenLockfile(workDir: string): { ret: number, stderr: string } {\n try {\n execSync('pnpm install --frozen-lockfile', {\n cwd: workDir,\n stdio: 'pipe',\n timeout: 120_000,\n })\n return { ret: 0, stderr: '' }\n } catch (err) {\n return { ret: 1, stderr: getStderrText(err as ExecError) }\n }\n}\n\n/**\n * 备份 pnpm-lock.yaml → `<name>.bak`,返回备份路径。\n * lockfile 不存在时会创建空 .bak(回滚时删除即可)。\n */\nfunction backupLockfilePath(workDir: string): string {\n const src = join(workDir, LOCKFILE_NAME)\n const bak = `${src}.bak`\n if (existsSync(src)) {\n copyFileSync(src, bak)\n }\n return bak\n}\n\n/** 从 .bak 还原 lockfile,并删除 .bak。 */\nfunction rollback(bakPath: string, workDir: string): void {\n const target = join(workDir, LOCKFILE_NAME)\n try {\n if (existsSync(bakPath)) {\n copyFileSync(bakPath, target)\n }\n } finally {\n try {\n rmSync(bakPath)\n } catch {\n /* ignore */\n }\n }\n}\n\n/**\n * 检测并修复 pnpm frozen-lockfile 漂移。\n *\n * 流程:\n * 1. 先跑 `pnpm i --frozen-lockfile` 诊断\n * 2. 若已合规 → 直接返回 success\n * 3. 若为 CREDENTIAL_ERROR → SKIP\n * 4. 按分类选取策略链,逐级尝试修复:\n * - 每个策略执行后立即用 `pnpm i --frozen-lockfile` 验证\n * - 验证通过 → 记录 diff、返回 success\n * - 验证失败 → 继续下一个策略\n * 5. 所有策略失败 → 回滚 lockfile、返回失败结果\n */\nexport function repairLockfile(params: RepairLockfileParams): LockfileRepairResult {\n const { workDir } = params\n const workDir_ = workDir // alias for consistency\n\n const attempts: RepairAttempt[] = []\n\n // ---- 1. 备份 ----\n const bakPath = backupLockfilePath(workDir_)\n\n // ---- 2. 诊断 ----\n const classification = classifyLockfileFailure(workDir_)\n\n if (classification.ok) {\n // 已合规,无需修复\n try {\n rmSync(bakPath)\n } catch {\n /* ignore */\n }\n return {\n success: true,\n diff: null,\n attemptHistory: attempts,\n }\n }\n\n const category = classification.category ?? 'UNKNOWN'\n const stderr = classification.stderr ?? ''\n\n // ---- 3. CREDENTIAL_ERROR 不可修复 ----\n if (category === 'CREDENTIAL_ERROR') {\n try {\n rmSync(bakPath)\n } catch {\n /* ignore */\n }\n return {\n success: false,\n failureCategory: 'CREDENTIAL_ERROR',\n failureDetail: `registry authentication error: ${truncate(stderr, 500)}`,\n strategy: 'SKIP',\n attemptHistory: attempts,\n }\n }\n\n // ---- 4. 逐级尝试 ----\n const strategyChain = getStrategyChain(category)\n\n let lastError = stderr\n const startTimestamp = Date.now()\n\n for (const strategy of strategyChain) {\n const command = getStrategyCommand(strategy)\n const t0 = Date.now()\n\n let strategySuccess = false\n let strategyError: string | undefined\n\n try {\n execRepair(command, workDir_)\n strategySuccess = true\n } catch (err) {\n strategyError = getStderrText(err as ExecError) || (err as Error).message\n }\n\n const durationMs = Date.now() - t0\n attempts.push({ strategy, command, success: strategySuccess, error: strategyError, durationMs })\n\n if (!strategySuccess) {\n lastError = strategyError ?? lastError\n continue\n }\n\n // 策略命令成功 → 验证 frozen-lockfile\n const verify = verifyFrozenLockfile(workDir_)\n\n if (verify.ret === 0) {\n // 验证通过 → 计算 diff\n const diff = computeLockfileDiff(bakPath, join(workDir_, LOCKFILE_NAME))\n try {\n rmSync(bakPath)\n } catch {\n /* ignore */\n }\n return {\n success: true,\n strategy,\n diff,\n attemptHistory: attempts,\n }\n }\n\n lastError = verify.stderr\n }\n\n // ---- 5. 全部失败 → 回滚 ----\n rollback(bakPath, workDir_)\n\n const durationMs = Date.now() - startTimestamp\n attempts.push({\n strategy: 'SKIP',\n command: 'rollback',\n success: true,\n durationMs,\n })\n\n return {\n success: false,\n failureCategory: category,\n failureDetail: truncate(lastError, 500),\n attemptHistory: attempts,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) {\n return text\n }\n return `${text.slice(0, maxLen)}…`\n}\n","import { spawn } from 'node:child_process'\n\n/**\n * 单条命令的执行结果\n */\nexport interface CommandResult {\n /** 执行的命令文本 */\n command: string\n /** 退出码(0 = 成功) */\n exitCode: number\n /** 执行耗时(毫秒) */\n durationMs: number\n /** stdout 摘要(截断到 200 行,已脱敏) */\n stdout: string\n /** stderr 摘要(截断到 200 行,已脱敏) */\n stderr: string\n}\n\n/**\n * 验证执行参数\n */\nexport interface VerificationParams {\n /** 工作目录 */\n workDir: string\n /**\n * 要执行的命令序列。\n * 默认: `pnpm install --frozen-lockfile` → `pnpm lint` → `pnpm build`\n */\n commands?: string[]\n}\n\n/**\n * 验证执行结果\n */\nexport interface VerificationResult {\n /** 所有命令是否全部成功 */\n success: boolean\n /** 每条命令的详细结果 */\n commandResults: CommandResult[]\n /** 失败的命令(仅 success=false 时填充) */\n failedCommand?: string\n /** 失败详情(仅 success=false 时填充) */\n failure?: string\n}\n\nconst DEFAULT_COMMANDS = [\n 'pnpm install --frozen-lockfile',\n 'pnpm lint',\n 'pnpm build',\n]\n\nconst MAX_OUTPUT_LINES = 200\n\n/**\n * 按顺序在工作目录执行命令序列,任一命令失败则停止。\n *\n * @example\n * ```ts\n * const result = await runVerification({ workDir: '/repo' })\n * if (!result.success) {\n * console.error(result.failure)\n * }\n * ```\n */\nexport async function runVerification(params: VerificationParams): Promise<VerificationResult> {\n const commands = params.commands ?? DEFAULT_COMMANDS\n const commandResults: CommandResult[] = []\n\n for (const command of commands) {\n const result = await execCommand(command, params.workDir)\n commandResults.push(result)\n\n if (result.exitCode !== 0) {\n return {\n success: false,\n commandResults,\n failedCommand: command,\n failure: `command \"${command}\" exited with code ${result.exitCode}`,\n }\n }\n }\n\n return {\n success: true,\n commandResults,\n }\n}\n\n/**\n * 执行单条 shell 命令,捕获 stdout/stderr(截断到 200 行)并脱敏。\n */\nfunction execCommand(command: string, workDir: string): Promise<CommandResult> {\n return new Promise((resolve) => {\n const startTime = Date.now()\n const cp = spawn(command, {\n cwd: workDir,\n shell: true,\n stdio: 'pipe',\n })\n\n const stdoutChunks: string[] = []\n const stderrChunks: string[] = []\n\n cp.stdout.on('data', (data: Buffer) => {\n stdoutChunks.push(data.toString('utf-8'))\n })\n\n cp.stderr.on('data', (data: Buffer) => {\n stderrChunks.push(data.toString('utf-8'))\n })\n\n cp.on('error', (err) => {\n resolve({\n command,\n exitCode: -1,\n durationMs: Date.now() - startTime,\n stdout: '',\n stderr: sanitizeOutput(err.message),\n })\n })\n\n cp.on('close', (code) => {\n const stdout = truncateLines(stdoutChunks.join(''), MAX_OUTPUT_LINES)\n const stderr = truncateLines(stderrChunks.join(''), MAX_OUTPUT_LINES)\n resolve({\n command,\n exitCode: code ?? -1,\n durationMs: Date.now() - startTime,\n stdout: sanitizeOutput(stdout),\n stderr: sanitizeOutput(stderr),\n })\n })\n })\n}\n\n/** 截断文本到指定行数 */\nfunction truncateLines(text: string, maxLines: number): string {\n const lines = text.split('\\n')\n if (lines.length <= maxLines) {\n return text\n }\n return `${lines.slice(0, maxLines).join('\\n')}\\n... (truncated, ${lines.length - maxLines} more lines)`\n}\n\n/**\n * 脱敏处理:移除命令输出中可能包含的敏感信息。\n *\n * 过滤规则:\n * - 包含 `token=` / `secret=` / `password=` 的行\n * - `npm_config_` / `GITHUB_TOKEN` / `NPM_TOKEN` 等环境变量暴露\n * - URL 中的 user:password@ 模式\n */\nconst SECRET_PATTERNS: [RegExp, string][] = [\n // 环境变量赋值(大写下划线变量 = 敏感值)\n [/^(GITHUB_TOKEN|NPM_TOKEN|NODE_AUTH_TOKEN|DOCKER_PASSWORD)=.+/gim, '$1=***'],\n // Token=xxx / secret=xxx 模式\n [/\\b(token|secret|password|api[_-]?key)\\s*[=:]\\s*\\S+/gi, '$1=***'],\n // URL 中的认证信息 user:pass@host\n [/\\/\\/[^@\\n]+@/g, '//***@'],\n // npm_config 环境变量(可能包含 registry token)\n [/npm_config_[a-z_]+=\\S+/g, 'npm_config_***=***'],\n]\n\n/**\n * 对外暴露以支持单独测试\n */\nexport function sanitizeOutput(output: string): string {\n let result = output\n for (const [pattern, replacement] of SECRET_PATTERNS) {\n result = result.replace(pattern, replacement)\n }\n return result\n}\n","// app-helpers.ts\n// DependfixApp 的辅助方法集合。\n// 为控制 app.ts 文件规模(max-lines 800),将不直接参与模式编排的方法\n// 提取为模块级函数;通过 AppContext 传入所需状态,行为与原类方法一致。\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { execSync } from 'node:child_process'\nimport { createInterface } from 'node:readline'\nimport type { Octokit } from '@octokit/rest'\nimport {\n toErrorMessage,\n type FixAction,\n type FixError,\n type Logger,\n type NormalizedSecurityAlert,\n type RepositoryResult,\n type RunResult,\n type RunReportConfig,\n type RunSummary,\n} from '@dependfix/core'\nimport type { RuntimeConfig } from './config'\nimport {\n upgradeDependency,\n overrideTransitiveDependency,\n type DependencyFixResult,\n} from './fixers/dependency'\nimport { repairLockfile, type LockfileRepairResult } from './fixers/pnpm'\nimport { runVerification, type VerificationResult } from './runners/verification-runner'\nimport {\n stageAndCommit,\n closePullRequest,\n listDependfixBranches,\n getBranchPrStatus,\n isConfirmAnswer,\n} from './github/pr-creator'\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_VERIFY_COMMANDS = [\n 'pnpm install --frozen-lockfile',\n 'pnpm lint',\n 'pnpm build',\n]\n\n/** 匹配 `pnpm <singleWord>` 模式的命令(可能是 package.json script 引用) */\nconst PNPM_SCRIPT_RE = /^pnpm\\s+([a-zA-Z][a-zA-Z0-9:_-]*)$/\n\n/** 自动修复提交的标准消息(本地 --commit 与 fix-and-pr 共用) */\nexport const FIX_COMMIT_MESSAGE = 'fix(deps): automated dependfix security repair'\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\n/** DependfixApp 提供给辅助方法的最小状态切片。 */\nexport interface AppContext {\n config: RuntimeConfig\n workDir: string\n logger: Logger\n customCommands?: string[]\n runId: string\n allAlerts: NormalizedSecurityAlert[]\n allActions: FixAction[]\n allErrors: FixError[]\n repoResults: RepositoryResult[]\n summary: RunSummary\n startedAt: string\n finishedAt: string\n}\n\n// ---------------------------------------------------------------------------\n// Upgrade helpers\n// ---------------------------------------------------------------------------\n\n/**\n * 升级单个告警对应的依赖(dry-run 仅记录)。\n * 优先直接升级,失败且为间接依赖时回退到 pnpm overrides。\n */\nexport async function upgradeAlert(\n ctx: Pick<AppContext, 'config' | 'logger' | 'workDir'>,\n alert: NormalizedSecurityAlert,\n): Promise<FixAction> {\n const { config, logger, workDir } = ctx\n const startMs = Date.now()\n\n if (config.dryRun) {\n logger.info(`[dry-run] Would upgrade ${alert.packageName} to ${alert.recommendedVersion}`)\n return {\n type: 'dependency-upgrade',\n repository: alert.repository,\n target: alert.packageName,\n fromVersion: alert.recommendedVersion ? `< ${alert.recommendedVersion}` : undefined,\n toVersion: alert.recommendedVersion,\n isMajor: false,\n success: true,\n durationMs: 0,\n }\n }\n\n try {\n // 优先尝试直接升级,失败自动回退到 overrides(处理间接依赖)\n let result: DependencyFixResult = await upgradeDependency({\n packageName: alert.packageName,\n targetVersion: alert.recommendedVersion,\n workDir,\n })\n\n if (!result.success && result.error?.includes('not found in dependencies')) {\n // 间接依赖 — 通过 pnpm overrides 升级\n result = await overrideTransitiveDependency({\n packageName: alert.packageName,\n targetVersion: alert.recommendedVersion,\n workDir,\n })\n logger.info(\n result.success\n ? `Upgraded ${result.packageName}: ${result.fromVersion} → ${result.toVersion} (pnpm overrides)`\n : `Failed to upgrade ${result.packageName}: ${result.error}`,\n )\n } else {\n logger.info(\n result.success\n ? `Upgraded ${result.packageName}: ${result.fromVersion} → ${result.toVersion}`\n : `Failed to upgrade ${result.packageName}: ${result.error}`,\n )\n }\n\n return {\n type: 'dependency-upgrade',\n repository: alert.repository,\n target: alert.packageName,\n fromVersion: result.fromVersion,\n toVersion: result.toVersion,\n isMajor: result.isMajor,\n success: result.success,\n error: result.error,\n durationMs: Date.now() - startMs,\n }\n } catch (error: unknown) {\n const message = toErrorMessage(error)\n logger.error(`Upgrade error for ${alert.packageName}: ${message}`)\n return {\n type: 'dependency-upgrade',\n repository: alert.repository,\n target: alert.packageName,\n toVersion: alert.recommendedVersion,\n success: false,\n error: message,\n durationMs: Date.now() - startMs,\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Lockfile repair\n// ---------------------------------------------------------------------------\n\n/** 尝试修复 pnpm-lock.yaml(dry-run 仅记录)。 */\nexport function tryLockfileRepair(\n ctx: Pick<AppContext, 'config' | 'logger' | 'workDir'>,\n repo: string,\n): FixAction {\n const { config, logger, workDir } = ctx\n const startMs = Date.now()\n\n if (config.dryRun) {\n logger.info(`[dry-run] Would attempt lockfile repair for ${repo}`)\n return {\n type: 'lockfile-repair',\n repository: repo,\n target: 'pnpm-lock.yaml',\n success: true,\n durationMs: 0,\n }\n }\n\n try {\n const result: LockfileRepairResult = repairLockfile({ workDir })\n\n logger.info(\n result.success\n ? `Lockfile repaired for ${repo} (strategy: ${result.strategy ?? 'N/A'})`\n : `Lockfile repair failed for ${repo}: ${result.failureDetail ?? 'unknown'}`,\n )\n\n return {\n type: 'lockfile-repair',\n repository: repo,\n target: 'pnpm-lock.yaml',\n success: result.success,\n error: result.success ? undefined : (result.failureDetail ?? 'Lockfile repair failed'),\n strategy: result.strategy,\n durationMs: Date.now() - startMs,\n diff: result.diff?.summary,\n }\n } catch (error: unknown) {\n const message = toErrorMessage(error)\n logger.error(`Lockfile repair error for ${repo}: ${message}`)\n return {\n type: 'lockfile-repair',\n repository: repo,\n target: 'pnpm-lock.yaml',\n success: false,\n error: message,\n durationMs: Date.now() - startMs,\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Verification\n// ---------------------------------------------------------------------------\n\n/** 执行验证命令链,返回逐命令结果。 */\nexport async function verifyProject(\n ctx: Pick<AppContext, 'customCommands' | 'logger' | 'workDir' | 'allErrors'>,\n repo: string,\n): Promise<FixAction[]> {\n const { customCommands, logger, workDir, allErrors } = ctx\n\n // 确定要执行的命令:用户自定义 > 默认命令链\n const rawCommands = customCommands ?? DEFAULT_VERIFY_COMMANDS\n\n // 仅对默认命令链做脚本存在性校验\n const isDefault = !customCommands\n const { valid, skipped } = isDefault\n ? validateVerifyCommands(rawCommands, workDir)\n : { valid: rawCommands, skipped: [] as string[] }\n\n // 记录被跳过的命令\n for (const cmd of skipped) {\n logger.info(`Skipping command \"${cmd}\": script not found in package.json`)\n allErrors.push({\n repository: repo,\n target: cmd,\n stage: 'verify',\n category: 'SCRIPT_NOT_FOUND',\n message: `Skipped: no matching script in package.json for \"${cmd}\"`,\n })\n }\n\n if (valid.length === 0) {\n logger.info(`No verification commands to run for ${repo}`)\n return []\n }\n\n try {\n const result: VerificationResult = await runVerification({\n workDir,\n commands: valid,\n })\n\n return result.commandResults.map((cr) => ({\n type: 'verification' as const,\n repository: repo,\n target: cr.command,\n success: cr.exitCode === 0,\n error: cr.exitCode !== 0 ? `exit code ${cr.exitCode}` : undefined,\n durationMs: cr.durationMs,\n }))\n } catch (error: unknown) {\n const message = toErrorMessage(error)\n logger.error(`Verification error for ${repo}: ${message}`)\n return [{\n type: 'verification',\n repository: repo,\n target: 'verification',\n success: false,\n error: message,\n }]\n }\n}\n\n/**\n * 校验默认命令链中的脚本引用是否存在。\n *\n * - `pnpm install --frozen-lockfile` 等非脚本命令 → 直接保留\n * - `pnpm lint` 等脚本命令 → 检查 `package.json#scripts` 是否存在对应键\n * - 用户自定义命令(`--commands`)不经过此校验\n */\nfunction validateVerifyCommands(commands: string[], workDir: string): { valid: string[], skipped: string[] } {\n const pkgJsonPath = join(workDir, 'package.json')\n let pkgScripts: Record<string, string> = {}\n\n if (existsSync(pkgJsonPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8')) as { scripts?: Record<string, string> }\n pkgScripts = pkg.scripts ?? {}\n } catch {\n // package.json 解析失败 → 不校验,全部当作有效\n return { valid: commands, skipped: [] }\n }\n }\n\n const valid: string[] = []\n const skipped: string[] = []\n\n for (const cmd of commands) {\n const match = PNPM_SCRIPT_RE.exec(cmd)\n if (match) {\n const scriptName = match[1]\n if (pkgScripts[scriptName]) {\n valid.push(cmd)\n } else {\n skipped.push(cmd)\n }\n } else {\n // 非脚本命令(如 `pnpm install --frozen-lockfile`)→ 直接保留\n valid.push(cmd)\n }\n }\n\n return { valid, skipped }\n}\n\n// ---------------------------------------------------------------------------\n// Local commit helpers\n// ---------------------------------------------------------------------------\n\n/**\n * 将修复产生的变更提交到本地当前分支。\n *\n * - 无任何变更(含已暂存变更)时跳过\n * - config 校验已保证 `commit` 与 `dryRun` / `createPullRequest` 互斥,\n * 因此这里不需要再检查这两个开关\n */\nexport function commitLocalChanges(\n ctx: Pick<AppContext, 'logger' | 'workDir'>,\n): void {\n const { logger, workDir } = ctx\n\n if (!hasGitChanges(workDir)) {\n logger.info('No changes to commit — skipping local commit')\n return\n }\n\n // 提交前先确保报告目录被 .gitignore 忽略,避免残留的 dependfix-reports/ 被 git add 提交\n ensureGitignore(workDir)\n\n stageAndCommit(FIX_COMMIT_MESSAGE, workDir)\n logger.info(`Committed fix changes to current branch: ${FIX_COMMIT_MESSAGE}`)\n}\n\n/**\n * 检查工作目录是否有未提交的变更(含未暂存与已暂存)。\n */\nexport function hasGitChanges(workDir: string): boolean {\n try {\n execSync('git diff --quiet', { cwd: workDir, stdio: 'pipe' })\n execSync('git diff --cached --quiet', { cwd: workDir, stdio: 'pipe' })\n return false // 两者都无变更\n } catch {\n return true // 任一有变更\n }\n}\n\n/**\n * 确保目标仓库的 `.gitignore` 中包含 `dependfix-reports/`。\n *\n * - 仅在 workDir 是 git 仓库时执行\n * - 已存在该条目时幂等跳过\n * - 失败(权限、磁盘满等)静默降级\n */\nexport function ensureGitignore(workDir: string): void {\n try {\n const gitDir = join(workDir, '.git')\n if (!existsSync(gitDir)) {\n return\n }\n\n const gitignorePath = join(workDir, '.gitignore')\n const entry = 'dependfix-reports/'\n\n let content = ''\n if (existsSync(gitignorePath)) {\n content = readFileSync(gitignorePath, 'utf-8')\n }\n\n // 幂等检查\n const lines = content.split('\\n')\n if (lines.some((l) => l.trim() === entry)) {\n return\n }\n\n // 追加(末尾无换行时补一个)\n const suffix = content.endsWith('\\n') || content.length === 0 ? '' : '\\n'\n const block = `${suffix}# dependfix\\n${entry}\\n`\n writeFileSync(gitignorePath, content + block, 'utf-8')\n } catch {\n // 静默降级\n }\n}\n\n// ---------------------------------------------------------------------------\n// PR helpers\n// ---------------------------------------------------------------------------\n\n/**\n * 关闭被取代的旧 PR。单条关闭失败不中断其余 PR 的关闭,\n * 失败记录为 `PR_CLOSE_FAILED`(与 PR 创建失败区分)。\n */\nexport async function closeSupersededPRs(\n ctx: Pick<AppContext, 'logger' | 'allErrors'>,\n client: Octokit,\n owner: string,\n repo: string,\n supersedePRs: { number: number }[],\n): Promise<void> {\n const { logger, allErrors } = ctx\n for (const old of supersedePRs) {\n try {\n await closePullRequest(client, owner, repo, old.number)\n logger.info(`Closed superseded PR #${old.number}`)\n } catch (error: unknown) {\n const message = toErrorMessage(error)\n logger.error(`Failed to close superseded PR #${old.number}: ${message}`)\n allErrors.push({\n repository: `${owner}/${repo}`,\n stage: 'report',\n category: 'PR_CLOSE_FAILED',\n message: `Failed to close PR #${old.number}: ${message}`,\n })\n }\n }\n}\n\n/**\n * (fix-and-pr + --cleanup-branches)将已合并的 dependfix 分支列为待清理清单,\n * 记录到报告与日志,不执行删除。\n */\nexport async function reportCleanupCandidates(\n ctx: Pick<AppContext, 'config' | 'logger' | 'allActions' | 'allErrors'>,\n client: Octokit,\n): Promise<void> {\n const { config, logger, allActions, allErrors } = ctx\n for (const repo of config.repositories) {\n const [owner, name] = repo.split('/')\n try {\n const branches = await listDependfixBranches(client, owner, name)\n for (const branch of branches) {\n const status = await getBranchPrStatus(client, owner, name, branch)\n if (status.merged) {\n logger.info(`[cleanup] merged branch awaiting manual cleanup: ${branch}`)\n allActions.push({\n type: 'branch-cleanup',\n repository: repo,\n target: branch,\n success: true,\n diff: 'merged; run `dependfix cleanup-branches` to delete',\n durationMs: 0,\n })\n }\n }\n } catch (error: unknown) {\n const message = toErrorMessage(error)\n logger.error(`[cleanup] detection failed for ${repo}: ${message}`)\n allErrors.push({\n repository: repo,\n stage: 'report',\n category: 'CLEANUP_DETECT_FAILED',\n message,\n })\n }\n }\n}\n\n/**\n * 交互式确认删除。非 TTY(CI/管道)时直接拒绝。\n */\nexport function confirmCleanup(\n ctx: Pick<AppContext, 'logger'>,\n repo: string,\n candidates: { branch: string }[],\n): Promise<boolean> {\n const { logger } = ctx\n if (!process.stdin.isTTY) {\n logger.warn('[cleanup] non-TTY environment — deletion requires interactive confirmation, aborting')\n return Promise.resolve(false)\n }\n\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n return new Promise((resolve) => {\n rl.question(\n `Delete ${candidates.length} branch(es) from ${repo}? [y/N] `,\n (answer) => {\n rl.close()\n resolve(isConfirmAnswer(answer))\n },\n )\n })\n}\n\n// ---------------------------------------------------------------------------\n// Result assembly\n// ---------------------------------------------------------------------------\n\n/** 汇总所有动作到 summary(alertsSkipped 已在 processRepoForFix 中累加)。 */\nexport function computeSummary(\n ctx: Pick<AppContext, 'config' | 'allActions' | 'allAlerts' | 'summary'>,\n): void {\n const { config, allActions, allAlerts, summary } = ctx\n\n let fixed = 0\n let failed = 0\n let lockfileRepairs = 0\n let verificationsPassed = 0\n let verificationsFailed = 0\n\n for (const action of allActions) {\n if (action.type === 'dependency-upgrade') {\n if (action.success) {\n fixed++\n } else {\n failed++\n }\n }\n if (action.type === 'lockfile-repair' && action.success) {\n lockfileRepairs++\n }\n if (action.type === 'verification') {\n if (action.success) {\n verificationsPassed++\n } else {\n verificationsFailed++\n }\n }\n }\n\n const fixable = allAlerts.filter((a) => a.fixable).length\n\n summary.repositoriesScanned = config.repositories.length\n summary.alertsFound = allAlerts.length\n summary.alertsFixable = fixable\n summary.alertsFixed = fixed\n summary.alertsFailed = failed\n summary.lockfileRepairs = lockfileRepairs\n summary.verificationsPassed = verificationsPassed\n summary.verificationsFailed = verificationsFailed\n}\n\n/** 组装最终运行结果。 */\nexport function buildRunResult(\n ctx: Pick<AppContext, 'config' | 'runId' | 'startedAt' | 'finishedAt' | 'summary' | 'repoResults' | 'allAlerts' | 'allActions' | 'allErrors'>,\n): RunResult {\n const reportConfig: RunReportConfig = {\n mode: ctx.config.mode,\n severityThreshold: ctx.config.severityThreshold,\n repositories: ctx.config.repositories,\n dryRun: ctx.config.dryRun,\n createPullRequest: ctx.config.createPullRequest,\n maxAlertsPerRepository: ctx.config.maxAlertsPerRepository,\n }\n\n return {\n runId: ctx.runId,\n startedAt: ctx.startedAt,\n finishedAt: ctx.finishedAt,\n config: reportConfig,\n summary: ctx.summary,\n repositories: ctx.repoResults,\n alerts: ctx.allAlerts,\n actions: ctx.allActions,\n errors: ctx.allErrors,\n }\n}\n\n/**\n * 计算退出码:\n * - 0: 全部仓库处理成功(无 failed actions、无 errors)\n * - 1: 部分仓库失败\n * - 2: 全部仓库失败(或无仓库被成功处理)\n */\nexport function computeExitCode(\n ctx: Pick<AppContext, 'config' | 'allErrors' | 'allActions' | 'repoResults'>,\n): number {\n const { config, allErrors, allActions, repoResults } = ctx\n const hasErrors = allErrors.length > 0\n const hasFailures = allActions.some((a) => !a.success)\n const hasRepoSuccess = repoResults.length > 0\n && repoResults.some((r) => r.alertsCount > 0 || r.fixed > 0 || r.verificationPassed === true)\n // cleanup-branches 模式不填充 repoResults,以成功的 branch-cleanup 动作判定\n const hasCleanupSuccess = config.mode === 'cleanup-branches'\n && allActions.some((a) => a.success && a.type === 'branch-cleanup')\n const hasSuccess = hasRepoSuccess || hasCleanupSuccess\n\n // fix-and-pr stub:总是返回 0(非错误)\n if (config.mode === 'fix-and-pr') {\n return 0\n }\n\n if (!hasErrors && !hasFailures) {\n return 0\n }\n\n if (hasSuccess) {\n return 1\n }\n\n return 2\n}\n","import type { Octokit } from '@octokit/rest'\nimport {\n createLogger,\n filterAlerts,\n prioritizeAlerts,\n limitAlerts,\n generateMarkdownReport,\n generateJsonReport,\n writeReport,\n createEmptyRunSummary,\n toErrorMessage,\n type Logger,\n type NormalizedSecurityAlert,\n type RunResult,\n type RunSummary,\n type RepositoryResult,\n type FixAction,\n type FixError,\n} from '@dependfix/core'\nimport { createGitHubClient } from './github/client'\nimport { fetchDependabotAlerts } from './github/dependabot-fetcher'\nimport { createFixBranch, stageAndCommit, pushBranch, createPullRequest, generatePRBody, computeFixFingerprint, computeFixAndPrPlan, findDependfixOpenPR, listDependfixBranches, getBranchPrStatus, deleteRemoteBranch, type DependfixBranchStatus } from './github/pr-creator'\nimport type { RuntimeConfig } from './config'\nimport {\n FIX_COMMIT_MESSAGE,\n type AppContext,\n upgradeAlert,\n tryLockfileRepair,\n verifyProject,\n commitLocalChanges,\n hasGitChanges,\n ensureGitignore,\n closeSupersededPRs,\n reportCleanupCandidates,\n confirmCleanup,\n computeSummary,\n buildRunResult,\n computeExitCode,\n} from './app-helpers'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DependfixAppOptions {\n /** 已解析的运行时配置 */\n config: RuntimeConfig\n /** 工作目录(默认 `process.cwd()`) */\n workDir?: string\n /** 是否输出详细日志 */\n verbose?: boolean\n /** 自定义验证命令(覆盖默认命令链) */\n commands?: string[]\n}\n\nexport interface DependfixRunResult {\n /** 结构化运行结果(用于报告生成) */\n result: RunResult\n /** 进程退出码:0=全部成功, 1=部分失败, 2=全部失败 */\n exitCode: number\n}\n\n// ---------------------------------------------------------------------------\n// DependfixApp\n// ---------------------------------------------------------------------------\n\nexport class DependfixApp {\n private readonly config: RuntimeConfig\n private readonly workDir: string\n private readonly logger: Logger\n private readonly verbose: boolean\n private readonly customCommands?: string[]\n private readonly runId: string\n\n private readonly allAlerts: NormalizedSecurityAlert[] = []\n private readonly allActions: FixAction[] = []\n private readonly allErrors: FixError[] = []\n private readonly repoResults: RepositoryResult[] = []\n private readonly summary: RunSummary = createEmptyRunSummary()\n private startedAt: string = ''\n private finishedAt: string = ''\n\n constructor(options: DependfixAppOptions) {\n this.config = options.config\n this.workDir = options.workDir ?? process.cwd()\n this.verbose = options.verbose ?? false\n this.customCommands = options.commands\n this.runId = `dependfix-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n\n this.logger = createLogger({\n name: 'dependfix',\n minLevel: this.verbose ? 'debug' : 'info',\n })\n }\n\n /** 供辅助方法使用的状态切片。 */\n private get ctx(): AppContext {\n return {\n config: this.config,\n workDir: this.workDir,\n logger: this.logger,\n customCommands: this.customCommands,\n runId: this.runId,\n allAlerts: this.allAlerts,\n allActions: this.allActions,\n allErrors: this.allErrors,\n repoResults: this.repoResults,\n summary: this.summary,\n startedAt: this.startedAt,\n finishedAt: this.finishedAt,\n }\n }\n\n // -----------------------------------------------------------------------\n // Public API\n // -----------------------------------------------------------------------\n\n /** 执行完整的编排流程,返回结构化结果和退出码。 */\n async run(): Promise<DependfixRunResult> {\n this.startedAt = new Date().toISOString()\n this.logger.info(`Starting dependfix run ${this.runId}`, {\n mode: this.config.mode,\n repositories: this.config.repositories,\n })\n\n try {\n await this.executeMode()\n } catch (error: unknown) {\n const message = toErrorMessage(error)\n this.logger.error(`Unexpected error: ${message}`)\n this.allErrors.push({\n repository: '*',\n stage: 'report',\n category: 'FATAL',\n message,\n })\n }\n\n this.finishedAt = new Date().toISOString()\n computeSummary(this.ctx)\n\n const runResult = buildRunResult(this.ctx)\n const exitCode = computeExitCode(this.ctx)\n\n // 生成并写入报告\n try {\n const md = generateMarkdownReport(runResult)\n const json = generateJsonReport(runResult)\n writeReport(md, json, this.startedAt, this.runId)\n this.logger.info(`Reports written to ./dependfix-reports/`)\n } catch (reportError: unknown) {\n const message = toErrorMessage(reportError)\n this.logger.error(`Failed to write reports: ${message}`)\n }\n\n // 确保目标仓库的 .gitignore 忽略报告目录\n ensureGitignore(this.workDir)\n\n this.logger.info(`Run ${this.runId} completed`, { exitCode })\n return { result: runResult, exitCode }\n }\n\n // -----------------------------------------------------------------------\n // Mode dispatch\n // -----------------------------------------------------------------------\n\n private async executeMode(): Promise<void> {\n switch (this.config.mode) {\n case 'report-only':\n await this.executeReportMode()\n break\n case 'fix':\n await this.executeFixMode()\n break\n case 'fix-and-pr':\n await this.executeFixAndPrMode()\n break\n case 'cleanup-branches':\n await this.executeCleanupBranchesMode()\n break\n }\n }\n\n // -----------------------------------------------------------------------\n // report-only mode\n // -----------------------------------------------------------------------\n\n private async executeReportMode(): Promise<void> {\n const client = this.createClient()\n for (const repo of this.config.repositories) {\n await this.processRepoForReport(client, repo)\n }\n }\n\n private async processRepoForReport(client: Octokit, repo: string): Promise<void> {\n const startTime = Date.now()\n const [owner, name] = repo.split('/')\n\n try {\n const alerts = await fetchDependabotAlerts(client, { owner, repo: name })\n const { filtered } = filterAlerts(alerts, { severityThreshold: this.config.severityThreshold })\n const prioritized = prioritizeAlerts(filtered)\n const { limited } = limitAlerts(prioritized, this.config.maxAlertsPerRepository)\n\n const defaultBranch = await this.fetchDefaultBranch(client, owner, name)\n\n this.allAlerts.push(...limited)\n this.repoResults.push({\n repository: repo,\n defaultBranch,\n alertsCount: limited.length,\n fixable: limited.filter((a) => a.fixable).length,\n fixed: 0,\n failed: 0,\n lockfileRepaired: false,\n durationMs: Date.now() - startTime,\n })\n\n this.logger.info(`Fetched ${limited.length} alerts for ${repo}`)\n } catch (error: unknown) {\n const message = toErrorMessage(error)\n this.logger.error(`Failed to fetch alerts for ${repo}: ${message}`)\n this.allErrors.push({\n repository: repo,\n stage: 'fetch',\n category: 'FETCH_FAILED',\n message,\n })\n this.repoResults.push({\n repository: repo,\n defaultBranch: '',\n alertsCount: 0,\n fixable: 0,\n fixed: 0,\n failed: 0,\n lockfileRepaired: false,\n durationMs: Date.now() - startTime,\n })\n }\n }\n\n // -----------------------------------------------------------------------\n // fix mode\n // -----------------------------------------------------------------------\n\n private async executeFixMode(): Promise<void> {\n const client = this.createClient()\n for (const repo of this.config.repositories) {\n await this.processRepoForFix(client, repo)\n }\n\n // 修复完成后,按需在本地当前分支直接提交(不推送、不创建 PR)\n if (this.config.commit) {\n try {\n commitLocalChanges(this.ctx)\n } catch (error: unknown) {\n const message = toErrorMessage(error)\n this.logger.error(`Local commit failed: ${message}`)\n this.allErrors.push({\n repository: '*',\n stage: 'fix',\n category: 'COMMIT_FAILED',\n message,\n })\n }\n }\n }\n\n private async processRepoForFix(client: Octokit, repo: string): Promise<void> {\n const startTime = Date.now()\n const [owner, name] = repo.split('/')\n let alertsCount = 0\n let fixable = 0\n let fixed = 0\n let failed = 0\n let lockfileRepaired = false\n let verificationPassed: boolean | undefined\n let defaultBranch = ''\n\n try {\n // 1. Fetch alerts\n const rawAlerts = await fetchDependabotAlerts(client, { owner, repo: name })\n const { filtered } = filterAlerts(rawAlerts, { severityThreshold: this.config.severityThreshold })\n const prioritized = prioritizeAlerts(filtered)\n const { limited } = limitAlerts(prioritized, this.config.maxAlertsPerRepository)\n alertsCount = limited.length\n fixable = limited.filter((a) => a.fixable).length\n\n defaultBranch = await this.fetchDefaultBranch(client, owner, name)\n\n this.allAlerts.push(...limited)\n\n // 2. Upgrade fixable dependencies\n const fixableAlerts = limited.filter((a) => a.fixable && a.recommendedVersion)\n for (const alert of fixableAlerts) {\n const action = await upgradeAlert(this.ctx, alert)\n this.allActions.push(action)\n if (action.success) {\n fixed++\n } else {\n failed++\n }\n }\n\n // Track skipped (non-fixable) alerts\n const skippedCount = limited.length - fixableAlerts.length\n this.summary.alertsSkipped += skippedCount\n\n // 3. Lockfile repair\n const repairAction = tryLockfileRepair(this.ctx, repo)\n this.allActions.push(repairAction)\n if (repairAction.success) {\n lockfileRepaired = true\n }\n\n // 4. Verification (skip in dry-run mode)\n if (!this.config.dryRun) {\n const verifyActions = await verifyProject(this.ctx, repo)\n this.allActions.push(...verifyActions)\n verificationPassed = verifyActions.every((a) => a.success)\n } else {\n this.logger.info(`[dry-run] Skipping verification for ${repo}`)\n verificationPassed = undefined\n }\n } catch (error: unknown) {\n const message = toErrorMessage(error)\n this.logger.error(`Failed to process ${repo}: ${message}`)\n this.allErrors.push({\n repository: repo,\n stage: 'fix',\n category: 'PROCESS_FAILED',\n message,\n })\n }\n\n this.repoResults.push({\n repository: repo,\n defaultBranch,\n alertsCount,\n fixable,\n fixed,\n failed,\n lockfileRepaired,\n verificationPassed,\n durationMs: Date.now() - startTime,\n })\n }\n\n // -----------------------------------------------------------------------\n // fix-and-pr mode\n // -----------------------------------------------------------------------\n\n /**\n * 修复 → 计算内容指纹 → 查重 →(同指纹跳过 / 异指纹关旧开新)。\n *\n * 去重语义(T210):\n * - 同告警集 → 同修复结果 → 同指纹 → 跳过,不重复提交相同 PR\n * - 内容变化 → 先创建新 PR(body 注明 Supersedes),成功后关闭旧 PR;\n * 新 PR 创建失败时保留旧 PR,避免出现\"无 PR 窗口\"\n */\n private async executeFixAndPrMode(): Promise<void> {\n const client = this.createClient()\n\n // 1. Run fix pipeline for all repos (same as fix mode)\n for (const repo of this.config.repositories) {\n await this.processRepoForFix(client, repo)\n }\n\n // 1.5 Optional: report merged dependfix branches for manual cleanup.\n // 在 early return 之前执行,保证\"PR 已存在(skip)\"等高频路径也能输出清单。\n if (this.config.cleanupBranches) {\n await reportCleanupCandidates(this.ctx, client)\n }\n\n // 2. Check if there are changes to commit (skip dry-run)\n if (this.config.dryRun) {\n this.logger.info('[dry-run] Would create branch, commit, push, and PR')\n return\n }\n\n if (!hasGitChanges(this.workDir)) {\n this.logger.info('No changes to commit — skipping PR creation')\n return\n }\n\n if (this.config.repositories.length === 0) {\n return\n }\n const firstRepo = this.config.repositories[0]\n const [owner, repo] = firstRepo.split('/')\n\n try {\n // 3. Compute content fingerprint from actual fix results\n const fingerprint = computeFixFingerprint(this.allActions)\n this.logger.info(`Fix fingerprint: ${fingerprint}`)\n\n // 4. Dedup: decide skip / supersede based on existing dependfix open PRs\n const existingPRs = await findDependfixOpenPR(client, owner, repo)\n const plan = computeFixAndPrPlan(existingPRs, fingerprint)\n\n if (plan.action === 'skip' && plan.sameContentPR) {\n this.logger.info(\n `Open PR #${plan.sameContentPR.number} already contains identical fixes — skipping PR creation`,\n )\n this.allActions.push({\n type: 'dependency-upgrade',\n repository: firstRepo,\n target: `PR #${plan.sameContentPR.number} (existing)`,\n fromVersion: undefined,\n toVersion: plan.sameContentPR.htmlUrl,\n isMajor: false,\n success: true,\n durationMs: 0,\n })\n\n // 关闭并存的异指纹旧 PR(保持\"永远单线\",异常态也收敛)\n await closeSupersededPRs(this.ctx, client, owner, repo, plan.supersedePRs)\n return\n }\n\n if (plan.supersedePRs.length > 0) {\n this.logger.info(\n `Existing PR(s) with different content will be superseded: ${plan.supersedePRs.map((pr) => `#${pr.number}`).join(', ')}`,\n )\n }\n\n // 5. Create fix branch (content-addressed) + commit + push\n const branchName = `dependfix/auto-fix-${fingerprint}`\n createFixBranch(branchName, this.workDir)\n this.logger.info(`Creating fix branch: ${branchName}`)\n\n this.logger.info('Staging and committing changes')\n stageAndCommit(FIX_COMMIT_MESSAGE, this.workDir)\n\n this.logger.info(`Pushing branch: ${branchName}`)\n pushBranch(branchName, this.workDir)\n\n // 6. Create PR (one PR covering all repos)\n const defaultBranch = await this.fetchDefaultBranch(client, owner, repo)\n\n // Build RunResult for PR body\n computeSummary(this.ctx)\n const runResult = buildRunResult(this.ctx)\n const prBody = generatePRBody(\n runResult,\n plan.supersedePRs.map((pr) => pr.number),\n )\n const prTitle = `fix(deps): automated security fix — ${this.summary.alertsFixed} upgrades`\n\n const pr = await createPullRequest({\n octokit: client,\n owner,\n repo,\n headBranch: branchName,\n baseBranch: defaultBranch,\n title: prTitle,\n body: prBody,\n })\n\n this.logger.info(`PR created: ${pr.htmlUrl}`)\n\n // Record PR as a fix action before closing superseded PRs,\n // so a close failure never hides the successfully created PR\n this.allActions.push({\n type: 'dependency-upgrade',\n repository: firstRepo,\n target: `PR #${pr.number}`,\n fromVersion: undefined,\n toVersion: pr.htmlUrl,\n isMajor: false,\n success: true,\n durationMs: 0,\n })\n\n // 7. Close superseded PRs only after new PR created successfully\n await closeSupersededPRs(this.ctx, client, owner, repo, plan.supersedePRs)\n } catch (error: unknown) {\n const message = toErrorMessage(error)\n this.logger.error(`PR creation failed: ${message}`)\n this.allErrors.push({\n repository: this.config.repositories[0] ?? '*',\n stage: 'report',\n category: 'PR_CREATION_FAILED',\n message,\n })\n }\n }\n\n // -----------------------------------------------------------------------\n // cleanup-branches mode\n // -----------------------------------------------------------------------\n\n /**\n * 清理已合并(或已关闭)的 dependfix 分支。\n *\n * - 分类:已合并(安全清理)/ 已关闭未合并(supersede 孤儿)/ open(跳过)\n * - 删除前必须交互式确认(y/N);非 TTY 环境(CI)默认拒绝\n * - 只删 `dependfix/` 前缀分支,绝不触碰 open PR 对应分支\n * - dry-run 模式仅列清单并打印 \"would delete\",不执行删除\n */\n private async executeCleanupBranchesMode(): Promise<void> {\n const client = this.createClient()\n\n for (const repo of this.config.repositories) {\n const [owner, name] = repo.split('/')\n try {\n const branches = await listDependfixBranches(client, owner, name)\n if (branches.length === 0) {\n this.logger.info(`[cleanup] ${repo}: no dependfix branches found`)\n continue\n }\n\n const statuses: DependfixBranchStatus[] = []\n for (const branch of branches) {\n statuses.push(await getBranchPrStatus(client, owner, name, branch))\n }\n\n const merged = statuses.filter((s) => s.merged)\n const orphaned = statuses.filter((s) => s.closed && !s.merged)\n const open = statuses.filter((s) => !s.closed)\n\n this.logger.info(\n `[cleanup] ${repo}: ${merged.length} merged, ${orphaned.length} closed, ${open.length} kept`,\n )\n for (const s of merged) {\n this.logger.info(` [merged] ${s.branch}${s.prNumber ? ` (PR #${s.prNumber})` : ''}`)\n }\n for (const s of orphaned) {\n this.logger.info(` [closed] ${s.branch}${s.prNumber ? ` (PR #${s.prNumber})` : ''}`)\n }\n for (const s of open) {\n const label = s.prNumber ? `[open — kept]` : `[no PR — kept]`\n this.logger.info(` ${label} ${s.branch}${s.prNumber ? ` (PR #${s.prNumber})` : ''}`)\n }\n\n const candidates = [...merged, ...orphaned]\n if (candidates.length === 0) {\n this.logger.info('[cleanup] nothing to delete')\n continue\n }\n\n if (this.config.dryRun) {\n this.logger.info(`[dry-run] Would delete ${candidates.length} branch(es): ${candidates.map((s) => s.branch).join(', ')}`)\n continue\n }\n\n if (!(await confirmCleanup(this.ctx, repo, candidates))) {\n this.logger.info('[cleanup] cancelled by user')\n continue\n }\n\n for (const s of candidates) {\n try {\n await deleteRemoteBranch(client, owner, name, s.branch)\n this.logger.info(`Deleted branch: ${s.branch}`)\n this.allActions.push({\n type: 'branch-cleanup',\n repository: repo,\n target: s.branch,\n success: true,\n diff: s.merged ? 'merged' : 'closed',\n durationMs: 0,\n })\n } catch (error: unknown) {\n const message = toErrorMessage(error)\n this.logger.error(`Failed to delete branch ${s.branch}: ${message}`)\n this.allErrors.push({\n repository: repo,\n stage: 'report',\n category: 'BRANCH_DELETE_FAILED',\n message: `Failed to delete ${s.branch}: ${message}`,\n })\n }\n }\n } catch (error: unknown) {\n const message = toErrorMessage(error)\n this.logger.error(`[cleanup] failed for ${repo}: ${message}`)\n this.allErrors.push({\n repository: repo,\n stage: 'report',\n category: 'CLEANUP_FAILED',\n message,\n })\n }\n }\n }\n\n // -----------------------------------------------------------------------\n // GitHub helpers\n // -----------------------------------------------------------------------\n\n private createClient(): Octokit {\n return createGitHubClient({ token: this.config.githubToken })\n }\n\n /**\n * 获取仓库的默认分支。\n * 失败时返回 `'unknown'`(不阻塞主流程)。\n */\n private async fetchDefaultBranch(client: Octokit, owner: string, repo: string): Promise<string> {\n try {\n const { data } = await client.rest.repos.get({ owner, repo })\n return data.default_branch\n } catch {\n return 'unknown'\n }\n }\n}\n","import { readFileSync, existsSync } from 'node:fs'\nimport { AppError, isValidRepoIdentifier } from '@dependfix/core'\n\n/**\n * 从文件读取仓库列表(每行一个 `owner/repo`)。\n * 空行和 `#` 开头的注释行会被跳过。\n */\nexport function readReposFile(filePath: string): string[] {\n if (!existsSync(filePath)) {\n throw new AppError(\n 'REPO_FILE_NOT_FOUND',\n `Repositories file not found: ${filePath}`,\n )\n }\n\n const content = readFileSync(filePath, 'utf-8')\n const lines = content.split('\\n')\n const repos: string[] = []\n\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i].trim()\n\n // 跳过空行和注释\n if (!line || line.startsWith('#')) {\n continue\n }\n\n if (!isValidRepoIdentifier(line)) {\n throw new AppError(\n 'CONFIG_VALIDATION_ERROR',\n `Invalid repository identifier at ${filePath}:${i + 1}: \"${line}\". Expected format: owner/repo`,\n )\n }\n\n repos.push(line)\n }\n\n if (repos.length === 0) {\n throw new AppError(\n 'CONFIG_VALIDATION_ERROR',\n `No valid repository identifiers found in ${filePath}`,\n )\n }\n\n return repos\n}\n\n/**\n * 合并 CLI 传入仓库列表与文件中的仓库列表,去重并校验。\n */\nexport function resolveRepoList(\n cliRepos: string[],\n reposFilePath?: string,\n): string[] {\n const repos = [...cliRepos]\n\n if (reposFilePath) {\n const fileRepos = readReposFile(reposFilePath)\n repos.push(...fileRepos)\n }\n\n const deduped = [...new Set(repos)]\n\n for (const repo of deduped) {\n if (!isValidRepoIdentifier(repo)) {\n throw new AppError(\n 'CONFIG_VALIDATION_ERROR',\n `Invalid repository identifier: \"${repo}\". Expected format: owner/repo`,\n )\n }\n }\n\n return deduped\n}\n","import { execSync } from 'node:child_process'\nimport { AppError, isValidRepoIdentifier, type SeverityThreshold } from '@dependfix/core'\nimport { resolveRepoList } from '../github/repo-selector'\n\nexport const RUNTIME_MODES = ['report-only', 'fix', 'fix-and-pr', 'cleanup-branches'] as const\nexport const SEVERITY_THRESHOLDS = ['critical', 'high', 'medium', 'all'] as const\n\nexport type RuntimeMode = typeof RUNTIME_MODES[number]\nexport type { SeverityThreshold }\n\nexport interface RuntimeConfig {\n mode: RuntimeMode\n severityThreshold: SeverityThreshold\n repositories: string[]\n dryRun: boolean\n createPullRequest: boolean\n /** 修复完成后是否在本地当前分支直接提交(不推送、不创建 PR) */\n commit: boolean\n /** fix-and-pr 模式下结束后是否列出已合并的 dependfix 分支到报告(不自动删除) */\n cleanupBranches: boolean\n githubToken: string\n maxAlertsPerRepository: number\n}\n\nexport interface CliConfigOverrides {\n mode?: RuntimeMode\n severityThreshold?: SeverityThreshold\n repositories?: string[]\n reposFilePath?: string\n dryRun?: boolean\n createPullRequest?: boolean\n /** 修复完成后是否在本地当前分支直接提交 */\n commit?: boolean\n /** fix-and-pr 模式下结束后是否列出已合并的 dependfix 分支到报告 */\n cleanupBranches?: boolean\n githubToken?: string\n maxAlertsPerRepository?: number\n /** 是否输出详细日志 */\n verbose?: boolean\n /** 自定义验证命令(覆盖默认的 `pnpm install --frozen-lockfile` / `pnpm lint` / `pnpm build`) */\n commands?: string[]\n}\n\nexport interface ResolveRuntimeConfigOptions {\n env?: NodeJS.ProcessEnv\n cliOverrides?: CliConfigOverrides\n /** 工作目录,用于从 git remote 推断仓库名(默认 `process.cwd()`) */\n workDir?: string\n}\n\nexport const DEFAULT_RUNTIME_CONFIG: Omit<RuntimeConfig, 'githubToken' | 'repositories' | 'dryRun' | 'createPullRequest' | 'commit' | 'cleanupBranches'> = {\n mode: 'report-only',\n severityThreshold: 'high',\n maxAlertsPerRepository: 10,\n}\n\nfunction isRuntimeMode(value: string): value is RuntimeMode {\n return RUNTIME_MODES.includes(value as RuntimeMode)\n}\n\nfunction isSeverityThreshold(value: string): value is SeverityThreshold {\n return SEVERITY_THRESHOLDS.includes(value as SeverityThreshold)\n}\n\nfunction normalizeBoolean(value: string | undefined, fieldName: string): boolean | undefined {\n if (value === undefined || value.trim() === '') {\n return undefined\n }\n\n const normalized = value.trim().toLowerCase()\n\n if (['true', '1', 'yes', 'on'].includes(normalized)) {\n return true\n }\n\n if (['false', '0', 'no', 'off'].includes(normalized)) {\n return false\n }\n\n throw new AppError('CONFIG_VALIDATION_ERROR', `Invalid boolean value for ${fieldName}: ${value}`)\n}\n\nfunction normalizeInteger(value: string | undefined, fieldName: string): number | undefined {\n if (value === undefined || value.trim() === '') {\n return undefined\n }\n\n const parsed = Number.parseInt(value, 10)\n\n if (!Number.isInteger(parsed) || parsed <= 0) {\n throw new AppError('CONFIG_VALIDATION_ERROR', `${fieldName} must be a positive integer`)\n }\n\n return parsed\n}\n\nfunction normalizeList(value: string | undefined): string[] | undefined {\n if (value === undefined || value.trim() === '') {\n return undefined\n }\n\n const items = value\n .split(',')\n .map((item) => item.trim())\n .filter(Boolean)\n\n return items.length > 0 ? items : undefined\n}\n\nfunction readRuntimeMode(value: string | undefined, fieldName: string): RuntimeMode | undefined {\n if (value === undefined || value.trim() === '') {\n return undefined\n }\n\n if (!isRuntimeMode(value)) {\n throw new AppError('CONFIG_VALIDATION_ERROR', `${fieldName} must be one of: ${RUNTIME_MODES.join(', ')}`)\n }\n\n return value\n}\n\nfunction readSeverityThreshold(value: string | undefined, fieldName: string): SeverityThreshold | undefined {\n if (value === undefined || value.trim() === '') {\n return undefined\n }\n\n if (!isSeverityThreshold(value)) {\n throw new AppError('CONFIG_VALIDATION_ERROR', `${fieldName} must be one of: ${SEVERITY_THRESHOLDS.join(', ')}`)\n }\n\n return value\n}\n\nexport function readEnvConfig(env: NodeJS.ProcessEnv = process.env): CliConfigOverrides {\n return {\n mode: readRuntimeMode(env.AUTO_FIX_GITHUB_SECURITY_MODE, 'AUTO_FIX_GITHUB_SECURITY_MODE'),\n severityThreshold: readSeverityThreshold(env.AUTO_FIX_GITHUB_SECURITY_SEVERITY_THRESHOLD, 'AUTO_FIX_GITHUB_SECURITY_SEVERITY_THRESHOLD'),\n repositories: normalizeList(env.AUTO_FIX_GITHUB_SECURITY_REPOSITORIES),\n dryRun: normalizeBoolean(env.AUTO_FIX_GITHUB_SECURITY_DRY_RUN, 'AUTO_FIX_GITHUB_SECURITY_DRY_RUN'),\n createPullRequest: normalizeBoolean(env.AUTO_FIX_GITHUB_SECURITY_CREATE_PR, 'AUTO_FIX_GITHUB_SECURITY_CREATE_PR'),\n commit: normalizeBoolean(env.AUTO_FIX_GITHUB_SECURITY_COMMIT, 'AUTO_FIX_GITHUB_SECURITY_COMMIT'),\n cleanupBranches: normalizeBoolean(env.AUTO_FIX_GITHUB_SECURITY_CLEANUP_BRANCHES, 'AUTO_FIX_GITHUB_SECURITY_CLEANUP_BRANCHES'),\n githubToken: env.AUTO_FIX_GITHUB_SECURITY_GITHUB_TOKEN?.trim() || env.GITHUB_TOKEN?.trim() || undefined,\n maxAlertsPerRepository: normalizeInteger(env.AUTO_FIX_GITHUB_SECURITY_MAX_ALERTS_PER_REPOSITORY, 'AUTO_FIX_GITHUB_SECURITY_MAX_ALERTS_PER_REPOSITORY'),\n }\n}\n\nfunction resolveDryRun(mode: RuntimeMode, cliOverrides: CliConfigOverrides, envConfig: CliConfigOverrides): boolean {\n if (cliOverrides.dryRun !== undefined) {\n return cliOverrides.dryRun\n }\n\n if (envConfig.dryRun !== undefined) {\n return envConfig.dryRun\n }\n\n return mode === 'report-only'\n}\n\nfunction resolveCreatePullRequest(mode: RuntimeMode, cliOverrides: CliConfigOverrides, envConfig: CliConfigOverrides): boolean {\n if (cliOverrides.createPullRequest !== undefined) {\n return cliOverrides.createPullRequest\n }\n\n if (envConfig.createPullRequest !== undefined) {\n return envConfig.createPullRequest\n }\n\n return mode === 'fix-and-pr'\n}\n\nfunction resolveCommit(cliOverrides: CliConfigOverrides, envConfig: CliConfigOverrides): boolean {\n if (cliOverrides.commit !== undefined) {\n return cliOverrides.commit\n }\n\n if (envConfig.commit !== undefined) {\n return envConfig.commit\n }\n\n return false\n}\n\nfunction resolveCleanupBranches(cliOverrides: CliConfigOverrides, envConfig: CliConfigOverrides): boolean {\n if (cliOverrides.cleanupBranches !== undefined) {\n return cliOverrides.cleanupBranches\n }\n\n if (envConfig.cleanupBranches !== undefined) {\n return envConfig.cleanupBranches\n }\n\n return false\n}\n\nfunction validateRuntimeConfig(config: RuntimeConfig): RuntimeConfig {\n if (!config.githubToken) {\n throw new AppError(\n 'CONFIG_VALIDATION_ERROR',\n 'Missing GitHub token. Provide GITHUB_TOKEN or AUTO_FIX_GITHUB_SECURITY_GITHUB_TOKEN.',\n )\n }\n\n if (config.repositories.length === 0) {\n throw new AppError(\n 'CONFIG_VALIDATION_ERROR',\n 'Missing target repositories. Provide --repo, --repository, --repos-file or AUTO_FIX_GITHUB_SECURITY_REPOSITORIES.',\n )\n }\n\n for (const repo of config.repositories) {\n if (!isValidRepoIdentifier(repo)) {\n throw new AppError('CONFIG_VALIDATION_ERROR', `Invalid repository identifier: \"${repo}\". Expected format: owner/repo`)\n }\n }\n\n if (config.mode === 'report-only' && config.createPullRequest) {\n throw new AppError('CONFIG_VALIDATION_ERROR', 'createPullRequest cannot be enabled when mode is report-only.')\n }\n\n if (config.dryRun && config.createPullRequest) {\n throw new AppError('CONFIG_VALIDATION_ERROR', 'createPullRequest cannot be enabled while dryRun is true.')\n }\n\n if (config.commit && config.mode !== 'fix') {\n throw new AppError('CONFIG_VALIDATION_ERROR', 'commit is only supported in fix mode.')\n }\n\n if (config.commit && config.dryRun) {\n throw new AppError('CONFIG_VALIDATION_ERROR', 'commit cannot be enabled while dryRun is true.')\n }\n\n if (config.commit && config.createPullRequest) {\n throw new AppError('CONFIG_VALIDATION_ERROR', 'commit cannot be enabled together with createPullRequest. Use fix-and-pr mode instead.')\n }\n\n return config\n}\n\nexport function resolveRuntimeConfig(options: ResolveRuntimeConfigOptions = {}): RuntimeConfig {\n const envConfig = readEnvConfig(options.env)\n const cliOverrides = options.cliOverrides ?? {}\n const mode = cliOverrides.mode ?? envConfig.mode ?? DEFAULT_RUNTIME_CONFIG.mode\n\n let repositories = resolveRepoList([\n ...(envConfig.repositories ?? []),\n ...(cliOverrides.repositories ?? []),\n ], cliOverrides.reposFilePath)\n\n // 自动推断:所有来源都未提供仓库时,从 git remote 提取\n if (repositories.length === 0) {\n const workDir = options.workDir ?? process.cwd()\n const inferred = inferRepoFromGitRemote(workDir)\n if (inferred) {\n repositories = [inferred]\n }\n }\n\n const config: RuntimeConfig = {\n mode,\n severityThreshold: cliOverrides.severityThreshold ?? envConfig.severityThreshold ?? DEFAULT_RUNTIME_CONFIG.severityThreshold,\n repositories,\n dryRun: resolveDryRun(mode, cliOverrides, envConfig),\n createPullRequest: resolveCreatePullRequest(mode, cliOverrides, envConfig),\n commit: resolveCommit(cliOverrides, envConfig),\n cleanupBranches: resolveCleanupBranches(cliOverrides, envConfig),\n githubToken: cliOverrides.githubToken ?? envConfig.githubToken ?? '',\n maxAlertsPerRepository: cliOverrides.maxAlertsPerRepository ?? envConfig.maxAlertsPerRepository ?? DEFAULT_RUNTIME_CONFIG.maxAlertsPerRepository,\n }\n\n return validateRuntimeConfig(config)\n}\n\n// ---------------------------------------------------------------------------\n// Git remote inference\n// ---------------------------------------------------------------------------\n\n/** 匹配 GitHub remote URL 的正则(HTTPS / SSH / git@ 格式) */\nconst GITHUB_REMOTE_RE = /github\\.com[/:]([^/]+)\\/([^/\\s.]+?)(?:\\.git)?\\s*$/i\n\n/**\n * 从 git remote origin 推断 owner/repo。\n *\n * 支持格式:\n * - `https://github.com/owner/repo.git`\n * - `git@github.com:owner/repo.git`\n * - `ssh://git@github.com/owner/repo.git`\n *\n * @returns `owner/repo` 或 `null`(非 GitHub / 无 origin)\n */\nexport function inferRepoFromGitRemote(workDir: string): string | null {\n try {\n const url = execSync('git remote get-url origin', {\n cwd: workDir,\n encoding: 'utf-8',\n stdio: 'pipe',\n }).trim()\n\n const match = GITHUB_REMOTE_RE.exec(url)\n return match ? `${match[1]}/${match[2]}` : null\n } catch {\n return null\n }\n}\n","import {\n defineCommand,\n parseArgs,\n type ArgsDef,\n type ParsedArgs,\n} from 'citty'\nimport { AppError, toAppError, isValidRepoIdentifier } from '@dependfix/core'\nimport { DependfixApp } from '../app'\nimport {\n type CliConfigOverrides,\n type RuntimeMode,\n type SeverityThreshold,\n resolveRuntimeConfig,\n RUNTIME_MODES,\n SEVERITY_THRESHOLDS,\n} from '../config'\n\n// ---------------------------------------------------------------------------\n// Public interfaces\n// ---------------------------------------------------------------------------\n\nexport interface CliInvocation {\n mode?: RuntimeMode\n configOverrides: CliConfigOverrides\n rawArgs: string[]\n}\n\nexport interface CliRunResult {\n ok: true\n invocation: CliInvocation\n config: ReturnType<typeof resolveRuntimeConfig>\n}\n\n// ---------------------------------------------------------------------------\n// Args definition for citty\n// ---------------------------------------------------------------------------\n\nconst argsDef = {\n mode: {\n type: 'positional' as const,\n description: '运行模式:report-only, fix, fix-and-pr, cleanup-branches',\n required: false,\n default: 'report-only' as const,\n },\n repo: {\n type: 'string' as const,\n description: '目标仓库 (owner/repo),逗号分隔多个',\n alias: ['r', 'repository', 'repositories'],\n },\n 'repos-file': {\n type: 'string' as const,\n description: '从文件读取仓库列表(每行一个 owner/repo)',\n },\n 'severity-threshold': {\n type: 'string' as const,\n description: '严重级别阈值:critical, high, medium, all',\n default: 'high' as const,\n },\n 'dry-run': {\n type: 'boolean' as const,\n description: '试运行模式,不实际写入文件',\n negativeDescription: '关闭试运行模式',\n },\n 'create-pr': {\n type: 'boolean' as const,\n description: '创建 Pull Request',\n negativeDescription: '不创建 Pull Request',\n },\n commit: {\n type: 'boolean' as const,\n description: '修复完成后在本地当前分支直接提交(不推送、不创建 PR)',\n negativeDescription: '不自动提交(默认)',\n },\n 'cleanup-branches': {\n type: 'boolean' as const,\n description: '(fix-and-pr 模式)结束后列出已合并的 dependfix 分支到报告,不自动删除',\n negativeDescription: '不执行分支清理检查',\n },\n 'github-token': {\n type: 'string' as const,\n description: 'GitHub Personal Access Token',\n },\n 'max-alerts-per-repository': {\n type: 'string' as const,\n description: '每个仓库最多处理的告警数',\n default: '10' as const,\n },\n commands: {\n type: 'string' as const,\n description: '自定义验证命令(逗号分隔),覆盖默认的 install/lint/build',\n },\n verbose: {\n type: 'boolean' as const,\n description: '输出详细日志',\n },\n} satisfies ArgsDef\n\n// ---------------------------------------------------------------------------\n// Conversion helpers\n// ---------------------------------------------------------------------------\n\nfunction isRuntimeMode(value: string): value is RuntimeMode {\n return RUNTIME_MODES.includes(value as RuntimeMode)\n}\n\nfunction isSeverityThreshold(value: string): value is SeverityThreshold {\n return SEVERITY_THRESHOLDS.includes(value as SeverityThreshold)\n}\n\nfunction appendRepositories(target: string[], value: string): void {\n for (const repository of value.split(',')) {\n const trimmed = repository.trim()\n\n if (!trimmed) {\n continue\n }\n\n if (!isValidRepoIdentifier(trimmed)) {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid repository identifier: \"${trimmed}\". Expected format: owner/repo`,\n )\n }\n\n target.push(trimmed)\n }\n}\n\nfunction parseCommandsFlag(value: string): string[] {\n return value\n .split(',')\n .map((cmd) => cmd.trim())\n .filter(Boolean)\n}\n\nfunction parsedArgsToCliOverrides(parsed: ParsedArgs<typeof argsDef>): CliConfigOverrides {\n const overrides: CliConfigOverrides = {}\n\n // mode\n const rawMode = parsed.mode\n if (isRuntimeMode(rawMode)) {\n overrides.mode = rawMode\n }\n\n // repositories (from --repo / --repository)\n const repoValue = parsed.repo\n if (repoValue) {\n const repos: string[] = []\n appendRepositories(repos, repoValue)\n if (repos.length > 0) {\n overrides.repositories = repos\n }\n }\n\n // repos-file\n const reposFile = parsed['repos-file']\n if (reposFile) {\n overrides.reposFilePath = reposFile\n }\n\n // severity-threshold\n const severityThreshold = parsed['severity-threshold']\n if (isSeverityThreshold(severityThreshold)) {\n overrides.severityThreshold = severityThreshold\n }\n\n // dry-run (three-state: true / false / undefined)\n if (parsed['dry-run'] !== undefined) {\n overrides.dryRun = parsed['dry-run']\n }\n\n // create-pr (three-state: true / false / undefined)\n if (parsed['create-pr'] !== undefined) {\n overrides.createPullRequest = parsed['create-pr']\n }\n\n // commit (three-state: true / false / undefined)\n if (parsed.commit !== undefined) {\n overrides.commit = parsed.commit\n }\n\n // cleanup-branches (three-state: true / false / undefined)\n if (parsed['cleanup-branches'] !== undefined) {\n overrides.cleanupBranches = parsed['cleanup-branches']\n }\n\n // github-token\n const githubToken = parsed['github-token']\n if (githubToken) {\n overrides.githubToken = githubToken\n }\n\n // max-alerts-per-repository\n const maxAlerts = parsed['max-alerts-per-repository']\n if (maxAlerts) {\n const num = Number.parseInt(maxAlerts, 10)\n if (Number.isNaN(num)) {\n throw new AppError(\n 'ARGUMENT_PARSE_ERROR',\n `Invalid --max-alerts-per-repository value: \"${maxAlerts}\". Expected a positive integer.`,\n )\n }\n overrides.maxAlertsPerRepository = num\n }\n\n // verbose (three-state: true / false / undefined)\n if (parsed.verbose !== undefined) {\n overrides.verbose = parsed.verbose\n }\n\n // commands\n const commandsValue = parsed.commands\n if (commandsValue) {\n overrides.commands = parseCommandsFlag(commandsValue)\n }\n\n return overrides\n}\n\n// ---------------------------------------------------------------------------\n// Public API (unchanged signatures)\n// ---------------------------------------------------------------------------\n\nexport function parseCliArgs(rawArgs: string[]): CliInvocation {\n const parsed = parseArgs<typeof argsDef>(rawArgs, argsDef)\n const configOverrides = parsedArgsToCliOverrides(parsed)\n\n return {\n mode: configOverrides.mode,\n configOverrides,\n rawArgs: [...rawArgs],\n }\n}\n\nexport function runCli(rawArgs: string[]): CliRunResult {\n const invocation = parseCliArgs(rawArgs)\n const config = resolveRuntimeConfig({\n env: process.env,\n cliOverrides: invocation.configOverrides,\n })\n\n return {\n ok: true,\n invocation,\n config,\n }\n}\n\n// ---------------------------------------------------------------------------\n// App execution\n// ---------------------------------------------------------------------------\n\nasync function runApp(rawArgs: string[]): Promise<number> {\n const invocation = parseCliArgs(rawArgs)\n const config = resolveRuntimeConfig({\n env: process.env,\n cliOverrides: invocation.configOverrides,\n })\n\n const app = new DependfixApp({\n config,\n verbose: invocation.configOverrides.verbose,\n commands: invocation.configOverrides.commands,\n })\n\n const { exitCode } = await app.run()\n return exitCode\n}\n\n// ---------------------------------------------------------------------------\n// citty command\n// ---------------------------------------------------------------------------\n\nexport const dependfixCommand = defineCommand({\n meta: {\n name: 'dependfix',\n version: '0.1.0',\n description: '自动化处理 Dependabot / Code Scanning 安全告警的修复工具',\n },\n args: argsDef,\n async run({ rawArgs }) {\n let exitCode = 1\n try {\n exitCode = await runApp(rawArgs)\n } catch (error: unknown) {\n const appError = toAppError(error, 'CLI_EXECUTION_FAILED')\n console.error(`Error: ${appError.message}`)\n exitCode = 1\n }\n process.exitCode = exitCode\n },\n})\n"],"x_google_ignoreList":[1,2,3,4],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,35 +1,76 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_cli = require('./cli.cjs');
|
|
3
|
+
let node_url = require("node:url");
|
|
4
|
+
let citty = require("citty");
|
|
5
|
+
let _dependfix_core = require("@dependfix/core");
|
|
2
6
|
|
|
3
|
-
//#region src/env.ts
|
|
4
|
-
const __PROD__ = process.env.NODE_ENV === "production";
|
|
5
|
-
const __DEV__ = process.env.NODE_ENV === "development";
|
|
6
|
-
|
|
7
|
-
//#endregion
|
|
8
7
|
//#region src/index.ts
|
|
9
8
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* @author CaoMeiYouRen
|
|
13
|
-
* @date 2020-11-28
|
|
14
|
-
* @export
|
|
9
|
+
* 简化调用的入口(内部数据处理,非 CLI 使用)。
|
|
15
10
|
*/
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
if (__DEV__) console.log("Hello development");
|
|
19
|
-
console.log("你好,世界!");
|
|
11
|
+
function main(args = process.argv.slice(2)) {
|
|
12
|
+
return require_cli.runCli(args);
|
|
20
13
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
console.log("require(\"os\"):", !!os);
|
|
26
|
-
} catch (e) {
|
|
27
|
-
console.log("require is not available:", e);
|
|
14
|
+
function isExecutedAsEntryPoint() {
|
|
15
|
+
const entry = process.argv[1];
|
|
16
|
+
if (!entry) return false;
|
|
17
|
+
return require("url").pathToFileURL(__filename).href === (0, node_url.pathToFileURL)(entry).href;
|
|
28
18
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
console.
|
|
19
|
+
if (isExecutedAsEntryPoint()) (0, citty.runMain)(require_cli.dependfixCommand).catch((error) => {
|
|
20
|
+
const appError = (0, _dependfix_core.toAppError)(error, "CLI_EXECUTION_FAILED");
|
|
21
|
+
console.error(appError.message);
|
|
22
|
+
process.exitCode = 1;
|
|
23
|
+
});
|
|
32
24
|
|
|
33
25
|
//#endregion
|
|
34
|
-
exports.
|
|
26
|
+
exports.DEFAULT_RUNTIME_CONFIG = require_cli.DEFAULT_RUNTIME_CONFIG;
|
|
27
|
+
exports.DependfixApp = require_cli.DependfixApp;
|
|
28
|
+
exports.RUNTIME_MODES = require_cli.RUNTIME_MODES;
|
|
29
|
+
exports.SEVERITY_THRESHOLDS = require_cli.SEVERITY_THRESHOLDS;
|
|
30
|
+
exports.classifyLockfileFailure = require_cli.classifyLockfileFailure;
|
|
31
|
+
exports.closePullRequest = require_cli.closePullRequest;
|
|
32
|
+
exports.computeFixAndPrPlan = require_cli.computeFixAndPrPlan;
|
|
33
|
+
exports.computeFixFingerprint = require_cli.computeFixFingerprint;
|
|
34
|
+
exports.computeLockfileDiff = require_cli.computeLockfileDiff;
|
|
35
|
+
exports.createFixBranch = require_cli.createFixBranch;
|
|
36
|
+
exports.createGitHubClient = require_cli.createGitHubClient;
|
|
37
|
+
exports.createPullRequest = require_cli.createPullRequest;
|
|
38
|
+
exports.deleteRemoteBranch = require_cli.deleteRemoteBranch;
|
|
39
|
+
exports.dependfixCommand = require_cli.dependfixCommand;
|
|
40
|
+
exports.ensurePnpmOverrides = require_cli.ensurePnpmOverrides;
|
|
41
|
+
exports.extractFingerprintFromBranch = require_cli.extractFingerprintFromBranch;
|
|
42
|
+
exports.extractPrefix = require_cli.extractPrefix;
|
|
43
|
+
exports.fetchDependabotAlerts = require_cli.fetchDependabotAlerts;
|
|
44
|
+
exports.findDependencyVersion = require_cli.findDependencyVersion;
|
|
45
|
+
exports.findDependfixOpenPR = require_cli.findDependfixOpenPR;
|
|
46
|
+
exports.generatePRBody = require_cli.generatePRBody;
|
|
47
|
+
exports.getBranchPrStatus = require_cli.getBranchPrStatus;
|
|
48
|
+
exports.inferRepoFromGitRemote = require_cli.inferRepoFromGitRemote;
|
|
49
|
+
exports.isConfirmAnswer = require_cli.isConfirmAnswer;
|
|
50
|
+
exports.listDependfixBranches = require_cli.listDependfixBranches;
|
|
51
|
+
exports.main = main;
|
|
52
|
+
exports.mapGitHubError = require_cli.mapGitHubError;
|
|
53
|
+
exports.overrideTransitiveDependency = require_cli.overrideTransitiveDependency;
|
|
54
|
+
exports.parseCliArgs = require_cli.parseCliArgs;
|
|
55
|
+
exports.parseMajorVersion = require_cli.parseMajorVersion;
|
|
56
|
+
exports.pushBranch = require_cli.pushBranch;
|
|
57
|
+
exports.readEnvConfig = require_cli.readEnvConfig;
|
|
58
|
+
exports.readLockfileVersion = require_cli.readLockfileVersion;
|
|
59
|
+
exports.readReposFile = require_cli.readReposFile;
|
|
60
|
+
exports.repairLockfile = require_cli.repairLockfile;
|
|
61
|
+
exports.resolvePnpmVersion = require_cli.resolvePnpmVersion;
|
|
62
|
+
exports.resolveRepoList = require_cli.resolveRepoList;
|
|
63
|
+
exports.resolveRuntimeConfig = require_cli.resolveRuntimeConfig;
|
|
64
|
+
exports.runCli = require_cli.runCli;
|
|
65
|
+
exports.runVerification = require_cli.runVerification;
|
|
66
|
+
exports.sanitizeOutput = require_cli.sanitizeOutput;
|
|
67
|
+
exports.stageAndCommit = require_cli.stageAndCommit;
|
|
68
|
+
exports.upgradeDependency = require_cli.upgradeDependency;
|
|
69
|
+
Object.keys(_dependfix_core).forEach(function (k) {
|
|
70
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
get: function () { return _dependfix_core[k]; }
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
35
76
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["runCli","dependfixCommand"],"sources":["../src/index.ts"],"sourcesContent":["import { pathToFileURL } from 'node:url'\nimport { runMain } from 'citty'\nimport { toAppError } from '@dependfix/core'\nimport { dependfixCommand, runCli, type CliRunResult } from './cli'\n\nexport * from '@dependfix/core'\nexport * from './app'\nexport * from './cli'\nexport * from './config'\nexport * from './fixers/dependency'\nexport * from './fixers/pnpm'\nexport * from './github'\nexport * from './runners'\n\n/**\n * 简化调用的入口(内部数据处理,非 CLI 使用)。\n */\nexport function main(args: string[] = process.argv.slice(2)): CliRunResult {\n return runCli(args)\n}\n\nfunction isExecutedAsEntryPoint(): boolean {\n const entry = process.argv[1]\n\n if (!entry) {\n return false\n }\n\n return import.meta.url === pathToFileURL(entry).href\n}\n\nif (isExecutedAsEntryPoint()) {\n // 优先使用 citty 接管(自动 --help、子命令等)\n void runMain(dependfixCommand).catch((error: unknown) => {\n const appError = toAppError(error, 'CLI_EXECUTION_FAILED')\n console.error(appError.message)\n process.exitCode = 1\n })\n}\n"],"mappings":";;;;;;;;;;AAiBA,SAAgB,KAAK,OAAiB,QAAQ,KAAK,MAAM,EAAE,EAAgB;AACvE,QAAOA,mBAAO,KAAK;;AAGvB,SAAS,yBAAkC;CACvC,MAAM,QAAQ,QAAQ,KAAK;AAE3B,KAAI,CAAC,MACD,QAAO;AAGX,sFAAyC,MAAM,CAAC;;AAGpD,IAAI,wBAAwB,CAExB,oBAAaC,6BAAiB,CAAC,OAAO,UAAmB;CACrD,MAAM,2CAAsB,OAAO,uBAAuB;AAC1D,SAAQ,MAAM,SAAS,QAAQ;AAC/B,SAAQ,WAAW;EACrB"}
|