prismalens 0.0.2 → 0.1.1
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 +227 -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 +298 -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 +105 -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 +12 -0
- package/dist/src/core/run-investigation.d.ts.map +1 -1
- package/dist/src/core/run-investigation.js +86 -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
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 Sumit Patel
|
|
3
|
+
/**
|
|
4
|
+
* `pl listen` (issue #58, Phase 1 R1) — the walking skeleton of unattended
|
|
5
|
+
* investigation: a token-authed local HTTP receiver for Alertmanager webhooks.
|
|
6
|
+
* Each firing alert runs the SAME seam chain as `investigate`/`serve`
|
|
7
|
+
* (loadConfig → resolveRunSandbox → resolveInvestigation → conductRun with the
|
|
8
|
+
* file session store), resolved PER PAYLOAD — config edits apply to the next
|
|
9
|
+
* webhook without a restart. Alert grouping via a debounce window is implemented;
|
|
10
|
+
* global caps (#62) and Slack notification are still future slices.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
13
|
+
import { resolve } from "node:path";
|
|
14
|
+
import { conductRun as engineConductRun } from "@prismalens/engine";
|
|
15
|
+
import { defineCommand } from "citty";
|
|
16
|
+
import { consola } from "consola";
|
|
17
|
+
import { loadConfig } from "../config/loader.js";
|
|
18
|
+
import { createCapsGate, } from "../core/caps.js";
|
|
19
|
+
import { resolveRepoSlug } from "../core/detect-repo.js";
|
|
20
|
+
import { createFileSessionStore } from "../core/file-session-store.js";
|
|
21
|
+
import { isSynthConfigured, pickServiceLabel, resolveInvestigation, } from "../core/run-investigation.js";
|
|
22
|
+
import { createSessionManager } from "../core/session.js";
|
|
23
|
+
import { startListenServer, WEBHOOK_PATH, } from "../http/server.js";
|
|
24
|
+
import { assertKnownFlags } from "./flags.js";
|
|
25
|
+
import { createGroupingLayer } from "./grouping.js";
|
|
26
|
+
import { liveTimelineEntry, resolveRunSandbox as realResolveRunSandbox, } from "./investigate.js";
|
|
27
|
+
/**
|
|
28
|
+
* The two external boundaries of the chain, injectable for tests: `conductRun`
|
|
29
|
+
* spawns a harness, `resolveRunSandbox` probes/arms a sandbox. Everything else
|
|
30
|
+
* (config loader, resolver, file store) runs real.
|
|
31
|
+
*/
|
|
32
|
+
import { notifyRun } from "../core/slack-notify.js";
|
|
33
|
+
/**
|
|
34
|
+
* Build the per-alert investigation executor the intake server drives — the
|
|
35
|
+
* `handleInvestigate` chain (jsonrpc/server.ts) minus the RPC framing. Rejects
|
|
36
|
+
* on a failed run (the server logs it and keeps serving); the sandbox is
|
|
37
|
+
* CALLER-OWNED and destroyed in `finally` (ADR-0020), matching investigate/serve.
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* Per-payload repo/cwd resolution (issue #58 AC5): the alert's service label
|
|
41
|
+
* (`service`/`namespace`/`job`) → `services[name].repo` → a `repos` entry's
|
|
42
|
+
* `local_path`, else the value itself when it is an existing local checkout.
|
|
43
|
+
* No label / no catalog match / no checkout ⇒ the listen cwd, exactly like a
|
|
44
|
+
* manual `pl investigate` run from that directory (single-player default).
|
|
45
|
+
*/
|
|
46
|
+
export function resolveRepoPath(alert, config) {
|
|
47
|
+
const name = pickServiceLabel(alert);
|
|
48
|
+
if (!name) {
|
|
49
|
+
throw new Error(`Listen dispatch refused: alert is missing a service label (cannot resolve an investigation workspace).`);
|
|
50
|
+
}
|
|
51
|
+
const repoRef = config.services[name]?.repo;
|
|
52
|
+
if (!repoRef) {
|
|
53
|
+
throw new Error(`Listen dispatch refused: service "${name}" has no mapped repo in config (cannot resolve an investigation workspace).`);
|
|
54
|
+
}
|
|
55
|
+
const localPath = config.repos[repoRef]?.local_path;
|
|
56
|
+
if (localPath)
|
|
57
|
+
return resolve(localPath);
|
|
58
|
+
if (existsSync(repoRef))
|
|
59
|
+
return resolve(repoRef);
|
|
60
|
+
throw new Error(`Listen dispatch refused: repo "${repoRef}" (mapped from service "${name}") does not exist locally (cannot resolve an investigation workspace).`);
|
|
61
|
+
}
|
|
62
|
+
export function createInvestigationRunner(options, deps = {}) {
|
|
63
|
+
const conductRun = deps.conductRun ?? engineConductRun;
|
|
64
|
+
const resolveRunSandbox = deps.resolveRunSandbox ?? realResolveRunSandbox;
|
|
65
|
+
const notify = deps.notify ?? notifyRun;
|
|
66
|
+
const cwd = options.cwd ? resolve(options.cwd) : process.cwd();
|
|
67
|
+
// One gate for the server's lifetime; state (active count + hourly window)
|
|
68
|
+
// persists across payloads even though config is re-resolved per payload.
|
|
69
|
+
const caps = deps.capsGate ?? createCapsGate();
|
|
70
|
+
return async (runId, alerts) => {
|
|
71
|
+
// Per payload, not once at startup: config (and thus repo/workspace/harness)
|
|
72
|
+
// is re-resolved for every alert, exactly like a fresh `pl investigate`.
|
|
73
|
+
const config = await loadConfig({
|
|
74
|
+
cwd,
|
|
75
|
+
...(options.configPath ? { configPath: options.configPath } : {}),
|
|
76
|
+
});
|
|
77
|
+
const alertname = (alerts[0]?.labels?.alertname ?? undefined);
|
|
78
|
+
// AC5: the checkout under investigation follows the ALERT (service label →
|
|
79
|
+
// catalog), not the directory the server happened to start in.
|
|
80
|
+
// MUST be resolved before consuming caps so unmapped alerts do not exhaust budget.
|
|
81
|
+
let repoPath;
|
|
82
|
+
try {
|
|
83
|
+
repoPath = resolveRepoPath(alerts[0], config);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
87
|
+
console.error(JSON.stringify({
|
|
88
|
+
event: "dispatch_refused",
|
|
89
|
+
runId,
|
|
90
|
+
reason,
|
|
91
|
+
...(alertname ? { alertname } : {}),
|
|
92
|
+
}));
|
|
93
|
+
options.log(`Refused run ${runId} (${reason})`);
|
|
94
|
+
const refusedSessions = createSessionManager(config.workspace.base_dir);
|
|
95
|
+
try {
|
|
96
|
+
await refusedSessions.recordSuppressed({
|
|
97
|
+
runId,
|
|
98
|
+
reason,
|
|
99
|
+
...(alertname ? { alertname } : {}),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
refusedSessions.close?.();
|
|
104
|
+
}
|
|
105
|
+
return; // refused, never consume caps
|
|
106
|
+
}
|
|
107
|
+
const dispatchCaps = {
|
|
108
|
+
maxConcurrent: config.listen.caps.max_concurrent,
|
|
109
|
+
maxPerHour: config.listen.caps.max_per_hour,
|
|
110
|
+
};
|
|
111
|
+
const decision = caps.tryDispatch(dispatchCaps, Date.now());
|
|
112
|
+
if (!decision.allow) {
|
|
113
|
+
// Structured suppression log — match the repo convention
|
|
114
|
+
// (slack-notify.ts: console.error(JSON.stringify({ event, ... }))).
|
|
115
|
+
console.error(JSON.stringify({
|
|
116
|
+
event: "dispatch_suppressed",
|
|
117
|
+
runId,
|
|
118
|
+
reason: decision.reason,
|
|
119
|
+
...(alertname ? { alertname } : {}),
|
|
120
|
+
maxConcurrent: dispatchCaps.maxConcurrent,
|
|
121
|
+
maxPerHour: dispatchCaps.maxPerHour,
|
|
122
|
+
}));
|
|
123
|
+
options.log(`Suppressed run ${runId} (${decision.reason} cap)`);
|
|
124
|
+
const suppressedSessions = createSessionManager(config.workspace.base_dir);
|
|
125
|
+
try {
|
|
126
|
+
await suppressedSessions.recordSuppressed({
|
|
127
|
+
runId,
|
|
128
|
+
reason: decision.reason,
|
|
129
|
+
...(alertname ? { alertname } : {}),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
suppressedSessions.close?.();
|
|
134
|
+
}
|
|
135
|
+
return; // never arm a sandbox / call conductRun for a suppressed run
|
|
136
|
+
}
|
|
137
|
+
const harnessName = config.agent.default;
|
|
138
|
+
const { selection } = await resolveRunSandbox(harnessName, config.agent.sandbox, config);
|
|
139
|
+
let sessions;
|
|
140
|
+
try {
|
|
141
|
+
const resolved = resolveInvestigation({
|
|
142
|
+
alerts,
|
|
143
|
+
repo: repoPath,
|
|
144
|
+
...(selection
|
|
145
|
+
? {
|
|
146
|
+
sandbox: selection.sandbox,
|
|
147
|
+
requestedSandbox: selection.requested,
|
|
148
|
+
}
|
|
149
|
+
: {}),
|
|
150
|
+
...(config.listen.caps.max_turns !== undefined
|
|
151
|
+
? { maxTurns: config.listen.caps.max_turns }
|
|
152
|
+
: {}),
|
|
153
|
+
isolateSettings: true,
|
|
154
|
+
}, config);
|
|
155
|
+
const { context, harness, synth, fidelity, maxBranches } = resolved;
|
|
156
|
+
const primaryAlert = context.alerts[0];
|
|
157
|
+
const repoSlug = await resolveRepoSlug(config.repo, repoPath);
|
|
158
|
+
sessions = createSessionManager(config.workspace.base_dir);
|
|
159
|
+
const fileSession = createFileSessionStore(sessions, {
|
|
160
|
+
runId,
|
|
161
|
+
alertname: primaryAlert.alertname,
|
|
162
|
+
agent: resolved.harnessName,
|
|
163
|
+
...(repoSlug ? { repo: repoSlug } : {}),
|
|
164
|
+
});
|
|
165
|
+
options.log(`Investigating "${primaryAlert.alertname}" with ${resolved.harnessName} in ${repoPath} (run ${runId})`);
|
|
166
|
+
const outcome = await conductRun({
|
|
167
|
+
context,
|
|
168
|
+
harness,
|
|
169
|
+
synth,
|
|
170
|
+
fidelity,
|
|
171
|
+
runId,
|
|
172
|
+
...(maxBranches !== undefined ? { maxBranches } : {}),
|
|
173
|
+
}, {
|
|
174
|
+
sink: (event) => {
|
|
175
|
+
const entry = liveTimelineEntry(event);
|
|
176
|
+
if (entry)
|
|
177
|
+
options.log(entry);
|
|
178
|
+
},
|
|
179
|
+
store: fileSession.store,
|
|
180
|
+
});
|
|
181
|
+
if (config.listen.slack_webhook_url) {
|
|
182
|
+
try {
|
|
183
|
+
await notify(config.listen.slack_webhook_url, primaryAlert.alertname, outcome);
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
// Fallback for an injected notify that violates the contract.
|
|
187
|
+
// The real notifyRun never rejects.
|
|
188
|
+
options.log(`Slack notification failed with error: ${err}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (!outcome.report) {
|
|
192
|
+
throw new Error(outcome.error ?? "investigation produced no evidence");
|
|
193
|
+
}
|
|
194
|
+
options.log(`Report saved for run ${runId}`);
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
caps.release();
|
|
198
|
+
// Close this run's sqlite connection — the runner opens one PER alert,
|
|
199
|
+
// so leaving it open would leak a handle on every webhook.
|
|
200
|
+
sessions?.close?.();
|
|
201
|
+
// The listen path owns the boundary's lifecycle (ADR-0020) — torn down
|
|
202
|
+
// whichever way the run exited (report, no-evidence, error, or throw).
|
|
203
|
+
if (selection)
|
|
204
|
+
await selection.sandbox.destroy();
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* The command body behind `pl listen`, separated from citty so it's testable:
|
|
210
|
+
* load config, demand a token, start the intake server wired to the real (or
|
|
211
|
+
* injected) investigation chain. Returns the running server (the caller owns
|
|
212
|
+
* `close()`; the CLI just lets it hold the event loop open).
|
|
213
|
+
*/
|
|
214
|
+
export async function startListenFromConfig(options, deps = {}) {
|
|
215
|
+
const cwd = options.cwd ? resolve(options.cwd) : process.cwd();
|
|
216
|
+
const config = await loadConfig({
|
|
217
|
+
cwd,
|
|
218
|
+
...(options.configPath ? { configPath: options.configPath } : {}),
|
|
219
|
+
});
|
|
220
|
+
if (!isSynthConfigured(config)) {
|
|
221
|
+
options.log("No Tier-1 provider configured (checked env + _FILE for: ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY, GROQ_API_KEY, OLLAMA_API_KEY) — reports will be RAW harness pass-through (un-synthesized). This is supported.");
|
|
222
|
+
}
|
|
223
|
+
if (!config.listen.token) {
|
|
224
|
+
// An open intake is not a mode — refuse to start rather than serve unauthenticated.
|
|
225
|
+
throw new Error("config is missing listen.token (required — intake auth). Add listen.token to prismalens.config.yaml.");
|
|
226
|
+
}
|
|
227
|
+
const sessions = createSessionManager(config.workspace.base_dir);
|
|
228
|
+
// Startup reaper (#136): mark any orphaned "running" runs from a previous
|
|
229
|
+
// dead listener as errored, so pl status reflects reality.
|
|
230
|
+
const staleRuns = await sessions.list({ status: ["running"] });
|
|
231
|
+
// Liveness guard: only reap runs whose updatedAt is older than the max possible
|
|
232
|
+
// run duration (wall-clock cap if configured, else 1 hour) plus 5 minutes slack.
|
|
233
|
+
const runWallClockMs = config.agent.limits.wall_clock_ms || 60 * 60 * 1000;
|
|
234
|
+
const staleThresholdMs = runWallClockMs + 5 * 60 * 1000;
|
|
235
|
+
const now = Date.now();
|
|
236
|
+
for (const run of staleRuns) {
|
|
237
|
+
const updatedTime = new Date(run.updatedAt).getTime();
|
|
238
|
+
if (now - updatedTime > staleThresholdMs) {
|
|
239
|
+
await sessions.update(run.runId, {
|
|
240
|
+
status: "errored",
|
|
241
|
+
error: "listener process died mid-investigation (interrupted)",
|
|
242
|
+
});
|
|
243
|
+
options.log(`Reaped stale run ${run.runId} from a previous listener`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const grouping = createGroupingLayer({
|
|
247
|
+
windowMs: config.listen.grouping_window_ms,
|
|
248
|
+
sessions,
|
|
249
|
+
runInvestigation: createInvestigationRunner(options, deps),
|
|
250
|
+
log: options.log,
|
|
251
|
+
});
|
|
252
|
+
const server = await startListenServer({
|
|
253
|
+
port: config.listen.port,
|
|
254
|
+
token: config.listen.token,
|
|
255
|
+
maxPending: config.listen.max_pending,
|
|
256
|
+
grouping,
|
|
257
|
+
});
|
|
258
|
+
// grouping holds a connection for the server's lifetime; release it on close
|
|
259
|
+
// (server.close already drains grouping via shutdown()).
|
|
260
|
+
return {
|
|
261
|
+
...server,
|
|
262
|
+
async close() {
|
|
263
|
+
await server.close();
|
|
264
|
+
sessions.close?.();
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
export default defineCommand({
|
|
269
|
+
meta: {
|
|
270
|
+
name: "listen",
|
|
271
|
+
description: "Start a token-authed local HTTP listener for Alertmanager webhooks; each firing alert triggers an investigation (Phase 1 R1).\n\nExamples:\n $ PRISMALENS_LISTEN_TOKEN=xyz pl listen --config my-stack.yaml",
|
|
272
|
+
},
|
|
273
|
+
args: {
|
|
274
|
+
config: {
|
|
275
|
+
type: "string",
|
|
276
|
+
description: "Path to prismalens.config.yaml (else global→project hierarchy).",
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
/* v8 ignore start — process-global glue (cwd + consola binding); the body it
|
|
280
|
+
delegates to is fully covered via startListenFromConfig tests */
|
|
281
|
+
async run({ args, cmd }) {
|
|
282
|
+
try {
|
|
283
|
+
assertKnownFlags(args, cmd);
|
|
284
|
+
const server = await startListenFromConfig({
|
|
285
|
+
cwd: process.cwd(),
|
|
286
|
+
...(args.config ? { configPath: args.config } : {}),
|
|
287
|
+
log: (line) => consola.log(line),
|
|
288
|
+
});
|
|
289
|
+
consola.success(`Listening for Alertmanager webhooks on http://127.0.0.1:${server.port}${WEBHOOK_PATH}`);
|
|
290
|
+
}
|
|
291
|
+
catch (err) {
|
|
292
|
+
consola.error(err instanceof Error ? err.message : String(err));
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
/* v8 ignore stop */
|
|
297
|
+
});
|
|
298
|
+
//# sourceMappingURL=listen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listen.js","sourceRoot":"","sources":["../../../src/cli/listen.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,6BAA6B;AAE7B;;;;;;;;GAQG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,UAAU,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EAEN,cAAc,GAEd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EACN,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,GACpB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,oBAAoB,EAAuB,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAEN,iBAAiB,EACjB,YAAY,GACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EACN,iBAAiB,EACjB,iBAAiB,IAAI,qBAAqB,GAC1C,MAAM,kBAAkB,CAAC;AAY1B;;;;GAIG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAapD;;;;;GAKG;AACH;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC9B,KAA8B,EAC9B,MAAgB;IAEhB,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACd,wGAAwG,CACxG,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;IAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACd,qCAAqC,IAAI,6EAA6E,CACtH,CAAC;IACH,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC;IACpD,IAAI,SAAS;QAAE,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC;IACzC,IAAI,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,IAAI,KAAK,CACd,kCAAkC,OAAO,2BAA2B,IAAI,wEAAwE,CAChJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,yBAAyB,CACxC,OAAmC,EACnC,OAAgC,EAAE;IAElC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,gBAAgB,CAAC;IACvD,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,IAAI,qBAAqB,CAAC;IAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,SAAS,CAAC;IACxC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IAE/D,2EAA2E;IAC3E,0EAA0E;IAC1E,MAAM,IAAI,GAAa,IAAI,CAAC,QAAQ,IAAI,cAAc,EAAE,CAAC;IAEzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAC9B,6EAA6E;QAC7E,yEAAyE;QACzE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC;YAC/B,GAAG;YACH,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACjE,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,CACjB,MAAM,CAAC,CAAC,CAAC,EAAE,MACX,EAAE,SAAS,IAAI,SAAS,CAAuB,CAAC;QAEjD,2EAA2E;QAC3E,+DAA+D;QAC/D,mFAAmF;QACnF,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACJ,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAChE,OAAO,CAAC,KAAK,CACZ,IAAI,CAAC,SAAS,CAAC;gBACd,KAAK,EAAE,kBAAkB;gBACzB,KAAK;gBACL,MAAM;gBACN,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACnC,CAAC,CACF,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,KAAK,MAAM,GAAG,CAAC,CAAC;YAChD,MAAM,eAAe,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YACxE,IAAI,CAAC;gBACJ,MAAM,eAAe,CAAC,gBAAgB,CAAC;oBACtC,KAAK;oBACL,MAAM;oBACN,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACnC,CAAC,CAAC;YACJ,CAAC;oBAAS,CAAC;gBACV,eAAe,CAAC,KAAK,EAAE,EAAE,CAAC;YAC3B,CAAC;YACD,OAAO,CAAC,8BAA8B;QACvC,CAAC;QAED,MAAM,YAAY,GAAiB;YAClC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc;YAChD,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY;SAC3C,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YACrB,yDAAyD;YACzD,oEAAoE;YACpE,OAAO,CAAC,KAAK,CACZ,IAAI,CAAC,SAAS,CAAC;gBACd,KAAK,EAAE,qBAAqB;gBAC5B,KAAK;gBACL,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnC,aAAa,EAAE,YAAY,CAAC,aAAa;gBACzC,UAAU,EAAE,YAAY,CAAC,UAAU;aACnC,CAAC,CACF,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,KAAK,QAAQ,CAAC,MAAM,OAAO,CAAC,CAAC;YAChE,MAAM,kBAAkB,GAAG,oBAAoB,CAC9C,MAAM,CAAC,SAAS,CAAC,QAAQ,CACzB,CAAC;YACF,IAAI,CAAC;gBACJ,MAAM,kBAAkB,CAAC,gBAAgB,CAAC;oBACzC,KAAK;oBACL,MAAM,EAAE,QAAQ,CAAC,MAAM;oBACvB,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACnC,CAAC,CAAC;YACJ,CAAC;oBAAS,CAAC;gBACV,kBAAkB,CAAC,KAAK,EAAE,EAAE,CAAC;YAC9B,CAAC;YACD,OAAO,CAAC,6DAA6D;QACtE,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;QACzC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,iBAAiB,CAC5C,WAAW,EACX,MAAM,CAAC,KAAK,CAAC,OAAO,EACpB,MAAM,CACN,CAAC;QACF,IAAI,QAAoC,CAAC;QACzC,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,oBAAoB,CACpC;gBACC,MAAM;gBACN,IAAI,EAAE,QAAQ;gBACd,GAAG,CAAC,SAAS;oBACZ,CAAC,CAAC;wBACA,OAAO,EAAE,SAAS,CAAC,OAAO;wBAC1B,gBAAgB,EAAE,SAAS,CAAC,SAAS;qBACrC;oBACF,CAAC,CAAC,EAAE,CAAC;gBACN,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS;oBAC7C,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE;oBAC5C,CAAC,CAAC,EAAE,CAAC;gBACN,eAAe,EAAE,IAAI;aACrB,EACD,MAAM,CACN,CAAC;YACF,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,QAAQ,CAAC;YACpE,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAEvC,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAC9D,QAAQ,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAC3D,MAAM,WAAW,GAAG,sBAAsB,CAAC,QAAQ,EAAE;gBACpD,KAAK;gBACL,SAAS,EAAE,YAAY,CAAC,SAAS;gBACjC,KAAK,EAAE,QAAQ,CAAC,WAAW;gBAC3B,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACvC,CAAC,CAAC;YAEH,OAAO,CAAC,GAAG,CACV,kBAAkB,YAAY,CAAC,SAAS,UAAU,QAAQ,CAAC,WAAW,OAAO,QAAQ,SAAS,KAAK,GAAG,CACtG,CAAC;YACF,MAAM,OAAO,GAAG,MAAM,UAAU,CAC/B;gBACC,OAAO;gBACP,OAAO;gBACP,KAAK;gBACL,QAAQ;gBACR,KAAK;gBACL,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACrD,EACD;gBACC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;oBACf,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;oBACvC,IAAI,KAAK;wBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBAC/B,CAAC;gBACD,KAAK,EAAE,WAAW,CAAC,KAAK;aACxB,CACD,CAAC;YAEF,IAAI,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;gBACrC,IAAI,CAAC;oBACJ,MAAM,MAAM,CACX,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAC/B,YAAY,CAAC,SAAmB,EAChC,OAAO,CACP,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACd,8DAA8D;oBAC9D,oCAAoC;oBACpC,OAAO,CAAC,GAAG,CAAC,yCAAyC,GAAG,EAAE,CAAC,CAAC;gBAC7D,CAAC;YACF,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,oCAAoC,CAAC,CAAC;YACxE,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,wBAAwB,KAAK,EAAE,CAAC,CAAC;QAC9C,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,uEAAuE;YACvE,2DAA2D;YAC3D,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC;YACpB,uEAAuE;YACvE,uEAAuE;YACvE,IAAI,SAAS;gBAAE,MAAM,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAClD,CAAC;IACF,CAAC,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAC1C,OAAmC,EACnC,OAAgC,EAAE;IAElC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IAC/D,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC;QAC/B,GAAG;QACH,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACjE,CAAC,CAAC;IAEH,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;QAChC,OAAO,CAAC,GAAG,CACV,0NAA0N,CAC1N,CAAC;IACH,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAC1B,oFAAoF;QACpF,MAAM,IAAI,KAAK,CACd,sGAAsG,CACtG,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAEjE,0EAA0E;IAC1E,2DAA2D;IAC3D,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAC/D,gFAAgF;IAChF,iFAAiF;IACjF,MAAM,cAAc,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC3E,MAAM,gBAAgB,GAAG,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IACxD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEvB,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;QACtD,IAAI,GAAG,GAAG,WAAW,GAAG,gBAAgB,EAAE,CAAC;YAC1C,MAAM,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE;gBAChC,MAAM,EAAE,SAAS;gBACjB,KAAK,EAAE,uDAAuD;aAC9D,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,oBAAoB,GAAG,CAAC,KAAK,2BAA2B,CAAC,CAAC;QACvE,CAAC;IACF,CAAC;IAED,MAAM,QAAQ,GAAG,mBAAmB,CAAC;QACpC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,kBAAkB;QAC1C,QAAQ;QACR,gBAAgB,EAAE,yBAAyB,CAAC,OAAO,EAAE,IAAI,CAAC;QAC1D,GAAG,EAAE,OAAO,CAAC,GAAG;KAChB,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;QACtC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI;QACxB,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK;QAC1B,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,WAAW;QACrC,QAAQ;KACR,CAAC,CAAC;IAEH,6EAA6E;IAC7E,yDAAyD;IACzD,OAAO;QACN,GAAG,MAAM;QACT,KAAK,CAAC,KAAK;YACV,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACrB,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC;QACpB,CAAC;KACD,CAAC;AACH,CAAC;AAED,eAAe,aAAa,CAAC;IAC5B,IAAI,EAAE;QACL,IAAI,EAAE,QAAQ;QACd,WAAW,EACV,8MAA8M;KAC/M;IACD,IAAI,EAAE;QACL,MAAM,EAAE;YACP,IAAI,EAAE,QAAQ;YACd,WAAW,EACV,iEAAiE;SAClE;KACD;IACD;uEACmE;IACnE,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE;QACtB,IAAI,CAAC;YACJ,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAE5B,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC;gBAC1C,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;gBAClB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnD,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;aAChC,CAAC,CAAC;YACH,OAAO,CAAC,OAAO,CACd,2DAA2D,MAAM,CAAC,IAAI,GAAG,YAAY,EAAE,CACvF,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACF,CAAC;IACD,oBAAoB;CACpB,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare const _default: import("citty").CommandDef<{
|
|
2
|
+
readonly id: {
|
|
3
|
+
readonly type: "positional";
|
|
4
|
+
readonly description: "The run ID";
|
|
5
|
+
readonly required: true;
|
|
6
|
+
};
|
|
7
|
+
readonly events: {
|
|
8
|
+
readonly type: "boolean";
|
|
9
|
+
readonly description: "Include timeline events";
|
|
10
|
+
readonly required: false;
|
|
11
|
+
};
|
|
12
|
+
readonly "base-dir": {
|
|
13
|
+
readonly type: "string";
|
|
14
|
+
readonly description: "Workspace base directory (default: ~/.prismalens)";
|
|
15
|
+
readonly required: false;
|
|
16
|
+
};
|
|
17
|
+
}>;
|
|
18
|
+
export default _default;
|
|
19
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/cli/report.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAQA,wBAqDG"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 Sumit Patel
|
|
3
|
+
import { defineCommand } from "citty";
|
|
4
|
+
import consola from "consola";
|
|
5
|
+
import { createSessionManager } from "../core/session.js";
|
|
6
|
+
import { assertKnownFlags } from "./flags.js";
|
|
7
|
+
export default defineCommand({
|
|
8
|
+
meta: {
|
|
9
|
+
name: "report",
|
|
10
|
+
description: "Print a stored report for a given run ID",
|
|
11
|
+
},
|
|
12
|
+
args: {
|
|
13
|
+
id: {
|
|
14
|
+
type: "positional",
|
|
15
|
+
description: "The run ID",
|
|
16
|
+
required: true,
|
|
17
|
+
},
|
|
18
|
+
events: {
|
|
19
|
+
type: "boolean",
|
|
20
|
+
description: "Include timeline events",
|
|
21
|
+
required: false,
|
|
22
|
+
},
|
|
23
|
+
"base-dir": {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "Workspace base directory (default: ~/.prismalens)",
|
|
26
|
+
required: false,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
async run({ args, cmd }) {
|
|
30
|
+
let sessions;
|
|
31
|
+
try {
|
|
32
|
+
assertKnownFlags(args, cmd);
|
|
33
|
+
sessions = createSessionManager(args["base-dir"]);
|
|
34
|
+
const report = await sessions.readReport(args.id);
|
|
35
|
+
if (!report) {
|
|
36
|
+
consola.error(`No report for run ${args.id}`);
|
|
37
|
+
// exitCode (not process.exit) so the finally block still closes the
|
|
38
|
+
// session manager before the process tears down.
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
consola.log(JSON.stringify(report, null, 2));
|
|
43
|
+
if (args.events) {
|
|
44
|
+
const events = await sessions.readEvents(args.id);
|
|
45
|
+
consola.log("\n--- Timeline Events ---");
|
|
46
|
+
for (const ev of events) {
|
|
47
|
+
consola.log(JSON.stringify(ev));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
consola.error("Failed to read report:", err);
|
|
53
|
+
process.exitCode = 1;
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
sessions?.close?.();
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/cli/report.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,6BAA6B;AAE7B,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,eAAe,aAAa,CAAC;IAC5B,IAAI,EAAE;QACL,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,0CAA0C;KACvD;IACD,IAAI,EAAE;QACL,EAAE,EAAE;YACH,IAAI,EAAE,YAAY;YAClB,WAAW,EAAE,YAAY;YACzB,QAAQ,EAAE,IAAI;SACd;QACD,MAAM,EAAE;YACP,IAAI,EAAE,SAAS;YACf,WAAW,EAAE,yBAAyB;YACtC,QAAQ,EAAE,KAAK;SACf;QACD,UAAU,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,mDAAmD;YAChE,QAAQ,EAAE,KAAK;SACf;KACD;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE;QACtB,IAAI,QAA6D,CAAC;QAClE,IAAI,CAAC;YACJ,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC5B,QAAQ,GAAG,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YAElD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,qBAAqB,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC9C,oEAAoE;gBACpE,iDAAiD;gBACjD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;gBACrB,OAAO;YACR,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAE7C,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAClD,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;gBACzC,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;oBACzB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;gBACjC,CAAC;YACF,CAAC;QACF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;YAC7C,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACtB,CAAC;gBAAS,CAAC;YACV,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC;QACrB,CAAC;IACF,CAAC;CACD,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../../src/cli/serve.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../../src/cli/serve.ts"],"names":[],"mappings":";AAqBA,wBAeG"}
|
package/dist/src/cli/serve.js
CHANGED
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
* written exactly as the `investigate` command writes it.
|
|
11
11
|
*/
|
|
12
12
|
import { defineCommand } from "citty";
|
|
13
|
+
import { consola } from "consola";
|
|
13
14
|
import { runJsonRpcServer } from "../jsonrpc/server.js";
|
|
14
15
|
import { cliVersion } from "../version.js";
|
|
16
|
+
import { assertKnownFlags } from "./flags.js";
|
|
15
17
|
/** Reported as serverInfo.version by `initialize` (matches the CLI's meta.version). */
|
|
16
18
|
const SERVER_VERSION = cliVersion();
|
|
17
19
|
export default defineCommand({
|
|
@@ -19,8 +21,15 @@ export default defineCommand({
|
|
|
19
21
|
name: "serve",
|
|
20
22
|
description: "Run the JSON-RPC 2.0 server over stdio (the LIVE channel for the desktop app / API).",
|
|
21
23
|
},
|
|
22
|
-
async run() {
|
|
23
|
-
|
|
24
|
+
async run({ args, cmd }) {
|
|
25
|
+
try {
|
|
26
|
+
assertKnownFlags(args, cmd);
|
|
27
|
+
await runJsonRpcServer({ version: SERVER_VERSION });
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
consola.error(err instanceof Error ? err.message : String(err));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
24
33
|
},
|
|
25
34
|
});
|
|
26
35
|
//# sourceMappingURL=serve.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../../src/cli/serve.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,6BAA6B;AAE7B;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../../src/cli/serve.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,6BAA6B;AAE7B;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,uFAAuF;AACvF,MAAM,cAAc,GAAG,UAAU,EAAE,CAAC;AAEpC,eAAe,aAAa,CAAC;IAC5B,IAAI,EAAE;QACL,IAAI,EAAE,OAAO;QACb,WAAW,EACV,sFAAsF;KACvF;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE;QACtB,IAAI,CAAC;YACJ,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC5B,MAAM,gBAAgB,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACF,CAAC;CACD,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare const _default: import("citty").CommandDef<{
|
|
2
|
+
readonly status: {
|
|
3
|
+
readonly type: "string";
|
|
4
|
+
readonly description: "Filter by status (running, done, errored, suppressed)";
|
|
5
|
+
readonly required: false;
|
|
6
|
+
};
|
|
7
|
+
readonly "base-dir": {
|
|
8
|
+
readonly type: "string";
|
|
9
|
+
readonly description: "Workspace base directory (default: ~/.prismalens)";
|
|
10
|
+
readonly required: false;
|
|
11
|
+
};
|
|
12
|
+
}>;
|
|
13
|
+
export default _default;
|
|
14
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/cli/status.ts"],"names":[],"mappings":";;;;;;;;;;;;AAQA,wBA6EG"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 Sumit Patel
|
|
3
|
+
import { defineCommand } from "citty";
|
|
4
|
+
import consola from "consola";
|
|
5
|
+
import { createSessionManager } from "../core/session.js";
|
|
6
|
+
import { assertKnownFlags } from "./flags.js";
|
|
7
|
+
export default defineCommand({
|
|
8
|
+
meta: {
|
|
9
|
+
name: "status",
|
|
10
|
+
description: "List incident groups and runs with their state (running, done, errored, suppressed)",
|
|
11
|
+
},
|
|
12
|
+
args: {
|
|
13
|
+
status: {
|
|
14
|
+
type: "string",
|
|
15
|
+
description: "Filter by status (running, done, errored, suppressed)",
|
|
16
|
+
required: false,
|
|
17
|
+
},
|
|
18
|
+
"base-dir": {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "Workspace base directory (default: ~/.prismalens)",
|
|
21
|
+
required: false,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
async run({ args, cmd }) {
|
|
25
|
+
let sessions;
|
|
26
|
+
try {
|
|
27
|
+
assertKnownFlags(args, cmd);
|
|
28
|
+
sessions = createSessionManager(args["base-dir"]);
|
|
29
|
+
const valid = [
|
|
30
|
+
"running",
|
|
31
|
+
"done",
|
|
32
|
+
"errored",
|
|
33
|
+
"suppressed",
|
|
34
|
+
];
|
|
35
|
+
let filter;
|
|
36
|
+
if (args.status) {
|
|
37
|
+
const parts = args.status
|
|
38
|
+
.split(",")
|
|
39
|
+
.map((s) => s.trim())
|
|
40
|
+
.filter((s) => s.length > 0);
|
|
41
|
+
const bad = parts.filter((s) => !valid.includes(s));
|
|
42
|
+
if (bad.length > 0) {
|
|
43
|
+
consola.error(`Invalid status: ${bad.join(", ")}. Valid: ${valid.join(", ")}`);
|
|
44
|
+
// exitCode (not process.exit) so the finally block still closes the
|
|
45
|
+
// session manager before the process tears down.
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
filter = { status: parts };
|
|
50
|
+
}
|
|
51
|
+
const runs = await sessions.list(filter);
|
|
52
|
+
if (runs.length === 0) {
|
|
53
|
+
consola.info("No runs found.");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
for (const run of runs) {
|
|
57
|
+
const info = [
|
|
58
|
+
`run_id: ${run.runId}`,
|
|
59
|
+
`status: ${run.status}`,
|
|
60
|
+
`created_at: ${run.createdAt}`,
|
|
61
|
+
];
|
|
62
|
+
if (run.alertname)
|
|
63
|
+
info.push(`alertname: ${run.alertname}`);
|
|
64
|
+
if (run.agent)
|
|
65
|
+
info.push(`agent: ${run.agent}`);
|
|
66
|
+
if (run.repo)
|
|
67
|
+
info.push(`repo: ${run.repo}`);
|
|
68
|
+
if (run.status === "suppressed" && run.suppressionReason) {
|
|
69
|
+
info.push(`suppressed_by: ${run.suppressionReason}`);
|
|
70
|
+
}
|
|
71
|
+
consola.log(info.join(" | "));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
consola.error("Failed to read status:", err);
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
sessions?.close?.();
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/cli/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/config/loader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/config/loader.ts"],"names":[],"mappings":"AAiCA,OAAO,EAAE,KAAK,QAAQ,EAAkB,MAAM,aAAa,CAAC;AAM5D,MAAM,WAAW,YAAY;IAC5B,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IACjC,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,YAAY,CAAC;CAC5B;AAED,uFAAuF;AACvF,wBAAgB,cAAc,CAC7B,GAAG,GAAE,MAAsB,GACzB,MAAM,GAAG,SAAS,CASpB;AA+FD,2EAA2E;AAC3E,wBAAsB,UAAU,CAC/B,OAAO,GAAE,iBAAsB,GAC7B,OAAO,CAAC,QAAQ,CAAC,CA6DnB"}
|
|
@@ -29,6 +29,7 @@ import { readFile } from "node:fs/promises";
|
|
|
29
29
|
import { dirname, join, parse as parsePath, resolve } from "node:path";
|
|
30
30
|
import envPaths from "env-paths";
|
|
31
31
|
import { parse as parseYaml } from "yaml";
|
|
32
|
+
import { ZodError } from "zod";
|
|
32
33
|
import { PlConfigSchema } from "./schema.js";
|
|
33
34
|
const PROJECT_CONFIG_FILENAME = "prismalens.config.yaml";
|
|
34
35
|
const PROJECT_LOCAL_CONFIG_FILENAME = "prismalens.config.local.yaml";
|
|
@@ -125,18 +126,30 @@ function applyCliOverrides(config, o) {
|
|
|
125
126
|
/** Load, merge, interpolate, and validate the config. Result is frozen. */
|
|
126
127
|
export async function loadConfig(options = {}) {
|
|
127
128
|
const cwd = options.cwd ?? process.cwd();
|
|
129
|
+
if (options.configPath) {
|
|
130
|
+
const resolvedPath = resolve(options.configPath);
|
|
131
|
+
if (!existsSync(resolvedPath)) {
|
|
132
|
+
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
128
135
|
const userFile = userConfigFile();
|
|
129
136
|
const projectFile = options.configPath
|
|
130
137
|
? resolve(options.configPath)
|
|
131
138
|
: findConfigFile(cwd);
|
|
132
139
|
const projectLocalFile = projectLocalConfigFile(projectFile, cwd);
|
|
133
140
|
let merged = {};
|
|
134
|
-
|
|
141
|
+
const readFiles = [];
|
|
142
|
+
if (userFile) {
|
|
135
143
|
merged = deepMerge(merged, await readYamlFile(userFile));
|
|
136
|
-
|
|
144
|
+
readFiles.push(userFile);
|
|
145
|
+
}
|
|
146
|
+
if (projectFile) {
|
|
137
147
|
merged = deepMerge(merged, await readYamlFile(projectFile));
|
|
148
|
+
readFiles.push(projectFile);
|
|
149
|
+
}
|
|
138
150
|
if (projectLocalFile && projectLocalFile !== projectFile) {
|
|
139
151
|
merged = deepMerge(merged, await readYamlFile(projectLocalFile));
|
|
152
|
+
readFiles.push(projectLocalFile);
|
|
140
153
|
}
|
|
141
154
|
if (options.cliOverrides) {
|
|
142
155
|
merged = applyCliOverrides(merged, options.cliOverrides);
|
|
@@ -147,6 +160,19 @@ export async function loadConfig(options = {}) {
|
|
|
147
160
|
config = PlConfigSchema.parse(interpolated);
|
|
148
161
|
}
|
|
149
162
|
catch (err) {
|
|
163
|
+
let attribution = "in config";
|
|
164
|
+
if (readFiles.length === 1) {
|
|
165
|
+
attribution = `in ${readFiles[0]}`;
|
|
166
|
+
}
|
|
167
|
+
else if (readFiles.length > 1) {
|
|
168
|
+
attribution = `(merged from: ${readFiles.join(", ")})`;
|
|
169
|
+
}
|
|
170
|
+
if (err instanceof ZodError) {
|
|
171
|
+
const issues = err.issues
|
|
172
|
+
.map((i) => `${i.path.length > 0 ? i.path.join(".") : "<root>"}: ${i.message}`)
|
|
173
|
+
.join("\n");
|
|
174
|
+
throw new Error(`Invalid configuration ${attribution}:\n${issues}`);
|
|
175
|
+
}
|
|
150
176
|
const detail = err instanceof Error ? err.message : String(err);
|
|
151
177
|
throw new Error(`Invalid PrismaLens configuration: ${detail}`);
|
|
152
178
|
}
|