@easbot/gateway 0.2.40 → 0.2.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -42,6 +42,7 @@ declare const ChannelTypeSchema: z.ZodEnum<{
42
42
  webchat: "webchat";
43
43
  signal: "signal";
44
44
  nostr: "nostr";
45
+ dingtalk: "dingtalk";
45
46
  }>;
46
47
  type ChannelType = z.infer<typeof ChannelTypeSchema>;
47
48
  declare const MessagePrioritySchema: z.ZodEnum<{
@@ -193,6 +194,20 @@ declare const ChannelConfigSchema: z.ZodObject<{
193
194
  maxConnections: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
194
195
  timeout: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
195
196
  }, z.core.$strip>>;
197
+ dingtalk: z.ZodOptional<z.ZodObject<{
198
+ enabled: z.ZodDefault<z.ZodBoolean>;
199
+ appKey: z.ZodString;
200
+ appSecret: z.ZodString;
201
+ token: z.ZodOptional<z.ZodString>;
202
+ encryptKey: z.ZodOptional<z.ZodString>;
203
+ webhook: z.ZodOptional<z.ZodObject<{
204
+ enabled: z.ZodDefault<z.ZodBoolean>;
205
+ path: z.ZodDefault<z.ZodOptional<z.ZodString>>;
206
+ port: z.ZodOptional<z.ZodNumber>;
207
+ }, z.core.$strip>>;
208
+ maxConnections: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
209
+ timeout: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
210
+ }, z.core.$strip>>;
196
211
  }, z.core.$strip>;
197
212
  declare const HTTPSConfigSchema: z.ZodObject<{
198
213
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -549,6 +564,20 @@ declare const GatewayConfigSchema: z.ZodObject<{
549
564
  maxConnections: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
550
565
  timeout: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
551
566
  }, z.core.$strip>>;
567
+ dingtalk: z.ZodOptional<z.ZodObject<{
568
+ enabled: z.ZodDefault<z.ZodBoolean>;
569
+ appKey: z.ZodString;
570
+ appSecret: z.ZodString;
571
+ token: z.ZodOptional<z.ZodString>;
572
+ encryptKey: z.ZodOptional<z.ZodString>;
573
+ webhook: z.ZodOptional<z.ZodObject<{
574
+ enabled: z.ZodDefault<z.ZodBoolean>;
575
+ path: z.ZodDefault<z.ZodOptional<z.ZodString>>;
576
+ port: z.ZodOptional<z.ZodNumber>;
577
+ }, z.core.$strip>>;
578
+ maxConnections: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
579
+ timeout: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
580
+ }, z.core.$strip>>;
552
581
  }, z.core.$strip>>;
553
582
  cluster: z.ZodOptional<z.ZodObject<{
554
583
  nodeId: z.ZodString;
@@ -705,6 +734,9 @@ interface ChannelCapabilities {
705
734
  supportsHtml: boolean;
706
735
  supportsDeliveryReceipt: boolean;
707
736
  supportsReadReceipt: boolean;
737
+ supportsSlashCommands: boolean;
738
+ supportsNativeCommands: boolean;
739
+ commandPrefix: string;
708
740
  maxMessageLength: number;
709
741
  maxMediaCount: number;
710
742
  }
@@ -2195,6 +2227,9 @@ declare class TelegramPlugin extends BaseChannelPlugin {
2195
2227
  supportsHtml: boolean;
2196
2228
  supportsDeliveryReceipt: boolean;
2197
2229
  supportsReadReceipt: boolean;
2230
+ supportsSlashCommands: boolean;
2231
+ supportsNativeCommands: boolean;
2232
+ commandPrefix: string;
2198
2233
  maxMessageLength: number;
2199
2234
  maxMediaCount: number;
2200
2235
  };
@@ -2232,13 +2267,39 @@ interface DiscordBotConfig {
2232
2267
  applicationId?: string;
2233
2268
  gateway?: {
2234
2269
  enabled: boolean;
2235
- intents: number[];
2270
+ intents?: number[];
2236
2271
  };
2237
2272
  webhook?: {
2238
2273
  enabled: boolean;
2239
2274
  path: string;
2240
2275
  port?: number;
2241
2276
  };
2277
+ commands?: DiscordCommand[];
2278
+ retry?: {
2279
+ maxAttempts?: number;
2280
+ initialDelay?: number;
2281
+ maxDelay?: number;
2282
+ };
2283
+ }
2284
+ interface DiscordCommand {
2285
+ name: string;
2286
+ description: string;
2287
+ options?: Array<{
2288
+ name: string;
2289
+ description: string;
2290
+ type: number;
2291
+ required?: boolean;
2292
+ }>;
2293
+ handler: (ctx: DiscordCommandContext) => Promise<void>;
2294
+ }
2295
+ interface DiscordCommandContext {
2296
+ userId: string;
2297
+ guildId?: string;
2298
+ channelId: string;
2299
+ messageId: string;
2300
+ commandName: string;
2301
+ options: Record<string, unknown>;
2302
+ reply?: (text: string) => Promise<string>;
2242
2303
  }
2243
2304
  declare class DiscordPlugin extends BaseChannelPlugin {
2244
2305
  readonly id: string;
@@ -2251,12 +2312,20 @@ declare class DiscordPlugin extends BaseChannelPlugin {
2251
2312
  private sessionId;
2252
2313
  private sequenceNumber;
2253
2314
  private abortController;
2315
+ private processedMessageIds;
2316
+ private sentMessages;
2317
+ private sessionIdMap;
2318
+ private commandRouter;
2254
2319
  private reconnectAttempts;
2255
2320
  private maxReconnectAttempts;
2321
+ private retryState;
2256
2322
  constructor(id?: string);
2257
2323
  protected doStart(): Promise<void>;
2258
2324
  protected doStop(): Promise<void>;
2325
+ protected doHealthCheck(): Promise<boolean>;
2259
2326
  send(message: GatewayMessage): Promise<string>;
2327
+ private createMessage;
2328
+ private syncCommands;
2260
2329
  getCapabilities(): {
2261
2330
  supportsText: boolean;
2262
2331
  supportsMedia: boolean;
@@ -2270,6 +2339,9 @@ declare class DiscordPlugin extends BaseChannelPlugin {
2270
2339
  supportsHtml: boolean;
2271
2340
  supportsDeliveryReceipt: boolean;
2272
2341
  supportsReadReceipt: boolean;
2342
+ supportsSlashCommands: boolean;
2343
+ supportsNativeCommands: boolean;
2344
+ commandPrefix: string;
2273
2345
  maxMessageLength: number;
2274
2346
  maxMediaCount: number;
2275
2347
  };
@@ -2277,14 +2349,17 @@ declare class DiscordPlugin extends BaseChannelPlugin {
2277
2349
  private handleGatewayMessage;
2278
2350
  private handleHello;
2279
2351
  private handleDispatch;
2352
+ private handleReactionEvent;
2353
+ private findSentMessageEntry;
2280
2354
  private handleDiscordMessage;
2281
2355
  private toGatewayMessage;
2282
2356
  private parseContent;
2357
+ private downloadFile;
2358
+ private downloadTextFile;
2283
2359
  protected buildSessionId(channelId: string, chatId: string, userId?: string): string;
2284
2360
  private processMessageAndGetSessionId;
2285
2361
  private getCurrentUser;
2286
2362
  private getGatewayBot;
2287
- private createMessage;
2288
2363
  private request;
2289
2364
  }
2290
2365
 
@@ -2298,6 +2373,24 @@ interface SlackBotConfig {
2298
2373
  path: string;
2299
2374
  port?: number;
2300
2375
  };
2376
+ commands?: SlackBotCommand[];
2377
+ retry?: {
2378
+ maxAttempts?: number;
2379
+ initialDelay?: number;
2380
+ maxDelay?: number;
2381
+ };
2382
+ }
2383
+ interface SlackBotCommand {
2384
+ command: string;
2385
+ description: string;
2386
+ handler: (ctx: SlackCommandContext) => Promise<void>;
2387
+ }
2388
+ interface SlackCommandContext {
2389
+ channelId: string;
2390
+ userId: string;
2391
+ args: string[];
2392
+ ts: string;
2393
+ reply?: (text: string) => Promise<string>;
2301
2394
  }
2302
2395
  declare class SlackPlugin extends BaseChannelPlugin {
2303
2396
  readonly id: string;
@@ -2307,10 +2400,22 @@ declare class SlackPlugin extends BaseChannelPlugin {
2307
2400
  private baseUrl;
2308
2401
  private socketModeClient;
2309
2402
  private abortController;
2403
+ private processedMessageIds;
2404
+ private sentMessages;
2405
+ private sessionIdMap;
2406
+ private commandRouter;
2407
+ private retryState;
2408
+ private socketReconnectAttempts;
2409
+ private maxSocketReconnectAttempts;
2310
2410
  constructor(id?: string);
2311
2411
  protected doStart(): Promise<void>;
2312
2412
  protected doStop(): Promise<void>;
2413
+ protected doHealthCheck(): Promise<boolean>;
2313
2414
  send(message: GatewayMessage): Promise<string>;
2415
+ private postMessage;
2416
+ private postImage;
2417
+ private postFile;
2418
+ private uploadFile;
2314
2419
  getCapabilities(): {
2315
2420
  supportsText: boolean;
2316
2421
  supportsMedia: boolean;
@@ -2324,18 +2429,31 @@ declare class SlackPlugin extends BaseChannelPlugin {
2324
2429
  supportsHtml: boolean;
2325
2430
  supportsDeliveryReceipt: boolean;
2326
2431
  supportsReadReceipt: boolean;
2432
+ supportsSlashCommands: boolean;
2433
+ supportsNativeCommands: boolean;
2434
+ commandPrefix: string;
2327
2435
  maxMessageLength: number;
2328
2436
  maxMediaCount: number;
2329
2437
  };
2330
2438
  private startSocketMode;
2331
2439
  private handleSocketModeEvent;
2440
+ private handleReactionEvent;
2441
+ private findSentMessageEntry;
2332
2442
  private handleSlackMessage;
2443
+ private handleCommand;
2444
+ protected getCommandList(): Promise<Array<{
2445
+ name: string;
2446
+ description?: string;
2447
+ agent?: string;
2448
+ model?: string;
2449
+ }>>;
2450
+ private syncBotCommands;
2333
2451
  private toGatewayMessage;
2334
2452
  private parseContent;
2453
+ private downloadFile;
2454
+ private downloadTextFile;
2335
2455
  protected buildSessionId(channelId: string, chatId: string, userId?: string): string;
2336
- private processMessageAndGetSessionId;
2337
2456
  private authTest;
2338
- private postMessage;
2339
2457
  private request;
2340
2458
  }
2341
2459
 
@@ -2349,6 +2467,11 @@ interface FeishuBotConfig {
2349
2467
  path: string;
2350
2468
  port?: number;
2351
2469
  };
2470
+ retry?: {
2471
+ maxAttempts?: number;
2472
+ initialDelay?: number;
2473
+ maxDelay?: number;
2474
+ };
2352
2475
  }
2353
2476
  interface FeishuMessageEvent {
2354
2477
  type: string;
@@ -2385,9 +2508,12 @@ declare class FeishuPlugin extends BaseChannelPlugin {
2385
2508
  private tenantAccessToken;
2386
2509
  private tokenExpireTime;
2387
2510
  private abortController;
2511
+ private processedMessageIds;
2512
+ private retryState;
2388
2513
  constructor(id?: string);
2389
2514
  protected doStart(): Promise<void>;
2390
2515
  protected doStop(): Promise<void>;
2516
+ protected doHealthCheck(): Promise<boolean>;
2391
2517
  send(message: GatewayMessage): Promise<string>;
2392
2518
  getCapabilities(): {
2393
2519
  supportsText: boolean;
@@ -2402,18 +2528,26 @@ declare class FeishuPlugin extends BaseChannelPlugin {
2402
2528
  supportsHtml: boolean;
2403
2529
  supportsDeliveryReceipt: boolean;
2404
2530
  supportsReadReceipt: boolean;
2531
+ supportsSlashCommands: boolean;
2532
+ supportsNativeCommands: boolean;
2533
+ commandPrefix: string;
2405
2534
  maxMessageLength: number;
2406
2535
  maxMediaCount: number;
2407
2536
  };
2408
2537
  handleWebhookEvent(event: FeishuMessageEvent): Promise<void>;
2538
+ private refreshTenantAccessToken;
2539
+ private ensureTokenValid;
2540
+ private sendMessage;
2541
+ private sendImage;
2542
+ private sendFile;
2543
+ private uploadImage;
2544
+ private uploadFile;
2409
2545
  private toGatewayMessage;
2410
2546
  private parseContent;
2547
+ private downloadImage;
2548
+ private downloadFile;
2411
2549
  private extractPostContent;
2412
2550
  protected buildSessionId(channelId: string, chatId: string, userId?: string): string;
2413
- private processMessageAndGetSessionId;
2414
- private refreshTenantAccessToken;
2415
- private ensureTokenValid;
2416
- private sendMessage;
2417
2551
  }
2418
2552
 
2419
2553
  interface WeChatBotConfig {
@@ -2428,6 +2562,11 @@ interface WeChatBotConfig {
2428
2562
  path: string;
2429
2563
  port?: number;
2430
2564
  };
2565
+ retry?: {
2566
+ maxAttempts?: number;
2567
+ initialDelay?: number;
2568
+ maxDelay?: number;
2569
+ };
2431
2570
  }
2432
2571
  declare class WeChatPlugin extends BaseChannelPlugin {
2433
2572
  readonly id: string;
@@ -2437,9 +2576,12 @@ declare class WeChatPlugin extends BaseChannelPlugin {
2437
2576
  private accessToken;
2438
2577
  private tokenExpireTime;
2439
2578
  private abortController;
2579
+ private processedMessageIds;
2580
+ private retryState;
2440
2581
  constructor(id?: string);
2441
2582
  protected doStart(): Promise<void>;
2442
2583
  protected doStop(): Promise<void>;
2584
+ protected doHealthCheck(): Promise<boolean>;
2443
2585
  send(message: GatewayMessage): Promise<string>;
2444
2586
  getCapabilities(): {
2445
2587
  supportsText: boolean;
@@ -2454,19 +2596,23 @@ declare class WeChatPlugin extends BaseChannelPlugin {
2454
2596
  supportsHtml: boolean;
2455
2597
  supportsDeliveryReceipt: boolean;
2456
2598
  supportsReadReceipt: boolean;
2599
+ supportsSlashCommands: boolean;
2600
+ supportsNativeCommands: boolean;
2601
+ commandPrefix: string;
2457
2602
  maxMessageLength: number;
2458
2603
  maxMediaCount: number;
2459
2604
  };
2460
2605
  handleWebhookMessage(xmlData: string): Promise<void>;
2461
- private parseXmlMessage;
2462
- private toGatewayMessage;
2463
- private parseContent;
2464
- protected buildSessionId(channelId: string, chatId: string, userId?: string): string;
2465
- private processMessageAndGetSessionId;
2466
2606
  private refreshAccessToken;
2467
2607
  private ensureTokenValid;
2468
2608
  private sendOfficialMessage;
2469
2609
  private sendWeComMessage;
2610
+ private sendImage;
2611
+ private uploadImage;
2612
+ private parseXmlMessage;
2613
+ private toGatewayMessage;
2614
+ private parseContent;
2615
+ protected buildSessionId(channelId: string, chatId: string, userId?: string): string;
2470
2616
  }
2471
2617
 
2472
2618
  interface WebChatConfig {
@@ -2488,11 +2634,13 @@ declare class WebChatPlugin extends BaseChannelPlugin {
2488
2634
  private connections;
2489
2635
  private heartbeatInterval;
2490
2636
  private abortController;
2637
+ private processedMessageIds;
2638
+ private sentMessages;
2491
2639
  private wsServer;
2492
2640
  constructor(id?: string);
2493
2641
  protected doStart(): Promise<void>;
2494
- private startWebSocketServer;
2495
2642
  protected doStop(): Promise<void>;
2643
+ protected doHealthCheck(): Promise<boolean>;
2496
2644
  send(message: GatewayMessage): Promise<string>;
2497
2645
  getCapabilities(): {
2498
2646
  supportsText: boolean;
@@ -2507,20 +2655,24 @@ declare class WebChatPlugin extends BaseChannelPlugin {
2507
2655
  supportsHtml: boolean;
2508
2656
  supportsDeliveryReceipt: boolean;
2509
2657
  supportsReadReceipt: boolean;
2658
+ supportsSlashCommands: boolean;
2659
+ supportsNativeCommands: boolean;
2660
+ commandPrefix: string;
2510
2661
  maxMessageLength: number;
2511
2662
  maxMediaCount: number;
2512
2663
  };
2513
2664
  pushEvent(sessionId: string, event: SessionEventPayload): void;
2514
2665
  getWebSocketServer(): GatewayWebSocketServer | null;
2515
2666
  handleConnection(ws: WebSocket, userId: string): Promise<void>;
2667
+ getConnectionCount(): number;
2668
+ getWsServerConnectionCount(): number;
2669
+ private startWebSocketServer;
2516
2670
  private handleWebSocketMessage;
2517
2671
  private handleMessageAcknowledgment;
2518
2672
  private handleChatMessage;
2519
2673
  private startHeartbeat;
2520
2674
  private findConnectionByUser;
2521
2675
  private generateConnectionId;
2522
- getConnectionCount(): number;
2523
- getWsServerConnectionCount(): number;
2524
2676
  }
2525
2677
 
2526
2678
  interface SignalBotConfig {
@@ -2532,6 +2684,15 @@ interface SignalBotConfig {
2532
2684
  path: string;
2533
2685
  port?: number;
2534
2686
  };
2687
+ polling?: {
2688
+ enabled: boolean;
2689
+ interval?: number;
2690
+ };
2691
+ retry?: {
2692
+ maxAttempts?: number;
2693
+ initialDelay?: number;
2694
+ maxDelay?: number;
2695
+ };
2535
2696
  }
2536
2697
  declare class SignalPlugin extends BaseChannelPlugin {
2537
2698
  readonly id: string;
@@ -2541,9 +2702,13 @@ declare class SignalPlugin extends BaseChannelPlugin {
2541
2702
  private abortController;
2542
2703
  private lastTimestamp;
2543
2704
  private pollingIntervalMs;
2705
+ private processedMessageIds;
2706
+ private sentMessages;
2707
+ private retryState;
2544
2708
  constructor(id?: string);
2545
2709
  protected doStart(): Promise<void>;
2546
2710
  protected doStop(): Promise<void>;
2711
+ protected doHealthCheck(): Promise<boolean>;
2547
2712
  send(message: GatewayMessage): Promise<string>;
2548
2713
  getCapabilities(): {
2549
2714
  supportsText: boolean;
@@ -2558,6 +2723,9 @@ declare class SignalPlugin extends BaseChannelPlugin {
2558
2723
  supportsHtml: boolean;
2559
2724
  supportsDeliveryReceipt: boolean;
2560
2725
  supportsReadReceipt: boolean;
2726
+ supportsSlashCommands: boolean;
2727
+ supportsNativeCommands: boolean;
2728
+ commandPrefix: string;
2561
2729
  maxMessageLength: number;
2562
2730
  maxMediaCount: number;
2563
2731
  };
@@ -2568,7 +2736,6 @@ declare class SignalPlugin extends BaseChannelPlugin {
2568
2736
  private toGatewayMessage;
2569
2737
  private parseContent;
2570
2738
  protected buildSessionId(channelId: string, chatId: string, userId?: string): string;
2571
- private processMessageAndGetSessionId;
2572
2739
  private getAccountInfo;
2573
2740
  private receiveMessages;
2574
2741
  private sendMessage;
@@ -2580,6 +2747,11 @@ interface NostrBotConfig {
2580
2747
  privateKey?: string;
2581
2748
  publicKey?: string;
2582
2749
  timeout: number;
2750
+ retry?: {
2751
+ maxAttempts?: number;
2752
+ initialDelay?: number;
2753
+ maxDelay?: number;
2754
+ };
2583
2755
  }
2584
2756
  declare class NostrPlugin extends BaseChannelPlugin {
2585
2757
  readonly id: string;
@@ -2588,13 +2760,16 @@ declare class NostrPlugin extends BaseChannelPlugin {
2588
2760
  private botConfig;
2589
2761
  private relayConnections;
2590
2762
  private subscriptions;
2591
- private eventHandlers;
2592
2763
  private abortController;
2764
+ private processedEventIds;
2765
+ private sentMessages;
2593
2766
  private relayReconnectAttempts;
2594
2767
  private maxReconnectAttempts;
2768
+ private retryState;
2595
2769
  constructor(id?: string);
2596
2770
  protected doStart(): Promise<void>;
2597
2771
  protected doStop(): Promise<void>;
2772
+ protected doHealthCheck(): Promise<boolean>;
2598
2773
  send(message: GatewayMessage): Promise<string>;
2599
2774
  getCapabilities(): {
2600
2775
  supportsText: boolean;
@@ -2609,6 +2784,9 @@ declare class NostrPlugin extends BaseChannelPlugin {
2609
2784
  supportsHtml: boolean;
2610
2785
  supportsDeliveryReceipt: boolean;
2611
2786
  supportsReadReceipt: boolean;
2787
+ supportsSlashCommands: boolean;
2788
+ supportsNativeCommands: boolean;
2789
+ commandPrefix: string;
2612
2790
  maxMessageLength: number;
2613
2791
  maxMediaCount: number;
2614
2792
  };
@@ -2619,8 +2797,8 @@ declare class NostrPlugin extends BaseChannelPlugin {
2619
2797
  private parseContent;
2620
2798
  private findReplyTo;
2621
2799
  protected buildSessionId(channelId: string, chatId: string, userId?: string): string;
2622
- private processMessageAndGetSessionId;
2623
2800
  private subscribeToRelay;
2801
+ private unsubscribeFromRelay;
2624
2802
  private createEvent;
2625
2803
  private computeEventId;
2626
2804
  private signEvent;
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import {Q,v,R,_ as _$1,$}from'./chunks/chunk-SPCU5JL2.mjs';export{p as AgentRegistryConfigSchema,k as AgentSyncConfigSchema,j as AuthConfigSchema,ha as BaseChannelPlugin,h as ChannelConfigSchema,fa as ChannelPluginLoader,ra as ChannelPluginRegistry,o as CircuitBreakerConfigSchema,m as ConnectionPoolConfigSchema,_ as DEFAULT_AGENT_REGISTRY_CONFIG,aa as DEFAULT_CONNECTION_POOL_CONFIG,$ as DEFAULT_SYNC_CONFIG,Z as DEFAULT_TOKEN_AUTH_CONFIG,Y as DEFAULT_WEBSOCKET_SERVER_CONFIG,ja as DiscordPlugin,c as FeishuChannelConfigSchema,la as FeishuPlugin,l as GatewayClusterConfigSchema,s as GatewayConfigSchema,ta as GatewayServer,r as GatewayServerConfigSchema,ea as GatewaySessionManager,i as HTTPSConfigSchema,n as MessageQueueConfigSchema,ba as MessageRouter,ca as MessageStore,g as NostrChannelConfigSchema,pa as NostrPlugin,q as SessionConfigSchema,da as SessionStore,f as SignalChannelConfigSchema,oa as SignalPlugin,b as SlackChannelConfigSchema,ka as SlackPlugin,a as TelegramChannelConfigSchema,ia as TelegramPlugin,d as WeChatChannelConfigSchema,ma as WeChatPlugin,e as WebChatChannelConfigSchema,na as WebChatPlugin,y as clearConfigCache,U as convertToGatewayMessage,qa as createChannelPlugin,W as createDefaultSessionState,S as createGatewayMessage,T as createTextMessage,V as generateSessionId,X as generateSubscriptionId,N as getAgentAdapter,F as getAgentRegistryConfig,I as getAuthConfig,B as getChannelConfig,E as getCircuitBreakerConfig,J as getClusterConfig,w as getConfig,z as getConfigDirectory,C as getConnectionPoolConfig,K as getDefaultAgent,H as getHTTPSConfig,L as getLogLevel,D as getMessageQueueConfig,A as getServerConfig,G as getSessionConfig,sa as getSupportedPlatforms,O as hasAgentAdapter,x as isConfigLoaded,v as loadConfig,u as parsePartialConfig,P as requireAgentAdapter,M as setAgentAdapter,t as validateConfig}from'./chunks/chunk-SPCU5JL2.mjs';import {b,a as a$1}from'./chunks/chunk-RGPJTZUB.mjs';import {Fetch}from'@easbot/utils';var a=b.create({service:"gateway"}),C=class c extends Error{constructor(t,n,s){super(t);a$1(this,"type");a$1(this,"stage");a$1(this,"cause");this.type="GatewayInitializationError",this.stage=n,this.cause=s,Error.captureStackTrace&&Error.captureStackTrace(this,c);}};function _(c){let e=["ECONNRESET","EPIPE","ETIMEDOUT","ECONNREFUSED","ENOTFOUND","EHOSTUNREACH","ENETUNREACH"],t=c.code;return e.includes(t??"")}function tt(){process.on("unhandledRejection",c=>{let e=c instanceof Error?c:new Error(String(c));_(e)||a.error("unhandled promise rejection",{error:e.message,stack:e.stack});}),process.on("uncaughtException",c=>{_(c)||(a.error("uncaught exception",{error:c.message,stack:c.stack}),process.exit(1));});}var x;(it=>{let c={initialized:false,initPromise:null,directory:".easbot",config:null},e,t={server:null,config:null,initialized:false,status:"stopped"};async function n(u={}){if(c.initialized)return;if(c.initPromise)return c.initPromise;let d={directory:u.directory??".easbot",printLogs:u.printLogs??false,logLevel:u.logLevel??"INFO"};return c.directory=d.directory,c.initPromise=(async()=>{let w=Q();try{await b.init({logDir:w.Path.log,print:d.printLogs,level:d.logLevel});}catch(l){throw new C("Failed to initialize log infrastructure","log",l instanceof Error?l:void 0)}try{tt();}catch(l){throw new C("Failed to install unhandled exception handlers","server",l instanceof Error?l:void 0)}Fetch.hasProxyConfigured()&&(Fetch.enableProxy({connectTimeout:1e4,keepAliveTimeout:3e4}),a.info("global proxy enabled",{proxyUrl:Fetch.getProxyUrl()}));try{c.config=await v(d.directory);}catch(l){throw new C("Failed to load gateway configuration","config",l instanceof Error?l:void 0)}c.initialized=true,a.info("gateway runtime initialized",{directory:d.directory,logLevel:d.logLevel});})(),c.initPromise}it.init=n;function s(){return c.initialized}it.isInitialized=s;function r(){return e||(e=async()=>{if(!c.initialized)throw new C("Gateway not initialized. Call init() first.","server");let u=R(),d=await v(u.directory);return d.server?.enabled?(t={server:null,config:d,initialized:true,status:"stopped"},a.info("gateway initialized",{port:d.server?.port,hostname:d.server?.hostname}),t):(a.debug("gateway server disabled or not configured"),{server:null,config:d,initialized:true,status:"stopped"})}),e}function i(){return r()()}it.state=i;async function o(){return (await i()).server}it.get=o;async function p(){return (await i()).config?.server?.enabled??false}it.isEnabled=p;async function G(){return (await i()).config}it.config=G;function st(){return t.status}it.getStatus=st;async function F(u){a.debug("Gateway.start: called"),c.initialized||(a.debug("Gateway.start: calling init()"),await n(),a.debug("Gateway.start: init() completed")),a.debug("Gateway.start: calling state()");let d=await i();if(a.debug("Gateway.start: state() completed",{status:d.status}),!(d.config?.server?.enabled??true))throw a.warn("gateway server is disabled"),new Error("Gateway server is disabled");if(t.status==="running"){a.info("gateway server is already running");return}if(t.status==="starting"){a.info("gateway server is starting");return}t.status="starting",a.debug("Gateway.start: status set to starting");try{let l={...d.config?.server,...u};a.debug("gateway start: creating server with config",{port:l.port,hostname:l.hostname,path:l.path,https:l.https?.enabled}),a.debug("Gateway.start: calling createGatewayServer");let I=await q(l);a.debug("Gateway.start: createGatewayServer completed"),a.debug("gateway start: server created, updating state"),t.server=I,t.status="running",t.error=void 0,a.info("gateway server started",{port:l.port,hostname:l.hostname});}catch(l){throw t.status="error",t.error=l instanceof Error?l.message:String(l),a.error("failed to start gateway server",{error:t.error}),l}}it.start=F;async function D(){if(t.status==="stopped"){a.info("gateway server is already stopped");return}if(t.status==="stopping"){a.info("gateway server is stopping");return}t.status="stopping";try{t.server&&await t.server.stop(),t.server=null,t.status="stopped",t.error=void 0,await b.close(),a.info("gateway server stopped");}catch(u){throw t.status="error",t.error=u instanceof Error?u.message:String(u),a.error("failed to stop gateway server",{error:t.error}),u}}it.stop=D;async function rt(u){a.info("restarting gateway server"),(t.status==="running"||t.status==="starting")&&await D(),await F(u);}it.restart=rt;async function at(){let u=R(),d=await v(u.directory);return t.config=d,c.config=d,a.info("gateway config reloaded"),d}it.reloadConfig=at;async function q(u,d){a.debug("createGatewayServer: starting");let{GatewayServer:w}=await import('./chunks/server-D6N6P2NV.mjs');a.debug("createGatewayServer: GatewayServer imported");let l=new w(u);a.debug("createGatewayServer: server instance created"),a.debug("createGatewayServer: created server instance, calling start()");let I=d?.startupTimeout??3e4;if(await(async()=>{let b=new Promise((A,U)=>{setTimeout(()=>{U(new Error(`Gateway server startup timeout after ${I}ms`));},I);});try{a.debug("createGatewayServer: calling server.start()"),await Promise.race([l.start(),b]),a.debug("createGatewayServer: server.start() completed");}catch(A){a.error("gateway server start failed, attempting cleanup",{error:A instanceof Error?A.message:String(A)});try{await l.stop();}catch{}throw A}})(),d?.onStarted)try{await d.onStarted(l);}catch(b){a.warn("server started but onStarted callback failed",{error:b instanceof Error?b.message:String(b)});}return a.info("gateway server created and started",{port:u.port,hostname:u.hostname}),l}it.createGatewayServer=q;})(x||(x={}));var f=b.create({service:"gateway:agent-registry"}),T=class{constructor(e={}){a$1(this,"config");a$1(this,"agents",new Map);a$1(this,"heartbeatTimer",null);a$1(this,"roundRobinIndex",0);this.config={..._$1,...e};}async register(e){if(this.agents.has(e.id))throw new Error(`Agent with ID "${e.id}" is already registered`);if(this.agents.size>=this.config.maxAgents)throw new Error(`Maximum number of agents (${this.config.maxAgents}) reached`);let t=Date.now(),n={...e,status:"healthy",lastHeartbeat:t,registeredAt:t,connectionCount:0};this.agents.set(e.id,n),f.info("Agent registered",{agentId:e.id,name:e.name,capabilities:e.capabilities,address:e.address,totalAgents:this.agents.size});}async deregister(e){let t=this.agents.get(e);if(!t){f.warn("Attempted to deregister unknown agent",{agentId:e});return}this.agents.delete(e),f.info("Agent deregistered",{agentId:e,name:t.name,totalAgents:this.agents.size});}async heartbeat(e,t){let n=this.agents.get(e);if(!n)throw f.warn("Heartbeat received from unknown agent",{agentId:e}),new Error(`Agent "${e}" is not registered`);n.lastHeartbeat=Date.now(),n.status="healthy",t&&(n.metadata={...n.metadata,...t}),f.debug("Agent heartbeat received",{agentId:e,lastHeartbeat:n.lastHeartbeat});}getAgent(e){return this.agents.get(e)}getAllAgents(){return Array.from(this.agents.values())}getHealthyAgents(){return this.getAllAgents().filter(e=>e.status==="healthy")}selectAgent(e){let{strategy:t,capabilities:n,healthyOnly:s=true}=e,r=s?this.getHealthyAgents():this.getAllAgents();if(n&&n.length>0&&(r=r.filter(o=>n.every(p=>o.capabilities.includes(p)))),r.length===0){f.warn("No agents available for selection",{strategy:t,capabilities:n,healthyOnly:s,totalAgents:this.agents.size});return}let i;switch(t){case "random":i=this.selectRandom(r);break;case "round-robin":i=this.selectRoundRobin(r);break;case "least-connections":i=this.selectLeastConnections(r);break;default:i=this.selectRandom(r);}return f.debug("Agent selected",{agentId:i.id,strategy:t,connectionCount:i.connectionCount}),i}incrementConnection(e){let t=this.agents.get(e);t&&(t.connectionCount++,f.debug("Agent connection incremented",{agentId:e,connectionCount:t.connectionCount}));}decrementConnection(e){let t=this.agents.get(e);t&&t.connectionCount>0&&(t.connectionCount--,f.debug("Agent connection decremented",{agentId:e,connectionCount:t.connectionCount}));}startHeartbeatCheck(){if(this.heartbeatTimer){f.warn("Heartbeat check is already running");return}f.info("Starting heartbeat check",{interval:this.config.heartbeatCheckInterval,timeout:this.config.heartbeatTimeout}),this.heartbeatTimer=setInterval(()=>this.checkHeartbeats(),this.config.heartbeatCheckInterval);}stopHeartbeatCheck(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null,f.info("Heartbeat check stopped"));}checkHeartbeats(){let e=Date.now(),{heartbeatTimeout:t,autoDeregisterTimeout:n}=this.config,s=[],r=0;for(let[i,o]of this.agents.entries()){let p=e-o.lastHeartbeat;if(p>n){s.push(i);continue}p>t&&o.status==="healthy"&&(o.status="unhealthy",r++,f.warn("Agent marked as unhealthy due to heartbeat timeout",{agentId:i,lastHeartbeat:o.lastHeartbeat,timeSinceLastHeartbeat:p,timeout:t}));}for(let i of s){let o=this.agents.get(i);o&&(f.info("Auto-deregistering agent due to timeout",{agentId:i,name:o.name,lastHeartbeat:o.lastHeartbeat,timeout:n}),this.agents.delete(i));}(r>0||s.length>0)&&f.info("Heartbeat check completed",{unhealthyCount:r,deregisteredCount:s.length,totalAgents:this.agents.size});}selectRandom(e){let t=Math.floor(Math.random()*e.length);return e[t]}selectRoundRobin(e){this.roundRobinIndex=this.roundRobinIndex%e.length;let t=e[this.roundRobinIndex];return this.roundRobinIndex++,t}selectLeastConnections(e){return e.reduce((t,n)=>n.connectionCount<t.connectionCount?n:t)}getStats(){let e=this.getAllAgents();return {totalAgents:e.length,healthyAgents:e.filter(t=>t.status==="healthy").length,unhealthyAgents:e.filter(t=>t.status==="unhealthy").length,offlineAgents:e.filter(t=>t.status==="offline").length,totalConnections:e.reduce((t,n)=>t+n.connectionCount,0)}}setAgentStatus(e,t){let n=this.agents.get(e);n&&(n.status=t);}setLastHeartbeat(e,t){let n=this.agents.get(e);n&&(n.lastHeartbeat=t);}};var h=b.create({service:"gateway:agent-sync-manager"}),M=class{constructor(e){a$1(this,"config");a$1(this,"registry");a$1(this,"localNodeId");a$1(this,"localNodeName");a$1(this,"remoteNodes",new Map);a$1(this,"remoteAgents",new Map);a$1(this,"syncTimer",null);a$1(this,"running",false);this.registry=e.registry,this.localNodeId=e.localNodeId,this.localNodeName=e.localNodeName,this.config={...$,...e.config};}async start(){if(this.running){h.warn("Agent sync manager is already running");return}h.info("Starting agent sync manager",{localNodeId:this.localNodeId,mode:this.config.mode,interval:this.config.interval,remoteNodes:this.config.remoteNodes.length});for(let e of this.config.remoteNodes)this.remoteNodes.set(e.id,{info:{id:e.id,name:e.id,address:e.address,port:e.port,lastSyncAt:0,status:"unknown"},lastSyncAt:0,status:"synced"});(this.config.mode==="pull"||this.config.mode==="both")&&(this.syncTimer=setInterval(()=>this.pullFromAllNodes(),this.config.interval)),this.running=true,h.info("Agent sync manager started");}async stop(){if(!this.running){h.warn("Agent sync manager is not running");return}h.info("Stopping agent sync manager"),this.syncTimer&&(clearInterval(this.syncTimer),this.syncTimer=null),this.remoteNodes.clear(),this.remoteAgents.clear(),this.running=false,h.info("Agent sync manager stopped");}async pullFromAllNodes(){for(let[e]of this.remoteNodes.entries())try{await this.pullFromNode(e);}catch(t){h.error("Failed to pull from node",{nodeId:e,error:t.message});}}async pullFromNode(e){let t=this.remoteNodes.get(e);if(!t)throw new Error(`Unknown remote node: ${e}`);t.status="syncing";let n=this.config.remoteNodes.find(s=>s.id===e);if(!n)throw new Error(`Remote node not found in config: ${e}`);h.debug("Pulling agent list from node",{nodeId:e,address:n.address});try{let s=`http://${n.address}:${n.port}/sync/agents`,r=await Fetch.get(s,{headers:{"Content-Type":"application/json","X-Gateway-Node-Id":this.localNodeId}});if(!r.ok)throw new Error(`HTTP ${r.status}`);let i=r.data.agents||[];for(let o of i)o.sourceGatewayId=e,this.remoteAgents.set(o.id,o);return t.lastSyncAt=Date.now(),t.status="synced",t.info.lastSyncAt=t.lastSyncAt,t.info.status="online",delete t.error,h.info("Pulled agent list from node",{nodeId:e,agentCount:i.length,lastSyncAt:t.lastSyncAt}),i}catch(s){throw t.status="error",t.error=s.message,t.info.status="offline",h.error("Failed to pull from node",{nodeId:e,error:s.message}),s}}async pushEvent(e){if(this.config.mode!=="push"&&this.config.mode!=="both")return;let t=[];for(let[n]of this.remoteNodes.entries())t.push(this.pushToNode(n,e));await Promise.allSettled(t);}async pushToNode(e,t){let n=this.config.remoteNodes.find(s=>s.id===e);if(!n){h.warn("Unknown remote node for push",{nodeId:e});return}try{let s=`http://${n.address}:${n.port}/sync/events`,r=await Fetch.post(s,t,{headers:{"Content-Type":"application/json","X-Gateway-Node-Id":this.localNodeId}});if(!r.ok)throw new Error(`HTTP ${r.status}`);h.debug("Pushed event to node",{nodeId:e,eventType:t.type});}catch(s){h.error("Failed to push to node",{nodeId:e,eventType:t.type,error:s.message});}}async handleSyncMessage(e){switch(h.debug("Received sync message",{type:e.type,sourceNodeId:e.sourceNodeId}),e.type){case "agent_list_request":await this.handleAgentListRequest(e);break;case "agent_list_response":await this.handleAgentListResponse(e);break;case "agent_register":await this.handleAgentRegister(e);break;case "agent_deregister":await this.handleAgentDeregister(e);break;case "agent_heartbeat":await this.handleAgentHeartbeat(e);break;case "agent_status_change":await this.handleAgentStatusChange(e);break;default:h.warn("Unknown sync message type",{type:e.type});}}async handleAgentListRequest(e){let t=this.getLocalAgentsForSync(),n={type:"agent_list_response",sourceNodeId:this.localNodeId,timestamp:Date.now(),requestId:e.requestId,agents:t,fullList:true,syncAt:Date.now()};await this.pushToNode(e.sourceNodeId,n);}async handleAgentListResponse(e){for(let t of e.agents)this.remoteAgents.set(t.id,t);h.info("Received agent list response",{sourceNodeId:e.sourceNodeId,agentCount:e.agents.length});}async handleAgentRegister(e){let{agent:t}=e,n=this.remoteAgents.get(t.id);if(n){let s=this.resolveConflict(n,t);this.remoteAgents.set(t.id,s);}else this.remoteAgents.set(t.id,t);h.info("Remote agent registered",{agentId:t.id,name:t.name,sourceNodeId:e.sourceNodeId});}async handleAgentDeregister(e){let{agentId:t}=e;this.remoteAgents.delete(t),h.info("Remote agent deregistered",{agentId:t,sourceNodeId:e.sourceNodeId});}async handleAgentHeartbeat(e){let{agentId:t,heartbeatAt:n,status:s}=e,r=this.remoteAgents.get(t);r&&(r.updatedAt=n,r.status=s),h.debug("Remote agent heartbeat received",{agentId:t,status:s,sourceNodeId:e.sourceNodeId});}async handleAgentStatusChange(e){let{agentId:t,newStatus:n,changedAt:s}=e,r=this.remoteAgents.get(t);r&&(r.status=n,r.updatedAt=s),h.info("Remote agent status changed",{agentId:t,newStatus:n,sourceNodeId:e.sourceNodeId});}resolveConflict(e,t){switch(this.config.conflictResolution){case "latest":return t.updatedAt>e.updatedAt?t:e;case "local":return e;case "remote":return t;default:return t.updatedAt>e.updatedAt?t:e}}getLocalAgentsForSync(){return this.registry.getAllAgents().map(t=>({id:t.id,name:t.name,sourceGatewayId:this.localNodeId,address:t.address,capabilities:t.capabilities,status:t.status,updatedAt:t.lastHeartbeat,metadata:t.metadata}))}getMergedAgentList(){let e=new Map;for(let t of this.getLocalAgentsForSync())e.set(t.id,t);for(let[t,n]of this.remoteAgents.entries())e.has(t)||e.set(t,n);return Array.from(e.values())}getRemoteAgents(){return Array.from(this.remoteAgents.values())}getStats(){let e=this.registry.getAllAgents(),t=0,n=0;for(let r of this.remoteNodes.values())r.info.status==="online"?t++:n++;let s=null;for(let r of this.remoteNodes.values())r.lastSyncAt>(s||0)&&(s=r.lastSyncAt);return {localAgentCount:e.length,remoteAgentCount:this.remoteAgents.size,totalAgentCount:e.length+this.remoteAgents.size,onlineNodeCount:t,offlineNodeCount:n,lastSyncAt:s}}isRunning(){return this.running}addRemoteNode(e){this.remoteNodes.set(e.id,{info:{id:e.id,name:e.id,address:e.address,port:e.port,lastSyncAt:0,status:"unknown"},lastSyncAt:0,status:"synced"}),h.info("Remote node added",{nodeId:e.id,address:e.address});}removeRemoteNode(e){this.remoteNodes.delete(e);for(let[t,n]of this.remoteAgents.entries())n.sourceGatewayId===e&&this.remoteAgents.delete(t);h.info("Remote node removed",{nodeId:e});}};var N=class{constructor(e){a$1(this,"log");a$1(this,"config");a$1(this,"ws",null);a$1(this,"state","disconnected");a$1(this,"subscriptions",new Map);a$1(this,"messageCallbacks",new Set);a$1(this,"agentListCallbacks",new Set);a$1(this,"cachedAgentList",[]);a$1(this,"localAgentList",[]);a$1(this,"reconnectAttempts",0);a$1(this,"reconnectTimer",null);a$1(this,"heartbeatTimer",null);a$1(this,"agentListRequestId",null);a$1(this,"agentListRequestResolve",null);this.config={url:e.url,type:e.type,id:e.id??`client_${Date.now()}_${Math.random().toString(36).substring(2,9)}`,reconnect:{enabled:e.reconnect?.enabled??true,maxAttempts:e.reconnect?.maxAttempts??5,delay:e.reconnect?.delay??3e3},heartbeat:{enabled:e.heartbeat?.enabled??true,interval:e.heartbeat?.interval??3e4}},this.log=b.create({service:`gateway:client:${this.config.id}`});}async connect(){if(this.state==="connected"||this.state==="connecting"){this.log.warn("already connected or connecting");return}return this.state="connecting",this.log.info("connecting to gateway",{url:this.config.url}),new Promise((e,t)=>{try{this.ws=new WebSocket(this.config.url),this.ws.onopen=()=>{this.state="connected",this.reconnectAttempts=0,this.log.info("connected to gateway"),this.config.heartbeat.enabled&&this.startHeartbeat(),this.resubscribeAll(),e();},this.ws.onmessage=n=>{this.handleMessage(n.data);},this.ws.onclose=n=>{this.handleClose(n.code,n.reason);},this.ws.onerror=n=>{this.log.error("WebSocket error",{error:String(n)}),this.state==="connecting"&&t(new Error("Connection failed"));};}catch(n){this.state="disconnected",t(n);}})}async disconnect(){this.state!=="disconnected"&&(this.log.info("disconnecting from gateway"),this.stopHeartbeat(),this.stopReconnect(),this.ws&&(this.ws.close(1e3,"Client disconnect"),this.ws=null),this.state="disconnected",this.log.info("disconnected from gateway"));}async subscribe(e,t){if(this.state!=="connected")throw new Error("Not connected to gateway");this.sendData({type:"subscribe",sessionId:e,backendSessionId:t,clientId:this.config.id}),this.log.info("subscribed to session",{sessionId:e,backendSessionId:t});}async unsubscribe(e){if(this.state!=="connected")throw new Error("Not connected to gateway");this.sendData({type:"unsubscribe",sessionId:e,clientId:this.config.id}),this.subscriptions.delete(e),this.log.info("unsubscribed from session",{sessionId:e});}async send(e){if(this.state!=="connected")throw new Error("Not connected to gateway");this.sendData({type:"message",message:e});}onMessage(e){this.messageCallbacks.add(e);}offMessage(e){this.messageCallbacks.delete(e);}getState(){return this.state}getId(){return this.config.id}getSubscriptions(){return [...this.subscriptions.keys()]}setLocalAgents(e){this.localAgentList=e,this.updateMergedAgentList();}async fetchAgentList(){if(this.state!=="connected")throw new Error("Not connected to gateway");return new Promise((e,t)=>{let n=`req_${Date.now()}_${Math.random().toString(36).substring(2,9)}`;this.agentListRequestId=n,this.agentListRequestResolve=e,this.sendData({type:"agent_list_request",requestId:n}),setTimeout(()=>{this.agentListRequestId===n&&(this.agentListRequestId=null,this.agentListRequestResolve=null,t(new Error("Agent list request timeout")));},1e4);})}getConnectableAgents(e={}){let{localAgents:t,preferLocal:n=true,filterOffline:s=true}=e,r=t||this.localAgentList,i=this.cachedAgentList,o=this.mergeAgentLists(r,i,n);return s?o.filter(p=>p.status!=="offline"):o}onAgentListChange(e){this.agentListCallbacks.add(e);}offAgentListChange(e){this.agentListCallbacks.delete(e);}mergeAgentLists(e,t,n){let s=new Map,r=n?t:e,i=n?e:t;for(let o of r)s.set(o.id,o);for(let o of i)s.set(o.id,o);return Array.from(s.values())}updateMergedAgentList(){let e=this.getConnectableAgents({filterOffline:false});for(let t of this.agentListCallbacks)try{t(e);}catch(n){this.log.error("agent list callback error",{error:String(n)});}}handleAgentListResponse(e,t){t&&this.agentListRequestId===t&&this.agentListRequestResolve&&(this.agentListRequestId=null,this.agentListRequestResolve(e),this.agentListRequestResolve=null),this.cachedAgentList=e,this.updateMergedAgentList(),this.log.debug("received agent list",{count:e.length});}sendData(e){if(!this.ws||this.ws.readyState!==WebSocket.OPEN){this.log.warn("WebSocket not ready, cannot send");return}this.ws.send(JSON.stringify(e));}handleMessage(e){try{let t=JSON.parse(e);switch(t.type){case "message":this.handleGatewayMessage(t.message);break;case "subscribed":this.subscriptions.set(t.sessionId,t.subscription);break;case "unsubscribed":this.subscriptions.delete(t.sessionId);break;case "pong":break;case "agent_list_response":this.handleAgentListResponse(t.agents||[],t.requestId);break;case "agent_update":this.handleAgentUpdate(t.agent,t.action);break;default:this.log.debug("unknown message type",{type:t.type});}}catch(t){this.log.error("failed to parse message",{error:String(t)});}}handleAgentUpdate(e,t){switch(t){case "add":case "update":{let n=this.cachedAgentList.findIndex(s=>s.id===e.id);n>=0?this.cachedAgentList[n]=e:this.cachedAgentList.push(e);break}case "remove":this.cachedAgentList=this.cachedAgentList.filter(n=>n.id!==e.id);break}this.updateMergedAgentList();}handleGatewayMessage(e){for(let t of this.messageCallbacks)try{t(e);}catch(n){this.log.error("message callback error",{error:String(n)});}}handleClose(e,t){this.log.info("connection closed",{code:e,reason:t}),this.state="disconnected",this.ws=null,this.stopHeartbeat(),this.config.reconnect.enabled&&this.scheduleReconnect();}scheduleReconnect(){if(this.reconnectAttempts>=this.config.reconnect.maxAttempts){this.log.error("max reconnect attempts reached");return}this.reconnectAttempts++,this.state="reconnecting";let e=this.config.reconnect.delay*this.reconnectAttempts;this.log.info("scheduling reconnect",{attempt:this.reconnectAttempts,delay:e}),this.reconnectTimer=setTimeout(async()=>{try{await this.connect();}catch(t){this.log.error("reconnect failed",{error:String(t)});}},e);}stopReconnect(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null);}startHeartbeat(){this.heartbeatTimer=setInterval(()=>{this.state==="connected"&&this.sendData({type:"ping"});},this.config.heartbeat.interval);}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null);}async resubscribeAll(){for(let e of this.subscriptions.keys())try{await this.subscribe(e);}catch(t){this.log.error("failed to resubscribe",{sessionId:e,error:String(t)});}}getHttpUrl(){return this.config.url.replace(/^ws:/,"http:").replace(/^wss:/,"https:").replace(/\/$/,"")}async httpRequest(e,t={}){let n=`${this.getHttpUrl()}${e}`,s=t.method||"GET",{signal:r,...i}=t,o=await Fetch.request(n,{method:s,...i,headers:{"Content-Type":"application/json",...i.headers},...r?{signal:r}:{}});if(!o.ok){let p=o.data,G=typeof p=="object"?JSON.stringify(p):String(p||"Unknown error");throw new Error(`HTTP ${o.status}: ${G}`)}return o.data}async healthCheck(){return this.httpRequest("/health")}async getStatus(){return this.httpRequest("/status")}async listChannels(){return this.httpRequest("/channels")}async listContacts(e,t={}){let n=new URLSearchParams({platform:e,limit:String(t.limit??100),offset:String(t.offset??0)});return this.httpRequest(`/contacts?${n}`)}async getContact(e){return this.httpRequest(`/contacts/${encodeURIComponent(e)}`)}async getSession(e){return this.httpRequest(`/sessions/${encodeURIComponent(e)}`)}async createSession(e){return this.httpRequest("/session",{method:"POST",body:JSON.stringify(e??{})})}async getOrCreateSession(e){if(e.sessionId)try{return await this.getSession(e.sessionId),{sessionId:e.sessionId,created:!1}}catch{this.log.debug("session not found, will create new",{sessionId:e.sessionId});}if(e.platform||e.channel||e.userId){let n=e.chatId??e.userId;try{let i=await this.listSessions({platform:e.platform,channelId:e.channel,userId:e.userId,chatId:n});if(i.sessions.length>0){let o=i.sessions[0];if(o)return this.log.info("found existing session",{sessionId:o.id,channel:e.channel,userId:e.userId,chatId:n}),{sessionId:o.id,created:!1}}}catch(i){this.log.warn("failed to search sessions",{error:String(i)});}let s={channel:{platform:e.platform,channelId:e.channel,userId:e.userId,chatId:n}},r=await this.createSession(s);return this.log.info("session created",{sessionId:r.id,params:s}),{sessionId:r.id,created:true}}let t=await this.createSession({channel:{platform:"api",channelId:"default"}});return this.log.info("session created (default)",{sessionId:t.id}),{sessionId:t.id,created:true}}async listSessions(e={}){let t=new URLSearchParams;return t.set("limit",String(e.limit??100)),t.set("offset",String(e.offset??0)),e.platform&&t.set("platform",e.platform),e.channelId&&t.set("channelId",e.channelId),e.userId&&t.set("userId",e.userId),e.chatId&&t.set("chatId",e.chatId),this.httpRequest(`/sessions?${t}`)}async sendMessage(e,t){return this.httpRequest(`/session/${encodeURIComponent(e)}/prompt`,{method:"POST",body:JSON.stringify(t)})}};async function Jt(c){let{Log:e}=await import('./chunks/log-FQII2FT4.mjs'),t=false;await e.init({logDir:c.logDir??process.env.EASBOT_LOG_PATH??process.cwd(),print:c.print??false,dev:c.dev??t,level:c.level??("INFO")});}
1
+ import {Q,v,R,_ as _$1,$}from'./chunks/chunk-OBFSXYWV.mjs';export{p as AgentRegistryConfigSchema,k as AgentSyncConfigSchema,j as AuthConfigSchema,ha as BaseChannelPlugin,h as ChannelConfigSchema,fa as ChannelPluginLoader,ra as ChannelPluginRegistry,o as CircuitBreakerConfigSchema,m as ConnectionPoolConfigSchema,_ as DEFAULT_AGENT_REGISTRY_CONFIG,aa as DEFAULT_CONNECTION_POOL_CONFIG,$ as DEFAULT_SYNC_CONFIG,Z as DEFAULT_TOKEN_AUTH_CONFIG,Y as DEFAULT_WEBSOCKET_SERVER_CONFIG,ja as DiscordPlugin,c as FeishuChannelConfigSchema,la as FeishuPlugin,l as GatewayClusterConfigSchema,s as GatewayConfigSchema,ta as GatewayServer,r as GatewayServerConfigSchema,ea as GatewaySessionManager,i as HTTPSConfigSchema,n as MessageQueueConfigSchema,ba as MessageRouter,ca as MessageStore,g as NostrChannelConfigSchema,pa as NostrPlugin,q as SessionConfigSchema,da as SessionStore,f as SignalChannelConfigSchema,oa as SignalPlugin,b as SlackChannelConfigSchema,ka as SlackPlugin,a as TelegramChannelConfigSchema,ia as TelegramPlugin,d as WeChatChannelConfigSchema,ma as WeChatPlugin,e as WebChatChannelConfigSchema,na as WebChatPlugin,y as clearConfigCache,U as convertToGatewayMessage,qa as createChannelPlugin,W as createDefaultSessionState,S as createGatewayMessage,T as createTextMessage,V as generateSessionId,X as generateSubscriptionId,N as getAgentAdapter,F as getAgentRegistryConfig,I as getAuthConfig,B as getChannelConfig,E as getCircuitBreakerConfig,J as getClusterConfig,w as getConfig,z as getConfigDirectory,C as getConnectionPoolConfig,K as getDefaultAgent,H as getHTTPSConfig,L as getLogLevel,D as getMessageQueueConfig,A as getServerConfig,G as getSessionConfig,sa as getSupportedPlatforms,O as hasAgentAdapter,x as isConfigLoaded,v as loadConfig,u as parsePartialConfig,P as requireAgentAdapter,M as setAgentAdapter,t as validateConfig}from'./chunks/chunk-OBFSXYWV.mjs';import {b,a as a$1}from'./chunks/chunk-RGPJTZUB.mjs';import {Fetch}from'@easbot/utils';var a=b.create({service:"gateway"}),C=class c extends Error{constructor(t,n,s){super(t);a$1(this,"type");a$1(this,"stage");a$1(this,"cause");this.type="GatewayInitializationError",this.stage=n,this.cause=s,Error.captureStackTrace&&Error.captureStackTrace(this,c);}};function _(c){let e=["ECONNRESET","EPIPE","ETIMEDOUT","ECONNREFUSED","ENOTFOUND","EHOSTUNREACH","ENETUNREACH"],t=c.code;return e.includes(t??"")}function tt(){process.on("unhandledRejection",c=>{let e=c instanceof Error?c:new Error(String(c));_(e)||a.error("unhandled promise rejection",{error:e.message,stack:e.stack});}),process.on("uncaughtException",c=>{_(c)||(a.error("uncaught exception",{error:c.message,stack:c.stack}),process.exit(1));});}var x;(it=>{let c={initialized:false,initPromise:null,directory:".easbot",config:null},e,t={server:null,config:null,initialized:false,status:"stopped"};async function n(u={}){if(c.initialized)return;if(c.initPromise)return c.initPromise;let d={directory:u.directory??".easbot",printLogs:u.printLogs??false,logLevel:u.logLevel??"INFO"};return c.directory=d.directory,c.initPromise=(async()=>{let w=Q();try{await b.init({logDir:w.Path.log,print:d.printLogs,level:d.logLevel});}catch(l){throw new C("Failed to initialize log infrastructure","log",l instanceof Error?l:void 0)}try{tt();}catch(l){throw new C("Failed to install unhandled exception handlers","server",l instanceof Error?l:void 0)}Fetch.hasProxyConfigured()&&(Fetch.enableProxy({connectTimeout:1e4,keepAliveTimeout:3e4}),a.info("global proxy enabled",{proxyUrl:Fetch.getProxyUrl()}));try{c.config=await v(d.directory);}catch(l){throw new C("Failed to load gateway configuration","config",l instanceof Error?l:void 0)}c.initialized=true,a.info("gateway runtime initialized",{directory:d.directory,logLevel:d.logLevel});})(),c.initPromise}it.init=n;function s(){return c.initialized}it.isInitialized=s;function r(){return e||(e=async()=>{if(!c.initialized)throw new C("Gateway not initialized. Call init() first.","server");let u=R(),d=await v(u.directory);return d.server?.enabled?(t={server:null,config:d,initialized:true,status:"stopped"},a.info("gateway initialized",{port:d.server?.port,hostname:d.server?.hostname}),t):(a.debug("gateway server disabled or not configured"),{server:null,config:d,initialized:true,status:"stopped"})}),e}function i(){return r()()}it.state=i;async function o(){return (await i()).server}it.get=o;async function p(){return (await i()).config?.server?.enabled??false}it.isEnabled=p;async function G(){return (await i()).config}it.config=G;function st(){return t.status}it.getStatus=st;async function F(u){a.debug("Gateway.start: called"),c.initialized||(a.debug("Gateway.start: calling init()"),await n(),a.debug("Gateway.start: init() completed")),a.debug("Gateway.start: calling state()");let d=await i();if(a.debug("Gateway.start: state() completed",{status:d.status}),!(d.config?.server?.enabled??true))throw a.warn("gateway server is disabled"),new Error("Gateway server is disabled");if(t.status==="running"){a.info("gateway server is already running");return}if(t.status==="starting"){a.info("gateway server is starting");return}t.status="starting",a.debug("Gateway.start: status set to starting");try{let l={...d.config?.server,...u};a.debug("gateway start: creating server with config",{port:l.port,hostname:l.hostname,path:l.path,https:l.https?.enabled}),a.debug("Gateway.start: calling createGatewayServer");let I=await q(l);a.debug("Gateway.start: createGatewayServer completed"),a.debug("gateway start: server created, updating state"),t.server=I,t.status="running",t.error=void 0,a.info("gateway server started",{port:l.port,hostname:l.hostname});}catch(l){throw t.status="error",t.error=l instanceof Error?l.message:String(l),a.error("failed to start gateway server",{error:t.error}),l}}it.start=F;async function D(){if(t.status==="stopped"){a.info("gateway server is already stopped");return}if(t.status==="stopping"){a.info("gateway server is stopping");return}t.status="stopping";try{t.server&&await t.server.stop(),t.server=null,t.status="stopped",t.error=void 0,await b.close(),a.info("gateway server stopped");}catch(u){throw t.status="error",t.error=u instanceof Error?u.message:String(u),a.error("failed to stop gateway server",{error:t.error}),u}}it.stop=D;async function rt(u){a.info("restarting gateway server"),(t.status==="running"||t.status==="starting")&&await D(),await F(u);}it.restart=rt;async function at(){let u=R(),d=await v(u.directory);return t.config=d,c.config=d,a.info("gateway config reloaded"),d}it.reloadConfig=at;async function q(u,d){a.debug("createGatewayServer: starting");let{GatewayServer:w}=await import('./chunks/server-54GY5XXD.mjs');a.debug("createGatewayServer: GatewayServer imported");let l=new w(u);a.debug("createGatewayServer: server instance created"),a.debug("createGatewayServer: created server instance, calling start()");let I=d?.startupTimeout??3e4;if(await(async()=>{let b=new Promise((A,U)=>{setTimeout(()=>{U(new Error(`Gateway server startup timeout after ${I}ms`));},I);});try{a.debug("createGatewayServer: calling server.start()"),await Promise.race([l.start(),b]),a.debug("createGatewayServer: server.start() completed");}catch(A){a.error("gateway server start failed, attempting cleanup",{error:A instanceof Error?A.message:String(A)});try{await l.stop();}catch{}throw A}})(),d?.onStarted)try{await d.onStarted(l);}catch(b){a.warn("server started but onStarted callback failed",{error:b instanceof Error?b.message:String(b)});}return a.info("gateway server created and started",{port:u.port,hostname:u.hostname}),l}it.createGatewayServer=q;})(x||(x={}));var f=b.create({service:"gateway:agent-registry"}),T=class{constructor(e={}){a$1(this,"config");a$1(this,"agents",new Map);a$1(this,"heartbeatTimer",null);a$1(this,"roundRobinIndex",0);this.config={..._$1,...e};}async register(e){if(this.agents.has(e.id))throw new Error(`Agent with ID "${e.id}" is already registered`);if(this.agents.size>=this.config.maxAgents)throw new Error(`Maximum number of agents (${this.config.maxAgents}) reached`);let t=Date.now(),n={...e,status:"healthy",lastHeartbeat:t,registeredAt:t,connectionCount:0};this.agents.set(e.id,n),f.info("Agent registered",{agentId:e.id,name:e.name,capabilities:e.capabilities,address:e.address,totalAgents:this.agents.size});}async deregister(e){let t=this.agents.get(e);if(!t){f.warn("Attempted to deregister unknown agent",{agentId:e});return}this.agents.delete(e),f.info("Agent deregistered",{agentId:e,name:t.name,totalAgents:this.agents.size});}async heartbeat(e,t){let n=this.agents.get(e);if(!n)throw f.warn("Heartbeat received from unknown agent",{agentId:e}),new Error(`Agent "${e}" is not registered`);n.lastHeartbeat=Date.now(),n.status="healthy",t&&(n.metadata={...n.metadata,...t}),f.debug("Agent heartbeat received",{agentId:e,lastHeartbeat:n.lastHeartbeat});}getAgent(e){return this.agents.get(e)}getAllAgents(){return Array.from(this.agents.values())}getHealthyAgents(){return this.getAllAgents().filter(e=>e.status==="healthy")}selectAgent(e){let{strategy:t,capabilities:n,healthyOnly:s=true}=e,r=s?this.getHealthyAgents():this.getAllAgents();if(n&&n.length>0&&(r=r.filter(o=>n.every(p=>o.capabilities.includes(p)))),r.length===0){f.warn("No agents available for selection",{strategy:t,capabilities:n,healthyOnly:s,totalAgents:this.agents.size});return}let i;switch(t){case "random":i=this.selectRandom(r);break;case "round-robin":i=this.selectRoundRobin(r);break;case "least-connections":i=this.selectLeastConnections(r);break;default:i=this.selectRandom(r);}return f.debug("Agent selected",{agentId:i.id,strategy:t,connectionCount:i.connectionCount}),i}incrementConnection(e){let t=this.agents.get(e);t&&(t.connectionCount++,f.debug("Agent connection incremented",{agentId:e,connectionCount:t.connectionCount}));}decrementConnection(e){let t=this.agents.get(e);t&&t.connectionCount>0&&(t.connectionCount--,f.debug("Agent connection decremented",{agentId:e,connectionCount:t.connectionCount}));}startHeartbeatCheck(){if(this.heartbeatTimer){f.warn("Heartbeat check is already running");return}f.info("Starting heartbeat check",{interval:this.config.heartbeatCheckInterval,timeout:this.config.heartbeatTimeout}),this.heartbeatTimer=setInterval(()=>this.checkHeartbeats(),this.config.heartbeatCheckInterval);}stopHeartbeatCheck(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null,f.info("Heartbeat check stopped"));}checkHeartbeats(){let e=Date.now(),{heartbeatTimeout:t,autoDeregisterTimeout:n}=this.config,s=[],r=0;for(let[i,o]of this.agents.entries()){let p=e-o.lastHeartbeat;if(p>n){s.push(i);continue}p>t&&o.status==="healthy"&&(o.status="unhealthy",r++,f.warn("Agent marked as unhealthy due to heartbeat timeout",{agentId:i,lastHeartbeat:o.lastHeartbeat,timeSinceLastHeartbeat:p,timeout:t}));}for(let i of s){let o=this.agents.get(i);o&&(f.info("Auto-deregistering agent due to timeout",{agentId:i,name:o.name,lastHeartbeat:o.lastHeartbeat,timeout:n}),this.agents.delete(i));}(r>0||s.length>0)&&f.info("Heartbeat check completed",{unhealthyCount:r,deregisteredCount:s.length,totalAgents:this.agents.size});}selectRandom(e){let t=Math.floor(Math.random()*e.length);return e[t]}selectRoundRobin(e){this.roundRobinIndex=this.roundRobinIndex%e.length;let t=e[this.roundRobinIndex];return this.roundRobinIndex++,t}selectLeastConnections(e){return e.reduce((t,n)=>n.connectionCount<t.connectionCount?n:t)}getStats(){let e=this.getAllAgents();return {totalAgents:e.length,healthyAgents:e.filter(t=>t.status==="healthy").length,unhealthyAgents:e.filter(t=>t.status==="unhealthy").length,offlineAgents:e.filter(t=>t.status==="offline").length,totalConnections:e.reduce((t,n)=>t+n.connectionCount,0)}}setAgentStatus(e,t){let n=this.agents.get(e);n&&(n.status=t);}setLastHeartbeat(e,t){let n=this.agents.get(e);n&&(n.lastHeartbeat=t);}};var h=b.create({service:"gateway:agent-sync-manager"}),M=class{constructor(e){a$1(this,"config");a$1(this,"registry");a$1(this,"localNodeId");a$1(this,"localNodeName");a$1(this,"remoteNodes",new Map);a$1(this,"remoteAgents",new Map);a$1(this,"syncTimer",null);a$1(this,"running",false);this.registry=e.registry,this.localNodeId=e.localNodeId,this.localNodeName=e.localNodeName,this.config={...$,...e.config};}async start(){if(this.running){h.warn("Agent sync manager is already running");return}h.info("Starting agent sync manager",{localNodeId:this.localNodeId,mode:this.config.mode,interval:this.config.interval,remoteNodes:this.config.remoteNodes.length});for(let e of this.config.remoteNodes)this.remoteNodes.set(e.id,{info:{id:e.id,name:e.id,address:e.address,port:e.port,lastSyncAt:0,status:"unknown"},lastSyncAt:0,status:"synced"});(this.config.mode==="pull"||this.config.mode==="both")&&(this.syncTimer=setInterval(()=>this.pullFromAllNodes(),this.config.interval)),this.running=true,h.info("Agent sync manager started");}async stop(){if(!this.running){h.warn("Agent sync manager is not running");return}h.info("Stopping agent sync manager"),this.syncTimer&&(clearInterval(this.syncTimer),this.syncTimer=null),this.remoteNodes.clear(),this.remoteAgents.clear(),this.running=false,h.info("Agent sync manager stopped");}async pullFromAllNodes(){for(let[e]of this.remoteNodes.entries())try{await this.pullFromNode(e);}catch(t){h.error("Failed to pull from node",{nodeId:e,error:t.message});}}async pullFromNode(e){let t=this.remoteNodes.get(e);if(!t)throw new Error(`Unknown remote node: ${e}`);t.status="syncing";let n=this.config.remoteNodes.find(s=>s.id===e);if(!n)throw new Error(`Remote node not found in config: ${e}`);h.debug("Pulling agent list from node",{nodeId:e,address:n.address});try{let s=`http://${n.address}:${n.port}/sync/agents`,r=await Fetch.get(s,{headers:{"Content-Type":"application/json","X-Gateway-Node-Id":this.localNodeId}});if(!r.ok)throw new Error(`HTTP ${r.status}`);let i=r.data.agents||[];for(let o of i)o.sourceGatewayId=e,this.remoteAgents.set(o.id,o);return t.lastSyncAt=Date.now(),t.status="synced",t.info.lastSyncAt=t.lastSyncAt,t.info.status="online",delete t.error,h.info("Pulled agent list from node",{nodeId:e,agentCount:i.length,lastSyncAt:t.lastSyncAt}),i}catch(s){throw t.status="error",t.error=s.message,t.info.status="offline",h.error("Failed to pull from node",{nodeId:e,error:s.message}),s}}async pushEvent(e){if(this.config.mode!=="push"&&this.config.mode!=="both")return;let t=[];for(let[n]of this.remoteNodes.entries())t.push(this.pushToNode(n,e));await Promise.allSettled(t);}async pushToNode(e,t){let n=this.config.remoteNodes.find(s=>s.id===e);if(!n){h.warn("Unknown remote node for push",{nodeId:e});return}try{let s=`http://${n.address}:${n.port}/sync/events`,r=await Fetch.post(s,t,{headers:{"Content-Type":"application/json","X-Gateway-Node-Id":this.localNodeId}});if(!r.ok)throw new Error(`HTTP ${r.status}`);h.debug("Pushed event to node",{nodeId:e,eventType:t.type});}catch(s){h.error("Failed to push to node",{nodeId:e,eventType:t.type,error:s.message});}}async handleSyncMessage(e){switch(h.debug("Received sync message",{type:e.type,sourceNodeId:e.sourceNodeId}),e.type){case "agent_list_request":await this.handleAgentListRequest(e);break;case "agent_list_response":await this.handleAgentListResponse(e);break;case "agent_register":await this.handleAgentRegister(e);break;case "agent_deregister":await this.handleAgentDeregister(e);break;case "agent_heartbeat":await this.handleAgentHeartbeat(e);break;case "agent_status_change":await this.handleAgentStatusChange(e);break;default:h.warn("Unknown sync message type",{type:e.type});}}async handleAgentListRequest(e){let t=this.getLocalAgentsForSync(),n={type:"agent_list_response",sourceNodeId:this.localNodeId,timestamp:Date.now(),requestId:e.requestId,agents:t,fullList:true,syncAt:Date.now()};await this.pushToNode(e.sourceNodeId,n);}async handleAgentListResponse(e){for(let t of e.agents)this.remoteAgents.set(t.id,t);h.info("Received agent list response",{sourceNodeId:e.sourceNodeId,agentCount:e.agents.length});}async handleAgentRegister(e){let{agent:t}=e,n=this.remoteAgents.get(t.id);if(n){let s=this.resolveConflict(n,t);this.remoteAgents.set(t.id,s);}else this.remoteAgents.set(t.id,t);h.info("Remote agent registered",{agentId:t.id,name:t.name,sourceNodeId:e.sourceNodeId});}async handleAgentDeregister(e){let{agentId:t}=e;this.remoteAgents.delete(t),h.info("Remote agent deregistered",{agentId:t,sourceNodeId:e.sourceNodeId});}async handleAgentHeartbeat(e){let{agentId:t,heartbeatAt:n,status:s}=e,r=this.remoteAgents.get(t);r&&(r.updatedAt=n,r.status=s),h.debug("Remote agent heartbeat received",{agentId:t,status:s,sourceNodeId:e.sourceNodeId});}async handleAgentStatusChange(e){let{agentId:t,newStatus:n,changedAt:s}=e,r=this.remoteAgents.get(t);r&&(r.status=n,r.updatedAt=s),h.info("Remote agent status changed",{agentId:t,newStatus:n,sourceNodeId:e.sourceNodeId});}resolveConflict(e,t){switch(this.config.conflictResolution){case "latest":return t.updatedAt>e.updatedAt?t:e;case "local":return e;case "remote":return t;default:return t.updatedAt>e.updatedAt?t:e}}getLocalAgentsForSync(){return this.registry.getAllAgents().map(t=>({id:t.id,name:t.name,sourceGatewayId:this.localNodeId,address:t.address,capabilities:t.capabilities,status:t.status,updatedAt:t.lastHeartbeat,metadata:t.metadata}))}getMergedAgentList(){let e=new Map;for(let t of this.getLocalAgentsForSync())e.set(t.id,t);for(let[t,n]of this.remoteAgents.entries())e.has(t)||e.set(t,n);return Array.from(e.values())}getRemoteAgents(){return Array.from(this.remoteAgents.values())}getStats(){let e=this.registry.getAllAgents(),t=0,n=0;for(let r of this.remoteNodes.values())r.info.status==="online"?t++:n++;let s=null;for(let r of this.remoteNodes.values())r.lastSyncAt>(s||0)&&(s=r.lastSyncAt);return {localAgentCount:e.length,remoteAgentCount:this.remoteAgents.size,totalAgentCount:e.length+this.remoteAgents.size,onlineNodeCount:t,offlineNodeCount:n,lastSyncAt:s}}isRunning(){return this.running}addRemoteNode(e){this.remoteNodes.set(e.id,{info:{id:e.id,name:e.id,address:e.address,port:e.port,lastSyncAt:0,status:"unknown"},lastSyncAt:0,status:"synced"}),h.info("Remote node added",{nodeId:e.id,address:e.address});}removeRemoteNode(e){this.remoteNodes.delete(e);for(let[t,n]of this.remoteAgents.entries())n.sourceGatewayId===e&&this.remoteAgents.delete(t);h.info("Remote node removed",{nodeId:e});}};var N=class{constructor(e){a$1(this,"log");a$1(this,"config");a$1(this,"ws",null);a$1(this,"state","disconnected");a$1(this,"subscriptions",new Map);a$1(this,"messageCallbacks",new Set);a$1(this,"agentListCallbacks",new Set);a$1(this,"cachedAgentList",[]);a$1(this,"localAgentList",[]);a$1(this,"reconnectAttempts",0);a$1(this,"reconnectTimer",null);a$1(this,"heartbeatTimer",null);a$1(this,"agentListRequestId",null);a$1(this,"agentListRequestResolve",null);this.config={url:e.url,type:e.type,id:e.id??`client_${Date.now()}_${Math.random().toString(36).substring(2,9)}`,reconnect:{enabled:e.reconnect?.enabled??true,maxAttempts:e.reconnect?.maxAttempts??5,delay:e.reconnect?.delay??3e3},heartbeat:{enabled:e.heartbeat?.enabled??true,interval:e.heartbeat?.interval??3e4}},this.log=b.create({service:`gateway:client:${this.config.id}`});}async connect(){if(this.state==="connected"||this.state==="connecting"){this.log.warn("already connected or connecting");return}return this.state="connecting",this.log.info("connecting to gateway",{url:this.config.url}),new Promise((e,t)=>{try{this.ws=new WebSocket(this.config.url),this.ws.onopen=()=>{this.state="connected",this.reconnectAttempts=0,this.log.info("connected to gateway"),this.config.heartbeat.enabled&&this.startHeartbeat(),this.resubscribeAll(),e();},this.ws.onmessage=n=>{this.handleMessage(n.data);},this.ws.onclose=n=>{this.handleClose(n.code,n.reason);},this.ws.onerror=n=>{this.log.error("WebSocket error",{error:String(n)}),this.state==="connecting"&&t(new Error("Connection failed"));};}catch(n){this.state="disconnected",t(n);}})}async disconnect(){this.state!=="disconnected"&&(this.log.info("disconnecting from gateway"),this.stopHeartbeat(),this.stopReconnect(),this.ws&&(this.ws.close(1e3,"Client disconnect"),this.ws=null),this.state="disconnected",this.log.info("disconnected from gateway"));}async subscribe(e,t){if(this.state!=="connected")throw new Error("Not connected to gateway");this.sendData({type:"subscribe",sessionId:e,backendSessionId:t,clientId:this.config.id}),this.log.info("subscribed to session",{sessionId:e,backendSessionId:t});}async unsubscribe(e){if(this.state!=="connected")throw new Error("Not connected to gateway");this.sendData({type:"unsubscribe",sessionId:e,clientId:this.config.id}),this.subscriptions.delete(e),this.log.info("unsubscribed from session",{sessionId:e});}async send(e){if(this.state!=="connected")throw new Error("Not connected to gateway");this.sendData({type:"message",message:e});}onMessage(e){this.messageCallbacks.add(e);}offMessage(e){this.messageCallbacks.delete(e);}getState(){return this.state}getId(){return this.config.id}getSubscriptions(){return [...this.subscriptions.keys()]}setLocalAgents(e){this.localAgentList=e,this.updateMergedAgentList();}async fetchAgentList(){if(this.state!=="connected")throw new Error("Not connected to gateway");return new Promise((e,t)=>{let n=`req_${Date.now()}_${Math.random().toString(36).substring(2,9)}`;this.agentListRequestId=n,this.agentListRequestResolve=e,this.sendData({type:"agent_list_request",requestId:n}),setTimeout(()=>{this.agentListRequestId===n&&(this.agentListRequestId=null,this.agentListRequestResolve=null,t(new Error("Agent list request timeout")));},1e4);})}getConnectableAgents(e={}){let{localAgents:t,preferLocal:n=true,filterOffline:s=true}=e,r=t||this.localAgentList,i=this.cachedAgentList,o=this.mergeAgentLists(r,i,n);return s?o.filter(p=>p.status!=="offline"):o}onAgentListChange(e){this.agentListCallbacks.add(e);}offAgentListChange(e){this.agentListCallbacks.delete(e);}mergeAgentLists(e,t,n){let s=new Map,r=n?t:e,i=n?e:t;for(let o of r)s.set(o.id,o);for(let o of i)s.set(o.id,o);return Array.from(s.values())}updateMergedAgentList(){let e=this.getConnectableAgents({filterOffline:false});for(let t of this.agentListCallbacks)try{t(e);}catch(n){this.log.error("agent list callback error",{error:String(n)});}}handleAgentListResponse(e,t){t&&this.agentListRequestId===t&&this.agentListRequestResolve&&(this.agentListRequestId=null,this.agentListRequestResolve(e),this.agentListRequestResolve=null),this.cachedAgentList=e,this.updateMergedAgentList(),this.log.debug("received agent list",{count:e.length});}sendData(e){if(!this.ws||this.ws.readyState!==WebSocket.OPEN){this.log.warn("WebSocket not ready, cannot send");return}this.ws.send(JSON.stringify(e));}handleMessage(e){try{let t=JSON.parse(e);switch(t.type){case "message":this.handleGatewayMessage(t.message);break;case "subscribed":this.subscriptions.set(t.sessionId,t.subscription);break;case "unsubscribed":this.subscriptions.delete(t.sessionId);break;case "pong":break;case "agent_list_response":this.handleAgentListResponse(t.agents||[],t.requestId);break;case "agent_update":this.handleAgentUpdate(t.agent,t.action);break;default:this.log.debug("unknown message type",{type:t.type});}}catch(t){this.log.error("failed to parse message",{error:String(t)});}}handleAgentUpdate(e,t){switch(t){case "add":case "update":{let n=this.cachedAgentList.findIndex(s=>s.id===e.id);n>=0?this.cachedAgentList[n]=e:this.cachedAgentList.push(e);break}case "remove":this.cachedAgentList=this.cachedAgentList.filter(n=>n.id!==e.id);break}this.updateMergedAgentList();}handleGatewayMessage(e){for(let t of this.messageCallbacks)try{t(e);}catch(n){this.log.error("message callback error",{error:String(n)});}}handleClose(e,t){this.log.info("connection closed",{code:e,reason:t}),this.state="disconnected",this.ws=null,this.stopHeartbeat(),this.config.reconnect.enabled&&this.scheduleReconnect();}scheduleReconnect(){if(this.reconnectAttempts>=this.config.reconnect.maxAttempts){this.log.error("max reconnect attempts reached");return}this.reconnectAttempts++,this.state="reconnecting";let e=this.config.reconnect.delay*this.reconnectAttempts;this.log.info("scheduling reconnect",{attempt:this.reconnectAttempts,delay:e}),this.reconnectTimer=setTimeout(async()=>{try{await this.connect();}catch(t){this.log.error("reconnect failed",{error:String(t)});}},e);}stopReconnect(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null);}startHeartbeat(){this.heartbeatTimer=setInterval(()=>{this.state==="connected"&&this.sendData({type:"ping"});},this.config.heartbeat.interval);}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null);}async resubscribeAll(){for(let e of this.subscriptions.keys())try{await this.subscribe(e);}catch(t){this.log.error("failed to resubscribe",{sessionId:e,error:String(t)});}}getHttpUrl(){return this.config.url.replace(/^ws:/,"http:").replace(/^wss:/,"https:").replace(/\/$/,"")}async httpRequest(e,t={}){let n=`${this.getHttpUrl()}${e}`,s=t.method||"GET",{signal:r,...i}=t,o=await Fetch.request(n,{method:s,...i,headers:{"Content-Type":"application/json",...i.headers},...r?{signal:r}:{}});if(!o.ok){let p=o.data,G=typeof p=="object"?JSON.stringify(p):String(p||"Unknown error");throw new Error(`HTTP ${o.status}: ${G}`)}return o.data}async healthCheck(){return this.httpRequest("/health")}async getStatus(){return this.httpRequest("/status")}async listChannels(){return this.httpRequest("/channels")}async listContacts(e,t={}){let n=new URLSearchParams({platform:e,limit:String(t.limit??100),offset:String(t.offset??0)});return this.httpRequest(`/contacts?${n}`)}async getContact(e){return this.httpRequest(`/contacts/${encodeURIComponent(e)}`)}async getSession(e){return this.httpRequest(`/sessions/${encodeURIComponent(e)}`)}async createSession(e){return this.httpRequest("/session",{method:"POST",body:JSON.stringify(e??{})})}async getOrCreateSession(e){if(e.sessionId)try{return await this.getSession(e.sessionId),{sessionId:e.sessionId,created:!1}}catch{this.log.debug("session not found, will create new",{sessionId:e.sessionId});}if(e.platform||e.channel||e.userId){let n=e.chatId??e.userId;try{let i=await this.listSessions({platform:e.platform,channelId:e.channel,userId:e.userId,chatId:n});if(i.sessions.length>0){let o=i.sessions[0];if(o)return this.log.info("found existing session",{sessionId:o.id,channel:e.channel,userId:e.userId,chatId:n}),{sessionId:o.id,created:!1}}}catch(i){this.log.warn("failed to search sessions",{error:String(i)});}let s={channel:{platform:e.platform,channelId:e.channel,userId:e.userId,chatId:n}},r=await this.createSession(s);return this.log.info("session created",{sessionId:r.id,params:s}),{sessionId:r.id,created:true}}let t=await this.createSession({channel:{platform:"api",channelId:"default"}});return this.log.info("session created (default)",{sessionId:t.id}),{sessionId:t.id,created:true}}async listSessions(e={}){let t=new URLSearchParams;return t.set("limit",String(e.limit??100)),t.set("offset",String(e.offset??0)),e.platform&&t.set("platform",e.platform),e.channelId&&t.set("channelId",e.channelId),e.userId&&t.set("userId",e.userId),e.chatId&&t.set("chatId",e.chatId),this.httpRequest(`/sessions?${t}`)}async sendMessage(e,t){return this.httpRequest(`/session/${encodeURIComponent(e)}/prompt`,{method:"POST",body:JSON.stringify(t)})}};async function Jt(c){let{Log:e}=await import('./chunks/log-FQII2FT4.mjs'),t=false;await e.init({logDir:c.logDir??process.env.EASBOT_LOG_PATH??process.cwd(),print:c.print??false,dev:c.dev??t,level:c.level??("INFO")});}
2
2
  export{T as AgentRegistry,M as AgentSyncManager,x as Gateway,N as GatewayClient,Jt as initLog};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easbot/gateway",
3
- "version": "0.2.40",
3
+ "version": "0.2.41",
4
4
  "description": "EASBot Gateway - AI Agent Server and Multi-channel Integration Platform - 支持 WebSocket、HTTP、Discord、Telegram、Slack 等多渠道集成",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -67,10 +67,10 @@
67
67
  "ws": "^8.20.0",
68
68
  "xdg-basedir": "^5.1.0",
69
69
  "zod": "^4.4.3",
70
- "@easbot/plugin": "0.2.40",
71
- "@easbot/sdk": "0.2.40",
72
- "@easbot/utils": "0.2.40",
73
- "@easbot/types": "0.2.40"
70
+ "@easbot/plugin": "0.2.41",
71
+ "@easbot/sdk": "0.2.41",
72
+ "@easbot/types": "0.2.41",
73
+ "@easbot/utils": "0.2.41"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@biomejs/biome": "^2.4.14",