@threadbase-sh/streamer 1.39.0 → 1.40.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/dist/cli.cjs +840 -357
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +668 -185
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +611 -128
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -4121,7 +4121,7 @@ var require_transport = __commonJS({
|
|
|
4121
4121
|
"node_modules/pino/lib/transport.js"(exports2, module2) {
|
|
4122
4122
|
"use strict";
|
|
4123
4123
|
var { createRequire } = require("module");
|
|
4124
|
-
var { existsSync:
|
|
4124
|
+
var { existsSync: existsSync16 } = require("fs");
|
|
4125
4125
|
var getCallers = require_caller();
|
|
4126
4126
|
var { join: join29, isAbsolute: isAbsolute2, sep: sep3 } = require("path");
|
|
4127
4127
|
var { fileURLToPath: fileURLToPath3 } = require("url");
|
|
@@ -4195,7 +4195,7 @@ var require_transport = __commonJS({
|
|
|
4195
4195
|
return false;
|
|
4196
4196
|
}
|
|
4197
4197
|
}
|
|
4198
|
-
return isAbsolute2(path2) && !
|
|
4198
|
+
return isAbsolute2(path2) && !existsSync16(path2);
|
|
4199
4199
|
}
|
|
4200
4200
|
function stripQuotes(value) {
|
|
4201
4201
|
const first = value[0];
|
|
@@ -5937,7 +5937,7 @@ var require_pino = __commonJS({
|
|
|
5937
5937
|
} = symbols;
|
|
5938
5938
|
var { epochTime, nullTime } = time3;
|
|
5939
5939
|
var { pid } = process;
|
|
5940
|
-
var
|
|
5940
|
+
var hostname6 = os2.hostname();
|
|
5941
5941
|
var defaultErrorSerializer = stdSerializers.err;
|
|
5942
5942
|
var defaultOptions2 = {
|
|
5943
5943
|
level: "info",
|
|
@@ -5947,7 +5947,7 @@ var require_pino = __commonJS({
|
|
|
5947
5947
|
errorKey: "err",
|
|
5948
5948
|
nestedKey: null,
|
|
5949
5949
|
enabled: true,
|
|
5950
|
-
base: { pid, hostname:
|
|
5950
|
+
base: { pid, hostname: hostname6 },
|
|
5951
5951
|
serializers: Object.assign(/* @__PURE__ */ Object.create(null), {
|
|
5952
5952
|
err: defaultErrorSerializer
|
|
5953
5953
|
}),
|
|
@@ -38620,8 +38620,8 @@ var require_service_config = __commonJS({
|
|
|
38620
38620
|
}
|
|
38621
38621
|
if (Array.isArray(validatedConfig.clientHostname)) {
|
|
38622
38622
|
let hostnameMatched = false;
|
|
38623
|
-
for (const
|
|
38624
|
-
if (
|
|
38623
|
+
for (const hostname6 of validatedConfig.clientHostname) {
|
|
38624
|
+
if (hostname6 === os2.hostname()) {
|
|
38625
38625
|
hostnameMatched = true;
|
|
38626
38626
|
}
|
|
38627
38627
|
}
|
|
@@ -51190,8 +51190,8 @@ var require_single_subchannel_channel = __commonJS({
|
|
|
51190
51190
|
if (splitPath2.length >= 2) {
|
|
51191
51191
|
serviceName = splitPath2[1];
|
|
51192
51192
|
}
|
|
51193
|
-
const
|
|
51194
|
-
this.serviceUrl = `https://${
|
|
51193
|
+
const hostname6 = (_b = (_a3 = (0, uri_parser_1.splitHostPort)(this.options.host)) === null || _a3 === void 0 ? void 0 : _a3.host) !== null && _b !== void 0 ? _b : "localhost";
|
|
51194
|
+
this.serviceUrl = `https://${hostname6}/${serviceName}`;
|
|
51195
51195
|
const timeout = (0, deadline_1.getRelativeTimeout)(options.deadline);
|
|
51196
51196
|
if (timeout !== Infinity) {
|
|
51197
51197
|
if (timeout <= 0) {
|
|
@@ -51838,8 +51838,8 @@ var require_resolver_dns = __commonJS({
|
|
|
51838
51838
|
}
|
|
51839
51839
|
trace("Looking up DNS hostname " + this.dnsHostname);
|
|
51840
51840
|
this.latestLookupResult = null;
|
|
51841
|
-
const
|
|
51842
|
-
this.pendingLookupPromise = this.lookup(
|
|
51841
|
+
const hostname6 = this.dnsHostname;
|
|
51842
|
+
this.pendingLookupPromise = this.lookup(hostname6);
|
|
51843
51843
|
this.pendingLookupPromise.then((addressList) => {
|
|
51844
51844
|
if (this.pendingLookupPromise === null) {
|
|
51845
51845
|
return;
|
|
@@ -51862,7 +51862,7 @@ var require_resolver_dns = __commonJS({
|
|
|
51862
51862
|
this.listener((0, call_interface_1.statusOrFromError)(this.defaultResolutionError), {}, this.latestServiceConfigResult, "");
|
|
51863
51863
|
});
|
|
51864
51864
|
if (this.isServiceConfigEnabled && this.pendingTxtPromise === null) {
|
|
51865
|
-
this.pendingTxtPromise = this.resolveTxt(
|
|
51865
|
+
this.pendingTxtPromise = this.resolveTxt(hostname6);
|
|
51866
51866
|
this.pendingTxtPromise.then((txtRecord) => {
|
|
51867
51867
|
if (this.pendingTxtPromise === null) {
|
|
51868
51868
|
return;
|
|
@@ -51904,12 +51904,12 @@ var require_resolver_dns = __commonJS({
|
|
|
51904
51904
|
this.continueResolving = true;
|
|
51905
51905
|
}
|
|
51906
51906
|
}
|
|
51907
|
-
async lookup(
|
|
51907
|
+
async lookup(hostname6) {
|
|
51908
51908
|
if (environment_1.GRPC_NODE_USE_ALTERNATIVE_RESOLVER) {
|
|
51909
51909
|
trace("Using alternative DNS resolver.");
|
|
51910
51910
|
const records = await Promise.allSettled([
|
|
51911
|
-
this.alternativeResolver.resolve4(
|
|
51912
|
-
this.alternativeResolver.resolve6(
|
|
51911
|
+
this.alternativeResolver.resolve4(hostname6),
|
|
51912
|
+
this.alternativeResolver.resolve6(hostname6)
|
|
51913
51913
|
]);
|
|
51914
51914
|
if (records.every((result) => result.status === "rejected")) {
|
|
51915
51915
|
throw new Error(records[0].reason);
|
|
@@ -51921,15 +51921,15 @@ var require_resolver_dns = __commonJS({
|
|
|
51921
51921
|
port: +this.port
|
|
51922
51922
|
}));
|
|
51923
51923
|
}
|
|
51924
|
-
const addressList = await dns_1.promises.lookup(
|
|
51924
|
+
const addressList = await dns_1.promises.lookup(hostname6, { all: true });
|
|
51925
51925
|
return addressList.map((addr) => ({ host: addr.address, port: +this.port }));
|
|
51926
51926
|
}
|
|
51927
|
-
async resolveTxt(
|
|
51927
|
+
async resolveTxt(hostname6) {
|
|
51928
51928
|
if (environment_1.GRPC_NODE_USE_ALTERNATIVE_RESOLVER) {
|
|
51929
51929
|
trace("Using alternative DNS resolver.");
|
|
51930
|
-
return this.alternativeResolver.resolveTxt(
|
|
51930
|
+
return this.alternativeResolver.resolveTxt(hostname6);
|
|
51931
51931
|
}
|
|
51932
|
-
return dns_1.promises.resolveTxt(
|
|
51932
|
+
return dns_1.promises.resolveTxt(hostname6);
|
|
51933
51933
|
}
|
|
51934
51934
|
startNextResolutionTimer() {
|
|
51935
51935
|
var _a3, _b;
|
|
@@ -52057,13 +52057,13 @@ var require_http_proxy = __commonJS({
|
|
|
52057
52057
|
userCred = proxyUrl.username;
|
|
52058
52058
|
}
|
|
52059
52059
|
}
|
|
52060
|
-
const
|
|
52060
|
+
const hostname6 = proxyUrl.hostname;
|
|
52061
52061
|
let port = proxyUrl.port;
|
|
52062
52062
|
if (port === "") {
|
|
52063
52063
|
port = "80";
|
|
52064
52064
|
}
|
|
52065
52065
|
const result = {
|
|
52066
|
-
address: `${
|
|
52066
|
+
address: `${hostname6}:${port}`
|
|
52067
52067
|
};
|
|
52068
52068
|
if (userCred) {
|
|
52069
52069
|
result.creds = userCred;
|
|
@@ -53376,8 +53376,8 @@ var require_load_balancing_call = __commonJS({
|
|
|
53376
53376
|
if (splitPath2.length >= 2) {
|
|
53377
53377
|
serviceName = splitPath2[1];
|
|
53378
53378
|
}
|
|
53379
|
-
const
|
|
53380
|
-
this.serviceUrl = `https://${
|
|
53379
|
+
const hostname6 = (_b = (_a3 = (0, uri_parser_1.splitHostPort)(this.host)) === null || _a3 === void 0 ? void 0 : _a3.host) !== null && _b !== void 0 ? _b : "localhost";
|
|
53380
|
+
this.serviceUrl = `https://${hostname6}/${serviceName}`;
|
|
53381
53381
|
this.startTime = /* @__PURE__ */ new Date();
|
|
53382
53382
|
}
|
|
53383
53383
|
getDeadlineInfo() {
|
|
@@ -91186,9 +91186,9 @@ var require_parse_host_uri = __commonJS({
|
|
|
91186
91186
|
var ipv4Hostname = "(?:\\d{1,3}(?:\\.\\d{1,3}){3})";
|
|
91187
91187
|
var ipv6Hostname = "(?:\\[(?<ipv6>[0-9a-fA-F.:]+)\\])";
|
|
91188
91188
|
var dnsHostname = "(?:[^:/]+)";
|
|
91189
|
-
var
|
|
91189
|
+
var hostname6 = `(?:${ipv4Hostname}|${ipv6Hostname}|${dnsHostname})`;
|
|
91190
91190
|
var port = "(?::(?<port>\\d+))";
|
|
91191
|
-
var protoHostPortRegex = new RegExp(`^${scheme}??(?<hostname>${
|
|
91191
|
+
var protoHostPortRegex = new RegExp(`^${scheme}??(?<hostname>${hostname6})${port}?$`);
|
|
91192
91192
|
function splitProtoHostPort(uri) {
|
|
91193
91193
|
const match2 = protoHostPortRegex.exec(uri);
|
|
91194
91194
|
if (!match2?.groups)
|
|
@@ -91200,9 +91200,9 @@ var require_parse_host_uri = __commonJS({
|
|
|
91200
91200
|
};
|
|
91201
91201
|
}
|
|
91202
91202
|
function joinProtoHostPort(components) {
|
|
91203
|
-
const { scheme: scheme2, hostname:
|
|
91203
|
+
const { scheme: scheme2, hostname: hostname7, port: port2 } = components;
|
|
91204
91204
|
const schemeText = scheme2 ? `${scheme2}:` : "";
|
|
91205
|
-
const hostnameText =
|
|
91205
|
+
const hostnameText = hostname7.includes(":") ? `[${hostname7}]` : hostname7;
|
|
91206
91206
|
const portText = port2 !== void 0 ? `:${port2}` : "";
|
|
91207
91207
|
return `${schemeText}${hostnameText}${portText}`;
|
|
91208
91208
|
}
|
|
@@ -104140,11 +104140,11 @@ var require_pg_connection_string = __commonJS({
|
|
|
104140
104140
|
config2.client_encoding = result.searchParams.get("encoding");
|
|
104141
104141
|
return config2;
|
|
104142
104142
|
}
|
|
104143
|
-
const
|
|
104143
|
+
const hostname6 = dummyHost ? "" : result.hostname;
|
|
104144
104144
|
if (!config2.host) {
|
|
104145
|
-
config2.host = decodeURIComponent(
|
|
104146
|
-
} else if (
|
|
104147
|
-
result.pathname =
|
|
104145
|
+
config2.host = decodeURIComponent(hostname6);
|
|
104146
|
+
} else if (hostname6 && /^%2f/i.test(hostname6)) {
|
|
104147
|
+
result.pathname = hostname6 + result.pathname;
|
|
104148
104148
|
}
|
|
104149
104149
|
if (!config2.port) {
|
|
104150
104150
|
config2.port = result.port;
|
|
@@ -135423,10 +135423,10 @@ async function search(query, options, scanner) {
|
|
|
135423
135423
|
// src/server.ts
|
|
135424
135424
|
var import_crypto13 = require("crypto");
|
|
135425
135425
|
var import_events2 = require("events");
|
|
135426
|
-
var
|
|
135426
|
+
var import_fs30 = require("fs");
|
|
135427
135427
|
var import_promises16 = require("fs/promises");
|
|
135428
135428
|
var import_http = require("http");
|
|
135429
|
-
var
|
|
135429
|
+
var import_os13 = require("os");
|
|
135430
135430
|
var import_path29 = require("path");
|
|
135431
135431
|
var import_readline4 = require("readline");
|
|
135432
135432
|
|
|
@@ -138231,6 +138231,215 @@ var errorMiddleware = (err, c) => {
|
|
|
138231
138231
|
return c.json({ error: message }, 500);
|
|
138232
138232
|
};
|
|
138233
138233
|
|
|
138234
|
+
// src/api/routes/backup.routes.ts
|
|
138235
|
+
var import_os5 = require("os");
|
|
138236
|
+
|
|
138237
|
+
// src/services/backup/backup.ts
|
|
138238
|
+
var BACKUP_FORMAT_VERSION = 1;
|
|
138239
|
+
var BackupError = class extends Error {
|
|
138240
|
+
constructor(message, code) {
|
|
138241
|
+
super(message);
|
|
138242
|
+
this.code = code;
|
|
138243
|
+
}
|
|
138244
|
+
code;
|
|
138245
|
+
};
|
|
138246
|
+
function validateArchive(input) {
|
|
138247
|
+
if (!input || typeof input !== "object") {
|
|
138248
|
+
throw new BackupError("Backup is not an object", "INVALID_ARCHIVE");
|
|
138249
|
+
}
|
|
138250
|
+
const archive = input;
|
|
138251
|
+
const manifest = archive.manifest;
|
|
138252
|
+
if (!manifest || typeof manifest !== "object") {
|
|
138253
|
+
throw new BackupError("Backup is missing its manifest", "INVALID_ARCHIVE");
|
|
138254
|
+
}
|
|
138255
|
+
if (manifest.formatVersion !== BACKUP_FORMAT_VERSION) {
|
|
138256
|
+
throw new BackupError(
|
|
138257
|
+
`Unsupported backup format version ${String(manifest.formatVersion)}; this build reads version ${BACKUP_FORMAT_VERSION}`,
|
|
138258
|
+
"UNSUPPORTED_VERSION"
|
|
138259
|
+
);
|
|
138260
|
+
}
|
|
138261
|
+
if (!Array.isArray(archive.projects)) {
|
|
138262
|
+
throw new BackupError("Backup is missing its projects array", "INVALID_ARCHIVE");
|
|
138263
|
+
}
|
|
138264
|
+
for (const [i, p2] of archive.projects.entries()) {
|
|
138265
|
+
if (!p2 || typeof p2 !== "object") {
|
|
138266
|
+
throw new BackupError(`Project at index ${i} is not an object`, "INVALID_ARCHIVE");
|
|
138267
|
+
}
|
|
138268
|
+
if (typeof p2.id !== "string" || p2.id.length === 0) {
|
|
138269
|
+
throw new BackupError(`Project at index ${i} has no id`, "INVALID_ARCHIVE");
|
|
138270
|
+
}
|
|
138271
|
+
if (typeof p2.path !== "string" || p2.path.length === 0) {
|
|
138272
|
+
throw new BackupError(`Project at index ${i} has no path`, "INVALID_ARCHIVE");
|
|
138273
|
+
}
|
|
138274
|
+
}
|
|
138275
|
+
const ids = new Set(archive.projects.map((p2) => p2.id));
|
|
138276
|
+
if (ids.size !== archive.projects.length) {
|
|
138277
|
+
throw new BackupError("Backup contains duplicate project ids", "INVALID_ARCHIVE");
|
|
138278
|
+
}
|
|
138279
|
+
return archive;
|
|
138280
|
+
}
|
|
138281
|
+
function remapPaths(projects, rules) {
|
|
138282
|
+
const ordered = [...rules].sort((a, b2) => b2.from.length - a.from.length);
|
|
138283
|
+
return projects.map((p2) => {
|
|
138284
|
+
const rule = ordered.find((r) => p2.path === r.from || p2.path.startsWith(`${r.from}/`));
|
|
138285
|
+
if (!rule) return p2;
|
|
138286
|
+
return { ...p2, path: `${rule.to}${p2.path.slice(rule.from.length)}` };
|
|
138287
|
+
});
|
|
138288
|
+
}
|
|
138289
|
+
function planRestore(incoming, existing) {
|
|
138290
|
+
const byId = new Map(existing.map((e) => [e.id, e]));
|
|
138291
|
+
const byPath = new Map(existing.map((e) => [e.path, e]));
|
|
138292
|
+
const plan = { create: [], update: [], conflict: [] };
|
|
138293
|
+
for (const p2 of incoming) {
|
|
138294
|
+
const sameId = byId.get(p2.id);
|
|
138295
|
+
if (sameId) {
|
|
138296
|
+
if (sameId.path !== p2.path) plan.update.push(p2);
|
|
138297
|
+
continue;
|
|
138298
|
+
}
|
|
138299
|
+
const samePath = byPath.get(p2.path);
|
|
138300
|
+
if (samePath) {
|
|
138301
|
+
plan.conflict.push({ incoming: p2, existingId: samePath.id });
|
|
138302
|
+
continue;
|
|
138303
|
+
}
|
|
138304
|
+
plan.create.push(p2);
|
|
138305
|
+
}
|
|
138306
|
+
return plan;
|
|
138307
|
+
}
|
|
138308
|
+
|
|
138309
|
+
// src/version.ts
|
|
138310
|
+
var import_node_fs7 = require("fs");
|
|
138311
|
+
var import_node_path8 = require("path");
|
|
138312
|
+
var cached2;
|
|
138313
|
+
function getVersion() {
|
|
138314
|
+
if (cached2 !== void 0) return cached2;
|
|
138315
|
+
cached2 = resolveVersion();
|
|
138316
|
+
return cached2;
|
|
138317
|
+
}
|
|
138318
|
+
function resolveVersion() {
|
|
138319
|
+
const scriptPath = process.argv[1] ?? "";
|
|
138320
|
+
const here = scriptPath ? (0, import_node_path8.dirname)(scriptPath) : process.cwd();
|
|
138321
|
+
let realHere = here;
|
|
138322
|
+
try {
|
|
138323
|
+
realHere = (0, import_node_path8.dirname)((0, import_node_fs7.realpathSync)(scriptPath));
|
|
138324
|
+
} catch {
|
|
138325
|
+
}
|
|
138326
|
+
const searchDirs = realHere === here ? [here, (0, import_node_path8.join)(here, "..")] : [here, (0, import_node_path8.join)(here, ".."), realHere, (0, import_node_path8.join)(realHere, "..")];
|
|
138327
|
+
for (const dir of searchDirs) {
|
|
138328
|
+
try {
|
|
138329
|
+
const v2 = (0, import_node_fs7.readFileSync)((0, import_node_path8.join)(dir, "version.txt"), "utf8").trim();
|
|
138330
|
+
if (v2) return v2;
|
|
138331
|
+
} catch {
|
|
138332
|
+
}
|
|
138333
|
+
}
|
|
138334
|
+
try {
|
|
138335
|
+
const pkg = JSON.parse((0, import_node_fs7.readFileSync)((0, import_node_path8.join)(here, "..", "package.json"), "utf8"));
|
|
138336
|
+
if (pkg.version) return `${pkg.version}+source`;
|
|
138337
|
+
} catch {
|
|
138338
|
+
}
|
|
138339
|
+
return "0.0.0+unknown";
|
|
138340
|
+
}
|
|
138341
|
+
|
|
138342
|
+
// src/api/routes/backup.routes.ts
|
|
138343
|
+
function readBody(c) {
|
|
138344
|
+
return new Promise((resolve4, reject) => {
|
|
138345
|
+
const chunks = [];
|
|
138346
|
+
c.env.incoming.on("data", (chunk) => chunks.push(chunk));
|
|
138347
|
+
c.env.incoming.on("end", () => {
|
|
138348
|
+
try {
|
|
138349
|
+
const raw2 = Buffer.concat(chunks).toString("utf-8");
|
|
138350
|
+
resolve4(raw2 ? JSON.parse(raw2) : {});
|
|
138351
|
+
} catch {
|
|
138352
|
+
reject(new Error("Invalid JSON body"));
|
|
138353
|
+
}
|
|
138354
|
+
});
|
|
138355
|
+
c.env.incoming.on("error", reject);
|
|
138356
|
+
});
|
|
138357
|
+
}
|
|
138358
|
+
var createBackupRoutes = (deps) => {
|
|
138359
|
+
const app = new Hono2();
|
|
138360
|
+
app.get("/export", (c) => {
|
|
138361
|
+
const repo = deps.projectsRepo();
|
|
138362
|
+
if (!repo) {
|
|
138363
|
+
return c.json({ error: "Project store is unavailable", code: "STORE_UNAVAILABLE" }, 503);
|
|
138364
|
+
}
|
|
138365
|
+
const projects = repo.listProjects().map((p2) => ({
|
|
138366
|
+
id: p2.id,
|
|
138367
|
+
path: p2.path,
|
|
138368
|
+
name: p2.name ?? null,
|
|
138369
|
+
createdAt: p2.createdAt,
|
|
138370
|
+
updatedAt: p2.updatedAt
|
|
138371
|
+
}));
|
|
138372
|
+
return c.json({
|
|
138373
|
+
manifest: {
|
|
138374
|
+
formatVersion: BACKUP_FORMAT_VERSION,
|
|
138375
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
138376
|
+
streamerVersion: getVersion(),
|
|
138377
|
+
sourceHost: (0, import_os5.hostname)(),
|
|
138378
|
+
// No endpoint here exports the API key. The flag is recorded so an
|
|
138379
|
+
// archive is self-describing about its own sensitivity rather than
|
|
138380
|
+
// requiring a reader to infer it.
|
|
138381
|
+
includesSecrets: false,
|
|
138382
|
+
counts: { projects: projects.length }
|
|
138383
|
+
},
|
|
138384
|
+
projects
|
|
138385
|
+
});
|
|
138386
|
+
});
|
|
138387
|
+
app.post("/restore", async (c) => {
|
|
138388
|
+
const repo = deps.projectsRepo();
|
|
138389
|
+
if (!repo) {
|
|
138390
|
+
return c.json({ error: "Project store is unavailable", code: "STORE_UNAVAILABLE" }, 503);
|
|
138391
|
+
}
|
|
138392
|
+
let body;
|
|
138393
|
+
try {
|
|
138394
|
+
body = await readBody(c);
|
|
138395
|
+
} catch {
|
|
138396
|
+
return c.json({ error: "Invalid JSON body", code: "INVALID_BODY" }, 400);
|
|
138397
|
+
}
|
|
138398
|
+
let archive;
|
|
138399
|
+
try {
|
|
138400
|
+
archive = validateArchive(body.archive);
|
|
138401
|
+
} catch (err) {
|
|
138402
|
+
if (err instanceof BackupError) {
|
|
138403
|
+
return c.json({ error: err.message, code: err.code }, 400);
|
|
138404
|
+
}
|
|
138405
|
+
throw err;
|
|
138406
|
+
}
|
|
138407
|
+
const rules = Array.isArray(body.pathMap) ? body.pathMap.filter((r) => typeof r?.from === "string" && typeof r?.to === "string").map((r) => ({ from: r.from, to: r.to })) : [];
|
|
138408
|
+
const incoming = rules.length > 0 ? remapPaths(archive.projects, rules) : archive.projects;
|
|
138409
|
+
const existing = repo.listProjects().map((p2) => ({ id: p2.id, path: p2.path }));
|
|
138410
|
+
const plan = planRestore(incoming, existing);
|
|
138411
|
+
const summary = {
|
|
138412
|
+
create: plan.create.length,
|
|
138413
|
+
update: plan.update.length,
|
|
138414
|
+
conflict: plan.conflict.length
|
|
138415
|
+
};
|
|
138416
|
+
if (body.apply !== true) {
|
|
138417
|
+
return c.json({ applied: false, summary, plan });
|
|
138418
|
+
}
|
|
138419
|
+
if (plan.conflict.length > 0) {
|
|
138420
|
+
return c.json(
|
|
138421
|
+
{
|
|
138422
|
+
error: "Restore has unresolved conflicts",
|
|
138423
|
+
code: "RESTORE_CONFLICT",
|
|
138424
|
+
summary,
|
|
138425
|
+
plan
|
|
138426
|
+
},
|
|
138427
|
+
409
|
|
138428
|
+
);
|
|
138429
|
+
}
|
|
138430
|
+
let applied = 0;
|
|
138431
|
+
for (const p2 of [...plan.create, ...plan.update]) {
|
|
138432
|
+
try {
|
|
138433
|
+
repo.upsertProjectByPath(p2.path, { name: p2.name });
|
|
138434
|
+
applied++;
|
|
138435
|
+
} catch {
|
|
138436
|
+
}
|
|
138437
|
+
}
|
|
138438
|
+
return c.json({ applied: true, summary, appliedCount: applied });
|
|
138439
|
+
});
|
|
138440
|
+
return app;
|
|
138441
|
+
};
|
|
138442
|
+
|
|
138234
138443
|
// src/api/routes/browse.routes.ts
|
|
138235
138444
|
var ALREADY_HANDLED = 597;
|
|
138236
138445
|
var alreadyHandled = () => new Response(null, { status: ALREADY_HANDLED });
|
|
@@ -138413,38 +138622,251 @@ var createDeviceRoutes = (deps) => {
|
|
|
138413
138622
|
return app;
|
|
138414
138623
|
};
|
|
138415
138624
|
|
|
138416
|
-
// src/
|
|
138417
|
-
var
|
|
138418
|
-
|
|
138419
|
-
|
|
138420
|
-
|
|
138421
|
-
|
|
138422
|
-
|
|
138423
|
-
|
|
138424
|
-
|
|
138425
|
-
|
|
138426
|
-
|
|
138427
|
-
|
|
138428
|
-
|
|
138429
|
-
|
|
138430
|
-
|
|
138431
|
-
|
|
138625
|
+
// src/api/routes/diagnostics.routes.ts
|
|
138626
|
+
var import_fs14 = require("fs");
|
|
138627
|
+
|
|
138628
|
+
// src/platform.ts
|
|
138629
|
+
var import_child_process = require("child_process");
|
|
138630
|
+
var import_fs13 = require("fs");
|
|
138631
|
+
var import_os6 = require("os");
|
|
138632
|
+
var import_path13 = require("path");
|
|
138633
|
+
var isWindows2 = (0, import_os6.platform)() === "win32";
|
|
138634
|
+
var _claudeExe;
|
|
138635
|
+
function resolveClaudeExe() {
|
|
138636
|
+
if (_claudeExe !== void 0) return _claudeExe;
|
|
138637
|
+
if (isWindows2) {
|
|
138638
|
+
try {
|
|
138639
|
+
const found = (0, import_child_process.execFileSync)("where.exe", ["claude"], {
|
|
138640
|
+
encoding: "utf-8",
|
|
138641
|
+
windowsHide: true,
|
|
138642
|
+
timeout: 3e3
|
|
138643
|
+
}).trim().split("\n")[0].trim();
|
|
138644
|
+
if (found) {
|
|
138645
|
+
_claudeExe = found;
|
|
138646
|
+
return _claudeExe;
|
|
138647
|
+
}
|
|
138648
|
+
} catch {
|
|
138649
|
+
}
|
|
138650
|
+
const candidates = [
|
|
138651
|
+
(0, import_path13.join)((0, import_os6.homedir)(), ".local", "bin", "claude.exe"),
|
|
138652
|
+
(0, import_path13.join)(
|
|
138653
|
+
process.env.LOCALAPPDATA ?? (0, import_path13.join)((0, import_os6.homedir)(), "AppData", "Local"),
|
|
138654
|
+
"Microsoft",
|
|
138655
|
+
"WindowsApps",
|
|
138656
|
+
"claude.exe"
|
|
138657
|
+
)
|
|
138658
|
+
];
|
|
138659
|
+
for (const p2 of candidates) {
|
|
138660
|
+
if ((0, import_fs13.existsSync)(p2)) {
|
|
138661
|
+
_claudeExe = p2;
|
|
138662
|
+
return _claudeExe;
|
|
138663
|
+
}
|
|
138664
|
+
}
|
|
138665
|
+
} else {
|
|
138666
|
+
try {
|
|
138667
|
+
const found = (0, import_child_process.execFileSync)("/usr/bin/which", ["claude"], {
|
|
138668
|
+
encoding: "utf-8",
|
|
138669
|
+
timeout: 3e3
|
|
138670
|
+
}).trim().split("\n")[0].trim();
|
|
138671
|
+
if (found && (0, import_fs13.existsSync)(found)) {
|
|
138672
|
+
_claudeExe = found;
|
|
138673
|
+
return _claudeExe;
|
|
138674
|
+
}
|
|
138675
|
+
} catch {
|
|
138676
|
+
}
|
|
138677
|
+
const candidates = [
|
|
138678
|
+
"/opt/homebrew/bin/claude",
|
|
138679
|
+
"/usr/local/bin/claude",
|
|
138680
|
+
(0, import_path13.join)((0, import_os6.homedir)(), ".local", "bin", "claude")
|
|
138681
|
+
];
|
|
138682
|
+
for (const p2 of candidates) {
|
|
138683
|
+
if ((0, import_fs13.existsSync)(p2)) {
|
|
138684
|
+
_claudeExe = p2;
|
|
138685
|
+
return _claudeExe;
|
|
138686
|
+
}
|
|
138687
|
+
}
|
|
138432
138688
|
}
|
|
138433
|
-
|
|
138434
|
-
|
|
138689
|
+
_claudeExe = "claude";
|
|
138690
|
+
return _claudeExe;
|
|
138691
|
+
}
|
|
138692
|
+
var _codexExe;
|
|
138693
|
+
function resolveCodexExe() {
|
|
138694
|
+
if (_codexExe !== void 0) return _codexExe;
|
|
138695
|
+
if (isWindows2) {
|
|
138435
138696
|
try {
|
|
138436
|
-
const
|
|
138437
|
-
|
|
138697
|
+
const found = (0, import_child_process.execFileSync)("where.exe", ["codex"], {
|
|
138698
|
+
encoding: "utf-8",
|
|
138699
|
+
windowsHide: true,
|
|
138700
|
+
timeout: 3e3
|
|
138701
|
+
}).trim().split("\n")[0].trim();
|
|
138702
|
+
if (found) {
|
|
138703
|
+
_codexExe = found;
|
|
138704
|
+
return _codexExe;
|
|
138705
|
+
}
|
|
138438
138706
|
} catch {
|
|
138439
138707
|
}
|
|
138708
|
+
const candidates = [
|
|
138709
|
+
(0, import_path13.join)((0, import_os6.homedir)(), ".local", "bin", "codex.exe"),
|
|
138710
|
+
(0, import_path13.join)(
|
|
138711
|
+
process.env.LOCALAPPDATA ?? (0, import_path13.join)((0, import_os6.homedir)(), "AppData", "Local"),
|
|
138712
|
+
"Microsoft",
|
|
138713
|
+
"WindowsApps",
|
|
138714
|
+
"codex.exe"
|
|
138715
|
+
)
|
|
138716
|
+
];
|
|
138717
|
+
for (const p2 of candidates) {
|
|
138718
|
+
if ((0, import_fs13.existsSync)(p2)) {
|
|
138719
|
+
_codexExe = p2;
|
|
138720
|
+
return _codexExe;
|
|
138721
|
+
}
|
|
138722
|
+
}
|
|
138723
|
+
} else {
|
|
138724
|
+
try {
|
|
138725
|
+
const found = (0, import_child_process.execFileSync)("/usr/bin/which", ["codex"], {
|
|
138726
|
+
encoding: "utf-8",
|
|
138727
|
+
timeout: 3e3
|
|
138728
|
+
}).trim().split("\n")[0].trim();
|
|
138729
|
+
if (found && (0, import_fs13.existsSync)(found)) {
|
|
138730
|
+
_codexExe = found;
|
|
138731
|
+
return _codexExe;
|
|
138732
|
+
}
|
|
138733
|
+
} catch {
|
|
138734
|
+
}
|
|
138735
|
+
const candidates = [
|
|
138736
|
+
"/opt/homebrew/bin/codex",
|
|
138737
|
+
"/usr/local/bin/codex",
|
|
138738
|
+
(0, import_path13.join)((0, import_os6.homedir)(), ".local", "bin", "codex")
|
|
138739
|
+
];
|
|
138740
|
+
for (const p2 of candidates) {
|
|
138741
|
+
if ((0, import_fs13.existsSync)(p2)) {
|
|
138742
|
+
_codexExe = p2;
|
|
138743
|
+
return _codexExe;
|
|
138744
|
+
}
|
|
138745
|
+
}
|
|
138440
138746
|
}
|
|
138747
|
+
_codexExe = "codex";
|
|
138748
|
+
return _codexExe;
|
|
138749
|
+
}
|
|
138750
|
+
|
|
138751
|
+
// src/services/diagnostics/diagnostics.ts
|
|
138752
|
+
var DIAGNOSTICS_CONTRACT_VERSION = 1;
|
|
138753
|
+
function redactPath(path2) {
|
|
138754
|
+
if (!path2) return null;
|
|
138755
|
+
const parts = path2.split(/[/\\]/).filter(Boolean);
|
|
138756
|
+
if (parts.length <= 2) return parts.join("/");
|
|
138757
|
+
return `\u2026/${parts.slice(-2).join("/")}`;
|
|
138758
|
+
}
|
|
138759
|
+
function worstStatus(checks) {
|
|
138760
|
+
const rank = { ok: 0, unknown: 1, degraded: 2, failed: 3 };
|
|
138761
|
+
return checks.reduce(
|
|
138762
|
+
(worst, c) => rank[c.status] > rank[worst] ? c.status : worst,
|
|
138763
|
+
"ok"
|
|
138764
|
+
);
|
|
138765
|
+
}
|
|
138766
|
+
function buildReport(checks, now = /* @__PURE__ */ new Date()) {
|
|
138767
|
+
return {
|
|
138768
|
+
contractVersion: DIAGNOSTICS_CONTRACT_VERSION,
|
|
138769
|
+
generatedAt: now.toISOString(),
|
|
138770
|
+
overall: worstStatus(checks),
|
|
138771
|
+
checks
|
|
138772
|
+
};
|
|
138773
|
+
}
|
|
138774
|
+
var SECRET_KEY_RE = /(key|token|secret|password|passwd|credential|authorization|cookie)/i;
|
|
138775
|
+
function redactValue(value) {
|
|
138776
|
+
if (Array.isArray(value)) {
|
|
138777
|
+
return value.map((v2) => redactValue(v2));
|
|
138778
|
+
}
|
|
138779
|
+
if (value && typeof value === "object") {
|
|
138780
|
+
const out = {};
|
|
138781
|
+
for (const [k2, v2] of Object.entries(value)) {
|
|
138782
|
+
out[k2] = SECRET_KEY_RE.test(k2) ? "[redacted]" : redactValue(v2);
|
|
138783
|
+
}
|
|
138784
|
+
return out;
|
|
138785
|
+
}
|
|
138786
|
+
return value;
|
|
138787
|
+
}
|
|
138788
|
+
|
|
138789
|
+
// src/api/routes/diagnostics.routes.ts
|
|
138790
|
+
function providerCheck(name, resolve4) {
|
|
138441
138791
|
try {
|
|
138442
|
-
const
|
|
138443
|
-
|
|
138792
|
+
const exe = resolve4();
|
|
138793
|
+
return {
|
|
138794
|
+
id: `provider:${name}`,
|
|
138795
|
+
status: "ok",
|
|
138796
|
+
summary: `${name} CLI is installed.`,
|
|
138797
|
+
remediation: "NONE",
|
|
138798
|
+
detail: { location: redactPath(exe) }
|
|
138799
|
+
};
|
|
138444
138800
|
} catch {
|
|
138801
|
+
return {
|
|
138802
|
+
id: `provider:${name}`,
|
|
138803
|
+
status: "failed",
|
|
138804
|
+
summary: `${name} CLI could not be located. Sessions for this provider cannot start.`,
|
|
138805
|
+
remediation: "PROVIDER_NOT_INSTALLED"
|
|
138806
|
+
};
|
|
138445
138807
|
}
|
|
138446
|
-
return "0.0.0+unknown";
|
|
138447
138808
|
}
|
|
138809
|
+
var createDiagnosticsRoutes = (deps) => {
|
|
138810
|
+
const app = new Hono2();
|
|
138811
|
+
app.get("/", (c) => {
|
|
138812
|
+
const checks = [];
|
|
138813
|
+
checks.push({
|
|
138814
|
+
id: "streamer",
|
|
138815
|
+
status: "ok",
|
|
138816
|
+
summary: "Streamer is running.",
|
|
138817
|
+
remediation: "NONE",
|
|
138818
|
+
detail: { version: getVersion(), uptimeSeconds: Math.floor(process.uptime()) }
|
|
138819
|
+
});
|
|
138820
|
+
checks.push(providerCheck("claude-code", resolveClaudeExe));
|
|
138821
|
+
checks.push(providerCheck("codex-cli", resolveCodexExe));
|
|
138822
|
+
const cacheAlert = deps.cacheMonitor()?.healthzField();
|
|
138823
|
+
checks.push(
|
|
138824
|
+
cacheAlert ? {
|
|
138825
|
+
id: "cache",
|
|
138826
|
+
status: "degraded",
|
|
138827
|
+
summary: "Conversation cache reported an integrity alert.",
|
|
138828
|
+
remediation: "CACHE_DEGRADED"
|
|
138829
|
+
} : {
|
|
138830
|
+
id: "cache",
|
|
138831
|
+
status: "ok",
|
|
138832
|
+
summary: "Conversation cache is healthy.",
|
|
138833
|
+
remediation: "NONE"
|
|
138834
|
+
}
|
|
138835
|
+
);
|
|
138836
|
+
let ptyOk = true;
|
|
138837
|
+
try {
|
|
138838
|
+
require.resolve("node-pty");
|
|
138839
|
+
} catch {
|
|
138840
|
+
ptyOk = false;
|
|
138841
|
+
}
|
|
138842
|
+
checks.push(
|
|
138843
|
+
ptyOk ? { id: "pty", status: "ok", summary: "PTY subsystem is available.", remediation: "NONE" } : {
|
|
138844
|
+
id: "pty",
|
|
138845
|
+
status: "failed",
|
|
138846
|
+
summary: "node-pty failed to load, so no managed session can start.",
|
|
138847
|
+
remediation: "PTY_UNAVAILABLE"
|
|
138848
|
+
}
|
|
138849
|
+
);
|
|
138850
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
138851
|
+
const claudeProjects = home ? `${home}/.claude/projects` : "";
|
|
138852
|
+
checks.push(
|
|
138853
|
+
claudeProjects && (0, import_fs14.existsSync)(claudeProjects) ? {
|
|
138854
|
+
id: "filesystem",
|
|
138855
|
+
status: "ok",
|
|
138856
|
+
summary: "Provider history directory is present.",
|
|
138857
|
+
remediation: "NONE",
|
|
138858
|
+
detail: { location: redactPath(claudeProjects) }
|
|
138859
|
+
} : {
|
|
138860
|
+
id: "filesystem",
|
|
138861
|
+
status: "degraded",
|
|
138862
|
+
summary: "Provider history directory was not found; history may be unavailable.",
|
|
138863
|
+
remediation: "FS_SCOPE_MISSING"
|
|
138864
|
+
}
|
|
138865
|
+
);
|
|
138866
|
+
return c.json(redactValue(buildReport(checks)));
|
|
138867
|
+
});
|
|
138868
|
+
return app;
|
|
138869
|
+
};
|
|
138448
138870
|
|
|
138449
138871
|
// src/api/routes/health.routes.ts
|
|
138450
138872
|
var createHealthRoutes = (deps) => {
|
|
@@ -138579,7 +139001,7 @@ function createLogsRoutes() {
|
|
|
138579
139001
|
// src/api/routes/misc.routes.ts
|
|
138580
139002
|
var import_node_child_process2 = require("child_process");
|
|
138581
139003
|
var import_node_crypto = require("crypto");
|
|
138582
|
-
var
|
|
139004
|
+
var import_os7 = require("os");
|
|
138583
139005
|
|
|
138584
139006
|
// src/db/repositories/push.repository.ts
|
|
138585
139007
|
var FAILURE_STREAK_LIMIT = 5;
|
|
@@ -138879,7 +139301,7 @@ var createMiscRoutes = (deps) => {
|
|
|
138879
139301
|
const ptyIds = deps.ptyAttachedIds();
|
|
138880
139302
|
return c.json({
|
|
138881
139303
|
version: getVersion(),
|
|
138882
|
-
machineName: (0,
|
|
139304
|
+
machineName: (0, import_os7.hostname)(),
|
|
138883
139305
|
platform: process.platform,
|
|
138884
139306
|
activeSessions: deps.sessionStore.list(ptyIds).filter((s3) => s3.status === "running").length,
|
|
138885
139307
|
publicUrl: deps.publicUrl,
|
|
@@ -139168,129 +139590,6 @@ var createProjectRoutes = (deps) => {
|
|
|
139168
139590
|
return app;
|
|
139169
139591
|
};
|
|
139170
139592
|
|
|
139171
|
-
// src/platform.ts
|
|
139172
|
-
var import_child_process = require("child_process");
|
|
139173
|
-
var import_fs13 = require("fs");
|
|
139174
|
-
var import_os6 = require("os");
|
|
139175
|
-
var import_path13 = require("path");
|
|
139176
|
-
var isWindows2 = (0, import_os6.platform)() === "win32";
|
|
139177
|
-
var _claudeExe;
|
|
139178
|
-
function resolveClaudeExe() {
|
|
139179
|
-
if (_claudeExe !== void 0) return _claudeExe;
|
|
139180
|
-
if (isWindows2) {
|
|
139181
|
-
try {
|
|
139182
|
-
const found = (0, import_child_process.execFileSync)("where.exe", ["claude"], {
|
|
139183
|
-
encoding: "utf-8",
|
|
139184
|
-
windowsHide: true,
|
|
139185
|
-
timeout: 3e3
|
|
139186
|
-
}).trim().split("\n")[0].trim();
|
|
139187
|
-
if (found) {
|
|
139188
|
-
_claudeExe = found;
|
|
139189
|
-
return _claudeExe;
|
|
139190
|
-
}
|
|
139191
|
-
} catch {
|
|
139192
|
-
}
|
|
139193
|
-
const candidates = [
|
|
139194
|
-
(0, import_path13.join)((0, import_os6.homedir)(), ".local", "bin", "claude.exe"),
|
|
139195
|
-
(0, import_path13.join)(
|
|
139196
|
-
process.env.LOCALAPPDATA ?? (0, import_path13.join)((0, import_os6.homedir)(), "AppData", "Local"),
|
|
139197
|
-
"Microsoft",
|
|
139198
|
-
"WindowsApps",
|
|
139199
|
-
"claude.exe"
|
|
139200
|
-
)
|
|
139201
|
-
];
|
|
139202
|
-
for (const p2 of candidates) {
|
|
139203
|
-
if ((0, import_fs13.existsSync)(p2)) {
|
|
139204
|
-
_claudeExe = p2;
|
|
139205
|
-
return _claudeExe;
|
|
139206
|
-
}
|
|
139207
|
-
}
|
|
139208
|
-
} else {
|
|
139209
|
-
try {
|
|
139210
|
-
const found = (0, import_child_process.execFileSync)("/usr/bin/which", ["claude"], {
|
|
139211
|
-
encoding: "utf-8",
|
|
139212
|
-
timeout: 3e3
|
|
139213
|
-
}).trim().split("\n")[0].trim();
|
|
139214
|
-
if (found && (0, import_fs13.existsSync)(found)) {
|
|
139215
|
-
_claudeExe = found;
|
|
139216
|
-
return _claudeExe;
|
|
139217
|
-
}
|
|
139218
|
-
} catch {
|
|
139219
|
-
}
|
|
139220
|
-
const candidates = [
|
|
139221
|
-
"/opt/homebrew/bin/claude",
|
|
139222
|
-
"/usr/local/bin/claude",
|
|
139223
|
-
(0, import_path13.join)((0, import_os6.homedir)(), ".local", "bin", "claude")
|
|
139224
|
-
];
|
|
139225
|
-
for (const p2 of candidates) {
|
|
139226
|
-
if ((0, import_fs13.existsSync)(p2)) {
|
|
139227
|
-
_claudeExe = p2;
|
|
139228
|
-
return _claudeExe;
|
|
139229
|
-
}
|
|
139230
|
-
}
|
|
139231
|
-
}
|
|
139232
|
-
_claudeExe = "claude";
|
|
139233
|
-
return _claudeExe;
|
|
139234
|
-
}
|
|
139235
|
-
var _codexExe;
|
|
139236
|
-
function resolveCodexExe() {
|
|
139237
|
-
if (_codexExe !== void 0) return _codexExe;
|
|
139238
|
-
if (isWindows2) {
|
|
139239
|
-
try {
|
|
139240
|
-
const found = (0, import_child_process.execFileSync)("where.exe", ["codex"], {
|
|
139241
|
-
encoding: "utf-8",
|
|
139242
|
-
windowsHide: true,
|
|
139243
|
-
timeout: 3e3
|
|
139244
|
-
}).trim().split("\n")[0].trim();
|
|
139245
|
-
if (found) {
|
|
139246
|
-
_codexExe = found;
|
|
139247
|
-
return _codexExe;
|
|
139248
|
-
}
|
|
139249
|
-
} catch {
|
|
139250
|
-
}
|
|
139251
|
-
const candidates = [
|
|
139252
|
-
(0, import_path13.join)((0, import_os6.homedir)(), ".local", "bin", "codex.exe"),
|
|
139253
|
-
(0, import_path13.join)(
|
|
139254
|
-
process.env.LOCALAPPDATA ?? (0, import_path13.join)((0, import_os6.homedir)(), "AppData", "Local"),
|
|
139255
|
-
"Microsoft",
|
|
139256
|
-
"WindowsApps",
|
|
139257
|
-
"codex.exe"
|
|
139258
|
-
)
|
|
139259
|
-
];
|
|
139260
|
-
for (const p2 of candidates) {
|
|
139261
|
-
if ((0, import_fs13.existsSync)(p2)) {
|
|
139262
|
-
_codexExe = p2;
|
|
139263
|
-
return _codexExe;
|
|
139264
|
-
}
|
|
139265
|
-
}
|
|
139266
|
-
} else {
|
|
139267
|
-
try {
|
|
139268
|
-
const found = (0, import_child_process.execFileSync)("/usr/bin/which", ["codex"], {
|
|
139269
|
-
encoding: "utf-8",
|
|
139270
|
-
timeout: 3e3
|
|
139271
|
-
}).trim().split("\n")[0].trim();
|
|
139272
|
-
if (found && (0, import_fs13.existsSync)(found)) {
|
|
139273
|
-
_codexExe = found;
|
|
139274
|
-
return _codexExe;
|
|
139275
|
-
}
|
|
139276
|
-
} catch {
|
|
139277
|
-
}
|
|
139278
|
-
const candidates = [
|
|
139279
|
-
"/opt/homebrew/bin/codex",
|
|
139280
|
-
"/usr/local/bin/codex",
|
|
139281
|
-
(0, import_path13.join)((0, import_os6.homedir)(), ".local", "bin", "codex")
|
|
139282
|
-
];
|
|
139283
|
-
for (const p2 of candidates) {
|
|
139284
|
-
if ((0, import_fs13.existsSync)(p2)) {
|
|
139285
|
-
_codexExe = p2;
|
|
139286
|
-
return _codexExe;
|
|
139287
|
-
}
|
|
139288
|
-
}
|
|
139289
|
-
}
|
|
139290
|
-
_codexExe = "codex";
|
|
139291
|
-
return _codexExe;
|
|
139292
|
-
}
|
|
139293
|
-
|
|
139294
139593
|
// src/providers.ts
|
|
139295
139594
|
var CLAUDE_CODE_PROVIDER2 = "claude-code";
|
|
139296
139595
|
var CODEX_CLI_PROVIDER2 = "codex-cli";
|
|
@@ -139576,6 +139875,7 @@ var createHonoApp = (deps, upgradeWebSocket) => {
|
|
|
139576
139875
|
app.use("*", authMiddleware(deps));
|
|
139577
139876
|
app.onError(errorMiddleware);
|
|
139578
139877
|
app.route("/healthz", createHealthRoutes(deps));
|
|
139878
|
+
app.route("/api/diagnostics", createDiagnosticsRoutes(deps));
|
|
139579
139879
|
app.route("/", createMiscRoutes(deps));
|
|
139580
139880
|
app.route("/api/sessions", createSessionRoutes(deps));
|
|
139581
139881
|
app.route("/api/conversations", createConversationRoutes(deps));
|
|
@@ -139584,6 +139884,7 @@ var createHonoApp = (deps, upgradeWebSocket) => {
|
|
|
139584
139884
|
app.route("/api/projects", createProjectRoutes(deps));
|
|
139585
139885
|
app.route("/api/providers", createProviderRoutes());
|
|
139586
139886
|
app.route("/api/devices", createDeviceRoutes(deps));
|
|
139887
|
+
app.route("/api/backup", createBackupRoutes(deps));
|
|
139587
139888
|
app.route("/api/pair", createPairRoutes(deps));
|
|
139588
139889
|
app.route("/api", createBrowseRoutes(deps));
|
|
139589
139890
|
app.route("/", createScannerRoutes(deps));
|
|
@@ -139654,13 +139955,13 @@ init_claude_flags();
|
|
|
139654
139955
|
|
|
139655
139956
|
// src/conversation-cache.ts
|
|
139656
139957
|
var import_better_sqlite32 = __toESM(require("better-sqlite3"), 1);
|
|
139657
|
-
var
|
|
139958
|
+
var import_fs17 = require("fs");
|
|
139658
139959
|
var import_promises12 = require("fs/promises");
|
|
139659
139960
|
var import_path17 = require("path");
|
|
139660
139961
|
var import_promises13 = require("timers/promises");
|
|
139661
139962
|
|
|
139662
139963
|
// src/db/sqlite-migrate.ts
|
|
139663
|
-
var
|
|
139964
|
+
var import_fs15 = require("fs");
|
|
139664
139965
|
var import_path15 = require("path");
|
|
139665
139966
|
var import_url6 = require("url");
|
|
139666
139967
|
var import_meta2 = {};
|
|
@@ -139679,7 +139980,7 @@ CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
|
139679
139980
|
function runSqliteMigrations(db, migrationsDir) {
|
|
139680
139981
|
db.exec(SCHEMA_MIGRATIONS_SQL);
|
|
139681
139982
|
const dir = migrationsDir ?? (0, import_path15.join)(getMigrationsDir(), "migrations");
|
|
139682
|
-
const files = (0,
|
|
139983
|
+
const files = (0, import_fs15.readdirSync)(dir).filter((f2) => f2.endsWith(".sql")).sort();
|
|
139683
139984
|
const appliedRows = db.prepare("SELECT id FROM schema_migrations").all();
|
|
139684
139985
|
const appliedSet = new Set(appliedRows.map((r) => r.id));
|
|
139685
139986
|
const recordApplied = db.prepare("INSERT INTO schema_migrations (id, applied_at) VALUES (?, ?)");
|
|
@@ -139690,7 +139991,7 @@ function runSqliteMigrations(db, migrationsDir) {
|
|
|
139690
139991
|
skipped.push(file2);
|
|
139691
139992
|
continue;
|
|
139692
139993
|
}
|
|
139693
|
-
const sql = (0,
|
|
139994
|
+
const sql = (0, import_fs15.readFileSync)((0, import_path15.join)(dir, file2), "utf-8");
|
|
139694
139995
|
const tx = db.transaction(() => {
|
|
139695
139996
|
db.exec(sql);
|
|
139696
139997
|
recordApplied.run(file2, (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -139702,7 +140003,7 @@ function runSqliteMigrations(db, migrationsDir) {
|
|
|
139702
140003
|
}
|
|
139703
140004
|
|
|
139704
140005
|
// src/services/conversations/isAgentConversation.ts
|
|
139705
|
-
var
|
|
140006
|
+
var import_fs16 = require("fs");
|
|
139706
140007
|
var DEFAULT_AGENT_ENTRYPOINTS = /* @__PURE__ */ new Set(["sdk-cli", "claude-vscode"]);
|
|
139707
140008
|
var CHUNK_BYTES = 64 * 1024;
|
|
139708
140009
|
var ENTRYPOINT_PROBE = `"entrypoint":`;
|
|
@@ -139724,12 +140025,12 @@ function isAgentFile(filePath, entrypoints = DEFAULT_AGENT_ENTRYPOINTS) {
|
|
|
139724
140025
|
if (cached3 !== void 0) return cached3;
|
|
139725
140026
|
let fd;
|
|
139726
140027
|
try {
|
|
139727
|
-
fd = (0,
|
|
140028
|
+
fd = (0, import_fs16.openSync)(filePath, "r");
|
|
139728
140029
|
} catch {
|
|
139729
140030
|
return false;
|
|
139730
140031
|
}
|
|
139731
140032
|
try {
|
|
139732
|
-
const fileSize = (0,
|
|
140033
|
+
const fileSize = (0, import_fs16.statSync)(filePath).size;
|
|
139733
140034
|
if (fileSize === 0) {
|
|
139734
140035
|
fileDecisionCache.set(key, false);
|
|
139735
140036
|
return false;
|
|
@@ -139740,7 +140041,7 @@ function isAgentFile(filePath, entrypoints = DEFAULT_AGENT_ENTRYPOINTS) {
|
|
|
139740
140041
|
let carry = "";
|
|
139741
140042
|
while (offset < fileSize) {
|
|
139742
140043
|
const toRead = Math.min(CHUNK_BYTES, fileSize - offset);
|
|
139743
|
-
const got = (0,
|
|
140044
|
+
const got = (0, import_fs16.readSync)(fd, buf, 0, toRead, offset);
|
|
139744
140045
|
if (got <= 0) break;
|
|
139745
140046
|
const chunk = carry + buf.toString("utf8", 0, got);
|
|
139746
140047
|
for (const marker of markers) {
|
|
@@ -139761,7 +140062,7 @@ function isAgentFile(filePath, entrypoints = DEFAULT_AGENT_ENTRYPOINTS) {
|
|
|
139761
140062
|
} catch {
|
|
139762
140063
|
return false;
|
|
139763
140064
|
} finally {
|
|
139764
|
-
(0,
|
|
140065
|
+
(0, import_fs16.closeSync)(fd);
|
|
139765
140066
|
}
|
|
139766
140067
|
}
|
|
139767
140068
|
function parseAgentEntrypointsEnv(raw2) {
|
|
@@ -140336,7 +140637,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140336
140637
|
if (!fileState) return null;
|
|
140337
140638
|
let stat7;
|
|
140338
140639
|
try {
|
|
140339
|
-
stat7 = (0,
|
|
140640
|
+
stat7 = (0, import_fs17.statSync)(filePath);
|
|
140340
140641
|
} catch {
|
|
140341
140642
|
return null;
|
|
140342
140643
|
}
|
|
@@ -140357,17 +140658,17 @@ var ConversationCache = class _ConversationCache {
|
|
|
140357
140658
|
);
|
|
140358
140659
|
if (rows.length === 0) return { messages: [], total, fromIndex: from };
|
|
140359
140660
|
const messages = [];
|
|
140360
|
-
const fd = (0,
|
|
140661
|
+
const fd = (0, import_fs17.openSync)(filePath, "r");
|
|
140361
140662
|
try {
|
|
140362
140663
|
const state = initialConvState();
|
|
140363
140664
|
for (const row of rows) {
|
|
140364
140665
|
const buf = Buffer.alloc(row.byte_length);
|
|
140365
|
-
(0,
|
|
140666
|
+
(0, import_fs17.readSync)(fd, buf, 0, row.byte_length, row.byte_offset);
|
|
140366
140667
|
const msg = parseJsonlLine(buf.toString("utf-8"), state);
|
|
140367
140668
|
if (msg) messages.push(msg);
|
|
140368
140669
|
}
|
|
140369
140670
|
} finally {
|
|
140370
|
-
(0,
|
|
140671
|
+
(0, import_fs17.closeSync)(fd);
|
|
140371
140672
|
}
|
|
140372
140673
|
return { messages, total, fromIndex: from };
|
|
140373
140674
|
}
|
|
@@ -140395,14 +140696,14 @@ var ConversationCache = class _ConversationCache {
|
|
|
140395
140696
|
isAgentFileCached(filePath) {
|
|
140396
140697
|
let s3;
|
|
140397
140698
|
try {
|
|
140398
|
-
s3 = (0,
|
|
140699
|
+
s3 = (0, import_fs17.statSync)(filePath);
|
|
140399
140700
|
} catch {
|
|
140400
140701
|
return false;
|
|
140401
140702
|
}
|
|
140402
140703
|
return this.classifyAgentFile(filePath, s3.mtimeMs, s3.size);
|
|
140403
140704
|
}
|
|
140404
140705
|
static open(dbPath, tailSize = 10, migrationsDir, options) {
|
|
140405
|
-
(0,
|
|
140706
|
+
(0, import_fs17.mkdirSync)((0, import_path17.dirname)(dbPath), { recursive: true });
|
|
140406
140707
|
const db = new import_better_sqlite32.default(dbPath);
|
|
140407
140708
|
db.pragma("journal_mode = WAL");
|
|
140408
140709
|
db.pragma("foreign_keys = ON");
|
|
@@ -140589,7 +140890,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140589
140890
|
let mtimeMs = null;
|
|
140590
140891
|
let fileSize = null;
|
|
140591
140892
|
try {
|
|
140592
|
-
const s3 = (0,
|
|
140893
|
+
const s3 = (0, import_fs17.statSync)(m2.filePath);
|
|
140593
140894
|
mtimeMs = s3.mtimeMs;
|
|
140594
140895
|
fileSize = s3.size;
|
|
140595
140896
|
} catch {
|
|
@@ -140649,8 +140950,8 @@ var ConversationCache = class _ConversationCache {
|
|
|
140649
140950
|
let fileSize;
|
|
140650
140951
|
let fd;
|
|
140651
140952
|
try {
|
|
140652
|
-
fileSize = (0,
|
|
140653
|
-
fd = (0,
|
|
140953
|
+
fileSize = (0, import_fs17.statSync)(filePath).size;
|
|
140954
|
+
fd = (0, import_fs17.openSync)(filePath, "r");
|
|
140654
140955
|
} catch {
|
|
140655
140956
|
return false;
|
|
140656
140957
|
}
|
|
@@ -140663,7 +140964,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140663
140964
|
while (pos > 0 && lines.length < this.tailSize * 4) {
|
|
140664
140965
|
const toRead = Math.min(CHUNK, pos);
|
|
140665
140966
|
pos -= toRead;
|
|
140666
|
-
(0,
|
|
140967
|
+
(0, import_fs17.readSync)(fd, buf, 0, toRead, pos);
|
|
140667
140968
|
const chunk = buf.subarray(0, toRead).toString("utf8");
|
|
140668
140969
|
const combined = chunk + partial2;
|
|
140669
140970
|
const parts = combined.split("\n");
|
|
@@ -140674,7 +140975,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140674
140975
|
}
|
|
140675
140976
|
if (partial2) lines.push(partial2);
|
|
140676
140977
|
} finally {
|
|
140677
|
-
(0,
|
|
140978
|
+
(0, import_fs17.closeSync)(fd);
|
|
140678
140979
|
}
|
|
140679
140980
|
const msgs = [];
|
|
140680
140981
|
for (let i = 0; i < lines.length && msgs.length < this.tailSize; i++) {
|
|
@@ -140887,7 +141188,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140887
141188
|
* `handleGetConversation` can still serve the cached tail even when the
|
|
140888
141189
|
* JSONL has been deleted.
|
|
140889
141190
|
*/
|
|
140890
|
-
pruneGhostFiles(exists =
|
|
141191
|
+
pruneGhostFiles(exists = import_fs17.existsSync) {
|
|
140891
141192
|
const rows = this.stmts.allFilePaths.all();
|
|
140892
141193
|
const ghosts = [];
|
|
140893
141194
|
const prune = this.db.transaction((ids) => {
|
|
@@ -140942,7 +141243,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140942
141243
|
* Returns the removed IDs.
|
|
140943
141244
|
*/
|
|
140944
141245
|
reconcileDeletions(livePaths, opts) {
|
|
140945
|
-
const exists = opts?.exists ??
|
|
141246
|
+
const exists = opts?.exists ?? import_fs17.existsSync;
|
|
140946
141247
|
const rows = this.stmts.allFilePaths.all();
|
|
140947
141248
|
const removed = [];
|
|
140948
141249
|
const drop = this.db.transaction((ids) => {
|
|
@@ -140977,7 +141278,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140977
141278
|
* reports drift for the CacheIntegrityMonitor to classify. `tailed` flags
|
|
140978
141279
|
* rows that still have cached history (which pruneGhostFiles would keep).
|
|
140979
141280
|
*/
|
|
140980
|
-
listMissingFiles(exists =
|
|
141281
|
+
listMissingFiles(exists = import_fs17.existsSync) {
|
|
140981
141282
|
const rows = this.stmts.allFilePathsWithTitle.all();
|
|
140982
141283
|
const missing = [];
|
|
140983
141284
|
for (const row of rows) {
|
|
@@ -141062,7 +141363,7 @@ function getDbConfig() {
|
|
|
141062
141363
|
}
|
|
141063
141364
|
|
|
141064
141365
|
// src/db/migrations.ts
|
|
141065
|
-
var
|
|
141366
|
+
var import_fs18 = require("fs");
|
|
141066
141367
|
var import_path18 = require("path");
|
|
141067
141368
|
var import_url7 = require("url");
|
|
141068
141369
|
var import_meta3 = {};
|
|
@@ -141085,10 +141386,10 @@ async function runMigrations2(pool2, migrationsDir) {
|
|
|
141085
141386
|
);
|
|
141086
141387
|
const appliedSet = new Set(applied.map((r) => r.name));
|
|
141087
141388
|
const dir = migrationsDir ?? (0, import_path18.join)(getMigrationsDir2(), "pg-migrations");
|
|
141088
|
-
const files = (0,
|
|
141389
|
+
const files = (0, import_fs18.readdirSync)(dir).filter((f2) => f2.endsWith(".sql")).sort();
|
|
141089
141390
|
for (const file2 of files) {
|
|
141090
141391
|
if (appliedSet.has(file2)) continue;
|
|
141091
|
-
const sql = (0,
|
|
141392
|
+
const sql = (0, import_fs18.readFileSync)((0, import_path18.join)(dir, file2), "utf-8");
|
|
141092
141393
|
await pool2.query(sql);
|
|
141093
141394
|
await pool2.query("INSERT INTO _migrations (name) VALUES ($1)", [file2]);
|
|
141094
141395
|
}
|
|
@@ -141440,8 +141741,8 @@ async function recordUpload(pool2, instanceId, row) {
|
|
|
141440
141741
|
init_feature_flags();
|
|
141441
141742
|
|
|
141442
141743
|
// src/handlers/handleListProjects.ts
|
|
141443
|
-
var
|
|
141444
|
-
var
|
|
141744
|
+
var import_fs19 = require("fs");
|
|
141745
|
+
var import_os8 = require("os");
|
|
141445
141746
|
var import_path19 = require("path");
|
|
141446
141747
|
function decodeProjectPath(dirName) {
|
|
141447
141748
|
return dirName.replace(/-/g, "/");
|
|
@@ -141449,14 +141750,14 @@ function decodeProjectPath(dirName) {
|
|
|
141449
141750
|
function handleListProjects(url2, res) {
|
|
141450
141751
|
const limit = Math.max(1, parseInt(url2.searchParams.get("limit") ?? "50", 10) || 50);
|
|
141451
141752
|
const offset = Math.max(0, parseInt(url2.searchParams.get("offset") ?? "0", 10) || 0);
|
|
141452
|
-
const projectsDir = (0, import_path19.join)((0,
|
|
141753
|
+
const projectsDir = (0, import_path19.join)((0, import_os8.homedir)(), ".claude", "projects");
|
|
141453
141754
|
let entries;
|
|
141454
141755
|
try {
|
|
141455
|
-
entries = (0,
|
|
141756
|
+
entries = (0, import_fs19.readdirSync)(projectsDir).map((dirName) => {
|
|
141456
141757
|
const fullPath = (0, import_path19.join)(projectsDir, dirName);
|
|
141457
141758
|
let mtime = 0;
|
|
141458
141759
|
try {
|
|
141459
|
-
mtime = (0,
|
|
141760
|
+
mtime = (0, import_fs19.statSync)(fullPath).mtimeMs;
|
|
141460
141761
|
} catch {
|
|
141461
141762
|
}
|
|
141462
141763
|
const path2 = decodeProjectPath(dirName);
|
|
@@ -141492,21 +141793,21 @@ var import_path23 = require("path");
|
|
|
141492
141793
|
// src/codex-pty-runner.ts
|
|
141493
141794
|
var import_headless = __toESM(require_xterm_headless(), 1);
|
|
141494
141795
|
var import_crypto8 = require("crypto");
|
|
141495
|
-
var
|
|
141796
|
+
var import_fs21 = require("fs");
|
|
141496
141797
|
var import_path21 = require("path");
|
|
141497
141798
|
init_logger();
|
|
141498
141799
|
|
|
141499
141800
|
// src/services/questions/codexGateAnswers.ts
|
|
141500
|
-
var
|
|
141501
|
-
var
|
|
141801
|
+
var import_fs20 = require("fs");
|
|
141802
|
+
var import_os9 = require("os");
|
|
141502
141803
|
var import_path20 = require("path");
|
|
141503
141804
|
function gateAnswersPath() {
|
|
141504
|
-
const dir = process.env.THREADBASE_CONFIG_DIR ?? (0, import_path20.join)((0,
|
|
141805
|
+
const dir = process.env.THREADBASE_CONFIG_DIR ?? (0, import_path20.join)((0, import_os9.homedir)(), ".threadbase");
|
|
141505
141806
|
return (0, import_path20.join)(dir, "gate-answers.json");
|
|
141506
141807
|
}
|
|
141507
141808
|
function loadGateAnswers() {
|
|
141508
141809
|
try {
|
|
141509
|
-
const parsed = JSON.parse((0,
|
|
141810
|
+
const parsed = JSON.parse((0, import_fs20.readFileSync)(gateAnswersPath(), "utf-8"));
|
|
141510
141811
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
141511
141812
|
} catch {
|
|
141512
141813
|
return {};
|
|
@@ -141514,8 +141815,8 @@ function loadGateAnswers() {
|
|
|
141514
141815
|
}
|
|
141515
141816
|
function saveGateAnswer(key, value) {
|
|
141516
141817
|
const path2 = gateAnswersPath();
|
|
141517
|
-
(0,
|
|
141518
|
-
(0,
|
|
141818
|
+
(0, import_fs20.mkdirSync)((0, import_path20.dirname)(path2), { recursive: true });
|
|
141819
|
+
(0, import_fs20.writeFileSync)(path2, `${JSON.stringify({ ...loadGateAnswers(), [key]: value }, null, 2)}
|
|
141519
141820
|
`);
|
|
141520
141821
|
}
|
|
141521
141822
|
function rememberedGateDigit(gate) {
|
|
@@ -142191,7 +142492,7 @@ var CodexPtyRunner = class {
|
|
|
142191
142492
|
session.statusUpdatedAt = /* @__PURE__ */ new Date();
|
|
142192
142493
|
const elapsedMs = session.completedAt.getTime() - session.startedAt.getTime();
|
|
142193
142494
|
if (exitCode !== 0 && elapsedMs < 2e3 && session.lastOutput === "") {
|
|
142194
|
-
if (!(0,
|
|
142495
|
+
if (!(0, import_fs21.existsSync)(session.projectPath)) {
|
|
142195
142496
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
142196
142497
|
} else {
|
|
142197
142498
|
session.failureReason = `Codex process exited immediately (code ${exitCode}).`;
|
|
@@ -142230,7 +142531,7 @@ function stripAnsi(str) {
|
|
|
142230
142531
|
// src/pty-manager.ts
|
|
142231
142532
|
var import_headless2 = __toESM(require_xterm_headless(), 1);
|
|
142232
142533
|
var import_crypto9 = require("crypto");
|
|
142233
|
-
var
|
|
142534
|
+
var import_fs22 = require("fs");
|
|
142234
142535
|
var import_path22 = require("path");
|
|
142235
142536
|
init_claude_flags();
|
|
142236
142537
|
init_logger();
|
|
@@ -143142,7 +143443,7 @@ var PTYManager = class {
|
|
|
143142
143443
|
session.statusUpdatedAt = /* @__PURE__ */ new Date();
|
|
143143
143444
|
const elapsedMs = session.completedAt.getTime() - session.startedAt.getTime();
|
|
143144
143445
|
if (exitCode !== 0 && elapsedMs < 2e3 && session.lastOutput === "") {
|
|
143145
|
-
if (!(0,
|
|
143446
|
+
if (!(0, import_fs22.existsSync)(session.projectPath)) {
|
|
143146
143447
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
143147
143448
|
} else {
|
|
143148
143449
|
session.failureReason = `Process exited immediately (code ${exitCode}). Check that the Claude binary is installed and accessible.`;
|
|
@@ -143351,10 +143652,10 @@ var PairTokenStore = class {
|
|
|
143351
143652
|
|
|
143352
143653
|
// src/process-discovery.ts
|
|
143353
143654
|
var import_child_process3 = require("child_process");
|
|
143354
|
-
var
|
|
143655
|
+
var import_os10 = require("os");
|
|
143355
143656
|
var import_path24 = require("path");
|
|
143356
143657
|
async function discoverClaudeProcesses() {
|
|
143357
|
-
if ((0,
|
|
143658
|
+
if ((0, import_os10.platform)() === "win32") return discoverWindows();
|
|
143358
143659
|
return discoverUnix();
|
|
143359
143660
|
}
|
|
143360
143661
|
var CLAUDE_CLI_SCRIPT = /claude-code[\\/](?:cli|index)\.(?:js|mjs|cjs)$/i;
|
|
@@ -143494,7 +143795,7 @@ async function getProcessArgsUnix(pid) {
|
|
|
143494
143795
|
async function getProcessArgs(pid) {
|
|
143495
143796
|
if (!Number.isInteger(pid) || pid < 1) return "";
|
|
143496
143797
|
try {
|
|
143497
|
-
if ((0,
|
|
143798
|
+
if ((0, import_os10.platform)() === "win32") {
|
|
143498
143799
|
const info = await getProcessInfoWindows(pid);
|
|
143499
143800
|
return info?.args ?? "";
|
|
143500
143801
|
}
|
|
@@ -143645,19 +143946,19 @@ function setCacheMetadata(repo, key, value) {
|
|
|
143645
143946
|
|
|
143646
143947
|
// src/services/cache-integrity/cacheIntegrityMonitor.ts
|
|
143647
143948
|
var import_crypto11 = require("crypto");
|
|
143648
|
-
var
|
|
143949
|
+
var import_fs25 = require("fs");
|
|
143649
143950
|
|
|
143650
143951
|
// src/services/cache-integrity/alertStore.ts
|
|
143651
|
-
var
|
|
143652
|
-
var
|
|
143952
|
+
var import_fs23 = require("fs");
|
|
143953
|
+
var import_os11 = require("os");
|
|
143653
143954
|
var import_path25 = require("path");
|
|
143654
143955
|
function alertStatePath() {
|
|
143655
|
-
const dir = process.env.THREADBASE_CONFIG_DIR ?? (0, import_path25.join)((0,
|
|
143956
|
+
const dir = process.env.THREADBASE_CONFIG_DIR ?? (0, import_path25.join)((0, import_os11.homedir)(), ".threadbase");
|
|
143656
143957
|
return (0, import_path25.join)(dir, "cache-alert.json");
|
|
143657
143958
|
}
|
|
143658
143959
|
function loadAlertState() {
|
|
143659
143960
|
try {
|
|
143660
|
-
const parsed = JSON.parse((0,
|
|
143961
|
+
const parsed = JSON.parse((0, import_fs23.readFileSync)(alertStatePath(), "utf-8"));
|
|
143661
143962
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
143662
143963
|
} catch {
|
|
143663
143964
|
return {};
|
|
@@ -143665,13 +143966,13 @@ function loadAlertState() {
|
|
|
143665
143966
|
}
|
|
143666
143967
|
function saveAlertState(state) {
|
|
143667
143968
|
const path2 = alertStatePath();
|
|
143668
|
-
(0,
|
|
143669
|
-
(0,
|
|
143969
|
+
(0, import_fs23.mkdirSync)((0, import_path25.dirname)(path2), { recursive: true });
|
|
143970
|
+
(0, import_fs23.writeFileSync)(path2, `${JSON.stringify(state, null, 2)}
|
|
143670
143971
|
`);
|
|
143671
143972
|
}
|
|
143672
143973
|
|
|
143673
143974
|
// src/services/cache-integrity/backup.ts
|
|
143674
|
-
var
|
|
143975
|
+
var import_fs24 = require("fs");
|
|
143675
143976
|
var import_path26 = require("path");
|
|
143676
143977
|
var DEFAULT_RETAIN = 3;
|
|
143677
143978
|
function retainCount() {
|
|
@@ -143684,16 +143985,16 @@ function timestamp(d) {
|
|
|
143684
143985
|
}
|
|
143685
143986
|
async function backupCacheDb(db, cacheDir) {
|
|
143686
143987
|
const backupsDir = (0, import_path26.join)(cacheDir, "backups");
|
|
143687
|
-
(0,
|
|
143988
|
+
(0, import_fs24.mkdirSync)(backupsDir, { recursive: true });
|
|
143688
143989
|
const destPath = (0, import_path26.join)(backupsDir, `cache-${timestamp(/* @__PURE__ */ new Date())}.db`);
|
|
143689
143990
|
await db.backup(destPath);
|
|
143690
143991
|
const retain = retainCount();
|
|
143691
|
-
const backups = (0,
|
|
143992
|
+
const backups = (0, import_fs24.readdirSync)(backupsDir).filter((f2) => f2.startsWith("cache-") && f2.endsWith(".db")).map((f2) => {
|
|
143692
143993
|
const full = (0, import_path26.join)(backupsDir, f2);
|
|
143693
|
-
return { full, mtime: (0,
|
|
143994
|
+
return { full, mtime: (0, import_fs24.statSync)(full).mtimeMs };
|
|
143694
143995
|
}).sort((a, b2) => b2.mtime - a.mtime);
|
|
143695
143996
|
for (const stale of backups.slice(retain)) {
|
|
143696
|
-
if ((0,
|
|
143997
|
+
if ((0, import_fs24.existsSync)(stale.full)) (0, import_fs24.unlinkSync)(stale.full);
|
|
143697
143998
|
}
|
|
143698
143999
|
return destPath;
|
|
143699
144000
|
}
|
|
@@ -143790,7 +144091,7 @@ var CacheIntegrityMonitor = class {
|
|
|
143790
144091
|
* the pending record, back up on high severity, and broadcast the alert.
|
|
143791
144092
|
*/
|
|
143792
144093
|
async runDetection(detectedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
143793
|
-
const all = this.cache.listMissingFiles(
|
|
144094
|
+
const all = this.cache.listMissingFiles(import_fs25.existsSync);
|
|
143794
144095
|
const missing = all.filter((m2) => !this.ignoredIds.has(m2.id));
|
|
143795
144096
|
if (missing.length === 0) {
|
|
143796
144097
|
if (this._pending) {
|
|
@@ -143894,7 +144195,7 @@ var CacheIntegrityMonitor = class {
|
|
|
143894
144195
|
case "prune_all": {
|
|
143895
144196
|
await this.ensureBackup(pending);
|
|
143896
144197
|
const backupPath = pending.backupPath;
|
|
143897
|
-
const stillMissing = pending.missing.filter((m2) => !(0,
|
|
144198
|
+
const stillMissing = pending.missing.filter((m2) => !(0, import_fs25.existsSync)(m2.filePath)).map((m2) => m2.id);
|
|
143898
144199
|
const pruned = this.cache.dropRowsById(stillMissing);
|
|
143899
144200
|
this.applyDeferredUnlinks();
|
|
143900
144201
|
this.clearPending();
|
|
@@ -143955,7 +144256,7 @@ var CacheIntegrityMonitor = class {
|
|
|
143955
144256
|
};
|
|
143956
144257
|
|
|
143957
144258
|
// src/services/conversations/conversationWatcher.ts
|
|
143958
|
-
var
|
|
144259
|
+
var import_fs26 = require("fs");
|
|
143959
144260
|
var import_promises14 = require("fs/promises");
|
|
143960
144261
|
var ConversationWatcher = class {
|
|
143961
144262
|
files = /* @__PURE__ */ new Map();
|
|
@@ -143981,7 +144282,7 @@ var ConversationWatcher = class {
|
|
|
143981
144282
|
if (this.files.has(key)) return;
|
|
143982
144283
|
let offset;
|
|
143983
144284
|
try {
|
|
143984
|
-
offset = (0,
|
|
144285
|
+
offset = (0, import_fs26.statSync)(filePath).size;
|
|
143985
144286
|
} catch {
|
|
143986
144287
|
offset = 0;
|
|
143987
144288
|
}
|
|
@@ -144163,14 +144464,14 @@ function findSearchTarget(messages, query) {
|
|
|
144163
144464
|
}
|
|
144164
144465
|
|
|
144165
144466
|
// src/services/conversations/pruneAgentConversations.ts
|
|
144166
|
-
var
|
|
144467
|
+
var import_fs27 = require("fs");
|
|
144167
144468
|
function pruneAgentConversations(cache) {
|
|
144168
144469
|
const db = cache.getDatabase();
|
|
144169
144470
|
const rows = db.prepare("SELECT id, file_path FROM conversation_meta").all();
|
|
144170
144471
|
let pruned = 0;
|
|
144171
144472
|
let missing = 0;
|
|
144172
144473
|
for (const row of rows) {
|
|
144173
|
-
if (!(0,
|
|
144474
|
+
if (!(0, import_fs27.existsSync)(row.file_path)) {
|
|
144174
144475
|
missing += 1;
|
|
144175
144476
|
continue;
|
|
144176
144477
|
}
|
|
@@ -144491,22 +144792,22 @@ function refreshConversationCache(deps) {
|
|
|
144491
144792
|
}
|
|
144492
144793
|
|
|
144493
144794
|
// src/services/conversations/shouldRefreshProjectsFromHdd.ts
|
|
144494
|
-
var
|
|
144495
|
-
var
|
|
144795
|
+
var import_fs28 = require("fs");
|
|
144796
|
+
var import_os12 = require("os");
|
|
144496
144797
|
var import_path27 = require("path");
|
|
144497
|
-
var DEFAULT_PROJECTS_DIR = (0, import_path27.join)((0,
|
|
144798
|
+
var DEFAULT_PROJECTS_DIR = (0, import_path27.join)((0, import_os12.homedir)(), ".claude", "projects");
|
|
144498
144799
|
function maxProjectsTreeMtimeMs(projectsDir) {
|
|
144499
144800
|
let maxMs;
|
|
144500
144801
|
try {
|
|
144501
|
-
maxMs = (0,
|
|
144802
|
+
maxMs = (0, import_fs28.statSync)(projectsDir).mtimeMs;
|
|
144502
144803
|
} catch {
|
|
144503
144804
|
return null;
|
|
144504
144805
|
}
|
|
144505
144806
|
try {
|
|
144506
|
-
for (const ent of (0,
|
|
144807
|
+
for (const ent of (0, import_fs28.readdirSync)(projectsDir, { withFileTypes: true })) {
|
|
144507
144808
|
if (!ent.isDirectory()) continue;
|
|
144508
144809
|
try {
|
|
144509
|
-
const childMs = (0,
|
|
144810
|
+
const childMs = (0, import_fs28.statSync)((0, import_path27.join)(projectsDir, ent.name)).mtimeMs;
|
|
144510
144811
|
if (childMs > maxMs) maxMs = childMs;
|
|
144511
144812
|
} catch {
|
|
144512
144813
|
}
|
|
@@ -145275,8 +145576,90 @@ function resolveAnswer(pending, body) {
|
|
|
145275
145576
|
}
|
|
145276
145577
|
}
|
|
145277
145578
|
|
|
145579
|
+
// src/services/search/searchQuery.ts
|
|
145580
|
+
var DEFAULT_SEARCH_LIMIT = 50;
|
|
145581
|
+
var MAX_SEARCH_LIMIT = 200;
|
|
145582
|
+
var MAX_QUERY_LENGTH = 256;
|
|
145583
|
+
var SearchQueryError = class extends Error {
|
|
145584
|
+
constructor(message, code) {
|
|
145585
|
+
super(message);
|
|
145586
|
+
this.code = code;
|
|
145587
|
+
}
|
|
145588
|
+
code;
|
|
145589
|
+
};
|
|
145590
|
+
function intOr(raw2, fallback) {
|
|
145591
|
+
if (raw2 === null) return fallback;
|
|
145592
|
+
const n = Number.parseInt(raw2, 10);
|
|
145593
|
+
return Number.isFinite(n) ? n : fallback;
|
|
145594
|
+
}
|
|
145595
|
+
function parseSearchQuery(params) {
|
|
145596
|
+
const q2 = (params.get("q") ?? "").trim();
|
|
145597
|
+
if (!q2) {
|
|
145598
|
+
throw new SearchQueryError("Missing query parameter: q", "invalid_query");
|
|
145599
|
+
}
|
|
145600
|
+
if (q2.length > MAX_QUERY_LENGTH) {
|
|
145601
|
+
throw new SearchQueryError(`Query exceeds ${MAX_QUERY_LENGTH} characters`, "query_too_long");
|
|
145602
|
+
}
|
|
145603
|
+
const limit = Math.min(
|
|
145604
|
+
Math.max(intOr(params.get("limit"), DEFAULT_SEARCH_LIMIT), 1),
|
|
145605
|
+
MAX_SEARCH_LIMIT
|
|
145606
|
+
);
|
|
145607
|
+
const offset = Math.max(intOr(params.get("offset"), 0), 0);
|
|
145608
|
+
const filters = {};
|
|
145609
|
+
const provider = params.get("provider");
|
|
145610
|
+
if (provider !== null) {
|
|
145611
|
+
if (!isProviderName(provider)) {
|
|
145612
|
+
throw new SearchQueryError(`Unknown provider: ${provider}`, "invalid_filter");
|
|
145613
|
+
}
|
|
145614
|
+
filters.provider = provider;
|
|
145615
|
+
}
|
|
145616
|
+
const projectPath = params.get("projectPath");
|
|
145617
|
+
if (projectPath) filters.projectPath = projectPath;
|
|
145618
|
+
const branch = params.get("branch");
|
|
145619
|
+
if (branch) filters.branch = branch;
|
|
145620
|
+
for (const [key, field] of [
|
|
145621
|
+
["since", "since"],
|
|
145622
|
+
["until", "until"]
|
|
145623
|
+
]) {
|
|
145624
|
+
const raw2 = params.get(key);
|
|
145625
|
+
if (raw2 === null) continue;
|
|
145626
|
+
const ms2 = Date.parse(raw2);
|
|
145627
|
+
if (Number.isNaN(ms2)) {
|
|
145628
|
+
throw new SearchQueryError(`Invalid ${key}: expected an ISO 8601 date`, "invalid_filter");
|
|
145629
|
+
}
|
|
145630
|
+
filters[field] = ms2;
|
|
145631
|
+
}
|
|
145632
|
+
if (filters.since != null && filters.until != null && filters.since > filters.until) {
|
|
145633
|
+
throw new SearchQueryError("`since` must not be after `until`", "invalid_filter");
|
|
145634
|
+
}
|
|
145635
|
+
return { q: q2, limit, offset, filters };
|
|
145636
|
+
}
|
|
145637
|
+
function applyFilters(results, filters) {
|
|
145638
|
+
return results.filter((r) => {
|
|
145639
|
+
if (filters.provider && r.provider !== filters.provider) return false;
|
|
145640
|
+
if (filters.projectPath && r.projectPath !== filters.projectPath) return false;
|
|
145641
|
+
if (filters.branch && r.branch !== filters.branch) return false;
|
|
145642
|
+
if (filters.since != null || filters.until != null) {
|
|
145643
|
+
const ts2 = r.lastActivity == null ? Number.NaN : new Date(r.lastActivity).getTime();
|
|
145644
|
+
if (Number.isNaN(ts2)) return false;
|
|
145645
|
+
if (filters.since != null && ts2 < filters.since) return false;
|
|
145646
|
+
if (filters.until != null && ts2 > filters.until) return false;
|
|
145647
|
+
}
|
|
145648
|
+
return true;
|
|
145649
|
+
});
|
|
145650
|
+
}
|
|
145651
|
+
function paginate(results, offset, limit) {
|
|
145652
|
+
const items = results.slice(offset, offset + limit);
|
|
145653
|
+
return {
|
|
145654
|
+
items,
|
|
145655
|
+
total: results.length,
|
|
145656
|
+
offset,
|
|
145657
|
+
hasMore: offset + items.length < results.length
|
|
145658
|
+
};
|
|
145659
|
+
}
|
|
145660
|
+
|
|
145278
145661
|
// src/services/sessions/conversationBusy.ts
|
|
145279
|
-
var
|
|
145662
|
+
var import_fs29 = require("fs");
|
|
145280
145663
|
var RESUME_BUSY_WINDOW_MS = 12e4;
|
|
145281
145664
|
function resolveResumeBusyWindowMs(env = process.env) {
|
|
145282
145665
|
const raw2 = env.THREADBASE_RESUME_BUSY_WINDOW_MS;
|
|
@@ -145293,7 +145676,7 @@ function conversationBusy(input) {
|
|
|
145293
145676
|
let lastActivityMs = null;
|
|
145294
145677
|
if (input.jsonlPath) {
|
|
145295
145678
|
try {
|
|
145296
|
-
const mtimeMs = (0,
|
|
145679
|
+
const mtimeMs = (0, import_fs29.statSync)(input.jsonlPath).mtimeMs;
|
|
145297
145680
|
const age = now - mtimeMs;
|
|
145298
145681
|
lastActivityMs = Math.max(0, age);
|
|
145299
145682
|
const isSelfEcho = input.selfPtyEndedAt != null && mtimeMs <= input.selfPtyEndedAt + SELF_ACTIVITY_SKEW_MS;
|
|
@@ -146003,6 +146386,8 @@ var DEFAULT_PTY_GRACE_PERIOD_MS = 27e4;
|
|
|
146003
146386
|
var GRACE_MAX_DEFERS = 4;
|
|
146004
146387
|
var IDLE_REAP_AFTER_MS = 6 * 60 * 60 * 1e3;
|
|
146005
146388
|
var IDLE_REAP_SWEEP_MS = 5 * 60 * 1e3;
|
|
146389
|
+
var SEARCH_OVERFETCH = 4;
|
|
146390
|
+
var SEARCH_MAX_SCAN = 1e3;
|
|
146006
146391
|
var RESUME_DISCOVERY_TIMEOUT_MS = 750;
|
|
146007
146392
|
var DISCOVERY_TTL_MS = 15e3;
|
|
146008
146393
|
var ADOPT_KILL_TIMEOUT_MS = 5e3;
|
|
@@ -146080,6 +146465,20 @@ var StreamerServer = class {
|
|
|
146080
146465
|
// Set by onConversationChanged while a scan is in-flight; getScanner() does
|
|
146081
146466
|
// a single rescan after the current one completes instead of restarting it.
|
|
146082
146467
|
scannerStale = false;
|
|
146468
|
+
// WHICH files scannerStale is about. A directory event names exactly one
|
|
146469
|
+
// JSONL, and the only correct response is refreshFile() on that one file —
|
|
146470
|
+
// but scannerStale alone carries no identity, so honoring it used to mean a
|
|
146471
|
+
// full-tree rescan. On the non-persistent scanner this server actually runs
|
|
146472
|
+
// (buildStatCache => persistent:false, see listen()), scan() opens by
|
|
146473
|
+
// clearing metadataCache AND conversationLRU — so one live session appending
|
|
146474
|
+
// to its own transcript threw away every OTHER conversation's parsed
|
|
146475
|
+
// snapshot, and the next full fetch of an unrelated conversation re-parsed
|
|
146476
|
+
// it from disk (745-2877ms on a 5MB/1112-message history) while the
|
|
146477
|
+
// per-file paginated path stayed at ~20ms throughout. Populated alongside
|
|
146478
|
+
// scannerStale and drained with it by takeStaleFiles(); an armed flag with
|
|
146479
|
+
// an EMPTY set means "stale, source unknown" and still falls back to the
|
|
146480
|
+
// full rescan.
|
|
146481
|
+
staleFiles = /* @__PURE__ */ new Set();
|
|
146083
146482
|
// Single-flight guard for the background disk reconcile: a burst of list
|
|
146084
146483
|
// polls during active session writes shares one rescan instead of queueing
|
|
146085
146484
|
// a full rescan per request.
|
|
@@ -146229,7 +146628,7 @@ var StreamerServer = class {
|
|
|
146229
146628
|
this.skipStartupWarmup = config2.skipStartupWarmup ?? false;
|
|
146230
146629
|
this.scannerPersistenceDisabled = config2.scannerPersistent === false;
|
|
146231
146630
|
this.scanProfiles = config2.scanProfiles;
|
|
146232
|
-
this.codexRoots = config2.codexRoots ?? [(0, import_path29.join)((0,
|
|
146631
|
+
this.codexRoots = config2.codexRoots ?? [(0, import_path29.join)((0, import_os13.homedir)(), ".codex", "sessions")];
|
|
146233
146632
|
this.ptyGracePeriodMs = config2.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
|
|
146234
146633
|
this.defaultSystemPrompt = config2.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
146235
146634
|
this.featureFlags = resolveFeatureFlags({ cli: config2.featureFlags, yaml: loadFeatureFlags() });
|
|
@@ -146243,12 +146642,15 @@ var StreamerServer = class {
|
|
|
146243
146642
|
this.claudeFlagsPersistable = config2.claudeFlags === void 0;
|
|
146244
146643
|
this.claudeFlags = config2.claudeFlags ?? loadClaudeFlags();
|
|
146245
146644
|
this.claudeExtraArgs = config2.claudeExtraArgs ?? loadClaudeExtraArgs();
|
|
146246
|
-
this.cacheDir = config2.cacheDir ?? loadCacheDir() ?? (0, import_path29.join)((0,
|
|
146645
|
+
this.cacheDir = config2.cacheDir ?? loadCacheDir() ?? (0, import_path29.join)((0, import_os13.homedir)(), ".threadbase", "cache");
|
|
146247
146646
|
this.tailSize = config2.tailSize ?? loadTailSize() ?? 10;
|
|
146248
146647
|
this.directoryDebounceMs = parseDirScanDebounceEnv(process.env.THREADBASE_DIR_SCAN_DEBOUNCE_MS) ?? config2.directoryScanDebounceMs ?? 1e3;
|
|
146249
146648
|
this.markScannerStaleDebounced = debounce(() => {
|
|
146250
146649
|
if (this.scannerReady) this.scannerStale = true;
|
|
146251
|
-
else
|
|
146650
|
+
else {
|
|
146651
|
+
this.scanner = null;
|
|
146652
|
+
this.staleFiles.clear();
|
|
146653
|
+
}
|
|
146252
146654
|
}, this.directoryDebounceMs);
|
|
146253
146655
|
this.includeAgents = parseIncludeAgentsEnv(process.env.THREADBASE_INCLUDE_AGENTS);
|
|
146254
146656
|
this.agentEntrypoints = parseAgentEntrypointsEnv(process.env.THREADBASE_AGENT_ENTRYPOINTS);
|
|
@@ -146291,7 +146693,7 @@ var StreamerServer = class {
|
|
|
146291
146693
|
const seqs = cache.extendMessageIndex(
|
|
146292
146694
|
filePath,
|
|
146293
146695
|
spans,
|
|
146294
|
-
(0,
|
|
146696
|
+
(0, import_fs30.statSync)(filePath),
|
|
146295
146697
|
readFrom,
|
|
146296
146698
|
endOffset
|
|
146297
146699
|
);
|
|
@@ -146341,6 +146743,7 @@ var StreamerServer = class {
|
|
|
146341
146743
|
if (!tailed) this.maybeAttachExternalTail(filePath);
|
|
146342
146744
|
this.sweepIdleExternalTails();
|
|
146343
146745
|
this.cache?.invalidateByFilePath(filePath, { skipIfTailed: true });
|
|
146746
|
+
this.staleFiles.add(filePath);
|
|
146344
146747
|
this.markScannerStaleDebounced();
|
|
146345
146748
|
this.log.debug?.(`Scanner invalidated by directory event: ${filePath}`, {
|
|
146346
146749
|
filePath,
|
|
@@ -146753,14 +147156,14 @@ var StreamerServer = class {
|
|
|
146753
147156
|
}
|
|
146754
147157
|
this.apnsClient = new ApnsClient(creds);
|
|
146755
147158
|
const sender = new LiveActivitySender(this.apnsClient, pushRepo);
|
|
146756
|
-
const serverId = process.env.THREADBASE_INSTANCE_ID ?? (0,
|
|
146757
|
-
this.liveActivityNotifier = new LiveActivityNotifier(sender, serverId, (0,
|
|
147159
|
+
const serverId = process.env.THREADBASE_INSTANCE_ID ?? (0, import_os13.hostname)();
|
|
147160
|
+
this.liveActivityNotifier = new LiveActivityNotifier(sender, serverId, (0, import_os13.hostname)());
|
|
146758
147161
|
this.liveActivityRenewal = new LiveActivityRenewalScheduler({
|
|
146759
147162
|
repo: pushRepo,
|
|
146760
147163
|
sender,
|
|
146761
147164
|
sessionStore: this.sessionStore,
|
|
146762
147165
|
serverId,
|
|
146763
|
-
serverLabel: (0,
|
|
147166
|
+
serverLabel: (0, import_os13.hostname)()
|
|
146764
147167
|
});
|
|
146765
147168
|
this.liveActivityRenewal.start();
|
|
146766
147169
|
this.log.info("Live Activity push enabled", {
|
|
@@ -147099,7 +147502,7 @@ var StreamerServer = class {
|
|
|
147099
147502
|
this.fileWatcher.watchDirectory(dir);
|
|
147100
147503
|
}
|
|
147101
147504
|
for (const dir of this.codexRoots) {
|
|
147102
|
-
if (!(0,
|
|
147505
|
+
if (!(0, import_fs30.existsSync)(dir)) continue;
|
|
147103
147506
|
this.fileWatcher.watchDirectory(dir);
|
|
147104
147507
|
}
|
|
147105
147508
|
} catch (err) {
|
|
@@ -147372,7 +147775,7 @@ var StreamerServer = class {
|
|
|
147372
147775
|
}
|
|
147373
147776
|
let body;
|
|
147374
147777
|
try {
|
|
147375
|
-
body = await
|
|
147778
|
+
body = await readBody2(req);
|
|
147376
147779
|
} catch (err) {
|
|
147377
147780
|
const message = err instanceof Error ? err.message : "Invalid body";
|
|
147378
147781
|
json2(res, 400, { error: message });
|
|
@@ -147418,7 +147821,7 @@ var StreamerServer = class {
|
|
|
147418
147821
|
nonce: sealed.nonce,
|
|
147419
147822
|
ephemeralPublicKey: sealed.ephemeralPublicKey,
|
|
147420
147823
|
publicUrl: this.publicUrl,
|
|
147421
|
-
machineName: (0,
|
|
147824
|
+
machineName: (0, import_os13.hostname)(),
|
|
147422
147825
|
...device && {
|
|
147423
147826
|
deviceId: device.deviceId,
|
|
147424
147827
|
deviceToken: device.deviceToken,
|
|
@@ -147546,7 +147949,7 @@ var StreamerServer = class {
|
|
|
147546
147949
|
if (this.scanProfiles && this.scanProfiles.length > 0) {
|
|
147547
147950
|
return this.scanProfiles.filter((p2) => p2.enabled).map((p2) => (0, import_path29.join)(p2.configDir, "projects"));
|
|
147548
147951
|
}
|
|
147549
|
-
return [(0, import_path29.join)((0,
|
|
147952
|
+
return [(0, import_path29.join)((0, import_os13.homedir)(), ".claude", "projects")];
|
|
147550
147953
|
}
|
|
147551
147954
|
/**
|
|
147552
147955
|
* Full-glob scan + cache upsert/delete reconcile. Used by ?refresh=1 and by
|
|
@@ -147587,21 +147990,46 @@ var StreamerServer = class {
|
|
|
147587
147990
|
// so a burst of list polls during active session writes shares one rescan
|
|
147588
147991
|
// rather than queueing a full rescan each; tracked so close() awaits the
|
|
147589
147992
|
// in-flight cache write before shutting the DB.
|
|
147590
|
-
startBackgroundConversationReconcile() {
|
|
147993
|
+
startBackgroundConversationReconcile(mode = "full") {
|
|
147591
147994
|
if (this.conversationReconcileInFlight) return;
|
|
147592
|
-
const
|
|
147995
|
+
const paths = mode === "files" ? this.takeStaleFiles() : [];
|
|
147996
|
+
const task = (paths.length > 0 ? this.reconcileStaleFilesFromDisk(paths) : this.reconcileConversationsCacheFromDisk()).finally(() => {
|
|
147593
147997
|
this.conversationReconcileInFlight = null;
|
|
147594
147998
|
});
|
|
147595
147999
|
this.conversationReconcileInFlight = task;
|
|
147596
148000
|
this.trackCacheWrite(task);
|
|
147597
148001
|
}
|
|
147598
|
-
|
|
147599
|
-
|
|
147600
|
-
|
|
147601
|
-
|
|
148002
|
+
// "files": a directory event named specific JSONLs, so refresh only those.
|
|
148003
|
+
// "full": disk drifted in ways a per-file refresh can't see (a project dir
|
|
148004
|
+
// appeared, rows vanished), so walk the tree. Order matters — the staleness
|
|
148005
|
+
// check short-circuits first so the HDD freshness probe stays off the hot
|
|
148006
|
+
// poll path, exactly as it did when this returned a boolean.
|
|
148007
|
+
conversationReconcileMode() {
|
|
148008
|
+
if (!this.cache) return null;
|
|
148009
|
+
if (this.scannerStale) return "files";
|
|
148010
|
+
if (!this.conversationsRepo || !this.cacheMetadataRepo) return null;
|
|
147602
148011
|
return shouldRefreshProjectsFromHdd(this.conversationsRepo, this.cacheMetadataRepo, {
|
|
147603
148012
|
projectsDirs: this.projectsDirsForFreshnessCheck()
|
|
147604
|
-
});
|
|
148013
|
+
}) ? "full" : null;
|
|
148014
|
+
}
|
|
148015
|
+
// The per-file half of reconcileConversationsCacheFromDisk: re-index just the
|
|
148016
|
+
// changed JSONLs and upsert their rows. No reconcileDeletions here — that
|
|
148017
|
+
// needs the whole live-path set, and deletions already have their own path
|
|
148018
|
+
// (onFileDeleted -> invalidateByFilePath). New projects still arrive via the
|
|
148019
|
+
// HDD-freshness "full" mode.
|
|
148020
|
+
async reconcileStaleFilesFromDisk(paths) {
|
|
148021
|
+
if (!this.cache) return;
|
|
148022
|
+
const scanner = await this.getScanner(true);
|
|
148023
|
+
const metas = await this.refreshStaleFiles(scanner, paths);
|
|
148024
|
+
if (metas.length === 0) return;
|
|
148025
|
+
try {
|
|
148026
|
+
this.cache.upsertFromScannerMeta(metas);
|
|
148027
|
+
} catch (err) {
|
|
148028
|
+
this.log.warn(
|
|
148029
|
+
`stale-file reconcile failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
148030
|
+
{ event: "conversations.reconcile_failed" }
|
|
148031
|
+
);
|
|
148032
|
+
}
|
|
147605
148033
|
}
|
|
147606
148034
|
async handleListConversations(url2, res) {
|
|
147607
148035
|
if (this.rejectIfWarmingUp(res)) return;
|
|
@@ -147611,10 +148039,11 @@ var StreamerServer = class {
|
|
|
147611
148039
|
const project = url2.searchParams.get("project") ?? void 0;
|
|
147612
148040
|
const providerFilter = url2.searchParams.get("provider") ?? void 0;
|
|
147613
148041
|
const bustCache = url2.searchParams.get("refresh") === "1";
|
|
147614
|
-
|
|
148042
|
+
const reconcileMode = this.conversationReconcileMode();
|
|
148043
|
+
if (this.cache && (bustCache || reconcileMode)) {
|
|
147615
148044
|
const canServeStale = !bustCache && this.cache.listConversations({ limit: 0, offset: 0 }).total > 0;
|
|
147616
148045
|
if (canServeStale) {
|
|
147617
|
-
this.startBackgroundConversationReconcile();
|
|
148046
|
+
this.startBackgroundConversationReconcile(reconcileMode ?? "full");
|
|
147618
148047
|
} else {
|
|
147619
148048
|
const shouldEmitProgress = createScanProgressThrottle();
|
|
147620
148049
|
await this.withWarmup(
|
|
@@ -147814,21 +148243,54 @@ var StreamerServer = class {
|
|
|
147814
148243
|
options ?? (this.scannerPersistenceDisabled ? { persistent: false } : void 0)
|
|
147815
148244
|
);
|
|
147816
148245
|
}
|
|
148246
|
+
// Drain the stale set and disarm the flag together. The caller owns the
|
|
148247
|
+
// returned paths: clearing before the refresh means events that land DURING
|
|
148248
|
+
// it re-arm the flag and get their own pass instead of being swallowed.
|
|
148249
|
+
takeStaleFiles() {
|
|
148250
|
+
const paths = [...this.staleFiles];
|
|
148251
|
+
this.staleFiles.clear();
|
|
148252
|
+
this.scannerStale = false;
|
|
148253
|
+
return paths;
|
|
148254
|
+
}
|
|
148255
|
+
// Reconcile exactly the JSONLs a directory event named. Failures are logged
|
|
148256
|
+
// and swallowed per file: one unreadable transcript must not abort the
|
|
148257
|
+
// others, and the file simply stays on its previous snapshot until the next
|
|
148258
|
+
// event — the same outcome the full rescan gave on a parse failure.
|
|
148259
|
+
async refreshStaleFiles(scanner, paths) {
|
|
148260
|
+
const metas = await Promise.all(
|
|
148261
|
+
paths.map(
|
|
148262
|
+
(filePath) => scanner.refreshFile(filePath).catch((err) => {
|
|
148263
|
+
this.log.warn("scanner.refreshFile: failed", {
|
|
148264
|
+
event: "scanner.refresh_failed",
|
|
148265
|
+
filePath,
|
|
148266
|
+
trigger: "directory-event",
|
|
148267
|
+
err
|
|
148268
|
+
});
|
|
148269
|
+
return null;
|
|
148270
|
+
})
|
|
148271
|
+
)
|
|
148272
|
+
);
|
|
148273
|
+
return metas.filter((m2) => m2 !== null);
|
|
148274
|
+
}
|
|
147817
148275
|
async getScanner(skipStaleRescan = false) {
|
|
147818
148276
|
if (this.scannerReady) {
|
|
147819
148277
|
await this.scannerReady;
|
|
147820
148278
|
if (this.scanner) {
|
|
147821
148279
|
if (skipStaleRescan) return this.scanner;
|
|
147822
148280
|
if (this.scannerStale) {
|
|
147823
|
-
|
|
147824
|
-
|
|
147825
|
-
|
|
147826
|
-
|
|
148281
|
+
const paths = this.takeStaleFiles();
|
|
148282
|
+
if (paths.length === 0) {
|
|
148283
|
+
this.scanner = null;
|
|
148284
|
+
this.scannerReady = null;
|
|
148285
|
+
return this.getScanner();
|
|
148286
|
+
}
|
|
148287
|
+
await this.refreshStaleFiles(this.scanner, paths);
|
|
148288
|
+
return this.scanner ?? this.getScanner();
|
|
147827
148289
|
}
|
|
147828
148290
|
return this.scanner;
|
|
147829
148291
|
}
|
|
147830
148292
|
}
|
|
147831
|
-
this.
|
|
148293
|
+
this.takeStaleFiles();
|
|
147832
148294
|
const statCache = this.buildStatCache(this.scanner);
|
|
147833
148295
|
this.scanner = this.newScanner(statCache ? { persistent: false } : void 0);
|
|
147834
148296
|
this.allScanners.add(this.scanner);
|
|
@@ -147862,7 +148324,7 @@ var StreamerServer = class {
|
|
|
147862
148324
|
// getScanner() anti-infinite-loop guard is preserved.
|
|
147863
148325
|
async rescanForRefresh(onProgress) {
|
|
147864
148326
|
if (this.scannerReady) await this.scannerReady;
|
|
147865
|
-
this.
|
|
148327
|
+
this.takeStaleFiles();
|
|
147866
148328
|
if (!this.scanner) {
|
|
147867
148329
|
this.scanner = new ConversationScanner();
|
|
147868
148330
|
this.allScanners.add(this.scanner);
|
|
@@ -147889,20 +148351,20 @@ var StreamerServer = class {
|
|
|
147889
148351
|
if (this.scanProfiles && this.scanProfiles.length > 0) {
|
|
147890
148352
|
return this.scanProfiles.filter((p2) => p2.enabled).map((p2) => (0, import_path29.join)(p2.configDir, "projects"));
|
|
147891
148353
|
}
|
|
147892
|
-
return [(0, import_path29.join)((0,
|
|
148354
|
+
return [(0, import_path29.join)((0, import_os13.homedir)(), ".claude", "projects")];
|
|
147893
148355
|
}
|
|
147894
148356
|
findJsonlPath(uuid3) {
|
|
147895
148357
|
const filename = `${uuid3}.jsonl`;
|
|
147896
148358
|
for (const projectsDir of this.projectsDirs()) {
|
|
147897
|
-
if (!(0,
|
|
147898
|
-
for (const dir of (0,
|
|
148359
|
+
if (!(0, import_fs30.existsSync)(projectsDir)) continue;
|
|
148360
|
+
for (const dir of (0, import_fs30.readdirSync)(projectsDir)) {
|
|
147899
148361
|
const fp = (0, import_path29.join)(projectsDir, dir, filename);
|
|
147900
|
-
if ((0,
|
|
148362
|
+
if ((0, import_fs30.existsSync)(fp)) return fp;
|
|
147901
148363
|
const projectDir = (0, import_path29.join)(projectsDir, dir);
|
|
147902
148364
|
try {
|
|
147903
|
-
for (const sub of (0,
|
|
148365
|
+
for (const sub of (0, import_fs30.readdirSync)(projectDir)) {
|
|
147904
148366
|
const subagentPath = (0, import_path29.join)(projectDir, sub, "subagents", filename);
|
|
147905
|
-
if ((0,
|
|
148367
|
+
if ((0, import_fs30.existsSync)(subagentPath)) return subagentPath;
|
|
147906
148368
|
}
|
|
147907
148369
|
} catch {
|
|
147908
148370
|
}
|
|
@@ -147912,7 +148374,7 @@ var StreamerServer = class {
|
|
|
147912
148374
|
}
|
|
147913
148375
|
async readCwdFromJsonl(filePath) {
|
|
147914
148376
|
return new Promise((resolve4) => {
|
|
147915
|
-
const rl = (0, import_readline4.createInterface)({ input: (0,
|
|
148377
|
+
const rl = (0, import_readline4.createInterface)({ input: (0, import_fs30.createReadStream)(filePath), crlfDelay: Infinity });
|
|
147916
148378
|
let found = false;
|
|
147917
148379
|
rl.on("line", (line) => {
|
|
147918
148380
|
if (found) return;
|
|
@@ -148002,7 +148464,7 @@ var StreamerServer = class {
|
|
|
148002
148464
|
if (this.isManagedTailPath(key)) return;
|
|
148003
148465
|
let mtimeMs;
|
|
148004
148466
|
try {
|
|
148005
|
-
mtimeMs = (0,
|
|
148467
|
+
mtimeMs = (0, import_fs30.statSync)(filePath).mtimeMs;
|
|
148006
148468
|
} catch {
|
|
148007
148469
|
return;
|
|
148008
148470
|
}
|
|
@@ -148184,7 +148646,7 @@ var StreamerServer = class {
|
|
|
148184
148646
|
if (!conv.filePath) return false;
|
|
148185
148647
|
let mtimeMs = null;
|
|
148186
148648
|
try {
|
|
148187
|
-
mtimeMs = (0,
|
|
148649
|
+
mtimeMs = (0, import_fs30.statSync)(conv.filePath).mtimeMs;
|
|
148188
148650
|
} catch {
|
|
148189
148651
|
return false;
|
|
148190
148652
|
}
|
|
@@ -148439,7 +148901,7 @@ var StreamerServer = class {
|
|
|
148439
148901
|
}
|
|
148440
148902
|
let body;
|
|
148441
148903
|
try {
|
|
148442
|
-
body = await
|
|
148904
|
+
body = await readBody2(req);
|
|
148443
148905
|
} catch {
|
|
148444
148906
|
res.setHeader("Accept-Query", "application/json");
|
|
148445
148907
|
json2(res, 422, { error: "Malformed JSON body", code: "invalid_query" });
|
|
@@ -148477,17 +148939,27 @@ var StreamerServer = class {
|
|
|
148477
148939
|
});
|
|
148478
148940
|
}
|
|
148479
148941
|
async handleSearch(url2, res) {
|
|
148480
|
-
|
|
148481
|
-
|
|
148482
|
-
|
|
148483
|
-
|
|
148942
|
+
let parsed;
|
|
148943
|
+
try {
|
|
148944
|
+
parsed = parseSearchQuery(url2.searchParams);
|
|
148945
|
+
} catch (err) {
|
|
148946
|
+
if (err instanceof SearchQueryError) {
|
|
148947
|
+
json2(res, 400, { error: err.message, code: err.code });
|
|
148948
|
+
return;
|
|
148949
|
+
}
|
|
148950
|
+
throw err;
|
|
148484
148951
|
}
|
|
148485
|
-
const
|
|
148952
|
+
const { q: q2, limit, offset, filters } = parsed;
|
|
148953
|
+
const startedAt = Date.now();
|
|
148486
148954
|
const scanner = await this.getScanner();
|
|
148487
148955
|
const results = await search(
|
|
148488
148956
|
q2,
|
|
148489
148957
|
{
|
|
148490
|
-
|
|
148958
|
+
// Fetch beyond the requested page: filters below are applied AFTER the
|
|
148959
|
+
// scanner returns, so slicing at `limit` here would drop results that a
|
|
148960
|
+
// later page should contain. Bounded so a broad query cannot pull an
|
|
148961
|
+
// unbounded set into memory.
|
|
148962
|
+
limit: Math.min(offset + limit * SEARCH_OVERFETCH, SEARCH_MAX_SCAN),
|
|
148491
148963
|
include: "conversations",
|
|
148492
148964
|
...this.scanProfiles ? { profiles: this.scanProfiles } : {},
|
|
148493
148965
|
...this.codexScanOpts()
|
|
@@ -148511,13 +148983,24 @@ var StreamerServer = class {
|
|
|
148511
148983
|
lastActivity: r.meta.timestamp,
|
|
148512
148984
|
firstMessage: r.meta.firstMessage ?? void 0,
|
|
148513
148985
|
lastMessage: r.meta.lastMessage ?? void 0,
|
|
148514
|
-
provider: r.meta.provider ?? CLAUDE_CODE_PROVIDER2
|
|
148986
|
+
provider: r.meta.provider ?? CLAUDE_CODE_PROVIDER2,
|
|
148987
|
+
// The scanner already computes relevance and match snippets; the previous
|
|
148988
|
+
// adapter discarded both, so results arrived in an unexplained order with
|
|
148989
|
+
// no indication of WHY anything matched.
|
|
148990
|
+
score: r.score,
|
|
148991
|
+
matches: Array.isArray(r.matches) ? r.matches.map((m2) => ({
|
|
148992
|
+
field: m2.field,
|
|
148993
|
+
snippet: m2.snippet
|
|
148994
|
+
})) : []
|
|
148515
148995
|
}));
|
|
148996
|
+
const page = paginate(applyFilters(adapted, filters), offset, limit);
|
|
148516
148997
|
json2(res, 200, {
|
|
148517
|
-
conversations:
|
|
148518
|
-
hasMore:
|
|
148519
|
-
offset:
|
|
148520
|
-
total:
|
|
148998
|
+
conversations: page.items,
|
|
148999
|
+
hasMore: page.hasMore,
|
|
149000
|
+
offset: page.offset,
|
|
149001
|
+
total: page.total,
|
|
149002
|
+
// Query timing, so a slow search is diagnosable rather than merely felt.
|
|
149003
|
+
tookMs: Date.now() - startedAt
|
|
148521
149004
|
});
|
|
148522
149005
|
}
|
|
148523
149006
|
async handleListSessions(url2, res) {
|
|
@@ -148563,7 +149046,7 @@ var StreamerServer = class {
|
|
|
148563
149046
|
if (this.rejectIfWarmingUp(res)) return;
|
|
148564
149047
|
const session = this.sessionStore.get(sessionId, this.ptyAttachedIds());
|
|
148565
149048
|
if (session) {
|
|
148566
|
-
if (!(0,
|
|
149049
|
+
if (!(0, import_fs30.existsSync)(session.projectPath)) {
|
|
148567
149050
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
148568
149051
|
}
|
|
148569
149052
|
const reconciled = this.withReconciledLifecycle([session])[0];
|
|
@@ -148590,7 +149073,7 @@ var StreamerServer = class {
|
|
|
148590
149073
|
json2(res, 404, { error: "Session not found" });
|
|
148591
149074
|
}
|
|
148592
149075
|
async handleResume(req, res) {
|
|
148593
|
-
const body = await
|
|
149076
|
+
const body = await readBody2(req);
|
|
148594
149077
|
const sessionId = body.sessionId ?? body.conversationId;
|
|
148595
149078
|
if (!sessionId) {
|
|
148596
149079
|
json2(res, 400, { error: "Missing sessionId" });
|
|
@@ -148722,7 +149205,7 @@ var StreamerServer = class {
|
|
|
148722
149205
|
return;
|
|
148723
149206
|
}
|
|
148724
149207
|
if (this.agentConfig.enabled) {
|
|
148725
|
-
const body2 = await
|
|
149208
|
+
const body2 = await readBody2(req);
|
|
148726
149209
|
const cache = this.cache;
|
|
148727
149210
|
if (!cache) {
|
|
148728
149211
|
json2(res, 503, {
|
|
@@ -148741,7 +149224,7 @@ var StreamerServer = class {
|
|
|
148741
149224
|
json2(res, result.status, result.body);
|
|
148742
149225
|
return;
|
|
148743
149226
|
}
|
|
148744
|
-
const body = await
|
|
149227
|
+
const body = await readBody2(req);
|
|
148745
149228
|
const { input, keys } = body;
|
|
148746
149229
|
let idempotencyKey;
|
|
148747
149230
|
try {
|
|
@@ -148905,7 +149388,7 @@ var StreamerServer = class {
|
|
|
148905
149388
|
});
|
|
148906
149389
|
}
|
|
148907
149390
|
async handleSendAnswer(sessionId, req, res) {
|
|
148908
|
-
const body = await
|
|
149391
|
+
const body = await readBody2(req);
|
|
148909
149392
|
const pending = this.pendingQuestions.get(sessionId);
|
|
148910
149393
|
const resolution = resolveAnswer(pending, body);
|
|
148911
149394
|
if (!resolution.ok) {
|
|
@@ -148934,7 +149417,7 @@ var StreamerServer = class {
|
|
|
148934
149417
|
json2(res, 400, { error: "Session has no project path" });
|
|
148935
149418
|
return;
|
|
148936
149419
|
}
|
|
148937
|
-
const body = await
|
|
149420
|
+
const body = await readBody2(req);
|
|
148938
149421
|
const { filename, mimeType, dataBase64 } = body ?? {};
|
|
148939
149422
|
if (typeof filename !== "string" || typeof mimeType !== "string" || typeof dataBase64 !== "string") {
|
|
148940
149423
|
json2(res, 400, { error: "Missing filename, mimeType, or dataBase64" });
|
|
@@ -149136,7 +149619,7 @@ var StreamerServer = class {
|
|
|
149136
149619
|
return;
|
|
149137
149620
|
}
|
|
149138
149621
|
if (this.agentConfig.enabled) {
|
|
149139
|
-
const body2 = await
|
|
149622
|
+
const body2 = await readBody2(req);
|
|
149140
149623
|
const result = await handleStartAgentSession(body2, {
|
|
149141
149624
|
sessionStore: this.sessionStore,
|
|
149142
149625
|
// biome-ignore lint/style/noNonNullAssertion: agentClient is set when agentConfig.enabled is true
|
|
@@ -149150,7 +149633,7 @@ var StreamerServer = class {
|
|
|
149150
149633
|
}
|
|
149151
149634
|
return;
|
|
149152
149635
|
}
|
|
149153
|
-
const body = await
|
|
149636
|
+
const body = await readBody2(req);
|
|
149154
149637
|
const { path: relativePath, provider: requestedProvider, systemPrompt: clientPrompt } = body;
|
|
149155
149638
|
if (requestedProvider !== void 0 && !isProviderName(requestedProvider)) {
|
|
149156
149639
|
json2(res, 400, { error: "Invalid provider" });
|
|
@@ -149287,7 +149770,7 @@ var StreamerServer = class {
|
|
|
149287
149770
|
// file isn't slurped in full.
|
|
149288
149771
|
readFirstLineSessionId(filePath) {
|
|
149289
149772
|
try {
|
|
149290
|
-
const content = (0,
|
|
149773
|
+
const content = (0, import_fs30.readFileSync)(filePath, "utf8");
|
|
149291
149774
|
const nl = content.indexOf("\n");
|
|
149292
149775
|
const firstLine = nl === -1 ? content : content.slice(0, nl);
|
|
149293
149776
|
if (!firstLine.trim()) return null;
|
|
@@ -149302,7 +149785,7 @@ var StreamerServer = class {
|
|
|
149302
149785
|
// was passed to Claude via --session-id so the filename matches from the start.
|
|
149303
149786
|
watchForJsonl(sessionId, projectPath) {
|
|
149304
149787
|
const encoded = projectPath.replace(/[/\\:.]/g, "-");
|
|
149305
|
-
const projectsDir = (0, import_path29.join)((0,
|
|
149788
|
+
const projectsDir = (0, import_path29.join)((0, import_os13.homedir)(), ".claude", "projects", encoded);
|
|
149306
149789
|
const expectedFile = `${sessionId}.jsonl`;
|
|
149307
149790
|
const filePath = (0, import_path29.join)(projectsDir, expectedFile);
|
|
149308
149791
|
const deadline = Date.now() + 12e4;
|
|
@@ -149322,11 +149805,11 @@ var StreamerServer = class {
|
|
|
149322
149805
|
cleanup();
|
|
149323
149806
|
return;
|
|
149324
149807
|
}
|
|
149325
|
-
let resolvedFilePath = (0,
|
|
149326
|
-
if (!resolvedFilePath && (0,
|
|
149808
|
+
let resolvedFilePath = (0, import_fs30.existsSync)(filePath) ? filePath : null;
|
|
149809
|
+
if (!resolvedFilePath && (0, import_fs30.existsSync)(projectsDir)) {
|
|
149327
149810
|
try {
|
|
149328
149811
|
const now = Date.now();
|
|
149329
|
-
const match2 = (0,
|
|
149812
|
+
const match2 = (0, import_fs30.readdirSync)(projectsDir).filter((f2) => f2.endsWith(".jsonl")).map((f2) => ({ f: f2, mtime: (0, import_fs30.statSync)((0, import_path29.join)(projectsDir, f2)).mtimeMs })).filter(({ mtime }) => now - mtime < 5e3).filter(
|
|
149330
149813
|
({ f: f2 }) => (0, import_path29.basename)(f2, ".jsonl") === sessionId || this.readFirstLineSessionId((0, import_path29.join)(projectsDir, f2)) === sessionId
|
|
149331
149814
|
).sort((a, b2) => b2.mtime - a.mtime)[0];
|
|
149332
149815
|
if (match2) resolvedFilePath = (0, import_path29.join)(projectsDir, match2.f);
|
|
@@ -149337,7 +149820,7 @@ var StreamerServer = class {
|
|
|
149337
149820
|
cleanup();
|
|
149338
149821
|
this.sessionFileMap.set(sessionId, resolvedFilePath);
|
|
149339
149822
|
try {
|
|
149340
|
-
const existing = (0,
|
|
149823
|
+
const existing = (0, import_fs30.readFileSync)(resolvedFilePath, "utf8").split("\n").filter(Boolean);
|
|
149341
149824
|
if (existing.length > 0) {
|
|
149342
149825
|
this.broadcastConversationLines(sessionId, existing);
|
|
149343
149826
|
}
|
|
@@ -149361,7 +149844,7 @@ var StreamerServer = class {
|
|
|
149361
149844
|
if (this.sessionFileMap.has(sessionId)) return;
|
|
149362
149845
|
try {
|
|
149363
149846
|
require("fs").mkdirSync(projectsDir, { recursive: true });
|
|
149364
|
-
watcher = (0,
|
|
149847
|
+
watcher = (0, import_fs30.watch)(projectsDir, tryWire);
|
|
149365
149848
|
watcher.on("error", cleanup);
|
|
149366
149849
|
} catch {
|
|
149367
149850
|
}
|
|
@@ -149390,7 +149873,7 @@ var StreamerServer = class {
|
|
|
149390
149873
|
};
|
|
149391
149874
|
const matchesProjectPath = (candidatePath) => {
|
|
149392
149875
|
try {
|
|
149393
|
-
const firstLine = (0,
|
|
149876
|
+
const firstLine = (0, import_fs30.readFileSync)(candidatePath, "utf8").split("\n", 1)[0];
|
|
149394
149877
|
if (!firstLine) return null;
|
|
149395
149878
|
const parsed = JSON.parse(firstLine);
|
|
149396
149879
|
if (parsed?.type !== "session_meta") return null;
|
|
@@ -149419,15 +149902,15 @@ var StreamerServer = class {
|
|
|
149419
149902
|
);
|
|
149420
149903
|
for (const root of this.codexRoots) {
|
|
149421
149904
|
const sessionsDir = (0, import_path29.join)(root, dateDir);
|
|
149422
|
-
if (!(0,
|
|
149905
|
+
if (!(0, import_fs30.existsSync)(sessionsDir)) continue;
|
|
149423
149906
|
let candidateFiles;
|
|
149424
149907
|
try {
|
|
149425
|
-
candidateFiles = (0,
|
|
149908
|
+
candidateFiles = (0, import_fs30.readdirSync)(sessionsDir).filter((f2) => f2.endsWith(".jsonl"));
|
|
149426
149909
|
} catch {
|
|
149427
149910
|
continue;
|
|
149428
149911
|
}
|
|
149429
149912
|
const nowMs = Date.now();
|
|
149430
|
-
const recentCandidates = candidateFiles.map((f2) => ({ f: f2, mtime: (0,
|
|
149913
|
+
const recentCandidates = candidateFiles.map((f2) => ({ f: f2, mtime: (0, import_fs30.statSync)((0, import_path29.join)(sessionsDir, f2)).mtimeMs })).filter(({ mtime }) => nowMs - mtime < 1e4).sort((a, b2) => b2.mtime - a.mtime);
|
|
149431
149914
|
for (const { f: f2 } of recentCandidates) {
|
|
149432
149915
|
const candidatePath = (0, import_path29.join)(sessionsDir, f2);
|
|
149433
149916
|
const match2 = matchesProjectPath(candidatePath);
|
|
@@ -149439,7 +149922,7 @@ var StreamerServer = class {
|
|
|
149439
149922
|
this.sessionFileMap.set(sessionId, candidatePath);
|
|
149440
149923
|
this.fileWatcher.watch(candidatePath);
|
|
149441
149924
|
try {
|
|
149442
|
-
const existing = (0,
|
|
149925
|
+
const existing = (0, import_fs30.readFileSync)(candidatePath, "utf8").split("\n").filter(Boolean);
|
|
149443
149926
|
if (existing.length > 0) {
|
|
149444
149927
|
this.broadcastConversationLines(sessionId, existing);
|
|
149445
149928
|
}
|
|
@@ -149508,7 +149991,7 @@ var StreamerServer = class {
|
|
|
149508
149991
|
});
|
|
149509
149992
|
return;
|
|
149510
149993
|
}
|
|
149511
|
-
const body = await
|
|
149994
|
+
const body = await readBody2(req);
|
|
149512
149995
|
const { path: relativePath, name } = body;
|
|
149513
149996
|
if (!name || typeof name !== "string") {
|
|
149514
149997
|
json2(res, 400, { error: "Missing name field" });
|
|
@@ -149538,7 +150021,7 @@ var StreamerServer = class {
|
|
|
149538
150021
|
}
|
|
149539
150022
|
let parsed;
|
|
149540
150023
|
try {
|
|
149541
|
-
parsed = await
|
|
150024
|
+
parsed = await readBody2(req);
|
|
149542
150025
|
} catch {
|
|
149543
150026
|
json2(res, 400, { error: "Invalid JSON" });
|
|
149544
150027
|
return;
|
|
@@ -149595,7 +150078,7 @@ var StreamerServer = class {
|
|
|
149595
150078
|
}
|
|
149596
150079
|
let parsed;
|
|
149597
150080
|
try {
|
|
149598
|
-
parsed = await
|
|
150081
|
+
parsed = await readBody2(req);
|
|
149599
150082
|
} catch {
|
|
149600
150083
|
json2(res, 400, { error: "Invalid JSON" });
|
|
149601
150084
|
return;
|
|
@@ -149654,7 +150137,7 @@ async function waitForProcessExit(pid, timeoutMs, pollMs = ADOPT_KILL_POLL_MS) {
|
|
|
149654
150137
|
}
|
|
149655
150138
|
function classifyResumability(cwd) {
|
|
149656
150139
|
if (!cwd) return { resumable: true };
|
|
149657
|
-
if ((0,
|
|
150140
|
+
if ((0, import_fs30.existsSync)(cwd)) return { resumable: true };
|
|
149658
150141
|
const ranInWorktree = /\/\.worktrees\//.test(cwd) || /\/\.claude\/worktrees\//.test(cwd);
|
|
149659
150142
|
return {
|
|
149660
150143
|
resumable: false,
|
|
@@ -149772,7 +150255,7 @@ function parseSessionListQuery(url2) {
|
|
|
149772
150255
|
const cursor = url2.searchParams.get("cursor") ?? void 0;
|
|
149773
150256
|
return { query: { limit, sortBy, order, status, cursor } };
|
|
149774
150257
|
}
|
|
149775
|
-
function
|
|
150258
|
+
function readBody2(req) {
|
|
149776
150259
|
return new Promise((resolve4, reject) => {
|
|
149777
150260
|
const chunks = [];
|
|
149778
150261
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
@@ -150324,7 +150807,7 @@ var import_node_fs18 = require("fs");
|
|
|
150324
150807
|
|
|
150325
150808
|
// node_modules/tar/dist/esm/index.min.js
|
|
150326
150809
|
var import_events3 = __toESM(require("events"), 1);
|
|
150327
|
-
var
|
|
150810
|
+
var import_fs31 = __toESM(require("fs"), 1);
|
|
150328
150811
|
var import_node_events3 = require("events");
|
|
150329
150812
|
var import_node_stream2 = __toESM(require("stream"), 1);
|
|
150330
150813
|
var import_node_string_decoder = require("string_decoder");
|
|
@@ -150338,8 +150821,8 @@ var Ps = __toESM(require("zlib"), 1);
|
|
|
150338
150821
|
var import_zlib = __toESM(require("zlib"), 1);
|
|
150339
150822
|
var import_node_path15 = require("path");
|
|
150340
150823
|
var import_node_path16 = require("path");
|
|
150341
|
-
var import_fs31 = __toESM(require("fs"), 1);
|
|
150342
150824
|
var import_fs32 = __toESM(require("fs"), 1);
|
|
150825
|
+
var import_fs33 = __toESM(require("fs"), 1);
|
|
150343
150826
|
var import_path31 = __toESM(require("path"), 1);
|
|
150344
150827
|
var import_node_path17 = require("path");
|
|
150345
150828
|
var import_path32 = __toESM(require("path"), 1);
|
|
@@ -150348,7 +150831,7 @@ var import_node_assert = __toESM(require("assert"), 1);
|
|
|
150348
150831
|
var import_node_crypto6 = require("crypto");
|
|
150349
150832
|
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
150350
150833
|
var import_node_path18 = __toESM(require("path"), 1);
|
|
150351
|
-
var
|
|
150834
|
+
var import_fs34 = __toESM(require("fs"), 1);
|
|
150352
150835
|
var import_node_fs15 = __toESM(require("fs"), 1);
|
|
150353
150836
|
var import_node_path19 = __toESM(require("path"), 1);
|
|
150354
150837
|
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
@@ -150700,7 +151183,7 @@ var A = class extends import_node_events3.EventEmitter {
|
|
|
150700
151183
|
return Wr;
|
|
150701
151184
|
}
|
|
150702
151185
|
};
|
|
150703
|
-
var Jr =
|
|
151186
|
+
var Jr = import_fs31.default.writev;
|
|
150704
151187
|
var ht = /* @__PURE__ */ Symbol("_autoClose");
|
|
150705
151188
|
var H2 = /* @__PURE__ */ Symbol("_close");
|
|
150706
151189
|
var te = /* @__PURE__ */ Symbol("_ended");
|
|
@@ -150755,7 +151238,7 @@ var _t = class extends A {
|
|
|
150755
151238
|
throw new TypeError("this is a readable stream");
|
|
150756
151239
|
}
|
|
150757
151240
|
[at]() {
|
|
150758
|
-
|
|
151241
|
+
import_fs31.default.open(this[U], "r", (t, e) => this[Ht](t, e));
|
|
150759
151242
|
}
|
|
150760
151243
|
[Ht](t, e) {
|
|
150761
151244
|
t ? this[Ut](t) : (this[u] = e, this.emit("open", e), this[zt]());
|
|
@@ -150768,7 +151251,7 @@ var _t = class extends A {
|
|
|
150768
151251
|
this[j] = true;
|
|
150769
151252
|
let t = this[vi]();
|
|
150770
151253
|
if (t.length === 0) return process.nextTick(() => this[Ii](null, 0, t));
|
|
150771
|
-
|
|
151254
|
+
import_fs31.default.read(this[u], t, 0, t.length, null, (e, i, r) => this[Ii](e, i, r));
|
|
150772
151255
|
}
|
|
150773
151256
|
}
|
|
150774
151257
|
[Ii](t, e, i) {
|
|
@@ -150777,7 +151260,7 @@ var _t = class extends A {
|
|
|
150777
151260
|
[H2]() {
|
|
150778
151261
|
if (this[ht] && typeof this[u] == "number") {
|
|
150779
151262
|
let t = this[u];
|
|
150780
|
-
this[u] = void 0,
|
|
151263
|
+
this[u] = void 0, import_fs31.default.close(t, (e) => e ? this.emit("error", e) : this.emit("close"));
|
|
150781
151264
|
}
|
|
150782
151265
|
}
|
|
150783
151266
|
[Ut](t) {
|
|
@@ -150805,7 +151288,7 @@ var Be = class extends _t {
|
|
|
150805
151288
|
[at]() {
|
|
150806
151289
|
let t = true;
|
|
150807
151290
|
try {
|
|
150808
|
-
this[Ht](null,
|
|
151291
|
+
this[Ht](null, import_fs31.default.openSync(this[U], "r")), t = false;
|
|
150809
151292
|
} finally {
|
|
150810
151293
|
t && this[H2]();
|
|
150811
151294
|
}
|
|
@@ -150816,7 +151299,7 @@ var Be = class extends _t {
|
|
|
150816
151299
|
if (!this[j]) {
|
|
150817
151300
|
this[j] = true;
|
|
150818
151301
|
do {
|
|
150819
|
-
let e = this[vi](), i = e.length === 0 ? 0 :
|
|
151302
|
+
let e = this[vi](), i = e.length === 0 ? 0 : import_fs31.default.readSync(this[u], e, 0, e.length, null);
|
|
150820
151303
|
if (!this[ki](i, e)) break;
|
|
150821
151304
|
} while (true);
|
|
150822
151305
|
this[j] = false;
|
|
@@ -150829,7 +151312,7 @@ var Be = class extends _t {
|
|
|
150829
151312
|
[H2]() {
|
|
150830
151313
|
if (this[ht] && typeof this[u] == "number") {
|
|
150831
151314
|
let t = this[u];
|
|
150832
|
-
this[u] = void 0,
|
|
151315
|
+
this[u] = void 0, import_fs31.default.closeSync(t), this.emit("close");
|
|
150833
151316
|
}
|
|
150834
151317
|
}
|
|
150835
151318
|
};
|
|
@@ -150871,7 +151354,7 @@ var et = class extends import_events3.default {
|
|
|
150871
151354
|
this[H2](), this[gt] = true, this.emit("error", t);
|
|
150872
151355
|
}
|
|
150873
151356
|
[at]() {
|
|
150874
|
-
|
|
151357
|
+
import_fs31.default.open(this[U], this[tt], this[ie], (t, e) => this[Ht](t, e));
|
|
150875
151358
|
}
|
|
150876
151359
|
[Ht](t, e) {
|
|
150877
151360
|
this[Me] && this[tt] === "r+" && t && t.code === "ENOENT" ? (this[tt] = "w", this[at]()) : t ? this[Ut](t) : (this[u] = e, this.emit("open", e), this[gt] || this[Ai]());
|
|
@@ -150883,7 +151366,7 @@ var et = class extends import_events3.default {
|
|
|
150883
151366
|
return typeof t == "string" && (t = Buffer.from(t, e)), this[te] ? (this.emit("error", new Error("write() after end()")), false) : this[u] === void 0 || this[gt] || this[Y2].length ? (this[Y2].push(t), this[ke] = true, false) : (this[gt] = true, this[ve](t), true);
|
|
150884
151367
|
}
|
|
150885
151368
|
[ve](t) {
|
|
150886
|
-
|
|
151369
|
+
import_fs31.default.write(this[u], t, 0, t.length, this[ot], (e, i) => this[Pt](e, i));
|
|
150887
151370
|
}
|
|
150888
151371
|
[Pt](t, e) {
|
|
150889
151372
|
t ? this[Ut](t) : (this[ot] !== void 0 && typeof e == "number" && (this[ot] += e), this[Y2].length ? this[Ai]() : (this[gt] = false, this[te] && !this[Ni] ? (this[Ni] = true, this[H2](), this.emit("finish")) : this[ke] && (this[ke] = false, this.emit("drain"))));
|
|
@@ -150899,7 +151382,7 @@ var et = class extends import_events3.default {
|
|
|
150899
151382
|
[H2]() {
|
|
150900
151383
|
if (this[ht] && typeof this[u] == "number") {
|
|
150901
151384
|
let t = this[u];
|
|
150902
|
-
this[u] = void 0,
|
|
151385
|
+
this[u] = void 0, import_fs31.default.close(t, (e) => e ? this.emit("error", e) : this.emit("close"));
|
|
150903
151386
|
}
|
|
150904
151387
|
}
|
|
150905
151388
|
};
|
|
@@ -150907,24 +151390,24 @@ var Wt = class extends et {
|
|
|
150907
151390
|
[at]() {
|
|
150908
151391
|
let t;
|
|
150909
151392
|
if (this[Me] && this[tt] === "r+") try {
|
|
150910
|
-
t =
|
|
151393
|
+
t = import_fs31.default.openSync(this[U], this[tt], this[ie]);
|
|
150911
151394
|
} catch (e) {
|
|
150912
151395
|
if (e?.code === "ENOENT") return this[tt] = "w", this[at]();
|
|
150913
151396
|
throw e;
|
|
150914
151397
|
}
|
|
150915
|
-
else t =
|
|
151398
|
+
else t = import_fs31.default.openSync(this[U], this[tt], this[ie]);
|
|
150916
151399
|
this[Ht](null, t);
|
|
150917
151400
|
}
|
|
150918
151401
|
[H2]() {
|
|
150919
151402
|
if (this[ht] && typeof this[u] == "number") {
|
|
150920
151403
|
let t = this[u];
|
|
150921
|
-
this[u] = void 0,
|
|
151404
|
+
this[u] = void 0, import_fs31.default.closeSync(t), this.emit("close");
|
|
150922
151405
|
}
|
|
150923
151406
|
}
|
|
150924
151407
|
[ve](t) {
|
|
150925
151408
|
let e = true;
|
|
150926
151409
|
try {
|
|
150927
|
-
this[Pt](null,
|
|
151410
|
+
this[Pt](null, import_fs31.default.writeSync(this[u], t, 0, t.length, this[ot])), e = false;
|
|
150928
151411
|
} finally {
|
|
150929
151412
|
if (e) try {
|
|
150930
151413
|
this[H2]();
|
|
@@ -151842,7 +152325,7 @@ var de = class extends A {
|
|
|
151842
152325
|
return t === "error" && (this.#t = true), super.emit(t, ...e);
|
|
151843
152326
|
}
|
|
151844
152327
|
[ss]() {
|
|
151845
|
-
|
|
152328
|
+
import_fs33.default.lstat(this.absolute, (t, e) => {
|
|
151846
152329
|
if (t) return this.emit("error", t);
|
|
151847
152330
|
this[si](e);
|
|
151848
152331
|
});
|
|
@@ -151880,7 +152363,7 @@ var de = class extends A {
|
|
|
151880
152363
|
this.path.slice(-1) !== "/" && (this.path += "/"), this.stat.size = 0, this[fe](), this.end();
|
|
151881
152364
|
}
|
|
151882
152365
|
[is]() {
|
|
151883
|
-
|
|
152366
|
+
import_fs33.default.readlink(this.absolute, (t, e) => {
|
|
151884
152367
|
if (t) return this.emit("error", t);
|
|
151885
152368
|
this[ns](e);
|
|
151886
152369
|
});
|
|
@@ -151903,7 +152386,7 @@ var de = class extends A {
|
|
|
151903
152386
|
this[os]();
|
|
151904
152387
|
}
|
|
151905
152388
|
[os]() {
|
|
151906
|
-
|
|
152389
|
+
import_fs33.default.open(this.absolute, "r", (t, e) => {
|
|
151907
152390
|
if (t) return this.emit("error", t);
|
|
151908
152391
|
this[hs](e);
|
|
151909
152392
|
});
|
|
@@ -151918,14 +152401,14 @@ var de = class extends A {
|
|
|
151918
152401
|
[ii]() {
|
|
151919
152402
|
let { fd: t, buf: e, offset: i, length: r, pos: n } = this;
|
|
151920
152403
|
if (t === void 0 || e === void 0) throw new Error("cannot read file without first opening");
|
|
151921
|
-
|
|
152404
|
+
import_fs33.default.read(t, e, i, r, n, (o, h) => {
|
|
151922
152405
|
if (o) return this[pt](() => this.emit("error", o));
|
|
151923
152406
|
this[rs](h);
|
|
151924
152407
|
});
|
|
151925
152408
|
}
|
|
151926
152409
|
[pt](t = () => {
|
|
151927
152410
|
}) {
|
|
151928
|
-
this.fd !== void 0 &&
|
|
152411
|
+
this.fd !== void 0 && import_fs33.default.close(this.fd, t);
|
|
151929
152412
|
}
|
|
151930
152413
|
[rs](t) {
|
|
151931
152414
|
if (t <= 0 && this.remain > 0) {
|
|
@@ -151960,20 +152443,20 @@ var de = class extends A {
|
|
|
151960
152443
|
var ni = class extends de {
|
|
151961
152444
|
sync = true;
|
|
151962
152445
|
[ss]() {
|
|
151963
|
-
this[si](
|
|
152446
|
+
this[si](import_fs33.default.lstatSync(this.absolute));
|
|
151964
152447
|
}
|
|
151965
152448
|
[is]() {
|
|
151966
|
-
this[ns](
|
|
152449
|
+
this[ns](import_fs33.default.readlinkSync(this.absolute));
|
|
151967
152450
|
}
|
|
151968
152451
|
[os]() {
|
|
151969
|
-
this[hs](
|
|
152452
|
+
this[hs](import_fs33.default.openSync(this.absolute, "r"));
|
|
151970
152453
|
}
|
|
151971
152454
|
[ii]() {
|
|
151972
152455
|
let t = true;
|
|
151973
152456
|
try {
|
|
151974
152457
|
let { fd: e, buf: i, offset: r, length: n, pos: o } = this;
|
|
151975
152458
|
if (e === void 0 || i === void 0) throw new Error("fd and buf must be set in READ method");
|
|
151976
|
-
let h =
|
|
152459
|
+
let h = import_fs33.default.readSync(e, i, r, n, o);
|
|
151977
152460
|
this[rs](h), t = false;
|
|
151978
152461
|
} finally {
|
|
151979
152462
|
if (t) try {
|
|
@@ -151988,7 +152471,7 @@ var ni = class extends de {
|
|
|
151988
152471
|
}
|
|
151989
152472
|
[pt](t = () => {
|
|
151990
152473
|
}) {
|
|
151991
|
-
this.fd !== void 0 &&
|
|
152474
|
+
this.fd !== void 0 && import_fs33.default.closeSync(this.fd), t();
|
|
151992
152475
|
}
|
|
151993
152476
|
};
|
|
151994
152477
|
var oi = class extends A {
|
|
@@ -152319,7 +152802,7 @@ var wt = class extends A {
|
|
|
152319
152802
|
[ds](t) {
|
|
152320
152803
|
t.pending = true, this[G] += 1;
|
|
152321
152804
|
let e = this.follow ? "stat" : "lstat";
|
|
152322
|
-
|
|
152805
|
+
import_fs32.default[e](t.absolute, (i, r) => {
|
|
152323
152806
|
t.pending = false, this[G] -= 1, i ? this.emit("error", i) : this[li](t, r);
|
|
152324
152807
|
});
|
|
152325
152808
|
}
|
|
@@ -152333,7 +152816,7 @@ var wt = class extends A {
|
|
|
152333
152816
|
this[Ft]();
|
|
152334
152817
|
}
|
|
152335
152818
|
[ms](t) {
|
|
152336
|
-
t.pending = true, this[G] += 1,
|
|
152819
|
+
t.pending = true, this[G] += 1, import_fs32.default.readdir(t.absolute, (e, i) => {
|
|
152337
152820
|
if (t.pending = false, this[G] -= 1, e) return this.emit("error", e);
|
|
152338
152821
|
this[fi](t, i);
|
|
152339
152822
|
});
|
|
@@ -152434,10 +152917,10 @@ var kt = class extends wt {
|
|
|
152434
152917
|
}
|
|
152435
152918
|
[ds](t) {
|
|
152436
152919
|
let e = this.follow ? "statSync" : "lstatSync";
|
|
152437
|
-
this[li](t,
|
|
152920
|
+
this[li](t, import_fs32.default[e](t.absolute));
|
|
152438
152921
|
}
|
|
152439
152922
|
[ms](t) {
|
|
152440
|
-
this[fi](t,
|
|
152923
|
+
this[fi](t, import_fs32.default.readdirSync(t.absolute));
|
|
152441
152924
|
}
|
|
152442
152925
|
[di](t) {
|
|
152443
152926
|
let e = t.entry, i = this.zip;
|
|
@@ -152488,8 +152971,8 @@ var Qn = K(Vn, $n, Xn, qn, (s3, t) => {
|
|
|
152488
152971
|
});
|
|
152489
152972
|
var Jn = process.env.__FAKE_PLATFORM__ || process.platform;
|
|
152490
152973
|
var Er = Jn === "win32";
|
|
152491
|
-
var { O_CREAT: wr, O_NOFOLLOW: mr, O_TRUNC: Sr, O_WRONLY: yr } =
|
|
152492
|
-
var Rr = Number(process.env.__FAKE_FS_O_FILENAME__) ||
|
|
152974
|
+
var { O_CREAT: wr, O_NOFOLLOW: mr, O_TRUNC: Sr, O_WRONLY: yr } = import_fs34.default.constants;
|
|
152975
|
+
var Rr = Number(process.env.__FAKE_FS_O_FILENAME__) || import_fs34.default.constants.UV_FS_O_FILEMAP || 0;
|
|
152493
152976
|
var jn = Er && !!Rr;
|
|
152494
152977
|
var to = 512 * 1024;
|
|
152495
152978
|
var eo = Rr | Sr | wr | yr;
|
|
@@ -153867,12 +154350,12 @@ program2.command("cache").description("Manage the local SQLite conversation cach
|
|
|
153867
154350
|
"Cache directory (default: ~/.threadbase/cache)",
|
|
153868
154351
|
`${process.env.HOME}/.threadbase/cache`
|
|
153869
154352
|
).action((opts) => {
|
|
153870
|
-
const { rmSync: rmSync6, existsSync:
|
|
154353
|
+
const { rmSync: rmSync6, existsSync: existsSync16 } = require("fs");
|
|
153871
154354
|
const { join: join29 } = require("path");
|
|
153872
154355
|
const dbPath = join29(opts.cacheDir, "cache.db");
|
|
153873
154356
|
for (const suffix of ["", "-shm", "-wal"]) {
|
|
153874
154357
|
const f2 = dbPath + suffix;
|
|
153875
|
-
if (
|
|
154358
|
+
if (existsSync16(f2)) {
|
|
153876
154359
|
rmSync6(f2);
|
|
153877
154360
|
log11.info(`Deleted ${f2}`, { path: f2 }, "console");
|
|
153878
154361
|
}
|