@threadbase-sh/streamer 1.41.0 → 1.41.2
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 +58 -10
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +58 -10
- 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 +58 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1581,6 +1581,7 @@ declare class PTYManager implements SessionRunner {
|
|
|
1581
1581
|
private chunkIndex;
|
|
1582
1582
|
private lastChunkAt;
|
|
1583
1583
|
private quietCheckers;
|
|
1584
|
+
private readyFallbackTimers;
|
|
1584
1585
|
private startPromises;
|
|
1585
1586
|
constructor(options?: PTYManagerOptions);
|
|
1586
1587
|
start(sessionId: string, options: StartSessionOptions): Promise<ManagedSession>;
|
|
@@ -1606,6 +1607,8 @@ declare class PTYManager implements SessionRunner {
|
|
|
1606
1607
|
private detectLivePrompts;
|
|
1607
1608
|
private handleQuiet;
|
|
1608
1609
|
private recheckReadyFromScreen;
|
|
1610
|
+
private armReadyFallback;
|
|
1611
|
+
private clearReadyFallback;
|
|
1609
1612
|
private markReady;
|
|
1610
1613
|
private handleExit;
|
|
1611
1614
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1581,6 +1581,7 @@ declare class PTYManager implements SessionRunner {
|
|
|
1581
1581
|
private chunkIndex;
|
|
1582
1582
|
private lastChunkAt;
|
|
1583
1583
|
private quietCheckers;
|
|
1584
|
+
private readyFallbackTimers;
|
|
1584
1585
|
private startPromises;
|
|
1585
1586
|
constructor(options?: PTYManagerOptions);
|
|
1586
1587
|
start(sessionId: string, options: StartSessionOptions): Promise<ManagedSession>;
|
|
@@ -1606,6 +1607,8 @@ declare class PTYManager implements SessionRunner {
|
|
|
1606
1607
|
private detectLivePrompts;
|
|
1607
1608
|
private handleQuiet;
|
|
1608
1609
|
private recheckReadyFromScreen;
|
|
1610
|
+
private armReadyFallback;
|
|
1611
|
+
private clearReadyFallback;
|
|
1609
1612
|
private markReady;
|
|
1610
1613
|
private handleExit;
|
|
1611
1614
|
}
|
package/dist/index.js
CHANGED
|
@@ -1902,8 +1902,8 @@ var PTY_COLS2 = 120;
|
|
|
1902
1902
|
var PTY_ROWS2 = 40;
|
|
1903
1903
|
var SCREEN_SCROLLBACK2 = 1e3;
|
|
1904
1904
|
var CLAUDE_PROMPT_MARKERS = ["\u256D", "\u276F"];
|
|
1905
|
-
var PROMPT_MARKER_FALLBACK_MS = 1e4;
|
|
1906
1905
|
var QUIET_DETECT_MS2 = 500;
|
|
1906
|
+
var CLAUDE_READY_FALLBACK_MS = 8e3;
|
|
1907
1907
|
function buildPasteBytes(input) {
|
|
1908
1908
|
return `\x1B[200~${input}\x1B[201~`;
|
|
1909
1909
|
}
|
|
@@ -1979,8 +1979,8 @@ var PTYManager = class {
|
|
|
1979
1979
|
// silently lost — the "dot bug".
|
|
1980
1980
|
queuedInputs = /* @__PURE__ */ new Map();
|
|
1981
1981
|
log;
|
|
1982
|
-
// Timestamp of first PTY chunk per session;
|
|
1983
|
-
//
|
|
1982
|
+
// Timestamp of first PTY chunk per session; used for the [pty.ready] elapsed
|
|
1983
|
+
// measurement so a slow boot is visible in the logs.
|
|
1984
1984
|
firstChunkAt = /* @__PURE__ */ new Map();
|
|
1985
1985
|
// Per-session chunk counter and last-chunk timestamp. Diagnostic-only,
|
|
1986
1986
|
// feeds the [pty.chunk] log lines so we can trace whether Claude responded
|
|
@@ -1991,6 +1991,8 @@ var PTYManager = class {
|
|
|
1991
1991
|
// QUIET_DETECT_MS after the last chunk so ready/prompt detection doesn't
|
|
1992
1992
|
// wait for another chunk that may never arrive (Claude blocked on input).
|
|
1993
1993
|
quietCheckers = /* @__PURE__ */ new Map();
|
|
1994
|
+
// Per-session flat backstop from the first chunk (CLAUDE_READY_FALLBACK_MS).
|
|
1995
|
+
readyFallbackTimers = /* @__PURE__ */ new Map();
|
|
1994
1996
|
// In-flight start()/startFresh() calls keyed by sessionId. A second
|
|
1995
1997
|
// concurrent resume for the same session (double-tap, client retry) awaits
|
|
1996
1998
|
// the first call's promise instead of spawning a duplicate PTY (CRITICAL #3).
|
|
@@ -2076,6 +2078,7 @@ var PTYManager = class {
|
|
|
2076
2078
|
};
|
|
2077
2079
|
this.sessions.set(sessionId, session);
|
|
2078
2080
|
this.pendingReady.add(sessionId);
|
|
2081
|
+
this.armReadyFallback(sessionId);
|
|
2079
2082
|
proc.onData((data) => {
|
|
2080
2083
|
this.handleOutput(sessionId, data);
|
|
2081
2084
|
});
|
|
@@ -2136,6 +2139,7 @@ var PTYManager = class {
|
|
|
2136
2139
|
};
|
|
2137
2140
|
this.sessions.set(sessionId, session);
|
|
2138
2141
|
this.pendingReady.add(sessionId);
|
|
2142
|
+
this.armReadyFallback(sessionId);
|
|
2139
2143
|
proc.onData((data) => {
|
|
2140
2144
|
this.handleOutput(sessionId, data);
|
|
2141
2145
|
});
|
|
@@ -2310,6 +2314,7 @@ var PTYManager = class {
|
|
|
2310
2314
|
this.shellPromptOpen.delete(sessionId);
|
|
2311
2315
|
this.quietCheckers.get(sessionId)?.cancel();
|
|
2312
2316
|
this.quietCheckers.delete(sessionId);
|
|
2317
|
+
this.clearReadyFallback(sessionId);
|
|
2313
2318
|
try {
|
|
2314
2319
|
session.process.kill("SIGINT");
|
|
2315
2320
|
} catch {
|
|
@@ -2398,6 +2403,8 @@ var PTYManager = class {
|
|
|
2398
2403
|
this.lastChunkAt.clear();
|
|
2399
2404
|
for (const quiet of this.quietCheckers.values()) quiet.cancel();
|
|
2400
2405
|
this.quietCheckers.clear();
|
|
2406
|
+
for (const timer of this.readyFallbackTimers.values()) clearTimeout(timer);
|
|
2407
|
+
this.readyFallbackTimers.clear();
|
|
2401
2408
|
this.permissionOpen.clear();
|
|
2402
2409
|
this.lastScreenQuestionKey.clear();
|
|
2403
2410
|
this.shellPromptOpen.clear();
|
|
@@ -2440,8 +2447,6 @@ var PTYManager = class {
|
|
|
2440
2447
|
const matchedMarker = CLAUDE_PROMPT_MARKERS.find((m) => stripped.includes(m));
|
|
2441
2448
|
if (session.status === "running" && matchedMarker) {
|
|
2442
2449
|
this.markReady(sessionId, session, "prompt-marker", `marker:${matchedMarker}`);
|
|
2443
|
-
} else if (session.status === "running" && this.pendingReady.has(sessionId) && now - (this.firstChunkAt.get(sessionId) ?? now) >= PROMPT_MARKER_FALLBACK_MS) {
|
|
2444
|
-
this.markReady(sessionId, session, "timeout-fallback", "fallback:timeout");
|
|
2445
2450
|
}
|
|
2446
2451
|
this.onOutput?.(sessionId, data);
|
|
2447
2452
|
this.detectLivePrompts(sessionId, data, stripped).catch((err) => {
|
|
@@ -2541,7 +2546,13 @@ var PTYManager = class {
|
|
|
2541
2546
|
const session = this.sessions.get(sessionId);
|
|
2542
2547
|
if (session?.status !== "running") return;
|
|
2543
2548
|
if (this.pendingReady.has(sessionId)) {
|
|
2544
|
-
this.
|
|
2549
|
+
this.recheckReadyFromScreen(sessionId).catch((err) => {
|
|
2550
|
+
this.log.warn("[pty.ready] boot screen recheck failed", {
|
|
2551
|
+
event: "pty.ready_recheck_failed",
|
|
2552
|
+
sessionId,
|
|
2553
|
+
err
|
|
2554
|
+
});
|
|
2555
|
+
});
|
|
2545
2556
|
} else {
|
|
2546
2557
|
this.recheckReadyFromScreen(sessionId).catch((err) => {
|
|
2547
2558
|
this.log.warn("[pty.ready] screen recheck failed", {
|
|
@@ -2573,9 +2584,32 @@ var PTYManager = class {
|
|
|
2573
2584
|
this.markReady(sessionId, session, "screen-marker", `quiet:screen-marker:${matchedMarker}`);
|
|
2574
2585
|
}
|
|
2575
2586
|
}
|
|
2587
|
+
// Flat backstop from the first chunk: if neither a prompt marker nor the
|
|
2588
|
+
// screen recheck settles the session within CLAUDE_READY_FALLBACK_MS, mark it
|
|
2589
|
+
// ready anyway so start requests resolve and queued input is not held
|
|
2590
|
+
// forever. This is what makes the quiet-checker safe to be strict — a boot
|
|
2591
|
+
// variant whose marker we cannot see still recovers, just 8s later instead of
|
|
2592
|
+
// 500ms sooner and wrong. unref() so it never holds the process open.
|
|
2593
|
+
armReadyFallback(sessionId) {
|
|
2594
|
+
const timer = setTimeout(() => {
|
|
2595
|
+
this.readyFallbackTimers.delete(sessionId);
|
|
2596
|
+
const session = this.sessions.get(sessionId);
|
|
2597
|
+
if (session?.status === "running" && this.pendingReady.has(sessionId)) {
|
|
2598
|
+
this.markReady(sessionId, session, "timeout-fallback", "fallback:timeout");
|
|
2599
|
+
}
|
|
2600
|
+
}, CLAUDE_READY_FALLBACK_MS);
|
|
2601
|
+
timer.unref?.();
|
|
2602
|
+
this.readyFallbackTimers.set(sessionId, timer);
|
|
2603
|
+
}
|
|
2604
|
+
clearReadyFallback(sessionId) {
|
|
2605
|
+
const timer = this.readyFallbackTimers.get(sessionId);
|
|
2606
|
+
if (timer) clearTimeout(timer);
|
|
2607
|
+
this.readyFallbackTimers.delete(sessionId);
|
|
2608
|
+
}
|
|
2576
2609
|
// Transition a session from "running" to "waiting_input", clear pendingReady,
|
|
2577
2610
|
// and flush any queued input. Idempotent: callers can invoke at any chunk.
|
|
2578
2611
|
markReady(sessionId, session, source, reason) {
|
|
2612
|
+
this.clearReadyFallback(sessionId);
|
|
2579
2613
|
session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
2580
2614
|
session.status = "waiting_input";
|
|
2581
2615
|
session.statusSource = source;
|
|
@@ -2619,6 +2653,7 @@ var PTYManager = class {
|
|
|
2619
2653
|
this.shellPromptOpen.delete(sessionId);
|
|
2620
2654
|
this.quietCheckers.get(sessionId)?.cancel();
|
|
2621
2655
|
this.quietCheckers.delete(sessionId);
|
|
2656
|
+
this.clearReadyFallback(sessionId);
|
|
2622
2657
|
}
|
|
2623
2658
|
};
|
|
2624
2659
|
function toPublicSession2(s) {
|
|
@@ -6625,7 +6660,8 @@ var ManagedSessionsRepository = class {
|
|
|
6625
6660
|
completed_at = @completed_at,
|
|
6626
6661
|
last_activity_at = @last_activity_at,
|
|
6627
6662
|
prompt_count = @prompt_count,
|
|
6628
|
-
failure_reason = COALESCE(@failure_reason, failure_reason)
|
|
6663
|
+
failure_reason = COALESCE(@failure_reason, failure_reason),
|
|
6664
|
+
session_name = COALESCE(@session_name, session_name)
|
|
6629
6665
|
WHERE session_id = @session_id
|
|
6630
6666
|
`);
|
|
6631
6667
|
this.getStmt = db.prepare("SELECT * FROM managed_sessions WHERE session_id = ?");
|
|
@@ -6682,7 +6718,8 @@ var ManagedSessionsRepository = class {
|
|
|
6682
6718
|
completed_at: fields.completedAt?.getTime() ?? null,
|
|
6683
6719
|
last_activity_at: fields.lastActivityAt?.getTime() ?? null,
|
|
6684
6720
|
prompt_count: fields.promptCount ?? 0,
|
|
6685
|
-
failure_reason: fields.failureReason ?? null
|
|
6721
|
+
failure_reason: fields.failureReason ?? null,
|
|
6722
|
+
session_name: fields.sessionName ?? null
|
|
6686
6723
|
});
|
|
6687
6724
|
}
|
|
6688
6725
|
get(sessionId) {
|
|
@@ -9739,7 +9776,14 @@ var StreamerServer = class {
|
|
|
9739
9776
|
this.sessionStore.updateManaged(session.id, {
|
|
9740
9777
|
status: session.status,
|
|
9741
9778
|
completedAt: session.completedAt,
|
|
9742
|
-
...session.lastActivityAt != null && { lastActivityAt: session.lastActivityAt }
|
|
9779
|
+
...session.lastActivityAt != null && { lastActivityAt: session.lastActivityAt },
|
|
9780
|
+
// The runner derives this from the first user message, on its own
|
|
9781
|
+
// copy of the session. Without mirroring it here SessionStore never
|
|
9782
|
+
// learns it, so a fresh live session is served with no sessionName
|
|
9783
|
+
// even though the registry has one — the name only appeared after a
|
|
9784
|
+
// restart rebuilt the row as a stub. Guarded so a runner that has not
|
|
9785
|
+
// derived one yet cannot blank a name set by enrichResumedSessionAsync.
|
|
9786
|
+
...session.sessionName != null && { sessionName: session.sessionName }
|
|
9743
9787
|
});
|
|
9744
9788
|
this.managedSessionsRepo?.recordStatus(
|
|
9745
9789
|
session.id,
|
|
@@ -9749,7 +9793,11 @@ var StreamerServer = class {
|
|
|
9749
9793
|
completedAt: session.completedAt,
|
|
9750
9794
|
lastActivityAt: session.lastActivityAt ?? null,
|
|
9751
9795
|
promptCount: session.promptCount,
|
|
9752
|
-
failureReason: session.failureReason ?? null
|
|
9796
|
+
failureReason: session.failureReason ?? null,
|
|
9797
|
+
// Derived from the first user message, so it does not exist yet at
|
|
9798
|
+
// recordSpawn. The input that produces it also flips
|
|
9799
|
+
// waiting_input→running, which lands here.
|
|
9800
|
+
sessionName: session.sessionName ?? null
|
|
9753
9801
|
}
|
|
9754
9802
|
);
|
|
9755
9803
|
if (session.status === "waiting_input" || session.status === "idle") {
|