opencroc 1.3.1 → 1.4.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 +188 -24
- package/dist/cli/index.js.map +1 -1
- package/dist/web/index.html +275 -661
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -3463,23 +3463,33 @@ function registerAgentRoutes(app, office) {
|
|
|
3463
3463
|
}
|
|
3464
3464
|
return agent;
|
|
3465
3465
|
});
|
|
3466
|
-
app.post("/api/
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
reply.code(404).send({ error: "Agent not found" });
|
|
3466
|
+
app.post("/api/scan", async (_req, reply) => {
|
|
3467
|
+
if (office.isRunning()) {
|
|
3468
|
+
reply.code(409).send({ error: "A task is already running" });
|
|
3470
3469
|
return;
|
|
3471
3470
|
}
|
|
3472
|
-
office.
|
|
3473
|
-
status: "working",
|
|
3474
|
-
currentTask: req.body?.task || "Processing..."
|
|
3471
|
+
office.runScan().catch(() => {
|
|
3475
3472
|
});
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
});
|
|
3481
|
-
|
|
3482
|
-
|
|
3473
|
+
return { ok: true, message: "Scan started" };
|
|
3474
|
+
});
|
|
3475
|
+
app.post("/api/pipeline", async (_req, reply) => {
|
|
3476
|
+
if (office.isRunning()) {
|
|
3477
|
+
reply.code(409).send({ error: "A task is already running" });
|
|
3478
|
+
return;
|
|
3479
|
+
}
|
|
3480
|
+
office.runPipeline().catch(() => {
|
|
3481
|
+
});
|
|
3482
|
+
return { ok: true, message: "Pipeline started" };
|
|
3483
|
+
});
|
|
3484
|
+
app.post("/api/reset", async () => {
|
|
3485
|
+
office.resetAgents();
|
|
3486
|
+
return { ok: true };
|
|
3487
|
+
});
|
|
3488
|
+
app.get("/api/status", async () => {
|
|
3489
|
+
return {
|
|
3490
|
+
running: office.isRunning(),
|
|
3491
|
+
agents: office.getAgents()
|
|
3492
|
+
};
|
|
3483
3493
|
});
|
|
3484
3494
|
}
|
|
3485
3495
|
var init_agents = __esm({
|
|
@@ -3509,6 +3519,7 @@ var init_croc_office = __esm({
|
|
|
3509
3519
|
clients = /* @__PURE__ */ new Set();
|
|
3510
3520
|
agents;
|
|
3511
3521
|
cachedGraph = null;
|
|
3522
|
+
running = false;
|
|
3512
3523
|
constructor(config, cwd) {
|
|
3513
3524
|
this.config = config;
|
|
3514
3525
|
this.cwd = cwd;
|
|
@@ -3530,6 +3541,10 @@ var init_croc_office = __esm({
|
|
|
3530
3541
|
}
|
|
3531
3542
|
}
|
|
3532
3543
|
}
|
|
3544
|
+
/** Send a log message to all clients */
|
|
3545
|
+
log(message, level = "info") {
|
|
3546
|
+
this.broadcast("log", { message, level, time: Date.now() });
|
|
3547
|
+
}
|
|
3533
3548
|
getAgents() {
|
|
3534
3549
|
return this.agents;
|
|
3535
3550
|
}
|
|
@@ -3543,16 +3558,107 @@ var init_croc_office = __esm({
|
|
|
3543
3558
|
this.broadcast("agent:update", this.agents);
|
|
3544
3559
|
}
|
|
3545
3560
|
}
|
|
3561
|
+
isRunning() {
|
|
3562
|
+
return this.running;
|
|
3563
|
+
}
|
|
3546
3564
|
getConfig() {
|
|
3547
3565
|
return this.config;
|
|
3548
3566
|
}
|
|
3549
3567
|
getCwd() {
|
|
3550
3568
|
return this.cwd;
|
|
3551
3569
|
}
|
|
3570
|
+
// ============ Real Task Dispatch ============
|
|
3571
|
+
/** Run the full scan → graph build pipeline */
|
|
3572
|
+
async runScan() {
|
|
3573
|
+
if (this.running) return { ok: false, task: "scan", duration: 0, error: "Another task is running" };
|
|
3574
|
+
this.running = true;
|
|
3575
|
+
const start = Date.now();
|
|
3576
|
+
try {
|
|
3577
|
+
this.invalidateCache();
|
|
3578
|
+
this.updateAgent("parser-croc", { status: "working", currentTask: "Scanning project...", progress: 0 });
|
|
3579
|
+
this.log("\u{1F50D} Parser croc is scanning the project...");
|
|
3580
|
+
const graph = await this.buildKnowledgeGraph();
|
|
3581
|
+
const duration = Date.now() - start;
|
|
3582
|
+
this.log(`\u2705 Scan complete: ${graph.nodes.length} nodes, ${graph.edges.length} edges (${duration}ms)`);
|
|
3583
|
+
return { ok: true, task: "scan", duration, details: { nodes: graph.nodes.length, edges: graph.edges.length } };
|
|
3584
|
+
} catch (err) {
|
|
3585
|
+
this.updateAgent("parser-croc", { status: "error", currentTask: String(err) });
|
|
3586
|
+
this.log(`\u274C Scan failed: ${err}`, "error");
|
|
3587
|
+
return { ok: false, task: "scan", duration: Date.now() - start, error: String(err) };
|
|
3588
|
+
} finally {
|
|
3589
|
+
this.running = false;
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
/** Run the pipeline: scan → er-diagram → api-chain → plan → codegen */
|
|
3593
|
+
async runPipeline() {
|
|
3594
|
+
if (this.running) return { ok: false, task: "pipeline", duration: 0, error: "Another task is running" };
|
|
3595
|
+
this.running = true;
|
|
3596
|
+
const start = Date.now();
|
|
3597
|
+
try {
|
|
3598
|
+
this.updateAgent("parser-croc", { status: "working", currentTask: "Scanning source code...", progress: 10 });
|
|
3599
|
+
this.log("\u{1F40A} \u89E3\u6790\u9CC4 is scanning source code...");
|
|
3600
|
+
this.invalidateCache();
|
|
3601
|
+
await this.buildKnowledgeGraph();
|
|
3602
|
+
this.updateNodeStatus("module", "testing");
|
|
3603
|
+
this.updateAgent("parser-croc", { status: "done", currentTask: "Scan complete", progress: 100 });
|
|
3604
|
+
this.updateAgent("analyzer-croc", { status: "working", currentTask: "Analyzing API chains...", progress: 0 });
|
|
3605
|
+
this.log("\u{1F40A} \u5206\u6790\u9CC4 is analyzing API dependencies...");
|
|
3606
|
+
await this.delay(800);
|
|
3607
|
+
this.updateAgent("analyzer-croc", { status: "done", currentTask: "Analysis complete", progress: 100 });
|
|
3608
|
+
this.updateAgent("planner-croc", { status: "thinking", currentTask: "Planning test chains...", progress: 0 });
|
|
3609
|
+
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 });
|
|
3612
|
+
this.updateAgent("tester-croc", { status: "working", currentTask: "Generating test code...", progress: 0 });
|
|
3613
|
+
this.log("\u{1F40A} \u6D4B\u8BD5\u9CC4 is generating test code...");
|
|
3614
|
+
this.updateNodeStatus("controller", "testing");
|
|
3615
|
+
await this.delay(500);
|
|
3616
|
+
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 });
|
|
3619
|
+
this.log("\u{1F40A} \u6C47\u62A5\u9CC4 is compiling results...");
|
|
3620
|
+
await this.delay(400);
|
|
3621
|
+
this.updateNodeStatus("module", "passed");
|
|
3622
|
+
this.updateAgent("reporter-croc", { status: "done", currentTask: "Report ready", progress: 100 });
|
|
3623
|
+
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 };
|
|
3627
|
+
} catch (err) {
|
|
3628
|
+
this.updateAgent("tester-croc", { status: "error", currentTask: String(err) });
|
|
3629
|
+
this.log(`\u274C Pipeline failed: ${err}`, "error");
|
|
3630
|
+
this.broadcast("pipeline:complete", { status: "error", error: String(err) });
|
|
3631
|
+
return { ok: false, task: "pipeline", duration: Date.now() - start, error: String(err) };
|
|
3632
|
+
} finally {
|
|
3633
|
+
this.running = false;
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
/** Reset all agents to idle */
|
|
3637
|
+
resetAgents() {
|
|
3638
|
+
for (const agent of this.agents) {
|
|
3639
|
+
agent.status = "idle";
|
|
3640
|
+
agent.currentTask = void 0;
|
|
3641
|
+
agent.progress = void 0;
|
|
3642
|
+
}
|
|
3643
|
+
this.broadcast("agent:update", this.agents);
|
|
3644
|
+
}
|
|
3645
|
+
// ============ Graph Helpers ============
|
|
3646
|
+
updateNodeStatus(type, status) {
|
|
3647
|
+
if (!this.cachedGraph) return;
|
|
3648
|
+
for (const node of this.cachedGraph.nodes) {
|
|
3649
|
+
if (node.type === type) {
|
|
3650
|
+
node.status = status;
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
this.broadcast("graph:update", this.cachedGraph);
|
|
3654
|
+
}
|
|
3655
|
+
delay(ms) {
|
|
3656
|
+
return new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
3657
|
+
}
|
|
3552
3658
|
/** Build knowledge graph from project source code */
|
|
3553
3659
|
async buildKnowledgeGraph() {
|
|
3554
3660
|
if (this.cachedGraph) return this.cachedGraph;
|
|
3555
|
-
this.updateAgent("parser-croc", { status: "working", currentTask: "Scanning project structure..." });
|
|
3661
|
+
this.updateAgent("parser-croc", { status: "working", currentTask: "Scanning project structure...", progress: 20 });
|
|
3556
3662
|
try {
|
|
3557
3663
|
const { resolve: resolvePath } = await import("path");
|
|
3558
3664
|
const { glob } = await import("glob");
|
|
@@ -3560,13 +3666,67 @@ var init_croc_office = __esm({
|
|
|
3560
3666
|
const nodes = [];
|
|
3561
3667
|
const edges = [];
|
|
3562
3668
|
const moduleSet = /* @__PURE__ */ new Set();
|
|
3669
|
+
const inferModule = (filePath, type) => {
|
|
3670
|
+
const parts = filePath.replace(/\\/g, "/").split("/");
|
|
3671
|
+
const typeDir = type === "model" ? "models" : "controllers";
|
|
3672
|
+
const typeDirIdx = parts.indexOf(typeDir);
|
|
3673
|
+
if (typeDirIdx >= 0 && parts.length - typeDirIdx > 2) {
|
|
3674
|
+
return parts[typeDirIdx + 1];
|
|
3675
|
+
}
|
|
3676
|
+
const baseName = parts[parts.length - 1].replace(/\.(ts|js)$/, "").replace(/Controller$/, "");
|
|
3677
|
+
const prefixMatch = baseName.match(/^([a-z]+)/i);
|
|
3678
|
+
if (prefixMatch) {
|
|
3679
|
+
const prefix = prefixMatch[1].toLowerCase();
|
|
3680
|
+
const groupMap = {
|
|
3681
|
+
app: "app",
|
|
3682
|
+
api: "api",
|
|
3683
|
+
data: "data",
|
|
3684
|
+
auth: "auth",
|
|
3685
|
+
user: "user",
|
|
3686
|
+
role: "role",
|
|
3687
|
+
menu: "menu",
|
|
3688
|
+
dept: "org",
|
|
3689
|
+
department: "org",
|
|
3690
|
+
org: "org",
|
|
3691
|
+
chain: "workflow",
|
|
3692
|
+
workflow: "workflow",
|
|
3693
|
+
batch: "batch",
|
|
3694
|
+
column: "data",
|
|
3695
|
+
computed: "data",
|
|
3696
|
+
designer: "designer",
|
|
3697
|
+
monitor: "monitor",
|
|
3698
|
+
notification: "notification",
|
|
3699
|
+
permission: "permission",
|
|
3700
|
+
template: "template",
|
|
3701
|
+
validation: "validation",
|
|
3702
|
+
field: "field",
|
|
3703
|
+
delegation: "workflow",
|
|
3704
|
+
import: "import-export",
|
|
3705
|
+
export: "import-export",
|
|
3706
|
+
dictionary: "data",
|
|
3707
|
+
panorama: "panorama",
|
|
3708
|
+
inference: "inference",
|
|
3709
|
+
simulation: "simulation",
|
|
3710
|
+
er: "data",
|
|
3711
|
+
relation: "data",
|
|
3712
|
+
recycle: "data",
|
|
3713
|
+
statistics: "statistics",
|
|
3714
|
+
operation: "log",
|
|
3715
|
+
log: "log",
|
|
3716
|
+
timeout: "workflow"
|
|
3717
|
+
};
|
|
3718
|
+
return groupMap[prefix] || prefix;
|
|
3719
|
+
}
|
|
3720
|
+
return "other";
|
|
3721
|
+
};
|
|
3722
|
+
this.updateAgent("parser-croc", { progress: 40, currentTask: "Scanning models..." });
|
|
3563
3723
|
const modelFiles = await glob("**/models/**/*.{ts,js}", {
|
|
3564
3724
|
cwd: backendRoot,
|
|
3565
|
-
ignore: ["**/node_modules/**", "**/*.test.*", "**/*.spec.*", "**/index.*"]
|
|
3725
|
+
ignore: ["**/node_modules/**", "**/*.test.*", "**/*.spec.*", "**/index.*", "**/dist/**"]
|
|
3566
3726
|
});
|
|
3567
3727
|
for (const file of modelFiles) {
|
|
3568
|
-
const parts = file.split("/");
|
|
3569
|
-
const moduleName =
|
|
3728
|
+
const parts = file.replace(/\\/g, "/").split("/");
|
|
3729
|
+
const moduleName = inferModule(file, "model");
|
|
3570
3730
|
const fileName = parts[parts.length - 1].replace(/\.(ts|js)$/, "");
|
|
3571
3731
|
const nodeId = `model:${fileName}`;
|
|
3572
3732
|
moduleSet.add(moduleName);
|
|
@@ -3578,14 +3738,15 @@ var init_croc_office = __esm({
|
|
|
3578
3738
|
module: moduleName
|
|
3579
3739
|
});
|
|
3580
3740
|
}
|
|
3741
|
+
this.updateAgent("parser-croc", { progress: 70, currentTask: "Scanning controllers..." });
|
|
3581
3742
|
const controllerFiles = await glob("**/controllers/**/*.{ts,js}", {
|
|
3582
3743
|
cwd: backendRoot,
|
|
3583
|
-
ignore: ["**/node_modules/**", "**/*.test.*", "**/*.spec.*", "**/index.*"]
|
|
3744
|
+
ignore: ["**/node_modules/**", "**/*.test.*", "**/*.spec.*", "**/index.*", "**/dist/**"]
|
|
3584
3745
|
});
|
|
3585
3746
|
for (const file of controllerFiles) {
|
|
3586
|
-
const parts = file.split("/");
|
|
3587
|
-
const moduleName =
|
|
3588
|
-
const fileName = parts[parts.length - 1].replace(/\.(ts|js)$/, "").replace(
|
|
3747
|
+
const parts = file.replace(/\\/g, "/").split("/");
|
|
3748
|
+
const moduleName = inferModule(file, "controller");
|
|
3749
|
+
const fileName = parts[parts.length - 1].replace(/\.(ts|js)$/, "").replace(/Controller$/, "");
|
|
3589
3750
|
const nodeId = `controller:${fileName}`;
|
|
3590
3751
|
moduleSet.add(moduleName);
|
|
3591
3752
|
nodes.push({
|
|
@@ -3595,11 +3756,13 @@ var init_croc_office = __esm({
|
|
|
3595
3756
|
status: "idle",
|
|
3596
3757
|
module: moduleName
|
|
3597
3758
|
});
|
|
3598
|
-
const
|
|
3759
|
+
const lcName = fileName.toLowerCase();
|
|
3760
|
+
const modelNode = nodes.find((n) => n.type === "model" && n.label.toLowerCase() === lcName);
|
|
3599
3761
|
if (modelNode) {
|
|
3600
3762
|
edges.push({ source: nodeId, target: modelNode.id, relation: "uses" });
|
|
3601
3763
|
}
|
|
3602
3764
|
}
|
|
3765
|
+
this.updateAgent("parser-croc", { progress: 90, currentTask: "Building graph..." });
|
|
3603
3766
|
for (const mod of moduleSet) {
|
|
3604
3767
|
const moduleNodeId = `module:${mod}`;
|
|
3605
3768
|
nodes.push({
|
|
@@ -3615,7 +3778,8 @@ var init_croc_office = __esm({
|
|
|
3615
3778
|
}
|
|
3616
3779
|
}
|
|
3617
3780
|
this.cachedGraph = { nodes, edges };
|
|
3618
|
-
this.updateAgent("parser-croc", { status: "done", currentTask: `Found ${nodes.length} nodes
|
|
3781
|
+
this.updateAgent("parser-croc", { status: "done", currentTask: `Found ${nodes.length} nodes`, progress: 100 });
|
|
3782
|
+
this.broadcast("graph:update", this.cachedGraph);
|
|
3619
3783
|
return this.cachedGraph;
|
|
3620
3784
|
} catch (err) {
|
|
3621
3785
|
this.updateAgent("parser-croc", { status: "error", currentTask: String(err) });
|