@secondlayer/sentinel 0.4.0 → 0.6.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 +2 -1
- package/dist/cli.js +842 -28
- package/dist/index.js +96 -2
- package/dist/mcp.js +614 -20
- package/dist/sentinel-client.d.ts +483 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -79,6 +79,13 @@ class SentinelError extends Error {
|
|
|
79
79
|
this.name = "SentinelError";
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
+
var runInProgress = (status) => status === "queued" || status === "running";
|
|
83
|
+
function runPhase(run) {
|
|
84
|
+
if (run.status === "queued")
|
|
85
|
+
return "queued";
|
|
86
|
+
const last = run.events.at(-1);
|
|
87
|
+
return last ? [last.name ?? last.kind, last.state].filter(Boolean).join(" ") : "starting";
|
|
88
|
+
}
|
|
82
89
|
var rowsOf = (intents) => intents.flatMap((i) => i.rows);
|
|
83
90
|
var subjectKey = (s) => s.kind === "fn" ? `fn:${s.fn}` : s.kind === "group" ? `group:${s.group}` : `asset:${s.asset}`;
|
|
84
91
|
var CONTROL_NAME = "Control changes";
|
|
@@ -206,10 +213,30 @@ function mutedUntilOf(w, now = Date.now()) {
|
|
|
206
213
|
const at = w.delivery.mutedUntil;
|
|
207
214
|
return at && Date.parse(at) > now ? at : null;
|
|
208
215
|
}
|
|
216
|
+
function changeText(c) {
|
|
217
|
+
const name = c.subject.kind === "asset" ? subjectName(c.subject, c.units) : c.watch.title;
|
|
218
|
+
const rule = (r) => ruleSummary(r, c.units);
|
|
219
|
+
switch (c.action) {
|
|
220
|
+
case "on":
|
|
221
|
+
return `${c.watch.title} turned on`;
|
|
222
|
+
case "off":
|
|
223
|
+
return `${c.watch.title} turned off`;
|
|
224
|
+
case "muted":
|
|
225
|
+
case "cooldown":
|
|
226
|
+
return `${c.watch.title}: ${c.action === "muted" ? "mute" : "cooldown"} ${c.note ?? ""}`.trim();
|
|
227
|
+
case "applied":
|
|
228
|
+
if (c.prevRule && c.rule)
|
|
229
|
+
return `${name} rule changed: ${rule(c.prevRule)} → ${rule(c.rule)}`;
|
|
230
|
+
return c.rule ? `${name} rule set: ${rule(c.rule)}` : `${name} rule set`;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
209
233
|
function createClient(opts) {
|
|
210
234
|
const baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
211
235
|
const doFetch = opts.fetch ?? globalThis.fetch;
|
|
212
|
-
const authHeader = () => ({
|
|
236
|
+
const authHeader = () => ({
|
|
237
|
+
authorization: `Bearer ${opts.token}`,
|
|
238
|
+
...opts.client ? { "x-sentinel-client": opts.client } : {}
|
|
239
|
+
});
|
|
213
240
|
async function call(method, path, body) {
|
|
214
241
|
const res = await doFetch(`${baseUrl}${path}`, {
|
|
215
242
|
method,
|
|
@@ -226,6 +253,7 @@ function createClient(opts) {
|
|
|
226
253
|
}
|
|
227
254
|
const plan = (id) => `/v1/plans/${encodeURIComponent(id)}`;
|
|
228
255
|
const appPlan = (id) => `/plan/${encodeURIComponent(id)}`;
|
|
256
|
+
const appProject = (id) => `/projects/${encodeURIComponent(id)}`;
|
|
229
257
|
return {
|
|
230
258
|
me: () => call("GET", "/v1/me"),
|
|
231
259
|
plans: async () => (await call("GET", "/v1/plans")).plans,
|
|
@@ -241,6 +269,8 @@ function createClient(opts) {
|
|
|
241
269
|
testsFromRepo: (id) => call("POST", `${appPlan(id)}/tests`, { fromRepo: true }),
|
|
242
270
|
previewPlan: (contractId) => call("POST", "/plan/preview", { contractId }),
|
|
243
271
|
createPlan: (contractId) => call("POST", "/plan", { contractId }),
|
|
272
|
+
previewImport: (url) => call("POST", "/projects/import/preview", { url }),
|
|
273
|
+
importRepo: (input) => call("POST", "/projects/import", input),
|
|
244
274
|
billing: () => call("GET", "/billing"),
|
|
245
275
|
watches: (planId) => call("GET", `${appPlan(planId)}/watches`),
|
|
246
276
|
setRule: (planId, watchId, rule) => call("POST", `${appPlan(planId)}/watches/${encodeURIComponent(watchId)}/rule`, { rule }),
|
|
@@ -257,9 +287,70 @@ function createClient(opts) {
|
|
|
257
287
|
watchContract: (contractId) => call("POST", "/contracts/watch", {
|
|
258
288
|
contractId
|
|
259
289
|
}),
|
|
260
|
-
stopMonitoring: (planId) => call("DELETE", `${appPlan(planId)}/monitor`)
|
|
290
|
+
stopMonitoring: (planId) => call("DELETE", `${appPlan(planId)}/monitor`),
|
|
291
|
+
watchActivity: (planId, intentId, opts = {}) => call("GET", `${appPlan(planId)}/watches/${encodeURIComponent(intentId)}/activity${query(opts)}`),
|
|
292
|
+
functions: async (planId) => (await call("GET", `${appPlan(planId)}/functions`)).functions,
|
|
293
|
+
functionActivity: (planId, fn) => call("GET", `${appPlan(planId)}/functions/${encodeURIComponent(fn)}/activity`),
|
|
294
|
+
alertPreview: (planId, example) => call("GET", `${appPlan(planId)}/alert-preview${query({ example })}`),
|
|
295
|
+
threshold: (planId, asset) => call("GET", `${appPlan(planId)}/watches/asset/${encodeURIComponent(asset)}/threshold`),
|
|
296
|
+
setThreshold: (planId, asset, amount) => call("POST", `${appPlan(planId)}/watches/asset/${encodeURIComponent(asset)}/threshold`, { amount }),
|
|
297
|
+
retryHistory: (planId) => call("POST", `${appPlan(planId)}/history/retry`, {}),
|
|
298
|
+
readAlert: (alertId) => call("POST", `/alerts/${encodeURIComponent(alertId)}/read`),
|
|
299
|
+
readAllAlerts: () => call("POST", "/alerts/read-all"),
|
|
300
|
+
ask: (q) => call("POST", "/ask", { q }),
|
|
301
|
+
acknowledge: (planId, finding, title, reason, note = null) => call("POST", `${appPlan(planId)}/acknowledge`, {
|
|
302
|
+
finding,
|
|
303
|
+
title,
|
|
304
|
+
reason,
|
|
305
|
+
note
|
|
306
|
+
}),
|
|
307
|
+
reopen: (planId, finding, title) => call("POST", `${appPlan(planId)}/reopen`, { finding, title }),
|
|
308
|
+
ackHistory: async (planId) => (await call("GET", `${appPlan(planId)}/acks`)).events,
|
|
309
|
+
notes: async (planId, finding) => (await call("GET", `${appPlan(planId)}/findings/${finding}/notes`)).notes,
|
|
310
|
+
addNote: (planId, finding, title, body) => call("POST", `${appPlan(planId)}/findings/${finding}/notes`, {
|
|
311
|
+
title,
|
|
312
|
+
body
|
|
313
|
+
}),
|
|
314
|
+
tests: (planId, files) => call("POST", `${appPlan(planId)}/tests`, { files }),
|
|
315
|
+
fuzzCheck: (planId, harness) => call("POST", `${appPlan(planId)}/fuzzcheck`, { harness }),
|
|
316
|
+
fuzz: (planId, invariants) => call("POST", `${appPlan(planId)}/fuzz`, { invariants }),
|
|
317
|
+
latestCommit: (planId) => call("GET", `${appPlan(planId)}/latest`),
|
|
318
|
+
updatePlan: (planId, commit) => call("POST", `${appPlan(planId)}/update`, commit ? { commit } : {}),
|
|
319
|
+
setAutoPlan: (planId, auto) => call("POST", `${appPlan(planId)}/wait`, { auto }),
|
|
320
|
+
setLink: (planId, dependency, url) => call("POST", `${appPlan(planId)}/link`, { id: dependency, url }),
|
|
321
|
+
planLinked: (planId) => call("POST", `${appPlan(planId)}/plan-linked`, {}),
|
|
322
|
+
renameContract: (planId, name) => call("POST", `${appPlan(planId)}/settings`, { name }),
|
|
323
|
+
removeContract: (planId, confirm) => call("POST", `${appPlan(planId)}/remove`, {
|
|
324
|
+
confirm
|
|
325
|
+
}),
|
|
326
|
+
projects: () => call("GET", "/projects"),
|
|
327
|
+
project: (projectId) => call("GET", appProject(projectId)),
|
|
328
|
+
projectOverview: (projectId, opts = {}) => call("GET", `${appProject(projectId)}/overview${opts.watches ? "?watches=1" : ""}`),
|
|
329
|
+
renameProject: (projectId, name) => call("POST", `${appProject(projectId)}/rename`, { name }),
|
|
330
|
+
removeProject: (projectId, confirm) => call("POST", `${appProject(projectId)}/remove`, {
|
|
331
|
+
confirm
|
|
332
|
+
}),
|
|
333
|
+
answerMergeSuggestion: (from, into, accept) => call("POST", "/projects/merge", { from, into, accept }),
|
|
334
|
+
mergeProject: (projectId, into) => call("POST", `${appProject(projectId)}/merge-into`, { into }),
|
|
335
|
+
moveProjectContract: (projectId, contractId, into) => call("POST", `${appProject(projectId)}/contracts/${encodeURIComponent(contractId)}/move`, { into }),
|
|
336
|
+
removeProjectContract: (projectId, contractId, confirm) => call("POST", `${appProject(projectId)}/contracts/${encodeURIComponent(contractId)}/remove`, { confirm }),
|
|
337
|
+
previewProjectContract: (projectId, contractId) => call("POST", `${appProject(projectId)}/contracts/preview`, { contractId }),
|
|
338
|
+
addProjectContract: (projectId, contractId, opts = {}) => call("POST", `${appProject(projectId)}/contracts`, { contractId, ...opts }),
|
|
339
|
+
setProjectDestinations: (projectId, dest) => call("POST", `${appProject(projectId)}/destinations`, dest),
|
|
340
|
+
setProjectMonitoring: (projectId, on) => call("POST", `${appProject(projectId)}/monitoring`, { on }),
|
|
341
|
+
projectWatches: (projectId) => call("GET", `${appProject(projectId)}/watches`),
|
|
342
|
+
projectActivity: (projectId, intentId, opts = {}) => call("GET", `${appProject(projectId)}/watches/${encodeURIComponent(intentId)}/activity${query(opts)}`),
|
|
343
|
+
projectChanges: (projectId, opts = {}) => call("GET", `${appProject(projectId)}/changes${query(opts)}`)
|
|
261
344
|
};
|
|
262
345
|
}
|
|
346
|
+
function query(params) {
|
|
347
|
+
const q = new URLSearchParams;
|
|
348
|
+
for (const [k, v] of Object.entries(params))
|
|
349
|
+
if (v !== undefined && v !== null)
|
|
350
|
+
q.set(k, String(v));
|
|
351
|
+
const s = q.toString();
|
|
352
|
+
return s ? `?${s}` : "";
|
|
353
|
+
}
|
|
263
354
|
var HOUR_MS = 3600000;
|
|
264
355
|
var muteEnd = (hours) => new Date(Date.now() + Math.min(hours * HOUR_MS, 7 * 24 * HOUR_MS - 60000)).toISOString();
|
|
265
356
|
async function watchContext(client, planId) {
|
|
@@ -295,6 +386,7 @@ export {
|
|
|
295
386
|
SentinelError,
|
|
296
387
|
SubjectError,
|
|
297
388
|
assetClause,
|
|
389
|
+
changeText,
|
|
298
390
|
clearSession,
|
|
299
391
|
clearTokenFile,
|
|
300
392
|
createClient,
|
|
@@ -312,6 +404,8 @@ export {
|
|
|
312
404
|
resolveToken,
|
|
313
405
|
rowsOf,
|
|
314
406
|
ruleSummary,
|
|
407
|
+
runInProgress,
|
|
408
|
+
runPhase,
|
|
315
409
|
savedChoices,
|
|
316
410
|
savedMonitor,
|
|
317
411
|
sessionPath,
|