chat-logbook 0.4.0 → 0.4.1

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/api/dist/index.js CHANGED
@@ -1,9 +1,15 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
1
7
  // src/index.ts
2
- import fs4 from "fs";
8
+ import fs6 from "fs";
3
9
  import os from "os";
4
- import path3 from "path";
10
+ import path4 from "path";
5
11
  import { exec } from "child_process";
6
- import { fileURLToPath as fileURLToPath2 } from "url";
12
+ import { fileURLToPath as fileURLToPath3 } from "url";
7
13
 
8
14
  // ../node_modules/.pnpm/@hono+node-server@1.19.11_hono@4.12.9/node_modules/@hono/node-server/dist/index.mjs
9
15
  import { createServer as createServerHTTP } from "http";
@@ -699,26 +705,26 @@ var handleParsingNestedValues = (form, key, value) => {
699
705
  };
700
706
 
701
707
  // ../node_modules/.pnpm/hono@4.12.9/node_modules/hono/dist/utils/url.js
702
- var splitPath = (path4) => {
703
- const paths = path4.split("/");
708
+ var splitPath = (path5) => {
709
+ const paths = path5.split("/");
704
710
  if (paths[0] === "") {
705
711
  paths.shift();
706
712
  }
707
713
  return paths;
708
714
  };
709
715
  var splitRoutingPath = (routePath) => {
710
- const { groups, path: path4 } = extractGroupsFromPath(routePath);
711
- const paths = splitPath(path4);
716
+ const { groups, path: path5 } = extractGroupsFromPath(routePath);
717
+ const paths = splitPath(path5);
712
718
  return replaceGroupMarks(paths, groups);
713
719
  };
714
- var extractGroupsFromPath = (path4) => {
720
+ var extractGroupsFromPath = (path5) => {
715
721
  const groups = [];
716
- path4 = path4.replace(/\{[^}]+\}/g, (match2, index) => {
722
+ path5 = path5.replace(/\{[^}]+\}/g, (match2, index) => {
717
723
  const mark = `@${index}`;
718
724
  groups.push([mark, match2]);
719
725
  return mark;
720
726
  });
721
- return { groups, path: path4 };
727
+ return { groups, path: path5 };
722
728
  };
723
729
  var replaceGroupMarks = (paths, groups) => {
724
730
  for (let i = groups.length - 1; i >= 0; i--) {
@@ -775,8 +781,8 @@ var getPath = (request) => {
775
781
  const queryIndex = url.indexOf("?", i);
776
782
  const hashIndex = url.indexOf("#", i);
777
783
  const end = queryIndex === -1 ? hashIndex === -1 ? void 0 : hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex);
778
- const path4 = url.slice(start, end);
779
- return tryDecodeURI(path4.includes("%25") ? path4.replace(/%25/g, "%2525") : path4);
784
+ const path5 = url.slice(start, end);
785
+ return tryDecodeURI(path5.includes("%25") ? path5.replace(/%25/g, "%2525") : path5);
780
786
  } else if (charCode === 63 || charCode === 35) {
781
787
  break;
782
788
  }
@@ -793,11 +799,11 @@ var mergePath = (base, sub, ...rest) => {
793
799
  }
794
800
  return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`;
795
801
  };
796
- var checkOptionalParameter = (path4) => {
797
- if (path4.charCodeAt(path4.length - 1) !== 63 || !path4.includes(":")) {
802
+ var checkOptionalParameter = (path5) => {
803
+ if (path5.charCodeAt(path5.length - 1) !== 63 || !path5.includes(":")) {
798
804
  return null;
799
805
  }
800
- const segments = path4.split("/");
806
+ const segments = path5.split("/");
801
807
  const results = [];
802
808
  let basePath = "";
803
809
  segments.forEach((segment) => {
@@ -938,9 +944,9 @@ var HonoRequest = class {
938
944
  */
939
945
  path;
940
946
  bodyCache = {};
941
- constructor(request, path4 = "/", matchResult = [[]]) {
947
+ constructor(request, path5 = "/", matchResult = [[]]) {
942
948
  this.raw = request;
943
- this.path = path4;
949
+ this.path = path5;
944
950
  this.#matchResult = matchResult;
945
951
  this.#validatedData = {};
946
952
  }
@@ -1677,8 +1683,8 @@ var Hono = class _Hono {
1677
1683
  return this;
1678
1684
  };
1679
1685
  });
1680
- this.on = (method, path4, ...handlers) => {
1681
- for (const p of [path4].flat()) {
1686
+ this.on = (method, path5, ...handlers) => {
1687
+ for (const p of [path5].flat()) {
1682
1688
  this.#path = p;
1683
1689
  for (const m of [method].flat()) {
1684
1690
  handlers.map((handler) => {
@@ -1735,8 +1741,8 @@ var Hono = class _Hono {
1735
1741
  * app.route("/api", app2) // GET /api/user
1736
1742
  * ```
1737
1743
  */
1738
- route(path4, app2) {
1739
- const subApp = this.basePath(path4);
1744
+ route(path5, app2) {
1745
+ const subApp = this.basePath(path5);
1740
1746
  app2.routes.map((r) => {
1741
1747
  let handler;
1742
1748
  if (app2.errorHandler === errorHandler) {
@@ -1762,9 +1768,9 @@ var Hono = class _Hono {
1762
1768
  * const api = new Hono().basePath('/api')
1763
1769
  * ```
1764
1770
  */
1765
- basePath(path4) {
1771
+ basePath(path5) {
1766
1772
  const subApp = this.#clone();
1767
- subApp._basePath = mergePath(this._basePath, path4);
1773
+ subApp._basePath = mergePath(this._basePath, path5);
1768
1774
  return subApp;
1769
1775
  }
1770
1776
  /**
@@ -1838,7 +1844,7 @@ var Hono = class _Hono {
1838
1844
  * })
1839
1845
  * ```
1840
1846
  */
1841
- mount(path4, applicationHandler, options) {
1847
+ mount(path5, applicationHandler, options) {
1842
1848
  let replaceRequest;
1843
1849
  let optionHandler;
1844
1850
  if (options) {
@@ -1865,7 +1871,7 @@ var Hono = class _Hono {
1865
1871
  return [c.env, executionContext];
1866
1872
  };
1867
1873
  replaceRequest ||= (() => {
1868
- const mergedPath = mergePath(this._basePath, path4);
1874
+ const mergedPath = mergePath(this._basePath, path5);
1869
1875
  const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
1870
1876
  return (request) => {
1871
1877
  const url = new URL(request.url);
@@ -1880,14 +1886,14 @@ var Hono = class _Hono {
1880
1886
  }
1881
1887
  await next();
1882
1888
  };
1883
- this.#addRoute(METHOD_NAME_ALL, mergePath(path4, "*"), handler);
1889
+ this.#addRoute(METHOD_NAME_ALL, mergePath(path5, "*"), handler);
1884
1890
  return this;
1885
1891
  }
1886
- #addRoute(method, path4, handler) {
1892
+ #addRoute(method, path5, handler) {
1887
1893
  method = method.toUpperCase();
1888
- path4 = mergePath(this._basePath, path4);
1889
- const r = { basePath: this._basePath, path: path4, method, handler };
1890
- this.router.add(method, path4, [handler, r]);
1894
+ path5 = mergePath(this._basePath, path5);
1895
+ const r = { basePath: this._basePath, path: path5, method, handler };
1896
+ this.router.add(method, path5, [handler, r]);
1891
1897
  this.routes.push(r);
1892
1898
  }
1893
1899
  #handleError(err, c) {
@@ -1900,10 +1906,10 @@ var Hono = class _Hono {
1900
1906
  if (method === "HEAD") {
1901
1907
  return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))();
1902
1908
  }
1903
- const path4 = this.getPath(request, { env });
1904
- const matchResult = this.router.match(method, path4);
1909
+ const path5 = this.getPath(request, { env });
1910
+ const matchResult = this.router.match(method, path5);
1905
1911
  const c = new Context(request, {
1906
- path: path4,
1912
+ path: path5,
1907
1913
  matchResult,
1908
1914
  env,
1909
1915
  executionCtx,
@@ -2003,7 +2009,7 @@ var Hono = class _Hono {
2003
2009
 
2004
2010
  // ../node_modules/.pnpm/hono@4.12.9/node_modules/hono/dist/router/reg-exp-router/matcher.js
2005
2011
  var emptyParam = [];
2006
- function match(method, path4) {
2012
+ function match(method, path5) {
2007
2013
  const matchers = this.buildAllMatchers();
2008
2014
  const match2 = ((method2, path22) => {
2009
2015
  const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
@@ -2019,7 +2025,7 @@ function match(method, path4) {
2019
2025
  return [matcher[1][index], match3];
2020
2026
  });
2021
2027
  this.match = match2;
2022
- return match2(method, path4);
2028
+ return match2(method, path5);
2023
2029
  }
2024
2030
 
2025
2031
  // ../node_modules/.pnpm/hono@4.12.9/node_modules/hono/dist/router/reg-exp-router/node.js
@@ -2134,12 +2140,12 @@ var Node = class _Node {
2134
2140
  var Trie = class {
2135
2141
  #context = { varIndex: 0 };
2136
2142
  #root = new Node();
2137
- insert(path4, index, pathErrorCheckOnly) {
2143
+ insert(path5, index, pathErrorCheckOnly) {
2138
2144
  const paramAssoc = [];
2139
2145
  const groups = [];
2140
2146
  for (let i = 0; ; ) {
2141
2147
  let replaced = false;
2142
- path4 = path4.replace(/\{[^}]+\}/g, (m) => {
2148
+ path5 = path5.replace(/\{[^}]+\}/g, (m) => {
2143
2149
  const mark = `@\\${i}`;
2144
2150
  groups[i] = [mark, m];
2145
2151
  i++;
@@ -2150,7 +2156,7 @@ var Trie = class {
2150
2156
  break;
2151
2157
  }
2152
2158
  }
2153
- const tokens = path4.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
2159
+ const tokens = path5.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
2154
2160
  for (let i = groups.length - 1; i >= 0; i--) {
2155
2161
  const [mark] = groups[i];
2156
2162
  for (let j = tokens.length - 1; j >= 0; j--) {
@@ -2189,9 +2195,9 @@ var Trie = class {
2189
2195
  // ../node_modules/.pnpm/hono@4.12.9/node_modules/hono/dist/router/reg-exp-router/router.js
2190
2196
  var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
2191
2197
  var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
2192
- function buildWildcardRegExp(path4) {
2193
- return wildcardRegExpCache[path4] ??= new RegExp(
2194
- path4 === "*" ? "" : `^${path4.replace(
2198
+ function buildWildcardRegExp(path5) {
2199
+ return wildcardRegExpCache[path5] ??= new RegExp(
2200
+ path5 === "*" ? "" : `^${path5.replace(
2195
2201
  /\/\*$|([.\\+*[^\]$()])/g,
2196
2202
  (_, metaChar) => metaChar ? `\\${metaChar}` : "(?:|/.*)"
2197
2203
  )}$`
@@ -2213,17 +2219,17 @@ function buildMatcherFromPreprocessedRoutes(routes) {
2213
2219
  );
2214
2220
  const staticMap = /* @__PURE__ */ Object.create(null);
2215
2221
  for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) {
2216
- const [pathErrorCheckOnly, path4, handlers] = routesWithStaticPathFlag[i];
2222
+ const [pathErrorCheckOnly, path5, handlers] = routesWithStaticPathFlag[i];
2217
2223
  if (pathErrorCheckOnly) {
2218
- staticMap[path4] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
2224
+ staticMap[path5] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
2219
2225
  } else {
2220
2226
  j++;
2221
2227
  }
2222
2228
  let paramAssoc;
2223
2229
  try {
2224
- paramAssoc = trie.insert(path4, j, pathErrorCheckOnly);
2230
+ paramAssoc = trie.insert(path5, j, pathErrorCheckOnly);
2225
2231
  } catch (e) {
2226
- throw e === PATH_ERROR ? new UnsupportedPathError(path4) : e;
2232
+ throw e === PATH_ERROR ? new UnsupportedPathError(path5) : e;
2227
2233
  }
2228
2234
  if (pathErrorCheckOnly) {
2229
2235
  continue;
@@ -2257,12 +2263,12 @@ function buildMatcherFromPreprocessedRoutes(routes) {
2257
2263
  }
2258
2264
  return [regexp, handlerMap, staticMap];
2259
2265
  }
2260
- function findMiddleware(middleware, path4) {
2266
+ function findMiddleware(middleware, path5) {
2261
2267
  if (!middleware) {
2262
2268
  return void 0;
2263
2269
  }
2264
2270
  for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {
2265
- if (buildWildcardRegExp(k).test(path4)) {
2271
+ if (buildWildcardRegExp(k).test(path5)) {
2266
2272
  return [...middleware[k]];
2267
2273
  }
2268
2274
  }
@@ -2276,7 +2282,7 @@ var RegExpRouter = class {
2276
2282
  this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
2277
2283
  this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
2278
2284
  }
2279
- add(method, path4, handler) {
2285
+ add(method, path5, handler) {
2280
2286
  const middleware = this.#middleware;
2281
2287
  const routes = this.#routes;
2282
2288
  if (!middleware || !routes) {
@@ -2291,18 +2297,18 @@ var RegExpRouter = class {
2291
2297
  });
2292
2298
  });
2293
2299
  }
2294
- if (path4 === "/*") {
2295
- path4 = "*";
2300
+ if (path5 === "/*") {
2301
+ path5 = "*";
2296
2302
  }
2297
- const paramCount = (path4.match(/\/:/g) || []).length;
2298
- if (/\*$/.test(path4)) {
2299
- const re = buildWildcardRegExp(path4);
2303
+ const paramCount = (path5.match(/\/:/g) || []).length;
2304
+ if (/\*$/.test(path5)) {
2305
+ const re = buildWildcardRegExp(path5);
2300
2306
  if (method === METHOD_NAME_ALL) {
2301
2307
  Object.keys(middleware).forEach((m) => {
2302
- middleware[m][path4] ||= findMiddleware(middleware[m], path4) || findMiddleware(middleware[METHOD_NAME_ALL], path4) || [];
2308
+ middleware[m][path5] ||= findMiddleware(middleware[m], path5) || findMiddleware(middleware[METHOD_NAME_ALL], path5) || [];
2303
2309
  });
2304
2310
  } else {
2305
- middleware[method][path4] ||= findMiddleware(middleware[method], path4) || findMiddleware(middleware[METHOD_NAME_ALL], path4) || [];
2311
+ middleware[method][path5] ||= findMiddleware(middleware[method], path5) || findMiddleware(middleware[METHOD_NAME_ALL], path5) || [];
2306
2312
  }
2307
2313
  Object.keys(middleware).forEach((m) => {
2308
2314
  if (method === METHOD_NAME_ALL || method === m) {
@@ -2320,7 +2326,7 @@ var RegExpRouter = class {
2320
2326
  });
2321
2327
  return;
2322
2328
  }
2323
- const paths = checkOptionalParameter(path4) || [path4];
2329
+ const paths = checkOptionalParameter(path5) || [path5];
2324
2330
  for (let i = 0, len = paths.length; i < len; i++) {
2325
2331
  const path22 = paths[i];
2326
2332
  Object.keys(routes).forEach((m) => {
@@ -2347,13 +2353,13 @@ var RegExpRouter = class {
2347
2353
  const routes = [];
2348
2354
  let hasOwnRoute = method === METHOD_NAME_ALL;
2349
2355
  [this.#middleware, this.#routes].forEach((r) => {
2350
- const ownRoute = r[method] ? Object.keys(r[method]).map((path4) => [path4, r[method][path4]]) : [];
2356
+ const ownRoute = r[method] ? Object.keys(r[method]).map((path5) => [path5, r[method][path5]]) : [];
2351
2357
  if (ownRoute.length !== 0) {
2352
2358
  hasOwnRoute ||= true;
2353
2359
  routes.push(...ownRoute);
2354
2360
  } else if (method !== METHOD_NAME_ALL) {
2355
2361
  routes.push(
2356
- ...Object.keys(r[METHOD_NAME_ALL]).map((path4) => [path4, r[METHOD_NAME_ALL][path4]])
2362
+ ...Object.keys(r[METHOD_NAME_ALL]).map((path5) => [path5, r[METHOD_NAME_ALL][path5]])
2357
2363
  );
2358
2364
  }
2359
2365
  });
@@ -2373,13 +2379,13 @@ var SmartRouter = class {
2373
2379
  constructor(init) {
2374
2380
  this.#routers = init.routers;
2375
2381
  }
2376
- add(method, path4, handler) {
2382
+ add(method, path5, handler) {
2377
2383
  if (!this.#routes) {
2378
2384
  throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
2379
2385
  }
2380
- this.#routes.push([method, path4, handler]);
2386
+ this.#routes.push([method, path5, handler]);
2381
2387
  }
2382
- match(method, path4) {
2388
+ match(method, path5) {
2383
2389
  if (!this.#routes) {
2384
2390
  throw new Error("Fatal error");
2385
2391
  }
@@ -2394,7 +2400,7 @@ var SmartRouter = class {
2394
2400
  for (let i2 = 0, len2 = routes.length; i2 < len2; i2++) {
2395
2401
  router.add(...routes[i2]);
2396
2402
  }
2397
- res = router.match(method, path4);
2403
+ res = router.match(method, path5);
2398
2404
  } catch (e) {
2399
2405
  if (e instanceof UnsupportedPathError) {
2400
2406
  continue;
@@ -2444,10 +2450,10 @@ var Node2 = class _Node2 {
2444
2450
  }
2445
2451
  this.#patterns = [];
2446
2452
  }
2447
- insert(method, path4, handler) {
2453
+ insert(method, path5, handler) {
2448
2454
  this.#order = ++this.#order;
2449
2455
  let curNode = this;
2450
- const parts = splitRoutingPath(path4);
2456
+ const parts = splitRoutingPath(path5);
2451
2457
  const possibleKeys = [];
2452
2458
  for (let i = 0, len = parts.length; i < len; i++) {
2453
2459
  const p = parts[i];
@@ -2496,12 +2502,12 @@ var Node2 = class _Node2 {
2496
2502
  }
2497
2503
  }
2498
2504
  }
2499
- search(method, path4) {
2505
+ search(method, path5) {
2500
2506
  const handlerSets = [];
2501
2507
  this.#params = emptyParams;
2502
2508
  const curNode = this;
2503
2509
  let curNodes = [curNode];
2504
- const parts = splitPath(path4);
2510
+ const parts = splitPath(path5);
2505
2511
  const curNodesQueue = [];
2506
2512
  const len = parts.length;
2507
2513
  let partOffsets = null;
@@ -2543,13 +2549,13 @@ var Node2 = class _Node2 {
2543
2549
  if (matcher instanceof RegExp) {
2544
2550
  if (partOffsets === null) {
2545
2551
  partOffsets = new Array(len);
2546
- let offset = path4[0] === "/" ? 1 : 0;
2552
+ let offset = path5[0] === "/" ? 1 : 0;
2547
2553
  for (let p = 0; p < len; p++) {
2548
2554
  partOffsets[p] = offset;
2549
2555
  offset += parts[p].length + 1;
2550
2556
  }
2551
2557
  }
2552
- const restPathString = path4.substring(partOffsets[i]);
2558
+ const restPathString = path5.substring(partOffsets[i]);
2553
2559
  const m = matcher.exec(restPathString);
2554
2560
  if (m) {
2555
2561
  params[name] = m[0];
@@ -2602,18 +2608,18 @@ var TrieRouter = class {
2602
2608
  constructor() {
2603
2609
  this.#node = new Node2();
2604
2610
  }
2605
- add(method, path4, handler) {
2606
- const results = checkOptionalParameter(path4);
2611
+ add(method, path5, handler) {
2612
+ const results = checkOptionalParameter(path5);
2607
2613
  if (results) {
2608
2614
  for (let i = 0, len = results.length; i < len; i++) {
2609
2615
  this.#node.insert(method, results[i], handler);
2610
2616
  }
2611
2617
  return;
2612
2618
  }
2613
- this.#node.insert(method, path4, handler);
2619
+ this.#node.insert(method, path5, handler);
2614
2620
  }
2615
- match(method, path4) {
2616
- return this.#node.search(method, path4);
2621
+ match(method, path5) {
2622
+ return this.#node.search(method, path5);
2617
2623
  }
2618
2624
  };
2619
2625
 
@@ -2744,10 +2750,10 @@ var createStreamBody = (stream) => {
2744
2750
  });
2745
2751
  return body;
2746
2752
  };
2747
- var getStats = (path4) => {
2753
+ var getStats = (path5) => {
2748
2754
  let stats;
2749
2755
  try {
2750
- stats = statSync(path4);
2756
+ stats = statSync(path5);
2751
2757
  } catch {
2752
2758
  }
2753
2759
  return stats;
@@ -2790,21 +2796,21 @@ var serveStatic = (options = { root: "" }) => {
2790
2796
  return next();
2791
2797
  }
2792
2798
  }
2793
- let path4 = join(
2799
+ let path5 = join(
2794
2800
  root,
2795
2801
  !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename, c) : filename
2796
2802
  );
2797
- let stats = getStats(path4);
2803
+ let stats = getStats(path5);
2798
2804
  if (stats && stats.isDirectory()) {
2799
2805
  const indexFile = options.index ?? "index.html";
2800
- path4 = join(path4, indexFile);
2801
- stats = getStats(path4);
2806
+ path5 = join(path5, indexFile);
2807
+ stats = getStats(path5);
2802
2808
  }
2803
2809
  if (!stats) {
2804
- await options.onNotFound?.(path4, c);
2810
+ await options.onNotFound?.(path5, c);
2805
2811
  return next();
2806
2812
  }
2807
- const mimeType = getMimeType(path4);
2813
+ const mimeType = getMimeType(path5);
2808
2814
  c.header("Content-Type", mimeType || "application/octet-stream");
2809
2815
  if (options.precompressed && (!mimeType || COMPRESSIBLE_CONTENT_TYPE_REGEX.test(mimeType))) {
2810
2816
  const acceptEncodingSet = new Set(
@@ -2814,12 +2820,12 @@ var serveStatic = (options = { root: "" }) => {
2814
2820
  if (!acceptEncodingSet.has(encoding)) {
2815
2821
  continue;
2816
2822
  }
2817
- const precompressedStats = getStats(path4 + ENCODINGS[encoding]);
2823
+ const precompressedStats = getStats(path5 + ENCODINGS[encoding]);
2818
2824
  if (precompressedStats) {
2819
2825
  c.header("Content-Encoding", encoding);
2820
2826
  c.header("Vary", "Accept-Encoding", { append: true });
2821
2827
  stats = precompressedStats;
2822
- path4 = path4 + ENCODINGS[encoding];
2828
+ path5 = path5 + ENCODINGS[encoding];
2823
2829
  break;
2824
2830
  }
2825
2831
  }
@@ -2833,7 +2839,7 @@ var serveStatic = (options = { root: "" }) => {
2833
2839
  result = c.body(null);
2834
2840
  } else if (!range) {
2835
2841
  c.header("Content-Length", size.toString());
2836
- result = c.body(createStreamBody(createReadStream(path4)), 200);
2842
+ result = c.body(createStreamBody(createReadStream(path5)), 200);
2837
2843
  } else {
2838
2844
  c.header("Accept-Ranges", "bytes");
2839
2845
  c.header("Date", stats.birthtime.toUTCString());
@@ -2844,22 +2850,16 @@ var serveStatic = (options = { root: "" }) => {
2844
2850
  end = size - 1;
2845
2851
  }
2846
2852
  const chunksize = end - start + 1;
2847
- const stream = createReadStream(path4, { start, end });
2853
+ const stream = createReadStream(path5, { start, end });
2848
2854
  c.header("Content-Length", chunksize.toString());
2849
2855
  c.header("Content-Range", `bytes ${start}-${end}/${stats.size}`);
2850
2856
  result = c.body(createStreamBody(stream), 206);
2851
2857
  }
2852
- await options.onFound?.(path4, c);
2858
+ await options.onFound?.(path5, c);
2853
2859
  return result;
2854
2860
  };
2855
2861
  };
2856
2862
 
2857
- // src/metadata/repository.ts
2858
- import fs2 from "fs";
2859
- import path from "path";
2860
- import { fileURLToPath } from "url";
2861
- import Database from "better-sqlite3";
2862
-
2863
2863
  // ../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.9.0/node_modules/drizzle-orm/entity.js
2864
2864
  var entityKind = /* @__PURE__ */ Symbol.for("drizzle:entityKind");
2865
2865
  function is(value, type) {
@@ -4173,7 +4173,7 @@ var QueryPromise = class {
4173
4173
  function mapResultRow(columns, row, joinsNotNullableMap) {
4174
4174
  const nullifyMap = {};
4175
4175
  const result = columns.reduce(
4176
- (result2, { path: path4, field }, columnIndex) => {
4176
+ (result2, { path: path5, field }, columnIndex) => {
4177
4177
  let decoder;
4178
4178
  if (is(field, Column)) {
4179
4179
  decoder = field;
@@ -4185,8 +4185,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
4185
4185
  decoder = field.sql.decoder;
4186
4186
  }
4187
4187
  let node = result2;
4188
- for (const [pathChunkIndex, pathChunk] of path4.entries()) {
4189
- if (pathChunkIndex < path4.length - 1) {
4188
+ for (const [pathChunkIndex, pathChunk] of path5.entries()) {
4189
+ if (pathChunkIndex < path5.length - 1) {
4190
4190
  if (!(pathChunk in node)) {
4191
4191
  node[pathChunk] = {};
4192
4192
  }
@@ -4194,8 +4194,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
4194
4194
  } else {
4195
4195
  const rawValue = row[columnIndex];
4196
4196
  const value = node[pathChunk] = rawValue === null ? null : decoder.mapFromDriverValue(rawValue);
4197
- if (joinsNotNullableMap && is(field, Column) && path4.length === 2) {
4198
- const objectName = path4[0];
4197
+ if (joinsNotNullableMap && is(field, Column) && path5.length === 2) {
4198
+ const objectName = path5[0];
4199
4199
  if (!(objectName in nullifyMap)) {
4200
4200
  nullifyMap[objectName] = value === null ? getTableName(field.table) : false;
4201
4201
  } else if (typeof nullifyMap[objectName] === "string" && nullifyMap[objectName] !== getTableName(field.table)) {
@@ -4754,76 +4754,17 @@ function mapRelationalRow(tablesConfig, tableConfig, row, buildQueryResultSelect
4754
4754
  return result;
4755
4755
  }
4756
4756
 
4757
- // ../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.9.0/node_modules/drizzle-orm/better-sqlite3/driver.js
4758
- import Client from "better-sqlite3";
4759
-
4760
- // ../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.9.0/node_modules/drizzle-orm/selection-proxy.js
4761
- var SelectionProxyHandler = class _SelectionProxyHandler {
4762
- static [entityKind] = "SelectionProxyHandler";
4763
- config;
4764
- constructor(config) {
4765
- this.config = { ...config };
4766
- }
4767
- get(subquery, prop) {
4768
- if (prop === "_") {
4769
- return {
4770
- ...subquery["_"],
4771
- selectedFields: new Proxy(
4772
- subquery._.selectedFields,
4773
- this
4774
- )
4775
- };
4776
- }
4777
- if (prop === ViewBaseConfig) {
4778
- return {
4779
- ...subquery[ViewBaseConfig],
4780
- selectedFields: new Proxy(
4781
- subquery[ViewBaseConfig].selectedFields,
4782
- this
4783
- )
4784
- };
4785
- }
4786
- if (typeof prop === "symbol") {
4787
- return subquery[prop];
4788
- }
4789
- const columns = is(subquery, Subquery) ? subquery._.selectedFields : is(subquery, View) ? subquery[ViewBaseConfig].selectedFields : subquery;
4790
- const value = columns[prop];
4791
- if (is(value, SQL.Aliased)) {
4792
- if (this.config.sqlAliasedBehavior === "sql" && !value.isSelectionField) {
4793
- return value.sql;
4794
- }
4795
- const newValue = value.clone();
4796
- newValue.isSelectionField = true;
4797
- return newValue;
4798
- }
4799
- if (is(value, SQL)) {
4800
- if (this.config.sqlBehavior === "sql") {
4801
- return value;
4802
- }
4803
- throw new Error(
4804
- `You tried to reference "${prop}" field from a subquery, which is a raw SQL field, but it doesn't have an alias declared. Please add an alias to the field using ".as('alias')" method.`
4805
- );
4806
- }
4807
- if (is(value, Column)) {
4808
- if (this.config.alias) {
4809
- return new Proxy(
4810
- value,
4811
- new ColumnAliasProxyHandler(
4812
- new Proxy(
4813
- value.table,
4814
- new TableAliasProxyHandler(this.config.alias, this.config.replaceOriginalName ?? false)
4815
- )
4816
- )
4817
- );
4818
- }
4819
- return value;
4820
- }
4821
- if (typeof value !== "object" || value === null) {
4822
- return value;
4823
- }
4824
- return new Proxy(value, new _SelectionProxyHandler(this.config));
4825
- }
4826
- };
4757
+ // src/archive/schema.ts
4758
+ var schema_exports = {};
4759
+ __export(schema_exports, {
4760
+ archiveMeta: () => archiveMeta,
4761
+ ingestionEvents: () => ingestionEvents,
4762
+ messages: () => messages,
4763
+ rawMessages: () => rawMessages,
4764
+ schemaVersion: () => schemaVersion,
4765
+ sessionScanState: () => sessionScanState,
4766
+ sessions: () => sessions
4767
+ });
4827
4768
 
4828
4769
  // ../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.9.0/node_modules/drizzle-orm/sqlite-core/foreign-keys.js
4829
4770
  var ForeignKeyBuilder2 = class {
@@ -5377,6 +5318,74 @@ function text(a, b = {}) {
5377
5318
  return new SQLiteTextBuilder(name, config);
5378
5319
  }
5379
5320
 
5321
+ // ../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.9.0/node_modules/drizzle-orm/selection-proxy.js
5322
+ var SelectionProxyHandler = class _SelectionProxyHandler {
5323
+ static [entityKind] = "SelectionProxyHandler";
5324
+ config;
5325
+ constructor(config) {
5326
+ this.config = { ...config };
5327
+ }
5328
+ get(subquery, prop) {
5329
+ if (prop === "_") {
5330
+ return {
5331
+ ...subquery["_"],
5332
+ selectedFields: new Proxy(
5333
+ subquery._.selectedFields,
5334
+ this
5335
+ )
5336
+ };
5337
+ }
5338
+ if (prop === ViewBaseConfig) {
5339
+ return {
5340
+ ...subquery[ViewBaseConfig],
5341
+ selectedFields: new Proxy(
5342
+ subquery[ViewBaseConfig].selectedFields,
5343
+ this
5344
+ )
5345
+ };
5346
+ }
5347
+ if (typeof prop === "symbol") {
5348
+ return subquery[prop];
5349
+ }
5350
+ const columns = is(subquery, Subquery) ? subquery._.selectedFields : is(subquery, View) ? subquery[ViewBaseConfig].selectedFields : subquery;
5351
+ const value = columns[prop];
5352
+ if (is(value, SQL.Aliased)) {
5353
+ if (this.config.sqlAliasedBehavior === "sql" && !value.isSelectionField) {
5354
+ return value.sql;
5355
+ }
5356
+ const newValue = value.clone();
5357
+ newValue.isSelectionField = true;
5358
+ return newValue;
5359
+ }
5360
+ if (is(value, SQL)) {
5361
+ if (this.config.sqlBehavior === "sql") {
5362
+ return value;
5363
+ }
5364
+ throw new Error(
5365
+ `You tried to reference "${prop}" field from a subquery, which is a raw SQL field, but it doesn't have an alias declared. Please add an alias to the field using ".as('alias')" method.`
5366
+ );
5367
+ }
5368
+ if (is(value, Column)) {
5369
+ if (this.config.alias) {
5370
+ return new Proxy(
5371
+ value,
5372
+ new ColumnAliasProxyHandler(
5373
+ new Proxy(
5374
+ value.table,
5375
+ new TableAliasProxyHandler(this.config.alias, this.config.replaceOriginalName ?? false)
5376
+ )
5377
+ )
5378
+ );
5379
+ }
5380
+ return value;
5381
+ }
5382
+ if (typeof value !== "object" || value === null) {
5383
+ return value;
5384
+ }
5385
+ return new Proxy(value, new _SelectionProxyHandler(this.config));
5386
+ }
5387
+ };
5388
+
5380
5389
  // ../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.9.0/node_modules/drizzle-orm/sqlite-core/columns/all.js
5381
5390
  function getSQLiteColumnBuilders() {
5382
5391
  return {
@@ -5428,6 +5437,52 @@ var sqliteTable = (name, columns, extraConfig) => {
5428
5437
  return sqliteTableBase(name, columns, extraConfig);
5429
5438
  };
5430
5439
 
5440
+ // ../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.9.0/node_modules/drizzle-orm/sqlite-core/indexes.js
5441
+ var IndexBuilderOn = class {
5442
+ constructor(name, unique) {
5443
+ this.name = name;
5444
+ this.unique = unique;
5445
+ }
5446
+ static [entityKind] = "SQLiteIndexBuilderOn";
5447
+ on(...columns) {
5448
+ return new IndexBuilder(this.name, columns, this.unique);
5449
+ }
5450
+ };
5451
+ var IndexBuilder = class {
5452
+ static [entityKind] = "SQLiteIndexBuilder";
5453
+ /** @internal */
5454
+ config;
5455
+ constructor(name, columns, unique) {
5456
+ this.config = {
5457
+ name,
5458
+ columns,
5459
+ unique,
5460
+ where: void 0
5461
+ };
5462
+ }
5463
+ /**
5464
+ * Condition for partial index.
5465
+ */
5466
+ where(condition) {
5467
+ this.config.where = condition;
5468
+ return this;
5469
+ }
5470
+ /** @internal */
5471
+ build(table) {
5472
+ return new Index(this.config, table);
5473
+ }
5474
+ };
5475
+ var Index = class {
5476
+ static [entityKind] = "SQLiteIndex";
5477
+ config;
5478
+ constructor(config, table) {
5479
+ this.config = { ...config, table };
5480
+ }
5481
+ };
5482
+ function uniqueIndex(name) {
5483
+ return new IndexBuilderOn(name, true);
5484
+ }
5485
+
5431
5486
  // ../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.9.0/node_modules/drizzle-orm/sqlite-core/utils.js
5432
5487
  function extractUsedTable(table) {
5433
5488
  if (is(table, SQLiteTable)) {
@@ -8114,6 +8169,219 @@ var SQLiteTransaction = class extends BaseSQLiteDatabase {
8114
8169
  }
8115
8170
  };
8116
8171
 
8172
+ // src/archive/schema.ts
8173
+ var archiveMeta = sqliteTable("archive_meta", {
8174
+ id: integer("id").primaryKey(),
8175
+ archiveUuid: text("archive_uuid").notNull(),
8176
+ createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull()
8177
+ });
8178
+ var schemaVersion = sqliteTable("schema_version", {
8179
+ version: integer("version").primaryKey(),
8180
+ appliedAt: integer("applied_at", { mode: "timestamp_ms" }).notNull()
8181
+ });
8182
+ var sessions = sqliteTable(
8183
+ "sessions",
8184
+ {
8185
+ id: text("id").primaryKey(),
8186
+ shortCode: text("short_code").notNull().unique(),
8187
+ agent: text("agent").notNull(),
8188
+ sourceSessionId: text("source_session_id").notNull(),
8189
+ firstSeenAt: integer("first_seen_at", { mode: "timestamp_ms" }).notNull(),
8190
+ project: text("project")
8191
+ },
8192
+ (t) => [
8193
+ uniqueIndex("sessions_agent_source_idx").on(t.agent, t.sourceSessionId)
8194
+ ]
8195
+ );
8196
+ var rawMessages = sqliteTable(
8197
+ "raw_messages",
8198
+ {
8199
+ id: integer("id").primaryKey({ autoIncrement: true }),
8200
+ agent: text("agent").notNull(),
8201
+ sessionId: text("session_id").notNull(),
8202
+ sourcePath: text("source_path").notNull(),
8203
+ sourceLocator: text("source_locator").notNull(),
8204
+ rawPayload: text("raw_payload").notNull(),
8205
+ payloadHash: text("payload_hash").notNull(),
8206
+ ingestedAt: integer("ingested_at", { mode: "timestamp_ms" }).notNull()
8207
+ },
8208
+ (t) => [
8209
+ uniqueIndex("raw_messages_idem_idx").on(
8210
+ t.agent,
8211
+ t.sessionId,
8212
+ t.payloadHash
8213
+ )
8214
+ ]
8215
+ );
8216
+ var messages = sqliteTable(
8217
+ "messages",
8218
+ {
8219
+ id: integer("id").primaryKey({ autoIncrement: true }),
8220
+ agent: text("agent").notNull(),
8221
+ sessionId: text("session_id").notNull(),
8222
+ messageId: text("message_id").notNull(),
8223
+ role: text("role").notNull(),
8224
+ ts: integer("ts", { mode: "timestamp_ms" }).notNull(),
8225
+ text: text("text").notNull(),
8226
+ blocks: text("blocks", { mode: "json" }).notNull(),
8227
+ rawId: integer("raw_id").notNull().references(() => rawMessages.id)
8228
+ },
8229
+ (t) => [
8230
+ uniqueIndex("messages_canonical_idx").on(t.agent, t.sessionId, t.messageId)
8231
+ ]
8232
+ );
8233
+ var sessionScanState = sqliteTable(
8234
+ "session_scan_state",
8235
+ {
8236
+ id: integer("id").primaryKey({ autoIncrement: true }),
8237
+ agent: text("agent").notNull(),
8238
+ sessionId: text("session_id").notNull(),
8239
+ sourcePath: text("source_path").notNull(),
8240
+ lastMtimeMs: integer("last_mtime_ms").notNull(),
8241
+ lastSizeBytes: integer("last_size_bytes").notNull(),
8242
+ lastScannedAt: integer("last_scanned_at", {
8243
+ mode: "timestamp_ms"
8244
+ }).notNull()
8245
+ },
8246
+ (t) => [uniqueIndex("session_scan_state_idx").on(t.agent, t.sessionId)]
8247
+ );
8248
+ var ingestionEvents = sqliteTable("ingestion_events", {
8249
+ id: integer("id").primaryKey({ autoIncrement: true }),
8250
+ agent: text("agent").notNull(),
8251
+ sessionId: text("session_id").notNull(),
8252
+ sourcePath: text("source_path").notNull(),
8253
+ eventType: text("event_type").notNull(),
8254
+ detail: text("detail", { mode: "json" }).notNull(),
8255
+ observedAt: integer("observed_at", { mode: "timestamp_ms" }).notNull()
8256
+ });
8257
+
8258
+ // src/visibility.ts
8259
+ function loadSessionVisibility(metadata2, opts) {
8260
+ const trashed = new Set(metadata2.listDeletedIds());
8261
+ const showTrashed = opts.includeTrashed === true;
8262
+ return {
8263
+ isTrashed: (internalId) => trashed.has(internalId),
8264
+ isVisible: (internalId) => showTrashed || !trashed.has(internalId)
8265
+ };
8266
+ }
8267
+
8268
+ // src/app.ts
8269
+ function toApiBlock(block) {
8270
+ if (block.type === "tool_result") {
8271
+ return {
8272
+ type: "tool_result",
8273
+ tool_use_id: String(block.toolUseId ?? ""),
8274
+ content: block.content
8275
+ };
8276
+ }
8277
+ return block;
8278
+ }
8279
+ var CLAUDE_CODE_AGENT = "claude-code";
8280
+ function createApp({ archive: archive2, metadata: metadata2, webDistDir: webDistDir2 }) {
8281
+ const app2 = new Hono2();
8282
+ function findArchiveSessionBySourceId(sourceSessionId) {
8283
+ return archive2.db.select().from(sessions).where(eq(sessions.sourceSessionId, sourceSessionId)).get();
8284
+ }
8285
+ app2.get("/api/sessions", (c) => {
8286
+ const visibility = loadSessionVisibility(metadata2, {
8287
+ includeTrashed: c.req.query("includeTrashed") === "true"
8288
+ });
8289
+ const rows = archive2.db.select().from(sessions).all();
8290
+ const sessions2 = [];
8291
+ for (const row of rows) {
8292
+ if (!visibility.isVisible(row.id)) continue;
8293
+ const isDeleted = visibility.isTrashed(row.id);
8294
+ const lastMessage = archive2.db.select({ ts: messages.ts }).from(messages).where(
8295
+ and(
8296
+ eq(messages.agent, CLAUDE_CODE_AGENT),
8297
+ eq(messages.sessionId, row.sourceSessionId)
8298
+ )
8299
+ ).orderBy(desc(messages.ts)).limit(1).get();
8300
+ const session = {
8301
+ id: row.sourceSessionId,
8302
+ title: deriveTitle(archive2, metadata2, row),
8303
+ project: row.project ?? "",
8304
+ createdAt: row.firstSeenAt.getTime(),
8305
+ updatedAt: lastMessage ? lastMessage.ts.getTime() : row.firstSeenAt.getTime()
8306
+ };
8307
+ if (isDeleted) session.isDeleted = true;
8308
+ sessions2.push(session);
8309
+ }
8310
+ return c.json({ sessions: sessions2 });
8311
+ });
8312
+ app2.delete("/api/sessions/:id", (c) => {
8313
+ const sessionId = c.req.param("id");
8314
+ const row = findArchiveSessionBySourceId(sessionId);
8315
+ if (!row) {
8316
+ return c.json({ error: "Session not found" }, 404);
8317
+ }
8318
+ metadata2.softDelete(row.id);
8319
+ return c.body(null, 204);
8320
+ });
8321
+ app2.post("/api/sessions/:id/restore", (c) => {
8322
+ const sessionId = c.req.param("id");
8323
+ const row = findArchiveSessionBySourceId(sessionId);
8324
+ if (!row) {
8325
+ return c.json({ error: "Session not found" }, 404);
8326
+ }
8327
+ metadata2.restore(row.id);
8328
+ return c.body(null, 204);
8329
+ });
8330
+ app2.get("/api/sessions/:id", (c) => {
8331
+ const sessionId = c.req.param("id");
8332
+ const row = findArchiveSessionBySourceId(sessionId);
8333
+ if (!row) {
8334
+ return c.json({ error: "Session not found" }, 404);
8335
+ }
8336
+ const visibility = loadSessionVisibility(metadata2, {
8337
+ includeTrashed: c.req.query("includeTrashed") === "true"
8338
+ });
8339
+ if (!visibility.isVisible(row.id)) {
8340
+ return c.json({ error: "Session not found" }, 404);
8341
+ }
8342
+ const rows = archive2.db.select().from(messages).where(
8343
+ and(
8344
+ eq(messages.agent, CLAUDE_CODE_AGENT),
8345
+ eq(messages.sessionId, sessionId)
8346
+ )
8347
+ ).orderBy(asc(messages.ts)).all();
8348
+ const messages2 = rows.map((m) => ({
8349
+ role: m.role,
8350
+ content: m.blocks.map(toApiBlock),
8351
+ timestamp: m.ts.toISOString()
8352
+ }));
8353
+ return c.json({ messages: messages2 });
8354
+ });
8355
+ function deriveTitle(archive3, metadata3, row) {
8356
+ const custom = metadata3.getCustomTitle(row.id);
8357
+ if (custom && custom.trim()) return custom;
8358
+ const firstUser = archive3.db.select({ text: messages.text }).from(messages).where(
8359
+ and(
8360
+ eq(messages.agent, CLAUDE_CODE_AGENT),
8361
+ eq(messages.sessionId, row.sourceSessionId),
8362
+ eq(messages.role, "user")
8363
+ )
8364
+ ).orderBy(asc(messages.ts)).limit(1).get();
8365
+ const text2 = firstUser?.text?.trim().split("\n")[0]?.trim();
8366
+ return text2 && text2.length > 0 ? text2 : "Untitled";
8367
+ }
8368
+ if (webDistDir2) {
8369
+ app2.use("*", serveStatic({ root: webDistDir2 }));
8370
+ app2.use("*", serveStatic({ root: webDistDir2, path: "index.html" }));
8371
+ }
8372
+ return app2;
8373
+ }
8374
+
8375
+ // src/archive/repository.ts
8376
+ import crypto5 from "crypto";
8377
+ import fs2 from "fs";
8378
+ import path from "path";
8379
+ import { fileURLToPath } from "url";
8380
+ import Database from "better-sqlite3";
8381
+
8382
+ // ../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.9.0/node_modules/drizzle-orm/better-sqlite3/driver.js
8383
+ import Client from "better-sqlite3";
8384
+
8117
8385
  // ../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.9.0/node_modules/drizzle-orm/better-sqlite3/session.js
8118
8386
  var BetterSQLiteSession = class extends SQLiteSession {
8119
8387
  constructor(client, dialect, schema, options = {}) {
@@ -8310,202 +8578,525 @@ function migrate(db, config) {
8310
8578
  db.dialect.migrate(migrations, db.session, config);
8311
8579
  }
8312
8580
 
8581
+ // src/archive/short-code.ts
8582
+ import crypto4 from "crypto";
8583
+ var CROCKFORD_ALPHABET = "0123456789abcdefghjkmnpqrstvwxyz";
8584
+ var SHORT_CODE_LENGTH = 6;
8585
+ var MAX_RETRIES = 5;
8586
+ function generateShortCode({
8587
+ isTaken,
8588
+ randomIndex = () => crypto4.randomInt(CROCKFORD_ALPHABET.length)
8589
+ }) {
8590
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
8591
+ let code = "";
8592
+ for (let i = 0; i < SHORT_CODE_LENGTH; i++) {
8593
+ code += CROCKFORD_ALPHABET[randomIndex()];
8594
+ }
8595
+ if (!isTaken(code)) {
8596
+ return code;
8597
+ }
8598
+ }
8599
+ throw new Error(
8600
+ `Failed to generate unique short_code after ${MAX_RETRIES + 1} attempts`
8601
+ );
8602
+ }
8603
+
8604
+ // src/archive/repository.ts
8605
+ function resolveMigrationsFolder() {
8606
+ const here = path.dirname(fileURLToPath(import.meta.url));
8607
+ const candidates = [
8608
+ path.join(here, "../../drizzle/archive"),
8609
+ path.join(here, "./drizzle/archive")
8610
+ ];
8611
+ const found = candidates.find((p) => fs2.existsSync(p));
8612
+ if (!found) {
8613
+ throw new Error("Could not locate archive drizzle migrations folder");
8614
+ }
8615
+ return found;
8616
+ }
8617
+ function createArchiveRepository({
8618
+ dataDir: dataDir2
8619
+ }) {
8620
+ fs2.mkdirSync(dataDir2, { recursive: true });
8621
+ const sqlite = new Database(path.join(dataDir2, "archive.db"));
8622
+ const db = drizzle(sqlite, { schema: schema_exports });
8623
+ migrate(db, { migrationsFolder: resolveMigrationsFolder() });
8624
+ const drizzleMigrations = sqlite.prepare("SELECT id, created_at FROM __drizzle_migrations ORDER BY id ASC").all();
8625
+ for (const m of drizzleMigrations) {
8626
+ db.insert(schemaVersion).values({ version: m.id, appliedAt: new Date(m.created_at) }).onConflictDoNothing().run();
8627
+ }
8628
+ const existing = db.select().from(archiveMeta).get();
8629
+ if (!existing) {
8630
+ db.insert(archiveMeta).values({
8631
+ id: 1,
8632
+ archiveUuid: crypto5.randomUUID(),
8633
+ createdAt: /* @__PURE__ */ new Date()
8634
+ }).run();
8635
+ }
8636
+ return {
8637
+ db,
8638
+ getArchiveUuid() {
8639
+ const row = db.select().from(archiveMeta).get();
8640
+ if (!row) {
8641
+ throw new Error("archive_meta row missing after initialization");
8642
+ }
8643
+ return row.archiveUuid;
8644
+ },
8645
+ getAppliedMigrations() {
8646
+ return db.select().from(schemaVersion).all().map((r) => ({ version: r.version, appliedAt: r.appliedAt }));
8647
+ },
8648
+ generateShortCode() {
8649
+ return generateShortCode({
8650
+ isTaken: (candidate) => db.select({ id: sessions.id }).from(sessions).where(eq(sessions.shortCode, candidate)).get() !== void 0
8651
+ });
8652
+ },
8653
+ close() {
8654
+ sqlite.close();
8655
+ }
8656
+ };
8657
+ }
8658
+
8659
+ // src/metadata/repository.ts
8660
+ import fs3 from "fs";
8661
+ import path2 from "path";
8662
+ import { fileURLToPath as fileURLToPath2 } from "url";
8663
+ import Database2 from "better-sqlite3";
8664
+
8313
8665
  // src/metadata/schema.ts
8314
8666
  var sessionsMeta = sqliteTable("sessions_meta", {
8315
- sessionId: text("session_id").primaryKey(),
8667
+ id: text("id").primaryKey(),
8316
8668
  isDeleted: integer("is_deleted", { mode: "boolean" }).notNull().default(false),
8669
+ customTitle: text("custom_title"),
8317
8670
  createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
8318
8671
  updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull()
8319
8672
  });
8320
8673
 
8321
8674
  // src/metadata/repository.ts
8322
- function resolveMigrationsFolder() {
8323
- const here = path.dirname(fileURLToPath(import.meta.url));
8675
+ function resolveMigrationsFolder2() {
8676
+ const here = path2.dirname(fileURLToPath2(import.meta.url));
8324
8677
  const candidates = [
8325
- path.join(here, "../../drizzle"),
8326
- path.join(here, "./drizzle")
8678
+ path2.join(here, "../../drizzle"),
8679
+ path2.join(here, "./drizzle")
8327
8680
  ];
8328
- const found = candidates.find((p) => fs2.existsSync(p));
8681
+ const found = candidates.find((p) => fs3.existsSync(p));
8329
8682
  if (!found) {
8330
8683
  throw new Error("Could not locate drizzle migrations folder");
8331
8684
  }
8332
8685
  return found;
8333
8686
  }
8687
+ var CLAUDE_CODE_AGENT2 = "claude-code";
8334
8688
  function createMetadataRepository({
8335
- dataDir: dataDir2
8689
+ dataDir: dataDir2,
8690
+ lookupInternalId,
8691
+ ensureSession: ensureSession2
8336
8692
  }) {
8337
- fs2.mkdirSync(dataDir2, { recursive: true });
8338
- const sqlite = new Database(path.join(dataDir2, "data.db"));
8693
+ fs3.mkdirSync(dataDir2, { recursive: true });
8694
+ const sqlite = new Database2(path2.join(dataDir2, "data.db"));
8339
8695
  const db = drizzle(sqlite);
8340
- migrate(db, { migrationsFolder: resolveMigrationsFolder() });
8696
+ migrate(db, { migrationsFolder: resolveMigrationsFolder2() });
8697
+ if (lookupInternalId) {
8698
+ rekeyLegacyRows(sqlite, db, lookupInternalId, ensureSession2);
8699
+ }
8341
8700
  return {
8342
- softDelete(sessionId) {
8701
+ softDelete(internalId) {
8343
8702
  const now = /* @__PURE__ */ new Date();
8344
8703
  db.insert(sessionsMeta).values({
8345
- sessionId,
8704
+ id: internalId,
8346
8705
  isDeleted: true,
8347
8706
  createdAt: now,
8348
8707
  updatedAt: now
8349
8708
  }).onConflictDoUpdate({
8350
- target: sessionsMeta.sessionId,
8709
+ target: sessionsMeta.id,
8351
8710
  set: { isDeleted: true, updatedAt: now }
8352
8711
  }).run();
8353
8712
  },
8354
- restore(sessionId) {
8713
+ restore(internalId) {
8355
8714
  const now = /* @__PURE__ */ new Date();
8356
- db.update(sessionsMeta).set({ isDeleted: false, updatedAt: now }).where(eq(sessionsMeta.sessionId, sessionId)).run();
8715
+ db.update(sessionsMeta).set({ isDeleted: false, updatedAt: now }).where(eq(sessionsMeta.id, internalId)).run();
8357
8716
  },
8358
- isDeleted(sessionId) {
8359
- const row = db.select({ isDeleted: sessionsMeta.isDeleted }).from(sessionsMeta).where(eq(sessionsMeta.sessionId, sessionId)).get();
8717
+ isDeleted(internalId) {
8718
+ const row = db.select({ isDeleted: sessionsMeta.isDeleted }).from(sessionsMeta).where(eq(sessionsMeta.id, internalId)).get();
8360
8719
  return row?.isDeleted ?? false;
8361
8720
  },
8362
8721
  listDeletedIds() {
8363
- const rows = db.select({ sessionId: sessionsMeta.sessionId }).from(sessionsMeta).where(eq(sessionsMeta.isDeleted, true)).all();
8364
- return rows.map((r) => r.sessionId);
8722
+ const rows = db.select({ id: sessionsMeta.id }).from(sessionsMeta).where(eq(sessionsMeta.isDeleted, true)).all();
8723
+ return rows.map((r) => r.id);
8724
+ },
8725
+ getCustomTitle(internalId) {
8726
+ const row = db.select({ customTitle: sessionsMeta.customTitle }).from(sessionsMeta).where(eq(sessionsMeta.id, internalId)).get();
8727
+ return row?.customTitle ?? null;
8728
+ },
8729
+ setCustomTitle(internalId, title) {
8730
+ const now = /* @__PURE__ */ new Date();
8731
+ db.insert(sessionsMeta).values({
8732
+ id: internalId,
8733
+ customTitle: title,
8734
+ createdAt: now,
8735
+ updatedAt: now
8736
+ }).onConflictDoUpdate({
8737
+ target: sessionsMeta.id,
8738
+ set: { customTitle: title, updatedAt: now }
8739
+ }).run();
8365
8740
  }
8366
8741
  };
8367
8742
  }
8368
-
8369
- // src/parser.ts
8370
- import fs3 from "fs";
8371
- import path2 from "path";
8372
- var COMMAND_PREFIXES = ["/clear", "/init", "/compact", "/help"];
8373
- function isCommand(display) {
8374
- return COMMAND_PREFIXES.some((cmd) => display.startsWith(cmd));
8743
+ var REKEY_USER_VERSION = 4;
8744
+ function rekeyLegacyRows(sqlite, db, lookupInternalId, ensureSession2) {
8745
+ const current = sqlite.pragma("user_version", { simple: true });
8746
+ if (current >= REKEY_USER_VERSION) return;
8747
+ const rows = db.select({ id: sessionsMeta.id }).from(sessionsMeta).all();
8748
+ for (const row of rows) {
8749
+ let target = lookupInternalId(CLAUDE_CODE_AGENT2, row.id);
8750
+ if (target === null) {
8751
+ if (!ensureSession2) continue;
8752
+ target = ensureSession2(CLAUDE_CODE_AGENT2, row.id);
8753
+ }
8754
+ if (target === row.id) continue;
8755
+ db.update(sessionsMeta).set({ id: target, updatedAt: /* @__PURE__ */ new Date() }).where(eq(sessionsMeta.id, row.id)).run();
8756
+ }
8757
+ sqlite.pragma(`user_version = ${REKEY_USER_VERSION}`);
8375
8758
  }
8376
- function parseHistoryEntry(raw2) {
8377
- const obj = raw2;
8378
- return {
8379
- display: typeof obj.display === "string" ? obj.display : "",
8380
- timestamp: typeof obj.timestamp === "number" ? obj.timestamp : 0,
8381
- project: typeof obj.project === "string" ? obj.project : "",
8382
- sessionId: typeof obj.sessionId === "string" ? obj.sessionId : ""
8759
+
8760
+ // src/ingestion/ingest.ts
8761
+ import crypto6 from "crypto";
8762
+ import fs4 from "fs";
8763
+ async function runIngestion(opts) {
8764
+ const now = opts.now ?? (() => /* @__PURE__ */ new Date());
8765
+ const result = {
8766
+ scanned: 0,
8767
+ rawInserted: 0,
8768
+ canonicalUpserted: 0,
8769
+ skippedByMtime: 0
8383
8770
  };
8771
+ for (const plugin of opts.plugins) {
8772
+ for await (const ref of plugin.discover(opts.env)) {
8773
+ result.scanned += 1;
8774
+ const stat = safeStat(ref.sourcePath);
8775
+ const prior = opts.archive.db.select().from(sessionScanState).where(
8776
+ and(
8777
+ eq(sessionScanState.agent, plugin.id),
8778
+ eq(sessionScanState.sessionId, ref.sessionId)
8779
+ )
8780
+ ).get();
8781
+ ensureSession(opts.archive, plugin.id, ref.sessionId, now(), ref.project);
8782
+ if (stat && prior && prior.lastMtimeMs === stat.mtimeMs && prior.lastSizeBytes === stat.size) {
8783
+ result.skippedByMtime += 1;
8784
+ continue;
8785
+ }
8786
+ for await (const raw2 of plugin.extractRaw(ref)) {
8787
+ const payloadJson = JSON.stringify(raw2.payload);
8788
+ const payloadHash = crypto6.createHash("sha256").update(payloadJson).digest("hex");
8789
+ const existing = opts.archive.db.select({ id: rawMessages.id }).from(rawMessages).where(
8790
+ and(
8791
+ eq(rawMessages.agent, plugin.id),
8792
+ eq(rawMessages.sessionId, ref.sessionId),
8793
+ eq(rawMessages.payloadHash, payloadHash)
8794
+ )
8795
+ ).get();
8796
+ let rawId;
8797
+ if (existing) {
8798
+ rawId = existing.id;
8799
+ } else {
8800
+ const inserted = opts.archive.db.insert(rawMessages).values({
8801
+ agent: plugin.id,
8802
+ sessionId: ref.sessionId,
8803
+ sourcePath: raw2.sourcePath,
8804
+ sourceLocator: raw2.sourceLocator,
8805
+ rawPayload: payloadJson,
8806
+ payloadHash,
8807
+ ingestedAt: now()
8808
+ }).returning({ id: rawMessages.id }).get();
8809
+ rawId = inserted.id;
8810
+ result.rawInserted += 1;
8811
+ }
8812
+ const canonical = plugin.normalize(raw2);
8813
+ if (!canonical) continue;
8814
+ const upserted = upsertCanonical(
8815
+ opts.archive,
8816
+ plugin.id,
8817
+ ref.sessionId,
8818
+ canonical,
8819
+ rawId
8820
+ );
8821
+ if (upserted) result.canonicalUpserted += 1;
8822
+ }
8823
+ if (stat) {
8824
+ recordScanState(
8825
+ opts.archive,
8826
+ plugin.id,
8827
+ ref.sessionId,
8828
+ ref.sourcePath,
8829
+ stat,
8830
+ now()
8831
+ );
8832
+ }
8833
+ }
8834
+ }
8835
+ return result;
8384
8836
  }
8385
- function listSessions(claudeDir2, historyFile = "history.jsonl") {
8386
- const historyPath = path2.join(claudeDir2, historyFile);
8387
- if (!fs3.existsSync(historyPath)) {
8388
- return [];
8389
- }
8390
- const content = fs3.readFileSync(historyPath, "utf-8");
8391
- const lines = content.trim().split("\n").filter(Boolean);
8392
- const grouped = /* @__PURE__ */ new Map();
8393
- for (const line of lines) {
8394
- const entry = parseHistoryEntry(JSON.parse(line));
8395
- if (!entry.sessionId) continue;
8396
- const entries = grouped.get(entry.sessionId) ?? [];
8397
- entries.push(entry);
8398
- grouped.set(entry.sessionId, entries);
8399
- }
8400
- const sessions = [];
8401
- for (const [id, entries] of grouped) {
8402
- const sorted = entries.toSorted((a, b) => a.timestamp - b.timestamp);
8403
- const firstNonCommand = sorted.find(
8404
- (e) => e.display && !isCommand(e.display)
8405
- );
8406
- const title = firstNonCommand?.display || "Untitled";
8407
- sessions.push({
8408
- id,
8409
- title,
8410
- project: sorted[0].project,
8411
- createdAt: sorted[0].timestamp,
8412
- updatedAt: sorted[sorted.length - 1].timestamp
8413
- });
8837
+ function safeStat(p) {
8838
+ try {
8839
+ return fs4.statSync(p);
8840
+ } catch {
8841
+ return null;
8414
8842
  }
8415
- return sessions;
8416
8843
  }
8417
- var CONVERSATION_TYPES = /* @__PURE__ */ new Set(["user", "assistant"]);
8418
- function getSessionMessages(sessionPath) {
8419
- if (!fs3.existsSync(sessionPath)) {
8420
- return [];
8421
- }
8422
- const content = fs3.readFileSync(sessionPath, "utf-8");
8423
- const lines = content.trim().split("\n").filter(Boolean);
8424
- const messages = [];
8425
- for (const line of lines) {
8426
- const entry = JSON.parse(line);
8427
- if (!CONVERSATION_TYPES.has(entry.type)) continue;
8428
- if (entry.isMeta) continue;
8429
- if (entry.isSidechain) continue;
8430
- if (!entry.message) continue;
8431
- messages.push({
8432
- role: entry.message.role,
8433
- content: entry.message.content,
8434
- timestamp: entry.timestamp ?? ""
8435
- });
8844
+ function recordScanState(archive2, agent, sessionId, sourcePath, stat, scannedAt) {
8845
+ const existing = archive2.db.select({ id: sessionScanState.id }).from(sessionScanState).where(
8846
+ and(
8847
+ eq(sessionScanState.agent, agent),
8848
+ eq(sessionScanState.sessionId, sessionId)
8849
+ )
8850
+ ).get();
8851
+ if (existing) {
8852
+ archive2.db.update(sessionScanState).set({
8853
+ sourcePath,
8854
+ lastMtimeMs: stat.mtimeMs,
8855
+ lastSizeBytes: stat.size,
8856
+ lastScannedAt: scannedAt
8857
+ }).where(eq(sessionScanState.id, existing.id)).run();
8858
+ } else {
8859
+ archive2.db.insert(sessionScanState).values({
8860
+ agent,
8861
+ sessionId,
8862
+ sourcePath,
8863
+ lastMtimeMs: stat.mtimeMs,
8864
+ lastSizeBytes: stat.size,
8865
+ lastScannedAt: scannedAt
8866
+ }).run();
8436
8867
  }
8437
- return messages;
8438
8868
  }
8439
- function findSessionFile(claudeDir2, sessionId) {
8440
- const projectsDir = path2.join(claudeDir2, "projects");
8441
- if (!fs3.existsSync(projectsDir)) {
8442
- return null;
8869
+ function ensureSession(archive2, agent, sourceSessionId, firstSeenAt, project) {
8870
+ const existing = archive2.db.select().from(sessions).where(
8871
+ and(
8872
+ eq(sessions.agent, agent),
8873
+ eq(sessions.sourceSessionId, sourceSessionId)
8874
+ )
8875
+ ).get();
8876
+ if (existing) {
8877
+ if (project && existing.project !== project) {
8878
+ archive2.db.update(sessions).set({ project }).where(eq(sessions.id, existing.id)).run();
8879
+ }
8880
+ return existing.id;
8881
+ }
8882
+ const id = crypto6.randomUUID();
8883
+ const shortCode = archive2.generateShortCode();
8884
+ archive2.db.insert(sessions).values({
8885
+ id,
8886
+ shortCode,
8887
+ agent,
8888
+ sourceSessionId,
8889
+ firstSeenAt,
8890
+ project: project ?? null
8891
+ }).run();
8892
+ return id;
8893
+ }
8894
+ function upsertCanonical(archive2, agent, sessionId, msg, rawId) {
8895
+ const existing = archive2.db.select().from(messages).where(
8896
+ and(
8897
+ eq(messages.agent, agent),
8898
+ eq(messages.sessionId, sessionId),
8899
+ eq(messages.messageId, msg.messageId)
8900
+ )
8901
+ ).get();
8902
+ const ts = parseTs(msg.ts);
8903
+ if (!existing) {
8904
+ archive2.db.insert(messages).values({
8905
+ agent,
8906
+ sessionId,
8907
+ messageId: msg.messageId,
8908
+ role: msg.role,
8909
+ ts,
8910
+ text: msg.text,
8911
+ blocks: msg.blocks,
8912
+ rawId
8913
+ }).run();
8914
+ return true;
8443
8915
  }
8444
- const projectDirs = fs3.readdirSync(projectsDir, { withFileTypes: true });
8445
- for (const dir of projectDirs) {
8446
- if (!dir.isDirectory()) continue;
8447
- const candidate = path2.join(projectsDir, dir.name, `${sessionId}.jsonl`);
8448
- if (fs3.existsSync(candidate)) {
8449
- return candidate;
8450
- }
8916
+ if (ts.getTime() >= existing.ts.getTime()) {
8917
+ archive2.db.update(messages).set({
8918
+ role: msg.role,
8919
+ ts,
8920
+ text: msg.text,
8921
+ blocks: msg.blocks,
8922
+ rawId
8923
+ }).where(eq(messages.id, existing.id)).run();
8924
+ return true;
8451
8925
  }
8452
- return null;
8926
+ return false;
8927
+ }
8928
+ function parseTs(ts) {
8929
+ const d = new Date(ts);
8930
+ if (Number.isNaN(d.getTime())) return /* @__PURE__ */ new Date(0);
8931
+ return d;
8453
8932
  }
8454
8933
 
8455
- // src/app.ts
8456
- function createApp({ claudeDir: claudeDir2, dataDir: dataDir2, webDistDir: webDistDir2 }) {
8457
- const app2 = new Hono2();
8458
- const metadata = createMetadataRepository({ dataDir: dataDir2 });
8459
- app2.get("/api/sessions", (c) => {
8460
- const includeDeleted = c.req.query("includeDeleted") === "true";
8461
- const deleted = new Set(metadata.listDeletedIds());
8462
- const sessions = listSessions(claudeDir2).filter((session) => findSessionFile(claudeDir2, session.id) !== null).filter((session) => includeDeleted || !deleted.has(session.id)).map(
8463
- (session) => deleted.has(session.id) ? { ...session, isDeleted: true } : session
8464
- );
8465
- return c.json({ sessions });
8934
+ // src/ingestion/background.ts
8935
+ function startIngestionInBackground(opts, bg = {}) {
8936
+ const runner = bg.runner ?? runIngestion;
8937
+ const onError = bg.onError ?? ((err) => {
8938
+ console.error("[ingestion] background run failed:", err);
8466
8939
  });
8467
- app2.delete("/api/sessions/:id", (c) => {
8468
- const sessionId = c.req.param("id");
8469
- if (findSessionFile(claudeDir2, sessionId) === null) {
8470
- return c.json({ error: "Session not found" }, 404);
8471
- }
8472
- metadata.softDelete(sessionId);
8473
- return c.body(null, 204);
8940
+ const done = runner(opts).then((r) => r).catch((err) => {
8941
+ onError(err);
8942
+ return null;
8474
8943
  });
8475
- app2.post("/api/sessions/:id/restore", (c) => {
8476
- const sessionId = c.req.param("id");
8477
- if (findSessionFile(claudeDir2, sessionId) === null) {
8478
- return c.json({ error: "Session not found" }, 404);
8944
+ return { done };
8945
+ }
8946
+
8947
+ // src/plugins/claude-code/plugin.ts
8948
+ import fs5 from "fs";
8949
+ import path3 from "path";
8950
+ import readline from "readline";
8951
+ var ClaudeCodePlugin = class {
8952
+ id = "claude-code";
8953
+ displayName = "Claude Code";
8954
+ async *discover(env) {
8955
+ const projectsDir = path3.join(env.homeDir, ".claude", "projects");
8956
+ if (!fs5.existsSync(projectsDir)) return;
8957
+ const projects = fs5.readdirSync(projectsDir, { withFileTypes: true });
8958
+ for (const project of projects) {
8959
+ if (!project.isDirectory()) continue;
8960
+ const projectPath = path3.join(projectsDir, project.name);
8961
+ const files = fs5.readdirSync(projectPath, { withFileTypes: true });
8962
+ for (const file of files) {
8963
+ if (!file.isFile() || !file.name.endsWith(".jsonl")) continue;
8964
+ const sourcePath = path3.join(projectPath, file.name);
8965
+ const sessionId = file.name.replace(/\.jsonl$/, "");
8966
+ const cwd = readCwdFromJsonl(sourcePath);
8967
+ yield {
8968
+ sessionId,
8969
+ sourcePath,
8970
+ watchPaths: [sourcePath],
8971
+ project: cwd ? path3.basename(cwd) : void 0
8972
+ };
8973
+ }
8479
8974
  }
8480
- metadata.restore(sessionId);
8481
- return c.body(null, 204);
8482
- });
8483
- app2.get("/api/sessions/:id", (c) => {
8484
- const sessionId = c.req.param("id");
8485
- const sessionPath = findSessionFile(claudeDir2, sessionId);
8486
- if (!sessionPath) {
8487
- return c.json({ error: "Session not found" }, 404);
8975
+ }
8976
+ async *extractRaw(ref) {
8977
+ if (!fs5.existsSync(ref.sourcePath)) return;
8978
+ const stream = fs5.createReadStream(ref.sourcePath, { encoding: "utf-8" });
8979
+ const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
8980
+ let lineNo = 0;
8981
+ for await (const line of rl) {
8982
+ lineNo += 1;
8983
+ if (!line) continue;
8984
+ yield {
8985
+ sessionId: ref.sessionId,
8986
+ sourcePath: ref.sourcePath,
8987
+ sourceLocator: `L${lineNo}`,
8988
+ payload: JSON.parse(line)
8989
+ };
8488
8990
  }
8489
- const messages = getSessionMessages(sessionPath);
8490
- return c.json({ messages });
8491
- });
8492
- if (webDistDir2) {
8493
- app2.use("*", serveStatic({ root: webDistDir2 }));
8494
- app2.use("*", serveStatic({ root: webDistDir2, path: "index.html" }));
8495
8991
  }
8496
- return app2;
8992
+ normalize(raw2) {
8993
+ const payload = raw2.payload;
8994
+ if (!payload || typeof payload !== "object") return null;
8995
+ if (payload.type !== "user" && payload.type !== "assistant") return null;
8996
+ if (payload.isMeta === true) return null;
8997
+ if (payload.isSidechain === true) return null;
8998
+ const message = payload.message;
8999
+ if (!message) return null;
9000
+ const role = message.role === "assistant" ? "assistant" : "user";
9001
+ const messageId = String(payload.uuid ?? "");
9002
+ const ts = String(payload.timestamp ?? "");
9003
+ if (typeof message.content === "string") {
9004
+ const text2 = message.content;
9005
+ return {
9006
+ messageId,
9007
+ role,
9008
+ ts,
9009
+ text: text2,
9010
+ blocks: [{ type: "text", text: text2 }]
9011
+ };
9012
+ }
9013
+ if (Array.isArray(message.content)) {
9014
+ const blocks = [];
9015
+ for (const block of message.content) {
9016
+ const normalized = normalizeBlock(block);
9017
+ if (normalized) blocks.push(normalized);
9018
+ }
9019
+ const text2 = blocks.filter((b) => b.type === "text").map((b) => b.text).join("\n");
9020
+ return { messageId, role, ts, text: text2, blocks };
9021
+ }
9022
+ return null;
9023
+ }
9024
+ };
9025
+ function readCwdFromJsonl(sourcePath) {
9026
+ let fd;
9027
+ try {
9028
+ fd = fs5.openSync(sourcePath, "r");
9029
+ const buf = Buffer.alloc(64 * 1024);
9030
+ const n = fs5.readSync(fd, buf, 0, buf.length, 0);
9031
+ const head = buf.subarray(0, n).toString("utf-8");
9032
+ const lines = head.split("\n");
9033
+ if (n === buf.length) lines.pop();
9034
+ for (const line of lines) {
9035
+ if (!line) continue;
9036
+ try {
9037
+ const obj = JSON.parse(line);
9038
+ if (typeof obj.cwd === "string" && obj.cwd.length > 0) {
9039
+ return obj.cwd;
9040
+ }
9041
+ } catch {
9042
+ }
9043
+ }
9044
+ } catch {
9045
+ } finally {
9046
+ if (fd !== void 0) {
9047
+ try {
9048
+ fs5.closeSync(fd);
9049
+ } catch {
9050
+ }
9051
+ }
9052
+ }
9053
+ return void 0;
9054
+ }
9055
+ function normalizeBlock(raw2) {
9056
+ if (!raw2 || typeof raw2 !== "object") return null;
9057
+ const b = raw2;
9058
+ switch (b.type) {
9059
+ case "text":
9060
+ return { type: "text", text: String(b.text ?? "") };
9061
+ case "thinking":
9062
+ return { type: "thinking", thinking: String(b.thinking ?? "") };
9063
+ case "tool_use":
9064
+ return {
9065
+ type: "tool_use",
9066
+ id: String(b.id ?? ""),
9067
+ name: String(b.name ?? ""),
9068
+ input: b.input
9069
+ };
9070
+ case "tool_result":
9071
+ return {
9072
+ type: "tool_result",
9073
+ toolUseId: String(b.tool_use_id ?? ""),
9074
+ content: b.content
9075
+ };
9076
+ default:
9077
+ return null;
9078
+ }
8497
9079
  }
8498
9080
 
9081
+ // src/plugins/registry.ts
9082
+ var plugins = [new ClaudeCodePlugin()];
9083
+
8499
9084
  // src/index.ts
8500
- var __dirname = path3.dirname(fileURLToPath2(import.meta.url));
8501
- var pkgPath = path3.join(__dirname, "../../package.json");
8502
- var pkg = JSON.parse(fs4.readFileSync(pkgPath, "utf-8"));
9085
+ var __dirname = path4.dirname(fileURLToPath3(import.meta.url));
9086
+ var pkgPath = path4.join(__dirname, "../../package.json");
9087
+ var pkg = JSON.parse(fs6.readFileSync(pkgPath, "utf-8"));
8503
9088
  updateNotifier({ pkg }).notify();
8504
- var claudeDir = path3.join(os.homedir(), ".claude");
8505
- var dataDir = path3.join(os.homedir(), ".chat-logbook");
8506
- var webDistDir = path3.join(__dirname, "../../web/dist");
8507
- var app = createApp({ claudeDir, dataDir, webDistDir });
9089
+ var dataDir = path4.join(os.homedir(), ".chat-logbook");
9090
+ var webDistDir = path4.join(__dirname, "../../web/dist");
8508
9091
  var port = Number(process.env.PORT) || 3100;
9092
+ var archive = createArchiveRepository({ dataDir });
9093
+ var metadata = createMetadataRepository({ dataDir });
9094
+ var app = createApp({ archive, metadata, webDistDir });
9095
+ startIngestionInBackground({
9096
+ plugins,
9097
+ archive,
9098
+ env: { homeDir: os.homedir() }
9099
+ });
8509
9100
  function openBrowser(url) {
8510
9101
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
8511
9102
  exec(`${cmd} ${url}`);