@vielhuber/wahelper 1.8.6 → 1.8.8
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/README.md +6 -0
- package/package.json +1 -1
- package/wahelper-daemon.js +85 -36
- package/wahelper.js +76 -25
package/README.md
CHANGED
|
@@ -53,6 +53,9 @@ npx wahelper \
|
|
|
53
53
|
--device "xxxxxxxxxxxx" \
|
|
54
54
|
...
|
|
55
55
|
|
|
56
|
+
# check the live connection
|
|
57
|
+
--action "get_status"
|
|
58
|
+
|
|
56
59
|
# fetch messages
|
|
57
60
|
--action "fetch_messages" \
|
|
58
61
|
--filter '{"from":"491234567890","to":"491234567890","message":"meeting","date_from":"2026-01-01","date_until":"2026-12-31"}' \
|
|
@@ -85,6 +88,9 @@ use vielhuber\wahelper\wahelper;
|
|
|
85
88
|
|
|
86
89
|
$wahelper = new wahelper();
|
|
87
90
|
|
|
91
|
+
// success is true only when connected
|
|
92
|
+
$status = $wahelper->getStatus(device: 'xxxxxxxxxxxx');
|
|
93
|
+
|
|
88
94
|
// fetch messages
|
|
89
95
|
$wahelper->fetchMessages(
|
|
90
96
|
device: 'xxxxxxxxxxxx',
|
package/package.json
CHANGED
package/wahelper-daemon.js
CHANGED
|
@@ -36,6 +36,7 @@ export default class wahelperDaemon {
|
|
|
36
36
|
this.dbLock = false;
|
|
37
37
|
this.connected = false;
|
|
38
38
|
this.connecting = false;
|
|
39
|
+
this.loggedOut = false;
|
|
39
40
|
this.qr = null;
|
|
40
41
|
this.pairingCode = null;
|
|
41
42
|
this.pairingCodeRequested = false;
|
|
@@ -276,6 +277,11 @@ export default class wahelperDaemon {
|
|
|
276
277
|
this.log('storeDataToDatabase');
|
|
277
278
|
|
|
278
279
|
let messages = Array.isArray(data?.messages) ? data.messages : [];
|
|
280
|
+
if (Array.isArray(data?.updates)) {
|
|
281
|
+
messages = data.updates
|
|
282
|
+
.filter(({ update }) => update?.message?.editedMessage)
|
|
283
|
+
.map(({ key, update }) => ({ ...update, key }));
|
|
284
|
+
}
|
|
279
285
|
let chats = Array.isArray(data) ? data : Array.isArray(data?.chats) ? data.chats : [];
|
|
280
286
|
if (messages.length === 0 && chats.length === 0) {
|
|
281
287
|
return;
|
|
@@ -301,14 +307,24 @@ export default class wahelperDaemon {
|
|
|
301
307
|
let query = this.db.prepare(`
|
|
302
308
|
INSERT OR IGNORE INTO messages
|
|
303
309
|
(id, \`from\`, \`to\`, content, media_data, media_filename, timestamp)
|
|
304
|
-
VALUES (
|
|
310
|
+
VALUES ($id, $from, $to, $content, $mediaData, $mediaFilename, $timestamp)
|
|
311
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
312
|
+
\`from\` = excluded.\`from\`,
|
|
313
|
+
\`to\` = excluded.\`to\`,
|
|
314
|
+
content = CASE WHEN $preserveContent THEN messages.content ELSE excluded.content END,
|
|
315
|
+
media_data = COALESCE(excluded.media_data, messages.media_data),
|
|
316
|
+
media_filename = COALESCE(excluded.media_filename, messages.media_filename),
|
|
317
|
+
timestamp = CASE WHEN $preserveTimestamp THEN messages.timestamp ELSE excluded.timestamp END
|
|
305
318
|
`);
|
|
306
319
|
|
|
307
320
|
for (let messages__value of messages) {
|
|
308
321
|
let id = messages__value.key?.id,
|
|
309
322
|
chatId = messages__value.key?.remoteJid,
|
|
310
323
|
fromMe = messages__value.key?.fromMe ? 1 : 0,
|
|
311
|
-
timestamp = messages__value.messageTimestamp;
|
|
324
|
+
timestamp = Number(messages__value.messageTimestamp ?? NaN);
|
|
325
|
+
let isEdited = messages__value.message?.editedMessage !== undefined;
|
|
326
|
+
// Edit events carry the edit time, not the original send time.
|
|
327
|
+
let preserveTimestamp = isEdited || isNaN(timestamp);
|
|
312
328
|
|
|
313
329
|
// status posts and newsletter items are not conversations. they carry
|
|
314
330
|
// their author in remoteJidAlt, so without this they would be stored
|
|
@@ -317,12 +333,7 @@ export default class wahelperDaemon {
|
|
|
317
333
|
continue;
|
|
318
334
|
}
|
|
319
335
|
|
|
320
|
-
if (timestamp
|
|
321
|
-
timestamp = Number(timestamp);
|
|
322
|
-
if (isNaN(timestamp)) {
|
|
323
|
-
timestamp = Math.floor(Date.now() / 1000);
|
|
324
|
-
}
|
|
325
|
-
} else {
|
|
336
|
+
if (isNaN(timestamp)) {
|
|
326
337
|
timestamp = Math.floor(Date.now() / 1000);
|
|
327
338
|
}
|
|
328
339
|
|
|
@@ -365,6 +376,7 @@ export default class wahelperDaemon {
|
|
|
365
376
|
}
|
|
366
377
|
|
|
367
378
|
let content = null,
|
|
379
|
+
preserveContent = false,
|
|
368
380
|
mediaFilename = null,
|
|
369
381
|
mediaData = null,
|
|
370
382
|
mediaBufferInput = null;
|
|
@@ -484,8 +496,9 @@ export default class wahelperDaemon {
|
|
|
484
496
|
}
|
|
485
497
|
|
|
486
498
|
// skip media download on first run (initial history sync after fresh pairing)
|
|
487
|
-
if (this.isFirstRun) {
|
|
499
|
+
if (this.isFirstRun && !isEdited) {
|
|
488
500
|
if (content === null || content === '') {
|
|
501
|
+
preserveContent = true;
|
|
489
502
|
content = '[Media message not downloaded on first run]';
|
|
490
503
|
}
|
|
491
504
|
mediaFilename = null;
|
|
@@ -506,12 +519,21 @@ export default class wahelperDaemon {
|
|
|
506
519
|
mediaData = buffer.toString('base64');
|
|
507
520
|
this.log('✅ Downloaded media ' + mediaFilename);
|
|
508
521
|
} catch (error) {
|
|
509
|
-
|
|
510
|
-
this.log('⚠️ Failed to download media: ' + error.message + '. Storing URL instead.');
|
|
522
|
+
this.log('⚠️ Failed to download media: ' + error.message + '. Leaving media data empty.');
|
|
511
523
|
}
|
|
512
524
|
}
|
|
513
525
|
|
|
514
|
-
query.run(
|
|
526
|
+
query.run({
|
|
527
|
+
id,
|
|
528
|
+
from,
|
|
529
|
+
to,
|
|
530
|
+
content,
|
|
531
|
+
mediaData,
|
|
532
|
+
mediaFilename,
|
|
533
|
+
timestamp,
|
|
534
|
+
preserveContent: Number(preserveContent),
|
|
535
|
+
preserveTimestamp: Number(preserveTimestamp)
|
|
536
|
+
});
|
|
515
537
|
count++;
|
|
516
538
|
|
|
517
539
|
if (length < 100 || count % 100 === 0) {
|
|
@@ -526,11 +548,11 @@ export default class wahelperDaemon {
|
|
|
526
548
|
this.db.exec('COMMIT');
|
|
527
549
|
this.log('END TRANSACTION');
|
|
528
550
|
if (count > 0) {
|
|
529
|
-
this.log('Stored ' + count + '
|
|
551
|
+
this.log('Stored ' + count + ' messages to database (' + length + ' total received)');
|
|
530
552
|
process.stdout.write(
|
|
531
553
|
'\r✅ Stored ' +
|
|
532
554
|
count +
|
|
533
|
-
'
|
|
555
|
+
' messages to database (' +
|
|
534
556
|
length +
|
|
535
557
|
' total received)' +
|
|
536
558
|
' '.repeat(10) +
|
|
@@ -677,8 +699,8 @@ export default class wahelperDaemon {
|
|
|
677
699
|
}
|
|
678
700
|
|
|
679
701
|
async sendMessageToUser(number = null, message = null, attachments = null) {
|
|
680
|
-
if (!this.connected
|
|
681
|
-
throw new Error('not_connected');
|
|
702
|
+
if (!this.getStatus().connected) {
|
|
703
|
+
throw new Error(this.loggedOut ? 'logged_out' : 'not_connected');
|
|
682
704
|
}
|
|
683
705
|
// validate all attachments exist before sending anything
|
|
684
706
|
if (attachments !== null && attachments.length > 0) {
|
|
@@ -703,8 +725,8 @@ export default class wahelperDaemon {
|
|
|
703
725
|
}
|
|
704
726
|
|
|
705
727
|
async sendMessageToGroup(name = null, message = null, attachments = null) {
|
|
706
|
-
if (!this.connected
|
|
707
|
-
throw new Error('not_connected');
|
|
728
|
+
if (!this.getStatus().connected) {
|
|
729
|
+
throw new Error(this.loggedOut ? 'logged_out' : 'not_connected');
|
|
708
730
|
}
|
|
709
731
|
// validate all attachments exist before sending anything
|
|
710
732
|
if (attachments !== null && attachments.length > 0) {
|
|
@@ -735,7 +757,7 @@ export default class wahelperDaemon {
|
|
|
735
757
|
}
|
|
736
758
|
|
|
737
759
|
connect() {
|
|
738
|
-
if (this.connecting) {
|
|
760
|
+
if (this.connecting || this.loggedOut) {
|
|
739
761
|
return;
|
|
740
762
|
}
|
|
741
763
|
this.connecting = true;
|
|
@@ -830,6 +852,7 @@ export default class wahelperDaemon {
|
|
|
830
852
|
|
|
831
853
|
socket.ev.on('messages.update', async updates => {
|
|
832
854
|
try {
|
|
855
|
+
await this.storeDataToDatabase({ updates });
|
|
833
856
|
await this.markMessagesRead(updates);
|
|
834
857
|
} catch (error) {
|
|
835
858
|
this.log('⛔ messages.update handler failed: ' + error.message);
|
|
@@ -863,10 +886,16 @@ export default class wahelperDaemon {
|
|
|
863
886
|
socket
|
|
864
887
|
.requestPairingCode(this.device)
|
|
865
888
|
.then(code => {
|
|
889
|
+
if (this.sock !== socket) {
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
866
892
|
this.pairingCode = code;
|
|
867
893
|
console.log('\nPairing code: ' + code);
|
|
868
894
|
})
|
|
869
895
|
.catch(err => {
|
|
896
|
+
if (this.sock !== socket) {
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
870
899
|
this.lastError = { source: 'pairing', message: err.message, at: Date.now() };
|
|
871
900
|
this.log('Pairing code request failed: ' + err.message);
|
|
872
901
|
});
|
|
@@ -895,16 +924,19 @@ export default class wahelperDaemon {
|
|
|
895
924
|
}
|
|
896
925
|
|
|
897
926
|
if (statusCode === DisconnectReason.loggedOut) {
|
|
898
|
-
|
|
927
|
+
this.loggedOut = true;
|
|
928
|
+
this.sock = null;
|
|
899
929
|
this.qr = null;
|
|
900
930
|
this.pairingCode = null;
|
|
901
931
|
this.pairingCodeRequested = false;
|
|
902
|
-
this.
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
932
|
+
this.lastError = {
|
|
933
|
+
source: 'disconnect',
|
|
934
|
+
message: 'Logged out. Automatic reconnect stopped; manual device linking required.',
|
|
935
|
+
statusCode,
|
|
936
|
+
at: Date.now()
|
|
937
|
+
};
|
|
938
|
+
this.log(this.lastError);
|
|
939
|
+
console.error(this.lastError.message);
|
|
908
940
|
return;
|
|
909
941
|
}
|
|
910
942
|
|
|
@@ -927,10 +959,16 @@ export default class wahelperDaemon {
|
|
|
927
959
|
source: 'disconnect',
|
|
928
960
|
message:
|
|
929
961
|
'connection closed' +
|
|
930
|
-
(reason
|
|
962
|
+
(reason
|
|
963
|
+
? ' (' + reason + ')'
|
|
964
|
+
: statusCode
|
|
965
|
+
? ' (statusCode=' + statusCode + ')'
|
|
966
|
+
: ''),
|
|
967
|
+
statusCode,
|
|
931
968
|
at: Date.now()
|
|
932
969
|
};
|
|
933
970
|
}
|
|
971
|
+
this.log(this.lastError);
|
|
934
972
|
this.consecutiveFailures++;
|
|
935
973
|
// first 3 attempts recover network blips fast (1s/2s/4s),
|
|
936
974
|
// after that back off to 15min so a persistent failure
|
|
@@ -978,6 +1016,19 @@ export default class wahelperDaemon {
|
|
|
978
1016
|
});
|
|
979
1017
|
}
|
|
980
1018
|
|
|
1019
|
+
getStatus() {
|
|
1020
|
+
return {
|
|
1021
|
+
success: true,
|
|
1022
|
+
connected: this.connected && this.sock?.ws?.isOpen === true,
|
|
1023
|
+
connecting: this.connecting,
|
|
1024
|
+
loggedOut: this.loggedOut,
|
|
1025
|
+
device: this.device,
|
|
1026
|
+
qr: this.qr,
|
|
1027
|
+
pairingCode: this.pairingCode,
|
|
1028
|
+
lastError: this.lastError
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
|
|
981
1032
|
startHttpServer() {
|
|
982
1033
|
this.httpServer = http.createServer(async (req, res) => {
|
|
983
1034
|
// bound to 127.0.0.1 below — every request additionally has to
|
|
@@ -1003,14 +1054,7 @@ export default class wahelperDaemon {
|
|
|
1003
1054
|
this.sendJsonResponse(res, 403, { success: false, message: 'forbidden' });
|
|
1004
1055
|
return;
|
|
1005
1056
|
}
|
|
1006
|
-
this.sendJsonResponse(res, 200,
|
|
1007
|
-
success: true,
|
|
1008
|
-
connected: this.connected,
|
|
1009
|
-
device: this.device,
|
|
1010
|
-
qr: this.qr,
|
|
1011
|
-
pairingCode: this.pairingCode,
|
|
1012
|
-
lastError: this.lastError
|
|
1013
|
-
});
|
|
1057
|
+
this.sendJsonResponse(res, 200, this.getStatus());
|
|
1014
1058
|
return;
|
|
1015
1059
|
}
|
|
1016
1060
|
|
|
@@ -1173,7 +1217,12 @@ export default class wahelperDaemon {
|
|
|
1173
1217
|
}
|
|
1174
1218
|
}
|
|
1175
1219
|
|
|
1176
|
-
|
|
1220
|
+
// Importing the class must not start the daemon or connect to WhatsApp.
|
|
1221
|
+
if (
|
|
1222
|
+
process.argv[1] &&
|
|
1223
|
+
fs.existsSync(process.argv[1]) &&
|
|
1224
|
+
fs.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
1225
|
+
) {
|
|
1177
1226
|
let daemon = new wahelperDaemon();
|
|
1178
1227
|
daemon.init();
|
|
1179
1228
|
}
|
package/wahelper.js
CHANGED
|
@@ -26,7 +26,11 @@ export default class wahelper {
|
|
|
26
26
|
this.authFolder = 'auth_' + this.formatNumber(this.args.device);
|
|
27
27
|
this.dbPath = 'whatsapp_' + this.formatNumber(this.args.device) + '.sqlite';
|
|
28
28
|
this.logPath = 'whatsapp_' + this.formatNumber(this.args.device) + '.log';
|
|
29
|
-
this.
|
|
29
|
+
if (this.args.request_id !== undefined && !/^[a-f0-9]{32}$/.test(this.args.request_id)) {
|
|
30
|
+
throw new Error('Invalid request id');
|
|
31
|
+
}
|
|
32
|
+
let requestSuffix = this.args.request_id === undefined ? '' : '_' + this.args.request_id;
|
|
33
|
+
this.dataPath = 'whatsapp_' + this.formatNumber(this.args.device) + requestSuffix + '.json';
|
|
30
34
|
this.port = this.computePort(this.formatNumber(this.args.device));
|
|
31
35
|
this.authToken = this.getAuthToken();
|
|
32
36
|
}
|
|
@@ -49,6 +53,28 @@ export default class wahelper {
|
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
async init() {
|
|
56
|
+
if (this.args.action === 'get_status') {
|
|
57
|
+
let status = this.args.device
|
|
58
|
+
? await this.callDaemon('GET', '/status')
|
|
59
|
+
: { success: false, message: 'missing_device' };
|
|
60
|
+
let connected = status.connected === true;
|
|
61
|
+
let response = {
|
|
62
|
+
success: connected,
|
|
63
|
+
message: connected ? 'connected' : status.loggedOut ? 'logged_out' : status.message || 'not_connected',
|
|
64
|
+
public_message: status.lastError?.message ?? null,
|
|
65
|
+
data: {
|
|
66
|
+
connected,
|
|
67
|
+
connecting: status.connecting === true,
|
|
68
|
+
loggedOut: status.loggedOut === true,
|
|
69
|
+
pairingRequired: status.loggedOut === true || Boolean(status.pairingCode || status.qr),
|
|
70
|
+
lastError: status.lastError ?? null
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
this.write(response, false);
|
|
74
|
+
console.log(JSON.stringify(response));
|
|
75
|
+
process.exitCode = connected ? 0 : 1;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
52
78
|
await this.awaitLock('init', true);
|
|
53
79
|
this.setLock('init', true);
|
|
54
80
|
this.write({ success: false, message: 'loading_state', data: null }, false);
|
|
@@ -123,7 +149,11 @@ export default class wahelper {
|
|
|
123
149
|
console.log('\n⚠️ Pairing required. Scan the QR code with WhatsApp, then retry.\n');
|
|
124
150
|
console.log(daemonStatus.qrString);
|
|
125
151
|
}
|
|
126
|
-
if (
|
|
152
|
+
if (
|
|
153
|
+
daemonStatus.message === 'daemon_error' ||
|
|
154
|
+
daemonStatus.message === 'daemon_timeout' ||
|
|
155
|
+
daemonStatus.message === 'logged_out'
|
|
156
|
+
) {
|
|
127
157
|
console.log('⛔ ' + (daemonStatus.public_message || daemonStatus.message));
|
|
128
158
|
}
|
|
129
159
|
this.write(
|
|
@@ -199,6 +229,7 @@ export default class wahelper {
|
|
|
199
229
|
};
|
|
200
230
|
} catch (error) {
|
|
201
231
|
this.log('⛔ Error fetching database: ' + error.message + ' (code: ' + error.code + ')');
|
|
232
|
+
this.write({ success: false, message: 'error', data: null }, true);
|
|
202
233
|
}
|
|
203
234
|
return null;
|
|
204
235
|
}
|
|
@@ -269,6 +300,7 @@ export default class wahelper {
|
|
|
269
300
|
};
|
|
270
301
|
} catch (error) {
|
|
271
302
|
this.log('⛔ Error fetching message from database: ' + error.message + ' (code: ' + error.code + ')');
|
|
303
|
+
this.write({ success: false, message: 'error', data: null }, true);
|
|
272
304
|
}
|
|
273
305
|
return null;
|
|
274
306
|
}
|
|
@@ -310,26 +342,25 @@ export default class wahelper {
|
|
|
310
342
|
}
|
|
311
343
|
|
|
312
344
|
async ensureDaemon() {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (status.connected) {
|
|
322
|
-
return status;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// poll up to 30s — return immediately when pairing code appears
|
|
326
|
-
console.log('Waiting for daemon to connect...');
|
|
327
|
-
for (let i = 0; i < 30; i++) {
|
|
328
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
329
|
-
status = await this.callDaemon('GET', '/status');
|
|
345
|
+
for (let i = 0; i <= 30; i++) {
|
|
346
|
+
let status = await this.callDaemon('GET', '/status');
|
|
347
|
+
if (status.success === false) {
|
|
348
|
+
return {
|
|
349
|
+
connected: false,
|
|
350
|
+
message: status.message === 'daemon_not_reachable' ? 'daemon_not_running' : status.message
|
|
351
|
+
};
|
|
352
|
+
}
|
|
330
353
|
if (status.connected) {
|
|
331
354
|
return status;
|
|
332
355
|
}
|
|
356
|
+
if (status.loggedOut) {
|
|
357
|
+
return {
|
|
358
|
+
connected: false,
|
|
359
|
+
message: 'logged_out',
|
|
360
|
+
public_message: status.lastError?.message,
|
|
361
|
+
data: status.lastError
|
|
362
|
+
};
|
|
363
|
+
}
|
|
333
364
|
if (status.pairingCode) {
|
|
334
365
|
return { connected: false, message: 'pairing_required', pairingCode: status.pairingCode };
|
|
335
366
|
}
|
|
@@ -347,6 +378,12 @@ export default class wahelper {
|
|
|
347
378
|
data: status.lastError
|
|
348
379
|
};
|
|
349
380
|
}
|
|
381
|
+
if (i === 0) {
|
|
382
|
+
console.log('Waiting for daemon to connect...');
|
|
383
|
+
}
|
|
384
|
+
if (i < 30) {
|
|
385
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
386
|
+
}
|
|
350
387
|
}
|
|
351
388
|
return { connected: false, message: 'daemon_timeout' };
|
|
352
389
|
}
|
|
@@ -394,7 +431,7 @@ export default class wahelper {
|
|
|
394
431
|
}
|
|
395
432
|
});
|
|
396
433
|
});
|
|
397
|
-
req.setTimeout(30000, () => {
|
|
434
|
+
req.setTimeout(path === '/status' ? 5000 : 30000, () => {
|
|
398
435
|
req.destroy();
|
|
399
436
|
this.log('⛔ Daemon request timeout');
|
|
400
437
|
resolve({ success: false, message: 'daemon_timeout' });
|
|
@@ -576,7 +613,14 @@ export default class wahelper {
|
|
|
576
613
|
this.writeOnEnd = msg;
|
|
577
614
|
return;
|
|
578
615
|
}
|
|
579
|
-
|
|
616
|
+
let responsePath = this.dirname + '/' + this.dataPath;
|
|
617
|
+
let temporaryPath = responsePath + '.' + crypto.randomUUID() + '.tmp';
|
|
618
|
+
try {
|
|
619
|
+
fs.writeFileSync(temporaryPath, JSON.stringify(msg), { flag: 'wx' });
|
|
620
|
+
fs.renameSync(temporaryPath, responsePath);
|
|
621
|
+
} finally {
|
|
622
|
+
fs.rmSync(temporaryPath, { force: true });
|
|
623
|
+
}
|
|
580
624
|
}
|
|
581
625
|
|
|
582
626
|
initDatabase() {
|
|
@@ -621,7 +665,14 @@ export default class wahelper {
|
|
|
621
665
|
}
|
|
622
666
|
}
|
|
623
667
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
.
|
|
627
|
-
.
|
|
668
|
+
// Importing the class must not start the CLI or terminate the caller.
|
|
669
|
+
if (
|
|
670
|
+
process.argv[1] &&
|
|
671
|
+
fs.existsSync(process.argv[1]) &&
|
|
672
|
+
fs.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
673
|
+
) {
|
|
674
|
+
let wa = new wahelper();
|
|
675
|
+
wa.init()
|
|
676
|
+
.then(() => process.exit(process.exitCode ?? 0))
|
|
677
|
+
.catch(() => process.exit(1));
|
|
678
|
+
}
|