@threadbase-sh/streamer 1.23.1 → 1.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1175 -350
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +1157 -374
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -28
- package/dist/index.d.ts +89 -28
- package/dist/index.js +1027 -249
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -26621,7 +26621,7 @@ var require_transport = __commonJS({
|
|
|
26621
26621
|
"node_modules/pino/lib/transport.js"(exports2, module2) {
|
|
26622
26622
|
"use strict";
|
|
26623
26623
|
var { createRequire } = require("module");
|
|
26624
|
-
var { existsSync:
|
|
26624
|
+
var { existsSync: existsSync12 } = require("fs");
|
|
26625
26625
|
var getCallers = require_caller();
|
|
26626
26626
|
var { join: join26, isAbsolute: isAbsolute3, sep: sep2 } = require("path");
|
|
26627
26627
|
var { fileURLToPath: fileURLToPath3 } = require("url");
|
|
@@ -26695,7 +26695,7 @@ var require_transport = __commonJS({
|
|
|
26695
26695
|
return false;
|
|
26696
26696
|
}
|
|
26697
26697
|
}
|
|
26698
|
-
return isAbsolute3(path2) && !
|
|
26698
|
+
return isAbsolute3(path2) && !existsSync12(path2);
|
|
26699
26699
|
}
|
|
26700
26700
|
function stripQuotes(value) {
|
|
26701
26701
|
const first = value[0];
|
|
@@ -100885,8 +100885,8 @@ var require_pattern = __commonJS({
|
|
|
100885
100885
|
}
|
|
100886
100886
|
exports2.endsWithSlashGlobStar = endsWithSlashGlobStar;
|
|
100887
100887
|
function isAffectDepthOfReadingPattern(pattern) {
|
|
100888
|
-
const
|
|
100889
|
-
return endsWithSlashGlobStar(pattern) || isStaticPattern(
|
|
100888
|
+
const basename11 = path2.basename(pattern);
|
|
100889
|
+
return endsWithSlashGlobStar(pattern) || isStaticPattern(basename11);
|
|
100890
100890
|
}
|
|
100891
100891
|
exports2.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
|
|
100892
100892
|
function expandPatternsWithBraceExpansion(patterns2) {
|
|
@@ -124663,6 +124663,50 @@ var init_platform = __esm({
|
|
|
124663
124663
|
}
|
|
124664
124664
|
});
|
|
124665
124665
|
|
|
124666
|
+
// src/db/check-sqlite-abi.ts
|
|
124667
|
+
var check_sqlite_abi_exports = {};
|
|
124668
|
+
__export(check_sqlite_abi_exports, {
|
|
124669
|
+
SqliteAbiError: () => SqliteAbiError,
|
|
124670
|
+
checkSqliteAbi: () => checkSqliteAbi,
|
|
124671
|
+
isAbiMismatch: () => isAbiMismatch
|
|
124672
|
+
});
|
|
124673
|
+
function isAbiMismatch(message) {
|
|
124674
|
+
return ABI_MARKERS.some((m2) => message.includes(m2));
|
|
124675
|
+
}
|
|
124676
|
+
function checkSqliteAbi() {
|
|
124677
|
+
try {
|
|
124678
|
+
const Database3 = require("better-sqlite3");
|
|
124679
|
+
const db = new Database3(":memory:");
|
|
124680
|
+
db.close();
|
|
124681
|
+
} catch (err) {
|
|
124682
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
124683
|
+
if (isAbiMismatch(message)) {
|
|
124684
|
+
throw new SqliteAbiError(message);
|
|
124685
|
+
}
|
|
124686
|
+
throw err;
|
|
124687
|
+
}
|
|
124688
|
+
}
|
|
124689
|
+
var REBUILD_HINT, SqliteAbiError, ABI_MARKERS;
|
|
124690
|
+
var init_check_sqlite_abi = __esm({
|
|
124691
|
+
"src/db/check-sqlite-abi.ts"() {
|
|
124692
|
+
"use strict";
|
|
124693
|
+
REBUILD_HINT = "npm rebuild better-sqlite3";
|
|
124694
|
+
SqliteAbiError = class extends Error {
|
|
124695
|
+
constructor(cause) {
|
|
124696
|
+
super(
|
|
124697
|
+
`better-sqlite3 native module failed to load \u2014 likely a Node ABI mismatch (node_modules was built against a different Node version).
|
|
124698
|
+
Running Node: ${process.version} (NODE_MODULE_VERSION ${process.versions.modules})
|
|
124699
|
+
Fix: ${REBUILD_HINT}
|
|
124700
|
+
(or 'npm ci' after any dependency-affecting pull)
|
|
124701
|
+
Underlying error: ${cause}`
|
|
124702
|
+
);
|
|
124703
|
+
this.name = "SqliteAbiError";
|
|
124704
|
+
}
|
|
124705
|
+
};
|
|
124706
|
+
ABI_MARKERS = ["NODE_MODULE_VERSION", "was compiled against a different Node.js version"];
|
|
124707
|
+
}
|
|
124708
|
+
});
|
|
124709
|
+
|
|
124666
124710
|
// src/lifecycle/prefs.ts
|
|
124667
124711
|
function readPrefs() {
|
|
124668
124712
|
const path2 = prefsPath();
|
|
@@ -129594,10 +129638,10 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
129594
129638
|
}
|
|
129595
129639
|
async _formatEntry(dirent, path2) {
|
|
129596
129640
|
let entry;
|
|
129597
|
-
const
|
|
129641
|
+
const basename11 = this._isDirent ? dirent.name : dirent;
|
|
129598
129642
|
try {
|
|
129599
|
-
const fullPath = (0, import_node_path5.resolve)((0, import_node_path5.join)(path2,
|
|
129600
|
-
entry = { path: (0, import_node_path5.relative)(this._root, fullPath), fullPath, basename:
|
|
129643
|
+
const fullPath = (0, import_node_path5.resolve)((0, import_node_path5.join)(path2, basename11));
|
|
129644
|
+
entry = { path: (0, import_node_path5.relative)(this._root, fullPath), fullPath, basename: basename11 };
|
|
129601
129645
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
129602
129646
|
} catch (err) {
|
|
129603
129647
|
this._onError(err);
|
|
@@ -130136,9 +130180,9 @@ var NodeFsHandler = class {
|
|
|
130136
130180
|
_watchWithNodeFs(path2, listener) {
|
|
130137
130181
|
const opts = this.fsw.options;
|
|
130138
130182
|
const directory = sysPath.dirname(path2);
|
|
130139
|
-
const
|
|
130183
|
+
const basename11 = sysPath.basename(path2);
|
|
130140
130184
|
const parent = this.fsw._getWatchedDir(directory);
|
|
130141
|
-
parent.add(
|
|
130185
|
+
parent.add(basename11);
|
|
130142
130186
|
const absolutePath = sysPath.resolve(path2);
|
|
130143
130187
|
const options = {
|
|
130144
130188
|
persistent: opts.persistent
|
|
@@ -130148,7 +130192,7 @@ var NodeFsHandler = class {
|
|
|
130148
130192
|
let closer;
|
|
130149
130193
|
if (opts.usePolling) {
|
|
130150
130194
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
130151
|
-
options.interval = enableBin && isBinaryPath(
|
|
130195
|
+
options.interval = enableBin && isBinaryPath(basename11) ? opts.binaryInterval : opts.interval;
|
|
130152
130196
|
closer = setFsWatchFileListener(path2, absolutePath, options, {
|
|
130153
130197
|
listener,
|
|
130154
130198
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -130171,10 +130215,10 @@ var NodeFsHandler = class {
|
|
|
130171
130215
|
return;
|
|
130172
130216
|
}
|
|
130173
130217
|
const dirname17 = sysPath.dirname(file2);
|
|
130174
|
-
const
|
|
130218
|
+
const basename11 = sysPath.basename(file2);
|
|
130175
130219
|
const parent = this.fsw._getWatchedDir(dirname17);
|
|
130176
130220
|
let prevStats = stats;
|
|
130177
|
-
if (parent.has(
|
|
130221
|
+
if (parent.has(basename11))
|
|
130178
130222
|
return;
|
|
130179
130223
|
const listener = async (path2, newStats) => {
|
|
130180
130224
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file2, 5))
|
|
@@ -130199,9 +130243,9 @@ var NodeFsHandler = class {
|
|
|
130199
130243
|
prevStats = newStats2;
|
|
130200
130244
|
}
|
|
130201
130245
|
} catch (error51) {
|
|
130202
|
-
this.fsw._remove(dirname17,
|
|
130246
|
+
this.fsw._remove(dirname17, basename11);
|
|
130203
130247
|
}
|
|
130204
|
-
} else if (parent.has(
|
|
130248
|
+
} else if (parent.has(basename11)) {
|
|
130205
130249
|
const at2 = newStats.atimeMs;
|
|
130206
130250
|
const mt2 = newStats.mtimeMs;
|
|
130207
130251
|
if (!at2 || at2 <= mt2 || mt2 !== prevStats.mtimeMs) {
|
|
@@ -133973,11 +134017,11 @@ async function search(query, options, scanner) {
|
|
|
133973
134017
|
|
|
133974
134018
|
// src/server.ts
|
|
133975
134019
|
var import_events3 = require("events");
|
|
133976
|
-
var
|
|
134020
|
+
var import_fs24 = require("fs");
|
|
133977
134021
|
var import_promises14 = require("fs/promises");
|
|
133978
134022
|
var import_http = require("http");
|
|
133979
134023
|
var import_os10 = require("os");
|
|
133980
|
-
var
|
|
134024
|
+
var import_path21 = require("path");
|
|
133981
134025
|
var import_readline4 = require("readline");
|
|
133982
134026
|
|
|
133983
134027
|
// src/agent/agent-client.ts
|
|
@@ -137104,8 +137148,10 @@ function runSqliteMigrations(db, migrationsDir) {
|
|
|
137104
137148
|
// src/providers.ts
|
|
137105
137149
|
var CLAUDE_CODE_PROVIDER2 = "claude-code";
|
|
137106
137150
|
var CODEX_CLI_PROVIDER2 = "codex-cli";
|
|
137107
|
-
function
|
|
137108
|
-
|
|
137151
|
+
function isProviderName(value) {
|
|
137152
|
+
return value === CLAUDE_CODE_PROVIDER2 || value === CODEX_CLI_PROVIDER2;
|
|
137153
|
+
}
|
|
137154
|
+
function isProviderResumable(_provider, availabilityResumable) {
|
|
137109
137155
|
return availabilityResumable;
|
|
137110
137156
|
}
|
|
137111
137157
|
|
|
@@ -138182,67 +138228,15 @@ function handleListProjects(url2, res) {
|
|
|
138182
138228
|
res.end(JSON.stringify({ projects: page, total }));
|
|
138183
138229
|
}
|
|
138184
138230
|
|
|
138185
|
-
// src/
|
|
138186
|
-
|
|
138231
|
+
// src/live-session-manager.ts
|
|
138232
|
+
var import_path18 = require("path");
|
|
138187
138233
|
|
|
138188
|
-
// src/
|
|
138234
|
+
// src/codex-pty-runner.ts
|
|
138235
|
+
var import_headless = __toESM(require_xterm_headless(), 1);
|
|
138189
138236
|
var import_crypto5 = require("crypto");
|
|
138190
|
-
var
|
|
138191
|
-
var SWEEP_INTERVAL_MS = 6e4;
|
|
138192
|
-
var PairTokenStore = class {
|
|
138193
|
-
current = null;
|
|
138194
|
-
ttlMs;
|
|
138195
|
-
sweepTimer = null;
|
|
138196
|
-
constructor(opts = {}) {
|
|
138197
|
-
this.ttlMs = (opts.ttlSeconds ?? DEFAULT_TTL_SECONDS) * 1e3;
|
|
138198
|
-
if (opts.autoSweep !== false) {
|
|
138199
|
-
this.sweepTimer = setInterval(() => this.sweep(), SWEEP_INTERVAL_MS);
|
|
138200
|
-
this.sweepTimer.unref?.();
|
|
138201
|
-
}
|
|
138202
|
-
}
|
|
138203
|
-
mint() {
|
|
138204
|
-
const token = `pt_${(0, import_crypto5.randomBytes)(16).toString("hex")}`;
|
|
138205
|
-
const expiresAt = Date.now() + this.ttlMs;
|
|
138206
|
-
this.current = { token, expiresAt, used: false };
|
|
138207
|
-
return {
|
|
138208
|
-
token,
|
|
138209
|
-
expiresAt,
|
|
138210
|
-
expiresInSeconds: Math.floor(this.ttlMs / 1e3)
|
|
138211
|
-
};
|
|
138212
|
-
}
|
|
138213
|
-
consume(token) {
|
|
138214
|
-
const record2 = this.current;
|
|
138215
|
-
if (!record2 || record2.token !== token) return { ok: false, reason: "unknown" };
|
|
138216
|
-
if (Date.now() > record2.expiresAt) {
|
|
138217
|
-
this.current = null;
|
|
138218
|
-
return { ok: false, reason: "expired" };
|
|
138219
|
-
}
|
|
138220
|
-
if (record2.used) return { ok: false, reason: "used" };
|
|
138221
|
-
record2.used = true;
|
|
138222
|
-
return { ok: true };
|
|
138223
|
-
}
|
|
138224
|
-
peek() {
|
|
138225
|
-
return this.current;
|
|
138226
|
-
}
|
|
138227
|
-
clear() {
|
|
138228
|
-
this.current = null;
|
|
138229
|
-
}
|
|
138230
|
-
sweep() {
|
|
138231
|
-
if (this.current && Date.now() > this.current.expiresAt) {
|
|
138232
|
-
this.current = null;
|
|
138233
|
-
}
|
|
138234
|
-
}
|
|
138235
|
-
dispose() {
|
|
138236
|
-
if (this.sweepTimer) clearInterval(this.sweepTimer);
|
|
138237
|
-
this.sweepTimer = null;
|
|
138238
|
-
this.current = null;
|
|
138239
|
-
}
|
|
138240
|
-
};
|
|
138241
|
-
|
|
138242
|
-
// src/process-discovery.ts
|
|
138243
|
-
var import_child_process2 = require("child_process");
|
|
138244
|
-
var import_os9 = require("os");
|
|
138237
|
+
var import_fs20 = require("fs");
|
|
138245
138238
|
var import_path16 = require("path");
|
|
138239
|
+
init_logger();
|
|
138246
138240
|
|
|
138247
138241
|
// src/platform.ts
|
|
138248
138242
|
var import_child_process = require("child_process");
|
|
@@ -138308,151 +138302,523 @@ function resolveClaudeExe() {
|
|
|
138308
138302
|
_claudeExe = "claude";
|
|
138309
138303
|
return _claudeExe;
|
|
138310
138304
|
}
|
|
138311
|
-
|
|
138312
|
-
|
|
138313
|
-
|
|
138314
|
-
if (
|
|
138315
|
-
|
|
138316
|
-
|
|
138317
|
-
|
|
138318
|
-
|
|
138319
|
-
|
|
138320
|
-
|
|
138321
|
-
|
|
138322
|
-
|
|
138323
|
-
|
|
138324
|
-
getProcessArgsUnix(pid),
|
|
138325
|
-
getProcessStartTimeUnix(pid)
|
|
138326
|
-
]);
|
|
138327
|
-
const conversationId = extractResumeId(args);
|
|
138328
|
-
return {
|
|
138329
|
-
pid,
|
|
138330
|
-
projectPath: cwd,
|
|
138331
|
-
projectName: (0, import_path16.basename)(cwd),
|
|
138332
|
-
branch: await readGitBranch2(cwd),
|
|
138333
|
-
conversationId,
|
|
138334
|
-
startedAt
|
|
138335
|
-
};
|
|
138336
|
-
} catch {
|
|
138337
|
-
return null;
|
|
138305
|
+
var _codexExe;
|
|
138306
|
+
function resolveCodexExe() {
|
|
138307
|
+
if (_codexExe !== void 0) return _codexExe;
|
|
138308
|
+
if (isWindows2) {
|
|
138309
|
+
try {
|
|
138310
|
+
const found = (0, import_child_process.execFileSync)("where.exe", ["codex"], {
|
|
138311
|
+
encoding: "utf-8",
|
|
138312
|
+
windowsHide: true,
|
|
138313
|
+
timeout: 3e3
|
|
138314
|
+
}).trim().split("\n")[0].trim();
|
|
138315
|
+
if (found) {
|
|
138316
|
+
_codexExe = found;
|
|
138317
|
+
return _codexExe;
|
|
138338
138318
|
}
|
|
138339
|
-
}
|
|
138340
|
-
|
|
138341
|
-
|
|
138342
|
-
|
|
138343
|
-
|
|
138344
|
-
|
|
138345
|
-
|
|
138346
|
-
|
|
138347
|
-
|
|
138348
|
-
|
|
138349
|
-
|
|
138350
|
-
|
|
138351
|
-
|
|
138352
|
-
|
|
138353
|
-
|
|
138354
|
-
branch: await readGitBranch2(info.cwd),
|
|
138355
|
-
conversationId: extractResumeId(info.args),
|
|
138356
|
-
startedAt: info.startedAt
|
|
138357
|
-
};
|
|
138358
|
-
} catch {
|
|
138359
|
-
return null;
|
|
138319
|
+
} catch {
|
|
138320
|
+
}
|
|
138321
|
+
const candidates = [
|
|
138322
|
+
(0, import_path15.join)((0, import_os8.homedir)(), ".local", "bin", "codex.exe"),
|
|
138323
|
+
(0, import_path15.join)(
|
|
138324
|
+
process.env.LOCALAPPDATA ?? (0, import_path15.join)((0, import_os8.homedir)(), "AppData", "Local"),
|
|
138325
|
+
"Microsoft",
|
|
138326
|
+
"WindowsApps",
|
|
138327
|
+
"codex.exe"
|
|
138328
|
+
)
|
|
138329
|
+
];
|
|
138330
|
+
for (const p2 of candidates) {
|
|
138331
|
+
if ((0, import_fs19.existsSync)(p2)) {
|
|
138332
|
+
_codexExe = p2;
|
|
138333
|
+
return _codexExe;
|
|
138360
138334
|
}
|
|
138361
|
-
}
|
|
138362
|
-
|
|
138363
|
-
|
|
138364
|
-
|
|
138365
|
-
|
|
138366
|
-
|
|
138367
|
-
|
|
138368
|
-
|
|
138369
|
-
|
|
138370
|
-
|
|
138371
|
-
(err, stdout2) => {
|
|
138372
|
-
if (err) reject(err);
|
|
138373
|
-
else resolve6(stdout2);
|
|
138335
|
+
}
|
|
138336
|
+
} else {
|
|
138337
|
+
try {
|
|
138338
|
+
const found = (0, import_child_process.execFileSync)("/usr/bin/which", ["codex"], {
|
|
138339
|
+
encoding: "utf-8",
|
|
138340
|
+
timeout: 3e3
|
|
138341
|
+
}).trim().split("\n")[0].trim();
|
|
138342
|
+
if (found && (0, import_fs19.existsSync)(found)) {
|
|
138343
|
+
_codexExe = found;
|
|
138344
|
+
return _codexExe;
|
|
138374
138345
|
}
|
|
138375
|
-
|
|
138376
|
-
|
|
138377
|
-
|
|
138378
|
-
|
|
138379
|
-
|
|
138380
|
-
|
|
138381
|
-
|
|
138382
|
-
|
|
138383
|
-
|
|
138346
|
+
} catch {
|
|
138347
|
+
}
|
|
138348
|
+
const candidates = [
|
|
138349
|
+
"/opt/homebrew/bin/codex",
|
|
138350
|
+
"/usr/local/bin/codex",
|
|
138351
|
+
(0, import_path15.join)((0, import_os8.homedir)(), ".local", "bin", "codex")
|
|
138352
|
+
];
|
|
138353
|
+
for (const p2 of candidates) {
|
|
138354
|
+
if ((0, import_fs19.existsSync)(p2)) {
|
|
138355
|
+
_codexExe = p2;
|
|
138356
|
+
return _codexExe;
|
|
138357
|
+
}
|
|
138358
|
+
}
|
|
138384
138359
|
}
|
|
138360
|
+
_codexExe = "codex";
|
|
138361
|
+
return _codexExe;
|
|
138385
138362
|
}
|
|
138386
|
-
|
|
138387
|
-
|
|
138388
|
-
|
|
138389
|
-
|
|
138390
|
-
|
|
138391
|
-
|
|
138392
|
-
|
|
138393
|
-
|
|
138394
|
-
|
|
138395
|
-
|
|
138396
|
-
|
|
138397
|
-
|
|
138398
|
-
|
|
138399
|
-
|
|
138400
|
-
try {
|
|
138401
|
-
const output = await run("tasklist", ["/FI", "IMAGENAME eq claude.exe", "/FO", "CSV", "/NH"]);
|
|
138402
|
-
return output.trim().split("\n").filter(Boolean).map((line) => {
|
|
138403
|
-
const parts = line.split(",");
|
|
138404
|
-
return Number.parseInt(parts[1]?.replace(/"/g, "") ?? "0", 10);
|
|
138405
|
-
}).filter((pid) => pid > 0);
|
|
138406
|
-
} catch {
|
|
138407
|
-
return [];
|
|
138408
|
-
}
|
|
138363
|
+
|
|
138364
|
+
// src/codex-pty-runner.ts
|
|
138365
|
+
var OUTPUT_BUFFER_MAX = 65536;
|
|
138366
|
+
var PTY_COLS = 120;
|
|
138367
|
+
var PTY_ROWS = 40;
|
|
138368
|
+
var SCREEN_SCROLLBACK = 1e3;
|
|
138369
|
+
var CODEX_PROMPT_READY_TEXT = "Ready";
|
|
138370
|
+
var CODEX_TRUST_GATE_REGEX = /trust the contents/i;
|
|
138371
|
+
var SUBMIT_BYTES = "\r";
|
|
138372
|
+
var CODEX_SUBMIT_DELAY_MS = 16;
|
|
138373
|
+
function digestBytes(s3) {
|
|
138374
|
+
const escaped = s3.replace(new RegExp(String.fromCharCode(27), "g"), "\\x1b").replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\t/g, "\\t");
|
|
138375
|
+
if (escaped.length <= 200) return escaped;
|
|
138376
|
+
return `${escaped.slice(0, 100)}\u2026[${escaped.length - 200}B omitted]\u2026${escaped.slice(-100)}`;
|
|
138409
138377
|
}
|
|
138410
|
-
|
|
138378
|
+
var pty = null;
|
|
138379
|
+
async function loadPty() {
|
|
138380
|
+
if (pty) return pty;
|
|
138411
138381
|
try {
|
|
138412
|
-
|
|
138413
|
-
|
|
138414
|
-
|
|
138415
|
-
|
|
138416
|
-
|
|
138417
|
-
|
|
138418
|
-
|
|
138419
|
-
]);
|
|
138420
|
-
const lines = output.trim().split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
138421
|
-
if (lines.length < 2) return null;
|
|
138422
|
-
const parts = lines[1].split(",");
|
|
138423
|
-
const args = parts[1] ?? "";
|
|
138424
|
-
const creationDate = parts[2] ?? "";
|
|
138425
|
-
const year = creationDate.slice(0, 4);
|
|
138426
|
-
const month = creationDate.slice(4, 6);
|
|
138427
|
-
const day = creationDate.slice(6, 8);
|
|
138428
|
-
const hour = creationDate.slice(8, 10);
|
|
138429
|
-
const min = creationDate.slice(10, 12);
|
|
138430
|
-
const sec = creationDate.slice(12, 14);
|
|
138431
|
-
const startedAt = /* @__PURE__ */ new Date(`${year}-${month}-${day}T${hour}:${min}:${sec}`);
|
|
138432
|
-
if (Number.isNaN(startedAt.getTime())) return null;
|
|
138433
|
-
const exePath = parts[3] ?? "";
|
|
138434
|
-
const cwd = exePath ? (0, import_path16.dirname)(exePath) : "";
|
|
138435
|
-
return { cwd, args, startedAt };
|
|
138436
|
-
} catch {
|
|
138437
|
-
return null;
|
|
138382
|
+
pty = await import("node-pty");
|
|
138383
|
+
return pty;
|
|
138384
|
+
} catch (err) {
|
|
138385
|
+
throw new Error(
|
|
138386
|
+
`node-pty is required for PTY management but failed to load. Ensure it is installed: npm install node-pty
|
|
138387
|
+
Original error: ${err}`
|
|
138388
|
+
);
|
|
138438
138389
|
}
|
|
138439
138390
|
}
|
|
138440
|
-
function
|
|
138441
|
-
|
|
138442
|
-
|
|
138391
|
+
function createScreen() {
|
|
138392
|
+
return new import_headless.Terminal({
|
|
138393
|
+
cols: PTY_COLS,
|
|
138394
|
+
rows: PTY_ROWS,
|
|
138395
|
+
scrollback: SCREEN_SCROLLBACK,
|
|
138396
|
+
allowProposedApi: true
|
|
138397
|
+
});
|
|
138443
138398
|
}
|
|
138444
|
-
|
|
138445
|
-
|
|
138446
|
-
|
|
138447
|
-
|
|
138448
|
-
|
|
138399
|
+
var CodexPtyRunner = class {
|
|
138400
|
+
sessions = /* @__PURE__ */ new Map();
|
|
138401
|
+
onOutput;
|
|
138402
|
+
onStatusChange;
|
|
138403
|
+
onReady;
|
|
138404
|
+
// Accepted for shape-compatibility with PTYManagerOptions; Codex has no
|
|
138405
|
+
// detected equivalent yet (Phase 0) — never invoked.
|
|
138406
|
+
onPermissionChange;
|
|
138407
|
+
onLiveQuestion;
|
|
138408
|
+
onLiveQuestionGone;
|
|
138409
|
+
log;
|
|
138410
|
+
// Tracks sessions whose PTY has spawned but Codex hasn't yet reached its
|
|
138411
|
+
// "Ready" status bar — i.e. onReady hasn't fired.
|
|
138412
|
+
pendingReady = /* @__PURE__ */ new Set();
|
|
138413
|
+
// Inputs received via sendInput() while the session was still pendingReady.
|
|
138414
|
+
// Flushed in arrival order once Codex reaches Ready.
|
|
138415
|
+
queuedInputs = /* @__PURE__ */ new Map();
|
|
138416
|
+
// Per-session debounce so the directory-trust gate's \r is only written once.
|
|
138417
|
+
trustGateAnswered = /* @__PURE__ */ new Set();
|
|
138418
|
+
// In-flight start()/startFresh() calls keyed by sessionId. A second
|
|
138419
|
+
// concurrent resume for the same session (double-tap, client retry) awaits
|
|
138420
|
+
// the first call's promise instead of spawning a duplicate PTY (CRITICAL #3).
|
|
138421
|
+
startPromises = /* @__PURE__ */ new Map();
|
|
138422
|
+
constructor(options = {}) {
|
|
138423
|
+
this.onOutput = options.onOutput;
|
|
138424
|
+
this.onStatusChange = options.onStatusChange;
|
|
138425
|
+
this.onReady = options.onReady;
|
|
138426
|
+
this.onPermissionChange = options.onPermissionChange;
|
|
138427
|
+
this.onLiveQuestion = options.onLiveQuestion;
|
|
138428
|
+
this.onLiveQuestionGone = options.onLiveQuestionGone;
|
|
138429
|
+
this.log = options.logger ?? getLogger("codex-pty");
|
|
138430
|
+
}
|
|
138431
|
+
// Resume an existing Codex session. sessionId is the Codex-persisted
|
|
138432
|
+
// session_meta.payload.id (Phase 0, Section 8) — Codex has no fresh-session
|
|
138433
|
+
// equivalent of --session-id, so start() always means "resume".
|
|
138434
|
+
async start(sessionId, options) {
|
|
138435
|
+
const existing = this.sessions.get(sessionId);
|
|
138436
|
+
if (existing) return toPublicSession(existing);
|
|
138437
|
+
const inFlight = this.startPromises.get(sessionId);
|
|
138438
|
+
if (inFlight) return inFlight;
|
|
138439
|
+
const promise2 = this.doStart(sessionId, options).finally(() => {
|
|
138440
|
+
this.startPromises.delete(sessionId);
|
|
138441
|
+
});
|
|
138442
|
+
this.startPromises.set(sessionId, promise2);
|
|
138443
|
+
return promise2;
|
|
138444
|
+
}
|
|
138445
|
+
async doStart(sessionId, options) {
|
|
138446
|
+
const nodePty = await loadPty();
|
|
138447
|
+
const projectName = options.projectName ?? (0, import_path16.basename)(options.projectPath);
|
|
138448
|
+
const proc = nodePty.spawn(
|
|
138449
|
+
resolveCodexExe(),
|
|
138450
|
+
["resume", sessionId, "--cd", options.projectPath, "--no-alt-screen"],
|
|
138451
|
+
{
|
|
138452
|
+
name: "xterm-256color",
|
|
138453
|
+
cols: PTY_COLS,
|
|
138454
|
+
rows: PTY_ROWS,
|
|
138455
|
+
cwd: options.projectPath,
|
|
138456
|
+
env: process.env
|
|
138457
|
+
}
|
|
138458
|
+
);
|
|
138459
|
+
const session = {
|
|
138460
|
+
id: sessionId,
|
|
138461
|
+
provider: CODEX_CLI_PROVIDER2,
|
|
138462
|
+
projectPath: options.projectPath,
|
|
138463
|
+
projectName,
|
|
138464
|
+
branch: options.branch ?? "",
|
|
138465
|
+
status: "running",
|
|
138466
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
138467
|
+
completedAt: null,
|
|
138468
|
+
promptCount: 0,
|
|
138469
|
+
lastOutput: "",
|
|
138470
|
+
process: proc,
|
|
138471
|
+
outputBuffer: Buffer.alloc(0),
|
|
138472
|
+
screen: createScreen()
|
|
138473
|
+
};
|
|
138474
|
+
this.sessions.set(sessionId, session);
|
|
138475
|
+
this.pendingReady.add(sessionId);
|
|
138476
|
+
proc.onData((data) => {
|
|
138477
|
+
this.handleOutput(sessionId, data);
|
|
138478
|
+
});
|
|
138479
|
+
proc.onExit(({ exitCode }) => {
|
|
138480
|
+
this.pendingReady.delete(sessionId);
|
|
138481
|
+
this.handleExit(sessionId, exitCode);
|
|
138482
|
+
});
|
|
138483
|
+
return toPublicSession(session);
|
|
138484
|
+
}
|
|
138485
|
+
// Start a brand-new Codex session. Codex has no --session-id equivalent for
|
|
138486
|
+
// a fresh launch — it assigns its own id, discovered later (Task 3's
|
|
138487
|
+
// binding logic). This runner generates a local placeholder id for the
|
|
138488
|
+
// ManagedSession handle only.
|
|
138489
|
+
async startFresh(options) {
|
|
138490
|
+
const nodePty = await loadPty();
|
|
138491
|
+
const sessionId = (0, import_crypto5.randomUUID)();
|
|
138492
|
+
const projectName = options.projectName ?? (0, import_path16.basename)(options.projectPath);
|
|
138493
|
+
const args = ["--cd", options.projectPath, "--no-alt-screen"];
|
|
138494
|
+
if (options.systemPrompt) {
|
|
138495
|
+
args.push(options.systemPrompt);
|
|
138496
|
+
}
|
|
138497
|
+
const proc = nodePty.spawn(resolveCodexExe(), args, {
|
|
138498
|
+
name: "xterm-256color",
|
|
138499
|
+
cols: PTY_COLS,
|
|
138500
|
+
rows: PTY_ROWS,
|
|
138501
|
+
cwd: options.projectPath,
|
|
138502
|
+
env: process.env
|
|
138503
|
+
});
|
|
138504
|
+
const session = {
|
|
138505
|
+
id: sessionId,
|
|
138506
|
+
provider: CODEX_CLI_PROVIDER2,
|
|
138507
|
+
projectPath: options.projectPath,
|
|
138508
|
+
projectName,
|
|
138509
|
+
branch: "",
|
|
138510
|
+
status: "running",
|
|
138511
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
138512
|
+
completedAt: null,
|
|
138513
|
+
promptCount: 0,
|
|
138514
|
+
lastOutput: "",
|
|
138515
|
+
process: proc,
|
|
138516
|
+
outputBuffer: Buffer.alloc(0),
|
|
138517
|
+
screen: createScreen()
|
|
138518
|
+
};
|
|
138519
|
+
this.sessions.set(sessionId, session);
|
|
138520
|
+
this.pendingReady.add(sessionId);
|
|
138521
|
+
proc.onData((data) => {
|
|
138522
|
+
this.handleOutput(sessionId, data);
|
|
138523
|
+
});
|
|
138524
|
+
proc.onExit(({ exitCode }) => {
|
|
138525
|
+
this.pendingReady.delete(sessionId);
|
|
138526
|
+
this.handleExit(sessionId, exitCode);
|
|
138527
|
+
});
|
|
138528
|
+
return toPublicSession(session);
|
|
138529
|
+
}
|
|
138530
|
+
// Write raw key bytes directly to the PTY, same as PTYManager.sendKeys.
|
|
138531
|
+
sendKeys(sessionId, keys) {
|
|
138532
|
+
const session = this.sessions.get(sessionId);
|
|
138533
|
+
if (!session) throw new Error(`Session not found: ${sessionId}`);
|
|
138534
|
+
if (session.status === "idle") {
|
|
138535
|
+
throw new Error(`Session is idle (no active PTY): ${sessionId}`);
|
|
138536
|
+
}
|
|
138537
|
+
if (session.status === "waiting_input") {
|
|
138538
|
+
session.status = "running";
|
|
138539
|
+
this.onStatusChange?.(toPublicSession(session));
|
|
138540
|
+
}
|
|
138541
|
+
this.log.info(
|
|
138542
|
+
`[codex.keys.write] ${sessionId.slice(0, 8)} bytes=${keys.length} digest=${digestBytes(keys)}`,
|
|
138543
|
+
{ event: "codex.keys_write", sessionId, byteLen: keys.length }
|
|
138544
|
+
);
|
|
138545
|
+
session.process.write(keys);
|
|
138546
|
+
session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
138547
|
+
}
|
|
138548
|
+
sendInput(sessionId, input) {
|
|
138549
|
+
const session = this.sessions.get(sessionId);
|
|
138550
|
+
if (!session) throw new Error(`Session not found: ${sessionId}`);
|
|
138551
|
+
if (session.status === "idle") {
|
|
138552
|
+
throw new Error(`Session is idle (no active PTY): ${sessionId}`);
|
|
138553
|
+
}
|
|
138554
|
+
if (this.pendingReady.has(sessionId)) {
|
|
138555
|
+
const queue = this.queuedInputs.get(sessionId) ?? [];
|
|
138556
|
+
queue.push(input);
|
|
138557
|
+
this.queuedInputs.set(sessionId, queue);
|
|
138558
|
+
session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
138559
|
+
session.promptCount++;
|
|
138560
|
+
this.log.warn(
|
|
138561
|
+
`[codex.input.queued] ${sessionId.slice(0, 8)} promptCount=${session.promptCount} queueLen=${queue.length}`,
|
|
138562
|
+
{
|
|
138563
|
+
event: "codex.input_queued",
|
|
138564
|
+
sessionId,
|
|
138565
|
+
promptCount: session.promptCount,
|
|
138566
|
+
queueLen: queue.length,
|
|
138567
|
+
inputLen: input.length
|
|
138568
|
+
}
|
|
138569
|
+
);
|
|
138570
|
+
return session.promptCount;
|
|
138571
|
+
}
|
|
138572
|
+
if (session.status === "waiting_input") {
|
|
138573
|
+
session.status = "running";
|
|
138574
|
+
this.onStatusChange?.(toPublicSession(session));
|
|
138575
|
+
}
|
|
138576
|
+
this.writeSubmit(sessionId, session, input, "direct", session.promptCount + 1);
|
|
138577
|
+
session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
138578
|
+
session.promptCount++;
|
|
138579
|
+
return session.promptCount;
|
|
138449
138580
|
}
|
|
138581
|
+
// Write the input as plain bytes (no bracketed-paste wrap — Phase 0
|
|
138582
|
+
// confirmed Codex accepts plain keystrokes), then submit \r after a short
|
|
138583
|
+
// delay so Codex's TUI gets an event-loop tick to process the input first.
|
|
138584
|
+
writeSubmit(sessionId, session, input, path2, promptCount) {
|
|
138585
|
+
this.log.info(
|
|
138586
|
+
`[codex.input.write] ${sessionId.slice(0, 8)} promptCount=${promptCount} bytes=${input.length} digest=${digestBytes(input)}`,
|
|
138587
|
+
{
|
|
138588
|
+
event: "codex.input_write",
|
|
138589
|
+
sessionId,
|
|
138590
|
+
promptCount,
|
|
138591
|
+
byteLen: input.length,
|
|
138592
|
+
digest: digestBytes(input),
|
|
138593
|
+
path: path2,
|
|
138594
|
+
phase: "input"
|
|
138595
|
+
}
|
|
138596
|
+
);
|
|
138597
|
+
session.process.write(input);
|
|
138598
|
+
setTimeout(() => {
|
|
138599
|
+
const current = this.sessions.get(sessionId);
|
|
138600
|
+
if (!current || current !== session) return;
|
|
138601
|
+
this.log.info(
|
|
138602
|
+
`[codex.input.submit] ${sessionId.slice(0, 8)} promptCount=${promptCount} digest=\\r`,
|
|
138603
|
+
{
|
|
138604
|
+
event: "codex.input_write",
|
|
138605
|
+
sessionId,
|
|
138606
|
+
promptCount,
|
|
138607
|
+
byteLen: SUBMIT_BYTES.length,
|
|
138608
|
+
digest: "\\r",
|
|
138609
|
+
path: path2,
|
|
138610
|
+
phase: "submit"
|
|
138611
|
+
}
|
|
138612
|
+
);
|
|
138613
|
+
current.process.write(SUBMIT_BYTES);
|
|
138614
|
+
}, CODEX_SUBMIT_DELAY_MS);
|
|
138615
|
+
}
|
|
138616
|
+
// Drain any inputs sent while the session was still pendingReady, writing
|
|
138617
|
+
// them in arrival order now that Codex is Ready.
|
|
138618
|
+
flushQueuedInputs(sessionId) {
|
|
138619
|
+
const queue = this.queuedInputs.get(sessionId);
|
|
138620
|
+
if (!queue || queue.length === 0) return;
|
|
138621
|
+
this.queuedInputs.delete(sessionId);
|
|
138622
|
+
const session = this.sessions.get(sessionId);
|
|
138623
|
+
if (!session) return;
|
|
138624
|
+
this.log.info(
|
|
138625
|
+
`[codex.flush] ${sessionId.slice(0, 8)} flushing ${queue.length} queued input(s)`,
|
|
138626
|
+
{
|
|
138627
|
+
event: "codex.flush_queued",
|
|
138628
|
+
sessionId,
|
|
138629
|
+
queueLen: queue.length
|
|
138630
|
+
}
|
|
138631
|
+
);
|
|
138632
|
+
queue.forEach((input, i) => {
|
|
138633
|
+
const writeAt = i * CODEX_SUBMIT_DELAY_MS * 2;
|
|
138634
|
+
if (writeAt === 0) {
|
|
138635
|
+
this.writeSubmit(sessionId, session, input, "flush", session.promptCount);
|
|
138636
|
+
} else {
|
|
138637
|
+
setTimeout(() => {
|
|
138638
|
+
const current = this.sessions.get(sessionId);
|
|
138639
|
+
if (!current || current !== session) return;
|
|
138640
|
+
this.writeSubmit(sessionId, session, input, "flush", session.promptCount);
|
|
138641
|
+
}, writeAt);
|
|
138642
|
+
}
|
|
138643
|
+
});
|
|
138644
|
+
}
|
|
138645
|
+
// SIGINT produces a clean exitCode=0 exit (Phase 0 — confirmed).
|
|
138646
|
+
cancel(sessionId) {
|
|
138647
|
+
const session = this.sessions.get(sessionId);
|
|
138648
|
+
if (!session) throw new Error(`Session not found: ${sessionId}`);
|
|
138649
|
+
session.process.kill("SIGINT");
|
|
138650
|
+
}
|
|
138651
|
+
killPid(pid) {
|
|
138652
|
+
try {
|
|
138653
|
+
process.kill(pid, "SIGTERM");
|
|
138654
|
+
} catch {
|
|
138655
|
+
}
|
|
138656
|
+
}
|
|
138657
|
+
// Kill the PTY and mark the session idle. Mirrors PTYManager.putOnHold.
|
|
138658
|
+
putOnHold(sessionId) {
|
|
138659
|
+
const session = this.sessions.get(sessionId);
|
|
138660
|
+
if (!session) return;
|
|
138661
|
+
this.pendingReady.delete(sessionId);
|
|
138662
|
+
this.queuedInputs.delete(sessionId);
|
|
138663
|
+
this.trustGateAnswered.delete(sessionId);
|
|
138664
|
+
try {
|
|
138665
|
+
session.process.kill("SIGINT");
|
|
138666
|
+
} catch {
|
|
138667
|
+
}
|
|
138668
|
+
session.status = "idle";
|
|
138669
|
+
session.completedAt = /* @__PURE__ */ new Date();
|
|
138670
|
+
session.screen.dispose();
|
|
138671
|
+
this.sessions.delete(sessionId);
|
|
138672
|
+
this.onStatusChange?.(toPublicSession(session));
|
|
138673
|
+
}
|
|
138674
|
+
getOutput(sessionId) {
|
|
138675
|
+
const session = this.sessions.get(sessionId);
|
|
138676
|
+
if (!session) throw new Error(`Session not found: ${sessionId}`);
|
|
138677
|
+
return session.outputBuffer.toString("utf-8");
|
|
138678
|
+
}
|
|
138679
|
+
// Render the last `maxLines` rows of the session's screen in true on-screen
|
|
138680
|
+
// order — same flush-then-read technique as PTYManager.getOutputLines.
|
|
138681
|
+
async getOutputLines(sessionId, maxLines) {
|
|
138682
|
+
const session = this.sessions.get(sessionId);
|
|
138683
|
+
if (!session) throw new Error(`Session not found: ${sessionId}`);
|
|
138684
|
+
await new Promise((resolve6) => session.screen.write("", () => resolve6()));
|
|
138685
|
+
const buf = session.screen.buffer.active;
|
|
138686
|
+
const lines = [];
|
|
138687
|
+
for (let y2 = 0; y2 < buf.length; y2++) {
|
|
138688
|
+
lines.push(buf.getLine(y2)?.translateToString(true) ?? "");
|
|
138689
|
+
}
|
|
138690
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
138691
|
+
lines.pop();
|
|
138692
|
+
}
|
|
138693
|
+
return lines.slice(-maxLines);
|
|
138694
|
+
}
|
|
138695
|
+
getSession(sessionId) {
|
|
138696
|
+
const session = this.sessions.get(sessionId);
|
|
138697
|
+
return session ? toPublicSession(session) : null;
|
|
138698
|
+
}
|
|
138699
|
+
hasSession(sessionId) {
|
|
138700
|
+
return this.sessions.has(sessionId);
|
|
138701
|
+
}
|
|
138702
|
+
listSessions() {
|
|
138703
|
+
return Array.from(this.sessions.values()).map(toPublicSession);
|
|
138704
|
+
}
|
|
138705
|
+
dispose() {
|
|
138706
|
+
for (const session of this.sessions.values()) {
|
|
138707
|
+
try {
|
|
138708
|
+
session.process.kill();
|
|
138709
|
+
} catch {
|
|
138710
|
+
}
|
|
138711
|
+
session.screen.dispose();
|
|
138712
|
+
}
|
|
138713
|
+
this.sessions.clear();
|
|
138714
|
+
this.pendingReady.clear();
|
|
138715
|
+
this.queuedInputs.clear();
|
|
138716
|
+
this.trustGateAnswered.clear();
|
|
138717
|
+
}
|
|
138718
|
+
handleOutput(sessionId, data) {
|
|
138719
|
+
const session = this.sessions.get(sessionId);
|
|
138720
|
+
if (!session) return;
|
|
138721
|
+
const chunk = Buffer.from(data, "utf-8");
|
|
138722
|
+
session.outputBuffer = Buffer.concat([session.outputBuffer, chunk]);
|
|
138723
|
+
if (session.outputBuffer.length > OUTPUT_BUFFER_MAX) {
|
|
138724
|
+
session.outputBuffer = session.outputBuffer.subarray(
|
|
138725
|
+
session.outputBuffer.length - OUTPUT_BUFFER_MAX
|
|
138726
|
+
);
|
|
138727
|
+
}
|
|
138728
|
+
session.screen.write(data);
|
|
138729
|
+
session.lastOutput = stripAnsi(data);
|
|
138730
|
+
this.onOutput?.(sessionId, data);
|
|
138731
|
+
this.detectReady(sessionId, session).catch((err) => {
|
|
138732
|
+
this.log.warn("[codex.ready_detect] failed", {
|
|
138733
|
+
event: "codex.ready_detect_failed",
|
|
138734
|
+
sessionId,
|
|
138735
|
+
err
|
|
138736
|
+
});
|
|
138737
|
+
});
|
|
138738
|
+
}
|
|
138739
|
+
// Renders the session's headless screen and checks for the directory-trust
|
|
138740
|
+
// gate (answered once, debounced) and the "Ready" status-bar text. Only
|
|
138741
|
+
// transitions to waiting_input / fires onReady when the rendered status
|
|
138742
|
+
// line literally contains "Ready" — `›` alone (visible during "Starting")
|
|
138743
|
+
// is NOT a valid readiness signal (Phase 0).
|
|
138744
|
+
async detectReady(sessionId, session) {
|
|
138745
|
+
if (session.status !== "running" || !this.pendingReady.has(sessionId)) return;
|
|
138746
|
+
const lines = await this.getOutputLines(sessionId, PTY_ROWS);
|
|
138747
|
+
const screenText = lines.join("\n");
|
|
138748
|
+
if (CODEX_TRUST_GATE_REGEX.test(screenText)) {
|
|
138749
|
+
if (!this.trustGateAnswered.has(sessionId)) {
|
|
138750
|
+
this.trustGateAnswered.add(sessionId);
|
|
138751
|
+
this.log.info(`[codex.trust_gate] ${sessionId.slice(0, 8)} auto-answering`, {
|
|
138752
|
+
event: "codex.trust_gate",
|
|
138753
|
+
sessionId
|
|
138754
|
+
});
|
|
138755
|
+
session.process.write("\r");
|
|
138756
|
+
}
|
|
138757
|
+
return;
|
|
138758
|
+
}
|
|
138759
|
+
const lastNonBlank2 = [...lines].reverse().find((l) => l.trim() !== "") ?? "";
|
|
138760
|
+
if (!lastNonBlank2.includes(CODEX_PROMPT_READY_TEXT)) return;
|
|
138761
|
+
this.markReady(sessionId, session);
|
|
138762
|
+
}
|
|
138763
|
+
markReady(sessionId, session) {
|
|
138764
|
+
session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
138765
|
+
session.status = "waiting_input";
|
|
138766
|
+
this.log.info(`[codex.ready] ${sessionId.slice(0, 8)}`, {
|
|
138767
|
+
event: "codex.ready",
|
|
138768
|
+
sessionId
|
|
138769
|
+
});
|
|
138770
|
+
this.onStatusChange?.(toPublicSession(session));
|
|
138771
|
+
if (this.pendingReady.has(sessionId)) {
|
|
138772
|
+
this.pendingReady.delete(sessionId);
|
|
138773
|
+
this.flushQueuedInputs(sessionId);
|
|
138774
|
+
this.onReady?.(toPublicSession(session));
|
|
138775
|
+
}
|
|
138776
|
+
}
|
|
138777
|
+
handleExit(sessionId, exitCode) {
|
|
138778
|
+
const session = this.sessions.get(sessionId);
|
|
138779
|
+
if (!session) return;
|
|
138780
|
+
session.completedAt = /* @__PURE__ */ new Date();
|
|
138781
|
+
session.status = "idle";
|
|
138782
|
+
const elapsedMs = session.completedAt.getTime() - session.startedAt.getTime();
|
|
138783
|
+
if (exitCode !== 0 && elapsedMs < 2e3 && session.lastOutput === "") {
|
|
138784
|
+
if (!(0, import_fs20.existsSync)(session.projectPath)) {
|
|
138785
|
+
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
138786
|
+
} else {
|
|
138787
|
+
session.failureReason = `Codex process exited immediately (code ${exitCode}).`;
|
|
138788
|
+
}
|
|
138789
|
+
}
|
|
138790
|
+
this.onStatusChange?.(toPublicSession(session));
|
|
138791
|
+
session.screen.dispose();
|
|
138792
|
+
this.sessions.delete(sessionId);
|
|
138793
|
+
this.queuedInputs.delete(sessionId);
|
|
138794
|
+
this.trustGateAnswered.delete(sessionId);
|
|
138795
|
+
}
|
|
138796
|
+
};
|
|
138797
|
+
function toPublicSession(s3) {
|
|
138798
|
+
return {
|
|
138799
|
+
id: s3.id,
|
|
138800
|
+
provider: s3.provider ?? CODEX_CLI_PROVIDER2,
|
|
138801
|
+
projectPath: s3.projectPath,
|
|
138802
|
+
projectName: s3.projectName,
|
|
138803
|
+
branch: s3.branch,
|
|
138804
|
+
status: s3.status,
|
|
138805
|
+
startedAt: s3.startedAt,
|
|
138806
|
+
completedAt: s3.completedAt,
|
|
138807
|
+
promptCount: s3.promptCount,
|
|
138808
|
+
lastOutput: s3.lastOutput,
|
|
138809
|
+
...s3.failureReason != null && { failureReason: s3.failureReason },
|
|
138810
|
+
...s3.lastActivityAt != null && { lastActivityAt: s3.lastActivityAt },
|
|
138811
|
+
...s3.filePath != null && { filePath: s3.filePath }
|
|
138812
|
+
};
|
|
138813
|
+
}
|
|
138814
|
+
function stripAnsi(str) {
|
|
138815
|
+
return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "");
|
|
138450
138816
|
}
|
|
138451
138817
|
|
|
138452
138818
|
// src/pty-manager.ts
|
|
138453
|
-
var
|
|
138819
|
+
var import_headless2 = __toESM(require_xterm_headless(), 1);
|
|
138454
138820
|
var import_crypto6 = require("crypto");
|
|
138455
|
-
var
|
|
138821
|
+
var import_fs21 = require("fs");
|
|
138456
138822
|
var import_path17 = require("path");
|
|
138457
138823
|
init_logger();
|
|
138458
138824
|
|
|
@@ -138613,28 +138979,28 @@ function detectShellPrompt(lines) {
|
|
|
138613
138979
|
}
|
|
138614
138980
|
|
|
138615
138981
|
// src/pty-manager.ts
|
|
138616
|
-
var
|
|
138617
|
-
var
|
|
138618
|
-
var
|
|
138619
|
-
var
|
|
138982
|
+
var OUTPUT_BUFFER_MAX2 = 65536;
|
|
138983
|
+
var PTY_COLS2 = 120;
|
|
138984
|
+
var PTY_ROWS2 = 40;
|
|
138985
|
+
var SCREEN_SCROLLBACK2 = 1e3;
|
|
138620
138986
|
var CLAUDE_PROMPT_MARKERS = ["\u256D", "\u276F"];
|
|
138621
138987
|
var PROMPT_MARKER_FALLBACK_MS = 1e4;
|
|
138622
138988
|
function buildPasteBytes(input) {
|
|
138623
138989
|
return `\x1B[200~${input}\x1B[201~`;
|
|
138624
138990
|
}
|
|
138625
|
-
var
|
|
138991
|
+
var SUBMIT_BYTES2 = "\r";
|
|
138626
138992
|
var SUBMIT_DELAY_MS = 16;
|
|
138627
|
-
function
|
|
138993
|
+
function digestBytes2(s3) {
|
|
138628
138994
|
const escaped = s3.replace(new RegExp(String.fromCharCode(27), "g"), "\\x1b").replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\t/g, "\\t");
|
|
138629
138995
|
if (escaped.length <= 200) return escaped;
|
|
138630
138996
|
return `${escaped.slice(0, 100)}\u2026[${escaped.length - 200}B omitted]\u2026${escaped.slice(-100)}`;
|
|
138631
138997
|
}
|
|
138632
|
-
var
|
|
138633
|
-
async function
|
|
138634
|
-
if (
|
|
138998
|
+
var pty2 = null;
|
|
138999
|
+
async function loadPty2() {
|
|
139000
|
+
if (pty2) return pty2;
|
|
138635
139001
|
try {
|
|
138636
|
-
|
|
138637
|
-
return
|
|
139002
|
+
pty2 = await import("node-pty");
|
|
139003
|
+
return pty2;
|
|
138638
139004
|
} catch (err) {
|
|
138639
139005
|
throw new Error(
|
|
138640
139006
|
`node-pty is required for PTY management but failed to load. Ensure it is installed: npm install node-pty
|
|
@@ -138642,11 +139008,11 @@ Original error: ${err}`
|
|
|
138642
139008
|
);
|
|
138643
139009
|
}
|
|
138644
139010
|
}
|
|
138645
|
-
function
|
|
138646
|
-
return new
|
|
138647
|
-
cols:
|
|
138648
|
-
rows:
|
|
138649
|
-
scrollback:
|
|
139011
|
+
function createScreen2() {
|
|
139012
|
+
return new import_headless2.Terminal({
|
|
139013
|
+
cols: PTY_COLS2,
|
|
139014
|
+
rows: PTY_ROWS2,
|
|
139015
|
+
scrollback: SCREEN_SCROLLBACK2,
|
|
138650
139016
|
allowProposedApi: true
|
|
138651
139017
|
});
|
|
138652
139018
|
}
|
|
@@ -138655,6 +139021,11 @@ function buildSpawnEnv() {
|
|
|
138655
139021
|
if (env.CLAUDE_API_KEY) {
|
|
138656
139022
|
env.ANTHROPIC_API_KEY = env.CLAUDE_API_KEY;
|
|
138657
139023
|
}
|
|
139024
|
+
for (const key of Object.keys(env)) {
|
|
139025
|
+
if (key === "CLAUDECODE" || key.startsWith("CLAUDE_CODE_")) {
|
|
139026
|
+
delete env[key];
|
|
139027
|
+
}
|
|
139028
|
+
}
|
|
138658
139029
|
return env;
|
|
138659
139030
|
}
|
|
138660
139031
|
var PTYManager = class {
|
|
@@ -138695,6 +139066,10 @@ var PTYManager = class {
|
|
|
138695
139066
|
// to a given input or fell silent. Reset on dispose().
|
|
138696
139067
|
chunkIndex = /* @__PURE__ */ new Map();
|
|
138697
139068
|
lastChunkAt = /* @__PURE__ */ new Map();
|
|
139069
|
+
// In-flight start()/startFresh() calls keyed by sessionId. A second
|
|
139070
|
+
// concurrent resume for the same session (double-tap, client retry) awaits
|
|
139071
|
+
// the first call's promise instead of spawning a duplicate PTY (CRITICAL #3).
|
|
139072
|
+
startPromises = /* @__PURE__ */ new Map();
|
|
138698
139073
|
constructor(options = {}) {
|
|
138699
139074
|
this.onOutput = options.onOutput;
|
|
138700
139075
|
this.onStatusChange = options.onStatusChange;
|
|
@@ -138717,7 +139092,18 @@ var PTYManager = class {
|
|
|
138717
139092
|
// custom-API-key — are cleared by the seeded ~/.claude.json in
|
|
138718
139093
|
// docker/entrypoint.sh.) startFresh() uses the same flag for the same reason.
|
|
138719
139094
|
async start(sessionId, options) {
|
|
138720
|
-
const
|
|
139095
|
+
const existing = this.sessions.get(sessionId);
|
|
139096
|
+
if (existing) return toPublicSession2(existing);
|
|
139097
|
+
const inFlight = this.startPromises.get(sessionId);
|
|
139098
|
+
if (inFlight) return inFlight;
|
|
139099
|
+
const promise2 = this.doStart(sessionId, options).finally(() => {
|
|
139100
|
+
this.startPromises.delete(sessionId);
|
|
139101
|
+
});
|
|
139102
|
+
this.startPromises.set(sessionId, promise2);
|
|
139103
|
+
return promise2;
|
|
139104
|
+
}
|
|
139105
|
+
async doStart(sessionId, options) {
|
|
139106
|
+
const nodePty = await loadPty2();
|
|
138721
139107
|
const projectName = options.projectName ?? (0, import_path17.basename)(options.projectPath);
|
|
138722
139108
|
const proc = nodePty.spawn(
|
|
138723
139109
|
resolveClaudeExe(),
|
|
@@ -138739,6 +139125,7 @@ var PTYManager = class {
|
|
|
138739
139125
|
);
|
|
138740
139126
|
const session = {
|
|
138741
139127
|
id: sessionId,
|
|
139128
|
+
provider: CLAUDE_CODE_PROVIDER2,
|
|
138742
139129
|
projectPath: options.projectPath,
|
|
138743
139130
|
projectName,
|
|
138744
139131
|
branch: options.branch ?? "",
|
|
@@ -138749,7 +139136,7 @@ var PTYManager = class {
|
|
|
138749
139136
|
lastOutput: "",
|
|
138750
139137
|
process: proc,
|
|
138751
139138
|
outputBuffer: Buffer.alloc(0),
|
|
138752
|
-
screen:
|
|
139139
|
+
screen: createScreen2()
|
|
138753
139140
|
};
|
|
138754
139141
|
this.sessions.set(sessionId, session);
|
|
138755
139142
|
this.pendingReady.add(sessionId);
|
|
@@ -138760,13 +139147,13 @@ var PTYManager = class {
|
|
|
138760
139147
|
this.pendingReady.delete(sessionId);
|
|
138761
139148
|
this.handleExit(sessionId, exitCode);
|
|
138762
139149
|
});
|
|
138763
|
-
return
|
|
139150
|
+
return toPublicSession2(session);
|
|
138764
139151
|
}
|
|
138765
139152
|
// Start a brand-new Claude session. A stable UUID is generated here and passed
|
|
138766
139153
|
// to Claude via --session-id so the JSONL filename matches from the start.
|
|
138767
139154
|
// onReady fires once Claude reaches its first prompt (waiting_input).
|
|
138768
139155
|
async startFresh(options) {
|
|
138769
|
-
const nodePty = await
|
|
139156
|
+
const nodePty = await loadPty2();
|
|
138770
139157
|
const sessionId = (0, import_crypto6.randomUUID)();
|
|
138771
139158
|
const projectName = options.projectName ?? (0, import_path17.basename)(options.projectPath);
|
|
138772
139159
|
const args = [
|
|
@@ -138789,6 +139176,7 @@ var PTYManager = class {
|
|
|
138789
139176
|
});
|
|
138790
139177
|
const session = {
|
|
138791
139178
|
id: sessionId,
|
|
139179
|
+
provider: CLAUDE_CODE_PROVIDER2,
|
|
138792
139180
|
projectPath: options.projectPath,
|
|
138793
139181
|
projectName,
|
|
138794
139182
|
branch: "",
|
|
@@ -138799,7 +139187,7 @@ var PTYManager = class {
|
|
|
138799
139187
|
lastOutput: "",
|
|
138800
139188
|
process: proc,
|
|
138801
139189
|
outputBuffer: Buffer.alloc(0),
|
|
138802
|
-
screen:
|
|
139190
|
+
screen: createScreen2()
|
|
138803
139191
|
};
|
|
138804
139192
|
this.sessions.set(sessionId, session);
|
|
138805
139193
|
this.pendingReady.add(sessionId);
|
|
@@ -138810,7 +139198,7 @@ var PTYManager = class {
|
|
|
138810
139198
|
this.pendingReady.delete(sessionId);
|
|
138811
139199
|
this.handleExit(sessionId, exitCode);
|
|
138812
139200
|
});
|
|
138813
|
-
return
|
|
139201
|
+
return toPublicSession2(session);
|
|
138814
139202
|
}
|
|
138815
139203
|
// Write raw key bytes directly to the PTY without bracketed-paste wrapping.
|
|
138816
139204
|
// Use for control sequences (arrow keys, Enter) that must not be quoted.
|
|
@@ -138822,10 +139210,10 @@ var PTYManager = class {
|
|
|
138822
139210
|
}
|
|
138823
139211
|
if (session.status === "waiting_input") {
|
|
138824
139212
|
session.status = "running";
|
|
138825
|
-
this.onStatusChange?.(
|
|
139213
|
+
this.onStatusChange?.(toPublicSession2(session));
|
|
138826
139214
|
}
|
|
138827
139215
|
this.log.info(
|
|
138828
|
-
`[pty.keys.write] ${sessionId.slice(0, 8)} bytes=${keys.length} digest=${
|
|
139216
|
+
`[pty.keys.write] ${sessionId.slice(0, 8)} bytes=${keys.length} digest=${digestBytes2(keys)}`,
|
|
138829
139217
|
{ event: "pty.keys_write", sessionId, byteLen: keys.length }
|
|
138830
139218
|
);
|
|
138831
139219
|
session.process.write(keys);
|
|
@@ -138857,7 +139245,7 @@ var PTYManager = class {
|
|
|
138857
139245
|
}
|
|
138858
139246
|
if (session.status === "waiting_input") {
|
|
138859
139247
|
session.status = "running";
|
|
138860
|
-
this.onStatusChange?.(
|
|
139248
|
+
this.onStatusChange?.(toPublicSession2(session));
|
|
138861
139249
|
}
|
|
138862
139250
|
this.writeSubmit(sessionId, session, input, "direct", session.promptCount + 1);
|
|
138863
139251
|
session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
@@ -138870,13 +139258,13 @@ var PTYManager = class {
|
|
|
138870
139258
|
writeSubmit(sessionId, session, input, path2, promptCount) {
|
|
138871
139259
|
const pasteBytes = buildPasteBytes(input);
|
|
138872
139260
|
this.log.info(
|
|
138873
|
-
`[pty.input.write] ${sessionId.slice(0, 8)} promptCount=${promptCount} bytes=${pasteBytes.length} digest=${
|
|
139261
|
+
`[pty.input.write] ${sessionId.slice(0, 8)} promptCount=${promptCount} bytes=${pasteBytes.length} digest=${digestBytes2(pasteBytes)}`,
|
|
138874
139262
|
{
|
|
138875
139263
|
event: "pty.input_write",
|
|
138876
139264
|
sessionId,
|
|
138877
139265
|
promptCount,
|
|
138878
139266
|
byteLen: pasteBytes.length,
|
|
138879
|
-
digest:
|
|
139267
|
+
digest: digestBytes2(pasteBytes),
|
|
138880
139268
|
path: path2,
|
|
138881
139269
|
phase: "paste"
|
|
138882
139270
|
}
|
|
@@ -138891,13 +139279,13 @@ var PTYManager = class {
|
|
|
138891
139279
|
event: "pty.input_write",
|
|
138892
139280
|
sessionId,
|
|
138893
139281
|
promptCount,
|
|
138894
|
-
byteLen:
|
|
139282
|
+
byteLen: SUBMIT_BYTES2.length,
|
|
138895
139283
|
digest: "\\r",
|
|
138896
139284
|
path: path2,
|
|
138897
139285
|
phase: "submit"
|
|
138898
139286
|
}
|
|
138899
139287
|
);
|
|
138900
|
-
current.process.write(
|
|
139288
|
+
current.process.write(SUBMIT_BYTES2);
|
|
138901
139289
|
}, SUBMIT_DELAY_MS);
|
|
138902
139290
|
}
|
|
138903
139291
|
// Drain any inputs that were sent while the session was still pendingReady,
|
|
@@ -138955,7 +139343,7 @@ var PTYManager = class {
|
|
|
138955
139343
|
session.completedAt = /* @__PURE__ */ new Date();
|
|
138956
139344
|
session.screen.dispose();
|
|
138957
139345
|
this.sessions.delete(sessionId);
|
|
138958
|
-
this.onStatusChange?.(
|
|
139346
|
+
this.onStatusChange?.(toPublicSession2(session));
|
|
138959
139347
|
}
|
|
138960
139348
|
getOutput(sessionId) {
|
|
138961
139349
|
const session = this.sessions.get(sessionId);
|
|
@@ -138986,13 +139374,13 @@ var PTYManager = class {
|
|
|
138986
139374
|
}
|
|
138987
139375
|
getSession(sessionId) {
|
|
138988
139376
|
const session = this.sessions.get(sessionId);
|
|
138989
|
-
return session ?
|
|
139377
|
+
return session ? toPublicSession2(session) : null;
|
|
138990
139378
|
}
|
|
138991
139379
|
hasSession(sessionId) {
|
|
138992
139380
|
return this.sessions.has(sessionId);
|
|
138993
139381
|
}
|
|
138994
139382
|
listSessions() {
|
|
138995
|
-
return Array.from(this.sessions.values()).map(
|
|
139383
|
+
return Array.from(this.sessions.values()).map(toPublicSession2);
|
|
138996
139384
|
}
|
|
138997
139385
|
dispose() {
|
|
138998
139386
|
for (const session of this.sessions.values()) {
|
|
@@ -139024,7 +139412,7 @@ var PTYManager = class {
|
|
|
139024
139412
|
this.lastChunkAt.set(sessionId, now);
|
|
139025
139413
|
const gapMs = last == null ? 0 : now - last;
|
|
139026
139414
|
this.log.info(
|
|
139027
|
-
`[pty.chunk] ${sessionId.slice(0, 8)} #${idx} +${chunk.length}B gap=${gapMs}ms status=${session.status} digest=${
|
|
139415
|
+
`[pty.chunk] ${sessionId.slice(0, 8)} #${idx} +${chunk.length}B gap=${gapMs}ms status=${session.status} digest=${digestBytes2(data)}`,
|
|
139028
139416
|
{
|
|
139029
139417
|
event: "pty.chunk",
|
|
139030
139418
|
sessionId,
|
|
@@ -139033,17 +139421,17 @@ var PTYManager = class {
|
|
|
139033
139421
|
gapMs,
|
|
139034
139422
|
status: session.status,
|
|
139035
139423
|
pendingReady: this.pendingReady.has(sessionId),
|
|
139036
|
-
digest:
|
|
139424
|
+
digest: digestBytes2(data)
|
|
139037
139425
|
}
|
|
139038
139426
|
);
|
|
139039
139427
|
session.outputBuffer = Buffer.concat([session.outputBuffer, chunk]);
|
|
139040
|
-
if (session.outputBuffer.length >
|
|
139428
|
+
if (session.outputBuffer.length > OUTPUT_BUFFER_MAX2) {
|
|
139041
139429
|
session.outputBuffer = session.outputBuffer.subarray(
|
|
139042
|
-
session.outputBuffer.length -
|
|
139430
|
+
session.outputBuffer.length - OUTPUT_BUFFER_MAX2
|
|
139043
139431
|
);
|
|
139044
139432
|
}
|
|
139045
139433
|
session.screen.write(data);
|
|
139046
|
-
const stripped =
|
|
139434
|
+
const stripped = stripAnsi2(data);
|
|
139047
139435
|
session.lastOutput = stripped;
|
|
139048
139436
|
const matchedMarker = CLAUDE_PROMPT_MARKERS.find((m2) => stripped.includes(m2));
|
|
139049
139437
|
if (session.status === "running" && matchedMarker) {
|
|
@@ -139146,11 +139534,11 @@ var PTYManager = class {
|
|
|
139146
139534
|
reason,
|
|
139147
139535
|
elapsedMs
|
|
139148
139536
|
});
|
|
139149
|
-
this.onStatusChange?.(
|
|
139537
|
+
this.onStatusChange?.(toPublicSession2(session));
|
|
139150
139538
|
if (this.pendingReady.has(sessionId)) {
|
|
139151
139539
|
this.pendingReady.delete(sessionId);
|
|
139152
139540
|
this.flushQueuedInputs(sessionId);
|
|
139153
|
-
this.onReady?.(
|
|
139541
|
+
this.onReady?.(toPublicSession2(session));
|
|
139154
139542
|
}
|
|
139155
139543
|
}
|
|
139156
139544
|
handleExit(sessionId, exitCode) {
|
|
@@ -139160,13 +139548,13 @@ var PTYManager = class {
|
|
|
139160
139548
|
session.status = "idle";
|
|
139161
139549
|
const elapsedMs = session.completedAt.getTime() - session.startedAt.getTime();
|
|
139162
139550
|
if (exitCode !== 0 && elapsedMs < 2e3 && session.lastOutput === "") {
|
|
139163
|
-
if (!(0,
|
|
139551
|
+
if (!(0, import_fs21.existsSync)(session.projectPath)) {
|
|
139164
139552
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
139165
139553
|
} else {
|
|
139166
139554
|
session.failureReason = `Process exited immediately (code ${exitCode}). Check that the Claude binary is installed and accessible.`;
|
|
139167
139555
|
}
|
|
139168
139556
|
}
|
|
139169
|
-
this.onStatusChange?.(
|
|
139557
|
+
this.onStatusChange?.(toPublicSession2(session));
|
|
139170
139558
|
session.screen.dispose();
|
|
139171
139559
|
this.sessions.delete(sessionId);
|
|
139172
139560
|
this.queuedInputs.delete(sessionId);
|
|
@@ -139176,9 +139564,10 @@ var PTYManager = class {
|
|
|
139176
139564
|
this.shellPromptOpen.delete(sessionId);
|
|
139177
139565
|
}
|
|
139178
139566
|
};
|
|
139179
|
-
function
|
|
139567
|
+
function toPublicSession2(s3) {
|
|
139180
139568
|
return {
|
|
139181
139569
|
id: s3.id,
|
|
139570
|
+
provider: s3.provider ?? CLAUDE_CODE_PROVIDER2,
|
|
139182
139571
|
projectPath: s3.projectPath,
|
|
139183
139572
|
projectName: s3.projectName,
|
|
139184
139573
|
branch: s3.branch,
|
|
@@ -139192,10 +139581,306 @@ function toPublicSession(s3) {
|
|
|
139192
139581
|
...s3.filePath != null && { filePath: s3.filePath }
|
|
139193
139582
|
};
|
|
139194
139583
|
}
|
|
139195
|
-
function
|
|
139584
|
+
function stripAnsi2(str) {
|
|
139196
139585
|
return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "");
|
|
139197
139586
|
}
|
|
139198
139587
|
|
|
139588
|
+
// src/live-session-manager.ts
|
|
139589
|
+
var LiveSessionManager = class {
|
|
139590
|
+
runners;
|
|
139591
|
+
constructor(options = {}) {
|
|
139592
|
+
this.runners = /* @__PURE__ */ new Map([
|
|
139593
|
+
[CLAUDE_CODE_PROVIDER2, new PTYManager(options)],
|
|
139594
|
+
[CODEX_CLI_PROVIDER2, new CodexPtyRunner(options)]
|
|
139595
|
+
]);
|
|
139596
|
+
}
|
|
139597
|
+
async start(sessionId, options) {
|
|
139598
|
+
const provider = options.provider ?? CLAUDE_CODE_PROVIDER2;
|
|
139599
|
+
const runner = this.assertSupportedProvider(provider, options.projectPath);
|
|
139600
|
+
return runner.start(sessionId, options);
|
|
139601
|
+
}
|
|
139602
|
+
async startFresh(options) {
|
|
139603
|
+
const provider = options.provider ?? CLAUDE_CODE_PROVIDER2;
|
|
139604
|
+
const runner = this.assertSupportedProvider(provider, options.projectPath);
|
|
139605
|
+
return runner.startFresh(options);
|
|
139606
|
+
}
|
|
139607
|
+
sendInput(sessionId, input) {
|
|
139608
|
+
return this.runnerFor(sessionId).sendInput(sessionId, input);
|
|
139609
|
+
}
|
|
139610
|
+
sendKeys(sessionId, keys) {
|
|
139611
|
+
this.runnerFor(sessionId).sendKeys(sessionId, keys);
|
|
139612
|
+
}
|
|
139613
|
+
cancel(sessionId) {
|
|
139614
|
+
this.runnerFor(sessionId).cancel(sessionId);
|
|
139615
|
+
}
|
|
139616
|
+
killPid(pid) {
|
|
139617
|
+
for (const runner of this.runners.values()) {
|
|
139618
|
+
runner.killPid(pid);
|
|
139619
|
+
}
|
|
139620
|
+
}
|
|
139621
|
+
// putOnHold tolerates an unknown sessionId (PTYManager.putOnHold is a no-op
|
|
139622
|
+
// when the session isn't in its map), so — unlike the other session-keyed
|
|
139623
|
+
// methods — route to the owning runner when found, otherwise broadcast to
|
|
139624
|
+
// every runner rather than throwing; this matches the pre-extraction
|
|
139625
|
+
// behavior of delegating straight through with no existence check.
|
|
139626
|
+
putOnHold(sessionId) {
|
|
139627
|
+
for (const runner of this.runners.values()) {
|
|
139628
|
+
if (runner.hasSession(sessionId) || runner.getSession(sessionId)) {
|
|
139629
|
+
runner.putOnHold(sessionId);
|
|
139630
|
+
return;
|
|
139631
|
+
}
|
|
139632
|
+
}
|
|
139633
|
+
for (const runner of this.runners.values()) {
|
|
139634
|
+
runner.putOnHold(sessionId);
|
|
139635
|
+
}
|
|
139636
|
+
}
|
|
139637
|
+
getOutput(sessionId) {
|
|
139638
|
+
return this.runnerFor(sessionId).getOutput(sessionId);
|
|
139639
|
+
}
|
|
139640
|
+
getOutputLines(sessionId, maxLines) {
|
|
139641
|
+
return this.runnerFor(sessionId).getOutputLines(sessionId, maxLines);
|
|
139642
|
+
}
|
|
139643
|
+
getSession(sessionId) {
|
|
139644
|
+
for (const runner of this.runners.values()) {
|
|
139645
|
+
const session = runner.getSession(sessionId);
|
|
139646
|
+
if (session) return session;
|
|
139647
|
+
}
|
|
139648
|
+
return null;
|
|
139649
|
+
}
|
|
139650
|
+
hasSession(sessionId) {
|
|
139651
|
+
for (const runner of this.runners.values()) {
|
|
139652
|
+
if (runner.hasSession(sessionId)) return true;
|
|
139653
|
+
}
|
|
139654
|
+
return false;
|
|
139655
|
+
}
|
|
139656
|
+
listSessions() {
|
|
139657
|
+
return Array.from(this.runners.values()).flatMap((runner) => runner.listSessions());
|
|
139658
|
+
}
|
|
139659
|
+
dispose() {
|
|
139660
|
+
for (const runner of this.runners.values()) {
|
|
139661
|
+
runner.dispose();
|
|
139662
|
+
}
|
|
139663
|
+
}
|
|
139664
|
+
// Look up which runner owns a session. Only one runner exists today, so
|
|
139665
|
+
// this is a linear scan across hasSession()/getSession() rather than a
|
|
139666
|
+
// separate session→provider index — see task-1-brief.md.
|
|
139667
|
+
runnerFor(sessionId) {
|
|
139668
|
+
for (const runner of this.runners.values()) {
|
|
139669
|
+
if (runner.hasSession(sessionId) || runner.getSession(sessionId)) return runner;
|
|
139670
|
+
}
|
|
139671
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
139672
|
+
}
|
|
139673
|
+
assertSupportedProvider(provider, projectPath) {
|
|
139674
|
+
const runner = this.runners.get(provider);
|
|
139675
|
+
if (runner) return runner;
|
|
139676
|
+
const err = new Error(
|
|
139677
|
+
`Live ${provider} sessions are not implemented yet for ${(0, import_path18.basename)(projectPath)}`
|
|
139678
|
+
);
|
|
139679
|
+
err.statusCode = 501;
|
|
139680
|
+
throw err;
|
|
139681
|
+
}
|
|
139682
|
+
};
|
|
139683
|
+
|
|
139684
|
+
// src/server.ts
|
|
139685
|
+
init_logger();
|
|
139686
|
+
|
|
139687
|
+
// src/pair-store.ts
|
|
139688
|
+
var import_crypto7 = require("crypto");
|
|
139689
|
+
var DEFAULT_TTL_SECONDS = 180;
|
|
139690
|
+
var SWEEP_INTERVAL_MS = 6e4;
|
|
139691
|
+
var PairTokenStore = class {
|
|
139692
|
+
current = null;
|
|
139693
|
+
ttlMs;
|
|
139694
|
+
sweepTimer = null;
|
|
139695
|
+
constructor(opts = {}) {
|
|
139696
|
+
this.ttlMs = (opts.ttlSeconds ?? DEFAULT_TTL_SECONDS) * 1e3;
|
|
139697
|
+
if (opts.autoSweep !== false) {
|
|
139698
|
+
this.sweepTimer = setInterval(() => this.sweep(), SWEEP_INTERVAL_MS);
|
|
139699
|
+
this.sweepTimer.unref?.();
|
|
139700
|
+
}
|
|
139701
|
+
}
|
|
139702
|
+
mint() {
|
|
139703
|
+
const token = `pt_${(0, import_crypto7.randomBytes)(16).toString("hex")}`;
|
|
139704
|
+
const expiresAt = Date.now() + this.ttlMs;
|
|
139705
|
+
this.current = { token, expiresAt, used: false };
|
|
139706
|
+
return {
|
|
139707
|
+
token,
|
|
139708
|
+
expiresAt,
|
|
139709
|
+
expiresInSeconds: Math.floor(this.ttlMs / 1e3)
|
|
139710
|
+
};
|
|
139711
|
+
}
|
|
139712
|
+
consume(token) {
|
|
139713
|
+
const record2 = this.current;
|
|
139714
|
+
if (!record2 || record2.token !== token) return { ok: false, reason: "unknown" };
|
|
139715
|
+
if (Date.now() > record2.expiresAt) {
|
|
139716
|
+
this.current = null;
|
|
139717
|
+
return { ok: false, reason: "expired" };
|
|
139718
|
+
}
|
|
139719
|
+
if (record2.used) return { ok: false, reason: "used" };
|
|
139720
|
+
record2.used = true;
|
|
139721
|
+
return { ok: true };
|
|
139722
|
+
}
|
|
139723
|
+
peek() {
|
|
139724
|
+
return this.current;
|
|
139725
|
+
}
|
|
139726
|
+
clear() {
|
|
139727
|
+
this.current = null;
|
|
139728
|
+
}
|
|
139729
|
+
sweep() {
|
|
139730
|
+
if (this.current && Date.now() > this.current.expiresAt) {
|
|
139731
|
+
this.current = null;
|
|
139732
|
+
}
|
|
139733
|
+
}
|
|
139734
|
+
dispose() {
|
|
139735
|
+
if (this.sweepTimer) clearInterval(this.sweepTimer);
|
|
139736
|
+
this.sweepTimer = null;
|
|
139737
|
+
this.current = null;
|
|
139738
|
+
}
|
|
139739
|
+
};
|
|
139740
|
+
|
|
139741
|
+
// src/process-discovery.ts
|
|
139742
|
+
var import_child_process2 = require("child_process");
|
|
139743
|
+
var import_os9 = require("os");
|
|
139744
|
+
var import_path19 = require("path");
|
|
139745
|
+
async function discoverClaudeProcesses() {
|
|
139746
|
+
if ((0, import_os9.platform)() === "win32") return discoverWindows();
|
|
139747
|
+
return discoverUnix();
|
|
139748
|
+
}
|
|
139749
|
+
async function discoverUnix() {
|
|
139750
|
+
const pids = await getPidsUnix();
|
|
139751
|
+
const results = await Promise.all(
|
|
139752
|
+
pids.map(async (pid) => {
|
|
139753
|
+
try {
|
|
139754
|
+
const [cwd, args, startedAt] = await Promise.all([
|
|
139755
|
+
getProcessCwdUnix(pid),
|
|
139756
|
+
getProcessArgsUnix(pid),
|
|
139757
|
+
getProcessStartTimeUnix(pid)
|
|
139758
|
+
]);
|
|
139759
|
+
const conversationId = extractResumeId(args);
|
|
139760
|
+
return {
|
|
139761
|
+
pid,
|
|
139762
|
+
projectPath: cwd,
|
|
139763
|
+
projectName: (0, import_path19.basename)(cwd),
|
|
139764
|
+
branch: await readGitBranch2(cwd),
|
|
139765
|
+
conversationId,
|
|
139766
|
+
startedAt
|
|
139767
|
+
};
|
|
139768
|
+
} catch {
|
|
139769
|
+
return null;
|
|
139770
|
+
}
|
|
139771
|
+
})
|
|
139772
|
+
);
|
|
139773
|
+
return results.filter((r) => r !== null);
|
|
139774
|
+
}
|
|
139775
|
+
async function discoverWindows() {
|
|
139776
|
+
const pids = await getPidsWindows();
|
|
139777
|
+
const results = await Promise.all(
|
|
139778
|
+
pids.map(async (pid) => {
|
|
139779
|
+
try {
|
|
139780
|
+
const info = await getProcessInfoWindows(pid);
|
|
139781
|
+
if (!info) return null;
|
|
139782
|
+
return {
|
|
139783
|
+
pid,
|
|
139784
|
+
projectPath: info.cwd,
|
|
139785
|
+
projectName: (0, import_path19.basename)(info.cwd),
|
|
139786
|
+
branch: await readGitBranch2(info.cwd),
|
|
139787
|
+
conversationId: extractResumeId(info.args),
|
|
139788
|
+
startedAt: info.startedAt
|
|
139789
|
+
};
|
|
139790
|
+
} catch {
|
|
139791
|
+
return null;
|
|
139792
|
+
}
|
|
139793
|
+
})
|
|
139794
|
+
);
|
|
139795
|
+
return results.filter((r) => r !== null);
|
|
139796
|
+
}
|
|
139797
|
+
function run(cmd, args, opts = {}) {
|
|
139798
|
+
return new Promise((resolve6, reject) => {
|
|
139799
|
+
(0, import_child_process2.execFile)(
|
|
139800
|
+
cmd,
|
|
139801
|
+
args,
|
|
139802
|
+
{ windowsHide: isWindows2, encoding: "utf-8", timeout: opts.timeout ?? 5e3, cwd: opts.cwd },
|
|
139803
|
+
(err, stdout2) => {
|
|
139804
|
+
if (err) reject(err);
|
|
139805
|
+
else resolve6(stdout2);
|
|
139806
|
+
}
|
|
139807
|
+
);
|
|
139808
|
+
});
|
|
139809
|
+
}
|
|
139810
|
+
async function getPidsUnix() {
|
|
139811
|
+
try {
|
|
139812
|
+
const output = await run("pgrep", ["-x", "claude"]);
|
|
139813
|
+
return output.trim().split("\n").filter(Boolean).map((s3) => Number.parseInt(s3, 10));
|
|
139814
|
+
} catch {
|
|
139815
|
+
return [];
|
|
139816
|
+
}
|
|
139817
|
+
}
|
|
139818
|
+
async function getProcessCwdUnix(pid) {
|
|
139819
|
+
const output = await run("lsof", ["-p", String(pid), "-a", "-d", "cwd", "-Fn"]);
|
|
139820
|
+
const match2 = output.match(/n(.+)/);
|
|
139821
|
+
return match2?.[1] ?? "";
|
|
139822
|
+
}
|
|
139823
|
+
async function getProcessArgsUnix(pid) {
|
|
139824
|
+
return (await run("ps", ["-p", String(pid), "-o", "args="])).trim();
|
|
139825
|
+
}
|
|
139826
|
+
async function getProcessStartTimeUnix(pid) {
|
|
139827
|
+
const raw2 = (await run("ps", ["-p", String(pid), "-o", "lstart="])).trim();
|
|
139828
|
+
const d = new Date(raw2);
|
|
139829
|
+
return Number.isNaN(d.getTime()) ? /* @__PURE__ */ new Date() : d;
|
|
139830
|
+
}
|
|
139831
|
+
async function getPidsWindows() {
|
|
139832
|
+
try {
|
|
139833
|
+
const output = await run("tasklist", ["/FI", "IMAGENAME eq claude.exe", "/FO", "CSV", "/NH"]);
|
|
139834
|
+
return output.trim().split("\n").filter(Boolean).map((line) => {
|
|
139835
|
+
const parts = line.split(",");
|
|
139836
|
+
return Number.parseInt(parts[1]?.replace(/"/g, "") ?? "0", 10);
|
|
139837
|
+
}).filter((pid) => pid > 0);
|
|
139838
|
+
} catch {
|
|
139839
|
+
return [];
|
|
139840
|
+
}
|
|
139841
|
+
}
|
|
139842
|
+
async function getProcessInfoWindows(pid) {
|
|
139843
|
+
try {
|
|
139844
|
+
const output = await run("wmic", [
|
|
139845
|
+
"process",
|
|
139846
|
+
"where",
|
|
139847
|
+
`ProcessId=${pid}`,
|
|
139848
|
+
"get",
|
|
139849
|
+
"CommandLine,CreationDate,ExecutablePath",
|
|
139850
|
+
"/FORMAT:CSV"
|
|
139851
|
+
]);
|
|
139852
|
+
const lines = output.trim().split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
139853
|
+
if (lines.length < 2) return null;
|
|
139854
|
+
const parts = lines[1].split(",");
|
|
139855
|
+
const args = parts[1] ?? "";
|
|
139856
|
+
const creationDate = parts[2] ?? "";
|
|
139857
|
+
const year = creationDate.slice(0, 4);
|
|
139858
|
+
const month = creationDate.slice(4, 6);
|
|
139859
|
+
const day = creationDate.slice(6, 8);
|
|
139860
|
+
const hour = creationDate.slice(8, 10);
|
|
139861
|
+
const min = creationDate.slice(10, 12);
|
|
139862
|
+
const sec = creationDate.slice(12, 14);
|
|
139863
|
+
const startedAt = /* @__PURE__ */ new Date(`${year}-${month}-${day}T${hour}:${min}:${sec}`);
|
|
139864
|
+
if (Number.isNaN(startedAt.getTime())) return null;
|
|
139865
|
+
const exePath = parts[3] ?? "";
|
|
139866
|
+
const cwd = exePath ? (0, import_path19.dirname)(exePath) : "";
|
|
139867
|
+
return { cwd, args, startedAt };
|
|
139868
|
+
} catch {
|
|
139869
|
+
return null;
|
|
139870
|
+
}
|
|
139871
|
+
}
|
|
139872
|
+
function extractResumeId(args) {
|
|
139873
|
+
const match2 = args.match(/--resume\s+(\S+)/);
|
|
139874
|
+
return match2?.[1] ?? null;
|
|
139875
|
+
}
|
|
139876
|
+
async function readGitBranch2(dir) {
|
|
139877
|
+
try {
|
|
139878
|
+
return (await run("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: dir, timeout: 3e3 })).trim();
|
|
139879
|
+
} catch {
|
|
139880
|
+
return "";
|
|
139881
|
+
}
|
|
139882
|
+
}
|
|
139883
|
+
|
|
139199
139884
|
// src/seal.ts
|
|
139200
139885
|
var import_tweetnacl = __toESM(require_nacl_fast(), 1);
|
|
139201
139886
|
var import_tweetnacl_util = __toESM(require_nacl_util(), 1);
|
|
@@ -139384,10 +140069,10 @@ var ReaddirpStream2 = class extends import_node_stream2.Readable {
|
|
|
139384
140069
|
}
|
|
139385
140070
|
async _formatEntry(dirent, path2) {
|
|
139386
140071
|
let entry;
|
|
139387
|
-
const
|
|
140072
|
+
const basename11 = this._isDirent ? dirent.name : dirent;
|
|
139388
140073
|
try {
|
|
139389
|
-
const fullPath = (0, import_node_path9.resolve)((0, import_node_path9.join)(path2,
|
|
139390
|
-
entry = { path: (0, import_node_path9.relative)(this._root, fullPath), fullPath, basename:
|
|
140074
|
+
const fullPath = (0, import_node_path9.resolve)((0, import_node_path9.join)(path2, basename11));
|
|
140075
|
+
entry = { path: (0, import_node_path9.relative)(this._root, fullPath), fullPath, basename: basename11 };
|
|
139391
140076
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
139392
140077
|
} catch (err) {
|
|
139393
140078
|
this._onError(err);
|
|
@@ -139928,9 +140613,9 @@ var NodeFsHandler2 = class {
|
|
|
139928
140613
|
_watchWithNodeFs(path2, listener) {
|
|
139929
140614
|
const opts = this.fsw.options;
|
|
139930
140615
|
const directory = sp.dirname(path2);
|
|
139931
|
-
const
|
|
140616
|
+
const basename11 = sp.basename(path2);
|
|
139932
140617
|
const parent = this.fsw._getWatchedDir(directory);
|
|
139933
|
-
parent.add(
|
|
140618
|
+
parent.add(basename11);
|
|
139934
140619
|
const absolutePath = sp.resolve(path2);
|
|
139935
140620
|
const options = {
|
|
139936
140621
|
persistent: opts.persistent
|
|
@@ -139940,7 +140625,7 @@ var NodeFsHandler2 = class {
|
|
|
139940
140625
|
let closer;
|
|
139941
140626
|
if (opts.usePolling) {
|
|
139942
140627
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
139943
|
-
options.interval = enableBin && isBinaryPath2(
|
|
140628
|
+
options.interval = enableBin && isBinaryPath2(basename11) ? opts.binaryInterval : opts.interval;
|
|
139944
140629
|
closer = setFsWatchFileListener2(path2, absolutePath, options, {
|
|
139945
140630
|
listener,
|
|
139946
140631
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -139963,10 +140648,10 @@ var NodeFsHandler2 = class {
|
|
|
139963
140648
|
return;
|
|
139964
140649
|
}
|
|
139965
140650
|
const dirname17 = sp.dirname(file2);
|
|
139966
|
-
const
|
|
140651
|
+
const basename11 = sp.basename(file2);
|
|
139967
140652
|
const parent = this.fsw._getWatchedDir(dirname17);
|
|
139968
140653
|
let prevStats = stats;
|
|
139969
|
-
if (parent.has(
|
|
140654
|
+
if (parent.has(basename11))
|
|
139970
140655
|
return;
|
|
139971
140656
|
const listener = async (path2, newStats) => {
|
|
139972
140657
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH2, file2, 5))
|
|
@@ -139991,9 +140676,9 @@ var NodeFsHandler2 = class {
|
|
|
139991
140676
|
prevStats = newStats2;
|
|
139992
140677
|
}
|
|
139993
140678
|
} catch (error51) {
|
|
139994
|
-
this.fsw._remove(dirname17,
|
|
140679
|
+
this.fsw._remove(dirname17, basename11);
|
|
139995
140680
|
}
|
|
139996
|
-
} else if (parent.has(
|
|
140681
|
+
} else if (parent.has(basename11)) {
|
|
139997
140682
|
const at2 = newStats.atimeMs;
|
|
139998
140683
|
const mt2 = newStats.mtimeMs;
|
|
139999
140684
|
if (!at2 || at2 <= mt2 || mt2 !== prevStats.mtimeMs) {
|
|
@@ -140950,7 +141635,7 @@ function watch2(paths, options = {}) {
|
|
|
140950
141635
|
var chokidar_default = { watch: watch2, FSWatcher: FSWatcher2 };
|
|
140951
141636
|
|
|
140952
141637
|
// src/services/conversations/conversationWatcher.ts
|
|
140953
|
-
var
|
|
141638
|
+
var import_fs22 = require("fs");
|
|
140954
141639
|
var import_promises12 = require("fs/promises");
|
|
140955
141640
|
var ConversationWatcher = class {
|
|
140956
141641
|
files = /* @__PURE__ */ new Map();
|
|
@@ -140971,7 +141656,7 @@ var ConversationWatcher = class {
|
|
|
140971
141656
|
if (this.files.has(filePath)) return;
|
|
140972
141657
|
let offset;
|
|
140973
141658
|
try {
|
|
140974
|
-
offset = (0,
|
|
141659
|
+
offset = (0, import_fs22.statSync)(filePath).size;
|
|
140975
141660
|
} catch {
|
|
140976
141661
|
offset = 0;
|
|
140977
141662
|
}
|
|
@@ -141080,14 +141765,14 @@ var ConversationWatcher = class {
|
|
|
141080
141765
|
};
|
|
141081
141766
|
|
|
141082
141767
|
// src/services/conversations/pruneAgentConversations.ts
|
|
141083
|
-
var
|
|
141768
|
+
var import_fs23 = require("fs");
|
|
141084
141769
|
function pruneAgentConversations(cache) {
|
|
141085
141770
|
const db = cache.getDatabase();
|
|
141086
141771
|
const rows = db.prepare("SELECT id, file_path FROM conversation_meta").all();
|
|
141087
141772
|
let pruned = 0;
|
|
141088
141773
|
let missing = 0;
|
|
141089
141774
|
for (const row of rows) {
|
|
141090
|
-
if (!(0,
|
|
141775
|
+
if (!(0, import_fs23.existsSync)(row.file_path)) {
|
|
141091
141776
|
missing += 1;
|
|
141092
141777
|
continue;
|
|
141093
141778
|
}
|
|
@@ -141396,6 +142081,7 @@ function managedToResponse(s3, ptyAttached) {
|
|
|
141396
142081
|
return {
|
|
141397
142082
|
id: s3.id,
|
|
141398
142083
|
conversationId: s3.id,
|
|
142084
|
+
provider: s3.provider ?? CLAUDE_CODE_PROVIDER2,
|
|
141399
142085
|
status: s3.status,
|
|
141400
142086
|
projectPath: s3.projectPath,
|
|
141401
142087
|
projectName: s3.projectName,
|
|
@@ -141421,13 +142107,15 @@ function managedToResponse(s3, ptyAttached) {
|
|
|
141421
142107
|
...s3.failureReason != null && { failureReason: s3.failureReason },
|
|
141422
142108
|
...s3.resumedFromConversationId != null && {
|
|
141423
142109
|
resumedFromConversationId: s3.resumedFromConversationId
|
|
141424
|
-
}
|
|
142110
|
+
},
|
|
142111
|
+
...s3.boundConversationId != null && { boundConversationId: s3.boundConversationId }
|
|
141425
142112
|
};
|
|
141426
142113
|
}
|
|
141427
142114
|
function discoveredToResponse(d, conversationId) {
|
|
141428
142115
|
return {
|
|
141429
142116
|
id: conversationId,
|
|
141430
142117
|
conversationId,
|
|
142118
|
+
provider: CLAUDE_CODE_PROVIDER2,
|
|
141431
142119
|
status: "idle",
|
|
141432
142120
|
projectPath: d.projectPath,
|
|
141433
142121
|
projectName: d.projectName,
|
|
@@ -141443,10 +142131,10 @@ function discoveredToResponse(d, conversationId) {
|
|
|
141443
142131
|
}
|
|
141444
142132
|
|
|
141445
142133
|
// src/uploads.ts
|
|
141446
|
-
var
|
|
142134
|
+
var import_crypto8 = require("crypto");
|
|
141447
142135
|
var import_promises13 = require("fs/promises");
|
|
141448
142136
|
var import_heic_convert = __toESM(require_heic_convert(), 1);
|
|
141449
|
-
var
|
|
142137
|
+
var import_path20 = require("path");
|
|
141450
142138
|
var UPLOAD_DIR_NAME = ".threadbase-uploads";
|
|
141451
142139
|
var MAX_BYTES = 25 * 1024 * 1024;
|
|
141452
142140
|
var HEIC_MIMES = /* @__PURE__ */ new Set(["image/heic", "image/heif"]);
|
|
@@ -141477,11 +142165,11 @@ async function saveUploadFile(input) {
|
|
|
141477
142165
|
mimeType = "image/jpeg";
|
|
141478
142166
|
originalName = originalName.replace(/\.(heic|heif)$/i, ".jpg");
|
|
141479
142167
|
}
|
|
141480
|
-
const id = `up_${(0,
|
|
142168
|
+
const id = `up_${(0, import_crypto8.randomBytes)(8).toString("hex")}`;
|
|
141481
142169
|
const safeName = sanitizeFilename(originalName) || `file${MIME_TO_EXT[mimeType] ?? ""}`;
|
|
141482
|
-
const dir = (0,
|
|
142170
|
+
const dir = (0, import_path20.join)(input.projectPath, UPLOAD_DIR_NAME, input.sessionId);
|
|
141483
142171
|
await (0, import_promises13.mkdir)(dir, { recursive: true });
|
|
141484
|
-
const filePath = (0,
|
|
142172
|
+
const filePath = (0, import_path20.join)(dir, `${Date.now()}-${id}-${safeName}`);
|
|
141485
142173
|
await (0, import_promises13.writeFile)(filePath, buffer);
|
|
141486
142174
|
return {
|
|
141487
142175
|
id,
|
|
@@ -141994,10 +142682,10 @@ var StreamerServer = class {
|
|
|
141994
142682
|
this.verbose = config2.verbose ?? false;
|
|
141995
142683
|
this.disableDb = config2.disableDb ?? false;
|
|
141996
142684
|
this.scanProfiles = config2.scanProfiles;
|
|
141997
|
-
this.codexRoots = config2.codexRoots ?? [(0,
|
|
142685
|
+
this.codexRoots = config2.codexRoots ?? [(0, import_path21.join)((0, import_os10.homedir)(), ".codex", "sessions")];
|
|
141998
142686
|
this.ptyGracePeriodMs = config2.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
|
|
141999
142687
|
this.defaultSystemPrompt = config2.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
142000
|
-
this.cacheDir = config2.cacheDir ?? loadCacheDir() ?? (0,
|
|
142688
|
+
this.cacheDir = config2.cacheDir ?? loadCacheDir() ?? (0, import_path21.join)((0, import_os10.homedir)(), ".threadbase", "cache");
|
|
142001
142689
|
this.tailSize = config2.tailSize ?? loadTailSize() ?? 10;
|
|
142002
142690
|
this.directoryDebounceMs = parseDirScanDebounceEnv(process.env.THREADBASE_DIR_SCAN_DEBOUNCE_MS) ?? config2.directoryScanDebounceMs ?? 1e3;
|
|
142003
142691
|
this.markScannerStaleDebounced = debounce(() => {
|
|
@@ -142081,7 +142769,7 @@ var StreamerServer = class {
|
|
|
142081
142769
|
});
|
|
142082
142770
|
}
|
|
142083
142771
|
});
|
|
142084
|
-
this.ptyManager = new
|
|
142772
|
+
this.ptyManager = new LiveSessionManager({
|
|
142085
142773
|
logger: getLogger("pty"),
|
|
142086
142774
|
onOutput: (sessionId, data) => {
|
|
142087
142775
|
this.wsHub.broadcast({ type: "terminal_output", sessionId, data });
|
|
@@ -142162,7 +142850,7 @@ var StreamerServer = class {
|
|
|
142162
142850
|
temporalClient,
|
|
142163
142851
|
taskQueue: agentConfig.temporal.taskQueue
|
|
142164
142852
|
});
|
|
142165
|
-
const conversationsBaseDir = agentConfig.conversationsDir || (0,
|
|
142853
|
+
const conversationsBaseDir = agentConfig.conversationsDir || (0, import_path21.join)((0, import_path21.dirname)(this.cacheDir), "conversations");
|
|
142166
142854
|
conversationWriter = createConversationWriter({
|
|
142167
142855
|
baseDir: conversationsBaseDir
|
|
142168
142856
|
});
|
|
@@ -142384,7 +143072,7 @@ var StreamerServer = class {
|
|
|
142384
143072
|
});
|
|
142385
143073
|
try {
|
|
142386
143074
|
this.cache = ConversationCache.open(
|
|
142387
|
-
(0,
|
|
143075
|
+
(0, import_path21.join)(this.cacheDir, "cache.db"),
|
|
142388
143076
|
this.tailSize,
|
|
142389
143077
|
void 0,
|
|
142390
143078
|
{
|
|
@@ -142410,18 +143098,19 @@ var StreamerServer = class {
|
|
|
142410
143098
|
if (this.scanProfiles && this.scanProfiles.length > 0) {
|
|
142411
143099
|
for (const profile of this.scanProfiles) {
|
|
142412
143100
|
if (profile.enabled) {
|
|
142413
|
-
this.fileWatcher.watchDirectory((0,
|
|
143101
|
+
this.fileWatcher.watchDirectory((0, import_path21.join)(profile.configDir, "projects"));
|
|
142414
143102
|
}
|
|
142415
143103
|
}
|
|
142416
143104
|
} else {
|
|
142417
|
-
this.fileWatcher.watchDirectory((0,
|
|
143105
|
+
this.fileWatcher.watchDirectory((0, import_path21.join)((0, import_os10.homedir)(), ".claude", "projects"));
|
|
142418
143106
|
}
|
|
142419
143107
|
} catch (err) {
|
|
142420
143108
|
const message = err instanceof Error ? err.message : String(err);
|
|
142421
|
-
|
|
142422
|
-
|
|
142423
|
-
|
|
142424
|
-
|
|
143109
|
+
const abiMismatch = message.includes("NODE_MODULE_VERSION") || message.includes("was compiled against a different Node.js version");
|
|
143110
|
+
this.log.error(
|
|
143111
|
+
`ConversationCache failed to open \u2014 running WITHOUT cache; /api/conversations, /api/conversations/count and /project-chats will 500.` + (abiMismatch ? ` Fix: npm rebuild better-sqlite3` : "") + ` (${message})`,
|
|
143112
|
+
{ error: message, abiMismatch, event: "cache.open_failed" }
|
|
143113
|
+
);
|
|
142425
143114
|
}
|
|
142426
143115
|
const warmupScanner = new ConversationScanner();
|
|
142427
143116
|
this.allScanners.add(warmupScanner);
|
|
@@ -142908,17 +143597,17 @@ var StreamerServer = class {
|
|
|
142908
143597
|
return this.getScanner();
|
|
142909
143598
|
}
|
|
142910
143599
|
findJsonlPath(uuid3) {
|
|
142911
|
-
const projectsDir = (0,
|
|
142912
|
-
if (!(0,
|
|
143600
|
+
const projectsDir = (0, import_path21.join)((0, import_os10.homedir)(), ".claude", "projects");
|
|
143601
|
+
if (!(0, import_fs24.existsSync)(projectsDir)) return null;
|
|
142913
143602
|
const filename = `${uuid3}.jsonl`;
|
|
142914
|
-
for (const dir of (0,
|
|
142915
|
-
const fp = (0,
|
|
142916
|
-
if ((0,
|
|
142917
|
-
const projectDir = (0,
|
|
143603
|
+
for (const dir of (0, import_fs24.readdirSync)(projectsDir)) {
|
|
143604
|
+
const fp = (0, import_path21.join)(projectsDir, dir, filename);
|
|
143605
|
+
if ((0, import_fs24.existsSync)(fp)) return fp;
|
|
143606
|
+
const projectDir = (0, import_path21.join)(projectsDir, dir);
|
|
142918
143607
|
try {
|
|
142919
|
-
for (const sub of (0,
|
|
142920
|
-
const subagentPath = (0,
|
|
142921
|
-
if ((0,
|
|
143608
|
+
for (const sub of (0, import_fs24.readdirSync)(projectDir)) {
|
|
143609
|
+
const subagentPath = (0, import_path21.join)(projectDir, sub, "subagents", filename);
|
|
143610
|
+
if ((0, import_fs24.existsSync)(subagentPath)) return subagentPath;
|
|
142922
143611
|
}
|
|
142923
143612
|
} catch {
|
|
142924
143613
|
}
|
|
@@ -142927,7 +143616,7 @@ var StreamerServer = class {
|
|
|
142927
143616
|
}
|
|
142928
143617
|
async readCwdFromJsonl(filePath) {
|
|
142929
143618
|
return new Promise((resolve6) => {
|
|
142930
|
-
const rl = (0, import_readline4.createInterface)({ input: (0,
|
|
143619
|
+
const rl = (0, import_readline4.createInterface)({ input: (0, import_fs24.createReadStream)(filePath), crlfDelay: Infinity });
|
|
142931
143620
|
let found = false;
|
|
142932
143621
|
rl.on("line", (line) => {
|
|
142933
143622
|
if (found) return;
|
|
@@ -142984,7 +143673,7 @@ var StreamerServer = class {
|
|
|
142984
143673
|
if (!conv.filePath) return false;
|
|
142985
143674
|
let mtimeMs = null;
|
|
142986
143675
|
try {
|
|
142987
|
-
mtimeMs = (0,
|
|
143676
|
+
mtimeMs = (0, import_fs24.statSync)(conv.filePath).mtimeMs;
|
|
142988
143677
|
} catch {
|
|
142989
143678
|
return false;
|
|
142990
143679
|
}
|
|
@@ -143227,7 +143916,7 @@ var StreamerServer = class {
|
|
|
143227
143916
|
handleGetSession(sessionId, res) {
|
|
143228
143917
|
const session = this.sessionStore.get(sessionId, this.ptyAttachedIds());
|
|
143229
143918
|
if (session) {
|
|
143230
|
-
if (!(0,
|
|
143919
|
+
if (!(0, import_fs24.existsSync)(session.projectPath)) {
|
|
143231
143920
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
143232
143921
|
}
|
|
143233
143922
|
json2(res, 200, session);
|
|
@@ -143267,7 +143956,10 @@ var StreamerServer = class {
|
|
|
143267
143956
|
json2(res, 400, { error: "Could not determine project path" });
|
|
143268
143957
|
return;
|
|
143269
143958
|
}
|
|
143959
|
+
const cachedConvMeta = this.cache?.getMetaById(sessionId);
|
|
143960
|
+
const provider = conv?.provider ?? cachedConvMeta?.provider ?? CLAUDE_CODE_PROVIDER2;
|
|
143270
143961
|
const session = await this.ptyManager.start(sessionId, {
|
|
143962
|
+
provider,
|
|
143271
143963
|
projectPath,
|
|
143272
143964
|
projectName: body.projectName,
|
|
143273
143965
|
branch: body.branch
|
|
@@ -143617,7 +144309,7 @@ var StreamerServer = class {
|
|
|
143617
144309
|
sessionStore: this.sessionStore,
|
|
143618
144310
|
// biome-ignore lint/style/noNonNullAssertion: agentClient is set when agentConfig.enabled is true
|
|
143619
144311
|
agentClient: this.agentClient,
|
|
143620
|
-
conversationsDir: this.cacheDir ? (0,
|
|
144312
|
+
conversationsDir: this.cacheDir ? (0, import_path21.join)((0, import_path21.dirname)(this.cacheDir), "conversations") : "",
|
|
143621
144313
|
agentConfig: this.agentConfig
|
|
143622
144314
|
});
|
|
143623
144315
|
json2(res, result.status, result.body);
|
|
@@ -143626,6 +144318,13 @@ var StreamerServer = class {
|
|
|
143626
144318
|
}
|
|
143627
144319
|
return;
|
|
143628
144320
|
}
|
|
144321
|
+
const body = await readBody(req);
|
|
144322
|
+
const { path: relativePath, provider: requestedProvider, systemPrompt: clientPrompt } = body;
|
|
144323
|
+
if (requestedProvider !== void 0 && !isProviderName(requestedProvider)) {
|
|
144324
|
+
json2(res, 400, { error: "Invalid provider" });
|
|
144325
|
+
return;
|
|
144326
|
+
}
|
|
144327
|
+
const provider = requestedProvider ?? CLAUDE_CODE_PROVIDER2;
|
|
143629
144328
|
if (!this.browseRoot) {
|
|
143630
144329
|
json2(res, 403, {
|
|
143631
144330
|
error: "File browsing not configured. Set browseRoot on the server.",
|
|
@@ -143633,8 +144332,6 @@ var StreamerServer = class {
|
|
|
143633
144332
|
});
|
|
143634
144333
|
return;
|
|
143635
144334
|
}
|
|
143636
|
-
const body = await readBody(req);
|
|
143637
|
-
const { path: relativePath, systemPrompt: clientPrompt } = body;
|
|
143638
144335
|
if (typeof relativePath !== "string") {
|
|
143639
144336
|
json2(res, 400, { error: "Missing path field" });
|
|
143640
144337
|
return;
|
|
@@ -143655,21 +144352,27 @@ var StreamerServer = class {
|
|
|
143655
144352
|
].filter(Boolean);
|
|
143656
144353
|
try {
|
|
143657
144354
|
const session = await this.ptyManager.startFresh({
|
|
144355
|
+
provider,
|
|
143658
144356
|
projectPath: resolvedPath,
|
|
143659
144357
|
projectName: body.projectName,
|
|
143660
144358
|
systemPrompt: systemPromptParts.join("\n")
|
|
143661
144359
|
});
|
|
143662
144360
|
this.sessionStore.addManaged(session);
|
|
143663
144361
|
json2(res, 202, { id: session.id, status: "pending" });
|
|
143664
|
-
|
|
144362
|
+
if (provider === CODEX_CLI_PROVIDER2) {
|
|
144363
|
+
this.watchForCodexRollout(session.id, resolvedPath);
|
|
144364
|
+
} else {
|
|
144365
|
+
this.watchForJsonl(session.id, resolvedPath);
|
|
144366
|
+
}
|
|
143665
144367
|
this.broadcastOrUnicastSessionList(req);
|
|
143666
144368
|
} catch (err) {
|
|
143667
144369
|
const message = err instanceof Error ? err.message : "Failed to start session";
|
|
144370
|
+
const statusCode = typeof err.statusCode === "number" ? err.statusCode : 500;
|
|
143668
144371
|
this.log.error(`[start] failed to start session: ${message}`, {
|
|
143669
144372
|
event: "session.start_failed",
|
|
143670
144373
|
error: message
|
|
143671
144374
|
});
|
|
143672
|
-
json2(res,
|
|
144375
|
+
json2(res, statusCode, { error: message });
|
|
143673
144376
|
}
|
|
143674
144377
|
}
|
|
143675
144378
|
// ─── Project linking ─────────────────────────────────────────────
|
|
@@ -143722,9 +144425,9 @@ var StreamerServer = class {
|
|
|
143722
144425
|
// was passed to Claude via --session-id so the filename matches from the start.
|
|
143723
144426
|
watchForJsonl(sessionId, projectPath) {
|
|
143724
144427
|
const encoded = projectPath.replace(/[/\\:.]/g, "-");
|
|
143725
|
-
const projectsDir = (0,
|
|
144428
|
+
const projectsDir = (0, import_path21.join)((0, import_os10.homedir)(), ".claude", "projects", encoded);
|
|
143726
144429
|
const expectedFile = `${sessionId}.jsonl`;
|
|
143727
|
-
const filePath = (0,
|
|
144430
|
+
const filePath = (0, import_path21.join)(projectsDir, expectedFile);
|
|
143728
144431
|
const deadline = Date.now() + 12e4;
|
|
143729
144432
|
let watcher = null;
|
|
143730
144433
|
const cleanup = () => {
|
|
@@ -143742,12 +144445,12 @@ var StreamerServer = class {
|
|
|
143742
144445
|
cleanup();
|
|
143743
144446
|
return;
|
|
143744
144447
|
}
|
|
143745
|
-
let resolvedFilePath = (0,
|
|
143746
|
-
if (!resolvedFilePath && (0,
|
|
144448
|
+
let resolvedFilePath = (0, import_fs24.existsSync)(filePath) ? filePath : null;
|
|
144449
|
+
if (!resolvedFilePath && (0, import_fs24.existsSync)(projectsDir)) {
|
|
143747
144450
|
try {
|
|
143748
144451
|
const now = Date.now();
|
|
143749
|
-
const recent = (0,
|
|
143750
|
-
if (recent) resolvedFilePath = (0,
|
|
144452
|
+
const recent = (0, import_fs24.readdirSync)(projectsDir).filter((f2) => f2.endsWith(".jsonl")).map((f2) => ({ f: f2, mtime: (0, import_fs24.statSync)((0, import_path21.join)(projectsDir, f2)).mtimeMs })).filter(({ mtime }) => now - mtime < 5e3).sort((a, b2) => b2.mtime - a.mtime)[0];
|
|
144453
|
+
if (recent) resolvedFilePath = (0, import_path21.join)(projectsDir, recent.f);
|
|
143751
144454
|
} catch {
|
|
143752
144455
|
}
|
|
143753
144456
|
}
|
|
@@ -143756,7 +144459,7 @@ var StreamerServer = class {
|
|
|
143756
144459
|
this.sessionFileMap.set(sessionId, resolvedFilePath);
|
|
143757
144460
|
this.fileWatcher.watch(resolvedFilePath);
|
|
143758
144461
|
try {
|
|
143759
|
-
const existing = (0,
|
|
144462
|
+
const existing = (0, import_fs24.readFileSync)(resolvedFilePath, "utf8").split("\n").filter(Boolean);
|
|
143760
144463
|
if (existing.length > 0) {
|
|
143761
144464
|
this.wsHub.broadcast({ type: "conversation_events", sessionId, lines: existing });
|
|
143762
144465
|
for (const line of existing) {
|
|
@@ -143782,11 +144485,126 @@ var StreamerServer = class {
|
|
|
143782
144485
|
if (this.sessionFileMap.has(sessionId)) return;
|
|
143783
144486
|
try {
|
|
143784
144487
|
require("fs").mkdirSync(projectsDir, { recursive: true });
|
|
143785
|
-
watcher = (0,
|
|
144488
|
+
watcher = (0, import_fs24.watch)(projectsDir, tryWire);
|
|
143786
144489
|
watcher.on("error", cleanup);
|
|
143787
144490
|
} catch {
|
|
143788
144491
|
}
|
|
143789
144492
|
}
|
|
144493
|
+
// Codex-equivalent of watchForJsonl(). Differs because Codex has no
|
|
144494
|
+
// filename-encoded session id (it assigns its own persisted id) and its
|
|
144495
|
+
// rollout files live under a date-nested directory
|
|
144496
|
+
// (~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-*.jsonl) that Codex creates
|
|
144497
|
+
// itself — it may not exist yet when this function is first called, so we
|
|
144498
|
+
// poll rather than fs.watch a not-yet-existent directory. Per Phase 0
|
|
144499
|
+
// findings, the rollout file appears within ~1s of process spawn (after
|
|
144500
|
+
// any directory-trust gate is cleared), well before any user input.
|
|
144501
|
+
watchForCodexRollout(sessionId, projectPath) {
|
|
144502
|
+
const deadline = Date.now() + 12e4;
|
|
144503
|
+
const now = /* @__PURE__ */ new Date();
|
|
144504
|
+
const dateDir = (0, import_path21.join)(
|
|
144505
|
+
String(now.getFullYear()),
|
|
144506
|
+
String(now.getMonth() + 1).padStart(2, "0"),
|
|
144507
|
+
String(now.getDate()).padStart(2, "0")
|
|
144508
|
+
);
|
|
144509
|
+
const sessionStartedAtMs = (this.sessionStore.getManaged(sessionId)?.startedAt?.getTime() ?? Date.now()) - 5e3;
|
|
144510
|
+
let intervalHandle = null;
|
|
144511
|
+
const cleanup = () => {
|
|
144512
|
+
if (intervalHandle) clearInterval(intervalHandle);
|
|
144513
|
+
intervalHandle = null;
|
|
144514
|
+
};
|
|
144515
|
+
const matchesProjectPath = (candidatePath) => {
|
|
144516
|
+
try {
|
|
144517
|
+
const firstLine = (0, import_fs24.readFileSync)(candidatePath, "utf8").split("\n", 1)[0];
|
|
144518
|
+
if (!firstLine) return null;
|
|
144519
|
+
const parsed = JSON.parse(firstLine);
|
|
144520
|
+
if (parsed?.type !== "session_meta") return null;
|
|
144521
|
+
const payload = parsed.payload ?? {};
|
|
144522
|
+
if (payload.cwd !== projectPath) return null;
|
|
144523
|
+
if (typeof payload.id !== "string") return null;
|
|
144524
|
+
const createdIso = payload.timestamp ?? parsed.timestamp;
|
|
144525
|
+
const createdAtMs = typeof createdIso === "string" ? Date.parse(createdIso) : Number.NaN;
|
|
144526
|
+
if (Number.isNaN(createdAtMs) || createdAtMs < sessionStartedAtMs) return null;
|
|
144527
|
+
return { id: payload.id, createdAtMs };
|
|
144528
|
+
} catch {
|
|
144529
|
+
return null;
|
|
144530
|
+
}
|
|
144531
|
+
};
|
|
144532
|
+
const tryWire = () => {
|
|
144533
|
+
if (!this.ptyManager.hasSession(sessionId)) {
|
|
144534
|
+
cleanup();
|
|
144535
|
+
return;
|
|
144536
|
+
}
|
|
144537
|
+
if (Date.now() > deadline) {
|
|
144538
|
+
cleanup();
|
|
144539
|
+
return;
|
|
144540
|
+
}
|
|
144541
|
+
const boundElsewhere = new Set(
|
|
144542
|
+
this.sessionStore.listManaged().filter((s3) => s3.id !== sessionId && s3.boundConversationId != null).map((s3) => s3.boundConversationId)
|
|
144543
|
+
);
|
|
144544
|
+
for (const root of this.codexRoots) {
|
|
144545
|
+
const sessionsDir = (0, import_path21.join)(root, dateDir);
|
|
144546
|
+
if (!(0, import_fs24.existsSync)(sessionsDir)) continue;
|
|
144547
|
+
let candidateFiles;
|
|
144548
|
+
try {
|
|
144549
|
+
candidateFiles = (0, import_fs24.readdirSync)(sessionsDir).filter((f2) => f2.endsWith(".jsonl"));
|
|
144550
|
+
} catch {
|
|
144551
|
+
continue;
|
|
144552
|
+
}
|
|
144553
|
+
const nowMs = Date.now();
|
|
144554
|
+
const recentCandidates = candidateFiles.map((f2) => ({ f: f2, mtime: (0, import_fs24.statSync)((0, import_path21.join)(sessionsDir, f2)).mtimeMs })).filter(({ mtime }) => nowMs - mtime < 1e4).sort((a, b2) => b2.mtime - a.mtime);
|
|
144555
|
+
for (const { f: f2 } of recentCandidates) {
|
|
144556
|
+
const candidatePath = (0, import_path21.join)(sessionsDir, f2);
|
|
144557
|
+
const match2 = matchesProjectPath(candidatePath);
|
|
144558
|
+
if (!match2) continue;
|
|
144559
|
+
if (boundElsewhere.has(match2.id)) continue;
|
|
144560
|
+
const codexSessionId = match2.id;
|
|
144561
|
+
cleanup();
|
|
144562
|
+
this.sessionStore.updateManaged(sessionId, { boundConversationId: codexSessionId });
|
|
144563
|
+
this.sessionFileMap.set(sessionId, candidatePath);
|
|
144564
|
+
this.fileWatcher.watch(candidatePath);
|
|
144565
|
+
try {
|
|
144566
|
+
const existing = (0, import_fs24.readFileSync)(candidatePath, "utf8").split("\n").filter(Boolean);
|
|
144567
|
+
if (existing.length > 0) {
|
|
144568
|
+
this.wsHub.broadcast({ type: "conversation_events", sessionId, lines: existing });
|
|
144569
|
+
for (const line of existing) {
|
|
144570
|
+
this.wsHub.broadcast({ type: "conversation_event", sessionId, line });
|
|
144571
|
+
}
|
|
144572
|
+
}
|
|
144573
|
+
} catch {
|
|
144574
|
+
}
|
|
144575
|
+
if (this.scannerReady) {
|
|
144576
|
+
this.scannerStale = true;
|
|
144577
|
+
} else {
|
|
144578
|
+
this.scanner = null;
|
|
144579
|
+
}
|
|
144580
|
+
this.linkSessionToProject(sessionId, projectPath, candidatePath);
|
|
144581
|
+
this.cache?.markAsStreamer(sessionId);
|
|
144582
|
+
const resp = this.sessionStore.get(sessionId, this.ptyAttachedIds());
|
|
144583
|
+
if (resp) {
|
|
144584
|
+
this.wsHub.broadcast({ type: "session_update", session: resp });
|
|
144585
|
+
}
|
|
144586
|
+
this.log.info(
|
|
144587
|
+
`[startFresh] bound Codex rollout for ${sessionId}`,
|
|
144588
|
+
{
|
|
144589
|
+
event: "session.codex_rollout_bound",
|
|
144590
|
+
sessionId,
|
|
144591
|
+
boundConversationId: codexSessionId,
|
|
144592
|
+
filePath: candidatePath
|
|
144593
|
+
},
|
|
144594
|
+
"pino"
|
|
144595
|
+
);
|
|
144596
|
+
return;
|
|
144597
|
+
}
|
|
144598
|
+
}
|
|
144599
|
+
};
|
|
144600
|
+
tryWire();
|
|
144601
|
+
if (!intervalHandle && Date.now() <= deadline) {
|
|
144602
|
+
const alreadyBound = this.sessionStore.getManaged(sessionId)?.boundConversationId != null;
|
|
144603
|
+
if (!alreadyBound) {
|
|
144604
|
+
intervalHandle = setInterval(tryWire, 250);
|
|
144605
|
+
}
|
|
144606
|
+
}
|
|
144607
|
+
}
|
|
143790
144608
|
async handleBrowse(url2, res) {
|
|
143791
144609
|
if (!this.browseRoot) {
|
|
143792
144610
|
json2(res, 403, {
|
|
@@ -143870,7 +144688,7 @@ var StreamerServer = class {
|
|
|
143870
144688
|
};
|
|
143871
144689
|
function classifyResumability(cwd) {
|
|
143872
144690
|
if (!cwd) return { resumable: true };
|
|
143873
|
-
if ((0,
|
|
144691
|
+
if ((0, import_fs24.existsSync)(cwd)) return { resumable: true };
|
|
143874
144692
|
const ranInWorktree = /\/\.worktrees\//.test(cwd) || /\/\.claude\/worktrees\//.test(cwd);
|
|
143875
144693
|
return {
|
|
143876
144694
|
resumable: false,
|
|
@@ -144450,13 +145268,13 @@ var import_node_fs17 = require("fs");
|
|
|
144450
145268
|
|
|
144451
145269
|
// node_modules/tar/dist/esm/index.min.js
|
|
144452
145270
|
var import_events4 = __toESM(require("events"), 1);
|
|
144453
|
-
var
|
|
145271
|
+
var import_fs25 = __toESM(require("fs"), 1);
|
|
144454
145272
|
var import_node_events3 = require("events");
|
|
144455
145273
|
var import_node_stream3 = __toESM(require("stream"), 1);
|
|
144456
145274
|
var import_node_string_decoder = require("string_decoder");
|
|
144457
145275
|
var import_node_path14 = __toESM(require("path"), 1);
|
|
144458
145276
|
var import_node_fs11 = __toESM(require("fs"), 1);
|
|
144459
|
-
var
|
|
145277
|
+
var import_path22 = require("path");
|
|
144460
145278
|
var import_events5 = require("events");
|
|
144461
145279
|
var import_assert = __toESM(require("assert"), 1);
|
|
144462
145280
|
var import_buffer = require("buffer");
|
|
@@ -144464,17 +145282,17 @@ var Ps = __toESM(require("zlib"), 1);
|
|
|
144464
145282
|
var import_zlib = __toESM(require("zlib"), 1);
|
|
144465
145283
|
var import_node_path15 = require("path");
|
|
144466
145284
|
var import_node_path16 = require("path");
|
|
144467
|
-
var import_fs25 = __toESM(require("fs"), 1);
|
|
144468
145285
|
var import_fs26 = __toESM(require("fs"), 1);
|
|
144469
|
-
var
|
|
145286
|
+
var import_fs27 = __toESM(require("fs"), 1);
|
|
145287
|
+
var import_path23 = __toESM(require("path"), 1);
|
|
144470
145288
|
var import_node_path17 = require("path");
|
|
144471
|
-
var
|
|
145289
|
+
var import_path24 = __toESM(require("path"), 1);
|
|
144472
145290
|
var import_node_fs12 = __toESM(require("fs"), 1);
|
|
144473
145291
|
var import_node_assert = __toESM(require("assert"), 1);
|
|
144474
145292
|
var import_node_crypto5 = require("crypto");
|
|
144475
145293
|
var import_node_fs13 = __toESM(require("fs"), 1);
|
|
144476
145294
|
var import_node_path18 = __toESM(require("path"), 1);
|
|
144477
|
-
var
|
|
145295
|
+
var import_fs28 = __toESM(require("fs"), 1);
|
|
144478
145296
|
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
144479
145297
|
var import_node_path19 = __toESM(require("path"), 1);
|
|
144480
145298
|
var import_node_fs15 = __toESM(require("fs"), 1);
|
|
@@ -144826,7 +145644,7 @@ var A = class extends import_node_events3.EventEmitter {
|
|
|
144826
145644
|
return Wr;
|
|
144827
145645
|
}
|
|
144828
145646
|
};
|
|
144829
|
-
var Jr =
|
|
145647
|
+
var Jr = import_fs25.default.writev;
|
|
144830
145648
|
var ht = /* @__PURE__ */ Symbol("_autoClose");
|
|
144831
145649
|
var H = /* @__PURE__ */ Symbol("_close");
|
|
144832
145650
|
var te = /* @__PURE__ */ Symbol("_ended");
|
|
@@ -144881,7 +145699,7 @@ var _t = class extends A {
|
|
|
144881
145699
|
throw new TypeError("this is a readable stream");
|
|
144882
145700
|
}
|
|
144883
145701
|
[at]() {
|
|
144884
|
-
|
|
145702
|
+
import_fs25.default.open(this[U2], "r", (t2, e) => this[Ht](t2, e));
|
|
144885
145703
|
}
|
|
144886
145704
|
[Ht](t2, e) {
|
|
144887
145705
|
t2 ? this[Ut](t2) : (this[u2] = e, this.emit("open", e), this[zt]());
|
|
@@ -144894,7 +145712,7 @@ var _t = class extends A {
|
|
|
144894
145712
|
this[j] = true;
|
|
144895
145713
|
let t2 = this[vi]();
|
|
144896
145714
|
if (t2.length === 0) return process.nextTick(() => this[Ii](null, 0, t2));
|
|
144897
|
-
|
|
145715
|
+
import_fs25.default.read(this[u2], t2, 0, t2.length, null, (e, i, r) => this[Ii](e, i, r));
|
|
144898
145716
|
}
|
|
144899
145717
|
}
|
|
144900
145718
|
[Ii](t2, e, i) {
|
|
@@ -144903,7 +145721,7 @@ var _t = class extends A {
|
|
|
144903
145721
|
[H]() {
|
|
144904
145722
|
if (this[ht] && typeof this[u2] == "number") {
|
|
144905
145723
|
let t2 = this[u2];
|
|
144906
|
-
this[u2] = void 0,
|
|
145724
|
+
this[u2] = void 0, import_fs25.default.close(t2, (e) => e ? this.emit("error", e) : this.emit("close"));
|
|
144907
145725
|
}
|
|
144908
145726
|
}
|
|
144909
145727
|
[Ut](t2) {
|
|
@@ -144931,7 +145749,7 @@ var Be = class extends _t {
|
|
|
144931
145749
|
[at]() {
|
|
144932
145750
|
let t2 = true;
|
|
144933
145751
|
try {
|
|
144934
|
-
this[Ht](null,
|
|
145752
|
+
this[Ht](null, import_fs25.default.openSync(this[U2], "r")), t2 = false;
|
|
144935
145753
|
} finally {
|
|
144936
145754
|
t2 && this[H]();
|
|
144937
145755
|
}
|
|
@@ -144942,7 +145760,7 @@ var Be = class extends _t {
|
|
|
144942
145760
|
if (!this[j]) {
|
|
144943
145761
|
this[j] = true;
|
|
144944
145762
|
do {
|
|
144945
|
-
let e = this[vi](), i = e.length === 0 ? 0 :
|
|
145763
|
+
let e = this[vi](), i = e.length === 0 ? 0 : import_fs25.default.readSync(this[u2], e, 0, e.length, null);
|
|
144946
145764
|
if (!this[ki](i, e)) break;
|
|
144947
145765
|
} while (true);
|
|
144948
145766
|
this[j] = false;
|
|
@@ -144955,7 +145773,7 @@ var Be = class extends _t {
|
|
|
144955
145773
|
[H]() {
|
|
144956
145774
|
if (this[ht] && typeof this[u2] == "number") {
|
|
144957
145775
|
let t2 = this[u2];
|
|
144958
|
-
this[u2] = void 0,
|
|
145776
|
+
this[u2] = void 0, import_fs25.default.closeSync(t2), this.emit("close");
|
|
144959
145777
|
}
|
|
144960
145778
|
}
|
|
144961
145779
|
};
|
|
@@ -144997,7 +145815,7 @@ var et = class extends import_events4.default {
|
|
|
144997
145815
|
this[H](), this[gt] = true, this.emit("error", t2);
|
|
144998
145816
|
}
|
|
144999
145817
|
[at]() {
|
|
145000
|
-
|
|
145818
|
+
import_fs25.default.open(this[U2], this[tt], this[ie], (t2, e) => this[Ht](t2, e));
|
|
145001
145819
|
}
|
|
145002
145820
|
[Ht](t2, e) {
|
|
145003
145821
|
this[Me] && this[tt] === "r+" && t2 && t2.code === "ENOENT" ? (this[tt] = "w", this[at]()) : t2 ? this[Ut](t2) : (this[u2] = e, this.emit("open", e), this[gt] || this[Ai]());
|
|
@@ -145009,7 +145827,7 @@ var et = class extends import_events4.default {
|
|
|
145009
145827
|
return typeof t2 == "string" && (t2 = Buffer.from(t2, e)), this[te] ? (this.emit("error", new Error("write() after end()")), false) : this[u2] === void 0 || this[gt] || this[Y2].length ? (this[Y2].push(t2), this[ke] = true, false) : (this[gt] = true, this[ve](t2), true);
|
|
145010
145828
|
}
|
|
145011
145829
|
[ve](t2) {
|
|
145012
|
-
|
|
145830
|
+
import_fs25.default.write(this[u2], t2, 0, t2.length, this[ot], (e, i) => this[Pt](e, i));
|
|
145013
145831
|
}
|
|
145014
145832
|
[Pt](t2, e) {
|
|
145015
145833
|
t2 ? this[Ut](t2) : (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[H](), this.emit("finish")) : this[ke] && (this[ke] = false, this.emit("drain"))));
|
|
@@ -145025,7 +145843,7 @@ var et = class extends import_events4.default {
|
|
|
145025
145843
|
[H]() {
|
|
145026
145844
|
if (this[ht] && typeof this[u2] == "number") {
|
|
145027
145845
|
let t2 = this[u2];
|
|
145028
|
-
this[u2] = void 0,
|
|
145846
|
+
this[u2] = void 0, import_fs25.default.close(t2, (e) => e ? this.emit("error", e) : this.emit("close"));
|
|
145029
145847
|
}
|
|
145030
145848
|
}
|
|
145031
145849
|
};
|
|
@@ -145033,24 +145851,24 @@ var Wt = class extends et {
|
|
|
145033
145851
|
[at]() {
|
|
145034
145852
|
let t2;
|
|
145035
145853
|
if (this[Me] && this[tt] === "r+") try {
|
|
145036
|
-
t2 =
|
|
145854
|
+
t2 = import_fs25.default.openSync(this[U2], this[tt], this[ie]);
|
|
145037
145855
|
} catch (e) {
|
|
145038
145856
|
if (e?.code === "ENOENT") return this[tt] = "w", this[at]();
|
|
145039
145857
|
throw e;
|
|
145040
145858
|
}
|
|
145041
|
-
else t2 =
|
|
145859
|
+
else t2 = import_fs25.default.openSync(this[U2], this[tt], this[ie]);
|
|
145042
145860
|
this[Ht](null, t2);
|
|
145043
145861
|
}
|
|
145044
145862
|
[H]() {
|
|
145045
145863
|
if (this[ht] && typeof this[u2] == "number") {
|
|
145046
145864
|
let t2 = this[u2];
|
|
145047
|
-
this[u2] = void 0,
|
|
145865
|
+
this[u2] = void 0, import_fs25.default.closeSync(t2), this.emit("close");
|
|
145048
145866
|
}
|
|
145049
145867
|
}
|
|
145050
145868
|
[ve](t2) {
|
|
145051
145869
|
let e = true;
|
|
145052
145870
|
try {
|
|
145053
|
-
this[Pt](null,
|
|
145871
|
+
this[Pt](null, import_fs25.default.writeSync(this[u2], t2, 0, t2.length, this[ot])), e = false;
|
|
145054
145872
|
} finally {
|
|
145055
145873
|
if (e) try {
|
|
145056
145874
|
this[H]();
|
|
@@ -145833,11 +146651,11 @@ var vn = (s3) => {
|
|
|
145833
146651
|
};
|
|
145834
146652
|
var Qi = (s3, t2) => {
|
|
145835
146653
|
let e = new Map(t2.map((n) => [ut(n), true])), i = s3.filter, r = (n, o = "") => {
|
|
145836
|
-
let h = o || (0,
|
|
146654
|
+
let h = o || (0, import_path22.parse)(n).root || ".", a;
|
|
145837
146655
|
if (n === h) a = false;
|
|
145838
146656
|
else {
|
|
145839
146657
|
let l = e.get(n);
|
|
145840
|
-
a = l !== void 0 ? l : r((0,
|
|
146658
|
+
a = l !== void 0 ? l : r((0, import_path22.dirname)(n), h);
|
|
145841
146659
|
}
|
|
145842
146660
|
return e.set(n, a), a;
|
|
145843
146661
|
};
|
|
@@ -145957,7 +146775,7 @@ var de = class extends A {
|
|
|
145957
146775
|
let [o, h] = ce(this.path);
|
|
145958
146776
|
o && typeof h == "string" && (this.path = h, r = o);
|
|
145959
146777
|
}
|
|
145960
|
-
this.win32 = !!i.win32 || process.platform === "win32", this.win32 && (this.path = Qs(this.path.replaceAll(/\\/g, "/")), t2 = t2.replaceAll(/\\/g, "/")), this.absolute = f(i.absolute ||
|
|
146778
|
+
this.win32 = !!i.win32 || process.platform === "win32", this.win32 && (this.path = Qs(this.path.replaceAll(/\\/g, "/")), t2 = t2.replaceAll(/\\/g, "/")), this.absolute = f(i.absolute || import_path23.default.resolve(this.cwd, t2)), this.path === "" && (this.path = "./"), r && this.warn("TAR_ENTRY_INFO", `stripping ${r} from absolute path`, { entry: this, path: r + this.path });
|
|
145961
146779
|
let n = this.statCache.get(this.absolute);
|
|
145962
146780
|
n ? this[si](n) : this[ss]();
|
|
145963
146781
|
}
|
|
@@ -145968,7 +146786,7 @@ var de = class extends A {
|
|
|
145968
146786
|
return t2 === "error" && (this.#t = true), super.emit(t2, ...e);
|
|
145969
146787
|
}
|
|
145970
146788
|
[ss]() {
|
|
145971
|
-
|
|
146789
|
+
import_fs27.default.lstat(this.absolute, (t2, e) => {
|
|
145972
146790
|
if (t2) return this.emit("error", t2);
|
|
145973
146791
|
this[si](e);
|
|
145974
146792
|
});
|
|
@@ -146006,7 +146824,7 @@ var de = class extends A {
|
|
|
146006
146824
|
this.path.slice(-1) !== "/" && (this.path += "/"), this.stat.size = 0, this[fe](), this.end();
|
|
146007
146825
|
}
|
|
146008
146826
|
[is]() {
|
|
146009
|
-
|
|
146827
|
+
import_fs27.default.readlink(this.absolute, (t2, e) => {
|
|
146010
146828
|
if (t2) return this.emit("error", t2);
|
|
146011
146829
|
this[ns](e);
|
|
146012
146830
|
});
|
|
@@ -146016,7 +146834,7 @@ var de = class extends A {
|
|
|
146016
146834
|
}
|
|
146017
146835
|
[sr](t2) {
|
|
146018
146836
|
if (!this.stat) throw new Error("cannot create link entry without stat");
|
|
146019
|
-
this.type = "Link", this.linkpath = f(
|
|
146837
|
+
this.type = "Link", this.linkpath = f(import_path23.default.relative(this.cwd, t2)), this.stat.size = 0, this[fe](), this.end();
|
|
146020
146838
|
}
|
|
146021
146839
|
[er]() {
|
|
146022
146840
|
if (!this.stat) throw new Error("cannot create file entry without stat");
|
|
@@ -146029,7 +146847,7 @@ var de = class extends A {
|
|
|
146029
146847
|
this[os]();
|
|
146030
146848
|
}
|
|
146031
146849
|
[os]() {
|
|
146032
|
-
|
|
146850
|
+
import_fs27.default.open(this.absolute, "r", (t2, e) => {
|
|
146033
146851
|
if (t2) return this.emit("error", t2);
|
|
146034
146852
|
this[hs](e);
|
|
146035
146853
|
});
|
|
@@ -146044,14 +146862,14 @@ var de = class extends A {
|
|
|
146044
146862
|
[ii]() {
|
|
146045
146863
|
let { fd: t2, buf: e, offset: i, length: r, pos: n } = this;
|
|
146046
146864
|
if (t2 === void 0 || e === void 0) throw new Error("cannot read file without first opening");
|
|
146047
|
-
|
|
146865
|
+
import_fs27.default.read(t2, e, i, r, n, (o, h) => {
|
|
146048
146866
|
if (o) return this[pt](() => this.emit("error", o));
|
|
146049
146867
|
this[rs](h);
|
|
146050
146868
|
});
|
|
146051
146869
|
}
|
|
146052
146870
|
[pt](t2 = () => {
|
|
146053
146871
|
}) {
|
|
146054
|
-
this.fd !== void 0 &&
|
|
146872
|
+
this.fd !== void 0 && import_fs27.default.close(this.fd, t2);
|
|
146055
146873
|
}
|
|
146056
146874
|
[rs](t2) {
|
|
146057
146875
|
if (t2 <= 0 && this.remain > 0) {
|
|
@@ -146086,20 +146904,20 @@ var de = class extends A {
|
|
|
146086
146904
|
var ni = class extends de {
|
|
146087
146905
|
sync = true;
|
|
146088
146906
|
[ss]() {
|
|
146089
|
-
this[si](
|
|
146907
|
+
this[si](import_fs27.default.lstatSync(this.absolute));
|
|
146090
146908
|
}
|
|
146091
146909
|
[is]() {
|
|
146092
|
-
this[ns](
|
|
146910
|
+
this[ns](import_fs27.default.readlinkSync(this.absolute));
|
|
146093
146911
|
}
|
|
146094
146912
|
[os]() {
|
|
146095
|
-
this[hs](
|
|
146913
|
+
this[hs](import_fs27.default.openSync(this.absolute, "r"));
|
|
146096
146914
|
}
|
|
146097
146915
|
[ii]() {
|
|
146098
146916
|
let t2 = true;
|
|
146099
146917
|
try {
|
|
146100
146918
|
let { fd: e, buf: i, offset: r, length: n, pos: o } = this;
|
|
146101
146919
|
if (e === void 0 || i === void 0) throw new Error("fd and buf must be set in READ method");
|
|
146102
|
-
let h =
|
|
146920
|
+
let h = import_fs27.default.readSync(e, i, r, n, o);
|
|
146103
146921
|
this[rs](h), t2 = false;
|
|
146104
146922
|
} finally {
|
|
146105
146923
|
if (t2) try {
|
|
@@ -146114,7 +146932,7 @@ var ni = class extends de {
|
|
|
146114
146932
|
}
|
|
146115
146933
|
[pt](t2 = () => {
|
|
146116
146934
|
}) {
|
|
146117
|
-
this.fd !== void 0 &&
|
|
146935
|
+
this.fd !== void 0 && import_fs27.default.closeSync(this.fd), t2();
|
|
146118
146936
|
}
|
|
146119
146937
|
};
|
|
146120
146938
|
var oi = class extends A {
|
|
@@ -146430,7 +147248,7 @@ var wt = class extends A {
|
|
|
146430
147248
|
return typeof t2 == "string" ? this[ci](t2) : this[or](t2), this.flowing;
|
|
146431
147249
|
}
|
|
146432
147250
|
[or](t2) {
|
|
146433
|
-
let e = f(
|
|
147251
|
+
let e = f(import_path24.default.resolve(this.cwd, t2.path));
|
|
146434
147252
|
if (!this.filter(t2.path, t2)) t2.resume();
|
|
146435
147253
|
else {
|
|
146436
147254
|
let i = new pi(t2.path, e);
|
|
@@ -146439,13 +147257,13 @@ var wt = class extends A {
|
|
|
146439
147257
|
this[Ft]();
|
|
146440
147258
|
}
|
|
146441
147259
|
[ci](t2) {
|
|
146442
|
-
let e = f(
|
|
147260
|
+
let e = f(import_path24.default.resolve(this.cwd, t2));
|
|
146443
147261
|
this[W].push(new pi(t2, e)), this[Ft]();
|
|
146444
147262
|
}
|
|
146445
147263
|
[ds](t2) {
|
|
146446
147264
|
t2.pending = true, this[G2] += 1;
|
|
146447
147265
|
let e = this.follow ? "stat" : "lstat";
|
|
146448
|
-
|
|
147266
|
+
import_fs26.default[e](t2.absolute, (i, r) => {
|
|
146449
147267
|
t2.pending = false, this[G2] -= 1, i ? this.emit("error", i) : this[li](t2, r);
|
|
146450
147268
|
});
|
|
146451
147269
|
}
|
|
@@ -146459,7 +147277,7 @@ var wt = class extends A {
|
|
|
146459
147277
|
this[Ft]();
|
|
146460
147278
|
}
|
|
146461
147279
|
[ms](t2) {
|
|
146462
|
-
t2.pending = true, this[G2] += 1,
|
|
147280
|
+
t2.pending = true, this[G2] += 1, import_fs26.default.readdir(t2.absolute, (e, i) => {
|
|
146463
147281
|
if (t2.pending = false, this[G2] -= 1, e) return this.emit("error", e);
|
|
146464
147282
|
this[fi](t2, i);
|
|
146465
147283
|
});
|
|
@@ -146560,10 +147378,10 @@ var kt = class extends wt {
|
|
|
146560
147378
|
}
|
|
146561
147379
|
[ds](t2) {
|
|
146562
147380
|
let e = this.follow ? "statSync" : "lstatSync";
|
|
146563
|
-
this[li](t2,
|
|
147381
|
+
this[li](t2, import_fs26.default[e](t2.absolute));
|
|
146564
147382
|
}
|
|
146565
147383
|
[ms](t2) {
|
|
146566
|
-
this[fi](t2,
|
|
147384
|
+
this[fi](t2, import_fs26.default.readdirSync(t2.absolute));
|
|
146567
147385
|
}
|
|
146568
147386
|
[di](t2) {
|
|
146569
147387
|
let e = t2.entry, i = this.zip;
|
|
@@ -146614,8 +147432,8 @@ var Qn = K2(Vn, $n, Xn, qn, (s3, t2) => {
|
|
|
146614
147432
|
});
|
|
146615
147433
|
var Jn = process.env.__FAKE_PLATFORM__ || process.platform;
|
|
146616
147434
|
var Er = Jn === "win32";
|
|
146617
|
-
var { O_CREAT: wr, O_NOFOLLOW: mr, O_TRUNC: Sr, O_WRONLY: yr } =
|
|
146618
|
-
var Rr = Number(process.env.__FAKE_FS_O_FILENAME__) ||
|
|
147435
|
+
var { O_CREAT: wr, O_NOFOLLOW: mr, O_TRUNC: Sr, O_WRONLY: yr } = import_fs28.default.constants;
|
|
147436
|
+
var Rr = Number(process.env.__FAKE_FS_O_FILENAME__) || import_fs28.default.constants.UV_FS_O_FILEMAP || 0;
|
|
146619
147437
|
var jn = Er && !!Rr;
|
|
146620
147438
|
var to = 512 * 1024;
|
|
146621
147439
|
var eo = Rr | Sr | wr | yr;
|
|
@@ -147739,6 +148557,13 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
147739
148557
|
"Run in multi-agent mode (PTY mode unreachable in this process)",
|
|
147740
148558
|
false
|
|
147741
148559
|
).action(async (opts) => {
|
|
148560
|
+
try {
|
|
148561
|
+
const { checkSqliteAbi: checkSqliteAbi2 } = await Promise.resolve().then(() => (init_check_sqlite_abi(), check_sqlite_abi_exports));
|
|
148562
|
+
checkSqliteAbi2();
|
|
148563
|
+
} catch (err) {
|
|
148564
|
+
log7.error(err instanceof Error ? err.message : String(err), void 0, "console");
|
|
148565
|
+
process.exit(1);
|
|
148566
|
+
}
|
|
147742
148567
|
if (opts.multiAgentFlow) {
|
|
147743
148568
|
process.env.MULTI_AGENT_FLOW = "true";
|
|
147744
148569
|
}
|
|
@@ -147836,12 +148661,12 @@ program2.command("cache").description("Manage the local SQLite conversation cach
|
|
|
147836
148661
|
"Cache directory (default: ~/.threadbase/cache)",
|
|
147837
148662
|
`${process.env.HOME}/.threadbase/cache`
|
|
147838
148663
|
).action((opts) => {
|
|
147839
|
-
const { rmSync: rmSync6, existsSync:
|
|
148664
|
+
const { rmSync: rmSync6, existsSync: existsSync12 } = require("fs");
|
|
147840
148665
|
const { join: join26 } = require("path");
|
|
147841
148666
|
const dbPath = join26(opts.cacheDir, "cache.db");
|
|
147842
148667
|
for (const suffix of ["", "-shm", "-wal"]) {
|
|
147843
148668
|
const f2 = dbPath + suffix;
|
|
147844
|
-
if (
|
|
148669
|
+
if (existsSync12(f2)) {
|
|
147845
148670
|
rmSync6(f2);
|
|
147846
148671
|
log7.info(`Deleted ${f2}`, { path: f2 }, "console");
|
|
147847
148672
|
}
|