@shahmilsaari/memory-core 1.0.22 → 1.0.26
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/README.md +75 -868
- package/dist/approval-queue-YBYRGBHP.js +7 -0
- package/dist/ast-analyzer-JM4CIOFY.js +44 -0
- package/dist/check-cache-6NWRTZJD.js +52 -0
- package/dist/check-logger-5HYSWA3S.js +21 -0
- package/dist/{chunk-UZDALJVQ.js → chunk-3XTHE74V.js} +2488 -1461
- package/dist/chunk-M7NKSXFS.js +301 -0
- package/dist/chunk-PQBWHAZN.js +156 -0
- package/dist/chunk-W6WEAV3S.js +69 -0
- package/dist/chunk-ZZBQEXEO.js +183 -0
- package/dist/classifier-MZ65R7FK.js +60 -0
- package/dist/cli.js +868 -1585
- package/dist/confidence-gate-ZQDAOS6P.js +64 -0
- package/dist/dashboard/assets/index-CE3AMEOD.js +2 -0
- package/dist/dashboard/assets/{index-DXXHB1Ik.css → index-CNc2vvZF.css} +1 -1
- package/dist/dashboard/index.html +2 -2
- package/dist/{dashboard-server-VOT2ZRVN.js → dashboard-server-EEFNE6NX.js} +161 -14
- package/dist/db-PRDHI2CN.js +29 -0
- package/dist/deepseek-critique-MALVIYGF.js +82 -0
- package/dist/deterministic-validator-PP56B46I.js +18 -0
- package/dist/evidence-HVMSONTT.js +65 -0
- package/dist/graph-TFNTB5OK.js +98 -0
- package/dist/incident-capture-RVPZULS7.js +20 -0
- package/dist/ollama-judge-D2LFK5PB.js +137 -0
- package/dist/rate-limiter-SLIPCXRF.js +41 -0
- package/dist/rules-V3QMN3AR.js +95 -0
- package/dist/watch-errors-B3FA26N4.js +99 -0
- package/package.json +1 -1
- package/dist/dashboard/assets/index-BRqvIBnm.js +0 -2
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
detectProject,
|
|
4
|
+
embed,
|
|
5
|
+
inferProjectArchitectures,
|
|
6
|
+
startWatch
|
|
7
|
+
} from "./chunk-3XTHE74V.js";
|
|
8
|
+
import {
|
|
9
|
+
getChatProviderLabel
|
|
10
|
+
} from "./chunk-PQBWHAZN.js";
|
|
2
11
|
import {
|
|
3
12
|
Config,
|
|
4
13
|
closePool,
|
|
5
14
|
deleteMemory,
|
|
6
|
-
detectProject,
|
|
7
|
-
embed,
|
|
8
|
-
getChatProviderLabel,
|
|
9
15
|
getPool,
|
|
10
|
-
inferProjectArchitectures,
|
|
11
16
|
listMemories,
|
|
12
17
|
saveMemory,
|
|
13
|
-
startWatch,
|
|
14
18
|
updateMemory
|
|
15
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-M7NKSXFS.js";
|
|
20
|
+
import "./chunk-ZZBQEXEO.js";
|
|
16
21
|
|
|
17
22
|
// src/dashboard-server.ts
|
|
18
23
|
import { createHash } from "crypto";
|
|
19
|
-
import { createReadStream, existsSync, readFileSync, watch } from "fs";
|
|
24
|
+
import { createReadStream, existsSync, mkdirSync, readFileSync, watch, writeFileSync } from "fs";
|
|
20
25
|
import { createServer } from "http";
|
|
21
26
|
import { basename, dirname, extname, join, normalize, relative, resolve } from "path";
|
|
22
27
|
import { fileURLToPath } from "url";
|
|
@@ -47,6 +52,63 @@ var snapshotBroadcastInFlight = false;
|
|
|
47
52
|
var snapshotBroadcastQueued = false;
|
|
48
53
|
var snapshotBroadcastForceRefresh = false;
|
|
49
54
|
var projectRoot = process.cwd();
|
|
55
|
+
var archState = {
|
|
56
|
+
pendingRules: 0,
|
|
57
|
+
archRuleCount: 0,
|
|
58
|
+
layers: [],
|
|
59
|
+
rules: [],
|
|
60
|
+
lastCheckResult: null
|
|
61
|
+
};
|
|
62
|
+
var HISTORY_MAX = 200;
|
|
63
|
+
function historyPath() {
|
|
64
|
+
return join(projectRoot, ".archmind", "violation-history.json");
|
|
65
|
+
}
|
|
66
|
+
function readViolationHistory() {
|
|
67
|
+
const path = historyPath();
|
|
68
|
+
if (!existsSync(path)) return [];
|
|
69
|
+
try {
|
|
70
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
71
|
+
} catch {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function appendViolationHistory(file, violations, timestamp) {
|
|
76
|
+
try {
|
|
77
|
+
const archmindDir = join(projectRoot, ".archmind");
|
|
78
|
+
mkdirSync(archmindDir, { recursive: true });
|
|
79
|
+
const history = readViolationHistory();
|
|
80
|
+
history.unshift({ timestamp, file, violations });
|
|
81
|
+
if (history.length > HISTORY_MAX) history.length = HISTORY_MAX;
|
|
82
|
+
writeFileSync(historyPath(), JSON.stringify(history, null, 2), "utf-8");
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function readArchState() {
|
|
87
|
+
const archmindDir = join(projectRoot, ".archmind");
|
|
88
|
+
const queuePath = join(archmindDir, "approval-queue.json");
|
|
89
|
+
const rulesPath = join(archmindDir, "rules.json");
|
|
90
|
+
const layersPath = join(archmindDir, "layers.json");
|
|
91
|
+
try {
|
|
92
|
+
const queue = readJsonFile(queuePath, {});
|
|
93
|
+
archState.pendingRules = Array.isArray(queue.items) ? queue.items.length : 0;
|
|
94
|
+
} catch {
|
|
95
|
+
archState.pendingRules = 0;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const config = readJsonFile(rulesPath, {});
|
|
99
|
+
archState.rules = Array.isArray(config.rules) ? config.rules : [];
|
|
100
|
+
archState.archRuleCount = archState.rules.length;
|
|
101
|
+
} catch {
|
|
102
|
+
archState.rules = [];
|
|
103
|
+
archState.archRuleCount = 0;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const config = readJsonFile(layersPath, {});
|
|
107
|
+
archState.layers = Array.isArray(config.layers) ? config.layers : [];
|
|
108
|
+
} catch {
|
|
109
|
+
archState.layers = [];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
50
112
|
function resolveDashboardProjectRoot(inputPath) {
|
|
51
113
|
const candidate = (inputPath ?? process.cwd()).trim();
|
|
52
114
|
if (candidate.endsWith(".memory-core.json")) {
|
|
@@ -85,7 +147,7 @@ function resolveDeclaredArchitectures(config, detectedFramework) {
|
|
|
85
147
|
const declaredArchitectures = [...new Set([
|
|
86
148
|
backendArchitecture,
|
|
87
149
|
frontendFramework
|
|
88
|
-
].filter((value) => Boolean(value)))];
|
|
150
|
+
].filter((value) => Boolean(value) && value !== "custom"))];
|
|
89
151
|
if (declaredArchitectures.length > 0) {
|
|
90
152
|
return { backendArchitecture, frontendFramework, declaredArchitectures };
|
|
91
153
|
}
|
|
@@ -145,8 +207,7 @@ function parseEnvFile(raw) {
|
|
|
145
207
|
return values;
|
|
146
208
|
}
|
|
147
209
|
function getRuntimeEnvPath() {
|
|
148
|
-
|
|
149
|
-
return existsSync(memoryEnv) ? memoryEnv : join(projectRoot, ".env");
|
|
210
|
+
return join(projectRoot, ".memory-core.env");
|
|
150
211
|
}
|
|
151
212
|
function reloadRuntimeEnv() {
|
|
152
213
|
const envPath = getRuntimeEnvPath();
|
|
@@ -179,9 +240,20 @@ function buildStatsPayload() {
|
|
|
179
240
|
files,
|
|
180
241
|
topRules: topEntries(rules),
|
|
181
242
|
topFiles: topEntries(files),
|
|
182
|
-
recentViolations: stats.recentViolations ?? []
|
|
243
|
+
recentViolations: stats.recentViolations ?? [],
|
|
244
|
+
tokens: stats.tokens ?? { calls: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
245
|
+
bypasses: stats.bypasses ?? { total: 0, withReason: 0, withoutReason: 0 }
|
|
183
246
|
};
|
|
184
247
|
}
|
|
248
|
+
function broadcastStats() {
|
|
249
|
+
const payload = JSON.stringify({ type: "stats", stats: buildStatsPayload(), timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
250
|
+
for (const client of clients) {
|
|
251
|
+
try {
|
|
252
|
+
client.write(payload);
|
|
253
|
+
} catch {
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
185
257
|
function redactDatabaseUrl(url) {
|
|
186
258
|
if (!url) return "(not set)";
|
|
187
259
|
return url.replace(/:\/\/([^:@/]+)(?::[^@/]+)?@/, "://$1:***@");
|
|
@@ -407,6 +479,7 @@ async function getSnapshotBase(forceRefresh = false) {
|
|
|
407
479
|
}
|
|
408
480
|
async function getSnapshot(options = {}) {
|
|
409
481
|
const base = await getSnapshotBase(options.forceBaseRefresh ?? false);
|
|
482
|
+
readArchState();
|
|
410
483
|
return {
|
|
411
484
|
config: base.config,
|
|
412
485
|
runtime: base.runtime,
|
|
@@ -416,7 +489,9 @@ async function getSnapshot(options = {}) {
|
|
|
416
489
|
watcher: watcherStatus,
|
|
417
490
|
memories: base.memories,
|
|
418
491
|
memoryCount: base.memoryCount,
|
|
419
|
-
dbError: base.dbError
|
|
492
|
+
dbError: base.dbError,
|
|
493
|
+
arch: { ...archState },
|
|
494
|
+
violationHistory: readViolationHistory().slice(0, 50)
|
|
420
495
|
};
|
|
421
496
|
}
|
|
422
497
|
function sendJson(res, status, payload) {
|
|
@@ -528,6 +603,53 @@ async function handleApi(req, res, url) {
|
|
|
528
603
|
scheduleSnapshotBroadcast({ delayMs: 0, forceBaseRefresh: true });
|
|
529
604
|
return;
|
|
530
605
|
}
|
|
606
|
+
if (req.method === "POST" && url.pathname === "/api/config/model") {
|
|
607
|
+
const body = await readBody(req);
|
|
608
|
+
const provider = typeof body.provider === "string" && body.provider.trim() ? body.provider.trim() : null;
|
|
609
|
+
const model = typeof body.model === "string" && body.model.trim() ? body.model.trim() : null;
|
|
610
|
+
if (!provider && !model) {
|
|
611
|
+
sendJson(res, 400, { error: "provider or model is required" });
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
const envPath = join(projectRoot, ".memory-core.env");
|
|
615
|
+
const existing = existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
|
|
616
|
+
let lines = existing.split("\n").filter((l) => l.trim());
|
|
617
|
+
if (provider) {
|
|
618
|
+
lines = lines.filter((l) => !l.startsWith("CHAT_PROVIDER="));
|
|
619
|
+
lines.push(`CHAT_PROVIDER=${provider}`);
|
|
620
|
+
}
|
|
621
|
+
if (model) {
|
|
622
|
+
lines = lines.filter((l) => !l.startsWith("CHAT_MODEL=") && !l.startsWith("OLLAMA_CHAT_MODEL="));
|
|
623
|
+
lines.push(`CHAT_MODEL=${model}`);
|
|
624
|
+
}
|
|
625
|
+
writeFileSync(envPath, lines.join("\n") + "\n", "utf-8");
|
|
626
|
+
reloadRuntimeEnv();
|
|
627
|
+
invalidateSnapshotBase();
|
|
628
|
+
scheduleSnapshotBroadcast({ delayMs: 0, forceBaseRefresh: true });
|
|
629
|
+
sendJson(res, 200, { ok: true });
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
if (req.method === "POST" && url.pathname === "/api/actions/sync") {
|
|
633
|
+
const { spawn } = await import("child_process");
|
|
634
|
+
const proc = spawn("memory-core", ["sync", "--no-confirm"], { cwd: projectRoot, detached: true, stdio: "ignore" });
|
|
635
|
+
proc.unref();
|
|
636
|
+
sendJson(res, 200, { ok: true, message: "Sync started" });
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
if (req.method === "POST" && url.pathname === "/api/actions/check-all") {
|
|
640
|
+
const { spawn } = await import("child_process");
|
|
641
|
+
const proc = spawn("memory-core", ["check", "--all"], { cwd: projectRoot, detached: true, stdio: "ignore" });
|
|
642
|
+
proc.unref();
|
|
643
|
+
sendJson(res, 200, { ok: true, message: "Full scan started" });
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (req.method === "POST" && url.pathname === "/api/actions/tune") {
|
|
647
|
+
const { spawn } = await import("child_process");
|
|
648
|
+
const proc = spawn("memory-core", ["tune"], { cwd: projectRoot, detached: true, stdio: "ignore" });
|
|
649
|
+
proc.unref();
|
|
650
|
+
sendJson(res, 200, { ok: true, message: "Tune started" });
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
531
653
|
sendJson(res, 404, { error: "Not found" });
|
|
532
654
|
} catch (err) {
|
|
533
655
|
sendJson(res, 500, { error: err.message });
|
|
@@ -549,8 +671,14 @@ function contentType(path) {
|
|
|
549
671
|
return "application/octet-stream";
|
|
550
672
|
}
|
|
551
673
|
}
|
|
674
|
+
function resolveDashboardDir() {
|
|
675
|
+
const distDir = join(projectRoot, "dist", "dashboard");
|
|
676
|
+
if (existsSync(join(distDir, "index.html"))) return distDir;
|
|
677
|
+
const srcDir = join(fileURLToPath(new URL(".", import.meta.url)), "dashboard");
|
|
678
|
+
return srcDir;
|
|
679
|
+
}
|
|
552
680
|
function serveStatic(req, res, url) {
|
|
553
|
-
const dashboardDir =
|
|
681
|
+
const dashboardDir = resolveDashboardDir();
|
|
554
682
|
const requested = url.pathname === "/" ? "/index.html" : url.pathname;
|
|
555
683
|
const filePath = normalize(join(dashboardDir, requested));
|
|
556
684
|
if (!filePath.startsWith(dashboardDir) || relative(dashboardDir, filePath).startsWith("..")) {
|
|
@@ -692,6 +820,7 @@ function handleWatchEvent(event) {
|
|
|
692
820
|
lastSeen: event.timestamp,
|
|
693
821
|
violations: event.violations
|
|
694
822
|
});
|
|
823
|
+
appendViolationHistory(event.file, event.violations, event.timestamp);
|
|
695
824
|
}
|
|
696
825
|
if (event.type === "error") {
|
|
697
826
|
watcherStatus.error = event.message;
|
|
@@ -726,18 +855,25 @@ function acceptWebSocket(req, socket) {
|
|
|
726
855
|
function startConfigWatch() {
|
|
727
856
|
let timer;
|
|
728
857
|
const watchedFiles = [".env", ".memory-core.env", ".memory-core.json", ".memory-core-stats.json"];
|
|
858
|
+
const archmindFiles = ["approval-queue.json", "rules.json"];
|
|
859
|
+
const archmindDir = join(projectRoot, ".archmind");
|
|
729
860
|
const watchedPaths = /* @__PURE__ */ new Set();
|
|
730
861
|
const watchers = [];
|
|
731
862
|
const reload = (filePath) => {
|
|
732
863
|
if (timer) clearTimeout(timer);
|
|
733
864
|
timer = setTimeout(async () => {
|
|
865
|
+
const fileName = filePath.split("/").pop() ?? "";
|
|
866
|
+
if (fileName === ".memory-core-stats.json") {
|
|
867
|
+
broadcastStats();
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
734
870
|
reloadRuntimeEnv();
|
|
735
871
|
await closePool();
|
|
736
872
|
invalidateSnapshotBase();
|
|
737
873
|
recentEvents.push({
|
|
738
874
|
type: "error",
|
|
739
875
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
740
|
-
message: `Configuration reloaded from ${
|
|
876
|
+
message: `Configuration reloaded from ${fileName}`
|
|
741
877
|
});
|
|
742
878
|
if (recentEvents.length > 100) recentEvents.shift();
|
|
743
879
|
scheduleSnapshotBroadcast({ forceBaseRefresh: true });
|
|
@@ -759,6 +895,16 @@ function startConfigWatch() {
|
|
|
759
895
|
}
|
|
760
896
|
reload(filePath);
|
|
761
897
|
}));
|
|
898
|
+
if (existsSync(archmindDir)) {
|
|
899
|
+
watchers.push(watch(archmindDir, (_eventType, filename) => {
|
|
900
|
+
if (typeof filename !== "string" || !archmindFiles.includes(filename)) return;
|
|
901
|
+
if (timer) clearTimeout(timer);
|
|
902
|
+
timer = setTimeout(() => {
|
|
903
|
+
readArchState();
|
|
904
|
+
scheduleSnapshotBroadcast({ forceBaseRefresh: false });
|
|
905
|
+
}, 150);
|
|
906
|
+
}));
|
|
907
|
+
}
|
|
762
908
|
return () => {
|
|
763
909
|
if (timer) clearTimeout(timer);
|
|
764
910
|
for (const watcher of watchers) watcher.close();
|
|
@@ -815,6 +961,7 @@ async function startDashboard(options = {}) {
|
|
|
815
961
|
}
|
|
816
962
|
export {
|
|
817
963
|
mapFrameworkToArchitecture,
|
|
964
|
+
readViolationHistory,
|
|
818
965
|
resolveDashboardProjectRoot,
|
|
819
966
|
resolveDeclaredArchitectures,
|
|
820
967
|
startDashboard
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
closePool,
|
|
4
|
+
deleteMemories,
|
|
5
|
+
deleteMemory,
|
|
6
|
+
getMemory,
|
|
7
|
+
getPool,
|
|
8
|
+
hashMemoryContent,
|
|
9
|
+
listMemories,
|
|
10
|
+
runMigrations,
|
|
11
|
+
saveMemory,
|
|
12
|
+
searchMemories,
|
|
13
|
+
updateMemory,
|
|
14
|
+
upsertMemory
|
|
15
|
+
} from "./chunk-M7NKSXFS.js";
|
|
16
|
+
export {
|
|
17
|
+
closePool,
|
|
18
|
+
deleteMemories,
|
|
19
|
+
deleteMemory,
|
|
20
|
+
getMemory,
|
|
21
|
+
getPool,
|
|
22
|
+
hashMemoryContent,
|
|
23
|
+
listMemories,
|
|
24
|
+
runMigrations,
|
|
25
|
+
saveMemory,
|
|
26
|
+
searchMemories,
|
|
27
|
+
updateMemory,
|
|
28
|
+
upsertMemory
|
|
29
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
callChatModel
|
|
4
|
+
} from "./chunk-PQBWHAZN.js";
|
|
5
|
+
|
|
6
|
+
// src/models/deepseek-critique.ts
|
|
7
|
+
function loadDeepSeekConfig(_configDir) {
|
|
8
|
+
return {};
|
|
9
|
+
}
|
|
10
|
+
var DeepSeekCritique = class {
|
|
11
|
+
constructor(_config) {
|
|
12
|
+
}
|
|
13
|
+
async critique(packet, primaryDecision) {
|
|
14
|
+
try {
|
|
15
|
+
const result = await callChatModel([
|
|
16
|
+
{
|
|
17
|
+
role: "system",
|
|
18
|
+
content: "You are an expert architecture reviewer. Validate or override model decisions based only on the provided evidence. Return ONLY valid JSON."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
role: "user",
|
|
22
|
+
content: buildCritiquePrompt(packet, primaryDecision)
|
|
23
|
+
}
|
|
24
|
+
]);
|
|
25
|
+
return parseDecision(result.content, primaryDecision);
|
|
26
|
+
} catch {
|
|
27
|
+
return {
|
|
28
|
+
decision: primaryDecision.decision,
|
|
29
|
+
confidence: primaryDecision.confidence,
|
|
30
|
+
reasoning: "Chat model unavailable for critique \u2014 using primary decision",
|
|
31
|
+
override: false
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
function buildCritiquePrompt(packet, primary) {
|
|
37
|
+
return `Validate this architecture review decision.
|
|
38
|
+
|
|
39
|
+
PRIMARY DECISION:
|
|
40
|
+
- Decision: ${primary.decision}
|
|
41
|
+
- Confidence: ${primary.confidence}
|
|
42
|
+
- Reasoning: ${primary.reasoning}
|
|
43
|
+
- Violations cited: ${primary.violations.join(", ") || "none"}
|
|
44
|
+
|
|
45
|
+
EVIDENCE SUMMARY:
|
|
46
|
+
- Changed files: ${packet.changedFiles.map((f) => `${f.file} (${f.layer})`).join(", ") || "none"}
|
|
47
|
+
- Violations: ${packet.violations.map((v) => `${v.rule.name}: ${v.fromFile} \u2192 ${v.toFile}`).join(", ") || "none"}
|
|
48
|
+
- Graph violations: ${packet.graphViolations.map((v) => v.ruleName).join(", ") || "none"}
|
|
49
|
+
- Matched rules: ${packet.matchedRules.map((r) => r.name).join(", ") || "none"}
|
|
50
|
+
|
|
51
|
+
TASK:
|
|
52
|
+
1. Is the primary decision supported by the evidence?
|
|
53
|
+
2. Were any violations missed?
|
|
54
|
+
3. Should the decision be overridden?
|
|
55
|
+
|
|
56
|
+
Return ONLY this JSON, no other text:
|
|
57
|
+
{
|
|
58
|
+
"decision": "allow",
|
|
59
|
+
"confidence": 0.9,
|
|
60
|
+
"reasoning": "",
|
|
61
|
+
"override": false
|
|
62
|
+
}`;
|
|
63
|
+
}
|
|
64
|
+
function parseDecision(raw, fallback) {
|
|
65
|
+
try {
|
|
66
|
+
const match = raw.match(/\{[\s\S]*\}/);
|
|
67
|
+
if (!match) throw new Error("no JSON");
|
|
68
|
+
const parsed = JSON.parse(match[0]);
|
|
69
|
+
return {
|
|
70
|
+
decision: ["allow", "warn", "block"].includes(parsed.decision) ? parsed.decision : fallback.decision,
|
|
71
|
+
confidence: typeof parsed.confidence === "number" ? Math.max(0, Math.min(1, parsed.confidence)) : fallback.confidence,
|
|
72
|
+
reasoning: parsed.reasoning ?? "",
|
|
73
|
+
override: parsed.override ?? false
|
|
74
|
+
};
|
|
75
|
+
} catch {
|
|
76
|
+
return { decision: fallback.decision, confidence: fallback.confidence, reasoning: "Parse error", override: false };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
DeepSeekCritique,
|
|
81
|
+
loadDeepSeekConfig
|
|
82
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/models/deterministic-validator.ts
|
|
4
|
+
var DeterministicValidator = class {
|
|
5
|
+
validate(packet) {
|
|
6
|
+
if (packet.violations.some((v) => v.rule.severity === "critical")) return "block";
|
|
7
|
+
if (packet.graphViolations.length > 0) return "block";
|
|
8
|
+
const newInRestricted = packet.changedFiles.filter(
|
|
9
|
+
(f) => f.isNew && ["infrastructure", "config"].includes(f.layer)
|
|
10
|
+
);
|
|
11
|
+
if (newInRestricted.length > 0) return "warn";
|
|
12
|
+
if (packet.violations.some((v) => v.rule.severity === "medium")) return "warn";
|
|
13
|
+
return "allow";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
export {
|
|
17
|
+
DeterministicValidator
|
|
18
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/core/evidence.ts
|
|
4
|
+
import { dirname, resolve } from "path";
|
|
5
|
+
var EvidencePacketBuilder = class {
|
|
6
|
+
constructor(cwd = process.cwd()) {
|
|
7
|
+
this.cwd = cwd;
|
|
8
|
+
}
|
|
9
|
+
cwd;
|
|
10
|
+
async build(diff, astAnalyzer, classifier, ruleMatcher, graph) {
|
|
11
|
+
const changedFiles = classifier.classifyDiff(diff);
|
|
12
|
+
const layersAffected = [...new Set(changedFiles.map((f) => f.layer))];
|
|
13
|
+
const importsMap = [];
|
|
14
|
+
const violations = [];
|
|
15
|
+
for (const file of [...diff.added, ...diff.modified]) {
|
|
16
|
+
const astInfo = astAnalyzer.analyze(file);
|
|
17
|
+
importsMap.push({ file, imports: astInfo.imports });
|
|
18
|
+
const fromClassified = classifier.classifyFile(file);
|
|
19
|
+
if (!fromClassified) continue;
|
|
20
|
+
for (const imp of astInfo.imports) {
|
|
21
|
+
if (imp.isExternal) continue;
|
|
22
|
+
const importedFile = this.resolveImport(file, imp.module);
|
|
23
|
+
const toClassified = classifier.classifyFile(importedFile);
|
|
24
|
+
if (!toClassified) continue;
|
|
25
|
+
const violatingRules = ruleMatcher.findViolatingRules(fromClassified.layer, toClassified.layer);
|
|
26
|
+
for (const rule of violatingRules) {
|
|
27
|
+
violations.push({ rule, fromFile: file, toFile: importedFile, imports: [imp] });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const graphViolations = graph.findViolations(ruleMatcher.getAllRules());
|
|
32
|
+
const matchedRules = ruleMatcher.getRulesForLayers(layersAffected);
|
|
33
|
+
return {
|
|
34
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35
|
+
changedFiles,
|
|
36
|
+
layersAffected,
|
|
37
|
+
imports: importsMap,
|
|
38
|
+
matchedRules,
|
|
39
|
+
violations,
|
|
40
|
+
graphViolations,
|
|
41
|
+
diffSummary: this.generateDiffSummary(changedFiles),
|
|
42
|
+
metadata: {
|
|
43
|
+
totalFilesChanged: diff.added.length + diff.modified.length,
|
|
44
|
+
newFiles: diff.added.length,
|
|
45
|
+
layerTransitions: violations.length
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
generateDiffSummary(files) {
|
|
50
|
+
const layers = [...new Set(files.map((f) => f.layer))];
|
|
51
|
+
const newCount = files.filter((f) => f.isNew).length;
|
|
52
|
+
return `Modified ${files.length} files across ${layers.length} layer(s) (${newCount} new)`;
|
|
53
|
+
}
|
|
54
|
+
resolveImport(fromFile, importPath) {
|
|
55
|
+
if (importPath.startsWith(".")) {
|
|
56
|
+
const abs = resolve(this.cwd, dirname(fromFile), importPath).replace(/\\/g, "/");
|
|
57
|
+
const base = this.cwd.replace(/\\/g, "/").replace(/\/?$/, "/");
|
|
58
|
+
return abs.startsWith(base) ? abs.slice(base.length) : abs;
|
|
59
|
+
}
|
|
60
|
+
return importPath;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
export {
|
|
64
|
+
EvidencePacketBuilder
|
|
65
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/core/graph.ts
|
|
4
|
+
import { dirname, resolve } from "path";
|
|
5
|
+
var DependencyGraph = class {
|
|
6
|
+
nodes = /* @__PURE__ */ new Map();
|
|
7
|
+
edges = [];
|
|
8
|
+
addNode(file, layer, imports) {
|
|
9
|
+
this.nodes.set(file, { id: file, layer, imports });
|
|
10
|
+
}
|
|
11
|
+
addEdge(from, to) {
|
|
12
|
+
this.edges.push({ from, to, type: "import" });
|
|
13
|
+
}
|
|
14
|
+
validateDependency(from, to, rules) {
|
|
15
|
+
const fromNode = this.nodes.get(from);
|
|
16
|
+
const toNode = this.nodes.get(to);
|
|
17
|
+
if (!fromNode || !toNode) return null;
|
|
18
|
+
for (const rule of rules) {
|
|
19
|
+
const fromMatches = rule.fromLayer === fromNode.layer || rule.fromLayer === "*";
|
|
20
|
+
const toMatches = rule.toLayer === toNode.layer || rule.toLayer === "*";
|
|
21
|
+
if (fromMatches && toMatches && !rule.allowed) {
|
|
22
|
+
return {
|
|
23
|
+
from,
|
|
24
|
+
fromLayer: fromNode.layer,
|
|
25
|
+
to,
|
|
26
|
+
toLayer: toNode.layer,
|
|
27
|
+
ruleName: rule.name,
|
|
28
|
+
path: [from, to]
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
findViolations(rules) {
|
|
35
|
+
const violations = [];
|
|
36
|
+
for (const edge of this.edges) {
|
|
37
|
+
const v = this.validateDependency(edge.from, edge.to, rules);
|
|
38
|
+
if (v) violations.push(v);
|
|
39
|
+
}
|
|
40
|
+
return violations;
|
|
41
|
+
}
|
|
42
|
+
findPath(from, to) {
|
|
43
|
+
const visited = /* @__PURE__ */ new Set();
|
|
44
|
+
const queue = [[from, [from]]];
|
|
45
|
+
while (queue.length > 0) {
|
|
46
|
+
const [current, path] = queue.shift();
|
|
47
|
+
if (current === to) return path;
|
|
48
|
+
if (visited.has(current)) continue;
|
|
49
|
+
visited.add(current);
|
|
50
|
+
const node = this.nodes.get(current);
|
|
51
|
+
if (!node) continue;
|
|
52
|
+
for (const neighbor of node.imports) {
|
|
53
|
+
if (!visited.has(neighbor)) queue.push([neighbor, [...path, neighbor]]);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
getNodes() {
|
|
59
|
+
return [...this.nodes.values()];
|
|
60
|
+
}
|
|
61
|
+
getEdges() {
|
|
62
|
+
return this.edges;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var GraphBuilder = class {
|
|
66
|
+
constructor(astAnalyzer, classifier, cwd = process.cwd()) {
|
|
67
|
+
this.astAnalyzer = astAnalyzer;
|
|
68
|
+
this.classifier = classifier;
|
|
69
|
+
this.cwd = cwd;
|
|
70
|
+
}
|
|
71
|
+
astAnalyzer;
|
|
72
|
+
classifier;
|
|
73
|
+
cwd;
|
|
74
|
+
async buildFromFiles(files) {
|
|
75
|
+
const graph = new DependencyGraph();
|
|
76
|
+
for (const file of files) {
|
|
77
|
+
const classified = this.classifier.classifyFile(file);
|
|
78
|
+
if (!classified) continue;
|
|
79
|
+
const astInfo = this.astAnalyzer.analyze(file);
|
|
80
|
+
const resolvedImports = astInfo.imports.filter((imp) => !imp.isExternal).map((imp) => this.resolveImport(file, imp.module));
|
|
81
|
+
graph.addNode(file, classified.layer, resolvedImports);
|
|
82
|
+
for (const resolved of resolvedImports) {
|
|
83
|
+
graph.addEdge(file, resolved);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return graph;
|
|
87
|
+
}
|
|
88
|
+
resolveImport(fromFile, importPath) {
|
|
89
|
+
if (importPath.startsWith(".")) {
|
|
90
|
+
return resolve(this.cwd, dirname(fromFile), importPath).replace(/\\/g, "/");
|
|
91
|
+
}
|
|
92
|
+
return importPath;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
export {
|
|
96
|
+
DependencyGraph,
|
|
97
|
+
GraphBuilder
|
|
98
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/automation/incident-capture.ts
|
|
4
|
+
var IncidentCaptureService = class {
|
|
5
|
+
draftRule(incident) {
|
|
6
|
+
return {
|
|
7
|
+
id: `auto-incident-${Date.now()}`,
|
|
8
|
+
name: `[INCIDENT] ${incident.what.slice(0, 60)}`,
|
|
9
|
+
description: `Root cause: ${incident.why}. Applies to: ${incident.where}`,
|
|
10
|
+
fromLayer: "*",
|
|
11
|
+
toLayer: "*",
|
|
12
|
+
allowed: false,
|
|
13
|
+
severity: "critical",
|
|
14
|
+
enforcement: "block"
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export {
|
|
19
|
+
IncidentCaptureService
|
|
20
|
+
};
|