prismalens 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -2
- package/dist/bin/prismalens.js +13 -0
- package/dist/bin/prismalens.js.map +1 -1
- package/dist/src/cli/doctor.d.ts +15 -1
- package/dist/src/cli/doctor.d.ts.map +1 -1
- package/dist/src/cli/doctor.js +107 -56
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/flags.d.ts +2 -0
- package/dist/src/cli/flags.d.ts.map +1 -0
- package/dist/src/cli/flags.js +25 -0
- package/dist/src/cli/flags.js.map +1 -0
- package/dist/src/cli/grouping.d.ts +18 -0
- package/dist/src/cli/grouping.d.ts.map +1 -0
- package/dist/src/cli/grouping.js +169 -0
- package/dist/src/cli/grouping.js.map +1 -0
- package/dist/src/cli/init.d.ts.map +1 -1
- package/dist/src/cli/init.js +26 -11
- package/dist/src/cli/init.js.map +1 -1
- package/dist/src/cli/investigate.d.ts +12 -0
- package/dist/src/cli/investigate.d.ts.map +1 -1
- package/dist/src/cli/investigate.js +142 -140
- package/dist/src/cli/investigate.js.map +1 -1
- package/dist/src/cli/listen.d.ts +50 -0
- package/dist/src/cli/listen.d.ts.map +1 -0
- package/dist/src/cli/listen.js +249 -0
- package/dist/src/cli/listen.js.map +1 -0
- package/dist/src/cli/report.d.ts +19 -0
- package/dist/src/cli/report.d.ts.map +1 -0
- package/dist/src/cli/report.js +60 -0
- package/dist/src/cli/report.js.map +1 -0
- package/dist/src/cli/serve.d.ts.map +1 -1
- package/dist/src/cli/serve.js +11 -2
- package/dist/src/cli/serve.js.map +1 -1
- package/dist/src/cli/status.d.ts +14 -0
- package/dist/src/cli/status.d.ts.map +1 -0
- package/dist/src/cli/status.js +83 -0
- package/dist/src/cli/status.js.map +1 -0
- package/dist/src/config/loader.d.ts.map +1 -1
- package/dist/src/config/loader.js +28 -2
- package/dist/src/config/loader.js.map +1 -1
- package/dist/src/config/schema.d.ts +121 -0
- package/dist/src/config/schema.d.ts.map +1 -1
- package/dist/src/config/schema.js +47 -1
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/core/caps.d.ts +36 -0
- package/dist/src/core/caps.d.ts.map +1 -0
- package/dist/src/core/caps.js +36 -0
- package/dist/src/core/caps.js.map +1 -0
- package/dist/src/core/db.d.ts +3 -0
- package/dist/src/core/db.d.ts.map +1 -0
- package/dist/src/core/db.js +71 -0
- package/dist/src/core/db.js.map +1 -0
- package/dist/src/core/file-session-store.js +2 -2
- package/dist/src/core/file-session-store.js.map +1 -1
- package/dist/src/core/run-investigation.d.ts +10 -0
- package/dist/src/core/run-investigation.d.ts.map +1 -1
- package/dist/src/core/run-investigation.js +85 -30
- package/dist/src/core/run-investigation.js.map +1 -1
- package/dist/src/core/session.d.ts +22 -1
- package/dist/src/core/session.d.ts.map +1 -1
- package/dist/src/core/session.js +6 -129
- package/dist/src/core/session.js.map +1 -1
- package/dist/src/core/slack-notify.d.ts +9 -0
- package/dist/src/core/slack-notify.d.ts.map +1 -0
- package/dist/src/core/slack-notify.js +57 -0
- package/dist/src/core/slack-notify.js.map +1 -0
- package/dist/src/core/sqlite-session-store.d.ts +39 -0
- package/dist/src/core/sqlite-session-store.d.ts.map +1 -0
- package/dist/src/core/sqlite-session-store.js +239 -0
- package/dist/src/core/sqlite-session-store.js.map +1 -0
- package/dist/src/http/server.d.ts +26 -0
- package/dist/src/http/server.d.ts.map +1 -0
- package/dist/src/http/server.js +151 -0
- package/dist/src/http/server.js.map +1 -0
- package/package.json +6 -5
|
@@ -12,28 +12,61 @@
|
|
|
12
12
|
* JSON-RPC `serve` path stay in lockstep, unchanged.
|
|
13
13
|
*/
|
|
14
14
|
import { resolve } from "node:path";
|
|
15
|
+
import { ensureV1, resolveCredentials } from "@prismalens/config/credentials";
|
|
15
16
|
import { INVESTIGATION_DEFAULTS } from "@prismalens/config/investigation";
|
|
16
|
-
import { getDefaultModel } from "@prismalens/config/llm";
|
|
17
|
-
import { resolveInvestigation as engineResolveInvestigation, } from "@prismalens/engine";
|
|
17
|
+
import { AUTO_SELECT_PROVIDER_IDS, getDefaultModel, } from "@prismalens/config/llm";
|
|
18
|
+
import { coerceFiringAlert, resolveInvestigation as engineResolveInvestigation, } from "@prismalens/engine";
|
|
18
19
|
/**
|
|
19
20
|
* Resolve a seed alert + merged `pl.config.yaml` into the inputs
|
|
20
21
|
* `investigateIncidentStream` wants: applies the shared host-local fallbacks, reads
|
|
21
22
|
* the BYO-key secrets from env, and delegates the actual build to the engine
|
|
22
23
|
* resolver. Throws (via the engine) on a hard input error.
|
|
23
24
|
*/
|
|
25
|
+
export function isSynthConfigured(config) {
|
|
26
|
+
if (config.synth.provider) {
|
|
27
|
+
return (resolveCredentials(config.synth.provider, config.synth.base_url)
|
|
28
|
+
.source !== "none");
|
|
29
|
+
}
|
|
30
|
+
for (const id of AUTO_SELECT_PROVIDER_IDS) {
|
|
31
|
+
if (resolveCredentials(id).source !== "none") {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
24
37
|
export function resolveInvestigation(args, config) {
|
|
25
38
|
// cwd = --repo (a local repo path) else the current directory.
|
|
26
39
|
const cwd = args.repo ? resolve(args.repo) : process.cwd();
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
let providerId = config.synth.provider;
|
|
41
|
+
let creds;
|
|
42
|
+
if (providerId) {
|
|
43
|
+
creds = resolveCredentials(providerId, config.synth.base_url);
|
|
44
|
+
if (providerId === "custom" && !creds.baseURL) {
|
|
45
|
+
throw new Error("Custom LLM provider requires synth.base_url to be configured.");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
for (const id of AUTO_SELECT_PROVIDER_IDS) {
|
|
50
|
+
const candidate = resolveCredentials(id);
|
|
51
|
+
if (candidate.source !== "none") {
|
|
52
|
+
providerId = id;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (providerId) {
|
|
57
|
+
creds = resolveCredentials(providerId, config.synth.base_url);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
creds = { providerId: "ollama", source: "none" };
|
|
61
|
+
providerId = "ollama";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const synthModel = config.synth.model ??
|
|
65
|
+
config.agent.model ??
|
|
66
|
+
getDefaultModel(providerId) ??
|
|
67
|
+
"";
|
|
68
|
+
if (!synthModel && creds.source !== "none") {
|
|
69
|
+
throw new Error("No synthesis model configured (set synth.model or agent.model).");
|
|
37
70
|
}
|
|
38
71
|
// Context enrichment (ADR-0015) — a single-alert CLI run is NOT context-free: it
|
|
39
72
|
// still knows the service/repos/logs from `prismalens.config.yaml`.
|
|
@@ -60,12 +93,15 @@ export function resolveInvestigation(args, config) {
|
|
|
60
93
|
// engine's SandboxLimits shape, dropping unset ones so "no cap" stays no cap
|
|
61
94
|
// (never a lying default). Only present when at least one knob is set.
|
|
62
95
|
const limits = toSandboxLimits(config.agent.limits);
|
|
96
|
+
const telemetry = {
|
|
97
|
+
prometheusUrl: config.telemetry.prometheusUrl ??
|
|
98
|
+
INVESTIGATION_DEFAULTS.telemetry.prometheusUrl,
|
|
99
|
+
alertmanagerUrl: config.telemetry.alertmanagerUrl ??
|
|
100
|
+
INVESTIGATION_DEFAULTS.telemetry.alertmanagerUrl,
|
|
101
|
+
apiUrl: config.telemetry.apiUrl ?? INVESTIGATION_DEFAULTS.telemetry.apiUrl,
|
|
102
|
+
};
|
|
63
103
|
const request = {
|
|
64
|
-
alert: args.alert,
|
|
65
104
|
query: args.query,
|
|
66
|
-
...(service ? { service } : {}),
|
|
67
|
-
...(repos ? { repos } : {}),
|
|
68
|
-
...(logs ? { logs } : {}),
|
|
69
105
|
harness: harnessName,
|
|
70
106
|
permissionMode,
|
|
71
107
|
...(harnessNative ? { harnessNative } : {}),
|
|
@@ -79,27 +115,43 @@ export function resolveInvestigation(args, config) {
|
|
|
79
115
|
...(config.agent.max_branches
|
|
80
116
|
? { maxBranches: config.agent.max_branches }
|
|
81
117
|
: {}),
|
|
118
|
+
...(args.maxTurns ? { maxTurns: args.maxTurns } : {}),
|
|
82
119
|
model: config.agent.model,
|
|
83
120
|
cwd,
|
|
84
|
-
telemetry: {
|
|
85
|
-
prometheusUrl: config.telemetry.prometheusUrl ??
|
|
86
|
-
INVESTIGATION_DEFAULTS.telemetry.prometheusUrl,
|
|
87
|
-
alertmanagerUrl: config.telemetry.alertmanagerUrl ??
|
|
88
|
-
INVESTIGATION_DEFAULTS.telemetry.alertmanagerUrl,
|
|
89
|
-
apiUrl: config.telemetry.apiUrl ?? INVESTIGATION_DEFAULTS.telemetry.apiUrl,
|
|
90
|
-
},
|
|
91
121
|
synth: {
|
|
92
|
-
providerId:
|
|
122
|
+
providerId: providerId,
|
|
93
123
|
model: synthModel,
|
|
94
|
-
apiKey,
|
|
95
|
-
baseURL:
|
|
124
|
+
apiKey: creds.apiKey ?? "",
|
|
125
|
+
baseURL: creds.baseURL,
|
|
126
|
+
configured: creds.source !== "none",
|
|
96
127
|
},
|
|
97
128
|
harnessEnv: {
|
|
98
129
|
OPENAI_API_KEY: process.env.OLLAMA_API_KEY ?? process.env.OPENAI_API_KEY,
|
|
99
|
-
OPENAI_BASE_URL:
|
|
130
|
+
OPENAI_BASE_URL: ensureV1(process.env.OLLAMA_BASE_URL ??
|
|
131
|
+
process.env.OPENAI_BASE_URL ??
|
|
132
|
+
INVESTIGATION_DEFAULTS.synth.baseURL),
|
|
100
133
|
},
|
|
101
134
|
initTimeoutMs: INVESTIGATION_DEFAULTS.harnessInitTimeoutMs,
|
|
102
135
|
};
|
|
136
|
+
if (args.alerts && args.alerts.length > 0) {
|
|
137
|
+
request.context = {
|
|
138
|
+
alerts: args.alerts.map(coerceFiringAlert),
|
|
139
|
+
telemetry,
|
|
140
|
+
...(service ? { service } : {}),
|
|
141
|
+
...(repos ? { repos } : {}),
|
|
142
|
+
...(logs ? { logs } : {}),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
request.alert = args.alert;
|
|
147
|
+
request.telemetry = telemetry;
|
|
148
|
+
if (service)
|
|
149
|
+
request.service = service;
|
|
150
|
+
if (repos)
|
|
151
|
+
request.repos = repos;
|
|
152
|
+
if (logs)
|
|
153
|
+
request.logs = logs;
|
|
154
|
+
}
|
|
103
155
|
return engineResolveInvestigation(request);
|
|
104
156
|
}
|
|
105
157
|
/**
|
|
@@ -122,7 +174,7 @@ function toSandboxLimits(limits) {
|
|
|
122
174
|
* telemetry + cwd, today's behaviour — never an error).
|
|
123
175
|
*/
|
|
124
176
|
function selectService(args, config) {
|
|
125
|
-
const name = args.service ?? pickServiceLabel(args.alert);
|
|
177
|
+
const name = args.service ?? pickServiceLabel(args.alerts?.[0] ?? args.alert);
|
|
126
178
|
if (!name)
|
|
127
179
|
return undefined;
|
|
128
180
|
const cfg = config.services[name];
|
|
@@ -135,8 +187,11 @@ function selectService(args, config) {
|
|
|
135
187
|
...(cfg.depends_on.length > 0 ? { dependsOn: cfg.depends_on } : {}),
|
|
136
188
|
};
|
|
137
189
|
}
|
|
138
|
-
/**
|
|
139
|
-
|
|
190
|
+
/**
|
|
191
|
+
* The alert's service identity label — `service`, else `namespace`, else `job`.
|
|
192
|
+
* Exported for the `listen` path's per-payload repo resolution (issue #58 AC5).
|
|
193
|
+
*/
|
|
194
|
+
export function pickServiceLabel(alert) {
|
|
140
195
|
const labels = (alert?.labels ?? {});
|
|
141
196
|
const pick = labels.service ?? labels.namespace ?? labels.job;
|
|
142
197
|
return typeof pick === "string" && pick ? pick : undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-investigation.js","sourceRoot":"","sources":["../../../src/core/run-investigation.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,6BAA6B;AAE7B;;;;;;;;;;GAUG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"run-investigation.js","sourceRoot":"","sources":["../../../src/core/run-investigation.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CanonicalEvent, InvestigationReport } from "@prismalens/contracts";
|
|
2
|
-
export type SessionStatus = "running" | "done" | "errored";
|
|
2
|
+
export type SessionStatus = "running" | "done" | "errored" | "suppressed";
|
|
3
3
|
export interface SessionRecord {
|
|
4
4
|
runId: string;
|
|
5
5
|
status: SessionStatus;
|
|
@@ -10,6 +10,9 @@ export interface SessionRecord {
|
|
|
10
10
|
repo?: string;
|
|
11
11
|
/** Absolute path to this run's workspace dir. */
|
|
12
12
|
workspacePath: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
/** Which cap suppressed this dispatch (only set when status === "suppressed"). */
|
|
15
|
+
suppressionReason?: string;
|
|
13
16
|
createdAt: string;
|
|
14
17
|
updatedAt: string;
|
|
15
18
|
completedAt?: string;
|
|
@@ -20,6 +23,12 @@ export interface CreateSessionInput {
|
|
|
20
23
|
agent?: string;
|
|
21
24
|
repo?: string;
|
|
22
25
|
}
|
|
26
|
+
export interface GroupRecord {
|
|
27
|
+
groupKey: string;
|
|
28
|
+
formedBy: "window" | "overlay";
|
|
29
|
+
alerts: Record<string, unknown>[];
|
|
30
|
+
lateAlerts: Record<string, unknown>[];
|
|
31
|
+
}
|
|
23
32
|
export interface SessionManager {
|
|
24
33
|
/** Resolved workspace base dir. */
|
|
25
34
|
readonly baseDir: string;
|
|
@@ -36,6 +45,18 @@ export interface SessionManager {
|
|
|
36
45
|
readEvents(runId: string): Promise<CanonicalEvent[]>;
|
|
37
46
|
writeReport(runId: string, report: InvestigationReport): Promise<void>;
|
|
38
47
|
readReport(runId: string): Promise<InvestigationReport | null>;
|
|
48
|
+
writeGroupRecord(runId: string, rec: GroupRecord): Promise<void>;
|
|
49
|
+
appendGroupAlert(runId: string, alert: Record<string, unknown>): Promise<void>;
|
|
50
|
+
/** Persist an over-cap dispatch decision (issue #62): a terminal `suppressed`
|
|
51
|
+
* run row with the cap that tripped, no workspace. Visible in `pl status`. */
|
|
52
|
+
recordSuppressed(input: {
|
|
53
|
+
runId: string;
|
|
54
|
+
reason: string;
|
|
55
|
+
alertname?: string;
|
|
56
|
+
}): Promise<void>;
|
|
57
|
+
/** Release any underlying handle (e.g. the sqlite connection). Optional so
|
|
58
|
+
* pure in-memory/file managers need not implement it. */
|
|
59
|
+
close?(): void;
|
|
39
60
|
}
|
|
40
61
|
export declare function resolveBaseDir(baseDir?: string): string;
|
|
41
62
|
export declare function createSessionManager(baseDir?: string): SessionManager;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/core/session.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/core/session.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACX,cAAc,EACd,mBAAmB,EACnB,MAAM,uBAAuB,CAAC;AAI/B,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,CAAC;AAE1E,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,cAAc;IAC9B,mCAAmC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1D,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAClD,MAAM,CACL,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,GAC5C,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1B,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,aAAa,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IACtE,4DAA4D;IAC5D,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IACrD,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IAC/D,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,gBAAgB,CACf,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB;mFAC+E;IAC/E,gBAAgB,CAAC,KAAK,EAAE;QACvB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB;6DACyD;IACzD,KAAK,CAAC,IAAI,IAAI,CAAC;CACf;AAUD,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAKvD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,cAAc,CAgBrE"}
|
package/dist/src/core/session.js
CHANGED
|
@@ -5,18 +5,14 @@
|
|
|
5
5
|
* retired pl orchestrator's session-manager.
|
|
6
6
|
*
|
|
7
7
|
* Layout under `<workspace.base_dir>` (default ~/.prismalens):
|
|
8
|
-
*
|
|
9
|
-
* runs/<runId>/
|
|
10
|
-
* runs/<runId>/events.jsonl — the canonical event stream (one JSON/line)
|
|
11
|
-
* runs/<runId>/report.json — the synthesized InvestigationReport
|
|
12
|
-
*
|
|
13
|
-
* The index is the source of truth for create/get/update/list; each run dir also
|
|
14
|
-
* mirrors its metadata so a run is self-describing on disk.
|
|
8
|
+
* prismalens.db - sqlite store for all run metadata
|
|
9
|
+
* runs/<runId>/ - per-run workspace dir
|
|
15
10
|
*/
|
|
16
|
-
import { appendFile, mkdir, readFile, rename, writeFile, } from "node:fs/promises";
|
|
17
11
|
import { homedir } from "node:os";
|
|
18
12
|
import { join, resolve } from "node:path";
|
|
19
13
|
import { getAppDataDir } from "@prismalens/config/investigation";
|
|
14
|
+
import { openDatabase } from "./db.js";
|
|
15
|
+
import { SqliteSessionManager } from "./sqlite-session-store.js";
|
|
20
16
|
const RUN_ID_RE = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
21
17
|
function expandHome(p) {
|
|
22
18
|
if (p === "~")
|
|
@@ -32,15 +28,10 @@ export function resolveBaseDir(baseDir) {
|
|
|
32
28
|
return getAppDataDir();
|
|
33
29
|
return resolve(expandHome(baseDir));
|
|
34
30
|
}
|
|
35
|
-
async function writeJsonAtomic(path, data) {
|
|
36
|
-
const tmp = `${path}.tmp`;
|
|
37
|
-
await writeFile(tmp, JSON.stringify(data, null, 2), "utf-8");
|
|
38
|
-
await rename(tmp, path);
|
|
39
|
-
}
|
|
40
31
|
export function createSessionManager(baseDir) {
|
|
41
32
|
const base = resolveBaseDir(baseDir);
|
|
33
|
+
const db = openDatabase(base);
|
|
42
34
|
const runsRoot = join(base, "runs");
|
|
43
|
-
const indexPath = join(base, "sessions.json");
|
|
44
35
|
function assertRunId(runId) {
|
|
45
36
|
if (!RUN_ID_RE.test(runId))
|
|
46
37
|
throw new Error(`Invalid runId: "${runId}"`);
|
|
@@ -49,120 +40,6 @@ export function createSessionManager(baseDir) {
|
|
|
49
40
|
assertRunId(runId);
|
|
50
41
|
return join(runsRoot, runId);
|
|
51
42
|
}
|
|
52
|
-
|
|
53
|
-
return join(workspaceDir(runId), "session.json");
|
|
54
|
-
}
|
|
55
|
-
async function readIndex() {
|
|
56
|
-
try {
|
|
57
|
-
const raw = await readFile(indexPath, "utf-8");
|
|
58
|
-
return JSON.parse(raw);
|
|
59
|
-
}
|
|
60
|
-
catch (err) {
|
|
61
|
-
if (err.code === "ENOENT")
|
|
62
|
-
return {};
|
|
63
|
-
throw err;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
async function upsertIndex(record) {
|
|
67
|
-
const index = await readIndex();
|
|
68
|
-
index[record.runId] = record;
|
|
69
|
-
await mkdir(base, { recursive: true });
|
|
70
|
-
await writeJsonAtomic(indexPath, index);
|
|
71
|
-
}
|
|
72
|
-
const manager = {
|
|
73
|
-
baseDir: base,
|
|
74
|
-
workspaceDir,
|
|
75
|
-
async create(input) {
|
|
76
|
-
assertRunId(input.runId);
|
|
77
|
-
const dir = workspaceDir(input.runId);
|
|
78
|
-
await mkdir(dir, { recursive: true });
|
|
79
|
-
const now = new Date().toISOString();
|
|
80
|
-
const record = {
|
|
81
|
-
runId: input.runId,
|
|
82
|
-
status: "running",
|
|
83
|
-
...(input.alertname !== undefined
|
|
84
|
-
? { alertname: input.alertname }
|
|
85
|
-
: {}),
|
|
86
|
-
...(input.agent !== undefined ? { agent: input.agent } : {}),
|
|
87
|
-
...(input.repo !== undefined ? { repo: input.repo } : {}),
|
|
88
|
-
workspacePath: dir,
|
|
89
|
-
createdAt: now,
|
|
90
|
-
updatedAt: now,
|
|
91
|
-
};
|
|
92
|
-
await writeJsonAtomic(metaPath(input.runId), record);
|
|
93
|
-
await upsertIndex(record);
|
|
94
|
-
return record;
|
|
95
|
-
},
|
|
96
|
-
async get(runId) {
|
|
97
|
-
assertRunId(runId);
|
|
98
|
-
const index = await readIndex();
|
|
99
|
-
const fromIndex = index[runId];
|
|
100
|
-
if (fromIndex)
|
|
101
|
-
return fromIndex;
|
|
102
|
-
try {
|
|
103
|
-
return JSON.parse(await readFile(metaPath(runId), "utf-8"));
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
async update(runId, updates) {
|
|
110
|
-
const existing = await manager.get(runId);
|
|
111
|
-
if (!existing)
|
|
112
|
-
throw new Error(`Session "${runId}" not found`);
|
|
113
|
-
const merged = {
|
|
114
|
-
...existing,
|
|
115
|
-
...updates,
|
|
116
|
-
runId,
|
|
117
|
-
updatedAt: new Date().toISOString(),
|
|
118
|
-
};
|
|
119
|
-
await writeJsonAtomic(metaPath(runId), merged);
|
|
120
|
-
await upsertIndex(merged);
|
|
121
|
-
return merged;
|
|
122
|
-
},
|
|
123
|
-
async list(filter) {
|
|
124
|
-
const index = await readIndex();
|
|
125
|
-
let records = Object.values(index);
|
|
126
|
-
if (filter?.status) {
|
|
127
|
-
const wanted = filter.status;
|
|
128
|
-
records = records.filter((r) => wanted.includes(r.status));
|
|
129
|
-
}
|
|
130
|
-
return records.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
131
|
-
},
|
|
132
|
-
async appendEvent(runId, event) {
|
|
133
|
-
const dir = workspaceDir(runId);
|
|
134
|
-
await mkdir(dir, { recursive: true });
|
|
135
|
-
await appendFile(join(dir, "events.jsonl"), `${JSON.stringify(event)}\n`, "utf-8");
|
|
136
|
-
},
|
|
137
|
-
async readEvents(runId) {
|
|
138
|
-
try {
|
|
139
|
-
const raw = await readFile(join(workspaceDir(runId), "events.jsonl"), "utf-8");
|
|
140
|
-
return raw
|
|
141
|
-
.split("\n")
|
|
142
|
-
.filter((line) => line.trim().length > 0)
|
|
143
|
-
.map((line) => JSON.parse(line));
|
|
144
|
-
}
|
|
145
|
-
catch (err) {
|
|
146
|
-
if (err.code === "ENOENT")
|
|
147
|
-
return [];
|
|
148
|
-
throw err;
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
async writeReport(runId, report) {
|
|
152
|
-
const dir = workspaceDir(runId);
|
|
153
|
-
await mkdir(dir, { recursive: true });
|
|
154
|
-
await writeJsonAtomic(join(dir, "report.json"), report);
|
|
155
|
-
},
|
|
156
|
-
async readReport(runId) {
|
|
157
|
-
try {
|
|
158
|
-
const raw = await readFile(join(workspaceDir(runId), "report.json"), "utf-8");
|
|
159
|
-
return JSON.parse(raw);
|
|
160
|
-
}
|
|
161
|
-
catch {
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
};
|
|
166
|
-
return manager;
|
|
43
|
+
return new SqliteSessionManager(base, db, workspaceDir, assertRunId);
|
|
167
44
|
}
|
|
168
45
|
//# sourceMappingURL=session.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/core/session.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,6BAA6B;AAE7B
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/core/session.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,6BAA6B;AAE7B;;;;;;;GAOG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAKjE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAsEjE,MAAM,SAAS,GAAG,wBAAwB,CAAC;AAE3C,SAAS,UAAU,CAAC,CAAS;IAC5B,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,OAAO,EAAE,CAAC;IAChC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,OAAO,CAAC,CAAC;AACV,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAgB;IAC9C,8EAA8E;IAC9E,wDAAwD;IACxD,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,eAAe;QAAE,OAAO,aAAa,EAAE,CAAC;IACpE,OAAO,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACpD,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAE9B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAEpC,SAAS,WAAW,CAAC,KAAa;QACjC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,GAAG,CAAC,CAAC;IAC1E,CAAC;IAED,SAAS,YAAY,CAAC,KAAa;QAClC,WAAW,CAAC,KAAK,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,IAAI,oBAAoB,CAAC,IAAI,EAAE,EAAE,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ConductedOutcome } from "@prismalens/engine";
|
|
2
|
+
export declare function buildSlackMessage(input: {
|
|
3
|
+
alertname: string;
|
|
4
|
+
runId: string;
|
|
5
|
+
outcome: ConductedOutcome;
|
|
6
|
+
}): string;
|
|
7
|
+
export declare function postToSlack(webhookUrl: string, text: string): Promise<void>;
|
|
8
|
+
export declare function notifyRun(webhookUrl: string, alertname: string, outcome: ConductedOutcome): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=slack-notify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-notify.d.ts","sourceRoot":"","sources":["../../../src/core/slack-notify.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,gBAAgB,CAAC;CAC1B,GAAG,MAAM,CA8BT;AAED,wBAAsB,WAAW,CAChC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,wBAAsB,SAAS,CAC9B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,gBAAgB,GACvB,OAAO,CAAC,IAAI,CAAC,CAoBf"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 Sumit Patel
|
|
3
|
+
export function buildSlackMessage(input) {
|
|
4
|
+
const { alertname, runId, outcome } = input;
|
|
5
|
+
if (outcome.failureKind === "cancelled") {
|
|
6
|
+
return "";
|
|
7
|
+
}
|
|
8
|
+
if (outcome.failureKind === "error") {
|
|
9
|
+
return `${alertname}\nInvestigation errored: ${outcome.error ?? "unknown error"}\nRun ${runId} — pl report ${runId}`;
|
|
10
|
+
}
|
|
11
|
+
if (outcome.failureKind === "no-evidence") {
|
|
12
|
+
return `${alertname}\nInvestigated — insufficient evidence to determine a cause.\nRun ${runId} — pl report ${runId}`;
|
|
13
|
+
}
|
|
14
|
+
const report = outcome.report;
|
|
15
|
+
if (!report) {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
let message = `${alertname}\n${report.summary}\nRoot cause: ${report.rootCause ?? "not determined"}`;
|
|
19
|
+
if (report.hypotheses.length > 0) {
|
|
20
|
+
const topHypothesis = report.hypotheses[0];
|
|
21
|
+
message += `\nTop hypothesis: ${topHypothesis.statement} (${topHypothesis.evidence.length} evidence)`;
|
|
22
|
+
}
|
|
23
|
+
message += `\nRun ${runId} — pl report ${runId}`;
|
|
24
|
+
return message;
|
|
25
|
+
}
|
|
26
|
+
export async function postToSlack(webhookUrl, text) {
|
|
27
|
+
const res = await fetch(webhookUrl, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: { "Content-Type": "application/json" },
|
|
30
|
+
body: JSON.stringify({ text }),
|
|
31
|
+
signal: AbortSignal.timeout(5000),
|
|
32
|
+
});
|
|
33
|
+
if (!res.ok) {
|
|
34
|
+
throw new Error(`Slack webhook returned ${res.status}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export async function notifyRun(webhookUrl, alertname, outcome) {
|
|
38
|
+
try {
|
|
39
|
+
const text = buildSlackMessage({
|
|
40
|
+
alertname,
|
|
41
|
+
runId: outcome.runId,
|
|
42
|
+
outcome,
|
|
43
|
+
});
|
|
44
|
+
if (!text) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
await postToSlack(webhookUrl, text);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.error(JSON.stringify({
|
|
51
|
+
event: "slack_delivery_failed",
|
|
52
|
+
runId: outcome.runId,
|
|
53
|
+
error: err instanceof Error ? err.message : String(err),
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=slack-notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-notify.js","sourceRoot":"","sources":["../../../src/core/slack-notify.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,6BAA6B;AAI7B,MAAM,UAAU,iBAAiB,CAAC,KAIjC;IACA,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAE5C,IAAI,OAAO,CAAC,WAAW,KAAK,WAAW,EAAE,CAAC;QACzC,OAAO,EAAE,CAAC;IACX,CAAC;IAED,IAAI,OAAO,CAAC,WAAW,KAAK,OAAO,EAAE,CAAC;QACrC,OAAO,GAAG,SAAS,4BAA4B,OAAO,CAAC,KAAK,IAAI,eAAe,SAAS,KAAK,gBAAgB,KAAK,EAAE,CAAC;IACtH,CAAC;IAED,IAAI,OAAO,CAAC,WAAW,KAAK,aAAa,EAAE,CAAC;QAC3C,OAAO,GAAG,SAAS,qEAAqE,KAAK,gBAAgB,KAAK,EAAE,CAAC;IACtH,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,OAAO,EAAE,CAAC;IACX,CAAC;IAED,IAAI,OAAO,GAAG,GAAG,SAAS,KAAK,MAAM,CAAC,OAAO,iBAAiB,MAAM,CAAC,SAAS,IAAI,gBAAgB,EAAE,CAAC;IAErG,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC3C,OAAO,IAAI,qBAAqB,aAAa,CAAC,SAAS,KAAK,aAAa,CAAC,QAAQ,CAAC,MAAM,YAAY,CAAC;IACvG,CAAC;IAED,OAAO,IAAI,SAAS,KAAK,gBAAgB,KAAK,EAAE,CAAC;IAEjD,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAChC,UAAkB,EAClB,IAAY;IAEZ,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,UAAU,EAAE;QACnC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC;QAC9B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC;KACjC,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACzD,CAAC;AACF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC9B,UAAkB,EAClB,SAAiB,EACjB,OAAyB;IAEzB,IAAI,CAAC;QACJ,MAAM,IAAI,GAAG,iBAAiB,CAAC;YAC9B,SAAS;YACT,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,OAAO;SACP,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,OAAO;QACR,CAAC;QACD,MAAM,WAAW,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CACZ,IAAI,CAAC,SAAS,CAAC;YACd,KAAK,EAAE,uBAAuB;YAC9B,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACvD,CAAC,CACF,CAAC;IACH,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import type { CanonicalEvent, InvestigationReport } from "@prismalens/contracts";
|
|
3
|
+
import type { CreateSessionInput, GroupRecord, SessionManager, SessionRecord, SessionStatus } from "./session.js";
|
|
4
|
+
export declare class SqliteSessionManager implements SessionManager {
|
|
5
|
+
readonly baseDir: string;
|
|
6
|
+
private db;
|
|
7
|
+
readonly workspaceDir: (runId: string) => string;
|
|
8
|
+
private assertRunId;
|
|
9
|
+
constructor(baseDir: string, db: DatabaseSync, workspaceDir: (runId: string) => string, assertRunId: (runId: string) => void);
|
|
10
|
+
create(input: CreateSessionInput): Promise<SessionRecord>;
|
|
11
|
+
get(runId: string): Promise<SessionRecord | null>;
|
|
12
|
+
update(runId: string, updates: Partial<Omit<SessionRecord, "runId">>): Promise<SessionRecord>;
|
|
13
|
+
list(filter?: {
|
|
14
|
+
status?: SessionStatus[];
|
|
15
|
+
}): Promise<SessionRecord[]>;
|
|
16
|
+
appendEvent(runId: string, event: CanonicalEvent): Promise<void>;
|
|
17
|
+
readEvents(runId: string): Promise<CanonicalEvent[]>;
|
|
18
|
+
writeReport(runId: string, report: InvestigationReport): Promise<void>;
|
|
19
|
+
readReport(runId: string): Promise<InvestigationReport | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Persist a group's record (ADR-0012 grouping): upsert the `groups` row with
|
|
22
|
+
* the key + how it formed (taken from the record, never hardcoded), then
|
|
23
|
+
* replace its `group_alerts` with the formative alerts (late=0) followed by
|
|
24
|
+
* any already-attached late alerts (late=1), each in order. The group row may
|
|
25
|
+
* already exist (a plain `create()` seeds one) — we keep its created_at.
|
|
26
|
+
*/
|
|
27
|
+
writeGroupRecord(runId: string, rec: GroupRecord): Promise<void>;
|
|
28
|
+
/** Attach a late alert to an existing group, appended in arrival order. */
|
|
29
|
+
appendGroupAlert(runId: string, alert: Record<string, unknown>): Promise<void>;
|
|
30
|
+
recordSuppressed(input: {
|
|
31
|
+
runId: string;
|
|
32
|
+
reason: string;
|
|
33
|
+
alertname?: string;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
/** Release the underlying db handle. Callers that own a transient manager
|
|
36
|
+
* (e.g. one per `pl listen` run) must close it or leak a connection. */
|
|
37
|
+
close(): void;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=sqlite-session-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-session-store.d.ts","sourceRoot":"","sources":["../../../src/core/sqlite-session-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EACX,cAAc,EACd,mBAAmB,EACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EACX,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,aAAa,EACb,aAAa,EACb,MAAM,cAAc,CAAC;AA8CtB,qBAAa,oBAAqB,YAAW,cAAc;aAEzC,OAAO,EAAE,MAAM;IAC/B,OAAO,CAAC,EAAE;aACM,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM;IACvD,OAAO,CAAC,WAAW;gBAHH,OAAO,EAAE,MAAM,EACvB,EAAE,EAAE,YAAY,EACR,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAC/C,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;IAGvC,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC;IAuCzD,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAOjD,MAAM,CACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,GAC5C,OAAO,CAAC,aAAa,CAAC;IA2DnB,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,aAAa,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAiBrE,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhE,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAQpD,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAStE,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAapE;;;;;;OAMG;IACG,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BtE,2EAA2E;IACrE,gBAAgB,CACrB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC;IAmBV,gBAAgB,CAAC,KAAK,EAAE;QAC7B,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCjB;4EACwE;IACxE,KAAK,IAAI,IAAI;CAGb"}
|