web: React+Vite SPA — sessions, streaming chat, task panel, spawn flow (build clean)

This commit is contained in:
Raphael Westphal
2026-08-18 13:37:14 +02:00
parent 345025b070
commit 7d5f866527
22 changed files with 4253 additions and 0 deletions
+158
View File
@@ -0,0 +1,158 @@
// Typed mirror of PROTOCOL.md v1.
export const PROTOCOL_VERSION: 1 = 1;
/** Event types emitted by the plugin, carried in the envelope `type` field. */
export const EventType = {
Hello: "hello",
MessageStart: "message_start",
MessageUpdate: "message_update",
MessageEnd: "message_end",
ToolExecutionStart: "tool_execution_start",
ToolExecutionUpdate: "tool_execution_update",
ToolExecutionEnd: "tool_execution_end",
AgentStart: "agent_start",
AgentEnd: "agent_end",
AgentSettled: "agent_settled",
SessionInfo: "session_info",
Bye: "bye",
} as const;
export type EventType = (typeof EventType)[keyof typeof EventType];
/** REST routes (base `/api`, bearer auth). */
export const Route = {
Sessions: "/api/sessions",
SessionEvents: (id: string): string => `/api/sessions/${encodeURIComponent(id)}/events`,
SessionPrompt: (id: string): string => `/api/sessions/${encodeURIComponent(id)}/prompt`,
SessionAbort: (id: string): string => `/api/sessions/${encodeURIComponent(id)}/abort`,
SessionContainer: (id: string): string => `/api/sessions/${encodeURIComponent(id)}/container`,
Spawn: "/api/spawn",
SpawnStatus: "/api/spawn/status",
GitlabStatus: "/api/gitlab/status",
GitlabConnect: "/api/gitlab/connect",
GitlabRepos: "/api/gitlab/repos",
} as const;
/** Session snapshot, carried by `hello`/`session_info` and REST session list. */
export interface SessionInfo {
id: string;
name: string | null;
cwd: string;
model: string;
provider: string;
/** true if spawned-by-daemon container session */
agent: boolean;
repo: string | null;
startedAt: number;
}
/** Session list row (REST `GET /api/sessions` + WS `session_list`). */
export interface SessionListItem extends SessionInfo {
online: boolean;
lastEventAt: number | null;
}
export type MessageRole = "user" | "assistant" | "toolResult" | "system";
export interface ToolCall {
id: string;
name: string;
argsJson: string;
}
export interface Message {
role: MessageRole;
id: string;
text: string;
thinking: string | null;
toolCalls: ToolCall[];
/** toolResult messages: which call this answers */
toolCallId: string | null;
}
/** Persisted event envelope + flattened payload. */
export interface EventFrame {
v: 1;
sessionId: string;
/** monotonic per-session, plugin-assigned, starts at 1 */
seq: number;
/** unix ms */
ts: number;
type: EventType | string;
// ---- payload fields (union, present depending on `type`) ----
session?: SessionInfo;
message?: Message;
delta?: string;
toolCallId?: string;
toolName?: string;
args?: string;
partial?: string;
isError?: boolean;
resultPreview?: string;
usage?: { inputTokens?: number; outputTokens?: number; totalCost?: number };
reason?: string;
}
/** Query params + response shapes for REST routes. */
export interface PromptBody {
message: string;
}
export interface PromptResponse {
ok: boolean;
}
export interface AbortResponse {
ok: boolean;
}
export interface ContainerResponse {
ok: boolean;
}
export interface SpawnBody {
repo: string;
branch?: string;
}
export interface SpawnResponse {
sessionId: string;
containerId: string;
}
export interface SpawnJob {
repo: string;
state: string;
containerId?: string;
sessionId?: string;
}
export interface GitlabStatus {
connected: boolean;
baseUrl: string;
username?: string;
}
export interface GitlabConnectResponse {
username: string;
}
export interface Repo {
path: string;
name: string;
namespace: string;
lastActivityAt: string;
webUrl: string;
defaultBranch: string;
}
/** ---- WS transport 3 (browser → daemon) ---- */
export type ServerFrame =
| { type: "session_list"; sessions: SessionListItem[] }
| { type: "events"; sessionId: string; after: number; events: EventFrame[] }
| { type: "spawn_status"; jobs: SpawnJob[] };
export type ClientFrame =
| { type: "subscribe"; sessionId: string }
| { type: "unsubscribe"; sessionId: string };