@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 +2 -2
- package/src/commands/gateway.ts +32 -24
- package/src/gateway/adapters/whatsapp.ts +4 -0
- package/src/gateway/api.ts +58 -14
- package/src/gateway/daemon.ts +1 -1
- package/src/polyfills.ts +5 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plosson/agentio",
|
|
3
|
-
"version": "0.5.
|
|
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
|
-
"
|
|
60
|
+
"long": "^5.3.2",
|
|
61
61
|
"qrcode-terminal": "^0.12.0",
|
|
62
62
|
"rss-parser": "^3.13.0"
|
|
63
63
|
}
|
package/src/commands/gateway.ts
CHANGED
|
@@ -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
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
const authState = await exportWhatsAppAuthState(profileName);
|
|
521
|
+
if (profiles.length > 0) {
|
|
522
|
+
await initDatabase();
|
|
523
|
+
}
|
|
525
524
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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
|
-
|
|
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
|
|
package/src/gateway/api.ts
CHANGED
|
@@ -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
|
|
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 =
|
|
354
|
+
async function handleWhatsAppPair(profile: string): Promise<Response> {
|
|
355
|
+
const whatsappAdapter = getOrCreateWhatsAppAdapter();
|
|
338
356
|
|
|
339
|
-
|
|
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: '
|
|
342
|
-
message:
|
|
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 =
|
|
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
|
|
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(
|
|
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,
|
package/src/gateway/daemon.ts
CHANGED
|
@@ -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
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
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;
|