ralph-research 0.1.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 (174) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/dist/adapters/extractor/command-extractor.d.ts +9 -0
  4. package/dist/adapters/extractor/command-extractor.js +93 -0
  5. package/dist/adapters/extractor/command-extractor.js.map +1 -0
  6. package/dist/adapters/extractor/llm-judge-extractor.d.ts +9 -0
  7. package/dist/adapters/extractor/llm-judge-extractor.js +12 -0
  8. package/dist/adapters/extractor/llm-judge-extractor.js.map +1 -0
  9. package/dist/adapters/fs/json-file-decision-store.d.ts +10 -0
  10. package/dist/adapters/fs/json-file-decision-store.js +53 -0
  11. package/dist/adapters/fs/json-file-decision-store.js.map +1 -0
  12. package/dist/adapters/fs/json-file-frontier-store.d.ts +8 -0
  13. package/dist/adapters/fs/json-file-frontier-store.js +29 -0
  14. package/dist/adapters/fs/json-file-frontier-store.js.map +1 -0
  15. package/dist/adapters/fs/json-file-run-store.d.ts +10 -0
  16. package/dist/adapters/fs/json-file-run-store.js +53 -0
  17. package/dist/adapters/fs/json-file-run-store.js.map +1 -0
  18. package/dist/adapters/fs/lockfile.d.ts +24 -0
  19. package/dist/adapters/fs/lockfile.js +110 -0
  20. package/dist/adapters/fs/lockfile.js.map +1 -0
  21. package/dist/adapters/fs/manifest-loader.d.ts +10 -0
  22. package/dist/adapters/fs/manifest-loader.js +43 -0
  23. package/dist/adapters/fs/manifest-loader.js.map +1 -0
  24. package/dist/adapters/git/git-client.d.ts +9 -0
  25. package/dist/adapters/git/git-client.js +23 -0
  26. package/dist/adapters/git/git-client.js.map +1 -0
  27. package/dist/adapters/index.d.ts +1 -0
  28. package/dist/adapters/index.js +3 -0
  29. package/dist/adapters/index.js.map +1 -0
  30. package/dist/adapters/judge/llm-judge-provider.d.ts +33 -0
  31. package/dist/adapters/judge/llm-judge-provider.js +90 -0
  32. package/dist/adapters/judge/llm-judge-provider.js.map +1 -0
  33. package/dist/adapters/proposer/command-proposer.d.ts +15 -0
  34. package/dist/adapters/proposer/command-proposer.js +29 -0
  35. package/dist/adapters/proposer/command-proposer.js.map +1 -0
  36. package/dist/app/context.d.ts +5 -0
  37. package/dist/app/context.js +7 -0
  38. package/dist/app/context.js.map +1 -0
  39. package/dist/app/services/manual-decision-service.d.ts +20 -0
  40. package/dist/app/services/manual-decision-service.js +143 -0
  41. package/dist/app/services/manual-decision-service.js.map +1 -0
  42. package/dist/app/services/project-state-service.d.ts +52 -0
  43. package/dist/app/services/project-state-service.js +92 -0
  44. package/dist/app/services/project-state-service.js.map +1 -0
  45. package/dist/app/services/run-cycle-service.d.ts +25 -0
  46. package/dist/app/services/run-cycle-service.js +69 -0
  47. package/dist/app/services/run-cycle-service.js.map +1 -0
  48. package/dist/cli/commands/accept.d.ts +10 -0
  49. package/dist/cli/commands/accept.js +54 -0
  50. package/dist/cli/commands/accept.js.map +1 -0
  51. package/dist/cli/commands/demo.d.ts +9 -0
  52. package/dist/cli/commands/demo.js +108 -0
  53. package/dist/cli/commands/demo.js.map +1 -0
  54. package/dist/cli/commands/frontier.d.ts +8 -0
  55. package/dist/cli/commands/frontier.js +48 -0
  56. package/dist/cli/commands/frontier.js.map +1 -0
  57. package/dist/cli/commands/init.d.ts +10 -0
  58. package/dist/cli/commands/init.js +123 -0
  59. package/dist/cli/commands/init.js.map +1 -0
  60. package/dist/cli/commands/inspect.d.ts +8 -0
  61. package/dist/cli/commands/inspect.js +55 -0
  62. package/dist/cli/commands/inspect.js.map +1 -0
  63. package/dist/cli/commands/reject.d.ts +10 -0
  64. package/dist/cli/commands/reject.js +54 -0
  65. package/dist/cli/commands/reject.js.map +1 -0
  66. package/dist/cli/commands/run.d.ts +13 -0
  67. package/dist/cli/commands/run.js +71 -0
  68. package/dist/cli/commands/run.js.map +1 -0
  69. package/dist/cli/commands/serve-mcp.d.ts +7 -0
  70. package/dist/cli/commands/serve-mcp.js +32 -0
  71. package/dist/cli/commands/serve-mcp.js.map +1 -0
  72. package/dist/cli/commands/status.d.ts +8 -0
  73. package/dist/cli/commands/status.js +53 -0
  74. package/dist/cli/commands/status.js.map +1 -0
  75. package/dist/cli/commands/validate.d.ts +11 -0
  76. package/dist/cli/commands/validate.js +56 -0
  77. package/dist/cli/commands/validate.js.map +1 -0
  78. package/dist/cli/main.d.ts +2 -0
  79. package/dist/cli/main.js +38 -0
  80. package/dist/cli/main.js.map +1 -0
  81. package/dist/core/engine/anchor-checker.d.ts +35 -0
  82. package/dist/core/engine/anchor-checker.js +84 -0
  83. package/dist/core/engine/anchor-checker.js.map +1 -0
  84. package/dist/core/engine/audit-sampler.d.ts +16 -0
  85. package/dist/core/engine/audit-sampler.js +25 -0
  86. package/dist/core/engine/audit-sampler.js.map +1 -0
  87. package/dist/core/engine/change-budget.d.ts +11 -0
  88. package/dist/core/engine/change-budget.js +10 -0
  89. package/dist/core/engine/change-budget.js.map +1 -0
  90. package/dist/core/engine/cycle-runner.d.ts +39 -0
  91. package/dist/core/engine/cycle-runner.js +652 -0
  92. package/dist/core/engine/cycle-runner.js.map +1 -0
  93. package/dist/core/engine/experiment-runner.d.ts +13 -0
  94. package/dist/core/engine/experiment-runner.js +24 -0
  95. package/dist/core/engine/experiment-runner.js.map +1 -0
  96. package/dist/core/engine/history-compactor.d.ts +15 -0
  97. package/dist/core/engine/history-compactor.js +76 -0
  98. package/dist/core/engine/history-compactor.js.map +1 -0
  99. package/dist/core/engine/judge-pack.d.ts +44 -0
  100. package/dist/core/engine/judge-pack.js +111 -0
  101. package/dist/core/engine/judge-pack.js.map +1 -0
  102. package/dist/core/engine/parallel-proposer.d.ts +21 -0
  103. package/dist/core/engine/parallel-proposer.js +58 -0
  104. package/dist/core/engine/parallel-proposer.js.map +1 -0
  105. package/dist/core/engine/scope-checker.d.ts +35 -0
  106. package/dist/core/engine/scope-checker.js +166 -0
  107. package/dist/core/engine/scope-checker.js.map +1 -0
  108. package/dist/core/engine/workspace-manager.d.ts +32 -0
  109. package/dist/core/engine/workspace-manager.js +145 -0
  110. package/dist/core/engine/workspace-manager.js.map +1 -0
  111. package/dist/core/index.d.ts +1 -0
  112. package/dist/core/index.js +3 -0
  113. package/dist/core/index.js.map +1 -0
  114. package/dist/core/manifest/defaults.d.ts +55 -0
  115. package/dist/core/manifest/defaults.js +56 -0
  116. package/dist/core/manifest/defaults.js.map +1 -0
  117. package/dist/core/manifest/schema.d.ts +647 -0
  118. package/dist/core/manifest/schema.js +254 -0
  119. package/dist/core/manifest/schema.js.map +1 -0
  120. package/dist/core/model/decision-record.d.ts +38 -0
  121. package/dist/core/model/decision-record.js +29 -0
  122. package/dist/core/model/decision-record.js.map +1 -0
  123. package/dist/core/model/frontier-entry.d.ts +24 -0
  124. package/dist/core/model/frontier-entry.js +15 -0
  125. package/dist/core/model/frontier-entry.js.map +1 -0
  126. package/dist/core/model/metric.d.ts +13 -0
  127. package/dist/core/model/metric.js +10 -0
  128. package/dist/core/model/metric.js.map +1 -0
  129. package/dist/core/model/run-record.d.ts +110 -0
  130. package/dist/core/model/run-record.js +104 -0
  131. package/dist/core/model/run-record.js.map +1 -0
  132. package/dist/core/ports/decision-store.d.ts +6 -0
  133. package/dist/core/ports/decision-store.js +2 -0
  134. package/dist/core/ports/decision-store.js.map +1 -0
  135. package/dist/core/ports/frontier-store.d.ts +5 -0
  136. package/dist/core/ports/frontier-store.js +2 -0
  137. package/dist/core/ports/frontier-store.js.map +1 -0
  138. package/dist/core/ports/run-store.d.ts +6 -0
  139. package/dist/core/ports/run-store.js +2 -0
  140. package/dist/core/ports/run-store.js.map +1 -0
  141. package/dist/core/state/constraint-engine.d.ts +18 -0
  142. package/dist/core/state/constraint-engine.js +42 -0
  143. package/dist/core/state/constraint-engine.js.map +1 -0
  144. package/dist/core/state/frontier-engine.d.ts +24 -0
  145. package/dist/core/state/frontier-engine.js +178 -0
  146. package/dist/core/state/frontier-engine.js.map +1 -0
  147. package/dist/core/state/ratchet-engine.d.ts +28 -0
  148. package/dist/core/state/ratchet-engine.js +177 -0
  149. package/dist/core/state/ratchet-engine.js.map +1 -0
  150. package/dist/core/state/run-state-machine.d.ts +17 -0
  151. package/dist/core/state/run-state-machine.js +94 -0
  152. package/dist/core/state/run-state-machine.js.map +1 -0
  153. package/dist/mcp/main.d.ts +1 -0
  154. package/dist/mcp/main.js +8 -0
  155. package/dist/mcp/main.js.map +1 -0
  156. package/dist/mcp/server.d.ts +6 -0
  157. package/dist/mcp/server.js +97 -0
  158. package/dist/mcp/server.js.map +1 -0
  159. package/dist/shared/fs-errors.d.ts +1 -0
  160. package/dist/shared/fs-errors.js +4 -0
  161. package/dist/shared/fs-errors.js.map +1 -0
  162. package/dist/shared/logger.d.ts +2 -0
  163. package/dist/shared/logger.js +5 -0
  164. package/dist/shared/logger.js.map +1 -0
  165. package/dist/shared/template-utils.d.ts +9 -0
  166. package/dist/shared/template-utils.js +50 -0
  167. package/dist/shared/template-utils.js.map +1 -0
  168. package/package.json +44 -0
  169. package/templates/writing/docs/draft.md +1 -0
  170. package/templates/writing/prompts/judge.md +15 -0
  171. package/templates/writing/ralph.yaml +63 -0
  172. package/templates/writing/scripts/experiment.mjs +6 -0
  173. package/templates/writing/scripts/metric.mjs +24 -0
  174. package/templates/writing/scripts/propose.mjs +13 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 research-ratchet contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # ralph-research
2
+
3
+ Local-first runtime for recursive research improvement.
4
+
5
+ `ralph-research` runs a bounded improvement loop over a real artifact:
6
+
7
+ 1. define a metric
8
+ 2. generate one candidate change
9
+ 3. evaluate it
10
+ 4. keep only verified improvements
11
+
12
+ The v0.1 focus is a writing workflow that is runnable in under five minutes on a local machine.
13
+
14
+ ## Quickstart
15
+
16
+ ### Zero-config demo
17
+
18
+ ```bash
19
+ npx ralph-research demo writing
20
+ ```
21
+
22
+ This creates a temporary writing repo, runs one accepted cycle, and prints the path plus the run id. The v0.1 demo supports the bundled `writing` template only.
23
+
24
+ ### Template flow
25
+
26
+ ```bash
27
+ npx ralph-research init --template writing
28
+ npx ralph-research run --json
29
+ npx ralph-research inspect run-0001 --json
30
+ ```
31
+
32
+ This path is the v0.1 success bar: `init -> run -> inspect` should work quickly and produce an acceptance reason you can inspect. The bundled template set is currently `writing` only.
33
+
34
+ ## Core Concepts
35
+
36
+ - `Manifest`: `ralph.yaml` defines the research program.
37
+ - `Metric`: how candidate quality is measured.
38
+ - `Frontier`: the currently accepted best candidate set.
39
+ - `Ratchet`: the acceptance policy that decides whether the frontier advances.
40
+ - `Proposer`: how a bounded candidate change is generated.
41
+ - `Judge`: how qualitative outputs are compared when numeric metrics are not enough.
42
+
43
+ ## Writing Template
44
+
45
+ The bundled writing template is self-contained:
46
+
47
+ - `docs/draft.md`: sample draft
48
+ - `scripts/propose.mjs`: bounded rewrite
49
+ - `scripts/experiment.mjs`: output materialization
50
+ - `scripts/metric.mjs`: local heuristic metric
51
+ - `prompts/judge.md`: pairwise judge prompt you can upgrade to later
52
+
53
+ The default template uses a local command metric so the first run does not require API keys. When you are ready, replace the numeric metric with an `llm_judge` extractor and use the included pairwise prompt as a starting point.
54
+
55
+ ## CLI
56
+
57
+ ```text
58
+ rrx validate
59
+ rrx doctor
60
+ rrx init --template writing
61
+ rrx demo writing
62
+ rrx run
63
+ rrx status
64
+ rrx frontier
65
+ rrx inspect <runId>
66
+ rrx accept <runId>
67
+ rrx reject <runId>
68
+ rrx serve-mcp --stdio
69
+ ```
70
+
71
+ ## MCP
72
+
73
+ The bundled MCP server currently supports stdio transport and exposes three thin tools backed by the same service layer as the CLI:
74
+
75
+ - `run_research_cycle`
76
+ - `get_research_status`
77
+ - `get_frontier`
78
+
79
+ ## Design Principles
80
+
81
+ - local-first execution
82
+ - bounded changes
83
+ - recoverable state transitions
84
+ - trusted signal before automation
85
+ - inspectable accept/reject decisions
86
+
87
+ ## Development
88
+
89
+ ```bash
90
+ npm install
91
+ npm test
92
+ npm run typecheck
93
+ npm run build
94
+ ```
95
+
96
+ ## License
97
+
98
+ MIT
@@ -0,0 +1,9 @@
1
+ import type { CommandMetricExtractorConfig } from "../../core/manifest/schema.js";
2
+ import type { MetricResult } from "../../core/model/metric.js";
3
+ export interface ExtractCommandMetricInput {
4
+ metricId: string;
5
+ direction: "maximize" | "minimize";
6
+ workspacePath: string;
7
+ env?: Record<string, string>;
8
+ }
9
+ export declare function extractCommandMetric(config: CommandMetricExtractorConfig, input: ExtractCommandMetricInput): Promise<MetricResult>;
@@ -0,0 +1,93 @@
1
+ import { resolve } from "node:path";
2
+ import { execaCommand } from "execa";
3
+ export async function extractCommandMetric(config, input) {
4
+ const cwd = config.cwd ? resolve(input.workspacePath, config.cwd) : resolve(input.workspacePath);
5
+ const result = await execaCommand(config.command, {
6
+ cwd,
7
+ env: { ...process.env, ...config.env, ...input.env },
8
+ reject: false,
9
+ shell: true,
10
+ timeout: config.timeoutSec * 1_000,
11
+ });
12
+ if (result.exitCode !== 0) {
13
+ throw new Error(`command metric extractor failed with exit code ${result.exitCode}: ${result.stderr || result.stdout}`);
14
+ }
15
+ return {
16
+ metricId: input.metricId,
17
+ direction: input.direction,
18
+ value: parseMetricValue(result.stdout, config),
19
+ details: {
20
+ parser: config.parser,
21
+ command: config.command,
22
+ cwd,
23
+ },
24
+ };
25
+ }
26
+ function parseMetricValue(stdout, config) {
27
+ switch (config.parser) {
28
+ case "plain_number":
29
+ return parseNumericValue(stdout.trim(), "plain_number");
30
+ case "regex":
31
+ return parseRegexValue(stdout, config.pattern);
32
+ case "json_path":
33
+ return parseJsonPathValue(stdout, config.valuePath);
34
+ }
35
+ }
36
+ function parseRegexValue(stdout, pattern) {
37
+ if (!pattern) {
38
+ throw new Error('command metric extractor with parser="regex" requires a pattern');
39
+ }
40
+ const match = new RegExp(pattern, "m").exec(stdout);
41
+ if (!match) {
42
+ throw new Error(`regex parser did not match pattern ${pattern}`);
43
+ }
44
+ const candidate = match.groups?.value ?? match[1] ?? match[0];
45
+ return parseNumericValue(candidate, "regex");
46
+ }
47
+ function parseJsonPathValue(stdout, valuePath) {
48
+ if (!valuePath) {
49
+ throw new Error('command metric extractor with parser="json_path" requires a valuePath');
50
+ }
51
+ const json = JSON.parse(stdout);
52
+ const value = readJsonPath(json, valuePath);
53
+ return parseNumericValue(value, "json_path");
54
+ }
55
+ function readJsonPath(root, path) {
56
+ if (!path.startsWith("$")) {
57
+ throw new Error(`unsupported JSONPath: ${path}`);
58
+ }
59
+ const tokens = path
60
+ .slice(1)
61
+ .replace(/\[(\d+)\]/g, ".$1")
62
+ .split(".")
63
+ .filter(Boolean);
64
+ let current = root;
65
+ for (const token of tokens) {
66
+ if (current === null || current === undefined) {
67
+ throw new Error(`JSONPath ${path} resolved to undefined at token ${token}`);
68
+ }
69
+ if (Array.isArray(current)) {
70
+ const index = Number.parseInt(token, 10);
71
+ current = current[index];
72
+ continue;
73
+ }
74
+ if (typeof current === "object") {
75
+ current = current[token];
76
+ continue;
77
+ }
78
+ throw new Error(`JSONPath ${path} could not descend into non-object value at token ${token}`);
79
+ }
80
+ return current;
81
+ }
82
+ function parseNumericValue(value, parserName) {
83
+ const numericValue = typeof value === "number"
84
+ ? value
85
+ : typeof value === "string"
86
+ ? Number.parseFloat(value.trim())
87
+ : Number.NaN;
88
+ if (!Number.isFinite(numericValue)) {
89
+ throw new Error(`${parserName} parser did not resolve to a finite number`);
90
+ }
91
+ return numericValue;
92
+ }
93
+ //# sourceMappingURL=command-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-extractor.js","sourceRoot":"","sources":["../../../src/adapters/extractor/command-extractor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { LlmJudgeMetricExtractorConfig, JudgePack } from "../../core/manifest/schema.js";
2
+ import type { MetricResult } from "../../core/model/metric.js";
3
+ import type { JudgeProvider } from "../judge/llm-judge-provider.js";
4
+ export interface ExtractLlmJudgeMetricInput {
5
+ metricId: string;
6
+ direction: "maximize" | "minimize";
7
+ prompt: string;
8
+ }
9
+ export declare function extractLlmJudgeMetric(config: LlmJudgeMetricExtractorConfig, pack: JudgePack, input: ExtractLlmJudgeMetricInput, provider: JudgeProvider): Promise<MetricResult>;
@@ -0,0 +1,12 @@
1
+ import { runLlmJudgeMetric } from "../../core/engine/judge-pack.js";
2
+ export async function extractLlmJudgeMetric(config, pack, input, provider) {
3
+ return runLlmJudgeMetric({
4
+ metricId: input.metricId,
5
+ direction: input.direction,
6
+ extractor: config,
7
+ pack,
8
+ prompt: input.prompt,
9
+ provider,
10
+ });
11
+ }
12
+ //# sourceMappingURL=llm-judge-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-judge-extractor.js","sourceRoot":"","sources":["../../../src/adapters/extractor/llm-judge-extractor.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAQpE,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAqC,EACrC,IAAe,EACf,KAAiC,EACjC,QAAuB;IAEvB,OAAO,iBAAiB,CAAC;QACvB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,SAAS,EAAE,MAAM;QACjB,IAAI;QACJ,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,QAAQ;KACT,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { type DecisionRecord } from "../../core/model/decision-record.js";
2
+ import type { DecisionStore } from "../../core/ports/decision-store.js";
3
+ export declare class JsonFileDecisionStore implements DecisionStore {
4
+ private readonly rootDir;
5
+ constructor(rootDir: string);
6
+ put(record: DecisionRecord): Promise<void>;
7
+ get(decisionId: string): Promise<DecisionRecord | null>;
8
+ list(): Promise<DecisionRecord[]>;
9
+ private getPath;
10
+ }
@@ -0,0 +1,53 @@
1
+ import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { decisionRecordSchema } from "../../core/model/decision-record.js";
4
+ import { isMissingFileError } from "../../shared/fs-errors.js";
5
+ export class JsonFileDecisionStore {
6
+ rootDir;
7
+ constructor(rootDir) {
8
+ this.rootDir = resolve(rootDir);
9
+ }
10
+ async put(record) {
11
+ const parsed = decisionRecordSchema.parse(record);
12
+ const path = this.getPath(parsed.decisionId);
13
+ await mkdir(dirname(path), { recursive: true });
14
+ await writeFile(path, `${JSON.stringify(parsed, null, 2)}\n`, "utf8");
15
+ }
16
+ async get(decisionId) {
17
+ const path = this.getPath(decisionId);
18
+ try {
19
+ const raw = await readFile(path, "utf8");
20
+ return decisionRecordSchema.parse(JSON.parse(raw));
21
+ }
22
+ catch (error) {
23
+ if (isMissingFileError(error)) {
24
+ return null;
25
+ }
26
+ throw error;
27
+ }
28
+ }
29
+ async list() {
30
+ try {
31
+ const entries = await readdir(this.rootDir, { withFileTypes: true });
32
+ const records = [];
33
+ for (const entry of entries) {
34
+ if (!entry.isFile() || !entry.name.endsWith(".json")) {
35
+ continue;
36
+ }
37
+ const raw = await readFile(join(this.rootDir, entry.name), "utf8");
38
+ records.push(decisionRecordSchema.parse(JSON.parse(raw)));
39
+ }
40
+ return records.sort((left, right) => left.decisionId.localeCompare(right.decisionId));
41
+ }
42
+ catch (error) {
43
+ if (isMissingFileError(error)) {
44
+ return [];
45
+ }
46
+ throw error;
47
+ }
48
+ }
49
+ getPath(decisionId) {
50
+ return join(this.rootDir, `${decisionId}.json`);
51
+ }
52
+ }
53
+ //# sourceMappingURL=json-file-decision-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-file-decision-store.js","sourceRoot":"","sources":["../../../src/adapters/fs/json-file-decision-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAAE,oBAAoB,EAAuB,MAAM,qCAAqC,CAAC;AAEhG,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,OAAO,qBAAqB;IACf,OAAO,CAAS;IAEjC,YAAmB,OAAe;QAChC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,MAAsB;QACrC,MAAM,MAAM,GAAG,oBAAoB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC7C,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxE,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,UAAkB;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACtC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACzC,OAAO,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9B,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,IAAI;QACf,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YACrE,MAAM,OAAO,GAAqB,EAAE,CAAC;YACrC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;oBACrD,SAAS;gBACX,CAAC;gBACD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;gBACnE,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC5D,CAAC;YACD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;QACxF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9B,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAEO,OAAO,CAAC,UAAkB;QAChC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,UAAU,OAAO,CAAC,CAAC;IAClD,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ import { type FrontierEntry } from "../../core/model/frontier-entry.js";
2
+ import type { FrontierStore } from "../../core/ports/frontier-store.js";
3
+ export declare class JsonFileFrontierStore implements FrontierStore {
4
+ private readonly path;
5
+ constructor(path: string);
6
+ save(entries: FrontierEntry[]): Promise<void>;
7
+ load(): Promise<FrontierEntry[]>;
8
+ }
@@ -0,0 +1,29 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, resolve } from "node:path";
3
+ import { frontierEntrySchema } from "../../core/model/frontier-entry.js";
4
+ import { isMissingFileError } from "../../shared/fs-errors.js";
5
+ const frontierSnapshotSchema = frontierEntrySchema.array();
6
+ export class JsonFileFrontierStore {
7
+ path;
8
+ constructor(path) {
9
+ this.path = resolve(path);
10
+ }
11
+ async save(entries) {
12
+ const parsed = frontierSnapshotSchema.parse(entries);
13
+ await mkdir(dirname(this.path), { recursive: true });
14
+ await writeFile(this.path, `${JSON.stringify(parsed, null, 2)}\n`, "utf8");
15
+ }
16
+ async load() {
17
+ try {
18
+ const raw = await readFile(this.path, "utf8");
19
+ return frontierSnapshotSchema.parse(JSON.parse(raw));
20
+ }
21
+ catch (error) {
22
+ if (isMissingFileError(error)) {
23
+ return [];
24
+ }
25
+ throw error;
26
+ }
27
+ }
28
+ }
29
+ //# sourceMappingURL=json-file-frontier-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-file-frontier-store.js","sourceRoot":"","sources":["../../../src/adapters/fs/json-file-frontier-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,mBAAmB,EAAsB,MAAM,oCAAoC,CAAC;AAE7F,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,sBAAsB,GAAG,mBAAmB,CAAC,KAAK,EAAE,CAAC;AAE3D,MAAM,OAAO,qBAAqB;IACf,IAAI,CAAS;IAE9B,YAAmB,IAAY;QAC7B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,OAAwB;QACxC,MAAM,MAAM,GAAG,sBAAsB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACrD,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC7E,CAAC;IAEM,KAAK,CAAC,IAAI;QACf,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC9C,OAAO,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9B,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,10 @@
1
+ import { type RunRecord } from "../../core/model/run-record.js";
2
+ import type { RunStore } from "../../core/ports/run-store.js";
3
+ export declare class JsonFileRunStore implements RunStore {
4
+ private readonly rootDir;
5
+ constructor(rootDir: string);
6
+ put(record: RunRecord): Promise<void>;
7
+ get(runId: string): Promise<RunRecord | null>;
8
+ list(): Promise<RunRecord[]>;
9
+ private getPath;
10
+ }
@@ -0,0 +1,53 @@
1
+ import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { runRecordSchema } from "../../core/model/run-record.js";
4
+ import { isMissingFileError } from "../../shared/fs-errors.js";
5
+ export class JsonFileRunStore {
6
+ rootDir;
7
+ constructor(rootDir) {
8
+ this.rootDir = resolve(rootDir);
9
+ }
10
+ async put(record) {
11
+ const parsed = runRecordSchema.parse(record);
12
+ const path = this.getPath(parsed.runId);
13
+ await mkdir(dirname(path), { recursive: true });
14
+ await writeFile(path, `${JSON.stringify(parsed, null, 2)}\n`, "utf8");
15
+ }
16
+ async get(runId) {
17
+ const path = this.getPath(runId);
18
+ try {
19
+ const raw = await readFile(path, "utf8");
20
+ return runRecordSchema.parse(JSON.parse(raw));
21
+ }
22
+ catch (error) {
23
+ if (isMissingFileError(error)) {
24
+ return null;
25
+ }
26
+ throw error;
27
+ }
28
+ }
29
+ async list() {
30
+ try {
31
+ const entries = await readdir(this.rootDir, { withFileTypes: true });
32
+ const records = [];
33
+ for (const entry of entries) {
34
+ if (!entry.isFile() || !entry.name.endsWith(".json")) {
35
+ continue;
36
+ }
37
+ const raw = await readFile(join(this.rootDir, entry.name), "utf8");
38
+ records.push(runRecordSchema.parse(JSON.parse(raw)));
39
+ }
40
+ return records.sort((left, right) => left.runId.localeCompare(right.runId));
41
+ }
42
+ catch (error) {
43
+ if (isMissingFileError(error)) {
44
+ return [];
45
+ }
46
+ throw error;
47
+ }
48
+ }
49
+ getPath(runId) {
50
+ return join(this.rootDir, `${runId}.json`);
51
+ }
52
+ }
53
+ //# sourceMappingURL=json-file-run-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-file-run-store.js","sourceRoot":"","sources":["../../../src/adapters/fs/json-file-run-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAAE,eAAe,EAAkB,MAAM,gCAAgC,CAAC;AAEjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,OAAO,gBAAgB;IACV,OAAO,CAAS;IAEjC,YAAmB,OAAe;QAChC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,MAAiB;QAChC,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxE,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,KAAa;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACzC,OAAO,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9B,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,IAAI;QACf,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YACrE,MAAM,OAAO,GAAgB,EAAE,CAAC;YAChC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;oBACrD,SAAS;gBACX,CAAC;gBACD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;gBACnE,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACvD,CAAC;YACD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9B,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAEO,OAAO,CAAC,KAAa;QAC3B,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC;IAC7C,CAAC;CACF"}
@@ -0,0 +1,24 @@
1
+ import { z } from "zod";
2
+ declare const lockfileMetadataSchema: z.ZodObject<{
3
+ pid: z.ZodNumber;
4
+ token: z.ZodString;
5
+ createdAt: z.ZodString;
6
+ updatedAt: z.ZodString;
7
+ ttlMs: z.ZodNumber;
8
+ }, z.core.$strip>;
9
+ export type LockfileMetadata = z.infer<typeof lockfileMetadataSchema>;
10
+ export interface AcquireLockOptions {
11
+ ttlMs?: number;
12
+ }
13
+ export interface LockHandle {
14
+ path: string;
15
+ metadata: LockfileMetadata;
16
+ }
17
+ export declare class LockAcquisitionError extends Error {
18
+ constructor(message: string);
19
+ }
20
+ export declare function acquireLock(path: string, options?: AcquireLockOptions): Promise<LockHandle>;
21
+ export declare function releaseLock(path: string, token?: string): Promise<void>;
22
+ export declare function isStaleLock(path: string): Promise<boolean>;
23
+ export declare function readLockMetadata(path: string): Promise<LockfileMetadata | null>;
24
+ export {};
@@ -0,0 +1,110 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
3
+ import { dirname, resolve } from "node:path";
4
+ import { z } from "zod";
5
+ import { isMissingFileError } from "../../shared/fs-errors.js";
6
+ const lockfileMetadataSchema = z.object({
7
+ pid: z.number().int().positive(),
8
+ token: z.string().min(1),
9
+ createdAt: z.string().datetime(),
10
+ updatedAt: z.string().datetime(),
11
+ ttlMs: z.number().int().positive(),
12
+ });
13
+ export class LockAcquisitionError extends Error {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "LockAcquisitionError";
17
+ }
18
+ }
19
+ const DEFAULT_LOCK_TTL_MS = 5 * 60 * 1000;
20
+ export async function acquireLock(path, options = {}) {
21
+ const resolvedPath = resolve(path);
22
+ const ttlMs = options.ttlMs ?? DEFAULT_LOCK_TTL_MS;
23
+ await mkdir(dirname(resolvedPath), { recursive: true });
24
+ const metadata = {
25
+ pid: process.pid,
26
+ token: randomUUID(),
27
+ createdAt: new Date().toISOString(),
28
+ updatedAt: new Date().toISOString(),
29
+ ttlMs,
30
+ };
31
+ try {
32
+ await writeFile(resolvedPath, `${JSON.stringify(metadata, null, 2)}\n`, {
33
+ encoding: "utf8",
34
+ flag: "wx",
35
+ });
36
+ return { path: resolvedPath, metadata };
37
+ }
38
+ catch (error) {
39
+ if (!isAlreadyExistsError(error)) {
40
+ throw error;
41
+ }
42
+ }
43
+ const stale = await isStaleLock(resolvedPath);
44
+ if (!stale) {
45
+ throw new LockAcquisitionError(`Active lock already exists at ${resolvedPath}`);
46
+ }
47
+ await rm(resolvedPath, { force: true });
48
+ await writeFile(resolvedPath, `${JSON.stringify(metadata, null, 2)}\n`, {
49
+ encoding: "utf8",
50
+ flag: "wx",
51
+ });
52
+ return { path: resolvedPath, metadata };
53
+ }
54
+ export async function releaseLock(path, token) {
55
+ const resolvedPath = resolve(path);
56
+ const metadata = await readLockMetadata(resolvedPath);
57
+ if (!metadata) {
58
+ return;
59
+ }
60
+ if (token && metadata.token !== token) {
61
+ throw new LockAcquisitionError(`Refusing to release lock at ${resolvedPath}: token mismatch`);
62
+ }
63
+ await rm(resolvedPath, { force: true });
64
+ }
65
+ export async function isStaleLock(path) {
66
+ const metadata = await readLockMetadata(path);
67
+ if (!metadata) {
68
+ return false;
69
+ }
70
+ const ageMs = Date.now() - Date.parse(metadata.updatedAt);
71
+ if (ageMs > metadata.ttlMs) {
72
+ return true;
73
+ }
74
+ return !isProcessAlive(metadata.pid);
75
+ }
76
+ export async function readLockMetadata(path) {
77
+ const resolvedPath = resolve(path);
78
+ try {
79
+ const raw = await readFile(resolvedPath, "utf8");
80
+ return lockfileMetadataSchema.parse(JSON.parse(raw));
81
+ }
82
+ catch (error) {
83
+ if (isMissingFileError(error)) {
84
+ return null;
85
+ }
86
+ throw error;
87
+ }
88
+ }
89
+ function isAlreadyExistsError(error) {
90
+ return error instanceof Error && "code" in error && error.code === "EEXIST";
91
+ }
92
+ function isProcessAlive(pid) {
93
+ try {
94
+ process.kill(pid, 0);
95
+ return true;
96
+ }
97
+ catch (error) {
98
+ if (error instanceof Error && "code" in error) {
99
+ const code = error.code;
100
+ if (code === "ESRCH") {
101
+ return false;
102
+ }
103
+ if (code === "EPERM") {
104
+ return true;
105
+ }
106
+ }
107
+ return false;
108
+ }
109
+ }
110
+ //# sourceMappingURL=lockfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../../src/adapters/fs/lockfile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type RalphManifest } from "../../core/manifest/schema.js";
2
+ export interface LoadedManifest {
3
+ path: string;
4
+ manifest: RalphManifest;
5
+ }
6
+ export declare class ManifestLoadError extends Error {
7
+ readonly causeValue?: unknown;
8
+ constructor(message: string, causeValue?: unknown);
9
+ }
10
+ export declare function loadManifestFromFile(path?: string): Promise<LoadedManifest>;
@@ -0,0 +1,43 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import { parse } from "yaml";
4
+ import { ZodError } from "zod";
5
+ import { DEFAULT_MANIFEST_FILENAME, RalphManifestSchema } from "../../core/manifest/schema.js";
6
+ export class ManifestLoadError extends Error {
7
+ causeValue;
8
+ constructor(message, causeValue) {
9
+ super(message);
10
+ this.name = "ManifestLoadError";
11
+ this.causeValue = causeValue;
12
+ }
13
+ }
14
+ export async function loadManifestFromFile(path = DEFAULT_MANIFEST_FILENAME) {
15
+ const resolvedPath = resolve(path);
16
+ let rawText;
17
+ try {
18
+ rawText = await readFile(resolvedPath, "utf8");
19
+ }
20
+ catch (error) {
21
+ throw new ManifestLoadError(`Failed to read manifest at ${resolvedPath}`, error);
22
+ }
23
+ let parsedYaml;
24
+ try {
25
+ parsedYaml = parse(rawText);
26
+ }
27
+ catch (error) {
28
+ throw new ManifestLoadError(`Failed to parse YAML from ${resolvedPath}`, error);
29
+ }
30
+ try {
31
+ return {
32
+ path: resolvedPath,
33
+ manifest: RalphManifestSchema.parse(parsedYaml),
34
+ };
35
+ }
36
+ catch (error) {
37
+ if (error instanceof ZodError) {
38
+ throw new ManifestLoadError(`Manifest validation failed for ${resolvedPath}`, error.flatten());
39
+ }
40
+ throw new ManifestLoadError(`Manifest validation failed for ${resolvedPath}`, error);
41
+ }
42
+ }
43
+ //# sourceMappingURL=manifest-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-loader.js","sourceRoot":"","sources":["../../../src/adapters/fs/manifest-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAE/B,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAsB,MAAM,+BAA+B,CAAC;AAOnH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1B,UAAU,CAAW;IAErC,YAAmB,OAAe,EAAE,UAAoB;QACtD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAAI,GAAG,yBAAyB;IACzE,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,iBAAiB,CAAC,8BAA8B,YAAY,EAAE,EAAE,KAAK,CAAC,CAAC;IACnF,CAAC;IAED,IAAI,UAAmB,CAAC;IACxB,IAAI,CAAC;QACH,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,iBAAiB,CAAC,6BAA6B,YAAY,EAAE,EAAE,KAAK,CAAC,CAAC;IAClF,CAAC;IAED,IAAI,CAAC;QACH,OAAO;YACL,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,mBAAmB,CAAC,KAAK,CAAC,UAAU,CAAC;SAChD,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,iBAAiB,CAAC,kCAAkC,YAAY,EAAE,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACjG,CAAC;QAED,MAAM,IAAI,iBAAiB,CAAC,kCAAkC,YAAY,EAAE,EAAE,KAAK,CAAC,CAAC;IACvF,CAAC;AACH,CAAC"}