agent-inspect 6.0.0 → 6.3.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/CHANGELOG.md +18 -0
- package/package.json +1 -1
- package/packages/cli/dist/index.cjs +491 -89
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +452 -50
- package/packages/cli/dist/index.mjs.map +1 -1
|
@@ -11,7 +11,7 @@ import { createServer } from 'http';
|
|
|
11
11
|
import { createRequire } from 'module';
|
|
12
12
|
|
|
13
13
|
// package.json
|
|
14
|
-
var version = "6.
|
|
14
|
+
var version = "6.3.0";
|
|
15
15
|
|
|
16
16
|
// packages/cli/src/trace-dir-scale.ts
|
|
17
17
|
var TRACE_COUNT_WARN = 1e3;
|
|
@@ -3129,13 +3129,13 @@ function pairSteps(left, right) {
|
|
|
3129
3129
|
return pairs;
|
|
3130
3130
|
}
|
|
3131
3131
|
function compareLeafSteps(L, R, segments, opts, out) {
|
|
3132
|
-
const
|
|
3132
|
+
const path25 = buildPath(segments);
|
|
3133
3133
|
if (L.name !== R.name) {
|
|
3134
3134
|
out.push({
|
|
3135
3135
|
kind: "structure",
|
|
3136
3136
|
severity: "warning",
|
|
3137
3137
|
message: "Step name differs",
|
|
3138
|
-
path:
|
|
3138
|
+
path: path25,
|
|
3139
3139
|
left: L.name,
|
|
3140
3140
|
right: R.name
|
|
3141
3141
|
});
|
|
@@ -3145,7 +3145,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
3145
3145
|
kind: "step-type",
|
|
3146
3146
|
severity: "warning",
|
|
3147
3147
|
message: "Step type differs",
|
|
3148
|
-
path:
|
|
3148
|
+
path: path25,
|
|
3149
3149
|
left: L.type,
|
|
3150
3150
|
right: R.type
|
|
3151
3151
|
});
|
|
@@ -3155,7 +3155,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
3155
3155
|
kind: "step-status",
|
|
3156
3156
|
severity: "warning",
|
|
3157
3157
|
message: "Step status differs",
|
|
3158
|
-
path:
|
|
3158
|
+
path: path25,
|
|
3159
3159
|
left: L.status,
|
|
3160
3160
|
right: R.status
|
|
3161
3161
|
});
|
|
@@ -3167,7 +3167,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
3167
3167
|
kind: "error",
|
|
3168
3168
|
severity: "error",
|
|
3169
3169
|
message: "Step error message differs",
|
|
3170
|
-
path:
|
|
3170
|
+
path: path25,
|
|
3171
3171
|
left: le || void 0,
|
|
3172
3172
|
right: re || void 0
|
|
3173
3173
|
});
|
|
@@ -3185,7 +3185,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
3185
3185
|
kind: "duration",
|
|
3186
3186
|
severity: "info",
|
|
3187
3187
|
message: "Step duration differs",
|
|
3188
|
-
path:
|
|
3188
|
+
path: path25,
|
|
3189
3189
|
left: ld,
|
|
3190
3190
|
right: rd
|
|
3191
3191
|
});
|
|
@@ -3198,7 +3198,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
3198
3198
|
kind: "metadata",
|
|
3199
3199
|
severity: "info",
|
|
3200
3200
|
message: "Step metadata differs",
|
|
3201
|
-
path:
|
|
3201
|
+
path: path25,
|
|
3202
3202
|
left: L.metadata,
|
|
3203
3203
|
right: R.metadata
|
|
3204
3204
|
});
|
|
@@ -3210,7 +3210,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
3210
3210
|
kind: "output",
|
|
3211
3211
|
severity: "info",
|
|
3212
3212
|
message: "Output preview differs",
|
|
3213
|
-
path:
|
|
3213
|
+
path: path25,
|
|
3214
3214
|
left: L.outputPreview,
|
|
3215
3215
|
right: R.outputPreview
|
|
3216
3216
|
});
|
|
@@ -3370,11 +3370,11 @@ function diffRuns(left, right, options) {
|
|
|
3370
3370
|
}
|
|
3371
3371
|
|
|
3372
3372
|
// packages/core/src/diff/renderer.ts
|
|
3373
|
-
function formatPath(
|
|
3374
|
-
if (
|
|
3373
|
+
function formatPath(path25) {
|
|
3374
|
+
if (path25 === void 0 || path25.path.length === 0) {
|
|
3375
3375
|
return "(run)";
|
|
3376
3376
|
}
|
|
3377
|
-
return
|
|
3377
|
+
return path25.path.map((s) => s.name).join(" > ");
|
|
3378
3378
|
}
|
|
3379
3379
|
function formatValue(v, verbose) {
|
|
3380
3380
|
if (v === void 0) return "(undefined)";
|
|
@@ -4479,17 +4479,17 @@ function applyRule(rule, value, replacement) {
|
|
|
4479
4479
|
}
|
|
4480
4480
|
return value;
|
|
4481
4481
|
}
|
|
4482
|
-
function childPath(
|
|
4482
|
+
function childPath(path25, key) {
|
|
4483
4483
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
4484
|
-
return
|
|
4484
|
+
return path25 ? `${path25}.${key}` : key;
|
|
4485
4485
|
}
|
|
4486
|
-
return `${
|
|
4486
|
+
return `${path25 || "$"}[${JSON.stringify(key)}]`;
|
|
4487
4487
|
}
|
|
4488
|
-
function indexPath(
|
|
4489
|
-
return `${
|
|
4488
|
+
function indexPath(path25, index) {
|
|
4489
|
+
return `${path25 || "$"}[${index}]`;
|
|
4490
4490
|
}
|
|
4491
|
-
function makeFinding(
|
|
4492
|
-
return preview === void 0 ? { path:
|
|
4491
|
+
function makeFinding(path25, detector, action, matchKind, severity = "warning", preview) {
|
|
4492
|
+
return preview === void 0 ? { path: path25, detector, action, severity, matchKind } : { path: path25, detector, action, severity, matchKind, preview };
|
|
4493
4493
|
}
|
|
4494
4494
|
function createRedactionProfile(profile = "local") {
|
|
4495
4495
|
switch (profile) {
|
|
@@ -4558,11 +4558,11 @@ var Redactor2 = class {
|
|
|
4558
4558
|
#recordFinding(state, finding) {
|
|
4559
4559
|
if (this.#collectFindings) state.findings.push(finding);
|
|
4560
4560
|
}
|
|
4561
|
-
#redactValue(value, key,
|
|
4561
|
+
#redactValue(value, key, path25, depth, state) {
|
|
4562
4562
|
if (depth > this.#maxDepth) {
|
|
4563
4563
|
this.#recordFinding(
|
|
4564
4564
|
state,
|
|
4565
|
-
makeFinding(
|
|
4565
|
+
makeFinding(path25, "structure.maxDepth", "truncate", "value", "warning")
|
|
4566
4566
|
);
|
|
4567
4567
|
return "[Truncated]";
|
|
4568
4568
|
}
|
|
@@ -4571,19 +4571,19 @@ var Redactor2 = class {
|
|
|
4571
4571
|
if (rule) {
|
|
4572
4572
|
this.#recordFinding(
|
|
4573
4573
|
state,
|
|
4574
|
-
makeFinding(
|
|
4574
|
+
makeFinding(path25, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|
|
4575
4575
|
);
|
|
4576
4576
|
return applyRule(rule, value, this.#replacement);
|
|
4577
4577
|
}
|
|
4578
4578
|
}
|
|
4579
4579
|
for (const detector of this.#detectors) {
|
|
4580
|
-
const detections = detector.detect({ path:
|
|
4580
|
+
const detections = detector.detect({ path: path25, key, value });
|
|
4581
4581
|
for (const detection of detections) {
|
|
4582
4582
|
const action = detection.action ?? "replace";
|
|
4583
4583
|
this.#recordFinding(
|
|
4584
4584
|
state,
|
|
4585
4585
|
makeFinding(
|
|
4586
|
-
|
|
4586
|
+
path25,
|
|
4587
4587
|
detector.id,
|
|
4588
4588
|
action,
|
|
4589
4589
|
detection.matchKind ?? detector.matchKind ?? "custom",
|
|
@@ -4601,7 +4601,7 @@ var Redactor2 = class {
|
|
|
4601
4601
|
const out = [];
|
|
4602
4602
|
state.seen.set(value, out);
|
|
4603
4603
|
value.forEach((item, index) => {
|
|
4604
|
-
out[index] = this.#redactValue(item, void 0, indexPath(
|
|
4604
|
+
out[index] = this.#redactValue(item, void 0, indexPath(path25, index), depth + 1, state);
|
|
4605
4605
|
});
|
|
4606
4606
|
return out;
|
|
4607
4607
|
}
|
|
@@ -4613,7 +4613,7 @@ var Redactor2 = class {
|
|
|
4613
4613
|
out[entryKey] = this.#redactValue(
|
|
4614
4614
|
entryValue,
|
|
4615
4615
|
entryKey,
|
|
4616
|
-
childPath(
|
|
4616
|
+
childPath(path25 === "$" ? "" : path25, entryKey),
|
|
4617
4617
|
depth + 1,
|
|
4618
4618
|
state
|
|
4619
4619
|
);
|
|
@@ -5540,23 +5540,23 @@ function evaluatePromptInjection(text, options = {}) {
|
|
|
5540
5540
|
}
|
|
5541
5541
|
return fail(ruleId, `Matched ${evidence.length} injection pattern(s).`, evidence, "warning");
|
|
5542
5542
|
}
|
|
5543
|
-
function validateSchemaField(value, field,
|
|
5543
|
+
function validateSchemaField(value, field, path25, evidence) {
|
|
5544
5544
|
const ruleId = "guardrail.structured-output";
|
|
5545
5545
|
if (field.type) {
|
|
5546
5546
|
const actual = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
5547
5547
|
if (actual !== field.type) {
|
|
5548
|
-
evidence.push({ ruleId, path:
|
|
5548
|
+
evidence.push({ ruleId, path: path25, preview: `expected ${field.type}, got ${actual}` });
|
|
5549
5549
|
return;
|
|
5550
5550
|
}
|
|
5551
5551
|
}
|
|
5552
5552
|
if (field.enum && !field.enum.some((item) => Object.is(item, value))) {
|
|
5553
|
-
evidence.push({ ruleId, path:
|
|
5553
|
+
evidence.push({ ruleId, path: path25, preview: "value not in enum" });
|
|
5554
5554
|
}
|
|
5555
5555
|
if (field.type === "object" && field.required && value && typeof value === "object" && !Array.isArray(value)) {
|
|
5556
5556
|
const record = value;
|
|
5557
5557
|
for (const key of field.required) {
|
|
5558
5558
|
if (!(key in record)) {
|
|
5559
|
-
evidence.push({ ruleId, path: `${
|
|
5559
|
+
evidence.push({ ruleId, path: `${path25}.${key}`, preview: "missing required key" });
|
|
5560
5560
|
}
|
|
5561
5561
|
}
|
|
5562
5562
|
}
|
|
@@ -6057,10 +6057,10 @@ function printHuman(result) {
|
|
|
6057
6057
|
console.log(`- ${diagnostic3.code}: ${diagnostic3.message}`);
|
|
6058
6058
|
}
|
|
6059
6059
|
for (const finding of result.findings) {
|
|
6060
|
-
const
|
|
6060
|
+
const path25 = finding.evidence[0]?.path;
|
|
6061
6061
|
const run = finding.evidence[0]?.runId;
|
|
6062
6062
|
const runPrefix = run ? `[${run}] ` : "";
|
|
6063
|
-
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${
|
|
6063
|
+
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path25 ? ` (${path25})` : ""}`);
|
|
6064
6064
|
}
|
|
6065
6065
|
}
|
|
6066
6066
|
function readErrorResult(error) {
|
|
@@ -7105,9 +7105,103 @@ function parsePort2(value) {
|
|
|
7105
7105
|
}
|
|
7106
7106
|
return parsed;
|
|
7107
7107
|
}
|
|
7108
|
+
function summarizeFileDropResult(result) {
|
|
7109
|
+
if (result.skipped) {
|
|
7110
|
+
console.log(`[AgentInspect studio] file-drop skipped: ${result.reason ?? "disabled"}`);
|
|
7111
|
+
return;
|
|
7112
|
+
}
|
|
7113
|
+
console.log(
|
|
7114
|
+
`[AgentInspect studio] file-drop scanned=${result.scanned} imported=${result.imported} skipped=${result.skippedFiles}`
|
|
7115
|
+
);
|
|
7116
|
+
for (const warning of result.warnings) {
|
|
7117
|
+
console.warn(`[AgentInspect studio] ${warning}`);
|
|
7118
|
+
}
|
|
7119
|
+
for (const error of result.errors) {
|
|
7120
|
+
console.error(`[AgentInspect studio] ${error}`);
|
|
7121
|
+
}
|
|
7122
|
+
}
|
|
7123
|
+
function summarizeGitHubResult(result) {
|
|
7124
|
+
if (result.skipped) {
|
|
7125
|
+
console.log(`[AgentInspect studio] github import skipped: ${result.reason ?? "disabled"}`);
|
|
7126
|
+
return;
|
|
7127
|
+
}
|
|
7128
|
+
if (result.imported) {
|
|
7129
|
+
console.log(
|
|
7130
|
+
`[AgentInspect studio] github artifact imported to ${result.destPath ?? "(unknown)"}`
|
|
7131
|
+
);
|
|
7132
|
+
} else if (result.destPath) {
|
|
7133
|
+
console.log(`[AgentInspect studio] github artifact unchanged: ${result.destPath}`);
|
|
7134
|
+
}
|
|
7135
|
+
for (const warning of result.registryImportWarnings) {
|
|
7136
|
+
console.warn(`[AgentInspect studio] ${warning}`);
|
|
7137
|
+
}
|
|
7138
|
+
for (const error of result.errors) {
|
|
7139
|
+
console.error(`[AgentInspect studio] ${error}`);
|
|
7140
|
+
}
|
|
7141
|
+
}
|
|
7142
|
+
async function studioImportBundleCommand(options) {
|
|
7143
|
+
const mod = await loadStudio();
|
|
7144
|
+
if (!mod) return;
|
|
7145
|
+
const result = await mod.runStudioBundleUploadImport({
|
|
7146
|
+
...options.workspace !== void 0 ? { workspacePath: options.workspace } : {},
|
|
7147
|
+
...options.db !== void 0 ? { dbPath: options.db } : {},
|
|
7148
|
+
bundlePath: options.path,
|
|
7149
|
+
...options.cwd !== void 0 ? { cwd: options.cwd } : {}
|
|
7150
|
+
});
|
|
7151
|
+
if (result.skipped) {
|
|
7152
|
+
console.log(`[AgentInspect studio] bundle import skipped: ${result.reason ?? "disabled"}`);
|
|
7153
|
+
} else if (result.imported) {
|
|
7154
|
+
console.log(`[AgentInspect studio] bundle imported to ${result.destPath ?? "(unknown)"}`);
|
|
7155
|
+
} else if (result.destPath) {
|
|
7156
|
+
console.log(`[AgentInspect studio] bundle unchanged: ${result.destPath}`);
|
|
7157
|
+
}
|
|
7158
|
+
for (const warning of result.registryImportWarnings) {
|
|
7159
|
+
console.warn(`[AgentInspect studio] ${warning}`);
|
|
7160
|
+
}
|
|
7161
|
+
for (const error of result.errors) {
|
|
7162
|
+
console.error(`[AgentInspect studio] ${error}`);
|
|
7163
|
+
}
|
|
7164
|
+
if (result.errors.length > 0) process.exitCode = 1;
|
|
7165
|
+
}
|
|
7166
|
+
async function studioImportGitHubCommand(options) {
|
|
7167
|
+
const mod = await loadStudio();
|
|
7168
|
+
if (!mod) return;
|
|
7169
|
+
const result = await mod.runStudioGitHubArtifactImport({
|
|
7170
|
+
...options.workspace !== void 0 ? { workspacePath: options.workspace } : {},
|
|
7171
|
+
...options.db !== void 0 ? { dbPath: options.db } : {},
|
|
7172
|
+
repo: options.repo,
|
|
7173
|
+
runId: options.runId,
|
|
7174
|
+
artifact: options.artifact,
|
|
7175
|
+
...options.tokenEnv !== void 0 ? { tokenEnv: options.tokenEnv } : {},
|
|
7176
|
+
...options.cwd !== void 0 ? { cwd: options.cwd } : {}
|
|
7177
|
+
});
|
|
7178
|
+
summarizeGitHubResult(result);
|
|
7179
|
+
if (result.errors.length > 0) {
|
|
7180
|
+
process.exitCode = 1;
|
|
7181
|
+
}
|
|
7182
|
+
}
|
|
7183
|
+
async function studioImportDropCommand(options = {}) {
|
|
7184
|
+
const mod = await loadStudio();
|
|
7185
|
+
if (!mod) return;
|
|
7186
|
+
const result = await mod.runStudioFileDropImport({
|
|
7187
|
+
...options.workspace !== void 0 ? { workspacePath: options.workspace } : {},
|
|
7188
|
+
...options.db !== void 0 ? { dbPath: options.db } : {},
|
|
7189
|
+
...options.dir !== void 0 ? { dropDir: options.dir } : {},
|
|
7190
|
+
...options.archive === true ? { archiveAfterImport: true } : {},
|
|
7191
|
+
...options.cwd !== void 0 ? { cwd: options.cwd } : {}
|
|
7192
|
+
});
|
|
7193
|
+
summarizeFileDropResult(result);
|
|
7194
|
+
if (result.errors.length > 0) {
|
|
7195
|
+
process.exitCode = 1;
|
|
7196
|
+
}
|
|
7197
|
+
}
|
|
7108
7198
|
async function studioCommand(options = {}) {
|
|
7109
7199
|
const mod = await loadStudio();
|
|
7110
7200
|
if (!mod) return;
|
|
7201
|
+
const ingest = options.ingest?.trim();
|
|
7202
|
+
if (ingest && ingest !== "file-drop" && ingest !== "http") {
|
|
7203
|
+
throw new Error(`Unsupported --ingest channel: ${ingest}. Supported: file-drop, http`);
|
|
7204
|
+
}
|
|
7111
7205
|
const port = parsePort2(options.port);
|
|
7112
7206
|
const host = options.host?.trim();
|
|
7113
7207
|
const info = await mod.startStudioServer({
|
|
@@ -7118,8 +7212,18 @@ async function studioCommand(options = {}) {
|
|
|
7118
7212
|
...options.server === true ? { server: true } : {},
|
|
7119
7213
|
...options.cwd !== void 0 ? { cwd: options.cwd } : {},
|
|
7120
7214
|
...options.auth === "basic" ? { auth: "basic" } : {},
|
|
7121
|
-
...options.passwordEnv !== void 0 ? { passwordEnv: options.passwordEnv } : {}
|
|
7215
|
+
...options.passwordEnv !== void 0 ? { passwordEnv: options.passwordEnv } : {},
|
|
7216
|
+
...ingest === "file-drop" ? { ingestFileDrop: true } : {},
|
|
7217
|
+
...ingest === "http" ? { ingestHttp: true } : {},
|
|
7218
|
+
...options.ingestTokenEnv !== void 0 ? { ingestTokenEnv: options.ingestTokenEnv } : {},
|
|
7219
|
+
...options.archiveFileDrop === true ? { archiveFileDrop: true } : {}
|
|
7122
7220
|
});
|
|
7221
|
+
if (ingest === "file-drop") {
|
|
7222
|
+
console.log(`[AgentInspect studio] file-drop ingest enabled for startup scan`);
|
|
7223
|
+
}
|
|
7224
|
+
if (ingest === "http") {
|
|
7225
|
+
console.log(`[AgentInspect studio] HTTP ingest enabled (token required on POST routes)`);
|
|
7226
|
+
}
|
|
7123
7227
|
console.log(`AgentInspect studio (read-only): ${info.url}`);
|
|
7124
7228
|
if (options.open === true && info.host !== "127.0.0.1" && info.host !== "localhost") {
|
|
7125
7229
|
console.warn("Skipping browser open for non-local host binding.");
|
|
@@ -7300,10 +7404,10 @@ async function evalRun(input3, options = {}) {
|
|
|
7300
7404
|
diagnostics: []
|
|
7301
7405
|
};
|
|
7302
7406
|
}
|
|
7303
|
-
function evidenceForRun(run,
|
|
7304
|
-
return [{ runId: run.runId, ...
|
|
7407
|
+
function evidenceForRun(run, path25) {
|
|
7408
|
+
return [{ runId: run.runId, ...path25 !== void 0 ? { path: path25 } : {} }];
|
|
7305
7409
|
}
|
|
7306
|
-
function evidenceForEvent(event,
|
|
7410
|
+
function evidenceForEvent(event, path25) {
|
|
7307
7411
|
return [
|
|
7308
7412
|
{
|
|
7309
7413
|
runId: event.runId,
|
|
@@ -7311,7 +7415,7 @@ function evidenceForEvent(event, path23) {
|
|
|
7311
7415
|
...event.parentId !== void 0 ? { parentId: event.parentId } : {},
|
|
7312
7416
|
kind: event.kind,
|
|
7313
7417
|
name: event.name,
|
|
7314
|
-
...
|
|
7418
|
+
...path25 !== void 0 ? { path: path25 } : {}
|
|
7315
7419
|
}
|
|
7316
7420
|
];
|
|
7317
7421
|
}
|
|
@@ -7469,9 +7573,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
|
|
|
7469
7573
|
function tokenize(text) {
|
|
7470
7574
|
return [...text.toLowerCase().matchAll(/[a-z0-9][a-z0-9'-]{2,}/g)].map((match) => match[0].replace(/^['-]+|['-]+$/g, "")).filter((token) => token.length > 2 && !STOP_WORDS.has(token));
|
|
7471
7575
|
}
|
|
7472
|
-
function firstEvidence(fields, run,
|
|
7576
|
+
function firstEvidence(fields, run, path25) {
|
|
7473
7577
|
const first = fields[0];
|
|
7474
|
-
return first === void 0 ? evidenceForRun(run,
|
|
7578
|
+
return first === void 0 ? evidenceForRun(run, path25) : evidenceForEvent(first.node.event, first.path);
|
|
7475
7579
|
}
|
|
7476
7580
|
function collectSourceIds(nodes, keys) {
|
|
7477
7581
|
const wanted = keySet(keys);
|
|
@@ -7848,8 +7952,8 @@ function renderEvalMarkdown(result) {
|
|
|
7848
7952
|
if (result.findings.length > 0) {
|
|
7849
7953
|
lines.push("", "## Findings");
|
|
7850
7954
|
for (const finding of result.findings) {
|
|
7851
|
-
const
|
|
7852
|
-
lines.push(`- ${finding.ruleId}: ${finding.message}${
|
|
7955
|
+
const path25 = finding.evidence[0]?.path;
|
|
7956
|
+
lines.push(`- ${finding.ruleId}: ${finding.message}${path25 ? ` (${path25})` : ""}`);
|
|
7853
7957
|
}
|
|
7854
7958
|
}
|
|
7855
7959
|
return `${lines.join("\n")}
|
|
@@ -8042,8 +8146,8 @@ function printHuman2(result) {
|
|
|
8042
8146
|
console.log(`- ${diagnostic3.code}: ${diagnostic3.message}`);
|
|
8043
8147
|
}
|
|
8044
8148
|
for (const finding of result.findings) {
|
|
8045
|
-
const
|
|
8046
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
8149
|
+
const path25 = finding.evidence[0]?.path;
|
|
8150
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path25 ? ` (${path25})` : ""}`);
|
|
8047
8151
|
}
|
|
8048
8152
|
}
|
|
8049
8153
|
function readErrorResult2(error) {
|
|
@@ -8281,8 +8385,8 @@ function printHuman3(result) {
|
|
|
8281
8385
|
console.log(`- ${diagnostic3.code}: ${diagnostic3.message}`);
|
|
8282
8386
|
}
|
|
8283
8387
|
for (const finding of result.findings) {
|
|
8284
|
-
const
|
|
8285
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
8388
|
+
const path25 = finding.evidence[0]?.path;
|
|
8389
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path25 ? ` (${path25})` : ""}`);
|
|
8286
8390
|
}
|
|
8287
8391
|
console.log(`Note: ${result.note}`);
|
|
8288
8392
|
}
|
|
@@ -8433,8 +8537,8 @@ function renderCheckSection(result) {
|
|
|
8433
8537
|
`Diagnostics: ${result.diagnostics.length}`
|
|
8434
8538
|
];
|
|
8435
8539
|
for (const finding of result.findings.slice(0, 10)) {
|
|
8436
|
-
const
|
|
8437
|
-
lines.push(`- ${finding.ruleId}: ${finding.message} (${
|
|
8540
|
+
const path25 = finding.evidence[0]?.path ?? "(run)";
|
|
8541
|
+
lines.push(`- ${finding.ruleId}: ${finding.message} (${path25})`);
|
|
8438
8542
|
}
|
|
8439
8543
|
for (const diagnostic3 of result.diagnostics.slice(0, 10)) {
|
|
8440
8544
|
lines.push(`- ${diagnostic3.code}: ${diagnostic3.message}`);
|
|
@@ -9759,6 +9863,176 @@ Summary: ${failed} failed, ${warned} warnings`);
|
|
|
9759
9863
|
if (failed > 0) process2.exitCode = 1;
|
|
9760
9864
|
}
|
|
9761
9865
|
|
|
9866
|
+
// packages/adapter-sdk/src/privacy.ts
|
|
9867
|
+
var PRIVACY_CHECKLIST_ITEMS = [
|
|
9868
|
+
{
|
|
9869
|
+
id: "capture-metadata-only-default",
|
|
9870
|
+
label: "Default capture is metadata-only (no full prompts/outputs on disk)",
|
|
9871
|
+
required: true
|
|
9872
|
+
},
|
|
9873
|
+
{
|
|
9874
|
+
id: "no-network-by-default",
|
|
9875
|
+
label: "Adapter tests and defaults do not call external networks",
|
|
9876
|
+
required: true
|
|
9877
|
+
},
|
|
9878
|
+
{
|
|
9879
|
+
id: "no-upload",
|
|
9880
|
+
label: "Adapter does not upload traces or logs to vendors by default",
|
|
9881
|
+
required: true
|
|
9882
|
+
},
|
|
9883
|
+
{
|
|
9884
|
+
id: "redaction-documented",
|
|
9885
|
+
label: "Redaction/capture modes are documented for adapter users",
|
|
9886
|
+
required: true
|
|
9887
|
+
},
|
|
9888
|
+
{
|
|
9889
|
+
id: "framework-deps-scoped",
|
|
9890
|
+
label: "Framework SDK dependencies stay in the optional adapter package",
|
|
9891
|
+
required: true
|
|
9892
|
+
}
|
|
9893
|
+
];
|
|
9894
|
+
function runPrivacyChecklist(input3 = {}) {
|
|
9895
|
+
const captureMode = input3.captureMode ?? "metadata-only";
|
|
9896
|
+
const items = [
|
|
9897
|
+
{
|
|
9898
|
+
id: "capture-metadata-only-default",
|
|
9899
|
+
ok: captureMode === "metadata-only",
|
|
9900
|
+
detail: captureMode === "metadata-only" ? void 0 : `captureMode is ${captureMode}; metadata-only is required by default`
|
|
9901
|
+
},
|
|
9902
|
+
{
|
|
9903
|
+
id: "no-network-by-default",
|
|
9904
|
+
ok: input3.networkAllowed !== true,
|
|
9905
|
+
detail: input3.networkAllowed ? "network must be disabled in adapter defaults" : void 0
|
|
9906
|
+
},
|
|
9907
|
+
{
|
|
9908
|
+
id: "no-upload",
|
|
9909
|
+
ok: input3.uploadAllowed !== true,
|
|
9910
|
+
detail: input3.uploadAllowed ? "upload must not be enabled by default" : void 0
|
|
9911
|
+
},
|
|
9912
|
+
{
|
|
9913
|
+
id: "redaction-documented",
|
|
9914
|
+
ok: input3.redactionDocumented !== false,
|
|
9915
|
+
detail: input3.redactionDocumented === false ? "document capture/redaction behavior in adapter README" : void 0
|
|
9916
|
+
},
|
|
9917
|
+
{
|
|
9918
|
+
id: "framework-deps-scoped",
|
|
9919
|
+
ok: input3.frameworkDepsPackageScoped !== false,
|
|
9920
|
+
detail: input3.frameworkDepsPackageScoped === false ? "keep framework SDK deps out of agent-inspect root/core" : void 0
|
|
9921
|
+
}
|
|
9922
|
+
];
|
|
9923
|
+
const requiredFailed = items.some(
|
|
9924
|
+
(item) => PRIVACY_CHECKLIST_ITEMS.find((def) => def.id === item.id)?.required && !item.ok
|
|
9925
|
+
);
|
|
9926
|
+
return { ok: !requiredFailed, items };
|
|
9927
|
+
}
|
|
9928
|
+
var PLUGIN_MANIFEST_FILENAME = "agent-inspect-plugin.manifest.json";
|
|
9929
|
+
var PLUGIN_NAME_PREFIXES = [
|
|
9930
|
+
"agent-inspect-plugin-",
|
|
9931
|
+
"agent-inspect-adapter-",
|
|
9932
|
+
"agent-inspect-renderer-",
|
|
9933
|
+
"agent-inspect-check-",
|
|
9934
|
+
"agent-inspect-importer-"
|
|
9935
|
+
];
|
|
9936
|
+
function isPlainObject2(value) {
|
|
9937
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9938
|
+
}
|
|
9939
|
+
function isPluginPackageName(name) {
|
|
9940
|
+
return PLUGIN_NAME_PREFIXES.some((prefix) => name.startsWith(prefix));
|
|
9941
|
+
}
|
|
9942
|
+
function inferPluginTypeFromName(name) {
|
|
9943
|
+
if (name.startsWith("agent-inspect-adapter-")) return "adapter";
|
|
9944
|
+
if (name.startsWith("agent-inspect-renderer-")) return "renderer";
|
|
9945
|
+
if (name.startsWith("agent-inspect-check-")) return "check";
|
|
9946
|
+
if (name.startsWith("agent-inspect-importer-")) return "importer";
|
|
9947
|
+
if (name.startsWith("agent-inspect-plugin-")) return "plugin";
|
|
9948
|
+
return void 0;
|
|
9949
|
+
}
|
|
9950
|
+
function parsePluginManifest(input3) {
|
|
9951
|
+
const errors = [];
|
|
9952
|
+
const warnings = [];
|
|
9953
|
+
if (!isPlainObject2(input3)) {
|
|
9954
|
+
return { ok: false, errors: ["manifest must be a JSON object"], warnings };
|
|
9955
|
+
}
|
|
9956
|
+
if (input3.schemaVersion !== "1.0") {
|
|
9957
|
+
errors.push('schemaVersion must be "1.0"');
|
|
9958
|
+
}
|
|
9959
|
+
const id = typeof input3.id === "string" ? input3.id.trim() : "";
|
|
9960
|
+
if (!id || !isPluginPackageName(id)) {
|
|
9961
|
+
errors.push("id must use an agent-inspect plugin naming prefix");
|
|
9962
|
+
}
|
|
9963
|
+
const type = typeof input3.type === "string" ? input3.type.trim() : "";
|
|
9964
|
+
const allowedTypes = [
|
|
9965
|
+
"plugin",
|
|
9966
|
+
"adapter",
|
|
9967
|
+
"renderer",
|
|
9968
|
+
"check",
|
|
9969
|
+
"importer"
|
|
9970
|
+
];
|
|
9971
|
+
if (!allowedTypes.includes(type)) {
|
|
9972
|
+
errors.push(`type must be one of: ${allowedTypes.join(", ")}`);
|
|
9973
|
+
}
|
|
9974
|
+
const name = typeof input3.name === "string" ? input3.name.trim() : "";
|
|
9975
|
+
if (!name) errors.push("name must be a non-empty string");
|
|
9976
|
+
const version2 = typeof input3.version === "string" ? input3.version.trim() : "";
|
|
9977
|
+
if (!version2) errors.push("version must be a non-empty string");
|
|
9978
|
+
let privacy;
|
|
9979
|
+
if (input3.privacy !== void 0) {
|
|
9980
|
+
if (!isPlainObject2(input3.privacy)) {
|
|
9981
|
+
errors.push("privacy must be an object");
|
|
9982
|
+
} else {
|
|
9983
|
+
privacy = {};
|
|
9984
|
+
if (input3.privacy.captureMode !== void 0) {
|
|
9985
|
+
privacy.captureMode = String(input3.privacy.captureMode);
|
|
9986
|
+
}
|
|
9987
|
+
if (input3.privacy.networkAllowed !== void 0) {
|
|
9988
|
+
privacy.networkAllowed = Boolean(input3.privacy.networkAllowed);
|
|
9989
|
+
}
|
|
9990
|
+
if (input3.privacy.uploadAllowed !== void 0) {
|
|
9991
|
+
privacy.uploadAllowed = Boolean(input3.privacy.uploadAllowed);
|
|
9992
|
+
}
|
|
9993
|
+
if (input3.privacy.redactionDocumented !== void 0) {
|
|
9994
|
+
privacy.redactionDocumented = Boolean(input3.privacy.redactionDocumented);
|
|
9995
|
+
}
|
|
9996
|
+
if (input3.privacy.frameworkDepsPackageScoped !== void 0) {
|
|
9997
|
+
privacy.frameworkDepsPackageScoped = Boolean(input3.privacy.frameworkDepsPackageScoped);
|
|
9998
|
+
}
|
|
9999
|
+
if (privacy.networkAllowed === true || privacy.uploadAllowed === true) {
|
|
10000
|
+
warnings.push("plugin declares network or upload \u2014 review before install");
|
|
10001
|
+
}
|
|
10002
|
+
}
|
|
10003
|
+
} else {
|
|
10004
|
+
warnings.push("privacy block missing; plugins doctor will assume conservative defaults");
|
|
10005
|
+
}
|
|
10006
|
+
if (errors.length > 0) return { ok: false, errors, warnings };
|
|
10007
|
+
return {
|
|
10008
|
+
ok: true,
|
|
10009
|
+
manifest: {
|
|
10010
|
+
schemaVersion: "1.0",
|
|
10011
|
+
id,
|
|
10012
|
+
type,
|
|
10013
|
+
name,
|
|
10014
|
+
version: version2,
|
|
10015
|
+
...privacy ? { privacy } : {}
|
|
10016
|
+
},
|
|
10017
|
+
errors: [],
|
|
10018
|
+
warnings
|
|
10019
|
+
};
|
|
10020
|
+
}
|
|
10021
|
+
async function readPluginManifestFile(packageDir) {
|
|
10022
|
+
const manifestPath = path14.join(packageDir, PLUGIN_MANIFEST_FILENAME);
|
|
10023
|
+
try {
|
|
10024
|
+
const raw = await readFile(manifestPath, "utf8");
|
|
10025
|
+
const parsed = parsePluginManifest(JSON.parse(raw));
|
|
10026
|
+
return { ...parsed, path: manifestPath };
|
|
10027
|
+
} catch (error) {
|
|
10028
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10029
|
+
return { ok: false, path: manifestPath, errors: [message], warnings: [] };
|
|
10030
|
+
}
|
|
10031
|
+
}
|
|
10032
|
+
function validatePluginPrivacy(manifest) {
|
|
10033
|
+
return runPrivacyChecklist(manifest.privacy ?? {});
|
|
10034
|
+
}
|
|
10035
|
+
|
|
9762
10036
|
// packages/adapter-sdk/src/indexer.ts
|
|
9763
10037
|
function defineIndexer(indexer) {
|
|
9764
10038
|
if (!indexer.id.trim()) throw new Error("indexer id is required");
|
|
@@ -9816,7 +10090,115 @@ function createTraceDirectoryIndexer() {
|
|
|
9816
10090
|
});
|
|
9817
10091
|
}
|
|
9818
10092
|
|
|
9819
|
-
// packages/cli/src/
|
|
10093
|
+
// packages/cli/src/plugins.ts
|
|
10094
|
+
async function readPackageName(packageDir) {
|
|
10095
|
+
try {
|
|
10096
|
+
const raw = await readFile(path14.join(packageDir, "package.json"), "utf8");
|
|
10097
|
+
const parsed = JSON.parse(raw);
|
|
10098
|
+
return typeof parsed.name === "string" ? parsed.name : void 0;
|
|
10099
|
+
} catch {
|
|
10100
|
+
return void 0;
|
|
10101
|
+
}
|
|
10102
|
+
}
|
|
10103
|
+
async function discoverPlugins(cwd = process.cwd()) {
|
|
10104
|
+
const nodeModules = path14.join(cwd, "node_modules");
|
|
10105
|
+
const found = [];
|
|
10106
|
+
let entries;
|
|
10107
|
+
try {
|
|
10108
|
+
entries = await readdir(nodeModules);
|
|
10109
|
+
} catch {
|
|
10110
|
+
return found;
|
|
10111
|
+
}
|
|
10112
|
+
for (const entry of entries.sort()) {
|
|
10113
|
+
if (entry.startsWith("@")) continue;
|
|
10114
|
+
if (!isPluginPackageName(entry)) continue;
|
|
10115
|
+
const packageDir = path14.join(nodeModules, entry);
|
|
10116
|
+
const manifestRead = await readPluginManifestFile(packageDir);
|
|
10117
|
+
found.push({
|
|
10118
|
+
packageName: entry,
|
|
10119
|
+
packageDir,
|
|
10120
|
+
...manifestRead.manifest ? { manifest: manifestRead.manifest } : {},
|
|
10121
|
+
manifestErrors: manifestRead.errors,
|
|
10122
|
+
warnings: manifestRead.warnings
|
|
10123
|
+
});
|
|
10124
|
+
}
|
|
10125
|
+
return found;
|
|
10126
|
+
}
|
|
10127
|
+
async function validatePluginPackage(packageRef, cwd = process.cwd()) {
|
|
10128
|
+
const packageDir = path14.isAbsolute(packageRef) ? packageRef : path14.join(cwd, "node_modules", packageRef);
|
|
10129
|
+
const packageName = await readPackageName(packageDir) ?? packageRef;
|
|
10130
|
+
const errors = [];
|
|
10131
|
+
const warnings = [];
|
|
10132
|
+
if (!isPluginPackageName(packageName)) {
|
|
10133
|
+
errors.push(`package name ${packageName} does not match plugin naming convention`);
|
|
10134
|
+
}
|
|
10135
|
+
const manifestRead = await readPluginManifestFile(packageDir);
|
|
10136
|
+
errors.push(...manifestRead.errors);
|
|
10137
|
+
warnings.push(...manifestRead.warnings);
|
|
10138
|
+
if (manifestRead.manifest) {
|
|
10139
|
+
const inferred = inferPluginTypeFromName(packageName);
|
|
10140
|
+
if (inferred && inferred !== manifestRead.manifest.type) {
|
|
10141
|
+
warnings.push(`manifest type ${manifestRead.manifest.type} differs from name prefix (${inferred})`);
|
|
10142
|
+
}
|
|
10143
|
+
const privacy = validatePluginPrivacy(manifestRead.manifest);
|
|
10144
|
+
if (!privacy.ok) {
|
|
10145
|
+
errors.push(
|
|
10146
|
+
...privacy.items.filter((item) => !item.ok).map((item) => item.detail ?? item.id)
|
|
10147
|
+
);
|
|
10148
|
+
}
|
|
10149
|
+
} else if (manifestRead.errors.some((e) => e.includes("ENOENT"))) {
|
|
10150
|
+
warnings.push(`missing ${PLUGIN_MANIFEST_FILENAME}; add manifest for doctor/validate`);
|
|
10151
|
+
}
|
|
10152
|
+
return {
|
|
10153
|
+
ok: errors.length === 0,
|
|
10154
|
+
packageName,
|
|
10155
|
+
packageDir,
|
|
10156
|
+
errors,
|
|
10157
|
+
warnings,
|
|
10158
|
+
manifest: manifestRead.manifest
|
|
10159
|
+
};
|
|
10160
|
+
}
|
|
10161
|
+
async function pluginsListCommand(cwd) {
|
|
10162
|
+
const plugins = await discoverPlugins(cwd);
|
|
10163
|
+
if (plugins.length === 0) {
|
|
10164
|
+
console.log("No agent-inspect plugins found in node_modules.");
|
|
10165
|
+
return;
|
|
10166
|
+
}
|
|
10167
|
+
for (const plugin of plugins) {
|
|
10168
|
+
const type = plugin.manifest?.type ?? inferPluginTypeFromName(plugin.packageName) ?? "unknown";
|
|
10169
|
+
console.log(`${plugin.packageName} (${type})`);
|
|
10170
|
+
}
|
|
10171
|
+
}
|
|
10172
|
+
async function pluginsDoctorCommand(cwd) {
|
|
10173
|
+
const plugins = await discoverPlugins(cwd);
|
|
10174
|
+
if (plugins.length === 0) {
|
|
10175
|
+
console.log("No agent-inspect plugins found.");
|
|
10176
|
+
return;
|
|
10177
|
+
}
|
|
10178
|
+
let issues = 0;
|
|
10179
|
+
for (const plugin of plugins) {
|
|
10180
|
+
const result = await validatePluginPackage(plugin.packageName, cwd);
|
|
10181
|
+
if (result.ok && result.warnings.length === 0) {
|
|
10182
|
+
console.log(`OK ${plugin.packageName}`);
|
|
10183
|
+
continue;
|
|
10184
|
+
}
|
|
10185
|
+
issues += 1;
|
|
10186
|
+
console.log(`WARN ${plugin.packageName}`);
|
|
10187
|
+
for (const warning of result.warnings) console.log(` - ${warning}`);
|
|
10188
|
+
for (const error of result.errors) console.log(` ! ${error}`);
|
|
10189
|
+
}
|
|
10190
|
+
if (issues > 0) process.exitCode = 1;
|
|
10191
|
+
}
|
|
10192
|
+
async function pluginsValidateCommand(packageRef, cwd) {
|
|
10193
|
+
const result = await validatePluginPackage(packageRef, cwd);
|
|
10194
|
+
for (const warning of result.warnings) console.warn(`warning: ${warning}`);
|
|
10195
|
+
if (!result.ok) {
|
|
10196
|
+
for (const error of result.errors) console.error(`error: ${error}`);
|
|
10197
|
+
process.exitCode = 1;
|
|
10198
|
+
return;
|
|
10199
|
+
}
|
|
10200
|
+
console.log(`Valid: ${result.packageName}`);
|
|
10201
|
+
}
|
|
9820
10202
|
var INDEX_FILENAME = ".agent-inspect-index.json";
|
|
9821
10203
|
function traceIndexPath(traceDir) {
|
|
9822
10204
|
return path14.join(traceDir, INDEX_FILENAME);
|
|
@@ -10781,11 +11163,21 @@ function createCliProgram() {
|
|
|
10781
11163
|
program.command("viewer").description("Start localhost read-only viewer (traces, suite, or workspace) (v5.3+)").option("--dir <path>", "trace directory (trace mode)").option("--suite", "suite evidence mode").option("--workspace", "workspace mode").option("--config <path>", "suite config path (suite mode)").option("--host <host>", "bind host (default 127.0.0.1)", "127.0.0.1").option("--port <number>", "bind port (default 7337)", "7337").option("--open", "open browser locally when host is localhost").action((opts) => {
|
|
10782
11164
|
runCommand(() => serveCommand(opts));
|
|
10783
11165
|
});
|
|
10784
|
-
program.command("studio").description(
|
|
11166
|
+
const studioCmd = program.command("studio").description(
|
|
10785
11167
|
"Start self-hosted read-only Studio analyzer (requires @agent-inspect/studio) (v6.0+)"
|
|
10786
|
-
).option("--workspace <path>", "studio registry manifest path").option("--db <path>", "studio database path (sqlite file or postgres URL)").option("--host <host>", "bind host (default 127.0.0.1)", "127.0.0.1").option("--port <number>", "bind port (default 7340)", "7340").option("--server", "bind for network access (0.0.0.0; requires explicit opt-in)").option("--auth <mode>", "auth mode: none or basic", "none").option("--password-env <name>", "env var for basic-auth password").option("--open", "open browser locally when host is localhost").action((opts) => {
|
|
11168
|
+
).option("--workspace <path>", "studio registry manifest path").option("--db <path>", "studio database path (sqlite file or postgres URL)").option("--host <host>", "bind host (default 127.0.0.1)", "127.0.0.1").option("--port <number>", "bind port (default 7340)", "7340").option("--server", "bind for network access (0.0.0.0; requires explicit opt-in)").option("--auth <mode>", "auth mode: none or basic", "none").option("--password-env <name>", "env var for basic-auth password").option("--ingest <channel>", "enable ingest channel on startup (file-drop, http)").option("--ingest-token-env <name>", "env var for HTTP ingest token (default STUDIO_INGEST_TOKEN)").option("--archive-file-drop", "archive file-drop sources after successful import").option("--open", "open browser locally when host is localhost").action((opts) => {
|
|
10787
11169
|
runCommand(() => studioCommand(opts));
|
|
10788
11170
|
});
|
|
11171
|
+
const studioImportCmd = studioCmd.command("import").description("Import external evidence into Studio (v6.1+)");
|
|
11172
|
+
studioImportCmd.command("drop").description("Import allowlisted files from a file-drop directory").option("--workspace <path>", "studio registry manifest path").option("--db <path>", "studio database path (sqlite file or postgres URL)").option("--dir <path>", "file-drop directory (defaults to registry import.fileDropDir)").option("--archive", "move imported files into .imported/ under the drop dir").action((opts) => {
|
|
11173
|
+
runCommand(() => studioImportDropCommand(opts));
|
|
11174
|
+
});
|
|
11175
|
+
studioImportCmd.command("github").description("Download a GitHub Actions artifact into the studio import dirs").requiredOption("--repo <owner/name>", "GitHub repository").requiredOption("--run-id <id>", "workflow run id").requiredOption("--artifact <name>", "artifact name").option("--workspace <path>", "studio registry manifest path").option("--db <path>", "studio database path (sqlite file or postgres URL)").option("--token-env <name>", "env var for GitHub token (default GITHUB_TOKEN)").action((opts) => {
|
|
11176
|
+
runCommand(() => studioImportGitHubCommand(opts));
|
|
11177
|
+
});
|
|
11178
|
+
studioImportCmd.command("bundle").description("Import a local share-safe bundle directory into Studio").requiredOption("--path <dir>", "bundle directory path").option("--workspace <path>", "studio registry manifest path").option("--db <path>", "studio database path (sqlite file or postgres URL)").action((opts) => {
|
|
11179
|
+
runCommand(() => studioImportBundleCommand(opts));
|
|
11180
|
+
});
|
|
10789
11181
|
program.command("eval").description("Run deterministic local evals against a trace").argument("<trace-path-or-run-id>", "trace file, directory, stdin -, or run id").option("--dir <path>", "trace directory for run-id lookup").addOption(
|
|
10790
11182
|
new Option("--format <format>", "trace input format").choices([
|
|
10791
11183
|
"agent-inspect-jsonl",
|
|
@@ -10999,6 +11391,16 @@ function createCliProgram() {
|
|
|
10999
11391
|
).action((opts) => {
|
|
11000
11392
|
runCommand(() => doctorCommand(opts));
|
|
11001
11393
|
});
|
|
11394
|
+
const pluginsCmd = program.command("plugins").description("List and validate explicit-install agent-inspect plugins (v6.2+)");
|
|
11395
|
+
pluginsCmd.command("list").description("List installed plugin packages in node_modules").action(() => {
|
|
11396
|
+
runCommand(() => pluginsListCommand());
|
|
11397
|
+
});
|
|
11398
|
+
pluginsCmd.command("doctor").description("Validate plugin manifests and privacy checklists").action(() => {
|
|
11399
|
+
runCommand(() => pluginsDoctorCommand());
|
|
11400
|
+
});
|
|
11401
|
+
pluginsCmd.command("validate").description("Validate one plugin package by name or path").argument("<package>", "plugin package name or directory path").action((packageRef) => {
|
|
11402
|
+
runCommand(() => pluginsValidateCommand(packageRef));
|
|
11403
|
+
});
|
|
11002
11404
|
const indexCmd = program.command("index").description("Optional local trace directory index (rebuildable metadata cache)");
|
|
11003
11405
|
indexCmd.command("build").description("Build or refresh .agent-inspect-index.json").option("--dir <path>", "trace directory").option("--json", "print JSON result").option("--max-entries <n>", "cap indexed entries (default 10000)").action((opts) => {
|
|
11004
11406
|
runCommand(() => indexBuildCommand(opts));
|