opencroc 1.8.0 → 1.8.2
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 +1107 -49
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +128 -1
- package/dist/index.js +548 -0
- package/dist/index.js.map +1 -1
- package/dist/web/dist/assets/main-Ccg3eDNK.js +1 -0
- package/dist/web/dist/assets/office-runtime-B3iNctxE.css +1 -0
- package/dist/web/dist/assets/office-runtime-BsCh82Pj.js +183 -0
- package/dist/web/dist/assets/pixel-page-3BYGm7dH.js +470 -0
- package/dist/web/dist/assets/react-vendor-C8RhVn0h.js +49 -0
- package/dist/web/dist/assets/studio-page-BInoyoV2.css +1 -0
- package/dist/web/dist/assets/studio-page-o3SCvE_v.js +351 -0
- package/dist/web/dist/assets/three-addons-BdrPp04O.js +470 -0
- package/dist/web/dist/assets/three-core-CsxM1PCY.js +4057 -0
- package/dist/web/dist/index.html +15 -0
- package/dist/web/index.html +11 -572
- package/dist/web/public/botreview/char_0.png +0 -0
- package/dist/web/public/botreview/char_1.png +0 -0
- package/dist/web/public/botreview/char_2.png +0 -0
- package/dist/web/public/botreview/coffee-machine.gif +0 -0
- package/dist/web/public/botreview/server.gif +0 -0
- package/dist/web/public/botreview/walls.png +0 -0
- package/dist/web/public/star/desk-v3.webp +0 -0
- package/dist/web/public/star/office_bg_small.webp +0 -0
- package/dist/web/public/star/star-idle-v5.png +0 -0
- package/dist/web/public/star/star-working-spritesheet-grid.webp +0 -0
- package/dist/web/src/app/AppLayout.tsx +34 -0
- package/dist/web/src/app/AppRouter.tsx +46 -0
- package/dist/web/src/app/bootstrap.tsx +22 -0
- package/dist/web/src/app/routes.tsx +52 -0
- package/dist/web/src/features/office/runtime/index.ts +1 -0
- package/dist/web/src/features/office/runtime/mount.ts +809 -0
- package/dist/web/src/features/pixel/runtime/index.ts +1 -0
- package/dist/web/src/features/pixel/runtime/mount.ts +728 -0
- package/dist/web/src/features/studio/runtime/index.ts +1 -0
- package/dist/web/src/features/studio/runtime/mount.ts +664 -0
- package/dist/web/src/features/three/engine/index.ts +1 -0
- package/dist/web/src/main.tsx +7 -0
- package/dist/web/src/pages/office/index.ts +1 -0
- package/dist/web/src/pages/office/page.tsx +283 -0
- package/dist/web/src/pages/pixel/index.ts +1 -0
- package/dist/web/src/pages/pixel/page.tsx +564 -0
- package/dist/web/src/pages/studio/index.ts +1 -0
- package/dist/web/src/pages/studio/page.tsx +446 -0
- package/dist/web/{js/agents.js → src/runtime/agents.ts} +304 -31
- package/dist/web/{js/camera.js → src/runtime/camera.ts} +12 -5
- package/dist/web/{js/dataviz.js → src/runtime/dataviz.ts} +38 -14
- package/dist/web/{js/effects.js → src/runtime/effects.ts} +139 -2
- package/dist/web/{js/engine.js → src/runtime/engine.ts} +45 -6
- package/dist/web/{js/office.js → src/runtime/office.ts} +136 -20
- package/dist/web/{js/ui.js → src/runtime/ui.ts} +11 -7
- package/dist/web/src/shared/assets.ts +4 -0
- package/dist/web/src/shared/navigation.ts +47 -0
- package/dist/web/src/styles/app-layout.css +19 -0
- package/dist/web/src/styles/office.css +268 -0
- package/dist/web/tsconfig.json +28 -0
- package/dist/web/vite.config.ts +93 -0
- package/package.json +11 -2
- package/dist/web/index-studio.html +0 -804
- package/dist/web/index-v2-pixel.html +0 -1571
- /package/dist/web/{assets → dist}/botreview/char_0.png +0 -0
- /package/dist/web/{assets → dist}/botreview/char_1.png +0 -0
- /package/dist/web/{assets → dist}/botreview/char_2.png +0 -0
- /package/dist/web/{assets → dist}/botreview/coffee-machine.gif +0 -0
- /package/dist/web/{assets → dist}/botreview/server.gif +0 -0
- /package/dist/web/{assets → dist}/botreview/walls.png +0 -0
- /package/dist/web/{assets → dist}/star/desk-v3.webp +0 -0
- /package/dist/web/{assets → dist}/star/office_bg_small.webp +0 -0
- /package/dist/web/{assets → dist}/star/star-idle-v5.png +0 -0
- /package/dist/web/{assets → dist}/star/star-working-spritesheet-grid.webp +0 -0
- /package/dist/web/{js/state.js → src/runtime/state.ts} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -6324,8 +6324,640 @@ var init_insight = __esm({
|
|
|
6324
6324
|
}
|
|
6325
6325
|
});
|
|
6326
6326
|
|
|
6327
|
+
// src/server/studio-store.ts
|
|
6328
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync11, readFileSync as readFileSync6, writeFileSync as writeFileSync10 } from "fs";
|
|
6329
|
+
import { dirname as dirname6 } from "path";
|
|
6330
|
+
function isSerializedSnapshotFile(value) {
|
|
6331
|
+
return "snapshots" in value && Array.isArray(value.snapshots);
|
|
6332
|
+
}
|
|
6333
|
+
var EMPTY_STUDIO_STORE, FileStudioSnapshotStore;
|
|
6334
|
+
var init_studio_store = __esm({
|
|
6335
|
+
"src/server/studio-store.ts"() {
|
|
6336
|
+
"use strict";
|
|
6337
|
+
init_esm_shims();
|
|
6338
|
+
EMPTY_STUDIO_STORE = {
|
|
6339
|
+
graph: null,
|
|
6340
|
+
risks: [],
|
|
6341
|
+
scanTime: 0,
|
|
6342
|
+
source: ""
|
|
6343
|
+
};
|
|
6344
|
+
FileStudioSnapshotStore = class {
|
|
6345
|
+
filePath;
|
|
6346
|
+
maxSnapshots;
|
|
6347
|
+
constructor(filePath, maxSnapshots = 12) {
|
|
6348
|
+
this.filePath = filePath;
|
|
6349
|
+
this.maxSnapshots = maxSnapshots;
|
|
6350
|
+
}
|
|
6351
|
+
load() {
|
|
6352
|
+
const data = this.readFile();
|
|
6353
|
+
if (!data || !data.currentSnapshotId) return null;
|
|
6354
|
+
const current = data.snapshots.find((snapshot) => snapshot.id === data.currentSnapshotId);
|
|
6355
|
+
return current ? this.toProjectStore(current) : null;
|
|
6356
|
+
}
|
|
6357
|
+
save(snapshot) {
|
|
6358
|
+
const data = this.readFile() ?? { version: 2, currentSnapshotId: null, snapshots: [] };
|
|
6359
|
+
const record = this.toSnapshotRecord(snapshot);
|
|
6360
|
+
data.currentSnapshotId = record.id;
|
|
6361
|
+
data.snapshots = [record, ...data.snapshots].slice(0, this.maxSnapshots);
|
|
6362
|
+
mkdirSync11(dirname6(this.filePath), { recursive: true });
|
|
6363
|
+
writeFileSync10(this.filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
6364
|
+
}
|
|
6365
|
+
list() {
|
|
6366
|
+
const data = this.readFile();
|
|
6367
|
+
if (!data) return [];
|
|
6368
|
+
return data.snapshots.map((snapshot) => ({
|
|
6369
|
+
id: snapshot.id,
|
|
6370
|
+
name: snapshot.name,
|
|
6371
|
+
source: snapshot.source,
|
|
6372
|
+
scanTime: snapshot.scanTime,
|
|
6373
|
+
nodeCount: snapshot.graph?.nodes.length ?? 0,
|
|
6374
|
+
riskCount: snapshot.risks.length,
|
|
6375
|
+
current: snapshot.id === data.currentSnapshotId,
|
|
6376
|
+
pinned: Boolean(snapshot.pinned),
|
|
6377
|
+
tags: Array.isArray(snapshot.tags) ? snapshot.tags : []
|
|
6378
|
+
})).sort((left, right) => {
|
|
6379
|
+
if (left.pinned !== right.pinned) return left.pinned ? -1 : 1;
|
|
6380
|
+
return right.scanTime - left.scanTime;
|
|
6381
|
+
});
|
|
6382
|
+
}
|
|
6383
|
+
loadById(id) {
|
|
6384
|
+
const data = this.readFile();
|
|
6385
|
+
if (!data) return null;
|
|
6386
|
+
const record = data.snapshots.find((snapshot) => snapshot.id === id);
|
|
6387
|
+
if (!record) return null;
|
|
6388
|
+
data.currentSnapshotId = record.id;
|
|
6389
|
+
mkdirSync11(dirname6(this.filePath), { recursive: true });
|
|
6390
|
+
writeFileSync10(this.filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
6391
|
+
return this.toProjectStore(record);
|
|
6392
|
+
}
|
|
6393
|
+
rename(id, name) {
|
|
6394
|
+
const nextName = name.trim();
|
|
6395
|
+
if (!nextName) return false;
|
|
6396
|
+
const data = this.readFile();
|
|
6397
|
+
if (!data) return false;
|
|
6398
|
+
const record = data.snapshots.find((snapshot) => snapshot.id === id);
|
|
6399
|
+
if (!record) return false;
|
|
6400
|
+
record.name = nextName;
|
|
6401
|
+
mkdirSync11(dirname6(this.filePath), { recursive: true });
|
|
6402
|
+
writeFileSync10(this.filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
6403
|
+
return true;
|
|
6404
|
+
}
|
|
6405
|
+
delete(id) {
|
|
6406
|
+
const data = this.readFile();
|
|
6407
|
+
if (!data) return false;
|
|
6408
|
+
const nextSnapshots = data.snapshots.filter((snapshot) => snapshot.id !== id);
|
|
6409
|
+
if (nextSnapshots.length === data.snapshots.length) return false;
|
|
6410
|
+
data.snapshots = nextSnapshots;
|
|
6411
|
+
if (data.currentSnapshotId === id) {
|
|
6412
|
+
data.currentSnapshotId = nextSnapshots[0]?.id ?? null;
|
|
6413
|
+
}
|
|
6414
|
+
mkdirSync11(dirname6(this.filePath), { recursive: true });
|
|
6415
|
+
writeFileSync10(this.filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
6416
|
+
return true;
|
|
6417
|
+
}
|
|
6418
|
+
pin(id, pinned) {
|
|
6419
|
+
const data = this.readFile();
|
|
6420
|
+
if (!data) return false;
|
|
6421
|
+
const record = data.snapshots.find((snapshot) => snapshot.id === id);
|
|
6422
|
+
if (!record) return false;
|
|
6423
|
+
record.pinned = pinned;
|
|
6424
|
+
mkdirSync11(dirname6(this.filePath), { recursive: true });
|
|
6425
|
+
writeFileSync10(this.filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
6426
|
+
return true;
|
|
6427
|
+
}
|
|
6428
|
+
updateTags(id, tags) {
|
|
6429
|
+
const data = this.readFile();
|
|
6430
|
+
if (!data) return false;
|
|
6431
|
+
const record = data.snapshots.find((snapshot) => snapshot.id === id);
|
|
6432
|
+
if (!record) return false;
|
|
6433
|
+
record.tags = this.normalizeTags(tags);
|
|
6434
|
+
mkdirSync11(dirname6(this.filePath), { recursive: true });
|
|
6435
|
+
writeFileSync10(this.filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
6436
|
+
return true;
|
|
6437
|
+
}
|
|
6438
|
+
readFile() {
|
|
6439
|
+
if (!existsSync18(this.filePath)) return null;
|
|
6440
|
+
try {
|
|
6441
|
+
const raw = readFileSync6(this.filePath, "utf-8");
|
|
6442
|
+
const parsed = JSON.parse(raw);
|
|
6443
|
+
return this.normalize(parsed);
|
|
6444
|
+
} catch {
|
|
6445
|
+
return null;
|
|
6446
|
+
}
|
|
6447
|
+
}
|
|
6448
|
+
normalize(parsed) {
|
|
6449
|
+
if (isSerializedSnapshotFile(parsed)) {
|
|
6450
|
+
return {
|
|
6451
|
+
version: 2,
|
|
6452
|
+
currentSnapshotId: typeof parsed.currentSnapshotId === "string" ? parsed.currentSnapshotId : null,
|
|
6453
|
+
snapshots: parsed.snapshots.map((snapshot) => ({
|
|
6454
|
+
id: snapshot.id,
|
|
6455
|
+
name: snapshot.name,
|
|
6456
|
+
pinned: Boolean(snapshot.pinned),
|
|
6457
|
+
tags: this.normalizeTags(snapshot.tags),
|
|
6458
|
+
graph: snapshot.graph ?? null,
|
|
6459
|
+
risks: Array.isArray(snapshot.risks) ? snapshot.risks : [],
|
|
6460
|
+
scanTime: typeof snapshot.scanTime === "number" ? snapshot.scanTime : 0,
|
|
6461
|
+
source: typeof snapshot.source === "string" ? snapshot.source : ""
|
|
6462
|
+
}))
|
|
6463
|
+
};
|
|
6464
|
+
}
|
|
6465
|
+
const legacyStore = parsed;
|
|
6466
|
+
const legacy = this.toSnapshotRecord({
|
|
6467
|
+
graph: legacyStore.graph ?? null,
|
|
6468
|
+
risks: Array.isArray(legacyStore.risks) ? legacyStore.risks : [],
|
|
6469
|
+
scanTime: typeof legacyStore.scanTime === "number" ? legacyStore.scanTime : 0,
|
|
6470
|
+
source: typeof legacyStore.source === "string" ? legacyStore.source : ""
|
|
6471
|
+
});
|
|
6472
|
+
return {
|
|
6473
|
+
version: 2,
|
|
6474
|
+
currentSnapshotId: legacy.id,
|
|
6475
|
+
snapshots: [legacy]
|
|
6476
|
+
};
|
|
6477
|
+
}
|
|
6478
|
+
toSnapshotRecord(snapshot) {
|
|
6479
|
+
const name = snapshot.graph?.projectInfo?.name || this.deriveName(snapshot.source);
|
|
6480
|
+
return {
|
|
6481
|
+
id: `${snapshot.scanTime || Date.now()}-${Math.random().toString(16).slice(2, 8)}`,
|
|
6482
|
+
name,
|
|
6483
|
+
pinned: false,
|
|
6484
|
+
tags: [],
|
|
6485
|
+
graph: snapshot.graph ?? null,
|
|
6486
|
+
risks: Array.isArray(snapshot.risks) ? snapshot.risks : [],
|
|
6487
|
+
scanTime: typeof snapshot.scanTime === "number" ? snapshot.scanTime : Date.now(),
|
|
6488
|
+
source: typeof snapshot.source === "string" ? snapshot.source : ""
|
|
6489
|
+
};
|
|
6490
|
+
}
|
|
6491
|
+
toProjectStore(snapshot) {
|
|
6492
|
+
return {
|
|
6493
|
+
graph: snapshot.graph ?? null,
|
|
6494
|
+
risks: Array.isArray(snapshot.risks) ? snapshot.risks : [],
|
|
6495
|
+
scanTime: typeof snapshot.scanTime === "number" ? snapshot.scanTime : 0,
|
|
6496
|
+
source: typeof snapshot.source === "string" ? snapshot.source : ""
|
|
6497
|
+
};
|
|
6498
|
+
}
|
|
6499
|
+
deriveName(source) {
|
|
6500
|
+
if (!source) return "unknown-project";
|
|
6501
|
+
const parts = source.split(/[\\/]/).filter(Boolean);
|
|
6502
|
+
return parts[parts.length - 1] || source;
|
|
6503
|
+
}
|
|
6504
|
+
normalizeTags(tags) {
|
|
6505
|
+
if (!Array.isArray(tags)) return [];
|
|
6506
|
+
return [...new Set(
|
|
6507
|
+
tags.map((tag) => typeof tag === "string" ? tag.trim() : "").filter(Boolean)
|
|
6508
|
+
)];
|
|
6509
|
+
}
|
|
6510
|
+
};
|
|
6511
|
+
}
|
|
6512
|
+
});
|
|
6513
|
+
|
|
6514
|
+
// src/agents/role-registry.ts
|
|
6515
|
+
var role_registry_exports = {};
|
|
6516
|
+
__export(role_registry_exports, {
|
|
6517
|
+
RoleRegistry: () => RoleRegistry,
|
|
6518
|
+
getRoleRegistry: () => getRoleRegistry
|
|
6519
|
+
});
|
|
6520
|
+
function getRoleRegistry() {
|
|
6521
|
+
if (!_registry) {
|
|
6522
|
+
_registry = new RoleRegistry();
|
|
6523
|
+
}
|
|
6524
|
+
return _registry;
|
|
6525
|
+
}
|
|
6526
|
+
var CORE_ROLES, LANGUAGE_ROLES, FRAMEWORK_ROLES, DOMAIN_ROLES, RoleRegistry, _registry;
|
|
6527
|
+
var init_role_registry = __esm({
|
|
6528
|
+
"src/agents/role-registry.ts"() {
|
|
6529
|
+
"use strict";
|
|
6530
|
+
init_esm_shims();
|
|
6531
|
+
CORE_ROLES = [
|
|
6532
|
+
{
|
|
6533
|
+
id: "parser-croc",
|
|
6534
|
+
name: "\u89E3\u6790\u9CC4",
|
|
6535
|
+
nameEn: "Parser Croc",
|
|
6536
|
+
category: "core",
|
|
6537
|
+
description: "\u89E3\u6790\u9879\u76EE\u7ED3\u6784\u3001\u63D0\u53D6\u5B9E\u4F53\u548C\u5173\u7CFB",
|
|
6538
|
+
sprite: "parser",
|
|
6539
|
+
color: "#34d399",
|
|
6540
|
+
priority: 0,
|
|
6541
|
+
triggers: { custom: () => true },
|
|
6542
|
+
// Always summoned
|
|
6543
|
+
systemPrompt: "You are an expert code parser. Analyze the project structure, extract all entities (classes, functions, APIs, models) and their relationships.",
|
|
6544
|
+
outputType: "analysis",
|
|
6545
|
+
tags: ["core", "parser", "structure"]
|
|
6546
|
+
},
|
|
6547
|
+
{
|
|
6548
|
+
id: "analyzer-croc",
|
|
6549
|
+
name: "\u5206\u6790\u9CC4",
|
|
6550
|
+
nameEn: "Analyzer Croc",
|
|
6551
|
+
category: "core",
|
|
6552
|
+
description: "\u6784\u5EFA\u77E5\u8BC6\u56FE\u8C31\u3001\u5206\u6790\u4F9D\u8D56\u5173\u7CFB",
|
|
6553
|
+
sprite: "analyzer",
|
|
6554
|
+
color: "#60a5fa",
|
|
6555
|
+
priority: 1,
|
|
6556
|
+
triggers: { custom: () => true },
|
|
6557
|
+
systemPrompt: "You are an expert software architect. Build a knowledge graph of the project, analyze dependencies, coupling, and cohesion.",
|
|
6558
|
+
outputType: "diagram",
|
|
6559
|
+
tags: ["core", "graph", "architecture"]
|
|
6560
|
+
},
|
|
6561
|
+
{
|
|
6562
|
+
id: "tester-croc",
|
|
6563
|
+
name: "\u6D4B\u8BD5\u9CC4",
|
|
6564
|
+
nameEn: "Tester Croc",
|
|
6565
|
+
category: "core",
|
|
6566
|
+
description: "\u751F\u6210\u548C\u6267\u884C E2E \u6D4B\u8BD5",
|
|
6567
|
+
sprite: "tester",
|
|
6568
|
+
color: "#a78bfa",
|
|
6569
|
+
priority: 2,
|
|
6570
|
+
triggers: { custom: () => true },
|
|
6571
|
+
systemPrompt: "You are an expert test engineer. Generate comprehensive E2E test cases covering all critical paths, edge cases, and error scenarios.",
|
|
6572
|
+
outputType: "analysis",
|
|
6573
|
+
tags: ["core", "testing", "e2e"]
|
|
6574
|
+
},
|
|
6575
|
+
{
|
|
6576
|
+
id: "healer-croc",
|
|
6577
|
+
name: "\u4FEE\u590D\u9CC4",
|
|
6578
|
+
nameEn: "Healer Croc",
|
|
6579
|
+
category: "core",
|
|
6580
|
+
description: "\u81EA\u52A8\u4FEE\u590D\u6D4B\u8BD5\u5931\u8D25\u548C\u4EE3\u7801\u95EE\u9898",
|
|
6581
|
+
sprite: "healer",
|
|
6582
|
+
color: "#f87171",
|
|
6583
|
+
priority: 3,
|
|
6584
|
+
triggers: { custom: () => true },
|
|
6585
|
+
systemPrompt: "You are an expert debugger and code fixer. Analyze test failures, diagnose root causes, and propose minimal targeted fixes.",
|
|
6586
|
+
outputType: "fix",
|
|
6587
|
+
tags: ["core", "healing", "debug"]
|
|
6588
|
+
},
|
|
6589
|
+
{
|
|
6590
|
+
id: "planner-croc",
|
|
6591
|
+
name: "\u89C4\u5212\u9CC4",
|
|
6592
|
+
nameEn: "Planner Croc",
|
|
6593
|
+
category: "core",
|
|
6594
|
+
description: "\u5236\u5B9A\u6D4B\u8BD5\u7B56\u7565\u548C\u6267\u884C\u8BA1\u5212",
|
|
6595
|
+
sprite: "planner",
|
|
6596
|
+
color: "#fbbf24",
|
|
6597
|
+
priority: 4,
|
|
6598
|
+
triggers: { custom: () => true },
|
|
6599
|
+
systemPrompt: "You are an expert project planner. Create test strategies, prioritize test execution order, and optimize the testing pipeline.",
|
|
6600
|
+
outputType: "analysis",
|
|
6601
|
+
tags: ["core", "planning", "strategy"]
|
|
6602
|
+
},
|
|
6603
|
+
{
|
|
6604
|
+
id: "reporter-croc",
|
|
6605
|
+
name: "\u6C47\u62A5\u9CC4",
|
|
6606
|
+
nameEn: "Reporter Croc",
|
|
6607
|
+
category: "core",
|
|
6608
|
+
description: "\u751F\u6210\u591A\u89C6\u89D2\u5206\u6790\u62A5\u544A",
|
|
6609
|
+
sprite: "reporter",
|
|
6610
|
+
color: "#22d3ee",
|
|
6611
|
+
priority: 5,
|
|
6612
|
+
triggers: { custom: () => true },
|
|
6613
|
+
systemPrompt: "You are an expert technical writer. Generate clear, actionable reports from multiple perspectives (developer, architect, tester, product, executive).",
|
|
6614
|
+
outputType: "report",
|
|
6615
|
+
tags: ["core", "reporting", "documentation"]
|
|
6616
|
+
}
|
|
6617
|
+
];
|
|
6618
|
+
LANGUAGE_ROLES = [
|
|
6619
|
+
{
|
|
6620
|
+
id: "python-croc",
|
|
6621
|
+
name: "Python\u4E13\u5BB6\u9CC4",
|
|
6622
|
+
nameEn: "Python Expert Croc",
|
|
6623
|
+
category: "language",
|
|
6624
|
+
description: "Python \u751F\u6001\u4E13\u5BB6\uFF1ADjango/Flask/FastAPI/SQLAlchemy",
|
|
6625
|
+
sprite: "language",
|
|
6626
|
+
color: "#3776ab",
|
|
6627
|
+
priority: 10,
|
|
6628
|
+
triggers: { languages: ["python"] },
|
|
6629
|
+
systemPrompt: "You are a Python ecosystem expert. Analyze Python code for best practices, type safety, async patterns, Django/Flask/FastAPI conventions, SQLAlchemy usage, and Python-specific security issues.",
|
|
6630
|
+
outputType: "review",
|
|
6631
|
+
tags: ["language", "python", "django", "flask", "fastapi"]
|
|
6632
|
+
},
|
|
6633
|
+
{
|
|
6634
|
+
id: "go-croc",
|
|
6635
|
+
name: "Go\u4E13\u5BB6\u9CC4",
|
|
6636
|
+
nameEn: "Go Expert Croc",
|
|
6637
|
+
category: "language",
|
|
6638
|
+
description: "Go \u751F\u6001\u4E13\u5BB6\uFF1Agoroutine/channel/\u63A5\u53E3\u8BBE\u8BA1",
|
|
6639
|
+
sprite: "language",
|
|
6640
|
+
color: "#00add8",
|
|
6641
|
+
priority: 10,
|
|
6642
|
+
triggers: { languages: ["go"] },
|
|
6643
|
+
systemPrompt: "You are a Go ecosystem expert. Analyze Go code for goroutine safety, channel patterns, interface design, error handling, and Go-specific performance concerns.",
|
|
6644
|
+
outputType: "review",
|
|
6645
|
+
tags: ["language", "go", "golang", "concurrency"]
|
|
6646
|
+
},
|
|
6647
|
+
{
|
|
6648
|
+
id: "java-croc",
|
|
6649
|
+
name: "Java\u4E13\u5BB6\u9CC4",
|
|
6650
|
+
nameEn: "Java Expert Croc",
|
|
6651
|
+
category: "language",
|
|
6652
|
+
description: "Java/Kotlin \u751F\u6001\u4E13\u5BB6\uFF1ASpring Boot/JPA/\u5FAE\u670D\u52A1",
|
|
6653
|
+
sprite: "language",
|
|
6654
|
+
color: "#ed8b00",
|
|
6655
|
+
priority: 10,
|
|
6656
|
+
triggers: { languages: ["java", "kotlin"] },
|
|
6657
|
+
systemPrompt: "You are a Java/Kotlin ecosystem expert. Analyze Spring Boot applications for bean lifecycle issues, JPA N+1 queries, transaction management, and microservice patterns.",
|
|
6658
|
+
outputType: "review",
|
|
6659
|
+
tags: ["language", "java", "kotlin", "spring"]
|
|
6660
|
+
},
|
|
6661
|
+
{
|
|
6662
|
+
id: "rust-croc",
|
|
6663
|
+
name: "Rust\u4E13\u5BB6\u9CC4",
|
|
6664
|
+
nameEn: "Rust Expert Croc",
|
|
6665
|
+
category: "language",
|
|
6666
|
+
description: "Rust \u751F\u6001\u4E13\u5BB6\uFF1A\u6240\u6709\u6743/\u751F\u547D\u5468\u671F/unsafe",
|
|
6667
|
+
sprite: "language",
|
|
6668
|
+
color: "#dea584",
|
|
6669
|
+
priority: 10,
|
|
6670
|
+
triggers: { languages: ["rust"] },
|
|
6671
|
+
systemPrompt: "You are a Rust ecosystem expert. Analyze Rust code for ownership patterns, lifetime issues, unsafe code safety, and performance optimization.",
|
|
6672
|
+
outputType: "review",
|
|
6673
|
+
tags: ["language", "rust", "ownership", "safety"]
|
|
6674
|
+
}
|
|
6675
|
+
];
|
|
6676
|
+
FRAMEWORK_ROLES = [
|
|
6677
|
+
{
|
|
6678
|
+
id: "react-croc",
|
|
6679
|
+
name: "React\u4E13\u5BB6\u9CC4",
|
|
6680
|
+
nameEn: "React Expert Croc",
|
|
6681
|
+
category: "framework",
|
|
6682
|
+
description: "React/Next.js \u524D\u7AEF\u6027\u80FD\u548C\u67B6\u6784\u4E13\u5BB6",
|
|
6683
|
+
sprite: "framework",
|
|
6684
|
+
color: "#61dafb",
|
|
6685
|
+
priority: 15,
|
|
6686
|
+
triggers: { frameworks: ["React", "Next.js"] },
|
|
6687
|
+
systemPrompt: "You are a React/Next.js expert. Analyze component architecture, render performance, state management, SSR/SSG patterns, and React-specific anti-patterns.",
|
|
6688
|
+
outputType: "review",
|
|
6689
|
+
tags: ["framework", "react", "nextjs", "frontend"]
|
|
6690
|
+
},
|
|
6691
|
+
{
|
|
6692
|
+
id: "vue-croc",
|
|
6693
|
+
name: "Vue\u4E13\u5BB6\u9CC4",
|
|
6694
|
+
nameEn: "Vue Expert Croc",
|
|
6695
|
+
category: "framework",
|
|
6696
|
+
description: "Vue/Nuxt \u4E13\u5BB6\uFF1A\u7EC4\u5408\u5F0FAPI/\u54CD\u5E94\u5F0F/SSR",
|
|
6697
|
+
sprite: "framework",
|
|
6698
|
+
color: "#42b883",
|
|
6699
|
+
priority: 15,
|
|
6700
|
+
triggers: { frameworks: ["Vue", "Nuxt"] },
|
|
6701
|
+
systemPrompt: "You are a Vue/Nuxt expert. Analyze Composition API usage, reactivity patterns, Pinia stores, SSR hydration, and Vue-specific best practices.",
|
|
6702
|
+
outputType: "review",
|
|
6703
|
+
tags: ["framework", "vue", "nuxt", "frontend"]
|
|
6704
|
+
},
|
|
6705
|
+
{
|
|
6706
|
+
id: "express-croc",
|
|
6707
|
+
name: "Express\u4E13\u5BB6\u9CC4",
|
|
6708
|
+
nameEn: "Express Expert Croc",
|
|
6709
|
+
category: "framework",
|
|
6710
|
+
description: "Express/Koa/Fastify \u8DEF\u7531\u548C\u4E2D\u95F4\u4EF6\u4E13\u5BB6",
|
|
6711
|
+
sprite: "framework",
|
|
6712
|
+
color: "#68a063",
|
|
6713
|
+
priority: 15,
|
|
6714
|
+
triggers: { frameworks: ["Express", "Koa", "Fastify"] },
|
|
6715
|
+
systemPrompt: "You are a Node.js backend expert. Analyze Express/Koa/Fastify middleware chains, route organization, error handling, authentication patterns, and performance.",
|
|
6716
|
+
outputType: "review",
|
|
6717
|
+
tags: ["framework", "express", "koa", "fastify", "nodejs"]
|
|
6718
|
+
},
|
|
6719
|
+
{
|
|
6720
|
+
id: "django-croc",
|
|
6721
|
+
name: "Django\u4E13\u5BB6\u9CC4",
|
|
6722
|
+
nameEn: "Django Expert Croc",
|
|
6723
|
+
category: "framework",
|
|
6724
|
+
description: "Django/DRF \u4E13\u5BB6\uFF1AORM/\u5E8F\u5217\u5316/\u6743\u9650",
|
|
6725
|
+
sprite: "framework",
|
|
6726
|
+
color: "#092e20",
|
|
6727
|
+
priority: 15,
|
|
6728
|
+
triggers: { frameworks: ["Django", "DRF"] },
|
|
6729
|
+
systemPrompt: "You are a Django/DRF expert. Analyze ORM query efficiency, serializer design, view permissions, middleware, and Django-specific security practices.",
|
|
6730
|
+
outputType: "review",
|
|
6731
|
+
tags: ["framework", "django", "drf", "python"]
|
|
6732
|
+
},
|
|
6733
|
+
{
|
|
6734
|
+
id: "spring-croc",
|
|
6735
|
+
name: "SpringBoot\u4E13\u5BB6\u9CC4",
|
|
6736
|
+
nameEn: "Spring Boot Expert Croc",
|
|
6737
|
+
category: "framework",
|
|
6738
|
+
description: "Spring Boot/Cloud \u5FAE\u670D\u52A1\u67B6\u6784\u4E13\u5BB6",
|
|
6739
|
+
sprite: "framework",
|
|
6740
|
+
color: "#6db33f",
|
|
6741
|
+
priority: 15,
|
|
6742
|
+
triggers: { frameworks: ["Spring Boot", "Spring Cloud"] },
|
|
6743
|
+
systemPrompt: "You are a Spring Boot/Cloud expert. Analyze bean configurations, transaction management, service discovery, circuit breakers, and microservice communication patterns.",
|
|
6744
|
+
outputType: "review",
|
|
6745
|
+
tags: ["framework", "spring", "springboot", "microservice"]
|
|
6746
|
+
}
|
|
6747
|
+
];
|
|
6748
|
+
DOMAIN_ROLES = [
|
|
6749
|
+
{
|
|
6750
|
+
id: "security-croc",
|
|
6751
|
+
name: "\u5B89\u5168\u5BA1\u8BA1\u9CC4",
|
|
6752
|
+
nameEn: "Security Auditor Croc",
|
|
6753
|
+
category: "domain",
|
|
6754
|
+
description: "\u5B89\u5168\u6F0F\u6D1E\u68C0\u6D4B\uFF1A\u6CE8\u5165/XSS/CSRF/\u8BA4\u8BC1/\u6388\u6743",
|
|
6755
|
+
sprite: "security",
|
|
6756
|
+
color: "#ef4444",
|
|
6757
|
+
priority: 8,
|
|
6758
|
+
triggers: {
|
|
6759
|
+
riskCategories: ["security"],
|
|
6760
|
+
custom: (ctx) => ctx.hasAPIs
|
|
6761
|
+
},
|
|
6762
|
+
systemPrompt: "You are a security auditor. Scan for OWASP Top 10 vulnerabilities: SQL injection, XSS, CSRF, broken authentication, sensitive data exposure, insecure deserialization, and missing access controls.",
|
|
6763
|
+
outputType: "report",
|
|
6764
|
+
tags: ["domain", "security", "owasp", "audit"]
|
|
6765
|
+
},
|
|
6766
|
+
{
|
|
6767
|
+
id: "performance-croc",
|
|
6768
|
+
name: "\u6027\u80FD\u5206\u6790\u9CC4",
|
|
6769
|
+
nameEn: "Performance Analyst Croc",
|
|
6770
|
+
category: "domain",
|
|
6771
|
+
description: "\u6027\u80FD\u74F6\u9888\u68C0\u6D4B\uFF1AN+1\u67E5\u8BE2/\u5185\u5B58\u6CC4\u6F0F/\u6162\u63A5\u53E3",
|
|
6772
|
+
sprite: "performance",
|
|
6773
|
+
color: "#f59e0b",
|
|
6774
|
+
priority: 9,
|
|
6775
|
+
triggers: {
|
|
6776
|
+
minEntities: 50,
|
|
6777
|
+
custom: (ctx) => ctx.hasModels && ctx.hasAPIs
|
|
6778
|
+
},
|
|
6779
|
+
systemPrompt: "You are a performance analyst. Detect N+1 queries, memory leaks, slow API endpoints, missing indexes, unnecessary data loading, and recommend caching strategies.",
|
|
6780
|
+
outputType: "report",
|
|
6781
|
+
tags: ["domain", "performance", "optimization", "n+1"]
|
|
6782
|
+
},
|
|
6783
|
+
{
|
|
6784
|
+
id: "architecture-croc",
|
|
6785
|
+
name: "\u67B6\u6784\u8BC4\u5BA1\u9CC4",
|
|
6786
|
+
nameEn: "Architecture Reviewer Croc",
|
|
6787
|
+
category: "domain",
|
|
6788
|
+
description: "\u67B6\u6784\u8D28\u91CF\u8BC4\u5BA1\uFF1A\u8026\u5408\u5EA6/\u5185\u805A\u6027/\u5206\u5C42/DDD",
|
|
6789
|
+
sprite: "architecture",
|
|
6790
|
+
color: "#8b5cf6",
|
|
6791
|
+
priority: 7,
|
|
6792
|
+
triggers: {
|
|
6793
|
+
minEntities: 30
|
|
6794
|
+
},
|
|
6795
|
+
systemPrompt: "You are a software architect. Evaluate coupling, cohesion, layering, dependency injection, SOLID principles, DDD patterns, and recommend architectural improvements.",
|
|
6796
|
+
outputType: "review",
|
|
6797
|
+
tags: ["domain", "architecture", "solid", "ddd"]
|
|
6798
|
+
},
|
|
6799
|
+
{
|
|
6800
|
+
id: "data-modeling-croc",
|
|
6801
|
+
name: "\u6570\u636E\u5EFA\u6A21\u9CC4",
|
|
6802
|
+
nameEn: "Data Modeling Croc",
|
|
6803
|
+
category: "domain",
|
|
6804
|
+
description: "\u6570\u636E\u6A21\u578B\u8BC4\u5BA1\uFF1A\u8303\u5F0F/\u7D22\u5F15/\u5173\u8054/\u8FC1\u79FB",
|
|
6805
|
+
sprite: "database",
|
|
6806
|
+
color: "#06b6d4",
|
|
6807
|
+
priority: 9,
|
|
6808
|
+
triggers: {
|
|
6809
|
+
custom: (ctx) => ctx.hasModels,
|
|
6810
|
+
frameworks: ["Sequelize", "Prisma", "TypeORM", "Drizzle", "SQLAlchemy", "Django"]
|
|
6811
|
+
},
|
|
6812
|
+
systemPrompt: "You are a database expert. Review data models for normalization, index design, relationship integrity, migration safety, cascade delete risks, and query optimization.",
|
|
6813
|
+
outputType: "review",
|
|
6814
|
+
tags: ["domain", "database", "modeling", "orm"]
|
|
6815
|
+
},
|
|
6816
|
+
{
|
|
6817
|
+
id: "devops-croc",
|
|
6818
|
+
name: "\u8FD0\u7EF4\u90E8\u7F72\u9CC4",
|
|
6819
|
+
nameEn: "DevOps Croc",
|
|
6820
|
+
category: "domain",
|
|
6821
|
+
description: "CI/CD\u3001Docker\u3001K8s \u90E8\u7F72\u548C\u8FD0\u7EF4\u4E13\u5BB6",
|
|
6822
|
+
sprite: "devops",
|
|
6823
|
+
color: "#2563eb",
|
|
6824
|
+
priority: 12,
|
|
6825
|
+
triggers: {
|
|
6826
|
+
custom: (ctx) => ctx.hasDocker || ctx.hasCI,
|
|
6827
|
+
filePatterns: ["Dockerfile", "docker-compose*", ".github/workflows/*", ".gitlab-ci*", "Jenkinsfile"]
|
|
6828
|
+
},
|
|
6829
|
+
systemPrompt: "You are a DevOps expert. Analyze Dockerfile efficiency, compose orchestration, CI/CD pipeline design, secret management, health checks, and deployment strategies.",
|
|
6830
|
+
outputType: "review",
|
|
6831
|
+
tags: ["domain", "devops", "docker", "ci/cd", "kubernetes"]
|
|
6832
|
+
},
|
|
6833
|
+
{
|
|
6834
|
+
id: "api-design-croc",
|
|
6835
|
+
name: "API\u8BBE\u8BA1\u9CC4",
|
|
6836
|
+
nameEn: "API Design Croc",
|
|
6837
|
+
category: "domain",
|
|
6838
|
+
description: "RESTful API \u8BBE\u8BA1\u8BC4\u5BA1\uFF1A\u547D\u540D/\u7248\u672C/\u5206\u9875/\u9519\u8BEF\u5904\u7406",
|
|
6839
|
+
sprite: "api",
|
|
6840
|
+
color: "#10b981",
|
|
6841
|
+
priority: 11,
|
|
6842
|
+
triggers: {
|
|
6843
|
+
custom: (ctx) => ctx.hasAPIs,
|
|
6844
|
+
minEntities: 10
|
|
6845
|
+
},
|
|
6846
|
+
systemPrompt: "You are an API design expert. Review REST API naming conventions, versioning strategy, pagination, filtering, error response format, rate limiting, and documentation completeness.",
|
|
6847
|
+
outputType: "review",
|
|
6848
|
+
tags: ["domain", "api", "rest", "design"]
|
|
6849
|
+
},
|
|
6850
|
+
{
|
|
6851
|
+
id: "refactor-croc",
|
|
6852
|
+
name: "\u91CD\u6784\u5EFA\u8BAE\u9CC4",
|
|
6853
|
+
nameEn: "Refactoring Croc",
|
|
6854
|
+
category: "domain",
|
|
6855
|
+
description: "\u4EE3\u7801\u8D28\u91CF\u8BC4\u4F30\uFF1A\u6280\u672F\u503A/\u590D\u6742\u5EA6/\u91CD\u590D\u4EE3\u7801",
|
|
6856
|
+
sprite: "refactor",
|
|
6857
|
+
color: "#ec4899",
|
|
6858
|
+
priority: 13,
|
|
6859
|
+
triggers: {
|
|
6860
|
+
riskCategories: ["maintainability"],
|
|
6861
|
+
minEntities: 40
|
|
6862
|
+
},
|
|
6863
|
+
systemPrompt: "You are a refactoring expert. Identify code smells, duplicated logic, high cyclomatic complexity, god classes, feature envy, and suggest targeted refactoring strategies with minimal risk.",
|
|
6864
|
+
outputType: "review",
|
|
6865
|
+
tags: ["domain", "refactoring", "quality", "tech-debt"]
|
|
6866
|
+
},
|
|
6867
|
+
{
|
|
6868
|
+
id: "microservice-croc",
|
|
6869
|
+
name: "\u670D\u52A1\u6CBB\u7406\u9CC4",
|
|
6870
|
+
nameEn: "Microservice Governance Croc",
|
|
6871
|
+
category: "domain",
|
|
6872
|
+
description: "\u5FAE\u670D\u52A1\u67B6\u6784\u6CBB\u7406\uFF1A\u670D\u52A1\u8FB9\u754C/\u7194\u65AD/\u94FE\u8DEF\u8FFD\u8E2A",
|
|
6873
|
+
sprite: "microservice",
|
|
6874
|
+
color: "#7c3aed",
|
|
6875
|
+
priority: 14,
|
|
6876
|
+
triggers: {
|
|
6877
|
+
projectTypes: ["microservice", "monorepo"],
|
|
6878
|
+
custom: (ctx) => ctx.entityCount > 100
|
|
6879
|
+
},
|
|
6880
|
+
systemPrompt: "You are a microservice governance expert. Analyze service boundaries, inter-service communication, circuit breakers, distributed tracing, service mesh, and API gateway patterns.",
|
|
6881
|
+
outputType: "review",
|
|
6882
|
+
tags: ["domain", "microservice", "governance", "distributed"]
|
|
6883
|
+
}
|
|
6884
|
+
];
|
|
6885
|
+
RoleRegistry = class {
|
|
6886
|
+
roles = /* @__PURE__ */ new Map();
|
|
6887
|
+
constructor() {
|
|
6888
|
+
for (const role of [...CORE_ROLES, ...LANGUAGE_ROLES, ...FRAMEWORK_ROLES, ...DOMAIN_ROLES]) {
|
|
6889
|
+
this.roles.set(role.id, role);
|
|
6890
|
+
}
|
|
6891
|
+
}
|
|
6892
|
+
/** Register a new role (community or custom) */
|
|
6893
|
+
register(role) {
|
|
6894
|
+
if (this.roles.has(role.id)) {
|
|
6895
|
+
throw new Error(`Role "${role.id}" is already registered`);
|
|
6896
|
+
}
|
|
6897
|
+
this.roles.set(role.id, role);
|
|
6898
|
+
}
|
|
6899
|
+
/** Unregister a role */
|
|
6900
|
+
unregister(id) {
|
|
6901
|
+
return this.roles.delete(id);
|
|
6902
|
+
}
|
|
6903
|
+
/** Get a role by ID */
|
|
6904
|
+
get(id) {
|
|
6905
|
+
return this.roles.get(id);
|
|
6906
|
+
}
|
|
6907
|
+
/** List all registered roles */
|
|
6908
|
+
list() {
|
|
6909
|
+
return Array.from(this.roles.values());
|
|
6910
|
+
}
|
|
6911
|
+
/** List roles by category */
|
|
6912
|
+
listByCategory(category) {
|
|
6913
|
+
return this.list().filter((r) => r.category === category);
|
|
6914
|
+
}
|
|
6915
|
+
/** Search roles by tags */
|
|
6916
|
+
search(query) {
|
|
6917
|
+
const q = query.toLowerCase();
|
|
6918
|
+
return this.list().filter(
|
|
6919
|
+
(r) => r.name.toLowerCase().includes(q) || r.nameEn.toLowerCase().includes(q) || r.description.toLowerCase().includes(q) || r.tags.some((t) => t.includes(q))
|
|
6920
|
+
);
|
|
6921
|
+
}
|
|
6922
|
+
/** Total number of registered roles */
|
|
6923
|
+
get size() {
|
|
6924
|
+
return this.roles.size;
|
|
6925
|
+
}
|
|
6926
|
+
};
|
|
6927
|
+
_registry = null;
|
|
6928
|
+
}
|
|
6929
|
+
});
|
|
6930
|
+
|
|
6327
6931
|
// src/server/routes/studio.ts
|
|
6328
|
-
function
|
|
6932
|
+
function restoreStore(snapshotStore) {
|
|
6933
|
+
return snapshotStore?.load() ?? { ...EMPTY_STUDIO_STORE };
|
|
6934
|
+
}
|
|
6935
|
+
function registerStudioRoutes(app, office, snapshotStore) {
|
|
6936
|
+
const store = restoreStore(snapshotStore);
|
|
6937
|
+
let lastScanResult = null;
|
|
6938
|
+
if (store.graph) {
|
|
6939
|
+
office.log(`\u267B\uFE0F Restored Studio snapshot: ${store.graph.nodes.length} nodes, ${store.risks.length} risks`, "info");
|
|
6940
|
+
}
|
|
6941
|
+
const persistStore = () => {
|
|
6942
|
+
snapshotStore?.save(store);
|
|
6943
|
+
};
|
|
6944
|
+
const broadcastGraph = () => {
|
|
6945
|
+
if (!store.graph) return;
|
|
6946
|
+
office.broadcast("graph:update", {
|
|
6947
|
+
nodes: store.graph.nodes.map((n) => ({
|
|
6948
|
+
id: n.id,
|
|
6949
|
+
label: n.label,
|
|
6950
|
+
type: n.type,
|
|
6951
|
+
module: n.module,
|
|
6952
|
+
status: n.status
|
|
6953
|
+
})),
|
|
6954
|
+
edges: store.graph.edges.map((e) => ({
|
|
6955
|
+
source: e.source,
|
|
6956
|
+
target: e.target,
|
|
6957
|
+
relation: e.relation
|
|
6958
|
+
}))
|
|
6959
|
+
});
|
|
6960
|
+
};
|
|
6329
6961
|
app.post("/api/studio/scan", async (req, reply) => {
|
|
6330
6962
|
const { target, branch, useLlm } = req.body || {};
|
|
6331
6963
|
if (!target || typeof target !== "string") {
|
|
@@ -6364,20 +6996,9 @@ function registerStudioRoutes(app, office) {
|
|
|
6364
6996
|
store.risks = risks;
|
|
6365
6997
|
store.scanTime = Date.now();
|
|
6366
6998
|
store.source = target;
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
label: n.label,
|
|
6371
|
-
type: n.type,
|
|
6372
|
-
module: n.module,
|
|
6373
|
-
status: n.status
|
|
6374
|
-
})),
|
|
6375
|
-
edges: graph.edges.map((e) => ({
|
|
6376
|
-
source: e.source,
|
|
6377
|
-
target: e.target,
|
|
6378
|
-
relation: e.relation
|
|
6379
|
-
}))
|
|
6380
|
-
});
|
|
6999
|
+
lastScanResult = scanResult;
|
|
7000
|
+
persistStore();
|
|
7001
|
+
broadcastGraph();
|
|
6381
7002
|
return {
|
|
6382
7003
|
ok: true,
|
|
6383
7004
|
project: graph.projectInfo,
|
|
@@ -6511,8 +7132,204 @@ function registerStudioRoutes(app, office) {
|
|
|
6511
7132
|
source: store.source
|
|
6512
7133
|
};
|
|
6513
7134
|
});
|
|
7135
|
+
app.get("/api/studio/snapshots", async () => {
|
|
7136
|
+
const snapshots = snapshotStore?.list() ?? [];
|
|
7137
|
+
return {
|
|
7138
|
+
total: snapshots.length,
|
|
7139
|
+
snapshots
|
|
7140
|
+
};
|
|
7141
|
+
});
|
|
7142
|
+
app.post("/api/studio/snapshots/:id/load", async (req, reply) => {
|
|
7143
|
+
if (!snapshotStore) {
|
|
7144
|
+
reply.code(501).send({ error: "Snapshot persistence is not configured." });
|
|
7145
|
+
return;
|
|
7146
|
+
}
|
|
7147
|
+
const restored = snapshotStore.loadById(req.params.id);
|
|
7148
|
+
if (!restored) {
|
|
7149
|
+
reply.code(404).send({ error: "Snapshot not found." });
|
|
7150
|
+
return;
|
|
7151
|
+
}
|
|
7152
|
+
store.graph = restored.graph;
|
|
7153
|
+
store.risks = restored.risks;
|
|
7154
|
+
store.scanTime = restored.scanTime;
|
|
7155
|
+
store.source = restored.source;
|
|
7156
|
+
office.log(`\u267B\uFE0F Restored snapshot: ${store.source || "unknown source"}`, "info");
|
|
7157
|
+
broadcastGraph();
|
|
7158
|
+
return {
|
|
7159
|
+
ok: true,
|
|
7160
|
+
source: store.source,
|
|
7161
|
+
scanTime: store.scanTime,
|
|
7162
|
+
graph: store.graph ? {
|
|
7163
|
+
nodeCount: store.graph.nodes.length,
|
|
7164
|
+
edgeCount: store.graph.edges.length
|
|
7165
|
+
} : null,
|
|
7166
|
+
risks: store.risks.length
|
|
7167
|
+
};
|
|
7168
|
+
});
|
|
7169
|
+
app.post("/api/studio/snapshots/:id/rename", async (req, reply) => {
|
|
7170
|
+
if (!snapshotStore) {
|
|
7171
|
+
reply.code(501).send({ error: "Snapshot persistence is not configured." });
|
|
7172
|
+
return;
|
|
7173
|
+
}
|
|
7174
|
+
const name = req.body?.name?.trim();
|
|
7175
|
+
if (!name) {
|
|
7176
|
+
reply.code(400).send({ error: "Snapshot name is required." });
|
|
7177
|
+
return;
|
|
7178
|
+
}
|
|
7179
|
+
const renamed = snapshotStore.rename(req.params.id, name);
|
|
7180
|
+
if (!renamed) {
|
|
7181
|
+
reply.code(404).send({ error: "Snapshot not found." });
|
|
7182
|
+
return;
|
|
7183
|
+
}
|
|
7184
|
+
return {
|
|
7185
|
+
ok: true,
|
|
7186
|
+
snapshots: snapshotStore.list()
|
|
7187
|
+
};
|
|
7188
|
+
});
|
|
7189
|
+
app.post("/api/studio/snapshots/:id/pin", async (req, reply) => {
|
|
7190
|
+
if (!snapshotStore) {
|
|
7191
|
+
reply.code(501).send({ error: "Snapshot persistence is not configured." });
|
|
7192
|
+
return;
|
|
7193
|
+
}
|
|
7194
|
+
const pinned = Boolean(req.body?.pinned);
|
|
7195
|
+
const updated = snapshotStore.pin(req.params.id, pinned);
|
|
7196
|
+
if (!updated) {
|
|
7197
|
+
reply.code(404).send({ error: "Snapshot not found." });
|
|
7198
|
+
return;
|
|
7199
|
+
}
|
|
7200
|
+
return {
|
|
7201
|
+
ok: true,
|
|
7202
|
+
snapshots: snapshotStore.list()
|
|
7203
|
+
};
|
|
7204
|
+
});
|
|
7205
|
+
app.post("/api/studio/snapshots/:id/tags", async (req, reply) => {
|
|
7206
|
+
if (!snapshotStore) {
|
|
7207
|
+
reply.code(501).send({ error: "Snapshot persistence is not configured." });
|
|
7208
|
+
return;
|
|
7209
|
+
}
|
|
7210
|
+
const rawTags = Array.isArray(req.body?.tags) ? req.body.tags : [];
|
|
7211
|
+
const tags = [...new Set(
|
|
7212
|
+
rawTags.map((tag) => typeof tag === "string" ? tag.trim() : "").filter(Boolean)
|
|
7213
|
+
)];
|
|
7214
|
+
const updated = snapshotStore.updateTags(req.params.id, tags);
|
|
7215
|
+
if (!updated) {
|
|
7216
|
+
reply.code(404).send({ error: "Snapshot not found." });
|
|
7217
|
+
return;
|
|
7218
|
+
}
|
|
7219
|
+
return {
|
|
7220
|
+
ok: true,
|
|
7221
|
+
snapshots: snapshotStore.list()
|
|
7222
|
+
};
|
|
7223
|
+
});
|
|
7224
|
+
app.post("/api/studio/snapshots/:id/delete", async (req, reply) => {
|
|
7225
|
+
if (!snapshotStore) {
|
|
7226
|
+
reply.code(501).send({ error: "Snapshot persistence is not configured." });
|
|
7227
|
+
return;
|
|
7228
|
+
}
|
|
7229
|
+
const deleted = snapshotStore.delete(req.params.id);
|
|
7230
|
+
if (!deleted) {
|
|
7231
|
+
reply.code(404).send({ error: "Snapshot not found." });
|
|
7232
|
+
return;
|
|
7233
|
+
}
|
|
7234
|
+
const current = snapshotStore.load();
|
|
7235
|
+
store.graph = current?.graph ?? null;
|
|
7236
|
+
store.risks = current?.risks ?? [];
|
|
7237
|
+
store.scanTime = current?.scanTime ?? 0;
|
|
7238
|
+
store.source = current?.source ?? "";
|
|
7239
|
+
if (store.graph) {
|
|
7240
|
+
broadcastGraph();
|
|
7241
|
+
}
|
|
7242
|
+
return {
|
|
7243
|
+
ok: true,
|
|
7244
|
+
hasCurrent: Boolean(current?.graph),
|
|
7245
|
+
snapshots: snapshotStore.list()
|
|
7246
|
+
};
|
|
7247
|
+
});
|
|
7248
|
+
app.get("/api/studio/roles", async (req) => {
|
|
7249
|
+
const { getRoleRegistry: getRoleRegistry2 } = await Promise.resolve().then(() => (init_role_registry(), role_registry_exports));
|
|
7250
|
+
const registry = getRoleRegistry2();
|
|
7251
|
+
const category = req.query.category;
|
|
7252
|
+
const search = req.query.search;
|
|
7253
|
+
let roles = registry.list();
|
|
7254
|
+
if (category) roles = roles.filter((r) => r.category === category);
|
|
7255
|
+
if (search) roles = registry.search(search);
|
|
7256
|
+
return {
|
|
7257
|
+
total: roles.length,
|
|
7258
|
+
categories: {
|
|
7259
|
+
core: roles.filter((r) => r.category === "core").length,
|
|
7260
|
+
language: roles.filter((r) => r.category === "language").length,
|
|
7261
|
+
framework: roles.filter((r) => r.category === "framework").length,
|
|
7262
|
+
domain: roles.filter((r) => r.category === "domain").length,
|
|
7263
|
+
community: roles.filter((r) => r.category === "community").length
|
|
7264
|
+
},
|
|
7265
|
+
roles: roles.map((r) => ({
|
|
7266
|
+
id: r.id,
|
|
7267
|
+
name: r.name,
|
|
7268
|
+
nameEn: r.nameEn,
|
|
7269
|
+
category: r.category,
|
|
7270
|
+
description: r.description,
|
|
7271
|
+
color: r.color,
|
|
7272
|
+
sprite: r.sprite,
|
|
7273
|
+
priority: r.priority,
|
|
7274
|
+
tags: r.tags,
|
|
7275
|
+
outputType: r.outputType
|
|
7276
|
+
}))
|
|
7277
|
+
};
|
|
7278
|
+
});
|
|
7279
|
+
app.post("/api/studio/summon", async (_req, reply) => {
|
|
7280
|
+
if (!lastScanResult) {
|
|
7281
|
+
reply.code(400).send({ error: "No project scanned. Run /api/studio/scan first." });
|
|
7282
|
+
return;
|
|
7283
|
+
}
|
|
7284
|
+
const riskCategories = [...new Set(store.risks.map((r) => r.category))];
|
|
7285
|
+
const plan = await office.summonForProject(lastScanResult, riskCategories);
|
|
7286
|
+
return {
|
|
7287
|
+
ok: true,
|
|
7288
|
+
totalAgents: office.getAgents().length,
|
|
7289
|
+
coreAgents: 6,
|
|
7290
|
+
dynamicAgents: office.getAgents().length - 6,
|
|
7291
|
+
reasoning: plan.reasoning,
|
|
7292
|
+
agents: office.getAgents().map((a) => ({
|
|
7293
|
+
id: a.id,
|
|
7294
|
+
name: a.name,
|
|
7295
|
+
role: a.role,
|
|
7296
|
+
sprite: a.sprite,
|
|
7297
|
+
status: a.status,
|
|
7298
|
+
category: a.category,
|
|
7299
|
+
color: a.color,
|
|
7300
|
+
description: a.description
|
|
7301
|
+
})),
|
|
7302
|
+
context: {
|
|
7303
|
+
languages: plan.context.languages,
|
|
7304
|
+
frameworks: plan.context.frameworks,
|
|
7305
|
+
projectType: plan.context.projectType,
|
|
7306
|
+
entityCount: plan.context.entityCount,
|
|
7307
|
+
hasAPIs: plan.context.hasAPIs,
|
|
7308
|
+
hasModels: plan.context.hasModels,
|
|
7309
|
+
hasFrontend: plan.context.hasFrontend,
|
|
7310
|
+
hasDocker: plan.context.hasDocker,
|
|
7311
|
+
hasCI: plan.context.hasCI
|
|
7312
|
+
}
|
|
7313
|
+
};
|
|
7314
|
+
});
|
|
7315
|
+
app.get("/api/studio/agents", async () => {
|
|
7316
|
+
const info = office.getSummonPlan();
|
|
7317
|
+
return {
|
|
7318
|
+
...info,
|
|
7319
|
+
agents: info.agents.map((a) => ({
|
|
7320
|
+
id: a.id,
|
|
7321
|
+
name: a.name,
|
|
7322
|
+
role: a.role,
|
|
7323
|
+
sprite: a.sprite,
|
|
7324
|
+
status: a.status,
|
|
7325
|
+
currentTask: a.currentTask,
|
|
7326
|
+
category: a.category,
|
|
7327
|
+
color: a.color,
|
|
7328
|
+
description: a.description
|
|
7329
|
+
}))
|
|
7330
|
+
};
|
|
7331
|
+
});
|
|
6514
7332
|
}
|
|
6515
|
-
var store;
|
|
6516
7333
|
var init_studio = __esm({
|
|
6517
7334
|
"src/server/routes/studio.ts"() {
|
|
6518
7335
|
"use strict";
|
|
@@ -6520,12 +7337,156 @@ var init_studio = __esm({
|
|
|
6520
7337
|
init_github_cloner();
|
|
6521
7338
|
init_graph();
|
|
6522
7339
|
init_insight();
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
7340
|
+
init_studio_store();
|
|
7341
|
+
}
|
|
7342
|
+
});
|
|
7343
|
+
|
|
7344
|
+
// src/agents/task-router.ts
|
|
7345
|
+
var task_router_exports = {};
|
|
7346
|
+
__export(task_router_exports, {
|
|
7347
|
+
buildMatchContext: () => buildMatchContext,
|
|
7348
|
+
planSummon: () => planSummon
|
|
7349
|
+
});
|
|
7350
|
+
function buildMatchContext(scan2) {
|
|
7351
|
+
const languages = scan2.languages;
|
|
7352
|
+
const frameworks = scan2.frameworks.map((f) => f.name);
|
|
7353
|
+
const frontendFrameworks = ["React", "Vue", "Angular", "Svelte", "Next.js", "Nuxt"];
|
|
7354
|
+
const backendFrameworks = ["Express", "Fastify", "NestJS", "Django", "Flask", "Spring Boot", "Gin", "Actix"];
|
|
7355
|
+
const hasFE = frameworks.some((f) => frontendFrameworks.includes(f));
|
|
7356
|
+
const hasBE = frameworks.some((f) => backendFrameworks.includes(f));
|
|
7357
|
+
const projectType = hasFE && hasBE ? "fullstack" : hasFE ? "frontend" : hasBE ? "backend" : "unknown";
|
|
7358
|
+
const entityTypes = new Set(scan2.entities.map((e) => e.type));
|
|
7359
|
+
const hasModels = entityTypes.has("model") || entityTypes.has("class");
|
|
7360
|
+
const hasAPIs = entityTypes.has("api") || entityTypes.has("route");
|
|
7361
|
+
const hasFrontend = hasFE || (languages["html"] ?? 0) > 10 || (languages["css"] ?? 0) > 5;
|
|
7362
|
+
const allPaths = scan2.files.map((f) => f.path);
|
|
7363
|
+
const hasDocker = allPaths.some(
|
|
7364
|
+
(p) => p.includes("Dockerfile") || p.includes("docker-compose")
|
|
7365
|
+
);
|
|
7366
|
+
const hasCI = allPaths.some(
|
|
7367
|
+
(p) => p.includes(".github/workflows") || p.includes(".gitlab-ci") || p.includes("Jenkinsfile")
|
|
7368
|
+
);
|
|
7369
|
+
const riskCategories = [];
|
|
7370
|
+
return {
|
|
7371
|
+
languages,
|
|
7372
|
+
frameworks,
|
|
7373
|
+
projectType,
|
|
7374
|
+
fileCount: scan2.files.length,
|
|
7375
|
+
entityCount: scan2.entities.length,
|
|
7376
|
+
riskCategories,
|
|
7377
|
+
hasModels,
|
|
7378
|
+
hasAPIs,
|
|
7379
|
+
hasFrontend,
|
|
7380
|
+
hasDocker,
|
|
7381
|
+
hasCI
|
|
7382
|
+
};
|
|
7383
|
+
}
|
|
7384
|
+
function matchesTriggers(trigger, ctx) {
|
|
7385
|
+
const reasons = [];
|
|
7386
|
+
let score = 0;
|
|
7387
|
+
let checks = 0;
|
|
7388
|
+
if (trigger.languages && trigger.languages.length > 0) {
|
|
7389
|
+
checks++;
|
|
7390
|
+
const matched = trigger.languages.filter((l) => (ctx.languages[l] ?? 0) > 0);
|
|
7391
|
+
if (matched.length > 0) {
|
|
7392
|
+
score++;
|
|
7393
|
+
reasons.push(`\u8BED\u8A00\u5339\u914D: ${matched.join(", ")}`);
|
|
7394
|
+
}
|
|
7395
|
+
}
|
|
7396
|
+
if (trigger.frameworks && trigger.frameworks.length > 0) {
|
|
7397
|
+
checks++;
|
|
7398
|
+
const matched = trigger.frameworks.filter(
|
|
7399
|
+
(f) => ctx.frameworks.some((cf) => cf.toLowerCase() === f.toLowerCase())
|
|
7400
|
+
);
|
|
7401
|
+
if (matched.length > 0) {
|
|
7402
|
+
score++;
|
|
7403
|
+
reasons.push(`\u6846\u67B6\u5339\u914D: ${matched.join(", ")}`);
|
|
7404
|
+
}
|
|
7405
|
+
}
|
|
7406
|
+
if (trigger.projectTypes && trigger.projectTypes.length > 0) {
|
|
7407
|
+
checks++;
|
|
7408
|
+
if (trigger.projectTypes.includes(ctx.projectType)) {
|
|
7409
|
+
score++;
|
|
7410
|
+
reasons.push(`\u9879\u76EE\u7C7B\u578B\u5339\u914D: ${ctx.projectType}`);
|
|
7411
|
+
}
|
|
7412
|
+
}
|
|
7413
|
+
if (trigger.minEntities !== void 0) {
|
|
7414
|
+
checks++;
|
|
7415
|
+
if (ctx.entityCount >= trigger.minEntities) {
|
|
7416
|
+
score++;
|
|
7417
|
+
reasons.push(`\u5B9E\u4F53\u6570\u91CF\u6EE1\u8DB3: ${ctx.entityCount} \u2265 ${trigger.minEntities}`);
|
|
7418
|
+
}
|
|
7419
|
+
}
|
|
7420
|
+
if (trigger.riskCategories && trigger.riskCategories.length > 0) {
|
|
7421
|
+
checks++;
|
|
7422
|
+
const matched = trigger.riskCategories.filter((r) => ctx.riskCategories.includes(r));
|
|
7423
|
+
if (matched.length > 0) {
|
|
7424
|
+
score++;
|
|
7425
|
+
reasons.push(`\u98CE\u9669\u7C7B\u522B\u5339\u914D: ${matched.join(", ")}`);
|
|
7426
|
+
}
|
|
7427
|
+
}
|
|
7428
|
+
if (trigger.custom) {
|
|
7429
|
+
checks++;
|
|
7430
|
+
try {
|
|
7431
|
+
if (trigger.custom(ctx)) {
|
|
7432
|
+
score++;
|
|
7433
|
+
reasons.push("\u81EA\u5B9A\u4E49\u6761\u4EF6\u6EE1\u8DB3");
|
|
7434
|
+
}
|
|
7435
|
+
} catch {
|
|
7436
|
+
}
|
|
7437
|
+
}
|
|
7438
|
+
const matches = checks === 0 ? false : score > 0;
|
|
7439
|
+
const confidence = checks > 0 ? score / checks : 0;
|
|
7440
|
+
return {
|
|
7441
|
+
matches,
|
|
7442
|
+
reason: reasons.join("; ") || "\u65E0\u5339\u914D",
|
|
7443
|
+
confidence
|
|
7444
|
+
};
|
|
7445
|
+
}
|
|
7446
|
+
function planSummon(scan2, maxRoles = 8, riskCategories = []) {
|
|
7447
|
+
const registry = getRoleRegistry();
|
|
7448
|
+
const ctx = buildMatchContext(scan2);
|
|
7449
|
+
ctx.riskCategories = riskCategories;
|
|
7450
|
+
const summoned = [];
|
|
7451
|
+
const reasoning = [];
|
|
7452
|
+
const allRoles = registry.list();
|
|
7453
|
+
const coreRoles = allRoles.filter((r) => r.category === "core");
|
|
7454
|
+
for (const role of coreRoles) {
|
|
7455
|
+
summoned.push({ role, reason: "\u6838\u5FC3\u89D2\u8272(\u59CB\u7EC8\u53EC\u5524)", confidence: 1 });
|
|
7456
|
+
reasoning.push(`\u2705 ${role.name} \u2014 \u6838\u5FC3\u89D2\u8272`);
|
|
7457
|
+
}
|
|
7458
|
+
const nonCore = allRoles.filter((r) => r.category !== "core");
|
|
7459
|
+
const candidates = [];
|
|
7460
|
+
for (const role of nonCore) {
|
|
7461
|
+
const result = matchesTriggers(role.triggers, ctx);
|
|
7462
|
+
if (result.matches) {
|
|
7463
|
+
candidates.push({
|
|
7464
|
+
role,
|
|
7465
|
+
reason: result.reason,
|
|
7466
|
+
confidence: result.confidence
|
|
7467
|
+
});
|
|
7468
|
+
}
|
|
7469
|
+
}
|
|
7470
|
+
candidates.sort((a, b) => {
|
|
7471
|
+
if (a.role.priority !== b.role.priority) return a.role.priority - b.role.priority;
|
|
7472
|
+
return b.confidence - a.confidence;
|
|
7473
|
+
});
|
|
7474
|
+
const selected = candidates.slice(0, maxRoles);
|
|
7475
|
+
for (const s of selected) {
|
|
7476
|
+
summoned.push(s);
|
|
7477
|
+
reasoning.push(`\u{1F40A} ${s.role.name} \u2014 ${s.reason} (\u7F6E\u4FE1\u5EA6: ${(s.confidence * 100).toFixed(0)}%)`);
|
|
7478
|
+
}
|
|
7479
|
+
const skipped = candidates.slice(maxRoles);
|
|
7480
|
+
for (const s of skipped) {
|
|
7481
|
+
reasoning.push(`\u23ED\uFE0F ${s.role.name} \u2014 \u5339\u914D\u4F46\u8D85\u51FA\u9650\u5236 (\u7F6E\u4FE1\u5EA6: ${(s.confidence * 100).toFixed(0)}%)`);
|
|
7482
|
+
}
|
|
7483
|
+
return { roles: summoned, reasoning, context: ctx };
|
|
7484
|
+
}
|
|
7485
|
+
var init_task_router = __esm({
|
|
7486
|
+
"src/agents/task-router.ts"() {
|
|
7487
|
+
"use strict";
|
|
7488
|
+
init_esm_shims();
|
|
7489
|
+
init_role_registry();
|
|
6529
7490
|
}
|
|
6530
7491
|
});
|
|
6531
7492
|
|
|
@@ -6728,14 +7689,14 @@ var runtime_bootstrap_exports = {};
|
|
|
6728
7689
|
__export(runtime_bootstrap_exports, {
|
|
6729
7690
|
createRuntimeBootstrap: () => createRuntimeBootstrap
|
|
6730
7691
|
});
|
|
6731
|
-
import { existsSync as
|
|
6732
|
-
import { dirname as
|
|
7692
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync12, writeFileSync as writeFileSync11 } from "fs";
|
|
7693
|
+
import { dirname as dirname7, join as join16 } from "path";
|
|
6733
7694
|
function ensureFile(filePath, content, force) {
|
|
6734
|
-
if (
|
|
7695
|
+
if (existsSync19(filePath) && !force) {
|
|
6735
7696
|
return false;
|
|
6736
7697
|
}
|
|
6737
|
-
|
|
6738
|
-
|
|
7698
|
+
mkdirSync12(dirname7(filePath), { recursive: true });
|
|
7699
|
+
writeFileSync11(filePath, content, "utf-8");
|
|
6739
7700
|
return true;
|
|
6740
7701
|
}
|
|
6741
7702
|
function createRuntimeBootstrap(config) {
|
|
@@ -6907,7 +7868,7 @@ var init_croc_office = __esm({
|
|
|
6907
7868
|
{ id: "planner-croc", name: "\u89C4\u5212\u9CC4", role: "planner", sprite: "planner", status: "idle", tokensUsed: 0 },
|
|
6908
7869
|
{ id: "reporter-croc", name: "\u6C47\u62A5\u9CC4", role: "reporter", sprite: "reporter", status: "idle", tokensUsed: 0 }
|
|
6909
7870
|
];
|
|
6910
|
-
CrocOffice = class {
|
|
7871
|
+
CrocOffice = class _CrocOffice {
|
|
6911
7872
|
config;
|
|
6912
7873
|
cwd;
|
|
6913
7874
|
clients = /* @__PURE__ */ new Set();
|
|
@@ -6919,6 +7880,10 @@ var init_croc_office = __esm({
|
|
|
6919
7880
|
lastExecutionMetrics = null;
|
|
6920
7881
|
lastExecutionQuality = null;
|
|
6921
7882
|
lastReports = [];
|
|
7883
|
+
static ACTIVE_AGENT_STATUSES = /* @__PURE__ */ new Set([
|
|
7884
|
+
"working",
|
|
7885
|
+
"thinking"
|
|
7886
|
+
]);
|
|
6922
7887
|
constructor(config, cwd) {
|
|
6923
7888
|
this.config = config;
|
|
6924
7889
|
this.cwd = cwd;
|
|
@@ -6953,10 +7918,34 @@ var init_croc_office = __esm({
|
|
|
6953
7918
|
updateAgent(id, update) {
|
|
6954
7919
|
const agent = this.agents.find((a) => a.id === id);
|
|
6955
7920
|
if (agent) {
|
|
7921
|
+
const wasActive = this.isAgentActive(agent.status);
|
|
6956
7922
|
Object.assign(agent, update);
|
|
7923
|
+
const isActive = this.isAgentActive(agent.status);
|
|
6957
7924
|
this.broadcast("agent:update", this.agents);
|
|
7925
|
+
if (!wasActive && isActive) {
|
|
7926
|
+
this.broadcast("agent:assigned", {
|
|
7927
|
+
id: agent.id,
|
|
7928
|
+
name: agent.name,
|
|
7929
|
+
role: agent.role,
|
|
7930
|
+
status: agent.status,
|
|
7931
|
+
currentTask: agent.currentTask ?? null,
|
|
7932
|
+
at: Date.now()
|
|
7933
|
+
});
|
|
7934
|
+
} else if (wasActive && !isActive) {
|
|
7935
|
+
this.broadcast("agent:released", {
|
|
7936
|
+
id: agent.id,
|
|
7937
|
+
name: agent.name,
|
|
7938
|
+
role: agent.role,
|
|
7939
|
+
status: agent.status,
|
|
7940
|
+
currentTask: agent.currentTask ?? null,
|
|
7941
|
+
at: Date.now()
|
|
7942
|
+
});
|
|
7943
|
+
}
|
|
6958
7944
|
}
|
|
6959
7945
|
}
|
|
7946
|
+
isAgentActive(status) {
|
|
7947
|
+
return _CrocOffice.ACTIVE_AGENT_STATUSES.has(status);
|
|
7948
|
+
}
|
|
6960
7949
|
isRunning() {
|
|
6961
7950
|
return this.running;
|
|
6962
7951
|
}
|
|
@@ -7034,13 +8023,13 @@ var init_croc_office = __esm({
|
|
|
7034
8023
|
const fullResult = await pipeline.run(["scan", "er-diagram", "api-chain", "plan", "codegen"]);
|
|
7035
8024
|
this.lastPipelineResult = fullResult;
|
|
7036
8025
|
this.lastGeneratedFiles = fullResult.generatedFiles;
|
|
7037
|
-
const { writeFileSync:
|
|
7038
|
-
const { dirname:
|
|
8026
|
+
const { writeFileSync: writeFileSync14, mkdirSync: mkdirSync15 } = await import("fs");
|
|
8027
|
+
const { dirname: dirname9 } = await import("path");
|
|
7039
8028
|
let filesWritten = 0;
|
|
7040
8029
|
for (const file of fullResult.generatedFiles) {
|
|
7041
8030
|
const fullPath = resolvePath(this.cwd, file.filePath);
|
|
7042
|
-
|
|
7043
|
-
|
|
8031
|
+
mkdirSync15(dirname9(fullPath), { recursive: true });
|
|
8032
|
+
writeFileSync14(fullPath, file.content, "utf-8");
|
|
7044
8033
|
filesWritten++;
|
|
7045
8034
|
}
|
|
7046
8035
|
this.updateNodeStatus("controller", "passed");
|
|
@@ -7086,12 +8075,75 @@ var init_croc_office = __esm({
|
|
|
7086
8075
|
/** Reset all agents to idle */
|
|
7087
8076
|
resetAgents() {
|
|
7088
8077
|
for (const agent of this.agents) {
|
|
8078
|
+
const wasActive = this.isAgentActive(agent.status);
|
|
7089
8079
|
agent.status = "idle";
|
|
7090
8080
|
agent.currentTask = void 0;
|
|
7091
8081
|
agent.progress = void 0;
|
|
8082
|
+
if (wasActive) {
|
|
8083
|
+
this.broadcast("agent:released", {
|
|
8084
|
+
id: agent.id,
|
|
8085
|
+
name: agent.name,
|
|
8086
|
+
role: agent.role,
|
|
8087
|
+
status: agent.status,
|
|
8088
|
+
currentTask: null,
|
|
8089
|
+
at: Date.now()
|
|
8090
|
+
});
|
|
8091
|
+
}
|
|
7092
8092
|
}
|
|
7093
8093
|
this.broadcast("agent:update", this.agents);
|
|
7094
8094
|
}
|
|
8095
|
+
/**
|
|
8096
|
+
* Dynamically summon crocs based on a project scan result.
|
|
8097
|
+
* Core 6 are always present; additional expert crocs are added based on project characteristics.
|
|
8098
|
+
*/
|
|
8099
|
+
async summonForProject(scan2, riskCategories = []) {
|
|
8100
|
+
const { planSummon: planSummon2 } = await Promise.resolve().then(() => (init_task_router(), task_router_exports));
|
|
8101
|
+
const plan = planSummon2(scan2, 8, riskCategories);
|
|
8102
|
+
const coreIds = new Set(DEFAULT_AGENTS.map((a) => a.id));
|
|
8103
|
+
this.agents = DEFAULT_AGENTS.map((a) => ({ ...a }));
|
|
8104
|
+
for (const summoned of plan.roles) {
|
|
8105
|
+
if (coreIds.has(summoned.role.id)) continue;
|
|
8106
|
+
const agent = {
|
|
8107
|
+
id: summoned.role.id,
|
|
8108
|
+
name: summoned.role.name,
|
|
8109
|
+
role: summoned.role.id.replace(/-croc$/, ""),
|
|
8110
|
+
sprite: summoned.role.sprite,
|
|
8111
|
+
status: "idle",
|
|
8112
|
+
tokensUsed: 0,
|
|
8113
|
+
category: summoned.role.category,
|
|
8114
|
+
color: summoned.role.color,
|
|
8115
|
+
description: summoned.role.description
|
|
8116
|
+
};
|
|
8117
|
+
this.agents.push(agent);
|
|
8118
|
+
}
|
|
8119
|
+
this.broadcast("agent:update", this.agents);
|
|
8120
|
+
for (const line of plan.reasoning) {
|
|
8121
|
+
this.log(line);
|
|
8122
|
+
}
|
|
8123
|
+
this.log(`\u{1F40A} \u5171\u53EC\u5524 ${this.agents.length} \u4E2A\u9CC4\u9C7C\u4E13\u5BB6 (${plan.roles.length - DEFAULT_AGENTS.length} \u4E2A\u52A8\u6001\u89D2\u8272)`);
|
|
8124
|
+
const dynamicAgents = this.agents.filter((a) => !coreIds.has(a.id));
|
|
8125
|
+
for (let i = 0; i < dynamicAgents.length; i++) {
|
|
8126
|
+
const agent = dynamicAgents[i];
|
|
8127
|
+
setTimeout(() => {
|
|
8128
|
+
this.updateAgent(agent.id, { status: "working", currentTask: "\u5206\u6790\u9879\u76EE\u4E2D\u2026" });
|
|
8129
|
+
setTimeout(() => {
|
|
8130
|
+
this.updateAgent(agent.id, { status: "idle", currentTask: void 0 });
|
|
8131
|
+
}, 2e3 + Math.random() * 1e3);
|
|
8132
|
+
}, 300 * i);
|
|
8133
|
+
}
|
|
8134
|
+
return plan;
|
|
8135
|
+
}
|
|
8136
|
+
/** Get the current summon plan context */
|
|
8137
|
+
getSummonPlan() {
|
|
8138
|
+
const coreIds = new Set(DEFAULT_AGENTS.map((a) => a.id));
|
|
8139
|
+
const coreCount = this.agents.filter((a) => coreIds.has(a.id)).length;
|
|
8140
|
+
return {
|
|
8141
|
+
agentCount: this.agents.length,
|
|
8142
|
+
coreCount,
|
|
8143
|
+
dynamicCount: this.agents.length - coreCount,
|
|
8144
|
+
agents: this.agents
|
|
8145
|
+
};
|
|
8146
|
+
}
|
|
7095
8147
|
/** Get last pipeline result */
|
|
7096
8148
|
getLastPipelineResult() {
|
|
7097
8149
|
return this.lastPipelineResult;
|
|
@@ -7124,14 +8176,14 @@ var init_croc_office = __esm({
|
|
|
7124
8176
|
let backendStatus;
|
|
7125
8177
|
try {
|
|
7126
8178
|
const { resolve: resolvePath } = await import("path");
|
|
7127
|
-
const { existsSync:
|
|
8179
|
+
const { existsSync: existsSync22 } = await import("fs");
|
|
7128
8180
|
const { createExecutionCoordinator: createExecutionCoordinator2 } = await Promise.resolve().then(() => (init_coordinator(), coordinator_exports));
|
|
7129
8181
|
const { createBackendManager: createBackendManager2 } = await Promise.resolve().then(() => (init_backend_manager(), backend_manager_exports));
|
|
7130
8182
|
const { createRuntimeBootstrap: createRuntimeBootstrap2 } = await Promise.resolve().then(() => (init_runtime_bootstrap(), runtime_bootstrap_exports));
|
|
7131
8183
|
const { createAuthProvisioner: createAuthProvisioner2 } = await Promise.resolve().then(() => (init_auth_provisioner(), auth_provisioner_exports));
|
|
7132
8184
|
const { buildExecutionQualityGate: buildExecutionQualityGate2 } = await Promise.resolve().then(() => (init_quality_gate(), quality_gate_exports));
|
|
7133
8185
|
const { categorizeFailure: categorizeFailure2 } = await Promise.resolve().then(() => (init_self_healing(), self_healing_exports));
|
|
7134
|
-
const testFiles = this.lastGeneratedFiles.map((f) => resolvePath(this.cwd, f.filePath)).filter((f) =>
|
|
8186
|
+
const testFiles = this.lastGeneratedFiles.map((f) => resolvePath(this.cwd, f.filePath)).filter((f) => existsSync22(f));
|
|
7135
8187
|
if (testFiles.length === 0) {
|
|
7136
8188
|
this.log("\u26A0\uFE0F No test files found on disk", "warn");
|
|
7137
8189
|
return { ok: false, task: "execute", duration: Date.now() - start, error: "No test files found on disk" };
|
|
@@ -7258,12 +8310,12 @@ var init_croc_office = __esm({
|
|
|
7258
8310
|
});
|
|
7259
8311
|
this.lastReports = reports;
|
|
7260
8312
|
const { resolve: resolvePath } = await import("path");
|
|
7261
|
-
const { writeFileSync:
|
|
8313
|
+
const { writeFileSync: writeFileSync14, mkdirSync: mkdirSync15 } = await import("fs");
|
|
7262
8314
|
const outDir = resolvePath(this.cwd, this.config.outDir || "./opencroc-output");
|
|
7263
|
-
|
|
8315
|
+
mkdirSync15(outDir, { recursive: true });
|
|
7264
8316
|
for (const report2 of reports) {
|
|
7265
8317
|
const fullPath = resolvePath(outDir, report2.filename);
|
|
7266
|
-
|
|
8318
|
+
writeFileSync14(fullPath, report2.content, "utf-8");
|
|
7267
8319
|
this.log(`\u{1F4C4} Generated ${report2.format} report: ${report2.filename}`);
|
|
7268
8320
|
}
|
|
7269
8321
|
this.updateAgent("reporter-croc", { status: "done", currentTask: `${reports.length} reports generated`, progress: 100 });
|
|
@@ -7508,40 +8560,45 @@ import Fastify from "fastify";
|
|
|
7508
8560
|
import fastifyStatic from "@fastify/static";
|
|
7509
8561
|
import fastifyWebsocket from "@fastify/websocket";
|
|
7510
8562
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7511
|
-
import { dirname as
|
|
7512
|
-
import { existsSync as
|
|
8563
|
+
import { dirname as dirname8, join as join17, resolve as resolve11 } from "path";
|
|
8564
|
+
import { existsSync as existsSync20 } from "fs";
|
|
7513
8565
|
async function startServer(opts) {
|
|
7514
8566
|
const app = Fastify({ logger: false });
|
|
7515
8567
|
await app.register(fastifyWebsocket);
|
|
7516
8568
|
const webDir = resolve11(__dirname2, "../web");
|
|
7517
|
-
if (
|
|
8569
|
+
if (existsSync20(webDir)) {
|
|
7518
8570
|
await app.register(fastifyStatic, {
|
|
7519
8571
|
root: webDir,
|
|
7520
8572
|
prefix: "/",
|
|
7521
|
-
decorateReply: false
|
|
8573
|
+
decorateReply: false,
|
|
8574
|
+
index: false
|
|
7522
8575
|
});
|
|
7523
8576
|
}
|
|
7524
8577
|
const office = new CrocOffice(opts.config, opts.cwd);
|
|
8578
|
+
const snapshotStore = new FileStudioSnapshotStore(resolve11(opts.cwd, ".opencroc/studio-snapshot.json"));
|
|
7525
8579
|
registerProjectRoutes(app, office);
|
|
7526
8580
|
registerAgentRoutes(app, office);
|
|
7527
|
-
registerStudioRoutes(app, office);
|
|
8581
|
+
registerStudioRoutes(app, office, snapshotStore);
|
|
7528
8582
|
app.register(async (fastify) => {
|
|
7529
8583
|
fastify.get("/ws", { websocket: true }, (socket) => {
|
|
7530
8584
|
office.addClient(socket);
|
|
7531
8585
|
socket.on("close", () => office.removeClient(socket));
|
|
7532
8586
|
});
|
|
7533
8587
|
});
|
|
8588
|
+
app.get("/index-studio.html", (_req, reply) => {
|
|
8589
|
+
reply.redirect("/studio");
|
|
8590
|
+
});
|
|
8591
|
+
app.get("/index-v2-pixel.html", (_req, reply) => {
|
|
8592
|
+
reply.redirect("/pixel");
|
|
8593
|
+
});
|
|
7534
8594
|
app.setNotFoundHandler((req, reply) => {
|
|
7535
8595
|
if (req.url.startsWith("/api/")) {
|
|
7536
8596
|
reply.code(404).send({ error: "Not found" });
|
|
7537
8597
|
return;
|
|
7538
8598
|
}
|
|
7539
|
-
const
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
reply.sendFile("index-studio.html");
|
|
7543
|
-
} else if (existsSync19(indexPath)) {
|
|
7544
|
-
reply.sendFile("index.html");
|
|
8599
|
+
const builtIndexPath = join17(webDir, "dist", "index.html");
|
|
8600
|
+
if (existsSync20(builtIndexPath)) {
|
|
8601
|
+
reply.sendFile("dist/index.html");
|
|
7545
8602
|
} else {
|
|
7546
8603
|
reply.code(200).header("content-type", "text/html").send(getEmbeddedHtml());
|
|
7547
8604
|
}
|
|
@@ -7691,8 +8748,9 @@ var init_server = __esm({
|
|
|
7691
8748
|
init_agents();
|
|
7692
8749
|
init_studio();
|
|
7693
8750
|
init_croc_office();
|
|
8751
|
+
init_studio_store();
|
|
7694
8752
|
__filename2 = fileURLToPath2(import.meta.url);
|
|
7695
|
-
__dirname2 =
|
|
8753
|
+
__dirname2 = dirname8(__filename2);
|
|
7696
8754
|
}
|
|
7697
8755
|
});
|
|
7698
8756
|
|