@oharness/server 0.7.1 → 0.9.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/browser-pane.d.ts +48 -0
- package/dist/browser-pane.js +185 -0
- package/dist/browser-pane.js.map +1 -0
- package/dist/files.d.ts +40 -1
- package/dist/files.js +94 -3
- package/dist/files.js.map +1 -1
- package/dist/i18n.js +190 -0
- package/dist/i18n.js.map +1 -1
- package/dist/main.js +20 -0
- package/dist/main.js.map +1 -1
- package/dist/page/client/account.d.ts +8 -0
- package/dist/page/client/account.js +1895 -0
- package/dist/page/client/account.js.map +1 -0
- package/dist/page/client/artifacts.d.ts +7 -0
- package/dist/page/client/artifacts.js +767 -0
- package/dist/page/client/artifacts.js.map +1 -0
- package/dist/page/client/base.d.ts +8 -0
- package/dist/page/client/base.js +404 -0
- package/dist/page/client/base.js.map +1 -0
- package/dist/page/client/browser.d.ts +21 -0
- package/dist/page/client/browser.js +906 -0
- package/dist/page/client/browser.js.map +1 -0
- package/dist/page/client/composer.d.ts +8 -0
- package/dist/page/client/composer.js +1714 -0
- package/dist/page/client/composer.js.map +1 -0
- package/dist/page/client/dialogs.d.ts +7 -0
- package/dist/page/client/dialogs.js +724 -0
- package/dist/page/client/dialogs.js.map +1 -0
- package/dist/page/client/events.d.ts +7 -0
- package/dist/page/client/events.js +288 -0
- package/dist/page/client/events.js.map +1 -0
- package/dist/page/client/files.d.ts +7 -0
- package/dist/page/client/files.js +412 -0
- package/dist/page/client/files.js.map +1 -0
- package/dist/page/client/index.d.ts +16 -0
- package/dist/page/client/index.js +50 -0
- package/dist/page/client/index.js.map +1 -0
- package/dist/page/client/panels.d.ts +7 -0
- package/dist/page/client/panels.js +659 -0
- package/dist/page/client/panels.js.map +1 -0
- package/dist/page/client/pickers.d.ts +7 -0
- package/dist/page/client/pickers.js +624 -0
- package/dist/page/client/pickers.js.map +1 -0
- package/dist/page/client/routines.d.ts +6 -0
- package/dist/page/client/routines.js +928 -0
- package/dist/page/client/routines.js.map +1 -0
- package/dist/page/client/shell.d.ts +6 -0
- package/dist/page/client/shell.js +700 -0
- package/dist/page/client/shell.js.map +1 -0
- package/dist/page/client/state.d.ts +7 -0
- package/dist/page/client/state.js +1283 -0
- package/dist/page/client/state.js.map +1 -0
- package/dist/page/client/terminal.d.ts +7 -0
- package/dist/page/client/terminal.js +347 -0
- package/dist/page/client/terminal.js.map +1 -0
- package/dist/page/client/transcript.d.ts +8 -0
- package/dist/page/client/transcript.js +866 -0
- package/dist/page/client/transcript.js.map +1 -0
- package/dist/page/client/usage.d.ts +7 -0
- package/dist/page/client/usage.js +954 -0
- package/dist/page/client/usage.js.map +1 -0
- package/dist/page/style.d.ts +1 -0
- package/dist/page/style.js +3016 -0
- package/dist/page/style.js.map +1 -0
- package/dist/page.js +96 -13871
- package/dist/page.js.map +1 -1
- package/dist/server.d.ts +24 -0
- package/dist/server.js +492 -23
- package/dist/server.js.map +1 -1
- package/dist/terminal.d.ts +34 -0
- package/dist/terminal.js +211 -0
- package/dist/terminal.js.map +1 -0
- package/package.json +9 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialogs.js","sourceRoot":"","sources":["../../../src/page/client/dialogs.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4sBhC,CAAC"}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The event stream. Everything the server says happened arrives here and is
|
|
3
|
+
* turned into a change on screen.
|
|
4
|
+
*
|
|
5
|
+
* One part of the client script; see ./index.ts for how the parts join.
|
|
6
|
+
*/
|
|
7
|
+
export const EVENTS = String.raw `
|
|
8
|
+
// --- events ---------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
const events = new EventSource(q("/events"));
|
|
11
|
+
events.onerror = () => { ctxText.textContent = "offline"; };
|
|
12
|
+
events.onmessage = (message) => {
|
|
13
|
+
const event = JSON.parse(message.data);
|
|
14
|
+
|
|
15
|
+
// Session-scoped events render into their own pane; the rest (model lists,
|
|
16
|
+
// notices about discovery) apply to the page.
|
|
17
|
+
if (event.session) {
|
|
18
|
+
P = pane(event.session);
|
|
19
|
+
if (!panes.has(activeId)) activeId = event.session;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
switch (event.type) {
|
|
23
|
+
case "connection_card":
|
|
24
|
+
connectionCard(event.card);
|
|
25
|
+
break;
|
|
26
|
+
case "prompt": {
|
|
27
|
+
// The previews this tab made a moment ago, so a message just sent looks
|
|
28
|
+
// like the same message will after a reload — the transcript has not
|
|
29
|
+
// been written yet, so there is nothing at /attachment to ask for.
|
|
30
|
+
const sent = sentPreviews.get(event.session) || [];
|
|
31
|
+
sentPreviews.delete(event.session);
|
|
32
|
+
addUser(event.text || "", sent);
|
|
33
|
+
startWorking(t("work.thinking"));
|
|
34
|
+
const unshown = (event.attachments || []).filter(
|
|
35
|
+
(name) => !sent.some((image) => image.name === name),
|
|
36
|
+
);
|
|
37
|
+
if (unshown.length) addNotice("attached: " + unshown.join(", "));
|
|
38
|
+
// Without this a turn that reads three files looks like it went and
|
|
39
|
+
// found them by itself, rather than being pointed at them.
|
|
40
|
+
if (event.references && event.references.length) {
|
|
41
|
+
addNotice("referenced: " + event.references.map((ref) => ref.value).join(", "));
|
|
42
|
+
}
|
|
43
|
+
setBusy(true);
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
case "text_delta":
|
|
48
|
+
sealThinking();
|
|
49
|
+
stopWorking();
|
|
50
|
+
if (!P.streaming) { P.streaming = { text: "", node: el("div") }; assistantTurn().appendChild(P.streaming.node); }
|
|
51
|
+
P.streaming.text += event.text;
|
|
52
|
+
// Re-render the whole block: markdown is not incremental, and a half
|
|
53
|
+
// fence rendered as literal text then rewritten looks worse than this.
|
|
54
|
+
P.streaming.node.replaceChildren(renderMarkdown(P.streaming.text));
|
|
55
|
+
follow();
|
|
56
|
+
break;
|
|
57
|
+
|
|
58
|
+
case "thinking_delta": {
|
|
59
|
+
// Tracked on the turn rather than found with :last-child, which stopped
|
|
60
|
+
// matching the moment anything else was appended after it.
|
|
61
|
+
let block = P.turn && P.turn.__thinking;
|
|
62
|
+
if (!block) {
|
|
63
|
+
// The reasoning block carries its own spinner and streams visible text,
|
|
64
|
+
// so the waiting row above it would be a second "Thinking" saying less.
|
|
65
|
+
stopWorking();
|
|
66
|
+
block = el("details", "thinking");
|
|
67
|
+
block.open = true;
|
|
68
|
+
const summary = el("summary");
|
|
69
|
+
summary.appendChild(el("span", "spinner"));
|
|
70
|
+
summary.appendChild(el("span", "t-label", t("work.thinking")));
|
|
71
|
+
block.appendChild(summary);
|
|
72
|
+
block.appendChild(el("div", "thinking-body"));
|
|
73
|
+
assistantTurn().appendChild(block);
|
|
74
|
+
P.turn.__thinking = block;
|
|
75
|
+
P.turn.__thinkingStart = Date.now();
|
|
76
|
+
}
|
|
77
|
+
block.querySelector(".thinking-body").textContent += event.text;
|
|
78
|
+
follow();
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// One event carrying the whole list rather than add/remove pairs: the
|
|
83
|
+
// page redraws from it, so a dropped event cannot leave a chip behind for
|
|
84
|
+
// a message that was sent ten minutes ago.
|
|
85
|
+
case "queue":
|
|
86
|
+
P.queued = event.queued || [];
|
|
87
|
+
renderQueued();
|
|
88
|
+
break;
|
|
89
|
+
|
|
90
|
+
case "assistant_message": P.streaming = null; break;
|
|
91
|
+
case "turn_start": dropPendingTools(); break;
|
|
92
|
+
// A session named from its first prompt, or renamed: the tab and the
|
|
93
|
+
// sidebar row both carry the name.
|
|
94
|
+
case "title":
|
|
95
|
+
P.title = event.title;
|
|
96
|
+
paintTabs();
|
|
97
|
+
void loadSessions();
|
|
98
|
+
break;
|
|
99
|
+
case "tool_pending":
|
|
100
|
+
sealThinking();
|
|
101
|
+
pendingToolCard(event.toolCallId, event.toolName);
|
|
102
|
+
startWorking(toolTitle(event.toolName));
|
|
103
|
+
break;
|
|
104
|
+
case "tool_start":
|
|
105
|
+
sealThinking();
|
|
106
|
+
browserToolStarted(event.session, event.toolName);
|
|
107
|
+
toolCard(event.toolCallId, event.title, event.toolName, event.input);
|
|
108
|
+
startWorking(event.toolName);
|
|
109
|
+
break;
|
|
110
|
+
case "tool_end":
|
|
111
|
+
finishTool(event.toolCallId, event.result && event.result.content, event.durationMs,
|
|
112
|
+
event.result && event.result.isError);
|
|
113
|
+
// Back to waiting on the model until the next tool or the next token.
|
|
114
|
+
startWorking(t("work.thinking"));
|
|
115
|
+
break;
|
|
116
|
+
|
|
117
|
+
case "artifact": {
|
|
118
|
+
// A revision the user typed, in a canvas they are looking at right now.
|
|
119
|
+
// It gets neither a card nor a redraw: the transcript records what
|
|
120
|
+
// happened in the conversation and this did not happen there, and
|
|
121
|
+
// rebuilding the frame would reload the editor under the hands of the
|
|
122
|
+
// person using it — losing their viewport, their selection and their
|
|
123
|
+
// undo history at the exact moment they pressed Save.
|
|
124
|
+
void refreshArtifactCount();
|
|
125
|
+
if (event.artifact.author === "user") {
|
|
126
|
+
const mine = artifacts.findIndex((a) => a.id === event.artifact.id);
|
|
127
|
+
if (mine >= 0) artifacts[mine] = event.artifact;
|
|
128
|
+
artifactVersion.delete(event.artifact.id);
|
|
129
|
+
if (selectedArtifact === event.artifact.id) refreshArtifactBar(event.artifact);
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// A card in the conversation, not only a row in the drawer. The drawer
|
|
134
|
+
// is a place you have to know to look; the transcript is where the user
|
|
135
|
+
// already is, and an artifact is a thing that happened in it.
|
|
136
|
+
artifactCard(event.artifact);
|
|
137
|
+
// The descriptor carries no body, so the list is patched from it and only
|
|
138
|
+
// the visible artifact is refetched. A revision of something the user is
|
|
139
|
+
// not looking at costs nothing.
|
|
140
|
+
const at = artifacts.findIndex((a) => a.id === event.artifact.id);
|
|
141
|
+
const before = at >= 0 ? artifacts[at] : null;
|
|
142
|
+
if (at >= 0) artifacts[at] = event.artifact;
|
|
143
|
+
else artifacts = artifacts.concat([event.artifact]);
|
|
144
|
+
// A new revision moves the view back to the latest: the user is watching
|
|
145
|
+
// the model work, and pinning them to v3 while it writes v4 is wrong.
|
|
146
|
+
// Only a revision, though — this event also fires when an artifact is
|
|
147
|
+
// published or withdrawn, and that must not yank the reader off the
|
|
148
|
+
// version they deliberately went back to.
|
|
149
|
+
if (!before || before.version !== event.artifact.version) {
|
|
150
|
+
artifactVersion.delete(event.artifact.id);
|
|
151
|
+
}
|
|
152
|
+
const wasSelected = selectedArtifact === event.artifact.id;
|
|
153
|
+
renderArtifacts(artifacts);
|
|
154
|
+
repaintPanel();
|
|
155
|
+
if (wasSelected) void showArtifact(event.artifact.id);
|
|
156
|
+
// First artifact of the session opens the drawer, the way the first file
|
|
157
|
+
// change makes the review button appear. Later ones do not steal focus.
|
|
158
|
+
// Not on a phone, where the drawer is the whole screen: it would cover
|
|
159
|
+
// the reply that produced the artifact.
|
|
160
|
+
if (artifacts.length === 1 && !isPhone()) shelf.classList.add("open");
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
case "permission_request": showAsk(event); break;
|
|
165
|
+
case "question_request": questionCard(event); break;
|
|
166
|
+
case "question_result": settleQuestion(event); break;
|
|
167
|
+
// Sent once per (re)connect, after the replay: a card this page still
|
|
168
|
+
// shows as open but the server no longer holds closed while we were away.
|
|
169
|
+
case "pending_interactions":
|
|
170
|
+
for (const entry of questionCards.values()) {
|
|
171
|
+
if ((entry.status === "open" || entry.status === "sending") && !event.ids.includes(entry.id)) closeQuestion(entry.id);
|
|
172
|
+
}
|
|
173
|
+
break;
|
|
174
|
+
// Answered in another window, or the turn waiting on it stopped.
|
|
175
|
+
case "interaction_closed":
|
|
176
|
+
withdrawAsk(event.id);
|
|
177
|
+
closeQuestion(event.id);
|
|
178
|
+
break;
|
|
179
|
+
case "permission_result":
|
|
180
|
+
if (!event.allowed) addNotice("denied: " + (event.reason || ""), "err");
|
|
181
|
+
else if (event.remembered) addNotice("remembered: " + event.remembered);
|
|
182
|
+
break;
|
|
183
|
+
|
|
184
|
+
case "compacted":
|
|
185
|
+
addNotice("compacted " + event.droppedMessages + " messages (" +
|
|
186
|
+
event.tokensBefore + " → " + event.tokensAfter + " tokens)");
|
|
187
|
+
break;
|
|
188
|
+
|
|
189
|
+
case "model_changed": addNotice(event.from + " → " + event.to); break;
|
|
190
|
+
case "memory_recall":
|
|
191
|
+
addNotice(t("memory.recalled", { count: event.notes.length, list: event.notes.map((note) => note.summary).join("; ") }));
|
|
192
|
+
break;
|
|
193
|
+
case "memory_extracted":
|
|
194
|
+
addNotice(t("memory.extracted", { count: event.notes.length, list: event.notes.map((note) => note.summary).join("; ") }));
|
|
195
|
+
break;
|
|
196
|
+
case "notice": addNotice(event.message); break;
|
|
197
|
+
// A routine ran: its session is new, and the Routines page, if open, has
|
|
198
|
+
// a result to show.
|
|
199
|
+
case "sessions":
|
|
200
|
+
void loadSessions();
|
|
201
|
+
if (shownPanel === "nav-routines") void showRoutines();
|
|
202
|
+
break;
|
|
203
|
+
case "error":
|
|
204
|
+
P.streaming = null;
|
|
205
|
+
// A 402 is not an error to print, it is a screen to open. What the
|
|
206
|
+
// gateway sends back is an HTTP body — a true sentence, in English,
|
|
207
|
+
// with nothing in it to click — and printing that was the whole of the
|
|
208
|
+
// old behaviour on the one failure the user can actually fix.
|
|
209
|
+
if (event.status === 402) { openPaywall(); break; }
|
|
210
|
+
// A 401 is the other status worth reading rather than printing. The
|
|
211
|
+
// credential the request went out with was refused, which means the
|
|
212
|
+
// panel's belief that this vendor is connected is now known to be
|
|
213
|
+
// wrong — and the vendor's own JSON, pasted into the transcript, tells
|
|
214
|
+
// the user neither that nor what to do about it.
|
|
215
|
+
if (event.status === 401 && event.provider) { openReconnect(event.provider); break; }
|
|
216
|
+
// Never signed in at all. It carries no status — nothing was sent — but
|
|
217
|
+
// it is the same screen as a refused key, and the engine's sentence ends
|
|
218
|
+
// at "or sign in" precisely because where that is done differs per
|
|
219
|
+
// frontend. Here it is a dialog, one click away.
|
|
220
|
+
if (event.code === "no_credentials" && event.provider) { openSignIn(event.provider); break; }
|
|
221
|
+
addNotice("✗ " + event.error, "err");
|
|
222
|
+
break;
|
|
223
|
+
case "done": {
|
|
224
|
+
sealThinking();
|
|
225
|
+
stopWorking();
|
|
226
|
+
dropPendingTools();
|
|
227
|
+
// The fork point comes from the server: one reply can span several
|
|
228
|
+
// stored messages, and a client-side count would drift the moment tool
|
|
229
|
+
// results land in a message of their own.
|
|
230
|
+
const finished = P.turn;
|
|
231
|
+
P.streaming = null;
|
|
232
|
+
P.turn = null;
|
|
233
|
+
if (finished) {
|
|
234
|
+
void getJson("/history")
|
|
235
|
+
.then((body) => {
|
|
236
|
+
const last = (body.entries || []).at(-1);
|
|
237
|
+
if (last) turnActions(finished, last.keepMessages);
|
|
238
|
+
})
|
|
239
|
+
.catch(() => {});
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
case "idle":
|
|
244
|
+
applyState(event.state);
|
|
245
|
+
renderChanges(event.changes);
|
|
246
|
+
void loadSessions();
|
|
247
|
+
break;
|
|
248
|
+
case "state": applyState(event.state); break;
|
|
249
|
+
case "connectors_changed":
|
|
250
|
+
if (authorizing === event.connector) authorizing = null;
|
|
251
|
+
offer = { connectors: event.connectors, plugins: event.plugins || [], skills: event.skills || [] };
|
|
252
|
+
// The failure already said what went wrong; this only announces the
|
|
253
|
+
// half that is worth celebrating.
|
|
254
|
+
if (event.authorized) addNotice(t("market.authorized", { name: event.connector }));
|
|
255
|
+
paintMarket();
|
|
256
|
+
break;
|
|
257
|
+
case "models_changed":
|
|
258
|
+
void loadModels().then(() => {
|
|
259
|
+
addNotice("discovered " + event.discovered + " models from " + event.provider);
|
|
260
|
+
// A sign-in that finished in a browser tab lands here, not in the
|
|
261
|
+
// click that started it — so whatever was waiting on it is told now.
|
|
262
|
+
const waiting = signedInWaiters.get(event.provider);
|
|
263
|
+
if (waiting) {
|
|
264
|
+
signedInWaiters.delete(event.provider);
|
|
265
|
+
waiting();
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (document.getElementById("nav-models").classList.contains("active")) void showModels();
|
|
269
|
+
});
|
|
270
|
+
break;
|
|
271
|
+
// Also fired when another window switches the project, which moves the
|
|
272
|
+
// working directory out from under whatever panel is open here.
|
|
273
|
+
case "session_changed":
|
|
274
|
+
applyState(event.state);
|
|
275
|
+
renderHistory(event.entries);
|
|
276
|
+
// Artifacts are per-session, so the shelf has to be rebuilt rather than
|
|
277
|
+
// left showing the previous session's work under the new transcript.
|
|
278
|
+
selectedArtifact = null;
|
|
279
|
+
artifactVersion.clear();
|
|
280
|
+
shelf.classList.remove("open", "full");
|
|
281
|
+
document.body.classList.remove("shelf-full");
|
|
282
|
+
void loadArtifacts();
|
|
283
|
+
void loadProjects().then(repaintPanel);
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
`;
|
|
288
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../../src/page/client/events.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwR/B,CAAC"}
|