@hasna/economy 0.5.7 → 0.5.9

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/chunks/{billing-22az60hy.js → billing-7twygmtj.js} +2 -2
  3. package/dist/chunks/{config-hsp7ndhj.js → config-mjgbekxj.js} +2 -2
  4. package/dist/chunks/{index-9wa1f2kv.js → index-7ek4r0ww.js} +1 -1
  5. package/dist/chunks/{index-z2n5y111.js → index-92x56q4h.js} +5 -4
  6. package/dist/chunks/{index-wq7c4wqs.js → index-970htsxe.js} +2 -2
  7. package/dist/chunks/{index-vajzfzgv.js → index-ep1pkyr3.js} +1 -1
  8. package/dist/chunks/{index-dk5rq1rq.js → index-gtjqy0kd.js} +1 -1
  9. package/dist/chunks/{index-b6c649mq.js → index-spwpgdbb.js} +82 -7
  10. package/dist/chunks/{index-c7h2rxx1.js → index-t0n32g8r.js} +1 -1
  11. package/dist/chunks/{index-6yr4hb72.js → index-twkdyrw4.js} +2 -2
  12. package/dist/chunks/{index-f5jv6q5q.js → index-zgvmrqyc.js} +1 -1
  13. package/dist/chunks/{open-projects-8qm46m3d.js → open-projects-bj561k5j.js} +1 -1
  14. package/dist/chunks/{pricing-k9wsjwbz.js → pricing-bkgvzpe9.js} +1 -1
  15. package/dist/chunks/{pricing-462f8xkm.js → pricing-bn88mhqk.js} +1 -1
  16. package/dist/chunks/{pricing-vxejj1qa.js → pricing-nt31hf53.js} +1 -1
  17. package/dist/chunks/{serve-ms8dvf7e.js → serve-tmv8zgvh.js} +14 -8
  18. package/dist/chunks/{sqlite-store-ht6frztj.js → sqlite-store-8t4anw98.js} +2 -2
  19. package/dist/chunks/{sqlite-store-13nhr1z0.js → sqlite-store-jfy0e8b6.js} +2 -2
  20. package/dist/chunks/{sqlite-store-gamn65xp.js → sqlite-store-r219qrjt.js} +2 -2
  21. package/dist/chunks/{tui-9t7340wd.js → tui-j8brj3wf.js} +4 -4
  22. package/dist/chunks/{watch-t6ssnx30.js → watch-ksn2mezm.js} +5 -5
  23. package/dist/chunks/{webhooks-nw4daw9y.js → webhooks-td46p6gk.js} +2 -2
  24. package/dist/cli/index.js +38 -32
  25. package/dist/cost-imports/contract.d.ts +84 -0
  26. package/dist/cost-imports/contract.d.ts.map +1 -0
  27. package/dist/cost-imports/decimal.d.ts +6 -0
  28. package/dist/cost-imports/decimal.d.ts.map +1 -0
  29. package/dist/cost-imports/providers.d.ts +4 -0
  30. package/dist/cost-imports/providers.d.ts.map +1 -0
  31. package/dist/index.js +72 -12
  32. package/dist/lib/analytics.d.ts +3 -0
  33. package/dist/lib/analytics.d.ts.map +1 -1
  34. package/dist/lib/comparison-contract.d.ts +8 -0
  35. package/dist/lib/comparison-contract.d.ts.map +1 -0
  36. package/dist/lib/comparison-test-contract.d.ts +3 -0
  37. package/dist/lib/comparison-test-contract.d.ts.map +1 -0
  38. package/dist/lib/ingest-journal-maintenance.d.ts.map +1 -1
  39. package/dist/lib/store/index.d.ts.map +1 -1
  40. package/dist/mcp/index.js +75 -15
  41. package/dist/otel/index.js +7 -5
  42. package/dist/server/index.js +40 -6
  43. package/dist/server/service-operations.d.ts.map +1 -1
  44. package/dist/server/service-worker.js +40 -6
  45. package/docs/comparison-coverage.md +30 -0
  46. package/docs/ingest-journal.md +5 -1
  47. package/native/README.md +48 -0
  48. package/native/Tools/pair-qr.py +6 -1
  49. package/native/Tools/pair-qr.swift +19 -1
  50. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -4,26 +4,28 @@ import {
4
4
  syncAnthropicBilling,
5
5
  syncGeminiBilling,
6
6
  syncOpenAIBilling
7
- } from "../chunks/index-dk5rq1rq.js";
7
+ } from "../chunks/index-gtjqy0kd.js";
8
8
  import {
9
9
  buildStatusLine,
10
10
  gatherStatusData
11
- } from "../chunks/index-6yr4hb72.js";
11
+ } from "../chunks/index-twkdyrw4.js";
12
12
  import"../chunks/index-w3bvv0ns.js";
13
13
  import {
14
14
  agentPaths,
15
15
  syncAll
16
- } from "../chunks/index-wq7c4wqs.js";
16
+ } from "../chunks/index-970htsxe.js";
17
17
  import {
18
18
  getStore,
19
19
  isCloudStore
20
- } from "../chunks/index-z2n5y111.js";
20
+ } from "../chunks/index-92x56q4h.js";
21
21
  import {
22
22
  billingDriftCheck,
23
+ comparisonValueChange,
23
24
  economyCloudStorage,
24
25
  economyTransportReport,
26
+ formatComparisonValue,
25
27
  packageMetadata
26
- } from "../chunks/index-b6c649mq.js";
28
+ } from "../chunks/index-spwpgdbb.js";
27
29
  import {
28
30
  AGENTS,
29
31
  MAX_INGEST_BYTES,
@@ -46,7 +48,7 @@ import {
46
48
  setIngestState,
47
49
  stableJson,
48
50
  validateIngest
49
- } from "../chunks/index-f5jv6q5q.js";
51
+ } from "../chunks/index-zgvmrqyc.js";
50
52
  import {
51
53
  __require
52
54
  } from "../chunks/index-p35cyhc4.js";
@@ -1279,7 +1281,7 @@ function registerExtendedCommands(program) {
1279
1281
  }
1280
1282
  }
1281
1283
  if (!cloud) {
1282
- const { openDatabase } = await import("../chunks/sqlite-store-13nhr1z0.js");
1284
+ const { openDatabase } = await import("../chunks/sqlite-store-r219qrjt.js");
1283
1285
  const db = openDatabase();
1284
1286
  ensurePricingSeeded(db);
1285
1287
  const zeroCostBuckets = queryZeroCostTokenizedModels(db, 5);
@@ -1357,23 +1359,23 @@ function registerExtendedCommands(program) {
1357
1359
  console.log();
1358
1360
  });
1359
1361
  program.command("tui").description("Terminal status dashboard (Linux/SSH friendly)").option("--watch", "Live refresh").option("--interval <seconds>", "Refresh interval", "30").action(async (opts) => {
1360
- const { runTui } = await import("../chunks/tui-9t7340wd.js");
1362
+ const { runTui } = await import("../chunks/tui-j8brj3wf.js");
1361
1363
  await runTui({
1362
1364
  watch: Boolean(opts.watch),
1363
1365
  interval: Number(opts.interval ?? 30)
1364
1366
  });
1365
1367
  });
1366
1368
  program.command("waybar").description("Print waybar-compatible JSON status line").action(async () => {
1367
- const { printWaybarJson } = await import("../chunks/tui-9t7340wd.js");
1369
+ const { printWaybarJson } = await import("../chunks/tui-j8brj3wf.js");
1368
1370
  await printWaybarJson();
1369
1371
  });
1370
1372
  const barCmd = program.command("bar").description("Status bar helpers (Linux)");
1371
1373
  barCmd.command("tui").description("Alias for economy tui").option("--watch", "Live refresh").action(async (opts) => {
1372
- const { runTui } = await import("../chunks/tui-9t7340wd.js");
1374
+ const { runTui } = await import("../chunks/tui-j8brj3wf.js");
1373
1375
  await runTui({ watch: Boolean(opts.watch) });
1374
1376
  });
1375
1377
  barCmd.command("waybar").description("Alias for economy waybar").action(async () => {
1376
- const { printWaybarJson } = await import("../chunks/tui-9t7340wd.js");
1378
+ const { printWaybarJson } = await import("../chunks/tui-j8brj3wf.js");
1377
1379
  await printWaybarJson();
1378
1380
  });
1379
1381
  program.command("estimate").description("Estimate cost for token counts").requiredOption("--model <model>", "Model name").option("--input <n>", "Input tokens", "0").option("--output <n>", "Output tokens", "0").action(async (opts) => {
@@ -2022,18 +2024,19 @@ class IngestJournalMaintenance {
2022
2024
  throw new Error("Journal maintenance limit must be 1\u201332");
2023
2025
  await this.recover();
2024
2026
  await verifyJournalAuthority(this.journal, this.cloud);
2025
- const deadline = Date.now() + 30000;
2027
+ const sweepDeadline = Date.now() + 30000;
2026
2028
  let retired = 0, bytes = 0;
2027
2029
  let backend;
2028
2030
  try {
2029
2031
  const names = this.journal.names();
2030
2032
  for (const name of names.filter((n) => n.endsWith(".intent.json"))) {
2031
- if (retired >= limit || Date.now() >= deadline || retired > 0 && bytes >= 1048576)
2033
+ if (retired >= limit || Date.now() >= sweepDeadline || retired > 0 && bytes >= 1048576)
2032
2034
  break;
2033
2035
  if (!names.includes(name.replace(".intent.", ".receipt.")))
2034
2036
  continue;
2035
2037
  const intent = this.journal.intent(name), receipt = this.journal.receipt(intent.command);
2036
- backend ??= await this.backend(deadline);
2038
+ const deadline = Date.now() + 30000;
2039
+ backend = await this.backend(deadline);
2037
2040
  const m = {
2038
2041
  version: 1,
2039
2042
  target: this.journal.target,
@@ -2069,7 +2072,8 @@ class IngestJournalMaintenance {
2069
2072
  try {
2070
2073
  await this.maintain();
2071
2074
  } catch (error) {
2072
- if (exists(this.control) || !this.journal.canStage(command))
2075
+ const deferrable = error instanceof JournalMaintenanceError && ["recovery_not_configured", "self_host_trash_authority_required", "trash_version_mismatch", "recovery_authority_unavailable"].includes(error.reason);
2076
+ if (!deferrable || exists(this.control) || !this.journal.canStage(command))
2073
2077
  throw error;
2074
2078
  }
2075
2079
  if (!this.journal.canStage(command))
@@ -2335,7 +2339,7 @@ function backfillMachineId(db) {
2335
2339
  return { requests: Number(reqCount.changes), sessions: Number(sessCount.changes) };
2336
2340
  }
2337
2341
  async function recalculateZeroCostRequests(db) {
2338
- const { assessCostFromDb } = await import("../chunks/pricing-462f8xkm.js");
2342
+ const { assessCostFromDb } = await import("../chunks/pricing-bn88mhqk.js");
2339
2343
  const zeroRows = db.prepare(`SELECT id, model, input_tokens, output_tokens, cache_read_tokens, cache_create_tokens, cache_create_5m_tokens, cache_create_1h_tokens
2340
2344
  FROM requests
2341
2345
  WHERE cost_usd = 0
@@ -2544,7 +2548,7 @@ async function postIngest(cloud, body) {
2544
2548
  }
2545
2549
  async function syncAllToCloud(cloud, opts = {}) {
2546
2550
  await runIngestJournalOperation(cloud, { action: "reconcile" });
2547
- const { openDatabase } = await import("../chunks/sqlite-store-13nhr1z0.js");
2551
+ const { openDatabase } = await import("../chunks/sqlite-store-r219qrjt.js");
2548
2552
  const scratch = openDatabase(":memory:", true);
2549
2553
  ensurePricingSeeded(scratch);
2550
2554
  const cachePath = opts.cachePath ?? getIngestCachePath();
@@ -2571,7 +2575,7 @@ async function syncAllToCloud(cloud, opts = {}) {
2571
2575
  }
2572
2576
  async function billingSyncToCloud(cloud, opts = {}) {
2573
2577
  await runIngestJournalOperation(cloud, { action: "reconcile" });
2574
- const { openDatabase } = await import("../chunks/sqlite-store-13nhr1z0.js");
2578
+ const { openDatabase } = await import("../chunks/sqlite-store-r219qrjt.js");
2575
2579
  const scratch = openDatabase(":memory:", true);
2576
2580
  try {
2577
2581
  const days = opts.days ?? 31;
@@ -2630,7 +2634,7 @@ function autosyncIntervalMs(env = process.env) {
2630
2634
  return Math.round(seconds * 1000);
2631
2635
  }
2632
2636
  async function autosyncMarkerStore(env = process.env) {
2633
- const { openDatabase } = await import("../chunks/sqlite-store-13nhr1z0.js");
2637
+ const { openDatabase } = await import("../chunks/sqlite-store-r219qrjt.js");
2634
2638
  if (isCloudStore(env))
2635
2639
  return openDatabase(getIngestCachePath());
2636
2640
  return openDatabase();
@@ -2663,7 +2667,7 @@ async function autoSync(opts = {}) {
2663
2667
  return;
2664
2668
  if (!await autoSyncDue(undefined, process.env))
2665
2669
  return;
2666
- const { openDatabase } = await import("../chunks/sqlite-store-13nhr1z0.js");
2670
+ const { openDatabase } = await import("../chunks/sqlite-store-r219qrjt.js");
2667
2671
  const db = openDatabase();
2668
2672
  ensurePricingSeeded(db);
2669
2673
  await syncAll(db, opts);
@@ -2931,13 +2935,13 @@ program.command("sync").description("Ingest cost data from all coding agents").o
2931
2935
  }
2932
2936
  return;
2933
2937
  }
2934
- const { openDatabase } = await import("../chunks/sqlite-store-13nhr1z0.js");
2938
+ const { openDatabase } = await import("../chunks/sqlite-store-r219qrjt.js");
2935
2939
  const db = openDatabase();
2936
2940
  ensurePricingSeeded(db);
2937
2941
  const anySpecific = opts.claude || opts.takumi || opts.codex || opts.gemini || opts.opencode || opts.cursor || opts.pi || opts.hermes || opts.loops;
2938
2942
  if (!anySpecific || opts.verbose) {
2939
2943
  try {
2940
- const { syncOpenProjectsRegistry } = await import("../chunks/open-projects-8qm46m3d.js");
2944
+ const { syncOpenProjectsRegistry } = await import("../chunks/open-projects-bj561k5j.js");
2941
2945
  const p = await syncOpenProjectsRegistry(db);
2942
2946
  if (opts.verbose)
2943
2947
  console.log(chalk3.dim(`Synced open-projects registry: ${p.imported} imported, ${p.skipped} skipped`));
@@ -2972,7 +2976,7 @@ program.command("sync").description("Ingest cost data from all coding agents").o
2972
2976
  console.log(chalk3.cyan(`\u2192 Backfilled machine_id='${machine}': ${r.requests} requests, ${r.sessions} sessions`));
2973
2977
  }
2974
2978
  if (opts.recalculate) {
2975
- const { getPricingFromDb: getPricingFromDb2 } = await import("../chunks/pricing-462f8xkm.js");
2979
+ const { getPricingFromDb: getPricingFromDb2 } = await import("../chunks/pricing-bn88mhqk.js");
2976
2980
  const r = await recalculateZeroCostRequests(db);
2977
2981
  console.log(chalk3.cyan(`\u2192 Recalculated: ${r.fixed}/${r.total} zero-cost requests now have pricing`));
2978
2982
  const zeroCostBuckets = r.buckets;
@@ -2987,7 +2991,7 @@ program.command("sync").description("Ingest cost data from all coding agents").o
2987
2991
  }
2988
2992
  }
2989
2993
  try {
2990
- const { checkAndFireWebhooks } = await import("../chunks/webhooks-nw4daw9y.js");
2994
+ const { checkAndFireWebhooks } = await import("../chunks/webhooks-td46p6gk.js");
2991
2995
  await checkAndFireWebhooks(db);
2992
2996
  } catch {}
2993
2997
  for (const line of syncOutcomeLines(result))
@@ -3198,7 +3202,7 @@ program.command("accounts [period]").description("List account usage by email ad
3198
3202
  console.log();
3199
3203
  });
3200
3204
  program.command("watch").description("Live stream of incoming costs").option("--interval <seconds>", "Poll interval in seconds", "10").option("--daemon", "Watch agent data directories and sync on change").option("--agent <agent>", `Filter by agent (${AGENTS.join("|")})`).option("--notify <amount>", "Fire macOS notification when cumulative cost crosses this USD threshold").action(async (opts) => {
3201
- const { watchCosts } = await import("../chunks/watch-t6ssnx30.js");
3205
+ const { watchCosts } = await import("../chunks/watch-ksn2mezm.js");
3202
3206
  await watchCosts({
3203
3207
  interval: parsePositiveCliInteger(opts.interval ?? "10", "--interval"),
3204
3208
  daemon: Boolean(opts.daemon),
@@ -3339,16 +3343,16 @@ projectCmd.command("show <nameOrPath>").description("Detailed project breakdown
3339
3343
  });
3340
3344
  var configCmd = program.command("config").description("Manage economy configuration");
3341
3345
  configCmd.command("set <key> <value>").description("Set a config value").action(async (_key, _value) => {
3342
- const { setConfigValue } = await import("../chunks/config-hsp7ndhj.js");
3346
+ const { setConfigValue } = await import("../chunks/config-mjgbekxj.js");
3343
3347
  setConfigValue(_key, _value);
3344
3348
  console.log(chalk3.green(`\u2713 ${_key} = ${_value}`));
3345
3349
  });
3346
3350
  configCmd.command("get <key>").description("Get a config value").action(async (key) => {
3347
- const { getConfigValue } = await import("../chunks/config-hsp7ndhj.js");
3351
+ const { getConfigValue } = await import("../chunks/config-mjgbekxj.js");
3348
3352
  console.log(getConfigValue(key) ?? chalk3.dim("(not set)"));
3349
3353
  });
3350
3354
  configCmd.command("webhook-test").description("Send a test payload to the configured webhook URL").action(async () => {
3351
- const { loadConfig } = await import("../chunks/config-hsp7ndhj.js");
3355
+ const { loadConfig } = await import("../chunks/config-mjgbekxj.js");
3352
3356
  const config = loadConfig();
3353
3357
  const url = config["webhook-url"];
3354
3358
  if (!url) {
@@ -3382,7 +3386,7 @@ configCmd.command("webhook-test").description("Send a test payload to the config
3382
3386
  }
3383
3387
  });
3384
3388
  configCmd.action(async () => {
3385
- const { loadConfig } = await import("../chunks/config-hsp7ndhj.js");
3389
+ const { loadConfig } = await import("../chunks/config-mjgbekxj.js");
3386
3390
  const config = loadConfig();
3387
3391
  console.log();
3388
3392
  printTable(["Key", "Value"], Object.entries(config).map(([k, v]) => [k, String(v)]));
@@ -3434,7 +3438,7 @@ pricingCmd.command("remove <model>").description("Remove pricing for a model").a
3434
3438
  });
3435
3439
  program.command("serve").description("Start the REST API server").option("-p, --port <port>", "Port", "3456").action(async (opts) => {
3436
3440
  const port = parseCliPort(opts.port ?? "3456", "--port");
3437
- const { startServer } = await import("../chunks/serve-ms8dvf7e.js");
3441
+ const { startServer } = await import("../chunks/serve-tmv8zgvh.js");
3438
3442
  startServer(port);
3439
3443
  });
3440
3444
  program.command("mcp").description("Show MCP server install commands").option("--claude", "Install into Claude Code").option("--codex", "Install into Codex").option("--gemini", "Install into Gemini CLI").option("--all", "Install into all agents").action(async (opts) => {
@@ -3614,11 +3618,13 @@ program.command("compare <period1> <period2>").description("Compare two periods
3614
3618
  console.log(chalk3.bold.cyan(` ${p1} vs ${p2}`));
3615
3619
  console.log();
3616
3620
  printTable(["Metric", p1, p2, "Change"], [
3617
- ["Cost", fmt(a.cost), fmt(b.cost), delta(a.cost, b.cost)],
3621
+ ["Usage value", formatComparisonValue(a), formatComparisonValue(b), comparisonValueChange(a, b)],
3618
3622
  ["Sessions", fmtCount(a.sessions), fmtCount(b.sessions), delta(a.sessions, b.sessions)],
3619
3623
  ["Requests", fmtCount(a.requests), fmtCount(b.requests), delta(a.requests, b.requests)],
3620
3624
  ["Tokens", fmtTokens2(a.tokens), fmtTokens2(b.tokens), delta(a.tokens, b.tokens)]
3621
3625
  ]);
3626
+ console.log(`${p1}: ${formatSummaryCoverage(a.money)}`);
3627
+ console.log(`${p2}: ${formatSummaryCoverage(b.money)}`);
3622
3628
  console.log();
3623
3629
  });
3624
3630
  program.command("forecast").description("Project end-of-month cost based on current burn rate").action(async () => {
@@ -3795,7 +3801,7 @@ billingCmd.command("sync").description("Sync actual billing from Anthropic, Open
3795
3801
  console.log(chalk3.green(`\u2713 pushed ${result.total} billing rows to the shared API`));
3796
3802
  return;
3797
3803
  }
3798
- const { openDatabase } = await import("../chunks/sqlite-store-13nhr1z0.js");
3804
+ const { openDatabase } = await import("../chunks/sqlite-store-r219qrjt.js");
3799
3805
  const db = openDatabase();
3800
3806
  const doAll = !opts.anthropic && !opts.openai && !opts.gemini;
3801
3807
  if (opts.anthropic || doAll) {
@@ -0,0 +1,84 @@
1
+ export type Provider = 'github' | 'aws' | 'blacksmith';
2
+ export interface Window {
3
+ start: string;
4
+ endExclusive: string;
5
+ timeZone: string;
6
+ }
7
+ /** Metadata from a trusted read/export, not a credential or authorization grant. */
8
+ export interface Capture {
9
+ provider: Provider;
10
+ account: string;
11
+ sourceId: string;
12
+ revision: string;
13
+ supersedes?: string;
14
+ observedAt: string;
15
+ window: Window;
16
+ currency: string;
17
+ currencySource: string;
18
+ complete: boolean;
19
+ awsQuery?: {
20
+ metric: 'UnblendedCost';
21
+ groupBy: 'SERVICE';
22
+ granularity: 'MONTHLY';
23
+ filter: null;
24
+ };
25
+ pages: Array<{
26
+ cursor: string | null;
27
+ nextCursor: string | null;
28
+ body: string;
29
+ }>;
30
+ }
31
+ export interface Authority {
32
+ provider: Provider;
33
+ account: string;
34
+ currency: string;
35
+ }
36
+ export interface CostRow {
37
+ id: string;
38
+ date: string;
39
+ dimensions: Record<string, string>;
40
+ kind: 'usage' | 'tax' | 'credit' | 'subscription' | 'included';
41
+ net: string;
42
+ gross?: string;
43
+ discount?: string;
44
+ quantity?: string;
45
+ unit?: string;
46
+ estimated: boolean;
47
+ }
48
+ export interface Snapshot {
49
+ schemaVersion: 1;
50
+ provider: Provider;
51
+ account: string;
52
+ sourceId: string;
53
+ revision: string;
54
+ supersedes: string | null;
55
+ observedAt: string;
56
+ window: Window;
57
+ currency: string;
58
+ currencySource: string;
59
+ basis: 'usage_report' | 'cost_explorer' | 'invoice';
60
+ scopeId: string;
61
+ contentHash: string;
62
+ snapshotId: string;
63
+ pageHashes: string[];
64
+ rows: CostRow[];
65
+ excludedRows: number;
66
+ total: string;
67
+ tax: string | null;
68
+ paymentStatus: 'unknown';
69
+ }
70
+ export declare function fail(code: string): never;
71
+ export declare function object(value: unknown): Record<string, unknown>;
72
+ export declare function list(value: unknown): unknown[];
73
+ /** Bounded metadata labels; URLs and control characters are refused. Not a secret scanner. */
74
+ export declare function label(value: unknown): string;
75
+ export declare function date(value: unknown): string;
76
+ export declare function stable(value: unknown): string;
77
+ export declare const hash: (value: string) => string;
78
+ export declare function validateCapture(c: Capture, authority: Authority): Record<string, unknown>[];
79
+ export declare function inWindow(day: unknown, c: Capture): string;
80
+ export declare function finish(c: Capture, rows: CostRow[], basis: Snapshot['basis'], excludedRows?: number): Snapshot;
81
+ export declare function admitRevision(history: readonly Snapshot[], next: Snapshot): 'append' | 'replay';
82
+ /** Totals stay partitioned by provider/account/currency/basis/window; no grand total. */
83
+ export declare function reconcile(history: readonly Snapshot[]): Snapshot[];
84
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/cost-imports/contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /** Exact, bounded decimal arithmetic. Provider precision is retained until display. */
2
+ export declare function decimal(value: unknown): string;
3
+ export declare function add(...values: string[]): string;
4
+ export declare function negate(value: string): string;
5
+ export declare function within(left: string, right: string, tolerance?: string): boolean;
6
+ //# sourceMappingURL=decimal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decimal.d.ts","sourceRoot":"","sources":["../../src/cost-imports/decimal.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAc9C;AAKD,wBAAgB,GAAG,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAK/C;AACD,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAyE;AACtH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,SAAM,GAAG,OAAO,CAI5E"}
@@ -0,0 +1,4 @@
1
+ import { type Capture, type Authority, type Snapshot } from './contract.js';
2
+ /** No HTTP or state access: normalize one fully captured provider report. */
3
+ export declare function normalizeCostImport(capture: Capture, authority: Authority): Snapshot;
4
+ //# sourceMappingURL=providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/cost-imports/providers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,SAAS,EAAgB,KAAK,QAAQ,EAA8E,MAAM,eAAe,CAAA;AAUrK,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,QAAQ,CAKpF"}
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  COST_BASIS,
5
5
  MAX_INGEST_BYTES,
6
6
  __require,
7
+ buildCostSummary,
7
8
  cacheDir,
8
9
  calendarPeriodBounds,
9
10
  calendarPeriodWhere,
@@ -56,13 +57,61 @@ import {
56
57
  upsertSubscription,
57
58
  usageSnapshotFilterForPeriod,
58
59
  validateIngest
59
- } from "./chunks/index-c7h2rxx1.js";
60
+ } from "./chunks/index-t0n32g8r.js";
61
+ // src/lib/comparison-contract.ts
62
+ function comparisonDateBounds(from, to) {
63
+ const valid = (value) => /^\d{4}-\d{2}-\d{2}$/.test(value) && Number.isFinite(Date.parse(value + "T00:00:00Z")) && new Date(value + "T00:00:00Z").toISOString().slice(0, 10) === value;
64
+ if (!valid(from) || !valid(to) || from > to) {
65
+ throw new RangeError("from and to must be real YYYY-MM-DD UTC dates with from <= to");
66
+ }
67
+ const end = new Date(to + "T00:00:00Z");
68
+ end.setUTCDate(end.getUTCDate() + 1);
69
+ const exclusive = end.toISOString().slice(0, 10);
70
+ if (!valid(exclusive))
71
+ throw new RangeError("to exceeds the supported UTC date range");
72
+ return { time_zone: "UTC", start_date: from, end_date_exclusive: exclusive };
73
+ }
74
+ function requireCurrentRangeStats(value, from, to) {
75
+ if (!value || typeof value !== "object")
76
+ throw new Error("COMPARISON_REPORT_UNAVAILABLE: Invalid response");
77
+ const report = value;
78
+ let money;
79
+ try {
80
+ money = requireCurrentCostSummary(report.money);
81
+ } catch {
82
+ throw new Error("COMPARISON_REPORT_UNAVAILABLE: The service must support comparison coverage");
83
+ }
84
+ for (const key of ["sessions", "requests", "tokens"]) {
85
+ if (!Number.isSafeInteger(report[key]) || report[key] < 0)
86
+ throw new Error("COMPARISON_REPORT_UNAVAILABLE: Invalid " + key);
87
+ }
88
+ if (report.cost !== money.total_usd || report.requests !== money.request_rows || report.tokens !== money.tokens || money.session_only_sessions !== 0 || money.session_only_value_usd !== 0 || money.requests !== money.request_rows) {
89
+ throw new Error("COMPARISON_REPORT_UNAVAILABLE: Inconsistent monetary coverage");
90
+ }
91
+ const knownRows = money.request_rows - money.unknown_price_requests;
92
+ const completeness = money.request_rows === 0 ? "no_records" : money.unknown_price_requests === 0 ? "complete" : knownRows > 0 ? "partial" : "unavailable";
93
+ if (money.completeness !== completeness || knownRows === 0 && money.total_usd !== 0 || money.sessions > money.request_rows || money.request_rows === 0 && money.tokens !== 0) {
94
+ throw new Error("COMPARISON_REPORT_UNAVAILABLE: Inconsistent request coverage");
95
+ }
96
+ const bounds = money.period_bounds;
97
+ if (!bounds.start_date || !bounds.end_date_exclusive || bounds.start_date >= bounds.end_date_exclusive) {
98
+ throw new Error("COMPARISON_REPORT_UNAVAILABLE: Missing comparison dates");
99
+ }
100
+ if (from !== undefined || to !== undefined) {
101
+ const expected = comparisonDateBounds(from, to);
102
+ if (bounds.start_date !== expected.start_date || bounds.end_date_exclusive !== expected.end_date_exclusive) {
103
+ throw new Error("COMPARISON_REPORT_UNAVAILABLE: Response dates do not match the requested range");
104
+ }
105
+ }
106
+ return report;
107
+ }
108
+
60
109
  // src/lib/store/index.ts
61
110
  import { randomUUID } from "crypto";
62
111
  // package.json
63
112
  var package_default = {
64
113
  name: "@hasna/economy",
65
- version: "0.5.7",
114
+ version: "0.5.9",
66
115
  description: "AI coding cost tracker \u2014 CLI + MCP server + REST API for Claude Code, Codex, OpenCode, Cursor, Pi, and Hermes, with legacy Gemini CLI session and Gemini API billing ingestion",
67
116
  repository: {
68
117
  type: "git",
@@ -1121,11 +1170,20 @@ function queryExportRows(db, type, period) {
1121
1170
  return db.prepare(`SELECT * FROM economy_effective_sessions WHERE ${where} ORDER BY started_at DESC`).all();
1122
1171
  }
1123
1172
  function queryRangeStats(db, from, to) {
1124
- const r = db.prepare(`SELECT COALESCE(SUM(cost_usd),0) as cost, COUNT(*) as requests,
1125
- COALESCE(SUM(input_tokens+output_tokens+cache_read_tokens+cache_create_tokens),0) as tokens
1126
- FROM economy_effective_requests WHERE DATE(timestamp) BETWEEN ? AND ?`).get(from, to);
1173
+ const bounds = comparisonDateBounds(from, to);
1174
+ const groups = db.prepare(`
1175
+ SELECT cost_basis, model, CASE WHEN cost_usd = 0 THEN 1 WHEN cost_usd < 0 THEN 2 ELSE 0 END AS zero_cost,
1176
+ SUM(cost_usd) AS cost_usd, COUNT(*) AS requests,
1177
+ COALESCE(SUM(input_tokens),0) AS input_tokens, COALESCE(SUM(output_tokens),0) AS output_tokens,
1178
+ COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens, COALESCE(SUM(cache_create_tokens),0) AS cache_create_tokens,
1179
+ COALESCE(SUM(cache_create_1h_tokens),0) AS cache_create_1h_tokens
1180
+ FROM economy_effective_requests WHERE DATE(timestamp) BETWEEN ? AND ?
1181
+ GROUP BY cost_basis, model, CASE WHEN cost_usd = 0 THEN 1 WHEN cost_usd < 0 THEN 2 ELSE 0 END
1182
+ `).all(from, to);
1183
+ const requestSessions = db.prepare("SELECT COUNT(DISTINCT session_id) AS sessions FROM economy_effective_requests WHERE DATE(timestamp) BETWEEN ? AND ?").get(from, to);
1184
+ const money = buildCostSummary("all", bounds, groups, { sessions: 0, requests: 0, tokens: 0, cost_usd: 0 }, requestSessions.sessions, listModelPricing(db));
1127
1185
  const s = db.prepare(`SELECT COUNT(*) as sessions FROM economy_effective_sessions WHERE DATE(started_at) BETWEEN ? AND ?`).get(from, to);
1128
- return { cost: r.cost, requests: r.requests, tokens: r.tokens, sessions: s.sessions };
1186
+ return { cost: money.total_usd, money, requests: money.request_rows, tokens: money.tokens, sessions: s.sessions };
1129
1187
  }
1130
1188
  function queryForecast(db, now = new Date) {
1131
1189
  const month = resolveReportingInterval({ kind: "calendar", period: "month" }, now);
@@ -1340,7 +1398,7 @@ class LocalStore {
1340
1398
  _db;
1341
1399
  async db() {
1342
1400
  if (!this._db) {
1343
- const { openDatabase } = await import("./chunks/sqlite-store-ht6frztj.js");
1401
+ const { openDatabase } = await import("./chunks/sqlite-store-8t4anw98.js");
1344
1402
  this._db = openDatabase();
1345
1403
  ensurePricingSeeded(this._db);
1346
1404
  }
@@ -1617,7 +1675,7 @@ class ApiStore {
1617
1675
  return cloudListItems(this.cloud, "export", q({ type, period }));
1618
1676
  }
1619
1677
  async rangeStats(from, to) {
1620
- return cloudObject(this.cloud, "/compare", q({ from, to }));
1678
+ return requireCurrentRangeStats(await cloudObject(this.cloud, "/compare", q({ from, to })), from, to);
1621
1679
  }
1622
1680
  async forecast() {
1623
1681
  return cloudObject(this.cloud, "/forecast");
@@ -2287,18 +2345,19 @@ class IngestJournalMaintenance {
2287
2345
  throw new Error("Journal maintenance limit must be 1\u201332");
2288
2346
  await this.recover();
2289
2347
  await verifyJournalAuthority(this.journal, this.cloud);
2290
- const deadline = Date.now() + 30000;
2348
+ const sweepDeadline = Date.now() + 30000;
2291
2349
  let retired = 0, bytes = 0;
2292
2350
  let backend;
2293
2351
  try {
2294
2352
  const names = this.journal.names();
2295
2353
  for (const name of names.filter((n) => n.endsWith(".intent.json"))) {
2296
- if (retired >= limit || Date.now() >= deadline || retired > 0 && bytes >= 1048576)
2354
+ if (retired >= limit || Date.now() >= sweepDeadline || retired > 0 && bytes >= 1048576)
2297
2355
  break;
2298
2356
  if (!names.includes(name.replace(".intent.", ".receipt.")))
2299
2357
  continue;
2300
2358
  const intent = this.journal.intent(name), receipt = this.journal.receipt(intent.command);
2301
- backend ??= await this.backend(deadline);
2359
+ const deadline = Date.now() + 30000;
2360
+ backend = await this.backend(deadline);
2302
2361
  const m = {
2303
2362
  version: 1,
2304
2363
  target: this.journal.target,
@@ -2334,7 +2393,8 @@ class IngestJournalMaintenance {
2334
2393
  try {
2335
2394
  await this.maintain();
2336
2395
  } catch (error) {
2337
- if (exists(this.control) || !this.journal.canStage(command))
2396
+ const deferrable = error instanceof JournalMaintenanceError && ["recovery_not_configured", "self_host_trash_authority_required", "trash_version_mismatch", "recovery_authority_unavailable"].includes(error.reason);
2397
+ if (!deferrable || exists(this.control) || !this.journal.canStage(command))
2338
2398
  throw error;
2339
2399
  }
2340
2400
  if (!this.journal.canStage(command))
@@ -1,3 +1,4 @@
1
+ import type { CostSummary } from '../types/index.js';
1
2
  import type { SqliteAdapter as Database } from '../db/sqlite-adapter.js';
2
3
  export interface ProjectDetailModel {
3
4
  model: string;
@@ -28,7 +29,9 @@ export type ExportType = 'sessions' | 'requests';
28
29
  /** Raw rows for a CSV export. CSV formatting stays in the caller. */
29
30
  export declare function queryExportRows(db: Database, type: ExportType, period: string): Array<Record<string, unknown>>;
30
31
  export interface RangeStats {
32
+ /** Known selected-request usage value subtotal. Use money.complete_total_usd for a complete total, never payment. */
31
33
  cost: number;
34
+ money: CostSummary;
32
35
  requests: number;
33
36
  tokens: number;
34
37
  sessions: number;
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/lib/analytics.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAIxE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAChD,MAAM,EAAE,kBAAkB,EAAE,CAAA;IAC5B,YAAY,EAAE,oBAAoB,EAAE,CAAA;CACrC;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,OAAa,GAAG,aAAa,GAAG,IAAI,CAoD3G;AAID,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,UAAU,CAAA;AAShD,qEAAqE;AACrE,wBAAgB,eAAe,CAC7B,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,GACb,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAOhC;AAID,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAUlF;AAID,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,mBAAmB,EAAE,MAAM,CAAA;IAC3B,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACvD,kBAAkB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CAC9D;AAED,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,GAAE,IAAiB,GAAG,YAAY,CAiChF;AAID,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,GAAG,eAAe,EAAE,CAWpE"}
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/lib/analytics.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AASpD,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAIxE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAChD,MAAM,EAAE,kBAAkB,EAAE,CAAA;IAC5B,YAAY,EAAE,oBAAoB,EAAE,CAAA;CACrC;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,OAAa,GAAG,aAAa,GAAG,IAAI,CAoD3G;AAID,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,UAAU,CAAA;AAShD,qEAAqE;AACrE,wBAAgB,eAAe,CAC7B,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,GACb,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAOhC;AAID,MAAM,WAAW,UAAU;IACzB,qHAAqH;IACrH,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,WAAW,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAqBlF;AAID,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,mBAAmB,EAAE,MAAM,CAAA;IAC3B,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACvD,kBAAkB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CAC9D;AAED,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,GAAE,IAAiB,GAAG,YAAY,CAiChF;AAID,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,GAAG,eAAe,EAAE,CAWpE"}
@@ -0,0 +1,8 @@
1
+ import type { RangeStats } from './analytics.js';
2
+ import type { CalendarPeriodBounds } from './periods.js';
3
+ export declare function comparisonDateBounds(from: string, to: string): CalendarPeriodBounds;
4
+ /** Old servers' bare cost sums cannot satisfy the comparison coverage contract. */
5
+ export declare function requireCurrentRangeStats(value: unknown, from?: string, to?: string): RangeStats;
6
+ export declare function formatComparisonValue(report: RangeStats): string;
7
+ export declare function comparisonValueChange(left: RangeStats, right: RangeStats): string;
8
+ //# sourceMappingURL=comparison-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comparison-contract.d.ts","sourceRoot":"","sources":["../../src/lib/comparison-contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAGxD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,oBAAoB,CAYnF;AAED,mFAAmF;AACnF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU,CAiC/F;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAEhE;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CASjF"}
@@ -0,0 +1,3 @@
1
+ import type { SqliteAdapter } from '../db/sqlite-adapter.js';
2
+ export declare function verifyComparisonCoverage(db: SqliteAdapter): Promise<void>;
3
+ //# sourceMappingURL=comparison-test-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comparison-test-contract.d.ts","sourceRoot":"","sources":["../../src/lib/comparison-test-contract.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAM5D,wBAAsB,wBAAwB,CAAC,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAwF/E"}
@@ -1 +1 @@
1
- {"version":3,"file":"ingest-journal-maintenance.d.ts","sourceRoot":"","sources":["../../src/lib/ingest-journal-maintenance.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ingest-journal-maintenance.d.ts","sourceRoot":"","sources":["../../src/lib/ingest-journal-maintenance.ts"],"names":[],"mappings":"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"}