@threadbase-sh/streamer 1.27.2 → 1.28.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 +636 -156
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +500 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +118 -1
- package/dist/index.d.ts +118 -1
- package/dist/index.js +481 -15
- package/dist/index.js.map +1 -1
- package/dist/migrations/009_create_offset_index.sql +28 -0
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -30931,7 +30931,7 @@ var require_websocket = __commonJS({
|
|
|
30931
30931
|
var http = require("http");
|
|
30932
30932
|
var net = require("net");
|
|
30933
30933
|
var tls = require("tls");
|
|
30934
|
-
var { randomBytes: randomBytes4, createHash:
|
|
30934
|
+
var { randomBytes: randomBytes4, createHash: createHash5 } = require("crypto");
|
|
30935
30935
|
var { Duplex, Readable: Readable3 } = require("stream");
|
|
30936
30936
|
var { URL: URL2 } = require("url");
|
|
30937
30937
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -31599,7 +31599,7 @@ var require_websocket = __commonJS({
|
|
|
31599
31599
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
31600
31600
|
return;
|
|
31601
31601
|
}
|
|
31602
|
-
const digest =
|
|
31602
|
+
const digest = createHash5("sha1").update(key + GUID).digest("base64");
|
|
31603
31603
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
31604
31604
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
31605
31605
|
return;
|
|
@@ -31968,7 +31968,7 @@ var require_websocket_server = __commonJS({
|
|
|
31968
31968
|
var EventEmitter6 = require("events");
|
|
31969
31969
|
var http = require("http");
|
|
31970
31970
|
var { Duplex } = require("stream");
|
|
31971
|
-
var { createHash:
|
|
31971
|
+
var { createHash: createHash5 } = require("crypto");
|
|
31972
31972
|
var extension2 = require_extension();
|
|
31973
31973
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
31974
31974
|
var subprotocol2 = require_subprotocol();
|
|
@@ -32275,7 +32275,7 @@ var require_websocket_server = __commonJS({
|
|
|
32275
32275
|
);
|
|
32276
32276
|
}
|
|
32277
32277
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
32278
|
-
const digest =
|
|
32278
|
+
const digest = createHash5("sha1").update(key + GUID).digest("base64");
|
|
32279
32279
|
const headers = [
|
|
32280
32280
|
"HTTP/1.1 101 Switching Protocols",
|
|
32281
32281
|
"Upgrade: websocket",
|
|
@@ -100397,11 +100397,11 @@ var require_out = __commonJS({
|
|
|
100397
100397
|
async.read(path2, getSettings(optionsOrSettingsOrCallback), callback);
|
|
100398
100398
|
}
|
|
100399
100399
|
exports2.stat = stat10;
|
|
100400
|
-
function
|
|
100400
|
+
function statSync10(path2, optionsOrSettings) {
|
|
100401
100401
|
const settings = getSettings(optionsOrSettings);
|
|
100402
100402
|
return sync.read(path2, settings);
|
|
100403
100403
|
}
|
|
100404
|
-
exports2.statSync =
|
|
100404
|
+
exports2.statSync = statSync10;
|
|
100405
100405
|
function getSettings(settingsOrOptions = {}) {
|
|
100406
100406
|
if (settingsOrOptions instanceof settings_1.default) {
|
|
100407
100407
|
return settingsOrOptions;
|
|
@@ -123750,14 +123750,14 @@ __export(prompt_exports, {
|
|
|
123750
123750
|
interactivePermissionModePrompt: () => interactivePermissionModePrompt,
|
|
123751
123751
|
interactivePrompt: () => interactivePrompt
|
|
123752
123752
|
});
|
|
123753
|
-
var import_node_process2,
|
|
123753
|
+
var import_node_process2, import_promises22, interactivePrompt, interactivePermissionModePrompt;
|
|
123754
123754
|
var init_prompt = __esm({
|
|
123755
123755
|
"src/lifecycle/prompt.ts"() {
|
|
123756
123756
|
"use strict";
|
|
123757
123757
|
import_node_process2 = require("process");
|
|
123758
|
-
|
|
123758
|
+
import_promises22 = require("readline/promises");
|
|
123759
123759
|
interactivePrompt = async ({ prodPort, suggestedAltPort, prodActive }) => {
|
|
123760
|
-
const rl = (0,
|
|
123760
|
+
const rl = (0, import_promises22.createInterface)({ input: import_node_process2.stdin, output: import_node_process2.stdout });
|
|
123761
123761
|
try {
|
|
123762
123762
|
if (!prodActive) {
|
|
123763
123763
|
import_node_process2.stdout.write(
|
|
@@ -123792,7 +123792,7 @@ The supervised prod streamer is already holding port ${prodPort}.
|
|
|
123792
123792
|
}
|
|
123793
123793
|
};
|
|
123794
123794
|
interactivePermissionModePrompt = async () => {
|
|
123795
|
-
const rl = (0,
|
|
123795
|
+
const rl = (0, import_promises22.createInterface)({ input: import_node_process2.stdin, output: import_node_process2.stdout });
|
|
123796
123796
|
try {
|
|
123797
123797
|
import_node_process2.stdout.write(
|
|
123798
123798
|
"\nWhich permission mode should spawned Claude Code sessions use?\n [a] acceptEdits \u2014 auto-approve file edits, still prompt for shell commands (default)\n [m] manual \u2014 prompt for every edit and command\n"
|
|
@@ -128554,9 +128554,10 @@ var import_promises8 = require("timers/promises");
|
|
|
128554
128554
|
var import_crypto2 = require("crypto");
|
|
128555
128555
|
var import_fs11 = require("fs");
|
|
128556
128556
|
var import_fs12 = require("fs");
|
|
128557
|
+
var import_fs13 = require("fs");
|
|
128557
128558
|
var import_readline3 = require("readline");
|
|
128558
128559
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
128559
|
-
var
|
|
128560
|
+
var import_fs14 = require("fs");
|
|
128560
128561
|
var import_path9 = require("path");
|
|
128561
128562
|
var import_promises9 = require("fs/promises");
|
|
128562
128563
|
var import_path10 = require("path");
|
|
@@ -130939,6 +130940,17 @@ function reduceConvLine(state, entry) {
|
|
|
130939
130940
|
attachment: entry.attachment !== void 0 ? entry.attachment : void 0
|
|
130940
130941
|
};
|
|
130941
130942
|
}
|
|
130943
|
+
function parseJsonlLine(line, state = initialConvState()) {
|
|
130944
|
+
const text = line.trimEnd();
|
|
130945
|
+
if (text.trim().length === 0) return null;
|
|
130946
|
+
let entry;
|
|
130947
|
+
try {
|
|
130948
|
+
entry = JSON.parse(text);
|
|
130949
|
+
} catch {
|
|
130950
|
+
return null;
|
|
130951
|
+
}
|
|
130952
|
+
return reduceConvLine(state, entry);
|
|
130953
|
+
}
|
|
130942
130954
|
function applyTeamInfo(messages, state) {
|
|
130943
130955
|
if (Object.keys(state.teamInfo).length === 0) return;
|
|
130944
130956
|
for (const m2 of messages) {
|
|
@@ -131600,7 +131612,7 @@ async function parseMetaWithProvider(provider, filePath, account, tier) {
|
|
|
131600
131612
|
const log8 = getLogger2();
|
|
131601
131613
|
const acc = provider.createEmptyAccumulator();
|
|
131602
131614
|
const rl = (0, import_readline3.createInterface)({
|
|
131603
|
-
input: (0,
|
|
131615
|
+
input: (0, import_fs13.createReadStream)(filePath),
|
|
131604
131616
|
crlfDelay: Infinity
|
|
131605
131617
|
});
|
|
131606
131618
|
try {
|
|
@@ -131823,7 +131835,7 @@ function hasColumn(db, table, column) {
|
|
|
131823
131835
|
}
|
|
131824
131836
|
function openDatabase(dbPath) {
|
|
131825
131837
|
if (dbPath !== ":memory:") {
|
|
131826
|
-
(0,
|
|
131838
|
+
(0, import_fs14.mkdirSync)((0, import_path9.dirname)(dbPath), { recursive: true });
|
|
131827
131839
|
}
|
|
131828
131840
|
const db = new import_better_sqlite3.default(dbPath);
|
|
131829
131841
|
db.pragma("journal_mode = WAL");
|
|
@@ -132623,7 +132635,13 @@ var PersistentEngine = class {
|
|
|
132623
132635
|
await this.ensureCheckpoints(filePath, total);
|
|
132624
132636
|
const beforeIndex = options.beforeIndex ?? total;
|
|
132625
132637
|
const fromIndex = Math.max(0, beforeIndex - options.limit);
|
|
132626
|
-
|
|
132638
|
+
let floor = this.checkpoints.floor(filePath, fromIndex);
|
|
132639
|
+
if (floor) {
|
|
132640
|
+
try {
|
|
132641
|
+
if (floor.byteOffset > (0, import_fs12.statSync)(filePath).size) floor = null;
|
|
132642
|
+
} catch {
|
|
132643
|
+
}
|
|
132644
|
+
}
|
|
132627
132645
|
return readPage(filePath, total, options, floor);
|
|
132628
132646
|
}
|
|
132629
132647
|
// Build or extend the checkpoint chain so it covers `total` messages. Cold
|
|
@@ -133512,8 +133530,8 @@ async function search(query, options, scanner) {
|
|
|
133512
133530
|
|
|
133513
133531
|
// src/server.ts
|
|
133514
133532
|
var import_events3 = require("events");
|
|
133515
|
-
var
|
|
133516
|
-
var
|
|
133533
|
+
var import_fs25 = require("fs");
|
|
133534
|
+
var import_promises19 = require("fs/promises");
|
|
133517
133535
|
var import_http = require("http");
|
|
133518
133536
|
var import_os10 = require("os");
|
|
133519
133537
|
var import_path22 = require("path");
|
|
@@ -136658,11 +136676,13 @@ async function createDirectory(parentAbsolutePath, name) {
|
|
|
136658
136676
|
|
|
136659
136677
|
// src/conversation-cache.ts
|
|
136660
136678
|
var import_better_sqlite32 = __toESM(require("better-sqlite3"), 1);
|
|
136661
|
-
var
|
|
136679
|
+
var import_fs17 = require("fs");
|
|
136680
|
+
var import_promises12 = require("fs/promises");
|
|
136662
136681
|
var import_path13 = require("path");
|
|
136682
|
+
var import_promises13 = require("timers/promises");
|
|
136663
136683
|
|
|
136664
136684
|
// src/db/sqlite-migrate.ts
|
|
136665
|
-
var
|
|
136685
|
+
var import_fs15 = require("fs");
|
|
136666
136686
|
var import_path12 = require("path");
|
|
136667
136687
|
var import_url6 = require("url");
|
|
136668
136688
|
var import_meta = {};
|
|
@@ -136681,7 +136701,7 @@ CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
|
136681
136701
|
function runSqliteMigrations(db, migrationsDir) {
|
|
136682
136702
|
db.exec(SCHEMA_MIGRATIONS_SQL);
|
|
136683
136703
|
const dir = migrationsDir ?? (0, import_path12.join)(getMigrationsDir(), "migrations");
|
|
136684
|
-
const files = (0,
|
|
136704
|
+
const files = (0, import_fs15.readdirSync)(dir).filter((f2) => f2.endsWith(".sql")).sort();
|
|
136685
136705
|
const appliedRows = db.prepare("SELECT id FROM schema_migrations").all();
|
|
136686
136706
|
const appliedSet = new Set(appliedRows.map((r) => r.id));
|
|
136687
136707
|
const recordApplied = db.prepare("INSERT INTO schema_migrations (id, applied_at) VALUES (?, ?)");
|
|
@@ -136692,7 +136712,7 @@ function runSqliteMigrations(db, migrationsDir) {
|
|
|
136692
136712
|
skipped.push(file2);
|
|
136693
136713
|
continue;
|
|
136694
136714
|
}
|
|
136695
|
-
const sql = (0,
|
|
136715
|
+
const sql = (0, import_fs15.readFileSync)((0, import_path12.join)(dir, file2), "utf-8");
|
|
136696
136716
|
const tx = db.transaction(() => {
|
|
136697
136717
|
db.exec(sql);
|
|
136698
136718
|
recordApplied.run(file2, (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -136717,7 +136737,7 @@ function isProviderResumable(_provider, availabilityResumable) {
|
|
|
136717
136737
|
}
|
|
136718
136738
|
|
|
136719
136739
|
// src/services/conversations/isAgentConversation.ts
|
|
136720
|
-
var
|
|
136740
|
+
var import_fs16 = require("fs");
|
|
136721
136741
|
var DEFAULT_AGENT_ENTRYPOINTS = /* @__PURE__ */ new Set(["sdk-cli", "claude-vscode"]);
|
|
136722
136742
|
var CHUNK_BYTES = 64 * 1024;
|
|
136723
136743
|
var ENTRYPOINT_PROBE = `"entrypoint":`;
|
|
@@ -136739,12 +136759,12 @@ function isAgentFile(filePath, entrypoints = DEFAULT_AGENT_ENTRYPOINTS) {
|
|
|
136739
136759
|
if (cached3 !== void 0) return cached3;
|
|
136740
136760
|
let fd;
|
|
136741
136761
|
try {
|
|
136742
|
-
fd = (0,
|
|
136762
|
+
fd = (0, import_fs16.openSync)(filePath, "r");
|
|
136743
136763
|
} catch {
|
|
136744
136764
|
return false;
|
|
136745
136765
|
}
|
|
136746
136766
|
try {
|
|
136747
|
-
const fileSize = (0,
|
|
136767
|
+
const fileSize = (0, import_fs16.statSync)(filePath).size;
|
|
136748
136768
|
if (fileSize === 0) {
|
|
136749
136769
|
fileDecisionCache.set(key, false);
|
|
136750
136770
|
return false;
|
|
@@ -136755,7 +136775,7 @@ function isAgentFile(filePath, entrypoints = DEFAULT_AGENT_ENTRYPOINTS) {
|
|
|
136755
136775
|
let carry = "";
|
|
136756
136776
|
while (offset < fileSize) {
|
|
136757
136777
|
const toRead = Math.min(CHUNK_BYTES, fileSize - offset);
|
|
136758
|
-
const got = (0,
|
|
136778
|
+
const got = (0, import_fs16.readSync)(fd, buf, 0, toRead, offset);
|
|
136759
136779
|
if (got <= 0) break;
|
|
136760
136780
|
const chunk = carry + buf.toString("utf8", 0, got);
|
|
136761
136781
|
for (const marker of markers) {
|
|
@@ -136776,7 +136796,7 @@ function isAgentFile(filePath, entrypoints = DEFAULT_AGENT_ENTRYPOINTS) {
|
|
|
136776
136796
|
} catch {
|
|
136777
136797
|
return false;
|
|
136778
136798
|
} finally {
|
|
136779
|
-
(0,
|
|
136799
|
+
(0, import_fs16.closeSync)(fd);
|
|
136780
136800
|
}
|
|
136781
136801
|
}
|
|
136782
136802
|
function parseAgentEntrypointsEnv(raw2) {
|
|
@@ -136785,6 +136805,33 @@ function parseAgentEntrypointsEnv(raw2) {
|
|
|
136785
136805
|
return new Set(parts);
|
|
136786
136806
|
}
|
|
136787
136807
|
|
|
136808
|
+
// src/utils/fileIdentity.ts
|
|
136809
|
+
var import_crypto5 = require("crypto");
|
|
136810
|
+
function fileIdentity(stat10, headBytes) {
|
|
136811
|
+
if (stat10.ino && stat10.ino > 0) return `inode:${stat10.dev}:${stat10.ino}`;
|
|
136812
|
+
const head = headBytes ?? Buffer.alloc(0);
|
|
136813
|
+
return `fp:${(0, import_crypto5.createHash)("sha1").update(head).digest("hex")}`;
|
|
136814
|
+
}
|
|
136815
|
+
function splitCompleteLines(buf, baseOffset) {
|
|
136816
|
+
const spans = [];
|
|
136817
|
+
let lineStart = 0;
|
|
136818
|
+
let consumed = 0;
|
|
136819
|
+
for (let i = 0; i < buf.length; i++) {
|
|
136820
|
+
if (buf[i] !== 10) continue;
|
|
136821
|
+
const lineLen = i - lineStart;
|
|
136822
|
+
if (lineLen > 0) {
|
|
136823
|
+
spans.push({
|
|
136824
|
+
byteOffset: baseOffset + lineStart,
|
|
136825
|
+
byteLength: lineLen,
|
|
136826
|
+
text: buf.toString("utf-8", lineStart, i)
|
|
136827
|
+
});
|
|
136828
|
+
}
|
|
136829
|
+
lineStart = i + 1;
|
|
136830
|
+
consumed = lineStart;
|
|
136831
|
+
}
|
|
136832
|
+
return { spans, consumed };
|
|
136833
|
+
}
|
|
136834
|
+
|
|
136788
136835
|
// src/conversation-cache.ts
|
|
136789
136836
|
function shortProjectName(fullPath) {
|
|
136790
136837
|
const parts = fullPath.split(/[/\\]/).filter(Boolean);
|
|
@@ -136833,6 +136880,14 @@ var ConversationCache = class _ConversationCache {
|
|
|
136833
136880
|
tailSize;
|
|
136834
136881
|
fileIndex = /* @__PURE__ */ new Map();
|
|
136835
136882
|
fileIndexLoaded = false;
|
|
136883
|
+
// Per-file scanner parse state for the incremental offset-index writer. The
|
|
136884
|
+
// reducer is stateful across lines (pending tool_uses, latest timestamp), so
|
|
136885
|
+
// it must persist between watcher reads of the same file. Cleared on
|
|
136886
|
+
// truncation/backfill.
|
|
136887
|
+
indexParseState = /* @__PURE__ */ new Map();
|
|
136888
|
+
// Single-flight guard for backfillIndex — concurrent detail requests for the
|
|
136889
|
+
// same cold file await one walk, not N. Entry dropped on settle.
|
|
136890
|
+
backfillInFlight = /* @__PURE__ */ new Map();
|
|
136836
136891
|
// Monotonically increasing counter for tail updated_at — guarantees strict
|
|
136837
136892
|
// ordering even when multiple updateFromLine() calls land within the same ms.
|
|
136838
136893
|
tailSeq = Date.now();
|
|
@@ -136996,6 +137051,42 @@ var ConversationCache = class _ConversationCache {
|
|
|
136996
137051
|
GROUP BY project_path
|
|
136997
137052
|
ORDER BY cnt DESC
|
|
136998
137053
|
LIMIT ?`
|
|
137054
|
+
),
|
|
137055
|
+
getFileState: db.prepare("SELECT * FROM conversation_file_state WHERE path = ?"),
|
|
137056
|
+
upsertFileState: db.prepare(
|
|
137057
|
+
`INSERT INTO conversation_file_state
|
|
137058
|
+
(path, identity, size, mtime_ms, byte_offset, last_message_index)
|
|
137059
|
+
VALUES (@path, @identity, @size, @mtime_ms, @byte_offset, @last_message_index)
|
|
137060
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
137061
|
+
identity = excluded.identity,
|
|
137062
|
+
size = excluded.size,
|
|
137063
|
+
mtime_ms = excluded.mtime_ms,
|
|
137064
|
+
byte_offset = excluded.byte_offset,
|
|
137065
|
+
last_message_index = excluded.last_message_index`
|
|
137066
|
+
),
|
|
137067
|
+
deleteFileState: db.prepare("DELETE FROM conversation_file_state WHERE path = ?"),
|
|
137068
|
+
deleteMessageIndex: db.prepare(
|
|
137069
|
+
"DELETE FROM conversation_message_index WHERE conversation_id = ?"
|
|
137070
|
+
),
|
|
137071
|
+
insertMessageIndexRow: db.prepare(
|
|
137072
|
+
`INSERT INTO conversation_message_index
|
|
137073
|
+
(conversation_id, message_index, byte_offset, byte_length, uuid, role, ts)
|
|
137074
|
+
VALUES (@conversation_id, @message_index, @byte_offset, @byte_length, @uuid, @role, @ts)
|
|
137075
|
+
ON CONFLICT(conversation_id, message_index) DO UPDATE SET
|
|
137076
|
+
byte_offset = excluded.byte_offset,
|
|
137077
|
+
byte_length = excluded.byte_length,
|
|
137078
|
+
uuid = excluded.uuid,
|
|
137079
|
+
role = excluded.role,
|
|
137080
|
+
ts = excluded.ts`
|
|
137081
|
+
),
|
|
137082
|
+
getMessageIndexWindow: db.prepare(
|
|
137083
|
+
`SELECT message_index, byte_offset, byte_length, uuid, role, ts
|
|
137084
|
+
FROM conversation_message_index
|
|
137085
|
+
WHERE conversation_id = ? AND message_index >= ? AND message_index < ?
|
|
137086
|
+
ORDER BY message_index ASC`
|
|
137087
|
+
),
|
|
137088
|
+
getIndexedMessageCount: db.prepare(
|
|
137089
|
+
"SELECT COUNT(*) as cnt FROM conversation_message_index WHERE conversation_id = ?"
|
|
136999
137090
|
)
|
|
137000
137091
|
};
|
|
137001
137092
|
}
|
|
@@ -137006,6 +137097,258 @@ var ConversationCache = class _ConversationCache {
|
|
|
137006
137097
|
getDatabase() {
|
|
137007
137098
|
return this.db;
|
|
137008
137099
|
}
|
|
137100
|
+
// ── Offset index (design 1b) ────────────────────────────────────────────
|
|
137101
|
+
// conversation_file_state + conversation_message_index back the windowed
|
|
137102
|
+
// detail read path (SQL window select + pread of byte ranges). All methods
|
|
137103
|
+
// are thin wrappers over the prepared statements above.
|
|
137104
|
+
getFileState(path2) {
|
|
137105
|
+
return this.stmts.getFileState.get(path2) ?? null;
|
|
137106
|
+
}
|
|
137107
|
+
upsertFileState(row) {
|
|
137108
|
+
this.stmts.upsertFileState.run(row);
|
|
137109
|
+
}
|
|
137110
|
+
/** Drop a file's index rows + file_state (truncation / identity change). */
|
|
137111
|
+
deleteFileIndex(path2, conversationId) {
|
|
137112
|
+
const tx = this.db.transaction(() => {
|
|
137113
|
+
this.stmts.deleteMessageIndex.run(conversationId);
|
|
137114
|
+
this.stmts.deleteFileState.run(path2);
|
|
137115
|
+
});
|
|
137116
|
+
tx();
|
|
137117
|
+
this.indexParseState.delete(path2);
|
|
137118
|
+
}
|
|
137119
|
+
/** Append/replace index rows in one transaction. */
|
|
137120
|
+
appendMessageIndexRows(rows) {
|
|
137121
|
+
const tx = this.db.transaction((batch) => {
|
|
137122
|
+
for (const r of batch) this.stmts.insertMessageIndexRow.run(r);
|
|
137123
|
+
});
|
|
137124
|
+
tx(rows);
|
|
137125
|
+
}
|
|
137126
|
+
/** Rows for message_index in [fromIndex, toIndex), ordered ascending. */
|
|
137127
|
+
getMessageIndexWindow(conversationId, fromIndex, toIndex) {
|
|
137128
|
+
return this.stmts.getMessageIndexWindow.all(
|
|
137129
|
+
conversationId,
|
|
137130
|
+
fromIndex,
|
|
137131
|
+
toIndex
|
|
137132
|
+
);
|
|
137133
|
+
}
|
|
137134
|
+
getIndexedMessageCount(conversationId) {
|
|
137135
|
+
return this.stmts.getIndexedMessageCount.get(conversationId).cnt;
|
|
137136
|
+
}
|
|
137137
|
+
/**
|
|
137138
|
+
* Conversation id for a JSONL path — the filename stem (matches the pseudo-id
|
|
137139
|
+
* updateFromLine derives and the uuid the detail read path resolves). The
|
|
137140
|
+
* offset index keys on this so the window select and the cursor agree.
|
|
137141
|
+
*/
|
|
137142
|
+
static conversationIdForFile(filePath) {
|
|
137143
|
+
return filePath.split(/[/\\]/).pop()?.replace(/\.jsonl$/, "") ?? filePath;
|
|
137144
|
+
}
|
|
137145
|
+
/**
|
|
137146
|
+
* Incremental offset-index writer: extend the index for a burst of appended
|
|
137147
|
+
* lines (one watcher read) using their byte spans. Each line is classified
|
|
137148
|
+
* with the scanner's parseJsonlLine (a running per-file reducer state), so the
|
|
137149
|
+
* message ordering can never drift from the scanner's. Message lines get an
|
|
137150
|
+
* index row at the next message_index; non-message lines (summary/sidecar)
|
|
137151
|
+
* get no row but still advance byte_offset. file_state is updated to the end
|
|
137152
|
+
* of the last consumed span.
|
|
137153
|
+
*
|
|
137154
|
+
* Requires an up-to-date `stat` (identity/size/mtime) for the file so the read
|
|
137155
|
+
* path can detect truncation/replacement.
|
|
137156
|
+
*
|
|
137157
|
+
* `readFrom` is the absolute byte offset the watcher read started at, and
|
|
137158
|
+
* `endOffset` is where it ended (readFrom + consumed, i.e. the watcher's new
|
|
137159
|
+
* entry.offset). CONTIGUITY GUARD: the read must begin exactly where the index
|
|
137160
|
+
* left off (`readFrom === existing.byte_offset`, or 0 with no state). If it
|
|
137161
|
+
* doesn't — the watcher attached at EOF after the server was down, or an
|
|
137162
|
+
* append raced an in-flight backfill — extending would assign wrong
|
|
137163
|
+
* message_index values over a hole. In that case this writes nothing and
|
|
137164
|
+
* returns null so the caller drops the index and backfills.
|
|
137165
|
+
*
|
|
137166
|
+
* On success returns the message_index assigned to each input span (null for a
|
|
137167
|
+
* non-message line) so the caller can stamp WS `seq`. Empty array when spans
|
|
137168
|
+
* is empty. `endOffset` is stored verbatim as byte_offset so the watcher's
|
|
137169
|
+
* offset and file_state.byte_offset are the same number by construction.
|
|
137170
|
+
*/
|
|
137171
|
+
extendMessageIndex(filePath, spans, stat10, readFrom, endOffset) {
|
|
137172
|
+
const existing = this.getFileState(filePath);
|
|
137173
|
+
const expectedStart = existing?.byte_offset ?? 0;
|
|
137174
|
+
if (readFrom !== expectedStart) return null;
|
|
137175
|
+
if (spans.length === 0) return [];
|
|
137176
|
+
const convId = _ConversationCache.conversationIdForFile(filePath);
|
|
137177
|
+
let state = this.indexParseState.get(filePath);
|
|
137178
|
+
if (!state) {
|
|
137179
|
+
state = initialConvState();
|
|
137180
|
+
this.indexParseState.set(filePath, state);
|
|
137181
|
+
}
|
|
137182
|
+
let nextIndex = existing ? existing.last_message_index + 1 : 0;
|
|
137183
|
+
const rows = [];
|
|
137184
|
+
const seqs = [];
|
|
137185
|
+
for (const span of spans) {
|
|
137186
|
+
const msg = parseJsonlLine(span.text, state);
|
|
137187
|
+
if (!msg) {
|
|
137188
|
+
seqs.push(null);
|
|
137189
|
+
continue;
|
|
137190
|
+
}
|
|
137191
|
+
rows.push({
|
|
137192
|
+
conversation_id: convId,
|
|
137193
|
+
message_index: nextIndex,
|
|
137194
|
+
byte_offset: span.byteOffset,
|
|
137195
|
+
byte_length: span.byteLength,
|
|
137196
|
+
uuid: msg.uuid ?? null,
|
|
137197
|
+
role: msg.role ?? null,
|
|
137198
|
+
ts: msg.timestamp ? Date.parse(msg.timestamp) || null : null
|
|
137199
|
+
});
|
|
137200
|
+
seqs.push(nextIndex);
|
|
137201
|
+
nextIndex++;
|
|
137202
|
+
}
|
|
137203
|
+
const tx = this.db.transaction(() => {
|
|
137204
|
+
for (const r of rows) this.stmts.insertMessageIndexRow.run(r);
|
|
137205
|
+
this.stmts.upsertFileState.run({
|
|
137206
|
+
path: filePath,
|
|
137207
|
+
identity: fileIdentity(stat10),
|
|
137208
|
+
size: stat10.size,
|
|
137209
|
+
mtime_ms: Math.round(stat10.mtimeMs),
|
|
137210
|
+
// Store the watcher's end offset verbatim — same number as entry.offset,
|
|
137211
|
+
// so the next read's contiguity check compares like-for-like (never
|
|
137212
|
+
// false-positive on a read that ended in trailing empty lines).
|
|
137213
|
+
byte_offset: endOffset,
|
|
137214
|
+
last_message_index: nextIndex - 1
|
|
137215
|
+
});
|
|
137216
|
+
});
|
|
137217
|
+
tx();
|
|
137218
|
+
return seqs;
|
|
137219
|
+
}
|
|
137220
|
+
clearIndexParseState(filePath) {
|
|
137221
|
+
this.indexParseState.delete(filePath);
|
|
137222
|
+
}
|
|
137223
|
+
/**
|
|
137224
|
+
* On-demand full backfill of the offset index for a file with no/stale
|
|
137225
|
+
* file_state (cold conversation, or after a truncation/replacement). Rebuilds
|
|
137226
|
+
* from byte 0: drops any existing rows, walks the whole file in chunks with a
|
|
137227
|
+
* running parse state, yields to the event loop every ~1000 lines so a large
|
|
137228
|
+
* file never blocks, and writes index rows + file_state.
|
|
137229
|
+
*
|
|
137230
|
+
* Single-flighted per path: concurrent callers await the same walk. The
|
|
137231
|
+
* triggering detail request is served by the scanner fallback while this runs.
|
|
137232
|
+
*/
|
|
137233
|
+
backfillIndex(filePath) {
|
|
137234
|
+
const inFlight = this.backfillInFlight.get(filePath);
|
|
137235
|
+
if (inFlight) return inFlight;
|
|
137236
|
+
const walk = this.runBackfill(filePath).finally(() => {
|
|
137237
|
+
this.backfillInFlight.delete(filePath);
|
|
137238
|
+
});
|
|
137239
|
+
this.backfillInFlight.set(filePath, walk);
|
|
137240
|
+
return walk;
|
|
137241
|
+
}
|
|
137242
|
+
async runBackfill(filePath) {
|
|
137243
|
+
const convId = _ConversationCache.conversationIdForFile(filePath);
|
|
137244
|
+
this.deleteFileIndex(filePath, convId);
|
|
137245
|
+
this.indexParseState.delete(filePath);
|
|
137246
|
+
const CHUNK = 256 * 1024;
|
|
137247
|
+
const YIELD_EVERY = 1e3;
|
|
137248
|
+
const state = initialConvState();
|
|
137249
|
+
const fh = await (0, import_promises12.open)(filePath, "r");
|
|
137250
|
+
let fileOffset = 0;
|
|
137251
|
+
let carry = Buffer.alloc(0);
|
|
137252
|
+
let nextIndex = 0;
|
|
137253
|
+
let linesSinceYield = 0;
|
|
137254
|
+
let lastConsumedEnd = 0;
|
|
137255
|
+
let stat10;
|
|
137256
|
+
try {
|
|
137257
|
+
stat10 = await fh.stat();
|
|
137258
|
+
const buf = Buffer.alloc(CHUNK);
|
|
137259
|
+
for (; ; ) {
|
|
137260
|
+
const { bytesRead } = await fh.read(buf, 0, CHUNK, null);
|
|
137261
|
+
if (bytesRead === 0) break;
|
|
137262
|
+
const combined = carry.length > 0 ? Buffer.concat([carry, buf.subarray(0, bytesRead)]) : buf.subarray(0, bytesRead);
|
|
137263
|
+
const { spans, consumed } = splitCompleteLines(combined, fileOffset);
|
|
137264
|
+
const rows = [];
|
|
137265
|
+
for (const span of spans) {
|
|
137266
|
+
const msg = parseJsonlLine(span.text, state);
|
|
137267
|
+
linesSinceYield++;
|
|
137268
|
+
if (msg) {
|
|
137269
|
+
rows.push({
|
|
137270
|
+
conversation_id: convId,
|
|
137271
|
+
message_index: nextIndex,
|
|
137272
|
+
byte_offset: span.byteOffset,
|
|
137273
|
+
byte_length: span.byteLength,
|
|
137274
|
+
uuid: msg.uuid ?? null,
|
|
137275
|
+
role: msg.role ?? null,
|
|
137276
|
+
ts: msg.timestamp ? Date.parse(msg.timestamp) || null : null
|
|
137277
|
+
});
|
|
137278
|
+
nextIndex++;
|
|
137279
|
+
}
|
|
137280
|
+
if (linesSinceYield >= YIELD_EVERY) {
|
|
137281
|
+
linesSinceYield = 0;
|
|
137282
|
+
await (0, import_promises13.setImmediate)();
|
|
137283
|
+
}
|
|
137284
|
+
}
|
|
137285
|
+
if (rows.length > 0) this.appendMessageIndexRows(rows);
|
|
137286
|
+
lastConsumedEnd = fileOffset + consumed;
|
|
137287
|
+
carry = Buffer.from(combined.subarray(consumed));
|
|
137288
|
+
fileOffset += consumed;
|
|
137289
|
+
}
|
|
137290
|
+
} finally {
|
|
137291
|
+
await fh.close();
|
|
137292
|
+
}
|
|
137293
|
+
this.upsertFileState({
|
|
137294
|
+
path: filePath,
|
|
137295
|
+
identity: fileIdentity(stat10),
|
|
137296
|
+
size: stat10.size,
|
|
137297
|
+
mtime_ms: Math.round(stat10.mtimeMs),
|
|
137298
|
+
byte_offset: lastConsumedEnd,
|
|
137299
|
+
last_message_index: nextIndex - 1
|
|
137300
|
+
});
|
|
137301
|
+
this.indexParseState.set(filePath, state);
|
|
137302
|
+
}
|
|
137303
|
+
/**
|
|
137304
|
+
* Windowed detail read straight from the offset index — the hot path.
|
|
137305
|
+
* Returns the parsed messages for message_index in [fromIndex, toIndex) plus
|
|
137306
|
+
* the total indexed count, or null when the index can't serve this file (no
|
|
137307
|
+
* file_state, identity/size mismatch = truncation/replacement, or cold index)
|
|
137308
|
+
* so the caller falls back to the scanner and enqueues a backfill.
|
|
137309
|
+
*
|
|
137310
|
+
* On a match it SQL-selects the window's byte ranges and preads exactly those
|
|
137311
|
+
* ranges from the JSONL (never the whole file), parsing only the sliced lines.
|
|
137312
|
+
* Returns messages in the same ConversationMessage shape parseJsonlLine
|
|
137313
|
+
* produces during a scan, so the payload is identical to the scanner path.
|
|
137314
|
+
*/
|
|
137315
|
+
readMessageWindow(filePath, fromIndex, toIndex) {
|
|
137316
|
+
const fileState = this.getFileState(filePath);
|
|
137317
|
+
if (!fileState) return null;
|
|
137318
|
+
let stat10;
|
|
137319
|
+
try {
|
|
137320
|
+
stat10 = (0, import_fs17.statSync)(filePath);
|
|
137321
|
+
} catch {
|
|
137322
|
+
return null;
|
|
137323
|
+
}
|
|
137324
|
+
if (fileIdentity(stat10) !== fileState.identity || stat10.size !== fileState.byte_offset) {
|
|
137325
|
+
return null;
|
|
137326
|
+
}
|
|
137327
|
+
const total = fileState.last_message_index + 1;
|
|
137328
|
+
const from = Math.max(0, fromIndex);
|
|
137329
|
+
const to2 = Math.min(toIndex, total);
|
|
137330
|
+
if (to2 <= from) return { messages: [], total, fromIndex: from };
|
|
137331
|
+
const rows = this.getMessageIndexWindow(
|
|
137332
|
+
_ConversationCache.conversationIdForFile(filePath),
|
|
137333
|
+
from,
|
|
137334
|
+
to2
|
|
137335
|
+
);
|
|
137336
|
+
if (rows.length === 0) return { messages: [], total, fromIndex: from };
|
|
137337
|
+
const messages = [];
|
|
137338
|
+
const fd = (0, import_fs17.openSync)(filePath, "r");
|
|
137339
|
+
try {
|
|
137340
|
+
const state = initialConvState();
|
|
137341
|
+
for (const row of rows) {
|
|
137342
|
+
const buf = Buffer.alloc(row.byte_length);
|
|
137343
|
+
(0, import_fs17.readSync)(fd, buf, 0, row.byte_length, row.byte_offset);
|
|
137344
|
+
const msg = parseJsonlLine(buf.toString("utf-8"), state);
|
|
137345
|
+
if (msg) messages.push(msg);
|
|
137346
|
+
}
|
|
137347
|
+
} finally {
|
|
137348
|
+
(0, import_fs17.closeSync)(fd);
|
|
137349
|
+
}
|
|
137350
|
+
return { messages, total, fromIndex: from };
|
|
137351
|
+
}
|
|
137009
137352
|
agentEntrypointsKey() {
|
|
137010
137353
|
return [...this.agentEntrypoints].sort().join(",");
|
|
137011
137354
|
}
|
|
@@ -137030,14 +137373,14 @@ var ConversationCache = class _ConversationCache {
|
|
|
137030
137373
|
isAgentFileCached(filePath) {
|
|
137031
137374
|
let s3;
|
|
137032
137375
|
try {
|
|
137033
|
-
s3 = (0,
|
|
137376
|
+
s3 = (0, import_fs17.statSync)(filePath);
|
|
137034
137377
|
} catch {
|
|
137035
137378
|
return false;
|
|
137036
137379
|
}
|
|
137037
137380
|
return this.classifyAgentFile(filePath, s3.mtimeMs, s3.size);
|
|
137038
137381
|
}
|
|
137039
137382
|
static open(dbPath, tailSize = 10, migrationsDir, options) {
|
|
137040
|
-
(0,
|
|
137383
|
+
(0, import_fs17.mkdirSync)((0, import_path13.dirname)(dbPath), { recursive: true });
|
|
137041
137384
|
const db = new import_better_sqlite32.default(dbPath);
|
|
137042
137385
|
db.pragma("journal_mode = WAL");
|
|
137043
137386
|
db.pragma("foreign_keys = ON");
|
|
@@ -137218,7 +137561,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
137218
137561
|
let mtimeMs = null;
|
|
137219
137562
|
let fileSize = null;
|
|
137220
137563
|
try {
|
|
137221
|
-
const s3 = (0,
|
|
137564
|
+
const s3 = (0, import_fs17.statSync)(m2.filePath);
|
|
137222
137565
|
mtimeMs = s3.mtimeMs;
|
|
137223
137566
|
fileSize = s3.size;
|
|
137224
137567
|
} catch {
|
|
@@ -137277,8 +137620,8 @@ var ConversationCache = class _ConversationCache {
|
|
|
137277
137620
|
let fileSize;
|
|
137278
137621
|
let fd;
|
|
137279
137622
|
try {
|
|
137280
|
-
fileSize = (0,
|
|
137281
|
-
fd = (0,
|
|
137623
|
+
fileSize = (0, import_fs17.statSync)(filePath).size;
|
|
137624
|
+
fd = (0, import_fs17.openSync)(filePath, "r");
|
|
137282
137625
|
} catch {
|
|
137283
137626
|
return false;
|
|
137284
137627
|
}
|
|
@@ -137291,7 +137634,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
137291
137634
|
while (pos > 0 && lines.length < this.tailSize * 4) {
|
|
137292
137635
|
const toRead = Math.min(CHUNK, pos);
|
|
137293
137636
|
pos -= toRead;
|
|
137294
|
-
(0,
|
|
137637
|
+
(0, import_fs17.readSync)(fd, buf, 0, toRead, pos);
|
|
137295
137638
|
const chunk = buf.subarray(0, toRead).toString("utf8");
|
|
137296
137639
|
const combined = chunk + partial2;
|
|
137297
137640
|
const parts = combined.split("\n");
|
|
@@ -137302,7 +137645,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
137302
137645
|
}
|
|
137303
137646
|
if (partial2) lines.push(partial2);
|
|
137304
137647
|
} finally {
|
|
137305
|
-
(0,
|
|
137648
|
+
(0, import_fs17.closeSync)(fd);
|
|
137306
137649
|
}
|
|
137307
137650
|
const msgs = [];
|
|
137308
137651
|
for (let i = 0; i < lines.length && msgs.length < this.tailSize; i++) {
|
|
@@ -137504,7 +137847,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
137504
137847
|
* `handleGetConversation` can still serve the cached tail even when the
|
|
137505
137848
|
* JSONL has been deleted.
|
|
137506
137849
|
*/
|
|
137507
|
-
pruneGhostFiles(exists =
|
|
137850
|
+
pruneGhostFiles(exists = import_fs17.existsSync) {
|
|
137508
137851
|
const rows = this.stmts.allFilePaths.all();
|
|
137509
137852
|
const ghosts = [];
|
|
137510
137853
|
const prune = this.db.transaction((ids) => {
|
|
@@ -137559,7 +137902,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
137559
137902
|
* Returns the removed IDs.
|
|
137560
137903
|
*/
|
|
137561
137904
|
reconcileDeletions(livePaths, opts) {
|
|
137562
|
-
const exists = opts?.exists ??
|
|
137905
|
+
const exists = opts?.exists ?? import_fs17.existsSync;
|
|
137563
137906
|
const rows = this.stmts.allFilePaths.all();
|
|
137564
137907
|
const removed = [];
|
|
137565
137908
|
const drop = this.db.transaction((ids) => {
|
|
@@ -137617,7 +137960,7 @@ function getDbConfig() {
|
|
|
137617
137960
|
}
|
|
137618
137961
|
|
|
137619
137962
|
// src/db/migrations.ts
|
|
137620
|
-
var
|
|
137963
|
+
var import_fs18 = require("fs");
|
|
137621
137964
|
var import_path14 = require("path");
|
|
137622
137965
|
var import_url7 = require("url");
|
|
137623
137966
|
var import_meta2 = {};
|
|
@@ -137640,10 +137983,10 @@ async function runMigrations2(pool2, migrationsDir) {
|
|
|
137640
137983
|
);
|
|
137641
137984
|
const appliedSet = new Set(applied.map((r) => r.name));
|
|
137642
137985
|
const dir = migrationsDir ?? (0, import_path14.join)(getMigrationsDir2(), "pg-migrations");
|
|
137643
|
-
const files = (0,
|
|
137986
|
+
const files = (0, import_fs18.readdirSync)(dir).filter((f2) => f2.endsWith(".sql")).sort();
|
|
137644
137987
|
for (const file2 of files) {
|
|
137645
137988
|
if (appliedSet.has(file2)) continue;
|
|
137646
|
-
const sql = (0,
|
|
137989
|
+
const sql = (0, import_fs18.readFileSync)((0, import_path14.join)(dir, file2), "utf-8");
|
|
137647
137990
|
await pool2.query(sql);
|
|
137648
137991
|
await pool2.query("INSERT INTO _migrations (name) VALUES ($1)", [file2]);
|
|
137649
137992
|
}
|
|
@@ -137729,7 +138072,7 @@ var ConversationsRepository = class {
|
|
|
137729
138072
|
};
|
|
137730
138073
|
|
|
137731
138074
|
// src/db/repositories/projects.repository.ts
|
|
137732
|
-
var
|
|
138075
|
+
var import_crypto6 = require("crypto");
|
|
137733
138076
|
|
|
137734
138077
|
// src/utils/canonicalizeProjectPath.ts
|
|
137735
138078
|
function canonicalizeProjectPath(projectPath) {
|
|
@@ -137822,7 +138165,7 @@ var ProjectsRepository = class {
|
|
|
137822
138165
|
});
|
|
137823
138166
|
return rowToProject(this.getById.get(existing.id));
|
|
137824
138167
|
}
|
|
137825
|
-
const id = (0,
|
|
138168
|
+
const id = (0, import_crypto6.randomUUID)();
|
|
137826
138169
|
this.insert.run({
|
|
137827
138170
|
id,
|
|
137828
138171
|
path: path2,
|
|
@@ -137878,7 +138221,7 @@ async function recordUpload(pool2, instanceId, row) {
|
|
|
137878
138221
|
}
|
|
137879
138222
|
|
|
137880
138223
|
// src/handlers/handleListProjects.ts
|
|
137881
|
-
var
|
|
138224
|
+
var import_fs19 = require("fs");
|
|
137882
138225
|
var import_os7 = require("os");
|
|
137883
138226
|
var import_path15 = require("path");
|
|
137884
138227
|
function decodeProjectPath(dirName) {
|
|
@@ -137890,11 +138233,11 @@ function handleListProjects(url2, res) {
|
|
|
137890
138233
|
const projectsDir = (0, import_path15.join)((0, import_os7.homedir)(), ".claude", "projects");
|
|
137891
138234
|
let entries;
|
|
137892
138235
|
try {
|
|
137893
|
-
entries = (0,
|
|
138236
|
+
entries = (0, import_fs19.readdirSync)(projectsDir).map((dirName) => {
|
|
137894
138237
|
const fullPath = (0, import_path15.join)(projectsDir, dirName);
|
|
137895
138238
|
let mtime = 0;
|
|
137896
138239
|
try {
|
|
137897
|
-
mtime = (0,
|
|
138240
|
+
mtime = (0, import_fs19.statSync)(fullPath).mtimeMs;
|
|
137898
138241
|
} catch {
|
|
137899
138242
|
}
|
|
137900
138243
|
const path2 = decodeProjectPath(dirName);
|
|
@@ -137917,14 +138260,14 @@ var import_path19 = require("path");
|
|
|
137917
138260
|
|
|
137918
138261
|
// src/codex-pty-runner.ts
|
|
137919
138262
|
var import_headless = __toESM(require_xterm_headless(), 1);
|
|
137920
|
-
var
|
|
137921
|
-
var
|
|
138263
|
+
var import_crypto7 = require("crypto");
|
|
138264
|
+
var import_fs21 = require("fs");
|
|
137922
138265
|
var import_path17 = require("path");
|
|
137923
138266
|
init_logger();
|
|
137924
138267
|
|
|
137925
138268
|
// src/platform.ts
|
|
137926
138269
|
var import_child_process = require("child_process");
|
|
137927
|
-
var
|
|
138270
|
+
var import_fs20 = require("fs");
|
|
137928
138271
|
var import_os8 = require("os");
|
|
137929
138272
|
var import_path16 = require("path");
|
|
137930
138273
|
var isWindows2 = (0, import_os8.platform)() === "win32";
|
|
@@ -137954,7 +138297,7 @@ function resolveClaudeExe() {
|
|
|
137954
138297
|
)
|
|
137955
138298
|
];
|
|
137956
138299
|
for (const p2 of candidates) {
|
|
137957
|
-
if ((0,
|
|
138300
|
+
if ((0, import_fs20.existsSync)(p2)) {
|
|
137958
138301
|
_claudeExe = p2;
|
|
137959
138302
|
return _claudeExe;
|
|
137960
138303
|
}
|
|
@@ -137965,7 +138308,7 @@ function resolveClaudeExe() {
|
|
|
137965
138308
|
encoding: "utf-8",
|
|
137966
138309
|
timeout: 3e3
|
|
137967
138310
|
}).trim().split("\n")[0].trim();
|
|
137968
|
-
if (found && (0,
|
|
138311
|
+
if (found && (0, import_fs20.existsSync)(found)) {
|
|
137969
138312
|
_claudeExe = found;
|
|
137970
138313
|
return _claudeExe;
|
|
137971
138314
|
}
|
|
@@ -137977,7 +138320,7 @@ function resolveClaudeExe() {
|
|
|
137977
138320
|
(0, import_path16.join)((0, import_os8.homedir)(), ".local", "bin", "claude")
|
|
137978
138321
|
];
|
|
137979
138322
|
for (const p2 of candidates) {
|
|
137980
|
-
if ((0,
|
|
138323
|
+
if ((0, import_fs20.existsSync)(p2)) {
|
|
137981
138324
|
_claudeExe = p2;
|
|
137982
138325
|
return _claudeExe;
|
|
137983
138326
|
}
|
|
@@ -138012,7 +138355,7 @@ function resolveCodexExe() {
|
|
|
138012
138355
|
)
|
|
138013
138356
|
];
|
|
138014
138357
|
for (const p2 of candidates) {
|
|
138015
|
-
if ((0,
|
|
138358
|
+
if ((0, import_fs20.existsSync)(p2)) {
|
|
138016
138359
|
_codexExe = p2;
|
|
138017
138360
|
return _codexExe;
|
|
138018
138361
|
}
|
|
@@ -138023,7 +138366,7 @@ function resolveCodexExe() {
|
|
|
138023
138366
|
encoding: "utf-8",
|
|
138024
138367
|
timeout: 3e3
|
|
138025
138368
|
}).trim().split("\n")[0].trim();
|
|
138026
|
-
if (found && (0,
|
|
138369
|
+
if (found && (0, import_fs20.existsSync)(found)) {
|
|
138027
138370
|
_codexExe = found;
|
|
138028
138371
|
return _codexExe;
|
|
138029
138372
|
}
|
|
@@ -138035,7 +138378,7 @@ function resolveCodexExe() {
|
|
|
138035
138378
|
(0, import_path16.join)((0, import_os8.homedir)(), ".local", "bin", "codex")
|
|
138036
138379
|
];
|
|
138037
138380
|
for (const p2 of candidates) {
|
|
138038
|
-
if ((0,
|
|
138381
|
+
if ((0, import_fs20.existsSync)(p2)) {
|
|
138039
138382
|
_codexExe = p2;
|
|
138040
138383
|
return _codexExe;
|
|
138041
138384
|
}
|
|
@@ -138172,7 +138515,7 @@ var CodexPtyRunner = class {
|
|
|
138172
138515
|
// ManagedSession handle only.
|
|
138173
138516
|
async startFresh(options) {
|
|
138174
138517
|
const nodePty = await loadPty();
|
|
138175
|
-
const sessionId = (0,
|
|
138518
|
+
const sessionId = (0, import_crypto7.randomUUID)();
|
|
138176
138519
|
const projectName = options.projectName ?? (0, import_path17.basename)(options.projectPath);
|
|
138177
138520
|
const args = ["--cd", options.projectPath, "--no-alt-screen"];
|
|
138178
138521
|
if (options.systemPrompt) {
|
|
@@ -138465,7 +138808,7 @@ var CodexPtyRunner = class {
|
|
|
138465
138808
|
session.status = "idle";
|
|
138466
138809
|
const elapsedMs = session.completedAt.getTime() - session.startedAt.getTime();
|
|
138467
138810
|
if (exitCode !== 0 && elapsedMs < 2e3 && session.lastOutput === "") {
|
|
138468
|
-
if (!(0,
|
|
138811
|
+
if (!(0, import_fs21.existsSync)(session.projectPath)) {
|
|
138469
138812
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
138470
138813
|
} else {
|
|
138471
138814
|
session.failureReason = `Codex process exited immediately (code ${exitCode}).`;
|
|
@@ -138501,8 +138844,8 @@ function stripAnsi(str) {
|
|
|
138501
138844
|
|
|
138502
138845
|
// src/pty-manager.ts
|
|
138503
138846
|
var import_headless2 = __toESM(require_xterm_headless(), 1);
|
|
138504
|
-
var
|
|
138505
|
-
var
|
|
138847
|
+
var import_crypto8 = require("crypto");
|
|
138848
|
+
var import_fs22 = require("fs");
|
|
138506
138849
|
var import_path18 = require("path");
|
|
138507
138850
|
init_logger();
|
|
138508
138851
|
|
|
@@ -138905,7 +139248,7 @@ var PTYManager = class {
|
|
|
138905
139248
|
// onReady fires once Claude reaches its first prompt (waiting_input).
|
|
138906
139249
|
async startFresh(options) {
|
|
138907
139250
|
const nodePty = await loadPty2();
|
|
138908
|
-
const sessionId = (0,
|
|
139251
|
+
const sessionId = (0, import_crypto8.randomUUID)();
|
|
138909
139252
|
const projectName = options.projectName ?? (0, import_path18.basename)(options.projectPath);
|
|
138910
139253
|
const args = [
|
|
138911
139254
|
"--permission-mode",
|
|
@@ -139350,7 +139693,7 @@ var PTYManager = class {
|
|
|
139350
139693
|
session.status = "idle";
|
|
139351
139694
|
const elapsedMs = session.completedAt.getTime() - session.startedAt.getTime();
|
|
139352
139695
|
if (exitCode !== 0 && elapsedMs < 2e3 && session.lastOutput === "") {
|
|
139353
|
-
if (!(0,
|
|
139696
|
+
if (!(0, import_fs22.existsSync)(session.projectPath)) {
|
|
139354
139697
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
139355
139698
|
} else {
|
|
139356
139699
|
session.failureReason = `Process exited immediately (code ${exitCode}). Check that the Claude binary is installed and accessible.`;
|
|
@@ -139489,7 +139832,7 @@ var LiveSessionManager = class {
|
|
|
139489
139832
|
init_logger();
|
|
139490
139833
|
|
|
139491
139834
|
// src/pair-store.ts
|
|
139492
|
-
var
|
|
139835
|
+
var import_crypto9 = require("crypto");
|
|
139493
139836
|
var DEFAULT_TTL_SECONDS = 180;
|
|
139494
139837
|
var SWEEP_INTERVAL_MS = 6e4;
|
|
139495
139838
|
var PairTokenStore = class {
|
|
@@ -139504,7 +139847,7 @@ var PairTokenStore = class {
|
|
|
139504
139847
|
}
|
|
139505
139848
|
}
|
|
139506
139849
|
mint() {
|
|
139507
|
-
const token = `pt_${(0,
|
|
139850
|
+
const token = `pt_${(0, import_crypto9.randomBytes)(16).toString("hex")}`;
|
|
139508
139851
|
const expiresAt = Date.now() + this.ttlMs;
|
|
139509
139852
|
this.current = { token, expiresAt, used: false };
|
|
139510
139853
|
return {
|
|
@@ -139709,11 +140052,11 @@ function seal(plaintext, recipientPublicKeyBase64) {
|
|
|
139709
140052
|
// node_modules/chokidar/index.js
|
|
139710
140053
|
var import_node_events2 = require("events");
|
|
139711
140054
|
var import_node_fs7 = require("fs");
|
|
139712
|
-
var
|
|
140055
|
+
var import_promises16 = require("fs/promises");
|
|
139713
140056
|
var sp2 = __toESM(require("path"), 1);
|
|
139714
140057
|
|
|
139715
140058
|
// node_modules/readdirp/index.js
|
|
139716
|
-
var
|
|
140059
|
+
var import_promises14 = require("fs/promises");
|
|
139717
140060
|
var import_node_path9 = require("path");
|
|
139718
140061
|
var import_node_stream2 = require("stream");
|
|
139719
140062
|
var EntryTypes2 = {
|
|
@@ -139794,7 +140137,7 @@ var ReaddirpStream2 = class extends import_node_stream2.Readable {
|
|
|
139794
140137
|
const { root, type } = opts;
|
|
139795
140138
|
this._fileFilter = normalizeFilter2(opts.fileFilter);
|
|
139796
140139
|
this._directoryFilter = normalizeFilter2(opts.directoryFilter);
|
|
139797
|
-
const statMethod = opts.lstat ?
|
|
140140
|
+
const statMethod = opts.lstat ? import_promises14.lstat : import_promises14.stat;
|
|
139798
140141
|
if (wantBigintFsStats2) {
|
|
139799
140142
|
this._stat = (path2) => statMethod(path2, { bigint: true });
|
|
139800
140143
|
} else {
|
|
@@ -139865,7 +140208,7 @@ var ReaddirpStream2 = class extends import_node_stream2.Readable {
|
|
|
139865
140208
|
async _exploreDir(path2, depth) {
|
|
139866
140209
|
let files;
|
|
139867
140210
|
try {
|
|
139868
|
-
files = await (0,
|
|
140211
|
+
files = await (0, import_promises14.readdir)(path2, this._rdOptions);
|
|
139869
140212
|
} catch (error51) {
|
|
139870
140213
|
this._onError(error51);
|
|
139871
140214
|
}
|
|
@@ -139903,8 +140246,8 @@ var ReaddirpStream2 = class extends import_node_stream2.Readable {
|
|
|
139903
140246
|
if (stats && stats.isSymbolicLink()) {
|
|
139904
140247
|
const full = entry.fullPath;
|
|
139905
140248
|
try {
|
|
139906
|
-
const entryRealPath = await (0,
|
|
139907
|
-
const entryRealPathStats = await (0,
|
|
140249
|
+
const entryRealPath = await (0, import_promises14.realpath)(full);
|
|
140250
|
+
const entryRealPathStats = await (0, import_promises14.lstat)(entryRealPath);
|
|
139908
140251
|
if (entryRealPathStats.isFile()) {
|
|
139909
140252
|
return "file";
|
|
139910
140253
|
}
|
|
@@ -139947,7 +140290,7 @@ function readdirp2(root, options = {}) {
|
|
|
139947
140290
|
|
|
139948
140291
|
// node_modules/chokidar/handler.js
|
|
139949
140292
|
var import_node_fs6 = require("fs");
|
|
139950
|
-
var
|
|
140293
|
+
var import_promises15 = require("fs/promises");
|
|
139951
140294
|
var import_node_os3 = require("os");
|
|
139952
140295
|
var sp = __toESM(require("path"), 1);
|
|
139953
140296
|
var STR_DATA2 = "data";
|
|
@@ -139974,7 +140317,7 @@ var EVENTS2 = {
|
|
|
139974
140317
|
};
|
|
139975
140318
|
var EV2 = EVENTS2;
|
|
139976
140319
|
var THROTTLE_MODE_WATCH2 = "watch";
|
|
139977
|
-
var statMethods2 = { lstat:
|
|
140320
|
+
var statMethods2 = { lstat: import_promises15.lstat, stat: import_promises15.stat };
|
|
139978
140321
|
var KEY_LISTENERS2 = "listeners";
|
|
139979
140322
|
var KEY_ERR2 = "errHandlers";
|
|
139980
140323
|
var KEY_RAW2 = "rawEmitters";
|
|
@@ -140331,7 +140674,7 @@ var setFsWatchListener2 = (path2, fullPath, options, handlers) => {
|
|
|
140331
140674
|
cont.watcherUnusable = true;
|
|
140332
140675
|
if (isWindows3 && error51.code === "EPERM") {
|
|
140333
140676
|
try {
|
|
140334
|
-
const fd = await (0,
|
|
140677
|
+
const fd = await (0, import_promises15.open)(path2, "r");
|
|
140335
140678
|
await fd.close();
|
|
140336
140679
|
broadcastErr(error51);
|
|
140337
140680
|
} catch (err) {
|
|
@@ -140462,7 +140805,7 @@ var NodeFsHandler2 = class {
|
|
|
140462
140805
|
return;
|
|
140463
140806
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
140464
140807
|
try {
|
|
140465
|
-
const newStats2 = await (0,
|
|
140808
|
+
const newStats2 = await (0, import_promises15.stat)(file2);
|
|
140466
140809
|
if (this.fsw.closed)
|
|
140467
140810
|
return;
|
|
140468
140811
|
const at2 = newStats2.atimeMs;
|
|
@@ -140517,7 +140860,7 @@ var NodeFsHandler2 = class {
|
|
|
140517
140860
|
this.fsw._incrReadyCount();
|
|
140518
140861
|
let linkPath;
|
|
140519
140862
|
try {
|
|
140520
|
-
linkPath = await (0,
|
|
140863
|
+
linkPath = await (0, import_promises15.realpath)(path2);
|
|
140521
140864
|
} catch (e) {
|
|
140522
140865
|
this.fsw._emitReady();
|
|
140523
140866
|
return true;
|
|
@@ -140666,7 +141009,7 @@ var NodeFsHandler2 = class {
|
|
|
140666
141009
|
let closer;
|
|
140667
141010
|
if (stats.isDirectory()) {
|
|
140668
141011
|
const absPath = sp.resolve(path2);
|
|
140669
|
-
const targetPath = follow ? await (0,
|
|
141012
|
+
const targetPath = follow ? await (0, import_promises15.realpath)(path2) : path2;
|
|
140670
141013
|
if (this.fsw.closed)
|
|
140671
141014
|
return;
|
|
140672
141015
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -140676,7 +141019,7 @@ var NodeFsHandler2 = class {
|
|
|
140676
141019
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
140677
141020
|
}
|
|
140678
141021
|
} else if (stats.isSymbolicLink()) {
|
|
140679
|
-
const targetPath = follow ? await (0,
|
|
141022
|
+
const targetPath = follow ? await (0, import_promises15.realpath)(path2) : path2;
|
|
140680
141023
|
if (this.fsw.closed)
|
|
140681
141024
|
return;
|
|
140682
141025
|
const parent = sp.dirname(wh.watchPath);
|
|
@@ -140836,7 +141179,7 @@ var DirEntry2 = class {
|
|
|
140836
141179
|
return;
|
|
140837
141180
|
const dir = this.path;
|
|
140838
141181
|
try {
|
|
140839
|
-
await (0,
|
|
141182
|
+
await (0, import_promises16.readdir)(dir);
|
|
140840
141183
|
} catch (err) {
|
|
140841
141184
|
if (this._removeWatcher) {
|
|
140842
141185
|
this._removeWatcher(sp2.dirname(dir), sp2.basename(dir));
|
|
@@ -141186,7 +141529,7 @@ var FSWatcher2 = class extends import_node_events2.EventEmitter {
|
|
|
141186
141529
|
const fullPath = opts.cwd ? sp2.join(opts.cwd, path2) : path2;
|
|
141187
141530
|
let stats2;
|
|
141188
141531
|
try {
|
|
141189
|
-
stats2 = await (0,
|
|
141532
|
+
stats2 = await (0, import_promises16.stat)(fullPath);
|
|
141190
141533
|
} catch (err) {
|
|
141191
141534
|
}
|
|
141192
141535
|
if (!stats2 || this.closed)
|
|
@@ -141439,19 +141782,21 @@ function watch2(paths, options = {}) {
|
|
|
141439
141782
|
var chokidar_default = { watch: watch2, FSWatcher: FSWatcher2 };
|
|
141440
141783
|
|
|
141441
141784
|
// src/services/conversations/conversationWatcher.ts
|
|
141442
|
-
var
|
|
141443
|
-
var
|
|
141785
|
+
var import_fs23 = require("fs");
|
|
141786
|
+
var import_promises17 = require("fs/promises");
|
|
141444
141787
|
var ConversationWatcher = class {
|
|
141445
141788
|
files = /* @__PURE__ */ new Map();
|
|
141446
141789
|
directories = /* @__PURE__ */ new Map();
|
|
141447
141790
|
onNewLine;
|
|
141448
141791
|
onNewLines;
|
|
141792
|
+
onNewLineSpans;
|
|
141449
141793
|
onConversationChanged;
|
|
141450
141794
|
onFileDeleted;
|
|
141451
141795
|
onError;
|
|
141452
141796
|
constructor(events = {}) {
|
|
141453
141797
|
this.onNewLine = events.onNewLine;
|
|
141454
141798
|
this.onNewLines = events.onNewLines;
|
|
141799
|
+
this.onNewLineSpans = events.onNewLineSpans;
|
|
141455
141800
|
this.onConversationChanged = events.onConversationChanged;
|
|
141456
141801
|
this.onFileDeleted = events.onFileDeleted;
|
|
141457
141802
|
this.onError = events.onError;
|
|
@@ -141460,7 +141805,7 @@ var ConversationWatcher = class {
|
|
|
141460
141805
|
if (this.files.has(filePath)) return;
|
|
141461
141806
|
let offset;
|
|
141462
141807
|
try {
|
|
141463
|
-
offset = (0,
|
|
141808
|
+
offset = (0, import_fs23.statSync)(filePath).size;
|
|
141464
141809
|
} catch {
|
|
141465
141810
|
offset = 0;
|
|
141466
141811
|
}
|
|
@@ -141544,20 +141889,24 @@ var ConversationWatcher = class {
|
|
|
141544
141889
|
entry.reading = true;
|
|
141545
141890
|
try {
|
|
141546
141891
|
for (; ; ) {
|
|
141547
|
-
const st2 = await (0,
|
|
141892
|
+
const st2 = await (0, import_promises17.stat)(filePath);
|
|
141548
141893
|
if (st2.size <= entry.offset) break;
|
|
141549
141894
|
const readFrom = entry.offset;
|
|
141550
141895
|
const bytesToRead = st2.size - readFrom;
|
|
141551
141896
|
const buf = Buffer.alloc(bytesToRead);
|
|
141552
|
-
const fh = await (0,
|
|
141897
|
+
const fh = await (0, import_promises17.open)(filePath, "r");
|
|
141553
141898
|
try {
|
|
141554
141899
|
await fh.read(buf, 0, bytesToRead, readFrom);
|
|
141555
141900
|
} finally {
|
|
141556
141901
|
await fh.close();
|
|
141557
141902
|
}
|
|
141558
|
-
|
|
141903
|
+
const { spans, consumed } = splitCompleteLines(buf, readFrom);
|
|
141904
|
+
entry.offset = readFrom + consumed;
|
|
141559
141905
|
if (!this.files.has(filePath)) return;
|
|
141560
|
-
const lines =
|
|
141906
|
+
const lines = spans.map((s3) => s3.text);
|
|
141907
|
+
if (spans.length > 0) {
|
|
141908
|
+
this.onNewLineSpans?.(filePath, spans, readFrom, entry.offset);
|
|
141909
|
+
}
|
|
141561
141910
|
if (this.onNewLines) {
|
|
141562
141911
|
this.onNewLines(filePath, lines);
|
|
141563
141912
|
} else {
|
|
@@ -141631,14 +141980,14 @@ function findSearchTarget(messages, query) {
|
|
|
141631
141980
|
}
|
|
141632
141981
|
|
|
141633
141982
|
// src/services/conversations/pruneAgentConversations.ts
|
|
141634
|
-
var
|
|
141983
|
+
var import_fs24 = require("fs");
|
|
141635
141984
|
function pruneAgentConversations(cache) {
|
|
141636
141985
|
const db = cache.getDatabase();
|
|
141637
141986
|
const rows = db.prepare("SELECT id, file_path FROM conversation_meta").all();
|
|
141638
141987
|
let pruned = 0;
|
|
141639
141988
|
let missing = 0;
|
|
141640
141989
|
for (const row of rows) {
|
|
141641
|
-
if (!(0,
|
|
141990
|
+
if (!(0, import_fs24.existsSync)(row.file_path)) {
|
|
141642
141991
|
missing += 1;
|
|
141643
141992
|
continue;
|
|
141644
141993
|
}
|
|
@@ -141997,8 +142346,8 @@ function discoveredToResponse(d, conversationId) {
|
|
|
141997
142346
|
}
|
|
141998
142347
|
|
|
141999
142348
|
// src/uploads.ts
|
|
142000
|
-
var
|
|
142001
|
-
var
|
|
142349
|
+
var import_crypto10 = require("crypto");
|
|
142350
|
+
var import_promises18 = require("fs/promises");
|
|
142002
142351
|
var import_heic_convert = __toESM(require_heic_convert(), 1);
|
|
142003
142352
|
var import_path21 = require("path");
|
|
142004
142353
|
var UPLOAD_DIR_NAME = ".threadbase-uploads";
|
|
@@ -142031,12 +142380,12 @@ async function saveUploadFile(input) {
|
|
|
142031
142380
|
mimeType = "image/jpeg";
|
|
142032
142381
|
originalName = originalName.replace(/\.(heic|heif)$/i, ".jpg");
|
|
142033
142382
|
}
|
|
142034
|
-
const id = `up_${(0,
|
|
142383
|
+
const id = `up_${(0, import_crypto10.randomBytes)(8).toString("hex")}`;
|
|
142035
142384
|
const safeName = sanitizeFilename(originalName) || `file${MIME_TO_EXT[mimeType] ?? ""}`;
|
|
142036
142385
|
const dir = (0, import_path21.join)(input.projectPath, UPLOAD_DIR_NAME, input.sessionId);
|
|
142037
|
-
await (0,
|
|
142386
|
+
await (0, import_promises18.mkdir)(dir, { recursive: true });
|
|
142038
142387
|
const filePath = (0, import_path21.join)(dir, `${Date.now()}-${id}-${safeName}`);
|
|
142039
|
-
await (0,
|
|
142388
|
+
await (0, import_promises18.writeFile)(filePath, buffer);
|
|
142040
142389
|
return {
|
|
142041
142390
|
id,
|
|
142042
142391
|
filePath,
|
|
@@ -142420,6 +142769,7 @@ var WSHub = class {
|
|
|
142420
142769
|
var BROWSE_SYSTEM_PROMPT = (browseRoot) => `You are working within the project boundary: ${browseRoot}. Do not read, write, or execute commands that access files or directories outside this boundary.`;
|
|
142421
142770
|
var DEFAULT_SYSTEM_PROMPT = "When presenting options or choices to the user, limit the options to at most 3.";
|
|
142422
142771
|
var DEFAULT_PTY_GRACE_PERIOD_MS = 27e4;
|
|
142772
|
+
var REFRESH_TTL_MS = 2e3;
|
|
142423
142773
|
var START_READY_TIMEOUT_MS = 15e3;
|
|
142424
142774
|
function parseIncludeAgentsEnv(raw2) {
|
|
142425
142775
|
if (raw2 === void 0) return false;
|
|
@@ -142434,6 +142784,10 @@ var StreamerServer = class {
|
|
|
142434
142784
|
fileWatcher;
|
|
142435
142785
|
sessionFileMap = /* @__PURE__ */ new Map();
|
|
142436
142786
|
// sessionId → JSONL filePath
|
|
142787
|
+
// Per-file seq assignments from the most recent onNewLineSpans (offset index),
|
|
142788
|
+
// handed to the immediately-following onNewLines so it can stamp WS `seq` on
|
|
142789
|
+
// the matching conversation_events entries. Same read → same lines order.
|
|
142790
|
+
pendingLineSeqs = /* @__PURE__ */ new Map();
|
|
142437
142791
|
pendingQuestions = /* @__PURE__ */ new Map();
|
|
142438
142792
|
// Content key of the AskUserQuestion currently broadcast for a session (from
|
|
142439
142793
|
// either the rendered screen or JSONL), used to de-dupe the two paths: when
|
|
@@ -142459,6 +142813,12 @@ var StreamerServer = class {
|
|
|
142459
142813
|
// Set by onConversationChanged while a scan is in-flight; getScanner() does
|
|
142460
142814
|
// a single rescan after the current one completes instead of restarting it.
|
|
142461
142815
|
scannerStale = false;
|
|
142816
|
+
// Single-flight + TTL guard around scanner.refreshFile (see refreshFileGuarded).
|
|
142817
|
+
// A live file's mtime is always newer than the snapshot, so an unguarded
|
|
142818
|
+
// refresh fires on every request and re-parses the whole file from byte 0.
|
|
142819
|
+
// Keyed by filePath; entries drop on settle + TTL expiry, so the map stays
|
|
142820
|
+
// bounded by the active-file set.
|
|
142821
|
+
refreshInFlight = /* @__PURE__ */ new Map();
|
|
142462
142822
|
// True only while bindWithRetry is actively retrying. The persistent
|
|
142463
142823
|
// listener-level 'error' handler demotes EADDRINUSE to debug during this
|
|
142464
142824
|
// window so the self-healing kickstart-relaunch race doesn't spam warn.
|
|
@@ -142551,7 +142911,7 @@ var StreamerServer = class {
|
|
|
142551
142911
|
this.agentEntrypoints = parseAgentEntrypointsEnv(process.env.THREADBASE_AGENT_ENTRYPOINTS);
|
|
142552
142912
|
const rawRoot = process.env.THREADBASE_BROWSE_ROOT ?? loadBrowseRoot() ?? config2.browseRoot;
|
|
142553
142913
|
if (rawRoot) {
|
|
142554
|
-
(0,
|
|
142914
|
+
(0, import_promises19.realpath)(rawRoot).then((resolved) => {
|
|
142555
142915
|
this.browseRoot = resolved;
|
|
142556
142916
|
if (this.verbose) this.log.info(`Browse root: ${resolved}`, { browseRoot: resolved });
|
|
142557
142917
|
}).catch(() => {
|
|
@@ -142573,6 +142933,42 @@ var StreamerServer = class {
|
|
|
142573
142933
|
this.sessionStore = new SessionStore();
|
|
142574
142934
|
this.wsHub = new WSHub();
|
|
142575
142935
|
this.fileWatcher = new ConversationWatcher({
|
|
142936
|
+
onNewLineSpans: (filePath, spans, readFrom, endOffset) => {
|
|
142937
|
+
if (!this.cache) return;
|
|
142938
|
+
const cache = this.cache;
|
|
142939
|
+
this.pendingLineSeqs.delete(filePath);
|
|
142940
|
+
try {
|
|
142941
|
+
const seqs = cache.extendMessageIndex(
|
|
142942
|
+
filePath,
|
|
142943
|
+
spans,
|
|
142944
|
+
(0, import_fs25.statSync)(filePath),
|
|
142945
|
+
readFrom,
|
|
142946
|
+
endOffset
|
|
142947
|
+
);
|
|
142948
|
+
if (seqs === null) {
|
|
142949
|
+
cache.deleteFileIndex(filePath, ConversationCache.conversationIdForFile(filePath));
|
|
142950
|
+
cache.clearIndexParseState(filePath);
|
|
142951
|
+
this.trackCacheWrite(
|
|
142952
|
+
cache.backfillIndex(filePath).catch((err) => {
|
|
142953
|
+
this.log.warn("offset-index.backfill_failed", {
|
|
142954
|
+
event: "offset_index.backfill_failed",
|
|
142955
|
+
filePath,
|
|
142956
|
+
trigger: "noncontiguous-append",
|
|
142957
|
+
err
|
|
142958
|
+
});
|
|
142959
|
+
})
|
|
142960
|
+
);
|
|
142961
|
+
return;
|
|
142962
|
+
}
|
|
142963
|
+
this.pendingLineSeqs.set(filePath, seqs);
|
|
142964
|
+
} catch (err) {
|
|
142965
|
+
this.log.warn("offset-index.extend_failed", {
|
|
142966
|
+
event: "offset_index.extend_failed",
|
|
142967
|
+
filePath,
|
|
142968
|
+
err
|
|
142969
|
+
});
|
|
142970
|
+
}
|
|
142971
|
+
},
|
|
142576
142972
|
onNewLines: (filePath, lines) => {
|
|
142577
142973
|
this.cache?.updateFromLines(filePath, lines);
|
|
142578
142974
|
for (const [sessionId, watchedPath] of this.sessionFileMap) {
|
|
@@ -142599,13 +142995,20 @@ var StreamerServer = class {
|
|
|
142599
142995
|
this.pendingQuestionKey.set(sessionId, key);
|
|
142600
142996
|
if (broadcast) this.wsHub.broadcast(m2);
|
|
142601
142997
|
}
|
|
142602
|
-
this.
|
|
142998
|
+
const seqs = this.pendingLineSeqs.get(filePath);
|
|
142999
|
+
this.wsHub.broadcast({
|
|
143000
|
+
type: "conversation_events",
|
|
143001
|
+
sessionId,
|
|
143002
|
+
lines,
|
|
143003
|
+
...seqs && seqs.length === lines.length ? { seqs } : {}
|
|
143004
|
+
});
|
|
142603
143005
|
for (const line of lines) {
|
|
142604
143006
|
this.wsHub.broadcast({ type: "conversation_event", sessionId, line });
|
|
142605
143007
|
}
|
|
142606
143008
|
break;
|
|
142607
143009
|
}
|
|
142608
143010
|
}
|
|
143011
|
+
this.pendingLineSeqs.delete(filePath);
|
|
142609
143012
|
},
|
|
142610
143013
|
onConversationChanged: (filePath) => {
|
|
142611
143014
|
this.fileWatcher.poke(filePath);
|
|
@@ -142657,7 +143060,7 @@ var StreamerServer = class {
|
|
|
142657
143060
|
if (session.status === "waiting_input" || session.status === "idle") {
|
|
142658
143061
|
const filePath = this.sessionFileMap.get(session.id);
|
|
142659
143062
|
if (filePath) {
|
|
142660
|
-
this.getScanner().then((scanner) =>
|
|
143063
|
+
this.getScanner().then((scanner) => this.refreshFileGuarded(scanner, filePath)).then((meta3) => {
|
|
142661
143064
|
this.log.info("scanner.refreshFile: ok", {
|
|
142662
143065
|
event: "scanner.refresh",
|
|
142663
143066
|
sessionId: session.id,
|
|
@@ -143106,6 +143509,34 @@ var StreamerServer = class {
|
|
|
143106
143509
|
this.inFlightCacheWrites.delete(guarded);
|
|
143107
143510
|
});
|
|
143108
143511
|
}
|
|
143512
|
+
// Single-flight + TTL wrapper for scanner.refreshFile. Both call sites (the
|
|
143513
|
+
// detail-stale branch and the per-turn refresh) route through here so that
|
|
143514
|
+
// N stacked retries on a live, actively-appended file cost one parse, not N:
|
|
143515
|
+
// - a refresh already in flight for the path → await the same promise;
|
|
143516
|
+
// - a refresh that settled within REFRESH_TTL_MS → skip, return null (the
|
|
143517
|
+
// caller keeps serving the current snapshot);
|
|
143518
|
+
// - otherwise start one, cache the promise, and stamp completedAt on settle.
|
|
143519
|
+
// The map is bounded by the active-file set (entries only live while a
|
|
143520
|
+
// refresh is in flight or within its TTL and are overwritten on the next
|
|
143521
|
+
// refresh of the same path).
|
|
143522
|
+
refreshFileGuarded(scanner, filePath) {
|
|
143523
|
+
const existing = this.refreshInFlight.get(filePath);
|
|
143524
|
+
if (existing) {
|
|
143525
|
+
const settled = existing.completedAt > 0;
|
|
143526
|
+
if (!settled) {
|
|
143527
|
+
return existing.promise;
|
|
143528
|
+
}
|
|
143529
|
+
if (Date.now() - existing.completedAt < REFRESH_TTL_MS) {
|
|
143530
|
+
return Promise.resolve(null);
|
|
143531
|
+
}
|
|
143532
|
+
}
|
|
143533
|
+
const entry = { promise: Promise.resolve(null), completedAt: 0 };
|
|
143534
|
+
entry.promise = scanner.refreshFile(filePath).finally(() => {
|
|
143535
|
+
entry.completedAt = Date.now();
|
|
143536
|
+
});
|
|
143537
|
+
this.refreshInFlight.set(filePath, entry);
|
|
143538
|
+
return entry.promise;
|
|
143539
|
+
}
|
|
143109
143540
|
async close() {
|
|
143110
143541
|
for (const timer of this.ptyGraceTimers.values()) clearTimeout(timer);
|
|
143111
143542
|
this.ptyGraceTimers.clear();
|
|
@@ -143516,16 +143947,16 @@ var StreamerServer = class {
|
|
|
143516
143947
|
}
|
|
143517
143948
|
findJsonlPath(uuid3) {
|
|
143518
143949
|
const projectsDir = (0, import_path22.join)((0, import_os10.homedir)(), ".claude", "projects");
|
|
143519
|
-
if (!(0,
|
|
143950
|
+
if (!(0, import_fs25.existsSync)(projectsDir)) return null;
|
|
143520
143951
|
const filename = `${uuid3}.jsonl`;
|
|
143521
|
-
for (const dir of (0,
|
|
143952
|
+
for (const dir of (0, import_fs25.readdirSync)(projectsDir)) {
|
|
143522
143953
|
const fp = (0, import_path22.join)(projectsDir, dir, filename);
|
|
143523
|
-
if ((0,
|
|
143954
|
+
if ((0, import_fs25.existsSync)(fp)) return fp;
|
|
143524
143955
|
const projectDir = (0, import_path22.join)(projectsDir, dir);
|
|
143525
143956
|
try {
|
|
143526
|
-
for (const sub of (0,
|
|
143957
|
+
for (const sub of (0, import_fs25.readdirSync)(projectDir)) {
|
|
143527
143958
|
const subagentPath = (0, import_path22.join)(projectDir, sub, "subagents", filename);
|
|
143528
|
-
if ((0,
|
|
143959
|
+
if ((0, import_fs25.existsSync)(subagentPath)) return subagentPath;
|
|
143529
143960
|
}
|
|
143530
143961
|
} catch {
|
|
143531
143962
|
}
|
|
@@ -143534,7 +143965,7 @@ var StreamerServer = class {
|
|
|
143534
143965
|
}
|
|
143535
143966
|
async readCwdFromJsonl(filePath) {
|
|
143536
143967
|
return new Promise((resolve6) => {
|
|
143537
|
-
const rl = (0, import_readline4.createInterface)({ input: (0,
|
|
143968
|
+
const rl = (0, import_readline4.createInterface)({ input: (0, import_fs25.createReadStream)(filePath), crlfDelay: Infinity });
|
|
143538
143969
|
let found = false;
|
|
143539
143970
|
rl.on("line", (line) => {
|
|
143540
143971
|
if (found) return;
|
|
@@ -143570,10 +144001,22 @@ var StreamerServer = class {
|
|
|
143570
144001
|
const scanner = await this.getScanner(true);
|
|
143571
144002
|
const fromIndex = await scanner.getConversation(uuid3);
|
|
143572
144003
|
if (fromIndex) {
|
|
144004
|
+
if (this.ptyManager.hasSession(uuid3)) {
|
|
144005
|
+
return fromIndex;
|
|
144006
|
+
}
|
|
143573
144007
|
if (fromIndex.filePath && this.isConversationSnapshotStale(fromIndex)) {
|
|
143574
|
-
const
|
|
143575
|
-
|
|
143576
|
-
|
|
144008
|
+
const filePath2 = fromIndex.filePath;
|
|
144009
|
+
this.trackCacheWrite(
|
|
144010
|
+
this.refreshFileGuarded(scanner, filePath2).catch((err) => {
|
|
144011
|
+
this.log.warn("scanner.refreshFile: failed", {
|
|
144012
|
+
event: "scanner.refresh_failed",
|
|
144013
|
+
conversationId: uuid3,
|
|
144014
|
+
filePath: filePath2,
|
|
144015
|
+
trigger: "detail-swr",
|
|
144016
|
+
err
|
|
144017
|
+
});
|
|
144018
|
+
})
|
|
144019
|
+
);
|
|
143577
144020
|
}
|
|
143578
144021
|
return fromIndex;
|
|
143579
144022
|
}
|
|
@@ -143591,7 +144034,7 @@ var StreamerServer = class {
|
|
|
143591
144034
|
if (!conv.filePath) return false;
|
|
143592
144035
|
let mtimeMs = null;
|
|
143593
144036
|
try {
|
|
143594
|
-
mtimeMs = (0,
|
|
144037
|
+
mtimeMs = (0, import_fs25.statSync)(conv.filePath).mtimeMs;
|
|
143595
144038
|
} catch {
|
|
143596
144039
|
return false;
|
|
143597
144040
|
}
|
|
@@ -143649,9 +144092,13 @@ var StreamerServer = class {
|
|
|
143649
144092
|
return;
|
|
143650
144093
|
}
|
|
143651
144094
|
const etagSource = conversation;
|
|
144095
|
+
const indexedCount = etagSource.filePath && this.cache ? this.cache.getIndexedMessageCount(
|
|
144096
|
+
ConversationCache.conversationIdForFile(etagSource.filePath)
|
|
144097
|
+
) : 0;
|
|
144098
|
+
const etagMessageCount = Math.max(etagSource.messageCount, indexedCount);
|
|
143652
144099
|
const etag = computeConversationEtag({
|
|
143653
144100
|
filePath: etagSource.filePath,
|
|
143654
|
-
messageCount:
|
|
144101
|
+
messageCount: etagMessageCount,
|
|
143655
144102
|
timestamp: etagSource.timestamp
|
|
143656
144103
|
});
|
|
143657
144104
|
const isFirstPage = !url2.searchParams.has("before_index") && !url2.searchParams.has("anchor_index") && !url2.searchParams.has("after_index");
|
|
@@ -143668,12 +144115,14 @@ var StreamerServer = class {
|
|
|
143668
144115
|
let slice = filtered;
|
|
143669
144116
|
let fromIdx = 0;
|
|
143670
144117
|
let messagePagination;
|
|
144118
|
+
let indexTotal = null;
|
|
143671
144119
|
if (usePaging) {
|
|
143672
144120
|
const limit = Math.min(Math.max(intParam(url2, "msg_limit", 80), 1), 500);
|
|
143673
144121
|
let beforeIndex = total;
|
|
143674
144122
|
let scanLimit = limit;
|
|
143675
144123
|
let anchorIndex = null;
|
|
143676
144124
|
let newerPaging = false;
|
|
144125
|
+
let usedAfterIndex = false;
|
|
143677
144126
|
if (url2.searchParams.has("before_index")) {
|
|
143678
144127
|
beforeIndex = intParam(url2, "before_index", total);
|
|
143679
144128
|
beforeIndex = Math.min(Math.max(beforeIndex, 0), total);
|
|
@@ -143682,6 +144131,7 @@ var StreamerServer = class {
|
|
|
143682
144131
|
beforeIndex = Math.min(total, from + limit);
|
|
143683
144132
|
scanLimit = beforeIndex - from;
|
|
143684
144133
|
newerPaging = true;
|
|
144134
|
+
usedAfterIndex = true;
|
|
143685
144135
|
} else if (hasAnchor) {
|
|
143686
144136
|
anchorIndex = Math.min(
|
|
143687
144137
|
Math.max(intParam(url2, "anchor_index", 0), 0),
|
|
@@ -143691,9 +144141,34 @@ var StreamerServer = class {
|
|
|
143691
144141
|
beforeIndex = Math.min(total, from + limit);
|
|
143692
144142
|
newerPaging = true;
|
|
143693
144143
|
}
|
|
143694
|
-
const
|
|
143695
|
-
const
|
|
143696
|
-
|
|
144144
|
+
const isTailRequest = !url2.searchParams.has("before_index") && !hasAfter && !hasAnchor;
|
|
144145
|
+
const indexFilePath = conversation.filePath;
|
|
144146
|
+
if (isTailRequest && indexFilePath && this.cache) {
|
|
144147
|
+
const indexed = this.cache.getIndexedMessageCount(
|
|
144148
|
+
ConversationCache.conversationIdForFile(indexFilePath)
|
|
144149
|
+
);
|
|
144150
|
+
if (indexed > beforeIndex) {
|
|
144151
|
+
beforeIndex = indexed;
|
|
144152
|
+
}
|
|
144153
|
+
}
|
|
144154
|
+
const windowStart = Math.max(0, beforeIndex - scanLimit);
|
|
144155
|
+
const indexWindow = scanLimit > 0 && !hasAnchor && indexFilePath && this.cache ? this.cache.readMessageWindow(indexFilePath, windowStart, beforeIndex) : null;
|
|
144156
|
+
if (!indexWindow && indexFilePath && this.cache && !hasAnchor) {
|
|
144157
|
+
this.trackCacheWrite(
|
|
144158
|
+
this.cache.backfillIndex(indexFilePath).catch((err) => {
|
|
144159
|
+
this.log.warn("offset-index.backfill_failed", {
|
|
144160
|
+
event: "offset_index.backfill_failed",
|
|
144161
|
+
conversationId: id,
|
|
144162
|
+
filePath: indexFilePath,
|
|
144163
|
+
err
|
|
144164
|
+
});
|
|
144165
|
+
})
|
|
144166
|
+
);
|
|
144167
|
+
}
|
|
144168
|
+
const pagedScanner = !indexWindow && this.scannerReady ? await this.getScanner(true) : null;
|
|
144169
|
+
const page = indexWindow ?? (scanLimit > 0 && pagedScanner && typeof pagedScanner.getConversationPage === "function" ? await pagedScanner.getConversationPage(id, { beforeIndex, limit: scanLimit }) : null);
|
|
144170
|
+
if (indexWindow) indexTotal = indexWindow.total;
|
|
144171
|
+
const start = page?.fromIndex ?? windowStart;
|
|
143697
144172
|
slice = page?.messages ?? filtered.slice(start, beforeIndex);
|
|
143698
144173
|
fromIdx = start;
|
|
143699
144174
|
const effectiveTotal = page?.total ?? total;
|
|
@@ -143709,6 +144184,9 @@ var StreamerServer = class {
|
|
|
143709
144184
|
messagePagination.has_more_newer = beforeIndex < effectiveTotal;
|
|
143710
144185
|
messagePagination.next_after_index = beforeIndex < effectiveTotal ? beforeIndex : null;
|
|
143711
144186
|
}
|
|
144187
|
+
if (usedAfterIndex) {
|
|
144188
|
+
messagePagination.etag = etag;
|
|
144189
|
+
}
|
|
143712
144190
|
}
|
|
143713
144191
|
const messagesPayload = slice.map((m2, localIdx) => {
|
|
143714
144192
|
const content = [];
|
|
@@ -143750,6 +144228,8 @@ var StreamerServer = class {
|
|
|
143750
144228
|
const cachedConvMeta = this.cache?.getMetaById(id);
|
|
143751
144229
|
const convProvider = coerceProviderForRunner(conv.provider ?? cachedConvMeta?.provider);
|
|
143752
144230
|
const availability = classifyResumability(conv.projectPath);
|
|
144231
|
+
const metaMessageCount = indexTotal != null && indexTotal > conv.messageCount ? indexTotal : conv.messageCount;
|
|
144232
|
+
const metaLastUpdatedAt = indexTotal != null && indexTotal > conv.messageCount ? slice.at(-1)?.timestamp ?? conv.timestamp : conv.timestamp;
|
|
143753
144233
|
const body = {
|
|
143754
144234
|
meta: {
|
|
143755
144235
|
id,
|
|
@@ -143757,8 +144237,8 @@ var StreamerServer = class {
|
|
|
143757
144237
|
project_name: conv.projectName,
|
|
143758
144238
|
project_path: conv.projectPath,
|
|
143759
144239
|
file_path: conv.filePath,
|
|
143760
|
-
last_updated_at:
|
|
143761
|
-
message_count:
|
|
144240
|
+
last_updated_at: metaLastUpdatedAt,
|
|
144241
|
+
message_count: metaMessageCount,
|
|
143762
144242
|
last_prompt: conv.lastPrompt ?? void 0,
|
|
143763
144243
|
provider: convProvider,
|
|
143764
144244
|
resumable: isProviderResumable(convProvider, availability.resumable),
|
|
@@ -143921,7 +144401,7 @@ var StreamerServer = class {
|
|
|
143921
144401
|
handleGetSession(sessionId, res) {
|
|
143922
144402
|
const session = this.sessionStore.get(sessionId, this.ptyAttachedIds());
|
|
143923
144403
|
if (session) {
|
|
143924
|
-
if (!(0,
|
|
144404
|
+
if (!(0, import_fs25.existsSync)(session.projectPath)) {
|
|
143925
144405
|
session.failureReason = `Project directory not found: ${session.projectPath}`;
|
|
143926
144406
|
}
|
|
143927
144407
|
json2(res, 200, session);
|
|
@@ -144478,11 +144958,11 @@ var StreamerServer = class {
|
|
|
144478
144958
|
cleanup();
|
|
144479
144959
|
return;
|
|
144480
144960
|
}
|
|
144481
|
-
let resolvedFilePath = (0,
|
|
144482
|
-
if (!resolvedFilePath && (0,
|
|
144961
|
+
let resolvedFilePath = (0, import_fs25.existsSync)(filePath) ? filePath : null;
|
|
144962
|
+
if (!resolvedFilePath && (0, import_fs25.existsSync)(projectsDir)) {
|
|
144483
144963
|
try {
|
|
144484
144964
|
const now = Date.now();
|
|
144485
|
-
const recent = (0,
|
|
144965
|
+
const recent = (0, import_fs25.readdirSync)(projectsDir).filter((f2) => f2.endsWith(".jsonl")).map((f2) => ({ f: f2, mtime: (0, import_fs25.statSync)((0, import_path22.join)(projectsDir, f2)).mtimeMs })).filter(({ mtime }) => now - mtime < 5e3).sort((a, b2) => b2.mtime - a.mtime)[0];
|
|
144486
144966
|
if (recent) resolvedFilePath = (0, import_path22.join)(projectsDir, recent.f);
|
|
144487
144967
|
} catch {
|
|
144488
144968
|
}
|
|
@@ -144492,7 +144972,7 @@ var StreamerServer = class {
|
|
|
144492
144972
|
this.sessionFileMap.set(sessionId, resolvedFilePath);
|
|
144493
144973
|
this.fileWatcher.watch(resolvedFilePath);
|
|
144494
144974
|
try {
|
|
144495
|
-
const existing = (0,
|
|
144975
|
+
const existing = (0, import_fs25.readFileSync)(resolvedFilePath, "utf8").split("\n").filter(Boolean);
|
|
144496
144976
|
if (existing.length > 0) {
|
|
144497
144977
|
this.wsHub.broadcast({ type: "conversation_events", sessionId, lines: existing });
|
|
144498
144978
|
for (const line of existing) {
|
|
@@ -144518,7 +144998,7 @@ var StreamerServer = class {
|
|
|
144518
144998
|
if (this.sessionFileMap.has(sessionId)) return;
|
|
144519
144999
|
try {
|
|
144520
145000
|
require("fs").mkdirSync(projectsDir, { recursive: true });
|
|
144521
|
-
watcher = (0,
|
|
145001
|
+
watcher = (0, import_fs25.watch)(projectsDir, tryWire);
|
|
144522
145002
|
watcher.on("error", cleanup);
|
|
144523
145003
|
} catch {
|
|
144524
145004
|
}
|
|
@@ -144547,7 +145027,7 @@ var StreamerServer = class {
|
|
|
144547
145027
|
};
|
|
144548
145028
|
const matchesProjectPath = (candidatePath) => {
|
|
144549
145029
|
try {
|
|
144550
|
-
const firstLine = (0,
|
|
145030
|
+
const firstLine = (0, import_fs25.readFileSync)(candidatePath, "utf8").split("\n", 1)[0];
|
|
144551
145031
|
if (!firstLine) return null;
|
|
144552
145032
|
const parsed = JSON.parse(firstLine);
|
|
144553
145033
|
if (parsed?.type !== "session_meta") return null;
|
|
@@ -144576,15 +145056,15 @@ var StreamerServer = class {
|
|
|
144576
145056
|
);
|
|
144577
145057
|
for (const root of this.codexRoots) {
|
|
144578
145058
|
const sessionsDir = (0, import_path22.join)(root, dateDir);
|
|
144579
|
-
if (!(0,
|
|
145059
|
+
if (!(0, import_fs25.existsSync)(sessionsDir)) continue;
|
|
144580
145060
|
let candidateFiles;
|
|
144581
145061
|
try {
|
|
144582
|
-
candidateFiles = (0,
|
|
145062
|
+
candidateFiles = (0, import_fs25.readdirSync)(sessionsDir).filter((f2) => f2.endsWith(".jsonl"));
|
|
144583
145063
|
} catch {
|
|
144584
145064
|
continue;
|
|
144585
145065
|
}
|
|
144586
145066
|
const nowMs = Date.now();
|
|
144587
|
-
const recentCandidates = candidateFiles.map((f2) => ({ f: f2, mtime: (0,
|
|
145067
|
+
const recentCandidates = candidateFiles.map((f2) => ({ f: f2, mtime: (0, import_fs25.statSync)((0, import_path22.join)(sessionsDir, f2)).mtimeMs })).filter(({ mtime }) => nowMs - mtime < 1e4).sort((a, b2) => b2.mtime - a.mtime);
|
|
144588
145068
|
for (const { f: f2 } of recentCandidates) {
|
|
144589
145069
|
const candidatePath = (0, import_path22.join)(sessionsDir, f2);
|
|
144590
145070
|
const match2 = matchesProjectPath(candidatePath);
|
|
@@ -144596,7 +145076,7 @@ var StreamerServer = class {
|
|
|
144596
145076
|
this.sessionFileMap.set(sessionId, candidatePath);
|
|
144597
145077
|
this.fileWatcher.watch(candidatePath);
|
|
144598
145078
|
try {
|
|
144599
|
-
const existing = (0,
|
|
145079
|
+
const existing = (0, import_fs25.readFileSync)(candidatePath, "utf8").split("\n").filter(Boolean);
|
|
144600
145080
|
if (existing.length > 0) {
|
|
144601
145081
|
this.wsHub.broadcast({ type: "conversation_events", sessionId, lines: existing });
|
|
144602
145082
|
for (const line of existing) {
|
|
@@ -144721,7 +145201,7 @@ var StreamerServer = class {
|
|
|
144721
145201
|
};
|
|
144722
145202
|
function classifyResumability(cwd) {
|
|
144723
145203
|
if (!cwd) return { resumable: true };
|
|
144724
|
-
if ((0,
|
|
145204
|
+
if ((0, import_fs25.existsSync)(cwd)) return { resumable: true };
|
|
144725
145205
|
const ranInWorktree = /\/\.worktrees\//.test(cwd) || /\/\.claude\/worktrees\//.test(cwd);
|
|
144726
145206
|
return {
|
|
144727
145207
|
resumable: false,
|
|
@@ -145040,7 +145520,7 @@ function isBrewInstall(scriptPath = process.argv[1] ?? "") {
|
|
|
145040
145520
|
var import_node_crypto4 = require("crypto");
|
|
145041
145521
|
var import_node_fs8 = require("fs");
|
|
145042
145522
|
var import_node_path10 = require("path");
|
|
145043
|
-
var
|
|
145523
|
+
var import_promises20 = require("stream/promises");
|
|
145044
145524
|
|
|
145045
145525
|
// src/updater/manifest.ts
|
|
145046
145526
|
init_zod();
|
|
@@ -145115,7 +145595,7 @@ async function downloadAndVerify(opts) {
|
|
|
145115
145595
|
}
|
|
145116
145596
|
});
|
|
145117
145597
|
try {
|
|
145118
|
-
await (0,
|
|
145598
|
+
await (0, import_promises20.pipeline)(res.body.pipeThrough(measured), out);
|
|
145119
145599
|
} catch (err) {
|
|
145120
145600
|
(0, import_node_fs8.rmSync)(targetPath, { force: true });
|
|
145121
145601
|
throw err;
|
|
@@ -145301,7 +145781,7 @@ var import_node_fs17 = require("fs");
|
|
|
145301
145781
|
|
|
145302
145782
|
// node_modules/tar/dist/esm/index.min.js
|
|
145303
145783
|
var import_events4 = __toESM(require("events"), 1);
|
|
145304
|
-
var
|
|
145784
|
+
var import_fs26 = __toESM(require("fs"), 1);
|
|
145305
145785
|
var import_node_events3 = require("events");
|
|
145306
145786
|
var import_node_stream3 = __toESM(require("stream"), 1);
|
|
145307
145787
|
var import_node_string_decoder = require("string_decoder");
|
|
@@ -145315,8 +145795,8 @@ var Ps = __toESM(require("zlib"), 1);
|
|
|
145315
145795
|
var import_zlib = __toESM(require("zlib"), 1);
|
|
145316
145796
|
var import_node_path15 = require("path");
|
|
145317
145797
|
var import_node_path16 = require("path");
|
|
145318
|
-
var import_fs26 = __toESM(require("fs"), 1);
|
|
145319
145798
|
var import_fs27 = __toESM(require("fs"), 1);
|
|
145799
|
+
var import_fs28 = __toESM(require("fs"), 1);
|
|
145320
145800
|
var import_path24 = __toESM(require("path"), 1);
|
|
145321
145801
|
var import_node_path17 = require("path");
|
|
145322
145802
|
var import_path25 = __toESM(require("path"), 1);
|
|
@@ -145325,11 +145805,11 @@ var import_node_assert = __toESM(require("assert"), 1);
|
|
|
145325
145805
|
var import_node_crypto5 = require("crypto");
|
|
145326
145806
|
var import_node_fs13 = __toESM(require("fs"), 1);
|
|
145327
145807
|
var import_node_path18 = __toESM(require("path"), 1);
|
|
145328
|
-
var
|
|
145808
|
+
var import_fs29 = __toESM(require("fs"), 1);
|
|
145329
145809
|
var import_node_fs14 = __toESM(require("fs"), 1);
|
|
145330
145810
|
var import_node_path19 = __toESM(require("path"), 1);
|
|
145331
145811
|
var import_node_fs15 = __toESM(require("fs"), 1);
|
|
145332
|
-
var
|
|
145812
|
+
var import_promises21 = __toESM(require("fs/promises"), 1);
|
|
145333
145813
|
var import_node_path20 = __toESM(require("path"), 1);
|
|
145334
145814
|
var import_node_path21 = require("path");
|
|
145335
145815
|
var import_node_fs16 = __toESM(require("fs"), 1);
|
|
@@ -145677,7 +146157,7 @@ var A = class extends import_node_events3.EventEmitter {
|
|
|
145677
146157
|
return Wr;
|
|
145678
146158
|
}
|
|
145679
146159
|
};
|
|
145680
|
-
var Jr =
|
|
146160
|
+
var Jr = import_fs26.default.writev;
|
|
145681
146161
|
var ht = /* @__PURE__ */ Symbol("_autoClose");
|
|
145682
146162
|
var H = /* @__PURE__ */ Symbol("_close");
|
|
145683
146163
|
var te = /* @__PURE__ */ Symbol("_ended");
|
|
@@ -145732,7 +146212,7 @@ var _t = class extends A {
|
|
|
145732
146212
|
throw new TypeError("this is a readable stream");
|
|
145733
146213
|
}
|
|
145734
146214
|
[at]() {
|
|
145735
|
-
|
|
146215
|
+
import_fs26.default.open(this[U2], "r", (t2, e) => this[Ht](t2, e));
|
|
145736
146216
|
}
|
|
145737
146217
|
[Ht](t2, e) {
|
|
145738
146218
|
t2 ? this[Ut](t2) : (this[u2] = e, this.emit("open", e), this[zt]());
|
|
@@ -145745,7 +146225,7 @@ var _t = class extends A {
|
|
|
145745
146225
|
this[j] = true;
|
|
145746
146226
|
let t2 = this[vi]();
|
|
145747
146227
|
if (t2.length === 0) return process.nextTick(() => this[Ii](null, 0, t2));
|
|
145748
|
-
|
|
146228
|
+
import_fs26.default.read(this[u2], t2, 0, t2.length, null, (e, i, r) => this[Ii](e, i, r));
|
|
145749
146229
|
}
|
|
145750
146230
|
}
|
|
145751
146231
|
[Ii](t2, e, i) {
|
|
@@ -145754,7 +146234,7 @@ var _t = class extends A {
|
|
|
145754
146234
|
[H]() {
|
|
145755
146235
|
if (this[ht] && typeof this[u2] == "number") {
|
|
145756
146236
|
let t2 = this[u2];
|
|
145757
|
-
this[u2] = void 0,
|
|
146237
|
+
this[u2] = void 0, import_fs26.default.close(t2, (e) => e ? this.emit("error", e) : this.emit("close"));
|
|
145758
146238
|
}
|
|
145759
146239
|
}
|
|
145760
146240
|
[Ut](t2) {
|
|
@@ -145782,7 +146262,7 @@ var Be = class extends _t {
|
|
|
145782
146262
|
[at]() {
|
|
145783
146263
|
let t2 = true;
|
|
145784
146264
|
try {
|
|
145785
|
-
this[Ht](null,
|
|
146265
|
+
this[Ht](null, import_fs26.default.openSync(this[U2], "r")), t2 = false;
|
|
145786
146266
|
} finally {
|
|
145787
146267
|
t2 && this[H]();
|
|
145788
146268
|
}
|
|
@@ -145793,7 +146273,7 @@ var Be = class extends _t {
|
|
|
145793
146273
|
if (!this[j]) {
|
|
145794
146274
|
this[j] = true;
|
|
145795
146275
|
do {
|
|
145796
|
-
let e = this[vi](), i = e.length === 0 ? 0 :
|
|
146276
|
+
let e = this[vi](), i = e.length === 0 ? 0 : import_fs26.default.readSync(this[u2], e, 0, e.length, null);
|
|
145797
146277
|
if (!this[ki](i, e)) break;
|
|
145798
146278
|
} while (true);
|
|
145799
146279
|
this[j] = false;
|
|
@@ -145806,7 +146286,7 @@ var Be = class extends _t {
|
|
|
145806
146286
|
[H]() {
|
|
145807
146287
|
if (this[ht] && typeof this[u2] == "number") {
|
|
145808
146288
|
let t2 = this[u2];
|
|
145809
|
-
this[u2] = void 0,
|
|
146289
|
+
this[u2] = void 0, import_fs26.default.closeSync(t2), this.emit("close");
|
|
145810
146290
|
}
|
|
145811
146291
|
}
|
|
145812
146292
|
};
|
|
@@ -145848,7 +146328,7 @@ var et = class extends import_events4.default {
|
|
|
145848
146328
|
this[H](), this[gt] = true, this.emit("error", t2);
|
|
145849
146329
|
}
|
|
145850
146330
|
[at]() {
|
|
145851
|
-
|
|
146331
|
+
import_fs26.default.open(this[U2], this[tt], this[ie], (t2, e) => this[Ht](t2, e));
|
|
145852
146332
|
}
|
|
145853
146333
|
[Ht](t2, e) {
|
|
145854
146334
|
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]());
|
|
@@ -145860,7 +146340,7 @@ var et = class extends import_events4.default {
|
|
|
145860
146340
|
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);
|
|
145861
146341
|
}
|
|
145862
146342
|
[ve](t2) {
|
|
145863
|
-
|
|
146343
|
+
import_fs26.default.write(this[u2], t2, 0, t2.length, this[ot], (e, i) => this[Pt](e, i));
|
|
145864
146344
|
}
|
|
145865
146345
|
[Pt](t2, e) {
|
|
145866
146346
|
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"))));
|
|
@@ -145876,7 +146356,7 @@ var et = class extends import_events4.default {
|
|
|
145876
146356
|
[H]() {
|
|
145877
146357
|
if (this[ht] && typeof this[u2] == "number") {
|
|
145878
146358
|
let t2 = this[u2];
|
|
145879
|
-
this[u2] = void 0,
|
|
146359
|
+
this[u2] = void 0, import_fs26.default.close(t2, (e) => e ? this.emit("error", e) : this.emit("close"));
|
|
145880
146360
|
}
|
|
145881
146361
|
}
|
|
145882
146362
|
};
|
|
@@ -145884,24 +146364,24 @@ var Wt = class extends et {
|
|
|
145884
146364
|
[at]() {
|
|
145885
146365
|
let t2;
|
|
145886
146366
|
if (this[Me] && this[tt] === "r+") try {
|
|
145887
|
-
t2 =
|
|
146367
|
+
t2 = import_fs26.default.openSync(this[U2], this[tt], this[ie]);
|
|
145888
146368
|
} catch (e) {
|
|
145889
146369
|
if (e?.code === "ENOENT") return this[tt] = "w", this[at]();
|
|
145890
146370
|
throw e;
|
|
145891
146371
|
}
|
|
145892
|
-
else t2 =
|
|
146372
|
+
else t2 = import_fs26.default.openSync(this[U2], this[tt], this[ie]);
|
|
145893
146373
|
this[Ht](null, t2);
|
|
145894
146374
|
}
|
|
145895
146375
|
[H]() {
|
|
145896
146376
|
if (this[ht] && typeof this[u2] == "number") {
|
|
145897
146377
|
let t2 = this[u2];
|
|
145898
|
-
this[u2] = void 0,
|
|
146378
|
+
this[u2] = void 0, import_fs26.default.closeSync(t2), this.emit("close");
|
|
145899
146379
|
}
|
|
145900
146380
|
}
|
|
145901
146381
|
[ve](t2) {
|
|
145902
146382
|
let e = true;
|
|
145903
146383
|
try {
|
|
145904
|
-
this[Pt](null,
|
|
146384
|
+
this[Pt](null, import_fs26.default.writeSync(this[u2], t2, 0, t2.length, this[ot])), e = false;
|
|
145905
146385
|
} finally {
|
|
145906
146386
|
if (e) try {
|
|
145907
146387
|
this[H]();
|
|
@@ -146819,7 +147299,7 @@ var de = class extends A {
|
|
|
146819
147299
|
return t2 === "error" && (this.#t = true), super.emit(t2, ...e);
|
|
146820
147300
|
}
|
|
146821
147301
|
[ss]() {
|
|
146822
|
-
|
|
147302
|
+
import_fs28.default.lstat(this.absolute, (t2, e) => {
|
|
146823
147303
|
if (t2) return this.emit("error", t2);
|
|
146824
147304
|
this[si](e);
|
|
146825
147305
|
});
|
|
@@ -146857,7 +147337,7 @@ var de = class extends A {
|
|
|
146857
147337
|
this.path.slice(-1) !== "/" && (this.path += "/"), this.stat.size = 0, this[fe](), this.end();
|
|
146858
147338
|
}
|
|
146859
147339
|
[is]() {
|
|
146860
|
-
|
|
147340
|
+
import_fs28.default.readlink(this.absolute, (t2, e) => {
|
|
146861
147341
|
if (t2) return this.emit("error", t2);
|
|
146862
147342
|
this[ns](e);
|
|
146863
147343
|
});
|
|
@@ -146880,7 +147360,7 @@ var de = class extends A {
|
|
|
146880
147360
|
this[os]();
|
|
146881
147361
|
}
|
|
146882
147362
|
[os]() {
|
|
146883
|
-
|
|
147363
|
+
import_fs28.default.open(this.absolute, "r", (t2, e) => {
|
|
146884
147364
|
if (t2) return this.emit("error", t2);
|
|
146885
147365
|
this[hs](e);
|
|
146886
147366
|
});
|
|
@@ -146895,14 +147375,14 @@ var de = class extends A {
|
|
|
146895
147375
|
[ii]() {
|
|
146896
147376
|
let { fd: t2, buf: e, offset: i, length: r, pos: n } = this;
|
|
146897
147377
|
if (t2 === void 0 || e === void 0) throw new Error("cannot read file without first opening");
|
|
146898
|
-
|
|
147378
|
+
import_fs28.default.read(t2, e, i, r, n, (o, h) => {
|
|
146899
147379
|
if (o) return this[pt](() => this.emit("error", o));
|
|
146900
147380
|
this[rs](h);
|
|
146901
147381
|
});
|
|
146902
147382
|
}
|
|
146903
147383
|
[pt](t2 = () => {
|
|
146904
147384
|
}) {
|
|
146905
|
-
this.fd !== void 0 &&
|
|
147385
|
+
this.fd !== void 0 && import_fs28.default.close(this.fd, t2);
|
|
146906
147386
|
}
|
|
146907
147387
|
[rs](t2) {
|
|
146908
147388
|
if (t2 <= 0 && this.remain > 0) {
|
|
@@ -146937,20 +147417,20 @@ var de = class extends A {
|
|
|
146937
147417
|
var ni = class extends de {
|
|
146938
147418
|
sync = true;
|
|
146939
147419
|
[ss]() {
|
|
146940
|
-
this[si](
|
|
147420
|
+
this[si](import_fs28.default.lstatSync(this.absolute));
|
|
146941
147421
|
}
|
|
146942
147422
|
[is]() {
|
|
146943
|
-
this[ns](
|
|
147423
|
+
this[ns](import_fs28.default.readlinkSync(this.absolute));
|
|
146944
147424
|
}
|
|
146945
147425
|
[os]() {
|
|
146946
|
-
this[hs](
|
|
147426
|
+
this[hs](import_fs28.default.openSync(this.absolute, "r"));
|
|
146947
147427
|
}
|
|
146948
147428
|
[ii]() {
|
|
146949
147429
|
let t2 = true;
|
|
146950
147430
|
try {
|
|
146951
147431
|
let { fd: e, buf: i, offset: r, length: n, pos: o } = this;
|
|
146952
147432
|
if (e === void 0 || i === void 0) throw new Error("fd and buf must be set in READ method");
|
|
146953
|
-
let h =
|
|
147433
|
+
let h = import_fs28.default.readSync(e, i, r, n, o);
|
|
146954
147434
|
this[rs](h), t2 = false;
|
|
146955
147435
|
} finally {
|
|
146956
147436
|
if (t2) try {
|
|
@@ -146965,7 +147445,7 @@ var ni = class extends de {
|
|
|
146965
147445
|
}
|
|
146966
147446
|
[pt](t2 = () => {
|
|
146967
147447
|
}) {
|
|
146968
|
-
this.fd !== void 0 &&
|
|
147448
|
+
this.fd !== void 0 && import_fs28.default.closeSync(this.fd), t2();
|
|
146969
147449
|
}
|
|
146970
147450
|
};
|
|
146971
147451
|
var oi = class extends A {
|
|
@@ -147296,7 +147776,7 @@ var wt = class extends A {
|
|
|
147296
147776
|
[ds](t2) {
|
|
147297
147777
|
t2.pending = true, this[G2] += 1;
|
|
147298
147778
|
let e = this.follow ? "stat" : "lstat";
|
|
147299
|
-
|
|
147779
|
+
import_fs27.default[e](t2.absolute, (i, r) => {
|
|
147300
147780
|
t2.pending = false, this[G2] -= 1, i ? this.emit("error", i) : this[li](t2, r);
|
|
147301
147781
|
});
|
|
147302
147782
|
}
|
|
@@ -147310,7 +147790,7 @@ var wt = class extends A {
|
|
|
147310
147790
|
this[Ft]();
|
|
147311
147791
|
}
|
|
147312
147792
|
[ms](t2) {
|
|
147313
|
-
t2.pending = true, this[G2] += 1,
|
|
147793
|
+
t2.pending = true, this[G2] += 1, import_fs27.default.readdir(t2.absolute, (e, i) => {
|
|
147314
147794
|
if (t2.pending = false, this[G2] -= 1, e) return this.emit("error", e);
|
|
147315
147795
|
this[fi](t2, i);
|
|
147316
147796
|
});
|
|
@@ -147411,10 +147891,10 @@ var kt = class extends wt {
|
|
|
147411
147891
|
}
|
|
147412
147892
|
[ds](t2) {
|
|
147413
147893
|
let e = this.follow ? "statSync" : "lstatSync";
|
|
147414
|
-
this[li](t2,
|
|
147894
|
+
this[li](t2, import_fs27.default[e](t2.absolute));
|
|
147415
147895
|
}
|
|
147416
147896
|
[ms](t2) {
|
|
147417
|
-
this[fi](t2,
|
|
147897
|
+
this[fi](t2, import_fs27.default.readdirSync(t2.absolute));
|
|
147418
147898
|
}
|
|
147419
147899
|
[di](t2) {
|
|
147420
147900
|
let e = t2.entry, i = this.zip;
|
|
@@ -147465,8 +147945,8 @@ var Qn = K2(Vn, $n, Xn, qn, (s3, t2) => {
|
|
|
147465
147945
|
});
|
|
147466
147946
|
var Jn = process.env.__FAKE_PLATFORM__ || process.platform;
|
|
147467
147947
|
var Er = Jn === "win32";
|
|
147468
|
-
var { O_CREAT: wr, O_NOFOLLOW: mr, O_TRUNC: Sr, O_WRONLY: yr } =
|
|
147469
|
-
var Rr = Number(process.env.__FAKE_FS_O_FILENAME__) ||
|
|
147948
|
+
var { O_CREAT: wr, O_NOFOLLOW: mr, O_TRUNC: Sr, O_WRONLY: yr } = import_fs29.default.constants;
|
|
147949
|
+
var Rr = Number(process.env.__FAKE_FS_O_FILENAME__) || import_fs29.default.constants.UV_FS_O_FILEMAP || 0;
|
|
147470
147950
|
var jn = Er && !!Rr;
|
|
147471
147951
|
var to = 512 * 1024;
|
|
147472
147952
|
var eo = Rr | Sr | wr | yr;
|
|
@@ -147561,7 +148041,7 @@ var gr = (s3, t2, e) => {
|
|
|
147561
148041
|
E2 ? e(E2) : x2 && a ? Es(x2, o, h, (Le) => S2(Le)) : n ? import_node_fs15.default.chmod(s3, r, e) : e();
|
|
147562
148042
|
};
|
|
147563
148043
|
if (s3 === d) return no(s3, S2);
|
|
147564
|
-
if (l) return
|
|
148044
|
+
if (l) return import_promises21.default.mkdir(s3, { mode: r, recursive: true }).then((E2) => S2(null, E2 ?? void 0), S2);
|
|
147565
148045
|
let D2 = f(import_node_path20.default.relative(d, s3)).split("/");
|
|
147566
148046
|
Ss(d, D2, r, c, d, void 0, S2);
|
|
147567
148047
|
};
|