Ground Bean Bro in displayed temperatures, stream replies, and add activity calendar
Test and deploy / test-and-deploy (push) Successful in 1m19s
Test and deploy / test-and-deploy (push) Successful in 1m19s
This commit is contained in:
1 parent
a72aec9f86
commit
d96ed22de1
16 files changed
+551
-484
No files matched your search
@@ -33,3 +33,51 @@ export async function api(url, options = {}) {
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
// Fetch-based SSE keeps the existing cookie authentication and CSRF protection.
|
||||
export async function streamPlanChat(body, onText) {
|
||||
const response = await protectedFetch("/api/plan-chat", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json", accept: "text/event-stream" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const data = await response.json().catch(() => ({}));
|
||||
const error = new Error(data.error || data.code || "Request failed");
|
||||
error.code = data.code;
|
||||
if (response.status === 401 && data.code === "unauthorized") location.assign("/login");
|
||||
throw error;
|
||||
}
|
||||
// Supports an older server during rolling deployment.
|
||||
if (!response.headers.get("content-type")?.includes("text/event-stream"))
|
||||
return response.json();
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = "";
|
||||
try {
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
buffer += decoder.decode(value, { stream: !done });
|
||||
let end;
|
||||
while ((end = buffer.indexOf("\n\n")) !== -1) {
|
||||
const frame = buffer.slice(0, end);
|
||||
buffer = buffer.slice(end + 2);
|
||||
const event = frame.match(/^event: (.+)$/m)?.[1];
|
||||
const raw = frame.match(/^data: (.+)$/m)?.[1];
|
||||
if (!raw) continue;
|
||||
const data = JSON.parse(raw);
|
||||
if (event === "reply") onText(data.text);
|
||||
if (event === "done") return data;
|
||||
if (event === "error") {
|
||||
const error = new Error(data.error || "Reply interrupted");
|
||||
error.code = data.code;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (done) throw new Error("Connection interrupted before Bean Bro finished. Please retry.");
|
||||
}
|
||||
} finally {
|
||||
await reader.cancel().catch(() => {});
|
||||
reader.releaseLock();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user