@@ -33,17 +33,9 @@ export type RunWslOptions = {
3333const DEFAULT_WSL_TIMEOUT_MS = 20_000
3434const 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.
4436export 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
4941export 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 ( / ^ ( H K E Y _ C U R R E N T _ U S E R \\ S o f t w a r e \\ M i c r o s o f t \\ W i n d o w s \\ C u r r e n t V e r s i o n \\ L x s s \\ \{ [ ^ } ] + \} ) / i)
248- if ( ! header ) continue
249- const name = block . match ( / ^ \s + D i s t r i b u t i o n N a m e \s + R E G _ S Z \s + ( .+ ?) \s * $ / m) ?. [ 1 ]
250- if ( ! name ) continue
251- const uidHex = block . match ( / ^ \s + D e f a u l t U i d \s + R E G _ D W O R D \s + 0 x ( [ 0 - 9 a - f ] + ) \s * $ / im) ?. [ 1 ] ?? "0"
252- const stateHex = block . match ( / ^ \s + S t a t e \s + R E G _ D W O R D \s + 0 x ( [ 0 - 9 a - f ] + ) \s * $ / im) ?. [ 1 ] ?? "0"
253- const versionHex = block . match ( / ^ \s + V e r s i o n \s + R E G _ D W O R D \s + 0 x ( [ 0 - 9 a - 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-
264202export 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
405311export 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