@threadbase-sh/streamer 1.69.4 → 1.69.6
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/api/handlers/sessions.handlers.d.ts +1 -0
- package/dist/api/handlers/sessions.handlers.d.ts.map +1 -1
- package/dist/cli.cjs +62 -20
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +62 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +62 -20
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/services/questions/answersToKeystrokes.d.ts +8 -0
- package/dist/services/questions/answersToKeystrokes.d.ts.map +1 -1
- package/dist/services/questions/resolveAnswer.d.ts +1 -1
- package/dist/services/questions/resolveAnswer.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9583,19 +9583,39 @@ var UnknownOptionError = class extends Error {
|
|
|
9583
9583
|
question;
|
|
9584
9584
|
value;
|
|
9585
9585
|
};
|
|
9586
|
+
var UnsupportedPromptShapeError = class extends Error {
|
|
9587
|
+
constructor(detail) {
|
|
9588
|
+
super(`Unsupported prompt shape: ${detail}`);
|
|
9589
|
+
this.detail = detail;
|
|
9590
|
+
this.name = "UnsupportedPromptShapeError";
|
|
9591
|
+
}
|
|
9592
|
+
detail;
|
|
9593
|
+
};
|
|
9594
|
+
var IncompleteAnswerError = class extends Error {
|
|
9595
|
+
constructor(question) {
|
|
9596
|
+
super(`Missing answer for question "${question}"`);
|
|
9597
|
+
this.question = question;
|
|
9598
|
+
this.name = "IncompleteAnswerError";
|
|
9599
|
+
}
|
|
9600
|
+
question;
|
|
9601
|
+
};
|
|
9586
9602
|
function answersToKeystrokes(questions, answers) {
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
const raw = answers[q.question];
|
|
9590
|
-
if (raw === void 0) {
|
|
9591
|
-
throw new Error(`Missing answer for question "${q.question}"`);
|
|
9592
|
-
}
|
|
9593
|
-
const label = Array.isArray(raw) ? raw[0] : raw;
|
|
9594
|
-
const target = q.options.findIndex((o) => o.label === label);
|
|
9595
|
-
if (target < 0) throw new UnknownOptionError(q.question, label);
|
|
9596
|
-
out += DOWN.repeat(target) + ENTER3;
|
|
9603
|
+
if (questions.length !== 1) {
|
|
9604
|
+
throw new UnsupportedPromptShapeError(`${questions.length} questions`);
|
|
9597
9605
|
}
|
|
9598
|
-
|
|
9606
|
+
const q = questions[0];
|
|
9607
|
+
if (q.multiSelect) throw new UnsupportedPromptShapeError("multiSelect");
|
|
9608
|
+
const raw = answers[q.question];
|
|
9609
|
+
if (raw === void 0 || Array.isArray(raw) && raw.length === 0) {
|
|
9610
|
+
throw new IncompleteAnswerError(q.question);
|
|
9611
|
+
}
|
|
9612
|
+
if (Array.isArray(raw) && raw.length > 1) {
|
|
9613
|
+
throw new UnsupportedPromptShapeError(`${raw.length} labels for a single-select question`);
|
|
9614
|
+
}
|
|
9615
|
+
const label = Array.isArray(raw) ? raw[0] : raw;
|
|
9616
|
+
const target = q.options.findIndex((o) => o.label === label);
|
|
9617
|
+
if (target < 0) throw new UnknownOptionError(q.question, label);
|
|
9618
|
+
return DOWN.repeat(target) + ENTER3;
|
|
9599
9619
|
}
|
|
9600
9620
|
|
|
9601
9621
|
// src/services/questions/resolveAnswer.ts
|
|
@@ -9609,6 +9629,10 @@ function resolveAnswer(pending, body) {
|
|
|
9609
9629
|
return { ok: true, keys: answersToKeystrokes(pending.questions, answers) };
|
|
9610
9630
|
} catch (e) {
|
|
9611
9631
|
if (e instanceof UnknownOptionError) return { ok: false, reason: "unknown_option" };
|
|
9632
|
+
if (e instanceof UnsupportedPromptShapeError) {
|
|
9633
|
+
return { ok: false, reason: "unsupported_prompt_shape" };
|
|
9634
|
+
}
|
|
9635
|
+
if (e instanceof IncompleteAnswerError) return { ok: false, reason: "incomplete_answer" };
|
|
9612
9636
|
throw e;
|
|
9613
9637
|
}
|
|
9614
9638
|
}
|
|
@@ -9938,6 +9962,13 @@ var SessionHandlers = class {
|
|
|
9938
9962
|
get sessionSubscribers() {
|
|
9939
9963
|
return this.deps.sessionSubscribers;
|
|
9940
9964
|
}
|
|
9965
|
+
// Prompt lifecycle events (question / permission and their cancellations)
|
|
9966
|
+
// carry prompt content and go ONLY to the session's subscribers — never to
|
|
9967
|
+
// every connected socket. A client that subscribes after a prompt opened
|
|
9968
|
+
// gets it from the subscribe replay (server-wiring), not from a broadcast.
|
|
9969
|
+
broadcastToSession(sessionId, message) {
|
|
9970
|
+
this.wsHub.broadcastToClients(this.sessionSubscribers.get(sessionId) ?? [], message);
|
|
9971
|
+
}
|
|
9941
9972
|
get agentConfig() {
|
|
9942
9973
|
return this.deps.agentConfig;
|
|
9943
9974
|
}
|
|
@@ -10492,7 +10523,7 @@ var SessionHandlers = class {
|
|
|
10492
10523
|
const toolUseId = `screen:${sessionId}:${key.length}`;
|
|
10493
10524
|
this.pendingQuestions.set(sessionId, { toolUseId, questions, origin: "pty" });
|
|
10494
10525
|
this.pendingQuestionKey.set(sessionId, key);
|
|
10495
|
-
this.
|
|
10526
|
+
this.broadcastToSession(sessionId, { type: "question", sessionId, toolUseId, questions });
|
|
10496
10527
|
}
|
|
10497
10528
|
// Permission gate opened/closed (OSC 777 + scraped options). Broadcasts the
|
|
10498
10529
|
// additive `permission` / `permission_cancelled` events. Mobile answers by
|
|
@@ -10502,7 +10533,7 @@ var SessionHandlers = class {
|
|
|
10502
10533
|
if (!this.pendingPermission.has(sessionId)) return;
|
|
10503
10534
|
this.pendingPermission.delete(sessionId);
|
|
10504
10535
|
this.pendingPermissionKey.delete(sessionId);
|
|
10505
|
-
this.
|
|
10536
|
+
this.broadcastToSession(sessionId, { type: "permission_cancelled", sessionId });
|
|
10506
10537
|
return;
|
|
10507
10538
|
}
|
|
10508
10539
|
const key = permissionContentKey(gate);
|
|
@@ -10516,7 +10547,7 @@ var SessionHandlers = class {
|
|
|
10516
10547
|
`[ws.broadcast_permission] ${sessionId.slice(0, 8)} subscribers=${subscriberCount}`,
|
|
10517
10548
|
{ event: "ws.broadcast_permission", sessionId, subscriberCount }
|
|
10518
10549
|
);
|
|
10519
|
-
this.
|
|
10550
|
+
this.broadcastToSession(sessionId, {
|
|
10520
10551
|
type: "permission",
|
|
10521
10552
|
sessionId,
|
|
10522
10553
|
...gate.prompt ? { prompt: gate.prompt } : {},
|
|
@@ -10564,7 +10595,7 @@ var SessionHandlers = class {
|
|
|
10564
10595
|
const gateClosed = () => {
|
|
10565
10596
|
this.pendingPermission.delete(sessionId);
|
|
10566
10597
|
this.pendingPermissionKey.delete(sessionId);
|
|
10567
|
-
this.
|
|
10598
|
+
this.broadcastToSession(sessionId, { type: "permission_cancelled", sessionId });
|
|
10568
10599
|
json(res, 409, { ok: false, reason: "gate_closed" });
|
|
10569
10600
|
};
|
|
10570
10601
|
const gate = this.pendingPermission.get(sessionId);
|
|
@@ -10636,14 +10667,21 @@ var SessionHandlers = class {
|
|
|
10636
10667
|
const pending = this.pendingQuestions.get(sessionId);
|
|
10637
10668
|
const resolution = resolveAnswer(pending, body);
|
|
10638
10669
|
if (!resolution.ok) {
|
|
10639
|
-
|
|
10670
|
+
const unanswerable = resolution.reason === "unsupported_prompt_shape" || resolution.reason === "incomplete_answer";
|
|
10671
|
+
json(res, 400, {
|
|
10672
|
+
ok: false,
|
|
10673
|
+
reason: resolution.reason,
|
|
10674
|
+
...unanswerable ? {
|
|
10675
|
+
error: "This prompt needs an answer the app cannot give yet; answer it in the terminal"
|
|
10676
|
+
} : {}
|
|
10677
|
+
});
|
|
10640
10678
|
return;
|
|
10641
10679
|
}
|
|
10642
10680
|
const toolUseId = pending?.toolUseId ?? "";
|
|
10643
10681
|
if (!await this.questionMenuStillOpen(sessionId)) {
|
|
10644
10682
|
this.pendingQuestions.delete(sessionId);
|
|
10645
10683
|
this.pendingQuestionKey.delete(sessionId);
|
|
10646
|
-
this.
|
|
10684
|
+
this.broadcastToSession(sessionId, { type: "question_cancelled", sessionId, toolUseId });
|
|
10647
10685
|
json(res, 409, { ok: false, reason: "question_gone" });
|
|
10648
10686
|
return;
|
|
10649
10687
|
}
|
|
@@ -10655,7 +10693,7 @@ var SessionHandlers = class {
|
|
|
10655
10693
|
return;
|
|
10656
10694
|
}
|
|
10657
10695
|
this.pendingQuestions.delete(sessionId);
|
|
10658
|
-
this.
|
|
10696
|
+
this.broadcastToSession(sessionId, { type: "question_cancelled", sessionId, toolUseId });
|
|
10659
10697
|
json(res, 200, { ok: true });
|
|
10660
10698
|
}
|
|
10661
10699
|
// Best-effort: a session we don't own a PTY for, or one that raced away
|
|
@@ -17776,7 +17814,7 @@ var StreamerServer = class {
|
|
|
17776
17814
|
priorToolUseId
|
|
17777
17815
|
});
|
|
17778
17816
|
this.pendingQuestionKey.set(sessionId, key);
|
|
17779
|
-
if (broadcast) this.wsHub.
|
|
17817
|
+
if (broadcast) this.wsHub.broadcastToClients(this.sessionSubscribers.get(sessionId) ?? [], m);
|
|
17780
17818
|
}
|
|
17781
17819
|
}
|
|
17782
17820
|
cancelPendingQuestion(sessionId) {
|
|
@@ -17784,7 +17822,11 @@ var StreamerServer = class {
|
|
|
17784
17822
|
if (!pq) return;
|
|
17785
17823
|
this.pendingQuestions.delete(sessionId);
|
|
17786
17824
|
this.pendingQuestionKey.delete(sessionId);
|
|
17787
|
-
this.wsHub.
|
|
17825
|
+
this.wsHub.broadcastToClients(this.sessionSubscribers.get(sessionId) ?? [], {
|
|
17826
|
+
type: "question_cancelled",
|
|
17827
|
+
sessionId,
|
|
17828
|
+
toolUseId: pq.toolUseId
|
|
17829
|
+
});
|
|
17788
17830
|
}
|
|
17789
17831
|
async handleBrowse(url, res) {
|
|
17790
17832
|
if (!this.browseRoot) {
|