chat-logbook 0.22.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
  };
@@ -3239,7 +3242,8 @@ function toApiBlock(block) {
3239
3242
  type: "tool_result",
3240
3243
  tool_use_id: String(block.toolUseId ?? ""),
3241
3244
  content: block.content,
3242
- ...block.isError === true ? { is_error: true } : {}
3245
+ ...block.isError === true ? { is_error: true } : {},
3246
+ ...typeof block.filePath === "string" && Array.isArray(block.patch) ? { file_path: block.filePath, patch: block.patch } : {}
3243
3247
  };
3244
3248
  }
3245
3249
  return block;
@@ -3378,7 +3382,9 @@ function createChatReader({
3378
3382
  id: m.messageId,
3379
3383
  role: m.role,
3380
3384
  content: m.blocks.map(toApiBlock),
3381
- timestamp: m.ts.toISOString()
3385
+ timestamp: m.ts.toISOString(),
3386
+ ...m.model === null ? {} : { model: m.model },
3387
+ ...m.effort === null ? {} : { effort: m.effort }
3382
3388
  }));
3383
3389
  }
3384
3390
  return {
@@ -3393,6 +3399,450 @@ function createChatReader({
3393
3399
  // src/list-contract.ts
3394
3400
  var MAX_PAGE_LIMIT = 200;
3395
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
+
3396
3846
  // src/app.ts
3397
3847
  var STREAM_HEARTBEAT_MS = 25e3;
3398
3848
  function createApp({
@@ -3663,6 +4113,52 @@ function createApp({
3663
4113
  }
3664
4114
  return c.json({ messages: messages2 });
3665
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
+ });
3666
4162
  app2.get("/api/tags", (c) => {
3667
4163
  return c.json({ tags: tags3.listTags() });
3668
4164
  });
@@ -5065,7 +5561,7 @@ var QueryPromise = class {
5065
5561
  function mapResultRow(columns, row, joinsNotNullableMap) {
5066
5562
  const nullifyMap = {};
5067
5563
  const result = columns.reduce(
5068
- (result2, { path: path8, field }, columnIndex) => {
5564
+ (result2, { path: path9, field }, columnIndex) => {
5069
5565
  let decoder;
5070
5566
  if (is(field, Column)) {
5071
5567
  decoder = field;
@@ -5077,8 +5573,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
5077
5573
  decoder = field.sql.decoder;
5078
5574
  }
5079
5575
  let node = result2;
5080
- for (const [pathChunkIndex, pathChunk] of path8.entries()) {
5081
- if (pathChunkIndex < path8.length - 1) {
5576
+ for (const [pathChunkIndex, pathChunk] of path9.entries()) {
5577
+ if (pathChunkIndex < path9.length - 1) {
5082
5578
  if (!(pathChunk in node)) {
5083
5579
  node[pathChunk] = {};
5084
5580
  }
@@ -5086,8 +5582,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
5086
5582
  } else {
5087
5583
  const rawValue = row[columnIndex];
5088
5584
  const value = node[pathChunk] = rawValue === null ? null : decoder.mapFromDriverValue(rawValue);
5089
- if (joinsNotNullableMap && is(field, Column) && path8.length === 2) {
5090
- const objectName = path8[0];
5585
+ if (joinsNotNullableMap && is(field, Column) && path9.length === 2) {
5586
+ const objectName = path9[0];
5091
5587
  if (!(objectName in nullifyMap)) {
5092
5588
  nullifyMap[objectName] = value === null ? getTableName(field.table) : false;
5093
5589
  } else if (typeof nullifyMap[objectName] === "string" && nullifyMap[objectName] !== getTableName(field.table)) {
@@ -5647,8 +6143,8 @@ function mapRelationalRow(tablesConfig, tableConfig, row, buildQueryResultSelect
5647
6143
  }
5648
6144
 
5649
6145
  // src/storage/openStore.ts
5650
- import fs3 from "fs";
5651
- import path2 from "path";
6146
+ import fs4 from "fs";
6147
+ import path4 from "path";
5652
6148
  import { fileURLToPath } from "url";
5653
6149
  import Database2 from "better-sqlite3";
5654
6150
 
@@ -9257,20 +9753,20 @@ function drizzle(...params) {
9257
9753
 
9258
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
9259
9755
  import crypto4 from "crypto";
9260
- import fs2 from "fs";
9756
+ import fs3 from "fs";
9261
9757
  function readMigrationFiles(config) {
9262
9758
  const migrationFolderTo = config.migrationsFolder;
9263
9759
  const migrationQueries = [];
9264
9760
  const journalPath = `${migrationFolderTo}/meta/_journal.json`;
9265
- if (!fs2.existsSync(journalPath)) {
9761
+ if (!fs3.existsSync(journalPath)) {
9266
9762
  throw new Error(`Can't find meta/_journal.json file`);
9267
9763
  }
9268
- const journalAsString = fs2.readFileSync(`${migrationFolderTo}/meta/_journal.json`).toString();
9764
+ const journalAsString = fs3.readFileSync(`${migrationFolderTo}/meta/_journal.json`).toString();
9269
9765
  const journal = JSON.parse(journalAsString);
9270
9766
  for (const journalEntry of journal.entries) {
9271
9767
  const migrationPath = `${migrationFolderTo}/${journalEntry.tag}.sql`;
9272
9768
  try {
9273
- const query = fs2.readFileSync(`${migrationFolderTo}/${journalEntry.tag}.sql`).toString();
9769
+ const query = fs3.readFileSync(`${migrationFolderTo}/${journalEntry.tag}.sql`).toString();
9274
9770
  const result = query.split("--> statement-breakpoint").map((it) => {
9275
9771
  return it;
9276
9772
  });
@@ -9295,12 +9791,12 @@ function migrate(db, config) {
9295
9791
 
9296
9792
  // src/storage/openStore.ts
9297
9793
  function resolveMigrationsFolder(callerUrl, migrationsSubdir) {
9298
- const here = path2.dirname(fileURLToPath(callerUrl));
9794
+ const here = path4.dirname(fileURLToPath(callerUrl));
9299
9795
  const candidates = [
9300
- path2.join(here, "../..", migrationsSubdir),
9301
- path2.join(here, ".", migrationsSubdir)
9796
+ path4.join(here, "../..", migrationsSubdir),
9797
+ path4.join(here, ".", migrationsSubdir)
9302
9798
  ];
9303
- const found = candidates.find((p) => fs3.existsSync(p));
9799
+ const found = candidates.find((p) => fs4.existsSync(p));
9304
9800
  if (!found) {
9305
9801
  throw new Error(`Could not locate migrations folder "${migrationsSubdir}"`);
9306
9802
  }
@@ -9314,8 +9810,8 @@ function openStore({
9314
9810
  schema
9315
9811
  }) {
9316
9812
  const migrationsFolder = resolveMigrationsFolder(callerUrl, migrationsSubdir);
9317
- fs3.mkdirSync(dataDir2, { recursive: true });
9318
- const sqlite = new Database2(path2.join(dataDir2, dbFile));
9813
+ fs4.mkdirSync(dataDir2, { recursive: true });
9814
+ const sqlite = new Database2(path4.join(dataDir2, dbFile));
9319
9815
  const db = drizzle(sqlite, { schema });
9320
9816
  migrate(db, { migrationsFolder });
9321
9817
  return { db, sqlite };
@@ -9334,7 +9830,12 @@ __export(schema_exports, {
9334
9830
  var archiveMeta = sqliteTable("archive_meta", {
9335
9831
  id: integer("id").primaryKey(),
9336
9832
  archiveUuid: text("archive_uuid").notNull(),
9337
- 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)
9338
9839
  });
9339
9840
  var schemaVersion = sqliteTable("schema_version", {
9340
9841
  version: integer("version").primaryKey(),
@@ -9403,6 +9904,13 @@ var messages = sqliteTable(
9403
9904
  ts: integer("ts", { mode: "timestamp_ms" }).notNull(),
9404
9905
  text: text("text").notNull(),
9405
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"),
9406
9914
  rawId: integer("raw_id").notNull().references(() => rawMessages.id)
9407
9915
  },
9408
9916
  (t) => [
@@ -9442,6 +9950,16 @@ function createArchiveReadSeam(db) {
9442
9950
  listMessagesByChat(agent, sourceId) {
9443
9951
  return db.select().from(messages).where(and(eq(messages.agent, agent), eq(messages.sourceId, sourceId))).orderBy(asc(messages.ts)).all();
9444
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
+ },
9445
9963
  listChatTsRanges(opts) {
9446
9964
  const scope = opts?.sourceIds !== void 0 ? inArray(messages.sourceId, opts.sourceIds) : sql`1 = 1`;
9447
9965
  return db.select({
@@ -9488,6 +10006,15 @@ function createArchiveReadSeam(db) {
9488
10006
  },
9489
10007
  listIngestionEvents() {
9490
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();
9491
10018
  }
9492
10019
  };
9493
10020
  }
@@ -9534,6 +10061,16 @@ function createArchiveRepository({
9534
10061
  }
9535
10062
  return row.archiveUuid;
9536
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
+ },
9537
10074
  getAppliedMigrations() {
9538
10075
  return db.select().from(schemaVersion).all().map((r) => ({ version: r.version, appliedAt: r.appliedAt }));
9539
10076
  },
@@ -9617,6 +10154,8 @@ function createArchiveRepository({
9617
10154
  ts,
9618
10155
  text: message.text,
9619
10156
  blocks: message.blocks,
10157
+ model: message.model ?? null,
10158
+ effort: message.effort ?? null,
9620
10159
  rawId
9621
10160
  }).run();
9622
10161
  return true;
@@ -9627,6 +10166,8 @@ function createArchiveRepository({
9627
10166
  ts,
9628
10167
  text: message.text,
9629
10168
  blocks: message.blocks,
10169
+ model: message.model ?? null,
10170
+ effort: message.effort ?? null,
9630
10171
  rawId
9631
10172
  }).where(eq(messages.id, existing2.id)).run();
9632
10173
  return true;
@@ -9728,8 +10269,8 @@ function createCheckpointRepository({
9728
10269
  }
9729
10270
 
9730
10271
  // src/metadata/repository.ts
9731
- import fs4 from "fs";
9732
- import path3 from "path";
10272
+ import fs5 from "fs";
10273
+ import path5 from "path";
9733
10274
 
9734
10275
  // src/metadata/schema.ts
9735
10276
  var schema_exports3 = {};
@@ -9809,11 +10350,11 @@ function computeSortKey(input) {
9809
10350
  var DB_FILE = "metadata.db";
9810
10351
  var LEGACY_DB_FILE = "data.db";
9811
10352
  function migrateLegacyDbFile(dataDir2) {
9812
- const legacy = path3.join(dataDir2, LEGACY_DB_FILE);
9813
- const current = path3.join(dataDir2, DB_FILE);
9814
- if (!fs4.existsSync(legacy)) return;
9815
- if (fs4.existsSync(current)) return;
9816
- 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);
9817
10358
  }
9818
10359
  var CLAUDE_CODE_AGENT = "claude-code";
9819
10360
  function createMetadataRepository({
@@ -10057,7 +10598,7 @@ function createTagRepository({
10057
10598
  }
10058
10599
 
10059
10600
  // src/ingestion/ingest.ts
10060
- import fs5 from "fs";
10601
+ import fs6 from "fs";
10061
10602
  async function runIngestion(opts) {
10062
10603
  const now = opts.now ?? (() => /* @__PURE__ */ new Date());
10063
10604
  const changedChatIds = /* @__PURE__ */ new Set();
@@ -10127,7 +10668,7 @@ async function runIngestion(opts) {
10127
10668
  }
10128
10669
  function safeStat(p) {
10129
10670
  try {
10130
- return fs5.statSync(p);
10671
+ return fs6.statSync(p);
10131
10672
  } catch {
10132
10673
  return null;
10133
10674
  }
@@ -10146,6 +10687,55 @@ function startIngestionInBackground(opts, bg = {}) {
10146
10687
  return { done };
10147
10688
  }
10148
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
+
10149
10739
  // ../node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
10150
10740
  import { EventEmitter } from "events";
10151
10741
  import { stat as statcb, Stats } from "fs";
@@ -10236,7 +10826,7 @@ var ReaddirpStream = class extends Readable3 {
10236
10826
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
10237
10827
  const statMethod = opts.lstat ? lstat : stat;
10238
10828
  if (wantBigintFsStats) {
10239
- this._stat = (path8) => statMethod(path8, { bigint: true });
10829
+ this._stat = (path9) => statMethod(path9, { bigint: true });
10240
10830
  } else {
10241
10831
  this._stat = statMethod;
10242
10832
  }
@@ -10261,8 +10851,8 @@ var ReaddirpStream = class extends Readable3 {
10261
10851
  const par = this.parent;
10262
10852
  const fil = par && par.files;
10263
10853
  if (fil && fil.length > 0) {
10264
- const { path: path8, depth } = par;
10265
- 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));
10266
10856
  const awaited = await Promise.all(slice);
10267
10857
  for (const entry of awaited) {
10268
10858
  if (!entry)
@@ -10302,20 +10892,20 @@ var ReaddirpStream = class extends Readable3 {
10302
10892
  this.reading = false;
10303
10893
  }
10304
10894
  }
10305
- async _exploreDir(path8, depth) {
10895
+ async _exploreDir(path9, depth) {
10306
10896
  let files;
10307
10897
  try {
10308
- files = await readdir(path8, this._rdOptions);
10898
+ files = await readdir(path9, this._rdOptions);
10309
10899
  } catch (error) {
10310
10900
  this._onError(error);
10311
10901
  }
10312
- return { files, depth, path: path8 };
10902
+ return { files, depth, path: path9 };
10313
10903
  }
10314
- async _formatEntry(dirent, path8) {
10904
+ async _formatEntry(dirent, path9) {
10315
10905
  let entry;
10316
10906
  const basename3 = this._isDirent ? dirent.name : dirent;
10317
10907
  try {
10318
- const fullPath = presolve(pjoin(path8, basename3));
10908
+ const fullPath = presolve(pjoin(path9, basename3));
10319
10909
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename3 };
10320
10910
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
10321
10911
  } catch (err) {
@@ -10715,16 +11305,16 @@ var delFromSet = (main, prop, item) => {
10715
11305
  };
10716
11306
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
10717
11307
  var FsWatchInstances = /* @__PURE__ */ new Map();
10718
- function createFsWatchInstance(path8, options, listener, errHandler, emitRaw) {
11308
+ function createFsWatchInstance(path9, options, listener, errHandler, emitRaw) {
10719
11309
  const handleEvent = (rawEvent, evPath) => {
10720
- listener(path8);
10721
- emitRaw(rawEvent, evPath, { watchedPath: path8 });
10722
- if (evPath && path8 !== evPath) {
10723
- 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));
10724
11314
  }
10725
11315
  };
10726
11316
  try {
10727
- return fs_watch(path8, {
11317
+ return fs_watch(path9, {
10728
11318
  persistent: options.persistent
10729
11319
  }, handleEvent);
10730
11320
  } catch (error) {
@@ -10740,12 +11330,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
10740
11330
  listener(val1, val2, val3);
10741
11331
  });
10742
11332
  };
10743
- var setFsWatchListener = (path8, fullPath, options, handlers) => {
11333
+ var setFsWatchListener = (path9, fullPath, options, handlers) => {
10744
11334
  const { listener, errHandler, rawEmitter } = handlers;
10745
11335
  let cont = FsWatchInstances.get(fullPath);
10746
11336
  let watcher2;
10747
11337
  if (!options.persistent) {
10748
- watcher2 = createFsWatchInstance(path8, options, listener, errHandler, rawEmitter);
11338
+ watcher2 = createFsWatchInstance(path9, options, listener, errHandler, rawEmitter);
10749
11339
  if (!watcher2)
10750
11340
  return;
10751
11341
  return watcher2.close.bind(watcher2);
@@ -10756,7 +11346,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
10756
11346
  addAndConvert(cont, KEY_RAW, rawEmitter);
10757
11347
  } else {
10758
11348
  watcher2 = createFsWatchInstance(
10759
- path8,
11349
+ path9,
10760
11350
  options,
10761
11351
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
10762
11352
  errHandler,
@@ -10771,7 +11361,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
10771
11361
  cont.watcherUnusable = true;
10772
11362
  if (isWindows && error.code === "EPERM") {
10773
11363
  try {
10774
- const fd = await open(path8, "r");
11364
+ const fd = await open(path9, "r");
10775
11365
  await fd.close();
10776
11366
  broadcastErr(error);
10777
11367
  } catch (err) {
@@ -10802,7 +11392,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
10802
11392
  };
10803
11393
  };
10804
11394
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
10805
- var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
11395
+ var setFsWatchFileListener = (path9, fullPath, options, handlers) => {
10806
11396
  const { listener, rawEmitter } = handlers;
10807
11397
  let cont = FsWatchFileInstances.get(fullPath);
10808
11398
  const copts = cont && cont.options;
@@ -10824,7 +11414,7 @@ var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
10824
11414
  });
10825
11415
  const currmtime = curr.mtimeMs;
10826
11416
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
10827
- foreach(cont.listeners, (listener2) => listener2(path8, curr));
11417
+ foreach(cont.listeners, (listener2) => listener2(path9, curr));
10828
11418
  }
10829
11419
  })
10830
11420
  };
@@ -10854,13 +11444,13 @@ var NodeFsHandler = class {
10854
11444
  * @param listener on fs change
10855
11445
  * @returns closer for the watcher instance
10856
11446
  */
10857
- _watchWithNodeFs(path8, listener) {
11447
+ _watchWithNodeFs(path9, listener) {
10858
11448
  const opts = this.fsw.options;
10859
- const directory = sp.dirname(path8);
10860
- const basename3 = sp.basename(path8);
11449
+ const directory = sp.dirname(path9);
11450
+ const basename3 = sp.basename(path9);
10861
11451
  const parent = this.fsw._getWatchedDir(directory);
10862
11452
  parent.add(basename3);
10863
- const absolutePath = sp.resolve(path8);
11453
+ const absolutePath = sp.resolve(path9);
10864
11454
  const options = {
10865
11455
  persistent: opts.persistent
10866
11456
  };
@@ -10870,12 +11460,12 @@ var NodeFsHandler = class {
10870
11460
  if (opts.usePolling) {
10871
11461
  const enableBin = opts.interval !== opts.binaryInterval;
10872
11462
  options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
10873
- closer = setFsWatchFileListener(path8, absolutePath, options, {
11463
+ closer = setFsWatchFileListener(path9, absolutePath, options, {
10874
11464
  listener,
10875
11465
  rawEmitter: this.fsw._emitRaw
10876
11466
  });
10877
11467
  } else {
10878
- closer = setFsWatchListener(path8, absolutePath, options, {
11468
+ closer = setFsWatchListener(path9, absolutePath, options, {
10879
11469
  listener,
10880
11470
  errHandler: this._boundHandleError,
10881
11471
  rawEmitter: this.fsw._emitRaw
@@ -10897,7 +11487,7 @@ var NodeFsHandler = class {
10897
11487
  let prevStats = stats;
10898
11488
  if (parent.has(basename3))
10899
11489
  return;
10900
- const listener = async (path8, newStats) => {
11490
+ const listener = async (path9, newStats) => {
10901
11491
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
10902
11492
  return;
10903
11493
  if (!newStats || newStats.mtimeMs === 0) {
@@ -10911,11 +11501,11 @@ var NodeFsHandler = class {
10911
11501
  this.fsw._emit(EV.CHANGE, file, newStats2);
10912
11502
  }
10913
11503
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
10914
- this.fsw._closeFile(path8);
11504
+ this.fsw._closeFile(path9);
10915
11505
  prevStats = newStats2;
10916
11506
  const closer2 = this._watchWithNodeFs(file, listener);
10917
11507
  if (closer2)
10918
- this.fsw._addPathCloser(path8, closer2);
11508
+ this.fsw._addPathCloser(path9, closer2);
10919
11509
  } else {
10920
11510
  prevStats = newStats2;
10921
11511
  }
@@ -10947,7 +11537,7 @@ var NodeFsHandler = class {
10947
11537
  * @param item basename of this item
10948
11538
  * @returns true if no more processing is needed for this entry.
10949
11539
  */
10950
- async _handleSymlink(entry, directory, path8, item) {
11540
+ async _handleSymlink(entry, directory, path9, item) {
10951
11541
  if (this.fsw.closed) {
10952
11542
  return;
10953
11543
  }
@@ -10957,7 +11547,7 @@ var NodeFsHandler = class {
10957
11547
  this.fsw._incrReadyCount();
10958
11548
  let linkPath;
10959
11549
  try {
10960
- linkPath = await fsrealpath(path8);
11550
+ linkPath = await fsrealpath(path9);
10961
11551
  } catch (e) {
10962
11552
  this.fsw._emitReady();
10963
11553
  return true;
@@ -10967,12 +11557,12 @@ var NodeFsHandler = class {
10967
11557
  if (dir.has(item)) {
10968
11558
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
10969
11559
  this.fsw._symlinkPaths.set(full, linkPath);
10970
- this.fsw._emit(EV.CHANGE, path8, entry.stats);
11560
+ this.fsw._emit(EV.CHANGE, path9, entry.stats);
10971
11561
  }
10972
11562
  } else {
10973
11563
  dir.add(item);
10974
11564
  this.fsw._symlinkPaths.set(full, linkPath);
10975
- this.fsw._emit(EV.ADD, path8, entry.stats);
11565
+ this.fsw._emit(EV.ADD, path9, entry.stats);
10976
11566
  }
10977
11567
  this.fsw._emitReady();
10978
11568
  return true;
@@ -11002,9 +11592,9 @@ var NodeFsHandler = class {
11002
11592
  return;
11003
11593
  }
11004
11594
  const item = entry.path;
11005
- let path8 = sp.join(directory, item);
11595
+ let path9 = sp.join(directory, item);
11006
11596
  current.add(item);
11007
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path8, item)) {
11597
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path9, item)) {
11008
11598
  return;
11009
11599
  }
11010
11600
  if (this.fsw.closed) {
@@ -11013,8 +11603,8 @@ var NodeFsHandler = class {
11013
11603
  }
11014
11604
  if (item === target || !target && !previous.has(item)) {
11015
11605
  this.fsw._incrReadyCount();
11016
- path8 = sp.join(dir, sp.relative(dir, path8));
11017
- this._addToNodeFs(path8, initialAdd, wh, depth + 1);
11606
+ path9 = sp.join(dir, sp.relative(dir, path9));
11607
+ this._addToNodeFs(path9, initialAdd, wh, depth + 1);
11018
11608
  }
11019
11609
  }).on(EV.ERROR, this._boundHandleError);
11020
11610
  return new Promise((resolve3, reject) => {
@@ -11083,13 +11673,13 @@ var NodeFsHandler = class {
11083
11673
  * @param depth Child path actually targeted for watch
11084
11674
  * @param target Child path actually targeted for watch
11085
11675
  */
11086
- async _addToNodeFs(path8, initialAdd, priorWh, depth, target) {
11676
+ async _addToNodeFs(path9, initialAdd, priorWh, depth, target) {
11087
11677
  const ready = this.fsw._emitReady;
11088
- if (this.fsw._isIgnored(path8) || this.fsw.closed) {
11678
+ if (this.fsw._isIgnored(path9) || this.fsw.closed) {
11089
11679
  ready();
11090
11680
  return false;
11091
11681
  }
11092
- const wh = this.fsw._getWatchHelpers(path8);
11682
+ const wh = this.fsw._getWatchHelpers(path9);
11093
11683
  if (priorWh) {
11094
11684
  wh.filterPath = (entry) => priorWh.filterPath(entry);
11095
11685
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -11105,8 +11695,8 @@ var NodeFsHandler = class {
11105
11695
  const follow = this.fsw.options.followSymlinks;
11106
11696
  let closer;
11107
11697
  if (stats.isDirectory()) {
11108
- const absPath = sp.resolve(path8);
11109
- const targetPath = follow ? await fsrealpath(path8) : path8;
11698
+ const absPath = sp.resolve(path9);
11699
+ const targetPath = follow ? await fsrealpath(path9) : path9;
11110
11700
  if (this.fsw.closed)
11111
11701
  return;
11112
11702
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -11116,29 +11706,29 @@ var NodeFsHandler = class {
11116
11706
  this.fsw._symlinkPaths.set(absPath, targetPath);
11117
11707
  }
11118
11708
  } else if (stats.isSymbolicLink()) {
11119
- const targetPath = follow ? await fsrealpath(path8) : path8;
11709
+ const targetPath = follow ? await fsrealpath(path9) : path9;
11120
11710
  if (this.fsw.closed)
11121
11711
  return;
11122
11712
  const parent = sp.dirname(wh.watchPath);
11123
11713
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
11124
11714
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
11125
- closer = await this._handleDir(parent, stats, initialAdd, depth, path8, wh, targetPath);
11715
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path9, wh, targetPath);
11126
11716
  if (this.fsw.closed)
11127
11717
  return;
11128
11718
  if (targetPath !== void 0) {
11129
- this.fsw._symlinkPaths.set(sp.resolve(path8), targetPath);
11719
+ this.fsw._symlinkPaths.set(sp.resolve(path9), targetPath);
11130
11720
  }
11131
11721
  } else {
11132
11722
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
11133
11723
  }
11134
11724
  ready();
11135
11725
  if (closer)
11136
- this.fsw._addPathCloser(path8, closer);
11726
+ this.fsw._addPathCloser(path9, closer);
11137
11727
  return false;
11138
11728
  } catch (error) {
11139
11729
  if (this.fsw._handleError(error)) {
11140
11730
  ready();
11141
- return path8;
11731
+ return path9;
11142
11732
  }
11143
11733
  }
11144
11734
  }
@@ -11181,24 +11771,24 @@ function createPattern(matcher) {
11181
11771
  }
11182
11772
  return () => false;
11183
11773
  }
11184
- function normalizePath(path8) {
11185
- if (typeof path8 !== "string")
11774
+ function normalizePath(path9) {
11775
+ if (typeof path9 !== "string")
11186
11776
  throw new Error("string expected");
11187
- path8 = sp2.normalize(path8);
11188
- path8 = path8.replace(/\\/g, "/");
11777
+ path9 = sp2.normalize(path9);
11778
+ path9 = path9.replace(/\\/g, "/");
11189
11779
  let prepend = false;
11190
- if (path8.startsWith("//"))
11780
+ if (path9.startsWith("//"))
11191
11781
  prepend = true;
11192
- path8 = path8.replace(DOUBLE_SLASH_RE, "/");
11782
+ path9 = path9.replace(DOUBLE_SLASH_RE, "/");
11193
11783
  if (prepend)
11194
- path8 = "/" + path8;
11195
- return path8;
11784
+ path9 = "/" + path9;
11785
+ return path9;
11196
11786
  }
11197
11787
  function matchPatterns(patterns, testString, stats) {
11198
- const path8 = normalizePath(testString);
11788
+ const path9 = normalizePath(testString);
11199
11789
  for (let index2 = 0; index2 < patterns.length; index2++) {
11200
11790
  const pattern = patterns[index2];
11201
- if (pattern(path8, stats)) {
11791
+ if (pattern(path9, stats)) {
11202
11792
  return true;
11203
11793
  }
11204
11794
  }
@@ -11236,19 +11826,19 @@ var toUnix = (string) => {
11236
11826
  }
11237
11827
  return str;
11238
11828
  };
11239
- var normalizePathToUnix = (path8) => toUnix(sp2.normalize(toUnix(path8)));
11240
- var normalizeIgnored = (cwd = "") => (path8) => {
11241
- if (typeof path8 === "string") {
11242
- 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));
11243
11833
  } else {
11244
- return path8;
11834
+ return path9;
11245
11835
  }
11246
11836
  };
11247
- var getAbsolutePath = (path8, cwd) => {
11248
- if (sp2.isAbsolute(path8)) {
11249
- return path8;
11837
+ var getAbsolutePath = (path9, cwd) => {
11838
+ if (sp2.isAbsolute(path9)) {
11839
+ return path9;
11250
11840
  }
11251
- return sp2.join(cwd, path8);
11841
+ return sp2.join(cwd, path9);
11252
11842
  };
11253
11843
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
11254
11844
  var DirEntry = class {
@@ -11313,10 +11903,10 @@ var WatchHelper = class {
11313
11903
  dirParts;
11314
11904
  followSymlinks;
11315
11905
  statMethod;
11316
- constructor(path8, follow, fsw) {
11906
+ constructor(path9, follow, fsw) {
11317
11907
  this.fsw = fsw;
11318
- const watchPath = path8;
11319
- this.path = path8 = path8.replace(REPLACER_RE, "");
11908
+ const watchPath = path9;
11909
+ this.path = path9 = path9.replace(REPLACER_RE, "");
11320
11910
  this.watchPath = watchPath;
11321
11911
  this.fullWatchPath = sp2.resolve(watchPath);
11322
11912
  this.dirParts = [];
@@ -11456,20 +12046,20 @@ var FSWatcher = class extends EventEmitter {
11456
12046
  this._closePromise = void 0;
11457
12047
  let paths = unifyPaths(paths_);
11458
12048
  if (cwd) {
11459
- paths = paths.map((path8) => {
11460
- const absPath = getAbsolutePath(path8, cwd);
12049
+ paths = paths.map((path9) => {
12050
+ const absPath = getAbsolutePath(path9, cwd);
11461
12051
  return absPath;
11462
12052
  });
11463
12053
  }
11464
- paths.forEach((path8) => {
11465
- this._removeIgnoredPath(path8);
12054
+ paths.forEach((path9) => {
12055
+ this._removeIgnoredPath(path9);
11466
12056
  });
11467
12057
  this._userIgnored = void 0;
11468
12058
  if (!this._readyCount)
11469
12059
  this._readyCount = 0;
11470
12060
  this._readyCount += paths.length;
11471
- Promise.all(paths.map(async (path8) => {
11472
- 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);
11473
12063
  if (res)
11474
12064
  this._emitReady();
11475
12065
  return res;
@@ -11491,17 +12081,17 @@ var FSWatcher = class extends EventEmitter {
11491
12081
  return this;
11492
12082
  const paths = unifyPaths(paths_);
11493
12083
  const { cwd } = this.options;
11494
- paths.forEach((path8) => {
11495
- if (!sp2.isAbsolute(path8) && !this._closers.has(path8)) {
12084
+ paths.forEach((path9) => {
12085
+ if (!sp2.isAbsolute(path9) && !this._closers.has(path9)) {
11496
12086
  if (cwd)
11497
- path8 = sp2.join(cwd, path8);
11498
- path8 = sp2.resolve(path8);
12087
+ path9 = sp2.join(cwd, path9);
12088
+ path9 = sp2.resolve(path9);
11499
12089
  }
11500
- this._closePath(path8);
11501
- this._addIgnoredPath(path8);
11502
- if (this._watched.has(path8)) {
12090
+ this._closePath(path9);
12091
+ this._addIgnoredPath(path9);
12092
+ if (this._watched.has(path9)) {
11503
12093
  this._addIgnoredPath({
11504
- path: path8,
12094
+ path: path9,
11505
12095
  recursive: true
11506
12096
  });
11507
12097
  }
@@ -11565,38 +12155,38 @@ var FSWatcher = class extends EventEmitter {
11565
12155
  * @param stats arguments to be passed with event
11566
12156
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
11567
12157
  */
11568
- async _emit(event, path8, stats) {
12158
+ async _emit(event, path9, stats) {
11569
12159
  if (this.closed)
11570
12160
  return;
11571
12161
  const opts = this.options;
11572
12162
  if (isWindows)
11573
- path8 = sp2.normalize(path8);
12163
+ path9 = sp2.normalize(path9);
11574
12164
  if (opts.cwd)
11575
- path8 = sp2.relative(opts.cwd, path8);
11576
- const args = [path8];
12165
+ path9 = sp2.relative(opts.cwd, path9);
12166
+ const args = [path9];
11577
12167
  if (stats != null)
11578
12168
  args.push(stats);
11579
12169
  const awf = opts.awaitWriteFinish;
11580
12170
  let pw;
11581
- if (awf && (pw = this._pendingWrites.get(path8))) {
12171
+ if (awf && (pw = this._pendingWrites.get(path9))) {
11582
12172
  pw.lastChange = /* @__PURE__ */ new Date();
11583
12173
  return this;
11584
12174
  }
11585
12175
  if (opts.atomic) {
11586
12176
  if (event === EVENTS.UNLINK) {
11587
- this._pendingUnlinks.set(path8, [event, ...args]);
12177
+ this._pendingUnlinks.set(path9, [event, ...args]);
11588
12178
  setTimeout(() => {
11589
- this._pendingUnlinks.forEach((entry, path9) => {
12179
+ this._pendingUnlinks.forEach((entry, path10) => {
11590
12180
  this.emit(...entry);
11591
12181
  this.emit(EVENTS.ALL, ...entry);
11592
- this._pendingUnlinks.delete(path9);
12182
+ this._pendingUnlinks.delete(path10);
11593
12183
  });
11594
12184
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
11595
12185
  return this;
11596
12186
  }
11597
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path8)) {
12187
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path9)) {
11598
12188
  event = EVENTS.CHANGE;
11599
- this._pendingUnlinks.delete(path8);
12189
+ this._pendingUnlinks.delete(path9);
11600
12190
  }
11601
12191
  }
11602
12192
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -11614,16 +12204,16 @@ var FSWatcher = class extends EventEmitter {
11614
12204
  this.emitWithAll(event, args);
11615
12205
  }
11616
12206
  };
11617
- this._awaitWriteFinish(path8, awf.stabilityThreshold, event, awfEmit);
12207
+ this._awaitWriteFinish(path9, awf.stabilityThreshold, event, awfEmit);
11618
12208
  return this;
11619
12209
  }
11620
12210
  if (event === EVENTS.CHANGE) {
11621
- const isThrottled = !this._throttle(EVENTS.CHANGE, path8, 50);
12211
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path9, 50);
11622
12212
  if (isThrottled)
11623
12213
  return this;
11624
12214
  }
11625
12215
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
11626
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path8) : path8;
12216
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path9) : path9;
11627
12217
  let stats2;
11628
12218
  try {
11629
12219
  stats2 = await stat3(fullPath);
@@ -11654,23 +12244,23 @@ var FSWatcher = class extends EventEmitter {
11654
12244
  * @param timeout duration of time to suppress duplicate actions
11655
12245
  * @returns tracking object or false if action should be suppressed
11656
12246
  */
11657
- _throttle(actionType, path8, timeout) {
12247
+ _throttle(actionType, path9, timeout) {
11658
12248
  if (!this._throttled.has(actionType)) {
11659
12249
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
11660
12250
  }
11661
12251
  const action2 = this._throttled.get(actionType);
11662
12252
  if (!action2)
11663
12253
  throw new Error("invalid throttle");
11664
- const actionPath = action2.get(path8);
12254
+ const actionPath = action2.get(path9);
11665
12255
  if (actionPath) {
11666
12256
  actionPath.count++;
11667
12257
  return false;
11668
12258
  }
11669
12259
  let timeoutObject;
11670
12260
  const clear = () => {
11671
- const item = action2.get(path8);
12261
+ const item = action2.get(path9);
11672
12262
  const count = item ? item.count : 0;
11673
- action2.delete(path8);
12263
+ action2.delete(path9);
11674
12264
  clearTimeout(timeoutObject);
11675
12265
  if (item)
11676
12266
  clearTimeout(item.timeoutObject);
@@ -11678,7 +12268,7 @@ var FSWatcher = class extends EventEmitter {
11678
12268
  };
11679
12269
  timeoutObject = setTimeout(clear, timeout);
11680
12270
  const thr = { timeoutObject, clear, count: 0 };
11681
- action2.set(path8, thr);
12271
+ action2.set(path9, thr);
11682
12272
  return thr;
11683
12273
  }
11684
12274
  _incrReadyCount() {
@@ -11692,44 +12282,44 @@ var FSWatcher = class extends EventEmitter {
11692
12282
  * @param event
11693
12283
  * @param awfEmit Callback to be called when ready for event to be emitted.
11694
12284
  */
11695
- _awaitWriteFinish(path8, threshold, event, awfEmit) {
12285
+ _awaitWriteFinish(path9, threshold, event, awfEmit) {
11696
12286
  const awf = this.options.awaitWriteFinish;
11697
12287
  if (typeof awf !== "object")
11698
12288
  return;
11699
12289
  const pollInterval = awf.pollInterval;
11700
12290
  let timeoutHandler;
11701
- let fullPath = path8;
11702
- if (this.options.cwd && !sp2.isAbsolute(path8)) {
11703
- 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);
11704
12294
  }
11705
12295
  const now = /* @__PURE__ */ new Date();
11706
12296
  const writes = this._pendingWrites;
11707
12297
  function awaitWriteFinishFn(prevStat) {
11708
12298
  statcb(fullPath, (err, curStat) => {
11709
- if (err || !writes.has(path8)) {
12299
+ if (err || !writes.has(path9)) {
11710
12300
  if (err && err.code !== "ENOENT")
11711
12301
  awfEmit(err);
11712
12302
  return;
11713
12303
  }
11714
12304
  const now2 = Number(/* @__PURE__ */ new Date());
11715
12305
  if (prevStat && curStat.size !== prevStat.size) {
11716
- writes.get(path8).lastChange = now2;
12306
+ writes.get(path9).lastChange = now2;
11717
12307
  }
11718
- const pw = writes.get(path8);
12308
+ const pw = writes.get(path9);
11719
12309
  const df = now2 - pw.lastChange;
11720
12310
  if (df >= threshold) {
11721
- writes.delete(path8);
12311
+ writes.delete(path9);
11722
12312
  awfEmit(void 0, curStat);
11723
12313
  } else {
11724
12314
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
11725
12315
  }
11726
12316
  });
11727
12317
  }
11728
- if (!writes.has(path8)) {
11729
- writes.set(path8, {
12318
+ if (!writes.has(path9)) {
12319
+ writes.set(path9, {
11730
12320
  lastChange: now,
11731
12321
  cancelWait: () => {
11732
- writes.delete(path8);
12322
+ writes.delete(path9);
11733
12323
  clearTimeout(timeoutHandler);
11734
12324
  return event;
11735
12325
  }
@@ -11740,8 +12330,8 @@ var FSWatcher = class extends EventEmitter {
11740
12330
  /**
11741
12331
  * Determines whether user has asked to ignore this path.
11742
12332
  */
11743
- _isIgnored(path8, stats) {
11744
- if (this.options.atomic && DOT_RE.test(path8))
12333
+ _isIgnored(path9, stats) {
12334
+ if (this.options.atomic && DOT_RE.test(path9))
11745
12335
  return true;
11746
12336
  if (!this._userIgnored) {
11747
12337
  const { cwd } = this.options;
@@ -11751,17 +12341,17 @@ var FSWatcher = class extends EventEmitter {
11751
12341
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
11752
12342
  this._userIgnored = anymatch(list, void 0);
11753
12343
  }
11754
- return this._userIgnored(path8, stats);
12344
+ return this._userIgnored(path9, stats);
11755
12345
  }
11756
- _isntIgnored(path8, stat4) {
11757
- return !this._isIgnored(path8, stat4);
12346
+ _isntIgnored(path9, stat4) {
12347
+ return !this._isIgnored(path9, stat4);
11758
12348
  }
11759
12349
  /**
11760
12350
  * Provides a set of common helpers and properties relating to symlink handling.
11761
12351
  * @param path file or directory pattern being watched
11762
12352
  */
11763
- _getWatchHelpers(path8) {
11764
- return new WatchHelper(path8, this.options.followSymlinks, this);
12353
+ _getWatchHelpers(path9) {
12354
+ return new WatchHelper(path9, this.options.followSymlinks, this);
11765
12355
  }
11766
12356
  // Directory helpers
11767
12357
  // -----------------
@@ -11793,63 +12383,63 @@ var FSWatcher = class extends EventEmitter {
11793
12383
  * @param item base path of item/directory
11794
12384
  */
11795
12385
  _remove(directory, item, isDirectory) {
11796
- const path8 = sp2.join(directory, item);
11797
- const fullPath = sp2.resolve(path8);
11798
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path8) || this._watched.has(fullPath);
11799
- 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))
11800
12390
  return;
11801
12391
  if (!isDirectory && this._watched.size === 1) {
11802
12392
  this.add(directory, item, true);
11803
12393
  }
11804
- const wp = this._getWatchedDir(path8);
12394
+ const wp = this._getWatchedDir(path9);
11805
12395
  const nestedDirectoryChildren = wp.getChildren();
11806
- nestedDirectoryChildren.forEach((nested) => this._remove(path8, nested));
12396
+ nestedDirectoryChildren.forEach((nested) => this._remove(path9, nested));
11807
12397
  const parent = this._getWatchedDir(directory);
11808
12398
  const wasTracked = parent.has(item);
11809
12399
  parent.remove(item);
11810
12400
  if (this._symlinkPaths.has(fullPath)) {
11811
12401
  this._symlinkPaths.delete(fullPath);
11812
12402
  }
11813
- let relPath = path8;
12403
+ let relPath = path9;
11814
12404
  if (this.options.cwd)
11815
- relPath = sp2.relative(this.options.cwd, path8);
12405
+ relPath = sp2.relative(this.options.cwd, path9);
11816
12406
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
11817
12407
  const event = this._pendingWrites.get(relPath).cancelWait();
11818
12408
  if (event === EVENTS.ADD)
11819
12409
  return;
11820
12410
  }
11821
- this._watched.delete(path8);
12411
+ this._watched.delete(path9);
11822
12412
  this._watched.delete(fullPath);
11823
12413
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
11824
- if (wasTracked && !this._isIgnored(path8))
11825
- this._emit(eventName, path8);
11826
- this._closePath(path8);
12414
+ if (wasTracked && !this._isIgnored(path9))
12415
+ this._emit(eventName, path9);
12416
+ this._closePath(path9);
11827
12417
  }
11828
12418
  /**
11829
12419
  * Closes all watchers for a path
11830
12420
  */
11831
- _closePath(path8) {
11832
- this._closeFile(path8);
11833
- const dir = sp2.dirname(path8);
11834
- 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));
11835
12425
  }
11836
12426
  /**
11837
12427
  * Closes only file-specific watchers
11838
12428
  */
11839
- _closeFile(path8) {
11840
- const closers = this._closers.get(path8);
12429
+ _closeFile(path9) {
12430
+ const closers = this._closers.get(path9);
11841
12431
  if (!closers)
11842
12432
  return;
11843
12433
  closers.forEach((closer) => closer());
11844
- this._closers.delete(path8);
12434
+ this._closers.delete(path9);
11845
12435
  }
11846
- _addPathCloser(path8, closer) {
12436
+ _addPathCloser(path9, closer) {
11847
12437
  if (!closer)
11848
12438
  return;
11849
- let list = this._closers.get(path8);
12439
+ let list = this._closers.get(path9);
11850
12440
  if (!list) {
11851
12441
  list = [];
11852
- this._closers.set(path8, list);
12442
+ this._closers.set(path9, list);
11853
12443
  }
11854
12444
  list.push(closer);
11855
12445
  }
@@ -11947,13 +12537,13 @@ function startWatcher(opts) {
11947
12537
  }
11948
12538
  return {
11949
12539
  ready,
11950
- notifyChange(path8) {
11951
- if (watcher2) watcher2.emit("change", path8);
11952
- else scheduleIngest(path8);
12540
+ notifyChange(path9) {
12541
+ if (watcher2) watcher2.emit("change", path9);
12542
+ else scheduleIngest(path9);
11953
12543
  },
11954
- notifyUnlink(path8) {
11955
- if (watcher2) watcher2.emit("unlink", path8);
11956
- else recordUnlink(path8);
12544
+ notifyUnlink(path9) {
12545
+ if (watcher2) watcher2.emit("unlink", path9);
12546
+ else recordUnlink(path9);
11957
12547
  },
11958
12548
  async close() {
11959
12549
  closed = true;
@@ -11964,142 +12554,6 @@ function startWatcher(opts) {
11964
12554
  };
11965
12555
  }
11966
12556
 
11967
- // src/plugins/claude-code/plugin.ts
11968
- import fs6 from "fs";
11969
- import path4 from "path";
11970
- import readline from "readline";
11971
- var ClaudeCodePlugin = class {
11972
- id = "claude-code";
11973
- displayName = "Claude Code";
11974
- async *discover(env) {
11975
- const projectsDir = path4.join(env.homeDir, ".claude", "projects");
11976
- if (!fs6.existsSync(projectsDir)) return;
11977
- const projects = fs6.readdirSync(projectsDir, { withFileTypes: true });
11978
- for (const project of projects) {
11979
- if (!project.isDirectory()) continue;
11980
- const projectPath = path4.join(projectsDir, project.name);
11981
- const files = fs6.readdirSync(projectPath, { withFileTypes: true });
11982
- for (const file of files) {
11983
- if (!file.isFile() || !file.name.endsWith(".jsonl")) continue;
11984
- const sourcePath = path4.join(projectPath, file.name);
11985
- const sourceId = file.name.replace(/\.jsonl$/, "");
11986
- const cwd = await readCwdFromJsonl(sourcePath);
11987
- yield {
11988
- sourceId,
11989
- sourcePath,
11990
- watchPaths: [sourcePath],
11991
- project: cwd ? path4.basename(cwd) : void 0,
11992
- projectPath: cwd ?? void 0
11993
- };
11994
- }
11995
- }
11996
- }
11997
- async *extractRaw(ref) {
11998
- if (!fs6.existsSync(ref.sourcePath)) return;
11999
- const stream2 = fs6.createReadStream(ref.sourcePath, { encoding: "utf-8" });
12000
- const rl = readline.createInterface({ input: stream2, crlfDelay: Infinity });
12001
- let lineNo = 0;
12002
- for await (const line of rl) {
12003
- lineNo += 1;
12004
- if (!line) continue;
12005
- yield {
12006
- sourceId: ref.sourceId,
12007
- sourcePath: ref.sourcePath,
12008
- sourceLocator: `L${lineNo}`,
12009
- payload: JSON.parse(line)
12010
- };
12011
- }
12012
- }
12013
- normalize(raw2) {
12014
- const payload = raw2.payload;
12015
- if (!payload || typeof payload !== "object") return null;
12016
- if (payload.type !== "user" && payload.type !== "assistant") return null;
12017
- if (payload.isMeta === true) return null;
12018
- if (payload.isSidechain === true) return null;
12019
- const message = payload.message;
12020
- if (!message) return null;
12021
- const role = message.role === "assistant" ? "assistant" : "user";
12022
- const messageId = String(payload.uuid ?? "");
12023
- const ts = String(payload.timestamp ?? "");
12024
- if (typeof message.content === "string") {
12025
- const text2 = message.content;
12026
- return {
12027
- messageId,
12028
- role,
12029
- ts,
12030
- text: text2,
12031
- blocks: [{ type: "text", text: text2 }]
12032
- };
12033
- }
12034
- if (Array.isArray(message.content)) {
12035
- const blocks = [];
12036
- for (const block of message.content) {
12037
- const normalized = normalizeBlock(block);
12038
- if (normalized) blocks.push(normalized);
12039
- }
12040
- const text2 = blocks.filter((b) => b.type === "text").map((b) => b.text).join("\n");
12041
- return { messageId, role, ts, text: text2, blocks };
12042
- }
12043
- return null;
12044
- }
12045
- };
12046
- async function readCwdFromJsonl(sourcePath) {
12047
- try {
12048
- const stream2 = fs6.createReadStream(sourcePath, { encoding: "utf-8" });
12049
- const rl = readline.createInterface({ input: stream2, crlfDelay: Infinity });
12050
- try {
12051
- for await (const line of rl) {
12052
- if (!line) continue;
12053
- let obj;
12054
- try {
12055
- obj = JSON.parse(line);
12056
- } catch {
12057
- continue;
12058
- }
12059
- if (typeof obj.cwd === "string" && obj.cwd.length > 0) {
12060
- return obj.cwd;
12061
- }
12062
- }
12063
- } finally {
12064
- rl.close();
12065
- stream2.destroy();
12066
- }
12067
- } catch {
12068
- }
12069
- return void 0;
12070
- }
12071
- function normalizeBlock(raw2) {
12072
- if (!raw2 || typeof raw2 !== "object") return null;
12073
- const b = raw2;
12074
- switch (b.type) {
12075
- case "text":
12076
- return { type: "text", text: String(b.text ?? "") };
12077
- case "thinking":
12078
- return { type: "thinking", thinking: String(b.thinking ?? "") };
12079
- case "tool_use":
12080
- return {
12081
- type: "tool_use",
12082
- id: String(b.id ?? ""),
12083
- name: String(b.name ?? ""),
12084
- input: b.input
12085
- };
12086
- case "tool_result":
12087
- return {
12088
- type: "tool_result",
12089
- toolUseId: String(b.tool_use_id ?? ""),
12090
- content: b.content,
12091
- // Only carried when the tool actually failed: a `false` on every
12092
- // successful result would grow the stored block for nothing.
12093
- ...b.is_error === true ? { isError: true } : {}
12094
- };
12095
- default:
12096
- return null;
12097
- }
12098
- }
12099
-
12100
- // src/plugins/registry.ts
12101
- var plugins = [new ClaudeCodePlugin()];
12102
-
12103
12557
  // src/cli/argv.ts
12104
12558
  var DEFAULT_PORT = 3100;
12105
12559
  function parseCliArgs(argv, env = {}) {
@@ -12155,28 +12609,28 @@ Environment:
12155
12609
  `;
12156
12610
 
12157
12611
  // src/config/data-dir.ts
12158
- import path5 from "path";
12612
+ import path6 from "path";
12159
12613
  function resolveDataDir(env, homeDir) {
12160
12614
  const override = env.CHAT_LOGBOOK_DATA_DIR?.trim();
12161
12615
  if (override) {
12162
- return path5.resolve(override);
12616
+ return path6.resolve(override);
12163
12617
  }
12164
- return path5.join(homeDir, ".chat-logbook");
12618
+ return path6.join(homeDir, ".chat-logbook");
12165
12619
  }
12166
12620
 
12167
12621
  // src/list-counts.ts
12168
12622
  import fs7 from "fs";
12169
- import path6 from "path";
12623
+ import path7 from "path";
12170
12624
  import Database3 from "better-sqlite3";
12171
12625
  var ARCHIVE_DB2 = "archive.db";
12172
12626
  var METADATA_DB2 = "metadata.db";
12173
12627
  function createChatCountsQuery({
12174
12628
  dataDir: dataDir2
12175
12629
  }) {
12176
- const archive2 = new Database3(path6.join(dataDir2, ARCHIVE_DB2), {
12630
+ const archive2 = new Database3(path7.join(dataDir2, ARCHIVE_DB2), {
12177
12631
  readonly: true
12178
12632
  });
12179
- const metadataPath = path6.join(dataDir2, METADATA_DB2);
12633
+ const metadataPath = path7.join(dataDir2, METADATA_DB2);
12180
12634
  const hasMetadata = fs7.existsSync(metadataPath);
12181
12635
  if (hasMetadata) {
12182
12636
  archive2.prepare("ATTACH DATABASE ? AS meta").run(metadataPath);
@@ -12312,8 +12766,8 @@ function reconcileTitleSortKeys({
12312
12766
  }
12313
12767
 
12314
12768
  // src/index.ts
12315
- var __dirname = path7.dirname(fileURLToPath2(import.meta.url));
12316
- var pkgPath = path7.join(__dirname, "../../package.json");
12769
+ var __dirname = path8.dirname(fileURLToPath2(import.meta.url));
12770
+ var pkgPath = path8.join(__dirname, "../../package.json");
12317
12771
  var pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
12318
12772
  var action = parseCliArgs(process.argv.slice(2), {
12319
12773
  PORT: process.env.PORT
@@ -12335,9 +12789,14 @@ var dataDir = resolveDataDir(
12335
12789
  { CHAT_LOGBOOK_DATA_DIR: process.env.CHAT_LOGBOOK_DATA_DIR },
12336
12790
  os.homedir()
12337
12791
  );
12338
- var webDistDir = path7.join(__dirname, "../../web/dist");
12792
+ var webDistDir = path8.join(__dirname, "../../web/dist");
12339
12793
  var port = action.port;
12340
12794
  var archive = createArchiveRepository({ dataDir });
12795
+ try {
12796
+ runRenormalizeIfStale({ plugins, archive });
12797
+ } catch (err) {
12798
+ console.error("[renormalize] startup pass failed:", err);
12799
+ }
12341
12800
  var checkpoint = createCheckpointRepository({ dataDir });
12342
12801
  var metadata = createMetadataRepository({ dataDir });
12343
12802
  var tags2 = createTagRepository({ dataDir });