opencroc 1.4.3 → 1.5.1

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/cli/index.js CHANGED
@@ -1270,6 +1270,10 @@ var init_config_validator = __esm({
1270
1270
  });
1271
1271
 
1272
1272
  // src/pipeline/index.ts
1273
+ var pipeline_exports = {};
1274
+ __export(pipeline_exports, {
1275
+ createPipeline: () => createPipeline
1276
+ });
1273
1277
  import * as fs4 from "fs";
1274
1278
  import * as path5 from "path";
1275
1279
  function createPipeline(config) {
@@ -1285,11 +1289,32 @@ function createPipeline(config) {
1285
1289
  validationErrors: [],
1286
1290
  duration: 0
1287
1291
  };
1292
+ const backendRoot = path5.resolve(config.backendRoot);
1293
+ const findDir = (name) => {
1294
+ const candidates = [
1295
+ path5.join(backendRoot, name),
1296
+ // ./models
1297
+ path5.join(backendRoot, "src", name),
1298
+ // ./src/models
1299
+ path5.join(backendRoot, "backend", "src", name),
1300
+ // ./backend/src/models
1301
+ path5.join(backendRoot, "backend", name),
1302
+ // ./backend/models
1303
+ path5.join(backendRoot, "server", "src", name),
1304
+ // ./server/src/models
1305
+ path5.join(backendRoot, "app", name)
1306
+ // ./app/models
1307
+ ];
1308
+ for (const c of candidates) {
1309
+ if (fs4.existsSync(c)) return c;
1310
+ }
1311
+ return null;
1312
+ };
1313
+ const modelsRoot = findDir("models");
1314
+ const controllersRoot = findDir("controllers");
1288
1315
  if (activeSteps.includes("scan")) {
1289
- const backendRoot = path5.resolve(config.backendRoot);
1290
- const modelsDir = path5.join(backendRoot, "models");
1291
- if (fs4.existsSync(modelsDir)) {
1292
- const dirs = fs4.readdirSync(modelsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
1316
+ if (modelsRoot) {
1317
+ const dirs = fs4.readdirSync(modelsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
1293
1318
  const moduleFilter = config.modules;
1294
1319
  for (const dir of dirs) {
1295
1320
  if (moduleFilter && !moduleFilter.includes(dir)) continue;
@@ -1298,20 +1323,20 @@ function createPipeline(config) {
1298
1323
  if (result.modules.length === 0) {
1299
1324
  result.modules.push("default");
1300
1325
  } else {
1301
- const rootFiles = fs4.readdirSync(modelsDir).filter((f) => f.endsWith(".ts") && !f.endsWith(".test.ts") && f !== "index.ts");
1326
+ const rootFiles = fs4.readdirSync(modelsRoot).filter((f) => f.endsWith(".ts") && !f.endsWith(".test.ts") && f !== "index.ts");
1302
1327
  if (rootFiles.length > 0) {
1303
1328
  result.modules.unshift("default");
1304
1329
  }
1305
1330
  }
1306
1331
  }
1307
1332
  }
1308
- const resolveModelDir = (backendRoot, mod) => mod === "default" ? path5.join(backendRoot, "models") : path5.join(backendRoot, "models", mod);
1309
- const resolveControllerDir = (backendRoot, mod) => mod === "default" ? path5.join(backendRoot, "controllers") : path5.join(backendRoot, "controllers", mod);
1333
+ const resolveModelDir = (_backendRoot, mod) => mod === "default" ? modelsRoot || path5.join(backendRoot, "models") : path5.join(modelsRoot || path5.join(backendRoot, "models"), mod);
1334
+ const resolveControllerDir = (_backendRoot, mod) => mod === "default" ? controllersRoot || path5.join(backendRoot, "controllers") : path5.join(controllersRoot || path5.join(backendRoot, "controllers"), mod);
1310
1335
  if (activeSteps.includes("er-diagram")) {
1311
1336
  const erGen = createERDiagramGenerator();
1312
- const backendRoot = path5.resolve(config.backendRoot);
1337
+ const backendRoot2 = path5.resolve(config.backendRoot);
1313
1338
  for (const mod of result.modules) {
1314
- const modelDir = resolveModelDir(backendRoot, mod);
1339
+ const modelDir = resolveModelDir(backendRoot2, mod);
1315
1340
  const tables = fs4.existsSync(modelDir) ? parseModuleModels(modelDir) : [];
1316
1341
  const relations = [];
1317
1342
  const assocFile = path5.join(modelDir, "associations.ts");
@@ -1334,9 +1359,9 @@ function createPipeline(config) {
1334
1359
  }
1335
1360
  if (activeSteps.includes("api-chain")) {
1336
1361
  const chainAnalyzer = createApiChainAnalyzer();
1337
- const backendRoot = path5.resolve(config.backendRoot);
1362
+ const backendRoot2 = path5.resolve(config.backendRoot);
1338
1363
  for (const mod of result.modules) {
1339
- const controllerDir = resolveControllerDir(backendRoot, mod);
1364
+ const controllerDir = resolveControllerDir(backendRoot2, mod);
1340
1365
  const endpoints = fs4.existsSync(controllerDir) ? parseControllerDirectory(controllerDir) : [];
1341
1366
  const analysis = chainAnalyzer.analyze(endpoints);
1342
1367
  analysis.moduleName = mod;
@@ -1353,10 +1378,10 @@ function createPipeline(config) {
1353
1378
  }
1354
1379
  }
1355
1380
  if (activeSteps.includes("plan")) {
1356
- const backendRoot = path5.resolve(config.backendRoot);
1381
+ const backendRoot2 = path5.resolve(config.backendRoot);
1357
1382
  const chainAnalyzer = createApiChainAnalyzer();
1358
1383
  for (const mod of result.modules) {
1359
- const controllerDir = resolveControllerDir(backendRoot, mod);
1384
+ const controllerDir = resolveControllerDir(backendRoot2, mod);
1360
1385
  const endpoints = fs4.existsSync(controllerDir) ? parseControllerDirectory(controllerDir) : [];
1361
1386
  const analysis = chainAnalyzer.analyze(endpoints);
1362
1387
  const topoOrder = topologicalSort(analysis.dag);
@@ -3491,6 +3516,39 @@ function registerAgentRoutes(app, office) {
3491
3516
  agents: office.getAgents()
3492
3517
  };
3493
3518
  });
3519
+ app.get("/api/files", async () => {
3520
+ const files = office.getGeneratedFiles();
3521
+ return files.map((f) => ({
3522
+ filePath: f.filePath,
3523
+ module: f.module,
3524
+ chain: f.chain,
3525
+ lines: f.content.split("\n").length,
3526
+ size: f.content.length
3527
+ }));
3528
+ });
3529
+ app.get("/api/files/:index", async (req, reply) => {
3530
+ const files = office.getGeneratedFiles();
3531
+ const idx = parseInt(req.params.index, 10);
3532
+ if (isNaN(idx) || idx < 0 || idx >= files.length) {
3533
+ reply.code(404).send({ error: "File not found" });
3534
+ return;
3535
+ }
3536
+ return files[idx];
3537
+ });
3538
+ app.get("/api/pipeline/result", async () => {
3539
+ const result = office.getLastPipelineResult();
3540
+ if (!result) return { ok: false, message: "No pipeline has been run yet" };
3541
+ return {
3542
+ ok: true,
3543
+ modules: result.modules,
3544
+ erDiagramCount: result.erDiagrams.size,
3545
+ chainCount: [...result.chainPlans.values()].reduce((s, p) => s + p.chains.length, 0),
3546
+ totalSteps: [...result.chainPlans.values()].reduce((s, p) => s + p.totalSteps, 0),
3547
+ filesGenerated: result.generatedFiles.length,
3548
+ validationErrors: result.validationErrors.length,
3549
+ duration: result.duration
3550
+ };
3551
+ });
3494
3552
  }
3495
3553
  var init_agents = __esm({
3496
3554
  "src/server/routes/agents.ts"() {
@@ -3520,6 +3578,8 @@ var init_croc_office = __esm({
3520
3578
  agents;
3521
3579
  cachedGraph = null;
3522
3580
  running = false;
3581
+ lastPipelineResult = null;
3582
+ lastGeneratedFiles = [];
3523
3583
  constructor(config, cwd) {
3524
3584
  this.config = config;
3525
3585
  this.cwd = cwd;
@@ -3589,41 +3649,92 @@ var init_croc_office = __esm({
3589
3649
  this.running = false;
3590
3650
  }
3591
3651
  }
3592
- /** Run the pipeline: scan → er-diagram → api-chain → plan → codegen */
3652
+ /** Run the real pipeline: scan → er-diagram → api-chain → plan → codegen → report */
3593
3653
  async runPipeline() {
3594
3654
  if (this.running) return { ok: false, task: "pipeline", duration: 0, error: "Another task is running" };
3595
3655
  this.running = true;
3596
3656
  const start = Date.now();
3597
3657
  try {
3658
+ const { resolve: resolvePath } = await import("path");
3659
+ const { createPipeline: createPipeline2 } = await Promise.resolve().then(() => (init_pipeline(), pipeline_exports));
3660
+ const backendRoot = resolvePath(this.cwd, this.config.backendRoot);
3661
+ const pipelineConfig = { ...this.config, backendRoot };
3662
+ const pipeline = createPipeline2(pipelineConfig);
3598
3663
  this.updateAgent("parser-croc", { status: "working", currentTask: "Scanning source code...", progress: 10 });
3599
- this.log("\u{1F40A} \u89E3\u6790\u9CC4 is scanning source code...");
3664
+ this.log(`\u{1F40A} \u89E3\u6790\u9CC4 scanning from: ${backendRoot}`);
3600
3665
  this.invalidateCache();
3601
3666
  await this.buildKnowledgeGraph();
3602
3667
  this.updateNodeStatus("module", "testing");
3603
- this.updateAgent("parser-croc", { status: "done", currentTask: "Scan complete", progress: 100 });
3668
+ this.updateAgent("parser-croc", { currentTask: "Parsing models & ER diagrams...", progress: 40 });
3669
+ const scanResult = await pipeline.run(["scan", "er-diagram"]);
3670
+ const moduleCount = scanResult.modules.length;
3671
+ const erCount = scanResult.erDiagrams.size;
3672
+ this.log(`\u{1F4CA} Found ${moduleCount} modules, ${erCount} ER diagrams`);
3673
+ this.updateAgent("parser-croc", { status: "done", currentTask: `${moduleCount} modules parsed`, progress: 100 });
3604
3674
  this.updateAgent("analyzer-croc", { status: "working", currentTask: "Analyzing API chains...", progress: 0 });
3605
3675
  this.log("\u{1F40A} \u5206\u6790\u9CC4 is analyzing API dependencies...");
3606
- await this.delay(800);
3676
+ const analyzeResult = await pipeline.run(["api-chain"]);
3677
+ const warnings = analyzeResult.validationErrors.filter((e) => e.severity === "warning");
3678
+ if (warnings.length > 0) {
3679
+ this.log(`\u26A0\uFE0F ${warnings.length} API chain warnings`, "warn");
3680
+ }
3607
3681
  this.updateAgent("analyzer-croc", { status: "done", currentTask: "Analysis complete", progress: 100 });
3608
3682
  this.updateAgent("planner-croc", { status: "thinking", currentTask: "Planning test chains...", progress: 0 });
3609
3683
  this.log("\u{1F40A} \u89C4\u5212\u9CC4 is planning test chains...");
3610
- await this.delay(600);
3611
- this.updateAgent("planner-croc", { status: "done", currentTask: "Plan ready", progress: 100 });
3684
+ const planResult = await pipeline.run(["plan"]);
3685
+ let totalChains = 0, totalSteps = 0;
3686
+ for (const [, plan] of planResult.chainPlans) {
3687
+ totalChains += plan.chains.length;
3688
+ totalSteps += plan.totalSteps;
3689
+ }
3690
+ this.log(`\u{1F4CB} Planned ${totalChains} test chains with ${totalSteps} steps`);
3691
+ this.updateAgent("planner-croc", { status: "done", currentTask: `${totalChains} chains planned`, progress: 100 });
3612
3692
  this.updateAgent("tester-croc", { status: "working", currentTask: "Generating test code...", progress: 0 });
3613
- this.log("\u{1F40A} \u6D4B\u8BD5\u9CC4 is generating test code...");
3693
+ this.log("\u{1F40A} \u6D4B\u8BD5\u9CC4 is generating Playwright test code...");
3614
3694
  this.updateNodeStatus("controller", "testing");
3615
- await this.delay(500);
3695
+ const fullResult = await pipeline.run(["scan", "er-diagram", "api-chain", "plan", "codegen"]);
3696
+ this.lastPipelineResult = fullResult;
3697
+ this.lastGeneratedFiles = fullResult.generatedFiles;
3698
+ const { writeFileSync: writeFileSync10, mkdirSync: mkdirSync10 } = await import("fs");
3699
+ const { dirname: dirname6 } = await import("path");
3700
+ let filesWritten = 0;
3701
+ for (const file of fullResult.generatedFiles) {
3702
+ const fullPath = resolvePath(this.cwd, file.filePath);
3703
+ mkdirSync10(dirname6(fullPath), { recursive: true });
3704
+ writeFileSync10(fullPath, file.content, "utf-8");
3705
+ filesWritten++;
3706
+ }
3616
3707
  this.updateNodeStatus("controller", "passed");
3617
- this.updateAgent("tester-croc", { status: "done", currentTask: "Tests generated", progress: 100 });
3618
- this.updateAgent("reporter-croc", { status: "working", currentTask: "Building report...", progress: 0 });
3708
+ this.log(`\u2705 Generated ${filesWritten} test files`);
3709
+ this.updateAgent("tester-croc", { status: "done", currentTask: `${filesWritten} files generated`, progress: 100 });
3710
+ this.broadcast("files:generated", fullResult.generatedFiles.map((f) => ({
3711
+ filePath: f.filePath,
3712
+ module: f.module,
3713
+ chain: f.chain,
3714
+ lines: f.content.split("\n").length
3715
+ })));
3716
+ this.updateAgent("reporter-croc", { status: "working", currentTask: "Compiling report...", progress: 0 });
3619
3717
  this.log("\u{1F40A} \u6C47\u62A5\u9CC4 is compiling results...");
3620
- await this.delay(400);
3718
+ const validateResult = await pipeline.run(["validate"]);
3719
+ const errors = validateResult.validationErrors.filter((e) => e.severity === "error");
3720
+ if (errors.length > 0) {
3721
+ this.log(`\u26A0\uFE0F ${errors.length} validation errors`, "warn");
3722
+ }
3621
3723
  this.updateNodeStatus("module", "passed");
3622
3724
  this.updateAgent("reporter-croc", { status: "done", currentTask: "Report ready", progress: 100 });
3623
3725
  const duration = Date.now() - start;
3624
- this.log(`\u2705 Pipeline complete in ${duration}ms`);
3625
- this.broadcast("pipeline:complete", { duration, status: "success" });
3626
- return { ok: true, task: "pipeline", duration };
3726
+ this.log(`\u2705 Pipeline complete in ${duration}ms \u2014 ${moduleCount} modules, ${totalChains} chains, ${filesWritten} files`);
3727
+ this.broadcast("pipeline:complete", {
3728
+ duration,
3729
+ status: "success",
3730
+ summary: { modules: moduleCount, chains: totalChains, steps: totalSteps, files: filesWritten }
3731
+ });
3732
+ return { ok: true, task: "pipeline", duration, details: {
3733
+ modules: moduleCount,
3734
+ chains: totalChains,
3735
+ steps: totalSteps,
3736
+ files: filesWritten
3737
+ } };
3627
3738
  } catch (err) {
3628
3739
  this.updateAgent("tester-croc", { status: "error", currentTask: String(err) });
3629
3740
  this.log(`\u274C Pipeline failed: ${err}`, "error");
@@ -3642,6 +3753,14 @@ var init_croc_office = __esm({
3642
3753
  }
3643
3754
  this.broadcast("agent:update", this.agents);
3644
3755
  }
3756
+ /** Get last pipeline result */
3757
+ getLastPipelineResult() {
3758
+ return this.lastPipelineResult;
3759
+ }
3760
+ /** Get generated test files from last pipeline run */
3761
+ getGeneratedFiles() {
3762
+ return this.lastGeneratedFiles;
3763
+ }
3645
3764
  // ============ Graph Helpers ============
3646
3765
  updateNodeStatus(type, status) {
3647
3766
  if (!this.cachedGraph) return;
@@ -3652,9 +3771,6 @@ var init_croc_office = __esm({
3652
3771
  }
3653
3772
  this.broadcast("graph:update", this.cachedGraph);
3654
3773
  }
3655
- delay(ms) {
3656
- return new Promise((resolve9) => setTimeout(resolve9, ms));
3657
- }
3658
3774
  /** Build knowledge graph from project source code */
3659
3775
  async buildKnowledgeGraph() {
3660
3776
  if (this.cachedGraph) return this.cachedGraph;