- gtk-window.ts: FFI wrapper calling libgtk-3.so.0 directly via bun:ffi - begin_resize_drag: delegates resize to window manager (zero CPU, smooth) - begin_move_drag: delegates move to window manager (replaces JS drag) - Removed all JavaScript-based drag/resize logic (no mousemove/mouseup) - RPC: window.beginResize + window.beginMove - Resize handles: 4px edges + 8px corners with proper cursors
868 lines
28 KiB
TypeScript
868 lines
28 KiB
TypeScript
/**
|
|
* Shared RPC schema for PTY bridge between Bun process and WebView.
|
|
*
|
|
* Bun holds the Unix socket connection to agor-ptyd; the WebView calls
|
|
* into Bun via requests, and Bun pushes output/close events via messages.
|
|
*/
|
|
|
|
// ── Requests (WebView → Bun, expects a response) ─────────────────────────────
|
|
|
|
export type PtyRPCRequests = {
|
|
/** Create a PTY session and subscribe to its output. */
|
|
"pty.create": {
|
|
params: {
|
|
sessionId: string;
|
|
cols: number;
|
|
rows: number;
|
|
/** Working directory for the shell process. */
|
|
cwd?: string;
|
|
/** Override shell binary (e.g. /usr/bin/ssh). Fix #3: direct spawn, no shell injection. */
|
|
shell?: string;
|
|
/** Arguments for the shell binary (e.g. ['-p', '22', 'user@host']). */
|
|
args?: string[];
|
|
};
|
|
response: { ok: boolean; error?: string };
|
|
};
|
|
/**
|
|
* Write input to a PTY session.
|
|
* `data` is raw UTF-8 text from the user (xterm onData). The pty-client
|
|
* layer encodes it to base64 before sending to the daemon; this RPC boundary
|
|
* carries raw text which the Bun handler forwards to PtyClient.writeInput().
|
|
*/
|
|
"pty.write": {
|
|
params: {
|
|
sessionId: string;
|
|
/** Raw UTF-8 text typed by the user (xterm onData delivers this). Encoded to base64 by pty-client before daemon transport. */
|
|
data: string;
|
|
};
|
|
response: { ok: boolean };
|
|
};
|
|
/** Notify the daemon that the terminal dimensions changed. */
|
|
"pty.resize": {
|
|
params: { sessionId: string; cols: number; rows: number };
|
|
response: { ok: boolean };
|
|
};
|
|
/** Unsubscribe from a session's output (session stays alive). */
|
|
"pty.unsubscribe": {
|
|
params: { sessionId: string };
|
|
response: { ok: boolean };
|
|
};
|
|
/** Kill a PTY session. */
|
|
"pty.close": {
|
|
params: { sessionId: string };
|
|
response: { ok: boolean };
|
|
};
|
|
|
|
// ── Settings RPC ───────────────────────────────────────────────────────────
|
|
|
|
/** Get a single setting value by key. Returns null if not set. */
|
|
"settings.get": {
|
|
params: { key: string };
|
|
response: { value: string | null };
|
|
};
|
|
/** Persist a setting key/value pair. */
|
|
"settings.set": {
|
|
params: { key: string; value: string };
|
|
response: { ok: boolean };
|
|
};
|
|
/** Return all settings as a flat object. */
|
|
"settings.getAll": {
|
|
params: Record<string, never>;
|
|
response: { settings: Record<string, string> };
|
|
};
|
|
/** Return all persisted projects. */
|
|
"settings.getProjects": {
|
|
params: Record<string, never>;
|
|
response: { projects: Array<{ id: string; config: string }> };
|
|
};
|
|
/** Persist a project config (JSON-serialised on the caller side). */
|
|
"settings.setProject": {
|
|
params: { id: string; config: string };
|
|
response: { ok: boolean };
|
|
};
|
|
/** Delete a project by id. */
|
|
"settings.deleteProject": {
|
|
params: { id: string };
|
|
response: { ok: boolean };
|
|
};
|
|
|
|
// ── Custom Themes RPC ──────────────────────────────────────────────────────
|
|
|
|
/** Return all user-saved custom themes. */
|
|
"themes.getCustom": {
|
|
params: Record<string, never>;
|
|
response: { themes: Array<{ id: string; name: string; palette: Record<string, string> }> };
|
|
};
|
|
/** Save (upsert) a custom theme by id. */
|
|
"themes.saveCustom": {
|
|
params: { id: string; name: string; palette: Record<string, string> };
|
|
response: { ok: boolean };
|
|
};
|
|
/** Delete a custom theme by id. */
|
|
"themes.deleteCustom": {
|
|
params: { id: string };
|
|
response: { ok: boolean };
|
|
};
|
|
|
|
// ── File I/O RPC ──────────────────────────────────────────────────────────
|
|
|
|
/** List directory children (files + subdirs). Returns sorted entries. */
|
|
"files.list": {
|
|
params: { path: string };
|
|
response: {
|
|
entries: Array<{
|
|
name: string;
|
|
type: "file" | "dir";
|
|
size: number;
|
|
}>;
|
|
error?: string;
|
|
};
|
|
};
|
|
/** Read a file's content. Returns text for text files, base64 for binary. */
|
|
"files.read": {
|
|
params: { path: string };
|
|
response: {
|
|
content?: string;
|
|
encoding: "utf8" | "base64";
|
|
size: number;
|
|
error?: string;
|
|
};
|
|
};
|
|
/** Get file stat info (mtime, size) for conflict detection. */
|
|
"files.stat": {
|
|
params: { path: string };
|
|
response: { mtimeMs: number; size: number; error?: string };
|
|
};
|
|
/** Extended stat — directory check, git detection, writability. Used by ProjectWizard. */
|
|
"files.statEx": {
|
|
params: { path: string };
|
|
response: {
|
|
exists: boolean;
|
|
isDirectory: boolean;
|
|
isGitRepo: boolean;
|
|
gitBranch?: string;
|
|
size?: number;
|
|
writable?: boolean;
|
|
error?: string;
|
|
};
|
|
};
|
|
"files.ensureDir": {
|
|
params: { path: string };
|
|
response: { ok: boolean; path?: string; error?: string };
|
|
};
|
|
/** Unguarded directory listing for PathBrowser (dirs only, no file content) */
|
|
"files.browse": {
|
|
params: { path: string };
|
|
response: { entries: { name: string; type: 'dir'; size: number }[]; error?: string };
|
|
};
|
|
/** Native folder picker dialog */
|
|
"files.pickDirectory": {
|
|
params: { startingFolder?: string };
|
|
response: { path: string | null };
|
|
};
|
|
/** Get home directory path */
|
|
"files.homeDir": {
|
|
params: {};
|
|
response: { path: string };
|
|
};
|
|
/** Write text content to a file (atomic temp+rename). */
|
|
"files.write": {
|
|
params: { path: string; content: string };
|
|
response: { ok: boolean; error?: string };
|
|
};
|
|
|
|
// ── Groups RPC ─────────────────────────────────────────────────────────────
|
|
|
|
/** Return all project groups. */
|
|
"groups.list": {
|
|
params: Record<string, never>;
|
|
response: { groups: Array<{ id: string; name: string; icon: string; position: number }> };
|
|
};
|
|
/** Create a new group. */
|
|
"groups.create": {
|
|
params: { id: string; name: string; icon: string; position: number };
|
|
response: { ok: boolean };
|
|
};
|
|
/** Delete a group by id. */
|
|
"groups.delete": {
|
|
params: { id: string };
|
|
response: { ok: boolean };
|
|
};
|
|
|
|
// ── Memora RPC ──────────────────────────────────────────────────────────
|
|
|
|
/** Search memories by query text (FTS5). */
|
|
"memora.search": {
|
|
params: { query: string; limit?: number };
|
|
response: {
|
|
memories: Array<{
|
|
id: number;
|
|
content: string;
|
|
tags: string;
|
|
metadata: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}>;
|
|
};
|
|
};
|
|
/** List recent memories. */
|
|
"memora.list": {
|
|
params: { limit?: number; tag?: string };
|
|
response: {
|
|
memories: Array<{
|
|
id: number;
|
|
content: string;
|
|
tags: string;
|
|
metadata: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}>;
|
|
};
|
|
};
|
|
|
|
// ── Project clone RPC ──────────────────────────────────────────────────────
|
|
|
|
/** Clone a project into a git worktree. branchName must match /^[a-zA-Z0-9\/_.-]+$/. */
|
|
"project.clone": {
|
|
params: { projectId: string; branchName: string };
|
|
response: { ok: boolean; project?: { id: string; config: string }; error?: string };
|
|
};
|
|
|
|
// ── Git RPC ──────────────────────────────────────────────────────────────────
|
|
|
|
/** List branches in a git repository. */
|
|
"git.branches": {
|
|
params: { path: string };
|
|
response: { branches: string[]; current: string; error?: string };
|
|
};
|
|
/** Clone a git repository. */
|
|
"git.clone": {
|
|
params: { url: string; target: string; branch?: string };
|
|
response: { ok: boolean; error?: string };
|
|
};
|
|
/** Probe a git remote URL (ls-remote). Returns branches on success. */
|
|
"git.probe": {
|
|
params: { url: string };
|
|
response: { ok: boolean; branches: string[]; defaultBranch: string; error?: string };
|
|
};
|
|
|
|
/** Check if sshfs is installed. */
|
|
"ssh.checkSshfs": {
|
|
params: Record<string, never>;
|
|
response: { installed: boolean; path: string | null };
|
|
};
|
|
|
|
/** Detect available shells on this system. */
|
|
"system.shells": {
|
|
params: Record<string, never>;
|
|
response: { shells: Array<{ path: string; name: string }>; loginShell: string };
|
|
};
|
|
|
|
/** Detect installed system fonts (uses fc-list). */
|
|
"system.fonts": {
|
|
params: Record<string, never>;
|
|
response: {
|
|
uiFonts: Array<{ family: string; preferred: boolean }>;
|
|
monoFonts: Array<{ family: string; isNerdFont: boolean }>;
|
|
};
|
|
};
|
|
|
|
// ── Project templates RPC ───────────────────────────────────────────────────
|
|
|
|
/** Return available project templates. Optionally pass custom template dir. */
|
|
"project.templates": {
|
|
params: { templateDir?: string };
|
|
response: {
|
|
templates: Array<{
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
icon: string;
|
|
}>;
|
|
};
|
|
};
|
|
/** Create a project from a template with real scaffold files. */
|
|
"project.createFromTemplate": {
|
|
params: { templateId: string; targetDir: string; projectName: string };
|
|
response: { ok: boolean; path: string; error?: string };
|
|
};
|
|
|
|
// ── Provider RPC ──────────────────────────────────────────────────────────
|
|
|
|
/** Scan for available AI providers on this machine. */
|
|
"provider.scan": {
|
|
params: Record<string, never>;
|
|
response: {
|
|
providers: Array<{
|
|
id: string;
|
|
available: boolean;
|
|
hasApiKey: boolean;
|
|
hasCli: boolean;
|
|
cliPath: string | null;
|
|
version: string | null;
|
|
}>;
|
|
};
|
|
};
|
|
/** Fetch model list for a specific provider. */
|
|
"provider.models": {
|
|
params: { provider: string };
|
|
response: {
|
|
models: Array<{
|
|
id: string;
|
|
name: string;
|
|
provider: string;
|
|
}>;
|
|
};
|
|
};
|
|
|
|
// ── Window control RPC ─────────────────────────────────────────────────────
|
|
|
|
/** Minimize the main window. */
|
|
"window.minimize": {
|
|
params: Record<string, never>;
|
|
response: { ok: boolean };
|
|
};
|
|
/** Toggle maximize/restore on the main window. */
|
|
"window.maximize": {
|
|
params: Record<string, never>;
|
|
response: { ok: boolean };
|
|
};
|
|
/** Close the main window. */
|
|
"window.close": {
|
|
params: Record<string, never>;
|
|
response: { ok: boolean };
|
|
};
|
|
/** Get current window frame (x, y, width, height). */
|
|
"window.getFrame": {
|
|
params: Record<string, never>;
|
|
response: { x: number; y: number; width: number; height: number };
|
|
};
|
|
/** Set the window position. */
|
|
"window.setPosition": {
|
|
params: { x: number; y: number };
|
|
response: { ok: boolean };
|
|
};
|
|
|
|
/** Begin native GTK resize drag — delegates to window manager. */
|
|
"window.beginResize": {
|
|
params: { edge: string; button: number; rootX: number; rootY: number };
|
|
response: { ok: boolean; error?: string };
|
|
};
|
|
/** Begin native GTK move drag — delegates to window manager. */
|
|
"window.beginMove": {
|
|
params: { button: number; rootX: number; rootY: number };
|
|
response: { ok: boolean };
|
|
};
|
|
|
|
// ── Keybindings RPC ────────────────────────────────────────────────────────
|
|
|
|
/** Return all persisted custom keybindings (overrides only). */
|
|
"keybindings.getAll": {
|
|
params: Record<string, never>;
|
|
response: { keybindings: Record<string, string> };
|
|
};
|
|
/** Persist a single keybinding override. */
|
|
"keybindings.set": {
|
|
params: { id: string; chord: string };
|
|
response: { ok: boolean };
|
|
};
|
|
/** Reset a keybinding to default (removes override). */
|
|
"keybindings.reset": {
|
|
params: { id: string };
|
|
response: { ok: boolean };
|
|
};
|
|
|
|
// ── Agent RPC ─────────────────────────────────────────────────────────────
|
|
|
|
/** Start an agent session with a given provider. */
|
|
"agent.start": {
|
|
params: {
|
|
sessionId: string;
|
|
provider: "claude" | "codex" | "ollama";
|
|
prompt: string;
|
|
cwd?: string;
|
|
model?: string;
|
|
systemPrompt?: string;
|
|
maxTurns?: number;
|
|
permissionMode?: string;
|
|
claudeConfigDir?: string;
|
|
extraEnv?: Record<string, string>;
|
|
additionalDirectories?: string[];
|
|
worktreeName?: string;
|
|
};
|
|
response: { ok: boolean; error?: string };
|
|
};
|
|
/** Stop a running agent session. */
|
|
"agent.stop": {
|
|
params: { sessionId: string };
|
|
response: { ok: boolean; error?: string };
|
|
};
|
|
/** Send a follow-up prompt to a running agent session. */
|
|
"agent.prompt": {
|
|
params: { sessionId: string; prompt: string };
|
|
response: { ok: boolean; error?: string };
|
|
};
|
|
/** List all active agent sessions with their state. */
|
|
"agent.list": {
|
|
params: Record<string, never>;
|
|
response: {
|
|
sessions: Array<{
|
|
sessionId: string;
|
|
provider: string;
|
|
status: string;
|
|
costUsd: number;
|
|
inputTokens: number;
|
|
outputTokens: number;
|
|
startedAt: number;
|
|
}>;
|
|
};
|
|
};
|
|
|
|
// ── Session persistence RPC ────────────────────────────────────────────
|
|
|
|
/** Save/update a session record. */
|
|
"session.save": {
|
|
params: {
|
|
projectId: string; sessionId: string; provider: string;
|
|
status: string; costUsd: number; inputTokens: number;
|
|
outputTokens: number; model: string; error?: string;
|
|
createdAt: number; updatedAt: number;
|
|
};
|
|
response: { ok: boolean };
|
|
};
|
|
/** Load the most recent session for a project. */
|
|
"session.load": {
|
|
params: { projectId: string };
|
|
response: {
|
|
session: {
|
|
projectId: string; sessionId: string; provider: string;
|
|
status: string; costUsd: number; inputTokens: number;
|
|
outputTokens: number; model: string; error?: string;
|
|
createdAt: number; updatedAt: number;
|
|
} | null;
|
|
};
|
|
};
|
|
/** List sessions for a project (max 20). */
|
|
"session.list": {
|
|
params: { projectId: string };
|
|
response: {
|
|
sessions: Array<{
|
|
projectId: string; sessionId: string; provider: string;
|
|
status: string; costUsd: number; inputTokens: number;
|
|
outputTokens: number; model: string; error?: string;
|
|
createdAt: number; updatedAt: number;
|
|
}>;
|
|
};
|
|
};
|
|
/** Save agent messages (batch). */
|
|
"session.messages.save": {
|
|
params: {
|
|
messages: Array<{
|
|
sessionId: string; msgId: string; role: string; content: string;
|
|
toolName?: string; toolInput?: string; timestamp: number;
|
|
costUsd?: number; inputTokens?: number; outputTokens?: number;
|
|
}>;
|
|
};
|
|
response: { ok: boolean };
|
|
};
|
|
/** Load all messages for a session. */
|
|
"session.messages.load": {
|
|
params: { sessionId: string };
|
|
response: {
|
|
messages: Array<{
|
|
sessionId: string; msgId: string; role: string; content: string;
|
|
toolName?: string; toolInput?: string; timestamp: number;
|
|
costUsd: number; inputTokens: number; outputTokens: number;
|
|
}>;
|
|
};
|
|
};
|
|
|
|
// ── btmsg RPC ──────────────────────────────────────────────────────────
|
|
|
|
/** Register an agent in btmsg. */
|
|
"btmsg.registerAgent": {
|
|
params: {
|
|
id: string; name: string; role: string;
|
|
groupId: string; tier: number; model?: string;
|
|
};
|
|
response: { ok: boolean };
|
|
};
|
|
/** List agents for a group. */
|
|
"btmsg.getAgents": {
|
|
params: { groupId: string };
|
|
response: {
|
|
agents: Array<{
|
|
id: string; name: string; role: string; groupId: string;
|
|
tier: number; model: string | null; status: string; unreadCount: number;
|
|
}>;
|
|
};
|
|
};
|
|
/** Send a direct message between agents. */
|
|
"btmsg.sendMessage": {
|
|
params: { fromAgent: string; toAgent: string; content: string };
|
|
response: { ok: boolean; messageId?: string; error?: string };
|
|
};
|
|
/** Get message history between two agents. */
|
|
"btmsg.listMessages": {
|
|
params: { agentId: string; otherId: string; limit?: number };
|
|
response: {
|
|
messages: Array<{
|
|
id: string; fromAgent: string; toAgent: string; content: string;
|
|
read: boolean; replyTo: string | null; createdAt: string;
|
|
senderName: string | null; senderRole: string | null;
|
|
}>;
|
|
};
|
|
};
|
|
/** Mark messages as read. */
|
|
"btmsg.markRead": {
|
|
params: { agentId: string; messageIds: string[] };
|
|
response: { ok: boolean };
|
|
};
|
|
/** List channels for a group. */
|
|
"btmsg.listChannels": {
|
|
params: { groupId: string };
|
|
response: {
|
|
channels: Array<{
|
|
id: string; name: string; groupId: string; createdBy: string;
|
|
memberCount: number; createdAt: string;
|
|
}>;
|
|
};
|
|
};
|
|
/** Create a channel. */
|
|
"btmsg.createChannel": {
|
|
params: { name: string; groupId: string; createdBy: string };
|
|
response: { ok: boolean; channelId?: string };
|
|
};
|
|
/** Get channel messages. */
|
|
"btmsg.getChannelMessages": {
|
|
params: { channelId: string; limit?: number };
|
|
response: {
|
|
messages: Array<{
|
|
id: string; channelId: string; fromAgent: string; content: string;
|
|
createdAt: string; senderName: string; senderRole: string;
|
|
}>;
|
|
};
|
|
};
|
|
/** Feature 7: Join a channel. */
|
|
"btmsg.joinChannel": {
|
|
params: { channelId: string; agentId: string };
|
|
response: { ok: boolean; error?: string };
|
|
};
|
|
/** Feature 7: Leave a channel. */
|
|
"btmsg.leaveChannel": {
|
|
params: { channelId: string; agentId: string };
|
|
response: { ok: boolean; error?: string };
|
|
};
|
|
/** Feature 7: Get channel member list. */
|
|
"btmsg.getChannelMembers": {
|
|
params: { channelId: string };
|
|
response: { members: Array<{ agentId: string; name: string; role: string }> };
|
|
};
|
|
/** Send a channel message. */
|
|
"btmsg.sendChannelMessage": {
|
|
params: { channelId: string; fromAgent: string; content: string };
|
|
response: { ok: boolean; messageId?: string };
|
|
};
|
|
/** Record agent heartbeat. */
|
|
"btmsg.heartbeat": {
|
|
params: { agentId: string };
|
|
response: { ok: boolean };
|
|
};
|
|
/** Get dead letter queue entries. */
|
|
"btmsg.getDeadLetters": {
|
|
params: { limit?: number };
|
|
response: {
|
|
letters: Array<{
|
|
id: number; fromAgent: string; toAgent: string;
|
|
content: string; error: string; createdAt: string;
|
|
}>;
|
|
};
|
|
};
|
|
/** Log an audit event. */
|
|
"btmsg.logAudit": {
|
|
params: { agentId: string; eventType: string; detail: string };
|
|
response: { ok: boolean };
|
|
};
|
|
/** Get audit log. */
|
|
"btmsg.getAuditLog": {
|
|
params: { limit?: number };
|
|
response: {
|
|
entries: Array<{
|
|
id: number; agentId: string; eventType: string;
|
|
detail: string; createdAt: string;
|
|
}>;
|
|
};
|
|
};
|
|
|
|
// ── bttask RPC ─────────────────────────────────────────────────────────
|
|
|
|
/** List tasks for a group. */
|
|
"bttask.listTasks": {
|
|
params: { groupId: string };
|
|
response: {
|
|
tasks: Array<{
|
|
id: string; title: string; description: string; status: string;
|
|
priority: string; assignedTo: string | null; createdBy: string;
|
|
groupId: string; parentTaskId: string | null; sortOrder: number;
|
|
createdAt: string; updatedAt: string; version: number;
|
|
}>;
|
|
};
|
|
};
|
|
/** Create a task. */
|
|
"bttask.createTask": {
|
|
params: {
|
|
title: string; description: string; priority: string;
|
|
groupId: string; createdBy: string; assignedTo?: string;
|
|
};
|
|
response: { ok: boolean; taskId?: string; error?: string };
|
|
};
|
|
/** Update task status with optimistic locking. */
|
|
"bttask.updateTaskStatus": {
|
|
params: { taskId: string; status: string; expectedVersion: number };
|
|
response: { ok: boolean; newVersion?: number; error?: string };
|
|
};
|
|
/** Delete a task. */
|
|
"bttask.deleteTask": {
|
|
params: { taskId: string };
|
|
response: { ok: boolean };
|
|
};
|
|
/** Add a comment to a task. */
|
|
"bttask.addComment": {
|
|
params: { taskId: string; agentId: string; content: string };
|
|
response: { ok: boolean; commentId?: string };
|
|
};
|
|
/** List comments for a task. */
|
|
"bttask.listComments": {
|
|
params: { taskId: string };
|
|
response: {
|
|
comments: Array<{
|
|
id: string; taskId: string; agentId: string;
|
|
content: string; createdAt: string;
|
|
}>;
|
|
};
|
|
};
|
|
/** Count tasks in 'review' status. */
|
|
"bttask.reviewQueueCount": {
|
|
params: { groupId: string };
|
|
response: { count: number };
|
|
};
|
|
|
|
// ── Search RPC ──────────────────────────────────────────────────────────
|
|
|
|
/** Full-text search across messages, tasks, and btmsg. Fix #13: typed error for invalid queries. */
|
|
"search.query": {
|
|
params: { query: string; limit?: number };
|
|
response: {
|
|
results: Array<{
|
|
resultType: string;
|
|
id: string;
|
|
title: string;
|
|
snippet: string;
|
|
score: number;
|
|
}>;
|
|
/** Set when query is invalid (e.g. FTS5 syntax error). */
|
|
error?: string;
|
|
};
|
|
};
|
|
/** Index a message for search. */
|
|
"search.indexMessage": {
|
|
params: { sessionId: string; role: string; content: string };
|
|
response: { ok: boolean };
|
|
};
|
|
/** Rebuild the entire search index. */
|
|
"search.rebuild": {
|
|
params: Record<string, never>;
|
|
response: { ok: boolean };
|
|
};
|
|
|
|
// ── Plugin RPC ──────────────────────────────────────────────────────────
|
|
|
|
/** Discover plugins from ~/.config/agor/plugins/. */
|
|
"plugin.discover": {
|
|
params: Record<string, never>;
|
|
response: {
|
|
plugins: Array<{
|
|
id: string;
|
|
name: string;
|
|
version: string;
|
|
description: string;
|
|
main: string;
|
|
permissions: string[];
|
|
}>;
|
|
};
|
|
};
|
|
/** Read a plugin file (path-traversal-safe). */
|
|
"plugin.readFile": {
|
|
params: { pluginId: string; filePath: string };
|
|
response: { ok: boolean; content: string; error?: string };
|
|
};
|
|
|
|
// ── Remote machine (relay) RPC ────────────────────────────────────────────
|
|
|
|
/** Connect to an agor-relay instance. */
|
|
"remote.connect": {
|
|
params: { url: string; token: string; label?: string };
|
|
response: { ok: boolean; machineId?: string; error?: string };
|
|
};
|
|
/** Disconnect from a relay instance (keeps machine in list for reconnect). */
|
|
"remote.disconnect": {
|
|
params: { machineId: string };
|
|
response: { ok: boolean; error?: string };
|
|
};
|
|
/** Remove a machine entirely — disconnects AND deletes from tracking. */
|
|
"remote.remove": {
|
|
params: { machineId: string };
|
|
response: { ok: boolean; error?: string };
|
|
};
|
|
/** List all known remote machines with connection status. */
|
|
"remote.list": {
|
|
params: Record<string, never>;
|
|
response: {
|
|
machines: Array<{
|
|
machineId: string;
|
|
label: string;
|
|
url: string;
|
|
status: "connecting" | "connected" | "disconnected" | "error";
|
|
latencyMs: number | null;
|
|
}>;
|
|
};
|
|
};
|
|
/** Send a command to a connected relay. */
|
|
"remote.send": {
|
|
params: { machineId: string; command: string; payload: Record<string, unknown> };
|
|
response: { ok: boolean; error?: string };
|
|
};
|
|
/** Feature 3: Get stored relay credentials. */
|
|
"remote.getStoredCredentials": {
|
|
params: Record<string, never>;
|
|
response: { credentials: Array<{ url: string; label: string }> };
|
|
};
|
|
/** Feature 3: Store a relay credential (XOR-obfuscated). */
|
|
"remote.storeCredential": {
|
|
params: { url: string; token: string; label?: string };
|
|
response: { ok: boolean };
|
|
};
|
|
/** Feature 3: Delete a stored relay credential. */
|
|
"remote.deleteCredential": {
|
|
params: { url: string };
|
|
response: { ok: boolean };
|
|
};
|
|
/** Get the status of a specific machine. */
|
|
"remote.status": {
|
|
params: { machineId: string };
|
|
response: {
|
|
status: "connecting" | "connected" | "disconnected" | "error";
|
|
latencyMs: number | null;
|
|
error?: string;
|
|
};
|
|
};
|
|
|
|
// ── Telemetry RPC ─────────────────────────────────────────────────────────
|
|
|
|
/** Feature 8: Transport diagnostics stats. */
|
|
"diagnostics.stats": {
|
|
params: Record<string, never>;
|
|
response: {
|
|
ptyConnected: boolean;
|
|
relayConnections: number;
|
|
activeSidecars: number;
|
|
rpcCallCount: number;
|
|
droppedEvents: number;
|
|
};
|
|
};
|
|
|
|
/** Log a telemetry event from the frontend. */
|
|
"telemetry.log": {
|
|
params: {
|
|
level: "info" | "warn" | "error";
|
|
message: string;
|
|
attributes?: Record<string, string | number | boolean>;
|
|
};
|
|
response: { ok: boolean };
|
|
};
|
|
|
|
// ── Updater RPC ──────────────────────────────────────────────────────────
|
|
|
|
/** Check GitHub Releases for a newer version. */
|
|
"updater.check": {
|
|
params: Record<string, never>;
|
|
response: {
|
|
available: boolean;
|
|
version: string;
|
|
downloadUrl: string;
|
|
releaseNotes: string;
|
|
checkedAt: number;
|
|
error?: string;
|
|
};
|
|
};
|
|
/** Get the current app version and last check timestamp. */
|
|
"updater.getVersion": {
|
|
params: Record<string, never>;
|
|
response: { version: string; lastCheck: number };
|
|
};
|
|
};
|
|
|
|
// ── Messages (Bun → WebView, fire-and-forget) ────────────────────────────────
|
|
|
|
export type PtyRPCMessages = {
|
|
/** PTY output chunk. data is base64-encoded raw bytes from the daemon. */
|
|
"pty.output": { sessionId: string; data: string };
|
|
/** PTY session exited. */
|
|
"pty.closed": { sessionId: string; exitCode: number | null };
|
|
|
|
// ── Agent events (Bun → WebView) ─────────────────────────────────────────
|
|
|
|
/** Agent message(s) parsed from sidecar NDJSON. */
|
|
"agent.message": {
|
|
sessionId: string;
|
|
messages: Array<{
|
|
id: string;
|
|
type: string;
|
|
parentId?: string;
|
|
content: unknown;
|
|
timestamp: number;
|
|
}>;
|
|
};
|
|
/** Agent session status change. */
|
|
"agent.status": {
|
|
sessionId: string;
|
|
status: string;
|
|
error?: string;
|
|
};
|
|
/** Agent cost/token update. */
|
|
"agent.cost": {
|
|
sessionId: string;
|
|
costUsd: number;
|
|
inputTokens: number;
|
|
outputTokens: number;
|
|
};
|
|
|
|
// ── Remote machine events (Bun → WebView) ────────────────────────────────
|
|
|
|
/** Remote relay event forwarded from a connected machine. */
|
|
"remote.event": {
|
|
machineId: string;
|
|
eventType: string;
|
|
sessionId?: string;
|
|
payload?: unknown;
|
|
};
|
|
/** Remote machine connection status change. */
|
|
"remote.statusChange": {
|
|
machineId: string;
|
|
status: "connecting" | "connected" | "disconnected" | "error";
|
|
error?: string;
|
|
};
|
|
|
|
// Feature 4: Push-based task/relay updates
|
|
/** Task board data changed (created, moved, deleted). */
|
|
"bttask.changed": { groupId: string };
|
|
/** New btmsg channel or DM message. */
|
|
"btmsg.newMessage": { groupId: string; channelId?: string };
|
|
};
|
|
|
|
// ── Combined schema ───────────────────────────────────────────────────────────
|
|
|
|
export type PtyRPCSchema = {
|
|
requests: PtyRPCRequests;
|
|
messages: PtyRPCMessages;
|
|
};
|