bootproof 0.1.0 → 0.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/README.md +873 -109
- package/dist/agent-plan.d.ts +44 -0
- package/dist/agent-plan.js +826 -0
- package/dist/agent-run.d.ts +117 -0
- package/dist/agent-run.js +459 -0
- package/dist/ai-repair.d.ts +58 -0
- package/dist/ai-repair.js +380 -0
- package/dist/cli.js +936 -38
- package/dist/diagnosis.js +114 -17
- package/dist/diff.d.ts +29 -0
- package/dist/diff.js +569 -0
- package/dist/exec.d.ts +30 -2
- package/dist/exec.js +332 -37
- package/dist/external-health.d.ts +16 -0
- package/dist/external-health.js +214 -0
- package/dist/infer.js +489 -41
- package/dist/plan.d.ts +2 -0
- package/dist/plan.js +49 -7
- package/dist/proof.d.ts +78 -2
- package/dist/proof.js +266 -13
- package/dist/receipt.d.ts +52 -0
- package/dist/receipt.js +356 -0
- package/dist/redact.d.ts +4 -0
- package/dist/redact.js +86 -2
- package/dist/registry.d.ts +82 -30
- package/dist/registry.js +355 -53
- package/dist/remote.d.ts +12 -1
- package/dist/remote.js +62 -18
- package/dist/repair-playbooks.d.ts +24 -0
- package/dist/repair-playbooks.js +593 -0
- package/dist/repair-safety.d.ts +130 -0
- package/dist/repair-safety.js +766 -0
- package/dist/repair.d.ts +142 -0
- package/dist/repair.js +1566 -0
- package/dist/run.d.ts +6 -1
- package/dist/run.js +385 -46
- package/dist/sbom.d.ts +22 -0
- package/dist/sbom.js +99 -0
- package/dist/taxonomy.d.ts +8 -2
- package/dist/taxonomy.js +428 -8
- package/dist/types.d.ts +57 -2
- package/docs/AGENT_IN_THE_LOOP.md +171 -0
- package/docs/AGENT_RUN_RECEIPTS.md +38 -0
- package/docs/CI_ACTION.md +71 -5
- package/docs/DETERMINISTIC_REPAIR_SAFETY_MODEL.md +705 -0
- package/docs/FAILURE_TAXONOMY.md +30 -1
- package/docs/HONESTY_CONTRACT.md +55 -4
- package/docs/LAUNCH_PLAYBOOK.md +232 -0
- package/docs/REAL_REPO_EVIDENCE.md +77 -0
- package/docs/REAL_WORLD_FIXTURES.md +105 -0
- package/docs/REGISTRY.md +48 -28
- package/docs/RELEASE_CHECKLIST.md +9 -1
- package/docs/REPAIR_RECEIPT.md +224 -0
- package/docs/agent-loop-gap-analysis.md +188 -0
- package/docs/examples/registry-seeds/advertised-port-mismatch.json +28 -0
- package/docs/examples/registry-seeds/airbyte-abctl-external-orchestrator.json +36 -0
- package/docs/examples/registry-seeds/go-ollama-service.json +36 -0
- package/docs/examples/registry-seeds/laravel-vite-sqlite.json +36 -0
- package/docs/examples/registry-seeds/monorepo-ambiguous-health.json +29 -0
- package/docs/examples/registry-seeds/php-composer.json +33 -0
- package/docs/examples/registry-seeds/rails-bundler.json +32 -0
- package/docs/examples/registry-seeds/sentry-devenv-direnv.json +41 -0
- package/docs/schemas/action-verdict-v1.schema.json +64 -0
- package/docs/schemas/agent-plan-v1.schema.json +148 -0
- package/docs/schemas/agent-run-receipts-v1.schema.json +192 -0
- package/docs/schemas/ai-repair-suggestion-v1.schema.json +70 -0
- package/docs/schemas/ci-context-v1.schema.json +63 -0
- package/docs/schemas/diff-result-v1.schema.json +66 -0
- package/docs/schemas/federated-receipt-v1.schema.json +51 -0
- package/docs/schemas/registry-entry-v1.schema.json +95 -0
- package/docs/schemas/registry-seed-example-v1.schema.json +102 -0
- package/docs/schemas/repair-action-v1.schema.json +136 -0
- package/docs/schemas/repair-receipt-v1.schema.json +221 -0
- package/package.json +13 -6
package/dist/remote.js
CHANGED
|
@@ -1,51 +1,91 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { buildExecutionEnv } from "./exec.js";
|
|
5
|
+
const REMOTE_PROVIDERS = {
|
|
6
|
+
"github.com": "github",
|
|
7
|
+
"gitlab.com": "gitlab",
|
|
8
|
+
"bitbucket.org": "bitbucket",
|
|
9
|
+
"codeberg.org": "codeberg",
|
|
10
|
+
};
|
|
4
11
|
export function isRemoteTarget(value) {
|
|
5
12
|
return /^[a-z][a-z0-9+.-]*:\/\//i.test(value) || /^git@/i.test(value);
|
|
6
13
|
}
|
|
7
|
-
export function
|
|
14
|
+
export function parseRemoteTarget(value) {
|
|
8
15
|
let url;
|
|
9
16
|
try {
|
|
10
17
|
url = new URL(value);
|
|
11
18
|
}
|
|
12
19
|
catch {
|
|
13
|
-
throw new Error("Remote targets must be full HTTPS
|
|
20
|
+
throw new Error("Remote targets must be full HTTPS Git repository URLs.");
|
|
14
21
|
}
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
const host = url.hostname.toLowerCase();
|
|
23
|
+
const provider = REMOTE_PROVIDERS[host];
|
|
24
|
+
if (url.protocol !== "https:" || !provider) {
|
|
25
|
+
throw new Error("Remote mode accepts credential-free HTTPS repositories from GitHub, GitLab, Bitbucket, or Codeberg.");
|
|
17
26
|
}
|
|
18
27
|
if (url.username || url.password || url.port || url.search || url.hash) {
|
|
19
28
|
throw new Error("Remote URLs must not contain credentials, custom ports, query strings, or fragments.");
|
|
20
29
|
}
|
|
21
30
|
const parts = url.pathname.split("/").filter(Boolean);
|
|
22
|
-
if (parts.length !== 2) {
|
|
23
|
-
throw new Error(
|
|
31
|
+
if (parts.length < 2 || (provider !== "gitlab" && parts.length !== 2)) {
|
|
32
|
+
throw new Error(provider === "gitlab"
|
|
33
|
+
? "GitLab URLs must identify a namespace and repository."
|
|
34
|
+
: `Remote ${provider} URLs must identify exactly one namespace and repository.`);
|
|
24
35
|
}
|
|
25
|
-
const
|
|
26
|
-
const
|
|
36
|
+
const repo = parts.at(-1).replace(/\.git$/i, "");
|
|
37
|
+
const namespaceParts = parts.slice(0, -1);
|
|
27
38
|
const safeSegment = /^[A-Za-z0-9_.-]+$/;
|
|
28
|
-
if (!
|
|
29
|
-
|
|
39
|
+
if (!namespaceParts.length ||
|
|
40
|
+
[...namespaceParts, repo].some(segment => !safeSegment.test(segment) || segment === "." || segment === ".." || segment === "-")) {
|
|
41
|
+
throw new Error("Remote namespace or repository names contain unsupported characters.");
|
|
30
42
|
}
|
|
43
|
+
const namespace = namespaceParts.join("/");
|
|
31
44
|
return {
|
|
32
45
|
originalUrl: value,
|
|
33
|
-
canonicalUrl: `https
|
|
34
|
-
|
|
46
|
+
canonicalUrl: `https://${host}/${namespace}/${repo}.git`,
|
|
47
|
+
provider,
|
|
48
|
+
host,
|
|
49
|
+
namespace,
|
|
35
50
|
repo,
|
|
36
51
|
};
|
|
37
52
|
}
|
|
38
|
-
export function
|
|
39
|
-
const remote =
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
export function parseGithubRemote(value) {
|
|
54
|
+
const remote = parseRemoteTarget(value);
|
|
55
|
+
if (remote.provider !== "github") {
|
|
56
|
+
throw new Error("Expected a public HTTPS GitHub repository URL.");
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
originalUrl: remote.originalUrl,
|
|
60
|
+
canonicalUrl: remote.canonicalUrl,
|
|
61
|
+
owner: remote.namespace,
|
|
62
|
+
repo: remote.repo,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function cloneRemoteTarget(value, cwd) {
|
|
66
|
+
const remote = parseRemoteTarget(value);
|
|
67
|
+
const namespaceRoot = path.join(cwd, ".bootproof", "remotes", remote.host, ...remote.namespace.split("/"));
|
|
68
|
+
fs.mkdirSync(namespaceRoot, { recursive: true });
|
|
69
|
+
const runRoot = fs.mkdtempSync(path.join(namespaceRoot, `${remote.repo}-`));
|
|
43
70
|
const repoPath = path.join(runRoot, "repo");
|
|
71
|
+
const isolatedGitConfig = path.join(runRoot, "gitconfig");
|
|
72
|
+
fs.writeFileSync(isolatedGitConfig, "");
|
|
44
73
|
try {
|
|
45
|
-
execFileSync("git", ["clone", "--depth", "1", "--single-branch", "--no-tags", "--", remote.canonicalUrl, repoPath], {
|
|
74
|
+
execFileSync("git", ["-c", "credential.helper=", "clone", "--depth", "1", "--single-branch", "--no-tags", "--", remote.canonicalUrl, repoPath], {
|
|
75
|
+
encoding: "utf8",
|
|
76
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
77
|
+
env: buildExecutionEnv({
|
|
78
|
+
GIT_ASKPASS: "",
|
|
79
|
+
GIT_CONFIG_GLOBAL: isolatedGitConfig,
|
|
80
|
+
GIT_CONFIG_NOSYSTEM: "1",
|
|
81
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
84
|
+
fs.rmSync(isolatedGitConfig, { force: true });
|
|
46
85
|
const marker = {
|
|
47
86
|
schema: "bootproof/remote-source/v1",
|
|
48
87
|
canonicalUrl: remote.canonicalUrl,
|
|
88
|
+
provider: remote.provider,
|
|
49
89
|
repoDirectory: "repo",
|
|
50
90
|
};
|
|
51
91
|
fs.writeFileSync(path.join(runRoot, "source.json"), JSON.stringify(marker, null, 2) + "\n");
|
|
@@ -57,6 +97,10 @@ export function cloneGithubRemote(value, cwd) {
|
|
|
57
97
|
}
|
|
58
98
|
return { ...remote, repoPath };
|
|
59
99
|
}
|
|
100
|
+
export function cloneGithubRemote(value, cwd) {
|
|
101
|
+
parseGithubRemote(value);
|
|
102
|
+
return cloneRemoteTarget(value, cwd);
|
|
103
|
+
}
|
|
60
104
|
export function managedRemoteSource(repoPath) {
|
|
61
105
|
const markerPath = path.join(path.dirname(path.resolve(repoPath)), "source.json");
|
|
62
106
|
try {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type RepairAction } from "./repair-safety.js";
|
|
2
|
+
import type { Attestation, FailureClass } from "./types.js";
|
|
3
|
+
export interface DeterministicRepairFileChange {
|
|
4
|
+
path: string;
|
|
5
|
+
before: string | null;
|
|
6
|
+
after: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DeterministicRepairCandidate {
|
|
9
|
+
id: string;
|
|
10
|
+
failureClass: FailureClass;
|
|
11
|
+
action: RepairAction;
|
|
12
|
+
followUpActions?: RepairAction[];
|
|
13
|
+
fileChanges?: DeterministicRepairFileChange[];
|
|
14
|
+
}
|
|
15
|
+
export interface RepairCandidateOptions {
|
|
16
|
+
repoPath?: string;
|
|
17
|
+
homebrewAvailable?: boolean;
|
|
18
|
+
homebrewPrefix?: string | null;
|
|
19
|
+
homebrewPostgresPackage?: string | null;
|
|
20
|
+
environment?: NodeJS.ProcessEnv;
|
|
21
|
+
}
|
|
22
|
+
export declare function executableAvailableOnPath(executable: string, environment?: NodeJS.ProcessEnv): boolean;
|
|
23
|
+
export declare function deterministicRepairCandidateFor(attestation: Attestation, options?: RepairCandidateOptions): DeterministicRepairCandidate | null;
|
|
24
|
+
export declare function repairProgressed(beforeFailureClass: FailureClass, after: Attestation | null): boolean;
|