feat: add sproutclaw webui

This commit is contained in:
root
2026-05-10 14:47:51 +08:00
parent e25415dd5f
commit c5295773a1
19 changed files with 6299 additions and 69 deletions

View File

@@ -25,6 +25,7 @@ import { type Theme, theme } from "../interactive/theme/theme.js";
import { attachJsonlLineReader, serializeJsonLine } from "./jsonl.js";
import type {
RpcCommand,
RpcExtensionInfo,
RpcExtensionUIRequest,
RpcExtensionUIResponse,
RpcResponse,
@@ -442,10 +443,18 @@ export async function runRpcMode(runtimeHost: AgentSessionRuntime): Promise<neve
autoCompactionEnabled: session.autoCompactionEnabled,
messageCount: session.messages.length,
pendingMessageCount: session.pendingMessageCount,
turnIndex: session.turnIndex,
stats: session.getSessionStats(),
};
return success(id, "get_state", state);
}
case "reload": {
await session.reload();
await rebindSession();
return success(id, "reload");
}
// =================================================================
// Model
// =================================================================
@@ -561,7 +570,7 @@ export async function runRpcMode(runtimeHost: AgentSessionRuntime): Promise<neve
}
case "switch_session": {
const result = await runtimeHost.switchSession(command.sessionPath);
const result = await runtimeHost.switchSession(command.sessionPath, { cwdOverride: command.cwdOverride });
if (!result.cancelled) {
await rebindSession();
}
@@ -652,6 +661,26 @@ export async function runRpcMode(runtimeHost: AgentSessionRuntime): Promise<neve
return success(id, "get_commands", { commands });
}
case "get_extensions": {
const extensionsResult = session.resourceLoader.getExtensions();
const extensions: RpcExtensionInfo[] = extensionsResult.extensions.map((extension) => {
const name = extension.path.split(/[\\/]/).filter(Boolean).pop() || extension.path;
return {
name,
path: extension.path,
resolvedPath: extension.resolvedPath,
sourceInfo: extension.sourceInfo,
commands: Array.from(extension.commands.keys()).sort(),
tools: Array.from(extension.tools.keys()).sort(),
flags: Array.from(extension.flags.keys()).sort(),
shortcuts: Array.from(extension.shortcuts.keys()).map(String).sort(),
handlers: Array.from(extension.handlers.keys()).sort(),
};
});
return success(id, "get_extensions", { extensions, errors: extensionsResult.errors });
}
default: {
const unknownCommand = command as { type: string };
return error(undefined, unknownCommand.type, `Unknown command: ${unknownCommand.type}`);

View File

@@ -26,6 +26,7 @@ export type RpcCommand =
// State
| { id?: string; type: "get_state" }
| { id?: string; type: "reload" }
// Model
| { id?: string; type: "set_model"; provider: string; modelId: string }
@@ -55,7 +56,7 @@ export type RpcCommand =
// Session
| { id?: string; type: "get_session_stats" }
| { id?: string; type: "export_html"; outputPath?: string }
| { id?: string; type: "switch_session"; sessionPath: string }
| { id?: string; type: "switch_session"; sessionPath: string; cwdOverride?: string }
| { id?: string; type: "fork"; entryId: string }
| { id?: string; type: "clone" }
| { id?: string; type: "get_fork_messages" }
@@ -66,7 +67,8 @@ export type RpcCommand =
| { id?: string; type: "get_messages" }
// Commands (available for invocation via prompt)
| { id?: string; type: "get_commands" };
| { id?: string; type: "get_commands" }
| { id?: string; type: "get_extensions" };
// ============================================================================
// RPC Slash Command (for get_commands response)
@@ -84,6 +86,19 @@ export interface RpcSlashCommand {
sourceInfo: SourceInfo;
}
/** A loaded extension with basic inventory metadata */
export interface RpcExtensionInfo {
name: string;
path: string;
resolvedPath: string;
sourceInfo: SourceInfo;
commands: string[];
tools: string[];
flags: string[];
shortcuts: string[];
handlers: string[];
}
// ============================================================================
// RPC State
// ============================================================================
@@ -101,6 +116,9 @@ export interface RpcSessionState {
autoCompactionEnabled: boolean;
messageCount: number;
pendingMessageCount: number;
/** Mirrors {@link AgentSession.turnIndex} (ticks every turn_end, resets agent_start). */
turnIndex: number;
stats: SessionStats;
}
// ============================================================================
@@ -118,6 +136,7 @@ export type RpcResponse =
// State
| { id?: string; type: "response"; command: "get_state"; success: true; data: RpcSessionState }
| { id?: string; type: "response"; command: "reload"; success: true }
// Model
| {
@@ -201,6 +220,13 @@ export type RpcResponse =
success: true;
data: { commands: RpcSlashCommand[] };
}
| {
id?: string;
type: "response";
command: "get_extensions";
success: true;
data: { extensions: RpcExtensionInfo[]; errors: Array<{ path: string; error: string }> };
}
// Error response (any command can fail)
| { id?: string; type: "response"; command: string; success: false; error: string };