@hasna/economy 0.5.2 → 0.5.3

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 (40) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/chunks/{billing-bkpxy4kv.js → billing-22az60hy.js} +2 -2
  3. package/dist/chunks/{config-j9mv3ehk.js → config-hsp7ndhj.js} +2 -2
  4. package/dist/chunks/{index-dj3tctqh.js → index-1vz1arz5.js} +14 -13
  5. package/dist/chunks/{index-hesxq90g.js → index-9wa1f2kv.js} +1 -1
  6. package/dist/chunks/{index-kzwqw3ve.js → index-bn04v378.js} +2 -2
  7. package/dist/chunks/{index-zpvea08j.js → index-c7h2rxx1.js} +38 -12
  8. package/dist/chunks/{index-k540hfww.js → index-dk5rq1rq.js} +1 -1
  9. package/dist/chunks/{index-ez9thsr7.js → index-f5jv6q5q.js} +38 -12
  10. package/dist/chunks/{index-9d2aky06.js → index-mhrkwvtk.js} +3 -3
  11. package/dist/chunks/{index-npsjjv96.js → index-s9mvven6.js} +2 -2
  12. package/dist/chunks/{index-2hwzmfgv.js → index-vajzfzgv.js} +38 -12
  13. package/dist/chunks/{open-projects-6z4pq6mx.js → open-projects-8qm46m3d.js} +1 -1
  14. package/dist/chunks/{pricing-vf2pycmh.js → pricing-462f8xkm.js} +1 -1
  15. package/dist/chunks/{pricing-f80p81gr.js → pricing-k9wsjwbz.js} +1 -1
  16. package/dist/chunks/{pricing-y21mcjsa.js → pricing-vxejj1qa.js} +1 -1
  17. package/dist/chunks/{serve-p2yarhm8.js → serve-8yqe7nhs.js} +18 -10
  18. package/dist/chunks/{sqlite-store-st99garx.js → sqlite-store-13nhr1z0.js} +2 -2
  19. package/dist/chunks/{sqlite-store-501f32ts.js → sqlite-store-gamn65xp.js} +2 -2
  20. package/dist/chunks/{sqlite-store-fq4natnf.js → sqlite-store-ht6frztj.js} +2 -2
  21. package/dist/chunks/{tui-x64hjfrh.js → tui-fe94bvp9.js} +4 -4
  22. package/dist/chunks/{watch-bxstjpbx.js → watch-ct8dvrpr.js} +5 -5
  23. package/dist/chunks/{webhooks-k4fh2t9c.js → webhooks-nw4daw9y.js} +20 -9
  24. package/dist/cli/index.js +27 -27
  25. package/dist/db/database.d.ts +2 -2
  26. package/dist/db/database.d.ts.map +1 -1
  27. package/dist/db/sync-pg.d.ts.map +1 -1
  28. package/dist/index.js +15 -14
  29. package/dist/lib/analytics.d.ts +1 -1
  30. package/dist/lib/analytics.d.ts.map +1 -1
  31. package/dist/lib/periods.d.ts +20 -1
  32. package/dist/lib/periods.d.ts.map +1 -1
  33. package/dist/lib/spikes.d.ts +3 -3
  34. package/dist/lib/spikes.d.ts.map +1 -1
  35. package/dist/mcp/index.js +18 -17
  36. package/dist/otel/index.js +1 -1
  37. package/dist/server/index.js +78 -33
  38. package/dist/server/service-operations.d.ts.map +1 -1
  39. package/dist/server/service-worker.js +78 -33
  40. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Use consistent UTC calendar and rolling intervals for charts, forecasts and spike detection. Bound report windows on both ends, preserve millisecond boundaries for rolling hours, and require consecutive observed days before detecting a daily cost spike.
8
+
3
9
  ## 0.5.2
4
10
 
5
11
  ### Patch Changes
@@ -3,8 +3,8 @@ import {
3
3
  syncAnthropicBilling,
4
4
  syncGeminiBilling,
5
5
  syncOpenAIBilling
6
- } from "./index-k540hfww.js";
7
- import"./index-ez9thsr7.js";
6
+ } from "./index-dk5rq1rq.js";
7
+ import"./index-f5jv6q5q.js";
8
8
  import"./index-p35cyhc4.js";
9
9
  export {
10
10
  syncOpenAIBilling,
@@ -4,8 +4,8 @@ import {
4
4
  loadConfig,
5
5
  saveConfig,
6
6
  setConfigValue
7
- } from "./index-hesxq90g.js";
8
- import"./index-ez9thsr7.js";
7
+ } from "./index-9wa1f2kv.js";
8
+ import"./index-f5jv6q5q.js";
9
9
  import"./index-p35cyhc4.js";
10
10
  export {
11
11
  setConfigValue,
@@ -14,12 +14,13 @@ import {
14
14
  querySummary,
15
15
  requireCurrentBillingSummary,
16
16
  requireCurrentCostSummary,
17
+ resolveReportingInterval,
17
18
  sessionOverlapWhere
18
- } from "./index-ez9thsr7.js";
19
+ } from "./index-f5jv6q5q.js";
19
20
  // package.json
20
21
  var package_default = {
21
22
  name: "@hasna/economy",
22
- version: "0.5.2",
23
+ version: "0.5.3",
23
24
  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",
24
25
  repository: {
25
26
  type: "git",
@@ -1230,7 +1231,8 @@ function buildBrief(db, opts = {}) {
1230
1231
  }
1231
1232
 
1232
1233
  // src/lib/analytics.ts
1233
- function queryProjectDetail(db, nameOrPath) {
1234
+ function queryProjectDetail(db, nameOrPath, now = new Date) {
1235
+ const dailyBounds = resolveReportingInterval({ kind: "rolling", unit: "day", count: 14 }, now);
1234
1236
  const like = `%${nameOrPath}%`;
1235
1237
  const sessions = db.prepare(`SELECT id, project_name, project_path, total_cost_usd, total_tokens, started_at
1236
1238
  FROM economy_effective_sessions
@@ -1244,8 +1246,8 @@ function queryProjectDetail(db, nameOrPath) {
1244
1246
  const daily = db.prepare(`SELECT DATE(r.timestamp) as date, COALESCE(SUM(r.cost_usd), 0) as cost_usd
1245
1247
  FROM economy_effective_requests r JOIN economy_effective_sessions s ON r.session_id = s.id
1246
1248
  WHERE (s.project_name LIKE ? OR s.project_path LIKE ?)
1247
- AND r.timestamp >= DATE('now', '-14 days')
1248
- GROUP BY date ORDER BY date ASC`).all(like, like);
1249
+ AND r.timestamp >= ? AND r.timestamp < ?
1250
+ GROUP BY date ORDER BY date ASC`).all(like, like, dailyBounds.start, dailyBounds.end_exclusive);
1249
1251
  const models = db.prepare(`SELECT r.model as model, COUNT(*) as requests, COALESCE(SUM(r.cost_usd), 0) as cost_usd
1250
1252
  FROM economy_effective_requests r JOIN economy_effective_sessions s ON r.session_id = s.id
1251
1253
  WHERE s.project_name LIKE ? OR s.project_path LIKE ?
@@ -1284,18 +1286,17 @@ function queryRangeStats(db, from, to) {
1284
1286
  return { cost: r.cost, requests: r.requests, tokens: r.tokens, sessions: s.sessions };
1285
1287
  }
1286
1288
  function queryForecast(db, now = new Date) {
1287
- const daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
1288
- const dayOfMonth = now.getDate();
1289
- const monthStart = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-01`;
1290
- const monthSoFar = db.prepare(`SELECT COALESCE(SUM(cost_usd),0) as cost FROM economy_effective_requests WHERE DATE(timestamp) >= ?`).get(monthStart);
1289
+ const month = resolveReportingInterval({ kind: "calendar", period: "month" }, now);
1290
+ const recent = resolveReportingInterval({ kind: "rolling", unit: "day", count: 7 }, now);
1291
+ const daysInMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 0)).getUTCDate();
1292
+ const dayOfMonth = now.getUTCDate();
1293
+ const monthSoFar = db.prepare(`SELECT COALESCE(SUM(cost_usd),0) as cost FROM economy_effective_requests WHERE timestamp >= ? AND timestamp < ?`).get(month.start, recent.end_exclusive);
1291
1294
  const dailyAvg = dayOfMonth > 0 ? monthSoFar.cost / dayOfMonth : 0;
1292
1295
  const projected = dailyAvg * daysInMonth;
1293
- const sevenDaysAgo = new Date(now);
1294
- sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
1295
- const last7 = db.prepare(`SELECT COALESCE(SUM(cost_usd),0) as cost FROM economy_effective_requests WHERE DATE(timestamp) >= ?`).get(sevenDaysAgo.toISOString().substring(0, 10));
1296
+ const last7 = db.prepare(`SELECT COALESCE(SUM(cost_usd),0) as cost FROM economy_effective_requests WHERE timestamp >= ? AND timestamp < ?`).get(recent.start, recent.end_exclusive);
1296
1297
  const last7DailyAvg = last7.cost / 7;
1297
1298
  const dailyCosts = db.prepare(`SELECT DATE(timestamp) as date, COALESCE(SUM(cost_usd),0) as cost_usd
1298
- FROM economy_effective_requests WHERE DATE(timestamp) >= ? GROUP BY date ORDER BY cost_usd ASC`).all(monthStart);
1299
+ FROM economy_effective_requests WHERE timestamp >= ? AND timestamp < ? GROUP BY date ORDER BY cost_usd ASC`).all(month.start, recent.end_exclusive);
1299
1300
  return {
1300
1301
  day_of_month: dayOfMonth,
1301
1302
  days_in_month: daysInMonth,
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  getDataDir
4
- } from "./index-ez9thsr7.js";
4
+ } from "./index-f5jv6q5q.js";
5
5
 
6
6
  // src/lib/config.ts
7
7
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
@@ -4,12 +4,12 @@ import {
4
4
  } from "./index-w3bvv0ns.js";
5
5
  import {
6
6
  getStore
7
- } from "./index-9d2aky06.js";
7
+ } from "./index-mhrkwvtk.js";
8
8
  import {
9
9
  formatReportedUsd,
10
10
  formatSummaryCoverage,
11
11
  formatSummaryValue
12
- } from "./index-ez9thsr7.js";
12
+ } from "./index-f5jv6q5q.js";
13
13
 
14
14
  // src/cli/commands/tui.ts
15
15
  import chalk from "chalk";
@@ -1072,12 +1072,32 @@ function meteredBudgetAmounts(groups, limit, threshold) {
1072
1072
  function ymd(date2) {
1073
1073
  return date2.toISOString().substring(0, 10);
1074
1074
  }
1075
- function calendarPeriodBounds(period, now = new Date) {
1075
+ function resolveReportingInterval(interval, now = new Date) {
1076
1076
  if (!Number.isFinite(now.getTime()))
1077
1077
  throw new RangeError("Invalid reporting clock");
1078
- const start = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
1078
+ const start = new Date(now);
1079
+ start.setUTCHours(0, 0, 0, 0);
1079
1080
  const end = new Date(start);
1080
- switch (period) {
1081
+ if (interval.kind === "rolling") {
1082
+ if (!Number.isSafeInteger(interval.count) || interval.count < 1)
1083
+ throw new RangeError("Invalid reporting window");
1084
+ if (interval.unit === "hour") {
1085
+ start.setTime(now.getTime() - interval.count * 3600000);
1086
+ if (!Number.isFinite(start.getTime()))
1087
+ throw new RangeError("Invalid reporting window");
1088
+ return { time_zone: "UTC", start: start.toISOString(), end_exclusive: now.toISOString() };
1089
+ }
1090
+ if (interval.unit !== "day")
1091
+ throw new RangeError("Invalid reporting unit");
1092
+ start.setUTCDate(start.getUTCDate() - interval.count + 1);
1093
+ end.setUTCDate(end.getUTCDate() + 1);
1094
+ if (!Number.isFinite(start.getTime()) || !Number.isFinite(end.getTime()))
1095
+ throw new RangeError("Invalid reporting window");
1096
+ return { time_zone: "UTC", start: ymd(start), end_exclusive: ymd(end) };
1097
+ }
1098
+ if (interval.kind !== "calendar")
1099
+ throw new RangeError("Invalid reporting interval");
1100
+ switch (interval.period) {
1081
1101
  case "today":
1082
1102
  end.setUTCDate(end.getUTCDate() + 1);
1083
1103
  break;
@@ -1100,11 +1120,15 @@ function calendarPeriodBounds(period, now = new Date) {
1100
1120
  end.setUTCFullYear(end.getUTCFullYear() + 1);
1101
1121
  break;
1102
1122
  case "all":
1103
- return { time_zone: "UTC", start_date: null, end_date_exclusive: null };
1123
+ return { time_zone: "UTC", start: null, end_exclusive: null };
1104
1124
  default:
1105
1125
  throw new RangeError("Invalid reporting period");
1106
1126
  }
1107
- return { time_zone: "UTC", start_date: ymd(start), end_date_exclusive: ymd(end) };
1127
+ return { time_zone: "UTC", start: ymd(start), end_exclusive: ymd(end) };
1128
+ }
1129
+ function calendarPeriodBounds(period, now = new Date) {
1130
+ const bounds = resolveReportingInterval({ kind: "calendar", period }, now);
1131
+ return { time_zone: bounds.time_zone, start_date: bounds.start, end_date_exclusive: bounds.end_exclusive };
1108
1132
  }
1109
1133
  function calendarPeriodWhere(period, column, now = new Date) {
1110
1134
  if (!/^[a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)?$/i.test(column)) {
@@ -2450,20 +2474,22 @@ function queryAccountBreakdownSince(db, since) {
2450
2474
  ORDER BY api_equivalent_usd DESC
2451
2475
  `).all(since, since);
2452
2476
  }
2453
- function queryDailyBreakdown(db, days = 30, machine) {
2477
+ function queryDailyBreakdown(db, days = 30, machine, now = new Date) {
2478
+ const bounds = resolveReportingInterval({ kind: "rolling", unit: "day", count: days }, now);
2454
2479
  const machineClause = machine ? " AND machine_id = ?" : "";
2455
- const params = machine ? [`-${days}`, machine] : [`-${days}`];
2480
+ const params = machine ? [bounds.start, bounds.end_exclusive, machine] : [bounds.start, bounds.end_exclusive];
2456
2481
  return db.prepare(`
2457
2482
  SELECT DATE(timestamp) as date, agent, COALESCE(SUM(cost_usd), 0) as cost_usd
2458
2483
  FROM economy_effective_requests
2459
- WHERE timestamp >= DATE('now', ? || ' days')${machineClause}
2484
+ WHERE timestamp >= ? AND timestamp < ?${machineClause}
2460
2485
  GROUP BY DATE(timestamp), agent
2461
2486
  ORDER BY date ASC
2462
2487
  `).all(...params);
2463
2488
  }
2464
- function queryHourlyBreakdown(db, machine, hours) {
2465
- const clauses = hours == null ? [`DATE(timestamp) = DATE('now')`] : [`DATETIME(timestamp) >= DATETIME('now', ?)`];
2466
- const params = hours == null ? [] : [`-${hours} hours`];
2489
+ function queryHourlyBreakdown(db, machine, hours, now = new Date) {
2490
+ const bounds = resolveReportingInterval(hours == null ? { kind: "calendar", period: "today" } : { kind: "rolling", unit: "hour", count: hours }, now);
2491
+ const clauses = hours == null ? ["timestamp >= ? AND timestamp < ?"] : ["STRFTIME('%Y-%m-%dT%H:%M:%fZ', timestamp) >= ? AND STRFTIME('%Y-%m-%dT%H:%M:%fZ', timestamp) < ?"];
2492
+ const params = [bounds.start, bounds.end_exclusive];
2467
2493
  if (machine) {
2468
2494
  clauses.push("machine_id = ?");
2469
2495
  params.push(machine);
@@ -3369,4 +3395,4 @@ function computeCostWithPricing(model, pricing, inputTokens, outputTokens, cache
3369
3395
  return (inputTokens * effective.inputPer1M + outputTokens * effective.outputPer1M + cacheReadTokens * effective.cacheReadPer1M + cacheWriteTokens * effective.cacheWritePer1M + cacheWrite1hTokens * (effective.cacheWrite1hPer1M ?? effective.cacheWritePer1M) + cacheStorageTokenHours * (effective.cacheStoragePer1MHour ?? 0)) / 1e6;
3370
3396
  }
3371
3397
 
3372
- export { __require, AGENTS, COST_BASIS, isAgent, subscriptionId, MAX_INGEST_BYTES, stableJson, canonicalIngestBodyHash, validateIngest, calendarPeriodBounds, calendarPeriodWhere, usageSnapshotFilterForPeriod, sessionOverlapWhere, dataDir, cacheDir, getMachineId, getHomeDir, getResolverDataRoot, getLegacyDataRoot, adoptResolverDataRoot, getExactDataRoot, getDataDir, getDbPath, initSchema, upsertCostCenter, getCostCenter, upsertRequest, upsertSession, rollupSession, querySessions, queryTopSessions, querySummary, queryZeroCostTokenizedModels, queryModelBreakdown, queryAgentBreakdown, queryProjectBreakdown, queryAccountBreakdown, queryCostCenterBreakdown, queryProjectBreakdownSince, queryAgentBreakdownSince, queryModelBreakdownSince, queryAccountBreakdownSince, queryDailyBreakdown, queryHourlyBreakdown, upsertProject, getProject, listProjects, deleteProject, upsertBudget, listBudgets, deleteBudget, getBudgetStatuses, upsertGoal, deleteGoal, listGoals, getGoalStatuses, getIngestState, loadIngestState, setIngestState, queryRequestsSince, insertFeedback, upsertBillingDaily, clearBillingRange, queryBillingSummary, countBillingRecords, getSessionDetail, listMachines, upsertModelPricing, getModelPricing, listModelPricing, deleteModelPricing, seedModelPricing, upsertSubscription, listSubscriptions, deleteSubscription, upsertUsageSnapshot, upsertScanReceipt, queryScanReceipts, queryUsageSnapshots, listMachineRegistry, dedupeRequests, bulkIngest, DEFAULT_PRICING, normalizeModelName, ensurePricingSeeded, getPricingFromDb, getPricing, computeCost, computeCostFromDb, UnknownModelPricingError, assessCostFromDb, getPricingFromRows, estimateCostFromRows, assessCostFromRows, hasKnownUsagePrice, requireCurrentCostSummary, requireCurrentBillingSummary };
3398
+ export { __require, AGENTS, COST_BASIS, isAgent, subscriptionId, MAX_INGEST_BYTES, stableJson, canonicalIngestBodyHash, validateIngest, resolveReportingInterval, calendarPeriodBounds, calendarPeriodWhere, usageSnapshotFilterForPeriod, sessionOverlapWhere, dataDir, cacheDir, getMachineId, getHomeDir, getResolverDataRoot, getLegacyDataRoot, adoptResolverDataRoot, getExactDataRoot, getDataDir, getDbPath, initSchema, upsertCostCenter, getCostCenter, upsertRequest, upsertSession, rollupSession, querySessions, queryTopSessions, querySummary, queryZeroCostTokenizedModels, queryModelBreakdown, queryAgentBreakdown, queryProjectBreakdown, queryAccountBreakdown, queryCostCenterBreakdown, queryProjectBreakdownSince, queryAgentBreakdownSince, queryModelBreakdownSince, queryAccountBreakdownSince, queryDailyBreakdown, queryHourlyBreakdown, upsertProject, getProject, listProjects, deleteProject, upsertBudget, listBudgets, deleteBudget, getBudgetStatuses, upsertGoal, deleteGoal, listGoals, getGoalStatuses, getIngestState, loadIngestState, setIngestState, queryRequestsSince, insertFeedback, upsertBillingDaily, clearBillingRange, queryBillingSummary, countBillingRecords, getSessionDetail, listMachines, upsertModelPricing, getModelPricing, listModelPricing, deleteModelPricing, seedModelPricing, upsertSubscription, listSubscriptions, deleteSubscription, upsertUsageSnapshot, upsertScanReceipt, queryScanReceipts, queryUsageSnapshots, listMachineRegistry, dedupeRequests, bulkIngest, DEFAULT_PRICING, normalizeModelName, ensurePricingSeeded, getPricingFromDb, getPricing, computeCost, computeCostFromDb, UnknownModelPricingError, assessCostFromDb, getPricingFromRows, estimateCostFromRows, assessCostFromRows, hasKnownUsagePrice, requireCurrentCostSummary, requireCurrentBillingSummary };
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  clearBillingRange,
4
4
  upsertBillingDaily
5
- } from "./index-ez9thsr7.js";
5
+ } from "./index-f5jv6q5q.js";
6
6
 
7
7
  // src/ingest/billing.ts
8
8
  import { readFileSync } from "fs";
@@ -1097,12 +1097,32 @@ function meteredBudgetAmounts(groups, limit, threshold) {
1097
1097
  function ymd(date2) {
1098
1098
  return date2.toISOString().substring(0, 10);
1099
1099
  }
1100
- function calendarPeriodBounds(period, now = new Date) {
1100
+ function resolveReportingInterval(interval, now = new Date) {
1101
1101
  if (!Number.isFinite(now.getTime()))
1102
1102
  throw new RangeError("Invalid reporting clock");
1103
- const start = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
1103
+ const start = new Date(now);
1104
+ start.setUTCHours(0, 0, 0, 0);
1104
1105
  const end = new Date(start);
1105
- switch (period) {
1106
+ if (interval.kind === "rolling") {
1107
+ if (!Number.isSafeInteger(interval.count) || interval.count < 1)
1108
+ throw new RangeError("Invalid reporting window");
1109
+ if (interval.unit === "hour") {
1110
+ start.setTime(now.getTime() - interval.count * 3600000);
1111
+ if (!Number.isFinite(start.getTime()))
1112
+ throw new RangeError("Invalid reporting window");
1113
+ return { time_zone: "UTC", start: start.toISOString(), end_exclusive: now.toISOString() };
1114
+ }
1115
+ if (interval.unit !== "day")
1116
+ throw new RangeError("Invalid reporting unit");
1117
+ start.setUTCDate(start.getUTCDate() - interval.count + 1);
1118
+ end.setUTCDate(end.getUTCDate() + 1);
1119
+ if (!Number.isFinite(start.getTime()) || !Number.isFinite(end.getTime()))
1120
+ throw new RangeError("Invalid reporting window");
1121
+ return { time_zone: "UTC", start: ymd(start), end_exclusive: ymd(end) };
1122
+ }
1123
+ if (interval.kind !== "calendar")
1124
+ throw new RangeError("Invalid reporting interval");
1125
+ switch (interval.period) {
1106
1126
  case "today":
1107
1127
  end.setUTCDate(end.getUTCDate() + 1);
1108
1128
  break;
@@ -1125,11 +1145,15 @@ function calendarPeriodBounds(period, now = new Date) {
1125
1145
  end.setUTCFullYear(end.getUTCFullYear() + 1);
1126
1146
  break;
1127
1147
  case "all":
1128
- return { time_zone: "UTC", start_date: null, end_date_exclusive: null };
1148
+ return { time_zone: "UTC", start: null, end_exclusive: null };
1129
1149
  default:
1130
1150
  throw new RangeError("Invalid reporting period");
1131
1151
  }
1132
- return { time_zone: "UTC", start_date: ymd(start), end_date_exclusive: ymd(end) };
1152
+ return { time_zone: "UTC", start: ymd(start), end_exclusive: ymd(end) };
1153
+ }
1154
+ function calendarPeriodBounds(period, now = new Date) {
1155
+ const bounds = resolveReportingInterval({ kind: "calendar", period }, now);
1156
+ return { time_zone: bounds.time_zone, start_date: bounds.start, end_date_exclusive: bounds.end_exclusive };
1133
1157
  }
1134
1158
  function calendarPeriodWhere(period, column, now = new Date) {
1135
1159
  if (!/^[a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)?$/i.test(column)) {
@@ -2518,20 +2542,22 @@ function queryAccountBreakdownSince(db, since) {
2518
2542
  ORDER BY api_equivalent_usd DESC
2519
2543
  `).all(since, since);
2520
2544
  }
2521
- function queryDailyBreakdown(db, days = 30, machine) {
2545
+ function queryDailyBreakdown(db, days = 30, machine, now = new Date) {
2546
+ const bounds = resolveReportingInterval({ kind: "rolling", unit: "day", count: days }, now);
2522
2547
  const machineClause = machine ? " AND machine_id = ?" : "";
2523
- const params = machine ? [`-${days}`, machine] : [`-${days}`];
2548
+ const params = machine ? [bounds.start, bounds.end_exclusive, machine] : [bounds.start, bounds.end_exclusive];
2524
2549
  return db.prepare(`
2525
2550
  SELECT DATE(timestamp) as date, agent, COALESCE(SUM(cost_usd), 0) as cost_usd
2526
2551
  FROM economy_effective_requests
2527
- WHERE timestamp >= DATE('now', ? || ' days')${machineClause}
2552
+ WHERE timestamp >= ? AND timestamp < ?${machineClause}
2528
2553
  GROUP BY DATE(timestamp), agent
2529
2554
  ORDER BY date ASC
2530
2555
  `).all(...params);
2531
2556
  }
2532
- function queryHourlyBreakdown(db, machine, hours) {
2533
- const clauses = hours == null ? [`DATE(timestamp) = DATE('now')`] : [`DATETIME(timestamp) >= DATETIME('now', ?)`];
2534
- const params = hours == null ? [] : [`-${hours} hours`];
2557
+ function queryHourlyBreakdown(db, machine, hours, now = new Date) {
2558
+ const bounds = resolveReportingInterval(hours == null ? { kind: "calendar", period: "today" } : { kind: "rolling", unit: "hour", count: hours }, now);
2559
+ const clauses = hours == null ? ["timestamp >= ? AND timestamp < ?"] : ["STRFTIME('%Y-%m-%dT%H:%M:%fZ', timestamp) >= ? AND STRFTIME('%Y-%m-%dT%H:%M:%fZ', timestamp) < ?"];
2560
+ const params = [bounds.start, bounds.end_exclusive];
2535
2561
  if (machine) {
2536
2562
  clauses.push("machine_id = ?");
2537
2563
  params.push(machine);
@@ -3437,4 +3463,4 @@ function computeCostWithPricing(model, pricing, inputTokens, outputTokens, cache
3437
3463
  return (inputTokens * effective.inputPer1M + outputTokens * effective.outputPer1M + cacheReadTokens * effective.cacheReadPer1M + cacheWriteTokens * effective.cacheWritePer1M + cacheWrite1hTokens * (effective.cacheWrite1hPer1M ?? effective.cacheWritePer1M) + cacheStorageTokenHours * (effective.cacheStoragePer1MHour ?? 0)) / 1e6;
3438
3464
  }
3439
3465
 
3440
- export { OBSERVATION_PG_MIGRATIONS, IngestError, observationId, subscriptionId, scanReceiptId, AGENTS, isAgent, parseAgent, MAX_INGEST_ROWS, MAX_INGEST_BYTES, stableJson, canonicalIngestBodyHash, validateIngest, INGEST_SCHEMA_SQL, LEGACY_PG_SQL, reconcileIngestReceipt, calendarPeriodBounds, calendarPeriodWhere, usageSnapshotFilterForPeriod, sessionOverlapWhere, dataDir, cacheDir, getMachineId, getHomeDir, getResolverDataRoot, getLegacyDataRoot, adoptResolverDataRoot, getExactDataRoot, getDataDir, getDbPath, initSchema, upsertCostCenter, getCostCenter, upsertRequest, upsertSession, rollupSession, querySessions, queryTopSessions, querySummary, queryZeroCostTokenizedModels, queryModelBreakdown, queryAgentBreakdown, queryProjectBreakdown, queryAccountBreakdown, queryCostCenterBreakdown, queryProjectBreakdownSince, queryAgentBreakdownSince, queryModelBreakdownSince, queryAccountBreakdownSince, queryDailyBreakdown, queryHourlyBreakdown, upsertProject, getProject, listProjects, deleteProject, upsertBudget, listBudgets, deleteBudget, getBudgetStatuses, upsertGoal, deleteGoal, listGoals, getGoalStatuses, getIngestState, loadIngestState, setIngestState, queryRequestsSince, insertFeedback, upsertBillingDaily, clearBillingRange, queryBillingSummary, countBillingRecords, getSessionDetail, listMachines, upsertModelPricing, getModelPricing, listModelPricing, deleteModelPricing, seedModelPricing, upsertSubscription, listSubscriptions, deleteSubscription, upsertUsageSnapshot, upsertScanReceipt, queryScanReceipts, queryUsageSnapshots, listMachineRegistry, dedupeRequests, bulkIngest, DEFAULT_PRICING, normalizeModelName, ensurePricingSeeded, getPricingFromDb, getPricing, computeCost, computeCostFromDb, UnknownModelPricingError, assessCostFromDb, getPricingFromRows, estimateCostFromRows, assessCostFromRows, formatReportedUsd, formatPlanMoney, hasKnownUsagePrice, requireCurrentCostSummary, formatSummaryValue, formatSummaryCoverage, requireCurrentBillingSummary, formatBillingValue };
3466
+ export { OBSERVATION_PG_MIGRATIONS, IngestError, observationId, subscriptionId, scanReceiptId, AGENTS, isAgent, parseAgent, MAX_INGEST_ROWS, MAX_INGEST_BYTES, stableJson, canonicalIngestBodyHash, validateIngest, INGEST_SCHEMA_SQL, LEGACY_PG_SQL, reconcileIngestReceipt, resolveReportingInterval, calendarPeriodBounds, calendarPeriodWhere, usageSnapshotFilterForPeriod, sessionOverlapWhere, dataDir, cacheDir, getMachineId, getHomeDir, getResolverDataRoot, getLegacyDataRoot, adoptResolverDataRoot, getExactDataRoot, getDataDir, getDbPath, initSchema, upsertCostCenter, getCostCenter, upsertRequest, upsertSession, rollupSession, querySessions, queryTopSessions, querySummary, queryZeroCostTokenizedModels, queryModelBreakdown, queryAgentBreakdown, queryProjectBreakdown, queryAccountBreakdown, queryCostCenterBreakdown, queryProjectBreakdownSince, queryAgentBreakdownSince, queryModelBreakdownSince, queryAccountBreakdownSince, queryDailyBreakdown, queryHourlyBreakdown, upsertProject, getProject, listProjects, deleteProject, upsertBudget, listBudgets, deleteBudget, getBudgetStatuses, upsertGoal, deleteGoal, listGoals, getGoalStatuses, getIngestState, loadIngestState, setIngestState, queryRequestsSince, insertFeedback, upsertBillingDaily, clearBillingRange, queryBillingSummary, countBillingRecords, getSessionDetail, listMachines, upsertModelPricing, getModelPricing, listModelPricing, deleteModelPricing, seedModelPricing, upsertSubscription, listSubscriptions, deleteSubscription, upsertUsageSnapshot, upsertScanReceipt, queryScanReceipts, queryUsageSnapshots, listMachineRegistry, dedupeRequests, bulkIngest, DEFAULT_PRICING, normalizeModelName, ensurePricingSeeded, getPricingFromDb, getPricing, computeCost, computeCostFromDb, UnknownModelPricingError, assessCostFromDb, getPricingFromRows, estimateCostFromRows, assessCostFromRows, formatReportedUsd, formatPlanMoney, hasKnownUsagePrice, requireCurrentCostSummary, formatSummaryValue, formatSummaryCoverage, requireCurrentBillingSummary, formatBillingValue };
@@ -14,7 +14,7 @@ import {
14
14
  querySavingsSummary,
15
15
  requireCurrentBillingDiffSummary,
16
16
  requireCurrentSavingsReport
17
- } from "./index-dj3tctqh.js";
17
+ } from "./index-1vz1arz5.js";
18
18
  import {
19
19
  deleteBudget,
20
20
  deleteGoal,
@@ -59,7 +59,7 @@ import {
59
59
  upsertSubscription,
60
60
  usageSnapshotFilterForPeriod,
61
61
  validateIngest
62
- } from "./index-ez9thsr7.js";
62
+ } from "./index-f5jv6q5q.js";
63
63
  import {
64
64
  __require
65
65
  } from "./index-p35cyhc4.js";
@@ -92,7 +92,7 @@ class LocalStore {
92
92
  _db;
93
93
  async db() {
94
94
  if (!this._db) {
95
- const { openDatabase } = await import("./sqlite-store-501f32ts.js");
95
+ const { openDatabase } = await import("./sqlite-store-13nhr1z0.js");
96
96
  this._db = openDatabase();
97
97
  ensurePricingSeeded(this._db);
98
98
  }
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  LOCAL_STORAGE_OPT_IN_KEYS
4
- } from "./index-dj3tctqh.js";
4
+ } from "./index-1vz1arz5.js";
5
5
  import {
6
6
  assessCostFromDb,
7
7
  getIngestState,
@@ -19,7 +19,7 @@ import {
19
19
  upsertSession,
20
20
  upsertSubscription,
21
21
  upsertUsageSnapshot
22
- } from "./index-ez9thsr7.js";
22
+ } from "./index-f5jv6q5q.js";
23
23
  import {
24
24
  __require
25
25
  } from "./index-p35cyhc4.js";
@@ -1064,12 +1064,32 @@ function meteredBudgetAmounts(groups, limit, threshold) {
1064
1064
  function ymd(date2) {
1065
1065
  return date2.toISOString().substring(0, 10);
1066
1066
  }
1067
- function calendarPeriodBounds(period, now = new Date) {
1067
+ function resolveReportingInterval(interval, now = new Date) {
1068
1068
  if (!Number.isFinite(now.getTime()))
1069
1069
  throw new RangeError("Invalid reporting clock");
1070
- const start = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
1070
+ const start = new Date(now);
1071
+ start.setUTCHours(0, 0, 0, 0);
1071
1072
  const end = new Date(start);
1072
- switch (period) {
1073
+ if (interval.kind === "rolling") {
1074
+ if (!Number.isSafeInteger(interval.count) || interval.count < 1)
1075
+ throw new RangeError("Invalid reporting window");
1076
+ if (interval.unit === "hour") {
1077
+ start.setTime(now.getTime() - interval.count * 3600000);
1078
+ if (!Number.isFinite(start.getTime()))
1079
+ throw new RangeError("Invalid reporting window");
1080
+ return { time_zone: "UTC", start: start.toISOString(), end_exclusive: now.toISOString() };
1081
+ }
1082
+ if (interval.unit !== "day")
1083
+ throw new RangeError("Invalid reporting unit");
1084
+ start.setUTCDate(start.getUTCDate() - interval.count + 1);
1085
+ end.setUTCDate(end.getUTCDate() + 1);
1086
+ if (!Number.isFinite(start.getTime()) || !Number.isFinite(end.getTime()))
1087
+ throw new RangeError("Invalid reporting window");
1088
+ return { time_zone: "UTC", start: ymd(start), end_exclusive: ymd(end) };
1089
+ }
1090
+ if (interval.kind !== "calendar")
1091
+ throw new RangeError("Invalid reporting interval");
1092
+ switch (interval.period) {
1073
1093
  case "today":
1074
1094
  end.setUTCDate(end.getUTCDate() + 1);
1075
1095
  break;
@@ -1092,11 +1112,15 @@ function calendarPeriodBounds(period, now = new Date) {
1092
1112
  end.setUTCFullYear(end.getUTCFullYear() + 1);
1093
1113
  break;
1094
1114
  case "all":
1095
- return { time_zone: "UTC", start_date: null, end_date_exclusive: null };
1115
+ return { time_zone: "UTC", start: null, end_exclusive: null };
1096
1116
  default:
1097
1117
  throw new RangeError("Invalid reporting period");
1098
1118
  }
1099
- return { time_zone: "UTC", start_date: ymd(start), end_date_exclusive: ymd(end) };
1119
+ return { time_zone: "UTC", start: ymd(start), end_exclusive: ymd(end) };
1120
+ }
1121
+ function calendarPeriodBounds(period, now = new Date) {
1122
+ const bounds = resolveReportingInterval({ kind: "calendar", period }, now);
1123
+ return { time_zone: bounds.time_zone, start_date: bounds.start, end_date_exclusive: bounds.end_exclusive };
1100
1124
  }
1101
1125
  function calendarPeriodWhere(period, column, now = new Date) {
1102
1126
  if (!/^[a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)?$/i.test(column)) {
@@ -2485,20 +2509,22 @@ function queryAccountBreakdownSince(db, since) {
2485
2509
  ORDER BY api_equivalent_usd DESC
2486
2510
  `).all(since, since);
2487
2511
  }
2488
- function queryDailyBreakdown(db, days = 30, machine) {
2512
+ function queryDailyBreakdown(db, days = 30, machine, now = new Date) {
2513
+ const bounds = resolveReportingInterval({ kind: "rolling", unit: "day", count: days }, now);
2489
2514
  const machineClause = machine ? " AND machine_id = ?" : "";
2490
- const params = machine ? [`-${days}`, machine] : [`-${days}`];
2515
+ const params = machine ? [bounds.start, bounds.end_exclusive, machine] : [bounds.start, bounds.end_exclusive];
2491
2516
  return db.prepare(`
2492
2517
  SELECT DATE(timestamp) as date, agent, COALESCE(SUM(cost_usd), 0) as cost_usd
2493
2518
  FROM economy_effective_requests
2494
- WHERE timestamp >= DATE('now', ? || ' days')${machineClause}
2519
+ WHERE timestamp >= ? AND timestamp < ?${machineClause}
2495
2520
  GROUP BY DATE(timestamp), agent
2496
2521
  ORDER BY date ASC
2497
2522
  `).all(...params);
2498
2523
  }
2499
- function queryHourlyBreakdown(db, machine, hours) {
2500
- const clauses = hours == null ? [`DATE(timestamp) = DATE('now')`] : [`DATETIME(timestamp) >= DATETIME('now', ?)`];
2501
- const params = hours == null ? [] : [`-${hours} hours`];
2524
+ function queryHourlyBreakdown(db, machine, hours, now = new Date) {
2525
+ const bounds = resolveReportingInterval(hours == null ? { kind: "calendar", period: "today" } : { kind: "rolling", unit: "hour", count: hours }, now);
2526
+ const clauses = hours == null ? ["timestamp >= ? AND timestamp < ?"] : ["STRFTIME('%Y-%m-%dT%H:%M:%fZ', timestamp) >= ? AND STRFTIME('%Y-%m-%dT%H:%M:%fZ', timestamp) < ?"];
2527
+ const params = [bounds.start, bounds.end_exclusive];
2502
2528
  if (machine) {
2503
2529
  clauses.push("machine_id = ?");
2504
2530
  params.push(machine);
@@ -3404,4 +3430,4 @@ function computeCostWithPricing(model, pricing, inputTokens, outputTokens, cache
3404
3430
  return (inputTokens * effective.inputPer1M + outputTokens * effective.outputPer1M + cacheReadTokens * effective.cacheReadPer1M + cacheWriteTokens * effective.cacheWritePer1M + cacheWrite1hTokens * (effective.cacheWrite1hPer1M ?? effective.cacheWritePer1M) + cacheStorageTokenHours * (effective.cacheStoragePer1MHour ?? 0)) / 1e6;
3405
3431
  }
3406
3432
 
3407
- export { observationId, subscriptionId, scanReceiptId, AGENTS, MAX_INGEST_ROWS, MAX_INGEST_BYTES, stableJson, canonicalIngestBodyHash, validateIngest, calendarPeriodBounds, calendarPeriodWhere, usageSnapshotFilterForPeriod, sessionOverlapWhere, dataDir, cacheDir, getMachineId, getHomeDir, getResolverDataRoot, getLegacyDataRoot, adoptResolverDataRoot, getExactDataRoot, getDataDir, getDbPath, initSchema, upsertCostCenter, getCostCenter, upsertRequest, upsertSession, rollupSession, querySessions, queryTopSessions, querySummary, queryZeroCostTokenizedModels, queryModelBreakdown, queryAgentBreakdown, queryProjectBreakdown, queryAccountBreakdown, queryCostCenterBreakdown, queryProjectBreakdownSince, queryAgentBreakdownSince, queryModelBreakdownSince, queryAccountBreakdownSince, queryDailyBreakdown, queryHourlyBreakdown, upsertProject, getProject, listProjects, deleteProject, upsertBudget, listBudgets, deleteBudget, getBudgetStatuses, upsertGoal, deleteGoal, listGoals, getGoalStatuses, getIngestState, loadIngestState, setIngestState, queryRequestsSince, insertFeedback, upsertBillingDaily, clearBillingRange, queryBillingSummary, countBillingRecords, getSessionDetail, listMachines, upsertModelPricing, getModelPricing, listModelPricing, deleteModelPricing, seedModelPricing, upsertSubscription, listSubscriptions, deleteSubscription, upsertUsageSnapshot, upsertScanReceipt, queryScanReceipts, queryUsageSnapshots, listMachineRegistry, dedupeRequests, bulkIngest, DEFAULT_PRICING, normalizeModelName, ensurePricingSeeded, getPricingFromDb, getPricing, computeCost, computeCostFromDb, UnknownModelPricingError, assessCostFromDb, getPricingFromRows, estimateCostFromRows, assessCostFromRows, formatReportedUsd, formatPlanMoney, hasKnownUsagePrice, requireCurrentCostSummary, formatSummaryValue, formatSummaryCoverage, requireCurrentBillingSummary, formatBillingValue };
3433
+ export { observationId, subscriptionId, scanReceiptId, AGENTS, MAX_INGEST_ROWS, MAX_INGEST_BYTES, stableJson, canonicalIngestBodyHash, validateIngest, resolveReportingInterval, calendarPeriodBounds, calendarPeriodWhere, usageSnapshotFilterForPeriod, sessionOverlapWhere, dataDir, cacheDir, getMachineId, getHomeDir, getResolverDataRoot, getLegacyDataRoot, adoptResolverDataRoot, getExactDataRoot, getDataDir, getDbPath, initSchema, upsertCostCenter, getCostCenter, upsertRequest, upsertSession, rollupSession, querySessions, queryTopSessions, querySummary, queryZeroCostTokenizedModels, queryModelBreakdown, queryAgentBreakdown, queryProjectBreakdown, queryAccountBreakdown, queryCostCenterBreakdown, queryProjectBreakdownSince, queryAgentBreakdownSince, queryModelBreakdownSince, queryAccountBreakdownSince, queryDailyBreakdown, queryHourlyBreakdown, upsertProject, getProject, listProjects, deleteProject, upsertBudget, listBudgets, deleteBudget, getBudgetStatuses, upsertGoal, deleteGoal, listGoals, getGoalStatuses, getIngestState, loadIngestState, setIngestState, queryRequestsSince, insertFeedback, upsertBillingDaily, clearBillingRange, queryBillingSummary, countBillingRecords, getSessionDetail, listMachines, upsertModelPricing, getModelPricing, listModelPricing, deleteModelPricing, seedModelPricing, upsertSubscription, listSubscriptions, deleteSubscription, upsertUsageSnapshot, upsertScanReceipt, queryScanReceipts, queryUsageSnapshots, listMachineRegistry, dedupeRequests, bulkIngest, DEFAULT_PRICING, normalizeModelName, ensurePricingSeeded, getPricingFromDb, getPricing, computeCost, computeCostFromDb, UnknownModelPricingError, assessCostFromDb, getPricingFromRows, estimateCostFromRows, assessCostFromRows, formatReportedUsd, formatPlanMoney, hasKnownUsagePrice, requireCurrentCostSummary, formatSummaryValue, formatSummaryCoverage, requireCurrentBillingSummary, formatBillingValue };
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  upsertProject
4
- } from "./index-ez9thsr7.js";
4
+ } from "./index-f5jv6q5q.js";
5
5
  import"./index-p35cyhc4.js";
6
6
 
7
7
  // src/lib/open-projects.ts
@@ -12,7 +12,7 @@ import {
12
12
  getPricingFromDb,
13
13
  getPricingFromRows,
14
14
  normalizeModelName
15
- } from "./index-2hwzmfgv.js";
15
+ } from "./index-f5jv6q5q.js";
16
16
  import"./index-p35cyhc4.js";
17
17
  export {
18
18
  normalizeModelName,
@@ -12,7 +12,7 @@ import {
12
12
  getPricingFromDb,
13
13
  getPricingFromRows,
14
14
  normalizeModelName
15
- } from "./index-zpvea08j.js";
15
+ } from "./index-c7h2rxx1.js";
16
16
  export {
17
17
  normalizeModelName,
18
18
  getPricingFromRows,
@@ -12,7 +12,7 @@ import {
12
12
  getPricingFromDb,
13
13
  getPricingFromRows,
14
14
  normalizeModelName
15
- } from "./index-ez9thsr7.js";
15
+ } from "./index-vajzfzgv.js";
16
16
  import"./index-p35cyhc4.js";
17
17
  export {
18
18
  normalizeModelName,
@@ -5,7 +5,7 @@ import {
5
5
  } from "./index-w3bvv0ns.js";
6
6
  import {
7
7
  syncAll
8
- } from "./index-npsjjv96.js";
8
+ } from "./index-s9mvven6.js";
9
9
  import {
10
10
  buildBrief,
11
11
  packageMetadata,
@@ -16,7 +16,7 @@ import {
16
16
  queryProjectDetail,
17
17
  queryRangeStats,
18
18
  querySavingsSummary
19
- } from "./index-dj3tctqh.js";
19
+ } from "./index-1vz1arz5.js";
20
20
  import {
21
21
  AGENTS,
22
22
  INGEST_SCHEMA_SQL,
@@ -59,6 +59,7 @@ import {
59
59
  queryTopSessions,
60
60
  queryUsageSnapshots,
61
61
  reconcileIngestReceipt,
62
+ resolveReportingInterval,
62
63
  subscriptionId,
63
64
  upsertBudget,
64
65
  upsertGoal,
@@ -66,7 +67,7 @@ import {
66
67
  upsertProject,
67
68
  upsertSubscription,
68
69
  usageSnapshotFilterForPeriod
69
- } from "./index-ez9thsr7.js";
70
+ } from "./index-f5jv6q5q.js";
70
71
  import {
71
72
  __require
72
73
  } from "./index-p35cyhc4.js";
@@ -133,15 +134,16 @@ function translateSqliteDates(sql) {
133
134
  s = s.replace(/DATE\('now',\s*(\$\d+)\s*\|\|\s*' days'\)/gi, (_m, p) => isoDate(`now() + ((${p}) || ' days')::interval`));
134
135
  s = s.replace(/DATETIME\('now',\s*(\$\d+)\)/gi, (_m, p) => `(now() + (${p})::interval)`);
135
136
  s = s.replace(/DATETIME\('now',\s*'([^']+)'\)/gi, (_m, lit) => `(now() + interval '${lit}')`);
137
+ s = s.replace(/STRFTIME\('%Y-%m-%dT%H:%M:%fZ',\s*([A-Za-z_][A-Za-z0-9_]*)\)/gi, (_m, col) => `to_char(CAST("${col}" AS timestamptz) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`);
136
138
  const strftimeMap = { "%H": "HH24", "%Y": "YYYY", "%m": "MM", "%d": "DD", "%M": "MI", "%S": "SS" };
137
139
  s = s.replace(/STRFTIME\('([^']+)',\s*([A-Za-z_][A-Za-z0-9_]*)\)/gi, (_m, fmt, col) => {
138
140
  const pgFmt = Object.entries(strftimeMap).reduce((acc, [k, v]) => acc.split(k).join(v), fmt);
139
- return `to_char(CAST("${col}" AS timestamptz), '${pgFmt}')`;
141
+ return `to_char(CAST("${col}" AS timestamptz) AT TIME ZONE 'UTC', '${pgFmt}')`;
140
142
  });
141
143
  s = s.replace(/\bDATE\('now'\)/gi, isoDate("now()"));
142
144
  s = s.replace(/\bDATETIME\('now'\)/gi, "now()");
143
145
  s = s.replace(/\bDATETIME\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)/gi, (_m, col) => `CAST("${col}" AS timestamptz)`);
144
- s = s.replace(/\bDATE\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)/gi, (_m, col) => `substr("${col}", 1, 10)`);
146
+ s = s.replace(/\bDATE\(\s*([A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?)\s*\)/gi, (_m, col) => `substr(${col.split(".").map((part) => `"${part}"`).join(".")}, 1, 10)`);
145
147
  return s;
146
148
  }
147
149
  function resolveWorkerPath() {
@@ -3836,7 +3838,13 @@ function createDataHandler(db, actorId) {
3836
3838
  if (path === "/api/daily" && method === "GET") {
3837
3839
  const days = Number(url.searchParams.get("days") ?? 30);
3838
3840
  const machine = url.searchParams.get("machine") ?? undefined;
3839
- return ok(queryDailyBreakdown(db, days, machine));
3841
+ const now = new Date;
3842
+ try {
3843
+ resolveReportingInterval({ kind: "rolling", unit: "day", count: days }, now);
3844
+ } catch {
3845
+ return err("days must be a positive integer within the supported UTC date range");
3846
+ }
3847
+ return ok(queryDailyBreakdown(db, days, machine, now));
3840
3848
  }
3841
3849
  if (path === "/api/hourly" && method === "GET") {
3842
3850
  const machine = url.searchParams.get("machine") ?? undefined;
@@ -3902,7 +3910,7 @@ function createDataHandler(db, actorId) {
3902
3910
  if (providers.some((provider) => !allowedProviders.has(provider)))
3903
3911
  return err("invalid billing provider");
3904
3912
  const results = {};
3905
- const { syncAnthropicBilling, syncOpenAIBilling, syncGeminiBilling } = await import("./billing-bkpxy4kv.js");
3913
+ const { syncAnthropicBilling, syncOpenAIBilling, syncGeminiBilling } = await import("./billing-22az60hy.js");
3906
3914
  async function capture(provider, fn) {
3907
3915
  try {
3908
3916
  results[provider] = await fn();
@@ -4057,7 +4065,7 @@ function createDataHandler(db, actorId) {
4057
4065
  const results = {};
4058
4066
  if (sources === "all") {
4059
4067
  try {
4060
- const { syncOpenProjectsRegistry } = await import("./open-projects-6z4pq6mx.js");
4068
+ const { syncOpenProjectsRegistry } = await import("./open-projects-8qm46m3d.js");
4061
4069
  results["projects"] = await syncOpenProjectsRegistry(db);
4062
4070
  } catch {}
4063
4071
  }
@@ -4065,7 +4073,7 @@ function createDataHandler(db, actorId) {
4065
4073
  const syncResult = await syncAll(db, selected);
4066
4074
  Object.assign(results, syncResult);
4067
4075
  try {
4068
- const { checkAndFireWebhooks } = await import("./webhooks-k4fh2t9c.js");
4076
+ const { checkAndFireWebhooks } = await import("./webhooks-nw4daw9y.js");
4069
4077
  await checkAndFireWebhooks(db);
4070
4078
  } catch {}
4071
4079
  return ok(results);
@@ -4089,7 +4097,7 @@ function createDataHandler(db, actorId) {
4089
4097
  return err("invalid JSON body");
4090
4098
  const result = bulkIngest(db, body, { actorId });
4091
4099
  try {
4092
- const { checkAndFireWebhooks } = await import("./webhooks-k4fh2t9c.js");
4100
+ const { checkAndFireWebhooks } = await import("./webhooks-nw4daw9y.js");
4093
4101
  if (body["budget_mode"] !== "create")
4094
4102
  await checkAndFireWebhooks(db);
4095
4103
  } catch {}
@@ -69,7 +69,7 @@ import {
69
69
  upsertSession,
70
70
  upsertSubscription,
71
71
  upsertUsageSnapshot
72
- } from "./index-2hwzmfgv.js";
72
+ } from "./index-f5jv6q5q.js";
73
73
  import {
74
74
  __require
75
75
  } from "./index-p35cyhc4.js";
@@ -180,7 +180,7 @@ function openDatabase(dbPath, skipSeed = false) {
180
180
  }
181
181
  }, `opening ${path}`);
182
182
  if (!skipSeed) {
183
- import("./pricing-vf2pycmh.js").then(({ ensurePricingSeeded }) => ensurePricingSeeded(db)).catch(() => {});
183
+ import("./pricing-462f8xkm.js").then(({ ensurePricingSeeded }) => ensurePricingSeeded(db)).catch(() => {});
184
184
  }
185
185
  return db;
186
186
  }