Skip to content

Commit 5778008

Browse files
committed
deslop
1 parent b70fc22 commit 5778008

3 files changed

Lines changed: 11 additions & 118 deletions

File tree

packages/desktop-electron/src/main/apps.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export async function wslPath(path: string, mode: "windows" | "linux" | null, di
4545

4646
const flag = mode === "windows" ? "-w" : "-u"
4747
try {
48-
const resolved = path.startsWith("~") ? `${distro ? await resolveWslHome(distro) : "/root"}${path.slice(1)}` : path
48+
const resolved = path.startsWith("~") ? `${await resolveWslHome(distro)}${path.slice(1)}` : path
4949
const input = mode === "linux" ? resolved.replace(/\\/g, "/") : resolved
5050
const output = await runWslInDistro(["wslpath", flag, input], distro)
5151
if (output.code !== 0) {

packages/desktop-electron/src/main/server.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,10 @@ export async function spawnWslSidecar(
8484
distro: string,
8585
opts: { onLine?: (line: WslCommandLine) => void; healthTimeoutMs?: number } = {},
8686
): Promise<WslSidecar> {
87-
// Every wsl.exe invocation below goes through wslArgs which injects
88-
// `--user root`. That matters even when a distro has DefaultUid=0
89-
// (i.e. the interactive first-run user account setup never ran):
90-
// explicit --user root bypasses the OOBE hook that would otherwise
91-
// prompt on stdin, so we can resolve opencode and spawn the sidecar
92-
// without any machine-wide first-run handshake. The earlier Ubuntu
93-
// hang was caused by invoking without --user (default uid 0 triggers
94-
// OOBE), not by the registry state itself. We still have a 20s
95-
// timeout in runCommand as a safety net for true wsl.exe wedges.
87+
// Do not pass --user here: the sidecar should inherit the distro's
88+
// default user so config, auth, git, ssh, and file ownership match the
89+
// user's normal WSL environment. If that default user is root, WSL will
90+
// choose root itself.
9691
const opencode = await resolveWslOpencode(distro)
9792
if (!opencode) throw new Error(`OpenCode is not installed in ${distro}`)
9893

packages/desktop-electron/src/main/wsl.ts

Lines changed: 6 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,9 @@ export type RunWslOptions = {
3333
const DEFAULT_WSL_TIMEOUT_MS = 20_000
3434
const DEFAULT_WSL_INSTALL_TIMEOUT_MS = 15 * 60_000
3535

36-
// `--user root` bypasses the distro's default-user requirement. A freshly
37-
// installed WSL distro (Ubuntu-24.04 in particular) prompts interactively
38-
// for a username/password on its first invocation; when spawned with
39-
// piped stdio that prompt blocks forever or silently reads garbage,
40-
// leaving the sidecar hanging and the server unhealthy. Running as root
41-
// sidesteps the entire first-run setup flow — opencode only needs an
42-
// HTTP listener in the distro, not a per-user environment, so root is
43-
// a safe default for the sidecar process.
4436
export function wslArgs(args: string[], distro?: string | null) {
45-
if (distro) return ["-d", distro, "--user", "root", "--", ...args]
46-
return ["--user", "root", "--", ...args]
37+
if (distro) return ["-d", distro, "--", ...args]
38+
return ["--", ...args]
4739
}
4840

4941
export function runWsl(args: string[], opts: RunWslOptions = {}) {
@@ -207,60 +199,6 @@ export function runWslInDistro(args: string[], distro?: string | null, opts?: Ru
207199
return runWsl(wslArgs(args, distro), opts)
208200
}
209201

210-
export type WslRegistryDistro = {
211-
name: string
212-
defaultUid: number
213-
state: number
214-
version: number
215-
}
216-
217-
// Read LXSS metadata from the Windows registry. This never invokes
218-
// wsl.exe, so it is safe to call when wsl.exe itself is wedged.
219-
// DefaultUid === 0 on a user-oriented distro means the first-run
220-
// "Create a default UNIX user account" step never completed.
221-
//
222-
// Uses a `reg query` fallback strategy because some hosts (e.g. Electron
223-
// spawning PowerShell with certain user profiles) return nothing from the
224-
// PowerShell registry provider; parsing `reg query` output is ugly but
225-
// native Windows and always available.
226-
export async function readWslDistrosFromRegistry(opts?: RunWslOptions): Promise<WslRegistryDistro[]> {
227-
// `reg query` prints each subkey's values in a stable format:
228-
//
229-
// HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lxss\{guid}
230-
// DistributionName REG_SZ Ubuntu-24.04
231-
// DefaultUid REG_DWORD 0x0
232-
// State REG_DWORD 0x1
233-
// Version REG_DWORD 0x2
234-
// ...
235-
const result = await runCommand(
236-
"reg.exe",
237-
["query", "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Lxss", "/s"],
238-
opts,
239-
)
240-
const stdout = result.stdout
241-
if (result.code !== 0 || !stdout) {
242-
return []
243-
}
244-
const blocks = stdout.split(/\r?\n\r?\n/)
245-
const out: WslRegistryDistro[] = []
246-
for (const block of blocks) {
247-
const header = block.match(/^(HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Lxss\\\{[^}]+\})/i)
248-
if (!header) continue
249-
const name = block.match(/^\s+DistributionName\s+REG_SZ\s+(.+?)\s*$/m)?.[1]
250-
if (!name) continue
251-
const uidHex = block.match(/^\s+DefaultUid\s+REG_DWORD\s+0x([0-9a-f]+)\s*$/im)?.[1] ?? "0"
252-
const stateHex = block.match(/^\s+State\s+REG_DWORD\s+0x([0-9a-f]+)\s*$/im)?.[1] ?? "0"
253-
const versionHex = block.match(/^\s+Version\s+REG_DWORD\s+0x([0-9a-f]+)\s*$/im)?.[1] ?? "0"
254-
out.push({
255-
name,
256-
defaultUid: Number.parseInt(uidHex, 16),
257-
state: Number.parseInt(stateHex, 16),
258-
version: Number.parseInt(versionHex, 16),
259-
})
260-
}
261-
return out
262-
}
263-
264202
export function runWslSh(script: string, distro?: string | null, opts?: RunWslOptions) {
265203
return runWslInDistro(["sh", "-lc", script], distro, opts)
266204
}
@@ -366,55 +304,15 @@ export async function probeWslDistro(name: string, opts?: RunWslOptions): Promis
366304
}
367305
}
368306

369-
async function readWslDefaultUser(distro: string, opts?: RunWslOptions) {
370-
const entry = (await readWslDistrosFromRegistry(opts)).find((item) => item.name === distro)
371-
if (!entry || entry.defaultUid === 0) return null
372-
373-
const passwd = firstLine(
374-
(
375-
await runWslSh(
376-
[
377-
"if command -v getent >/dev/null 2>&1; then",
378-
` getent passwd ${entry.defaultUid}`,
379-
"else",
380-
` awk -F: '$3 == ${entry.defaultUid} { print; exit }' /etc/passwd`,
381-
"fi",
382-
].join("\n"),
383-
distro,
384-
opts,
385-
)
386-
).stdout,
387-
)
388-
if (!passwd) return null
389-
390-
const parts = passwd.split(":")
391-
const username = parts[0]?.trim() ?? ""
392-
const home = parts[5]?.trim() ?? ""
393-
if (!home) return null
394-
return { username: username || null, home }
395-
}
396-
397-
export async function resolveWslHome(distro: string, opts?: RunWslOptions) {
398-
return (await readWslDefaultUser(distro, opts))?.home ?? "/root"
399-
}
400-
401-
function opencodeCandidate(path: string) {
402-
return `if [ -x ${shellEscape(path)} ]; then printf "%s\\n" ${shellEscape(path)}; fi`
307+
export async function resolveWslHome(distro?: string | null, opts?: RunWslOptions) {
308+
return firstLine((await runWslSh('printf "%s\\n" "$HOME"', distro, opts)).stdout) ?? "/"
403309
}
404310

405311
export async function resolveWslOpencode(distro: string, opts?: RunWslOptions) {
406-
const command = firstLine((await runWslSh("command -v opencode 2>/dev/null || true", distro, opts)).stdout)
407-
if (command && !command.startsWith("/mnt/")) return command
312+
const command = firstLine((await runWslSh("command -v opencode 2>/dev/null | grep -v '^/mnt/' | head -n 1 || true", distro, opts)).stdout)
313+
if (command) return command
408314

409-
const home = await resolveWslHome(distro, opts)
410315
for (const candidate of [
411-
...(home !== "/root"
412-
? [
413-
opencodeCandidate(`${home}/.local/bin/opencode`),
414-
opencodeCandidate(`${home}/bin/opencode`),
415-
opencodeCandidate(`${home}/.opencode/bin/opencode`),
416-
]
417-
: []),
418316
'if [ -x "${XDG_BIN_DIR:-$HOME/.local/bin}/opencode" ]; then printf "%s\\n" "${XDG_BIN_DIR:-$HOME/.local/bin}/opencode"; fi',
419317
'if [ -x "$HOME/bin/opencode" ]; then printf "%s\\n" "$HOME/bin/opencode"; fi',
420318
'if [ -x "$HOME/.opencode/bin/opencode" ]; then printf "%s\\n" "$HOME/.opencode/bin/opencode"; fi',

0 commit comments

Comments
 (0)