diff options
| author | Elvis Claros Castro <elvis@claros.ar> | 2026-09-26 19:26:33 -0300 |
|---|---|---|
| committer | Elvis Claros Castro <elvis@claros.ar> | 2026-09-26 19:26:33 -0300 |
| commit | d80640d01eeb84907e36ff4af33add9081eda17c (patch) | |
| tree | 27698b34e06e28d111c2196700015d9c82f78274 /src/session.js | |
| download | wa-cli-main.tar.gz wa-cli-main.zip | |
Diffstat (limited to 'src/session.js')
| -rw-r--r-- | src/session.js | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/src/session.js b/src/session.js new file mode 100644 index 0000000..9197636 --- /dev/null +++ b/src/session.js @@ -0,0 +1,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})`); + } +} |