@prooflane/inspector-beta 0.1.0-beta.6 → 0.1.0-beta.7
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.mjs +2 -2
- package/dist/server.mjs +177 -115
- package/dist/ui/assets/index-Dj20G-qT.css +1 -0
- package/dist/ui/assets/index-T0eXH06n.js +69 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/index-CHGGvbAw.js +0 -69
- package/dist/ui/assets/index-D_0fJhzv.css +0 -1
package/dist/server.mjs
CHANGED
|
@@ -4225,7 +4225,7 @@ function validateGovernedGateManifest(value) {
|
|
|
4225
4225
|
if (!record2(value))
|
|
4226
4226
|
return { ok: !1, error: "Gate manifest must be an object." };
|
|
4227
4227
|
let forbidden = findForbiddenGateManifestMaterial(value);
|
|
4228
|
-
return forbidden ? { ok: !1, error: `Gate manifest rejected: ${forbidden}.` } : exactKeys(value, ["schemaVersion", "suiteRef", "policyRef", "targetScope", "environment", "requiredPillars", "settings", "approvalRules"]) ? value.schemaVersion !== GATE_MANIFEST_VERSION ? { ok: !1, error: `Gate manifest must use ${GATE_MANIFEST_VERSION}.` } : validateSuiteReference(value.suiteRef) ? validatePolicyReference(value.policyRef) ? !record2(value.targetScope) || !exactKeys(value.targetScope, ["projectId"]) || !text2(value.targetScope.projectId, 2, 128, projectPattern) ? { ok: !1, error: "Gate targetScope must contain one stable projectId." } : text2(value.environment, 2, 64, slugPattern) ? !Array.isArray(value.requiredPillars) || value.requiredPillars.length < 1 || value.requiredPillars.length > POLICY_PILLARS.length ? { ok: !1, error: "Gate requiredPillars must contain at least one supported Policy pillar." } : value.requiredPillars.every((pillar) => POLICY_PILLARS.includes(pillar)) ? new Set(value.requiredPillars).size !== value.requiredPillars.length ? { ok: !1, error: "Gate requiredPillars must be unique." } : !record2(value.settings) || !exactKeys(value.settings, ["executionMode", "evidenceSync"]) || value.settings.executionMode !== "ALL_CONTROLS" || value.settings.evidenceSync !== "COMPACT_ONLY" ? { ok: !1, error: "Gate settings must execute all signed Suite controls and synchronize compact evidence only." } : !record2(value.approvalRules) || !exactKeys(value.approvalRules, ["minimumApprovals", "requireDistinctReviewer"]) || value.approvalRules.minimumApprovals !== 1 || value.approvalRules.requireDistinctReviewer
|
|
4228
|
+
return forbidden ? { ok: !1, error: `Gate manifest rejected: ${forbidden}.` } : exactKeys(value, ["schemaVersion", "suiteRef", "policyRef", "targetScope", "environment", "requiredPillars", "settings", "approvalRules"]) ? value.schemaVersion !== GATE_MANIFEST_VERSION ? { ok: !1, error: `Gate manifest must use ${GATE_MANIFEST_VERSION}.` } : validateSuiteReference(value.suiteRef) ? validatePolicyReference(value.policyRef) ? !record2(value.targetScope) || !exactKeys(value.targetScope, ["projectId"]) || !text2(value.targetScope.projectId, 2, 128, projectPattern) ? { ok: !1, error: "Gate targetScope must contain one stable projectId." } : text2(value.environment, 2, 64, slugPattern) ? !Array.isArray(value.requiredPillars) || value.requiredPillars.length < 1 || value.requiredPillars.length > POLICY_PILLARS.length ? { ok: !1, error: "Gate requiredPillars must contain at least one supported Policy pillar." } : value.requiredPillars.every((pillar) => POLICY_PILLARS.includes(pillar)) ? new Set(value.requiredPillars).size !== value.requiredPillars.length ? { ok: !1, error: "Gate requiredPillars must be unique." } : !record2(value.settings) || !exactKeys(value.settings, ["executionMode", "evidenceSync"]) || value.settings.executionMode !== "ALL_CONTROLS" || value.settings.evidenceSync !== "COMPACT_ONLY" ? { ok: !1, error: "Gate settings must execute all signed Suite controls and synchronize compact evidence only." } : !record2(value.approvalRules) || !exactKeys(value.approvalRules, ["minimumApprovals", "requireDistinctReviewer"]) || value.approvalRules.minimumApprovals !== 1 || typeof value.approvalRules.requireDistinctReviewer != "boolean" ? { ok: !1, error: "Gate approvalRules must require one recorded approval and a boolean reviewer policy." } : { ok: !0, manifest: value } : { ok: !1, error: "Gate requiredPillars contains an unsupported pillar." } : { ok: !1, error: "Gate environment must be a bounded lowercase slug." } : { ok: !1, error: "Gate policyRef must pin an exact Policy identity, version ID, version, and SHA-256 hash." } : { ok: !1, error: "Gate suiteRef must pin an exact Suite identity, slug, version ID, version, and SHA-256 hash." } : { ok: !1, error: "Gate manifest must contain only the frozen v1 fields." };
|
|
4229
4229
|
}
|
|
4230
4230
|
function hashGovernedGateManifest(value) {
|
|
4231
4231
|
let validated = validateGovernedGateManifest(value);
|
|
@@ -11624,8 +11624,8 @@ function publicServerError(path) {
|
|
|
11624
11624
|
}
|
|
11625
11625
|
var HOSTED_SAMMY_AUTHORIZATION = Symbol("prooflane.hosted-sammy-authorization");
|
|
11626
11626
|
function buildApp() {
|
|
11627
|
-
let
|
|
11628
|
-
|
|
11627
|
+
let app = Fastify({ logger: !1 });
|
|
11628
|
+
app.addHook("onSend", async (request, reply, payload) => !request.url.startsWith("/api") || reply.statusCode < 500 ? payload : (reply.header("cache-control", "no-store"), reply.type("application/json; charset=utf-8"), JSON.stringify({ error: publicServerError(request.url), reference: request.id })));
|
|
11629
11629
|
let store = new Store(dbPath()), svc = new InspectorService(store), mcpManager = new McpConnectionManager(() => new InspectorService(store), store), env = (process.env.KAWACH_ENV ?? process.env.NODE_ENV ?? "production").toLowerCase(), isDev = env === "dev" || env === "development", intelligenceMode = PUBLIC_RUNNER_BUILD || process.env.KAWACH_INTELLIGENCE_MODE === "hosted" ? "hosted" : "legacy", AUTH0_DOMAIN = process.env.AUTH0_DOMAIN, AUTH0_CLIENT_ID = process.env.AUTH0_CLIENT_ID, AUTH0_AUDIENCE = process.env.AUTH0_AUDIENCE, CONTROL_PLANE_URL = process.env.KAWACH_CONTROL_PLANE_URL?.replace(/\/$/, ""), PROOFLANE_PROJECT = (process.env.PROOFLANE_PROJECT ?? "default").trim(), configuredNumber = (name, fallback, minimum, maximum) => {
|
|
11630
11630
|
let value = Number(process.env[name]);
|
|
11631
11631
|
return Number.isFinite(value) ? Math.max(minimum, Math.min(maximum, value)) : fallback;
|
|
@@ -11725,10 +11725,19 @@ function buildApp() {
|
|
|
11725
11725
|
method,
|
|
11726
11726
|
headers: body === void 0 ? { authorization } : { authorization, "content-type": "application/json" },
|
|
11727
11727
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
11728
|
-
}), text6 = await response.text(), payload =
|
|
11729
|
-
|
|
11730
|
-
|
|
11731
|
-
|
|
11728
|
+
}), text6 = await response.text(), payload = null;
|
|
11729
|
+
if (text6)
|
|
11730
|
+
try {
|
|
11731
|
+
payload = JSON.parse(text6);
|
|
11732
|
+
} catch {
|
|
11733
|
+
if (response.ok)
|
|
11734
|
+
return reply.code(502).send({ error: "Prooflane Cloud Workspace returned an invalid response." });
|
|
11735
|
+
payload = response.status === 404 ? { error: "The deployed Prooflane control plane does not support this Inspector route yet. Deploy the matching control-plane build, then retry.", upgrade: !0 } : { error: `Prooflane Cloud request failed (${response.status}).` };
|
|
11736
|
+
}
|
|
11737
|
+
let upstreamError = payload && typeof payload == "object" && typeof payload.error == "string" ? String(payload.error).trim() : "";
|
|
11738
|
+
return !response.ok && response.status === 404 && (!upstreamError || /^not found$/i.test(upstreamError) || /route.*not found/i.test(upstreamError)) && (payload = { error: "The deployed Prooflane control plane does not support this Inspector route yet. Deploy the matching control-plane build, then retry.", upgrade: !0 }), response.ok && await onSuccess?.(payload), reply.code(response.status).send(payload);
|
|
11739
|
+
} catch (error) {
|
|
11740
|
+
return error instanceof SyntaxError ? reply.code(502).send({ error: "Prooflane Cloud Workspace returned an invalid response." }) : reply.code(502).send({ error: "Prooflane Cloud Workspace could not be reached. Check KAWACH_CONTROL_PLANE_URL and your connection." });
|
|
11732
11741
|
}
|
|
11733
11742
|
}
|
|
11734
11743
|
async function requestControlPlaneJson(req, path, method = "GET", body, idempotencyKey) {
|
|
@@ -11745,9 +11754,19 @@ function buildApp() {
|
|
|
11745
11754
|
} catch {
|
|
11746
11755
|
throw new ControlPlaneRequestError(502, "Prooflane Cloud Workspace could not be reached.");
|
|
11747
11756
|
}
|
|
11748
|
-
let text6 = await response.text(), payload
|
|
11749
|
-
|
|
11750
|
-
|
|
11757
|
+
let text6 = await response.text(), payload;
|
|
11758
|
+
try {
|
|
11759
|
+
payload = text6 ? JSON.parse(text6) : {};
|
|
11760
|
+
} catch {
|
|
11761
|
+
if (response.ok)
|
|
11762
|
+
throw new ControlPlaneRequestError(502, "Prooflane Cloud Workspace returned an invalid response.");
|
|
11763
|
+
let message2 = response.status === 404 ? "The deployed Prooflane control plane does not support this Inspector route yet. Deploy the matching control-plane build, then retry." : `Prooflane Cloud request failed (${response.status}).`;
|
|
11764
|
+
throw new ControlPlaneRequestError(response.status, message2);
|
|
11765
|
+
}
|
|
11766
|
+
if (!response.ok) {
|
|
11767
|
+
let upstreamError = typeof payload.error == "string" ? String(payload.error).trim().slice(0, 300) : "", message2 = response.status === 404 && (!upstreamError || /^not found$/i.test(upstreamError) || /route.*not found/i.test(upstreamError)) ? "The deployed Prooflane control plane does not support this Inspector route yet. Deploy the matching control-plane build, then retry." : upstreamError || "Prooflane Cloud request failed.";
|
|
11768
|
+
throw new ControlPlaneRequestError(response.status, message2);
|
|
11769
|
+
}
|
|
11751
11770
|
return payload;
|
|
11752
11771
|
}
|
|
11753
11772
|
async function cloudPlan(req) {
|
|
@@ -11792,7 +11811,7 @@ function buildApp() {
|
|
|
11792
11811
|
async function resolvePlan(req) {
|
|
11793
11812
|
return (await resolvePlanState(req)).plan;
|
|
11794
11813
|
}
|
|
11795
|
-
|
|
11814
|
+
app.addHook("preHandler", async (req, reply) => {
|
|
11796
11815
|
if (req.method === "GET" || req.method === "HEAD" || req.method === "OPTIONS")
|
|
11797
11816
|
return;
|
|
11798
11817
|
let path = req.url.split("?", 1)[0] ?? req.url;
|
|
@@ -11838,17 +11857,54 @@ function buildApp() {
|
|
|
11838
11857
|
let p = await resolvePlan(req);
|
|
11839
11858
|
return { core: p.core, security: p.security, rag: p.rag, deployment: p.deployment, ci: p.ci };
|
|
11840
11859
|
}
|
|
11841
|
-
|
|
11842
|
-
if (intelligenceMode === "hosted" && (/^\/api\/intelligence\/(?:
|
|
11843
|
-
return reply.code(410).send({
|
|
11860
|
+
if (app.addHook("onRequest", async (req, reply) => {
|
|
11861
|
+
if (intelligenceMode === "hosted" && (/^\/api\/intelligence\/(?:effectiveness|learning-candidates)(?:\/|\?|$)/.test(req.url) || /^\/api\/(?:assurance|boundary)(?:\/|\?|$)/.test(req.url)) && req.raw.method !== "OPTIONS")
|
|
11862
|
+
return /^\/api\/assurance\/gates(?:\/|\?|$)/.test(req.url) && reply.header("cache-control", "no-store"), reply.code(410).send({
|
|
11844
11863
|
ok: !1,
|
|
11845
11864
|
error: "This feature uses Hosted Intelligence and is unavailable through the retired local route."
|
|
11846
11865
|
});
|
|
11847
11866
|
let plan = await resolvePlan(req);
|
|
11848
11867
|
mcpManager.reconcileEntitlement(PROOFLANE_PROJECT, mcpEntitlementForPlan(plan)), svc.setToolLimit(plan.tools), mcpManager.setToolLimit(PROOFLANE_PROJECT, plan.tools), activeMcpConnectionId && mcpManager.getConnection(PROOFLANE_PROJECT, activeMcpConnectionId)?.status === "ENTITLEMENT_LOCKED" && svc.setToolLimit(0);
|
|
11849
|
-
}),
|
|
11868
|
+
}), intelligenceMode === "hosted") {
|
|
11869
|
+
let hostedSurfaceRefreshInFlight = null, relayHostedIntelligenceSurface = async (req, reply, refresh) => {
|
|
11870
|
+
try {
|
|
11871
|
+
refresh && svc.isConnected() && (hostedSurfaceRefreshInFlight ??= svc.refreshCapabilities().finally(() => {
|
|
11872
|
+
hostedSurfaceRefreshInFlight = null;
|
|
11873
|
+
}), await hostedSurfaceRefreshInFlight);
|
|
11874
|
+
let capabilities = svc.getCapabilities();
|
|
11875
|
+
return capabilities ? proxyControlPlane(req, reply, "/v1/intelligence/surface", { capabilities }) : { connected: !1 };
|
|
11876
|
+
} catch (error) {
|
|
11877
|
+
return reply.code(503).send({ error: `MCP discovery refresh failed: ${error instanceof Error ? error.message : String(error)}` });
|
|
11878
|
+
}
|
|
11879
|
+
};
|
|
11880
|
+
app.get("/api/intelligence/surface", async (req, reply) => (reply.header("cache-control", "no-store"), relayHostedIntelligenceSurface(req, reply, !1))), app.post("/api/intelligence/surface/refresh", async (req, reply) => relayHostedIntelligenceSurface(req, reply, !0));
|
|
11881
|
+
let hostedSammyPath = `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/intelligence`, hostedCapabilities = (reply) => {
|
|
11882
|
+
let capabilities = svc.getCapabilities();
|
|
11883
|
+
return capabilities || (reply.code(409).send({ error: "Connect an MCP target before using Sammy Intelligence." }), null);
|
|
11884
|
+
}, selectedBody = (value, keys) => {
|
|
11885
|
+
if (!value || typeof value != "object" || Array.isArray(value))
|
|
11886
|
+
return {};
|
|
11887
|
+
let source = value;
|
|
11888
|
+
return Object.fromEntries(keys.filter((key) => source[key] !== void 0).map((key) => [key, source[key]]));
|
|
11889
|
+
};
|
|
11890
|
+
app.post("/api/intelligence/analyze-options", async (req, reply) => {
|
|
11891
|
+
let capabilities = hostedCapabilities(reply);
|
|
11892
|
+
if (capabilities)
|
|
11893
|
+
return proxyControlPlane(req, reply, `${hostedSammyPath}/analyze-options`, { ...selectedBody(req.body, ["graphVersion"]), capabilities });
|
|
11894
|
+
}), app.post("/api/intelligence/generations", async (req, reply) => {
|
|
11895
|
+
let capabilities = hostedCapabilities(reply);
|
|
11896
|
+
if (capabilities)
|
|
11897
|
+
return proxyControlPlane(req, reply, `${hostedSammyPath}/generations`, { ...selectedBody(req.body, ["idempotencyKey", "strategy", "sammyEnabled", "selectedRecommendationIds", "datasetVersionRefs", "promptBundleVersion"]), capabilities });
|
|
11898
|
+
}), app.get("/api/intelligence/generations/:id", async (req, reply) => proxyControlPlane(req, reply, `${hostedSammyPath}/generations/${encodeURIComponent(req.params.id)}`, void 0, "GET")), app.post("/api/intelligence/generations/:id/review", async (req, reply) => proxyControlPlane(req, reply, `${hostedSammyPath}/generations/${encodeURIComponent(req.params.id)}/review`, {})), app.post("/api/intelligence/generations/:id/approve", async (req, reply) => {
|
|
11899
|
+
let capabilities = hostedCapabilities(reply);
|
|
11900
|
+
if (capabilities)
|
|
11901
|
+
return proxyControlPlane(req, reply, `${hostedSammyPath}/generations/${encodeURIComponent(req.params.id)}/approve`, { ...selectedBody(req.body, ["gateId", "name", "policyVersion"]), capabilities });
|
|
11902
|
+
}), app.post("/api/intelligence/generations/:id/compile-suite", async (req, reply) => proxyControlPlane(req, reply, `${hostedSammyPath}/generations/${encodeURIComponent(req.params.id)}/compile-suite`, {})), app.get("/api/intelligence/usage", async (req, reply) => proxyControlPlane(req, reply, `${hostedSammyPath}/usage`, void 0, "GET")), app.get("/api/intelligence/datasets", async (req, reply) => proxyControlPlane(req, reply, `${hostedSammyPath}/datasets`, void 0, "GET")), app.post("/api/intelligence/datasets", async (req, reply) => proxyControlPlane(req, reply, `${hostedSammyPath}/datasets`, selectedBody(req.body, ["name", "format", "content"]))), app.get("/api/intelligence/native-artifacts", async (req, reply) => proxyControlPlane(req, reply, `${hostedSammyPath}/native-artifacts${req.query.pillar ? `?pillar=${encodeURIComponent(req.query.pillar)}` : ""}`, void 0, "GET")), app.post("/api/intelligence/native-artifacts/:generationId/:testId/review", async (req, reply) => proxyControlPlane(req, reply, `${hostedSammyPath}/native-artifacts/${encodeURIComponent(req.params.generationId)}/${encodeURIComponent(req.params.testId)}/review`, selectedBody(req.body, ["action", "replacement", "trustedGolden"]))), app.post("/api/annotations", async (req, reply) => proxyControlPlane(req, reply, `${hostedSammyPath}/annotations`, selectedBody(req.body, ["targetType", "targetId", "labels", "text", "resolution"]))), app.get("/api/annotations/context", async (req, reply) => proxyControlPlane(req, reply, `${hostedSammyPath}/annotations${req.query.targetId ? `?targetId=${encodeURIComponent(req.query.targetId)}` : ""}`, void 0, "GET"));
|
|
11903
|
+
}
|
|
11904
|
+
app.addHook("onSend", async (_req, reply) => {
|
|
11850
11905
|
reply.header("x-content-type-options", "nosniff"), reply.header("x-frame-options", "SAMEORIGIN"), reply.header("referrer-policy", "no-referrer"), reply.header("x-permitted-cross-domain-policies", "none");
|
|
11851
|
-
}),
|
|
11906
|
+
}), app.get("/api/config", async (req, reply) => {
|
|
11907
|
+
reply.header("cache-control", "no-store");
|
|
11852
11908
|
let resolution = await resolvePlanState(req), plan = resolution.plan;
|
|
11853
11909
|
return {
|
|
11854
11910
|
env: isDev ? "dev" : "production",
|
|
@@ -11898,9 +11954,9 @@ function buildApp() {
|
|
|
11898
11954
|
project: { id: PROOFLANE_PROJECT },
|
|
11899
11955
|
intelligenceMode
|
|
11900
11956
|
};
|
|
11901
|
-
}),
|
|
11957
|
+
}), app.post("/api/workspace/consent", async (req, reply) => proxyControlPlane(req, reply, "/v1/workspace/consent", req.body ?? {})), app.post("/api/ci/token", async (req, reply) => proxyControlPlane(req, reply, "/v1/ci/token", { projectId: PROOFLANE_PROJECT }));
|
|
11902
11958
|
let githubOidcTrustPath = `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/github-oidc-trust`;
|
|
11903
|
-
|
|
11959
|
+
app.get("/api/governance/github-oidc-trust", async (req, reply) => proxyControlPlane(req, reply, githubOidcTrustPath, void 0, "GET")), app.put("/api/governance/github-oidc-trust", async (req, reply) => proxyControlPlane(req, reply, githubOidcTrustPath, req.body ?? {}, "PUT")), app.delete("/api/governance/github-oidc-trust", async (req, reply) => proxyControlPlane(req, reply, githubOidcTrustPath, void 0, "DELETE")), app.post("/api/trial/start", async (req, reply) => proxyControlPlane(req, reply, "/v1/trial/start", {})), app.get("/api/workspace/status", async (req, reply) => proxyControlPlane(req, reply, "/v1/workspace", void 0, "GET")), app.get("/api/gate-runs", async (req, reply) => proxyControlPlane(req, reply, `/v1/gate-runs?limit=${encodeURIComponent(req.query.limit ?? "200")}`, void 0, "GET")), app.post("/api/gate-runs", async (req, reply) => proxyControlPlane(req, reply, "/v1/gate-runs", req.body ?? {})), app.get("/api/governed-runs", async (req, reply) => proxyControlPlane(req, reply, `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/runs?limit=${encodeURIComponent(req.query.limit ?? "50")}`, void 0, "GET")), app.post("/api/intelligence/infer", async (req, reply) => {
|
|
11904
11960
|
let result = await relaySammyInferenceBoundary({
|
|
11905
11961
|
request: req.body,
|
|
11906
11962
|
controlPlaneUrl: CONTROL_PLANE_URL,
|
|
@@ -11910,7 +11966,7 @@ function buildApp() {
|
|
|
11910
11966
|
return reply.code(result.status).send(result.payload);
|
|
11911
11967
|
});
|
|
11912
11968
|
let suiteRegistryPath = `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/suites`;
|
|
11913
|
-
|
|
11969
|
+
app.get("/api/suites", async (req, reply) => proxyControlPlane(req, reply, suiteRegistryPath, void 0, "GET")), app.post("/api/suites", async (req, reply) => proxyControlPlane(req, reply, suiteRegistryPath, req.body ?? {})), app.post("/api/governance/suites/:slug/drift", async (req, reply) => {
|
|
11914
11970
|
try {
|
|
11915
11971
|
let capabilities = svc.getCapabilities();
|
|
11916
11972
|
if (!capabilities)
|
|
@@ -11939,7 +11995,7 @@ function buildApp() {
|
|
|
11939
11995
|
} catch (error) {
|
|
11940
11996
|
return error instanceof ControlPlaneRequestError ? reply.code(error.status).send({ error: error.message }) : reply.code(500).send({ error: "Governed surface drift assessment could not complete." });
|
|
11941
11997
|
}
|
|
11942
|
-
}),
|
|
11998
|
+
}), app.get("/api/suites/:slug", async (req, reply) => proxyControlPlane(req, reply, `${suiteRegistryPath}/${encodeURIComponent(req.params.slug)}`, void 0, "GET")), app.patch("/api/suites/:slug", async (req, reply) => proxyControlPlane(req, reply, `${suiteRegistryPath}/${encodeURIComponent(req.params.slug)}`, req.body ?? {}, "PATCH")), app.delete("/api/suites/:slug", async (req, reply) => proxyControlPlane(req, reply, `${suiteRegistryPath}/${encodeURIComponent(req.params.slug)}`, void 0, "DELETE")), app.post("/api/suites/:slug/versions", async (req, reply) => proxyControlPlane(req, reply, `${suiteRegistryPath}/${encodeURIComponent(req.params.slug)}/versions`, req.body ?? {})), app.get("/api/suites/:slug/versions/:version", async (req, reply) => proxyControlPlane(req, reply, `${suiteRegistryPath}/${encodeURIComponent(req.params.slug)}/versions/${encodeURIComponent(req.params.version)}`, void 0, "GET")), app.put("/api/suites/:slug/versions/:version", async (req, reply) => proxyControlPlane(req, reply, `${suiteRegistryPath}/${encodeURIComponent(req.params.slug)}/versions/${encodeURIComponent(req.params.version)}`, req.body ?? {}, "PUT")), app.post("/api/suites/:slug/versions/:version/review", async (req, reply) => proxyControlPlane(req, reply, `${suiteRegistryPath}/${encodeURIComponent(req.params.slug)}/versions/${encodeURIComponent(req.params.version)}/review`, {})), app.post("/api/suites/:slug/versions/:version/approve", async (req, reply) => proxyControlPlane(req, reply, `${suiteRegistryPath}/${encodeURIComponent(req.params.slug)}/versions/${encodeURIComponent(req.params.version)}/approve`, {})), app.post("/api/suites/:slug/versions/:version/deprecate", async (req, reply) => proxyControlPlane(req, reply, `${suiteRegistryPath}/${encodeURIComponent(req.params.slug)}/versions/${encodeURIComponent(req.params.version)}/deprecate`, {})), app.get("/api/suites/:slug/aliases", async (req, reply) => proxyControlPlane(req, reply, `${suiteRegistryPath}/${encodeURIComponent(req.params.slug)}/aliases`, void 0, "GET")), app.get("/api/suite-signing-keys/:keyId", async (req, reply) => proxyControlPlane(req, reply, `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/suite-signing-keys/${encodeURIComponent(req.params.keyId)}`, void 0, "GET"));
|
|
11943
11999
|
let localMigrationInspection = async (req) => {
|
|
11944
12000
|
let cloudResponse = await requestControlPlaneJson(req, suiteRegistryPath);
|
|
11945
12001
|
if (cloudResponse.projectId !== PROOFLANE_PROJECT || !Array.isArray(cloudResponse.suites))
|
|
@@ -11955,14 +12011,14 @@ function buildApp() {
|
|
|
11955
12011
|
let scan = discoverLegacyLocalSuites({ cwd: process.cwd() }), plan = planLegacyLocalSuiteMigration(scan.suites, cloud), suites = plan.map(safeLocalSuiteMigrationSummary);
|
|
11956
12012
|
return { plan, response: { version: "prooflane-local-suite-migration-status-v1", projectId: PROOFLANE_PROJECT, suites, issues: scan.issues, pending: suites.filter((suite) => suite.action === "CREATE_SUITE_DRAFT" || suite.action === "CREATE_VERSION_DRAFT").length, conflicts: scan.issues.length + suites.filter((suite) => suite.action === "CONFLICT").length } };
|
|
11957
12013
|
};
|
|
11958
|
-
|
|
12014
|
+
app.get("/api/suite-migration/status", async (req, reply) => {
|
|
11959
12015
|
try {
|
|
11960
12016
|
return (await localMigrationInspection(req)).response;
|
|
11961
12017
|
} catch (error) {
|
|
11962
12018
|
let status = error instanceof ControlPlaneRequestError ? error.status : 400;
|
|
11963
12019
|
return reply.code(status).send({ error: (error instanceof Error ? error.message : "Local Suite migration status failed.").slice(0, 300) });
|
|
11964
12020
|
}
|
|
11965
|
-
}),
|
|
12021
|
+
}), app.post("/api/suite-migration/sync", async (req, reply) => {
|
|
11966
12022
|
try {
|
|
11967
12023
|
if (!req.body || typeof req.body != "object" || Array.isArray(req.body))
|
|
11968
12024
|
return reply.code(400).send({ error: "Migration sync request must be an object." });
|
|
@@ -12011,15 +12067,15 @@ ${item.artifact.localFingerprint}`));
|
|
|
12011
12067
|
}
|
|
12012
12068
|
});
|
|
12013
12069
|
let gateRegistryPath = `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/gates`;
|
|
12014
|
-
|
|
12070
|
+
app.get("/api/governance/gates", async (req, reply) => proxyControlPlane(req, reply, gateRegistryPath, void 0, "GET")), app.post("/api/governance/gates", async (req, reply) => proxyControlPlane(req, reply, gateRegistryPath, req.body ?? {})), app.get("/api/governance/gates/:slug", async (req, reply) => proxyControlPlane(req, reply, `${gateRegistryPath}/${encodeURIComponent(req.params.slug)}`, void 0, "GET")), app.post("/api/governance/gates/:slug/versions", async (req, reply) => proxyControlPlane(req, reply, `${gateRegistryPath}/${encodeURIComponent(req.params.slug)}/versions`, req.body ?? {})), app.get("/api/governance/gates/:slug/versions/:version", async (req, reply) => proxyControlPlane(req, reply, `${gateRegistryPath}/${encodeURIComponent(req.params.slug)}/versions/${encodeURIComponent(req.params.version)}`, void 0, "GET")), app.put("/api/governance/gates/:slug/versions/:version", async (req, reply) => proxyControlPlane(req, reply, `${gateRegistryPath}/${encodeURIComponent(req.params.slug)}/versions/${encodeURIComponent(req.params.version)}`, req.body ?? {}, "PUT"));
|
|
12015
12071
|
for (let transition of ["review", "approve", "deprecate"])
|
|
12016
|
-
|
|
12017
|
-
|
|
12072
|
+
app.post(`/api/governance/gates/:slug/versions/:version/${transition}`, async (req, reply) => proxyControlPlane(req, reply, `${gateRegistryPath}/${encodeURIComponent(req.params.slug)}/versions/${encodeURIComponent(req.params.version)}/${transition}`, {}));
|
|
12073
|
+
app.get("/api/governance/gates/:slug/aliases", async (req, reply) => proxyControlPlane(req, reply, `${gateRegistryPath}/${encodeURIComponent(req.params.slug)}/aliases`, void 0, "GET")), app.get("/api/governance/gates/:slug/audit", async (req, reply) => proxyControlPlane(req, reply, `${gateRegistryPath}/${encodeURIComponent(req.params.slug)}/audit?limit=${encodeURIComponent(req.query.limit ?? "50")}`, void 0, "GET")), app.get("/api/governance/gates/:slug/resolve", async (req, reply) => proxyControlPlane(req, reply, `${gateRegistryPath}/${encodeURIComponent(req.params.slug)}/resolve`, void 0, "GET")), app.get("/api/governance/gate-signing-keys/:keyId", async (req, reply) => proxyControlPlane(req, reply, `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/gate-signing-keys/${encodeURIComponent(req.params.keyId)}`, void 0, "GET"));
|
|
12018
12074
|
let learningRegistryPath = `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/learning`, learningMutationAuthorized = (req, reply) => typeof req.headers.authorization == "string" && req.headers.authorization.startsWith("Bearer ") ? !0 : (reply.code(401).send({ error: "Sign in to your Prooflane account first." }), !1);
|
|
12019
|
-
|
|
12075
|
+
app.get("/api/governance/learning/runs/:runId/findings", async (req, reply) => proxyControlPlane(req, reply, `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/runs/${encodeURIComponent(req.params.runId)}/learning-findings`, void 0, "GET")), app.get("/api/governance/learning/runs/:runId/disposition", async (req, reply) => proxyControlPlane(req, reply, `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/runs/${encodeURIComponent(req.params.runId)}/learning-disposition`, void 0, "GET")), app.put("/api/governance/learning/runs/:runId/disposition", async (req, reply) => {
|
|
12020
12076
|
if (learningMutationAuthorized(req, reply))
|
|
12021
12077
|
return !isApproveLearningDispositionRequest(req.body) || findSensitiveMaterial(req.body) ? reply.code(400).send({ error: "Expected a compact prooflane-learning-disposition-request-v1 envelope." }) : proxyControlPlane(req, reply, `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/runs/${encodeURIComponent(req.params.runId)}/learning-disposition`, req.body, "PUT");
|
|
12022
|
-
}),
|
|
12078
|
+
}), app.get("/api/governance/learning/generations", async (req, reply) => proxyControlPlane(req, reply, `${learningRegistryPath}/generations?limit=${encodeURIComponent(req.query.limit ?? "50")}`, void 0, "GET")), app.post("/api/governance/learning/generations", async (req, reply) => {
|
|
12023
12079
|
if (!learningMutationAuthorized(req, reply))
|
|
12024
12080
|
return;
|
|
12025
12081
|
if (!isCreateLearningGenerationRequest(req.body))
|
|
@@ -12033,15 +12089,15 @@ ${item.artifact.localFingerprint}`));
|
|
|
12033
12089
|
let status = error instanceof ControlPlaneRequestError ? error.status : 502;
|
|
12034
12090
|
return reply.code(status).send({ error: error instanceof Error ? error.message : "Governed learning generation failed." });
|
|
12035
12091
|
}
|
|
12036
|
-
}),
|
|
12092
|
+
}), app.get("/api/governance/learning/tests", async (req, reply) => proxyControlPlane(req, reply, `${learningRegistryPath}/tests?limit=${encodeURIComponent(req.query.limit ?? "100")}`, void 0, "GET"));
|
|
12037
12093
|
for (let transition of ["review", "approve"])
|
|
12038
|
-
|
|
12094
|
+
app.post(`/api/governance/learning/tests/:testId/${transition}`, async (req, reply) => {
|
|
12039
12095
|
if (learningMutationAuthorized(req, reply))
|
|
12040
12096
|
return !req.body || typeof req.body != "object" || Array.isArray(req.body) || Object.keys(req.body).length !== 0 ? reply.code(400).send({ error: "Learning lifecycle transitions accept an empty body only." }) : proxyControlPlane(req, reply, `${learningRegistryPath}/tests/${encodeURIComponent(req.params.testId)}/${transition}`, {});
|
|
12041
12097
|
});
|
|
12042
|
-
|
|
12098
|
+
app.get("/api/governance/learning/audit", async (req, reply) => proxyControlPlane(req, reply, `${learningRegistryPath}/audit?limit=${encodeURIComponent(req.query.limit ?? "100")}`, void 0, "GET"));
|
|
12043
12099
|
let runnerRegistryPath = `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/runners`;
|
|
12044
|
-
|
|
12100
|
+
app.get("/api/governance/runners", async (req, reply) => proxyControlPlane(req, reply, runnerRegistryPath, void 0, "GET")), app.post("/api/governance/runners/:runnerId/revoke", async (req, reply) => proxyControlPlane(req, reply, `${runnerRegistryPath}/${encodeURIComponent(req.params.runnerId)}/revoke`, {})), app.post("/api/governance/gates/:slug/remote-runs", async (req, reply) => proxyControlPlane(req, reply, `${gateRegistryPath}/${encodeURIComponent(req.params.slug)}/remote-runs`, req.body ?? {})), app.get("/api/governance/runner-jobs/:jobId", async (req, reply) => proxyControlPlane(req, reply, `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/runner-jobs/${encodeURIComponent(req.params.jobId)}`, void 0, "GET")), app.post("/api/governance/runner-jobs/:jobId/cancel", async (req, reply) => proxyControlPlane(req, reply, `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/runner-jobs/${encodeURIComponent(req.params.jobId)}/cancel`, {})), app.post("/api/governance/gates/:slug/runs", async (req, reply) => {
|
|
12045
12101
|
let projectPath = `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}`, run, startedAt = (/* @__PURE__ */ new Date()).toISOString(), startedMs = Date.now();
|
|
12046
12102
|
try {
|
|
12047
12103
|
let resolutionPayload = await requestControlPlaneJson(req, `${gateRegistryPath}/${encodeURIComponent(req.params.slug)}/resolve`), object = (value) => !!value && typeof value == "object" && !Array.isArray(value);
|
|
@@ -12151,7 +12207,7 @@ ${PROOFLANE_PROJECT}`, "utf8").digest("hex").slice(0, 24)}`, idempotencyKey = `l
|
|
|
12151
12207
|
}
|
|
12152
12208
|
});
|
|
12153
12209
|
let policyRegistryPath = `/v1/projects/${encodeURIComponent(PROOFLANE_PROJECT)}/policies`;
|
|
12154
|
-
|
|
12210
|
+
app.get("/api/policies", async (req, reply) => proxyControlPlane(req, reply, policyRegistryPath, void 0, "GET")), app.post("/api/policies", async (req, reply) => proxyControlPlane(req, reply, policyRegistryPath, req.body ?? {})), app.get("/api/policies/:policyId", async (req, reply) => proxyControlPlane(req, reply, `${policyRegistryPath}/${encodeURIComponent(req.params.policyId)}`, void 0, "GET")), app.post("/api/policies/:policyId/versions", async (req, reply) => proxyControlPlane(req, reply, `${policyRegistryPath}/${encodeURIComponent(req.params.policyId)}/versions`, req.body ?? {})), app.put("/api/policies/:policyId/versions/:version", async (req, reply) => proxyControlPlane(req, reply, `${policyRegistryPath}/${encodeURIComponent(req.params.policyId)}/versions/${encodeURIComponent(req.params.version)}`, req.body ?? {}, "PUT")), app.post("/api/policies/:policyId/versions/:version/review", async (req, reply) => proxyControlPlane(req, reply, `${policyRegistryPath}/${encodeURIComponent(req.params.policyId)}/versions/${encodeURIComponent(req.params.version)}/review`, {})), app.post("/api/policies/:policyId/versions/:version/approve", async (req, reply) => proxyControlPlane(req, reply, `${policyRegistryPath}/${encodeURIComponent(req.params.policyId)}/versions/${encodeURIComponent(req.params.version)}/approve`, {})), app.post("/api/policies/:policyId/versions/:version/deprecate", async (req, reply) => proxyControlPlane(req, reply, `${policyRegistryPath}/${encodeURIComponent(req.params.policyId)}/versions/${encodeURIComponent(req.params.version)}/deprecate`, {})), app.post("/api/assure/target-snapshot", async (req, reply) => proxyControlPlane(req, reply, "/v1/assure/target-snapshot", req.body ?? {})), app.get("/api/assure/target-snapshot", async (req, reply) => proxyControlPlane(req, reply, "/v1/assure/target-snapshot", void 0, "GET")), app.post("/api/workspace/backup-preference", async (req, reply) => proxyControlPlane(req, reply, "/v1/workspace/backup-preference", req.body ?? {})), app.post("/api/workspace/backup", async (req, reply) => {
|
|
12155
12211
|
let snapshots = svc.listSnapshots().map(({ id, createdAt, kind, origin, snapshot }) => ({ id, createdAt, kind, origin, snapshot })), backup = {
|
|
12156
12212
|
version: "workspace-backup-v1",
|
|
12157
12213
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -12167,7 +12223,7 @@ ${PROOFLANE_PROJECT}`, "utf8").digest("hex").slice(0, 24)}`, idempotencyKey = `l
|
|
|
12167
12223
|
let capturedAt = payload && typeof payload == "object" && typeof payload.capturedAt == "string" ? payload.capturedAt : backup.capturedAt;
|
|
12168
12224
|
svc.markCloudWorkspaceBackedUp(capturedAt);
|
|
12169
12225
|
});
|
|
12170
|
-
}),
|
|
12226
|
+
}), app.get("/api/workspace/backup/latest", async (req, reply) => proxyControlPlane(req, reply, `/v1/workspace/backup/latest${req.query?.restorable ? "?restorable=1" : ""}`, void 0, "GET")), app.get("/api/workspace/backup/versions", async (req, reply) => proxyControlPlane(req, reply, `/v1/workspace/backup/versions${req.query?.limit ? `?limit=${encodeURIComponent(req.query.limit)}` : ""}`, void 0, "GET")), app.post("/api/workspace/restore", async (req, reply) => {
|
|
12171
12227
|
if (!CONTROL_PLANE_URL)
|
|
12172
12228
|
return reply.code(503).send({ error: "Prooflane Cloud Workspace is not configured for this Inspector." });
|
|
12173
12229
|
let authorization = req.headers.authorization;
|
|
@@ -12186,20 +12242,20 @@ ${PROOFLANE_PROJECT}`, "utf8").digest("hex").slice(0, 24)}`, idempotencyKey = `l
|
|
|
12186
12242
|
return reply.code(404).send({ ok: !1, error: "No cloud backup was found to restore." });
|
|
12187
12243
|
let restored = svc.restoreFromBackup(backup.value), capturedAt = backup.value.capturedAt ?? backup.createdAt;
|
|
12188
12244
|
return svc.markCloudWorkspaceBackedUp(capturedAt), reply.send({ ok: !0, restored, capturedAt, backupId: backup.id });
|
|
12189
|
-
}),
|
|
12245
|
+
}), app.post("/api/workspace/local/lifecycle", async (req, reply) => {
|
|
12190
12246
|
if (req.headers["x-kawach-local-lifecycle"] !== localLifecycleKey)
|
|
12191
12247
|
return reply.code(403).send({ error: "Invalid local workspace lifecycle request." });
|
|
12192
12248
|
let subject = typeof req.body?.subject == "string" ? req.body.subject.slice(0, 320) : void 0;
|
|
12193
12249
|
await mcpManager.clearWorkspace(PROOFLANE_PROJECT);
|
|
12194
12250
|
let result = await svc.applyLocalWorkspaceLifecycle(subject);
|
|
12195
12251
|
return svc = new InspectorService(store), activeMcpConnectionId = null, activeMcpAuthMode = "unknown", oauthFlows.clear(), { ok: !0, result, reason: req.body?.reason ?? "login" };
|
|
12196
|
-
}),
|
|
12252
|
+
}), app.post("/api/intelligence/leases", async (req, reply) => proxyControlPlane(req, reply, "/v1/intelligence/leases", req.body ?? {})), app.post("/api/intelligence/evidence", async (req, reply) => proxyControlPlane(req, reply, "/v1/intelligence/evidence", req.body ?? {})), app.post("/api/intelligence/evaluations", async (req, reply) => proxyControlPlane(req, reply, "/v1/intelligence/evaluations", req.body ?? {})), app.get("/api/security/catalog", async (req, reply) => proxyControlPlane(req, reply, "/v1/security/catalog", void 0, "GET"));
|
|
12197
12253
|
for (let pillar of ["security", "rag"])
|
|
12198
|
-
|
|
12254
|
+
app.post(`/api/${pillar}/scans`, async (req, reply) => proxyControlPlane(req, reply, "/v1/scans", { pillar, ...req.body ?? {} })), app.get(`/api/${pillar}/scans/:id`, async (req, reply) => proxyControlPlane(req, reply, `/v1/scans/${encodeURIComponent(req.params.id)}`, void 0, "GET")), app.get(`/api/${pillar}/scans/:id/events`, async (req, reply) => {
|
|
12199
12255
|
let after = Math.max(0, Math.trunc(Number(req.query.after)) || 0);
|
|
12200
12256
|
return proxyControlPlane(req, reply, `/v1/scans/${encodeURIComponent(req.params.id)}/events?after=${after}`, void 0, "GET");
|
|
12201
|
-
}),
|
|
12202
|
-
|
|
12257
|
+
}), app.post(`/api/${pillar}/scans/:id/next-task`, async (req, reply) => proxyControlPlane(req, reply, `/v1/scans/${encodeURIComponent(req.params.id)}/next-task`, {})), app.post(`/api/${pillar}/scans/:id/evidence`, async (req, reply) => proxyControlPlane(req, reply, `/v1/scans/${encodeURIComponent(req.params.id)}/evidence`, req.body ?? {})), app.post(`/api/${pillar}/scans/:id/stop`, async (req, reply) => proxyControlPlane(req, reply, `/v1/scans/${encodeURIComponent(req.params.id)}/cancel`, {})), app.post(`/api/${pillar}/scans/:id/retry`, async (req, reply) => proxyControlPlane(req, reply, `/v1/scans/${encodeURIComponent(req.params.id)}/retry`, {}));
|
|
12258
|
+
app.post("/api/benchmark/evidence/generate", async (req, reply) => {
|
|
12203
12259
|
let generator = req.body?.generator, count = Math.min(20, Math.max(1, Math.trunc(req.body?.count ?? 4))), brief = req.body?.brief?.trim();
|
|
12204
12260
|
if (!generator?.model?.trim() || !generator.apiKey?.trim())
|
|
12205
12261
|
return reply.code(400).send({ error: "Configure the evidence-generator model and API key." });
|
|
@@ -12285,7 +12341,7 @@ ${existing.length ? existing.join(`
|
|
|
12285
12341
|
(run?.status === "CANCELLED" || run?.status === "FAILED") && store.releaseBenchmarkEntitlement(workspaceId, entitlement.id, entitlement.reservationKey);
|
|
12286
12342
|
}
|
|
12287
12343
|
};
|
|
12288
|
-
|
|
12344
|
+
app.get("/api/benchmark/entitlements", async () => {
|
|
12289
12345
|
let workspaceId = benchmarkWorkspaceId();
|
|
12290
12346
|
ensureFreeFastBenchmarkEntitlement(), reconcileTerminalBenchmarkReservations(workspaceId);
|
|
12291
12347
|
let entitlements = store.listBenchmarkEntitlements(workspaceId);
|
|
@@ -12297,7 +12353,7 @@ ${existing.length ? existing.join(`
|
|
|
12297
12353
|
pricingPolicy: benchmarkPricingPolicy,
|
|
12298
12354
|
purchase: { verification: "SERVER_REQUIRED", clientPaidFlagsAccepted: !1 }
|
|
12299
12355
|
};
|
|
12300
|
-
}),
|
|
12356
|
+
}), app.post("/api/benchmark/purchases/verify", async (req, reply) => {
|
|
12301
12357
|
let receipt = req.body?.receipt?.trim(), idempotencyKey = req.body?.idempotencyKey?.trim();
|
|
12302
12358
|
return !receipt || receipt.length > 65536 || !idempotencyKey || idempotencyKey.length > 120 ? reply.code(400).send({ error: "A bounded purchase receipt and idempotency key are required." }) : proxyControlPlane(req, reply, "/v1/benchmark/purchases/verify", { ...req.body, receipt, idempotencyKey, projectId: PROOFLANE_PROJECT }, "POST", (payload) => {
|
|
12303
12359
|
let response = payload, entitlement = response?.entitlement;
|
|
@@ -12305,7 +12361,7 @@ ${existing.length ? existing.join(`
|
|
|
12305
12361
|
throw new Error("Prooflane returned an invalid Benchmark purchase entitlement.");
|
|
12306
12362
|
store.saveBenchmarkEntitlement(entitlement);
|
|
12307
12363
|
});
|
|
12308
|
-
}),
|
|
12364
|
+
}), app.post("/api/benchmark/plans", async (req, reply) => {
|
|
12309
12365
|
if (intelligenceMode === "hosted")
|
|
12310
12366
|
return proxyControlPlane(req, reply, "/v1/benchmark/plans", req.body ?? {});
|
|
12311
12367
|
let body = req.body ?? {}, objective = body.objective?.trim(), targetType = body.targetType, mode = body.mode, targets = body.targets;
|
|
@@ -12402,7 +12458,7 @@ ${existing.length ? existing.join(`
|
|
|
12402
12458
|
let message2 = error instanceof Error ? error.message : String(error), status = error instanceof ControlPlaneRequestError ? error.status : /allowance|locked|not enabled/i.test(message2) ? 403 : 502;
|
|
12403
12459
|
return reply.code(status).send({ error: status < 500 ? message2 : publicIntelligenceGenerationError(message2) });
|
|
12404
12460
|
}
|
|
12405
|
-
}),
|
|
12461
|
+
}), app.post("/api/benchmark/run", async (req, reply) => {
|
|
12406
12462
|
let suppliedCandidates = req.body?.candidates, workflowPlan = req.body?.plan, suppliedCases = req.body?.cases;
|
|
12407
12463
|
if (!Array.isArray(suppliedCandidates) || suppliedCandidates.length < 2 || suppliedCandidates.length > 8)
|
|
12408
12464
|
return reply.code(400).send({ error: "Choose between 2 and 8 benchmark candidates." });
|
|
@@ -12614,16 +12670,16 @@ ${existing.length ? existing.join(`
|
|
|
12614
12670
|
} finally {
|
|
12615
12671
|
keys.clear(), prices.clear();
|
|
12616
12672
|
}
|
|
12617
|
-
}),
|
|
12673
|
+
}), app.get("/api/benchmark/history", async (req) => {
|
|
12618
12674
|
let limit = Math.max(1, Math.min(100, Number(req.query.limit) || 25));
|
|
12619
12675
|
return { runs: store.listBenchmarkRuns(benchmarkWorkspaceId(), limit).map((record6) => ({ runId: record6.runId, status: record6.status, planId: record6.plan.planId, planVersion: record6.plan.version, objective: record6.plan.objective, mode: record6.plan.mode, targetType: record6.plan.targetType, summary: record6.summary, createdAt: record6.createdAt, updatedAt: record6.updatedAt })) };
|
|
12620
|
-
}),
|
|
12676
|
+
}), app.get("/api/benchmark/runs/:id", async (req, reply) => {
|
|
12621
12677
|
let record6 = store.getBenchmarkRun(req.params.id);
|
|
12622
12678
|
return !record6 || record6.workspaceId !== benchmarkWorkspaceId() ? reply.code(404).send({ error: "Benchmark Run not found." }) : { run: record6 };
|
|
12623
|
-
}),
|
|
12679
|
+
}), app.get("/api/benchmark/runs/:id/report.html", async (req, reply) => {
|
|
12624
12680
|
let record6 = store.getBenchmarkRun(req.params.id);
|
|
12625
12681
|
return !record6 || record6.workspaceId !== benchmarkWorkspaceId() || !record6.reportHtml ? reply.code(404).send({ error: "Benchmark HTML report not found." }) : (reply.header("Content-Disposition", `attachment; filename="benchmark-report-${record6.runId}.html"`), reply.type("text/html; charset=utf-8"), record6.reportHtml);
|
|
12626
|
-
}),
|
|
12682
|
+
}), app.post("/api/intelligence/execute", async (req, reply) => {
|
|
12627
12683
|
if (!req.body?.lease)
|
|
12628
12684
|
return reply.code(400).send({ ok: !1, error: "A hosted Intelligence lease is required." });
|
|
12629
12685
|
let claims;
|
|
@@ -12894,7 +12950,7 @@ ${answer}`
|
|
|
12894
12950
|
let tagsFile = () => join4(process.cwd(), DEFAULT_TAGS_FILE), readTagsConfig = (file) => parseTagsConfig(existsSync2(file) ? readFileSync3(file, "utf8") : ""), writeTagsConfig = (file, config) => {
|
|
12895
12951
|
mkdirSync2(dirname4(file), { recursive: !0 }), writeFileSync(file, serializeTagsConfig(config));
|
|
12896
12952
|
};
|
|
12897
|
-
|
|
12953
|
+
app.get("/api/tags", async () => ({ ok: !0, path: DEFAULT_TAGS_FILE, config: readTagsConfig(tagsFile()) })), app.post("/api/tags", async (req, reply) => {
|
|
12898
12954
|
let plan = await resolvePlan(req);
|
|
12899
12955
|
if (!plan.gates)
|
|
12900
12956
|
return reply.code(402).send({ ok: !1, upgrade: !0, feature: "gates", tier: plan.tier, error: "CI gates are part of the Scale plan. Upgrade to capture tagged tests for CI." });
|
|
@@ -12903,7 +12959,7 @@ ${answer}`
|
|
|
12903
12959
|
return reply.code(400).send({ ok: !1, error: "A valid tagged test (id, pillar, tags[]) is required." });
|
|
12904
12960
|
let file = tagsFile(), next = upsertTest(readTagsConfig(file), { ...test, createdAt: test.createdAt || (/* @__PURE__ */ new Date()).toISOString() });
|
|
12905
12961
|
return writeTagsConfig(file, next), { ok: !0, path: DEFAULT_TAGS_FILE, config: next };
|
|
12906
|
-
}),
|
|
12962
|
+
}), app.delete("/api/tags/:id", async (req, reply) => {
|
|
12907
12963
|
let plan = await resolvePlan(req);
|
|
12908
12964
|
if (!plan.gates)
|
|
12909
12965
|
return reply.code(402).send({ ok: !1, upgrade: !0, feature: "gates", tier: plan.tier, error: "CI gates are part of the Scale plan." });
|
|
@@ -12927,7 +12983,7 @@ ${answer}`
|
|
|
12927
12983
|
mkdirSync2(dirname4(file), { recursive: !0 }), writeFileSync(file, JSON.stringify({ version: "prooflane-gates-v1", gates }, null, 2) + `
|
|
12928
12984
|
`, "utf8");
|
|
12929
12985
|
};
|
|
12930
|
-
|
|
12986
|
+
app.get("/api/gates", async () => ({ ok: !0, path: ".prooflane/gates.json", gates: readLegacyRegressionGroups() })), app.put("/api/gates/:id", async (req, reply) => {
|
|
12931
12987
|
let plan = await resolvePlan(req);
|
|
12932
12988
|
if (!plan.gates)
|
|
12933
12989
|
return reply.code(402).send({ ok: !1, upgrade: !0, feature: "gates", tier: plan.tier, error: "Legacy local regression groups are part of the Scale plan." });
|
|
@@ -12935,7 +12991,7 @@ ${answer}`
|
|
|
12935
12991
|
return reply.code(400).send({ ok: !1, error: "A valid legacy local regression group with at least one tag is required." });
|
|
12936
12992
|
let current = readLegacyRegressionGroups(), existing = current.find((gate2) => gate2.id === req.params.id), gate = { ...req.body.gate, createdAt: existing?.createdAt ?? req.body.gate.createdAt }, next = [...current.filter((item) => item.id !== gate.id), gate].sort((a, b) => a.name.localeCompare(b.name));
|
|
12937
12993
|
return writeLegacyRegressionGroups(next), { ok: !0, path: ".prooflane/gates.json", gate, gates: next };
|
|
12938
|
-
}),
|
|
12994
|
+
}), app.delete("/api/gates/:id", async (req, reply) => {
|
|
12939
12995
|
let plan = await resolvePlan(req);
|
|
12940
12996
|
if (!plan.gates)
|
|
12941
12997
|
return reply.code(402).send({ ok: !1, upgrade: !0, feature: "gates", tier: plan.tier, error: "Legacy local regression groups are part of the Scale plan." });
|
|
@@ -12967,7 +13023,7 @@ ${answer}`
|
|
|
12967
13023
|
throw new Error("Connected MCP runtime was not registered.");
|
|
12968
13024
|
return runtime.setToolLimit?.(plan.tools), svc = runtime, activeMcpConnectionId = result.connection.id, activeMcpAuthMode = authModeFor(connection), result;
|
|
12969
13025
|
}
|
|
12970
|
-
|
|
13026
|
+
app.post("/api/connect", async (req, reply) => {
|
|
12971
13027
|
try {
|
|
12972
13028
|
let disconnected = mcpManager.listConnections(PROOFLANE_PROJECT).filter((connection) => connection.status === "DISCONNECTED");
|
|
12973
13029
|
if (!activeMcpConnectionId && disconnected.length === 1) {
|
|
@@ -12979,21 +13035,21 @@ ${answer}`
|
|
|
12979
13035
|
} catch (error) {
|
|
12980
13036
|
return sendMcpError(reply, error);
|
|
12981
13037
|
}
|
|
12982
|
-
}),
|
|
13038
|
+
}), app.get("/api/mcp/connections", async (req) => {
|
|
12983
13039
|
let entitlement = mcpEntitlementForPlan(await resolvePlan(req)), connections = mcpManager.reconcileEntitlement(PROOFLANE_PROJECT, entitlement);
|
|
12984
13040
|
return {
|
|
12985
13041
|
connections,
|
|
12986
13042
|
entitlement,
|
|
12987
13043
|
usage: { current: connections.length, allowed: entitlement.maxConnections }
|
|
12988
13044
|
};
|
|
12989
|
-
}),
|
|
13045
|
+
}), app.post("/api/mcp/connections", async (req, reply) => {
|
|
12990
13046
|
try {
|
|
12991
13047
|
let result = await connectMcp(req, req.body.connection, req.body.displayName ?? req.body.name);
|
|
12992
13048
|
return reply.code(201).send({ ok: !0, ...result });
|
|
12993
13049
|
} catch (error) {
|
|
12994
13050
|
return sendMcpError(reply, error);
|
|
12995
13051
|
}
|
|
12996
|
-
}),
|
|
13052
|
+
}), app.post("/api/mcp/connections/:id/reconnect", async (req, reply) => {
|
|
12997
13053
|
try {
|
|
12998
13054
|
let plan = await resolvePlan(req);
|
|
12999
13055
|
mcpManager.reconcileEntitlement(PROOFLANE_PROJECT, mcpEntitlementForPlan(plan));
|
|
@@ -13005,7 +13061,7 @@ ${answer}`
|
|
|
13005
13061
|
} catch (error) {
|
|
13006
13062
|
return sendMcpError(reply, error);
|
|
13007
13063
|
}
|
|
13008
|
-
}),
|
|
13064
|
+
}), app.post("/api/mcp/connections/:id/activate", async (req, reply) => {
|
|
13009
13065
|
try {
|
|
13010
13066
|
let plan = await resolvePlan(req);
|
|
13011
13067
|
mcpManager.reconcileEntitlement(PROOFLANE_PROJECT, mcpEntitlementForPlan(plan));
|
|
@@ -13021,32 +13077,32 @@ ${answer}`
|
|
|
13021
13077
|
} catch (error) {
|
|
13022
13078
|
return sendMcpError(reply, error);
|
|
13023
13079
|
}
|
|
13024
|
-
}),
|
|
13080
|
+
}), app.post("/api/mcp/connections/:id/refresh", async (req, reply) => {
|
|
13025
13081
|
try {
|
|
13026
13082
|
let plan = await resolvePlan(req);
|
|
13027
13083
|
return mcpManager.reconcileEntitlement(PROOFLANE_PROJECT, mcpEntitlementForPlan(plan)), { ok: !0, tools: await mcpManager.refreshTools(PROOFLANE_PROJECT, req.params.id, await requestActor(req)), connection: mcpManager.getConnection(PROOFLANE_PROJECT, req.params.id) };
|
|
13028
13084
|
} catch (error) {
|
|
13029
13085
|
return sendMcpError(reply, error);
|
|
13030
13086
|
}
|
|
13031
|
-
}),
|
|
13087
|
+
}), app.post("/api/mcp/connections/:id/disconnect", async (req, reply) => {
|
|
13032
13088
|
try {
|
|
13033
13089
|
return await mcpManager.disconnectConnection(PROOFLANE_PROJECT, req.params.id, await requestActor(req)), activeMcpConnectionId === req.params.id && (activeMcpConnectionId = null, activeMcpAuthMode = "unknown", svc = new InspectorService(store)), { ok: !0 };
|
|
13034
13090
|
} catch (error) {
|
|
13035
13091
|
return sendMcpError(reply, error);
|
|
13036
13092
|
}
|
|
13037
|
-
}),
|
|
13093
|
+
}), app.delete("/api/mcp/connections/:id", async (req, reply) => {
|
|
13038
13094
|
try {
|
|
13039
13095
|
return await mcpManager.removeConnection(PROOFLANE_PROJECT, req.params.id, await requestActor(req)), activeMcpConnectionId === req.params.id && (activeMcpConnectionId = null, activeMcpAuthMode = "unknown", svc = new InspectorService(store)), { ok: !0 };
|
|
13040
13096
|
} catch (error) {
|
|
13041
13097
|
return sendMcpError(reply, error);
|
|
13042
13098
|
}
|
|
13043
|
-
}),
|
|
13099
|
+
}), app.get("/api/mcp/tools", async (req) => {
|
|
13044
13100
|
let entitlement = mcpEntitlementForPlan(await resolvePlan(req));
|
|
13045
13101
|
return mcpManager.reconcileEntitlement(PROOFLANE_PROJECT, entitlement), { tools: mcpManager.listTools(PROOFLANE_PROJECT, req.query.connectionId) };
|
|
13046
|
-
}),
|
|
13102
|
+
}), app.get("/api/features/:featureType/:resourceId/mcp-binding", async (req, reply) => {
|
|
13047
13103
|
let featureType = parseFeatureType(req.params.featureType);
|
|
13048
13104
|
return !featureType || !validResourceId(req.params.resourceId) ? reply.code(400).send({ error: "A valid MCP feature type and resource ID are required." }) : { binding: mcpManager.getBinding(PROOFLANE_PROJECT, featureType, req.params.resourceId) };
|
|
13049
|
-
}),
|
|
13105
|
+
}), app.put("/api/features/:featureType/:resourceId/mcp-binding", async (req, reply) => {
|
|
13050
13106
|
let featureType = parseFeatureType(req.params.featureType);
|
|
13051
13107
|
if (!featureType || !validResourceId(req.params.resourceId))
|
|
13052
13108
|
return reply.code(400).send({ error: "A valid MCP feature type and resource ID are required." });
|
|
@@ -13072,10 +13128,10 @@ ${answer}`
|
|
|
13072
13128
|
} catch (error) {
|
|
13073
13129
|
return sendMcpError(reply, error);
|
|
13074
13130
|
}
|
|
13075
|
-
}),
|
|
13131
|
+
}), app.delete("/api/features/:featureType/:resourceId/mcp-binding", async (req, reply) => {
|
|
13076
13132
|
let featureType = parseFeatureType(req.params.featureType);
|
|
13077
13133
|
return !featureType || !validResourceId(req.params.resourceId) ? reply.code(400).send({ error: "A valid MCP feature type and resource ID are required." }) : { ok: mcpManager.deleteBinding(PROOFLANE_PROJECT, featureType, req.params.resourceId, await requestActor(req)) };
|
|
13078
|
-
}),
|
|
13134
|
+
}), app.post("/api/features/:featureType/:resourceId/mcp-runs", async (req, reply) => {
|
|
13079
13135
|
let featureType = parseFeatureType(req.params.featureType);
|
|
13080
13136
|
if (!featureType || !validResourceId(req.params.resourceId))
|
|
13081
13137
|
return reply.code(400).send({ error: "A valid MCP feature type and resource ID are required." });
|
|
@@ -13087,10 +13143,10 @@ ${answer}`
|
|
|
13087
13143
|
} catch (error) {
|
|
13088
13144
|
return sendMcpError(reply, error);
|
|
13089
13145
|
}
|
|
13090
|
-
}),
|
|
13146
|
+
}), app.get("/api/mcp/runs/:runId", async (req, reply) => {
|
|
13091
13147
|
let snapshot = mcpManager.getRunSnapshot(PROOFLANE_PROJECT, req.params.runId);
|
|
13092
13148
|
return snapshot ? { snapshot } : reply.code(404).send({ error: "MCP run snapshot was not found." });
|
|
13093
|
-
}),
|
|
13149
|
+
}), app.post("/api/mcp/runs/:runId/invoke", async (req, reply) => {
|
|
13094
13150
|
let controller = new AbortController(), cancel = () => controller.abort();
|
|
13095
13151
|
req.raw.once("aborted", cancel);
|
|
13096
13152
|
try {
|
|
@@ -13105,12 +13161,12 @@ ${answer}`
|
|
|
13105
13161
|
} finally {
|
|
13106
13162
|
req.raw.off("aborted", cancel);
|
|
13107
13163
|
}
|
|
13108
|
-
}),
|
|
13164
|
+
}), app.get("/api/mcp/audit", async (req) => ({
|
|
13109
13165
|
events: mcpManager.listAuditEvents(PROOFLANE_PROJECT, Number(req.query.limit ?? 100))
|
|
13110
|
-
})),
|
|
13166
|
+
})), app.get("/api/capabilities", async () => {
|
|
13111
13167
|
let capabilities = svc.getCapabilities(), searchable = capabilities ? JSON.stringify(capabilities).toLowerCase() : "", tools = capabilities?.tools ?? [], resources = capabilities?.resources ?? [], rag = tools.some((tool) => /rag|retriev|search|knowledge|vector|semantic/.test(`${tool.name} ${tool.description ?? ""}`.toLowerCase())) || resources.some((resource) => /knowledge|document|vector|search|rag/.test(`${resource.uri} ${resource.name ?? ""} ${resource.description ?? ""}`.toLowerCase())), notableTools = tools.filter((tool) => /create|update|write|delete|remove|execute|admin|push|merge|dispatch|upload/.test(`${tool.name} ${tool.description ?? ""}`.toLowerCase())).map((tool) => tool.name), sensitivitySignals = [...new Set((searchable.match(/email|phone|address|ssn|personal|secret|token|password|api[_ -]?key|user[_ -]?id|account[_ -]?id/g) ?? []).slice(0, 8))];
|
|
13112
13168
|
return { connected: svc.isConnected(), connectionId: activeMcpConnectionId, capabilities, discovery: { toolCount: tools.length, auth: { mode: activeMcpAuthMode, status: activeMcpAuthMode === "unknown" ? "UNKNOWN" : "OBSERVED" }, rag: { present: rag, status: rag ? "OBSERVED" : "NOT_OBSERVED" }, notable: { tools: notableTools, status: notableTools.length ? "CLASSIFIED" : "NONE_CLASSIFIED" }, sensitivity: { signals: sensitivitySignals, status: sensitivitySignals.length ? "INFERRED" : "NOT_INFERRED" } } };
|
|
13113
|
-
}),
|
|
13169
|
+
}), app.post("/api/connect/oauth/start", async (req, reply) => {
|
|
13114
13170
|
let { url, name } = req.body, plan = await resolvePlan(req), entitlement = mcpEntitlementForPlan(plan), actor = await requestActor(req);
|
|
13115
13171
|
try {
|
|
13116
13172
|
mcpManager.assertCanAddMcpConnection(PROOFLANE_PROJECT, entitlement);
|
|
@@ -13137,10 +13193,10 @@ ${answer}`
|
|
|
13137
13193
|
authUrl: flow.authUrl,
|
|
13138
13194
|
redirectUri: flow.redirectUri
|
|
13139
13195
|
};
|
|
13140
|
-
}),
|
|
13196
|
+
}), app.get("/api/connect/oauth/status", async (req) => {
|
|
13141
13197
|
let rec = oauthFlows.get(req.query.flowId);
|
|
13142
13198
|
return rec || { status: "error", error: "Unknown flow." };
|
|
13143
|
-
}),
|
|
13199
|
+
}), app.post("/api/disconnect", async () => (activeMcpConnectionId ? await mcpManager.disconnectConnection(PROOFLANE_PROJECT, activeMcpConnectionId, "local-user") : await svc.disconnect(), activeMcpConnectionId = null, activeMcpAuthMode = "unknown", svc = new InspectorService(store), { ok: !0 })), app.post("/api/tools/:name/call", async (req) => svc.callTool(req.params.name, req.body?.input ?? {})), app.post("/api/tools/:name/snapshot", async (req) => svc.snapshotTool(req.params.name, req.body?.input ?? {})), app.post("/api/security/verify-capability", async (req, reply) => {
|
|
13144
13200
|
let toolName = typeof req.body?.toolName == "string" ? req.body.toolName : "";
|
|
13145
13201
|
if (!toolName)
|
|
13146
13202
|
return reply.code(400).send({ error: "toolName is required." });
|
|
@@ -13193,19 +13249,19 @@ ${answer}`
|
|
|
13193
13249
|
} catch (error) {
|
|
13194
13250
|
return error instanceof ExecutionGateError ? reply.code(400).send({ error: error.message }) : reply.code(500).send({ error: error instanceof Error ? error.message : "Capability verification failed." });
|
|
13195
13251
|
}
|
|
13196
|
-
}),
|
|
13252
|
+
}), app.post("/api/diff/snapshot", async (req) => svc.diffAgainst(req.body.snapshot)), app.post("/api/diff/values", async (req) => diffTool(req.body.tool, req.body.expected, req.body.actual)), app.post("/api/resources/read", async (req) => svc.readResource(req.body.uri)), app.post("/api/prompts/:name/get", async (req) => svc.getPrompt(req.params.name, req.body?.args)), app.post("/api/drift", async (req) => {
|
|
13197
13253
|
let current = svc.getCapabilities();
|
|
13198
13254
|
if (!current)
|
|
13199
13255
|
throw new Error("Not connected.");
|
|
13200
13256
|
return detectCapabilityDrift(req.body.baseline, current);
|
|
13201
|
-
}),
|
|
13257
|
+
}), app.post("/api/record/start", async (req) => (svc.startRecording(req.body?.label), { ok: !0, recording: !0 })), app.get("/api/record/status", async () => ({ recording: svc.isRecording() })), app.post("/api/record/stop", async () => svc.stopRecording()), app.get("/api/sessions", async () => svc.listSessions()), app.get("/api/sessions/:id", async (req) => {
|
|
13202
13258
|
let session = svc.getSession(req.params.id);
|
|
13203
13259
|
if (!session)
|
|
13204
13260
|
throw new Error(`Session not found: ${req.params.id}`);
|
|
13205
13261
|
return session;
|
|
13206
|
-
}),
|
|
13262
|
+
}), app.post("/api/sessions/:id/golden", async (req) => (svc.markGolden(req.params.id, req.body?.golden ?? !0), { ok: !0 })), app.post("/api/sessions/:id/automate", async (req) => svc.automate(req.params.id, req.body.format)), app.post("/api/sessions/:id/fault", async (req) => svc.injectFault(req.params.id, req.body.step, req.body.type));
|
|
13207
13263
|
let HTTP_TIMEOUT_MS = 3e4, HTTP_BODY_CAP = 2e6;
|
|
13208
|
-
|
|
13264
|
+
app.post("/api/http/request", async (req) => {
|
|
13209
13265
|
let { method, url, headers, body } = req.body, start = Date.now(), controller = new AbortController(), timer = setTimeout(() => controller.abort(), HTTP_TIMEOUT_MS), hasBody = !["GET", "HEAD"].includes(method.toUpperCase());
|
|
13210
13266
|
try {
|
|
13211
13267
|
let res = await fetch(url, {
|
|
@@ -13236,9 +13292,9 @@ ${answer}`
|
|
|
13236
13292
|
} finally {
|
|
13237
13293
|
clearTimeout(timer);
|
|
13238
13294
|
}
|
|
13239
|
-
}),
|
|
13295
|
+
}), app.post("/api/http/snapshot", async (req) => svc.saveHttpSnapshot(req.body.request, req.body.response, req.body.origin ?? "captured")), app.get("/api/snapshots", async () => svc.listSnapshots()), app.patch("/api/snapshots/:id", async (req, reply) => svc.updateSnapshot(req.params.id, req.body) ? { ok: !0 } : reply.code(404).send({ ok: !1, error: "Contract not found." })), app.delete("/api/snapshots/:id", async (req) => (svc.deleteSnapshot(req.params.id), { ok: !0 })), app.post("/api/automate/contracts", async (req) => svc.automateSnapshots(req.body.snapshotIds, req.body.format)), app.post("/api/prompts/generate", async (req) => ({
|
|
13240
13296
|
prompts: svc.generatePromptsForTool(req.body.tool)
|
|
13241
|
-
})),
|
|
13297
|
+
})), app.get("/api/prompts/custom", async () => svc.listCustomPrompts()), app.post("/api/llm/run", async (req) => callLlm(req.body)), app.post("/api/prompts/run-agent", async (req, reply) => {
|
|
13242
13298
|
let { tool, prompt, llm } = req.body, t = svc.getCapabilities()?.tools.find((x) => x.name === tool);
|
|
13243
13299
|
return t ? runAgent({
|
|
13244
13300
|
...llm,
|
|
@@ -13249,7 +13305,7 @@ ${answer}`
|
|
|
13249
13305
|
return { output: r.output, isError: r.isError };
|
|
13250
13306
|
}
|
|
13251
13307
|
}) : reply.code(400).send({ ok: !1, toolCalls: [], error: `Tool "${tool}" isn't on the current connection \u2014 connect first.`, latencyMs: 0 });
|
|
13252
|
-
}),
|
|
13308
|
+
}), app.post("/api/rag/hosted-run", async (req, reply) => {
|
|
13253
13309
|
let cases = Array.isArray(req.body?.cases) ? req.body.cases.filter((candidate) => candidate && typeof candidate.id == "string" && hostedRagCaseQuery(candidate).trim()) : [], capabilities = svc.getCapabilities();
|
|
13254
13310
|
if (!capabilities)
|
|
13255
13311
|
return reply.code(400).send({ ok: !1, error: "Connect to an MCP server first \u2014 RAG Assurance runs against its retrieval tools." });
|
|
@@ -13432,7 +13488,7 @@ ${answer}`
|
|
|
13432
13488
|
evidence: JSON.stringify(mcpSnapshot ? { ...snapshot, mcp: mcpSnapshot.mcp, hostedScanId: hostedScan.id } : { ...snapshot, hostedScanId: hostedScan.id })
|
|
13433
13489
|
}), diff = previous ? hostedRagScoreDiff(previous, snapshot) : void 0;
|
|
13434
13490
|
return finish({ ok: !0, report, diff, run, hostedScan });
|
|
13435
|
-
}),
|
|
13491
|
+
}), app.get("/api/rag/hosted-diff/:id", async (req, reply) => {
|
|
13436
13492
|
let runs = svc.listRuns(1e3).filter((record6) => record6.pillar === "rag"), index = runs.findIndex((record6) => record6.id === req.params.id);
|
|
13437
13493
|
if (index === -1)
|
|
13438
13494
|
return reply.code(404).send({ ok: !1, error: "Run not found." });
|
|
@@ -13472,7 +13528,7 @@ ${answer}`
|
|
|
13472
13528
|
durationMs
|
|
13473
13529
|
});
|
|
13474
13530
|
}
|
|
13475
|
-
|
|
13531
|
+
app.post("/api/perf/run", async (req, reply) => {
|
|
13476
13532
|
let { target, vus, durationS, targetMs } = req.body;
|
|
13477
13533
|
if (vus > 5) {
|
|
13478
13534
|
let plan = await resolvePlan(req);
|
|
@@ -13513,7 +13569,7 @@ ${answer}`
|
|
|
13513
13569
|
};
|
|
13514
13570
|
let metrics = await runLoadTest({ fire, vus: cappedVus, durationMs, targetMs });
|
|
13515
13571
|
return mcpSnapshot ? { ok: !0, ...metrics, mcpSnapshot, mcpFact: recordBoundMcpFact(mcpSnapshot, metrics.errors === 0) } : { ok: !0, ...metrics };
|
|
13516
|
-
}),
|
|
13572
|
+
}), app.post("/api/prompts/generate-ai", async (req, reply) => {
|
|
13517
13573
|
let plan = await resolvePlan(req);
|
|
13518
13574
|
if (!plan.hosted)
|
|
13519
13575
|
return reply.code(402).send({
|
|
@@ -13542,17 +13598,17 @@ Cover a mix of: positive (valid, useful requests), negative (misuse \u2014 missi
|
|
|
13542
13598
|
skipped: parsed.length - saved.length,
|
|
13543
13599
|
tokens: (res.usage?.inputTokens ?? 0) + (res.usage?.outputTokens ?? 0)
|
|
13544
13600
|
};
|
|
13545
|
-
}),
|
|
13601
|
+
}), app.post("/api/prompts/custom/:id/golden", async (req) => (svc.setPromptGolden(req.params.id, req.body.expected, req.body.assert ?? "similar"), { ok: !0 })), app.post("/api/prompts/custom/:id/checks", async (req) => (svc.setPromptChecks(req.params.id, {
|
|
13546
13602
|
input: req.body.input,
|
|
13547
13603
|
assertions: req.body.assertions,
|
|
13548
13604
|
checkSchema: req.body.checkSchema
|
|
13549
|
-
}), { ok: !0 })),
|
|
13605
|
+
}), { ok: !0 })), app.post("/api/prompts/assert", async (req, reply) => {
|
|
13550
13606
|
try {
|
|
13551
13607
|
return { ok: !0, ...await svc.assertTool(req.body.tool, req.body.input ?? {}, req.body.assertions ?? [], req.body.checkSchema ?? !1) };
|
|
13552
13608
|
} catch (e) {
|
|
13553
13609
|
return reply.code(400).send({ ok: !1, error: e instanceof Error ? e.message : String(e) });
|
|
13554
13610
|
}
|
|
13555
|
-
}),
|
|
13611
|
+
}), app.post("/api/prompts/custom", async (req, reply) => {
|
|
13556
13612
|
let plan = await resolvePlan(req);
|
|
13557
13613
|
return !isUnlimited(plan.prompts) && svc.listCustomPrompts().length >= plan.prompts ? reply.code(402).send({
|
|
13558
13614
|
ok: !1,
|
|
@@ -13562,7 +13618,7 @@ Cover a mix of: positive (valid, useful requests), negative (misuse \u2014 missi
|
|
|
13562
13618
|
limit: plan.prompts,
|
|
13563
13619
|
error: `Your ${plan.label} plan stores up to ${plan.prompts} test prompts. Delete one or upgrade to add more.`
|
|
13564
13620
|
}) : svc.addCustomPrompt(req.body.name, req.body.content, req.body.tool);
|
|
13565
|
-
}),
|
|
13621
|
+
}), app.patch("/api/prompts/custom/:id", async (req) => (svc.updateCustomPrompt(req.params.id, req.body), { ok: !0 })), app.delete("/api/prompts/custom/:id", async (req) => (svc.deleteCustomPrompt(req.params.id), { ok: !0 })), app.post("/api/prompts/generate-cases", async (req, reply) => {
|
|
13566
13622
|
let plan = await resolvePlan(req);
|
|
13567
13623
|
if (!plan.hosted)
|
|
13568
13624
|
return reply.code(402).send({
|
|
@@ -13614,7 +13670,7 @@ Assertions must be objectively checkable against the tool's JSON response. Never
|
|
|
13614
13670
|
errorKind: "hosted-generation",
|
|
13615
13671
|
error: "The model did not return usable test cases. Retry, or choose a model that reliably follows structured-output instructions."
|
|
13616
13672
|
});
|
|
13617
|
-
}),
|
|
13673
|
+
}), app.post("/api/prompts/custom/bulk", async (req, reply) => {
|
|
13618
13674
|
let cases = (req.body?.cases ?? []).filter((c) => c && typeof c.content == "string" && c.content.trim());
|
|
13619
13675
|
if (!cases.length)
|
|
13620
13676
|
return reply.code(400).send({ ok: !1, error: "Select at least one reviewed case to save." });
|
|
@@ -13666,7 +13722,7 @@ Assertions must be objectively checkable against the tool's JSON response. Never
|
|
|
13666
13722
|
let all = svc.getCapabilities()?.tools ?? [], enabled = session.config.enabledTools;
|
|
13667
13723
|
return enabled && enabled.length ? all.filter((t) => enabled.includes(t.name)) : all;
|
|
13668
13724
|
};
|
|
13669
|
-
|
|
13725
|
+
app.post("/api/prompt-studio/sessions", async (req) => {
|
|
13670
13726
|
let body = req.body ?? {}, now = (/* @__PURE__ */ new Date()).toISOString(), session = {
|
|
13671
13727
|
id: `ps-${randomUUID7().slice(0, 8)}`,
|
|
13672
13728
|
name: (body.name ?? "").trim().slice(0, 120) || `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
|
|
@@ -13697,13 +13753,13 @@ Assertions must be objectively checkable against the tool's JSON response. Never
|
|
|
13697
13753
|
oldest && studioSessions.delete(oldest.id);
|
|
13698
13754
|
}
|
|
13699
13755
|
return { ok: !0, session: studioSummary(session), events: [] };
|
|
13700
|
-
}),
|
|
13756
|
+
}), app.get("/api/prompt-studio/sessions", async () => ({
|
|
13701
13757
|
ok: !0,
|
|
13702
13758
|
sessions: [...studioSessions.values()].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)).map(studioSummary)
|
|
13703
|
-
})),
|
|
13759
|
+
})), app.get("/api/prompt-studio/sessions/:id", async (req, reply) => {
|
|
13704
13760
|
let session = studioSessions.get(req.params.id);
|
|
13705
13761
|
return session ? { ok: !0, session: studioSummary(session), events: session.events } : reply.code(404).send({ ok: !1, error: "That Prompt Studio session is no longer available." });
|
|
13706
|
-
}),
|
|
13762
|
+
}), app.get("/api/prompt-studio/sessions/:id/suggestions", async (req, reply) => {
|
|
13707
13763
|
let session = studioSessions.get(req.params.id);
|
|
13708
13764
|
if (!session)
|
|
13709
13765
|
return reply.code(404).send({ ok: !1, error: "That Prompt Studio session is no longer available." });
|
|
@@ -13713,10 +13769,10 @@ Assertions must be objectively checkable against the tool's JSON response. Never
|
|
|
13713
13769
|
observation: { prompt: observation.prompt, answer: observation.answer, toolCalls: observation.toolCalls },
|
|
13714
13770
|
suggestions: suggestPromptTestChecks(observation)
|
|
13715
13771
|
};
|
|
13716
|
-
}),
|
|
13772
|
+
}), app.delete("/api/prompt-studio/sessions/:id", async (req) => (studioSessions.delete(req.params.id), { ok: !0 })), app.post("/api/prompt-studio/sessions/:id/stop", async (req, reply) => {
|
|
13717
13773
|
let session = studioSessions.get(req.params.id);
|
|
13718
13774
|
return session ? (session.stopRequested = !0, { ok: !0, stopping: session.running }) : reply.code(404).send({ ok: !1, error: "That Prompt Studio session is no longer available." });
|
|
13719
|
-
}),
|
|
13775
|
+
}), app.post("/api/prompt-studio/sessions/:id/messages", async (req, reply) => {
|
|
13720
13776
|
let session = studioSessions.get(req.params.id);
|
|
13721
13777
|
if (!session)
|
|
13722
13778
|
return reply.code(404).send({ ok: !1, error: "That Prompt Studio session is no longer available. Start a new session." });
|
|
@@ -13795,7 +13851,7 @@ Assertions must be objectively checkable against the tool's JSON response. Never
|
|
|
13795
13851
|
} finally {
|
|
13796
13852
|
session.running = !1, session.stopRequested = !1;
|
|
13797
13853
|
}
|
|
13798
|
-
}),
|
|
13854
|
+
}), app.post("/api/prompt-studio/sessions/:id/tools/:tool", async (req, reply) => {
|
|
13799
13855
|
let session = studioSessions.get(req.params.id);
|
|
13800
13856
|
if (!session)
|
|
13801
13857
|
return reply.code(404).send({ ok: !1, error: "That Prompt Studio session is no longer available." });
|
|
@@ -13840,7 +13896,7 @@ Assertions must be objectively checkable against the tool's JSON response. Never
|
|
|
13840
13896
|
let attributed = attributeError(error instanceof Error ? error.message : String(error), "tool");
|
|
13841
13897
|
return pushStudioEvent(session, turnId, "run-failed", { tool: tool.name, error: attributed.detail, errorKind: attributed.kind }), reply.code(502).send({ ok: !1, error: attributed.detail, errorKind: attributed.kind, errorTitle: attributed.title });
|
|
13842
13898
|
}
|
|
13843
|
-
}),
|
|
13899
|
+
}), app.post("/api/model-scans/run", async (req, reply) => {
|
|
13844
13900
|
let target = req.body?.path?.trim();
|
|
13845
13901
|
if (!target || target.length > 4096)
|
|
13846
13902
|
return reply.code(400).send({ ok: !1, error: "A valid local model file or directory path is required." });
|
|
@@ -13852,7 +13908,7 @@ Assertions must be objectively checkable against the tool's JSON response. Never
|
|
|
13852
13908
|
maxArchiveExpandedBytes: Math.round(maxArchiveMb * 1024 * 1024)
|
|
13853
13909
|
});
|
|
13854
13910
|
return store.saveModelScan(report), reply.header("Cache-Control", "no-store"), { ok: !0, report };
|
|
13855
|
-
}),
|
|
13911
|
+
}), app.get("/api/model-scans", async (req, reply) => {
|
|
13856
13912
|
let limit = Math.max(1, Math.min(200, Math.trunc(Number(req.query.limit) || 25)));
|
|
13857
13913
|
return reply.header("Cache-Control", "no-store"), store.listModelScans(limit).map((report) => ({
|
|
13858
13914
|
scanId: report.scanId,
|
|
@@ -13863,16 +13919,16 @@ Assertions must be objectively checkable against the tool's JSON response. Never
|
|
|
13863
13919
|
summary: report.summary,
|
|
13864
13920
|
artifactIdentity: report.artifactIdentity
|
|
13865
13921
|
}));
|
|
13866
|
-
}),
|
|
13922
|
+
}), app.get("/api/model-scans/:id", async (req, reply) => {
|
|
13867
13923
|
let report = store.getModelScan(req.params.id);
|
|
13868
13924
|
return report ? (reply.header("Cache-Control", "no-store"), { ok: !0, report }) : reply.code(404).send({ ok: !1, error: "Model scan not found." });
|
|
13869
|
-
}),
|
|
13925
|
+
}), app.get("/api/model-scans/:id/export", async (req, reply) => {
|
|
13870
13926
|
let report = store.getModelScan(req.params.id);
|
|
13871
13927
|
if (!report)
|
|
13872
13928
|
return reply.code(404).send({ ok: !1, error: "Model scan not found." });
|
|
13873
13929
|
let safeName = report.target.displayName.replace(/[^A-Za-z0-9._-]+/g, "-").slice(0, 100) || "model", format = String(req.query.format ?? "json").toLowerCase();
|
|
13874
13930
|
return reply.header("Cache-Control", "no-store"), format === "html" ? (reply.type("text/html; charset=utf-8").header("Content-Disposition", `attachment; filename="${safeName}-prooflane-model-scan.html"`), reply.send(modelScanToHtml(report))) : format === "sarif" ? (reply.type("application/sarif+json").header("Content-Disposition", `attachment; filename="${safeName}-prooflane-model-scan.sarif.json"`), reply.send(JSON.stringify(modelScanToSarif(report), null, 2))) : (reply.type("application/json").header("Content-Disposition", `attachment; filename="${safeName}-prooflane-model-scan.json"`), reply.send(JSON.stringify(report, null, 2)));
|
|
13875
|
-
}),
|
|
13931
|
+
}), app.delete("/api/model-scans/:id", async (req, reply) => store.deleteModelScan(req.params.id) ? { ok: !0 } : reply.code(404).send({ ok: !1, error: "Model scan not found." })), app.get("/api/model-security/packs", async (_req, reply) => (reply.header("Cache-Control", "no-store"), {
|
|
13876
13932
|
packs: MODEL_SECURITY_PACKS,
|
|
13877
13933
|
checks: MODEL_SECURITY_RULES.map((rule) => ({
|
|
13878
13934
|
id: rule.id,
|
|
@@ -13886,7 +13942,7 @@ Assertions must be objectively checkable against the tool's JSON response. Never
|
|
|
13886
13942
|
stages: MODEL_SCAN_STAGES.map((stage) => ({ id: stage, label: MODEL_SCAN_STAGE_LABEL[stage] })),
|
|
13887
13943
|
sourceTypes: SUPPORTED_MODEL_SOURCE_TYPES,
|
|
13888
13944
|
evidenceClasses: MODEL_EVIDENCE_CLASSES.map((id) => ({ id, label: MODEL_EVIDENCE_LABEL[id] }))
|
|
13889
|
-
})),
|
|
13945
|
+
})), app.post("/api/model-security/run", async (req, reply) => {
|
|
13890
13946
|
let target = req.body?.target;
|
|
13891
13947
|
if (!target?.sourceType || !target.source?.trim())
|
|
13892
13948
|
return reply.code(400).send({ ok: !1, error: "A model security scan needs a target source type and source." });
|
|
@@ -13929,19 +13985,19 @@ Assertions must be objectively checkable against the tool's JSON response. Never
|
|
|
13929
13985
|
let message2 = error instanceof Error ? error.message : String(error);
|
|
13930
13986
|
return wantStream && streamOpen ? (streamWrite({ t: "done", ok: !1, error: message2 }), reply.raw.end(), reply) : reply.code(400).send({ ok: !1, error: message2 });
|
|
13931
13987
|
}
|
|
13932
|
-
}),
|
|
13988
|
+
}), app.get("/api/model-security", async (req, reply) => {
|
|
13933
13989
|
let limit = Math.max(1, Math.min(200, Math.trunc(Number(req.query.limit) || 25)));
|
|
13934
13990
|
return reply.header("Cache-Control", "no-store"), store.listModelSecurityScans(limit).map(modelSecuritySummary);
|
|
13935
|
-
}),
|
|
13991
|
+
}), app.get("/api/model-security/:id", async (req, reply) => {
|
|
13936
13992
|
let report = store.getModelSecurityScan(req.params.id);
|
|
13937
13993
|
return report ? (reply.header("Cache-Control", "no-store"), { ok: !0, report }) : reply.code(404).send({ ok: !1, error: "Model security scan not found." });
|
|
13938
|
-
}),
|
|
13994
|
+
}), app.get("/api/model-security/:id/export", async (req, reply) => {
|
|
13939
13995
|
let report = store.getModelSecurityScan(req.params.id);
|
|
13940
13996
|
if (!report)
|
|
13941
13997
|
return reply.code(404).send({ ok: !1, error: "Model security scan not found." });
|
|
13942
13998
|
let safeName = report.target.displayName.replace(/[^A-Za-z0-9._-]+/g, "-").slice(0, 100) || "model", format = String(req.query.format ?? "json").toLowerCase();
|
|
13943
13999
|
return reply.header("Cache-Control", "no-store"), format === "html" ? (reply.type("text/html; charset=utf-8").header("Content-Disposition", `attachment; filename="${safeName}-model-supply-chain.html"`), reply.send(modelSecurityToHtml(report))) : format === "sarif" ? (reply.type("application/sarif+json").header("Content-Disposition", `attachment; filename="${safeName}-model-supply-chain.sarif.json"`), reply.send(JSON.stringify(modelSecurityToSarif(report), null, 2))) : (reply.type("application/json").header("Content-Disposition", `attachment; filename="${safeName}-model-supply-chain.json"`), reply.send(JSON.stringify(report, null, 2)));
|
|
13944
|
-
}),
|
|
14000
|
+
}), app.delete("/api/model-security/:id", async (req, reply) => store.deleteModelSecurityScan(req.params.id) ? { ok: !0 } : reply.code(404).send({ ok: !1, error: "Model security scan not found." })), app.post("/api/runs", async (req) => svc.recordRun(req.body)), app.get("/api/runs", async (req, reply) => {
|
|
13945
14001
|
let plan = await resolvePlan(req), requestedLimit = Number(req.query.limit), limit = Number.isFinite(requestedLimit) && requestedLimit > 0 ? Math.min(1e3, Math.trunc(requestedLimit)) : void 0;
|
|
13946
14002
|
reply.header("Cache-Control", "no-store");
|
|
13947
14003
|
let runs = svc.listRuns(limit);
|
|
@@ -13951,26 +14007,32 @@ Assertions must be objectively checkable against the tool's JSON response. Never
|
|
|
13951
14007
|
return runs.filter((run) => Date.parse(run.createdAt) >= oldest);
|
|
13952
14008
|
});
|
|
13953
14009
|
let uiDist = process.env.KAWACH_UI_DIST ?? join4(__dirname, "../../ui/dist");
|
|
13954
|
-
return existsSync2(uiDist) && (
|
|
14010
|
+
return existsSync2(uiDist) && (app.register(fastifyStatic, { root: uiDist }), app.setNotFoundHandler((req, reply) => {
|
|
13955
14011
|
if (req.url.startsWith("/api")) {
|
|
13956
14012
|
reply.code(404).send({ error: "Not found" });
|
|
13957
14013
|
return;
|
|
13958
14014
|
}
|
|
13959
14015
|
reply.sendFile("index.html");
|
|
13960
|
-
})),
|
|
14016
|
+
})), app.setErrorHandler((err, req, reply) => {
|
|
13961
14017
|
let upstream = err instanceof KawachError ? err.statusCode : void 0;
|
|
13962
14018
|
req.log.error({ err, upstream }, "Inspector request failed"), reply.code(upstream ? 502 : 500).send({ error: publicServerError(req.url), reference: req.id });
|
|
13963
|
-
}),
|
|
14019
|
+
}), app.addHook("onClose", async () => store.close()), app;
|
|
14020
|
+
}
|
|
14021
|
+
|
|
14022
|
+
// ../server/dist/start.js
|
|
14023
|
+
var PORT = Number(process.env.KAWACH_UI_PORT ?? 7357), HOST = "127.0.0.1";
|
|
14024
|
+
async function startServer(options = {}) {
|
|
14025
|
+
let app = buildApp();
|
|
14026
|
+
options.configureApp?.(app), await app.listen({ port: PORT, host: HOST }), console.log(`Kawach Inspector UI \u2192 http://localhost:${PORT}`);
|
|
14027
|
+
for (let sig of ["SIGINT", "SIGTERM"])
|
|
14028
|
+
process.once(sig, () => {
|
|
14029
|
+
app.close().finally(() => process.exit(0));
|
|
14030
|
+
});
|
|
14031
|
+
return app;
|
|
14032
|
+
}
|
|
14033
|
+
function failServerStart(error) {
|
|
14034
|
+
console.error("Failed to start Kawach Inspector server:", error), process.exit(1);
|
|
13964
14035
|
}
|
|
13965
14036
|
|
|
13966
14037
|
// ../server/dist/index.js
|
|
13967
|
-
|
|
13968
|
-
app.listen({ port: PORT, host: HOST }).then(() => {
|
|
13969
|
-
console.log(`Kawach Inspector UI \u2192 http://localhost:${PORT}`);
|
|
13970
|
-
}).catch((err) => {
|
|
13971
|
-
console.error("Failed to start Kawach Inspector server:", err), process.exit(1);
|
|
13972
|
-
});
|
|
13973
|
-
for (let sig of ["SIGINT", "SIGTERM"])
|
|
13974
|
-
process.on(sig, () => {
|
|
13975
|
-
app.close().finally(() => process.exit(0));
|
|
13976
|
-
});
|
|
14038
|
+
startServer().catch(failServerStart);
|