@secondlayer/sentinel 0.2.0 → 0.3.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/cli.js +8 -9
- package/dist/index.js +9 -9
- package/dist/mcp.js +19 -11
- package/dist/sentinel-client.d.ts +36 -15
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -176,16 +176,15 @@ function ruleSummary(rule, units) {
|
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
|
+
function assetClause(a) {
|
|
180
|
+
const symbol = subjectName({ kind: "asset", asset: a.asset }, a.units);
|
|
181
|
+
const amount = a.threshold.amount;
|
|
182
|
+
if (amount === null)
|
|
183
|
+
return `${symbol} every outflow`;
|
|
184
|
+
return `${symbol} over ${a.units ? toUnits(amount, a.units.decimals) : `${group(amount)} base units`}`;
|
|
185
|
+
}
|
|
179
186
|
function intentLine(i) {
|
|
180
|
-
|
|
181
|
-
if (i.kind !== "money_out" || !t)
|
|
182
|
-
return i.summary;
|
|
183
|
-
const units = i.units ?? null;
|
|
184
|
-
if (t.amount === null) {
|
|
185
|
-
const sug = t.suggested ? ` (suggested ${amountText(t.suggested.amount, units)}, ${t.suggested.basis})` : "";
|
|
186
|
-
return `no threshold yet: every outflow alerts${sug}`;
|
|
187
|
-
}
|
|
188
|
-
return `${amountText(t.amount, units)} or more in one tx · ${t.basis}`;
|
|
187
|
+
return i.kind === "money_out" && i.assets?.length ? i.assets.map(assetClause).join(" · ") : i.summary;
|
|
189
188
|
}
|
|
190
189
|
var isMutableRule = (rule) => rule.kind !== "signature" && rule.kind !== "audit";
|
|
191
190
|
function mutedUntilOf(w, now = Date.now()) {
|
package/dist/index.js
CHANGED
|
@@ -185,16 +185,15 @@ function kindTag(rule) {
|
|
|
185
185
|
return rule.triggerClass === "governance.proposal_submitted" ? "governance" : "upgrade";
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
|
+
function assetClause(a) {
|
|
189
|
+
const symbol = subjectName({ kind: "asset", asset: a.asset }, a.units);
|
|
190
|
+
const amount = a.threshold.amount;
|
|
191
|
+
if (amount === null)
|
|
192
|
+
return `${symbol} every outflow`;
|
|
193
|
+
return `${symbol} over ${a.units ? toUnits(amount, a.units.decimals) : `${group(amount)} base units`}`;
|
|
194
|
+
}
|
|
188
195
|
function intentLine(i) {
|
|
189
|
-
|
|
190
|
-
if (i.kind !== "money_out" || !t)
|
|
191
|
-
return i.summary;
|
|
192
|
-
const units = i.units ?? null;
|
|
193
|
-
if (t.amount === null) {
|
|
194
|
-
const sug = t.suggested ? ` (suggested ${amountText(t.suggested.amount, units)}, ${t.suggested.basis})` : "";
|
|
195
|
-
return `no threshold yet: every outflow alerts${sug}`;
|
|
196
|
-
}
|
|
197
|
-
return `${amountText(t.amount, units)} or more in one tx · ${t.basis}`;
|
|
196
|
+
return i.kind === "money_out" && i.assets?.length ? i.assets.map(assetClause).join(" · ") : i.summary;
|
|
198
197
|
}
|
|
199
198
|
function subjectStatus(ws) {
|
|
200
199
|
const st = ws.map((w) => w.status);
|
|
@@ -295,6 +294,7 @@ export {
|
|
|
295
294
|
NOT_LOGGED_IN,
|
|
296
295
|
SentinelError,
|
|
297
296
|
SubjectError,
|
|
297
|
+
assetClause,
|
|
298
298
|
clearSession,
|
|
299
299
|
clearTokenFile,
|
|
300
300
|
createClient,
|
package/dist/mcp.js
CHANGED
|
@@ -12750,16 +12750,15 @@ function ruleSummary(rule, units) {
|
|
|
12750
12750
|
}
|
|
12751
12751
|
}
|
|
12752
12752
|
}
|
|
12753
|
+
function assetClause(a) {
|
|
12754
|
+
const symbol = subjectName({ kind: "asset", asset: a.asset }, a.units);
|
|
12755
|
+
const amount = a.threshold.amount;
|
|
12756
|
+
if (amount === null)
|
|
12757
|
+
return `${symbol} every outflow`;
|
|
12758
|
+
return `${symbol} over ${a.units ? toUnits(amount, a.units.decimals) : `${group(amount)} base units`}`;
|
|
12759
|
+
}
|
|
12753
12760
|
function intentLine(i) {
|
|
12754
|
-
|
|
12755
|
-
if (i.kind !== "money_out" || !t)
|
|
12756
|
-
return i.summary;
|
|
12757
|
-
const units = i.units ?? null;
|
|
12758
|
-
if (t.amount === null) {
|
|
12759
|
-
const sug = t.suggested ? ` (suggested ${amountText(t.suggested.amount, units)}, ${t.suggested.basis})` : "";
|
|
12760
|
-
return `no threshold yet: every outflow alerts${sug}`;
|
|
12761
|
-
}
|
|
12762
|
-
return `${amountText(t.amount, units)} or more in one tx · ${t.basis}`;
|
|
12761
|
+
return i.kind === "money_out" && i.assets?.length ? i.assets.map(assetClause).join(" · ") : i.summary;
|
|
12763
12762
|
}
|
|
12764
12763
|
function subjectStatus(ws) {
|
|
12765
12764
|
const st = ws.map((w) => w.status);
|
|
@@ -20646,8 +20645,17 @@ function intentView(i) {
|
|
|
20646
20645
|
kind: i.kind,
|
|
20647
20646
|
on: i.on,
|
|
20648
20647
|
alertLevel: i.alertLevel,
|
|
20648
|
+
coverage: i.coverage,
|
|
20649
20649
|
summary: intentLine(i),
|
|
20650
|
+
...i.assets ? {
|
|
20651
|
+
assets: i.assets.map((a) => ({
|
|
20652
|
+
subject: subjectName({ kind: "asset", asset: a.asset }, a.units),
|
|
20653
|
+
limit: assetClause(a),
|
|
20654
|
+
watchIds: a.watchIds
|
|
20655
|
+
}))
|
|
20656
|
+
} : {},
|
|
20650
20657
|
members: i.members,
|
|
20658
|
+
...i.alsoChecks ? { alsoChecks: i.alsoChecks } : {},
|
|
20651
20659
|
status: subjectStatus(i.rows),
|
|
20652
20660
|
mutedUntil: i.rows.map((w) => mutedUntilOf(w)).find(Boolean) ?? null,
|
|
20653
20661
|
rules: i.rows.map((w) => ({
|
|
@@ -20857,7 +20865,7 @@ function buildTools(client) {
|
|
|
20857
20865
|
run: async (a) => a.confirm === true ? client.testsFromRepo(a.planId) : paidRun("Run the Clarinet suite from this pre-launch plan's repo in the airgapped sandbox.")
|
|
20858
20866
|
},
|
|
20859
20867
|
list_watches: {
|
|
20860
|
-
description: "What a plan watches, as intent watches (Money out per token, Control changes, one per finding, the fns you added): on or off, the threshold or summary, the fns covered, each rule in token units, any mute, and the subject name the other watch tools take.",
|
|
20868
|
+
description: "What a plan watches, as intent watches (Money out once per contract with a limit per token, Control changes, one per finding, the fns you added): on or off, the threshold or summary, the fns covered, what it can see (coverage: direct-calls means a call made from another contract isn't visible yet), each rule in token units, any mute, and the subject name the other watch tools take.",
|
|
20861
20869
|
input: { planId },
|
|
20862
20870
|
run: async (a) => {
|
|
20863
20871
|
const ctx = await watchContext(client, a.planId);
|
|
@@ -21041,7 +21049,7 @@ async function runTool(tool, args) {
|
|
|
21041
21049
|
}
|
|
21042
21050
|
}
|
|
21043
21051
|
function buildServer(client) {
|
|
21044
|
-
const server = new McpServer({ name: "sentinel", version: "0.
|
|
21052
|
+
const server = new McpServer({ name: "sentinel", version: "0.3.0" });
|
|
21045
21053
|
for (const [name, tool] of Object.entries(buildTools(client))) {
|
|
21046
21054
|
server.registerTool(name, { description: tool.description, inputSchema: tool.input }, (args) => runTool(tool, args));
|
|
21047
21055
|
}
|
|
@@ -170,8 +170,29 @@ export type StoredWatch = {
|
|
|
170
170
|
units: Units | null;
|
|
171
171
|
};
|
|
172
172
|
export type IntentKind = "money_out" | "control" | "finding" | "owner_fn";
|
|
173
|
+
/** A Money out limit: null amount means every outflow alerts until one is set. */
|
|
174
|
+
export type Threshold = {
|
|
175
|
+
amount: string | null;
|
|
176
|
+
basis: string;
|
|
177
|
+
confirmed: boolean;
|
|
178
|
+
suggested: {
|
|
179
|
+
amount: string;
|
|
180
|
+
basis: string;
|
|
181
|
+
} | null;
|
|
182
|
+
};
|
|
183
|
+
/** One asset inside Money out: its own limit, the rows behind it and its last 7 UTC days (oldest first). */
|
|
184
|
+
export type AssetWatch = {
|
|
185
|
+
asset: string;
|
|
186
|
+
units: Units | null;
|
|
187
|
+
threshold: Threshold;
|
|
188
|
+
watchIds: string[];
|
|
189
|
+
stats7d: {
|
|
190
|
+
events: number[];
|
|
191
|
+
alerts: number[];
|
|
192
|
+
};
|
|
193
|
+
};
|
|
173
194
|
/**
|
|
174
|
-
* One intent watch: what a team sees (Money out
|
|
195
|
+
* One intent watch: what a team sees (Money out holding every asset, Control changes, one per finding, a fn the team
|
|
175
196
|
* added). `rows` are the stored watches behind it: what a rule edit, a switch or a mute acts on.
|
|
176
197
|
*/
|
|
177
198
|
export type IntentWatch<R = StoredWatch & {
|
|
@@ -184,28 +205,26 @@ export type IntentWatch<R = StoredWatch & {
|
|
|
184
205
|
summary: string;
|
|
185
206
|
on: boolean;
|
|
186
207
|
alertLevel: "urgent" | "normal";
|
|
208
|
+
/** What it can see: `direct-calls` (a call another contract makes isn't visible yet) or `all-calls`. */
|
|
209
|
+
coverage: "direct-calls" | "all-calls";
|
|
187
210
|
watchIds: string[];
|
|
188
211
|
rows: R[];
|
|
189
212
|
members: {
|
|
190
213
|
group: string;
|
|
191
214
|
fns: string[];
|
|
192
215
|
}[];
|
|
193
|
-
/** Money out: the
|
|
194
|
-
|
|
195
|
-
units?: Units | null;
|
|
196
|
-
threshold?: {
|
|
197
|
-
amount: string | null;
|
|
198
|
-
basis: string;
|
|
199
|
-
confirmed: boolean;
|
|
200
|
-
suggested: {
|
|
201
|
-
amount: string;
|
|
202
|
-
basis: string;
|
|
203
|
-
} | null;
|
|
204
|
-
};
|
|
216
|
+
/** Money out: every asset the contract sends, STX first then by symbol, each with its own limit. */
|
|
217
|
+
assets?: AssetWatch[];
|
|
205
218
|
findingRef?: {
|
|
206
219
|
title: string;
|
|
207
220
|
index: number | null;
|
|
208
221
|
};
|
|
222
|
+
/** Control changes: the state only its members write, read every `everyMinutes` between calls. */
|
|
223
|
+
alsoChecks?: {
|
|
224
|
+
vars: string[];
|
|
225
|
+
maps: string[];
|
|
226
|
+
everyMinutes: number;
|
|
227
|
+
};
|
|
209
228
|
};
|
|
210
229
|
/** The stored rows behind a list of intent watches, in order. */
|
|
211
230
|
export declare const rowsOf: <R>(intents: {
|
|
@@ -305,8 +324,10 @@ export declare function ruleSummary(rule: WatchRule, units: Units | null): strin
|
|
|
305
324
|
export type WatchKindTag = "finding" | "outflow" | "who-acts" | "governance" | "upgrade";
|
|
306
325
|
/** The plain tag a rule is listed under. */
|
|
307
326
|
export declare function kindTag(rule: WatchRule): WatchKindTag;
|
|
308
|
-
/**
|
|
309
|
-
export declare function
|
|
327
|
+
/** One Money out clause: `STX over 500` or `MIA every outflow`, in token units where they are known. */
|
|
328
|
+
export declare function assetClause(a: Pick<AssetWatch, "asset" | "units" | "threshold">): string;
|
|
329
|
+
/** An intent watch's one line: a Money out clause per asset, else its summary. */
|
|
330
|
+
export declare function intentLine(i: Pick<IntentWatch<unknown>, "kind" | "summary" | "assets">): string;
|
|
310
331
|
/** A subject's status from its rules: live if any is, else paused if all are, else suggested. */
|
|
311
332
|
export declare function subjectStatus(ws: {
|
|
312
333
|
status: WatchStatus;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@secondlayer/sentinel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Sentinel from a terminal or an agent: the CLI, an MCP server and a client for your Stacks contract monitoring.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "https://runsentinel.app/docs",
|