@memnexus-ai/mx-agent-cli 0.1.208 → 0.1.210
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/dist/__tests__/claude-sync.test.js +77 -5
- package/dist/__tests__/claude-sync.test.js.map +1 -1
- package/dist/__tests__/config-generation-gate.test.js +18 -9
- package/dist/__tests__/config-generation-gate.test.js.map +1 -1
- package/dist/__tests__/generate-claude.test.js +90 -6
- package/dist/__tests__/generate-claude.test.js.map +1 -1
- package/dist/__tests__/security-posture.test.d.ts +8 -0
- package/dist/__tests__/security-posture.test.d.ts.map +1 -0
- package/dist/__tests__/security-posture.test.js +82 -0
- package/dist/__tests__/security-posture.test.js.map +1 -0
- package/dist/__tests__/start-report-generation-capture.test.d.ts +17 -0
- package/dist/__tests__/start-report-generation-capture.test.d.ts.map +1 -0
- package/dist/__tests__/start-report-generation-capture.test.js +127 -0
- package/dist/__tests__/start-report-generation-capture.test.js.map +1 -0
- package/dist/__tests__/startup-health-render.test.d.ts +11 -0
- package/dist/__tests__/startup-health-render.test.d.ts.map +1 -0
- package/dist/__tests__/startup-health-render.test.js +60 -0
- package/dist/__tests__/startup-health-render.test.js.map +1 -0
- package/dist/__tests__/startup-report.test.js +34 -1
- package/dist/__tests__/startup-report.test.js.map +1 -1
- package/dist/__tests__/startup-shipper.test.d.ts +13 -0
- package/dist/__tests__/startup-shipper.test.d.ts.map +1 -0
- package/dist/__tests__/startup-shipper.test.js +149 -0
- package/dist/__tests__/startup-shipper.test.js.map +1 -0
- package/dist/agent-config/settings.json +3 -3
- package/dist/commands/dashboard.d.ts +21 -0
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +72 -1
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +16 -0
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +17 -0
- package/dist/commands/sync.js.map +1 -1
- package/dist/lib/claude.d.ts.map +1 -1
- package/dist/lib/claude.js +31 -6
- package/dist/lib/claude.js.map +1 -1
- package/dist/lib/security-posture.d.ts +69 -0
- package/dist/lib/security-posture.d.ts.map +1 -0
- package/dist/lib/security-posture.js +102 -0
- package/dist/lib/security-posture.js.map +1 -0
- package/dist/lib/startup-report.d.ts +19 -3
- package/dist/lib/startup-report.d.ts.map +1 -1
- package/dist/lib/startup-report.js +66 -8
- package/dist/lib/startup-report.js.map +1 -1
- package/dist/lib/startup-shipper.d.ts +55 -0
- package/dist/lib/startup-shipper.d.ts.map +1 -0
- package/dist/lib/startup-shipper.js +177 -0
- package/dist/lib/startup-shipper.js.map +1 -0
- package/package.json +1 -1
- package/scripts/generate-claude.mjs +45 -1
- package/dist/__tests__/config-mirror-integrity.test.d.ts +0 -32
- package/dist/__tests__/config-mirror-integrity.test.d.ts.map +0 -1
- package/dist/__tests__/config-mirror-integrity.test.js +0 -248
- package/dist/__tests__/config-mirror-integrity.test.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-shipper.d.ts","sourceRoot":"","sources":["../../src/lib/startup-shipper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAcH;oEACoE;AACpE,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAY7D;AAwCD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CA8BnF;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAgCjG"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* startup-shipper.ts — ships StartupReports to the central observability
|
|
3
|
+
* service (platform v2.58 Phase 2, #4350).
|
|
4
|
+
*
|
|
5
|
+
* Durability model mirrors the local JSONL from Phase 1: every report is first
|
|
6
|
+
* written to a spool dir (~/.mx-agent/logs/startup/spool/<uuid>.json), then a
|
|
7
|
+
* best-effort POST deletes it on success. A failed or token-less POST leaves the
|
|
8
|
+
* file spooled; the Stop-hook `mx-agent sync` flushes the backlog on the next
|
|
9
|
+
* session end.
|
|
10
|
+
*
|
|
11
|
+
* Discipline (identical to startup-report.ts): NOTHING here throws. Shipping is
|
|
12
|
+
* additive telemetry — it must never block, slow, or fail a session start. Every
|
|
13
|
+
* filesystem and network call is wrapped; the worst outcome of any failure is a
|
|
14
|
+
* report that stays spooled for a later retry.
|
|
15
|
+
*
|
|
16
|
+
* - spoolReport(json) — write one report to the spool. Drops files older
|
|
17
|
+
* than 14 days at write time (bounded disk/staleness).
|
|
18
|
+
* - flushSpool(cfg) — POST every spooled report, deleting each on 2xx.
|
|
19
|
+
* Enforces a 50-file cap (drops oldest beyond it) and
|
|
20
|
+
* skips entirely when no token is configured.
|
|
21
|
+
* - postSpoolFile(p, cfg) — POST one spool file with a 3 s timeout.
|
|
22
|
+
*/
|
|
23
|
+
import { mkdirSync, writeFileSync, readdirSync, statSync, unlinkSync, readFileSync } from 'fs';
|
|
24
|
+
import { homedir } from 'os';
|
|
25
|
+
import { join } from 'path';
|
|
26
|
+
import { randomUUID } from 'crypto';
|
|
27
|
+
/** Drop spooled files older than this at write time (bounded disk / staleness). */
|
|
28
|
+
const MAX_SPOOL_AGE_MS = 14 * 24 * 60 * 60 * 1000; // 14 days
|
|
29
|
+
/** Flush keeps at most this many files; the oldest beyond it are dropped. */
|
|
30
|
+
const MAX_SPOOL_FILES = 50;
|
|
31
|
+
/** Per-POST timeout — a single ship attempt must never stall longer than this. */
|
|
32
|
+
const POST_TIMEOUT_MS = 3000;
|
|
33
|
+
/** Absolute path to the spool directory. Computed at call time so tests that
|
|
34
|
+
* repoint $HOME observe the change (matches startup-report.ts). */
|
|
35
|
+
export function spoolDir() {
|
|
36
|
+
return join(homedir(), '.mx-agent', 'logs', 'startup', 'spool');
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Write a report JSON string to the spool. Never throws. Returns the file path,
|
|
40
|
+
* or null if the write could not be performed (e.g. a read-only spool dir).
|
|
41
|
+
* Drops files older than 14 days first.
|
|
42
|
+
*/
|
|
43
|
+
export function spoolReport(reportJson) {
|
|
44
|
+
try {
|
|
45
|
+
const dir = spoolDir();
|
|
46
|
+
mkdirSync(dir, { recursive: true });
|
|
47
|
+
dropAgedFiles(dir);
|
|
48
|
+
const path = join(dir, `${randomUUID()}.json`);
|
|
49
|
+
writeFileSync(path, reportJson);
|
|
50
|
+
return path;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Spooling is best-effort — a failure here is silently tolerated.
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Remove spool files whose mtime is older than the age cap. Never throws. */
|
|
58
|
+
function dropAgedFiles(dir) {
|
|
59
|
+
try {
|
|
60
|
+
const now = Date.now();
|
|
61
|
+
for (const f of readdirSync(dir)) {
|
|
62
|
+
if (!f.endsWith('.json'))
|
|
63
|
+
continue;
|
|
64
|
+
const p = join(dir, f);
|
|
65
|
+
try {
|
|
66
|
+
if (now - statSync(p).mtimeMs > MAX_SPOOL_AGE_MS)
|
|
67
|
+
unlinkSync(p);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
/* individual file error — skip it */
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
/* dir unreadable / absent — nothing to age out */
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** List spool files (basenames), oldest-first by mtime. Never throws. */
|
|
79
|
+
function listSpoolFilesOldestFirst(dir) {
|
|
80
|
+
let names;
|
|
81
|
+
try {
|
|
82
|
+
names = readdirSync(dir).filter((f) => f.endsWith('.json'));
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
return names
|
|
88
|
+
.map((f) => {
|
|
89
|
+
try {
|
|
90
|
+
return { f, m: statSync(join(dir, f)).mtimeMs };
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return { f, m: 0 };
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
.sort((a, b) => a.m - b.m)
|
|
97
|
+
.map((x) => x.f);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* POST one spool file to the observability service. Returns true and deletes the
|
|
101
|
+
* file on a 2xx response. Any non-2xx, network error, or timeout returns false
|
|
102
|
+
* and leaves the file spooled. Never throws.
|
|
103
|
+
*/
|
|
104
|
+
export async function postSpoolFile(path, cfg) {
|
|
105
|
+
if (!cfg.apiToken)
|
|
106
|
+
return false; // no token → cannot ship
|
|
107
|
+
let body;
|
|
108
|
+
try {
|
|
109
|
+
body = readFileSync(path, 'utf-8');
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false; // file vanished (concurrent flush) — nothing to do
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const res = await fetch(`${cfg.apiUrl}/v1/startup-reports`, {
|
|
116
|
+
method: 'POST',
|
|
117
|
+
headers: {
|
|
118
|
+
'Content-Type': 'application/json',
|
|
119
|
+
Authorization: `Bearer ${cfg.apiToken}`,
|
|
120
|
+
},
|
|
121
|
+
body,
|
|
122
|
+
signal: AbortSignal.timeout(POST_TIMEOUT_MS),
|
|
123
|
+
});
|
|
124
|
+
if (res.ok) {
|
|
125
|
+
try {
|
|
126
|
+
unlinkSync(path);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
/* already removed — fine */
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
return false; // 4xx/5xx — leave spooled for a later retry
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return false; // network / timeout / abort — leave spooled
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Flush the spool: POST every spooled report, deleting each on success.
|
|
141
|
+
*
|
|
142
|
+
* Enforces the 50-file cap first (drops the oldest beyond it, with a dim note if
|
|
143
|
+
* a logger is provided). If no token is configured the whole flush is skipped —
|
|
144
|
+
* files stay spooled so a later run with a token can retry. Never throws.
|
|
145
|
+
*/
|
|
146
|
+
export async function flushSpool(cfg) {
|
|
147
|
+
const dir = spoolDir();
|
|
148
|
+
let files = listSpoolFilesOldestFirst(dir);
|
|
149
|
+
// 50-file cap: drop the oldest beyond the cap so disk stays bounded.
|
|
150
|
+
if (files.length > MAX_SPOOL_FILES) {
|
|
151
|
+
const drop = files.slice(0, files.length - MAX_SPOOL_FILES);
|
|
152
|
+
for (const f of drop) {
|
|
153
|
+
try {
|
|
154
|
+
unlinkSync(join(dir, f));
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
/* ignore */
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (cfg.log) {
|
|
161
|
+
cfg.log(`startup-report spool exceeded ${MAX_SPOOL_FILES} files — dropped ${drop.length} oldest`);
|
|
162
|
+
}
|
|
163
|
+
files = files.slice(files.length - MAX_SPOOL_FILES);
|
|
164
|
+
}
|
|
165
|
+
// No token → cannot ship anything. Leave the backlog for a later sync.
|
|
166
|
+
if (!cfg.apiToken) {
|
|
167
|
+
return { shipped: 0, remaining: files.length };
|
|
168
|
+
}
|
|
169
|
+
let shipped = 0;
|
|
170
|
+
for (const f of files) {
|
|
171
|
+
if (await postSpoolFile(join(dir, f), cfg))
|
|
172
|
+
shipped++;
|
|
173
|
+
}
|
|
174
|
+
const remaining = listSpoolFilesOldestFirst(dir).length;
|
|
175
|
+
return { shipped, remaining };
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=startup-shipper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-shipper.js","sourceRoot":"","sources":["../../src/lib/startup-shipper.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
renameSync, rmSync, unlinkSync, writeFileSync,
|
|
69
69
|
} from 'fs';
|
|
70
70
|
import { homedir, tmpdir } from 'os';
|
|
71
|
-
import { dirname, join, relative, resolve, sep } from 'path';
|
|
71
|
+
import { basename, dirname, join, relative, resolve, sep } from 'path';
|
|
72
72
|
import { pathToFileURL } from 'url';
|
|
73
73
|
import { spawnSync } from 'child_process';
|
|
74
74
|
import { createHash } from 'crypto';
|
|
@@ -189,6 +189,46 @@ function pruneExtras(destDir, expected) {
|
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
// ── ORPHAN-SIBLING SWEEP (KI-GENERATOR-SWAP-SIBLINGS-UNGUARDED) ───────────────
|
|
193
|
+
//
|
|
194
|
+
// A crash mid-generation can only ever leave a `.claude.staging-*` sibling
|
|
195
|
+
// behind, and a failed prev-cleanup can leave a `.claude.prev-*` sibling — stale
|
|
196
|
+
// settings.json + hooks copies at an unguarded path (same-UID residual, not an
|
|
197
|
+
// escalation). Sweep any such orphan siblings of destRoot at generation ENTRY,
|
|
198
|
+
// before a new staging dir is created, so debris never accumulates across runs.
|
|
199
|
+
//
|
|
200
|
+
// Single-writer assumption: generation is serialized per worktree by the leader
|
|
201
|
+
// lock, so any staging/prev sibling present at entry is orphaned from a prior
|
|
202
|
+
// crashed run, never an in-flight concurrent generation. Best-effort per entry
|
|
203
|
+
// (an un-removable orphan never blocks generation). FAILS SAFE on a symlink
|
|
204
|
+
// sibling: unlink the LINK itself, never rmSync THROUGH it (a crafted link could
|
|
205
|
+
// otherwise delete outside the parent).
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Remove stale `.claude.staging-*` / `.claude.prev-*` siblings of `destRoot`.
|
|
209
|
+
* @param destRoot absolute path to the `.claude/` dir about to be generated
|
|
210
|
+
* @returns names of the siblings that were swept (for observability/tests)
|
|
211
|
+
*/
|
|
212
|
+
export function sweepOrphanSiblings(destRoot) {
|
|
213
|
+
const dest = resolve(destRoot);
|
|
214
|
+
const parent = dirname(dest);
|
|
215
|
+
const base = basename(dest);
|
|
216
|
+
const stagingPrefix = `${base}.staging-`;
|
|
217
|
+
const prevPrefix = `${base}.prev-`;
|
|
218
|
+
const swept = [];
|
|
219
|
+
for (const ent of listDir(parent)) {
|
|
220
|
+
if (!ent.name.startsWith(stagingPrefix) && !ent.name.startsWith(prevPrefix)) continue;
|
|
221
|
+
const p = join(parent, ent.name);
|
|
222
|
+
if (ent.isSymbolicLink()) {
|
|
223
|
+
// Never follow/delete THROUGH a link — remove only the link entry itself.
|
|
224
|
+
try { unlinkSync(p); swept.push(ent.name); } catch { /* best-effort */ }
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
try { rmSync(p, { recursive: true, force: true }); swept.push(ent.name); } catch { /* best-effort */ }
|
|
228
|
+
}
|
|
229
|
+
return swept;
|
|
230
|
+
}
|
|
231
|
+
|
|
192
232
|
// ── GENERATE-TO-STAGING + ATOMIC SWAP (Phase 4 prereq; Red Team finding 1) ────
|
|
193
233
|
//
|
|
194
234
|
// Generation writes the WHOLE tree into a staging dir (a sibling of destRoot so
|
|
@@ -488,6 +528,10 @@ export function generateClaudeConfig(opts) {
|
|
|
488
528
|
// reach the write phase); the existing dest is guarded separately at swap time.
|
|
489
529
|
const parent = dirname(destRoot);
|
|
490
530
|
mkdirSync(parent, { recursive: true });
|
|
531
|
+
// Sweep stale staging/prev siblings from a prior crashed run BEFORE creating a
|
|
532
|
+
// fresh staging dir (KI-GENERATOR-SWAP-SIBLINGS-UNGUARDED). The just-created
|
|
533
|
+
// staging dir below cannot be a sweep target: it does not exist yet.
|
|
534
|
+
sweepOrphanSiblings(destRoot);
|
|
491
535
|
const staging = mkdtempSync(join(parent, '.claude.staging-'));
|
|
492
536
|
|
|
493
537
|
try {
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mirror-drift guard for the agent-config → .claude config bundle.
|
|
3
|
-
*
|
|
4
|
-
* The repo keeps two copies of certain hook/skill/agent files:
|
|
5
|
-
* - mx-agent-system/agent-config/* — the source of truth that ships in the
|
|
6
|
-
* CLI's synced bundle (what `mx-agent` writes into new worktrees).
|
|
7
|
-
* - .claude/* — the copy that actually runs in this
|
|
8
|
-
* repo's own worktrees.
|
|
9
|
-
*
|
|
10
|
-
* These were hand-synced three separate times on 2026-07-02 (issue #3728,
|
|
11
|
-
* known-issues v68 / KI-HOOK-MIRROR-DRIFT-CHECK). When they drift, a worktree
|
|
12
|
-
* silently runs stale hook or skill logic. This test asserts byte-identity of
|
|
13
|
-
* the known mirror pairs so drift fails locally before it ships.
|
|
14
|
-
*
|
|
15
|
-
* ── PHASE 4 RETIREMENT (config-single-source migration #4304) ────────────────
|
|
16
|
-
* This entire file — both the MIRROR_PAIRS byte-identity suite and the
|
|
17
|
-
* `settings.json permissions parity` suite — is a TWO-COPY drift gate. It exists
|
|
18
|
-
* only because `.claude/` is still COMMITTED alongside `agent-config/`. Phase 4
|
|
19
|
-
* gitignores `.claude/`, deleting the second committed copy; at that point there
|
|
20
|
-
* is nothing to compare and these assertions become vacuous (every pair SKIPs on
|
|
21
|
-
* the `existsSync(claudeAbs)` guard once `.claude/` is untracked, and the parity
|
|
22
|
-
* suite short-circuits on `bothPresent`).
|
|
23
|
-
*
|
|
24
|
-
* DELETE this file in the Phase 4 PR. Its guarantee is replaced by the
|
|
25
|
-
* generation-correctness gate in `config-generation-gate.test.ts` (Phase 3),
|
|
26
|
-
* which proves the single remaining source generates correct, stable output —
|
|
27
|
-
* see that file's header for the kept-vs-added rationale. Until Phase 4 lands,
|
|
28
|
-
* KEEP these assertions: they are the live guard against a PR editing one side of
|
|
29
|
-
* the mirror while `.claude/` is still tracked.
|
|
30
|
-
*/
|
|
31
|
-
export {};
|
|
32
|
-
//# sourceMappingURL=config-mirror-integrity.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config-mirror-integrity.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config-mirror-integrity.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG"}
|
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mirror-drift guard for the agent-config → .claude config bundle.
|
|
3
|
-
*
|
|
4
|
-
* The repo keeps two copies of certain hook/skill/agent files:
|
|
5
|
-
* - mx-agent-system/agent-config/* — the source of truth that ships in the
|
|
6
|
-
* CLI's synced bundle (what `mx-agent` writes into new worktrees).
|
|
7
|
-
* - .claude/* — the copy that actually runs in this
|
|
8
|
-
* repo's own worktrees.
|
|
9
|
-
*
|
|
10
|
-
* These were hand-synced three separate times on 2026-07-02 (issue #3728,
|
|
11
|
-
* known-issues v68 / KI-HOOK-MIRROR-DRIFT-CHECK). When they drift, a worktree
|
|
12
|
-
* silently runs stale hook or skill logic. This test asserts byte-identity of
|
|
13
|
-
* the known mirror pairs so drift fails locally before it ships.
|
|
14
|
-
*
|
|
15
|
-
* ── PHASE 4 RETIREMENT (config-single-source migration #4304) ────────────────
|
|
16
|
-
* This entire file — both the MIRROR_PAIRS byte-identity suite and the
|
|
17
|
-
* `settings.json permissions parity` suite — is a TWO-COPY drift gate. It exists
|
|
18
|
-
* only because `.claude/` is still COMMITTED alongside `agent-config/`. Phase 4
|
|
19
|
-
* gitignores `.claude/`, deleting the second committed copy; at that point there
|
|
20
|
-
* is nothing to compare and these assertions become vacuous (every pair SKIPs on
|
|
21
|
-
* the `existsSync(claudeAbs)` guard once `.claude/` is untracked, and the parity
|
|
22
|
-
* suite short-circuits on `bothPresent`).
|
|
23
|
-
*
|
|
24
|
-
* DELETE this file in the Phase 4 PR. Its guarantee is replaced by the
|
|
25
|
-
* generation-correctness gate in `config-generation-gate.test.ts` (Phase 3),
|
|
26
|
-
* which proves the single remaining source generates correct, stable output —
|
|
27
|
-
* see that file's header for the kept-vs-added rationale. Until Phase 4 lands,
|
|
28
|
-
* KEEP these assertions: they are the live guard against a PR editing one side of
|
|
29
|
-
* the mirror while `.claude/` is still tracked.
|
|
30
|
-
*/
|
|
31
|
-
import { describe, it, expect } from 'vitest';
|
|
32
|
-
import { readFileSync, existsSync } from 'fs';
|
|
33
|
-
import { dirname, join } from 'path';
|
|
34
|
-
import { fileURLToPath } from 'url';
|
|
35
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
36
|
-
/**
|
|
37
|
-
* Ascend from this test file to the repo root — identified by the presence of
|
|
38
|
-
* the mx-agent-system/agent-config tree — instead of hard-coding a fixed number
|
|
39
|
-
* of `..` segments. Keeps the test correct if the file is ever relocated.
|
|
40
|
-
*/
|
|
41
|
-
function findRepoRoot(start) {
|
|
42
|
-
let dir = start;
|
|
43
|
-
for (let i = 0; i < 20; i++) {
|
|
44
|
-
if (existsSync(join(dir, 'mx-agent-system', 'agent-config')))
|
|
45
|
-
return dir;
|
|
46
|
-
const parent = dirname(dir);
|
|
47
|
-
if (parent === dir)
|
|
48
|
-
break;
|
|
49
|
-
dir = parent;
|
|
50
|
-
}
|
|
51
|
-
throw new Error(`config-mirror-integrity: could not locate repo root from ${start}`);
|
|
52
|
-
}
|
|
53
|
-
const REPO_ROOT = findRepoRoot(__dirname);
|
|
54
|
-
// [ .claude runtime copy, agent-config source of truth ] — repo-root-relative.
|
|
55
|
-
const MIRROR_PAIRS = [
|
|
56
|
-
[
|
|
57
|
-
'.claude/hooks/deploy-verification-reminder.sh',
|
|
58
|
-
'mx-agent-system/agent-config/hooks/deploy-verification-reminder.sh',
|
|
59
|
-
],
|
|
60
|
-
[
|
|
61
|
-
'.claude/hooks/deploy-verification-reminder.test.sh',
|
|
62
|
-
'mx-agent-system/agent-config/hooks/deploy-verification-reminder.test.sh',
|
|
63
|
-
],
|
|
64
|
-
// Shared git guards + push validators (#3912). These were mirror-drifting
|
|
65
|
-
// silently (git-mutation-guard.sh wasn't even present in the source-of-truth
|
|
66
|
-
// copy). Enforce byte-identity so a hook fix lands in both places.
|
|
67
|
-
[
|
|
68
|
-
'.claude/hooks/git-mutation-guard.sh',
|
|
69
|
-
'mx-agent-system/agent-config/hooks/git-mutation-guard.sh',
|
|
70
|
-
],
|
|
71
|
-
[
|
|
72
|
-
'.claude/hooks/git-mutation-guard.test.sh',
|
|
73
|
-
'mx-agent-system/agent-config/hooks/git-mutation-guard.test.sh',
|
|
74
|
-
],
|
|
75
|
-
[
|
|
76
|
-
'.claude/hooks/push-matcher.test.sh',
|
|
77
|
-
'mx-agent-system/agent-config/hooks/push-matcher.test.sh',
|
|
78
|
-
],
|
|
79
|
-
[
|
|
80
|
-
'.claude/hooks/mx-guard-hook.sh',
|
|
81
|
-
'mx-agent-system/agent-config/hooks/mx-guard-hook.sh',
|
|
82
|
-
],
|
|
83
|
-
[
|
|
84
|
-
'.claude/hooks/ci-validate-hook.sh',
|
|
85
|
-
'mx-agent-system/agent-config/hooks/ci-validate-hook.sh',
|
|
86
|
-
],
|
|
87
|
-
// Package→validate-script routing table extracted from ci-validate-hook.sh
|
|
88
|
-
// (Platform v2.35, #4017). Shipped default = the MemNexus map; the hook reads
|
|
89
|
-
// it at push time and fails open if absent. Guard it against mirror drift.
|
|
90
|
-
[
|
|
91
|
-
'.claude/hooks/ci-validate-routes.conf',
|
|
92
|
-
'mx-agent-system/agent-config/hooks/ci-validate-routes.conf',
|
|
93
|
-
],
|
|
94
|
-
[
|
|
95
|
-
'.claude/hooks/worktree-guard.sh',
|
|
96
|
-
'mx-agent-system/agent-config/hooks/worktree-guard.sh',
|
|
97
|
-
],
|
|
98
|
-
[
|
|
99
|
-
'.claude/hooks/worktree-guard.test.sh',
|
|
100
|
-
'mx-agent-system/agent-config/hooks/worktree-guard.test.sh',
|
|
101
|
-
],
|
|
102
|
-
[
|
|
103
|
-
'.claude/hooks/workflow-dispatch-guard.sh',
|
|
104
|
-
'mx-agent-system/agent-config/hooks/workflow-dispatch-guard.sh',
|
|
105
|
-
],
|
|
106
|
-
// Named-memory truncating-write guard (Platform v2.33, #3996). Blocks
|
|
107
|
-
// full-content `mx memories update|create --name X --content <NEW>` writes
|
|
108
|
-
// that shrink a shared named memory past a threshold — two real clobber
|
|
109
|
-
// incidents (2026-07-07 known-issues, 2026-07-09 escalations).
|
|
110
|
-
[
|
|
111
|
-
'.claude/hooks/named-memory-write-guard.sh',
|
|
112
|
-
'mx-agent-system/agent-config/hooks/named-memory-write-guard.sh',
|
|
113
|
-
],
|
|
114
|
-
[
|
|
115
|
-
'.claude/hooks/named-memory-write-guard.test.sh',
|
|
116
|
-
'mx-agent-system/agent-config/hooks/named-memory-write-guard.test.sh',
|
|
117
|
-
],
|
|
118
|
-
[
|
|
119
|
-
'.claude/skills/mx-post-merge-sync/SKILL.md',
|
|
120
|
-
'mx-agent-system/agent-config/skills/mx-post-merge-sync/SKILL.md',
|
|
121
|
-
],
|
|
122
|
-
[
|
|
123
|
-
'.claude/agents/git-expert.md',
|
|
124
|
-
'mx-agent-system/agent-config/agents/git-expert.md',
|
|
125
|
-
],
|
|
126
|
-
// Stop hook that warns when a merged session left its iteration log stale
|
|
127
|
-
// (Platform v2.35, #4017 — product-slug parameterization). Guard the pair.
|
|
128
|
-
[
|
|
129
|
-
'.claude/hooks/iteration-log-check.sh',
|
|
130
|
-
'mx-agent-system/agent-config/hooks/iteration-log-check.sh',
|
|
131
|
-
],
|
|
132
|
-
[
|
|
133
|
-
'.claude/hooks/iteration-log-check.test.sh',
|
|
134
|
-
'mx-agent-system/agent-config/hooks/iteration-log-check.test.sh',
|
|
135
|
-
],
|
|
136
|
-
// Agent/skill instruction files carrying the product-slug named-memory
|
|
137
|
-
// convention (Platform v2.35, #4017). These had been mirror-unguarded; the
|
|
138
|
-
// KI-HOOK-MIRROR-DRIFT-CHECK history (three hand-syncs, #3728) justifies
|
|
139
|
-
// enforcing byte-identity now that they are edited in lockstep.
|
|
140
|
-
[
|
|
141
|
-
'.claude/agents/memory-creator.md',
|
|
142
|
-
'mx-agent-system/agent-config/agents/memory-creator.md',
|
|
143
|
-
],
|
|
144
|
-
[
|
|
145
|
-
'.claude/agents/status-reporter.md',
|
|
146
|
-
'mx-agent-system/agent-config/agents/status-reporter.md',
|
|
147
|
-
],
|
|
148
|
-
[
|
|
149
|
-
'.claude/agents/prior-art-researcher.md',
|
|
150
|
-
'mx-agent-system/agent-config/agents/prior-art-researcher.md',
|
|
151
|
-
],
|
|
152
|
-
[
|
|
153
|
-
'.claude/agents/dogfood-auditor.md',
|
|
154
|
-
'mx-agent-system/agent-config/agents/dogfood-auditor.md',
|
|
155
|
-
],
|
|
156
|
-
[
|
|
157
|
-
'.claude/skills/mx-session-start/SKILL.md',
|
|
158
|
-
'mx-agent-system/agent-config/skills/mx-session-start/SKILL.md',
|
|
159
|
-
],
|
|
160
|
-
[
|
|
161
|
-
'.claude/skills/mx-iteration-close/SKILL.md',
|
|
162
|
-
'mx-agent-system/agent-config/skills/mx-iteration-close/SKILL.md',
|
|
163
|
-
],
|
|
164
|
-
// Session-start coordination brief hook (PR-D). The .claude runtime copy is
|
|
165
|
-
// synced by the normal distribution mechanism; until Gate 3 cutover it may be
|
|
166
|
-
// absent, in which case this pair SKIPS (the loop's existsSync guard). Once
|
|
167
|
-
// synced it is asserted byte-identical.
|
|
168
|
-
[
|
|
169
|
-
'.claude/hooks/coordination-brief.sh',
|
|
170
|
-
'mx-agent-system/agent-config/hooks/coordination-brief.sh',
|
|
171
|
-
],
|
|
172
|
-
// The co-located hook test suite. Not part of the shipped config surface
|
|
173
|
-
// (bundled-config excludes *.test.sh), but both dev copies are edited in
|
|
174
|
-
// lockstep — enforce byte-identity so the slug-validation tests can't drift
|
|
175
|
-
// (v2.40 QA finding, KI-COORDMT-HOOK-EXPORT-ROBUSTNESS).
|
|
176
|
-
[
|
|
177
|
-
'.claude/hooks/coordination-brief.test.sh',
|
|
178
|
-
'mx-agent-system/agent-config/hooks/coordination-brief.test.sh',
|
|
179
|
-
],
|
|
180
|
-
// NOTE: agent-config/settings.json is intentionally NOT a byte-identity mirror
|
|
181
|
-
// pair — the worktree .claude/settings.json has its SessionStart/hook wiring
|
|
182
|
-
// COMPOSED/merged from the source at deploy time (see claude.ts), so the two
|
|
183
|
-
// are not byte-identical by design. But the SECURITY-SENSITIVE part — the
|
|
184
|
-
// `permissions` allow/deny/ask lists and the experimental-teams flag — must
|
|
185
|
-
// never drift, because config-sync/`mx-agent new` ship agent-config's copy
|
|
186
|
-
// into worktrees verbatim. A prior drift (agent-config/settings.json lagged
|
|
187
|
-
// active by the "Monitor" allow entry from #4275, and the git-mutation-guard
|
|
188
|
-
// #4289 hardening) shipped a weakened config to consumer worktrees (#4294).
|
|
189
|
-
// The `settings.json permissions parity` describe below guards that surface
|
|
190
|
-
// semantically rather than by byte-identity.
|
|
191
|
-
];
|
|
192
|
-
describe('config mirror integrity', () => {
|
|
193
|
-
for (const [claudeRel, agentConfigRel] of MIRROR_PAIRS) {
|
|
194
|
-
const claudeAbs = join(REPO_ROOT, claudeRel);
|
|
195
|
-
const agentConfigAbs = join(REPO_ROOT, agentConfigRel);
|
|
196
|
-
// The .claude copy is not present in every checkout (e.g. a package-only
|
|
197
|
-
// export of the CLI). Skip rather than fail spuriously when it's absent.
|
|
198
|
-
if (!existsSync(claudeAbs)) {
|
|
199
|
-
it.skip(`${claudeRel} ↔ ${agentConfigRel} (SKIPPED: ${claudeRel} absent in this checkout)`, () => { });
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
it(`${claudeRel} is byte-identical to ${agentConfigRel}`, () => {
|
|
203
|
-
const claudeBytes = readFileSync(claudeAbs);
|
|
204
|
-
const agentConfigBytes = readFileSync(agentConfigAbs);
|
|
205
|
-
expect(claudeBytes.equals(agentConfigBytes)).toBe(true);
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
/**
|
|
210
|
-
* settings.json is composed (SessionStart/hook wiring is merged into the worktree
|
|
211
|
-
* copy at deploy time), so it is not a byte-identity mirror pair above. But its
|
|
212
|
-
* `permissions` block and the experimental-teams flag are shipped verbatim into
|
|
213
|
-
* consumer worktrees by config-sync/`mx-agent new`; drift there ships a weakened
|
|
214
|
-
* security posture. This suite asserts semantic parity of exactly those fields
|
|
215
|
-
* between the active runtime copy and the agent-config source of truth so a
|
|
216
|
-
* regression like #4294 (agent-config lagging the active allow-list) fails
|
|
217
|
-
* locally before it ships.
|
|
218
|
-
*/
|
|
219
|
-
describe('settings.json permissions parity', () => {
|
|
220
|
-
const claudeSettingsAbs = join(REPO_ROOT, '.claude/settings.json');
|
|
221
|
-
const agentConfigSettingsAbs = join(REPO_ROOT, 'mx-agent-system/agent-config/settings.json');
|
|
222
|
-
const bothPresent = existsSync(claudeSettingsAbs) && existsSync(agentConfigSettingsAbs);
|
|
223
|
-
const loadPerms = (abs) => {
|
|
224
|
-
const parsed = JSON.parse(readFileSync(abs, 'utf-8'));
|
|
225
|
-
return parsed;
|
|
226
|
-
};
|
|
227
|
-
(bothPresent ? it : it.skip)('permissions.allow is identical between active and agent-config', () => {
|
|
228
|
-
const active = loadPerms(claudeSettingsAbs);
|
|
229
|
-
const template = loadPerms(agentConfigSettingsAbs);
|
|
230
|
-
expect(template.permissions?.allow).toEqual(active.permissions?.allow);
|
|
231
|
-
});
|
|
232
|
-
(bothPresent ? it : it.skip)('permissions.deny is identical between active and agent-config', () => {
|
|
233
|
-
const active = loadPerms(claudeSettingsAbs);
|
|
234
|
-
const template = loadPerms(agentConfigSettingsAbs);
|
|
235
|
-
expect(template.permissions?.deny).toEqual(active.permissions?.deny);
|
|
236
|
-
});
|
|
237
|
-
(bothPresent ? it : it.skip)('permissions.ask is identical between active and agent-config', () => {
|
|
238
|
-
const active = loadPerms(claudeSettingsAbs);
|
|
239
|
-
const template = loadPerms(agentConfigSettingsAbs);
|
|
240
|
-
expect(template.permissions?.ask).toEqual(active.permissions?.ask);
|
|
241
|
-
});
|
|
242
|
-
(bothPresent ? it : it.skip)('CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS flag is identical between active and agent-config', () => {
|
|
243
|
-
const active = loadPerms(claudeSettingsAbs);
|
|
244
|
-
const template = loadPerms(agentConfigSettingsAbs);
|
|
245
|
-
expect(template.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS).toEqual(active.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS);
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
//# sourceMappingURL=config-mirror-integrity.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config-mirror-integrity.test.js","sourceRoot":"","sources":["../../src/__tests__/config-mirror-integrity.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D;;;;GAIG;AACH,SAAS,YAAY,CAAC,KAAa;IACjC,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,EAAE,cAAc,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QACzE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,4DAA4D,KAAK,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,SAAS,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;AAE1C,+EAA+E;AAC/E,MAAM,YAAY,GAA6C;IAC7D;QACE,+CAA+C;QAC/C,oEAAoE;KACrE;IACD;QACE,oDAAoD;QACpD,yEAAyE;KAC1E;IACD,0EAA0E;IAC1E,6EAA6E;IAC7E,mEAAmE;IACnE;QACE,qCAAqC;QACrC,0DAA0D;KAC3D;IACD;QACE,0CAA0C;QAC1C,+DAA+D;KAChE;IACD;QACE,oCAAoC;QACpC,yDAAyD;KAC1D;IACD;QACE,gCAAgC;QAChC,qDAAqD;KACtD;IACD;QACE,mCAAmC;QACnC,wDAAwD;KACzD;IACD,2EAA2E;IAC3E,8EAA8E;IAC9E,2EAA2E;IAC3E;QACE,uCAAuC;QACvC,4DAA4D;KAC7D;IACD;QACE,iCAAiC;QACjC,sDAAsD;KACvD;IACD;QACE,sCAAsC;QACtC,2DAA2D;KAC5D;IACD;QACE,0CAA0C;QAC1C,+DAA+D;KAChE;IACD,sEAAsE;IACtE,2EAA2E;IAC3E,wEAAwE;IACxE,+DAA+D;IAC/D;QACE,2CAA2C;QAC3C,gEAAgE;KACjE;IACD;QACE,gDAAgD;QAChD,qEAAqE;KACtE;IACD;QACE,4CAA4C;QAC5C,iEAAiE;KAClE;IACD;QACE,8BAA8B;QAC9B,mDAAmD;KACpD;IACD,0EAA0E;IAC1E,2EAA2E;IAC3E;QACE,sCAAsC;QACtC,2DAA2D;KAC5D;IACD;QACE,2CAA2C;QAC3C,gEAAgE;KACjE;IACD,uEAAuE;IACvE,2EAA2E;IAC3E,yEAAyE;IACzE,gEAAgE;IAChE;QACE,kCAAkC;QAClC,uDAAuD;KACxD;IACD;QACE,mCAAmC;QACnC,wDAAwD;KACzD;IACD;QACE,wCAAwC;QACxC,6DAA6D;KAC9D;IACD;QACE,mCAAmC;QACnC,wDAAwD;KACzD;IACD;QACE,0CAA0C;QAC1C,+DAA+D;KAChE;IACD;QACE,4CAA4C;QAC5C,iEAAiE;KAClE;IACD,4EAA4E;IAC5E,8EAA8E;IAC9E,4EAA4E;IAC5E,wCAAwC;IACxC;QACE,qCAAqC;QACrC,0DAA0D;KAC3D;IACD,yEAAyE;IACzE,yEAAyE;IACzE,4EAA4E;IAC5E,yDAAyD;IACzD;QACE,0CAA0C;QAC1C,+DAA+D;KAChE;IACD,+EAA+E;IAC/E,6EAA6E;IAC7E,6EAA6E;IAC7E,0EAA0E;IAC1E,4EAA4E;IAC5E,2EAA2E;IAC3E,4EAA4E;IAC5E,6EAA6E;IAC7E,4EAA4E;IAC5E,4EAA4E;IAC5E,6CAA6C;CAC9C,CAAC;AAEF,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,KAAK,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,IAAI,YAAY,EAAE,CAAC;QACvD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC7C,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QAEvD,yEAAyE;QACzE,yEAAyE;QACzE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3B,EAAE,CAAC,IAAI,CAAC,GAAG,SAAS,MAAM,cAAc,cAAc,SAAS,2BAA2B,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACtG,SAAS;QACX,CAAC;QAED,EAAE,CAAC,GAAG,SAAS,yBAAyB,cAAc,EAAE,EAAE,GAAG,EAAE;YAC7D,MAAM,WAAW,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;YAC5C,MAAM,gBAAgB,GAAG,YAAY,CAAC,cAAc,CAAC,CAAC;YACtD,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAEH;;;;;;;;;GASG;AACH,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,EAAE,uBAAuB,CAAC,CAAC;IACnE,MAAM,sBAAsB,GAAG,IAAI,CACjC,SAAS,EACT,4CAA4C,CAC7C,CAAC;IAEF,MAAM,WAAW,GACf,UAAU,CAAC,iBAAiB,CAAC,IAAI,UAAU,CAAC,sBAAsB,CAAC,CAAC;IAEtE,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,EAAE;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAGnD,CAAC;QACF,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAC1B,gEAAgE,EAChE,GAAG,EAAE;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACnD,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IACzE,CAAC,CACF,CAAC;IAEF,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAC1B,+DAA+D,EAC/D,GAAG,EAAE;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACnD,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACvE,CAAC,CACF,CAAC;IAEF,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAC1B,8DAA8D,EAC9D,GAAG,EAAE;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACnD,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IACrE,CAAC,CACF,CAAC;IAEF,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAC1B,wFAAwF,EACxF,GAAG,EAAE;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACnD,MAAM,CAAC,QAAQ,CAAC,oCAAoC,CAAC,CAAC,OAAO,CAC3D,MAAM,CAAC,oCAAoC,CAC5C,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC,CAAC,CAAC"}
|