feat(hub): register RPC handlers for hub, agent, and gateway operations

Add 5 new RPC methods (getHubInfo, listAgents, createAgent, deleteAgent,
updateGateway) mirroring the existing Console HTTP API, enabling pure
WebSocket communication from the frontend.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Naiyuan Qing 2026-02-02 14:40:09 +08:00
parent 64878d9fe1
commit 1f22c3e578
10 changed files with 283 additions and 0 deletions

View file

@ -207,6 +207,136 @@ const result = await client.request<GetAgentMessagesResult>(
}
```
### `getHubInfo`
Returns Hub status information. No parameters required.
**Response:**
```ts
interface GetHubInfoResult {
hubId: string; // Hub device ID
url: string; // Current Gateway URL
connectionState: string; // "disconnected" | "connecting" | "connected" | "registered"
agentCount: number; // Number of active agents
}
```
**Example:**
```ts
const info = await client.request<GetHubInfoResult>(hubDeviceId, "getHubInfo");
```
---
### `listAgents`
Lists all active agents. No parameters required.
**Response:**
```ts
interface ListAgentsResult {
agents: { id: string; closed: boolean }[];
}
```
**Example:**
```ts
const result = await client.request<ListAgentsResult>(hubDeviceId, "listAgents");
```
---
### `createAgent`
Creates a new agent or restores an existing one.
**Parameters:**
```ts
interface CreateAgentParams {
id?: string; // optional - reuse existing session ID
}
```
**Response:**
```ts
interface CreateAgentResult {
id: string; // the created/restored agent session ID
}
```
**Example:**
```ts
const result = await client.request<CreateAgentResult>(hubDeviceId, "createAgent");
// or with specific ID:
const result = await client.request<CreateAgentResult>(hubDeviceId, "createAgent", { id: "existing-id" });
```
---
### `deleteAgent`
Closes and removes an agent.
**Parameters:**
```ts
interface DeleteAgentParams {
id: string; // required - agent ID to delete
}
```
**Response:**
```ts
interface DeleteAgentResult {
ok: boolean; // true if agent was found and deleted
}
```
**Example:**
```ts
const result = await client.request<DeleteAgentResult>(hubDeviceId, "deleteAgent", { id: "019abc12-..." });
```
---
### `updateGateway`
Reconnects the Hub to a different Gateway URL.
**Parameters:**
```ts
interface UpdateGatewayParams {
url: string; // required - new Gateway URL
}
```
**Response:**
```ts
interface UpdateGatewayResult {
url: string; // the new URL
connectionState: string; // connection state after reconnect
}
```
**Example:**
```ts
const result = await client.request<UpdateGatewayResult>(hubDeviceId, "updateGateway", { url: "http://localhost:4000" });
```
---
## Adding New RPC Methods
1. Create a handler file in `src/hub/rpc/handlers/`:

View file

@ -16,6 +16,14 @@ export {
isResponseError,
type GetAgentMessagesParams,
type GetAgentMessagesResult,
type GetHubInfoResult,
type ListAgentsResult,
type CreateAgentParams,
type CreateAgentResult,
type DeleteAgentParams,
type DeleteAgentResult,
type UpdateGatewayParams,
type UpdateGatewayResult,
} from "./rpc.js";
export { StreamAction, type StreamPayload } from "./stream.js";

View file

@ -72,3 +72,47 @@ export interface GetAgentMessagesResult {
offset: number;
limit: number;
}
/** getHubInfo - no params needed */
export interface GetHubInfoResult {
hubId: string;
url: string;
connectionState: string;
agentCount: number;
}
/** listAgents - no params needed */
export interface ListAgentsResult {
agents: { id: string; closed: boolean }[];
}
/** createAgent - request params */
export interface CreateAgentParams {
id?: string;
}
/** createAgent - response payload */
export interface CreateAgentResult {
id: string;
}
/** deleteAgent - request params */
export interface DeleteAgentParams {
id: string;
}
/** deleteAgent - response payload */
export interface DeleteAgentResult {
ok: boolean;
}
/** updateGateway - request params */
export interface UpdateGatewayParams {
url: string;
}
/** updateGateway - response payload */
export interface UpdateGatewayResult {
url: string;
connectionState: string;
}

View file

@ -12,6 +12,11 @@ import { getHubId } from "./hub-identity.js";
import { loadAgentRecords, addAgentRecord, removeAgentRecord } from "./agent-store.js";
import { RpcDispatcher, RpcError } from "./rpc/dispatcher.js";
import { createGetAgentMessagesHandler } from "./rpc/handlers/get-agent-messages.js";
import { createGetHubInfoHandler } from "./rpc/handlers/get-hub-info.js";
import { createListAgentsHandler } from "./rpc/handlers/list-agents.js";
import { createCreateAgentHandler } from "./rpc/handlers/create-agent.js";
import { createDeleteAgentHandler } from "./rpc/handlers/delete-agent.js";
import { createUpdateGatewayHandler } from "./rpc/handlers/update-gateway.js";
export class Hub {
private readonly agents = new Map<string, AsyncAgent>();
@ -34,6 +39,11 @@ export class Hub {
this.rpc = new RpcDispatcher();
this.rpc.register("getAgentMessages", createGetAgentMessagesHandler());
this.rpc.register("getHubInfo", createGetHubInfoHandler(this));
this.rpc.register("listAgents", createListAgentsHandler(this));
this.rpc.register("createAgent", createCreateAgentHandler(this));
this.rpc.register("deleteAgent", createDeleteAgentHandler(this));
this.rpc.register("updateGateway", createUpdateGatewayHandler(this));
this.client = this.createClient(this.url);
this.client.connect();

View file

@ -0,0 +1,13 @@
import type { RpcHandler } from "../dispatcher.js";
interface HubLike {
createAgent(id?: string): { sessionId: string };
}
export function createCreateAgentHandler(hub: HubLike): RpcHandler {
return (params: unknown) => {
const { id } = (params ?? {}) as { id?: string };
const agent = hub.createAgent(id);
return { id: agent.sessionId };
};
}

View file

@ -0,0 +1,19 @@
import { RpcError, type RpcHandler } from "../dispatcher.js";
interface HubLike {
closeAgent(id: string): boolean;
}
export function createDeleteAgentHandler(hub: HubLike): RpcHandler {
return (params: unknown) => {
if (!params || typeof params !== "object") {
throw new RpcError("INVALID_PARAMS", "params must be an object");
}
const { id } = params as { id?: string };
if (!id) {
throw new RpcError("INVALID_PARAMS", "Missing required param: id");
}
const ok = hub.closeAgent(id);
return { ok };
};
}

View file

@ -0,0 +1,17 @@
import type { RpcHandler } from "../dispatcher.js";
interface HubLike {
hubId: string;
url: string;
connectionState: string;
listAgents(): string[];
}
export function createGetHubInfoHandler(hub: HubLike): RpcHandler {
return () => ({
hubId: hub.hubId,
url: hub.url,
connectionState: hub.connectionState,
agentCount: hub.listAgents().length,
});
}

View file

@ -0,0 +1,16 @@
import type { RpcHandler } from "../dispatcher.js";
interface HubLike {
listAgents(): string[];
getAgent(id: string): { closed: boolean } | undefined;
}
export function createListAgentsHandler(hub: HubLike): RpcHandler {
return () => {
const agents = hub.listAgents().map((id) => {
const agent = hub.getAgent(id);
return { id, closed: agent?.closed ?? true };
});
return { agents };
};
}

View file

@ -0,0 +1,21 @@
import { RpcError, type RpcHandler } from "../dispatcher.js";
interface HubLike {
url: string;
connectionState: string;
reconnect(url: string): void;
}
export function createUpdateGatewayHandler(hub: HubLike): RpcHandler {
return (params: unknown) => {
if (!params || typeof params !== "object") {
throw new RpcError("INVALID_PARAMS", "params must be an object");
}
const { url } = params as { url?: string };
if (!url) {
throw new RpcError("INVALID_PARAMS", "Missing required param: url");
}
hub.reconnect(url);
return { url: hub.url, connectionState: hub.connectionState };
};
}

View file

@ -1,2 +1,7 @@
export { RpcDispatcher, RpcError, type RpcHandler } from "./dispatcher.js";
export { createGetAgentMessagesHandler } from "./handlers/get-agent-messages.js";
export { createGetHubInfoHandler } from "./handlers/get-hub-info.js";
export { createListAgentsHandler } from "./handlers/list-agents.js";
export { createCreateAgentHandler } from "./handlers/create-agent.js";
export { createDeleteAgentHandler } from "./handlers/delete-agent.js";
export { createUpdateGatewayHandler } from "./handlers/update-gateway.js";