Summary
The getUrl() method in ServerManager.ts appends a base64-encoded project directory path to the OpenCode server URL. However, the OpenCode server does not recognize any path prefix — all requests with this prefix return the SPA HTML page instead of the expected JSON API response, causing the health check to always fail (false negative) and the iframe to load incorrectly.
Steps to Reproduce
- Install the plugin and configure it to start the OpenCode server
- Observe that the iframe spins indefinitely (does not load the OpenCode UI)
- Open Obsidian Developer Tools → Network tab
- Observe requests like:
GET http://127.0.0.1:4302/{base64-encoded-path}/global/health → returns HTTP 200 with Content-Type: text/html (SPA page)
POST http://127.0.0.1:4302/{base64-encoded-path}/session → also returns SPA HTML
Expected Behavior
GET http://127.0.0.1:4302/global/health should return JSON: {"healthy": true, "version": "1.15.4"} with Content-Type: application/json
- The iframe should load the SPA at
http://127.0.0.1:4302/ (without path prefix)
- The health check should correctly validate server health
Root Cause
src/server/ServerManager.ts (L78-81):
getUrl(): string {
const encodedPath = Buffer.from(this.projectDirectory).toString('base64');
return `http://${this.settings.hostname}:${this.settings.port}/${encodedPath}`;
}
This method encodes the projectDirectory (the Obsidian vault root path) into the URL. But the OpenCode server (verified on v1.15.4) has no concept of a path-prefixed base URL. Running opencode serve --help shows only --port, --hostname, and --cors options — no base-path or project-directory URL prefix.
Health check also uses this URL:
checkServerHealth() {
const url = `${this.getUrl()}/global/health`;
return fetch(url, { method: "GET" })
.then(r => 200 === r.status && "healthy" === r.statusText)
.catch(() => false);
}
Since the SPA returns status 200 with statusText "OK" (not "healthy"), the check always fails.
Compounding issue: In src/main.ts, the getProjectDirectory() method uses:
const projectDirectory = this.settings.projectDirectory || this.app.vault.getRoot().path;
The || operator treats empty string as falsy, so even if the external launcher sets projectDirectory: "" in data.json, the plugin falls back to the vault path. This means the user cannot work around this bug from outside the plugin.
Impact
- Plugin iframe never loads the OpenCode UI correctly
- Health check never passes, causing the server to be repeatedly restarted in an infinite loop
- The plugin is effectively non-functional when started via the plugin's launch mechanism
Proposed Fix
Modify getUrl() to return the raw server URL without encoding the project directory:
getUrl(): string {
return `http://${this.settings.hostname}:${this.settings.port}`;
}
The projectDirectory is already correctly used as the server's cwd (working directory) when spawning the process — it does not need to be part of the URL. The health check would then correctly hit GET /global/health, and the iframe would load the SPA at the root URL.
Environment
- Plugin version: latest (fetched from main branch)
- OpenCode server: opencode-ai via npm
- OS: Windows
Summary
The
getUrl()method inServerManager.tsappends a base64-encoded project directory path to the OpenCode server URL. However, the OpenCode server does not recognize any path prefix — all requests with this prefix return the SPA HTML page instead of the expected JSON API response, causing the health check to always fail (false negative) and the iframe to load incorrectly.Steps to Reproduce
GET http://127.0.0.1:4302/{base64-encoded-path}/global/health→ returns HTTP 200 withContent-Type: text/html(SPA page)POST http://127.0.0.1:4302/{base64-encoded-path}/session→ also returns SPA HTMLExpected Behavior
GET http://127.0.0.1:4302/global/healthshould return JSON:{"healthy": true, "version": "1.15.4"}withContent-Type: application/jsonhttp://127.0.0.1:4302/(without path prefix)Root Cause
src/server/ServerManager.ts(L78-81):This method encodes the
projectDirectory(the Obsidian vault root path) into the URL. But the OpenCode server (verified on v1.15.4) has no concept of a path-prefixed base URL. Runningopencode serve --helpshows only--port,--hostname, and--corsoptions — no base-path or project-directory URL prefix.Health check also uses this URL:
Since the SPA returns status 200 with statusText "OK" (not "healthy"), the check always fails.
Compounding issue: In
src/main.ts, thegetProjectDirectory()method uses:The
||operator treats empty string as falsy, so even if the external launcher setsprojectDirectory: ""indata.json, the plugin falls back to the vault path. This means the user cannot work around this bug from outside the plugin.Impact
Proposed Fix
Modify
getUrl()to return the raw server URL without encoding the project directory:The
projectDirectoryis already correctly used as the server'scwd(working directory) when spawning the process — it does not need to be part of the URL. The health check would then correctly hitGET /global/health, and the iframe would load the SPA at the root URL.Environment