1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
// CLI side: find (or start) the account daemon and talk to it.
import { spawn } from 'node:child_process';
import fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { loadConfig, paths } from './config.js';
import { WaError, call, ping } from './rpc.js';
const BIN = fileURLToPath(new URL('../bin/wa.js', import.meta.url));
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
export class Session {
constructor(account, { autostart } = {}) {
this.account = account;
this.paths = paths(account);
this.config = loadConfig(account);
this.autostart = autostart ?? this.config.autostart;
}
running() {
return ping(this.paths.socket);
}
async call(method, params = {}, opts = {}) {
await this.ensureDaemon();
try {
return await call(this.paths.socket, method, params, opts);
} catch (e) {
if (e.syscall === 'connect') throw new WaError('EDAEMON', `could not connect to the daemon (${e.code})`);
throw e;
}
}
async ensureDaemon() {
if (await this.running()) return;
if (!this.autostart) {
throw new WaError('EDAEMON', `the daemon for account "${this.account}" is not running: ` +
`\`wa daemon start\` or \`systemctl --user start wa-daemon@${this.account}\``);
}
await this.startBackground();
}
async startBackground() {
const P = this.paths;
fs.mkdirSync(P.stateDir, { recursive: true, mode: 0o700 });
const logFd = fs.openSync(P.logFile, 'a');
if (process.stderr.isTTY) process.stderr.write(`wa: starting the daemon (account ${this.account}), log in ${P.logFile}\n`);
const child = spawn(process.execPath, [BIN, 'daemon', 'start', '--foreground', '--account', this.account], {
detached: true,
stdio: ['ignore', logFd, logFd],
env: { ...process.env, WA_CLI_ACCOUNT: this.account },
});
fs.closeSync(logFd);
let exited = null;
child.on('exit', (code) => { exited = code; });
child.unref();
const deadline = Date.now() + 30000;
while (Date.now() < deadline) {
if (await this.running()) return;
if (exited !== null) {
const tail = fs.readFileSync(P.logFile, 'utf8').trimEnd().split('\n').slice(-5).join('\n');
throw new WaError('EDAEMON', `the daemon exited while starting (code ${exited}):\n${tail}`);
}
await sleep(150);
}
throw new WaError('EDAEMON', `the daemon did not open the socket within 30 s (see ${P.logFile})`);
}
}
|