From 96ffbb5aaffa6bba16cb40a0e4e95b55150b22c5 Mon Sep 17 00:00:00 2001 From: cyb1278588254 <48212932+cyb1278588254@users.noreply.github.com> Date: Mon, 2 Mar 2026 12:33:20 +0800 Subject: [PATCH] CLI: add config path subcommand to print active config file path (#26256) Merged via squash. Prepared head SHA: b11c593a34c5730f4244c054e1d1ab536953b0ef Co-authored-by: cyb1278588254 <48212932+cyb1278588254@users.noreply.github.com> Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com> Reviewed-by: @gumadeiras --- CHANGELOG.md | 1 + docs/cli/config.md | 10 ++++++++-- docs/cli/index.md | 3 ++- src/cli/config-cli.test.ts | 23 +++++++++++++++++++++++ src/cli/config-cli.ts | 20 +++++++++++++++++++- src/cli/program/command-registry.ts | 2 +- 6 files changed, 54 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74a8d88c7ba..741c7e791e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai - OpenAI/Streaming transport: make `openai` Responses WebSocket-first by default (`transport: "auto"` with SSE fallback), add shared OpenAI WS stream/connection runtime wiring with per-session cleanup, and preserve server-side compaction payload mutation (`store` + `context_management`) on the WS path. - Android/Gateway capability refresh: add live Android capability integration coverage and node canvas capability refresh wiring, plus runtime hardening for A2UI readiness retries, scoped canvas URL normalization, debug diagnostics JSON, and JavaScript MIME delivery. (#28388) Thanks @obviyus. - Android/Nodes parity: add `system.notify`, `photos.latest`, `contacts.search`/`contacts.add`, `calendar.events`/`calendar.add`, and `motion.activity`/`motion.pedometer`, with motion sensor-aware command gating and improved activity sampling reliability. (#29398) Thanks @obviyus. +- CLI/Config: add `openclaw config file` to print the active config file path resolved from `OPENCLAW_CONFIG_PATH` or the default location. (#26256) thanks @cyb1278588254. - Feishu/Docx tables + uploads: add `feishu_doc` actions for Docx table creation/cell writing (`create_table`, `write_table_cells`, `create_table_with_values`) and image/file uploads (`upload_image`, `upload_file`) with stricter create/upload error handling for missing `document_id` and placeholder cleanup failures. (#20304) Thanks @xuhao1. - Feishu/Reactions: add inbound `im.message.reaction.created_v1` handling, route verified reactions through synthetic inbound turns, and harden verification with timeout + fail-closed filtering so non-bot or unverified reactions are dropped. (#16716) Thanks @schumilin. - Feishu/Chat tooling: add `feishu_chat` tool actions for chat info and member queries, with configurable enablement under `channels.feishu.tools.chat`. (#14674) Thanks @liuweifly. diff --git a/docs/cli/config.md b/docs/cli/config.md index 18a3a0f197d..8bee6deec7a 100644 --- a/docs/cli/config.md +++ b/docs/cli/config.md @@ -1,5 +1,5 @@ --- -summary: "CLI reference for `openclaw config` (get/set/unset config values)" +summary: "CLI reference for `openclaw config` (get/set/unset values and config file path)" read_when: - You want to read or edit config non-interactively title: "config" @@ -7,12 +7,14 @@ title: "config" # `openclaw config` -Config helpers: get/set/unset values by path. Run without a subcommand to open +Config helpers: get/set/unset values by path and print the active config file. +Run without a subcommand to open the configure wizard (same as `openclaw configure`). ## Examples ```bash +openclaw config file openclaw config get browser.executablePath openclaw config set browser.executablePath "/usr/bin/google-chrome" openclaw config set agents.defaults.heartbeat.every "2h" @@ -47,4 +49,8 @@ openclaw config set gateway.port 19001 --strict-json openclaw config set channels.whatsapp.groups '["*"]' --strict-json ``` +## Subcommands + +- `config file`: Print the active config file path (resolved from `OPENCLAW_CONFIG_PATH` or default location). + Restart the gateway after edits. diff --git a/docs/cli/index.md b/docs/cli/index.md index bb09b062210..a20c53aad19 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -380,7 +380,7 @@ Interactive configuration wizard (models, channels, skills, gateway). ### `config` -Non-interactive config helpers (get/set/unset). Running `openclaw config` with no +Non-interactive config helpers (get/set/unset/file). Running `openclaw config` with no subcommand launches the wizard. Subcommands: @@ -388,6 +388,7 @@ Subcommands: - `config get `: print a config value (dot/bracket path). - `config set `: set a value (JSON5 or raw string). - `config unset `: remove a value. +- `config file`: print the active config file path. ### `doctor` diff --git a/src/cli/config-cli.test.ts b/src/cli/config-cli.test.ts index e672a0f1f8d..f0dc2fd6fc5 100644 --- a/src/cli/config-cli.test.ts +++ b/src/cli/config-cli.test.ts @@ -288,4 +288,27 @@ describe("config cli", () => { }); }); }); + + describe("config file", () => { + it("prints the active config file path", async () => { + const resolved: OpenClawConfig = { gateway: { port: 18789 } }; + setSnapshot(resolved, resolved); + + await runConfigCommand(["config", "file"]); + + expect(mockLog).toHaveBeenCalledWith("/tmp/openclaw.json"); + expect(mockWriteConfigFile).not.toHaveBeenCalled(); + }); + + it("handles config file path with home directory", async () => { + const resolved: OpenClawConfig = { gateway: { port: 18789 } }; + const snapshot = buildSnapshot({ resolved, config: resolved }); + snapshot.path = "/home/user/.openclaw/openclaw.json"; + mockReadConfigFileSnapshot.mockResolvedValueOnce(snapshot); + + await runConfigCommand(["config", "file"]); + + expect(mockLog).toHaveBeenCalledWith("/home/user/.openclaw/openclaw.json"); + }); + }); }); diff --git a/src/cli/config-cli.ts b/src/cli/config-cli.ts index 39e60e4ce36..13cc72b1111 100644 --- a/src/cli/config-cli.ts +++ b/src/cli/config-cli.ts @@ -324,11 +324,22 @@ export async function runConfigUnset(opts: { path: string; runtime?: RuntimeEnv } } +export async function runConfigFile(opts: { runtime?: RuntimeEnv }) { + const runtime = opts.runtime ?? defaultRuntime; + try { + const snapshot = await readConfigFileSnapshot(); + runtime.log(shortenHomePath(snapshot.path)); + } catch (err) { + runtime.error(danger(String(err))); + runtime.exit(1); + } +} + export function registerConfigCli(program: Command) { const cmd = program .command("config") .description( - "Non-interactive config helpers (get/set/unset). Run without subcommand for the setup wizard.", + "Non-interactive config helpers (get/set/unset/file). Run without subcommand for the setup wizard.", ) .addHelpText( "after", @@ -390,4 +401,11 @@ export function registerConfigCli(program: Command) { .action(async (path: string) => { await runConfigUnset({ path }); }); + + cmd + .command("file") + .description("Print the active config file path") + .action(async () => { + await runConfigFile({}); + }); } diff --git a/src/cli/program/command-registry.ts b/src/cli/program/command-registry.ts index 9ad44cf3eeb..324b5692893 100644 --- a/src/cli/program/command-registry.ts +++ b/src/cli/program/command-registry.ts @@ -83,7 +83,7 @@ const coreEntries: CoreCliEntry[] = [ { name: "config", description: - "Non-interactive config helpers (get/set/unset). Default: starts setup wizard.", + "Non-interactive config helpers (get/set/unset/file). Default: starts setup wizard.", hasSubcommands: true, }, ],