chat-logbook 0.21.0 → 0.23.0

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
@@ -7,7 +7,7 @@ var __export = (target, all) => {
7
7
  // src/index.ts
8
8
  import fs8 from "fs";
9
9
  import os from "os";
10
- import path7 from "path";
10
+ import path8 from "path";
11
11
  import { exec } from "child_process";
12
12
  import { fileURLToPath as fileURLToPath2 } from "url";
13
13
 
@@ -585,6 +585,9 @@ var serve = (options, listeningListener) => {
585
585
  // src/index.ts
586
586
  import updateNotifier from "update-notifier";
587
587
 
588
+ // src/app.ts
589
+ import { createHash } from "crypto";
590
+
588
591
  // ../node_modules/.pnpm/hono@4.12.9/node_modules/hono/dist/compose.js
589
592
  var compose = (middleware, onError, onNotFound) => {
590
593
  return (context, next) => {
@@ -705,26 +708,26 @@ var handleParsingNestedValues = (form, key, value) => {
705
708
  };
706
709
 
707
710
  // ../node_modules/.pnpm/hono@4.12.9/node_modules/hono/dist/utils/url.js
708
- var splitPath = (path8) => {
709
- const paths = path8.split("/");
711
+ var splitPath = (path9) => {
712
+ const paths = path9.split("/");
710
713
  if (paths[0] === "") {
711
714
  paths.shift();
712
715
  }
713
716
  return paths;
714
717
  };
715
718
  var splitRoutingPath = (routePath) => {
716
- const { groups, path: path8 } = extractGroupsFromPath(routePath);
717
- const paths = splitPath(path8);
719
+ const { groups, path: path9 } = extractGroupsFromPath(routePath);
720
+ const paths = splitPath(path9);
718
721
  return replaceGroupMarks(paths, groups);
719
722
  };
720
- var extractGroupsFromPath = (path8) => {
723
+ var extractGroupsFromPath = (path9) => {
721
724
  const groups = [];
722
- path8 = path8.replace(/\{[^}]+\}/g, (match2, index2) => {
725
+ path9 = path9.replace(/\{[^}]+\}/g, (match2, index2) => {
723
726
  const mark = `@${index2}`;
724
727
  groups.push([mark, match2]);
725
728
  return mark;
726
729
  });
727
- return { groups, path: path8 };
730
+ return { groups, path: path9 };
728
731
  };
729
732
  var replaceGroupMarks = (paths, groups) => {
730
733
  for (let i = groups.length - 1; i >= 0; i--) {
@@ -781,8 +784,8 @@ var getPath = (request) => {
781
784
  const queryIndex = url.indexOf("?", i);
782
785
  const hashIndex = url.indexOf("#", i);
783
786
  const end = queryIndex === -1 ? hashIndex === -1 ? void 0 : hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex);
784
- const path8 = url.slice(start, end);
785
- return tryDecodeURI(path8.includes("%25") ? path8.replace(/%25/g, "%2525") : path8);
787
+ const path9 = url.slice(start, end);
788
+ return tryDecodeURI(path9.includes("%25") ? path9.replace(/%25/g, "%2525") : path9);
786
789
  } else if (charCode === 63 || charCode === 35) {
787
790
  break;
788
791
  }
@@ -799,11 +802,11 @@ var mergePath = (base, sub, ...rest) => {
799
802
  }
800
803
  return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`;
801
804
  };
802
- var checkOptionalParameter = (path8) => {
803
- if (path8.charCodeAt(path8.length - 1) !== 63 || !path8.includes(":")) {
805
+ var checkOptionalParameter = (path9) => {
806
+ if (path9.charCodeAt(path9.length - 1) !== 63 || !path9.includes(":")) {
804
807
  return null;
805
808
  }
806
- const segments = path8.split("/");
809
+ const segments = path9.split("/");
807
810
  const results = [];
808
811
  let basePath = "";
809
812
  segments.forEach((segment) => {
@@ -944,9 +947,9 @@ var HonoRequest = class {
944
947
  */
945
948
  path;
946
949
  bodyCache = {};
947
- constructor(request, path8 = "/", matchResult = [[]]) {
950
+ constructor(request, path9 = "/", matchResult = [[]]) {
948
951
  this.raw = request;
949
- this.path = path8;
952
+ this.path = path9;
950
953
  this.#matchResult = matchResult;
951
954
  this.#validatedData = {};
952
955
  }
@@ -1683,8 +1686,8 @@ var Hono = class _Hono {
1683
1686
  return this;
1684
1687
  };
1685
1688
  });
1686
- this.on = (method, path8, ...handlers) => {
1687
- for (const p of [path8].flat()) {
1689
+ this.on = (method, path9, ...handlers) => {
1690
+ for (const p of [path9].flat()) {
1688
1691
  this.#path = p;
1689
1692
  for (const m of [method].flat()) {
1690
1693
  handlers.map((handler) => {
@@ -1741,8 +1744,8 @@ var Hono = class _Hono {
1741
1744
  * app.route("/api", app2) // GET /api/user
1742
1745
  * ```
1743
1746
  */
1744
- route(path8, app2) {
1745
- const subApp = this.basePath(path8);
1747
+ route(path9, app2) {
1748
+ const subApp = this.basePath(path9);
1746
1749
  app2.routes.map((r) => {
1747
1750
  let handler;
1748
1751
  if (app2.errorHandler === errorHandler) {
@@ -1768,9 +1771,9 @@ var Hono = class _Hono {
1768
1771
  * const api = new Hono().basePath('/api')
1769
1772
  * ```
1770
1773
  */
1771
- basePath(path8) {
1774
+ basePath(path9) {
1772
1775
  const subApp = this.#clone();
1773
- subApp._basePath = mergePath(this._basePath, path8);
1776
+ subApp._basePath = mergePath(this._basePath, path9);
1774
1777
  return subApp;
1775
1778
  }
1776
1779
  /**
@@ -1844,7 +1847,7 @@ var Hono = class _Hono {
1844
1847
  * })
1845
1848
  * ```
1846
1849
  */
1847
- mount(path8, applicationHandler, options) {
1850
+ mount(path9, applicationHandler, options) {
1848
1851
  let replaceRequest;
1849
1852
  let optionHandler;
1850
1853
  if (options) {
@@ -1871,7 +1874,7 @@ var Hono = class _Hono {
1871
1874
  return [c.env, executionContext];
1872
1875
  };
1873
1876
  replaceRequest ||= (() => {
1874
- const mergedPath = mergePath(this._basePath, path8);
1877
+ const mergedPath = mergePath(this._basePath, path9);
1875
1878
  const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
1876
1879
  return (request) => {
1877
1880
  const url = new URL(request.url);
@@ -1886,14 +1889,14 @@ var Hono = class _Hono {
1886
1889
  }
1887
1890
  await next();
1888
1891
  };
1889
- this.#addRoute(METHOD_NAME_ALL, mergePath(path8, "*"), handler);
1892
+ this.#addRoute(METHOD_NAME_ALL, mergePath(path9, "*"), handler);
1890
1893
  return this;
1891
1894
  }
1892
- #addRoute(method, path8, handler) {
1895
+ #addRoute(method, path9, handler) {
1893
1896
  method = method.toUpperCase();
1894
- path8 = mergePath(this._basePath, path8);
1895
- const r = { basePath: this._basePath, path: path8, method, handler };
1896
- this.router.add(method, path8, [handler, r]);
1897
+ path9 = mergePath(this._basePath, path9);
1898
+ const r = { basePath: this._basePath, path: path9, method, handler };
1899
+ this.router.add(method, path9, [handler, r]);
1897
1900
  this.routes.push(r);
1898
1901
  }
1899
1902
  #handleError(err, c) {
@@ -1906,10 +1909,10 @@ var Hono = class _Hono {
1906
1909
  if (method === "HEAD") {
1907
1910
  return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))();
1908
1911
  }
1909
- const path8 = this.getPath(request, { env });
1910
- const matchResult = this.router.match(method, path8);
1912
+ const path9 = this.getPath(request, { env });
1913
+ const matchResult = this.router.match(method, path9);
1911
1914
  const c = new Context(request, {
1912
- path: path8,
1915
+ path: path9,
1913
1916
  matchResult,
1914
1917
  env,
1915
1918
  executionCtx,
@@ -2009,7 +2012,7 @@ var Hono = class _Hono {
2009
2012
 
2010
2013
  // ../node_modules/.pnpm/hono@4.12.9/node_modules/hono/dist/router/reg-exp-router/matcher.js
2011
2014
  var emptyParam = [];
2012
- function match(method, path8) {
2015
+ function match(method, path9) {
2013
2016
  const matchers = this.buildAllMatchers();
2014
2017
  const match2 = ((method2, path22) => {
2015
2018
  const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
@@ -2025,7 +2028,7 @@ function match(method, path8) {
2025
2028
  return [matcher[1][index2], match3];
2026
2029
  });
2027
2030
  this.match = match2;
2028
- return match2(method, path8);
2031
+ return match2(method, path9);
2029
2032
  }
2030
2033
 
2031
2034
  // ../node_modules/.pnpm/hono@4.12.9/node_modules/hono/dist/router/reg-exp-router/node.js
@@ -2140,12 +2143,12 @@ var Node = class _Node {
2140
2143
  var Trie = class {
2141
2144
  #context = { varIndex: 0 };
2142
2145
  #root = new Node();
2143
- insert(path8, index2, pathErrorCheckOnly) {
2146
+ insert(path9, index2, pathErrorCheckOnly) {
2144
2147
  const paramAssoc = [];
2145
2148
  const groups = [];
2146
2149
  for (let i = 0; ; ) {
2147
2150
  let replaced = false;
2148
- path8 = path8.replace(/\{[^}]+\}/g, (m) => {
2151
+ path9 = path9.replace(/\{[^}]+\}/g, (m) => {
2149
2152
  const mark = `@\\${i}`;
2150
2153
  groups[i] = [mark, m];
2151
2154
  i++;
@@ -2156,7 +2159,7 @@ var Trie = class {
2156
2159
  break;
2157
2160
  }
2158
2161
  }
2159
- const tokens = path8.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
2162
+ const tokens = path9.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
2160
2163
  for (let i = groups.length - 1; i >= 0; i--) {
2161
2164
  const [mark] = groups[i];
2162
2165
  for (let j = tokens.length - 1; j >= 0; j--) {
@@ -2195,9 +2198,9 @@ var Trie = class {
2195
2198
  // ../node_modules/.pnpm/hono@4.12.9/node_modules/hono/dist/router/reg-exp-router/router.js
2196
2199
  var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
2197
2200
  var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
2198
- function buildWildcardRegExp(path8) {
2199
- return wildcardRegExpCache[path8] ??= new RegExp(
2200
- path8 === "*" ? "" : `^${path8.replace(
2201
+ function buildWildcardRegExp(path9) {
2202
+ return wildcardRegExpCache[path9] ??= new RegExp(
2203
+ path9 === "*" ? "" : `^${path9.replace(
2201
2204
  /\/\*$|([.\\+*[^\]$()])/g,
2202
2205
  (_, metaChar) => metaChar ? `\\${metaChar}` : "(?:|/.*)"
2203
2206
  )}$`
@@ -2219,17 +2222,17 @@ function buildMatcherFromPreprocessedRoutes(routes) {
2219
2222
  );
2220
2223
  const staticMap = /* @__PURE__ */ Object.create(null);
2221
2224
  for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) {
2222
- const [pathErrorCheckOnly, path8, handlers] = routesWithStaticPathFlag[i];
2225
+ const [pathErrorCheckOnly, path9, handlers] = routesWithStaticPathFlag[i];
2223
2226
  if (pathErrorCheckOnly) {
2224
- staticMap[path8] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
2227
+ staticMap[path9] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
2225
2228
  } else {
2226
2229
  j++;
2227
2230
  }
2228
2231
  let paramAssoc;
2229
2232
  try {
2230
- paramAssoc = trie.insert(path8, j, pathErrorCheckOnly);
2233
+ paramAssoc = trie.insert(path9, j, pathErrorCheckOnly);
2231
2234
  } catch (e) {
2232
- throw e === PATH_ERROR ? new UnsupportedPathError(path8) : e;
2235
+ throw e === PATH_ERROR ? new UnsupportedPathError(path9) : e;
2233
2236
  }
2234
2237
  if (pathErrorCheckOnly) {
2235
2238
  continue;
@@ -2263,12 +2266,12 @@ function buildMatcherFromPreprocessedRoutes(routes) {
2263
2266
  }
2264
2267
  return [regexp, handlerMap, staticMap];
2265
2268
  }
2266
- function findMiddleware(middleware, path8) {
2269
+ function findMiddleware(middleware, path9) {
2267
2270
  if (!middleware) {
2268
2271
  return void 0;
2269
2272
  }
2270
2273
  for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {
2271
- if (buildWildcardRegExp(k).test(path8)) {
2274
+ if (buildWildcardRegExp(k).test(path9)) {
2272
2275
  return [...middleware[k]];
2273
2276
  }
2274
2277
  }
@@ -2282,7 +2285,7 @@ var RegExpRouter = class {
2282
2285
  this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
2283
2286
  this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
2284
2287
  }
2285
- add(method, path8, handler) {
2288
+ add(method, path9, handler) {
2286
2289
  const middleware = this.#middleware;
2287
2290
  const routes = this.#routes;
2288
2291
  if (!middleware || !routes) {
@@ -2297,18 +2300,18 @@ var RegExpRouter = class {
2297
2300
  });
2298
2301
  });
2299
2302
  }
2300
- if (path8 === "/*") {
2301
- path8 = "*";
2303
+ if (path9 === "/*") {
2304
+ path9 = "*";
2302
2305
  }
2303
- const paramCount = (path8.match(/\/:/g) || []).length;
2304
- if (/\*$/.test(path8)) {
2305
- const re = buildWildcardRegExp(path8);
2306
+ const paramCount = (path9.match(/\/:/g) || []).length;
2307
+ if (/\*$/.test(path9)) {
2308
+ const re = buildWildcardRegExp(path9);
2306
2309
  if (method === METHOD_NAME_ALL) {
2307
2310
  Object.keys(middleware).forEach((m) => {
2308
- middleware[m][path8] ||= findMiddleware(middleware[m], path8) || findMiddleware(middleware[METHOD_NAME_ALL], path8) || [];
2311
+ middleware[m][path9] ||= findMiddleware(middleware[m], path9) || findMiddleware(middleware[METHOD_NAME_ALL], path9) || [];
2309
2312
  });
2310
2313
  } else {
2311
- middleware[method][path8] ||= findMiddleware(middleware[method], path8) || findMiddleware(middleware[METHOD_NAME_ALL], path8) || [];
2314
+ middleware[method][path9] ||= findMiddleware(middleware[method], path9) || findMiddleware(middleware[METHOD_NAME_ALL], path9) || [];
2312
2315
  }
2313
2316
  Object.keys(middleware).forEach((m) => {
2314
2317
  if (method === METHOD_NAME_ALL || method === m) {
@@ -2326,7 +2329,7 @@ var RegExpRouter = class {
2326
2329
  });
2327
2330
  return;
2328
2331
  }
2329
- const paths = checkOptionalParameter(path8) || [path8];
2332
+ const paths = checkOptionalParameter(path9) || [path9];
2330
2333
  for (let i = 0, len = paths.length; i < len; i++) {
2331
2334
  const path22 = paths[i];
2332
2335
  Object.keys(routes).forEach((m) => {
@@ -2353,13 +2356,13 @@ var RegExpRouter = class {
2353
2356
  const routes = [];
2354
2357
  let hasOwnRoute = method === METHOD_NAME_ALL;
2355
2358
  [this.#middleware, this.#routes].forEach((r) => {
2356
- const ownRoute = r[method] ? Object.keys(r[method]).map((path8) => [path8, r[method][path8]]) : [];
2359
+ const ownRoute = r[method] ? Object.keys(r[method]).map((path9) => [path9, r[method][path9]]) : [];
2357
2360
  if (ownRoute.length !== 0) {
2358
2361
  hasOwnRoute ||= true;
2359
2362
  routes.push(...ownRoute);
2360
2363
  } else if (method !== METHOD_NAME_ALL) {
2361
2364
  routes.push(
2362
- ...Object.keys(r[METHOD_NAME_ALL]).map((path8) => [path8, r[METHOD_NAME_ALL][path8]])
2365
+ ...Object.keys(r[METHOD_NAME_ALL]).map((path9) => [path9, r[METHOD_NAME_ALL][path9]])
2363
2366
  );
2364
2367
  }
2365
2368
  });
@@ -2379,13 +2382,13 @@ var SmartRouter = class {
2379
2382
  constructor(init) {
2380
2383
  this.#routers = init.routers;
2381
2384
  }
2382
- add(method, path8, handler) {
2385
+ add(method, path9, handler) {
2383
2386
  if (!this.#routes) {
2384
2387
  throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
2385
2388
  }
2386
- this.#routes.push([method, path8, handler]);
2389
+ this.#routes.push([method, path9, handler]);
2387
2390
  }
2388
- match(method, path8) {
2391
+ match(method, path9) {
2389
2392
  if (!this.#routes) {
2390
2393
  throw new Error("Fatal error");
2391
2394
  }
@@ -2400,7 +2403,7 @@ var SmartRouter = class {
2400
2403
  for (let i2 = 0, len2 = routes.length; i2 < len2; i2++) {
2401
2404
  router.add(...routes[i2]);
2402
2405
  }
2403
- res = router.match(method, path8);
2406
+ res = router.match(method, path9);
2404
2407
  } catch (e) {
2405
2408
  if (e instanceof UnsupportedPathError) {
2406
2409
  continue;
@@ -2450,10 +2453,10 @@ var Node2 = class _Node2 {
2450
2453
  }
2451
2454
  this.#patterns = [];
2452
2455
  }
2453
- insert(method, path8, handler) {
2456
+ insert(method, path9, handler) {
2454
2457
  this.#order = ++this.#order;
2455
2458
  let curNode = this;
2456
- const parts = splitRoutingPath(path8);
2459
+ const parts = splitRoutingPath(path9);
2457
2460
  const possibleKeys = [];
2458
2461
  for (let i = 0, len = parts.length; i < len; i++) {
2459
2462
  const p = parts[i];
@@ -2502,12 +2505,12 @@ var Node2 = class _Node2 {
2502
2505
  }
2503
2506
  }
2504
2507
  }
2505
- search(method, path8) {
2508
+ search(method, path9) {
2506
2509
  const handlerSets = [];
2507
2510
  this.#params = emptyParams;
2508
2511
  const curNode = this;
2509
2512
  let curNodes = [curNode];
2510
- const parts = splitPath(path8);
2513
+ const parts = splitPath(path9);
2511
2514
  const curNodesQueue = [];
2512
2515
  const len = parts.length;
2513
2516
  let partOffsets = null;
@@ -2549,13 +2552,13 @@ var Node2 = class _Node2 {
2549
2552
  if (matcher instanceof RegExp) {
2550
2553
  if (partOffsets === null) {
2551
2554
  partOffsets = new Array(len);
2552
- let offset = path8[0] === "/" ? 1 : 0;
2555
+ let offset = path9[0] === "/" ? 1 : 0;
2553
2556
  for (let p = 0; p < len; p++) {
2554
2557
  partOffsets[p] = offset;
2555
2558
  offset += parts[p].length + 1;
2556
2559
  }
2557
2560
  }
2558
- const restPathString = path8.substring(partOffsets[i]);
2561
+ const restPathString = path9.substring(partOffsets[i]);
2559
2562
  const m = matcher.exec(restPathString);
2560
2563
  if (m) {
2561
2564
  params[name] = m[0];
@@ -2608,18 +2611,18 @@ var TrieRouter = class {
2608
2611
  constructor() {
2609
2612
  this.#node = new Node2();
2610
2613
  }
2611
- add(method, path8, handler) {
2612
- const results = checkOptionalParameter(path8);
2614
+ add(method, path9, handler) {
2615
+ const results = checkOptionalParameter(path9);
2613
2616
  if (results) {
2614
2617
  for (let i = 0, len = results.length; i < len; i++) {
2615
2618
  this.#node.insert(method, results[i], handler);
2616
2619
  }
2617
2620
  return;
2618
2621
  }
2619
- this.#node.insert(method, path8, handler);
2622
+ this.#node.insert(method, path9, handler);
2620
2623
  }
2621
- match(method, path8) {
2622
- return this.#node.search(method, path8);
2624
+ match(method, path9) {
2625
+ return this.#node.search(method, path9);
2623
2626
  }
2624
2627
  };
2625
2628
 
@@ -2899,10 +2902,10 @@ var createStreamBody = (stream2) => {
2899
2902
  });
2900
2903
  return body;
2901
2904
  };
2902
- var getStats = (path8) => {
2905
+ var getStats = (path9) => {
2903
2906
  let stats;
2904
2907
  try {
2905
- stats = statSync(path8);
2908
+ stats = statSync(path9);
2906
2909
  } catch {
2907
2910
  }
2908
2911
  return stats;
@@ -2945,21 +2948,21 @@ var serveStatic = (options = { root: "" }) => {
2945
2948
  return next();
2946
2949
  }
2947
2950
  }
2948
- let path8 = join(
2951
+ let path9 = join(
2949
2952
  root,
2950
2953
  !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename, c) : filename
2951
2954
  );
2952
- let stats = getStats(path8);
2955
+ let stats = getStats(path9);
2953
2956
  if (stats && stats.isDirectory()) {
2954
2957
  const indexFile = options.index ?? "index.html";
2955
- path8 = join(path8, indexFile);
2956
- stats = getStats(path8);
2958
+ path9 = join(path9, indexFile);
2959
+ stats = getStats(path9);
2957
2960
  }
2958
2961
  if (!stats) {
2959
- await options.onNotFound?.(path8, c);
2962
+ await options.onNotFound?.(path9, c);
2960
2963
  return next();
2961
2964
  }
2962
- const mimeType = getMimeType(path8);
2965
+ const mimeType = getMimeType(path9);
2963
2966
  c.header("Content-Type", mimeType || "application/octet-stream");
2964
2967
  if (options.precompressed && (!mimeType || COMPRESSIBLE_CONTENT_TYPE_REGEX.test(mimeType))) {
2965
2968
  const acceptEncodingSet = new Set(
@@ -2969,12 +2972,12 @@ var serveStatic = (options = { root: "" }) => {
2969
2972
  if (!acceptEncodingSet.has(encoding)) {
2970
2973
  continue;
2971
2974
  }
2972
- const precompressedStats = getStats(path8 + ENCODINGS[encoding]);
2975
+ const precompressedStats = getStats(path9 + ENCODINGS[encoding]);
2973
2976
  if (precompressedStats) {
2974
2977
  c.header("Content-Encoding", encoding);
2975
2978
  c.header("Vary", "Accept-Encoding", { append: true });
2976
2979
  stats = precompressedStats;
2977
- path8 = path8 + ENCODINGS[encoding];
2980
+ path9 = path9 + ENCODINGS[encoding];
2978
2981
  break;
2979
2982
  }
2980
2983
  }
@@ -2988,7 +2991,7 @@ var serveStatic = (options = { root: "" }) => {
2988
2991
  result = c.body(null);
2989
2992
  } else if (!range) {
2990
2993
  c.header("Content-Length", size.toString());
2991
- result = c.body(createStreamBody(createReadStream(path8)), 200);
2994
+ result = c.body(createStreamBody(createReadStream(path9)), 200);
2992
2995
  } else {
2993
2996
  c.header("Accept-Ranges", "bytes");
2994
2997
  c.header("Date", stats.birthtime.toUTCString());
@@ -2999,12 +3002,12 @@ var serveStatic = (options = { root: "" }) => {
2999
3002
  end = size - 1;
3000
3003
  }
3001
3004
  const chunksize = end - start + 1;
3002
- const stream2 = createReadStream(path8, { start, end });
3005
+ const stream2 = createReadStream(path9, { start, end });
3003
3006
  c.header("Content-Length", chunksize.toString());
3004
3007
  c.header("Content-Range", `bytes ${start}-${end}/${stats.size}`);
3005
3008
  result = c.body(createStreamBody(stream2), 206);
3006
3009
  }
3007
- await options.onFound?.(path8, c);
3010
+ await options.onFound?.(path9, c);
3008
3011
  return result;
3009
3012
  };
3010
3013
  };
@@ -3238,7 +3241,9 @@ function toApiBlock(block) {
3238
3241
  return {
3239
3242
  type: "tool_result",
3240
3243
  tool_use_id: String(block.toolUseId ?? ""),
3241
- content: block.content
3244
+ content: block.content,
3245
+ ...block.isError === true ? { is_error: true } : {},
3246
+ ...typeof block.filePath === "string" && Array.isArray(block.patch) ? { file_path: block.filePath, patch: block.patch } : {}
3242
3247
  };
3243
3248
  }
3244
3249
  return block;
@@ -3374,9 +3379,12 @@ function createChatReader({
3374
3379
  if (!visibility.isVisible(row.id)) return null;
3375
3380
  const rows = archive2.read.listMessagesByChat(row.agent, row.sourceId);
3376
3381
  return rows.map((m) => ({
3382
+ id: m.messageId,
3377
3383
  role: m.role,
3378
3384
  content: m.blocks.map(toApiBlock),
3379
- timestamp: m.ts.toISOString()
3385
+ timestamp: m.ts.toISOString(),
3386
+ ...m.model === null ? {} : { model: m.model },
3387
+ ...m.effort === null ? {} : { effort: m.effort }
3380
3388
  }));
3381
3389
  }
3382
3390
  return {
@@ -3391,6 +3399,450 @@ function createChatReader({
3391
3399
  // src/list-contract.ts
3392
3400
  var MAX_PAGE_LIMIT = 200;
3393
3401
 
3402
+ // src/plugins/claude-code/plugin.ts
3403
+ import fs2 from "fs";
3404
+ import path3 from "path";
3405
+ import readline from "readline";
3406
+
3407
+ // src/plugins/visualize-widget.ts
3408
+ var SHOW_WIDGET_TOOL = "mcp__visualize__show_widget";
3409
+ function svgWidgetCode(block) {
3410
+ if (!block || typeof block !== "object") return null;
3411
+ const b = block;
3412
+ if (b.type !== "tool_use" || b.name !== SHOW_WIDGET_TOOL) return null;
3413
+ const input = b.input;
3414
+ const code = input?.widget_code;
3415
+ if (typeof code !== "string") return null;
3416
+ return code.trimStart().startsWith("<svg") ? code : null;
3417
+ }
3418
+ var RAMPS = {
3419
+ "c-purple": { fill: "#3C3489", stroke: "#AFA9EC", title: "#CECBF6" },
3420
+ "c-teal": { fill: "#085041", stroke: "#5DCAA5", title: "#9FE1CB" },
3421
+ "c-coral": { fill: "#712B13", stroke: "#F0997B", title: "#F5C4B3" },
3422
+ "c-pink": { fill: "#72243E", stroke: "#ED93B1", title: "#F4C0D1" },
3423
+ "c-gray": { fill: "#444441", stroke: "#B4B2A9", title: "#D3D1C7" },
3424
+ "c-blue": { fill: "#0C447C", stroke: "#85B7EB", title: "#B5D4F4" },
3425
+ "c-green": { fill: "#27500A", stroke: "#97C459", title: "#C0DD97" },
3426
+ "c-amber": { fill: "#633806", stroke: "#EF9F27", title: "#FAC775" },
3427
+ "c-red": { fill: "#791F1F", stroke: "#F09595", title: "#F7C1C1" }
3428
+ };
3429
+ var TEXT_PRIMARY = "#eee8d5";
3430
+ var TEXT_SECONDARY = "#93a1a1";
3431
+ var FONT_SANS = "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif";
3432
+ function widgetStylesheet() {
3433
+ const ramps = Object.entries(RAMPS).map(
3434
+ ([name, c]) => `.${name}>rect,.${name}>circle,.${name}>ellipse,rect.${name},circle.${name},ellipse.${name}{fill:${c.fill};stroke:${c.stroke};stroke-width:1.5}.${name}>text.t,.${name}>text.th{fill:${c.title}}.${name}>text.ts{fill:${c.stroke}}`
3435
+ ).join("");
3436
+ return `text{font-family:${FONT_SANS}}.t{font-size:14px;fill:${TEXT_PRIMARY}}.th{font-size:14px;font-weight:500;fill:${TEXT_PRIMARY}}.ts{font-size:12px;fill:${TEXT_SECONDARY}}` + ramps;
3437
+ }
3438
+ function themeWidgetSvg(svg) {
3439
+ const root = /<svg\b[^>]*>/.exec(svg);
3440
+ if (!root) return svg;
3441
+ const sized = withIntrinsicSize(root[0]);
3442
+ const at = root.index + root[0].length;
3443
+ return svg.slice(0, root.index) + sized + `<style>${widgetStylesheet()}</style>` + svg.slice(at);
3444
+ }
3445
+ function withIntrinsicSize(rootTag) {
3446
+ if (/\s(width|height)\s*=/.test(rootTag)) return rootTag;
3447
+ const viewBox = /\bviewBox\s*=\s*"([^"]*)"/.exec(rootTag);
3448
+ if (!viewBox) return rootTag;
3449
+ const parts = viewBox[1].trim().split(/[\s,]+/).map(Number);
3450
+ if (parts.length !== 4 || parts.some((n) => !Number.isFinite(n))) {
3451
+ return rootTag;
3452
+ }
3453
+ const [, , width, height] = parts;
3454
+ if (width <= 0 || height <= 0) return rootTag;
3455
+ return `${rootTag.slice(0, -1)} width="${width}" height="${height}">`;
3456
+ }
3457
+
3458
+ // src/plugins/mcp-tool-name.ts
3459
+ var PREFIX = "mcp__";
3460
+ var SEPARATOR = "__";
3461
+ function mcpServerName(toolName) {
3462
+ if (!toolName.startsWith(PREFIX)) return null;
3463
+ const rest = toolName.slice(PREFIX.length);
3464
+ const end = rest.indexOf(SEPARATOR);
3465
+ const server2 = end === -1 ? rest : rest.slice(0, end);
3466
+ if (!server2) return null;
3467
+ return server2.replace(/_/g, " ");
3468
+ }
3469
+
3470
+ // src/plugins/claude-code/actions.ts
3471
+ function getString(input, key) {
3472
+ if (typeof input !== "object" || input === null) return void 0;
3473
+ const value = input[key];
3474
+ return typeof value === "string" && value !== "" ? value : void 0;
3475
+ }
3476
+ var FILE_KINDS = {
3477
+ Edit: "edit",
3478
+ MultiEdit: "edit",
3479
+ Write: "write",
3480
+ Read: "read"
3481
+ };
3482
+ var SEARCH_TOOLS = /* @__PURE__ */ new Set(["Grep", "Glob", "WebSearch", "ToolSearch"]);
3483
+ function firstLine(text2) {
3484
+ return text2?.split("\n", 1)[0];
3485
+ }
3486
+ function phrase(value) {
3487
+ return value ? { object: { type: "phrase", value } } : {};
3488
+ }
3489
+ function path2(value) {
3490
+ return value ? { object: { type: "path", value } } : {};
3491
+ }
3492
+ function detail(value) {
3493
+ return value ? { detail: value } : {};
3494
+ }
3495
+ function toolAction(name, input) {
3496
+ if (name === "Bash") {
3497
+ const command = getString(input, "command");
3498
+ const said = getString(input, "description") ?? firstLine(command);
3499
+ return { kind: "execute", ...phrase(said), ...detail(command) };
3500
+ }
3501
+ if (name === "Agent" || name === "SendMessage") {
3502
+ const task = getString(input, "description") ?? getString(input, "summary");
3503
+ return { kind: "delegate", ...phrase(task) };
3504
+ }
3505
+ if (SEARCH_TOOLS.has(name)) {
3506
+ const looked = getString(input, "pattern") ?? getString(input, "query");
3507
+ return { kind: "search", ...phrase(looked) };
3508
+ }
3509
+ const fileKind = FILE_KINDS[name];
3510
+ if (fileKind) {
3511
+ return { kind: fileKind, ...path2(getString(input, "file_path")) };
3512
+ }
3513
+ if (name === "WebFetch") {
3514
+ return { kind: "read", ...phrase(getString(input, "url")) };
3515
+ }
3516
+ return { kind: "other", ...phrase(mcpServerName(name) ?? name) };
3517
+ }
3518
+
3519
+ // src/plugins/claude-code/plugin.ts
3520
+ var ClaudeCodePlugin = class {
3521
+ id = "claude-code";
3522
+ displayName = "Claude Code";
3523
+ async *discover(env) {
3524
+ const projectsDir = path3.join(env.homeDir, ".claude", "projects");
3525
+ if (!fs2.existsSync(projectsDir)) return;
3526
+ const projects = fs2.readdirSync(projectsDir, { withFileTypes: true });
3527
+ for (const project of projects) {
3528
+ if (!project.isDirectory()) continue;
3529
+ const projectPath = path3.join(projectsDir, project.name);
3530
+ const files = fs2.readdirSync(projectPath, { withFileTypes: true });
3531
+ for (const file of files) {
3532
+ if (!file.isFile() || !file.name.endsWith(".jsonl")) continue;
3533
+ const sourcePath = path3.join(projectPath, file.name);
3534
+ const sourceId = file.name.replace(/\.jsonl$/, "");
3535
+ const cwd = await readCwdFromJsonl(sourcePath);
3536
+ yield {
3537
+ sourceId,
3538
+ sourcePath,
3539
+ watchPaths: [sourcePath],
3540
+ project: cwd ? path3.basename(cwd) : void 0,
3541
+ projectPath: cwd ?? void 0
3542
+ };
3543
+ }
3544
+ }
3545
+ }
3546
+ async *extractRaw(ref) {
3547
+ if (!fs2.existsSync(ref.sourcePath)) return;
3548
+ const stream2 = fs2.createReadStream(ref.sourcePath, { encoding: "utf-8" });
3549
+ const rl = readline.createInterface({ input: stream2, crlfDelay: Infinity });
3550
+ let lineNo = 0;
3551
+ for await (const line of rl) {
3552
+ lineNo += 1;
3553
+ if (!line) continue;
3554
+ yield {
3555
+ sourceId: ref.sourceId,
3556
+ sourcePath: ref.sourcePath,
3557
+ sourceLocator: `L${lineNo}`,
3558
+ payload: JSON.parse(line)
3559
+ };
3560
+ }
3561
+ }
3562
+ normalize(raw2) {
3563
+ const payload = raw2.payload;
3564
+ if (!payload || typeof payload !== "object") return null;
3565
+ if (payload.type !== "user" && payload.type !== "assistant") return null;
3566
+ if (payload.isMeta === true) return null;
3567
+ if (payload.isSidechain === true) return null;
3568
+ const message = payload.message;
3569
+ if (!message) return null;
3570
+ const role = message.role === "assistant" ? "assistant" : "user";
3571
+ const messageId = String(payload.uuid ?? "");
3572
+ const ts = String(payload.timestamp ?? "");
3573
+ const model = typeof message.model === "string" && message.model !== "" ? { model: message.model } : {};
3574
+ const effort = typeof payload.effort === "string" && payload.effort !== "" ? { effort: payload.effort } : {};
3575
+ const cwd = typeof payload.cwd === "string" ? payload.cwd : void 0;
3576
+ if (typeof message.content === "string") {
3577
+ const command = parseCommandMarkup(message.content);
3578
+ if (command) {
3579
+ const commandLine = `${command.name} ${command.args}`.trim();
3580
+ return {
3581
+ messageId,
3582
+ role,
3583
+ ts,
3584
+ text: commandLine,
3585
+ blocks: [{ type: "command", name: command.name, args: command.args }]
3586
+ };
3587
+ }
3588
+ const system = parseSystemNoise(message.content);
3589
+ if (system) {
3590
+ return {
3591
+ messageId,
3592
+ role,
3593
+ ts,
3594
+ text: system.summary,
3595
+ blocks: [system]
3596
+ };
3597
+ }
3598
+ return {
3599
+ messageId,
3600
+ role,
3601
+ ts,
3602
+ text: translateFileMentions(message.content, cwd, asPath),
3603
+ blocks: [
3604
+ {
3605
+ type: "text",
3606
+ text: translateFileMentions(message.content, cwd, asLink)
3607
+ }
3608
+ ],
3609
+ ...model,
3610
+ ...effort
3611
+ };
3612
+ }
3613
+ if (Array.isArray(message.content)) {
3614
+ const raw3 = message.content.flatMap(
3615
+ (block, index2) => normalizeBlock(block, messageId, index2, payload.toolUseResult)
3616
+ );
3617
+ const blocks = raw3.map(
3618
+ (block) => block.type === "text" ? { ...block, text: translateFileMentions(block.text, cwd, asLink) } : block
3619
+ );
3620
+ const text2 = raw3.filter((b) => b.type === "text").map((b) => translateFileMentions(b.text, cwd, asPath)).join("\n");
3621
+ return { messageId, role, ts, text: text2, blocks, ...model, ...effort };
3622
+ }
3623
+ return null;
3624
+ }
3625
+ resolveImage(ref, loadPayload) {
3626
+ const address = parseImageRef(ref);
3627
+ if (!address) return null;
3628
+ const record = loadPayload(address.messageId);
3629
+ if (!record || typeof record !== "object") return null;
3630
+ const message = record.message;
3631
+ if (!message || !Array.isArray(message.content)) return null;
3632
+ const block = message.content[address.index];
3633
+ if (!block) return null;
3634
+ const widget = svgWidgetCode(block);
3635
+ if (widget) {
3636
+ return {
3637
+ mediaType: "image/svg+xml",
3638
+ bytes: Buffer.from(themeWidgetSvg(widget), "utf8"),
3639
+ rendered: true
3640
+ };
3641
+ }
3642
+ if (block.type !== "image") return null;
3643
+ const source = block.source;
3644
+ if (!source || source.type !== "base64") return null;
3645
+ const mediaType = String(source.media_type ?? "");
3646
+ const data = source.data;
3647
+ if (!mediaType || typeof data !== "string") return null;
3648
+ return { mediaType, bytes: Buffer.from(data, "base64") };
3649
+ }
3650
+ };
3651
+ async function readCwdFromJsonl(sourcePath) {
3652
+ try {
3653
+ const stream2 = fs2.createReadStream(sourcePath, { encoding: "utf-8" });
3654
+ const rl = readline.createInterface({ input: stream2, crlfDelay: Infinity });
3655
+ try {
3656
+ for await (const line of rl) {
3657
+ if (!line) continue;
3658
+ let obj;
3659
+ try {
3660
+ obj = JSON.parse(line);
3661
+ } catch {
3662
+ continue;
3663
+ }
3664
+ if (typeof obj.cwd === "string" && obj.cwd.length > 0) {
3665
+ return obj.cwd;
3666
+ }
3667
+ }
3668
+ } finally {
3669
+ rl.close();
3670
+ stream2.destroy();
3671
+ }
3672
+ } catch {
3673
+ }
3674
+ return void 0;
3675
+ }
3676
+ var COMMAND_NAME_RE = /<command-name>([\s\S]*?)<\/command-name>/;
3677
+ var COMMAND_ARGS_RE = /<command-args>([\s\S]*?)<\/command-args>/;
3678
+ function parseCommandMarkup(content) {
3679
+ const nameMatch = COMMAND_NAME_RE.exec(content);
3680
+ if (!nameMatch) return null;
3681
+ const name = nameMatch[1].trim();
3682
+ const argsMatch = COMMAND_ARGS_RE.exec(content);
3683
+ const args = argsMatch ? argsMatch[1].trim() : "";
3684
+ return { name, args };
3685
+ }
3686
+ var TASK_NOTIFICATION_RE = /<task-notification>[\s\S]*<\/task-notification>/;
3687
+ var TASK_SUMMARY_RE = /<summary>([\s\S]*?)<\/summary>/;
3688
+ var LOCAL_COMMAND_STDOUT_RE = /<local-command-stdout>([\s\S]*?)<\/local-command-stdout>/;
3689
+ var ANSI_SGR_RE = /\u001b\[[0-9;]*m/g;
3690
+ function stripAnsi(text2) {
3691
+ return text2.replace(ANSI_SGR_RE, "");
3692
+ }
3693
+ function parseSystemNoise(content) {
3694
+ const notification = TASK_NOTIFICATION_RE.exec(content);
3695
+ if (notification) {
3696
+ const summaryMatch = TASK_SUMMARY_RE.exec(notification[0]);
3697
+ return {
3698
+ type: "system",
3699
+ kind: "task-notification",
3700
+ summary: summaryMatch?.[1].trim() || "Task notification",
3701
+ detail: notification[0]
3702
+ };
3703
+ }
3704
+ const stdout = LOCAL_COMMAND_STDOUT_RE.exec(content);
3705
+ if (stdout) {
3706
+ return {
3707
+ type: "system",
3708
+ kind: "local-command-stdout",
3709
+ summary: stripAnsi(stdout[1]).trim(),
3710
+ detail: ""
3711
+ };
3712
+ }
3713
+ return null;
3714
+ }
3715
+ var FILE_MENTION_RE = /(^|\s)@(?:"([^"\n]+)"|(\S+))/g;
3716
+ var CODE_SEGMENT_RE = /```[\s\S]*?```|`[^`\n]*`/g;
3717
+ var asLink = (mentioned, cwd) => `[${mentioned}](${fileUrl(resolveAgainst(mentioned, cwd))})`;
3718
+ var asPath = (mentioned) => mentioned;
3719
+ function translateFileMentions(text2, cwd, write) {
3720
+ let out = "";
3721
+ let last = 0;
3722
+ for (const code of text2.matchAll(CODE_SEGMENT_RE)) {
3723
+ out += translateProse(text2.slice(last, code.index), cwd, write) + code[0];
3724
+ last = code.index + code[0].length;
3725
+ }
3726
+ return out + translateProse(text2.slice(last), cwd, write);
3727
+ }
3728
+ function translateProse(text2, cwd, write) {
3729
+ return text2.replace(
3730
+ FILE_MENTION_RE,
3731
+ (match2, lead, quoted, bare) => {
3732
+ const mentioned = quoted ?? bare;
3733
+ if (!looksLikeFilePath(mentioned)) return match2;
3734
+ return `${lead}${write(mentioned, cwd)}`;
3735
+ }
3736
+ );
3737
+ }
3738
+ function resolveAgainst(mentioned, cwd) {
3739
+ if (!cwd) return mentioned;
3740
+ if (mentioned.startsWith("/") || mentioned.startsWith("~")) return mentioned;
3741
+ return path3.posix.join(cwd, mentioned);
3742
+ }
3743
+ function looksLikeFilePath(mentioned) {
3744
+ if (mentioned.endsWith("/")) return true;
3745
+ const basename3 = mentioned.slice(mentioned.lastIndexOf("/") + 1);
3746
+ if (/^\.[A-Za-z0-9]/.test(basename3)) return true;
3747
+ return /^[^.].*\.[A-Za-z0-9]+$/.test(basename3);
3748
+ }
3749
+ function fileUrl(filePath) {
3750
+ return `file://${encodeURI(filePath).replace(/[()]/g, encodeURIComponent)}`;
3751
+ }
3752
+ function imageRef(messageId, index2) {
3753
+ return `${messageId}.${index2}`;
3754
+ }
3755
+ function parseImageRef(ref) {
3756
+ const dot = ref.lastIndexOf(".");
3757
+ if (dot <= 0) return null;
3758
+ const index2 = Number(ref.slice(dot + 1));
3759
+ if (!Number.isInteger(index2) || index2 < 0) return null;
3760
+ return { messageId: ref.slice(0, dot), index: index2 };
3761
+ }
3762
+ function normalizeBlock(raw2, messageId, index2, toolUseResult) {
3763
+ const one = normalizeOneBlock(raw2, messageId, index2, toolUseResult);
3764
+ if (!one) return [];
3765
+ if (one.type === "tool_use" && svgWidgetCode(raw2)) {
3766
+ return [
3767
+ one,
3768
+ {
3769
+ type: "image",
3770
+ mediaType: "image/svg+xml",
3771
+ ref: imageRef(messageId, index2)
3772
+ }
3773
+ ];
3774
+ }
3775
+ return [one];
3776
+ }
3777
+ function editedFile(toolUseResult) {
3778
+ if (!toolUseResult || typeof toolUseResult !== "object") return {};
3779
+ const r = toolUseResult;
3780
+ if (typeof r.filePath !== "string" || !r.filePath) return {};
3781
+ if (!Array.isArray(r.structuredPatch)) return {};
3782
+ const patch = r.structuredPatch.filter(isPatchHunk);
3783
+ if (patch.length > 0) return { filePath: r.filePath, patch };
3784
+ const created = createdFilePatch(r);
3785
+ if (created) return { filePath: r.filePath, patch: created };
3786
+ return {};
3787
+ }
3788
+ function createdFilePatch(r) {
3789
+ if (r.type !== "create" || typeof r.content !== "string" || !r.content) {
3790
+ return null;
3791
+ }
3792
+ const content = r.content.endsWith("\n") ? r.content.slice(0, -1) : r.content;
3793
+ const lines = content.split("\n").map((line) => `+${line}`);
3794
+ return [
3795
+ { oldStart: 0, oldLines: 0, newStart: 1, newLines: lines.length, lines }
3796
+ ];
3797
+ }
3798
+ function isPatchHunk(hunk) {
3799
+ if (!hunk || typeof hunk !== "object") return false;
3800
+ const h = hunk;
3801
+ return typeof h.oldStart === "number" && typeof h.oldLines === "number" && typeof h.newStart === "number" && typeof h.newLines === "number" && Array.isArray(h.lines) && h.lines.every((line) => typeof line === "string");
3802
+ }
3803
+ function normalizeOneBlock(raw2, messageId, index2, toolUseResult) {
3804
+ if (!raw2 || typeof raw2 !== "object") return null;
3805
+ const b = raw2;
3806
+ switch (b.type) {
3807
+ case "image": {
3808
+ const source = b.source;
3809
+ if (!source || source.type !== "base64") return null;
3810
+ const mediaType = String(source.media_type ?? "");
3811
+ if (!mediaType) return null;
3812
+ return { type: "image", mediaType, ref: imageRef(messageId, index2) };
3813
+ }
3814
+ case "text":
3815
+ return { type: "text", text: String(b.text ?? "") };
3816
+ case "thinking":
3817
+ return { type: "thinking", thinking: String(b.thinking ?? "") };
3818
+ case "tool_use": {
3819
+ const name = String(b.name ?? "");
3820
+ return {
3821
+ type: "tool_use",
3822
+ id: String(b.id ?? ""),
3823
+ name,
3824
+ input: b.input,
3825
+ action: toolAction(name, b.input)
3826
+ };
3827
+ }
3828
+ case "tool_result":
3829
+ return {
3830
+ type: "tool_result",
3831
+ toolUseId: String(b.tool_use_id ?? ""),
3832
+ content: b.content,
3833
+ // Only carried when the tool actually failed: a `false` on every
3834
+ // successful result would grow the stored block for nothing.
3835
+ ...b.is_error === true ? { isError: true } : {},
3836
+ ...editedFile(toolUseResult)
3837
+ };
3838
+ default:
3839
+ return null;
3840
+ }
3841
+ }
3842
+
3843
+ // src/plugins/registry.ts
3844
+ var plugins = [new ClaudeCodePlugin()];
3845
+
3394
3846
  // src/app.ts
3395
3847
  var STREAM_HEARTBEAT_MS = 25e3;
3396
3848
  function createApp({
@@ -3661,6 +4113,52 @@ function createApp({
3661
4113
  }
3662
4114
  return c.json({ messages: messages2 });
3663
4115
  });
4116
+ app2.get("/api/chats/:id/images/:ref", (c) => {
4117
+ const row = reader.findChat(c.req.param("id"));
4118
+ if (!row) return c.json({ error: "Chat not found" }, 404);
4119
+ const plugin = plugins.find((p) => p.id === row.agent);
4120
+ if (!plugin?.resolveImage) {
4121
+ return c.json({ error: "Image not found" }, 404);
4122
+ }
4123
+ const image = plugin.resolveImage(c.req.param("ref"), (messageId) => {
4124
+ const payload = archive2.read.findRawPayloadForMessage(
4125
+ row.agent,
4126
+ row.sourceId,
4127
+ messageId
4128
+ );
4129
+ if (payload === null) return null;
4130
+ try {
4131
+ return JSON.parse(payload);
4132
+ } catch {
4133
+ return null;
4134
+ }
4135
+ });
4136
+ if (!image) return c.json({ error: "Image not found" }, 404);
4137
+ if (image.rendered) {
4138
+ const etag = `"${createHash("sha256").update(image.bytes).digest("base64url").slice(0, 27)}"`;
4139
+ if (c.req.header("if-none-match") === etag) {
4140
+ return c.body(null, 304, { ETag: etag, "Cache-Control": "no-cache" });
4141
+ }
4142
+ return c.body(new Uint8Array(image.bytes), 200, {
4143
+ "Content-Type": image.mediaType,
4144
+ "Cache-Control": "no-cache",
4145
+ ETag: etag,
4146
+ "Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; sandbox",
4147
+ "X-Content-Type-Options": "nosniff"
4148
+ });
4149
+ }
4150
+ return c.body(new Uint8Array(image.bytes), 200, {
4151
+ "Content-Type": image.mediaType,
4152
+ // Verbatim archived bytes never change, so the browser keeps them forever.
4153
+ "Cache-Control": "public, max-age=31536000, immutable",
4154
+ // Archived bytes are the Agent's, not ours — an SVG widget may carry a
4155
+ // script. The pane renders these through `<img>`, which already refuses
4156
+ // to run scripts or load external resources; saying it again in the
4157
+ // headers keeps that true for anyone opening the URL on its own.
4158
+ "Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; sandbox",
4159
+ "X-Content-Type-Options": "nosniff"
4160
+ });
4161
+ });
3664
4162
  app2.get("/api/tags", (c) => {
3665
4163
  return c.json({ tags: tags3.listTags() });
3666
4164
  });
@@ -5063,7 +5561,7 @@ var QueryPromise = class {
5063
5561
  function mapResultRow(columns, row, joinsNotNullableMap) {
5064
5562
  const nullifyMap = {};
5065
5563
  const result = columns.reduce(
5066
- (result2, { path: path8, field }, columnIndex) => {
5564
+ (result2, { path: path9, field }, columnIndex) => {
5067
5565
  let decoder;
5068
5566
  if (is(field, Column)) {
5069
5567
  decoder = field;
@@ -5075,8 +5573,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
5075
5573
  decoder = field.sql.decoder;
5076
5574
  }
5077
5575
  let node = result2;
5078
- for (const [pathChunkIndex, pathChunk] of path8.entries()) {
5079
- if (pathChunkIndex < path8.length - 1) {
5576
+ for (const [pathChunkIndex, pathChunk] of path9.entries()) {
5577
+ if (pathChunkIndex < path9.length - 1) {
5080
5578
  if (!(pathChunk in node)) {
5081
5579
  node[pathChunk] = {};
5082
5580
  }
@@ -5084,8 +5582,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
5084
5582
  } else {
5085
5583
  const rawValue = row[columnIndex];
5086
5584
  const value = node[pathChunk] = rawValue === null ? null : decoder.mapFromDriverValue(rawValue);
5087
- if (joinsNotNullableMap && is(field, Column) && path8.length === 2) {
5088
- const objectName = path8[0];
5585
+ if (joinsNotNullableMap && is(field, Column) && path9.length === 2) {
5586
+ const objectName = path9[0];
5089
5587
  if (!(objectName in nullifyMap)) {
5090
5588
  nullifyMap[objectName] = value === null ? getTableName(field.table) : false;
5091
5589
  } else if (typeof nullifyMap[objectName] === "string" && nullifyMap[objectName] !== getTableName(field.table)) {
@@ -5645,8 +6143,8 @@ function mapRelationalRow(tablesConfig, tableConfig, row, buildQueryResultSelect
5645
6143
  }
5646
6144
 
5647
6145
  // src/storage/openStore.ts
5648
- import fs3 from "fs";
5649
- import path2 from "path";
6146
+ import fs4 from "fs";
6147
+ import path4 from "path";
5650
6148
  import { fileURLToPath } from "url";
5651
6149
  import Database2 from "better-sqlite3";
5652
6150
 
@@ -9255,20 +9753,20 @@ function drizzle(...params) {
9255
9753
 
9256
9754
  // ../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.9.0/node_modules/drizzle-orm/migrator.js
9257
9755
  import crypto4 from "crypto";
9258
- import fs2 from "fs";
9756
+ import fs3 from "fs";
9259
9757
  function readMigrationFiles(config) {
9260
9758
  const migrationFolderTo = config.migrationsFolder;
9261
9759
  const migrationQueries = [];
9262
9760
  const journalPath = `${migrationFolderTo}/meta/_journal.json`;
9263
- if (!fs2.existsSync(journalPath)) {
9761
+ if (!fs3.existsSync(journalPath)) {
9264
9762
  throw new Error(`Can't find meta/_journal.json file`);
9265
9763
  }
9266
- const journalAsString = fs2.readFileSync(`${migrationFolderTo}/meta/_journal.json`).toString();
9764
+ const journalAsString = fs3.readFileSync(`${migrationFolderTo}/meta/_journal.json`).toString();
9267
9765
  const journal = JSON.parse(journalAsString);
9268
9766
  for (const journalEntry of journal.entries) {
9269
9767
  const migrationPath = `${migrationFolderTo}/${journalEntry.tag}.sql`;
9270
9768
  try {
9271
- const query = fs2.readFileSync(`${migrationFolderTo}/${journalEntry.tag}.sql`).toString();
9769
+ const query = fs3.readFileSync(`${migrationFolderTo}/${journalEntry.tag}.sql`).toString();
9272
9770
  const result = query.split("--> statement-breakpoint").map((it) => {
9273
9771
  return it;
9274
9772
  });
@@ -9293,12 +9791,12 @@ function migrate(db, config) {
9293
9791
 
9294
9792
  // src/storage/openStore.ts
9295
9793
  function resolveMigrationsFolder(callerUrl, migrationsSubdir) {
9296
- const here = path2.dirname(fileURLToPath(callerUrl));
9794
+ const here = path4.dirname(fileURLToPath(callerUrl));
9297
9795
  const candidates = [
9298
- path2.join(here, "../..", migrationsSubdir),
9299
- path2.join(here, ".", migrationsSubdir)
9796
+ path4.join(here, "../..", migrationsSubdir),
9797
+ path4.join(here, ".", migrationsSubdir)
9300
9798
  ];
9301
- const found = candidates.find((p) => fs3.existsSync(p));
9799
+ const found = candidates.find((p) => fs4.existsSync(p));
9302
9800
  if (!found) {
9303
9801
  throw new Error(`Could not locate migrations folder "${migrationsSubdir}"`);
9304
9802
  }
@@ -9312,8 +9810,8 @@ function openStore({
9312
9810
  schema
9313
9811
  }) {
9314
9812
  const migrationsFolder = resolveMigrationsFolder(callerUrl, migrationsSubdir);
9315
- fs3.mkdirSync(dataDir2, { recursive: true });
9316
- const sqlite = new Database2(path2.join(dataDir2, dbFile));
9813
+ fs4.mkdirSync(dataDir2, { recursive: true });
9814
+ const sqlite = new Database2(path4.join(dataDir2, dbFile));
9317
9815
  const db = drizzle(sqlite, { schema });
9318
9816
  migrate(db, { migrationsFolder });
9319
9817
  return { db, sqlite };
@@ -9332,7 +9830,12 @@ __export(schema_exports, {
9332
9830
  var archiveMeta = sqliteTable("archive_meta", {
9333
9831
  id: integer("id").primaryKey(),
9334
9832
  archiveUuid: text("archive_uuid").notNull(),
9335
- createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull()
9833
+ createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
9834
+ // The normalize-output version this archive's Normalized layer was last built
9835
+ // at. Startup compares it to the code's NORMALIZE_VERSION and re-normalizes
9836
+ // from Raw once per bump, so a new block kind reaches dormant chats without
9837
+ // re-reading Source (ADR-0023). Additive, default 0 for existing archives.
9838
+ normalizeVersion: integer("normalize_version").notNull().default(0)
9336
9839
  });
9337
9840
  var schemaVersion = sqliteTable("schema_version", {
9338
9841
  version: integer("version").primaryKey(),
@@ -9401,6 +9904,13 @@ var messages = sqliteTable(
9401
9904
  ts: integer("ts", { mode: "timestamp_ms" }).notNull(),
9402
9905
  text: text("text").notNull(),
9403
9906
  blocks: text("blocks", { mode: "json" }).notNull(),
9907
+ // The model id the Agent recorded on this message (ADR-0023). Nullable:
9908
+ // reader turns record none, and neither do rows normalized before #195 —
9909
+ // those get backfilled by the next re-normalize pass.
9910
+ model: text("model"),
9911
+ // The reasoning effort the Agent recorded for this message (ADR-0023).
9912
+ // Nullable on the same terms as `model`: many turns record none.
9913
+ effort: text("effort"),
9404
9914
  rawId: integer("raw_id").notNull().references(() => rawMessages.id)
9405
9915
  },
9406
9916
  (t) => [
@@ -9440,6 +9950,16 @@ function createArchiveReadSeam(db) {
9440
9950
  listMessagesByChat(agent, sourceId) {
9441
9951
  return db.select().from(messages).where(and(eq(messages.agent, agent), eq(messages.sourceId, sourceId))).orderBy(asc(messages.ts)).all();
9442
9952
  },
9953
+ findRawPayloadForMessage(agent, sourceId, messageId) {
9954
+ const row = db.select({ rawPayload: rawMessages.rawPayload }).from(messages).innerJoin(rawMessages, eq(messages.rawId, rawMessages.id)).where(
9955
+ and(
9956
+ eq(messages.agent, agent),
9957
+ eq(messages.sourceId, sourceId),
9958
+ eq(messages.messageId, messageId)
9959
+ )
9960
+ ).get();
9961
+ return row?.rawPayload ?? null;
9962
+ },
9443
9963
  listChatTsRanges(opts) {
9444
9964
  const scope = opts?.sourceIds !== void 0 ? inArray(messages.sourceId, opts.sourceIds) : sql`1 = 1`;
9445
9965
  return db.select({
@@ -9486,6 +10006,15 @@ function createArchiveReadSeam(db) {
9486
10006
  },
9487
10007
  listIngestionEvents() {
9488
10008
  return db.select().from(ingestionEvents).all();
10009
+ },
10010
+ listRawMessages() {
10011
+ return db.select({
10012
+ id: rawMessages.id,
10013
+ agent: rawMessages.agent,
10014
+ sourceId: rawMessages.sourceId,
10015
+ sourcePath: rawMessages.sourcePath,
10016
+ rawPayload: rawMessages.rawPayload
10017
+ }).from(rawMessages).orderBy(asc(rawMessages.id)).all();
9489
10018
  }
9490
10019
  };
9491
10020
  }
@@ -9532,6 +10061,16 @@ function createArchiveRepository({
9532
10061
  }
9533
10062
  return row.archiveUuid;
9534
10063
  },
10064
+ getNormalizeVersion() {
10065
+ const row = db.select().from(archiveMeta).get();
10066
+ if (!row) {
10067
+ throw new Error("archive_meta row missing after initialization");
10068
+ }
10069
+ return row.normalizeVersion;
10070
+ },
10071
+ setNormalizeVersion(version2) {
10072
+ db.update(archiveMeta).set({ normalizeVersion: version2 }).where(eq(archiveMeta.id, 1)).run();
10073
+ },
9535
10074
  getAppliedMigrations() {
9536
10075
  return db.select().from(schemaVersion).all().map((r) => ({ version: r.version, appliedAt: r.appliedAt }));
9537
10076
  },
@@ -9615,6 +10154,8 @@ function createArchiveRepository({
9615
10154
  ts,
9616
10155
  text: message.text,
9617
10156
  blocks: message.blocks,
10157
+ model: message.model ?? null,
10158
+ effort: message.effort ?? null,
9618
10159
  rawId
9619
10160
  }).run();
9620
10161
  return true;
@@ -9625,6 +10166,8 @@ function createArchiveRepository({
9625
10166
  ts,
9626
10167
  text: message.text,
9627
10168
  blocks: message.blocks,
10169
+ model: message.model ?? null,
10170
+ effort: message.effort ?? null,
9628
10171
  rawId
9629
10172
  }).where(eq(messages.id, existing2.id)).run();
9630
10173
  return true;
@@ -9726,8 +10269,8 @@ function createCheckpointRepository({
9726
10269
  }
9727
10270
 
9728
10271
  // src/metadata/repository.ts
9729
- import fs4 from "fs";
9730
- import path3 from "path";
10272
+ import fs5 from "fs";
10273
+ import path5 from "path";
9731
10274
 
9732
10275
  // src/metadata/schema.ts
9733
10276
  var schema_exports3 = {};
@@ -9807,11 +10350,11 @@ function computeSortKey(input) {
9807
10350
  var DB_FILE = "metadata.db";
9808
10351
  var LEGACY_DB_FILE = "data.db";
9809
10352
  function migrateLegacyDbFile(dataDir2) {
9810
- const legacy = path3.join(dataDir2, LEGACY_DB_FILE);
9811
- const current = path3.join(dataDir2, DB_FILE);
9812
- if (!fs4.existsSync(legacy)) return;
9813
- if (fs4.existsSync(current)) return;
9814
- fs4.renameSync(legacy, current);
10353
+ const legacy = path5.join(dataDir2, LEGACY_DB_FILE);
10354
+ const current = path5.join(dataDir2, DB_FILE);
10355
+ if (!fs5.existsSync(legacy)) return;
10356
+ if (fs5.existsSync(current)) return;
10357
+ fs5.renameSync(legacy, current);
9815
10358
  }
9816
10359
  var CLAUDE_CODE_AGENT = "claude-code";
9817
10360
  function createMetadataRepository({
@@ -10055,7 +10598,7 @@ function createTagRepository({
10055
10598
  }
10056
10599
 
10057
10600
  // src/ingestion/ingest.ts
10058
- import fs5 from "fs";
10601
+ import fs6 from "fs";
10059
10602
  async function runIngestion(opts) {
10060
10603
  const now = opts.now ?? (() => /* @__PURE__ */ new Date());
10061
10604
  const changedChatIds = /* @__PURE__ */ new Set();
@@ -10125,7 +10668,7 @@ async function runIngestion(opts) {
10125
10668
  }
10126
10669
  function safeStat(p) {
10127
10670
  try {
10128
- return fs5.statSync(p);
10671
+ return fs6.statSync(p);
10129
10672
  } catch {
10130
10673
  return null;
10131
10674
  }
@@ -10144,6 +10687,55 @@ function startIngestionInBackground(opts, bg = {}) {
10144
10687
  return { done };
10145
10688
  }
10146
10689
 
10690
+ // src/ingestion/renormalize.ts
10691
+ function renormalizeFromRaw({
10692
+ plugins: plugins2,
10693
+ archive: archive2
10694
+ }) {
10695
+ const pluginById = new Map(plugins2.map((p) => [p.id, p]));
10696
+ const result = { scanned: 0, normalizedUpserted: 0 };
10697
+ for (const raw2 of archive2.read.listRawMessages()) {
10698
+ result.scanned += 1;
10699
+ const plugin = pluginById.get(raw2.agent);
10700
+ if (!plugin) continue;
10701
+ let payload;
10702
+ try {
10703
+ payload = JSON.parse(raw2.rawPayload);
10704
+ } catch {
10705
+ continue;
10706
+ }
10707
+ const record = {
10708
+ sourceId: raw2.sourceId,
10709
+ sourcePath: raw2.sourcePath,
10710
+ // Re-normalize works entirely off the stored payload; the plugin does not
10711
+ // read the file, so a synthetic locator is enough.
10712
+ sourceLocator: "renormalize",
10713
+ payload
10714
+ };
10715
+ const normalized = plugin.normalize(record);
10716
+ if (!normalized) continue;
10717
+ const upserted = archive2.upsertNormalizedMessage({
10718
+ agent: raw2.agent,
10719
+ sourceId: raw2.sourceId,
10720
+ message: normalized,
10721
+ rawId: raw2.id
10722
+ });
10723
+ if (upserted) result.normalizedUpserted += 1;
10724
+ }
10725
+ return result;
10726
+ }
10727
+ var NORMALIZE_VERSION = 11;
10728
+ function runRenormalizeIfStale({
10729
+ plugins: plugins2,
10730
+ archive: archive2,
10731
+ targetVersion = NORMALIZE_VERSION
10732
+ }) {
10733
+ if (archive2.getNormalizeVersion() >= targetVersion) return false;
10734
+ renormalizeFromRaw({ plugins: plugins2, archive: archive2 });
10735
+ archive2.setNormalizeVersion(targetVersion);
10736
+ return true;
10737
+ }
10738
+
10147
10739
  // ../node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
10148
10740
  import { EventEmitter } from "events";
10149
10741
  import { stat as statcb, Stats } from "fs";
@@ -10234,7 +10826,7 @@ var ReaddirpStream = class extends Readable3 {
10234
10826
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
10235
10827
  const statMethod = opts.lstat ? lstat : stat;
10236
10828
  if (wantBigintFsStats) {
10237
- this._stat = (path8) => statMethod(path8, { bigint: true });
10829
+ this._stat = (path9) => statMethod(path9, { bigint: true });
10238
10830
  } else {
10239
10831
  this._stat = statMethod;
10240
10832
  }
@@ -10259,8 +10851,8 @@ var ReaddirpStream = class extends Readable3 {
10259
10851
  const par = this.parent;
10260
10852
  const fil = par && par.files;
10261
10853
  if (fil && fil.length > 0) {
10262
- const { path: path8, depth } = par;
10263
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path8));
10854
+ const { path: path9, depth } = par;
10855
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path9));
10264
10856
  const awaited = await Promise.all(slice);
10265
10857
  for (const entry of awaited) {
10266
10858
  if (!entry)
@@ -10300,20 +10892,20 @@ var ReaddirpStream = class extends Readable3 {
10300
10892
  this.reading = false;
10301
10893
  }
10302
10894
  }
10303
- async _exploreDir(path8, depth) {
10895
+ async _exploreDir(path9, depth) {
10304
10896
  let files;
10305
10897
  try {
10306
- files = await readdir(path8, this._rdOptions);
10898
+ files = await readdir(path9, this._rdOptions);
10307
10899
  } catch (error) {
10308
10900
  this._onError(error);
10309
10901
  }
10310
- return { files, depth, path: path8 };
10902
+ return { files, depth, path: path9 };
10311
10903
  }
10312
- async _formatEntry(dirent, path8) {
10904
+ async _formatEntry(dirent, path9) {
10313
10905
  let entry;
10314
10906
  const basename3 = this._isDirent ? dirent.name : dirent;
10315
10907
  try {
10316
- const fullPath = presolve(pjoin(path8, basename3));
10908
+ const fullPath = presolve(pjoin(path9, basename3));
10317
10909
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename3 };
10318
10910
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
10319
10911
  } catch (err) {
@@ -10713,16 +11305,16 @@ var delFromSet = (main, prop, item) => {
10713
11305
  };
10714
11306
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
10715
11307
  var FsWatchInstances = /* @__PURE__ */ new Map();
10716
- function createFsWatchInstance(path8, options, listener, errHandler, emitRaw) {
11308
+ function createFsWatchInstance(path9, options, listener, errHandler, emitRaw) {
10717
11309
  const handleEvent = (rawEvent, evPath) => {
10718
- listener(path8);
10719
- emitRaw(rawEvent, evPath, { watchedPath: path8 });
10720
- if (evPath && path8 !== evPath) {
10721
- fsWatchBroadcast(sp.resolve(path8, evPath), KEY_LISTENERS, sp.join(path8, evPath));
11310
+ listener(path9);
11311
+ emitRaw(rawEvent, evPath, { watchedPath: path9 });
11312
+ if (evPath && path9 !== evPath) {
11313
+ fsWatchBroadcast(sp.resolve(path9, evPath), KEY_LISTENERS, sp.join(path9, evPath));
10722
11314
  }
10723
11315
  };
10724
11316
  try {
10725
- return fs_watch(path8, {
11317
+ return fs_watch(path9, {
10726
11318
  persistent: options.persistent
10727
11319
  }, handleEvent);
10728
11320
  } catch (error) {
@@ -10738,12 +11330,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
10738
11330
  listener(val1, val2, val3);
10739
11331
  });
10740
11332
  };
10741
- var setFsWatchListener = (path8, fullPath, options, handlers) => {
11333
+ var setFsWatchListener = (path9, fullPath, options, handlers) => {
10742
11334
  const { listener, errHandler, rawEmitter } = handlers;
10743
11335
  let cont = FsWatchInstances.get(fullPath);
10744
11336
  let watcher2;
10745
11337
  if (!options.persistent) {
10746
- watcher2 = createFsWatchInstance(path8, options, listener, errHandler, rawEmitter);
11338
+ watcher2 = createFsWatchInstance(path9, options, listener, errHandler, rawEmitter);
10747
11339
  if (!watcher2)
10748
11340
  return;
10749
11341
  return watcher2.close.bind(watcher2);
@@ -10754,7 +11346,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
10754
11346
  addAndConvert(cont, KEY_RAW, rawEmitter);
10755
11347
  } else {
10756
11348
  watcher2 = createFsWatchInstance(
10757
- path8,
11349
+ path9,
10758
11350
  options,
10759
11351
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
10760
11352
  errHandler,
@@ -10769,7 +11361,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
10769
11361
  cont.watcherUnusable = true;
10770
11362
  if (isWindows && error.code === "EPERM") {
10771
11363
  try {
10772
- const fd = await open(path8, "r");
11364
+ const fd = await open(path9, "r");
10773
11365
  await fd.close();
10774
11366
  broadcastErr(error);
10775
11367
  } catch (err) {
@@ -10800,7 +11392,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
10800
11392
  };
10801
11393
  };
10802
11394
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
10803
- var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
11395
+ var setFsWatchFileListener = (path9, fullPath, options, handlers) => {
10804
11396
  const { listener, rawEmitter } = handlers;
10805
11397
  let cont = FsWatchFileInstances.get(fullPath);
10806
11398
  const copts = cont && cont.options;
@@ -10822,7 +11414,7 @@ var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
10822
11414
  });
10823
11415
  const currmtime = curr.mtimeMs;
10824
11416
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
10825
- foreach(cont.listeners, (listener2) => listener2(path8, curr));
11417
+ foreach(cont.listeners, (listener2) => listener2(path9, curr));
10826
11418
  }
10827
11419
  })
10828
11420
  };
@@ -10852,13 +11444,13 @@ var NodeFsHandler = class {
10852
11444
  * @param listener on fs change
10853
11445
  * @returns closer for the watcher instance
10854
11446
  */
10855
- _watchWithNodeFs(path8, listener) {
11447
+ _watchWithNodeFs(path9, listener) {
10856
11448
  const opts = this.fsw.options;
10857
- const directory = sp.dirname(path8);
10858
- const basename3 = sp.basename(path8);
11449
+ const directory = sp.dirname(path9);
11450
+ const basename3 = sp.basename(path9);
10859
11451
  const parent = this.fsw._getWatchedDir(directory);
10860
11452
  parent.add(basename3);
10861
- const absolutePath = sp.resolve(path8);
11453
+ const absolutePath = sp.resolve(path9);
10862
11454
  const options = {
10863
11455
  persistent: opts.persistent
10864
11456
  };
@@ -10868,12 +11460,12 @@ var NodeFsHandler = class {
10868
11460
  if (opts.usePolling) {
10869
11461
  const enableBin = opts.interval !== opts.binaryInterval;
10870
11462
  options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
10871
- closer = setFsWatchFileListener(path8, absolutePath, options, {
11463
+ closer = setFsWatchFileListener(path9, absolutePath, options, {
10872
11464
  listener,
10873
11465
  rawEmitter: this.fsw._emitRaw
10874
11466
  });
10875
11467
  } else {
10876
- closer = setFsWatchListener(path8, absolutePath, options, {
11468
+ closer = setFsWatchListener(path9, absolutePath, options, {
10877
11469
  listener,
10878
11470
  errHandler: this._boundHandleError,
10879
11471
  rawEmitter: this.fsw._emitRaw
@@ -10895,7 +11487,7 @@ var NodeFsHandler = class {
10895
11487
  let prevStats = stats;
10896
11488
  if (parent.has(basename3))
10897
11489
  return;
10898
- const listener = async (path8, newStats) => {
11490
+ const listener = async (path9, newStats) => {
10899
11491
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
10900
11492
  return;
10901
11493
  if (!newStats || newStats.mtimeMs === 0) {
@@ -10909,11 +11501,11 @@ var NodeFsHandler = class {
10909
11501
  this.fsw._emit(EV.CHANGE, file, newStats2);
10910
11502
  }
10911
11503
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
10912
- this.fsw._closeFile(path8);
11504
+ this.fsw._closeFile(path9);
10913
11505
  prevStats = newStats2;
10914
11506
  const closer2 = this._watchWithNodeFs(file, listener);
10915
11507
  if (closer2)
10916
- this.fsw._addPathCloser(path8, closer2);
11508
+ this.fsw._addPathCloser(path9, closer2);
10917
11509
  } else {
10918
11510
  prevStats = newStats2;
10919
11511
  }
@@ -10945,7 +11537,7 @@ var NodeFsHandler = class {
10945
11537
  * @param item basename of this item
10946
11538
  * @returns true if no more processing is needed for this entry.
10947
11539
  */
10948
- async _handleSymlink(entry, directory, path8, item) {
11540
+ async _handleSymlink(entry, directory, path9, item) {
10949
11541
  if (this.fsw.closed) {
10950
11542
  return;
10951
11543
  }
@@ -10955,7 +11547,7 @@ var NodeFsHandler = class {
10955
11547
  this.fsw._incrReadyCount();
10956
11548
  let linkPath;
10957
11549
  try {
10958
- linkPath = await fsrealpath(path8);
11550
+ linkPath = await fsrealpath(path9);
10959
11551
  } catch (e) {
10960
11552
  this.fsw._emitReady();
10961
11553
  return true;
@@ -10965,12 +11557,12 @@ var NodeFsHandler = class {
10965
11557
  if (dir.has(item)) {
10966
11558
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
10967
11559
  this.fsw._symlinkPaths.set(full, linkPath);
10968
- this.fsw._emit(EV.CHANGE, path8, entry.stats);
11560
+ this.fsw._emit(EV.CHANGE, path9, entry.stats);
10969
11561
  }
10970
11562
  } else {
10971
11563
  dir.add(item);
10972
11564
  this.fsw._symlinkPaths.set(full, linkPath);
10973
- this.fsw._emit(EV.ADD, path8, entry.stats);
11565
+ this.fsw._emit(EV.ADD, path9, entry.stats);
10974
11566
  }
10975
11567
  this.fsw._emitReady();
10976
11568
  return true;
@@ -11000,9 +11592,9 @@ var NodeFsHandler = class {
11000
11592
  return;
11001
11593
  }
11002
11594
  const item = entry.path;
11003
- let path8 = sp.join(directory, item);
11595
+ let path9 = sp.join(directory, item);
11004
11596
  current.add(item);
11005
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path8, item)) {
11597
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path9, item)) {
11006
11598
  return;
11007
11599
  }
11008
11600
  if (this.fsw.closed) {
@@ -11011,8 +11603,8 @@ var NodeFsHandler = class {
11011
11603
  }
11012
11604
  if (item === target || !target && !previous.has(item)) {
11013
11605
  this.fsw._incrReadyCount();
11014
- path8 = sp.join(dir, sp.relative(dir, path8));
11015
- this._addToNodeFs(path8, initialAdd, wh, depth + 1);
11606
+ path9 = sp.join(dir, sp.relative(dir, path9));
11607
+ this._addToNodeFs(path9, initialAdd, wh, depth + 1);
11016
11608
  }
11017
11609
  }).on(EV.ERROR, this._boundHandleError);
11018
11610
  return new Promise((resolve3, reject) => {
@@ -11081,13 +11673,13 @@ var NodeFsHandler = class {
11081
11673
  * @param depth Child path actually targeted for watch
11082
11674
  * @param target Child path actually targeted for watch
11083
11675
  */
11084
- async _addToNodeFs(path8, initialAdd, priorWh, depth, target) {
11676
+ async _addToNodeFs(path9, initialAdd, priorWh, depth, target) {
11085
11677
  const ready = this.fsw._emitReady;
11086
- if (this.fsw._isIgnored(path8) || this.fsw.closed) {
11678
+ if (this.fsw._isIgnored(path9) || this.fsw.closed) {
11087
11679
  ready();
11088
11680
  return false;
11089
11681
  }
11090
- const wh = this.fsw._getWatchHelpers(path8);
11682
+ const wh = this.fsw._getWatchHelpers(path9);
11091
11683
  if (priorWh) {
11092
11684
  wh.filterPath = (entry) => priorWh.filterPath(entry);
11093
11685
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -11103,8 +11695,8 @@ var NodeFsHandler = class {
11103
11695
  const follow = this.fsw.options.followSymlinks;
11104
11696
  let closer;
11105
11697
  if (stats.isDirectory()) {
11106
- const absPath = sp.resolve(path8);
11107
- const targetPath = follow ? await fsrealpath(path8) : path8;
11698
+ const absPath = sp.resolve(path9);
11699
+ const targetPath = follow ? await fsrealpath(path9) : path9;
11108
11700
  if (this.fsw.closed)
11109
11701
  return;
11110
11702
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -11114,29 +11706,29 @@ var NodeFsHandler = class {
11114
11706
  this.fsw._symlinkPaths.set(absPath, targetPath);
11115
11707
  }
11116
11708
  } else if (stats.isSymbolicLink()) {
11117
- const targetPath = follow ? await fsrealpath(path8) : path8;
11709
+ const targetPath = follow ? await fsrealpath(path9) : path9;
11118
11710
  if (this.fsw.closed)
11119
11711
  return;
11120
11712
  const parent = sp.dirname(wh.watchPath);
11121
11713
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
11122
11714
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
11123
- closer = await this._handleDir(parent, stats, initialAdd, depth, path8, wh, targetPath);
11715
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path9, wh, targetPath);
11124
11716
  if (this.fsw.closed)
11125
11717
  return;
11126
11718
  if (targetPath !== void 0) {
11127
- this.fsw._symlinkPaths.set(sp.resolve(path8), targetPath);
11719
+ this.fsw._symlinkPaths.set(sp.resolve(path9), targetPath);
11128
11720
  }
11129
11721
  } else {
11130
11722
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
11131
11723
  }
11132
11724
  ready();
11133
11725
  if (closer)
11134
- this.fsw._addPathCloser(path8, closer);
11726
+ this.fsw._addPathCloser(path9, closer);
11135
11727
  return false;
11136
11728
  } catch (error) {
11137
11729
  if (this.fsw._handleError(error)) {
11138
11730
  ready();
11139
- return path8;
11731
+ return path9;
11140
11732
  }
11141
11733
  }
11142
11734
  }
@@ -11179,24 +11771,24 @@ function createPattern(matcher) {
11179
11771
  }
11180
11772
  return () => false;
11181
11773
  }
11182
- function normalizePath(path8) {
11183
- if (typeof path8 !== "string")
11774
+ function normalizePath(path9) {
11775
+ if (typeof path9 !== "string")
11184
11776
  throw new Error("string expected");
11185
- path8 = sp2.normalize(path8);
11186
- path8 = path8.replace(/\\/g, "/");
11777
+ path9 = sp2.normalize(path9);
11778
+ path9 = path9.replace(/\\/g, "/");
11187
11779
  let prepend = false;
11188
- if (path8.startsWith("//"))
11780
+ if (path9.startsWith("//"))
11189
11781
  prepend = true;
11190
- path8 = path8.replace(DOUBLE_SLASH_RE, "/");
11782
+ path9 = path9.replace(DOUBLE_SLASH_RE, "/");
11191
11783
  if (prepend)
11192
- path8 = "/" + path8;
11193
- return path8;
11784
+ path9 = "/" + path9;
11785
+ return path9;
11194
11786
  }
11195
11787
  function matchPatterns(patterns, testString, stats) {
11196
- const path8 = normalizePath(testString);
11788
+ const path9 = normalizePath(testString);
11197
11789
  for (let index2 = 0; index2 < patterns.length; index2++) {
11198
11790
  const pattern = patterns[index2];
11199
- if (pattern(path8, stats)) {
11791
+ if (pattern(path9, stats)) {
11200
11792
  return true;
11201
11793
  }
11202
11794
  }
@@ -11234,19 +11826,19 @@ var toUnix = (string) => {
11234
11826
  }
11235
11827
  return str;
11236
11828
  };
11237
- var normalizePathToUnix = (path8) => toUnix(sp2.normalize(toUnix(path8)));
11238
- var normalizeIgnored = (cwd = "") => (path8) => {
11239
- if (typeof path8 === "string") {
11240
- return normalizePathToUnix(sp2.isAbsolute(path8) ? path8 : sp2.join(cwd, path8));
11829
+ var normalizePathToUnix = (path9) => toUnix(sp2.normalize(toUnix(path9)));
11830
+ var normalizeIgnored = (cwd = "") => (path9) => {
11831
+ if (typeof path9 === "string") {
11832
+ return normalizePathToUnix(sp2.isAbsolute(path9) ? path9 : sp2.join(cwd, path9));
11241
11833
  } else {
11242
- return path8;
11834
+ return path9;
11243
11835
  }
11244
11836
  };
11245
- var getAbsolutePath = (path8, cwd) => {
11246
- if (sp2.isAbsolute(path8)) {
11247
- return path8;
11837
+ var getAbsolutePath = (path9, cwd) => {
11838
+ if (sp2.isAbsolute(path9)) {
11839
+ return path9;
11248
11840
  }
11249
- return sp2.join(cwd, path8);
11841
+ return sp2.join(cwd, path9);
11250
11842
  };
11251
11843
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
11252
11844
  var DirEntry = class {
@@ -11311,10 +11903,10 @@ var WatchHelper = class {
11311
11903
  dirParts;
11312
11904
  followSymlinks;
11313
11905
  statMethod;
11314
- constructor(path8, follow, fsw) {
11906
+ constructor(path9, follow, fsw) {
11315
11907
  this.fsw = fsw;
11316
- const watchPath = path8;
11317
- this.path = path8 = path8.replace(REPLACER_RE, "");
11908
+ const watchPath = path9;
11909
+ this.path = path9 = path9.replace(REPLACER_RE, "");
11318
11910
  this.watchPath = watchPath;
11319
11911
  this.fullWatchPath = sp2.resolve(watchPath);
11320
11912
  this.dirParts = [];
@@ -11454,20 +12046,20 @@ var FSWatcher = class extends EventEmitter {
11454
12046
  this._closePromise = void 0;
11455
12047
  let paths = unifyPaths(paths_);
11456
12048
  if (cwd) {
11457
- paths = paths.map((path8) => {
11458
- const absPath = getAbsolutePath(path8, cwd);
12049
+ paths = paths.map((path9) => {
12050
+ const absPath = getAbsolutePath(path9, cwd);
11459
12051
  return absPath;
11460
12052
  });
11461
12053
  }
11462
- paths.forEach((path8) => {
11463
- this._removeIgnoredPath(path8);
12054
+ paths.forEach((path9) => {
12055
+ this._removeIgnoredPath(path9);
11464
12056
  });
11465
12057
  this._userIgnored = void 0;
11466
12058
  if (!this._readyCount)
11467
12059
  this._readyCount = 0;
11468
12060
  this._readyCount += paths.length;
11469
- Promise.all(paths.map(async (path8) => {
11470
- const res = await this._nodeFsHandler._addToNodeFs(path8, !_internal, void 0, 0, _origAdd);
12061
+ Promise.all(paths.map(async (path9) => {
12062
+ const res = await this._nodeFsHandler._addToNodeFs(path9, !_internal, void 0, 0, _origAdd);
11471
12063
  if (res)
11472
12064
  this._emitReady();
11473
12065
  return res;
@@ -11489,17 +12081,17 @@ var FSWatcher = class extends EventEmitter {
11489
12081
  return this;
11490
12082
  const paths = unifyPaths(paths_);
11491
12083
  const { cwd } = this.options;
11492
- paths.forEach((path8) => {
11493
- if (!sp2.isAbsolute(path8) && !this._closers.has(path8)) {
12084
+ paths.forEach((path9) => {
12085
+ if (!sp2.isAbsolute(path9) && !this._closers.has(path9)) {
11494
12086
  if (cwd)
11495
- path8 = sp2.join(cwd, path8);
11496
- path8 = sp2.resolve(path8);
12087
+ path9 = sp2.join(cwd, path9);
12088
+ path9 = sp2.resolve(path9);
11497
12089
  }
11498
- this._closePath(path8);
11499
- this._addIgnoredPath(path8);
11500
- if (this._watched.has(path8)) {
12090
+ this._closePath(path9);
12091
+ this._addIgnoredPath(path9);
12092
+ if (this._watched.has(path9)) {
11501
12093
  this._addIgnoredPath({
11502
- path: path8,
12094
+ path: path9,
11503
12095
  recursive: true
11504
12096
  });
11505
12097
  }
@@ -11563,38 +12155,38 @@ var FSWatcher = class extends EventEmitter {
11563
12155
  * @param stats arguments to be passed with event
11564
12156
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
11565
12157
  */
11566
- async _emit(event, path8, stats) {
12158
+ async _emit(event, path9, stats) {
11567
12159
  if (this.closed)
11568
12160
  return;
11569
12161
  const opts = this.options;
11570
12162
  if (isWindows)
11571
- path8 = sp2.normalize(path8);
12163
+ path9 = sp2.normalize(path9);
11572
12164
  if (opts.cwd)
11573
- path8 = sp2.relative(opts.cwd, path8);
11574
- const args = [path8];
12165
+ path9 = sp2.relative(opts.cwd, path9);
12166
+ const args = [path9];
11575
12167
  if (stats != null)
11576
12168
  args.push(stats);
11577
12169
  const awf = opts.awaitWriteFinish;
11578
12170
  let pw;
11579
- if (awf && (pw = this._pendingWrites.get(path8))) {
12171
+ if (awf && (pw = this._pendingWrites.get(path9))) {
11580
12172
  pw.lastChange = /* @__PURE__ */ new Date();
11581
12173
  return this;
11582
12174
  }
11583
12175
  if (opts.atomic) {
11584
12176
  if (event === EVENTS.UNLINK) {
11585
- this._pendingUnlinks.set(path8, [event, ...args]);
12177
+ this._pendingUnlinks.set(path9, [event, ...args]);
11586
12178
  setTimeout(() => {
11587
- this._pendingUnlinks.forEach((entry, path9) => {
12179
+ this._pendingUnlinks.forEach((entry, path10) => {
11588
12180
  this.emit(...entry);
11589
12181
  this.emit(EVENTS.ALL, ...entry);
11590
- this._pendingUnlinks.delete(path9);
12182
+ this._pendingUnlinks.delete(path10);
11591
12183
  });
11592
12184
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
11593
12185
  return this;
11594
12186
  }
11595
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path8)) {
12187
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path9)) {
11596
12188
  event = EVENTS.CHANGE;
11597
- this._pendingUnlinks.delete(path8);
12189
+ this._pendingUnlinks.delete(path9);
11598
12190
  }
11599
12191
  }
11600
12192
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -11612,16 +12204,16 @@ var FSWatcher = class extends EventEmitter {
11612
12204
  this.emitWithAll(event, args);
11613
12205
  }
11614
12206
  };
11615
- this._awaitWriteFinish(path8, awf.stabilityThreshold, event, awfEmit);
12207
+ this._awaitWriteFinish(path9, awf.stabilityThreshold, event, awfEmit);
11616
12208
  return this;
11617
12209
  }
11618
12210
  if (event === EVENTS.CHANGE) {
11619
- const isThrottled = !this._throttle(EVENTS.CHANGE, path8, 50);
12211
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path9, 50);
11620
12212
  if (isThrottled)
11621
12213
  return this;
11622
12214
  }
11623
12215
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
11624
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path8) : path8;
12216
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path9) : path9;
11625
12217
  let stats2;
11626
12218
  try {
11627
12219
  stats2 = await stat3(fullPath);
@@ -11652,23 +12244,23 @@ var FSWatcher = class extends EventEmitter {
11652
12244
  * @param timeout duration of time to suppress duplicate actions
11653
12245
  * @returns tracking object or false if action should be suppressed
11654
12246
  */
11655
- _throttle(actionType, path8, timeout) {
12247
+ _throttle(actionType, path9, timeout) {
11656
12248
  if (!this._throttled.has(actionType)) {
11657
12249
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
11658
12250
  }
11659
12251
  const action2 = this._throttled.get(actionType);
11660
12252
  if (!action2)
11661
12253
  throw new Error("invalid throttle");
11662
- const actionPath = action2.get(path8);
12254
+ const actionPath = action2.get(path9);
11663
12255
  if (actionPath) {
11664
12256
  actionPath.count++;
11665
12257
  return false;
11666
12258
  }
11667
12259
  let timeoutObject;
11668
12260
  const clear = () => {
11669
- const item = action2.get(path8);
12261
+ const item = action2.get(path9);
11670
12262
  const count = item ? item.count : 0;
11671
- action2.delete(path8);
12263
+ action2.delete(path9);
11672
12264
  clearTimeout(timeoutObject);
11673
12265
  if (item)
11674
12266
  clearTimeout(item.timeoutObject);
@@ -11676,7 +12268,7 @@ var FSWatcher = class extends EventEmitter {
11676
12268
  };
11677
12269
  timeoutObject = setTimeout(clear, timeout);
11678
12270
  const thr = { timeoutObject, clear, count: 0 };
11679
- action2.set(path8, thr);
12271
+ action2.set(path9, thr);
11680
12272
  return thr;
11681
12273
  }
11682
12274
  _incrReadyCount() {
@@ -11690,44 +12282,44 @@ var FSWatcher = class extends EventEmitter {
11690
12282
  * @param event
11691
12283
  * @param awfEmit Callback to be called when ready for event to be emitted.
11692
12284
  */
11693
- _awaitWriteFinish(path8, threshold, event, awfEmit) {
12285
+ _awaitWriteFinish(path9, threshold, event, awfEmit) {
11694
12286
  const awf = this.options.awaitWriteFinish;
11695
12287
  if (typeof awf !== "object")
11696
12288
  return;
11697
12289
  const pollInterval = awf.pollInterval;
11698
12290
  let timeoutHandler;
11699
- let fullPath = path8;
11700
- if (this.options.cwd && !sp2.isAbsolute(path8)) {
11701
- fullPath = sp2.join(this.options.cwd, path8);
12291
+ let fullPath = path9;
12292
+ if (this.options.cwd && !sp2.isAbsolute(path9)) {
12293
+ fullPath = sp2.join(this.options.cwd, path9);
11702
12294
  }
11703
12295
  const now = /* @__PURE__ */ new Date();
11704
12296
  const writes = this._pendingWrites;
11705
12297
  function awaitWriteFinishFn(prevStat) {
11706
12298
  statcb(fullPath, (err, curStat) => {
11707
- if (err || !writes.has(path8)) {
12299
+ if (err || !writes.has(path9)) {
11708
12300
  if (err && err.code !== "ENOENT")
11709
12301
  awfEmit(err);
11710
12302
  return;
11711
12303
  }
11712
12304
  const now2 = Number(/* @__PURE__ */ new Date());
11713
12305
  if (prevStat && curStat.size !== prevStat.size) {
11714
- writes.get(path8).lastChange = now2;
12306
+ writes.get(path9).lastChange = now2;
11715
12307
  }
11716
- const pw = writes.get(path8);
12308
+ const pw = writes.get(path9);
11717
12309
  const df = now2 - pw.lastChange;
11718
12310
  if (df >= threshold) {
11719
- writes.delete(path8);
12311
+ writes.delete(path9);
11720
12312
  awfEmit(void 0, curStat);
11721
12313
  } else {
11722
12314
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
11723
12315
  }
11724
12316
  });
11725
12317
  }
11726
- if (!writes.has(path8)) {
11727
- writes.set(path8, {
12318
+ if (!writes.has(path9)) {
12319
+ writes.set(path9, {
11728
12320
  lastChange: now,
11729
12321
  cancelWait: () => {
11730
- writes.delete(path8);
12322
+ writes.delete(path9);
11731
12323
  clearTimeout(timeoutHandler);
11732
12324
  return event;
11733
12325
  }
@@ -11738,8 +12330,8 @@ var FSWatcher = class extends EventEmitter {
11738
12330
  /**
11739
12331
  * Determines whether user has asked to ignore this path.
11740
12332
  */
11741
- _isIgnored(path8, stats) {
11742
- if (this.options.atomic && DOT_RE.test(path8))
12333
+ _isIgnored(path9, stats) {
12334
+ if (this.options.atomic && DOT_RE.test(path9))
11743
12335
  return true;
11744
12336
  if (!this._userIgnored) {
11745
12337
  const { cwd } = this.options;
@@ -11749,17 +12341,17 @@ var FSWatcher = class extends EventEmitter {
11749
12341
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
11750
12342
  this._userIgnored = anymatch(list, void 0);
11751
12343
  }
11752
- return this._userIgnored(path8, stats);
12344
+ return this._userIgnored(path9, stats);
11753
12345
  }
11754
- _isntIgnored(path8, stat4) {
11755
- return !this._isIgnored(path8, stat4);
12346
+ _isntIgnored(path9, stat4) {
12347
+ return !this._isIgnored(path9, stat4);
11756
12348
  }
11757
12349
  /**
11758
12350
  * Provides a set of common helpers and properties relating to symlink handling.
11759
12351
  * @param path file or directory pattern being watched
11760
12352
  */
11761
- _getWatchHelpers(path8) {
11762
- return new WatchHelper(path8, this.options.followSymlinks, this);
12353
+ _getWatchHelpers(path9) {
12354
+ return new WatchHelper(path9, this.options.followSymlinks, this);
11763
12355
  }
11764
12356
  // Directory helpers
11765
12357
  // -----------------
@@ -11791,63 +12383,63 @@ var FSWatcher = class extends EventEmitter {
11791
12383
  * @param item base path of item/directory
11792
12384
  */
11793
12385
  _remove(directory, item, isDirectory) {
11794
- const path8 = sp2.join(directory, item);
11795
- const fullPath = sp2.resolve(path8);
11796
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path8) || this._watched.has(fullPath);
11797
- if (!this._throttle("remove", path8, 100))
12386
+ const path9 = sp2.join(directory, item);
12387
+ const fullPath = sp2.resolve(path9);
12388
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path9) || this._watched.has(fullPath);
12389
+ if (!this._throttle("remove", path9, 100))
11798
12390
  return;
11799
12391
  if (!isDirectory && this._watched.size === 1) {
11800
12392
  this.add(directory, item, true);
11801
12393
  }
11802
- const wp = this._getWatchedDir(path8);
12394
+ const wp = this._getWatchedDir(path9);
11803
12395
  const nestedDirectoryChildren = wp.getChildren();
11804
- nestedDirectoryChildren.forEach((nested) => this._remove(path8, nested));
12396
+ nestedDirectoryChildren.forEach((nested) => this._remove(path9, nested));
11805
12397
  const parent = this._getWatchedDir(directory);
11806
12398
  const wasTracked = parent.has(item);
11807
12399
  parent.remove(item);
11808
12400
  if (this._symlinkPaths.has(fullPath)) {
11809
12401
  this._symlinkPaths.delete(fullPath);
11810
12402
  }
11811
- let relPath = path8;
12403
+ let relPath = path9;
11812
12404
  if (this.options.cwd)
11813
- relPath = sp2.relative(this.options.cwd, path8);
12405
+ relPath = sp2.relative(this.options.cwd, path9);
11814
12406
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
11815
12407
  const event = this._pendingWrites.get(relPath).cancelWait();
11816
12408
  if (event === EVENTS.ADD)
11817
12409
  return;
11818
12410
  }
11819
- this._watched.delete(path8);
12411
+ this._watched.delete(path9);
11820
12412
  this._watched.delete(fullPath);
11821
12413
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
11822
- if (wasTracked && !this._isIgnored(path8))
11823
- this._emit(eventName, path8);
11824
- this._closePath(path8);
12414
+ if (wasTracked && !this._isIgnored(path9))
12415
+ this._emit(eventName, path9);
12416
+ this._closePath(path9);
11825
12417
  }
11826
12418
  /**
11827
12419
  * Closes all watchers for a path
11828
12420
  */
11829
- _closePath(path8) {
11830
- this._closeFile(path8);
11831
- const dir = sp2.dirname(path8);
11832
- this._getWatchedDir(dir).remove(sp2.basename(path8));
12421
+ _closePath(path9) {
12422
+ this._closeFile(path9);
12423
+ const dir = sp2.dirname(path9);
12424
+ this._getWatchedDir(dir).remove(sp2.basename(path9));
11833
12425
  }
11834
12426
  /**
11835
12427
  * Closes only file-specific watchers
11836
12428
  */
11837
- _closeFile(path8) {
11838
- const closers = this._closers.get(path8);
12429
+ _closeFile(path9) {
12430
+ const closers = this._closers.get(path9);
11839
12431
  if (!closers)
11840
12432
  return;
11841
12433
  closers.forEach((closer) => closer());
11842
- this._closers.delete(path8);
12434
+ this._closers.delete(path9);
11843
12435
  }
11844
- _addPathCloser(path8, closer) {
12436
+ _addPathCloser(path9, closer) {
11845
12437
  if (!closer)
11846
12438
  return;
11847
- let list = this._closers.get(path8);
12439
+ let list = this._closers.get(path9);
11848
12440
  if (!list) {
11849
12441
  list = [];
11850
- this._closers.set(path8, list);
12442
+ this._closers.set(path9, list);
11851
12443
  }
11852
12444
  list.push(closer);
11853
12445
  }
@@ -11945,13 +12537,13 @@ function startWatcher(opts) {
11945
12537
  }
11946
12538
  return {
11947
12539
  ready,
11948
- notifyChange(path8) {
11949
- if (watcher2) watcher2.emit("change", path8);
11950
- else scheduleIngest(path8);
12540
+ notifyChange(path9) {
12541
+ if (watcher2) watcher2.emit("change", path9);
12542
+ else scheduleIngest(path9);
11951
12543
  },
11952
- notifyUnlink(path8) {
11953
- if (watcher2) watcher2.emit("unlink", path8);
11954
- else recordUnlink(path8);
12544
+ notifyUnlink(path9) {
12545
+ if (watcher2) watcher2.emit("unlink", path9);
12546
+ else recordUnlink(path9);
11955
12547
  },
11956
12548
  async close() {
11957
12549
  closed = true;
@@ -11962,139 +12554,6 @@ function startWatcher(opts) {
11962
12554
  };
11963
12555
  }
11964
12556
 
11965
- // src/plugins/claude-code/plugin.ts
11966
- import fs6 from "fs";
11967
- import path4 from "path";
11968
- import readline from "readline";
11969
- var ClaudeCodePlugin = class {
11970
- id = "claude-code";
11971
- displayName = "Claude Code";
11972
- async *discover(env) {
11973
- const projectsDir = path4.join(env.homeDir, ".claude", "projects");
11974
- if (!fs6.existsSync(projectsDir)) return;
11975
- const projects = fs6.readdirSync(projectsDir, { withFileTypes: true });
11976
- for (const project of projects) {
11977
- if (!project.isDirectory()) continue;
11978
- const projectPath = path4.join(projectsDir, project.name);
11979
- const files = fs6.readdirSync(projectPath, { withFileTypes: true });
11980
- for (const file of files) {
11981
- if (!file.isFile() || !file.name.endsWith(".jsonl")) continue;
11982
- const sourcePath = path4.join(projectPath, file.name);
11983
- const sourceId = file.name.replace(/\.jsonl$/, "");
11984
- const cwd = await readCwdFromJsonl(sourcePath);
11985
- yield {
11986
- sourceId,
11987
- sourcePath,
11988
- watchPaths: [sourcePath],
11989
- project: cwd ? path4.basename(cwd) : void 0,
11990
- projectPath: cwd ?? void 0
11991
- };
11992
- }
11993
- }
11994
- }
11995
- async *extractRaw(ref) {
11996
- if (!fs6.existsSync(ref.sourcePath)) return;
11997
- const stream2 = fs6.createReadStream(ref.sourcePath, { encoding: "utf-8" });
11998
- const rl = readline.createInterface({ input: stream2, crlfDelay: Infinity });
11999
- let lineNo = 0;
12000
- for await (const line of rl) {
12001
- lineNo += 1;
12002
- if (!line) continue;
12003
- yield {
12004
- sourceId: ref.sourceId,
12005
- sourcePath: ref.sourcePath,
12006
- sourceLocator: `L${lineNo}`,
12007
- payload: JSON.parse(line)
12008
- };
12009
- }
12010
- }
12011
- normalize(raw2) {
12012
- const payload = raw2.payload;
12013
- if (!payload || typeof payload !== "object") return null;
12014
- if (payload.type !== "user" && payload.type !== "assistant") return null;
12015
- if (payload.isMeta === true) return null;
12016
- if (payload.isSidechain === true) return null;
12017
- const message = payload.message;
12018
- if (!message) return null;
12019
- const role = message.role === "assistant" ? "assistant" : "user";
12020
- const messageId = String(payload.uuid ?? "");
12021
- const ts = String(payload.timestamp ?? "");
12022
- if (typeof message.content === "string") {
12023
- const text2 = message.content;
12024
- return {
12025
- messageId,
12026
- role,
12027
- ts,
12028
- text: text2,
12029
- blocks: [{ type: "text", text: text2 }]
12030
- };
12031
- }
12032
- if (Array.isArray(message.content)) {
12033
- const blocks = [];
12034
- for (const block of message.content) {
12035
- const normalized = normalizeBlock(block);
12036
- if (normalized) blocks.push(normalized);
12037
- }
12038
- const text2 = blocks.filter((b) => b.type === "text").map((b) => b.text).join("\n");
12039
- return { messageId, role, ts, text: text2, blocks };
12040
- }
12041
- return null;
12042
- }
12043
- };
12044
- async function readCwdFromJsonl(sourcePath) {
12045
- try {
12046
- const stream2 = fs6.createReadStream(sourcePath, { encoding: "utf-8" });
12047
- const rl = readline.createInterface({ input: stream2, crlfDelay: Infinity });
12048
- try {
12049
- for await (const line of rl) {
12050
- if (!line) continue;
12051
- let obj;
12052
- try {
12053
- obj = JSON.parse(line);
12054
- } catch {
12055
- continue;
12056
- }
12057
- if (typeof obj.cwd === "string" && obj.cwd.length > 0) {
12058
- return obj.cwd;
12059
- }
12060
- }
12061
- } finally {
12062
- rl.close();
12063
- stream2.destroy();
12064
- }
12065
- } catch {
12066
- }
12067
- return void 0;
12068
- }
12069
- function normalizeBlock(raw2) {
12070
- if (!raw2 || typeof raw2 !== "object") return null;
12071
- const b = raw2;
12072
- switch (b.type) {
12073
- case "text":
12074
- return { type: "text", text: String(b.text ?? "") };
12075
- case "thinking":
12076
- return { type: "thinking", thinking: String(b.thinking ?? "") };
12077
- case "tool_use":
12078
- return {
12079
- type: "tool_use",
12080
- id: String(b.id ?? ""),
12081
- name: String(b.name ?? ""),
12082
- input: b.input
12083
- };
12084
- case "tool_result":
12085
- return {
12086
- type: "tool_result",
12087
- toolUseId: String(b.tool_use_id ?? ""),
12088
- content: b.content
12089
- };
12090
- default:
12091
- return null;
12092
- }
12093
- }
12094
-
12095
- // src/plugins/registry.ts
12096
- var plugins = [new ClaudeCodePlugin()];
12097
-
12098
12557
  // src/cli/argv.ts
12099
12558
  var DEFAULT_PORT = 3100;
12100
12559
  function parseCliArgs(argv, env = {}) {
@@ -12150,28 +12609,28 @@ Environment:
12150
12609
  `;
12151
12610
 
12152
12611
  // src/config/data-dir.ts
12153
- import path5 from "path";
12612
+ import path6 from "path";
12154
12613
  function resolveDataDir(env, homeDir) {
12155
12614
  const override = env.CHAT_LOGBOOK_DATA_DIR?.trim();
12156
12615
  if (override) {
12157
- return path5.resolve(override);
12616
+ return path6.resolve(override);
12158
12617
  }
12159
- return path5.join(homeDir, ".chat-logbook");
12618
+ return path6.join(homeDir, ".chat-logbook");
12160
12619
  }
12161
12620
 
12162
12621
  // src/list-counts.ts
12163
12622
  import fs7 from "fs";
12164
- import path6 from "path";
12623
+ import path7 from "path";
12165
12624
  import Database3 from "better-sqlite3";
12166
12625
  var ARCHIVE_DB2 = "archive.db";
12167
12626
  var METADATA_DB2 = "metadata.db";
12168
12627
  function createChatCountsQuery({
12169
12628
  dataDir: dataDir2
12170
12629
  }) {
12171
- const archive2 = new Database3(path6.join(dataDir2, ARCHIVE_DB2), {
12630
+ const archive2 = new Database3(path7.join(dataDir2, ARCHIVE_DB2), {
12172
12631
  readonly: true
12173
12632
  });
12174
- const metadataPath = path6.join(dataDir2, METADATA_DB2);
12633
+ const metadataPath = path7.join(dataDir2, METADATA_DB2);
12175
12634
  const hasMetadata = fs7.existsSync(metadataPath);
12176
12635
  if (hasMetadata) {
12177
12636
  archive2.prepare("ATTACH DATABASE ? AS meta").run(metadataPath);
@@ -12307,8 +12766,8 @@ function reconcileTitleSortKeys({
12307
12766
  }
12308
12767
 
12309
12768
  // src/index.ts
12310
- var __dirname = path7.dirname(fileURLToPath2(import.meta.url));
12311
- var pkgPath = path7.join(__dirname, "../../package.json");
12769
+ var __dirname = path8.dirname(fileURLToPath2(import.meta.url));
12770
+ var pkgPath = path8.join(__dirname, "../../package.json");
12312
12771
  var pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
12313
12772
  var action = parseCliArgs(process.argv.slice(2), {
12314
12773
  PORT: process.env.PORT
@@ -12330,9 +12789,14 @@ var dataDir = resolveDataDir(
12330
12789
  { CHAT_LOGBOOK_DATA_DIR: process.env.CHAT_LOGBOOK_DATA_DIR },
12331
12790
  os.homedir()
12332
12791
  );
12333
- var webDistDir = path7.join(__dirname, "../../web/dist");
12792
+ var webDistDir = path8.join(__dirname, "../../web/dist");
12334
12793
  var port = action.port;
12335
12794
  var archive = createArchiveRepository({ dataDir });
12795
+ try {
12796
+ runRenormalizeIfStale({ plugins, archive });
12797
+ } catch (err) {
12798
+ console.error("[renormalize] startup pass failed:", err);
12799
+ }
12336
12800
  var checkpoint = createCheckpointRepository({ dataDir });
12337
12801
  var metadata = createMetadataRepository({ dataDir });
12338
12802
  var tags2 = createTagRepository({ dataDir });