UNPKG

@gguf/claw

Version:

WhatsApp gateway CLI (Baileys web) with Pi RPC agent

460 lines (454 loc) 16.4 kB
import { t as formatCliCommand } from "./command-format-3xiXujG0.js"; import { M as VERSION, k as collectConfigEnvVars } from "./config-DCT1RAo6.js"; import { a as NODE_SERVICE_MARKER, d as resolveGatewaySystemdServiceName, g as resolveNodeWindowsTaskName, h as resolveNodeSystemdServiceName, i as NODE_SERVICE_KIND, l as resolveGatewayLaunchAgentLabel, m as resolveNodeLaunchAgentLabel, n as GATEWAY_SERVICE_KIND, o as NODE_WINDOWS_TASK_SCRIPT_NAME, r as GATEWAY_SERVICE_MARKER } from "./constants-CLUi6T-M.js"; import { n as isSupportedNodeVersion } from "./runtime-guard-DvBpNsbR.js"; import { execFile } from "node:child_process"; import path from "node:path"; import { promisify } from "node:util"; import fs from "node:fs/promises"; //#region src/daemon/program-args.ts function isNodeRuntime(execPath) { const base = path.basename(execPath).toLowerCase(); return base === "node" || base === "node.exe"; } function isBunRuntime(execPath) { const base = path.basename(execPath).toLowerCase(); return base === "bun" || base === "bun.exe"; } async function resolveCliEntrypointPathForService() { const argv1 = process.argv[1]; if (!argv1) throw new Error("Unable to resolve CLI entrypoint path"); const normalized = path.resolve(argv1); const resolvedPath = await resolveRealpathSafe(normalized); if (/[/\\]dist[/\\].+\.(cjs|js|mjs)$/.test(resolvedPath)) { await fs.access(resolvedPath); if (/[/\\]dist[/\\].+\.(cjs|js|mjs)$/.test(normalized) && normalized !== resolvedPath) try { await fs.access(normalized); return normalized; } catch {} return resolvedPath; } const distCandidates = buildDistCandidates(resolvedPath, normalized); for (const candidate of distCandidates) try { await fs.access(candidate); return candidate; } catch {} throw new Error(`Cannot find built CLI at ${distCandidates.join(" or ")}. Run "pnpm build" first, or use dev mode.`); } async function resolveRealpathSafe(inputPath) { try { return await fs.realpath(inputPath); } catch { return inputPath; } } function buildDistCandidates(...inputs) { const candidates = []; const seen = /* @__PURE__ */ new Set(); for (const inputPath of inputs) { if (!inputPath) continue; const baseDir = path.dirname(inputPath); appendDistCandidates(candidates, seen, path.resolve(baseDir, "..")); appendDistCandidates(candidates, seen, baseDir); appendNodeModulesBinCandidates(candidates, seen, inputPath); } return candidates; } function appendDistCandidates(candidates, seen, baseDir) { const distDir = path.resolve(baseDir, "dist"); const distEntries = [ path.join(distDir, "index.js"), path.join(distDir, "index.mjs"), path.join(distDir, "entry.js"), path.join(distDir, "entry.mjs") ]; for (const entry of distEntries) { if (seen.has(entry)) continue; seen.add(entry); candidates.push(entry); } } function appendNodeModulesBinCandidates(candidates, seen, inputPath) { const parts = inputPath.split(path.sep); const binIndex = parts.lastIndexOf(".bin"); if (binIndex <= 0) return; if (parts[binIndex - 1] !== "node_modules") return; const binName = path.basename(inputPath); const nodeModulesDir = parts.slice(0, binIndex).join(path.sep); appendDistCandidates(candidates, seen, path.join(nodeModulesDir, binName)); } function resolveRepoRootForDev() { const argv1 = process.argv[1]; if (!argv1) throw new Error("Unable to resolve repo root"); const parts = path.resolve(argv1).split(path.sep); const srcIndex = parts.lastIndexOf("src"); if (srcIndex === -1) throw new Error("Dev mode requires running from repo (src/index.ts)"); return parts.slice(0, srcIndex).join(path.sep); } async function resolveBunPath() { return await resolveBinaryPath("bun"); } async function resolveNodePath() { return await resolveBinaryPath("node"); } async function resolveBinaryPath(binary) { const { execSync } = await import("node:child_process"); const cmd = process.platform === "win32" ? "where" : "which"; try { const resolved = execSync(`${cmd} ${binary}`, { encoding: "utf8" }).trim().split(/\r?\n/)[0]?.trim(); if (!resolved) throw new Error("empty"); await fs.access(resolved); return resolved; } catch { if (binary === "bun") throw new Error("Bun not found in PATH. Install bun: https://bun.sh"); throw new Error("Node not found in PATH. Install Node 22+."); } } async function resolveCliProgramArguments(params) { const execPath = process.execPath; const runtime = params.runtime ?? "auto"; if (runtime === "node") return { programArguments: [ params.nodePath ?? (isNodeRuntime(execPath) ? execPath : await resolveNodePath()), await resolveCliEntrypointPathForService(), ...params.args ] }; if (runtime === "bun") { if (params.dev) { const repoRoot = resolveRepoRootForDev(); const devCliPath = path.join(repoRoot, "src", "index.ts"); await fs.access(devCliPath); return { programArguments: [ isBunRuntime(execPath) ? execPath : await resolveBunPath(), devCliPath, ...params.args ], workingDirectory: repoRoot }; } return { programArguments: [ isBunRuntime(execPath) ? execPath : await resolveBunPath(), await resolveCliEntrypointPathForService(), ...params.args ] }; } if (!params.dev) try { return { programArguments: [ execPath, await resolveCliEntrypointPathForService(), ...params.args ] }; } catch (error) { if (!isNodeRuntime(execPath)) return { programArguments: [execPath, ...params.args] }; throw error; } const repoRoot = resolveRepoRootForDev(); const devCliPath = path.join(repoRoot, "src", "index.ts"); await fs.access(devCliPath); if (isBunRuntime(execPath)) return { programArguments: [ execPath, devCliPath, ...params.args ], workingDirectory: repoRoot }; return { programArguments: [ await resolveBunPath(), devCliPath, ...params.args ], workingDirectory: repoRoot }; } async function resolveGatewayProgramArguments(params) { return resolveCliProgramArguments({ args: [ "gateway", "--port", String(params.port) ], dev: params.dev, runtime: params.runtime, nodePath: params.nodePath }); } async function resolveNodeProgramArguments(params) { const args = [ "node", "run", "--host", params.host, "--port", String(params.port) ]; if (params.tls || params.tlsFingerprint) args.push("--tls"); if (params.tlsFingerprint) args.push("--tls-fingerprint", params.tlsFingerprint); if (params.nodeId) args.push("--node-id", params.nodeId); if (params.displayName) args.push("--display-name", params.displayName); return resolveCliProgramArguments({ args, dev: params.dev, runtime: params.runtime, nodePath: params.nodePath }); } //#endregion //#region src/daemon/runtime-paths.ts const VERSION_MANAGER_MARKERS = [ "/.nvm/", "/.fnm/", "/.volta/", "/.asdf/", "/.n/", "/.nodenv/", "/.nodebrew/", "/nvs/" ]; function getPathModule(platform) { return platform === "win32" ? path.win32 : path.posix; } function normalizeForCompare(input, platform) { const normalized = getPathModule(platform).normalize(input).replaceAll("\\", "/"); if (platform === "win32") return normalized.toLowerCase(); return normalized; } function buildSystemNodeCandidates(env, platform) { if (platform === "darwin") return [ "/opt/homebrew/bin/node", "/usr/local/bin/node", "/usr/bin/node" ]; if (platform === "linux") return ["/usr/local/bin/node", "/usr/bin/node"]; if (platform === "win32") { const pathModule = getPathModule(platform); const programFiles = env.ProgramFiles ?? "C:\\Program Files"; const programFilesX86 = env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)"; return [pathModule.join(programFiles, "nodejs", "node.exe"), pathModule.join(programFilesX86, "nodejs", "node.exe")]; } return []; } const execFileAsync = promisify(execFile); async function resolveNodeVersion(nodePath, execFileImpl) { try { const { stdout } = await execFileImpl(nodePath, ["-p", "process.versions.node"], { encoding: "utf8" }); const value = stdout.trim(); return value ? value : null; } catch { return null; } } function isVersionManagedNodePath(nodePath, platform = process.platform) { const normalized = normalizeForCompare(nodePath, platform); return VERSION_MANAGER_MARKERS.some((marker) => normalized.includes(marker)); } function isSystemNodePath(nodePath, env = process.env, platform = process.platform) { const normalized = normalizeForCompare(nodePath, platform); return buildSystemNodeCandidates(env, platform).some((candidate) => { return normalized === normalizeForCompare(candidate, platform); }); } async function resolveSystemNodePath(env = process.env, platform = process.platform) { const candidates = buildSystemNodeCandidates(env, platform); for (const candidate of candidates) try { await fs.access(candidate); return candidate; } catch {} return null; } async function resolveSystemNodeInfo(params) { const systemNode = await resolveSystemNodePath(params.env ?? process.env, params.platform ?? process.platform); if (!systemNode) return null; const version = await resolveNodeVersion(systemNode, params.execFile ?? execFileAsync); return { path: systemNode, version, supported: isSupportedNodeVersion(version) }; } function renderSystemNodeWarning(systemNode, selectedNodePath) { if (!systemNode || systemNode.supported) return null; const versionLabel = systemNode.version ?? "unknown"; const selectedLabel = selectedNodePath ? ` Using ${selectedNodePath} for the daemon.` : ""; return `System Node ${versionLabel} at ${systemNode.path} is below the required Node 22+.${selectedLabel} Install Node 22+ from nodejs.org or Homebrew.`; } async function resolvePreferredNodePath(params) { if (params.runtime !== "node") return; const systemNode = await resolveSystemNodeInfo(params); if (!systemNode?.supported) return; return systemNode.path; } //#endregion //#region src/daemon/service-env.ts function resolveSystemPathDirs(platform) { if (platform === "darwin") return [ "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin" ]; if (platform === "linux") return [ "/usr/local/bin", "/usr/bin", "/bin" ]; return []; } /** * Resolve common user bin directories for Linux. * These are paths where npm global installs and node version managers typically place binaries. */ function resolveLinuxUserBinDirs(home, env) { if (!home) return []; const dirs = []; const add = (dir) => { if (dir) dirs.push(dir); }; const appendSubdir = (base, subdir) => { if (!base) return; return base.endsWith(`/${subdir}`) ? base : path.posix.join(base, subdir); }; add(env?.PNPM_HOME); add(appendSubdir(env?.NPM_CONFIG_PREFIX, "bin")); add(appendSubdir(env?.BUN_INSTALL, "bin")); add(appendSubdir(env?.VOLTA_HOME, "bin")); add(appendSubdir(env?.ASDF_DATA_DIR, "shims")); add(appendSubdir(env?.NVM_DIR, "current/bin")); add(appendSubdir(env?.FNM_DIR, "current/bin")); dirs.push(`${home}/.local/bin`); dirs.push(`${home}/.npm-global/bin`); dirs.push(`${home}/bin`); dirs.push(`${home}/.nvm/current/bin`); dirs.push(`${home}/.fnm/current/bin`); dirs.push(`${home}/.volta/bin`); dirs.push(`${home}/.asdf/shims`); dirs.push(`${home}/.local/share/pnpm`); dirs.push(`${home}/.bun/bin`); return dirs; } function getMinimalServicePathParts(options = {}) { const platform = options.platform ?? process.platform; if (platform === "win32") return []; const parts = []; const extraDirs = options.extraDirs ?? []; const systemDirs = resolveSystemPathDirs(platform); const linuxUserDirs = platform === "linux" ? resolveLinuxUserBinDirs(options.home, options.env) : []; const add = (dir) => { if (!dir) return; if (!parts.includes(dir)) parts.push(dir); }; for (const dir of extraDirs) add(dir); for (const dir of linuxUserDirs) add(dir); for (const dir of systemDirs) add(dir); return parts; } function getMinimalServicePathPartsFromEnv(options = {}) { const env = options.env ?? process.env; return getMinimalServicePathParts({ ...options, home: options.home ?? env.HOME, env }); } function buildMinimalServicePath(options = {}) { const env = options.env ?? process.env; if ((options.platform ?? process.platform) === "win32") return env.PATH ?? ""; return getMinimalServicePathPartsFromEnv({ ...options, env }).join(path.posix.delimiter); } function buildServiceEnvironment(params) { const { env, port, token, launchdLabel } = params; const profile = env.OPENCLAW_PROFILE; const resolvedLaunchdLabel = launchdLabel || (process.platform === "darwin" ? resolveGatewayLaunchAgentLabel(profile) : void 0); const systemdUnit = `${resolveGatewaySystemdServiceName(profile)}.service`; const stateDir = env.OPENCLAW_STATE_DIR; const configPath = env.OPENCLAW_CONFIG_PATH; return { HOME: env.HOME, PATH: buildMinimalServicePath({ env }), OPENCLAW_PROFILE: profile, OPENCLAW_STATE_DIR: stateDir, OPENCLAW_CONFIG_PATH: configPath, OPENCLAW_GATEWAY_PORT: String(port), OPENCLAW_GATEWAY_TOKEN: token, OPENCLAW_LAUNCHD_LABEL: resolvedLaunchdLabel, OPENCLAW_SYSTEMD_UNIT: systemdUnit, OPENCLAW_SERVICE_MARKER: GATEWAY_SERVICE_MARKER, OPENCLAW_SERVICE_KIND: GATEWAY_SERVICE_KIND, OPENCLAW_SERVICE_VERSION: VERSION }; } function buildNodeServiceEnvironment(params) { const { env } = params; const stateDir = env.OPENCLAW_STATE_DIR; const configPath = env.OPENCLAW_CONFIG_PATH; return { HOME: env.HOME, PATH: buildMinimalServicePath({ env }), OPENCLAW_STATE_DIR: stateDir, OPENCLAW_CONFIG_PATH: configPath, OPENCLAW_LAUNCHD_LABEL: resolveNodeLaunchAgentLabel(), OPENCLAW_SYSTEMD_UNIT: resolveNodeSystemdServiceName(), OPENCLAW_WINDOWS_TASK_NAME: resolveNodeWindowsTaskName(), OPENCLAW_TASK_SCRIPT_NAME: NODE_WINDOWS_TASK_SCRIPT_NAME, OPENCLAW_LOG_PREFIX: "node", OPENCLAW_SERVICE_MARKER: NODE_SERVICE_MARKER, OPENCLAW_SERVICE_KIND: NODE_SERVICE_KIND, OPENCLAW_SERVICE_VERSION: VERSION }; } //#endregion //#region src/commands/daemon-install-helpers.ts function resolveGatewayDevMode(argv = process.argv) { const normalizedEntry = argv[1]?.replaceAll("\\", "/"); return Boolean(normalizedEntry?.includes("/src/") && normalizedEntry.endsWith(".ts")); } async function buildGatewayInstallPlan(params) { const devMode = params.devMode ?? resolveGatewayDevMode(); const nodePath = params.nodePath ?? await resolvePreferredNodePath({ env: params.env, runtime: params.runtime }); const { programArguments, workingDirectory } = await resolveGatewayProgramArguments({ port: params.port, dev: devMode, runtime: params.runtime, nodePath }); if (params.runtime === "node") { const warning = renderSystemNodeWarning(await resolveSystemNodeInfo({ env: params.env }), programArguments[0]); if (warning) params.warn?.(warning, "Gateway runtime"); } const serviceEnvironment = buildServiceEnvironment({ env: params.env, port: params.port, token: params.token, launchdLabel: process.platform === "darwin" ? resolveGatewayLaunchAgentLabel(params.env.OPENCLAW_PROFILE) : void 0 }); const environment = { ...collectConfigEnvVars(params.config) }; Object.assign(environment, serviceEnvironment); return { programArguments, workingDirectory, environment }; } function gatewayInstallErrorHint(platform = process.platform) { return platform === "win32" ? "Tip: rerun from an elevated PowerShell (Start → type PowerShell → right-click → Run as administrator) or skip service install." : `Tip: rerun \`${formatCliCommand("openclaw gateway install")}\` after fixing the error.`; } //#endregion //#region src/commands/daemon-runtime.ts const DEFAULT_GATEWAY_DAEMON_RUNTIME = "node"; const GATEWAY_DAEMON_RUNTIME_OPTIONS = [{ value: "node", label: "Node (recommended)", hint: "Required for WhatsApp + Telegram. Bun can corrupt memory on reconnect." }]; function isGatewayDaemonRuntime(value) { return value === "node" || value === "bun"; } //#endregion export { gatewayInstallErrorHint as a, getMinimalServicePathPartsFromEnv as c, renderSystemNodeWarning as d, resolvePreferredNodePath as f, resolveNodeProgramArguments as h, buildGatewayInstallPlan as i, isSystemNodePath as l, resolveSystemNodePath as m, GATEWAY_DAEMON_RUNTIME_OPTIONS as n, resolveGatewayDevMode as o, resolveSystemNodeInfo as p, isGatewayDaemonRuntime as r, buildNodeServiceEnvironment as s, DEFAULT_GATEWAY_DAEMON_RUNTIME as t, isVersionManagedNodePath as u };