@hasna/economy 0.5.8 → 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.9
4
+
5
+ ### Patch Changes
6
+
7
+ - Give each journal recovery transaction its own bounded deadline so slow storage does not interrupt healthy collection, and stop ingestion whenever cleanup leaves unresolved recovery state.
8
+
3
9
  ## 0.5.8
4
10
 
5
11
  ### Patch Changes
@@ -15,7 +15,7 @@ import {
15
15
  requireCurrentBillingDiffSummary,
16
16
  requireCurrentRangeStats,
17
17
  requireCurrentSavingsReport
18
- } from "./index-h2mcj7gv.js";
18
+ } from "./index-spwpgdbb.js";
19
19
  import {
20
20
  deleteBudget,
21
21
  deleteGoal,
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  LOCAL_STORAGE_OPT_IN_KEYS
4
- } from "./index-h2mcj7gv.js";
4
+ } from "./index-spwpgdbb.js";
5
5
  import {
6
6
  assessCostFromDb,
7
7
  getIngestState,
@@ -86,7 +86,7 @@ function comparisonValueChange(left, right) {
86
86
  // package.json
87
87
  var package_default = {
88
88
  name: "@hasna/economy",
89
- version: "0.5.8",
89
+ version: "0.5.9",
90
90
  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",
91
91
  repository: {
92
92
  type: "git",
@@ -4,7 +4,7 @@ import {
4
4
  } from "./index-w3bvv0ns.js";
5
5
  import {
6
6
  getStore
7
- } from "./index-a1gh3d6t.js";
7
+ } from "./index-92x56q4h.js";
8
8
  import {
9
9
  formatReportedUsd,
10
10
  formatSummaryCoverage,
@@ -5,7 +5,7 @@ import {
5
5
  } from "./index-w3bvv0ns.js";
6
6
  import {
7
7
  syncAll
8
- } from "./index-9za4cmq2.js";
8
+ } from "./index-970htsxe.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-h2mcj7gv.js";
19
+ } from "./index-spwpgdbb.js";
20
20
  import {
21
21
  AGENTS,
22
22
  INGEST_SCHEMA_SQL,
@@ -6,10 +6,10 @@ import {
6
6
  printStatusLine,
7
7
  printWaybarJson,
8
8
  runTui
9
- } from "./index-gf3kan31.js";
9
+ } from "./index-twkdyrw4.js";
10
10
  import"./index-w3bvv0ns.js";
11
- import"./index-a1gh3d6t.js";
12
- import"./index-h2mcj7gv.js";
11
+ import"./index-92x56q4h.js";
12
+ import"./index-spwpgdbb.js";
13
13
  import"./index-zgvmrqyc.js";
14
14
  import"./index-p35cyhc4.js";
15
15
  export {
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  agentPaths,
4
4
  syncAll
5
- } from "./index-9za4cmq2.js";
5
+ } from "./index-970htsxe.js";
6
6
  import {
7
7
  getStore,
8
8
  isCloudStore
9
- } from "./index-a1gh3d6t.js";
10
- import"./index-h2mcj7gv.js";
9
+ } from "./index-92x56q4h.js";
10
+ import"./index-spwpgdbb.js";
11
11
  import {
12
12
  formatSummaryValue
13
13
  } from "./index-zgvmrqyc.js";
package/dist/cli/index.js CHANGED
@@ -8,16 +8,16 @@ import {
8
8
  import {
9
9
  buildStatusLine,
10
10
  gatherStatusData
11
- } from "../chunks/index-gf3kan31.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-9za4cmq2.js";
16
+ } from "../chunks/index-970htsxe.js";
17
17
  import {
18
18
  getStore,
19
19
  isCloudStore
20
- } from "../chunks/index-a1gh3d6t.js";
20
+ } from "../chunks/index-92x56q4h.js";
21
21
  import {
22
22
  billingDriftCheck,
23
23
  comparisonValueChange,
@@ -25,7 +25,7 @@ import {
25
25
  economyTransportReport,
26
26
  formatComparisonValue,
27
27
  packageMetadata
28
- } from "../chunks/index-h2mcj7gv.js";
28
+ } from "../chunks/index-spwpgdbb.js";
29
29
  import {
30
30
  AGENTS,
31
31
  MAX_INGEST_BYTES,
@@ -1359,23 +1359,23 @@ function registerExtendedCommands(program) {
1359
1359
  console.log();
1360
1360
  });
1361
1361
  program.command("tui").description("Terminal status dashboard (Linux/SSH friendly)").option("--watch", "Live refresh").option("--interval <seconds>", "Refresh interval", "30").action(async (opts) => {
1362
- const { runTui } = await import("../chunks/tui-s6tpce3h.js");
1362
+ const { runTui } = await import("../chunks/tui-j8brj3wf.js");
1363
1363
  await runTui({
1364
1364
  watch: Boolean(opts.watch),
1365
1365
  interval: Number(opts.interval ?? 30)
1366
1366
  });
1367
1367
  });
1368
1368
  program.command("waybar").description("Print waybar-compatible JSON status line").action(async () => {
1369
- const { printWaybarJson } = await import("../chunks/tui-s6tpce3h.js");
1369
+ const { printWaybarJson } = await import("../chunks/tui-j8brj3wf.js");
1370
1370
  await printWaybarJson();
1371
1371
  });
1372
1372
  const barCmd = program.command("bar").description("Status bar helpers (Linux)");
1373
1373
  barCmd.command("tui").description("Alias for economy tui").option("--watch", "Live refresh").action(async (opts) => {
1374
- const { runTui } = await import("../chunks/tui-s6tpce3h.js");
1374
+ const { runTui } = await import("../chunks/tui-j8brj3wf.js");
1375
1375
  await runTui({ watch: Boolean(opts.watch) });
1376
1376
  });
1377
1377
  barCmd.command("waybar").description("Alias for economy waybar").action(async () => {
1378
- const { printWaybarJson } = await import("../chunks/tui-s6tpce3h.js");
1378
+ const { printWaybarJson } = await import("../chunks/tui-j8brj3wf.js");
1379
1379
  await printWaybarJson();
1380
1380
  });
1381
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) => {
@@ -2024,18 +2024,19 @@ class IngestJournalMaintenance {
2024
2024
  throw new Error("Journal maintenance limit must be 1\u201332");
2025
2025
  await this.recover();
2026
2026
  await verifyJournalAuthority(this.journal, this.cloud);
2027
- const deadline = Date.now() + 30000;
2027
+ const sweepDeadline = Date.now() + 30000;
2028
2028
  let retired = 0, bytes = 0;
2029
2029
  let backend;
2030
2030
  try {
2031
2031
  const names = this.journal.names();
2032
2032
  for (const name of names.filter((n) => n.endsWith(".intent.json"))) {
2033
- if (retired >= limit || Date.now() >= deadline || retired > 0 && bytes >= 1048576)
2033
+ if (retired >= limit || Date.now() >= sweepDeadline || retired > 0 && bytes >= 1048576)
2034
2034
  break;
2035
2035
  if (!names.includes(name.replace(".intent.", ".receipt.")))
2036
2036
  continue;
2037
2037
  const intent = this.journal.intent(name), receipt = this.journal.receipt(intent.command);
2038
- backend ??= await this.backend(deadline);
2038
+ const deadline = Date.now() + 30000;
2039
+ backend = await this.backend(deadline);
2039
2040
  const m = {
2040
2041
  version: 1,
2041
2042
  target: this.journal.target,
@@ -2071,7 +2072,8 @@ class IngestJournalMaintenance {
2071
2072
  try {
2072
2073
  await this.maintain();
2073
2074
  } catch (error) {
2074
- 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))
2075
2077
  throw error;
2076
2078
  }
2077
2079
  if (!this.journal.canStage(command))
@@ -3200,7 +3202,7 @@ program.command("accounts [period]").description("List account usage by email ad
3200
3202
  console.log();
3201
3203
  });
3202
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) => {
3203
- const { watchCosts } = await import("../chunks/watch-cv5va8x6.js");
3205
+ const { watchCosts } = await import("../chunks/watch-ksn2mezm.js");
3204
3206
  await watchCosts({
3205
3207
  interval: parsePositiveCliInteger(opts.interval ?? "10", "--interval"),
3206
3208
  daemon: Boolean(opts.daemon),
@@ -3436,7 +3438,7 @@ pricingCmd.command("remove <model>").description("Remove pricing for a model").a
3436
3438
  });
3437
3439
  program.command("serve").description("Start the REST API server").option("-p, --port <port>", "Port", "3456").action(async (opts) => {
3438
3440
  const port = parseCliPort(opts.port ?? "3456", "--port");
3439
- const { startServer } = await import("../chunks/serve-14cgmx02.js");
3441
+ const { startServer } = await import("../chunks/serve-tmv8zgvh.js");
3440
3442
  startServer(port);
3441
3443
  });
3442
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) => {
package/dist/index.js CHANGED
@@ -111,7 +111,7 @@ import { randomUUID } from "crypto";
111
111
  // package.json
112
112
  var package_default = {
113
113
  name: "@hasna/economy",
114
- version: "0.5.8",
114
+ version: "0.5.9",
115
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",
116
116
  repository: {
117
117
  type: "git",
@@ -2345,18 +2345,19 @@ class IngestJournalMaintenance {
2345
2345
  throw new Error("Journal maintenance limit must be 1\u201332");
2346
2346
  await this.recover();
2347
2347
  await verifyJournalAuthority(this.journal, this.cloud);
2348
- const deadline = Date.now() + 30000;
2348
+ const sweepDeadline = Date.now() + 30000;
2349
2349
  let retired = 0, bytes = 0;
2350
2350
  let backend;
2351
2351
  try {
2352
2352
  const names = this.journal.names();
2353
2353
  for (const name of names.filter((n) => n.endsWith(".intent.json"))) {
2354
- if (retired >= limit || Date.now() >= deadline || retired > 0 && bytes >= 1048576)
2354
+ if (retired >= limit || Date.now() >= sweepDeadline || retired > 0 && bytes >= 1048576)
2355
2355
  break;
2356
2356
  if (!names.includes(name.replace(".intent.", ".receipt.")))
2357
2357
  continue;
2358
2358
  const intent = this.journal.intent(name), receipt = this.journal.receipt(intent.command);
2359
- backend ??= await this.backend(deadline);
2359
+ const deadline = Date.now() + 30000;
2360
+ backend = await this.backend(deadline);
2360
2361
  const m = {
2361
2362
  version: 1,
2362
2363
  target: this.journal.target,
@@ -2392,7 +2393,8 @@ class IngestJournalMaintenance {
2392
2393
  try {
2393
2394
  await this.maintain();
2394
2395
  } catch (error) {
2395
- 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))
2396
2398
  throw error;
2397
2399
  }
2398
2400
  if (!this.journal.canStage(command))
@@ -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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"}
package/dist/mcp/index.js CHANGED
@@ -86,7 +86,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
86
86
  // package.json
87
87
  var package_default = {
88
88
  name: "@hasna/economy",
89
- version: "0.5.8",
89
+ version: "0.5.9",
90
90
  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",
91
91
  repository: {
92
92
  type: "git",
@@ -1343,18 +1343,19 @@ class IngestJournalMaintenance {
1343
1343
  throw new Error("Journal maintenance limit must be 1\u201332");
1344
1344
  await this.recover();
1345
1345
  await verifyJournalAuthority(this.journal, this.cloud);
1346
- const deadline = Date.now() + 30000;
1346
+ const sweepDeadline = Date.now() + 30000;
1347
1347
  let retired = 0, bytes = 0;
1348
1348
  let backend;
1349
1349
  try {
1350
1350
  const names = this.journal.names();
1351
1351
  for (const name of names.filter((n) => n.endsWith(".intent.json"))) {
1352
- if (retired >= limit || Date.now() >= deadline || retired > 0 && bytes >= 1048576)
1352
+ if (retired >= limit || Date.now() >= sweepDeadline || retired > 0 && bytes >= 1048576)
1353
1353
  break;
1354
1354
  if (!names.includes(name.replace(".intent.", ".receipt.")))
1355
1355
  continue;
1356
1356
  const intent = this.journal.intent(name), receipt = this.journal.receipt(intent.command);
1357
- backend ??= await this.backend(deadline);
1357
+ const deadline = Date.now() + 30000;
1358
+ backend = await this.backend(deadline);
1358
1359
  const m = {
1359
1360
  version: 1,
1360
1361
  target: this.journal.target,
@@ -1390,7 +1391,8 @@ class IngestJournalMaintenance {
1390
1391
  try {
1391
1392
  await this.maintain();
1392
1393
  } catch (error) {
1393
- if (exists(this.control) || !this.journal.canStage(command))
1394
+ const deferrable = error instanceof JournalMaintenanceError && ["recovery_not_configured", "self_host_trash_authority_required", "trash_version_mismatch", "recovery_authority_unavailable"].includes(error.reason);
1395
+ if (!deferrable || exists(this.control) || !this.journal.canStage(command))
1394
1396
  throw error;
1395
1397
  }
1396
1398
  if (!this.journal.canStage(command))
@@ -3281,18 +3281,19 @@ class IngestJournalMaintenance {
3281
3281
  throw new Error("Journal maintenance limit must be 1\u201332");
3282
3282
  await this.recover();
3283
3283
  await verifyJournalAuthority(this.journal, this.cloud);
3284
- const deadline = Date.now() + 30000;
3284
+ const sweepDeadline = Date.now() + 30000;
3285
3285
  let retired = 0, bytes = 0;
3286
3286
  let backend;
3287
3287
  try {
3288
3288
  const names = this.journal.names();
3289
3289
  for (const name of names.filter((n) => n.endsWith(".intent.json"))) {
3290
- if (retired >= limit || Date.now() >= deadline || retired > 0 && bytes >= 1048576)
3290
+ if (retired >= limit || Date.now() >= sweepDeadline || retired > 0 && bytes >= 1048576)
3291
3291
  break;
3292
3292
  if (!names.includes(name.replace(".intent.", ".receipt.")))
3293
3293
  continue;
3294
3294
  const intent = this.journal.intent(name), receipt = this.journal.receipt(intent.command);
3295
- backend ??= await this.backend(deadline);
3295
+ const deadline = Date.now() + 30000;
3296
+ backend = await this.backend(deadline);
3296
3297
  const m = {
3297
3298
  version: 1,
3298
3299
  target: this.journal.target,
@@ -3328,7 +3329,8 @@ class IngestJournalMaintenance {
3328
3329
  try {
3329
3330
  await this.maintain();
3330
3331
  } catch (error) {
3331
- if (exists(this.control) || !this.journal.canStage(command))
3332
+ const deferrable = error instanceof JournalMaintenanceError && ["recovery_not_configured", "self_host_trash_authority_required", "trash_version_mismatch", "recovery_authority_unavailable"].includes(error.reason);
3333
+ if (!deferrable || exists(this.control) || !this.journal.canStage(command))
3332
3334
  throw error;
3333
3335
  }
3334
3336
  if (!this.journal.canStage(command))
@@ -3480,7 +3482,7 @@ async function pushIngestRows(cloud, scratch) {
3480
3482
  // package.json
3481
3483
  var package_default = {
3482
3484
  name: "@hasna/economy",
3483
- version: "0.5.8",
3485
+ version: "0.5.9",
3484
3486
  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",
3485
3487
  repository: {
3486
3488
  type: "git",
@@ -5578,7 +5578,7 @@ function isAuthorizedRequest(req, path) {
5578
5578
  // package.json
5579
5579
  var package_default = {
5580
5580
  name: "@hasna/economy",
5581
- version: "0.5.8",
5581
+ version: "0.5.9",
5582
5582
  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",
5583
5583
  repository: {
5584
5584
  type: "git",
@@ -7454,7 +7454,7 @@ import { randomUUID } from "crypto";
7454
7454
  // package.json
7455
7455
  var package_default = {
7456
7456
  name: "@hasna/economy",
7457
- version: "0.5.8",
7457
+ version: "0.5.9",
7458
7458
  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",
7459
7459
  repository: {
7460
7460
  type: "git",
@@ -35,7 +35,11 @@ closed. No operator input can relabel an uncertain intent as rejected.
35
35
 
36
36
  A bounded sweep starts at 64 commands, 48 MiB, or insufficient next-command
37
37
  headroom. It retires at most 32 pairs or approximately 1 MiB per pass; a single
38
- large pair is handled individually. Each pass has a 30-second network deadline.
38
+ large pair is handled individually. A sweep admits new transactions for up to
39
+ 30 seconds; each admitted transaction has its own 30-second deadline for all
40
+ three captures. The last transaction may finish after admission closes, so this
41
+ phase can take up to 60 seconds. Prerequisite recovery and authority checks have
42
+ their own bounds.
39
43
  The active journal remains bounded; Trash's recoverable remote retention and
40
44
  unfinished transfer staging are governed by Trash's own policy.
41
45
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/economy",
3
- "version": "0.5.8",
3
+ "version": "0.5.9",
4
4
  "description": "AI coding cost tracker — CLI + MCP server + REST API for Claude Code, Codex, OpenCode, Cursor, Pi, and Hermes, with legacy Gemini CLI session and Gemini API billing ingestion",
5
5
  "repository": {
6
6
  "type": "git",