@x402sentinel/x402 0.1.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.
@@ -0,0 +1,178 @@
1
+ 'use strict';
2
+
3
+ var chunk7H4FRU7K_cjs = require('./chunk-7H4FRU7K.cjs');
4
+
5
+ // src/dashboard/queries.ts
6
+ async function spendByAgent(storage, agentId, range) {
7
+ const { start, end } = chunk7H4FRU7K_cjs.resolveTimeRange(range);
8
+ const records = await storage.query({ agentId, startTime: start, endTime: end });
9
+ return aggregateSpend(records);
10
+ }
11
+ async function spendByTeam(storage, team, range) {
12
+ const { start, end } = chunk7H4FRU7K_cjs.resolveTimeRange(range);
13
+ const records = await storage.query({ team, startTime: start, endTime: end });
14
+ return aggregateSpend(records);
15
+ }
16
+ async function spendByEndpoint(storage, pattern, range) {
17
+ const { start, end } = chunk7H4FRU7K_cjs.resolveTimeRange(range);
18
+ const records = await storage.query({ endpoint: pattern, startTime: start, endTime: end });
19
+ return aggregateSpend(records);
20
+ }
21
+ async function topSpenders(storage, limit, range) {
22
+ const { start, end } = chunk7H4FRU7K_cjs.resolveTimeRange(range);
23
+ const records = await storage.query({ startTime: start, endTime: end });
24
+ const byAgent = /* @__PURE__ */ new Map();
25
+ for (const r of records) {
26
+ const entry = byAgent.get(r.agent_id) ?? { raw: 0n, count: 0 };
27
+ entry.raw += chunk7H4FRU7K_cjs.parseUSDC(r.amount);
28
+ entry.count++;
29
+ byAgent.set(r.agent_id, entry);
30
+ }
31
+ return [...byAgent.entries()].map(([agentId, data]) => ({
32
+ agentId,
33
+ spend: chunk7H4FRU7K_cjs.formatUSDCHuman(data.raw),
34
+ count: data.count
35
+ })).sort((a, b) => {
36
+ const aRaw = chunk7H4FRU7K_cjs.parseUSDC(a.spend);
37
+ const bRaw = chunk7H4FRU7K_cjs.parseUSDC(b.spend);
38
+ return aRaw > bRaw ? -1 : aRaw < bRaw ? 1 : 0;
39
+ }).slice(0, limit);
40
+ }
41
+ async function violations(storage, range) {
42
+ const { start, end } = chunk7H4FRU7K_cjs.resolveTimeRange(range);
43
+ return storage.query({ status: ["blocked"], startTime: start, endTime: end });
44
+ }
45
+ async function anomalies(storage, range) {
46
+ const { start, end } = chunk7H4FRU7K_cjs.resolveTimeRange(range);
47
+ return storage.query({ status: ["flagged"], startTime: start, endTime: end });
48
+ }
49
+ function aggregateSpend(records) {
50
+ let totalRaw = 0n;
51
+ for (const r of records) {
52
+ totalRaw += chunk7H4FRU7K_cjs.parseUSDC(r.amount);
53
+ }
54
+ return {
55
+ spend: chunk7H4FRU7K_cjs.formatUSDCHuman(totalRaw),
56
+ count: records.length,
57
+ records
58
+ };
59
+ }
60
+
61
+ // src/dashboard/index.ts
62
+ var SentinelDashboard = class {
63
+ storage;
64
+ /** @internal Reserved for future remote API integration */
65
+ apiKey;
66
+ /** @internal Reserved for future remote API integration */
67
+ baseUrl;
68
+ constructor(config) {
69
+ this.storage = config.storage;
70
+ this.apiKey = config.apiKey;
71
+ this.baseUrl = config.baseUrl ?? "https://api.valeo.money/v1";
72
+ }
73
+ /** Query spend data with flexible filters */
74
+ async getSpend(query) {
75
+ const records = await this.getFilteredRecords(query);
76
+ let totalRaw = 0n;
77
+ const byAgent = {};
78
+ const byEndpoint = {};
79
+ for (const r of records) {
80
+ const raw = chunk7H4FRU7K_cjs.parseUSDC(r.amount);
81
+ totalRaw += raw;
82
+ const agentEntry = byAgent[r.agent_id] ?? { spend: "0.00", count: 0 };
83
+ agentEntry.spend = chunk7H4FRU7K_cjs.formatUSDCHuman(chunk7H4FRU7K_cjs.parseUSDC(agentEntry.spend) + raw);
84
+ agentEntry.count++;
85
+ byAgent[r.agent_id] = agentEntry;
86
+ const epEntry = byEndpoint[r.endpoint] ?? { spend: "0.00", count: 0 };
87
+ epEntry.spend = chunk7H4FRU7K_cjs.formatUSDCHuman(chunk7H4FRU7K_cjs.parseUSDC(epEntry.spend) + raw);
88
+ epEntry.count++;
89
+ byEndpoint[r.endpoint] = epEntry;
90
+ }
91
+ return {
92
+ totalSpend: chunk7H4FRU7K_cjs.formatUSDCHuman(totalRaw),
93
+ count: records.length,
94
+ byAgent,
95
+ byEndpoint
96
+ };
97
+ }
98
+ /** Get summary info for all known agents */
99
+ async getAgents() {
100
+ const allRecords = await this.storage.query({});
101
+ const agentMap = /* @__PURE__ */ new Map();
102
+ for (const r of allRecords) {
103
+ const entry = agentMap.get(r.agent_id) ?? {
104
+ team: r.team,
105
+ raw: 0n,
106
+ count: 0,
107
+ lastActive: 0
108
+ };
109
+ entry.raw += chunk7H4FRU7K_cjs.parseUSDC(r.amount);
110
+ entry.count++;
111
+ if (r.created_at > entry.lastActive) entry.lastActive = r.created_at;
112
+ agentMap.set(r.agent_id, entry);
113
+ }
114
+ return [...agentMap.entries()].map(([agentId, data]) => ({
115
+ agentId,
116
+ team: data.team,
117
+ totalSpend: chunk7H4FRU7K_cjs.formatUSDCHuman(data.raw),
118
+ transactionCount: data.count,
119
+ lastActive: data.lastActive
120
+ }));
121
+ }
122
+ /** Get all violations and anomalies as alerts */
123
+ async getAlerts() {
124
+ const allRecords = await this.storage.query({});
125
+ const alerts = [];
126
+ for (const r of allRecords) {
127
+ if (r.policy_evaluation === "blocked") {
128
+ alerts.push({
129
+ type: "violation",
130
+ record: r,
131
+ message: `Budget violation by ${r.agent_id} on ${r.endpoint}: $${r.amount}`
132
+ });
133
+ } else if (r.policy_evaluation === "flagged") {
134
+ alerts.push({
135
+ type: "anomaly",
136
+ record: r,
137
+ message: `Anomaly detected for ${r.agent_id} on ${r.endpoint}: $${r.amount}`
138
+ });
139
+ }
140
+ }
141
+ return alerts;
142
+ }
143
+ /** Stub: Sync local records to remote API (future api.valeo.money) */
144
+ async sync() {
145
+ console.warn("[sentinel] Dashboard sync is not yet implemented \u2014 records remain local only");
146
+ return { synced: 0 };
147
+ }
148
+ /** Stub: Export audit data as PDF via remote API */
149
+ async exportPDF(_query) {
150
+ throw new Error("PDF export requires the Valeo hosted dashboard (api.valeo.money) \u2014 coming soon");
151
+ }
152
+ async getFilteredRecords(query) {
153
+ if (query.agentId) {
154
+ const result2 = await spendByAgent(this.storage, query.agentId, query.range);
155
+ return result2.records;
156
+ }
157
+ if (query.team) {
158
+ const result2 = await spendByTeam(this.storage, query.team, query.range);
159
+ return result2.records;
160
+ }
161
+ if (query.endpoint) {
162
+ const result2 = await spendByEndpoint(this.storage, query.endpoint, query.range);
163
+ return result2.records;
164
+ }
165
+ const result = await spendByEndpoint(this.storage, "", query.range);
166
+ return result.records;
167
+ }
168
+ };
169
+
170
+ exports.SentinelDashboard = SentinelDashboard;
171
+ exports.anomalies = anomalies;
172
+ exports.spendByAgent = spendByAgent;
173
+ exports.spendByEndpoint = spendByEndpoint;
174
+ exports.spendByTeam = spendByTeam;
175
+ exports.topSpenders = topSpenders;
176
+ exports.violations = violations;
177
+ //# sourceMappingURL=dashboard.cjs.map
178
+ //# sourceMappingURL=dashboard.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dashboard/queries.ts","../src/dashboard/index.ts"],"names":["resolveTimeRange","parseUSDC","formatUSDCHuman","result"],"mappings":"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","file":"dashboard.cjs","sourcesContent":["import type { AuditRecord } from \"../types/audit\";\nimport type { StorageBackend } from \"../audit/storage/interface\";\nimport { resolveTimeRange } from \"../utils/time\";\nimport { parseUSDC, formatUSDCHuman } from \"../utils/money\";\n\nexport type TimeRange =\n | \"last_hour\"\n | \"last_day\"\n | \"last_week\"\n | \"last_month\"\n | { start: number; end: number };\n\nexport interface SpendResult {\n spend: string;\n count: number;\n records: AuditRecord[];\n}\n\n/** Query spend for a specific agent within a time range */\nexport async function spendByAgent(\n storage: StorageBackend,\n agentId: string,\n range: TimeRange,\n): Promise<SpendResult> {\n const { start, end } = resolveTimeRange(range);\n const records = await storage.query({ agentId, startTime: start, endTime: end });\n return aggregateSpend(records);\n}\n\n/** Query spend for a team within a time range */\nexport async function spendByTeam(\n storage: StorageBackend,\n team: string,\n range: TimeRange,\n): Promise<SpendResult> {\n const { start, end } = resolveTimeRange(range);\n const records = await storage.query({ team, startTime: start, endTime: end });\n return aggregateSpend(records);\n}\n\n/** Query spend for an endpoint pattern within a time range */\nexport async function spendByEndpoint(\n storage: StorageBackend,\n pattern: string,\n range: TimeRange,\n): Promise<SpendResult> {\n const { start, end } = resolveTimeRange(range);\n const records = await storage.query({ endpoint: pattern, startTime: start, endTime: end });\n return aggregateSpend(records);\n}\n\n/** Get top spending agents within a time range */\nexport async function topSpenders(\n storage: StorageBackend,\n limit: number,\n range: TimeRange,\n): Promise<Array<{ agentId: string; spend: string; count: number }>> {\n const { start, end } = resolveTimeRange(range);\n const records = await storage.query({ startTime: start, endTime: end });\n\n const byAgent = new Map<string, { raw: bigint; count: number }>();\n for (const r of records) {\n const entry = byAgent.get(r.agent_id) ?? { raw: 0n, count: 0 };\n entry.raw += parseUSDC(r.amount);\n entry.count++;\n byAgent.set(r.agent_id, entry);\n }\n\n return [...byAgent.entries()]\n .map(([agentId, data]) => ({\n agentId,\n spend: formatUSDCHuman(data.raw),\n count: data.count,\n }))\n .sort((a, b) => {\n const aRaw = parseUSDC(a.spend);\n const bRaw = parseUSDC(b.spend);\n return aRaw > bRaw ? -1 : aRaw < bRaw ? 1 : 0;\n })\n .slice(0, limit);\n}\n\n/** Get all blocked (violation) records within a time range */\nexport async function violations(\n storage: StorageBackend,\n range: TimeRange,\n): Promise<AuditRecord[]> {\n const { start, end } = resolveTimeRange(range);\n return storage.query({ status: [\"blocked\"], startTime: start, endTime: end });\n}\n\n/** Get all flagged (anomaly) records within a time range */\nexport async function anomalies(\n storage: StorageBackend,\n range: TimeRange,\n): Promise<AuditRecord[]> {\n const { start, end } = resolveTimeRange(range);\n return storage.query({ status: [\"flagged\"], startTime: start, endTime: end });\n}\n\nfunction aggregateSpend(records: AuditRecord[]): SpendResult {\n let totalRaw = 0n;\n for (const r of records) {\n totalRaw += parseUSDC(r.amount);\n }\n return {\n spend: formatUSDCHuman(totalRaw),\n count: records.length,\n records,\n };\n}\n","import type { AuditRecord, AuditQuery } from \"../types/audit\";\nimport type { StorageBackend } from \"../audit/storage/interface\";\nimport { spendByAgent, spendByTeam, spendByEndpoint } from \"./queries\";\nimport type { TimeRange } from \"./queries\";\nimport { parseUSDC, formatUSDCHuman } from \"../utils/money\";\n\n/** Query parameters for dashboard spend queries */\nexport interface SpendQuery {\n agentId?: string;\n team?: string;\n endpoint?: string;\n range: TimeRange;\n}\n\n/** Aggregated spend report */\nexport interface SpendReport {\n totalSpend: string;\n count: number;\n byAgent: Record<string, { spend: string; count: number }>;\n byEndpoint: Record<string, { spend: string; count: number }>;\n}\n\n/** Summary for a single agent */\nexport interface AgentSummary {\n agentId: string;\n team: string | null;\n totalSpend: string;\n transactionCount: number;\n lastActive: number;\n}\n\n/** Alert from violations or anomaly detection */\nexport interface Alert {\n type: \"violation\" | \"anomaly\";\n record: AuditRecord;\n message: string;\n}\n\nexport interface DashboardConfig {\n storage: StorageBackend;\n apiKey?: string;\n baseUrl?: string;\n}\n\n/**\n * Dashboard client for querying Sentinel audit data.\n * Runs queries locally against the configured StorageBackend.\n * Remote sync/export are stubs for future api.valeo.money integration.\n */\nexport class SentinelDashboard {\n private readonly storage: StorageBackend;\n /** @internal Reserved for future remote API integration */\n readonly apiKey?: string;\n /** @internal Reserved for future remote API integration */\n readonly baseUrl: string;\n\n constructor(config: DashboardConfig) {\n this.storage = config.storage;\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? \"https://api.valeo.money/v1\";\n }\n\n /** Query spend data with flexible filters */\n async getSpend(query: SpendQuery): Promise<SpendReport> {\n const records = await this.getFilteredRecords(query);\n\n let totalRaw = 0n;\n const byAgent: Record<string, { spend: string; count: number }> = {};\n const byEndpoint: Record<string, { spend: string; count: number }> = {};\n\n for (const r of records) {\n const raw = parseUSDC(r.amount);\n totalRaw += raw;\n\n const agentEntry = byAgent[r.agent_id] ?? { spend: \"0.00\", count: 0 };\n agentEntry.spend = formatUSDCHuman(parseUSDC(agentEntry.spend) + raw);\n agentEntry.count++;\n byAgent[r.agent_id] = agentEntry;\n\n const epEntry = byEndpoint[r.endpoint] ?? { spend: \"0.00\", count: 0 };\n epEntry.spend = formatUSDCHuman(parseUSDC(epEntry.spend) + raw);\n epEntry.count++;\n byEndpoint[r.endpoint] = epEntry;\n }\n\n return {\n totalSpend: formatUSDCHuman(totalRaw),\n count: records.length,\n byAgent,\n byEndpoint,\n };\n }\n\n /** Get summary info for all known agents */\n async getAgents(): Promise<AgentSummary[]> {\n const allRecords = await this.storage.query({});\n const agentMap = new Map<\n string,\n { team: string | null; raw: bigint; count: number; lastActive: number }\n >();\n\n for (const r of allRecords) {\n const entry = agentMap.get(r.agent_id) ?? {\n team: r.team,\n raw: 0n,\n count: 0,\n lastActive: 0,\n };\n entry.raw += parseUSDC(r.amount);\n entry.count++;\n if (r.created_at > entry.lastActive) entry.lastActive = r.created_at;\n agentMap.set(r.agent_id, entry);\n }\n\n return [...agentMap.entries()].map(([agentId, data]) => ({\n agentId,\n team: data.team,\n totalSpend: formatUSDCHuman(data.raw),\n transactionCount: data.count,\n lastActive: data.lastActive,\n }));\n }\n\n /** Get all violations and anomalies as alerts */\n async getAlerts(): Promise<Alert[]> {\n const allRecords = await this.storage.query({});\n const alerts: Alert[] = [];\n\n for (const r of allRecords) {\n if (r.policy_evaluation === \"blocked\") {\n alerts.push({\n type: \"violation\",\n record: r,\n message: `Budget violation by ${r.agent_id} on ${r.endpoint}: $${r.amount}`,\n });\n } else if (r.policy_evaluation === \"flagged\") {\n alerts.push({\n type: \"anomaly\",\n record: r,\n message: `Anomaly detected for ${r.agent_id} on ${r.endpoint}: $${r.amount}`,\n });\n }\n }\n\n return alerts;\n }\n\n /** Stub: Sync local records to remote API (future api.valeo.money) */\n async sync(): Promise<{ synced: number }> {\n // Future implementation: POST local records to remote API\n console.warn(\"[sentinel] Dashboard sync is not yet implemented — records remain local only\");\n return { synced: 0 };\n }\n\n /** Stub: Export audit data as PDF via remote API */\n async exportPDF(_query: AuditQuery): Promise<Buffer> {\n throw new Error(\"PDF export requires the Valeo hosted dashboard (api.valeo.money) — coming soon\");\n }\n\n private async getFilteredRecords(query: SpendQuery): Promise<AuditRecord[]> {\n if (query.agentId) {\n const result = await spendByAgent(this.storage, query.agentId, query.range);\n return result.records;\n }\n if (query.team) {\n const result = await spendByTeam(this.storage, query.team, query.range);\n return result.records;\n }\n if (query.endpoint) {\n const result = await spendByEndpoint(this.storage, query.endpoint, query.range);\n return result.records;\n }\n // No filter — get all in time range\n const result = await spendByEndpoint(this.storage, \"\", query.range);\n return result.records;\n }\n}\n\nexport {\n spendByAgent,\n spendByTeam,\n spendByEndpoint,\n topSpenders,\n violations,\n anomalies,\n type TimeRange,\n type SpendResult,\n} from \"./queries\";\n"]}
@@ -0,0 +1,95 @@
1
+ import { A as AuditRecord, S as StorageBackend, a as AuditQuery } from './interface-CNi4rtm1.cjs';
2
+
3
+ type TimeRange = "last_hour" | "last_day" | "last_week" | "last_month" | {
4
+ start: number;
5
+ end: number;
6
+ };
7
+ interface SpendResult {
8
+ spend: string;
9
+ count: number;
10
+ records: AuditRecord[];
11
+ }
12
+ /** Query spend for a specific agent within a time range */
13
+ declare function spendByAgent(storage: StorageBackend, agentId: string, range: TimeRange): Promise<SpendResult>;
14
+ /** Query spend for a team within a time range */
15
+ declare function spendByTeam(storage: StorageBackend, team: string, range: TimeRange): Promise<SpendResult>;
16
+ /** Query spend for an endpoint pattern within a time range */
17
+ declare function spendByEndpoint(storage: StorageBackend, pattern: string, range: TimeRange): Promise<SpendResult>;
18
+ /** Get top spending agents within a time range */
19
+ declare function topSpenders(storage: StorageBackend, limit: number, range: TimeRange): Promise<Array<{
20
+ agentId: string;
21
+ spend: string;
22
+ count: number;
23
+ }>>;
24
+ /** Get all blocked (violation) records within a time range */
25
+ declare function violations(storage: StorageBackend, range: TimeRange): Promise<AuditRecord[]>;
26
+ /** Get all flagged (anomaly) records within a time range */
27
+ declare function anomalies(storage: StorageBackend, range: TimeRange): Promise<AuditRecord[]>;
28
+
29
+ /** Query parameters for dashboard spend queries */
30
+ interface SpendQuery {
31
+ agentId?: string;
32
+ team?: string;
33
+ endpoint?: string;
34
+ range: TimeRange;
35
+ }
36
+ /** Aggregated spend report */
37
+ interface SpendReport {
38
+ totalSpend: string;
39
+ count: number;
40
+ byAgent: Record<string, {
41
+ spend: string;
42
+ count: number;
43
+ }>;
44
+ byEndpoint: Record<string, {
45
+ spend: string;
46
+ count: number;
47
+ }>;
48
+ }
49
+ /** Summary for a single agent */
50
+ interface AgentSummary {
51
+ agentId: string;
52
+ team: string | null;
53
+ totalSpend: string;
54
+ transactionCount: number;
55
+ lastActive: number;
56
+ }
57
+ /** Alert from violations or anomaly detection */
58
+ interface Alert {
59
+ type: "violation" | "anomaly";
60
+ record: AuditRecord;
61
+ message: string;
62
+ }
63
+ interface DashboardConfig {
64
+ storage: StorageBackend;
65
+ apiKey?: string;
66
+ baseUrl?: string;
67
+ }
68
+ /**
69
+ * Dashboard client for querying Sentinel audit data.
70
+ * Runs queries locally against the configured StorageBackend.
71
+ * Remote sync/export are stubs for future api.valeo.money integration.
72
+ */
73
+ declare class SentinelDashboard {
74
+ private readonly storage;
75
+ /** @internal Reserved for future remote API integration */
76
+ readonly apiKey?: string;
77
+ /** @internal Reserved for future remote API integration */
78
+ readonly baseUrl: string;
79
+ constructor(config: DashboardConfig);
80
+ /** Query spend data with flexible filters */
81
+ getSpend(query: SpendQuery): Promise<SpendReport>;
82
+ /** Get summary info for all known agents */
83
+ getAgents(): Promise<AgentSummary[]>;
84
+ /** Get all violations and anomalies as alerts */
85
+ getAlerts(): Promise<Alert[]>;
86
+ /** Stub: Sync local records to remote API (future api.valeo.money) */
87
+ sync(): Promise<{
88
+ synced: number;
89
+ }>;
90
+ /** Stub: Export audit data as PDF via remote API */
91
+ exportPDF(_query: AuditQuery): Promise<Buffer>;
92
+ private getFilteredRecords;
93
+ }
94
+
95
+ export { type AgentSummary, type Alert, type DashboardConfig, SentinelDashboard, type SpendQuery, type SpendReport, type SpendResult, type TimeRange, anomalies, spendByAgent, spendByEndpoint, spendByTeam, topSpenders, violations };
@@ -0,0 +1,95 @@
1
+ import { A as AuditRecord, S as StorageBackend, a as AuditQuery } from './interface-CNi4rtm1.js';
2
+
3
+ type TimeRange = "last_hour" | "last_day" | "last_week" | "last_month" | {
4
+ start: number;
5
+ end: number;
6
+ };
7
+ interface SpendResult {
8
+ spend: string;
9
+ count: number;
10
+ records: AuditRecord[];
11
+ }
12
+ /** Query spend for a specific agent within a time range */
13
+ declare function spendByAgent(storage: StorageBackend, agentId: string, range: TimeRange): Promise<SpendResult>;
14
+ /** Query spend for a team within a time range */
15
+ declare function spendByTeam(storage: StorageBackend, team: string, range: TimeRange): Promise<SpendResult>;
16
+ /** Query spend for an endpoint pattern within a time range */
17
+ declare function spendByEndpoint(storage: StorageBackend, pattern: string, range: TimeRange): Promise<SpendResult>;
18
+ /** Get top spending agents within a time range */
19
+ declare function topSpenders(storage: StorageBackend, limit: number, range: TimeRange): Promise<Array<{
20
+ agentId: string;
21
+ spend: string;
22
+ count: number;
23
+ }>>;
24
+ /** Get all blocked (violation) records within a time range */
25
+ declare function violations(storage: StorageBackend, range: TimeRange): Promise<AuditRecord[]>;
26
+ /** Get all flagged (anomaly) records within a time range */
27
+ declare function anomalies(storage: StorageBackend, range: TimeRange): Promise<AuditRecord[]>;
28
+
29
+ /** Query parameters for dashboard spend queries */
30
+ interface SpendQuery {
31
+ agentId?: string;
32
+ team?: string;
33
+ endpoint?: string;
34
+ range: TimeRange;
35
+ }
36
+ /** Aggregated spend report */
37
+ interface SpendReport {
38
+ totalSpend: string;
39
+ count: number;
40
+ byAgent: Record<string, {
41
+ spend: string;
42
+ count: number;
43
+ }>;
44
+ byEndpoint: Record<string, {
45
+ spend: string;
46
+ count: number;
47
+ }>;
48
+ }
49
+ /** Summary for a single agent */
50
+ interface AgentSummary {
51
+ agentId: string;
52
+ team: string | null;
53
+ totalSpend: string;
54
+ transactionCount: number;
55
+ lastActive: number;
56
+ }
57
+ /** Alert from violations or anomaly detection */
58
+ interface Alert {
59
+ type: "violation" | "anomaly";
60
+ record: AuditRecord;
61
+ message: string;
62
+ }
63
+ interface DashboardConfig {
64
+ storage: StorageBackend;
65
+ apiKey?: string;
66
+ baseUrl?: string;
67
+ }
68
+ /**
69
+ * Dashboard client for querying Sentinel audit data.
70
+ * Runs queries locally against the configured StorageBackend.
71
+ * Remote sync/export are stubs for future api.valeo.money integration.
72
+ */
73
+ declare class SentinelDashboard {
74
+ private readonly storage;
75
+ /** @internal Reserved for future remote API integration */
76
+ readonly apiKey?: string;
77
+ /** @internal Reserved for future remote API integration */
78
+ readonly baseUrl: string;
79
+ constructor(config: DashboardConfig);
80
+ /** Query spend data with flexible filters */
81
+ getSpend(query: SpendQuery): Promise<SpendReport>;
82
+ /** Get summary info for all known agents */
83
+ getAgents(): Promise<AgentSummary[]>;
84
+ /** Get all violations and anomalies as alerts */
85
+ getAlerts(): Promise<Alert[]>;
86
+ /** Stub: Sync local records to remote API (future api.valeo.money) */
87
+ sync(): Promise<{
88
+ synced: number;
89
+ }>;
90
+ /** Stub: Export audit data as PDF via remote API */
91
+ exportPDF(_query: AuditQuery): Promise<Buffer>;
92
+ private getFilteredRecords;
93
+ }
94
+
95
+ export { type AgentSummary, type Alert, type DashboardConfig, SentinelDashboard, type SpendQuery, type SpendReport, type SpendResult, type TimeRange, anomalies, spendByAgent, spendByEndpoint, spendByTeam, topSpenders, violations };
@@ -0,0 +1,170 @@
1
+ import { resolveTimeRange, parseUSDC, formatUSDCHuman } from './chunk-25PZCEL2.js';
2
+
3
+ // src/dashboard/queries.ts
4
+ async function spendByAgent(storage, agentId, range) {
5
+ const { start, end } = resolveTimeRange(range);
6
+ const records = await storage.query({ agentId, startTime: start, endTime: end });
7
+ return aggregateSpend(records);
8
+ }
9
+ async function spendByTeam(storage, team, range) {
10
+ const { start, end } = resolveTimeRange(range);
11
+ const records = await storage.query({ team, startTime: start, endTime: end });
12
+ return aggregateSpend(records);
13
+ }
14
+ async function spendByEndpoint(storage, pattern, range) {
15
+ const { start, end } = resolveTimeRange(range);
16
+ const records = await storage.query({ endpoint: pattern, startTime: start, endTime: end });
17
+ return aggregateSpend(records);
18
+ }
19
+ async function topSpenders(storage, limit, range) {
20
+ const { start, end } = resolveTimeRange(range);
21
+ const records = await storage.query({ startTime: start, endTime: end });
22
+ const byAgent = /* @__PURE__ */ new Map();
23
+ for (const r of records) {
24
+ const entry = byAgent.get(r.agent_id) ?? { raw: 0n, count: 0 };
25
+ entry.raw += parseUSDC(r.amount);
26
+ entry.count++;
27
+ byAgent.set(r.agent_id, entry);
28
+ }
29
+ return [...byAgent.entries()].map(([agentId, data]) => ({
30
+ agentId,
31
+ spend: formatUSDCHuman(data.raw),
32
+ count: data.count
33
+ })).sort((a, b) => {
34
+ const aRaw = parseUSDC(a.spend);
35
+ const bRaw = parseUSDC(b.spend);
36
+ return aRaw > bRaw ? -1 : aRaw < bRaw ? 1 : 0;
37
+ }).slice(0, limit);
38
+ }
39
+ async function violations(storage, range) {
40
+ const { start, end } = resolveTimeRange(range);
41
+ return storage.query({ status: ["blocked"], startTime: start, endTime: end });
42
+ }
43
+ async function anomalies(storage, range) {
44
+ const { start, end } = resolveTimeRange(range);
45
+ return storage.query({ status: ["flagged"], startTime: start, endTime: end });
46
+ }
47
+ function aggregateSpend(records) {
48
+ let totalRaw = 0n;
49
+ for (const r of records) {
50
+ totalRaw += parseUSDC(r.amount);
51
+ }
52
+ return {
53
+ spend: formatUSDCHuman(totalRaw),
54
+ count: records.length,
55
+ records
56
+ };
57
+ }
58
+
59
+ // src/dashboard/index.ts
60
+ var SentinelDashboard = class {
61
+ storage;
62
+ /** @internal Reserved for future remote API integration */
63
+ apiKey;
64
+ /** @internal Reserved for future remote API integration */
65
+ baseUrl;
66
+ constructor(config) {
67
+ this.storage = config.storage;
68
+ this.apiKey = config.apiKey;
69
+ this.baseUrl = config.baseUrl ?? "https://api.valeo.money/v1";
70
+ }
71
+ /** Query spend data with flexible filters */
72
+ async getSpend(query) {
73
+ const records = await this.getFilteredRecords(query);
74
+ let totalRaw = 0n;
75
+ const byAgent = {};
76
+ const byEndpoint = {};
77
+ for (const r of records) {
78
+ const raw = parseUSDC(r.amount);
79
+ totalRaw += raw;
80
+ const agentEntry = byAgent[r.agent_id] ?? { spend: "0.00", count: 0 };
81
+ agentEntry.spend = formatUSDCHuman(parseUSDC(agentEntry.spend) + raw);
82
+ agentEntry.count++;
83
+ byAgent[r.agent_id] = agentEntry;
84
+ const epEntry = byEndpoint[r.endpoint] ?? { spend: "0.00", count: 0 };
85
+ epEntry.spend = formatUSDCHuman(parseUSDC(epEntry.spend) + raw);
86
+ epEntry.count++;
87
+ byEndpoint[r.endpoint] = epEntry;
88
+ }
89
+ return {
90
+ totalSpend: formatUSDCHuman(totalRaw),
91
+ count: records.length,
92
+ byAgent,
93
+ byEndpoint
94
+ };
95
+ }
96
+ /** Get summary info for all known agents */
97
+ async getAgents() {
98
+ const allRecords = await this.storage.query({});
99
+ const agentMap = /* @__PURE__ */ new Map();
100
+ for (const r of allRecords) {
101
+ const entry = agentMap.get(r.agent_id) ?? {
102
+ team: r.team,
103
+ raw: 0n,
104
+ count: 0,
105
+ lastActive: 0
106
+ };
107
+ entry.raw += parseUSDC(r.amount);
108
+ entry.count++;
109
+ if (r.created_at > entry.lastActive) entry.lastActive = r.created_at;
110
+ agentMap.set(r.agent_id, entry);
111
+ }
112
+ return [...agentMap.entries()].map(([agentId, data]) => ({
113
+ agentId,
114
+ team: data.team,
115
+ totalSpend: formatUSDCHuman(data.raw),
116
+ transactionCount: data.count,
117
+ lastActive: data.lastActive
118
+ }));
119
+ }
120
+ /** Get all violations and anomalies as alerts */
121
+ async getAlerts() {
122
+ const allRecords = await this.storage.query({});
123
+ const alerts = [];
124
+ for (const r of allRecords) {
125
+ if (r.policy_evaluation === "blocked") {
126
+ alerts.push({
127
+ type: "violation",
128
+ record: r,
129
+ message: `Budget violation by ${r.agent_id} on ${r.endpoint}: $${r.amount}`
130
+ });
131
+ } else if (r.policy_evaluation === "flagged") {
132
+ alerts.push({
133
+ type: "anomaly",
134
+ record: r,
135
+ message: `Anomaly detected for ${r.agent_id} on ${r.endpoint}: $${r.amount}`
136
+ });
137
+ }
138
+ }
139
+ return alerts;
140
+ }
141
+ /** Stub: Sync local records to remote API (future api.valeo.money) */
142
+ async sync() {
143
+ console.warn("[sentinel] Dashboard sync is not yet implemented \u2014 records remain local only");
144
+ return { synced: 0 };
145
+ }
146
+ /** Stub: Export audit data as PDF via remote API */
147
+ async exportPDF(_query) {
148
+ throw new Error("PDF export requires the Valeo hosted dashboard (api.valeo.money) \u2014 coming soon");
149
+ }
150
+ async getFilteredRecords(query) {
151
+ if (query.agentId) {
152
+ const result2 = await spendByAgent(this.storage, query.agentId, query.range);
153
+ return result2.records;
154
+ }
155
+ if (query.team) {
156
+ const result2 = await spendByTeam(this.storage, query.team, query.range);
157
+ return result2.records;
158
+ }
159
+ if (query.endpoint) {
160
+ const result2 = await spendByEndpoint(this.storage, query.endpoint, query.range);
161
+ return result2.records;
162
+ }
163
+ const result = await spendByEndpoint(this.storage, "", query.range);
164
+ return result.records;
165
+ }
166
+ };
167
+
168
+ export { SentinelDashboard, anomalies, spendByAgent, spendByEndpoint, spendByTeam, topSpenders, violations };
169
+ //# sourceMappingURL=dashboard.js.map
170
+ //# sourceMappingURL=dashboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dashboard/queries.ts","../src/dashboard/index.ts"],"names":["result"],"mappings":"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","file":"dashboard.js","sourcesContent":["import type { AuditRecord } from \"../types/audit\";\nimport type { StorageBackend } from \"../audit/storage/interface\";\nimport { resolveTimeRange } from \"../utils/time\";\nimport { parseUSDC, formatUSDCHuman } from \"../utils/money\";\n\nexport type TimeRange =\n | \"last_hour\"\n | \"last_day\"\n | \"last_week\"\n | \"last_month\"\n | { start: number; end: number };\n\nexport interface SpendResult {\n spend: string;\n count: number;\n records: AuditRecord[];\n}\n\n/** Query spend for a specific agent within a time range */\nexport async function spendByAgent(\n storage: StorageBackend,\n agentId: string,\n range: TimeRange,\n): Promise<SpendResult> {\n const { start, end } = resolveTimeRange(range);\n const records = await storage.query({ agentId, startTime: start, endTime: end });\n return aggregateSpend(records);\n}\n\n/** Query spend for a team within a time range */\nexport async function spendByTeam(\n storage: StorageBackend,\n team: string,\n range: TimeRange,\n): Promise<SpendResult> {\n const { start, end } = resolveTimeRange(range);\n const records = await storage.query({ team, startTime: start, endTime: end });\n return aggregateSpend(records);\n}\n\n/** Query spend for an endpoint pattern within a time range */\nexport async function spendByEndpoint(\n storage: StorageBackend,\n pattern: string,\n range: TimeRange,\n): Promise<SpendResult> {\n const { start, end } = resolveTimeRange(range);\n const records = await storage.query({ endpoint: pattern, startTime: start, endTime: end });\n return aggregateSpend(records);\n}\n\n/** Get top spending agents within a time range */\nexport async function topSpenders(\n storage: StorageBackend,\n limit: number,\n range: TimeRange,\n): Promise<Array<{ agentId: string; spend: string; count: number }>> {\n const { start, end } = resolveTimeRange(range);\n const records = await storage.query({ startTime: start, endTime: end });\n\n const byAgent = new Map<string, { raw: bigint; count: number }>();\n for (const r of records) {\n const entry = byAgent.get(r.agent_id) ?? { raw: 0n, count: 0 };\n entry.raw += parseUSDC(r.amount);\n entry.count++;\n byAgent.set(r.agent_id, entry);\n }\n\n return [...byAgent.entries()]\n .map(([agentId, data]) => ({\n agentId,\n spend: formatUSDCHuman(data.raw),\n count: data.count,\n }))\n .sort((a, b) => {\n const aRaw = parseUSDC(a.spend);\n const bRaw = parseUSDC(b.spend);\n return aRaw > bRaw ? -1 : aRaw < bRaw ? 1 : 0;\n })\n .slice(0, limit);\n}\n\n/** Get all blocked (violation) records within a time range */\nexport async function violations(\n storage: StorageBackend,\n range: TimeRange,\n): Promise<AuditRecord[]> {\n const { start, end } = resolveTimeRange(range);\n return storage.query({ status: [\"blocked\"], startTime: start, endTime: end });\n}\n\n/** Get all flagged (anomaly) records within a time range */\nexport async function anomalies(\n storage: StorageBackend,\n range: TimeRange,\n): Promise<AuditRecord[]> {\n const { start, end } = resolveTimeRange(range);\n return storage.query({ status: [\"flagged\"], startTime: start, endTime: end });\n}\n\nfunction aggregateSpend(records: AuditRecord[]): SpendResult {\n let totalRaw = 0n;\n for (const r of records) {\n totalRaw += parseUSDC(r.amount);\n }\n return {\n spend: formatUSDCHuman(totalRaw),\n count: records.length,\n records,\n };\n}\n","import type { AuditRecord, AuditQuery } from \"../types/audit\";\nimport type { StorageBackend } from \"../audit/storage/interface\";\nimport { spendByAgent, spendByTeam, spendByEndpoint } from \"./queries\";\nimport type { TimeRange } from \"./queries\";\nimport { parseUSDC, formatUSDCHuman } from \"../utils/money\";\n\n/** Query parameters for dashboard spend queries */\nexport interface SpendQuery {\n agentId?: string;\n team?: string;\n endpoint?: string;\n range: TimeRange;\n}\n\n/** Aggregated spend report */\nexport interface SpendReport {\n totalSpend: string;\n count: number;\n byAgent: Record<string, { spend: string; count: number }>;\n byEndpoint: Record<string, { spend: string; count: number }>;\n}\n\n/** Summary for a single agent */\nexport interface AgentSummary {\n agentId: string;\n team: string | null;\n totalSpend: string;\n transactionCount: number;\n lastActive: number;\n}\n\n/** Alert from violations or anomaly detection */\nexport interface Alert {\n type: \"violation\" | \"anomaly\";\n record: AuditRecord;\n message: string;\n}\n\nexport interface DashboardConfig {\n storage: StorageBackend;\n apiKey?: string;\n baseUrl?: string;\n}\n\n/**\n * Dashboard client for querying Sentinel audit data.\n * Runs queries locally against the configured StorageBackend.\n * Remote sync/export are stubs for future api.valeo.money integration.\n */\nexport class SentinelDashboard {\n private readonly storage: StorageBackend;\n /** @internal Reserved for future remote API integration */\n readonly apiKey?: string;\n /** @internal Reserved for future remote API integration */\n readonly baseUrl: string;\n\n constructor(config: DashboardConfig) {\n this.storage = config.storage;\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? \"https://api.valeo.money/v1\";\n }\n\n /** Query spend data with flexible filters */\n async getSpend(query: SpendQuery): Promise<SpendReport> {\n const records = await this.getFilteredRecords(query);\n\n let totalRaw = 0n;\n const byAgent: Record<string, { spend: string; count: number }> = {};\n const byEndpoint: Record<string, { spend: string; count: number }> = {};\n\n for (const r of records) {\n const raw = parseUSDC(r.amount);\n totalRaw += raw;\n\n const agentEntry = byAgent[r.agent_id] ?? { spend: \"0.00\", count: 0 };\n agentEntry.spend = formatUSDCHuman(parseUSDC(agentEntry.spend) + raw);\n agentEntry.count++;\n byAgent[r.agent_id] = agentEntry;\n\n const epEntry = byEndpoint[r.endpoint] ?? { spend: \"0.00\", count: 0 };\n epEntry.spend = formatUSDCHuman(parseUSDC(epEntry.spend) + raw);\n epEntry.count++;\n byEndpoint[r.endpoint] = epEntry;\n }\n\n return {\n totalSpend: formatUSDCHuman(totalRaw),\n count: records.length,\n byAgent,\n byEndpoint,\n };\n }\n\n /** Get summary info for all known agents */\n async getAgents(): Promise<AgentSummary[]> {\n const allRecords = await this.storage.query({});\n const agentMap = new Map<\n string,\n { team: string | null; raw: bigint; count: number; lastActive: number }\n >();\n\n for (const r of allRecords) {\n const entry = agentMap.get(r.agent_id) ?? {\n team: r.team,\n raw: 0n,\n count: 0,\n lastActive: 0,\n };\n entry.raw += parseUSDC(r.amount);\n entry.count++;\n if (r.created_at > entry.lastActive) entry.lastActive = r.created_at;\n agentMap.set(r.agent_id, entry);\n }\n\n return [...agentMap.entries()].map(([agentId, data]) => ({\n agentId,\n team: data.team,\n totalSpend: formatUSDCHuman(data.raw),\n transactionCount: data.count,\n lastActive: data.lastActive,\n }));\n }\n\n /** Get all violations and anomalies as alerts */\n async getAlerts(): Promise<Alert[]> {\n const allRecords = await this.storage.query({});\n const alerts: Alert[] = [];\n\n for (const r of allRecords) {\n if (r.policy_evaluation === \"blocked\") {\n alerts.push({\n type: \"violation\",\n record: r,\n message: `Budget violation by ${r.agent_id} on ${r.endpoint}: $${r.amount}`,\n });\n } else if (r.policy_evaluation === \"flagged\") {\n alerts.push({\n type: \"anomaly\",\n record: r,\n message: `Anomaly detected for ${r.agent_id} on ${r.endpoint}: $${r.amount}`,\n });\n }\n }\n\n return alerts;\n }\n\n /** Stub: Sync local records to remote API (future api.valeo.money) */\n async sync(): Promise<{ synced: number }> {\n // Future implementation: POST local records to remote API\n console.warn(\"[sentinel] Dashboard sync is not yet implemented — records remain local only\");\n return { synced: 0 };\n }\n\n /** Stub: Export audit data as PDF via remote API */\n async exportPDF(_query: AuditQuery): Promise<Buffer> {\n throw new Error(\"PDF export requires the Valeo hosted dashboard (api.valeo.money) — coming soon\");\n }\n\n private async getFilteredRecords(query: SpendQuery): Promise<AuditRecord[]> {\n if (query.agentId) {\n const result = await spendByAgent(this.storage, query.agentId, query.range);\n return result.records;\n }\n if (query.team) {\n const result = await spendByTeam(this.storage, query.team, query.range);\n return result.records;\n }\n if (query.endpoint) {\n const result = await spendByEndpoint(this.storage, query.endpoint, query.range);\n return result.records;\n }\n // No filter — get all in time range\n const result = await spendByEndpoint(this.storage, \"\", query.range);\n return result.records;\n }\n}\n\nexport {\n spendByAgent,\n spendByTeam,\n spendByEndpoint,\n topSpenders,\n violations,\n anomalies,\n type TimeRange,\n type SpendResult,\n} from \"./queries\";\n"]}