2026-07-29 18:34:06 +08:00
|
|
|
/**
|
|
|
|
|
* Boot-time backend resolution for the adaptive directory-picker composition:
|
|
|
|
|
* one pure decision from sampled host facts to a concrete backend kind. The
|
|
|
|
|
* caller samples exactly once per boot, so the mounted capability stays
|
|
|
|
|
* stable for the service lifetime as the seam requires.
|
|
|
|
|
* @module @deepseek-ai/dsh-host-directory-picker-auto/resolve
|
|
|
|
|
*/
|
|
|
|
|
|
2026-07-29 21:09:02 +08:00
|
|
|
import type { Config as HttpServerConfig } from '@deepseek-ai/dsh-host-webserver'
|
|
|
|
|
|
2026-07-29 18:34:06 +08:00
|
|
|
/** Concrete interaction backend the resolver chooses between. */
|
|
|
|
|
export type DirectoryPickerBackendKind = 'native' | 'browse'
|
|
|
|
|
|
|
|
|
|
/** Environment keys the resolution reads (a `process.env` subset). */
|
|
|
|
|
export type DirectoryPickerEnv = Readonly<
|
|
|
|
|
Partial<Record<'SSH_CONNECTION' | 'SSH_TTY' | 'DISPLAY' | 'WAYLAND_DISPLAY', string>>
|
|
|
|
|
>
|
|
|
|
|
|
|
|
|
|
/** Host facts the backend choice is a pure function of, sampled once at boot. */
|
|
|
|
|
export interface DirectoryPickerHostFacts {
|
2026-07-29 21:09:02 +08:00
|
|
|
/** Effective webserver bind host (the schema's closed loopback/all-interfaces union). */
|
|
|
|
|
bindHost: HttpServerConfig['host']
|
2026-07-29 18:34:06 +08:00
|
|
|
/** Host process platform. */
|
|
|
|
|
platform: NodeJS.Platform
|
|
|
|
|
/** Environment sample; SSH marks a remote operator, DISPLAY/WAYLAND_DISPLAY a Linux display. */
|
|
|
|
|
env: DirectoryPickerEnv
|
2026-07-29 21:09:02 +08:00
|
|
|
/** Whether a Linux chooser binary the native backend can drive (zenity/kdialog) is on PATH; consulted only when `platform` is linux. */
|
|
|
|
|
linuxChooser: boolean
|
2026-07-29 18:34:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** An env value counts only when set and non-blank (an empty export is "unset" by shell convention). */
|
|
|
|
|
const present = (value: string | undefined): boolean => value !== undefined && value !== ''
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resolve which backend serves this boot. `native` requires every signal that
|
2026-07-29 21:09:02 +08:00
|
|
|
* the operator can see the host display and the native backend can serve it:
|
|
|
|
|
* a loopback-only bind (an all-interfaces bind admits remote browsers no OS
|
|
|
|
|
* chooser can reach), no SSH launch (under SSH port-forwarding the chooser
|
|
|
|
|
* would open on the unattended server), and a servable display session —
|
|
|
|
|
* assumed on darwin/win32, requiring `DISPLAY`/`WAYLAND_DISPLAY` plus a
|
|
|
|
|
* chooser binary on linux, and never true elsewhere (the native backend
|
|
|
|
|
* drives exactly darwin/win32/linux). Anything ambiguous resolves to
|
|
|
|
|
* `browse`, which works everywhere.
|
2026-07-29 18:34:06 +08:00
|
|
|
* @param facts - the sampled host facts.
|
|
|
|
|
* @returns the backend kind to mount.
|
|
|
|
|
*/
|
|
|
|
|
export function resolveDirectoryPickerBackend(facts: DirectoryPickerHostFacts): DirectoryPickerBackendKind {
|
|
|
|
|
if (facts.bindHost !== '127.0.0.1') return 'browse'
|
|
|
|
|
if (present(facts.env.SSH_CONNECTION) || present(facts.env.SSH_TTY)) return 'browse'
|
|
|
|
|
if (facts.platform === 'darwin' || facts.platform === 'win32') return 'native'
|
2026-07-29 21:09:02 +08:00
|
|
|
if (facts.platform !== 'linux' || !facts.linuxChooser) return 'browse'
|
2026-07-29 18:34:06 +08:00
|
|
|
return present(facts.env.DISPLAY) || present(facts.env.WAYLAND_DISPLAY) ? 'native' : 'browse'
|
|
|
|
|
}
|