assertledger 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.fr.md +23 -3
- package/README.md +22 -3
- package/dist/build-info.json +1 -1
- package/dist/cli.d.ts +8 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +228 -6
- package/dist/cli.js.map +1 -1
- package/dist/diagnostics.d.ts +6 -0
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +19 -1
- package/dist/diagnostics.js.map +1 -1
- package/dist/engine/connection.d.ts +25 -1
- package/dist/engine/connection.d.ts.map +1 -1
- package/dist/engine/connection.js +144 -31
- package/dist/engine/connection.js.map +1 -1
- package/dist/engine/demo.d.ts +16 -0
- package/dist/engine/demo.d.ts.map +1 -0
- package/dist/engine/demo.js +51 -0
- package/dist/engine/demo.js.map +1 -0
- package/dist/engine/index.d.ts +16 -1
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +95 -12
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/setup.d.ts +39 -0
- package/dist/engine/setup.d.ts.map +1 -0
- package/dist/engine/setup.js +411 -0
- package/dist/engine/setup.js.map +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +12 -2
- package/dist/mcp/index.js.map +1 -1
- package/docs/developer-experience.md +32 -0
- package/docs/reference.md +3 -0
- package/docs/repository-init.md +22 -0
- package/package.json +1 -1
package/dist/engine/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { createReadStream } from "node:fs";
|
|
4
|
-
import { cp, lstat, mkdir, mkdtemp, open, readdir, readFile, realpath, rename, rm, stat, writeFile, } from "node:fs/promises";
|
|
4
|
+
import { cp, link, lstat, mkdir, mkdtemp, open, readdir, readFile, realpath, rename, rm, stat, writeFile, } from "node:fs/promises";
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { Readable } from "node:stream";
|
|
8
8
|
import { pipeline } from "node:stream/promises";
|
|
9
9
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
10
10
|
import { createScanner, SyntaxKind } from "typescript/unstable/ast";
|
|
11
|
-
import { AdapterSchema, isRepositoryExcludeName, parseAgenticBenchmarkAcquisitionRequest, parseAgenticBenchmarkAcquisitionResult, parseEvidenceManifest, parseRepositoryAudit, parseRepositoryInitConfig, parseRepositoryInitConfigV2, parseRepositoryInitLock, parseRepositoryInitLockV2, parseRepositoryInitResult, parseRepositoryInitResultV2, parseVersionedRepositoryInitConfig, parseVersionedRepositoryInitLock,
|
|
11
|
+
import { AdapterSchema, isRepositoryExcludeName, parseAgenticBenchmarkAcquisitionRequest, parseAgenticBenchmarkAcquisitionResult, parseEvidenceManifest, parseRepositoryAudit, parseRepositoryInitConfig, parseRepositoryInitConfigV2, parseRepositoryInitLock, parseRepositoryInitLockV2, parseRepositoryInitResult, parseRepositoryInitResultV2, parseVerificationRequest, parseVersionedRepositoryInitConfig, parseVersionedRepositoryInitLock, parseVersionedVerificationRequest, RepositoryInitDetectionsSchema, repositoryInitConfigDigest, repositoryInitConfigV2Digest, repositoryInitLockDigest, repositoryInitLockV2Digest, } from "../contracts/index.js";
|
|
12
12
|
import { assertSafeRelativePath, canonicalize, createAgenticBenchmark, decideEvidence, portablePathKey, replayAgenticBenchmark, replayEvidenceManifest, sealManifestArtifact, sha256Canonical, } from "../core/index.js";
|
|
13
|
-
import { NODE_TEST_ADAPTER_PROFILE } from "./adapters/node-test-profile.js";
|
|
14
13
|
import { BUN_TEST_ADAPTER_PROFILE } from "./adapters/bun-test-profile.js";
|
|
14
|
+
import { NODE_TEST_ADAPTER_PROFILE } from "./adapters/node-test-profile.js";
|
|
15
15
|
import { runNodeTestRuntimePreflight, } from "./adapters/node-test-runtime.js";
|
|
16
|
-
import { CONTAINER_LIMITATIONS, CONTAINER_REPORTER_FILE, CONTAINER_RESULT_FILE, CONTAINER_WORKSPACE, DEFAULT_CONTAINER_RUNTIME_COMMAND, prepareContainerBackend, runContainerExecution, } from "./container.js";
|
|
17
16
|
import { normalizeRuntimeFacts, RUNTIME_FACTS_VERSION } from "./adapters/runtime-facts.js";
|
|
17
|
+
import { CONTAINER_LIMITATIONS, CONTAINER_REPORTER_FILE, CONTAINER_RESULT_FILE, CONTAINER_WORKSPACE, DEFAULT_CONTAINER_RUNTIME_COMMAND, prepareContainerBackend, runContainerExecution, } from "./container.js";
|
|
18
18
|
import { NODE_TEST_REPORTER_SOURCE } from "./node-test-reporter.js";
|
|
19
19
|
import { runBunRuntimeDoctorChecks, runRuntimeDoctorChecks, } from "./runtime-doctor.js";
|
|
20
20
|
const DEFAULT_EXCLUDES = new Set([".git", ".testforge", "node_modules"]);
|
|
@@ -35,6 +35,20 @@ const INIT_LOCK_FILE = "assertledger.lock.json";
|
|
|
35
35
|
const INIT_MANAGED_FILES = new Set([INIT_CONFIG_FILE, INIT_LOCK_FILE]);
|
|
36
36
|
const INIT_PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun", "uv", "poetry", "pip"];
|
|
37
37
|
const INIT_FRAMEWORKS = ["node:test", "vitest", "jest", "bun:test", "pytest"];
|
|
38
|
+
export class RepositoryInitWriteError extends Error {
|
|
39
|
+
targetPath;
|
|
40
|
+
temporaryPath;
|
|
41
|
+
temporaryCleanup;
|
|
42
|
+
installedPaths;
|
|
43
|
+
constructor(cause, targetPath, temporaryPath, temporaryCleanup, installedPaths) {
|
|
44
|
+
super("INIT_WRITE_FAILED", { cause });
|
|
45
|
+
this.name = "RepositoryInitWriteError";
|
|
46
|
+
this.targetPath = targetPath;
|
|
47
|
+
this.temporaryPath = temporaryPath;
|
|
48
|
+
this.temporaryCleanup = temporaryCleanup;
|
|
49
|
+
this.installedPaths = installedPaths;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
38
52
|
async function removeTemporaryDirectory(directory) {
|
|
39
53
|
await rm(directory, TEMPORARY_CLEANUP_OPTIONS);
|
|
40
54
|
}
|
|
@@ -310,6 +324,13 @@ async function configuredRepositoryFramework(root) {
|
|
|
310
324
|
return undefined;
|
|
311
325
|
}
|
|
312
326
|
}
|
|
327
|
+
/** The refusal of a repository link. The message stays the stable code, which callers match and
|
|
328
|
+
* the CLI classifies by value; the link found travels as the cause so a facade can name it. */
|
|
329
|
+
function repositoryLinkError(relative) {
|
|
330
|
+
return new Error("UNSUPPORTED_REPOSITORY_SYMLINK", {
|
|
331
|
+
cause: relative.split(path.sep).join("/"),
|
|
332
|
+
});
|
|
333
|
+
}
|
|
313
334
|
async function walkFiles(root, excludes) {
|
|
314
335
|
const files = [];
|
|
315
336
|
const spellings = new Map();
|
|
@@ -324,7 +345,7 @@ async function walkFiles(root, excludes) {
|
|
|
324
345
|
const relative = path.posix.join(relativeDirectory.split(path.sep).join("/"), entry.name);
|
|
325
346
|
const entryStats = await lstat(path.join(root, ...relative.split("/")));
|
|
326
347
|
if (entryStats.isSymbolicLink())
|
|
327
|
-
throw
|
|
348
|
+
throw repositoryLinkError(relative);
|
|
328
349
|
registerPortablePath(spellings, relative);
|
|
329
350
|
if (entryStats.isDirectory())
|
|
330
351
|
await visit(relative);
|
|
@@ -584,16 +605,75 @@ function initJavaScriptModuleSpecifiers(source) {
|
|
|
584
605
|
specifiers.delete("");
|
|
585
606
|
return specifiers;
|
|
586
607
|
}
|
|
587
|
-
async function atomicInitWrite(root, relative, content) {
|
|
608
|
+
async function atomicInitWrite(root, relative, content, action, installedPaths, dependencies = {}) {
|
|
588
609
|
const target = path.join(root, relative);
|
|
589
610
|
const temporary = path.join(root, `.${relative}.${process.pid}.${Date.now()}.tmp`);
|
|
611
|
+
const openTemporary = dependencies.openTemporary ?? ((temporaryPath) => open(temporaryPath, "wx"));
|
|
612
|
+
const writeTemporary = dependencies.writeTemporary ??
|
|
613
|
+
((_temporaryPath, temporaryContent, handle) => handle.writeFile(temporaryContent));
|
|
614
|
+
const renameTemporary = dependencies.renameTemporary ?? rename;
|
|
615
|
+
const linkTemporary = dependencies.linkTemporary ?? link;
|
|
616
|
+
const removeTemporary = dependencies.removeTemporary ?? ((temporaryPath) => rm(temporaryPath, { force: true }));
|
|
617
|
+
let temporaryOwned = false;
|
|
618
|
+
let temporaryHandle;
|
|
619
|
+
let temporaryHandleClosed = true;
|
|
620
|
+
let targetInstalled = false;
|
|
590
621
|
try {
|
|
591
|
-
await
|
|
592
|
-
|
|
622
|
+
temporaryHandle = await openTemporary(temporary);
|
|
623
|
+
temporaryOwned = true;
|
|
624
|
+
temporaryHandleClosed = false;
|
|
625
|
+
try {
|
|
626
|
+
await writeTemporary(temporary, content, temporaryHandle);
|
|
627
|
+
}
|
|
628
|
+
finally {
|
|
629
|
+
await temporaryHandle.close();
|
|
630
|
+
temporaryHandleClosed = true;
|
|
631
|
+
}
|
|
632
|
+
if (action === "CREATE") {
|
|
633
|
+
await linkTemporary(temporary, target);
|
|
634
|
+
targetInstalled = true;
|
|
635
|
+
await removeTemporary(temporary);
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
await renameTemporary(temporary, target);
|
|
639
|
+
targetInstalled = true;
|
|
640
|
+
}
|
|
593
641
|
}
|
|
594
642
|
catch (error) {
|
|
595
|
-
|
|
596
|
-
|
|
643
|
+
let temporaryCleanup = "NOT_OWNED";
|
|
644
|
+
if (!temporaryOwned) {
|
|
645
|
+
try {
|
|
646
|
+
await lstat(temporary);
|
|
647
|
+
temporaryCleanup = "UNRESOLVED";
|
|
648
|
+
}
|
|
649
|
+
catch (inspectionError) {
|
|
650
|
+
if (inspectionError.code !== "ENOENT") {
|
|
651
|
+
temporaryCleanup = "UNRESOLVED";
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (temporaryOwned && !temporaryHandleClosed && temporaryHandle !== undefined) {
|
|
656
|
+
try {
|
|
657
|
+
await temporaryHandle.close();
|
|
658
|
+
temporaryHandleClosed = true;
|
|
659
|
+
}
|
|
660
|
+
catch {
|
|
661
|
+
temporaryCleanup = "UNRESOLVED";
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (temporaryOwned) {
|
|
665
|
+
temporaryCleanup = "REMOVED";
|
|
666
|
+
try {
|
|
667
|
+
await removeTemporary(temporary);
|
|
668
|
+
}
|
|
669
|
+
catch {
|
|
670
|
+
temporaryCleanup = "UNRESOLVED";
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
throw new RepositoryInitWriteError(error, target, temporary, temporaryCleanup, [
|
|
674
|
+
...installedPaths,
|
|
675
|
+
...(targetInstalled ? [target] : []),
|
|
676
|
+
]);
|
|
597
677
|
}
|
|
598
678
|
}
|
|
599
679
|
function initEmptyDetections(reasonCodes) {
|
|
@@ -620,7 +700,7 @@ function initTerminalResult(status, detections, reasonCodes) {
|
|
|
620
700
|
};
|
|
621
701
|
return bunBuiltIn ? parseRepositoryInitResultV2(result) : parseRepositoryInitResult(result);
|
|
622
702
|
}
|
|
623
|
-
export async function initializeRepository(requestedRoot, options = {}) {
|
|
703
|
+
export async function initializeRepository(requestedRoot, options = {}, writeDependencies = {}) {
|
|
624
704
|
let root;
|
|
625
705
|
try {
|
|
626
706
|
root = await realpath(requestedRoot);
|
|
@@ -1082,11 +1162,14 @@ export async function initializeRepository(requestedRoot, options = {}) {
|
|
|
1082
1162
|
? parseRepositoryInitResultV2(resultValue)
|
|
1083
1163
|
: parseRepositoryInitResult(resultValue);
|
|
1084
1164
|
if (!options.dryRun) {
|
|
1165
|
+
const installedPaths = [];
|
|
1085
1166
|
for (const action of actions) {
|
|
1086
1167
|
const file = plannedFiles.find((candidate) => candidate.path === action.path);
|
|
1087
1168
|
if (file === undefined)
|
|
1088
1169
|
throw new Error("INIT_PLAN_INCONSISTENT");
|
|
1089
|
-
await atomicInitWrite(root, file.path, file.content);
|
|
1170
|
+
await atomicInitWrite(root, file.path, file.content, action.kind, installedPaths, writeDependencies);
|
|
1171
|
+
if (action.kind === "CREATE")
|
|
1172
|
+
installedPaths.push(path.join(root, file.path));
|
|
1090
1173
|
}
|
|
1091
1174
|
}
|
|
1092
1175
|
return result;
|