Environment
Description
On a reinstall (when ~/.config/opencode/opencode.json already exists from a prior install), the installer's "update API key and URL in existing config" code path uses sed to rewrite the baseURL line. The sed pattern is greedy through end-of-line and drops the trailing comma, leaving:
"options": {
"baseURL": "http://127.0.0.1:11434/v1" <-- missing comma
"apiKey": "no-key"
},
That is invalid JSON. The downstream opencode-web.service (user systemd unit) parses the file with cp opencode.json config.json, fails with CommaExpected at line 11, column 9, and goes into a permanent crash-loop. On my machine the systemd restart counter had reached 1265 by the time I noticed, because the broken state survives across reboots.
The from-scratch heredoc generator on lines 127–152 is fine — it only triggers the bug on the update path on lines 156–157.
Steps to Reproduce
- Run
./install.sh --tier 1 on a clean machine — install succeeds, ~/.config/opencode/opencode.json is generated correctly with valid JSON.
- Run the installer a second time (any tier, any flags) — the existing-file branch in
07-devtools.sh runs the two sed commands.
cat ~/.config/opencode/opencode.json — observe the missing comma after "baseURL".
systemctl --user status opencode-web — observe Active: activating (auto-restart) (Result: exit-code) with high restart counter.
journalctl --user -u opencode-web -n 50 — confirms CommaExpected at line 11, column 9.
Expected Behavior
A reinstall should leave opencode.json as valid JSON. The OpenCode Web service should start successfully on 127.0.0.1:3003.
Actual Behavior
opencode.json becomes invalid JSON after the first reinstall. opencode-web.service crash-loops indefinitely. Port 3003 never comes up. The installer's "Linking OpenCode Web" phase blocks for ~2 minutes waiting for the service before timing out and continuing.
Relevant Logs / Output
dream-server/installers/phases/07-devtools.sh:155-157:
# Reinstall: update API key and URL in existing config (key may have changed)
_sed_i "s|\"apiKey\":.*|\"apiKey\": \"${_opencode_key}\"|" "$OPENCODE_CONFIG_DIR/opencode.json"
_sed_i "s|\"baseURL\":.*|\"baseURL\": \"${_opencode_url}\"|" "$OPENCODE_CONFIG_DIR/opencode.json"
The .* in "baseURL":.* matches through the trailing , on the original line, and the replacement string does not put it back.
journalctl excerpt:
opencode[61671]: --- Errors ---
opencode[61671]: CommaExpected at line 11, column 9
opencode[61671]: Line 11: "apiKey": "no-key"
opencode[61671]: ^
opencode[61671]: --- End ---
systemd[233]: opencode-web.service: Failed with result 'exit-code'.
systemd[233]: opencode-web.service: Scheduled restart job, restart counter is at 1265.
Root Cause Hypothesis
The two sed patterns at 07-devtools.sh:156-157 are not JSON-aware. They match to end-of-line via .*, which on the baseURL line consumes the trailing , separator. The apiKey line is the last key inside options so it has no trailing comma — the apiKey sed is harmless. The baseURL sed is the corrupting one.
Two reasonable fixes (in increasing order of robustness):
-
Cheap fix — preserve the trailing comma explicitly in the replacement:
_sed_i "s|\"baseURL\": *\"[^\"]*\"|\"baseURL\": \"${_opencode_url}\"|" "$OPENCODE_CONFIG_DIR/opencode.json"
_sed_i "s|\"apiKey\": *\"[^\"]*\"|\"apiKey\": \"${_opencode_key}\"|" "$OPENCODE_CONFIG_DIR/opencode.json"
Match only the quoted value, leave the rest of the line (including the comma) untouched.
-
Robust fix — use jq if available, fall back to the regenerate-from-template path:
if command -v jq >/dev/null 2>&1; then
jq --arg url "$_opencode_url" --arg key "$_opencode_key" \
'.provider["llama-server"].options.baseURL = $url
| .provider["llama-server"].options.apiKey = $key' \
"$OPENCODE_CONFIG_DIR/opencode.json" > "$OPENCODE_CONFIG_DIR/opencode.json.tmp" \
&& mv "$OPENCODE_CONFIG_DIR/opencode.json.tmp" "$OPENCODE_CONFIG_DIR/opencode.json"
else
# Fall back to the heredoc — overwrite is safer than a broken sed
...
fi
Either fix should also re-sync config.json from opencode.json (the existing cp on line 161 already does this).
Severity / Impact
Medium. Doesn't break the main DreamServer stack — Open WebUI / llama-server / dashboard all install fine. But:
- OpenCode Web (port 3003) is silently broken on every reinstall.
- The installer's "Linking OpenCode Web" phase wastes ~2 minutes waiting for a service that will never come up.
- A naive user re-running
./install.sh to update will degrade their working OpenCode Web setup. The bug is silent — the installer prints ✓ OpenCode config updated (API key and URL refreshed) even though it just corrupted the file.
Environment
upstream/mainatcd11612(merge of PR feat(extensions): service templates — curated multi-extension presets Light-Heart-Labs/DreamServer#887) + locally-merged PRs feat(dashboard): status tooltips, legend, and Extensions visual alignment Light-Heart-Labs/DreamServer#865, fix(macos-installer): guard Bash 4+ and auto-install via Homebrew Light-Heart-Labs/DreamServer#889, fix(dashboard-api): invalidate .compose-flags via host agent on mutations Light-Heart-Labs/DreamServer#890, fix(extensions): clean up stale progress files on disable/uninstall Light-Heart-Labs/DreamServer#891, fix(extensions): clean up stale progress files on purge Light-Heart-Labs/DreamServer#892./install.sh --tier 1 --non-interactiveDescription
On a reinstall (when
~/.config/opencode/opencode.jsonalready exists from a prior install), the installer's "update API key and URL in existing config" code path usessedto rewrite thebaseURLline. The sed pattern is greedy through end-of-line and drops the trailing comma, leaving:That is invalid JSON. The downstream
opencode-web.service(user systemd unit) parses the file withcp opencode.json config.json, fails withCommaExpected at line 11, column 9, and goes into a permanent crash-loop. On my machine the systemd restart counter had reached 1265 by the time I noticed, because the broken state survives across reboots.The from-scratch heredoc generator on lines 127–152 is fine — it only triggers the bug on the update path on lines 156–157.
Steps to Reproduce
./install.sh --tier 1on a clean machine — install succeeds,~/.config/opencode/opencode.jsonis generated correctly with valid JSON.07-devtools.shruns the twosedcommands.cat ~/.config/opencode/opencode.json— observe the missing comma after"baseURL".systemctl --user status opencode-web— observeActive: activating (auto-restart) (Result: exit-code)with high restart counter.journalctl --user -u opencode-web -n 50— confirmsCommaExpected at line 11, column 9.Expected Behavior
A reinstall should leave
opencode.jsonas valid JSON. The OpenCode Web service should start successfully on127.0.0.1:3003.Actual Behavior
opencode.jsonbecomes invalid JSON after the first reinstall.opencode-web.servicecrash-loops indefinitely. Port 3003 never comes up. The installer's "Linking OpenCode Web" phase blocks for ~2 minutes waiting for the service before timing out and continuing.Relevant Logs / Output
dream-server/installers/phases/07-devtools.sh:155-157:The
.*in"baseURL":.*matches through the trailing,on the original line, and the replacement string does not put it back.journalctl excerpt:
Root Cause Hypothesis
The two
sedpatterns at07-devtools.sh:156-157are not JSON-aware. They match to end-of-line via.*, which on thebaseURLline consumes the trailing,separator. TheapiKeyline is the last key insideoptionsso it has no trailing comma — the apiKey sed is harmless. The baseURL sed is the corrupting one.Two reasonable fixes (in increasing order of robustness):
Cheap fix — preserve the trailing comma explicitly in the replacement:
Match only the quoted value, leave the rest of the line (including the comma) untouched.
Robust fix — use
jqif available, fall back to the regenerate-from-template path:Either fix should also re-sync
config.jsonfromopencode.json(the existingcpon line 161 already does this).Severity / Impact
Medium. Doesn't break the main DreamServer stack — Open WebUI / llama-server / dashboard all install fine. But:
./install.shto update will degrade their working OpenCode Web setup. The bug is silent — the installer prints✓ OpenCode config updated (API key and URL refreshed)even though it just corrupted the file.