@threadbase-sh/streamer 1.24.1 → 1.24.3

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/index.d.cts CHANGED
@@ -202,6 +202,7 @@ type WSMessage = {
202
202
  type: "permission";
203
203
  sessionId: string;
204
204
  prompt?: string;
205
+ detail?: string;
205
206
  options: PermissionOption[];
206
207
  cursor?: number;
207
208
  } | {
@@ -326,6 +327,7 @@ interface PTYManagerOptions {
326
327
  onReady?: (session: ManagedSession) => void;
327
328
  onPermissionChange?: (sessionId: string, gate: {
328
329
  prompt?: string;
330
+ detail?: string;
329
331
  options: PermissionOption[];
330
332
  cursor?: number;
331
333
  } | null) => void;
@@ -832,6 +834,7 @@ declare class StreamerServer {
832
834
  private pendingQuestionKey;
833
835
  private pendingPermission;
834
836
  private scanner;
837
+ private scannerPersistenceDisabled;
835
838
  private allScanners;
836
839
  private scannerReady;
837
840
  private scannerStale;
@@ -911,6 +914,7 @@ declare class StreamerServer {
911
914
  private handleGetPopularProjects;
912
915
  private buildStatCache;
913
916
  private codexScanOpts;
917
+ private newScanner;
914
918
  private getScanner;
915
919
  private getFreshScanner;
916
920
  private findJsonlPath;
package/dist/index.d.ts CHANGED
@@ -202,6 +202,7 @@ type WSMessage = {
202
202
  type: "permission";
203
203
  sessionId: string;
204
204
  prompt?: string;
205
+ detail?: string;
205
206
  options: PermissionOption[];
206
207
  cursor?: number;
207
208
  } | {
@@ -326,6 +327,7 @@ interface PTYManagerOptions {
326
327
  onReady?: (session: ManagedSession) => void;
327
328
  onPermissionChange?: (sessionId: string, gate: {
328
329
  prompt?: string;
330
+ detail?: string;
329
331
  options: PermissionOption[];
330
332
  cursor?: number;
331
333
  } | null) => void;
@@ -832,6 +834,7 @@ declare class StreamerServer {
832
834
  private pendingQuestionKey;
833
835
  private pendingPermission;
834
836
  private scanner;
837
+ private scannerPersistenceDisabled;
835
838
  private allScanners;
836
839
  private scannerReady;
837
840
  private scannerStale;
@@ -911,6 +914,7 @@ declare class StreamerServer {
911
914
  private handleGetPopularProjects;
912
915
  private buildStatCache;
913
916
  private codexScanOpts;
917
+ private newScanner;
914
918
  private getScanner;
915
919
  private getFreshScanner;
916
920
  private findJsonlPath;
package/dist/index.js CHANGED
@@ -1145,14 +1145,41 @@ function scrapePermissionGate(lines) {
1145
1145
  }
1146
1146
  if (options.length === 0) return null;
1147
1147
  let prompt;
1148
+ let promptLine = -1;
1148
1149
  for (let i = firstOptionLine - 1; i >= 0; i--) {
1149
1150
  const t = stripGutter(lines[i]).trim();
1150
1151
  if (t.length === 0) continue;
1151
1152
  if (BOX_ONLY_RE.test(lines[i].trim()) || FOOTER_RE.test(t) || PROMPT_ARROW_RE.test(t)) continue;
1152
1153
  prompt = t || void 0;
1154
+ promptLine = i;
1153
1155
  break;
1154
1156
  }
1155
- return { ...prompt ? { prompt } : {}, options, ...cursor !== void 0 ? { cursor } : {} };
1157
+ const detail = promptLine > 0 ? scrapeDetail(lines, promptLine) : void 0;
1158
+ return {
1159
+ ...prompt ? { prompt } : {},
1160
+ ...detail ? { detail } : {},
1161
+ options,
1162
+ ...cursor !== void 0 ? { cursor } : {}
1163
+ };
1164
+ }
1165
+ var MAX_DETAIL_LINES = 6;
1166
+ function scrapeDetail(lines, promptLine) {
1167
+ const collected = [];
1168
+ let blankRun = 0;
1169
+ for (let i = promptLine - 1; i >= 0; i--) {
1170
+ const t = stripGutter(lines[i]).trim();
1171
+ if (BOX_ONLY_RE.test(t)) break;
1172
+ if (t.length === 0) {
1173
+ blankRun++;
1174
+ if (blankRun >= 2) break;
1175
+ continue;
1176
+ }
1177
+ blankRun = 0;
1178
+ if (FOOTER_RE.test(t) || PROMPT_ARROW_RE.test(t)) continue;
1179
+ collected.push(t);
1180
+ if (collected.length >= MAX_DETAIL_LINES) break;
1181
+ }
1182
+ return collected.length > 0 ? collected.reverse().join("\n") : void 0;
1156
1183
  }
1157
1184
 
1158
1185
  // src/services/questions/detectQuestionFromScreen.ts
@@ -4788,6 +4815,11 @@ var StreamerServer = class {
4788
4815
  // /input { keys }. Cleared when the gate closes.
4789
4816
  pendingPermission = /* @__PURE__ */ new Map();
4790
4817
  scanner = null;
4818
+ // Set when better-sqlite3 is unusable (e.g. node ABI mismatch made
4819
+ // ConversationCache.open throw). All scanners are then built with
4820
+ // persistent: false so requests serve from disk instead of 500ing on
4821
+ // every touch of the scanner's own SQLite index.
4822
+ scannerPersistenceDisabled = false;
4791
4823
  // Tracks every ConversationScanner ever created so close() can shut them all
4792
4824
  // down and release SQLite handles (open handles block temp-dir deletion on Windows).
4793
4825
  allScanners = /* @__PURE__ */ new Set();
@@ -5287,11 +5319,12 @@ var StreamerServer = class {
5287
5319
  const message = err instanceof Error ? err.message : String(err);
5288
5320
  const abiMismatch = message.includes("NODE_MODULE_VERSION") || message.includes("was compiled against a different Node.js version");
5289
5321
  this.log.error(
5290
- `ConversationCache failed to open \u2014 running WITHOUT cache; /api/conversations, /api/conversations/count and /project-chats will 500.` + (abiMismatch ? ` Fix: npm rebuild better-sqlite3` : "") + ` (${message})`,
5322
+ `ConversationCache failed to open \u2014 running WITHOUT cache; /api/conversations, /api/conversations/count and /project-chats serve from disk (degraded).` + (abiMismatch ? ` Fix: npm rebuild better-sqlite3` : "") + ` (${message})`,
5291
5323
  { error: message, abiMismatch, event: "cache.open_failed" }
5292
5324
  );
5325
+ this.scannerPersistenceDisabled = true;
5293
5326
  }
5294
- const warmupScanner = new ConversationScanner();
5327
+ const warmupScanner = this.newScanner();
5295
5328
  this.allScanners.add(warmupScanner);
5296
5329
  const warmupStatCache = this.buildStatCache(null);
5297
5330
  const shouldEmitProgress = createScanProgressThrottle();
@@ -5308,6 +5341,10 @@ var StreamerServer = class {
5308
5341
  }
5309
5342
  }
5310
5343
  }).then(async () => {
5344
+ if (!this.scannerReady && !this.scanner) {
5345
+ this.scanner = warmupScanner;
5346
+ this.scannerReady = Promise.resolve();
5347
+ }
5311
5348
  if (!this.cache) return;
5312
5349
  const metas = [...warmupScanner.getMetadataCache().values()];
5313
5350
  const upsertedIds = new Set(this.cache.upsertFromScannerMeta(metas));
@@ -5356,10 +5393,6 @@ var StreamerServer = class {
5356
5393
  event: "cache.warmup_failed"
5357
5394
  });
5358
5395
  }).finally(() => {
5359
- if (!this.scannerReady && !this.scanner) {
5360
- this.scanner = warmupScanner;
5361
- this.scannerReady = Promise.resolve();
5362
- }
5363
5396
  this.cacheReady = true;
5364
5397
  this.wsHub.broadcast({ type: "cache_ready" });
5365
5398
  resolveWarm();
@@ -5742,6 +5775,11 @@ var StreamerServer = class {
5742
5775
  // this — its per-file refreshFile (in findConversationByUuid) already
5743
5776
  // reconciles the one conversation being requested, so paying a full-tree
5744
5777
  // rescan just because some OTHER file changed is the stall this avoids.
5778
+ newScanner() {
5779
+ return new ConversationScanner(
5780
+ this.scannerPersistenceDisabled ? { persistent: false } : void 0
5781
+ );
5782
+ }
5745
5783
  async getScanner(skipStaleRescan = false) {
5746
5784
  if (this.scannerReady) {
5747
5785
  await this.scannerReady;
@@ -5758,14 +5796,20 @@ var StreamerServer = class {
5758
5796
  }
5759
5797
  this.scannerStale = false;
5760
5798
  const statCache = this.buildStatCache(this.scanner);
5761
- this.scanner = new ConversationScanner();
5799
+ this.scanner = this.newScanner();
5762
5800
  this.allScanners.add(this.scanner);
5763
5801
  this.scannerReady = this.scanner.scan({
5764
5802
  ...this.scanProfiles ? { profiles: this.scanProfiles } : {},
5765
5803
  ...this.codexScanOpts(),
5766
5804
  ...statCache ? { statCache } : {}
5767
5805
  });
5768
- await this.scannerReady;
5806
+ try {
5807
+ await this.scannerReady;
5808
+ } catch (err) {
5809
+ this.scanner = null;
5810
+ this.scannerReady = null;
5811
+ throw err;
5812
+ }
5769
5813
  const scanner = this.scanner;
5770
5814
  if (!scanner) return this.getScanner();
5771
5815
  return scanner;
@@ -5820,7 +5864,7 @@ var StreamerServer = class {
5820
5864
  const filePath2 = this.findJsonlPath(uuid);
5821
5865
  if (filePath2) {
5822
5866
  const account = this.cache?.getMetaById(uuid)?.account ?? void 0;
5823
- const coldScanner = this.scanner ?? new ConversationScanner();
5867
+ const coldScanner = this.scanner ?? this.newScanner();
5824
5868
  const page = await coldScanner.parseSingleFilePage(filePath2, account, {
5825
5869
  limit: Number.MAX_SAFE_INTEGER
5826
5870
  });
@@ -6302,6 +6346,7 @@ var StreamerServer = class {
6302
6346
  type: "permission",
6303
6347
  sessionId,
6304
6348
  ...gate.prompt ? { prompt: gate.prompt } : {},
6349
+ ...gate.detail ? { detail: gate.detail } : {},
6305
6350
  options: gate.options,
6306
6351
  ...gate.cursor !== void 0 ? { cursor: gate.cursor } : {}
6307
6352
  });