@threadbase-sh/streamer 1.23.0 → 1.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1194 -354
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +1176 -378
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +107 -29
- package/dist/index.d.ts +107 -29
- package/dist/index.js +1046 -253
- package/dist/index.js.map +1 -1
- package/package.json +2 -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
|
|
|
@@ -137289,7 +137335,10 @@ var ConversationCache = class _ConversationCache {
|
|
|
137289
137335
|
model = excluded.model,
|
|
137290
137336
|
account = excluded.account,
|
|
137291
137337
|
branch = excluded.branch,
|
|
137292
|
-
message_count
|
|
137338
|
+
-- message_count is incremented by live tailing but recounted from
|
|
137339
|
+
-- scratch by a scanner rescan; a stale rescan must not carry a live
|
|
137340
|
+
-- session's count backwards, so take the max instead of overwriting.
|
|
137341
|
+
message_count = MAX(conversation_meta.message_count, excluded.message_count),
|
|
137293
137342
|
last_activity = excluded.last_activity,
|
|
137294
137343
|
first_message = excluded.first_message,
|
|
137295
137344
|
last_message = excluded.last_message,
|
|
@@ -137566,6 +137615,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
137566
137615
|
fileSize = s3.size;
|
|
137567
137616
|
} catch {
|
|
137568
137617
|
}
|
|
137618
|
+
const seq = ++this.tailSeq;
|
|
137569
137619
|
this.stmts.upsertFull.run({
|
|
137570
137620
|
id,
|
|
137571
137621
|
file_path: m2.filePath,
|
|
@@ -137580,7 +137630,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
137580
137630
|
first_message: m2.firstMessage ? JSON.stringify(m2.firstMessage) : null,
|
|
137581
137631
|
last_message: m2.lastMessage ? JSON.stringify(m2.lastMessage) : null,
|
|
137582
137632
|
preview: m2.preview ?? null,
|
|
137583
|
-
updated_at:
|
|
137633
|
+
updated_at: seq,
|
|
137584
137634
|
mtime_ms: mtimeMs,
|
|
137585
137635
|
file_size: fileSize,
|
|
137586
137636
|
provider: m2.provider ?? CLAUDE_CODE_PROVIDER2
|
|
@@ -137797,9 +137847,25 @@ var ConversationCache = class _ConversationCache {
|
|
|
137797
137847
|
this.fileIndex.clear();
|
|
137798
137848
|
}
|
|
137799
137849
|
}
|
|
137800
|
-
|
|
137850
|
+
/**
|
|
137851
|
+
* Drop the cached row for a file. Two callers with opposite intent:
|
|
137852
|
+
* - a directory-watch "change" event (the file was appended to) — pass
|
|
137853
|
+
* `skipIfTailed: true`. A cached tail means the row is being actively
|
|
137854
|
+
* maintained from the file's real content by the live-tail
|
|
137855
|
+
* (updateFromLines) or warm-up path, fresher than any scanner-derived
|
|
137856
|
+
* view. Both watchers fire on the same append with no ordering guarantee;
|
|
137857
|
+
* without this guard the invalidate can land after the tail write and wipe
|
|
137858
|
+
* the just-cached row, flickering the conversation out of
|
|
137859
|
+
* /api/conversations on nearly every message (CRITICAL #2). The debounced
|
|
137860
|
+
* rescan still re-derives metadata, so skipping the eager drop loses
|
|
137861
|
+
* nothing.
|
|
137862
|
+
* - a genuine unlink (the file is gone) — leave `skipIfTailed` false so the
|
|
137863
|
+
* row is always removed, otherwise a deleted session ghosts in the cache.
|
|
137864
|
+
*/
|
|
137865
|
+
invalidateByFilePath(filePath, opts) {
|
|
137801
137866
|
const row = this.stmts.getIdByFilePath.get(filePath);
|
|
137802
137867
|
if (!row) return null;
|
|
137868
|
+
if (opts?.skipIfTailed && this.stmts.hasTail.get(row.id)) return null;
|
|
137803
137869
|
this.invalidate(row.id);
|
|
137804
137870
|
return row.id;
|
|
137805
137871
|
}
|
|
@@ -138162,67 +138228,15 @@ function handleListProjects(url2, res) {
|
|
|
138162
138228
|
res.end(JSON.stringify({ projects: page, total }));
|
|
138163
138229
|
}
|
|
138164
138230
|
|
|
138165
|
-
// src/
|
|
138166
|
-
|
|
138231
|
+
// src/live-session-manager.ts
|
|
138232
|
+
var import_path18 = require("path");
|
|
138167
138233
|
|
|
138168
|
-
// src/
|
|
138234
|
+
// src/codex-pty-runner.ts
|
|
138235
|
+
var import_headless = __toESM(require_xterm_headless(), 1);
|
|
138169
138236
|
var import_crypto5 = require("crypto");
|
|
138170
|
-
var
|
|
138171
|
-
var SWEEP_INTERVAL_MS = 6e4;
|
|
138172
|
-
var PairTokenStore = class {
|
|
138173
|
-
current = null;
|
|
138174
|
-
ttlMs;
|
|
138175
|
-
sweepTimer = null;
|
|
138176
|
-
constructor(opts = {}) {
|
|
138177
|
-
this.ttlMs = (opts.ttlSeconds ?? DEFAULT_TTL_SECONDS) * 1e3;
|
|
138178
|
-
if (opts.autoSweep !== false) {
|
|
138179
|
-
this.sweepTimer = setInterval(() => this.sweep(), SWEEP_INTERVAL_MS);
|
|
138180
|
-
this.sweepTimer.unref?.();
|
|
138181
|
-
}
|
|
138182
|
-
}
|
|
138183
|
-
mint() {
|
|
138184
|
-
const token = `pt_${(0, import_crypto5.randomBytes)(16).toString("hex")}`;
|
|
138185
|
-
const expiresAt = Date.now() + this.ttlMs;
|
|
138186
|
-
this.current = { token, expiresAt, used: false };
|
|
138187
|
-
return {
|
|
138188
|
-
token,
|
|
138189
|
-
expiresAt,
|
|
138190
|
-
expiresInSeconds: Math.floor(this.ttlMs / 1e3)
|
|
138191
|
-
};
|
|
138192
|
-
}
|
|
138193
|
-
consume(token) {
|
|
138194
|
-
const record2 = this.current;
|
|
138195
|
-
if (!record2 || record2.token !== token) return { ok: false, reason: "unknown" };
|
|
138196
|
-
if (Date.now() > record2.expiresAt) {
|
|
138197
|
-
this.current = null;
|
|
138198
|
-
return { ok: false, reason: "expired" };
|
|
138199
|
-
}
|
|
138200
|
-
if (record2.used) return { ok: false, reason: "used" };
|
|
138201
|
-
record2.used = true;
|
|
138202
|
-
return { ok: true };
|
|
138203
|
-
}
|
|
138204
|
-
peek() {
|
|
138205
|
-
return this.current;
|
|
138206
|
-
}
|
|
138207
|
-
clear() {
|
|
138208
|
-
this.current = null;
|
|
138209
|
-
}
|
|
138210
|
-
sweep() {
|
|
138211
|
-
if (this.current && Date.now() > this.current.expiresAt) {
|
|
138212
|
-
this.current = null;
|
|
138213
|
-
}
|
|
138214
|
-
}
|
|
138215
|
-
dispose() {
|
|
138216
|
-
if (this.sweepTimer) clearInterval(this.sweepTimer);
|
|
138217
|
-
this.sweepTimer = null;
|
|
138218
|
-
this.current = null;
|
|
138219
|
-
}
|
|
138220
|
-
};
|
|
138221
|
-
|
|
138222
|
-
// src/process-discovery.ts
|
|
138223
|
-
var import_child_process2 = require("child_process");
|
|
138224
|
-
var import_os9 = require("os");
|
|
138237
|
+
var import_fs20 = require("fs");
|
|
138225
138238
|
var import_path16 = require("path");
|
|
138239
|
+
init_logger();
|
|
138226
138240
|
|
|
138227
138241
|
// src/platform.ts
|
|
138228
138242
|
var import_child_process = require("child_process");
|
|
@@ -138288,151 +138302,523 @@ function resolveClaudeExe() {
|
|
|
138288
138302
|
_claudeExe = "claude";
|
|
138289
138303
|
return _claudeExe;
|
|
138290
138304
|
}
|
|
138291
|
-
|
|
138292
|
-
|
|
138293
|
-
|
|
138294
|
-
if (
|
|
138295
|
-
|
|
138296
|
-
|
|
138297
|
-
|
|
138298
|
-
|
|
138299
|
-
|
|
138300
|
-
|
|
138301
|
-
|
|
138302
|
-
|
|
138303
|
-
|
|
138304
|
-
getProcessArgsUnix(pid),
|
|
138305
|
-
getProcessStartTimeUnix(pid)
|
|
138306
|
-
]);
|
|
138307
|
-
const conversationId = extractResumeId(args);
|
|
138308
|
-
return {
|
|
138309
|
-
pid,
|
|
138310
|
-
projectPath: cwd,
|
|
138311
|
-
projectName: (0, import_path16.basename)(cwd),
|
|
138312
|
-
branch: await readGitBranch2(cwd),
|
|
138313
|
-
conversationId,
|
|
138314
|
-
startedAt
|
|
138315
|
-
};
|
|
138316
|
-
} catch {
|
|
138317
|
-
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;
|
|
138318
138318
|
}
|
|
138319
|
-
}
|
|
138320
|
-
|
|
138321
|
-
|
|
138322
|
-
|
|
138323
|
-
|
|
138324
|
-
|
|
138325
|
-
|
|
138326
|
-
|
|
138327
|
-
|
|
138328
|
-
|
|
138329
|
-
|
|
138330
|
-
|
|
138331
|
-
|
|
138332
|
-
|
|
138333
|
-
|
|
138334
|
-
branch: await readGitBranch2(info.cwd),
|
|
138335
|
-
conversationId: extractResumeId(info.args),
|
|
138336
|
-
startedAt: info.startedAt
|
|
138337
|
-
};
|
|
138338
|
-
} catch {
|
|
138339
|
-
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;
|
|
138340
138334
|
}
|
|
138341
|
-
}
|
|
138342
|
-
|
|
138343
|
-
|
|
138344
|
-
|
|
138345
|
-
|
|
138346
|
-
|
|
138347
|
-
|
|
138348
|
-
|
|
138349
|
-
|
|
138350
|
-
|
|
138351
|
-
(err, stdout2) => {
|
|
138352
|
-
if (err) reject(err);
|
|
138353
|
-
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;
|
|
138354
138345
|
}
|
|
138355
|
-
|
|
138356
|
-
|
|
138357
|
-
|
|
138358
|
-
|
|
138359
|
-
|
|
138360
|
-
|
|
138361
|
-
|
|
138362
|
-
|
|
138363
|
-
|
|
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
|
+
}
|
|
138364
138359
|
}
|
|
138360
|
+
_codexExe = "codex";
|
|
138361
|
+
return _codexExe;
|
|
138365
138362
|
}
|
|
138366
|
-
|
|
138367
|
-
|
|
138368
|
-
|
|
138369
|
-
|
|
138370
|
-
|
|
138371
|
-
|
|
138372
|
-
|
|
138373
|
-
|
|
138374
|
-
|
|
138375
|
-
|
|
138376
|
-
|
|
138377
|
-
|
|
138378
|
-
|
|
138379
|
-
|
|
138380
|
-
try {
|
|
138381
|
-
const output = await run("tasklist", ["/FI", "IMAGENAME eq claude.exe", "/FO", "CSV", "/NH"]);
|
|
138382
|
-
return output.trim().split("\n").filter(Boolean).map((line) => {
|
|
138383
|
-
const parts = line.split(",");
|
|
138384
|
-
return Number.parseInt(parts[1]?.replace(/"/g, "") ?? "0", 10);
|
|
138385
|
-
}).filter((pid) => pid > 0);
|
|
138386
|
-
} catch {
|
|
138387
|
-
return [];
|
|
138388
|
-
}
|
|
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)}`;
|
|
138389
138377
|
}
|
|
138390
|
-
|
|
138378
|
+
var pty = null;
|
|
138379
|
+
async function loadPty() {
|
|
138380
|
+
if (pty) return pty;
|
|
138391
138381
|
try {
|
|
138392
|
-
|
|
138393
|
-
|
|
138394
|
-
|
|
138395
|
-
|
|
138396
|
-
|
|
138397
|
-
|
|
138398
|
-
|
|
138399
|
-
]);
|
|
138400
|
-
const lines = output.trim().split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
138401
|
-
if (lines.length < 2) return null;
|
|
138402
|
-
const parts = lines[1].split(",");
|
|
138403
|
-
const args = parts[1] ?? "";
|
|
138404
|
-
const creationDate = parts[2] ?? "";
|
|
138405
|
-
const year = creationDate.slice(0, 4);
|
|
138406
|
-
const month = creationDate.slice(4, 6);
|
|
138407
|
-
const day = creationDate.slice(6, 8);
|
|
138408
|
-
const hour = creationDate.slice(8, 10);
|
|
138409
|
-
const min = creationDate.slice(10, 12);
|
|
138410
|
-
const sec = creationDate.slice(12, 14);
|
|
138411
|
-
const startedAt = /* @__PURE__ */ new Date(`${year}-${month}-${day}T${hour}:${min}:${sec}`);
|
|
138412
|
-
if (Number.isNaN(startedAt.getTime())) return null;
|
|
138413
|
-
const exePath = parts[3] ?? "";
|
|
138414
|
-
const cwd = exePath ? (0, import_path16.dirname)(exePath) : "";
|
|
138415
|
-
return { cwd, args, startedAt };
|
|
138416
|
-
} catch {
|
|
138417
|
-
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
|
+
);
|
|
138418
138389
|
}
|
|
138419
138390
|
}
|
|
138420
|
-
function
|
|
138421
|
-
|
|
138422
|
-
|
|
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
|
+
});
|
|
138423
138398
|
}
|
|
138424
|
-
|
|
138425
|
-
|
|
138426
|
-
|
|
138427
|
-
|
|
138428
|
-
|
|
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;
|
|
138429
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, "");
|
|
138430
138816
|
}
|
|
138431
138817
|
|
|
138432
138818
|
// src/pty-manager.ts
|
|
138433
|
-
var
|
|
138819
|
+
var import_headless2 = __toESM(require_xterm_headless(), 1);
|
|
138434
138820
|
var import_crypto6 = require("crypto");
|
|
138435
|
-
var
|
|
138821
|
+
var import_fs21 = require("fs");
|
|
138436
138822
|
var import_path17 = require("path");
|
|
138437
138823
|
init_logger();
|
|
138438
138824
|
|
|
@@ -138593,28 +138979,28 @@ function detectShellPrompt(lines) {
|
|
|
138593
138979
|
}
|
|
138594
138980
|
|
|
138595
138981
|
// src/pty-manager.ts
|
|
138596
|
-
var
|
|
138597
|
-
var
|
|
138598
|
-
var
|
|
138599
|
-
var
|
|
138982
|
+
var OUTPUT_BUFFER_MAX2 = 65536;
|
|
138983
|
+
var PTY_COLS2 = 120;
|
|
138984
|
+
var PTY_ROWS2 = 40;
|
|
138985
|
+
var SCREEN_SCROLLBACK2 = 1e3;
|
|
138600
138986
|
var CLAUDE_PROMPT_MARKERS = ["\u256D", "\u276F"];
|
|
138601
138987
|
var PROMPT_MARKER_FALLBACK_MS = 1e4;
|
|
138602
138988
|
function buildPasteBytes(input) {
|
|
138603
138989
|
return `\x1B[200~${input}\x1B[201~`;
|
|
138604
138990
|
}
|
|
138605
|
-
var
|
|
138991
|
+
var SUBMIT_BYTES2 = "\r";
|
|
138606
138992
|
var SUBMIT_DELAY_MS = 16;
|
|
138607
|
-
function
|
|
138993
|
+
function digestBytes2(s3) {
|
|
138608
138994
|
const escaped = s3.replace(new RegExp(String.fromCharCode(27), "g"), "\\x1b").replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\t/g, "\\t");
|
|
138609
138995
|
if (escaped.length <= 200) return escaped;
|
|
138610
138996
|
return `${escaped.slice(0, 100)}\u2026[${escaped.length - 200}B omitted]\u2026${escaped.slice(-100)}`;
|
|
138611
138997
|
}
|
|
138612
|
-
var
|
|
138613
|
-
async function
|
|
138614
|
-
if (
|
|
138998
|
+
var pty2 = null;
|
|
138999
|
+
async function loadPty2() {
|
|
139000
|
+
if (pty2) return pty2;
|
|
138615
139001
|
try {
|
|
138616
|
-
|
|
138617
|
-
return
|
|
139002
|
+
pty2 = await import("node-pty");
|
|
139003
|
+
return pty2;
|
|
138618
139004
|
} catch (err) {
|
|
138619
139005
|
throw new Error(
|
|
138620
139006
|
`node-pty is required for PTY management but failed to load. Ensure it is installed: npm install node-pty
|
|
@@ -138622,11 +139008,11 @@ Original error: ${err}`
|
|
|
138622
139008
|
);
|
|
138623
139009
|
}
|
|
138624
139010
|
}
|
|
138625
|
-
function
|
|
138626
|
-
return new
|
|
138627
|
-
cols:
|
|
138628
|
-
rows:
|
|
138629
|
-
scrollback:
|
|
139011
|
+
function createScreen2() {
|
|
139012
|
+
return new import_headless2.Terminal({
|
|
139013
|
+
cols: PTY_COLS2,
|
|
139014
|
+
rows: PTY_ROWS2,
|
|
139015
|
+
scrollback: SCREEN_SCROLLBACK2,
|
|
138630
139016
|
allowProposedApi: true
|
|
138631
139017
|
});
|
|
138632
139018
|
}
|
|
@@ -138675,6 +139061,10 @@ var PTYManager = class {
|
|
|
138675
139061
|
// to a given input or fell silent. Reset on dispose().
|
|
138676
139062
|
chunkIndex = /* @__PURE__ */ new Map();
|
|
138677
139063
|
lastChunkAt = /* @__PURE__ */ new Map();
|
|
139064
|
+
// In-flight start()/startFresh() calls keyed by sessionId. A second
|
|
139065
|
+
// concurrent resume for the same session (double-tap, client retry) awaits
|
|
139066
|
+
// the first call's promise instead of spawning a duplicate PTY (CRITICAL #3).
|
|
139067
|
+
startPromises = /* @__PURE__ */ new Map();
|
|
138678
139068
|
constructor(options = {}) {
|
|
138679
139069
|
this.onOutput = options.onOutput;
|
|
138680
139070
|
this.onStatusChange = options.onStatusChange;
|
|
@@ -138697,7 +139087,18 @@ var PTYManager = class {
|
|
|
138697
139087
|
// custom-API-key — are cleared by the seeded ~/.claude.json in
|
|
138698
139088
|
// docker/entrypoint.sh.) startFresh() uses the same flag for the same reason.
|
|
138699
139089
|
async start(sessionId, options) {
|
|
138700
|
-
const
|
|
139090
|
+
const existing = this.sessions.get(sessionId);
|
|
139091
|
+
if (existing) return toPublicSession2(existing);
|
|
139092
|
+
const inFlight = this.startPromises.get(sessionId);
|
|
139093
|
+
if (inFlight) return inFlight;
|
|
139094
|
+
const promise2 = this.doStart(sessionId, options).finally(() => {
|
|
139095
|
+
this.startPromises.delete(sessionId);
|
|
139096
|
+
});
|
|
139097
|
+
this.startPromises.set(sessionId, promise2);
|
|
139098
|
+
return promise2;
|
|
139099
|
+
}
|
|
139100
|
+
async doStart(sessionId, options) {
|
|
139101
|
+
const nodePty = await loadPty2();
|
|
138701
139102
|
const projectName = options.projectName ?? (0, import_path17.basename)(options.projectPath);
|
|
138702
139103
|
const proc = nodePty.spawn(
|
|
138703
139104
|
resolveClaudeExe(),
|
|
@@ -138719,6 +139120,7 @@ var PTYManager = class {
|
|
|
138719
139120
|
);
|
|
138720
139121
|
const session = {
|
|
138721
139122
|
id: sessionId,
|
|
139123
|
+
provider: CLAUDE_CODE_PROVIDER2,
|
|
138722
139124
|
projectPath: options.projectPath,
|
|
138723
139125
|
projectName,
|
|
138724
139126
|
branch: options.branch ?? "",
|
|
@@ -138729,7 +139131,7 @@ var PTYManager = class {
|
|
|
138729
139131
|
lastOutput: "",
|
|
138730
139132
|
process: proc,
|
|
138731
139133
|
outputBuffer: Buffer.alloc(0),
|
|
138732
|
-
screen:
|
|
139134
|
+
screen: createScreen2()
|
|
138733
139135
|
};
|
|
138734
139136
|
this.sessions.set(sessionId, session);
|
|
138735
139137
|
this.pendingReady.add(sessionId);
|
|
@@ -138740,13 +139142,13 @@ var PTYManager = class {
|
|
|
138740
139142
|
this.pendingReady.delete(sessionId);
|
|
138741
139143
|
this.handleExit(sessionId, exitCode);
|
|
138742
139144
|
});
|
|
138743
|
-
return
|
|
139145
|
+
return toPublicSession2(session);
|
|
138744
139146
|
}
|
|
138745
139147
|
// Start a brand-new Claude session. A stable UUID is generated here and passed
|
|
138746
139148
|
// to Claude via --session-id so the JSONL filename matches from the start.
|
|
138747
139149
|
// onReady fires once Claude reaches its first prompt (waiting_input).
|
|
138748
139150
|
async startFresh(options) {
|
|
138749
|
-
const nodePty = await
|
|
139151
|
+
const nodePty = await loadPty2();
|
|
138750
139152
|
const sessionId = (0, import_crypto6.randomUUID)();
|
|
138751
139153
|
const projectName = options.projectName ?? (0, import_path17.basename)(options.projectPath);
|
|
138752
139154
|
const args = [
|
|
@@ -138769,6 +139171,7 @@ var PTYManager = class {
|
|
|
138769
139171
|
});
|
|
138770
139172
|
const session = {
|
|
138771
139173
|
id: sessionId,
|
|
139174
|
+
provider: CLAUDE_CODE_PROVIDER2,
|
|
138772
139175
|
projectPath: options.projectPath,
|
|
138773
139176
|
projectName,
|
|
138774
139177
|
branch: "",
|
|
@@ -138779,7 +139182,7 @@ var PTYManager = class {
|
|
|
138779
139182
|
lastOutput: "",
|
|
138780
139183
|
process: proc,
|
|
138781
139184
|
outputBuffer: Buffer.alloc(0),
|
|
138782
|
-
screen:
|
|
139185
|
+
screen: createScreen2()
|
|
138783
139186
|
};
|
|
138784
139187
|
this.sessions.set(sessionId, session);
|
|
138785
139188
|
this.pendingReady.add(sessionId);
|
|
@@ -138790,7 +139193,7 @@ var PTYManager = class {
|
|
|
138790
139193
|
this.pendingReady.delete(sessionId);
|
|
138791
139194
|
this.handleExit(sessionId, exitCode);
|
|
138792
139195
|
});
|
|
138793
|
-
return
|
|
139196
|
+
return toPublicSession2(session);
|
|
138794
139197
|
}
|
|
138795
139198
|
// Write raw key bytes directly to the PTY without bracketed-paste wrapping.
|
|
138796
139199
|
// Use for control sequences (arrow keys, Enter) that must not be quoted.
|
|
@@ -138802,10 +139205,10 @@ var PTYManager = class {
|
|
|
138802
139205
|
}
|
|
138803
139206
|
if (session.status === "waiting_input") {
|
|
138804
139207
|
session.status = "running";
|
|
138805
|
-
this.onStatusChange?.(
|
|
139208
|
+
this.onStatusChange?.(toPublicSession2(session));
|
|
138806
139209
|
}
|
|
138807
139210
|
this.log.info(
|
|
138808
|
-
`[pty.keys.write] ${sessionId.slice(0, 8)} bytes=${keys.length} digest=${
|
|
139211
|
+
`[pty.keys.write] ${sessionId.slice(0, 8)} bytes=${keys.length} digest=${digestBytes2(keys)}`,
|
|
138809
139212
|
{ event: "pty.keys_write", sessionId, byteLen: keys.length }
|
|
138810
139213
|
);
|
|
138811
139214
|
session.process.write(keys);
|
|
@@ -138837,7 +139240,7 @@ var PTYManager = class {
|
|
|
138837
139240
|
}
|
|
138838
139241
|
if (session.status === "waiting_input") {
|
|
138839
139242
|
session.status = "running";
|
|
138840
|
-
this.onStatusChange?.(
|
|
139243
|
+
this.onStatusChange?.(toPublicSession2(session));
|
|
138841
139244
|
}
|
|
138842
139245
|
this.writeSubmit(sessionId, session, input, "direct", session.promptCount + 1);
|
|
138843
139246
|
session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
@@ -138850,13 +139253,13 @@ var PTYManager = class {
|
|
|
138850
139253
|
writeSubmit(sessionId, session, input, path2, promptCount) {
|
|
138851
139254
|
const pasteBytes = buildPasteBytes(input);
|
|
138852
139255
|
this.log.info(
|
|
138853
|
-
`[pty.input.write] ${sessionId.slice(0, 8)} promptCount=${promptCount} bytes=${pasteBytes.length} digest=${
|
|
139256
|
+
`[pty.input.write] ${sessionId.slice(0, 8)} promptCount=${promptCount} bytes=${pasteBytes.length} digest=${digestBytes2(pasteBytes)}`,
|
|
138854
139257
|
{
|
|
138855
139258
|
event: "pty.input_write",
|
|
138856
139259
|
sessionId,
|
|
138857
139260
|
promptCount,
|
|
138858
139261
|
byteLen: pasteBytes.length,
|
|
138859
|
-
digest:
|
|
139262
|
+
digest: digestBytes2(pasteBytes),
|
|
138860
139263
|
path: path2,
|
|
138861
139264
|
phase: "paste"
|
|
138862
139265
|
}
|
|
@@ -138871,13 +139274,13 @@ var PTYManager = class {
|
|
|
138871
139274
|
event: "pty.input_write",
|
|
138872
139275
|
sessionId,
|
|
138873
139276
|
promptCount,
|
|
138874
|
-
byteLen:
|
|
139277
|
+
byteLen: SUBMIT_BYTES2.length,
|
|
138875
139278
|
digest: "\\r",
|
|
138876
139279
|
path: path2,
|
|
138877
139280
|
phase: "submit"
|
|
138878
139281
|
}
|
|
138879
139282
|
);
|
|
138880
|
-
current.process.write(
|
|
139283
|
+
current.process.write(SUBMIT_BYTES2);
|
|
138881
139284
|
}, SUBMIT_DELAY_MS);
|
|
138882
139285
|
}
|
|
138883
139286
|
// Drain any inputs that were sent while the session was still pendingReady,
|
|
@@ -138935,7 +139338,7 @@ var PTYManager = class {
|
|
|
138935
139338
|
session.completedAt = /* @__PURE__ */ new Date();
|
|
138936
139339
|
session.screen.dispose();
|
|
138937
139340
|
this.sessions.delete(sessionId);
|
|
138938
|
-
this.onStatusChange?.(
|
|
139341
|
+
this.onStatusChange?.(toPublicSession2(session));
|
|
138939
139342
|
}
|
|
138940
139343
|
getOutput(sessionId) {
|
|
138941
139344
|
const session = this.sessions.get(sessionId);
|
|
@@ -138966,13 +139369,13 @@ var PTYManager = class {
|
|
|
138966
139369
|
}
|
|
138967
139370
|
getSession(sessionId) {
|
|
138968
139371
|
const session = this.sessions.get(sessionId);
|
|
138969
|
-
return session ?
|
|
139372
|
+
return session ? toPublicSession2(session) : null;
|
|
138970
139373
|
}
|
|
138971
139374
|
hasSession(sessionId) {
|
|
138972
139375
|
return this.sessions.has(sessionId);
|
|
138973
139376
|
}
|
|
138974
139377
|
listSessions() {
|
|
138975
|
-
return Array.from(this.sessions.values()).map(
|
|
139378
|
+
return Array.from(this.sessions.values()).map(toPublicSession2);
|
|
138976
139379
|
}
|
|
138977
139380
|
dispose() {
|
|
138978
139381
|
for (const session of this.sessions.values()) {
|
|
@@ -139004,7 +139407,7 @@ var PTYManager = class {
|
|
|
139004
139407
|
this.lastChunkAt.set(sessionId, now);
|
|
139005
139408
|
const gapMs = last == null ? 0 : now - last;
|
|
139006
139409
|
this.log.info(
|
|
139007
|
-
`[pty.chunk] ${sessionId.slice(0, 8)} #${idx} +${chunk.length}B gap=${gapMs}ms status=${session.status} digest=${
|
|
139410
|
+
`[pty.chunk] ${sessionId.slice(0, 8)} #${idx} +${chunk.length}B gap=${gapMs}ms status=${session.status} digest=${digestBytes2(data)}`,
|
|
139008
139411
|
{
|
|
139009
139412
|
event: "pty.chunk",
|
|
139010
139413
|
sessionId,
|
|
@@ -139013,17 +139416,17 @@ var PTYManager = class {
|
|
|
139013
139416
|
gapMs,
|
|
139014
139417
|
status: session.status,
|
|
139015
139418
|
pendingReady: this.pendingReady.has(sessionId),
|
|
139016
|
-
digest:
|
|
139419
|
+
digest: digestBytes2(data)
|
|
139017
139420
|
}
|
|
139018
139421
|
);
|
|
139019
139422
|
session.outputBuffer = Buffer.concat([session.outputBuffer, chunk]);
|
|
139020
|
-
if (session.outputBuffer.length >
|
|
139423
|
+
if (session.outputBuffer.length > OUTPUT_BUFFER_MAX2) {
|
|
139021
139424
|
session.outputBuffer = session.outputBuffer.subarray(
|
|
139022
|
-
session.outputBuffer.length -
|
|
139425
|
+
session.outputBuffer.length - OUTPUT_BUFFER_MAX2
|
|
139023
139426
|
);
|
|
139024
139427
|
}
|
|
139025
139428
|
session.screen.write(data);
|
|
139026
|
-
const stripped =
|
|
139429
|
+
const stripped = stripAnsi2(data);
|
|
139027
139430
|
session.lastOutput = stripped;
|
|
139028
139431
|
const matchedMarker = CLAUDE_PROMPT_MARKERS.find((m2) => stripped.includes(m2));
|
|
139029
139432
|
if (session.status === "running" && matchedMarker) {
|
|
@@ -139126,11 +139529,11 @@ var PTYManager = class {
|
|
|
139126
139529
|
reason,
|
|
139127
139530
|
elapsedMs
|
|
139128
139531
|
});
|
|
139129
|
-
this.onStatusChange?.(
|
|
139532
|
+
this.onStatusChange?.(toPublicSession2(session));
|
|
139130
139533
|
if (this.pendingReady.has(sessionId)) {
|
|
139131
139534
|
this.pendingReady.delete(sessionId);
|
|
139132
139535
|
this.flushQueuedInputs(sessionId);
|
|
139133
|
-
this.onReady?.(
|
|
139536
|
+
this.onReady?.(toPublicSession2(session));
|
|
139134
139537
|
}
|
|
139135
139538
|
}
|
|
139136
139539
|
handleExit(sessionId, exitCode) {
|
|
@@ -139140,13 +139543,13 @@ var PTYManager = class {
|
|
|
139140
139543
|
session.status = "idle";
|
|
139141
139544
|
const elapsedMs = session.completedAt.getTime() - session.startedAt.getTime();
|
|
139142
139545
|
if (exitCode !== 0 && elapsedMs < 2e3 && session.lastOutput === "") {
|
|
139143
|
-
if (!(0,
|
|
139546
|
+
if (!(0, import_fs21.existsSync)(session.projectPath)) {
|
|
139144
139547
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
139145
139548
|
} else {
|
|
139146
139549
|
session.failureReason = `Process exited immediately (code ${exitCode}). Check that the Claude binary is installed and accessible.`;
|
|
139147
139550
|
}
|
|
139148
139551
|
}
|
|
139149
|
-
this.onStatusChange?.(
|
|
139552
|
+
this.onStatusChange?.(toPublicSession2(session));
|
|
139150
139553
|
session.screen.dispose();
|
|
139151
139554
|
this.sessions.delete(sessionId);
|
|
139152
139555
|
this.queuedInputs.delete(sessionId);
|
|
@@ -139156,9 +139559,10 @@ var PTYManager = class {
|
|
|
139156
139559
|
this.shellPromptOpen.delete(sessionId);
|
|
139157
139560
|
}
|
|
139158
139561
|
};
|
|
139159
|
-
function
|
|
139562
|
+
function toPublicSession2(s3) {
|
|
139160
139563
|
return {
|
|
139161
139564
|
id: s3.id,
|
|
139565
|
+
provider: s3.provider ?? CLAUDE_CODE_PROVIDER2,
|
|
139162
139566
|
projectPath: s3.projectPath,
|
|
139163
139567
|
projectName: s3.projectName,
|
|
139164
139568
|
branch: s3.branch,
|
|
@@ -139172,10 +139576,306 @@ function toPublicSession(s3) {
|
|
|
139172
139576
|
...s3.filePath != null && { filePath: s3.filePath }
|
|
139173
139577
|
};
|
|
139174
139578
|
}
|
|
139175
|
-
function
|
|
139579
|
+
function stripAnsi2(str) {
|
|
139176
139580
|
return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "");
|
|
139177
139581
|
}
|
|
139178
139582
|
|
|
139583
|
+
// src/live-session-manager.ts
|
|
139584
|
+
var LiveSessionManager = class {
|
|
139585
|
+
runners;
|
|
139586
|
+
constructor(options = {}) {
|
|
139587
|
+
this.runners = /* @__PURE__ */ new Map([
|
|
139588
|
+
[CLAUDE_CODE_PROVIDER2, new PTYManager(options)],
|
|
139589
|
+
[CODEX_CLI_PROVIDER2, new CodexPtyRunner(options)]
|
|
139590
|
+
]);
|
|
139591
|
+
}
|
|
139592
|
+
async start(sessionId, options) {
|
|
139593
|
+
const provider = options.provider ?? CLAUDE_CODE_PROVIDER2;
|
|
139594
|
+
const runner = this.assertSupportedProvider(provider, options.projectPath);
|
|
139595
|
+
return runner.start(sessionId, options);
|
|
139596
|
+
}
|
|
139597
|
+
async startFresh(options) {
|
|
139598
|
+
const provider = options.provider ?? CLAUDE_CODE_PROVIDER2;
|
|
139599
|
+
const runner = this.assertSupportedProvider(provider, options.projectPath);
|
|
139600
|
+
return runner.startFresh(options);
|
|
139601
|
+
}
|
|
139602
|
+
sendInput(sessionId, input) {
|
|
139603
|
+
return this.runnerFor(sessionId).sendInput(sessionId, input);
|
|
139604
|
+
}
|
|
139605
|
+
sendKeys(sessionId, keys) {
|
|
139606
|
+
this.runnerFor(sessionId).sendKeys(sessionId, keys);
|
|
139607
|
+
}
|
|
139608
|
+
cancel(sessionId) {
|
|
139609
|
+
this.runnerFor(sessionId).cancel(sessionId);
|
|
139610
|
+
}
|
|
139611
|
+
killPid(pid) {
|
|
139612
|
+
for (const runner of this.runners.values()) {
|
|
139613
|
+
runner.killPid(pid);
|
|
139614
|
+
}
|
|
139615
|
+
}
|
|
139616
|
+
// putOnHold tolerates an unknown sessionId (PTYManager.putOnHold is a no-op
|
|
139617
|
+
// when the session isn't in its map), so — unlike the other session-keyed
|
|
139618
|
+
// methods — route to the owning runner when found, otherwise broadcast to
|
|
139619
|
+
// every runner rather than throwing; this matches the pre-extraction
|
|
139620
|
+
// behavior of delegating straight through with no existence check.
|
|
139621
|
+
putOnHold(sessionId) {
|
|
139622
|
+
for (const runner of this.runners.values()) {
|
|
139623
|
+
if (runner.hasSession(sessionId) || runner.getSession(sessionId)) {
|
|
139624
|
+
runner.putOnHold(sessionId);
|
|
139625
|
+
return;
|
|
139626
|
+
}
|
|
139627
|
+
}
|
|
139628
|
+
for (const runner of this.runners.values()) {
|
|
139629
|
+
runner.putOnHold(sessionId);
|
|
139630
|
+
}
|
|
139631
|
+
}
|
|
139632
|
+
getOutput(sessionId) {
|
|
139633
|
+
return this.runnerFor(sessionId).getOutput(sessionId);
|
|
139634
|
+
}
|
|
139635
|
+
getOutputLines(sessionId, maxLines) {
|
|
139636
|
+
return this.runnerFor(sessionId).getOutputLines(sessionId, maxLines);
|
|
139637
|
+
}
|
|
139638
|
+
getSession(sessionId) {
|
|
139639
|
+
for (const runner of this.runners.values()) {
|
|
139640
|
+
const session = runner.getSession(sessionId);
|
|
139641
|
+
if (session) return session;
|
|
139642
|
+
}
|
|
139643
|
+
return null;
|
|
139644
|
+
}
|
|
139645
|
+
hasSession(sessionId) {
|
|
139646
|
+
for (const runner of this.runners.values()) {
|
|
139647
|
+
if (runner.hasSession(sessionId)) return true;
|
|
139648
|
+
}
|
|
139649
|
+
return false;
|
|
139650
|
+
}
|
|
139651
|
+
listSessions() {
|
|
139652
|
+
return Array.from(this.runners.values()).flatMap((runner) => runner.listSessions());
|
|
139653
|
+
}
|
|
139654
|
+
dispose() {
|
|
139655
|
+
for (const runner of this.runners.values()) {
|
|
139656
|
+
runner.dispose();
|
|
139657
|
+
}
|
|
139658
|
+
}
|
|
139659
|
+
// Look up which runner owns a session. Only one runner exists today, so
|
|
139660
|
+
// this is a linear scan across hasSession()/getSession() rather than a
|
|
139661
|
+
// separate session→provider index — see task-1-brief.md.
|
|
139662
|
+
runnerFor(sessionId) {
|
|
139663
|
+
for (const runner of this.runners.values()) {
|
|
139664
|
+
if (runner.hasSession(sessionId) || runner.getSession(sessionId)) return runner;
|
|
139665
|
+
}
|
|
139666
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
139667
|
+
}
|
|
139668
|
+
assertSupportedProvider(provider, projectPath) {
|
|
139669
|
+
const runner = this.runners.get(provider);
|
|
139670
|
+
if (runner) return runner;
|
|
139671
|
+
const err = new Error(
|
|
139672
|
+
`Live ${provider} sessions are not implemented yet for ${(0, import_path18.basename)(projectPath)}`
|
|
139673
|
+
);
|
|
139674
|
+
err.statusCode = 501;
|
|
139675
|
+
throw err;
|
|
139676
|
+
}
|
|
139677
|
+
};
|
|
139678
|
+
|
|
139679
|
+
// src/server.ts
|
|
139680
|
+
init_logger();
|
|
139681
|
+
|
|
139682
|
+
// src/pair-store.ts
|
|
139683
|
+
var import_crypto7 = require("crypto");
|
|
139684
|
+
var DEFAULT_TTL_SECONDS = 180;
|
|
139685
|
+
var SWEEP_INTERVAL_MS = 6e4;
|
|
139686
|
+
var PairTokenStore = class {
|
|
139687
|
+
current = null;
|
|
139688
|
+
ttlMs;
|
|
139689
|
+
sweepTimer = null;
|
|
139690
|
+
constructor(opts = {}) {
|
|
139691
|
+
this.ttlMs = (opts.ttlSeconds ?? DEFAULT_TTL_SECONDS) * 1e3;
|
|
139692
|
+
if (opts.autoSweep !== false) {
|
|
139693
|
+
this.sweepTimer = setInterval(() => this.sweep(), SWEEP_INTERVAL_MS);
|
|
139694
|
+
this.sweepTimer.unref?.();
|
|
139695
|
+
}
|
|
139696
|
+
}
|
|
139697
|
+
mint() {
|
|
139698
|
+
const token = `pt_${(0, import_crypto7.randomBytes)(16).toString("hex")}`;
|
|
139699
|
+
const expiresAt = Date.now() + this.ttlMs;
|
|
139700
|
+
this.current = { token, expiresAt, used: false };
|
|
139701
|
+
return {
|
|
139702
|
+
token,
|
|
139703
|
+
expiresAt,
|
|
139704
|
+
expiresInSeconds: Math.floor(this.ttlMs / 1e3)
|
|
139705
|
+
};
|
|
139706
|
+
}
|
|
139707
|
+
consume(token) {
|
|
139708
|
+
const record2 = this.current;
|
|
139709
|
+
if (!record2 || record2.token !== token) return { ok: false, reason: "unknown" };
|
|
139710
|
+
if (Date.now() > record2.expiresAt) {
|
|
139711
|
+
this.current = null;
|
|
139712
|
+
return { ok: false, reason: "expired" };
|
|
139713
|
+
}
|
|
139714
|
+
if (record2.used) return { ok: false, reason: "used" };
|
|
139715
|
+
record2.used = true;
|
|
139716
|
+
return { ok: true };
|
|
139717
|
+
}
|
|
139718
|
+
peek() {
|
|
139719
|
+
return this.current;
|
|
139720
|
+
}
|
|
139721
|
+
clear() {
|
|
139722
|
+
this.current = null;
|
|
139723
|
+
}
|
|
139724
|
+
sweep() {
|
|
139725
|
+
if (this.current && Date.now() > this.current.expiresAt) {
|
|
139726
|
+
this.current = null;
|
|
139727
|
+
}
|
|
139728
|
+
}
|
|
139729
|
+
dispose() {
|
|
139730
|
+
if (this.sweepTimer) clearInterval(this.sweepTimer);
|
|
139731
|
+
this.sweepTimer = null;
|
|
139732
|
+
this.current = null;
|
|
139733
|
+
}
|
|
139734
|
+
};
|
|
139735
|
+
|
|
139736
|
+
// src/process-discovery.ts
|
|
139737
|
+
var import_child_process2 = require("child_process");
|
|
139738
|
+
var import_os9 = require("os");
|
|
139739
|
+
var import_path19 = require("path");
|
|
139740
|
+
async function discoverClaudeProcesses() {
|
|
139741
|
+
if ((0, import_os9.platform)() === "win32") return discoverWindows();
|
|
139742
|
+
return discoverUnix();
|
|
139743
|
+
}
|
|
139744
|
+
async function discoverUnix() {
|
|
139745
|
+
const pids = await getPidsUnix();
|
|
139746
|
+
const results = await Promise.all(
|
|
139747
|
+
pids.map(async (pid) => {
|
|
139748
|
+
try {
|
|
139749
|
+
const [cwd, args, startedAt] = await Promise.all([
|
|
139750
|
+
getProcessCwdUnix(pid),
|
|
139751
|
+
getProcessArgsUnix(pid),
|
|
139752
|
+
getProcessStartTimeUnix(pid)
|
|
139753
|
+
]);
|
|
139754
|
+
const conversationId = extractResumeId(args);
|
|
139755
|
+
return {
|
|
139756
|
+
pid,
|
|
139757
|
+
projectPath: cwd,
|
|
139758
|
+
projectName: (0, import_path19.basename)(cwd),
|
|
139759
|
+
branch: await readGitBranch2(cwd),
|
|
139760
|
+
conversationId,
|
|
139761
|
+
startedAt
|
|
139762
|
+
};
|
|
139763
|
+
} catch {
|
|
139764
|
+
return null;
|
|
139765
|
+
}
|
|
139766
|
+
})
|
|
139767
|
+
);
|
|
139768
|
+
return results.filter((r) => r !== null);
|
|
139769
|
+
}
|
|
139770
|
+
async function discoverWindows() {
|
|
139771
|
+
const pids = await getPidsWindows();
|
|
139772
|
+
const results = await Promise.all(
|
|
139773
|
+
pids.map(async (pid) => {
|
|
139774
|
+
try {
|
|
139775
|
+
const info = await getProcessInfoWindows(pid);
|
|
139776
|
+
if (!info) return null;
|
|
139777
|
+
return {
|
|
139778
|
+
pid,
|
|
139779
|
+
projectPath: info.cwd,
|
|
139780
|
+
projectName: (0, import_path19.basename)(info.cwd),
|
|
139781
|
+
branch: await readGitBranch2(info.cwd),
|
|
139782
|
+
conversationId: extractResumeId(info.args),
|
|
139783
|
+
startedAt: info.startedAt
|
|
139784
|
+
};
|
|
139785
|
+
} catch {
|
|
139786
|
+
return null;
|
|
139787
|
+
}
|
|
139788
|
+
})
|
|
139789
|
+
);
|
|
139790
|
+
return results.filter((r) => r !== null);
|
|
139791
|
+
}
|
|
139792
|
+
function run(cmd, args, opts = {}) {
|
|
139793
|
+
return new Promise((resolve6, reject) => {
|
|
139794
|
+
(0, import_child_process2.execFile)(
|
|
139795
|
+
cmd,
|
|
139796
|
+
args,
|
|
139797
|
+
{ windowsHide: isWindows2, encoding: "utf-8", timeout: opts.timeout ?? 5e3, cwd: opts.cwd },
|
|
139798
|
+
(err, stdout2) => {
|
|
139799
|
+
if (err) reject(err);
|
|
139800
|
+
else resolve6(stdout2);
|
|
139801
|
+
}
|
|
139802
|
+
);
|
|
139803
|
+
});
|
|
139804
|
+
}
|
|
139805
|
+
async function getPidsUnix() {
|
|
139806
|
+
try {
|
|
139807
|
+
const output = await run("pgrep", ["-x", "claude"]);
|
|
139808
|
+
return output.trim().split("\n").filter(Boolean).map((s3) => Number.parseInt(s3, 10));
|
|
139809
|
+
} catch {
|
|
139810
|
+
return [];
|
|
139811
|
+
}
|
|
139812
|
+
}
|
|
139813
|
+
async function getProcessCwdUnix(pid) {
|
|
139814
|
+
const output = await run("lsof", ["-p", String(pid), "-a", "-d", "cwd", "-Fn"]);
|
|
139815
|
+
const match2 = output.match(/n(.+)/);
|
|
139816
|
+
return match2?.[1] ?? "";
|
|
139817
|
+
}
|
|
139818
|
+
async function getProcessArgsUnix(pid) {
|
|
139819
|
+
return (await run("ps", ["-p", String(pid), "-o", "args="])).trim();
|
|
139820
|
+
}
|
|
139821
|
+
async function getProcessStartTimeUnix(pid) {
|
|
139822
|
+
const raw2 = (await run("ps", ["-p", String(pid), "-o", "lstart="])).trim();
|
|
139823
|
+
const d = new Date(raw2);
|
|
139824
|
+
return Number.isNaN(d.getTime()) ? /* @__PURE__ */ new Date() : d;
|
|
139825
|
+
}
|
|
139826
|
+
async function getPidsWindows() {
|
|
139827
|
+
try {
|
|
139828
|
+
const output = await run("tasklist", ["/FI", "IMAGENAME eq claude.exe", "/FO", "CSV", "/NH"]);
|
|
139829
|
+
return output.trim().split("\n").filter(Boolean).map((line) => {
|
|
139830
|
+
const parts = line.split(",");
|
|
139831
|
+
return Number.parseInt(parts[1]?.replace(/"/g, "") ?? "0", 10);
|
|
139832
|
+
}).filter((pid) => pid > 0);
|
|
139833
|
+
} catch {
|
|
139834
|
+
return [];
|
|
139835
|
+
}
|
|
139836
|
+
}
|
|
139837
|
+
async function getProcessInfoWindows(pid) {
|
|
139838
|
+
try {
|
|
139839
|
+
const output = await run("wmic", [
|
|
139840
|
+
"process",
|
|
139841
|
+
"where",
|
|
139842
|
+
`ProcessId=${pid}`,
|
|
139843
|
+
"get",
|
|
139844
|
+
"CommandLine,CreationDate,ExecutablePath",
|
|
139845
|
+
"/FORMAT:CSV"
|
|
139846
|
+
]);
|
|
139847
|
+
const lines = output.trim().split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
139848
|
+
if (lines.length < 2) return null;
|
|
139849
|
+
const parts = lines[1].split(",");
|
|
139850
|
+
const args = parts[1] ?? "";
|
|
139851
|
+
const creationDate = parts[2] ?? "";
|
|
139852
|
+
const year = creationDate.slice(0, 4);
|
|
139853
|
+
const month = creationDate.slice(4, 6);
|
|
139854
|
+
const day = creationDate.slice(6, 8);
|
|
139855
|
+
const hour = creationDate.slice(8, 10);
|
|
139856
|
+
const min = creationDate.slice(10, 12);
|
|
139857
|
+
const sec = creationDate.slice(12, 14);
|
|
139858
|
+
const startedAt = /* @__PURE__ */ new Date(`${year}-${month}-${day}T${hour}:${min}:${sec}`);
|
|
139859
|
+
if (Number.isNaN(startedAt.getTime())) return null;
|
|
139860
|
+
const exePath = parts[3] ?? "";
|
|
139861
|
+
const cwd = exePath ? (0, import_path19.dirname)(exePath) : "";
|
|
139862
|
+
return { cwd, args, startedAt };
|
|
139863
|
+
} catch {
|
|
139864
|
+
return null;
|
|
139865
|
+
}
|
|
139866
|
+
}
|
|
139867
|
+
function extractResumeId(args) {
|
|
139868
|
+
const match2 = args.match(/--resume\s+(\S+)/);
|
|
139869
|
+
return match2?.[1] ?? null;
|
|
139870
|
+
}
|
|
139871
|
+
async function readGitBranch2(dir) {
|
|
139872
|
+
try {
|
|
139873
|
+
return (await run("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: dir, timeout: 3e3 })).trim();
|
|
139874
|
+
} catch {
|
|
139875
|
+
return "";
|
|
139876
|
+
}
|
|
139877
|
+
}
|
|
139878
|
+
|
|
139179
139879
|
// src/seal.ts
|
|
139180
139880
|
var import_tweetnacl = __toESM(require_nacl_fast(), 1);
|
|
139181
139881
|
var import_tweetnacl_util = __toESM(require_nacl_util(), 1);
|
|
@@ -139364,10 +140064,10 @@ var ReaddirpStream2 = class extends import_node_stream2.Readable {
|
|
|
139364
140064
|
}
|
|
139365
140065
|
async _formatEntry(dirent, path2) {
|
|
139366
140066
|
let entry;
|
|
139367
|
-
const
|
|
140067
|
+
const basename11 = this._isDirent ? dirent.name : dirent;
|
|
139368
140068
|
try {
|
|
139369
|
-
const fullPath = (0, import_node_path9.resolve)((0, import_node_path9.join)(path2,
|
|
139370
|
-
entry = { path: (0, import_node_path9.relative)(this._root, fullPath), fullPath, basename:
|
|
140069
|
+
const fullPath = (0, import_node_path9.resolve)((0, import_node_path9.join)(path2, basename11));
|
|
140070
|
+
entry = { path: (0, import_node_path9.relative)(this._root, fullPath), fullPath, basename: basename11 };
|
|
139371
140071
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
139372
140072
|
} catch (err) {
|
|
139373
140073
|
this._onError(err);
|
|
@@ -139908,9 +140608,9 @@ var NodeFsHandler2 = class {
|
|
|
139908
140608
|
_watchWithNodeFs(path2, listener) {
|
|
139909
140609
|
const opts = this.fsw.options;
|
|
139910
140610
|
const directory = sp.dirname(path2);
|
|
139911
|
-
const
|
|
140611
|
+
const basename11 = sp.basename(path2);
|
|
139912
140612
|
const parent = this.fsw._getWatchedDir(directory);
|
|
139913
|
-
parent.add(
|
|
140613
|
+
parent.add(basename11);
|
|
139914
140614
|
const absolutePath = sp.resolve(path2);
|
|
139915
140615
|
const options = {
|
|
139916
140616
|
persistent: opts.persistent
|
|
@@ -139920,7 +140620,7 @@ var NodeFsHandler2 = class {
|
|
|
139920
140620
|
let closer;
|
|
139921
140621
|
if (opts.usePolling) {
|
|
139922
140622
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
139923
|
-
options.interval = enableBin && isBinaryPath2(
|
|
140623
|
+
options.interval = enableBin && isBinaryPath2(basename11) ? opts.binaryInterval : opts.interval;
|
|
139924
140624
|
closer = setFsWatchFileListener2(path2, absolutePath, options, {
|
|
139925
140625
|
listener,
|
|
139926
140626
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -139943,10 +140643,10 @@ var NodeFsHandler2 = class {
|
|
|
139943
140643
|
return;
|
|
139944
140644
|
}
|
|
139945
140645
|
const dirname17 = sp.dirname(file2);
|
|
139946
|
-
const
|
|
140646
|
+
const basename11 = sp.basename(file2);
|
|
139947
140647
|
const parent = this.fsw._getWatchedDir(dirname17);
|
|
139948
140648
|
let prevStats = stats;
|
|
139949
|
-
if (parent.has(
|
|
140649
|
+
if (parent.has(basename11))
|
|
139950
140650
|
return;
|
|
139951
140651
|
const listener = async (path2, newStats) => {
|
|
139952
140652
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH2, file2, 5))
|
|
@@ -139971,9 +140671,9 @@ var NodeFsHandler2 = class {
|
|
|
139971
140671
|
prevStats = newStats2;
|
|
139972
140672
|
}
|
|
139973
140673
|
} catch (error51) {
|
|
139974
|
-
this.fsw._remove(dirname17,
|
|
140674
|
+
this.fsw._remove(dirname17, basename11);
|
|
139975
140675
|
}
|
|
139976
|
-
} else if (parent.has(
|
|
140676
|
+
} else if (parent.has(basename11)) {
|
|
139977
140677
|
const at2 = newStats.atimeMs;
|
|
139978
140678
|
const mt2 = newStats.mtimeMs;
|
|
139979
140679
|
if (!at2 || at2 <= mt2 || mt2 !== prevStats.mtimeMs) {
|
|
@@ -140930,7 +141630,7 @@ function watch2(paths, options = {}) {
|
|
|
140930
141630
|
var chokidar_default = { watch: watch2, FSWatcher: FSWatcher2 };
|
|
140931
141631
|
|
|
140932
141632
|
// src/services/conversations/conversationWatcher.ts
|
|
140933
|
-
var
|
|
141633
|
+
var import_fs22 = require("fs");
|
|
140934
141634
|
var import_promises12 = require("fs/promises");
|
|
140935
141635
|
var ConversationWatcher = class {
|
|
140936
141636
|
files = /* @__PURE__ */ new Map();
|
|
@@ -140951,7 +141651,7 @@ var ConversationWatcher = class {
|
|
|
140951
141651
|
if (this.files.has(filePath)) return;
|
|
140952
141652
|
let offset;
|
|
140953
141653
|
try {
|
|
140954
|
-
offset = (0,
|
|
141654
|
+
offset = (0, import_fs22.statSync)(filePath).size;
|
|
140955
141655
|
} catch {
|
|
140956
141656
|
offset = 0;
|
|
140957
141657
|
}
|
|
@@ -141060,14 +141760,14 @@ var ConversationWatcher = class {
|
|
|
141060
141760
|
};
|
|
141061
141761
|
|
|
141062
141762
|
// src/services/conversations/pruneAgentConversations.ts
|
|
141063
|
-
var
|
|
141763
|
+
var import_fs23 = require("fs");
|
|
141064
141764
|
function pruneAgentConversations(cache) {
|
|
141065
141765
|
const db = cache.getDatabase();
|
|
141066
141766
|
const rows = db.prepare("SELECT id, file_path FROM conversation_meta").all();
|
|
141067
141767
|
let pruned = 0;
|
|
141068
141768
|
let missing = 0;
|
|
141069
141769
|
for (const row of rows) {
|
|
141070
|
-
if (!(0,
|
|
141770
|
+
if (!(0, import_fs23.existsSync)(row.file_path)) {
|
|
141071
141771
|
missing += 1;
|
|
141072
141772
|
continue;
|
|
141073
141773
|
}
|
|
@@ -141376,6 +142076,7 @@ function managedToResponse(s3, ptyAttached) {
|
|
|
141376
142076
|
return {
|
|
141377
142077
|
id: s3.id,
|
|
141378
142078
|
conversationId: s3.id,
|
|
142079
|
+
provider: s3.provider ?? CLAUDE_CODE_PROVIDER2,
|
|
141379
142080
|
status: s3.status,
|
|
141380
142081
|
projectPath: s3.projectPath,
|
|
141381
142082
|
projectName: s3.projectName,
|
|
@@ -141401,13 +142102,15 @@ function managedToResponse(s3, ptyAttached) {
|
|
|
141401
142102
|
...s3.failureReason != null && { failureReason: s3.failureReason },
|
|
141402
142103
|
...s3.resumedFromConversationId != null && {
|
|
141403
142104
|
resumedFromConversationId: s3.resumedFromConversationId
|
|
141404
|
-
}
|
|
142105
|
+
},
|
|
142106
|
+
...s3.boundConversationId != null && { boundConversationId: s3.boundConversationId }
|
|
141405
142107
|
};
|
|
141406
142108
|
}
|
|
141407
142109
|
function discoveredToResponse(d, conversationId) {
|
|
141408
142110
|
return {
|
|
141409
142111
|
id: conversationId,
|
|
141410
142112
|
conversationId,
|
|
142113
|
+
provider: CLAUDE_CODE_PROVIDER2,
|
|
141411
142114
|
status: "idle",
|
|
141412
142115
|
projectPath: d.projectPath,
|
|
141413
142116
|
projectName: d.projectName,
|
|
@@ -141423,10 +142126,10 @@ function discoveredToResponse(d, conversationId) {
|
|
|
141423
142126
|
}
|
|
141424
142127
|
|
|
141425
142128
|
// src/uploads.ts
|
|
141426
|
-
var
|
|
142129
|
+
var import_crypto8 = require("crypto");
|
|
141427
142130
|
var import_promises13 = require("fs/promises");
|
|
141428
142131
|
var import_heic_convert = __toESM(require_heic_convert(), 1);
|
|
141429
|
-
var
|
|
142132
|
+
var import_path20 = require("path");
|
|
141430
142133
|
var UPLOAD_DIR_NAME = ".threadbase-uploads";
|
|
141431
142134
|
var MAX_BYTES = 25 * 1024 * 1024;
|
|
141432
142135
|
var HEIC_MIMES = /* @__PURE__ */ new Set(["image/heic", "image/heif"]);
|
|
@@ -141457,11 +142160,11 @@ async function saveUploadFile(input) {
|
|
|
141457
142160
|
mimeType = "image/jpeg";
|
|
141458
142161
|
originalName = originalName.replace(/\.(heic|heif)$/i, ".jpg");
|
|
141459
142162
|
}
|
|
141460
|
-
const id = `up_${(0,
|
|
142163
|
+
const id = `up_${(0, import_crypto8.randomBytes)(8).toString("hex")}`;
|
|
141461
142164
|
const safeName = sanitizeFilename(originalName) || `file${MIME_TO_EXT[mimeType] ?? ""}`;
|
|
141462
|
-
const dir = (0,
|
|
142165
|
+
const dir = (0, import_path20.join)(input.projectPath, UPLOAD_DIR_NAME, input.sessionId);
|
|
141463
142166
|
await (0, import_promises13.mkdir)(dir, { recursive: true });
|
|
141464
|
-
const filePath = (0,
|
|
142167
|
+
const filePath = (0, import_path20.join)(dir, `${Date.now()}-${id}-${safeName}`);
|
|
141465
142168
|
await (0, import_promises13.writeFile)(filePath, buffer);
|
|
141466
142169
|
return {
|
|
141467
142170
|
id,
|
|
@@ -141974,10 +142677,10 @@ var StreamerServer = class {
|
|
|
141974
142677
|
this.verbose = config2.verbose ?? false;
|
|
141975
142678
|
this.disableDb = config2.disableDb ?? false;
|
|
141976
142679
|
this.scanProfiles = config2.scanProfiles;
|
|
141977
|
-
this.codexRoots = config2.codexRoots ?? [(0,
|
|
142680
|
+
this.codexRoots = config2.codexRoots ?? [(0, import_path21.join)((0, import_os10.homedir)(), ".codex", "sessions")];
|
|
141978
142681
|
this.ptyGracePeriodMs = config2.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
|
|
141979
142682
|
this.defaultSystemPrompt = config2.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
141980
|
-
this.cacheDir = config2.cacheDir ?? loadCacheDir() ?? (0,
|
|
142683
|
+
this.cacheDir = config2.cacheDir ?? loadCacheDir() ?? (0, import_path21.join)((0, import_os10.homedir)(), ".threadbase", "cache");
|
|
141981
142684
|
this.tailSize = config2.tailSize ?? loadTailSize() ?? 10;
|
|
141982
142685
|
this.directoryDebounceMs = parseDirScanDebounceEnv(process.env.THREADBASE_DIR_SCAN_DEBOUNCE_MS) ?? config2.directoryScanDebounceMs ?? 1e3;
|
|
141983
142686
|
this.markScannerStaleDebounced = debounce(() => {
|
|
@@ -142044,7 +142747,7 @@ var StreamerServer = class {
|
|
|
142044
142747
|
}
|
|
142045
142748
|
},
|
|
142046
142749
|
onConversationChanged: (filePath) => {
|
|
142047
|
-
this.cache?.invalidateByFilePath(filePath);
|
|
142750
|
+
this.cache?.invalidateByFilePath(filePath, { skipIfTailed: true });
|
|
142048
142751
|
this.markScannerStaleDebounced();
|
|
142049
142752
|
this.log.debug?.(`Scanner invalidated by directory event: ${filePath}`, {
|
|
142050
142753
|
filePath,
|
|
@@ -142061,7 +142764,7 @@ var StreamerServer = class {
|
|
|
142061
142764
|
});
|
|
142062
142765
|
}
|
|
142063
142766
|
});
|
|
142064
|
-
this.ptyManager = new
|
|
142767
|
+
this.ptyManager = new LiveSessionManager({
|
|
142065
142768
|
logger: getLogger("pty"),
|
|
142066
142769
|
onOutput: (sessionId, data) => {
|
|
142067
142770
|
this.wsHub.broadcast({ type: "terminal_output", sessionId, data });
|
|
@@ -142142,7 +142845,7 @@ var StreamerServer = class {
|
|
|
142142
142845
|
temporalClient,
|
|
142143
142846
|
taskQueue: agentConfig.temporal.taskQueue
|
|
142144
142847
|
});
|
|
142145
|
-
const conversationsBaseDir = agentConfig.conversationsDir || (0,
|
|
142848
|
+
const conversationsBaseDir = agentConfig.conversationsDir || (0, import_path21.join)((0, import_path21.dirname)(this.cacheDir), "conversations");
|
|
142146
142849
|
conversationWriter = createConversationWriter({
|
|
142147
142850
|
baseDir: conversationsBaseDir
|
|
142148
142851
|
});
|
|
@@ -142364,7 +143067,7 @@ var StreamerServer = class {
|
|
|
142364
143067
|
});
|
|
142365
143068
|
try {
|
|
142366
143069
|
this.cache = ConversationCache.open(
|
|
142367
|
-
(0,
|
|
143070
|
+
(0, import_path21.join)(this.cacheDir, "cache.db"),
|
|
142368
143071
|
this.tailSize,
|
|
142369
143072
|
void 0,
|
|
142370
143073
|
{
|
|
@@ -142390,18 +143093,19 @@ var StreamerServer = class {
|
|
|
142390
143093
|
if (this.scanProfiles && this.scanProfiles.length > 0) {
|
|
142391
143094
|
for (const profile of this.scanProfiles) {
|
|
142392
143095
|
if (profile.enabled) {
|
|
142393
|
-
this.fileWatcher.watchDirectory((0,
|
|
143096
|
+
this.fileWatcher.watchDirectory((0, import_path21.join)(profile.configDir, "projects"));
|
|
142394
143097
|
}
|
|
142395
143098
|
}
|
|
142396
143099
|
} else {
|
|
142397
|
-
this.fileWatcher.watchDirectory((0,
|
|
143100
|
+
this.fileWatcher.watchDirectory((0, import_path21.join)((0, import_os10.homedir)(), ".claude", "projects"));
|
|
142398
143101
|
}
|
|
142399
143102
|
} catch (err) {
|
|
142400
143103
|
const message = err instanceof Error ? err.message : String(err);
|
|
142401
|
-
|
|
142402
|
-
|
|
142403
|
-
|
|
142404
|
-
|
|
143104
|
+
const abiMismatch = message.includes("NODE_MODULE_VERSION") || message.includes("was compiled against a different Node.js version");
|
|
143105
|
+
this.log.error(
|
|
143106
|
+
`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})`,
|
|
143107
|
+
{ error: message, abiMismatch, event: "cache.open_failed" }
|
|
143108
|
+
);
|
|
142405
143109
|
}
|
|
142406
143110
|
const warmupScanner = new ConversationScanner();
|
|
142407
143111
|
this.allScanners.add(warmupScanner);
|
|
@@ -142888,17 +143592,17 @@ var StreamerServer = class {
|
|
|
142888
143592
|
return this.getScanner();
|
|
142889
143593
|
}
|
|
142890
143594
|
findJsonlPath(uuid3) {
|
|
142891
|
-
const projectsDir = (0,
|
|
142892
|
-
if (!(0,
|
|
143595
|
+
const projectsDir = (0, import_path21.join)((0, import_os10.homedir)(), ".claude", "projects");
|
|
143596
|
+
if (!(0, import_fs24.existsSync)(projectsDir)) return null;
|
|
142893
143597
|
const filename = `${uuid3}.jsonl`;
|
|
142894
|
-
for (const dir of (0,
|
|
142895
|
-
const fp = (0,
|
|
142896
|
-
if ((0,
|
|
142897
|
-
const projectDir = (0,
|
|
143598
|
+
for (const dir of (0, import_fs24.readdirSync)(projectsDir)) {
|
|
143599
|
+
const fp = (0, import_path21.join)(projectsDir, dir, filename);
|
|
143600
|
+
if ((0, import_fs24.existsSync)(fp)) return fp;
|
|
143601
|
+
const projectDir = (0, import_path21.join)(projectsDir, dir);
|
|
142898
143602
|
try {
|
|
142899
|
-
for (const sub of (0,
|
|
142900
|
-
const subagentPath = (0,
|
|
142901
|
-
if ((0,
|
|
143603
|
+
for (const sub of (0, import_fs24.readdirSync)(projectDir)) {
|
|
143604
|
+
const subagentPath = (0, import_path21.join)(projectDir, sub, "subagents", filename);
|
|
143605
|
+
if ((0, import_fs24.existsSync)(subagentPath)) return subagentPath;
|
|
142902
143606
|
}
|
|
142903
143607
|
} catch {
|
|
142904
143608
|
}
|
|
@@ -142907,7 +143611,7 @@ var StreamerServer = class {
|
|
|
142907
143611
|
}
|
|
142908
143612
|
async readCwdFromJsonl(filePath) {
|
|
142909
143613
|
return new Promise((resolve6) => {
|
|
142910
|
-
const rl = (0, import_readline4.createInterface)({ input: (0,
|
|
143614
|
+
const rl = (0, import_readline4.createInterface)({ input: (0, import_fs24.createReadStream)(filePath), crlfDelay: Infinity });
|
|
142911
143615
|
let found = false;
|
|
142912
143616
|
rl.on("line", (line) => {
|
|
142913
143617
|
if (found) return;
|
|
@@ -142964,7 +143668,7 @@ var StreamerServer = class {
|
|
|
142964
143668
|
if (!conv.filePath) return false;
|
|
142965
143669
|
let mtimeMs = null;
|
|
142966
143670
|
try {
|
|
142967
|
-
mtimeMs = (0,
|
|
143671
|
+
mtimeMs = (0, import_fs24.statSync)(conv.filePath).mtimeMs;
|
|
142968
143672
|
} catch {
|
|
142969
143673
|
return false;
|
|
142970
143674
|
}
|
|
@@ -143207,7 +143911,7 @@ var StreamerServer = class {
|
|
|
143207
143911
|
handleGetSession(sessionId, res) {
|
|
143208
143912
|
const session = this.sessionStore.get(sessionId, this.ptyAttachedIds());
|
|
143209
143913
|
if (session) {
|
|
143210
|
-
if (!(0,
|
|
143914
|
+
if (!(0, import_fs24.existsSync)(session.projectPath)) {
|
|
143211
143915
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
143212
143916
|
}
|
|
143213
143917
|
json2(res, 200, session);
|
|
@@ -143247,7 +143951,10 @@ var StreamerServer = class {
|
|
|
143247
143951
|
json2(res, 400, { error: "Could not determine project path" });
|
|
143248
143952
|
return;
|
|
143249
143953
|
}
|
|
143954
|
+
const cachedConvMeta = this.cache?.getMetaById(sessionId);
|
|
143955
|
+
const provider = conv?.provider ?? cachedConvMeta?.provider ?? CLAUDE_CODE_PROVIDER2;
|
|
143250
143956
|
const session = await this.ptyManager.start(sessionId, {
|
|
143957
|
+
provider,
|
|
143251
143958
|
projectPath,
|
|
143252
143959
|
projectName: body.projectName,
|
|
143253
143960
|
branch: body.branch
|
|
@@ -143597,7 +144304,7 @@ var StreamerServer = class {
|
|
|
143597
144304
|
sessionStore: this.sessionStore,
|
|
143598
144305
|
// biome-ignore lint/style/noNonNullAssertion: agentClient is set when agentConfig.enabled is true
|
|
143599
144306
|
agentClient: this.agentClient,
|
|
143600
|
-
conversationsDir: this.cacheDir ? (0,
|
|
144307
|
+
conversationsDir: this.cacheDir ? (0, import_path21.join)((0, import_path21.dirname)(this.cacheDir), "conversations") : "",
|
|
143601
144308
|
agentConfig: this.agentConfig
|
|
143602
144309
|
});
|
|
143603
144310
|
json2(res, result.status, result.body);
|
|
@@ -143606,6 +144313,13 @@ var StreamerServer = class {
|
|
|
143606
144313
|
}
|
|
143607
144314
|
return;
|
|
143608
144315
|
}
|
|
144316
|
+
const body = await readBody(req);
|
|
144317
|
+
const { path: relativePath, provider: requestedProvider, systemPrompt: clientPrompt } = body;
|
|
144318
|
+
if (requestedProvider !== void 0 && !isProviderName(requestedProvider)) {
|
|
144319
|
+
json2(res, 400, { error: "Invalid provider" });
|
|
144320
|
+
return;
|
|
144321
|
+
}
|
|
144322
|
+
const provider = requestedProvider ?? CLAUDE_CODE_PROVIDER2;
|
|
143609
144323
|
if (!this.browseRoot) {
|
|
143610
144324
|
json2(res, 403, {
|
|
143611
144325
|
error: "File browsing not configured. Set browseRoot on the server.",
|
|
@@ -143613,8 +144327,6 @@ var StreamerServer = class {
|
|
|
143613
144327
|
});
|
|
143614
144328
|
return;
|
|
143615
144329
|
}
|
|
143616
|
-
const body = await readBody(req);
|
|
143617
|
-
const { path: relativePath, systemPrompt: clientPrompt } = body;
|
|
143618
144330
|
if (typeof relativePath !== "string") {
|
|
143619
144331
|
json2(res, 400, { error: "Missing path field" });
|
|
143620
144332
|
return;
|
|
@@ -143635,21 +144347,27 @@ var StreamerServer = class {
|
|
|
143635
144347
|
].filter(Boolean);
|
|
143636
144348
|
try {
|
|
143637
144349
|
const session = await this.ptyManager.startFresh({
|
|
144350
|
+
provider,
|
|
143638
144351
|
projectPath: resolvedPath,
|
|
143639
144352
|
projectName: body.projectName,
|
|
143640
144353
|
systemPrompt: systemPromptParts.join("\n")
|
|
143641
144354
|
});
|
|
143642
144355
|
this.sessionStore.addManaged(session);
|
|
143643
144356
|
json2(res, 202, { id: session.id, status: "pending" });
|
|
143644
|
-
|
|
144357
|
+
if (provider === CODEX_CLI_PROVIDER2) {
|
|
144358
|
+
this.watchForCodexRollout(session.id, resolvedPath);
|
|
144359
|
+
} else {
|
|
144360
|
+
this.watchForJsonl(session.id, resolvedPath);
|
|
144361
|
+
}
|
|
143645
144362
|
this.broadcastOrUnicastSessionList(req);
|
|
143646
144363
|
} catch (err) {
|
|
143647
144364
|
const message = err instanceof Error ? err.message : "Failed to start session";
|
|
144365
|
+
const statusCode = typeof err.statusCode === "number" ? err.statusCode : 500;
|
|
143648
144366
|
this.log.error(`[start] failed to start session: ${message}`, {
|
|
143649
144367
|
event: "session.start_failed",
|
|
143650
144368
|
error: message
|
|
143651
144369
|
});
|
|
143652
|
-
json2(res,
|
|
144370
|
+
json2(res, statusCode, { error: message });
|
|
143653
144371
|
}
|
|
143654
144372
|
}
|
|
143655
144373
|
// ─── Project linking ─────────────────────────────────────────────
|
|
@@ -143702,9 +144420,9 @@ var StreamerServer = class {
|
|
|
143702
144420
|
// was passed to Claude via --session-id so the filename matches from the start.
|
|
143703
144421
|
watchForJsonl(sessionId, projectPath) {
|
|
143704
144422
|
const encoded = projectPath.replace(/[/\\:.]/g, "-");
|
|
143705
|
-
const projectsDir = (0,
|
|
144423
|
+
const projectsDir = (0, import_path21.join)((0, import_os10.homedir)(), ".claude", "projects", encoded);
|
|
143706
144424
|
const expectedFile = `${sessionId}.jsonl`;
|
|
143707
|
-
const filePath = (0,
|
|
144425
|
+
const filePath = (0, import_path21.join)(projectsDir, expectedFile);
|
|
143708
144426
|
const deadline = Date.now() + 12e4;
|
|
143709
144427
|
let watcher = null;
|
|
143710
144428
|
const cleanup = () => {
|
|
@@ -143722,12 +144440,12 @@ var StreamerServer = class {
|
|
|
143722
144440
|
cleanup();
|
|
143723
144441
|
return;
|
|
143724
144442
|
}
|
|
143725
|
-
let resolvedFilePath = (0,
|
|
143726
|
-
if (!resolvedFilePath && (0,
|
|
144443
|
+
let resolvedFilePath = (0, import_fs24.existsSync)(filePath) ? filePath : null;
|
|
144444
|
+
if (!resolvedFilePath && (0, import_fs24.existsSync)(projectsDir)) {
|
|
143727
144445
|
try {
|
|
143728
144446
|
const now = Date.now();
|
|
143729
|
-
const recent = (0,
|
|
143730
|
-
if (recent) resolvedFilePath = (0,
|
|
144447
|
+
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];
|
|
144448
|
+
if (recent) resolvedFilePath = (0, import_path21.join)(projectsDir, recent.f);
|
|
143731
144449
|
} catch {
|
|
143732
144450
|
}
|
|
143733
144451
|
}
|
|
@@ -143736,7 +144454,7 @@ var StreamerServer = class {
|
|
|
143736
144454
|
this.sessionFileMap.set(sessionId, resolvedFilePath);
|
|
143737
144455
|
this.fileWatcher.watch(resolvedFilePath);
|
|
143738
144456
|
try {
|
|
143739
|
-
const existing = (0,
|
|
144457
|
+
const existing = (0, import_fs24.readFileSync)(resolvedFilePath, "utf8").split("\n").filter(Boolean);
|
|
143740
144458
|
if (existing.length > 0) {
|
|
143741
144459
|
this.wsHub.broadcast({ type: "conversation_events", sessionId, lines: existing });
|
|
143742
144460
|
for (const line of existing) {
|
|
@@ -143762,11 +144480,126 @@ var StreamerServer = class {
|
|
|
143762
144480
|
if (this.sessionFileMap.has(sessionId)) return;
|
|
143763
144481
|
try {
|
|
143764
144482
|
require("fs").mkdirSync(projectsDir, { recursive: true });
|
|
143765
|
-
watcher = (0,
|
|
144483
|
+
watcher = (0, import_fs24.watch)(projectsDir, tryWire);
|
|
143766
144484
|
watcher.on("error", cleanup);
|
|
143767
144485
|
} catch {
|
|
143768
144486
|
}
|
|
143769
144487
|
}
|
|
144488
|
+
// Codex-equivalent of watchForJsonl(). Differs because Codex has no
|
|
144489
|
+
// filename-encoded session id (it assigns its own persisted id) and its
|
|
144490
|
+
// rollout files live under a date-nested directory
|
|
144491
|
+
// (~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-*.jsonl) that Codex creates
|
|
144492
|
+
// itself — it may not exist yet when this function is first called, so we
|
|
144493
|
+
// poll rather than fs.watch a not-yet-existent directory. Per Phase 0
|
|
144494
|
+
// findings, the rollout file appears within ~1s of process spawn (after
|
|
144495
|
+
// any directory-trust gate is cleared), well before any user input.
|
|
144496
|
+
watchForCodexRollout(sessionId, projectPath) {
|
|
144497
|
+
const deadline = Date.now() + 12e4;
|
|
144498
|
+
const now = /* @__PURE__ */ new Date();
|
|
144499
|
+
const dateDir = (0, import_path21.join)(
|
|
144500
|
+
String(now.getFullYear()),
|
|
144501
|
+
String(now.getMonth() + 1).padStart(2, "0"),
|
|
144502
|
+
String(now.getDate()).padStart(2, "0")
|
|
144503
|
+
);
|
|
144504
|
+
const sessionStartedAtMs = (this.sessionStore.getManaged(sessionId)?.startedAt?.getTime() ?? Date.now()) - 5e3;
|
|
144505
|
+
let intervalHandle = null;
|
|
144506
|
+
const cleanup = () => {
|
|
144507
|
+
if (intervalHandle) clearInterval(intervalHandle);
|
|
144508
|
+
intervalHandle = null;
|
|
144509
|
+
};
|
|
144510
|
+
const matchesProjectPath = (candidatePath) => {
|
|
144511
|
+
try {
|
|
144512
|
+
const firstLine = (0, import_fs24.readFileSync)(candidatePath, "utf8").split("\n", 1)[0];
|
|
144513
|
+
if (!firstLine) return null;
|
|
144514
|
+
const parsed = JSON.parse(firstLine);
|
|
144515
|
+
if (parsed?.type !== "session_meta") return null;
|
|
144516
|
+
const payload = parsed.payload ?? {};
|
|
144517
|
+
if (payload.cwd !== projectPath) return null;
|
|
144518
|
+
if (typeof payload.id !== "string") return null;
|
|
144519
|
+
const createdIso = payload.timestamp ?? parsed.timestamp;
|
|
144520
|
+
const createdAtMs = typeof createdIso === "string" ? Date.parse(createdIso) : Number.NaN;
|
|
144521
|
+
if (Number.isNaN(createdAtMs) || createdAtMs < sessionStartedAtMs) return null;
|
|
144522
|
+
return { id: payload.id, createdAtMs };
|
|
144523
|
+
} catch {
|
|
144524
|
+
return null;
|
|
144525
|
+
}
|
|
144526
|
+
};
|
|
144527
|
+
const tryWire = () => {
|
|
144528
|
+
if (!this.ptyManager.hasSession(sessionId)) {
|
|
144529
|
+
cleanup();
|
|
144530
|
+
return;
|
|
144531
|
+
}
|
|
144532
|
+
if (Date.now() > deadline) {
|
|
144533
|
+
cleanup();
|
|
144534
|
+
return;
|
|
144535
|
+
}
|
|
144536
|
+
const boundElsewhere = new Set(
|
|
144537
|
+
this.sessionStore.listManaged().filter((s3) => s3.id !== sessionId && s3.boundConversationId != null).map((s3) => s3.boundConversationId)
|
|
144538
|
+
);
|
|
144539
|
+
for (const root of this.codexRoots) {
|
|
144540
|
+
const sessionsDir = (0, import_path21.join)(root, dateDir);
|
|
144541
|
+
if (!(0, import_fs24.existsSync)(sessionsDir)) continue;
|
|
144542
|
+
let candidateFiles;
|
|
144543
|
+
try {
|
|
144544
|
+
candidateFiles = (0, import_fs24.readdirSync)(sessionsDir).filter((f2) => f2.endsWith(".jsonl"));
|
|
144545
|
+
} catch {
|
|
144546
|
+
continue;
|
|
144547
|
+
}
|
|
144548
|
+
const nowMs = Date.now();
|
|
144549
|
+
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);
|
|
144550
|
+
for (const { f: f2 } of recentCandidates) {
|
|
144551
|
+
const candidatePath = (0, import_path21.join)(sessionsDir, f2);
|
|
144552
|
+
const match2 = matchesProjectPath(candidatePath);
|
|
144553
|
+
if (!match2) continue;
|
|
144554
|
+
if (boundElsewhere.has(match2.id)) continue;
|
|
144555
|
+
const codexSessionId = match2.id;
|
|
144556
|
+
cleanup();
|
|
144557
|
+
this.sessionStore.updateManaged(sessionId, { boundConversationId: codexSessionId });
|
|
144558
|
+
this.sessionFileMap.set(sessionId, candidatePath);
|
|
144559
|
+
this.fileWatcher.watch(candidatePath);
|
|
144560
|
+
try {
|
|
144561
|
+
const existing = (0, import_fs24.readFileSync)(candidatePath, "utf8").split("\n").filter(Boolean);
|
|
144562
|
+
if (existing.length > 0) {
|
|
144563
|
+
this.wsHub.broadcast({ type: "conversation_events", sessionId, lines: existing });
|
|
144564
|
+
for (const line of existing) {
|
|
144565
|
+
this.wsHub.broadcast({ type: "conversation_event", sessionId, line });
|
|
144566
|
+
}
|
|
144567
|
+
}
|
|
144568
|
+
} catch {
|
|
144569
|
+
}
|
|
144570
|
+
if (this.scannerReady) {
|
|
144571
|
+
this.scannerStale = true;
|
|
144572
|
+
} else {
|
|
144573
|
+
this.scanner = null;
|
|
144574
|
+
}
|
|
144575
|
+
this.linkSessionToProject(sessionId, projectPath, candidatePath);
|
|
144576
|
+
this.cache?.markAsStreamer(sessionId);
|
|
144577
|
+
const resp = this.sessionStore.get(sessionId, this.ptyAttachedIds());
|
|
144578
|
+
if (resp) {
|
|
144579
|
+
this.wsHub.broadcast({ type: "session_update", session: resp });
|
|
144580
|
+
}
|
|
144581
|
+
this.log.info(
|
|
144582
|
+
`[startFresh] bound Codex rollout for ${sessionId}`,
|
|
144583
|
+
{
|
|
144584
|
+
event: "session.codex_rollout_bound",
|
|
144585
|
+
sessionId,
|
|
144586
|
+
boundConversationId: codexSessionId,
|
|
144587
|
+
filePath: candidatePath
|
|
144588
|
+
},
|
|
144589
|
+
"pino"
|
|
144590
|
+
);
|
|
144591
|
+
return;
|
|
144592
|
+
}
|
|
144593
|
+
}
|
|
144594
|
+
};
|
|
144595
|
+
tryWire();
|
|
144596
|
+
if (!intervalHandle && Date.now() <= deadline) {
|
|
144597
|
+
const alreadyBound = this.sessionStore.getManaged(sessionId)?.boundConversationId != null;
|
|
144598
|
+
if (!alreadyBound) {
|
|
144599
|
+
intervalHandle = setInterval(tryWire, 250);
|
|
144600
|
+
}
|
|
144601
|
+
}
|
|
144602
|
+
}
|
|
143770
144603
|
async handleBrowse(url2, res) {
|
|
143771
144604
|
if (!this.browseRoot) {
|
|
143772
144605
|
json2(res, 403, {
|
|
@@ -143850,7 +144683,7 @@ var StreamerServer = class {
|
|
|
143850
144683
|
};
|
|
143851
144684
|
function classifyResumability(cwd) {
|
|
143852
144685
|
if (!cwd) return { resumable: true };
|
|
143853
|
-
if ((0,
|
|
144686
|
+
if ((0, import_fs24.existsSync)(cwd)) return { resumable: true };
|
|
143854
144687
|
const ranInWorktree = /\/\.worktrees\//.test(cwd) || /\/\.claude\/worktrees\//.test(cwd);
|
|
143855
144688
|
return {
|
|
143856
144689
|
resumable: false,
|
|
@@ -144430,13 +145263,13 @@ var import_node_fs17 = require("fs");
|
|
|
144430
145263
|
|
|
144431
145264
|
// node_modules/tar/dist/esm/index.min.js
|
|
144432
145265
|
var import_events4 = __toESM(require("events"), 1);
|
|
144433
|
-
var
|
|
145266
|
+
var import_fs25 = __toESM(require("fs"), 1);
|
|
144434
145267
|
var import_node_events3 = require("events");
|
|
144435
145268
|
var import_node_stream3 = __toESM(require("stream"), 1);
|
|
144436
145269
|
var import_node_string_decoder = require("string_decoder");
|
|
144437
145270
|
var import_node_path14 = __toESM(require("path"), 1);
|
|
144438
145271
|
var import_node_fs11 = __toESM(require("fs"), 1);
|
|
144439
|
-
var
|
|
145272
|
+
var import_path22 = require("path");
|
|
144440
145273
|
var import_events5 = require("events");
|
|
144441
145274
|
var import_assert = __toESM(require("assert"), 1);
|
|
144442
145275
|
var import_buffer = require("buffer");
|
|
@@ -144444,17 +145277,17 @@ var Ps = __toESM(require("zlib"), 1);
|
|
|
144444
145277
|
var import_zlib = __toESM(require("zlib"), 1);
|
|
144445
145278
|
var import_node_path15 = require("path");
|
|
144446
145279
|
var import_node_path16 = require("path");
|
|
144447
|
-
var import_fs25 = __toESM(require("fs"), 1);
|
|
144448
145280
|
var import_fs26 = __toESM(require("fs"), 1);
|
|
144449
|
-
var
|
|
145281
|
+
var import_fs27 = __toESM(require("fs"), 1);
|
|
145282
|
+
var import_path23 = __toESM(require("path"), 1);
|
|
144450
145283
|
var import_node_path17 = require("path");
|
|
144451
|
-
var
|
|
145284
|
+
var import_path24 = __toESM(require("path"), 1);
|
|
144452
145285
|
var import_node_fs12 = __toESM(require("fs"), 1);
|
|
144453
145286
|
var import_node_assert = __toESM(require("assert"), 1);
|
|
144454
145287
|
var import_node_crypto5 = require("crypto");
|
|
144455
145288
|
var import_node_fs13 = __toESM(require("fs"), 1);
|
|
144456
145289
|
var import_node_path18 = __toESM(require("path"), 1);
|
|
144457
|
-
var
|
|
145290
|
+
var import_fs28 = __toESM(require("fs"), 1);
|
|
144458
145291
|
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
144459
145292
|
var import_node_path19 = __toESM(require("path"), 1);
|
|
144460
145293
|
var import_node_fs15 = __toESM(require("fs"), 1);
|
|
@@ -144806,7 +145639,7 @@ var A = class extends import_node_events3.EventEmitter {
|
|
|
144806
145639
|
return Wr;
|
|
144807
145640
|
}
|
|
144808
145641
|
};
|
|
144809
|
-
var Jr =
|
|
145642
|
+
var Jr = import_fs25.default.writev;
|
|
144810
145643
|
var ht = /* @__PURE__ */ Symbol("_autoClose");
|
|
144811
145644
|
var H = /* @__PURE__ */ Symbol("_close");
|
|
144812
145645
|
var te = /* @__PURE__ */ Symbol("_ended");
|
|
@@ -144861,7 +145694,7 @@ var _t = class extends A {
|
|
|
144861
145694
|
throw new TypeError("this is a readable stream");
|
|
144862
145695
|
}
|
|
144863
145696
|
[at]() {
|
|
144864
|
-
|
|
145697
|
+
import_fs25.default.open(this[U2], "r", (t2, e) => this[Ht](t2, e));
|
|
144865
145698
|
}
|
|
144866
145699
|
[Ht](t2, e) {
|
|
144867
145700
|
t2 ? this[Ut](t2) : (this[u2] = e, this.emit("open", e), this[zt]());
|
|
@@ -144874,7 +145707,7 @@ var _t = class extends A {
|
|
|
144874
145707
|
this[j] = true;
|
|
144875
145708
|
let t2 = this[vi]();
|
|
144876
145709
|
if (t2.length === 0) return process.nextTick(() => this[Ii](null, 0, t2));
|
|
144877
|
-
|
|
145710
|
+
import_fs25.default.read(this[u2], t2, 0, t2.length, null, (e, i, r) => this[Ii](e, i, r));
|
|
144878
145711
|
}
|
|
144879
145712
|
}
|
|
144880
145713
|
[Ii](t2, e, i) {
|
|
@@ -144883,7 +145716,7 @@ var _t = class extends A {
|
|
|
144883
145716
|
[H]() {
|
|
144884
145717
|
if (this[ht] && typeof this[u2] == "number") {
|
|
144885
145718
|
let t2 = this[u2];
|
|
144886
|
-
this[u2] = void 0,
|
|
145719
|
+
this[u2] = void 0, import_fs25.default.close(t2, (e) => e ? this.emit("error", e) : this.emit("close"));
|
|
144887
145720
|
}
|
|
144888
145721
|
}
|
|
144889
145722
|
[Ut](t2) {
|
|
@@ -144911,7 +145744,7 @@ var Be = class extends _t {
|
|
|
144911
145744
|
[at]() {
|
|
144912
145745
|
let t2 = true;
|
|
144913
145746
|
try {
|
|
144914
|
-
this[Ht](null,
|
|
145747
|
+
this[Ht](null, import_fs25.default.openSync(this[U2], "r")), t2 = false;
|
|
144915
145748
|
} finally {
|
|
144916
145749
|
t2 && this[H]();
|
|
144917
145750
|
}
|
|
@@ -144922,7 +145755,7 @@ var Be = class extends _t {
|
|
|
144922
145755
|
if (!this[j]) {
|
|
144923
145756
|
this[j] = true;
|
|
144924
145757
|
do {
|
|
144925
|
-
let e = this[vi](), i = e.length === 0 ? 0 :
|
|
145758
|
+
let e = this[vi](), i = e.length === 0 ? 0 : import_fs25.default.readSync(this[u2], e, 0, e.length, null);
|
|
144926
145759
|
if (!this[ki](i, e)) break;
|
|
144927
145760
|
} while (true);
|
|
144928
145761
|
this[j] = false;
|
|
@@ -144935,7 +145768,7 @@ var Be = class extends _t {
|
|
|
144935
145768
|
[H]() {
|
|
144936
145769
|
if (this[ht] && typeof this[u2] == "number") {
|
|
144937
145770
|
let t2 = this[u2];
|
|
144938
|
-
this[u2] = void 0,
|
|
145771
|
+
this[u2] = void 0, import_fs25.default.closeSync(t2), this.emit("close");
|
|
144939
145772
|
}
|
|
144940
145773
|
}
|
|
144941
145774
|
};
|
|
@@ -144977,7 +145810,7 @@ var et = class extends import_events4.default {
|
|
|
144977
145810
|
this[H](), this[gt] = true, this.emit("error", t2);
|
|
144978
145811
|
}
|
|
144979
145812
|
[at]() {
|
|
144980
|
-
|
|
145813
|
+
import_fs25.default.open(this[U2], this[tt], this[ie], (t2, e) => this[Ht](t2, e));
|
|
144981
145814
|
}
|
|
144982
145815
|
[Ht](t2, e) {
|
|
144983
145816
|
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]());
|
|
@@ -144989,7 +145822,7 @@ var et = class extends import_events4.default {
|
|
|
144989
145822
|
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);
|
|
144990
145823
|
}
|
|
144991
145824
|
[ve](t2) {
|
|
144992
|
-
|
|
145825
|
+
import_fs25.default.write(this[u2], t2, 0, t2.length, this[ot], (e, i) => this[Pt](e, i));
|
|
144993
145826
|
}
|
|
144994
145827
|
[Pt](t2, e) {
|
|
144995
145828
|
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"))));
|
|
@@ -145005,7 +145838,7 @@ var et = class extends import_events4.default {
|
|
|
145005
145838
|
[H]() {
|
|
145006
145839
|
if (this[ht] && typeof this[u2] == "number") {
|
|
145007
145840
|
let t2 = this[u2];
|
|
145008
|
-
this[u2] = void 0,
|
|
145841
|
+
this[u2] = void 0, import_fs25.default.close(t2, (e) => e ? this.emit("error", e) : this.emit("close"));
|
|
145009
145842
|
}
|
|
145010
145843
|
}
|
|
145011
145844
|
};
|
|
@@ -145013,24 +145846,24 @@ var Wt = class extends et {
|
|
|
145013
145846
|
[at]() {
|
|
145014
145847
|
let t2;
|
|
145015
145848
|
if (this[Me] && this[tt] === "r+") try {
|
|
145016
|
-
t2 =
|
|
145849
|
+
t2 = import_fs25.default.openSync(this[U2], this[tt], this[ie]);
|
|
145017
145850
|
} catch (e) {
|
|
145018
145851
|
if (e?.code === "ENOENT") return this[tt] = "w", this[at]();
|
|
145019
145852
|
throw e;
|
|
145020
145853
|
}
|
|
145021
|
-
else t2 =
|
|
145854
|
+
else t2 = import_fs25.default.openSync(this[U2], this[tt], this[ie]);
|
|
145022
145855
|
this[Ht](null, t2);
|
|
145023
145856
|
}
|
|
145024
145857
|
[H]() {
|
|
145025
145858
|
if (this[ht] && typeof this[u2] == "number") {
|
|
145026
145859
|
let t2 = this[u2];
|
|
145027
|
-
this[u2] = void 0,
|
|
145860
|
+
this[u2] = void 0, import_fs25.default.closeSync(t2), this.emit("close");
|
|
145028
145861
|
}
|
|
145029
145862
|
}
|
|
145030
145863
|
[ve](t2) {
|
|
145031
145864
|
let e = true;
|
|
145032
145865
|
try {
|
|
145033
|
-
this[Pt](null,
|
|
145866
|
+
this[Pt](null, import_fs25.default.writeSync(this[u2], t2, 0, t2.length, this[ot])), e = false;
|
|
145034
145867
|
} finally {
|
|
145035
145868
|
if (e) try {
|
|
145036
145869
|
this[H]();
|
|
@@ -145813,11 +146646,11 @@ var vn = (s3) => {
|
|
|
145813
146646
|
};
|
|
145814
146647
|
var Qi = (s3, t2) => {
|
|
145815
146648
|
let e = new Map(t2.map((n) => [ut(n), true])), i = s3.filter, r = (n, o = "") => {
|
|
145816
|
-
let h = o || (0,
|
|
146649
|
+
let h = o || (0, import_path22.parse)(n).root || ".", a;
|
|
145817
146650
|
if (n === h) a = false;
|
|
145818
146651
|
else {
|
|
145819
146652
|
let l = e.get(n);
|
|
145820
|
-
a = l !== void 0 ? l : r((0,
|
|
146653
|
+
a = l !== void 0 ? l : r((0, import_path22.dirname)(n), h);
|
|
145821
146654
|
}
|
|
145822
146655
|
return e.set(n, a), a;
|
|
145823
146656
|
};
|
|
@@ -145937,7 +146770,7 @@ var de = class extends A {
|
|
|
145937
146770
|
let [o, h] = ce(this.path);
|
|
145938
146771
|
o && typeof h == "string" && (this.path = h, r = o);
|
|
145939
146772
|
}
|
|
145940
|
-
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 ||
|
|
146773
|
+
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 });
|
|
145941
146774
|
let n = this.statCache.get(this.absolute);
|
|
145942
146775
|
n ? this[si](n) : this[ss]();
|
|
145943
146776
|
}
|
|
@@ -145948,7 +146781,7 @@ var de = class extends A {
|
|
|
145948
146781
|
return t2 === "error" && (this.#t = true), super.emit(t2, ...e);
|
|
145949
146782
|
}
|
|
145950
146783
|
[ss]() {
|
|
145951
|
-
|
|
146784
|
+
import_fs27.default.lstat(this.absolute, (t2, e) => {
|
|
145952
146785
|
if (t2) return this.emit("error", t2);
|
|
145953
146786
|
this[si](e);
|
|
145954
146787
|
});
|
|
@@ -145986,7 +146819,7 @@ var de = class extends A {
|
|
|
145986
146819
|
this.path.slice(-1) !== "/" && (this.path += "/"), this.stat.size = 0, this[fe](), this.end();
|
|
145987
146820
|
}
|
|
145988
146821
|
[is]() {
|
|
145989
|
-
|
|
146822
|
+
import_fs27.default.readlink(this.absolute, (t2, e) => {
|
|
145990
146823
|
if (t2) return this.emit("error", t2);
|
|
145991
146824
|
this[ns](e);
|
|
145992
146825
|
});
|
|
@@ -145996,7 +146829,7 @@ var de = class extends A {
|
|
|
145996
146829
|
}
|
|
145997
146830
|
[sr](t2) {
|
|
145998
146831
|
if (!this.stat) throw new Error("cannot create link entry without stat");
|
|
145999
|
-
this.type = "Link", this.linkpath = f(
|
|
146832
|
+
this.type = "Link", this.linkpath = f(import_path23.default.relative(this.cwd, t2)), this.stat.size = 0, this[fe](), this.end();
|
|
146000
146833
|
}
|
|
146001
146834
|
[er]() {
|
|
146002
146835
|
if (!this.stat) throw new Error("cannot create file entry without stat");
|
|
@@ -146009,7 +146842,7 @@ var de = class extends A {
|
|
|
146009
146842
|
this[os]();
|
|
146010
146843
|
}
|
|
146011
146844
|
[os]() {
|
|
146012
|
-
|
|
146845
|
+
import_fs27.default.open(this.absolute, "r", (t2, e) => {
|
|
146013
146846
|
if (t2) return this.emit("error", t2);
|
|
146014
146847
|
this[hs](e);
|
|
146015
146848
|
});
|
|
@@ -146024,14 +146857,14 @@ var de = class extends A {
|
|
|
146024
146857
|
[ii]() {
|
|
146025
146858
|
let { fd: t2, buf: e, offset: i, length: r, pos: n } = this;
|
|
146026
146859
|
if (t2 === void 0 || e === void 0) throw new Error("cannot read file without first opening");
|
|
146027
|
-
|
|
146860
|
+
import_fs27.default.read(t2, e, i, r, n, (o, h) => {
|
|
146028
146861
|
if (o) return this[pt](() => this.emit("error", o));
|
|
146029
146862
|
this[rs](h);
|
|
146030
146863
|
});
|
|
146031
146864
|
}
|
|
146032
146865
|
[pt](t2 = () => {
|
|
146033
146866
|
}) {
|
|
146034
|
-
this.fd !== void 0 &&
|
|
146867
|
+
this.fd !== void 0 && import_fs27.default.close(this.fd, t2);
|
|
146035
146868
|
}
|
|
146036
146869
|
[rs](t2) {
|
|
146037
146870
|
if (t2 <= 0 && this.remain > 0) {
|
|
@@ -146066,20 +146899,20 @@ var de = class extends A {
|
|
|
146066
146899
|
var ni = class extends de {
|
|
146067
146900
|
sync = true;
|
|
146068
146901
|
[ss]() {
|
|
146069
|
-
this[si](
|
|
146902
|
+
this[si](import_fs27.default.lstatSync(this.absolute));
|
|
146070
146903
|
}
|
|
146071
146904
|
[is]() {
|
|
146072
|
-
this[ns](
|
|
146905
|
+
this[ns](import_fs27.default.readlinkSync(this.absolute));
|
|
146073
146906
|
}
|
|
146074
146907
|
[os]() {
|
|
146075
|
-
this[hs](
|
|
146908
|
+
this[hs](import_fs27.default.openSync(this.absolute, "r"));
|
|
146076
146909
|
}
|
|
146077
146910
|
[ii]() {
|
|
146078
146911
|
let t2 = true;
|
|
146079
146912
|
try {
|
|
146080
146913
|
let { fd: e, buf: i, offset: r, length: n, pos: o } = this;
|
|
146081
146914
|
if (e === void 0 || i === void 0) throw new Error("fd and buf must be set in READ method");
|
|
146082
|
-
let h =
|
|
146915
|
+
let h = import_fs27.default.readSync(e, i, r, n, o);
|
|
146083
146916
|
this[rs](h), t2 = false;
|
|
146084
146917
|
} finally {
|
|
146085
146918
|
if (t2) try {
|
|
@@ -146094,7 +146927,7 @@ var ni = class extends de {
|
|
|
146094
146927
|
}
|
|
146095
146928
|
[pt](t2 = () => {
|
|
146096
146929
|
}) {
|
|
146097
|
-
this.fd !== void 0 &&
|
|
146930
|
+
this.fd !== void 0 && import_fs27.default.closeSync(this.fd), t2();
|
|
146098
146931
|
}
|
|
146099
146932
|
};
|
|
146100
146933
|
var oi = class extends A {
|
|
@@ -146410,7 +147243,7 @@ var wt = class extends A {
|
|
|
146410
147243
|
return typeof t2 == "string" ? this[ci](t2) : this[or](t2), this.flowing;
|
|
146411
147244
|
}
|
|
146412
147245
|
[or](t2) {
|
|
146413
|
-
let e = f(
|
|
147246
|
+
let e = f(import_path24.default.resolve(this.cwd, t2.path));
|
|
146414
147247
|
if (!this.filter(t2.path, t2)) t2.resume();
|
|
146415
147248
|
else {
|
|
146416
147249
|
let i = new pi(t2.path, e);
|
|
@@ -146419,13 +147252,13 @@ var wt = class extends A {
|
|
|
146419
147252
|
this[Ft]();
|
|
146420
147253
|
}
|
|
146421
147254
|
[ci](t2) {
|
|
146422
|
-
let e = f(
|
|
147255
|
+
let e = f(import_path24.default.resolve(this.cwd, t2));
|
|
146423
147256
|
this[W].push(new pi(t2, e)), this[Ft]();
|
|
146424
147257
|
}
|
|
146425
147258
|
[ds](t2) {
|
|
146426
147259
|
t2.pending = true, this[G2] += 1;
|
|
146427
147260
|
let e = this.follow ? "stat" : "lstat";
|
|
146428
|
-
|
|
147261
|
+
import_fs26.default[e](t2.absolute, (i, r) => {
|
|
146429
147262
|
t2.pending = false, this[G2] -= 1, i ? this.emit("error", i) : this[li](t2, r);
|
|
146430
147263
|
});
|
|
146431
147264
|
}
|
|
@@ -146439,7 +147272,7 @@ var wt = class extends A {
|
|
|
146439
147272
|
this[Ft]();
|
|
146440
147273
|
}
|
|
146441
147274
|
[ms](t2) {
|
|
146442
|
-
t2.pending = true, this[G2] += 1,
|
|
147275
|
+
t2.pending = true, this[G2] += 1, import_fs26.default.readdir(t2.absolute, (e, i) => {
|
|
146443
147276
|
if (t2.pending = false, this[G2] -= 1, e) return this.emit("error", e);
|
|
146444
147277
|
this[fi](t2, i);
|
|
146445
147278
|
});
|
|
@@ -146540,10 +147373,10 @@ var kt = class extends wt {
|
|
|
146540
147373
|
}
|
|
146541
147374
|
[ds](t2) {
|
|
146542
147375
|
let e = this.follow ? "statSync" : "lstatSync";
|
|
146543
|
-
this[li](t2,
|
|
147376
|
+
this[li](t2, import_fs26.default[e](t2.absolute));
|
|
146544
147377
|
}
|
|
146545
147378
|
[ms](t2) {
|
|
146546
|
-
this[fi](t2,
|
|
147379
|
+
this[fi](t2, import_fs26.default.readdirSync(t2.absolute));
|
|
146547
147380
|
}
|
|
146548
147381
|
[di](t2) {
|
|
146549
147382
|
let e = t2.entry, i = this.zip;
|
|
@@ -146594,8 +147427,8 @@ var Qn = K2(Vn, $n, Xn, qn, (s3, t2) => {
|
|
|
146594
147427
|
});
|
|
146595
147428
|
var Jn = process.env.__FAKE_PLATFORM__ || process.platform;
|
|
146596
147429
|
var Er = Jn === "win32";
|
|
146597
|
-
var { O_CREAT: wr, O_NOFOLLOW: mr, O_TRUNC: Sr, O_WRONLY: yr } =
|
|
146598
|
-
var Rr = Number(process.env.__FAKE_FS_O_FILENAME__) ||
|
|
147430
|
+
var { O_CREAT: wr, O_NOFOLLOW: mr, O_TRUNC: Sr, O_WRONLY: yr } = import_fs28.default.constants;
|
|
147431
|
+
var Rr = Number(process.env.__FAKE_FS_O_FILENAME__) || import_fs28.default.constants.UV_FS_O_FILEMAP || 0;
|
|
146599
147432
|
var jn = Er && !!Rr;
|
|
146600
147433
|
var to = 512 * 1024;
|
|
146601
147434
|
var eo = Rr | Sr | wr | yr;
|
|
@@ -147719,6 +148552,13 @@ program2.command("serve").description("Start the streamer server").option("-p, -
|
|
|
147719
148552
|
"Run in multi-agent mode (PTY mode unreachable in this process)",
|
|
147720
148553
|
false
|
|
147721
148554
|
).action(async (opts) => {
|
|
148555
|
+
try {
|
|
148556
|
+
const { checkSqliteAbi: checkSqliteAbi2 } = await Promise.resolve().then(() => (init_check_sqlite_abi(), check_sqlite_abi_exports));
|
|
148557
|
+
checkSqliteAbi2();
|
|
148558
|
+
} catch (err) {
|
|
148559
|
+
log7.error(err instanceof Error ? err.message : String(err), void 0, "console");
|
|
148560
|
+
process.exit(1);
|
|
148561
|
+
}
|
|
147722
148562
|
if (opts.multiAgentFlow) {
|
|
147723
148563
|
process.env.MULTI_AGENT_FLOW = "true";
|
|
147724
148564
|
}
|
|
@@ -147816,12 +148656,12 @@ program2.command("cache").description("Manage the local SQLite conversation cach
|
|
|
147816
148656
|
"Cache directory (default: ~/.threadbase/cache)",
|
|
147817
148657
|
`${process.env.HOME}/.threadbase/cache`
|
|
147818
148658
|
).action((opts) => {
|
|
147819
|
-
const { rmSync: rmSync6, existsSync:
|
|
148659
|
+
const { rmSync: rmSync6, existsSync: existsSync12 } = require("fs");
|
|
147820
148660
|
const { join: join26 } = require("path");
|
|
147821
148661
|
const dbPath = join26(opts.cacheDir, "cache.db");
|
|
147822
148662
|
for (const suffix of ["", "-shm", "-wal"]) {
|
|
147823
148663
|
const f2 = dbPath + suffix;
|
|
147824
|
-
if (
|
|
148664
|
+
if (existsSync12(f2)) {
|
|
147825
148665
|
rmSync6(f2);
|
|
147826
148666
|
log7.info(`Deleted ${f2}`, { path: f2 }, "console");
|
|
147827
148667
|
}
|