@agentreel/agent 0.1.0 → 0.1.2

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/dist/cli.js CHANGED
@@ -17,7 +17,7 @@ function ensureAgentreelDir() {
17
17
  mkdirSync(AGENTREEL_DIR, { recursive: true });
18
18
  mkdirSync(QUEUE_DIR, { recursive: true });
19
19
  }
20
- var HOME, AGENTREEL_DIR, DB_PATH, CONFIG_PATH, QUEUE_DIR, LOG_PATH, CLAUDE_DIR, CLAUDE_SETTINGS_PATH;
20
+ var HOME, AGENTREEL_DIR, DB_PATH, CONFIG_PATH, QUEUE_DIR, LOG_PATH, DAEMON_PID_PATH, DAEMON_LOG_PATH, CLAUDE_DIR, CLAUDE_SETTINGS_PATH;
21
21
  var init_paths = __esm({
22
22
  "src/paths.ts"() {
23
23
  "use strict";
@@ -27,6 +27,8 @@ var init_paths = __esm({
27
27
  CONFIG_PATH = join(AGENTREEL_DIR, "config.json");
28
28
  QUEUE_DIR = join(AGENTREEL_DIR, "queue");
29
29
  LOG_PATH = join(AGENTREEL_DIR, "agent.log");
30
+ DAEMON_PID_PATH = join(AGENTREEL_DIR, "daemon.pid");
31
+ DAEMON_LOG_PATH = join(AGENTREEL_DIR, "daemon.log");
30
32
  CLAUDE_DIR = join(HOME, ".claude");
31
33
  CLAUDE_SETTINGS_PATH = join(CLAUDE_DIR, "settings.json");
32
34
  }
@@ -194,8 +196,36 @@ function migrate(db) {
194
196
  );
195
197
  CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id, ts);
196
198
  CREATE INDEX IF NOT EXISTS idx_events_pending ON events(uploaded_at) WHERE uploaded_at IS NULL;
199
+ CREATE TABLE IF NOT EXISTS meta (
200
+ key TEXT PRIMARY KEY,
201
+ value TEXT
202
+ );
197
203
  `);
198
204
  }
205
+ function getMeta(key) {
206
+ const db = getDb();
207
+ const row = db.prepare(`SELECT value FROM meta WHERE key = ?`).get(key);
208
+ return row?.value ?? null;
209
+ }
210
+ function setMeta(key, value) {
211
+ const db = getDb();
212
+ if (value == null) {
213
+ db.prepare(`DELETE FROM meta WHERE key = ?`).run(key);
214
+ return;
215
+ }
216
+ db.prepare(
217
+ `INSERT INTO meta (key, value) VALUES (?, ?)
218
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`
219
+ ).run(key, value);
220
+ }
221
+ function pendingByteSize() {
222
+ const db = getDb();
223
+ const row = db.prepare(
224
+ `SELECT COALESCE(SUM(LENGTH(payload)), 0) AS bytes
225
+ FROM events WHERE uploaded_at IS NULL`
226
+ ).get();
227
+ return row.bytes;
228
+ }
199
229
  function upsertSession(s) {
200
230
  const db = getDb();
201
231
  db.prepare(
@@ -292,7 +322,181 @@ async function initCommand() {
292
322
  // src/commands/status.ts
293
323
  init_paths();
294
324
  import pc2 from "picocolors";
295
- import { existsSync as existsSync3 } from "fs";
325
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
326
+
327
+ // src/upload/client.ts
328
+ var IngestError = class extends Error {
329
+ constructor(message, status, body) {
330
+ super(message);
331
+ this.status = status;
332
+ this.body = body;
333
+ this.name = "IngestError";
334
+ }
335
+ status;
336
+ body;
337
+ /** 4xx (except 408/429) — payload-shaped problem the agent can't fix by retrying. */
338
+ get isPermanent() {
339
+ return this.status >= 400 && this.status < 500 && this.status !== 408 && this.status !== 429;
340
+ }
341
+ };
342
+ async function postIngest(cfg, body) {
343
+ if (!cfg.apiKey) {
344
+ throw new Error("Not linked. Run: agentreel link <key>");
345
+ }
346
+ const url = `${cfg.apiBaseUrl.replace(/\/$/, "")}/api/v1/sessions/ingest`;
347
+ const res = await fetch(url, {
348
+ method: "POST",
349
+ headers: {
350
+ "content-type": "application/json",
351
+ authorization: `Bearer ${cfg.apiKey}`
352
+ },
353
+ body: JSON.stringify(body)
354
+ });
355
+ let data = null;
356
+ try {
357
+ data = await res.json();
358
+ } catch {
359
+ }
360
+ if (!res.ok || !data?.ok) {
361
+ const reason = data?.reason ? ` (${data.reason})` : "";
362
+ const tag = data?.error ?? `http-${res.status}`;
363
+ throw new IngestError(`${tag}${reason}`, res.status, data);
364
+ }
365
+ return data;
366
+ }
367
+
368
+ // src/upload/queue.ts
369
+ var MAX_BATCH_BYTES = 1024 * 1024;
370
+ var MAX_BATCH_EVENTS = 500;
371
+ function takePendingBatch(maxEvents = MAX_BATCH_EVENTS, maxBytes = MAX_BATCH_BYTES) {
372
+ const db = getDb();
373
+ const candidateRows = db.prepare(
374
+ `SELECT id, session_id, tool, type, ts, cwd, payload
375
+ FROM events
376
+ WHERE uploaded_at IS NULL
377
+ ORDER BY ts ASC
378
+ LIMIT ?`
379
+ ).all(maxEvents);
380
+ if (candidateRows.length === 0) return { sessions: [], events: [], eventIds: [] };
381
+ const eventRows = [];
382
+ let bytes = 0;
383
+ for (const row of candidateRows) {
384
+ const rowBytes = Buffer.byteLength(row.payload, "utf8");
385
+ if (eventRows.length > 0 && bytes + rowBytes > maxBytes) break;
386
+ eventRows.push(row);
387
+ bytes += rowBytes;
388
+ }
389
+ const sessionIds = [...new Set(eventRows.map((e) => e.session_id))];
390
+ const placeholders = sessionIds.map(() => "?").join(",");
391
+ const sessionRows = db.prepare(
392
+ `SELECT id, tool, started_at, ended_at, cwd, total_cost_cents, total_tokens
393
+ FROM sessions WHERE id IN (${placeholders})`
394
+ ).all(...sessionIds);
395
+ const sessions = sessionRows.map((s) => ({
396
+ id: s.id,
397
+ tool: s.tool,
398
+ started_at: s.started_at,
399
+ ended_at: s.ended_at,
400
+ cwd: s.cwd,
401
+ total_cost_cents: s.total_cost_cents,
402
+ total_tokens: s.total_tokens
403
+ }));
404
+ const events = eventRows.map((e) => ({
405
+ id: e.id,
406
+ session_id: e.session_id,
407
+ ts: e.ts,
408
+ type: e.type,
409
+ tool: e.tool,
410
+ cwd: e.cwd,
411
+ payload: safeParse(e.payload)
412
+ }));
413
+ return { sessions, events, eventIds: eventRows.map((r) => r.id) };
414
+ }
415
+ function markUploaded(eventIds) {
416
+ if (eventIds.length === 0) return;
417
+ const db = getDb();
418
+ const now = Date.now();
419
+ const stmt = db.prepare(`UPDATE events SET uploaded_at = ? WHERE id = ?`);
420
+ const tx = db.transaction((ids) => {
421
+ for (const id of ids) stmt.run(now, id);
422
+ });
423
+ tx(eventIds);
424
+ }
425
+ function safeParse(s) {
426
+ try {
427
+ return JSON.parse(s);
428
+ } catch {
429
+ return s;
430
+ }
431
+ }
432
+
433
+ // src/upload/flush.ts
434
+ var META_LAST_SYNC_AT = "last_sync_at";
435
+ var META_LAST_ERROR_AT = "last_error_at";
436
+ var META_LAST_ERROR_MSG = "last_error_msg";
437
+ var META_CONSECUTIVE_FAILS = "consecutive_failures";
438
+ var META_NEXT_ATTEMPT_AT = "next_attempt_at";
439
+ var BACKOFF_BASE_MS = 1e3;
440
+ var BACKOFF_MAX_MS = 5 * 6e4;
441
+ function nextAttemptAt() {
442
+ const raw = getMeta(META_NEXT_ATTEMPT_AT);
443
+ return raw ? Number(raw) : 0;
444
+ }
445
+ function consecutiveFailures() {
446
+ const raw = getMeta(META_CONSECUTIVE_FAILS);
447
+ return raw ? Number(raw) : 0;
448
+ }
449
+ function recordSuccess() {
450
+ const now = Date.now();
451
+ setMeta(META_LAST_SYNC_AT, String(now));
452
+ setMeta(META_LAST_ERROR_AT, null);
453
+ setMeta(META_LAST_ERROR_MSG, null);
454
+ setMeta(META_CONSECUTIVE_FAILS, "0");
455
+ setMeta(META_NEXT_ATTEMPT_AT, "0");
456
+ }
457
+ function recordFailure(err, permanent) {
458
+ const now = Date.now();
459
+ const fails = consecutiveFailures() + 1;
460
+ setMeta(META_LAST_ERROR_AT, String(now));
461
+ setMeta(META_LAST_ERROR_MSG, truncate(err.message, 500));
462
+ setMeta(META_CONSECUTIVE_FAILS, String(fails));
463
+ if (permanent) {
464
+ setMeta(META_NEXT_ATTEMPT_AT, String(now + BACKOFF_MAX_MS));
465
+ return;
466
+ }
467
+ const exp = Math.min(BACKOFF_MAX_MS, BACKOFF_BASE_MS * 2 ** (fails - 1));
468
+ const jitter = exp * 0.25 * Math.random();
469
+ setMeta(META_NEXT_ATTEMPT_AT, String(now + exp + jitter));
470
+ }
471
+ function truncate(s, n) {
472
+ return s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
473
+ }
474
+ async function flushOnce() {
475
+ const cfg = readConfig();
476
+ if (!cfg.apiKey) {
477
+ throw new Error("Not linked. Run: agentreel link <api-key>");
478
+ }
479
+ const batch = takePendingBatch(MAX_BATCH_EVENTS, MAX_BATCH_BYTES);
480
+ if (batch.events.length === 0) {
481
+ return { uploadedEvents: 0, uploadedSessions: 0, moreAvailable: false };
482
+ }
483
+ try {
484
+ const res = await postIngest(cfg, { sessions: batch.sessions, events: batch.events });
485
+ markUploaded(batch.eventIds);
486
+ recordSuccess();
487
+ return {
488
+ uploadedEvents: res.events_written ?? 0,
489
+ uploadedSessions: res.sessions_written ?? 0,
490
+ moreAvailable: batch.events.length === MAX_BATCH_EVENTS
491
+ };
492
+ } catch (err) {
493
+ const permanent = err instanceof IngestError && err.isPermanent;
494
+ recordFailure(err, permanent);
495
+ throw err;
496
+ }
497
+ }
498
+
499
+ // src/commands/status.ts
296
500
  function fmtDuration(ms) {
297
501
  const s = Math.round(ms / 1e3);
298
502
  if (s < 60) return `${s}s`;
@@ -301,6 +505,25 @@ function fmtDuration(ms) {
301
505
  const h = Math.floor(m / 60);
302
506
  return `${h}h ${m % 60}m`;
303
507
  }
508
+ function fmtAgo(ms) {
509
+ return fmtDuration(Date.now() - ms) + " ago";
510
+ }
511
+ function fmtBytes(n) {
512
+ if (n < 1024) return `${n} B`;
513
+ if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
514
+ return `${(n / 1024 / 1024).toFixed(2)} MB`;
515
+ }
516
+ function daemonStatus() {
517
+ if (!existsSync3(DAEMON_PID_PATH)) return { running: false, pid: null };
518
+ try {
519
+ const pid = Number(readFileSync3(DAEMON_PID_PATH, "utf8").trim());
520
+ if (!Number.isFinite(pid) || pid <= 0) return { running: false, pid: null };
521
+ process.kill(pid, 0);
522
+ return { running: true, pid };
523
+ } catch {
524
+ return { running: false, pid: null };
525
+ }
526
+ }
304
527
  async function statusCommand() {
305
528
  const cfg = readConfig();
306
529
  console.log(pc2.bold(pc2.cyan("AgentReel status\n")));
@@ -311,14 +534,38 @@ async function statusCommand() {
311
534
  );
312
535
  console.log(" api base: " + cfg.apiBaseUrl);
313
536
  console.log(" authenticated: " + (cfg.apiKey ? pc2.green("yes") : pc2.yellow("no")));
537
+ const d = daemonStatus();
538
+ console.log(
539
+ " daemon: " + (d.running ? pc2.green(`running (pid ${d.pid})`) : pc2.dim("stopped"))
540
+ );
314
541
  console.log();
315
542
  if (!existsSync3(DB_PATH)) {
316
543
  console.log(pc2.yellow("Run `agentreel init` to install hooks."));
317
544
  return;
318
545
  }
319
546
  const { total, pending } = countEvents();
547
+ const pendingBytes = pendingByteSize();
320
548
  console.log(` events captured: ${total}`);
321
- console.log(` pending upload: ${pending}`);
549
+ console.log(` pending upload: ${pending}` + (pending > 0 ? pc2.dim(` (${fmtBytes(pendingBytes)})`) : ""));
550
+ const lastSync = numMeta(META_LAST_SYNC_AT);
551
+ const lastErr = numMeta(META_LAST_ERROR_AT);
552
+ const lastErrMsg = getMeta(META_LAST_ERROR_MSG);
553
+ const fails = consecutiveFailures();
554
+ const nextAt = numMeta(META_NEXT_ATTEMPT_AT);
555
+ console.log(
556
+ ` last sync: ` + (lastSync ? pc2.green(fmtAgo(lastSync)) : pc2.dim("never"))
557
+ );
558
+ if (lastErr) {
559
+ console.log(
560
+ ` last error: ` + pc2.red(fmtAgo(lastErr)) + pc2.dim(` \xB7 ${fails} consecutive`)
561
+ );
562
+ if (lastErrMsg) console.log(pc2.dim(` ${lastErrMsg}`));
563
+ if (nextAt && nextAt > Date.now()) {
564
+ console.log(
565
+ pc2.dim(` next attempt in ${fmtDuration(nextAt - Date.now())}`)
566
+ );
567
+ }
568
+ }
322
569
  console.log();
323
570
  const sessions = listRecentSessions(5);
324
571
  if (sessions.length === 0) {
@@ -328,43 +575,19 @@ async function statusCommand() {
328
575
  console.log(pc2.bold("recent sessions:"));
329
576
  for (const s of sessions) {
330
577
  const dur = s.ended_at ? fmtDuration(s.ended_at - s.started_at) : pc2.dim("active");
331
- const ts = new Date(s.started_at).toLocaleString();
332
- console.log(` ${pc2.dim(s.id.slice(0, 8))} ${s.tool.padEnd(11)} ${ts} ${dur}`);
578
+ const ts2 = new Date(s.started_at).toLocaleString();
579
+ console.log(` ${pc2.dim(s.id.slice(0, 8))} ${s.tool.padEnd(11)} ${ts2} ${dur}`);
333
580
  }
334
581
  }
335
-
336
- // src/commands/auth.ts
337
- import pc3 from "picocolors";
338
-
339
- // src/upload/client.ts
340
- async function postIngest(cfg, body) {
341
- if (!cfg.apiKey) {
342
- throw new Error("Not linked. Run: agentreel link <key>");
343
- }
344
- const url = `${cfg.apiBaseUrl.replace(/\/$/, "")}/api/v1/sessions/ingest`;
345
- const res = await fetch(url, {
346
- method: "POST",
347
- headers: {
348
- "content-type": "application/json",
349
- authorization: `Bearer ${cfg.apiKey}`
350
- },
351
- body: JSON.stringify(body)
352
- });
353
- let data;
354
- try {
355
- data = await res.json();
356
- } catch {
357
- throw new Error(`Ingest returned ${res.status} with non-JSON body`);
358
- }
359
- if (!res.ok || !data.ok) {
360
- throw new Error(
361
- `Ingest failed: ${res.status} ${data.error ?? ""} ${data.reason ?? ""}`.trim()
362
- );
363
- }
364
- return data;
582
+ function numMeta(key) {
583
+ const v = getMeta(key);
584
+ if (!v) return null;
585
+ const n = Number(v);
586
+ return Number.isFinite(n) && n > 0 ? n : null;
365
587
  }
366
588
 
367
589
  // src/commands/auth.ts
590
+ import pc3 from "picocolors";
368
591
  async function logoutCommand() {
369
592
  const cfg = readConfig();
370
593
  cfg.apiKey = void 0;
@@ -539,7 +762,7 @@ function computeDiff(before, after) {
539
762
  }
540
763
 
541
764
  // src/redact/ignore.ts
542
- import { existsSync as existsSync5, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
765
+ import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
543
766
  import { join as join4, relative, sep as sep3 } from "path";
544
767
  import ignore from "ignore";
545
768
  var FILENAME = ".agentreelignore";
@@ -549,7 +772,7 @@ function loadFor(workspace) {
549
772
  const path = join4(workspace, FILENAME);
550
773
  if (!existsSync5(path)) return null;
551
774
  try {
552
- const raw = readFileSync3(path, "utf8");
775
+ const raw = readFileSync4(path, "utf8");
553
776
  return ignore({ allowRelativePaths: true }).add(raw);
554
777
  } catch {
555
778
  return null;
@@ -841,16 +1064,16 @@ var CursorSessionManager = class {
841
1064
  open = /* @__PURE__ */ new Map();
842
1065
  ingest(snapshot) {
843
1066
  const key = snapshot.workspace ?? GLOBAL_KEY;
844
- const ts = snapshot.timestamp;
1067
+ const ts2 = snapshot.timestamp;
845
1068
  const cwd = snapshot.workspace ?? "";
846
1069
  let session = this.open.get(key);
847
1070
  let isNew = false;
848
- if (!session || ts - session.lastTs > IDLE_MS) {
1071
+ if (!session || ts2 - session.lastTs > IDLE_MS) {
849
1072
  if (session) this.closeSession(session, session.lastTs);
850
1073
  session = {
851
1074
  id: `cur_${nanoid(10)}`,
852
- startedAt: ts,
853
- lastTs: ts,
1075
+ startedAt: ts2,
1076
+ lastTs: ts2,
854
1077
  cwd
855
1078
  };
856
1079
  this.open.set(key, session);
@@ -858,18 +1081,18 @@ var CursorSessionManager = class {
858
1081
  upsertSession({
859
1082
  id: session.id,
860
1083
  tool: "cursor",
861
- startedAt: ts,
1084
+ startedAt: ts2,
862
1085
  cwd
863
1086
  });
864
1087
  } else {
865
- session.lastTs = ts;
1088
+ session.lastTs = ts2;
866
1089
  }
867
1090
  const event = {
868
1091
  id: nanoid(),
869
1092
  sessionId: session.id,
870
1093
  tool: "cursor",
871
1094
  type: "tool_use_post",
872
- ts,
1095
+ ts: ts2,
873
1096
  cwd,
874
1097
  payload: {
875
1098
  tool_name: "Edit",
@@ -921,17 +1144,17 @@ async function watchCommand() {
921
1144
  const sessions = new CursorSessionManager();
922
1145
  const watcher = startCursorWatcher(dir, async (snap) => {
923
1146
  const { sessionId, isNew } = sessions.ingest(snap);
924
- const ts = new Date(snap.timestamp).toLocaleTimeString();
1147
+ const ts2 = new Date(snap.timestamp).toLocaleTimeString();
925
1148
  const ws = snap.workspace ? basename2(snap.workspace) : pc4.dim("no-workspace");
926
1149
  const file = snap.filePath.split("/").slice(-2).join("/");
927
1150
  const sourceLabel = snap.source === "cursor-ai" ? pc4.magenta("ai") : snap.source === "cursor-manual" ? pc4.cyan("man") : pc4.dim("?");
928
1151
  const stats = snap.binary ? pc4.dim("binary") : `${pc4.green("+" + snap.added)} ${pc4.red("-" + snap.removed)}`;
929
1152
  if (isNew) {
930
1153
  console.log(
931
- `${pc4.dim(ts)} ${pc4.yellow("session")} ${pc4.dim(sessionId)} ${ws}`
1154
+ `${pc4.dim(ts2)} ${pc4.yellow("session")} ${pc4.dim(sessionId)} ${ws}`
932
1155
  );
933
1156
  }
934
- console.log(`${pc4.dim(ts)} edit ${sourceLabel} ${file.padEnd(36)} ${stats}`);
1157
+ console.log(`${pc4.dim(ts2)} edit ${sourceLabel} ${file.padEnd(36)} ${stats}`);
935
1158
  });
936
1159
  const shutdown = async () => {
937
1160
  console.log(pc4.dim("\n closing sessions\u2026"));
@@ -945,63 +1168,6 @@ async function watchCommand() {
945
1168
 
946
1169
  // src/commands/push.ts
947
1170
  import pc5 from "picocolors";
948
-
949
- // src/upload/queue.ts
950
- function takePendingBatch(maxEvents = 500) {
951
- const db = getDb();
952
- const eventRows = db.prepare(
953
- `SELECT id, session_id, tool, type, ts, cwd, payload
954
- FROM events
955
- WHERE uploaded_at IS NULL
956
- ORDER BY ts ASC
957
- LIMIT ?`
958
- ).all(maxEvents);
959
- if (eventRows.length === 0) return { sessions: [], events: [], eventIds: [] };
960
- const sessionIds = [...new Set(eventRows.map((e) => e.session_id))];
961
- const placeholders = sessionIds.map(() => "?").join(",");
962
- const sessionRows = db.prepare(
963
- `SELECT id, tool, started_at, ended_at, cwd, total_cost_cents, total_tokens
964
- FROM sessions WHERE id IN (${placeholders})`
965
- ).all(...sessionIds);
966
- const sessions = sessionRows.map((s) => ({
967
- id: s.id,
968
- tool: s.tool,
969
- started_at: s.started_at,
970
- ended_at: s.ended_at,
971
- cwd: s.cwd,
972
- total_cost_cents: s.total_cost_cents,
973
- total_tokens: s.total_tokens
974
- }));
975
- const events = eventRows.map((e) => ({
976
- id: e.id,
977
- session_id: e.session_id,
978
- ts: e.ts,
979
- type: e.type,
980
- tool: e.tool,
981
- cwd: e.cwd,
982
- payload: safeParse(e.payload)
983
- }));
984
- return { sessions, events, eventIds: eventRows.map((r) => r.id) };
985
- }
986
- function markUploaded(eventIds) {
987
- if (eventIds.length === 0) return;
988
- const db = getDb();
989
- const now = Date.now();
990
- const stmt = db.prepare(`UPDATE events SET uploaded_at = ? WHERE id = ?`);
991
- const tx = db.transaction((ids) => {
992
- for (const id of ids) stmt.run(now, id);
993
- });
994
- tx(eventIds);
995
- }
996
- function safeParse(s) {
997
- try {
998
- return JSON.parse(s);
999
- } catch {
1000
- return s;
1001
- }
1002
- }
1003
-
1004
- // src/commands/push.ts
1005
1171
  async function pushCommand() {
1006
1172
  const cfg = readConfig();
1007
1173
  if (!cfg.apiKey) {
@@ -1010,16 +1176,27 @@ async function pushCommand() {
1010
1176
  }
1011
1177
  let totalSessions = 0;
1012
1178
  let totalEvents = 0;
1013
- while (true) {
1014
- const batch = takePendingBatch(500);
1015
- if (batch.events.length === 0) break;
1016
- console.log(
1017
- pc5.dim(` uploading ${batch.events.length} events across ${batch.sessions.length} sessions\u2026`)
1018
- );
1019
- const res = await postIngest(cfg, { sessions: batch.sessions, events: batch.events });
1020
- markUploaded(batch.eventIds);
1021
- totalSessions += res.sessions_written ?? 0;
1022
- totalEvents += res.events_written ?? 0;
1179
+ for (; ; ) {
1180
+ let res;
1181
+ try {
1182
+ res = await flushOnce();
1183
+ } catch (err) {
1184
+ const e = err;
1185
+ if (err instanceof IngestError) {
1186
+ console.error(pc5.red("\u2717 ") + e.message);
1187
+ if (err.isPermanent) {
1188
+ console.error(
1189
+ pc5.dim(" not retrying \u2014 fix the cause (re-link, upgrade plan, etc.) and run push again.")
1190
+ );
1191
+ }
1192
+ } else {
1193
+ console.error(pc5.red("\u2717 ") + e.message);
1194
+ }
1195
+ process.exit(1);
1196
+ }
1197
+ totalSessions += res.uploadedSessions;
1198
+ totalEvents += res.uploadedEvents;
1199
+ if (!res.moreAvailable) break;
1023
1200
  }
1024
1201
  if (totalEvents === 0) {
1025
1202
  console.log(pc5.green("\u2713") + " queue is empty \u2014 nothing to upload.");
@@ -1030,6 +1207,134 @@ async function pushCommand() {
1030
1207
  );
1031
1208
  }
1032
1209
 
1210
+ // src/commands/daemon.ts
1211
+ init_paths();
1212
+ import pc6 from "picocolors";
1213
+ import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync3, unlinkSync } from "fs";
1214
+ var TICK_INTERVAL_MS = 3e4;
1215
+ var EARLY_FLUSH_BYTES = MAX_BATCH_BYTES;
1216
+ async function daemonCommand() {
1217
+ const cfg = readConfig();
1218
+ if (!cfg.apiKey) {
1219
+ console.error(pc6.red("\u2717 Not linked. Run ") + pc6.cyan("agentreel link <api-key>"));
1220
+ process.exit(1);
1221
+ }
1222
+ if (!claimPidFile()) {
1223
+ process.exit(1);
1224
+ }
1225
+ const release = () => {
1226
+ try {
1227
+ unlinkSync(DAEMON_PID_PATH);
1228
+ } catch {
1229
+ }
1230
+ };
1231
+ let stopping = false;
1232
+ const stop = () => {
1233
+ if (stopping) return;
1234
+ stopping = true;
1235
+ console.log(pc6.dim("\n daemon stopping\u2026"));
1236
+ release();
1237
+ process.exit(0);
1238
+ };
1239
+ process.on("SIGINT", stop);
1240
+ process.on("SIGTERM", stop);
1241
+ process.on("beforeExit", release);
1242
+ console.log(pc6.green("\u25CF") + " agentreel daemon running");
1243
+ console.log(pc6.dim(` api: ${cfg.apiBaseUrl}`));
1244
+ console.log(pc6.dim(` tick: every ${TICK_INTERVAL_MS / 1e3}s, early flush at ${(EARLY_FLUSH_BYTES / 1024).toFixed(0)} KB`));
1245
+ console.log(pc6.dim(" Ctrl-C to stop"));
1246
+ for (; ; ) {
1247
+ if (stopping) return;
1248
+ const nextAt = nextAttemptAt();
1249
+ const now = Date.now();
1250
+ if (nextAt > now) {
1251
+ await sleepInterruptible(nextAt - now, () => stopping);
1252
+ continue;
1253
+ }
1254
+ try {
1255
+ for (; ; ) {
1256
+ const res = await flushOnce();
1257
+ if (res.uploadedEvents > 0) {
1258
+ console.log(
1259
+ pc6.dim(` [${ts()}]`) + pc6.green(" \u2713") + ` ${res.uploadedEvents} events \xB7 ${res.uploadedSessions} sessions`
1260
+ );
1261
+ }
1262
+ if (!res.moreAvailable) break;
1263
+ }
1264
+ } catch (err) {
1265
+ const e = err;
1266
+ if (err instanceof IngestError && err.isPermanent) {
1267
+ console.error(
1268
+ pc6.dim(` [${ts()}]`) + pc6.red(" \u2717 ") + e.message + pc6.dim(" (5min backoff)")
1269
+ );
1270
+ } else {
1271
+ console.error(pc6.dim(` [${ts()}]`) + pc6.yellow(" \xB7 ") + e.message);
1272
+ }
1273
+ }
1274
+ await waitForTickOrPressure(TICK_INTERVAL_MS, EARLY_FLUSH_BYTES, () => stopping);
1275
+ }
1276
+ }
1277
+ function claimPidFile() {
1278
+ ensureAgentreelDir();
1279
+ if (existsSync8(DAEMON_PID_PATH)) {
1280
+ const raw = readFileSync5(DAEMON_PID_PATH, "utf8").trim();
1281
+ const pid = Number(raw);
1282
+ if (Number.isFinite(pid) && pid > 0 && isAlive(pid)) {
1283
+ console.error(pc6.red(`\u2717 daemon already running (pid ${pid})`));
1284
+ console.error(pc6.dim(` if this is wrong, remove ${DAEMON_PID_PATH}`));
1285
+ return false;
1286
+ }
1287
+ }
1288
+ writeFileSync3(DAEMON_PID_PATH, String(process.pid), { encoding: "utf8", mode: 384 });
1289
+ return true;
1290
+ }
1291
+ function isAlive(pid) {
1292
+ try {
1293
+ process.kill(pid, 0);
1294
+ return true;
1295
+ } catch {
1296
+ return false;
1297
+ }
1298
+ }
1299
+ function sleepInterruptible(ms, shouldStop) {
1300
+ return new Promise((resolve2) => {
1301
+ if (ms <= 0) return resolve2();
1302
+ const start = Date.now();
1303
+ const t = setInterval(() => {
1304
+ if (shouldStop() || Date.now() - start >= ms) {
1305
+ clearInterval(t);
1306
+ resolve2();
1307
+ }
1308
+ }, 250);
1309
+ });
1310
+ }
1311
+ function waitForTickOrPressure(tickMs, pressureBytes, shouldStop) {
1312
+ return new Promise((resolve2) => {
1313
+ const start = Date.now();
1314
+ const t = setInterval(() => {
1315
+ if (shouldStop()) {
1316
+ clearInterval(t);
1317
+ return resolve2();
1318
+ }
1319
+ if (Date.now() - start >= tickMs) {
1320
+ clearInterval(t);
1321
+ return resolve2();
1322
+ }
1323
+ try {
1324
+ if (pendingByteSize() >= pressureBytes) {
1325
+ clearInterval(t);
1326
+ return resolve2();
1327
+ }
1328
+ } catch {
1329
+ }
1330
+ }, 1e3);
1331
+ });
1332
+ }
1333
+ function ts() {
1334
+ const d = /* @__PURE__ */ new Date();
1335
+ return d.toTimeString().slice(0, 8);
1336
+ }
1337
+
1033
1338
  // src/hooks/handler.ts
1034
1339
  import { nanoid as nanoid2 } from "nanoid";
1035
1340
  import { appendFileSync } from "fs";
@@ -1069,28 +1374,28 @@ async function runHook(eventArg) {
1069
1374
  const input = JSON.parse(raw);
1070
1375
  const hookEventName = input.hook_event_name ?? eventArg ?? "Unknown";
1071
1376
  const type = HOOK_EVENT_TO_TYPE[hookEventName] ?? "unknown";
1072
- const ts = Date.now();
1377
+ const ts2 = Date.now();
1073
1378
  const sessionId = input.session_id ?? "unknown-session";
1074
1379
  if (type === "session_start") {
1075
1380
  upsertSession({
1076
1381
  id: sessionId,
1077
1382
  tool: "claude-code",
1078
- startedAt: ts,
1383
+ startedAt: ts2,
1079
1384
  cwd: input.cwd
1080
1385
  });
1081
1386
  } else if (type === "session_end") {
1082
1387
  upsertSession({
1083
1388
  id: sessionId,
1084
1389
  tool: "claude-code",
1085
- startedAt: ts,
1086
- endedAt: ts,
1390
+ startedAt: ts2,
1391
+ endedAt: ts2,
1087
1392
  cwd: input.cwd
1088
1393
  });
1089
1394
  } else {
1090
1395
  upsertSession({
1091
1396
  id: sessionId,
1092
1397
  tool: "claude-code",
1093
- startedAt: ts,
1398
+ startedAt: ts2,
1094
1399
  cwd: input.cwd
1095
1400
  });
1096
1401
  }
@@ -1100,7 +1405,7 @@ async function runHook(eventArg) {
1100
1405
  sessionId,
1101
1406
  tool: "claude-code",
1102
1407
  type,
1103
- ts,
1408
+ ts: ts2,
1104
1409
  cwd: input.cwd,
1105
1410
  payload: safePayload
1106
1411
  };
@@ -1111,8 +1416,9 @@ async function runHook(eventArg) {
1111
1416
  }
1112
1417
 
1113
1418
  // src/cli.ts
1419
+ var VERSION = true ? "0.1.2" : "dev";
1114
1420
  var program = new Command();
1115
- program.name("agentreel").description("AgentReel \u2014 capture Claude Code and Cursor sessions locally").version("0.0.0");
1421
+ program.name("agentreel").description("AgentReel \u2014 capture Claude Code and Cursor sessions locally").version(VERSION);
1116
1422
  program.command("init").description("install Claude Code hooks and create the local SQLite buffer").action(async () => {
1117
1423
  await initCommand();
1118
1424
  });
@@ -1128,6 +1434,9 @@ program.command("link [api-key]").description("authenticate the local agent with
1128
1434
  program.command("push").description("upload pending events to agentreel.dev").action(async () => {
1129
1435
  await pushCommand();
1130
1436
  });
1437
+ program.command("daemon").description("run the background uploader (30s ticks, 1MB early-flush, exponential backoff)").action(async () => {
1438
+ await daemonCommand();
1439
+ });
1131
1440
  program.command("logout").description("clear local credentials").action(async () => {
1132
1441
  await logoutCommand();
1133
1442
  });
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/paths.ts","../src/hooks/install.ts","../src/cli.ts","../src/commands/init.ts","../src/config.ts","../src/db.ts","../src/commands/status.ts","../src/commands/auth.ts","../src/upload/client.ts","../src/commands/watch.ts","../src/cursor/paths.ts","../src/cursor/watcher.ts","../src/cursor/entries.ts","../src/cursor/diff.ts","../src/redact/ignore.ts","../src/redact/patterns.ts","../src/redact/scrubber.ts","../src/cursor/session.ts","../src/commands/push.ts","../src/upload/queue.ts","../src/hooks/handler.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { mkdirSync } from \"node:fs\";\n\nexport const HOME = homedir();\nexport const AGENTREEL_DIR = join(HOME, \".agentreel\");\nexport const DB_PATH = join(AGENTREEL_DIR, \"sessions.db\");\nexport const CONFIG_PATH = join(AGENTREEL_DIR, \"config.json\");\nexport const QUEUE_DIR = join(AGENTREEL_DIR, \"queue\");\nexport const LOG_PATH = join(AGENTREEL_DIR, \"agent.log\");\n\nexport const CLAUDE_DIR = join(HOME, \".claude\");\nexport const CLAUDE_SETTINGS_PATH = join(CLAUDE_DIR, \"settings.json\");\n\nexport function ensureAgentreelDir(): void {\n mkdirSync(AGENTREEL_DIR, { recursive: true });\n mkdirSync(QUEUE_DIR, { recursive: true });\n}\n","import { readFileSync, writeFileSync, existsSync, copyFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { CLAUDE_DIR, CLAUDE_SETTINGS_PATH } from \"../paths.js\";\n\nconst HOOK_MARKER = \"agentreel:v1\";\n\nconst HOOK_EVENTS = [\n \"SessionStart\",\n \"SessionEnd\",\n \"UserPromptSubmit\",\n \"PreToolUse\",\n \"PostToolUse\",\n \"Notification\",\n \"Stop\",\n \"SubagentStop\",\n \"PreCompact\",\n] as const;\n\ntype HookEntry = {\n type: \"command\";\n command: string;\n timeout?: number;\n};\n\ntype HookGroup = {\n matcher?: string;\n hooks: HookEntry[];\n __agentreel?: string;\n};\n\ntype ClaudeSettings = {\n hooks?: Record<string, HookGroup[]>;\n [key: string]: unknown;\n};\n\nfunction readSettings(): ClaudeSettings {\n if (!existsSync(CLAUDE_SETTINGS_PATH)) return {};\n const raw = readFileSync(CLAUDE_SETTINGS_PATH, \"utf8\");\n if (!raw.trim()) return {};\n return JSON.parse(raw) as ClaudeSettings;\n}\n\nfunction backupSettings(): string | null {\n if (!existsSync(CLAUDE_SETTINGS_PATH)) return null;\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backup = `${CLAUDE_SETTINGS_PATH}.agentreel-backup-${stamp}`;\n copyFileSync(CLAUDE_SETTINGS_PATH, backup);\n return backup;\n}\n\nfunction buildHookEntry(event: string, hookCommandPrefix: string): HookEntry {\n // Final command shape: `<prefix> hook <Event>`. The prefix can be an\n // absolute path to dist/cli.js (global / dev installs) or an `npx -y …`\n // form (when the user ran us via npx and we don't have a stable on-disk\n // location to point Claude Code at).\n return {\n type: \"command\",\n command: `${hookCommandPrefix} hook ${event}`,\n timeout: 5,\n };\n}\n\nexport function quotePath(p: string): string {\n return /[\\s'\"$`\\\\]/.test(p) ? `\"${p.replace(/\"/g, '\\\\\"')}\"` : p;\n}\n\nexport interface InstallResult {\n backup: string | null;\n installedEvents: string[];\n hookCommandPrefix: string;\n}\n\nexport function installClaudeCodeHooks(hookCommandPrefix: string): InstallResult {\n mkdirSync(CLAUDE_DIR, { recursive: true });\n mkdirSync(dirname(CLAUDE_SETTINGS_PATH), { recursive: true });\n const backup = backupSettings();\n\n const settings = readSettings();\n settings.hooks ??= {};\n\n for (const event of HOOK_EVENTS) {\n const groups = settings.hooks[event] ?? [];\n // Drop any prior agentreel-managed group so re-running is idempotent.\n const filtered = groups.filter((g) => g.__agentreel !== HOOK_MARKER);\n filtered.push({\n matcher: \".*\",\n __agentreel: HOOK_MARKER,\n hooks: [buildHookEntry(event, hookCommandPrefix)],\n });\n settings.hooks[event] = filtered;\n }\n\n writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2) + \"\\n\", \"utf8\");\n return { backup, installedEvents: [...HOOK_EVENTS], hookCommandPrefix };\n}\n\nexport function uninstallClaudeCodeHooks(): { backup: string | null } {\n if (!existsSync(CLAUDE_SETTINGS_PATH)) return { backup: null };\n const backup = backupSettings();\n const settings = readSettings();\n if (settings.hooks) {\n for (const event of Object.keys(settings.hooks)) {\n const groups = settings.hooks[event] ?? [];\n const remaining = groups.filter((g) => g.__agentreel !== HOOK_MARKER);\n if (remaining.length === 0) delete settings.hooks[event];\n else settings.hooks[event] = remaining;\n }\n if (Object.keys(settings.hooks).length === 0) delete settings.hooks;\n }\n writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2) + \"\\n\", \"utf8\");\n return { backup };\n}\n","import { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { linkCommand, logoutCommand, uninstallCommand } from \"./commands/auth.js\";\nimport { watchCommand } from \"./commands/watch.js\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { runHook } from \"./hooks/handler.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"agentreel\")\n .description(\"AgentReel — capture Claude Code and Cursor sessions locally\")\n .version(\"0.0.0\");\n\nprogram\n .command(\"init\")\n .description(\"install Claude Code hooks and create the local SQLite buffer\")\n .action(async () => {\n await initCommand();\n });\n\nprogram\n .command(\"status\")\n .description(\"show local capture status, recent sessions, queue depth\")\n .action(async () => {\n await statusCommand();\n });\n\nprogram\n .command(\"watch\")\n .description(\"watch Cursor's local history and capture edits as events\")\n .action(async () => {\n await watchCommand();\n });\n\nprogram\n .command(\"link [api-key]\")\n .description(\"authenticate the local agent with agentreel.dev\")\n .option(\"--api <url>\", \"override the API base URL (default https://api.agentreel.dev)\")\n .action(async (apiKey: string | undefined, opts: { api?: string }) => {\n await linkCommand(apiKey, opts);\n });\n\nprogram\n .command(\"push\")\n .description(\"upload pending events to agentreel.dev\")\n .action(async () => {\n await pushCommand();\n });\n\nprogram\n .command(\"logout\")\n .description(\"clear local credentials\")\n .action(async () => {\n await logoutCommand();\n });\n\nprogram\n .command(\"uninstall\")\n .description(\"remove AgentReel hooks from ~/.claude/settings.json\")\n .action(async () => {\n await uninstallCommand();\n });\n\nprogram\n .command(\"hook <event>\")\n .description(\"internal: hook handler invoked by Claude Code (reads JSON from stdin)\")\n .action(async (event: string) => {\n await runHook(event);\n });\n\nprogram.parseAsync(process.argv).catch((err) => {\n // Top-level: if we got this far on a hook invocation, something is very wrong.\n // For all other commands, print and exit non-zero.\n const cmd = process.argv[2];\n if (cmd === \"hook\") {\n process.exit(0);\n }\n console.error(err);\n process.exit(1);\n});\n","import { realpathSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { sep } from \"node:path\";\nimport pc from \"picocolors\";\nimport { installClaudeCodeHooks, quotePath } from \"../hooks/install.js\";\nimport { readConfig, writeConfig } from \"../config.js\";\nimport { getDb } from \"../db.js\";\nimport { ensureAgentreelDir } from \"../paths.js\";\n\nconst PACKAGE_NAME = \"@agentreel/agent\";\n\ninterface ResolvedBinary {\n absolutePath: string;\n isEphemeral: boolean; // true when running from an npx temp dir\n}\n\nfunction resolveAgentBinary(): ResolvedBinary {\n // tsup builds to dist/cli.js with a node shebang; npm symlinks it to\n // <prefix>/bin/agentreel. The realpath through that symlink is stable for\n // npm i -g and for in-tree development. For `npx` installs it points\n // inside ~/.npm/_npx/<hash>/ which gets cleaned eventually — we detect\n // that and fall back to an `npx -y …` hook command instead.\n const here = fileURLToPath(import.meta.url);\n const real = realpathSync(here);\n const isEphemeral = real.includes(`${sep}_npx${sep}`) || real.includes(\"/_npx/\");\n return { absolutePath: real, isEphemeral };\n}\n\nfunction chooseHookPrefix(bin: ResolvedBinary): { prefix: string; mode: \"absolute\" | \"npx\" } {\n if (process.env.AGENTREEL_HOOK_COMMAND) {\n return { prefix: process.env.AGENTREEL_HOOK_COMMAND, mode: \"absolute\" };\n }\n if (bin.isEphemeral) {\n return { prefix: `npx -y ${PACKAGE_NAME}`, mode: \"npx\" };\n }\n return { prefix: quotePath(bin.absolutePath), mode: \"absolute\" };\n}\n\nexport async function initCommand(): Promise<void> {\n ensureAgentreelDir();\n\n // Touch the DB so the file exists and migrations run.\n getDb();\n\n const cfg = readConfig();\n if (!cfg.installedAt) cfg.installedAt = Date.now();\n cfg.hooksInstalled = true;\n writeConfig(cfg);\n\n const binary = resolveAgentBinary();\n const { prefix, mode } = chooseHookPrefix(binary);\n const result = installClaudeCodeHooks(prefix);\n\n console.log(pc.bold(pc.cyan(\"\\n AgentReel \")) + pc.dim(\"Loom for AI coding sessions\\n\"));\n console.log(pc.green(\"✓\") + \" Created ~/.agentreel/sessions.db\");\n console.log(pc.green(\"✓\") + \" Wrote ~/.agentreel/config.json\");\n console.log(\n pc.green(\"✓\") +\n ` Installed ${result.installedEvents.length} Claude Code hooks → ~/.claude/settings.json`,\n );\n if (result.backup) {\n console.log(pc.dim(` (backup: ${result.backup})`));\n }\n console.log();\n console.log(pc.dim(\" Hook command: \") + pc.dim(`${prefix} hook <Event>`));\n if (mode === \"npx\") {\n console.log(\n pc.dim(\" \") +\n pc.yellow(\"•\") +\n pc.dim(\n ` Hooks resolve via npx every time Claude Code fires an event.\\n ` +\n ` For faster cold starts, run: npm i -g ${PACKAGE_NAME}`,\n ),\n );\n }\n console.log();\n console.log(pc.bold(\"Next:\") + \" open Claude Code and run a prompt.\");\n console.log(\" then \" + pc.cyan(\"agentreel status\") + \" to see captured events.\\n\");\n}\n","import { readFileSync, writeFileSync, existsSync, chmodSync } from \"node:fs\";\nimport { CONFIG_PATH, ensureAgentreelDir } from \"./paths.js\";\n\nexport interface AgentConfig {\n apiKey?: string;\n apiBaseUrl: string;\n workspaceId?: string;\n installedAt?: number;\n hooksInstalled?: boolean;\n schemaVersion: 1;\n}\n\nconst DEFAULT: AgentConfig = {\n apiBaseUrl: \"https://api.agentreel.dev\",\n schemaVersion: 1,\n};\n\nexport function readConfig(): AgentConfig {\n if (!existsSync(CONFIG_PATH)) return { ...DEFAULT };\n try {\n const raw = readFileSync(CONFIG_PATH, \"utf8\");\n return { ...DEFAULT, ...JSON.parse(raw) };\n } catch {\n return { ...DEFAULT };\n }\n}\n\nexport function writeConfig(cfg: AgentConfig): void {\n ensureAgentreelDir();\n // mode 0o600 — config.json holds the API key; only the owning user\n // should ever be able to read it. chmod after write to be sure even\n // if the file already existed with looser perms.\n writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2) + \"\\n\", {\n encoding: \"utf8\",\n mode: 0o600,\n });\n try {\n chmodSync(CONFIG_PATH, 0o600);\n } catch {\n // Non-Unix or filesystem without mode bits — best effort.\n }\n}\n","import Database from \"better-sqlite3\";\nimport type { Database as DB } from \"better-sqlite3\";\nimport { DB_PATH, ensureAgentreelDir } from \"./paths.js\";\nimport type { AgentEvent, Session, Tool } from \"@agentreel/shared-types\";\n\nlet _db: DB | null = null;\n\nexport function getDb(): DB {\n if (_db) return _db;\n ensureAgentreelDir();\n const db = new Database(DB_PATH);\n db.pragma(\"journal_mode = WAL\");\n db.pragma(\"synchronous = NORMAL\");\n migrate(db);\n _db = db;\n return db;\n}\n\nfunction migrate(db: DB): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n tool TEXT NOT NULL,\n started_at INTEGER NOT NULL,\n ended_at INTEGER,\n cwd TEXT,\n total_cost_cents INTEGER,\n total_tokens INTEGER\n );\n CREATE TABLE IF NOT EXISTS events (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n tool TEXT NOT NULL,\n type TEXT NOT NULL,\n ts INTEGER NOT NULL,\n cwd TEXT,\n payload TEXT NOT NULL,\n uploaded_at INTEGER\n );\n CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id, ts);\n CREATE INDEX IF NOT EXISTS idx_events_pending ON events(uploaded_at) WHERE uploaded_at IS NULL;\n `);\n}\n\nexport function upsertSession(s: Session): void {\n const db = getDb();\n db.prepare(\n `INSERT INTO sessions (id, tool, started_at, ended_at, cwd, total_cost_cents, total_tokens)\n VALUES (@id, @tool, @startedAt, @endedAt, @cwd, @totalCostCents, @totalTokens)\n ON CONFLICT(id) DO UPDATE SET\n ended_at = COALESCE(excluded.ended_at, sessions.ended_at),\n cwd = COALESCE(excluded.cwd, sessions.cwd),\n total_cost_cents = COALESCE(excluded.total_cost_cents, sessions.total_cost_cents),\n total_tokens = COALESCE(excluded.total_tokens, sessions.total_tokens)`,\n ).run({\n id: s.id,\n tool: s.tool,\n startedAt: s.startedAt,\n endedAt: s.endedAt ?? null,\n cwd: s.cwd ?? null,\n totalCostCents: s.totalCostCents ?? null,\n totalTokens: s.totalTokens ?? null,\n });\n}\n\nexport function insertEvent(e: AgentEvent): void {\n const db = getDb();\n db.prepare(\n `INSERT OR IGNORE INTO events (id, session_id, tool, type, ts, cwd, payload)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n ).run(e.id, e.sessionId, e.tool, e.type, e.ts, e.cwd ?? null, JSON.stringify(e.payload ?? {}));\n}\n\nexport function countEvents(): { total: number; pending: number } {\n const db = getDb();\n const total = db.prepare(`SELECT COUNT(*) AS c FROM events`).get() as { c: number };\n const pending = db\n .prepare(`SELECT COUNT(*) AS c FROM events WHERE uploaded_at IS NULL`)\n .get() as { c: number };\n return { total: total.c, pending: pending.c };\n}\n\nexport function listRecentSessions(limit = 10): Array<{\n id: string;\n tool: Tool;\n started_at: number;\n ended_at: number | null;\n cwd: string | null;\n}> {\n const db = getDb();\n return db\n .prepare(\n `SELECT id, tool, started_at, ended_at, cwd\n FROM sessions ORDER BY started_at DESC LIMIT ?`,\n )\n .all(limit) as Array<{\n id: string;\n tool: Tool;\n started_at: number;\n ended_at: number | null;\n cwd: string | null;\n }>;\n}\n","import pc from \"picocolors\";\nimport { existsSync } from \"node:fs\";\nimport { CLAUDE_SETTINGS_PATH, DB_PATH, CONFIG_PATH } from \"../paths.js\";\nimport { countEvents, listRecentSessions } from \"../db.js\";\nimport { readConfig } from \"../config.js\";\n\nfunction fmtDuration(ms: number): string {\n const s = Math.round(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n if (m < 60) return `${m}m ${s % 60}s`;\n const h = Math.floor(m / 60);\n return `${h}h ${m % 60}m`;\n}\n\nexport async function statusCommand(): Promise<void> {\n const cfg = readConfig();\n console.log(pc.bold(pc.cyan(\"AgentReel status\\n\")));\n console.log(\" config: \" + (existsSync(CONFIG_PATH) ? CONFIG_PATH : pc.red(\"missing\")));\n console.log(\" database: \" + (existsSync(DB_PATH) ? DB_PATH : pc.red(\"not initialized\")));\n console.log(\n \" claude hooks: \" +\n (existsSync(CLAUDE_SETTINGS_PATH) ? CLAUDE_SETTINGS_PATH : pc.red(\"not installed\")),\n );\n console.log(\" api base: \" + cfg.apiBaseUrl);\n console.log(\" authenticated: \" + (cfg.apiKey ? pc.green(\"yes\") : pc.yellow(\"no\")));\n console.log();\n\n if (!existsSync(DB_PATH)) {\n console.log(pc.yellow(\"Run `agentreel init` to install hooks.\"));\n return;\n }\n\n const { total, pending } = countEvents();\n console.log(` events captured: ${total}`);\n console.log(` pending upload: ${pending}`);\n console.log();\n\n const sessions = listRecentSessions(5);\n if (sessions.length === 0) {\n console.log(pc.dim(\" no sessions yet — start a Claude Code session to capture one.\"));\n return;\n }\n console.log(pc.bold(\"recent sessions:\"));\n for (const s of sessions) {\n const dur = s.ended_at ? fmtDuration(s.ended_at - s.started_at) : pc.dim(\"active\");\n const ts = new Date(s.started_at).toLocaleString();\n console.log(` ${pc.dim(s.id.slice(0, 8))} ${s.tool.padEnd(11)} ${ts} ${dur}`);\n }\n}\n","import pc from \"picocolors\";\nimport { readConfig, writeConfig } from \"../config.js\";\nimport { postIngest } from \"../upload/client.js\";\n\nexport async function logoutCommand(): Promise<void> {\n const cfg = readConfig();\n cfg.apiKey = undefined;\n cfg.workspaceId = undefined;\n writeConfig(cfg);\n console.log(pc.green(\"✓\") + \" Cleared local credentials.\");\n}\n\ninterface LinkOpts {\n api?: string;\n}\n\nexport async function linkCommand(rawKey: string | undefined, opts: LinkOpts): Promise<void> {\n const key = (rawKey ?? (await promptHidden(\"Paste your AgentReel API key: \"))).trim();\n if (!key) {\n console.error(pc.red(\"✗ No key provided.\"));\n process.exit(1);\n }\n if (!key.startsWith(\"ar_live_\")) {\n console.error(pc.red(\"✗ Keys start with `ar_live_`. Did you paste the right value?\"));\n process.exit(1);\n }\n\n const cfg = readConfig();\n cfg.apiKey = key;\n if (opts.api) cfg.apiBaseUrl = opts.api;\n\n console.log(pc.dim(` validating against ${cfg.apiBaseUrl}…`));\n try {\n await postIngest(cfg, {});\n } catch (err) {\n console.error(pc.red(\"✗ Validation failed: \") + (err as Error).message);\n process.exit(1);\n }\n writeConfig(cfg);\n console.log(pc.green(\"✓\") + \" Linked.\");\n console.log(pc.dim(\" api: \") + cfg.apiBaseUrl);\n console.log(pc.dim(\" key: \") + key.slice(0, 12) + \"…\");\n}\n\nexport async function uninstallCommand(): Promise<void> {\n const { uninstallClaudeCodeHooks } = await import(\"../hooks/install.js\");\n const { backup } = uninstallClaudeCodeHooks();\n console.log(pc.green(\"✓\") + \" Removed AgentReel hooks from ~/.claude/settings.json\");\n if (backup) console.log(pc.dim(` (backup: ${backup})`));\n}\n\nconst CTRL_C = 0x03;\nconst BACKSPACE = 0x7f;\nconst BACKSPACE_ALT = 0x08;\nconst NEWLINE = 0x0a;\nconst CARRIAGE = 0x0d;\n\nfunction promptHidden(prompt: string): Promise<string> {\n return new Promise((resolve) => {\n process.stdout.write(prompt);\n let buf = \"\";\n const stdin = process.stdin;\n stdin.setRawMode?.(true);\n stdin.resume();\n stdin.setEncoding(\"utf8\");\n const onData = (chunk: string) => {\n for (const ch of chunk) {\n const code = ch.charCodeAt(0);\n if (code === NEWLINE || code === CARRIAGE) {\n stdin.setRawMode?.(false);\n stdin.pause();\n stdin.removeListener(\"data\", onData);\n process.stdout.write(\"\\n\");\n return resolve(buf);\n }\n if (code === CTRL_C) {\n stdin.setRawMode?.(false);\n process.exit(130);\n }\n if (code === BACKSPACE || code === BACKSPACE_ALT) {\n buf = buf.slice(0, -1);\n } else {\n buf += ch;\n }\n }\n };\n stdin.on(\"data\", onData);\n });\n}\n","import type { AgentConfig } from \"../config.js\";\n\nexport interface IngestSession {\n id: string;\n tool: string;\n started_at: number;\n ended_at?: number | null;\n cwd?: string | null;\n total_cost_cents?: number | null;\n total_tokens?: number | null;\n}\n\nexport interface IngestEvent {\n id: string;\n session_id: string;\n ts: number;\n type: string;\n tool: string;\n cwd?: string | null;\n payload: unknown;\n}\n\nexport interface IngestResponse {\n ok: boolean;\n sessions_written?: number;\n events_written?: number;\n error?: string;\n reason?: string;\n}\n\nexport async function postIngest(\n cfg: AgentConfig,\n body: { sessions?: IngestSession[]; events?: IngestEvent[] },\n): Promise<IngestResponse> {\n if (!cfg.apiKey) {\n throw new Error(\"Not linked. Run: agentreel link <key>\");\n }\n const url = `${cfg.apiBaseUrl.replace(/\\/$/, \"\")}/api/v1/sessions/ingest`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${cfg.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n let data: IngestResponse;\n try {\n data = (await res.json()) as IngestResponse;\n } catch {\n throw new Error(`Ingest returned ${res.status} with non-JSON body`);\n }\n if (!res.ok || !data.ok) {\n throw new Error(\n `Ingest failed: ${res.status} ${data.error ?? \"\"} ${data.reason ?? \"\"}`.trim(),\n );\n }\n return data;\n}\n","import pc from \"picocolors\";\nimport { existsSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { cursorHistoryDir } from \"../cursor/paths.js\";\nimport { startCursorWatcher } from \"../cursor/watcher.js\";\nimport { CursorSessionManager } from \"../cursor/session.js\";\nimport { ensureAgentreelDir } from \"../paths.js\";\nimport { getDb } from \"../db.js\";\n\nexport async function watchCommand(): Promise<void> {\n ensureAgentreelDir();\n // Touch DB so the schema exists.\n getDb();\n\n const dir = cursorHistoryDir();\n if (!existsSync(dir)) {\n console.error(pc.red(\"✗ Cursor history directory not found:\"));\n console.error(\" \" + dir);\n console.error();\n console.error(pc.dim(\"Open Cursor at least once, edit a file, then re-run.\"));\n console.error(pc.dim(\"(Or set AGENTREEL_CURSOR_HISTORY_DIR to a custom path.)\"));\n process.exit(1);\n }\n\n console.log(pc.bold(pc.cyan(\"AgentReel · Cursor watcher\\n\")));\n console.log(pc.dim(\" watching \") + dir);\n console.log(pc.dim(\" press Ctrl+C to stop\\n\"));\n\n const sessions = new CursorSessionManager();\n\n const watcher = startCursorWatcher(dir, async (snap) => {\n const { sessionId, isNew } = sessions.ingest(snap);\n const ts = new Date(snap.timestamp).toLocaleTimeString();\n const ws = snap.workspace ? basename(snap.workspace) : pc.dim(\"no-workspace\");\n const file = snap.filePath.split(\"/\").slice(-2).join(\"/\");\n const sourceLabel =\n snap.source === \"cursor-ai\"\n ? pc.magenta(\"ai\")\n : snap.source === \"cursor-manual\"\n ? pc.cyan(\"man\")\n : pc.dim(\"?\");\n const stats = snap.binary\n ? pc.dim(\"binary\")\n : `${pc.green(\"+\" + snap.added)} ${pc.red(\"-\" + snap.removed)}`;\n if (isNew) {\n console.log(\n `${pc.dim(ts)} ${pc.yellow(\"session\")} ${pc.dim(sessionId)} ${ws}`,\n );\n }\n console.log(`${pc.dim(ts)} edit ${sourceLabel} ${file.padEnd(36)} ${stats}`);\n });\n\n // Graceful shutdown — flush sessions, close watcher.\n const shutdown = async () => {\n console.log(pc.dim(\"\\n closing sessions…\"));\n sessions.closeAll();\n await watcher.close();\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n","import { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\n\n// Cursor is a VS Code fork; its history layout matches Code's:\n// <userData>/User/History/<hashId>/{entries.json, <id>.<ext>, ...}\nexport function defaultCursorHistoryDir(): string {\n const home = homedir();\n switch (platform()) {\n case \"darwin\":\n return join(home, \"Library\", \"Application Support\", \"Cursor\", \"User\", \"History\");\n case \"win32\": {\n const appData = process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\");\n return join(appData, \"Cursor\", \"User\", \"History\");\n }\n default:\n return join(home, \".config\", \"Cursor\", \"User\", \"History\");\n }\n}\n\nexport function cursorHistoryDir(): string {\n return process.env.AGENTREEL_CURSOR_HISTORY_DIR ?? defaultCursorHistoryDir();\n}\n","import chokidar, { type FSWatcher } from \"chokidar\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { basename, dirname, join } from \"node:path\";\nimport {\n findWorkspaceRoot,\n isProbablyBinary,\n readEntries,\n resourceToPath,\n type HistoryEntry,\n} from \"./entries.js\";\nimport { computeDiff } from \"./diff.js\";\nimport { isIgnored } from \"../redact/ignore.js\";\nimport { scrubString } from \"../redact/scrubber.js\";\n\nexport interface SnapshotEvent {\n filePath: string;\n workspace: string | null;\n source: \"cursor-ai\" | \"cursor-manual\" | \"unknown\";\n timestamp: number;\n patch: string;\n added: number;\n removed: number;\n binary: boolean;\n}\n\nexport type SnapshotHandler = (e: SnapshotEvent) => void | Promise<void>;\n\nexport interface WatcherOptions {\n /** Discard the very first entry for any tracked file (Cursor's \"first observation\" snapshot). Default true. */\n skipFirstObservation?: boolean;\n}\n\nexport function startCursorWatcher(\n historyDir: string,\n onSnapshot: SnapshotHandler,\n opts: WatcherOptions = {},\n): FSWatcher {\n const skipFirst = opts.skipFirstObservation ?? true;\n\n const watcher = chokidar.watch(historyDir, {\n ignoreInitial: true,\n depth: 2,\n persistent: true,\n awaitWriteFinish: { stabilityThreshold: 120, pollInterval: 40 },\n });\n\n watcher.on(\"add\", async (path) => {\n try {\n const folder = dirname(path);\n const file = basename(path);\n // The folder name is the resource hash; siblings include entries.json\n // and one snapshot file per saved version.\n if (file === \"entries.json\") return;\n // Wait briefly for entries.json to mention this file — Cursor writes\n // the snapshot before updating the manifest in some versions.\n const ctx = await waitForEntry(folder, file, 2_000);\n if (!ctx) return;\n await processSnapshot(folder, ctx, skipFirst, onSnapshot);\n } catch (err) {\n // The watcher must never crash the host process.\n // eslint-disable-next-line no-console\n console.error(\"[agentreel] cursor watcher: error processing\", path, err);\n }\n });\n\n return watcher;\n}\n\ninterface SnapshotContext {\n resource: string;\n current: HistoryEntry;\n previous: HistoryEntry | null;\n currentIdx: number;\n}\n\nasync function waitForEntry(\n folder: string,\n fileId: string,\n totalMs: number,\n): Promise<SnapshotContext | null> {\n const start = Date.now();\n while (Date.now() - start < totalMs) {\n const file = await readEntries(folder);\n if (file) {\n const idx = file.entries.findIndex((e) => e.id === fileId);\n if (idx >= 0) {\n const current = file.entries[idx];\n if (!current) return null;\n const previous = idx > 0 ? (file.entries[idx - 1] ?? null) : null;\n return { resource: file.resource, current, previous, currentIdx: idx };\n }\n }\n await sleep(150);\n }\n return null;\n}\n\nasync function processSnapshot(\n folder: string,\n ctx: SnapshotContext,\n skipFirst: boolean,\n onSnapshot: SnapshotHandler,\n): Promise<void> {\n const filePath = resourceToPath(ctx.resource);\n if (!filePath) return;\n\n // Skip files inside common dependency / build dirs — high noise, low value.\n if (NOISY_PATH.test(filePath)) return;\n\n // Honor per-workspace .agentreelignore (gitignore syntax).\n const workspace = findWorkspaceRoot(filePath);\n if (isIgnored(workspace, filePath)) return;\n\n if (!ctx.previous) {\n // First time Cursor has seen this file — no diff to compute.\n if (skipFirst) return;\n }\n\n const newSnapshot = join(folder, ctx.current.id);\n let before = \"\";\n let after = \"\";\n if (ctx.previous) {\n const prevPath = join(folder, ctx.previous.id);\n if (existsSync(prevPath)) {\n before = await safeRead(prevPath);\n }\n }\n if (existsSync(newSnapshot)) {\n after = await safeRead(newSnapshot);\n }\n\n // No real change — chokidar can fire spurious \"add\" events on some\n // filesystems. Skip silently.\n if (before === after) return;\n\n const binary = isProbablyBinary(filePath);\n let patch = \"\";\n let added = 0;\n let removed = 0;\n if (!binary) {\n // Scrub BEFORE diffing so secrets never enter the patch text. We diff\n // the redacted versions instead — the dashboard will still show the\n // shape of the change, just with [REDACTED:*] in place of values.\n const beforeSafe = scrubString(before);\n const afterSafe = scrubString(after);\n const result = computeDiff(beforeSafe, afterSafe);\n patch = result.patch;\n added = result.added;\n removed = result.removed;\n }\n\n const source = classifySource(ctx.current.source);\n onSnapshot({\n filePath,\n workspace,\n source,\n timestamp: ctx.current.timestamp ?? Date.now(),\n patch,\n added,\n removed,\n binary,\n });\n}\n\nfunction classifySource(raw?: string): SnapshotEvent[\"source\"] {\n if (!raw) return \"cursor-manual\";\n const s = raw.toLowerCase();\n if (s.includes(\"composer\") || s.includes(\"ai\") || s.includes(\"chat\")) return \"cursor-ai\";\n return \"cursor-manual\";\n}\n\nasync function safeRead(path: string): Promise<string> {\n try {\n return await readFile(path, \"utf8\");\n } catch {\n return \"\";\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nconst NOISY_PATH =\n /[\\\\/](node_modules|\\.next|\\.turbo|dist|build|\\.git|coverage|\\.cache|\\.venv|venv|target|out)[\\\\/]/;\n","import { readFile } from \"node:fs/promises\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { join, dirname, resolve, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport interface HistoryEntry {\n id: string; // filename of the snapshot, e.g. \"rEMc.ts\"\n timestamp: number; // ms epoch\n source?: string; // e.g. \"Cursor.Composer\" for AI edits, \"\" for manual\n}\n\nexport interface HistoryFile {\n version: number;\n resource: string; // file:///...\n entries: HistoryEntry[];\n}\n\nexport async function readEntries(historyFolder: string): Promise<HistoryFile | null> {\n const path = join(historyFolder, \"entries.json\");\n if (!existsSync(path)) return null;\n try {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as HistoryFile;\n } catch {\n return null;\n }\n}\n\nexport function resourceToPath(resource: string): string | null {\n if (!resource.startsWith(\"file://\")) return null;\n try {\n return fileURLToPath(resource);\n } catch {\n return null;\n }\n}\n\n// Walk up from `path` looking for a directory that contains `.git`. That\n// directory is the workspace root for the purposes of grouping events.\nexport function findWorkspaceRoot(path: string): string | null {\n let dir = dirname(resolve(path));\n while (dir && dir !== sep) {\n const git = join(dir, \".git\");\n try {\n if (existsSync(git)) return dir;\n } catch {\n // ignore\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nexport function isProbablyBinary(path: string): boolean {\n try {\n const s = statSync(path);\n if (s.size > 1024 * 1024) return true; // >1MB — skip diff\n } catch {\n return false;\n }\n // Lightweight extension allowlist for v1. Anything else, treat as binary\n // and skip diffing (we still record the event, just without a patch).\n const text =\n /\\.(ts|tsx|js|jsx|mjs|cjs|json|jsonc|md|mdx|css|scss|html|xml|yaml|yml|toml|sh|bash|zsh|fish|py|rb|go|rs|java|kt|swift|c|cc|cpp|h|hpp|cs|php|sql|prisma|graphql|gql|env|gitignore|dockerfile|tf|hcl|lua|vue|svelte|astro|txt|csv|tsv|log|conf|ini)$/i;\n return !text.test(path);\n}\n","import { diffLines } from \"diff\";\nimport DiffMatchPatch from \"diff-match-patch\";\n\nexport interface DiffResult {\n /** diff-match-patch wire format — compact, reversible. Stored on the event. */\n patch: string;\n /** Line-level adds/removes — surfaced in the dashboard list. */\n added: number;\n removed: number;\n}\n\nconst dmp = new DiffMatchPatch.diff_match_patch();\n// Allow expensive cleanup on small diffs; cap at 1MB to avoid pathological inputs.\ndmp.Diff_Timeout = 1.0;\n\nexport function computeDiff(before: string, after: string): DiffResult {\n // Patch — compact DMP format, much smaller than unified diff for small edits.\n const patches = dmp.patch_make(before, after);\n const patch = dmp.patch_toText(patches);\n\n // Line-level stats (cheap; users see these in the timeline list).\n let added = 0;\n let removed = 0;\n for (const part of diffLines(before, after)) {\n const n =\n part.count ??\n (part.value.match(/\\n/g)?.length ?? (part.value.length > 0 ? 1 : 0));\n if (part.added) added += n;\n else if (part.removed) removed += n;\n }\n\n return { patch, added, removed };\n}\n","import { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\nimport ignore, { type Ignore } from \"ignore\";\n\nconst FILENAME = \".agentreelignore\";\nconst TTL_MS = 5_000; // re-read the file at most every 5 seconds per workspace\n\ninterface CacheEntry {\n matcher: Ignore | null;\n loadedAt: number;\n fileMtime: number;\n}\n\nconst cache = new Map<string, CacheEntry>();\n\nfunction loadFor(workspace: string): Ignore | null {\n const path = join(workspace, FILENAME);\n if (!existsSync(path)) return null;\n try {\n const raw = readFileSync(path, \"utf8\");\n return ignore({ allowRelativePaths: true }).add(raw);\n } catch {\n return null;\n }\n}\n\nfunction getCached(workspace: string): Ignore | null {\n const entry = cache.get(workspace);\n const path = join(workspace, FILENAME);\n let mtime = 0;\n try {\n mtime = existsSync(path) ? statSync(path).mtimeMs : 0;\n } catch {\n mtime = 0;\n }\n const now = Date.now();\n if (entry && now - entry.loadedAt < TTL_MS && entry.fileMtime === mtime) {\n return entry.matcher;\n }\n const matcher = loadFor(workspace);\n cache.set(workspace, { matcher, loadedAt: now, fileMtime: mtime });\n return matcher;\n}\n\n/**\n * Returns true if `filePath` should be skipped because it matches a rule\n * in the workspace's `.agentreelignore`. Returns false when the workspace\n * has no ignore file or the path doesn't match.\n */\nexport function isIgnored(workspace: string | null | undefined, filePath: string): boolean {\n if (!workspace) return false;\n const matcher = getCached(workspace);\n if (!matcher) return false;\n const rel = relative(workspace, filePath);\n // ignore can't reason about paths that escape the workspace.\n if (!rel || rel.startsWith(\"..\") || rel.startsWith(sep)) return false;\n // gitignore syntax expects forward slashes.\n return matcher.ignores(rel.split(sep).join(\"/\"));\n}\n","// Order matters. Multiline / specific patterns first, generic last.\n\nexport interface RedactionRule {\n name: string;\n re: RegExp;\n replacement: string | ((match: string) => string);\n}\n\nexport const PATTERNS: RedactionRule[] = [\n // PEM-encoded private keys (multiline, must run early)\n {\n name: \"pem-private-key\",\n re: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\\s\\S]+?-----END [A-Z0-9 ]*PRIVATE KEY-----/g,\n replacement: \"[REDACTED:private-key]\",\n },\n\n // JWT (header.payload.signature) — eyJ-prefixed base64url segments\n {\n name: \"jwt\",\n re: /\\beyJ[A-Za-z0-9_-]{8,}\\.eyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\b/g,\n replacement: \"[REDACTED:jwt]\",\n },\n\n // GitHub fine-grained PATs (84 chars after prefix is the official format)\n {\n name: \"github-fine-grained-pat\",\n re: /\\bgithub_pat_[A-Za-z0-9_]{82,}\\b/g,\n replacement: \"[REDACTED:gh-fine-pat]\",\n },\n // GitHub OAuth, PAT, app, server, refresh tokens\n {\n name: \"github-token\",\n re: /\\bgh[pousr]_[A-Za-z0-9]{36,255}\\b/g,\n replacement: \"[REDACTED:gh-token]\",\n },\n\n // Anthropic\n {\n name: \"anthropic-key\",\n re: /\\bsk-ant-(?:api\\d{2}-)?[A-Za-z0-9_-]{40,}\\b/g,\n replacement: \"[REDACTED:anthropic-key]\",\n },\n\n // OpenAI (sk-proj-..., sk-svcacct-..., legacy sk-...)\n {\n name: \"openai-key\",\n re: /\\bsk-(?:proj-|svcacct-|admin-)?[A-Za-z0-9_-]{20,}\\b/g,\n replacement: \"[REDACTED:openai-key]\",\n },\n\n // Stripe\n {\n name: \"stripe-secret\",\n re: /\\bsk_(?:test|live)_[A-Za-z0-9]{16,}\\b/g,\n replacement: \"[REDACTED:stripe-secret]\",\n },\n {\n name: \"stripe-restricted\",\n re: /\\brk_(?:test|live)_[A-Za-z0-9]{16,}\\b/g,\n replacement: \"[REDACTED:stripe-restricted]\",\n },\n {\n name: \"stripe-publishable\",\n re: /\\bpk_(?:test|live)_[A-Za-z0-9]{16,}\\b/g,\n replacement: \"[REDACTED:stripe-publishable]\",\n },\n {\n name: \"stripe-webhook\",\n re: /\\bwhsec_[A-Za-z0-9]{32,}\\b/g,\n replacement: \"[REDACTED:stripe-webhook]\",\n },\n\n // AWS access key IDs (and STS / temporary forms)\n {\n name: \"aws-access-key-id\",\n re: /\\b(?:AKIA|ASIA|AGPA|AROA|AIDA|ANPA|ANVA|AIPA)[0-9A-Z]{16}\\b/g,\n replacement: \"[REDACTED:aws-key-id]\",\n },\n\n // Slack tokens\n {\n name: \"slack-token\",\n re: /\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b/g,\n replacement: \"[REDACTED:slack-token]\",\n },\n\n // Google API keys\n {\n name: \"google-api-key\",\n re: /\\bAIza[A-Za-z0-9_-]{35}\\b/g,\n replacement: \"[REDACTED:google-api-key]\",\n },\n\n // npm tokens\n {\n name: \"npm-token\",\n re: /\\bnpm_[A-Za-z0-9]{36}\\b/g,\n replacement: \"[REDACTED:npm-token]\",\n },\n\n // dotenv-style KEY=VALUE on its own line, where the KEY name looks sensitive.\n // This is a fallback for arbitrary secrets that don't match a specific\n // provider pattern. Captures the key, replaces the value.\n {\n name: \"dotenv-secret\",\n re: /^(\\s*(?:export\\s+)?[A-Z][A-Z0-9_]*?(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|PWD|API|AUTH|CREDENTIAL|PRIVATE|SESSION|COOKIE|BEARER|DSN)[A-Z0-9_]*\\s*=\\s*)(['\"]?)([^\\n'\"]{4,})\\2/gm,\n replacement: (m: string) => {\n // m is the full match; we keep the \"KEY=\" and quote, replace the value.\n // Re-run a small regex against m to preserve the prefix.\n const inner =\n /^(\\s*(?:export\\s+)?[A-Z][A-Z0-9_]*\\s*=\\s*)(['\"]?)([^\\n'\"]{4,})\\2/.exec(m);\n if (!inner) return \"[REDACTED:dotenv-secret]\";\n const [, prefix, quote] = inner;\n return `${prefix ?? \"\"}${quote ?? \"\"}[REDACTED:dotenv-secret]${quote ?? \"\"}`;\n },\n },\n\n // Email addresses\n {\n name: \"email\",\n re: /\\b[A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z]{2,}\\b/g,\n replacement: \"[REDACTED:email]\",\n },\n\n // IPv4 — validates each octet is 0-255 to cut version-string false positives.\n // Skips three benign forms below in the post-filter.\n {\n name: \"ipv4\",\n re: /\\b(?:(?:25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\b/g,\n replacement: (m: string) => {\n if (m === \"0.0.0.0\" || m === \"127.0.0.1\" || m === \"255.255.255.255\") return m;\n return \"[REDACTED:ip]\";\n },\n },\n];\n\n// Sensitive key-name shapes used by the recursive object walker. If an\n// object's key name matches AND the value is a non-trivial string, we redact\n// the whole value regardless of provider-pattern match.\n// Names that almost-always carry a secret value. Avoid generic words like\n// `session` (matches `session_id`) or bare `token` (matches `csrf_token`\n// inputs that are themselves not sensitive in our context).\nexport const SENSITIVE_KEY_RE =\n /(?:^|[_\\-.])(?:api[_-]?key|access[_-]?token|secret|password|passwd|pwd|authorization|bearer|credential|private[_-]?key|client[_-]?secret|webhook[_-]?secret|service[_-]?account|refresh[_-]?token)(?:$|[_\\-.])/i;\n","import { PATTERNS, SENSITIVE_KEY_RE } from \"./patterns.js\";\n\n/** Run all redaction patterns against a string. Idempotent. */\nexport function scrubString(input: string): string {\n if (!input) return input;\n let s = input;\n for (const rule of PATTERNS) {\n if (typeof rule.replacement === \"function\") {\n s = s.replace(rule.re, rule.replacement);\n } else {\n s = s.replace(rule.re, rule.replacement);\n }\n }\n return s;\n}\n\n/**\n * Recursively redact a JSON-shaped value. Strings get pattern-scrubbed;\n * object properties whose KEY name looks sensitive (`apiKey`, `password`,\n * etc.) have their entire string value replaced — that catches arbitrary\n * secrets that don't match any provider pattern.\n *\n * Cycle-safe via a WeakSet seen.\n */\nexport function scrubAny<T>(value: T, seen: WeakSet<object> = new WeakSet()): T {\n if (value == null) return value;\n if (typeof value === \"string\") return scrubString(value) as T;\n if (typeof value !== \"object\") return value;\n if (seen.has(value as object)) return value;\n seen.add(value as object);\n\n if (Array.isArray(value)) {\n return value.map((v) => scrubAny(v, seen)) as T;\n }\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n if (SENSITIVE_KEY_RE.test(k) && typeof v === \"string\" && v.length >= 4) {\n out[k] = \"[REDACTED:by-key-name]\";\n } else {\n out[k] = scrubAny(v, seen);\n }\n }\n return out as T;\n}\n","import { nanoid } from \"nanoid\";\nimport { insertEvent, upsertSession } from \"../db.js\";\nimport type { AgentEvent } from \"@agentreel/shared-types\";\nimport type { SnapshotEvent } from \"./watcher.js\";\n\nconst IDLE_MS = 5 * 60 * 1000; // 5 minute gap = new session\nconst GLOBAL_KEY = \"__global__\";\n\ninterface OpenSession {\n id: string;\n startedAt: number;\n lastTs: number;\n cwd: string;\n}\n\nexport class CursorSessionManager {\n private open = new Map<string, OpenSession>();\n\n ingest(snapshot: SnapshotEvent): { sessionId: string; isNew: boolean } {\n const key = snapshot.workspace ?? GLOBAL_KEY;\n const ts = snapshot.timestamp;\n const cwd = snapshot.workspace ?? \"\";\n\n let session = this.open.get(key);\n let isNew = false;\n if (!session || ts - session.lastTs > IDLE_MS) {\n // Close out the previous session for this key, if any.\n if (session) this.closeSession(session, session.lastTs);\n session = {\n id: `cur_${nanoid(10)}`,\n startedAt: ts,\n lastTs: ts,\n cwd,\n };\n this.open.set(key, session);\n isNew = true;\n upsertSession({\n id: session.id,\n tool: \"cursor\",\n startedAt: ts,\n cwd,\n });\n } else {\n session.lastTs = ts;\n }\n\n const event: AgentEvent = {\n id: nanoid(),\n sessionId: session.id,\n tool: \"cursor\",\n type: \"tool_use_post\",\n ts,\n cwd,\n payload: {\n tool_name: \"Edit\",\n file_path: snapshot.filePath,\n added: snapshot.added,\n removed: snapshot.removed,\n binary: snapshot.binary,\n source: snapshot.source,\n patch: snapshot.patch,\n },\n };\n insertEvent(event);\n return { sessionId: session.id, isNew };\n }\n\n /** Stamp ended_at on every open session — call on shutdown. */\n closeAll(): void {\n const now = Date.now();\n for (const s of this.open.values()) this.closeSession(s, Math.max(s.lastTs, now));\n this.open.clear();\n }\n\n private closeSession(s: OpenSession, endedAt: number) {\n upsertSession({\n id: s.id,\n tool: \"cursor\",\n startedAt: s.startedAt,\n endedAt,\n cwd: s.cwd,\n });\n }\n}\n","import pc from \"picocolors\";\nimport { readConfig } from \"../config.js\";\nimport { postIngest } from \"../upload/client.js\";\nimport { markUploaded, takePendingBatch } from \"../upload/queue.js\";\n\nexport async function pushCommand(): Promise<void> {\n const cfg = readConfig();\n if (!cfg.apiKey) {\n console.error(pc.red(\"✗ Not linked. Run \") + pc.cyan(\"agentreel link <api-key>\"));\n process.exit(1);\n }\n\n let totalSessions = 0;\n let totalEvents = 0;\n\n while (true) {\n const batch = takePendingBatch(500);\n if (batch.events.length === 0) break;\n\n console.log(\n pc.dim(` uploading ${batch.events.length} events across ${batch.sessions.length} sessions…`),\n );\n const res = await postIngest(cfg, { sessions: batch.sessions, events: batch.events });\n markUploaded(batch.eventIds);\n totalSessions += res.sessions_written ?? 0;\n totalEvents += res.events_written ?? 0;\n }\n\n if (totalEvents === 0) {\n console.log(pc.green(\"✓\") + \" queue is empty — nothing to upload.\");\n return;\n }\n console.log(\n pc.green(\"✓\") +\n ` uploaded ${totalEvents} events · ${totalSessions} session rows touched`,\n );\n}\n","import { getDb } from \"../db.js\";\nimport type { IngestEvent, IngestSession } from \"./client.js\";\n\ninterface DBSessionRow {\n id: string;\n tool: string;\n started_at: number;\n ended_at: number | null;\n cwd: string | null;\n total_cost_cents: number | null;\n total_tokens: number | null;\n}\n\ninterface DBEventRow {\n id: string;\n session_id: string;\n tool: string;\n type: string;\n ts: number;\n cwd: string | null;\n payload: string;\n}\n\nexport interface PendingBatch {\n sessions: IngestSession[];\n events: IngestEvent[];\n eventIds: string[]; // for the post-write mark\n}\n\nexport function takePendingBatch(maxEvents = 500): PendingBatch {\n const db = getDb();\n const eventRows = db\n .prepare(\n `SELECT id, session_id, tool, type, ts, cwd, payload\n FROM events\n WHERE uploaded_at IS NULL\n ORDER BY ts ASC\n LIMIT ?`,\n )\n .all(maxEvents) as DBEventRow[];\n\n if (eventRows.length === 0) return { sessions: [], events: [], eventIds: [] };\n\n const sessionIds = [...new Set(eventRows.map((e) => e.session_id))];\n const placeholders = sessionIds.map(() => \"?\").join(\",\");\n const sessionRows = db\n .prepare(\n `SELECT id, tool, started_at, ended_at, cwd, total_cost_cents, total_tokens\n FROM sessions WHERE id IN (${placeholders})`,\n )\n .all(...sessionIds) as DBSessionRow[];\n\n const sessions: IngestSession[] = sessionRows.map((s) => ({\n id: s.id,\n tool: s.tool,\n started_at: s.started_at,\n ended_at: s.ended_at,\n cwd: s.cwd,\n total_cost_cents: s.total_cost_cents,\n total_tokens: s.total_tokens,\n }));\n\n const events: IngestEvent[] = eventRows.map((e) => ({\n id: e.id,\n session_id: e.session_id,\n ts: e.ts,\n type: e.type,\n tool: e.tool,\n cwd: e.cwd,\n payload: safeParse(e.payload),\n }));\n\n return { sessions, events, eventIds: eventRows.map((r) => r.id) };\n}\n\nexport function markUploaded(eventIds: string[]): void {\n if (eventIds.length === 0) return;\n const db = getDb();\n const now = Date.now();\n const stmt = db.prepare(`UPDATE events SET uploaded_at = ? WHERE id = ?`);\n const tx = db.transaction((ids: string[]) => {\n for (const id of ids) stmt.run(now, id);\n });\n tx(eventIds);\n}\n\nfunction safeParse(s: string): unknown {\n try {\n return JSON.parse(s);\n } catch {\n return s;\n }\n}\n","import { nanoid } from \"nanoid\";\nimport { appendFileSync } from \"node:fs\";\nimport type { AgentEvent, ClaudeCodeHookInput, EventType } from \"@agentreel/shared-types\";\nimport { insertEvent, upsertSession } from \"../db.js\";\nimport { LOG_PATH, ensureAgentreelDir } from \"../paths.js\";\nimport { scrubAny } from \"../redact/scrubber.js\";\n\nconst HOOK_EVENT_TO_TYPE: Record<string, EventType> = {\n SessionStart: \"session_start\",\n SessionEnd: \"session_end\",\n UserPromptSubmit: \"user_prompt_submit\",\n PreToolUse: \"tool_use_pre\",\n PostToolUse: \"tool_use_post\",\n Notification: \"notification\",\n Stop: \"stop\",\n SubagentStop: \"subagent_stop\",\n PreCompact: \"pre_compact\",\n};\n\nasync function readStdin(): Promise<string> {\n if (process.stdin.isTTY) return \"\";\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks).toString(\"utf8\");\n}\n\nfunction logError(err: unknown): void {\n try {\n ensureAgentreelDir();\n const line = `[${new Date().toISOString()}] hook error: ${\n err instanceof Error ? err.stack ?? err.message : String(err)\n }\\n`;\n appendFileSync(LOG_PATH, line);\n } catch {\n // swallow — never block Claude Code\n }\n}\n\nexport async function runHook(eventArg?: string): Promise<void> {\n // Hooks must never block or fail Claude Code. Wrap everything.\n try {\n const raw = await readStdin();\n if (!raw.trim()) return;\n const input = JSON.parse(raw) as ClaudeCodeHookInput;\n\n const hookEventName = input.hook_event_name ?? eventArg ?? \"Unknown\";\n const type: EventType = HOOK_EVENT_TO_TYPE[hookEventName] ?? \"unknown\";\n const ts = Date.now();\n const sessionId = input.session_id ?? \"unknown-session\";\n\n if (type === \"session_start\") {\n upsertSession({\n id: sessionId,\n tool: \"claude-code\",\n startedAt: ts,\n cwd: input.cwd,\n });\n } else if (type === \"session_end\") {\n upsertSession({\n id: sessionId,\n tool: \"claude-code\",\n startedAt: ts,\n endedAt: ts,\n cwd: input.cwd,\n });\n } else {\n // Make sure the session row exists so events have a parent.\n upsertSession({\n id: sessionId,\n tool: \"claude-code\",\n startedAt: ts,\n cwd: input.cwd,\n });\n }\n\n // Scrub the entire payload before persistence — never let raw secrets\n // touch SQLite, even briefly. scrubAny handles strings recursively and\n // also redacts values whose KEY name looks sensitive.\n const safePayload = scrubAny(input);\n const event: AgentEvent = {\n id: nanoid(),\n sessionId,\n tool: \"claude-code\",\n type,\n ts,\n cwd: input.cwd,\n payload: safePayload,\n };\n insertEvent(event);\n } catch (err) {\n logError(err);\n }\n}\n"],"mappings":"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","names":["mkdirSync","readFileSync","writeFileSync","existsSync","pc","existsSync","pc","existsSync","pc","pc","uninstallClaudeCodeHooks","resolve","pc","existsSync","basename","homedir","join","readFile","existsSync","dirname","join","existsSync","join","dirname","sep","fileURLToPath","existsSync","readFileSync","statSync","join","sep","dirname","join","existsSync","readFile","existsSync","pc","basename","pc","pc","nanoid","nanoid"]}
1
+ {"version":3,"sources":["../src/paths.ts","../src/hooks/install.ts","../src/cli.ts","../src/commands/init.ts","../src/config.ts","../src/db.ts","../src/commands/status.ts","../src/upload/client.ts","../src/upload/queue.ts","../src/upload/flush.ts","../src/commands/auth.ts","../src/commands/watch.ts","../src/cursor/paths.ts","../src/cursor/watcher.ts","../src/cursor/entries.ts","../src/cursor/diff.ts","../src/redact/ignore.ts","../src/redact/patterns.ts","../src/redact/scrubber.ts","../src/cursor/session.ts","../src/commands/push.ts","../src/commands/daemon.ts","../src/hooks/handler.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { mkdirSync } from \"node:fs\";\n\nexport const HOME = homedir();\nexport const AGENTREEL_DIR = join(HOME, \".agentreel\");\nexport const DB_PATH = join(AGENTREEL_DIR, \"sessions.db\");\nexport const CONFIG_PATH = join(AGENTREEL_DIR, \"config.json\");\nexport const QUEUE_DIR = join(AGENTREEL_DIR, \"queue\");\nexport const LOG_PATH = join(AGENTREEL_DIR, \"agent.log\");\nexport const DAEMON_PID_PATH = join(AGENTREEL_DIR, \"daemon.pid\");\nexport const DAEMON_LOG_PATH = join(AGENTREEL_DIR, \"daemon.log\");\n\nexport const CLAUDE_DIR = join(HOME, \".claude\");\nexport const CLAUDE_SETTINGS_PATH = join(CLAUDE_DIR, \"settings.json\");\n\nexport function ensureAgentreelDir(): void {\n mkdirSync(AGENTREEL_DIR, { recursive: true });\n mkdirSync(QUEUE_DIR, { recursive: true });\n}\n","import { readFileSync, writeFileSync, existsSync, copyFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { CLAUDE_DIR, CLAUDE_SETTINGS_PATH } from \"../paths.js\";\n\nconst HOOK_MARKER = \"agentreel:v1\";\n\nconst HOOK_EVENTS = [\n \"SessionStart\",\n \"SessionEnd\",\n \"UserPromptSubmit\",\n \"PreToolUse\",\n \"PostToolUse\",\n \"Notification\",\n \"Stop\",\n \"SubagentStop\",\n \"PreCompact\",\n] as const;\n\ntype HookEntry = {\n type: \"command\";\n command: string;\n timeout?: number;\n};\n\ntype HookGroup = {\n matcher?: string;\n hooks: HookEntry[];\n __agentreel?: string;\n};\n\ntype ClaudeSettings = {\n hooks?: Record<string, HookGroup[]>;\n [key: string]: unknown;\n};\n\nfunction readSettings(): ClaudeSettings {\n if (!existsSync(CLAUDE_SETTINGS_PATH)) return {};\n const raw = readFileSync(CLAUDE_SETTINGS_PATH, \"utf8\");\n if (!raw.trim()) return {};\n return JSON.parse(raw) as ClaudeSettings;\n}\n\nfunction backupSettings(): string | null {\n if (!existsSync(CLAUDE_SETTINGS_PATH)) return null;\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backup = `${CLAUDE_SETTINGS_PATH}.agentreel-backup-${stamp}`;\n copyFileSync(CLAUDE_SETTINGS_PATH, backup);\n return backup;\n}\n\nfunction buildHookEntry(event: string, hookCommandPrefix: string): HookEntry {\n // Final command shape: `<prefix> hook <Event>`. The prefix can be an\n // absolute path to dist/cli.js (global / dev installs) or an `npx -y …`\n // form (when the user ran us via npx and we don't have a stable on-disk\n // location to point Claude Code at).\n return {\n type: \"command\",\n command: `${hookCommandPrefix} hook ${event}`,\n timeout: 5,\n };\n}\n\nexport function quotePath(p: string): string {\n return /[\\s'\"$`\\\\]/.test(p) ? `\"${p.replace(/\"/g, '\\\\\"')}\"` : p;\n}\n\nexport interface InstallResult {\n backup: string | null;\n installedEvents: string[];\n hookCommandPrefix: string;\n}\n\nexport function installClaudeCodeHooks(hookCommandPrefix: string): InstallResult {\n mkdirSync(CLAUDE_DIR, { recursive: true });\n mkdirSync(dirname(CLAUDE_SETTINGS_PATH), { recursive: true });\n const backup = backupSettings();\n\n const settings = readSettings();\n settings.hooks ??= {};\n\n for (const event of HOOK_EVENTS) {\n const groups = settings.hooks[event] ?? [];\n // Drop any prior agentreel-managed group so re-running is idempotent.\n const filtered = groups.filter((g) => g.__agentreel !== HOOK_MARKER);\n filtered.push({\n matcher: \".*\",\n __agentreel: HOOK_MARKER,\n hooks: [buildHookEntry(event, hookCommandPrefix)],\n });\n settings.hooks[event] = filtered;\n }\n\n writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2) + \"\\n\", \"utf8\");\n return { backup, installedEvents: [...HOOK_EVENTS], hookCommandPrefix };\n}\n\nexport function uninstallClaudeCodeHooks(): { backup: string | null } {\n if (!existsSync(CLAUDE_SETTINGS_PATH)) return { backup: null };\n const backup = backupSettings();\n const settings = readSettings();\n if (settings.hooks) {\n for (const event of Object.keys(settings.hooks)) {\n const groups = settings.hooks[event] ?? [];\n const remaining = groups.filter((g) => g.__agentreel !== HOOK_MARKER);\n if (remaining.length === 0) delete settings.hooks[event];\n else settings.hooks[event] = remaining;\n }\n if (Object.keys(settings.hooks).length === 0) delete settings.hooks;\n }\n writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2) + \"\\n\", \"utf8\");\n return { backup };\n}\n","import { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\n\n// Replaced at build time by tsup's `define`. Fallback covers running via\n// ts-node where the literal isn't substituted.\ndeclare const __AGENTREEL_VERSION__: string;\nconst VERSION =\n typeof __AGENTREEL_VERSION__ === \"string\" ? __AGENTREEL_VERSION__ : \"dev\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { linkCommand, logoutCommand, uninstallCommand } from \"./commands/auth.js\";\nimport { watchCommand } from \"./commands/watch.js\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { daemonCommand } from \"./commands/daemon.js\";\nimport { runHook } from \"./hooks/handler.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"agentreel\")\n .description(\"AgentReel — capture Claude Code and Cursor sessions locally\")\n .version(VERSION);\n\nprogram\n .command(\"init\")\n .description(\"install Claude Code hooks and create the local SQLite buffer\")\n .action(async () => {\n await initCommand();\n });\n\nprogram\n .command(\"status\")\n .description(\"show local capture status, recent sessions, queue depth\")\n .action(async () => {\n await statusCommand();\n });\n\nprogram\n .command(\"watch\")\n .description(\"watch Cursor's local history and capture edits as events\")\n .action(async () => {\n await watchCommand();\n });\n\nprogram\n .command(\"link [api-key]\")\n .description(\"authenticate the local agent with agentreel.dev\")\n .option(\"--api <url>\", \"override the API base URL (default https://api.agentreel.dev)\")\n .action(async (apiKey: string | undefined, opts: { api?: string }) => {\n await linkCommand(apiKey, opts);\n });\n\nprogram\n .command(\"push\")\n .description(\"upload pending events to agentreel.dev\")\n .action(async () => {\n await pushCommand();\n });\n\nprogram\n .command(\"daemon\")\n .description(\"run the background uploader (30s ticks, 1MB early-flush, exponential backoff)\")\n .action(async () => {\n await daemonCommand();\n });\n\nprogram\n .command(\"logout\")\n .description(\"clear local credentials\")\n .action(async () => {\n await logoutCommand();\n });\n\nprogram\n .command(\"uninstall\")\n .description(\"remove AgentReel hooks from ~/.claude/settings.json\")\n .action(async () => {\n await uninstallCommand();\n });\n\nprogram\n .command(\"hook <event>\")\n .description(\"internal: hook handler invoked by Claude Code (reads JSON from stdin)\")\n .action(async (event: string) => {\n await runHook(event);\n });\n\nprogram.parseAsync(process.argv).catch((err) => {\n // Top-level: if we got this far on a hook invocation, something is very wrong.\n // For all other commands, print and exit non-zero.\n const cmd = process.argv[2];\n if (cmd === \"hook\") {\n process.exit(0);\n }\n console.error(err);\n process.exit(1);\n});\n","import { realpathSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { sep } from \"node:path\";\nimport pc from \"picocolors\";\nimport { installClaudeCodeHooks, quotePath } from \"../hooks/install.js\";\nimport { readConfig, writeConfig } from \"../config.js\";\nimport { getDb } from \"../db.js\";\nimport { ensureAgentreelDir } from \"../paths.js\";\n\nconst PACKAGE_NAME = \"@agentreel/agent\";\n\ninterface ResolvedBinary {\n absolutePath: string;\n isEphemeral: boolean; // true when running from an npx temp dir\n}\n\nfunction resolveAgentBinary(): ResolvedBinary {\n // tsup builds to dist/cli.js with a node shebang; npm symlinks it to\n // <prefix>/bin/agentreel. The realpath through that symlink is stable for\n // npm i -g and for in-tree development. For `npx` installs it points\n // inside ~/.npm/_npx/<hash>/ which gets cleaned eventually — we detect\n // that and fall back to an `npx -y …` hook command instead.\n const here = fileURLToPath(import.meta.url);\n const real = realpathSync(here);\n const isEphemeral = real.includes(`${sep}_npx${sep}`) || real.includes(\"/_npx/\");\n return { absolutePath: real, isEphemeral };\n}\n\nfunction chooseHookPrefix(bin: ResolvedBinary): { prefix: string; mode: \"absolute\" | \"npx\" } {\n if (process.env.AGENTREEL_HOOK_COMMAND) {\n return { prefix: process.env.AGENTREEL_HOOK_COMMAND, mode: \"absolute\" };\n }\n if (bin.isEphemeral) {\n return { prefix: `npx -y ${PACKAGE_NAME}`, mode: \"npx\" };\n }\n return { prefix: quotePath(bin.absolutePath), mode: \"absolute\" };\n}\n\nexport async function initCommand(): Promise<void> {\n ensureAgentreelDir();\n\n // Touch the DB so the file exists and migrations run.\n getDb();\n\n const cfg = readConfig();\n if (!cfg.installedAt) cfg.installedAt = Date.now();\n cfg.hooksInstalled = true;\n writeConfig(cfg);\n\n const binary = resolveAgentBinary();\n const { prefix, mode } = chooseHookPrefix(binary);\n const result = installClaudeCodeHooks(prefix);\n\n console.log(pc.bold(pc.cyan(\"\\n AgentReel \")) + pc.dim(\"Loom for AI coding sessions\\n\"));\n console.log(pc.green(\"✓\") + \" Created ~/.agentreel/sessions.db\");\n console.log(pc.green(\"✓\") + \" Wrote ~/.agentreel/config.json\");\n console.log(\n pc.green(\"✓\") +\n ` Installed ${result.installedEvents.length} Claude Code hooks → ~/.claude/settings.json`,\n );\n if (result.backup) {\n console.log(pc.dim(` (backup: ${result.backup})`));\n }\n console.log();\n console.log(pc.dim(\" Hook command: \") + pc.dim(`${prefix} hook <Event>`));\n if (mode === \"npx\") {\n console.log(\n pc.dim(\" \") +\n pc.yellow(\"•\") +\n pc.dim(\n ` Hooks resolve via npx every time Claude Code fires an event.\\n ` +\n ` For faster cold starts, run: npm i -g ${PACKAGE_NAME}`,\n ),\n );\n }\n console.log();\n console.log(pc.bold(\"Next:\") + \" open Claude Code and run a prompt.\");\n console.log(\" then \" + pc.cyan(\"agentreel status\") + \" to see captured events.\\n\");\n}\n","import { readFileSync, writeFileSync, existsSync, chmodSync } from \"node:fs\";\nimport { CONFIG_PATH, ensureAgentreelDir } from \"./paths.js\";\n\nexport interface AgentConfig {\n apiKey?: string;\n apiBaseUrl: string;\n workspaceId?: string;\n installedAt?: number;\n hooksInstalled?: boolean;\n schemaVersion: 1;\n}\n\nconst DEFAULT: AgentConfig = {\n apiBaseUrl: \"https://api.agentreel.dev\",\n schemaVersion: 1,\n};\n\nexport function readConfig(): AgentConfig {\n if (!existsSync(CONFIG_PATH)) return { ...DEFAULT };\n try {\n const raw = readFileSync(CONFIG_PATH, \"utf8\");\n return { ...DEFAULT, ...JSON.parse(raw) };\n } catch {\n return { ...DEFAULT };\n }\n}\n\nexport function writeConfig(cfg: AgentConfig): void {\n ensureAgentreelDir();\n // mode 0o600 — config.json holds the API key; only the owning user\n // should ever be able to read it. chmod after write to be sure even\n // if the file already existed with looser perms.\n writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2) + \"\\n\", {\n encoding: \"utf8\",\n mode: 0o600,\n });\n try {\n chmodSync(CONFIG_PATH, 0o600);\n } catch {\n // Non-Unix or filesystem without mode bits — best effort.\n }\n}\n","import Database from \"better-sqlite3\";\nimport type { Database as DB } from \"better-sqlite3\";\nimport { DB_PATH, ensureAgentreelDir } from \"./paths.js\";\nimport type { AgentEvent, Session, Tool } from \"@agentreel/shared-types\";\n\nlet _db: DB | null = null;\n\nexport function getDb(): DB {\n if (_db) return _db;\n ensureAgentreelDir();\n const db = new Database(DB_PATH);\n db.pragma(\"journal_mode = WAL\");\n db.pragma(\"synchronous = NORMAL\");\n migrate(db);\n _db = db;\n return db;\n}\n\nfunction migrate(db: DB): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n tool TEXT NOT NULL,\n started_at INTEGER NOT NULL,\n ended_at INTEGER,\n cwd TEXT,\n total_cost_cents INTEGER,\n total_tokens INTEGER\n );\n CREATE TABLE IF NOT EXISTS events (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n tool TEXT NOT NULL,\n type TEXT NOT NULL,\n ts INTEGER NOT NULL,\n cwd TEXT,\n payload TEXT NOT NULL,\n uploaded_at INTEGER\n );\n CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id, ts);\n CREATE INDEX IF NOT EXISTS idx_events_pending ON events(uploaded_at) WHERE uploaded_at IS NULL;\n CREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT\n );\n `);\n}\n\nexport function getMeta(key: string): string | null {\n const db = getDb();\n const row = db.prepare(`SELECT value FROM meta WHERE key = ?`).get(key) as\n | { value: string }\n | undefined;\n return row?.value ?? null;\n}\n\nexport function setMeta(key: string, value: string | null): void {\n const db = getDb();\n if (value == null) {\n db.prepare(`DELETE FROM meta WHERE key = ?`).run(key);\n return;\n }\n db.prepare(\n `INSERT INTO meta (key, value) VALUES (?, ?)\n ON CONFLICT(key) DO UPDATE SET value = excluded.value`,\n ).run(key, value);\n}\n\nexport function pendingByteSize(): number {\n const db = getDb();\n const row = db\n .prepare(\n `SELECT COALESCE(SUM(LENGTH(payload)), 0) AS bytes\n FROM events WHERE uploaded_at IS NULL`,\n )\n .get() as { bytes: number };\n return row.bytes;\n}\n\nexport function upsertSession(s: Session): void {\n const db = getDb();\n db.prepare(\n `INSERT INTO sessions (id, tool, started_at, ended_at, cwd, total_cost_cents, total_tokens)\n VALUES (@id, @tool, @startedAt, @endedAt, @cwd, @totalCostCents, @totalTokens)\n ON CONFLICT(id) DO UPDATE SET\n ended_at = COALESCE(excluded.ended_at, sessions.ended_at),\n cwd = COALESCE(excluded.cwd, sessions.cwd),\n total_cost_cents = COALESCE(excluded.total_cost_cents, sessions.total_cost_cents),\n total_tokens = COALESCE(excluded.total_tokens, sessions.total_tokens)`,\n ).run({\n id: s.id,\n tool: s.tool,\n startedAt: s.startedAt,\n endedAt: s.endedAt ?? null,\n cwd: s.cwd ?? null,\n totalCostCents: s.totalCostCents ?? null,\n totalTokens: s.totalTokens ?? null,\n });\n}\n\nexport function insertEvent(e: AgentEvent): void {\n const db = getDb();\n db.prepare(\n `INSERT OR IGNORE INTO events (id, session_id, tool, type, ts, cwd, payload)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n ).run(e.id, e.sessionId, e.tool, e.type, e.ts, e.cwd ?? null, JSON.stringify(e.payload ?? {}));\n}\n\nexport function countEvents(): { total: number; pending: number } {\n const db = getDb();\n const total = db.prepare(`SELECT COUNT(*) AS c FROM events`).get() as { c: number };\n const pending = db\n .prepare(`SELECT COUNT(*) AS c FROM events WHERE uploaded_at IS NULL`)\n .get() as { c: number };\n return { total: total.c, pending: pending.c };\n}\n\nexport function listRecentSessions(limit = 10): Array<{\n id: string;\n tool: Tool;\n started_at: number;\n ended_at: number | null;\n cwd: string | null;\n}> {\n const db = getDb();\n return db\n .prepare(\n `SELECT id, tool, started_at, ended_at, cwd\n FROM sessions ORDER BY started_at DESC LIMIT ?`,\n )\n .all(limit) as Array<{\n id: string;\n tool: Tool;\n started_at: number;\n ended_at: number | null;\n cwd: string | null;\n }>;\n}\n","import pc from \"picocolors\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport {\n CLAUDE_SETTINGS_PATH,\n DB_PATH,\n CONFIG_PATH,\n DAEMON_PID_PATH,\n} from \"../paths.js\";\nimport { countEvents, getMeta, listRecentSessions, pendingByteSize } from \"../db.js\";\nimport { readConfig } from \"../config.js\";\nimport {\n META_LAST_ERROR_AT,\n META_LAST_ERROR_MSG,\n META_LAST_SYNC_AT,\n META_NEXT_ATTEMPT_AT,\n consecutiveFailures,\n} from \"../upload/flush.js\";\n\nfunction fmtDuration(ms: number): string {\n const s = Math.round(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n if (m < 60) return `${m}m ${s % 60}s`;\n const h = Math.floor(m / 60);\n return `${h}h ${m % 60}m`;\n}\n\nfunction fmtAgo(ms: number): string {\n return fmtDuration(Date.now() - ms) + \" ago\";\n}\n\nfunction fmtBytes(n: number): string {\n if (n < 1024) return `${n} B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;\n return `${(n / 1024 / 1024).toFixed(2)} MB`;\n}\n\nfunction daemonStatus(): { running: boolean; pid: number | null } {\n if (!existsSync(DAEMON_PID_PATH)) return { running: false, pid: null };\n try {\n const pid = Number(readFileSync(DAEMON_PID_PATH, \"utf8\").trim());\n if (!Number.isFinite(pid) || pid <= 0) return { running: false, pid: null };\n process.kill(pid, 0);\n return { running: true, pid };\n } catch {\n return { running: false, pid: null };\n }\n}\n\nexport async function statusCommand(): Promise<void> {\n const cfg = readConfig();\n console.log(pc.bold(pc.cyan(\"AgentReel status\\n\")));\n console.log(\" config: \" + (existsSync(CONFIG_PATH) ? CONFIG_PATH : pc.red(\"missing\")));\n console.log(\" database: \" + (existsSync(DB_PATH) ? DB_PATH : pc.red(\"not initialized\")));\n console.log(\n \" claude hooks: \" +\n (existsSync(CLAUDE_SETTINGS_PATH) ? CLAUDE_SETTINGS_PATH : pc.red(\"not installed\")),\n );\n console.log(\" api base: \" + cfg.apiBaseUrl);\n console.log(\" authenticated: \" + (cfg.apiKey ? pc.green(\"yes\") : pc.yellow(\"no\")));\n\n const d = daemonStatus();\n console.log(\n \" daemon: \" +\n (d.running ? pc.green(`running (pid ${d.pid})`) : pc.dim(\"stopped\")),\n );\n console.log();\n\n if (!existsSync(DB_PATH)) {\n console.log(pc.yellow(\"Run `agentreel init` to install hooks.\"));\n return;\n }\n\n const { total, pending } = countEvents();\n const pendingBytes = pendingByteSize();\n console.log(` events captured: ${total}`);\n console.log(` pending upload: ${pending}` + (pending > 0 ? pc.dim(` (${fmtBytes(pendingBytes)})`) : \"\"));\n\n const lastSync = numMeta(META_LAST_SYNC_AT);\n const lastErr = numMeta(META_LAST_ERROR_AT);\n const lastErrMsg = getMeta(META_LAST_ERROR_MSG);\n const fails = consecutiveFailures();\n const nextAt = numMeta(META_NEXT_ATTEMPT_AT);\n\n console.log(\n ` last sync: ` +\n (lastSync ? pc.green(fmtAgo(lastSync)) : pc.dim(\"never\")),\n );\n if (lastErr) {\n console.log(\n ` last error: ` +\n pc.red(fmtAgo(lastErr)) +\n pc.dim(` · ${fails} consecutive`),\n );\n if (lastErrMsg) console.log(pc.dim(` ${lastErrMsg}`));\n if (nextAt && nextAt > Date.now()) {\n console.log(\n pc.dim(` next attempt in ${fmtDuration(nextAt - Date.now())}`),\n );\n }\n }\n console.log();\n\n const sessions = listRecentSessions(5);\n if (sessions.length === 0) {\n console.log(pc.dim(\" no sessions yet — start a Claude Code session to capture one.\"));\n return;\n }\n console.log(pc.bold(\"recent sessions:\"));\n for (const s of sessions) {\n const dur = s.ended_at ? fmtDuration(s.ended_at - s.started_at) : pc.dim(\"active\");\n const ts = new Date(s.started_at).toLocaleString();\n console.log(` ${pc.dim(s.id.slice(0, 8))} ${s.tool.padEnd(11)} ${ts} ${dur}`);\n }\n}\n\nfunction numMeta(key: string): number | null {\n const v = getMeta(key);\n if (!v) return null;\n const n = Number(v);\n return Number.isFinite(n) && n > 0 ? n : null;\n}\n","import type { AgentConfig } from \"../config.js\";\n\nexport interface IngestSession {\n id: string;\n tool: string;\n started_at: number;\n ended_at?: number | null;\n cwd?: string | null;\n total_cost_cents?: number | null;\n total_tokens?: number | null;\n}\n\nexport interface IngestEvent {\n id: string;\n session_id: string;\n ts: number;\n type: string;\n tool: string;\n cwd?: string | null;\n payload: unknown;\n}\n\nexport interface IngestResponse {\n ok: boolean;\n sessions_written?: number;\n events_written?: number;\n error?: string;\n reason?: string;\n}\n\nexport class IngestError extends Error {\n constructor(\n message: string,\n readonly status: number,\n readonly body: IngestResponse | null,\n ) {\n super(message);\n this.name = \"IngestError\";\n }\n /** 4xx (except 408/429) — payload-shaped problem the agent can't fix by retrying. */\n get isPermanent(): boolean {\n return this.status >= 400 && this.status < 500 && this.status !== 408 && this.status !== 429;\n }\n}\n\nexport async function postIngest(\n cfg: AgentConfig,\n body: { sessions?: IngestSession[]; events?: IngestEvent[] },\n): Promise<IngestResponse> {\n if (!cfg.apiKey) {\n throw new Error(\"Not linked. Run: agentreel link <key>\");\n }\n const url = `${cfg.apiBaseUrl.replace(/\\/$/, \"\")}/api/v1/sessions/ingest`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${cfg.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n let data: IngestResponse | null = null;\n try {\n data = (await res.json()) as IngestResponse;\n } catch {\n // Non-JSON body — treat as transient if 5xx, permanent otherwise.\n }\n if (!res.ok || !data?.ok) {\n const reason = data?.reason ? ` (${data.reason})` : \"\";\n const tag = data?.error ?? `http-${res.status}`;\n throw new IngestError(`${tag}${reason}`, res.status, data);\n }\n return data;\n}\n","import { getDb } from \"../db.js\";\nimport type { IngestEvent, IngestSession } from \"./client.js\";\n\ninterface DBSessionRow {\n id: string;\n tool: string;\n started_at: number;\n ended_at: number | null;\n cwd: string | null;\n total_cost_cents: number | null;\n total_tokens: number | null;\n}\n\ninterface DBEventRow {\n id: string;\n session_id: string;\n tool: string;\n type: string;\n ts: number;\n cwd: string | null;\n payload: string;\n}\n\nexport interface PendingBatch {\n sessions: IngestSession[];\n events: IngestEvent[];\n eventIds: string[]; // for the post-write mark\n}\n\n// Cloud cap is 5 MB; we stay well under that to leave room for JSON framing\n// + auth headers and to keep individual round-trips snappy.\nexport const MAX_BATCH_BYTES = 1024 * 1024; // 1 MB\nexport const MAX_BATCH_EVENTS = 500;\n\nexport function takePendingBatch(\n maxEvents = MAX_BATCH_EVENTS,\n maxBytes = MAX_BATCH_BYTES,\n): PendingBatch {\n const db = getDb();\n const candidateRows = db\n .prepare(\n `SELECT id, session_id, tool, type, ts, cwd, payload\n FROM events\n WHERE uploaded_at IS NULL\n ORDER BY ts ASC\n LIMIT ?`,\n )\n .all(maxEvents) as DBEventRow[];\n\n if (candidateRows.length === 0) return { sessions: [], events: [], eventIds: [] };\n\n // Trim to byte budget. Always include at least one event so a single\n // oversized payload doesn't get stuck in the queue forever — the server\n // can reject it and we'll mark it uploaded to skip past.\n const eventRows: DBEventRow[] = [];\n let bytes = 0;\n for (const row of candidateRows) {\n const rowBytes = Buffer.byteLength(row.payload, \"utf8\");\n if (eventRows.length > 0 && bytes + rowBytes > maxBytes) break;\n eventRows.push(row);\n bytes += rowBytes;\n }\n\n const sessionIds = [...new Set(eventRows.map((e) => e.session_id))];\n const placeholders = sessionIds.map(() => \"?\").join(\",\");\n const sessionRows = db\n .prepare(\n `SELECT id, tool, started_at, ended_at, cwd, total_cost_cents, total_tokens\n FROM sessions WHERE id IN (${placeholders})`,\n )\n .all(...sessionIds) as DBSessionRow[];\n\n const sessions: IngestSession[] = sessionRows.map((s) => ({\n id: s.id,\n tool: s.tool,\n started_at: s.started_at,\n ended_at: s.ended_at,\n cwd: s.cwd,\n total_cost_cents: s.total_cost_cents,\n total_tokens: s.total_tokens,\n }));\n\n const events: IngestEvent[] = eventRows.map((e) => ({\n id: e.id,\n session_id: e.session_id,\n ts: e.ts,\n type: e.type,\n tool: e.tool,\n cwd: e.cwd,\n payload: safeParse(e.payload),\n }));\n\n return { sessions, events, eventIds: eventRows.map((r) => r.id) };\n}\n\nexport function markUploaded(eventIds: string[]): void {\n if (eventIds.length === 0) return;\n const db = getDb();\n const now = Date.now();\n const stmt = db.prepare(`UPDATE events SET uploaded_at = ? WHERE id = ?`);\n const tx = db.transaction((ids: string[]) => {\n for (const id of ids) stmt.run(now, id);\n });\n tx(eventIds);\n}\n\nfunction safeParse(s: string): unknown {\n try {\n return JSON.parse(s);\n } catch {\n return s;\n }\n}\n","import { getMeta, setMeta } from \"../db.js\";\nimport { readConfig } from \"../config.js\";\nimport { IngestError, postIngest } from \"./client.js\";\nimport { MAX_BATCH_BYTES, MAX_BATCH_EVENTS, markUploaded, takePendingBatch } from \"./queue.js\";\n\nexport const META_LAST_SYNC_AT = \"last_sync_at\";\nexport const META_LAST_ERROR_AT = \"last_error_at\";\nexport const META_LAST_ERROR_MSG = \"last_error_msg\";\nexport const META_CONSECUTIVE_FAILS = \"consecutive_failures\";\nexport const META_NEXT_ATTEMPT_AT = \"next_attempt_at\";\n\nconst BACKOFF_BASE_MS = 1_000;\nconst BACKOFF_MAX_MS = 5 * 60_000;\n\nexport interface FlushResult {\n uploadedEvents: number;\n uploadedSessions: number;\n /** True if there are still events pending after this flush (hit batch cap, more to do). */\n moreAvailable: boolean;\n}\n\n/** Compute the wall-clock time the next attempt is allowed to start. */\nexport function nextAttemptAt(): number {\n const raw = getMeta(META_NEXT_ATTEMPT_AT);\n return raw ? Number(raw) : 0;\n}\n\nexport function consecutiveFailures(): number {\n const raw = getMeta(META_CONSECUTIVE_FAILS);\n return raw ? Number(raw) : 0;\n}\n\nfunction recordSuccess() {\n const now = Date.now();\n setMeta(META_LAST_SYNC_AT, String(now));\n setMeta(META_LAST_ERROR_AT, null);\n setMeta(META_LAST_ERROR_MSG, null);\n setMeta(META_CONSECUTIVE_FAILS, \"0\");\n setMeta(META_NEXT_ATTEMPT_AT, \"0\");\n}\n\nfunction recordFailure(err: Error, permanent: boolean) {\n const now = Date.now();\n const fails = consecutiveFailures() + 1;\n setMeta(META_LAST_ERROR_AT, String(now));\n setMeta(META_LAST_ERROR_MSG, truncate(err.message, 500));\n setMeta(META_CONSECUTIVE_FAILS, String(fails));\n if (permanent) {\n // Permanent errors (4xx like bad-prefix, plan-limit) — back off the\n // longest window so we don't hammer the server, but don't give up\n // forever; user may upgrade plan or re-link, and we want the next\n // tick to recover within minutes.\n setMeta(META_NEXT_ATTEMPT_AT, String(now + BACKOFF_MAX_MS));\n return;\n }\n // Exponential: 1s, 2s, 4s, ..., capped at 5min, with 0–25% jitter.\n const exp = Math.min(BACKOFF_MAX_MS, BACKOFF_BASE_MS * 2 ** (fails - 1));\n const jitter = exp * 0.25 * Math.random();\n setMeta(META_NEXT_ATTEMPT_AT, String(now + exp + jitter));\n}\n\nfunction truncate(s: string, n: number) {\n return s.length > n ? s.slice(0, n - 1) + \"…\" : s;\n}\n\n/**\n * Flush a single batch. Returns moreAvailable=true if the queue still has\n * events the daemon should pick up on the next tick (we hit the byte/event\n * cap mid-flush). Throws on failure — caller decides whether to swallow.\n */\nexport async function flushOnce(): Promise<FlushResult> {\n const cfg = readConfig();\n if (!cfg.apiKey) {\n throw new Error(\"Not linked. Run: agentreel link <api-key>\");\n }\n const batch = takePendingBatch(MAX_BATCH_EVENTS, MAX_BATCH_BYTES);\n if (batch.events.length === 0) {\n // Nothing to do isn't an error — but don't stamp last_sync, since\n // we didn't actually round-trip anything.\n return { uploadedEvents: 0, uploadedSessions: 0, moreAvailable: false };\n }\n try {\n const res = await postIngest(cfg, { sessions: batch.sessions, events: batch.events });\n markUploaded(batch.eventIds);\n recordSuccess();\n return {\n uploadedEvents: res.events_written ?? 0,\n uploadedSessions: res.sessions_written ?? 0,\n moreAvailable: batch.events.length === MAX_BATCH_EVENTS,\n };\n } catch (err) {\n const permanent = err instanceof IngestError && err.isPermanent;\n recordFailure(err as Error, permanent);\n throw err;\n }\n}\n","import pc from \"picocolors\";\nimport { readConfig, writeConfig } from \"../config.js\";\nimport { postIngest } from \"../upload/client.js\";\n\nexport async function logoutCommand(): Promise<void> {\n const cfg = readConfig();\n cfg.apiKey = undefined;\n cfg.workspaceId = undefined;\n writeConfig(cfg);\n console.log(pc.green(\"✓\") + \" Cleared local credentials.\");\n}\n\ninterface LinkOpts {\n api?: string;\n}\n\nexport async function linkCommand(rawKey: string | undefined, opts: LinkOpts): Promise<void> {\n const key = (rawKey ?? (await promptHidden(\"Paste your AgentReel API key: \"))).trim();\n if (!key) {\n console.error(pc.red(\"✗ No key provided.\"));\n process.exit(1);\n }\n if (!key.startsWith(\"ar_live_\")) {\n console.error(pc.red(\"✗ Keys start with `ar_live_`. Did you paste the right value?\"));\n process.exit(1);\n }\n\n const cfg = readConfig();\n cfg.apiKey = key;\n if (opts.api) cfg.apiBaseUrl = opts.api;\n\n console.log(pc.dim(` validating against ${cfg.apiBaseUrl}…`));\n try {\n await postIngest(cfg, {});\n } catch (err) {\n console.error(pc.red(\"✗ Validation failed: \") + (err as Error).message);\n process.exit(1);\n }\n writeConfig(cfg);\n console.log(pc.green(\"✓\") + \" Linked.\");\n console.log(pc.dim(\" api: \") + cfg.apiBaseUrl);\n console.log(pc.dim(\" key: \") + key.slice(0, 12) + \"…\");\n}\n\nexport async function uninstallCommand(): Promise<void> {\n const { uninstallClaudeCodeHooks } = await import(\"../hooks/install.js\");\n const { backup } = uninstallClaudeCodeHooks();\n console.log(pc.green(\"✓\") + \" Removed AgentReel hooks from ~/.claude/settings.json\");\n if (backup) console.log(pc.dim(` (backup: ${backup})`));\n}\n\nconst CTRL_C = 0x03;\nconst BACKSPACE = 0x7f;\nconst BACKSPACE_ALT = 0x08;\nconst NEWLINE = 0x0a;\nconst CARRIAGE = 0x0d;\n\nfunction promptHidden(prompt: string): Promise<string> {\n return new Promise((resolve) => {\n process.stdout.write(prompt);\n let buf = \"\";\n const stdin = process.stdin;\n stdin.setRawMode?.(true);\n stdin.resume();\n stdin.setEncoding(\"utf8\");\n const onData = (chunk: string) => {\n for (const ch of chunk) {\n const code = ch.charCodeAt(0);\n if (code === NEWLINE || code === CARRIAGE) {\n stdin.setRawMode?.(false);\n stdin.pause();\n stdin.removeListener(\"data\", onData);\n process.stdout.write(\"\\n\");\n return resolve(buf);\n }\n if (code === CTRL_C) {\n stdin.setRawMode?.(false);\n process.exit(130);\n }\n if (code === BACKSPACE || code === BACKSPACE_ALT) {\n buf = buf.slice(0, -1);\n } else {\n buf += ch;\n }\n }\n };\n stdin.on(\"data\", onData);\n });\n}\n","import pc from \"picocolors\";\nimport { existsSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { cursorHistoryDir } from \"../cursor/paths.js\";\nimport { startCursorWatcher } from \"../cursor/watcher.js\";\nimport { CursorSessionManager } from \"../cursor/session.js\";\nimport { ensureAgentreelDir } from \"../paths.js\";\nimport { getDb } from \"../db.js\";\n\nexport async function watchCommand(): Promise<void> {\n ensureAgentreelDir();\n // Touch DB so the schema exists.\n getDb();\n\n const dir = cursorHistoryDir();\n if (!existsSync(dir)) {\n console.error(pc.red(\"✗ Cursor history directory not found:\"));\n console.error(\" \" + dir);\n console.error();\n console.error(pc.dim(\"Open Cursor at least once, edit a file, then re-run.\"));\n console.error(pc.dim(\"(Or set AGENTREEL_CURSOR_HISTORY_DIR to a custom path.)\"));\n process.exit(1);\n }\n\n console.log(pc.bold(pc.cyan(\"AgentReel · Cursor watcher\\n\")));\n console.log(pc.dim(\" watching \") + dir);\n console.log(pc.dim(\" press Ctrl+C to stop\\n\"));\n\n const sessions = new CursorSessionManager();\n\n const watcher = startCursorWatcher(dir, async (snap) => {\n const { sessionId, isNew } = sessions.ingest(snap);\n const ts = new Date(snap.timestamp).toLocaleTimeString();\n const ws = snap.workspace ? basename(snap.workspace) : pc.dim(\"no-workspace\");\n const file = snap.filePath.split(\"/\").slice(-2).join(\"/\");\n const sourceLabel =\n snap.source === \"cursor-ai\"\n ? pc.magenta(\"ai\")\n : snap.source === \"cursor-manual\"\n ? pc.cyan(\"man\")\n : pc.dim(\"?\");\n const stats = snap.binary\n ? pc.dim(\"binary\")\n : `${pc.green(\"+\" + snap.added)} ${pc.red(\"-\" + snap.removed)}`;\n if (isNew) {\n console.log(\n `${pc.dim(ts)} ${pc.yellow(\"session\")} ${pc.dim(sessionId)} ${ws}`,\n );\n }\n console.log(`${pc.dim(ts)} edit ${sourceLabel} ${file.padEnd(36)} ${stats}`);\n });\n\n // Graceful shutdown — flush sessions, close watcher.\n const shutdown = async () => {\n console.log(pc.dim(\"\\n closing sessions…\"));\n sessions.closeAll();\n await watcher.close();\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n","import { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\n\n// Cursor is a VS Code fork; its history layout matches Code's:\n// <userData>/User/History/<hashId>/{entries.json, <id>.<ext>, ...}\nexport function defaultCursorHistoryDir(): string {\n const home = homedir();\n switch (platform()) {\n case \"darwin\":\n return join(home, \"Library\", \"Application Support\", \"Cursor\", \"User\", \"History\");\n case \"win32\": {\n const appData = process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\");\n return join(appData, \"Cursor\", \"User\", \"History\");\n }\n default:\n return join(home, \".config\", \"Cursor\", \"User\", \"History\");\n }\n}\n\nexport function cursorHistoryDir(): string {\n return process.env.AGENTREEL_CURSOR_HISTORY_DIR ?? defaultCursorHistoryDir();\n}\n","import chokidar, { type FSWatcher } from \"chokidar\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { basename, dirname, join } from \"node:path\";\nimport {\n findWorkspaceRoot,\n isProbablyBinary,\n readEntries,\n resourceToPath,\n type HistoryEntry,\n} from \"./entries.js\";\nimport { computeDiff } from \"./diff.js\";\nimport { isIgnored } from \"../redact/ignore.js\";\nimport { scrubString } from \"../redact/scrubber.js\";\n\nexport interface SnapshotEvent {\n filePath: string;\n workspace: string | null;\n source: \"cursor-ai\" | \"cursor-manual\" | \"unknown\";\n timestamp: number;\n patch: string;\n added: number;\n removed: number;\n binary: boolean;\n}\n\nexport type SnapshotHandler = (e: SnapshotEvent) => void | Promise<void>;\n\nexport interface WatcherOptions {\n /** Discard the very first entry for any tracked file (Cursor's \"first observation\" snapshot). Default true. */\n skipFirstObservation?: boolean;\n}\n\nexport function startCursorWatcher(\n historyDir: string,\n onSnapshot: SnapshotHandler,\n opts: WatcherOptions = {},\n): FSWatcher {\n const skipFirst = opts.skipFirstObservation ?? true;\n\n const watcher = chokidar.watch(historyDir, {\n ignoreInitial: true,\n depth: 2,\n persistent: true,\n awaitWriteFinish: { stabilityThreshold: 120, pollInterval: 40 },\n });\n\n watcher.on(\"add\", async (path) => {\n try {\n const folder = dirname(path);\n const file = basename(path);\n // The folder name is the resource hash; siblings include entries.json\n // and one snapshot file per saved version.\n if (file === \"entries.json\") return;\n // Wait briefly for entries.json to mention this file — Cursor writes\n // the snapshot before updating the manifest in some versions.\n const ctx = await waitForEntry(folder, file, 2_000);\n if (!ctx) return;\n await processSnapshot(folder, ctx, skipFirst, onSnapshot);\n } catch (err) {\n // The watcher must never crash the host process.\n // eslint-disable-next-line no-console\n console.error(\"[agentreel] cursor watcher: error processing\", path, err);\n }\n });\n\n return watcher;\n}\n\ninterface SnapshotContext {\n resource: string;\n current: HistoryEntry;\n previous: HistoryEntry | null;\n currentIdx: number;\n}\n\nasync function waitForEntry(\n folder: string,\n fileId: string,\n totalMs: number,\n): Promise<SnapshotContext | null> {\n const start = Date.now();\n while (Date.now() - start < totalMs) {\n const file = await readEntries(folder);\n if (file) {\n const idx = file.entries.findIndex((e) => e.id === fileId);\n if (idx >= 0) {\n const current = file.entries[idx];\n if (!current) return null;\n const previous = idx > 0 ? (file.entries[idx - 1] ?? null) : null;\n return { resource: file.resource, current, previous, currentIdx: idx };\n }\n }\n await sleep(150);\n }\n return null;\n}\n\nasync function processSnapshot(\n folder: string,\n ctx: SnapshotContext,\n skipFirst: boolean,\n onSnapshot: SnapshotHandler,\n): Promise<void> {\n const filePath = resourceToPath(ctx.resource);\n if (!filePath) return;\n\n // Skip files inside common dependency / build dirs — high noise, low value.\n if (NOISY_PATH.test(filePath)) return;\n\n // Honor per-workspace .agentreelignore (gitignore syntax).\n const workspace = findWorkspaceRoot(filePath);\n if (isIgnored(workspace, filePath)) return;\n\n if (!ctx.previous) {\n // First time Cursor has seen this file — no diff to compute.\n if (skipFirst) return;\n }\n\n const newSnapshot = join(folder, ctx.current.id);\n let before = \"\";\n let after = \"\";\n if (ctx.previous) {\n const prevPath = join(folder, ctx.previous.id);\n if (existsSync(prevPath)) {\n before = await safeRead(prevPath);\n }\n }\n if (existsSync(newSnapshot)) {\n after = await safeRead(newSnapshot);\n }\n\n // No real change — chokidar can fire spurious \"add\" events on some\n // filesystems. Skip silently.\n if (before === after) return;\n\n const binary = isProbablyBinary(filePath);\n let patch = \"\";\n let added = 0;\n let removed = 0;\n if (!binary) {\n // Scrub BEFORE diffing so secrets never enter the patch text. We diff\n // the redacted versions instead — the dashboard will still show the\n // shape of the change, just with [REDACTED:*] in place of values.\n const beforeSafe = scrubString(before);\n const afterSafe = scrubString(after);\n const result = computeDiff(beforeSafe, afterSafe);\n patch = result.patch;\n added = result.added;\n removed = result.removed;\n }\n\n const source = classifySource(ctx.current.source);\n onSnapshot({\n filePath,\n workspace,\n source,\n timestamp: ctx.current.timestamp ?? Date.now(),\n patch,\n added,\n removed,\n binary,\n });\n}\n\nfunction classifySource(raw?: string): SnapshotEvent[\"source\"] {\n if (!raw) return \"cursor-manual\";\n const s = raw.toLowerCase();\n if (s.includes(\"composer\") || s.includes(\"ai\") || s.includes(\"chat\")) return \"cursor-ai\";\n return \"cursor-manual\";\n}\n\nasync function safeRead(path: string): Promise<string> {\n try {\n return await readFile(path, \"utf8\");\n } catch {\n return \"\";\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nconst NOISY_PATH =\n /[\\\\/](node_modules|\\.next|\\.turbo|dist|build|\\.git|coverage|\\.cache|\\.venv|venv|target|out)[\\\\/]/;\n","import { readFile } from \"node:fs/promises\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { join, dirname, resolve, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport interface HistoryEntry {\n id: string; // filename of the snapshot, e.g. \"rEMc.ts\"\n timestamp: number; // ms epoch\n source?: string; // e.g. \"Cursor.Composer\" for AI edits, \"\" for manual\n}\n\nexport interface HistoryFile {\n version: number;\n resource: string; // file:///...\n entries: HistoryEntry[];\n}\n\nexport async function readEntries(historyFolder: string): Promise<HistoryFile | null> {\n const path = join(historyFolder, \"entries.json\");\n if (!existsSync(path)) return null;\n try {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as HistoryFile;\n } catch {\n return null;\n }\n}\n\nexport function resourceToPath(resource: string): string | null {\n if (!resource.startsWith(\"file://\")) return null;\n try {\n return fileURLToPath(resource);\n } catch {\n return null;\n }\n}\n\n// Walk up from `path` looking for a directory that contains `.git`. That\n// directory is the workspace root for the purposes of grouping events.\nexport function findWorkspaceRoot(path: string): string | null {\n let dir = dirname(resolve(path));\n while (dir && dir !== sep) {\n const git = join(dir, \".git\");\n try {\n if (existsSync(git)) return dir;\n } catch {\n // ignore\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nexport function isProbablyBinary(path: string): boolean {\n try {\n const s = statSync(path);\n if (s.size > 1024 * 1024) return true; // >1MB — skip diff\n } catch {\n return false;\n }\n // Lightweight extension allowlist for v1. Anything else, treat as binary\n // and skip diffing (we still record the event, just without a patch).\n const text =\n /\\.(ts|tsx|js|jsx|mjs|cjs|json|jsonc|md|mdx|css|scss|html|xml|yaml|yml|toml|sh|bash|zsh|fish|py|rb|go|rs|java|kt|swift|c|cc|cpp|h|hpp|cs|php|sql|prisma|graphql|gql|env|gitignore|dockerfile|tf|hcl|lua|vue|svelte|astro|txt|csv|tsv|log|conf|ini)$/i;\n return !text.test(path);\n}\n","import { diffLines } from \"diff\";\nimport DiffMatchPatch from \"diff-match-patch\";\n\nexport interface DiffResult {\n /** diff-match-patch wire format — compact, reversible. Stored on the event. */\n patch: string;\n /** Line-level adds/removes — surfaced in the dashboard list. */\n added: number;\n removed: number;\n}\n\nconst dmp = new DiffMatchPatch.diff_match_patch();\n// Allow expensive cleanup on small diffs; cap at 1MB to avoid pathological inputs.\ndmp.Diff_Timeout = 1.0;\n\nexport function computeDiff(before: string, after: string): DiffResult {\n // Patch — compact DMP format, much smaller than unified diff for small edits.\n const patches = dmp.patch_make(before, after);\n const patch = dmp.patch_toText(patches);\n\n // Line-level stats (cheap; users see these in the timeline list).\n let added = 0;\n let removed = 0;\n for (const part of diffLines(before, after)) {\n const n =\n part.count ??\n (part.value.match(/\\n/g)?.length ?? (part.value.length > 0 ? 1 : 0));\n if (part.added) added += n;\n else if (part.removed) removed += n;\n }\n\n return { patch, added, removed };\n}\n","import { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\nimport ignore, { type Ignore } from \"ignore\";\n\nconst FILENAME = \".agentreelignore\";\nconst TTL_MS = 5_000; // re-read the file at most every 5 seconds per workspace\n\ninterface CacheEntry {\n matcher: Ignore | null;\n loadedAt: number;\n fileMtime: number;\n}\n\nconst cache = new Map<string, CacheEntry>();\n\nfunction loadFor(workspace: string): Ignore | null {\n const path = join(workspace, FILENAME);\n if (!existsSync(path)) return null;\n try {\n const raw = readFileSync(path, \"utf8\");\n return ignore({ allowRelativePaths: true }).add(raw);\n } catch {\n return null;\n }\n}\n\nfunction getCached(workspace: string): Ignore | null {\n const entry = cache.get(workspace);\n const path = join(workspace, FILENAME);\n let mtime = 0;\n try {\n mtime = existsSync(path) ? statSync(path).mtimeMs : 0;\n } catch {\n mtime = 0;\n }\n const now = Date.now();\n if (entry && now - entry.loadedAt < TTL_MS && entry.fileMtime === mtime) {\n return entry.matcher;\n }\n const matcher = loadFor(workspace);\n cache.set(workspace, { matcher, loadedAt: now, fileMtime: mtime });\n return matcher;\n}\n\n/**\n * Returns true if `filePath` should be skipped because it matches a rule\n * in the workspace's `.agentreelignore`. Returns false when the workspace\n * has no ignore file or the path doesn't match.\n */\nexport function isIgnored(workspace: string | null | undefined, filePath: string): boolean {\n if (!workspace) return false;\n const matcher = getCached(workspace);\n if (!matcher) return false;\n const rel = relative(workspace, filePath);\n // ignore can't reason about paths that escape the workspace.\n if (!rel || rel.startsWith(\"..\") || rel.startsWith(sep)) return false;\n // gitignore syntax expects forward slashes.\n return matcher.ignores(rel.split(sep).join(\"/\"));\n}\n","// Order matters. Multiline / specific patterns first, generic last.\n\nexport interface RedactionRule {\n name: string;\n re: RegExp;\n replacement: string | ((match: string) => string);\n}\n\nexport const PATTERNS: RedactionRule[] = [\n // PEM-encoded private keys (multiline, must run early)\n {\n name: \"pem-private-key\",\n re: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\\s\\S]+?-----END [A-Z0-9 ]*PRIVATE KEY-----/g,\n replacement: \"[REDACTED:private-key]\",\n },\n\n // JWT (header.payload.signature) — eyJ-prefixed base64url segments\n {\n name: \"jwt\",\n re: /\\beyJ[A-Za-z0-9_-]{8,}\\.eyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\b/g,\n replacement: \"[REDACTED:jwt]\",\n },\n\n // GitHub fine-grained PATs (84 chars after prefix is the official format)\n {\n name: \"github-fine-grained-pat\",\n re: /\\bgithub_pat_[A-Za-z0-9_]{82,}\\b/g,\n replacement: \"[REDACTED:gh-fine-pat]\",\n },\n // GitHub OAuth, PAT, app, server, refresh tokens\n {\n name: \"github-token\",\n re: /\\bgh[pousr]_[A-Za-z0-9]{36,255}\\b/g,\n replacement: \"[REDACTED:gh-token]\",\n },\n\n // Anthropic\n {\n name: \"anthropic-key\",\n re: /\\bsk-ant-(?:api\\d{2}-)?[A-Za-z0-9_-]{40,}\\b/g,\n replacement: \"[REDACTED:anthropic-key]\",\n },\n\n // OpenAI (sk-proj-..., sk-svcacct-..., legacy sk-...)\n {\n name: \"openai-key\",\n re: /\\bsk-(?:proj-|svcacct-|admin-)?[A-Za-z0-9_-]{20,}\\b/g,\n replacement: \"[REDACTED:openai-key]\",\n },\n\n // Stripe\n {\n name: \"stripe-secret\",\n re: /\\bsk_(?:test|live)_[A-Za-z0-9]{16,}\\b/g,\n replacement: \"[REDACTED:stripe-secret]\",\n },\n {\n name: \"stripe-restricted\",\n re: /\\brk_(?:test|live)_[A-Za-z0-9]{16,}\\b/g,\n replacement: \"[REDACTED:stripe-restricted]\",\n },\n {\n name: \"stripe-publishable\",\n re: /\\bpk_(?:test|live)_[A-Za-z0-9]{16,}\\b/g,\n replacement: \"[REDACTED:stripe-publishable]\",\n },\n {\n name: \"stripe-webhook\",\n re: /\\bwhsec_[A-Za-z0-9]{32,}\\b/g,\n replacement: \"[REDACTED:stripe-webhook]\",\n },\n\n // AWS access key IDs (and STS / temporary forms)\n {\n name: \"aws-access-key-id\",\n re: /\\b(?:AKIA|ASIA|AGPA|AROA|AIDA|ANPA|ANVA|AIPA)[0-9A-Z]{16}\\b/g,\n replacement: \"[REDACTED:aws-key-id]\",\n },\n\n // Slack tokens\n {\n name: \"slack-token\",\n re: /\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b/g,\n replacement: \"[REDACTED:slack-token]\",\n },\n\n // Google API keys\n {\n name: \"google-api-key\",\n re: /\\bAIza[A-Za-z0-9_-]{35}\\b/g,\n replacement: \"[REDACTED:google-api-key]\",\n },\n\n // npm tokens\n {\n name: \"npm-token\",\n re: /\\bnpm_[A-Za-z0-9]{36}\\b/g,\n replacement: \"[REDACTED:npm-token]\",\n },\n\n // dotenv-style KEY=VALUE on its own line, where the KEY name looks sensitive.\n // This is a fallback for arbitrary secrets that don't match a specific\n // provider pattern. Captures the key, replaces the value.\n {\n name: \"dotenv-secret\",\n re: /^(\\s*(?:export\\s+)?[A-Z][A-Z0-9_]*?(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|PWD|API|AUTH|CREDENTIAL|PRIVATE|SESSION|COOKIE|BEARER|DSN)[A-Z0-9_]*\\s*=\\s*)(['\"]?)([^\\n'\"]{4,})\\2/gm,\n replacement: (m: string) => {\n // m is the full match; we keep the \"KEY=\" and quote, replace the value.\n // Re-run a small regex against m to preserve the prefix.\n const inner =\n /^(\\s*(?:export\\s+)?[A-Z][A-Z0-9_]*\\s*=\\s*)(['\"]?)([^\\n'\"]{4,})\\2/.exec(m);\n if (!inner) return \"[REDACTED:dotenv-secret]\";\n const [, prefix, quote] = inner;\n return `${prefix ?? \"\"}${quote ?? \"\"}[REDACTED:dotenv-secret]${quote ?? \"\"}`;\n },\n },\n\n // Email addresses\n {\n name: \"email\",\n re: /\\b[A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z]{2,}\\b/g,\n replacement: \"[REDACTED:email]\",\n },\n\n // IPv4 — validates each octet is 0-255 to cut version-string false positives.\n // Skips three benign forms below in the post-filter.\n {\n name: \"ipv4\",\n re: /\\b(?:(?:25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\b/g,\n replacement: (m: string) => {\n if (m === \"0.0.0.0\" || m === \"127.0.0.1\" || m === \"255.255.255.255\") return m;\n return \"[REDACTED:ip]\";\n },\n },\n];\n\n// Sensitive key-name shapes used by the recursive object walker. If an\n// object's key name matches AND the value is a non-trivial string, we redact\n// the whole value regardless of provider-pattern match.\n// Names that almost-always carry a secret value. Avoid generic words like\n// `session` (matches `session_id`) or bare `token` (matches `csrf_token`\n// inputs that are themselves not sensitive in our context).\nexport const SENSITIVE_KEY_RE =\n /(?:^|[_\\-.])(?:api[_-]?key|access[_-]?token|secret|password|passwd|pwd|authorization|bearer|credential|private[_-]?key|client[_-]?secret|webhook[_-]?secret|service[_-]?account|refresh[_-]?token)(?:$|[_\\-.])/i;\n","import { PATTERNS, SENSITIVE_KEY_RE } from \"./patterns.js\";\n\n/** Run all redaction patterns against a string. Idempotent. */\nexport function scrubString(input: string): string {\n if (!input) return input;\n let s = input;\n for (const rule of PATTERNS) {\n if (typeof rule.replacement === \"function\") {\n s = s.replace(rule.re, rule.replacement);\n } else {\n s = s.replace(rule.re, rule.replacement);\n }\n }\n return s;\n}\n\n/**\n * Recursively redact a JSON-shaped value. Strings get pattern-scrubbed;\n * object properties whose KEY name looks sensitive (`apiKey`, `password`,\n * etc.) have their entire string value replaced — that catches arbitrary\n * secrets that don't match any provider pattern.\n *\n * Cycle-safe via a WeakSet seen.\n */\nexport function scrubAny<T>(value: T, seen: WeakSet<object> = new WeakSet()): T {\n if (value == null) return value;\n if (typeof value === \"string\") return scrubString(value) as T;\n if (typeof value !== \"object\") return value;\n if (seen.has(value as object)) return value;\n seen.add(value as object);\n\n if (Array.isArray(value)) {\n return value.map((v) => scrubAny(v, seen)) as T;\n }\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n if (SENSITIVE_KEY_RE.test(k) && typeof v === \"string\" && v.length >= 4) {\n out[k] = \"[REDACTED:by-key-name]\";\n } else {\n out[k] = scrubAny(v, seen);\n }\n }\n return out as T;\n}\n","import { nanoid } from \"nanoid\";\nimport { insertEvent, upsertSession } from \"../db.js\";\nimport type { AgentEvent } from \"@agentreel/shared-types\";\nimport type { SnapshotEvent } from \"./watcher.js\";\n\nconst IDLE_MS = 5 * 60 * 1000; // 5 minute gap = new session\nconst GLOBAL_KEY = \"__global__\";\n\ninterface OpenSession {\n id: string;\n startedAt: number;\n lastTs: number;\n cwd: string;\n}\n\nexport class CursorSessionManager {\n private open = new Map<string, OpenSession>();\n\n ingest(snapshot: SnapshotEvent): { sessionId: string; isNew: boolean } {\n const key = snapshot.workspace ?? GLOBAL_KEY;\n const ts = snapshot.timestamp;\n const cwd = snapshot.workspace ?? \"\";\n\n let session = this.open.get(key);\n let isNew = false;\n if (!session || ts - session.lastTs > IDLE_MS) {\n // Close out the previous session for this key, if any.\n if (session) this.closeSession(session, session.lastTs);\n session = {\n id: `cur_${nanoid(10)}`,\n startedAt: ts,\n lastTs: ts,\n cwd,\n };\n this.open.set(key, session);\n isNew = true;\n upsertSession({\n id: session.id,\n tool: \"cursor\",\n startedAt: ts,\n cwd,\n });\n } else {\n session.lastTs = ts;\n }\n\n const event: AgentEvent = {\n id: nanoid(),\n sessionId: session.id,\n tool: \"cursor\",\n type: \"tool_use_post\",\n ts,\n cwd,\n payload: {\n tool_name: \"Edit\",\n file_path: snapshot.filePath,\n added: snapshot.added,\n removed: snapshot.removed,\n binary: snapshot.binary,\n source: snapshot.source,\n patch: snapshot.patch,\n },\n };\n insertEvent(event);\n return { sessionId: session.id, isNew };\n }\n\n /** Stamp ended_at on every open session — call on shutdown. */\n closeAll(): void {\n const now = Date.now();\n for (const s of this.open.values()) this.closeSession(s, Math.max(s.lastTs, now));\n this.open.clear();\n }\n\n private closeSession(s: OpenSession, endedAt: number) {\n upsertSession({\n id: s.id,\n tool: \"cursor\",\n startedAt: s.startedAt,\n endedAt,\n cwd: s.cwd,\n });\n }\n}\n","import pc from \"picocolors\";\nimport { readConfig } from \"../config.js\";\nimport { flushOnce } from \"../upload/flush.js\";\nimport { IngestError } from \"../upload/client.js\";\n\nexport async function pushCommand(): Promise<void> {\n const cfg = readConfig();\n if (!cfg.apiKey) {\n console.error(pc.red(\"✗ Not linked. Run \") + pc.cyan(\"agentreel link <api-key>\"));\n process.exit(1);\n }\n\n let totalSessions = 0;\n let totalEvents = 0;\n\n // Drain in batches of MAX_BATCH_EVENTS / MAX_BATCH_BYTES until empty.\n for (;;) {\n let res;\n try {\n res = await flushOnce();\n } catch (err) {\n const e = err as Error;\n if (err instanceof IngestError) {\n console.error(pc.red(\"✗ \") + e.message);\n if (err.isPermanent) {\n console.error(\n pc.dim(\" not retrying — fix the cause (re-link, upgrade plan, etc.) and run push again.\"),\n );\n }\n } else {\n console.error(pc.red(\"✗ \") + e.message);\n }\n process.exit(1);\n }\n totalSessions += res.uploadedSessions;\n totalEvents += res.uploadedEvents;\n if (!res.moreAvailable) break;\n }\n\n if (totalEvents === 0) {\n console.log(pc.green(\"✓\") + \" queue is empty — nothing to upload.\");\n return;\n }\n console.log(\n pc.green(\"✓\") +\n ` uploaded ${totalEvents} events · ${totalSessions} session rows touched`,\n );\n}\n","import pc from \"picocolors\";\nimport { existsSync, readFileSync, writeFileSync, unlinkSync } from \"node:fs\";\nimport { DAEMON_PID_PATH, ensureAgentreelDir } from \"../paths.js\";\nimport { pendingByteSize } from \"../db.js\";\nimport { flushOnce, nextAttemptAt } from \"../upload/flush.js\";\nimport { MAX_BATCH_BYTES } from \"../upload/queue.js\";\nimport { IngestError } from \"../upload/client.js\";\nimport { readConfig } from \"../config.js\";\n\nconst TICK_INTERVAL_MS = 30_000;\nconst EARLY_FLUSH_BYTES = MAX_BATCH_BYTES; // flush early once pending crosses 1 MB\n\nexport async function daemonCommand(): Promise<void> {\n const cfg = readConfig();\n if (!cfg.apiKey) {\n console.error(pc.red(\"✗ Not linked. Run \") + pc.cyan(\"agentreel link <api-key>\"));\n process.exit(1);\n }\n\n if (!claimPidFile()) {\n process.exit(1);\n }\n\n const release = () => {\n try {\n unlinkSync(DAEMON_PID_PATH);\n } catch {\n /* ignore */\n }\n };\n\n let stopping = false;\n const stop = () => {\n if (stopping) return;\n stopping = true;\n console.log(pc.dim(\"\\n daemon stopping…\"));\n release();\n process.exit(0);\n };\n process.on(\"SIGINT\", stop);\n process.on(\"SIGTERM\", stop);\n process.on(\"beforeExit\", release);\n\n console.log(pc.green(\"●\") + \" agentreel daemon running\");\n console.log(pc.dim(` api: ${cfg.apiBaseUrl}`));\n console.log(pc.dim(` tick: every ${TICK_INTERVAL_MS / 1000}s, early flush at ${(EARLY_FLUSH_BYTES / 1024).toFixed(0)} KB`));\n console.log(pc.dim(\" Ctrl-C to stop\"));\n\n // Loop until killed. Each iteration:\n // 1. honor backoff (next_attempt_at) before doing anything\n // 2. drain the queue in batches as long as moreAvailable\n // 3. sleep up to 30s, but wake early if pending bytes cross 1 MB\n for (;;) {\n if (stopping) return;\n\n // Backoff: if a prior failure scheduled a retry in the future, wait.\n const nextAt = nextAttemptAt();\n const now = Date.now();\n if (nextAt > now) {\n await sleepInterruptible(nextAt - now, () => stopping);\n continue;\n }\n\n // Drain.\n try {\n for (;;) {\n const res = await flushOnce();\n if (res.uploadedEvents > 0) {\n console.log(\n pc.dim(` [${ts()}]`) +\n pc.green(\" ✓\") +\n ` ${res.uploadedEvents} events · ${res.uploadedSessions} sessions`,\n );\n }\n if (!res.moreAvailable) break;\n }\n } catch (err) {\n const e = err as Error;\n if (err instanceof IngestError && err.isPermanent) {\n console.error(\n pc.dim(` [${ts()}]`) + pc.red(\" ✗ \") + e.message + pc.dim(\" (5min backoff)\"),\n );\n } else {\n console.error(pc.dim(` [${ts()}]`) + pc.yellow(\" · \") + e.message);\n }\n // flushOnce already recorded the backoff window.\n }\n\n // Wait for the next tick, but wake early if the queue hits 1 MB.\n await waitForTickOrPressure(TICK_INTERVAL_MS, EARLY_FLUSH_BYTES, () => stopping);\n }\n}\n\nfunction claimPidFile(): boolean {\n ensureAgentreelDir();\n if (existsSync(DAEMON_PID_PATH)) {\n const raw = readFileSync(DAEMON_PID_PATH, \"utf8\").trim();\n const pid = Number(raw);\n if (Number.isFinite(pid) && pid > 0 && isAlive(pid)) {\n console.error(pc.red(`✗ daemon already running (pid ${pid})`));\n console.error(pc.dim(` if this is wrong, remove ${DAEMON_PID_PATH}`));\n return false;\n }\n // Stale pid file — overwrite.\n }\n writeFileSync(DAEMON_PID_PATH, String(process.pid), { encoding: \"utf8\", mode: 0o600 });\n return true;\n}\n\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction sleepInterruptible(ms: number, shouldStop: () => boolean): Promise<void> {\n return new Promise((resolve) => {\n if (ms <= 0) return resolve();\n const start = Date.now();\n const t = setInterval(() => {\n if (shouldStop() || Date.now() - start >= ms) {\n clearInterval(t);\n resolve();\n }\n }, 250);\n });\n}\n\nfunction waitForTickOrPressure(\n tickMs: number,\n pressureBytes: number,\n shouldStop: () => boolean,\n): Promise<void> {\n return new Promise((resolve) => {\n const start = Date.now();\n const t = setInterval(() => {\n if (shouldStop()) {\n clearInterval(t);\n return resolve();\n }\n if (Date.now() - start >= tickMs) {\n clearInterval(t);\n return resolve();\n }\n // Pressure check: cheap COUNT/SUM query on the indexed pending column.\n try {\n if (pendingByteSize() >= pressureBytes) {\n clearInterval(t);\n return resolve();\n }\n } catch {\n // db locked / not initialized — let the tick handle it.\n }\n }, 1_000);\n });\n}\n\nfunction ts(): string {\n const d = new Date();\n return d.toTimeString().slice(0, 8);\n}\n","import { nanoid } from \"nanoid\";\nimport { appendFileSync } from \"node:fs\";\nimport type { AgentEvent, ClaudeCodeHookInput, EventType } from \"@agentreel/shared-types\";\nimport { insertEvent, upsertSession } from \"../db.js\";\nimport { LOG_PATH, ensureAgentreelDir } from \"../paths.js\";\nimport { scrubAny } from \"../redact/scrubber.js\";\n\nconst HOOK_EVENT_TO_TYPE: Record<string, EventType> = {\n SessionStart: \"session_start\",\n SessionEnd: \"session_end\",\n UserPromptSubmit: \"user_prompt_submit\",\n PreToolUse: \"tool_use_pre\",\n PostToolUse: \"tool_use_post\",\n Notification: \"notification\",\n Stop: \"stop\",\n SubagentStop: \"subagent_stop\",\n PreCompact: \"pre_compact\",\n};\n\nasync function readStdin(): Promise<string> {\n if (process.stdin.isTTY) return \"\";\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks).toString(\"utf8\");\n}\n\nfunction logError(err: unknown): void {\n try {\n ensureAgentreelDir();\n const line = `[${new Date().toISOString()}] hook error: ${\n err instanceof Error ? err.stack ?? err.message : String(err)\n }\\n`;\n appendFileSync(LOG_PATH, line);\n } catch {\n // swallow — never block Claude Code\n }\n}\n\nexport async function runHook(eventArg?: string): Promise<void> {\n // Hooks must never block or fail Claude Code. Wrap everything.\n try {\n const raw = await readStdin();\n if (!raw.trim()) return;\n const input = JSON.parse(raw) as ClaudeCodeHookInput;\n\n const hookEventName = input.hook_event_name ?? eventArg ?? \"Unknown\";\n const type: EventType = HOOK_EVENT_TO_TYPE[hookEventName] ?? \"unknown\";\n const ts = Date.now();\n const sessionId = input.session_id ?? \"unknown-session\";\n\n if (type === \"session_start\") {\n upsertSession({\n id: sessionId,\n tool: \"claude-code\",\n startedAt: ts,\n cwd: input.cwd,\n });\n } else if (type === \"session_end\") {\n upsertSession({\n id: sessionId,\n tool: \"claude-code\",\n startedAt: ts,\n endedAt: ts,\n cwd: input.cwd,\n });\n } else {\n // Make sure the session row exists so events have a parent.\n upsertSession({\n id: sessionId,\n tool: \"claude-code\",\n startedAt: ts,\n cwd: input.cwd,\n });\n }\n\n // Scrub the entire payload before persistence — never let raw secrets\n // touch SQLite, even briefly. scrubAny handles strings recursively and\n // also redacts values whose KEY name looks sensitive.\n const safePayload = scrubAny(input);\n const event: AgentEvent = {\n id: nanoid(),\n sessionId,\n tool: \"claude-code\",\n type,\n ts,\n cwd: input.cwd,\n payload: safePayload,\n };\n insertEvent(event);\n } catch (err) {\n logError(err);\n }\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAcnB,SAAS,qBAA2B;AACzC,YAAU,eAAe,EAAE,WAAW,KAAK,CAAC;AAC5C,YAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAC1C;AAnBA,IAIa,MACA,eACA,SACA,aACA,WACA,UACA,iBACA,iBAEA,YACA;AAdb;AAAA;AAAA;AAIO,IAAM,OAAO,QAAQ;AACrB,IAAM,gBAAgB,KAAK,MAAM,YAAY;AAC7C,IAAM,UAAU,KAAK,eAAe,aAAa;AACjD,IAAM,cAAc,KAAK,eAAe,aAAa;AACrD,IAAM,YAAY,KAAK,eAAe,OAAO;AAC7C,IAAM,WAAW,KAAK,eAAe,WAAW;AAChD,IAAM,kBAAkB,KAAK,eAAe,YAAY;AACxD,IAAM,kBAAkB,KAAK,eAAe,YAAY;AAExD,IAAM,aAAa,KAAK,MAAM,SAAS;AACvC,IAAM,uBAAuB,KAAK,YAAY,eAAe;AAAA;AAAA;;;ACdpE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,cAAc,eAAe,YAAY,cAAc,aAAAA,kBAAiB;AACjF,SAAS,eAAe;AAkCxB,SAAS,eAA+B;AACtC,MAAI,CAAC,WAAW,oBAAoB,EAAG,QAAO,CAAC;AAC/C,QAAM,MAAM,aAAa,sBAAsB,MAAM;AACrD,MAAI,CAAC,IAAI,KAAK,EAAG,QAAO,CAAC;AACzB,SAAO,KAAK,MAAM,GAAG;AACvB;AAEA,SAAS,iBAAgC;AACvC,MAAI,CAAC,WAAW,oBAAoB,EAAG,QAAO;AAC9C,QAAM,SAAQ,oBAAI,KAAK,GAAE,YAAY,EAAE,QAAQ,SAAS,GAAG;AAC3D,QAAM,SAAS,GAAG,oBAAoB,qBAAqB,KAAK;AAChE,eAAa,sBAAsB,MAAM;AACzC,SAAO;AACT;AAEA,SAAS,eAAe,OAAe,mBAAsC;AAK3E,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,GAAG,iBAAiB,SAAS,KAAK;AAAA,IAC3C,SAAS;AAAA,EACX;AACF;AAEO,SAAS,UAAU,GAAmB;AAC3C,SAAO,aAAa,KAAK,CAAC,IAAI,IAAI,EAAE,QAAQ,MAAM,KAAK,CAAC,MAAM;AAChE;AAQO,SAAS,uBAAuB,mBAA0C;AAC/E,EAAAA,WAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AACzC,EAAAA,WAAU,QAAQ,oBAAoB,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5D,QAAM,SAAS,eAAe;AAE9B,QAAM,WAAW,aAAa;AAC9B,WAAS,UAAU,CAAC;AAEpB,aAAW,SAAS,aAAa;AAC/B,UAAM,SAAS,SAAS,MAAM,KAAK,KAAK,CAAC;AAEzC,UAAM,WAAW,OAAO,OAAO,CAAC,MAAM,EAAE,gBAAgB,WAAW;AACnE,aAAS,KAAK;AAAA,MACZ,SAAS;AAAA,MACT,aAAa;AAAA,MACb,OAAO,CAAC,eAAe,OAAO,iBAAiB,CAAC;AAAA,IAClD,CAAC;AACD,aAAS,MAAM,KAAK,IAAI;AAAA,EAC1B;AAEA,gBAAc,sBAAsB,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,MAAM,MAAM;AACpF,SAAO,EAAE,QAAQ,iBAAiB,CAAC,GAAG,WAAW,GAAG,kBAAkB;AACxE;AAEO,SAAS,2BAAsD;AACpE,MAAI,CAAC,WAAW,oBAAoB,EAAG,QAAO,EAAE,QAAQ,KAAK;AAC7D,QAAM,SAAS,eAAe;AAC9B,QAAM,WAAW,aAAa;AAC9B,MAAI,SAAS,OAAO;AAClB,eAAW,SAAS,OAAO,KAAK,SAAS,KAAK,GAAG;AAC/C,YAAM,SAAS,SAAS,MAAM,KAAK,KAAK,CAAC;AACzC,YAAM,YAAY,OAAO,OAAO,CAAC,MAAM,EAAE,gBAAgB,WAAW;AACpE,UAAI,UAAU,WAAW,EAAG,QAAO,SAAS,MAAM,KAAK;AAAA,UAClD,UAAS,MAAM,KAAK,IAAI;AAAA,IAC/B;AACA,QAAI,OAAO,KAAK,SAAS,KAAK,EAAE,WAAW,EAAG,QAAO,SAAS;AAAA,EAChE;AACA,gBAAc,sBAAsB,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,MAAM,MAAM;AACpF,SAAO,EAAE,OAAO;AAClB;AA/GA,IAIM,aAEA;AANN;AAAA;AAAA;AAEA;AAEA,IAAM,cAAc;AAEpB,IAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA;AAAA;;;AChBA,SAAS,eAAe;;;ACIxB;AAJA,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,WAAW;AACpB,OAAO,QAAQ;;;ACFf;AADA,SAAS,gBAAAC,eAAc,iBAAAC,gBAAe,cAAAC,aAAY,iBAAiB;AAYnE,IAAM,UAAuB;AAAA,EAC3B,YAAY;AAAA,EACZ,eAAe;AACjB;AAEO,SAAS,aAA0B;AACxC,MAAI,CAACA,YAAW,WAAW,EAAG,QAAO,EAAE,GAAG,QAAQ;AAClD,MAAI;AACF,UAAM,MAAMF,cAAa,aAAa,MAAM;AAC5C,WAAO,EAAE,GAAG,SAAS,GAAG,KAAK,MAAM,GAAG,EAAE;AAAA,EAC1C,QAAQ;AACN,WAAO,EAAE,GAAG,QAAQ;AAAA,EACtB;AACF;AAEO,SAAS,YAAY,KAAwB;AAClD,qBAAmB;AAInB,EAAAC,eAAc,aAAa,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,MAAM;AAAA,IAC9D,UAAU;AAAA,IACV,MAAM;AAAA,EACR,CAAC;AACD,MAAI;AACF,cAAU,aAAa,GAAK;AAAA,EAC9B,QAAQ;AAAA,EAER;AACF;;;ACvCA;AAFA,OAAO,cAAc;AAKrB,IAAI,MAAiB;AAEd,SAAS,QAAY;AAC1B,MAAI,IAAK,QAAO;AAChB,qBAAmB;AACnB,QAAM,KAAK,IAAI,SAAS,OAAO;AAC/B,KAAG,OAAO,oBAAoB;AAC9B,KAAG,OAAO,sBAAsB;AAChC,UAAQ,EAAE;AACV,QAAM;AACN,SAAO;AACT;AAEA,SAAS,QAAQ,IAAc;AAC7B,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GA0BP;AACH;AAEO,SAAS,QAAQ,KAA4B;AAClD,QAAM,KAAK,MAAM;AACjB,QAAM,MAAM,GAAG,QAAQ,sCAAsC,EAAE,IAAI,GAAG;AAGtE,SAAO,KAAK,SAAS;AACvB;AAEO,SAAS,QAAQ,KAAa,OAA4B;AAC/D,QAAM,KAAK,MAAM;AACjB,MAAI,SAAS,MAAM;AACjB,OAAG,QAAQ,gCAAgC,EAAE,IAAI,GAAG;AACpD;AAAA,EACF;AACA,KAAG;AAAA,IACD;AAAA;AAAA,EAEF,EAAE,IAAI,KAAK,KAAK;AAClB;AAEO,SAAS,kBAA0B;AACxC,QAAM,KAAK,MAAM;AACjB,QAAM,MAAM,GACT;AAAA,IACC;AAAA;AAAA,EAEF,EACC,IAAI;AACP,SAAO,IAAI;AACb;AAEO,SAAS,cAAc,GAAkB;AAC9C,QAAM,KAAK,MAAM;AACjB,KAAG;AAAA,IACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOF,EAAE,IAAI;AAAA,IACJ,IAAI,EAAE;AAAA,IACN,MAAM,EAAE;AAAA,IACR,WAAW,EAAE;AAAA,IACb,SAAS,EAAE,WAAW;AAAA,IACtB,KAAK,EAAE,OAAO;AAAA,IACd,gBAAgB,EAAE,kBAAkB;AAAA,IACpC,aAAa,EAAE,eAAe;AAAA,EAChC,CAAC;AACH;AAEO,SAAS,YAAY,GAAqB;AAC/C,QAAM,KAAK,MAAM;AACjB,KAAG;AAAA,IACD;AAAA;AAAA,EAEF,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,MAAM,KAAK,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;AAC/F;AAEO,SAAS,cAAkD;AAChE,QAAM,KAAK,MAAM;AACjB,QAAM,QAAQ,GAAG,QAAQ,kCAAkC,EAAE,IAAI;AACjE,QAAM,UAAU,GACb,QAAQ,4DAA4D,EACpE,IAAI;AACP,SAAO,EAAE,OAAO,MAAM,GAAG,SAAS,QAAQ,EAAE;AAC9C;AAEO,SAAS,mBAAmB,QAAQ,IAMxC;AACD,QAAM,KAAK,MAAM;AACjB,SAAO,GACJ;AAAA,IACC;AAAA;AAAA,EAEF,EACC,IAAI,KAAK;AAOd;;;AFlIA;AAEA,IAAM,eAAe;AAOrB,SAAS,qBAAqC;AAM5C,QAAM,OAAO,cAAc,YAAY,GAAG;AAC1C,QAAM,OAAO,aAAa,IAAI;AAC9B,QAAM,cAAc,KAAK,SAAS,GAAG,GAAG,OAAO,GAAG,EAAE,KAAK,KAAK,SAAS,QAAQ;AAC/E,SAAO,EAAE,cAAc,MAAM,YAAY;AAC3C;AAEA,SAAS,iBAAiB,KAAmE;AAC3F,MAAI,QAAQ,IAAI,wBAAwB;AACtC,WAAO,EAAE,QAAQ,QAAQ,IAAI,wBAAwB,MAAM,WAAW;AAAA,EACxE;AACA,MAAI,IAAI,aAAa;AACnB,WAAO,EAAE,QAAQ,UAAU,YAAY,IAAI,MAAM,MAAM;AAAA,EACzD;AACA,SAAO,EAAE,QAAQ,UAAU,IAAI,YAAY,GAAG,MAAM,WAAW;AACjE;AAEA,eAAsB,cAA6B;AACjD,qBAAmB;AAGnB,QAAM;AAEN,QAAM,MAAM,WAAW;AACvB,MAAI,CAAC,IAAI,YAAa,KAAI,cAAc,KAAK,IAAI;AACjD,MAAI,iBAAiB;AACrB,cAAY,GAAG;AAEf,QAAM,SAAS,mBAAmB;AAClC,QAAM,EAAE,QAAQ,KAAK,IAAI,iBAAiB,MAAM;AAChD,QAAM,SAAS,uBAAuB,MAAM;AAE5C,UAAQ,IAAI,GAAG,KAAK,GAAG,KAAK,iBAAiB,CAAC,IAAI,GAAG,IAAI,+BAA+B,CAAC;AACzF,UAAQ,IAAI,GAAG,MAAM,QAAG,IAAI,mCAAmC;AAC/D,UAAQ,IAAI,GAAG,MAAM,QAAG,IAAI,iCAAiC;AAC7D,UAAQ;AAAA,IACN,GAAG,MAAM,QAAG,IACV,cAAc,OAAO,gBAAgB,MAAM;AAAA,EAC/C;AACA,MAAI,OAAO,QAAQ;AACjB,YAAQ,IAAI,GAAG,IAAI,cAAc,OAAO,MAAM,GAAG,CAAC;AAAA,EACpD;AACA,UAAQ,IAAI;AACZ,UAAQ,IAAI,GAAG,IAAI,kBAAkB,IAAI,GAAG,IAAI,GAAG,MAAM,eAAe,CAAC;AACzE,MAAI,SAAS,OAAO;AAClB,YAAQ;AAAA,MACN,GAAG,IAAI,IAAI,IACT,GAAG,OAAO,QAAG,IACb,GAAG;AAAA,QACD;AAAA,4CAC6C,YAAY;AAAA,MAC3D;AAAA,IACJ;AAAA,EACF;AACA,UAAQ,IAAI;AACZ,UAAQ,IAAI,GAAG,KAAK,OAAO,IAAI,qCAAqC;AACpE,UAAQ,IAAI,gBAAgB,GAAG,KAAK,kBAAkB,IAAI,4BAA4B;AACxF;;;AG5EA;AAFA,OAAOE,SAAQ;AACf,SAAS,cAAAC,aAAY,gBAAAC,qBAAoB;;;AC6BlC,IAAM,cAAN,cAA0B,MAAM;AAAA,EACrC,YACE,SACS,QACA,MACT;AACA,UAAM,OAAO;AAHJ;AACA;AAGT,SAAK,OAAO;AAAA,EACd;AAAA,EALW;AAAA,EACA;AAAA;AAAA,EAMX,IAAI,cAAuB;AACzB,WAAO,KAAK,UAAU,OAAO,KAAK,SAAS,OAAO,KAAK,WAAW,OAAO,KAAK,WAAW;AAAA,EAC3F;AACF;AAEA,eAAsB,WACpB,KACA,MACyB;AACzB,MAAI,CAAC,IAAI,QAAQ;AACf,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACzD;AACA,QAAM,MAAM,GAAG,IAAI,WAAW,QAAQ,OAAO,EAAE,CAAC;AAChD,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,eAAe,UAAU,IAAI,MAAM;AAAA,IACrC;AAAA,IACA,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC;AACD,MAAI,OAA8B;AAClC,MAAI;AACF,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB,QAAQ;AAAA,EAER;AACA,MAAI,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI;AACxB,UAAM,SAAS,MAAM,SAAS,KAAK,KAAK,MAAM,MAAM;AACpD,UAAM,MAAM,MAAM,SAAS,QAAQ,IAAI,MAAM;AAC7C,UAAM,IAAI,YAAY,GAAG,GAAG,GAAG,MAAM,IAAI,IAAI,QAAQ,IAAI;AAAA,EAC3D;AACA,SAAO;AACT;;;AC1CO,IAAM,kBAAkB,OAAO;AAC/B,IAAM,mBAAmB;AAEzB,SAAS,iBACd,YAAY,kBACZ,WAAW,iBACG;AACd,QAAM,KAAK,MAAM;AACjB,QAAM,gBAAgB,GACnB;AAAA,IACC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKF,EACC,IAAI,SAAS;AAEhB,MAAI,cAAc,WAAW,EAAG,QAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,GAAG,UAAU,CAAC,EAAE;AAKhF,QAAM,YAA0B,CAAC;AACjC,MAAI,QAAQ;AACZ,aAAW,OAAO,eAAe;AAC/B,UAAM,WAAW,OAAO,WAAW,IAAI,SAAS,MAAM;AACtD,QAAI,UAAU,SAAS,KAAK,QAAQ,WAAW,SAAU;AACzD,cAAU,KAAK,GAAG;AAClB,aAAS;AAAA,EACX;AAEA,QAAM,aAAa,CAAC,GAAG,IAAI,IAAI,UAAU,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAClE,QAAM,eAAe,WAAW,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AACvD,QAAM,cAAc,GACjB;AAAA,IACC;AAAA,oCAC8B,YAAY;AAAA,EAC5C,EACC,IAAI,GAAG,UAAU;AAEpB,QAAM,WAA4B,YAAY,IAAI,CAAC,OAAO;AAAA,IACxD,IAAI,EAAE;AAAA,IACN,MAAM,EAAE;AAAA,IACR,YAAY,EAAE;AAAA,IACd,UAAU,EAAE;AAAA,IACZ,KAAK,EAAE;AAAA,IACP,kBAAkB,EAAE;AAAA,IACpB,cAAc,EAAE;AAAA,EAClB,EAAE;AAEF,QAAM,SAAwB,UAAU,IAAI,CAAC,OAAO;AAAA,IAClD,IAAI,EAAE;AAAA,IACN,YAAY,EAAE;AAAA,IACd,IAAI,EAAE;AAAA,IACN,MAAM,EAAE;AAAA,IACR,MAAM,EAAE;AAAA,IACR,KAAK,EAAE;AAAA,IACP,SAAS,UAAU,EAAE,OAAO;AAAA,EAC9B,EAAE;AAEF,SAAO,EAAE,UAAU,QAAQ,UAAU,UAAU,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE;AAClE;AAEO,SAAS,aAAa,UAA0B;AACrD,MAAI,SAAS,WAAW,EAAG;AAC3B,QAAM,KAAK,MAAM;AACjB,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,OAAO,GAAG,QAAQ,gDAAgD;AACxE,QAAM,KAAK,GAAG,YAAY,CAAC,QAAkB;AAC3C,eAAW,MAAM,IAAK,MAAK,IAAI,KAAK,EAAE;AAAA,EACxC,CAAC;AACD,KAAG,QAAQ;AACb;AAEA,SAAS,UAAU,GAAoB;AACrC,MAAI;AACF,WAAO,KAAK,MAAM,CAAC;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AC3GO,IAAM,oBAAoB;AAC1B,IAAM,qBAAqB;AAC3B,IAAM,sBAAsB;AAC5B,IAAM,yBAAyB;AAC/B,IAAM,uBAAuB;AAEpC,IAAM,kBAAkB;AACxB,IAAM,iBAAiB,IAAI;AAUpB,SAAS,gBAAwB;AACtC,QAAM,MAAM,QAAQ,oBAAoB;AACxC,SAAO,MAAM,OAAO,GAAG,IAAI;AAC7B;AAEO,SAAS,sBAA8B;AAC5C,QAAM,MAAM,QAAQ,sBAAsB;AAC1C,SAAO,MAAM,OAAO,GAAG,IAAI;AAC7B;AAEA,SAAS,gBAAgB;AACvB,QAAM,MAAM,KAAK,IAAI;AACrB,UAAQ,mBAAmB,OAAO,GAAG,CAAC;AACtC,UAAQ,oBAAoB,IAAI;AAChC,UAAQ,qBAAqB,IAAI;AACjC,UAAQ,wBAAwB,GAAG;AACnC,UAAQ,sBAAsB,GAAG;AACnC;AAEA,SAAS,cAAc,KAAY,WAAoB;AACrD,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,QAAQ,oBAAoB,IAAI;AACtC,UAAQ,oBAAoB,OAAO,GAAG,CAAC;AACvC,UAAQ,qBAAqB,SAAS,IAAI,SAAS,GAAG,CAAC;AACvD,UAAQ,wBAAwB,OAAO,KAAK,CAAC;AAC7C,MAAI,WAAW;AAKb,YAAQ,sBAAsB,OAAO,MAAM,cAAc,CAAC;AAC1D;AAAA,EACF;AAEA,QAAM,MAAM,KAAK,IAAI,gBAAgB,kBAAkB,MAAM,QAAQ,EAAE;AACvE,QAAM,SAAS,MAAM,OAAO,KAAK,OAAO;AACxC,UAAQ,sBAAsB,OAAO,MAAM,MAAM,MAAM,CAAC;AAC1D;AAEA,SAAS,SAAS,GAAW,GAAW;AACtC,SAAO,EAAE,SAAS,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,IAAI,WAAM;AAClD;AAOA,eAAsB,YAAkC;AACtD,QAAM,MAAM,WAAW;AACvB,MAAI,CAAC,IAAI,QAAQ;AACf,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACA,QAAM,QAAQ,iBAAiB,kBAAkB,eAAe;AAChE,MAAI,MAAM,OAAO,WAAW,GAAG;AAG7B,WAAO,EAAE,gBAAgB,GAAG,kBAAkB,GAAG,eAAe,MAAM;AAAA,EACxE;AACA,MAAI;AACF,UAAM,MAAM,MAAM,WAAW,KAAK,EAAE,UAAU,MAAM,UAAU,QAAQ,MAAM,OAAO,CAAC;AACpF,iBAAa,MAAM,QAAQ;AAC3B,kBAAc;AACd,WAAO;AAAA,MACL,gBAAgB,IAAI,kBAAkB;AAAA,MACtC,kBAAkB,IAAI,oBAAoB;AAAA,MAC1C,eAAe,MAAM,OAAO,WAAW;AAAA,IACzC;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,YAAY,eAAe,eAAe,IAAI;AACpD,kBAAc,KAAc,SAAS;AACrC,UAAM;AAAA,EACR;AACF;;;AH7EA,SAAS,YAAY,IAAoB;AACvC,QAAM,IAAI,KAAK,MAAM,KAAK,GAAI;AAC9B,MAAI,IAAI,GAAI,QAAO,GAAG,CAAC;AACvB,QAAM,IAAI,KAAK,MAAM,IAAI,EAAE;AAC3B,MAAI,IAAI,GAAI,QAAO,GAAG,CAAC,KAAK,IAAI,EAAE;AAClC,QAAM,IAAI,KAAK,MAAM,IAAI,EAAE;AAC3B,SAAO,GAAG,CAAC,KAAK,IAAI,EAAE;AACxB;AAEA,SAAS,OAAO,IAAoB;AAClC,SAAO,YAAY,KAAK,IAAI,IAAI,EAAE,IAAI;AACxC;AAEA,SAAS,SAAS,GAAmB;AACnC,MAAI,IAAI,KAAM,QAAO,GAAG,CAAC;AACzB,MAAI,IAAI,OAAO,KAAM,QAAO,IAAI,IAAI,MAAM,QAAQ,CAAC,CAAC;AACpD,SAAO,IAAI,IAAI,OAAO,MAAM,QAAQ,CAAC,CAAC;AACxC;AAEA,SAAS,eAAyD;AAChE,MAAI,CAACC,YAAW,eAAe,EAAG,QAAO,EAAE,SAAS,OAAO,KAAK,KAAK;AACrE,MAAI;AACF,UAAM,MAAM,OAAOC,cAAa,iBAAiB,MAAM,EAAE,KAAK,CAAC;AAC/D,QAAI,CAAC,OAAO,SAAS,GAAG,KAAK,OAAO,EAAG,QAAO,EAAE,SAAS,OAAO,KAAK,KAAK;AAC1E,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO,EAAE,SAAS,MAAM,IAAI;AAAA,EAC9B,QAAQ;AACN,WAAO,EAAE,SAAS,OAAO,KAAK,KAAK;AAAA,EACrC;AACF;AAEA,eAAsB,gBAA+B;AACnD,QAAM,MAAM,WAAW;AACvB,UAAQ,IAAIC,IAAG,KAAKA,IAAG,KAAK,oBAAoB,CAAC,CAAC;AAClD,UAAQ,IAAI,uBAAuBF,YAAW,WAAW,IAAI,cAAcE,IAAG,IAAI,SAAS,EAAE;AAC7F,UAAQ,IAAI,uBAAuBF,YAAW,OAAO,IAAI,UAAUE,IAAG,IAAI,iBAAiB,EAAE;AAC7F,UAAQ;AAAA,IACN,uBACGF,YAAW,oBAAoB,IAAI,uBAAuBE,IAAG,IAAI,eAAe;AAAA,EACrF;AACA,UAAQ,IAAI,sBAAsB,IAAI,UAAU;AAChD,UAAQ,IAAI,uBAAuB,IAAI,SAASA,IAAG,MAAM,KAAK,IAAIA,IAAG,OAAO,IAAI,EAAE;AAElF,QAAM,IAAI,aAAa;AACvB,UAAQ;AAAA,IACN,uBACG,EAAE,UAAUA,IAAG,MAAM,gBAAgB,EAAE,GAAG,GAAG,IAAIA,IAAG,IAAI,SAAS;AAAA,EACtE;AACA,UAAQ,IAAI;AAEZ,MAAI,CAACF,YAAW,OAAO,GAAG;AACxB,YAAQ,IAAIE,IAAG,OAAO,wCAAwC,CAAC;AAC/D;AAAA,EACF;AAEA,QAAM,EAAE,OAAO,QAAQ,IAAI,YAAY;AACvC,QAAM,eAAe,gBAAgB;AACrC,UAAQ,IAAI,uBAAuB,KAAK,EAAE;AAC1C,UAAQ,IAAI,uBAAuB,OAAO,MAAM,UAAU,IAAIA,IAAG,IAAI,KAAK,SAAS,YAAY,CAAC,GAAG,IAAI,GAAG;AAE1G,QAAM,WAAW,QAAQ,iBAAiB;AAC1C,QAAM,UAAU,QAAQ,kBAAkB;AAC1C,QAAM,aAAa,QAAQ,mBAAmB;AAC9C,QAAM,QAAQ,oBAAoB;AAClC,QAAM,SAAS,QAAQ,oBAAoB;AAE3C,UAAQ;AAAA,IACN,0BACG,WAAWA,IAAG,MAAM,OAAO,QAAQ,CAAC,IAAIA,IAAG,IAAI,OAAO;AAAA,EAC3D;AACA,MAAI,SAAS;AACX,YAAQ;AAAA,MACN,yBACEA,IAAG,IAAI,OAAO,OAAO,CAAC,IACtBA,IAAG,IAAI,SAAM,KAAK,cAAc;AAAA,IACpC;AACA,QAAI,WAAY,SAAQ,IAAIA,IAAG,IAAI,OAAO,UAAU,EAAE,CAAC;AACvD,QAAI,UAAU,SAAS,KAAK,IAAI,GAAG;AACjC,cAAQ;AAAA,QACNA,IAAG,IAAI,uBAAuB,YAAY,SAAS,KAAK,IAAI,CAAC,CAAC,EAAE;AAAA,MAClE;AAAA,IACF;AAAA,EACF;AACA,UAAQ,IAAI;AAEZ,QAAM,WAAW,mBAAmB,CAAC;AACrC,MAAI,SAAS,WAAW,GAAG;AACzB,YAAQ,IAAIA,IAAG,IAAI,sEAAiE,CAAC;AACrF;AAAA,EACF;AACA,UAAQ,IAAIA,IAAG,KAAK,kBAAkB,CAAC;AACvC,aAAW,KAAK,UAAU;AACxB,UAAM,MAAM,EAAE,WAAW,YAAY,EAAE,WAAW,EAAE,UAAU,IAAIA,IAAG,IAAI,QAAQ;AACjF,UAAMC,MAAK,IAAI,KAAK,EAAE,UAAU,EAAE,eAAe;AACjD,YAAQ,IAAI,KAAKD,IAAG,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,OAAO,EAAE,CAAC,KAAKC,GAAE,KAAK,GAAG,EAAE;AAAA,EAClF;AACF;AAEA,SAAS,QAAQ,KAA4B;AAC3C,QAAM,IAAI,QAAQ,GAAG;AACrB,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,IAAI,OAAO,CAAC;AAClB,SAAO,OAAO,SAAS,CAAC,KAAK,IAAI,IAAI,IAAI;AAC3C;;;AIzHA,OAAOC,SAAQ;AAIf,eAAsB,gBAA+B;AACnD,QAAM,MAAM,WAAW;AACvB,MAAI,SAAS;AACb,MAAI,cAAc;AAClB,cAAY,GAAG;AACf,UAAQ,IAAIC,IAAG,MAAM,QAAG,IAAI,6BAA6B;AAC3D;AAMA,eAAsB,YAAY,QAA4B,MAA+B;AAC3F,QAAM,OAAO,UAAW,MAAM,aAAa,gCAAgC,GAAI,KAAK;AACpF,MAAI,CAAC,KAAK;AACR,YAAQ,MAAMA,IAAG,IAAI,yBAAoB,CAAC;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,CAAC,IAAI,WAAW,UAAU,GAAG;AAC/B,YAAQ,MAAMA,IAAG,IAAI,mEAA8D,CAAC;AACpF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,WAAW;AACvB,MAAI,SAAS;AACb,MAAI,KAAK,IAAK,KAAI,aAAa,KAAK;AAEpC,UAAQ,IAAIA,IAAG,IAAI,wBAAwB,IAAI,UAAU,QAAG,CAAC;AAC7D,MAAI;AACF,UAAM,WAAW,KAAK,CAAC,CAAC;AAAA,EAC1B,SAAS,KAAK;AACZ,YAAQ,MAAMA,IAAG,IAAI,4BAAuB,IAAK,IAAc,OAAO;AACtE,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,cAAY,GAAG;AACf,UAAQ,IAAIA,IAAG,MAAM,QAAG,IAAI,UAAU;AACtC,UAAQ,IAAIA,IAAG,IAAI,SAAS,IAAI,IAAI,UAAU;AAC9C,UAAQ,IAAIA,IAAG,IAAI,SAAS,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI,QAAG;AACxD;AAEA,eAAsB,mBAAkC;AACtD,QAAM,EAAE,0BAAAC,0BAAyB,IAAI,MAAM;AAC3C,QAAM,EAAE,OAAO,IAAIA,0BAAyB;AAC5C,UAAQ,IAAID,IAAG,MAAM,QAAG,IAAI,uDAAuD;AACnF,MAAI,OAAQ,SAAQ,IAAIA,IAAG,IAAI,cAAc,MAAM,GAAG,CAAC;AACzD;AAEA,IAAM,SAAS;AACf,IAAM,YAAY;AAClB,IAAM,gBAAgB;AACtB,IAAM,UAAU;AAChB,IAAM,WAAW;AAEjB,SAAS,aAAa,QAAiC;AACrD,SAAO,IAAI,QAAQ,CAACE,aAAY;AAC9B,YAAQ,OAAO,MAAM,MAAM;AAC3B,QAAI,MAAM;AACV,UAAM,QAAQ,QAAQ;AACtB,UAAM,aAAa,IAAI;AACvB,UAAM,OAAO;AACb,UAAM,YAAY,MAAM;AACxB,UAAM,SAAS,CAAC,UAAkB;AAChC,iBAAW,MAAM,OAAO;AACtB,cAAM,OAAO,GAAG,WAAW,CAAC;AAC5B,YAAI,SAAS,WAAW,SAAS,UAAU;AACzC,gBAAM,aAAa,KAAK;AACxB,gBAAM,MAAM;AACZ,gBAAM,eAAe,QAAQ,MAAM;AACnC,kBAAQ,OAAO,MAAM,IAAI;AACzB,iBAAOA,SAAQ,GAAG;AAAA,QACpB;AACA,YAAI,SAAS,QAAQ;AACnB,gBAAM,aAAa,KAAK;AACxB,kBAAQ,KAAK,GAAG;AAAA,QAClB;AACA,YAAI,SAAS,aAAa,SAAS,eAAe;AAChD,gBAAM,IAAI,MAAM,GAAG,EAAE;AAAA,QACvB,OAAO;AACL,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AACA,UAAM,GAAG,QAAQ,MAAM;AAAA,EACzB,CAAC;AACH;;;ACxFA,OAAOC,SAAQ;AACf,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,YAAAC,iBAAgB;;;ACFzB,SAAS,WAAAC,UAAS,gBAAgB;AAClC,SAAS,QAAAC,aAAY;AAId,SAAS,0BAAkC;AAChD,QAAM,OAAOD,SAAQ;AACrB,UAAQ,SAAS,GAAG;AAAA,IAClB,KAAK;AACH,aAAOC,MAAK,MAAM,WAAW,uBAAuB,UAAU,QAAQ,SAAS;AAAA,IACjF,KAAK,SAAS;AACZ,YAAM,UAAU,QAAQ,IAAI,WAAWA,MAAK,MAAM,WAAW,SAAS;AACtE,aAAOA,MAAK,SAAS,UAAU,QAAQ,SAAS;AAAA,IAClD;AAAA,IACA;AACE,aAAOA,MAAK,MAAM,WAAW,UAAU,QAAQ,SAAS;AAAA,EAC5D;AACF;AAEO,SAAS,mBAA2B;AACzC,SAAO,QAAQ,IAAI,gCAAgC,wBAAwB;AAC7E;;;ACrBA,OAAO,cAAkC;AACzC,SAAS,YAAAC,iBAAgB;AACzB,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,UAAU,WAAAC,UAAS,QAAAC,aAAY;;;ACHxC,SAAS,gBAAgB;AACzB,SAAS,cAAAC,aAAY,gBAAgB;AACrC,SAAS,QAAAC,OAAM,WAAAC,UAAS,SAAS,OAAAC,YAAW;AAC5C,SAAS,iBAAAC,sBAAqB;AAc9B,eAAsB,YAAY,eAAoD;AACpF,QAAM,OAAOH,MAAK,eAAe,cAAc;AAC/C,MAAI,CAACD,YAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,MAAM,MAAM;AACvC,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,eAAe,UAAiC;AAC9D,MAAI,CAAC,SAAS,WAAW,SAAS,EAAG,QAAO;AAC5C,MAAI;AACF,WAAOI,eAAc,QAAQ;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAIO,SAAS,kBAAkB,MAA6B;AAC7D,MAAI,MAAMF,SAAQ,QAAQ,IAAI,CAAC;AAC/B,SAAO,OAAO,QAAQC,MAAK;AACzB,UAAM,MAAMF,MAAK,KAAK,MAAM;AAC5B,QAAI;AACF,UAAID,YAAW,GAAG,EAAG,QAAO;AAAA,IAC9B,QAAQ;AAAA,IAER;AACA,UAAM,SAASE,SAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEO,SAAS,iBAAiB,MAAuB;AACtD,MAAI;AACF,UAAM,IAAI,SAAS,IAAI;AACvB,QAAI,EAAE,OAAO,OAAO,KAAM,QAAO;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AAGA,QAAM,OACJ;AACF,SAAO,CAAC,KAAK,KAAK,IAAI;AACxB;;;ACnEA,SAAS,iBAAiB;AAC1B,OAAO,oBAAoB;AAU3B,IAAM,MAAM,IAAI,eAAe,iBAAiB;AAEhD,IAAI,eAAe;AAEZ,SAAS,YAAY,QAAgB,OAA2B;AAErE,QAAM,UAAU,IAAI,WAAW,QAAQ,KAAK;AAC5C,QAAM,QAAQ,IAAI,aAAa,OAAO;AAGtC,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,aAAW,QAAQ,UAAU,QAAQ,KAAK,GAAG;AAC3C,UAAM,IACJ,KAAK,UACJ,KAAK,MAAM,MAAM,KAAK,GAAG,WAAW,KAAK,MAAM,SAAS,IAAI,IAAI;AACnE,QAAI,KAAK,MAAO,UAAS;AAAA,aAChB,KAAK,QAAS,YAAW;AAAA,EACpC;AAEA,SAAO,EAAE,OAAO,OAAO,QAAQ;AACjC;;;AChCA,SAAS,cAAAG,aAAY,gBAAAC,eAAc,YAAAC,iBAAgB;AACnD,SAAS,QAAAC,OAAM,UAAU,OAAAC,YAAW;AACpC,OAAO,YAA6B;AAEpC,IAAM,WAAW;AACjB,IAAM,SAAS;AAQf,IAAM,QAAQ,oBAAI,IAAwB;AAE1C,SAAS,QAAQ,WAAkC;AACjD,QAAM,OAAOD,MAAK,WAAW,QAAQ;AACrC,MAAI,CAACH,YAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,UAAM,MAAMC,cAAa,MAAM,MAAM;AACrC,WAAO,OAAO,EAAE,oBAAoB,KAAK,CAAC,EAAE,IAAI,GAAG;AAAA,EACrD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,UAAU,WAAkC;AACnD,QAAM,QAAQ,MAAM,IAAI,SAAS;AACjC,QAAM,OAAOE,MAAK,WAAW,QAAQ;AACrC,MAAI,QAAQ;AACZ,MAAI;AACF,YAAQH,YAAW,IAAI,IAAIE,UAAS,IAAI,EAAE,UAAU;AAAA,EACtD,QAAQ;AACN,YAAQ;AAAA,EACV;AACA,QAAM,MAAM,KAAK,IAAI;AACrB,MAAI,SAAS,MAAM,MAAM,WAAW,UAAU,MAAM,cAAc,OAAO;AACvE,WAAO,MAAM;AAAA,EACf;AACA,QAAM,UAAU,QAAQ,SAAS;AACjC,QAAM,IAAI,WAAW,EAAE,SAAS,UAAU,KAAK,WAAW,MAAM,CAAC;AACjE,SAAO;AACT;AAOO,SAAS,UAAU,WAAsC,UAA2B;AACzF,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,UAAU,UAAU,SAAS;AACnC,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,MAAM,SAAS,WAAW,QAAQ;AAExC,MAAI,CAAC,OAAO,IAAI,WAAW,IAAI,KAAK,IAAI,WAAWE,IAAG,EAAG,QAAO;AAEhE,SAAO,QAAQ,QAAQ,IAAI,MAAMA,IAAG,EAAE,KAAK,GAAG,CAAC;AACjD;;;AClDO,IAAM,WAA4B;AAAA;AAAA,EAEvC;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAEA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa,CAAC,MAAc;AAG1B,YAAM,QACJ,mEAAmE,KAAK,CAAC;AAC3E,UAAI,CAAC,MAAO,QAAO;AACnB,YAAM,CAAC,EAAE,QAAQ,KAAK,IAAI;AAC1B,aAAO,GAAG,UAAU,EAAE,GAAG,SAAS,EAAE,2BAA2B,SAAS,EAAE;AAAA,IAC5E;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA;AAAA,EAIA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa,CAAC,MAAc;AAC1B,UAAI,MAAM,aAAa,MAAM,eAAe,MAAM,kBAAmB,QAAO;AAC5E,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAQO,IAAM,mBACX;;;AC5IK,SAAS,YAAY,OAAuB;AACjD,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,IAAI;AACR,aAAW,QAAQ,UAAU;AAC3B,QAAI,OAAO,KAAK,gBAAgB,YAAY;AAC1C,UAAI,EAAE,QAAQ,KAAK,IAAI,KAAK,WAAW;AAAA,IACzC,OAAO;AACL,UAAI,EAAE,QAAQ,KAAK,IAAI,KAAK,WAAW;AAAA,IACzC;AAAA,EACF;AACA,SAAO;AACT;AAUO,SAAS,SAAY,OAAU,OAAwB,oBAAI,QAAQ,GAAM;AAC9E,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,UAAU,SAAU,QAAO,YAAY,KAAK;AACvD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,KAAK,IAAI,KAAe,EAAG,QAAO;AACtC,OAAK,IAAI,KAAe;AAExB,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC;AAAA,EAC3C;AAEA,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACrE,QAAI,iBAAiB,KAAK,CAAC,KAAK,OAAO,MAAM,YAAY,EAAE,UAAU,GAAG;AACtE,UAAI,CAAC,IAAI;AAAA,IACX,OAAO;AACL,UAAI,CAAC,IAAI,SAAS,GAAG,IAAI;AAAA,IAC3B;AAAA,EACF;AACA,SAAO;AACT;;;ALXO,SAAS,mBACd,YACA,YACA,OAAuB,CAAC,GACb;AACX,QAAM,YAAY,KAAK,wBAAwB;AAE/C,QAAM,UAAU,SAAS,MAAM,YAAY;AAAA,IACzC,eAAe;AAAA,IACf,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,kBAAkB,EAAE,oBAAoB,KAAK,cAAc,GAAG;AAAA,EAChE,CAAC;AAED,UAAQ,GAAG,OAAO,OAAO,SAAS;AAChC,QAAI;AACF,YAAM,SAASC,SAAQ,IAAI;AAC3B,YAAM,OAAO,SAAS,IAAI;AAG1B,UAAI,SAAS,eAAgB;AAG7B,YAAM,MAAM,MAAM,aAAa,QAAQ,MAAM,GAAK;AAClD,UAAI,CAAC,IAAK;AACV,YAAM,gBAAgB,QAAQ,KAAK,WAAW,UAAU;AAAA,IAC1D,SAAS,KAAK;AAGZ,cAAQ,MAAM,gDAAgD,MAAM,GAAG;AAAA,IACzE;AAAA,EACF,CAAC;AAED,SAAO;AACT;AASA,eAAe,aACb,QACA,QACA,SACiC;AACjC,QAAM,QAAQ,KAAK,IAAI;AACvB,SAAO,KAAK,IAAI,IAAI,QAAQ,SAAS;AACnC,UAAM,OAAO,MAAM,YAAY,MAAM;AACrC,QAAI,MAAM;AACR,YAAM,MAAM,KAAK,QAAQ,UAAU,CAAC,MAAM,EAAE,OAAO,MAAM;AACzD,UAAI,OAAO,GAAG;AACZ,cAAM,UAAU,KAAK,QAAQ,GAAG;AAChC,YAAI,CAAC,QAAS,QAAO;AACrB,cAAM,WAAW,MAAM,IAAK,KAAK,QAAQ,MAAM,CAAC,KAAK,OAAQ;AAC7D,eAAO,EAAE,UAAU,KAAK,UAAU,SAAS,UAAU,YAAY,IAAI;AAAA,MACvE;AAAA,IACF;AACA,UAAM,MAAM,GAAG;AAAA,EACjB;AACA,SAAO;AACT;AAEA,eAAe,gBACb,QACA,KACA,WACA,YACe;AACf,QAAM,WAAW,eAAe,IAAI,QAAQ;AAC5C,MAAI,CAAC,SAAU;AAGf,MAAI,WAAW,KAAK,QAAQ,EAAG;AAG/B,QAAM,YAAY,kBAAkB,QAAQ;AAC5C,MAAI,UAAU,WAAW,QAAQ,EAAG;AAEpC,MAAI,CAAC,IAAI,UAAU;AAEjB,QAAI,UAAW;AAAA,EACjB;AAEA,QAAM,cAAcC,MAAK,QAAQ,IAAI,QAAQ,EAAE;AAC/C,MAAI,SAAS;AACb,MAAI,QAAQ;AACZ,MAAI,IAAI,UAAU;AAChB,UAAM,WAAWA,MAAK,QAAQ,IAAI,SAAS,EAAE;AAC7C,QAAIC,YAAW,QAAQ,GAAG;AACxB,eAAS,MAAM,SAAS,QAAQ;AAAA,IAClC;AAAA,EACF;AACA,MAAIA,YAAW,WAAW,GAAG;AAC3B,YAAQ,MAAM,SAAS,WAAW;AAAA,EACpC;AAIA,MAAI,WAAW,MAAO;AAEtB,QAAM,SAAS,iBAAiB,QAAQ;AACxC,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,MAAI,CAAC,QAAQ;AAIX,UAAM,aAAa,YAAY,MAAM;AACrC,UAAM,YAAY,YAAY,KAAK;AACnC,UAAM,SAAS,YAAY,YAAY,SAAS;AAChD,YAAQ,OAAO;AACf,YAAQ,OAAO;AACf,cAAU,OAAO;AAAA,EACnB;AAEA,QAAM,SAAS,eAAe,IAAI,QAAQ,MAAM;AAChD,aAAW;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,IAAI,QAAQ,aAAa,KAAK,IAAI;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEA,SAAS,eAAe,KAAuC;AAC7D,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,IAAI,IAAI,YAAY;AAC1B,MAAI,EAAE,SAAS,UAAU,KAAK,EAAE,SAAS,IAAI,KAAK,EAAE,SAAS,MAAM,EAAG,QAAO;AAC7E,SAAO;AACT;AAEA,eAAe,SAAS,MAA+B;AACrD,MAAI;AACF,WAAO,MAAMC,UAAS,MAAM,MAAM;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC7C;AAEA,IAAM,aACJ;;;AMzLF,SAAS,cAAc;AAKvB,IAAM,UAAU,IAAI,KAAK;AACzB,IAAM,aAAa;AASZ,IAAM,uBAAN,MAA2B;AAAA,EACxB,OAAO,oBAAI,IAAyB;AAAA,EAE5C,OAAO,UAAgE;AACrE,UAAM,MAAM,SAAS,aAAa;AAClC,UAAMC,MAAK,SAAS;AACpB,UAAM,MAAM,SAAS,aAAa;AAElC,QAAI,UAAU,KAAK,KAAK,IAAI,GAAG;AAC/B,QAAI,QAAQ;AACZ,QAAI,CAAC,WAAWA,MAAK,QAAQ,SAAS,SAAS;AAE7C,UAAI,QAAS,MAAK,aAAa,SAAS,QAAQ,MAAM;AACtD,gBAAU;AAAA,QACR,IAAI,OAAO,OAAO,EAAE,CAAC;AAAA,QACrB,WAAWA;AAAA,QACX,QAAQA;AAAA,QACR;AAAA,MACF;AACA,WAAK,KAAK,IAAI,KAAK,OAAO;AAC1B,cAAQ;AACR,oBAAc;AAAA,QACZ,IAAI,QAAQ;AAAA,QACZ,MAAM;AAAA,QACN,WAAWA;AAAA,QACX;AAAA,MACF,CAAC;AAAA,IACH,OAAO;AACL,cAAQ,SAASA;AAAA,IACnB;AAEA,UAAM,QAAoB;AAAA,MACxB,IAAI,OAAO;AAAA,MACX,WAAW,QAAQ;AAAA,MACnB,MAAM;AAAA,MACN,MAAM;AAAA,MACN,IAAAA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,QACP,WAAW;AAAA,QACX,WAAW,SAAS;AAAA,QACpB,OAAO,SAAS;AAAA,QAChB,SAAS,SAAS;AAAA,QAClB,QAAQ,SAAS;AAAA,QACjB,QAAQ,SAAS;AAAA,QACjB,OAAO,SAAS;AAAA,MAClB;AAAA,IACF;AACA,gBAAY,KAAK;AACjB,WAAO,EAAE,WAAW,QAAQ,IAAI,MAAM;AAAA,EACxC;AAAA;AAAA,EAGA,WAAiB;AACf,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,KAAK,KAAK,KAAK,OAAO,EAAG,MAAK,aAAa,GAAG,KAAK,IAAI,EAAE,QAAQ,GAAG,CAAC;AAChF,SAAK,KAAK,MAAM;AAAA,EAClB;AAAA,EAEQ,aAAa,GAAgB,SAAiB;AACpD,kBAAc;AAAA,MACZ,IAAI,EAAE;AAAA,MACN,MAAM;AAAA,MACN,WAAW,EAAE;AAAA,MACb;AAAA,MACA,KAAK,EAAE;AAAA,IACT,CAAC;AAAA,EACH;AACF;;;AR7EA;AAGA,eAAsB,eAA8B;AAClD,qBAAmB;AAEnB,QAAM;AAEN,QAAM,MAAM,iBAAiB;AAC7B,MAAI,CAACC,YAAW,GAAG,GAAG;AACpB,YAAQ,MAAMC,IAAG,IAAI,4CAAuC,CAAC;AAC7D,YAAQ,MAAM,OAAO,GAAG;AACxB,YAAQ,MAAM;AACd,YAAQ,MAAMA,IAAG,IAAI,sDAAsD,CAAC;AAC5E,YAAQ,MAAMA,IAAG,IAAI,yDAAyD,CAAC;AAC/E,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAIA,IAAG,KAAKA,IAAG,KAAK,iCAA8B,CAAC,CAAC;AAC5D,UAAQ,IAAIA,IAAG,IAAI,aAAa,IAAI,GAAG;AACvC,UAAQ,IAAIA,IAAG,IAAI,0BAA0B,CAAC;AAE9C,QAAM,WAAW,IAAI,qBAAqB;AAE1C,QAAM,UAAU,mBAAmB,KAAK,OAAO,SAAS;AACtD,UAAM,EAAE,WAAW,MAAM,IAAI,SAAS,OAAO,IAAI;AACjD,UAAMC,MAAK,IAAI,KAAK,KAAK,SAAS,EAAE,mBAAmB;AACvD,UAAM,KAAK,KAAK,YAAYC,UAAS,KAAK,SAAS,IAAIF,IAAG,IAAI,cAAc;AAC5E,UAAM,OAAO,KAAK,SAAS,MAAM,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,GAAG;AACxD,UAAM,cACJ,KAAK,WAAW,cACZA,IAAG,QAAQ,IAAI,IACf,KAAK,WAAW,kBACdA,IAAG,KAAK,KAAK,IACbA,IAAG,IAAI,GAAG;AAClB,UAAM,QAAQ,KAAK,SACfA,IAAG,IAAI,QAAQ,IACf,GAAGA,IAAG,MAAM,MAAM,KAAK,KAAK,CAAC,IAAIA,IAAG,IAAI,MAAM,KAAK,OAAO,CAAC;AAC/D,QAAI,OAAO;AACT,cAAQ;AAAA,QACN,GAAGA,IAAG,IAAIC,GAAE,CAAC,KAAKD,IAAG,OAAO,SAAS,CAAC,KAAKA,IAAG,IAAI,SAAS,CAAC,KAAK,EAAE;AAAA,MACrE;AAAA,IACF;AACA,YAAQ,IAAI,GAAGA,IAAG,IAAIC,GAAE,CAAC,cAAc,WAAW,KAAK,KAAK,OAAO,EAAE,CAAC,KAAK,KAAK,EAAE;AAAA,EACpF,CAAC;AAGD,QAAM,WAAW,YAAY;AAC3B,YAAQ,IAAID,IAAG,IAAI,4BAAuB,CAAC;AAC3C,aAAS,SAAS;AAClB,UAAM,QAAQ,MAAM;AACpB,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,GAAG,UAAU,QAAQ;AAC7B,UAAQ,GAAG,WAAW,QAAQ;AAChC;;;AS7DA,OAAOG,SAAQ;AAKf,eAAsB,cAA6B;AACjD,QAAM,MAAM,WAAW;AACvB,MAAI,CAAC,IAAI,QAAQ;AACf,YAAQ,MAAMC,IAAG,IAAI,yBAAoB,IAAIA,IAAG,KAAK,0BAA0B,CAAC;AAChF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,gBAAgB;AACpB,MAAI,cAAc;AAGlB,aAAS;AACP,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,UAAU;AAAA,IACxB,SAAS,KAAK;AACZ,YAAM,IAAI;AACV,UAAI,eAAe,aAAa;AAC9B,gBAAQ,MAAMA,IAAG,IAAI,SAAI,IAAI,EAAE,OAAO;AACtC,YAAI,IAAI,aAAa;AACnB,kBAAQ;AAAA,YACNA,IAAG,IAAI,uFAAkF;AAAA,UAC3F;AAAA,QACF;AAAA,MACF,OAAO;AACL,gBAAQ,MAAMA,IAAG,IAAI,SAAI,IAAI,EAAE,OAAO;AAAA,MACxC;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,qBAAiB,IAAI;AACrB,mBAAe,IAAI;AACnB,QAAI,CAAC,IAAI,cAAe;AAAA,EAC1B;AAEA,MAAI,gBAAgB,GAAG;AACrB,YAAQ,IAAIA,IAAG,MAAM,QAAG,IAAI,2CAAsC;AAClE;AAAA,EACF;AACA,UAAQ;AAAA,IACNA,IAAG,MAAM,QAAG,IACV,aAAa,WAAW,gBAAa,aAAa;AAAA,EACtD;AACF;;;AC7CA;AAFA,OAAOC,SAAQ;AACf,SAAS,cAAAC,aAAY,gBAAAC,eAAc,iBAAAC,gBAAe,kBAAkB;AAQpE,IAAM,mBAAmB;AACzB,IAAM,oBAAoB;AAE1B,eAAsB,gBAA+B;AACnD,QAAM,MAAM,WAAW;AACvB,MAAI,CAAC,IAAI,QAAQ;AACf,YAAQ,MAAMC,IAAG,IAAI,yBAAoB,IAAIA,IAAG,KAAK,0BAA0B,CAAC;AAChF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,CAAC,aAAa,GAAG;AACnB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI;AACF,iBAAW,eAAe;AAAA,IAC5B,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,WAAW;AACf,QAAM,OAAO,MAAM;AACjB,QAAI,SAAU;AACd,eAAW;AACX,YAAQ,IAAIA,IAAG,IAAI,2BAAsB,CAAC;AAC1C,YAAQ;AACR,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,GAAG,UAAU,IAAI;AACzB,UAAQ,GAAG,WAAW,IAAI;AAC1B,UAAQ,GAAG,cAAc,OAAO;AAEhC,UAAQ,IAAIA,IAAG,MAAM,QAAG,IAAI,2BAA2B;AACvD,UAAQ,IAAIA,IAAG,IAAI,UAAU,IAAI,UAAU,EAAE,CAAC;AAC9C,UAAQ,IAAIA,IAAG,IAAI,iBAAiB,mBAAmB,GAAI,sBAAsB,oBAAoB,MAAM,QAAQ,CAAC,CAAC,KAAK,CAAC;AAC3H,UAAQ,IAAIA,IAAG,IAAI,kBAAkB,CAAC;AAMtC,aAAS;AACP,QAAI,SAAU;AAGd,UAAM,SAAS,cAAc;AAC7B,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,SAAS,KAAK;AAChB,YAAM,mBAAmB,SAAS,KAAK,MAAM,QAAQ;AACrD;AAAA,IACF;AAGA,QAAI;AACF,iBAAS;AACP,cAAM,MAAM,MAAM,UAAU;AAC5B,YAAI,IAAI,iBAAiB,GAAG;AAC1B,kBAAQ;AAAA,YACNA,IAAG,IAAI,MAAM,GAAG,CAAC,GAAG,IAClBA,IAAG,MAAM,SAAI,IACb,IAAI,IAAI,cAAc,gBAAa,IAAI,gBAAgB;AAAA,UAC3D;AAAA,QACF;AACA,YAAI,CAAC,IAAI,cAAe;AAAA,MAC1B;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,IAAI;AACV,UAAI,eAAe,eAAe,IAAI,aAAa;AACjD,gBAAQ;AAAA,UACNA,IAAG,IAAI,MAAM,GAAG,CAAC,GAAG,IAAIA,IAAG,IAAI,UAAK,IAAI,EAAE,UAAUA,IAAG,IAAI,iBAAiB;AAAA,QAC9E;AAAA,MACF,OAAO;AACL,gBAAQ,MAAMA,IAAG,IAAI,MAAM,GAAG,CAAC,GAAG,IAAIA,IAAG,OAAO,QAAK,IAAI,EAAE,OAAO;AAAA,MACpE;AAAA,IAEF;AAGA,UAAM,sBAAsB,kBAAkB,mBAAmB,MAAM,QAAQ;AAAA,EACjF;AACF;AAEA,SAAS,eAAwB;AAC/B,qBAAmB;AACnB,MAAIC,YAAW,eAAe,GAAG;AAC/B,UAAM,MAAMC,cAAa,iBAAiB,MAAM,EAAE,KAAK;AACvD,UAAM,MAAM,OAAO,GAAG;AACtB,QAAI,OAAO,SAAS,GAAG,KAAK,MAAM,KAAK,QAAQ,GAAG,GAAG;AACnD,cAAQ,MAAMF,IAAG,IAAI,sCAAiC,GAAG,GAAG,CAAC;AAC7D,cAAQ,MAAMA,IAAG,IAAI,8BAA8B,eAAe,EAAE,CAAC;AACrE,aAAO;AAAA,IACT;AAAA,EAEF;AACA,EAAAG,eAAc,iBAAiB,OAAO,QAAQ,GAAG,GAAG,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AACrF,SAAO;AACT;AAEA,SAAS,QAAQ,KAAsB;AACrC,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,mBAAmB,IAAY,YAA0C;AAChF,SAAO,IAAI,QAAQ,CAACC,aAAY;AAC9B,QAAI,MAAM,EAAG,QAAOA,SAAQ;AAC5B,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,IAAI,YAAY,MAAM;AAC1B,UAAI,WAAW,KAAK,KAAK,IAAI,IAAI,SAAS,IAAI;AAC5C,sBAAc,CAAC;AACf,QAAAA,SAAQ;AAAA,MACV;AAAA,IACF,GAAG,GAAG;AAAA,EACR,CAAC;AACH;AAEA,SAAS,sBACP,QACA,eACA,YACe;AACf,SAAO,IAAI,QAAQ,CAACA,aAAY;AAC9B,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,IAAI,YAAY,MAAM;AAC1B,UAAI,WAAW,GAAG;AAChB,sBAAc,CAAC;AACf,eAAOA,SAAQ;AAAA,MACjB;AACA,UAAI,KAAK,IAAI,IAAI,SAAS,QAAQ;AAChC,sBAAc,CAAC;AACf,eAAOA,SAAQ;AAAA,MACjB;AAEA,UAAI;AACF,YAAI,gBAAgB,KAAK,eAAe;AACtC,wBAAc,CAAC;AACf,iBAAOA,SAAQ;AAAA,QACjB;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF,GAAG,GAAK;AAAA,EACV,CAAC;AACH;AAEA,SAAS,KAAa;AACpB,QAAM,IAAI,oBAAI,KAAK;AACnB,SAAO,EAAE,aAAa,EAAE,MAAM,GAAG,CAAC;AACpC;;;ACnKA,SAAS,UAAAC,eAAc;AACvB,SAAS,sBAAsB;AAG/B;AAGA,IAAM,qBAAgD;AAAA,EACpD,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,cAAc;AAAA,EACd,MAAM;AAAA,EACN,cAAc;AAAA,EACd,YAAY;AACd;AAEA,eAAe,YAA6B;AAC1C,MAAI,QAAQ,MAAM,MAAO,QAAO;AAChC,QAAM,SAAmB,CAAC;AAC1B,mBAAiB,SAAS,QAAQ,OAAO;AACvC,WAAO,KAAK,KAAe;AAAA,EAC7B;AACA,SAAO,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM;AAC9C;AAEA,SAAS,SAAS,KAAoB;AACpC,MAAI;AACF,uBAAmB;AACnB,UAAM,OAAO,KAAI,oBAAI,KAAK,GAAE,YAAY,CAAC,iBACvC,eAAe,QAAQ,IAAI,SAAS,IAAI,UAAU,OAAO,GAAG,CAC9D;AAAA;AACA,mBAAe,UAAU,IAAI;AAAA,EAC/B,QAAQ;AAAA,EAER;AACF;AAEA,eAAsB,QAAQ,UAAkC;AAE9D,MAAI;AACF,UAAM,MAAM,MAAM,UAAU;AAC5B,QAAI,CAAC,IAAI,KAAK,EAAG;AACjB,UAAM,QAAQ,KAAK,MAAM,GAAG;AAE5B,UAAM,gBAAgB,MAAM,mBAAmB,YAAY;AAC3D,UAAM,OAAkB,mBAAmB,aAAa,KAAK;AAC7D,UAAMC,MAAK,KAAK,IAAI;AACpB,UAAM,YAAY,MAAM,cAAc;AAEtC,QAAI,SAAS,iBAAiB;AAC5B,oBAAc;AAAA,QACZ,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,WAAWA;AAAA,QACX,KAAK,MAAM;AAAA,MACb,CAAC;AAAA,IACH,WAAW,SAAS,eAAe;AACjC,oBAAc;AAAA,QACZ,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,WAAWA;AAAA,QACX,SAASA;AAAA,QACT,KAAK,MAAM;AAAA,MACb,CAAC;AAAA,IACH,OAAO;AAEL,oBAAc;AAAA,QACZ,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,WAAWA;AAAA,QACX,KAAK,MAAM;AAAA,MACb,CAAC;AAAA,IACH;AAKA,UAAM,cAAc,SAAS,KAAK;AAClC,UAAM,QAAoB;AAAA,MACxB,IAAIC,QAAO;AAAA,MACX;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA,IAAAD;AAAA,MACA,KAAK,MAAM;AAAA,MACX,SAAS;AAAA,IACX;AACA,gBAAY,KAAK;AAAA,EACnB,SAAS,KAAK;AACZ,aAAS,GAAG;AAAA,EACd;AACF;;;ApBxFA,IAAM,UACJ,OAA4C,UAAwB;AAQtE,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,WAAW,EAChB,YAAY,kEAA6D,EACzE,QAAQ,OAAO;AAElB,QACG,QAAQ,MAAM,EACd,YAAY,8DAA8D,EAC1E,OAAO,YAAY;AAClB,QAAM,YAAY;AACpB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,yDAAyD,EACrE,OAAO,YAAY;AAClB,QAAM,cAAc;AACtB,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,0DAA0D,EACtE,OAAO,YAAY;AAClB,QAAM,aAAa;AACrB,CAAC;AAEH,QACG,QAAQ,gBAAgB,EACxB,YAAY,iDAAiD,EAC7D,OAAO,eAAe,+DAA+D,EACrF,OAAO,OAAO,QAA4B,SAA2B;AACpE,QAAM,YAAY,QAAQ,IAAI;AAChC,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,wCAAwC,EACpD,OAAO,YAAY;AAClB,QAAM,YAAY;AACpB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,+EAA+E,EAC3F,OAAO,YAAY;AAClB,QAAM,cAAc;AACtB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,yBAAyB,EACrC,OAAO,YAAY;AAClB,QAAM,cAAc;AACtB,CAAC;AAEH,QACG,QAAQ,WAAW,EACnB,YAAY,qDAAqD,EACjE,OAAO,YAAY;AAClB,QAAM,iBAAiB;AACzB,CAAC;AAEH,QACG,QAAQ,cAAc,EACtB,YAAY,uEAAuE,EACnF,OAAO,OAAO,UAAkB;AAC/B,QAAM,QAAQ,KAAK;AACrB,CAAC;AAEH,QAAQ,WAAW,QAAQ,IAAI,EAAE,MAAM,CAAC,QAAQ;AAG9C,QAAM,MAAM,QAAQ,KAAK,CAAC;AAC1B,MAAI,QAAQ,QAAQ;AAClB,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,MAAM,GAAG;AACjB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["mkdirSync","readFileSync","writeFileSync","existsSync","pc","existsSync","readFileSync","existsSync","readFileSync","pc","ts","pc","pc","uninstallClaudeCodeHooks","resolve","pc","existsSync","basename","homedir","join","readFile","existsSync","dirname","join","existsSync","join","dirname","sep","fileURLToPath","existsSync","readFileSync","statSync","join","sep","dirname","join","existsSync","readFile","ts","existsSync","pc","ts","basename","pc","pc","pc","existsSync","readFileSync","writeFileSync","pc","existsSync","readFileSync","writeFileSync","resolve","nanoid","ts","nanoid"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentreel/agent",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Loom for AI coding sessions — captures every Claude Code and Cursor session locally and ships it to a scrubbable, shareable replay.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -13,13 +13,13 @@
13
13
  "developer-tools",
14
14
  "agentreel"
15
15
  ],
16
- "homepage": "https://agentreel.dev",
16
+ "homepage": "https://github.com/agentreel/agent#readme",
17
17
  "bugs": {
18
- "url": "https://github.com/agentreel/agentreel/issues"
18
+ "url": "https://github.com/agentreel/agent/issues"
19
19
  },
20
20
  "repository": {
21
21
  "type": "git",
22
- "url": "git+https://github.com/agentreel/agentreel.git",
22
+ "url": "git+https://github.com/agentreel/agent.git",
23
23
  "directory": "apps/local-agent"
24
24
  },
25
25
  "license": "MIT",