@hasna/economy 0.5.4 → 0.5.6

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.
@@ -4909,6 +4909,48 @@ function assertLegacyPlanCurrent(expected, current) {
4909
4909
  if (!same(state(expected), state(current)))
4910
4910
  fail("Legacy plan is stale or belongs to a different store or schema");
4911
4911
  }
4912
+ async function originalPages(load, count3) {
4913
+ const originals = [];
4914
+ let after = null;
4915
+ for (;; ) {
4916
+ const page = await load(after);
4917
+ if (!Array.isArray(page) || page.length > ORIGINAL_PAGE_ROWS)
4918
+ fail("Invalid original transfer page");
4919
+ for (const value of page) {
4920
+ const row = record(value);
4921
+ fields(row, ["id", "original"]);
4922
+ if (typeof row.original !== "string")
4923
+ fail("Invalid original transfer row");
4924
+ const id = identifier3(row.id);
4925
+ if (after !== null && cOrder(after, id) >= 0)
4926
+ fail("Original transfer cursor did not advance");
4927
+ after = id;
4928
+ originals.push({ id, original: row.original });
4929
+ if (originals.length > count3)
4930
+ fail("Original transfer exceeds the complete population");
4931
+ }
4932
+ if (page.length < ORIGINAL_PAGE_ROWS) {
4933
+ if (originals.length !== count3)
4934
+ fail("Original transfer omitted part of the complete population");
4935
+ return originals;
4936
+ }
4937
+ }
4938
+ }
4939
+ function* restoreBatches(originals) {
4940
+ let rows = [], bytes = 2;
4941
+ for (const row of originals) {
4942
+ const size = Buffer.byteLength(row.original);
4943
+ if (rows.length && (rows.length === ORIGINAL_PAGE_ROWS || bytes + 1 + size > RESTORE_BATCH_BYTES)) {
4944
+ yield "[" + rows.join(",") + "]";
4945
+ rows = [];
4946
+ bytes = 2;
4947
+ }
4948
+ bytes += size + (rows.length ? 1 : 0);
4949
+ rows.push(row.original);
4950
+ }
4951
+ if (rows.length)
4952
+ yield "[" + rows.join(",") + "]";
4953
+ }
4912
4954
  function parseLegacyCapsule(value) {
4913
4955
  const c = record(value);
4914
4956
  const mode = legacyMode("capsule", c.schema);
@@ -4981,7 +5023,12 @@ async function preserveLegacyOriginals(client, input) {
4981
5023
  (SELECT COALESCE(SUM(octet_length(row_to_json(sessions)::text)),0) FROM sessions WHERE id=ANY($1::text[]))::text AS session_bytes`, [ids])).rows[0];
4982
5024
  if (!sizes || !/^[0-9]+$/.test(sizes.request_bytes) || !/^[0-9]+$/.test(sizes.session_bytes) || BigInt(sizes.request_bytes) + BigInt(sizes.session_bytes) > BigInt(mode.originalBytes))
4983
5025
  fail(`Preservation exceeds ${mode.originalBytes / 1024 / 1024} MiB of original row text; complete sessions cannot be truncated`);
4984
- const requests = (await client.query(`SELECT id,row_to_json(requests)::text AS original FROM requests WHERE session_id=ANY($1::text[]) ORDER BY session_id COLLATE "C",id COLLATE "C" LIMIT ${mode.members + 1}`, [ids])).rows;
5026
+ const requests = [];
5027
+ for (const session of current.sessions) {
5028
+ requests.push(...await originalPages(async (after) => (await client.query(`SELECT id,row_to_json(requests)::text AS original FROM requests
5029
+ WHERE session_id=$1::text AND ($2::text IS NULL OR id COLLATE "C" > $2::text COLLATE "C")
5030
+ ORDER BY id COLLATE "C" LIMIT ${ORIGINAL_PAGE_ROWS}`, [session.id, after])).rows, session.request_count));
5031
+ }
4985
5032
  const sessions = (await client.query(`SELECT id,row_to_json(sessions)::text AS original FROM sessions WHERE id=ANY($1::text[]) ORDER BY id COLLATE "C" LIMIT ${mode.sessions + 1}`, [ids])).rows;
4986
5033
  const body = { schema: mode.capsule, plan: expected, captured_at: current.snapshot.at, requests, sessions };
4987
5034
  return parseLegacyCapsule({ ...body, capsule_sha256: sha2(JSON.stringify(body)) });
@@ -5114,9 +5161,10 @@ async function rehearseLegacyRestore(client, input, scope = "transaction") {
5114
5161
  if ((await client.query("SELECT current_setting('transaction_read_only') AS read_only")).rows[0]?.read_only !== "on")
5115
5162
  fail("Restore reconstruction requires a read-only transaction");
5116
5163
  for (const { source, temp, originals } of restoreTables) {
5117
- const payload = "[" + originals.map((x) => x.original).join(",") + "]";
5118
- await client.query("INSERT INTO pg_temp." + temp + " SELECT * FROM json_populate_recordset(NULL::" + source + ",$1::json)", [payload]);
5119
- const restored = (await client.query("SELECT id,row_to_json(t)::text AS original FROM pg_temp." + temp + ' t ORDER BY id COLLATE "C"')).rows;
5164
+ for (const payload of restoreBatches(originals)) {
5165
+ await client.query("INSERT INTO pg_temp." + temp + " SELECT * FROM json_populate_recordset(NULL::" + source + ",$1::json)", [payload]);
5166
+ }
5167
+ const restored = await originalPages(async (after) => (await client.query("SELECT id,row_to_json(t)::text AS original FROM pg_temp." + temp + ' t WHERE ($1::text IS NULL OR id COLLATE "C" > $1::text COLLATE "C") ORDER BY id COLLATE "C" LIMIT ' + ORIGINAL_PAGE_ROWS, [after])).rows, originals.length);
5120
5168
  const expectedRows = [...originals].sort((a, b) => cOrder(a.id, b.id));
5121
5169
  if (!same(restored, expectedRows))
5122
5170
  fail("Restored original bytes differ from preservation");
@@ -5170,11 +5218,12 @@ var MAX_BYTES, sha2 = (s) => createHash7("sha256").update(s).digest("hex"), fail
5170
5218
  }, record = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : fail("Expected a JSON object"), hash = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v) ? v : fail("Expected a lowercase SHA-256 digest"), identifier3 = (v) => typeof v === "string" && v.length > 0 && v.length <= 1024 && !v.includes("\x00") ? v : fail("Expected a nonempty identifier of at most 1024 characters without NUL"), fields = (o, names) => {
5171
5219
  if (Object.keys(o).sort().join(",") !== [...names].sort().join(","))
5172
5220
  fail("Unexpected or missing preservation fields");
5173
- }, cOrder = (a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b)), same = (a, b) => JSON.stringify(a) === JSON.stringify(b), quote3 = (s) => '"' + s.replaceAll('"', '""') + '"';
5221
+ }, cOrder = (a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b)), same = (a, b) => JSON.stringify(a) === JSON.stringify(b), quote3 = (s) => '"' + s.replaceAll('"', '""') + '"', ORIGINAL_PAGE_ROWS = 1000, RESTORE_BATCH_BYTES;
5174
5222
  var init_legacy_preservation = __esm(() => {
5175
5223
  init_legacy_plan();
5176
5224
  init_legacy_limits();
5177
5225
  MAX_BYTES = LEGACY_SINGLE_SESSION.capsuleBytes;
5226
+ RESTORE_BATCH_BYTES = 1024 * 1024;
5178
5227
  });
5179
5228
 
5180
5229
  // src/server/legacy-operations.ts
@@ -5529,7 +5578,7 @@ function isAuthorizedRequest(req, path) {
5529
5578
  // package.json
5530
5579
  var package_default = {
5531
5580
  name: "@hasna/economy",
5532
- version: "0.5.4",
5581
+ version: "0.5.6",
5533
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",
5534
5583
  repository: {
5535
5584
  type: "git",
@@ -5562,6 +5611,8 @@ var package_default = {
5562
5611
  files: [
5563
5612
  "dist",
5564
5613
  "docs",
5614
+ "native/Tools/pair-qr.py",
5615
+ "native/Tools/pair-qr.swift",
5565
5616
  "postinstall.js",
5566
5617
  "LICENSE",
5567
5618
  "README.md",
@@ -8812,8 +8863,8 @@ function sourceTimeMillis(value) {
8812
8863
  init_database();
8813
8864
  init_pricing();
8814
8865
  import { readdirSync as readdirSync2, readFileSync as readFileSync2, existsSync as existsSync4, statSync as statSync2 } from "fs";
8815
- import { homedir as homedir3 } from "os";
8816
- import { join as join5, basename } from "path";
8866
+ import { homedir as homedir4 } from "os";
8867
+ import { join as join6, basename } from "path";
8817
8868
 
8818
8869
  // src/lib/accounts-store.ts
8819
8870
  import { existsSync as existsSync3, readFileSync } from "fs";
@@ -9436,12 +9487,41 @@ function withAccount(record, account) {
9436
9487
  };
9437
9488
  }
9438
9489
 
9490
+ // src/lib/paths.ts
9491
+ import { homedir as homedir3 } from "os";
9492
+ import { join as join5 } from "path";
9493
+ function getHomeDir2() {
9494
+ return process.env["USERPROFILE"] ?? process.env["HOME"] ?? homedir3();
9495
+ }
9496
+ function agentPaths() {
9497
+ const home = getHomeDir2();
9498
+ const claudeHome = process.env["CLAUDE_CONFIG_DIR"] || join5(home, ".claude");
9499
+ const codexHome = process.env["CODEX_HOME"] || join5(home, ".codex");
9500
+ return {
9501
+ claudeProjects: join5(claudeHome, "projects"),
9502
+ claudeCredentials: join5(claudeHome, ".credentials.json"),
9503
+ takumiProjects: join5(home, ".takumi", "projects"),
9504
+ codexDir: codexHome,
9505
+ codexDb: join5(codexHome, "state_5.sqlite"),
9506
+ codexAuth: join5(codexHome, "auth.json"),
9507
+ codexConfig: join5(codexHome, "config.toml"),
9508
+ geminiTmp: join5(home, ".gemini", "tmp"),
9509
+ geminiHistory: join5(home, ".gemini", "history"),
9510
+ opencodeMessages: join5(home, ".local", "share", "opencode", "storage", "message"),
9511
+ piSessions: join5(home, ".pi", "agent", "sessions"),
9512
+ hermesDir: join5(home, ".hermes"),
9513
+ hermesDb: join5(home, ".hermes", "state.db")
9514
+ };
9515
+ }
9516
+
9439
9517
  // src/ingest/claude.ts
9440
9518
  function autoDetectProject(cwd, projects) {
9441
9519
  return projects.find((p) => cwd === p.path || cwd.startsWith(p.path + "/"));
9442
9520
  }
9443
- var CLAUDE_PROJECTS_DIR = process.env["HASNA_ECONOMY_CLAUDE_PROJECTS_DIR"] ?? join5(homedir3(), ".claude", "projects");
9444
- var TAKUMI_PROJECTS_DIR = process.env["HASNA_ECONOMY_TAKUMI_PROJECTS_DIR"] ?? join5(homedir3(), ".takumi", "projects");
9521
+ var TAKUMI_PROJECTS_DIR = process.env["HASNA_ECONOMY_TAKUMI_PROJECTS_DIR"] ?? join6(homedir4(), ".takumi", "projects");
9522
+ function claudeProjectsDir() {
9523
+ return process.env["HASNA_ECONOMY_CLAUDE_PROJECTS_DIR"] ?? agentPaths().claudeProjects;
9524
+ }
9445
9525
  function dirNameToPath(dirName) {
9446
9526
  return dirName.replace(/^-/, "/").replace(/-/g, "/").replace(/\/\//g, "/-");
9447
9527
  }
@@ -9451,9 +9531,9 @@ function collectJsonlFiles(projectDir) {
9451
9531
  try {
9452
9532
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
9453
9533
  if (entry.isDirectory())
9454
- walk(join5(dir, entry.name));
9534
+ walk(join6(dir, entry.name));
9455
9535
  else if (entry.name.endsWith(".jsonl"))
9456
- files.push(join5(dir, entry.name));
9536
+ files.push(join6(dir, entry.name));
9457
9537
  }
9458
9538
  } catch {
9459
9539
  collectionProblem("failed", "source_unreadable");
@@ -9462,7 +9542,7 @@ function collectJsonlFiles(projectDir) {
9462
9542
  walk(projectDir);
9463
9543
  return files;
9464
9544
  }
9465
- async function ingestClaude(db, verbose = false, projectsDir = CLAUDE_PROJECTS_DIR) {
9545
+ async function ingestClaude(db, verbose = false, projectsDir = claudeProjectsDir()) {
9466
9546
  return ingestJsonlProjects(db, projectsDir, "claude", verbose);
9467
9547
  }
9468
9548
  async function ingestTakumi(db, verbose = false, projectsDir = TAKUMI_PROJECTS_DIR) {
@@ -9484,7 +9564,7 @@ async function ingestJsonlProjects(db, projectsDir, agentName, verbose = false)
9484
9564
  const ingestState = loadIngestState(db, agentName);
9485
9565
  const projectDirs = readdirSync2(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
9486
9566
  for (const projectDirEntry of projectDirs) {
9487
- const projectDirPath = join5(projectsDir, projectDirEntry.name);
9567
+ const projectDirPath = join6(projectsDir, projectDirEntry.name);
9488
9568
  const projectPath = dirNameToPath(projectDirEntry.name);
9489
9569
  const jsonlFiles = collectJsonlFiles(projectDirPath);
9490
9570
  for (const filePath of jsonlFiles) {
@@ -9635,24 +9715,19 @@ init_database();
9635
9715
  init_pricing();
9636
9716
  import { existsSync as existsSync5, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
9637
9717
  import { createHash as createHash4 } from "crypto";
9638
- import { homedir as homedir4 } from "os";
9639
- import { join as join6, basename as basename2 } from "path";
9640
- var DEFAULT_CODEX_DB_PATH = join6(homedir4(), ".codex", "state_5.sqlite");
9641
- var DEFAULT_CODEX_CONFIG_PATH = join6(homedir4(), ".codex", "config.toml");
9642
- var DEFAULT_CODEWITH_DB_PATH = join6(homedir4(), ".codewith", "state_5.sqlite");
9643
- var DEFAULT_CODEWITH_CONFIG_PATH = join6(homedir4(), ".codewith", "config.toml");
9718
+ import { join as join7, basename as basename2 } from "path";
9644
9719
  var CODEX_INGEST_VERSION = "rollout-events-v4";
9645
9720
  function codexDbPath() {
9646
- return process.env["HASNA_ECONOMY_CODEX_DB_PATH"] ?? DEFAULT_CODEX_DB_PATH;
9721
+ return process.env["HASNA_ECONOMY_CODEX_DB_PATH"] ?? agentPaths().codexDb;
9647
9722
  }
9648
9723
  function codexConfigPath() {
9649
- return process.env["HASNA_ECONOMY_CODEX_CONFIG_PATH"] ?? DEFAULT_CODEX_CONFIG_PATH;
9724
+ return process.env["HASNA_ECONOMY_CODEX_CONFIG_PATH"] ?? agentPaths().codexConfig;
9650
9725
  }
9651
9726
  function codewithDbPath() {
9652
- return process.env["HASNA_ECONOMY_CODEWITH_DB_PATH"] ?? DEFAULT_CODEWITH_DB_PATH;
9727
+ return process.env["HASNA_ECONOMY_CODEWITH_DB_PATH"] ?? join7(getHomeDir2(), ".codewith", "state_5.sqlite");
9653
9728
  }
9654
9729
  function codewithConfigPath() {
9655
- return process.env["HASNA_ECONOMY_CODEWITH_CONFIG_PATH"] ?? DEFAULT_CODEWITH_CONFIG_PATH;
9730
+ return process.env["HASNA_ECONOMY_CODEWITH_CONFIG_PATH"] ?? join7(getHomeDir2(), ".codewith", "config.toml");
9656
9731
  }
9657
9732
  function codexSources() {
9658
9733
  const explicitCodexPath = process.env["HASNA_ECONOMY_CODEX_DB_PATH"];
@@ -9664,7 +9739,7 @@ function codexSources() {
9664
9739
  sessionPrefix: "codex",
9665
9740
  stateSource: "codex"
9666
9741
  }];
9667
- if (!explicitCodexPath || explicitCodewithPath) {
9742
+ if (!explicitCodexPath && !process.env["CODEX_HOME"] || explicitCodewithPath) {
9668
9743
  sources.push({
9669
9744
  label: "Codewith",
9670
9745
  dbPath: codewithDbPath(),
@@ -9904,9 +9979,9 @@ init_database();
9904
9979
  init_pricing();
9905
9980
  import { readdirSync as readdirSync3, readFileSync as readFileSync4, existsSync as existsSync6, statSync as statSync4 } from "fs";
9906
9981
  import { homedir as homedir5 } from "os";
9907
- import { join as join7, basename as basename3 } from "path";
9908
- var DEFAULT_GEMINI_TMP_DIR = join7(homedir5(), ".gemini", "tmp");
9909
- var DEFAULT_GEMINI_HISTORY_DIR = join7(homedir5(), ".gemini", "history");
9982
+ import { join as join8, basename as basename3 } from "path";
9983
+ var DEFAULT_GEMINI_TMP_DIR = join8(homedir5(), ".gemini", "tmp");
9984
+ var DEFAULT_GEMINI_HISTORY_DIR = join8(homedir5(), ".gemini", "history");
9910
9985
  function geminiTmpDir() {
9911
9986
  return process.env["HASNA_ECONOMY_GEMINI_TMP_DIR"] ?? DEFAULT_GEMINI_TMP_DIR;
9912
9987
  }
@@ -9928,7 +10003,7 @@ function listProjectDirs(...roots) {
9928
10003
  try {
9929
10004
  for (const entry of readdirSync3(root, { withFileTypes: true })) {
9930
10005
  if (entry.isDirectory())
9931
- dirs.add(join7(root, entry.name));
10006
+ dirs.add(join8(root, entry.name));
9932
10007
  }
9933
10008
  } catch {
9934
10009
  collectionProblem("failed", "source_unreadable");
@@ -9941,7 +10016,7 @@ function projectRoot(projectDir, chatData) {
9941
10016
  return chatData.projectPath;
9942
10017
  if (chatData.project_path)
9943
10018
  return chatData.project_path;
9944
- const rootFile = join7(projectDir, ".project_root");
10019
+ const rootFile = join8(projectDir, ".project_root");
9945
10020
  try {
9946
10021
  if (existsSync6(rootFile))
9947
10022
  return readFileSync4(rootFile, "utf-8").trim();
@@ -9966,12 +10041,12 @@ async function ingestGemini(db, verbose) {
9966
10041
  const account = await resolveAccountForAgent("gemini");
9967
10042
  const projectDirs = listProjectDirs(tmpDir, historyDir);
9968
10043
  for (const projectDir of projectDirs) {
9969
- const chatsDir = join7(projectDir, "chats");
10044
+ const chatsDir = join8(projectDir, "chats");
9970
10045
  if (!existsSync6(chatsDir))
9971
10046
  continue;
9972
10047
  let chatFiles = [];
9973
10048
  try {
9974
- chatFiles = readdirSync3(chatsDir).filter((f) => f.endsWith(".json")).map((f) => join7(chatsDir, f));
10049
+ chatFiles = readdirSync3(chatsDir).filter((f) => f.endsWith(".json")).map((f) => join8(chatsDir, f));
9975
10050
  } catch {
9976
10051
  collectionProblem("failed", "source_unreadable");
9977
10052
  continue;
@@ -10083,13 +10158,13 @@ init_database();
10083
10158
  init_pricing();
10084
10159
  import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
10085
10160
  import { homedir as homedir6 } from "os";
10086
- import { join as join8 } from "path";
10087
- var OPENCODE_STORAGE = join8(homedir6(), ".local", "share", "opencode", "storage");
10161
+ import { join as join9 } from "path";
10162
+ var OPENCODE_STORAGE = join9(homedir6(), ".local", "share", "opencode", "storage");
10088
10163
  function walkJsonFiles(dir, acc = []) {
10089
10164
  if (!existsSync7(dir))
10090
10165
  return acc;
10091
10166
  for (const entry of readdirSync4(dir, { withFileTypes: true })) {
10092
- const p = join8(dir, entry.name);
10167
+ const p = join9(dir, entry.name);
10093
10168
  if (entry.isDirectory())
10094
10169
  walkJsonFiles(p, acc);
10095
10170
  else if (entry.name.endsWith(".json"))
@@ -10109,7 +10184,7 @@ function parseSessionIdFromPath(filePath) {
10109
10184
  return null;
10110
10185
  }
10111
10186
  async function ingestOpenCode(db, verbose = false, sourceDir = OPENCODE_STORAGE) {
10112
- const messageDir = join8(sourceDir, "message");
10187
+ const messageDir = join9(sourceDir, "message");
10113
10188
  if (!existsSync7(messageDir))
10114
10189
  collectionProblem("missing", "source_missing");
10115
10190
  const files = walkJsonFiles(messageDir);
@@ -10304,13 +10379,13 @@ async function ingestCursor(db, verbose = false) {
10304
10379
  init_database();
10305
10380
  import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync5, statSync as statSync6 } from "fs";
10306
10381
  import { homedir as homedir7 } from "os";
10307
- import { join as join9 } from "path";
10308
- var PI_SESSION_DIR = process.env["PI_CODING_AGENT_SESSION_DIR"] ?? join9(homedir7(), ".pi", "agent", "sessions");
10382
+ import { join as join10 } from "path";
10383
+ var PI_SESSION_DIR = process.env["PI_CODING_AGENT_SESSION_DIR"] ?? join10(homedir7(), ".pi", "agent", "sessions");
10309
10384
  function walkSessions(dir, acc = []) {
10310
10385
  if (!existsSync8(dir))
10311
10386
  return acc;
10312
10387
  for (const entry of readdirSync5(dir, { withFileTypes: true })) {
10313
- const p = join9(dir, entry.name);
10388
+ const p = join10(dir, entry.name);
10314
10389
  if (entry.isDirectory())
10315
10390
  walkSessions(p, acc);
10316
10391
  else if (entry.name.endsWith(".json"))
@@ -10411,8 +10486,8 @@ async function ingestPi(db, verbose = false, sourceDir = PI_SESSION_DIR) {
10411
10486
  init_database();
10412
10487
  import { existsSync as existsSync9, statSync as statSync7 } from "fs";
10413
10488
  import { homedir as homedir8 } from "os";
10414
- import { join as join10 } from "path";
10415
- var HERMES_DB = join10(homedir8(), ".hermes", "state.db");
10489
+ import { join as join11 } from "path";
10490
+ var HERMES_DB = join11(homedir8(), ".hermes", "state.db");
10416
10491
  async function ingestHermes(db, verbose = false, sourcePath = HERMES_DB) {
10417
10492
  if (!existsSync9(sourcePath)) {
10418
10493
  collectionProblem("missing", "source_missing");
@@ -10482,7 +10557,7 @@ init_database();
10482
10557
  init_pricing();
10483
10558
  import { existsSync as existsSync10 } from "fs";
10484
10559
  import { homedir as homedir9 } from "os";
10485
- import { join as join11 } from "path";
10560
+ import { join as join12 } from "path";
10486
10561
  import { homedir as pathsResolverHomedir2 } from "os";
10487
10562
  import { join as pathsResolverJoin2 } from "path";
10488
10563
  var PATHS_RESOLVER_KIND_ENV2 = {
@@ -10549,8 +10624,8 @@ function defaultLoopsDbPath() {
10549
10624
  app: "loops",
10550
10625
  home: process.env["HOME"] || process.env["USERPROFILE"] || undefined
10551
10626
  });
10552
- const legacy = join11(process.env["HOME"] || process.env["USERPROFILE"] || homedir9(), ".hasna", "loops", "loops.db");
10553
- return existsSync10(join11(resolved, "loops.db")) ? join11(resolved, "loops.db") : legacy;
10627
+ const legacy = join12(process.env["HOME"] || process.env["USERPROFILE"] || homedir9(), ".hasna", "loops", "loops.db");
10628
+ return existsSync10(join12(resolved, "loops.db")) ? join12(resolved, "loops.db") : legacy;
10554
10629
  }
10555
10630
  function loopsDbPath() {
10556
10631
  return process.env["HASNA_ECONOMY_LOOPS_DB_PATH"] ?? defaultLoopsDbPath();
@@ -10734,33 +10809,6 @@ async function ingestLoops(db, verbose = false) {
10734
10809
 
10735
10810
  // src/ingest/claude-quota.ts
10736
10811
  import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
10737
-
10738
- // src/lib/paths.ts
10739
- import { homedir as homedir10 } from "os";
10740
- import { join as join12 } from "path";
10741
- function getHomeDir2() {
10742
- return process.env["USERPROFILE"] ?? process.env["HOME"] ?? homedir10();
10743
- }
10744
- function agentPaths() {
10745
- const home = getHomeDir2();
10746
- return {
10747
- claudeProjects: join12(home, ".claude", "projects"),
10748
- claudeCredentials: join12(home, ".claude", ".credentials.json"),
10749
- takumiProjects: join12(home, ".takumi", "projects"),
10750
- codexDir: join12(home, ".codex"),
10751
- codexDb: join12(home, ".codex", "state_5.sqlite"),
10752
- codexAuth: join12(home, ".codex", "auth.json"),
10753
- codexConfig: join12(home, ".codex", "config.toml"),
10754
- geminiTmp: join12(home, ".gemini", "tmp"),
10755
- geminiHistory: join12(home, ".gemini", "history"),
10756
- opencodeMessages: join12(home, ".local", "share", "opencode", "storage", "message"),
10757
- piSessions: join12(home, ".pi", "agent", "sessions"),
10758
- hermesDir: join12(home, ".hermes"),
10759
- hermesDb: join12(home, ".hermes", "state.db")
10760
- };
10761
- }
10762
-
10763
- // src/ingest/claude-quota.ts
10764
10812
  var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
10765
10813
  var OAUTH_BETA = "oauth-2025-04-20";
10766
10814
  function readClaudeToken() {
@@ -1 +1 @@
1
- {"version":3,"file":"legacy-preservation.d.ts","sourceRoot":"","sources":["../../src/server/legacy-preservation.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgD,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACnH,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAc,MAAM,oBAAoB,CAAA;AAevF,2FAA2F;AAC3F,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CA8C1D;AAOD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI,CAEvF;AAED,UAAU,QAAQ;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;AACnD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,eAAe,CAAC,OAAO,GAAG,OAAO,qBAAqB,CAAC,OAAO,CAAA;IAC7E,IAAI,EAAE,UAAU,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,QAAQ,EAAE,CAAA;IACpB,QAAQ,EAAE,QAAQ,EAAE,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,wFAAwF;AACxF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CA4ChE;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAkB1G;AAmBD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAU,GAAG,OAAO,CAS5E;AAED;sFACsF;AACtF,wBAAgB,kBAAkB,CAAC,IAAI,EAAC,MAAM,EAAC,KAAK,EAAC,OAAO;;;;;;;;;;;EAwB3D;AAED;;kFAEkF;AAClF,wBAAsB,qBAAqB,CAAC,MAAM,EAAC,YAAY,EAAC,KAAK,EAAC,OAAO,EAAC,KAAK,GAAC,aAAa,GAAC,WAAyB;;;;;;;;;;GAwD1H;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAE,GAAG;IAAC,KAAK,EAAC,MAAM,CAAC;IAAA,MAAM,CAAC,EAAC,MAAM,CAAA;CAAC,CAM1G;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAC,MAAM,EAAC,OAAO,EAAC;IAAC,KAAK,EAAC,MAAM,CAAC;IAAA,MAAM,CAAC,EAAC,MAAM,CAAA;CAAC,iBAgB/F"}
1
+ {"version":3,"file":"legacy-preservation.d.ts","sourceRoot":"","sources":["../../src/server/legacy-preservation.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgD,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACnH,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAc,MAAM,oBAAoB,CAAA;AAevF,2FAA2F;AAC3F,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CA8C1D;AAOD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI,CAEvF;AAED,UAAU,QAAQ;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;AA6CnD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,eAAe,CAAC,OAAO,GAAG,OAAO,qBAAqB,CAAC,OAAO,CAAA;IAC7E,IAAI,EAAE,UAAU,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,QAAQ,EAAE,CAAA;IACpB,QAAQ,EAAE,QAAQ,EAAE,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,wFAAwF;AACxF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CA4ChE;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAwB1G;AAmBD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAU,GAAG,OAAO,CAS5E;AAED;sFACsF;AACtF,wBAAgB,kBAAkB,CAAC,IAAI,EAAC,MAAM,EAAC,KAAK,EAAC,OAAO;;;;;;;;;;;EAwB3D;AAED;;kFAEkF;AAClF,wBAAsB,qBAAqB,CAAC,MAAM,EAAC,YAAY,EAAC,KAAK,EAAC,OAAO,EAAC,KAAK,GAAC,aAAa,GAAC,WAAyB;;;;;;;;;;GA2D1H;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAE,GAAG;IAAC,KAAK,EAAC,MAAM,CAAC;IAAA,MAAM,CAAC,EAAC,MAAM,CAAA;CAAC,CAM1G;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAC,MAAM,EAAC,OAAO,EAAC;IAAC,KAAK,EAAC,MAAM,CAAC;IAAA,MAAM,CAAC,EAAC,MAAM,CAAA;CAAC,iBAgB/F"}