@codyswann/lisa 4.17.5 → 4.17.7
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 +6 -0
- package/dist/cli/doctor-reconciliation.d.ts +15 -0
- package/dist/cli/doctor-reconciliation.d.ts.map +1 -0
- package/dist/cli/doctor-reconciliation.js +123 -0
- package/dist/cli/doctor-reconciliation.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +7 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +15 -1
- package/dist/core/lisa.js.map +1 -1
- package/dist/core/reconciliation-report.d.ts +100 -0
- package/dist/core/reconciliation-report.d.ts.map +1 -0
- package/dist/core/reconciliation-report.js +178 -0
- package/dist/core/reconciliation-report.js.map +1 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +17 -1
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/utils/postinstall-trampoline-report-source.d.ts +50 -0
- package/dist/utils/postinstall-trampoline-report-source.d.ts.map +1 -0
- package/dist/utils/postinstall-trampoline-report-source.js +113 -0
- package/dist/utils/postinstall-trampoline-report-source.js.map +1 -0
- package/dist/utils/postinstall-trampoline-source.d.ts +65 -0
- package/dist/utils/postinstall-trampoline-source.d.ts.map +1 -0
- package/dist/utils/postinstall-trampoline-source.js +312 -0
- package/dist/utils/postinstall-trampoline-source.js.map +1 -0
- package/dist/utils/postinstall-trampoline.d.ts +4 -2
- package/dist/utils/postinstall-trampoline.d.ts.map +1 -1
- package/dist/utils/postinstall-trampoline.js +5 -197
- package/dist/utils/postinstall-trampoline.js.map +1 -1
- package/package.json +3 -2
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- 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-cursor/.claude-plugin/plugin.json +1 -1
- 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/scripts/check-shell-guard-refusal-coverage.mjs +233 -0
- package/scripts/lib/shell-guard-refusal-coverage.mjs +315 -0
- package/scripts/lib/shell-guard-trace.mjs +256 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file postinstall-trampoline-report-source.ts
|
|
3
|
+
* @description The two reporting fragments inlined into the detached
|
|
4
|
+
* reconciliation child.
|
|
5
|
+
*
|
|
6
|
+
* These exist because the child is detached and `stdio: "ignore"`: anything it
|
|
7
|
+
* says to a stream is said to nobody, so the only evidence that survives the
|
|
8
|
+
* package manager exiting is a file. Before them, a trampoline that never
|
|
9
|
+
* spawned, one that died on arrival, and one that ran and correctly did nothing
|
|
10
|
+
* were indistinguishable — which is what made CodySwannGT/lisa#2750 invisible
|
|
11
|
+
* for as long as it existed.
|
|
12
|
+
* @module utils
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The report writer inlined into the trampoline child.
|
|
16
|
+
*
|
|
17
|
+
* This is the deliverable half of CodySwannGT/lisa#2750. The child is detached
|
|
18
|
+
* and `stdio: "ignore"`, so anything it says to a stream is said to nobody; the
|
|
19
|
+
* only place it can leave evidence is the filesystem, and the only evidence that
|
|
20
|
+
* survives the parent exiting is a file. Every write is atomic (temp + rename)
|
|
21
|
+
* so `lisa doctor` never reads a half-written document, and every write is
|
|
22
|
+
* wrapped so a read-only or full disk degrades to the old silence rather than
|
|
23
|
+
* killing a reconciliation that would otherwise have worked.
|
|
24
|
+
*
|
|
25
|
+
* `scheduled_at` and `lisa_version` are inherited from the report the parent
|
|
26
|
+
* apply wrote, so the age doctor reports is the age of the WHOLE reconciliation
|
|
27
|
+
* rather than of its last heartbeat.
|
|
28
|
+
* @param literals - Inlined JSON-safe literals
|
|
29
|
+
* @param literals.reportPath - Absolute path of the report file
|
|
30
|
+
* @param literals.reportSchemaVersion - Schema version stamped into each write
|
|
31
|
+
* @param literals.lisaVersion - Fallback Lisa version when no prior report exists
|
|
32
|
+
* @returns JS source fragment
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildTrampolineReporter(literals: {
|
|
35
|
+
readonly reportPath: string;
|
|
36
|
+
readonly reportSchemaVersion: string;
|
|
37
|
+
readonly lisaVersion: string;
|
|
38
|
+
}): string;
|
|
39
|
+
/**
|
|
40
|
+
* The regen-outcome reporter inlined into the trampoline child.
|
|
41
|
+
*
|
|
42
|
+
* Split from the main IIFE only so each stays under the repo's function-length
|
|
43
|
+
* cap; it is the terminal half of one decision. Three outcomes, deliberately
|
|
44
|
+
* distinct: a regen that failed, a package.json change with no lockfile to
|
|
45
|
+
* repair, and a regen that landed. Collapsing any pair of them would restore
|
|
46
|
+
* exactly the ambiguity CodySwannGT/lisa#2750 was filed about.
|
|
47
|
+
* @returns JS source fragment
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildTrampolineRegenReporter(): string;
|
|
50
|
+
//# sourceMappingURL=postinstall-trampoline-report-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postinstall-trampoline-report-source.d.ts","sourceRoot":"","sources":["../../src/utils/postinstall-trampoline-report-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE;IAChD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,GAAG,MAAM,CAqCT;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,CA8BrD"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file postinstall-trampoline-report-source.ts
|
|
3
|
+
* @description The two reporting fragments inlined into the detached
|
|
4
|
+
* reconciliation child.
|
|
5
|
+
*
|
|
6
|
+
* These exist because the child is detached and `stdio: "ignore"`: anything it
|
|
7
|
+
* says to a stream is said to nobody, so the only evidence that survives the
|
|
8
|
+
* package manager exiting is a file. Before them, a trampoline that never
|
|
9
|
+
* spawned, one that died on arrival, and one that ran and correctly did nothing
|
|
10
|
+
* were indistinguishable — which is what made CodySwannGT/lisa#2750 invisible
|
|
11
|
+
* for as long as it existed.
|
|
12
|
+
* @module utils
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The report writer inlined into the trampoline child.
|
|
16
|
+
*
|
|
17
|
+
* This is the deliverable half of CodySwannGT/lisa#2750. The child is detached
|
|
18
|
+
* and `stdio: "ignore"`, so anything it says to a stream is said to nobody; the
|
|
19
|
+
* only place it can leave evidence is the filesystem, and the only evidence that
|
|
20
|
+
* survives the parent exiting is a file. Every write is atomic (temp + rename)
|
|
21
|
+
* so `lisa doctor` never reads a half-written document, and every write is
|
|
22
|
+
* wrapped so a read-only or full disk degrades to the old silence rather than
|
|
23
|
+
* killing a reconciliation that would otherwise have worked.
|
|
24
|
+
*
|
|
25
|
+
* `scheduled_at` and `lisa_version` are inherited from the report the parent
|
|
26
|
+
* apply wrote, so the age doctor reports is the age of the WHOLE reconciliation
|
|
27
|
+
* rather than of its last heartbeat.
|
|
28
|
+
* @param literals - Inlined JSON-safe literals
|
|
29
|
+
* @param literals.reportPath - Absolute path of the report file
|
|
30
|
+
* @param literals.reportSchemaVersion - Schema version stamped into each write
|
|
31
|
+
* @param literals.lisaVersion - Fallback Lisa version when no prior report exists
|
|
32
|
+
* @returns JS source fragment
|
|
33
|
+
*/
|
|
34
|
+
export function buildTrampolineReporter(literals) {
|
|
35
|
+
return `
|
|
36
|
+
const REPORT_PATH = ${literals.reportPath};
|
|
37
|
+
|
|
38
|
+
function readReport() {
|
|
39
|
+
try { return JSON.parse(readFileSync(REPORT_PATH, "utf8")); }
|
|
40
|
+
catch { return null; }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function report(outcome, detail, packageManagers) {
|
|
44
|
+
try {
|
|
45
|
+
const prior = readReport();
|
|
46
|
+
const now = new Date().toISOString();
|
|
47
|
+
const doc = {
|
|
48
|
+
schema_version: ${literals.reportSchemaVersion},
|
|
49
|
+
lisa_version:
|
|
50
|
+
prior && typeof prior.lisa_version === "string"
|
|
51
|
+
? prior.lisa_version
|
|
52
|
+
: ${literals.lisaVersion},
|
|
53
|
+
outcome: outcome,
|
|
54
|
+
scheduled_at:
|
|
55
|
+
prior && typeof prior.scheduled_at === "string"
|
|
56
|
+
? prior.scheduled_at
|
|
57
|
+
: now,
|
|
58
|
+
updated_at: now,
|
|
59
|
+
package_managers: packageManagers || [],
|
|
60
|
+
detail: detail,
|
|
61
|
+
};
|
|
62
|
+
mkdirSync(path.dirname(REPORT_PATH), { recursive: true });
|
|
63
|
+
const tempPath = REPORT_PATH + ".tmp";
|
|
64
|
+
writeFileSync(tempPath, JSON.stringify(doc, null, 2) + "\\n");
|
|
65
|
+
renameSync(tempPath, REPORT_PATH);
|
|
66
|
+
} catch {
|
|
67
|
+
// A report that cannot be written must never break an install.
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The regen-outcome reporter inlined into the trampoline child.
|
|
74
|
+
*
|
|
75
|
+
* Split from the main IIFE only so each stays under the repo's function-length
|
|
76
|
+
* cap; it is the terminal half of one decision. Three outcomes, deliberately
|
|
77
|
+
* distinct: a regen that failed, a package.json change with no lockfile to
|
|
78
|
+
* repair, and a regen that landed. Collapsing any pair of them would restore
|
|
79
|
+
* exactly the ambiguity CodySwannGT/lisa#2750 was filed about.
|
|
80
|
+
* @returns JS source fragment
|
|
81
|
+
*/
|
|
82
|
+
export function buildTrampolineRegenReporter() {
|
|
83
|
+
return `
|
|
84
|
+
function reportRegen(regen, reapplyNote) {
|
|
85
|
+
if (regen.failed.length > 0) {
|
|
86
|
+
report(
|
|
87
|
+
"failed",
|
|
88
|
+
"Lockfile regeneration failed for: " + regen.failed.join(", ") +
|
|
89
|
+
". Lockfiles are still out of sync with package.json, so a " +
|
|
90
|
+
"frozen-lockfile install will fail." + reapplyNote,
|
|
91
|
+
regen.attempted
|
|
92
|
+
);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (regen.attempted.length === 0) {
|
|
96
|
+
report(
|
|
97
|
+
"not-needed",
|
|
98
|
+
"package.json changed but no lockfile was found to regenerate." +
|
|
99
|
+
reapplyNote,
|
|
100
|
+
[]
|
|
101
|
+
);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
report(
|
|
105
|
+
"regenerated",
|
|
106
|
+
"Regenerated lockfiles for: " + regen.attempted.join(", ") + "." +
|
|
107
|
+
reapplyNote,
|
|
108
|
+
regen.attempted
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
`;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=postinstall-trampoline-report-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postinstall-trampoline-report-source.js","sourceRoot":"","sources":["../../src/utils/postinstall-trampoline-report-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAIvC;IACC,OAAO;0BACiB,QAAQ,CAAC,UAAU;;;;;;;;;;;;4BAYjB,QAAQ,CAAC,mBAAmB;;;;kBAItC,QAAQ,CAAC,WAAW;;;;;;;;;;;;;;;;;;GAkBnC,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B;IAC1C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { LockfileRegenPlan, PackageManager } from "./package-manager-detect.js";
|
|
2
|
+
/**
|
|
3
|
+
* What the detached child needs in order to leave a durable record of itself.
|
|
4
|
+
*
|
|
5
|
+
* Passed in by the caller rather than imported, so this module stays free of a
|
|
6
|
+
* dependency on `core/`: the report's path and schema still have exactly one
|
|
7
|
+
* definition (`core/reconciliation-report`), and it travels here as data.
|
|
8
|
+
*/
|
|
9
|
+
export interface TrampolineReporting {
|
|
10
|
+
/** Absolute path of the report file the child writes. */
|
|
11
|
+
readonly reportPath: string;
|
|
12
|
+
/** Schema version stamped into every write. */
|
|
13
|
+
readonly reportSchemaVersion: number;
|
|
14
|
+
/** Lisa version, used only when no scheduled report exists to inherit it from. */
|
|
15
|
+
readonly lisaVersion: string;
|
|
16
|
+
/**
|
|
17
|
+
* sha256 of package.json as it stood BEFORE the apply that scheduled this
|
|
18
|
+
* child — which is the content the package manager resolved its lockfile
|
|
19
|
+
* from.
|
|
20
|
+
*
|
|
21
|
+
* This is the whole of CodySwannGT/lisa#2750. The child used to regenerate
|
|
22
|
+
* lockfiles only when ITS OWN re-apply changed package.json, which on a plain
|
|
23
|
+
* `bun install` it never does: the first apply (inside postinstall) already
|
|
24
|
+
* made the change, and the re-apply is idempotent. So the drift the first
|
|
25
|
+
* apply created — the drift the trampoline exists to repair — was the one
|
|
26
|
+
* case that never triggered a regen. Comparing against this baseline asks the
|
|
27
|
+
* question that actually matters: does package.json still match what the
|
|
28
|
+
* lockfile was built from?
|
|
29
|
+
*/
|
|
30
|
+
readonly baselinePackageJsonHash: string | null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Shape of the parameters embedded into the trampoline's inline JS source.
|
|
34
|
+
* Grouping keeps the callsite readable and the injected literals explicit.
|
|
35
|
+
*/
|
|
36
|
+
interface TrampolineSourceParams {
|
|
37
|
+
readonly parentPid: number;
|
|
38
|
+
readonly pollIntervalMs: number;
|
|
39
|
+
readonly maxWaitMs: number;
|
|
40
|
+
readonly settleDelayMs: number;
|
|
41
|
+
readonly lisaEntry: string;
|
|
42
|
+
readonly projectDir: string;
|
|
43
|
+
readonly nodeBin: string;
|
|
44
|
+
readonly trampolineEnvVar: string;
|
|
45
|
+
readonly lockfileRegenPlans: Readonly<Record<PackageManager, LockfileRegenPlan>>;
|
|
46
|
+
readonly reporting: TrampolineReporting;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Build the inline JS source that runs inside the detached trampoline child.
|
|
50
|
+
* The source is passed to `node -e` so it must be self-contained (no imports that
|
|
51
|
+
* require resolution via package.json, which is exactly the file we're racing).
|
|
52
|
+
*
|
|
53
|
+
* The trampoline now runs in two phases inside the child:
|
|
54
|
+
* 1. Wait for the parent PM to exit, then re-invoke Lisa to reconcile package.json.
|
|
55
|
+
* 2. If Lisa's re-invocation mutated package.json, regenerate whichever lockfiles
|
|
56
|
+
* are present in the project so `bun install --frozen-lockfile` / `npm ci` in
|
|
57
|
+
* downstream CI jobs do not fail with "lockfile had changes, but lockfile is
|
|
58
|
+
* frozen." Lockfile regen runs with `--ignore-scripts` so the parent PM's
|
|
59
|
+
* lifecycle hooks are not re-invoked (which would retrigger this trampoline).
|
|
60
|
+
* @param params - Embedded constants to inline into the trampoline source
|
|
61
|
+
* @returns JS source suitable for `node -e`
|
|
62
|
+
*/
|
|
63
|
+
export declare function buildTrampolineSource(params: TrampolineSourceParams): string;
|
|
64
|
+
export {};
|
|
65
|
+
//# sourceMappingURL=postinstall-trampoline-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postinstall-trampoline-source.d.ts","sourceRoot":"","sources":["../../src/utils/postinstall-trampoline-source.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACf,MAAM,6BAA6B,CAAC;AAMrC;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,+CAA+C;IAC/C,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,kFAAkF;IAClF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjD;AAED;;;GAGG;AACH,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,kBAAkB,EAAE,QAAQ,CACnC,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,CAC1C,CAAC;IACF,QAAQ,CAAC,SAAS,EAAE,mBAAmB,CAAC;CACzC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CA4B5E"}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file postinstall-trampoline-source.ts
|
|
3
|
+
* @description The JS source that runs inside the detached reconciliation child.
|
|
4
|
+
*
|
|
5
|
+
* Split out of `postinstall-trampoline` because it is a different kind of code:
|
|
6
|
+
* everything here is a string template compiled into a `node -e` payload, which
|
|
7
|
+
* cannot import anything (it runs while package.json — the file it is repairing
|
|
8
|
+
* — is in flux), so every constant it needs is inlined as a JSON literal at
|
|
9
|
+
* build time. Keeping it beside the scheduling logic made one module that was
|
|
10
|
+
* half TypeScript and half generated JavaScript.
|
|
11
|
+
*
|
|
12
|
+
* The child's job, in order: announce that it is alive, wait for the package
|
|
13
|
+
* manager to exit, re-apply Lisa, and regenerate lockfiles when package.json no
|
|
14
|
+
* longer matches the baseline the package manager resolved its lockfile from —
|
|
15
|
+
* then record what it did (CodySwannGT/lisa#2750).
|
|
16
|
+
* @module utils
|
|
17
|
+
*/
|
|
18
|
+
import { PM_PATH_ENV_NAMES, PM_PATH_ENV_PREFIXES } from "./pm-env.js";
|
|
19
|
+
import { buildTrampolineRegenReporter, buildTrampolineReporter, } from "./postinstall-trampoline-report-source.js";
|
|
20
|
+
/**
|
|
21
|
+
* Build the inline JS source that runs inside the detached trampoline child.
|
|
22
|
+
* The source is passed to `node -e` so it must be self-contained (no imports that
|
|
23
|
+
* require resolution via package.json, which is exactly the file we're racing).
|
|
24
|
+
*
|
|
25
|
+
* The trampoline now runs in two phases inside the child:
|
|
26
|
+
* 1. Wait for the parent PM to exit, then re-invoke Lisa to reconcile package.json.
|
|
27
|
+
* 2. If Lisa's re-invocation mutated package.json, regenerate whichever lockfiles
|
|
28
|
+
* are present in the project so `bun install --frozen-lockfile` / `npm ci` in
|
|
29
|
+
* downstream CI jobs do not fail with "lockfile had changes, but lockfile is
|
|
30
|
+
* frozen." Lockfile regen runs with `--ignore-scripts` so the parent PM's
|
|
31
|
+
* lifecycle hooks are not re-invoked (which would retrigger this trampoline).
|
|
32
|
+
* @param params - Embedded constants to inline into the trampoline source
|
|
33
|
+
* @returns JS source suitable for `node -e`
|
|
34
|
+
*/
|
|
35
|
+
export function buildTrampolineSource(params) {
|
|
36
|
+
// JSON.stringify gives us safe inline literals for all primitive types.
|
|
37
|
+
const literals = {
|
|
38
|
+
parentPid: JSON.stringify(params.parentPid),
|
|
39
|
+
pollIntervalMs: JSON.stringify(params.pollIntervalMs),
|
|
40
|
+
maxWaitMs: JSON.stringify(params.maxWaitMs),
|
|
41
|
+
settleDelayMs: JSON.stringify(params.settleDelayMs),
|
|
42
|
+
lisaEntry: JSON.stringify(params.lisaEntry),
|
|
43
|
+
projectDir: JSON.stringify(params.projectDir),
|
|
44
|
+
nodeBin: JSON.stringify(params.nodeBin),
|
|
45
|
+
trampolineEnvVar: JSON.stringify(params.trampolineEnvVar),
|
|
46
|
+
lockfilePlans: JSON.stringify(params.lockfileRegenPlans),
|
|
47
|
+
pmEnvPrefixes: JSON.stringify(PM_PATH_ENV_PREFIXES),
|
|
48
|
+
pmEnvNames: JSON.stringify(PM_PATH_ENV_NAMES),
|
|
49
|
+
reportPath: JSON.stringify(params.reporting.reportPath),
|
|
50
|
+
reportSchemaVersion: JSON.stringify(params.reporting.reportSchemaVersion),
|
|
51
|
+
lisaVersion: JSON.stringify(params.reporting.lisaVersion),
|
|
52
|
+
baselineHash: JSON.stringify(params.reporting.baselinePackageJsonHash),
|
|
53
|
+
};
|
|
54
|
+
return [
|
|
55
|
+
buildTrampolinePrelude(literals),
|
|
56
|
+
buildTrampolineReporter(literals),
|
|
57
|
+
buildTrampolinePmHelpers(literals),
|
|
58
|
+
buildTrampolineHelpers(literals),
|
|
59
|
+
buildTrampolineRegenReporter(),
|
|
60
|
+
buildTrampolineMain(literals),
|
|
61
|
+
].join("\n");
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Inline `require` prelude + the lockfile plan table. Kept separate so each
|
|
65
|
+
* chunk of the trampoline source stays under the 75-line max-lines-per-function
|
|
66
|
+
* cap enforced by eslint.
|
|
67
|
+
* @param literals - Inlined JSON-safe literals
|
|
68
|
+
* @param literals.lockfilePlans - JSON-serialized lockfile plan table
|
|
69
|
+
* @param literals.pmEnvPrefixes - JSON-serialized package-manager env var prefixes to strip
|
|
70
|
+
* @param literals.pmEnvNames - JSON-serialized package-manager env var names to strip
|
|
71
|
+
* @returns JS source fragment
|
|
72
|
+
*/
|
|
73
|
+
function buildTrampolinePrelude(literals) {
|
|
74
|
+
return `
|
|
75
|
+
const { spawn } = require("node:child_process");
|
|
76
|
+
const { createHash } = require("node:crypto");
|
|
77
|
+
const {
|
|
78
|
+
existsSync,
|
|
79
|
+
mkdirSync,
|
|
80
|
+
readFileSync,
|
|
81
|
+
renameSync,
|
|
82
|
+
writeFileSync,
|
|
83
|
+
} = require("node:fs");
|
|
84
|
+
const path = require("node:path");
|
|
85
|
+
|
|
86
|
+
const LOCKFILE_PLANS = ${literals.lockfilePlans};
|
|
87
|
+
const PM_ENV_PREFIXES = ${literals.pmEnvPrefixes};
|
|
88
|
+
const PM_ENV_NAMES = ${literals.pmEnvNames};
|
|
89
|
+
// Strip PM path/lifecycle vars from spawned bun/Lisa env. Mirrors pm-env.ts.
|
|
90
|
+
function sanitizeEnv(env) {
|
|
91
|
+
const bad = (k) => PM_ENV_NAMES.indexOf(k) !== -1 || PM_ENV_PREFIXES.some((p) => k.startsWith(p));
|
|
92
|
+
return Object.fromEntries(Object.entries(env).filter(([k]) => !bad(k)));
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Package-manager helpers inlined into the trampoline child: the `engines`
|
|
98
|
+
* opt-out reader, the lockfile-presence detector, and the regen loop.
|
|
99
|
+
*
|
|
100
|
+
* The regen loop is still best-effort at the process level — a missing package
|
|
101
|
+
* manager binary must never cascade into a failed install — but it now returns
|
|
102
|
+
* which managers it tried and which failed, so the outcome can be recorded
|
|
103
|
+
* instead of swallowed (CodySwannGT/lisa#2750).
|
|
104
|
+
* @param literals - Inlined JSON-safe literals
|
|
105
|
+
* @param literals.projectDir - Project directory whose lockfiles are regenerated
|
|
106
|
+
* @returns JS source fragment
|
|
107
|
+
*/
|
|
108
|
+
function buildTrampolinePmHelpers(literals) {
|
|
109
|
+
return `
|
|
110
|
+
function enginesForbiddenManagers(dir) {
|
|
111
|
+
try {
|
|
112
|
+
const pkg = JSON.parse(readFileSync(path.join(dir, "package.json"), "utf8"));
|
|
113
|
+
const engines = (pkg && pkg.engines) || {};
|
|
114
|
+
return ["bun", "npm", "yarn", "pnpm"].filter(
|
|
115
|
+
(pm) => typeof engines[pm] === "string" && /please-use|do-not-use/i.test(engines[pm])
|
|
116
|
+
);
|
|
117
|
+
} catch {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function detectPackageManagers(dir) {
|
|
123
|
+
const forbidden = enginesForbiddenManagers(dir);
|
|
124
|
+
return Object.values(LOCKFILE_PLANS)
|
|
125
|
+
.filter((plan) => [plan.lockfile].concat(plan.lockfileAlternatives || []).some((f) => existsSync(path.join(dir, f))))
|
|
126
|
+
.map((plan) => plan.pm)
|
|
127
|
+
.filter((pm) => forbidden.indexOf(pm) === -1);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function regenerateLockfiles() {
|
|
131
|
+
const attempted = [];
|
|
132
|
+
const failed = [];
|
|
133
|
+
for (const pm of detectPackageManagers(${literals.projectDir})) {
|
|
134
|
+
const plan = LOCKFILE_PLANS[pm];
|
|
135
|
+
if (!plan) continue;
|
|
136
|
+
attempted.push(pm);
|
|
137
|
+
// Still best-effort at the process level: a missing PM binary must not
|
|
138
|
+
// cascade into an install failure. What changed is that the failure is
|
|
139
|
+
// now RECORDED instead of swallowed outright, which is what makes a
|
|
140
|
+
// regen that did not work distinguishable from one that had nothing to
|
|
141
|
+
// do (CodySwannGT/lisa#2750).
|
|
142
|
+
const ok = await spawnChild(plan.command, plan.args);
|
|
143
|
+
if (!ok) {
|
|
144
|
+
failed.push(pm + " (" + plan.command + " " + plan.args.join(" ") + ")");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return { attempted: attempted, failed: failed };
|
|
148
|
+
}
|
|
149
|
+
`;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Process helpers inlined into the trampoline child: parent-liveness probe,
|
|
153
|
+
* file hasher, the wait loop, the sanitised child spawner, and the Lisa
|
|
154
|
+
* re-invoker. Each mirrors an exported TS helper in `postinstall-trampoline` or
|
|
155
|
+
* `package-manager-detect`, so the logic stays test-covered via those.
|
|
156
|
+
* @param literals - Inlined JSON-safe literals
|
|
157
|
+
* @param literals.parentPid - Parent package-manager PID for liveness probe
|
|
158
|
+
* @param literals.pollIntervalMs - Poll interval for parent-liveness checks
|
|
159
|
+
* @param literals.maxWaitMs - Max wait deadline before bailing out
|
|
160
|
+
* @param literals.nodeBin - Node binary path to re-invoke Lisa with
|
|
161
|
+
* @param literals.lisaEntry - Absolute path to Lisa's dist/index.js
|
|
162
|
+
* @param literals.projectDir - Project directory Lisa will reconcile
|
|
163
|
+
* @param literals.trampolineEnvVar - Env var name used to mark child as trampoline
|
|
164
|
+
* @returns JS source fragment
|
|
165
|
+
*/
|
|
166
|
+
function buildTrampolineHelpers(literals) {
|
|
167
|
+
return `
|
|
168
|
+
function isAlive(pid) {
|
|
169
|
+
if (!pid || pid <= 1) return false;
|
|
170
|
+
try { process.kill(pid, 0); return true; } catch { return false; }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function hashFile(p) {
|
|
174
|
+
try { return createHash("sha256").update(readFileSync(p)).digest("hex"); }
|
|
175
|
+
catch { return null; }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function waitForParent() {
|
|
179
|
+
const deadline = Date.now() + ${literals.maxWaitMs};
|
|
180
|
+
while (Date.now() < deadline) {
|
|
181
|
+
if (!isAlive(${literals.parentPid})) return true;
|
|
182
|
+
await new Promise((r) => setTimeout(r, ${literals.pollIntervalMs}));
|
|
183
|
+
}
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function spawnChild(command, args) {
|
|
188
|
+
return new Promise((resolve) => {
|
|
189
|
+
try {
|
|
190
|
+
const child = spawn(command, args, {
|
|
191
|
+
cwd: ${literals.projectDir},
|
|
192
|
+
stdio: "ignore",
|
|
193
|
+
env: Object.assign(sanitizeEnv(process.env), { [${literals.trampolineEnvVar}]: "1" }),
|
|
194
|
+
});
|
|
195
|
+
child.on("exit", (code) => resolve(code === 0));
|
|
196
|
+
child.on("error", () => resolve(false));
|
|
197
|
+
} catch {
|
|
198
|
+
resolve(false);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function runLisa() {
|
|
204
|
+
return spawnChild(${literals.nodeBin}, [${literals.lisaEntry}, "--yes", "--skip-git-check", ${literals.projectDir}]);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
`;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Top-level async IIFE that orchestrates the trampoline child:
|
|
211
|
+
* 1) announce that the child is alive (so a spawn that produced nothing is
|
|
212
|
+
* distinguishable from one that ran),
|
|
213
|
+
* 2) wait for the parent PM to exit,
|
|
214
|
+
* 3) re-run Lisa,
|
|
215
|
+
* 4) regenerate lockfiles when package.json no longer matches the baseline the
|
|
216
|
+
* package manager resolved its lockfile from,
|
|
217
|
+
* 5) record the outcome either way.
|
|
218
|
+
*
|
|
219
|
+
* Step 4 is the fix for CodySwannGT/lisa#2750. It used to compare package.json
|
|
220
|
+
* before and after the child's OWN re-apply, and regenerate only when that
|
|
221
|
+
* re-apply changed something. On the path that matters — a plain `bun install`
|
|
222
|
+
* where the first apply (inside postinstall) already rewrote package.json and
|
|
223
|
+
* the re-apply is therefore idempotent — that comparison is always equal, so
|
|
224
|
+
* the regen never ran and the lockfile stayed exactly as stale as the install
|
|
225
|
+
* left it. Measured on a bun consumer: `bun.lock`'s mtime never moved, while
|
|
226
|
+
* the regen plan run by hand fixed the drift in 1.66 s.
|
|
227
|
+
*
|
|
228
|
+
* The baseline comparison asks the right question instead: package.json is
|
|
229
|
+
* compared against its content BEFORE the first apply, which is what the
|
|
230
|
+
* lockfile was built from. It also no longer requires the re-apply to have
|
|
231
|
+
* SUCCEEDED — a re-apply that fails leaves the first apply's drift in place,
|
|
232
|
+
* and refusing to regen there is the one case where the lockfile is guaranteed
|
|
233
|
+
* wrong.
|
|
234
|
+
*
|
|
235
|
+
* Timing out MUST NOT re-run Lisa — that would reintroduce the package.json
|
|
236
|
+
* race the trampoline is designed to avoid (parent PM still writing).
|
|
237
|
+
* @param literals - Inlined JSON-safe literals
|
|
238
|
+
* @param literals.settleDelayMs - Settle delay before re-invoking Lisa
|
|
239
|
+
* @param literals.projectDir - Project directory Lisa will reconcile
|
|
240
|
+
* @param literals.baselineHash - sha256 of package.json before the apply that scheduled this child
|
|
241
|
+
* @returns JS source fragment
|
|
242
|
+
*/
|
|
243
|
+
function buildTrampolineMain(literals) {
|
|
244
|
+
return `
|
|
245
|
+
(async () => {
|
|
246
|
+
try {
|
|
247
|
+
report(
|
|
248
|
+
"started",
|
|
249
|
+
"Waiting for the package manager to exit before re-applying Lisa.",
|
|
250
|
+
[]
|
|
251
|
+
);
|
|
252
|
+
const parentExited = await waitForParent();
|
|
253
|
+
if (!parentExited) {
|
|
254
|
+
report(
|
|
255
|
+
"parent-wait-timed-out",
|
|
256
|
+
"The package manager was still running after the wait deadline, so " +
|
|
257
|
+
"Lisa did not re-apply (re-applying under a live package manager " +
|
|
258
|
+
"is the race this exists to avoid). Lockfiles may be out of sync " +
|
|
259
|
+
"with package.json.",
|
|
260
|
+
[]
|
|
261
|
+
);
|
|
262
|
+
process.exit(0);
|
|
263
|
+
}
|
|
264
|
+
await new Promise((r) => setTimeout(r, ${literals.settleDelayMs}));
|
|
265
|
+
|
|
266
|
+
const pkgPath = path.join(${literals.projectDir}, "package.json");
|
|
267
|
+
const baselineHash = ${literals.baselineHash};
|
|
268
|
+
|
|
269
|
+
const lisaOk = await runLisa();
|
|
270
|
+
const reapplyNote = lisaOk
|
|
271
|
+
? ""
|
|
272
|
+
: " The re-apply itself exited non-zero; run it by hand to see why.";
|
|
273
|
+
|
|
274
|
+
const postHash = hashFile(pkgPath);
|
|
275
|
+
if (postHash === null) {
|
|
276
|
+
report(
|
|
277
|
+
"failed",
|
|
278
|
+
"package.json could not be read after the re-apply, so lockfile " +
|
|
279
|
+
"drift could not be assessed." + reapplyNote,
|
|
280
|
+
[]
|
|
281
|
+
);
|
|
282
|
+
process.exit(0);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Compare against what the package manager built the lockfile FROM,
|
|
286
|
+
// not against what this child's own idempotent re-apply changed.
|
|
287
|
+
if (baselineHash !== null && postHash === baselineHash) {
|
|
288
|
+
report(
|
|
289
|
+
"not-needed",
|
|
290
|
+
"package.json matches what the package manager resolved the " +
|
|
291
|
+
"lockfile from, so no lockfile can be stale." + reapplyNote,
|
|
292
|
+
[]
|
|
293
|
+
);
|
|
294
|
+
process.exit(0);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
reportRegen(await regenerateLockfiles(), reapplyNote);
|
|
298
|
+
|
|
299
|
+
process.exit(0);
|
|
300
|
+
} catch (error) {
|
|
301
|
+
report(
|
|
302
|
+
"failed",
|
|
303
|
+
"The reconciliation aborted before completing: " +
|
|
304
|
+
(error && error.message ? error.message : String(error)),
|
|
305
|
+
[]
|
|
306
|
+
);
|
|
307
|
+
process.exit(0);
|
|
308
|
+
}
|
|
309
|
+
})();
|
|
310
|
+
`;
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=postinstall-trampoline-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postinstall-trampoline-source.js","sourceRoot":"","sources":["../../src/utils/postinstall-trampoline-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { type LockfileRegenPlan, type PackageManager, LOCKFILE_REGEN_PLANS, detectPackageManagers, enginesForbiddenManagers, getLockfileRegenPlan } from "./package-manager-detect.js";
|
|
3
|
+
import type { TrampolineReporting } from "./postinstall-trampoline-source.js";
|
|
3
4
|
export { LOCKFILE_REGEN_PLANS, detectPackageManagers, enginesForbiddenManagers, getLockfileRegenPlan, };
|
|
4
|
-
export type { LockfileRegenPlan, PackageManager };
|
|
5
|
+
export type { LockfileRegenPlan, PackageManager, TrampolineReporting };
|
|
5
6
|
/**
|
|
6
7
|
* Determine whether this Lisa invocation is running as a package-manager lifecycle
|
|
7
8
|
* script (postinstall, prepare, etc.). Works across npm, bun, yarn, and pnpm since
|
|
@@ -103,8 +104,9 @@ export declare function regenerateLockfilesInProcess(projectDir: string, spawnFn
|
|
|
103
104
|
* @param projectDir - Absolute path to the project directory Lisa will reconcile
|
|
104
105
|
* @param lisaDistDir - Absolute path to Lisa's dist directory (where index.js lives)
|
|
105
106
|
* @param parentPid - PID of the package-manager process to wait on (usually process.ppid)
|
|
107
|
+
* @param reporting - Report path/schema plus the pre-apply package.json baseline the child compares against
|
|
106
108
|
* @param spawnFn - Optional spawn implementation; defaults to node:child_process spawn. Tests pass a vi.fn() spy here as a dependency-injection seam, avoiding the unreliable vi.doMock-on-builtins pattern that breaks under v8 coverage in CI runners.
|
|
107
109
|
* @returns Promise that resolves immediately after spawning the detached child
|
|
108
110
|
*/
|
|
109
|
-
export declare function scheduleReconciliationChild(projectDir: string, lisaDistDir: string, parentPid: number, spawnFn?: typeof spawn): Promise<void>;
|
|
111
|
+
export declare function scheduleReconciliationChild(projectDir: string, lisaDistDir: string, parentPid: number, reporting: TrampolineReporting, spawnFn?: typeof spawn): Promise<void>;
|
|
110
112
|
//# sourceMappingURL=postinstall-trampoline.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postinstall-trampoline.d.ts","sourceRoot":"","sources":["../../src/utils/postinstall-trampoline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"postinstall-trampoline.d.ts","sourceRoot":"","sources":["../../src/utils/postinstall-trampoline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAM3C,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACrB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAqC9E,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,GACrB,CAAC;AACF,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,mBAAmB,EAAE,CAAC;AAkBvE;;;;;GAKG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AA0BD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,IAAI,OAAO,CASvC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uCAAuC,CACrD,MAAM,EAAE,OAAO,GACd,OAAO,CAaT;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAIxD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOxD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,4BAA4B,CAChD,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,2BAA2B,CAC/C,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,mBAAmB,EAM9B,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,IAAI,CAAC,CAoCf"}
|