@threadbase-sh/streamer 1.44.3 → 1.45.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 +94 -17
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +92 -15
- 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 +92 -15
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -114168,14 +114168,19 @@ function scrapePermissionGate(lines) {
|
|
|
114168
114168
|
const options = [];
|
|
114169
114169
|
let cursor;
|
|
114170
114170
|
let firstOptionLine = -1;
|
|
114171
|
-
for (let i =
|
|
114172
|
-
const
|
|
114173
|
-
|
|
114174
|
-
|
|
114175
|
-
|
|
114176
|
-
|
|
114177
|
-
|
|
114178
|
-
|
|
114171
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
114172
|
+
const stripped = stripGutter(lines[i]);
|
|
114173
|
+
const m2 = OPTION_RE.exec(stripped);
|
|
114174
|
+
if (m2) {
|
|
114175
|
+
const index = Number.parseInt(m2[2], 10);
|
|
114176
|
+
if (!Number.isFinite(index)) continue;
|
|
114177
|
+
firstOptionLine = i;
|
|
114178
|
+
if (m2[1]) cursor = index;
|
|
114179
|
+
options.unshift({ index, label: m2[3] });
|
|
114180
|
+
continue;
|
|
114181
|
+
}
|
|
114182
|
+
if (options.length === 0) continue;
|
|
114183
|
+
if (stripped.trim().length === 0 || BOX_ONLY_RE.test(lines[i].trim())) break;
|
|
114179
114184
|
}
|
|
114180
114185
|
if (options.length === 0) return null;
|
|
114181
114186
|
let prompt;
|
|
@@ -114214,7 +114219,15 @@ function scrapeDetail(lines, promptLine) {
|
|
|
114214
114219
|
}
|
|
114215
114220
|
return collected.length > 0 ? collected.reverse().join("\n") : void 0;
|
|
114216
114221
|
}
|
|
114217
|
-
|
|
114222
|
+
function detectGateScreen(lines) {
|
|
114223
|
+
if (lines.some((l) => ASK_MENU_FOOTER_RE.test(l))) return null;
|
|
114224
|
+
if (!lines.some((l) => GATE_FOOTER_RE.test(l))) return null;
|
|
114225
|
+
const gate = scrapePermissionGate(lines);
|
|
114226
|
+
if (!gate || gate.options.length < 2) return null;
|
|
114227
|
+
if (!gate.options.some((o) => YES_NO_LABEL_RE.test(o.label))) return null;
|
|
114228
|
+
return gate;
|
|
114229
|
+
}
|
|
114230
|
+
var OSC_777_PERMISSION_RE, OSC_777_WAITING_RE, OPTION_RE, FOOTER_RE, BOX_ONLY_RE, PROMPT_ARROW_RE, MAX_DETAIL_LINES, GATE_FOOTER_RE, ASK_MENU_FOOTER_RE, YES_NO_LABEL_RE;
|
|
114218
114231
|
var init_detectPermissionGate = __esm({
|
|
114219
114232
|
"src/services/questions/detectPermissionGate.ts"() {
|
|
114220
114233
|
"use strict";
|
|
@@ -114225,6 +114238,9 @@ var init_detectPermissionGate = __esm({
|
|
|
114225
114238
|
BOX_ONLY_RE = /^[\s│─┌┐└┘├┤┬┴┼╭╮╰╯╱╲=_-]+$/;
|
|
114226
114239
|
PROMPT_ARROW_RE = /^[\s]*[❯›>]\s*$/;
|
|
114227
114240
|
MAX_DETAIL_LINES = 6;
|
|
114241
|
+
GATE_FOOTER_RE = /esc to cancel/i;
|
|
114242
|
+
ASK_MENU_FOOTER_RE = /Enter to select/i;
|
|
114243
|
+
YES_NO_LABEL_RE = /^(yes|no)\b/i;
|
|
114228
114244
|
}
|
|
114229
114245
|
});
|
|
114230
114246
|
|
|
@@ -114448,7 +114464,7 @@ function toPublicSession2(s3) {
|
|
|
114448
114464
|
function stripAnsi2(str) {
|
|
114449
114465
|
return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "");
|
|
114450
114466
|
}
|
|
114451
|
-
var import_headless2, import_crypto9, import_fs22, import_path22, OUTPUT_BUFFER_MAX2, INPUT_HISTORY_MAX2, PTY_COLS2, PTY_ROWS2, SCREEN_SCROLLBACK2, CLAUDE_PROMPT_MARKERS, QUIET_DETECT_MS2, CLAUDE_READY_FALLBACK_MS, SUBMIT_BYTES2, SUBMIT_DELAY_MS, SUBMIT_MAX_WAIT_MS, pty2, PTYManager;
|
|
114467
|
+
var import_headless2, import_crypto9, import_fs22, import_path22, OUTPUT_BUFFER_MAX2, INPUT_HISTORY_MAX2, PTY_COLS2, PTY_ROWS2, SCREEN_SCROLLBACK2, CLAUDE_PROMPT_MARKERS, QUIET_DETECT_MS2, OSC_TAIL_CHARS, SCRAPE_THROTTLE_MS, CLAUDE_READY_FALLBACK_MS, SUBMIT_BYTES2, SUBMIT_DELAY_MS, SUBMIT_MAX_WAIT_MS, pty2, PTYManager;
|
|
114452
114468
|
var init_pty_manager = __esm({
|
|
114453
114469
|
"src/pty-manager.ts"() {
|
|
114454
114470
|
"use strict";
|
|
@@ -114472,6 +114488,8 @@ var init_pty_manager = __esm({
|
|
|
114472
114488
|
SCREEN_SCROLLBACK2 = 1e3;
|
|
114473
114489
|
CLAUDE_PROMPT_MARKERS = ["\u256D", "\u276F"];
|
|
114474
114490
|
QUIET_DETECT_MS2 = 500;
|
|
114491
|
+
OSC_TAIL_CHARS = 128;
|
|
114492
|
+
SCRAPE_THROTTLE_MS = 300;
|
|
114475
114493
|
CLAUDE_READY_FALLBACK_MS = 8e3;
|
|
114476
114494
|
SUBMIT_BYTES2 = "\r";
|
|
114477
114495
|
SUBMIT_DELAY_MS = 16;
|
|
@@ -114490,6 +114508,12 @@ var init_pty_manager = __esm({
|
|
|
114490
114508
|
// the next prompt-ready without a fresh 777 (gate closed). Prevents
|
|
114491
114509
|
// re-broadcasting open/close on every chunk.
|
|
114492
114510
|
permissionOpen = /* @__PURE__ */ new Set();
|
|
114511
|
+
// Last chunk's raw tail per session — prepended to the next chunk before
|
|
114512
|
+
// the OSC regex test so a split escape still matches. Consumed on match.
|
|
114513
|
+
oscTail = /* @__PURE__ */ new Map();
|
|
114514
|
+
// When the last full detection pass ran per session (any trigger) — the
|
|
114515
|
+
// clock the SCRAPE_THROTTLE_MS ceiling is measured against.
|
|
114516
|
+
lastDetectAt = /* @__PURE__ */ new Map();
|
|
114493
114517
|
// Content key of the last AskUserQuestion broadcast from the rendered screen,
|
|
114494
114518
|
// per session — de-dupes the same menu firing on consecutive repaints.
|
|
114495
114519
|
lastScreenQuestionKey = /* @__PURE__ */ new Map();
|
|
@@ -114498,6 +114522,13 @@ var init_pty_manager = __esm({
|
|
|
114498
114522
|
// on a prompt-ready/marker return and de-dupe consecutive repaints. Modelled
|
|
114499
114523
|
// on permissionOpen but keyed by content (a shell prompt has no OSC trigger).
|
|
114500
114524
|
shellPromptOpen = /* @__PURE__ */ new Map();
|
|
114525
|
+
// Content key (permissionContentKey, cursor excluded — see arm 3) of the
|
|
114526
|
+
// gate that was still painted on screen when arm 2 closed it. Suppresses
|
|
114527
|
+
// arm 3 re-claiming that same box from a later trigger-less/throttled pass
|
|
114528
|
+
// before Claude erases it — otherwise the paint-time claim reopens the
|
|
114529
|
+
// card it just closed. Cleared once detectGateScreen sees the box is gone,
|
|
114530
|
+
// so a genuinely new (even content-identical) gate is claimed normally.
|
|
114531
|
+
closedGateKey = /* @__PURE__ */ new Map();
|
|
114501
114532
|
// Tracks sessions (both fresh and resume) whose PTY has spawned but Claude
|
|
114502
114533
|
// hasn't yet reached an interactive prompt — i.e. onReady hasn't fired.
|
|
114503
114534
|
pendingReady = /* @__PURE__ */ new Set();
|
|
@@ -114851,8 +114882,11 @@ var init_pty_manager = __esm({
|
|
|
114851
114882
|
this.queuedInputs.delete(sessionId);
|
|
114852
114883
|
this.firstChunkAt.delete(sessionId);
|
|
114853
114884
|
this.permissionOpen.delete(sessionId);
|
|
114885
|
+
this.oscTail.delete(sessionId);
|
|
114886
|
+
this.lastDetectAt.delete(sessionId);
|
|
114854
114887
|
this.lastScreenQuestionKey.delete(sessionId);
|
|
114855
114888
|
this.shellPromptOpen.delete(sessionId);
|
|
114889
|
+
this.closedGateKey.delete(sessionId);
|
|
114856
114890
|
this.quietCheckers.get(sessionId)?.cancel();
|
|
114857
114891
|
this.quietCheckers.delete(sessionId);
|
|
114858
114892
|
this.clearReadyFallback(sessionId);
|
|
@@ -114947,8 +114981,11 @@ var init_pty_manager = __esm({
|
|
|
114947
114981
|
for (const timer of this.readyFallbackTimers.values()) clearTimeout(timer);
|
|
114948
114982
|
this.readyFallbackTimers.clear();
|
|
114949
114983
|
this.permissionOpen.clear();
|
|
114984
|
+
this.oscTail.clear();
|
|
114985
|
+
this.lastDetectAt.clear();
|
|
114950
114986
|
this.lastScreenQuestionKey.clear();
|
|
114951
114987
|
this.shellPromptOpen.clear();
|
|
114988
|
+
this.closedGateKey.clear();
|
|
114952
114989
|
}
|
|
114953
114990
|
handleOutput(sessionId, data) {
|
|
114954
114991
|
const session = this.sessions.get(sessionId);
|
|
@@ -115012,16 +115049,24 @@ var init_pty_manager = __esm({
|
|
|
115012
115049
|
async detectLivePrompts(sessionId, rawData, stripped) {
|
|
115013
115050
|
const session = this.sessions.get(sessionId);
|
|
115014
115051
|
if (!session) return;
|
|
115015
|
-
const
|
|
115016
|
-
const
|
|
115052
|
+
const oscWindow = (this.oscTail.get(sessionId) ?? "") + rawData;
|
|
115053
|
+
const oscPermission = hasPermissionOsc(oscWindow);
|
|
115054
|
+
const oscWaitingForInput = hasWaitingForInputOsc(oscWindow);
|
|
115055
|
+
if (rawData !== "") {
|
|
115056
|
+
if (oscPermission || oscWaitingForInput) this.oscTail.delete(sessionId);
|
|
115057
|
+
else this.oscTail.set(sessionId, oscWindow.slice(-OSC_TAIL_CHARS));
|
|
115058
|
+
}
|
|
115017
115059
|
const hasAskFooter = /Enter to select/i.test(stripped);
|
|
115018
115060
|
const hasPromptMarker = CLAUDE_PROMPT_MARKERS.some((m2) => stripped.includes(m2));
|
|
115019
115061
|
const hasShellPromptHint = /[[(]\s*y\s*\/\s*n\s*[\])]|press\s+(enter|return|any key)|\bcontinue\b\s*\?|^\s*(?:❯|>)?\s*\d+[.)]\s+\S/im.test(
|
|
115020
115062
|
stripped
|
|
115021
115063
|
);
|
|
115022
|
-
|
|
115064
|
+
const nowMs = Date.now();
|
|
115065
|
+
const scrapeDue = nowMs - (this.lastDetectAt.get(sessionId) ?? 0) >= SCRAPE_THROTTLE_MS;
|
|
115066
|
+
if (!oscPermission && !oscWaitingForInput && !hasAskFooter && !hasShellPromptHint && !scrapeDue && !this.permissionOpen.has(sessionId) && !this.shellPromptOpen.has(sessionId) && !this.lastScreenQuestionKey.has(sessionId)) {
|
|
115023
115067
|
return;
|
|
115024
115068
|
}
|
|
115069
|
+
this.lastDetectAt.set(sessionId, nowMs);
|
|
115025
115070
|
const lines = await this.getOutputLines(sessionId, 60);
|
|
115026
115071
|
const askFooterOnScreen = lines.some((l) => /Enter to select/i.test(l));
|
|
115027
115072
|
if (oscPermission || hasAskFooter || askFooterOnScreen) {
|
|
@@ -115041,13 +115086,30 @@ var init_pty_manager = __esm({
|
|
|
115041
115086
|
this.permissionOpen.add(sessionId);
|
|
115042
115087
|
this.onPermissionChange?.(sessionId, gate ?? { options: [] });
|
|
115043
115088
|
} else if (this.permissionOpen.has(sessionId) && !askFooterOnScreen) {
|
|
115044
|
-
const gate =
|
|
115045
|
-
|
|
115089
|
+
const gate = detectGateScreen(lines);
|
|
115090
|
+
const stillPainted = gate !== null || scrapePermissionGate(lines) !== null;
|
|
115091
|
+
if (oscWaitingForInput || !stillPainted && hasPromptMarker) {
|
|
115046
115092
|
this.permissionOpen.delete(sessionId);
|
|
115047
115093
|
this.onPermissionChange?.(sessionId, null);
|
|
115094
|
+
if (gate) {
|
|
115095
|
+
this.closedGateKey.set(sessionId, permissionContentKey({ ...gate, cursor: void 0 }));
|
|
115096
|
+
} else {
|
|
115097
|
+
this.closedGateKey.delete(sessionId);
|
|
115098
|
+
}
|
|
115048
115099
|
} else if (gate) {
|
|
115049
115100
|
this.onPermissionChange?.(sessionId, gate);
|
|
115050
115101
|
}
|
|
115102
|
+
} else if (!askFooterOnScreen && !oscWaitingForInput) {
|
|
115103
|
+
const gate = detectGateScreen(lines);
|
|
115104
|
+
if (gate) {
|
|
115105
|
+
const key = permissionContentKey({ ...gate, cursor: void 0 });
|
|
115106
|
+
if (this.closedGateKey.get(sessionId) !== key) {
|
|
115107
|
+
this.permissionOpen.add(sessionId);
|
|
115108
|
+
this.onPermissionChange?.(sessionId, gate);
|
|
115109
|
+
}
|
|
115110
|
+
} else {
|
|
115111
|
+
this.closedGateKey.delete(sessionId);
|
|
115112
|
+
}
|
|
115051
115113
|
}
|
|
115052
115114
|
if (askFooterOnScreen) {
|
|
115053
115115
|
const detected = detectQuestionFromScreen(lines);
|
|
@@ -115190,8 +115252,11 @@ var init_pty_manager = __esm({
|
|
|
115190
115252
|
this.queuedInputs.delete(sessionId);
|
|
115191
115253
|
this.firstChunkAt.delete(sessionId);
|
|
115192
115254
|
this.permissionOpen.delete(sessionId);
|
|
115255
|
+
this.oscTail.delete(sessionId);
|
|
115256
|
+
this.lastDetectAt.delete(sessionId);
|
|
115193
115257
|
this.lastScreenQuestionKey.delete(sessionId);
|
|
115194
115258
|
this.shellPromptOpen.delete(sessionId);
|
|
115259
|
+
this.closedGateKey.delete(sessionId);
|
|
115195
115260
|
this.quietCheckers.get(sessionId)?.cancel();
|
|
115196
115261
|
this.quietCheckers.delete(sessionId);
|
|
115197
115262
|
this.clearReadyFallback(sessionId);
|
|
@@ -147568,7 +147633,12 @@ function managedToResponse(s3, ptyAttached) {
|
|
|
147568
147633
|
// the durable registry, so it is a previous run's session with no process
|
|
147569
147634
|
// behind it — `resumable`, and `historical` rather than `managed`
|
|
147570
147635
|
// (docs/plans/live-sessions-persistence-plan.md §4, Phase 1).
|
|
147571
|
-
|
|
147636
|
+
// A session this run itself put on hold (grace timer, explicit hold_session,
|
|
147637
|
+
// idle reaper) is the other exception: the PTY is gone the same way an exit
|
|
147638
|
+
// leaves it gone, but the conversation is still resumable, not terminal —
|
|
147639
|
+
// `putOnHold` records that by leaving `statusSource: "shutdown"` (the only
|
|
147640
|
+
// place either runner sets it), so it is checked here alongside `rehydrated`.
|
|
147641
|
+
lifecycle: ptyAttached ? "attached" : s3.rehydrated || s3.statusSource === "shutdown" ? "resumable" : s3.failureReason != null ? "failed" : "completed",
|
|
147572
147642
|
lifecycleSource: ptyAttached ? s3.reconciled ? "reconcile" : "spawn" : s3.rehydrated ? "reconcile" : "exit",
|
|
147573
147643
|
// We own its PTY, so `status` is the authoritative signal — no inferred
|
|
147574
147644
|
// `activity` is attached for managed sessions.
|
|
@@ -148422,7 +148492,14 @@ var StreamerServer = class {
|
|
|
148422
148492
|
// even though the registry has one — the name only appeared after a
|
|
148423
148493
|
// restart rebuilt the row as a stub. Guarded so a runner that has not
|
|
148424
148494
|
// derived one yet cannot blank a name set by enrichResumedSessionAsync.
|
|
148425
|
-
...session.sessionName != null && { sessionName: session.sessionName }
|
|
148495
|
+
...session.sessionName != null && { sessionName: session.sessionName },
|
|
148496
|
+
// Without mirroring this, SessionStore's copy is frozen at whatever
|
|
148497
|
+
// `addManaged` saw at spawn ("spawn") forever, because updateManaged
|
|
148498
|
+
// is a partial merge — so managedToResponse can never tell a
|
|
148499
|
+
// grace-timer/idle-reaper hold (statusSource "shutdown") apart from a
|
|
148500
|
+
// genuine process exit ("process-exit"), and reports both as
|
|
148501
|
+
// `lifecycle: "completed"`. See managedToResponse in session-store.ts.
|
|
148502
|
+
...session.statusSource != null && { statusSource: session.statusSource }
|
|
148426
148503
|
});
|
|
148427
148504
|
this.managedSessionsRepo?.recordStatus(
|
|
148428
148505
|
session.id,
|