gadget/packages/api/src/messages/socket.ts
Rob Colbert 1e13f95808 Phase 2: ACE Editor integration and file operations
- Added react-ace and ace-builds dependencies
- Created EditorPanel component with ACE editor integration
- Implemented file read/write socket protocol
- Added backend handlers for fileReadRequest and fileWriteRequest
- Implemented file loading from tree click
- Implemented file saving with Ctrl+S shortcut
- Added dirty state tracking and unsaved changes indicator
- Enforced workspace mode (read-only in Agent mode)
- Added security: path traversal prevention, binary file detection, file size limits
- Updated FilesPanel with split view (tree + editor)

Enables Users to edit files for the first time in Gadget Code.
2026-05-12 19:32:58 -04:00

159 lines
4.5 KiB
TypeScript

// src/messages/socket.ts
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
// Licensed under the Apache License, Version 2.0
import {
StatusMessage,
ProcessWorkOrderMessage,
ResponseMessage,
ThinkingMessage,
ToolCallMessage,
WorkOrderCompleteMessage,
RequestCrashRecoveryMessage,
CrashRecoveryResponseMessage,
RequestTerminationMessage,
WorkspaceModeChangedMessage,
LogMessage,
} from "./drone.ts";
import { SessionUpdatedMessage } from "./web.ts";
import {
AbortWorkOrderMessage,
ReleaseSessionLockMessage,
RequestSessionLockMessage,
RequestWorkspaceModeMessage,
SessionHeartbeatMessage,
SubmitPromptMessage,
FileTreeRequestMessage,
FileTreeResponseMessage,
FileReadRequestMessage,
FileReadResponseMessage,
FileWriteRequestMessage,
FileWriteResponseMessage,
} from "./ide.ts";
/*
There are two different kinds of clients that connect to the gadget-code:web
Socket.IO server:
1. The gadget-code:ide (ReactJS front-end)
2. The gadget-drone work order runner (NodeJS headless process)
gadget-code:ide sends Socket.IO messages to gadget-code:web, which then routes
them to the appropriate gadget-drone socket.
gadget-drone sends messages to gadget-code:web intending for them to be routed
to the appropriate gadget-code:ide socket.
This architecture lets the IDE run under User control in any browser anywhere,
and serve as a remote control surface for one or more gadget-drone processes
running work orders on projects in chat sessions.
*/
export interface ClientToServerEvents {
/*
* gadget-code:ide => gadget-code:web
*/
requestSessionLock: RequestSessionLockMessage;
requestWorkspaceMode: RequestWorkspaceModeMessage;
submitPrompt: SubmitPromptMessage;
abortWorkOrder: AbortWorkOrderMessage;
releaseSessionLock: ReleaseSessionLockMessage;
sessionHeartbeat: SessionHeartbeatMessage;
fileTreeRequest: FileTreeRequestMessage;
fileReadRequest: FileReadRequestMessage;
fileWriteRequest: FileWriteRequestMessage;
/*
* gadget-drone => gadget-code:web
*/
log: LogMessage;
status: StatusMessage;
thinking: ThinkingMessage;
response: ResponseMessage;
toolCall: ToolCallMessage;
workOrderComplete: WorkOrderCompleteMessage;
requestCrashRecovery: RequestCrashRecoveryMessage;
requestTermination: RequestTerminationMessage;
workspaceModeChanged: WorkspaceModeChangedMessage;
"agent:thinking": AgentThinkingMessage;
"agent:response": AgentResponseMessage;
"agent:tool-call": AgentToolCallMessage;
"agent:tool-result": AgentToolResultMessage;
"agent:complete": AgentCompleteMessage;
}
export type AgentThinkingMessage = (data: {
agentId: string;
thinking: string;
}) => void;
export type AgentResponseMessage = (data: {
agentId: string;
chunk: string;
}) => void;
export type AgentToolCallMessage = (data: {
agentId: string;
tool: string;
args: unknown;
}) => void;
export type AgentToolResultMessage = (data: {
agentId: string;
tool: string;
result: unknown;
}) => void;
export type AgentCompleteMessage = (data: {
agentId: string;
response?: string;
subagent?: Record<string, unknown>;
stats?: Record<string, unknown>;
}) => void;
export interface ServerToClientEvents {
/*
* gadget-code:web => gadget-drone
*/
requestSessionLock: RequestSessionLockMessage;
requestWorkspaceMode: RequestWorkspaceModeMessage;
releaseSessionLock: ReleaseSessionLockMessage;
sessionHeartbeat: SessionHeartbeatMessage;
abortWorkOrder: AbortWorkOrderMessage;
processWorkOrder: ProcessWorkOrderMessage;
crashRecoveryResponse: CrashRecoveryResponseMessage;
requestTermination: RequestTerminationMessage;
fileTreeRequest: FileTreeRequestMessage;
fileReadRequest: FileReadRequestMessage;
fileWriteRequest: FileWriteRequestMessage;
/*
* gadget-code:web => gadget-code:ide
*/
log: LogMessage;
status: StatusMessage;
thinking: ThinkingMessage;
response: ResponseMessage;
toolCall: ToolCallMessage;
workOrderComplete: WorkOrderCompleteMessage;
workspaceModeChanged: WorkspaceModeChangedMessage;
sessionUpdated: SessionUpdatedMessage;
tabLockDenied: (data: { message: string }) => void;
fileTreeResponse: FileTreeResponseMessage;
fileReadResponse: FileReadResponseMessage;
fileWriteResponse: FileWriteResponseMessage;
"agent:thinking": AgentThinkingMessage;
"agent:response": AgentResponseMessage;
"agent:tool-call": AgentToolCallMessage;
"agent:tool-result": AgentToolResultMessage;
"agent:complete": AgentCompleteMessage;
}
export interface SocketData {
/* no data defined */
}