@a-company/sentinel 0.2.0 → 3.5.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/dist/adapters/express.d.ts +3 -1
- package/dist/adapters/fastify.d.ts +3 -1
- package/dist/adapters/hono.d.ts +3 -1
- package/dist/{chunk-KPMG4XED.js → chunk-FOF7CPJ6.js} +994 -2
- package/dist/chunk-VQ3SIN7S.js +422 -0
- package/dist/cli.js +6 -6
- package/dist/{commands-KIMGFR2I.js → commands-7PHRWGOB.js} +1791 -289
- package/dist/{dist-2F7NO4H4.js → dist-AG5JNIZU.js} +27 -2
- package/dist/{dist-BPWLYV4U.js → dist-TYG2XME3.js} +27 -2
- package/dist/index.d.ts +47 -5
- package/dist/index.js +141 -186
- package/dist/mcp.js +1040 -9
- package/dist/sdk-BTblv--p.d.ts +180 -0
- package/dist/server/index.d.ts +19 -3
- package/dist/server/index.js +581 -9
- package/dist/storage-BqCJqZat.d.ts +129 -0
- package/dist/transport-DqamniUy.d.ts +185 -0
- package/dist/transport.d.ts +2 -0
- package/dist/transport.js +10 -0
- package/dist/{sdk-B27_vK1g.d.ts → types-BmVoO1iF.d.ts} +196 -259
- package/package.json +15 -1
- package/ui/dist/assets/{index-DPxatSdT.css → index-9iUtfyBP.css} +1 -1
- package/ui/dist/assets/index-BfINPxlF.js +62 -0
- package/ui/dist/assets/index-BfINPxlF.js.map +1 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-BNgsn_C8.js +0 -62
- package/ui/dist/assets/index-BNgsn_C8.js.map +0 -1
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
var DEFAULTS = {
|
|
4
|
+
url: "http://localhost:3838",
|
|
5
|
+
batchSize: 50,
|
|
6
|
+
flushIntervalMs: 5e3,
|
|
7
|
+
maxBufferSize: 1e3,
|
|
8
|
+
maxRetries: 3,
|
|
9
|
+
retryBackoffMs: 1e3
|
|
10
|
+
};
|
|
11
|
+
var SentinelClient = class {
|
|
12
|
+
url;
|
|
13
|
+
service;
|
|
14
|
+
version;
|
|
15
|
+
environment;
|
|
16
|
+
token;
|
|
17
|
+
batchSize;
|
|
18
|
+
maxBufferSize;
|
|
19
|
+
maxRetries;
|
|
20
|
+
retryBackoffMs;
|
|
21
|
+
onDrop;
|
|
22
|
+
onError;
|
|
23
|
+
sessionId;
|
|
24
|
+
logBuffer = [];
|
|
25
|
+
metricsBuffer = [];
|
|
26
|
+
flushTimer = null;
|
|
27
|
+
closed = false;
|
|
28
|
+
beforeUnloadHandler = null;
|
|
29
|
+
constructor(options) {
|
|
30
|
+
this.url = (options.url ?? DEFAULTS.url).replace(/\/+$/, "");
|
|
31
|
+
this.service = options.service;
|
|
32
|
+
this.version = options.version;
|
|
33
|
+
this.environment = options.environment;
|
|
34
|
+
this.token = options.token;
|
|
35
|
+
this.batchSize = options.batchSize ?? DEFAULTS.batchSize;
|
|
36
|
+
this.maxBufferSize = options.maxBufferSize ?? DEFAULTS.maxBufferSize;
|
|
37
|
+
this.maxRetries = options.maxRetries ?? DEFAULTS.maxRetries;
|
|
38
|
+
this.retryBackoffMs = options.retryBackoffMs ?? DEFAULTS.retryBackoffMs;
|
|
39
|
+
this.onDrop = options.onDrop;
|
|
40
|
+
this.onError = options.onError;
|
|
41
|
+
this.sessionId = uuidv4();
|
|
42
|
+
const intervalMs = options.flushIntervalMs ?? DEFAULTS.flushIntervalMs;
|
|
43
|
+
this.flushTimer = setInterval(() => {
|
|
44
|
+
this.flush().catch((err) => {
|
|
45
|
+
this.handleError(err);
|
|
46
|
+
});
|
|
47
|
+
}, intervalMs);
|
|
48
|
+
if (this.flushTimer && typeof this.flushTimer === "object" && "unref" in this.flushTimer) {
|
|
49
|
+
this.flushTimer.unref();
|
|
50
|
+
}
|
|
51
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.addEventListener === "function") {
|
|
52
|
+
this.beforeUnloadHandler = () => {
|
|
53
|
+
this.flushSync();
|
|
54
|
+
};
|
|
55
|
+
globalThis.addEventListener("beforeunload", this.beforeUnloadHandler);
|
|
56
|
+
}
|
|
57
|
+
this.registerService();
|
|
58
|
+
}
|
|
59
|
+
// ── Logging Methods ──────────────────────────────────────────────
|
|
60
|
+
/** Log a debug-level message */
|
|
61
|
+
debug(symbol, message, data) {
|
|
62
|
+
this.log("debug", symbol, message, data);
|
|
63
|
+
}
|
|
64
|
+
/** Log an info-level message */
|
|
65
|
+
info(symbol, message, data) {
|
|
66
|
+
this.log("info", symbol, message, data);
|
|
67
|
+
}
|
|
68
|
+
/** Log a warn-level message */
|
|
69
|
+
warn(symbol, message, data) {
|
|
70
|
+
this.log("warn", symbol, message, data);
|
|
71
|
+
}
|
|
72
|
+
/** Log an error-level message */
|
|
73
|
+
error(symbol, message, data) {
|
|
74
|
+
this.log("error", symbol, message, data);
|
|
75
|
+
}
|
|
76
|
+
/** Log a message at the specified level */
|
|
77
|
+
log(level, symbol, message, data) {
|
|
78
|
+
if (this.closed) return;
|
|
79
|
+
const entry = {
|
|
80
|
+
level,
|
|
81
|
+
symbol,
|
|
82
|
+
message,
|
|
83
|
+
service: this.service,
|
|
84
|
+
sessionId: this.sessionId,
|
|
85
|
+
environment: this.environment,
|
|
86
|
+
data
|
|
87
|
+
};
|
|
88
|
+
this.pushToLogBuffer(entry);
|
|
89
|
+
}
|
|
90
|
+
// ── Metrics Methods ──────────────────────────────────────────────
|
|
91
|
+
/** Record a counter metric (increments) */
|
|
92
|
+
counter(name, value, tags) {
|
|
93
|
+
this.metric({ name, type: "counter", value: value ?? 1, tags });
|
|
94
|
+
}
|
|
95
|
+
/** Record a gauge metric (current value) */
|
|
96
|
+
gauge(name, value, tags) {
|
|
97
|
+
this.metric({ name, type: "gauge", value, tags });
|
|
98
|
+
}
|
|
99
|
+
/** Record a histogram metric (distribution) */
|
|
100
|
+
histogram(name, value, tags) {
|
|
101
|
+
this.metric({ name, type: "histogram", value, tags });
|
|
102
|
+
}
|
|
103
|
+
/** Record a metric of any type */
|
|
104
|
+
metric(input) {
|
|
105
|
+
if (this.closed) return;
|
|
106
|
+
const entry = {
|
|
107
|
+
...input,
|
|
108
|
+
service: this.service,
|
|
109
|
+
environment: this.environment
|
|
110
|
+
};
|
|
111
|
+
this.pushToMetricsBuffer(entry);
|
|
112
|
+
}
|
|
113
|
+
// ── State Push ───────────────────────────────────────────────────
|
|
114
|
+
/** Push an application state snapshot to the server */
|
|
115
|
+
async pushState(state, activeFlows, activeGates) {
|
|
116
|
+
if (this.closed) return;
|
|
117
|
+
await this.post("/api/state", {
|
|
118
|
+
service: this.service,
|
|
119
|
+
sessionId: this.sessionId,
|
|
120
|
+
state,
|
|
121
|
+
activeFlows,
|
|
122
|
+
activeGates
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// ── Tracing ──────────────────────────────────────────────────────
|
|
126
|
+
/** Start a trace span. Call end() on the returned SpanContext when the operation completes. */
|
|
127
|
+
startSpan(symbol, operation, parentSpanId) {
|
|
128
|
+
const traceId = parentSpanId ? parentSpanId.split("-")[0] || uuidv4() : uuidv4();
|
|
129
|
+
const spanId = uuidv4();
|
|
130
|
+
const startTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
131
|
+
const startMs = Date.now();
|
|
132
|
+
const self = this;
|
|
133
|
+
return {
|
|
134
|
+
traceId,
|
|
135
|
+
spanId,
|
|
136
|
+
async end(status = "ok") {
|
|
137
|
+
const endTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
138
|
+
const durationMs = Date.now() - startMs;
|
|
139
|
+
const span = {
|
|
140
|
+
traceId,
|
|
141
|
+
spanId,
|
|
142
|
+
parentSpanId,
|
|
143
|
+
service: self.service,
|
|
144
|
+
symbol,
|
|
145
|
+
operation,
|
|
146
|
+
startTime,
|
|
147
|
+
endTime,
|
|
148
|
+
durationMs,
|
|
149
|
+
status
|
|
150
|
+
};
|
|
151
|
+
await self.post("/api/traces", span);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// ── Buffer Management ────────────────────────────────────────────
|
|
156
|
+
/** Flush all buffered logs and metrics to the server */
|
|
157
|
+
async flush() {
|
|
158
|
+
const logEntries = this.drainLogBuffer();
|
|
159
|
+
const metricEntries = this.drainMetricsBuffer();
|
|
160
|
+
const promises = [];
|
|
161
|
+
if (logEntries.length > 0) {
|
|
162
|
+
promises.push(this.sendLogs(logEntries));
|
|
163
|
+
}
|
|
164
|
+
if (metricEntries.length > 0) {
|
|
165
|
+
promises.push(this.sendMetrics(metricEntries));
|
|
166
|
+
}
|
|
167
|
+
await Promise.allSettled(promises);
|
|
168
|
+
}
|
|
169
|
+
// ── Lifecycle ────────────────────────────────────────────────────
|
|
170
|
+
/** Flush remaining entries and shut down the client */
|
|
171
|
+
async close() {
|
|
172
|
+
if (this.closed) return;
|
|
173
|
+
this.closed = true;
|
|
174
|
+
if (this.flushTimer !== null) {
|
|
175
|
+
clearInterval(this.flushTimer);
|
|
176
|
+
this.flushTimer = null;
|
|
177
|
+
}
|
|
178
|
+
if (this.beforeUnloadHandler && typeof globalThis !== "undefined" && typeof globalThis.removeEventListener === "function") {
|
|
179
|
+
globalThis.removeEventListener("beforeunload", this.beforeUnloadHandler);
|
|
180
|
+
this.beforeUnloadHandler = null;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
await this.flush();
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/** Get the session ID assigned to this client instance */
|
|
188
|
+
getSessionId() {
|
|
189
|
+
return this.sessionId;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Synchronous best-effort flush using sendBeacon (browser) or sync XHR fallback.
|
|
193
|
+
* Used in beforeunload where async is unreliable.
|
|
194
|
+
*/
|
|
195
|
+
flushSync() {
|
|
196
|
+
const logEntries = this.drainLogBuffer();
|
|
197
|
+
const metricEntries = this.drainMetricsBuffer();
|
|
198
|
+
const sendBeacon = typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" ? navigator.sendBeacon.bind(navigator) : void 0;
|
|
199
|
+
if (sendBeacon) {
|
|
200
|
+
if (logEntries.length > 0) {
|
|
201
|
+
sendBeacon(`${this.url}/api/logs`, JSON.stringify({ entries: logEntries }));
|
|
202
|
+
}
|
|
203
|
+
if (metricEntries.length > 0) {
|
|
204
|
+
sendBeacon(`${this.url}/api/metrics`, JSON.stringify({ entries: metricEntries }));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
209
|
+
// PRIVATE METHODS
|
|
210
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
211
|
+
/** Register this service with the Sentinel server (fire-and-forget) */
|
|
212
|
+
registerService() {
|
|
213
|
+
const registration = {
|
|
214
|
+
name: this.service,
|
|
215
|
+
version: this.version,
|
|
216
|
+
pid: typeof process !== "undefined" ? process.pid : void 0,
|
|
217
|
+
environment: this.environment
|
|
218
|
+
};
|
|
219
|
+
this.post("/api/services", registration).catch(() => {
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/** Push a log entry into the ring buffer, enforcing maxBufferSize */
|
|
223
|
+
pushToLogBuffer(entry) {
|
|
224
|
+
if (this.logBuffer.length >= this.maxBufferSize) {
|
|
225
|
+
const dropCount = Math.max(1, Math.floor(this.maxBufferSize * 0.1));
|
|
226
|
+
this.logBuffer.splice(0, dropCount);
|
|
227
|
+
if (this.onDrop) {
|
|
228
|
+
this.onDrop(dropCount);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
this.logBuffer.push(entry);
|
|
232
|
+
if (this.logBuffer.length >= this.batchSize) {
|
|
233
|
+
this.flush().catch((err) => {
|
|
234
|
+
this.handleError(err);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/** Push a metric entry into the ring buffer, enforcing maxBufferSize */
|
|
239
|
+
pushToMetricsBuffer(entry) {
|
|
240
|
+
if (this.metricsBuffer.length >= this.maxBufferSize) {
|
|
241
|
+
const dropCount = Math.max(1, Math.floor(this.maxBufferSize * 0.1));
|
|
242
|
+
this.metricsBuffer.splice(0, dropCount);
|
|
243
|
+
if (this.onDrop) {
|
|
244
|
+
this.onDrop(dropCount);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
this.metricsBuffer.push(entry);
|
|
248
|
+
if (this.metricsBuffer.length >= this.batchSize) {
|
|
249
|
+
this.flush().catch((err) => {
|
|
250
|
+
this.handleError(err);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/** Drain and return all entries from the log buffer */
|
|
255
|
+
drainLogBuffer() {
|
|
256
|
+
const entries = this.logBuffer;
|
|
257
|
+
this.logBuffer = [];
|
|
258
|
+
return entries;
|
|
259
|
+
}
|
|
260
|
+
/** Drain and return all entries from the metrics buffer */
|
|
261
|
+
drainMetricsBuffer() {
|
|
262
|
+
const entries = this.metricsBuffer;
|
|
263
|
+
this.metricsBuffer = [];
|
|
264
|
+
return entries;
|
|
265
|
+
}
|
|
266
|
+
/** Send log entries to the server with retry */
|
|
267
|
+
async sendLogs(entries) {
|
|
268
|
+
try {
|
|
269
|
+
await this.post("/api/logs", { entries });
|
|
270
|
+
} catch (err) {
|
|
271
|
+
const capacity = this.maxBufferSize - this.logBuffer.length;
|
|
272
|
+
if (capacity > 0) {
|
|
273
|
+
const toRestore = entries.slice(0, capacity);
|
|
274
|
+
this.logBuffer.unshift(...toRestore);
|
|
275
|
+
const dropped = entries.length - toRestore.length;
|
|
276
|
+
if (dropped > 0 && this.onDrop) {
|
|
277
|
+
this.onDrop(dropped);
|
|
278
|
+
}
|
|
279
|
+
} else if (this.onDrop) {
|
|
280
|
+
this.onDrop(entries.length);
|
|
281
|
+
}
|
|
282
|
+
throw err;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/** Send metric entries to the server with retry */
|
|
286
|
+
async sendMetrics(entries) {
|
|
287
|
+
try {
|
|
288
|
+
await this.post("/api/metrics", { entries });
|
|
289
|
+
} catch (err) {
|
|
290
|
+
const capacity = this.maxBufferSize - this.metricsBuffer.length;
|
|
291
|
+
if (capacity > 0) {
|
|
292
|
+
const toRestore = entries.slice(0, capacity);
|
|
293
|
+
this.metricsBuffer.unshift(...toRestore);
|
|
294
|
+
const dropped = entries.length - toRestore.length;
|
|
295
|
+
if (dropped > 0 && this.onDrop) {
|
|
296
|
+
this.onDrop(dropped);
|
|
297
|
+
}
|
|
298
|
+
} else if (this.onDrop) {
|
|
299
|
+
this.onDrop(entries.length);
|
|
300
|
+
}
|
|
301
|
+
throw err;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* POST JSON to the Sentinel server with exponential backoff retry.
|
|
306
|
+
* Retries on network errors and 5xx responses. Does NOT retry on 4xx.
|
|
307
|
+
*/
|
|
308
|
+
async post(path, body) {
|
|
309
|
+
const fetchFn = this.getFetch();
|
|
310
|
+
if (!fetchFn) {
|
|
311
|
+
return void 0;
|
|
312
|
+
}
|
|
313
|
+
const url = `${this.url}${path}`;
|
|
314
|
+
const headers = {
|
|
315
|
+
"Content-Type": "application/json"
|
|
316
|
+
};
|
|
317
|
+
if (this.token) {
|
|
318
|
+
headers["Authorization"] = `Bearer ${this.token}`;
|
|
319
|
+
}
|
|
320
|
+
let lastError;
|
|
321
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
322
|
+
try {
|
|
323
|
+
const response = await fetchFn(url, {
|
|
324
|
+
method: "POST",
|
|
325
|
+
headers,
|
|
326
|
+
body: JSON.stringify(body)
|
|
327
|
+
});
|
|
328
|
+
if (response.status >= 400 && response.status < 500) {
|
|
329
|
+
const text = await response.text().catch(() => "");
|
|
330
|
+
const err = new Error(`Sentinel server returned ${response.status}: ${text}`);
|
|
331
|
+
this.handleError(err);
|
|
332
|
+
return void 0;
|
|
333
|
+
}
|
|
334
|
+
if (response.status >= 500) {
|
|
335
|
+
const text = await response.text().catch(() => "");
|
|
336
|
+
lastError = new Error(`Sentinel server returned ${response.status}: ${text}`);
|
|
337
|
+
if (attempt < this.maxRetries) {
|
|
338
|
+
await this.backoff(attempt);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
this.handleError(lastError);
|
|
342
|
+
throw lastError;
|
|
343
|
+
}
|
|
344
|
+
const contentType = response.headers.get("content-type") || "";
|
|
345
|
+
if (contentType.includes("application/json")) {
|
|
346
|
+
return await response.json();
|
|
347
|
+
}
|
|
348
|
+
return void 0;
|
|
349
|
+
} catch (err) {
|
|
350
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
351
|
+
if (attempt < this.maxRetries) {
|
|
352
|
+
await this.backoff(attempt);
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
this.handleError(lastError);
|
|
356
|
+
throw lastError;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (lastError) throw lastError;
|
|
360
|
+
return void 0;
|
|
361
|
+
}
|
|
362
|
+
/** Get the fetch function, falling back to globalThis.fetch */
|
|
363
|
+
getFetch() {
|
|
364
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.fetch === "function") {
|
|
365
|
+
return globalThis.fetch;
|
|
366
|
+
}
|
|
367
|
+
return void 0;
|
|
368
|
+
}
|
|
369
|
+
/** Wait with exponential backoff */
|
|
370
|
+
backoff(attempt) {
|
|
371
|
+
const delayMs = this.retryBackoffMs * Math.pow(2, attempt);
|
|
372
|
+
const jitter = Math.floor(Math.random() * delayMs * 0.25);
|
|
373
|
+
return new Promise((resolve) => setTimeout(resolve, delayMs + jitter));
|
|
374
|
+
}
|
|
375
|
+
/** Handle an error, calling the onError callback if provided */
|
|
376
|
+
handleError(err) {
|
|
377
|
+
if (this.onError) {
|
|
378
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
379
|
+
this.onError(error);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
function createSentinelClient(options) {
|
|
384
|
+
return new SentinelClient(options);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// src/transport.ts
|
|
388
|
+
var SentinelTransport = class {
|
|
389
|
+
client;
|
|
390
|
+
constructor(client) {
|
|
391
|
+
this.client = client;
|
|
392
|
+
}
|
|
393
|
+
send(entry) {
|
|
394
|
+
this.client.log(
|
|
395
|
+
entry.level,
|
|
396
|
+
entry.symbol,
|
|
397
|
+
entry.message,
|
|
398
|
+
{
|
|
399
|
+
...entry.data,
|
|
400
|
+
symbolType: entry.symbolType,
|
|
401
|
+
correlationId: entry.correlationId
|
|
402
|
+
}
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
function createSentinelTransport(clientOrOptions) {
|
|
407
|
+
const client = clientOrOptions instanceof SentinelClient ? clientOrOptions : new SentinelClient(clientOrOptions);
|
|
408
|
+
return new SentinelTransport(client);
|
|
409
|
+
}
|
|
410
|
+
function enableSentinel(logger, clientOrOptions) {
|
|
411
|
+
const transport = createSentinelTransport(clientOrOptions);
|
|
412
|
+
logger.addTransport(transport);
|
|
413
|
+
return transport;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export {
|
|
417
|
+
SentinelClient,
|
|
418
|
+
createSentinelClient,
|
|
419
|
+
SentinelTransport,
|
|
420
|
+
createSentinelTransport,
|
|
421
|
+
enableSentinel
|
|
422
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -5,28 +5,28 @@ import { Command } from "commander";
|
|
|
5
5
|
var program = new Command();
|
|
6
6
|
program.name("sentinel").description("Semantic error monitoring \u2014 errors that speak your language").version("0.2.0");
|
|
7
7
|
program.command("dashboard", { isDefault: true }).description("Launch the Sentinel dashboard").option("-p, --port <port>", "Port number", "3838").option("--no-open", "Don't open browser").action(async (opts) => {
|
|
8
|
-
const { launchDashboard } = await import("./commands-
|
|
8
|
+
const { launchDashboard } = await import("./commands-7PHRWGOB.js");
|
|
9
9
|
await launchDashboard(opts);
|
|
10
10
|
});
|
|
11
11
|
program.command("init").description("Initialize Sentinel in current project").option("--detect", "Auto-detect symbols from codebase").action(async (opts) => {
|
|
12
|
-
const { initProject } = await import("./commands-
|
|
12
|
+
const { initProject } = await import("./commands-7PHRWGOB.js");
|
|
13
13
|
await initProject(opts);
|
|
14
14
|
});
|
|
15
15
|
var triage = program.command("triage").description("Incident triage");
|
|
16
16
|
triage.command("list").description("List incidents").option("-s, --status <status>", "Filter by status (open, investigating, resolved, wont-fix)").option("-e, --env <env>", "Filter by environment").option("--symbol <symbol>", "Filter by symbol").option("-n, --limit <n>", "Max results", "10").action(async (opts) => {
|
|
17
|
-
const { triageList } = await import("./commands-
|
|
17
|
+
const { triageList } = await import("./commands-7PHRWGOB.js");
|
|
18
18
|
await triageList(opts);
|
|
19
19
|
});
|
|
20
20
|
triage.command("show <id>").description("Show incident details").option("--timeline", "Include flow timeline").action(async (id, opts) => {
|
|
21
|
-
const { triageShow } = await import("./commands-
|
|
21
|
+
const { triageShow } = await import("./commands-7PHRWGOB.js");
|
|
22
22
|
await triageShow(id, opts);
|
|
23
23
|
});
|
|
24
24
|
triage.command("resolve <id>").description("Resolve an incident").option("--pattern <id>", "Pattern that resolved it").option("--commit <hash>", "Fix commit").option("--notes <text>", "Resolution notes").action(async (id, opts) => {
|
|
25
|
-
const { triageResolve } = await import("./commands-
|
|
25
|
+
const { triageResolve } = await import("./commands-7PHRWGOB.js");
|
|
26
26
|
await triageResolve(id, opts);
|
|
27
27
|
});
|
|
28
28
|
triage.command("stats").description("Show incident statistics").option("-p, --period <period>", "Period (7d, 30d, 90d)", "7d").action(async (opts) => {
|
|
29
|
-
const { triageStats } = await import("./commands-
|
|
29
|
+
const { triageStats } = await import("./commands-7PHRWGOB.js");
|
|
30
30
|
await triageStats(opts);
|
|
31
31
|
});
|
|
32
32
|
program.parse();
|