haansi 0.1.31 → 0.1.32

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 (2) hide show
  1. package/dist/haansi.js +359 -99
  2. package/package.json +1 -1
package/dist/haansi.js CHANGED
@@ -95,7 +95,7 @@ var require_package = __commonJS({
95
95
  "package.json"(exports2, module2) {
96
96
  module2.exports = {
97
97
  name: "haansi",
98
- version: "0.1.31",
98
+ version: "0.1.32",
99
99
  description: "Haansi CLI - Session collector and MCP server for Claude Code & OpenAI Codex",
100
100
  bin: {
101
101
  haansi: "./dist/haansi.js"
@@ -1203,16 +1203,37 @@ var init_metrics = __esm({
1203
1203
  MetricsService = class {
1204
1204
  projectId;
1205
1205
  serviceName;
1206
- isLocal;
1207
1206
  constructor(serviceName, projectId) {
1208
1207
  this.serviceName = serviceName;
1209
1208
  this.projectId = projectId === null ? "" : projectId || process.env.GCP_PROJECT_ID || "";
1210
- this.isLocal = process.env.NODE_ENV === "development" || !this.projectId;
1211
- if (!this.isLocal && !getCloudBackend2()) {
1212
- console.error(
1213
- `[MetricsService] WARNING: No cloud metrics backend registered for service "${serviceName}". Metrics will be logged to console only. Register a cloud provider to enable cloud metrics.`
1214
- );
1215
- }
1209
+ }
1210
+ /**
1211
+ * Whether to log to the console instead of a cloud backend.
1212
+ *
1213
+ * TWO BUGS LIVED IN THE OLD VERSION OF THIS, and both silently discarded
1214
+ * every metric on AWS.
1215
+ *
1216
+ * It read `NODE_ENV === "development" || !projectId`, where `projectId`
1217
+ * falls back to `GCP_PROJECT_ID`. On AWS that variable is unset — so a
1218
+ * production service classified itself as local and logged metrics to
1219
+ * stdout, where nothing could alarm or autoscale on them. The GCP-shaped
1220
+ * question ("do I have a project id?") had become a proxy for "am I in the
1221
+ * cloud?" the moment a second provider existed.
1222
+ *
1223
+ * And it was evaluated ONCE, in the constructor. Services construct
1224
+ * MetricsService at module load and register their cloud provider during
1225
+ * startup, so even on GCP the answer could be computed before the backend
1226
+ * that would have answered it existed.
1227
+ *
1228
+ * Now it asks the question that actually matters, at the moment it matters:
1229
+ * is there a registered backend to send to? A registered backend means
1230
+ * cloud, whatever any environment variable says. Development still forces
1231
+ * console so a local run never writes to a real metric namespace.
1232
+ */
1233
+ get isLocal() {
1234
+ if (process.env.NODE_ENV === "development") return true;
1235
+ if (getCloudBackend2()) return false;
1236
+ return !this.projectId;
1216
1237
  }
1217
1238
  /**
1218
1239
  * Record a custom metric
@@ -1651,6 +1672,110 @@ var init_observability_service = __esm({
1651
1672
  }
1652
1673
  });
1653
1674
 
1675
+ // ../../packages/@company/observability/src/scaling-metrics.ts
1676
+ function spec(s) {
1677
+ return s;
1678
+ }
1679
+ var SCALING_METRICS_SERVICE, SCALING_METRICS_NAMESPACE, SCALING_METRICS;
1680
+ var init_scaling_metrics = __esm({
1681
+ "../../packages/@company/observability/src/scaling-metrics.ts"() {
1682
+ "use strict";
1683
+ init_types();
1684
+ SCALING_METRICS_SERVICE = "backlog-reconciler";
1685
+ SCALING_METRICS_NAMESPACE = `Haansi/${SCALING_METRICS_SERVICE}`;
1686
+ SCALING_METRICS = {
1687
+ CANONICAL_BACKLOG_EVENTS: spec({
1688
+ name: "haansi/ingestion/canonical_backlog_events",
1689
+ unit: "Count",
1690
+ description: "source_events rows awaiting normalization (normalized_at IS NULL), all orgs.",
1691
+ dimensions: ["environment"],
1692
+ treatMissingData: "breaching",
1693
+ missingDataRationale: "The reconciler is the only publisher and runs on a schedule independent of every worker. Its silence means the reconciler itself is broken, and the backlog it was watching is unobserved rather than empty. Scaling out against an unknown backlog wastes money; scaling in loses the queue. Breaching keeps capacity up until a human looks."
1694
+ }),
1695
+ CANONICAL_BACKLOG_OLDEST_SECONDS: spec({
1696
+ name: "haansi/ingestion/canonical_backlog_oldest_seconds",
1697
+ unit: "Seconds",
1698
+ description: "Age of the oldest un-normalized source_event. The latency signal count cannot give.",
1699
+ dimensions: ["environment"],
1700
+ treatMissingData: "breaching",
1701
+ missingDataRationale: "Same publisher as the count, so the same reasoning: absence means unobserved, not fresh."
1702
+ }),
1703
+ SESSION_MINING_BACKLOG_RUNS: spec({
1704
+ name: "haansi/mining/session_backlog_runs",
1705
+ unit: "Count",
1706
+ description: "session_mining_runs not in a terminal stage, all orgs. Includes rows awaiting a provider batch.",
1707
+ dimensions: ["environment", "queue"],
1708
+ treatMissingData: "breaching",
1709
+ missingDataRationale: "Published by the reconciler, not by the mining workers it measures, so absence is a reconciler fault rather than an empty queue."
1710
+ }),
1711
+ SESSION_MINING_OLDEST_SECONDS: spec({
1712
+ name: "haansi/mining/session_backlog_oldest_seconds",
1713
+ unit: "Seconds",
1714
+ description: "Age of the oldest non-terminal session mining run, by stage queue.",
1715
+ dimensions: ["environment", "queue"],
1716
+ treatMissingData: "breaching",
1717
+ missingDataRationale: "As above \u2014 reconciler-published."
1718
+ }),
1719
+ SESSION_MINING_FAILED_RUNS: spec({
1720
+ name: "haansi/mining/session_failed_runs",
1721
+ unit: "Count",
1722
+ description: "session_mining_runs in stage 'failed', all orgs.",
1723
+ dimensions: ["environment"],
1724
+ treatMissingData: "notBreaching",
1725
+ missingDataRationale: "The ONE deliberate exception. This drives alerting, not scaling: no capacity decision reads it, so a missing value cannot cost or lose anything. Treating absence as breaching would page on the reconciler being down, which its own liveness metric already covers."
1726
+ }),
1727
+ EMBEDDING_BACKLOG_ROWS: spec({
1728
+ name: "haansi/embeddings/backlog_rows",
1729
+ unit: "Count",
1730
+ description: "Rows with a NULL embedding across the intelligence tables that carry one.",
1731
+ dimensions: ["environment", "workload"],
1732
+ treatMissingData: "breaching",
1733
+ missingDataRationale: "This is the metric that must be able to wake a fleet from zero, so it is published by the reconciler and never by an embedding worker. A worker-published version of it would read as zero precisely when the fleet is asleep with work pending \u2014 the failure this whole phase exists to remove."
1734
+ }),
1735
+ EMBEDDING_BACKLOG_OLDEST_SECONDS: spec({
1736
+ name: "haansi/embeddings/backlog_oldest_seconds",
1737
+ unit: "Seconds",
1738
+ description: "Age of the oldest row still missing an embedding.",
1739
+ dimensions: ["environment", "workload"],
1740
+ treatMissingData: "breaching",
1741
+ missingDataRationale: "As above \u2014 reconciler-published wake source."
1742
+ }),
1743
+ EMBEDDING_CLAIMS_PENDING: spec({
1744
+ name: "haansi/embeddings/claims_pending",
1745
+ unit: "Count",
1746
+ description: "embedding_claims awaiting a worker (pending, or leased with an expired lease).",
1747
+ dimensions: ["environment"],
1748
+ treatMissingData: "breaching",
1749
+ missingDataRationale: "Reconciler-published, so absence means the queue is unobserved rather than empty. This is the signal that sizes the worker fleet; reading silence as zero would scale it away with work queued."
1750
+ }),
1751
+ EMBEDDING_CLAIMS_ACTIVE_LEASES: spec({
1752
+ name: "haansi/embeddings/claims_active_leases",
1753
+ unit: "Count",
1754
+ description: "Claims currently leased with an unexpired lease \u2014 work in flight right now.",
1755
+ dimensions: ["environment"],
1756
+ treatMissingData: "breaching",
1757
+ missingDataRationale: "This is the SCALE-IN PROTECTION signal: a non-zero value means a worker is mid-inference and must not be terminated. Absence has to read as 'possibly busy', because the alternative \u2014 assuming idle \u2014 kills a worker holding a lease and the claim then waits out its full lease window before anyone retries it."
1758
+ }),
1759
+ EMBEDDING_CLAIMS_OLDEST_SECONDS: spec({
1760
+ name: "haansi/embeddings/claims_oldest_seconds",
1761
+ unit: "Seconds",
1762
+ description: "Age of the oldest outstanding claim. The latency signal a count cannot give.",
1763
+ dimensions: ["environment"],
1764
+ treatMissingData: "breaching",
1765
+ missingDataRationale: "As above. A queue of five that has not moved in an hour is a stuck consumer, and a count-only alarm reads it as healthy."
1766
+ }),
1767
+ RECONCILER_LIVENESS: spec({
1768
+ name: "haansi/reconciler/liveness",
1769
+ unit: "Count",
1770
+ description: "Emitted as 1 by every successful reconciler run. The publisher's own heartbeat.",
1771
+ dimensions: ["environment"],
1772
+ treatMissingData: "breaching",
1773
+ missingDataRationale: "Every other metric here declares that its absence means 'the reconciler is broken'. This is the metric that says so directly, so an operator gets one alarm naming the cause instead of six naming symptoms."
1774
+ })
1775
+ };
1776
+ }
1777
+ });
1778
+
1654
1779
  // ../../packages/@company/observability/src/middleware.ts
1655
1780
  var init_middleware = __esm({
1656
1781
  "../../packages/@company/observability/src/middleware.ts"() {
@@ -1683,6 +1808,7 @@ var init_src2 = __esm({
1683
1808
  init_observability_service();
1684
1809
  init_logger();
1685
1810
  init_metrics();
1811
+ init_scaling_metrics();
1686
1812
  init_middleware();
1687
1813
  init_health();
1688
1814
  init_degradation();
@@ -1967,7 +2093,7 @@ var init_agents = __esm({
1967
2093
  }
1968
2094
  });
1969
2095
 
1970
- // ../service-capture/claude-sessions/collector.ts
2096
+ // ../service-capture/agent-sessions/collector.ts
1971
2097
  var collector_exports = {};
1972
2098
  __export(collector_exports, {
1973
2099
  collect: () => collect,
@@ -2134,9 +2260,12 @@ async function getUploadedSessions(apiUrl2, token) {
2134
2260
  });
2135
2261
  }
2136
2262
  return map2;
2137
- } catch {
2138
- logger.warn("Could not fetch uploaded sessions list, will upload all");
2139
- return /* @__PURE__ */ new Map();
2263
+ } catch (err) {
2264
+ void logger.warn(
2265
+ "Could not fetch uploaded sessions list \u2014 skipping this run rather than re-uploading everything",
2266
+ { error: err?.message ?? String(err) }
2267
+ );
2268
+ return null;
2140
2269
  }
2141
2270
  }
2142
2271
  async function uploadSession(sessionId, content, apiUrl2, token, agentSource, gitInfo) {
@@ -2174,7 +2303,63 @@ async function uploadSession(sessionId, content, apiUrl2, token, agentSource, gi
2174
2303
  throw err;
2175
2304
  }
2176
2305
  }
2177
- async function collect(options) {
2306
+ function acquireRunLock() {
2307
+ try {
2308
+ (0, import_fs3.mkdirSync)((0, import_path3.join)((0, import_os3.homedir)(), ".haansi"), { recursive: true });
2309
+ } catch {
2310
+ }
2311
+ for (let attempt = 0; attempt < 2; attempt++) {
2312
+ try {
2313
+ const fd = (0, import_fs3.openSync)(LOCK_FILE, "wx");
2314
+ (0, import_fs3.writeSync)(fd, String(process.pid));
2315
+ return fd;
2316
+ } catch {
2317
+ const holder = Number(
2318
+ (() => {
2319
+ try {
2320
+ return (0, import_fs3.readFileSync)(LOCK_FILE, "utf-8").trim();
2321
+ } catch {
2322
+ return "";
2323
+ }
2324
+ })()
2325
+ );
2326
+ if (!holder || !isProcessAlive(holder)) {
2327
+ try {
2328
+ (0, import_fs3.unlinkSync)(LOCK_FILE);
2329
+ } catch {
2330
+ }
2331
+ continue;
2332
+ }
2333
+ void logger.warn(
2334
+ "Another collector run is in progress \u2014 skipping this run",
2335
+ {
2336
+ holderPid: holder
2337
+ }
2338
+ );
2339
+ return null;
2340
+ }
2341
+ }
2342
+ return null;
2343
+ }
2344
+ function isProcessAlive(pid) {
2345
+ try {
2346
+ process.kill(pid, 0);
2347
+ return true;
2348
+ } catch (err) {
2349
+ return err?.code === "EPERM";
2350
+ }
2351
+ }
2352
+ function releaseRunLock(fd) {
2353
+ try {
2354
+ (0, import_fs3.closeSync)(fd);
2355
+ } catch {
2356
+ }
2357
+ try {
2358
+ (0, import_fs3.unlinkSync)(LOCK_FILE);
2359
+ } catch {
2360
+ }
2361
+ }
2362
+ async function collectLocked(options) {
2178
2363
  const apiUrl2 = process.env.HAANSI_API_URL ?? DEFAULT_API_URL2;
2179
2364
  const token = resolveToken2();
2180
2365
  if (!token && !options.dryRun) {
@@ -2195,6 +2380,13 @@ async function collect(options) {
2195
2380
  let errors = 0;
2196
2381
  let deferred = 0;
2197
2382
  const remoteMap = options.forceAll ? /* @__PURE__ */ new Map() : await getUploadedSessions(apiUrl2, token);
2383
+ if (remoteMap === null) {
2384
+ void logger.warn("Skipping collection: server inventory unavailable", {
2385
+ localSessions: localSessions.length,
2386
+ hint: "re-authenticate with `haansi auth login` if this persists"
2387
+ });
2388
+ return { uploaded: 0, skipped: localSessions.length, errors: 0 };
2389
+ }
2198
2390
  const toUpload = [];
2199
2391
  for (const session of localSessions) {
2200
2392
  const remote = remoteMap.get(session.sessionId);
@@ -2304,9 +2496,19 @@ async function main() {
2304
2496
  });
2305
2497
  if (errors > 0) process.exit(1);
2306
2498
  }
2307
- var import_dotenv, import_path3, import_fs3, import_os3, import_zlib, import_child_process, logger, DEFAULT_API_URL2, CREDENTIALS_FILE4, MIN_LINES, EDGE_SCRUB_PATTERNS, GIT_BIN_CANDIDATES, resolvedGitBin, UPLOAD_RATE_LIMIT_RETRIES, DEFAULT_RATE_LIMIT_WAIT_MS;
2499
+ async function collect(options) {
2500
+ if (options.dryRun) return collectLocked(options);
2501
+ const lock = acquireRunLock();
2502
+ if (lock === null) return { uploaded: 0, skipped: 0, errors: 0 };
2503
+ try {
2504
+ return await collectLocked(options);
2505
+ } finally {
2506
+ releaseRunLock(lock);
2507
+ }
2508
+ }
2509
+ var import_dotenv, import_path3, import_fs3, import_os3, import_zlib, import_child_process, logger, DEFAULT_API_URL2, CREDENTIALS_FILE4, LOCK_FILE, MIN_LINES, EDGE_SCRUB_PATTERNS, GIT_BIN_CANDIDATES, resolvedGitBin, UPLOAD_RATE_LIMIT_RETRIES, DEFAULT_RATE_LIMIT_WAIT_MS;
2308
2510
  var init_collector = __esm({
2309
- "../service-capture/claude-sessions/collector.ts"() {
2511
+ "../service-capture/agent-sessions/collector.ts"() {
2310
2512
  "use strict";
2311
2513
  import_dotenv = __toESM(require_main());
2312
2514
  import_path3 = require("path");
@@ -2321,6 +2523,7 @@ var init_collector = __esm({
2321
2523
  logger = createLogger("session-collector");
2322
2524
  DEFAULT_API_URL2 = SERVICE_URL_DEFAULTS.haansiApiBaseUrl;
2323
2525
  CREDENTIALS_FILE4 = (0, import_path3.join)((0, import_os3.homedir)(), ".haansi", "credentials.json");
2526
+ LOCK_FILE = (0, import_path3.join)((0, import_os3.homedir)(), ".haansi", "collector.lock");
2324
2527
  MIN_LINES = 4;
2325
2528
  EDGE_SCRUB_PATTERNS = [
2326
2529
  {
@@ -3395,7 +3598,50 @@ var require_node_cron = __commonJS({
3395
3598
  }
3396
3599
  });
3397
3600
 
3398
- // ../service-capture/claude-sessions/collector-daemon.ts
3601
+ // ../service-capture/agent-sessions/log-rotation.ts
3602
+ function rotateLogIfLarge(logFile, maxBytes = MAX_LOG_BYTES, keepBytes = KEEP_TAIL_BYTES) {
3603
+ let sizeBefore = 0;
3604
+ try {
3605
+ sizeBefore = (0, import_fs4.statSync)(logFile).size;
3606
+ } catch {
3607
+ return { rotated: false, sizeBefore: 0, tailKept: 0 };
3608
+ }
3609
+ if (sizeBefore <= maxBytes) {
3610
+ return { rotated: false, sizeBefore, tailKept: 0 };
3611
+ }
3612
+ let tailKept = 0;
3613
+ try {
3614
+ const start = Math.max(0, sizeBefore - keepBytes);
3615
+ const length = sizeBefore - start;
3616
+ const buf = Buffer.alloc(length);
3617
+ const fd = (0, import_fs4.openSync)(logFile, "r");
3618
+ try {
3619
+ (0, import_fs4.readSync)(fd, buf, 0, length, start);
3620
+ } finally {
3621
+ (0, import_fs4.closeSync)(fd);
3622
+ }
3623
+ (0, import_fs4.writeFileSync)(`${logFile}.1`, buf);
3624
+ tailKept = length;
3625
+ } catch {
3626
+ }
3627
+ try {
3628
+ (0, import_fs4.truncateSync)(logFile, 0);
3629
+ } catch {
3630
+ return { rotated: false, sizeBefore, tailKept };
3631
+ }
3632
+ return { rotated: true, sizeBefore, tailKept };
3633
+ }
3634
+ var import_fs4, MAX_LOG_BYTES, KEEP_TAIL_BYTES;
3635
+ var init_log_rotation = __esm({
3636
+ "../service-capture/agent-sessions/log-rotation.ts"() {
3637
+ "use strict";
3638
+ import_fs4 = require("fs");
3639
+ MAX_LOG_BYTES = 10 * 1024 * 1024;
3640
+ KEEP_TAIL_BYTES = 1 * 1024 * 1024;
3641
+ }
3642
+ });
3643
+
3644
+ // ../service-capture/agent-sessions/collector-daemon.ts
3399
3645
  var collector_daemon_exports = {};
3400
3646
  function resolveAgentsEnv() {
3401
3647
  const raw = process.env.HAANSI_AGENTS;
@@ -3406,16 +3652,27 @@ function resolveAgentsEnv() {
3406
3652
  }
3407
3653
  function getOnDiskVersion() {
3408
3654
  try {
3409
- const realEntry = (0, import_fs4.realpathSync)(process.argv[1]);
3655
+ const realEntry = (0, import_fs5.realpathSync)(process.argv[1]);
3410
3656
  const pkgPath = (0, import_path4.resolve)((0, import_path4.dirname)(realEntry), "..", "package.json");
3411
- if (!(0, import_fs4.existsSync)(pkgPath)) return null;
3412
- const pkg = JSON.parse((0, import_fs4.readFileSync)(pkgPath, "utf-8"));
3657
+ if (!(0, import_fs5.existsSync)(pkgPath)) return null;
3658
+ const pkg = JSON.parse((0, import_fs5.readFileSync)(pkgPath, "utf-8"));
3413
3659
  return pkg.version ?? null;
3414
3660
  } catch {
3415
3661
  return null;
3416
3662
  }
3417
3663
  }
3664
+ function trimLog() {
3665
+ const { rotated, sizeBefore, tailKept } = rotateLogIfLarge(LOG_FILE);
3666
+ if (rotated) {
3667
+ logger2.info("Rotated daemon log", {
3668
+ sizeBefore,
3669
+ tailKept,
3670
+ previous: `${LOG_FILE}.1`
3671
+ });
3672
+ }
3673
+ }
3418
3674
  async function run() {
3675
+ trimLog();
3419
3676
  const agents = resolveAgentsEnv();
3420
3677
  logger2.info("Collect run starting", { agents: agents?.join(",") ?? "all" });
3421
3678
  const start = Date.now();
@@ -3457,21 +3714,24 @@ async function main2() {
3457
3714
  process.on("SIGTERM", () => shutdown("SIGTERM"));
3458
3715
  process.on("SIGINT", () => shutdown("SIGINT"));
3459
3716
  }
3460
- var import_dotenv2, import_fs4, import_path4, import_node_cron, logger2, IN_MEMORY_VERSION, SCHEDULE;
3717
+ var import_dotenv2, import_fs5, import_path4, import_os4, import_node_cron, logger2, IN_MEMORY_VERSION, SCHEDULE, LOG_FILE;
3461
3718
  var init_collector_daemon = __esm({
3462
- "../service-capture/claude-sessions/collector-daemon.ts"() {
3719
+ "../service-capture/agent-sessions/collector-daemon.ts"() {
3463
3720
  "use strict";
3464
3721
  import_dotenv2 = __toESM(require_main());
3465
- import_fs4 = require("fs");
3722
+ import_fs5 = require("fs");
3466
3723
  import_path4 = require("path");
3724
+ import_os4 = require("os");
3467
3725
  import_node_cron = __toESM(require_node_cron());
3468
3726
  init_collector();
3469
3727
  init_src2();
3728
+ init_log_rotation();
3470
3729
  (0, import_dotenv2.config)({ path: (0, import_path4.resolve)(__dirname, "../../../.env") });
3471
3730
  logger2 = createLogger("collector-daemon");
3472
3731
  IN_MEMORY_VERSION = // eslint-disable-next-line @typescript-eslint/no-require-imports
3473
3732
  require_package().version;
3474
3733
  SCHEDULE = process.env.HAANSI_COLLECT_SCHEDULE ?? process.env.CLAUDE_COLLECT_SCHEDULE ?? "*/30 * * * *";
3734
+ LOG_FILE = (0, import_path4.join)((0, import_os4.homedir)(), ".haansi", "daemon.log");
3475
3735
  main2().catch((err) => {
3476
3736
  logger2.error("Daemon failed to start", err);
3477
3737
  process.exit(1);
@@ -3479,7 +3739,7 @@ var init_collector_daemon = __esm({
3479
3739
  }
3480
3740
  });
3481
3741
 
3482
- // ../service-capture/claude-sessions/http-timeout.ts
3742
+ // ../service-capture/agent-sessions/http-timeout.ts
3483
3743
  async function fetchWithTimeout(url2, init2 = {}, timeoutMs = 15e3) {
3484
3744
  const controller = new AbortController();
3485
3745
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -3490,7 +3750,7 @@ async function fetchWithTimeout(url2, init2 = {}, timeoutMs = 15e3) {
3490
3750
  }
3491
3751
  }
3492
3752
  var init_http_timeout = __esm({
3493
- "../service-capture/claude-sessions/http-timeout.ts"() {
3753
+ "../service-capture/agent-sessions/http-timeout.ts"() {
3494
3754
  "use strict";
3495
3755
  }
3496
3756
  });
@@ -52318,7 +52578,7 @@ var require_view = __commonJS({
52318
52578
  var dirname3 = path.dirname;
52319
52579
  var basename3 = path.basename;
52320
52580
  var extname = path.extname;
52321
- var join12 = path.join;
52581
+ var join13 = path.join;
52322
52582
  var resolve4 = path.resolve;
52323
52583
  module2.exports = View;
52324
52584
  function View(name, options) {
@@ -52380,12 +52640,12 @@ var require_view = __commonJS({
52380
52640
  };
52381
52641
  View.prototype.resolve = function resolve5(dir, file2) {
52382
52642
  var ext = this.ext;
52383
- var path2 = join12(dir, file2);
52643
+ var path2 = join13(dir, file2);
52384
52644
  var stat = tryStat(path2);
52385
52645
  if (stat && stat.isFile()) {
52386
52646
  return path2;
52387
52647
  }
52388
- path2 = join12(dir, basename3(file2, ext), "index" + ext);
52648
+ path2 = join13(dir, basename3(file2, ext), "index" + ext);
52389
52649
  stat = tryStat(path2);
52390
52650
  if (stat && stat.isFile()) {
52391
52651
  return path2;
@@ -54810,24 +55070,24 @@ var require_charset = __commonJS({
54810
55070
  function getCharsetPriority(charset, accepted, index) {
54811
55071
  var priority = { o: -1, q: 0, s: 0 };
54812
55072
  for (var i = 0; i < accepted.length; i++) {
54813
- var spec = specify(charset, accepted[i], index);
54814
- if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {
54815
- priority = spec;
55073
+ var spec2 = specify(charset, accepted[i], index);
55074
+ if (spec2 && (priority.s - spec2.s || priority.q - spec2.q || priority.o - spec2.o) < 0) {
55075
+ priority = spec2;
54816
55076
  }
54817
55077
  }
54818
55078
  return priority;
54819
55079
  }
54820
- function specify(charset, spec, index) {
55080
+ function specify(charset, spec2, index) {
54821
55081
  var s = 0;
54822
- if (spec.charset.toLowerCase() === charset.toLowerCase()) {
55082
+ if (spec2.charset.toLowerCase() === charset.toLowerCase()) {
54823
55083
  s |= 1;
54824
- } else if (spec.charset !== "*") {
55084
+ } else if (spec2.charset !== "*") {
54825
55085
  return null;
54826
55086
  }
54827
55087
  return {
54828
55088
  i: index,
54829
- o: spec.i,
54830
- q: spec.q,
55089
+ o: spec2.i,
55090
+ q: spec2.q,
54831
55091
  s
54832
55092
  };
54833
55093
  }
@@ -54846,11 +55106,11 @@ var require_charset = __commonJS({
54846
55106
  function compareSpecs(a, b) {
54847
55107
  return b.q - a.q || b.s - a.s || a.o - b.o || a.i - b.i || 0;
54848
55108
  }
54849
- function getFullCharset(spec) {
54850
- return spec.charset;
55109
+ function getFullCharset(spec2) {
55110
+ return spec2.charset;
54851
55111
  }
54852
- function isQuality(spec) {
54853
- return spec.q > 0;
55112
+ function isQuality(spec2) {
55113
+ return spec2.q > 0;
54854
55114
  }
54855
55115
  }
54856
55116
  });
@@ -54908,25 +55168,25 @@ var require_encoding = __commonJS({
54908
55168
  function getEncodingPriority(encoding, accepted, index) {
54909
55169
  var priority = { encoding, o: -1, q: 0, s: 0 };
54910
55170
  for (var i = 0; i < accepted.length; i++) {
54911
- var spec = specify(encoding, accepted[i], index);
54912
- if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {
54913
- priority = spec;
55171
+ var spec2 = specify(encoding, accepted[i], index);
55172
+ if (spec2 && (priority.s - spec2.s || priority.q - spec2.q || priority.o - spec2.o) < 0) {
55173
+ priority = spec2;
54914
55174
  }
54915
55175
  }
54916
55176
  return priority;
54917
55177
  }
54918
- function specify(encoding, spec, index) {
55178
+ function specify(encoding, spec2, index) {
54919
55179
  var s = 0;
54920
- if (spec.encoding.toLowerCase() === encoding.toLowerCase()) {
55180
+ if (spec2.encoding.toLowerCase() === encoding.toLowerCase()) {
54921
55181
  s |= 1;
54922
- } else if (spec.encoding !== "*") {
55182
+ } else if (spec2.encoding !== "*") {
54923
55183
  return null;
54924
55184
  }
54925
55185
  return {
54926
55186
  encoding,
54927
55187
  i: index,
54928
- o: spec.i,
54929
- q: spec.q,
55188
+ o: spec2.i,
55189
+ q: spec2.q,
54930
55190
  s
54931
55191
  };
54932
55192
  }
@@ -54959,11 +55219,11 @@ var require_encoding = __commonJS({
54959
55219
  function compareSpecs(a, b) {
54960
55220
  return b.q - a.q || b.s - a.s || a.o - b.o || a.i - b.i;
54961
55221
  }
54962
- function getFullEncoding(spec) {
54963
- return spec.encoding;
55222
+ function getFullEncoding(spec2) {
55223
+ return spec2.encoding;
54964
55224
  }
54965
- function isQuality(spec) {
54966
- return spec.q > 0;
55225
+ function isQuality(spec2) {
55226
+ return spec2.q > 0;
54967
55227
  }
54968
55228
  }
54969
55229
  });
@@ -55012,30 +55272,30 @@ var require_language = __commonJS({
55012
55272
  function getLanguagePriority(language, accepted, index) {
55013
55273
  var priority = { o: -1, q: 0, s: 0 };
55014
55274
  for (var i = 0; i < accepted.length; i++) {
55015
- var spec = specify(language, accepted[i], index);
55016
- if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {
55017
- priority = spec;
55275
+ var spec2 = specify(language, accepted[i], index);
55276
+ if (spec2 && (priority.s - spec2.s || priority.q - spec2.q || priority.o - spec2.o) < 0) {
55277
+ priority = spec2;
55018
55278
  }
55019
55279
  }
55020
55280
  return priority;
55021
55281
  }
55022
- function specify(language, spec, index) {
55282
+ function specify(language, spec2, index) {
55023
55283
  var p = parseLanguage(language);
55024
55284
  if (!p) return null;
55025
55285
  var s = 0;
55026
- if (spec.full.toLowerCase() === p.full.toLowerCase()) {
55286
+ if (spec2.full.toLowerCase() === p.full.toLowerCase()) {
55027
55287
  s |= 4;
55028
- } else if (spec.prefix.toLowerCase() === p.full.toLowerCase()) {
55288
+ } else if (spec2.prefix.toLowerCase() === p.full.toLowerCase()) {
55029
55289
  s |= 2;
55030
- } else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {
55290
+ } else if (spec2.full.toLowerCase() === p.prefix.toLowerCase()) {
55031
55291
  s |= 1;
55032
- } else if (spec.full !== "*") {
55292
+ } else if (spec2.full !== "*") {
55033
55293
  return null;
55034
55294
  }
55035
55295
  return {
55036
55296
  i: index,
55037
- o: spec.i,
55038
- q: spec.q,
55297
+ o: spec2.i,
55298
+ q: spec2.q,
55039
55299
  s
55040
55300
  };
55041
55301
  }
@@ -55054,11 +55314,11 @@ var require_language = __commonJS({
55054
55314
  function compareSpecs(a, b) {
55055
55315
  return b.q - a.q || b.s - a.s || a.o - b.o || a.i - b.i || 0;
55056
55316
  }
55057
- function getFullLanguage(spec) {
55058
- return spec.full;
55317
+ function getFullLanguage(spec2) {
55318
+ return spec2.full;
55059
55319
  }
55060
- function isQuality(spec) {
55061
- return spec.q > 0;
55320
+ function isQuality(spec2) {
55321
+ return spec2.q > 0;
55062
55322
  }
55063
55323
  }
55064
55324
  });
@@ -55113,33 +55373,33 @@ var require_mediaType = __commonJS({
55113
55373
  function getMediaTypePriority(type, accepted, index) {
55114
55374
  var priority = { o: -1, q: 0, s: 0 };
55115
55375
  for (var i = 0; i < accepted.length; i++) {
55116
- var spec = specify(type, accepted[i], index);
55117
- if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {
55118
- priority = spec;
55376
+ var spec2 = specify(type, accepted[i], index);
55377
+ if (spec2 && (priority.s - spec2.s || priority.q - spec2.q || priority.o - spec2.o) < 0) {
55378
+ priority = spec2;
55119
55379
  }
55120
55380
  }
55121
55381
  return priority;
55122
55382
  }
55123
- function specify(type, spec, index) {
55383
+ function specify(type, spec2, index) {
55124
55384
  var p = parseMediaType(type);
55125
55385
  var s = 0;
55126
55386
  if (!p) {
55127
55387
  return null;
55128
55388
  }
55129
- if (spec.type.toLowerCase() == p.type.toLowerCase()) {
55389
+ if (spec2.type.toLowerCase() == p.type.toLowerCase()) {
55130
55390
  s |= 4;
55131
- } else if (spec.type != "*") {
55391
+ } else if (spec2.type != "*") {
55132
55392
  return null;
55133
55393
  }
55134
- if (spec.subtype.toLowerCase() == p.subtype.toLowerCase()) {
55394
+ if (spec2.subtype.toLowerCase() == p.subtype.toLowerCase()) {
55135
55395
  s |= 2;
55136
- } else if (spec.subtype != "*") {
55396
+ } else if (spec2.subtype != "*") {
55137
55397
  return null;
55138
55398
  }
55139
- var keys = Object.keys(spec.params);
55399
+ var keys = Object.keys(spec2.params);
55140
55400
  if (keys.length > 0) {
55141
55401
  if (keys.every(function(k) {
55142
- return spec.params[k] == "*" || (spec.params[k] || "").toLowerCase() == (p.params[k] || "").toLowerCase();
55402
+ return spec2.params[k] == "*" || (spec2.params[k] || "").toLowerCase() == (p.params[k] || "").toLowerCase();
55143
55403
  })) {
55144
55404
  s |= 1;
55145
55405
  } else {
@@ -55148,8 +55408,8 @@ var require_mediaType = __commonJS({
55148
55408
  }
55149
55409
  return {
55150
55410
  i: index,
55151
- o: spec.i,
55152
- q: spec.q,
55411
+ o: spec2.i,
55412
+ q: spec2.q,
55153
55413
  s
55154
55414
  };
55155
55415
  }
@@ -55168,11 +55428,11 @@ var require_mediaType = __commonJS({
55168
55428
  function compareSpecs(a, b) {
55169
55429
  return b.q - a.q || b.s - a.s || a.o - b.o || a.i - b.i || 0;
55170
55430
  }
55171
- function getFullType(spec) {
55172
- return spec.type + "/" + spec.subtype;
55431
+ function getFullType(spec2) {
55432
+ return spec2.type + "/" + spec2.subtype;
55173
55433
  }
55174
- function isQuality(spec) {
55175
- return spec.q > 0;
55434
+ function isQuality(spec2) {
55435
+ return spec2.q > 0;
55176
55436
  }
55177
55437
  function quoteCount(string3) {
55178
55438
  var count = 0;
@@ -56030,7 +56290,7 @@ var require_send = __commonJS({
56030
56290
  var Stream = require("stream");
56031
56291
  var util2 = require("util");
56032
56292
  var extname = path.extname;
56033
- var join12 = path.join;
56293
+ var join13 = path.join;
56034
56294
  var normalize = path.normalize;
56035
56295
  var resolve4 = path.resolve;
56036
56296
  var sep = path.sep;
@@ -56202,7 +56462,7 @@ var require_send = __commonJS({
56202
56462
  return res;
56203
56463
  }
56204
56464
  parts = path2.split(sep);
56205
- path2 = normalize(join12(root, path2));
56465
+ path2 = normalize(join13(root, path2));
56206
56466
  } else {
56207
56467
  if (UP_PATH_REGEXP.test(path2)) {
56208
56468
  debug('malicious path "%s"', path2);
@@ -56335,7 +56595,7 @@ var require_send = __commonJS({
56335
56595
  if (err) return self.onStatError(err);
56336
56596
  return self.error(404);
56337
56597
  }
56338
- var p = join12(path2, self._index[i]);
56598
+ var p = join13(path2, self._index[i]);
56339
56599
  debug('stat "%s"', p);
56340
56600
  fs.stat(p, function(err2, stat) {
56341
56601
  if (err2) return next(err2);
@@ -58428,13 +58688,13 @@ var init_streamableHttp = __esm({
58428
58688
  }
58429
58689
  });
58430
58690
 
58431
- // ../service-capture/claude-sessions/mcp-server.ts
58691
+ // ../service-capture/agent-sessions/mcp-server.ts
58432
58692
  var mcp_server_exports = {};
58433
58693
  function resolveToken3() {
58434
58694
  if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
58435
- if ((0, import_fs5.existsSync)(CREDENTIALS_FILE5)) {
58695
+ if ((0, import_fs6.existsSync)(CREDENTIALS_FILE5)) {
58436
58696
  try {
58437
- const creds = JSON.parse((0, import_fs5.readFileSync)(CREDENTIALS_FILE5, "utf-8"));
58697
+ const creds = JSON.parse((0, import_fs6.readFileSync)(CREDENTIALS_FILE5, "utf-8"));
58438
58698
  if (creds.token) return creds.token;
58439
58699
  } catch {
58440
58700
  }
@@ -58519,14 +58779,14 @@ async function main3() {
58519
58779
  }
58520
58780
  warmupInBackground();
58521
58781
  }
58522
- var import_dotenv3, import_path5, import_fs5, import_os4, DEFAULT_API_URL3, CREDENTIALS_FILE5, API_URL, AGENT_SOURCE, DEFAULT_SOURCE_TOOL, TOKEN, contextSchema, isCollecting, lastCollectTime, COLLECT_COOLDOWN_MS, mcpServer, server;
58782
+ var import_dotenv3, import_path5, import_fs6, import_os5, DEFAULT_API_URL3, CREDENTIALS_FILE5, API_URL, AGENT_SOURCE, DEFAULT_SOURCE_TOOL, TOKEN, contextSchema, isCollecting, lastCollectTime, COLLECT_COOLDOWN_MS, mcpServer, server;
58523
58783
  var init_mcp_server2 = __esm({
58524
- "../service-capture/claude-sessions/mcp-server.ts"() {
58784
+ "../service-capture/agent-sessions/mcp-server.ts"() {
58525
58785
  "use strict";
58526
58786
  import_dotenv3 = __toESM(require_main());
58527
58787
  import_path5 = require("path");
58528
- import_fs5 = require("fs");
58529
- import_os4 = require("os");
58788
+ import_fs6 = require("fs");
58789
+ import_os5 = require("os");
58530
58790
  init_collector();
58531
58791
  init_http_timeout();
58532
58792
  init_service_urls();
@@ -58536,7 +58796,7 @@ var init_mcp_server2 = __esm({
58536
58796
  init_types4();
58537
58797
  (0, import_dotenv3.config)({ path: (0, import_path5.resolve)(__dirname, "../../../.env") });
58538
58798
  DEFAULT_API_URL3 = SERVICE_URL_DEFAULTS.haansiApiBaseUrl;
58539
- CREDENTIALS_FILE5 = (0, import_path5.join)((0, import_os4.homedir)(), ".haansi", "credentials.json");
58799
+ CREDENTIALS_FILE5 = (0, import_path5.join)((0, import_os5.homedir)(), ".haansi", "credentials.json");
58540
58800
  API_URL = process.env.HAANSI_API_URL ?? DEFAULT_API_URL3;
58541
58801
  AGENT_SOURCE = process.env.HAANSI_AGENT_SOURCE?.toLowerCase();
58542
58802
  DEFAULT_SOURCE_TOOL = AGENT_SOURCE === "codex" ? "codex-mcp" : "claude-code-mcp";
@@ -59036,8 +59296,8 @@ function warnIfStaleDevBundle(haansiPath) {
59036
59296
  const bundle = (0, import_node_fs6.realpathSync)(haansiPath);
59037
59297
  const repoRoot = (0, import_node_path5.dirname)((0, import_node_path5.dirname)((0, import_node_path5.dirname)((0, import_node_path5.dirname)(bundle))));
59038
59298
  const sources = [
59039
- "apps/service-capture/claude-sessions/collector.ts",
59040
- "apps/service-capture/claude-sessions/collector-daemon.ts",
59299
+ "apps/service-capture/agent-sessions/collector.ts",
59300
+ "apps/service-capture/agent-sessions/collector-daemon.ts",
59041
59301
  "apps/service-capture/common/agents/codex.ts",
59042
59302
  "apps/service-capture/common/agents/claude-code.ts"
59043
59303
  ].map((p) => (0, import_node_path5.join)(repoRoot, p));
@@ -59078,9 +59338,9 @@ function buildPlist(haansiPath) {
59078
59338
  <key>KeepAlive</key>
59079
59339
  <true/>
59080
59340
  <key>StandardOutPath</key>
59081
- <string>${LOG_FILE}</string>
59341
+ <string>${LOG_FILE2}</string>
59082
59342
  <key>StandardErrorPath</key>
59083
- <string>${LOG_FILE}</string>
59343
+ <string>${LOG_FILE2}</string>
59084
59344
  </dict>
59085
59345
  </plist>`;
59086
59346
  }
@@ -59111,12 +59371,12 @@ async function setupDaemon(uninstall) {
59111
59371
  (0, import_node_child_process3.spawnSync)("launchctl", ["load", PLIST_PATH], { stdio: "inherit" });
59112
59372
  console.log("Daemon installed and started.");
59113
59373
  console.log(`Plist : ${PLIST_PATH}`);
59114
- console.log(`Logs : ${LOG_FILE}`);
59374
+ console.log(`Logs : ${LOG_FILE2}`);
59115
59375
  console.log(`
59116
59376
  The daemon will start automatically on every login.`);
59117
59377
  console.log(`To uninstall: haansi setup-daemon --uninstall`);
59118
59378
  }
59119
- var import_node_child_process3, import_node_fs6, import_node_path5, import_node_os5, PLIST_LABEL, PLIST_PATH, LOG_FILE;
59379
+ var import_node_child_process3, import_node_fs6, import_node_path5, import_node_os5, PLIST_LABEL, PLIST_PATH, LOG_FILE2;
59120
59380
  var init_setup_daemon = __esm({
59121
59381
  "src/commands/setup-daemon.ts"() {
59122
59382
  "use strict";
@@ -59131,7 +59391,7 @@ var init_setup_daemon = __esm({
59131
59391
  "LaunchAgents",
59132
59392
  `${PLIST_LABEL}.plist`
59133
59393
  );
59134
- LOG_FILE = (0, import_node_path5.join)((0, import_node_os5.homedir)(), ".haansi", "daemon.log");
59394
+ LOG_FILE2 = (0, import_node_path5.join)((0, import_node_os5.homedir)(), ".haansi", "daemon.log");
59135
59395
  }
59136
59396
  });
59137
59397
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "haansi",
3
- "version": "0.1.31",
3
+ "version": "0.1.32",
4
4
  "description": "Haansi CLI - Session collector and MCP server for Claude Code & OpenAI Codex",
5
5
  "bin": {
6
6
  "haansi": "./dist/haansi.js"