fix(desktop): load WSL shell environment
This commit is contained in:
parent
c9db6e9a1f
commit
11b14dff4d
3 changed files with 62 additions and 16 deletions
29
packages/desktop/src/main/wsl/sidecar-shell.test.ts
Normal file
29
packages/desktop/src/main/wsl/sidecar-shell.test.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { expect, test } from "bun:test"
|
||||
import { chmod, mkdtemp, rm, writeFile } from "node:fs/promises"
|
||||
import { tmpdir } from "node:os"
|
||||
import { join } from "node:path"
|
||||
import { wslSidecarShell } from "./sidecar-shell"
|
||||
|
||||
test("loads interactive WSL environment variables for the desktop server", async () => {
|
||||
const home = await mkdtemp(join(tmpdir(), "opencode-wsl-env-"))
|
||||
const opencode = join(home, "opencode")
|
||||
await writeFile(join(home, ".profile"), 'source "$HOME/.bashrc"\n')
|
||||
await writeFile(join(home, ".bashrc"), "export COMPANY_BASEURL=https://company.example\n")
|
||||
await writeFile(opencode, '#!/usr/bin/env bash\nprintf "%s" "$COMPANY_BASEURL"\n')
|
||||
await chmod(opencode, 0o755)
|
||||
|
||||
const shell = wslSidecarShell(opencode, 4096, "opencode", "secret", false)
|
||||
const proc = Bun.spawn(shell.args, {
|
||||
cwd: home,
|
||||
env: { ...process.env, HOME: home },
|
||||
stdin: new Blob([shell.script]),
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
})
|
||||
const output = await new Response(proc.stdout).text()
|
||||
await new Response(proc.stderr).text()
|
||||
const code = await proc.exited
|
||||
await rm(home, { recursive: true, force: true })
|
||||
|
||||
expect({ code, output }).toEqual({ code: 0, output: "https://company.example" })
|
||||
})
|
||||
28
packages/desktop/src/main/wsl/sidecar-shell.ts
Normal file
28
packages/desktop/src/main/wsl/sidecar-shell.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { shellEscape } from "./runtime"
|
||||
|
||||
export function wslSidecarShell(opencode: string, port: number, username: string, password: string, packaged: boolean) {
|
||||
return {
|
||||
// Explicit WSL commands skip login startup files. Load the same user environment
|
||||
// as a terminal launch, then replace the interactive shell before reading stdin.
|
||||
args: [
|
||||
"bash",
|
||||
"--noprofile",
|
||||
"--norc",
|
||||
"-ic",
|
||||
'for file in "$HOME/.bash_profile" "$HOME/.bash_login" "$HOME/.profile"; do [[ ! -r "$file" ]] || { source "$file"; break; }; done; exec bash -se',
|
||||
],
|
||||
script: [
|
||||
"set -euo pipefail",
|
||||
'cd "$HOME" || cd /',
|
||||
'PATH=$(awk -v RS=: -v ORS=: \'$0 !~ /^\\/mnt\\//\' <<<"$PATH" | sed "s/:$//")',
|
||||
"export PATH",
|
||||
"export WSLENV=",
|
||||
"export OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER=true",
|
||||
"export OPENCODE_CLIENT=desktop",
|
||||
`export OPENCODE_SERVER_USERNAME=${shellEscape(username)}`,
|
||||
`export OPENCODE_SERVER_PASSWORD=${shellEscape(password)}`,
|
||||
'export XDG_STATE_HOME="$HOME/.local/state"',
|
||||
`exec ${shellEscape(opencode)} --print-logs --log-level ${packaged ? "WARN" : "INFO"} serve --hostname 0.0.0.0 --port ${port}`,
|
||||
].join("\n"),
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,8 @@ import { randomUUID } from "node:crypto"
|
|||
import { createServer } from "node:net"
|
||||
import { app } from "electron"
|
||||
import { checkHealth } from "../server"
|
||||
import { type WslCommandLine, resolveWslOpencode, shellEscape, wslArgs } from "./runtime"
|
||||
import { type WslCommandLine, resolveWslOpencode, wslArgs } from "./runtime"
|
||||
import { wslSidecarShell } from "./sidecar-shell"
|
||||
import { pollWslHealth } from "./startup"
|
||||
|
||||
export type WslSidecar = {
|
||||
|
|
@ -23,24 +24,12 @@ export async function spawnWslSidecar(
|
|||
const port = await allocatePort()
|
||||
const password = randomUUID()
|
||||
const username = "opencode"
|
||||
const script = [
|
||||
"set -euo pipefail",
|
||||
'cd "$HOME" || cd /',
|
||||
'PATH=$(awk -v RS=: -v ORS=: \'$0 !~ /^\\/mnt\\//\' <<<"$PATH" | sed "s/:$//")',
|
||||
"export PATH",
|
||||
"export WSLENV=",
|
||||
"export OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER=true",
|
||||
"export OPENCODE_CLIENT=desktop",
|
||||
`export OPENCODE_SERVER_USERNAME=${shellEscape(username)}`,
|
||||
`export OPENCODE_SERVER_PASSWORD=${shellEscape(password)}`,
|
||||
'export XDG_STATE_HOME="$HOME/.local/state"',
|
||||
`exec ${shellEscape(opencode)} --print-logs --log-level ${app.isPackaged ? "WARN" : "INFO"} serve --hostname 0.0.0.0 --port ${port}`,
|
||||
].join("\n")
|
||||
const child = spawn("wsl", wslArgs(["bash", "-se"], distro), {
|
||||
const shell = wslSidecarShell(opencode, port, username, password, app.isPackaged)
|
||||
const child = spawn("wsl", wslArgs(shell.args, distro), {
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
windowsHide: true,
|
||||
})
|
||||
child.stdin.end(script)
|
||||
child.stdin.end(shell.script)
|
||||
|
||||
const recentOutput: string[] = []
|
||||
const emit = (line: WslCommandLine) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue