Client or integration
OpenCodex dashboard
Area
Other
Summary
Enabling the DSH integration from the dashboard never applies. The panel shows this refusal, and the file is left untouched:
无法安全地更改配置: C:\Users\<user>\.dsh\settings.yaml uses YAML source opencodex cannot patch without risking unrelated comments or formatting, so it was left alone
The CLI agrees, and 恢复中心 stays empty, so nothing was written and no snapshot exists:
clientId: dsh
state: absent
installed: true
configPath: $DSH_HOME\settings.yaml
snapshotCount: 0
retentionDegraded: false
The trigger is an empty llm-pi-ai.providers container. The source-preserving patcher in src/integrations/omp-yaml-source.ts requires every segment of the path ["llm-pi-ai", "providers", "opencodex"] to already be a non-empty plain block map. When llm-pi-ai.providers exists but is empty, locatePath returns null instead of a missing-leaf insertion point, patchYamlFragmentSource returns null, and src/integrations/writer.ts turns that into the "left alone" refusal.
What I expected instead: an empty container should be an ordinary insertion case, exactly like an absent llm-pi-ai key already is. Both are states a user reaches without hand-editing YAML.
Reproduction
Minimal $DSH_HOME/settings.yaml:
llm-pi-ai:
providers: {}
ui-theme:
preference: system
ocx start
- Open the dashboard, select DSH on the integration page, and toggle it on.
- Observe the refusal box. Nothing is written, and 恢复中心 registers no entry.
ocx integration client status --client dsh reports state: absent and snapshotCount: 0.
I also drove the shipped patcher directly, calling patchYamlFragmentSource from src/integrations/omp-yaml-source.ts with the real settings file, the path above, and a small generated provider block. Five cases:
A) providers: {} -> null (refused)
B) providers: -> null (refused) (empty block key; parses as null)
C) providers: with one sibling -> patched OK
D) providers: with opencodex -> patched OK
E) llm-pi-ai key absent entirely -> patched OK
Case A fails because isPlainBlockKey does not accept the providers: {} line, while readPath still finds the key defined, so locatePath returns null rather than falling through to the missing-key branch. Case B reaches the isPlainRecord(readPath(parsed, prefix)) guard, where an empty block key parses as null and the guard rejects it.
The empty shape is not exotic. It is what the file holds once the last provider entry is removed, and an integration that only applies when an unrelated provider already exists cannot be enabled on a clean profile.
Version
2.50.0
Operating system
Windows 11 Pro (10.0.26200)
Provider and model
Not provider-specific. Reproduced with no providers configured at all.
Logs or error output
PS> ocx integration client status --client dsh
clientId: dsh
state: absent
installed: true
configPath: $DSH_HOME\settings.yaml
snapshotCount: 0
retentionDegraded: false
Dashboard refusal: 无法安全地更改配置: $DSH_HOME\settings.yaml uses YAML source opencodex cannot patch without risking unrelated comments or formatting, so it was left alone
Redacted configuration
Suggested fix
Let locatePath treat an empty container as a missing leaf:
- accept a plain block key whose value is an empty inline collection (
providers: {}, providers: []) as a match, and
- allow
readPath(parsed, prefix) to be null or an empty record when the matched source line is a plain key with no block children, then insert the child at parentIndent + 2.
upsertSource already has the insertion branch for this case, so the change stays confined to how the path is located.
Checks
Client or integration
OpenCodex dashboard
Area
Other
Summary
Enabling the DSH integration from the dashboard never applies. The panel shows this refusal, and the file is left untouched:
The CLI agrees, and 恢复中心 stays empty, so nothing was written and no snapshot exists:
The trigger is an empty
llm-pi-ai.providerscontainer. The source-preserving patcher insrc/integrations/omp-yaml-source.tsrequires every segment of the path["llm-pi-ai", "providers", "opencodex"]to already be a non-empty plain block map. Whenllm-pi-ai.providersexists but is empty,locatePathreturns null instead of a missing-leaf insertion point,patchYamlFragmentSourcereturns null, andsrc/integrations/writer.tsturns that into the "left alone" refusal.What I expected instead: an empty container should be an ordinary insertion case, exactly like an absent
llm-pi-aikey already is. Both are states a user reaches without hand-editing YAML.Reproduction
Minimal
$DSH_HOME/settings.yaml:ocx startocx integration client status --client dshreportsstate: absentandsnapshotCount: 0.I also drove the shipped patcher directly, calling
patchYamlFragmentSourcefromsrc/integrations/omp-yaml-source.tswith the real settings file, the path above, and a small generated provider block. Five cases:Case A fails because
isPlainBlockKeydoes not accept theproviders: {}line, whilereadPathstill finds the key defined, solocatePathreturns null rather than falling through to the missing-key branch. Case B reaches theisPlainRecord(readPath(parsed, prefix))guard, where an empty block key parses as null and the guard rejects it.The empty shape is not exotic. It is what the file holds once the last provider entry is removed, and an integration that only applies when an unrelated provider already exists cannot be enabled on a clean profile.
Version
2.50.0
Operating system
Windows 11 Pro (10.0.26200)
Provider and model
Not provider-specific. Reproduced with no providers configured at all.
Logs or error output
Dashboard refusal: 无法安全地更改配置:
$DSH_HOME\settings.yamluses YAML source opencodex cannot patch without risking unrelated comments or formatting, so it was left aloneRedacted configuration
Suggested fix
Let
locatePathtreat an empty container as a missing leaf:providers: {},providers: []) as a match, andreadPath(parsed, prefix)to be null or an empty record when the matched source line is a plain key with no block children, then insert the child atparentIndent + 2.upsertSourcealready has the insertion branch for this case, so the change stays confined to how the path is located.Checks