From fcd64933bdb93c16531b5b29d1134f40581b03e9 Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 8 Mar 2026 14:45:02 +0800 Subject: [PATCH 1/2] refactor(platform): inject channel server/client via Platform DI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename WebSocketFactory → ChannelClientFactory at the source type level. Server now reads channelServer from Platform instead of importing WebSocketServer directly, enabling non-Node platforms (e.g. Cloudflare DO) to provide their own ChannelServer implementation. Co-Authored-By: Claude Opus 4.6 --- packages/agentx/src/RemoteClient.ts | 2 +- packages/agentx/src/index.ts | 8 ++++---- packages/core/src/network/RpcClient.ts | 10 +++++----- packages/core/src/network/index.ts | 2 +- packages/core/src/platform/types.ts | 16 +++++++++++++--- packages/node-platform/src/index.ts | 12 ++++++++++-- .../src/network/WebSocketFactory.ts | 8 ++++---- packages/server/src/Server.ts | 19 ++++++++----------- 8 files changed, 46 insertions(+), 31 deletions(-) diff --git a/packages/agentx/src/RemoteClient.ts b/packages/agentx/src/RemoteClient.ts index 81eeb732..5adcecb9 100644 --- a/packages/agentx/src/RemoteClient.ts +++ b/packages/agentx/src/RemoteClient.ts @@ -47,7 +47,7 @@ export class RemoteClient implements AgentX { // Create RPC client (WebSocket factory from platform if available) this.rpcClient = new RpcClient({ url: config.serverUrl!, - createWebSocket: config.customPlatform?.webSocketFactory, + createWebSocket: config.customPlatform?.channelClient, timeout: config.timeout ?? 30000, autoReconnect: config.autoReconnect ?? true, headers: config.headers as Record | undefined, diff --git a/packages/agentx/src/index.ts b/packages/agentx/src/index.ts index c7443d8a..39b4c6b3 100644 --- a/packages/agentx/src/index.ts +++ b/packages/agentx/src/index.ts @@ -69,11 +69,11 @@ export async function createAgentX(config: AgentXConfig): Promise { /** * Resolve platform for remote mode * - * In Node.js: auto-import node-platform to get webSocketFactory + * In Node.js: auto-import node-platform to get channelClient * In browser: no platform needed (native WebSocket is the default) */ async function resolvePlatformForRemote(config: AgentXConfig): Promise { - if (config.customPlatform?.webSocketFactory) { + if (config.customPlatform?.channelClient) { return config; } @@ -82,14 +82,14 @@ async function resolvePlatformForRemote(config: AgentXConfig): Promise WebSocket; +export type ChannelClientFactory = (url: string) => WebSocket; /** * RpcClient configuration @@ -64,7 +64,7 @@ export interface RpcClientConfig { * Factory for creating WebSocket instances. * If not provided, falls back to the global WebSocket constructor. */ - createWebSocket?: WebSocketFactory; + createWebSocket?: ChannelClientFactory; /** * Request timeout in milliseconds (default: 30000) diff --git a/packages/core/src/network/index.ts b/packages/core/src/network/index.ts index 8f139b71..a0584884 100644 --- a/packages/core/src/network/index.ts +++ b/packages/core/src/network/index.ts @@ -55,7 +55,7 @@ export { wrapMessage, } from "./protocol"; // RPC Client -export type { RpcClientConfig, RpcClientState, WebSocketFactory } from "./RpcClient"; +export type { ChannelClientFactory, RpcClientConfig, RpcClientState } from "./RpcClient"; export { RpcClient } from "./RpcClient"; // Types export type { diff --git a/packages/core/src/platform/types.ts b/packages/core/src/platform/types.ts index f6e1dd69..60d80e51 100644 --- a/packages/core/src/platform/types.ts +++ b/packages/core/src/platform/types.ts @@ -23,7 +23,8 @@ import type { BashProvider } from "../bash/types"; import type { ContainerRepository } from "../container/types"; import type { EventBus } from "../event/types"; import type { ImageRepository } from "../image/types"; -import type { WebSocketFactory } from "../network/RpcClient"; +import type { ChannelClientFactory } from "../network/RpcClient"; +import type { ChannelServer } from "../network/types"; import type { SessionRepository } from "../session/types"; // ============================================================================ @@ -75,10 +76,19 @@ export interface AgentXPlatform { readonly bashProvider?: BashProvider; /** - * WebSocket factory for creating client connections + * Channel server for accepting client connections (server-side) + * + * Optional — only needed for server scenarios. + * Node.js platform provides ws-based implementation. + * Cloudflare platform provides DO Hibernation API implementation. + */ + readonly channelServer?: ChannelServer; + + /** + * Channel client factory for creating connections (client-side) * * Optional — browser uses native WebSocket by default. * Node.js platform provides ws-based implementation. */ - readonly webSocketFactory?: WebSocketFactory; + readonly channelClient?: ChannelClientFactory; } diff --git a/packages/node-platform/src/index.ts b/packages/node-platform/src/index.ts index d2dc7654..67212c56 100644 --- a/packages/node-platform/src/index.ts +++ b/packages/node-platform/src/index.ts @@ -110,16 +110,24 @@ export async function createNodePlatform( // Create event bus const eventBus = new EventBusImpl(); - // Create WebSocket factory (uses ws library for Node.js) + // Create channel client factory (uses ws library for Node.js) const { createNodeWebSocket } = await import("./network/WebSocketFactory"); + // Create channel server (uses ws library for Node.js) + const { WebSocketServer } = await import("./network"); + const channelServer = new WebSocketServer({ + heartbeat: true, + heartbeatInterval: 30000, + }); + return { containerRepository: persistence.containers, imageRepository: persistence.images, sessionRepository: persistence.sessions, eventBus, bashProvider, - webSocketFactory: createNodeWebSocket, + channelServer, + channelClient: createNodeWebSocket, }; } diff --git a/packages/node-platform/src/network/WebSocketFactory.ts b/packages/node-platform/src/network/WebSocketFactory.ts index e672991b..d8b9498b 100644 --- a/packages/node-platform/src/network/WebSocketFactory.ts +++ b/packages/node-platform/src/network/WebSocketFactory.ts @@ -1,16 +1,16 @@ /** - * Node.js WebSocket factory using the ws library + * Node.js channel client factory using the ws library * - * Provides WebSocketFactory implementation for @agentxjs/core RpcClient. + * Provides ChannelClientFactory implementation for @agentxjs/core RpcClient. * Browser environments use native WebSocket (the default in RpcClient). */ -import type { WebSocketFactory } from "@agentxjs/core/network"; +import type { ChannelClientFactory } from "@agentxjs/core/network"; /** * Create a WebSocket instance using the ws library (Node.js) */ -export const createNodeWebSocket: WebSocketFactory = (url: string): WebSocket => { +export const createNodeWebSocket: ChannelClientFactory = (url: string): WebSocket => { // eslint-disable-next-line @typescript-eslint/no-require-imports const { default: WS } = require("ws"); return new WS(url) as unknown as WebSocket; diff --git a/packages/server/src/Server.ts b/packages/server/src/Server.ts index fec45a21..8a05d7ef 100644 --- a/packages/server/src/Server.ts +++ b/packages/server/src/Server.ts @@ -26,11 +26,7 @@ import { } from "@agentxjs/core/network"; import type { AgentXPlatform } from "@agentxjs/core/runtime"; import { createAgentXRuntime } from "@agentxjs/core/runtime"; -import { - type DeferredPlatformConfig, - isDeferredPlatform, - WebSocketServer, -} from "@agentxjs/node-platform"; +import { type DeferredPlatformConfig, isDeferredPlatform } from "@agentxjs/node-platform"; import { createLogger } from "commonxjs/logger"; import { CommandHandler } from "./CommandHandler"; import type { AgentXServer } from "./types"; @@ -51,6 +47,8 @@ interface ConnectionState { export interface ServerConfig { /** * AgentX Platform (can be AgentXPlatform or DeferredPlatformConfig) + * + * Must provide `channelServer` for accepting WebSocket connections. */ platform: AgentXPlatform | DeferredPlatformConfig; @@ -99,12 +97,11 @@ export async function createServer(config: ServerConfig): Promise // Create runtime from platform + driver const runtime = createAgentXRuntime(platform, config.createDriver); - // Create WebSocket server - const wsServer = new WebSocketServer({ - heartbeat: true, - heartbeatInterval: 30000, - debug: config.debug, - }); + // Get channel server from platform + const wsServer = platform.channelServer; + if (!wsServer) { + throw new Error("Platform must provide channelServer for server mode"); + } // Create command handler (no longer needs eventBus) const commandHandler = new CommandHandler(runtime); From 6d81e79d291dd04ccf2ff968e755663df0b58682 Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 8 Mar 2026 14:45:22 +0800 Subject: [PATCH 2/2] chore: add changeset for channel platform injection Co-Authored-By: Claude Opus 4.6 --- .changeset/channel-platform-injection.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/channel-platform-injection.md diff --git a/.changeset/channel-platform-injection.md b/.changeset/channel-platform-injection.md new file mode 100644 index 00000000..90ded821 --- /dev/null +++ b/.changeset/channel-platform-injection.md @@ -0,0 +1,8 @@ +--- +"@agentxjs/core": minor +"@agentxjs/server": minor +"@agentxjs/node-platform": minor +"agentxjs": minor +--- + +Inject channel server/client via Platform DI. Rename WebSocketFactory → ChannelClientFactory, webSocketFactory → channelClient. Server reads channelServer from Platform instead of importing WebSocketServer directly, enabling non-Node platforms (e.g. Cloudflare DO) to provide their own ChannelServer implementation.