@secondlayer/sentinel 0.2.1 → 0.4.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 +17 -11
- package/dist/sentinel-client.d.ts +33 -17
- 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);
|
|
@@ -20648,6 +20647,13 @@ function intentView(i) {
|
|
|
20648
20647
|
alertLevel: i.alertLevel,
|
|
20649
20648
|
coverage: i.coverage,
|
|
20650
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
|
+
} : {},
|
|
20651
20657
|
members: i.members,
|
|
20652
20658
|
...i.alsoChecks ? { alsoChecks: i.alsoChecks } : {},
|
|
20653
20659
|
status: subjectStatus(i.rows),
|
|
@@ -20859,7 +20865,7 @@ function buildTools(client) {
|
|
|
20859
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.")
|
|
20860
20866
|
},
|
|
20861
20867
|
list_watches: {
|
|
20862
|
-
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, 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.",
|
|
20868
|
+
description: "What a plan watches, as intent watches (Money out once per contract with a limit per token, Control changes, Admin operations, Settings, 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.",
|
|
20863
20869
|
input: { planId },
|
|
20864
20870
|
run: async (a) => {
|
|
20865
20871
|
const ctx = await watchContext(client, a.planId);
|
|
@@ -21043,7 +21049,7 @@ async function runTool(tool, args) {
|
|
|
21043
21049
|
}
|
|
21044
21050
|
}
|
|
21045
21051
|
function buildServer(client) {
|
|
21046
|
-
const server = new McpServer({ name: "sentinel", version: "0.
|
|
21052
|
+
const server = new McpServer({ name: "sentinel", version: "0.4.0" });
|
|
21047
21053
|
for (const [name, tool] of Object.entries(buildTools(client))) {
|
|
21048
21054
|
server.registerTool(name, { description: tool.description, inputSchema: tool.input }, (args) => runTool(tool, args));
|
|
21049
21055
|
}
|
|
@@ -107,6 +107,7 @@ export type WatchRule = {
|
|
|
107
107
|
} | {
|
|
108
108
|
kind: "outflowSingle";
|
|
109
109
|
amount: string | null;
|
|
110
|
+
internal?: string[];
|
|
110
111
|
} | {
|
|
111
112
|
kind: "audit";
|
|
112
113
|
triggerClass: string;
|
|
@@ -114,6 +115,7 @@ export type WatchRule = {
|
|
|
114
115
|
kind: "outflowWindow";
|
|
115
116
|
amount: string;
|
|
116
117
|
blocks: number;
|
|
118
|
+
internal?: string[];
|
|
117
119
|
} | {
|
|
118
120
|
kind: "actor";
|
|
119
121
|
signals: ("newCaller" | "firstCall" | "roleChange")[];
|
|
@@ -121,6 +123,7 @@ export type WatchRule = {
|
|
|
121
123
|
} | {
|
|
122
124
|
kind: "valueNewActor";
|
|
123
125
|
amount: string;
|
|
126
|
+
internal?: string[];
|
|
124
127
|
};
|
|
125
128
|
/** The rule kinds a caller can add or edit (the others come from a verified finding or governance). */
|
|
126
129
|
export type EditableRule = Exclude<WatchRule, {
|
|
@@ -169,10 +172,31 @@ export type StoredWatch = {
|
|
|
169
172
|
updatedAt: string;
|
|
170
173
|
units: Units | null;
|
|
171
174
|
};
|
|
172
|
-
export type IntentKind = "money_out" | "control" | "finding" | "owner_fn";
|
|
175
|
+
export type IntentKind = "money_out" | "control" | "admin_op" | "setting" | "finding" | "owner_fn";
|
|
176
|
+
/** A Money out limit: null amount means every outflow alerts until one is set. */
|
|
177
|
+
export type Threshold = {
|
|
178
|
+
amount: string | null;
|
|
179
|
+
basis: string;
|
|
180
|
+
confirmed: boolean;
|
|
181
|
+
suggested: {
|
|
182
|
+
amount: string;
|
|
183
|
+
basis: string;
|
|
184
|
+
} | null;
|
|
185
|
+
};
|
|
186
|
+
/** One asset inside Money out: its own limit, the rows behind it and its last 7 UTC days (oldest first). */
|
|
187
|
+
export type AssetWatch = {
|
|
188
|
+
asset: string;
|
|
189
|
+
units: Units | null;
|
|
190
|
+
threshold: Threshold;
|
|
191
|
+
watchIds: string[];
|
|
192
|
+
stats7d: {
|
|
193
|
+
events: number[];
|
|
194
|
+
alerts: number[];
|
|
195
|
+
};
|
|
196
|
+
};
|
|
173
197
|
/**
|
|
174
|
-
* One intent watch: what a team sees (Money out
|
|
175
|
-
* added). `rows` are the stored watches behind it: what a rule edit, a switch or a mute acts on.
|
|
198
|
+
* One intent watch: what a team sees (Money out holding every asset, Control changes, Admin operations, Settings, one per
|
|
199
|
+
* finding, a fn the team added). `rows` are the stored watches behind it: what a rule edit, a switch or a mute acts on.
|
|
176
200
|
*/
|
|
177
201
|
export type IntentWatch<R = StoredWatch & {
|
|
178
202
|
on: boolean;
|
|
@@ -192,18 +216,8 @@ export type IntentWatch<R = StoredWatch & {
|
|
|
192
216
|
group: string;
|
|
193
217
|
fns: string[];
|
|
194
218
|
}[];
|
|
195
|
-
/** Money out: the
|
|
196
|
-
|
|
197
|
-
units?: Units | null;
|
|
198
|
-
threshold?: {
|
|
199
|
-
amount: string | null;
|
|
200
|
-
basis: string;
|
|
201
|
-
confirmed: boolean;
|
|
202
|
-
suggested: {
|
|
203
|
-
amount: string;
|
|
204
|
-
basis: string;
|
|
205
|
-
} | null;
|
|
206
|
-
};
|
|
219
|
+
/** Money out: every asset the contract sends, STX first then by symbol, each with its own limit. */
|
|
220
|
+
assets?: AssetWatch[];
|
|
207
221
|
findingRef?: {
|
|
208
222
|
title: string;
|
|
209
223
|
index: number | null;
|
|
@@ -313,8 +327,10 @@ export declare function ruleSummary(rule: WatchRule, units: Units | null): strin
|
|
|
313
327
|
export type WatchKindTag = "finding" | "outflow" | "who-acts" | "governance" | "upgrade";
|
|
314
328
|
/** The plain tag a rule is listed under. */
|
|
315
329
|
export declare function kindTag(rule: WatchRule): WatchKindTag;
|
|
316
|
-
/**
|
|
317
|
-
export declare function
|
|
330
|
+
/** One Money out clause: `STX over 500` or `MIA every outflow`, in token units where they are known. */
|
|
331
|
+
export declare function assetClause(a: Pick<AssetWatch, "asset" | "units" | "threshold">): string;
|
|
332
|
+
/** An intent watch's one line: a Money out clause per asset, else its summary. */
|
|
333
|
+
export declare function intentLine(i: Pick<IntentWatch<unknown>, "kind" | "summary" | "assets">): string;
|
|
318
334
|
/** A subject's status from its rules: live if any is, else paused if all are, else suggested. */
|
|
319
335
|
export declare function subjectStatus(ws: {
|
|
320
336
|
status: WatchStatus;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@secondlayer/sentinel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|