@xdevops/issue-auto-finish 1.0.68 → 1.0.69
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/{chunk-O5KLWME7.js → chunk-DEM52VS6.js} +2 -2
- package/dist/{chunk-YQ6EL6PW.js → chunk-EYGZJYIO.js} +1 -1
- package/dist/{chunk-4EGXVU5K.js → chunk-RPL2X7KB.js} +250 -12
- package/dist/chunk-RPL2X7KB.js.map +1 -0
- package/dist/cli.js +3 -3
- package/dist/e2e/E2ESetupRunner.d.ts +28 -0
- package/dist/e2e/E2ESetupRunner.d.ts.map +1 -0
- package/dist/index.js +2 -2
- package/dist/{init-GL24QXK3.js → init-G75RO7SX.js} +2 -2
- package/dist/{restart-L4W2TYQL.js → restart-2K7RHMYC.js} +2 -2
- package/dist/run.js +2 -2
- package/dist/{start-BHURMPBK.js → start-NVOOEF2A.js} +2 -2
- package/dist/web/routes/setup.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/web/frontend/dist/assets/index-B8u7dV-R.js +127 -0
- package/src/web/frontend/dist/assets/index-BQKHLcAS.css +1 -0
- package/src/web/frontend/dist/index.html +2 -2
- package/dist/chunk-4EGXVU5K.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/{chunk-O5KLWME7.js.map → chunk-DEM52VS6.js.map} +0 -0
- /package/dist/{chunk-YQ6EL6PW.js.map → chunk-EYGZJYIO.js.map} +0 -0
- /package/dist/{init-GL24QXK3.js.map → init-G75RO7SX.js.map} +0 -0
- /package/dist/{restart-L4W2TYQL.js.map → restart-2K7RHMYC.js.map} +0 -0
- /package/dist/{start-BHURMPBK.js.map → start-NVOOEF2A.js.map} +0 -0
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-Z75EDBWI.js";
|
|
5
5
|
import {
|
|
6
6
|
createSetupRouter
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-RPL2X7KB.js";
|
|
8
8
|
import {
|
|
9
9
|
buildLockNoteBody,
|
|
10
10
|
buildReleaseNoteBody,
|
|
@@ -8714,4 +8714,4 @@ function migrateKnowledgeDir(srcDir, destDir) {
|
|
|
8714
8714
|
export {
|
|
8715
8715
|
main
|
|
8716
8716
|
};
|
|
8717
|
-
//# sourceMappingURL=chunk-
|
|
8717
|
+
//# sourceMappingURL=chunk-DEM52VS6.js.map
|
|
@@ -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-SONIIB3D.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 });
|
|
@@ -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-RPL2X7KB.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"]}
|
package/dist/cli.js
CHANGED
|
@@ -85,11 +85,11 @@ var defaults = getCliDefaults();
|
|
|
85
85
|
var program = new Command();
|
|
86
86
|
program.name("issue-auto-finish").description("Issue Auto-Finish: AI-powered issue resolution daemon").version(pkg.version, "-v, --version");
|
|
87
87
|
program.command("init").description("Launch the interactive web setup wizard").option("-p, --port <port>", "Port for the setup wizard", "3456").option("-c, --config <path>", "Config file path to generate").action(async (opts) => {
|
|
88
|
-
const { initCommand } = await import("./init-
|
|
88
|
+
const { initCommand } = await import("./init-G75RO7SX.js");
|
|
89
89
|
await initCommand({ port: parseInt(opts.port, 10), config: opts.config });
|
|
90
90
|
});
|
|
91
91
|
program.command("start").description("Start the issue-auto-finish daemon service").option("-c, --config <path>", "Path to .env config file").option("-d, --daemon", "Run as a background daemon").action(async (opts) => {
|
|
92
|
-
const { startCommand } = await import("./start-
|
|
92
|
+
const { startCommand } = await import("./start-NVOOEF2A.js");
|
|
93
93
|
await startCommand({ config: opts.config, daemon: opts.daemon });
|
|
94
94
|
});
|
|
95
95
|
program.command("stop").description("Stop the running service").option("-p, --port <port>", "Web UI port", defaults.port).option("-H, --host <host>", "Service host", defaults.host).option("-f, --force", "Force kill via SIGKILL").action(async (opts) => {
|
|
@@ -101,7 +101,7 @@ program.command("stop").description("Stop the running service").option("-p, --po
|
|
|
101
101
|
});
|
|
102
102
|
});
|
|
103
103
|
program.command("restart").description("Restart the service (stop + start)").option("-c, --config <path>", "Path to .env config file").option("-p, --port <port>", "Web UI port", defaults.port).option("-H, --host <host>", "Service host", defaults.host).option("-d, --daemon", "Restart as a background daemon").action(async (opts) => {
|
|
104
|
-
const { restartCommand } = await import("./restart-
|
|
104
|
+
const { restartCommand } = await import("./restart-2K7RHMYC.js");
|
|
105
105
|
await restartCommand({
|
|
106
106
|
config: opts.config,
|
|
107
107
|
host: opts.host,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface E2ESetupEvent {
|
|
2
|
+
step: string;
|
|
3
|
+
status?: 'running' | 'done' | 'failed';
|
|
4
|
+
message?: string;
|
|
5
|
+
log?: string;
|
|
6
|
+
result?: Record<string, string>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* One-click E2E UAT environment setup runner.
|
|
10
|
+
* Yields SSE events for each step of the setup process.
|
|
11
|
+
*/
|
|
12
|
+
export declare class E2ESetupRunner {
|
|
13
|
+
private readonly projectRoot;
|
|
14
|
+
private readonly vendorDir;
|
|
15
|
+
private readonly repoUrl;
|
|
16
|
+
private readonly pythonCandidates;
|
|
17
|
+
constructor(projectRoot: string);
|
|
18
|
+
run(): AsyncGenerator<E2ESetupEvent>;
|
|
19
|
+
private cloneOrUpdate;
|
|
20
|
+
private detectPython;
|
|
21
|
+
private installPlaywright;
|
|
22
|
+
private installChromium;
|
|
23
|
+
private configure;
|
|
24
|
+
private reloadConfig;
|
|
25
|
+
private runProcess;
|
|
26
|
+
private patchEnvFlag;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=E2ESetupRunner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E2ESetupRunner.d.ts","sourceRoot":"","sources":["../../src/e2e/E2ESetupRunner.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;;GAGG;AACH,qBAAa,cAAc;IAOb,OAAO,CAAC,QAAQ,CAAC,WAAW;IANxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;IACnE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAE/B;gBAE2B,WAAW,EAAE,MAAM;IAIzC,GAAG,IAAI,cAAc,CAAC,aAAa,CAAC;YAqB5B,aAAa;YAoCb,YAAY;YAkCZ,iBAAiB;YA0BjB,eAAe;YAmBf,SAAS;YAmCT,YAAY;YAeZ,UAAU;IAoDzB,OAAO,CAAC,YAAY;CAWrB"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
main
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DEM52VS6.js";
|
|
4
4
|
import "./chunk-Z75EDBWI.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-RPL2X7KB.js";
|
|
6
6
|
import "./chunk-VFTESEYG.js";
|
|
7
7
|
import "./chunk-JUXD7ZBK.js";
|
|
8
8
|
import "./chunk-VJD6TIKY.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createSetupRouter
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-RPL2X7KB.js";
|
|
4
4
|
import "./chunk-JUXD7ZBK.js";
|
|
5
5
|
import "./chunk-ACVOOHAR.js";
|
|
6
6
|
import "./chunk-2QHSU5YR.js";
|
|
@@ -84,4 +84,4 @@ async function initCommand(options) {
|
|
|
84
84
|
export {
|
|
85
85
|
initCommand
|
|
86
86
|
};
|
|
87
|
-
//# sourceMappingURL=init-
|
|
87
|
+
//# sourceMappingURL=init-G75RO7SX.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
startCommand
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-EYGZJYIO.js";
|
|
4
4
|
import {
|
|
5
5
|
stopCommand
|
|
6
6
|
} from "./chunk-YF2SNJQL.js";
|
|
@@ -41,4 +41,4 @@ async function restartCommand(options) {
|
|
|
41
41
|
export {
|
|
42
42
|
restartCommand
|
|
43
43
|
};
|
|
44
|
-
//# sourceMappingURL=restart-
|
|
44
|
+
//# sourceMappingURL=restart-2K7RHMYC.js.map
|
package/dist/run.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
main
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DEM52VS6.js";
|
|
4
4
|
import "./chunk-Z75EDBWI.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-RPL2X7KB.js";
|
|
6
6
|
import "./chunk-VFTESEYG.js";
|
|
7
7
|
import "./chunk-JUXD7ZBK.js";
|
|
8
8
|
import "./chunk-VJD6TIKY.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
startCommand
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-EYGZJYIO.js";
|
|
4
4
|
import "./chunk-GXRFUJTN.js";
|
|
5
5
|
import "./chunk-XCHQMDNZ.js";
|
|
6
6
|
import "./chunk-TN2SYADO.js";
|
|
@@ -9,4 +9,4 @@ import "./chunk-GF2RRYHB.js";
|
|
|
9
9
|
export {
|
|
10
10
|
startCommand
|
|
11
11
|
};
|
|
12
|
-
//# sourceMappingURL=start-
|
|
12
|
+
//# sourceMappingURL=start-NVOOEF2A.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/web/routes/setup.ts"],"names":[],"mappings":"AAAA,OAAO,OAAwC,MAAM,SAAS,CAAC;AAC/D,QAAA,MAAQ,MAAM,uBAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/web/routes/setup.ts"],"names":[],"mappings":"AAAA,OAAO,OAAwC,MAAM,SAAS,CAAC;AAC/D,QAAA,MAAQ,MAAM,uBAAY,CAAC;AAU3B,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAoCD,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,eAAoB,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CA0RvF"}
|