@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/chunks/chunk-OBFSXYWV.mjs +30 -0
- package/dist/chunks/chunk-RFOEJ3A2.cjs +30 -0
- package/dist/chunks/server-3QASQ56E.cjs +1 -0
- package/dist/chunks/server-54GY5XXD.mjs +1 -0
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +197 -19
- package/dist/index.d.ts +197 -19
- package/dist/index.mjs +1 -1
- package/package.json +5 -5
- package/dist/chunks/chunk-BVJ3UMFP.cjs +0 -26
- package/dist/chunks/chunk-SPCU5JL2.mjs +0 -26
- package/dist/chunks/server-D6N6P2NV.mjs +0 -1
- package/dist/chunks/server-JPVKX37A.cjs +0 -1
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
|
|
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.
|
|
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.
|
|
71
|
-
"@easbot/sdk": "0.2.
|
|
72
|
-
"@easbot/
|
|
73
|
-
"@easbot/
|
|
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",
|