@codyswann/lisa 2.261.0 → 2.263.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/all/copy-contents/gitignore +2 -0
- package/dist/cli/ui-config-write.d.ts.map +1 -1
- package/dist/cli/ui-config-write.js +24 -1
- package/dist/cli/ui-config-write.js.map +1 -1
- package/dist/core/learnings-lock.d.ts +7 -0
- package/dist/core/learnings-lock.d.ts.map +1 -1
- package/dist/core/learnings-lock.js +49 -8
- package/dist/core/learnings-lock.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +23 -5
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/health/contract.d.ts +56 -0
- package/dist/health/contract.d.ts.map +1 -0
- package/dist/health/contract.js +153 -0
- package/dist/health/contract.js.map +1 -0
- package/dist/health/directory-sync.d.ts +17 -0
- package/dist/health/directory-sync.d.ts.map +1 -0
- package/dist/health/directory-sync.js +69 -0
- package/dist/health/directory-sync.js.map +1 -0
- package/dist/health/index.d.ts +3 -0
- package/dist/health/index.d.ts.map +1 -0
- package/dist/health/index.js +3 -0
- package/dist/health/index.js.map +1 -0
- package/dist/health/storage.d.ts +39 -0
- package/dist/health/storage.d.ts.map +1 -0
- package/dist/health/storage.js +262 -0
- package/dist/health/storage.js.map +1 -0
- package/dist/health/strict-validation.d.ts +56 -0
- package/dist/health/strict-validation.d.ts.map +1 -0
- package/dist/health/strict-validation.js +144 -0
- package/dist/health/strict-validation.js.map +1 -0
- package/dist/sync/config-sync.d.ts.map +1 -1
- package/dist/sync/config-sync.js +18 -6
- package/dist/sync/config-sync.js.map +1 -1
- package/dist/sync/registry.d.ts +2 -0
- package/dist/sync/registry.d.ts.map +1 -1
- package/dist/sync/registry.js +7 -0
- package/dist/sync/registry.js.map +1 -1
- package/package.json +4 -2
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +33 -3
- package/plugins/lisa/hooks/enforce-verification-gate.sh +168 -8
- package/plugins/lisa/rules/reference/config-resolution.md +1 -0
- package/plugins/lisa/skills/lisa-implement/SKILL.md +33 -3
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +33 -3
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/enforce-verification-gate.sh +168 -8
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +1 -0
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +33 -3
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/enforce-verification-gate.sh +168 -8
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +1 -0
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +33 -3
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/enforce-verification-gate.sh +168 -8
- package/plugins/src/base/rules/reference/config-resolution.md +1 -0
- package/plugins/src/base/skills/lisa-implement/SKILL.md +33 -3
- package/scripts/verify-health-contract-built.mjs +143 -0
- package/ui/README.md +8 -3
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export declare const HEALTH_SCHEDULE_VALUES: readonly ["off", "daily", "weekly"];
|
|
2
|
+
/** Supported unattended health-run cadence. */
|
|
3
|
+
export type HealthSchedule = (typeof HEALTH_SCHEDULE_VALUES)[number];
|
|
4
|
+
/** Origin of one health finding. */
|
|
5
|
+
export type HealthLayer = "deterministic" | "agentic";
|
|
6
|
+
/** Outcome of one health check. */
|
|
7
|
+
export type HealthStatus = "pass" | "warn" | "fail";
|
|
8
|
+
/** Scope executed by a health run. */
|
|
9
|
+
export type HealthMode = "deterministic" | "full";
|
|
10
|
+
/** Canonical aggregate health verdict. */
|
|
11
|
+
export type HealthVerdict = "in band" | "drift detected";
|
|
12
|
+
/** One bounded, attributable health-check result. */
|
|
13
|
+
export interface HealthFinding {
|
|
14
|
+
readonly check: string;
|
|
15
|
+
readonly layer: HealthLayer;
|
|
16
|
+
readonly status: HealthStatus;
|
|
17
|
+
readonly reason: string;
|
|
18
|
+
}
|
|
19
|
+
/** Derived zero-filled counts and verdict. */
|
|
20
|
+
export interface HealthSummary {
|
|
21
|
+
readonly verdict: HealthVerdict;
|
|
22
|
+
readonly counts: Readonly<{
|
|
23
|
+
pass: number;
|
|
24
|
+
warn: number;
|
|
25
|
+
fail: number;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
/** Complete immutable Health v1 result envelope. */
|
|
29
|
+
export interface HealthResult {
|
|
30
|
+
readonly schemaVersion: 1;
|
|
31
|
+
readonly runId: string;
|
|
32
|
+
readonly mode: HealthMode;
|
|
33
|
+
readonly startedAt: string;
|
|
34
|
+
readonly completedAt: string;
|
|
35
|
+
readonly findings: readonly HealthFinding[];
|
|
36
|
+
readonly summary: HealthSummary;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Validate a health schedule value.
|
|
40
|
+
* @param value - Untrusted schedule candidate
|
|
41
|
+
* @returns Validated closed schedule value
|
|
42
|
+
*/
|
|
43
|
+
export declare function validateHealthSchedule(value: unknown): HealthSchedule;
|
|
44
|
+
/**
|
|
45
|
+
* Derive zero-filled counts and the canonical verdict.
|
|
46
|
+
* @param findings - Untrusted finding collection
|
|
47
|
+
* @returns Frozen canonical summary
|
|
48
|
+
*/
|
|
49
|
+
export declare function summarizeHealthFindings(findings: unknown): HealthSummary;
|
|
50
|
+
/**
|
|
51
|
+
* Validate untrusted data into a detached, deeply frozen Health v1 result.
|
|
52
|
+
* @param candidate - Untrusted result envelope
|
|
53
|
+
* @returns Frozen validated Health v1 result
|
|
54
|
+
*/
|
|
55
|
+
export declare function validateHealthResult(candidate: unknown): HealthResult;
|
|
56
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/health/contract.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,sBAAsB,qCAAsC,CAAC;AAC1E,+CAA+C;AAC/C,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AACrE,oCAAoC;AACpC,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,SAAS,CAAC;AACtD,mCAAmC;AACnC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AACpD,sCAAsC;AACtC,MAAM,MAAM,UAAU,GAAG,eAAe,GAAG,MAAM,CAAC;AAClD,0CAA0C;AAC1C,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,gBAAgB,CAAC;AAEzD,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,8CAA8C;AAC9C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzE;AAED,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IAC5C,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;CACjC;AAkBD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAKrE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,OAAO,GAAG,aAAa,CAaxE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,GAAG,YAAY,CA6CrE"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/** Strict executable contract for Health v1 results and scheduling. */
|
|
2
|
+
import { hasUnsafeTextCharacter, readStrictDenseArray, readStrictProperty as read, requireBoundedMachineId, requireCanonicalUtcTimestamp, requireClosedString as requireEnum, requireStrictRecord as requireRecord, } from "./strict-validation.js";
|
|
3
|
+
export const HEALTH_SCHEDULE_VALUES = ["off", "daily", "weekly"];
|
|
4
|
+
const RESULT_FIELDS = [
|
|
5
|
+
"schemaVersion",
|
|
6
|
+
"runId",
|
|
7
|
+
"mode",
|
|
8
|
+
"startedAt",
|
|
9
|
+
"completedAt",
|
|
10
|
+
"findings",
|
|
11
|
+
"summary",
|
|
12
|
+
];
|
|
13
|
+
const FINDING_FIELDS = ["check", "layer", "status", "reason"];
|
|
14
|
+
const SUMMARY_FIELDS = ["verdict", "counts"];
|
|
15
|
+
const COUNT_FIELDS = ["pass", "warn", "fail"];
|
|
16
|
+
const MAX_FINDINGS = 200;
|
|
17
|
+
const MAX_ID_BYTES = 128;
|
|
18
|
+
const MAX_REASON_BYTES = 2_000;
|
|
19
|
+
/**
|
|
20
|
+
* Validate a health schedule value.
|
|
21
|
+
* @param value - Untrusted schedule candidate
|
|
22
|
+
* @returns Validated closed schedule value
|
|
23
|
+
*/
|
|
24
|
+
export function validateHealthSchedule(value) {
|
|
25
|
+
if (!HEALTH_SCHEDULE_VALUES.includes(value)) {
|
|
26
|
+
throw new Error("Invalid health.schedule: expected off, daily, or weekly");
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Derive zero-filled counts and the canonical verdict.
|
|
32
|
+
* @param findings - Untrusted finding collection
|
|
33
|
+
* @returns Frozen canonical summary
|
|
34
|
+
*/
|
|
35
|
+
export function summarizeHealthFindings(findings) {
|
|
36
|
+
const validated = requireFindings(findings);
|
|
37
|
+
const counts = validated.reduce((current, finding) => ({
|
|
38
|
+
...current,
|
|
39
|
+
[finding.status]: current[finding.status] + 1,
|
|
40
|
+
}), { pass: 0, warn: 0, fail: 0 });
|
|
41
|
+
return Object.freeze({
|
|
42
|
+
verdict: counts.fail > 0 ? "drift detected" : "in band",
|
|
43
|
+
counts: Object.freeze(counts),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Validate untrusted data into a detached, deeply frozen Health v1 result.
|
|
48
|
+
* @param candidate - Untrusted result envelope
|
|
49
|
+
* @returns Frozen validated Health v1 result
|
|
50
|
+
*/
|
|
51
|
+
export function validateHealthResult(candidate) {
|
|
52
|
+
const input = requireRecord(candidate, RESULT_FIELDS, "HealthResult");
|
|
53
|
+
if (read(input, "schemaVersion") !== 1) {
|
|
54
|
+
throw new Error("Invalid schemaVersion: expected 1");
|
|
55
|
+
}
|
|
56
|
+
const runId = requireBoundedMachineId(read(input, "runId"), "runId", MAX_ID_BYTES);
|
|
57
|
+
const mode = requireEnum(read(input, "mode"), ["deterministic", "full"], "mode");
|
|
58
|
+
const startedAt = requireCanonicalUtcTimestamp(read(input, "startedAt"), "startedAt");
|
|
59
|
+
const completedAt = requireCanonicalUtcTimestamp(read(input, "completedAt"), "completedAt");
|
|
60
|
+
if (completedAt < startedAt) {
|
|
61
|
+
throw new Error("Invalid completedAt: precedes startedAt");
|
|
62
|
+
}
|
|
63
|
+
const findings = requireFindings(read(input, "findings"));
|
|
64
|
+
const supplied = requireSummary(read(input, "summary"));
|
|
65
|
+
const derived = summarizeHealthFindings(findings);
|
|
66
|
+
assertModeLayers(mode, findings);
|
|
67
|
+
if (supplied.verdict !== derived.verdict ||
|
|
68
|
+
COUNT_FIELDS.some(key => supplied.counts[key] !== derived.counts[key])) {
|
|
69
|
+
throw new Error("Invalid summary: verdict or counts do not match findings");
|
|
70
|
+
}
|
|
71
|
+
return Object.freeze({
|
|
72
|
+
schemaVersion: 1,
|
|
73
|
+
runId,
|
|
74
|
+
mode,
|
|
75
|
+
startedAt,
|
|
76
|
+
completedAt,
|
|
77
|
+
findings: Object.freeze(findings),
|
|
78
|
+
summary: derived,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Validate and detach a non-empty finding list.
|
|
83
|
+
* @param value - Untrusted finding collection
|
|
84
|
+
* @returns Detached validated findings
|
|
85
|
+
*/
|
|
86
|
+
function requireFindings(value) {
|
|
87
|
+
const findings = readStrictDenseArray(value, 1, MAX_FINDINGS, "findings").map((candidate, index) => requireFinding(candidate, index));
|
|
88
|
+
const checks = findings.map(finding => finding.check);
|
|
89
|
+
if (new Set(checks).size !== checks.length) {
|
|
90
|
+
throw new Error("Invalid findings: check identifiers must be unique");
|
|
91
|
+
}
|
|
92
|
+
return findings;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Validate one finding.
|
|
96
|
+
* @param candidate - Untrusted finding candidate
|
|
97
|
+
* @param index - Finding index used in validation messages
|
|
98
|
+
* @returns Frozen validated finding
|
|
99
|
+
*/
|
|
100
|
+
function requireFinding(candidate, index) {
|
|
101
|
+
const input = requireRecord(candidate, FINDING_FIELDS, `findings[${index}]`);
|
|
102
|
+
const reason = read(input, "reason");
|
|
103
|
+
if (typeof reason !== "string" ||
|
|
104
|
+
reason.trim() !== reason ||
|
|
105
|
+
reason.length === 0 ||
|
|
106
|
+
Buffer.byteLength(reason, "utf8") > MAX_REASON_BYTES ||
|
|
107
|
+
hasUnsafeTextCharacter(reason)) {
|
|
108
|
+
throw new Error(`Invalid findings[${index}].reason: expected readable text`);
|
|
109
|
+
}
|
|
110
|
+
return Object.freeze({
|
|
111
|
+
check: requireBoundedMachineId(read(input, "check"), `findings[${index}].check`, MAX_ID_BYTES),
|
|
112
|
+
layer: requireEnum(read(input, "layer"), ["deterministic", "agentic"], `findings[${index}].layer`),
|
|
113
|
+
status: requireEnum(read(input, "status"), ["pass", "warn", "fail"], `findings[${index}].status`),
|
|
114
|
+
reason,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Validate supplied summary shape and scalar values before comparison.
|
|
119
|
+
* @param value - Untrusted summary candidate
|
|
120
|
+
* @returns Frozen validated supplied summary
|
|
121
|
+
*/
|
|
122
|
+
function requireSummary(value) {
|
|
123
|
+
const summary = requireRecord(value, SUMMARY_FIELDS, "summary");
|
|
124
|
+
const counts = requireRecord(read(summary, "counts"), COUNT_FIELDS, "summary.counts");
|
|
125
|
+
const normalizedCounts = Object.fromEntries(COUNT_FIELDS.map(field => {
|
|
126
|
+
const count = read(counts, field);
|
|
127
|
+
if (!Number.isSafeInteger(count) || count < 0) {
|
|
128
|
+
throw new Error(`Invalid summary.counts.${field}: expected a non-negative integer`);
|
|
129
|
+
}
|
|
130
|
+
return [field, count];
|
|
131
|
+
}));
|
|
132
|
+
return Object.freeze({
|
|
133
|
+
verdict: requireEnum(read(summary, "verdict"), ["in band", "drift detected"], "summary.verdict"),
|
|
134
|
+
counts: Object.freeze(normalizedCounts),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Enforce mode meaning rather than treating it as decorative metadata.
|
|
139
|
+
* @param mode - Validated run mode
|
|
140
|
+
* @param findings - Validated run findings
|
|
141
|
+
*/
|
|
142
|
+
function assertModeLayers(mode, findings) {
|
|
143
|
+
const layers = new Set(findings.map(finding => finding.layer));
|
|
144
|
+
if (mode === "deterministic" &&
|
|
145
|
+
(layers.size !== 1 || !layers.has("deterministic"))) {
|
|
146
|
+
throw new Error("Invalid findings: deterministic mode permits deterministic findings only");
|
|
147
|
+
}
|
|
148
|
+
if (mode === "full" &&
|
|
149
|
+
(!layers.has("deterministic") || !layers.has("agentic"))) {
|
|
150
|
+
throw new Error("Invalid findings: full mode requires both deterministic and agentic findings");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../../src/health/contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Minimal directory handle used by the production opener and focused tests. */
|
|
2
|
+
export interface DirectorySyncHandle {
|
|
3
|
+
readonly sync: () => Promise<void>;
|
|
4
|
+
readonly close: () => Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
/** Injectable directory opener. */
|
|
7
|
+
export type OpenDirectory = (directory: string, flags: string) => Promise<DirectorySyncHandle>;
|
|
8
|
+
/**
|
|
9
|
+
* Flush a containing directory when the host filesystem supports it.
|
|
10
|
+
* Atomic rename protects ordinary crashes. A successful directory sync adds
|
|
11
|
+
* power-loss durability; known platform-level lack of support is best effort.
|
|
12
|
+
* Permission, device, and other real I/O failures remain fatal.
|
|
13
|
+
* @param directory - Directory containing the renamed target
|
|
14
|
+
* @param openDirectory - Injectable opener used by focused fault tests
|
|
15
|
+
*/
|
|
16
|
+
export declare function syncContainingDirectory(directory: string, openDirectory?: OpenDirectory): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=directory-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-sync.d.ts","sourceRoot":"","sources":["../../src/health/directory-sync.ts"],"names":[],"mappings":"AAGA,gFAAgF;AAChF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,mCAAmC;AACnC,MAAM,MAAM,aAAa,GAAG,CAC1B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,KACV,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAiBlC;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,MAAM,EACjB,aAAa,GAAE,aAAoB,GAClC,OAAO,CAAC,IAAI,CAAC,CAUf"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** Best-effort directory durability without hiding genuine I/O failures. */
|
|
2
|
+
import { open } from "node:fs/promises";
|
|
3
|
+
const OPEN_UNSUPPORTED = new Set([
|
|
4
|
+
"EISDIR",
|
|
5
|
+
"EINVAL",
|
|
6
|
+
"ENOTSUP",
|
|
7
|
+
"EOPNOTSUPP",
|
|
8
|
+
"ENOSYS",
|
|
9
|
+
]);
|
|
10
|
+
const SYNC_UNSUPPORTED = new Set([
|
|
11
|
+
"EBADF",
|
|
12
|
+
"EINVAL",
|
|
13
|
+
"ENOTSUP",
|
|
14
|
+
"EOPNOTSUPP",
|
|
15
|
+
"ENOSYS",
|
|
16
|
+
]);
|
|
17
|
+
/**
|
|
18
|
+
* Flush a containing directory when the host filesystem supports it.
|
|
19
|
+
* Atomic rename protects ordinary crashes. A successful directory sync adds
|
|
20
|
+
* power-loss durability; known platform-level lack of support is best effort.
|
|
21
|
+
* Permission, device, and other real I/O failures remain fatal.
|
|
22
|
+
* @param directory - Directory containing the renamed target
|
|
23
|
+
* @param openDirectory - Injectable opener used by focused fault tests
|
|
24
|
+
*/
|
|
25
|
+
export async function syncContainingDirectory(directory, openDirectory = open) {
|
|
26
|
+
const handle = await tryOpenDirectory(directory, openDirectory);
|
|
27
|
+
if (handle === undefined)
|
|
28
|
+
return;
|
|
29
|
+
try {
|
|
30
|
+
await handle.sync();
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (!hasCode(error, SYNC_UNSUPPORTED))
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
await handle.close();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Open a directory or identify a host that does not expose directory opens.
|
|
42
|
+
* @param directory - Directory to open
|
|
43
|
+
* @param openDirectory - Platform directory opener
|
|
44
|
+
* @returns Open handle, or undefined when directory opens are unsupported
|
|
45
|
+
*/
|
|
46
|
+
async function tryOpenDirectory(directory, openDirectory) {
|
|
47
|
+
try {
|
|
48
|
+
return await openDirectory(directory, "r");
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (hasCode(error, OPEN_UNSUPPORTED))
|
|
52
|
+
return undefined;
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Match a Node-style error code against a closed unsupported-code set.
|
|
58
|
+
* @param error - Candidate Node-style error
|
|
59
|
+
* @param codes - Closed set of recognized codes
|
|
60
|
+
* @returns Whether the error carries a recognized code
|
|
61
|
+
*/
|
|
62
|
+
function hasCode(error, codes) {
|
|
63
|
+
return (typeof error === "object" &&
|
|
64
|
+
error !== null &&
|
|
65
|
+
"code" in error &&
|
|
66
|
+
typeof error.code === "string" &&
|
|
67
|
+
codes.has(error.code));
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=directory-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-sync.js","sourceRoot":"","sources":["../../src/health/directory-sync.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAcxC,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;IAC/B,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,YAAY;IACZ,QAAQ;CACT,CAAC,CAAC;AACH,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;IAC/B,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;IACZ,QAAQ;CACT,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,SAAiB,EACjB,gBAA+B,IAAI;IAEnC,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IAChE,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO;IACjC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,gBAAgB,CAAC;YAAE,MAAM,KAAK,CAAC;IACrD,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,gBAAgB,CAC7B,SAAiB,EACjB,aAA4B;IAE5B,IAAI,CAAC;QACH,OAAO,MAAM,aAAa,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,KAAK,EAAE,gBAAgB,CAAC;YAAE,OAAO,SAAS,CAAC;QACvD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,OAAO,CAAC,KAAc,EAAE,KAA0B;IACzD,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACf,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;QAC9B,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CACtB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/health/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/health/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type HealthResult } from "./contract.js";
|
|
2
|
+
export declare const HEALTH_RESULT_PATH = ".lisa/health/latest.json";
|
|
3
|
+
export declare const MAX_HEALTH_RESULT_BYTES: number;
|
|
4
|
+
/** Non-throwing read outcome for persisted runtime health state. */
|
|
5
|
+
export type HealthReadResult = {
|
|
6
|
+
readonly status: "available";
|
|
7
|
+
readonly result: HealthResult;
|
|
8
|
+
readonly lastRun: string;
|
|
9
|
+
} | {
|
|
10
|
+
readonly status: "never-run";
|
|
11
|
+
} | {
|
|
12
|
+
readonly status: "unreadable";
|
|
13
|
+
readonly reason: string;
|
|
14
|
+
};
|
|
15
|
+
/** Atomic write or monotonic no-op outcome. */
|
|
16
|
+
export type HealthWriteResult = {
|
|
17
|
+
readonly status: "written";
|
|
18
|
+
readonly path: string;
|
|
19
|
+
readonly result: HealthResult;
|
|
20
|
+
} | {
|
|
21
|
+
readonly status: "unchanged";
|
|
22
|
+
readonly path: string;
|
|
23
|
+
readonly result: HealthResult;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Read the canonical latest result without throwing for runtime-state faults.
|
|
27
|
+
* @param projectRoot - Project containing runtime health state
|
|
28
|
+
* @returns Available, never-run, or unreadable outcome
|
|
29
|
+
*/
|
|
30
|
+
export declare function readLatestHealthResult(projectRoot: string): Promise<HealthReadResult>;
|
|
31
|
+
/**
|
|
32
|
+
* Validate and atomically publish a completed result. Writes are serialized,
|
|
33
|
+
* and an older or equal completion timestamp cannot replace a newer result.
|
|
34
|
+
* @param projectRoot - Project receiving runtime health state
|
|
35
|
+
* @param candidate - Untrusted completed result candidate
|
|
36
|
+
* @returns Written or monotonic unchanged outcome
|
|
37
|
+
*/
|
|
38
|
+
export declare function writeLatestHealthResult(projectRoot: string, candidate: unknown): Promise<HealthWriteResult>;
|
|
39
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/health/storage.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,YAAY,EAAwB,MAAM,eAAe,CAAC;AAGxE,eAAO,MAAM,kBAAkB,6BAA6B,CAAC;AAC7D,eAAO,MAAM,uBAAuB,QAAa,CAAC;AAElD,oEAAoE;AACpE,MAAM,MAAM,gBAAgB,GACxB;IACE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,GACD;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAChC;IAAE,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,+CAA+C;AAC/C,MAAM,MAAM,iBAAiB,GACzB;IACE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;CAC/B,GACD;IACE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;CAC/B,CAAC;AAEN;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,gBAAgB,CAAC,CAkB3B;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC,iBAAiB,CAAC,CA4C5B"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/** Confined, bounded, atomic storage for the latest completed Health v1 run. */
|
|
2
|
+
import * as fse from "fs-extra";
|
|
3
|
+
import { lstat, open, realpath, rename, rm } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { withFileTargetLock } from "../core/learnings-lock.js";
|
|
6
|
+
import { validateHealthResult } from "./contract.js";
|
|
7
|
+
import { syncContainingDirectory } from "./directory-sync.js";
|
|
8
|
+
export const HEALTH_RESULT_PATH = ".lisa/health/latest.json";
|
|
9
|
+
export const MAX_HEALTH_RESULT_BYTES = 256 * 1024;
|
|
10
|
+
/**
|
|
11
|
+
* Read the canonical latest result without throwing for runtime-state faults.
|
|
12
|
+
* @param projectRoot - Project containing runtime health state
|
|
13
|
+
* @returns Available, never-run, or unreadable outcome
|
|
14
|
+
*/
|
|
15
|
+
export async function readLatestHealthResult(projectRoot) {
|
|
16
|
+
try {
|
|
17
|
+
const target = await resolveReadTarget(projectRoot);
|
|
18
|
+
const payload = await readBoundedRegularFile(target);
|
|
19
|
+
if (payload === undefined)
|
|
20
|
+
return Object.freeze({ status: "never-run" });
|
|
21
|
+
const parsed = JSON.parse(payload);
|
|
22
|
+
const result = validateHealthResult(parsed);
|
|
23
|
+
return Object.freeze({
|
|
24
|
+
status: "available",
|
|
25
|
+
result,
|
|
26
|
+
lastRun: result.completedAt,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return Object.freeze({
|
|
31
|
+
status: "unreadable",
|
|
32
|
+
reason: safeReadReason(error),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Validate and atomically publish a completed result. Writes are serialized,
|
|
38
|
+
* and an older or equal completion timestamp cannot replace a newer result.
|
|
39
|
+
* @param projectRoot - Project receiving runtime health state
|
|
40
|
+
* @param candidate - Untrusted completed result candidate
|
|
41
|
+
* @returns Written or monotonic unchanged outcome
|
|
42
|
+
*/
|
|
43
|
+
export async function writeLatestHealthResult(projectRoot, candidate) {
|
|
44
|
+
const result = validateHealthResult(candidate);
|
|
45
|
+
const serialized = serializeHealthResult(result);
|
|
46
|
+
const { directory, target } = await resolveWriteTarget(projectRoot);
|
|
47
|
+
return withFileTargetLock(target, async () => {
|
|
48
|
+
await assertSafeTarget(target);
|
|
49
|
+
const current = await readLatestHealthResult(projectRoot);
|
|
50
|
+
if (current.status === "available") {
|
|
51
|
+
const samePayload = serializeHealthResult(current.result) === serialized;
|
|
52
|
+
if (current.result.runId === result.runId) {
|
|
53
|
+
if (!samePayload) {
|
|
54
|
+
throw new Error("Health result conflict: runId was reused");
|
|
55
|
+
}
|
|
56
|
+
return unchanged(target, current.result);
|
|
57
|
+
}
|
|
58
|
+
if (current.result.completedAt === result.completedAt) {
|
|
59
|
+
throw new Error("Health result conflict: completedAt is shared by different runs");
|
|
60
|
+
}
|
|
61
|
+
if (current.result.completedAt > result.completedAt) {
|
|
62
|
+
return unchanged(target, current.result);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const temporary = path.join(directory, `.${path.basename(target)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
66
|
+
try {
|
|
67
|
+
const handle = await open(temporary, "wx", 0o600);
|
|
68
|
+
try {
|
|
69
|
+
await handle.writeFile(serialized, "utf8");
|
|
70
|
+
await handle.sync();
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
await handle.close();
|
|
74
|
+
}
|
|
75
|
+
await assertSafeTarget(target);
|
|
76
|
+
await rename(temporary, target);
|
|
77
|
+
await syncContainingDirectory(directory);
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
await rm(temporary, { force: true });
|
|
81
|
+
}
|
|
82
|
+
return Object.freeze({ status: "written", path: target, result });
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Serialize once and reject over-budget output before filesystem mutation.
|
|
87
|
+
* @param result - Validated result
|
|
88
|
+
* @returns Canonical bounded JSON payload
|
|
89
|
+
*/
|
|
90
|
+
function serializeHealthResult(result) {
|
|
91
|
+
const serialized = `${JSON.stringify(result, null, 2)}\n`;
|
|
92
|
+
if (Buffer.byteLength(serialized, "utf8") > MAX_HEALTH_RESULT_BYTES) {
|
|
93
|
+
throw new Error("Health result exceeds 256 KiB");
|
|
94
|
+
}
|
|
95
|
+
return serialized;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Return the canonical idempotent no-write result.
|
|
99
|
+
* @param target - Canonical result path
|
|
100
|
+
* @param result - Existing persisted result
|
|
101
|
+
* @returns Frozen unchanged outcome
|
|
102
|
+
*/
|
|
103
|
+
function unchanged(target, result) {
|
|
104
|
+
return Object.freeze({ status: "unchanged", path: target, result });
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Resolve an existing project root and canonical read target.
|
|
108
|
+
* @param projectRoot - Project containing runtime health state
|
|
109
|
+
* @returns Confined canonical target
|
|
110
|
+
*/
|
|
111
|
+
async function resolveReadTarget(projectRoot) {
|
|
112
|
+
const root = await requireProjectRoot(projectRoot);
|
|
113
|
+
const lisaDirectory = path.join(root, ".lisa");
|
|
114
|
+
const healthDirectory = path.join(lisaDirectory, "health");
|
|
115
|
+
if ((await statPath(lisaDirectory)) !== undefined) {
|
|
116
|
+
await assertConfinedDirectory(root, lisaDirectory);
|
|
117
|
+
}
|
|
118
|
+
if ((await statPath(healthDirectory)) !== undefined) {
|
|
119
|
+
await assertConfinedDirectory(root, healthDirectory);
|
|
120
|
+
}
|
|
121
|
+
return path.join(root, HEALTH_RESULT_PATH);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Create safe canonical parent directories and return write paths.
|
|
125
|
+
* @param projectRoot - Project receiving runtime health state
|
|
126
|
+
* @returns Confined parent directory and target
|
|
127
|
+
*/
|
|
128
|
+
async function resolveWriteTarget(projectRoot) {
|
|
129
|
+
const root = await requireProjectRoot(projectRoot);
|
|
130
|
+
const lisaDirectory = path.join(root, ".lisa");
|
|
131
|
+
const directory = path.join(lisaDirectory, "health");
|
|
132
|
+
await ensureSafeDirectory(root, lisaDirectory);
|
|
133
|
+
await ensureSafeDirectory(root, directory);
|
|
134
|
+
return { directory, target: path.join(directory, "latest.json") };
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Resolve and verify a real directory root.
|
|
138
|
+
* @param projectRoot - Project-root candidate
|
|
139
|
+
* @returns Canonical real project root
|
|
140
|
+
*/
|
|
141
|
+
async function requireProjectRoot(projectRoot) {
|
|
142
|
+
const resolved = path.resolve(projectRoot);
|
|
143
|
+
const root = await realpath(resolved);
|
|
144
|
+
const metadata = await lstat(root);
|
|
145
|
+
if (!metadata.isDirectory()) {
|
|
146
|
+
throw new Error("Health project root is not a directory");
|
|
147
|
+
}
|
|
148
|
+
return root;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Create one directory only after its nearest existing parent stays confined.
|
|
152
|
+
* @param root - Canonical project root
|
|
153
|
+
* @param directory - Intended child directory
|
|
154
|
+
*/
|
|
155
|
+
async function ensureSafeDirectory(root, directory) {
|
|
156
|
+
if ((await statPath(directory)) !== undefined) {
|
|
157
|
+
await assertConfinedDirectory(root, directory);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
await assertConfinedDirectory(root, path.dirname(directory));
|
|
161
|
+
await fse.ensureDir(directory);
|
|
162
|
+
await assertConfinedDirectory(root, directory);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Reject symlinked, special, or root-escaping storage parents.
|
|
166
|
+
* @param root - Canonical project root
|
|
167
|
+
* @param directory - Existing storage parent
|
|
168
|
+
*/
|
|
169
|
+
async function assertConfinedDirectory(root, directory) {
|
|
170
|
+
const metadata = await lstat(directory);
|
|
171
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
172
|
+
throw new Error("Unsafe health storage parent");
|
|
173
|
+
}
|
|
174
|
+
const actual = await realpath(directory);
|
|
175
|
+
if (actual !== root && !actual.startsWith(`${root}${path.sep}`)) {
|
|
176
|
+
throw new Error("Unsafe health storage parent escapes project root");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Reject an existing target unless it is a regular non-symlink file.
|
|
181
|
+
* @param target - Canonical result target
|
|
182
|
+
*/
|
|
183
|
+
async function assertSafeTarget(target) {
|
|
184
|
+
try {
|
|
185
|
+
const metadata = await lstat(target);
|
|
186
|
+
if (!metadata.isFile() || metadata.isSymbolicLink()) {
|
|
187
|
+
throw new Error("Unsafe health result target");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
if (error.code !== "ENOENT")
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Read a bounded stable regular-file inode.
|
|
197
|
+
* @param target - Canonical result target
|
|
198
|
+
* @returns Strict UTF-8 payload, or undefined when absent
|
|
199
|
+
*/
|
|
200
|
+
async function readBoundedRegularFile(target) {
|
|
201
|
+
try {
|
|
202
|
+
const before = await lstat(target);
|
|
203
|
+
if (!before.isFile() || before.isSymbolicLink()) {
|
|
204
|
+
throw new Error("Unsafe health result target");
|
|
205
|
+
}
|
|
206
|
+
if (before.size > MAX_HEALTH_RESULT_BYTES) {
|
|
207
|
+
throw new Error("Health result exceeds 256 KiB");
|
|
208
|
+
}
|
|
209
|
+
const handle = await open(target, "r");
|
|
210
|
+
try {
|
|
211
|
+
const opened = await handle.stat();
|
|
212
|
+
if (!opened.isFile() ||
|
|
213
|
+
opened.dev !== before.dev ||
|
|
214
|
+
opened.ino !== before.ino ||
|
|
215
|
+
opened.size > MAX_HEALTH_RESULT_BYTES) {
|
|
216
|
+
throw new Error("Health result changed during read");
|
|
217
|
+
}
|
|
218
|
+
const bytes = await handle.readFile();
|
|
219
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
await handle.close();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
if (error.code === "ENOENT")
|
|
227
|
+
return undefined;
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Read path metadata without following symlinks.
|
|
233
|
+
* @param filePath - Filesystem path
|
|
234
|
+
* @returns Metadata, or undefined when absent
|
|
235
|
+
*/
|
|
236
|
+
async function statPath(filePath) {
|
|
237
|
+
try {
|
|
238
|
+
return await lstat(filePath);
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
if (error.code === "ENOENT")
|
|
242
|
+
return undefined;
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Reduce runtime-state failures to a non-sensitive operator-readable reason.
|
|
248
|
+
* @param error - Read failure
|
|
249
|
+
* @returns Stable operator-facing reason
|
|
250
|
+
*/
|
|
251
|
+
function safeReadReason(error) {
|
|
252
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
253
|
+
if (message.includes("schemaVersion"))
|
|
254
|
+
return "unsupported health result schema";
|
|
255
|
+
if (message.includes("256 KiB"))
|
|
256
|
+
return "health result exceeds size limit";
|
|
257
|
+
if (message.includes("Unsafe") || message.includes("changed during read")) {
|
|
258
|
+
return "unsafe health result storage";
|
|
259
|
+
}
|
|
260
|
+
return "malformed health result";
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/health/storage.ts"],"names":[],"mappings":"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"}
|