0c15d3d0b2
M2 delivers the read-only MCP capability broker gateway and four Day-1 infrastructure adapters (Proxmox, SSH/Linux, GitHub, Gitea). 13 REQs (015-027) all pass. 656 tests green. M1 non-regression verified. MCP spec 2025-06-18 conformance verified (PROTOCOL.md + 7 tests). Defense-in-depth SSH (broker layer 1 + Relay Agent layer 2 + no-shell exec). Two-track LLM smoke (Track A mock-path P0 gate passes). CI: Gitea Actions (.gitea/workflows/ci.yml) with Postgres 16 + RLS verification. Phases shipped: P0 pre-execution v0.1.0 P1 Wave F — MCP gateway v0.1.1 P2 Wave G — Proxmox v0.1.2 P3 Wave H — SSH/Linux v0.1.3 P4 Wave I — Git adapters v0.1.4 P5 Wave J — SSE+smoke+UI v0.1.5 P6 Final — review+ship v0.1.6 ← milestone release ---ci--- phase: 6 milestone: v0.2 status: complete phase_role: final milestone_complete: true requirements: covered: [REQ-015, REQ-016, REQ-017, REQ-018, REQ-019, REQ-020, REQ-021, REQ-022, REQ-023, REQ-024, REQ-025, REQ-026, REQ-027] partial: [] ---/ci---
87 lines
3.2 KiB
JavaScript
87 lines
3.2 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* check-llm-mock-guard.mjs — build-time grep guard (R-008, G-018 Task 6).
|
|
*
|
|
* `@coreci/llm-mock` is a CI-only devDependency. It MUST NOT appear in the
|
|
* prod build output of the control plane (`.next/`), nor in the prod source
|
|
* of the broker (`packages/mcp/src/`). The eslint `no-restricted-imports`
|
|
* rule is the primary guard; this grep is the defense-in-depth backstop for
|
|
* a stray import that slips past linting (e.g. a dynamic import string).
|
|
*
|
|
* Run via `pnpm check:llm-mock-guard`. Exits non-zero if `llm-mock` appears in:
|
|
* - apps/control-plane/.next/** (after `pnpm build`)
|
|
* - apps/control-plane/app/** (prod source)
|
|
* - apps/control-plane/lib/** (prod source)
|
|
* - packages/mcp/src/** (prod broker source)
|
|
*
|
|
* Exempts tests/** (the smoke imports the mock) and packages/llm-mock/**
|
|
* (the package itself).
|
|
*
|
|
* Usage: node scripts/check-llm-mock-guard.mjs [--built]
|
|
* --built: also scan apps/control-plane/.next/ (after a build). Skipped by
|
|
* default so the check runs fast in CI before the build step.
|
|
*/
|
|
import { readdirSync, statSync, readFileSync, existsSync } from "node:fs";
|
|
import { join, relative } from "node:path";
|
|
import { argv, cwd, exit } from "node:process";
|
|
|
|
const root = cwd();
|
|
const scanBuilt = argv.includes("--built");
|
|
|
|
/** Directories whose PROD source must not import @coreci/llm-mock. */
|
|
const prodSourceRoots = [
|
|
join(root, "apps/control-plane/app"),
|
|
join(root, "apps/control-plane/lib"),
|
|
join(root, "packages/mcp/src"),
|
|
];
|
|
|
|
/** Build output directories scanned only with --built (after `pnpm build`). */
|
|
const builtRoots = scanBuilt ? [join(root, "apps/control-plane/.next")] : [];
|
|
|
|
/** Extensions to scan (source + bundled JS). */
|
|
const exts = [".ts", ".tsx", ".js", ".mjs", ".cjs", ".jsx"];
|
|
|
|
/** Walk a directory recursively, yielding file paths matching the extensions. */
|
|
function* walk(dir) {
|
|
if (!existsSync(dir)) return;
|
|
for (const entry of readdirSync(dir)) {
|
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
const full = join(dir, entry);
|
|
const st = statSync(full);
|
|
if (st.isDirectory()) {
|
|
yield* walk(full);
|
|
} else if (st.isFile() && exts.some((e) => entry.endsWith(e))) {
|
|
yield full;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** The forbidden substrings (catch any import path into the mock). */
|
|
const forbidden = ["@coreci/llm-mock", "llm-mock/server", "llm-mock/patterns", "llm-mock/retry"];
|
|
|
|
let violations = 0;
|
|
const roots = [...prodSourceRoots, ...builtRoots];
|
|
for (const rootDir of roots) {
|
|
for (const file of walk(rootDir)) {
|
|
let content;
|
|
try {
|
|
content = readFileSync(file, "utf8");
|
|
} catch {
|
|
continue; // unreadable (binary) — skip
|
|
}
|
|
for (const needle of forbidden) {
|
|
if (content.includes(needle)) {
|
|
const rel = relative(root, file);
|
|
console.error(`[llm-mock-guard] VIOLATION: '${needle}' found in ${rel}`);
|
|
violations++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (violations > 0) {
|
|
console.error(`\n[llm-mock-guard] ${violations} violation(s) found. @coreci/llm-mock is CI-only (R-008).`);
|
|
console.error("Remove the import from prod code; the LLM smoke imports the mock from tests/**.");
|
|
exit(1);
|
|
}
|
|
console.log(`[llm-mock-guard] OK — no @coreci/llm-mock imports in prod source${scanBuilt ? " or build output" : ""}.`); |