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.
Files changed (74) hide show
  1. package/README.md +873 -109
  2. package/dist/agent-plan.d.ts +44 -0
  3. package/dist/agent-plan.js +826 -0
  4. package/dist/agent-run.d.ts +117 -0
  5. package/dist/agent-run.js +459 -0
  6. package/dist/ai-repair.d.ts +58 -0
  7. package/dist/ai-repair.js +380 -0
  8. package/dist/cli.js +936 -38
  9. package/dist/diagnosis.js +114 -17
  10. package/dist/diff.d.ts +29 -0
  11. package/dist/diff.js +569 -0
  12. package/dist/exec.d.ts +30 -2
  13. package/dist/exec.js +332 -37
  14. package/dist/external-health.d.ts +16 -0
  15. package/dist/external-health.js +214 -0
  16. package/dist/infer.js +489 -41
  17. package/dist/plan.d.ts +2 -0
  18. package/dist/plan.js +49 -7
  19. package/dist/proof.d.ts +78 -2
  20. package/dist/proof.js +266 -13
  21. package/dist/receipt.d.ts +52 -0
  22. package/dist/receipt.js +356 -0
  23. package/dist/redact.d.ts +4 -0
  24. package/dist/redact.js +86 -2
  25. package/dist/registry.d.ts +82 -30
  26. package/dist/registry.js +355 -53
  27. package/dist/remote.d.ts +12 -1
  28. package/dist/remote.js +62 -18
  29. package/dist/repair-playbooks.d.ts +24 -0
  30. package/dist/repair-playbooks.js +593 -0
  31. package/dist/repair-safety.d.ts +130 -0
  32. package/dist/repair-safety.js +766 -0
  33. package/dist/repair.d.ts +142 -0
  34. package/dist/repair.js +1566 -0
  35. package/dist/run.d.ts +6 -1
  36. package/dist/run.js +385 -46
  37. package/dist/sbom.d.ts +22 -0
  38. package/dist/sbom.js +99 -0
  39. package/dist/taxonomy.d.ts +8 -2
  40. package/dist/taxonomy.js +428 -8
  41. package/dist/types.d.ts +57 -2
  42. package/docs/AGENT_IN_THE_LOOP.md +171 -0
  43. package/docs/AGENT_RUN_RECEIPTS.md +38 -0
  44. package/docs/CI_ACTION.md +71 -5
  45. package/docs/DETERMINISTIC_REPAIR_SAFETY_MODEL.md +705 -0
  46. package/docs/FAILURE_TAXONOMY.md +30 -1
  47. package/docs/HONESTY_CONTRACT.md +55 -4
  48. package/docs/LAUNCH_PLAYBOOK.md +232 -0
  49. package/docs/REAL_REPO_EVIDENCE.md +77 -0
  50. package/docs/REAL_WORLD_FIXTURES.md +105 -0
  51. package/docs/REGISTRY.md +48 -28
  52. package/docs/RELEASE_CHECKLIST.md +9 -1
  53. package/docs/REPAIR_RECEIPT.md +224 -0
  54. package/docs/agent-loop-gap-analysis.md +188 -0
  55. package/docs/examples/registry-seeds/advertised-port-mismatch.json +28 -0
  56. package/docs/examples/registry-seeds/airbyte-abctl-external-orchestrator.json +36 -0
  57. package/docs/examples/registry-seeds/go-ollama-service.json +36 -0
  58. package/docs/examples/registry-seeds/laravel-vite-sqlite.json +36 -0
  59. package/docs/examples/registry-seeds/monorepo-ambiguous-health.json +29 -0
  60. package/docs/examples/registry-seeds/php-composer.json +33 -0
  61. package/docs/examples/registry-seeds/rails-bundler.json +32 -0
  62. package/docs/examples/registry-seeds/sentry-devenv-direnv.json +41 -0
  63. package/docs/schemas/action-verdict-v1.schema.json +64 -0
  64. package/docs/schemas/agent-plan-v1.schema.json +148 -0
  65. package/docs/schemas/agent-run-receipts-v1.schema.json +192 -0
  66. package/docs/schemas/ai-repair-suggestion-v1.schema.json +70 -0
  67. package/docs/schemas/ci-context-v1.schema.json +63 -0
  68. package/docs/schemas/diff-result-v1.schema.json +66 -0
  69. package/docs/schemas/federated-receipt-v1.schema.json +51 -0
  70. package/docs/schemas/registry-entry-v1.schema.json +95 -0
  71. package/docs/schemas/registry-seed-example-v1.schema.json +102 -0
  72. package/docs/schemas/repair-action-v1.schema.json +136 -0
  73. package/docs/schemas/repair-receipt-v1.schema.json +221 -0
  74. 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 parseGithubRemote(value) {
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 GitHub repository URLs.");
20
+ throw new Error("Remote targets must be full HTTPS Git repository URLs.");
14
21
  }
15
- if (url.protocol !== "https:" || url.hostname.toLowerCase() !== "github.com") {
16
- throw new Error("Remote mode currently accepts only public HTTPS GitHub repository URLs.");
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("Remote GitHub URLs must identify exactly one repository: https://github.com/owner/repo.");
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 owner = parts[0];
26
- const repo = parts[1].replace(/\.git$/i, "");
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 (!safeSegment.test(owner) || !safeSegment.test(repo) || owner === "." || owner === ".." || repo === "." || repo === "..") {
29
- throw new Error("Remote GitHub owner and repository names contain unsupported characters.");
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://github.com/${owner}/${repo}.git`,
34
- owner,
46
+ canonicalUrl: `https://${host}/${namespace}/${repo}.git`,
47
+ provider,
48
+ host,
49
+ namespace,
35
50
  repo,
36
51
  };
37
52
  }
38
- export function cloneGithubRemote(value, cwd) {
39
- const remote = parseGithubRemote(value);
40
- const ownerRoot = path.join(cwd, ".bootproof", "remotes", "github.com", remote.owner);
41
- fs.mkdirSync(ownerRoot, { recursive: true });
42
- const runRoot = fs.mkdtempSync(path.join(ownerRoot, `${remote.repo}-`));
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], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
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;