@attest-ai/core 1.0.0-rc.3 → 1.0.0-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +356 -32
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +146 -5
- package/dist/cjs/proto/index.cjs.map +1 -1
- package/dist/cjs/proto/index.d.cts +14 -1
- package/dist/esm/cli.d.ts +3 -1
- package/dist/esm/cli.js +238 -31
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/index.d.ts +146 -5
- package/dist/esm/index.js +346 -32
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/proto/index.d.ts +14 -1
- package/dist/esm/proto/index.js.map +1 -1
- package/package.json +1 -1
package/dist/esm/cli.js
CHANGED
|
@@ -8,8 +8,8 @@ import * as path4 from "path";
|
|
|
8
8
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9
9
|
|
|
10
10
|
// src/version.ts
|
|
11
|
-
var VERSION = "1.0.0-rc.
|
|
12
|
-
var ENGINE_VERSION = "1.0.0-rc.
|
|
11
|
+
var VERSION = "1.0.0-rc.5";
|
|
12
|
+
var ENGINE_VERSION = "1.0.0-rc.5";
|
|
13
13
|
|
|
14
14
|
// src/engine-manager.ts
|
|
15
15
|
import { spawn, spawnSync } from "child_process";
|
|
@@ -319,10 +319,17 @@ jobs:
|
|
|
319
319
|
- name: Run tests
|
|
320
320
|
id: tests
|
|
321
321
|
continue-on-error: true
|
|
322
|
+
# Traces (prompts, outputs, tool args; not redacted) travel in the artifact; on a public
|
|
323
|
+
# repository any signed-in GitHub user can download it. Enable on private repositories only:
|
|
324
|
+
# env:
|
|
325
|
+
# ATTEST_REPORT_TRACES: "1"
|
|
322
326
|
run: npx vitest run ${REPORT_ARGS}
|
|
323
327
|
- name: Write the JUnit report
|
|
324
328
|
if: always()
|
|
325
329
|
run: npx attest report junit --report attest-report.json --out attest-junit.xml
|
|
330
|
+
- name: Write the failure viewer
|
|
331
|
+
if: always()
|
|
332
|
+
run: npx attest view attest-report.json --no-open --out attest-report.html
|
|
326
333
|
- name: Upload the Attest report
|
|
327
334
|
if: always()
|
|
328
335
|
uses: actions/upload-artifact@v7
|
|
@@ -331,6 +338,7 @@ jobs:
|
|
|
331
338
|
path: |
|
|
332
339
|
attest-report.json
|
|
333
340
|
attest-junit.xml
|
|
341
|
+
attest-report.html
|
|
334
342
|
if-no-files-found: error
|
|
335
343
|
- name: Evaluate the merge policy
|
|
336
344
|
if: always()
|
|
@@ -359,28 +367,36 @@ jobs:
|
|
|
359
367
|
echo "::error::vitest failed; see the Run tests step"
|
|
360
368
|
exit 1
|
|
361
369
|
`;
|
|
362
|
-
function write(
|
|
370
|
+
function write(targetDir, rel, content, result, quiet) {
|
|
371
|
+
const file = path2.join(targetDir, ...rel.split("/"));
|
|
363
372
|
if (fs2.existsSync(file)) {
|
|
364
|
-
|
|
373
|
+
result.skipped.push(rel);
|
|
374
|
+
if (!quiet) process.stderr.write(`Skipped (exists): ${file}
|
|
365
375
|
`);
|
|
366
376
|
return;
|
|
367
377
|
}
|
|
368
378
|
fs2.mkdirSync(path2.dirname(file), { recursive: true });
|
|
369
379
|
fs2.writeFileSync(file, content);
|
|
370
|
-
|
|
380
|
+
result.created.push(rel);
|
|
381
|
+
if (!quiet) process.stdout.write(`Created: ${file}
|
|
371
382
|
`);
|
|
372
383
|
}
|
|
373
384
|
function scaffoldProject(targetDir, options2 = {}) {
|
|
374
|
-
|
|
375
|
-
|
|
385
|
+
const quiet = options2.quiet ?? false;
|
|
386
|
+
const result = { created: [], skipped: [] };
|
|
387
|
+
write(targetDir, "tests/my-agent.test.ts", SAMPLE_TEST_TEMPLATE, result, quiet);
|
|
388
|
+
write(targetDir, "attest.policy.yaml", POLICY_TEMPLATE, result, quiet);
|
|
376
389
|
if (options2.ci === "github") {
|
|
377
|
-
write(
|
|
390
|
+
write(targetDir, ".github/workflows/attest.yml", GITHUB_WORKFLOW_TEMPLATE, result, quiet);
|
|
378
391
|
}
|
|
379
|
-
|
|
380
|
-
|
|
392
|
+
if (!quiet) {
|
|
393
|
+
process.stdout.write(
|
|
394
|
+
`
|
|
381
395
|
Next: run \`npx vitest run\`. Then change the line marked ${DEMO_MARKER} in tests/my-agent.test.ts and run it again to see a failure.
|
|
382
396
|
`
|
|
383
|
-
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
return result;
|
|
384
400
|
}
|
|
385
401
|
|
|
386
402
|
// src/capture.ts
|
|
@@ -608,23 +624,7 @@ function emitVitest(fixturePath) {
|
|
|
608
624
|
|
|
609
625
|
// src/cli.ts
|
|
610
626
|
var HELP_FLAGS = /* @__PURE__ */ new Set(["-h", "--help"]);
|
|
611
|
-
var SDK_USAGE = "usage: attest <command> [args]\n\nSDK commands:\n init [--ci github]
|
|
612
|
-
var INIT_USAGE = "usage: attest init [--ci github] (scaffolds into the current directory)";
|
|
613
|
-
function cmdInit(args) {
|
|
614
|
-
if (args.some((a) => HELP_FLAGS.has(a))) {
|
|
615
|
-
process.stdout.write(`${INIT_USAGE}
|
|
616
|
-
`);
|
|
617
|
-
return 0;
|
|
618
|
-
}
|
|
619
|
-
const joined = args.join(" ");
|
|
620
|
-
if (args.length > 0 && joined !== "--ci github" && joined !== "--ci=github") {
|
|
621
|
-
process.stderr.write(`${INIT_USAGE}
|
|
622
|
-
`);
|
|
623
|
-
return 2;
|
|
624
|
-
}
|
|
625
|
-
scaffoldProject(process.cwd(), args.length > 0 ? { ci: "github" } : {});
|
|
626
|
-
return 0;
|
|
627
|
-
}
|
|
627
|
+
var SDK_USAGE = "usage: attest <command> [args]\n\nSDK commands:\n init [--ci github] [--skills] [--mcp] [--json]\n scaffold a sample test and merge policy here\n (--ci github also writes .github/workflows/attest.yml;\n --skills installs the agent skills with attest-engine;\n --mcp writes the MCP client configs (.mcp.json, .cursor/mcp.json,\n .codex/config.toml) with attest-engine;\n --json prints {created, updated, skipped} paths as JSON)\n\n`attest capture` (engine) also drafts tests/attest/<name>.test.ts from a verified\nfixture, unless that file exists.\n\noptions:\n --version print the SDK version and the engine version it requires\n --help print this help\n";
|
|
628
628
|
var HELP_PROBE_TIMEOUT_MS = 1e4;
|
|
629
629
|
function errorMessage(err) {
|
|
630
630
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -682,24 +682,69 @@ function exitStatus(status, signal) {
|
|
|
682
682
|
if (signal !== null) return 128 + (os2.constants.signals[signal] ?? 0);
|
|
683
683
|
return 1;
|
|
684
684
|
}
|
|
685
|
-
async function
|
|
685
|
+
async function engineProviding(command) {
|
|
686
686
|
let engine;
|
|
687
687
|
try {
|
|
688
688
|
engine = await resolveEngine();
|
|
689
689
|
} catch (err) {
|
|
690
690
|
process.stderr.write(`attest: ${errorMessage(err)}
|
|
691
691
|
`);
|
|
692
|
-
return
|
|
692
|
+
return null;
|
|
693
693
|
}
|
|
694
|
-
const command = args[0] ?? "";
|
|
695
694
|
const commands = engineCommands(engine);
|
|
696
695
|
if (commands === null || command !== "help" && !commands.has(command)) {
|
|
697
696
|
process.stderr.write(
|
|
698
697
|
`attest: attest-engine at ${engine} (version ${ENGINE_VERSION}) predates \`attest ${command}\`: its help does not list that command. Set ATTEST_ENGINE_PATH to an engine built from this release (make engine), or install a release whose engine provides it.
|
|
699
698
|
`
|
|
700
699
|
);
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
return engine;
|
|
703
|
+
}
|
|
704
|
+
var INIT_HELP_ENTRY = {
|
|
705
|
+
name: "init",
|
|
706
|
+
summary: "scaffold a sample test and policy; --ci github, --skills, --mcp, --json",
|
|
707
|
+
schema: "cli/init.schema.json",
|
|
708
|
+
exit_codes: [
|
|
709
|
+
{ code: 0, meaning: "success" },
|
|
710
|
+
{
|
|
711
|
+
code: 1,
|
|
712
|
+
meaning: "--skills or --mcp could not install: the engine is missing, too old or failed"
|
|
713
|
+
},
|
|
714
|
+
{ code: 2, meaning: "usage error" }
|
|
715
|
+
]
|
|
716
|
+
};
|
|
717
|
+
function helpJson(engine) {
|
|
718
|
+
const proc = spawnSync2(engine, ["help", "--json"], {
|
|
719
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
720
|
+
encoding: "utf-8"
|
|
721
|
+
});
|
|
722
|
+
if (proc.error) throw proc.error;
|
|
723
|
+
if (proc.status !== 0) {
|
|
724
|
+
process.stderr.write(proc.stderr);
|
|
725
|
+
return exitStatus(proc.status, proc.signal);
|
|
726
|
+
}
|
|
727
|
+
let doc;
|
|
728
|
+
try {
|
|
729
|
+
doc = JSON.parse(proc.stdout);
|
|
730
|
+
if (!Array.isArray(doc.commands)) throw new Error("no commands array");
|
|
731
|
+
} catch (err) {
|
|
732
|
+
process.stderr.write(`attest: \`${engine} help --json\` printed an unexpected document: ${errorMessage(err)}
|
|
733
|
+
`);
|
|
701
734
|
return 1;
|
|
702
735
|
}
|
|
736
|
+
doc.commands = [...doc.commands, INIT_HELP_ENTRY].sort(
|
|
737
|
+
(a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
|
738
|
+
);
|
|
739
|
+
process.stdout.write(`${JSON.stringify(doc, null, 2)}
|
|
740
|
+
`);
|
|
741
|
+
return 0;
|
|
742
|
+
}
|
|
743
|
+
async function forward(args) {
|
|
744
|
+
const command = args[0] ?? "";
|
|
745
|
+
const engine = await engineProviding(command);
|
|
746
|
+
if (engine === null) return 1;
|
|
747
|
+
if (args.length === 2 && command === "help" && args[1] === "--json") return helpJson(engine);
|
|
703
748
|
if (command === "capture" && captureWritesFixture(args.slice(1))) {
|
|
704
749
|
return captureAndEmit(engine, args.slice(1));
|
|
705
750
|
}
|
|
@@ -761,6 +806,167 @@ function captureAndEmit(engine, args) {
|
|
|
761
806
|
);
|
|
762
807
|
return code;
|
|
763
808
|
}
|
|
809
|
+
var INIT_USAGE = "usage: attest init [--ci github] [--skills] [--mcp] [--json] (scaffolds into the current directory)";
|
|
810
|
+
var INIT_KINDS = ["created", "updated", "skipped"];
|
|
811
|
+
var INIT_KINDS_WITH_CONFLICT = [...INIT_KINDS, "conflict"];
|
|
812
|
+
var SKILLS_KINDS = [
|
|
813
|
+
["created", "created"],
|
|
814
|
+
["updated", "updated"],
|
|
815
|
+
["unchanged", "skipped"]
|
|
816
|
+
];
|
|
817
|
+
var MCP_KINDS = [
|
|
818
|
+
["created", "created"],
|
|
819
|
+
["updated", "updated"],
|
|
820
|
+
["unchanged", "skipped"],
|
|
821
|
+
["conflict", "conflict"]
|
|
822
|
+
];
|
|
823
|
+
function parseInitArgs(args) {
|
|
824
|
+
const parsed = { ci: false, skills: false, mcp: false, json: false };
|
|
825
|
+
for (let i = 0; i < args.length; i++) {
|
|
826
|
+
const arg = args[i];
|
|
827
|
+
if (arg === "--ci" && args[i + 1] === "github" && !parsed.ci) {
|
|
828
|
+
parsed.ci = true;
|
|
829
|
+
i++;
|
|
830
|
+
} else if (arg === "--ci=github" && !parsed.ci) {
|
|
831
|
+
parsed.ci = true;
|
|
832
|
+
} else if (arg === "--skills" && !parsed.skills) {
|
|
833
|
+
parsed.skills = true;
|
|
834
|
+
} else if (arg === "--mcp" && !parsed.mcp) {
|
|
835
|
+
parsed.mcp = true;
|
|
836
|
+
} else if (arg === "--json" && !parsed.json) {
|
|
837
|
+
parsed.json = true;
|
|
838
|
+
} else {
|
|
839
|
+
return null;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return parsed;
|
|
843
|
+
}
|
|
844
|
+
function emptyInitResult() {
|
|
845
|
+
return { created: [], updated: [], skipped: [], conflict: [] };
|
|
846
|
+
}
|
|
847
|
+
function runEngineInstall(engine, subcommand, kinds, what) {
|
|
848
|
+
const argv = [...subcommand, "--json"];
|
|
849
|
+
const shown = [engine, ...argv].join(" ");
|
|
850
|
+
const proc = spawnSync2(engine, argv, {
|
|
851
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
852
|
+
encoding: "utf-8"
|
|
853
|
+
});
|
|
854
|
+
if (proc.error) throw proc.error;
|
|
855
|
+
if (proc.status !== 0) {
|
|
856
|
+
const detail = proc.stderr.trim();
|
|
857
|
+
throw new Error(
|
|
858
|
+
`${what} failed (\`${shown}\` exited ${String(exitStatus(proc.status, proc.signal))})` + (detail === "" ? "" : `: ${detail}`)
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
process.stderr.write(proc.stderr);
|
|
862
|
+
let doc;
|
|
863
|
+
try {
|
|
864
|
+
doc = JSON.parse(proc.stdout);
|
|
865
|
+
} catch (err) {
|
|
866
|
+
throw new Error(`\`${shown}\` printed invalid JSON: ${errorMessage(err)}`);
|
|
867
|
+
}
|
|
868
|
+
if (typeof doc !== "object" || doc === null || Array.isArray(doc)) {
|
|
869
|
+
throw new Error(`\`${shown}\` printed ${JSON.stringify(doc)}, not an object`);
|
|
870
|
+
}
|
|
871
|
+
const record = doc;
|
|
872
|
+
const result = emptyInitResult();
|
|
873
|
+
for (const [engineKind, initKind] of kinds) {
|
|
874
|
+
const paths = record[engineKind];
|
|
875
|
+
if (!Array.isArray(paths) || !paths.every((p) => typeof p === "string")) {
|
|
876
|
+
throw new Error(`\`${shown}\` printed no \`${engineKind}\` list of paths: ${proc.stdout}`);
|
|
877
|
+
}
|
|
878
|
+
result[initKind].push(...paths);
|
|
879
|
+
}
|
|
880
|
+
return result;
|
|
881
|
+
}
|
|
882
|
+
function installSkills(engine, root) {
|
|
883
|
+
return runEngineInstall(engine, ["skills", "install", "--dir", root], SKILLS_KINDS, "skills install");
|
|
884
|
+
}
|
|
885
|
+
function mcpLaunchCommand(root) {
|
|
886
|
+
return fs4.existsSync(path4.join(root, "pnpm-lock.yaml")) ? ["pnpm", "exec", "attest", "mcp"] : ["npx", "--no", "attest", "mcp"];
|
|
887
|
+
}
|
|
888
|
+
function installMcp(engine, root) {
|
|
889
|
+
return runEngineInstall(
|
|
890
|
+
engine,
|
|
891
|
+
["mcp", "install", "--dir", root, "--command", JSON.stringify(mcpLaunchCommand(root))],
|
|
892
|
+
MCP_KINDS,
|
|
893
|
+
"mcp install"
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
function printSkills(root, skills) {
|
|
897
|
+
const abs = (rel) => path4.join(root, ...rel.split("/"));
|
|
898
|
+
for (const rel of skills.created) process.stdout.write(`Created: ${abs(rel)}
|
|
899
|
+
`);
|
|
900
|
+
for (const rel of skills.updated) process.stdout.write(`Updated: ${abs(rel)}
|
|
901
|
+
`);
|
|
902
|
+
for (const rel of skills.skipped) process.stderr.write(`Skipped (unchanged): ${abs(rel)}
|
|
903
|
+
`);
|
|
904
|
+
for (const rel of skills.conflict) {
|
|
905
|
+
process.stderr.write(
|
|
906
|
+
`Conflict (a different attest entry, left as it is; \`attest-engine mcp install --force\` replaces it): ${abs(rel)}
|
|
907
|
+
`
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
async function runInit(parsed, root) {
|
|
912
|
+
const result = emptyInitResult();
|
|
913
|
+
const installs = [
|
|
914
|
+
[parsed.skills, "skills", installSkills],
|
|
915
|
+
[parsed.mcp, "mcp", installMcp]
|
|
916
|
+
];
|
|
917
|
+
for (const [wanted, command, install] of installs) {
|
|
918
|
+
if (!wanted) continue;
|
|
919
|
+
const engine = await engineProviding(command);
|
|
920
|
+
if (engine === null) return null;
|
|
921
|
+
let installed;
|
|
922
|
+
try {
|
|
923
|
+
installed = install(engine, root);
|
|
924
|
+
} catch (err) {
|
|
925
|
+
process.stderr.write(`attest: ${errorMessage(err)}
|
|
926
|
+
`);
|
|
927
|
+
return null;
|
|
928
|
+
}
|
|
929
|
+
if (!parsed.json) printSkills(root, installed);
|
|
930
|
+
for (const kind of INIT_KINDS_WITH_CONFLICT) result[kind].push(...installed[kind]);
|
|
931
|
+
}
|
|
932
|
+
const scaffolded = scaffoldProject(root, {
|
|
933
|
+
...parsed.ci ? { ci: "github" } : {},
|
|
934
|
+
quiet: parsed.json
|
|
935
|
+
});
|
|
936
|
+
result.created.push(...scaffolded.created);
|
|
937
|
+
result.skipped.push(...scaffolded.skipped);
|
|
938
|
+
return result;
|
|
939
|
+
}
|
|
940
|
+
async function cmdInit(args) {
|
|
941
|
+
if (args.some((a) => HELP_FLAGS.has(a))) {
|
|
942
|
+
process.stdout.write(`${INIT_USAGE}
|
|
943
|
+
`);
|
|
944
|
+
return 0;
|
|
945
|
+
}
|
|
946
|
+
const parsed = parseInitArgs(args);
|
|
947
|
+
if (parsed === null) {
|
|
948
|
+
process.stderr.write(`${INIT_USAGE}
|
|
949
|
+
`);
|
|
950
|
+
return 2;
|
|
951
|
+
}
|
|
952
|
+
const root = process.cwd();
|
|
953
|
+
if (!parsed.json) return await runInit(parsed, root) === null ? 1 : 0;
|
|
954
|
+
const stdoutWrite = process.stdout.write;
|
|
955
|
+
process.stdout.write = process.stderr.write.bind(process.stderr);
|
|
956
|
+
let result;
|
|
957
|
+
try {
|
|
958
|
+
result = await runInit(parsed, root);
|
|
959
|
+
} finally {
|
|
960
|
+
process.stdout.write = stdoutWrite;
|
|
961
|
+
}
|
|
962
|
+
if (result === null) return 1;
|
|
963
|
+
const done = result;
|
|
964
|
+
const kinds = parsed.mcp ? INIT_KINDS_WITH_CONFLICT : INIT_KINDS;
|
|
965
|
+
const sorted = Object.fromEntries(kinds.map((k) => [k, [...done[k]].sort()]));
|
|
966
|
+
process.stdout.write(`${JSON.stringify(sorted)}
|
|
967
|
+
`);
|
|
968
|
+
return 0;
|
|
969
|
+
}
|
|
764
970
|
async function main(argv = process.argv.slice(2)) {
|
|
765
971
|
const [command] = argv;
|
|
766
972
|
if (command === void 0 || HELP_FLAGS.has(command) || command === "help" && argv.length === 1) {
|
|
@@ -801,6 +1007,7 @@ export {
|
|
|
801
1007
|
captureWritesFixture,
|
|
802
1008
|
engineCommands,
|
|
803
1009
|
exitStatus,
|
|
1010
|
+
helpJson,
|
|
804
1011
|
main
|
|
805
1012
|
};
|
|
806
1013
|
//# sourceMappingURL=cli.js.map
|