@pleri/olam-cli 0.1.144 → 0.1.146
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/commands/doctor.d.ts +53 -23
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +117 -46
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/logs.d.ts +17 -3
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +38 -35
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/memory/bridge.d.ts +57 -0
- package/dist/commands/memory/bridge.d.ts.map +1 -0
- package/dist/commands/memory/bridge.js +156 -0
- package/dist/commands/memory/bridge.js.map +1 -0
- package/dist/commands/memory/index.d.ts +3 -0
- package/dist/commands/memory/index.d.ts.map +1 -1
- package/dist/commands/memory/index.js +10 -1
- package/dist/commands/memory/index.js.map +1 -1
- package/dist/commands/memory/reclassify.d.ts +56 -0
- package/dist/commands/memory/reclassify.d.ts.map +1 -0
- package/dist/commands/memory/reclassify.js +177 -0
- package/dist/commands/memory/reclassify.js.map +1 -0
- package/dist/commands/memory/stats.d.ts +69 -0
- package/dist/commands/memory/stats.d.ts.map +1 -0
- package/dist/commands/memory/stats.js +164 -0
- package/dist/commands/memory/stats.js.map +1 -0
- package/dist/commands/skills-doctor.d.ts +14 -0
- package/dist/commands/skills-doctor.d.ts.map +1 -0
- package/dist/commands/skills-doctor.js +126 -0
- package/dist/commands/skills-doctor.js.map +1 -0
- package/dist/commands/skills-hook.d.ts +19 -0
- package/dist/commands/skills-hook.d.ts.map +1 -0
- package/dist/commands/skills-hook.js +99 -0
- package/dist/commands/skills-hook.js.map +1 -0
- package/dist/commands/skills-migrate-back.d.ts +21 -0
- package/dist/commands/skills-migrate-back.d.ts.map +1 -0
- package/dist/commands/skills-migrate-back.js +222 -0
- package/dist/commands/skills-migrate-back.js.map +1 -0
- package/dist/commands/skills-migrate.d.ts +33 -0
- package/dist/commands/skills-migrate.d.ts.map +1 -0
- package/dist/commands/skills-migrate.js +216 -0
- package/dist/commands/skills-migrate.js.map +1 -0
- package/dist/commands/skills-onboard.d.ts +26 -0
- package/dist/commands/skills-onboard.d.ts.map +1 -0
- package/dist/commands/skills-onboard.js +227 -0
- package/dist/commands/skills-onboard.js.map +1 -0
- package/dist/commands/skills-shadow-backups.d.ts +15 -0
- package/dist/commands/skills-shadow-backups.d.ts.map +1 -0
- package/dist/commands/skills-shadow-backups.js +132 -0
- package/dist/commands/skills-shadow-backups.js.map +1 -0
- package/dist/commands/skills-source.d.ts +37 -0
- package/dist/commands/skills-source.d.ts.map +1 -0
- package/dist/commands/skills-source.js +431 -0
- package/dist/commands/skills-source.js.map +1 -0
- package/dist/commands/skills.d.ts +11 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +170 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/status.d.ts +27 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +102 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/substrate-audit-log.d.ts +49 -0
- package/dist/commands/substrate-audit-log.d.ts.map +1 -0
- package/dist/commands/substrate-audit-log.js +148 -0
- package/dist/commands/substrate-audit-log.js.map +1 -0
- package/dist/commands/substrate.d.ts +60 -0
- package/dist/commands/substrate.d.ts.map +1 -0
- package/dist/commands/substrate.js +175 -0
- package/dist/commands/substrate.js.map +1 -0
- package/dist/commands/upgrade.d.ts +10 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +30 -0
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/image-digests.json +7 -7
- package/dist/index.js +8394 -3876
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +69 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +146 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/health-probes.d.ts +72 -0
- package/dist/lib/health-probes.d.ts.map +1 -1
- package/dist/lib/health-probes.js +218 -0
- package/dist/lib/health-probes.js.map +1 -1
- package/dist/lib/instrumentation.d.ts +85 -0
- package/dist/lib/instrumentation.d.ts.map +1 -0
- package/dist/lib/instrumentation.js +104 -0
- package/dist/lib/instrumentation.js.map +1 -0
- package/dist/lib/kubectl-wrap.d.ts +59 -0
- package/dist/lib/kubectl-wrap.d.ts.map +1 -0
- package/dist/lib/kubectl-wrap.js +130 -0
- package/dist/lib/kubectl-wrap.js.map +1 -0
- package/dist/lib/manifest-refresh.d.ts +95 -0
- package/dist/lib/manifest-refresh.d.ts.map +1 -0
- package/dist/lib/manifest-refresh.js +222 -0
- package/dist/lib/manifest-refresh.js.map +1 -0
- package/dist/lib/port-forward.d.ts +101 -0
- package/dist/lib/port-forward.d.ts.map +1 -0
- package/dist/lib/port-forward.js +240 -0
- package/dist/lib/port-forward.js.map +1 -0
- package/dist/lib/upgrade-kubernetes.d.ts +77 -0
- package/dist/lib/upgrade-kubernetes.d.ts.map +1 -0
- package/dist/lib/upgrade-kubernetes.js +277 -0
- package/dist/lib/upgrade-kubernetes.js.map +1 -0
- package/dist/mcp-server.js +3328 -1166
- package/host-cp/k8s/manifests/00-namespace.yaml +7 -0
- package/host-cp/k8s/manifests/10-serviceaccount.yaml +8 -0
- package/host-cp/k8s/manifests/20-rbac.yaml +34 -0
- package/host-cp/k8s/manifests/30-configmap.yaml +30 -0
- package/host-cp/k8s/manifests/45-pvc.yaml +27 -0
- package/host-cp/k8s/manifests/50-deployment.yaml +148 -0
- package/host-cp/k8s/manifests/60-service.yaml +22 -0
- package/host-cp/k8s/templates/40-secret-template.yaml +32 -0
- package/host-cp/src/agent-runtime-trigger.mjs +74 -4
- package/host-cp/src/engine-identity.mjs +32 -0
- package/host-cp/src/plan-chat-service.mjs +31 -7
- package/host-cp/src/server.mjs +219 -9
- package/package.json +3 -2
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* instrumentation.ts — stdout JSON event emitter for olam-cli.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1b A3 of olam-host-suite-phase-1b-k3s-beta-flavour (plan
|
|
5
|
+
* ~/.claude/plans/olam-host-suite-phase-1b-k3s-beta-flavour.md).
|
|
6
|
+
*
|
|
7
|
+
* Canonical event schema (Decision 21 — `olam.instr.v1`):
|
|
8
|
+
* {
|
|
9
|
+
* "schema": "olam.instr.v1",
|
|
10
|
+
* "event": "substrate.set" | "upgrade.complete",
|
|
11
|
+
* "install_id": "<uuid-v4-from-config>",
|
|
12
|
+
* "ts": "<ISO-8601-UTC>",
|
|
13
|
+
* <event-specific-fields>
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* Emission target: **stderr**, newline-delimited, each line prefixed with
|
|
17
|
+
* `OLAM_INSTR ` so consumers can grep-distinguish from other stderr output.
|
|
18
|
+
*
|
|
19
|
+
* Why stderr (and not stdout)? olam-cli's existing stdout-JSON contracts
|
|
20
|
+
* (e.g. `olam status --json`, `olam doctor --json`) reserve stdout for
|
|
21
|
+
* command output that consumers parse positionally. Mixing instrumentation
|
|
22
|
+
* into stdout breaks those contracts. stderr keeps the emitter
|
|
23
|
+
* grep-distinguishable AND segregated from the data-plane output.
|
|
24
|
+
*
|
|
25
|
+
* Consumers: month-6 aggregator script (`scripts/aggregate-substrate-events.mjs`,
|
|
26
|
+
* D20) groups events by `install_id` for Falsifiable signal thresholds 1+4.
|
|
27
|
+
*
|
|
28
|
+
* Defensive contract:
|
|
29
|
+
* - emit() MUST NOT throw if the config can't be read; falls back to a
|
|
30
|
+
* placeholder install_id ('unknown') + emits a one-line stderr WARN.
|
|
31
|
+
* - Schema is versioned (`olam.instr.v1`); v2 fields land additively.
|
|
32
|
+
* Consumers MUST tolerate unknown fields.
|
|
33
|
+
*/
|
|
34
|
+
export declare const INSTRUMENTATION_SCHEMA: "olam.instr.v1";
|
|
35
|
+
export declare const INSTRUMENTATION_PREFIX: "OLAM_INSTR ";
|
|
36
|
+
export type SubstrateSetEvent = {
|
|
37
|
+
schema: typeof INSTRUMENTATION_SCHEMA;
|
|
38
|
+
event: 'substrate.set';
|
|
39
|
+
install_id: string;
|
|
40
|
+
ts: string;
|
|
41
|
+
substrate: 'compose' | 'kubernetes';
|
|
42
|
+
flavor?: string;
|
|
43
|
+
};
|
|
44
|
+
export type UpgradeCompleteEvent = {
|
|
45
|
+
schema: typeof INSTRUMENTATION_SCHEMA;
|
|
46
|
+
event: 'upgrade.complete';
|
|
47
|
+
install_id: string;
|
|
48
|
+
ts: string;
|
|
49
|
+
substrate: 'compose' | 'kubernetes';
|
|
50
|
+
duration_ms: number;
|
|
51
|
+
flavor?: string;
|
|
52
|
+
};
|
|
53
|
+
export type InstrumentationEvent = SubstrateSetEvent | UpgradeCompleteEvent;
|
|
54
|
+
export type EmitOpts = {
|
|
55
|
+
/** Inject a stderr-like sink for tests; defaults to process.stderr. */
|
|
56
|
+
stderr?: NodeJS.WritableStream;
|
|
57
|
+
/** Inject a clock for tests; defaults to Date.now(). */
|
|
58
|
+
now?: () => Date;
|
|
59
|
+
/** Inject a config-reader for tests; defaults to readConfig(). */
|
|
60
|
+
installIdProvider?: () => string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Emit a `substrate.set` event. Called by `olam substrate set` ONLY on success
|
|
64
|
+
* (failure paths emit nothing — by design; thresholds count successful switches).
|
|
65
|
+
*
|
|
66
|
+
* @param payload — substrate + optional flavor
|
|
67
|
+
* @param opts — test-only injection points (stderr / now / installIdProvider)
|
|
68
|
+
*/
|
|
69
|
+
export declare function emitSubstrateSet(payload: {
|
|
70
|
+
substrate: 'compose' | 'kubernetes';
|
|
71
|
+
flavor?: string;
|
|
72
|
+
}, opts?: EmitOpts): void;
|
|
73
|
+
/**
|
|
74
|
+
* Emit an `upgrade.complete` event. Called by Phase C C2's substrate-aware
|
|
75
|
+
* `olam upgrade` ONLY on success.
|
|
76
|
+
*
|
|
77
|
+
* @param payload — substrate + duration_ms (and optional flavor)
|
|
78
|
+
* @param opts — test-only injection points
|
|
79
|
+
*/
|
|
80
|
+
export declare function emitUpgradeComplete(payload: {
|
|
81
|
+
substrate: 'compose' | 'kubernetes';
|
|
82
|
+
duration_ms: number;
|
|
83
|
+
flavor?: string;
|
|
84
|
+
}, opts?: EmitOpts): void;
|
|
85
|
+
//# sourceMappingURL=instrumentation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/lib/instrumentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAIH,eAAO,MAAM,sBAAsB,EAAG,eAAwB,CAAC;AAC/D,eAAO,MAAM,sBAAsB,EAAG,aAAsB,CAAC;AAE7D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,OAAO,sBAAsB,CAAC;IACtC,KAAK,EAAE,eAAe,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,SAAS,GAAG,YAAY,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,OAAO,sBAAsB,CAAC;IACtC,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,SAAS,GAAG,YAAY,CAAC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;AAE5E,MAAM,MAAM,QAAQ,GAAG;IACrB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,kEAAkE;IAClE,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAC;CAClC,CAAC;AAmCF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE;IAAE,SAAS,EAAE,SAAS,GAAG,YAAY,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,EACjE,IAAI,GAAE,QAAa,GAClB,IAAI,CAUN;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE;IACP,SAAS,EAAE,SAAS,GAAG,YAAY,CAAC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,EACD,IAAI,GAAE,QAAa,GAClB,IAAI,CAWN"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* instrumentation.ts — stdout JSON event emitter for olam-cli.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1b A3 of olam-host-suite-phase-1b-k3s-beta-flavour (plan
|
|
5
|
+
* ~/.claude/plans/olam-host-suite-phase-1b-k3s-beta-flavour.md).
|
|
6
|
+
*
|
|
7
|
+
* Canonical event schema (Decision 21 — `olam.instr.v1`):
|
|
8
|
+
* {
|
|
9
|
+
* "schema": "olam.instr.v1",
|
|
10
|
+
* "event": "substrate.set" | "upgrade.complete",
|
|
11
|
+
* "install_id": "<uuid-v4-from-config>",
|
|
12
|
+
* "ts": "<ISO-8601-UTC>",
|
|
13
|
+
* <event-specific-fields>
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* Emission target: **stderr**, newline-delimited, each line prefixed with
|
|
17
|
+
* `OLAM_INSTR ` so consumers can grep-distinguish from other stderr output.
|
|
18
|
+
*
|
|
19
|
+
* Why stderr (and not stdout)? olam-cli's existing stdout-JSON contracts
|
|
20
|
+
* (e.g. `olam status --json`, `olam doctor --json`) reserve stdout for
|
|
21
|
+
* command output that consumers parse positionally. Mixing instrumentation
|
|
22
|
+
* into stdout breaks those contracts. stderr keeps the emitter
|
|
23
|
+
* grep-distinguishable AND segregated from the data-plane output.
|
|
24
|
+
*
|
|
25
|
+
* Consumers: month-6 aggregator script (`scripts/aggregate-substrate-events.mjs`,
|
|
26
|
+
* D20) groups events by `install_id` for Falsifiable signal thresholds 1+4.
|
|
27
|
+
*
|
|
28
|
+
* Defensive contract:
|
|
29
|
+
* - emit() MUST NOT throw if the config can't be read; falls back to a
|
|
30
|
+
* placeholder install_id ('unknown') + emits a one-line stderr WARN.
|
|
31
|
+
* - Schema is versioned (`olam.instr.v1`); v2 fields land additively.
|
|
32
|
+
* Consumers MUST tolerate unknown fields.
|
|
33
|
+
*/
|
|
34
|
+
import { readConfig } from './config.js';
|
|
35
|
+
export const INSTRUMENTATION_SCHEMA = 'olam.instr.v1';
|
|
36
|
+
export const INSTRUMENTATION_PREFIX = 'OLAM_INSTR ';
|
|
37
|
+
function nowIso(now) {
|
|
38
|
+
const d = now ? now() : new Date();
|
|
39
|
+
return d.toISOString();
|
|
40
|
+
}
|
|
41
|
+
function resolveInstallId(opts) {
|
|
42
|
+
try {
|
|
43
|
+
if (opts.installIdProvider)
|
|
44
|
+
return opts.installIdProvider();
|
|
45
|
+
const cfg = readConfig();
|
|
46
|
+
return cfg.install_id;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return 'unknown';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function writeLine(stderr, payload) {
|
|
53
|
+
try {
|
|
54
|
+
const line = `${INSTRUMENTATION_PREFIX}${JSON.stringify(payload)}\n`;
|
|
55
|
+
stderr.write(line);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
// emit must never throw; surface the failure tersely to stderr.
|
|
59
|
+
try {
|
|
60
|
+
stderr.write(`[WARN] olam instrumentation emit failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// give up; emitter contract is best-effort.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Emit a `substrate.set` event. Called by `olam substrate set` ONLY on success
|
|
69
|
+
* (failure paths emit nothing — by design; thresholds count successful switches).
|
|
70
|
+
*
|
|
71
|
+
* @param payload — substrate + optional flavor
|
|
72
|
+
* @param opts — test-only injection points (stderr / now / installIdProvider)
|
|
73
|
+
*/
|
|
74
|
+
export function emitSubstrateSet(payload, opts = {}) {
|
|
75
|
+
const event = {
|
|
76
|
+
schema: INSTRUMENTATION_SCHEMA,
|
|
77
|
+
event: 'substrate.set',
|
|
78
|
+
install_id: resolveInstallId(opts),
|
|
79
|
+
ts: nowIso(opts.now),
|
|
80
|
+
substrate: payload.substrate,
|
|
81
|
+
...(payload.flavor !== undefined ? { flavor: payload.flavor } : {}),
|
|
82
|
+
};
|
|
83
|
+
writeLine(opts.stderr ?? process.stderr, event);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Emit an `upgrade.complete` event. Called by Phase C C2's substrate-aware
|
|
87
|
+
* `olam upgrade` ONLY on success.
|
|
88
|
+
*
|
|
89
|
+
* @param payload — substrate + duration_ms (and optional flavor)
|
|
90
|
+
* @param opts — test-only injection points
|
|
91
|
+
*/
|
|
92
|
+
export function emitUpgradeComplete(payload, opts = {}) {
|
|
93
|
+
const event = {
|
|
94
|
+
schema: INSTRUMENTATION_SCHEMA,
|
|
95
|
+
event: 'upgrade.complete',
|
|
96
|
+
install_id: resolveInstallId(opts),
|
|
97
|
+
ts: nowIso(opts.now),
|
|
98
|
+
substrate: payload.substrate,
|
|
99
|
+
duration_ms: payload.duration_ms,
|
|
100
|
+
...(payload.flavor !== undefined ? { flavor: payload.flavor } : {}),
|
|
101
|
+
};
|
|
102
|
+
writeLine(opts.stderr ?? process.stderr, event);
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=instrumentation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../src/lib/instrumentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,CAAC,MAAM,sBAAsB,GAAG,eAAwB,CAAC;AAC/D,MAAM,CAAC,MAAM,sBAAsB,GAAG,aAAsB,CAAC;AAgC7D,SAAS,MAAM,CAAC,GAAgB;IAC9B,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IACnC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;AACzB,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAc;IACtC,IAAI,CAAC;QACH,IAAI,IAAI,CAAC,iBAAiB;YAAE,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC5D,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,OAAO,GAAG,CAAC,UAAU,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,MAA6B,EAAE,OAA6B;IAC7E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,GAAG,sBAAsB,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;QACrE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,gEAAgE;QAChE,IAAI,CAAC;YACH,MAAM,CAAC,KAAK,CACV,4CACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,IAAI,CACL,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,4CAA4C;QAC9C,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAiE,EACjE,OAAiB,EAAE;IAEnB,MAAM,KAAK,GAAsB;QAC/B,MAAM,EAAE,sBAAsB;QAC9B,KAAK,EAAE,eAAe;QACtB,UAAU,EAAE,gBAAgB,CAAC,IAAI,CAAC;QAClC,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QACpB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpE,CAAC;IACF,SAAS,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAIC,EACD,OAAiB,EAAE;IAEnB,MAAM,KAAK,GAAyB;QAClC,MAAM,EAAE,sBAAsB;QAC9B,KAAK,EAAE,kBAAkB;QACzB,UAAU,EAAE,gBAAgB,CAAC,IAAI,CAAC;QAClC,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QACpB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpE,CAAC;IACF,SAAS,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kubectl-wrap.ts — subprocess timeout wrapper for kubectl invocations.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1b C1 of olam-host-suite-phase-1b-k3s-beta-flavour (plan
|
|
5
|
+
* ~/.claude/plans/olam-host-suite-phase-1b-k3s-beta-flavour.md).
|
|
6
|
+
*
|
|
7
|
+
* ALL kubectl invocations in olam-cli MUST go through `kubectlWrap`.
|
|
8
|
+
* Direct `child_process.spawn('kubectl', ...)` calls outside this module
|
|
9
|
+
* break the timeout guarantee and fail `audit:kubectl-callers` (Seam C1).
|
|
10
|
+
*
|
|
11
|
+
* Timeout lifecycle:
|
|
12
|
+
* 1. Spawn kubectl with the provided args.
|
|
13
|
+
* 2. Start wall-clock timer (default 120 000 ms; per-call configurable).
|
|
14
|
+
* 3. On timer fire: SIGTERM the process; wait 5 000 ms; SIGKILL if still alive.
|
|
15
|
+
* 4. Returns { ok: false, reason: 'timeout', … } immediately after the kill
|
|
16
|
+
* sequence completes (does not wait further for exit after SIGKILL).
|
|
17
|
+
*
|
|
18
|
+
* Returns:
|
|
19
|
+
* { ok: true, stdout, stderr, exitCode } — clean exit 0
|
|
20
|
+
* { ok: false, stdout, stderr, exitCode, reason } — non-zero exit or timeout
|
|
21
|
+
*
|
|
22
|
+
* reason values:
|
|
23
|
+
* 'timeout' — wall-clock limit exceeded; process SIGKILLed
|
|
24
|
+
* 'spawn' — spawn itself failed (e.g. kubectl not on PATH)
|
|
25
|
+
* 'nonzero' — kubectl exited with non-zero status
|
|
26
|
+
*/
|
|
27
|
+
import { spawn } from 'node:child_process';
|
|
28
|
+
export declare const DEFAULT_TIMEOUT_MS = 120000;
|
|
29
|
+
export type KubectlResult = {
|
|
30
|
+
ok: true;
|
|
31
|
+
stdout: string;
|
|
32
|
+
stderr: string;
|
|
33
|
+
exitCode: number;
|
|
34
|
+
} | {
|
|
35
|
+
ok: false;
|
|
36
|
+
stdout: string;
|
|
37
|
+
stderr: string;
|
|
38
|
+
exitCode: number;
|
|
39
|
+
reason: 'timeout' | 'spawn' | 'nonzero';
|
|
40
|
+
};
|
|
41
|
+
export interface KubectlWrapOpts {
|
|
42
|
+
/** Per-call timeout override (ms). Defaults to DEFAULT_TIMEOUT_MS (120 s). */
|
|
43
|
+
readonly timeout?: number;
|
|
44
|
+
/** Additional environment variables to merge into the subprocess env. */
|
|
45
|
+
readonly env?: Record<string, string>;
|
|
46
|
+
/**
|
|
47
|
+
* spawn factory — injectable for tests so unit tests never fork a real kubectl.
|
|
48
|
+
* Signature matches `child_process.spawn` return type.
|
|
49
|
+
*/
|
|
50
|
+
readonly spawnImpl?: typeof spawn;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Invoke kubectl with a timeout.
|
|
54
|
+
*
|
|
55
|
+
* @param args - kubectl arguments (e.g. ['get', 'pods', '-n', 'olam'])
|
|
56
|
+
* @param opts - timeout, env overrides, and spawn factory for injection
|
|
57
|
+
*/
|
|
58
|
+
export declare function kubectlWrap(args: readonly string[], opts?: KubectlWrapOpts): Promise<KubectlResult>;
|
|
59
|
+
//# sourceMappingURL=kubectl-wrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kubectl-wrap.d.ts","sourceRoot":"","sources":["../../src/lib/kubectl-wrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,eAAO,MAAM,kBAAkB,SAAU,CAAC;AAG1C,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAA;CAAE,CAAC;AAE7G,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,aAAa,CAAC,CAuFxB"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kubectl-wrap.ts — subprocess timeout wrapper for kubectl invocations.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1b C1 of olam-host-suite-phase-1b-k3s-beta-flavour (plan
|
|
5
|
+
* ~/.claude/plans/olam-host-suite-phase-1b-k3s-beta-flavour.md).
|
|
6
|
+
*
|
|
7
|
+
* ALL kubectl invocations in olam-cli MUST go through `kubectlWrap`.
|
|
8
|
+
* Direct `child_process.spawn('kubectl', ...)` calls outside this module
|
|
9
|
+
* break the timeout guarantee and fail `audit:kubectl-callers` (Seam C1).
|
|
10
|
+
*
|
|
11
|
+
* Timeout lifecycle:
|
|
12
|
+
* 1. Spawn kubectl with the provided args.
|
|
13
|
+
* 2. Start wall-clock timer (default 120 000 ms; per-call configurable).
|
|
14
|
+
* 3. On timer fire: SIGTERM the process; wait 5 000 ms; SIGKILL if still alive.
|
|
15
|
+
* 4. Returns { ok: false, reason: 'timeout', … } immediately after the kill
|
|
16
|
+
* sequence completes (does not wait further for exit after SIGKILL).
|
|
17
|
+
*
|
|
18
|
+
* Returns:
|
|
19
|
+
* { ok: true, stdout, stderr, exitCode } — clean exit 0
|
|
20
|
+
* { ok: false, stdout, stderr, exitCode, reason } — non-zero exit or timeout
|
|
21
|
+
*
|
|
22
|
+
* reason values:
|
|
23
|
+
* 'timeout' — wall-clock limit exceeded; process SIGKILLed
|
|
24
|
+
* 'spawn' — spawn itself failed (e.g. kubectl not on PATH)
|
|
25
|
+
* 'nonzero' — kubectl exited with non-zero status
|
|
26
|
+
*/
|
|
27
|
+
import { spawn } from 'node:child_process';
|
|
28
|
+
export const DEFAULT_TIMEOUT_MS = 120_000;
|
|
29
|
+
const SIGKILL_GRACE_MS = 5_000;
|
|
30
|
+
/**
|
|
31
|
+
* Invoke kubectl with a timeout.
|
|
32
|
+
*
|
|
33
|
+
* @param args - kubectl arguments (e.g. ['get', 'pods', '-n', 'olam'])
|
|
34
|
+
* @param opts - timeout, env overrides, and spawn factory for injection
|
|
35
|
+
*/
|
|
36
|
+
export async function kubectlWrap(args, opts = {}) {
|
|
37
|
+
const timeoutMs = opts.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
38
|
+
const spawnImpl = opts.spawnImpl ?? spawn;
|
|
39
|
+
const stdout = [];
|
|
40
|
+
const stderr = [];
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
let resolved = false;
|
|
43
|
+
let killTimer = null;
|
|
44
|
+
let sigkillTimer = null;
|
|
45
|
+
function safeResolve(r) {
|
|
46
|
+
if (resolved)
|
|
47
|
+
return;
|
|
48
|
+
resolved = true;
|
|
49
|
+
if (killTimer !== null) {
|
|
50
|
+
clearTimeout(killTimer);
|
|
51
|
+
killTimer = null;
|
|
52
|
+
}
|
|
53
|
+
if (sigkillTimer !== null) {
|
|
54
|
+
clearTimeout(sigkillTimer);
|
|
55
|
+
sigkillTimer = null;
|
|
56
|
+
}
|
|
57
|
+
resolve(r);
|
|
58
|
+
}
|
|
59
|
+
let child;
|
|
60
|
+
try {
|
|
61
|
+
child = spawnImpl('kubectl', [...args], {
|
|
62
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
63
|
+
env: { ...process.env, ...(opts.env ?? {}) },
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
resolve({
|
|
68
|
+
ok: false,
|
|
69
|
+
stdout: '',
|
|
70
|
+
stderr: err instanceof Error ? err.message : String(err),
|
|
71
|
+
exitCode: -1,
|
|
72
|
+
reason: 'spawn',
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (child.stdout) {
|
|
77
|
+
child.stdout.on('data', (chunk) => { stdout.push(chunk.toString('utf8')); });
|
|
78
|
+
}
|
|
79
|
+
if (child.stderr) {
|
|
80
|
+
child.stderr.on('data', (chunk) => { stderr.push(chunk.toString('utf8')); });
|
|
81
|
+
}
|
|
82
|
+
child.on('error', (err) => {
|
|
83
|
+
safeResolve({
|
|
84
|
+
ok: false,
|
|
85
|
+
stdout: stdout.join(''),
|
|
86
|
+
stderr: err.message,
|
|
87
|
+
exitCode: -1,
|
|
88
|
+
reason: 'spawn',
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
child.on('exit', (code) => {
|
|
92
|
+
const exitCode = code ?? -1;
|
|
93
|
+
const stdoutStr = stdout.join('');
|
|
94
|
+
const stderrStr = stderr.join('');
|
|
95
|
+
if (exitCode === 0) {
|
|
96
|
+
safeResolve({ ok: true, stdout: stdoutStr, stderr: stderrStr, exitCode });
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
safeResolve({ ok: false, stdout: stdoutStr, stderr: stderrStr, exitCode, reason: 'nonzero' });
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
// Wall-clock timeout: SIGTERM then SIGKILL after grace period.
|
|
103
|
+
killTimer = setTimeout(() => {
|
|
104
|
+
killTimer = null;
|
|
105
|
+
// SIGTERM — polite shutdown request.
|
|
106
|
+
try {
|
|
107
|
+
child.kill('SIGTERM');
|
|
108
|
+
}
|
|
109
|
+
catch { /* already exited */ }
|
|
110
|
+
// SIGKILL after 5 s grace — hard kill if SIGTERM wasn't enough.
|
|
111
|
+
sigkillTimer = setTimeout(() => {
|
|
112
|
+
sigkillTimer = null;
|
|
113
|
+
try {
|
|
114
|
+
child.kill('SIGKILL');
|
|
115
|
+
}
|
|
116
|
+
catch { /* already exited */ }
|
|
117
|
+
// Resolve immediately after SIGKILL without waiting for the 'exit' event.
|
|
118
|
+
// The 'exit' event may fire after this resolve; safeResolve is idempotent.
|
|
119
|
+
safeResolve({
|
|
120
|
+
ok: false,
|
|
121
|
+
stdout: stdout.join(''),
|
|
122
|
+
stderr: stderr.join(''),
|
|
123
|
+
exitCode: -1,
|
|
124
|
+
reason: 'timeout',
|
|
125
|
+
});
|
|
126
|
+
}, SIGKILL_GRACE_MS);
|
|
127
|
+
}, timeoutMs);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=kubectl-wrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kubectl-wrap.js","sourceRoot":"","sources":["../../src/lib/kubectl-wrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* manifest-refresh.ts — D14 --force-refresh-manifests flag implementation.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1b C2 of olam-host-suite-phase-1b-k3s-beta-flavour (plan
|
|
5
|
+
* ~/.claude/plans/olam-host-suite-phase-1b-k3s-beta-flavour.md).
|
|
6
|
+
*
|
|
7
|
+
* Decisions consumed:
|
|
8
|
+
* D14 — --force-refresh-manifests flag (NOT a subcommand); requires
|
|
9
|
+
* --accept-security-regression when security-sensitive fields differ.
|
|
10
|
+
*
|
|
11
|
+
* Security-sensitive manifest fields (refuse without --accept-security-regression):
|
|
12
|
+
* - securityContext (pod or container level)
|
|
13
|
+
* - resources.limits
|
|
14
|
+
* - capabilities (add/drop lists)
|
|
15
|
+
* - readOnlyRootFilesystem
|
|
16
|
+
* - RBAC Role rules (rules[].verbs / resources / apiGroups)
|
|
17
|
+
*
|
|
18
|
+
* Audit log:
|
|
19
|
+
* ~/.olam/state/manifest-refresh-audit.jsonl (mode 0600; append-only)
|
|
20
|
+
* ENOSPC aborts the refresh with "audit log unavailable: <error>".
|
|
21
|
+
* The file is created on first write; mode set via writeFileSync options.
|
|
22
|
+
*
|
|
23
|
+
* Two functions are exported:
|
|
24
|
+
* diffManifestSecurityFields — pure diff, no I/O; testable.
|
|
25
|
+
* runManifestRefresh — performs the full refresh with audit-log.
|
|
26
|
+
*/
|
|
27
|
+
import * as fs from 'node:fs';
|
|
28
|
+
export declare const MANIFEST_REFRESH_AUDIT_LOG: string;
|
|
29
|
+
/** Security-sensitive field paths checked by the diff. */
|
|
30
|
+
export declare const SECURITY_SENSITIVE_FIELDS: readonly ["securityContext", "resources.limits", "capabilities", "readOnlyRootFilesystem", "rules"];
|
|
31
|
+
export type SecuritySensitiveField = (typeof SECURITY_SENSITIVE_FIELDS)[number];
|
|
32
|
+
export interface ManifestDiffResult {
|
|
33
|
+
/** True when any security-sensitive field value differs between old and new. */
|
|
34
|
+
hasSecurityRegression: boolean;
|
|
35
|
+
/** List of field keys that changed. */
|
|
36
|
+
changedFields: SecuritySensitiveField[];
|
|
37
|
+
}
|
|
38
|
+
export interface ManifestRefreshAuditEntry {
|
|
39
|
+
ts: string;
|
|
40
|
+
manifests_path: string;
|
|
41
|
+
security_regression: boolean;
|
|
42
|
+
changed_fields: string[];
|
|
43
|
+
accepted: boolean;
|
|
44
|
+
operator_pid: number;
|
|
45
|
+
}
|
|
46
|
+
export interface ManifestRefreshDeps {
|
|
47
|
+
/** Override state dir for tests. */
|
|
48
|
+
readonly stateDir?: string;
|
|
49
|
+
/** Override manifests dir for tests. */
|
|
50
|
+
readonly manifestsDir?: string;
|
|
51
|
+
/** Override audit log path for tests. */
|
|
52
|
+
readonly auditLogPath?: string;
|
|
53
|
+
/** Override fs.readdirSync for tests. */
|
|
54
|
+
readonly readdirSync?: typeof fs.readdirSync;
|
|
55
|
+
/** Override fs.readFileSync for tests. */
|
|
56
|
+
readonly readFileSync?: typeof fs.readFileSync;
|
|
57
|
+
/** Override fs.writeFileSync for tests. */
|
|
58
|
+
readonly writeFileSync?: typeof fs.writeFileSync;
|
|
59
|
+
/** Override fs.existsSync for tests. */
|
|
60
|
+
readonly existsSync?: typeof fs.existsSync;
|
|
61
|
+
/** Override Date.now for tests. */
|
|
62
|
+
readonly now?: () => Date;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Pure diff between two YAML manifest file contents (as strings).
|
|
66
|
+
* Parses each as JSON (manifests may be JSON or YAML; we handle JSON
|
|
67
|
+
* and treat parse failures as "content changed" = regression to be safe).
|
|
68
|
+
*
|
|
69
|
+
* Used by runManifestRefresh and directly by tests.
|
|
70
|
+
*/
|
|
71
|
+
export declare function diffManifestSecurityFields(oldContent: string, newContent: string): ManifestDiffResult;
|
|
72
|
+
export type ManifestRefreshResult = {
|
|
73
|
+
ok: true;
|
|
74
|
+
message: string;
|
|
75
|
+
} | {
|
|
76
|
+
ok: false;
|
|
77
|
+
message: string;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Run the manifest refresh check.
|
|
81
|
+
*
|
|
82
|
+
* @param manifestsDir — path to ~/.olam/k8s/manifests/
|
|
83
|
+
* @param acceptRegression — true when --accept-security-regression is set
|
|
84
|
+
* @param deps — injectable for tests
|
|
85
|
+
*
|
|
86
|
+
* Returns ok=false when:
|
|
87
|
+
* - Security-sensitive fields differ AND !acceptRegression.
|
|
88
|
+
* - Audit log write fails (ENOSPC).
|
|
89
|
+
*
|
|
90
|
+
* On ok=true the audit log entry is written with accepted=true (or
|
|
91
|
+
* accepted=false when no regression was detected — regression-free
|
|
92
|
+
* refreshes are still audited).
|
|
93
|
+
*/
|
|
94
|
+
export declare function runManifestRefresh(manifestsDir: string, acceptRegression: boolean, deps?: ManifestRefreshDeps): Promise<ManifestRefreshResult>;
|
|
95
|
+
//# sourceMappingURL=manifest-refresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-refresh.d.ts","sourceRoot":"","sources":["../../src/lib/manifest-refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAI9B,eAAO,MAAM,0BAA0B,QAA4D,CAAC;AAEpG,0DAA0D;AAC1D,eAAO,MAAM,yBAAyB,qGAM5B,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhF,MAAM,WAAW,kBAAkB;IACjC,gFAAgF;IAChF,qBAAqB,EAAE,OAAO,CAAC;IAC/B,uCAAuC;IACvC,aAAa,EAAE,sBAAsB,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,wCAAwC;IACxC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,yCAAyC;IACzC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,yCAAyC;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,CAAC,WAAW,CAAC;IAC7C,0CAA0C;IAC1C,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC,YAAY,CAAC;IAC/C,2CAA2C;IAC3C,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC,aAAa,CAAC;IACjD,wCAAwC;IACxC,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC;IAC3C,mCAAmC;IACnC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAkCD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,kBAAkB,CA4BpB;AAyBD,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,kBAAkB,CACtC,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,OAAO,EACzB,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,qBAAqB,CAAC,CAqFhC"}
|