agentverify 1.3.0 → 1.4.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 +28 -0
- package/README.md +12 -1
- package/dist/{chunk-PY3NS2YH.mjs → chunk-XVA2M2HS.mjs} +8 -2
- package/dist/cli.js +87 -20
- package/dist/cli.mjs +81 -20
- package/dist/sdk.d.mts +26 -0
- package/dist/sdk.d.ts +26 -0
- package/dist/sdk.js +8 -2
- package/dist/sdk.mjs +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `agentverify` CLI/SDK package are documented here.
|
|
4
|
+
|
|
5
|
+
## [1.4.0] - Unreleased
|
|
6
|
+
|
|
7
|
+
### Changed — CI exit codes now distinguish a security failure from an execution error (behavior change)
|
|
8
|
+
|
|
9
|
+
Previously, `--ci` mode used exit code `1` for **both** a real `NOT_VERIFIED` verdict **and** any execution error (missing/invalid API key, network failure, missing file). That made it impossible for a pipeline to tell "the agent failed the security check" apart from "the scan itself couldn't run."
|
|
10
|
+
|
|
11
|
+
New exit codes:
|
|
12
|
+
|
|
13
|
+
| Code | Meaning | Previously |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `0` | All scanned files verified | `0` (unchanged) |
|
|
16
|
+
| `1` | `NOT_VERIFIED` — a real security/verification failure | `1` (unchanged) |
|
|
17
|
+
| `2` | `NOT_ASSESSED` — insufficient evidence for a verdict | `2` (unchanged) |
|
|
18
|
+
| `3` | **New.** Execution error — bad/missing API key, network failure, missing file, unknown command | was `1` |
|
|
19
|
+
|
|
20
|
+
**If your pipeline only checks `exit code != 0` to block a deployment, this is not a breaking change** — every previously-non-zero case is still non-zero. **If your pipeline branches on the exact code `1`**, an execution error will now surface as `3` instead — update any such logic to treat both `1` and `3` as failures, or to react differently to `3` (an infrastructure problem, not a security finding).
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Scanner: findings that reference a detected credential no longer include the live secret value in `evidence` — only a redacted preview (e.g. `api_key: "sk_l****" (redacted)`). Affects report evidence produced by any scan, including those made public via report sharing.
|
|
25
|
+
|
|
26
|
+
## [1.3.0] - 2026-07-13
|
|
27
|
+
|
|
28
|
+
Initial public release. See git history for prior changes.
|
package/README.md
CHANGED
|
@@ -51,6 +51,17 @@ agentverify scan ./agents --ci
|
|
|
51
51
|
|
|
52
52
|
Normal scans print findings without failing the shell just because issues exist. CI mode is stricter: it exits non-zero when an agent is `NOT_VERIFIED`, when a scan errors, or when submitted content cannot be assessed.
|
|
53
53
|
|
|
54
|
+
Exit codes distinguish a security/verification result from an application error, so a pipeline can tell "the agent failed the check" apart from "the scan itself couldn't run":
|
|
55
|
+
|
|
56
|
+
| Code | Meaning |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `0` | All scanned files verified |
|
|
59
|
+
| `1` | `NOT_VERIFIED` — a security/verification check failed |
|
|
60
|
+
| `2` | `NOT_ASSESSED` — insufficient evidence for a verdict (see `--allow-not-assessed`) |
|
|
61
|
+
| `3` | Execution error — the scan itself could not run (bad key, network failure, missing file) |
|
|
62
|
+
|
|
63
|
+
All non-zero codes still fail a pipeline step by default, so an existing `if [ $? -ne 0 ]`-style check keeps working unchanged.
|
|
64
|
+
|
|
54
65
|
## SDK Usage
|
|
55
66
|
|
|
56
67
|
```ts
|
|
@@ -105,7 +116,7 @@ The workflow requires a valid `AGENTVERIFY_API_KEY`. The local Worker code path
|
|
|
105
116
|
|
|
106
117
|
Saved CLI reports sync to the dashboard through the API `cliReports` save path. If the API cannot save the report, the CLI still prints local scan output and tells you dashboard sync did not complete.
|
|
107
118
|
|
|
108
|
-
Verified reports can expose a README badge from the report page. A packaged Agent Verify GitHub Action
|
|
119
|
+
Verified reports can expose a README badge from the report page. A packaged Agent Verify GitHub Action (`AI-Blockchain-Ventures/agentverify@v1`) already exists and produces text/Markdown/JSON output plus an optional workflow artifact — see below. Automatically posting that output as a PR comment (rather than just producing it for you to post) is still a planned enhancement, not yet built.
|
|
109
120
|
|
|
110
121
|
See the full GitHub Action guide at `docs/github-action.md` for composite action inputs, JSON/Markdown examples, branch protection, optional PR comments, and badge guidance.
|
|
111
122
|
|
|
@@ -48,7 +48,12 @@ function normalizeScanResult(data) {
|
|
|
48
48
|
scanDuration: 0
|
|
49
49
|
},
|
|
50
50
|
saved: data.saved === true,
|
|
51
|
-
reportUrl: data.reportUrl ?? null
|
|
51
|
+
reportUrl: data.reportUrl ?? null,
|
|
52
|
+
artifactFingerprint: data.artifactFingerprint,
|
|
53
|
+
reportIntegrity: data.reportIntegrity,
|
|
54
|
+
policyProfile: data.policyProfile ?? null,
|
|
55
|
+
policyResult: data.policyResult ?? null,
|
|
56
|
+
attestation: data.attestation ?? null
|
|
52
57
|
};
|
|
53
58
|
}
|
|
54
59
|
async function scan(input, options) {
|
|
@@ -74,7 +79,8 @@ async function scan(input, options) {
|
|
|
74
79
|
body: JSON.stringify({
|
|
75
80
|
content: input.content,
|
|
76
81
|
fileName: input.fileName ?? "agent",
|
|
77
|
-
platform: input.platform
|
|
82
|
+
platform: input.platform,
|
|
83
|
+
policyId: input.policyId
|
|
78
84
|
}),
|
|
79
85
|
signal: controller.signal
|
|
80
86
|
});
|
package/dist/cli.js
CHANGED
|
@@ -55,7 +55,12 @@ function normalizeScanResult(data) {
|
|
|
55
55
|
scanDuration: 0
|
|
56
56
|
},
|
|
57
57
|
saved: data.saved === true,
|
|
58
|
-
reportUrl: data.reportUrl ?? null
|
|
58
|
+
reportUrl: data.reportUrl ?? null,
|
|
59
|
+
artifactFingerprint: data.artifactFingerprint,
|
|
60
|
+
reportIntegrity: data.reportIntegrity,
|
|
61
|
+
policyProfile: data.policyProfile ?? null,
|
|
62
|
+
policyResult: data.policyResult ?? null,
|
|
63
|
+
attestation: data.attestation ?? null
|
|
59
64
|
};
|
|
60
65
|
}
|
|
61
66
|
async function scan(input, options) {
|
|
@@ -81,7 +86,8 @@ async function scan(input, options) {
|
|
|
81
86
|
body: JSON.stringify({
|
|
82
87
|
content: input.content,
|
|
83
88
|
fileName: input.fileName ?? "agent",
|
|
84
|
-
platform: input.platform
|
|
89
|
+
platform: input.platform,
|
|
90
|
+
policyId: input.policyId
|
|
85
91
|
}),
|
|
86
92
|
signal: controller.signal
|
|
87
93
|
});
|
|
@@ -110,9 +116,13 @@ async function scan(input, options) {
|
|
|
110
116
|
}
|
|
111
117
|
|
|
112
118
|
// src/cli.ts
|
|
113
|
-
var VERSION = "1.
|
|
119
|
+
var VERSION = "1.4.0";
|
|
114
120
|
var API_URL = process.env.AGENTVERIFY_API_URL ?? "https://agentverify-api.agentverify.workers.dev/v1/scan";
|
|
115
121
|
var A2SPA_DOCS_URL = "https://aimodularity.com/A2SPA/docs";
|
|
122
|
+
var EXIT_OK = 0;
|
|
123
|
+
var EXIT_NOT_VERIFIED = 1;
|
|
124
|
+
var EXIT_NOT_ASSESSED = 2;
|
|
125
|
+
var EXIT_EXECUTION_ERROR = 3;
|
|
116
126
|
var c = {
|
|
117
127
|
reset: "\x1B[0m",
|
|
118
128
|
bold: "\x1B[1m",
|
|
@@ -127,6 +137,7 @@ var c = {
|
|
|
127
137
|
var write = (value = "") => process.stdout.write(`${value}
|
|
128
138
|
`);
|
|
129
139
|
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".ts", ".py", ".json", ".yaml", ".yml", ".md", ".mjs", ".cjs"]);
|
|
140
|
+
var MAX_SCAN_FILE_BYTES = 5 * 1024 * 1024;
|
|
130
141
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "vendor", "coverage", ".cache"]);
|
|
131
142
|
function collectFiles(dir) {
|
|
132
143
|
const files = [];
|
|
@@ -158,7 +169,9 @@ function parseArgs(argv) {
|
|
|
158
169
|
ci: false,
|
|
159
170
|
allowNotAssessed: false,
|
|
160
171
|
help: false,
|
|
161
|
-
version: false
|
|
172
|
+
version: false,
|
|
173
|
+
policy: "",
|
|
174
|
+
summaryFile: ""
|
|
162
175
|
};
|
|
163
176
|
if (result.command === "--help" || result.command === "-h") {
|
|
164
177
|
result.command = "";
|
|
@@ -176,6 +189,8 @@ function parseArgs(argv) {
|
|
|
176
189
|
else if (arg === "--markdown") result.markdown = true;
|
|
177
190
|
else if (arg === "--ci") result.ci = true;
|
|
178
191
|
else if (arg === "--allow-not-assessed") result.allowNotAssessed = true;
|
|
192
|
+
else if (arg === "--policy") result.policy = args[++i] ?? "";
|
|
193
|
+
else if (arg === "--summary-file") result.summaryFile = args[++i] ?? "";
|
|
179
194
|
else if (arg === "--help" || arg === "-h") result.help = true;
|
|
180
195
|
else if (arg === "--version" || arg === "-v") result.version = true;
|
|
181
196
|
else if (!arg.startsWith("-")) result.dir = arg;
|
|
@@ -203,6 +218,10 @@ ${c.bold}Options:${c.reset}
|
|
|
203
218
|
--ci CI mode: concise output and non-zero exit for NOT VERIFIED
|
|
204
219
|
--allow-not-assessed
|
|
205
220
|
In CI mode, do not fail only because content is NOT_ASSESSED
|
|
221
|
+
--policy <id> Evaluate a built-in policy profile against this scan's evidence
|
|
222
|
+
in addition to the scanner's own verdict (e.g. financial-agent)
|
|
223
|
+
--summary-file <path>
|
|
224
|
+
Write results as JSON to this path (in addition to any other output)
|
|
206
225
|
--version, -v Print version
|
|
207
226
|
--help, -h Show this help message
|
|
208
227
|
|
|
@@ -222,6 +241,12 @@ ${c.bold}Examples:${c.reset}
|
|
|
222
241
|
${c.gray}# Markdown summary for pull requests${c.reset}
|
|
223
242
|
agentverify scan . --key av_your_key --markdown
|
|
224
243
|
|
|
244
|
+
${c.bold}Exit codes (--ci):${c.reset}
|
|
245
|
+
0 All scanned files verified
|
|
246
|
+
1 NOT VERIFIED \u2014 a security/verification check failed
|
|
247
|
+
2 NOT ASSESSED \u2014 insufficient evidence for a verdict (see --allow-not-assessed)
|
|
248
|
+
3 Execution error \u2014 the scan itself could not run (bad key, network, missing file)
|
|
249
|
+
|
|
225
250
|
${c.bold}Get your API key:${c.reset}
|
|
226
251
|
https://aimodularity.com/agentverify/dashboard
|
|
227
252
|
|
|
@@ -243,6 +268,16 @@ function getFixFirst(result) {
|
|
|
243
268
|
function getRelevantThreatCategories(result) {
|
|
244
269
|
return (result.threatCategories ?? []).filter((item) => item.status !== "not_assessed").slice(0, 6).map((item) => `${item.label} (${item.status.replace("_", " ")})`);
|
|
245
270
|
}
|
|
271
|
+
function criticalCount(result) {
|
|
272
|
+
return result.findings.filter((f) => f.severity === "critical").length;
|
|
273
|
+
}
|
|
274
|
+
function highCount(result) {
|
|
275
|
+
return result.findings.filter((f) => f.severity === "high").length;
|
|
276
|
+
}
|
|
277
|
+
function fingerprintPreview(result) {
|
|
278
|
+
const hash = result.artifactFingerprint?.artifactHash;
|
|
279
|
+
return hash ? `${hash.slice(0, 12)}\u2026` : null;
|
|
280
|
+
}
|
|
246
281
|
function printDetailedSummary(result, fileName) {
|
|
247
282
|
const verified = result.verdict === "VERIFIED";
|
|
248
283
|
const verdictColor = verified ? c.green : result.verdict === "NOT_ASSESSED" ? c.yellow : c.red;
|
|
@@ -253,6 +288,10 @@ ${c.bold}${c.cyan}Agent Verify${c.reset}`);
|
|
|
253
288
|
write(`${c.bold}Verdict:${c.reset} ${verdictColor}${result.verdict === "NOT_VERIFIED" ? "NOT VERIFIED" : result.verdict.replace("_", " ")}${c.reset}`);
|
|
254
289
|
write(`${c.bold}Risk level:${c.reset} ${result.riskLevel}`);
|
|
255
290
|
write(`${c.bold}Confidence:${c.reset} ${result.confidence}/100`);
|
|
291
|
+
write(`${c.bold}Critical / High findings:${c.reset} ${criticalCount(result)} / ${highCount(result)}`);
|
|
292
|
+
if (result.metadata?.scannerVersion) write(`${c.bold}Ruleset version:${c.reset} ${result.metadata.scannerVersion}`);
|
|
293
|
+
if (fingerprintPreview(result)) write(`${c.bold}Artifact fingerprint:${c.reset} ${fingerprintPreview(result)}`);
|
|
294
|
+
if (result.policyProfile) write(`${c.bold}Policy (${result.policyProfile}):${c.reset} ${result.policyResult === "PASS" ? `${c.green}PASS${c.reset}` : `${c.red}FAIL${c.reset}`}`);
|
|
256
295
|
write(`${c.bold}Top blocker:${c.reset} ${getTopBlocker(result)}`);
|
|
257
296
|
write(`
|
|
258
297
|
${c.bold}Fix first:${c.reset}`);
|
|
@@ -279,7 +318,11 @@ function markdownSummary(results, summary) {
|
|
|
279
318
|
lines.push(`## ${item.file}`, "", `Error: ${item.error ?? "Unknown error"}`, "");
|
|
280
319
|
continue;
|
|
281
320
|
}
|
|
282
|
-
lines.push(`## ${item.file}`, "", `Score: ${item.result.riskScore}/100`, `Verdict: ${item.result.verdict === "NOT_VERIFIED" ? "NOT VERIFIED" : item.result.verdict.replace("_", " ")}`, `Risk level: ${item.result.riskLevel}`, `
|
|
321
|
+
lines.push(`## ${item.file}`, "", `Score: ${item.result.riskScore}/100`, `Verdict: ${item.result.verdict === "NOT_VERIFIED" ? "NOT VERIFIED" : item.result.verdict.replace("_", " ")}`, `Risk level: ${item.result.riskLevel}`, `Critical / High findings: ${criticalCount(item.result)} / ${highCount(item.result)}`);
|
|
322
|
+
if (item.result.metadata?.scannerVersion) lines.push(`Ruleset version: ${item.result.metadata.scannerVersion}`);
|
|
323
|
+
if (fingerprintPreview(item.result)) lines.push(`Artifact fingerprint: \`${fingerprintPreview(item.result)}\``);
|
|
324
|
+
if (item.result.policyProfile) lines.push(`Policy (${item.result.policyProfile}): ${item.result.policyResult}`);
|
|
325
|
+
lines.push(`Top blocker: ${getTopBlocker(item.result)}`, "");
|
|
283
326
|
const fixes = getFixFirst(item.result);
|
|
284
327
|
if (fixes.length) lines.push("Fix first:", ...fixes.map((fix, index) => `${index + 1}. ${fix}`), "");
|
|
285
328
|
const threats = getRelevantThreatCategories(item.result);
|
|
@@ -289,6 +332,24 @@ function markdownSummary(results, summary) {
|
|
|
289
332
|
lines.push(`A2SPA docs: ${A2SPA_DOCS_URL}`);
|
|
290
333
|
return lines.join("\n");
|
|
291
334
|
}
|
|
335
|
+
function writeGithubJobSummary(results, summary) {
|
|
336
|
+
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
337
|
+
if (!summaryPath) return;
|
|
338
|
+
try {
|
|
339
|
+
(0, import_fs.appendFileSync)(summaryPath, `${markdownSummary(results, summary)}
|
|
340
|
+
`);
|
|
341
|
+
} catch (e) {
|
|
342
|
+
console.error(`Warning: could not write GitHub job summary: ${e instanceof Error ? e.message : e}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function writeSummaryFile(path, results, summary) {
|
|
346
|
+
if (!path) return;
|
|
347
|
+
try {
|
|
348
|
+
(0, import_fs.writeFileSync)(path, JSON.stringify({ results, summary }, null, 2));
|
|
349
|
+
} catch (e) {
|
|
350
|
+
console.error(`Warning: could not write summary file: ${e instanceof Error ? e.message : e}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
292
353
|
function printVerdict(result, fileName) {
|
|
293
354
|
const verified = result.verdict === "VERIFIED";
|
|
294
355
|
const notAssessed = result.verdict === "NOT_ASSESSED";
|
|
@@ -311,7 +372,7 @@ ${c.red}Error:${c.reset} API key required
|
|
|
311
372
|
console.error(` Get your key at https://aimodularity.com/agentverify/dashboard/`);
|
|
312
373
|
console.error(` Example: AGENTVERIFY_API_KEY=av_your_key agentverify scan .
|
|
313
374
|
`);
|
|
314
|
-
process.exit(
|
|
375
|
+
process.exit(EXIT_EXECUTION_ERROR);
|
|
315
376
|
}
|
|
316
377
|
let files = [];
|
|
317
378
|
if (args.file) {
|
|
@@ -342,10 +403,14 @@ ${c.bold}Agent Verify${c.reset} ${c.gray}v${VERSION}${c.reset}`);
|
|
|
342
403
|
const batch = files.slice(i, i + CONCURRENCY);
|
|
343
404
|
const batchResults = await Promise.allSettled(
|
|
344
405
|
batch.map(async (filePath) => {
|
|
406
|
+
const fileStat = (0, import_fs.statSync)(filePath);
|
|
407
|
+
if (fileStat.size > MAX_SCAN_FILE_BYTES) {
|
|
408
|
+
throw new Error(`File is ${(fileStat.size / 1024 / 1024).toFixed(1)}MB, over the ${MAX_SCAN_FILE_BYTES / 1024 / 1024}MB scan limit \u2014 skipped`);
|
|
409
|
+
}
|
|
345
410
|
const content = (0, import_fs.readFileSync)(filePath, "utf-8");
|
|
346
411
|
const relPath = (0, import_path.relative)(args.dir === "." ? process.cwd() : args.dir, filePath);
|
|
347
412
|
const result = await scan(
|
|
348
|
-
{ content, fileName: relPath },
|
|
413
|
+
{ content, fileName: relPath, policyId: args.policy || void 0 },
|
|
349
414
|
{ apiKey: args.key, apiUrl: API_URL }
|
|
350
415
|
);
|
|
351
416
|
return { filePath, relPath, result };
|
|
@@ -376,21 +441,23 @@ ${c.bold}Agent Verify${c.reset} ${c.gray}v${VERSION}${c.reset}`);
|
|
|
376
441
|
}
|
|
377
442
|
}
|
|
378
443
|
const summary = { total: files.length, verified, notVerified, notAssessed, errors };
|
|
444
|
+
writeGithubJobSummary(jsonResults, summary);
|
|
445
|
+
writeSummaryFile(args.summaryFile, jsonResults, summary);
|
|
379
446
|
if (args.json) {
|
|
380
447
|
write(JSON.stringify({ results: jsonResults, summary }, null, 2));
|
|
381
|
-
if (errors > 0) process.exit(
|
|
448
|
+
if (errors > 0) process.exit(EXIT_EXECUTION_ERROR);
|
|
382
449
|
if (args.ci) {
|
|
383
|
-
if (notVerified > 0) process.exit(
|
|
384
|
-
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(
|
|
450
|
+
if (notVerified > 0) process.exit(EXIT_NOT_VERIFIED);
|
|
451
|
+
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(EXIT_NOT_ASSESSED);
|
|
385
452
|
}
|
|
386
453
|
return;
|
|
387
454
|
}
|
|
388
455
|
if (args.markdown) {
|
|
389
456
|
write(markdownSummary(jsonResults, summary));
|
|
390
|
-
if (errors > 0) process.exit(
|
|
457
|
+
if (errors > 0) process.exit(EXIT_EXECUTION_ERROR);
|
|
391
458
|
if (args.ci) {
|
|
392
|
-
if (notVerified > 0) process.exit(
|
|
393
|
-
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(
|
|
459
|
+
if (notVerified > 0) process.exit(EXIT_NOT_VERIFIED);
|
|
460
|
+
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(EXIT_NOT_ASSESSED);
|
|
394
461
|
}
|
|
395
462
|
return;
|
|
396
463
|
}
|
|
@@ -410,21 +477,21 @@ ${c.gray}Saved CLI reports appear in your dashboard: https://aimodularity.com/ag
|
|
|
410
477
|
${c.gray}No dashboard report was saved. Check API key access and retry if you need dashboard sync.${c.reset}`);
|
|
411
478
|
write(`${c.gray}A2SPA docs: ${A2SPA_DOCS_URL}${c.reset}
|
|
412
479
|
`);
|
|
413
|
-
if (errors > 0) process.exit(
|
|
480
|
+
if (errors > 0) process.exit(EXIT_EXECUTION_ERROR);
|
|
414
481
|
if (args.ci) {
|
|
415
|
-
if (notVerified > 0) process.exit(
|
|
416
|
-
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(
|
|
482
|
+
if (notVerified > 0) process.exit(EXIT_NOT_VERIFIED);
|
|
483
|
+
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(EXIT_NOT_ASSESSED);
|
|
417
484
|
}
|
|
418
485
|
}
|
|
419
486
|
async function main() {
|
|
420
487
|
const args = parseArgs(process.argv);
|
|
421
488
|
if (args.version) {
|
|
422
489
|
write(`agentverify v${VERSION}`);
|
|
423
|
-
process.exit(
|
|
490
|
+
process.exit(EXIT_OK);
|
|
424
491
|
}
|
|
425
492
|
if (args.help || !args.command) {
|
|
426
493
|
printHelp();
|
|
427
|
-
process.exit(
|
|
494
|
+
process.exit(EXIT_OK);
|
|
428
495
|
}
|
|
429
496
|
if (args.command === "scan") {
|
|
430
497
|
await runScan(args);
|
|
@@ -433,11 +500,11 @@ async function main() {
|
|
|
433
500
|
${c.red}Unknown command: ${args.command}${c.reset}`);
|
|
434
501
|
console.error(`Run ${c.cyan}agentverify --help${c.reset} for usage
|
|
435
502
|
`);
|
|
436
|
-
process.exit(
|
|
503
|
+
process.exit(EXIT_EXECUTION_ERROR);
|
|
437
504
|
}
|
|
438
505
|
}
|
|
439
506
|
main().catch((err) => {
|
|
440
507
|
console.error(`
|
|
441
508
|
${c.red}Fatal error:${c.reset}`, err instanceof Error ? err.message : err);
|
|
442
|
-
process.exit(
|
|
509
|
+
process.exit(EXIT_EXECUTION_ERROR);
|
|
443
510
|
});
|
package/dist/cli.mjs
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
scan
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XVA2M2HS.mjs";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
|
-
import { readFileSync, readdirSync, statSync } from "fs";
|
|
7
|
+
import { readFileSync, readdirSync, statSync, appendFileSync, writeFileSync } from "fs";
|
|
8
8
|
import { join, relative, extname } from "path";
|
|
9
|
-
var VERSION = "1.
|
|
9
|
+
var VERSION = "1.4.0";
|
|
10
10
|
var API_URL = process.env.AGENTVERIFY_API_URL ?? "https://agentverify-api.agentverify.workers.dev/v1/scan";
|
|
11
11
|
var A2SPA_DOCS_URL = "https://aimodularity.com/A2SPA/docs";
|
|
12
|
+
var EXIT_OK = 0;
|
|
13
|
+
var EXIT_NOT_VERIFIED = 1;
|
|
14
|
+
var EXIT_NOT_ASSESSED = 2;
|
|
15
|
+
var EXIT_EXECUTION_ERROR = 3;
|
|
12
16
|
var c = {
|
|
13
17
|
reset: "\x1B[0m",
|
|
14
18
|
bold: "\x1B[1m",
|
|
@@ -23,6 +27,7 @@ var c = {
|
|
|
23
27
|
var write = (value = "") => process.stdout.write(`${value}
|
|
24
28
|
`);
|
|
25
29
|
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".ts", ".py", ".json", ".yaml", ".yml", ".md", ".mjs", ".cjs"]);
|
|
30
|
+
var MAX_SCAN_FILE_BYTES = 5 * 1024 * 1024;
|
|
26
31
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "vendor", "coverage", ".cache"]);
|
|
27
32
|
function collectFiles(dir) {
|
|
28
33
|
const files = [];
|
|
@@ -54,7 +59,9 @@ function parseArgs(argv) {
|
|
|
54
59
|
ci: false,
|
|
55
60
|
allowNotAssessed: false,
|
|
56
61
|
help: false,
|
|
57
|
-
version: false
|
|
62
|
+
version: false,
|
|
63
|
+
policy: "",
|
|
64
|
+
summaryFile: ""
|
|
58
65
|
};
|
|
59
66
|
if (result.command === "--help" || result.command === "-h") {
|
|
60
67
|
result.command = "";
|
|
@@ -72,6 +79,8 @@ function parseArgs(argv) {
|
|
|
72
79
|
else if (arg === "--markdown") result.markdown = true;
|
|
73
80
|
else if (arg === "--ci") result.ci = true;
|
|
74
81
|
else if (arg === "--allow-not-assessed") result.allowNotAssessed = true;
|
|
82
|
+
else if (arg === "--policy") result.policy = args[++i] ?? "";
|
|
83
|
+
else if (arg === "--summary-file") result.summaryFile = args[++i] ?? "";
|
|
75
84
|
else if (arg === "--help" || arg === "-h") result.help = true;
|
|
76
85
|
else if (arg === "--version" || arg === "-v") result.version = true;
|
|
77
86
|
else if (!arg.startsWith("-")) result.dir = arg;
|
|
@@ -99,6 +108,10 @@ ${c.bold}Options:${c.reset}
|
|
|
99
108
|
--ci CI mode: concise output and non-zero exit for NOT VERIFIED
|
|
100
109
|
--allow-not-assessed
|
|
101
110
|
In CI mode, do not fail only because content is NOT_ASSESSED
|
|
111
|
+
--policy <id> Evaluate a built-in policy profile against this scan's evidence
|
|
112
|
+
in addition to the scanner's own verdict (e.g. financial-agent)
|
|
113
|
+
--summary-file <path>
|
|
114
|
+
Write results as JSON to this path (in addition to any other output)
|
|
102
115
|
--version, -v Print version
|
|
103
116
|
--help, -h Show this help message
|
|
104
117
|
|
|
@@ -118,6 +131,12 @@ ${c.bold}Examples:${c.reset}
|
|
|
118
131
|
${c.gray}# Markdown summary for pull requests${c.reset}
|
|
119
132
|
agentverify scan . --key av_your_key --markdown
|
|
120
133
|
|
|
134
|
+
${c.bold}Exit codes (--ci):${c.reset}
|
|
135
|
+
0 All scanned files verified
|
|
136
|
+
1 NOT VERIFIED \u2014 a security/verification check failed
|
|
137
|
+
2 NOT ASSESSED \u2014 insufficient evidence for a verdict (see --allow-not-assessed)
|
|
138
|
+
3 Execution error \u2014 the scan itself could not run (bad key, network, missing file)
|
|
139
|
+
|
|
121
140
|
${c.bold}Get your API key:${c.reset}
|
|
122
141
|
https://aimodularity.com/agentverify/dashboard
|
|
123
142
|
|
|
@@ -139,6 +158,16 @@ function getFixFirst(result) {
|
|
|
139
158
|
function getRelevantThreatCategories(result) {
|
|
140
159
|
return (result.threatCategories ?? []).filter((item) => item.status !== "not_assessed").slice(0, 6).map((item) => `${item.label} (${item.status.replace("_", " ")})`);
|
|
141
160
|
}
|
|
161
|
+
function criticalCount(result) {
|
|
162
|
+
return result.findings.filter((f) => f.severity === "critical").length;
|
|
163
|
+
}
|
|
164
|
+
function highCount(result) {
|
|
165
|
+
return result.findings.filter((f) => f.severity === "high").length;
|
|
166
|
+
}
|
|
167
|
+
function fingerprintPreview(result) {
|
|
168
|
+
const hash = result.artifactFingerprint?.artifactHash;
|
|
169
|
+
return hash ? `${hash.slice(0, 12)}\u2026` : null;
|
|
170
|
+
}
|
|
142
171
|
function printDetailedSummary(result, fileName) {
|
|
143
172
|
const verified = result.verdict === "VERIFIED";
|
|
144
173
|
const verdictColor = verified ? c.green : result.verdict === "NOT_ASSESSED" ? c.yellow : c.red;
|
|
@@ -149,6 +178,10 @@ ${c.bold}${c.cyan}Agent Verify${c.reset}`);
|
|
|
149
178
|
write(`${c.bold}Verdict:${c.reset} ${verdictColor}${result.verdict === "NOT_VERIFIED" ? "NOT VERIFIED" : result.verdict.replace("_", " ")}${c.reset}`);
|
|
150
179
|
write(`${c.bold}Risk level:${c.reset} ${result.riskLevel}`);
|
|
151
180
|
write(`${c.bold}Confidence:${c.reset} ${result.confidence}/100`);
|
|
181
|
+
write(`${c.bold}Critical / High findings:${c.reset} ${criticalCount(result)} / ${highCount(result)}`);
|
|
182
|
+
if (result.metadata?.scannerVersion) write(`${c.bold}Ruleset version:${c.reset} ${result.metadata.scannerVersion}`);
|
|
183
|
+
if (fingerprintPreview(result)) write(`${c.bold}Artifact fingerprint:${c.reset} ${fingerprintPreview(result)}`);
|
|
184
|
+
if (result.policyProfile) write(`${c.bold}Policy (${result.policyProfile}):${c.reset} ${result.policyResult === "PASS" ? `${c.green}PASS${c.reset}` : `${c.red}FAIL${c.reset}`}`);
|
|
152
185
|
write(`${c.bold}Top blocker:${c.reset} ${getTopBlocker(result)}`);
|
|
153
186
|
write(`
|
|
154
187
|
${c.bold}Fix first:${c.reset}`);
|
|
@@ -175,7 +208,11 @@ function markdownSummary(results, summary) {
|
|
|
175
208
|
lines.push(`## ${item.file}`, "", `Error: ${item.error ?? "Unknown error"}`, "");
|
|
176
209
|
continue;
|
|
177
210
|
}
|
|
178
|
-
lines.push(`## ${item.file}`, "", `Score: ${item.result.riskScore}/100`, `Verdict: ${item.result.verdict === "NOT_VERIFIED" ? "NOT VERIFIED" : item.result.verdict.replace("_", " ")}`, `Risk level: ${item.result.riskLevel}`, `
|
|
211
|
+
lines.push(`## ${item.file}`, "", `Score: ${item.result.riskScore}/100`, `Verdict: ${item.result.verdict === "NOT_VERIFIED" ? "NOT VERIFIED" : item.result.verdict.replace("_", " ")}`, `Risk level: ${item.result.riskLevel}`, `Critical / High findings: ${criticalCount(item.result)} / ${highCount(item.result)}`);
|
|
212
|
+
if (item.result.metadata?.scannerVersion) lines.push(`Ruleset version: ${item.result.metadata.scannerVersion}`);
|
|
213
|
+
if (fingerprintPreview(item.result)) lines.push(`Artifact fingerprint: \`${fingerprintPreview(item.result)}\``);
|
|
214
|
+
if (item.result.policyProfile) lines.push(`Policy (${item.result.policyProfile}): ${item.result.policyResult}`);
|
|
215
|
+
lines.push(`Top blocker: ${getTopBlocker(item.result)}`, "");
|
|
179
216
|
const fixes = getFixFirst(item.result);
|
|
180
217
|
if (fixes.length) lines.push("Fix first:", ...fixes.map((fix, index) => `${index + 1}. ${fix}`), "");
|
|
181
218
|
const threats = getRelevantThreatCategories(item.result);
|
|
@@ -185,6 +222,24 @@ function markdownSummary(results, summary) {
|
|
|
185
222
|
lines.push(`A2SPA docs: ${A2SPA_DOCS_URL}`);
|
|
186
223
|
return lines.join("\n");
|
|
187
224
|
}
|
|
225
|
+
function writeGithubJobSummary(results, summary) {
|
|
226
|
+
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
227
|
+
if (!summaryPath) return;
|
|
228
|
+
try {
|
|
229
|
+
appendFileSync(summaryPath, `${markdownSummary(results, summary)}
|
|
230
|
+
`);
|
|
231
|
+
} catch (e) {
|
|
232
|
+
console.error(`Warning: could not write GitHub job summary: ${e instanceof Error ? e.message : e}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function writeSummaryFile(path, results, summary) {
|
|
236
|
+
if (!path) return;
|
|
237
|
+
try {
|
|
238
|
+
writeFileSync(path, JSON.stringify({ results, summary }, null, 2));
|
|
239
|
+
} catch (e) {
|
|
240
|
+
console.error(`Warning: could not write summary file: ${e instanceof Error ? e.message : e}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
188
243
|
function printVerdict(result, fileName) {
|
|
189
244
|
const verified = result.verdict === "VERIFIED";
|
|
190
245
|
const notAssessed = result.verdict === "NOT_ASSESSED";
|
|
@@ -207,7 +262,7 @@ ${c.red}Error:${c.reset} API key required
|
|
|
207
262
|
console.error(` Get your key at https://aimodularity.com/agentverify/dashboard/`);
|
|
208
263
|
console.error(` Example: AGENTVERIFY_API_KEY=av_your_key agentverify scan .
|
|
209
264
|
`);
|
|
210
|
-
process.exit(
|
|
265
|
+
process.exit(EXIT_EXECUTION_ERROR);
|
|
211
266
|
}
|
|
212
267
|
let files = [];
|
|
213
268
|
if (args.file) {
|
|
@@ -238,10 +293,14 @@ ${c.bold}Agent Verify${c.reset} ${c.gray}v${VERSION}${c.reset}`);
|
|
|
238
293
|
const batch = files.slice(i, i + CONCURRENCY);
|
|
239
294
|
const batchResults = await Promise.allSettled(
|
|
240
295
|
batch.map(async (filePath) => {
|
|
296
|
+
const fileStat = statSync(filePath);
|
|
297
|
+
if (fileStat.size > MAX_SCAN_FILE_BYTES) {
|
|
298
|
+
throw new Error(`File is ${(fileStat.size / 1024 / 1024).toFixed(1)}MB, over the ${MAX_SCAN_FILE_BYTES / 1024 / 1024}MB scan limit \u2014 skipped`);
|
|
299
|
+
}
|
|
241
300
|
const content = readFileSync(filePath, "utf-8");
|
|
242
301
|
const relPath = relative(args.dir === "." ? process.cwd() : args.dir, filePath);
|
|
243
302
|
const result = await scan(
|
|
244
|
-
{ content, fileName: relPath },
|
|
303
|
+
{ content, fileName: relPath, policyId: args.policy || void 0 },
|
|
245
304
|
{ apiKey: args.key, apiUrl: API_URL }
|
|
246
305
|
);
|
|
247
306
|
return { filePath, relPath, result };
|
|
@@ -272,21 +331,23 @@ ${c.bold}Agent Verify${c.reset} ${c.gray}v${VERSION}${c.reset}`);
|
|
|
272
331
|
}
|
|
273
332
|
}
|
|
274
333
|
const summary = { total: files.length, verified, notVerified, notAssessed, errors };
|
|
334
|
+
writeGithubJobSummary(jsonResults, summary);
|
|
335
|
+
writeSummaryFile(args.summaryFile, jsonResults, summary);
|
|
275
336
|
if (args.json) {
|
|
276
337
|
write(JSON.stringify({ results: jsonResults, summary }, null, 2));
|
|
277
|
-
if (errors > 0) process.exit(
|
|
338
|
+
if (errors > 0) process.exit(EXIT_EXECUTION_ERROR);
|
|
278
339
|
if (args.ci) {
|
|
279
|
-
if (notVerified > 0) process.exit(
|
|
280
|
-
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(
|
|
340
|
+
if (notVerified > 0) process.exit(EXIT_NOT_VERIFIED);
|
|
341
|
+
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(EXIT_NOT_ASSESSED);
|
|
281
342
|
}
|
|
282
343
|
return;
|
|
283
344
|
}
|
|
284
345
|
if (args.markdown) {
|
|
285
346
|
write(markdownSummary(jsonResults, summary));
|
|
286
|
-
if (errors > 0) process.exit(
|
|
347
|
+
if (errors > 0) process.exit(EXIT_EXECUTION_ERROR);
|
|
287
348
|
if (args.ci) {
|
|
288
|
-
if (notVerified > 0) process.exit(
|
|
289
|
-
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(
|
|
349
|
+
if (notVerified > 0) process.exit(EXIT_NOT_VERIFIED);
|
|
350
|
+
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(EXIT_NOT_ASSESSED);
|
|
290
351
|
}
|
|
291
352
|
return;
|
|
292
353
|
}
|
|
@@ -306,21 +367,21 @@ ${c.gray}Saved CLI reports appear in your dashboard: https://aimodularity.com/ag
|
|
|
306
367
|
${c.gray}No dashboard report was saved. Check API key access and retry if you need dashboard sync.${c.reset}`);
|
|
307
368
|
write(`${c.gray}A2SPA docs: ${A2SPA_DOCS_URL}${c.reset}
|
|
308
369
|
`);
|
|
309
|
-
if (errors > 0) process.exit(
|
|
370
|
+
if (errors > 0) process.exit(EXIT_EXECUTION_ERROR);
|
|
310
371
|
if (args.ci) {
|
|
311
|
-
if (notVerified > 0) process.exit(
|
|
312
|
-
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(
|
|
372
|
+
if (notVerified > 0) process.exit(EXIT_NOT_VERIFIED);
|
|
373
|
+
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(EXIT_NOT_ASSESSED);
|
|
313
374
|
}
|
|
314
375
|
}
|
|
315
376
|
async function main() {
|
|
316
377
|
const args = parseArgs(process.argv);
|
|
317
378
|
if (args.version) {
|
|
318
379
|
write(`agentverify v${VERSION}`);
|
|
319
|
-
process.exit(
|
|
380
|
+
process.exit(EXIT_OK);
|
|
320
381
|
}
|
|
321
382
|
if (args.help || !args.command) {
|
|
322
383
|
printHelp();
|
|
323
|
-
process.exit(
|
|
384
|
+
process.exit(EXIT_OK);
|
|
324
385
|
}
|
|
325
386
|
if (args.command === "scan") {
|
|
326
387
|
await runScan(args);
|
|
@@ -329,11 +390,11 @@ async function main() {
|
|
|
329
390
|
${c.red}Unknown command: ${args.command}${c.reset}`);
|
|
330
391
|
console.error(`Run ${c.cyan}agentverify --help${c.reset} for usage
|
|
331
392
|
`);
|
|
332
|
-
process.exit(
|
|
393
|
+
process.exit(EXIT_EXECUTION_ERROR);
|
|
333
394
|
}
|
|
334
395
|
}
|
|
335
396
|
main().catch((err) => {
|
|
336
397
|
console.error(`
|
|
337
398
|
${c.red}Fatal error:${c.reset}`, err instanceof Error ? err.message : err);
|
|
338
|
-
process.exit(
|
|
399
|
+
process.exit(EXIT_EXECUTION_ERROR);
|
|
339
400
|
});
|
package/dist/sdk.d.mts
CHANGED
|
@@ -2,6 +2,27 @@ interface ScanInput {
|
|
|
2
2
|
content: string;
|
|
3
3
|
fileName?: string;
|
|
4
4
|
platform?: string;
|
|
5
|
+
/** One of the built-in policy profile ids (e.g. "financial-agent") — evaluated server-side against this scan's evidence, in addition to (never instead of) the scanner's own verdict. */
|
|
6
|
+
policyId?: string;
|
|
7
|
+
}
|
|
8
|
+
interface ArtifactFingerprint {
|
|
9
|
+
artifactHash: string;
|
|
10
|
+
artifactHashAlgorithm: string;
|
|
11
|
+
artifactFingerprintVersion: string;
|
|
12
|
+
}
|
|
13
|
+
interface ReportIntegrity {
|
|
14
|
+
reportHash: string;
|
|
15
|
+
algorithm: string;
|
|
16
|
+
schemaVersion: string;
|
|
17
|
+
scannerVersion: string;
|
|
18
|
+
scanId: string;
|
|
19
|
+
timestamp: string;
|
|
20
|
+
}
|
|
21
|
+
interface SignedAttestation {
|
|
22
|
+
payload: Record<string, unknown>;
|
|
23
|
+
signature: string;
|
|
24
|
+
algorithm: string;
|
|
25
|
+
publicKey: Record<string, unknown>;
|
|
5
26
|
}
|
|
6
27
|
type Verdict = 'VERIFIED' | 'NOT_VERIFIED' | 'NOT_ASSESSED';
|
|
7
28
|
type RiskLevel = 'Low Risk' | 'Moderate Risk' | 'High Risk';
|
|
@@ -103,6 +124,11 @@ interface ScanResult {
|
|
|
103
124
|
};
|
|
104
125
|
saved: boolean;
|
|
105
126
|
reportUrl?: string | null;
|
|
127
|
+
artifactFingerprint?: ArtifactFingerprint;
|
|
128
|
+
reportIntegrity?: ReportIntegrity;
|
|
129
|
+
policyProfile?: string | null;
|
|
130
|
+
policyResult?: string | null;
|
|
131
|
+
attestation?: SignedAttestation | null;
|
|
106
132
|
}
|
|
107
133
|
interface ScanOptions {
|
|
108
134
|
apiKey: string;
|
package/dist/sdk.d.ts
CHANGED
|
@@ -2,6 +2,27 @@ interface ScanInput {
|
|
|
2
2
|
content: string;
|
|
3
3
|
fileName?: string;
|
|
4
4
|
platform?: string;
|
|
5
|
+
/** One of the built-in policy profile ids (e.g. "financial-agent") — evaluated server-side against this scan's evidence, in addition to (never instead of) the scanner's own verdict. */
|
|
6
|
+
policyId?: string;
|
|
7
|
+
}
|
|
8
|
+
interface ArtifactFingerprint {
|
|
9
|
+
artifactHash: string;
|
|
10
|
+
artifactHashAlgorithm: string;
|
|
11
|
+
artifactFingerprintVersion: string;
|
|
12
|
+
}
|
|
13
|
+
interface ReportIntegrity {
|
|
14
|
+
reportHash: string;
|
|
15
|
+
algorithm: string;
|
|
16
|
+
schemaVersion: string;
|
|
17
|
+
scannerVersion: string;
|
|
18
|
+
scanId: string;
|
|
19
|
+
timestamp: string;
|
|
20
|
+
}
|
|
21
|
+
interface SignedAttestation {
|
|
22
|
+
payload: Record<string, unknown>;
|
|
23
|
+
signature: string;
|
|
24
|
+
algorithm: string;
|
|
25
|
+
publicKey: Record<string, unknown>;
|
|
5
26
|
}
|
|
6
27
|
type Verdict = 'VERIFIED' | 'NOT_VERIFIED' | 'NOT_ASSESSED';
|
|
7
28
|
type RiskLevel = 'Low Risk' | 'Moderate Risk' | 'High Risk';
|
|
@@ -103,6 +124,11 @@ interface ScanResult {
|
|
|
103
124
|
};
|
|
104
125
|
saved: boolean;
|
|
105
126
|
reportUrl?: string | null;
|
|
127
|
+
artifactFingerprint?: ArtifactFingerprint;
|
|
128
|
+
reportIntegrity?: ReportIntegrity;
|
|
129
|
+
policyProfile?: string | null;
|
|
130
|
+
policyResult?: string | null;
|
|
131
|
+
attestation?: SignedAttestation | null;
|
|
106
132
|
}
|
|
107
133
|
interface ScanOptions {
|
|
108
134
|
apiKey: string;
|
package/dist/sdk.js
CHANGED
|
@@ -73,7 +73,12 @@ function normalizeScanResult(data) {
|
|
|
73
73
|
scanDuration: 0
|
|
74
74
|
},
|
|
75
75
|
saved: data.saved === true,
|
|
76
|
-
reportUrl: data.reportUrl ?? null
|
|
76
|
+
reportUrl: data.reportUrl ?? null,
|
|
77
|
+
artifactFingerprint: data.artifactFingerprint,
|
|
78
|
+
reportIntegrity: data.reportIntegrity,
|
|
79
|
+
policyProfile: data.policyProfile ?? null,
|
|
80
|
+
policyResult: data.policyResult ?? null,
|
|
81
|
+
attestation: data.attestation ?? null
|
|
77
82
|
};
|
|
78
83
|
}
|
|
79
84
|
async function scan(input, options) {
|
|
@@ -99,7 +104,8 @@ async function scan(input, options) {
|
|
|
99
104
|
body: JSON.stringify({
|
|
100
105
|
content: input.content,
|
|
101
106
|
fileName: input.fileName ?? "agent",
|
|
102
|
-
platform: input.platform
|
|
107
|
+
platform: input.platform,
|
|
108
|
+
policyId: input.policyId
|
|
103
109
|
}),
|
|
104
110
|
signal: controller.signal
|
|
105
111
|
});
|
package/dist/sdk.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentverify",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "CLI and SDK for Agent Verify — execution trust analysis for AI agents",
|
|
5
5
|
"bin": {
|
|
6
|
-
"agentverify": "
|
|
6
|
+
"agentverify": "dist/cli.js"
|
|
7
7
|
},
|
|
8
8
|
"main": "./dist/sdk.js",
|
|
9
9
|
"module": "./dist/sdk.mjs",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"./cli": "./dist/cli.js"
|
|
18
18
|
},
|
|
19
|
-
"files": ["dist", "README.md", "LICENSE"],
|
|
19
|
+
"files": ["dist", "README.md", "LICENSE", "CHANGELOG.md"],
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsup src/cli.ts src/sdk.ts --format cjs,esm --dts --clean",
|
|
22
22
|
"dev": "tsup src/cli.ts src/sdk.ts --format cjs,esm --dts --watch",
|