@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 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
- const t = i.threshold;
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
- const t = i.threshold;
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
- const t = i.threshold;
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.2.1" });
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 per asset, Control changes, one per finding, a fn the team
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 asset, its token units and its threshold (null amount: every outflow alerts). */
196
- asset?: string;
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
- /** An intent watch's one line: a Money out threshold in token units (or its fail-safe), else its summary. */
317
- export declare function intentLine(i: Pick<IntentWatch<unknown>, "kind" | "summary" | "threshold" | "units">): string;
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.2.1",
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",