@xdevops/issue-auto-finish 1.0.68 → 1.0.70
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/{LockNote-G32LJFVG.js → LockNote-3ATNJJ5K.js} +3 -3
- package/dist/{ai-runner-DHH74B4O.js → ai-runner-PJYBQ6LO.js} +2 -2
- package/dist/{analyze-STQYS7K4.js → analyze-4MTNYZNK.js} +3 -3
- package/dist/{braindump-UOCBJGPF.js → braindump-MBIFTOSK.js} +6 -6
- package/dist/{chunk-VFTESEYG.js → chunk-E4Q6KZ23.js} +4 -3
- package/dist/chunk-E4Q6KZ23.js.map +1 -0
- package/dist/{chunk-YQ6EL6PW.js → chunk-ECAOTSHA.js} +2 -2
- package/dist/{chunk-4EGXVU5K.js → chunk-KZ6CKYWK.js} +254 -16
- package/dist/chunk-KZ6CKYWK.js.map +1 -0
- package/dist/{chunk-XCHQMDNZ.js → chunk-LD222Y4U.js} +5 -3
- package/dist/chunk-LD222Y4U.js.map +1 -0
- package/dist/{chunk-JUXD7ZBK.js → chunk-M22VPSJ2.js} +2 -2
- package/dist/{chunk-O5KLWME7.js → chunk-OPWGIRIO.js} +167 -36
- package/dist/chunk-OPWGIRIO.js.map +1 -0
- package/dist/{chunk-2QHSU5YR.js → chunk-QCVJKH52.js} +9 -1
- package/dist/chunk-QCVJKH52.js.map +1 -0
- package/dist/{chunk-Z75EDBWI.js → chunk-RCAARXN2.js} +3 -3
- package/dist/{chunk-TASSKSJ3.js → chunk-WHTYES3V.js} +9 -1
- package/dist/{chunk-TASSKSJ3.js.map → chunk-WHTYES3V.js.map} +1 -1
- package/dist/{chunk-TGVBX5CF.js → chunk-WVZQMM3R.js} +3 -2
- package/dist/chunk-WVZQMM3R.js.map +1 -0
- package/dist/{chunk-VJD6TIKY.js → chunk-ZBEFRFEW.js} +80 -7
- package/dist/chunk-ZBEFRFEW.js.map +1 -0
- package/dist/cli.js +8 -8
- package/dist/{config-SONIIB3D.js → config-OQ6ZBQSM.js} +3 -3
- package/dist/config-schema.d.ts +2 -0
- package/dist/config-schema.d.ts.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/coordination/LockNote.d.ts +1 -1
- package/dist/coordination/LockNote.d.ts.map +1 -1
- package/dist/deploy/PreviewReaper.d.ts +38 -0
- package/dist/deploy/PreviewReaper.d.ts.map +1 -0
- package/dist/deploy/index.d.ts +1 -0
- package/dist/deploy/index.d.ts.map +1 -1
- package/dist/{doctor-JMAZCEHC.js → doctor-7VTUPXAZ.js} +3 -3
- package/dist/e2e/E2ESetupRunner.d.ts +28 -0
- package/dist/e2e/E2ESetupRunner.d.ts.map +1 -0
- package/dist/events/EventBus.d.ts +1 -1
- package/dist/events/EventBus.d.ts.map +1 -1
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/zh-CN.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -10
- package/dist/{init-GL24QXK3.js → init-7TMZG72C.js} +6 -6
- package/dist/lib.js +5 -5
- package/dist/lifecycle/ActionLifecycleManager.d.ts.map +1 -1
- package/dist/orchestrator/PipelineOrchestrator.d.ts +3 -0
- package/dist/orchestrator/PipelineOrchestrator.d.ts.map +1 -1
- package/dist/{restart-L4W2TYQL.js → restart-AK2XV3AR.js} +4 -4
- package/dist/run.js +10 -10
- package/dist/{start-BHURMPBK.js → start-2OIZUMWY.js} +4 -4
- package/dist/tracker/ExecutableTask.d.ts.map +1 -1
- package/dist/tracker/IssueState.d.ts +2 -1
- package/dist/tracker/IssueState.d.ts.map +1 -1
- package/dist/web/WebServer.d.ts +1 -0
- package/dist/web/WebServer.d.ts.map +1 -1
- package/dist/web/routes/api.d.ts +2 -0
- package/dist/web/routes/api.d.ts.map +1 -1
- package/dist/web/routes/setup.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/web/frontend/dist/assets/index-BH2GGJHH.js +127 -0
- package/src/web/frontend/dist/assets/index-CPNbFsHB.css +1 -0
- package/src/web/frontend/dist/index.html +2 -2
- package/dist/chunk-2QHSU5YR.js.map +0 -1
- package/dist/chunk-4EGXVU5K.js.map +0 -1
- package/dist/chunk-O5KLWME7.js.map +0 -1
- package/dist/chunk-TGVBX5CF.js.map +0 -1
- package/dist/chunk-VFTESEYG.js.map +0 -1
- package/dist/chunk-VJD6TIKY.js.map +0 -1
- package/dist/chunk-XCHQMDNZ.js.map +0 -1
- package/src/web/frontend/dist/assets/index-BM-aPpBO.css +0 -1
- package/src/web/frontend/dist/assets/index-BYpsherD.js +0 -125
- /package/dist/{LockNote-G32LJFVG.js.map → LockNote-3ATNJJ5K.js.map} +0 -0
- /package/dist/{ai-runner-DHH74B4O.js.map → ai-runner-PJYBQ6LO.js.map} +0 -0
- /package/dist/{analyze-STQYS7K4.js.map → analyze-4MTNYZNK.js.map} +0 -0
- /package/dist/{braindump-UOCBJGPF.js.map → braindump-MBIFTOSK.js.map} +0 -0
- /package/dist/{chunk-YQ6EL6PW.js.map → chunk-ECAOTSHA.js.map} +0 -0
- /package/dist/{chunk-JUXD7ZBK.js.map → chunk-M22VPSJ2.js.map} +0 -0
- /package/dist/{chunk-Z75EDBWI.js.map → chunk-RCAARXN2.js.map} +0 -0
- /package/dist/{config-SONIIB3D.js.map → config-OQ6ZBQSM.js.map} +0 -0
- /package/dist/{doctor-JMAZCEHC.js.map → doctor-7VTUPXAZ.js.map} +0 -0
- /package/dist/{init-GL24QXK3.js.map → init-7TMZG72C.js.map} +0 -0
- /package/dist/{restart-L4W2TYQL.js.map → restart-AK2XV3AR.js.map} +0 -0
- /package/dist/{start-BHURMPBK.js.map → start-2OIZUMWY.js.map} +0 -0
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
findLockNote,
|
|
6
6
|
isLockStale,
|
|
7
7
|
parseLockNote
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-E4Q6KZ23.js";
|
|
9
|
+
import "./chunk-QCVJKH52.js";
|
|
10
10
|
export {
|
|
11
11
|
buildLockNoteBody,
|
|
12
12
|
buildReleaseNoteBody,
|
|
@@ -15,4 +15,4 @@ export {
|
|
|
15
15
|
isLockStale,
|
|
16
16
|
parseLockNote
|
|
17
17
|
};
|
|
18
|
-
//# sourceMappingURL=LockNote-
|
|
18
|
+
//# sourceMappingURL=LockNote-3ATNJJ5K.js.map
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
resolveModelForRunner,
|
|
16
16
|
resolveRunnerMode,
|
|
17
17
|
validateRunnerRegistry
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-WHTYES3V.js";
|
|
19
19
|
import "./chunk-GF2RRYHB.js";
|
|
20
20
|
export {
|
|
21
21
|
BaseAIRunner,
|
|
@@ -34,4 +34,4 @@ export {
|
|
|
34
34
|
resolveRunnerMode,
|
|
35
35
|
validateRunnerRegistry
|
|
36
36
|
};
|
|
37
|
-
//# sourceMappingURL=ai-runner-
|
|
37
|
+
//# sourceMappingURL=ai-runner-PJYBQ6LO.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadConfig
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LD222Y4U.js";
|
|
4
4
|
import {
|
|
5
5
|
analyze
|
|
6
6
|
} from "./chunk-B7TVVODN.js";
|
|
@@ -11,7 +11,7 @@ import "./chunk-DAX3FD2O.js";
|
|
|
11
11
|
import "./chunk-DADQSKPL.js";
|
|
12
12
|
import {
|
|
13
13
|
createAIRunner
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-WHTYES3V.js";
|
|
15
15
|
import "./chunk-GF2RRYHB.js";
|
|
16
16
|
|
|
17
17
|
// src/cli/commands/analyze.ts
|
|
@@ -72,4 +72,4 @@ async function analyzeCommand(opts) {
|
|
|
72
72
|
export {
|
|
73
73
|
analyzeCommand
|
|
74
74
|
};
|
|
75
|
-
//# sourceMappingURL=analyze-
|
|
75
|
+
//# sourceMappingURL=analyze-4MTNYZNK.js.map
|
|
@@ -9,23 +9,23 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
BraindumpOrchestrator,
|
|
11
11
|
BraindumpTracker
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-RCAARXN2.js";
|
|
13
13
|
import {
|
|
14
14
|
AsyncMutex,
|
|
15
15
|
GitOperations
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-WVZQMM3R.js";
|
|
17
17
|
import "./chunk-ACVOOHAR.js";
|
|
18
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-QCVJKH52.js";
|
|
19
19
|
import {
|
|
20
20
|
loadConfig
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-LD222Y4U.js";
|
|
22
22
|
import "./chunk-B7TVVODN.js";
|
|
23
23
|
import "./chunk-TN2SYADO.js";
|
|
24
24
|
import "./chunk-DAX3FD2O.js";
|
|
25
25
|
import "./chunk-DADQSKPL.js";
|
|
26
26
|
import {
|
|
27
27
|
createAIRunner
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-WHTYES3V.js";
|
|
29
29
|
import "./chunk-GF2RRYHB.js";
|
|
30
30
|
|
|
31
31
|
// src/cli/commands/braindump.ts
|
|
@@ -204,4 +204,4 @@ ${bold("\u786E\u8BA4\u6267\u884C\uFF1F")} (${green("y")}=\u786E\u8BA4 / ${red("q
|
|
|
204
204
|
export {
|
|
205
205
|
braindumpCommand
|
|
206
206
|
};
|
|
207
|
-
//# sourceMappingURL=braindump-
|
|
207
|
+
//# sourceMappingURL=braindump-MBIFTOSK.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
t
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-QCVJKH52.js";
|
|
4
4
|
|
|
5
5
|
// src/coordination/LockNote.ts
|
|
6
6
|
var LOCK_NOTE_REGEX = /<!-- iaf-lock:(\{.*?\}) -->/;
|
|
@@ -15,7 +15,8 @@ function buildReleaseNoteBody(reason) {
|
|
|
15
15
|
completed: t("lock.completedMessage"),
|
|
16
16
|
failed: t("lock.failedMessage"),
|
|
17
17
|
cancelled: t("lock.cancelledMessage"),
|
|
18
|
-
"crash-recovery": t("lock.crashRecoveryMessage")
|
|
18
|
+
"crash-recovery": t("lock.crashRecoveryMessage"),
|
|
19
|
+
deployed: t("lock.deployedMessage")
|
|
19
20
|
};
|
|
20
21
|
return `${messages[reason]}
|
|
21
22
|
|
|
@@ -65,4 +66,4 @@ export {
|
|
|
65
66
|
findAllLockNotes,
|
|
66
67
|
isLockStale
|
|
67
68
|
};
|
|
68
|
-
//# sourceMappingURL=chunk-
|
|
69
|
+
//# sourceMappingURL=chunk-E4Q6KZ23.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/coordination/LockNote.ts"],"sourcesContent":["import type { GongfengNote } from '../clients/GongfengClient.js';\nimport { t } from '../i18n/index.js';\n\n/**\n * 锁评论协议。\n *\n * 锁评论格式(嵌入在可见内容中的 HTML 注释):\n * ```\n * 🚀 **自动处理开始**\n *\n * 已检测到 `auto-finish` 标签,开始自动分析和实施。\n *\n * <!-- iaf-lock:{\"nodeId\":\"devbox-a3f1c902\",\"ts\":1711100000000} -->\n * ```\n *\n * 向后兼容旧格式(纯 HTML 注释,无可见内容)。\n * 不带 AGENT_NOTE_MARKER,不会被 cleanupAgentNotes 清理。\n */\n\nconst LOCK_NOTE_REGEX = /<!-- iaf-lock:(\\{.*?\\}) -->/;\n\nexport interface LockPayload {\n nodeId: string;\n ts: number;\n phase?: string;\n}\n\nexport interface LockNoteInfo {\n noteId: number;\n payload: LockPayload;\n}\n\n/** 释放锁时的原因 */\nexport type ReleaseReason = 'completed' | 'failed' | 'cancelled' | 'crash-recovery' | 'deployed';\n\n/** 构建锁评论正文,可附带可见消息 */\nexport function buildLockNoteBody(payload: LockPayload, message?: string): string {\n const lockTag = `<!-- iaf-lock:${JSON.stringify(payload)} -->`;\n return message ? `${message}\\n\\n${lockTag}` : lockTag;\n}\n\n/** 构建释放后的评论正文(可见状态 + 释放标记) */\nexport function buildReleaseNoteBody(reason: ReleaseReason): string {\n const messages: Record<ReleaseReason, string> = {\n completed: t('lock.completedMessage'),\n failed: t('lock.failedMessage'),\n cancelled: t('lock.cancelledMessage'),\n 'crash-recovery': t('lock.crashRecoveryMessage'),\n deployed: t('lock.deployedMessage'),\n };\n return `${messages[reason]}\\n\\n<!-- iaf-lock-released -->`;\n}\n\n/** 从评论正文中解析锁载荷,非锁评论返回 null。支持嵌入在可见内容中的锁标记。 */\nexport function parseLockNote(body: string): LockPayload | null {\n const match = body.match(LOCK_NOTE_REGEX);\n if (!match) return null;\n try {\n const payload = JSON.parse(match[1]) as LockPayload;\n if (typeof payload.nodeId !== 'string' || typeof payload.ts !== 'number') {\n return null;\n }\n return payload;\n } catch {\n return null;\n }\n}\n\n/** 在评论列表中查找第一条锁评论 */\nexport function findLockNote(notes: GongfengNote[]): LockNoteInfo | null {\n for (const note of notes) {\n const payload = parseLockNote(note.body);\n if (payload) {\n return { noteId: note.id, payload };\n }\n }\n return null;\n}\n\n/** 在评论列表中查找所有锁评论 */\nexport function findAllLockNotes(notes: GongfengNote[]): LockNoteInfo[] {\n const result: LockNoteInfo[] = [];\n for (const note of notes) {\n const payload = parseLockNote(note.body);\n if (payload) {\n result.push({ noteId: note.id, payload });\n }\n }\n return result;\n}\n\n/** 判断锁评论是否过期 */\nexport function isLockStale(payload: LockPayload, staleMs: number): boolean {\n return Date.now() - payload.ts > staleMs;\n}\n"],"mappings":";;;;;AAmBA,IAAM,kBAAkB;AAiBjB,SAAS,kBAAkB,SAAsB,SAA0B;AAChF,QAAM,UAAU,iBAAiB,KAAK,UAAU,OAAO,CAAC;AACxD,SAAO,UAAU,GAAG,OAAO;AAAA;AAAA,EAAO,OAAO,KAAK;AAChD;AAGO,SAAS,qBAAqB,QAA+B;AAClE,QAAM,WAA0C;AAAA,IAC9C,WAAW,EAAE,uBAAuB;AAAA,IACpC,QAAQ,EAAE,oBAAoB;AAAA,IAC9B,WAAW,EAAE,uBAAuB;AAAA,IACpC,kBAAkB,EAAE,2BAA2B;AAAA,IAC/C,UAAU,EAAE,sBAAsB;AAAA,EACpC;AACA,SAAO,GAAG,SAAS,MAAM,CAAC;AAAA;AAAA;AAC5B;AAGO,SAAS,cAAc,MAAkC;AAC9D,QAAM,QAAQ,KAAK,MAAM,eAAe;AACxC,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACF,UAAM,UAAU,KAAK,MAAM,MAAM,CAAC,CAAC;AACnC,QAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,QAAQ,OAAO,UAAU;AACxE,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,aAAa,OAA4C;AACvE,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,cAAc,KAAK,IAAI;AACvC,QAAI,SAAS;AACX,aAAO,EAAE,QAAQ,KAAK,IAAI,QAAQ;AAAA,IACpC;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,iBAAiB,OAAuC;AACtE,QAAM,SAAyB,CAAC;AAChC,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,cAAc,KAAK,IAAI;AACvC,QAAI,SAAS;AACX,aAAO,KAAK,EAAE,QAAQ,KAAK,IAAI,QAAQ,CAAC;AAAA,IAC1C;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,YAAY,SAAsB,SAA0B;AAC1E,SAAO,KAAK,IAAI,IAAI,QAAQ,KAAK;AACnC;","names":[]}
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-GXRFUJTN.js";
|
|
6
6
|
import {
|
|
7
7
|
resolveConfigFilePath
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LD222Y4U.js";
|
|
9
9
|
import {
|
|
10
10
|
ensureDir,
|
|
11
11
|
resolveLogsDir
|
|
@@ -89,4 +89,4 @@ async function startDaemon(configPath) {
|
|
|
89
89
|
export {
|
|
90
90
|
startCommand
|
|
91
91
|
};
|
|
92
|
-
//# sourceMappingURL=chunk-
|
|
92
|
+
//# sourceMappingURL=chunk-ECAOTSHA.js.map
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DependencyChecker
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-M22VPSJ2.js";
|
|
4
4
|
import {
|
|
5
5
|
getProjectKnowledge
|
|
6
6
|
} from "./chunk-ACVOOHAR.js";
|
|
7
7
|
import {
|
|
8
8
|
getLocalIP,
|
|
9
9
|
resolveConfigFilePath
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-LD222Y4U.js";
|
|
11
11
|
import {
|
|
12
12
|
analyze,
|
|
13
13
|
collectStaticInfo
|
|
@@ -19,8 +19,8 @@ import {
|
|
|
19
19
|
|
|
20
20
|
// src/web/routes/setup.ts
|
|
21
21
|
import express from "express";
|
|
22
|
-
import
|
|
23
|
-
import
|
|
22
|
+
import fs3 from "fs";
|
|
23
|
+
import path3 from "path";
|
|
24
24
|
|
|
25
25
|
// src/cli/setup/ConfigGenerator.ts
|
|
26
26
|
import fs from "fs";
|
|
@@ -278,6 +278,225 @@ var ConfigGenerator = class _ConfigGenerator {
|
|
|
278
278
|
}
|
|
279
279
|
};
|
|
280
280
|
|
|
281
|
+
// src/e2e/E2ESetupRunner.ts
|
|
282
|
+
import { spawn } from "child_process";
|
|
283
|
+
import fs2 from "fs";
|
|
284
|
+
import path2 from "path";
|
|
285
|
+
var E2ESetupRunner = class {
|
|
286
|
+
constructor(projectRoot) {
|
|
287
|
+
this.projectRoot = projectRoot;
|
|
288
|
+
this.vendorDir = path2.join(projectRoot, "vendor", "oa_pc_uat");
|
|
289
|
+
}
|
|
290
|
+
vendorDir;
|
|
291
|
+
repoUrl = "git@git.woa.com:fishcui/oa_pc_uat.git";
|
|
292
|
+
pythonCandidates = [
|
|
293
|
+
"python3.12",
|
|
294
|
+
"python3.11",
|
|
295
|
+
"python3.10",
|
|
296
|
+
"python3.9",
|
|
297
|
+
"python3.8",
|
|
298
|
+
"python3"
|
|
299
|
+
];
|
|
300
|
+
async *run() {
|
|
301
|
+
yield* this.cloneOrUpdate();
|
|
302
|
+
const pythonBin = yield* this.detectPython();
|
|
303
|
+
if (!pythonBin) return;
|
|
304
|
+
yield* this.installPlaywright(pythonBin);
|
|
305
|
+
yield* this.installChromium(pythonBin);
|
|
306
|
+
yield* this.configure(pythonBin);
|
|
307
|
+
yield* this.reloadConfig();
|
|
308
|
+
}
|
|
309
|
+
async *cloneOrUpdate() {
|
|
310
|
+
const step = "clone";
|
|
311
|
+
yield { step, status: "running", message: "Preparing vendor/oa_pc_uat..." };
|
|
312
|
+
const vendorParent = path2.dirname(this.vendorDir);
|
|
313
|
+
if (!fs2.existsSync(vendorParent)) {
|
|
314
|
+
fs2.mkdirSync(vendorParent, { recursive: true });
|
|
315
|
+
}
|
|
316
|
+
if (fs2.existsSync(path2.join(this.vendorDir, "main.py"))) {
|
|
317
|
+
yield { step, log: "vendor/oa_pc_uat exists, running git pull..." };
|
|
318
|
+
const result = yield* this.runProcess(step, "git", ["-C", this.vendorDir, "pull", "--ff-only"], 6e4);
|
|
319
|
+
if (!result.ok) {
|
|
320
|
+
yield { step, status: "failed", message: `git pull failed (exit ${result.exitCode})` };
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
yield { step, log: `Cloning ${this.repoUrl}...` };
|
|
325
|
+
const result = yield* this.runProcess(step, "git", ["clone", this.repoUrl, this.vendorDir], 12e4);
|
|
326
|
+
if (!result.ok) {
|
|
327
|
+
yield { step, status: "failed", message: `git clone failed (exit ${result.exitCode})` };
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (!fs2.existsSync(path2.join(this.vendorDir, "main.py"))) {
|
|
332
|
+
yield { step, status: "failed", message: "vendor/oa_pc_uat/main.py not found after clone" };
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
yield { step, status: "done", message: "vendor/oa_pc_uat ready" };
|
|
336
|
+
}
|
|
337
|
+
async *detectPython() {
|
|
338
|
+
const step = "detect-python";
|
|
339
|
+
yield { step, status: "running", message: "Detecting Python 3.8+..." };
|
|
340
|
+
const { execFileSync } = await import("child_process");
|
|
341
|
+
for (const candidate of this.pythonCandidates) {
|
|
342
|
+
try {
|
|
343
|
+
const output = execFileSync(candidate, ["--version"], {
|
|
344
|
+
encoding: "utf-8",
|
|
345
|
+
timeout: 5e3
|
|
346
|
+
}).trim();
|
|
347
|
+
yield { step, log: `${candidate}: ${output}` };
|
|
348
|
+
const match = output.match(/Python\s+(\d+)\.(\d+)/);
|
|
349
|
+
if (match) {
|
|
350
|
+
const major = parseInt(match[1], 10);
|
|
351
|
+
const minor = parseInt(match[2], 10);
|
|
352
|
+
if (major === 3 && minor >= 8) {
|
|
353
|
+
yield { step, status: "done", message: `Using ${candidate} (${output})` };
|
|
354
|
+
return candidate;
|
|
355
|
+
}
|
|
356
|
+
yield { step, log: `${candidate} version too old (need >= 3.8)` };
|
|
357
|
+
}
|
|
358
|
+
} catch {
|
|
359
|
+
yield { step, log: `${candidate}: not found` };
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
yield { step, status: "failed", message: "No Python 3.8+ found" };
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
async *installPlaywright(pythonBin) {
|
|
366
|
+
const step = "install-playwright";
|
|
367
|
+
yield { step, status: "running", message: "Installing playwright==1.48.0..." };
|
|
368
|
+
const result = yield* this.runProcess(
|
|
369
|
+
step,
|
|
370
|
+
pythonBin,
|
|
371
|
+
["-m", "pip", "install", "playwright==1.48.0", "--user", "-i", "https://pypi.org/simple/"],
|
|
372
|
+
3e5
|
|
373
|
+
);
|
|
374
|
+
if (!result.ok) {
|
|
375
|
+
yield { step, status: "failed", message: `pip install failed (exit ${result.exitCode})` };
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
const { execFileSync } = await import("child_process");
|
|
380
|
+
execFileSync(pythonBin, ["-c", "import playwright"], { encoding: "utf-8", timeout: 1e4 });
|
|
381
|
+
yield { step, status: "done", message: "playwright installed" };
|
|
382
|
+
} catch {
|
|
383
|
+
yield { step, status: "failed", message: "playwright import check failed after install" };
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
async *installChromium(pythonBin) {
|
|
387
|
+
const step = "install-chromium";
|
|
388
|
+
yield { step, status: "running", message: "Installing Chromium browser..." };
|
|
389
|
+
const result = yield* this.runProcess(
|
|
390
|
+
step,
|
|
391
|
+
pythonBin,
|
|
392
|
+
["-m", "playwright", "install", "chromium"],
|
|
393
|
+
3e5
|
|
394
|
+
);
|
|
395
|
+
if (!result.ok) {
|
|
396
|
+
yield { step, status: "failed", message: `playwright install chromium failed (exit ${result.exitCode})` };
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
yield { step, status: "done", message: "Chromium installed" };
|
|
400
|
+
}
|
|
401
|
+
async *configure(pythonBin) {
|
|
402
|
+
const step = "configure";
|
|
403
|
+
yield { step, status: "running", message: "Updating .env configuration..." };
|
|
404
|
+
try {
|
|
405
|
+
const current = ConfigGenerator.readCurrent();
|
|
406
|
+
current.e2e = {
|
|
407
|
+
uatVendorDir: this.vendorDir,
|
|
408
|
+
pythonBin
|
|
409
|
+
};
|
|
410
|
+
const configPath = ConfigGenerator.mergeWrite(current);
|
|
411
|
+
yield { step, log: `Updated ${configPath}` };
|
|
412
|
+
this.patchEnvFlag(configPath, "E2E_UI_ENABLED", "true");
|
|
413
|
+
yield { step, log: "Set E2E_UI_ENABLED=true" };
|
|
414
|
+
yield {
|
|
415
|
+
step,
|
|
416
|
+
status: "done",
|
|
417
|
+
message: "Configuration saved",
|
|
418
|
+
result: {
|
|
419
|
+
vendorDir: this.vendorDir,
|
|
420
|
+
pythonBin,
|
|
421
|
+
configPath
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
} catch (err) {
|
|
425
|
+
yield { step, status: "failed", message: `Config write failed: ${err.message}` };
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
async *reloadConfig() {
|
|
429
|
+
const step = "reload";
|
|
430
|
+
yield { step, status: "running", message: "Reloading configuration..." };
|
|
431
|
+
try {
|
|
432
|
+
const { reloadConfig } = await import("./config-OQ6ZBQSM.js");
|
|
433
|
+
reloadConfig();
|
|
434
|
+
yield { step, status: "done", message: "Configuration reloaded" };
|
|
435
|
+
} catch (err) {
|
|
436
|
+
yield { step, status: "failed", message: `Reload failed: ${err.message}` };
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
// ── helpers ──
|
|
440
|
+
async *runProcess(step, command, args, timeoutMs) {
|
|
441
|
+
const proc = spawn(command, args, {
|
|
442
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
443
|
+
env: { ...process.env }
|
|
444
|
+
});
|
|
445
|
+
const chunks = [];
|
|
446
|
+
let notify = null;
|
|
447
|
+
let closed = false;
|
|
448
|
+
let closedCode = 1;
|
|
449
|
+
proc.stdout?.on("data", (d) => {
|
|
450
|
+
chunks.push(d.toString());
|
|
451
|
+
notify?.();
|
|
452
|
+
});
|
|
453
|
+
proc.stderr?.on("data", (d) => {
|
|
454
|
+
chunks.push(d.toString());
|
|
455
|
+
notify?.();
|
|
456
|
+
});
|
|
457
|
+
proc.on("close", (code) => {
|
|
458
|
+
closed = true;
|
|
459
|
+
closedCode = code ?? 1;
|
|
460
|
+
notify?.();
|
|
461
|
+
});
|
|
462
|
+
const timer = setTimeout(() => {
|
|
463
|
+
proc.kill("SIGTERM");
|
|
464
|
+
setTimeout(() => {
|
|
465
|
+
if (!closed) proc.kill("SIGKILL");
|
|
466
|
+
}, 5e3);
|
|
467
|
+
notify?.();
|
|
468
|
+
}, timeoutMs);
|
|
469
|
+
try {
|
|
470
|
+
while (!closed || chunks.length > 0) {
|
|
471
|
+
if (chunks.length > 0) {
|
|
472
|
+
yield { step, log: chunks.shift() };
|
|
473
|
+
} else if (!closed) {
|
|
474
|
+
await new Promise((r) => {
|
|
475
|
+
notify = r;
|
|
476
|
+
});
|
|
477
|
+
notify = null;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
} finally {
|
|
481
|
+
clearTimeout(timer);
|
|
482
|
+
}
|
|
483
|
+
return { ok: closedCode === 0, exitCode: closedCode };
|
|
484
|
+
}
|
|
485
|
+
patchEnvFlag(envPath, key, value) {
|
|
486
|
+
if (!fs2.existsSync(envPath)) return;
|
|
487
|
+
let content = fs2.readFileSync(envPath, "utf-8");
|
|
488
|
+
const regex = new RegExp(`^${key}=.*$`, "m");
|
|
489
|
+
if (regex.test(content)) {
|
|
490
|
+
content = content.replace(regex, `${key}=${value}`);
|
|
491
|
+
} else {
|
|
492
|
+
content = content.trimEnd() + `
|
|
493
|
+
${key}=${value}
|
|
494
|
+
`;
|
|
495
|
+
}
|
|
496
|
+
fs2.writeFileSync(envPath, content, "utf-8");
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
|
|
281
500
|
// src/web/routes/setup.ts
|
|
282
501
|
var { Router } = express;
|
|
283
502
|
function setupSSE(req, res) {
|
|
@@ -460,9 +679,9 @@ function createSetupRouter(deps = {}) {
|
|
|
460
679
|
return;
|
|
461
680
|
}
|
|
462
681
|
try {
|
|
463
|
-
const exists =
|
|
464
|
-
const isDirectory = exists &&
|
|
465
|
-
const isGitRepo = exists &&
|
|
682
|
+
const exists = fs3.existsSync(dirPath);
|
|
683
|
+
const isDirectory = exists && fs3.statSync(dirPath).isDirectory();
|
|
684
|
+
const isGitRepo = exists && fs3.existsSync(dirPath + "/.git");
|
|
466
685
|
res.json({ exists, isDirectory, isGitRepo });
|
|
467
686
|
} catch (err) {
|
|
468
687
|
res.json({ exists: false, isDirectory: false, isGitRepo: false, error: err.message });
|
|
@@ -482,8 +701,8 @@ function createSetupRouter(deps = {}) {
|
|
|
482
701
|
sse.write({ step: "collected", message: "Static info collected" });
|
|
483
702
|
sse.write({ step: "analyzing", message: "Running AI analysis..." });
|
|
484
703
|
try {
|
|
485
|
-
const { loadConfig } = await import("./config-
|
|
486
|
-
const { createAIRunner } = await import("./ai-runner-
|
|
704
|
+
const { loadConfig } = await import("./config-OQ6ZBQSM.js");
|
|
705
|
+
const { createAIRunner } = await import("./ai-runner-PJYBQ6LO.js");
|
|
487
706
|
const config = loadConfig();
|
|
488
707
|
const runner = createAIRunner(config.ai);
|
|
489
708
|
const knowledge = await analyze({ workDir, aiRunner: runner });
|
|
@@ -501,11 +720,11 @@ function createSetupRouter(deps = {}) {
|
|
|
501
720
|
router.get("/api/setup/workspace-config", (_req, res) => {
|
|
502
721
|
try {
|
|
503
722
|
const wsPath = ConfigGenerator.getWorkspaceConfigPath();
|
|
504
|
-
if (!
|
|
723
|
+
if (!fs3.existsSync(wsPath)) {
|
|
505
724
|
res.json({ configured: false, config: null, configPath: wsPath });
|
|
506
725
|
return;
|
|
507
726
|
}
|
|
508
|
-
const raw =
|
|
727
|
+
const raw = fs3.readFileSync(wsPath, "utf-8");
|
|
509
728
|
const config = JSON.parse(raw);
|
|
510
729
|
res.json({ configured: true, config, configPath: wsPath });
|
|
511
730
|
} catch (err) {
|
|
@@ -520,11 +739,11 @@ function createSetupRouter(deps = {}) {
|
|
|
520
739
|
}
|
|
521
740
|
try {
|
|
522
741
|
const wsPath = ConfigGenerator.getWorkspaceConfigPath();
|
|
523
|
-
const dir =
|
|
524
|
-
if (!
|
|
525
|
-
|
|
742
|
+
const dir = path3.dirname(wsPath);
|
|
743
|
+
if (!fs3.existsSync(dir)) {
|
|
744
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
526
745
|
}
|
|
527
|
-
|
|
746
|
+
fs3.writeFileSync(wsPath, JSON.stringify(wsConfig, null, 2) + "\n", "utf-8");
|
|
528
747
|
res.json({ success: true, configPath: wsPath });
|
|
529
748
|
} catch (err) {
|
|
530
749
|
res.status(500).json({ error: err.message });
|
|
@@ -538,10 +757,29 @@ function createSetupRouter(deps = {}) {
|
|
|
538
757
|
res.json({ exists: false, knowledge: null });
|
|
539
758
|
}
|
|
540
759
|
});
|
|
760
|
+
router.post("/api/setup/e2e-setup", (req, res) => {
|
|
761
|
+
const { projectRoot } = req.body;
|
|
762
|
+
const root = projectRoot || process.env.PROJECT_WORK_DIR || process.cwd();
|
|
763
|
+
const runner = new E2ESetupRunner(root);
|
|
764
|
+
const sse = setupSSE(req, res);
|
|
765
|
+
(async () => {
|
|
766
|
+
try {
|
|
767
|
+
for await (const event of runner.run()) {
|
|
768
|
+
if (sse.isClosed()) break;
|
|
769
|
+
sse.write(event);
|
|
770
|
+
}
|
|
771
|
+
sse.write({ done: true });
|
|
772
|
+
} catch (err) {
|
|
773
|
+
sse.write({ error: err.message });
|
|
774
|
+
} finally {
|
|
775
|
+
sse.end();
|
|
776
|
+
}
|
|
777
|
+
})();
|
|
778
|
+
});
|
|
541
779
|
return router;
|
|
542
780
|
}
|
|
543
781
|
|
|
544
782
|
export {
|
|
545
783
|
createSetupRouter
|
|
546
784
|
};
|
|
547
|
-
//# sourceMappingURL=chunk-
|
|
785
|
+
//# sourceMappingURL=chunk-KZ6CKYWK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/web/routes/setup.ts","../src/cli/setup/ConfigGenerator.ts","../src/e2e/E2ESetupRunner.ts"],"sourcesContent":["import express, { type Request, type Response } from 'express';\nconst { Router } = express;\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { DependencyChecker } from '../../cli/setup/DependencyChecker.js';\nimport { ConfigGenerator, SetupConfig } from '../../cli/setup/ConfigGenerator.js';\nimport { resolveConfigFilePath } from '../../config.js';\nimport { collectStaticInfo, analyze } from '../../knowledge/KnowledgeAnalyzer.js';\nimport { getProjectKnowledge } from '../../knowledge/index.js';\nimport { E2ESetupRunner } from '../../e2e/E2ESetupRunner.js';\n\nexport interface SetupRouterDeps {\n configPath?: string;\n onComplete?: (configPath: string) => void;\n serviceMode?: boolean;\n}\n\ninterface SSEHandle {\n write: (data: unknown) => void;\n isClosed: () => boolean;\n end: () => void;\n}\n\nfunction setupSSE(req: Request, res: Response): SSEHandle {\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no');\n res.flushHeaders();\n\n let closed = false;\n req.on('close', () => { closed = true; });\n\n return {\n write(data: unknown) {\n if (closed) return;\n res.write(`data: ${JSON.stringify(data)}\\n\\n`);\n if (typeof (res as unknown as { flush?: () => void }).flush === 'function') {\n (res as unknown as { flush: () => void }).flush();\n }\n },\n isClosed: () => closed,\n end() {\n if (!closed) {\n closed = true;\n res.end();\n }\n },\n };\n}\n\nexport function createSetupRouter(deps: SetupRouterDeps = {}): ReturnType<typeof Router> {\n const router = Router();\n const checker = new DependencyChecker();\n\n router.get('/api/setup/health', (_req: Request, res: Response) => {\n res.json({ ok: true, timestamp: Date.now() });\n });\n\n router.get('/api/setup/status', (_req: Request, res: Response) => {\n const initialized = deps.serviceMode || ConfigGenerator.isInitialized(deps.configPath);\n res.json({\n initialized,\n configPath: deps.configPath ?? ConfigGenerator.getGlobalConfigPath(),\n globalConfigDir: ConfigGenerator.getGlobalConfigDir(),\n });\n });\n\n router.get('/api/setup/current-config', (_req: Request, res: Response) => {\n try {\n const config = ConfigGenerator.readCurrent();\n const configPath = deps.configPath ?? resolveConfigFilePath();\n res.json({ config, configPath });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/api/setup/check-env', async (_req: Request, res: Response) => {\n try {\n const aiMode = ((_req.query.aiMode as string) || undefined);\n const results = await checker.checkAll(aiMode);\n res.json({ dependencies: results });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.post('/api/setup/validate-token', async (req: Request, res: Response) => {\n const { apiUrl, token } = req.body as { apiUrl?: string; token?: string };\n if (!apiUrl || !token) {\n res.status(400).json({ error: 'apiUrl and token are required' });\n return;\n }\n try {\n const url = `${apiUrl.replace(/\\/$/, '')}/api/v3/user`;\n const resp = await fetch(url, {\n headers: { 'PRIVATE-TOKEN': token },\n signal: AbortSignal.timeout(10000),\n });\n if (resp.ok) {\n const user = (await resp.json()) as { username?: string; name?: string };\n res.json({ valid: true, user: { username: user.username, name: user.name } });\n } else {\n res.json({ valid: false, error: `HTTP ${resp.status}: ${resp.statusText}` });\n }\n } catch (err) {\n res.json({ valid: false, error: (err as Error).message });\n }\n });\n\n router.post('/api/setup/validate-repo', async (req: Request, res: Response) => {\n const { apiUrl, token, projectPath } = req.body as {\n apiUrl?: string; token?: string; projectPath?: string;\n };\n if (!apiUrl || !token || !projectPath) {\n res.status(400).json({ error: 'apiUrl, token, and projectPath are required' });\n return;\n }\n try {\n const encoded = encodeURIComponent(projectPath);\n const url = `${apiUrl.replace(/\\/$/, '')}/api/v3/projects/${encoded}`;\n const resp = await fetch(url, {\n headers: { 'PRIVATE-TOKEN': token },\n signal: AbortSignal.timeout(10000),\n });\n if (resp.ok) {\n const project = (await resp.json()) as {\n id?: number; name?: string; path_with_namespace?: string;\n };\n res.json({ valid: true, project });\n } else {\n res.json({ valid: false, error: `HTTP ${resp.status}: ${resp.statusText}` });\n }\n } catch (err) {\n res.json({ valid: false, error: (err as Error).message });\n }\n });\n\n router.post('/api/setup/install-dep', (req: Request, res: Response) => {\n const { name } = req.body as { name?: string };\n if (!name) {\n res.status(400).json({ error: 'name is required' });\n return;\n }\n\n const sse = setupSSE(req, res);\n\n (async () => {\n try {\n for await (const line of checker.install(name)) {\n if (sse.isClosed()) break;\n sse.write({ line });\n console.log(` [install] ${line}`);\n }\n sse.write({ done: true });\n } catch (err) {\n sse.write({ error: (err as Error).message });\n } finally {\n sse.end();\n }\n })();\n });\n\n router.post('/api/setup/install-all', (req: Request, res: Response) => {\n const { aiMode } = req.body as { aiMode?: string };\n\n const sse = setupSSE(req, res);\n\n (async () => {\n try {\n for await (const event of checker.installAll(aiMode)) {\n if (sse.isClosed()) break;\n sse.write(event);\n if (event.line) console.log(` [install] ${event.line}`);\n if (event.phase && event.status) {\n console.log(` [install] [${event.phase}] ${event.dep} — ${event.status}`);\n }\n }\n sse.write({ done: true });\n } catch (err) {\n sse.write({ error: (err as Error).message });\n } finally {\n sse.end();\n }\n })();\n });\n\n router.post('/api/setup/generate-preview', (req: Request, res: Response) => {\n const config = req.body as SetupConfig;\n try {\n const preview = ConfigGenerator.generate(config);\n res.json({ preview });\n } catch (err) {\n res.status(400).json({ error: (err as Error).message });\n }\n });\n\n router.post('/api/setup/complete', (req: Request, res: Response) => {\n const { config, outputPath } = req.body as {\n config?: SetupConfig;\n outputPath?: string;\n };\n if (!config) {\n res.status(400).json({ error: 'config is required' });\n return;\n }\n try {\n const targetPath = outputPath ?? deps.configPath;\n const savedPath = ConfigGenerator.mergeWrite(config, targetPath);\n res.json({ success: true, configPath: savedPath });\n deps.onComplete?.(savedPath);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.post('/api/setup/check-path', (req: Request, res: Response) => {\n const { path: dirPath } = req.body as { path?: string };\n if (!dirPath) {\n res.status(400).json({ error: 'path is required' });\n return;\n }\n try {\n const exists = fs.existsSync(dirPath);\n const isDirectory = exists && fs.statSync(dirPath).isDirectory();\n const isGitRepo = exists && fs.existsSync(dirPath + '/.git');\n res.json({ exists, isDirectory, isGitRepo });\n } catch (err) {\n res.json({ exists: false, isDirectory: false, isGitRepo: false, error: (err as Error).message });\n }\n });\n\n router.post('/api/setup/analyze-repo', (req: Request, res: Response) => {\n const { workDir } = req.body as { workDir?: string };\n if (!workDir) {\n res.status(400).json({ error: 'workDir is required' });\n return;\n }\n\n const sse = setupSSE(req, res);\n\n (async () => {\n try {\n sse.write({ step: 'collecting', message: 'Collecting project info...' });\n const staticInfo = await collectStaticInfo(workDir);\n sse.write({ step: 'collected', message: 'Static info collected' });\n\n sse.write({ step: 'analyzing', message: 'Running AI analysis...' });\n\n try {\n const { loadConfig } = await import('../../config.js');\n const { createAIRunner } = await import('../../ai-runner/index.js');\n const config = loadConfig();\n const runner = createAIRunner(config.ai);\n const knowledge = await analyze({ workDir, aiRunner: runner });\n sse.write({ step: 'done', knowledge });\n } catch (aiErr) {\n sse.write({ step: 'ai_unavailable', message: (aiErr as Error).message, staticInfo });\n }\n } catch (err) {\n sse.write({ step: 'error', error: (err as Error).message });\n } finally {\n sse.end();\n }\n })();\n });\n\n router.get('/api/setup/workspace-config', (_req: Request, res: Response) => {\n try {\n const wsPath = ConfigGenerator.getWorkspaceConfigPath();\n if (!fs.existsSync(wsPath)) {\n res.json({ configured: false, config: null, configPath: wsPath });\n return;\n }\n const raw = fs.readFileSync(wsPath, 'utf-8');\n const config = JSON.parse(raw);\n res.json({ configured: true, config, configPath: wsPath });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.post('/api/setup/workspace-config', (req: Request, res: Response) => {\n const { config: wsConfig } = req.body as { config?: unknown };\n if (!wsConfig) {\n res.status(400).json({ error: 'config is required' });\n return;\n }\n try {\n const wsPath = ConfigGenerator.getWorkspaceConfigPath();\n const dir = path.dirname(wsPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(wsPath, JSON.stringify(wsConfig, null, 2) + '\\n', 'utf-8');\n res.json({ success: true, configPath: wsPath });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/api/setup/knowledge-preview', (_req: Request, res: Response) => {\n const knowledge = getProjectKnowledge();\n if (knowledge) {\n res.json({ exists: true, knowledge });\n } else {\n res.json({ exists: false, knowledge: null });\n }\n });\n\n router.post('/api/setup/e2e-setup', (req: Request, res: Response) => {\n const { projectRoot } = req.body as { projectRoot?: string };\n const root = projectRoot || process.env.PROJECT_WORK_DIR || process.cwd();\n const runner = new E2ESetupRunner(root);\n const sse = setupSSE(req, res);\n\n (async () => {\n try {\n for await (const event of runner.run()) {\n if (sse.isClosed()) break;\n sse.write(event);\n }\n sse.write({ done: true });\n } catch (err) {\n sse.write({ error: (err as Error).message });\n } finally {\n sse.end();\n }\n })();\n });\n\n return router;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { getLocalIP } from '../../utils/network.js';\nimport { getGlobalDir, resolveDataDir } from '../../paths.js';\nimport type { WorkspaceConfig } from '../../workspace/WorkspaceConfig.js';\n\nexport { getLocalIP };\n\nexport interface SetupConfig {\n gongfeng: {\n apiUrl: string;\n privateToken: string;\n projectPath: string;\n };\n project: {\n workDir: string;\n gitRootDir?: string;\n baseBranch?: string;\n branchPrefix?: string;\n worktreeBaseDir?: string;\n projectSubDir?: string;\n };\n ai: {\n mode: string;\n model?: string;\n phaseTimeoutMs?: number;\n };\n poll?: {\n discoveryIntervalMs?: number;\n driveIntervalMs?: number;\n maxRetries?: number;\n maxConcurrent?: number;\n };\n web?: {\n port?: number;\n };\n pipeline?: {\n mode?: string;\n };\n review?: {\n enabled?: boolean;\n };\n e2e?: {\n uatVendorDir?: string;\n uatConfigFile?: string;\n pythonBin?: string;\n };\n release?: {\n enabled?: boolean;\n };\n}\n\nexport class ConfigGenerator {\n /** @deprecated Use `getLocalIP()` from `../../utils/network.js` instead */\n static getLocalIP(): string {\n return getLocalIP();\n }\n\n static getGlobalConfigDir(): string {\n return getGlobalDir();\n }\n\n static getGlobalConfigPath(): string {\n return path.join(ConfigGenerator.getGlobalConfigDir(), '.env');\n }\n\n static isInitialized(configPath?: string): boolean {\n const envPath = configPath ?? ConfigGenerator.getGlobalConfigPath();\n return fs.existsSync(envPath);\n }\n\n static generate(config: SetupConfig): string {\n const lines: string[] = [];\n\n const addSection = (title: string) => {\n if (lines.length > 0) lines.push('');\n lines.push('# ' + title);\n };\n\n const addVar = (\n key: string,\n value: string | number | boolean | undefined,\n ) => {\n if (value === undefined) return;\n lines.push(key + '=' + String(value));\n };\n\n const addNonDefault = (\n key: string,\n value: string | number | boolean | undefined,\n defaultValue: string | number | boolean,\n ) => {\n if (value === undefined || value === '' || value === defaultValue) return;\n lines.push(key + '=' + String(value));\n };\n\n addSection('Gongfeng');\n addVar('GONGFENG_API_URL', config.gongfeng.apiUrl);\n addVar('GONGFENG_PRIVATE_TOKEN', config.gongfeng.privateToken);\n addVar('GONGFENG_PROJECT_PATH', config.gongfeng.projectPath);\n\n addSection('Project');\n addVar('PROJECT_WORK_DIR', config.project.workDir);\n addNonDefault('GIT_ROOT_DIR', config.project.gitRootDir, '');\n addNonDefault('BASE_BRANCH', config.project.baseBranch, 'master');\n addNonDefault('BRANCH_PREFIX', config.project.branchPrefix, 'feat/issue');\n addNonDefault('WORKTREE_BASE_DIR', config.project.worktreeBaseDir, '');\n addNonDefault('PROJECT_SUBDIR', config.project.projectSubDir, '');\n\n addSection('AI');\n addVar('AI_RUNNER_MODE', config.ai.mode);\n addNonDefault('AI_MODEL', config.ai.model, 'Claude-4.6-Opus');\n\n addNonDefault('CLAUDE_PHASE_TIMEOUT_MS', config.ai.phaseTimeoutMs, 1800000);\n addNonDefault('POLL_DISCOVERY_INTERVAL_MS', config.poll?.discoveryIntervalMs, 60000);\n addNonDefault('POLL_DRIVE_INTERVAL_MS', config.poll?.driveIntervalMs, 15000);\n addNonDefault('MAX_RETRIES', config.poll?.maxRetries, 3);\n addNonDefault('MAX_CONCURRENT_ISSUES', config.poll?.maxConcurrent, 3);\n addNonDefault('PIPELINE_MODE', config.pipeline?.mode, 'auto');\n addNonDefault('REVIEW_ENABLED', config.review?.enabled, true);\n addNonDefault('RELEASE_ENABLED', config.release?.enabled, false);\n\n const port = config.web?.port ?? 3000;\n if (port !== 3000) {\n addNonDefault('WEB_PORT', port, 3000);\n }\n\n addNonDefault('E2E_UAT_VENDOR_DIR', config.e2e?.uatVendorDir, '');\n addNonDefault('E2E_UAT_CONFIG_FILE', config.e2e?.uatConfigFile, '');\n addNonDefault('E2E_PYTHON_BIN', config.e2e?.pythonBin, 'python3');\n\n addSection('Workspace');\n addVar('WORKSPACE_CONFIG_PATH', ConfigGenerator.getWorkspaceConfigPath());\n\n return lines.join('\\n') + '\\n';\n }\n\n static readCurrent(): SetupConfig {\n const env = process.env;\n const aiMode = env.AI_RUNNER_MODE || 'claude-internal';\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: env.GIT_ROOT_DIR || undefined,\n baseBranch: env.BASE_BRANCH || undefined,\n branchPrefix: env.BRANCH_PREFIX || undefined,\n worktreeBaseDir: env.WORKTREE_BASE_DIR || undefined,\n projectSubDir: env.PROJECT_SUBDIR || undefined,\n },\n ai: {\n mode: aiMode,\n model: env.AI_MODEL || undefined,\n phaseTimeoutMs: env.CLAUDE_PHASE_TIMEOUT_MS\n ? parseInt(env.CLAUDE_PHASE_TIMEOUT_MS, 10)\n : undefined,\n },\n poll: {\n discoveryIntervalMs: env.POLL_DISCOVERY_INTERVAL_MS\n ? parseInt(env.POLL_DISCOVERY_INTERVAL_MS, 10)\n : undefined,\n driveIntervalMs: env.POLL_DRIVE_INTERVAL_MS\n ? parseInt(env.POLL_DRIVE_INTERVAL_MS, 10)\n : undefined,\n maxRetries: env.MAX_RETRIES\n ? parseInt(env.MAX_RETRIES, 10)\n : undefined,\n maxConcurrent: env.MAX_CONCURRENT_ISSUES\n ? parseInt(env.MAX_CONCURRENT_ISSUES, 10)\n : undefined,\n },\n web: {\n port: env.WEB_PORT ? parseInt(env.WEB_PORT, 10) : undefined,\n },\n pipeline: {\n mode: env.PIPELINE_MODE || undefined,\n },\n review: {\n enabled: env.REVIEW_ENABLED !== undefined\n ? env.REVIEW_ENABLED === 'true'\n : undefined,\n },\n release: {\n enabled: env.RELEASE_ENABLED !== undefined\n ? env.RELEASE_ENABLED === 'true'\n : undefined,\n },\n e2e: {\n uatVendorDir: env.E2E_UAT_VENDOR_DIR || undefined,\n uatConfigFile: env.E2E_UAT_CONFIG_FILE || undefined,\n pythonBin: env.E2E_PYTHON_BIN || undefined,\n },\n };\n }\n\n static getWorkspaceConfigPath(): string {\n return path.join(resolveDataDir(), 'workspace.json');\n }\n\n static buildWorkspaceConfig(config: SetupConfig): WorkspaceConfig {\n return {\n primary: {\n name: 'primary',\n projectPath: config.gongfeng.projectPath,\n localGitRoot: config.project.gitRootDir ?? config.project.workDir,\n projectSubDir: config.project.projectSubDir ?? '',\n baseBranch: config.project.baseBranch,\n branchPrefix: config.project.branchPrefix,\n role: '',\n },\n associates: [],\n };\n }\n\n static writeWorkspaceConfig(config: SetupConfig): string {\n const wsPath = ConfigGenerator.getWorkspaceConfigPath();\n const dir = path.dirname(wsPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n const wsConfig = ConfigGenerator.buildWorkspaceConfig(config);\n fs.writeFileSync(wsPath, JSON.stringify(wsConfig, null, 2) + '\\n', 'utf-8');\n return wsPath;\n }\n\n static write(config: SetupConfig, outputPath?: string): string {\n const targetPath = outputPath ?? ConfigGenerator.getGlobalConfigPath();\n const dir = path.dirname(targetPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n const content = ConfigGenerator.generate(config);\n fs.writeFileSync(targetPath, content, 'utf-8');\n ConfigGenerator.writeWorkspaceConfig(config);\n return targetPath;\n }\n\n /**\n * 增量更新 .env 文件:只更新 SetupConfig 涉及的 key,保留其余内容。\n * 如果文件不存在,退化为全量写入。\n */\n static mergeWrite(config: SetupConfig, outputPath?: string): string {\n const targetPath = outputPath ?? ConfigGenerator.getGlobalConfigPath();\n\n if (!fs.existsSync(targetPath)) {\n return ConfigGenerator.write(config, targetPath);\n }\n\n const existing = fs.readFileSync(targetPath, 'utf-8');\n const updates = ConfigGenerator.toEnvEntries(config);\n const merged = ConfigGenerator.mergeEnvContent(existing, updates);\n\n const dir = path.dirname(targetPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(targetPath, merged, 'utf-8');\n ConfigGenerator.writeWorkspaceConfig(config);\n return targetPath;\n }\n\n /**\n * 将 SetupConfig 转换为 env key-value 对。\n * null 值表示该 key 应从文件中删除(用户恢复为默认值时)。\n */\n static toEnvEntries(config: SetupConfig): Map<string, string | null> {\n const entries = new Map<string, string | null>();\n\n // 必填项始终写入\n entries.set('GONGFENG_API_URL', config.gongfeng.apiUrl);\n entries.set('GONGFENG_PRIVATE_TOKEN', config.gongfeng.privateToken);\n entries.set('GONGFENG_PROJECT_PATH', config.gongfeng.projectPath);\n entries.set('PROJECT_WORK_DIR', config.project.workDir);\n entries.set('AI_RUNNER_MODE', config.ai.mode);\n\n // 可选项:有值写入,值为默认值或空时标记删除\n const optionals: Array<[string, string | number | boolean | undefined, string | number | boolean]> = [\n ['GIT_ROOT_DIR', config.project.gitRootDir, ''],\n ['BASE_BRANCH', config.project.baseBranch, 'master'],\n ['BRANCH_PREFIX', config.project.branchPrefix, 'feat/issue'],\n ['WORKTREE_BASE_DIR', config.project.worktreeBaseDir, ''],\n ['PROJECT_SUBDIR', config.project.projectSubDir, ''],\n ['AI_MODEL', config.ai.model, 'Claude-4.6-Opus'],\n ['CLAUDE_PHASE_TIMEOUT_MS', config.ai.phaseTimeoutMs, 1800000],\n ['POLL_DISCOVERY_INTERVAL_MS', config.poll?.discoveryIntervalMs, 60000],\n ['POLL_DRIVE_INTERVAL_MS', config.poll?.driveIntervalMs, 15000],\n ['MAX_RETRIES', config.poll?.maxRetries, 3],\n ['MAX_CONCURRENT_ISSUES', config.poll?.maxConcurrent, 3],\n ['PIPELINE_MODE', config.pipeline?.mode, 'auto'],\n ['REVIEW_ENABLED', config.review?.enabled, true],\n ['RELEASE_ENABLED', config.release?.enabled, false],\n ['WEB_PORT', config.web?.port, 3000],\n ['E2E_UAT_VENDOR_DIR', config.e2e?.uatVendorDir, ''],\n ['E2E_UAT_CONFIG_FILE', config.e2e?.uatConfigFile, ''],\n ['E2E_PYTHON_BIN', config.e2e?.pythonBin, 'python3'],\n ['WORKSPACE_CONFIG_PATH', ConfigGenerator.getWorkspaceConfigPath(), ''],\n ];\n\n for (const [key, value, defaultValue] of optionals) {\n if (value === undefined || value === '' || value === defaultValue) {\n entries.set(key, null);\n } else {\n entries.set(key, String(value));\n }\n }\n\n return entries;\n }\n\n /**\n * 将 updates 合并到现有 .env 内容中。\n * - 已有的 key:原地更新值或删除行\n * - 新增的 key:追加到文件末尾\n * - 注释和空行:原样保留\n */\n static mergeEnvContent(\n existing: string,\n updates: Map<string, string | null>,\n ): string {\n const remaining = new Map(updates);\n const lines = existing.split('\\n');\n const result: string[] = [];\n\n for (const line of lines) {\n const trimmed = line.trim();\n // 空行或注释:保留\n if (trimmed === '' || trimmed.startsWith('#')) {\n result.push(line);\n continue;\n }\n // 解析 KEY=VALUE\n const eqIdx = trimmed.indexOf('=');\n if (eqIdx === -1) {\n result.push(line);\n continue;\n }\n const key = trimmed.substring(0, eqIdx).trim();\n if (remaining.has(key)) {\n const newValue = remaining.get(key);\n remaining.delete(key);\n if (newValue !== null) {\n result.push(`${key}=${newValue}`);\n }\n // newValue === null → 删除该行\n } else {\n // 不在 updates 范围内:原样保留\n result.push(line);\n }\n }\n\n // 追加新增的 key(原文件中不存在的)\n const newEntries = [...remaining.entries()].filter(([, v]) => v !== null);\n if (newEntries.length > 0) {\n if (result.length > 0 && result[result.length - 1]?.trim() !== '') {\n result.push('');\n }\n for (const [key, value] of newEntries) {\n result.push(`${key}=${value}`);\n }\n }\n\n return result.join('\\n');\n }\n}\n","import { spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { ConfigGenerator } from '../cli/setup/ConfigGenerator.js';\n\nexport interface E2ESetupEvent {\n step: string;\n status?: 'running' | 'done' | 'failed';\n message?: string;\n log?: string;\n result?: Record<string, string>;\n}\n\n/**\n * One-click E2E UAT environment setup runner.\n * Yields SSE events for each step of the setup process.\n */\nexport class E2ESetupRunner {\n private readonly vendorDir: string;\n private readonly repoUrl = 'git@git.woa.com:fishcui/oa_pc_uat.git';\n private readonly pythonCandidates = [\n 'python3.12', 'python3.11', 'python3.10', 'python3.9', 'python3.8', 'python3',\n ];\n\n constructor(private readonly projectRoot: string) {\n this.vendorDir = path.join(projectRoot, 'vendor', 'oa_pc_uat');\n }\n\n async *run(): AsyncGenerator<E2ESetupEvent> {\n // Step 1: Clone or update repo\n yield* this.cloneOrUpdate();\n\n // Step 2: Detect Python 3.8+\n const pythonBin = yield* this.detectPython();\n if (!pythonBin) return;\n\n // Step 3: Install playwright\n yield* this.installPlaywright(pythonBin);\n\n // Step 4: Install Chromium\n yield* this.installChromium(pythonBin);\n\n // Step 5: Configure .env\n yield* this.configure(pythonBin);\n\n // Step 6: Reload config\n yield* this.reloadConfig();\n }\n\n private async *cloneOrUpdate(): AsyncGenerator<E2ESetupEvent> {\n const step = 'clone';\n yield { step, status: 'running', message: 'Preparing vendor/oa_pc_uat...' };\n\n const vendorParent = path.dirname(this.vendorDir);\n if (!fs.existsSync(vendorParent)) {\n fs.mkdirSync(vendorParent, { recursive: true });\n }\n\n if (fs.existsSync(path.join(this.vendorDir, 'main.py'))) {\n // Already exists — git pull\n yield { step, log: 'vendor/oa_pc_uat exists, running git pull...' };\n const result = yield* this.runProcess(step, 'git', ['-C', this.vendorDir, 'pull', '--ff-only'], 60_000);\n if (!result.ok) {\n yield { step, status: 'failed', message: `git pull failed (exit ${result.exitCode})` };\n return;\n }\n } else {\n // Clone fresh\n yield { step, log: `Cloning ${this.repoUrl}...` };\n const result = yield* this.runProcess(step, 'git', ['clone', this.repoUrl, this.vendorDir], 120_000);\n if (!result.ok) {\n yield { step, status: 'failed', message: `git clone failed (exit ${result.exitCode})` };\n return;\n }\n }\n\n // Verify\n if (!fs.existsSync(path.join(this.vendorDir, 'main.py'))) {\n yield { step, status: 'failed', message: 'vendor/oa_pc_uat/main.py not found after clone' };\n return;\n }\n\n yield { step, status: 'done', message: 'vendor/oa_pc_uat ready' };\n }\n\n private async *detectPython(): AsyncGenerator<E2ESetupEvent, string | null> {\n const step = 'detect-python';\n yield { step, status: 'running', message: 'Detecting Python 3.8+...' };\n\n const { execFileSync } = await import('node:child_process');\n\n for (const candidate of this.pythonCandidates) {\n try {\n const output = execFileSync(candidate, ['--version'], {\n encoding: 'utf-8',\n timeout: 5_000,\n }).trim();\n yield { step, log: `${candidate}: ${output}` };\n\n // Parse version, require >= 3.8\n const match = output.match(/Python\\s+(\\d+)\\.(\\d+)/);\n if (match) {\n const major = parseInt(match[1], 10);\n const minor = parseInt(match[2], 10);\n if (major === 3 && minor >= 8) {\n yield { step, status: 'done', message: `Using ${candidate} (${output})` };\n return candidate;\n }\n yield { step, log: `${candidate} version too old (need >= 3.8)` };\n }\n } catch {\n yield { step, log: `${candidate}: not found` };\n }\n }\n\n yield { step, status: 'failed', message: 'No Python 3.8+ found' };\n return null;\n }\n\n private async *installPlaywright(pythonBin: string): AsyncGenerator<E2ESetupEvent> {\n const step = 'install-playwright';\n yield { step, status: 'running', message: 'Installing playwright==1.48.0...' };\n\n const result = yield* this.runProcess(\n step,\n pythonBin,\n ['-m', 'pip', 'install', 'playwright==1.48.0', '--user', '-i', 'https://pypi.org/simple/'],\n 300_000,\n );\n\n if (!result.ok) {\n yield { step, status: 'failed', message: `pip install failed (exit ${result.exitCode})` };\n return;\n }\n\n // Verify\n try {\n const { execFileSync } = await import('node:child_process');\n execFileSync(pythonBin, ['-c', 'import playwright'], { encoding: 'utf-8', timeout: 10_000 });\n yield { step, status: 'done', message: 'playwright installed' };\n } catch {\n yield { step, status: 'failed', message: 'playwright import check failed after install' };\n }\n }\n\n private async *installChromium(pythonBin: string): AsyncGenerator<E2ESetupEvent> {\n const step = 'install-chromium';\n yield { step, status: 'running', message: 'Installing Chromium browser...' };\n\n const result = yield* this.runProcess(\n step,\n pythonBin,\n ['-m', 'playwright', 'install', 'chromium'],\n 300_000,\n );\n\n if (!result.ok) {\n yield { step, status: 'failed', message: `playwright install chromium failed (exit ${result.exitCode})` };\n return;\n }\n\n yield { step, status: 'done', message: 'Chromium installed' };\n }\n\n private async *configure(pythonBin: string): AsyncGenerator<E2ESetupEvent> {\n const step = 'configure';\n yield { step, status: 'running', message: 'Updating .env configuration...' };\n\n try {\n const current = ConfigGenerator.readCurrent();\n current.e2e = {\n uatVendorDir: this.vendorDir,\n pythonBin,\n };\n\n // Also set E2E_UI_ENABLED via env so mergeWrite picks it up\n // mergeWrite only handles SetupConfig keys, so we patch the file directly for E2E_UI_ENABLED\n const configPath = ConfigGenerator.mergeWrite(current);\n yield { step, log: `Updated ${configPath}` };\n\n // Patch E2E_UI_ENABLED=true (not in SetupConfig, handled separately)\n this.patchEnvFlag(configPath, 'E2E_UI_ENABLED', 'true');\n yield { step, log: 'Set E2E_UI_ENABLED=true' };\n\n yield {\n step,\n status: 'done',\n message: 'Configuration saved',\n result: {\n vendorDir: this.vendorDir,\n pythonBin,\n configPath,\n },\n };\n } catch (err) {\n yield { step, status: 'failed', message: `Config write failed: ${(err as Error).message}` };\n }\n }\n\n private async *reloadConfig(): AsyncGenerator<E2ESetupEvent> {\n const step = 'reload';\n yield { step, status: 'running', message: 'Reloading configuration...' };\n\n try {\n const { reloadConfig } = await import('../config.js');\n reloadConfig();\n yield { step, status: 'done', message: 'Configuration reloaded' };\n } catch (err) {\n yield { step, status: 'failed', message: `Reload failed: ${(err as Error).message}` };\n }\n }\n\n // ── helpers ──\n\n private async *runProcess(\n step: string,\n command: string,\n args: string[],\n timeoutMs: number,\n ): AsyncGenerator<E2ESetupEvent, { ok: boolean; exitCode: number }> {\n const proc = spawn(command, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n env: { ...process.env },\n });\n\n const chunks: string[] = [];\n let notify: (() => void) | null = null;\n let closed = false;\n let closedCode = 1;\n\n proc.stdout?.on('data', (d: Buffer) => {\n chunks.push(d.toString());\n notify?.();\n });\n proc.stderr?.on('data', (d: Buffer) => {\n chunks.push(d.toString());\n notify?.();\n });\n proc.on('close', (code) => {\n closed = true;\n closedCode = code ?? 1;\n notify?.();\n });\n\n const timer = setTimeout(() => {\n proc.kill('SIGTERM');\n setTimeout(() => { if (!closed) proc.kill('SIGKILL'); }, 5000);\n notify?.();\n }, timeoutMs);\n\n try {\n while (!closed || chunks.length > 0) {\n if (chunks.length > 0) {\n yield { step, log: chunks.shift()! };\n } else if (!closed) {\n await new Promise<void>((r) => { notify = r; });\n notify = null;\n }\n }\n } finally {\n clearTimeout(timer);\n }\n\n return { ok: closedCode === 0, exitCode: closedCode };\n }\n\n private patchEnvFlag(envPath: string, key: string, value: string): void {\n if (!fs.existsSync(envPath)) return;\n let content = fs.readFileSync(envPath, 'utf-8');\n const regex = new RegExp(`^${key}=.*$`, 'm');\n if (regex.test(content)) {\n content = content.replace(regex, `${key}=${value}`);\n } else {\n content = content.trimEnd() + `\\n${key}=${value}\\n`;\n }\n fs.writeFileSync(envPath, content, 'utf-8');\n }\n}\n"],"mappings":"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","names":["fs","path","fs","path","path","fs","fs","path"]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
getBinaryEnvKey,
|
|
6
6
|
getDefaultBinary
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-WHTYES3V.js";
|
|
8
8
|
|
|
9
9
|
// src/config.ts
|
|
10
10
|
import { config as loadDotenv } from "dotenv";
|
|
@@ -116,6 +116,7 @@ var envSchema = z.object({
|
|
|
116
116
|
PREVIEW_HOST: z.string().optional().default(""),
|
|
117
117
|
PREVIEW_TTL_MS: envMs(String(24 * 60 * 60 * 1e3)),
|
|
118
118
|
PREVIEW_KEEP_AFTER_COMPLETE: envBoolean("true"),
|
|
119
|
+
PREVIEW_REAP_INTERVAL_MS: envMs("300000"),
|
|
119
120
|
// --- Brainstorm ---
|
|
120
121
|
BRAINSTORM_ENABLED: envBoolean("false"),
|
|
121
122
|
BRAINSTORM_MAX_ROUNDS: envInt("5", { min: 1, max: 20 }),
|
|
@@ -295,7 +296,8 @@ function transformEnvToConfig(env, dirname) {
|
|
|
295
296
|
enabled: env.PREVIEW_ENABLED,
|
|
296
297
|
host: env.PREVIEW_HOST,
|
|
297
298
|
ttlMs: env.PREVIEW_TTL_MS,
|
|
298
|
-
keepAfterComplete: env.PREVIEW_KEEP_AFTER_COMPLETE
|
|
299
|
+
keepAfterComplete: env.PREVIEW_KEEP_AFTER_COMPLETE,
|
|
300
|
+
reapIntervalMs: env.PREVIEW_REAP_INTERVAL_MS
|
|
299
301
|
},
|
|
300
302
|
brainstorm: {
|
|
301
303
|
enabled: env.BRAINSTORM_ENABLED,
|
|
@@ -441,4 +443,4 @@ export {
|
|
|
441
443
|
resetDotenvCache,
|
|
442
444
|
reloadConfig
|
|
443
445
|
};
|
|
444
|
-
//# sourceMappingURL=chunk-
|
|
446
|
+
//# sourceMappingURL=chunk-LD222Y4U.js.map
|