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