ralph-research 0.1.0 → 0.1.2
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 +69 -0
- package/dist/adapters/extractor/command-extractor.js +46 -4
- package/dist/adapters/extractor/command-extractor.js.map +1 -1
- package/dist/adapters/fs/json-file-frontier-store.js +4 -2
- package/dist/adapters/fs/json-file-frontier-store.js.map +1 -1
- package/dist/adapters/fs/lockfile.d.ts +21 -1
- package/dist/adapters/fs/lockfile.js +65 -11
- package/dist/adapters/fs/lockfile.js.map +1 -1
- package/dist/adapters/fs/manifest-loader.d.ts +5 -1
- package/dist/adapters/fs/manifest-loader.js +15 -2
- package/dist/adapters/fs/manifest-loader.js.map +1 -1
- package/dist/adapters/git/git-client.d.ts +2 -0
- package/dist/adapters/git/git-client.js +19 -0
- package/dist/adapters/git/git-client.js.map +1 -1
- package/dist/app/services/manual-decision-service.js +76 -25
- package/dist/app/services/manual-decision-service.js.map +1 -1
- package/dist/app/services/project-state-service.d.ts +26 -1
- package/dist/app/services/project-state-service.js +172 -14
- package/dist/app/services/project-state-service.js.map +1 -1
- package/dist/app/services/run-admission-service.d.ts +20 -0
- package/dist/app/services/run-admission-service.js +30 -0
- package/dist/app/services/run-admission-service.js.map +1 -0
- package/dist/app/services/run-cycle-service.d.ts +5 -4
- package/dist/app/services/run-cycle-service.js +175 -14
- package/dist/app/services/run-cycle-service.js.map +1 -1
- package/dist/app/services/run-loop-service.d.ts +21 -0
- package/dist/app/services/run-loop-service.js +155 -0
- package/dist/app/services/run-loop-service.js.map +1 -0
- package/dist/cli/commands/demo.js +1 -0
- package/dist/cli/commands/demo.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +8 -0
- package/dist/cli/commands/doctor.js +59 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/init.js +1 -0
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/inspect.js +4 -0
- package/dist/cli/commands/inspect.js.map +1 -1
- package/dist/cli/commands/run.d.ts +3 -1
- package/dist/cli/commands/run.js +31 -28
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/status.js +35 -4
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/validate.js +21 -18
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/main.js +3 -10
- package/dist/cli/main.js.map +1 -1
- package/dist/core/engine/cycle-runner.d.ts +2 -0
- package/dist/core/engine/cycle-runner.js +504 -34
- package/dist/core/engine/cycle-runner.js.map +1 -1
- package/dist/core/engine/promotion-artifact.d.ts +23 -0
- package/dist/core/engine/promotion-artifact.js +58 -0
- package/dist/core/engine/promotion-artifact.js.map +1 -0
- package/dist/core/engine/workspace-manager.d.ts +10 -1
- package/dist/core/engine/workspace-manager.js +70 -3
- package/dist/core/engine/workspace-manager.js.map +1 -1
- package/dist/core/manifest/admission.d.ts +16 -0
- package/dist/core/manifest/admission.js +64 -0
- package/dist/core/manifest/admission.js.map +1 -0
- package/dist/core/manifest/schema.d.ts +47 -0
- package/dist/core/manifest/schema.js +18 -1
- package/dist/core/manifest/schema.js.map +1 -1
- package/dist/core/model/decision-record.d.ts +4 -0
- package/dist/core/model/decision-record.js +6 -0
- package/dist/core/model/decision-record.js.map +1 -1
- package/dist/core/model/metric-diagnostics.d.ts +7 -0
- package/dist/core/model/metric-diagnostics.js +51 -0
- package/dist/core/model/metric-diagnostics.js.map +1 -0
- package/dist/core/model/run-record.d.ts +6 -0
- package/dist/core/model/run-record.js +4 -0
- package/dist/core/model/run-record.js.map +1 -1
- package/dist/core/state/frontier-materializer.d.ts +12 -0
- package/dist/core/state/frontier-materializer.js +74 -0
- package/dist/core/state/frontier-materializer.js.map +1 -0
- package/dist/core/state/frontier-semantics.d.ts +12 -0
- package/dist/core/state/frontier-semantics.js +26 -0
- package/dist/core/state/frontier-semantics.js.map +1 -0
- package/dist/core/state/ratchet-engine.js +29 -21
- package/dist/core/state/ratchet-engine.js.map +1 -1
- package/dist/core/state/recovery-classifier.d.ts +17 -0
- package/dist/core/state/recovery-classifier.js +150 -0
- package/dist/core/state/recovery-classifier.js.map +1 -0
- package/dist/core/state/run-state-machine.js +33 -23
- package/dist/core/state/run-state-machine.js.map +1 -1
- package/dist/core/state/stopping-target.d.ts +14 -0
- package/dist/core/state/stopping-target.js +67 -0
- package/dist/core/state/stopping-target.js.map +1 -0
- package/dist/mcp/server.js +17 -23
- package/dist/mcp/server.js.map +1 -1
- package/package.json +2 -2
- package/templates/writing/ralph.yaml +7 -0
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@ This creates a temporary writing repo, runs one accepted cycle, and prints the p
|
|
|
26
26
|
```bash
|
|
27
27
|
npx ralph-research init --template writing
|
|
28
28
|
npx ralph-research run --json
|
|
29
|
+
npx ralph-research run --until-target --until-no-improve 3 --json
|
|
29
30
|
npx ralph-research inspect run-0001 --json
|
|
30
31
|
```
|
|
31
32
|
|
|
@@ -60,6 +61,8 @@ rrx doctor
|
|
|
60
61
|
rrx init --template writing
|
|
61
62
|
rrx demo writing
|
|
62
63
|
rrx run
|
|
64
|
+
rrx run --until-target
|
|
65
|
+
rrx run --until-target --until-no-improve 3
|
|
63
66
|
rrx status
|
|
64
67
|
rrx frontier
|
|
65
68
|
rrx inspect <runId>
|
|
@@ -68,6 +71,72 @@ rrx reject <runId>
|
|
|
68
71
|
rrx serve-mcp --stdio
|
|
69
72
|
```
|
|
70
73
|
|
|
74
|
+
`rrx run --cycles N` still executes a finite loop. Progressive modes are opt-in:
|
|
75
|
+
|
|
76
|
+
- `--until-target`: keep iterating until `manifest.stopping.target` is satisfied
|
|
77
|
+
- `--until-no-improve N`: stop after `N` consecutive cycles without a frontier improvement
|
|
78
|
+
- `--cycles N` with a progressive flag: treat `N` as a max-cycle cap instead of an exact count
|
|
79
|
+
|
|
80
|
+
`rrx status` now reports both the persisted latest run snapshot and the runtime view derived from the lock heartbeat, so `running (alive)` is distinguished from `stale (resumable)` and the output includes heartbeat and last-progress timestamps when available.
|
|
81
|
+
|
|
82
|
+
## Stopping Targets
|
|
83
|
+
|
|
84
|
+
Use `stopping.target` when the workflow contract is "keep going until metric X reaches threshold Y":
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
metrics:
|
|
88
|
+
catalog:
|
|
89
|
+
- id: exact_rate
|
|
90
|
+
kind: numeric
|
|
91
|
+
direction: maximize
|
|
92
|
+
extractor:
|
|
93
|
+
type: command
|
|
94
|
+
command: "python scripts/metric.py"
|
|
95
|
+
parser: plain_number
|
|
96
|
+
|
|
97
|
+
frontier:
|
|
98
|
+
strategy: single_best
|
|
99
|
+
primaryMetric: exact_rate
|
|
100
|
+
|
|
101
|
+
ratchet:
|
|
102
|
+
type: epsilon_improve
|
|
103
|
+
metric: exact_rate
|
|
104
|
+
epsilon: 0
|
|
105
|
+
|
|
106
|
+
stopping:
|
|
107
|
+
target:
|
|
108
|
+
metric: exact_rate
|
|
109
|
+
op: ">="
|
|
110
|
+
value: 0.8
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Metric Diagnostics
|
|
114
|
+
|
|
115
|
+
If a metric script can explain why a candidate was zeroed or downgraded, prefer JSON output plus `parser: json_path` so the reason survives into `run`, `decision`, and `inspect` output:
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
metrics:
|
|
119
|
+
catalog:
|
|
120
|
+
- id: exact_rate
|
|
121
|
+
kind: numeric
|
|
122
|
+
direction: maximize
|
|
123
|
+
extractor:
|
|
124
|
+
type: command
|
|
125
|
+
command: "python scripts/metric.py"
|
|
126
|
+
parser: json_path
|
|
127
|
+
valuePath: $.value
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"value": 0,
|
|
133
|
+
"metricId": "overfit_safe_exact_rate",
|
|
134
|
+
"reasons": ["all_missing_features", "normalized_order_leak"]
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
When `project.workspace=git`, rrx now warns if proposer, experiment, or metric command files are dirty in the working tree, because detached candidate worktrees only see committed baseline content.
|
|
139
|
+
|
|
71
140
|
## MCP
|
|
72
141
|
|
|
73
142
|
The bundled MCP server currently supports stdio transport and exposes three thin tools backed by the same service layer as the CLI:
|
|
@@ -12,23 +12,29 @@ export async function extractCommandMetric(config, input) {
|
|
|
12
12
|
if (result.exitCode !== 0) {
|
|
13
13
|
throw new Error(`command metric extractor failed with exit code ${result.exitCode}: ${result.stderr || result.stdout}`);
|
|
14
14
|
}
|
|
15
|
+
const parsedMetric = parseMetricValue(result.stdout, config);
|
|
15
16
|
return {
|
|
16
17
|
metricId: input.metricId,
|
|
17
18
|
direction: input.direction,
|
|
18
|
-
value:
|
|
19
|
+
value: parsedMetric.value,
|
|
19
20
|
details: {
|
|
20
21
|
parser: config.parser,
|
|
21
22
|
command: config.command,
|
|
22
23
|
cwd,
|
|
24
|
+
...(parsedMetric.details ? parsedMetric.details : {}),
|
|
23
25
|
},
|
|
24
26
|
};
|
|
25
27
|
}
|
|
26
28
|
function parseMetricValue(stdout, config) {
|
|
27
29
|
switch (config.parser) {
|
|
28
30
|
case "plain_number":
|
|
29
|
-
return
|
|
31
|
+
return {
|
|
32
|
+
value: parseNumericValue(stdout.trim(), "plain_number"),
|
|
33
|
+
};
|
|
30
34
|
case "regex":
|
|
31
|
-
return
|
|
35
|
+
return {
|
|
36
|
+
value: parseRegexValue(stdout, config.pattern),
|
|
37
|
+
};
|
|
32
38
|
case "json_path":
|
|
33
39
|
return parseJsonPathValue(stdout, config.valuePath);
|
|
34
40
|
}
|
|
@@ -50,7 +56,11 @@ function parseJsonPathValue(stdout, valuePath) {
|
|
|
50
56
|
}
|
|
51
57
|
const json = JSON.parse(stdout);
|
|
52
58
|
const value = readJsonPath(json, valuePath);
|
|
53
|
-
|
|
59
|
+
const details = extractJsonMetricDetails(json);
|
|
60
|
+
return {
|
|
61
|
+
value: parseNumericValue(value, "json_path"),
|
|
62
|
+
...(Object.keys(details).length > 0 ? { details } : {}),
|
|
63
|
+
};
|
|
54
64
|
}
|
|
55
65
|
function readJsonPath(root, path) {
|
|
56
66
|
if (!path.startsWith("$")) {
|
|
@@ -90,4 +100,36 @@ function parseNumericValue(value, parserName) {
|
|
|
90
100
|
}
|
|
91
101
|
return numericValue;
|
|
92
102
|
}
|
|
103
|
+
function extractJsonMetricDetails(root) {
|
|
104
|
+
if (!root || typeof root !== "object" || Array.isArray(root)) {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
const record = root;
|
|
108
|
+
const details = {};
|
|
109
|
+
if (typeof record.reason === "string") {
|
|
110
|
+
details.reason = record.reason;
|
|
111
|
+
}
|
|
112
|
+
if (Array.isArray(record.reasons)) {
|
|
113
|
+
details.reasons = record.reasons.filter((entry) => typeof entry === "string");
|
|
114
|
+
}
|
|
115
|
+
if (typeof record.metricId === "string") {
|
|
116
|
+
details.sourceMetricId = record.metricId;
|
|
117
|
+
}
|
|
118
|
+
if (typeof record.sourceMetricId === "string") {
|
|
119
|
+
details.sourceMetricId = record.sourceMetricId;
|
|
120
|
+
}
|
|
121
|
+
if (Array.isArray(record.invalidReasons)) {
|
|
122
|
+
details.invalidReasons = record.invalidReasons.filter((entry) => typeof entry === "string");
|
|
123
|
+
}
|
|
124
|
+
if (Array.isArray(record.flags)) {
|
|
125
|
+
details.flags = record.flags.filter((entry) => typeof entry === "string");
|
|
126
|
+
}
|
|
127
|
+
if (Array.isArray(record.diagnostics)) {
|
|
128
|
+
details.diagnostics = record.diagnostics.filter((entry) => typeof entry === "string");
|
|
129
|
+
}
|
|
130
|
+
if (record.details && typeof record.details === "object" && !Array.isArray(record.details)) {
|
|
131
|
+
Object.assign(details, record.details);
|
|
132
|
+
}
|
|
133
|
+
return details;
|
|
134
|
+
}
|
|
93
135
|
//# sourceMappingURL=command-extractor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-extractor.js","sourceRoot":"","sources":["../../../src/adapters/extractor/command-extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAYrC,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAoC,EACpC,KAAgC;IAEhC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IACjG,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,OAAO,EAAE;QAChD,GAAG;QACH,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,EAAE;QACpD,MAAM,EAAE,KAAK;QACb,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,MAAM,CAAC,UAAU,GAAG,KAAK;KACnC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,kDAAkD,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1H,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"command-extractor.js","sourceRoot":"","sources":["../../../src/adapters/extractor/command-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { frontierEntrySchema } from "../../core/model/frontier-entry.js";
|
|
4
4
|
import { isMissingFileError } from "../../shared/fs-errors.js";
|
|
@@ -10,8 +10,10 @@ export class JsonFileFrontierStore {
|
|
|
10
10
|
}
|
|
11
11
|
async save(entries) {
|
|
12
12
|
const parsed = frontierSnapshotSchema.parse(entries);
|
|
13
|
+
const tempPath = `${this.path}.tmp-${process.pid}`;
|
|
13
14
|
await mkdir(dirname(this.path), { recursive: true });
|
|
14
|
-
await writeFile(
|
|
15
|
+
await writeFile(tempPath, `${JSON.stringify(parsed, null, 2)}\n`, "utf8");
|
|
16
|
+
await rename(tempPath, this.path);
|
|
15
17
|
}
|
|
16
18
|
async load() {
|
|
17
19
|
try {
|
|
@@ -1 +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;
|
|
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,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtE,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,QAAQ,GAAG,GAAG,IAAI,CAAC,IAAI,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAC;QACnD,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,MAAM,SAAS,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1E,MAAM,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,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"}
|
|
@@ -5,20 +5,40 @@ declare const lockfileMetadataSchema: z.ZodObject<{
|
|
|
5
5
|
createdAt: z.ZodString;
|
|
6
6
|
updatedAt: z.ZodString;
|
|
7
7
|
ttlMs: z.ZodNumber;
|
|
8
|
+
graceMs: z.ZodDefault<z.ZodNumber>;
|
|
9
|
+
owner: z.ZodOptional<z.ZodObject<{
|
|
10
|
+
runId: z.ZodOptional<z.ZodString>;
|
|
11
|
+
operation: z.ZodOptional<z.ZodString>;
|
|
12
|
+
}, z.core.$strip>>;
|
|
8
13
|
}, z.core.$strip>;
|
|
9
14
|
export type LockfileMetadata = z.infer<typeof lockfileMetadataSchema>;
|
|
10
15
|
export interface AcquireLockOptions {
|
|
11
16
|
ttlMs?: number;
|
|
17
|
+
graceMs?: number;
|
|
18
|
+
owner?: LockfileMetadata["owner"];
|
|
12
19
|
}
|
|
13
20
|
export interface LockHandle {
|
|
14
21
|
path: string;
|
|
15
22
|
metadata: LockfileMetadata;
|
|
16
23
|
}
|
|
24
|
+
export interface LockRuntimeState {
|
|
25
|
+
metadata: LockfileMetadata;
|
|
26
|
+
processAlive: boolean;
|
|
27
|
+
stale: boolean;
|
|
28
|
+
heartbeatAgeMs: number;
|
|
29
|
+
}
|
|
17
30
|
export declare class LockAcquisitionError extends Error {
|
|
18
|
-
|
|
31
|
+
readonly metadata: LockfileMetadata | undefined;
|
|
32
|
+
readonly heartbeatAgeMs: number | undefined;
|
|
33
|
+
constructor(message: string, options?: {
|
|
34
|
+
metadata?: LockfileMetadata;
|
|
35
|
+
heartbeatAgeMs?: number;
|
|
36
|
+
});
|
|
19
37
|
}
|
|
20
38
|
export declare function acquireLock(path: string, options?: AcquireLockOptions): Promise<LockHandle>;
|
|
39
|
+
export declare function renewLock(path: string, token: string): Promise<LockfileMetadata>;
|
|
21
40
|
export declare function releaseLock(path: string, token?: string): Promise<void>;
|
|
22
41
|
export declare function isStaleLock(path: string): Promise<boolean>;
|
|
42
|
+
export declare function inspectLock(path: string): Promise<LockRuntimeState | null>;
|
|
23
43
|
export declare function readLockMetadata(path: string): Promise<LockfileMetadata | null>;
|
|
24
44
|
export {};
|
|
@@ -3,30 +3,45 @@ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { isMissingFileError } from "../../shared/fs-errors.js";
|
|
6
|
+
const DEFAULT_LOCK_TTL_MS = 5 * 60 * 1000;
|
|
7
|
+
const DEFAULT_LOCK_GRACE_MS = 30 * 1000;
|
|
8
|
+
const lockOwnerSchema = z.object({
|
|
9
|
+
runId: z.string().min(1).optional(),
|
|
10
|
+
operation: z.string().min(1).optional(),
|
|
11
|
+
});
|
|
6
12
|
const lockfileMetadataSchema = z.object({
|
|
7
13
|
pid: z.number().int().positive(),
|
|
8
14
|
token: z.string().min(1),
|
|
9
15
|
createdAt: z.string().datetime(),
|
|
10
16
|
updatedAt: z.string().datetime(),
|
|
11
17
|
ttlMs: z.number().int().positive(),
|
|
18
|
+
graceMs: z.number().int().nonnegative().default(DEFAULT_LOCK_GRACE_MS),
|
|
19
|
+
owner: lockOwnerSchema.optional(),
|
|
12
20
|
});
|
|
13
21
|
export class LockAcquisitionError extends Error {
|
|
14
|
-
|
|
22
|
+
metadata;
|
|
23
|
+
heartbeatAgeMs;
|
|
24
|
+
constructor(message, options = {}) {
|
|
15
25
|
super(message);
|
|
16
26
|
this.name = "LockAcquisitionError";
|
|
27
|
+
this.metadata = options.metadata;
|
|
28
|
+
this.heartbeatAgeMs = options.heartbeatAgeMs;
|
|
17
29
|
}
|
|
18
30
|
}
|
|
19
|
-
const DEFAULT_LOCK_TTL_MS = 5 * 60 * 1000;
|
|
20
31
|
export async function acquireLock(path, options = {}) {
|
|
21
32
|
const resolvedPath = resolve(path);
|
|
22
33
|
const ttlMs = options.ttlMs ?? DEFAULT_LOCK_TTL_MS;
|
|
34
|
+
const graceMs = options.graceMs ?? DEFAULT_LOCK_GRACE_MS;
|
|
23
35
|
await mkdir(dirname(resolvedPath), { recursive: true });
|
|
36
|
+
const now = new Date().toISOString();
|
|
24
37
|
const metadata = {
|
|
25
38
|
pid: process.pid,
|
|
26
39
|
token: randomUUID(),
|
|
27
|
-
createdAt:
|
|
28
|
-
updatedAt:
|
|
40
|
+
createdAt: now,
|
|
41
|
+
updatedAt: now,
|
|
29
42
|
ttlMs,
|
|
43
|
+
graceMs,
|
|
44
|
+
...(options.owner ? { owner: options.owner } : {}),
|
|
30
45
|
};
|
|
31
46
|
try {
|
|
32
47
|
await writeFile(resolvedPath, `${JSON.stringify(metadata, null, 2)}\n`, {
|
|
@@ -40,9 +55,20 @@ export async function acquireLock(path, options = {}) {
|
|
|
40
55
|
throw error;
|
|
41
56
|
}
|
|
42
57
|
}
|
|
58
|
+
const existing = await readLockMetadata(resolvedPath);
|
|
43
59
|
const stale = await isStaleLock(resolvedPath);
|
|
44
60
|
if (!stale) {
|
|
45
|
-
|
|
61
|
+
const heartbeatAgeMs = existing ? Date.now() - Date.parse(existing.updatedAt) : undefined;
|
|
62
|
+
const details = [
|
|
63
|
+
`Active lock already exists at ${resolvedPath}`,
|
|
64
|
+
existing ? `pid=${existing.pid}` : null,
|
|
65
|
+
existing?.owner?.runId ? `runId=${existing.owner.runId}` : null,
|
|
66
|
+
heartbeatAgeMs === undefined ? null : `heartbeatAgeMs=${heartbeatAgeMs}`,
|
|
67
|
+
].filter(Boolean).join(" ");
|
|
68
|
+
throw new LockAcquisitionError(details, {
|
|
69
|
+
...(existing ? { metadata: existing } : {}),
|
|
70
|
+
...(heartbeatAgeMs === undefined ? {} : { heartbeatAgeMs }),
|
|
71
|
+
});
|
|
46
72
|
}
|
|
47
73
|
await rm(resolvedPath, { force: true });
|
|
48
74
|
await writeFile(resolvedPath, `${JSON.stringify(metadata, null, 2)}\n`, {
|
|
@@ -51,6 +77,24 @@ export async function acquireLock(path, options = {}) {
|
|
|
51
77
|
});
|
|
52
78
|
return { path: resolvedPath, metadata };
|
|
53
79
|
}
|
|
80
|
+
export async function renewLock(path, token) {
|
|
81
|
+
const resolvedPath = resolve(path);
|
|
82
|
+
const metadata = await readLockMetadata(resolvedPath);
|
|
83
|
+
if (!metadata) {
|
|
84
|
+
throw new LockAcquisitionError(`Refusing to renew lock at ${resolvedPath}: lock is missing`);
|
|
85
|
+
}
|
|
86
|
+
if (metadata.token !== token) {
|
|
87
|
+
throw new LockAcquisitionError(`Refusing to renew lock at ${resolvedPath}: token mismatch`, {
|
|
88
|
+
metadata,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const updated = {
|
|
92
|
+
...metadata,
|
|
93
|
+
updatedAt: new Date().toISOString(),
|
|
94
|
+
};
|
|
95
|
+
await writeFile(resolvedPath, `${JSON.stringify(updated, null, 2)}\n`, "utf8");
|
|
96
|
+
return updated;
|
|
97
|
+
}
|
|
54
98
|
export async function releaseLock(path, token) {
|
|
55
99
|
const resolvedPath = resolve(path);
|
|
56
100
|
const metadata = await readLockMetadata(resolvedPath);
|
|
@@ -63,15 +107,25 @@ export async function releaseLock(path, token) {
|
|
|
63
107
|
await rm(resolvedPath, { force: true });
|
|
64
108
|
}
|
|
65
109
|
export async function isStaleLock(path) {
|
|
66
|
-
const
|
|
67
|
-
if (!
|
|
110
|
+
const runtime = await inspectLock(path);
|
|
111
|
+
if (!runtime) {
|
|
68
112
|
return false;
|
|
69
113
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
114
|
+
return runtime.stale;
|
|
115
|
+
}
|
|
116
|
+
export async function inspectLock(path) {
|
|
117
|
+
const metadata = await readLockMetadata(path);
|
|
118
|
+
if (!metadata) {
|
|
119
|
+
return null;
|
|
73
120
|
}
|
|
74
|
-
|
|
121
|
+
const heartbeatAgeMs = Date.now() - Date.parse(metadata.updatedAt);
|
|
122
|
+
const processAlive = isProcessAlive(metadata.pid);
|
|
123
|
+
return {
|
|
124
|
+
metadata,
|
|
125
|
+
processAlive,
|
|
126
|
+
stale: heartbeatAgeMs > metadata.ttlMs + metadata.graceMs || !processAlive,
|
|
127
|
+
heartbeatAgeMs,
|
|
128
|
+
};
|
|
75
129
|
}
|
|
76
130
|
export async function readLockMetadata(path) {
|
|
77
131
|
const resolvedPath = resolve(path);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../../src/adapters/fs/lockfile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAChC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACxB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;
|
|
1
|
+
{"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../../src/adapters/fs/lockfile.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,9 +2,13 @@ import { type RalphManifest } from "../../core/manifest/schema.js";
|
|
|
2
2
|
export interface LoadedManifest {
|
|
3
3
|
path: string;
|
|
4
4
|
manifest: RalphManifest;
|
|
5
|
+
resolvedBaselineRef: string;
|
|
5
6
|
}
|
|
6
7
|
export declare class ManifestLoadError extends Error {
|
|
7
8
|
readonly causeValue?: unknown;
|
|
8
9
|
constructor(message: string, causeValue?: unknown);
|
|
9
10
|
}
|
|
10
|
-
export
|
|
11
|
+
export interface LoadManifestOptions {
|
|
12
|
+
repoRoot?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function loadManifestFromFile(path?: string, options?: LoadManifestOptions): Promise<LoadedManifest>;
|
|
@@ -2,6 +2,7 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { parse } from "yaml";
|
|
4
4
|
import { ZodError } from "zod";
|
|
5
|
+
import { compileManifestAdmission } from "../../core/manifest/admission.js";
|
|
5
6
|
import { DEFAULT_MANIFEST_FILENAME, RalphManifestSchema } from "../../core/manifest/schema.js";
|
|
6
7
|
export class ManifestLoadError extends Error {
|
|
7
8
|
causeValue;
|
|
@@ -11,7 +12,7 @@ export class ManifestLoadError extends Error {
|
|
|
11
12
|
this.causeValue = causeValue;
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
|
-
export async function loadManifestFromFile(path = DEFAULT_MANIFEST_FILENAME) {
|
|
15
|
+
export async function loadManifestFromFile(path = DEFAULT_MANIFEST_FILENAME, options = {}) {
|
|
15
16
|
const resolvedPath = resolve(path);
|
|
16
17
|
let rawText;
|
|
17
18
|
try {
|
|
@@ -28,12 +29,24 @@ export async function loadManifestFromFile(path = DEFAULT_MANIFEST_FILENAME) {
|
|
|
28
29
|
throw new ManifestLoadError(`Failed to parse YAML from ${resolvedPath}`, error);
|
|
29
30
|
}
|
|
30
31
|
try {
|
|
32
|
+
const manifest = RalphManifestSchema.parse(parsedYaml);
|
|
33
|
+
const admission = await compileManifestAdmission(manifest, options);
|
|
34
|
+
if (!admission.executable) {
|
|
35
|
+
throw new ManifestLoadError(`Manifest admission failed for ${resolvedPath}`, {
|
|
36
|
+
executable: false,
|
|
37
|
+
issues: admission.issues,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
31
40
|
return {
|
|
32
41
|
path: resolvedPath,
|
|
33
|
-
manifest
|
|
42
|
+
manifest,
|
|
43
|
+
resolvedBaselineRef: admission.resolvedBaselineRef,
|
|
34
44
|
};
|
|
35
45
|
}
|
|
36
46
|
catch (error) {
|
|
47
|
+
if (error instanceof ManifestLoadError) {
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
37
50
|
if (error instanceof ZodError) {
|
|
38
51
|
throw new ManifestLoadError(`Manifest validation failed for ${resolvedPath}`, error.flatten());
|
|
39
52
|
}
|
|
@@ -1 +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;
|
|
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,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAsB,MAAM,+BAA+B,CAAC;AAQnH,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;AAMD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAI,GAAG,yBAAyB,EAChC,UAA+B,EAAE;IAEjC,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,MAAM,QAAQ,GAAG,mBAAmB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,wBAAwB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEpE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,IAAI,iBAAiB,CAAC,iCAAiC,YAAY,EAAE,EAAE;gBAC3E,UAAU,EAAE,KAAK;gBACjB,MAAM,EAAE,SAAS,CAAC,MAAM;aACzB,CAAC,CAAC;QACL,CAAC;QAED,OAAO;YACL,IAAI,EAAE,YAAY;YAClB,QAAQ;YACR,mBAAmB,EAAE,SAAS,CAAC,mBAAmB;SACnD,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,iBAAiB,EAAE,CAAC;YACvC,MAAM,KAAK,CAAC;QACd,CAAC;QAED,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"}
|
|
@@ -6,4 +6,6 @@ export declare class GitClient {
|
|
|
6
6
|
constructor(repoRoot: string);
|
|
7
7
|
stageAndCommitPaths(paths: string[], message: string): Promise<CommitResult>;
|
|
8
8
|
getHeadSha(): Promise<string>;
|
|
9
|
+
applyPatchIfNeeded(patchPath: string): Promise<"applied" | "already_applied">;
|
|
10
|
+
private canApplyPatch;
|
|
9
11
|
}
|
|
@@ -19,5 +19,24 @@ export class GitClient {
|
|
|
19
19
|
const { stdout } = await execa("git", ["-C", this.repoRoot, "rev-parse", "HEAD"]);
|
|
20
20
|
return stdout.trim();
|
|
21
21
|
}
|
|
22
|
+
async applyPatchIfNeeded(patchPath) {
|
|
23
|
+
if (await this.canApplyPatch(["apply", "--check", "--3way", "--index", "-p2", patchPath])) {
|
|
24
|
+
await execa("git", ["-C", this.repoRoot, "apply", "--3way", "--index", "-p2", patchPath]);
|
|
25
|
+
return "applied";
|
|
26
|
+
}
|
|
27
|
+
if (await this.canApplyPatch(["apply", "--check", "--reverse", "--index", "-p2", patchPath])) {
|
|
28
|
+
return "already_applied";
|
|
29
|
+
}
|
|
30
|
+
throw new Error(`repository state diverged from durable promotion patch ${patchPath}`);
|
|
31
|
+
}
|
|
32
|
+
async canApplyPatch(args) {
|
|
33
|
+
try {
|
|
34
|
+
await execa("git", ["-C", this.repoRoot, ...args]);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
22
41
|
}
|
|
23
42
|
//# sourceMappingURL=git-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-client.js","sourceRoot":"","sources":["../../../src/adapters/git/git-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAM9B,MAAM,OAAO,SAAS;IACH,QAAQ,CAAS;IAElC,YAAmB,QAAgB;QACjC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAEM,KAAK,CAAC,mBAAmB,CAAC,KAAe,EAAE,OAAe;QAC/D,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACnF,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;QACpF,CAAC;QAED,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC;QAC7E,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACnE,OAAO;YACL,SAAS,EAAE,MAAM,IAAI,CAAC,UAAU,EAAE;SACnC,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,UAAU;QACrB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;QAClF,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"git-client.js","sourceRoot":"","sources":["../../../src/adapters/git/git-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAM9B,MAAM,OAAO,SAAS;IACH,QAAQ,CAAS;IAElC,YAAmB,QAAgB;QACjC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAEM,KAAK,CAAC,mBAAmB,CAAC,KAAe,EAAE,OAAe;QAC/D,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACnF,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;QACpF,CAAC;QAED,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC;QAC7E,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACnE,OAAO;YACL,SAAS,EAAE,MAAM,IAAI,CAAC,UAAU,EAAE;SACnC,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,UAAU;QACrB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;QAClF,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC;IAEM,KAAK,CAAC,kBAAkB,CAAC,SAAiB;QAC/C,IAAI,MAAM,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;YAC1F,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;YAC1F,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,IAAI,MAAM,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;YAC7F,OAAO,iBAAiB,CAAC;QAC3B,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,0DAA0D,SAAS,EAAE,CAAC,CAAC;IACzF,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,IAAc;QACxC,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
|