// Paths, accounts and user config. // // Everything follows XDG, split per account (like gws with several accounts): // config ~/.config/wa-cli/config.json, hooks/ // session ~/.local/share/wa-cli// (LocalAuth: Chrome profile) // state ~/.local/state/wa-cli// (pid, daemon log) // media ~/.local/share/wa-cli//media/ // socket $XDG_RUNTIME_DIR/wa-cli/.sock import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; const env = process.env; const HOME = os.homedir(); const XDG_CONFIG = env.XDG_CONFIG_HOME || path.join(HOME, '.config'); const XDG_DATA = env.XDG_DATA_HOME || path.join(HOME, '.local/share'); const XDG_STATE = env.XDG_STATE_HOME || path.join(HOME, '.local/state'); const XDG_CACHE = env.XDG_CACHE_HOME || path.join(HOME, '.cache'); const RUNTIME = env.XDG_RUNTIME_DIR || path.join(os.tmpdir(), `wa-cli-${process.getuid()}`); const ACCOUNT_RE = /^[A-Za-z0-9_-]+$/; export const VERSION = JSON.parse( fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8'), ).version; export function accountName(flag) { const name = flag || env.WA_CLI_ACCOUNT || 'default'; if (!ACCOUNT_RE.test(name)) { throw Object.assign(new Error(`invalid account name: ${name} (only [A-Za-z0-9_-])`), { code: 'EINVAL' }); } return name; } export function paths(account) { const configDir = env.WA_CLI_CONFIG_DIR || path.join(XDG_CONFIG, 'wa-cli'); const dataDir = path.join(env.WA_CLI_DATA_DIR || path.join(XDG_DATA, 'wa-cli'), account); const stateDir = path.join(XDG_STATE, 'wa-cli', account); return { configDir, configFile: path.join(configDir, 'config.json'), hooksDir: path.join(configDir, 'hooks'), dataDir, mediaDir: path.join(dataDir, 'media'), stateDir, pidFile: path.join(stateDir, 'daemon.pid'), logFile: path.join(stateDir, 'daemon.log'), cacheDir: path.join(XDG_CACHE, 'wa-cli'), socket: env.WA_CLI_SOCKET || path.join(RUNTIME, 'wa-cli', `${account}.sock`), }; } const DEFAULTS = { autostart: true, // the CLI starts the daemon if it is not running readyTimeoutMs: 120000, // how long a command waits for the session to be ready startTimeoutMs: 180000, // if WhatsApp Web does not load within this time, the daemon exits (and is restarted) hooks: true, // ejecutar ~/.config/wa-cli/hooks/ hookTimeoutMs: 60000, browserName: 'wa-cli', // shows as "wa-cli (Linux)" under Linked devices deviceName: 'Linux', puppeteer: { headless: true }, }; // config.json: { ...globals, "accounts": { "": { ...overrides } } } export function loadConfig(account) { const p = paths(account); let file = {}; try { file = JSON.parse(fs.readFileSync(p.configFile, 'utf8')); } catch (e) { if (e.code !== 'ENOENT') { throw Object.assign(new Error(`${p.configFile}: ${e.message}`), { code: 'EINVAL' }); } } const { accounts = {}, ...global } = file; const cfg = { ...DEFAULTS, ...global, ...(accounts[account] || {}) }; cfg.puppeteer = { ...DEFAULTS.puppeteer, ...global.puppeteer, ...accounts[account]?.puppeteer }; cfg.wamail = { ...global.wamail, ...accounts[account]?.wamail }; if (env.WA_CLI_AUTOSTART !== undefined) cfg.autostart = !/^(0|false|no)$/i.test(env.WA_CLI_AUTOSTART); return cfg; }