sentinelayer-cli 0.46.5 → 0.48.0
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 +47 -3
- package/package.json +12 -7
- package/skills/omar-gate-setup/SKILL.md +103 -0
- package/skills/senti-agent-identity/SKILL.md +80 -0
- package/skills/senti-judge/SKILL.md +139 -0
- package/skills/senti-room-notifications/SKILL.md +73 -0
- package/skills/senti-room-notifications/listen-wrapper.cjs +150 -0
- package/skills/senti-room-notifications/package.json +1 -0
- package/skills/senti-room-notifications/test-classifier.js +109 -0
- package/skills/senti-room-notifications/test-receipt.js +54 -0
- package/skills/sentinelayer-cli/SKILL.md +222 -0
- package/src/agents/devtestbot/runner.js +22 -11
- package/src/agents/devtestbot/tool.js +24 -11
- package/src/agents/jules/fix-cycle.js +7 -1
- package/src/agents/jules/tools/runtime-audit.js +17 -12
- package/src/agents/shared-tools/dispatch-core.js +10 -5
- package/src/ai/aidenid.js +4 -2
- package/src/ai/domain-target-store.js +11 -5
- package/src/ai/identity-store.js +11 -5
- package/src/ai/site-store.js +8 -4
- package/src/audit/orchestrator.js +21 -14
- package/src/audit/package.js +6 -4
- package/src/audit/persona-loop.js +7 -0
- package/src/audit/replay.js +4 -2
- package/src/auth/gate.js +32 -1
- package/src/auth/session-store.js +37 -0
- package/src/board/evidence-resolvers.js +3 -4
- package/src/cli.js +23 -3
- package/src/commands/ai/identity-lifecycle.js +30 -26
- package/src/commands/ai/provision-governance.js +32 -24
- package/src/commands/ai/shared.js +6 -4
- package/src/commands/audit.js +14 -12
- package/src/commands/board.js +2 -2
- package/src/commands/chat.js +8 -6
- package/src/commands/judge.js +125 -0
- package/src/commands/legacy-args.js +1 -0
- package/src/commands/mcp.js +45 -52
- package/src/commands/omargate.js +2 -1
- package/src/commands/plugin.js +11 -3
- package/src/commands/review.js +9 -4
- package/src/commands/scan.js +7 -7
- package/src/commands/session.js +516 -30
- package/src/commands/skills.js +45 -0
- package/src/commands/watch.js +9 -7
- package/src/config/output-filesystem.js +156 -0
- package/src/config/schema.js +4 -2
- package/src/config/service.js +40 -4
- package/src/cost/history.js +15 -9
- package/src/daemon/artifact-lineage.js +15 -12
- package/src/daemon/assignment-ledger.js +31 -25
- package/src/daemon/ast-drift.js +7 -3
- package/src/daemon/budget-governor.js +26 -20
- package/src/daemon/error-worker.js +28 -22
- package/src/daemon/fix-cycle.js +5 -4
- package/src/daemon/hybrid-mapper.js +19 -16
- package/src/daemon/ingest-refresh.js +4 -0
- package/src/daemon/jira-lifecycle.js +21 -17
- package/src/daemon/operator-control.js +18 -15
- package/src/daemon/reliability-lane.js +18 -15
- package/src/daemon/scope-engine.js +12 -8
- package/src/daemon/watchdog.js +15 -12
- package/src/engram/build-state.js +24 -3
- package/src/engram/capability-index.js +367 -0
- package/src/engram/compile-graph.js +1063 -0
- package/src/engram/contracts/action-receipt.js +82 -0
- package/src/engram/contracts/capability-index.js +259 -0
- package/src/engram/contracts/capability.js +377 -0
- package/src/engram/contracts/content-hash.js +8 -0
- package/src/engram/contracts/evidence.js +258 -0
- package/src/engram/contracts/graph.js +594 -0
- package/src/engram/contracts/identity.js +8 -0
- package/src/engram/contracts/index.js +149 -0
- package/src/engram/contracts/media-asset.js +85 -0
- package/src/engram/contracts/money.js +249 -0
- package/src/engram/contracts/source-snapshot.js +57 -0
- package/src/engram/contracts/task-context-pack.js +114 -0
- package/src/engram/contracts/temporal-segment.js +72 -0
- package/src/engram/contracts/versions.js +96 -0
- package/src/engram/extract-html.js +692 -0
- package/src/engram/graph-store.js +877 -0
- package/src/engram/index.js +98 -14
- package/src/engram/ingest-site.js +132 -0
- package/src/engram/live-state.js +1180 -0
- package/src/engram/namespace.js +34 -1
- package/src/engram/retrieve-site.js +753 -0
- package/src/engram/safe-reader.js +597 -0
- package/src/engram/source-intake.js +251 -0
- package/src/engram/store.js +227 -9
- package/src/ingest/engine.js +21 -28
- package/src/judge/verdict.js +305 -0
- package/src/legacy-cli.js +96 -19
- package/src/mcp/bridge-inputs.js +25 -4
- package/src/mcp/cli-command-tools.js +70 -0
- package/src/mcp/registry.js +17 -13
- package/src/mcp/session-stdio-server.js +297 -19
- package/src/memory/blackboard.js +5 -4
- package/src/net/gh-api-path.js +35 -9
- package/src/net/test-egress-guard.js +16 -4
- package/src/plugin/manifest.js +12 -8
- package/src/review/ai-review.js +110 -10
- package/src/review/investor-dd-devtestbot.js +17 -6
- package/src/review/investor-dd-orchestrator.js +21 -13
- package/src/review/local-review.js +26 -16
- package/src/review/omargate-cache.js +5 -5
- package/src/review/omargate-orchestrator.js +89 -17
- package/src/review/replay.js +9 -4
- package/src/review/report.js +22 -13
- package/src/review/typesafe-severity.js +201 -0
- package/src/session/admission-auth.js +34 -7
- package/src/session/admission.js +110 -15
- package/src/session/attachment-summary.js +77 -0
- package/src/session/edit-summary.js +19 -0
- package/src/session/invitations.js +65 -1
- package/src/session/mentions.js +17 -9
- package/src/session/message-edits.js +5 -4
- package/src/session/preview.js +10 -2
- package/src/session/recap.js +3 -2
- package/src/session/room-files.js +519 -0
- package/src/session/route-classes.js +22 -8
- package/src/session/setup-guides.js +4 -0
- package/src/session/standup.js +192 -0
- package/src/session/sync.js +229 -10
- package/src/session/tickets.js +41 -0
- package/src/session/transcript.js +2 -1
- package/src/session/wake/resolve-target.js +7 -3
- package/src/skills/listener-runtime.cjs +92 -0
- package/src/skills/service.js +207 -0
- package/src/swarm/factory.js +5 -5
- package/src/swarm/pentest.js +9 -9
- package/src/swarm/report.js +4 -4
- package/src/swarm/runtime.js +15 -12
- package/src/swarm/scenario-dsl.js +7 -5
- package/src/telemetry/ledger.js +16 -7
|
@@ -3,7 +3,8 @@ import fsp from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
5
|
import { getIdentityById } from "../../ai/identity-store.js";
|
|
6
|
-
import {
|
|
6
|
+
import { resolveOutputContext } from "../../config/service.js";
|
|
7
|
+
import { requireOutputFileSystem } from "../../config/output-filesystem.js";
|
|
7
8
|
import { createAgentEvent } from "../../events/schema.js";
|
|
8
9
|
import { DEVTESTBOT_DEFINITION, DEVTESTBOT_LANES } from "./config/definition.js";
|
|
9
10
|
import { launch } from "./runner.js";
|
|
@@ -63,7 +64,7 @@ export async function executeDevTestBotRunSessionTool(input = {}, ctx = {}) {
|
|
|
63
64
|
|
|
64
65
|
export async function runDevTestBotSession(input = {}, ctx = {}) {
|
|
65
66
|
const targetPath = path.resolve(String(input.targetPath || ctx.targetPath || process.cwd()));
|
|
66
|
-
const outputRoot = await
|
|
67
|
+
const { outputRoot, outputFs: resolvedOutputFs } = await resolveOutputContext({
|
|
67
68
|
cwd: targetPath,
|
|
68
69
|
outputDirOverride: input.outputRoot || ctx.outputRoot || "",
|
|
69
70
|
env: ctx.env || process.env,
|
|
@@ -74,10 +75,13 @@ export async function runDevTestBotSession(input = {}, ctx = {}) {
|
|
|
74
75
|
const recordVideo = input.recordVideo !== undefined ? Boolean(input.recordVideo) : true;
|
|
75
76
|
const baseUrl = normalizeString(input.baseUrl || ctx.baseUrl);
|
|
76
77
|
const identityId = normalizeString(input.identityId || ctx.identityId);
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
const explicitOutputDir = input.outputDir || ctx.outputDir;
|
|
79
|
+
const artifactOutput = explicitOutputDir
|
|
80
|
+
? await resolveOutputContext({ cwd: targetPath, outputDirOverride: explicitOutputDir, env: ctx.env || process.env })
|
|
81
|
+
: { outputRoot: path.join(outputRoot, "runs", runId, "devtestbot"), outputFs: resolvedOutputFs };
|
|
82
|
+
const artifactRoot = artifactOutput.outputRoot;
|
|
83
|
+
const outputFs = ctx.outputFs === undefined ? artifactOutput.outputFs : requireOutputFileSystem(ctx.outputFs);
|
|
84
|
+
await outputFs.mkdir(artifactRoot, { recursive: true });
|
|
81
85
|
|
|
82
86
|
const { registryPath, identity } = await resolveIdentity({
|
|
83
87
|
outputRoot,
|
|
@@ -142,6 +146,7 @@ export async function runDevTestBotSession(input = {}, ctx = {}) {
|
|
|
142
146
|
scope,
|
|
143
147
|
baseUrl,
|
|
144
148
|
artifactRoot,
|
|
149
|
+
outputFs,
|
|
145
150
|
sensitiveValues,
|
|
146
151
|
});
|
|
147
152
|
for (const finding of dryRun.findings) {
|
|
@@ -168,6 +173,7 @@ export async function runDevTestBotSession(input = {}, ctx = {}) {
|
|
|
168
173
|
completed: true,
|
|
169
174
|
dryRun: true,
|
|
170
175
|
artifactRoot,
|
|
176
|
+
outputFs,
|
|
171
177
|
artifactBundle: dryRun.artifactBundle,
|
|
172
178
|
artifacts: dryRun.artifacts,
|
|
173
179
|
findings: dryRun.findings,
|
|
@@ -183,6 +189,7 @@ export async function runDevTestBotSession(input = {}, ctx = {}) {
|
|
|
183
189
|
baseUrl: normalizedBaseUrl,
|
|
184
190
|
identityCreds,
|
|
185
191
|
outputDir: artifactRoot,
|
|
192
|
+
outputFs,
|
|
186
193
|
recordVideo,
|
|
187
194
|
runLighthouse: input.runLighthouse !== undefined ? Boolean(input.runLighthouse) : true,
|
|
188
195
|
lighthouseTimeoutMs: input.lighthouseTimeoutMs,
|
|
@@ -229,6 +236,7 @@ export async function runDevTestBotSession(input = {}, ctx = {}) {
|
|
|
229
236
|
completed: true,
|
|
230
237
|
dryRun: false,
|
|
231
238
|
artifactRoot,
|
|
239
|
+
outputFs,
|
|
232
240
|
artifacts,
|
|
233
241
|
findings,
|
|
234
242
|
laneSummaries,
|
|
@@ -249,6 +257,7 @@ export async function runDevTestBotSession(input = {}, ctx = {}) {
|
|
|
249
257
|
completed: false,
|
|
250
258
|
dryRun: !execute,
|
|
251
259
|
artifactRoot,
|
|
260
|
+
outputFs,
|
|
252
261
|
artifacts: {},
|
|
253
262
|
findings: [],
|
|
254
263
|
laneSummaries: {},
|
|
@@ -351,7 +360,8 @@ async function driveScope({ runner, scope }) {
|
|
|
351
360
|
}
|
|
352
361
|
}
|
|
353
362
|
|
|
354
|
-
async function buildDryRunResult({ runId, scope, baseUrl, artifactRoot, sensitiveValues }) {
|
|
363
|
+
async function buildDryRunResult({ runId, scope, baseUrl, artifactRoot, sensitiveValues, outputFs }) {
|
|
364
|
+
const writeJson = (...args) => writeJsonWithFs(outputFs, ...args);
|
|
355
365
|
const artifacts = {
|
|
356
366
|
manifestPath: path.join(artifactRoot, "manifest.json"),
|
|
357
367
|
};
|
|
@@ -561,6 +571,7 @@ function normalizeFinding(input, sensitiveValues) {
|
|
|
561
571
|
}
|
|
562
572
|
|
|
563
573
|
async function writeDevTestBotResult({
|
|
574
|
+
outputFs,
|
|
564
575
|
runId,
|
|
565
576
|
scope,
|
|
566
577
|
completed,
|
|
@@ -574,6 +585,7 @@ async function writeDevTestBotResult({
|
|
|
574
585
|
sensitiveValues,
|
|
575
586
|
error = "",
|
|
576
587
|
}) {
|
|
588
|
+
const writeJson = (...args) => writeJsonWithFs(outputFs, ...args);
|
|
577
589
|
const findingsPath = path.join(artifactRoot, "findings.json");
|
|
578
590
|
const eventsPath = path.join(artifactRoot, "events.ndjson");
|
|
579
591
|
const resultPath = path.join(artifactRoot, "devtestbot-result.json");
|
|
@@ -586,7 +598,7 @@ async function writeDevTestBotResult({
|
|
|
586
598
|
artifacts: sanitizeArtifacts(artifacts || artifactBundle?.artifacts || {}, sensitiveValues),
|
|
587
599
|
};
|
|
588
600
|
await writeJson(findingsPath, findings);
|
|
589
|
-
await
|
|
601
|
+
await outputFs.writeFile(eventsPath, `${events.map((event) => JSON.stringify(event)).join("\n")}\n`, "utf-8");
|
|
590
602
|
const result = sanitizeJson({
|
|
591
603
|
schemaVersion: 1,
|
|
592
604
|
runId,
|
|
@@ -701,7 +713,8 @@ async function readJsonIfPresent(filePath) {
|
|
|
701
713
|
}
|
|
702
714
|
}
|
|
703
715
|
|
|
704
|
-
async function
|
|
705
|
-
|
|
706
|
-
await
|
|
716
|
+
async function writeJsonWithFs(outputFs, filePath, payload) {
|
|
717
|
+
requireOutputFileSystem(outputFs);
|
|
718
|
+
await outputFs.mkdir(path.dirname(filePath), { recursive: true });
|
|
719
|
+
await outputFs.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
|
|
707
720
|
}
|
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
// Jules passes its own identity; any persona can use the same lifecycle.
|
|
3
3
|
import { runFixCycle as _runFixCycle } from "../../daemon/fix-cycle.js";
|
|
4
4
|
import { JULES_DEFINITION } from "./config/definition.js";
|
|
5
|
+
import { resolveOutputContext } from "../../config/service.js";
|
|
6
|
+
import { requireOutputFileSystem } from "../../config/output-filesystem.js";
|
|
5
7
|
|
|
6
|
-
export function runFixCycle(opts) {
|
|
8
|
+
export async function runFixCycle(opts = {}) {
|
|
9
|
+
const outputFs = Object.hasOwn(opts, "outputFs")
|
|
10
|
+
? requireOutputFileSystem(opts.outputFs)
|
|
11
|
+
: (await resolveOutputContext({ cwd: opts.rootPath || process.cwd(), outputDirOverride: ".sentinelayer" })).outputFs;
|
|
7
12
|
return _runFixCycle({
|
|
8
13
|
...opts,
|
|
14
|
+
outputFs,
|
|
9
15
|
agentIdentity: {
|
|
10
16
|
id: JULES_DEFINITION.id,
|
|
11
17
|
persona: JULES_DEFINITION.persona,
|
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
6
7
|
import { credentialedRequest, isAuthenticated } from "../../../auth/credential-destinations.js";
|
|
7
8
|
import { urlPathSegment } from "../../../net/url-path.js";
|
|
8
9
|
import { assertPermittedAuditTarget } from "./url-policy.js";
|
|
@@ -52,16 +53,24 @@ const RUNTIME_DISPATCH = {
|
|
|
52
53
|
/**
|
|
53
54
|
* How Lighthouse runs. No shell: the target URL can come from the repository (package.json,
|
|
54
55
|
* vercel.json, .env files), and a shell would expand variables and operators inside it. Credentials
|
|
55
|
-
* are removed from
|
|
56
|
-
*
|
|
56
|
+
* are removed from the child environment. Resolve the installed Lighthouse dependency and run
|
|
57
|
+
* its CLI with Node directly, so this fallback cannot download another Lighthouse version.
|
|
57
58
|
*/
|
|
58
59
|
function lighthouseInvocation(
|
|
59
60
|
targetUrl,
|
|
60
61
|
outputPath,
|
|
61
|
-
{
|
|
62
|
+
{ execPath = process.execPath, exists = fs.existsSync, resolve = import.meta.resolve, env = process.env } = {},
|
|
62
63
|
) {
|
|
64
|
+
let lighthouseCli;
|
|
65
|
+
try {
|
|
66
|
+
lighthouseCli = fileURLToPath(resolve("lighthouse/cli/index.js"));
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (!exists(lighthouseCli)) return null;
|
|
71
|
+
|
|
63
72
|
const args = [
|
|
64
|
-
|
|
73
|
+
lighthouseCli, targetUrl,
|
|
65
74
|
"--output", "json", "--output-path", outputPath,
|
|
66
75
|
"--chrome-flags=--headless --no-sandbox --disable-gpu", "--quiet",
|
|
67
76
|
];
|
|
@@ -71,17 +80,13 @@ function lighthouseInvocation(
|
|
|
71
80
|
stdio: ["pipe", "pipe", "pipe"],
|
|
72
81
|
env: buildScrubbedEnv(env),
|
|
73
82
|
};
|
|
74
|
-
|
|
75
|
-
return { file: "npx", args, options };
|
|
76
|
-
}
|
|
77
|
-
const npxCli = path.join(path.dirname(execPath), "node_modules", "npm", "bin", "npx-cli.js");
|
|
78
|
-
return exists(npxCli) ? { file: execPath, args: [npxCli, ...args], options } : null;
|
|
83
|
+
return { file: execPath, args, options };
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
export const __runtimeAuditForTests = Object.freeze({ lighthouseInvocation, detectDeployedUrl });
|
|
82
87
|
|
|
83
88
|
/**
|
|
84
|
-
* Run
|
|
89
|
+
* Run the installed Lighthouse CLI.
|
|
85
90
|
* Returns performance, accessibility, best-practices, SEO scores + key metrics.
|
|
86
91
|
*/
|
|
87
92
|
async function lighthouseScan(input) {
|
|
@@ -92,7 +97,7 @@ async function lighthouseScan(input) {
|
|
|
92
97
|
const targetUrl = resolveRuntimeTargetUrl(url, input, "lighthouse_scan");
|
|
93
98
|
|
|
94
99
|
// Prefer SentinelLayer API scanner (authenticated, server-side Lighthouse).
|
|
95
|
-
// Falls back to local
|
|
100
|
+
// Falls back to the local Lighthouse dependency if API unavailable.
|
|
96
101
|
try {
|
|
97
102
|
const apiResult = await callScannerApi(targetUrl);
|
|
98
103
|
if (apiResult.available) return apiResult;
|
|
@@ -110,7 +115,7 @@ async function lighthouseScan(input) {
|
|
|
110
115
|
|
|
111
116
|
const invocation = lighthouseInvocation(targetUrl, outputPath);
|
|
112
117
|
if (!invocation) {
|
|
113
|
-
return { available: false, reason: "
|
|
118
|
+
return { available: false, reason: "Lighthouse is not installed locally" };
|
|
114
119
|
}
|
|
115
120
|
execFileSync(invocation.file, invocation.args, invocation.options);
|
|
116
121
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { bindOutputFileSystem, requireOutputFileSystem } from "../../config/output-filesystem.js";
|
|
2
3
|
import { evaluateBudget } from "../../cost/budget.js";
|
|
3
4
|
import {
|
|
4
5
|
normalizeRunEvent,
|
|
@@ -28,6 +29,7 @@ export function createToolDispatcher(toolMap, readOnlyTools) {
|
|
|
28
29
|
const READ_ONLY_TOOLS = new Set(readOnlyTools || []);
|
|
29
30
|
|
|
30
31
|
async function dispatchTool(toolName, input, ctx) {
|
|
32
|
+
if (ctx.artifactDir) requireOutputFileSystem(ctx.outputFs);
|
|
31
33
|
const handler = TOOL_MAP[toolName];
|
|
32
34
|
if (!handler) {
|
|
33
35
|
throw new ToolDispatchError(`Unknown tool: ${toolName}`);
|
|
@@ -186,9 +188,9 @@ export function createToolDispatcher(toolMap, readOnlyTools) {
|
|
|
186
188
|
const serialized = JSON.stringify(result);
|
|
187
189
|
if (serialized.length > RESULT_PERSIST_THRESHOLD && ctx.artifactDir) {
|
|
188
190
|
const refPath = `${ctx.artifactDir}/tool-results/${eventId}.json`;
|
|
189
|
-
const
|
|
190
|
-
await
|
|
191
|
-
await
|
|
191
|
+
const outputFs = requireOutputFileSystem(ctx.outputFs);
|
|
192
|
+
await outputFs.mkdir(`${ctx.artifactDir}/tool-results`, { recursive: true });
|
|
193
|
+
await outputFs.writeFile(refPath, serialized, "utf-8");
|
|
192
194
|
return {
|
|
193
195
|
_persisted: true,
|
|
194
196
|
_refPath: refPath,
|
|
@@ -224,9 +226,11 @@ export function createAgentContext({
|
|
|
224
226
|
sessionId,
|
|
225
227
|
runId,
|
|
226
228
|
artifactDir,
|
|
229
|
+
outputFs,
|
|
227
230
|
onEvent,
|
|
228
231
|
}) {
|
|
229
|
-
|
|
232
|
+
if (artifactDir || outputFs !== undefined) requireOutputFileSystem(outputFs);
|
|
233
|
+
const context = {
|
|
230
234
|
agentIdentity,
|
|
231
235
|
budget: {
|
|
232
236
|
maxCostUsd: budget?.maxCostUsd ?? 5.0,
|
|
@@ -250,6 +254,7 @@ export function createAgentContext({
|
|
|
250
254
|
lastToolName: null,
|
|
251
255
|
onEvent,
|
|
252
256
|
};
|
|
257
|
+
return outputFs === undefined ? context : bindOutputFileSystem(context, outputFs);
|
|
253
258
|
}
|
|
254
259
|
|
|
255
260
|
function snapshotUsage(ctx) {
|
|
@@ -295,7 +300,7 @@ async function safeAppendEvent(ctx, eventData) {
|
|
|
295
300
|
});
|
|
296
301
|
if (ctx.artifactDir) {
|
|
297
302
|
await appendRunEvent(
|
|
298
|
-
{ targetPath: ctx.artifactDir, outputDir: ctx.artifactDir },
|
|
303
|
+
{ targetPath: ctx.artifactDir, outputDir: ctx.artifactDir, outputFs: ctx.outputFs },
|
|
299
304
|
normalized,
|
|
300
305
|
);
|
|
301
306
|
}
|
package/src/ai/aidenid.js
CHANGED
|
@@ -440,7 +440,9 @@ export async function revokeIdentity({
|
|
|
440
440
|
});
|
|
441
441
|
|
|
442
442
|
const response = await fetchImpl(
|
|
443
|
-
|
|
443
|
+
// AIdenID has no single-identity /revoke route: POST /{identity_id}/squash is the revoke (it records SQUASHED
|
|
444
|
+
// and schedules cleanup). /revoke-children (cascade) is a separate, unchanged route.
|
|
445
|
+
`${normalizedApiUrl}/v1/identities/${urlPathSegment(normalizedIdentityId, { label: "identityId" })}/squash`,
|
|
444
446
|
{
|
|
445
447
|
method: "POST",
|
|
446
448
|
headers: requestHeaders,
|
|
@@ -450,7 +452,7 @@ export async function revokeIdentity({
|
|
|
450
452
|
if (!response.ok) {
|
|
451
453
|
const details = await parseErrorBody(response);
|
|
452
454
|
throw new Error(
|
|
453
|
-
`AIdenID revoke request failed with status ${response.status}${details ? `: ${details}` : ""}`
|
|
455
|
+
`AIdenID squash (revoke) request failed with status ${response.status}${details ? `: ${details}` : ""}`
|
|
454
456
|
);
|
|
455
457
|
}
|
|
456
458
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fsp from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { requireOutputFileSystem } from "../config/output-filesystem.js";
|
|
3
4
|
|
|
4
5
|
const REGISTRY_SCHEMA_VERSION = "1.0.0";
|
|
5
6
|
|
|
@@ -104,15 +105,16 @@ async function loadRegistryInternal(filePath) {
|
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
async function writeRegistryInternal(filePath, registry = {}) {
|
|
108
|
+
async function writeRegistryInternal(filePath, registry = {}, outputFs) {
|
|
109
|
+
requireOutputFileSystem(outputFs);
|
|
108
110
|
const normalized = {
|
|
109
111
|
schemaVersion: REGISTRY_SCHEMA_VERSION,
|
|
110
112
|
generatedAt: new Date().toISOString(),
|
|
111
113
|
domains: Array.isArray(registry.domains) ? registry.domains.map(normalizeDomainRecord) : [],
|
|
112
114
|
targets: Array.isArray(registry.targets) ? registry.targets.map(normalizeTargetRecord) : [],
|
|
113
115
|
};
|
|
114
|
-
await
|
|
115
|
-
await
|
|
116
|
+
await outputFs.mkdir(path.dirname(filePath), { recursive: true });
|
|
117
|
+
await outputFs.writeFile(filePath, `${JSON.stringify(normalized, null, 2)}\n`, "utf-8");
|
|
116
118
|
return normalized;
|
|
117
119
|
}
|
|
118
120
|
|
|
@@ -153,11 +155,13 @@ export async function getTargetById({ outputRoot, targetId } = {}) {
|
|
|
153
155
|
}
|
|
154
156
|
|
|
155
157
|
export async function recordDomainProofResponse({
|
|
158
|
+
outputFs,
|
|
156
159
|
outputRoot,
|
|
157
160
|
domain = {},
|
|
158
161
|
proof = {},
|
|
159
162
|
context = {},
|
|
160
163
|
} = {}) {
|
|
164
|
+
requireOutputFileSystem(outputFs);
|
|
161
165
|
const domainId = normalizeString(domain.id || context.domainId);
|
|
162
166
|
if (!domainId) {
|
|
163
167
|
throw new Error("Cannot record domain without domain.id.");
|
|
@@ -201,7 +205,7 @@ export async function recordDomainProofResponse({
|
|
|
201
205
|
registry.domains.push(nextRecord);
|
|
202
206
|
}
|
|
203
207
|
|
|
204
|
-
const saved = await writeRegistryInternal(registryPath, registry);
|
|
208
|
+
const saved = await writeRegistryInternal(registryPath, registry, outputFs);
|
|
205
209
|
const record = saved.domains.find((item) => item.domainId === domainId) || null;
|
|
206
210
|
return {
|
|
207
211
|
registryPath,
|
|
@@ -210,11 +214,13 @@ export async function recordDomainProofResponse({
|
|
|
210
214
|
}
|
|
211
215
|
|
|
212
216
|
export async function recordTargetProofResponse({
|
|
217
|
+
outputFs,
|
|
213
218
|
outputRoot,
|
|
214
219
|
target = {},
|
|
215
220
|
proof = {},
|
|
216
221
|
context = {},
|
|
217
222
|
} = {}) {
|
|
223
|
+
requireOutputFileSystem(outputFs);
|
|
218
224
|
const targetId = normalizeString(target.id || context.targetId);
|
|
219
225
|
if (!targetId) {
|
|
220
226
|
throw new Error("Cannot record target without target.id.");
|
|
@@ -259,7 +265,7 @@ export async function recordTargetProofResponse({
|
|
|
259
265
|
registry.targets.push(nextRecord);
|
|
260
266
|
}
|
|
261
267
|
|
|
262
|
-
const saved = await writeRegistryInternal(registryPath, registry);
|
|
268
|
+
const saved = await writeRegistryInternal(registryPath, registry, outputFs);
|
|
263
269
|
const record = saved.targets.find((item) => item.targetId === targetId) || null;
|
|
264
270
|
return {
|
|
265
271
|
registryPath,
|
package/src/ai/identity-store.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fsp from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { requireOutputFileSystem } from "../config/output-filesystem.js";
|
|
3
4
|
|
|
4
5
|
const REGISTRY_SCHEMA_VERSION = "1.0.0";
|
|
5
6
|
const TERMINAL_IDENTITY_STATUSES = new Set(["SQUASHED"]);
|
|
@@ -96,14 +97,15 @@ async function loadRegistryInternal(filePath) {
|
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
async function writeRegistryInternal(filePath, registry = {}) {
|
|
100
|
+
async function writeRegistryInternal(filePath, registry = {}, outputFs) {
|
|
101
|
+
requireOutputFileSystem(outputFs);
|
|
100
102
|
const normalized = {
|
|
101
103
|
schemaVersion: REGISTRY_SCHEMA_VERSION,
|
|
102
104
|
generatedAt: new Date().toISOString(),
|
|
103
105
|
identities: Array.isArray(registry.identities) ? registry.identities.map(normalizeIdentityRecord) : [],
|
|
104
106
|
};
|
|
105
|
-
await
|
|
106
|
-
await
|
|
107
|
+
await outputFs.mkdir(path.dirname(filePath), { recursive: true });
|
|
108
|
+
await outputFs.writeFile(filePath, `${JSON.stringify(normalized, null, 2)}\n`, "utf-8");
|
|
107
109
|
return normalized;
|
|
108
110
|
}
|
|
109
111
|
|
|
@@ -130,10 +132,12 @@ export async function getIdentityById({ outputRoot, identityId } = {}) {
|
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
export async function recordProvisionedIdentity({
|
|
135
|
+
outputFs,
|
|
133
136
|
outputRoot,
|
|
134
137
|
response = {},
|
|
135
138
|
context = {},
|
|
136
139
|
} = {}) {
|
|
140
|
+
requireOutputFileSystem(outputFs);
|
|
137
141
|
const registryPath = resolveIdentityRegistryPath({ outputRoot });
|
|
138
142
|
const registry = await loadRegistryInternal(registryPath);
|
|
139
143
|
const identityId = normalizeString(response.id);
|
|
@@ -180,7 +184,7 @@ export async function recordProvisionedIdentity({
|
|
|
180
184
|
registry.identities.push(nextRecord);
|
|
181
185
|
}
|
|
182
186
|
|
|
183
|
-
const saved = await writeRegistryInternal(registryPath, registry);
|
|
187
|
+
const saved = await writeRegistryInternal(registryPath, registry, outputFs);
|
|
184
188
|
const identity = saved.identities.find((item) => item.identityId === identityId) || null;
|
|
185
189
|
return {
|
|
186
190
|
registryPath,
|
|
@@ -189,6 +193,7 @@ export async function recordProvisionedIdentity({
|
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
export async function updateIdentityStatus({
|
|
196
|
+
outputFs,
|
|
192
197
|
outputRoot,
|
|
193
198
|
identityId,
|
|
194
199
|
status,
|
|
@@ -196,6 +201,7 @@ export async function updateIdentityStatus({
|
|
|
196
201
|
squashedAt = "",
|
|
197
202
|
metadataPatch = {},
|
|
198
203
|
} = {}) {
|
|
204
|
+
requireOutputFileSystem(outputFs);
|
|
199
205
|
const normalizedIdentityId = normalizeString(identityId);
|
|
200
206
|
if (!normalizedIdentityId) {
|
|
201
207
|
throw new Error("identityId is required.");
|
|
@@ -226,7 +232,7 @@ export async function updateIdentityStatus({
|
|
|
226
232
|
},
|
|
227
233
|
});
|
|
228
234
|
|
|
229
|
-
const saved = await writeRegistryInternal(registryPath, registry);
|
|
235
|
+
const saved = await writeRegistryInternal(registryPath, registry, outputFs);
|
|
230
236
|
return {
|
|
231
237
|
registryPath,
|
|
232
238
|
identity: saved.identities[index],
|
package/src/ai/site-store.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fsp from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { requireOutputFileSystem } from "../config/output-filesystem.js";
|
|
3
4
|
|
|
4
5
|
const REGISTRY_SCHEMA_VERSION = "1.0.0";
|
|
5
6
|
|
|
@@ -60,14 +61,15 @@ async function loadRegistryInternal(filePath) {
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
async function writeRegistryInternal(filePath, registry = {}) {
|
|
64
|
+
async function writeRegistryInternal(filePath, registry = {}, outputFs) {
|
|
65
|
+
requireOutputFileSystem(outputFs);
|
|
64
66
|
const normalized = {
|
|
65
67
|
schemaVersion: REGISTRY_SCHEMA_VERSION,
|
|
66
68
|
generatedAt: new Date().toISOString(),
|
|
67
69
|
sites: Array.isArray(registry.sites) ? registry.sites.map(normalizeSiteRecord) : [],
|
|
68
70
|
};
|
|
69
|
-
await
|
|
70
|
-
await
|
|
71
|
+
await outputFs.mkdir(path.dirname(filePath), { recursive: true });
|
|
72
|
+
await outputFs.writeFile(filePath, `${JSON.stringify(normalized, null, 2)}\n`, "utf-8");
|
|
71
73
|
return normalized;
|
|
72
74
|
}
|
|
73
75
|
|
|
@@ -85,10 +87,12 @@ export async function listSites({ outputRoot, identityId = "" } = {}) {
|
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
export async function recordTemporarySite({
|
|
90
|
+
outputFs,
|
|
88
91
|
outputRoot,
|
|
89
92
|
site = {},
|
|
90
93
|
context = {},
|
|
91
94
|
} = {}) {
|
|
95
|
+
requireOutputFileSystem(outputFs);
|
|
92
96
|
const siteId = normalizeString(site.id || context.siteId);
|
|
93
97
|
if (!siteId) {
|
|
94
98
|
throw new Error("Cannot record temporary site without site.id.");
|
|
@@ -136,7 +140,7 @@ export async function recordTemporarySite({
|
|
|
136
140
|
registry.sites.push(nextRecord);
|
|
137
141
|
}
|
|
138
142
|
|
|
139
|
-
const saved = await writeRegistryInternal(registryPath, registry);
|
|
143
|
+
const saved = await writeRegistryInternal(registryPath, registry, outputFs);
|
|
140
144
|
const savedSite = saved.sites.find((item) => item.siteId === siteId) || null;
|
|
141
145
|
return {
|
|
142
146
|
registryPath,
|
|
@@ -4,7 +4,8 @@ import path from "node:path";
|
|
|
4
4
|
|
|
5
5
|
import { resolveAgentIdPath } from "../agents/agent-id-path.js";
|
|
6
6
|
import { userCredential } from "../auth/credential-destinations.js";
|
|
7
|
-
import {
|
|
7
|
+
import { resolveOutputContext } from "../config/service.js";
|
|
8
|
+
import { bindOutputFileSystem, requireOutputFileSystem } from "../config/output-filesystem.js";
|
|
8
9
|
import { createAgentEvent } from "../events/schema.js";
|
|
9
10
|
import { resolveCodebaseIngest } from "../ingest/engine.js";
|
|
10
11
|
import { runDeterministicReviewPipeline } from "../review/local-review.js";
|
|
@@ -271,7 +272,9 @@ async function buildSpecialistSeed({
|
|
|
271
272
|
deterministicBaseline,
|
|
272
273
|
ingest,
|
|
273
274
|
agentsDirectory,
|
|
275
|
+
outputFs,
|
|
274
276
|
}) {
|
|
277
|
+
requireOutputFileSystem(outputFs);
|
|
275
278
|
let findings = [];
|
|
276
279
|
let summary = severitySummary(findings);
|
|
277
280
|
let confidence = computeConfidenceFloor(agent.confidenceFloor, findings.length);
|
|
@@ -285,7 +288,7 @@ async function buildSpecialistSeed({
|
|
|
285
288
|
summary = securitySpecialist.summary;
|
|
286
289
|
confidence = securitySpecialist.confidence;
|
|
287
290
|
specialistReportPath = path.join(agentsDirectory, "SECURITY_AGENT_REPORT.md");
|
|
288
|
-
await
|
|
291
|
+
await outputFs.writeFile(
|
|
289
292
|
specialistReportPath,
|
|
290
293
|
`${renderSecuritySpecialistMarkdown(securitySpecialist).trim()}\n`,
|
|
291
294
|
"utf-8"
|
|
@@ -299,7 +302,7 @@ async function buildSpecialistSeed({
|
|
|
299
302
|
summary = architectureSpecialist.summary;
|
|
300
303
|
confidence = architectureSpecialist.confidence;
|
|
301
304
|
specialistReportPath = path.join(agentsDirectory, "ARCHITECTURE_AGENT_REPORT.md");
|
|
302
|
-
await
|
|
305
|
+
await outputFs.writeFile(
|
|
303
306
|
specialistReportPath,
|
|
304
307
|
`${renderArchitectureSpecialistMarkdown(architectureSpecialist).trim()}\n`,
|
|
305
308
|
"utf-8"
|
|
@@ -313,7 +316,7 @@ async function buildSpecialistSeed({
|
|
|
313
316
|
summary = testingSpecialist.summary;
|
|
314
317
|
confidence = testingSpecialist.confidence;
|
|
315
318
|
specialistReportPath = path.join(agentsDirectory, "TESTING_AGENT_REPORT.md");
|
|
316
|
-
await
|
|
319
|
+
await outputFs.writeFile(
|
|
317
320
|
specialistReportPath,
|
|
318
321
|
`${renderTestingSpecialistMarkdown(testingSpecialist).trim()}\n`,
|
|
319
322
|
"utf-8"
|
|
@@ -327,7 +330,7 @@ async function buildSpecialistSeed({
|
|
|
327
330
|
summary = performanceSpecialist.summary;
|
|
328
331
|
confidence = performanceSpecialist.confidence;
|
|
329
332
|
specialistReportPath = path.join(agentsDirectory, "PERFORMANCE_AGENT_REPORT.md");
|
|
330
|
-
await
|
|
333
|
+
await outputFs.writeFile(
|
|
331
334
|
specialistReportPath,
|
|
332
335
|
`${renderPerformanceSpecialistMarkdown(performanceSpecialist).trim()}\n`,
|
|
333
336
|
"utf-8"
|
|
@@ -341,7 +344,7 @@ async function buildSpecialistSeed({
|
|
|
341
344
|
summary = complianceSpecialist.summary;
|
|
342
345
|
confidence = complianceSpecialist.confidence;
|
|
343
346
|
specialistReportPath = path.join(agentsDirectory, "COMPLIANCE_AGENT_REPORT.md");
|
|
344
|
-
await
|
|
347
|
+
await outputFs.writeFile(
|
|
345
348
|
specialistReportPath,
|
|
346
349
|
`${renderComplianceSpecialistMarkdown(complianceSpecialist).trim()}\n`,
|
|
347
350
|
"utf-8"
|
|
@@ -355,7 +358,7 @@ async function buildSpecialistSeed({
|
|
|
355
358
|
summary = documentationSpecialist.summary;
|
|
356
359
|
confidence = documentationSpecialist.confidence;
|
|
357
360
|
specialistReportPath = path.join(agentsDirectory, "DOCUMENTATION_AGENT_REPORT.md");
|
|
358
|
-
await
|
|
361
|
+
await outputFs.writeFile(
|
|
359
362
|
specialistReportPath,
|
|
360
363
|
`${renderDocumentationSpecialistMarkdown(documentationSpecialist).trim()}\n`,
|
|
361
364
|
"utf-8"
|
|
@@ -388,7 +391,7 @@ export async function runAuditOrchestrator({
|
|
|
388
391
|
const isolationMode = normalizeIsolationMode(isolation);
|
|
389
392
|
const useDeterministicSeed = seedFromDeterministic !== false;
|
|
390
393
|
const requestedOmarGateReuse = normalizeString(reuseOmarGate);
|
|
391
|
-
const outputRoot = await
|
|
394
|
+
const { outputRoot, outputFs } = await resolveOutputContext({
|
|
392
395
|
cwd: normalizedTargetPath,
|
|
393
396
|
outputDirOverride: outputDir,
|
|
394
397
|
env: process.env,
|
|
@@ -402,7 +405,7 @@ export async function runAuditOrchestrator({
|
|
|
402
405
|
const agentPaths = new Map(
|
|
403
406
|
agents.map((agent) => [agent, resolveAgentIdPath(agentsDirectory, agent.id, ".json")])
|
|
404
407
|
);
|
|
405
|
-
await
|
|
408
|
+
await outputFs.mkdir(agentsDirectory, { recursive: true });
|
|
406
409
|
const blackboard = createBlackboard({
|
|
407
410
|
runId,
|
|
408
411
|
scope: "audit-orchestrator",
|
|
@@ -655,6 +658,7 @@ export async function runAuditOrchestrator({
|
|
|
655
658
|
deterministicBaseline,
|
|
656
659
|
ingest,
|
|
657
660
|
agentsDirectory,
|
|
661
|
+
outputFs,
|
|
658
662
|
})
|
|
659
663
|
: {
|
|
660
664
|
findings: [],
|
|
@@ -682,6 +686,7 @@ export async function runAuditOrchestrator({
|
|
|
682
686
|
sharedContext,
|
|
683
687
|
hybridContext,
|
|
684
688
|
artifactDir: agentsDirectory,
|
|
689
|
+
outputFs,
|
|
685
690
|
provider,
|
|
686
691
|
onEvent,
|
|
687
692
|
clientFactory,
|
|
@@ -761,7 +766,7 @@ export async function runAuditOrchestrator({
|
|
|
761
766
|
confidence,
|
|
762
767
|
});
|
|
763
768
|
const agentPath = agentPaths.get(agent);
|
|
764
|
-
await
|
|
769
|
+
await outputFs.writeFile(agentPath, `${JSON.stringify(result, null, 2)}\n`, "utf-8");
|
|
765
770
|
emitAuditLifecycleEvent(
|
|
766
771
|
onEvent,
|
|
767
772
|
runId,
|
|
@@ -844,6 +849,7 @@ export async function runAuditOrchestrator({
|
|
|
844
849
|
`Reconcile complete: ${uniqueFindings.length} unique finding(s).`
|
|
845
850
|
);
|
|
846
851
|
const sharedMemoryArtifact = await writeBlackboardArtifact(blackboard, {
|
|
852
|
+
outputFs,
|
|
847
853
|
outputRoot,
|
|
848
854
|
});
|
|
849
855
|
const sharedMemorySummary = summarizeBlackboard(blackboard);
|
|
@@ -907,9 +913,10 @@ export async function runAuditOrchestrator({
|
|
|
907
913
|
|
|
908
914
|
const reportJsonPath = path.join(runDirectory, "AUDIT_REPORT.json");
|
|
909
915
|
const reportMarkdownPath = path.join(runDirectory, "AUDIT_REPORT.md");
|
|
910
|
-
await
|
|
911
|
-
await
|
|
916
|
+
await outputFs.writeFile(reportJsonPath, `${JSON.stringify(report, null, 2)}\n`, "utf-8");
|
|
917
|
+
await outputFs.writeFile(reportMarkdownPath, `${buildAuditMarkdown(report).trim()}\n`, "utf-8");
|
|
912
918
|
const ddPackage = await writeDdPackage({
|
|
919
|
+
outputFs,
|
|
913
920
|
report,
|
|
914
921
|
runDirectory,
|
|
915
922
|
});
|
|
@@ -930,11 +937,11 @@ export async function runAuditOrchestrator({
|
|
|
930
937
|
`Audit orchestrator complete: P0=${summary.P0} P1=${summary.P1} P2=${summary.P2} P3=${summary.P3}.`
|
|
931
938
|
);
|
|
932
939
|
|
|
933
|
-
return {
|
|
940
|
+
return bindOutputFileSystem({
|
|
934
941
|
...report,
|
|
935
942
|
reportJsonPath,
|
|
936
943
|
reportMarkdownPath,
|
|
937
944
|
ddPackage,
|
|
938
|
-
};
|
|
945
|
+
}, outputFs);
|
|
939
946
|
}
|
|
940
947
|
|
package/src/audit/package.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { requireOutputFileSystem } from "../config/output-filesystem.js";
|
|
1
2
|
import fsp from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
|
|
@@ -116,7 +117,8 @@ export function buildDdPackageManifest(report = {}) {
|
|
|
116
117
|
};
|
|
117
118
|
}
|
|
118
119
|
|
|
119
|
-
export async function writeDdPackage({ report = {}, runDirectory } = {}) {
|
|
120
|
+
export async function writeDdPackage({ report = {}, runDirectory, outputFs } = {}) {
|
|
121
|
+
requireOutputFileSystem(outputFs);
|
|
120
122
|
const resolvedRunDirectory = normalizeRunDirectory(runDirectory || report.runDirectory);
|
|
121
123
|
const manifest = buildDdPackageManifest({
|
|
122
124
|
...report,
|
|
@@ -138,9 +140,9 @@ export async function writeDdPackage({ report = {}, runDirectory } = {}) {
|
|
|
138
140
|
findingsIndex,
|
|
139
141
|
});
|
|
140
142
|
|
|
141
|
-
await
|
|
142
|
-
await
|
|
143
|
-
await
|
|
143
|
+
await outputFs.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf-8");
|
|
144
|
+
await outputFs.writeFile(findingsIndexPath, `${JSON.stringify(findingsIndex, null, 2)}\n`, "utf-8");
|
|
145
|
+
await outputFs.writeFile(executiveSummaryPath, `${executiveSummary.trim()}\n`, "utf-8");
|
|
144
146
|
|
|
145
147
|
return {
|
|
146
148
|
manifest,
|