UNPKG

@gguf/claw

Version:

Multi-channel AI gateway with extensible messaging integrations

239 lines (237 loc) 8.87 kB
import "./paths-B4BZAPZh.js"; import { B as theme, O as danger, S as shortenHomePath, k as info } from "./utils-CP9YLh6M.js"; import "./registry-B-j4DRfe.js"; import { f as defaultRuntime } from "./subsystem-BCQGGxdd.js"; import "./exec-DYqRzFbo.js"; import "./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 { l as writeConfigFile, o as readConfigFileSnapshot } from "./config-PQiujvsf.js"; import "./manifest-registry-4k4vkhPS.js"; import { t as formatDocsLink } from "./links-BVCMOGeE.js"; import JSON5 from "json5"; //#region src/cli/config-cli.ts function isIndexSegment(raw) { return /^[0-9]+$/.test(raw); } function parsePath(raw) { const trimmed = raw.trim(); if (!trimmed) return []; const parts = []; let current = ""; let i = 0; while (i < trimmed.length) { const ch = trimmed[i]; if (ch === "\\") { const next = trimmed[i + 1]; if (next) current += next; i += 2; continue; } if (ch === ".") { if (current) parts.push(current); current = ""; i += 1; continue; } if (ch === "[") { if (current) parts.push(current); current = ""; const close = trimmed.indexOf("]", i); if (close === -1) throw new Error(`Invalid path (missing "]"): ${raw}`); const inside = trimmed.slice(i + 1, close).trim(); if (!inside) throw new Error(`Invalid path (empty "[]"): ${raw}`); parts.push(inside); i = close + 1; continue; } current += ch; i += 1; } if (current) parts.push(current); return parts.map((part) => part.trim()).filter(Boolean); } function parseValue(raw, opts) { const trimmed = raw.trim(); if (opts.strictJson) try { return JSON5.parse(trimmed); } catch (err) { throw new Error(`Failed to parse JSON5 value: ${String(err)}`, { cause: err }); } try { return JSON5.parse(trimmed); } catch { return raw; } } function getAtPath(root, path) { let current = root; for (const segment of path) { if (!current || typeof current !== "object") return { found: false }; if (Array.isArray(current)) { if (!isIndexSegment(segment)) return { found: false }; const index = Number.parseInt(segment, 10); if (!Number.isFinite(index) || index < 0 || index >= current.length) return { found: false }; current = current[index]; continue; } const record = current; if (!(segment in record)) return { found: false }; current = record[segment]; } return { found: true, value: current }; } function setAtPath(root, path, value) { let current = root; for (let i = 0; i < path.length - 1; i += 1) { const segment = path[i]; const next = path[i + 1]; const nextIsIndex = Boolean(next && isIndexSegment(next)); if (Array.isArray(current)) { if (!isIndexSegment(segment)) throw new Error(`Expected numeric index for array segment "${segment}"`); const index = Number.parseInt(segment, 10); const existing = current[index]; if (!existing || typeof existing !== "object") current[index] = nextIsIndex ? [] : {}; current = current[index]; continue; } if (!current || typeof current !== "object") throw new Error(`Cannot traverse into "${segment}" (not an object)`); const record = current; const existing = record[segment]; if (!existing || typeof existing !== "object") record[segment] = nextIsIndex ? [] : {}; current = record[segment]; } const last = path[path.length - 1]; if (Array.isArray(current)) { if (!isIndexSegment(last)) throw new Error(`Expected numeric index for array segment "${last}"`); const index = Number.parseInt(last, 10); current[index] = value; return; } if (!current || typeof current !== "object") throw new Error(`Cannot set "${last}" (parent is not an object)`); current[last] = value; } function unsetAtPath(root, path) { let current = root; for (let i = 0; i < path.length - 1; i += 1) { const segment = path[i]; if (!current || typeof current !== "object") return false; if (Array.isArray(current)) { if (!isIndexSegment(segment)) return false; const index = Number.parseInt(segment, 10); if (!Number.isFinite(index) || index < 0 || index >= current.length) return false; current = current[index]; continue; } const record = current; if (!(segment in record)) return false; current = record[segment]; } const last = path[path.length - 1]; if (Array.isArray(current)) { if (!isIndexSegment(last)) return false; const index = Number.parseInt(last, 10); if (!Number.isFinite(index) || index < 0 || index >= current.length) return false; current.splice(index, 1); return true; } if (!current || typeof current !== "object") return false; const record = current; if (!(last in record)) return false; delete record[last]; return true; } async function loadValidConfig(runtime = defaultRuntime) { const snapshot = await readConfigFileSnapshot(); if (snapshot.valid) return snapshot; runtime.error(`Config invalid at ${shortenHomePath(snapshot.path)}.`); for (const issue of snapshot.issues) runtime.error(`- ${issue.path || "<root>"}: ${issue.message}`); runtime.error(`Run \`${formatCliCommand("openclaw doctor")}\` to repair, then retry.`); runtime.exit(1); return snapshot; } function parseRequiredPath(path) { const parsedPath = parsePath(path); if (parsedPath.length === 0) throw new Error("Path is empty."); return parsedPath; } async function runConfigGet(opts) { const runtime = opts.runtime ?? defaultRuntime; try { const parsedPath = parseRequiredPath(opts.path); const res = getAtPath((await loadValidConfig(runtime)).config, parsedPath); if (!res.found) { runtime.error(danger(`Config path not found: ${opts.path}`)); runtime.exit(1); return; } if (opts.json) { runtime.log(JSON.stringify(res.value ?? null, null, 2)); return; } if (typeof res.value === "string" || typeof res.value === "number" || typeof res.value === "boolean") { runtime.log(String(res.value)); return; } runtime.log(JSON.stringify(res.value ?? null, null, 2)); } catch (err) { runtime.error(danger(String(err))); runtime.exit(1); } } async function runConfigUnset(opts) { const runtime = opts.runtime ?? defaultRuntime; try { const parsedPath = parseRequiredPath(opts.path); const snapshot = await loadValidConfig(runtime); const next = structuredClone(snapshot.resolved); if (!unsetAtPath(next, parsedPath)) { runtime.error(danger(`Config path not found: ${opts.path}`)); runtime.exit(1); return; } await writeConfigFile(next); runtime.log(info(`Removed ${opts.path}. Restart the gateway to apply.`)); } catch (err) { runtime.error(danger(String(err))); runtime.exit(1); } } function registerConfigCli(program) { const cmd = program.command("config").description("Non-interactive config helpers (get/set/unset). Run without subcommand for the setup wizard.").addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/config", "docs.openclaw.ai/cli/config")}\n`).option("--section <section>", "Configure wizard sections (repeatable). Use with no subcommand.", (value, previous) => [...previous, value], []).action(async (opts) => { const { configureCommandFromSectionsArg } = await import("./configure-CBZlcN2L.js").then((n) => n.t); await configureCommandFromSectionsArg(opts.section, defaultRuntime); }); cmd.command("get").description("Get a config value by dot path").argument("<path>", "Config path (dot or bracket notation)").option("--json", "Output JSON", false).action(async (path, opts) => { await runConfigGet({ path, json: Boolean(opts.json) }); }); cmd.command("set").description("Set a config value by dot path").argument("<path>", "Config path (dot or bracket notation)").argument("<value>", "Value (JSON5 or raw string)").option("--strict-json", "Strict JSON5 parsing (error instead of raw string fallback)", false).option("--json", "Legacy alias for --strict-json", false).action(async (path, value, opts) => { try { const parsedPath = parsePath(path); if (parsedPath.length === 0) throw new Error("Path is empty."); const parsedValue = parseValue(value, { strictJson: Boolean(opts.strictJson || opts.json) }); const snapshot = await loadValidConfig(); const next = structuredClone(snapshot.resolved); setAtPath(next, parsedPath, parsedValue); await writeConfigFile(next); defaultRuntime.log(info(`Updated ${path}. Restart the gateway to apply.`)); } catch (err) { defaultRuntime.error(danger(String(err))); defaultRuntime.exit(1); } }); cmd.command("unset").description("Remove a config value by dot path").argument("<path>", "Config path (dot or bracket notation)").action(async (path) => { await runConfigUnset({ path }); }); } //#endregion export { registerConfigCli };