@threadbase-sh/streamer 1.39.1 → 1.41.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/README.md +1 -1
- package/dist/cli.cjs +1055 -397
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +881 -223
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +824 -166
- package/dist/index.js.map +1 -1
- package/dist/{migrations/010_create_managed_sessions.sql → runtime-migrations/001_create_managed_sessions.sql} +6 -0
- package/package.json +2 -2
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
|
}),
|
|
@@ -6213,6 +6213,12 @@ var init_feature_flags = __esm({
|
|
|
6213
6213
|
description: "Send the built system prompt to fresh Codex sessions. Off by default: Codex has no --system-prompt flag, so the prompt goes in the positional [PROMPT] argument, which Codex treats as the user's opening turn rather than a system-level instruction.",
|
|
6214
6214
|
default: false,
|
|
6215
6215
|
env: "THREADBASE_FEATURE_CODEX_SYSTEM_PROMPT"
|
|
6216
|
+
},
|
|
6217
|
+
{
|
|
6218
|
+
id: "sessionRehydration",
|
|
6219
|
+
description: "Seed the session list at boot with sessions a previous streamer run left behind, so a restart leaves them one tap from resuming instead of silently gone. On by default, with a kill switch: it changes what GET /api/sessions contains.",
|
|
6220
|
+
default: true,
|
|
6221
|
+
env: "THREADBASE_FEATURE_SESSION_REHYDRATION"
|
|
6216
6222
|
}
|
|
6217
6223
|
];
|
|
6218
6224
|
}
|
|
@@ -38620,8 +38626,8 @@ var require_service_config = __commonJS({
|
|
|
38620
38626
|
}
|
|
38621
38627
|
if (Array.isArray(validatedConfig.clientHostname)) {
|
|
38622
38628
|
let hostnameMatched = false;
|
|
38623
|
-
for (const
|
|
38624
|
-
if (
|
|
38629
|
+
for (const hostname6 of validatedConfig.clientHostname) {
|
|
38630
|
+
if (hostname6 === os2.hostname()) {
|
|
38625
38631
|
hostnameMatched = true;
|
|
38626
38632
|
}
|
|
38627
38633
|
}
|
|
@@ -51190,8 +51196,8 @@ var require_single_subchannel_channel = __commonJS({
|
|
|
51190
51196
|
if (splitPath2.length >= 2) {
|
|
51191
51197
|
serviceName = splitPath2[1];
|
|
51192
51198
|
}
|
|
51193
|
-
const
|
|
51194
|
-
this.serviceUrl = `https://${
|
|
51199
|
+
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";
|
|
51200
|
+
this.serviceUrl = `https://${hostname6}/${serviceName}`;
|
|
51195
51201
|
const timeout = (0, deadline_1.getRelativeTimeout)(options.deadline);
|
|
51196
51202
|
if (timeout !== Infinity) {
|
|
51197
51203
|
if (timeout <= 0) {
|
|
@@ -51838,8 +51844,8 @@ var require_resolver_dns = __commonJS({
|
|
|
51838
51844
|
}
|
|
51839
51845
|
trace("Looking up DNS hostname " + this.dnsHostname);
|
|
51840
51846
|
this.latestLookupResult = null;
|
|
51841
|
-
const
|
|
51842
|
-
this.pendingLookupPromise = this.lookup(
|
|
51847
|
+
const hostname6 = this.dnsHostname;
|
|
51848
|
+
this.pendingLookupPromise = this.lookup(hostname6);
|
|
51843
51849
|
this.pendingLookupPromise.then((addressList) => {
|
|
51844
51850
|
if (this.pendingLookupPromise === null) {
|
|
51845
51851
|
return;
|
|
@@ -51862,7 +51868,7 @@ var require_resolver_dns = __commonJS({
|
|
|
51862
51868
|
this.listener((0, call_interface_1.statusOrFromError)(this.defaultResolutionError), {}, this.latestServiceConfigResult, "");
|
|
51863
51869
|
});
|
|
51864
51870
|
if (this.isServiceConfigEnabled && this.pendingTxtPromise === null) {
|
|
51865
|
-
this.pendingTxtPromise = this.resolveTxt(
|
|
51871
|
+
this.pendingTxtPromise = this.resolveTxt(hostname6);
|
|
51866
51872
|
this.pendingTxtPromise.then((txtRecord) => {
|
|
51867
51873
|
if (this.pendingTxtPromise === null) {
|
|
51868
51874
|
return;
|
|
@@ -51904,12 +51910,12 @@ var require_resolver_dns = __commonJS({
|
|
|
51904
51910
|
this.continueResolving = true;
|
|
51905
51911
|
}
|
|
51906
51912
|
}
|
|
51907
|
-
async lookup(
|
|
51913
|
+
async lookup(hostname6) {
|
|
51908
51914
|
if (environment_1.GRPC_NODE_USE_ALTERNATIVE_RESOLVER) {
|
|
51909
51915
|
trace("Using alternative DNS resolver.");
|
|
51910
51916
|
const records = await Promise.allSettled([
|
|
51911
|
-
this.alternativeResolver.resolve4(
|
|
51912
|
-
this.alternativeResolver.resolve6(
|
|
51917
|
+
this.alternativeResolver.resolve4(hostname6),
|
|
51918
|
+
this.alternativeResolver.resolve6(hostname6)
|
|
51913
51919
|
]);
|
|
51914
51920
|
if (records.every((result) => result.status === "rejected")) {
|
|
51915
51921
|
throw new Error(records[0].reason);
|
|
@@ -51921,15 +51927,15 @@ var require_resolver_dns = __commonJS({
|
|
|
51921
51927
|
port: +this.port
|
|
51922
51928
|
}));
|
|
51923
51929
|
}
|
|
51924
|
-
const addressList = await dns_1.promises.lookup(
|
|
51930
|
+
const addressList = await dns_1.promises.lookup(hostname6, { all: true });
|
|
51925
51931
|
return addressList.map((addr) => ({ host: addr.address, port: +this.port }));
|
|
51926
51932
|
}
|
|
51927
|
-
async resolveTxt(
|
|
51933
|
+
async resolveTxt(hostname6) {
|
|
51928
51934
|
if (environment_1.GRPC_NODE_USE_ALTERNATIVE_RESOLVER) {
|
|
51929
51935
|
trace("Using alternative DNS resolver.");
|
|
51930
|
-
return this.alternativeResolver.resolveTxt(
|
|
51936
|
+
return this.alternativeResolver.resolveTxt(hostname6);
|
|
51931
51937
|
}
|
|
51932
|
-
return dns_1.promises.resolveTxt(
|
|
51938
|
+
return dns_1.promises.resolveTxt(hostname6);
|
|
51933
51939
|
}
|
|
51934
51940
|
startNextResolutionTimer() {
|
|
51935
51941
|
var _a3, _b;
|
|
@@ -52057,13 +52063,13 @@ var require_http_proxy = __commonJS({
|
|
|
52057
52063
|
userCred = proxyUrl.username;
|
|
52058
52064
|
}
|
|
52059
52065
|
}
|
|
52060
|
-
const
|
|
52066
|
+
const hostname6 = proxyUrl.hostname;
|
|
52061
52067
|
let port = proxyUrl.port;
|
|
52062
52068
|
if (port === "") {
|
|
52063
52069
|
port = "80";
|
|
52064
52070
|
}
|
|
52065
52071
|
const result = {
|
|
52066
|
-
address: `${
|
|
52072
|
+
address: `${hostname6}:${port}`
|
|
52067
52073
|
};
|
|
52068
52074
|
if (userCred) {
|
|
52069
52075
|
result.creds = userCred;
|
|
@@ -53376,8 +53382,8 @@ var require_load_balancing_call = __commonJS({
|
|
|
53376
53382
|
if (splitPath2.length >= 2) {
|
|
53377
53383
|
serviceName = splitPath2[1];
|
|
53378
53384
|
}
|
|
53379
|
-
const
|
|
53380
|
-
this.serviceUrl = `https://${
|
|
53385
|
+
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";
|
|
53386
|
+
this.serviceUrl = `https://${hostname6}/${serviceName}`;
|
|
53381
53387
|
this.startTime = /* @__PURE__ */ new Date();
|
|
53382
53388
|
}
|
|
53383
53389
|
getDeadlineInfo() {
|
|
@@ -91186,9 +91192,9 @@ var require_parse_host_uri = __commonJS({
|
|
|
91186
91192
|
var ipv4Hostname = "(?:\\d{1,3}(?:\\.\\d{1,3}){3})";
|
|
91187
91193
|
var ipv6Hostname = "(?:\\[(?<ipv6>[0-9a-fA-F.:]+)\\])";
|
|
91188
91194
|
var dnsHostname = "(?:[^:/]+)";
|
|
91189
|
-
var
|
|
91195
|
+
var hostname6 = `(?:${ipv4Hostname}|${ipv6Hostname}|${dnsHostname})`;
|
|
91190
91196
|
var port = "(?::(?<port>\\d+))";
|
|
91191
|
-
var protoHostPortRegex = new RegExp(`^${scheme}??(?<hostname>${
|
|
91197
|
+
var protoHostPortRegex = new RegExp(`^${scheme}??(?<hostname>${hostname6})${port}?$`);
|
|
91192
91198
|
function splitProtoHostPort(uri) {
|
|
91193
91199
|
const match2 = protoHostPortRegex.exec(uri);
|
|
91194
91200
|
if (!match2?.groups)
|
|
@@ -91200,9 +91206,9 @@ var require_parse_host_uri = __commonJS({
|
|
|
91200
91206
|
};
|
|
91201
91207
|
}
|
|
91202
91208
|
function joinProtoHostPort(components) {
|
|
91203
|
-
const { scheme: scheme2, hostname:
|
|
91209
|
+
const { scheme: scheme2, hostname: hostname7, port: port2 } = components;
|
|
91204
91210
|
const schemeText = scheme2 ? `${scheme2}:` : "";
|
|
91205
|
-
const hostnameText =
|
|
91211
|
+
const hostnameText = hostname7.includes(":") ? `[${hostname7}]` : hostname7;
|
|
91206
91212
|
const portText = port2 !== void 0 ? `:${port2}` : "";
|
|
91207
91213
|
return `${schemeText}${hostnameText}${portText}`;
|
|
91208
91214
|
}
|
|
@@ -104140,11 +104146,11 @@ var require_pg_connection_string = __commonJS({
|
|
|
104140
104146
|
config2.client_encoding = result.searchParams.get("encoding");
|
|
104141
104147
|
return config2;
|
|
104142
104148
|
}
|
|
104143
|
-
const
|
|
104149
|
+
const hostname6 = dummyHost ? "" : result.hostname;
|
|
104144
104150
|
if (!config2.host) {
|
|
104145
|
-
config2.host = decodeURIComponent(
|
|
104146
|
-
} else if (
|
|
104147
|
-
result.pathname =
|
|
104151
|
+
config2.host = decodeURIComponent(hostname6);
|
|
104152
|
+
} else if (hostname6 && /^%2f/i.test(hostname6)) {
|
|
104153
|
+
result.pathname = hostname6 + result.pathname;
|
|
104148
104154
|
}
|
|
104149
104155
|
if (!config2.port) {
|
|
104150
104156
|
config2.port = result.port;
|
|
@@ -124077,8 +124083,8 @@ function isAbiMismatch(message) {
|
|
|
124077
124083
|
}
|
|
124078
124084
|
function checkSqliteAbi() {
|
|
124079
124085
|
try {
|
|
124080
|
-
const
|
|
124081
|
-
const db = new
|
|
124086
|
+
const Database4 = require("better-sqlite3");
|
|
124087
|
+
const db = new Database4(":memory:");
|
|
124082
124088
|
db.close();
|
|
124083
124089
|
} catch (err) {
|
|
124084
124090
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -135423,10 +135429,10 @@ async function search(query, options, scanner) {
|
|
|
135423
135429
|
// src/server.ts
|
|
135424
135430
|
var import_crypto13 = require("crypto");
|
|
135425
135431
|
var import_events2 = require("events");
|
|
135426
|
-
var
|
|
135432
|
+
var import_fs30 = require("fs");
|
|
135427
135433
|
var import_promises16 = require("fs/promises");
|
|
135428
135434
|
var import_http = require("http");
|
|
135429
|
-
var
|
|
135435
|
+
var import_os13 = require("os");
|
|
135430
135436
|
var import_path29 = require("path");
|
|
135431
135437
|
var import_readline4 = require("readline");
|
|
135432
135438
|
|
|
@@ -138231,6 +138237,215 @@ var errorMiddleware = (err, c) => {
|
|
|
138231
138237
|
return c.json({ error: message }, 500);
|
|
138232
138238
|
};
|
|
138233
138239
|
|
|
138240
|
+
// src/api/routes/backup.routes.ts
|
|
138241
|
+
var import_os5 = require("os");
|
|
138242
|
+
|
|
138243
|
+
// src/services/backup/backup.ts
|
|
138244
|
+
var BACKUP_FORMAT_VERSION = 1;
|
|
138245
|
+
var BackupError = class extends Error {
|
|
138246
|
+
constructor(message, code) {
|
|
138247
|
+
super(message);
|
|
138248
|
+
this.code = code;
|
|
138249
|
+
}
|
|
138250
|
+
code;
|
|
138251
|
+
};
|
|
138252
|
+
function validateArchive(input) {
|
|
138253
|
+
if (!input || typeof input !== "object") {
|
|
138254
|
+
throw new BackupError("Backup is not an object", "INVALID_ARCHIVE");
|
|
138255
|
+
}
|
|
138256
|
+
const archive = input;
|
|
138257
|
+
const manifest = archive.manifest;
|
|
138258
|
+
if (!manifest || typeof manifest !== "object") {
|
|
138259
|
+
throw new BackupError("Backup is missing its manifest", "INVALID_ARCHIVE");
|
|
138260
|
+
}
|
|
138261
|
+
if (manifest.formatVersion !== BACKUP_FORMAT_VERSION) {
|
|
138262
|
+
throw new BackupError(
|
|
138263
|
+
`Unsupported backup format version ${String(manifest.formatVersion)}; this build reads version ${BACKUP_FORMAT_VERSION}`,
|
|
138264
|
+
"UNSUPPORTED_VERSION"
|
|
138265
|
+
);
|
|
138266
|
+
}
|
|
138267
|
+
if (!Array.isArray(archive.projects)) {
|
|
138268
|
+
throw new BackupError("Backup is missing its projects array", "INVALID_ARCHIVE");
|
|
138269
|
+
}
|
|
138270
|
+
for (const [i, p2] of archive.projects.entries()) {
|
|
138271
|
+
if (!p2 || typeof p2 !== "object") {
|
|
138272
|
+
throw new BackupError(`Project at index ${i} is not an object`, "INVALID_ARCHIVE");
|
|
138273
|
+
}
|
|
138274
|
+
if (typeof p2.id !== "string" || p2.id.length === 0) {
|
|
138275
|
+
throw new BackupError(`Project at index ${i} has no id`, "INVALID_ARCHIVE");
|
|
138276
|
+
}
|
|
138277
|
+
if (typeof p2.path !== "string" || p2.path.length === 0) {
|
|
138278
|
+
throw new BackupError(`Project at index ${i} has no path`, "INVALID_ARCHIVE");
|
|
138279
|
+
}
|
|
138280
|
+
}
|
|
138281
|
+
const ids = new Set(archive.projects.map((p2) => p2.id));
|
|
138282
|
+
if (ids.size !== archive.projects.length) {
|
|
138283
|
+
throw new BackupError("Backup contains duplicate project ids", "INVALID_ARCHIVE");
|
|
138284
|
+
}
|
|
138285
|
+
return archive;
|
|
138286
|
+
}
|
|
138287
|
+
function remapPaths(projects, rules) {
|
|
138288
|
+
const ordered = [...rules].sort((a, b2) => b2.from.length - a.from.length);
|
|
138289
|
+
return projects.map((p2) => {
|
|
138290
|
+
const rule = ordered.find((r) => p2.path === r.from || p2.path.startsWith(`${r.from}/`));
|
|
138291
|
+
if (!rule) return p2;
|
|
138292
|
+
return { ...p2, path: `${rule.to}${p2.path.slice(rule.from.length)}` };
|
|
138293
|
+
});
|
|
138294
|
+
}
|
|
138295
|
+
function planRestore(incoming, existing) {
|
|
138296
|
+
const byId = new Map(existing.map((e) => [e.id, e]));
|
|
138297
|
+
const byPath = new Map(existing.map((e) => [e.path, e]));
|
|
138298
|
+
const plan = { create: [], update: [], conflict: [] };
|
|
138299
|
+
for (const p2 of incoming) {
|
|
138300
|
+
const sameId = byId.get(p2.id);
|
|
138301
|
+
if (sameId) {
|
|
138302
|
+
if (sameId.path !== p2.path) plan.update.push(p2);
|
|
138303
|
+
continue;
|
|
138304
|
+
}
|
|
138305
|
+
const samePath = byPath.get(p2.path);
|
|
138306
|
+
if (samePath) {
|
|
138307
|
+
plan.conflict.push({ incoming: p2, existingId: samePath.id });
|
|
138308
|
+
continue;
|
|
138309
|
+
}
|
|
138310
|
+
plan.create.push(p2);
|
|
138311
|
+
}
|
|
138312
|
+
return plan;
|
|
138313
|
+
}
|
|
138314
|
+
|
|
138315
|
+
// src/version.ts
|
|
138316
|
+
var import_node_fs7 = require("fs");
|
|
138317
|
+
var import_node_path8 = require("path");
|
|
138318
|
+
var cached2;
|
|
138319
|
+
function getVersion() {
|
|
138320
|
+
if (cached2 !== void 0) return cached2;
|
|
138321
|
+
cached2 = resolveVersion();
|
|
138322
|
+
return cached2;
|
|
138323
|
+
}
|
|
138324
|
+
function resolveVersion() {
|
|
138325
|
+
const scriptPath = process.argv[1] ?? "";
|
|
138326
|
+
const here = scriptPath ? (0, import_node_path8.dirname)(scriptPath) : process.cwd();
|
|
138327
|
+
let realHere = here;
|
|
138328
|
+
try {
|
|
138329
|
+
realHere = (0, import_node_path8.dirname)((0, import_node_fs7.realpathSync)(scriptPath));
|
|
138330
|
+
} catch {
|
|
138331
|
+
}
|
|
138332
|
+
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, "..")];
|
|
138333
|
+
for (const dir of searchDirs) {
|
|
138334
|
+
try {
|
|
138335
|
+
const v2 = (0, import_node_fs7.readFileSync)((0, import_node_path8.join)(dir, "version.txt"), "utf8").trim();
|
|
138336
|
+
if (v2) return v2;
|
|
138337
|
+
} catch {
|
|
138338
|
+
}
|
|
138339
|
+
}
|
|
138340
|
+
try {
|
|
138341
|
+
const pkg = JSON.parse((0, import_node_fs7.readFileSync)((0, import_node_path8.join)(here, "..", "package.json"), "utf8"));
|
|
138342
|
+
if (pkg.version) return `${pkg.version}+source`;
|
|
138343
|
+
} catch {
|
|
138344
|
+
}
|
|
138345
|
+
return "0.0.0+unknown";
|
|
138346
|
+
}
|
|
138347
|
+
|
|
138348
|
+
// src/api/routes/backup.routes.ts
|
|
138349
|
+
function readBody(c) {
|
|
138350
|
+
return new Promise((resolve4, reject) => {
|
|
138351
|
+
const chunks = [];
|
|
138352
|
+
c.env.incoming.on("data", (chunk) => chunks.push(chunk));
|
|
138353
|
+
c.env.incoming.on("end", () => {
|
|
138354
|
+
try {
|
|
138355
|
+
const raw2 = Buffer.concat(chunks).toString("utf-8");
|
|
138356
|
+
resolve4(raw2 ? JSON.parse(raw2) : {});
|
|
138357
|
+
} catch {
|
|
138358
|
+
reject(new Error("Invalid JSON body"));
|
|
138359
|
+
}
|
|
138360
|
+
});
|
|
138361
|
+
c.env.incoming.on("error", reject);
|
|
138362
|
+
});
|
|
138363
|
+
}
|
|
138364
|
+
var createBackupRoutes = (deps) => {
|
|
138365
|
+
const app = new Hono2();
|
|
138366
|
+
app.get("/export", (c) => {
|
|
138367
|
+
const repo = deps.projectsRepo();
|
|
138368
|
+
if (!repo) {
|
|
138369
|
+
return c.json({ error: "Project store is unavailable", code: "STORE_UNAVAILABLE" }, 503);
|
|
138370
|
+
}
|
|
138371
|
+
const projects = repo.listProjects().map((p2) => ({
|
|
138372
|
+
id: p2.id,
|
|
138373
|
+
path: p2.path,
|
|
138374
|
+
name: p2.name ?? null,
|
|
138375
|
+
createdAt: p2.createdAt,
|
|
138376
|
+
updatedAt: p2.updatedAt
|
|
138377
|
+
}));
|
|
138378
|
+
return c.json({
|
|
138379
|
+
manifest: {
|
|
138380
|
+
formatVersion: BACKUP_FORMAT_VERSION,
|
|
138381
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
138382
|
+
streamerVersion: getVersion(),
|
|
138383
|
+
sourceHost: (0, import_os5.hostname)(),
|
|
138384
|
+
// No endpoint here exports the API key. The flag is recorded so an
|
|
138385
|
+
// archive is self-describing about its own sensitivity rather than
|
|
138386
|
+
// requiring a reader to infer it.
|
|
138387
|
+
includesSecrets: false,
|
|
138388
|
+
counts: { projects: projects.length }
|
|
138389
|
+
},
|
|
138390
|
+
projects
|
|
138391
|
+
});
|
|
138392
|
+
});
|
|
138393
|
+
app.post("/restore", async (c) => {
|
|
138394
|
+
const repo = deps.projectsRepo();
|
|
138395
|
+
if (!repo) {
|
|
138396
|
+
return c.json({ error: "Project store is unavailable", code: "STORE_UNAVAILABLE" }, 503);
|
|
138397
|
+
}
|
|
138398
|
+
let body;
|
|
138399
|
+
try {
|
|
138400
|
+
body = await readBody(c);
|
|
138401
|
+
} catch {
|
|
138402
|
+
return c.json({ error: "Invalid JSON body", code: "INVALID_BODY" }, 400);
|
|
138403
|
+
}
|
|
138404
|
+
let archive;
|
|
138405
|
+
try {
|
|
138406
|
+
archive = validateArchive(body.archive);
|
|
138407
|
+
} catch (err) {
|
|
138408
|
+
if (err instanceof BackupError) {
|
|
138409
|
+
return c.json({ error: err.message, code: err.code }, 400);
|
|
138410
|
+
}
|
|
138411
|
+
throw err;
|
|
138412
|
+
}
|
|
138413
|
+
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 })) : [];
|
|
138414
|
+
const incoming = rules.length > 0 ? remapPaths(archive.projects, rules) : archive.projects;
|
|
138415
|
+
const existing = repo.listProjects().map((p2) => ({ id: p2.id, path: p2.path }));
|
|
138416
|
+
const plan = planRestore(incoming, existing);
|
|
138417
|
+
const summary = {
|
|
138418
|
+
create: plan.create.length,
|
|
138419
|
+
update: plan.update.length,
|
|
138420
|
+
conflict: plan.conflict.length
|
|
138421
|
+
};
|
|
138422
|
+
if (body.apply !== true) {
|
|
138423
|
+
return c.json({ applied: false, summary, plan });
|
|
138424
|
+
}
|
|
138425
|
+
if (plan.conflict.length > 0) {
|
|
138426
|
+
return c.json(
|
|
138427
|
+
{
|
|
138428
|
+
error: "Restore has unresolved conflicts",
|
|
138429
|
+
code: "RESTORE_CONFLICT",
|
|
138430
|
+
summary,
|
|
138431
|
+
plan
|
|
138432
|
+
},
|
|
138433
|
+
409
|
|
138434
|
+
);
|
|
138435
|
+
}
|
|
138436
|
+
let applied = 0;
|
|
138437
|
+
for (const p2 of [...plan.create, ...plan.update]) {
|
|
138438
|
+
try {
|
|
138439
|
+
repo.upsertProjectByPath(p2.path, { name: p2.name });
|
|
138440
|
+
applied++;
|
|
138441
|
+
} catch {
|
|
138442
|
+
}
|
|
138443
|
+
}
|
|
138444
|
+
return c.json({ applied: true, summary, appliedCount: applied });
|
|
138445
|
+
});
|
|
138446
|
+
return app;
|
|
138447
|
+
};
|
|
138448
|
+
|
|
138234
138449
|
// src/api/routes/browse.routes.ts
|
|
138235
138450
|
var ALREADY_HANDLED = 597;
|
|
138236
138451
|
var alreadyHandled = () => new Response(null, { status: ALREADY_HANDLED });
|
|
@@ -138413,38 +138628,251 @@ var createDeviceRoutes = (deps) => {
|
|
|
138413
138628
|
return app;
|
|
138414
138629
|
};
|
|
138415
138630
|
|
|
138416
|
-
// src/
|
|
138417
|
-
var
|
|
138418
|
-
|
|
138419
|
-
|
|
138420
|
-
|
|
138421
|
-
|
|
138422
|
-
|
|
138423
|
-
|
|
138424
|
-
|
|
138425
|
-
|
|
138426
|
-
|
|
138427
|
-
|
|
138428
|
-
|
|
138429
|
-
|
|
138430
|
-
|
|
138431
|
-
|
|
138631
|
+
// src/api/routes/diagnostics.routes.ts
|
|
138632
|
+
var import_fs14 = require("fs");
|
|
138633
|
+
|
|
138634
|
+
// src/platform.ts
|
|
138635
|
+
var import_child_process = require("child_process");
|
|
138636
|
+
var import_fs13 = require("fs");
|
|
138637
|
+
var import_os6 = require("os");
|
|
138638
|
+
var import_path13 = require("path");
|
|
138639
|
+
var isWindows2 = (0, import_os6.platform)() === "win32";
|
|
138640
|
+
var _claudeExe;
|
|
138641
|
+
function resolveClaudeExe() {
|
|
138642
|
+
if (_claudeExe !== void 0) return _claudeExe;
|
|
138643
|
+
if (isWindows2) {
|
|
138644
|
+
try {
|
|
138645
|
+
const found = (0, import_child_process.execFileSync)("where.exe", ["claude"], {
|
|
138646
|
+
encoding: "utf-8",
|
|
138647
|
+
windowsHide: true,
|
|
138648
|
+
timeout: 3e3
|
|
138649
|
+
}).trim().split("\n")[0].trim();
|
|
138650
|
+
if (found) {
|
|
138651
|
+
_claudeExe = found;
|
|
138652
|
+
return _claudeExe;
|
|
138653
|
+
}
|
|
138654
|
+
} catch {
|
|
138655
|
+
}
|
|
138656
|
+
const candidates = [
|
|
138657
|
+
(0, import_path13.join)((0, import_os6.homedir)(), ".local", "bin", "claude.exe"),
|
|
138658
|
+
(0, import_path13.join)(
|
|
138659
|
+
process.env.LOCALAPPDATA ?? (0, import_path13.join)((0, import_os6.homedir)(), "AppData", "Local"),
|
|
138660
|
+
"Microsoft",
|
|
138661
|
+
"WindowsApps",
|
|
138662
|
+
"claude.exe"
|
|
138663
|
+
)
|
|
138664
|
+
];
|
|
138665
|
+
for (const p2 of candidates) {
|
|
138666
|
+
if ((0, import_fs13.existsSync)(p2)) {
|
|
138667
|
+
_claudeExe = p2;
|
|
138668
|
+
return _claudeExe;
|
|
138669
|
+
}
|
|
138670
|
+
}
|
|
138671
|
+
} else {
|
|
138672
|
+
try {
|
|
138673
|
+
const found = (0, import_child_process.execFileSync)("/usr/bin/which", ["claude"], {
|
|
138674
|
+
encoding: "utf-8",
|
|
138675
|
+
timeout: 3e3
|
|
138676
|
+
}).trim().split("\n")[0].trim();
|
|
138677
|
+
if (found && (0, import_fs13.existsSync)(found)) {
|
|
138678
|
+
_claudeExe = found;
|
|
138679
|
+
return _claudeExe;
|
|
138680
|
+
}
|
|
138681
|
+
} catch {
|
|
138682
|
+
}
|
|
138683
|
+
const candidates = [
|
|
138684
|
+
"/opt/homebrew/bin/claude",
|
|
138685
|
+
"/usr/local/bin/claude",
|
|
138686
|
+
(0, import_path13.join)((0, import_os6.homedir)(), ".local", "bin", "claude")
|
|
138687
|
+
];
|
|
138688
|
+
for (const p2 of candidates) {
|
|
138689
|
+
if ((0, import_fs13.existsSync)(p2)) {
|
|
138690
|
+
_claudeExe = p2;
|
|
138691
|
+
return _claudeExe;
|
|
138692
|
+
}
|
|
138693
|
+
}
|
|
138432
138694
|
}
|
|
138433
|
-
|
|
138434
|
-
|
|
138695
|
+
_claudeExe = "claude";
|
|
138696
|
+
return _claudeExe;
|
|
138697
|
+
}
|
|
138698
|
+
var _codexExe;
|
|
138699
|
+
function resolveCodexExe() {
|
|
138700
|
+
if (_codexExe !== void 0) return _codexExe;
|
|
138701
|
+
if (isWindows2) {
|
|
138435
138702
|
try {
|
|
138436
|
-
const
|
|
138437
|
-
|
|
138703
|
+
const found = (0, import_child_process.execFileSync)("where.exe", ["codex"], {
|
|
138704
|
+
encoding: "utf-8",
|
|
138705
|
+
windowsHide: true,
|
|
138706
|
+
timeout: 3e3
|
|
138707
|
+
}).trim().split("\n")[0].trim();
|
|
138708
|
+
if (found) {
|
|
138709
|
+
_codexExe = found;
|
|
138710
|
+
return _codexExe;
|
|
138711
|
+
}
|
|
138438
138712
|
} catch {
|
|
138439
138713
|
}
|
|
138714
|
+
const candidates = [
|
|
138715
|
+
(0, import_path13.join)((0, import_os6.homedir)(), ".local", "bin", "codex.exe"),
|
|
138716
|
+
(0, import_path13.join)(
|
|
138717
|
+
process.env.LOCALAPPDATA ?? (0, import_path13.join)((0, import_os6.homedir)(), "AppData", "Local"),
|
|
138718
|
+
"Microsoft",
|
|
138719
|
+
"WindowsApps",
|
|
138720
|
+
"codex.exe"
|
|
138721
|
+
)
|
|
138722
|
+
];
|
|
138723
|
+
for (const p2 of candidates) {
|
|
138724
|
+
if ((0, import_fs13.existsSync)(p2)) {
|
|
138725
|
+
_codexExe = p2;
|
|
138726
|
+
return _codexExe;
|
|
138727
|
+
}
|
|
138728
|
+
}
|
|
138729
|
+
} else {
|
|
138730
|
+
try {
|
|
138731
|
+
const found = (0, import_child_process.execFileSync)("/usr/bin/which", ["codex"], {
|
|
138732
|
+
encoding: "utf-8",
|
|
138733
|
+
timeout: 3e3
|
|
138734
|
+
}).trim().split("\n")[0].trim();
|
|
138735
|
+
if (found && (0, import_fs13.existsSync)(found)) {
|
|
138736
|
+
_codexExe = found;
|
|
138737
|
+
return _codexExe;
|
|
138738
|
+
}
|
|
138739
|
+
} catch {
|
|
138740
|
+
}
|
|
138741
|
+
const candidates = [
|
|
138742
|
+
"/opt/homebrew/bin/codex",
|
|
138743
|
+
"/usr/local/bin/codex",
|
|
138744
|
+
(0, import_path13.join)((0, import_os6.homedir)(), ".local", "bin", "codex")
|
|
138745
|
+
];
|
|
138746
|
+
for (const p2 of candidates) {
|
|
138747
|
+
if ((0, import_fs13.existsSync)(p2)) {
|
|
138748
|
+
_codexExe = p2;
|
|
138749
|
+
return _codexExe;
|
|
138750
|
+
}
|
|
138751
|
+
}
|
|
138752
|
+
}
|
|
138753
|
+
_codexExe = "codex";
|
|
138754
|
+
return _codexExe;
|
|
138755
|
+
}
|
|
138756
|
+
|
|
138757
|
+
// src/services/diagnostics/diagnostics.ts
|
|
138758
|
+
var DIAGNOSTICS_CONTRACT_VERSION = 1;
|
|
138759
|
+
function redactPath(path2) {
|
|
138760
|
+
if (!path2) return null;
|
|
138761
|
+
const parts = path2.split(/[/\\]/).filter(Boolean);
|
|
138762
|
+
if (parts.length <= 2) return parts.join("/");
|
|
138763
|
+
return `\u2026/${parts.slice(-2).join("/")}`;
|
|
138764
|
+
}
|
|
138765
|
+
function worstStatus(checks) {
|
|
138766
|
+
const rank = { ok: 0, unknown: 1, degraded: 2, failed: 3 };
|
|
138767
|
+
return checks.reduce(
|
|
138768
|
+
(worst, c) => rank[c.status] > rank[worst] ? c.status : worst,
|
|
138769
|
+
"ok"
|
|
138770
|
+
);
|
|
138771
|
+
}
|
|
138772
|
+
function buildReport(checks, now = /* @__PURE__ */ new Date()) {
|
|
138773
|
+
return {
|
|
138774
|
+
contractVersion: DIAGNOSTICS_CONTRACT_VERSION,
|
|
138775
|
+
generatedAt: now.toISOString(),
|
|
138776
|
+
overall: worstStatus(checks),
|
|
138777
|
+
checks
|
|
138778
|
+
};
|
|
138779
|
+
}
|
|
138780
|
+
var SECRET_KEY_RE = /(key|token|secret|password|passwd|credential|authorization|cookie)/i;
|
|
138781
|
+
function redactValue(value) {
|
|
138782
|
+
if (Array.isArray(value)) {
|
|
138783
|
+
return value.map((v2) => redactValue(v2));
|
|
138784
|
+
}
|
|
138785
|
+
if (value && typeof value === "object") {
|
|
138786
|
+
const out = {};
|
|
138787
|
+
for (const [k2, v2] of Object.entries(value)) {
|
|
138788
|
+
out[k2] = SECRET_KEY_RE.test(k2) ? "[redacted]" : redactValue(v2);
|
|
138789
|
+
}
|
|
138790
|
+
return out;
|
|
138440
138791
|
}
|
|
138792
|
+
return value;
|
|
138793
|
+
}
|
|
138794
|
+
|
|
138795
|
+
// src/api/routes/diagnostics.routes.ts
|
|
138796
|
+
function providerCheck(name, resolve4) {
|
|
138441
138797
|
try {
|
|
138442
|
-
const
|
|
138443
|
-
|
|
138798
|
+
const exe = resolve4();
|
|
138799
|
+
return {
|
|
138800
|
+
id: `provider:${name}`,
|
|
138801
|
+
status: "ok",
|
|
138802
|
+
summary: `${name} CLI is installed.`,
|
|
138803
|
+
remediation: "NONE",
|
|
138804
|
+
detail: { location: redactPath(exe) }
|
|
138805
|
+
};
|
|
138444
138806
|
} catch {
|
|
138807
|
+
return {
|
|
138808
|
+
id: `provider:${name}`,
|
|
138809
|
+
status: "failed",
|
|
138810
|
+
summary: `${name} CLI could not be located. Sessions for this provider cannot start.`,
|
|
138811
|
+
remediation: "PROVIDER_NOT_INSTALLED"
|
|
138812
|
+
};
|
|
138445
138813
|
}
|
|
138446
|
-
return "0.0.0+unknown";
|
|
138447
138814
|
}
|
|
138815
|
+
var createDiagnosticsRoutes = (deps) => {
|
|
138816
|
+
const app = new Hono2();
|
|
138817
|
+
app.get("/", (c) => {
|
|
138818
|
+
const checks = [];
|
|
138819
|
+
checks.push({
|
|
138820
|
+
id: "streamer",
|
|
138821
|
+
status: "ok",
|
|
138822
|
+
summary: "Streamer is running.",
|
|
138823
|
+
remediation: "NONE",
|
|
138824
|
+
detail: { version: getVersion(), uptimeSeconds: Math.floor(process.uptime()) }
|
|
138825
|
+
});
|
|
138826
|
+
checks.push(providerCheck("claude-code", resolveClaudeExe));
|
|
138827
|
+
checks.push(providerCheck("codex-cli", resolveCodexExe));
|
|
138828
|
+
const cacheAlert = deps.cacheMonitor()?.healthzField();
|
|
138829
|
+
checks.push(
|
|
138830
|
+
cacheAlert ? {
|
|
138831
|
+
id: "cache",
|
|
138832
|
+
status: "degraded",
|
|
138833
|
+
summary: "Conversation cache reported an integrity alert.",
|
|
138834
|
+
remediation: "CACHE_DEGRADED"
|
|
138835
|
+
} : {
|
|
138836
|
+
id: "cache",
|
|
138837
|
+
status: "ok",
|
|
138838
|
+
summary: "Conversation cache is healthy.",
|
|
138839
|
+
remediation: "NONE"
|
|
138840
|
+
}
|
|
138841
|
+
);
|
|
138842
|
+
let ptyOk = true;
|
|
138843
|
+
try {
|
|
138844
|
+
require.resolve("node-pty");
|
|
138845
|
+
} catch {
|
|
138846
|
+
ptyOk = false;
|
|
138847
|
+
}
|
|
138848
|
+
checks.push(
|
|
138849
|
+
ptyOk ? { id: "pty", status: "ok", summary: "PTY subsystem is available.", remediation: "NONE" } : {
|
|
138850
|
+
id: "pty",
|
|
138851
|
+
status: "failed",
|
|
138852
|
+
summary: "node-pty failed to load, so no managed session can start.",
|
|
138853
|
+
remediation: "PTY_UNAVAILABLE"
|
|
138854
|
+
}
|
|
138855
|
+
);
|
|
138856
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
138857
|
+
const claudeProjects = home ? `${home}/.claude/projects` : "";
|
|
138858
|
+
checks.push(
|
|
138859
|
+
claudeProjects && (0, import_fs14.existsSync)(claudeProjects) ? {
|
|
138860
|
+
id: "filesystem",
|
|
138861
|
+
status: "ok",
|
|
138862
|
+
summary: "Provider history directory is present.",
|
|
138863
|
+
remediation: "NONE",
|
|
138864
|
+
detail: { location: redactPath(claudeProjects) }
|
|
138865
|
+
} : {
|
|
138866
|
+
id: "filesystem",
|
|
138867
|
+
status: "degraded",
|
|
138868
|
+
summary: "Provider history directory was not found; history may be unavailable.",
|
|
138869
|
+
remediation: "FS_SCOPE_MISSING"
|
|
138870
|
+
}
|
|
138871
|
+
);
|
|
138872
|
+
return c.json(redactValue(buildReport(checks)));
|
|
138873
|
+
});
|
|
138874
|
+
return app;
|
|
138875
|
+
};
|
|
138448
138876
|
|
|
138449
138877
|
// src/api/routes/health.routes.ts
|
|
138450
138878
|
var createHealthRoutes = (deps) => {
|
|
@@ -138579,7 +139007,7 @@ function createLogsRoutes() {
|
|
|
138579
139007
|
// src/api/routes/misc.routes.ts
|
|
138580
139008
|
var import_node_child_process2 = require("child_process");
|
|
138581
139009
|
var import_node_crypto = require("crypto");
|
|
138582
|
-
var
|
|
139010
|
+
var import_os7 = require("os");
|
|
138583
139011
|
|
|
138584
139012
|
// src/db/repositories/push.repository.ts
|
|
138585
139013
|
var FAILURE_STREAK_LIMIT = 5;
|
|
@@ -138879,7 +139307,7 @@ var createMiscRoutes = (deps) => {
|
|
|
138879
139307
|
const ptyIds = deps.ptyAttachedIds();
|
|
138880
139308
|
return c.json({
|
|
138881
139309
|
version: getVersion(),
|
|
138882
|
-
machineName: (0,
|
|
139310
|
+
machineName: (0, import_os7.hostname)(),
|
|
138883
139311
|
platform: process.platform,
|
|
138884
139312
|
activeSessions: deps.sessionStore.list(ptyIds).filter((s3) => s3.status === "running").length,
|
|
138885
139313
|
publicUrl: deps.publicUrl,
|
|
@@ -139168,129 +139596,6 @@ var createProjectRoutes = (deps) => {
|
|
|
139168
139596
|
return app;
|
|
139169
139597
|
};
|
|
139170
139598
|
|
|
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
139599
|
// src/providers.ts
|
|
139295
139600
|
var CLAUDE_CODE_PROVIDER2 = "claude-code";
|
|
139296
139601
|
var CODEX_CLI_PROVIDER2 = "codex-cli";
|
|
@@ -139576,6 +139881,7 @@ var createHonoApp = (deps, upgradeWebSocket) => {
|
|
|
139576
139881
|
app.use("*", authMiddleware(deps));
|
|
139577
139882
|
app.onError(errorMiddleware);
|
|
139578
139883
|
app.route("/healthz", createHealthRoutes(deps));
|
|
139884
|
+
app.route("/api/diagnostics", createDiagnosticsRoutes(deps));
|
|
139579
139885
|
app.route("/", createMiscRoutes(deps));
|
|
139580
139886
|
app.route("/api/sessions", createSessionRoutes(deps));
|
|
139581
139887
|
app.route("/api/conversations", createConversationRoutes(deps));
|
|
@@ -139584,6 +139890,7 @@ var createHonoApp = (deps, upgradeWebSocket) => {
|
|
|
139584
139890
|
app.route("/api/projects", createProjectRoutes(deps));
|
|
139585
139891
|
app.route("/api/providers", createProviderRoutes());
|
|
139586
139892
|
app.route("/api/devices", createDeviceRoutes(deps));
|
|
139893
|
+
app.route("/api/backup", createBackupRoutes(deps));
|
|
139587
139894
|
app.route("/api/pair", createPairRoutes(deps));
|
|
139588
139895
|
app.route("/api", createBrowseRoutes(deps));
|
|
139589
139896
|
app.route("/", createScannerRoutes(deps));
|
|
@@ -139654,13 +139961,13 @@ init_claude_flags();
|
|
|
139654
139961
|
|
|
139655
139962
|
// src/conversation-cache.ts
|
|
139656
139963
|
var import_better_sqlite32 = __toESM(require("better-sqlite3"), 1);
|
|
139657
|
-
var
|
|
139964
|
+
var import_fs17 = require("fs");
|
|
139658
139965
|
var import_promises12 = require("fs/promises");
|
|
139659
139966
|
var import_path17 = require("path");
|
|
139660
139967
|
var import_promises13 = require("timers/promises");
|
|
139661
139968
|
|
|
139662
139969
|
// src/db/sqlite-migrate.ts
|
|
139663
|
-
var
|
|
139970
|
+
var import_fs15 = require("fs");
|
|
139664
139971
|
var import_path15 = require("path");
|
|
139665
139972
|
var import_url6 = require("url");
|
|
139666
139973
|
var import_meta2 = {};
|
|
@@ -139670,6 +139977,9 @@ function getMigrationsDir() {
|
|
|
139670
139977
|
}
|
|
139671
139978
|
return __dirname;
|
|
139672
139979
|
}
|
|
139980
|
+
function resolveMigrationsDir(name = "migrations") {
|
|
139981
|
+
return (0, import_path15.join)(getMigrationsDir(), name);
|
|
139982
|
+
}
|
|
139673
139983
|
var SCHEMA_MIGRATIONS_SQL = `
|
|
139674
139984
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
139675
139985
|
id TEXT PRIMARY KEY,
|
|
@@ -139678,8 +139988,8 @@ CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
|
139678
139988
|
`;
|
|
139679
139989
|
function runSqliteMigrations(db, migrationsDir) {
|
|
139680
139990
|
db.exec(SCHEMA_MIGRATIONS_SQL);
|
|
139681
|
-
const dir = migrationsDir ?? (
|
|
139682
|
-
const files = (0,
|
|
139991
|
+
const dir = migrationsDir ?? resolveMigrationsDir();
|
|
139992
|
+
const files = (0, import_fs15.readdirSync)(dir).filter((f2) => f2.endsWith(".sql")).sort();
|
|
139683
139993
|
const appliedRows = db.prepare("SELECT id FROM schema_migrations").all();
|
|
139684
139994
|
const appliedSet = new Set(appliedRows.map((r) => r.id));
|
|
139685
139995
|
const recordApplied = db.prepare("INSERT INTO schema_migrations (id, applied_at) VALUES (?, ?)");
|
|
@@ -139690,7 +140000,7 @@ function runSqliteMigrations(db, migrationsDir) {
|
|
|
139690
140000
|
skipped.push(file2);
|
|
139691
140001
|
continue;
|
|
139692
140002
|
}
|
|
139693
|
-
const sql = (0,
|
|
140003
|
+
const sql = (0, import_fs15.readFileSync)((0, import_path15.join)(dir, file2), "utf-8");
|
|
139694
140004
|
const tx = db.transaction(() => {
|
|
139695
140005
|
db.exec(sql);
|
|
139696
140006
|
recordApplied.run(file2, (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -139702,7 +140012,7 @@ function runSqliteMigrations(db, migrationsDir) {
|
|
|
139702
140012
|
}
|
|
139703
140013
|
|
|
139704
140014
|
// src/services/conversations/isAgentConversation.ts
|
|
139705
|
-
var
|
|
140015
|
+
var import_fs16 = require("fs");
|
|
139706
140016
|
var DEFAULT_AGENT_ENTRYPOINTS = /* @__PURE__ */ new Set(["sdk-cli", "claude-vscode"]);
|
|
139707
140017
|
var CHUNK_BYTES = 64 * 1024;
|
|
139708
140018
|
var ENTRYPOINT_PROBE = `"entrypoint":`;
|
|
@@ -139724,12 +140034,12 @@ function isAgentFile(filePath, entrypoints = DEFAULT_AGENT_ENTRYPOINTS) {
|
|
|
139724
140034
|
if (cached3 !== void 0) return cached3;
|
|
139725
140035
|
let fd;
|
|
139726
140036
|
try {
|
|
139727
|
-
fd = (0,
|
|
140037
|
+
fd = (0, import_fs16.openSync)(filePath, "r");
|
|
139728
140038
|
} catch {
|
|
139729
140039
|
return false;
|
|
139730
140040
|
}
|
|
139731
140041
|
try {
|
|
139732
|
-
const fileSize = (0,
|
|
140042
|
+
const fileSize = (0, import_fs16.statSync)(filePath).size;
|
|
139733
140043
|
if (fileSize === 0) {
|
|
139734
140044
|
fileDecisionCache.set(key, false);
|
|
139735
140045
|
return false;
|
|
@@ -139740,7 +140050,7 @@ function isAgentFile(filePath, entrypoints = DEFAULT_AGENT_ENTRYPOINTS) {
|
|
|
139740
140050
|
let carry = "";
|
|
139741
140051
|
while (offset < fileSize) {
|
|
139742
140052
|
const toRead = Math.min(CHUNK_BYTES, fileSize - offset);
|
|
139743
|
-
const got = (0,
|
|
140053
|
+
const got = (0, import_fs16.readSync)(fd, buf, 0, toRead, offset);
|
|
139744
140054
|
if (got <= 0) break;
|
|
139745
140055
|
const chunk = carry + buf.toString("utf8", 0, got);
|
|
139746
140056
|
for (const marker of markers) {
|
|
@@ -139761,7 +140071,7 @@ function isAgentFile(filePath, entrypoints = DEFAULT_AGENT_ENTRYPOINTS) {
|
|
|
139761
140071
|
} catch {
|
|
139762
140072
|
return false;
|
|
139763
140073
|
} finally {
|
|
139764
|
-
(0,
|
|
140074
|
+
(0, import_fs16.closeSync)(fd);
|
|
139765
140075
|
}
|
|
139766
140076
|
}
|
|
139767
140077
|
function parseAgentEntrypointsEnv(raw2) {
|
|
@@ -140336,7 +140646,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140336
140646
|
if (!fileState) return null;
|
|
140337
140647
|
let stat7;
|
|
140338
140648
|
try {
|
|
140339
|
-
stat7 = (0,
|
|
140649
|
+
stat7 = (0, import_fs17.statSync)(filePath);
|
|
140340
140650
|
} catch {
|
|
140341
140651
|
return null;
|
|
140342
140652
|
}
|
|
@@ -140357,17 +140667,17 @@ var ConversationCache = class _ConversationCache {
|
|
|
140357
140667
|
);
|
|
140358
140668
|
if (rows.length === 0) return { messages: [], total, fromIndex: from };
|
|
140359
140669
|
const messages = [];
|
|
140360
|
-
const fd = (0,
|
|
140670
|
+
const fd = (0, import_fs17.openSync)(filePath, "r");
|
|
140361
140671
|
try {
|
|
140362
140672
|
const state = initialConvState();
|
|
140363
140673
|
for (const row of rows) {
|
|
140364
140674
|
const buf = Buffer.alloc(row.byte_length);
|
|
140365
|
-
(0,
|
|
140675
|
+
(0, import_fs17.readSync)(fd, buf, 0, row.byte_length, row.byte_offset);
|
|
140366
140676
|
const msg = parseJsonlLine(buf.toString("utf-8"), state);
|
|
140367
140677
|
if (msg) messages.push(msg);
|
|
140368
140678
|
}
|
|
140369
140679
|
} finally {
|
|
140370
|
-
(0,
|
|
140680
|
+
(0, import_fs17.closeSync)(fd);
|
|
140371
140681
|
}
|
|
140372
140682
|
return { messages, total, fromIndex: from };
|
|
140373
140683
|
}
|
|
@@ -140395,14 +140705,14 @@ var ConversationCache = class _ConversationCache {
|
|
|
140395
140705
|
isAgentFileCached(filePath) {
|
|
140396
140706
|
let s3;
|
|
140397
140707
|
try {
|
|
140398
|
-
s3 = (0,
|
|
140708
|
+
s3 = (0, import_fs17.statSync)(filePath);
|
|
140399
140709
|
} catch {
|
|
140400
140710
|
return false;
|
|
140401
140711
|
}
|
|
140402
140712
|
return this.classifyAgentFile(filePath, s3.mtimeMs, s3.size);
|
|
140403
140713
|
}
|
|
140404
140714
|
static open(dbPath, tailSize = 10, migrationsDir, options) {
|
|
140405
|
-
(0,
|
|
140715
|
+
(0, import_fs17.mkdirSync)((0, import_path17.dirname)(dbPath), { recursive: true });
|
|
140406
140716
|
const db = new import_better_sqlite32.default(dbPath);
|
|
140407
140717
|
db.pragma("journal_mode = WAL");
|
|
140408
140718
|
db.pragma("foreign_keys = ON");
|
|
@@ -140589,7 +140899,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140589
140899
|
let mtimeMs = null;
|
|
140590
140900
|
let fileSize = null;
|
|
140591
140901
|
try {
|
|
140592
|
-
const s3 = (0,
|
|
140902
|
+
const s3 = (0, import_fs17.statSync)(m2.filePath);
|
|
140593
140903
|
mtimeMs = s3.mtimeMs;
|
|
140594
140904
|
fileSize = s3.size;
|
|
140595
140905
|
} catch {
|
|
@@ -140649,8 +140959,8 @@ var ConversationCache = class _ConversationCache {
|
|
|
140649
140959
|
let fileSize;
|
|
140650
140960
|
let fd;
|
|
140651
140961
|
try {
|
|
140652
|
-
fileSize = (0,
|
|
140653
|
-
fd = (0,
|
|
140962
|
+
fileSize = (0, import_fs17.statSync)(filePath).size;
|
|
140963
|
+
fd = (0, import_fs17.openSync)(filePath, "r");
|
|
140654
140964
|
} catch {
|
|
140655
140965
|
return false;
|
|
140656
140966
|
}
|
|
@@ -140663,7 +140973,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140663
140973
|
while (pos > 0 && lines.length < this.tailSize * 4) {
|
|
140664
140974
|
const toRead = Math.min(CHUNK, pos);
|
|
140665
140975
|
pos -= toRead;
|
|
140666
|
-
(0,
|
|
140976
|
+
(0, import_fs17.readSync)(fd, buf, 0, toRead, pos);
|
|
140667
140977
|
const chunk = buf.subarray(0, toRead).toString("utf8");
|
|
140668
140978
|
const combined = chunk + partial2;
|
|
140669
140979
|
const parts = combined.split("\n");
|
|
@@ -140674,7 +140984,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140674
140984
|
}
|
|
140675
140985
|
if (partial2) lines.push(partial2);
|
|
140676
140986
|
} finally {
|
|
140677
|
-
(0,
|
|
140987
|
+
(0, import_fs17.closeSync)(fd);
|
|
140678
140988
|
}
|
|
140679
140989
|
const msgs = [];
|
|
140680
140990
|
for (let i = 0; i < lines.length && msgs.length < this.tailSize; i++) {
|
|
@@ -140887,7 +141197,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140887
141197
|
* `handleGetConversation` can still serve the cached tail even when the
|
|
140888
141198
|
* JSONL has been deleted.
|
|
140889
141199
|
*/
|
|
140890
|
-
pruneGhostFiles(exists =
|
|
141200
|
+
pruneGhostFiles(exists = import_fs17.existsSync) {
|
|
140891
141201
|
const rows = this.stmts.allFilePaths.all();
|
|
140892
141202
|
const ghosts = [];
|
|
140893
141203
|
const prune = this.db.transaction((ids) => {
|
|
@@ -140942,7 +141252,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140942
141252
|
* Returns the removed IDs.
|
|
140943
141253
|
*/
|
|
140944
141254
|
reconcileDeletions(livePaths, opts) {
|
|
140945
|
-
const exists = opts?.exists ??
|
|
141255
|
+
const exists = opts?.exists ?? import_fs17.existsSync;
|
|
140946
141256
|
const rows = this.stmts.allFilePaths.all();
|
|
140947
141257
|
const removed = [];
|
|
140948
141258
|
const drop = this.db.transaction((ids) => {
|
|
@@ -140977,7 +141287,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
140977
141287
|
* reports drift for the CacheIntegrityMonitor to classify. `tailed` flags
|
|
140978
141288
|
* rows that still have cached history (which pruneGhostFiles would keep).
|
|
140979
141289
|
*/
|
|
140980
|
-
listMissingFiles(exists =
|
|
141290
|
+
listMissingFiles(exists = import_fs17.existsSync) {
|
|
140981
141291
|
const rows = this.stmts.allFilePathsWithTitle.all();
|
|
140982
141292
|
const missing = [];
|
|
140983
141293
|
for (const row of rows) {
|
|
@@ -141062,7 +141372,7 @@ function getDbConfig() {
|
|
|
141062
141372
|
}
|
|
141063
141373
|
|
|
141064
141374
|
// src/db/migrations.ts
|
|
141065
|
-
var
|
|
141375
|
+
var import_fs18 = require("fs");
|
|
141066
141376
|
var import_path18 = require("path");
|
|
141067
141377
|
var import_url7 = require("url");
|
|
141068
141378
|
var import_meta3 = {};
|
|
@@ -141085,10 +141395,10 @@ async function runMigrations2(pool2, migrationsDir) {
|
|
|
141085
141395
|
);
|
|
141086
141396
|
const appliedSet = new Set(applied.map((r) => r.name));
|
|
141087
141397
|
const dir = migrationsDir ?? (0, import_path18.join)(getMigrationsDir2(), "pg-migrations");
|
|
141088
|
-
const files = (0,
|
|
141398
|
+
const files = (0, import_fs18.readdirSync)(dir).filter((f2) => f2.endsWith(".sql")).sort();
|
|
141089
141399
|
for (const file2 of files) {
|
|
141090
141400
|
if (appliedSet.has(file2)) continue;
|
|
141091
|
-
const sql = (0,
|
|
141401
|
+
const sql = (0, import_fs18.readFileSync)((0, import_path18.join)(dir, file2), "utf-8");
|
|
141092
141402
|
await pool2.query(sql);
|
|
141093
141403
|
await pool2.query("INSERT INTO _migrations (name) VALUES ($1)", [file2]);
|
|
141094
141404
|
}
|
|
@@ -141179,6 +141489,7 @@ var ManagedSessionsRepository = class {
|
|
|
141179
141489
|
updateStatusStmt;
|
|
141180
141490
|
getStmt;
|
|
141181
141491
|
listNonTerminalStmt;
|
|
141492
|
+
listRecoverableStmt;
|
|
141182
141493
|
deleteStmt;
|
|
141183
141494
|
constructor(db) {
|
|
141184
141495
|
this.upsertStmt = db.prepare(`
|
|
@@ -141231,6 +141542,13 @@ var ManagedSessionsRepository = class {
|
|
|
141231
141542
|
WHERE completed_at IS NULL
|
|
141232
141543
|
ORDER BY started_at ASC
|
|
141233
141544
|
`);
|
|
141545
|
+
this.listRecoverableStmt = db.prepare(`
|
|
141546
|
+
SELECT * FROM managed_sessions
|
|
141547
|
+
WHERE (completed_at IS NULL OR status_source = 'shutdown')
|
|
141548
|
+
AND status_updated_at >= @since
|
|
141549
|
+
ORDER BY status_updated_at DESC
|
|
141550
|
+
LIMIT @limit
|
|
141551
|
+
`);
|
|
141234
141552
|
this.deleteStmt = db.prepare("DELETE FROM managed_sessions WHERE session_id = ?");
|
|
141235
141553
|
}
|
|
141236
141554
|
/** Record a session at spawn, or refresh every field of an existing row. */
|
|
@@ -141282,6 +141600,14 @@ var ManagedSessionsRepository = class {
|
|
|
141282
141600
|
listNonTerminal() {
|
|
141283
141601
|
return this.listNonTerminalStmt.all();
|
|
141284
141602
|
}
|
|
141603
|
+
/**
|
|
141604
|
+
* Rows a restart could bring back: still open, or closed by our own shutdown,
|
|
141605
|
+
* and touched no longer ago than `sinceMs`. Newest first, capped — the caller
|
|
141606
|
+
* decides which of these actually deserve rehydrating (`shouldRehydrate`).
|
|
141607
|
+
*/
|
|
141608
|
+
listRecoverable({ sinceMs, limit }) {
|
|
141609
|
+
return this.listRecoverableStmt.all({ since: sinceMs, limit });
|
|
141610
|
+
}
|
|
141285
141611
|
delete(sessionId) {
|
|
141286
141612
|
this.deleteStmt.run(sessionId);
|
|
141287
141613
|
}
|
|
@@ -141417,6 +141743,54 @@ var SessionsRepository = class {
|
|
|
141417
141743
|
}
|
|
141418
141744
|
};
|
|
141419
141745
|
|
|
141746
|
+
// src/db/runtime-store.ts
|
|
141747
|
+
var import_better_sqlite33 = __toESM(require("better-sqlite3"), 1);
|
|
141748
|
+
var RuntimeStore = class _RuntimeStore {
|
|
141749
|
+
constructor(db) {
|
|
141750
|
+
this.db = db;
|
|
141751
|
+
}
|
|
141752
|
+
db;
|
|
141753
|
+
static open(dbPath, migrationsDir) {
|
|
141754
|
+
const db = new import_better_sqlite33.default(dbPath);
|
|
141755
|
+
db.pragma("journal_mode = WAL");
|
|
141756
|
+
runSqliteMigrations(db, migrationsDir ?? resolveMigrationsDir("runtime-migrations"));
|
|
141757
|
+
return new _RuntimeStore(db);
|
|
141758
|
+
}
|
|
141759
|
+
getDatabase() {
|
|
141760
|
+
return this.db;
|
|
141761
|
+
}
|
|
141762
|
+
/**
|
|
141763
|
+
* One-time move of `managed_sessions` rows out of a pre-split `cache.db`.
|
|
141764
|
+
*
|
|
141765
|
+
* Non-destructive by design: the source table is left in place so an older
|
|
141766
|
+
* streamer rolled back onto the same machine still finds its registry. Runs
|
|
141767
|
+
* only when this file's table is empty, so a second boot is a no-op rather
|
|
141768
|
+
* than a re-copy that would resurrect rows deleted since.
|
|
141769
|
+
*
|
|
141770
|
+
* Returns the number of rows copied.
|
|
141771
|
+
*/
|
|
141772
|
+
importLegacyManagedSessions(source) {
|
|
141773
|
+
const existing = this.db.prepare("SELECT COUNT(*) AS n FROM managed_sessions").get();
|
|
141774
|
+
if (existing.n > 0) return 0;
|
|
141775
|
+
const hasTable = source.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'managed_sessions'").get();
|
|
141776
|
+
if (!hasTable) return 0;
|
|
141777
|
+
const rows = source.prepare("SELECT * FROM managed_sessions").all();
|
|
141778
|
+
if (rows.length === 0) return 0;
|
|
141779
|
+
const columns = Object.keys(rows[0]);
|
|
141780
|
+
const insert = this.db.prepare(
|
|
141781
|
+
`INSERT OR IGNORE INTO managed_sessions (${columns.join(", ")})
|
|
141782
|
+
VALUES (${columns.map((c) => `@${c}`).join(", ")})`
|
|
141783
|
+
);
|
|
141784
|
+
this.db.transaction((batch) => {
|
|
141785
|
+
for (const row of batch) insert.run(row);
|
|
141786
|
+
})(rows);
|
|
141787
|
+
return rows.length;
|
|
141788
|
+
}
|
|
141789
|
+
close() {
|
|
141790
|
+
this.db.close();
|
|
141791
|
+
}
|
|
141792
|
+
};
|
|
141793
|
+
|
|
141420
141794
|
// src/db/upload-records.ts
|
|
141421
141795
|
async function recordUpload(pool2, instanceId, row) {
|
|
141422
141796
|
if (!pool2) return;
|
|
@@ -141440,8 +141814,8 @@ async function recordUpload(pool2, instanceId, row) {
|
|
|
141440
141814
|
init_feature_flags();
|
|
141441
141815
|
|
|
141442
141816
|
// src/handlers/handleListProjects.ts
|
|
141443
|
-
var
|
|
141444
|
-
var
|
|
141817
|
+
var import_fs19 = require("fs");
|
|
141818
|
+
var import_os8 = require("os");
|
|
141445
141819
|
var import_path19 = require("path");
|
|
141446
141820
|
function decodeProjectPath(dirName) {
|
|
141447
141821
|
return dirName.replace(/-/g, "/");
|
|
@@ -141449,14 +141823,14 @@ function decodeProjectPath(dirName) {
|
|
|
141449
141823
|
function handleListProjects(url2, res) {
|
|
141450
141824
|
const limit = Math.max(1, parseInt(url2.searchParams.get("limit") ?? "50", 10) || 50);
|
|
141451
141825
|
const offset = Math.max(0, parseInt(url2.searchParams.get("offset") ?? "0", 10) || 0);
|
|
141452
|
-
const projectsDir = (0, import_path19.join)((0,
|
|
141826
|
+
const projectsDir = (0, import_path19.join)((0, import_os8.homedir)(), ".claude", "projects");
|
|
141453
141827
|
let entries;
|
|
141454
141828
|
try {
|
|
141455
|
-
entries = (0,
|
|
141829
|
+
entries = (0, import_fs19.readdirSync)(projectsDir).map((dirName) => {
|
|
141456
141830
|
const fullPath = (0, import_path19.join)(projectsDir, dirName);
|
|
141457
141831
|
let mtime = 0;
|
|
141458
141832
|
try {
|
|
141459
|
-
mtime = (0,
|
|
141833
|
+
mtime = (0, import_fs19.statSync)(fullPath).mtimeMs;
|
|
141460
141834
|
} catch {
|
|
141461
141835
|
}
|
|
141462
141836
|
const path2 = decodeProjectPath(dirName);
|
|
@@ -141492,21 +141866,21 @@ var import_path23 = require("path");
|
|
|
141492
141866
|
// src/codex-pty-runner.ts
|
|
141493
141867
|
var import_headless = __toESM(require_xterm_headless(), 1);
|
|
141494
141868
|
var import_crypto8 = require("crypto");
|
|
141495
|
-
var
|
|
141869
|
+
var import_fs21 = require("fs");
|
|
141496
141870
|
var import_path21 = require("path");
|
|
141497
141871
|
init_logger();
|
|
141498
141872
|
|
|
141499
141873
|
// src/services/questions/codexGateAnswers.ts
|
|
141500
|
-
var
|
|
141501
|
-
var
|
|
141874
|
+
var import_fs20 = require("fs");
|
|
141875
|
+
var import_os9 = require("os");
|
|
141502
141876
|
var import_path20 = require("path");
|
|
141503
141877
|
function gateAnswersPath() {
|
|
141504
|
-
const dir = process.env.THREADBASE_CONFIG_DIR ?? (0, import_path20.join)((0,
|
|
141878
|
+
const dir = process.env.THREADBASE_CONFIG_DIR ?? (0, import_path20.join)((0, import_os9.homedir)(), ".threadbase");
|
|
141505
141879
|
return (0, import_path20.join)(dir, "gate-answers.json");
|
|
141506
141880
|
}
|
|
141507
141881
|
function loadGateAnswers() {
|
|
141508
141882
|
try {
|
|
141509
|
-
const parsed = JSON.parse((0,
|
|
141883
|
+
const parsed = JSON.parse((0, import_fs20.readFileSync)(gateAnswersPath(), "utf-8"));
|
|
141510
141884
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
141511
141885
|
} catch {
|
|
141512
141886
|
return {};
|
|
@@ -141514,8 +141888,8 @@ function loadGateAnswers() {
|
|
|
141514
141888
|
}
|
|
141515
141889
|
function saveGateAnswer(key, value) {
|
|
141516
141890
|
const path2 = gateAnswersPath();
|
|
141517
|
-
(0,
|
|
141518
|
-
(0,
|
|
141891
|
+
(0, import_fs20.mkdirSync)((0, import_path20.dirname)(path2), { recursive: true });
|
|
141892
|
+
(0, import_fs20.writeFileSync)(path2, `${JSON.stringify({ ...loadGateAnswers(), [key]: value }, null, 2)}
|
|
141519
141893
|
`);
|
|
141520
141894
|
}
|
|
141521
141895
|
function rememberedGateDigit(gate) {
|
|
@@ -142191,7 +142565,7 @@ var CodexPtyRunner = class {
|
|
|
142191
142565
|
session.statusUpdatedAt = /* @__PURE__ */ new Date();
|
|
142192
142566
|
const elapsedMs = session.completedAt.getTime() - session.startedAt.getTime();
|
|
142193
142567
|
if (exitCode !== 0 && elapsedMs < 2e3 && session.lastOutput === "") {
|
|
142194
|
-
if (!(0,
|
|
142568
|
+
if (!(0, import_fs21.existsSync)(session.projectPath)) {
|
|
142195
142569
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
142196
142570
|
} else {
|
|
142197
142571
|
session.failureReason = `Codex process exited immediately (code ${exitCode}).`;
|
|
@@ -142230,7 +142604,7 @@ function stripAnsi(str) {
|
|
|
142230
142604
|
// src/pty-manager.ts
|
|
142231
142605
|
var import_headless2 = __toESM(require_xterm_headless(), 1);
|
|
142232
142606
|
var import_crypto9 = require("crypto");
|
|
142233
|
-
var
|
|
142607
|
+
var import_fs22 = require("fs");
|
|
142234
142608
|
var import_path22 = require("path");
|
|
142235
142609
|
init_claude_flags();
|
|
142236
142610
|
init_logger();
|
|
@@ -142438,6 +142812,12 @@ function detectShellPrompt(lines) {
|
|
|
142438
142812
|
return null;
|
|
142439
142813
|
}
|
|
142440
142814
|
|
|
142815
|
+
// src/utils/deriveSessionName.ts
|
|
142816
|
+
function deriveSessionName(firstMessageText) {
|
|
142817
|
+
const firstLine = firstMessageText.split("\n", 1)[0]?.trim() ?? "";
|
|
142818
|
+
return firstLine.slice(0, 80);
|
|
142819
|
+
}
|
|
142820
|
+
|
|
142441
142821
|
// src/pty-manager.ts
|
|
142442
142822
|
var OUTPUT_BUFFER_MAX2 = 65536;
|
|
142443
142823
|
var INPUT_HISTORY_MAX2 = 50;
|
|
@@ -142911,6 +143291,10 @@ var PTYManager = class {
|
|
|
142911
143291
|
if (session.inputHistory.length > INPUT_HISTORY_MAX2) {
|
|
142912
143292
|
session.inputHistory.shift();
|
|
142913
143293
|
}
|
|
143294
|
+
if (session.firstMessageText === void 0) {
|
|
143295
|
+
session.firstMessageText = text;
|
|
143296
|
+
session.sessionName = deriveSessionName(text);
|
|
143297
|
+
}
|
|
142914
143298
|
this.onUserMessage?.(session.id, text, ts2);
|
|
142915
143299
|
}
|
|
142916
143300
|
getSession(sessionId) {
|
|
@@ -143142,7 +143526,7 @@ var PTYManager = class {
|
|
|
143142
143526
|
session.statusUpdatedAt = /* @__PURE__ */ new Date();
|
|
143143
143527
|
const elapsedMs = session.completedAt.getTime() - session.startedAt.getTime();
|
|
143144
143528
|
if (exitCode !== 0 && elapsedMs < 2e3 && session.lastOutput === "") {
|
|
143145
|
-
if (!(0,
|
|
143529
|
+
if (!(0, import_fs22.existsSync)(session.projectPath)) {
|
|
143146
143530
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
143147
143531
|
} else {
|
|
143148
143532
|
session.failureReason = `Process exited immediately (code ${exitCode}). Check that the Claude binary is installed and accessible.`;
|
|
@@ -143176,7 +143560,9 @@ function toPublicSession2(s3) {
|
|
|
143176
143560
|
...s3.lastActivityAt != null && { lastActivityAt: s3.lastActivityAt },
|
|
143177
143561
|
...s3.statusSource != null && { statusSource: s3.statusSource },
|
|
143178
143562
|
...s3.statusUpdatedAt != null && { statusUpdatedAt: s3.statusUpdatedAt },
|
|
143179
|
-
...s3.filePath != null && { filePath: s3.filePath }
|
|
143563
|
+
...s3.filePath != null && { filePath: s3.filePath },
|
|
143564
|
+
...s3.sessionName != null && { sessionName: s3.sessionName },
|
|
143565
|
+
...s3.firstMessageText != null && { firstMessageText: s3.firstMessageText }
|
|
143180
143566
|
};
|
|
143181
143567
|
}
|
|
143182
143568
|
function stripAnsi2(str) {
|
|
@@ -143351,10 +143737,10 @@ var PairTokenStore = class {
|
|
|
143351
143737
|
|
|
143352
143738
|
// src/process-discovery.ts
|
|
143353
143739
|
var import_child_process3 = require("child_process");
|
|
143354
|
-
var
|
|
143740
|
+
var import_os10 = require("os");
|
|
143355
143741
|
var import_path24 = require("path");
|
|
143356
143742
|
async function discoverClaudeProcesses() {
|
|
143357
|
-
if ((0,
|
|
143743
|
+
if ((0, import_os10.platform)() === "win32") return discoverWindows();
|
|
143358
143744
|
return discoverUnix();
|
|
143359
143745
|
}
|
|
143360
143746
|
var CLAUDE_CLI_SCRIPT = /claude-code[\\/](?:cli|index)\.(?:js|mjs|cjs)$/i;
|
|
@@ -143494,7 +143880,7 @@ async function getProcessArgsUnix(pid) {
|
|
|
143494
143880
|
async function getProcessArgs(pid) {
|
|
143495
143881
|
if (!Number.isInteger(pid) || pid < 1) return "";
|
|
143496
143882
|
try {
|
|
143497
|
-
if ((0,
|
|
143883
|
+
if ((0, import_os10.platform)() === "win32") {
|
|
143498
143884
|
const info = await getProcessInfoWindows(pid);
|
|
143499
143885
|
return info?.args ?? "";
|
|
143500
143886
|
}
|
|
@@ -143645,19 +144031,19 @@ function setCacheMetadata(repo, key, value) {
|
|
|
143645
144031
|
|
|
143646
144032
|
// src/services/cache-integrity/cacheIntegrityMonitor.ts
|
|
143647
144033
|
var import_crypto11 = require("crypto");
|
|
143648
|
-
var
|
|
144034
|
+
var import_fs25 = require("fs");
|
|
143649
144035
|
|
|
143650
144036
|
// src/services/cache-integrity/alertStore.ts
|
|
143651
|
-
var
|
|
143652
|
-
var
|
|
144037
|
+
var import_fs23 = require("fs");
|
|
144038
|
+
var import_os11 = require("os");
|
|
143653
144039
|
var import_path25 = require("path");
|
|
143654
144040
|
function alertStatePath() {
|
|
143655
|
-
const dir = process.env.THREADBASE_CONFIG_DIR ?? (0, import_path25.join)((0,
|
|
144041
|
+
const dir = process.env.THREADBASE_CONFIG_DIR ?? (0, import_path25.join)((0, import_os11.homedir)(), ".threadbase");
|
|
143656
144042
|
return (0, import_path25.join)(dir, "cache-alert.json");
|
|
143657
144043
|
}
|
|
143658
144044
|
function loadAlertState() {
|
|
143659
144045
|
try {
|
|
143660
|
-
const parsed = JSON.parse((0,
|
|
144046
|
+
const parsed = JSON.parse((0, import_fs23.readFileSync)(alertStatePath(), "utf-8"));
|
|
143661
144047
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
143662
144048
|
} catch {
|
|
143663
144049
|
return {};
|
|
@@ -143665,13 +144051,13 @@ function loadAlertState() {
|
|
|
143665
144051
|
}
|
|
143666
144052
|
function saveAlertState(state) {
|
|
143667
144053
|
const path2 = alertStatePath();
|
|
143668
|
-
(0,
|
|
143669
|
-
(0,
|
|
144054
|
+
(0, import_fs23.mkdirSync)((0, import_path25.dirname)(path2), { recursive: true });
|
|
144055
|
+
(0, import_fs23.writeFileSync)(path2, `${JSON.stringify(state, null, 2)}
|
|
143670
144056
|
`);
|
|
143671
144057
|
}
|
|
143672
144058
|
|
|
143673
144059
|
// src/services/cache-integrity/backup.ts
|
|
143674
|
-
var
|
|
144060
|
+
var import_fs24 = require("fs");
|
|
143675
144061
|
var import_path26 = require("path");
|
|
143676
144062
|
var DEFAULT_RETAIN = 3;
|
|
143677
144063
|
function retainCount() {
|
|
@@ -143684,16 +144070,16 @@ function timestamp(d) {
|
|
|
143684
144070
|
}
|
|
143685
144071
|
async function backupCacheDb(db, cacheDir) {
|
|
143686
144072
|
const backupsDir = (0, import_path26.join)(cacheDir, "backups");
|
|
143687
|
-
(0,
|
|
144073
|
+
(0, import_fs24.mkdirSync)(backupsDir, { recursive: true });
|
|
143688
144074
|
const destPath = (0, import_path26.join)(backupsDir, `cache-${timestamp(/* @__PURE__ */ new Date())}.db`);
|
|
143689
144075
|
await db.backup(destPath);
|
|
143690
144076
|
const retain = retainCount();
|
|
143691
|
-
const backups = (0,
|
|
144077
|
+
const backups = (0, import_fs24.readdirSync)(backupsDir).filter((f2) => f2.startsWith("cache-") && f2.endsWith(".db")).map((f2) => {
|
|
143692
144078
|
const full = (0, import_path26.join)(backupsDir, f2);
|
|
143693
|
-
return { full, mtime: (0,
|
|
144079
|
+
return { full, mtime: (0, import_fs24.statSync)(full).mtimeMs };
|
|
143694
144080
|
}).sort((a, b2) => b2.mtime - a.mtime);
|
|
143695
144081
|
for (const stale of backups.slice(retain)) {
|
|
143696
|
-
if ((0,
|
|
144082
|
+
if ((0, import_fs24.existsSync)(stale.full)) (0, import_fs24.unlinkSync)(stale.full);
|
|
143697
144083
|
}
|
|
143698
144084
|
return destPath;
|
|
143699
144085
|
}
|
|
@@ -143790,7 +144176,7 @@ var CacheIntegrityMonitor = class {
|
|
|
143790
144176
|
* the pending record, back up on high severity, and broadcast the alert.
|
|
143791
144177
|
*/
|
|
143792
144178
|
async runDetection(detectedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
143793
|
-
const all = this.cache.listMissingFiles(
|
|
144179
|
+
const all = this.cache.listMissingFiles(import_fs25.existsSync);
|
|
143794
144180
|
const missing = all.filter((m2) => !this.ignoredIds.has(m2.id));
|
|
143795
144181
|
if (missing.length === 0) {
|
|
143796
144182
|
if (this._pending) {
|
|
@@ -143894,7 +144280,7 @@ var CacheIntegrityMonitor = class {
|
|
|
143894
144280
|
case "prune_all": {
|
|
143895
144281
|
await this.ensureBackup(pending);
|
|
143896
144282
|
const backupPath = pending.backupPath;
|
|
143897
|
-
const stillMissing = pending.missing.filter((m2) => !(0,
|
|
144283
|
+
const stillMissing = pending.missing.filter((m2) => !(0, import_fs25.existsSync)(m2.filePath)).map((m2) => m2.id);
|
|
143898
144284
|
const pruned = this.cache.dropRowsById(stillMissing);
|
|
143899
144285
|
this.applyDeferredUnlinks();
|
|
143900
144286
|
this.clearPending();
|
|
@@ -143955,7 +144341,7 @@ var CacheIntegrityMonitor = class {
|
|
|
143955
144341
|
};
|
|
143956
144342
|
|
|
143957
144343
|
// src/services/conversations/conversationWatcher.ts
|
|
143958
|
-
var
|
|
144344
|
+
var import_fs26 = require("fs");
|
|
143959
144345
|
var import_promises14 = require("fs/promises");
|
|
143960
144346
|
var ConversationWatcher = class {
|
|
143961
144347
|
files = /* @__PURE__ */ new Map();
|
|
@@ -143981,7 +144367,7 @@ var ConversationWatcher = class {
|
|
|
143981
144367
|
if (this.files.has(key)) return;
|
|
143982
144368
|
let offset;
|
|
143983
144369
|
try {
|
|
143984
|
-
offset = (0,
|
|
144370
|
+
offset = (0, import_fs26.statSync)(filePath).size;
|
|
143985
144371
|
} catch {
|
|
143986
144372
|
offset = 0;
|
|
143987
144373
|
}
|
|
@@ -144163,14 +144549,14 @@ function findSearchTarget(messages, query) {
|
|
|
144163
144549
|
}
|
|
144164
144550
|
|
|
144165
144551
|
// src/services/conversations/pruneAgentConversations.ts
|
|
144166
|
-
var
|
|
144552
|
+
var import_fs27 = require("fs");
|
|
144167
144553
|
function pruneAgentConversations(cache) {
|
|
144168
144554
|
const db = cache.getDatabase();
|
|
144169
144555
|
const rows = db.prepare("SELECT id, file_path FROM conversation_meta").all();
|
|
144170
144556
|
let pruned = 0;
|
|
144171
144557
|
let missing = 0;
|
|
144172
144558
|
for (const row of rows) {
|
|
144173
|
-
if (!(0,
|
|
144559
|
+
if (!(0, import_fs27.existsSync)(row.file_path)) {
|
|
144174
144560
|
missing += 1;
|
|
144175
144561
|
continue;
|
|
144176
144562
|
}
|
|
@@ -144491,22 +144877,22 @@ function refreshConversationCache(deps) {
|
|
|
144491
144877
|
}
|
|
144492
144878
|
|
|
144493
144879
|
// src/services/conversations/shouldRefreshProjectsFromHdd.ts
|
|
144494
|
-
var
|
|
144495
|
-
var
|
|
144880
|
+
var import_fs28 = require("fs");
|
|
144881
|
+
var import_os12 = require("os");
|
|
144496
144882
|
var import_path27 = require("path");
|
|
144497
|
-
var DEFAULT_PROJECTS_DIR = (0, import_path27.join)((0,
|
|
144883
|
+
var DEFAULT_PROJECTS_DIR = (0, import_path27.join)((0, import_os12.homedir)(), ".claude", "projects");
|
|
144498
144884
|
function maxProjectsTreeMtimeMs(projectsDir) {
|
|
144499
144885
|
let maxMs;
|
|
144500
144886
|
try {
|
|
144501
|
-
maxMs = (0,
|
|
144887
|
+
maxMs = (0, import_fs28.statSync)(projectsDir).mtimeMs;
|
|
144502
144888
|
} catch {
|
|
144503
144889
|
return null;
|
|
144504
144890
|
}
|
|
144505
144891
|
try {
|
|
144506
|
-
for (const ent of (0,
|
|
144892
|
+
for (const ent of (0, import_fs28.readdirSync)(projectsDir, { withFileTypes: true })) {
|
|
144507
144893
|
if (!ent.isDirectory()) continue;
|
|
144508
144894
|
try {
|
|
144509
|
-
const childMs = (0,
|
|
144895
|
+
const childMs = (0, import_fs28.statSync)((0, import_path27.join)(projectsDir, ent.name)).mtimeMs;
|
|
144510
144896
|
if (childMs > maxMs) maxMs = childMs;
|
|
144511
144897
|
} catch {
|
|
144512
144898
|
}
|
|
@@ -144734,7 +145120,8 @@ function contentStateForSession(args) {
|
|
|
144734
145120
|
status,
|
|
144735
145121
|
startedAt: args.startedAtOverride ?? args.session.startedAt.getTime(),
|
|
144736
145122
|
lastOutput: truncateLastOutput(args.session.lastOutput ?? ""),
|
|
144737
|
-
...args.serverLabel != null && { serverLabel: args.serverLabel }
|
|
145123
|
+
...args.serverLabel != null && { serverLabel: args.serverLabel },
|
|
145124
|
+
...args.session.sessionName != null && { sessionName: args.session.sessionName }
|
|
144738
145125
|
};
|
|
144739
145126
|
}
|
|
144740
145127
|
var LiveActivityNotifier = class {
|
|
@@ -144747,14 +145134,17 @@ var LiveActivityNotifier = class {
|
|
|
144747
145134
|
serverId;
|
|
144748
145135
|
serverLabel;
|
|
144749
145136
|
/**
|
|
144750
|
-
*
|
|
145137
|
+
* Sessions with a currently open (pushed) activity.
|
|
144751
145138
|
*
|
|
144752
|
-
*
|
|
144753
|
-
*
|
|
144754
|
-
*
|
|
144755
|
-
*
|
|
145139
|
+
* An activity opens on a `waiting_input → running` edge (the user sent a
|
|
145140
|
+
* prompt) and closes on the matching `running → waiting_input` edge (the
|
|
145141
|
+
* response, including any sub-agents, finished) — so this set is what makes
|
|
145142
|
+
* the notifier per-turn rather than per-session. A session's very first
|
|
145143
|
+
* `running` (right after spawn, before any user prompt) has no prior
|
|
145144
|
+
* `waiting_input` and therefore no edge, so it never opens an activity —
|
|
145145
|
+
* this is what keeps a fresh/idle session from pushing anything.
|
|
144756
145146
|
*/
|
|
144757
|
-
|
|
145147
|
+
openActivity = /* @__PURE__ */ new Map();
|
|
144758
145148
|
/**
|
|
144759
145149
|
* React to a session status change.
|
|
144760
145150
|
*
|
|
@@ -144762,34 +145152,22 @@ var LiveActivityNotifier = class {
|
|
|
144762
145152
|
* transition, so this returns a promise the caller may ignore and every error
|
|
144763
145153
|
* is logged rather than propagated.
|
|
144764
145154
|
*/
|
|
144765
|
-
async onStatusChange(session) {
|
|
145155
|
+
async onStatusChange(session, previousStatus) {
|
|
144766
145156
|
const status = toLiveActivityStatus(session.status);
|
|
144767
145157
|
try {
|
|
144768
145158
|
if (!status) {
|
|
144769
|
-
await this.endFor(session);
|
|
145159
|
+
if (this.openActivity.has(session.id)) await this.endFor(session);
|
|
144770
145160
|
return;
|
|
144771
145161
|
}
|
|
144772
|
-
if (
|
|
144773
|
-
|
|
144774
|
-
|
|
144775
|
-
|
|
144776
|
-
|
|
144777
|
-
|
|
144778
|
-
|
|
144779
|
-
const outcome = await this.sender.send({
|
|
144780
|
-
sessionId: session.id,
|
|
144781
|
-
event: "update",
|
|
144782
|
-
contentState
|
|
144783
|
-
});
|
|
144784
|
-
this.lastPushed.set(session.id, status);
|
|
144785
|
-
if (outcome.attempted > 0) {
|
|
144786
|
-
log4.info("live_activity.updated", {
|
|
144787
|
-
event: "live_activity.updated",
|
|
144788
|
-
sessionId: session.id,
|
|
144789
|
-
status,
|
|
144790
|
-
...outcome
|
|
144791
|
-
});
|
|
145162
|
+
if (status === "running" && previousStatus === "waiting_input") {
|
|
145163
|
+
await this.startTurn(session);
|
|
145164
|
+
return;
|
|
145165
|
+
}
|
|
145166
|
+
if (status === "waiting_input" && previousStatus === "running") {
|
|
145167
|
+
if (this.openActivity.has(session.id)) await this.endFor(session);
|
|
145168
|
+
return;
|
|
144792
145169
|
}
|
|
145170
|
+
await this.maybeSendName(session);
|
|
144793
145171
|
} catch (err) {
|
|
144794
145172
|
log4.error("live_activity.notify_failed", {
|
|
144795
145173
|
event: "live_activity.notify_failed",
|
|
@@ -144799,14 +145177,57 @@ var LiveActivityNotifier = class {
|
|
|
144799
145177
|
});
|
|
144800
145178
|
}
|
|
144801
145179
|
}
|
|
145180
|
+
async startTurn(session) {
|
|
145181
|
+
const contentState = contentStateForSession({
|
|
145182
|
+
session,
|
|
145183
|
+
serverId: this.serverId,
|
|
145184
|
+
serverLabel: this.serverLabel
|
|
145185
|
+
});
|
|
145186
|
+
if (!contentState) return;
|
|
145187
|
+
const outcome = await this.sender.send({
|
|
145188
|
+
sessionId: session.id,
|
|
145189
|
+
event: "update",
|
|
145190
|
+
contentState
|
|
145191
|
+
});
|
|
145192
|
+
this.openActivity.set(session.id, { sessionNameSent: session.sessionName != null });
|
|
145193
|
+
if (outcome.attempted > 0) {
|
|
145194
|
+
log4.info("live_activity.updated", {
|
|
145195
|
+
event: "live_activity.updated",
|
|
145196
|
+
sessionId: session.id,
|
|
145197
|
+
status: contentState.status,
|
|
145198
|
+
...outcome
|
|
145199
|
+
});
|
|
145200
|
+
}
|
|
145201
|
+
}
|
|
145202
|
+
async maybeSendName(session) {
|
|
145203
|
+
const open3 = this.openActivity.get(session.id);
|
|
145204
|
+
if (!open3 || open3.sessionNameSent || session.sessionName == null) return;
|
|
145205
|
+
const contentState = contentStateForSession({
|
|
145206
|
+
session,
|
|
145207
|
+
serverId: this.serverId,
|
|
145208
|
+
serverLabel: this.serverLabel
|
|
145209
|
+
});
|
|
145210
|
+
if (!contentState) return;
|
|
145211
|
+
const outcome = await this.sender.send({
|
|
145212
|
+
sessionId: session.id,
|
|
145213
|
+
event: "update",
|
|
145214
|
+
contentState
|
|
145215
|
+
});
|
|
145216
|
+
open3.sessionNameSent = true;
|
|
145217
|
+
if (outcome.attempted > 0) {
|
|
145218
|
+
log4.info("live_activity.updated", {
|
|
145219
|
+
event: "live_activity.updated",
|
|
145220
|
+
sessionId: session.id,
|
|
145221
|
+
status: contentState.status,
|
|
145222
|
+
...outcome
|
|
145223
|
+
});
|
|
145224
|
+
}
|
|
145225
|
+
}
|
|
144802
145226
|
async endFor(session) {
|
|
144803
|
-
|
|
144804
|
-
|
|
145227
|
+
this.openActivity.delete(session.id);
|
|
145228
|
+
const status = toLiveActivityStatus(session.status);
|
|
144805
145229
|
const contentState = contentStateForSession({
|
|
144806
|
-
session: {
|
|
144807
|
-
...session,
|
|
144808
|
-
status: lastStatus === "waiting_input" ? "waiting_input" : "running"
|
|
144809
|
-
},
|
|
145230
|
+
session: { ...session, status: status ?? "waiting_input" },
|
|
144810
145231
|
serverId: this.serverId,
|
|
144811
145232
|
serverLabel: this.serverLabel
|
|
144812
145233
|
});
|
|
@@ -144820,9 +145241,9 @@ var LiveActivityNotifier = class {
|
|
|
144820
145241
|
});
|
|
144821
145242
|
}
|
|
144822
145243
|
}
|
|
144823
|
-
/** Drop cached state for a session, so a resume re-
|
|
145244
|
+
/** Drop cached state for a session, so a resume re-opens on its next turn. */
|
|
144824
145245
|
forget(sessionId) {
|
|
144825
|
-
this.
|
|
145246
|
+
this.openActivity.delete(sessionId);
|
|
144826
145247
|
}
|
|
144827
145248
|
};
|
|
144828
145249
|
|
|
@@ -145057,7 +145478,8 @@ var LiveActivityRenewalScheduler = class {
|
|
|
145057
145478
|
status,
|
|
145058
145479
|
startedAt,
|
|
145059
145480
|
lastOutput: session.lastOutput ?? "",
|
|
145060
|
-
...this.deps.serverLabel != null && { serverLabel: this.deps.serverLabel }
|
|
145481
|
+
...this.deps.serverLabel != null && { serverLabel: this.deps.serverLabel },
|
|
145482
|
+
...session.sessionName != null && { sessionName: session.sessionName }
|
|
145061
145483
|
};
|
|
145062
145484
|
try {
|
|
145063
145485
|
await this.deps.sender.send({
|
|
@@ -145117,7 +145539,8 @@ var LiveActivityRenewalScheduler = class {
|
|
|
145117
145539
|
// Carried through unchanged — the whole point of the renewal.
|
|
145118
145540
|
startedAt: args.startedAt,
|
|
145119
145541
|
lastOutput: truncateLastOutput(session.lastOutput ?? ""),
|
|
145120
|
-
...this.deps.serverLabel != null && { serverLabel: this.deps.serverLabel }
|
|
145542
|
+
...this.deps.serverLabel != null && { serverLabel: this.deps.serverLabel },
|
|
145543
|
+
...session.sessionName != null && { sessionName: session.sessionName }
|
|
145121
145544
|
},
|
|
145122
145545
|
now: args.now,
|
|
145123
145546
|
staleDate: args.startedAt + ACTIVITY_MAX_LIFETIME_MS
|
|
@@ -145275,8 +145698,90 @@ function resolveAnswer(pending, body) {
|
|
|
145275
145698
|
}
|
|
145276
145699
|
}
|
|
145277
145700
|
|
|
145701
|
+
// src/services/search/searchQuery.ts
|
|
145702
|
+
var DEFAULT_SEARCH_LIMIT = 50;
|
|
145703
|
+
var MAX_SEARCH_LIMIT = 200;
|
|
145704
|
+
var MAX_QUERY_LENGTH = 256;
|
|
145705
|
+
var SearchQueryError = class extends Error {
|
|
145706
|
+
constructor(message, code) {
|
|
145707
|
+
super(message);
|
|
145708
|
+
this.code = code;
|
|
145709
|
+
}
|
|
145710
|
+
code;
|
|
145711
|
+
};
|
|
145712
|
+
function intOr(raw2, fallback) {
|
|
145713
|
+
if (raw2 === null) return fallback;
|
|
145714
|
+
const n = Number.parseInt(raw2, 10);
|
|
145715
|
+
return Number.isFinite(n) ? n : fallback;
|
|
145716
|
+
}
|
|
145717
|
+
function parseSearchQuery(params) {
|
|
145718
|
+
const q2 = (params.get("q") ?? "").trim();
|
|
145719
|
+
if (!q2) {
|
|
145720
|
+
throw new SearchQueryError("Missing query parameter: q", "invalid_query");
|
|
145721
|
+
}
|
|
145722
|
+
if (q2.length > MAX_QUERY_LENGTH) {
|
|
145723
|
+
throw new SearchQueryError(`Query exceeds ${MAX_QUERY_LENGTH} characters`, "query_too_long");
|
|
145724
|
+
}
|
|
145725
|
+
const limit = Math.min(
|
|
145726
|
+
Math.max(intOr(params.get("limit"), DEFAULT_SEARCH_LIMIT), 1),
|
|
145727
|
+
MAX_SEARCH_LIMIT
|
|
145728
|
+
);
|
|
145729
|
+
const offset = Math.max(intOr(params.get("offset"), 0), 0);
|
|
145730
|
+
const filters = {};
|
|
145731
|
+
const provider = params.get("provider");
|
|
145732
|
+
if (provider !== null) {
|
|
145733
|
+
if (!isProviderName(provider)) {
|
|
145734
|
+
throw new SearchQueryError(`Unknown provider: ${provider}`, "invalid_filter");
|
|
145735
|
+
}
|
|
145736
|
+
filters.provider = provider;
|
|
145737
|
+
}
|
|
145738
|
+
const projectPath = params.get("projectPath");
|
|
145739
|
+
if (projectPath) filters.projectPath = projectPath;
|
|
145740
|
+
const branch = params.get("branch");
|
|
145741
|
+
if (branch) filters.branch = branch;
|
|
145742
|
+
for (const [key, field] of [
|
|
145743
|
+
["since", "since"],
|
|
145744
|
+
["until", "until"]
|
|
145745
|
+
]) {
|
|
145746
|
+
const raw2 = params.get(key);
|
|
145747
|
+
if (raw2 === null) continue;
|
|
145748
|
+
const ms2 = Date.parse(raw2);
|
|
145749
|
+
if (Number.isNaN(ms2)) {
|
|
145750
|
+
throw new SearchQueryError(`Invalid ${key}: expected an ISO 8601 date`, "invalid_filter");
|
|
145751
|
+
}
|
|
145752
|
+
filters[field] = ms2;
|
|
145753
|
+
}
|
|
145754
|
+
if (filters.since != null && filters.until != null && filters.since > filters.until) {
|
|
145755
|
+
throw new SearchQueryError("`since` must not be after `until`", "invalid_filter");
|
|
145756
|
+
}
|
|
145757
|
+
return { q: q2, limit, offset, filters };
|
|
145758
|
+
}
|
|
145759
|
+
function applyFilters(results, filters) {
|
|
145760
|
+
return results.filter((r) => {
|
|
145761
|
+
if (filters.provider && r.provider !== filters.provider) return false;
|
|
145762
|
+
if (filters.projectPath && r.projectPath !== filters.projectPath) return false;
|
|
145763
|
+
if (filters.branch && r.branch !== filters.branch) return false;
|
|
145764
|
+
if (filters.since != null || filters.until != null) {
|
|
145765
|
+
const ts2 = r.lastActivity == null ? Number.NaN : new Date(r.lastActivity).getTime();
|
|
145766
|
+
if (Number.isNaN(ts2)) return false;
|
|
145767
|
+
if (filters.since != null && ts2 < filters.since) return false;
|
|
145768
|
+
if (filters.until != null && ts2 > filters.until) return false;
|
|
145769
|
+
}
|
|
145770
|
+
return true;
|
|
145771
|
+
});
|
|
145772
|
+
}
|
|
145773
|
+
function paginate(results, offset, limit) {
|
|
145774
|
+
const items = results.slice(offset, offset + limit);
|
|
145775
|
+
return {
|
|
145776
|
+
items,
|
|
145777
|
+
total: results.length,
|
|
145778
|
+
offset,
|
|
145779
|
+
hasMore: offset + items.length < results.length
|
|
145780
|
+
};
|
|
145781
|
+
}
|
|
145782
|
+
|
|
145278
145783
|
// src/services/sessions/conversationBusy.ts
|
|
145279
|
-
var
|
|
145784
|
+
var import_fs29 = require("fs");
|
|
145280
145785
|
var RESUME_BUSY_WINDOW_MS = 12e4;
|
|
145281
145786
|
function resolveResumeBusyWindowMs(env = process.env) {
|
|
145282
145787
|
const raw2 = env.THREADBASE_RESUME_BUSY_WINDOW_MS;
|
|
@@ -145293,7 +145798,7 @@ function conversationBusy(input) {
|
|
|
145293
145798
|
let lastActivityMs = null;
|
|
145294
145799
|
if (input.jsonlPath) {
|
|
145295
145800
|
try {
|
|
145296
|
-
const mtimeMs = (0,
|
|
145801
|
+
const mtimeMs = (0, import_fs29.statSync)(input.jsonlPath).mtimeMs;
|
|
145297
145802
|
const age = now - mtimeMs;
|
|
145298
145803
|
lastActivityMs = Math.max(0, age);
|
|
145299
145804
|
const isSelfEcho = input.selfPtyEndedAt != null && mtimeMs <= input.selfPtyEndedAt + SELF_ACTIVITY_SKEW_MS;
|
|
@@ -145422,6 +145927,50 @@ async function reconcileSessions(rows, probe, currentInstanceId) {
|
|
|
145422
145927
|
return Promise.all(rows.map((row) => classifySession(row, probe, currentInstanceId)));
|
|
145423
145928
|
}
|
|
145424
145929
|
|
|
145930
|
+
// src/services/sessions/rehydrateSessions.ts
|
|
145931
|
+
var REHYDRATE_MAX = 25;
|
|
145932
|
+
var REHYDRATE_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
145933
|
+
var AGENT_EXIT_SOURCES = /* @__PURE__ */ new Set(["exit", "process-exit"]);
|
|
145934
|
+
function shouldRehydrate(row, opts) {
|
|
145935
|
+
if (!opts.projectExists(row.project_path)) return false;
|
|
145936
|
+
if (opts.now - row.status_updated_at > REHYDRATE_WINDOW_MS) return false;
|
|
145937
|
+
if (AGENT_EXIT_SOURCES.has(row.status_source) && row.failure_reason == null) return false;
|
|
145938
|
+
return true;
|
|
145939
|
+
}
|
|
145940
|
+
function rowToStubSession(row) {
|
|
145941
|
+
return {
|
|
145942
|
+
id: row.session_id,
|
|
145943
|
+
provider: row.provider,
|
|
145944
|
+
projectPath: row.project_path,
|
|
145945
|
+
projectName: row.project_name,
|
|
145946
|
+
branch: row.branch,
|
|
145947
|
+
// No PTY exists for a stub, so this is the only truthful status.
|
|
145948
|
+
status: "idle",
|
|
145949
|
+
startedAt: new Date(row.started_at),
|
|
145950
|
+
completedAt: row.completed_at != null ? new Date(row.completed_at) : null,
|
|
145951
|
+
promptCount: row.prompt_count,
|
|
145952
|
+
lastOutput: "",
|
|
145953
|
+
rehydrated: true,
|
|
145954
|
+
...row.session_name != null && { sessionName: row.session_name },
|
|
145955
|
+
...row.project_id != null && { projectId: row.project_id },
|
|
145956
|
+
...row.bound_conversation_id != null && { boundConversationId: row.bound_conversation_id },
|
|
145957
|
+
...row.resumed_from_conversation_id != null && {
|
|
145958
|
+
resumedFromConversationId: row.resumed_from_conversation_id
|
|
145959
|
+
},
|
|
145960
|
+
...row.failure_reason != null && { failureReason: row.failure_reason },
|
|
145961
|
+
...row.last_activity_at != null && { lastActivityAt: new Date(row.last_activity_at) },
|
|
145962
|
+
// Only `shutdown` crosses over. It is the one registry source that is also a
|
|
145963
|
+
// wire StatusSource *and* that genuinely describes the `idle` above — the
|
|
145964
|
+
// streamer stopped this session. A crashed row still says `transition` over
|
|
145965
|
+
// a `running` status, and copying that here would attach observed-confidence
|
|
145966
|
+
// provenance to a status we derived at boot, so leave it unset instead.
|
|
145967
|
+
...row.status_source === "shutdown" && {
|
|
145968
|
+
statusSource: "shutdown",
|
|
145969
|
+
statusUpdatedAt: new Date(row.status_updated_at)
|
|
145970
|
+
}
|
|
145971
|
+
};
|
|
145972
|
+
}
|
|
145973
|
+
|
|
145425
145974
|
// src/agent/dedupe.ts
|
|
145426
145975
|
function createProgressDedupeLRU(capacity) {
|
|
145427
145976
|
if (!Number.isFinite(capacity) || capacity < 1) {
|
|
@@ -145595,14 +146144,15 @@ function managedToResponse(s3, ptyAttached) {
|
|
|
145595
146144
|
// Lifecycle for a session this run knows about. `attached` while we hold
|
|
145596
146145
|
// its PTY; once the PTY is gone the session is terminal from this run's
|
|
145597
146146
|
// perspective — `failed` when it recorded a reason, else `completed`.
|
|
145598
|
-
//
|
|
145599
|
-
//
|
|
145600
|
-
//
|
|
145601
|
-
|
|
145602
|
-
|
|
146147
|
+
// A `rehydrated` stub is the exception: the boot rehydrator seeded it from
|
|
146148
|
+
// the durable registry, so it is a previous run's session with no process
|
|
146149
|
+
// behind it — `resumable`, and `historical` rather than `managed`
|
|
146150
|
+
// (docs/plans/live-sessions-persistence-plan.md §4, Phase 1).
|
|
146151
|
+
lifecycle: ptyAttached ? "attached" : s3.rehydrated ? "resumable" : s3.failureReason != null ? "failed" : "completed",
|
|
146152
|
+
lifecycleSource: ptyAttached ? "spawn" : s3.rehydrated ? "reconcile" : "exit",
|
|
145603
146153
|
// We spawned it, so `status` is the authoritative signal — no inferred
|
|
145604
146154
|
// `activity` is attached for managed sessions.
|
|
145605
|
-
ownership: "managed",
|
|
146155
|
+
ownership: s3.rehydrated ? "historical" : "managed",
|
|
145606
146156
|
projectPath: s3.projectPath,
|
|
145607
146157
|
projectName: s3.projectName,
|
|
145608
146158
|
branch: s3.branch,
|
|
@@ -146003,6 +146553,8 @@ var DEFAULT_PTY_GRACE_PERIOD_MS = 27e4;
|
|
|
146003
146553
|
var GRACE_MAX_DEFERS = 4;
|
|
146004
146554
|
var IDLE_REAP_AFTER_MS = 6 * 60 * 60 * 1e3;
|
|
146005
146555
|
var IDLE_REAP_SWEEP_MS = 5 * 60 * 1e3;
|
|
146556
|
+
var SEARCH_OVERFETCH = 4;
|
|
146557
|
+
var SEARCH_MAX_SCAN = 1e3;
|
|
146006
146558
|
var RESUME_DISCOVERY_TIMEOUT_MS = 750;
|
|
146007
146559
|
var DISCOVERY_TTL_MS = 15e3;
|
|
146008
146560
|
var ADOPT_KILL_TIMEOUT_MS = 5e3;
|
|
@@ -146190,10 +146742,13 @@ var StreamerServer = class {
|
|
|
146190
146742
|
projectsRepo = null;
|
|
146191
146743
|
conversationsRepo = null;
|
|
146192
146744
|
sessionsRepo = null;
|
|
146193
|
-
// Durable session registry (C1 Phase 2). Null when
|
|
146194
|
-
//
|
|
146195
|
-
// taking the server down with it, so every write goes through `?.`.
|
|
146745
|
+
// Durable session registry (C1 Phase 2). Null when runtime.db failed to open
|
|
146746
|
+
// — persistence degrades to today's in-memory-only behaviour rather than
|
|
146747
|
+
// taking the server down with it, so every write goes through `?.`. Note the
|
|
146748
|
+
// handle is runtime.db, NOT the conversation cache: a cache failure used to
|
|
146749
|
+
// null this repo and silently disable all session persistence.
|
|
146196
146750
|
managedSessionsRepo = null;
|
|
146751
|
+
runtimeStore = null;
|
|
146197
146752
|
// Identifies this streamer run. A registry row carrying a different id is a
|
|
146198
146753
|
// session that outlived the process that started it.
|
|
146199
146754
|
streamerInstanceId = (0, import_crypto13.randomUUID)();
|
|
@@ -146212,6 +146767,7 @@ var StreamerServer = class {
|
|
|
146212
146767
|
liveActivityRenewal = null;
|
|
146213
146768
|
discoveryCache = null;
|
|
146214
146769
|
cacheDir;
|
|
146770
|
+
runtimeDbPath;
|
|
146215
146771
|
tailSize;
|
|
146216
146772
|
directoryDebounceMs;
|
|
146217
146773
|
// Trailing-debounced trigger that flags the scanner stale after a quiet
|
|
@@ -146243,7 +146799,7 @@ var StreamerServer = class {
|
|
|
146243
146799
|
this.skipStartupWarmup = config2.skipStartupWarmup ?? false;
|
|
146244
146800
|
this.scannerPersistenceDisabled = config2.scannerPersistent === false;
|
|
146245
146801
|
this.scanProfiles = config2.scanProfiles;
|
|
146246
|
-
this.codexRoots = config2.codexRoots ?? [(0, import_path29.join)((0,
|
|
146802
|
+
this.codexRoots = config2.codexRoots ?? [(0, import_path29.join)((0, import_os13.homedir)(), ".codex", "sessions")];
|
|
146247
146803
|
this.ptyGracePeriodMs = config2.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
|
|
146248
146804
|
this.defaultSystemPrompt = config2.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
146249
146805
|
this.featureFlags = resolveFeatureFlags({ cli: config2.featureFlags, yaml: loadFeatureFlags() });
|
|
@@ -146257,7 +146813,8 @@ var StreamerServer = class {
|
|
|
146257
146813
|
this.claudeFlagsPersistable = config2.claudeFlags === void 0;
|
|
146258
146814
|
this.claudeFlags = config2.claudeFlags ?? loadClaudeFlags();
|
|
146259
146815
|
this.claudeExtraArgs = config2.claudeExtraArgs ?? loadClaudeExtraArgs();
|
|
146260
|
-
this.cacheDir = config2.cacheDir ?? loadCacheDir() ?? (0, import_path29.join)((0,
|
|
146816
|
+
this.cacheDir = config2.cacheDir ?? loadCacheDir() ?? (0, import_path29.join)((0, import_os13.homedir)(), ".threadbase", "cache");
|
|
146817
|
+
this.runtimeDbPath = config2.runtimeDbPath ?? process.env.THREADBASE_RUNTIME_DB ?? (0, import_path29.join)(process.env.THREADBASE_CONFIG_DIR ?? (0, import_path29.join)((0, import_os13.homedir)(), ".threadbase"), "runtime.db");
|
|
146261
146818
|
this.tailSize = config2.tailSize ?? loadTailSize() ?? 10;
|
|
146262
146819
|
this.directoryDebounceMs = parseDirScanDebounceEnv(process.env.THREADBASE_DIR_SCAN_DEBOUNCE_MS) ?? config2.directoryScanDebounceMs ?? 1e3;
|
|
146263
146820
|
this.markScannerStaleDebounced = debounce(() => {
|
|
@@ -146308,7 +146865,7 @@ var StreamerServer = class {
|
|
|
146308
146865
|
const seqs = cache.extendMessageIndex(
|
|
146309
146866
|
filePath,
|
|
146310
146867
|
spans,
|
|
146311
|
-
(0,
|
|
146868
|
+
(0, import_fs30.statSync)(filePath),
|
|
146312
146869
|
readFrom,
|
|
146313
146870
|
endOffset
|
|
146314
146871
|
);
|
|
@@ -146428,6 +146985,7 @@ var StreamerServer = class {
|
|
|
146428
146985
|
if (resp) this.wsHub.broadcast({ type: "session_ready", session: resp });
|
|
146429
146986
|
},
|
|
146430
146987
|
onStatusChange: (session) => {
|
|
146988
|
+
const previousStatus = this.sessionStore.getManaged(session.id)?.status;
|
|
146431
146989
|
this.sessionStore.updateManaged(session.id, {
|
|
146432
146990
|
status: session.status,
|
|
146433
146991
|
completedAt: session.completedAt,
|
|
@@ -146482,7 +147040,7 @@ var StreamerServer = class {
|
|
|
146482
147040
|
if (resp) {
|
|
146483
147041
|
this.wsHub.broadcast({ type: "session_update", session: resp });
|
|
146484
147042
|
}
|
|
146485
|
-
void this.liveActivityNotifier?.onStatusChange(session);
|
|
147043
|
+
void this.liveActivityNotifier?.onStatusChange(session, previousStatus);
|
|
146486
147044
|
this.sessionStatusBus.emit(`status:${session.id}`, session.status);
|
|
146487
147045
|
}
|
|
146488
147046
|
});
|
|
@@ -146533,6 +147091,7 @@ var StreamerServer = class {
|
|
|
146533
147091
|
conversationsRepo: () => this.conversationsRepo,
|
|
146534
147092
|
sessionsRepo: () => this.sessionsRepo,
|
|
146535
147093
|
cacheMetadataRepo: () => this.cacheMetadataRepo,
|
|
147094
|
+
runtimeStore: () => this.runtimeStore,
|
|
146536
147095
|
ptyAttachedIds: () => this.ptyAttachedIds(),
|
|
146537
147096
|
handleListSessions: (url2, res) => this.handleListSessions(url2, res),
|
|
146538
147097
|
handleSessionsCount: (res) => this.handleSessionsCount(res),
|
|
@@ -146771,14 +147330,14 @@ var StreamerServer = class {
|
|
|
146771
147330
|
}
|
|
146772
147331
|
this.apnsClient = new ApnsClient(creds);
|
|
146773
147332
|
const sender = new LiveActivitySender(this.apnsClient, pushRepo);
|
|
146774
|
-
const serverId = process.env.THREADBASE_INSTANCE_ID ?? (0,
|
|
146775
|
-
this.liveActivityNotifier = new LiveActivityNotifier(sender, serverId, (0,
|
|
147333
|
+
const serverId = process.env.THREADBASE_INSTANCE_ID ?? (0, import_os13.hostname)();
|
|
147334
|
+
this.liveActivityNotifier = new LiveActivityNotifier(sender, serverId, (0, import_os13.hostname)());
|
|
146776
147335
|
this.liveActivityRenewal = new LiveActivityRenewalScheduler({
|
|
146777
147336
|
repo: pushRepo,
|
|
146778
147337
|
sender,
|
|
146779
147338
|
sessionStore: this.sessionStore,
|
|
146780
147339
|
serverId,
|
|
146781
|
-
serverLabel: (0,
|
|
147340
|
+
serverLabel: (0, import_os13.hostname)()
|
|
146782
147341
|
});
|
|
146783
147342
|
this.liveActivityRenewal.start();
|
|
146784
147343
|
this.log.info("Live Activity push enabled", {
|
|
@@ -146832,6 +147391,58 @@ var StreamerServer = class {
|
|
|
146832
147391
|
}
|
|
146833
147392
|
return verdicts;
|
|
146834
147393
|
}
|
|
147394
|
+
/**
|
|
147395
|
+
* Seed the session list with what previous runs left behind (persistence plan
|
|
147396
|
+
* Phase 1, gaps G1/G2/G8).
|
|
147397
|
+
*
|
|
147398
|
+
* Reconciliation classifies rows and stops there; a verdict is overlaid onto a
|
|
147399
|
+
* SessionResponse that already exists, and after a clean restart none does —
|
|
147400
|
+
* `SessionStore` starts empty. So the user's session did not become
|
|
147401
|
+
* `resumable`, it became *absent*. This is the half that puts it back.
|
|
147402
|
+
*
|
|
147403
|
+
* The seeded stubs hold no PTY and are never handed to `LiveSessionManager`,
|
|
147404
|
+
* so `reapIdleSessions` and `startGraceTimer` — both of which iterate
|
|
147405
|
+
* `ptyManager.listSessions()` — cannot observe them. A later resume calls
|
|
147406
|
+
* `sessionStore.addManaged` with the real session, which overwrites the stub
|
|
147407
|
+
* by id rather than duplicating it.
|
|
147408
|
+
*/
|
|
147409
|
+
rehydratePreviousSessions(verdicts) {
|
|
147410
|
+
if (!this.featureFlags.sessionRehydration || !this.managedSessionsRepo) return;
|
|
147411
|
+
try {
|
|
147412
|
+
const now = Date.now();
|
|
147413
|
+
const rows = this.managedSessionsRepo.listRecoverable({
|
|
147414
|
+
sinceMs: now - REHYDRATE_WINDOW_MS,
|
|
147415
|
+
limit: REHYDRATE_MAX + 1
|
|
147416
|
+
});
|
|
147417
|
+
const truncated = rows.length > REHYDRATE_MAX;
|
|
147418
|
+
const candidates = truncated ? rows.slice(0, REHYDRATE_MAX) : rows;
|
|
147419
|
+
if (candidates.length === 0) return;
|
|
147420
|
+
const lifecycleByVerdict = new Map(verdicts.map((v2) => [v2.sessionId, v2.lifecycle]));
|
|
147421
|
+
let rehydrated = 0;
|
|
147422
|
+
for (const row of candidates) {
|
|
147423
|
+
if (this.sessionStore.getManaged(row.session_id)) continue;
|
|
147424
|
+
if (!shouldRehydrate(row, { now, projectExists: import_fs30.existsSync })) continue;
|
|
147425
|
+
this.sessionStore.addManaged(rowToStubSession(row));
|
|
147426
|
+
this.sessionLifecycles.set(
|
|
147427
|
+
row.session_id,
|
|
147428
|
+
lifecycleByVerdict.get(row.session_id) ?? "resumable"
|
|
147429
|
+
);
|
|
147430
|
+
if (row.completed_at != null) this.selfPtyEndedAt.set(row.session_id, row.completed_at);
|
|
147431
|
+
rehydrated++;
|
|
147432
|
+
}
|
|
147433
|
+
this.log.info(`[rehydrate] recovered ${rehydrated} session(s) from the registry`, {
|
|
147434
|
+
event: "sessions.rehydrated",
|
|
147435
|
+
rehydrated,
|
|
147436
|
+
skipped: candidates.length - rehydrated,
|
|
147437
|
+
truncated
|
|
147438
|
+
});
|
|
147439
|
+
} catch (err) {
|
|
147440
|
+
this.log.warn("[rehydrate] failed to rehydrate previous sessions", {
|
|
147441
|
+
event: "sessions.rehydrate_failed",
|
|
147442
|
+
err
|
|
147443
|
+
});
|
|
147444
|
+
}
|
|
147445
|
+
}
|
|
146835
147446
|
/**
|
|
146836
147447
|
* Pick a token guaranteed to appear in the spawned process's argv, for the
|
|
146837
147448
|
* reconciler's pid-reuse guard.
|
|
@@ -147065,6 +147676,17 @@ var StreamerServer = class {
|
|
|
147065
147676
|
port,
|
|
147066
147677
|
event: "server.listening"
|
|
147067
147678
|
});
|
|
147679
|
+
try {
|
|
147680
|
+
this.runtimeStore = RuntimeStore.open(this.runtimeDbPath);
|
|
147681
|
+
this.managedSessionsRepo = new ManagedSessionsRepository(this.runtimeStore.getDatabase());
|
|
147682
|
+
} catch (err) {
|
|
147683
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
147684
|
+
const abiMismatch = message.includes("NODE_MODULE_VERSION") || message.includes("was compiled against a different Node.js version");
|
|
147685
|
+
this.log.error(
|
|
147686
|
+
`Runtime store failed to open \u2014 session persistence DISABLED; sessions will not survive a restart.` + (abiMismatch ? ` Fix: npm rebuild better-sqlite3` : "") + ` (${message})`,
|
|
147687
|
+
{ error: message, abiMismatch, path: this.runtimeDbPath, event: "runtime.open_failed" }
|
|
147688
|
+
);
|
|
147689
|
+
}
|
|
147068
147690
|
try {
|
|
147069
147691
|
this.cache = ConversationCache.open(
|
|
147070
147692
|
(0, import_path29.join)(this.cacheDir, "cache.db"),
|
|
@@ -147092,8 +147714,20 @@ var StreamerServer = class {
|
|
|
147092
147714
|
this.projectsRepo = new ProjectsRepository(db);
|
|
147093
147715
|
this.conversationsRepo = new ConversationsRepository(this.cache);
|
|
147094
147716
|
this.sessionsRepo = new SessionsRepository(this.sessionStore);
|
|
147095
|
-
|
|
147096
|
-
|
|
147717
|
+
try {
|
|
147718
|
+
const copied = this.runtimeStore?.importLegacyManagedSessions(db) ?? 0;
|
|
147719
|
+
if (copied > 0) {
|
|
147720
|
+
this.log.info(`Copied ${copied} managed session row(s) from cache.db to runtime.db`, {
|
|
147721
|
+
copied,
|
|
147722
|
+
event: "runtime.legacy_import"
|
|
147723
|
+
});
|
|
147724
|
+
}
|
|
147725
|
+
} catch (err) {
|
|
147726
|
+
this.log.warn("[registry] legacy managed_sessions copy failed", {
|
|
147727
|
+
event: "runtime.legacy_import_failed",
|
|
147728
|
+
err
|
|
147729
|
+
});
|
|
147730
|
+
}
|
|
147097
147731
|
this.cacheMetadataRepo = new CacheMetadataRepository(db);
|
|
147098
147732
|
this.pushRepo = new PushRepository(db);
|
|
147099
147733
|
this.devicesRepo = new DevicesRepository(db);
|
|
@@ -147117,7 +147751,7 @@ var StreamerServer = class {
|
|
|
147117
147751
|
this.fileWatcher.watchDirectory(dir);
|
|
147118
147752
|
}
|
|
147119
147753
|
for (const dir of this.codexRoots) {
|
|
147120
|
-
if (!(0,
|
|
147754
|
+
if (!(0, import_fs30.existsSync)(dir)) continue;
|
|
147121
147755
|
this.fileWatcher.watchDirectory(dir);
|
|
147122
147756
|
}
|
|
147123
147757
|
} catch (err) {
|
|
@@ -147129,6 +147763,7 @@ var StreamerServer = class {
|
|
|
147129
147763
|
);
|
|
147130
147764
|
this.scannerPersistenceDisabled = true;
|
|
147131
147765
|
}
|
|
147766
|
+
void this.reconcilePreviousSessions().then((v2) => this.rehydratePreviousSessions(v2));
|
|
147132
147767
|
if (this.skipStartupWarmup) {
|
|
147133
147768
|
this.log.debug?.("startup warm-up scan skipped (skipStartupWarmup)", {
|
|
147134
147769
|
event: "cache.warmup_skipped"
|
|
@@ -147336,6 +147971,7 @@ var StreamerServer = class {
|
|
|
147336
147971
|
this.allScanners.clear();
|
|
147337
147972
|
this.scanner = null;
|
|
147338
147973
|
this.cache?.close();
|
|
147974
|
+
this.runtimeStore?.close();
|
|
147339
147975
|
this.ptyManager.dispose();
|
|
147340
147976
|
this.fileWatcher.dispose();
|
|
147341
147977
|
this.externalTails.clear();
|
|
@@ -147390,7 +148026,7 @@ var StreamerServer = class {
|
|
|
147390
148026
|
}
|
|
147391
148027
|
let body;
|
|
147392
148028
|
try {
|
|
147393
|
-
body = await
|
|
148029
|
+
body = await readBody2(req);
|
|
147394
148030
|
} catch (err) {
|
|
147395
148031
|
const message = err instanceof Error ? err.message : "Invalid body";
|
|
147396
148032
|
json2(res, 400, { error: message });
|
|
@@ -147436,7 +148072,7 @@ var StreamerServer = class {
|
|
|
147436
148072
|
nonce: sealed.nonce,
|
|
147437
148073
|
ephemeralPublicKey: sealed.ephemeralPublicKey,
|
|
147438
148074
|
publicUrl: this.publicUrl,
|
|
147439
|
-
machineName: (0,
|
|
148075
|
+
machineName: (0, import_os13.hostname)(),
|
|
147440
148076
|
...device && {
|
|
147441
148077
|
deviceId: device.deviceId,
|
|
147442
148078
|
deviceToken: device.deviceToken,
|
|
@@ -147564,7 +148200,7 @@ var StreamerServer = class {
|
|
|
147564
148200
|
if (this.scanProfiles && this.scanProfiles.length > 0) {
|
|
147565
148201
|
return this.scanProfiles.filter((p2) => p2.enabled).map((p2) => (0, import_path29.join)(p2.configDir, "projects"));
|
|
147566
148202
|
}
|
|
147567
|
-
return [(0, import_path29.join)((0,
|
|
148203
|
+
return [(0, import_path29.join)((0, import_os13.homedir)(), ".claude", "projects")];
|
|
147568
148204
|
}
|
|
147569
148205
|
/**
|
|
147570
148206
|
* Full-glob scan + cache upsert/delete reconcile. Used by ?refresh=1 and by
|
|
@@ -147783,7 +148419,8 @@ var StreamerServer = class {
|
|
|
147783
148419
|
}
|
|
147784
148420
|
handleSessionsCount(res) {
|
|
147785
148421
|
if (this.rejectIfWarmingUp(res)) return;
|
|
147786
|
-
|
|
148422
|
+
const total = this.sessionStore.list(this.ptyAttachedIds()).filter((s3) => s3.ownership !== "historical").length;
|
|
148423
|
+
json2(res, 200, { total });
|
|
147787
148424
|
}
|
|
147788
148425
|
handleGetRecentSessions(url2, res) {
|
|
147789
148426
|
if (this.rejectIfWarmingUp(res)) return;
|
|
@@ -147966,20 +148603,20 @@ var StreamerServer = class {
|
|
|
147966
148603
|
if (this.scanProfiles && this.scanProfiles.length > 0) {
|
|
147967
148604
|
return this.scanProfiles.filter((p2) => p2.enabled).map((p2) => (0, import_path29.join)(p2.configDir, "projects"));
|
|
147968
148605
|
}
|
|
147969
|
-
return [(0, import_path29.join)((0,
|
|
148606
|
+
return [(0, import_path29.join)((0, import_os13.homedir)(), ".claude", "projects")];
|
|
147970
148607
|
}
|
|
147971
148608
|
findJsonlPath(uuid3) {
|
|
147972
148609
|
const filename = `${uuid3}.jsonl`;
|
|
147973
148610
|
for (const projectsDir of this.projectsDirs()) {
|
|
147974
|
-
if (!(0,
|
|
147975
|
-
for (const dir of (0,
|
|
148611
|
+
if (!(0, import_fs30.existsSync)(projectsDir)) continue;
|
|
148612
|
+
for (const dir of (0, import_fs30.readdirSync)(projectsDir)) {
|
|
147976
148613
|
const fp = (0, import_path29.join)(projectsDir, dir, filename);
|
|
147977
|
-
if ((0,
|
|
148614
|
+
if ((0, import_fs30.existsSync)(fp)) return fp;
|
|
147978
148615
|
const projectDir = (0, import_path29.join)(projectsDir, dir);
|
|
147979
148616
|
try {
|
|
147980
|
-
for (const sub of (0,
|
|
148617
|
+
for (const sub of (0, import_fs30.readdirSync)(projectDir)) {
|
|
147981
148618
|
const subagentPath = (0, import_path29.join)(projectDir, sub, "subagents", filename);
|
|
147982
|
-
if ((0,
|
|
148619
|
+
if ((0, import_fs30.existsSync)(subagentPath)) return subagentPath;
|
|
147983
148620
|
}
|
|
147984
148621
|
} catch {
|
|
147985
148622
|
}
|
|
@@ -147989,7 +148626,7 @@ var StreamerServer = class {
|
|
|
147989
148626
|
}
|
|
147990
148627
|
async readCwdFromJsonl(filePath) {
|
|
147991
148628
|
return new Promise((resolve4) => {
|
|
147992
|
-
const rl = (0, import_readline4.createInterface)({ input: (0,
|
|
148629
|
+
const rl = (0, import_readline4.createInterface)({ input: (0, import_fs30.createReadStream)(filePath), crlfDelay: Infinity });
|
|
147993
148630
|
let found = false;
|
|
147994
148631
|
rl.on("line", (line) => {
|
|
147995
148632
|
if (found) return;
|
|
@@ -148079,7 +148716,7 @@ var StreamerServer = class {
|
|
|
148079
148716
|
if (this.isManagedTailPath(key)) return;
|
|
148080
148717
|
let mtimeMs;
|
|
148081
148718
|
try {
|
|
148082
|
-
mtimeMs = (0,
|
|
148719
|
+
mtimeMs = (0, import_fs30.statSync)(filePath).mtimeMs;
|
|
148083
148720
|
} catch {
|
|
148084
148721
|
return;
|
|
148085
148722
|
}
|
|
@@ -148261,7 +148898,7 @@ var StreamerServer = class {
|
|
|
148261
148898
|
if (!conv.filePath) return false;
|
|
148262
148899
|
let mtimeMs = null;
|
|
148263
148900
|
try {
|
|
148264
|
-
mtimeMs = (0,
|
|
148901
|
+
mtimeMs = (0, import_fs30.statSync)(conv.filePath).mtimeMs;
|
|
148265
148902
|
} catch {
|
|
148266
148903
|
return false;
|
|
148267
148904
|
}
|
|
@@ -148516,7 +149153,7 @@ var StreamerServer = class {
|
|
|
148516
149153
|
}
|
|
148517
149154
|
let body;
|
|
148518
149155
|
try {
|
|
148519
|
-
body = await
|
|
149156
|
+
body = await readBody2(req);
|
|
148520
149157
|
} catch {
|
|
148521
149158
|
res.setHeader("Accept-Query", "application/json");
|
|
148522
149159
|
json2(res, 422, { error: "Malformed JSON body", code: "invalid_query" });
|
|
@@ -148554,17 +149191,27 @@ var StreamerServer = class {
|
|
|
148554
149191
|
});
|
|
148555
149192
|
}
|
|
148556
149193
|
async handleSearch(url2, res) {
|
|
148557
|
-
|
|
148558
|
-
|
|
148559
|
-
|
|
148560
|
-
|
|
149194
|
+
let parsed;
|
|
149195
|
+
try {
|
|
149196
|
+
parsed = parseSearchQuery(url2.searchParams);
|
|
149197
|
+
} catch (err) {
|
|
149198
|
+
if (err instanceof SearchQueryError) {
|
|
149199
|
+
json2(res, 400, { error: err.message, code: err.code });
|
|
149200
|
+
return;
|
|
149201
|
+
}
|
|
149202
|
+
throw err;
|
|
148561
149203
|
}
|
|
148562
|
-
const
|
|
149204
|
+
const { q: q2, limit, offset, filters } = parsed;
|
|
149205
|
+
const startedAt = Date.now();
|
|
148563
149206
|
const scanner = await this.getScanner();
|
|
148564
149207
|
const results = await search(
|
|
148565
149208
|
q2,
|
|
148566
149209
|
{
|
|
148567
|
-
|
|
149210
|
+
// Fetch beyond the requested page: filters below are applied AFTER the
|
|
149211
|
+
// scanner returns, so slicing at `limit` here would drop results that a
|
|
149212
|
+
// later page should contain. Bounded so a broad query cannot pull an
|
|
149213
|
+
// unbounded set into memory.
|
|
149214
|
+
limit: Math.min(offset + limit * SEARCH_OVERFETCH, SEARCH_MAX_SCAN),
|
|
148568
149215
|
include: "conversations",
|
|
148569
149216
|
...this.scanProfiles ? { profiles: this.scanProfiles } : {},
|
|
148570
149217
|
...this.codexScanOpts()
|
|
@@ -148588,13 +149235,24 @@ var StreamerServer = class {
|
|
|
148588
149235
|
lastActivity: r.meta.timestamp,
|
|
148589
149236
|
firstMessage: r.meta.firstMessage ?? void 0,
|
|
148590
149237
|
lastMessage: r.meta.lastMessage ?? void 0,
|
|
148591
|
-
provider: r.meta.provider ?? CLAUDE_CODE_PROVIDER2
|
|
149238
|
+
provider: r.meta.provider ?? CLAUDE_CODE_PROVIDER2,
|
|
149239
|
+
// The scanner already computes relevance and match snippets; the previous
|
|
149240
|
+
// adapter discarded both, so results arrived in an unexplained order with
|
|
149241
|
+
// no indication of WHY anything matched.
|
|
149242
|
+
score: r.score,
|
|
149243
|
+
matches: Array.isArray(r.matches) ? r.matches.map((m2) => ({
|
|
149244
|
+
field: m2.field,
|
|
149245
|
+
snippet: m2.snippet
|
|
149246
|
+
})) : []
|
|
148592
149247
|
}));
|
|
149248
|
+
const page = paginate(applyFilters(adapted, filters), offset, limit);
|
|
148593
149249
|
json2(res, 200, {
|
|
148594
|
-
conversations:
|
|
148595
|
-
hasMore:
|
|
148596
|
-
offset:
|
|
148597
|
-
total:
|
|
149250
|
+
conversations: page.items,
|
|
149251
|
+
hasMore: page.hasMore,
|
|
149252
|
+
offset: page.offset,
|
|
149253
|
+
total: page.total,
|
|
149254
|
+
// Query timing, so a slow search is diagnosable rather than merely felt.
|
|
149255
|
+
tookMs: Date.now() - startedAt
|
|
148598
149256
|
});
|
|
148599
149257
|
}
|
|
148600
149258
|
async handleListSessions(url2, res) {
|
|
@@ -148640,7 +149298,7 @@ var StreamerServer = class {
|
|
|
148640
149298
|
if (this.rejectIfWarmingUp(res)) return;
|
|
148641
149299
|
const session = this.sessionStore.get(sessionId, this.ptyAttachedIds());
|
|
148642
149300
|
if (session) {
|
|
148643
|
-
if (!(0,
|
|
149301
|
+
if (!(0, import_fs30.existsSync)(session.projectPath)) {
|
|
148644
149302
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
148645
149303
|
}
|
|
148646
149304
|
const reconciled = this.withReconciledLifecycle([session])[0];
|
|
@@ -148667,7 +149325,7 @@ var StreamerServer = class {
|
|
|
148667
149325
|
json2(res, 404, { error: "Session not found" });
|
|
148668
149326
|
}
|
|
148669
149327
|
async handleResume(req, res) {
|
|
148670
|
-
const body = await
|
|
149328
|
+
const body = await readBody2(req);
|
|
148671
149329
|
const sessionId = body.sessionId ?? body.conversationId;
|
|
148672
149330
|
if (!sessionId) {
|
|
148673
149331
|
json2(res, 400, { error: "Missing sessionId" });
|
|
@@ -148799,7 +149457,7 @@ var StreamerServer = class {
|
|
|
148799
149457
|
return;
|
|
148800
149458
|
}
|
|
148801
149459
|
if (this.agentConfig.enabled) {
|
|
148802
|
-
const body2 = await
|
|
149460
|
+
const body2 = await readBody2(req);
|
|
148803
149461
|
const cache = this.cache;
|
|
148804
149462
|
if (!cache) {
|
|
148805
149463
|
json2(res, 503, {
|
|
@@ -148818,7 +149476,7 @@ var StreamerServer = class {
|
|
|
148818
149476
|
json2(res, result.status, result.body);
|
|
148819
149477
|
return;
|
|
148820
149478
|
}
|
|
148821
|
-
const body = await
|
|
149479
|
+
const body = await readBody2(req);
|
|
148822
149480
|
const { input, keys } = body;
|
|
148823
149481
|
let idempotencyKey;
|
|
148824
149482
|
try {
|
|
@@ -148982,7 +149640,7 @@ var StreamerServer = class {
|
|
|
148982
149640
|
});
|
|
148983
149641
|
}
|
|
148984
149642
|
async handleSendAnswer(sessionId, req, res) {
|
|
148985
|
-
const body = await
|
|
149643
|
+
const body = await readBody2(req);
|
|
148986
149644
|
const pending = this.pendingQuestions.get(sessionId);
|
|
148987
149645
|
const resolution = resolveAnswer(pending, body);
|
|
148988
149646
|
if (!resolution.ok) {
|
|
@@ -149011,7 +149669,7 @@ var StreamerServer = class {
|
|
|
149011
149669
|
json2(res, 400, { error: "Session has no project path" });
|
|
149012
149670
|
return;
|
|
149013
149671
|
}
|
|
149014
|
-
const body = await
|
|
149672
|
+
const body = await readBody2(req);
|
|
149015
149673
|
const { filename, mimeType, dataBase64 } = body ?? {};
|
|
149016
149674
|
if (typeof filename !== "string" || typeof mimeType !== "string" || typeof dataBase64 !== "string") {
|
|
149017
149675
|
json2(res, 400, { error: "Missing filename, mimeType, or dataBase64" });
|
|
@@ -149213,7 +149871,7 @@ var StreamerServer = class {
|
|
|
149213
149871
|
return;
|
|
149214
149872
|
}
|
|
149215
149873
|
if (this.agentConfig.enabled) {
|
|
149216
|
-
const body2 = await
|
|
149874
|
+
const body2 = await readBody2(req);
|
|
149217
149875
|
const result = await handleStartAgentSession(body2, {
|
|
149218
149876
|
sessionStore: this.sessionStore,
|
|
149219
149877
|
// biome-ignore lint/style/noNonNullAssertion: agentClient is set when agentConfig.enabled is true
|
|
@@ -149227,7 +149885,7 @@ var StreamerServer = class {
|
|
|
149227
149885
|
}
|
|
149228
149886
|
return;
|
|
149229
149887
|
}
|
|
149230
|
-
const body = await
|
|
149888
|
+
const body = await readBody2(req);
|
|
149231
149889
|
const { path: relativePath, provider: requestedProvider, systemPrompt: clientPrompt } = body;
|
|
149232
149890
|
if (requestedProvider !== void 0 && !isProviderName(requestedProvider)) {
|
|
149233
149891
|
json2(res, 400, { error: "Invalid provider" });
|
|
@@ -149364,7 +150022,7 @@ var StreamerServer = class {
|
|
|
149364
150022
|
// file isn't slurped in full.
|
|
149365
150023
|
readFirstLineSessionId(filePath) {
|
|
149366
150024
|
try {
|
|
149367
|
-
const content = (0,
|
|
150025
|
+
const content = (0, import_fs30.readFileSync)(filePath, "utf8");
|
|
149368
150026
|
const nl = content.indexOf("\n");
|
|
149369
150027
|
const firstLine = nl === -1 ? content : content.slice(0, nl);
|
|
149370
150028
|
if (!firstLine.trim()) return null;
|
|
@@ -149379,7 +150037,7 @@ var StreamerServer = class {
|
|
|
149379
150037
|
// was passed to Claude via --session-id so the filename matches from the start.
|
|
149380
150038
|
watchForJsonl(sessionId, projectPath) {
|
|
149381
150039
|
const encoded = projectPath.replace(/[/\\:.]/g, "-");
|
|
149382
|
-
const projectsDir = (0, import_path29.join)((0,
|
|
150040
|
+
const projectsDir = (0, import_path29.join)((0, import_os13.homedir)(), ".claude", "projects", encoded);
|
|
149383
150041
|
const expectedFile = `${sessionId}.jsonl`;
|
|
149384
150042
|
const filePath = (0, import_path29.join)(projectsDir, expectedFile);
|
|
149385
150043
|
const deadline = Date.now() + 12e4;
|
|
@@ -149399,11 +150057,11 @@ var StreamerServer = class {
|
|
|
149399
150057
|
cleanup();
|
|
149400
150058
|
return;
|
|
149401
150059
|
}
|
|
149402
|
-
let resolvedFilePath = (0,
|
|
149403
|
-
if (!resolvedFilePath && (0,
|
|
150060
|
+
let resolvedFilePath = (0, import_fs30.existsSync)(filePath) ? filePath : null;
|
|
150061
|
+
if (!resolvedFilePath && (0, import_fs30.existsSync)(projectsDir)) {
|
|
149404
150062
|
try {
|
|
149405
150063
|
const now = Date.now();
|
|
149406
|
-
const match2 = (0,
|
|
150064
|
+
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(
|
|
149407
150065
|
({ f: f2 }) => (0, import_path29.basename)(f2, ".jsonl") === sessionId || this.readFirstLineSessionId((0, import_path29.join)(projectsDir, f2)) === sessionId
|
|
149408
150066
|
).sort((a, b2) => b2.mtime - a.mtime)[0];
|
|
149409
150067
|
if (match2) resolvedFilePath = (0, import_path29.join)(projectsDir, match2.f);
|
|
@@ -149414,7 +150072,7 @@ var StreamerServer = class {
|
|
|
149414
150072
|
cleanup();
|
|
149415
150073
|
this.sessionFileMap.set(sessionId, resolvedFilePath);
|
|
149416
150074
|
try {
|
|
149417
|
-
const existing = (0,
|
|
150075
|
+
const existing = (0, import_fs30.readFileSync)(resolvedFilePath, "utf8").split("\n").filter(Boolean);
|
|
149418
150076
|
if (existing.length > 0) {
|
|
149419
150077
|
this.broadcastConversationLines(sessionId, existing);
|
|
149420
150078
|
}
|
|
@@ -149438,7 +150096,7 @@ var StreamerServer = class {
|
|
|
149438
150096
|
if (this.sessionFileMap.has(sessionId)) return;
|
|
149439
150097
|
try {
|
|
149440
150098
|
require("fs").mkdirSync(projectsDir, { recursive: true });
|
|
149441
|
-
watcher = (0,
|
|
150099
|
+
watcher = (0, import_fs30.watch)(projectsDir, tryWire);
|
|
149442
150100
|
watcher.on("error", cleanup);
|
|
149443
150101
|
} catch {
|
|
149444
150102
|
}
|
|
@@ -149467,7 +150125,7 @@ var StreamerServer = class {
|
|
|
149467
150125
|
};
|
|
149468
150126
|
const matchesProjectPath = (candidatePath) => {
|
|
149469
150127
|
try {
|
|
149470
|
-
const firstLine = (0,
|
|
150128
|
+
const firstLine = (0, import_fs30.readFileSync)(candidatePath, "utf8").split("\n", 1)[0];
|
|
149471
150129
|
if (!firstLine) return null;
|
|
149472
150130
|
const parsed = JSON.parse(firstLine);
|
|
149473
150131
|
if (parsed?.type !== "session_meta") return null;
|
|
@@ -149496,15 +150154,15 @@ var StreamerServer = class {
|
|
|
149496
150154
|
);
|
|
149497
150155
|
for (const root of this.codexRoots) {
|
|
149498
150156
|
const sessionsDir = (0, import_path29.join)(root, dateDir);
|
|
149499
|
-
if (!(0,
|
|
150157
|
+
if (!(0, import_fs30.existsSync)(sessionsDir)) continue;
|
|
149500
150158
|
let candidateFiles;
|
|
149501
150159
|
try {
|
|
149502
|
-
candidateFiles = (0,
|
|
150160
|
+
candidateFiles = (0, import_fs30.readdirSync)(sessionsDir).filter((f2) => f2.endsWith(".jsonl"));
|
|
149503
150161
|
} catch {
|
|
149504
150162
|
continue;
|
|
149505
150163
|
}
|
|
149506
150164
|
const nowMs = Date.now();
|
|
149507
|
-
const recentCandidates = candidateFiles.map((f2) => ({ f: f2, mtime: (0,
|
|
150165
|
+
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);
|
|
149508
150166
|
for (const { f: f2 } of recentCandidates) {
|
|
149509
150167
|
const candidatePath = (0, import_path29.join)(sessionsDir, f2);
|
|
149510
150168
|
const match2 = matchesProjectPath(candidatePath);
|
|
@@ -149516,7 +150174,7 @@ var StreamerServer = class {
|
|
|
149516
150174
|
this.sessionFileMap.set(sessionId, candidatePath);
|
|
149517
150175
|
this.fileWatcher.watch(candidatePath);
|
|
149518
150176
|
try {
|
|
149519
|
-
const existing = (0,
|
|
150177
|
+
const existing = (0, import_fs30.readFileSync)(candidatePath, "utf8").split("\n").filter(Boolean);
|
|
149520
150178
|
if (existing.length > 0) {
|
|
149521
150179
|
this.broadcastConversationLines(sessionId, existing);
|
|
149522
150180
|
}
|
|
@@ -149585,7 +150243,7 @@ var StreamerServer = class {
|
|
|
149585
150243
|
});
|
|
149586
150244
|
return;
|
|
149587
150245
|
}
|
|
149588
|
-
const body = await
|
|
150246
|
+
const body = await readBody2(req);
|
|
149589
150247
|
const { path: relativePath, name } = body;
|
|
149590
150248
|
if (!name || typeof name !== "string") {
|
|
149591
150249
|
json2(res, 400, { error: "Missing name field" });
|
|
@@ -149615,7 +150273,7 @@ var StreamerServer = class {
|
|
|
149615
150273
|
}
|
|
149616
150274
|
let parsed;
|
|
149617
150275
|
try {
|
|
149618
|
-
parsed = await
|
|
150276
|
+
parsed = await readBody2(req);
|
|
149619
150277
|
} catch {
|
|
149620
150278
|
json2(res, 400, { error: "Invalid JSON" });
|
|
149621
150279
|
return;
|
|
@@ -149672,7 +150330,7 @@ var StreamerServer = class {
|
|
|
149672
150330
|
}
|
|
149673
150331
|
let parsed;
|
|
149674
150332
|
try {
|
|
149675
|
-
parsed = await
|
|
150333
|
+
parsed = await readBody2(req);
|
|
149676
150334
|
} catch {
|
|
149677
150335
|
json2(res, 400, { error: "Invalid JSON" });
|
|
149678
150336
|
return;
|
|
@@ -149731,7 +150389,7 @@ async function waitForProcessExit(pid, timeoutMs, pollMs = ADOPT_KILL_POLL_MS) {
|
|
|
149731
150389
|
}
|
|
149732
150390
|
function classifyResumability(cwd) {
|
|
149733
150391
|
if (!cwd) return { resumable: true };
|
|
149734
|
-
if ((0,
|
|
150392
|
+
if ((0, import_fs30.existsSync)(cwd)) return { resumable: true };
|
|
149735
150393
|
const ranInWorktree = /\/\.worktrees\//.test(cwd) || /\/\.claude\/worktrees\//.test(cwd);
|
|
149736
150394
|
return {
|
|
149737
150395
|
resumable: false,
|
|
@@ -149849,7 +150507,7 @@ function parseSessionListQuery(url2) {
|
|
|
149849
150507
|
const cursor = url2.searchParams.get("cursor") ?? void 0;
|
|
149850
150508
|
return { query: { limit, sortBy, order, status, cursor } };
|
|
149851
150509
|
}
|
|
149852
|
-
function
|
|
150510
|
+
function readBody2(req) {
|
|
149853
150511
|
return new Promise((resolve4, reject) => {
|
|
149854
150512
|
const chunks = [];
|
|
149855
150513
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
@@ -150401,7 +151059,7 @@ var import_node_fs18 = require("fs");
|
|
|
150401
151059
|
|
|
150402
151060
|
// node_modules/tar/dist/esm/index.min.js
|
|
150403
151061
|
var import_events3 = __toESM(require("events"), 1);
|
|
150404
|
-
var
|
|
151062
|
+
var import_fs31 = __toESM(require("fs"), 1);
|
|
150405
151063
|
var import_node_events3 = require("events");
|
|
150406
151064
|
var import_node_stream2 = __toESM(require("stream"), 1);
|
|
150407
151065
|
var import_node_string_decoder = require("string_decoder");
|
|
@@ -150415,8 +151073,8 @@ var Ps = __toESM(require("zlib"), 1);
|
|
|
150415
151073
|
var import_zlib = __toESM(require("zlib"), 1);
|
|
150416
151074
|
var import_node_path15 = require("path");
|
|
150417
151075
|
var import_node_path16 = require("path");
|
|
150418
|
-
var import_fs31 = __toESM(require("fs"), 1);
|
|
150419
151076
|
var import_fs32 = __toESM(require("fs"), 1);
|
|
151077
|
+
var import_fs33 = __toESM(require("fs"), 1);
|
|
150420
151078
|
var import_path31 = __toESM(require("path"), 1);
|
|
150421
151079
|
var import_node_path17 = require("path");
|
|
150422
151080
|
var import_path32 = __toESM(require("path"), 1);
|
|
@@ -150425,7 +151083,7 @@ var import_node_assert = __toESM(require("assert"), 1);
|
|
|
150425
151083
|
var import_node_crypto6 = require("crypto");
|
|
150426
151084
|
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
150427
151085
|
var import_node_path18 = __toESM(require("path"), 1);
|
|
150428
|
-
var
|
|
151086
|
+
var import_fs34 = __toESM(require("fs"), 1);
|
|
150429
151087
|
var import_node_fs15 = __toESM(require("fs"), 1);
|
|
150430
151088
|
var import_node_path19 = __toESM(require("path"), 1);
|
|
150431
151089
|
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
@@ -150777,7 +151435,7 @@ var A = class extends import_node_events3.EventEmitter {
|
|
|
150777
151435
|
return Wr;
|
|
150778
151436
|
}
|
|
150779
151437
|
};
|
|
150780
|
-
var Jr =
|
|
151438
|
+
var Jr = import_fs31.default.writev;
|
|
150781
151439
|
var ht = /* @__PURE__ */ Symbol("_autoClose");
|
|
150782
151440
|
var H2 = /* @__PURE__ */ Symbol("_close");
|
|
150783
151441
|
var te = /* @__PURE__ */ Symbol("_ended");
|
|
@@ -150832,7 +151490,7 @@ var _t = class extends A {
|
|
|
150832
151490
|
throw new TypeError("this is a readable stream");
|
|
150833
151491
|
}
|
|
150834
151492
|
[at]() {
|
|
150835
|
-
|
|
151493
|
+
import_fs31.default.open(this[U], "r", (t, e) => this[Ht](t, e));
|
|
150836
151494
|
}
|
|
150837
151495
|
[Ht](t, e) {
|
|
150838
151496
|
t ? this[Ut](t) : (this[u] = e, this.emit("open", e), this[zt]());
|
|
@@ -150845,7 +151503,7 @@ var _t = class extends A {
|
|
|
150845
151503
|
this[j] = true;
|
|
150846
151504
|
let t = this[vi]();
|
|
150847
151505
|
if (t.length === 0) return process.nextTick(() => this[Ii](null, 0, t));
|
|
150848
|
-
|
|
151506
|
+
import_fs31.default.read(this[u], t, 0, t.length, null, (e, i, r) => this[Ii](e, i, r));
|
|
150849
151507
|
}
|
|
150850
151508
|
}
|
|
150851
151509
|
[Ii](t, e, i) {
|
|
@@ -150854,7 +151512,7 @@ var _t = class extends A {
|
|
|
150854
151512
|
[H2]() {
|
|
150855
151513
|
if (this[ht] && typeof this[u] == "number") {
|
|
150856
151514
|
let t = this[u];
|
|
150857
|
-
this[u] = void 0,
|
|
151515
|
+
this[u] = void 0, import_fs31.default.close(t, (e) => e ? this.emit("error", e) : this.emit("close"));
|
|
150858
151516
|
}
|
|
150859
151517
|
}
|
|
150860
151518
|
[Ut](t) {
|
|
@@ -150882,7 +151540,7 @@ var Be = class extends _t {
|
|
|
150882
151540
|
[at]() {
|
|
150883
151541
|
let t = true;
|
|
150884
151542
|
try {
|
|
150885
|
-
this[Ht](null,
|
|
151543
|
+
this[Ht](null, import_fs31.default.openSync(this[U], "r")), t = false;
|
|
150886
151544
|
} finally {
|
|
150887
151545
|
t && this[H2]();
|
|
150888
151546
|
}
|
|
@@ -150893,7 +151551,7 @@ var Be = class extends _t {
|
|
|
150893
151551
|
if (!this[j]) {
|
|
150894
151552
|
this[j] = true;
|
|
150895
151553
|
do {
|
|
150896
|
-
let e = this[vi](), i = e.length === 0 ? 0 :
|
|
151554
|
+
let e = this[vi](), i = e.length === 0 ? 0 : import_fs31.default.readSync(this[u], e, 0, e.length, null);
|
|
150897
151555
|
if (!this[ki](i, e)) break;
|
|
150898
151556
|
} while (true);
|
|
150899
151557
|
this[j] = false;
|
|
@@ -150906,7 +151564,7 @@ var Be = class extends _t {
|
|
|
150906
151564
|
[H2]() {
|
|
150907
151565
|
if (this[ht] && typeof this[u] == "number") {
|
|
150908
151566
|
let t = this[u];
|
|
150909
|
-
this[u] = void 0,
|
|
151567
|
+
this[u] = void 0, import_fs31.default.closeSync(t), this.emit("close");
|
|
150910
151568
|
}
|
|
150911
151569
|
}
|
|
150912
151570
|
};
|
|
@@ -150948,7 +151606,7 @@ var et = class extends import_events3.default {
|
|
|
150948
151606
|
this[H2](), this[gt] = true, this.emit("error", t);
|
|
150949
151607
|
}
|
|
150950
151608
|
[at]() {
|
|
150951
|
-
|
|
151609
|
+
import_fs31.default.open(this[U], this[tt], this[ie], (t, e) => this[Ht](t, e));
|
|
150952
151610
|
}
|
|
150953
151611
|
[Ht](t, e) {
|
|
150954
151612
|
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]());
|
|
@@ -150960,7 +151618,7 @@ var et = class extends import_events3.default {
|
|
|
150960
151618
|
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);
|
|
150961
151619
|
}
|
|
150962
151620
|
[ve](t) {
|
|
150963
|
-
|
|
151621
|
+
import_fs31.default.write(this[u], t, 0, t.length, this[ot], (e, i) => this[Pt](e, i));
|
|
150964
151622
|
}
|
|
150965
151623
|
[Pt](t, e) {
|
|
150966
151624
|
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"))));
|
|
@@ -150976,7 +151634,7 @@ var et = class extends import_events3.default {
|
|
|
150976
151634
|
[H2]() {
|
|
150977
151635
|
if (this[ht] && typeof this[u] == "number") {
|
|
150978
151636
|
let t = this[u];
|
|
150979
|
-
this[u] = void 0,
|
|
151637
|
+
this[u] = void 0, import_fs31.default.close(t, (e) => e ? this.emit("error", e) : this.emit("close"));
|
|
150980
151638
|
}
|
|
150981
151639
|
}
|
|
150982
151640
|
};
|
|
@@ -150984,24 +151642,24 @@ var Wt = class extends et {
|
|
|
150984
151642
|
[at]() {
|
|
150985
151643
|
let t;
|
|
150986
151644
|
if (this[Me] && this[tt] === "r+") try {
|
|
150987
|
-
t =
|
|
151645
|
+
t = import_fs31.default.openSync(this[U], this[tt], this[ie]);
|
|
150988
151646
|
} catch (e) {
|
|
150989
151647
|
if (e?.code === "ENOENT") return this[tt] = "w", this[at]();
|
|
150990
151648
|
throw e;
|
|
150991
151649
|
}
|
|
150992
|
-
else t =
|
|
151650
|
+
else t = import_fs31.default.openSync(this[U], this[tt], this[ie]);
|
|
150993
151651
|
this[Ht](null, t);
|
|
150994
151652
|
}
|
|
150995
151653
|
[H2]() {
|
|
150996
151654
|
if (this[ht] && typeof this[u] == "number") {
|
|
150997
151655
|
let t = this[u];
|
|
150998
|
-
this[u] = void 0,
|
|
151656
|
+
this[u] = void 0, import_fs31.default.closeSync(t), this.emit("close");
|
|
150999
151657
|
}
|
|
151000
151658
|
}
|
|
151001
151659
|
[ve](t) {
|
|
151002
151660
|
let e = true;
|
|
151003
151661
|
try {
|
|
151004
|
-
this[Pt](null,
|
|
151662
|
+
this[Pt](null, import_fs31.default.writeSync(this[u], t, 0, t.length, this[ot])), e = false;
|
|
151005
151663
|
} finally {
|
|
151006
151664
|
if (e) try {
|
|
151007
151665
|
this[H2]();
|
|
@@ -151919,7 +152577,7 @@ var de = class extends A {
|
|
|
151919
152577
|
return t === "error" && (this.#t = true), super.emit(t, ...e);
|
|
151920
152578
|
}
|
|
151921
152579
|
[ss]() {
|
|
151922
|
-
|
|
152580
|
+
import_fs33.default.lstat(this.absolute, (t, e) => {
|
|
151923
152581
|
if (t) return this.emit("error", t);
|
|
151924
152582
|
this[si](e);
|
|
151925
152583
|
});
|
|
@@ -151957,7 +152615,7 @@ var de = class extends A {
|
|
|
151957
152615
|
this.path.slice(-1) !== "/" && (this.path += "/"), this.stat.size = 0, this[fe](), this.end();
|
|
151958
152616
|
}
|
|
151959
152617
|
[is]() {
|
|
151960
|
-
|
|
152618
|
+
import_fs33.default.readlink(this.absolute, (t, e) => {
|
|
151961
152619
|
if (t) return this.emit("error", t);
|
|
151962
152620
|
this[ns](e);
|
|
151963
152621
|
});
|
|
@@ -151980,7 +152638,7 @@ var de = class extends A {
|
|
|
151980
152638
|
this[os]();
|
|
151981
152639
|
}
|
|
151982
152640
|
[os]() {
|
|
151983
|
-
|
|
152641
|
+
import_fs33.default.open(this.absolute, "r", (t, e) => {
|
|
151984
152642
|
if (t) return this.emit("error", t);
|
|
151985
152643
|
this[hs](e);
|
|
151986
152644
|
});
|
|
@@ -151995,14 +152653,14 @@ var de = class extends A {
|
|
|
151995
152653
|
[ii]() {
|
|
151996
152654
|
let { fd: t, buf: e, offset: i, length: r, pos: n } = this;
|
|
151997
152655
|
if (t === void 0 || e === void 0) throw new Error("cannot read file without first opening");
|
|
151998
|
-
|
|
152656
|
+
import_fs33.default.read(t, e, i, r, n, (o, h) => {
|
|
151999
152657
|
if (o) return this[pt](() => this.emit("error", o));
|
|
152000
152658
|
this[rs](h);
|
|
152001
152659
|
});
|
|
152002
152660
|
}
|
|
152003
152661
|
[pt](t = () => {
|
|
152004
152662
|
}) {
|
|
152005
|
-
this.fd !== void 0 &&
|
|
152663
|
+
this.fd !== void 0 && import_fs33.default.close(this.fd, t);
|
|
152006
152664
|
}
|
|
152007
152665
|
[rs](t) {
|
|
152008
152666
|
if (t <= 0 && this.remain > 0) {
|
|
@@ -152037,20 +152695,20 @@ var de = class extends A {
|
|
|
152037
152695
|
var ni = class extends de {
|
|
152038
152696
|
sync = true;
|
|
152039
152697
|
[ss]() {
|
|
152040
|
-
this[si](
|
|
152698
|
+
this[si](import_fs33.default.lstatSync(this.absolute));
|
|
152041
152699
|
}
|
|
152042
152700
|
[is]() {
|
|
152043
|
-
this[ns](
|
|
152701
|
+
this[ns](import_fs33.default.readlinkSync(this.absolute));
|
|
152044
152702
|
}
|
|
152045
152703
|
[os]() {
|
|
152046
|
-
this[hs](
|
|
152704
|
+
this[hs](import_fs33.default.openSync(this.absolute, "r"));
|
|
152047
152705
|
}
|
|
152048
152706
|
[ii]() {
|
|
152049
152707
|
let t = true;
|
|
152050
152708
|
try {
|
|
152051
152709
|
let { fd: e, buf: i, offset: r, length: n, pos: o } = this;
|
|
152052
152710
|
if (e === void 0 || i === void 0) throw new Error("fd and buf must be set in READ method");
|
|
152053
|
-
let h =
|
|
152711
|
+
let h = import_fs33.default.readSync(e, i, r, n, o);
|
|
152054
152712
|
this[rs](h), t = false;
|
|
152055
152713
|
} finally {
|
|
152056
152714
|
if (t) try {
|
|
@@ -152065,7 +152723,7 @@ var ni = class extends de {
|
|
|
152065
152723
|
}
|
|
152066
152724
|
[pt](t = () => {
|
|
152067
152725
|
}) {
|
|
152068
|
-
this.fd !== void 0 &&
|
|
152726
|
+
this.fd !== void 0 && import_fs33.default.closeSync(this.fd), t();
|
|
152069
152727
|
}
|
|
152070
152728
|
};
|
|
152071
152729
|
var oi = class extends A {
|
|
@@ -152396,7 +153054,7 @@ var wt = class extends A {
|
|
|
152396
153054
|
[ds](t) {
|
|
152397
153055
|
t.pending = true, this[G] += 1;
|
|
152398
153056
|
let e = this.follow ? "stat" : "lstat";
|
|
152399
|
-
|
|
153057
|
+
import_fs32.default[e](t.absolute, (i, r) => {
|
|
152400
153058
|
t.pending = false, this[G] -= 1, i ? this.emit("error", i) : this[li](t, r);
|
|
152401
153059
|
});
|
|
152402
153060
|
}
|
|
@@ -152410,7 +153068,7 @@ var wt = class extends A {
|
|
|
152410
153068
|
this[Ft]();
|
|
152411
153069
|
}
|
|
152412
153070
|
[ms](t) {
|
|
152413
|
-
t.pending = true, this[G] += 1,
|
|
153071
|
+
t.pending = true, this[G] += 1, import_fs32.default.readdir(t.absolute, (e, i) => {
|
|
152414
153072
|
if (t.pending = false, this[G] -= 1, e) return this.emit("error", e);
|
|
152415
153073
|
this[fi](t, i);
|
|
152416
153074
|
});
|
|
@@ -152511,10 +153169,10 @@ var kt = class extends wt {
|
|
|
152511
153169
|
}
|
|
152512
153170
|
[ds](t) {
|
|
152513
153171
|
let e = this.follow ? "statSync" : "lstatSync";
|
|
152514
|
-
this[li](t,
|
|
153172
|
+
this[li](t, import_fs32.default[e](t.absolute));
|
|
152515
153173
|
}
|
|
152516
153174
|
[ms](t) {
|
|
152517
|
-
this[fi](t,
|
|
153175
|
+
this[fi](t, import_fs32.default.readdirSync(t.absolute));
|
|
152518
153176
|
}
|
|
152519
153177
|
[di](t) {
|
|
152520
153178
|
let e = t.entry, i = this.zip;
|
|
@@ -152565,8 +153223,8 @@ var Qn = K(Vn, $n, Xn, qn, (s3, t) => {
|
|
|
152565
153223
|
});
|
|
152566
153224
|
var Jn = process.env.__FAKE_PLATFORM__ || process.platform;
|
|
152567
153225
|
var Er = Jn === "win32";
|
|
152568
|
-
var { O_CREAT: wr, O_NOFOLLOW: mr, O_TRUNC: Sr, O_WRONLY: yr } =
|
|
152569
|
-
var Rr = Number(process.env.__FAKE_FS_O_FILENAME__) ||
|
|
153226
|
+
var { O_CREAT: wr, O_NOFOLLOW: mr, O_TRUNC: Sr, O_WRONLY: yr } = import_fs34.default.constants;
|
|
153227
|
+
var Rr = Number(process.env.__FAKE_FS_O_FILENAME__) || import_fs34.default.constants.UV_FS_O_FILEMAP || 0;
|
|
152570
153228
|
var jn = Er && !!Rr;
|
|
152571
153229
|
var to = 512 * 1024;
|
|
152572
153230
|
var eo = Rr | Sr | wr | yr;
|
|
@@ -153944,12 +154602,12 @@ program2.command("cache").description("Manage the local SQLite conversation cach
|
|
|
153944
154602
|
"Cache directory (default: ~/.threadbase/cache)",
|
|
153945
154603
|
`${process.env.HOME}/.threadbase/cache`
|
|
153946
154604
|
).action((opts) => {
|
|
153947
|
-
const { rmSync: rmSync6, existsSync:
|
|
154605
|
+
const { rmSync: rmSync6, existsSync: existsSync16 } = require("fs");
|
|
153948
154606
|
const { join: join29 } = require("path");
|
|
153949
154607
|
const dbPath = join29(opts.cacheDir, "cache.db");
|
|
153950
154608
|
for (const suffix of ["", "-shm", "-wal"]) {
|
|
153951
154609
|
const f2 = dbPath + suffix;
|
|
153952
|
-
if (
|
|
154610
|
+
if (existsSync16(f2)) {
|
|
153953
154611
|
rmSync6(f2);
|
|
153954
154612
|
log11.info(`Deleted ${f2}`, { path: f2 }, "console");
|
|
153955
154613
|
}
|