@hasna/economy 0.2.10 → 0.2.12

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,5 +1,6 @@
1
1
  import { SqliteAdapter as Database } from '@hasna/cloud';
2
2
  import type { EconomyRequest, EconomySession, EconomyProject, Budget, BudgetStatus, CostSummary, ModelBreakdown, ProjectBreakdown, Period, SessionFilter } from '../types/index.js';
3
+ export declare function getMachineId(): string;
3
4
  export declare function getDataDir(): string;
4
5
  export declare function getDbPath(): string;
5
6
  export declare function openDatabase(dbPath?: string, skipSeed?: boolean): Database;
@@ -8,7 +9,7 @@ export declare function upsertSession(db: Database, session: EconomySession): vo
8
9
  export declare function rollupSession(db: Database, sessionId: string): void;
9
10
  export declare function querySessions(db: Database, filter?: SessionFilter): EconomySession[];
10
11
  export declare function queryTopSessions(db: Database, n?: number, agent?: string): EconomySession[];
11
- export declare function querySummary(db: Database, period: Period): CostSummary;
12
+ export declare function querySummary(db: Database, period: Period, machine?: string): CostSummary;
12
13
  export declare function queryModelBreakdown(db: Database): ModelBreakdown[];
13
14
  export declare function queryProjectBreakdown(db: Database): ProjectBreakdown[];
14
15
  export declare function queryDailyBreakdown(db: Database, days?: number): Array<{
@@ -47,6 +48,14 @@ export declare function getGoalStatuses(db: Database): GoalStatus[];
47
48
  export declare function getIngestState(db: Database, source: string, key: string): string | null;
48
49
  export declare function setIngestState(db: Database, source: string, key: string, value: string): void;
49
50
  export declare function queryRequestsSince(db: Database, since: string): EconomyRequest[];
51
+ export interface MachineInfo {
52
+ machine_id: string;
53
+ sessions: number;
54
+ requests: number;
55
+ total_cost_usd: number;
56
+ last_active: string;
57
+ }
58
+ export declare function listMachines(db: Database): MachineInfo[];
50
59
  export interface DbModelPricing {
51
60
  model: string;
52
61
  input_per_1m: number;
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pg-migrations.d.ts","sourceRoot":"","sources":["../../src/db/pg-migrations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,aAAa,EAAE,MAAM,EAmGjC,CAAC"}
1
+ {"version":3,"file":"pg-migrations.d.ts","sourceRoot":"","sources":["../../src/db/pg-migrations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,aAAa,EAAE,MAAM,EAuGjC,CAAC"}
package/dist/index.js CHANGED
@@ -107,8 +107,17 @@ var init_pricing = __esm(() => {
107
107
  // src/db/database.ts
108
108
  import { SqliteAdapter as Database } from "@hasna/cloud";
109
109
  import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
110
+ import { hostname } from "os";
110
111
  import { homedir } from "os";
111
112
  import { join } from "path";
113
+ function getMachineId() {
114
+ if (process.env["ECONOMY_MACHINE_ID"])
115
+ return process.env["ECONOMY_MACHINE_ID"];
116
+ const h = hostname().toLowerCase();
117
+ if (h.startsWith("spark") || h.startsWith("apple"))
118
+ return h.split(".")[0];
119
+ return h.split(".")[0];
120
+ }
112
121
  function getDataDir() {
113
122
  const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
114
123
  const newDir = join(home, ".hasna", "economy");
@@ -141,6 +150,7 @@ function openDatabase(dbPath, skipSeed = false) {
141
150
  }
142
151
  const db = new Database(path);
143
152
  db.exec("PRAGMA journal_mode = WAL");
153
+ db.exec("PRAGMA busy_timeout = 5000");
144
154
  db.exec("PRAGMA foreign_keys = ON");
145
155
  initSchema(db);
146
156
  if (!skipSeed) {
@@ -162,7 +172,8 @@ function initSchema(db) {
162
172
  cost_usd REAL NOT NULL DEFAULT 0,
163
173
  duration_ms INTEGER DEFAULT 0,
164
174
  timestamp TEXT NOT NULL,
165
- source_request_id TEXT
175
+ source_request_id TEXT,
176
+ machine_id TEXT DEFAULT ''
166
177
  );
167
178
 
168
179
  CREATE TABLE IF NOT EXISTS sessions (
@@ -174,7 +185,8 @@ function initSchema(db) {
174
185
  ended_at TEXT,
175
186
  total_cost_usd REAL DEFAULT 0,
176
187
  total_tokens INTEGER DEFAULT 0,
177
- request_count INTEGER DEFAULT 0
188
+ request_count INTEGER DEFAULT 0,
189
+ machine_id TEXT DEFAULT ''
178
190
  );
179
191
 
180
192
  CREATE TABLE IF NOT EXISTS projects (
@@ -240,6 +252,15 @@ function initSchema(db) {
240
252
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
241
253
  );
242
254
  `);
255
+ const cols = db.prepare(`PRAGMA table_info(requests)`).all();
256
+ if (!cols.some((c) => c.name === "machine_id")) {
257
+ db.exec(`ALTER TABLE requests ADD COLUMN machine_id TEXT DEFAULT ''`);
258
+ db.exec(`ALTER TABLE sessions ADD COLUMN machine_id TEXT DEFAULT ''`);
259
+ }
260
+ db.exec(`
261
+ CREATE INDEX IF NOT EXISTS idx_requests_machine ON requests(machine_id);
262
+ CREATE INDEX IF NOT EXISTS idx_sessions_machine ON sessions(machine_id);
263
+ `);
243
264
  }
244
265
  function periodWhere(period) {
245
266
  switch (period) {
@@ -278,17 +299,17 @@ function upsertRequest(db, req) {
278
299
  INSERT OR REPLACE INTO requests
279
300
  (id, agent, session_id, model, input_tokens, output_tokens,
280
301
  cache_read_tokens, cache_create_tokens, cost_usd, duration_ms,
281
- timestamp, source_request_id)
282
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
283
- `).run(req.id, req.agent, req.session_id, req.model, req.input_tokens, req.output_tokens, req.cache_read_tokens, req.cache_create_tokens, req.cost_usd, req.duration_ms, req.timestamp, req.source_request_id);
302
+ timestamp, source_request_id, machine_id)
303
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
304
+ `).run(req.id, req.agent, req.session_id, req.model, req.input_tokens, req.output_tokens, req.cache_read_tokens, req.cache_create_tokens, req.cost_usd, req.duration_ms, req.timestamp, req.source_request_id, req.machine_id ?? "");
284
305
  }
285
306
  function upsertSession(db, session) {
286
307
  db.prepare(`
287
308
  INSERT OR REPLACE INTO sessions
288
309
  (id, agent, project_path, project_name, started_at, ended_at,
289
- total_cost_usd, total_tokens, request_count)
290
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
291
- `).run(session.id, session.agent, session.project_path, session.project_name, session.started_at, session.ended_at ?? null, session.total_cost_usd, session.total_tokens, session.request_count);
310
+ total_cost_usd, total_tokens, request_count, machine_id)
311
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
312
+ `).run(session.id, session.agent, session.project_path, session.project_name, session.started_at, session.ended_at ?? null, session.total_cost_usd, session.total_tokens, session.request_count, session.machine_id ?? "");
292
313
  }
293
314
  function rollupSession(db, sessionId) {
294
315
  db.prepare(`
@@ -318,6 +339,10 @@ function querySessions(db, filter = {}) {
318
339
  conditions.push("started_at >= ?");
319
340
  params.push(filter.since);
320
341
  }
342
+ if (filter.machine) {
343
+ conditions.push("machine_id = ?");
344
+ params.push(filter.machine);
345
+ }
321
346
  if (filter.search) {
322
347
  const q = `%${filter.search}%`;
323
348
  conditions.push("(project_name LIKE ? OR agent LIKE ? OR id LIKE ?)");
@@ -336,24 +361,25 @@ function queryTopSessions(db, n = 10, agent) {
336
361
  }
337
362
  return db.prepare(`SELECT * FROM sessions ORDER BY total_cost_usd DESC LIMIT ?`).all(n);
338
363
  }
339
- function querySummary(db, period) {
364
+ function querySummary(db, period, machine) {
340
365
  const rWhere = periodWhere(period);
341
366
  const sWhere = sessionPeriodWhere(period);
367
+ const machineClause = machine ? ` AND machine_id = '${machine.replace(/'/g, "''")}'` : "";
342
368
  const r = db.prepare(`
343
369
  SELECT COALESCE(SUM(cost_usd), 0) as total_usd,
344
370
  COUNT(*) as requests,
345
371
  COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as tokens
346
- FROM requests WHERE ${rWhere}
372
+ FROM requests WHERE ${rWhere}${machineClause}
347
373
  `).get();
348
374
  const codexTotals = db.prepare(`
349
375
  SELECT COALESCE(SUM(total_cost_usd), 0) as cost_usd,
350
376
  COALESCE(SUM(total_tokens), 0) as tokens,
351
377
  COUNT(*) as sessions
352
378
  FROM sessions
353
- WHERE ${sWhere}
379
+ WHERE ${sWhere}${machineClause}
354
380
  AND id NOT IN (SELECT DISTINCT session_id FROM requests)
355
381
  `).get();
356
- const sessionCount = db.prepare(`SELECT COUNT(*) as sessions FROM sessions WHERE ${sWhere}`).get();
382
+ const sessionCount = db.prepare(`SELECT COUNT(*) as sessions FROM sessions WHERE ${sWhere}${machineClause}`).get();
357
383
  return {
358
384
  total_usd: r.total_usd + codexTotals.cost_usd,
359
385
  requests: r.requests,
@@ -507,6 +533,20 @@ function setIngestState(db, source, key, value) {
507
533
  function queryRequestsSince(db, since) {
508
534
  return db.prepare(`SELECT * FROM requests WHERE timestamp > ? ORDER BY timestamp ASC`).all(since);
509
535
  }
536
+ function listMachines(db) {
537
+ return db.prepare(`
538
+ SELECT
539
+ s.machine_id,
540
+ COUNT(DISTINCT s.id) as sessions,
541
+ COALESCE((SELECT COUNT(*) FROM requests r WHERE r.machine_id = s.machine_id), 0) as requests,
542
+ COALESCE(SUM(s.total_cost_usd), 0) as total_cost_usd,
543
+ MAX(s.started_at) as last_active
544
+ FROM sessions s
545
+ WHERE s.machine_id != ''
546
+ GROUP BY s.machine_id
547
+ ORDER BY total_cost_usd DESC
548
+ `).all();
549
+ }
510
550
  function upsertModelPricing(db, p) {
511
551
  db.prepare(`
512
552
  INSERT OR REPLACE INTO model_pricing
@@ -794,6 +834,7 @@ async function ingestClaude(db, verbose = false, _telemetryDir) {
794
834
  console.log("Claude projects dir not found:", PROJECTS_DIR);
795
835
  return { files: 0, requests: 0, sessions: 0 };
796
836
  }
837
+ const machineId = getMachineId();
797
838
  let totalFiles = 0;
798
839
  let totalRequests = 0;
799
840
  const touchedSessions = new Set;
@@ -865,7 +906,8 @@ async function ingestClaude(db, verbose = false, _telemetryDir) {
865
906
  cost_usd: costUsd,
866
907
  duration_ms: 0,
867
908
  timestamp,
868
- source_request_id: reqId
909
+ source_request_id: reqId,
910
+ machine_id: machineId
869
911
  });
870
912
  if (!touchedSessions.has(sessionId)) {
871
913
  const existing = db.prepare(`SELECT id FROM sessions WHERE id = ?`).get(sessionId);
@@ -881,7 +923,8 @@ async function ingestClaude(db, verbose = false, _telemetryDir) {
881
923
  ended_at: null,
882
924
  total_cost_usd: 0,
883
925
  total_tokens: 0,
884
- request_count: 0
926
+ request_count: 0,
927
+ machine_id: machineId
885
928
  };
886
929
  upsertSession(db, session);
887
930
  }
@@ -903,7 +946,7 @@ init_database();
903
946
  import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
904
947
  import { homedir as homedir3 } from "os";
905
948
  import { join as join4, basename as basename2 } from "path";
906
- import { Database as Database2 } from "bun:sqlite";
949
+ import { Database as BunDatabase } from "bun:sqlite";
907
950
  var CODEX_DB_PATH = join4(homedir3(), ".codex", "state_5.sqlite");
908
951
  var CODEX_CONFIG_PATH = join4(homedir3(), ".codex", "config.toml");
909
952
  function readCodexModel() {
@@ -923,10 +966,11 @@ async function ingestCodex(db, verbose = false) {
923
966
  console.log("Codex DB not found:", CODEX_DB_PATH);
924
967
  return { sessions: 0 };
925
968
  }
969
+ const machineId = getMachineId();
926
970
  let codexDb = null;
927
971
  let ingested = 0;
928
972
  try {
929
- codexDb = new Database2(CODEX_DB_PATH, { readonly: true });
973
+ codexDb = new BunDatabase(CODEX_DB_PATH, { readonly: true });
930
974
  const threads = codexDb.prepare(`SELECT id, cwd, created_at, updated_at, tokens_used, title FROM threads WHERE tokens_used > 0`).all();
931
975
  for (const thread of threads) {
932
976
  const stateKey = thread.id;
@@ -947,7 +991,8 @@ async function ingestCodex(db, verbose = false) {
947
991
  ended_at: endedAt,
948
992
  total_cost_usd: costUsd,
949
993
  total_tokens: thread.tokens_used,
950
- request_count: 1
994
+ request_count: 1,
995
+ machine_id: machineId
951
996
  });
952
997
  setIngestState(db, "codex", stateKey, "done");
953
998
  ingested++;
@@ -982,6 +1027,7 @@ export {
982
1027
  normalizeModelName,
983
1028
  listProjects,
984
1029
  listModelPricing,
1030
+ listMachines,
985
1031
  listGoals,
986
1032
  listBudgets,
987
1033
  ingestCodex,
@@ -990,6 +1036,7 @@ export {
990
1036
  getPricingFromDb,
991
1037
  getPricing,
992
1038
  getModelPricing,
1039
+ getMachineId,
993
1040
  getIngestState,
994
1041
  getGoalStatuses,
995
1042
  getDbPath,
@@ -1,4 +1,4 @@
1
- import { Database } from 'bun:sqlite';
1
+ import type { SqliteAdapter as Database } from '@hasna/cloud';
2
2
  export declare function ingestClaude(db: Database, verbose?: boolean, _telemetryDir?: string): Promise<{
3
3
  files: number;
4
4
  requests: number;
@@ -1 +1 @@
1
- {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/ingest/claude.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AA2DrC,wBAAsB,YAAY,CAChC,EAAE,EAAE,QAAQ,EACZ,OAAO,UAAQ,EACf,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA2HhE"}
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/ingest/claude.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AA2D7D,wBAAsB,YAAY,CAChC,EAAE,EAAE,QAAQ,EACZ,OAAO,UAAQ,EACf,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA8HhE"}
@@ -1,4 +1,4 @@
1
- import { Database } from 'bun:sqlite';
1
+ import type { SqliteAdapter as Database } from '@hasna/cloud';
2
2
  declare function readCodexModel(): string;
3
3
  export declare function ingestCodex(db: Database, verbose?: boolean): Promise<{
4
4
  sessions: number;
@@ -1 +1 @@
1
- {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/ingest/codex.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAkBrC,iBAAS,cAAc,IAAI,MAAM,CAShC;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAyD9F;AAED,OAAO,EAAE,cAAc,EAAE,CAAA"}
1
+ {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/ingest/codex.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAkB7D,iBAAS,cAAc,IAAI,MAAM,CAShC;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA2D9F;AAED,OAAO,EAAE,cAAc,EAAE,CAAA"}
@@ -1,4 +1,4 @@
1
- import { Database } from 'bun:sqlite';
1
+ import type { SqliteAdapter as Database } from '@hasna/cloud';
2
2
  export declare function ingestGemini(db: Database, verbose?: boolean): Promise<{
3
3
  sessions: number;
4
4
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/ingest/gemini.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AA0BrC,wBAAsB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA2EjG"}
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/ingest/gemini.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AA0B7D,wBAAsB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA6EjG"}
@@ -0,0 +1,8 @@
1
+ type PackageMetadata = {
2
+ name: string;
3
+ version: string;
4
+ };
5
+ export declare function getPackageMetadata(): PackageMetadata;
6
+ export declare const packageMetadata: PackageMetadata;
7
+ export {};
8
+ //# sourceMappingURL=package-metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-metadata.d.ts","sourceRoot":"","sources":["../../src/lib/package-metadata.ts"],"names":[],"mappings":"AAEA,KAAK,eAAe,GAAG;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAID,wBAAgB,kBAAkB,IAAI,eAAe,CAYpD;AAED,eAAO,MAAM,eAAe,iBAAuB,CAAA"}
@@ -1,4 +1,4 @@
1
- import type { Database } from 'bun:sqlite';
1
+ import type { SqliteAdapter as Database } from '@hasna/cloud';
2
2
  import type { ModelPricing } from '../types/index.js';
3
3
  export declare const DEFAULT_PRICING: Record<string, ModelPricing>;
4
4
  export declare function normalizeModelName(raw: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/lib/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAKrD,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CA6BxD,CAAA;AAGD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKtD;AAGD,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAEtD;AAGD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAuBjF;AAGD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAO7D;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,eAAe,SAAI,EACnB,gBAAgB,SAAI,GACnB,MAAM,CASR;AAED,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,eAAe,SAAI,EACnB,gBAAgB,SAAI,GACnB,MAAM,CASR"}
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/lib/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAKrD,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CA6BxD,CAAA;AAGD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKtD;AAGD,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAEtD;AAGD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAuBjF;AAGD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAO7D;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,eAAe,SAAI,EACnB,gBAAgB,SAAI,GACnB,MAAM,CASR;AAED,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,eAAe,SAAI,EACnB,gBAAgB,SAAI,GACnB,MAAM,CASR"}
@@ -1,3 +1,3 @@
1
- import type { Database } from 'bun:sqlite';
1
+ import type { SqliteAdapter as Database } from '@hasna/cloud';
2
2
  export declare function checkAndFireWebhooks(db: Database): Promise<void>;
3
3
  //# sourceMappingURL=webhooks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/lib/webhooks.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE1C,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBtE"}
1
+ {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/lib/webhooks.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAE7D,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBtE"}