From 3c1d616dc180d53232eccca05255ca853084ab23 Mon Sep 17 00:00:00 2001 From: jeffusion Date: Tue, 24 Mar 2026 10:00:13 +0800 Subject: [PATCH] fix(lint): resolve biome violations across src modules --- src/crypto/__tests__/secrets.test.ts | 6 +-- src/db/__tests__/provider-repo.test.ts | 4 +- src/db/__tests__/secret-repo.test.ts | 8 +-- src/index.ts | 2 +- src/llm/__tests__/resilience.test.ts | 8 +-- src/llm/gateway.ts | 10 ++-- src/llm/providers/base.ts | 1 - src/review/codex/codex-engine.ts | 1 - src/review/codex/codex-runner.ts | 16 +++--- src/review/codex/mcp-tools.ts | 18 +++++-- src/review/context/local-repo-manager.ts | 52 ++++++++++++------- src/review/context/token-counter.ts | 6 ++- src/review/engine.ts | 16 +++--- src/review/learning/learning-system.ts | 2 +- .../tools/function-reference-search-tool.ts | 6 +-- 15 files changed, 91 insertions(+), 65 deletions(-) diff --git a/src/crypto/__tests__/secrets.test.ts b/src/crypto/__tests__/secrets.test.ts index dde98b8..91d9ebb 100644 --- a/src/crypto/__tests__/secrets.test.ts +++ b/src/crypto/__tests__/secrets.test.ts @@ -29,12 +29,12 @@ describe('secrets — AES-256-GCM encryption', () => { beforeEach(() => { savedEncryptionKey = process.env.ENCRYPTION_KEY; - delete process.env.ENCRYPTION_KEY; + Reflect.deleteProperty(process.env, 'ENCRYPTION_KEY'); }); afterEach(() => { if (savedEncryptionKey === undefined) { - delete process.env.ENCRYPTION_KEY; + Reflect.deleteProperty(process.env, 'ENCRYPTION_KEY'); } else { process.env.ENCRYPTION_KEY = savedEncryptionKey; } @@ -53,7 +53,7 @@ describe('secrets — AES-256-GCM encryption', () => { }); test('throws if ENCRYPTION_KEY is not set', async () => { - delete process.env.ENCRYPTION_KEY; + Reflect.deleteProperty(process.env, 'ENCRYPTION_KEY'); const secrets = await importFresh(); expect(() => secrets.initMasterKey()).toThrow('ENCRYPTION_KEY env var is required'); diff --git a/src/db/__tests__/provider-repo.test.ts b/src/db/__tests__/provider-repo.test.ts index 34aa103..ee02d66 100644 --- a/src/db/__tests__/provider-repo.test.ts +++ b/src/db/__tests__/provider-repo.test.ts @@ -32,7 +32,7 @@ describe('provider-repo', () => { afterEach(() => { closeDatabase(); if (savedDbPath === undefined) { - delete process.env.DATABASE_PATH; + Reflect.deleteProperty(process.env, 'DATABASE_PATH'); } else { process.env.DATABASE_PATH = savedDbPath; } @@ -212,7 +212,7 @@ describe('provider-repo', () => { }); test('deleting provider does not affect other providers', () => { - const p1 = providerRepo.create({ ...sampleInput, name: 'Keep' }); + providerRepo.create({ ...sampleInput, name: 'Keep' }); const p2 = providerRepo.create({ ...sampleInput, name: 'Delete' }); providerRepo.delete(p2.id); diff --git a/src/db/__tests__/secret-repo.test.ts b/src/db/__tests__/secret-repo.test.ts index 0bb1b82..76540b5 100644 --- a/src/db/__tests__/secret-repo.test.ts +++ b/src/db/__tests__/secret-repo.test.ts @@ -37,7 +37,9 @@ describe('secret-repo', () => { mkdirSync(tmpDir, { recursive: true }); dbPath = join(tmpDir, 'test.db'); process.env.DATABASE_PATH = dbPath; - process.env.ENCRYPTION_KEY = Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString('hex'); + process.env.ENCRYPTION_KEY = Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString( + 'hex' + ); initMasterKey(); initDatabase(); @@ -49,12 +51,12 @@ describe('secret-repo', () => { afterEach(() => { closeDatabase(); if (savedDbPath === undefined) { - delete process.env.DATABASE_PATH; + Reflect.deleteProperty(process.env, 'DATABASE_PATH'); } else { process.env.DATABASE_PATH = savedDbPath; } if (savedEncryptionKey === undefined) { - delete process.env.ENCRYPTION_KEY; + Reflect.deleteProperty(process.env, 'ENCRYPTION_KEY'); } else { process.env.ENCRYPTION_KEY = savedEncryptionKey; } diff --git a/src/index.ts b/src/index.ts index e0d88d8..9bc2934 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,10 +9,10 @@ import { llmConfigRouter } from './controllers/llm-config'; import { handleGiteaWebhook } from './controllers/review'; import { initMasterKey } from './crypto/secrets'; import { initDatabase } from './db/database'; +import { cleanupScheduler } from './review/cleanup-scheduler'; import { codexEngine } from './review/codex/codex-engine'; import { mcpRouter } from './review/codex/mcp-handler'; import { reviewEngine } from './review/engine'; -import { cleanupScheduler } from './review/cleanup-scheduler'; initMasterKey(); initDatabase(); diff --git a/src/llm/__tests__/resilience.test.ts b/src/llm/__tests__/resilience.test.ts index 9680ee0..a7c7906 100644 --- a/src/llm/__tests__/resilience.test.ts +++ b/src/llm/__tests__/resilience.test.ts @@ -118,8 +118,8 @@ describe('LLMSemaphore', () => { expect(sem.activeCount).toBe(2); expect(sem.pendingCount).toBe(0); - const p3 = sem.acquire().then(() => sequence.push('acquire3')); - const p4 = sem.acquire().then(() => sequence.push('acquire4')); + sem.acquire().then(() => sequence.push('acquire3')); + sem.acquire().then(() => sequence.push('acquire4')); const p5 = sem.acquire().then(() => sequence.push('acquire5')); await new Promise((resolve) => setTimeout(resolve, 10)); @@ -344,7 +344,7 @@ describe('retryWithBackoff', () => { try { await retryWithBackoff(fn, { maxAttempts: 2 }); expect(true).toBe(false); - } catch (e: any) { + } catch { expect(callCount).toBe(2); } }); @@ -509,7 +509,7 @@ describe('withResilience', () => { }, { maxAttempts: 3, baseDelayMs: 10, jitter: false } ); - } catch (e: any) { + } catch { expect(callCount).toBe(3); } }); diff --git a/src/llm/gateway.ts b/src/llm/gateway.ts index 58d615f..c70708f 100644 --- a/src/llm/gateway.ts +++ b/src/llm/gateway.ts @@ -40,10 +40,7 @@ export class LLMGateway { private semaphore: LLMSemaphore; private retryOptions: Partial; - constructor( - maxConcurrent = 4, - retryOptions?: Partial - ) { + constructor(maxConcurrent = 4, retryOptions?: Partial) { this.semaphore = new LLMSemaphore(maxConcurrent); this.retryOptions = retryOptions ?? {}; } @@ -105,7 +102,10 @@ export class LLMGateway { () => { const provider = this.getOrCreateProvider(assignment.provider_id); if (!provider.embed) { - throw new LLMError(`Provider '${provider.type}' does not support embeddings`, provider.type); + throw new LLMError( + `Provider '${provider.type}' does not support embeddings`, + provider.type + ); } return provider.embed(texts); }, diff --git a/src/llm/providers/base.ts b/src/llm/providers/base.ts index 960ee8c..d653ce1 100644 --- a/src/llm/providers/base.ts +++ b/src/llm/providers/base.ts @@ -32,7 +32,6 @@ export interface LLMProvider { /** Optional: embedding interface (only for providers that support it). */ embed?(texts: string[]): Promise; - } // --------------------------------------------------------------------------- diff --git a/src/review/codex/codex-engine.ts b/src/review/codex/codex-engine.ts index ae53e1b..5e32413 100644 --- a/src/review/codex/codex-engine.ts +++ b/src/review/codex/codex-engine.ts @@ -50,7 +50,6 @@ class CodexEngine { return; } - await this.store.init(); const recovered = await this.store.recoverInterruptedRuns(); if (recovered > 0) { diff --git a/src/review/codex/codex-runner.ts b/src/review/codex/codex-runner.ts index 3fdf460..2824bc5 100644 --- a/src/review/codex/codex-runner.ts +++ b/src/review/codex/codex-runner.ts @@ -218,12 +218,12 @@ export class CodexRunner { `name = "OpenAI"`, `base_url = "${apiUrl}"`, `env_key = "OPENAI_API_KEY"`, - `requires_openai_auth = false`, + 'requires_openai_auth = false', '', '[mcp_servers.gitea-review]', `url = "http://127.0.0.1:${port}/mcp/gitea-review"`, `http_headers = { "X-Review-Run-Id" = "${runId}" }`, - `required = true`, + 'required = true', '', ]; @@ -276,9 +276,7 @@ export class CodexRunner { sections.push(`## 项目级审查要求\n\n${projectPrompt}`); } - sections.push( - '当要求冲突时,优先级为:项目级审查要求 > 全局审查要求 > 审查原则。' - ); + sections.push('当要求冲突时,优先级为:项目级审查要求 > 全局审查要求 > 审查原则。'); const contextLines: string[] = ['## 当前审查目标']; @@ -288,7 +286,7 @@ export class CodexRunner { if (run.baseSha) contextLines.push(`- Base SHA:${run.baseSha}`); if (run.headSha) contextLines.push(`- Head SHA:${run.headSha}`); if (lastReviewedHead) { - contextLines.push(`- 增量审查模式:仅审查上次审查后的新变更`); + contextLines.push('- 增量审查模式:仅审查上次审查后的新变更'); contextLines.push(`- 上次审查 SHA:${lastReviewedHead}`); contextLines.push(`- 请使用 \`git diff ${lastReviewedHead}..${run.headSha}\` 获取增量差异`); } else { @@ -314,7 +312,11 @@ export class CodexRunner { /** * 执行 codex exec 子进程(自定义 prompt + MCP 工具) */ - private async runCodexProcess(workspacePath: string, run: ReviewRun, lastReviewedHead?: string): Promise { + private async runCodexProcess( + workspacePath: string, + run: ReviewRun, + lastReviewedHead?: string + ): Promise { const timeoutMs = config.review.codexTimeoutMs; const codexHome = path.join(workspacePath, '.codex'); diff --git a/src/review/codex/mcp-tools.ts b/src/review/codex/mcp-tools.ts index 1d3af39..9aba241 100644 --- a/src/review/codex/mcp-tools.ts +++ b/src/review/codex/mcp-tools.ts @@ -166,14 +166,21 @@ export class McpToolExecutor { if (!prNumber) { prNumber = ctx.relatedPrNumber; if (!prNumber && ctx.commitSha) { - const related = await giteaService.getRelatedPullRequest(ctx.owner, ctx.repo, ctx.commitSha); + const related = await giteaService.getRelatedPullRequest( + ctx.owner, + ctx.repo, + ctx.commitSha + ); prNumber = related?.number; } } if (prNumber) { await giteaService.addPullRequestComment(ctx.owner, ctx.repo, prNumber, body); - logger.info('Codex MCP: \u5df2\u53d1\u5e03 PR \u5ba1\u67e5\u603b\u7ed3', { runId: ctx.runId, prNumber }); + logger.info('Codex MCP: \u5df2\u53d1\u5e03 PR \u5ba1\u67e5\u603b\u7ed3', { + runId: ctx.runId, + prNumber, + }); } else if (ctx.commitSha) { await giteaService.addCommitComment(ctx.owner, ctx.repo, ctx.commitSha, body); logger.info('Codex MCP: \u5df2\u53d1\u5e03 Commit \u5ba1\u67e5\u603b\u7ed3', { @@ -182,7 +189,12 @@ export class McpToolExecutor { }); } else { return { - content: [{ type: 'text', text: '\u65e0\u6cd5\u53d1\u5e03\uff1a\u7f3a\u5c11 PR number \u6216 commit SHA' }], + content: [ + { + type: 'text', + text: '\u65e0\u6cd5\u53d1\u5e03\uff1a\u7f3a\u5c11 PR number \u6216 commit SHA', + }, + ], isError: true, }; } diff --git a/src/review/context/local-repo-manager.ts b/src/review/context/local-repo-manager.ts index be7a827..ae01b68 100644 --- a/src/review/context/local-repo-manager.ts +++ b/src/review/context/local-repo-manager.ts @@ -112,7 +112,16 @@ export class LocalRepoManager { // fetch使用认证参数 await this.sandboxExec.run( 'git', - [...authArgs, '--git-dir', mirrorPath, 'fetch', '--prune', 'origin', '+refs/*:refs/*', '^refs/reviewed/*'], + [ + ...authArgs, + '--git-dir', + mirrorPath, + 'fetch', + '--prune', + 'origin', + '+refs/*:refs/*', + '^refs/reviewed/*', + ], { cwd: this.workDir, timeoutMs: this.commandTimeoutMs, @@ -252,27 +261,24 @@ export class LocalRepoManager { * 保存审查快照 ref,记录 PR 最后一次成功审查的 baseSha 和 headSha * 存储在 mirror 的 refs/reviewed/pr/{prNumber}/head 和 refs/reviewed/pr/{prNumber}/base */ - async saveReviewedRef(mirrorPath: string, prNumber: number, baseSha: string, headSha: string): Promise { + async saveReviewedRef( + mirrorPath: string, + prNumber: number, + baseSha: string, + headSha: string + ): Promise { const unlock = await this.acquireMirrorLock(mirrorPath); try { const headRef = `refs/reviewed/pr/${prNumber}/head`; const baseRef = `refs/reviewed/pr/${prNumber}/base`; - await this.sandboxExec.run( - 'git', - ['--git-dir', mirrorPath, 'update-ref', headRef, headSha], - { - cwd: this.workDir, - timeoutMs: this.commandTimeoutMs, - } - ); - await this.sandboxExec.run( - 'git', - ['--git-dir', mirrorPath, 'update-ref', baseRef, baseSha], - { - cwd: this.workDir, - timeoutMs: this.commandTimeoutMs, - } - ); + await this.sandboxExec.run('git', ['--git-dir', mirrorPath, 'update-ref', headRef, headSha], { + cwd: this.workDir, + timeoutMs: this.commandTimeoutMs, + }); + await this.sandboxExec.run('git', ['--git-dir', mirrorPath, 'update-ref', baseRef, baseSha], { + cwd: this.workDir, + timeoutMs: this.commandTimeoutMs, + }); logger.info('已保存审查快照 ref', { mirrorPath, prNumber, baseSha, headSha }); } finally { unlock(); @@ -283,7 +289,10 @@ export class LocalRepoManager { * 解析上次审查的快照(baseSha + headSha) * 如果任一 ref 不存在,返回 null */ - async resolveReviewedRef(mirrorPath: string, prNumber: number): Promise<{ baseSha: string; headSha: string } | null> { + async resolveReviewedRef( + mirrorPath: string, + prNumber: number + ): Promise<{ baseSha: string; headSha: string } | null> { try { const headRef = `refs/reviewed/pr/${prNumber}/head`; const baseRef = `refs/reviewed/pr/${prNumber}/base`; @@ -375,7 +384,10 @@ export class LocalRepoManager { if (now - lastActive > maxAgeMs) { await rm(mirrorPath, { recursive: true, force: true }); cleaned++; - logger.info('已清理过期 mirror 目录', { mirrorPath, lastActiveDaysAgo: Math.floor((now - lastActive) / (24 * 60 * 60 * 1000)) }); + logger.info('已清理过期 mirror 目录', { + mirrorPath, + lastActiveDaysAgo: Math.floor((now - lastActive) / (24 * 60 * 60 * 1000)), + }); } } catch (error) { logger.warn('检查/清理 mirror 目录失败', { diff --git a/src/review/context/token-counter.ts b/src/review/context/token-counter.ts index 1159592..6fca58c 100644 --- a/src/review/context/token-counter.ts +++ b/src/review/context/token-counter.ts @@ -243,7 +243,11 @@ export class TokenCounter { }); }, CATALOG_TTL_MS); // Don't prevent process exit - if (this.refreshTimer && typeof this.refreshTimer === 'object' && 'unref' in this.refreshTimer) { + if ( + this.refreshTimer && + typeof this.refreshTimer === 'object' && + 'unref' in this.refreshTimer + ) { (this.refreshTimer as NodeJS.Timeout).unref(); } } diff --git a/src/review/engine.ts b/src/review/engine.ts index 02dcc67..47ac3e6 100644 --- a/src/review/engine.ts +++ b/src/review/engine.ts @@ -42,7 +42,10 @@ class ReviewEngine { } /** Fresh DiffExtractor that reads current config values. */ - private createDiffExtractor(sandboxExec: SandboxExec, localRepoManager: LocalRepoManager): DiffExtractor { + private createDiffExtractor( + sandboxExec: SandboxExec, + localRepoManager: LocalRepoManager + ): DiffExtractor { return new DiffExtractor( sandboxExec, localRepoManager, @@ -66,13 +69,10 @@ class ReviewEngine { } // Configure LLM Gateway resilience from current config - llmGateway.updateResilienceConfig( - config.review.llmMaxConcurrentCalls, - { - maxAttempts: config.review.llmRetryMaxAttempts, - baseDelayMs: config.review.llmRetryBaseDelayMs, - } - ); + llmGateway.updateResilienceConfig(config.review.llmMaxConcurrentCalls, { + maxAttempts: config.review.llmRetryMaxAttempts, + baseDelayMs: config.review.llmRetryBaseDelayMs, + }); // Preload dynamic model catalog from models.dev (non-blocking) tokenCounter.refreshCatalog().catch((error) => { diff --git a/src/review/learning/learning-system.ts b/src/review/learning/learning-system.ts index 164f9da..bd8c848 100644 --- a/src/review/learning/learning-system.ts +++ b/src/review/learning/learning-system.ts @@ -1,5 +1,5 @@ -import type { LLMMessage } from '../../llm/types'; import config from '../../config'; +import type { LLMMessage } from '../../llm/types'; import { logger } from '../../utils/logger'; import { VectorMemoryStore } from '../memory/vector-store'; import { FileReviewStore } from '../store/file-review-store'; diff --git a/src/review/tools/function-reference-search-tool.ts b/src/review/tools/function-reference-search-tool.ts index dcd4ff8..17f4ce5 100644 --- a/src/review/tools/function-reference-search-tool.ts +++ b/src/review/tools/function-reference-search-tool.ts @@ -78,11 +78,7 @@ export function createFunctionReferenceSearchTool(sandbox: SandboxExec): Tool { for (const task of tasks) { const pattern = task.patterns.join('|'); - const args = [ - '--json', - '--max-count', - String(max_results || 30), - ]; + const args = ['--json', '--max-count', String(max_results || 30)]; if (file_types && file_types.length > 0) { args.push('--type-add', `custom:*.{${file_types.join(',')}}`);