@praneeth_54/agentdoctor 2.0.0 → 2.0.1
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/CHANGELOG.md +73 -4
- package/README.md +283 -158
- package/dist/architecture/contract.d.ts +64 -0
- package/dist/architecture/contract.js +373 -0
- package/dist/assurance/change.d.ts +211 -0
- package/dist/assurance/change.js +624 -0
- package/dist/assurance/proof.d.ts +136 -0
- package/dist/assurance/proof.js +338 -0
- package/dist/auth/index.d.ts +81 -0
- package/dist/auth/index.js +130 -0
- package/dist/auth/rbac.d.ts +9 -0
- package/dist/auth/rbac.js +26 -0
- package/dist/cli/commands/architecture.d.ts +6 -0
- package/dist/cli/commands/architecture.js +109 -0
- package/dist/cli/commands/assurance.d.ts +67 -0
- package/dist/cli/commands/assurance.js +210 -0
- package/dist/cli/commands/complete.js +2 -2
- package/dist/cli/commands/platform.d.ts +2 -0
- package/dist/cli/commands/platform.js +5 -1
- package/dist/cli/commands/policy-graph-run.d.ts +40 -0
- package/dist/cli/commands/policy-graph-run.js +200 -0
- package/dist/cli/commands/workspace.d.ts +9 -0
- package/dist/cli/commands/workspace.js +82 -0
- package/dist/cli/program.js +535 -17
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/index.js +2 -2
- package/dist/core/brain-product/init.js +5 -2
- package/dist/core/secrets/scan.d.ts +7 -1
- package/dist/core/secrets/scan.js +9 -9
- package/dist/coverage/cobertura.d.ts +6 -0
- package/dist/coverage/cobertura.js +52 -0
- package/dist/coverage/istanbul.d.ts +7 -0
- package/dist/coverage/istanbul.js +134 -0
- package/dist/coverage/lcov.d.ts +7 -0
- package/dist/coverage/lcov.js +59 -0
- package/dist/coverage/load.d.ts +9 -0
- package/dist/coverage/load.js +39 -0
- package/dist/coverage/types.d.ts +23 -0
- package/dist/coverage/types.js +5 -0
- package/dist/dashboard/server.d.ts +1 -1
- package/dist/dashboard/server.js +21 -5
- package/dist/enforcement/runner.d.ts +47 -3
- package/dist/enforcement/runner.js +420 -16
- package/dist/index.d.ts +23 -2
- package/dist/index.js +15 -2
- package/dist/intelligence/graph/build.d.ts +2 -0
- package/dist/intelligence/graph/build.js +49 -5
- package/dist/intelligence/graph/incremental.d.ts +68 -0
- package/dist/intelligence/graph/incremental.js +234 -0
- package/dist/intelligence/resolve/imports.d.ts +36 -0
- package/dist/intelligence/resolve/imports.js +245 -0
- package/dist/languages/go.d.ts +9 -0
- package/dist/languages/go.js +45 -0
- package/dist/languages/index.d.ts +11 -0
- package/dist/languages/index.js +76 -0
- package/dist/languages/php.d.ts +7 -0
- package/dist/languages/php.js +193 -0
- package/dist/languages/python.d.ts +7 -0
- package/dist/languages/python.js +150 -0
- package/dist/languages/types.d.ts +42 -0
- package/dist/languages/types.js +26 -0
- package/dist/languages/typescript.d.ts +3 -0
- package/dist/languages/typescript.js +95 -0
- package/dist/mcp/intelligence/handlers.d.ts +5 -0
- package/dist/mcp/intelligence/handlers.js +229 -40
- package/dist/mcp/intelligence/path-safety.d.ts +2 -4
- package/dist/mcp/intelligence/path-safety.js +25 -44
- package/dist/mcp/intelligence/registry.d.ts +1 -1
- package/dist/mcp/intelligence/registry.js +69 -1
- package/dist/platform/firewall/evaluate.d.ts +6 -3
- package/dist/platform/firewall/evaluate.js +66 -15
- package/dist/platform/graph/build.js +19 -23
- package/dist/platform/health/analyze.js +23 -17
- package/dist/platform/index.js +2 -0
- package/dist/platform/test-impact/analyze.d.ts +21 -3
- package/dist/platform/test-impact/analyze.js +214 -28
- package/dist/platform/tokens/plan.js +39 -20
- package/dist/platform/types.d.ts +9 -0
- package/dist/policy/compose.d.ts +34 -0
- package/dist/policy/compose.js +118 -0
- package/dist/policy/packs.js +33 -1
- package/dist/security/paths.d.ts +21 -0
- package/dist/security/paths.js +105 -0
- package/dist/storage/postgres.d.ts +26 -0
- package/dist/storage/postgres.js +90 -0
- package/dist/storage/provider.d.ts +2 -7
- package/dist/storage/provider.js +22 -16
- package/dist/storage/sqlite.d.ts +33 -0
- package/dist/storage/sqlite.js +77 -0
- package/dist/team/auth.js +1 -1
- package/dist/workspace/index.d.ts +69 -0
- package/dist/workspace/index.js +220 -0
- package/package.json +9 -3
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { evaluateAgentAction, EVALUATE_ONLY } from "../../platform/firewall/evaluate.js";
|
|
2
|
+
import { runControlledCommand } from "../../enforcement/runner.js";
|
|
3
|
+
import { graphBuild, graphRebuild, graphStatus, graphUpdate, } from "../../intelligence/graph/incremental.js";
|
|
4
|
+
import { buildIntelligenceGraph } from "../../intelligence/graph/build.js";
|
|
5
|
+
import { EXIT_CODES } from "../../types/index.js";
|
|
6
|
+
function emit(json, value, human) {
|
|
7
|
+
if (json) {
|
|
8
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
process.stdout.write(`${human}\n`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export async function runGraphSurfaceCommand(options) {
|
|
15
|
+
try {
|
|
16
|
+
const mode = options.mode ?? "auto";
|
|
17
|
+
if (options.action === "status") {
|
|
18
|
+
const status = await graphStatus({ root: options.root });
|
|
19
|
+
emit(Boolean(options.json), status, [
|
|
20
|
+
`Graph index ${status.present ? "present" : "missing"}`,
|
|
21
|
+
` path: ${status.path}`,
|
|
22
|
+
` nodes=${status.nodeCount} edges=${status.edgeCount} files=${status.fileCount}`,
|
|
23
|
+
` builder=${status.builder ?? "n/a"}`,
|
|
24
|
+
` stale=${status.staleFiles.length} missing=${status.missingFiles.length}`,
|
|
25
|
+
status.corrupt ? ` corrupt: ${status.error}` : "",
|
|
26
|
+
]
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
.join("\n"));
|
|
29
|
+
return EXIT_CODES.SUCCESS;
|
|
30
|
+
}
|
|
31
|
+
if (options.action === "update") {
|
|
32
|
+
const result = await graphUpdate({ root: options.root, mode });
|
|
33
|
+
emit(Boolean(options.json), result, `graph update rebuilt=${result.rebuilt} reason=${result.reason} changed=${result.changedFiles.length} nodes=${result.snapshot.nodes.length}`);
|
|
34
|
+
return EXIT_CODES.SUCCESS;
|
|
35
|
+
}
|
|
36
|
+
if (options.action === "rebuild") {
|
|
37
|
+
const result = await graphRebuild({ root: options.root, mode });
|
|
38
|
+
emit(Boolean(options.json), result, `graph rebuild nodes=${result.snapshot.nodes.length} edges=${result.snapshot.edges.length} builder=${result.snapshot.builder}`);
|
|
39
|
+
return EXIT_CODES.SUCCESS;
|
|
40
|
+
}
|
|
41
|
+
// build / show (backward-compat one-shot)
|
|
42
|
+
if (options.action === "show") {
|
|
43
|
+
const graph = await buildIntelligenceGraph({ root: options.root, mode });
|
|
44
|
+
emit(Boolean(options.json), graph, `graph builder=${graph.builder} nodes=${graph.nodes.length} edges=${graph.edges.length} astFiles=${graph.astFilesParsed}`);
|
|
45
|
+
return EXIT_CODES.SUCCESS;
|
|
46
|
+
}
|
|
47
|
+
const result = await graphBuild({ root: options.root, mode });
|
|
48
|
+
emit(Boolean(options.json), result, `graph build nodes=${result.snapshot.nodes.length} edges=${result.snapshot.edges.length} builder=${result.snapshot.builder} path=${result.path}`);
|
|
49
|
+
return EXIT_CODES.SUCCESS;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
53
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
54
|
+
return EXIT_CODES.INTERNAL_ERROR;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export async function runPolicyCheckCommand(options) {
|
|
58
|
+
try {
|
|
59
|
+
const verdict = await evaluateAgentAction(options.root, {
|
|
60
|
+
actionId: `policy-check-${Date.now()}`,
|
|
61
|
+
agentId: "cli",
|
|
62
|
+
timestamp: new Date().toISOString(),
|
|
63
|
+
type: "shell",
|
|
64
|
+
params: { command: options.command },
|
|
65
|
+
repositoryRoot: options.root,
|
|
66
|
+
}, { failClosed: options.failClosed === true });
|
|
67
|
+
emit(Boolean(options.json), { ...verdict, notice: EVALUATE_ONLY }, `policy check decision=${verdict.decision} risk=${verdict.riskLevel}\n ${verdict.reason}\n ${EVALUATE_ONLY}`);
|
|
68
|
+
return EXIT_CODES.SUCCESS;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
72
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
73
|
+
return EXIT_CODES.INTERNAL_ERROR;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export async function runPolicyExplainCommand(options) {
|
|
77
|
+
try {
|
|
78
|
+
const verdict = await evaluateAgentAction(options.root, {
|
|
79
|
+
actionId: `policy-explain-${Date.now()}`,
|
|
80
|
+
agentId: "cli",
|
|
81
|
+
timestamp: new Date().toISOString(),
|
|
82
|
+
type: "shell",
|
|
83
|
+
params: { command: options.command },
|
|
84
|
+
repositoryRoot: options.root,
|
|
85
|
+
}, { failClosed: options.failClosed === true });
|
|
86
|
+
const explanation = {
|
|
87
|
+
command: options.command,
|
|
88
|
+
decision: verdict.decision,
|
|
89
|
+
reason: verdict.reason,
|
|
90
|
+
policyId: verdict.policyId ?? null,
|
|
91
|
+
riskLevel: verdict.riskLevel,
|
|
92
|
+
approvalStatus: verdict.approvalStatus,
|
|
93
|
+
executionResult: verdict.executionResult,
|
|
94
|
+
notice: EVALUATE_ONLY,
|
|
95
|
+
meaning: verdict.decision === "allow"
|
|
96
|
+
? "Allowlisted or matched an allow rule — still evaluate-only unless agentdoctor run / enforce --execute is used."
|
|
97
|
+
: verdict.decision === "block" || verdict.decision === "deny-network"
|
|
98
|
+
? "Would be blocked by AgentDoctor-controlled runner if executed through it."
|
|
99
|
+
: "Requires human approval or further policy before controlled execution.",
|
|
100
|
+
};
|
|
101
|
+
emit(Boolean(options.json), explanation, [
|
|
102
|
+
`policy explain: ${options.command}`,
|
|
103
|
+
` decision: ${verdict.decision}`,
|
|
104
|
+
` policyId: ${verdict.policyId ?? "n/a"}`,
|
|
105
|
+
` risk: ${verdict.riskLevel}`,
|
|
106
|
+
` reason: ${verdict.reason}`,
|
|
107
|
+
` ${explanation.meaning}`,
|
|
108
|
+
].join("\n"));
|
|
109
|
+
return EXIT_CODES.SUCCESS;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
113
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
114
|
+
return EXIT_CODES.INTERNAL_ERROR;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export async function runPolicyEnforceCommand(options) {
|
|
118
|
+
try {
|
|
119
|
+
const result = await runControlledCommand({
|
|
120
|
+
root: options.root,
|
|
121
|
+
command: options.command,
|
|
122
|
+
executeIfAllowed: options.execute === true,
|
|
123
|
+
failClosed: options.failClosed === true,
|
|
124
|
+
});
|
|
125
|
+
emit(Boolean(options.json), result, [
|
|
126
|
+
`policy enforce decision=${result.decision.decision} status=${result.decision.executionStatus}`,
|
|
127
|
+
` enforced=${result.enforced}`,
|
|
128
|
+
` ${result.notice}`,
|
|
129
|
+
].join("\n"));
|
|
130
|
+
return EXIT_CODES.SUCCESS;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
134
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
135
|
+
return EXIT_CODES.INTERNAL_ERROR;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export async function runRunExplainCommand(options) {
|
|
139
|
+
try {
|
|
140
|
+
const { explainControlledRun } = await import("../../enforcement/runner.js");
|
|
141
|
+
const explanation = await explainControlledRun({
|
|
142
|
+
root: options.root,
|
|
143
|
+
command: options.command,
|
|
144
|
+
failClosed: options.failClosed === true,
|
|
145
|
+
useShell: options.shell === true,
|
|
146
|
+
});
|
|
147
|
+
emit(Boolean(options.json), explanation, [
|
|
148
|
+
`run explain: ${explanation.command}`,
|
|
149
|
+
` decision: ${explanation.decision}`,
|
|
150
|
+
` policyId: ${explanation.policyId ?? "n/a"}`,
|
|
151
|
+
` risk: ${explanation.riskLevel}`,
|
|
152
|
+
` wouldExecute: ${explanation.wouldExecute}`,
|
|
153
|
+
` ${explanation.meaning}`,
|
|
154
|
+
` ${explanation.notice}`,
|
|
155
|
+
].join("\n"));
|
|
156
|
+
return EXIT_CODES.SUCCESS;
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
160
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
161
|
+
return EXIT_CODES.INTERNAL_ERROR;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
export async function runRunCommand(options) {
|
|
165
|
+
try {
|
|
166
|
+
if (!options.argv.length) {
|
|
167
|
+
process.stderr.write("Error: provide a command after -- (e.g. agentdoctor run -- npm --version)\n");
|
|
168
|
+
return EXIT_CODES.USAGE_ERROR;
|
|
169
|
+
}
|
|
170
|
+
const result = await runControlledCommand({
|
|
171
|
+
root: options.root,
|
|
172
|
+
argv: options.argv,
|
|
173
|
+
executeIfAllowed: true,
|
|
174
|
+
useShell: options.shell === true,
|
|
175
|
+
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
|
176
|
+
failClosed: options.failClosed === true,
|
|
177
|
+
});
|
|
178
|
+
emit(Boolean(options.json), result, [
|
|
179
|
+
`run decision=${result.decision.decision} status=${result.decision.executionStatus}`,
|
|
180
|
+
` exitCode=${result.exitCode ?? "n/a"} durationMs=${result.durationMs ?? "n/a"}`,
|
|
181
|
+
` ${result.notice}`,
|
|
182
|
+
result.stdout ? `--- stdout ---\n${result.stdout}` : "",
|
|
183
|
+
result.stderr ? `--- stderr ---\n${result.stderr}` : "",
|
|
184
|
+
]
|
|
185
|
+
.filter(Boolean)
|
|
186
|
+
.join("\n"));
|
|
187
|
+
if (result.decision.executionStatus === "blocked-by-enforcement") {
|
|
188
|
+
return EXIT_CODES.ISSUES_OR_THRESHOLD;
|
|
189
|
+
}
|
|
190
|
+
if (result.decision.executionStatus === "execution-failed") {
|
|
191
|
+
return EXIT_CODES.INTERNAL_ERROR;
|
|
192
|
+
}
|
|
193
|
+
return EXIT_CODES.SUCCESS;
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
197
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
198
|
+
return EXIT_CODES.INTERNAL_ERROR;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { EXIT_CODES } from "../../types/index.js";
|
|
2
|
+
import { addRepositoryToWorkspace, initWorkspace, listWorkspaces, removeWorkspace, workspaceStatus, } from "../../workspace/index.js";
|
|
3
|
+
function emit(json, value, human) {
|
|
4
|
+
if (json) {
|
|
5
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
6
|
+
}
|
|
7
|
+
else {
|
|
8
|
+
process.stdout.write(`${human}\n`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export async function runWorkspaceCommand(options) {
|
|
12
|
+
try {
|
|
13
|
+
const controlRoot = options.root;
|
|
14
|
+
switch (options.action) {
|
|
15
|
+
case "init": {
|
|
16
|
+
if (!options.name?.trim()) {
|
|
17
|
+
process.stderr.write("Error: workspace init requires --name\n");
|
|
18
|
+
return EXIT_CODES.USAGE_ERROR;
|
|
19
|
+
}
|
|
20
|
+
const ws = await initWorkspace({
|
|
21
|
+
controlRoot,
|
|
22
|
+
name: options.name,
|
|
23
|
+
...(options.id ? { id: options.id } : {}),
|
|
24
|
+
...(options.repositoryRoot ? { repositoryRoot: options.repositoryRoot } : {}),
|
|
25
|
+
allowCrossRead: options.allowCrossRead === true,
|
|
26
|
+
});
|
|
27
|
+
emit(Boolean(options.json), ws, `workspace init id=${ws.id} name=${ws.name} roots=${ws.repositoryRoots.length} allowCrossRead=${ws.allowCrossRead}`);
|
|
28
|
+
return EXIT_CODES.SUCCESS;
|
|
29
|
+
}
|
|
30
|
+
case "add": {
|
|
31
|
+
if (!options.id?.trim() || !options.repositoryRoot?.trim()) {
|
|
32
|
+
process.stderr.write("Error: workspace add requires --id and --repo\n");
|
|
33
|
+
return EXIT_CODES.USAGE_ERROR;
|
|
34
|
+
}
|
|
35
|
+
const ws = await addRepositoryToWorkspace({
|
|
36
|
+
controlRoot,
|
|
37
|
+
workspaceId: options.id,
|
|
38
|
+
repositoryRoot: options.repositoryRoot,
|
|
39
|
+
});
|
|
40
|
+
emit(Boolean(options.json), ws, `workspace add id=${ws.id} roots=${ws.repositoryRoots.join(", ")}`);
|
|
41
|
+
return EXIT_CODES.SUCCESS;
|
|
42
|
+
}
|
|
43
|
+
case "list": {
|
|
44
|
+
const list = await listWorkspaces(controlRoot);
|
|
45
|
+
emit(Boolean(options.json), { workspaces: list }, list.length === 0
|
|
46
|
+
? "No workspaces under .agentdoctor/workspaces/"
|
|
47
|
+
: list
|
|
48
|
+
.map((w) => `${w.id}\t${w.name}\troots=${w.repositoryRoots.length}\tcrossRead=${w.allowCrossRead}`)
|
|
49
|
+
.join("\n"));
|
|
50
|
+
return EXIT_CODES.SUCCESS;
|
|
51
|
+
}
|
|
52
|
+
case "status": {
|
|
53
|
+
if (!options.id?.trim()) {
|
|
54
|
+
process.stderr.write("Error: workspace status requires --id\n");
|
|
55
|
+
return EXIT_CODES.USAGE_ERROR;
|
|
56
|
+
}
|
|
57
|
+
const status = await workspaceStatus(controlRoot, options.id);
|
|
58
|
+
emit(Boolean(options.json), status, status.ok
|
|
59
|
+
? `workspace ${options.id} ok roots=${status.workspace?.repositoryRoots.length ?? 0}`
|
|
60
|
+
: `workspace ${options.id} ${status.error ?? "degraded"} missing=${status.missingRoots.join(", ") || "none"}`);
|
|
61
|
+
return status.ok ? EXIT_CODES.SUCCESS : EXIT_CODES.ISSUES_OR_THRESHOLD;
|
|
62
|
+
}
|
|
63
|
+
case "remove": {
|
|
64
|
+
if (!options.id?.trim()) {
|
|
65
|
+
process.stderr.write("Error: workspace remove requires --id\n");
|
|
66
|
+
return EXIT_CODES.USAGE_ERROR;
|
|
67
|
+
}
|
|
68
|
+
const removed = await removeWorkspace(controlRoot, options.id);
|
|
69
|
+
emit(Boolean(options.json), { removed, id: options.id }, removed ? `workspace removed ${options.id}` : `workspace not found ${options.id}`);
|
|
70
|
+
return removed ? EXIT_CODES.SUCCESS : EXIT_CODES.ISSUES_OR_THRESHOLD;
|
|
71
|
+
}
|
|
72
|
+
default:
|
|
73
|
+
process.stderr.write("Error: unknown workspace action\n");
|
|
74
|
+
return EXIT_CODES.USAGE_ERROR;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
79
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
80
|
+
return EXIT_CODES.INTERNAL_ERROR;
|
|
81
|
+
}
|
|
82
|
+
}
|