`openclaw config patch` with a null leaf at an array index removed three
entries instead of one, and reported success. runConfigOperations spliced the
element out of the in-memory config and also pushed the path into unsetPaths,
which the writer replays twice against the persist candidate, so every replay
removed one more neighbour.
runConfigUnset already suppresses unsetPaths for array leaves (fb73f2161e,
issue #76290). runConfigOperations ignored the leafContainer that unsetAtPath
returns and pushed unconditionally. It now applies the same guard, so object
keys keep their write-level unset path and array indices do not.
* fix(cli): make commands, completion, and JSON output reliable
* fix(cli): reconcile completion coverage with current main
* test(cli): keep test routing stable across isolation lanes
* refactor(config): move model normalization to input owners
* fix(config): merge canonical doctor model rows
* fix(config): align touched model map identities
* ci(env): lower production variable budget
* chore: leave changelog to release automation
* feat(config): journal every config change with source labels and manual-edit detection
* fix(config): narrow journal exports and align plugin-write test expectations
* refactor(config): split journal snapshot store out of io.audit
* chore(config): keep journal snapshot context helpers module-local
* fix: bound misc unbounded fs.readFile calls; remove unused fs import
* fix: decode buffer to string before passing to string consumers
readRegularFile and readRegularFileSync return { buffer, stat },
not a string. All 4 new call sites passed the raw object to functions
expecting a string (JSON.parse, RegExp.test, template literals, etc.),
causing TS2345 type errors and runtime failures.
Fix each call by extracting .buffer and calling .toString('utf8')
before passing the result to string consumers.
* style: fix oxfmt formatting in config-set-input.ts
* fix: bound config and trajectory metadata reads
Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>
* refactor: isolate bounded read ownership
Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(secrets): use resolveStateDir for .env path to cover legacy .clawdbot dir
* fix(secrets): replace override-based legacy .env test with true automatic fallback, add apply-path coverage
* fix(config-cli): emit JSON error on config get --json for missing paths
When is used with a nonexistent
path, the command emits no JSON on stdout, only a text error on stderr.
This breaks automated consumers that expect machine-readable errors.
Fix: output JSON to stdout before exit(1) when
opts.json is set. This matches the existing JSON error output pattern
already used by runConfigValidate and handleConfigMutationError.
Also re-throw synthetic exit errors in the catch block so the
--json branch's writeRuntimeJson+exit is not caught and re-emitted
as a text error.
* fix(config-cli): use typed ExitError signal instead of __exit__ string sentinel
* fix(secrets): reuse resolved stateDir for .env path in audit
* fix(secrets): pass resolved stateDir into apply .env scrubbing, add root-switch regression
* test(config-cli): migrate remaining __exit__ sentinel to ExitError
* fix(secrets): cover config and state dotenv roots
* style(secrets): format dotenv path helper
* test(secrets): track temporary directories
* test(config): expect typed exit for missing patch files
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
* fix(cli): wrap readFileSync ENOENT with user-friendly messages and suppress cause leak
* fix(secrets): type missing plan errors
* style(secrets): format missing plan error branch
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
`config file` (print the active config path) called readConfigFileSnapshot(), eagerly creating the state dir + state/openclaw.sqlite — so a --profile typo minted a stray profile tree. Resolve the path via resolveConfigPath() instead; no state initialized.
`config file`, `config patch --dry-run --json`, and `config unset --json` ran
the config path through shortenHomePath, emitting `~/…` or the literal
`$OPENCLAW_HOME/…` token in machine output — breaking `cat $(openclaw config
file)` and corrupting JSON path fields. `agents list --json` already resolves.
Emit the raw resolved snapshot path at these machine-output sites; human table
rendering keeps the friendly abbreviation.
* fix(config): improve validation error messages with line numbers, bracket paths, and received values
- Add JSON5-aware path navigator (issue-location.ts) that resolves line
numbers and received values from raw config text without a full parser
- Add formatConfigIssuePath() for bracket notation (agents.list[3])
- Add appendReceivedValueHint() for safe value display (got: "none")
- Add attachConfigIssueDiagnostics() to enrich issues with location info
- Wire enrichment into runConfigValidate() and loadValidConfig() in config-cli.ts
- Add resolveIssueLocationPrefix() to issue-format.ts for sourceFile:line prefix
- Sensitive paths and secret refs are never leaked in received value hints
- Paths in 'd files gracefully degrade (no line number shown)
Closes#104854
* fix(config): type fixes from tsgo:core validation
- Add sourceFile to ConfigIssueFormatOptions (used in resolveIssueLocationPrefix)
- Fix readKey regex test for undefined raw[c.pos]
* fix(config): satisfy eslint(curly) and formatting for issue-location.ts
- Add braces to all single-line if/while/for statements
- Run oxfmt for consistent formatting
* fix(config): trim issue-location.ts to 500 lines for LOC ratchet
* fix(config): fix formatConfigIssuePath ternary bug from trimming
* fix(config): trim issue-location.ts to 469 lines for LOC ratchet
* fix(config): format issue-location.test.ts
* fix(config): document JSON5 subset and add 20 edge case tests
- Add explicit supported-subset documentation to issue-location.ts header
- Add 20 tests covering hex numbers, leading decimals, Infinity, NaN,
null/bool, trailing commas, deep nesting, unicode escapes, multi-line
strings, unicode keys, escaped quotes, numeric separators, block comments,
mixed quotes, empty objects/arrays, and graceful degradation
* fix(config): address autoreview findings on JSON5 docs and tests
- Remove numeric separator claim (not valid JSON5)
- Remove 'None known' unsupported-syntax claim, document known limitations
- Restructure scalar tests to exercise skipVal (resolve sibling after exotic value)
- Remove numeric separator test
* fix(config): constrain received value diagnostics
* fix(config): preserve JSON5 diagnostic locations
* fix(config): preserve validation path ownership
* fix(config): redact dotted plugin paths
* test(config): avoid diagnostic fixture shadowing
* refactor(config): privatize diagnostic helpers
---------
Co-authored-by: Erick Kinnee <erick@ekinnee.dev>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(cli): narrow config hint branch
* Plan config hints from actual changes
* Plan direct unset hints from actual changes
* Expand broad unset hint paths
* fix(cli): respect reload mode in config hints
---------
Co-authored-by: Kiran Magic <kiran@Alices-Laptop.local>
Co-authored-by: kiranmagic7 <262980978+kiranmagic7@users.noreply.github.com>
Replace the hardcoded Mattermost-only open-DM config check with a generic,
plugin-agnostic warning driven by a single shared evaluator
(evaluateDmPolicyAllowFromDependency) reused by the Zod refinements and the
CLI validator. Surface warnings at 'config validate' and on config load.
Remove the Mattermost-specific status-issues module now covered generically;
keep the runtime drop-log diagnostic.
Route invalid-config recovery output for source-only installed plugin packages to plugin packaging guidance instead of openclaw doctor --fix.
Validated with focused config/CLI/gateway/plugin tests, autoreview, Crabbox/Testbox E2E tbx_01ksgr80tnvvc13kv6t126yv78, and green PR CI on 3b3ce73d0f.
Thanks @brokemac79.
* Add config unset dry-run
Add --dry-run support to config unset, including JSON output and allow-exec validation parity with config set/patch dry-run handling.
* Refresh checks after proof update
* fix(config): address unset dry-run review
Return structured JSON when config unset dry-run misses a path and validate broad secret provider/default unsets against affected SecretRefs.