UNPKG

@gguf/claw

Version:

Multi-channel AI gateway with extensible messaging integrations

597 lines (590 loc) 21.1 kB
import { g as resolveStateDir, i as isNixMode, m as resolveOAuthDir, o as resolveConfigPath, u as resolveGatewayPort } from "./paths-B4BZAPZh.js"; import { B as theme, _ as resolveHomeDir, x as shortenHomeInString, y as resolveUserPath } from "./utils-CP9YLh6M.js"; import "./thinking-EAliFiVK.js"; import "./reply-BylFHBd4.js"; import "./registry-B-j4DRfe.js"; import { f as defaultRuntime } from "./subsystem-BCQGGxdd.js"; import "./exec-DYqRzFbo.js"; import { D as resolveDefaultAgentWorkspaceDir } from "./agent-scope-BnZW9Gh2.js"; import "./model-selection-CqaTAlhy.js"; import "./github-copilot-token-D2zp6kMZ.js"; import { t as formatCliCommand } from "./command-format-DEKzLnLg.js"; import "./boolean-BsqeuxE6.js"; import "./env-VriqyjXT.js"; import "./message-channel-Bena1Tzd.js"; import "./send-CsyONLnQ.js"; import { i as loadConfig, o as readConfigFileSnapshot } from "./config-PQiujvsf.js"; import "./manifest-registry-4k4vkhPS.js"; import "./runner-U04aiHHC.js"; import "./image-DxEpBZim.js"; import "./models-config-B459BnCS.js"; import "./pi-model-discovery-4uUnLc3n.js"; import "./pi-embedded-helpers-BdscQhyS.js"; import "./sandbox-UAzvS0V6.js"; import "./image-ops-CI1VknD1.js"; import "./common-a25M2Kvi.js"; import "./chrome-Dd5zBIFu.js"; import "./tailscale-C3JsBEiY.js"; import "./auth-DZSWPd8D.js"; import "./server-context-DAWsUNUs.js"; import "./frontmatter-6HZ0_y2V.js"; import "./skills-CLmzWt48.js"; import "./routes-DIQa_0pt.js"; import "./redact-B7Mjvk0c.js"; import "./errors-kfGqPQ4b.js"; import "./fs-safe-_OpKPuZA.js"; import "./paths-D1WZUYry.js"; import "./ssrf-6f5m2MMA.js"; import "./store-DSGYY-H9.js"; import "./ports-DwDxX6cS.js"; import "./trash-DLzIf__E.js"; import "./sessions-Z1BZU3xh.js"; import "./dock-7F-MiPtF.js"; import "./normalize-Ci8UyR3Z.js"; import "./accounts-Bj1dJ-fd.js"; import "./accounts-DOl1Wkxo.js"; import "./accounts-t6R5_PzC.js"; import "./bindings-Dja1z-h8.js"; import "./logging-xYH6GmRT.js"; import "./send-uu2Jc-Js.js"; import "./plugins-CKbXkuXd.js"; import "./paths-C2NfoGZE.js"; import "./tool-display-Bx4M6uNT.js"; import "./fetch-guard-C1sYsWgl.js"; import "./api-key-rotation-CpGefdGL.js"; import "./local-roots-DiDvXIIo.js"; import "./sqlite-JcMMx8Z5.js"; import "./model-catalog-CIixTnGt.js"; import "./tokens-D4lZk7-h.js"; import "./with-timeout-DijiQjw6.js"; import "./deliver-DgvS3uCz.js"; import "./diagnostic-UUwku4RV.js"; import "./diagnostic-session-state-ByqoIyGn.js"; import "./send-DbwyyQAv.js"; import "./model-VbWjwqaW.js"; import "./reply-prefix-DAK7-zK4.js"; import "./memory-cli-DxmtLhh_.js"; import "./manager-C0teWocQ.js"; import "./retry-rUEdE6zT.js"; import "./chunk-CMylpCPi.js"; import "./markdown-tables-B9VGUdDc.js"; import "./ir-24qCLTKH.js"; import "./render-CXDO_kgw.js"; import "./commands-registry-bb_U-3FM.js"; import "./client-BdSkEtCd.js"; import "./call-BCz_8mqq.js"; import "./pairing-token-B-_eiWlR.js"; import "./channel-activity-DynIQUB_.js"; import "./fetch-Bu5zBCce.js"; import "./tables-C70h_80U.js"; import "./send-CAfFUUkm.js"; import "./pairing-store-T5DpOfoL.js"; import "./proxy-BiOWrea2.js"; import { t as formatDocsLink } from "./links-BVCMOGeE.js"; import { n as runCommandWithRuntime } from "./cli-utils-CX1oQ81G.js"; import "./help-format-dbk2xW0E.js"; import "./progress-CaVMHLaE.js"; import "./resolve-route-2TU_aaLs.js"; import "./replies-NhrZJ3yf.js"; import "./skill-commands-N8Sho46-.js"; import "./workspace-dirs-CE3CxabZ.js"; import "./pi-tools.policy-DTy1DnPK.js"; import "./outbound-attachment-CntoLTI_.js"; import "./delivery-queue-CxDDEWQg.js"; import "./session-cost-usage-BlUDaylg.js"; import "./send-4LoRaltQ.js"; import { f as openUrl, g as resolveControlUiLinks, i as detectBrowserOpenSupport, o as formatControlUiSshHint } from "./onboard-helpers-DbjfBSMX.js"; import { n as stylePromptMessage, r as stylePromptTitle, t as stylePromptHint } from "./prompt-style-VqCNjBi8.js"; import "./pairing-labels-DaESq9ML.js"; import "./exec-approvals-q0C8VDMT.js"; import "./nodes-screen-5fMfTT2n.js"; import "./control-service-BoO2TGOx.js"; import "./stagger-B6VQyn1F.js"; import "./channel-selection-DuDSoVSC.js"; import "./runtime-guard-DlT3NecY.js"; import "./program-context-Cx3damTh.js"; import "./catalog-CgtgPHMj.js"; import "./skills-status-kQ1GipOi.js"; import { t as copyToClipboard } from "./clipboard-DE_XZZD9.js"; import "./note-C-OTWf0F.js"; import "./plugin-auto-enable-DJOFx9vI.js"; import "./channels-status-issues-ClkP4ZUC.js"; import "./completion-cli-BOzsB_m2.js"; import "./daemon-runtime-CZuP7tS4.js"; import "./systemd-B6S4ND7H.js"; import { t as resolveGatewayService } from "./service-Bw9h81em.js"; import "./systemd-hints-B9MFGq86.js"; import "./diagnostics-CArN76wS.js"; import "./health-Bt3xmMjK.js"; import "./control-ui-assets-BmaUgzFj.js"; import "./update-check-CHLDbXI4.js"; import "./dm-policy-shared-XAIBK3tz.js"; import "./session-dirs-BExMNJFf.js"; import "./logging-Cl6-FWdR.js"; import "./doctor-config-flow-CruHQlOq.js"; import "./systemd-linger-C3Tnphf9.js"; import "./health-format-DQNr1Hum.js"; import { n as doctorCommand, t as selectStyled } from "./prompt-select-styled-BZ1PSy1q.js"; import "./doctor-completion-D-vVYUz8.js"; import path from "node:path"; import fs from "node:fs/promises"; import { cancel, confirm, isCancel, multiselect } from "@clack/prompts"; //#region src/commands/dashboard.ts async function dashboardCommand(runtime = defaultRuntime, options = {}) { const snapshot = await readConfigFileSnapshot(); const cfg = snapshot.valid ? snapshot.config : {}; const port = resolveGatewayPort(cfg); const bind = cfg.gateway?.bind ?? "loopback"; const basePath = cfg.gateway?.controlUi?.basePath; const customBindHost = cfg.gateway?.customBindHost; const token = cfg.gateway?.auth?.token ?? process.env.OPENCLAW_GATEWAY_TOKEN ?? ""; const links = resolveControlUiLinks({ port, bind: bind === "lan" ? "loopback" : bind, customBindHost, basePath }); const dashboardUrl = token ? `${links.httpUrl}#token=${encodeURIComponent(token)}` : links.httpUrl; runtime.log(`Dashboard URL: ${dashboardUrl}`); const copied = await copyToClipboard(dashboardUrl).catch(() => false); runtime.log(copied ? "Copied to clipboard." : "Copy to clipboard unavailable."); let opened = false; let hint; if (!options.noOpen) { if ((await detectBrowserOpenSupport()).ok) opened = await openUrl(dashboardUrl); if (!opened) hint = formatControlUiSshHint({ port, basePath, token: token || void 0 }); } else hint = "Browser launch disabled (--no-open). Use the URL above."; if (opened) runtime.log("Opened in your browser. Keep that tab to control OpenClaw."); else if (hint) runtime.log(hint); } //#endregion //#region src/commands/cleanup-utils.ts function collectWorkspaceDirs(cfg) { const dirs = /* @__PURE__ */ new Set(); const defaults = cfg?.agents?.defaults; if (typeof defaults?.workspace === "string" && defaults.workspace.trim()) dirs.add(resolveUserPath(defaults.workspace)); const list = Array.isArray(cfg?.agents?.list) ? cfg?.agents?.list : []; for (const agent of list) { const workspace = agent.workspace; if (typeof workspace === "string" && workspace.trim()) dirs.add(resolveUserPath(workspace)); } if (dirs.size === 0) dirs.add(resolveDefaultAgentWorkspaceDir()); return [...dirs]; } function buildCleanupPlan(params) { return { configInsideState: isPathWithin(params.configPath, params.stateDir), oauthInsideState: isPathWithin(params.oauthDir, params.stateDir), workspaceDirs: collectWorkspaceDirs(params.cfg) }; } function isPathWithin(child, parent) { const relative = path.relative(parent, child); return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative); } function isUnsafeRemovalTarget(target) { if (!target.trim()) return true; const resolved = path.resolve(target); if (resolved === path.parse(resolved).root) return true; const home = resolveHomeDir(); if (home && resolved === path.resolve(home)) return true; return false; } async function removePath(target, runtime, opts) { if (!target?.trim()) return { ok: false, skipped: true }; const resolved = path.resolve(target); const displayLabel = shortenHomeInString(opts?.label ?? resolved); if (isUnsafeRemovalTarget(resolved)) { runtime.error(`Refusing to remove unsafe path: ${displayLabel}`); return { ok: false }; } if (opts?.dryRun) { runtime.log(`[dry-run] remove ${displayLabel}`); return { ok: true, skipped: true }; } try { await fs.rm(resolved, { recursive: true, force: true }); runtime.log(`Removed ${displayLabel}`); return { ok: true }; } catch (err) { runtime.error(`Failed to remove ${displayLabel}: ${String(err)}`); return { ok: false }; } } async function listAgentSessionDirs(stateDir) { const root = path.join(stateDir, "agents"); try { return (await fs.readdir(root, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => path.join(root, entry.name, "sessions")); } catch { return []; } } //#endregion //#region src/commands/cleanup-plan.ts function resolveCleanupPlanFromDisk() { const cfg = loadConfig(); const stateDir = resolveStateDir(); const configPath = resolveConfigPath(); const oauthDir = resolveOAuthDir(); return { cfg, stateDir, configPath, oauthDir, ...buildCleanupPlan({ cfg, stateDir, configPath, oauthDir }) }; } //#endregion //#region src/commands/reset.ts async function stopGatewayIfRunning(runtime) { if (isNixMode) return; const service = resolveGatewayService(); let loaded = false; try { loaded = await service.isLoaded({ env: process.env }); } catch (err) { runtime.error(`Gateway service check failed: ${String(err)}`); return; } if (!loaded) return; try { await service.stop({ env: process.env, stdout: process.stdout }); } catch (err) { runtime.error(`Gateway stop failed: ${String(err)}`); } } async function resetCommand(runtime, opts) { const interactive = !opts.nonInteractive; if (!interactive && !opts.yes) { runtime.error("Non-interactive mode requires --yes."); runtime.exit(1); return; } let scope = opts.scope; if (!scope) { if (!interactive) { runtime.error("Non-interactive mode requires --scope."); runtime.exit(1); return; } const selection = await selectStyled({ message: "Reset scope", options: [ { value: "config", label: "Config only", hint: "openclaw.json" }, { value: "config+creds+sessions", label: "Config + credentials + sessions", hint: "keeps workspace + auth profiles" }, { value: "full", label: "Full reset", hint: "state dir + workspace" } ], initialValue: "config+creds+sessions" }); if (isCancel(selection)) { cancel(stylePromptTitle("Reset cancelled.") ?? "Reset cancelled."); runtime.exit(0); return; } scope = selection; } if (![ "config", "config+creds+sessions", "full" ].includes(scope)) { runtime.error("Invalid --scope. Expected \"config\", \"config+creds+sessions\", or \"full\"."); runtime.exit(1); return; } if (interactive && !opts.yes) { const ok = await confirm({ message: stylePromptMessage(`Proceed with ${scope} reset?`) }); if (isCancel(ok) || !ok) { cancel(stylePromptTitle("Reset cancelled.") ?? "Reset cancelled."); runtime.exit(0); return; } } const dryRun = Boolean(opts.dryRun); const { stateDir, configPath, oauthDir, configInsideState, oauthInsideState, workspaceDirs } = resolveCleanupPlanFromDisk(); if (scope !== "config") if (dryRun) runtime.log("[dry-run] stop gateway service"); else await stopGatewayIfRunning(runtime); if (scope === "config") { await removePath(configPath, runtime, { dryRun, label: configPath }); return; } if (scope === "config+creds+sessions") { await removePath(configPath, runtime, { dryRun, label: configPath }); await removePath(oauthDir, runtime, { dryRun, label: oauthDir }); const sessionDirs = await listAgentSessionDirs(stateDir); for (const dir of sessionDirs) await removePath(dir, runtime, { dryRun, label: dir }); runtime.log(`Next: ${formatCliCommand("openclaw onboard --install-daemon")}`); return; } if (scope === "full") { await removePath(stateDir, runtime, { dryRun, label: stateDir }); if (!configInsideState) await removePath(configPath, runtime, { dryRun, label: configPath }); if (!oauthInsideState) await removePath(oauthDir, runtime, { dryRun, label: oauthDir }); for (const workspace of workspaceDirs) await removePath(workspace, runtime, { dryRun, label: workspace }); runtime.log(`Next: ${formatCliCommand("openclaw onboard --install-daemon")}`); return; } } //#endregion //#region src/commands/uninstall.ts const multiselectStyled = (params) => multiselect({ ...params, message: stylePromptMessage(params.message), options: params.options.map((opt) => opt.hint === void 0 ? opt : { ...opt, hint: stylePromptHint(opt.hint) }) }); function buildScopeSelection(opts) { const hadExplicit = Boolean(opts.all || opts.service || opts.state || opts.workspace || opts.app); const scopes = /* @__PURE__ */ new Set(); if (opts.all || opts.service) scopes.add("service"); if (opts.all || opts.state) scopes.add("state"); if (opts.all || opts.workspace) scopes.add("workspace"); if (opts.all || opts.app) scopes.add("app"); return { scopes, hadExplicit }; } async function stopAndUninstallService(runtime) { if (isNixMode) { runtime.error("Nix mode detected; service uninstall is disabled."); return false; } const service = resolveGatewayService(); let loaded = false; try { loaded = await service.isLoaded({ env: process.env }); } catch (err) { runtime.error(`Gateway service check failed: ${String(err)}`); return false; } if (!loaded) { runtime.log(`Gateway service ${service.notLoadedText}.`); return true; } try { await service.stop({ env: process.env, stdout: process.stdout }); } catch (err) { runtime.error(`Gateway stop failed: ${String(err)}`); } try { await service.uninstall({ env: process.env, stdout: process.stdout }); return true; } catch (err) { runtime.error(`Gateway uninstall failed: ${String(err)}`); return false; } } async function removeMacApp(runtime, dryRun) { if (process.platform !== "darwin") return; await removePath("/Applications/OpenClaw.app", runtime, { dryRun, label: "/Applications/OpenClaw.app" }); } async function uninstallCommand(runtime, opts) { const { scopes, hadExplicit } = buildScopeSelection(opts); const interactive = !opts.nonInteractive; if (!interactive && !opts.yes) { runtime.error("Non-interactive mode requires --yes."); runtime.exit(1); return; } if (!hadExplicit) { if (!interactive) { runtime.error("Non-interactive mode requires explicit scopes (use --all)."); runtime.exit(1); return; } const selection = await multiselectStyled({ message: "Uninstall which components?", options: [ { value: "service", label: "Gateway service", hint: "launchd / systemd / schtasks" }, { value: "state", label: "State + config", hint: "~/.openclaw" }, { value: "workspace", label: "Workspace", hint: "agent files" }, { value: "app", label: "macOS app", hint: "/Applications/OpenClaw.app" } ], initialValues: [ "service", "state", "workspace" ] }); if (isCancel(selection)) { cancel(stylePromptTitle("Uninstall cancelled.") ?? "Uninstall cancelled."); runtime.exit(0); return; } for (const value of selection) scopes.add(value); } if (scopes.size === 0) { runtime.log("Nothing selected."); return; } if (interactive && !opts.yes) { const ok = await confirm({ message: stylePromptMessage("Proceed with uninstall?") }); if (isCancel(ok) || !ok) { cancel(stylePromptTitle("Uninstall cancelled.") ?? "Uninstall cancelled."); runtime.exit(0); return; } } const dryRun = Boolean(opts.dryRun); const { stateDir, configPath, oauthDir, configInsideState, oauthInsideState, workspaceDirs } = resolveCleanupPlanFromDisk(); if (scopes.has("service")) if (dryRun) runtime.log("[dry-run] remove gateway service"); else await stopAndUninstallService(runtime); if (scopes.has("state")) { await removePath(stateDir, runtime, { dryRun, label: stateDir }); if (!configInsideState) await removePath(configPath, runtime, { dryRun, label: configPath }); if (!oauthInsideState) await removePath(oauthDir, runtime, { dryRun, label: oauthDir }); } if (scopes.has("workspace")) for (const workspace of workspaceDirs) await removePath(workspace, runtime, { dryRun, label: workspace }); if (scopes.has("app")) await removeMacApp(runtime, dryRun); runtime.log("CLI still installed. Remove via npm/pnpm if desired."); if (scopes.has("state") && !scopes.has("workspace")) { const home = resolveHomeDir(); if (home && workspaceDirs.some((dir) => dir.startsWith(path.resolve(home)))) runtime.log("Tip: workspaces were preserved. Re-run with --workspace to remove them."); } } //#endregion //#region src/cli/program/register.maintenance.ts function registerMaintenanceCommands(program) { program.command("doctor").description("Health checks + quick fixes for the gateway and channels").addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/doctor", "docs.openclaw.ai/cli/doctor")}\n`).option("--no-workspace-suggestions", "Disable workspace memory system suggestions", false).option("--yes", "Accept defaults without prompting", false).option("--repair", "Apply recommended repairs without prompting", false).option("--fix", "Apply recommended repairs (alias for --repair)", false).option("--force", "Apply aggressive repairs (overwrites custom service config)", false).option("--non-interactive", "Run without prompts (safe migrations only)", false).option("--generate-gateway-token", "Generate and configure a gateway token", false).option("--deep", "Scan system services for extra gateway installs", false).action(async (opts) => { await runCommandWithRuntime(defaultRuntime, async () => { await doctorCommand(defaultRuntime, { workspaceSuggestions: opts.workspaceSuggestions, yes: Boolean(opts.yes), repair: Boolean(opts.repair) || Boolean(opts.fix), force: Boolean(opts.force), nonInteractive: Boolean(opts.nonInteractive), generateGatewayToken: Boolean(opts.generateGatewayToken), deep: Boolean(opts.deep) }); defaultRuntime.exit(0); }); }); program.command("dashboard").description("Open the Control UI with your current token").addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/dashboard", "docs.openclaw.ai/cli/dashboard")}\n`).option("--no-open", "Print URL but do not launch a browser", false).action(async (opts) => { await runCommandWithRuntime(defaultRuntime, async () => { await dashboardCommand(defaultRuntime, { noOpen: Boolean(opts.noOpen) }); }); }); program.command("reset").description("Reset local config/state (keeps the CLI installed)").addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/reset", "docs.openclaw.ai/cli/reset")}\n`).option("--scope <scope>", "config|config+creds+sessions|full (default: interactive prompt)").option("--yes", "Skip confirmation prompts", false).option("--non-interactive", "Disable prompts (requires --scope + --yes)", false).option("--dry-run", "Print actions without removing files", false).action(async (opts) => { await runCommandWithRuntime(defaultRuntime, async () => { await resetCommand(defaultRuntime, { scope: opts.scope, yes: Boolean(opts.yes), nonInteractive: Boolean(opts.nonInteractive), dryRun: Boolean(opts.dryRun) }); }); }); program.command("uninstall").description("Uninstall the gateway service + local data (CLI remains)").addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/uninstall", "docs.openclaw.ai/cli/uninstall")}\n`).option("--service", "Remove the gateway service", false).option("--state", "Remove state + config", false).option("--workspace", "Remove workspace dirs", false).option("--app", "Remove the macOS app", false).option("--all", "Remove service + state + workspace + app", false).option("--yes", "Skip confirmation prompts", false).option("--non-interactive", "Disable prompts (requires --yes)", false).option("--dry-run", "Print actions without removing files", false).action(async (opts) => { await runCommandWithRuntime(defaultRuntime, async () => { await uninstallCommand(defaultRuntime, { service: Boolean(opts.service), state: Boolean(opts.state), workspace: Boolean(opts.workspace), app: Boolean(opts.app), all: Boolean(opts.all), yes: Boolean(opts.yes), nonInteractive: Boolean(opts.nonInteractive), dryRun: Boolean(opts.dryRun) }); }); }); } //#endregion export { registerMaintenanceCommands };