@plosson/agentio 0.4.2 → 0.4.3

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.
@@ -0,0 +1,325 @@
1
+ import type { ServiceName, GatewayConfig } from '../types/config';
2
+
3
+ // Re-export for convenience
4
+ export type { GatewayConfig } from '../types/config';
5
+
6
+ /**
7
+ * Inbound message - received from external services
8
+ */
9
+ export interface InboundMessage {
10
+ id: string; // UUID
11
+ service: ServiceName;
12
+ profile: string;
13
+ conversationId: string; // Chat/thread identifier
14
+ platformId: string; // Original message ID from platform
15
+
16
+ senderId: string;
17
+ senderName?: string;
18
+ senderHandle?: string;
19
+
20
+ content?: string; // Message text
21
+ mediaType?: MediaType;
22
+ mediaPath?: string; // Local path if downloaded
23
+
24
+ receivedAt: number; // Unix timestamp
25
+ status: InboxStatus;
26
+ doneAt?: number;
27
+
28
+ replyToId?: string; // If this is a reply
29
+ metadata?: Record<string, unknown>;
30
+ }
31
+
32
+ /**
33
+ * Outbound message - queued for sending to services
34
+ */
35
+ export interface OutboundMessage {
36
+ id: string; // UUID
37
+ service: ServiceName;
38
+ profile: string;
39
+ conversationId: string; // Destination chat/thread
40
+
41
+ content?: string;
42
+ mediaPath?: string;
43
+ mediaType?: MediaType;
44
+
45
+ replyToPlatformId?: string; // If replying to a message
46
+
47
+ queuedAt: number; // Unix timestamp
48
+ status: OutboxStatus;
49
+ sentAt?: number;
50
+ error?: string;
51
+ platformId?: string; // Assigned after send
52
+
53
+ metadata?: Record<string, unknown>;
54
+ }
55
+
56
+ export type MediaType = 'image' | 'video' | 'audio' | 'document';
57
+ export type InboxStatus = 'pending' | 'done';
58
+ export type OutboxStatus = 'pending' | 'sending' | 'sent' | 'failed';
59
+
60
+ export const DEFAULT_GATEWAY_CONFIG: Required<GatewayConfig> = {
61
+ name: '',
62
+ secret: '',
63
+ api: {
64
+ port: 7890,
65
+ host: '127.0.0.1',
66
+ secret: '',
67
+ },
68
+ webhook: {
69
+ url: '',
70
+ secret: '',
71
+ debounceMs: 2000,
72
+ },
73
+ media: {
74
+ download: true,
75
+ maxSizeMb: 50,
76
+ },
77
+ retention: {
78
+ doneMessagesDays: 30,
79
+ sentMessagesDays: 7,
80
+ },
81
+ };
82
+
83
+ /**
84
+ * Gateway HTTP API request/response types
85
+ */
86
+ export interface InboxPullRequest {
87
+ service?: ServiceName;
88
+ profile?: string;
89
+ conversationId?: string; // Filter by conversation/group
90
+ limit?: number;
91
+ status?: InboxStatus;
92
+ }
93
+
94
+ export interface InboxPullResponse {
95
+ messages: InboundMessage[];
96
+ }
97
+
98
+ export interface InboxGetRequest {
99
+ id: string;
100
+ }
101
+
102
+ export interface InboxGetResponse {
103
+ message: InboundMessage | null;
104
+ }
105
+
106
+ export interface InboxAckRequest {
107
+ id: string;
108
+ }
109
+
110
+ export interface InboxAckResponse {
111
+ success: boolean;
112
+ }
113
+
114
+ export interface InboxReplyRequest {
115
+ id: string;
116
+ content: string;
117
+ mediaPath?: string;
118
+ mediaType?: MediaType;
119
+ }
120
+
121
+ export interface InboxReplyResponse {
122
+ outboxId: string;
123
+ status: OutboxStatus;
124
+ }
125
+
126
+ export interface InboxStatsRequest {
127
+ service?: ServiceName;
128
+ profile?: string;
129
+ }
130
+
131
+ export interface InboxStatsResponse {
132
+ pending: number;
133
+ done: number;
134
+ total: number;
135
+ }
136
+
137
+ export interface OutboxSendRequest {
138
+ service: ServiceName;
139
+ profile: string;
140
+ conversationId: string;
141
+ content?: string;
142
+ mediaPath?: string;
143
+ mediaType?: MediaType;
144
+ replyToPlatformId?: string;
145
+ metadata?: Record<string, unknown>;
146
+ }
147
+
148
+ export interface OutboxSendResponse {
149
+ id: string;
150
+ status: OutboxStatus;
151
+ }
152
+
153
+ export interface OutboxStatusRequest {
154
+ id: string;
155
+ }
156
+
157
+ export interface OutboxStatusResponse {
158
+ message: OutboundMessage | null;
159
+ }
160
+
161
+ export interface OutboxListRequest {
162
+ service?: ServiceName;
163
+ profile?: string;
164
+ status?: OutboxStatus;
165
+ limit?: number;
166
+ }
167
+
168
+ export interface OutboxListResponse {
169
+ messages: OutboundMessage[];
170
+ }
171
+
172
+ export interface GatewayStatusResponse {
173
+ running: boolean;
174
+ uptime: number;
175
+ adapters: {
176
+ service: ServiceName;
177
+ profile: string;
178
+ connected: boolean;
179
+ error?: string;
180
+ }[];
181
+ }
182
+
183
+ export interface HealthResponse {
184
+ status: 'ok' | 'error';
185
+ timestamp: number;
186
+ }
187
+
188
+ /**
189
+ * Webhook notification payload
190
+ */
191
+ export interface WebhookPayload {
192
+ event: 'inbox.message';
193
+ timestamp: number;
194
+ messages: {
195
+ id: string;
196
+ service: ServiceName;
197
+ profile: string;
198
+ sender: string;
199
+ preview: string;
200
+ }[];
201
+ }
202
+
203
+ /**
204
+ * Daemon state
205
+ */
206
+ export interface DaemonState {
207
+ pid: number;
208
+ startedAt: number;
209
+ adapters: string[]; // Format: "service:profile"
210
+ }
211
+
212
+ /**
213
+ * WhatsApp pairing request/response types
214
+ */
215
+ export interface WhatsAppPairRequest {
216
+ profile: string;
217
+ }
218
+
219
+ export interface WhatsAppPairResponse {
220
+ status: 'waiting_qr' | 'connected' | 'connecting' | 'not_configured';
221
+ qrCode?: string; // QR code string for Baileys
222
+ phoneNumber?: string; // Phone number if connected
223
+ displayName?: string; // Display name if available
224
+ message?: string; // Status message
225
+ }
226
+
227
+ /**
228
+ * WhatsApp Group API types
229
+ */
230
+ import type {
231
+ WhatsAppGroup,
232
+ WhatsAppGroupCreateOptions,
233
+ WhatsAppGroupUpdateOptions,
234
+ WhatsAppParticipantAction,
235
+ } from '../types/whatsapp';
236
+
237
+ export interface WhatsAppGroupListRequest {
238
+ profile: string;
239
+ }
240
+
241
+ export interface WhatsAppGroupListResponse {
242
+ groups: WhatsAppGroup[];
243
+ }
244
+
245
+ export interface WhatsAppGroupGetRequest {
246
+ profile: string;
247
+ groupId: string;
248
+ }
249
+
250
+ export interface WhatsAppGroupGetResponse {
251
+ group: WhatsAppGroup;
252
+ }
253
+
254
+ export interface WhatsAppGroupCreateRequest {
255
+ profile: string;
256
+ name: string;
257
+ participants: string[];
258
+ picture?: string; // Path to profile picture image
259
+ }
260
+
261
+ export interface WhatsAppGroupCreateResponse {
262
+ group: WhatsAppGroup;
263
+ }
264
+
265
+ export interface WhatsAppGroupUpdateRequest {
266
+ profile: string;
267
+ groupId: string;
268
+ subject?: string;
269
+ description?: string;
270
+ picture?: string; // Path to profile picture image
271
+ }
272
+
273
+ export interface WhatsAppGroupUpdateResponse {
274
+ success: boolean;
275
+ }
276
+
277
+ export interface WhatsAppGroupParticipantsRequest {
278
+ profile: string;
279
+ groupId: string;
280
+ participants: string[];
281
+ action: WhatsAppParticipantAction;
282
+ }
283
+
284
+ export interface WhatsAppGroupParticipantsResponse {
285
+ success: boolean;
286
+ results?: { participant: string; status: string }[];
287
+ }
288
+
289
+ export interface WhatsAppGroupLeaveRequest {
290
+ profile: string;
291
+ groupId: string;
292
+ }
293
+
294
+ export interface WhatsAppGroupLeaveResponse {
295
+ success: boolean;
296
+ }
297
+
298
+ export interface WhatsAppGroupInviteRequest {
299
+ profile: string;
300
+ groupId: string;
301
+ }
302
+
303
+ export interface WhatsAppGroupInviteResponse {
304
+ inviteCode: string;
305
+ inviteLink: string;
306
+ }
307
+
308
+ export interface WhatsAppGroupJoinRequest {
309
+ profile: string;
310
+ inviteCode: string;
311
+ }
312
+
313
+ export interface WhatsAppGroupJoinResponse {
314
+ groupId: string;
315
+ }
316
+
317
+ export interface WhatsAppGroupResolveRequest {
318
+ profile: string;
319
+ nameOrId: string;
320
+ }
321
+
322
+ export interface WhatsAppGroupResolveResponse {
323
+ groupId: string | null;
324
+ groupName: string | null;
325
+ }
@@ -0,0 +1,109 @@
1
+ import { createHmac } from 'crypto';
2
+ import type { ServiceName } from '../types/config';
3
+ import type { WebhookPayload, GatewayConfig } from './types';
4
+
5
+ interface PendingMessage {
6
+ id: string;
7
+ service: ServiceName;
8
+ profile: string;
9
+ sender: string;
10
+ preview: string;
11
+ }
12
+
13
+ let pendingMessages: PendingMessage[] = [];
14
+ let debounceTimer: ReturnType<typeof setTimeout> | null = null;
15
+ let webhookConfig: GatewayConfig['webhook'] | null = null;
16
+
17
+ /**
18
+ * Configure the webhook notifier
19
+ */
20
+ export function configureWebhook(config: GatewayConfig['webhook']): void {
21
+ webhookConfig = config;
22
+ }
23
+
24
+ /**
25
+ * Queue a message for webhook notification
26
+ * Messages are debounced to batch multiple notifications
27
+ */
28
+ export function queueWebhookNotification(message: PendingMessage): void {
29
+ if (!webhookConfig?.url) return;
30
+
31
+ pendingMessages.push(message);
32
+
33
+ // Clear existing timer
34
+ if (debounceTimer) {
35
+ clearTimeout(debounceTimer);
36
+ }
37
+
38
+ // Set new debounce timer
39
+ const debounceMs = webhookConfig.debounceMs ?? 2000;
40
+ debounceTimer = setTimeout(() => {
41
+ sendWebhook();
42
+ }, debounceMs);
43
+ }
44
+
45
+ /**
46
+ * Send pending webhook notifications
47
+ */
48
+ async function sendWebhook(): Promise<void> {
49
+ if (!webhookConfig?.url || pendingMessages.length === 0) return;
50
+
51
+ const messages = [...pendingMessages];
52
+ pendingMessages = [];
53
+ debounceTimer = null;
54
+
55
+ const payload: WebhookPayload = {
56
+ event: 'inbox.message',
57
+ timestamp: Math.floor(Date.now() / 1000),
58
+ messages,
59
+ };
60
+
61
+ const body = JSON.stringify(payload);
62
+ const headers: Record<string, string> = {
63
+ 'Content-Type': 'application/json',
64
+ };
65
+
66
+ // Add HMAC signature if secret is configured
67
+ if (webhookConfig.secret) {
68
+ const signature = createHmac('sha256', webhookConfig.secret)
69
+ .update(body)
70
+ .digest('hex');
71
+ headers['X-Agentio-Signature'] = `sha256=${signature}`;
72
+ }
73
+
74
+ try {
75
+ const response = await fetch(webhookConfig.url, {
76
+ method: 'POST',
77
+ headers,
78
+ body,
79
+ });
80
+
81
+ if (!response.ok) {
82
+ console.error(`Webhook failed: ${response.status} ${response.statusText}`);
83
+ }
84
+ } catch (error) {
85
+ console.error('Webhook error:', error instanceof Error ? error.message : error);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Flush any pending webhook notifications immediately
91
+ */
92
+ export async function flushWebhook(): Promise<void> {
93
+ if (debounceTimer) {
94
+ clearTimeout(debounceTimer);
95
+ }
96
+ await sendWebhook();
97
+ }
98
+
99
+ /**
100
+ * Stop the webhook notifier
101
+ */
102
+ export function stopWebhook(): void {
103
+ if (debounceTimer) {
104
+ clearTimeout(debounceTimer);
105
+ debounceTimer = null;
106
+ }
107
+ pendingMessages = [];
108
+ webhookConfig = null;
109
+ }
package/src/index.ts CHANGED
@@ -1,21 +1,30 @@
1
1
  #!/usr/bin/env bun
2
+ // Polyfills must be imported first (before any code that uses protobufjs)
3
+ import './polyfills';
2
4
  import { Command } from 'commander';
3
- import { registerGmailCommands } from './commands/gmail';
5
+ // Services (alphabetical)
6
+ import { registerDiscourseCommands } from './commands/discourse';
7
+ import { registerGCalCommands } from './commands/gcal';
8
+ import { registerGChatCommands } from './commands/gchat';
4
9
  import { registerGDocsCommands } from './commands/gdocs';
5
10
  import { registerGDriveCommands } from './commands/gdrive';
6
- import { registerTelegramCommands } from './commands/telegram';
7
- import { registerGChatCommands } from './commands/gchat';
8
11
  import { registerGitHubCommands } from './commands/github';
12
+ import { registerGmailCommands } from './commands/gmail';
13
+ import { registerGTasksCommands } from './commands/gtasks';
9
14
  import { registerJiraCommands } from './commands/jira';
10
- import { registerSlackCommands } from './commands/slack';
11
15
  import { registerRssCommands } from './commands/rss';
12
- import { registerDiscourseCommands } from './commands/discourse';
16
+ import { registerSlackCommands } from './commands/slack';
13
17
  import { registerSqlCommands } from './commands/sql';
14
- import { registerUpdateCommand } from './commands/update';
15
- import { registerConfigCommands } from './commands/config';
18
+ import { registerTelegramCommands } from './commands/telegram';
19
+ import { registerWhatsAppCommands } from './commands/whatsapp';
20
+
21
+ // Agentio utilities
16
22
  import { registerClaudeCommands } from './commands/claude';
17
- import { registerStatusCommand } from './commands/status';
23
+ import { registerConfigCommands } from './commands/config';
18
24
  import { registerDocsCommand } from './commands/docs';
25
+ import { registerGatewayCommands } from './commands/gateway';
26
+ import { registerStatusCommand } from './commands/status';
27
+ import { registerUpdateCommand } from './commands/update';
19
28
 
20
29
  declare const BUILD_VERSION: string | undefined;
21
30
 
@@ -34,21 +43,28 @@ program
34
43
  .description('CLI for LLM agents to interact with communication and tracking services')
35
44
  .version(getVersion());
36
45
 
37
- registerGmailCommands(program);
46
+ // Services (alphabetical)
47
+ registerDiscourseCommands(program);
48
+ registerGCalCommands(program);
49
+ registerGChatCommands(program);
38
50
  registerGDocsCommands(program);
39
51
  registerGDriveCommands(program);
40
- registerTelegramCommands(program);
41
- registerGChatCommands(program);
42
52
  registerGitHubCommands(program);
53
+ registerGmailCommands(program);
54
+ registerGTasksCommands(program);
43
55
  registerJiraCommands(program);
44
- registerSlackCommands(program);
45
56
  registerRssCommands(program);
46
- registerDiscourseCommands(program);
57
+ registerSlackCommands(program);
47
58
  registerSqlCommands(program);
48
- registerUpdateCommand(program);
49
- registerConfigCommands(program);
59
+ registerTelegramCommands(program);
60
+ registerWhatsAppCommands(program);
61
+
62
+ // Agentio utilities
50
63
  registerClaudeCommands(program);
51
- registerStatusCommand(program);
64
+ registerConfigCommands(program);
52
65
  registerDocsCommand(program);
66
+ registerGatewayCommands(program);
67
+ registerStatusCommand(program);
68
+ registerUpdateCommand(program);
53
69
 
54
70
  program.parse();
@@ -0,0 +1,10 @@
1
+ // Polyfills for native binary compilation
2
+ // This must be imported BEFORE any code that uses protobufjs (e.g., Baileys)
3
+
4
+ import Long from 'long';
5
+ import protobuf from 'protobufjs';
6
+
7
+ // Fix for protobufjs Long support in Bun native binaries
8
+ // Without this, $util.Long.fromBits is undefined at runtime
9
+ protobuf.util.Long = Long;
10
+ protobuf.configure();