@xdevops/issue-auto-finish 1.0.86 → 1.0.88
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/dist/AIRunnerRegistry-II3WWSFN.js +31 -0
- package/dist/PtyRunner-6UGI5STW.js +22 -0
- package/dist/TerminalManager-RT2N7N5R.js +8 -0
- package/dist/ai-runner/AIRunner.d.ts +9 -1
- package/dist/ai-runner/AIRunner.d.ts.map +1 -1
- package/dist/ai-runner/AIRunnerRegistry.d.ts +37 -1
- package/dist/ai-runner/AIRunnerRegistry.d.ts.map +1 -1
- package/dist/ai-runner/PtyRunner.d.ts +114 -0
- package/dist/ai-runner/PtyRunner.d.ts.map +1 -0
- package/dist/ai-runner/index.d.ts +3 -1
- package/dist/ai-runner/index.d.ts.map +1 -1
- package/dist/{ai-runner-SVUNA3FX.js → ai-runner-HLA44WI6.js} +12 -3
- package/dist/{analyze-SXXPE5XL.js → analyze-ZIXNC5GN.js} +10 -8
- package/dist/{analyze-SXXPE5XL.js.map → analyze-ZIXNC5GN.js.map} +1 -1
- package/dist/{braindump-4E5SDMSZ.js → braindump-56WAY2RD.js} +10 -8
- package/dist/{braindump-4E5SDMSZ.js.map → braindump-56WAY2RD.js.map} +1 -1
- package/dist/{chunk-ICXB2WP5.js → chunk-2GJ2KA4Z.js} +3 -3
- package/dist/{chunk-P4O4ZXEC.js → chunk-2YQHKXLL.js} +40 -19
- package/dist/chunk-2YQHKXLL.js.map +1 -0
- package/dist/chunk-AVGZH64A.js +211 -0
- package/dist/chunk-AVGZH64A.js.map +1 -0
- package/dist/{chunk-QO5VTSMI.js → chunk-C4MFVASL.js} +455 -202
- package/dist/chunk-C4MFVASL.js.map +1 -0
- package/dist/{chunk-HOFYJEJ4.js → chunk-D47Q5745.js} +11 -11
- package/dist/{chunk-OUPJMHAL.js → chunk-IP3QTP5A.js} +1026 -764
- package/dist/chunk-IP3QTP5A.js.map +1 -0
- package/dist/chunk-KC5S66OZ.js +177 -0
- package/dist/chunk-KC5S66OZ.js.map +1 -0
- package/dist/{chunk-KTYPZTF4.js → chunk-LNFMARKQ.js} +51 -9
- package/dist/chunk-LNFMARKQ.js.map +1 -0
- package/dist/{chunk-4QV6D34Y.js → chunk-M5C2WILQ.js} +8 -6
- package/dist/{chunk-4QV6D34Y.js.map → chunk-M5C2WILQ.js.map} +1 -1
- package/dist/{chunk-FWEW5E3B.js → chunk-NZHKAPU6.js} +35 -5
- package/dist/chunk-NZHKAPU6.js.map +1 -0
- package/dist/{chunk-4LFNFRCL.js → chunk-SAMTXC4A.js} +91 -214
- package/dist/chunk-SAMTXC4A.js.map +1 -0
- package/dist/chunk-U237JSLB.js +1 -0
- package/dist/chunk-U237JSLB.js.map +1 -0
- package/dist/chunk-U6GWFTKA.js +657 -0
- package/dist/chunk-U6GWFTKA.js.map +1 -0
- package/dist/cli/setup/PreflightChecker.d.ts +1 -0
- package/dist/cli/setup/PreflightChecker.d.ts.map +1 -1
- package/dist/cli/setup/env-metadata.d.ts.map +1 -1
- package/dist/cli.js +8 -7
- package/dist/cli.js.map +1 -1
- package/dist/{config-QLINHCHD.js → config-WTRSZLOC.js} +4 -3
- package/dist/config-WTRSZLOC.js.map +1 -0
- package/dist/config-schema.d.ts +17 -1
- package/dist/config-schema.d.ts.map +1 -1
- package/dist/config.d.ts +20 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/errors/PhaseAbortedError.d.ts +3 -3
- package/dist/errors/PhaseAbortedError.d.ts.map +1 -1
- package/dist/errors-S3BWYA4I.js +43 -0
- package/dist/errors-S3BWYA4I.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -11
- package/dist/{init-TDKDC6YP.js → init-4THZTNXY.js} +7 -6
- package/dist/{init-TDKDC6YP.js.map → init-4THZTNXY.js.map} +1 -1
- package/dist/lib.js +9 -7
- package/dist/lib.js.map +1 -1
- package/dist/orchestrator/IssueProcessingContext.d.ts +39 -21
- package/dist/orchestrator/IssueProcessingContext.d.ts.map +1 -1
- package/dist/orchestrator/PipelineOrchestrator.d.ts +10 -1
- package/dist/orchestrator/PipelineOrchestrator.d.ts.map +1 -1
- package/dist/orchestrator/steps/PhaseLoopStep.d.ts +1 -1
- package/dist/orchestrator/steps/PhaseLoopStep.d.ts.map +1 -1
- package/dist/orchestrator/steps/SetupStep.d.ts.map +1 -1
- package/dist/persistence/PlanPersistence.d.ts +7 -1
- package/dist/persistence/PlanPersistence.d.ts.map +1 -1
- package/dist/phases/BasePhase.d.ts +31 -42
- package/dist/phases/BasePhase.d.ts.map +1 -1
- package/dist/phases/BuildPhase.d.ts.map +1 -1
- package/dist/phases/PhaseFactory.d.ts +2 -3
- package/dist/phases/PhaseFactory.d.ts.map +1 -1
- package/dist/phases/PhaseOutcome.d.ts +42 -0
- package/dist/phases/PhaseOutcome.d.ts.map +1 -0
- package/dist/phases/PlanPhase.d.ts +1 -1
- package/dist/phases/PlanPhase.d.ts.map +1 -1
- package/dist/phases/ReleasePhase.d.ts +8 -18
- package/dist/phases/ReleasePhase.d.ts.map +1 -1
- package/dist/phases/UatPhase.d.ts +7 -24
- package/dist/phases/UatPhase.d.ts.map +1 -1
- package/dist/phases/VerifyPhase.d.ts +4 -4
- package/dist/phases/VerifyPhase.d.ts.map +1 -1
- package/dist/poller/IssuePoller.d.ts.map +1 -1
- package/dist/prompts/release-templates.d.ts.map +1 -1
- package/dist/prompts/templates.d.ts.map +1 -1
- package/dist/{restart-4NSHDOX3.js → restart-VEDIKB6M.js} +6 -5
- package/dist/{restart-4NSHDOX3.js.map → restart-VEDIKB6M.js.map} +1 -1
- package/dist/run.js +14 -11
- package/dist/run.js.map +1 -1
- package/dist/settings/ExperimentalSettings.d.ts +1 -1
- package/dist/settings/ExperimentalSettings.d.ts.map +1 -1
- package/dist/start-5N6DIWM2.js +15 -0
- package/dist/start-5N6DIWM2.js.map +1 -0
- package/dist/terminal/TerminalManager.d.ts +62 -0
- package/dist/terminal/TerminalManager.d.ts.map +1 -0
- package/dist/terminal/TerminalWebSocket.d.ts +9 -0
- package/dist/terminal/TerminalWebSocket.d.ts.map +1 -0
- package/dist/tracker/ExecutableTask.d.ts +4 -2
- package/dist/tracker/ExecutableTask.d.ts.map +1 -1
- package/dist/tracker/IssueState.d.ts +11 -1
- package/dist/tracker/IssueState.d.ts.map +1 -1
- package/dist/tracker/IssueTracker.d.ts +19 -1
- package/dist/tracker/IssueTracker.d.ts.map +1 -1
- package/dist/web/WebServer.d.ts +4 -0
- package/dist/web/WebServer.d.ts.map +1 -1
- package/dist/web/routes/terminal.d.ts +11 -0
- package/dist/web/routes/terminal.d.ts.map +1 -0
- package/dist/webhook/CommandExecutor.d.ts.map +1 -1
- package/package.json +7 -1
- package/src/web/frontend/dist/assets/index-COYziOhv.css +1 -0
- package/src/web/frontend/dist/assets/index-D_oTMuJU.js +151 -0
- package/src/web/frontend/dist/index.html +2 -2
- package/dist/chunk-4LFNFRCL.js.map +0 -1
- package/dist/chunk-DADQSKPL.js +0 -1
- package/dist/chunk-FWEW5E3B.js.map +0 -1
- package/dist/chunk-KTYPZTF4.js.map +0 -1
- package/dist/chunk-OUPJMHAL.js.map +0 -1
- package/dist/chunk-P4O4ZXEC.js.map +0 -1
- package/dist/chunk-QO5VTSMI.js.map +0 -1
- package/dist/start-XZIBPLC2.js +0 -14
- package/src/web/frontend/dist/assets/index-BWVpNmFm.js +0 -133
- package/src/web/frontend/dist/assets/index-C7lorIa0.css +0 -1
- /package/dist/{ai-runner-SVUNA3FX.js.map → AIRunnerRegistry-II3WWSFN.js.map} +0 -0
- /package/dist/{chunk-DADQSKPL.js.map → PtyRunner-6UGI5STW.js.map} +0 -0
- /package/dist/{config-QLINHCHD.js.map → TerminalManager-RT2N7N5R.js.map} +0 -0
- /package/dist/{start-XZIBPLC2.js.map → ai-runner-HLA44WI6.js.map} +0 -0
- /package/dist/{chunk-ICXB2WP5.js.map → chunk-2GJ2KA4Z.js.map} +0 -0
- /package/dist/{chunk-HOFYJEJ4.js.map → chunk-D47Q5745.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/braindump/BraindumpOrchestrator.ts","../src/braindump/prompts/split-prompt.ts","../src/braindump/TaskSplitter.ts","../src/braindump/MergeQueue.ts","../src/braindump/prompts/task-prompt.ts","../src/braindump/BraindumpTracker.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { Config, AIRunnerMode } from '../config.js';\nimport { GitOperations } from '../git/GitOperations.js';\nimport { AsyncMutex } from '../utils/AsyncMutex.js';\nimport { createAIRunner, type AIRunner, type StreamEvent } from '../ai-runner/index.js';\nimport { eventBus } from '../events/EventBus.js';\nimport { isShuttingDown } from '../shutdown/ShutdownSignal.js';\nimport { logger as rootLogger } from '../logger.js';\nimport {\n BatchStatus,\n TaskStatus,\n type BraindumpBatch,\n type BraindumpTask,\n type SplitResult,\n} from './BraindumpState.js';\nimport { BraindumpTracker } from './BraindumpTracker.js';\nimport { TaskSplitter } from './TaskSplitter.js';\nimport { MergeQueue } from './MergeQueue.js';\nimport { buildTaskPrompt } from './prompts/task-prompt.js';\n\nconst logger = rootLogger.child('BraindumpOrchestrator');\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport interface BatchCreateOptions {\n targetBranch?: string;\n maxConcurrent?: number;\n defaultAiRunnerMode?: AIRunnerMode;\n}\n\nexport class BraindumpOrchestrator {\n private config: Config;\n private mainGit: GitOperations;\n private mainGitMutex: AsyncMutex;\n private defaultAiRunner: AIRunner;\n private tracker: BraindumpTracker;\n private mergeQueue: MergeQueue;\n private activeBatches = new Set<string>();\n\n constructor(\n config: Config,\n mainGit: GitOperations,\n mainGitMutex: AsyncMutex,\n defaultAiRunner: AIRunner,\n tracker: BraindumpTracker,\n ) {\n this.config = config;\n this.mainGit = mainGit;\n this.mainGitMutex = mainGitMutex;\n this.defaultAiRunner = defaultAiRunner;\n this.tracker = tracker;\n\n this.mergeQueue = new MergeQueue({\n mainGit,\n mainGitMutex,\n aiRunner: defaultAiRunner,\n tracker,\n worktreeBaseDir: config.project.worktreeBaseDir || config.project.gitRootDir,\n projectSubDir: config.project.projectSubDir,\n phaseTimeoutMs: config.braindump.taskTimeoutMs,\n maxConflictAttempts: config.braindump.maxConflictAttempts,\n });\n }\n\n getTracker(): BraindumpTracker {\n return this.tracker;\n }\n\n /** Replace the default AI runner (used by config hot-reload) */\n setAIRunner(runner: AIRunner): void {\n this.defaultAiRunner = runner;\n logger.info('BraindumpOrchestrator AIRunner replaced via hot-reload');\n }\n\n // ── Batch lifecycle ──\n\n createBatch(rawInput: string, options?: BatchCreateOptions): BraindumpBatch {\n const id = crypto.randomUUID();\n const targetBranch = options?.targetBranch || this.config.project.baseBranch;\n const batchShort = id.slice(0, 8);\n\n const batch: BraindumpBatch = {\n id,\n rawInput,\n targetBranch,\n integrationBranch: `braindump/${batchShort}/integration`,\n status: BatchStatus.Draft,\n tasks: [],\n maxConcurrent: options?.maxConcurrent || this.config.braindump.maxConcurrent,\n defaultAiRunnerMode: options?.defaultAiRunnerMode || this.config.ai.mode,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n };\n\n return this.tracker.createBatch(batch);\n }\n\n async splitBatch(\n batchId: string,\n onEvent?: (event: StreamEvent) => void,\n ): Promise<BraindumpBatch> {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) throw new Error(`Batch ${batchId} not found`);\n\n this.tracker.updateBatchStatus(batchId, BatchStatus.Splitting);\n\n try {\n const splitter = new TaskSplitter(this.defaultAiRunner);\n const result: SplitResult = await splitter.split(\n batch.rawInput,\n this.config.project.workDir,\n this.config.braindump.splitTimeoutMs,\n onEvent,\n );\n\n const batchShort = batchId.slice(0, 8);\n const tasks: BraindumpTask[] = result.tasks.map((t, i) => ({\n id: crypto.randomUUID(),\n index: i,\n title: t.title,\n description: t.description,\n dependsOn: t.dependsOn.map(depIdx => {\n // Convert index-based dependencies to task IDs (we'll set them below)\n return String(depIdx);\n }),\n branchName: `braindump/${batchShort}/task-${i}`,\n status: TaskStatus.Pending,\n attempts: 0,\n }));\n\n // Resolve index-based dependsOn to actual task IDs\n for (const task of tasks) {\n task.dependsOn = task.dependsOn\n .map(depIdxStr => {\n const depIdx = parseInt(depIdxStr, 10);\n return tasks[depIdx]?.id;\n })\n .filter((id): id is string => !!id);\n\n // Mark tasks with dependencies as blocked\n if (task.dependsOn.length > 0) {\n task.status = TaskStatus.Blocked;\n }\n }\n\n this.tracker.updateBatch(batchId, {\n tasks,\n status: BatchStatus.WaitingConfirm,\n });\n\n eventBus.emitTyped('braindump:split:done', { batchId, taskCount: tasks.length });\n logger.info('Batch split completed', { batchId, taskCount: tasks.length });\n\n return this.tracker.getBatch(batchId)!;\n } catch (err) {\n const errorMsg = (err as Error).message;\n this.tracker.updateBatchStatus(batchId, BatchStatus.Failed, errorMsg);\n throw err;\n }\n }\n\n confirmBatch(batchId: string, tasks?: BraindumpTask[]): void {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) throw new Error(`Batch ${batchId} not found`);\n if (batch.status !== BatchStatus.WaitingConfirm) {\n throw new Error(`Batch ${batchId} is not waiting for confirmation (status: ${batch.status})`);\n }\n\n if (tasks) {\n this.tracker.updateBatch(batchId, { tasks });\n }\n eventBus.emitTyped('braindump:confirmed', { batchId });\n }\n\n async executeBatch(batchId: string): Promise<void> {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) throw new Error(`Batch ${batchId} not found`);\n if (this.activeBatches.has(batchId)) {\n throw new Error(`Batch ${batchId} is already being executed`);\n }\n\n this.activeBatches.add(batchId);\n this.tracker.updateBatchStatus(batchId, BatchStatus.Running);\n\n try {\n // 1. Create integration branch from target\n await this.mainGitMutex.runExclusive(async () => {\n await this.mainGit.fetch();\n const exists = await this.mainGit.branchExists(batch.integrationBranch);\n if (!exists) {\n await this.mainGit.createBranch(batch.integrationBranch, batch.targetBranch);\n await this.mainGit.push(batch.integrationBranch);\n // Switch back to original branch\n await this.mainGit.checkout(this.config.project.baseBranch);\n }\n });\n\n // 2. Scheduling loop\n const runningTasks = new Map<string, Promise<void>>();\n\n while (!isShuttingDown()) {\n const currentBatch = this.tracker.getBatch(batchId);\n if (!currentBatch || currentBatch.status === BatchStatus.Failed) break;\n\n // Check if all tasks are in terminal state\n const allTerminal = currentBatch.tasks.every(\n t => t.status === TaskStatus.Merged || t.status === TaskStatus.Failed,\n );\n if (allTerminal) break;\n\n // Unblock tasks whose dependencies are met\n this.unblockReadyTasks(batchId);\n\n // Get ready tasks (pending + not running)\n const ready = currentBatch.tasks.filter(\n t => t.status === TaskStatus.Pending && !runningTasks.has(t.id),\n );\n const available = currentBatch.maxConcurrent - runningTasks.size;\n\n // Start new tasks\n for (const task of ready.slice(0, available)) {\n const promise = this.executeTask(batchId, task).finally(() => {\n runningTasks.delete(task.id);\n });\n runningTasks.set(task.id, promise);\n }\n\n await sleep(2000);\n }\n\n // Wait for remaining running tasks\n if (runningTasks.size > 0) {\n await Promise.allSettled([...runningTasks.values()]);\n }\n\n // Determine final batch status\n const finalBatch = this.tracker.getBatch(batchId)!;\n const allMerged = finalBatch.tasks.every(t => t.status === TaskStatus.Merged);\n const anyFailed = finalBatch.tasks.some(t => t.status === TaskStatus.Failed);\n\n if (allMerged) {\n this.tracker.updateBatchStatus(batchId, BatchStatus.Completed);\n eventBus.emitTyped('braindump:completed', { batchId });\n logger.info('Batch completed successfully', { batchId });\n } else if (anyFailed) {\n this.tracker.updateBatchStatus(batchId, BatchStatus.Failed, 'Some tasks failed');\n eventBus.emitTyped('braindump:failed', { batchId });\n logger.warn('Batch completed with failures', { batchId });\n }\n } catch (err) {\n const errorMsg = (err as Error).message;\n this.tracker.updateBatchStatus(batchId, BatchStatus.Failed, errorMsg);\n eventBus.emitTyped('braindump:failed', { batchId, error: errorMsg });\n logger.error('Batch execution failed', { batchId, error: errorMsg });\n } finally {\n this.activeBatches.delete(batchId);\n }\n }\n\n async cancelBatch(batchId: string): Promise<void> {\n this.tracker.updateBatchStatus(batchId, BatchStatus.Failed, 'Cancelled by user');\n this.activeBatches.delete(batchId);\n }\n\n async retryFailed(batchId: string): Promise<void> {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) throw new Error(`Batch ${batchId} not found`);\n\n for (const task of batch.tasks) {\n if (task.status === TaskStatus.Failed) {\n task.status = TaskStatus.Pending;\n task.lastError = undefined;\n }\n }\n this.tracker.updateBatch(batchId, { tasks: batch.tasks });\n await this.executeBatch(batchId);\n }\n\n recoverInterrupted(): number {\n return this.tracker.recoverInterrupted();\n }\n\n // ── Private ──\n\n private unblockReadyTasks(batchId: string): void {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) return;\n\n for (const task of batch.tasks) {\n if (task.status !== TaskStatus.Blocked) continue;\n\n const allDepsMet = task.dependsOn.every(depId => {\n const dep = batch.tasks.find(t => t.id === depId);\n return dep && dep.status === TaskStatus.Merged;\n });\n\n if (allDepsMet) {\n this.tracker.updateTaskStatus(batchId, task.id, TaskStatus.Pending);\n }\n }\n }\n\n private async executeTask(batchId: string, task: BraindumpTask): Promise<void> {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) return;\n\n logger.info('Starting task execution', { batchId, taskId: task.id, title: task.title });\n\n this.tracker.updateTaskStatus(batchId, task.id, TaskStatus.Running, {\n startedAt: new Date().toISOString(),\n });\n eventBus.emitTyped('braindump:task:started', { batchId, taskId: task.id, title: task.title });\n\n const batchShort = batchId.slice(0, 8);\n const worktreeBaseDir = this.config.project.worktreeBaseDir || this.config.project.gitRootDir;\n const taskWorktreeDir = `${worktreeBaseDir}/braindump-${batchShort}-task-${task.index}`;\n const taskWorkDir = this.config.project.projectSubDir\n ? `${taskWorktreeDir}/${this.config.project.projectSubDir}`\n : taskWorktreeDir;\n\n try {\n // 1. Create worktree from integration branch\n await this.mainGitMutex.runExclusive(async () => {\n await this.mainGit.fetch();\n const worktrees = await this.mainGit.worktreeList();\n if (!worktrees.includes(taskWorktreeDir)) {\n await this.mainGit.worktreeAdd(\n taskWorktreeDir,\n task.branchName,\n `origin/${batch.integrationBranch}`,\n );\n }\n });\n\n // 2. Run AI\n const aiRunner = this.getRunnerForTask(task);\n const prompt = buildTaskPrompt(task.title, task.description);\n\n await aiRunner.run({\n prompt,\n workDir: taskWorkDir,\n timeoutMs: this.config.braindump.taskTimeoutMs,\n idleTimeoutMs: this.config.ai.idleTimeoutMs,\n onStreamEvent: (event: StreamEvent) => {\n eventBus.emitTyped('agent:output', {\n batchId,\n taskId: task.id,\n phase: 'task-execute',\n event,\n });\n },\n });\n\n // 3. Commit changes\n const wtGit = new GitOperations(taskWorktreeDir);\n if (await wtGit.hasChanges()) {\n await wtGit.add(['.']);\n await wtGit.commit(`braindump: ${task.title}`);\n await wtGit.push(task.branchName);\n }\n\n // 4. Mark done\n this.tracker.updateTaskStatus(batchId, task.id, TaskStatus.Done, {\n completedAt: new Date().toISOString(),\n });\n eventBus.emitTyped('braindump:task:completed', { batchId, taskId: task.id });\n logger.info('Task execution completed', { batchId, taskId: task.id });\n\n // 5. Enqueue for merge\n await this.mergeQueue.enqueue(batchId, {\n ...task,\n status: TaskStatus.Done,\n completedAt: new Date().toISOString(),\n });\n } catch (err) {\n const errorMsg = (err as Error).message;\n logger.error('Task execution failed', { batchId, taskId: task.id, error: errorMsg });\n this.tracker.markTaskFailed(batchId, task.id, errorMsg);\n }\n }\n\n private getRunnerForTask(task: BraindumpTask): AIRunner {\n const mode = task.aiRunnerMode || this.config.ai.mode;\n if (mode === this.config.ai.mode) return this.defaultAiRunner;\n return createAIRunner({\n mode,\n binary: this.config.ai.binary,\n phaseTimeoutMs: this.config.braindump.taskTimeoutMs,\n nvmNodeVersion: this.config.ai.nvmNodeVersion,\n model: this.config.ai.model,\n });\n }\n}\n","/**\n * Build the prompt for AI task splitting.\n * The AI should browse the codebase, analyze the user's input,\n * and output a structured JSON with independent tasks.\n */\nexport function buildSplitPrompt(rawInput: string): string {\n return `你是一个项目管理专家和代码架构师。用户输入了一段包含多个想法/特性/任务的文本内容。\n\n你的任务:\n1. 先浏览项目代码库,了解项目结构和技术栈\n2. 分析用户输入的内容,识别出所有独立的特性或任务\n3. 为每个任务生成清晰的标题、详细描述和依赖关系\n4. 标注哪些任务可以并行执行(没有依赖关系的任务)\n\n## 用户输入\n\n${rawInput}\n\n## 输出要求\n\n请严格按以下 JSON 格式输出(用 \\`\\`\\`json 包裹):\n\n\\`\\`\\`json\n{\n \"tasks\": [\n {\n \"title\": \"简洁的任务标题\",\n \"description\": \"详细的任务描述,包含:\\\\n1. 需要修改哪些文件\\\\n2. 具体要做什么改动\\\\n3. 验收标准\",\n \"dependsOn\": [],\n \"estimatedComplexity\": \"low\"\n },\n {\n \"title\": \"另一个任务\",\n \"description\": \"这个任务依赖第一个任务的完成\",\n \"dependsOn\": [0],\n \"estimatedComplexity\": \"medium\"\n }\n ]\n}\n\\`\\`\\`\n\n## 注意事项\n\n- \\`dependsOn\\` 中填写的是任务在数组中的索引(从 0 开始),表示本任务依赖哪些任务先完成\n- 如果两个任务修改不同文件且逻辑独立,它们之间不应有依赖关系(可并行)\n- 如果两个任务修改相同文件或存在逻辑先后顺序,应设置依赖关系\n- \\`estimatedComplexity\\` 可选值:low(简单改动)、medium(中等复杂度)、high(复杂重构)\n- 每个任务的描述要足够详细,让 AI Agent 能独立完成该任务\n- 任务粒度适中:不要太大(一个任务不应包含多个独立变更),也不要太小(一行代码的改动不需要单独一个任务)`;\n}\n","import type { AIRunner, RunResult, StreamEvent } from '../ai-runner/index.js';\nimport type { SplitResult } from './BraindumpState.js';\nimport { buildSplitPrompt } from './prompts/split-prompt.js';\nimport { AIOutputParseError } from '../errors/index.js';\nimport { logger as rootLogger } from '../logger.js';\n\nconst logger = rootLogger.child('TaskSplitter');\n\nexport class TaskSplitter {\n constructor(private aiRunner: AIRunner) {}\n\n async split(\n rawInput: string,\n workDir: string,\n timeoutMs: number,\n onEvent?: (event: StreamEvent) => void,\n ): Promise<SplitResult> {\n logger.info('Starting task split', { inputLength: rawInput.length });\n\n const prompt = buildSplitPrompt(rawInput);\n const result: RunResult = await this.aiRunner.run({\n prompt,\n workDir,\n timeoutMs,\n onStreamEvent: onEvent,\n });\n\n return this.parseSplitOutput(result.output);\n }\n\n private parseSplitOutput(output: string): SplitResult {\n // Extract JSON block from AI output (wrapped in ```json ... ```)\n const jsonMatch = output.match(/```json\\s*\\n([\\s\\S]*?)\\n\\s*```/);\n if (!jsonMatch) {\n // Try parsing the entire output as JSON\n try {\n return this.validateResult(JSON.parse(output));\n } catch {\n throw new AIOutputParseError(\n 'Failed to parse task split output: no JSON block found in AI response',\n output,\n );\n }\n }\n\n try {\n return this.validateResult(JSON.parse(jsonMatch[1]));\n } catch (err) {\n throw new AIOutputParseError(`Failed to parse task split JSON: ${(err as Error).message}`, jsonMatch[1]);\n }\n }\n\n private validateResult(raw: unknown): SplitResult {\n if (!raw || typeof raw !== 'object') {\n throw new AIOutputParseError('Split result is not an object');\n }\n\n const obj = raw as Record<string, unknown>;\n if (!Array.isArray(obj.tasks)) {\n throw new AIOutputParseError('Split result missing \"tasks\" array');\n }\n\n const tasks = obj.tasks.map((t: Record<string, unknown>, i: number) => ({\n title: String(t.title || `Task ${i + 1}`),\n description: String(t.description || ''),\n dependsOn: Array.isArray(t.dependsOn)\n ? t.dependsOn.map(Number).filter(n => !isNaN(n))\n : [],\n estimatedComplexity: (['low', 'medium', 'high'].includes(String(t.estimatedComplexity))\n ? String(t.estimatedComplexity)\n : 'medium') as 'low' | 'medium' | 'high',\n }));\n\n logger.info('Parsed split result', { taskCount: tasks.length });\n return { tasks };\n }\n}\n","import { GitOperations } from '../git/GitOperations.js';\nimport { ConflictResolver } from '../git/ConflictResolver.js';\nimport { AsyncMutex } from '../utils/AsyncMutex.js';\nimport type { AIRunner } from '../ai-runner/index.js';\nimport { eventBus } from '../events/EventBus.js';\nimport { logger as rootLogger } from '../logger.js';\nimport type { BraindumpTask } from './BraindumpState.js';\nimport { TaskStatus } from './BraindumpState.js';\nimport { BraindumpTracker } from './BraindumpTracker.js';\n\nconst logger = rootLogger.child('MergeQueue');\n\ninterface MergeRequest {\n batchId: string;\n task: BraindumpTask;\n resolve: () => void;\n reject: (err: Error) => void;\n}\n\n/**\n * Serial merge queue: tasks are merged one at a time into the integration branch.\n * This ensures each merge sees the latest integration state and minimizes conflicts.\n */\nexport class MergeQueue {\n private queue: MergeRequest[] = [];\n private processing = false;\n private mainGit: GitOperations;\n private mainGitMutex: AsyncMutex;\n private conflictResolver: ConflictResolver;\n private tracker: BraindumpTracker;\n private worktreeBaseDir: string;\n private projectSubDir: string;\n private phaseTimeoutMs: number;\n private maxConflictAttempts: number;\n\n constructor(opts: {\n mainGit: GitOperations;\n mainGitMutex: AsyncMutex;\n aiRunner: AIRunner;\n tracker: BraindumpTracker;\n worktreeBaseDir: string;\n projectSubDir: string;\n phaseTimeoutMs: number;\n maxConflictAttempts: number;\n }) {\n this.mainGit = opts.mainGit;\n this.mainGitMutex = opts.mainGitMutex;\n this.conflictResolver = new ConflictResolver(opts.aiRunner);\n this.tracker = opts.tracker;\n this.worktreeBaseDir = opts.worktreeBaseDir;\n this.projectSubDir = opts.projectSubDir;\n this.phaseTimeoutMs = opts.phaseTimeoutMs;\n this.maxConflictAttempts = opts.maxConflictAttempts;\n }\n\n /** Enqueue a completed task for merging. Returns a promise that resolves when merge is done. */\n enqueue(batchId: string, task: BraindumpTask): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n this.queue.push({ batchId, task, resolve, reject });\n this.processNext();\n });\n }\n\n get pendingCount(): number {\n return this.queue.length;\n }\n\n private async processNext(): Promise<void> {\n if (this.processing || this.queue.length === 0) return;\n this.processing = true;\n\n // Sort by task index to ensure deterministic merge order\n this.queue.sort((a, b) => a.task.index - b.task.index);\n const req = this.queue.shift()!;\n\n try {\n await this.mergeTask(req.batchId, req.task);\n req.resolve();\n } catch (err) {\n req.reject(err as Error);\n } finally {\n this.processing = false;\n // Process next item if any\n if (this.queue.length > 0) {\n this.processNext();\n }\n }\n }\n\n private async mergeTask(batchId: string, task: BraindumpTask): Promise<void> {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) throw new Error(`Batch ${batchId} not found`);\n\n logger.info('Merging task into integration branch', {\n batchId, taskId: task.id, taskIndex: task.index, branch: task.branchName,\n });\n\n this.tracker.updateTaskStatus(batchId, task.id, TaskStatus.Merging);\n eventBus.emitTyped('braindump:task:merging', { batchId, taskId: task.id });\n\n const taskWorktreeDir = this.getTaskWorktreeDir(batchId, task.index);\n const taskWorkDir = this.projectSubDir\n ? `${taskWorktreeDir}/${this.projectSubDir}`\n : taskWorktreeDir;\n\n try {\n const wtGit = new GitOperations(taskWorktreeDir);\n\n // 1. Checkout the task branch\n await wtGit.checkout(task.branchName);\n\n // 2. Rebase onto integration branch (with AI conflict resolution)\n await this.conflictResolver.resolve({\n wtGit,\n targetRef: `origin/${batch.integrationBranch}`,\n workDir: taskWorkDir,\n branchName: task.branchName,\n contextId: task.id,\n phaseTimeoutMs: this.phaseTimeoutMs,\n maxAttempts: this.maxConflictAttempts,\n onEvent: (event) => {\n eventBus.emitTyped('agent:output', {\n batchId,\n taskId: task.id,\n phase: 'merge-conflict-resolve',\n event,\n });\n },\n });\n\n // 3. Force push the rebased task branch\n await wtGit.forcePush(task.branchName);\n\n // 4. Merge into integration branch (in main repo under mutex)\n await this.mainGitMutex.runExclusive(async () => {\n await this.mainGit.fetch();\n await this.mainGit.checkout(batch.integrationBranch);\n try {\n await this.mainGit.mergeFF(task.branchName);\n } catch {\n // If ff not possible, do a regular merge\n await this.mainGit.merge(task.branchName, `merge: braindump task #${task.index} - ${task.title}`);\n }\n await this.mainGit.push(batch.integrationBranch);\n });\n\n // 5. Update status\n this.tracker.updateTaskStatus(batchId, task.id, TaskStatus.Merged, {\n mergedAt: new Date().toISOString(),\n });\n eventBus.emitTyped('braindump:task:merged', { batchId, taskId: task.id });\n\n logger.info('Task merged successfully', { batchId, taskId: task.id });\n } catch (err) {\n const errorMsg = (err as Error).message;\n logger.error('Task merge failed', { batchId, taskId: task.id, error: errorMsg });\n\n // Try to abort any in-progress rebase\n try {\n const wtGit = new GitOperations(taskWorktreeDir);\n if (await wtGit.isRebaseInProgress()) {\n await wtGit.rebaseAbort();\n }\n } catch { /* ignore */ }\n\n this.tracker.markTaskFailed(batchId, task.id, `Merge failed: ${errorMsg}`);\n throw err;\n }\n }\n\n private getTaskWorktreeDir(batchId: string, taskIndex: number): string {\n const batchShort = batchId.slice(0, 8);\n return `${this.worktreeBaseDir}/braindump-${batchShort}-task-${taskIndex}`;\n }\n}\n","/**\n * Build the prompt for executing a single braindump task.\n */\nexport function buildTaskPrompt(taskTitle: string, taskDescription: string): string {\n return `你需要完成以下任务:\n\n## 任务:${taskTitle}\n\n${taskDescription}\n\n## 要求\n\n1. 仔细阅读相关代码,理解现有架构\n2. 按照任务描述进行代码修改\n3. 确保修改不会破坏现有功能\n4. 遵循项目现有的代码风格和规范\n5. 完成后确保代码可以通过 lint 检查`;\n}\n","import { BatchStatus, TaskStatus, type BraindumpBatch, type BraindumpTask } from './BraindumpState.js';\nimport { BaseTracker } from '../tracker/BaseTracker.js';\nimport { type ExecutableTask, braindumpTaskToExecutableTask } from '../tracker/ExecutableTask.js';\nimport { BatchNotFoundError, TaskNotFoundError } from '../errors/index.js';\nimport { logger as rootLogger } from '../logger.js';\nimport { eventBus } from '../events/EventBus.js';\n\nconst logger = rootLogger.child('BraindumpTracker');\n\nexport class BraindumpTracker extends BaseTracker<BraindumpBatch> {\n constructor(dataDir: string) {\n super(dataDir, 'braindump-tracker.json', 'batches', 'braindump-tracker');\n }\n\n // ── CRUD ──\n\n getBatch(batchId: string): BraindumpBatch | undefined {\n return this.getByKey(batchId);\n }\n\n getAll(): BraindumpBatch[] {\n return this.getAllRecords();\n }\n\n createBatch(batch: BraindumpBatch): BraindumpBatch {\n this.setRecord(batch.id, batch);\n this.save();\n logger.info('Batch created', { batchId: batch.id });\n eventBus.emitTyped('braindump:created', { batchId: batch.id });\n return batch;\n }\n\n updateBatch(batchId: string, updates: Partial<BraindumpBatch>): void {\n const batch = this.collection[batchId];\n if (!batch) throw new BatchNotFoundError(batchId);\n Object.assign(batch, updates, { updatedAt: new Date().toISOString() });\n this.save();\n }\n\n deleteBatch(batchId: string): boolean {\n if (!this.deleteByKey(batchId)) return false;\n logger.info('Batch deleted', { batchId });\n return true;\n }\n\n // ── Status helpers ──\n\n updateBatchStatus(batchId: string, status: BatchStatus, error?: string): void {\n const batch = this.collection[batchId];\n if (!batch) throw new BatchNotFoundError(batchId);\n batch.status = status;\n batch.updatedAt = new Date().toISOString();\n if (error !== undefined) batch.lastError = error;\n if (status === BatchStatus.Completed) batch.completedAt = new Date().toISOString();\n this.save();\n }\n\n updateTaskStatus(batchId: string, taskId: string, status: TaskStatus, extra?: Partial<BraindumpTask>): void {\n const batch = this.collection[batchId];\n if (!batch) throw new BatchNotFoundError(batchId);\n const task = batch.tasks.find(t => t.id === taskId);\n if (!task) throw new TaskNotFoundError(taskId);\n task.status = status;\n if (extra) Object.assign(task, extra);\n batch.updatedAt = new Date().toISOString();\n this.save();\n }\n\n markTaskFailed(batchId: string, taskId: string, error: string): void {\n const batch = this.collection[batchId];\n if (!batch) return;\n const task = batch.tasks.find(t => t.id === taskId);\n if (!task) return;\n task.status = TaskStatus.Failed;\n task.lastError = error;\n task.attempts += 1;\n batch.updatedAt = new Date().toISOString();\n this.save();\n logger.warn('Task marked failed', { batchId, taskId, error, attempts: task.attempts });\n eventBus.emitTyped('braindump:task:failed', { batchId, taskId, error });\n }\n\n // ── Recovery ──\n\n /** Recover batches interrupted by service restart. Returns count of recovered batches. */\n recoverInterrupted(): number {\n const IN_PROGRESS_TASK_STATES = new Set([TaskStatus.Running, TaskStatus.Merging, TaskStatus.ConflictResolving]);\n let count = 0;\n\n for (const batch of this.getAllRecords()) {\n if (batch.status !== BatchStatus.Running && batch.status !== BatchStatus.Merging) continue;\n\n let recovered = false;\n for (const task of batch.tasks) {\n if (IN_PROGRESS_TASK_STATES.has(task.status)) {\n task.status = TaskStatus.Failed;\n task.lastError = 'Interrupted by service restart';\n task.attempts += 1;\n recovered = true;\n }\n }\n\n if (recovered) {\n // If any task is still pending/blocked/done, keep batch running for retry\n const hasRecoverable = batch.tasks.some(\n t => t.status === TaskStatus.Pending || t.status === TaskStatus.Blocked || t.status === TaskStatus.Done,\n );\n if (!hasRecoverable) {\n batch.status = BatchStatus.Failed;\n batch.lastError = 'Interrupted by service restart';\n }\n batch.updatedAt = new Date().toISOString();\n count++;\n }\n }\n\n if (count > 0) {\n this.save();\n logger.info('Recovered interrupted braindump batches', { count });\n }\n return count;\n }\n\n /** 将指定批次的所有任务投影为 ExecutableTask[] */\n toExecutableTasks(batchId: string): ExecutableTask[] {\n const batch = this.getByKey(batchId);\n if (!batch) return [];\n return batch.tasks.map((task) =>\n braindumpTaskToExecutableTask(task, batch),\n );\n }\n}\n"],"mappings":"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","names":["logger","logger","logger","logger"]}
|
|
1
|
+
{"version":3,"sources":["../src/braindump/BraindumpOrchestrator.ts","../src/braindump/prompts/split-prompt.ts","../src/braindump/TaskSplitter.ts","../src/braindump/MergeQueue.ts","../src/braindump/prompts/task-prompt.ts","../src/braindump/BraindumpTracker.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { Config, AIRunnerMode } from '../config.js';\nimport { GitOperations } from '../git/GitOperations.js';\nimport { AsyncMutex } from '../utils/AsyncMutex.js';\nimport { createAIRunner, type AIRunner, type StreamEvent } from '../ai-runner/index.js';\nimport { eventBus } from '../events/EventBus.js';\nimport { isShuttingDown } from '../shutdown/ShutdownSignal.js';\nimport { logger as rootLogger } from '../logger.js';\nimport {\n BatchStatus,\n TaskStatus,\n type BraindumpBatch,\n type BraindumpTask,\n type SplitResult,\n} from './BraindumpState.js';\nimport { BraindumpTracker } from './BraindumpTracker.js';\nimport { TaskSplitter } from './TaskSplitter.js';\nimport { MergeQueue } from './MergeQueue.js';\nimport { buildTaskPrompt } from './prompts/task-prompt.js';\n\nconst logger = rootLogger.child('BraindumpOrchestrator');\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport interface BatchCreateOptions {\n targetBranch?: string;\n maxConcurrent?: number;\n defaultAiRunnerMode?: AIRunnerMode;\n}\n\nexport class BraindumpOrchestrator {\n private config: Config;\n private mainGit: GitOperations;\n private mainGitMutex: AsyncMutex;\n private defaultAiRunner: AIRunner;\n private tracker: BraindumpTracker;\n private mergeQueue: MergeQueue;\n private activeBatches = new Set<string>();\n\n constructor(\n config: Config,\n mainGit: GitOperations,\n mainGitMutex: AsyncMutex,\n defaultAiRunner: AIRunner,\n tracker: BraindumpTracker,\n ) {\n this.config = config;\n this.mainGit = mainGit;\n this.mainGitMutex = mainGitMutex;\n this.defaultAiRunner = defaultAiRunner;\n this.tracker = tracker;\n\n this.mergeQueue = new MergeQueue({\n mainGit,\n mainGitMutex,\n aiRunner: defaultAiRunner,\n tracker,\n worktreeBaseDir: config.project.worktreeBaseDir || config.project.gitRootDir,\n projectSubDir: config.project.projectSubDir,\n phaseTimeoutMs: config.braindump.taskTimeoutMs,\n maxConflictAttempts: config.braindump.maxConflictAttempts,\n });\n }\n\n getTracker(): BraindumpTracker {\n return this.tracker;\n }\n\n /** Replace the default AI runner (used by config hot-reload) */\n setAIRunner(runner: AIRunner): void {\n this.defaultAiRunner = runner;\n logger.info('BraindumpOrchestrator AIRunner replaced via hot-reload');\n }\n\n // ── Batch lifecycle ──\n\n createBatch(rawInput: string, options?: BatchCreateOptions): BraindumpBatch {\n const id = crypto.randomUUID();\n const targetBranch = options?.targetBranch || this.config.project.baseBranch;\n const batchShort = id.slice(0, 8);\n\n const batch: BraindumpBatch = {\n id,\n rawInput,\n targetBranch,\n integrationBranch: `braindump/${batchShort}/integration`,\n status: BatchStatus.Draft,\n tasks: [],\n maxConcurrent: options?.maxConcurrent || this.config.braindump.maxConcurrent,\n defaultAiRunnerMode: options?.defaultAiRunnerMode || this.config.ai.mode,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n };\n\n return this.tracker.createBatch(batch);\n }\n\n async splitBatch(\n batchId: string,\n onEvent?: (event: StreamEvent) => void,\n ): Promise<BraindumpBatch> {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) throw new Error(`Batch ${batchId} not found`);\n\n this.tracker.updateBatchStatus(batchId, BatchStatus.Splitting);\n\n try {\n const splitter = new TaskSplitter(this.defaultAiRunner);\n const result: SplitResult = await splitter.split(\n batch.rawInput,\n this.config.project.workDir,\n this.config.braindump.splitTimeoutMs,\n onEvent,\n );\n\n const batchShort = batchId.slice(0, 8);\n const tasks: BraindumpTask[] = result.tasks.map((t, i) => ({\n id: crypto.randomUUID(),\n index: i,\n title: t.title,\n description: t.description,\n dependsOn: t.dependsOn.map(depIdx => {\n // Convert index-based dependencies to task IDs (we'll set them below)\n return String(depIdx);\n }),\n branchName: `braindump/${batchShort}/task-${i}`,\n status: TaskStatus.Pending,\n attempts: 0,\n }));\n\n // Resolve index-based dependsOn to actual task IDs\n for (const task of tasks) {\n task.dependsOn = task.dependsOn\n .map(depIdxStr => {\n const depIdx = parseInt(depIdxStr, 10);\n return tasks[depIdx]?.id;\n })\n .filter((id): id is string => !!id);\n\n // Mark tasks with dependencies as blocked\n if (task.dependsOn.length > 0) {\n task.status = TaskStatus.Blocked;\n }\n }\n\n this.tracker.updateBatch(batchId, {\n tasks,\n status: BatchStatus.WaitingConfirm,\n });\n\n eventBus.emitTyped('braindump:split:done', { batchId, taskCount: tasks.length });\n logger.info('Batch split completed', { batchId, taskCount: tasks.length });\n\n return this.tracker.getBatch(batchId)!;\n } catch (err) {\n const errorMsg = (err as Error).message;\n this.tracker.updateBatchStatus(batchId, BatchStatus.Failed, errorMsg);\n throw err;\n }\n }\n\n confirmBatch(batchId: string, tasks?: BraindumpTask[]): void {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) throw new Error(`Batch ${batchId} not found`);\n if (batch.status !== BatchStatus.WaitingConfirm) {\n throw new Error(`Batch ${batchId} is not waiting for confirmation (status: ${batch.status})`);\n }\n\n if (tasks) {\n this.tracker.updateBatch(batchId, { tasks });\n }\n eventBus.emitTyped('braindump:confirmed', { batchId });\n }\n\n async executeBatch(batchId: string): Promise<void> {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) throw new Error(`Batch ${batchId} not found`);\n if (this.activeBatches.has(batchId)) {\n throw new Error(`Batch ${batchId} is already being executed`);\n }\n\n this.activeBatches.add(batchId);\n this.tracker.updateBatchStatus(batchId, BatchStatus.Running);\n\n try {\n // 1. Create integration branch from target\n await this.mainGitMutex.runExclusive(async () => {\n await this.mainGit.fetch();\n const exists = await this.mainGit.branchExists(batch.integrationBranch);\n if (!exists) {\n await this.mainGit.createBranch(batch.integrationBranch, batch.targetBranch);\n await this.mainGit.push(batch.integrationBranch);\n // Switch back to original branch\n await this.mainGit.checkout(this.config.project.baseBranch);\n }\n });\n\n // 2. Scheduling loop\n const runningTasks = new Map<string, Promise<void>>();\n\n while (!isShuttingDown()) {\n const currentBatch = this.tracker.getBatch(batchId);\n if (!currentBatch || currentBatch.status === BatchStatus.Failed) break;\n\n // Check if all tasks are in terminal state\n const allTerminal = currentBatch.tasks.every(\n t => t.status === TaskStatus.Merged || t.status === TaskStatus.Failed,\n );\n if (allTerminal) break;\n\n // Unblock tasks whose dependencies are met\n this.unblockReadyTasks(batchId);\n\n // Get ready tasks (pending + not running)\n const ready = currentBatch.tasks.filter(\n t => t.status === TaskStatus.Pending && !runningTasks.has(t.id),\n );\n const available = currentBatch.maxConcurrent - runningTasks.size;\n\n // Start new tasks\n for (const task of ready.slice(0, available)) {\n const promise = this.executeTask(batchId, task).finally(() => {\n runningTasks.delete(task.id);\n });\n runningTasks.set(task.id, promise);\n }\n\n await sleep(2000);\n }\n\n // Wait for remaining running tasks\n if (runningTasks.size > 0) {\n await Promise.allSettled([...runningTasks.values()]);\n }\n\n // Determine final batch status\n const finalBatch = this.tracker.getBatch(batchId)!;\n const allMerged = finalBatch.tasks.every(t => t.status === TaskStatus.Merged);\n const anyFailed = finalBatch.tasks.some(t => t.status === TaskStatus.Failed);\n\n if (allMerged) {\n this.tracker.updateBatchStatus(batchId, BatchStatus.Completed);\n eventBus.emitTyped('braindump:completed', { batchId });\n logger.info('Batch completed successfully', { batchId });\n } else if (anyFailed) {\n this.tracker.updateBatchStatus(batchId, BatchStatus.Failed, 'Some tasks failed');\n eventBus.emitTyped('braindump:failed', { batchId });\n logger.warn('Batch completed with failures', { batchId });\n }\n } catch (err) {\n const errorMsg = (err as Error).message;\n this.tracker.updateBatchStatus(batchId, BatchStatus.Failed, errorMsg);\n eventBus.emitTyped('braindump:failed', { batchId, error: errorMsg });\n logger.error('Batch execution failed', { batchId, error: errorMsg });\n } finally {\n this.activeBatches.delete(batchId);\n }\n }\n\n async cancelBatch(batchId: string): Promise<void> {\n this.tracker.updateBatchStatus(batchId, BatchStatus.Failed, 'Cancelled by user');\n this.activeBatches.delete(batchId);\n }\n\n async retryFailed(batchId: string): Promise<void> {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) throw new Error(`Batch ${batchId} not found`);\n\n for (const task of batch.tasks) {\n if (task.status === TaskStatus.Failed) {\n task.status = TaskStatus.Pending;\n task.lastError = undefined;\n }\n }\n this.tracker.updateBatch(batchId, { tasks: batch.tasks });\n await this.executeBatch(batchId);\n }\n\n recoverInterrupted(): number {\n return this.tracker.recoverInterrupted();\n }\n\n // ── Private ──\n\n private unblockReadyTasks(batchId: string): void {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) return;\n\n for (const task of batch.tasks) {\n if (task.status !== TaskStatus.Blocked) continue;\n\n const allDepsMet = task.dependsOn.every(depId => {\n const dep = batch.tasks.find(t => t.id === depId);\n return dep && dep.status === TaskStatus.Merged;\n });\n\n if (allDepsMet) {\n this.tracker.updateTaskStatus(batchId, task.id, TaskStatus.Pending);\n }\n }\n }\n\n private async executeTask(batchId: string, task: BraindumpTask): Promise<void> {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) return;\n\n logger.info('Starting task execution', { batchId, taskId: task.id, title: task.title });\n\n this.tracker.updateTaskStatus(batchId, task.id, TaskStatus.Running, {\n startedAt: new Date().toISOString(),\n });\n eventBus.emitTyped('braindump:task:started', { batchId, taskId: task.id, title: task.title });\n\n const batchShort = batchId.slice(0, 8);\n const worktreeBaseDir = this.config.project.worktreeBaseDir || this.config.project.gitRootDir;\n const taskWorktreeDir = `${worktreeBaseDir}/braindump-${batchShort}-task-${task.index}`;\n const taskWorkDir = this.config.project.projectSubDir\n ? `${taskWorktreeDir}/${this.config.project.projectSubDir}`\n : taskWorktreeDir;\n\n try {\n // 1. Create worktree from integration branch\n await this.mainGitMutex.runExclusive(async () => {\n await this.mainGit.fetch();\n const worktrees = await this.mainGit.worktreeList();\n if (!worktrees.includes(taskWorktreeDir)) {\n await this.mainGit.worktreeAdd(\n taskWorktreeDir,\n task.branchName,\n `origin/${batch.integrationBranch}`,\n );\n }\n });\n\n // 2. Run AI\n const aiRunner = this.getRunnerForTask(task);\n const prompt = buildTaskPrompt(task.title, task.description);\n\n await aiRunner.run({\n prompt,\n workDir: taskWorkDir,\n timeoutMs: this.config.braindump.taskTimeoutMs,\n idleTimeoutMs: this.config.ai.idleTimeoutMs,\n onStreamEvent: (event: StreamEvent) => {\n eventBus.emitTyped('agent:output', {\n batchId,\n taskId: task.id,\n phase: 'task-execute',\n event,\n });\n },\n });\n\n // 3. Commit changes\n const wtGit = new GitOperations(taskWorktreeDir);\n if (await wtGit.hasChanges()) {\n await wtGit.add(['.']);\n await wtGit.commit(`braindump: ${task.title}`);\n await wtGit.push(task.branchName);\n }\n\n // 4. Mark done\n this.tracker.updateTaskStatus(batchId, task.id, TaskStatus.Done, {\n completedAt: new Date().toISOString(),\n });\n eventBus.emitTyped('braindump:task:completed', { batchId, taskId: task.id });\n logger.info('Task execution completed', { batchId, taskId: task.id });\n\n // 5. Enqueue for merge\n await this.mergeQueue.enqueue(batchId, {\n ...task,\n status: TaskStatus.Done,\n completedAt: new Date().toISOString(),\n });\n } catch (err) {\n const errorMsg = (err as Error).message;\n logger.error('Task execution failed', { batchId, taskId: task.id, error: errorMsg });\n this.tracker.markTaskFailed(batchId, task.id, errorMsg);\n }\n }\n\n private getRunnerForTask(task: BraindumpTask): AIRunner {\n const mode = task.aiRunnerMode || this.config.ai.mode;\n if (mode === this.config.ai.mode) return this.defaultAiRunner;\n return createAIRunner({\n mode,\n binary: this.config.ai.binary,\n phaseTimeoutMs: this.config.braindump.taskTimeoutMs,\n nvmNodeVersion: this.config.ai.nvmNodeVersion,\n model: this.config.ai.model,\n });\n }\n}\n","/**\n * Build the prompt for AI task splitting.\n * The AI should browse the codebase, analyze the user's input,\n * and output a structured JSON with independent tasks.\n */\nexport function buildSplitPrompt(rawInput: string): string {\n return `你是一个项目管理专家和代码架构师。用户输入了一段包含多个想法/特性/任务的文本内容。\n\n你的任务:\n1. 先浏览项目代码库,了解项目结构和技术栈\n2. 分析用户输入的内容,识别出所有独立的特性或任务\n3. 为每个任务生成清晰的标题、详细描述和依赖关系\n4. 标注哪些任务可以并行执行(没有依赖关系的任务)\n\n## 用户输入\n\n${rawInput}\n\n## 输出要求\n\n请严格按以下 JSON 格式输出(用 \\`\\`\\`json 包裹):\n\n\\`\\`\\`json\n{\n \"tasks\": [\n {\n \"title\": \"简洁的任务标题\",\n \"description\": \"详细的任务描述,包含:\\\\n1. 需要修改哪些文件\\\\n2. 具体要做什么改动\\\\n3. 验收标准\",\n \"dependsOn\": [],\n \"estimatedComplexity\": \"low\"\n },\n {\n \"title\": \"另一个任务\",\n \"description\": \"这个任务依赖第一个任务的完成\",\n \"dependsOn\": [0],\n \"estimatedComplexity\": \"medium\"\n }\n ]\n}\n\\`\\`\\`\n\n## 注意事项\n\n- \\`dependsOn\\` 中填写的是任务在数组中的索引(从 0 开始),表示本任务依赖哪些任务先完成\n- 如果两个任务修改不同文件且逻辑独立,它们之间不应有依赖关系(可并行)\n- 如果两个任务修改相同文件或存在逻辑先后顺序,应设置依赖关系\n- \\`estimatedComplexity\\` 可选值:low(简单改动)、medium(中等复杂度)、high(复杂重构)\n- 每个任务的描述要足够详细,让 AI Agent 能独立完成该任务\n- 任务粒度适中:不要太大(一个任务不应包含多个独立变更),也不要太小(一行代码的改动不需要单独一个任务)`;\n}\n","import type { AIRunner, RunResult, StreamEvent } from '../ai-runner/index.js';\nimport type { SplitResult } from './BraindumpState.js';\nimport { buildSplitPrompt } from './prompts/split-prompt.js';\nimport { AIOutputParseError } from '../errors/index.js';\nimport { logger as rootLogger } from '../logger.js';\n\nconst logger = rootLogger.child('TaskSplitter');\n\nexport class TaskSplitter {\n constructor(private aiRunner: AIRunner) {}\n\n async split(\n rawInput: string,\n workDir: string,\n timeoutMs: number,\n onEvent?: (event: StreamEvent) => void,\n ): Promise<SplitResult> {\n logger.info('Starting task split', { inputLength: rawInput.length });\n\n const prompt = buildSplitPrompt(rawInput);\n const result: RunResult = await this.aiRunner.run({\n prompt,\n workDir,\n timeoutMs,\n onStreamEvent: onEvent,\n });\n\n return this.parseSplitOutput(result.output);\n }\n\n private parseSplitOutput(output: string): SplitResult {\n // Extract JSON block from AI output (wrapped in ```json ... ```)\n const jsonMatch = output.match(/```json\\s*\\n([\\s\\S]*?)\\n\\s*```/);\n if (!jsonMatch) {\n // Try parsing the entire output as JSON\n try {\n return this.validateResult(JSON.parse(output));\n } catch {\n throw new AIOutputParseError(\n 'Failed to parse task split output: no JSON block found in AI response',\n output,\n );\n }\n }\n\n try {\n return this.validateResult(JSON.parse(jsonMatch[1]));\n } catch (err) {\n throw new AIOutputParseError(`Failed to parse task split JSON: ${(err as Error).message}`, jsonMatch[1]);\n }\n }\n\n private validateResult(raw: unknown): SplitResult {\n if (!raw || typeof raw !== 'object') {\n throw new AIOutputParseError('Split result is not an object');\n }\n\n const obj = raw as Record<string, unknown>;\n if (!Array.isArray(obj.tasks)) {\n throw new AIOutputParseError('Split result missing \"tasks\" array');\n }\n\n const tasks = obj.tasks.map((t: Record<string, unknown>, i: number) => ({\n title: String(t.title || `Task ${i + 1}`),\n description: String(t.description || ''),\n dependsOn: Array.isArray(t.dependsOn)\n ? t.dependsOn.map(Number).filter(n => !isNaN(n))\n : [],\n estimatedComplexity: (['low', 'medium', 'high'].includes(String(t.estimatedComplexity))\n ? String(t.estimatedComplexity)\n : 'medium') as 'low' | 'medium' | 'high',\n }));\n\n logger.info('Parsed split result', { taskCount: tasks.length });\n return { tasks };\n }\n}\n","import { GitOperations } from '../git/GitOperations.js';\nimport { ConflictResolver } from '../git/ConflictResolver.js';\nimport { AsyncMutex } from '../utils/AsyncMutex.js';\nimport type { AIRunner } from '../ai-runner/index.js';\nimport { eventBus } from '../events/EventBus.js';\nimport { logger as rootLogger } from '../logger.js';\nimport type { BraindumpTask } from './BraindumpState.js';\nimport { TaskStatus } from './BraindumpState.js';\nimport { BraindumpTracker } from './BraindumpTracker.js';\n\nconst logger = rootLogger.child('MergeQueue');\n\ninterface MergeRequest {\n batchId: string;\n task: BraindumpTask;\n resolve: () => void;\n reject: (err: Error) => void;\n}\n\n/**\n * Serial merge queue: tasks are merged one at a time into the integration branch.\n * This ensures each merge sees the latest integration state and minimizes conflicts.\n */\nexport class MergeQueue {\n private queue: MergeRequest[] = [];\n private processing = false;\n private mainGit: GitOperations;\n private mainGitMutex: AsyncMutex;\n private conflictResolver: ConflictResolver;\n private tracker: BraindumpTracker;\n private worktreeBaseDir: string;\n private projectSubDir: string;\n private phaseTimeoutMs: number;\n private maxConflictAttempts: number;\n\n constructor(opts: {\n mainGit: GitOperations;\n mainGitMutex: AsyncMutex;\n aiRunner: AIRunner;\n tracker: BraindumpTracker;\n worktreeBaseDir: string;\n projectSubDir: string;\n phaseTimeoutMs: number;\n maxConflictAttempts: number;\n }) {\n this.mainGit = opts.mainGit;\n this.mainGitMutex = opts.mainGitMutex;\n this.conflictResolver = new ConflictResolver(opts.aiRunner);\n this.tracker = opts.tracker;\n this.worktreeBaseDir = opts.worktreeBaseDir;\n this.projectSubDir = opts.projectSubDir;\n this.phaseTimeoutMs = opts.phaseTimeoutMs;\n this.maxConflictAttempts = opts.maxConflictAttempts;\n }\n\n /** Enqueue a completed task for merging. Returns a promise that resolves when merge is done. */\n enqueue(batchId: string, task: BraindumpTask): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n this.queue.push({ batchId, task, resolve, reject });\n this.processNext();\n });\n }\n\n get pendingCount(): number {\n return this.queue.length;\n }\n\n private async processNext(): Promise<void> {\n if (this.processing || this.queue.length === 0) return;\n this.processing = true;\n\n // Sort by task index to ensure deterministic merge order\n this.queue.sort((a, b) => a.task.index - b.task.index);\n const req = this.queue.shift()!;\n\n try {\n await this.mergeTask(req.batchId, req.task);\n req.resolve();\n } catch (err) {\n req.reject(err as Error);\n } finally {\n this.processing = false;\n // Process next item if any\n if (this.queue.length > 0) {\n this.processNext();\n }\n }\n }\n\n private async mergeTask(batchId: string, task: BraindumpTask): Promise<void> {\n const batch = this.tracker.getBatch(batchId);\n if (!batch) throw new Error(`Batch ${batchId} not found`);\n\n logger.info('Merging task into integration branch', {\n batchId, taskId: task.id, taskIndex: task.index, branch: task.branchName,\n });\n\n this.tracker.updateTaskStatus(batchId, task.id, TaskStatus.Merging);\n eventBus.emitTyped('braindump:task:merging', { batchId, taskId: task.id });\n\n const taskWorktreeDir = this.getTaskWorktreeDir(batchId, task.index);\n const taskWorkDir = this.projectSubDir\n ? `${taskWorktreeDir}/${this.projectSubDir}`\n : taskWorktreeDir;\n\n try {\n const wtGit = new GitOperations(taskWorktreeDir);\n\n // 1. Checkout the task branch\n await wtGit.checkout(task.branchName);\n\n // 2. Rebase onto integration branch (with AI conflict resolution)\n await this.conflictResolver.resolve({\n wtGit,\n targetRef: `origin/${batch.integrationBranch}`,\n workDir: taskWorkDir,\n branchName: task.branchName,\n contextId: task.id,\n phaseTimeoutMs: this.phaseTimeoutMs,\n maxAttempts: this.maxConflictAttempts,\n onEvent: (event) => {\n eventBus.emitTyped('agent:output', {\n batchId,\n taskId: task.id,\n phase: 'merge-conflict-resolve',\n event,\n });\n },\n });\n\n // 3. Force push the rebased task branch\n await wtGit.forcePush(task.branchName);\n\n // 4. Merge into integration branch (in main repo under mutex)\n await this.mainGitMutex.runExclusive(async () => {\n await this.mainGit.fetch();\n await this.mainGit.checkout(batch.integrationBranch);\n try {\n await this.mainGit.mergeFF(task.branchName);\n } catch {\n // If ff not possible, do a regular merge\n await this.mainGit.merge(task.branchName, `merge: braindump task #${task.index} - ${task.title}`);\n }\n await this.mainGit.push(batch.integrationBranch);\n });\n\n // 5. Update status\n this.tracker.updateTaskStatus(batchId, task.id, TaskStatus.Merged, {\n mergedAt: new Date().toISOString(),\n });\n eventBus.emitTyped('braindump:task:merged', { batchId, taskId: task.id });\n\n logger.info('Task merged successfully', { batchId, taskId: task.id });\n } catch (err) {\n const errorMsg = (err as Error).message;\n logger.error('Task merge failed', { batchId, taskId: task.id, error: errorMsg });\n\n // Try to abort any in-progress rebase\n try {\n const wtGit = new GitOperations(taskWorktreeDir);\n if (await wtGit.isRebaseInProgress()) {\n await wtGit.rebaseAbort();\n }\n } catch { /* ignore */ }\n\n this.tracker.markTaskFailed(batchId, task.id, `Merge failed: ${errorMsg}`);\n throw err;\n }\n }\n\n private getTaskWorktreeDir(batchId: string, taskIndex: number): string {\n const batchShort = batchId.slice(0, 8);\n return `${this.worktreeBaseDir}/braindump-${batchShort}-task-${taskIndex}`;\n }\n}\n","/**\n * Build the prompt for executing a single braindump task.\n */\nexport function buildTaskPrompt(taskTitle: string, taskDescription: string): string {\n return `你需要完成以下任务:\n\n## 任务:${taskTitle}\n\n${taskDescription}\n\n## 要求\n\n1. 仔细阅读相关代码,理解现有架构\n2. 按照任务描述进行代码修改\n3. 确保修改不会破坏现有功能\n4. 遵循项目现有的代码风格和规范\n5. 完成后确保代码可以通过 lint 检查`;\n}\n","import { BatchStatus, TaskStatus, type BraindumpBatch, type BraindumpTask } from './BraindumpState.js';\nimport { BaseTracker } from '../tracker/BaseTracker.js';\nimport { type ExecutableTask, braindumpTaskToExecutableTask } from '../tracker/ExecutableTask.js';\nimport { BatchNotFoundError, TaskNotFoundError } from '../errors/index.js';\nimport { logger as rootLogger } from '../logger.js';\nimport { eventBus } from '../events/EventBus.js';\n\nconst logger = rootLogger.child('BraindumpTracker');\n\nexport class BraindumpTracker extends BaseTracker<BraindumpBatch> {\n constructor(dataDir: string) {\n super(dataDir, 'braindump-tracker.json', 'batches', 'braindump-tracker');\n }\n\n // ── CRUD ──\n\n getBatch(batchId: string): BraindumpBatch | undefined {\n return this.getByKey(batchId);\n }\n\n getAll(): BraindumpBatch[] {\n return this.getAllRecords();\n }\n\n createBatch(batch: BraindumpBatch): BraindumpBatch {\n this.setRecord(batch.id, batch);\n this.save();\n logger.info('Batch created', { batchId: batch.id });\n eventBus.emitTyped('braindump:created', { batchId: batch.id });\n return batch;\n }\n\n updateBatch(batchId: string, updates: Partial<BraindumpBatch>): void {\n const batch = this.collection[batchId];\n if (!batch) throw new BatchNotFoundError(batchId);\n Object.assign(batch, updates, { updatedAt: new Date().toISOString() });\n this.save();\n }\n\n deleteBatch(batchId: string): boolean {\n if (!this.deleteByKey(batchId)) return false;\n logger.info('Batch deleted', { batchId });\n return true;\n }\n\n // ── Status helpers ──\n\n updateBatchStatus(batchId: string, status: BatchStatus, error?: string): void {\n const batch = this.collection[batchId];\n if (!batch) throw new BatchNotFoundError(batchId);\n batch.status = status;\n batch.updatedAt = new Date().toISOString();\n if (error !== undefined) batch.lastError = error;\n if (status === BatchStatus.Completed) batch.completedAt = new Date().toISOString();\n this.save();\n }\n\n updateTaskStatus(batchId: string, taskId: string, status: TaskStatus, extra?: Partial<BraindumpTask>): void {\n const batch = this.collection[batchId];\n if (!batch) throw new BatchNotFoundError(batchId);\n const task = batch.tasks.find(t => t.id === taskId);\n if (!task) throw new TaskNotFoundError(taskId);\n task.status = status;\n if (extra) Object.assign(task, extra);\n batch.updatedAt = new Date().toISOString();\n this.save();\n }\n\n markTaskFailed(batchId: string, taskId: string, error: string): void {\n const batch = this.collection[batchId];\n if (!batch) return;\n const task = batch.tasks.find(t => t.id === taskId);\n if (!task) return;\n task.status = TaskStatus.Failed;\n task.lastError = error;\n task.attempts += 1;\n batch.updatedAt = new Date().toISOString();\n this.save();\n logger.warn('Task marked failed', { batchId, taskId, error, attempts: task.attempts });\n eventBus.emitTyped('braindump:task:failed', { batchId, taskId, error });\n }\n\n // ── Recovery ──\n\n /** Recover batches interrupted by service restart. Returns count of recovered batches. */\n recoverInterrupted(): number {\n const IN_PROGRESS_TASK_STATES = new Set([TaskStatus.Running, TaskStatus.Merging, TaskStatus.ConflictResolving]);\n let count = 0;\n\n for (const batch of this.getAllRecords()) {\n if (batch.status !== BatchStatus.Running && batch.status !== BatchStatus.Merging) continue;\n\n let recovered = false;\n for (const task of batch.tasks) {\n if (IN_PROGRESS_TASK_STATES.has(task.status)) {\n task.status = TaskStatus.Failed;\n task.lastError = 'Interrupted by service restart';\n task.attempts += 1;\n recovered = true;\n }\n }\n\n if (recovered) {\n // If any task is still pending/blocked/done, keep batch running for retry\n const hasRecoverable = batch.tasks.some(\n t => t.status === TaskStatus.Pending || t.status === TaskStatus.Blocked || t.status === TaskStatus.Done,\n );\n if (!hasRecoverable) {\n batch.status = BatchStatus.Failed;\n batch.lastError = 'Interrupted by service restart';\n }\n batch.updatedAt = new Date().toISOString();\n count++;\n }\n }\n\n if (count > 0) {\n this.save();\n logger.info('Recovered interrupted braindump batches', { count });\n }\n return count;\n }\n\n /** 将指定批次的所有任务投影为 ExecutableTask[] */\n toExecutableTasks(batchId: string): ExecutableTask[] {\n const batch = this.getByKey(batchId);\n if (!batch) return [];\n return batch.tasks.map((task) =>\n braindumpTaskToExecutableTask(task, batch),\n );\n }\n}\n"],"mappings":"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","names":["logger","logger","logger","logger"]}
|
|
@@ -7,12 +7,13 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
getBinaryEnvKey,
|
|
9
9
|
getDefaultBinary
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-SAMTXC4A.js";
|
|
11
11
|
|
|
12
12
|
// src/config.ts
|
|
13
13
|
import { config as loadDotenv } from "dotenv";
|
|
14
14
|
import path2 from "path";
|
|
15
15
|
import fs from "fs";
|
|
16
|
+
import os from "os";
|
|
16
17
|
import { fileURLToPath } from "url";
|
|
17
18
|
|
|
18
19
|
// src/config-schema.ts
|
|
@@ -33,6 +34,15 @@ function envPort(defaultValue) {
|
|
|
33
34
|
function envMs(defaultValue) {
|
|
34
35
|
return z.coerce.number().int().min(1e3, "Duration must be >= 1000ms").optional().default(Number(defaultValue));
|
|
35
36
|
}
|
|
37
|
+
function parsePtyPhaseAgents(raw) {
|
|
38
|
+
if (!raw) return {};
|
|
39
|
+
const result = {};
|
|
40
|
+
for (const pair of raw.split(",")) {
|
|
41
|
+
const [phase, agent] = pair.split(":").map((s) => s.trim());
|
|
42
|
+
if (phase && agent) result[phase] = agent;
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
36
46
|
var envSchema = z.object({
|
|
37
47
|
// --- Required ---
|
|
38
48
|
GONGFENG_API_URL: z.string().url("GONGFENG_API_URL must be a valid URL"),
|
|
@@ -93,7 +103,7 @@ var envSchema = z.object({
|
|
|
93
103
|
E2E_UAT_VENDOR_DIR: z.string().optional().default(""),
|
|
94
104
|
E2E_UAT_CONFIG_FILE: z.string().optional().default(""),
|
|
95
105
|
E2E_PYTHON_BIN: z.string().optional().default("python3"),
|
|
96
|
-
E2E_AI_RUNNER_MODE: z.string().optional()
|
|
106
|
+
E2E_AI_RUNNER_MODE: z.string().optional(),
|
|
97
107
|
E2E_AI_MODEL: z.string().optional(),
|
|
98
108
|
// --- Preview ---
|
|
99
109
|
PREVIEW_ENABLED: envBoolean("false"),
|
|
@@ -159,6 +169,16 @@ var envSchema = z.object({
|
|
|
159
169
|
VERIFY_FIX_LOOP_ENABLED: envBoolean("true"),
|
|
160
170
|
VERIFY_FIX_MAX_ITERATIONS: envInt("3", { min: 1, max: 10 }),
|
|
161
171
|
VERIFY_TODOLIST_CHECK_ENABLED: envBoolean("true"),
|
|
172
|
+
// --- Terminal (交互式终端) ---
|
|
173
|
+
TERMINAL_ENABLED: envBoolean("false"),
|
|
174
|
+
TERMINAL_IDLE_TIMEOUT_MS: envMs("1800000"),
|
|
175
|
+
TERMINAL_MAX_SESSIONS: envInt("5", { min: 1 }),
|
|
176
|
+
// --- PTY Runner ---
|
|
177
|
+
PTY_IDLE_DETECT_MS: envMs("30000"),
|
|
178
|
+
/** Default AI agent for PTY mode (must have a PtyProfile in registry) */
|
|
179
|
+
PTY_DEFAULT_AGENT: z.string().optional().default("claude-internal"),
|
|
180
|
+
/** Per-phase agent overrides, format: "plan:claude-internal,build:codebuddy" */
|
|
181
|
+
PTY_PHASE_AGENTS: z.string().optional().default(""),
|
|
162
182
|
// --- Analytics (运营分析) ---
|
|
163
183
|
ANALYTICS_ENABLED: envBoolean("false"),
|
|
164
184
|
ANALYTICS_TARGET_API_URL: z.string().url().optional(),
|
|
@@ -275,7 +295,7 @@ function transformEnvToConfig(env, dirname) {
|
|
|
275
295
|
uatVendorDir: env.E2E_UAT_VENDOR_DIR,
|
|
276
296
|
uatConfigFile: env.E2E_UAT_CONFIG_FILE,
|
|
277
297
|
pythonBin: env.E2E_PYTHON_BIN,
|
|
278
|
-
aiRunnerMode: env.E2E_AI_RUNNER_MODE,
|
|
298
|
+
aiRunnerMode: env.E2E_AI_RUNNER_MODE ?? env.AI_RUNNER_MODE,
|
|
279
299
|
aiModel: env.E2E_AI_MODEL
|
|
280
300
|
},
|
|
281
301
|
preview: {
|
|
@@ -356,6 +376,16 @@ function transformEnvToConfig(env, dirname) {
|
|
|
356
376
|
cacheTtlMs: env.ANALYTICS_CACHE_TTL_MS,
|
|
357
377
|
skillAutoDiscover: env.ANALYTICS_SKILL_AUTO_DISCOVER
|
|
358
378
|
},
|
|
379
|
+
terminal: {
|
|
380
|
+
enabled: env.TERMINAL_ENABLED,
|
|
381
|
+
idleTimeoutMs: env.TERMINAL_IDLE_TIMEOUT_MS,
|
|
382
|
+
maxSessions: env.TERMINAL_MAX_SESSIONS
|
|
383
|
+
},
|
|
384
|
+
pty: {
|
|
385
|
+
idleDetectMs: env.PTY_IDLE_DETECT_MS,
|
|
386
|
+
defaultAgent: env.PTY_DEFAULT_AGENT,
|
|
387
|
+
phaseAgents: parsePtyPhaseAgents(env.PTY_PHASE_AGENTS)
|
|
388
|
+
},
|
|
359
389
|
workspace: {
|
|
360
390
|
configPath: env.WORKSPACE_CONFIG_PATH
|
|
361
391
|
},
|
|
@@ -384,7 +414,7 @@ ${lines.join("\n")}`
|
|
|
384
414
|
var __dirname = path2.dirname(fileURLToPath(import.meta.url));
|
|
385
415
|
function resolveConfigFilePath(configPath) {
|
|
386
416
|
if (configPath) return configPath;
|
|
387
|
-
if (process.env.IAF_CONFIG_PATH) return process.env.IAF_CONFIG_PATH;
|
|
417
|
+
if (process.env.IAF_CONFIG_PATH) return process.env.IAF_CONFIG_PATH.replace(/^~(?=$|\/)/, os.homedir());
|
|
388
418
|
const localEnv = path2.resolve(__dirname, "../.env");
|
|
389
419
|
if (fs.existsSync(localEnv)) return localEnv;
|
|
390
420
|
const globalEnv = path2.join(getGlobalDir(), ".env");
|
|
@@ -428,4 +458,4 @@ export {
|
|
|
428
458
|
resetDotenvCache,
|
|
429
459
|
reloadConfig
|
|
430
460
|
};
|
|
431
|
-
//# sourceMappingURL=chunk-
|
|
461
|
+
//# sourceMappingURL=chunk-NZHKAPU6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/config-schema.ts"],"sourcesContent":["import { config as loadDotenv } from 'dotenv';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport { fileURLToPath } from 'node:url';\nimport {\n envSchema,\n extractEnvSubset,\n transformEnvToConfig,\n ConfigValidationError,\n} from './config-schema.js';\nimport { getGlobalDir } from './paths.js';\n\nexport type {\n Config,\n AIRunnerMode,\n BrainstormAgentConfig,\n ChatAgentConfig,\n // Sub-type aliases for interface segregation\n GongfengConfig,\n ProjectConfig,\n AIConfig,\n PollConfig,\n PipelineConfig,\n ReviewConfig,\n WebConfig,\n IssueNoteSyncConfig,\n WebhookConfig,\n E2eConfig,\n PreviewConfig,\n BrainstormConfig,\n ChatConfig,\n BraindumpConfig,\n AutoUpdateConfig,\n IWikiConfig,\n KnowledgeConfig,\n DistillConfig,\n SyncConfig,\n WorkspaceEnvConfig,\n} from './config-schema.js';\nexport { ConfigValidationError } from './config-schema.js';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n/**\n * Resolve the path to the .env configuration file.\n *\n * Priority (first match wins):\n * 1. Explicit `configPath` argument (e.g. --config CLI flag)\n * 2. `IAF_CONFIG_PATH` environment variable\n * 3. Source-mode: `.env` next to compiled output (`dist/../.env`)\n * 4. Global config written by `issue-auto-finish init` (`{IAF_HOME}/.env`)\n * 5. CWD `.env` (legacy / manual setup)\n * 6. Fallback: global path (even if it doesn't exist yet)\n *\n * `IAF_HOME` defaults to `~/.issue-auto-finish` and can be overridden via env.\n *\n * This is the **single source of truth** for .env path resolution.\n * CLI commands and route handlers should call this instead of\n * rolling their own lookup logic.\n */\nexport function resolveConfigFilePath(configPath?: string): string {\n if (configPath) return configPath;\n if (process.env.IAF_CONFIG_PATH) return process.env.IAF_CONFIG_PATH.replace(/^~(?=$|\\/)/, os.homedir());\n\n // Source-mode: .env next to the compiled output\n const localEnv = path.resolve(__dirname, '../.env');\n if (fs.existsSync(localEnv)) return localEnv;\n\n // Global config written by `issue-auto-finish init`\n // Checked before CWD to avoid picking up unrelated project .env files\n const globalEnv = path.join(getGlobalDir(), '.env');\n if (fs.existsSync(globalEnv)) return globalEnv;\n\n // Fallback: CWD .env (legacy / manual setup)\n const cwdEnv = path.resolve(process.cwd(), '.env');\n if (fs.existsSync(cwdEnv)) return cwdEnv;\n\n return globalEnv;\n}\n\nlet _dotenvLoaded = false;\nfunction ensureDotenvLoaded(): void {\n if (_dotenvLoaded) return;\n _dotenvLoaded = true;\n loadDotenv({ path: resolveConfigFilePath() });\n}\n\nexport function loadConfig() {\n ensureDotenvLoaded();\n\n const subset = extractEnvSubset(process.env);\n const result = envSchema.safeParse(subset);\n\n if (!result.success) {\n throw new ConfigValidationError(result.error);\n }\n\n return transformEnvToConfig(result.data, __dirname);\n}\n\n/**\n * Reset dotenv cache so the next loadConfig() re-reads the .env file.\n */\nexport function resetDotenvCache(): void {\n _dotenvLoaded = false;\n}\n\n/**\n * Reload config from .env file.\n * Clears dotenv cache, removes stale IAF env vars from process.env,\n * then re-reads and re-parses everything.\n */\nexport function reloadConfig() {\n resetDotenvCache();\n // Clear process.env of IAF-related keys so dotenv can overwrite them\n const iafKeys = extractEnvSubset(process.env);\n for (const key of Object.keys(iafKeys)) {\n delete process.env[key];\n }\n return loadConfig();\n}\n","/**\n * Zod-based configuration schema and transformation layer.\n *\n * Architecture:\n * process.env → extractEnvSubset() → envSchema.safeParse() → transformEnvToConfig() → Config\n *\n * The schema is kept flat (keys = env var names) so that validation errors\n * directly reference environment variable names without extra mapping.\n */\nimport { z } from 'zod';\nimport { getDefaultBinary, getBinaryEnvKey } from './ai-runner/AIRunnerRegistry.js';\nimport { getLocalIP } from './utils/network.js';\nimport path from 'node:path';\n\n// ---------------------------------------------------------------------------\n// Reusable zod helpers\n// ---------------------------------------------------------------------------\n\n/** Boolean env var: only `'true'` (case-sensitive) is truthy. */\nfunction envBoolean(defaultValue: string = 'false') {\n return z\n .string()\n .optional()\n .default(defaultValue)\n .transform((v) => v === 'true');\n}\n\n/** Integer env var with optional min/max bounds. */\nfunction envInt(defaultValue: string, opts?: { min?: number; max?: number }) {\n let schema = z.coerce.number().int();\n if (opts?.min !== undefined) schema = schema.min(opts.min);\n if (opts?.max !== undefined) schema = schema.max(opts.max);\n return schema.optional().default(Number(defaultValue));\n}\n\n/** Port number: integer 1–65535. */\nfunction envPort(defaultValue: string) {\n return z.coerce\n .number()\n .int()\n .min(1, 'Port must be >= 1')\n .max(65535, 'Port must be <= 65535')\n .optional()\n .default(Number(defaultValue));\n}\n\n/** Millisecond duration: integer >= 1000. */\nfunction envMs(defaultValue: string) {\n return z.coerce\n .number()\n .int()\n .min(1000, 'Duration must be >= 1000ms')\n .optional()\n .default(Number(defaultValue));\n}\n\n/** Parse \"plan:claude-internal,build:codebuddy\" → { plan: 'claude-internal', build: 'codebuddy' } */\nfunction parsePtyPhaseAgents(raw: string): Record<string, string> {\n if (!raw) return {};\n const result: Record<string, string> = {};\n for (const pair of raw.split(',')) {\n const [phase, agent] = pair.split(':').map((s) => s.trim());\n if (phase && agent) result[phase] = agent;\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Flat environment schema\n// ---------------------------------------------------------------------------\n\nexport const envSchema = z.object({\n // --- Required ---\n GONGFENG_API_URL: z.string().url('GONGFENG_API_URL must be a valid URL'),\n GONGFENG_PRIVATE_TOKEN: z.string().min(1, 'GONGFENG_PRIVATE_TOKEN is required'),\n GONGFENG_PROJECT_PATH: z.string().min(1, 'GONGFENG_PROJECT_PATH is required'),\n PROJECT_WORK_DIR: z.string().min(1, 'PROJECT_WORK_DIR is required'),\n\n // --- Paths (override auto-detected data/logs directories) ---\n DATA_DIR: z.string().optional(),\n LOGS_DIR: z.string().optional(),\n\n // --- Project ---\n GIT_ROOT_DIR: z.string().optional(),\n BASE_BRANCH: z.string().optional().default('master'),\n BRANCH_PREFIX: z.string().optional().default('feat/issue'),\n WORKTREE_BASE_DIR: z.string().optional().default(''),\n PROJECT_SUBDIR: z.string().optional().default(''),\n\n // --- AI ---\n AI_RUNNER_MODE: z\n .string()\n .optional()\n .default('claude-internal'),\n AI_MODEL: z.string().optional().default('Claude-4.6-Opus'),\n CLAUDE_BINARY: z.string().optional(),\n CURSOR_BINARY: z.string().optional(),\n CODEBUDDY_BINARY: z.string().optional(),\n CODEBUDDY_ACP_AUTO_APPROVE: envBoolean('true'),\n CLAUDE_PHASE_TIMEOUT_MS: envMs('1800000'),\n AI_IDLE_TIMEOUT_MS: z.coerce\n .number()\n .int()\n .min(0, 'AI_IDLE_TIMEOUT_MS must be >= 0 (0 to disable)')\n .optional()\n .default(1200000),\n NVM_NODE_VERSION: z.string().optional().default('20'),\n\n // --- Pipeline ---\n PIPELINE_MODE: z\n .string()\n .optional()\n .default('auto'),\n\n // --- Poll ---\n POLL_DISCOVERY_INTERVAL_MS: envMs('60000'),\n POLL_DRIVE_INTERVAL_MS: envMs('15000'),\n MAX_RETRIES: envInt('3', { min: 0, max: 100 }),\n MAX_CONCURRENT_ISSUES: envInt('3', { min: 1 }),\n\n // --- Review ---\n REVIEW_ENABLED: envBoolean('true'),\n REVIEW_AUTO_APPROVE_LABELS: z.string().optional().default(''),\n\n // --- Web ---\n WEB_ENABLED: envBoolean('true'),\n WEB_HOST: z.string().optional().default('0.0.0.0'),\n WEB_PORT: envPort('3000'),\n FRONTEND_DIST_DIR: z.string().optional(),\n\n // --- Issue Note Sync ---\n ISSUE_NOTE_SYNC_ENABLED: envBoolean('true'),\n WEB_BASE_URL: z.string().optional(),\n\n // --- Webhook ---\n WEBHOOK_ENABLED: envBoolean('false'),\n WEBHOOK_HOST: z.string().optional().default('0.0.0.0'),\n WEBHOOK_PORT: envPort('8081'),\n WEBHOOK_SECRET: z.string().optional().default(''),\n WEBHOOK_LLM_FALLBACK: envBoolean('true'),\n WEBHOOK_LLM_BINARY: z.string().optional().default('claude-internal'),\n\n // --- E2E ---\n E2E_UI_ENABLED: envBoolean('false'),\n E2E_BASE_URL: z.string().optional().default('https://localhost:8890'),\n E2E_BACKEND_URL: z.string().optional().default('http://127.0.0.1:3000'),\n E2E_AUTH_COOKIES: z.string().optional().default('[]'),\n E2E_BACKEND_PORT_BASE: envPort('4000'),\n E2E_FRONTEND_PORT_BASE: envPort('9000'),\n E2E_UAT_VENDOR_DIR: z.string().optional().default(''),\n E2E_UAT_CONFIG_FILE: z.string().optional().default(''),\n E2E_PYTHON_BIN: z.string().optional().default('python3'),\n E2E_AI_RUNNER_MODE: z.string().optional(),\n E2E_AI_MODEL: z.string().optional(),\n\n // --- Preview ---\n PREVIEW_ENABLED: envBoolean('false'),\n PREVIEW_HOST: z.string().optional().default(''),\n PREVIEW_TTL_MS: envMs(String(24 * 60 * 60 * 1000)),\n PREVIEW_KEEP_AFTER_COMPLETE: envBoolean('false'),\n PREVIEW_REAP_INTERVAL_MS: envMs('300000'),\n\n // --- Brainstorm ---\n BRAINSTORM_ENABLED: envBoolean('false'),\n BRAINSTORM_MAX_ROUNDS: envInt('5', { min: 1, max: 20 }),\n BRAINSTORM_TIMEOUT_MS: envMs('600000'),\n BRAINSTORM_GENERATOR_MODE: z\n .string()\n .optional(),\n BRAINSTORM_GENERATOR_BINARY: z.string().optional(),\n BRAINSTORM_GENERATOR_MODEL: z.string().optional(),\n BRAINSTORM_REVIEWER_MODE: z\n .string()\n .optional(),\n BRAINSTORM_REVIEWER_BINARY: z.string().optional(),\n BRAINSTORM_REVIEWER_MODEL: z.string().optional(),\n\n // --- Chat ---\n CHAT_ENABLED: envBoolean('false'),\n CHAT_TIMEOUT_MS: envMs('300000'),\n CHAT_MAX_SESSION_MESSAGES: envInt('100', { min: 1 }),\n CHAT_AGENT_MODE: z\n .string()\n .optional(),\n CHAT_AGENT_BINARY: z.string().optional(),\n CHAT_AGENT_MODEL: z.string().optional(),\n\n // --- Braindump ---\n BRAINDUMP_ENABLED: envBoolean('false'),\n BRAINDUMP_MAX_CONCURRENT: z.coerce.number().int().min(1).optional(),\n BRAINDUMP_SPLIT_TIMEOUT_MS: envMs('300000'),\n BRAINDUMP_TASK_TIMEOUT_MS: z.coerce.number().int().min(1000).optional(),\n BRAINDUMP_MAX_CONFLICT_ATTEMPTS: envInt('20', { min: 1 }),\n BRAINDUMP_CREATE_MR: envBoolean('false'),\n\n // --- Auto Update ---\n AUTO_UPDATE_ENABLED: envBoolean('true'),\n AUTO_UPDATE_INTERVAL_MS: envMs('600000'),\n AUTO_UPDATE_REGISTRY: z\n .string()\n .url('AUTO_UPDATE_REGISTRY must be a valid URL')\n .optional()\n .default('https://registry.npmjs.org'),\n AUTO_UPDATE_DRAIN_TIMEOUT_MS: envMs('300000'),\n\n // --- iWiki ---\n IWIKI_AUTH_COOKIE: z.string().optional(),\n IWIKI_AUTH_TOKEN: z.string().optional(),\n IWIKI_BASE_URL: z.string().optional(),\n\n // --- Locale ---\n LOCALE: z.enum(['zh-CN', 'en']).optional().default('zh-CN'),\n\n // --- Knowledge ---\n KNOWLEDGE_ENABLED: envBoolean('false'),\n KNOWLEDGE_PATH: z.string().optional(),\n KNOWLEDGE_ORPHAN_BRANCH: z.string().optional().default('iaf/knowledge'),\n KNOWLEDGE_SYNC_VECTORS: envBoolean('false'),\n KNOWLEDGE_AUTO_RESTORE: envBoolean('true'),\n\n // --- Distill (知识蒸馏) ---\n DISTILL_ENABLED: envBoolean('false'),\n DISTILL_INTERVAL_MS: envMs('86400000'),\n DISTILL_DIARY_SUMMARIZE: envBoolean('false'),\n DISTILL_MIN_DIARIES_FOR_DISTILL: envInt('3', { min: 1 }),\n DISTILL_MEMORY_CONFIDENCE_THRESHOLD: z.coerce.number().min(0).max(1).optional().default(0.7),\n DISTILL_VECTOR_ENABLED: envBoolean('true'),\n\n // --- Sync (生成文件同步到目标项目) ---\n SYNC_KNOWLEDGE_TO_PROJECT: envBoolean('false'),\n SYNC_RULES_TO_PROJECT: envBoolean('false'),\n\n // --- Release (发布) ---\n RELEASE_ENABLED: envBoolean('false'),\n RELEASE_DETECT_CACHE_TTL_MS: envMs('604800000'),\n\n // --- Verify-Fix Loop (验证-修复循环) ---\n VERIFY_FIX_LOOP_ENABLED: envBoolean('true'),\n VERIFY_FIX_MAX_ITERATIONS: envInt('3', { min: 1, max: 10 }),\n VERIFY_TODOLIST_CHECK_ENABLED: envBoolean('true'),\n\n // --- Terminal (交互式终端) ---\n TERMINAL_ENABLED: envBoolean('false'),\n TERMINAL_IDLE_TIMEOUT_MS: envMs('1800000'),\n TERMINAL_MAX_SESSIONS: envInt('5', { min: 1 }),\n\n // --- PTY Runner ---\n PTY_IDLE_DETECT_MS: envMs('30000'),\n /** Default AI agent for PTY mode (must have a PtyProfile in registry) */\n PTY_DEFAULT_AGENT: z.string().optional().default('claude-internal'),\n /** Per-phase agent overrides, format: \"plan:claude-internal,build:codebuddy\" */\n PTY_PHASE_AGENTS: z.string().optional().default(''),\n\n // --- Analytics (运营分析) ---\n ANALYTICS_ENABLED: envBoolean('false'),\n ANALYTICS_TARGET_API_URL: z.string().url().optional(),\n ANALYTICS_TARGET_API_TOKEN: z.string().optional(),\n ANALYTICS_CACHE_TTL_MS: envMs('300000'),\n ANALYTICS_SKILL_AUTO_DISCOVER: envBoolean('true'),\n\n // --- Coordination (多节点协调) ---\n NODE_ID: z.string().optional(),\n\n // --- Workspace (多仓库) ---\n WORKSPACE_CONFIG_PATH: z.string().optional(),\n\n // --- Tenants (多租户) ---\n TENANTS_CONFIG_PATH: z.string().optional(),\n});\n\nexport type ParsedEnv = z.infer<typeof envSchema>;\n\n// ---------------------------------------------------------------------------\n// Extract env subset\n// ---------------------------------------------------------------------------\n\n/**\n * Picks only the keys declared in `envSchema` from the given env object.\n * Empty strings are converted to `undefined` to preserve the existing\n * \"empty = unset\" behavior.\n */\nexport function extractEnvSubset(\n env: Record<string, string | undefined>,\n): Record<string, string | undefined> {\n const keys = Object.keys(envSchema.shape) as (keyof typeof envSchema.shape)[];\n const subset: Record<string, string | undefined> = {};\n for (const key of keys) {\n const val = env[key as string];\n subset[key as string] = val || undefined; // empty string → undefined\n }\n return subset;\n}\n\n// ---------------------------------------------------------------------------\n// AI Runner helpers\n// ---------------------------------------------------------------------------\n\nexport type AIRunnerMode = string;\n\nfunction resolveAIBinary(\n mode: AIRunnerMode,\n env: ParsedEnv,\n): string {\n const envKey = getBinaryEnvKey(mode);\n if (envKey) {\n const envVal = (env as Record<string, unknown>)[envKey];\n if (typeof envVal === 'string' && envVal) return envVal;\n }\n return getDefaultBinary(mode);\n}\n\nexport interface BrainstormAgentConfig {\n mode: AIRunnerMode;\n binary: string;\n nvmNodeVersion: string;\n model?: string;\n}\n\nexport type ChatAgentConfig = BrainstormAgentConfig;\n\nfunction buildBrainstormAgent(\n role: 'GENERATOR' | 'REVIEWER',\n env: ParsedEnv,\n): BrainstormAgentConfig {\n const roleMode =\n (role === 'GENERATOR' ? env.BRAINSTORM_GENERATOR_MODE : env.BRAINSTORM_REVIEWER_MODE) ??\n env.AI_RUNNER_MODE;\n return {\n mode: roleMode,\n binary:\n (role === 'GENERATOR' ? env.BRAINSTORM_GENERATOR_BINARY : env.BRAINSTORM_REVIEWER_BINARY) ??\n resolveAIBinary(roleMode, env),\n nvmNodeVersion: env.NVM_NODE_VERSION,\n model:\n (role === 'GENERATOR' ? env.BRAINSTORM_GENERATOR_MODEL : env.BRAINSTORM_REVIEWER_MODEL) ??\n env.AI_MODEL,\n };\n}\n\nfunction buildChatAgent(env: ParsedEnv): ChatAgentConfig {\n // Chat defaults to claude-internal for lightweight conversational use,\n // independent of the global AI_RUNNER_MODE (which may be cursor-agent).\n const chatMode = env.CHAT_AGENT_MODE ?? 'claude-internal';\n return {\n mode: chatMode,\n binary: env.CHAT_AGENT_BINARY ?? resolveAIBinary(chatMode, env),\n nvmNodeVersion: env.NVM_NODE_VERSION,\n model: env.CHAT_AGENT_MODEL ?? env.AI_MODEL,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Transform parsed env → nested Config\n// ---------------------------------------------------------------------------\n\nexport function transformEnvToConfig(env: ParsedEnv, dirname: string) {\n const aiMode = env.AI_RUNNER_MODE;\n const webPort = env.WEB_PORT;\n\n const gitRootDir = env.GIT_ROOT_DIR ?? env.PROJECT_WORK_DIR;\n\n return {\n gongfeng: {\n apiUrl: env.GONGFENG_API_URL,\n privateToken: env.GONGFENG_PRIVATE_TOKEN,\n projectPath: env.GONGFENG_PROJECT_PATH,\n },\n project: {\n workDir: env.PROJECT_WORK_DIR,\n gitRootDir,\n baseBranch: env.BASE_BRANCH,\n branchPrefix: env.BRANCH_PREFIX,\n worktreeBaseDir: env.WORKTREE_BASE_DIR,\n projectSubDir: env.PROJECT_SUBDIR,\n },\n ai: {\n mode: aiMode,\n binary: resolveAIBinary(aiMode, env),\n phaseTimeoutMs: env.CLAUDE_PHASE_TIMEOUT_MS,\n idleTimeoutMs: env.AI_IDLE_TIMEOUT_MS || undefined,\n nvmNodeVersion: env.NVM_NODE_VERSION,\n model: env.AI_MODEL,\n codebuddyAcpAutoApprove: env.CODEBUDDY_ACP_AUTO_APPROVE,\n },\n poll: {\n discoveryIntervalMs: env.POLL_DISCOVERY_INTERVAL_MS,\n driveIntervalMs: env.POLL_DRIVE_INTERVAL_MS,\n maxRetries: env.MAX_RETRIES,\n maxConcurrent: env.MAX_CONCURRENT_ISSUES,\n },\n pipeline: {\n mode: env.PIPELINE_MODE,\n },\n review: {\n enabled: env.REVIEW_ENABLED,\n autoApproveLabels: env.REVIEW_AUTO_APPROVE_LABELS\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n },\n web: {\n enabled: env.WEB_ENABLED,\n host: env.WEB_HOST,\n port: webPort,\n frontendDistDir:\n env.FRONTEND_DIST_DIR ??\n path.resolve(dirname, '..', 'src/web/frontend/dist'),\n },\n issueNoteSync: {\n enabled: env.ISSUE_NOTE_SYNC_ENABLED,\n webBaseUrl: env.WEB_BASE_URL ?? `http://${getLocalIP()}:${webPort}`,\n },\n webhook: {\n enabled: env.WEBHOOK_ENABLED,\n host: env.WEBHOOK_HOST,\n port: env.WEBHOOK_PORT,\n secret: env.WEBHOOK_SECRET,\n llmFallback: env.WEBHOOK_LLM_FALLBACK,\n llmBinary: env.WEBHOOK_LLM_BINARY,\n },\n e2e: {\n enabled: env.E2E_UI_ENABLED,\n baseUrl: env.E2E_BASE_URL,\n backendUrl: env.E2E_BACKEND_URL,\n authCookies: env.E2E_AUTH_COOKIES,\n backendPortBase: env.E2E_BACKEND_PORT_BASE,\n frontendPortBase: env.E2E_FRONTEND_PORT_BASE,\n uatVendorDir: env.E2E_UAT_VENDOR_DIR,\n uatConfigFile: env.E2E_UAT_CONFIG_FILE,\n pythonBin: env.E2E_PYTHON_BIN,\n aiRunnerMode: env.E2E_AI_RUNNER_MODE ?? env.AI_RUNNER_MODE,\n aiModel: env.E2E_AI_MODEL,\n },\n preview: {\n enabled: env.PREVIEW_ENABLED,\n host: env.PREVIEW_HOST,\n ttlMs: env.PREVIEW_TTL_MS,\n keepAfterComplete: env.PREVIEW_KEEP_AFTER_COMPLETE,\n reapIntervalMs: env.PREVIEW_REAP_INTERVAL_MS,\n },\n brainstorm: {\n enabled: env.BRAINSTORM_ENABLED,\n maxRefinementRounds: env.BRAINSTORM_MAX_ROUNDS,\n timeoutMs: env.BRAINSTORM_TIMEOUT_MS,\n generator: buildBrainstormAgent('GENERATOR', env),\n reviewer: buildBrainstormAgent('REVIEWER', env),\n },\n chat: {\n enabled: env.CHAT_ENABLED,\n timeoutMs: env.CHAT_TIMEOUT_MS,\n maxSessionMessages: env.CHAT_MAX_SESSION_MESSAGES,\n agent: buildChatAgent(env),\n },\n braindump: {\n enabled: env.BRAINDUMP_ENABLED,\n maxConcurrent: env.BRAINDUMP_MAX_CONCURRENT ?? env.MAX_CONCURRENT_ISSUES,\n splitTimeoutMs: env.BRAINDUMP_SPLIT_TIMEOUT_MS,\n taskTimeoutMs: env.BRAINDUMP_TASK_TIMEOUT_MS ?? env.CLAUDE_PHASE_TIMEOUT_MS,\n maxConflictAttempts: env.BRAINDUMP_MAX_CONFLICT_ATTEMPTS,\n createMr: env.BRAINDUMP_CREATE_MR,\n },\n autoUpdate: {\n enabled: env.AUTO_UPDATE_ENABLED,\n intervalMs: env.AUTO_UPDATE_INTERVAL_MS,\n registry: env.AUTO_UPDATE_REGISTRY,\n drainTimeoutMs: env.AUTO_UPDATE_DRAIN_TIMEOUT_MS,\n },\n iwiki: {\n authCookie: env.IWIKI_AUTH_COOKIE,\n authToken: env.IWIKI_AUTH_TOKEN,\n baseUrl: env.IWIKI_BASE_URL,\n },\n locale: env.LOCALE,\n knowledge: {\n enabled: env.KNOWLEDGE_ENABLED,\n path: env.KNOWLEDGE_PATH,\n orphanBranch: env.KNOWLEDGE_ORPHAN_BRANCH,\n syncVectors: env.KNOWLEDGE_SYNC_VECTORS,\n autoRestore: env.KNOWLEDGE_AUTO_RESTORE,\n },\n distill: {\n enabled: env.DISTILL_ENABLED,\n intervalMs: env.DISTILL_INTERVAL_MS,\n diarySummarize: env.DISTILL_DIARY_SUMMARIZE,\n minDiariesForDistill: env.DISTILL_MIN_DIARIES_FOR_DISTILL,\n memoryConfidenceThreshold: env.DISTILL_MEMORY_CONFIDENCE_THRESHOLD,\n vectorEnabled: env.DISTILL_VECTOR_ENABLED,\n },\n coordination: {\n nodeId: env.NODE_ID,\n },\n sync: {\n knowledgeToProject: env.SYNC_KNOWLEDGE_TO_PROJECT,\n rulesToProject: env.SYNC_RULES_TO_PROJECT,\n },\n release: {\n enabled: env.RELEASE_ENABLED,\n detectCacheTtlMs: env.RELEASE_DETECT_CACHE_TTL_MS,\n },\n verifyFixLoop: {\n enabled: env.VERIFY_FIX_LOOP_ENABLED,\n maxIterations: env.VERIFY_FIX_MAX_ITERATIONS,\n todolistCheckEnabled: env.VERIFY_TODOLIST_CHECK_ENABLED,\n },\n analytics: {\n enabled: env.ANALYTICS_ENABLED,\n targetApiUrl: env.ANALYTICS_TARGET_API_URL,\n targetApiToken: env.ANALYTICS_TARGET_API_TOKEN,\n cacheTtlMs: env.ANALYTICS_CACHE_TTL_MS,\n skillAutoDiscover: env.ANALYTICS_SKILL_AUTO_DISCOVER,\n },\n terminal: {\n enabled: env.TERMINAL_ENABLED,\n idleTimeoutMs: env.TERMINAL_IDLE_TIMEOUT_MS,\n maxSessions: env.TERMINAL_MAX_SESSIONS,\n },\n pty: {\n idleDetectMs: env.PTY_IDLE_DETECT_MS,\n defaultAgent: env.PTY_DEFAULT_AGENT,\n phaseAgents: parsePtyPhaseAgents(env.PTY_PHASE_AGENTS),\n },\n workspace: {\n configPath: env.WORKSPACE_CONFIG_PATH,\n },\n tenants: {\n configPath: env.TENANTS_CONFIG_PATH,\n },\n } as const;\n}\n\n// ---------------------------------------------------------------------------\n// Config type (derived from transform)\n// ---------------------------------------------------------------------------\n\n// We use a widened version so the type is writable and compatible with existing\n// code that assigns to Config properties.\ntype TransformResult = ReturnType<typeof transformEnvToConfig>;\n\n// Deeply writable version of the transform result\ntype DeepWritable<T> = {\n -readonly [K in keyof T]: T[K] extends object ? DeepWritable<T[K]> : T[K];\n};\n\nexport type Config = DeepWritable<TransformResult>;\n\n// ---------------------------------------------------------------------------\n// Sub-type aliases for interface segregation\n// ---------------------------------------------------------------------------\n\n/** Gongfeng API connection settings. */\nexport type GongfengConfig = Config['gongfeng'];\n/** Project / repository settings. */\nexport type ProjectConfig = Config['project'];\n/** AI runner settings. */\nexport type AIConfig = Config['ai'];\n/** Polling intervals and concurrency limits. */\nexport type PollConfig = Config['poll'];\n/** Pipeline mode settings. */\nexport type PipelineConfig = Config['pipeline'];\n/** Review / gate settings. */\nexport type ReviewConfig = Config['review'];\n/** Web dashboard settings. */\nexport type WebConfig = Config['web'];\n/** Issue note-sync settings. */\nexport type IssueNoteSyncConfig = Config['issueNoteSync'];\n/** Webhook settings. */\nexport type WebhookConfig = Config['webhook'];\n/** E2E test settings. */\nexport type E2eConfig = Config['e2e'];\n/** Preview environment settings. */\nexport type PreviewConfig = Config['preview'];\n/** Brainstorm agent settings. */\nexport type BrainstormConfig = Config['brainstorm'];\n/** Chat agent settings. */\nexport type ChatConfig = Config['chat'];\n/** Braindump batch settings. */\nexport type BraindumpConfig = Config['braindump'];\n/** Auto-update settings. */\nexport type AutoUpdateConfig = Config['autoUpdate'];\n/** iWiki settings. */\nexport type IWikiConfig = Config['iwiki'];\n/** Knowledge settings. */\nexport type KnowledgeConfig = Config['knowledge'];\n/** Distill (knowledge distillation) settings. */\nexport type DistillConfig = Config['distill'];\n/** Coordination (multi-node claiming) settings. */\nexport type CoordinationConfig = Config['coordination'];\n/** Sync (generated files sync to project) settings. */\nexport type SyncConfig = Config['sync'];\n/** Verify-fix loop settings. */\nexport type VerifyFixLoopConfig = Config['verifyFixLoop'];\n/** Release (发布) settings. */\nexport type ReleaseConfig = Config['release'];\n/** Analytics (operational analysis) settings. */\nexport type AnalyticsConfig = Config['analytics'];\n/** Workspace (multi-repo) settings. */\nexport type WorkspaceEnvConfig = Config['workspace'];\n/** Tenants (multi-tenant) settings. */\nexport type TenantsEnvConfig = Config['tenants'];\n\n// ---------------------------------------------------------------------------\n// ConfigValidationError\n// ---------------------------------------------------------------------------\n\nexport class ConfigValidationError extends Error {\n public readonly issues: z.ZodIssue[];\n\n constructor(zodError: z.ZodError) {\n const lines = zodError.issues.map((issue) => {\n const envVar = issue.path.length > 0 ? issue.path.join('.') : '(root)';\n return ` - ${envVar}: ${issue.message}`;\n });\n super(\n `Configuration validation failed:\\n${lines.join('\\n')}`,\n );\n this.name = 'ConfigValidationError';\n this.issues = zodError.issues;\n }\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,UAAU,kBAAkB;AACrC,OAAOA,WAAU;AACjB,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,SAAS,qBAAqB;;;ACK9B,SAAS,SAAS;AAGlB,OAAO,UAAU;AAOjB,SAAS,WAAW,eAAuB,SAAS;AAClD,SAAO,EACJ,OAAO,EACP,SAAS,EACT,QAAQ,YAAY,EACpB,UAAU,CAAC,MAAM,MAAM,MAAM;AAClC;AAGA,SAAS,OAAO,cAAsB,MAAuC;AAC3E,MAAI,SAAS,EAAE,OAAO,OAAO,EAAE,IAAI;AACnC,MAAI,MAAM,QAAQ,OAAW,UAAS,OAAO,IAAI,KAAK,GAAG;AACzD,MAAI,MAAM,QAAQ,OAAW,UAAS,OAAO,IAAI,KAAK,GAAG;AACzD,SAAO,OAAO,SAAS,EAAE,QAAQ,OAAO,YAAY,CAAC;AACvD;AAGA,SAAS,QAAQ,cAAsB;AACrC,SAAO,EAAE,OACN,OAAO,EACP,IAAI,EACJ,IAAI,GAAG,mBAAmB,EAC1B,IAAI,OAAO,uBAAuB,EAClC,SAAS,EACT,QAAQ,OAAO,YAAY,CAAC;AACjC;AAGA,SAAS,MAAM,cAAsB;AACnC,SAAO,EAAE,OACN,OAAO,EACP,IAAI,EACJ,IAAI,KAAM,4BAA4B,EACtC,SAAS,EACT,QAAQ,OAAO,YAAY,CAAC;AACjC;AAGA,SAAS,oBAAoB,KAAqC;AAChE,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,QAAM,SAAiC,CAAC;AACxC,aAAW,QAAQ,IAAI,MAAM,GAAG,GAAG;AACjC,UAAM,CAAC,OAAO,KAAK,IAAI,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAC1D,QAAI,SAAS,MAAO,QAAO,KAAK,IAAI;AAAA,EACtC;AACA,SAAO;AACT;AAMO,IAAM,YAAY,EAAE,OAAO;AAAA;AAAA,EAEhC,kBAAkB,EAAE,OAAO,EAAE,IAAI,sCAAsC;AAAA,EACvE,wBAAwB,EAAE,OAAO,EAAE,IAAI,GAAG,oCAAoC;AAAA,EAC9E,uBAAuB,EAAE,OAAO,EAAE,IAAI,GAAG,mCAAmC;AAAA,EAC5E,kBAAkB,EAAE,OAAO,EAAE,IAAI,GAAG,8BAA8B;AAAA;AAAA,EAGlE,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAG9B,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,QAAQ;AAAA,EACnD,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,YAAY;AAAA,EACzD,mBAAmB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE;AAAA,EACnD,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE;AAAA;AAAA,EAGhD,gBAAgB,EACb,OAAO,EACP,SAAS,EACT,QAAQ,iBAAiB;AAAA,EAC5B,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,iBAAiB;AAAA,EACzD,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,kBAAkB,EAAE,OAAO,EAAE,SAAS;AAAA,EACtC,4BAA4B,WAAW,MAAM;AAAA,EAC7C,yBAAyB,MAAM,SAAS;AAAA,EACxC,oBAAoB,EAAE,OACnB,OAAO,EACP,IAAI,EACJ,IAAI,GAAG,gDAAgD,EACvD,SAAS,EACT,QAAQ,IAAO;AAAA,EAClB,kBAAkB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,IAAI;AAAA;AAAA,EAGpD,eAAe,EACZ,OAAO,EACP,SAAS,EACT,QAAQ,MAAM;AAAA;AAAA,EAGjB,4BAA4B,MAAM,OAAO;AAAA,EACzC,wBAAwB,MAAM,OAAO;AAAA,EACrC,aAAa,OAAO,KAAK,EAAE,KAAK,GAAG,KAAK,IAAI,CAAC;AAAA,EAC7C,uBAAuB,OAAO,KAAK,EAAE,KAAK,EAAE,CAAC;AAAA;AAAA,EAG7C,gBAAgB,WAAW,MAAM;AAAA,EACjC,4BAA4B,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE;AAAA;AAAA,EAG5D,aAAa,WAAW,MAAM;AAAA,EAC9B,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,SAAS;AAAA,EACjD,UAAU,QAAQ,MAAM;AAAA,EACxB,mBAAmB,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAGvC,yBAAyB,WAAW,MAAM;AAAA,EAC1C,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAGlC,iBAAiB,WAAW,OAAO;AAAA,EACnC,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,SAAS;AAAA,EACrD,cAAc,QAAQ,MAAM;AAAA,EAC5B,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE;AAAA,EAChD,sBAAsB,WAAW,MAAM;AAAA,EACvC,oBAAoB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,iBAAiB;AAAA;AAAA,EAGnE,gBAAgB,WAAW,OAAO;AAAA,EAClC,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,wBAAwB;AAAA,EACpE,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,uBAAuB;AAAA,EACtE,kBAAkB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,IAAI;AAAA,EACpD,uBAAuB,QAAQ,MAAM;AAAA,EACrC,wBAAwB,QAAQ,MAAM;AAAA,EACtC,oBAAoB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE;AAAA,EACpD,qBAAqB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE;AAAA,EACrD,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,SAAS;AAAA,EACvD,oBAAoB,EAAE,OAAO,EAAE,SAAS;AAAA,EACxC,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAGlC,iBAAiB,WAAW,OAAO;AAAA,EACnC,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE;AAAA,EAC9C,gBAAgB,MAAM,OAAO,KAAK,KAAK,KAAK,GAAI,CAAC;AAAA,EACjD,6BAA6B,WAAW,OAAO;AAAA,EAC/C,0BAA0B,MAAM,QAAQ;AAAA;AAAA,EAGxC,oBAAoB,WAAW,OAAO;AAAA,EACtC,uBAAuB,OAAO,KAAK,EAAE,KAAK,GAAG,KAAK,GAAG,CAAC;AAAA,EACtD,uBAAuB,MAAM,QAAQ;AAAA,EACrC,2BAA2B,EACxB,OAAO,EACP,SAAS;AAAA,EACZ,6BAA6B,EAAE,OAAO,EAAE,SAAS;AAAA,EACjD,4BAA4B,EAAE,OAAO,EAAE,SAAS;AAAA,EAChD,0BAA0B,EACvB,OAAO,EACP,SAAS;AAAA,EACZ,4BAA4B,EAAE,OAAO,EAAE,SAAS;AAAA,EAChD,2BAA2B,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAG/C,cAAc,WAAW,OAAO;AAAA,EAChC,iBAAiB,MAAM,QAAQ;AAAA,EAC/B,2BAA2B,OAAO,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,EACnD,iBAAiB,EACd,OAAO,EACP,SAAS;AAAA,EACZ,mBAAmB,EAAE,OAAO,EAAE,SAAS;AAAA,EACvC,kBAAkB,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAGtC,mBAAmB,WAAW,OAAO;AAAA,EACrC,0BAA0B,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAClE,4BAA4B,MAAM,QAAQ;AAAA,EAC1C,2BAA2B,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,GAAI,EAAE,SAAS;AAAA,EACtE,iCAAiC,OAAO,MAAM,EAAE,KAAK,EAAE,CAAC;AAAA,EACxD,qBAAqB,WAAW,OAAO;AAAA;AAAA,EAGvC,qBAAqB,WAAW,MAAM;AAAA,EACtC,yBAAyB,MAAM,QAAQ;AAAA,EACvC,sBAAsB,EACnB,OAAO,EACP,IAAI,0CAA0C,EAC9C,SAAS,EACT,QAAQ,4BAA4B;AAAA,EACvC,8BAA8B,MAAM,QAAQ;AAAA;AAAA,EAG5C,mBAAmB,EAAE,OAAO,EAAE,SAAS;AAAA,EACvC,kBAAkB,EAAE,OAAO,EAAE,SAAS;AAAA,EACtC,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAGpC,QAAQ,EAAE,KAAK,CAAC,SAAS,IAAI,CAAC,EAAE,SAAS,EAAE,QAAQ,OAAO;AAAA;AAAA,EAG1D,mBAAmB,WAAW,OAAO;AAAA,EACrC,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,EACpC,yBAAyB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,eAAe;AAAA,EACtE,wBAAwB,WAAW,OAAO;AAAA,EAC1C,wBAAwB,WAAW,MAAM;AAAA;AAAA,EAGzC,iBAAiB,WAAW,OAAO;AAAA,EACnC,qBAAqB,MAAM,UAAU;AAAA,EACrC,yBAAyB,WAAW,OAAO;AAAA,EAC3C,iCAAiC,OAAO,KAAK,EAAE,KAAK,EAAE,CAAC;AAAA,EACvD,qCAAqC,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,QAAQ,GAAG;AAAA,EAC3F,wBAAwB,WAAW,MAAM;AAAA;AAAA,EAGzC,2BAA2B,WAAW,OAAO;AAAA,EAC7C,uBAAuB,WAAW,OAAO;AAAA;AAAA,EAGzC,iBAAiB,WAAW,OAAO;AAAA,EACnC,6BAA6B,MAAM,WAAW;AAAA;AAAA,EAG9C,yBAAyB,WAAW,MAAM;AAAA,EAC1C,2BAA2B,OAAO,KAAK,EAAE,KAAK,GAAG,KAAK,GAAG,CAAC;AAAA,EAC1D,+BAA+B,WAAW,MAAM;AAAA;AAAA,EAGhD,kBAAkB,WAAW,OAAO;AAAA,EACpC,0BAA0B,MAAM,SAAS;AAAA,EACzC,uBAAuB,OAAO,KAAK,EAAE,KAAK,EAAE,CAAC;AAAA;AAAA,EAG7C,oBAAoB,MAAM,OAAO;AAAA;AAAA,EAEjC,mBAAmB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,iBAAiB;AAAA;AAAA,EAElE,kBAAkB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE;AAAA;AAAA,EAGlD,mBAAmB,WAAW,OAAO;AAAA,EACrC,0BAA0B,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACpD,4BAA4B,EAAE,OAAO,EAAE,SAAS;AAAA,EAChD,wBAAwB,MAAM,QAAQ;AAAA,EACtC,+BAA+B,WAAW,MAAM;AAAA;AAAA,EAGhD,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAG7B,uBAAuB,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAG3C,qBAAqB,EAAE,OAAO,EAAE,SAAS;AAC3C,CAAC;AAaM,SAAS,iBACd,KACoC;AACpC,QAAM,OAAO,OAAO,KAAK,UAAU,KAAK;AACxC,QAAM,SAA6C,CAAC;AACpD,aAAW,OAAO,MAAM;AACtB,UAAM,MAAM,IAAI,GAAa;AAC7B,WAAO,GAAa,IAAI,OAAO;AAAA,EACjC;AACA,SAAO;AACT;AAQA,SAAS,gBACP,MACA,KACQ;AACR,QAAM,SAAS,gBAAgB,IAAI;AACnC,MAAI,QAAQ;AACV,UAAM,SAAU,IAAgC,MAAM;AACtD,QAAI,OAAO,WAAW,YAAY,OAAQ,QAAO;AAAA,EACnD;AACA,SAAO,iBAAiB,IAAI;AAC9B;AAWA,SAAS,qBACP,MACA,KACuB;AACvB,QAAM,YACH,SAAS,cAAc,IAAI,4BAA4B,IAAI,6BAC5D,IAAI;AACN,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SACG,SAAS,cAAc,IAAI,8BAA8B,IAAI,+BAC9D,gBAAgB,UAAU,GAAG;AAAA,IAC/B,gBAAgB,IAAI;AAAA,IACpB,QACG,SAAS,cAAc,IAAI,6BAA6B,IAAI,8BAC7D,IAAI;AAAA,EACR;AACF;AAEA,SAAS,eAAe,KAAiC;AAGvD,QAAM,WAAW,IAAI,mBAAmB;AACxC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,IAAI,qBAAqB,gBAAgB,UAAU,GAAG;AAAA,IAC9D,gBAAgB,IAAI;AAAA,IACpB,OAAO,IAAI,oBAAoB,IAAI;AAAA,EACrC;AACF;AAMO,SAAS,qBAAqB,KAAgB,SAAiB;AACpE,QAAM,SAAS,IAAI;AACnB,QAAM,UAAU,IAAI;AAEpB,QAAM,aAAa,IAAI,gBAAgB,IAAI;AAE3C,SAAO;AAAA,IACL,UAAU;AAAA,MACR,QAAQ,IAAI;AAAA,MACZ,cAAc,IAAI;AAAA,MAClB,aAAa,IAAI;AAAA,IACnB;AAAA,IACA,SAAS;AAAA,MACP,SAAS,IAAI;AAAA,MACb;AAAA,MACA,YAAY,IAAI;AAAA,MAChB,cAAc,IAAI;AAAA,MAClB,iBAAiB,IAAI;AAAA,MACrB,eAAe,IAAI;AAAA,IACrB;AAAA,IACA,IAAI;AAAA,MACF,MAAM;AAAA,MACN,QAAQ,gBAAgB,QAAQ,GAAG;AAAA,MACnC,gBAAgB,IAAI;AAAA,MACpB,eAAe,IAAI,sBAAsB;AAAA,MACzC,gBAAgB,IAAI;AAAA,MACpB,OAAO,IAAI;AAAA,MACX,yBAAyB,IAAI;AAAA,IAC/B;AAAA,IACA,MAAM;AAAA,MACJ,qBAAqB,IAAI;AAAA,MACzB,iBAAiB,IAAI;AAAA,MACrB,YAAY,IAAI;AAAA,MAChB,eAAe,IAAI;AAAA,IACrB;AAAA,IACA,UAAU;AAAA,MACR,MAAM,IAAI;AAAA,IACZ;AAAA,IACA,QAAQ;AAAA,MACN,SAAS,IAAI;AAAA,MACb,mBAAmB,IAAI,2BACpB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AAAA,IACnB;AAAA,IACA,KAAK;AAAA,MACH,SAAS,IAAI;AAAA,MACb,MAAM,IAAI;AAAA,MACV,MAAM;AAAA,MACN,iBACE,IAAI,qBACJ,KAAK,QAAQ,SAAS,MAAM,uBAAuB;AAAA,IACvD;AAAA,IACA,eAAe;AAAA,MACb,SAAS,IAAI;AAAA,MACb,YAAY,IAAI,gBAAgB,UAAU,WAAW,CAAC,IAAI,OAAO;AAAA,IACnE;AAAA,IACA,SAAS;AAAA,MACP,SAAS,IAAI;AAAA,MACb,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,QAAQ,IAAI;AAAA,MACZ,aAAa,IAAI;AAAA,MACjB,WAAW,IAAI;AAAA,IACjB;AAAA,IACA,KAAK;AAAA,MACH,SAAS,IAAI;AAAA,MACb,SAAS,IAAI;AAAA,MACb,YAAY,IAAI;AAAA,MAChB,aAAa,IAAI;AAAA,MACjB,iBAAiB,IAAI;AAAA,MACrB,kBAAkB,IAAI;AAAA,MACtB,cAAc,IAAI;AAAA,MAClB,eAAe,IAAI;AAAA,MACnB,WAAW,IAAI;AAAA,MACf,cAAc,IAAI,sBAAsB,IAAI;AAAA,MAC5C,SAAS,IAAI;AAAA,IACf;AAAA,IACA,SAAS;AAAA,MACP,SAAS,IAAI;AAAA,MACb,MAAM,IAAI;AAAA,MACV,OAAO,IAAI;AAAA,MACX,mBAAmB,IAAI;AAAA,MACvB,gBAAgB,IAAI;AAAA,IACtB;AAAA,IACA,YAAY;AAAA,MACV,SAAS,IAAI;AAAA,MACb,qBAAqB,IAAI;AAAA,MACzB,WAAW,IAAI;AAAA,MACf,WAAW,qBAAqB,aAAa,GAAG;AAAA,MAChD,UAAU,qBAAqB,YAAY,GAAG;AAAA,IAChD;AAAA,IACA,MAAM;AAAA,MACJ,SAAS,IAAI;AAAA,MACb,WAAW,IAAI;AAAA,MACf,oBAAoB,IAAI;AAAA,MACxB,OAAO,eAAe,GAAG;AAAA,IAC3B;AAAA,IACA,WAAW;AAAA,MACT,SAAS,IAAI;AAAA,MACb,eAAe,IAAI,4BAA4B,IAAI;AAAA,MACnD,gBAAgB,IAAI;AAAA,MACpB,eAAe,IAAI,6BAA6B,IAAI;AAAA,MACpD,qBAAqB,IAAI;AAAA,MACzB,UAAU,IAAI;AAAA,IAChB;AAAA,IACA,YAAY;AAAA,MACV,SAAS,IAAI;AAAA,MACb,YAAY,IAAI;AAAA,MAChB,UAAU,IAAI;AAAA,MACd,gBAAgB,IAAI;AAAA,IACtB;AAAA,IACA,OAAO;AAAA,MACL,YAAY,IAAI;AAAA,MAChB,WAAW,IAAI;AAAA,MACf,SAAS,IAAI;AAAA,IACf;AAAA,IACA,QAAQ,IAAI;AAAA,IACZ,WAAW;AAAA,MACT,SAAS,IAAI;AAAA,MACb,MAAM,IAAI;AAAA,MACV,cAAc,IAAI;AAAA,MAClB,aAAa,IAAI;AAAA,MACjB,aAAa,IAAI;AAAA,IACnB;AAAA,IACA,SAAS;AAAA,MACP,SAAS,IAAI;AAAA,MACb,YAAY,IAAI;AAAA,MAChB,gBAAgB,IAAI;AAAA,MACpB,sBAAsB,IAAI;AAAA,MAC1B,2BAA2B,IAAI;AAAA,MAC/B,eAAe,IAAI;AAAA,IACrB;AAAA,IACA,cAAc;AAAA,MACZ,QAAQ,IAAI;AAAA,IACd;AAAA,IACA,MAAM;AAAA,MACJ,oBAAoB,IAAI;AAAA,MACxB,gBAAgB,IAAI;AAAA,IACtB;AAAA,IACA,SAAS;AAAA,MACP,SAAS,IAAI;AAAA,MACb,kBAAkB,IAAI;AAAA,IACxB;AAAA,IACA,eAAe;AAAA,MACb,SAAS,IAAI;AAAA,MACb,eAAe,IAAI;AAAA,MACnB,sBAAsB,IAAI;AAAA,IAC5B;AAAA,IACA,WAAW;AAAA,MACT,SAAS,IAAI;AAAA,MACb,cAAc,IAAI;AAAA,MAClB,gBAAgB,IAAI;AAAA,MACpB,YAAY,IAAI;AAAA,MAChB,mBAAmB,IAAI;AAAA,IACzB;AAAA,IACA,UAAU;AAAA,MACR,SAAS,IAAI;AAAA,MACb,eAAe,IAAI;AAAA,MACnB,aAAa,IAAI;AAAA,IACnB;AAAA,IACA,KAAK;AAAA,MACH,cAAc,IAAI;AAAA,MAClB,cAAc,IAAI;AAAA,MAClB,aAAa,oBAAoB,IAAI,gBAAgB;AAAA,IACvD;AAAA,IACA,WAAW;AAAA,MACT,YAAY,IAAI;AAAA,IAClB;AAAA,IACA,SAAS;AAAA,MACP,YAAY,IAAI;AAAA,IAClB;AAAA,EACF;AACF;AA4EO,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAC/B;AAAA,EAEhB,YAAY,UAAsB;AAChC,UAAM,QAAQ,SAAS,OAAO,IAAI,CAAC,UAAU;AAC3C,YAAM,SAAS,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK,GAAG,IAAI;AAC9D,aAAO,OAAO,MAAM,KAAK,MAAM,OAAO;AAAA,IACxC,CAAC;AACD;AAAA,MACE;AAAA,EAAqC,MAAM,KAAK,IAAI,CAAC;AAAA,IACvD;AACA,SAAK,OAAO;AACZ,SAAK,SAAS,SAAS;AAAA,EACzB;AACF;;;ADhkBA,IAAM,YAAYC,MAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAmBtD,SAAS,sBAAsB,YAA6B;AACjE,MAAI,WAAY,QAAO;AACvB,MAAI,QAAQ,IAAI,gBAAiB,QAAO,QAAQ,IAAI,gBAAgB,QAAQ,cAAc,GAAG,QAAQ,CAAC;AAGtG,QAAM,WAAWA,MAAK,QAAQ,WAAW,SAAS;AAClD,MAAI,GAAG,WAAW,QAAQ,EAAG,QAAO;AAIpC,QAAM,YAAYA,MAAK,KAAK,aAAa,GAAG,MAAM;AAClD,MAAI,GAAG,WAAW,SAAS,EAAG,QAAO;AAGrC,QAAM,SAASA,MAAK,QAAQ,QAAQ,IAAI,GAAG,MAAM;AACjD,MAAI,GAAG,WAAW,MAAM,EAAG,QAAO;AAElC,SAAO;AACT;AAEA,IAAI,gBAAgB;AACpB,SAAS,qBAA2B;AAClC,MAAI,cAAe;AACnB,kBAAgB;AAChB,aAAW,EAAE,MAAM,sBAAsB,EAAE,CAAC;AAC9C;AAEO,SAAS,aAAa;AAC3B,qBAAmB;AAEnB,QAAM,SAAS,iBAAiB,QAAQ,GAAG;AAC3C,QAAM,SAAS,UAAU,UAAU,MAAM;AAEzC,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI,sBAAsB,OAAO,KAAK;AAAA,EAC9C;AAEA,SAAO,qBAAqB,OAAO,MAAM,SAAS;AACpD;AAKO,SAAS,mBAAyB;AACvC,kBAAgB;AAClB;AAOO,SAAS,eAAe;AAC7B,mBAAiB;AAEjB,QAAM,UAAU,iBAAiB,QAAQ,GAAG;AAC5C,aAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,WAAO,QAAQ,IAAI,GAAG;AAAA,EACxB;AACA,SAAO,WAAW;AACpB;","names":["path","path"]}
|