beecork 1.4.10 → 1.4.11
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.
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
1
2
|
export interface CostSummary {
|
|
2
3
|
today: number;
|
|
3
4
|
last7Days: number;
|
|
@@ -19,6 +20,11 @@ export interface ActivitySummary {
|
|
|
19
20
|
}
|
|
20
21
|
export declare function getCostSummary(): CostSummary;
|
|
21
22
|
export declare function getActivitySummary(hours?: number): ActivitySummary;
|
|
23
|
+
/**
|
|
24
|
+
* Returns a notification only on state transitions (ok↔breach), persisting
|
|
25
|
+
* the state in the `preferences` table so daemon restarts don't re-fire.
|
|
26
|
+
*/
|
|
27
|
+
export declare function checkAnomaliesWithDb(db: Database.Database): string | null;
|
|
22
28
|
export declare function checkAnomalies(): string | null;
|
|
23
29
|
export declare function formatCostSummary(summary: CostSummary): string;
|
|
24
30
|
export declare function formatActivitySummary(summary: ActivitySummary): string;
|
|
@@ -30,8 +30,12 @@ export function getActivitySummary(hours = 24) {
|
|
|
30
30
|
activeTabsCount,
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const ANOMALY_STATE_KEY = 'anomaly_spend_state';
|
|
34
|
+
/**
|
|
35
|
+
* Returns a notification only on state transitions (ok↔breach), persisting
|
|
36
|
+
* the state in the `preferences` table so daemon restarts don't re-fire.
|
|
37
|
+
*/
|
|
38
|
+
export function checkAnomaliesWithDb(db) {
|
|
35
39
|
// Today's spend
|
|
36
40
|
const todaySpend = db.prepare("SELECT COALESCE(SUM(cost_usd), 0) as total FROM messages WHERE created_at > date('now')").get().total;
|
|
37
41
|
// 7-day rolling average (excluding today)
|
|
@@ -43,10 +47,21 @@ export function checkAnomalies() {
|
|
|
43
47
|
GROUP BY date(created_at)
|
|
44
48
|
)
|
|
45
49
|
`).get().avg;
|
|
46
|
-
|
|
50
|
+
const isBreach = avgSpend > 0 && todaySpend > avgSpend * 2;
|
|
51
|
+
const newState = isBreach ? 'breach' : 'ok';
|
|
52
|
+
const prevRow = db.prepare('SELECT value FROM preferences WHERE key = ?').get(ANOMALY_STATE_KEY);
|
|
53
|
+
const prevState = prevRow?.value ?? 'ok';
|
|
54
|
+
if (newState === prevState)
|
|
55
|
+
return null;
|
|
56
|
+
db.prepare(`INSERT INTO preferences (key, value, updated_at) VALUES (?, ?, datetime('now'))
|
|
57
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = datetime('now')`).run(ANOMALY_STATE_KEY, newState);
|
|
58
|
+
if (isBreach) {
|
|
47
59
|
return `⚠️ Anomaly: Today's spend ($${todaySpend.toFixed(4)}) exceeds 2x your 7-day average ($${avgSpend.toFixed(4)}/day)`;
|
|
48
60
|
}
|
|
49
|
-
return
|
|
61
|
+
return `✅ Recovered: Today's spend ($${todaySpend.toFixed(4)}) is back within 2x your 7-day average ($${avgSpend.toFixed(4)}/day)`;
|
|
62
|
+
}
|
|
63
|
+
export function checkAnomalies() {
|
|
64
|
+
return checkAnomaliesWithDb(getDb());
|
|
50
65
|
}
|
|
51
66
|
export function formatCostSummary(summary) {
|
|
52
67
|
const lines = [
|