@plosson/agentio 0.5.8 → 0.5.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plosson/agentio",
3
- "version": "0.5.8",
3
+ "version": "0.5.10",
4
4
  "description": "CLI for LLM agents to interact with communication and tracking services",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -57,7 +57,7 @@
57
57
  "commander": "^14.0.2",
58
58
  "google-auth-library": "^10.0.0",
59
59
  "libsodium-wrappers": "^0.8.1",
60
- "protobufjs": "^8.0.0",
60
+ "long": "^5.3.2",
61
61
  "qrcode-terminal": "^0.12.0",
62
62
  "rss-parser": "^3.13.0"
63
63
  }
@@ -7,7 +7,7 @@ import { password } from '@inquirer/prompts';
7
7
  import { handleError, CliError } from '../utils/errors';
8
8
  import { loadConfig, saveConfig } from '../config/config-manager';
9
9
  import { startGateway, getGatewayConfig, LOG_FILE } from '../gateway/daemon';
10
- import { exportWhatsAppAuthState } from '../gateway/store';
10
+ import { initDatabase, closeDatabase, exportWhatsAppAuthState } from '../gateway/store';
11
11
  import { isInteractive } from '../utils/interactive';
12
12
  import type { Config } from '../types/config';
13
13
 
@@ -518,32 +518,40 @@ export function registerGatewayCommands(program: Command): void {
518
518
  if (options.service === 'all' || options.service === 'whatsapp') {
519
519
  const profiles = config.profiles.whatsapp || [];
520
520
 
521
- for (const entry of profiles) {
522
- const profileName = typeof entry === 'string' ? entry : entry.name;
523
- console.log(` Exporting whatsapp:${profileName}...`);
524
- const authState = await exportWhatsAppAuthState(profileName);
521
+ if (profiles.length > 0) {
522
+ await initDatabase();
523
+ }
525
524
 
526
- if (!authState) {
527
- console.log(' No auth state found, skipping');
528
- continue;
529
- }
525
+ try {
526
+ for (const entry of profiles) {
527
+ const profileName = typeof entry === 'string' ? entry : entry.name;
528
+ console.log(` Exporting whatsapp:${profileName}...`);
529
+ const authState = await exportWhatsAppAuthState(profileName);
530
530
 
531
- // Send to remote gateway
532
- const response = await fetch(`${url}/import/whatsapp/${encodeURIComponent(profileName)}`, {
533
- method: 'POST',
534
- headers: {
535
- 'Content-Type': 'application/json',
536
- 'X-API-Key': key,
537
- },
538
- body: JSON.stringify(authState),
539
- });
540
-
541
- if (!response.ok) {
542
- const error = await response.text();
543
- throw new CliError('API_ERROR', `Failed to import to remote: ${error}`);
544
- }
531
+ if (!authState) {
532
+ console.log(' No auth state found, skipping');
533
+ continue;
534
+ }
545
535
 
546
- console.log(' Transferred successfully');
536
+ // Send to remote gateway
537
+ const response = await fetch(`${url}/import/whatsapp/${encodeURIComponent(profileName)}`, {
538
+ method: 'POST',
539
+ headers: {
540
+ 'Content-Type': 'application/json',
541
+ 'X-API-Key': key,
542
+ },
543
+ body: JSON.stringify(authState),
544
+ });
545
+
546
+ if (!response.ok) {
547
+ const error = await response.text();
548
+ throw new CliError('API_ERROR', `Failed to import to remote: ${error}`);
549
+ }
550
+
551
+ console.log(' Transferred successfully');
552
+ }
553
+ } finally {
554
+ closeDatabase();
547
555
  }
548
556
  }
549
557
 
@@ -75,6 +75,10 @@ export class WhatsAppAdapter extends BaseAdapter {
75
75
 
76
76
  private profiles: Map<string, ProfileConnection> = new Map();
77
77
 
78
+ hasProfile(profile: string): boolean {
79
+ return this.profiles.has(profile);
80
+ }
81
+
78
82
  async connect(profile: string, credentials: unknown): Promise<void> {
79
83
  const creds = credentials as WhatsAppCredentials;
80
84
 
@@ -52,13 +52,31 @@ import {
52
52
  importWhatsAppAuthState,
53
53
  type WhatsAppAuthExport,
54
54
  } from './store';
55
- import type { ServiceAdapter } from './adapters/types';
56
- import type { WhatsAppAdapter } from './adapters/whatsapp';
55
+ import type { ServiceAdapter, AdapterInboundMessage } from './adapters/types';
56
+ import { WhatsAppAdapter } from './adapters/whatsapp';
57
57
 
58
58
  let server: Server<unknown> | null = null;
59
59
  let apiKey: string = '';
60
60
  let startTime: number = 0;
61
61
  let adapters: Map<ServiceName, ServiceAdapter> = new Map();
62
+ let onAdapterMessage: ((service: ServiceName, profile: string, message: AdapterInboundMessage) => void) | null = null;
63
+
64
+ /**
65
+ * Get or lazily create the WhatsApp adapter
66
+ */
67
+ function getOrCreateWhatsAppAdapter(): WhatsAppAdapter {
68
+ let adapter = adapters.get('whatsapp') as WhatsAppAdapter | undefined;
69
+ if (!adapter) {
70
+ adapter = new WhatsAppAdapter();
71
+ adapter.onMessage = (profile, message) => {
72
+ if (onAdapterMessage) {
73
+ onAdapterMessage('whatsapp', profile, message);
74
+ }
75
+ };
76
+ adapters.set('whatsapp', adapter);
77
+ }
78
+ return adapter;
79
+ }
62
80
 
63
81
  /**
64
82
  * JSON error response helper
@@ -333,19 +351,39 @@ function handleMedia(id: string): Response {
333
351
  /**
334
352
  * Handle WhatsApp pairing request
335
353
  */
336
- function handleWhatsAppPair(profile: string): Response {
337
- const whatsappAdapter = adapters.get('whatsapp') as WhatsAppAdapter | undefined;
354
+ async function handleWhatsAppPair(profile: string): Promise<Response> {
355
+ const whatsappAdapter = getOrCreateWhatsAppAdapter();
338
356
 
339
- if (!whatsappAdapter) {
357
+ // If the profile isn't known to the adapter yet, start connecting it
358
+ const state = whatsappAdapter.getWhatsAppState(profile);
359
+ if (!state.connected && !whatsappAdapter.hasProfile(profile)) {
360
+ try {
361
+ await whatsappAdapter.connect(profile, { paired: false });
362
+ } catch (error) {
363
+ const message = error instanceof Error ? error.message : 'Failed to start pairing';
364
+ const response: WhatsAppPairResponse = {
365
+ status: 'connecting',
366
+ message,
367
+ };
368
+ return jsonResponse(response);
369
+ }
370
+ // Re-check state after connect
371
+ const newState = whatsappAdapter.getWhatsAppState(profile);
372
+ if (newState.qrCode) {
373
+ const response: WhatsAppPairResponse = {
374
+ status: 'waiting_qr',
375
+ qrCode: newState.qrCode,
376
+ message: 'Scan QR code with WhatsApp on your phone',
377
+ };
378
+ return jsonResponse(response);
379
+ }
340
380
  const response: WhatsAppPairResponse = {
341
- status: 'not_configured',
342
- message: 'WhatsApp is not configured. Add a profile first.',
381
+ status: 'connecting',
382
+ message: newState.error || 'Connecting to WhatsApp...',
343
383
  };
344
384
  return jsonResponse(response);
345
385
  }
346
386
 
347
- const state = whatsappAdapter.getWhatsAppState(profile);
348
-
349
387
  if (state.connected) {
350
388
  const response: WhatsAppPairResponse = {
351
389
  status: 'connected',
@@ -390,13 +428,14 @@ async function handleWhatsAppImport(profile: string, request: Request): Promise<
390
428
  });
391
429
 
392
430
  // Disconnect and reconnect WhatsApp adapter to use new credentials
393
- const whatsappAdapter = adapters.get('whatsapp') as WhatsAppAdapter | undefined;
394
- if (whatsappAdapter) {
431
+ const whatsappAdapter = getOrCreateWhatsAppAdapter();
432
+ if (whatsappAdapter.hasProfile(profile)) {
395
433
  await whatsappAdapter.disconnect(profile);
396
- // Note: reconnection will happen on next daemon cycle or manual reload
397
434
  }
435
+ // Reconnect with imported auth state
436
+ await whatsappAdapter.connect(profile, { paired: true });
398
437
 
399
- return jsonResponse({ success: true, message: 'Auth state imported. Reload gateway to reconnect.' });
438
+ return jsonResponse({ success: true, message: 'Auth state imported and reconnecting.' });
400
439
  } catch (error) {
401
440
  const message = error instanceof Error ? error.message : 'Import failed';
402
441
  return jsonError(message, 500);
@@ -753,12 +792,17 @@ async function handleRequest(request: Request): Promise<Response> {
753
792
  /**
754
793
  * Start the API server
755
794
  */
756
- export function startApiServer(config: GatewayConfig, serviceAdapters: Map<ServiceName, ServiceAdapter>): Server<unknown> {
795
+ export function startApiServer(
796
+ config: GatewayConfig,
797
+ serviceAdapters: Map<ServiceName, ServiceAdapter>,
798
+ messageHandler?: (service: ServiceName, profile: string, message: AdapterInboundMessage) => void,
799
+ ): Server<unknown> {
757
800
  const port = config?.server?.port ?? DEFAULT_GATEWAY_CONFIG.server.port;
758
801
  const host = config?.server?.host ?? DEFAULT_GATEWAY_CONFIG.server.host;
759
802
  apiKey = config?.apiKey ?? '';
760
803
  startTime = Date.now();
761
804
  adapters = serviceAdapters;
805
+ onAdapterMessage = messageHandler ?? null;
762
806
 
763
807
  server = Bun.serve({
764
808
  port,
@@ -274,7 +274,7 @@ export async function startGateway(): Promise<void> {
274
274
  await initializeAdapters();
275
275
 
276
276
  // Start API server
277
- startApiServer(gatewayConfig, adapters);
277
+ startApiServer(gatewayConfig, adapters, handleInboundMessage);
278
278
 
279
279
  // Start outbox processor (every 2 seconds)
280
280
  outboxInterval = setInterval(processOutbox, 2000);
package/src/polyfills.ts CHANGED
@@ -2,9 +2,9 @@
2
2
  // This must be imported BEFORE any code that uses protobufjs (e.g., Baileys)
3
3
 
4
4
  import Long from 'long';
5
- import protobuf from 'protobufjs';
6
5
 
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();
6
+ // Fix for protobufjs Long support in Bun native binaries.
7
+ // protobufjs/minimal uses inquire("long") with eval-based require that fails in Bun bundles.
8
+ // It falls back to checking global.Long (util/minimal.js:181), so we set it on globalThis
9
+ // BEFORE any protobufjs code initializes (WAProto evaluates $util.Long at module load time).
10
+ (globalThis as any).Long = Long;