@threadbase-sh/streamer 1.36.2 → 1.36.4
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 +44 -8
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +44 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +44 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -141048,6 +141048,9 @@ var import_path22 = require("path");
|
|
|
141048
141048
|
init_logger();
|
|
141049
141049
|
|
|
141050
141050
|
// src/services/questions/detectPermissionGate.ts
|
|
141051
|
+
function permissionContentKey(gate) {
|
|
141052
|
+
return `${gate.prompt ?? ""}::${gate.detail ?? ""}::${gate.options.map((o) => `${o.index}.${o.label}`).join(",")}::${gate.cursor ?? ""}`;
|
|
141053
|
+
}
|
|
141051
141054
|
var OSC_777_PERMISSION_RE = /\x1b\]777;notify;Claude Code;[^\x07\x1b]*needs your permission/;
|
|
141052
141055
|
var OSC_777_WAITING_RE = /\x1b\]777;notify;Claude Code;[^\x07\x1b]*waiting for your input/;
|
|
141053
141056
|
function hasPermissionOsc(rawData) {
|
|
@@ -144070,6 +144073,11 @@ var StreamerServer = class {
|
|
|
144070
144073
|
// to pendingQuestions; mobile answers it by sending the option index via
|
|
144071
144074
|
// /input { keys }. Cleared when the gate closes.
|
|
144072
144075
|
pendingPermission = /* @__PURE__ */ new Map();
|
|
144076
|
+
// Content key (prompt + detail + options + cursor) of the permission gate
|
|
144077
|
+
// currently broadcast for a session — mirrors pendingQuestionKey so a PTY
|
|
144078
|
+
// repaint of the same gate doesn't re-broadcast on every tick. Cleared
|
|
144079
|
+
// alongside pendingPermission.
|
|
144080
|
+
pendingPermissionKey = /* @__PURE__ */ new Map();
|
|
144073
144081
|
scanner = null;
|
|
144074
144082
|
// Set when better-sqlite3 is unusable (e.g. node ABI mismatch made
|
|
144075
144083
|
// ConversationCache.open throw), or when config.scannerPersistent is false
|
|
@@ -144085,6 +144093,10 @@ var StreamerServer = class {
|
|
|
144085
144093
|
// Set by onConversationChanged while a scan is in-flight; getScanner() does
|
|
144086
144094
|
// a single rescan after the current one completes instead of restarting it.
|
|
144087
144095
|
scannerStale = false;
|
|
144096
|
+
// Single-flight guard for the background disk reconcile: a burst of list
|
|
144097
|
+
// polls during active session writes shares one rescan instead of queueing
|
|
144098
|
+
// a full rescan per request.
|
|
144099
|
+
conversationReconcileInFlight = null;
|
|
144088
144100
|
// Single-flight + TTL guard around scanner.refreshFile (see refreshFileGuarded).
|
|
144089
144101
|
// A live file's mtime is always newer than the snapshot, so an unguarded
|
|
144090
144102
|
// refresh fires on every request and re-parses the whole file from byte 0.
|
|
@@ -144363,6 +144375,7 @@ var StreamerServer = class {
|
|
|
144363
144375
|
this.cancelPendingQuestion(session.id);
|
|
144364
144376
|
}
|
|
144365
144377
|
this.pendingPermission.delete(session.id);
|
|
144378
|
+
this.pendingPermissionKey.delete(session.id);
|
|
144366
144379
|
this.contendedSessions.delete(session.id);
|
|
144367
144380
|
this.rememberSelfPtyEnded(session.id);
|
|
144368
144381
|
}
|
|
@@ -145102,9 +145115,9 @@ var StreamerServer = class {
|
|
|
145102
145115
|
* the automatic freshness path when the directory watcher marked the scanner
|
|
145103
145116
|
* stale or shouldRefreshProjectsFromHdd detected disk drift.
|
|
145104
145117
|
*/
|
|
145105
|
-
async reconcileConversationsCacheFromDisk() {
|
|
145118
|
+
async reconcileConversationsCacheFromDisk(onProgress) {
|
|
145106
145119
|
if (!this.cache) return;
|
|
145107
|
-
const scanner = await this.rescanForRefresh();
|
|
145120
|
+
const scanner = await this.rescanForRefresh(onProgress);
|
|
145108
145121
|
const metas = [...scanner.getMetadataCache().values()];
|
|
145109
145122
|
try {
|
|
145110
145123
|
this.cache.upsertFromScannerMeta(metas);
|
|
@@ -145132,6 +145145,18 @@ var StreamerServer = class {
|
|
|
145132
145145
|
);
|
|
145133
145146
|
}
|
|
145134
145147
|
}
|
|
145148
|
+
// Reconcile the cache from disk without blocking the caller. Single-flighted
|
|
145149
|
+
// so a burst of list polls during active session writes shares one rescan
|
|
145150
|
+
// rather than queueing a full rescan each; tracked so close() awaits the
|
|
145151
|
+
// in-flight cache write before shutting the DB.
|
|
145152
|
+
startBackgroundConversationReconcile() {
|
|
145153
|
+
if (this.conversationReconcileInFlight) return;
|
|
145154
|
+
const task = this.reconcileConversationsCacheFromDisk().finally(() => {
|
|
145155
|
+
this.conversationReconcileInFlight = null;
|
|
145156
|
+
});
|
|
145157
|
+
this.conversationReconcileInFlight = task;
|
|
145158
|
+
this.trackCacheWrite(task);
|
|
145159
|
+
}
|
|
145135
145160
|
shouldAutoReconcileConversationList() {
|
|
145136
145161
|
if (!this.cache) return false;
|
|
145137
145162
|
if (this.scannerStale) return true;
|
|
@@ -145149,13 +145174,19 @@ var StreamerServer = class {
|
|
|
145149
145174
|
const providerFilter = url2.searchParams.get("provider") ?? void 0;
|
|
145150
145175
|
const bustCache = url2.searchParams.get("refresh") === "1";
|
|
145151
145176
|
if (this.cache && (bustCache || this.shouldAutoReconcileConversationList())) {
|
|
145152
|
-
|
|
145177
|
+
const canServeStale = !bustCache && this.cache.listConversations({ limit: 0, offset: 0 }).total > 0;
|
|
145178
|
+
if (canServeStale) {
|
|
145179
|
+
this.startBackgroundConversationReconcile();
|
|
145180
|
+
} else {
|
|
145181
|
+
const shouldEmitProgress = createScanProgressThrottle();
|
|
145153
145182
|
await this.withWarmup(
|
|
145154
145183
|
"conversation_refresh",
|
|
145155
|
-
() => this.reconcileConversationsCacheFromDisk()
|
|
145184
|
+
() => this.reconcileConversationsCacheFromDisk((scanned, total2) => {
|
|
145185
|
+
if (shouldEmitProgress(scanned, total2)) {
|
|
145186
|
+
this.wsHub.broadcast({ type: "scan_progress", scanned, total: total2 });
|
|
145187
|
+
}
|
|
145188
|
+
})
|
|
145156
145189
|
);
|
|
145157
|
-
} else {
|
|
145158
|
-
await this.reconcileConversationsCacheFromDisk();
|
|
145159
145190
|
}
|
|
145160
145191
|
}
|
|
145161
145192
|
if (this.cache) {
|
|
@@ -145391,7 +145422,7 @@ var StreamerServer = class {
|
|
|
145391
145422
|
// getFreshScanner() this does NOT discard the warm scanner. scannerReady is
|
|
145392
145423
|
// only ever reassigned to a live scan promise (never nulled mid-scan), so the
|
|
145393
145424
|
// getScanner() anti-infinite-loop guard is preserved.
|
|
145394
|
-
async rescanForRefresh() {
|
|
145425
|
+
async rescanForRefresh(onProgress) {
|
|
145395
145426
|
if (this.scannerReady) await this.scannerReady;
|
|
145396
145427
|
this.scannerStale = false;
|
|
145397
145428
|
if (!this.scanner) {
|
|
@@ -145402,7 +145433,8 @@ var StreamerServer = class {
|
|
|
145402
145433
|
this.scannerReady = scanner.scan({
|
|
145403
145434
|
...this.scanProfiles ? { profiles: this.scanProfiles } : {},
|
|
145404
145435
|
...this.codexScanOpts(),
|
|
145405
|
-
fullRescan: true
|
|
145436
|
+
fullRescan: true,
|
|
145437
|
+
...onProgress ? { onProgress } : {}
|
|
145406
145438
|
});
|
|
145407
145439
|
await this.scannerReady;
|
|
145408
145440
|
return scanner;
|
|
@@ -146380,10 +146412,14 @@ var StreamerServer = class {
|
|
|
146380
146412
|
if (gate === null) {
|
|
146381
146413
|
if (!this.pendingPermission.has(sessionId)) return;
|
|
146382
146414
|
this.pendingPermission.delete(sessionId);
|
|
146415
|
+
this.pendingPermissionKey.delete(sessionId);
|
|
146383
146416
|
this.wsHub.broadcast({ type: "permission_cancelled", sessionId });
|
|
146384
146417
|
return;
|
|
146385
146418
|
}
|
|
146419
|
+
const key = permissionContentKey(gate);
|
|
146420
|
+
if (this.pendingPermissionKey.get(sessionId) === key) return;
|
|
146386
146421
|
this.pendingPermission.set(sessionId, gate);
|
|
146422
|
+
this.pendingPermissionKey.set(sessionId, key);
|
|
146387
146423
|
const subscriberCount = this.sessionSubscribers.get(sessionId)?.size ?? 0;
|
|
146388
146424
|
this.log.info(
|
|
146389
146425
|
`[ws.broadcast_permission] ${sessionId.slice(0, 8)} subscribers=${subscriberCount}`,
|