217 lines
6.5 KiB
TypeScript
217 lines
6.5 KiB
TypeScript
import type { GlobalSettings } from '../../../../shared/types'
|
|
import {
|
|
deriveGlobalWindowsRuntimeDefaultFromLegacySettings,
|
|
normalizeGlobalWindowsRuntimeDefault
|
|
} from '../../../../shared/project-execution-runtime'
|
|
import {
|
|
buildWslLoginShellCommand,
|
|
escapeWslShCommandForWindows
|
|
} from '../../../../shared/wsl-login-shell-command'
|
|
import { buildAgentFeatureSkillInstallCommand } from '../../../../shared/agent-feature-install-commands'
|
|
import { toast } from 'sonner'
|
|
import type { CliInstallStatus } from '../../../../shared/cli-install-types'
|
|
import {
|
|
isOrcaCliAvailableOnPath,
|
|
showOrcaCliRegistrationPromptToast
|
|
} from '@/lib/agent-skill-cli-prerequisite'
|
|
import { translate } from '@/i18n/i18n'
|
|
|
|
export type LocalAgentRuntime = {
|
|
runtime: 'host' | 'wsl'
|
|
wslDistro?: string | null
|
|
label: string
|
|
}
|
|
|
|
const LOCAL_HOST_AGENT_RUNTIME: LocalAgentRuntime = {
|
|
runtime: 'host',
|
|
label: ''
|
|
}
|
|
|
|
export function getHostRuntimeLabel(): string {
|
|
return navigator.userAgent.includes('Windows') ? 'Windows' : 'This device'
|
|
}
|
|
|
|
export function getSelectedAgentRuntime(
|
|
settings: GlobalSettings,
|
|
wslSupportedPlatform: boolean,
|
|
wslAvailable: boolean,
|
|
wslCapabilitiesLoading: boolean
|
|
): LocalAgentRuntime {
|
|
const defaultRuntime = normalizeGlobalWindowsRuntimeDefault(
|
|
settings.localWindowsRuntimeDefault ??
|
|
deriveGlobalWindowsRuntimeDefaultFromLegacySettings(settings, {
|
|
wslAvailable: wslCapabilitiesLoading ? undefined : wslAvailable
|
|
}).defaultRuntime
|
|
)
|
|
if (wslSupportedPlatform && defaultRuntime.kind === 'wsl') {
|
|
const selectedDistro = defaultRuntime.distro?.trim() || null
|
|
return {
|
|
runtime: 'wsl',
|
|
wslDistro: selectedDistro,
|
|
label: selectedDistro
|
|
? `WSL ${selectedDistro}`
|
|
: translate('auto.components.settings.CliSkillRuntimeSetup.c47127f222', 'WSL default')
|
|
}
|
|
}
|
|
return { runtime: 'host', label: getHostRuntimeLabel() }
|
|
}
|
|
|
|
function quotePowerShellSingle(value: string): string {
|
|
return `'${value.replaceAll("'", "''")}'`
|
|
}
|
|
|
|
export function getWslCliDistroRequest(
|
|
runtime?: LocalAgentRuntime
|
|
): { distro: string } | undefined {
|
|
return runtime?.runtime === 'wsl' && runtime.wslDistro?.trim()
|
|
? { distro: runtime.wslDistro.trim() }
|
|
: undefined
|
|
}
|
|
|
|
export function buildSkillCommandForRuntime(
|
|
command: string,
|
|
runtime?: LocalAgentRuntime,
|
|
currentPlatform = getSkillCommandPlatform()
|
|
): string {
|
|
const resolvedRuntime = runtime ?? LOCAL_HOST_AGENT_RUNTIME
|
|
const normalizedCommand = normalizeWindowsSkillUpdateCommand(
|
|
command,
|
|
resolvedRuntime,
|
|
currentPlatform
|
|
)
|
|
if (resolvedRuntime.runtime !== 'wsl') {
|
|
return normalizedCommand
|
|
}
|
|
|
|
const distroArg = resolvedRuntime.wslDistro?.trim()
|
|
? ` -d ${quotePowerShellSingle(resolvedRuntime.wslDistro.trim())}`
|
|
: ''
|
|
const wslCommand = escapeWslShCommandForWindows(buildWslLoginShellCommand(normalizedCommand))
|
|
return `wsl.exe${distroArg} -- sh -c ${quotePowerShellSingle(wslCommand)}`
|
|
}
|
|
|
|
function normalizeWindowsSkillUpdateCommand(
|
|
command: string,
|
|
runtime: LocalAgentRuntime,
|
|
currentPlatform: NodeJS.Platform
|
|
): string {
|
|
if (runtime.runtime === 'wsl' || currentPlatform !== 'win32') {
|
|
return command
|
|
}
|
|
|
|
const trimmedCommand = command.trim()
|
|
const updateMatch = /^npx\s+skills\s+update\s+([A-Za-z0-9_-]+)\s+--global$/i.exec(trimmedCommand)
|
|
if (!updateMatch) {
|
|
return command
|
|
}
|
|
|
|
// Why: the `skills update` subcommand is currently unreliable on native
|
|
// Windows, while reinstalling from the same repo source is idempotent and
|
|
// keeps the setup affordance working.
|
|
return buildAgentFeatureSkillInstallCommand([updateMatch[1]])
|
|
}
|
|
|
|
function getSkillCommandPlatform(): NodeJS.Platform {
|
|
const platform =
|
|
typeof window === 'undefined' ? undefined : window.api?.platform?.get?.()?.platform
|
|
if (platform) {
|
|
return platform
|
|
}
|
|
|
|
const userAgent = typeof navigator === 'undefined' ? '' : navigator.userAgent
|
|
if (userAgent.includes('Windows')) {
|
|
return 'win32'
|
|
}
|
|
if (userAgent.includes('Mac')) {
|
|
return 'darwin'
|
|
}
|
|
return 'linux'
|
|
}
|
|
|
|
export function buildSkillInstallCommandForRuntime(
|
|
command: string,
|
|
runtime: LocalAgentRuntime
|
|
): string {
|
|
return buildSkillCommandForRuntime(command, runtime)
|
|
}
|
|
|
|
export function getSkillDiscoveryTargetForRuntime(
|
|
runtime: LocalAgentRuntime
|
|
): { runtime: 'wsl'; wslDistro?: string | null } | undefined {
|
|
return runtime.runtime === 'wsl'
|
|
? { runtime: 'wsl', wslDistro: runtime.wslDistro ?? null }
|
|
: undefined
|
|
}
|
|
|
|
export function getAgentSkillTerminalShellOverride(
|
|
currentPlatform: string,
|
|
settings: GlobalSettings,
|
|
runtime: LocalAgentRuntime
|
|
): string | undefined {
|
|
if (currentPlatform !== 'win32') {
|
|
return undefined
|
|
}
|
|
if (runtime.runtime === 'wsl') {
|
|
return 'powershell.exe'
|
|
}
|
|
return settings.terminalWindowsShell.toLowerCase() === 'wsl.exe' ? 'powershell.exe' : undefined
|
|
}
|
|
|
|
export async function ensureWslCliAvailableForAgentSkillTerminal(
|
|
runtime?: LocalAgentRuntime
|
|
): Promise<CliInstallStatus | null> {
|
|
const args = getWslCliDistroRequest(runtime)
|
|
try {
|
|
const status = await window.api.cli.getWslInstallStatus(args)
|
|
if (!status.supported) {
|
|
toast.warning(
|
|
translate(
|
|
'auto.components.settings.CliSkillRuntimeSetup.775a4cfbb8',
|
|
'WSL shell command registration is unavailable'
|
|
),
|
|
{
|
|
description:
|
|
status.detail ??
|
|
translate(
|
|
'auto.components.settings.CliSkillRuntimeSetup.fc0fcf72fd',
|
|
'Register the WSL shell command before skill setup.'
|
|
)
|
|
}
|
|
)
|
|
return status
|
|
}
|
|
if (status.state !== 'installed' || !status.pathConfigured) {
|
|
await showOrcaCliRegistrationPromptToast()
|
|
const next = await window.api.cli.installWsl(args)
|
|
if (!isOrcaCliAvailableOnPath(next)) {
|
|
toast.warning(
|
|
translate(
|
|
'auto.components.settings.CliSkillRuntimeSetup.3728a94fb6',
|
|
'WSL shell command needs attention'
|
|
),
|
|
{
|
|
description:
|
|
next.detail ??
|
|
translate(
|
|
'auto.components.settings.CliSkillRuntimeSetup.fc0fcf72fd',
|
|
'Register the WSL shell command before skill setup.'
|
|
)
|
|
}
|
|
)
|
|
}
|
|
return next
|
|
}
|
|
return status
|
|
} catch (error) {
|
|
toast.error(
|
|
error instanceof Error
|
|
? error.message
|
|
: translate(
|
|
'auto.components.settings.CliSkillRuntimeSetup.0ed08febc5',
|
|
'Failed to register the WSL shell command.'
|
|
)
|
|
)
|
|
return null
|
|
}
|
|
}
|