@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.cjs
CHANGED
|
@@ -9623,19 +9623,39 @@ var UnknownOptionError = class extends Error {
|
|
|
9623
9623
|
question;
|
|
9624
9624
|
value;
|
|
9625
9625
|
};
|
|
9626
|
+
var UnsupportedPromptShapeError = class extends Error {
|
|
9627
|
+
constructor(detail) {
|
|
9628
|
+
super(`Unsupported prompt shape: ${detail}`);
|
|
9629
|
+
this.detail = detail;
|
|
9630
|
+
this.name = "UnsupportedPromptShapeError";
|
|
9631
|
+
}
|
|
9632
|
+
detail;
|
|
9633
|
+
};
|
|
9634
|
+
var IncompleteAnswerError = class extends Error {
|
|
9635
|
+
constructor(question) {
|
|
9636
|
+
super(`Missing answer for question "${question}"`);
|
|
9637
|
+
this.question = question;
|
|
9638
|
+
this.name = "IncompleteAnswerError";
|
|
9639
|
+
}
|
|
9640
|
+
question;
|
|
9641
|
+
};
|
|
9626
9642
|
function answersToKeystrokes(questions, answers) {
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
const raw = answers[q.question];
|
|
9630
|
-
if (raw === void 0) {
|
|
9631
|
-
throw new Error(`Missing answer for question "${q.question}"`);
|
|
9632
|
-
}
|
|
9633
|
-
const label = Array.isArray(raw) ? raw[0] : raw;
|
|
9634
|
-
const target = q.options.findIndex((o) => o.label === label);
|
|
9635
|
-
if (target < 0) throw new UnknownOptionError(q.question, label);
|
|
9636
|
-
out += DOWN.repeat(target) + ENTER3;
|
|
9643
|
+
if (questions.length !== 1) {
|
|
9644
|
+
throw new UnsupportedPromptShapeError(`${questions.length} questions`);
|
|
9637
9645
|
}
|
|
9638
|
-
|
|
9646
|
+
const q = questions[0];
|
|
9647
|
+
if (q.multiSelect) throw new UnsupportedPromptShapeError("multiSelect");
|
|
9648
|
+
const raw = answers[q.question];
|
|
9649
|
+
if (raw === void 0 || Array.isArray(raw) && raw.length === 0) {
|
|
9650
|
+
throw new IncompleteAnswerError(q.question);
|
|
9651
|
+
}
|
|
9652
|
+
if (Array.isArray(raw) && raw.length > 1) {
|
|
9653
|
+
throw new UnsupportedPromptShapeError(`${raw.length} labels for a single-select question`);
|
|
9654
|
+
}
|
|
9655
|
+
const label = Array.isArray(raw) ? raw[0] : raw;
|
|
9656
|
+
const target = q.options.findIndex((o) => o.label === label);
|
|
9657
|
+
if (target < 0) throw new UnknownOptionError(q.question, label);
|
|
9658
|
+
return DOWN.repeat(target) + ENTER3;
|
|
9639
9659
|
}
|
|
9640
9660
|
|
|
9641
9661
|
// src/services/questions/resolveAnswer.ts
|
|
@@ -9649,6 +9669,10 @@ function resolveAnswer(pending, body) {
|
|
|
9649
9669
|
return { ok: true, keys: answersToKeystrokes(pending.questions, answers) };
|
|
9650
9670
|
} catch (e) {
|
|
9651
9671
|
if (e instanceof UnknownOptionError) return { ok: false, reason: "unknown_option" };
|
|
9672
|
+
if (e instanceof UnsupportedPromptShapeError) {
|
|
9673
|
+
return { ok: false, reason: "unsupported_prompt_shape" };
|
|
9674
|
+
}
|
|
9675
|
+
if (e instanceof IncompleteAnswerError) return { ok: false, reason: "incomplete_answer" };
|
|
9652
9676
|
throw e;
|
|
9653
9677
|
}
|
|
9654
9678
|
}
|
|
@@ -9978,6 +10002,13 @@ var SessionHandlers = class {
|
|
|
9978
10002
|
get sessionSubscribers() {
|
|
9979
10003
|
return this.deps.sessionSubscribers;
|
|
9980
10004
|
}
|
|
10005
|
+
// Prompt lifecycle events (question / permission and their cancellations)
|
|
10006
|
+
// carry prompt content and go ONLY to the session's subscribers — never to
|
|
10007
|
+
// every connected socket. A client that subscribes after a prompt opened
|
|
10008
|
+
// gets it from the subscribe replay (server-wiring), not from a broadcast.
|
|
10009
|
+
broadcastToSession(sessionId, message) {
|
|
10010
|
+
this.wsHub.broadcastToClients(this.sessionSubscribers.get(sessionId) ?? [], message);
|
|
10011
|
+
}
|
|
9981
10012
|
get agentConfig() {
|
|
9982
10013
|
return this.deps.agentConfig;
|
|
9983
10014
|
}
|
|
@@ -10532,7 +10563,7 @@ var SessionHandlers = class {
|
|
|
10532
10563
|
const toolUseId = `screen:${sessionId}:${key.length}`;
|
|
10533
10564
|
this.pendingQuestions.set(sessionId, { toolUseId, questions, origin: "pty" });
|
|
10534
10565
|
this.pendingQuestionKey.set(sessionId, key);
|
|
10535
|
-
this.
|
|
10566
|
+
this.broadcastToSession(sessionId, { type: "question", sessionId, toolUseId, questions });
|
|
10536
10567
|
}
|
|
10537
10568
|
// Permission gate opened/closed (OSC 777 + scraped options). Broadcasts the
|
|
10538
10569
|
// additive `permission` / `permission_cancelled` events. Mobile answers by
|
|
@@ -10542,7 +10573,7 @@ var SessionHandlers = class {
|
|
|
10542
10573
|
if (!this.pendingPermission.has(sessionId)) return;
|
|
10543
10574
|
this.pendingPermission.delete(sessionId);
|
|
10544
10575
|
this.pendingPermissionKey.delete(sessionId);
|
|
10545
|
-
this.
|
|
10576
|
+
this.broadcastToSession(sessionId, { type: "permission_cancelled", sessionId });
|
|
10546
10577
|
return;
|
|
10547
10578
|
}
|
|
10548
10579
|
const key = permissionContentKey(gate);
|
|
@@ -10556,7 +10587,7 @@ var SessionHandlers = class {
|
|
|
10556
10587
|
`[ws.broadcast_permission] ${sessionId.slice(0, 8)} subscribers=${subscriberCount}`,
|
|
10557
10588
|
{ event: "ws.broadcast_permission", sessionId, subscriberCount }
|
|
10558
10589
|
);
|
|
10559
|
-
this.
|
|
10590
|
+
this.broadcastToSession(sessionId, {
|
|
10560
10591
|
type: "permission",
|
|
10561
10592
|
sessionId,
|
|
10562
10593
|
...gate.prompt ? { prompt: gate.prompt } : {},
|
|
@@ -10604,7 +10635,7 @@ var SessionHandlers = class {
|
|
|
10604
10635
|
const gateClosed = () => {
|
|
10605
10636
|
this.pendingPermission.delete(sessionId);
|
|
10606
10637
|
this.pendingPermissionKey.delete(sessionId);
|
|
10607
|
-
this.
|
|
10638
|
+
this.broadcastToSession(sessionId, { type: "permission_cancelled", sessionId });
|
|
10608
10639
|
json(res, 409, { ok: false, reason: "gate_closed" });
|
|
10609
10640
|
};
|
|
10610
10641
|
const gate = this.pendingPermission.get(sessionId);
|
|
@@ -10676,14 +10707,21 @@ var SessionHandlers = class {
|
|
|
10676
10707
|
const pending = this.pendingQuestions.get(sessionId);
|
|
10677
10708
|
const resolution = resolveAnswer(pending, body);
|
|
10678
10709
|
if (!resolution.ok) {
|
|
10679
|
-
|
|
10710
|
+
const unanswerable = resolution.reason === "unsupported_prompt_shape" || resolution.reason === "incomplete_answer";
|
|
10711
|
+
json(res, 400, {
|
|
10712
|
+
ok: false,
|
|
10713
|
+
reason: resolution.reason,
|
|
10714
|
+
...unanswerable ? {
|
|
10715
|
+
error: "This prompt needs an answer the app cannot give yet; answer it in the terminal"
|
|
10716
|
+
} : {}
|
|
10717
|
+
});
|
|
10680
10718
|
return;
|
|
10681
10719
|
}
|
|
10682
10720
|
const toolUseId = pending?.toolUseId ?? "";
|
|
10683
10721
|
if (!await this.questionMenuStillOpen(sessionId)) {
|
|
10684
10722
|
this.pendingQuestions.delete(sessionId);
|
|
10685
10723
|
this.pendingQuestionKey.delete(sessionId);
|
|
10686
|
-
this.
|
|
10724
|
+
this.broadcastToSession(sessionId, { type: "question_cancelled", sessionId, toolUseId });
|
|
10687
10725
|
json(res, 409, { ok: false, reason: "question_gone" });
|
|
10688
10726
|
return;
|
|
10689
10727
|
}
|
|
@@ -10695,7 +10733,7 @@ var SessionHandlers = class {
|
|
|
10695
10733
|
return;
|
|
10696
10734
|
}
|
|
10697
10735
|
this.pendingQuestions.delete(sessionId);
|
|
10698
|
-
this.
|
|
10736
|
+
this.broadcastToSession(sessionId, { type: "question_cancelled", sessionId, toolUseId });
|
|
10699
10737
|
json(res, 200, { ok: true });
|
|
10700
10738
|
}
|
|
10701
10739
|
// Best-effort: a session we don't own a PTY for, or one that raced away
|
|
@@ -17805,7 +17843,7 @@ var StreamerServer = class {
|
|
|
17805
17843
|
priorToolUseId
|
|
17806
17844
|
});
|
|
17807
17845
|
this.pendingQuestionKey.set(sessionId, key);
|
|
17808
|
-
if (broadcast) this.wsHub.
|
|
17846
|
+
if (broadcast) this.wsHub.broadcastToClients(this.sessionSubscribers.get(sessionId) ?? [], m);
|
|
17809
17847
|
}
|
|
17810
17848
|
}
|
|
17811
17849
|
cancelPendingQuestion(sessionId) {
|
|
@@ -17813,7 +17851,11 @@ var StreamerServer = class {
|
|
|
17813
17851
|
if (!pq) return;
|
|
17814
17852
|
this.pendingQuestions.delete(sessionId);
|
|
17815
17853
|
this.pendingQuestionKey.delete(sessionId);
|
|
17816
|
-
this.wsHub.
|
|
17854
|
+
this.wsHub.broadcastToClients(this.sessionSubscribers.get(sessionId) ?? [], {
|
|
17855
|
+
type: "question_cancelled",
|
|
17856
|
+
sessionId,
|
|
17857
|
+
toolUseId: pq.toolUseId
|
|
17858
|
+
});
|
|
17817
17859
|
}
|
|
17818
17860
|
async handleBrowse(url, res) {
|
|
17819
17861
|
if (!this.browseRoot) {
|