@gopherhole/sdk
Advanced tools
+186
| /** | ||
| * GopherHole Transport Layer | ||
| * | ||
| * Defines the Transport interface and implementations for HTTP, WebSocket, and Auto modes. | ||
| * The transport handles sending JSON-RPC requests to the hub — connection lifecycle | ||
| * and push events are managed separately by the GopherHole class. | ||
| */ | ||
| export type TransportMode = 'http' | 'ws' | 'auto'; | ||
| /** | ||
| * Transport interface for sending JSON-RPC requests to the hub. | ||
| */ | ||
| export interface Transport { | ||
| /** Send a JSON-RPC request and return the parsed result */ | ||
| request<T>(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<T>; | ||
| /** Whether this transport is currently able to send requests */ | ||
| readonly isOpen: boolean; | ||
| } | ||
| /** | ||
| * HTTP Transport — sends JSON-RPC requests via HTTP POST to /a2a. | ||
| * Always available, no connection required. | ||
| */ | ||
| export class HttpTransport implements Transport { | ||
| constructor( | ||
| private apiUrl: string, | ||
| private apiKey: string, | ||
| private defaultTimeout: number, | ||
| ) {} | ||
| get isOpen(): boolean { | ||
| return true; // HTTP is always available | ||
| } | ||
| async request<T>(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<T> { | ||
| const timeout = timeoutMs ?? this.defaultTimeout; | ||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), timeout); | ||
| try { | ||
| const response = await fetch(`${this.apiUrl}/a2a`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${this.apiKey}`, | ||
| }, | ||
| body: JSON.stringify({ | ||
| jsonrpc: '2.0', | ||
| method, | ||
| params, | ||
| id: Date.now(), | ||
| }), | ||
| signal: controller.signal, | ||
| }); | ||
| const data = await response.json(); | ||
| if (data.error) { | ||
| throw new Error(data.error.message || 'RPC error'); | ||
| } | ||
| return data.result as T; | ||
| } catch (err) { | ||
| if (err instanceof Error && err.name === 'AbortError') { | ||
| throw new Error(`Request timeout after ${timeout}ms`); | ||
| } | ||
| throw err; | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * WebSocket Transport — sends JSON-RPC requests as frames over an existing WebSocket connection. | ||
| * Requires an open WebSocket connection. Falls back to HTTP if wsFallback is enabled. | ||
| */ | ||
| export class WsTransport implements Transport { | ||
| private pendingRequests = new Map<number, { | ||
| resolve: (value: unknown) => void; | ||
| reject: (reason: Error) => void; | ||
| timer: ReturnType<typeof setTimeout>; | ||
| }>(); | ||
| private requestId = 0; | ||
| private httpFallback: HttpTransport | null; | ||
| constructor( | ||
| private getWs: () => WebSocket | null, | ||
| private defaultTimeout: number, | ||
| wsFallback: boolean, | ||
| apiUrl: string, | ||
| apiKey: string, | ||
| ) { | ||
| this.httpFallback = wsFallback ? new HttpTransport(apiUrl, apiKey, defaultTimeout) : null; | ||
| } | ||
| get isOpen(): boolean { | ||
| const ws = this.getWs(); | ||
| return ws?.readyState === 1; | ||
| } | ||
| /** | ||
| * Handle an incoming WebSocket message. Called by the GopherHole class when a | ||
| * message arrives on the WebSocket. Returns true if the message was a JSON-RPC | ||
| * response that was consumed, false otherwise. | ||
| */ | ||
| handleMessage(data: Record<string, unknown>): boolean { | ||
| if (data.jsonrpc === '2.0' && data.id != null && (data.result !== undefined || data.error !== undefined)) { | ||
| const pending = this.pendingRequests.get(data.id as number); | ||
| if (pending) { | ||
| this.pendingRequests.delete(data.id as number); | ||
| clearTimeout(pending.timer); | ||
| if (data.error) { | ||
| pending.reject(new Error((data.error as { message: string }).message || 'RPC error')); | ||
| } else { | ||
| pending.resolve(data.result); | ||
| } | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| async request<T>(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<T> { | ||
| const ws = this.getWs(); | ||
| if (!ws || ws.readyState !== 1) { | ||
| if (this.httpFallback) { | ||
| return this.httpFallback.request<T>(method, params, timeoutMs); | ||
| } | ||
| throw new Error('WebSocket not connected. Call connect() first or enable wsFallback.'); | ||
| } | ||
| const timeout = timeoutMs ?? this.defaultTimeout; | ||
| const id = ++this.requestId; | ||
| return new Promise<T>((resolve, reject) => { | ||
| const timer = setTimeout(() => { | ||
| this.pendingRequests.delete(id); | ||
| reject(new Error(`Request timeout after ${timeout}ms`)); | ||
| }, timeout); | ||
| this.pendingRequests.set(id, { | ||
| resolve: resolve as (value: unknown) => void, | ||
| reject, | ||
| timer, | ||
| }); | ||
| ws.send(JSON.stringify({ | ||
| jsonrpc: '2.0', | ||
| id, | ||
| method, | ||
| params, | ||
| })); | ||
| }); | ||
| } | ||
| /** Clean up pending requests on disconnect */ | ||
| cleanup(): void { | ||
| for (const [id, pending] of this.pendingRequests) { | ||
| clearTimeout(pending.timer); | ||
| pending.reject(new Error('WebSocket disconnected')); | ||
| } | ||
| this.pendingRequests.clear(); | ||
| } | ||
| } | ||
| /** | ||
| * Auto Transport — uses HTTP for RPC requests (same as current SDK behaviour). | ||
| * This is the default transport that preserves backwards compatibility. | ||
| */ | ||
| export class AutoTransport implements Transport { | ||
| private http: HttpTransport; | ||
| constructor(apiUrl: string, apiKey: string, defaultTimeout: number) { | ||
| this.http = new HttpTransport(apiUrl, apiKey, defaultTimeout); | ||
| } | ||
| get isOpen(): boolean { | ||
| return true; // HTTP is always available | ||
| } | ||
| async request<T>(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<T> { | ||
| return this.http.request<T>(method, params, timeoutMs); | ||
| } | ||
| } |
+79
-7
@@ -267,2 +267,64 @@ import { EventEmitter } from 'eventemitter3'; | ||
| /** | ||
| * GopherHole Transport Layer | ||
| * | ||
| * Defines the Transport interface and implementations for HTTP, WebSocket, and Auto modes. | ||
| * The transport handles sending JSON-RPC requests to the hub — connection lifecycle | ||
| * and push events are managed separately by the GopherHole class. | ||
| */ | ||
| type TransportMode = 'http' | 'ws' | 'auto'; | ||
| /** | ||
| * Transport interface for sending JSON-RPC requests to the hub. | ||
| */ | ||
| interface Transport { | ||
| /** Send a JSON-RPC request and return the parsed result */ | ||
| request<T>(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<T>; | ||
| /** Whether this transport is currently able to send requests */ | ||
| readonly isOpen: boolean; | ||
| } | ||
| /** | ||
| * HTTP Transport — sends JSON-RPC requests via HTTP POST to /a2a. | ||
| * Always available, no connection required. | ||
| */ | ||
| declare class HttpTransport implements Transport { | ||
| private apiUrl; | ||
| private apiKey; | ||
| private defaultTimeout; | ||
| constructor(apiUrl: string, apiKey: string, defaultTimeout: number); | ||
| get isOpen(): boolean; | ||
| request<T>(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<T>; | ||
| } | ||
| /** | ||
| * WebSocket Transport — sends JSON-RPC requests as frames over an existing WebSocket connection. | ||
| * Requires an open WebSocket connection. Falls back to HTTP if wsFallback is enabled. | ||
| */ | ||
| declare class WsTransport implements Transport { | ||
| private getWs; | ||
| private defaultTimeout; | ||
| private pendingRequests; | ||
| private requestId; | ||
| private httpFallback; | ||
| constructor(getWs: () => WebSocket | null, defaultTimeout: number, wsFallback: boolean, apiUrl: string, apiKey: string); | ||
| get isOpen(): boolean; | ||
| /** | ||
| * Handle an incoming WebSocket message. Called by the GopherHole class when a | ||
| * message arrives on the WebSocket. Returns true if the message was a JSON-RPC | ||
| * response that was consumed, false otherwise. | ||
| */ | ||
| handleMessage(data: Record<string, unknown>): boolean; | ||
| request<T>(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<T>; | ||
| /** Clean up pending requests on disconnect */ | ||
| cleanup(): void; | ||
| } | ||
| /** | ||
| * Auto Transport — uses HTTP for RPC requests (same as current SDK behaviour). | ||
| * This is the default transport that preserves backwards compatibility. | ||
| */ | ||
| declare class AutoTransport implements Transport { | ||
| private http; | ||
| constructor(apiUrl: string, apiKey: string, defaultTimeout: number); | ||
| get isOpen(): boolean; | ||
| request<T>(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<T>; | ||
| } | ||
| /** | ||
| * GopherHole A2A HTTP Client | ||
@@ -670,2 +732,6 @@ * Implements A2A JSON-RPC over HTTP with SSE streaming support | ||
| hubUrl?: string; | ||
| /** Transport mode: 'http', 'ws', or 'auto' (default: 'auto') */ | ||
| transport?: TransportMode; | ||
| /** Fall back to HTTP if WebSocket disconnects (only applies to 'ws' mode, default: true) */ | ||
| wsFallback?: boolean; | ||
| /** Agent card to register on connect */ | ||
@@ -805,2 +871,5 @@ agentCard?: AgentCardConfig; | ||
| private apiUrl; | ||
| private transportMode; | ||
| private transport; | ||
| private wsTransport; | ||
| private ws; | ||
@@ -828,3 +897,6 @@ private autoReconnect; | ||
| /** | ||
| * Connect to the GopherHole hub via WebSocket | ||
| * Connect to the GopherHole hub via WebSocket. | ||
| * For transport: 'http', this is a no-op. | ||
| * For transport: 'ws', this is required before sending any messages. | ||
| * For transport: 'auto', this is optional and enables push events. | ||
| */ | ||
@@ -884,5 +956,4 @@ connect(): Promise<void>; | ||
| /** | ||
| * Respond to an incoming task via WebSocket (completes the task) | ||
| * Use this when you receive a 'message' event and want to send back a response | ||
| * that completes the original task. | ||
| * Respond to an incoming task (completes the task). | ||
| * Uses WebSocket if connected, otherwise falls back to HTTP via task/respond RPC. | ||
| */ | ||
@@ -1018,3 +1089,3 @@ respond(taskId: string, text: string, options?: { | ||
| /** | ||
| * Make a JSON-RPC call to the A2A endpoint | ||
| * Make a JSON-RPC call via the configured transport | ||
| */ | ||
@@ -1047,3 +1118,4 @@ private rpc; | ||
| /** | ||
| * Discover public agents with comprehensive search | ||
| * Discover public agents with comprehensive search. | ||
| * Uses JSON-RPC via transport for 'ws' mode, HTTP REST for 'http'/'auto'. | ||
| */ | ||
@@ -1243,2 +1315,2 @@ discover(options?: DiscoverOptions): Promise<DiscoverResult>; | ||
| export { type A2AArtifact, A2AClient, type A2AClientOptions, type A2AMessage, type A2ATask, type A2ATaskStatus, type AgentArtifact, type AgentAuthentication, type AgentCapabilities, type AgentCard, type AgentCardConfig, type AgentCategory, type AgentInfoResult, type AgentMessagePart, type AgentReview, type AgentSkill, type AgentSkillConfig, type AgentTaskResult, type AgentTaskStatus, type Artifact, type AvailableAgent, type ContentMode, type DataContent, type DataPart, type DiscoverNearbyOptions, type DiscoverNearbyResult, type DiscoverOptions, type DiscoverResult, type DiscoveredAgent, type FileContent, type FilePart, GopherHole, GopherHoleAgent, type GopherHoleAgentOptions, type GopherHoleOptions, type Artifact$1 as HttpArtifact, type Task$1 as HttpTask, type TaskStatus$1 as HttpTaskStatus, type IncomingMessage, type InputMode, type JsonRpcError, JsonRpcErrorCodes, type JsonRpcRequest$1 as JsonRpcRequest, type JsonRpcResponse$1 as JsonRpcResponse, MEMORY_TYPES, type MemoryType, type MemoryTypeInfo, type Message, type MessageContext, type MessageHandler, type MessageMetadata, type MessagePart, type MessagePayload, type MessageSendConfiguration, type NearbyAgent, type OutputMode, type Part, type PublicAgent, type PushNotificationConfig, type RatingResult, SYSTEM_SENDER_ID, type SecretInfo, type SendAndWaitOptions, type SendMessageConfig, type SendOptions, type StreamResponse, type SystemMessage, type Task, type TaskArtifactUpdateEvent, type TaskEvent, type TaskListConfiguration, type TaskPushNotificationConfig, type TaskQueryConfiguration, type AgentTaskResult as TaskResult, type TaskState, type TaskStatus, type TaskStatusUpdateEvent, TaskStream, type TextPart, type Workspace, type WorkspaceForgetParams, type WorkspaceListMemoriesParams, type WorkspaceMember, type WorkspaceMemory, type WorkspaceQueryParams, type WorkspaceStoreParams, type WorkspaceUpdateParams, GopherHole as default, getTaskResponseText }; | ||
| export { type A2AArtifact, A2AClient, type A2AClientOptions, type A2AMessage, type A2ATask, type A2ATaskStatus, type AgentArtifact, type AgentAuthentication, type AgentCapabilities, type AgentCard, type AgentCardConfig, type AgentCategory, type AgentInfoResult, type AgentMessagePart, type AgentReview, type AgentSkill, type AgentSkillConfig, type AgentTaskResult, type AgentTaskStatus, type Artifact, AutoTransport, type AvailableAgent, type ContentMode, type DataContent, type DataPart, type DiscoverNearbyOptions, type DiscoverNearbyResult, type DiscoverOptions, type DiscoverResult, type DiscoveredAgent, type FileContent, type FilePart, GopherHole, GopherHoleAgent, type GopherHoleAgentOptions, type GopherHoleOptions, type Artifact$1 as HttpArtifact, type Task$1 as HttpTask, type TaskStatus$1 as HttpTaskStatus, HttpTransport, type IncomingMessage, type InputMode, type JsonRpcError, JsonRpcErrorCodes, type JsonRpcRequest$1 as JsonRpcRequest, type JsonRpcResponse$1 as JsonRpcResponse, MEMORY_TYPES, type MemoryType, type MemoryTypeInfo, type Message, type MessageContext, type MessageHandler, type MessageMetadata, type MessagePart, type MessagePayload, type MessageSendConfiguration, type NearbyAgent, type OutputMode, type Part, type PublicAgent, type PushNotificationConfig, type RatingResult, SYSTEM_SENDER_ID, type SecretInfo, type SendAndWaitOptions, type SendMessageConfig, type SendOptions, type StreamResponse, type SystemMessage, type Task, type TaskArtifactUpdateEvent, type TaskEvent, type TaskListConfiguration, type TaskPushNotificationConfig, type TaskQueryConfiguration, type AgentTaskResult as TaskResult, type TaskState, type TaskStatus, type TaskStatusUpdateEvent, TaskStream, type TextPart, type Transport, type TransportMode, type Workspace, type WorkspaceForgetParams, type WorkspaceListMemoriesParams, type WorkspaceMember, type WorkspaceMemory, type WorkspaceQueryParams, type WorkspaceStoreParams, type WorkspaceUpdateParams, WsTransport, GopherHole as default, getTaskResponseText }; |
+79
-7
@@ -267,2 +267,64 @@ import { EventEmitter } from 'eventemitter3'; | ||
| /** | ||
| * GopherHole Transport Layer | ||
| * | ||
| * Defines the Transport interface and implementations for HTTP, WebSocket, and Auto modes. | ||
| * The transport handles sending JSON-RPC requests to the hub — connection lifecycle | ||
| * and push events are managed separately by the GopherHole class. | ||
| */ | ||
| type TransportMode = 'http' | 'ws' | 'auto'; | ||
| /** | ||
| * Transport interface for sending JSON-RPC requests to the hub. | ||
| */ | ||
| interface Transport { | ||
| /** Send a JSON-RPC request and return the parsed result */ | ||
| request<T>(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<T>; | ||
| /** Whether this transport is currently able to send requests */ | ||
| readonly isOpen: boolean; | ||
| } | ||
| /** | ||
| * HTTP Transport — sends JSON-RPC requests via HTTP POST to /a2a. | ||
| * Always available, no connection required. | ||
| */ | ||
| declare class HttpTransport implements Transport { | ||
| private apiUrl; | ||
| private apiKey; | ||
| private defaultTimeout; | ||
| constructor(apiUrl: string, apiKey: string, defaultTimeout: number); | ||
| get isOpen(): boolean; | ||
| request<T>(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<T>; | ||
| } | ||
| /** | ||
| * WebSocket Transport — sends JSON-RPC requests as frames over an existing WebSocket connection. | ||
| * Requires an open WebSocket connection. Falls back to HTTP if wsFallback is enabled. | ||
| */ | ||
| declare class WsTransport implements Transport { | ||
| private getWs; | ||
| private defaultTimeout; | ||
| private pendingRequests; | ||
| private requestId; | ||
| private httpFallback; | ||
| constructor(getWs: () => WebSocket | null, defaultTimeout: number, wsFallback: boolean, apiUrl: string, apiKey: string); | ||
| get isOpen(): boolean; | ||
| /** | ||
| * Handle an incoming WebSocket message. Called by the GopherHole class when a | ||
| * message arrives on the WebSocket. Returns true if the message was a JSON-RPC | ||
| * response that was consumed, false otherwise. | ||
| */ | ||
| handleMessage(data: Record<string, unknown>): boolean; | ||
| request<T>(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<T>; | ||
| /** Clean up pending requests on disconnect */ | ||
| cleanup(): void; | ||
| } | ||
| /** | ||
| * Auto Transport — uses HTTP for RPC requests (same as current SDK behaviour). | ||
| * This is the default transport that preserves backwards compatibility. | ||
| */ | ||
| declare class AutoTransport implements Transport { | ||
| private http; | ||
| constructor(apiUrl: string, apiKey: string, defaultTimeout: number); | ||
| get isOpen(): boolean; | ||
| request<T>(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<T>; | ||
| } | ||
| /** | ||
| * GopherHole A2A HTTP Client | ||
@@ -670,2 +732,6 @@ * Implements A2A JSON-RPC over HTTP with SSE streaming support | ||
| hubUrl?: string; | ||
| /** Transport mode: 'http', 'ws', or 'auto' (default: 'auto') */ | ||
| transport?: TransportMode; | ||
| /** Fall back to HTTP if WebSocket disconnects (only applies to 'ws' mode, default: true) */ | ||
| wsFallback?: boolean; | ||
| /** Agent card to register on connect */ | ||
@@ -805,2 +871,5 @@ agentCard?: AgentCardConfig; | ||
| private apiUrl; | ||
| private transportMode; | ||
| private transport; | ||
| private wsTransport; | ||
| private ws; | ||
@@ -828,3 +897,6 @@ private autoReconnect; | ||
| /** | ||
| * Connect to the GopherHole hub via WebSocket | ||
| * Connect to the GopherHole hub via WebSocket. | ||
| * For transport: 'http', this is a no-op. | ||
| * For transport: 'ws', this is required before sending any messages. | ||
| * For transport: 'auto', this is optional and enables push events. | ||
| */ | ||
@@ -884,5 +956,4 @@ connect(): Promise<void>; | ||
| /** | ||
| * Respond to an incoming task via WebSocket (completes the task) | ||
| * Use this when you receive a 'message' event and want to send back a response | ||
| * that completes the original task. | ||
| * Respond to an incoming task (completes the task). | ||
| * Uses WebSocket if connected, otherwise falls back to HTTP via task/respond RPC. | ||
| */ | ||
@@ -1018,3 +1089,3 @@ respond(taskId: string, text: string, options?: { | ||
| /** | ||
| * Make a JSON-RPC call to the A2A endpoint | ||
| * Make a JSON-RPC call via the configured transport | ||
| */ | ||
@@ -1047,3 +1118,4 @@ private rpc; | ||
| /** | ||
| * Discover public agents with comprehensive search | ||
| * Discover public agents with comprehensive search. | ||
| * Uses JSON-RPC via transport for 'ws' mode, HTTP REST for 'http'/'auto'. | ||
| */ | ||
@@ -1243,2 +1315,2 @@ discover(options?: DiscoverOptions): Promise<DiscoverResult>; | ||
| export { type A2AArtifact, A2AClient, type A2AClientOptions, type A2AMessage, type A2ATask, type A2ATaskStatus, type AgentArtifact, type AgentAuthentication, type AgentCapabilities, type AgentCard, type AgentCardConfig, type AgentCategory, type AgentInfoResult, type AgentMessagePart, type AgentReview, type AgentSkill, type AgentSkillConfig, type AgentTaskResult, type AgentTaskStatus, type Artifact, type AvailableAgent, type ContentMode, type DataContent, type DataPart, type DiscoverNearbyOptions, type DiscoverNearbyResult, type DiscoverOptions, type DiscoverResult, type DiscoveredAgent, type FileContent, type FilePart, GopherHole, GopherHoleAgent, type GopherHoleAgentOptions, type GopherHoleOptions, type Artifact$1 as HttpArtifact, type Task$1 as HttpTask, type TaskStatus$1 as HttpTaskStatus, type IncomingMessage, type InputMode, type JsonRpcError, JsonRpcErrorCodes, type JsonRpcRequest$1 as JsonRpcRequest, type JsonRpcResponse$1 as JsonRpcResponse, MEMORY_TYPES, type MemoryType, type MemoryTypeInfo, type Message, type MessageContext, type MessageHandler, type MessageMetadata, type MessagePart, type MessagePayload, type MessageSendConfiguration, type NearbyAgent, type OutputMode, type Part, type PublicAgent, type PushNotificationConfig, type RatingResult, SYSTEM_SENDER_ID, type SecretInfo, type SendAndWaitOptions, type SendMessageConfig, type SendOptions, type StreamResponse, type SystemMessage, type Task, type TaskArtifactUpdateEvent, type TaskEvent, type TaskListConfiguration, type TaskPushNotificationConfig, type TaskQueryConfiguration, type AgentTaskResult as TaskResult, type TaskState, type TaskStatus, type TaskStatusUpdateEvent, TaskStream, type TextPart, type Workspace, type WorkspaceForgetParams, type WorkspaceListMemoriesParams, type WorkspaceMember, type WorkspaceMemory, type WorkspaceQueryParams, type WorkspaceStoreParams, type WorkspaceUpdateParams, GopherHole as default, getTaskResponseText }; | ||
| export { type A2AArtifact, A2AClient, type A2AClientOptions, type A2AMessage, type A2ATask, type A2ATaskStatus, type AgentArtifact, type AgentAuthentication, type AgentCapabilities, type AgentCard, type AgentCardConfig, type AgentCategory, type AgentInfoResult, type AgentMessagePart, type AgentReview, type AgentSkill, type AgentSkillConfig, type AgentTaskResult, type AgentTaskStatus, type Artifact, AutoTransport, type AvailableAgent, type ContentMode, type DataContent, type DataPart, type DiscoverNearbyOptions, type DiscoverNearbyResult, type DiscoverOptions, type DiscoverResult, type DiscoveredAgent, type FileContent, type FilePart, GopherHole, GopherHoleAgent, type GopherHoleAgentOptions, type GopherHoleOptions, type Artifact$1 as HttpArtifact, type Task$1 as HttpTask, type TaskStatus$1 as HttpTaskStatus, HttpTransport, type IncomingMessage, type InputMode, type JsonRpcError, JsonRpcErrorCodes, type JsonRpcRequest$1 as JsonRpcRequest, type JsonRpcResponse$1 as JsonRpcResponse, MEMORY_TYPES, type MemoryType, type MemoryTypeInfo, type Message, type MessageContext, type MessageHandler, type MessageMetadata, type MessagePart, type MessagePayload, type MessageSendConfiguration, type NearbyAgent, type OutputMode, type Part, type PublicAgent, type PushNotificationConfig, type RatingResult, SYSTEM_SENDER_ID, type SecretInfo, type SendAndWaitOptions, type SendMessageConfig, type SendOptions, type StreamResponse, type SystemMessage, type Task, type TaskArtifactUpdateEvent, type TaskEvent, type TaskListConfiguration, type TaskPushNotificationConfig, type TaskQueryConfiguration, type AgentTaskResult as TaskResult, type TaskState, type TaskStatus, type TaskStatusUpdateEvent, TaskStream, type TextPart, type Transport, type TransportMode, type Workspace, type WorkspaceForgetParams, type WorkspaceListMemoriesParams, type WorkspaceMember, type WorkspaceMemory, type WorkspaceQueryParams, type WorkspaceStoreParams, type WorkspaceUpdateParams, WsTransport, GopherHole as default, getTaskResponseText }; |
+220
-52
@@ -24,4 +24,6 @@ "use strict"; | ||
| A2AClient: () => A2AClient, | ||
| AutoTransport: () => AutoTransport, | ||
| GopherHole: () => GopherHole, | ||
| GopherHoleAgent: () => GopherHoleAgent, | ||
| HttpTransport: () => HttpTransport, | ||
| JsonRpcErrorCodes: () => JsonRpcErrorCodes, | ||
@@ -31,2 +33,3 @@ MEMORY_TYPES: () => MEMORY_TYPES, | ||
| TaskStream: () => TaskStream, | ||
| WsTransport: () => WsTransport, | ||
| default: () => index_default, | ||
@@ -55,2 +58,128 @@ getTaskResponseText: () => getTaskResponseText | ||
| // src/transport.ts | ||
| var HttpTransport = class { | ||
| constructor(apiUrl, apiKey, defaultTimeout) { | ||
| this.apiUrl = apiUrl; | ||
| this.apiKey = apiKey; | ||
| this.defaultTimeout = defaultTimeout; | ||
| } | ||
| get isOpen() { | ||
| return true; | ||
| } | ||
| async request(method, params, timeoutMs) { | ||
| const timeout = timeoutMs ?? this.defaultTimeout; | ||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), timeout); | ||
| try { | ||
| const response = await fetch(`${this.apiUrl}/a2a`, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| "Authorization": `Bearer ${this.apiKey}` | ||
| }, | ||
| body: JSON.stringify({ | ||
| jsonrpc: "2.0", | ||
| method, | ||
| params, | ||
| id: Date.now() | ||
| }), | ||
| signal: controller.signal | ||
| }); | ||
| const data = await response.json(); | ||
| if (data.error) { | ||
| throw new Error(data.error.message || "RPC error"); | ||
| } | ||
| return data.result; | ||
| } catch (err) { | ||
| if (err instanceof Error && err.name === "AbortError") { | ||
| throw new Error(`Request timeout after ${timeout}ms`); | ||
| } | ||
| throw err; | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
| } | ||
| }; | ||
| var WsTransport = class { | ||
| constructor(getWs, defaultTimeout, wsFallback, apiUrl, apiKey) { | ||
| this.getWs = getWs; | ||
| this.defaultTimeout = defaultTimeout; | ||
| this.pendingRequests = /* @__PURE__ */ new Map(); | ||
| this.requestId = 0; | ||
| this.httpFallback = wsFallback ? new HttpTransport(apiUrl, apiKey, defaultTimeout) : null; | ||
| } | ||
| get isOpen() { | ||
| const ws = this.getWs(); | ||
| return ws?.readyState === 1; | ||
| } | ||
| /** | ||
| * Handle an incoming WebSocket message. Called by the GopherHole class when a | ||
| * message arrives on the WebSocket. Returns true if the message was a JSON-RPC | ||
| * response that was consumed, false otherwise. | ||
| */ | ||
| handleMessage(data) { | ||
| if (data.jsonrpc === "2.0" && data.id != null && (data.result !== void 0 || data.error !== void 0)) { | ||
| const pending = this.pendingRequests.get(data.id); | ||
| if (pending) { | ||
| this.pendingRequests.delete(data.id); | ||
| clearTimeout(pending.timer); | ||
| if (data.error) { | ||
| pending.reject(new Error(data.error.message || "RPC error")); | ||
| } else { | ||
| pending.resolve(data.result); | ||
| } | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| async request(method, params, timeoutMs) { | ||
| const ws = this.getWs(); | ||
| if (!ws || ws.readyState !== 1) { | ||
| if (this.httpFallback) { | ||
| return this.httpFallback.request(method, params, timeoutMs); | ||
| } | ||
| throw new Error("WebSocket not connected. Call connect() first or enable wsFallback."); | ||
| } | ||
| const timeout = timeoutMs ?? this.defaultTimeout; | ||
| const id = ++this.requestId; | ||
| return new Promise((resolve, reject) => { | ||
| const timer = setTimeout(() => { | ||
| this.pendingRequests.delete(id); | ||
| reject(new Error(`Request timeout after ${timeout}ms`)); | ||
| }, timeout); | ||
| this.pendingRequests.set(id, { | ||
| resolve, | ||
| reject, | ||
| timer | ||
| }); | ||
| ws.send(JSON.stringify({ | ||
| jsonrpc: "2.0", | ||
| id, | ||
| method, | ||
| params | ||
| })); | ||
| }); | ||
| } | ||
| /** Clean up pending requests on disconnect */ | ||
| cleanup() { | ||
| for (const [id, pending] of this.pendingRequests) { | ||
| clearTimeout(pending.timer); | ||
| pending.reject(new Error("WebSocket disconnected")); | ||
| } | ||
| this.pendingRequests.clear(); | ||
| } | ||
| }; | ||
| var AutoTransport = class { | ||
| constructor(apiUrl, apiKey, defaultTimeout) { | ||
| this.http = new HttpTransport(apiUrl, apiKey, defaultTimeout); | ||
| } | ||
| get isOpen() { | ||
| return true; | ||
| } | ||
| async request(method, params, timeoutMs) { | ||
| return this.http.request(method, params, timeoutMs); | ||
| } | ||
| }; | ||
| // src/http.ts | ||
@@ -546,2 +675,3 @@ var import_eventemitter3 = require("eventemitter3"); | ||
| super(); | ||
| this.wsTransport = null; | ||
| this.ws = null; | ||
@@ -564,2 +694,23 @@ this.reconnectAttempts = 0; | ||
| this.messageTimeout = options.messageTimeout ?? 3e4; | ||
| this.transportMode = options.transport ?? "auto"; | ||
| const wsFallback = options.wsFallback ?? true; | ||
| switch (this.transportMode) { | ||
| case "http": | ||
| this.transport = new HttpTransport(this.apiUrl, this.apiKey, this.requestTimeout); | ||
| break; | ||
| case "ws": | ||
| this.wsTransport = new WsTransport( | ||
| () => this.ws, | ||
| this.requestTimeout, | ||
| wsFallback, | ||
| this.apiUrl, | ||
| this.apiKey | ||
| ); | ||
| this.transport = this.wsTransport; | ||
| break; | ||
| case "auto": | ||
| default: | ||
| this.transport = new AutoTransport(this.apiUrl, this.apiKey, this.requestTimeout); | ||
| break; | ||
| } | ||
| } | ||
@@ -582,5 +733,11 @@ /** | ||
| /** | ||
| * Connect to the GopherHole hub via WebSocket | ||
| * Connect to the GopherHole hub via WebSocket. | ||
| * For transport: 'http', this is a no-op. | ||
| * For transport: 'ws', this is required before sending any messages. | ||
| * For transport: 'auto', this is optional and enables push events. | ||
| */ | ||
| async connect() { | ||
| if (this.transportMode === "http") { | ||
| return; | ||
| } | ||
| return new Promise((resolve, reject) => { | ||
@@ -634,2 +791,5 @@ const WS = typeof WebSocket !== "undefined" ? WebSocket : require("ws"); | ||
| } | ||
| if (this.wsTransport) { | ||
| this.wsTransport.cleanup(); | ||
| } | ||
| if (this.ws) { | ||
@@ -730,25 +890,33 @@ this.ws.close(); | ||
| /** | ||
| * Respond to an incoming task via WebSocket (completes the task) | ||
| * Use this when you receive a 'message' event and want to send back a response | ||
| * that completes the original task. | ||
| * Respond to an incoming task (completes the task). | ||
| * Uses WebSocket if connected, otherwise falls back to HTTP via task/respond RPC. | ||
| */ | ||
| respond(taskId, text, options) { | ||
| if (!this.ws || this.ws.readyState !== 1) { | ||
| throw new Error("WebSocket not connected"); | ||
| const status = { | ||
| state: options?.status ?? "completed", | ||
| message: options?.message | ||
| }; | ||
| const artifact = { | ||
| artifactId: `response-${Date.now()}`, | ||
| mimeType: "text/plain", | ||
| parts: [{ kind: "text", text }] | ||
| }; | ||
| if (this.ws?.readyState === 1) { | ||
| this.ws.send(JSON.stringify({ | ||
| type: "task_response", | ||
| taskId, | ||
| status, | ||
| artifact, | ||
| lastChunk: true | ||
| })); | ||
| return; | ||
| } | ||
| const response = { | ||
| type: "task_response", | ||
| this.rpc("task/respond", { | ||
| taskId, | ||
| status: { | ||
| state: options?.status ?? "completed", | ||
| message: options?.message | ||
| }, | ||
| artifact: { | ||
| artifactId: `response-${Date.now()}`, | ||
| mimeType: "text/plain", | ||
| parts: [{ kind: "text", text }] | ||
| }, | ||
| status, | ||
| artifact, | ||
| lastChunk: true | ||
| }; | ||
| this.ws.send(JSON.stringify(response)); | ||
| }).catch((err) => { | ||
| this.emit("error", new Error(`Failed to respond to task ${taskId}: ${err.message}`)); | ||
| }); | ||
| } | ||
@@ -923,36 +1091,6 @@ /** | ||
| /** | ||
| * Make a JSON-RPC call to the A2A endpoint | ||
| * Make a JSON-RPC call via the configured transport | ||
| */ | ||
| async rpc(method, params, timeoutMs) { | ||
| const timeout = timeoutMs ?? this.requestTimeout; | ||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), timeout); | ||
| try { | ||
| const response = await fetch(`${this.apiUrl}/a2a`, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| "Authorization": `Bearer ${this.apiKey}` | ||
| }, | ||
| body: JSON.stringify({ | ||
| jsonrpc: "2.0", | ||
| method, | ||
| params, | ||
| id: Date.now() | ||
| }), | ||
| signal: controller.signal | ||
| }); | ||
| const data = await response.json(); | ||
| if (data.error) { | ||
| throw new Error(data.error.message || "RPC error"); | ||
| } | ||
| return data.result; | ||
| } catch (err) { | ||
| if (err instanceof Error && err.name === "AbortError") { | ||
| throw new Error(`Request timeout after ${timeout}ms`); | ||
| } | ||
| throw err; | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
| return this.transport.request(method, params, timeoutMs); | ||
| } | ||
@@ -963,2 +1101,5 @@ /** | ||
| handleMessage(data) { | ||
| if (this.wsTransport?.handleMessage(data)) { | ||
| return; | ||
| } | ||
| if (data.type === "message") { | ||
@@ -1041,5 +1182,18 @@ const message = { | ||
| /** | ||
| * Discover public agents with comprehensive search | ||
| * Discover public agents with comprehensive search. | ||
| * Uses JSON-RPC via transport for 'ws' mode, HTTP REST for 'http'/'auto'. | ||
| */ | ||
| async discover(options) { | ||
| if (this.transportMode === "ws") { | ||
| return this.rpc("x-gopherhole/agents.discover", { | ||
| query: options?.query, | ||
| category: options?.category, | ||
| tag: options?.tag, | ||
| owner: options?.owner, | ||
| verified: options?.verified, | ||
| sort: options?.sort, | ||
| limit: options?.limit, | ||
| offset: options?.offset | ||
| }); | ||
| } | ||
| const params = new URLSearchParams(); | ||
@@ -1116,2 +1270,13 @@ if (options?.query) params.set("q", options.query); | ||
| async discoverNearby(options) { | ||
| if (this.transportMode === "ws") { | ||
| return this.rpc("x-gopherhole/agents.discover.nearby", { | ||
| lat: options.lat, | ||
| lng: options.lng, | ||
| radius: options.radius, | ||
| tag: options.tag, | ||
| category: options.category, | ||
| limit: options.limit, | ||
| offset: options.offset | ||
| }); | ||
| } | ||
| const params = new URLSearchParams(); | ||
@@ -1216,4 +1381,6 @@ params.set("lat", String(options.lat)); | ||
| A2AClient, | ||
| AutoTransport, | ||
| GopherHole, | ||
| GopherHoleAgent, | ||
| HttpTransport, | ||
| JsonRpcErrorCodes, | ||
@@ -1223,3 +1390,4 @@ MEMORY_TYPES, | ||
| TaskStream, | ||
| WsTransport, | ||
| getTaskResponseText | ||
| }); |
+217
-52
@@ -28,2 +28,128 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { | ||
| // src/transport.ts | ||
| var HttpTransport = class { | ||
| constructor(apiUrl, apiKey, defaultTimeout) { | ||
| this.apiUrl = apiUrl; | ||
| this.apiKey = apiKey; | ||
| this.defaultTimeout = defaultTimeout; | ||
| } | ||
| get isOpen() { | ||
| return true; | ||
| } | ||
| async request(method, params, timeoutMs) { | ||
| const timeout = timeoutMs ?? this.defaultTimeout; | ||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), timeout); | ||
| try { | ||
| const response = await fetch(`${this.apiUrl}/a2a`, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| "Authorization": `Bearer ${this.apiKey}` | ||
| }, | ||
| body: JSON.stringify({ | ||
| jsonrpc: "2.0", | ||
| method, | ||
| params, | ||
| id: Date.now() | ||
| }), | ||
| signal: controller.signal | ||
| }); | ||
| const data = await response.json(); | ||
| if (data.error) { | ||
| throw new Error(data.error.message || "RPC error"); | ||
| } | ||
| return data.result; | ||
| } catch (err) { | ||
| if (err instanceof Error && err.name === "AbortError") { | ||
| throw new Error(`Request timeout after ${timeout}ms`); | ||
| } | ||
| throw err; | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
| } | ||
| }; | ||
| var WsTransport = class { | ||
| constructor(getWs, defaultTimeout, wsFallback, apiUrl, apiKey) { | ||
| this.getWs = getWs; | ||
| this.defaultTimeout = defaultTimeout; | ||
| this.pendingRequests = /* @__PURE__ */ new Map(); | ||
| this.requestId = 0; | ||
| this.httpFallback = wsFallback ? new HttpTransport(apiUrl, apiKey, defaultTimeout) : null; | ||
| } | ||
| get isOpen() { | ||
| const ws = this.getWs(); | ||
| return ws?.readyState === 1; | ||
| } | ||
| /** | ||
| * Handle an incoming WebSocket message. Called by the GopherHole class when a | ||
| * message arrives on the WebSocket. Returns true if the message was a JSON-RPC | ||
| * response that was consumed, false otherwise. | ||
| */ | ||
| handleMessage(data) { | ||
| if (data.jsonrpc === "2.0" && data.id != null && (data.result !== void 0 || data.error !== void 0)) { | ||
| const pending = this.pendingRequests.get(data.id); | ||
| if (pending) { | ||
| this.pendingRequests.delete(data.id); | ||
| clearTimeout(pending.timer); | ||
| if (data.error) { | ||
| pending.reject(new Error(data.error.message || "RPC error")); | ||
| } else { | ||
| pending.resolve(data.result); | ||
| } | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| async request(method, params, timeoutMs) { | ||
| const ws = this.getWs(); | ||
| if (!ws || ws.readyState !== 1) { | ||
| if (this.httpFallback) { | ||
| return this.httpFallback.request(method, params, timeoutMs); | ||
| } | ||
| throw new Error("WebSocket not connected. Call connect() first or enable wsFallback."); | ||
| } | ||
| const timeout = timeoutMs ?? this.defaultTimeout; | ||
| const id = ++this.requestId; | ||
| return new Promise((resolve, reject) => { | ||
| const timer = setTimeout(() => { | ||
| this.pendingRequests.delete(id); | ||
| reject(new Error(`Request timeout after ${timeout}ms`)); | ||
| }, timeout); | ||
| this.pendingRequests.set(id, { | ||
| resolve, | ||
| reject, | ||
| timer | ||
| }); | ||
| ws.send(JSON.stringify({ | ||
| jsonrpc: "2.0", | ||
| id, | ||
| method, | ||
| params | ||
| })); | ||
| }); | ||
| } | ||
| /** Clean up pending requests on disconnect */ | ||
| cleanup() { | ||
| for (const [id, pending] of this.pendingRequests) { | ||
| clearTimeout(pending.timer); | ||
| pending.reject(new Error("WebSocket disconnected")); | ||
| } | ||
| this.pendingRequests.clear(); | ||
| } | ||
| }; | ||
| var AutoTransport = class { | ||
| constructor(apiUrl, apiKey, defaultTimeout) { | ||
| this.http = new HttpTransport(apiUrl, apiKey, defaultTimeout); | ||
| } | ||
| get isOpen() { | ||
| return true; | ||
| } | ||
| async request(method, params, timeoutMs) { | ||
| return this.http.request(method, params, timeoutMs); | ||
| } | ||
| }; | ||
| // src/http.ts | ||
@@ -519,2 +645,3 @@ import { EventEmitter } from "eventemitter3"; | ||
| super(); | ||
| this.wsTransport = null; | ||
| this.ws = null; | ||
@@ -537,2 +664,23 @@ this.reconnectAttempts = 0; | ||
| this.messageTimeout = options.messageTimeout ?? 3e4; | ||
| this.transportMode = options.transport ?? "auto"; | ||
| const wsFallback = options.wsFallback ?? true; | ||
| switch (this.transportMode) { | ||
| case "http": | ||
| this.transport = new HttpTransport(this.apiUrl, this.apiKey, this.requestTimeout); | ||
| break; | ||
| case "ws": | ||
| this.wsTransport = new WsTransport( | ||
| () => this.ws, | ||
| this.requestTimeout, | ||
| wsFallback, | ||
| this.apiUrl, | ||
| this.apiKey | ||
| ); | ||
| this.transport = this.wsTransport; | ||
| break; | ||
| case "auto": | ||
| default: | ||
| this.transport = new AutoTransport(this.apiUrl, this.apiKey, this.requestTimeout); | ||
| break; | ||
| } | ||
| } | ||
@@ -555,5 +703,11 @@ /** | ||
| /** | ||
| * Connect to the GopherHole hub via WebSocket | ||
| * Connect to the GopherHole hub via WebSocket. | ||
| * For transport: 'http', this is a no-op. | ||
| * For transport: 'ws', this is required before sending any messages. | ||
| * For transport: 'auto', this is optional and enables push events. | ||
| */ | ||
| async connect() { | ||
| if (this.transportMode === "http") { | ||
| return; | ||
| } | ||
| return new Promise((resolve, reject) => { | ||
@@ -607,2 +761,5 @@ const WS = typeof WebSocket !== "undefined" ? WebSocket : __require("ws"); | ||
| } | ||
| if (this.wsTransport) { | ||
| this.wsTransport.cleanup(); | ||
| } | ||
| if (this.ws) { | ||
@@ -703,25 +860,33 @@ this.ws.close(); | ||
| /** | ||
| * Respond to an incoming task via WebSocket (completes the task) | ||
| * Use this when you receive a 'message' event and want to send back a response | ||
| * that completes the original task. | ||
| * Respond to an incoming task (completes the task). | ||
| * Uses WebSocket if connected, otherwise falls back to HTTP via task/respond RPC. | ||
| */ | ||
| respond(taskId, text, options) { | ||
| if (!this.ws || this.ws.readyState !== 1) { | ||
| throw new Error("WebSocket not connected"); | ||
| const status = { | ||
| state: options?.status ?? "completed", | ||
| message: options?.message | ||
| }; | ||
| const artifact = { | ||
| artifactId: `response-${Date.now()}`, | ||
| mimeType: "text/plain", | ||
| parts: [{ kind: "text", text }] | ||
| }; | ||
| if (this.ws?.readyState === 1) { | ||
| this.ws.send(JSON.stringify({ | ||
| type: "task_response", | ||
| taskId, | ||
| status, | ||
| artifact, | ||
| lastChunk: true | ||
| })); | ||
| return; | ||
| } | ||
| const response = { | ||
| type: "task_response", | ||
| this.rpc("task/respond", { | ||
| taskId, | ||
| status: { | ||
| state: options?.status ?? "completed", | ||
| message: options?.message | ||
| }, | ||
| artifact: { | ||
| artifactId: `response-${Date.now()}`, | ||
| mimeType: "text/plain", | ||
| parts: [{ kind: "text", text }] | ||
| }, | ||
| status, | ||
| artifact, | ||
| lastChunk: true | ||
| }; | ||
| this.ws.send(JSON.stringify(response)); | ||
| }).catch((err) => { | ||
| this.emit("error", new Error(`Failed to respond to task ${taskId}: ${err.message}`)); | ||
| }); | ||
| } | ||
@@ -896,36 +1061,6 @@ /** | ||
| /** | ||
| * Make a JSON-RPC call to the A2A endpoint | ||
| * Make a JSON-RPC call via the configured transport | ||
| */ | ||
| async rpc(method, params, timeoutMs) { | ||
| const timeout = timeoutMs ?? this.requestTimeout; | ||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), timeout); | ||
| try { | ||
| const response = await fetch(`${this.apiUrl}/a2a`, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| "Authorization": `Bearer ${this.apiKey}` | ||
| }, | ||
| body: JSON.stringify({ | ||
| jsonrpc: "2.0", | ||
| method, | ||
| params, | ||
| id: Date.now() | ||
| }), | ||
| signal: controller.signal | ||
| }); | ||
| const data = await response.json(); | ||
| if (data.error) { | ||
| throw new Error(data.error.message || "RPC error"); | ||
| } | ||
| return data.result; | ||
| } catch (err) { | ||
| if (err instanceof Error && err.name === "AbortError") { | ||
| throw new Error(`Request timeout after ${timeout}ms`); | ||
| } | ||
| throw err; | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
| return this.transport.request(method, params, timeoutMs); | ||
| } | ||
@@ -936,2 +1071,5 @@ /** | ||
| handleMessage(data) { | ||
| if (this.wsTransport?.handleMessage(data)) { | ||
| return; | ||
| } | ||
| if (data.type === "message") { | ||
@@ -1014,5 +1152,18 @@ const message = { | ||
| /** | ||
| * Discover public agents with comprehensive search | ||
| * Discover public agents with comprehensive search. | ||
| * Uses JSON-RPC via transport for 'ws' mode, HTTP REST for 'http'/'auto'. | ||
| */ | ||
| async discover(options) { | ||
| if (this.transportMode === "ws") { | ||
| return this.rpc("x-gopherhole/agents.discover", { | ||
| query: options?.query, | ||
| category: options?.category, | ||
| tag: options?.tag, | ||
| owner: options?.owner, | ||
| verified: options?.verified, | ||
| sort: options?.sort, | ||
| limit: options?.limit, | ||
| offset: options?.offset | ||
| }); | ||
| } | ||
| const params = new URLSearchParams(); | ||
@@ -1089,2 +1240,13 @@ if (options?.query) params.set("q", options.query); | ||
| async discoverNearby(options) { | ||
| if (this.transportMode === "ws") { | ||
| return this.rpc("x-gopherhole/agents.discover.nearby", { | ||
| lat: options.lat, | ||
| lng: options.lng, | ||
| radius: options.radius, | ||
| tag: options.tag, | ||
| category: options.category, | ||
| limit: options.limit, | ||
| offset: options.offset | ||
| }); | ||
| } | ||
| const params = new URLSearchParams(); | ||
@@ -1188,4 +1350,6 @@ params.set("lat", String(options.lat)); | ||
| A2AClient, | ||
| AutoTransport, | ||
| GopherHole, | ||
| GopherHoleAgent, | ||
| HttpTransport, | ||
| JsonRpcErrorCodes, | ||
@@ -1195,4 +1359,5 @@ MEMORY_TYPES, | ||
| TaskStream, | ||
| WsTransport, | ||
| index_default as default, | ||
| getTaskResponseText | ||
| }; |
+1
-1
| { | ||
| "name": "@gopherhole/sdk", | ||
| "version": "0.5.4", | ||
| "version": "0.6.0", | ||
| "description": "GopherHole SDK - Connect AI agents via the A2A protocol", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
+117
-62
@@ -6,2 +6,6 @@ import { EventEmitter } from 'eventemitter3'; | ||
| // Re-export transport types | ||
| export { TransportMode, Transport, HttpTransport, WsTransport, AutoTransport } from './transport.js'; | ||
| import { TransportMode, Transport, HttpTransport, WsTransport, AutoTransport } from './transport.js'; | ||
| // Re-export HTTP client for A2A JSON-RPC over HTTP | ||
@@ -59,2 +63,6 @@ export { A2AClient, TaskStream } from './http.js'; | ||
| hubUrl?: string; | ||
| /** Transport mode: 'http', 'ws', or 'auto' (default: 'auto') */ | ||
| transport?: TransportMode; | ||
| /** Fall back to HTTP if WebSocket disconnects (only applies to 'ws' mode, default: true) */ | ||
| wsFallback?: boolean; | ||
| /** Agent card to register on connect */ | ||
@@ -245,2 +253,5 @@ agentCard?: AgentCardConfig; | ||
| private apiUrl: string; | ||
| private transportMode: TransportMode; | ||
| private transport: Transport; | ||
| private wsTransport: WsTransport | null = null; | ||
| private ws: WebSocket | null = null; | ||
@@ -276,2 +287,25 @@ private autoReconnect: boolean; | ||
| this.messageTimeout = options.messageTimeout ?? 30000; | ||
| this.transportMode = options.transport ?? 'auto'; | ||
| // Initialize transport based on mode | ||
| const wsFallback = options.wsFallback ?? true; | ||
| switch (this.transportMode) { | ||
| case 'http': | ||
| this.transport = new HttpTransport(this.apiUrl, this.apiKey, this.requestTimeout); | ||
| break; | ||
| case 'ws': | ||
| this.wsTransport = new WsTransport( | ||
| () => this.ws, | ||
| this.requestTimeout, | ||
| wsFallback, | ||
| this.apiUrl, | ||
| this.apiKey, | ||
| ); | ||
| this.transport = this.wsTransport; | ||
| break; | ||
| case 'auto': | ||
| default: | ||
| this.transport = new AutoTransport(this.apiUrl, this.apiKey, this.requestTimeout); | ||
| break; | ||
| } | ||
| } | ||
@@ -297,5 +331,13 @@ | ||
| /** | ||
| * Connect to the GopherHole hub via WebSocket | ||
| * Connect to the GopherHole hub via WebSocket. | ||
| * For transport: 'http', this is a no-op. | ||
| * For transport: 'ws', this is required before sending any messages. | ||
| * For transport: 'auto', this is optional and enables push events. | ||
| */ | ||
| async connect(): Promise<void> { | ||
| // HTTP transport — no WebSocket needed | ||
| if (this.transportMode === 'http') { | ||
| return; | ||
| } | ||
| return new Promise((resolve, reject) => { | ||
@@ -361,2 +403,5 @@ // Browser or Node WebSocket | ||
| } | ||
| if (this.wsTransport) { | ||
| this.wsTransport.cleanup(); | ||
| } | ||
| if (this.ws) { | ||
@@ -482,27 +527,37 @@ this.ws.close(); | ||
| /** | ||
| * Respond to an incoming task via WebSocket (completes the task) | ||
| * Use this when you receive a 'message' event and want to send back a response | ||
| * that completes the original task. | ||
| * Respond to an incoming task (completes the task). | ||
| * Uses WebSocket if connected, otherwise falls back to HTTP via task/respond RPC. | ||
| */ | ||
| respond(taskId: string, text: string, options?: { status?: 'completed' | 'failed'; message?: string }): void { | ||
| if (!this.ws || this.ws.readyState !== 1) { | ||
| throw new Error('WebSocket not connected'); | ||
| const status = { | ||
| state: options?.status ?? 'completed', | ||
| message: options?.message, | ||
| }; | ||
| const artifact = { | ||
| artifactId: `response-${Date.now()}`, | ||
| mimeType: 'text/plain', | ||
| parts: [{ kind: 'text' as const, text }], | ||
| }; | ||
| // Use WebSocket if connected (existing behaviour) | ||
| if (this.ws?.readyState === 1) { | ||
| this.ws.send(JSON.stringify({ | ||
| type: 'task_response', | ||
| taskId, | ||
| status, | ||
| artifact, | ||
| lastChunk: true, | ||
| })); | ||
| return; | ||
| } | ||
| const response = { | ||
| type: 'task_response', | ||
| // Fall back to HTTP via task/respond RPC (enables transport: 'http') | ||
| this.rpc('task/respond', { | ||
| taskId, | ||
| status: { | ||
| state: options?.status ?? 'completed', | ||
| message: options?.message, | ||
| }, | ||
| artifact: { | ||
| artifactId: `response-${Date.now()}`, | ||
| mimeType: 'text/plain', | ||
| parts: [{ kind: 'text', text }], | ||
| }, | ||
| status, | ||
| artifact, | ||
| lastChunk: true, | ||
| }; | ||
| this.ws.send(JSON.stringify(response)); | ||
| }).catch((err) => { | ||
| this.emit('error', new Error(`Failed to respond to task ${taskId}: ${(err as Error).message}`)); | ||
| }); | ||
| } | ||
@@ -710,40 +765,6 @@ | ||
| /** | ||
| * Make a JSON-RPC call to the A2A endpoint | ||
| * Make a JSON-RPC call via the configured transport | ||
| */ | ||
| private async rpc(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<unknown> { | ||
| const timeout = timeoutMs ?? this.requestTimeout; | ||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), timeout); | ||
| try { | ||
| const response = await fetch(`${this.apiUrl}/a2a`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${this.apiKey}`, | ||
| }, | ||
| body: JSON.stringify({ | ||
| jsonrpc: '2.0', | ||
| method, | ||
| params, | ||
| id: Date.now(), | ||
| }), | ||
| signal: controller.signal, | ||
| }); | ||
| const data = await response.json(); | ||
| if (data.error) { | ||
| throw new Error(data.error.message || 'RPC error'); | ||
| } | ||
| return data.result; | ||
| } catch (err) { | ||
| if (err instanceof Error && err.name === 'AbortError') { | ||
| throw new Error(`Request timeout after ${timeout}ms`); | ||
| } | ||
| throw err; | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
| return this.transport.request(method, params, timeoutMs); | ||
| } | ||
@@ -755,2 +776,7 @@ | ||
| private handleMessage(data: any): void { | ||
| // Route JSON-RPC responses to the WsTransport (for transport: 'ws' mode) | ||
| if (this.wsTransport?.handleMessage(data)) { | ||
| return; | ||
| } | ||
| if (data.type === 'message') { | ||
@@ -852,7 +878,23 @@ const message: Message = { | ||
| /** | ||
| * Discover public agents with comprehensive search | ||
| * Discover public agents with comprehensive search. | ||
| * Uses JSON-RPC via transport for 'ws' mode, HTTP REST for 'http'/'auto'. | ||
| */ | ||
| async discover(options?: DiscoverOptions): Promise<DiscoverResult> { | ||
| // WebSocket transport: route through JSON-RPC | ||
| if (this.transportMode === 'ws') { | ||
| return this.rpc('x-gopherhole/agents.discover', { | ||
| query: options?.query, | ||
| category: options?.category, | ||
| tag: options?.tag, | ||
| owner: options?.owner, | ||
| verified: options?.verified, | ||
| sort: options?.sort, | ||
| limit: options?.limit, | ||
| offset: options?.offset, | ||
| }) as Promise<DiscoverResult>; | ||
| } | ||
| // HTTP/Auto: use REST endpoint (existing behaviour) | ||
| const params = new URLSearchParams(); | ||
| if (options?.query) params.set('q', options.query); | ||
@@ -869,4 +911,3 @@ if (options?.category) params.set('category', options.category); | ||
| if (options?.scope) params.set('scope', options.scope); | ||
| // Include API key to see same-tenant agents (not just public) | ||
| const response = await fetch(`${this.apiUrl}/api/discover/agents?${params}`, { | ||
@@ -940,4 +981,18 @@ headers: { | ||
| async discoverNearby(options: DiscoverNearbyOptions): Promise<DiscoverNearbyResult> { | ||
| // WebSocket transport: route through JSON-RPC | ||
| if (this.transportMode === 'ws') { | ||
| return this.rpc('x-gopherhole/agents.discover.nearby', { | ||
| lat: options.lat, | ||
| lng: options.lng, | ||
| radius: options.radius, | ||
| tag: options.tag, | ||
| category: options.category, | ||
| limit: options.limit, | ||
| offset: options.offset, | ||
| }) as Promise<DiscoverNearbyResult>; | ||
| } | ||
| // HTTP/Auto: use REST endpoint | ||
| const params = new URLSearchParams(); | ||
| params.set('lat', String(options.lat)); | ||
@@ -950,3 +1005,3 @@ params.set('lng', String(options.lng)); | ||
| if (options.offset) params.set('offset', String(options.offset)); | ||
| const response = await fetch(`${this.apiUrl}/api/discover/agents/nearby?${params}`, { | ||
@@ -953,0 +1008,0 @@ headers: { |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 6 instances
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 6 instances
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
67821767
0.04%1077
0.09%350219
0.18%