mirror of
https://github.com/stablyai/orca.git
synced 2026-10-01 16:02:03 +00:00
fix(onboarding): build the agent step around skills, not CLI registration (#22720)
* fix(onboarding): build the agent step around skills, not CLI registration The checklist step "Enable Orca CLI" was marked done once the agent skills were installed, while Settings -> Browser still showed CLI registration as a pending step. Orca terminals already put the bundled CLI on PATH, so registration only matters for shells Orca did not launch, plus WSL, where `orca-ide` exists only once registered. - Rename the step to "Give agents Orca skills"; setup registers the CLI only for WSL (isOrcaCliRegistrationRequired), via onboarding-cli-registration.ts. - Settings -> Browser drops the CLI step outside WSL (2 steps instead of 3). - Skills panel: "All skills installed" + "Update skills" replaces the disabled button; status pills sit top-right; no "Installed" beside "Unavailable". - Full Disk Access moves to the "Start work in multiple repos" step. Fixes STA-8306 / #22524. * refactor(onboarding): simplify agent-skill step state after review - One done rule: isAgentCapabilitiesDone in feature-wall-setup-progress.ts, reused by the skills panel instead of a mirrored copy. - 'unavailable' is an install-status tone instead of a second boolean; pill/note rendering moves to AgentCapabilityStatusBadges.tsx. - "Update skills" skips Computer Use when it can't run (no warning toast); setup takes an explicit selection. - The WSL gate lives in registerOnboardingCliIfRequired; onboarding deps drop the now-unreachable host CLI branches. - BrowserUsePane: one cliRequired/cliReady pair, no host CLI status fetch, WSL-only enable path, single "Finish the steps below." string. - Full Disk Access placement goes through a SelectedStepFooter switch. - Prune orphaned locale keys (and their boot-bundle entries); fix a stale comment. * fix(skills): require CLI registration only for WSL setup * fix(skills): retain CLI install labels for WSL * docs(skills): clarify remaining WSL registration fallback * fix(skills): skip WSL registration when the host confirms managed CLI access * fix(wsl): prepare managed shell wrappers before onboarding probes * test: update daemon capability and terminal hook expectations * refactor(onboarding): remove CLI registration checks from skill setup * refactor(setup): remove redundant state and obsolete registration scaffolding * fix(settings): stop registering the CLI before installing the CLI skill The General > Orca CLI skill panel still registered `orca` on PATH before opening the install terminal, contradicting the rest of skill setup. Orca terminals already provide the CLI, so the shell command toggle now says it is only for terminals outside Orca. * style(onboarding): polish the setup checklist and first-run steps Make onboarding monochrome: completion is a neutral check, selection a neutral outline, and color only flags real problems. Tighten the checklist rail and header, single-line agent cards with a grid that scrolls only when it runs out of room, a labeled permission switch under the grid, calmer notification and skill cards, sentence-case copy, and a labeled "Hide checklist from sidebar" action. Workspace setup leads with "Add project" when no git project exists. * fix(emulator): drop the Enable Orca CLI step from agent control setup Agents that drive the emulator run in Orca terminals, which already provide the `orca` command. Agent control setup in the emulator card and Settings is now a single step: install the Orca CLI skill. * fix(onboarding): hide the Full Disk Access card once access is granted A granted card has no remaining action and only takes space on the add projects step. It also no longer flashes a "Checking" state before the first status arrives. * fix(onboarding): address review on permission warning, hide button, and translations - Name the permission switch "Yolo mode" (matching Settings > Agents) and state the risk: agents act without asking and some bypass their sandbox. - Hide the modal's "Hide checklist from sidebar" button below sm, where the header centers its title under it; the sidebar entry keeps its own control. - Translate every string this PR adds into es, fr, ja, ko, and zh.
This commit is contained in:
@@ -83,7 +83,7 @@ describe('cookie-import Google disclosure footer', () => {
|
||||
<BrowserUseCookieImportStep
|
||||
cookiesImported={false}
|
||||
isImportingDefault={false}
|
||||
step3Blocked={false}
|
||||
disabled={false}
|
||||
sourceLabel={null}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useState } from 'react'
|
||||
import { ChevronDown, ChevronUp } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useAppStore } from '@/store'
|
||||
import { Button } from '../ui/button'
|
||||
import { MobileEmulatorAgentSetupGuideSteps } from './MobileEmulatorAgentSetupGuideSteps'
|
||||
@@ -59,8 +58,8 @@ export function MobileEmulatorAgentSetupGuide({
|
||||
)}{' '}
|
||||
</span>
|
||||
{translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuide.72736b051f',
|
||||
'Set up Orca CLI + skill when you want agents to drive this simulator.'
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuide.installSkillPrompt',
|
||||
'Install the Orca CLI skill when you want agents to drive this simulator.'
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
@@ -120,18 +119,6 @@ export function MobileEmulatorAgentSetupGuide({
|
||||
|
||||
{expanded && !setup.setupComplete ? (
|
||||
<div className="scrollbar-sleek max-h-[min(36vh,16rem)] overflow-y-auto border-t border-border/60 px-3 pb-2">
|
||||
<div className="flex items-center justify-end py-1.5">
|
||||
<span
|
||||
className={cn(
|
||||
'rounded-full px-2 py-0.5 text-[10px] font-medium',
|
||||
setup.setupComplete
|
||||
? 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-400'
|
||||
: 'bg-muted text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{setup.completedCount}/2
|
||||
</span>
|
||||
</div>
|
||||
<MobileEmulatorAgentSetupGuideSteps setup={setup} worktreeId={worktreeId} />
|
||||
<div className="pb-1 pt-1">
|
||||
<button
|
||||
|
||||
@@ -1,21 +1,8 @@
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useAppStore } from '@/store'
|
||||
import { ORCA_CLI_SKILL_INSTALL_COMMAND } from '@/lib/agent-feature-install-commands'
|
||||
import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime'
|
||||
import {
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import { AgentSkillSetupPanel } from '../settings/AgentSkillSetupPanel'
|
||||
import { buildSkillCommandForRuntime } from '../settings/CliSkillRuntimeSetup'
|
||||
import { StepBadge } from '../settings/SetupStepBadge'
|
||||
import { Button } from '../ui/button'
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip'
|
||||
import {
|
||||
getMobileEmulatorCliStepBadgeState,
|
||||
shouldShowMobileEmulatorSkillPreInstallNotice
|
||||
} from './mobile-emulator-agent-setup-cli-state'
|
||||
import type { useMobileEmulatorAgentSetupState } from './use-mobile-emulator-agent-setup-state'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
@@ -34,162 +21,51 @@ export function MobileEmulatorAgentSetupGuideSteps({
|
||||
// commands; routing them to a WSL runtime would install where we never look.
|
||||
const skillInstallCommand = buildSkillCommandForRuntime(ORCA_CLI_SKILL_INSTALL_COMMAND)
|
||||
const terminalWorktreeId = `mobile-emulator-${worktreeId}-orca-cli-skill-terminal`
|
||||
const showSkillPreInstallNotice = shouldShowMobileEmulatorSkillPreInstallNotice({
|
||||
cliEnabled: setup.cliEnabled,
|
||||
cliSkillInstalled: setup.cliSkillInstalled
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="divide-y divide-border/40">
|
||||
<div className="flex items-center gap-3 py-2.5">
|
||||
<StepBadge
|
||||
index={1}
|
||||
state={getMobileEmulatorCliStepBadgeState({
|
||||
cliBusy: setup.cliBusy,
|
||||
cliEnabled: setup.cliEnabled,
|
||||
cliPathNeedsAttention: setup.cliPathNeedsAttention
|
||||
})}
|
||||
/>
|
||||
<div className="min-w-0 flex-1 space-y-0.5">
|
||||
<p className="text-sm font-medium">
|
||||
{translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.9b49d892e3',
|
||||
'Enable Orca CLI'
|
||||
)}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.3d8dc52c93',
|
||||
'Registers the orca command for emulator control in agent shells.'
|
||||
)}
|
||||
</p>
|
||||
{setup.cliInstallStatus?.commandPath && setup.cliEnabled ? (
|
||||
<p className="text-[11px] text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.settings.MobileEmulatorAgentControlRow.aaf62a3dd2',
|
||||
'Installed at'
|
||||
)}{' '}
|
||||
<code className="rounded bg-muted px-1 py-0.5">
|
||||
{setup.cliInstallStatus.commandPath}
|
||||
</code>
|
||||
</p>
|
||||
) : null}
|
||||
{setup.cliPathNeedsAttention && setup.cliInstallStatus?.detail ? (
|
||||
<p className="text-[11px] text-amber-600 dark:text-amber-400">
|
||||
{setup.cliInstallStatus.detail}
|
||||
</p>
|
||||
) : null}
|
||||
{setup.cliBusy ? (
|
||||
<p className="text-[11px] leading-snug text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.3d34423e88',
|
||||
'Registering the Orca CLI'
|
||||
)}{' '}
|
||||
{setup.cliInstallStatus?.commandPath ? (
|
||||
<code className="rounded bg-muted px-1 py-0.5">
|
||||
{setup.cliInstallStatus.commandPath}
|
||||
</code>
|
||||
) : null}{' '}
|
||||
{translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.3be27641c9',
|
||||
'so emulator commands can run from agent shells.'
|
||||
)}
|
||||
</p>
|
||||
) : null}
|
||||
{!setup.cliEnabled && !setup.cliPathNeedsAttention && setup.cliInstallStatus?.detail ? (
|
||||
<p className="text-[11px] text-muted-foreground">{setup.cliInstallStatus.detail}</p>
|
||||
) : null}
|
||||
</div>
|
||||
<TooltipProvider delayDuration={250}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant={setup.cliEnabled ? 'outline' : 'default'}
|
||||
disabled={
|
||||
setup.cliLoading || setup.cliBusy || !setup.cliSupported || setup.cliEnabled
|
||||
}
|
||||
onClick={() => {
|
||||
recordFeatureInteraction('mobile-emulator-agent-setup')
|
||||
void setup.handleEnableCli()
|
||||
}}
|
||||
>
|
||||
{setup.cliLoading || setup.cliBusy ? (
|
||||
<Loader2 className="size-3.5 animate-spin" />
|
||||
) : null}
|
||||
{setup.cliActionLabel}
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
{!setup.cliSupported && !setup.cliLoading && setup.cliInstallStatus?.detail ? (
|
||||
<TooltipContent side="left" sideOffset={6}>
|
||||
{setup.cliInstallStatus.detail}
|
||||
</TooltipContent>
|
||||
) : null}
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
|
||||
<div className={cn('flex items-start gap-3 py-2.5', setup.step2Blocked && 'opacity-60')}>
|
||||
<div className="mt-0.5 shrink-0">
|
||||
<StepBadge index={2} state={setup.cliSkillInstalled ? 'done' : 'pending'} />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm font-medium">
|
||||
{translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.21f5687c07',
|
||||
'Orca CLI skill'
|
||||
)}
|
||||
</p>
|
||||
<AgentSkillSetupPanel
|
||||
variant="inline"
|
||||
hideHeader
|
||||
className="min-w-0"
|
||||
title={translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.21f5687c07',
|
||||
'Orca CLI skill'
|
||||
)}
|
||||
description={translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.64fb057667',
|
||||
'Teaches agents the orca emulator commands for this worktree.'
|
||||
)}
|
||||
command={skillInstallCommand}
|
||||
terminalTitle={translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.5c59ea96ca',
|
||||
'Mobile emulator Orca CLI skill setup'
|
||||
)}
|
||||
terminalAriaLabel={translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.bff5341ac3',
|
||||
'Mobile emulator Orca CLI skill install terminal'
|
||||
)}
|
||||
terminalWorktreeId={terminalWorktreeId}
|
||||
terminalShellOverride={activeSkillRuntime.terminalShellOverride}
|
||||
installed={setup.cliSkillInstalled}
|
||||
loading={setup.cliSkillLoading || setup.setupRechecking}
|
||||
error={setup.cliSkillError}
|
||||
installDisabled={setup.step2Blocked}
|
||||
showInstallWhenInstalled={!setup.cliSkillInstalled}
|
||||
terminalHeightPx={112}
|
||||
preInstallNotice={
|
||||
showSkillPreInstallNotice ? AGENT_SKILL_CLI_PREREQUISITE_NOTICE : undefined
|
||||
}
|
||||
openingHint={translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.3941719a56',
|
||||
'Checking Orca CLI before opening skill setup.'
|
||||
)}
|
||||
onBeforeOpenTerminal={async () => {
|
||||
recordFeatureInteraction('mobile-emulator-agent-setup')
|
||||
await ensureOrcaCliAvailableForAgentSkillTerminal()
|
||||
}}
|
||||
onRecheck={() => {
|
||||
recordFeatureInteraction('mobile-emulator-agent-setup')
|
||||
void setup.recheckSetup()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-2.5">
|
||||
<p className="text-sm font-medium">
|
||||
{translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.21f5687c07',
|
||||
'Orca CLI skill'
|
||||
)}
|
||||
</p>
|
||||
<AgentSkillSetupPanel
|
||||
variant="inline"
|
||||
hideHeader
|
||||
className="min-w-0"
|
||||
title={translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.21f5687c07',
|
||||
'Orca CLI skill'
|
||||
)}
|
||||
description={translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.64fb057667',
|
||||
'Teaches agents the orca emulator commands for this worktree.'
|
||||
)}
|
||||
command={skillInstallCommand}
|
||||
terminalTitle={translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.5c59ea96ca',
|
||||
'Mobile emulator Orca CLI skill setup'
|
||||
)}
|
||||
terminalAriaLabel={translate(
|
||||
'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.bff5341ac3',
|
||||
'Mobile emulator Orca CLI skill install terminal'
|
||||
)}
|
||||
terminalWorktreeId={terminalWorktreeId}
|
||||
terminalShellOverride={activeSkillRuntime.terminalShellOverride}
|
||||
installed={setup.cliSkillInstalled}
|
||||
loading={setup.cliSkillLoading || setup.setupRechecking}
|
||||
error={setup.cliSkillError}
|
||||
showInstallWhenInstalled={!setup.cliSkillInstalled}
|
||||
terminalHeightPx={112}
|
||||
onBeforeOpenTerminal={() => {
|
||||
recordFeatureInteraction('mobile-emulator-agent-setup')
|
||||
}}
|
||||
onRecheck={() => {
|
||||
recordFeatureInteraction('mobile-emulator-agent-setup')
|
||||
void setup.recheckSetup()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
-87
@@ -1,87 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { CliInstallStatus } from '../../../../shared/cli-install-types'
|
||||
import {
|
||||
getMobileEmulatorCliPathNeedsAttention,
|
||||
getMobileEmulatorCliStepBadgeState,
|
||||
shouldShowMobileEmulatorSkillPreInstallNotice
|
||||
} from './mobile-emulator-agent-setup-cli-state'
|
||||
|
||||
function cliStatus(overrides: Partial<CliInstallStatus> = {}): CliInstallStatus {
|
||||
return {
|
||||
platform: 'darwin',
|
||||
commandName: 'orca',
|
||||
commandPath: '/usr/local/bin/orca',
|
||||
pathDirectory: '/usr/local/bin',
|
||||
pathConfigured: true,
|
||||
launcherPath: '/Applications/Orca.app/Contents/MacOS/orca',
|
||||
installMethod: 'symlink',
|
||||
supported: true,
|
||||
state: 'installed',
|
||||
currentTarget: null,
|
||||
unsupportedReason: null,
|
||||
detail: null,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
describe('getMobileEmulatorCliPathNeedsAttention', () => {
|
||||
it('flags installed CLIs that are not visible on PATH yet', () => {
|
||||
expect(getMobileEmulatorCliPathNeedsAttention(cliStatus({ pathConfigured: false }))).toBe(true)
|
||||
expect(getMobileEmulatorCliPathNeedsAttention(cliStatus())).toBe(false)
|
||||
expect(getMobileEmulatorCliPathNeedsAttention(cliStatus({ state: 'not_installed' }))).toBe(
|
||||
false
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getMobileEmulatorCliStepBadgeState', () => {
|
||||
it('marks enabled CLIs as done', () => {
|
||||
expect(
|
||||
getMobileEmulatorCliStepBadgeState({
|
||||
cliBusy: false,
|
||||
cliEnabled: true,
|
||||
cliPathNeedsAttention: false
|
||||
})
|
||||
).toBe('done')
|
||||
})
|
||||
|
||||
it('marks PATH-fix and registration flows as in progress', () => {
|
||||
expect(
|
||||
getMobileEmulatorCliStepBadgeState({
|
||||
cliBusy: true,
|
||||
cliEnabled: false,
|
||||
cliPathNeedsAttention: false
|
||||
})
|
||||
).toBe('in-progress')
|
||||
expect(
|
||||
getMobileEmulatorCliStepBadgeState({
|
||||
cliBusy: false,
|
||||
cliEnabled: false,
|
||||
cliPathNeedsAttention: true
|
||||
})
|
||||
).toBe('in-progress')
|
||||
})
|
||||
})
|
||||
|
||||
describe('shouldShowMobileEmulatorSkillPreInstallNotice', () => {
|
||||
it('hides the prereq notice once either step is already complete', () => {
|
||||
expect(
|
||||
shouldShowMobileEmulatorSkillPreInstallNotice({
|
||||
cliEnabled: true,
|
||||
cliSkillInstalled: false
|
||||
})
|
||||
).toBe(false)
|
||||
expect(
|
||||
shouldShowMobileEmulatorSkillPreInstallNotice({
|
||||
cliEnabled: false,
|
||||
cliSkillInstalled: true
|
||||
})
|
||||
).toBe(false)
|
||||
expect(
|
||||
shouldShowMobileEmulatorSkillPreInstallNotice({
|
||||
cliEnabled: false,
|
||||
cliSkillInstalled: false
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,29 +0,0 @@
|
||||
import type { CliInstallStatus } from '../../../../shared/cli-install-types'
|
||||
import type { StepState } from '../settings/SetupStepBadge'
|
||||
|
||||
export function getMobileEmulatorCliPathNeedsAttention(status: CliInstallStatus | null): boolean {
|
||||
return status?.state === 'installed' && status.pathConfigured === false
|
||||
}
|
||||
|
||||
export function getMobileEmulatorCliStepBadgeState(input: {
|
||||
cliBusy: boolean
|
||||
cliEnabled: boolean
|
||||
cliPathNeedsAttention: boolean
|
||||
}): StepState {
|
||||
if (input.cliEnabled) {
|
||||
return 'done'
|
||||
}
|
||||
if (input.cliBusy || input.cliPathNeedsAttention) {
|
||||
return 'in-progress'
|
||||
}
|
||||
return 'pending'
|
||||
}
|
||||
|
||||
export function shouldShowMobileEmulatorSkillPreInstallNotice(input: {
|
||||
cliEnabled: boolean
|
||||
cliSkillInstalled: boolean
|
||||
}): boolean {
|
||||
// Why: an installed skill should not reopen with "Install" just because CLI
|
||||
// probes are stale; only gate first-time setup on CLI availability.
|
||||
return !input.cliSkillInstalled && !input.cliEnabled
|
||||
}
|
||||
+9
-151
@@ -1,67 +1,24 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import type { CliInstallStatus } from '../../../../shared/cli-install-types'
|
||||
import { ORCA_CLI_SKILL_NAME } from '@/lib/agent-feature-install-commands'
|
||||
import {
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal,
|
||||
isOrcaCliAvailableOnPath
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import {
|
||||
GLOBAL_AGENT_SKILL_SOURCE_KINDS,
|
||||
useInstalledAgentSkill
|
||||
} from '@/hooks/useInstalledAgentSkills'
|
||||
import { useMountedRef } from '@/hooks/useMountedRef'
|
||||
import { getMobileEmulatorCliPathNeedsAttention } from './mobile-emulator-agent-setup-cli-state'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
function getCliActionLabel(status: CliInstallStatus | null, busy: boolean): string {
|
||||
if (busy) {
|
||||
return translate(
|
||||
'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.fdcca1ec75',
|
||||
'Registering...'
|
||||
)
|
||||
}
|
||||
if (isOrcaCliAvailableOnPath(status)) {
|
||||
return translate(
|
||||
'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.69fb2c2289',
|
||||
'Enabled'
|
||||
)
|
||||
}
|
||||
if (status?.state === 'installed') {
|
||||
return translate(
|
||||
'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.c6705092ba',
|
||||
'Fix PATH'
|
||||
)
|
||||
}
|
||||
return translate(
|
||||
'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.7c1b6bdb1e',
|
||||
'Enable'
|
||||
)
|
||||
}
|
||||
|
||||
// Why: Orca terminals already provide the `orca` command, so agent control only needs the skill.
|
||||
export function useMobileEmulatorAgentSetupState(enabled = true): {
|
||||
cliActionLabel: string
|
||||
cliBusy: boolean
|
||||
cliEnabled: boolean
|
||||
cliInstallStatus: CliInstallStatus | null
|
||||
cliPathNeedsAttention: boolean
|
||||
cliLoading: boolean
|
||||
cliSkillError: string | null
|
||||
cliSkillInstalled: boolean
|
||||
cliSkillLoading: boolean
|
||||
cliSupported: boolean
|
||||
completedCount: number
|
||||
handleEnableCli: () => Promise<void>
|
||||
recheckSetup: () => Promise<void>
|
||||
refreshCliSkill: () => Promise<boolean>
|
||||
setupComplete: boolean
|
||||
setupRechecking: boolean
|
||||
statusReady: boolean
|
||||
step2Blocked: boolean
|
||||
} {
|
||||
const [cliInstallStatus, setCliInstallStatus] = useState<CliInstallStatus | null>(null)
|
||||
const [cliLoading, setCliLoading] = useState(true)
|
||||
const [cliBusy, setCliBusy] = useState(false)
|
||||
const [setupRechecking, setSetupRechecking] = useState(false)
|
||||
const mountedRef = useMountedRef()
|
||||
const {
|
||||
@@ -74,60 +31,17 @@ export function useMobileEmulatorAgentSetupState(enabled = true): {
|
||||
sourceKinds: GLOBAL_AGENT_SKILL_SOURCE_KINDS
|
||||
})
|
||||
|
||||
const refreshCliStatus = useCallback(async (): Promise<void> => {
|
||||
setCliLoading(true)
|
||||
try {
|
||||
const status = await window.api.cli.getInstallStatus()
|
||||
if (mountedRef.current) {
|
||||
setCliInstallStatus(status)
|
||||
}
|
||||
} catch (error) {
|
||||
if (mountedRef.current) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: translate(
|
||||
'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.51074ccb05',
|
||||
'Failed to load CLI status.'
|
||||
)
|
||||
)
|
||||
setCliInstallStatus(null)
|
||||
}
|
||||
} finally {
|
||||
if (mountedRef.current) {
|
||||
setCliLoading(false)
|
||||
}
|
||||
}
|
||||
}, [mountedRef])
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
void refreshCliStatus()
|
||||
}, [enabled, refreshCliStatus])
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
// Why: users often register the CLI from Settings first; refresh on focus so
|
||||
// the emulator guide reflects the latest install/PATH state.
|
||||
// Why: users often install the skill from Settings or a terminal; refresh on focus.
|
||||
const handleFocus = (): void => {
|
||||
void refreshCliStatus()
|
||||
void refreshCliSkill()
|
||||
}
|
||||
window.addEventListener('focus', handleFocus)
|
||||
return () => window.removeEventListener('focus', handleFocus)
|
||||
}, [enabled, refreshCliSkill, refreshCliStatus])
|
||||
|
||||
const cliEnabled = isOrcaCliAvailableOnPath(cliInstallStatus)
|
||||
const cliPathNeedsAttention = getMobileEmulatorCliPathNeedsAttention(cliInstallStatus)
|
||||
const cliSupported = cliInstallStatus?.supported ?? false
|
||||
const completedCount = [cliEnabled, cliSkillInstalled].filter(Boolean).length
|
||||
const step2Blocked = !cliEnabled && !cliSkillInstalled
|
||||
const setupComplete = cliEnabled && cliSkillInstalled
|
||||
const statusReady = !cliLoading && !cliSkillLoading
|
||||
}, [enabled, refreshCliSkill])
|
||||
|
||||
const recheckSetup = useCallback(async (): Promise<void> => {
|
||||
if (setupRechecking) {
|
||||
@@ -135,18 +49,11 @@ export function useMobileEmulatorAgentSetupState(enabled = true): {
|
||||
}
|
||||
setSetupRechecking(true)
|
||||
try {
|
||||
const [cliStatus, skillInstalled] = await Promise.all([
|
||||
window.api.cli.getInstallStatus(),
|
||||
refreshCliSkill()
|
||||
])
|
||||
if (mountedRef.current) {
|
||||
setCliInstallStatus(cliStatus)
|
||||
}
|
||||
const cliReady = isOrcaCliAvailableOnPath(cliStatus)
|
||||
const skillInstalled = await refreshCliSkill()
|
||||
if (!mountedRef.current) {
|
||||
return
|
||||
}
|
||||
if (cliReady && skillInstalled) {
|
||||
if (skillInstalled) {
|
||||
toast.success(
|
||||
translate(
|
||||
'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.35dea1ae12',
|
||||
@@ -155,28 +62,10 @@ export function useMobileEmulatorAgentSetupState(enabled = true): {
|
||||
)
|
||||
return
|
||||
}
|
||||
if (skillInstalled) {
|
||||
toast.message(
|
||||
translate(
|
||||
'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.9dff3a6338',
|
||||
'Skill is installed. Enable the Orca CLI to finish setup.'
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
if (cliReady) {
|
||||
toast.message(
|
||||
translate(
|
||||
'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.15986a1080',
|
||||
'Orca CLI is ready. Install the skill to finish setup.'
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
toast.message(
|
||||
translate(
|
||||
'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.4c26913def',
|
||||
'Still not set up. Complete both steps to enable agent control.'
|
||||
'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.skillNotInstalled',
|
||||
'The Orca CLI skill is not installed yet.'
|
||||
)
|
||||
)
|
||||
} catch (error) {
|
||||
@@ -197,45 +86,14 @@ export function useMobileEmulatorAgentSetupState(enabled = true): {
|
||||
}
|
||||
}, [mountedRef, refreshCliSkill, setupRechecking])
|
||||
|
||||
const handleEnableCli = useCallback(async (): Promise<void> => {
|
||||
setCliBusy(true)
|
||||
try {
|
||||
const next = await ensureOrcaCliAvailableForAgentSkillTerminal({
|
||||
onStatusChange: setCliInstallStatus
|
||||
})
|
||||
if (mountedRef.current && isOrcaCliAvailableOnPath(next)) {
|
||||
toast.success(
|
||||
translate(
|
||||
'auto.components.emulator.pane.use.mobile.emulator.agent.setup.state.2b519eed94',
|
||||
'Registered the Orca CLI in PATH.'
|
||||
)
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
if (mountedRef.current) {
|
||||
setCliBusy(false)
|
||||
}
|
||||
}
|
||||
}, [mountedRef])
|
||||
|
||||
return {
|
||||
cliActionLabel: getCliActionLabel(cliInstallStatus, cliBusy),
|
||||
cliBusy,
|
||||
cliEnabled,
|
||||
cliInstallStatus,
|
||||
cliPathNeedsAttention,
|
||||
cliLoading,
|
||||
cliSkillError,
|
||||
cliSkillInstalled,
|
||||
cliSkillLoading,
|
||||
cliSupported,
|
||||
completedCount,
|
||||
handleEnableCli,
|
||||
recheckSetup,
|
||||
refreshCliSkill,
|
||||
setupComplete,
|
||||
setupComplete: cliSkillInstalled,
|
||||
setupRechecking,
|
||||
statusReady,
|
||||
step2Blocked
|
||||
statusReady: !cliSkillLoading
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getDefaultAgentCapabilitySetupSelection } from './agent-capability-setup-status'
|
||||
import {
|
||||
getDefaultAgentCapabilitySetupSelection,
|
||||
isAgentCapabilityReadinessComplete
|
||||
} from './agent-capability-setup-status'
|
||||
|
||||
const READY_INPUT = {
|
||||
browserUseSkillInstalled: true,
|
||||
@@ -67,3 +70,31 @@ describe('getDefaultAgentCapabilitySetupSelection', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('isAgentCapabilityReadinessComplete', () => {
|
||||
it('is complete when every skill is ready, or Computer Use cannot run here', () => {
|
||||
expect(isAgentCapabilityReadinessComplete(READY_INPUT)).toBe(true)
|
||||
expect(
|
||||
isAgentCapabilityReadinessComplete({
|
||||
...READY_INPUT,
|
||||
computerUseReady: false,
|
||||
computerUseUnavailable: true
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('is incomplete while Computer Use still needs macOS access or a skill is missing', () => {
|
||||
expect(isAgentCapabilityReadinessComplete({ ...READY_INPUT, computerUseReady: false })).toBe(
|
||||
false
|
||||
)
|
||||
expect(
|
||||
isAgentCapabilityReadinessComplete({ ...READY_INPUT, orchestrationSkillInstalled: false })
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('does not claim completion while install probes are still running', () => {
|
||||
expect(
|
||||
isAgentCapabilityReadinessComplete({ ...READY_INPUT, browserUseSkillLoading: true })
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Button } from '@/components/ui/button'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime'
|
||||
import { useAppStore } from '@/store'
|
||||
import { AgentCapabilityStatusNote, AgentCapabilityStatusPill } from './AgentCapabilityStatusBadges'
|
||||
import { FeatureSetupInlineTerminal } from '../onboarding/FeatureSetupInlineTerminal'
|
||||
import type { OnboardingFeatureSetupRuntimeContext } from '../onboarding/onboarding-feature-setup-runtime'
|
||||
import {
|
||||
@@ -15,13 +16,12 @@ import {
|
||||
type OnboardingFeatureSetupSelection
|
||||
} from '../onboarding/onboarding-feature-setup'
|
||||
import {
|
||||
getAgentCapabilityStatusClassName,
|
||||
getDefaultAgentCapabilitySetupSelection,
|
||||
isAgentCapabilityReadinessChecking,
|
||||
isAgentCapabilityReadinessComplete,
|
||||
useAgentCapabilitySetupStatus,
|
||||
type AgentCapabilityInstallStatus
|
||||
} from './agent-capability-setup-status'
|
||||
import { FullDiskAccessSetupPrompt } from './FullDiskAccessSetupPrompt'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
export function AgentCapabilitiesSetupAction(props: {
|
||||
@@ -140,6 +140,7 @@ export function AgentCapabilitiesSetupAction(props: {
|
||||
featureSetupRuntime={featureSetupRuntime}
|
||||
setupBusyLabel={setupBusyLabel}
|
||||
onStartFeatureSetup={() => void handleStartFeatureSetup()}
|
||||
allReady={isAgentCapabilityReadinessComplete(readiness)}
|
||||
installStatus={capabilitySetupStatus.installStatus}
|
||||
/>
|
||||
</div>
|
||||
@@ -212,10 +213,13 @@ function AgentCapabilitySetupControls(props: {
|
||||
featureSetupRuntime: OnboardingFeatureSetupRuntimeContext | null
|
||||
setupBusyLabel: string | null
|
||||
onStartFeatureSetup: () => void
|
||||
allReady: boolean
|
||||
installStatus: Record<OnboardingFeatureSetupId, AgentCapabilityInstallStatus>
|
||||
}): React.JSX.Element {
|
||||
const hasSelectedFeatures = hasSelectedOnboardingFeatureSetup(props.featureSetup)
|
||||
const showSetupAction = !props.featureSetupCommand
|
||||
// Why: a disabled install button is noise once everything is set up.
|
||||
const showAllReady = props.allReady && !hasSelectedFeatures && !props.setupBusyLabel
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -224,8 +228,15 @@ function AgentCapabilitySetupControls(props: {
|
||||
onChange={props.onFeatureSetupChange}
|
||||
installStatus={props.installStatus}
|
||||
/>
|
||||
<FullDiskAccessSetupPrompt />
|
||||
{showSetupAction ? (
|
||||
{showSetupAction && showAllReady ? (
|
||||
<p className="mt-6 flex items-center gap-1.5 text-sm text-muted-foreground">
|
||||
<Check className="size-4" />
|
||||
{translate(
|
||||
'auto.components.feature.wall.AgentCapabilitiesSetupAction.allInstalled',
|
||||
'All skills installed'
|
||||
)}
|
||||
</p>
|
||||
) : showSetupAction ? (
|
||||
<div className="mt-6 flex items-center">
|
||||
<Button
|
||||
type="button"
|
||||
@@ -241,8 +252,8 @@ function AgentCapabilitySetupControls(props: {
|
||||
)}
|
||||
{props.setupBusyLabel ??
|
||||
translate(
|
||||
'auto.components.feature.wall.AgentCapabilitiesSetupAction.c89534cbe9',
|
||||
'Install CLI & Skills'
|
||||
'auto.components.feature.wall.AgentCapabilitiesSetupAction.installSkills',
|
||||
'Install skills'
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -277,35 +288,32 @@ function AgentCapabilitySetupChecklist(props: {
|
||||
aria-checked={selected}
|
||||
aria-label={`${selected ? 'Disable' : 'Enable'} ${row.title}`}
|
||||
className={cn(
|
||||
'flex min-h-24 flex-col rounded-lg border px-4 py-3 text-left transition-colors',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
selected
|
||||
? 'border-ring bg-accent text-foreground ring-2 ring-ring/25'
|
||||
: 'border-border bg-muted/20 text-muted-foreground hover:bg-muted/40'
|
||||
'flex min-h-24 flex-col rounded-lg border px-4 py-3.5 text-left transition-colors',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
||||
selected ? 'border-foreground/60 bg-accent' : 'border-border hover:bg-accent/60'
|
||||
)}
|
||||
onClick={() => props.onChange({ ...props.value, [row.id]: !selected })}
|
||||
>
|
||||
<span className="flex items-start justify-between gap-3">
|
||||
<span
|
||||
className={cn(
|
||||
'flex size-8 items-center justify-center rounded-lg border',
|
||||
selected
|
||||
? 'border-border bg-background text-foreground'
|
||||
: 'border-border bg-muted/40'
|
||||
)}
|
||||
>
|
||||
<span className="grid size-7 place-items-center rounded-md bg-muted text-foreground">
|
||||
{row.icon}
|
||||
</span>
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn(
|
||||
'flex size-5 items-center justify-center rounded-full border transition-colors',
|
||||
selected
|
||||
? 'border-primary bg-primary text-primary-foreground'
|
||||
: 'border-border bg-background'
|
||||
)}
|
||||
>
|
||||
{selected ? <Check className="size-3" strokeWidth={3} /> : null}
|
||||
<span className="flex items-center gap-2">
|
||||
<AgentCapabilityStatusPill status={installStatus} />
|
||||
{/* Why: an empty circle on an installed card reads as "not done"; show it only when it means something. */}
|
||||
{selected || !installStatus.installed ? (
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn(
|
||||
'flex size-5 items-center justify-center rounded-full border transition-colors',
|
||||
selected
|
||||
? 'border-foreground bg-foreground text-background'
|
||||
: 'border-border'
|
||||
)}
|
||||
>
|
||||
{selected ? <Check className="size-3" strokeWidth={3} /> : null}
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
</span>
|
||||
<span className="mt-3 text-sm font-medium text-foreground">{row.title}</span>
|
||||
@@ -320,41 +328,3 @@ function AgentCapabilitySetupChecklist(props: {
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function AgentCapabilityStatusNote(props: {
|
||||
status: AgentCapabilityInstallStatus
|
||||
}): React.JSX.Element {
|
||||
if (props.status.installed) {
|
||||
return (
|
||||
<span className="mt-2 flex flex-wrap items-center gap-1.5">
|
||||
<span className="rounded-full border border-green-500/45 bg-green-500/10 px-2 py-0.5 text-[11px] font-semibold leading-none text-green-700 dark:text-green-300">
|
||||
{translate(
|
||||
'auto.components.feature.wall.AgentCapabilitiesSetupAction.b8dc9dd8a2',
|
||||
'Installed'
|
||||
)}
|
||||
</span>
|
||||
{props.status.tone !== 'ready' ? (
|
||||
<span
|
||||
className={cn(
|
||||
'text-xs font-medium',
|
||||
getAgentCapabilityStatusClassName(props.status.tone)
|
||||
)}
|
||||
>
|
||||
{props.status.label}
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
'mt-1 text-xs font-medium',
|
||||
getAgentCapabilityStatusClassName(props.status.tone)
|
||||
)}
|
||||
>
|
||||
{props.status.label}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Check } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import {
|
||||
getAgentCapabilityStatusClassName,
|
||||
type AgentCapabilityInstallStatus
|
||||
} from './agent-capability-setup-status'
|
||||
|
||||
// Why: pills sit top-right so they line up across cards regardless of description length.
|
||||
export function AgentCapabilityStatusPill(props: {
|
||||
status: AgentCapabilityInstallStatus
|
||||
}): React.JSX.Element | null {
|
||||
if (props.status.tone === 'unavailable') {
|
||||
return (
|
||||
<span className="text-[11px] font-medium text-muted-foreground">{props.status.label}</span>
|
||||
)
|
||||
}
|
||||
if (!props.status.installed) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<span className="flex items-center gap-1 text-[11px] font-medium text-muted-foreground">
|
||||
<Check className="size-3" />
|
||||
{translate(
|
||||
'auto.components.feature.wall.AgentCapabilitiesSetupAction.b8dc9dd8a2',
|
||||
'Installed'
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
/** Secondary status text (checking, errors, pending actions); installed/unavailable live in the pill. */
|
||||
export function AgentCapabilityStatusNote(props: {
|
||||
status: AgentCapabilityInstallStatus
|
||||
}): React.JSX.Element | null {
|
||||
if (props.status.tone === 'ready' || props.status.tone === 'unavailable') {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
'mt-2 text-xs font-medium',
|
||||
getAgentCapabilityStatusClassName(props.status.tone)
|
||||
)}
|
||||
>
|
||||
{props.status.label}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -4,19 +4,11 @@ import {
|
||||
ORCA_CLI_SKILL_NAME,
|
||||
ORCA_CLI_SKILL_UPDATE_COMMAND
|
||||
} from '@/lib/agent-feature-install-commands'
|
||||
import {
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import { BROWSER_USE_ENABLED_STORAGE_KEY } from '@/lib/browser-use-setup-state'
|
||||
import type { InstalledAgentSkillState } from '@/hooks/useInstalledAgentSkills'
|
||||
import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime'
|
||||
import { AgentSkillSetupPanel } from '@/components/settings/AgentSkillSetupPanel'
|
||||
import {
|
||||
buildSkillCommandForRuntime,
|
||||
ensureWslCliAvailableForAgentSkillTerminal,
|
||||
getWslCliDistroRequest
|
||||
} from '@/components/settings/CliSkillRuntimeSetup'
|
||||
import { buildSkillCommandForRuntime } from '@/components/settings/CliSkillRuntimeSetup'
|
||||
import { useAppStore } from '@/store'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
@@ -34,11 +26,8 @@ export function BrowserUseSkillSetupCard(props: {
|
||||
? buildSkillCommandForRuntime(ORCA_CLI_SKILL_UPDATE_COMMAND, activeSkillRuntime.agentRuntime)
|
||||
: ORCA_CLI_SKILL_UPDATE_COMMAND
|
||||
|
||||
const handleBeforeOpenTerminal = async (): Promise<void> => {
|
||||
const handleBeforeOpenTerminal = (): void => {
|
||||
useAppStore.getState().recordFeatureInteraction('agent-browser-setup')
|
||||
await (activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime)
|
||||
: ensureOrcaCliAvailableForAgentSkillTerminal())
|
||||
localStorage.setItem(BROWSER_USE_ENABLED_STORAGE_KEY, '1')
|
||||
}
|
||||
|
||||
@@ -65,14 +54,6 @@ export function BrowserUseSkillSetupCard(props: {
|
||||
error={activeSkillRuntime.installDisabledReason ?? skill.error}
|
||||
installDisabled={Boolean(activeSkillRuntime.installDisabledReason)}
|
||||
terminalHeightPx={terminalHeightPx}
|
||||
preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE}
|
||||
getPrerequisiteStatus={() =>
|
||||
activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? window.api.cli.getWslInstallStatus(
|
||||
getWslCliDistroRequest(activeSkillRuntime.agentRuntime)
|
||||
)
|
||||
: window.api.cli.getInstallStatus()
|
||||
}
|
||||
onBeforeOpenTerminal={handleBeforeOpenTerminal}
|
||||
showRecheckWhenInstalled={false}
|
||||
onRecheck={skill.refresh}
|
||||
|
||||
@@ -89,8 +89,8 @@ const BROWSER_ONLY_FEATURE_SETUP: OnboardingFeatureSetupSelection = {
|
||||
linearTickets: false
|
||||
}
|
||||
|
||||
// The grab→agent flow relies on the Orca CLI and browser skill, so offer the same
|
||||
// install action the Enable Orca CLI step uses, scoped to just browser use.
|
||||
// The grab→agent flow relies on the browser skill, so offer the same install action
|
||||
// the agent skills step uses, scoped to just browser use.
|
||||
function BrowserSkillInstallButton(): React.JSX.Element {
|
||||
const recordFeatureInteraction = useAppStore((s) => s.recordFeatureInteraction)
|
||||
const [command, setCommand] = useState<string | null>(null)
|
||||
@@ -186,10 +186,7 @@ function BrowserSkillInstallButton(): React.JSX.Element {
|
||||
'auto.components.feature.wall.FeatureWallBrowserAction.5f97caf76b',
|
||||
'Installing…'
|
||||
)
|
||||
: translate(
|
||||
'auto.components.feature.wall.FeatureWallBrowserAction.c2df599513',
|
||||
'Install CLI & Skill'
|
||||
)}
|
||||
: translate('auto.components.skills.SkillInstallDialog.39acb9e8f4', 'Install skill')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -109,9 +109,9 @@ export function FeatureWallRail(props: {
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
'flex size-7 shrink-0 items-center justify-center rounded-sm border font-mono text-xs',
|
||||
'flex size-7 shrink-0 items-center justify-center rounded-sm border text-xs tabular-nums',
|
||||
isDone
|
||||
? 'border-emerald-500/40 bg-emerald-500/10 text-emerald-600 dark:text-emerald-300'
|
||||
? 'border-foreground bg-foreground text-background'
|
||||
: 'border-border bg-card text-muted-foreground'
|
||||
)}
|
||||
aria-label={
|
||||
@@ -157,9 +157,9 @@ export function FeatureWallRail(props: {
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
'flex size-5 shrink-0 items-center justify-center rounded-sm border font-mono text-[10px]',
|
||||
'flex size-5 shrink-0 items-center justify-center rounded-sm border text-[10px] tabular-nums',
|
||||
isStepDone
|
||||
? 'border-emerald-500/40 bg-emerald-500/10 text-emerald-600 dark:text-emerald-300'
|
||||
? 'border-foreground bg-foreground text-background'
|
||||
: 'border-border bg-card text-muted-foreground'
|
||||
)}
|
||||
aria-label={
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
import { getFeatureWallSetupStepsForSection } from '../../../../shared/feature-wall-setup-steps'
|
||||
import { cn } from '@/lib/utils'
|
||||
import type { FeatureWallSetupProgress } from './feature-wall-setup-progress'
|
||||
import { FullDiskAccessSetupPrompt } from './FullDiskAccessSetupPrompt'
|
||||
import { AgentCapabilitiesSetupAction } from './AgentCapabilitiesSetupAction'
|
||||
import {
|
||||
AddReposAction,
|
||||
@@ -47,10 +48,8 @@ function SetupStepRow(props: {
|
||||
active: boolean
|
||||
ordinal: number
|
||||
onSelect: () => void
|
||||
layout: FeatureWallSetupChecklistLayout
|
||||
}): React.JSX.Element {
|
||||
const { step, done, active, ordinal, onSelect, layout } = props
|
||||
const isEmbedded = layout === 'embedded'
|
||||
const { step, done, active, ordinal, onSelect } = props
|
||||
const localizedStepCopy = getLocalizedFeatureWallSetupChecklistCopy(step)
|
||||
return (
|
||||
<button
|
||||
@@ -58,38 +57,36 @@ function SetupStepRow(props: {
|
||||
onClick={onSelect}
|
||||
aria-current={active ? 'step' : undefined}
|
||||
className={cn(
|
||||
'relative flex w-full items-center gap-3 text-left transition-colors',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
isEmbedded
|
||||
? cn(
|
||||
'rounded-lg px-3 py-2',
|
||||
active ? 'bg-accent text-accent-foreground' : 'hover:bg-accent'
|
||||
)
|
||||
: cn(
|
||||
'rounded-md border px-3 py-2.5',
|
||||
active
|
||||
? 'border-border bg-accent text-accent-foreground'
|
||||
: 'border-border bg-background hover:bg-accent'
|
||||
)
|
||||
'flex w-full items-center gap-3 rounded-md px-2.5 py-2 text-left transition-colors',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
||||
active ? 'bg-accent' : 'hover:bg-accent/60'
|
||||
)}
|
||||
>
|
||||
{active ? (
|
||||
<span className="absolute bottom-2 left-0 top-2 w-0.5 rounded-full bg-foreground" />
|
||||
) : null}
|
||||
<span
|
||||
className={cn(
|
||||
'flex size-5 shrink-0 items-center justify-center rounded-full border',
|
||||
done
|
||||
? 'border-green-500/45 bg-green-500/10 text-green-600 dark:text-green-300'
|
||||
? 'border-foreground bg-foreground text-background'
|
||||
: 'border-border text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{done ? <Check className="size-3" /> : <span className="text-xs">{ordinal}</span>}
|
||||
{done ? (
|
||||
<Check className="size-3" strokeWidth={3} />
|
||||
) : (
|
||||
<span className="text-[11px] tabular-nums">{ordinal}</span>
|
||||
)}
|
||||
</span>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="block text-[15px] font-medium leading-snug text-foreground">
|
||||
{localizedStepCopy.name}
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
'min-w-0 flex-1 text-sm leading-snug',
|
||||
active
|
||||
? 'font-medium text-foreground'
|
||||
: done
|
||||
? 'text-muted-foreground'
|
||||
: 'text-foreground'
|
||||
)}
|
||||
>
|
||||
{localizedStepCopy.name}
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
@@ -102,20 +99,19 @@ function SetupSection(props: {
|
||||
activeStepId: FeatureWallSetupStepId | null
|
||||
progress: FeatureWallSetupProgress
|
||||
onSelectStep: (id: FeatureWallSetupStepId) => void
|
||||
layout: FeatureWallSetupChecklistLayout
|
||||
}): React.JSX.Element {
|
||||
const doneCount = props.steps.filter((step) => props.progress.stepDone[step.id]).length
|
||||
return (
|
||||
<section className="space-y-2">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<h4 className="text-xs font-semibold uppercase tracking-[0.08em] text-muted-foreground">
|
||||
<h4 className="text-[11px] font-semibold uppercase tracking-[0.05em] text-muted-foreground">
|
||||
{props.title}
|
||||
</h4>
|
||||
<span className="font-mono text-xs text-muted-foreground">
|
||||
<span className="text-[11px] tabular-nums text-muted-foreground">
|
||||
{doneCount}/{props.steps.length}
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<div className="space-y-0.5">
|
||||
{props.steps.map((step, index) => (
|
||||
<SetupStepRow
|
||||
key={step.id}
|
||||
@@ -123,7 +119,6 @@ function SetupSection(props: {
|
||||
done={props.progress.stepDone[step.id]}
|
||||
active={props.activeStepId === step.id}
|
||||
ordinal={props.startOrdinal + index}
|
||||
layout={props.layout}
|
||||
onSelect={() => props.onSelectStep(step.id)}
|
||||
/>
|
||||
))}
|
||||
@@ -170,6 +165,15 @@ function SelectedStepAction(props: FeatureWallSetupChecklistProps): React.JSX.El
|
||||
return null
|
||||
}
|
||||
|
||||
// Full-width content below the caption/visual grid.
|
||||
function SelectedStepFooter(props: { stepId: FeatureWallSetupStepId }): React.JSX.Element | null {
|
||||
// Why: Full Disk Access matters for projects in protected folders, so it sits with adding projects.
|
||||
if (props.stepId === 'add-two-repos') {
|
||||
return <FullDiskAccessSetupPrompt />
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function SelectedStepVisual(props: { stepId: FeatureWallSetupStepId }): React.JSX.Element | null {
|
||||
if (props.stepId === 'two-worktrees') {
|
||||
return <SetupWorkspacesVisual />
|
||||
@@ -299,7 +303,6 @@ export function FeatureWallSetupChecklist(
|
||||
activeStepId={activeStep?.id ?? null}
|
||||
progress={progress}
|
||||
onSelectStep={onSelectStep}
|
||||
layout={layout}
|
||||
/>
|
||||
<SetupSection
|
||||
title={translate(
|
||||
@@ -311,7 +314,6 @@ export function FeatureWallSetupChecklist(
|
||||
activeStepId={activeStep?.id ?? null}
|
||||
progress={progress}
|
||||
onSelectStep={onSelectStep}
|
||||
layout={layout}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -324,31 +326,21 @@ export function FeatureWallSetupChecklist(
|
||||
)}
|
||||
>
|
||||
{activeStep ? (
|
||||
<div className={cn('flex h-full flex-col', isEmbedded ? 'gap-7' : 'gap-5')}>
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="min-w-0">
|
||||
<div className="text-2xl font-semibold leading-tight text-foreground">
|
||||
{getLocalizedFeatureWallSetupChecklistCopy(activeStep).name}
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
className={cn(
|
||||
'shrink-0 rounded-full border px-2.5 py-1 text-xs font-medium',
|
||||
activeDone
|
||||
? 'border-green-500/45 bg-green-500/10 text-green-600 dark:text-green-300'
|
||||
: 'border-border bg-muted/30 text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{activeDone
|
||||
? translate(
|
||||
'auto.components.feature.wall.FeatureWallSetupChecklist.13294d3405',
|
||||
'Done'
|
||||
)
|
||||
: translate(
|
||||
'auto.components.feature.wall.FeatureWallSetupChecklist.0235b268b2',
|
||||
'Not done yet'
|
||||
)}
|
||||
</span>
|
||||
<div className={cn('flex h-full flex-col', isEmbedded ? 'gap-4' : 'gap-3')}>
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<h3 className="min-w-0 text-xl font-semibold tracking-tight text-foreground">
|
||||
{getLocalizedFeatureWallSetupChecklistCopy(activeStep).name}
|
||||
</h3>
|
||||
{/* Why: a "not done" pill on every open step is noise; only completion is news. */}
|
||||
{activeDone ? (
|
||||
<span className="flex shrink-0 items-center gap-1.5 text-xs font-medium text-muted-foreground">
|
||||
<Check className="size-3.5" />
|
||||
{translate(
|
||||
'auto.components.feature.wall.FeatureWallSetupChecklist.13294d3405',
|
||||
'Done'
|
||||
)}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
@@ -360,7 +352,7 @@ export function FeatureWallSetupChecklist(
|
||||
<div className="min-w-0">
|
||||
<p
|
||||
className={cn(
|
||||
'text-base leading-relaxed text-muted-foreground',
|
||||
'text-sm leading-relaxed text-muted-foreground',
|
||||
hasStepVisual && !isEmbedded ? 'pr-4 sm:pr-6' : null
|
||||
)}
|
||||
>
|
||||
@@ -368,12 +360,13 @@ export function FeatureWallSetupChecklist(
|
||||
</p>
|
||||
{/* Action lives under the caption, not after the grid, so it sits just
|
||||
below the copy instead of being pushed down by the taller visual. */}
|
||||
<div className={cn('min-w-0', isEmbedded ? 'mt-8' : 'mt-7')}>
|
||||
<div className={cn('min-w-0', isEmbedded ? 'mt-7' : 'mt-6')}>
|
||||
<SelectedStepAction {...props} />
|
||||
</div>
|
||||
</div>
|
||||
<SelectedStepVisual stepId={activeStep.id} />
|
||||
</div>
|
||||
<SelectedStepFooter stepId={activeStep.id} />
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
|
||||
@@ -161,12 +161,26 @@ export function SetupScriptAction(): React.JSX.Element {
|
||||
}
|
||||
}, [repo, setupScript, updateRepo])
|
||||
|
||||
// Why: disabled inputs with the prerequisite in fine print read as broken; lead with the fix.
|
||||
if (!canConfigure) {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.feature.wall.FeatureWallSetupWorkflowActions.486c2f4d8d',
|
||||
'Add a git project first, then configure the setup script for that repository.'
|
||||
)}
|
||||
</p>
|
||||
<AddReposAction />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="grid max-w-2xl gap-2 sm:grid-cols-[minmax(0,1fr)_auto]">
|
||||
<Input
|
||||
value={setupScript}
|
||||
disabled={!canConfigure}
|
||||
onChange={(event) => setSetupScript(event.target.value)}
|
||||
placeholder={translate(
|
||||
'auto.components.feature.wall.FeatureWallSetupWorkflowActions.5c5b65044e',
|
||||
@@ -182,7 +196,7 @@ export function SetupScriptAction(): React.JSX.Element {
|
||||
type="button"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
disabled={!canConfigure || setupScript.trim().length === 0}
|
||||
disabled={setupScript.trim().length === 0}
|
||||
onClick={() => void handleSaveSetupScript()}
|
||||
>
|
||||
<Save className="size-3.5" />
|
||||
@@ -197,7 +211,6 @@ export function SetupScriptAction(): React.JSX.Element {
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-fit gap-2 px-0 text-muted-foreground hover:bg-transparent hover:text-foreground"
|
||||
disabled={!canConfigure}
|
||||
onClick={openLocalCommandSettings}
|
||||
>
|
||||
<Settings className="size-3.5" />
|
||||
@@ -206,14 +219,6 @@ export function SetupScriptAction(): React.JSX.Element {
|
||||
'View in settings'
|
||||
)}
|
||||
</Button>
|
||||
{!canConfigure ? (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.feature.wall.FeatureWallSetupWorkflowActions.486c2f4d8d',
|
||||
'Add a git project first, then configure the setup script for that repository.'
|
||||
)}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -80,10 +80,11 @@ describe('FullDiskAccessSetupPrompt state helpers', () => {
|
||||
expect(isFullDiskAccessSetupVisible('unsupported')).toBe(false)
|
||||
})
|
||||
|
||||
it('shows the setup prompt for macOS statuses users can act on', () => {
|
||||
it('shows the setup prompt only for macOS statuses users can act on', () => {
|
||||
expect(isFullDiskAccessSetupVisible('unknown')).toBe(true)
|
||||
expect(isFullDiskAccessSetupVisible('denied')).toBe(true)
|
||||
expect(isFullDiskAccessSetupVisible('granted')).toBe(true)
|
||||
expect(isFullDiskAccessSetupVisible('granted')).toBe(false)
|
||||
expect(isFullDiskAccessSetupVisible('ready')).toBe(false)
|
||||
})
|
||||
|
||||
it('treats granted and entitled statuses as ready', () => {
|
||||
@@ -109,7 +110,7 @@ describe('FullDiskAccessSetupPrompt state helpers', () => {
|
||||
})
|
||||
|
||||
expect(getStatus).toHaveBeenCalledTimes(2)
|
||||
expect(container.textContent).toContain('Granted')
|
||||
expect(container.textContent).not.toContain('Full Disk Access')
|
||||
root.unmount()
|
||||
})
|
||||
|
||||
@@ -130,14 +131,14 @@ describe('FullDiskAccessSetupPrompt state helpers', () => {
|
||||
window.dispatchEvent(new Event('focus'))
|
||||
await Promise.resolve()
|
||||
})
|
||||
expect(container.textContent).toContain('Granted')
|
||||
expect(container.textContent).not.toContain('Full Disk Access')
|
||||
|
||||
await act(async () => {
|
||||
resolveFirst([{ id: 'full-disk-access', status: 'unknown' }])
|
||||
await Promise.resolve()
|
||||
})
|
||||
|
||||
expect(container.textContent).toContain('Granted')
|
||||
expect(container.textContent).not.toContain('Full Disk Access')
|
||||
root.unmount()
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { Check, ExternalLink, HardDrive, Loader2 } from 'lucide-react'
|
||||
import { ExternalLink, HardDrive, Loader2 } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import type {
|
||||
DeveloperPermissionId,
|
||||
@@ -18,7 +18,6 @@ type FullDiskAccessStatusState = {
|
||||
}
|
||||
|
||||
type FullDiskAccessButtonState = {
|
||||
ready: boolean
|
||||
requesting: boolean
|
||||
}
|
||||
|
||||
@@ -27,7 +26,8 @@ const FULL_DISK_ACCESS_PERMISSION_ID: DeveloperPermissionId = 'full-disk-access'
|
||||
export function isFullDiskAccessSetupVisible(
|
||||
status: DeveloperPermissionStatus | undefined
|
||||
): boolean {
|
||||
return status !== undefined && status !== 'unsupported'
|
||||
// Why: once granted there is nothing left to do, so the card would only take space.
|
||||
return status !== undefined && status !== 'unsupported' && !isFullDiskAccessReady(status)
|
||||
}
|
||||
|
||||
export function isFullDiskAccessReady(status: DeveloperPermissionStatus | undefined): boolean {
|
||||
@@ -47,9 +47,6 @@ function getFullDiskAccessStatusLabel(args: FullDiskAccessStatusState): string {
|
||||
'Checking'
|
||||
)
|
||||
}
|
||||
if (isFullDiskAccessReady(args.status)) {
|
||||
return translate('auto.components.feature.wall.FullDiskAccessSetupPrompt.48d87edcd2', 'Granted')
|
||||
}
|
||||
return translate(
|
||||
'auto.components.feature.wall.FullDiskAccessSetupPrompt.6db9a69f4e',
|
||||
'Recommended'
|
||||
@@ -63,9 +60,6 @@ function getFullDiskAccessButtonLabel(args: FullDiskAccessButtonState): string {
|
||||
'Opening...'
|
||||
)
|
||||
}
|
||||
if (args.ready) {
|
||||
return translate('auto.components.feature.wall.FullDiskAccessSetupPrompt.48d87edcd2', 'Granted')
|
||||
}
|
||||
return translate(
|
||||
'auto.components.feature.wall.FullDiskAccessSetupPrompt.6e3d62b816',
|
||||
'Open Full Disk Access'
|
||||
@@ -76,9 +70,6 @@ function FullDiskAccessButtonIcon(props: FullDiskAccessButtonState): React.JSX.E
|
||||
if (props.requesting) {
|
||||
return <Loader2 className="size-3.5 animate-spin" />
|
||||
}
|
||||
if (props.ready) {
|
||||
return <Check className="size-3.5" />
|
||||
}
|
||||
return <ExternalLink className="size-3.5" />
|
||||
}
|
||||
|
||||
@@ -151,8 +142,7 @@ export function FullDiskAccessSetupPrompt(): React.JSX.Element | null {
|
||||
const { checking, refresh, status } = useFullDiskAccessStatus()
|
||||
const mountedRef = useMountedRef()
|
||||
const [requesting, setRequesting] = useState(false)
|
||||
const ready = isFullDiskAccessReady(status)
|
||||
const visible = checking || isFullDiskAccessSetupVisible(status)
|
||||
const visible = isFullDiskAccessSetupVisible(status)
|
||||
|
||||
const handleOpenFullDiskAccess = useCallback(async (): Promise<void> => {
|
||||
setRequesting(true)
|
||||
@@ -195,7 +185,7 @@ export function FullDiskAccessSetupPrompt(): React.JSX.Element | null {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-5 flex items-center justify-between gap-4 rounded-lg border border-border/60 bg-muted/20 px-4 py-3">
|
||||
<div className="flex items-center justify-between gap-4 rounded-lg border border-border/60 bg-muted/20 px-4 py-3">
|
||||
<div className="flex min-w-0 items-start gap-3">
|
||||
<div className="mt-0.5 text-muted-foreground">
|
||||
<HardDrive className="size-4" />
|
||||
@@ -208,9 +198,7 @@ export function FullDiskAccessSetupPrompt(): React.JSX.Element | null {
|
||||
'Full Disk Access'
|
||||
)}
|
||||
</span>
|
||||
<Badge variant={ready ? 'secondary' : 'outline'} className="uppercase tracking-wider">
|
||||
{getFullDiskAccessStatusLabel({ checking, status })}
|
||||
</Badge>
|
||||
<Badge variant="outline">{getFullDiskAccessStatusLabel({ checking, status })}</Badge>
|
||||
</div>
|
||||
<p className="text-xs leading-snug text-muted-foreground">
|
||||
{translate(
|
||||
@@ -225,11 +213,11 @@ export function FullDiskAccessSetupPrompt(): React.JSX.Element | null {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="shrink-0 gap-1.5"
|
||||
disabled={ready || requesting || checking}
|
||||
disabled={requesting || checking}
|
||||
onClick={() => void handleOpenFullDiskAccess()}
|
||||
>
|
||||
<FullDiskAccessButtonIcon ready={ready} requesting={requesting} />
|
||||
{getFullDiskAccessButtonLabel({ ready, requesting })}
|
||||
<FullDiskAccessButtonIcon requesting={requesting} />
|
||||
{getFullDiskAccessButtonLabel({ requesting })}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -14,8 +14,14 @@ import {
|
||||
} from '@/hooks/useInstalledAgentSkills'
|
||||
import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import { isAgentCapabilitiesDone } from './feature-wall-setup-progress'
|
||||
|
||||
export type AgentCapabilityInstallStatusTone = 'ready' | 'pending' | 'checking' | 'error'
|
||||
export type AgentCapabilityInstallStatusTone =
|
||||
| 'ready'
|
||||
| 'pending'
|
||||
| 'checking'
|
||||
| 'error'
|
||||
| 'unavailable'
|
||||
|
||||
export type AgentCapabilityInstallStatus = {
|
||||
label: string
|
||||
@@ -119,14 +125,18 @@ export function isAgentCapabilityReadinessChecking(readiness: AgentCapabilityRea
|
||||
)
|
||||
}
|
||||
|
||||
export function isAgentCapabilityReadinessComplete(readiness: AgentCapabilityReadiness): boolean {
|
||||
return !isAgentCapabilityReadinessChecking(readiness) && isAgentCapabilitiesDone(readiness)
|
||||
}
|
||||
|
||||
export function getAgentCapabilityStatusClassName(tone: AgentCapabilityInstallStatusTone): string {
|
||||
switch (tone) {
|
||||
case 'ready':
|
||||
return 'text-green-600 dark:text-green-300'
|
||||
case 'error':
|
||||
return 'text-destructive'
|
||||
case 'ready':
|
||||
case 'checking':
|
||||
case 'pending':
|
||||
case 'unavailable':
|
||||
return 'text-muted-foreground'
|
||||
}
|
||||
}
|
||||
@@ -166,8 +176,8 @@ function getSkillInstallStatus(skill: {
|
||||
}
|
||||
return {
|
||||
label: translate(
|
||||
'auto.components.feature.wall.agent.capability.setup.status.aae94eeb52',
|
||||
'Click Install CLI & Skills'
|
||||
'auto.components.feature.wall.agent.capability.setup.status.notInstalled',
|
||||
'Not installed'
|
||||
),
|
||||
tone: 'pending'
|
||||
}
|
||||
@@ -211,22 +221,22 @@ function getComputerUseInstallStatus(
|
||||
label:
|
||||
permissions.unavailableReason === 'web_client'
|
||||
? translate(
|
||||
'auto.components.feature.wall.agent.capability.setup.status.4c8e1f92a7',
|
||||
'open Orca Desktop on this Mac'
|
||||
'auto.components.feature.wall.agent.capability.setup.status.requiresOrcaDesktop',
|
||||
'Requires Orca Desktop'
|
||||
)
|
||||
: translate(
|
||||
'auto.components.feature.wall.agent.capability.setup.status.6d2b0a84e1',
|
||||
'Unavailable in this build'
|
||||
),
|
||||
tone: 'pending',
|
||||
tone: 'unavailable',
|
||||
installed: true
|
||||
}
|
||||
}
|
||||
if (!permissions.ready) {
|
||||
return {
|
||||
label: translate(
|
||||
'auto.components.feature.wall.agent.capability.setup.status.21d4f79c93',
|
||||
'click Install CLI & Skills to open macOS access settings'
|
||||
'auto.components.feature.wall.agent.capability.setup.status.needsMacAccess',
|
||||
'Needs macOS access'
|
||||
),
|
||||
tone: 'pending',
|
||||
installed: true
|
||||
|
||||
@@ -51,9 +51,9 @@ export function IntegrationStep(props: {
|
||||
className={cn(
|
||||
'flex size-7 shrink-0 items-center justify-center rounded-full border text-[13px] font-semibold leading-none',
|
||||
done
|
||||
? 'border-status-success-border bg-status-success-background text-status-success'
|
||||
? 'border-foreground bg-foreground text-background'
|
||||
: active
|
||||
? 'border-foreground bg-foreground text-background'
|
||||
? 'border-foreground text-foreground'
|
||||
: 'border-border text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
|
||||
+6
-6
@@ -11,8 +11,8 @@ const getLocalizedFeatureWallSetupChecklistCopyById = createLocalizedCatalog(
|
||||
(): Record<FeatureWallSetupStepId, LocalizedFeatureWallSetupChecklistCopy> => ({
|
||||
'two-worktrees': {
|
||||
name: translate(
|
||||
'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.ec0a363633',
|
||||
'Multi-task'
|
||||
'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.workOnTwoTasks',
|
||||
'Work on two tasks at once'
|
||||
),
|
||||
description: translate(
|
||||
'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.62bac8f43c',
|
||||
@@ -51,12 +51,12 @@ const getLocalizedFeatureWallSetupChecklistCopyById = createLocalizedCatalog(
|
||||
},
|
||||
'agent-capabilities': {
|
||||
name: translate(
|
||||
'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.fee5557b02',
|
||||
'Enable Orca CLI'
|
||||
'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.agentSkillsName',
|
||||
'Give agents Orca skills'
|
||||
),
|
||||
description: translate(
|
||||
'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.7bcb4097fa',
|
||||
'Register the Orca shell command and install agent skills for browser, computer, and orchestration workflows.'
|
||||
'auto.components.feature.wall.feature.wall.setup.checklist.localized.copy.agentSkillsDescription',
|
||||
"Install the skills agents use to drive Orca's browser, control your computer, and coordinate multi-step work."
|
||||
)
|
||||
},
|
||||
'task-sources': {
|
||||
|
||||
@@ -42,14 +42,31 @@ function countAvailableNonMainWorktrees(worktreesByRepo: Record<string, Worktree
|
||||
)
|
||||
}
|
||||
|
||||
export type AgentCapabilitiesState = {
|
||||
browserUseSkillInstalled: boolean
|
||||
computerUseSkillInstalled: boolean
|
||||
computerUseReady: boolean
|
||||
computerUseUnavailable: boolean
|
||||
orchestrationSkillInstalled: boolean
|
||||
}
|
||||
|
||||
export function isAgentCapabilitiesDone(state: AgentCapabilitiesState): boolean {
|
||||
return (
|
||||
state.browserUseSkillInstalled &&
|
||||
state.computerUseSkillInstalled &&
|
||||
(state.computerUseReady || state.computerUseUnavailable) &&
|
||||
state.orchestrationSkillInstalled
|
||||
)
|
||||
}
|
||||
|
||||
export function getFeatureWallSetupProgress(
|
||||
input: FeatureWallSetupProgressInput
|
||||
): FeatureWallSetupProgress {
|
||||
const agentCapabilitiesDone =
|
||||
input.browserUseSkillInstalled &&
|
||||
input.computerUseSkillInstalled &&
|
||||
(input.computerUsePermissionsReady || input.computerUseUnavailable === true) &&
|
||||
input.orchestrationSkillInstalled
|
||||
const agentCapabilitiesDone = isAgentCapabilitiesDone({
|
||||
...input,
|
||||
computerUseReady: input.computerUsePermissionsReady,
|
||||
computerUseUnavailable: input.computerUseUnavailable === true
|
||||
})
|
||||
const stepDone: Record<FeatureWallSetupStepId, boolean> = {
|
||||
'default-agent':
|
||||
Boolean(input.settings?.defaultTuiAgent) && input.settings?.defaultTuiAgent !== 'blank',
|
||||
|
||||
+6
-22
@@ -10,10 +10,6 @@ import { AgentSkillSetupPanel } from '@/components/settings/AgentSkillSetupPanel
|
||||
import { IntegrationStatusPill } from '@/components/integration-status-pill'
|
||||
import { SkillFreshnessStatusPill } from '@/components/skills/SkillFreshnessStatusPill'
|
||||
import { ORCHESTRATION_SKILL_NAME } from '@/lib/agent-feature-install-commands'
|
||||
import {
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import {
|
||||
ORCHESTRATION_SKILL_INSTALL_COMMAND,
|
||||
ORCHESTRATION_SKILL_UPDATE_COMMAND
|
||||
@@ -25,11 +21,7 @@ import {
|
||||
import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime'
|
||||
import { refreshSkillFreshness } from '@/hooks/useSkillFreshness'
|
||||
import { useAppStore } from '@/store'
|
||||
import {
|
||||
buildSkillCommandForRuntime,
|
||||
ensureWslCliAvailableForAgentSkillTerminal,
|
||||
getWslCliDistroRequest
|
||||
} from '@/components/settings/CliSkillRuntimeSetup'
|
||||
import { buildSkillCommandForRuntime } from '@/components/settings/CliSkillRuntimeSetup'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
type FloatingTerminalOrchestrationDialogProps = {
|
||||
@@ -153,20 +145,12 @@ export function FloatingTerminalOrchestrationDialog({
|
||||
installDisabled={Boolean(activeSkillRuntime.installDisabledReason)}
|
||||
variant="inline"
|
||||
hideHeader
|
||||
installLabel="Install CLI & skill"
|
||||
preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE}
|
||||
getPrerequisiteStatus={() =>
|
||||
activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? window.api.cli.getWslInstallStatus(
|
||||
getWslCliDistroRequest(activeSkillRuntime.agentRuntime)
|
||||
)
|
||||
: window.api.cli.getInstallStatus()
|
||||
}
|
||||
onBeforeOpenTerminal={async () => {
|
||||
installLabel={translate(
|
||||
'auto.components.skills.SkillInstallDialog.39acb9e8f4',
|
||||
'Install skill'
|
||||
)}
|
||||
onBeforeOpenTerminal={() => {
|
||||
useAppStore.getState().recordFeatureInteraction('agent-orchestration-setup')
|
||||
await (activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime)
|
||||
: ensureOrcaCliAvailableForAgentSkillTerminal())
|
||||
}}
|
||||
onRecheck={recheckOrchestrationSkill}
|
||||
/>
|
||||
|
||||
@@ -117,7 +117,7 @@ export function MacNotificationPermissionCard({
|
||||
}): React.JSX.Element | null {
|
||||
if (state === 'checking') {
|
||||
return (
|
||||
<section className="rounded-xl border border-border bg-muted/20 px-5 py-4 text-[13px] text-muted-foreground">
|
||||
<section className="rounded-lg border border-border px-4 py-3.5 text-[13px] text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.onboarding.NotificationStep.56b836215c',
|
||||
'Checking notification permission…'
|
||||
@@ -128,10 +128,10 @@ export function MacNotificationPermissionCard({
|
||||
|
||||
if (state === 'enabled') {
|
||||
return (
|
||||
<section className="flex items-center gap-2.5 rounded-xl border border-emerald-500/30 bg-emerald-500/[0.07] px-5 py-4">
|
||||
<Check className="size-4 shrink-0 text-emerald-600 dark:text-emerald-400" strokeWidth={3} />
|
||||
<div className="min-w-0">
|
||||
<div className="text-sm font-semibold text-foreground">
|
||||
<section className="flex items-start gap-3 rounded-lg border border-border px-4 py-3.5">
|
||||
<Check className="mt-0.5 size-4 shrink-0 text-foreground" />
|
||||
<div className="min-w-0 space-y-0.5">
|
||||
<div className="text-sm font-medium text-foreground">
|
||||
{translate(
|
||||
'auto.components.onboarding.NotificationStep.fd84d3e9b8',
|
||||
'Notifications are enabled'
|
||||
@@ -150,22 +150,24 @@ export function MacNotificationPermissionCard({
|
||||
|
||||
if (state === 'awaiting-permission') {
|
||||
return (
|
||||
<section className="rounded-xl border border-border bg-card px-5 py-4">
|
||||
<div className="flex flex-wrap items-start justify-between gap-4">
|
||||
<div className="min-w-0 space-y-1">
|
||||
<div className="flex items-center gap-2 text-sm font-semibold text-foreground">
|
||||
<BellRing className="size-4" />
|
||||
{translate(
|
||||
'auto.components.onboarding.NotificationStep.95d99b52fa',
|
||||
'Allow notifications for Orca'
|
||||
)}
|
||||
<section className="rounded-lg border border-border px-4 py-3.5">
|
||||
<div className="flex flex-wrap items-center justify-between gap-4">
|
||||
<div className="flex min-w-0 items-start gap-3">
|
||||
<BellRing className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
|
||||
<div className="min-w-0 space-y-0.5">
|
||||
<div className="text-sm font-medium text-foreground">
|
||||
{translate(
|
||||
'auto.components.onboarding.NotificationStep.95d99b52fa',
|
||||
'Allow notifications for Orca'
|
||||
)}
|
||||
</div>
|
||||
<p className="max-w-[58ch] text-[13px] leading-relaxed text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.onboarding.mac.notification.permission.card.f696515944',
|
||||
'Click Allow in the macOS dialog.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<p className="max-w-[58ch] text-[13px] leading-relaxed text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.onboarding.mac.notification.permission.card.f696515944',
|
||||
'Click Allow in the macOS dialog.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -187,25 +189,24 @@ export function MacNotificationPermissionCard({
|
||||
|
||||
if (state === 'blocked') {
|
||||
return (
|
||||
<section
|
||||
role="alert"
|
||||
className="rounded-xl border border-amber-500/40 bg-amber-500/10 px-5 py-4"
|
||||
>
|
||||
<div className="flex flex-wrap items-start justify-between gap-4">
|
||||
<div className="min-w-0 space-y-1">
|
||||
<div className="flex items-center gap-2 text-sm font-semibold text-amber-700 dark:text-amber-300">
|
||||
<TriangleAlert className="size-4" />
|
||||
{translate(
|
||||
'auto.components.onboarding.NotificationStep.90b5d2e363',
|
||||
'macOS is not delivering Orca notifications'
|
||||
)}
|
||||
<section role="alert" className="rounded-lg border border-status-warning-border px-4 py-3.5">
|
||||
<div className="flex flex-wrap items-center justify-between gap-4">
|
||||
<div className="flex min-w-0 items-start gap-3">
|
||||
<TriangleAlert className="mt-0.5 size-4 shrink-0 text-status-warning" />
|
||||
<div className="min-w-0 space-y-0.5">
|
||||
<div className="text-sm font-medium text-foreground">
|
||||
{translate(
|
||||
'auto.components.onboarding.NotificationStep.90b5d2e363',
|
||||
'macOS is not delivering Orca notifications'
|
||||
)}
|
||||
</div>
|
||||
<p className="max-w-[58ch] text-[13px] leading-relaxed text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.onboarding.mac.notification.permission.card.721d2bedb6',
|
||||
'Turn on Allow notifications for Orca in System Settings.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<p className="max-w-[58ch] text-[13px] leading-relaxed text-amber-700/80 dark:text-amber-200/80">
|
||||
{translate(
|
||||
'auto.components.onboarding.mac.notification.permission.card.721d2bedb6',
|
||||
'Turn on Allow notifications for Orca in System Settings.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
|
||||
@@ -19,10 +19,10 @@ describe('AgentStep', () => {
|
||||
</TooltipProvider>
|
||||
)
|
||||
|
||||
expect(html).toContain(`Show ${AGENT_CATALOG.length - 1} more agents→`)
|
||||
expect(html).toContain(`Show ${AGENT_CATALOG.length - 1} more agents →`)
|
||||
expect(html).toContain('data-agent-grid-scroll')
|
||||
expect(html).toContain('data-slot="checkbox"')
|
||||
expect(html).toContain('Yolo / Dangerously skip permissions')
|
||||
expect(html).toContain('data-slot="switch"')
|
||||
expect(html).toContain('Yolo mode')
|
||||
expect(html).not.toContain('role="radiogroup"')
|
||||
})
|
||||
|
||||
@@ -41,6 +41,6 @@ describe('AgentStep', () => {
|
||||
)
|
||||
|
||||
expect(html).toContain('Hide agents')
|
||||
expect(html).not.toContain(`Show ${AGENT_CATALOG.length - 1} more agents→`)
|
||||
expect(html).not.toContain(`Show ${AGENT_CATALOG.length - 1} more agents →`)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { useLayoutEffect, useRef, useState } from 'react'
|
||||
import { Check, ExternalLink, Info } from 'lucide-react'
|
||||
import { useId, useState } from 'react'
|
||||
import { Check, ExternalLink } from 'lucide-react'
|
||||
import { getAgentCatalog, AgentIcon, type AgentCatalogEntry } from '@/lib/agent-catalog'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import type { TuiAgent } from '../../../../shared/tui-agent'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
const AGENT_GRID_MAX_ROWS = 4
|
||||
|
||||
type AgentStepProps = {
|
||||
selectedAgent: TuiAgent | null
|
||||
// `fromCollapsedSection` tells the controller whether the click happened
|
||||
@@ -22,43 +20,6 @@ type AgentStepProps = {
|
||||
onYoloPermissionsChange?: (enabled: boolean) => void
|
||||
}
|
||||
|
||||
function useAgentGridScrollMaxHeight(
|
||||
scrollRef: React.RefObject<HTMLDivElement | null>,
|
||||
remeasureKey: string
|
||||
): number | undefined {
|
||||
const [maxHeight, setMaxHeight] = useState<number | undefined>(undefined)
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const scroll = scrollRef.current
|
||||
if (!scroll) {
|
||||
return
|
||||
}
|
||||
|
||||
const measure = (): void => {
|
||||
const card = scroll.querySelector<HTMLElement>('[data-agent-card]')
|
||||
const grid = card?.closest<HTMLElement>('[data-agent-grid]')
|
||||
if (!card || !grid) {
|
||||
setMaxHeight(undefined)
|
||||
return
|
||||
}
|
||||
const gap = Number.parseFloat(getComputedStyle(grid).rowGap || '10')
|
||||
const cardHeight = card.getBoundingClientRect().height
|
||||
setMaxHeight(Math.ceil(AGENT_GRID_MAX_ROWS * cardHeight + (AGENT_GRID_MAX_ROWS - 1) * gap))
|
||||
}
|
||||
|
||||
measure()
|
||||
const observer = new ResizeObserver(measure)
|
||||
observer.observe(scroll)
|
||||
const card = scroll.querySelector<HTMLElement>('[data-agent-card]')
|
||||
if (card) {
|
||||
observer.observe(card)
|
||||
}
|
||||
return () => observer.disconnect()
|
||||
}, [remeasureKey, scrollRef])
|
||||
|
||||
return maxHeight
|
||||
}
|
||||
|
||||
export function AgentStep({
|
||||
selectedAgent,
|
||||
onSelect,
|
||||
@@ -98,21 +59,16 @@ export function AgentStep({
|
||||
? translate('auto.components.onboarding.AgentStep.hideAgents', 'Hide agents')
|
||||
: translate(
|
||||
'auto.components.onboarding.AgentStep.showMoreAgents',
|
||||
'Show {{value0}} more agents→',
|
||||
'Show {{value0}} more agents →',
|
||||
{
|
||||
value0: fallbackRest.length
|
||||
}
|
||||
)
|
||||
const agentGridScrollRef = useRef<HTMLDivElement>(null)
|
||||
const agentGridScrollMaxHeight = useAgentGridScrollMaxHeight(
|
||||
agentGridScrollRef,
|
||||
`${primary.length}:${fallbackRest.length}:${openState}:${hasDetected}`
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-5">
|
||||
{!hasDetected && !isDetecting && (
|
||||
<div className="shrink-0 rounded-lg border border-amber-400/30 bg-amber-400/10 px-4 py-3 text-xs text-amber-700 dark:text-amber-200/90">
|
||||
<div className="shrink-0 rounded-lg border border-border px-4 py-3 text-xs text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.onboarding.AgentStep.1eee1c7bd8',
|
||||
'No agents detected on your PATH. Pick one to install later, or continue with a blank terminal.'
|
||||
@@ -120,25 +76,27 @@ export function AgentStep({
|
||||
</div>
|
||||
)}
|
||||
{selectedEntry && (
|
||||
<div className="flex shrink-0 items-center justify-between gap-3 rounded-lg border border-amber-400/30 bg-amber-400/10 px-4 py-2.5 text-xs text-amber-700 dark:text-amber-200/90">
|
||||
<div className="flex shrink-0 items-center justify-between gap-3 rounded-lg border border-status-warning-border px-4 py-2.5 text-xs text-muted-foreground">
|
||||
<span>
|
||||
<span className="font-medium">{selectedEntry.label}</span>{' '}
|
||||
<span className="font-medium text-foreground">{selectedEntry.label}</span>{' '}
|
||||
{translate(
|
||||
'auto.components.onboarding.AgentStep.69af7e9c1c',
|
||||
"isn't on your PATH yet. Orca will set it as your default and you can install it any time."
|
||||
)}
|
||||
</span>
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
className="inline-flex shrink-0 items-center gap-1 rounded-md border border-amber-400/40 bg-amber-400/10 px-2 py-1 font-medium text-amber-800 hover:bg-amber-400/20 dark:text-amber-100"
|
||||
variant="outline"
|
||||
size="xs"
|
||||
className="shrink-0"
|
||||
onClick={() => void window.api.shell.openUrl(selectedEntry.homepageUrl)}
|
||||
>
|
||||
{translate('auto.components.onboarding.AgentStep.9c163bb0e0', 'Install instructions')}
|
||||
<ExternalLink className="size-3" />
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<section className="flex min-h-0 flex-1 flex-col gap-3 overflow-hidden">
|
||||
<section className="flex min-h-0 flex-col gap-3 overflow-hidden">
|
||||
<SectionHeader
|
||||
label={
|
||||
hasDetected
|
||||
@@ -149,16 +107,10 @@ export function AgentStep({
|
||||
: translate('auto.components.onboarding.AgentStep.e6a369bd04', 'Popular agents')
|
||||
}
|
||||
count={primary.length}
|
||||
showDetectedIndicator={hasDetected}
|
||||
/>
|
||||
<div
|
||||
ref={agentGridScrollRef}
|
||||
data-agent-grid-scroll
|
||||
className="scrollbar-sleek min-h-0 flex-1 overflow-y-auto pr-1"
|
||||
style={agentGridScrollMaxHeight ? { maxHeight: agentGridScrollMaxHeight } : undefined}
|
||||
>
|
||||
<div data-agent-grid-scroll className="scrollbar-sleek min-h-0 overflow-y-auto pr-1">
|
||||
<div className="space-y-3">
|
||||
<div data-agent-grid className="grid grid-cols-2 gap-2.5 md:grid-cols-3">
|
||||
<div className="grid grid-cols-2 gap-2.5 md:grid-cols-3">
|
||||
{primary.map((agent) => (
|
||||
<AgentButton
|
||||
key={agent.id}
|
||||
@@ -174,7 +126,7 @@ export function AgentStep({
|
||||
{fallbackRestLabel}
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className="collapsible-height-content">
|
||||
<div data-agent-grid className="grid grid-cols-2 gap-2.5 md:grid-cols-3">
|
||||
<div className="grid grid-cols-2 gap-2.5 md:grid-cols-3">
|
||||
{fallbackRest.map((agent) => (
|
||||
<AgentButton
|
||||
key={agent.id}
|
||||
@@ -205,67 +157,34 @@ function YoloPermissionsControl({
|
||||
yoloPermissions: boolean
|
||||
onYoloPermissionsChange?: (enabled: boolean) => void
|
||||
}): React.JSX.Element {
|
||||
const switchId = useId()
|
||||
return (
|
||||
<label className="mt-auto flex shrink-0 cursor-pointer items-center justify-between gap-4 rounded-lg border border-border bg-muted/25 px-4 py-3 transition-colors hover:bg-muted/40">
|
||||
<span className="flex min-w-0 items-center gap-3">
|
||||
<Checkbox
|
||||
checked={yoloPermissions}
|
||||
onCheckedChange={(checked) => onYoloPermissionsChange?.(checked === true)}
|
||||
className="border-border bg-card data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground"
|
||||
aria-label={translate(
|
||||
'auto.components.onboarding.AgentStep.yoloPermissionsLabel',
|
||||
'Yolo / Dangerously skip permissions'
|
||||
)}
|
||||
/>
|
||||
<span className="min-w-0 text-sm font-medium text-foreground">
|
||||
<div className="flex shrink-0 items-center justify-between gap-6 border-t border-border pt-4">
|
||||
<label htmlFor={switchId} className="min-w-0 cursor-pointer space-y-0.5">
|
||||
<span className="block text-sm font-medium text-foreground">
|
||||
{translate('auto.components.onboarding.AgentStep.yoloModeLabel', 'Yolo mode')}
|
||||
</span>
|
||||
<span className="block text-xs text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.onboarding.AgentStep.yoloPermissionsLabel',
|
||||
'Yolo / Dangerously skip permissions'
|
||||
'auto.components.onboarding.AgentStep.yoloPermissionsDescription',
|
||||
'Agents run commands and edit files without asking, and some also bypass their sandbox. Use only in projects you trust.'
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={translate(
|
||||
'auto.components.onboarding.AgentStep.yoloPermissionsInfo',
|
||||
'Agent permission info'
|
||||
)}
|
||||
onPointerDown={(event) => event.preventDefault()}
|
||||
className="grid size-6 shrink-0 place-items-center rounded-md text-muted-foreground outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50"
|
||||
>
|
||||
<Info className="size-3.5" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" sideOffset={6} style={{ zIndex: 120 }}>
|
||||
{translate(
|
||||
'auto.components.onboarding.AgentStep.yoloPermissionsTooltip',
|
||||
'Skip permission checks for agents for less interruptions'
|
||||
)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</label>
|
||||
<Switch
|
||||
id={switchId}
|
||||
checked={yoloPermissions}
|
||||
onCheckedChange={(checked) => onYoloPermissionsChange?.(checked)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SectionHeader({
|
||||
label,
|
||||
count,
|
||||
showDetectedIndicator = false
|
||||
}: {
|
||||
label: string
|
||||
count: number
|
||||
showDetectedIndicator?: boolean
|
||||
}) {
|
||||
function SectionHeader({ label, count }: { label: string; count: number }) {
|
||||
return (
|
||||
<div className="flex shrink-0 items-center gap-2 text-[11px] font-medium uppercase tracking-[0.14em] text-muted-foreground">
|
||||
{showDetectedIndicator && (
|
||||
<span className="size-1.5 shrink-0 rounded-full bg-emerald-500" aria-hidden="true" />
|
||||
)}
|
||||
<div className="flex shrink-0 items-center gap-1.5 text-xs font-medium text-muted-foreground">
|
||||
<span>{label}</span>
|
||||
<span className="text-muted-foreground/60">·</span>
|
||||
<span className="tabular-nums text-muted-foreground">{count}</span>
|
||||
<span className="tabular-nums text-muted-foreground/70">{count}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -282,32 +201,26 @@ function AgentButton({
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
data-agent-card
|
||||
aria-pressed={selected}
|
||||
title={agent.label}
|
||||
className={cn(
|
||||
'group relative overflow-hidden rounded-xl border p-3.5 text-left transition-all',
|
||||
selected
|
||||
? 'border-violet-500/60 bg-violet-500/10 ring-2 ring-violet-500/30'
|
||||
: 'border-border bg-muted/30 hover:bg-muted/60'
|
||||
'flex min-w-0 items-center gap-2.5 rounded-lg border px-3 py-2.5 text-left transition-colors',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
||||
selected ? 'border-foreground/60 bg-accent' : 'border-border hover:bg-accent/60'
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
<span className="grid size-7 shrink-0 place-items-center rounded-md bg-muted text-foreground">
|
||||
<AgentIcon agent={agent.id} size={16} />
|
||||
</span>
|
||||
<span className="min-w-0 flex-1 truncate text-sm font-medium text-foreground">
|
||||
{agent.label}
|
||||
</span>
|
||||
{selected ? (
|
||||
<div className="absolute right-2 top-2 grid size-5 place-items-center rounded-full bg-violet-500 text-white shadow-sm">
|
||||
<Check className="size-3" strokeWidth={3} />
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex min-w-0 items-start gap-2.5 pr-6">
|
||||
<span className="grid size-7 shrink-0 place-items-center rounded-md bg-muted text-foreground">
|
||||
<AgentIcon agent={agent.id} size={16} />
|
||||
<span className="grid size-4 shrink-0 place-items-center rounded-full bg-foreground text-background">
|
||||
<Check className="size-2.5" strokeWidth={3} />
|
||||
</span>
|
||||
<div className="min-w-0">
|
||||
<div className="truncate text-sm font-medium text-foreground">{agent.label}</div>
|
||||
<div className="mt-0.5 truncate font-mono text-[11px] text-muted-foreground">
|
||||
{agent.cmd}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ export function GhosttyDiscoveryRow({
|
||||
|
||||
if (discovery.status === 'imported') {
|
||||
return (
|
||||
<div className="flex items-center gap-2.5 rounded-lg border border-emerald-500/30 bg-emerald-500/[0.07] px-3.5 py-2.5 text-[12px] text-foreground">
|
||||
<Check className="size-3.5 text-emerald-600 dark:text-emerald-400" strokeWidth={3} />
|
||||
<div className="flex items-center gap-2.5 rounded-lg border border-border px-3.5 py-2.5 text-[12px] text-foreground">
|
||||
<Check className="size-3.5" />
|
||||
<span className="flex-1">
|
||||
<span className="font-medium">
|
||||
{translate('auto.components.onboarding.ThemeStep.78b6386140', 'Imported from Ghostty.')}
|
||||
@@ -52,7 +52,7 @@ export function GhosttyDiscoveryRow({
|
||||
|
||||
const { preview, fields } = discovery
|
||||
return (
|
||||
<div className="flex items-center gap-3 rounded-lg border border-violet-500/30 bg-violet-500/[0.06] px-3.5 py-2.5">
|
||||
<div className="flex items-center gap-3 rounded-lg border border-border px-3.5 py-2.5">
|
||||
<img src={ghosttyIcon} alt="" className="size-4 shrink-0" />
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="text-[12px] text-foreground">
|
||||
|
||||
@@ -27,9 +27,9 @@ describe('NotificationStep', () => {
|
||||
<NotificationStep settings={createSettings()} updateSettings={vi.fn()} />
|
||||
)
|
||||
|
||||
expect(html).toContain('Notification Sound')
|
||||
expect(html).toContain('Choose a sound')
|
||||
expect(html).toContain('role="combobox"')
|
||||
expect(html).toContain('Send Test Notification')
|
||||
expect(html).toContain('Send test notification')
|
||||
expect(html).not.toContain('aria-pressed')
|
||||
expect(html).not.toContain('Agent task complete')
|
||||
expect(html).not.toContain('Terminal bell')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { BellRing, FileAudio, Upload } from 'lucide-react'
|
||||
import { BellRing, Upload } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import type { GlobalSettings } from '../../../../shared/global-settings-types'
|
||||
import { Button } from '@/components/ui/button'
|
||||
@@ -27,16 +27,6 @@ type NotificationStepProps = {
|
||||
|
||||
const CHOOSE_CUSTOM_SOUND_VALUE = 'choose-custom-file'
|
||||
|
||||
type NotificationSoundSelectValue =
|
||||
| GlobalSettings['notifications']['customSoundId']
|
||||
| typeof CHOOSE_CUSTOM_SOUND_VALUE
|
||||
|
||||
function isNotificationSoundId(
|
||||
value: NotificationSoundSelectValue
|
||||
): value is GlobalSettings['notifications']['customSoundId'] {
|
||||
return value !== CHOOSE_CUSTOM_SOUND_VALUE
|
||||
}
|
||||
|
||||
export function NotificationStep({
|
||||
settings,
|
||||
updateSettings
|
||||
@@ -123,13 +113,19 @@ export function NotificationStep({
|
||||
}
|
||||
}
|
||||
|
||||
const handleSoundSelect = async (value: NotificationSoundSelectValue): Promise<void> => {
|
||||
if (!isNotificationSoundId(value)) {
|
||||
const handleSoundSelect = async (value: string): Promise<void> => {
|
||||
if (value === CHOOSE_CUSTOM_SOUND_VALUE) {
|
||||
await handleChooseCustomSound()
|
||||
return
|
||||
}
|
||||
await updateNotificationSettings({ customSoundId: value })
|
||||
await previewSound(value)
|
||||
const option = getNotificationSoundOptions(
|
||||
notificationSettingsRef.current?.customSoundPath
|
||||
).find((candidate) => candidate.id === value)
|
||||
if (!option) {
|
||||
return
|
||||
}
|
||||
await updateNotificationSettings({ customSoundId: option.id })
|
||||
await previewSound(option.id)
|
||||
}
|
||||
|
||||
const handleSendTestNotification = async (): Promise<void> => {
|
||||
@@ -176,7 +172,7 @@ export function NotificationStep({
|
||||
const soundOptions = getNotificationSoundOptions(customPath)
|
||||
|
||||
return (
|
||||
<div ref={setSelectPortalHost} className="space-y-5">
|
||||
<div ref={setSelectPortalHost} className="space-y-6">
|
||||
<MacNotificationPermissionCard state={macPermissionState} />
|
||||
|
||||
<section className="space-y-3">
|
||||
@@ -192,75 +188,64 @@ export function NotificationStep({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-foreground">
|
||||
<FileAudio className="size-4" />
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Select
|
||||
value={selectedSoundId}
|
||||
disabled={isPickingSound}
|
||||
onValueChange={(value) => void handleSoundSelect(value)}
|
||||
>
|
||||
<SelectTrigger className="w-56 max-w-full" size="sm">
|
||||
<SelectValue
|
||||
placeholder={translate(
|
||||
'auto.components.onboarding.NotificationStep.dc897423e1',
|
||||
'Choose notification sound'
|
||||
)}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent
|
||||
portalContainer={selectPortalRoot}
|
||||
align="start"
|
||||
className="w-[--radix-select-trigger-width]"
|
||||
>
|
||||
{soundOptions.map((option) => {
|
||||
const OptionIcon = option.icon
|
||||
return (
|
||||
<SelectItem key={option.id} value={option.id}>
|
||||
<OptionIcon className="size-4" />
|
||||
<span className="truncate">{option.title}</span>
|
||||
</SelectItem>
|
||||
)
|
||||
})}
|
||||
<SelectSeparator />
|
||||
<SelectItem value={CHOOSE_CUSTOM_SOUND_VALUE}>
|
||||
<Upload className="size-4" />
|
||||
<span>
|
||||
{customPath
|
||||
? translate(
|
||||
'auto.components.onboarding.NotificationStep.ac80d97e02',
|
||||
'Change custom file'
|
||||
)
|
||||
: translate(
|
||||
'auto.components.onboarding.NotificationStep.c0692baa52',
|
||||
'Choose custom file'
|
||||
)}
|
||||
</span>
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
onClick={() => void handleSendTestNotification()}
|
||||
>
|
||||
<BellRing className="size-3.5" />
|
||||
{translate(
|
||||
'auto.components.onboarding.NotificationStep.53aaffe49a',
|
||||
'Notification Sound'
|
||||
'auto.components.onboarding.NotificationStep.3bede04483',
|
||||
'Send test notification'
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Select
|
||||
value={selectedSoundId}
|
||||
disabled={isPickingSound}
|
||||
onValueChange={(value) =>
|
||||
void handleSoundSelect(value as NotificationSoundSelectValue)
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-[360px] max-w-full" size="sm">
|
||||
<SelectValue
|
||||
placeholder={translate(
|
||||
'auto.components.onboarding.NotificationStep.dc897423e1',
|
||||
'Choose notification sound'
|
||||
)}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent
|
||||
portalContainer={selectPortalRoot}
|
||||
align="start"
|
||||
className="w-[--radix-select-trigger-width]"
|
||||
>
|
||||
{soundOptions.map((option) => {
|
||||
const OptionIcon = option.icon
|
||||
return (
|
||||
<SelectItem key={option.id} value={option.id}>
|
||||
<OptionIcon className="size-4" />
|
||||
<span className="truncate">{option.title}</span>
|
||||
</SelectItem>
|
||||
)
|
||||
})}
|
||||
<SelectSeparator />
|
||||
<SelectItem value={CHOOSE_CUSTOM_SOUND_VALUE}>
|
||||
<Upload className="size-4" />
|
||||
<span>
|
||||
{customPath
|
||||
? translate(
|
||||
'auto.components.onboarding.NotificationStep.ac80d97e02',
|
||||
'Change Custom File'
|
||||
)
|
||||
: translate(
|
||||
'auto.components.onboarding.NotificationStep.c0692baa52',
|
||||
'Choose Custom File'
|
||||
)}
|
||||
</span>
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
onClick={() => void handleSendTestNotification()}
|
||||
>
|
||||
<BellRing className="size-3.5" />
|
||||
{translate(
|
||||
'auto.components.onboarding.NotificationStep.3bede04483',
|
||||
'Send Test Notification'
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -221,17 +221,15 @@ export function ThemeStep({ theme, onThemeChange, settings, updateSettings }: Th
|
||||
<button
|
||||
key={id}
|
||||
className={cn(
|
||||
'group overflow-hidden rounded-xl border p-3 text-left transition-all',
|
||||
selected
|
||||
? 'border-violet-500/60 bg-violet-500/10 ring-2 ring-violet-500/30'
|
||||
: 'border-border bg-muted/30 hover:bg-muted/60'
|
||||
'group overflow-hidden rounded-xl border p-3 text-left transition-colors',
|
||||
selected ? 'border-foreground/60 bg-accent' : 'border-border hover:bg-accent/60'
|
||||
)}
|
||||
onClick={() => applyOnboardingThemeSelection(id, onThemeChange, updateSettings)}
|
||||
>
|
||||
<div className="relative mb-3 h-24 overflow-hidden rounded-lg border border-border">
|
||||
<ChromePreview variant={id} />
|
||||
{selected && (
|
||||
<div className="absolute right-1.5 top-1.5 grid size-5 place-items-center rounded-full bg-violet-500 text-white shadow-sm">
|
||||
<div className="absolute right-1.5 top-1.5 grid size-5 place-items-center rounded-full bg-foreground text-background shadow-sm">
|
||||
<Check className="size-3" strokeWidth={3} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { CliInstallStatus } from '../../../../shared/cli-install-types'
|
||||
import type {
|
||||
ComputerUsePermissionSetupResult,
|
||||
ComputerUsePermissionStatusResult
|
||||
@@ -19,7 +18,6 @@ import {
|
||||
import {
|
||||
DEFAULT_ONBOARDING_FEATURE_SETUP_SELECTION,
|
||||
buildOnboardingFeatureSetupClipboardText,
|
||||
createOnboardingFeatureSetupDeps,
|
||||
onboardingFeatureSetupRunTelemetry,
|
||||
onboardingFeatureSetupTelemetryFeature,
|
||||
onboardingFeatureSetupTelemetrySelection,
|
||||
@@ -39,21 +37,6 @@ const ORCHESTRATION_ONLY_SKILL_INSTALL_COMMAND = buildAgentFeatureSkillInstallCo
|
||||
ORCHESTRATION_SKILL_NAME
|
||||
])
|
||||
|
||||
const INSTALLED_CLI_STATUS: CliInstallStatus = {
|
||||
platform: 'darwin',
|
||||
commandName: 'orca',
|
||||
commandPath: '/usr/local/bin/orca',
|
||||
pathDirectory: '/usr/local/bin',
|
||||
pathConfigured: true,
|
||||
launcherPath: '/Applications/Orca.app/Contents/MacOS/Orca',
|
||||
installMethod: 'symlink',
|
||||
supported: true,
|
||||
state: 'installed',
|
||||
currentTarget: '/Applications/Orca.app/Contents/MacOS/Orca',
|
||||
unsupportedReason: null,
|
||||
detail: null
|
||||
}
|
||||
|
||||
const GRANTED_COMPUTER_USE_STATUS: ComputerUsePermissionStatusResult = {
|
||||
platform: 'darwin',
|
||||
helperAppPath: '/Applications/Orca Computer Use.app',
|
||||
@@ -82,9 +65,6 @@ function createDeps(
|
||||
return {
|
||||
storage,
|
||||
clipboardWrites,
|
||||
getCliStatus: vi.fn(async () => INSTALLED_CLI_STATUS),
|
||||
showCliRegistrationPrompt: vi.fn(async () => undefined),
|
||||
installCli: vi.fn(async () => INSTALLED_CLI_STATUS),
|
||||
writeClipboardText: vi.fn(async (text: string) => {
|
||||
clipboardWrites.push(text)
|
||||
}),
|
||||
@@ -101,6 +81,11 @@ function createDeps(
|
||||
}
|
||||
}
|
||||
|
||||
const WSL_RUNTIME_CONTEXT = {
|
||||
agentRuntime: { runtime: 'wsl' as const, wslDistro: 'Ubuntu', label: 'WSL Ubuntu' },
|
||||
installDisabledReason: null
|
||||
}
|
||||
|
||||
describe('onboarding feature setup runner', () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals()
|
||||
@@ -156,29 +141,6 @@ describe('onboarding feature setup runner', () => {
|
||||
).toBeUndefined()
|
||||
})
|
||||
|
||||
it('uses the WSL CLI APIs for the selected distro', async () => {
|
||||
const getInstallStatus = vi.fn()
|
||||
const install = vi.fn()
|
||||
const getWslInstallStatus = vi.fn(async () => INSTALLED_CLI_STATUS)
|
||||
const installWsl = vi.fn(async () => INSTALLED_CLI_STATUS)
|
||||
vi.stubGlobal('window', {
|
||||
api: { cli: { getInstallStatus, install, getWslInstallStatus, installWsl } }
|
||||
})
|
||||
const deps = createOnboardingFeatureSetupDeps({
|
||||
runtime: 'wsl',
|
||||
wslDistro: 'Ubuntu',
|
||||
label: 'WSL Ubuntu'
|
||||
})
|
||||
|
||||
await deps.getCliStatus()
|
||||
await deps.installCli()
|
||||
|
||||
expect(getWslInstallStatus).toHaveBeenCalledWith({ distro: 'Ubuntu' })
|
||||
expect(installWsl).toHaveBeenCalledWith({ distro: 'Ubuntu' })
|
||||
expect(getInstallStatus).not.toHaveBeenCalled()
|
||||
expect(install).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps the runner on the host when the selected WSL runtime needs repair', async () => {
|
||||
const deps = createDeps()
|
||||
|
||||
@@ -213,7 +175,6 @@ describe('onboarding feature setup runner', () => {
|
||||
expect(
|
||||
onboardingFeatureSetupRunTelemetry(selection, {
|
||||
selectedIds: ['browserUse', 'orchestration', 'linearTickets'],
|
||||
cliTouched: true,
|
||||
skillCommandsCopied: false,
|
||||
skillInstallCommand: ORCHESTRATION_ONLY_SKILL_INSTALL_COMMAND,
|
||||
computerUsePermissionsOpened: false,
|
||||
@@ -225,7 +186,7 @@ describe('onboarding feature setup runner', () => {
|
||||
linear_tickets: true,
|
||||
orchestration: true,
|
||||
selected_count: 2,
|
||||
cli_touched: true,
|
||||
cli_touched: false,
|
||||
skill_commands_copied: false,
|
||||
skill_install_command_prepared: true,
|
||||
computer_use_permissions_opened: false,
|
||||
@@ -255,15 +216,11 @@ describe('onboarding feature setup runner', () => {
|
||||
|
||||
expect(result).toEqual({
|
||||
selectedIds: ['browserUse', 'computerUse', 'orchestration', 'linearTickets'],
|
||||
cliTouched: false,
|
||||
skillCommandsCopied: true,
|
||||
skillInstallCommand: ALL_SKILL_INSTALL_COMMAND,
|
||||
computerUsePermissionsOpened: true,
|
||||
warnings: []
|
||||
})
|
||||
expect(deps.getCliStatus).toHaveBeenCalledTimes(1)
|
||||
expect(deps.showCliRegistrationPrompt).not.toHaveBeenCalled()
|
||||
expect(deps.installCli).not.toHaveBeenCalled()
|
||||
expect(deps.getComputerUsePermissionStatus).toHaveBeenCalledTimes(1)
|
||||
expect(deps.openComputerUsePermissionSetup).toHaveBeenCalledTimes(1)
|
||||
expect(deps.storage.get(BROWSER_USE_ENABLED_STORAGE_KEY)).toBe('1')
|
||||
@@ -273,6 +230,16 @@ describe('onboarding feature setup runner', () => {
|
||||
expect(deps.clipboardWrites).toEqual([ALL_SKILL_INSTALL_COMMAND])
|
||||
})
|
||||
|
||||
it('installs WSL skills without checking or changing CLI registration', async () => {
|
||||
const deps = createDeps()
|
||||
const result = await runOnboardingFeatureSetup(
|
||||
{ browserUse: false, computerUse: false, orchestration: true, linearTickets: false },
|
||||
deps,
|
||||
WSL_RUNTIME_CONTEXT
|
||||
)
|
||||
expect(result.skillInstallCommand).toBe(ORCHESTRATION_ONLY_SKILL_INSTALL_COMMAND)
|
||||
})
|
||||
|
||||
it('keeps invasive Browser Use and Computer Use setup untouched when only Orchestration is selected', async () => {
|
||||
const deps = createDeps()
|
||||
const selection: OnboardingFeatureSetupSelection = {
|
||||
@@ -288,9 +255,6 @@ describe('onboarding feature setup runner', () => {
|
||||
expect(result.skillCommandsCopied).toBe(true)
|
||||
expect(result.skillInstallCommand).toBe(ORCHESTRATION_ONLY_SKILL_INSTALL_COMMAND)
|
||||
expect(result.computerUsePermissionsOpened).toBe(false)
|
||||
expect(deps.getCliStatus).toHaveBeenCalledTimes(1)
|
||||
expect(deps.showCliRegistrationPrompt).not.toHaveBeenCalled()
|
||||
expect(deps.installCli).not.toHaveBeenCalled()
|
||||
expect(deps.getComputerUsePermissionStatus).not.toHaveBeenCalled()
|
||||
expect(deps.openComputerUsePermissionSetup).not.toHaveBeenCalled()
|
||||
expect(deps.storage.get(BROWSER_USE_ENABLED_STORAGE_KEY)).toBe('0')
|
||||
@@ -308,7 +272,6 @@ describe('onboarding feature setup runner', () => {
|
||||
|
||||
expect(result).toEqual({
|
||||
selectedIds: [],
|
||||
cliTouched: false,
|
||||
skillCommandsCopied: false,
|
||||
skillInstallCommand: null,
|
||||
computerUsePermissionsOpened: false,
|
||||
@@ -316,8 +279,6 @@ describe('onboarding feature setup runner', () => {
|
||||
})
|
||||
expect(deps.storage.get(BROWSER_USE_ENABLED_STORAGE_KEY)).toBe('0')
|
||||
expect(deps.storage.get(ORCHESTRATION_ENABLED_STORAGE_KEY)).toBe('0')
|
||||
expect(deps.getCliStatus).not.toHaveBeenCalled()
|
||||
expect(deps.showCliRegistrationPrompt).not.toHaveBeenCalled()
|
||||
expect(deps.getComputerUsePermissionStatus).not.toHaveBeenCalled()
|
||||
expect(deps.clipboardWrites).toEqual([])
|
||||
})
|
||||
@@ -377,52 +338,4 @@ describe('onboarding feature setup runner', () => {
|
||||
message: 'Orca Computer Use.app was not found'
|
||||
})
|
||||
})
|
||||
|
||||
it('shows CLI registration context before installing a missing CLI during onboarding', async () => {
|
||||
const staleStatus: CliInstallStatus = {
|
||||
...INSTALLED_CLI_STATUS,
|
||||
state: 'stale',
|
||||
currentTarget: '/tmp/other-orca',
|
||||
detail: '/usr/local/bin/orca points to a different launcher.'
|
||||
}
|
||||
const showCliRegistrationPrompt = vi.fn(async () => undefined)
|
||||
const installCli = vi.fn(async () => INSTALLED_CLI_STATUS)
|
||||
const deps = createDeps({
|
||||
getCliStatus: vi.fn(async () => staleStatus),
|
||||
showCliRegistrationPrompt,
|
||||
installCli
|
||||
})
|
||||
|
||||
const result = await runOnboardingFeatureSetup(
|
||||
{ browserUse: true, computerUse: false, orchestration: false, linearTickets: false },
|
||||
deps
|
||||
)
|
||||
|
||||
expect(result.cliTouched).toBe(true)
|
||||
expect(showCliRegistrationPrompt).toHaveBeenCalledTimes(1)
|
||||
expect(installCli).toHaveBeenCalledTimes(1)
|
||||
expect(showCliRegistrationPrompt.mock.invocationCallOrder[0]).toBeLessThan(
|
||||
installCli.mock.invocationCallOrder[0]
|
||||
)
|
||||
})
|
||||
|
||||
it('warns without changing PATH when the Windows registry read is unknown', async () => {
|
||||
const unknownStatus: CliInstallStatus = {
|
||||
...INSTALLED_CLI_STATUS,
|
||||
platform: 'win32',
|
||||
pathConfigured: null,
|
||||
detail: 'Orca could not read the Windows user PATH registry value.'
|
||||
}
|
||||
const deps = createDeps({ getCliStatus: vi.fn(async () => unknownStatus) })
|
||||
|
||||
const result = await runOnboardingFeatureSetup(
|
||||
{ browserUse: true, computerUse: false, orchestration: false, linearTickets: false },
|
||||
deps
|
||||
)
|
||||
|
||||
expect(result.cliTouched).toBe(false)
|
||||
expect(result.warnings).toContainEqual({ featureId: 'cli', message: unknownStatus.detail })
|
||||
expect(deps.showCliRegistrationPrompt).not.toHaveBeenCalled()
|
||||
expect(deps.installCli).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { CliInstallStatus } from '../../../../shared/cli-install-types'
|
||||
import type {
|
||||
ComputerUsePermissionSetupResult,
|
||||
ComputerUsePermissionStatusResult
|
||||
@@ -12,13 +11,9 @@ import {
|
||||
} from '@/lib/agent-feature-install-commands'
|
||||
import { BROWSER_USE_ENABLED_STORAGE_KEY } from '@/lib/browser-use-setup-state'
|
||||
import { e2eConfig } from '@/lib/e2e-config'
|
||||
import { showOrcaCliRegistrationPromptToast } from '@/lib/agent-skill-cli-prerequisite'
|
||||
import type { ProjectAgentSkillRuntime } from '@/lib/project-skill-runtime'
|
||||
import type { OnboardingFeatureSetupRuntimeContext } from './onboarding-feature-setup-runtime'
|
||||
import {
|
||||
buildSkillCommandForRuntime,
|
||||
getWslCliDistroRequest
|
||||
} from '../settings/CliSkillRuntimeSetup'
|
||||
import { buildSkillCommandForRuntime } from '../settings/CliSkillRuntimeSetup'
|
||||
import {
|
||||
ORCHESTRATION_ENABLED_STORAGE_KEY,
|
||||
ORCHESTRATION_SETUP_DISMISSED_STORAGE_KEY,
|
||||
@@ -72,13 +67,12 @@ const FEATURE_TELEMETRY_IDS: Record<
|
||||
}
|
||||
|
||||
export type OnboardingFeatureSetupWarning = {
|
||||
featureId: OnboardingFeatureSetupId | 'cli' | 'skills'
|
||||
featureId: OnboardingFeatureSetupId | 'skills'
|
||||
message: string
|
||||
}
|
||||
|
||||
export type OnboardingFeatureSetupResult = {
|
||||
selectedIds: OnboardingFeatureSetupId[]
|
||||
cliTouched: boolean
|
||||
skillCommandsCopied: boolean
|
||||
skillInstallCommand: string | null
|
||||
computerUsePermissionsOpened: boolean
|
||||
@@ -86,9 +80,6 @@ export type OnboardingFeatureSetupResult = {
|
||||
}
|
||||
|
||||
export type OnboardingFeatureSetupDeps = {
|
||||
getCliStatus: () => Promise<CliInstallStatus>
|
||||
showCliRegistrationPrompt?: () => Promise<void>
|
||||
installCli: () => Promise<CliInstallStatus>
|
||||
writeClipboardText: (text: string) => Promise<void>
|
||||
getComputerUsePermissionStatus: () => Promise<ComputerUsePermissionStatusResult>
|
||||
openComputerUsePermissionSetup: () => Promise<ComputerUsePermissionSetupResult>
|
||||
@@ -161,7 +152,7 @@ export function onboardingFeatureSetupRunTelemetry(
|
||||
): EventProps<'onboarding_feature_setup_run'> {
|
||||
return {
|
||||
...onboardingFeatureSetupTelemetrySelection(selection),
|
||||
cli_touched: result.cliTouched,
|
||||
cli_touched: false,
|
||||
skill_commands_copied: result.skillCommandsCopied,
|
||||
skill_install_command_prepared: result.skillInstallCommand !== null,
|
||||
computer_use_permissions_opened: result.computerUsePermissionsOpened,
|
||||
@@ -169,26 +160,13 @@ export function onboardingFeatureSetupRunTelemetry(
|
||||
}
|
||||
}
|
||||
|
||||
export function createOnboardingFeatureSetupDeps(
|
||||
agentRuntime?: ProjectAgentSkillRuntime
|
||||
): OnboardingFeatureSetupDeps {
|
||||
export function createOnboardingFeatureSetupDeps(): OnboardingFeatureSetupDeps {
|
||||
const e2eDeps = getE2EOnboardingFeatureSetupDeps()
|
||||
if (e2eDeps) {
|
||||
return e2eDeps
|
||||
}
|
||||
|
||||
// Register `orca` on the same PATH used by the skill install (#12103).
|
||||
const wslDistroRequest =
|
||||
agentRuntime?.runtime === 'wsl' ? getWslCliDistroRequest(agentRuntime) : undefined
|
||||
const isWsl = agentRuntime?.runtime === 'wsl'
|
||||
return {
|
||||
getCliStatus: () =>
|
||||
isWsl
|
||||
? window.api.cli.getWslInstallStatus(wslDistroRequest)
|
||||
: window.api.cli.getInstallStatus(),
|
||||
showCliRegistrationPrompt: showOrcaCliRegistrationPromptToast,
|
||||
installCli: () =>
|
||||
isWsl ? window.api.cli.installWsl(wslDistroRequest) : window.api.cli.install(),
|
||||
writeClipboardText: (text) => window.api.ui.writeClipboardText(text),
|
||||
getComputerUsePermissionStatus: () => window.api.computerUsePermissions.getStatus(),
|
||||
openComputerUsePermissionSetup: () => window.api.computerUsePermissions.openSetup(),
|
||||
@@ -216,10 +194,9 @@ export async function runOnboardingFeatureSetup(
|
||||
const agentRuntime = runtimeContext?.installDisabledReason
|
||||
? undefined
|
||||
: runtimeContext?.agentRuntime
|
||||
const deps = explicitDeps ?? createOnboardingFeatureSetupDeps(agentRuntime)
|
||||
const deps = explicitDeps ?? createOnboardingFeatureSetupDeps()
|
||||
const selectedIds = selectedOnboardingFeatureSetupIds(selection)
|
||||
const warnings: OnboardingFeatureSetupWarning[] = []
|
||||
let cliTouched = false
|
||||
let skillCommandsCopied = false
|
||||
const skillInstallCommand = buildOnboardingFeatureSetupSkillCommand(selection)
|
||||
let computerUsePermissionsOpened = false
|
||||
@@ -234,7 +211,6 @@ export async function runOnboardingFeatureSetup(
|
||||
if (selectedIds.length === 0) {
|
||||
return {
|
||||
selectedIds,
|
||||
cliTouched,
|
||||
skillCommandsCopied,
|
||||
skillInstallCommand,
|
||||
computerUsePermissionsOpened,
|
||||
@@ -242,36 +218,6 @@ export async function runOnboardingFeatureSetup(
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const status = await deps.getCliStatus()
|
||||
if (!status.supported) {
|
||||
warnings.push({
|
||||
featureId: 'cli',
|
||||
message: status.detail ?? 'Orca CLI registration is not available on this platform.'
|
||||
})
|
||||
} else if (status.pathConfigured === null) {
|
||||
// Why: an unknown registry read cannot safely drive a PATH read-modify-write.
|
||||
warnings.push({
|
||||
featureId: 'cli',
|
||||
message: status.detail ?? 'Orca could not check your Windows user PATH.'
|
||||
})
|
||||
} else if (status.state !== 'installed' || status.pathConfigured === false) {
|
||||
await deps.showCliRegistrationPrompt?.()
|
||||
const next = await deps.installCli()
|
||||
cliTouched = true
|
||||
if (next.state !== 'installed') {
|
||||
warnings.push({
|
||||
featureId: 'cli',
|
||||
message: next.detail ?? 'Orca CLI registration needs attention.'
|
||||
})
|
||||
} else if (next.pathConfigured !== true && next.detail) {
|
||||
warnings.push({ featureId: 'cli', message: next.detail })
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
warnings.push({ featureId: 'cli', message: formatFeatureSetupError(error) })
|
||||
}
|
||||
|
||||
if (selection.computerUse) {
|
||||
try {
|
||||
const status = await deps.getComputerUsePermissionStatus()
|
||||
@@ -306,7 +252,6 @@ export async function runOnboardingFeatureSetup(
|
||||
|
||||
return {
|
||||
selectedIds,
|
||||
cliTouched,
|
||||
skillCommandsCopied,
|
||||
skillInstallCommand,
|
||||
computerUsePermissionsOpened,
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import type { CliInstallStatus } from '../../../../shared/cli-install-types'
|
||||
import { Button } from '../ui/button'
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip'
|
||||
import { SearchableSetting } from './SearchableSetting'
|
||||
import { StepBadge } from './SetupStepBadge'
|
||||
import { getBrowserUsePaneSearchEntries } from './browser-use-search'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
type BrowserUseCliStepProps = {
|
||||
cliStatus: CliInstallStatus | null
|
||||
cliEnabled: boolean
|
||||
cliLoading: boolean
|
||||
cliBusy: boolean
|
||||
cliSupported: boolean
|
||||
cliPathNeedsAttention: boolean
|
||||
onEnableCli: () => void
|
||||
}
|
||||
|
||||
export function BrowserUseCliStep({
|
||||
cliStatus,
|
||||
cliEnabled,
|
||||
cliLoading,
|
||||
cliBusy,
|
||||
cliSupported,
|
||||
cliPathNeedsAttention,
|
||||
onEnableCli
|
||||
}: BrowserUseCliStepProps): React.JSX.Element {
|
||||
return (
|
||||
<SearchableSetting
|
||||
title={translate('auto.components.settings.BrowserUsePane.c6065d205d', 'Enable Orca CLI')}
|
||||
description={translate(
|
||||
'auto.components.settings.BrowserUsePane.c79eff0213',
|
||||
'Register the Orca CLI so agents can drive the browser.'
|
||||
)}
|
||||
keywords={getBrowserUsePaneSearchEntries()[0].keywords}
|
||||
className="rounded-xl border border-border/60 bg-card/50 p-4"
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<StepBadge index={1} state={cliEnabled ? 'done' : cliBusy ? 'in-progress' : 'pending'} />
|
||||
<div className="min-w-0 flex-1 space-y-1">
|
||||
<p className="text-sm font-medium">
|
||||
{translate('auto.components.settings.BrowserUsePane.c6065d205d', 'Enable Orca CLI')}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.settings.BrowserUsePane.9fca1f7f5d',
|
||||
'Registers the Orca CLI command so agents can orchestrate the browser from their shell.'
|
||||
)}
|
||||
</p>
|
||||
{cliStatus?.commandPath && cliEnabled ? (
|
||||
<p className="text-[11px] text-muted-foreground">
|
||||
{translate('auto.components.settings.BrowserUsePane.e9f3f3b488', 'Installed at')}{' '}
|
||||
<code className="rounded bg-muted px-1 py-0.5">{cliStatus.commandPath}</code>
|
||||
</p>
|
||||
) : null}
|
||||
{cliPathNeedsAttention && cliStatus?.detail ? (
|
||||
<p className="text-[11px] text-amber-600 dark:text-amber-400">{cliStatus.detail}</p>
|
||||
) : null}
|
||||
</div>
|
||||
<TooltipProvider delayDuration={250}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={cliEnabled ? 'outline' : 'default'}
|
||||
disabled={cliLoading || cliBusy || !cliSupported || cliEnabled}
|
||||
onClick={() => void onEnableCli()}
|
||||
>
|
||||
{cliBusy
|
||||
? translate(
|
||||
'auto.components.settings.BrowserUsePane.8b3054dac7',
|
||||
'Registering...'
|
||||
)
|
||||
: cliEnabled
|
||||
? translate('auto.components.settings.BrowserUsePane.0289434ed6', 'Enabled')
|
||||
: cliPathNeedsAttention
|
||||
? translate(
|
||||
'auto.components.settings.BrowserUsePane.ad8cb0ee22',
|
||||
'Fix PATH'
|
||||
)
|
||||
: translate('auto.components.settings.BrowserUsePane.de9b2f32f3', 'Enable')}
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
{!cliSupported && !cliLoading && cliStatus?.detail ? (
|
||||
<TooltipContent side="left" sideOffset={6}>
|
||||
{cliStatus.detail}
|
||||
</TooltipContent>
|
||||
) : null}
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</SearchableSetting>
|
||||
)
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import { translate } from '@/i18n/i18n'
|
||||
type BrowserUseCookieImportStepProps = {
|
||||
cookiesImported: boolean
|
||||
isImportingDefault: boolean
|
||||
step3Blocked: boolean
|
||||
disabled: boolean
|
||||
sourceLabel: string | null
|
||||
onConfigureMoreBrowsers?: () => void
|
||||
}
|
||||
@@ -33,7 +33,7 @@ type BrowserUseCookieImportStepProps = {
|
||||
export function BrowserUseCookieImportStep({
|
||||
cookiesImported,
|
||||
isImportingDefault,
|
||||
step3Blocked,
|
||||
disabled,
|
||||
sourceLabel,
|
||||
onConfigureMoreBrowsers
|
||||
}: BrowserUseCookieImportStepProps): React.JSX.Element {
|
||||
@@ -95,15 +95,12 @@ export function BrowserUseCookieImportStep({
|
||||
'auto.components.settings.BrowserUsePane.af8c83ed61',
|
||||
'Import cookies from Chrome, Edge, or other browsers so agents can reuse your logins.'
|
||||
)}
|
||||
keywords={getBrowserUsePaneSearchEntries()[2].keywords}
|
||||
className={cn(
|
||||
'rounded-xl border border-border/60 bg-card/50 p-4',
|
||||
step3Blocked && 'opacity-60'
|
||||
)}
|
||||
keywords={getBrowserUsePaneSearchEntries()[1].keywords}
|
||||
className={cn('rounded-xl border border-border/60 bg-card/50 p-4', disabled && 'opacity-60')}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<StepBadge
|
||||
index={3}
|
||||
index={2}
|
||||
state={cookiesImported ? 'done' : isImportingDefault ? 'in-progress' : 'pending'}
|
||||
/>
|
||||
<div className="min-w-0 flex-1 space-y-1">
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import type { CliInstallStatus } from '../../../../shared/cli-install-types'
|
||||
import { useEffect, useState } from 'react'
|
||||
import {
|
||||
ORCA_CLI_SKILL_INSTALL_COMMAND,
|
||||
ORCA_CLI_SKILL_NAME,
|
||||
ORCA_CLI_SKILL_UPDATE_COMMAND
|
||||
} from '@/lib/agent-feature-install-commands'
|
||||
import {
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal,
|
||||
isOrcaCliAvailableOnPath
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import { BROWSER_USE_ENABLED_STORAGE_KEY } from '@/lib/browser-use-setup-state'
|
||||
import {
|
||||
GLOBAL_AGENT_SKILL_SOURCE_KINDS,
|
||||
useInstalledAgentSkill
|
||||
} from '@/hooks/useInstalledAgentSkills'
|
||||
import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime'
|
||||
import { useMountedRef } from '@/hooks/useMountedRef'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useAppStore } from '../../store'
|
||||
import { BROWSER_FAMILY_LABELS } from '../../../../shared/constants'
|
||||
@@ -28,13 +20,8 @@ import { BrowserUseExamples } from './BrowserUseExamples'
|
||||
import { BrowserUseComputerUseNotice } from './BrowserUseComputerUseNotice'
|
||||
import { BrowserUseEnableSwitch } from './BrowserUseEnableSwitch'
|
||||
import { BrowserUseSkillStep } from './BrowserUseSkillStep'
|
||||
import { BrowserUseCliStep } from './BrowserUseCliStep'
|
||||
import { BrowserUseCookieImportStep } from './BrowserUseCookieImportStep'
|
||||
import {
|
||||
buildSkillCommandForRuntime,
|
||||
ensureWslCliAvailableForAgentSkillTerminal,
|
||||
getWslCliDistroRequest
|
||||
} from './CliSkillRuntimeSetup'
|
||||
import { buildSkillCommandForRuntime } from './CliSkillRuntimeSetup'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
type BrowserUseSetupProps = {
|
||||
@@ -51,10 +38,6 @@ export function BrowserUseSetup({
|
||||
const fetchBrowserSessionProfiles = useAppStore((s) => s.fetchBrowserSessionProfiles)
|
||||
const browserSessionImportState = useAppStore((s) => s.browserSessionImportState)
|
||||
|
||||
const [cliStatus, setCliStatus] = useState<CliInstallStatus | null>(null)
|
||||
const [cliLoading, setCliLoading] = useState(true)
|
||||
const [cliBusy, setCliBusy] = useState(false)
|
||||
const mountedRef = useMountedRef()
|
||||
const activeSkillRuntime = useActiveProjectSkillRuntime()
|
||||
const browserUseInstallCommand = !activeSkillRuntime.installDisabledReason
|
||||
? buildSkillCommandForRuntime(ORCA_CLI_SKILL_INSTALL_COMMAND, activeSkillRuntime.agentRuntime)
|
||||
@@ -63,15 +46,6 @@ export function BrowserUseSetup({
|
||||
? buildSkillCommandForRuntime(ORCA_CLI_SKILL_UPDATE_COMMAND, activeSkillRuntime.agentRuntime)
|
||||
: ORCA_CLI_SKILL_UPDATE_COMMAND
|
||||
|
||||
const handleCliStatusChange = useCallback(
|
||||
(nextStatus: CliInstallStatus | null): void => {
|
||||
if (mountedRef.current) {
|
||||
setCliStatus(nextStatus)
|
||||
}
|
||||
},
|
||||
[mountedRef]
|
||||
)
|
||||
|
||||
const [browserUseEnabled, setBrowserUseEnabled] = useState<boolean>(() => {
|
||||
return localStorage.getItem(BROWSER_USE_ENABLED_STORAGE_KEY) === '1'
|
||||
})
|
||||
@@ -84,54 +58,16 @@ export function BrowserUseSetup({
|
||||
}
|
||||
}
|
||||
|
||||
const refreshCli = useCallback(async (): Promise<void> => {
|
||||
setCliLoading(true)
|
||||
try {
|
||||
if (activeSkillRuntime.installDisabledReason) {
|
||||
handleCliStatusChange(null)
|
||||
return
|
||||
}
|
||||
const nextStatus =
|
||||
activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? await window.api.cli.getWslInstallStatus(
|
||||
getWslCliDistroRequest(activeSkillRuntime.agentRuntime)
|
||||
)
|
||||
: await window.api.cli.getInstallStatus()
|
||||
handleCliStatusChange(nextStatus)
|
||||
} catch (error) {
|
||||
if (mountedRef.current) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: translate(
|
||||
'auto.components.settings.BrowserUsePane.180a9abf3a',
|
||||
'Failed to load CLI status.'
|
||||
)
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
if (mountedRef.current) {
|
||||
setCliLoading(false)
|
||||
}
|
||||
}
|
||||
}, [activeSkillRuntime, handleCliStatusChange, mountedRef])
|
||||
|
||||
useEffect(() => {
|
||||
if (!browserUseEnabled) {
|
||||
return
|
||||
}
|
||||
void refreshCli()
|
||||
void fetchBrowserSessionProfiles()
|
||||
}, [browserUseEnabled, fetchBrowserSessionProfiles, refreshCli])
|
||||
}, [browserUseEnabled, fetchBrowserSessionProfiles])
|
||||
|
||||
const defaultProfile = browserSessionProfiles.find((p) => p.id === 'default')
|
||||
const cookiesImported = !!defaultProfile?.source
|
||||
|
||||
const cliEnabled = isOrcaCliAvailableOnPath(cliStatus)
|
||||
const cliPathNeedsAttention =
|
||||
cliStatus?.state === 'installed' && cliStatus.pathConfigured === false
|
||||
const cliSupported = cliStatus?.supported ?? false
|
||||
|
||||
const {
|
||||
installed: skillDetected,
|
||||
loading: skillLoading,
|
||||
@@ -143,47 +79,18 @@ export function BrowserUseSetup({
|
||||
sourceKinds: GLOBAL_AGENT_SKILL_SOURCE_KINDS
|
||||
})
|
||||
|
||||
const handleEnableCli = async (): Promise<void> => {
|
||||
if (activeSkillRuntime.installDisabledReason) {
|
||||
return
|
||||
}
|
||||
setCliBusy(true)
|
||||
try {
|
||||
const next =
|
||||
activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? await ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime)
|
||||
: await ensureOrcaCliAvailableForAgentSkillTerminal({
|
||||
onStatusChange: handleCliStatusChange
|
||||
})
|
||||
if (activeSkillRuntime.agentRuntime?.runtime === 'wsl') {
|
||||
handleCliStatusChange(next)
|
||||
}
|
||||
if (mountedRef.current && isOrcaCliAvailableOnPath(next)) {
|
||||
toast.success(
|
||||
translate(
|
||||
'auto.components.settings.BrowserUsePane.721aee31b4',
|
||||
'Registered the Orca CLI in PATH.'
|
||||
)
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
if (mountedRef.current) {
|
||||
setCliBusy(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isImportingDefault =
|
||||
browserSessionImportState?.profileId === 'default' &&
|
||||
browserSessionImportState.status === 'importing'
|
||||
|
||||
const showStep1 = matchesSettingsSearch(searchQuery, [getBrowserUsePaneSearchEntries()[0]])
|
||||
const showStep2 = matchesSettingsSearch(searchQuery, [getBrowserUsePaneSearchEntries()[1]])
|
||||
const showStep3 = matchesSettingsSearch(searchQuery, [getBrowserUsePaneSearchEntries()[2]])
|
||||
const completedCount = [cliEnabled, skillDetected, cookiesImported].filter(Boolean).length
|
||||
const step2Blocked =
|
||||
Boolean(activeSkillRuntime.installDisabledReason) || (!cliEnabled && !skillDetected)
|
||||
const step3Blocked = !cookiesImported && (!cliEnabled || !skillDetected)
|
||||
const showSkillStep = matchesSettingsSearch(searchQuery, [getBrowserUsePaneSearchEntries()[0]])
|
||||
const showCookieImportStep = matchesSettingsSearch(searchQuery, [
|
||||
getBrowserUsePaneSearchEntries()[1]
|
||||
])
|
||||
const steps = [skillDetected, cookiesImported]
|
||||
const completedCount = steps.filter(Boolean).length
|
||||
const skillDisabled = Boolean(activeSkillRuntime.installDisabledReason)
|
||||
const cookieImportDisabled = !cookiesImported && !skillDetected
|
||||
|
||||
const sourceLabel = defaultProfile?.source
|
||||
? `${BROWSER_FAMILY_LABELS[defaultProfile.source.browserFamily] ?? defaultProfile.source.browserFamily}${defaultProfile.source.profileName ? ` (${defaultProfile.source.profileName})` : ''}`
|
||||
@@ -220,20 +127,20 @@ export function BrowserUseSetup({
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.settings.BrowserUsePane.702488a5f7',
|
||||
'Let coding agents drive this browser with your logins. Finish the three steps below.'
|
||||
'auto.components.settings.BrowserUsePane.finishSteps',
|
||||
'Let coding agents drive this browser with your logins. Finish the steps below.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 text-[10px] font-medium ${
|
||||
completedCount === 3
|
||||
completedCount === steps.length
|
||||
? 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-400'
|
||||
: 'bg-muted text-muted-foreground'
|
||||
}`}
|
||||
>
|
||||
{completedCount}/3
|
||||
{completedCount}/{steps.length}
|
||||
</span>
|
||||
<BrowserUseEnableSwitch
|
||||
enabled={browserUseEnabled}
|
||||
@@ -246,19 +153,7 @@ export function BrowserUseSetup({
|
||||
<BrowserUseComputerUseNotice onOpenComputerUse={onOpenComputerUse} />
|
||||
) : null}
|
||||
|
||||
{showStep1 ? (
|
||||
<BrowserUseCliStep
|
||||
cliStatus={cliStatus}
|
||||
cliEnabled={cliEnabled}
|
||||
cliLoading={cliLoading}
|
||||
cliBusy={cliBusy}
|
||||
cliSupported={cliSupported}
|
||||
cliPathNeedsAttention={cliPathNeedsAttention}
|
||||
onEnableCli={() => void handleEnableCli()}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{showStep2 ? (
|
||||
{showSkillStep ? (
|
||||
<SearchableSetting
|
||||
title={translate(
|
||||
'auto.components.settings.BrowserUsePane.2d6ead9ab2',
|
||||
@@ -268,10 +163,10 @@ export function BrowserUseSetup({
|
||||
'auto.components.settings.BrowserUsePane.68ea76eb71',
|
||||
"Install the Browser Use skill so agents can operate Orca's browser."
|
||||
)}
|
||||
keywords={getBrowserUsePaneSearchEntries()[1].keywords}
|
||||
keywords={getBrowserUsePaneSearchEntries()[0].keywords}
|
||||
className={cn(
|
||||
'rounded-xl border border-border/60 bg-card/50 p-4',
|
||||
step2Blocked && 'opacity-60'
|
||||
skillDisabled && 'opacity-60'
|
||||
)}
|
||||
>
|
||||
<BrowserUseSkillStep
|
||||
@@ -280,35 +175,22 @@ export function BrowserUseSetup({
|
||||
skillDetected={skillDetected}
|
||||
skillLoading={skillLoading}
|
||||
skillError={activeSkillRuntime.installDisabledReason ?? skillError}
|
||||
disabled={step2Blocked}
|
||||
disabled={skillDisabled}
|
||||
terminalShellOverride={activeSkillRuntime.terminalShellOverride}
|
||||
terminalRuntime={activeSkillRuntime.agentRuntime}
|
||||
preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE}
|
||||
getPrerequisiteStatus={() =>
|
||||
activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? window.api.cli.getWslInstallStatus(
|
||||
getWslCliDistroRequest(activeSkillRuntime.agentRuntime)
|
||||
)
|
||||
: window.api.cli.getInstallStatus()
|
||||
}
|
||||
onBeforeOpenTerminal={async () => {
|
||||
onBeforeOpenTerminal={() => {
|
||||
useAppStore.getState().recordFeatureInteraction('agent-browser-setup')
|
||||
await (activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime)
|
||||
: ensureOrcaCliAvailableForAgentSkillTerminal({
|
||||
onStatusChange: handleCliStatusChange
|
||||
}))
|
||||
}}
|
||||
onRecheck={refreshSkill}
|
||||
/>
|
||||
</SearchableSetting>
|
||||
) : null}
|
||||
|
||||
{showStep3 ? (
|
||||
{showCookieImportStep ? (
|
||||
<BrowserUseCookieImportStep
|
||||
cookiesImported={cookiesImported}
|
||||
isImportingDefault={isImportingDefault}
|
||||
step3Blocked={step3Blocked}
|
||||
disabled={cookieImportDisabled}
|
||||
sourceLabel={sourceLabel}
|
||||
onConfigureMoreBrowsers={onConfigureMoreBrowsers}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { AgentSkillSetupPanel } from './AgentSkillSetupPanel'
|
||||
import type { LocalAgentRuntime } from './CliSkillRuntimeSetup'
|
||||
import { StepBadge } from './SetupStepBadge'
|
||||
@@ -13,8 +12,6 @@ type Props = {
|
||||
disabled?: boolean
|
||||
terminalShellOverride?: string
|
||||
terminalRuntime?: LocalAgentRuntime
|
||||
preInstallNotice?: ReactNode
|
||||
getPrerequisiteStatus?: () => Promise<Awaited<ReturnType<typeof window.api.cli.getInstallStatus>>>
|
||||
onBeforeOpenTerminal?: () => void | Promise<void>
|
||||
onRecheck: () => void | Promise<unknown>
|
||||
}
|
||||
@@ -28,8 +25,6 @@ export function BrowserUseSkillStep({
|
||||
disabled = false,
|
||||
terminalShellOverride,
|
||||
terminalRuntime,
|
||||
preInstallNotice,
|
||||
getPrerequisiteStatus,
|
||||
onBeforeOpenTerminal,
|
||||
onRecheck
|
||||
}: Props): React.JSX.Element {
|
||||
@@ -55,9 +50,7 @@ export function BrowserUseSkillStep({
|
||||
loading={skillLoading}
|
||||
error={skillError}
|
||||
installDisabled={disabled}
|
||||
leading={<StepBadge index={2} state={skillDetected ? 'done' : 'pending'} />}
|
||||
preInstallNotice={preInstallNotice}
|
||||
getPrerequisiteStatus={getPrerequisiteStatus}
|
||||
leading={<StepBadge index={1} state={skillDetected ? 'done' : 'pending'} />}
|
||||
onBeforeOpenTerminal={onBeforeOpenTerminal}
|
||||
onRecheck={onRecheck}
|
||||
/>
|
||||
|
||||
@@ -10,18 +10,26 @@ import {
|
||||
} from '@/lib/agent-feature-install-commands'
|
||||
import { CliSection } from './CliSection'
|
||||
|
||||
const capturedPanel = vi.hoisted(() => ({
|
||||
canUseLocalSkillFreshness: true,
|
||||
props: null as null | {
|
||||
command: string
|
||||
installedCommand: string
|
||||
terminalRuntime?: { runtime: 'host' | 'wsl'; wslDistro?: string | null; label: string }
|
||||
freshnessSkillName?: string
|
||||
getPrerequisiteStatus: () => Promise<unknown>
|
||||
onBeforeOpenTerminal: () => Promise<void>
|
||||
},
|
||||
useInstalledAgentSkill: vi.fn()
|
||||
}))
|
||||
type CapturedPanelProps = {
|
||||
command: string
|
||||
installedCommand: string
|
||||
terminalRuntime?: { runtime: 'host' | 'wsl'; wslDistro?: string | null; label: string }
|
||||
freshnessSkillName?: string
|
||||
getPrerequisiteStatus?: () => Promise<unknown>
|
||||
onBeforeOpenTerminal?: () => Promise<void>
|
||||
}
|
||||
|
||||
const capturedPanel = vi.hoisted(
|
||||
(): {
|
||||
canUseLocalSkillFreshness: boolean
|
||||
props: CapturedPanelProps | null
|
||||
useInstalledAgentSkill: ReturnType<typeof vi.fn>
|
||||
} => ({
|
||||
canUseLocalSkillFreshness: true,
|
||||
props: null,
|
||||
useInstalledAgentSkill: vi.fn()
|
||||
})
|
||||
)
|
||||
const toastError = vi.hoisted(() => vi.fn())
|
||||
|
||||
vi.mock('sonner', () => ({ toast: { error: toastError, success: vi.fn() } }))
|
||||
@@ -52,13 +60,7 @@ afterEach(() => {
|
||||
})
|
||||
|
||||
vi.mock('./AgentSkillSetupPanel', () => ({
|
||||
AgentSkillSetupPanel: function AgentSkillSetupPanel(props: {
|
||||
command: string
|
||||
installedCommand: string
|
||||
freshnessSkillName?: string
|
||||
getPrerequisiteStatus: () => Promise<unknown>
|
||||
onBeforeOpenTerminal: () => Promise<void>
|
||||
}) {
|
||||
AgentSkillSetupPanel: function AgentSkillSetupPanel(props: CapturedPanelProps) {
|
||||
capturedPanel.props = props
|
||||
return <div data-testid="agent-skill-setup-panel" />
|
||||
}
|
||||
@@ -101,7 +103,7 @@ describe('CliSection project runtime defaults', () => {
|
||||
expect(capturedPanel.props?.freshnessSkillName).toBeUndefined()
|
||||
})
|
||||
|
||||
it('passes the default project WSL distro to CLI skill prerequisite checks', async () => {
|
||||
it('targets the default project WSL distro without registering the CLI', async () => {
|
||||
const getWslInstallStatus = vi
|
||||
.fn()
|
||||
.mockResolvedValue({ supported: true, state: 'installed', pathConfigured: true })
|
||||
@@ -130,9 +132,6 @@ describe('CliSection project runtime defaults', () => {
|
||||
/>
|
||||
)
|
||||
|
||||
await capturedPanel.props?.getPrerequisiteStatus()
|
||||
await capturedPanel.props?.onBeforeOpenTerminal()
|
||||
|
||||
expect(capturedPanel.useInstalledAgentSkill).toHaveBeenCalledWith(
|
||||
'orca-cli',
|
||||
expect.objectContaining({
|
||||
@@ -147,8 +146,9 @@ describe('CliSection project runtime defaults', () => {
|
||||
wslDistro: 'Ubuntu',
|
||||
label: 'WSL Ubuntu'
|
||||
})
|
||||
expect(getWslInstallStatus).toHaveBeenCalledWith({ distro: 'Ubuntu' })
|
||||
expect(getWslInstallStatus).toHaveBeenCalledTimes(2)
|
||||
expect(capturedPanel.props?.getPrerequisiteStatus).toBeUndefined()
|
||||
expect(capturedPanel.props?.onBeforeOpenTerminal).toBeUndefined()
|
||||
expect(getWslInstallStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('renders an inline unknown PATH state without offering a mutation', async () => {
|
||||
|
||||
@@ -8,11 +8,6 @@ import {
|
||||
ORCA_CLI_SKILL_NAME,
|
||||
ORCA_CLI_SKILL_UPDATE_COMMAND
|
||||
} from '@/lib/agent-feature-install-commands'
|
||||
import {
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal,
|
||||
isOrcaCliAvailableOnPath
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import {
|
||||
GLOBAL_AGENT_SKILL_SOURCE_KINDS,
|
||||
useInstalledAgentSkill
|
||||
@@ -26,11 +21,9 @@ import { AgentSkillSetupPanel } from './AgentSkillSetupPanel'
|
||||
import { CliRegistrationDialog } from './CliRegistrationDialog'
|
||||
import {
|
||||
buildSkillCommandForRuntime,
|
||||
ensureWslCliAvailableForAgentSkillTerminal,
|
||||
getAgentSkillTerminalShellOverride,
|
||||
getSelectedAgentRuntime,
|
||||
getSkillDiscoveryTargetForRuntime,
|
||||
getWslCliDistroRequest
|
||||
getSkillDiscoveryTargetForRuntime
|
||||
} from './CliSkillRuntimeSetup'
|
||||
import { WslCliRegistration } from './WslCliRegistration'
|
||||
import { useCliRegistrationActions } from './use-cli-registration-actions'
|
||||
@@ -115,14 +108,6 @@ export function CliSection({
|
||||
settings,
|
||||
agentRuntime
|
||||
)
|
||||
const getCliSkillPrerequisiteStatus = useCallback(
|
||||
() =>
|
||||
agentRuntime.runtime === 'wsl'
|
||||
? window.api.cli.getWslInstallStatus(getWslCliDistroRequest(agentRuntime))
|
||||
: window.api.cli.getInstallStatus(),
|
||||
[agentRuntime]
|
||||
)
|
||||
|
||||
const handleStatusChange = useCallback(
|
||||
(nextStatus: CliInstallStatus): void => {
|
||||
if (mountedRef.current) {
|
||||
@@ -185,8 +170,8 @@ export function CliSection({
|
||||
</h2>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.settings.CliSection.6930feda9e',
|
||||
'Use Orca from your terminal to open the app, manage worktrees, and interact with Orca terminals.'
|
||||
'auto.components.settings.CliSection.outsideOrcaDescription',
|
||||
'Orca terminals already have `orca`. Turn this on to use `orca` from other terminals outside Orca.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
@@ -350,16 +335,6 @@ export function CliSection({
|
||||
installed={cliSkillDetected}
|
||||
loading={cliSkillLoading}
|
||||
error={cliSkillError}
|
||||
preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE}
|
||||
getPrerequisiteStatus={getCliSkillPrerequisiteStatus}
|
||||
isPrerequisiteAvailable={isOrcaCliAvailableOnPath}
|
||||
onBeforeOpenTerminal={async () => {
|
||||
await (agentRuntime.runtime === 'wsl'
|
||||
? ensureWslCliAvailableForAgentSkillTerminal(agentRuntime)
|
||||
: ensureOrcaCliAvailableForAgentSkillTerminal({
|
||||
onStatusChange: handleStatusChange
|
||||
}))
|
||||
}}
|
||||
onRecheck={refreshCliSkill}
|
||||
freshnessSkillName={cliSkillFreshnessName}
|
||||
/>
|
||||
|
||||
@@ -4,10 +4,6 @@ import {
|
||||
COMPUTER_USE_SKILL_NAME,
|
||||
COMPUTER_USE_SKILL_UPDATE_COMMAND
|
||||
} from '@/lib/agent-feature-install-commands'
|
||||
import {
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import {
|
||||
GLOBAL_AGENT_SKILL_SOURCE_KINDS,
|
||||
useInstalledAgentSkill
|
||||
@@ -15,11 +11,7 @@ import {
|
||||
import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime'
|
||||
import { useAppStore } from '@/store'
|
||||
import { AgentSkillSetupPanel } from './AgentSkillSetupPanel'
|
||||
import {
|
||||
buildSkillCommandForRuntime,
|
||||
ensureWslCliAvailableForAgentSkillTerminal,
|
||||
getWslCliDistroRequest
|
||||
} from './CliSkillRuntimeSetup'
|
||||
import { buildSkillCommandForRuntime } from './CliSkillRuntimeSetup'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
export function ComputerUseSkillSetupPanel(): React.JSX.Element {
|
||||
@@ -65,19 +57,8 @@ export function ComputerUseSkillSetupPanel(): React.JSX.Element {
|
||||
error={activeSkillRuntime.installDisabledReason ?? computerUseSkillError}
|
||||
installDisabled={Boolean(activeSkillRuntime.installDisabledReason)}
|
||||
icon={<MonitorCog className="size-5" />}
|
||||
preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE}
|
||||
getPrerequisiteStatus={() =>
|
||||
activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? window.api.cli.getWslInstallStatus(
|
||||
getWslCliDistroRequest(activeSkillRuntime.agentRuntime)
|
||||
)
|
||||
: window.api.cli.getInstallStatus()
|
||||
}
|
||||
onBeforeOpenTerminal={async () => {
|
||||
onBeforeOpenTerminal={() => {
|
||||
useAppStore.getState().recordFeatureInteraction('computer-use-setup')
|
||||
await (activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime)
|
||||
: ensureOrcaCliAvailableForAgentSkillTerminal())
|
||||
}}
|
||||
onRecheck={refreshComputerUseSkill}
|
||||
freshnessSkillName={
|
||||
|
||||
@@ -8,10 +8,6 @@ import { Button } from '../ui/button'
|
||||
import { AgentSkillSetupPanel } from './AgentSkillSetupPanel'
|
||||
import { EphemeralVmRecipeRow } from './EphemeralVmRecipeRow'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import {
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import {
|
||||
EPHEMERAL_VMS_SKILL_INSTALL_COMMAND,
|
||||
EPHEMERAL_VMS_SKILL_NAME,
|
||||
@@ -22,11 +18,7 @@ import {
|
||||
useInstalledAgentSkill
|
||||
} from '@/hooks/useInstalledAgentSkills'
|
||||
import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime'
|
||||
import {
|
||||
buildSkillCommandForRuntime,
|
||||
ensureWslCliAvailableForAgentSkillTerminal,
|
||||
getWslCliDistroRequest
|
||||
} from './CliSkillRuntimeSetup'
|
||||
import { buildSkillCommandForRuntime } from './CliSkillRuntimeSetup'
|
||||
|
||||
type RecipeCatalogEntry = Awaited<
|
||||
ReturnType<typeof window.api.ephemeralVm.listRecipeCatalog>
|
||||
@@ -187,19 +179,6 @@ export function EphemeralVmsPane(): React.JSX.Element {
|
||||
error={activeSkillRuntime.installDisabledReason ?? skillError}
|
||||
installDisabled={Boolean(activeSkillRuntime.installDisabledReason)}
|
||||
icon={<Server className="size-5" />}
|
||||
preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE}
|
||||
getPrerequisiteStatus={() =>
|
||||
activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? window.api.cli.getWslInstallStatus(
|
||||
getWslCliDistroRequest(activeSkillRuntime.agentRuntime)
|
||||
)
|
||||
: window.api.cli.getInstallStatus()
|
||||
}
|
||||
onBeforeOpenTerminal={async () => {
|
||||
await (activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime)
|
||||
: ensureOrcaCliAvailableForAgentSkillTerminal())
|
||||
}}
|
||||
onRecheck={refreshSkill}
|
||||
freshnessSkillName={
|
||||
activeSkillRuntime.canUseLocalSkillFreshness ? EPHEMERAL_VMS_SKILL_NAME : undefined
|
||||
|
||||
@@ -88,9 +88,6 @@ export function LinearAgentSkillPane(): React.JSX.Element {
|
||||
loading={skillSetup.skillLoading}
|
||||
error={skillSetup.error}
|
||||
installDisabled={skillSetup.installDisabled}
|
||||
preInstallNotice={skillSetup.preInstallNotice}
|
||||
getPrerequisiteStatus={skillSetup.getPrerequisiteStatus}
|
||||
onBeforeOpenTerminal={skillSetup.onBeforeOpenTerminal}
|
||||
onRecheck={skillSetup.refreshSkill}
|
||||
freshnessSkillName={skillSetup.freshnessSkillName}
|
||||
/>
|
||||
|
||||
@@ -2,10 +2,17 @@ import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { MobileEmulatorAgentControlRow } from './MobileEmulatorAgentControlRow'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
canUseLocalSkillFreshness: true,
|
||||
freshnessSkillName: undefined as string | undefined
|
||||
}))
|
||||
const mocks = vi.hoisted(
|
||||
(): {
|
||||
canUseLocalSkillFreshness: boolean
|
||||
freshnessSkillName: string | undefined
|
||||
panelProps: Record<string, unknown>
|
||||
} => ({
|
||||
canUseLocalSkillFreshness: true,
|
||||
freshnessSkillName: undefined,
|
||||
panelProps: {}
|
||||
})
|
||||
)
|
||||
|
||||
vi.mock('@/hooks/useActiveProjectSkillRuntime', () => ({
|
||||
useActiveProjectSkillRuntime: () => ({
|
||||
@@ -16,30 +23,25 @@ vi.mock('@/hooks/useActiveProjectSkillRuntime', () => ({
|
||||
|
||||
vi.mock('../emulator-pane/use-mobile-emulator-agent-setup-state', () => ({
|
||||
useMobileEmulatorAgentSetupState: () => ({
|
||||
cliActionLabel: 'Enable',
|
||||
cliBusy: false,
|
||||
cliEnabled: true,
|
||||
cliInstallStatus: null,
|
||||
cliLoading: false,
|
||||
cliSkillError: null,
|
||||
cliSkillInstalled: true,
|
||||
cliSkillLoading: false,
|
||||
cliSupported: true,
|
||||
completedCount: 2,
|
||||
handleEnableCli: vi.fn(),
|
||||
recheckSetup: vi.fn(),
|
||||
refreshCliSkill: vi.fn(),
|
||||
step2Blocked: false
|
||||
setupComplete: true,
|
||||
setupRechecking: false,
|
||||
statusReady: true
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('./AgentSkillSetupPanel', () => ({
|
||||
AgentSkillSetupPanel: ({ freshnessSkillName }: { freshnessSkillName?: string }) => {
|
||||
mocks.freshnessSkillName = freshnessSkillName
|
||||
AgentSkillSetupPanel: (props: Record<string, unknown> & { freshnessSkillName?: string }) => {
|
||||
mocks.freshnessSkillName = props.freshnessSkillName
|
||||
mocks.panelProps = props
|
||||
return null
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('./SetupStepBadge', () => ({ StepBadge: () => null }))
|
||||
vi.mock('./MobileEmulatorExamples', () => ({ MobileEmulatorExamples: () => null }))
|
||||
|
||||
describe('MobileEmulatorAgentControlRow freshness authority', () => {
|
||||
@@ -56,4 +58,11 @@ describe('MobileEmulatorAgentControlRow freshness authority', () => {
|
||||
renderToStaticMarkup(<MobileEmulatorAgentControlRow />)
|
||||
expect(mocks.freshnessSkillName).toBeUndefined()
|
||||
})
|
||||
|
||||
it('installs the skill without registering the CLI first', () => {
|
||||
renderToStaticMarkup(<MobileEmulatorAgentControlRow />)
|
||||
expect(mocks.panelProps.onBeforeOpenTerminal).toBeUndefined()
|
||||
expect(mocks.panelProps.preInstallNotice).toBeUndefined()
|
||||
expect(mocks.panelProps.installDisabled).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,22 +1,14 @@
|
||||
import { Import, Loader2 } from 'lucide-react'
|
||||
import { Import } from 'lucide-react'
|
||||
import {
|
||||
ORCA_CLI_SKILL_INSTALL_COMMAND,
|
||||
ORCA_CLI_SKILL_NAME,
|
||||
ORCA_CLI_SKILL_UPDATE_COMMAND
|
||||
} from '@/lib/agent-feature-install-commands'
|
||||
import {
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime'
|
||||
import { useMobileEmulatorAgentSetupState } from '../emulator-pane/use-mobile-emulator-agent-setup-state'
|
||||
import { AgentSkillSetupPanel } from './AgentSkillSetupPanel'
|
||||
import { buildSkillCommandForRuntime } from './CliSkillRuntimeSetup'
|
||||
import { StepBadge } from './SetupStepBadge'
|
||||
import { MobileEmulatorExamples } from './MobileEmulatorExamples'
|
||||
import { Button } from '../ui/button'
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
const EMULATOR_CLI_COMMANDS = [
|
||||
@@ -34,10 +26,6 @@ export function MobileEmulatorAgentControlRow(): React.JSX.Element {
|
||||
const cliSkillInstallCommand = buildSkillCommandForRuntime(ORCA_CLI_SKILL_INSTALL_COMMAND)
|
||||
const cliSkillUpdateCommand = buildSkillCommandForRuntime(ORCA_CLI_SKILL_UPDATE_COMMAND)
|
||||
|
||||
const handleEnableCli = async (): Promise<void> => {
|
||||
await setup.handleEnableCli()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-2xl border border-border/60 bg-card/30 p-4">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
@@ -55,98 +43,10 @@ export function MobileEmulatorAgentControlRow(): React.JSX.Element {
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
className={`shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium ${
|
||||
setup.completedCount === 2
|
||||
? 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-400'
|
||||
: 'bg-muted text-muted-foreground'
|
||||
}`}
|
||||
>
|
||||
{setup.completedCount}/2
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 divide-y divide-border/40">
|
||||
<div className="flex items-start gap-3 py-3">
|
||||
<StepBadge
|
||||
index={1}
|
||||
state={setup.cliEnabled ? 'done' : setup.cliBusy ? 'in-progress' : 'pending'}
|
||||
/>
|
||||
<div className="min-w-0 flex-1 space-y-1">
|
||||
<p className="text-sm font-medium">
|
||||
{translate(
|
||||
'auto.components.settings.MobileEmulatorAgentControlRow.4f2205f3b6',
|
||||
'Enable Orca CLI'
|
||||
)}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.settings.MobileEmulatorAgentControlRow.2fef055608',
|
||||
'Registers the Orca CLI command so agents can control the active emulator from their shell.'
|
||||
)}
|
||||
</p>
|
||||
{setup.cliInstallStatus?.commandPath && setup.cliEnabled ? (
|
||||
<p className="text-[11px] text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.settings.MobileEmulatorAgentControlRow.aaf62a3dd2',
|
||||
'Installed at'
|
||||
)}{' '}
|
||||
<code className="rounded bg-muted px-1 py-0.5">
|
||||
{setup.cliInstallStatus.commandPath}
|
||||
</code>
|
||||
</p>
|
||||
) : null}
|
||||
{!setup.cliEnabled && setup.cliInstallStatus?.detail ? (
|
||||
<p className="text-[11px] text-muted-foreground">{setup.cliInstallStatus.detail}</p>
|
||||
) : null}
|
||||
{setup.cliBusy ? (
|
||||
<p className="text-[11px] leading-snug text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.settings.MobileEmulatorAgentControlRow.3d34423e88',
|
||||
'Registering the Orca CLI'
|
||||
)}{' '}
|
||||
{setup.cliInstallStatus?.commandPath ? (
|
||||
<code className="rounded bg-muted px-1 py-0.5">
|
||||
{setup.cliInstallStatus.commandPath}
|
||||
</code>
|
||||
) : null}{' '}
|
||||
{translate(
|
||||
'auto.components.settings.MobileEmulatorAgentControlRow.3be27641c9',
|
||||
'so emulator commands can run from agent shells.'
|
||||
)}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<TooltipProvider delayDuration={250}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant={setup.cliEnabled ? 'outline' : 'default'}
|
||||
disabled={
|
||||
setup.cliLoading || setup.cliBusy || !setup.cliSupported || setup.cliEnabled
|
||||
}
|
||||
onClick={() => void handleEnableCli()}
|
||||
>
|
||||
{setup.cliLoading || setup.cliBusy ? (
|
||||
<Loader2 className="size-3.5 animate-spin" />
|
||||
) : null}
|
||||
{setup.cliActionLabel}
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
{!setup.cliSupported && !setup.cliLoading && setup.cliInstallStatus?.detail ? (
|
||||
<TooltipContent side="left" sideOffset={6}>
|
||||
{setup.cliInstallStatus.detail}
|
||||
</TooltipContent>
|
||||
) : null}
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
|
||||
<div className={cn('py-3', setup.step2Blocked && 'opacity-60')}>
|
||||
<div className="py-3">
|
||||
<AgentSkillSetupPanel
|
||||
variant="inline"
|
||||
title={translate(
|
||||
@@ -166,16 +66,6 @@ export function MobileEmulatorAgentControlRow(): React.JSX.Element {
|
||||
installed={setup.cliSkillInstalled}
|
||||
loading={setup.cliSkillLoading}
|
||||
error={setup.cliSkillError}
|
||||
installDisabled={setup.step2Blocked}
|
||||
leading={<StepBadge index={2} state={setup.cliSkillInstalled ? 'done' : 'pending'} />}
|
||||
preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE}
|
||||
openingHint={translate(
|
||||
'auto.components.settings.MobileEmulatorAgentControlRow.3941719a56',
|
||||
'Checking Orca CLI before opening skill setup.'
|
||||
)}
|
||||
onBeforeOpenTerminal={async () => {
|
||||
await ensureOrcaCliAvailableForAgentSkillTerminal()
|
||||
}}
|
||||
onRecheck={setup.refreshCliSkill}
|
||||
freshnessSkillName={
|
||||
activeSkillRuntime.canUseLocalSkillFreshness ? ORCA_CLI_SKILL_NAME : undefined
|
||||
|
||||
@@ -2,10 +2,6 @@ import { useState } from 'react'
|
||||
import { ArrowRightLeft, GitBranch, ListChecks, Workflow, type LucideIcon } from 'lucide-react'
|
||||
import { ORCHESTRATION_SKILL_NAME } from '@/lib/agent-feature-install-commands'
|
||||
import type { SkillUsageExample } from '@/lib/skill-usage-example'
|
||||
import {
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import {
|
||||
ORCHESTRATION_SKILL_INSTALL_COMMAND,
|
||||
ORCHESTRATION_SKILL_UPDATE_COMMAND
|
||||
@@ -21,11 +17,7 @@ import { matchesSettingsSearch } from './settings-search'
|
||||
import { useAppStore } from '../../store'
|
||||
import { getOrchestrationPaneSearchEntries } from './orchestration-search'
|
||||
import { AgentSkillSetupPanel } from './AgentSkillSetupPanel'
|
||||
import {
|
||||
buildSkillCommandForRuntime,
|
||||
ensureWslCliAvailableForAgentSkillTerminal,
|
||||
getWslCliDistroRequest
|
||||
} from './CliSkillRuntimeSetup'
|
||||
import { buildSkillCommandForRuntime } from './CliSkillRuntimeSetup'
|
||||
import { OrchestrationSkillAgentCoverage } from './OrchestrationSkillAgentCoverage'
|
||||
import { SkillUsageExamplesSection } from './SkillUsageExamplesSection'
|
||||
import { OrchestrationSkillPromptDialog } from './OrchestrationSkillPromptDialog'
|
||||
@@ -132,19 +124,8 @@ export function OrchestrationPane({
|
||||
error={activeSkillRuntime.installDisabledReason ?? orchestrationSkillError}
|
||||
installDisabled={Boolean(activeSkillRuntime.installDisabledReason)}
|
||||
icon={<Workflow className="size-5" />}
|
||||
preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE}
|
||||
getPrerequisiteStatus={() =>
|
||||
activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? window.api.cli.getWslInstallStatus(
|
||||
getWslCliDistroRequest(activeSkillRuntime.agentRuntime)
|
||||
)
|
||||
: window.api.cli.getInstallStatus()
|
||||
}
|
||||
onBeforeOpenTerminal={async () => {
|
||||
onBeforeOpenTerminal={() => {
|
||||
useAppStore.getState().recordFeatureInteraction('agent-orchestration-setup')
|
||||
await (activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime)
|
||||
: ensureOrcaCliAvailableForAgentSkillTerminal())
|
||||
}}
|
||||
actionHint={
|
||||
// Installed updates stay on the primary panel so there is only one update path.
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import type { JSX } from 'react'
|
||||
import {
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import {
|
||||
ORCHESTRATION_SKILL_INSTALL_COMMAND,
|
||||
ORCHESTRATION_SKILL_UPDATE_COMMAND
|
||||
@@ -11,11 +7,7 @@ import { ORCHESTRATION_SKILL_NAME } from '@/lib/agent-feature-install-commands'
|
||||
import type { InstalledAgentSkillState } from '@/hooks/useInstalledAgentSkills'
|
||||
import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime'
|
||||
import { AgentSkillSetupPanel } from './AgentSkillSetupPanel'
|
||||
import {
|
||||
buildSkillCommandForRuntime,
|
||||
ensureWslCliAvailableForAgentSkillTerminal,
|
||||
getWslCliDistroRequest
|
||||
} from './CliSkillRuntimeSetup'
|
||||
import { buildSkillCommandForRuntime } from './CliSkillRuntimeSetup'
|
||||
import { useAppStore } from '@/store'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
@@ -62,19 +54,8 @@ export function OrchestrationSetupCard(props: {
|
||||
error={activeSkillRuntime.installDisabledReason ?? skill.error}
|
||||
installDisabled={Boolean(activeSkillRuntime.installDisabledReason)}
|
||||
terminalHeightPx={terminalHeightPx}
|
||||
preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE}
|
||||
getPrerequisiteStatus={() =>
|
||||
activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? window.api.cli.getWslInstallStatus(
|
||||
getWslCliDistroRequest(activeSkillRuntime.agentRuntime)
|
||||
)
|
||||
: window.api.cli.getInstallStatus()
|
||||
}
|
||||
onBeforeOpenTerminal={async () => {
|
||||
onBeforeOpenTerminal={() => {
|
||||
useAppStore.getState().recordFeatureInteraction('agent-orchestration-setup')
|
||||
await (activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime)
|
||||
: ensureOrcaCliAvailableForAgentSkillTerminal())
|
||||
}}
|
||||
onRecheck={skill.refresh}
|
||||
freshnessSkillName={
|
||||
|
||||
@@ -142,9 +142,6 @@ export function TaskSourceLinearSetup({
|
||||
loading={skillSetup.skillLoading}
|
||||
error={skillSetup.error}
|
||||
installDisabled={skillSetup.installDisabled}
|
||||
preInstallNotice={skillSetup.preInstallNotice}
|
||||
getPrerequisiteStatus={skillSetup.getPrerequisiteStatus}
|
||||
onBeforeOpenTerminal={skillSetup.onBeforeOpenTerminal}
|
||||
onRecheck={skillSetup.refreshSkill}
|
||||
freshnessSkillName={skillSetup.freshnessSkillName}
|
||||
/>
|
||||
|
||||
@@ -3,29 +3,6 @@ import { translateSearchKeyword } from './settings-search-keywords'
|
||||
import { createLocalizedCatalog } from '@/i18n/localized-catalog'
|
||||
|
||||
export const getBrowserUsePaneSearchEntries = createLocalizedCatalog(() => [
|
||||
{
|
||||
title: translate('auto.components.settings.browser.use.search.50f0860e18', 'Enable Orca CLI'),
|
||||
description: translate(
|
||||
'auto.components.settings.browser.use.search.890ddf943d',
|
||||
'Register the Orca CLI so agents can drive the browser.'
|
||||
),
|
||||
keywords: [
|
||||
...translateSearchKeyword(
|
||||
'auto.components.settings.browser.use.search.ba4eb53b72',
|
||||
'browser use'
|
||||
),
|
||||
...translateSearchKeyword('auto.components.settings.browser.use.search.85fab5e12c', 'cli'),
|
||||
...translateSearchKeyword('auto.components.settings.browser.use.search.ff05cbc344', 'orca'),
|
||||
...translateSearchKeyword('auto.components.settings.browser.use.search.30c74aaa1f', 'path'),
|
||||
...translateSearchKeyword(
|
||||
'auto.components.settings.browser.use.search.3ffafc9b95',
|
||||
'command'
|
||||
),
|
||||
...translateSearchKeyword('auto.components.settings.browser.use.search.7e0dcb257a', 'shell'),
|
||||
...translateSearchKeyword('auto.components.settings.browser.use.search.034c5e8d7f', 'enable'),
|
||||
...translateSearchKeyword('auto.components.settings.browser.use.search.e56c7b55c9', 'setup')
|
||||
]
|
||||
},
|
||||
{
|
||||
title: translate(
|
||||
'auto.components.settings.browser.use.search.a1414dcefb',
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import {
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import { useMemo } from 'react'
|
||||
import {
|
||||
LINEAR_AGENT_SKILL_NAMES,
|
||||
ORCA_LINEAR_SKILL_INSTALL_COMMAND
|
||||
@@ -13,12 +9,7 @@ import {
|
||||
useInstalledAgentSkillNames
|
||||
} from '@/hooks/useInstalledAgentSkills'
|
||||
import { useActiveProjectSkillRuntime } from '@/hooks/useActiveProjectSkillRuntime'
|
||||
import {
|
||||
buildSkillCommandForRuntime,
|
||||
ensureWslCliAvailableForAgentSkillTerminal,
|
||||
getWslCliDistroRequest,
|
||||
type LocalAgentRuntime
|
||||
} from './CliSkillRuntimeSetup'
|
||||
import { buildSkillCommandForRuntime, type LocalAgentRuntime } from './CliSkillRuntimeSetup'
|
||||
|
||||
// Shared install/update wiring for Task Sources + Linear settings.
|
||||
export function useLinearAgentSkillSetup(): {
|
||||
@@ -36,10 +27,7 @@ export function useLinearAgentSkillSetup(): {
|
||||
error: string | null
|
||||
terminalShellOverride: string | undefined
|
||||
terminalRuntime: LocalAgentRuntime | undefined
|
||||
preInstallNotice: string
|
||||
refreshSkill: () => Promise<boolean>
|
||||
getPrerequisiteStatus: () => Promise<Awaited<ReturnType<typeof window.api.cli.getInstallStatus>>>
|
||||
onBeforeOpenTerminal: () => Promise<void>
|
||||
} {
|
||||
const activeSkillRuntime = useActiveProjectSkillRuntime()
|
||||
const {
|
||||
@@ -76,22 +64,6 @@ export function useLinearAgentSkillSetup(): {
|
||||
? updateTarget.skillName
|
||||
: undefined
|
||||
|
||||
const getPrerequisiteStatus = useCallback(
|
||||
() =>
|
||||
activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? window.api.cli.getWslInstallStatus(
|
||||
getWslCliDistroRequest(activeSkillRuntime.agentRuntime)
|
||||
)
|
||||
: window.api.cli.getInstallStatus(),
|
||||
[activeSkillRuntime.agentRuntime]
|
||||
)
|
||||
|
||||
const onBeforeOpenTerminal = useCallback(async () => {
|
||||
await (activeSkillRuntime.agentRuntime?.runtime === 'wsl'
|
||||
? ensureWslCliAvailableForAgentSkillTerminal(activeSkillRuntime.agentRuntime)
|
||||
: ensureOrcaCliAvailableForAgentSkillTerminal())
|
||||
}, [activeSkillRuntime.agentRuntime])
|
||||
|
||||
const installDisabled = Boolean(activeSkillRuntime.installDisabledReason)
|
||||
|
||||
return {
|
||||
@@ -106,9 +78,6 @@ export function useLinearAgentSkillSetup(): {
|
||||
error: activeSkillRuntime.installDisabledReason ?? skillError,
|
||||
terminalShellOverride: activeSkillRuntime.terminalShellOverride,
|
||||
terminalRuntime: activeSkillRuntime.agentRuntime,
|
||||
preInstallNotice: AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
refreshSkill,
|
||||
getPrerequisiteStatus,
|
||||
onBeforeOpenTerminal
|
||||
refreshSkill
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useCallback, useEffect, useMemo, useState, type JSX } from 'react'
|
||||
import { EyeOff } from 'lucide-react'
|
||||
import {
|
||||
FEATURE_WALL_SETUP_STEP_IDS,
|
||||
getFirstIncompleteFeatureWallSetupStepId,
|
||||
@@ -14,10 +13,8 @@ import {
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { useAppStore } from '@/store'
|
||||
import { FeatureWallSetupChecklist } from '../feature-wall/FeatureWallSetupChecklist'
|
||||
import { SetupGuideProgressRing } from './SetupGuideProgressRing'
|
||||
import { useSetupGuideProgress } from './use-setup-guide-progress'
|
||||
import { useSetupGuideOpenCloseTelemetry } from './use-setup-guide-telemetry'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
@@ -141,44 +138,24 @@ function SetupGuideModalContent({
|
||||
className="grid h-[min(780px,calc(100vh-2rem))] w-[min(1080px,calc(100vw-2rem))] max-w-none grid-rows-[auto_minmax(0,1fr)] gap-0 p-0 sm:max-w-none"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
aria-label={translate(
|
||||
'auto.components.setup.guide.SetupGuideModal.f3b5ffb2a6',
|
||||
'Hide checklist from sidebar'
|
||||
)}
|
||||
onClick={handleHideFromSidebar}
|
||||
className="absolute right-10 top-3.5 text-muted-foreground"
|
||||
>
|
||||
<EyeOff className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" sideOffset={4}>
|
||||
{translate(
|
||||
'auto.components.setup.guide.SetupGuideModal.28cf59fcb4',
|
||||
'This will hide the checklist from the sidebar'
|
||||
)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
{/* Why: labeled so it can't be mistaken for close; hidden below sm, where the header
|
||||
centers the title under it (the sidebar entry keeps its own hide control). */}
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
onClick={handleHideFromSidebar}
|
||||
className="absolute right-11 top-3.5 hidden sm:inline-flex"
|
||||
>
|
||||
{translate(
|
||||
'auto.components.setup.guide.SetupGuideModal.f3b5ffb2a6',
|
||||
'Hide checklist from sidebar'
|
||||
)}
|
||||
</Button>
|
||||
<DialogHeader className="gap-1 border-b border-border px-7 py-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<DialogTitle className="text-lg">
|
||||
{translate(
|
||||
'auto.components.setup.guide.SetupGuideModal.48a9e5ef2d',
|
||||
'Getting started'
|
||||
)}
|
||||
</DialogTitle>
|
||||
<SetupGuideProgressRing
|
||||
done={progress.coreDoneCount}
|
||||
total={progress.coreTotal}
|
||||
className="text-green-600 dark:text-green-300"
|
||||
sizeClassName="size-5"
|
||||
/>
|
||||
</div>
|
||||
<DialogTitle className="text-lg">
|
||||
{translate('auto.components.setup.guide.SetupGuideModal.48a9e5ef2d', 'Getting started')}
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-sm text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.setup.guide.SetupGuideModal.3598a3ca0c',
|
||||
|
||||
@@ -12,10 +12,6 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog'
|
||||
import {
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
isOrcaCliAvailableOnPath
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
type AgentSkillSetupPanelProps = ComponentProps<typeof AgentSkillSetupPanel>
|
||||
@@ -32,8 +28,6 @@ type LinearAgentSkillSetupDialogProps = {
|
||||
installed: boolean
|
||||
loading: boolean
|
||||
error: string | null
|
||||
getPrerequisiteStatus?: AgentSkillSetupPanelProps['getPrerequisiteStatus']
|
||||
onBeforeOpenTerminal: AgentSkillSetupPanelProps['onBeforeOpenTerminal']
|
||||
onRecheck: AgentSkillSetupPanelProps['onRecheck']
|
||||
onOpenChange: (open: boolean) => void
|
||||
onDismissPermanently: () => void
|
||||
@@ -52,8 +46,6 @@ export function LinearAgentSkillSetupDialog({
|
||||
installed,
|
||||
loading,
|
||||
error,
|
||||
getPrerequisiteStatus,
|
||||
onBeforeOpenTerminal,
|
||||
onRecheck,
|
||||
onOpenChange,
|
||||
onDismissPermanently,
|
||||
@@ -144,16 +136,12 @@ export function LinearAgentSkillSetupDialog({
|
||||
loading={loading}
|
||||
error={error}
|
||||
installLabel={translate(
|
||||
'auto.components.sidebar.LinearAgentSkillSetupPrompt.install',
|
||||
'Install CLI & Skill'
|
||||
'auto.components.skills.SkillInstallDialog.39acb9e8f4',
|
||||
'Install skill'
|
||||
)}
|
||||
// Why: Install is this modal's sole CTA, so make it the filled primary —
|
||||
// matching the other setup surfaces (filled primary + muted dismiss).
|
||||
installVariant="default"
|
||||
preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE}
|
||||
getPrerequisiteStatus={getPrerequisiteStatus}
|
||||
isPrerequisiteAvailable={isOrcaCliAvailableOnPath}
|
||||
onBeforeOpenTerminal={onBeforeOpenTerminal}
|
||||
onRecheck={onRecheck}
|
||||
/>
|
||||
{/* Why: permanent opt-out as a quiet EyeOff icon next to the × — matching
|
||||
|
||||
+37
-73
@@ -3,7 +3,6 @@
|
||||
import { act, type ComponentProps, type ReactNode } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { toast } from 'sonner'
|
||||
import type { CliInstallStatus } from '../../../../shared/cli-install-types'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { LINEAR_AGENT_SKILL_NAMES } from '@/lib/agent-feature-install-commands'
|
||||
import {
|
||||
@@ -18,6 +17,19 @@ import { getExistingLinearAgentSkillSetupReminderState } from './linear-agent-sk
|
||||
|
||||
const HOST_DISMISS_STORAGE_KEY = 'orca.linearTicketsSkill.setupDismissed.host'
|
||||
|
||||
const wslFedoraProps = {
|
||||
linked: true,
|
||||
remote: false,
|
||||
surface: 'modal',
|
||||
currentPlatform: 'win32',
|
||||
settings: {
|
||||
localAgentRuntime: 'wsl',
|
||||
localAgentWslDistro: 'Fedora',
|
||||
terminalWindowsShell: 'wsl.exe',
|
||||
activeRuntimeEnvironmentId: null
|
||||
}
|
||||
} satisfies ComponentProps<typeof LinearAgentSkillSetupPrompt>
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
skillState: {
|
||||
installed: false,
|
||||
@@ -29,8 +41,6 @@ const mocks = vi.hoisted(() => ({
|
||||
useInstalledAgentSkillNames: vi.fn(),
|
||||
getCliStatus: vi.fn(),
|
||||
getWslCliStatus: vi.fn(),
|
||||
ensureCli: vi.fn(async () => null as CliInstallStatus | null),
|
||||
ensureWslCli: vi.fn(async () => null as CliInstallStatus | null),
|
||||
toastDismiss: vi.fn(),
|
||||
toastWarning: vi.fn(() => 'linear-setup-toast-id'),
|
||||
panelProps: [] as Record<string, unknown>[]
|
||||
@@ -48,13 +58,6 @@ vi.mock('@/hooks/useInstalledAgentSkills', async (importOriginal) => ({
|
||||
useInstalledAgentSkillNames: mocks.useInstalledAgentSkillNames
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/agent-skill-cli-prerequisite', () => ({
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE: 'CLI registration notice',
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal: mocks.ensureCli,
|
||||
isOrcaCliAvailableOnPath: (status: CliInstallStatus | null | undefined) =>
|
||||
status?.state === 'installed' && status.pathConfigured
|
||||
}))
|
||||
|
||||
vi.mock('../settings/CliSkillRuntimeSetup', () => ({
|
||||
buildSkillCommandForRuntime: (
|
||||
command: string,
|
||||
@@ -62,12 +65,7 @@ vi.mock('../settings/CliSkillRuntimeSetup', () => ({
|
||||
) =>
|
||||
runtime.runtime === 'wsl'
|
||||
? `wsl.exe${runtime.wslDistro ? ` -d '${runtime.wslDistro}'` : ''} --exec bash -lc '${command}'`
|
||||
: command,
|
||||
ensureWslCliAvailableForAgentSkillTerminal: mocks.ensureWslCli,
|
||||
getWslCliDistroRequest: (runtime?: { runtime: string; wslDistro?: string | null }) =>
|
||||
runtime?.runtime === 'wsl' && runtime.wslDistro?.trim()
|
||||
? { distro: runtime.wslDistro.trim() }
|
||||
: undefined
|
||||
: command
|
||||
}))
|
||||
|
||||
vi.mock('../settings/AgentSkillSetupPanel', () => ({
|
||||
@@ -78,7 +76,14 @@ vi.mock('../settings/AgentSkillSetupPanel', () => ({
|
||||
<h2>{String(props.title)}</h2>
|
||||
<p>{String(props.description)}</p>
|
||||
<code>{String(props.command)}</code>
|
||||
<button type="button" onClick={() => void (props.onBeforeOpenTerminal as () => void)()}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (typeof props.onBeforeOpenTerminal === 'function') {
|
||||
void props.onBeforeOpenTerminal()
|
||||
}
|
||||
}}
|
||||
>
|
||||
Mock install
|
||||
</button>
|
||||
<button type="button" onClick={() => void (props.onRecheck as () => void)()}>
|
||||
@@ -92,24 +97,6 @@ vi.mock('../settings/AgentSkillSetupPanel', () => ({
|
||||
let root: Root | null = null
|
||||
let container: HTMLDivElement | null = null
|
||||
|
||||
function cliStatus(overrides: Partial<CliInstallStatus>): CliInstallStatus {
|
||||
return {
|
||||
platform: 'darwin',
|
||||
commandName: 'orca',
|
||||
commandPath: '/usr/local/bin/orca',
|
||||
pathDirectory: '/usr/local/bin',
|
||||
pathConfigured: true,
|
||||
launcherPath: '/Applications/Orca.app/Contents/MacOS/Orca',
|
||||
installMethod: 'symlink',
|
||||
supported: true,
|
||||
state: 'installed',
|
||||
currentTarget: '/Applications/Orca.app/Contents/MacOS/Orca',
|
||||
unsupportedReason: null,
|
||||
detail: null,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
async function renderPrompt(
|
||||
props: ComponentProps<typeof LinearAgentSkillSetupPrompt>
|
||||
): Promise<void> {
|
||||
@@ -173,15 +160,7 @@ describe('LinearAgentSkillSetupPrompt reminder toast', () => {
|
||||
mocks.useInstalledAgentSkillNames.mockReset()
|
||||
mocks.useInstalledAgentSkillNames.mockReturnValue(mocks.skillState)
|
||||
mocks.getCliStatus.mockReset()
|
||||
mocks.getCliStatus.mockResolvedValue(
|
||||
cliStatus({ state: 'not_installed', pathConfigured: false })
|
||||
)
|
||||
mocks.getWslCliStatus.mockReset()
|
||||
mocks.getWslCliStatus.mockResolvedValue(
|
||||
cliStatus({ state: 'not_installed', pathConfigured: false })
|
||||
)
|
||||
mocks.ensureCli.mockClear()
|
||||
mocks.ensureWslCli.mockClear()
|
||||
mocks.toastDismiss.mockClear()
|
||||
mocks.toastWarning.mockClear()
|
||||
mocks.toastWarning.mockReturnValue('linear-setup-toast-id')
|
||||
@@ -217,12 +196,15 @@ describe('LinearAgentSkillSetupPrompt reminder toast', () => {
|
||||
expect(document.body.textContent).not.toContain(
|
||||
'Enable agents to read and edit the attached Linear ticket.'
|
||||
)
|
||||
// Why: host terminals already have the bundled CLI, so host reminders name only the skill.
|
||||
expect(mocks.getCliStatus).not.toHaveBeenCalled()
|
||||
expect(mocks.getWslCliStatus).not.toHaveBeenCalled()
|
||||
expect(toast.warning).toHaveBeenCalledWith(
|
||||
'Orca CLI and Linear skill are missing',
|
||||
'Linear skill is missing',
|
||||
expect.objectContaining({
|
||||
id: 'linear-agent-skill-setup-orca.linearTicketsSkill.setupDismissed.host',
|
||||
description:
|
||||
'Install the Orca CLI and the Linear skill to enable your agents to read and edit Linear tasks.',
|
||||
'Install the Linear skill to enable your agents to read and edit Linear tasks through the Orca CLI.',
|
||||
action: {
|
||||
label: 'Set up',
|
||||
onClick: expect.any(Function)
|
||||
@@ -231,17 +213,11 @@ describe('LinearAgentSkillSetupPrompt reminder toast', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('does not repeat the Orca CLI in CLI-only reminder toast copy', async () => {
|
||||
it('does not remind WSL users about CLI registration when skills are installed', async () => {
|
||||
mocks.skillState.installed = true
|
||||
await snoozeInitialModal({ linked: true, remote: false, surface: 'modal' })
|
||||
await renderPrompt({ linked: true, remote: false, surface: 'modal' })
|
||||
|
||||
expect(toast.warning).toHaveBeenCalledWith(
|
||||
'Orca CLI is missing',
|
||||
expect.objectContaining({
|
||||
description: 'Install the Orca CLI to enable your agents to read and edit Linear tasks.'
|
||||
})
|
||||
)
|
||||
await renderPrompt(wslFedoraProps)
|
||||
expect(toast.warning).not.toHaveBeenCalled()
|
||||
expect(mocks.getWslCliStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps remote setup nuance in reminder toast copy', async () => {
|
||||
@@ -249,35 +225,23 @@ describe('LinearAgentSkillSetupPrompt reminder toast', () => {
|
||||
await renderPrompt({ linked: true, remote: true, surface: 'modal' })
|
||||
|
||||
expect(toast.warning).toHaveBeenCalledWith(
|
||||
'Orca CLI and Linear skill are missing',
|
||||
'Linear skill is missing',
|
||||
expect.objectContaining({
|
||||
description:
|
||||
'Install the Orca CLI and the Linear skill to enable your agents to read and edit Linear tasks. Remote agent environments may need their own setup.'
|
||||
'Install the Linear skill to enable your agents to read and edit Linear tasks through the Orca CLI. Remote agent environments may need their own setup.'
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('keeps WSL target nuance in reminder toast copy', async () => {
|
||||
const wslProps = {
|
||||
linked: true,
|
||||
remote: false,
|
||||
surface: 'modal',
|
||||
currentPlatform: 'win32',
|
||||
settings: {
|
||||
localAgentRuntime: 'wsl',
|
||||
localAgentWslDistro: 'Fedora',
|
||||
terminalWindowsShell: 'wsl.exe',
|
||||
activeRuntimeEnvironmentId: null
|
||||
}
|
||||
} satisfies ComponentProps<typeof LinearAgentSkillSetupPrompt>
|
||||
await snoozeInitialModal(wslProps)
|
||||
await renderPrompt(wslProps)
|
||||
await snoozeInitialModal(wslFedoraProps)
|
||||
await renderPrompt(wslFedoraProps)
|
||||
|
||||
expect(toast.warning).toHaveBeenCalledWith(
|
||||
'Orca CLI and Linear skill are missing',
|
||||
'Linear skill is missing',
|
||||
expect.objectContaining({
|
||||
description:
|
||||
'Install the Orca CLI and the Linear skill to enable your agents to read and edit Linear tasks. This setup runs in the selected WSL agent runtime.'
|
||||
'Install the Linear skill to enable your agents to read and edit Linear tasks through the Orca CLI. This setup runs in the selected WSL agent runtime.'
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { act, type ComponentProps, type ReactNode } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import type { CliInstallStatus } from '../../../../shared/cli-install-types'
|
||||
import type { ProjectExecutionRuntimeResolution } from '../../../../shared/project-execution-runtime'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { LINEAR_AGENT_SKILL_NAMES } from '@/lib/agent-feature-install-commands'
|
||||
@@ -10,6 +9,7 @@ import {
|
||||
LinearAgentSkillSetupPrompt,
|
||||
_linearAgentSkillSetupPromptInternalsForTests
|
||||
} from './LinearAgentSkillSetupPrompt'
|
||||
import type { LinearAgentSkillPromptSettings } from './linear-agent-skill-runtime'
|
||||
|
||||
const HOST_DISMISS_STORAGE_KEY = 'orca.linearTicketsSkill.setupDismissed.host'
|
||||
const FEDORA_DISMISS_STORAGE_KEY = 'orca.linearTicketsSkill.setupDismissed.wsl.Fedora'
|
||||
@@ -48,8 +48,6 @@ const mocks = vi.hoisted(() => ({
|
||||
useInstalledAgentSkillNames: vi.fn(),
|
||||
getCliStatus: vi.fn(),
|
||||
getWslCliStatus: vi.fn(),
|
||||
ensureCli: vi.fn(async () => null as CliInstallStatus | null),
|
||||
ensureWslCli: vi.fn(async () => null as CliInstallStatus | null),
|
||||
panelProps: [] as Record<string, unknown>[]
|
||||
}))
|
||||
|
||||
@@ -58,23 +56,11 @@ vi.mock('@/hooks/useInstalledAgentSkills', async (importOriginal) => ({
|
||||
useInstalledAgentSkillNames: mocks.useInstalledAgentSkillNames
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/agent-skill-cli-prerequisite', () => ({
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE: 'CLI registration notice',
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal: mocks.ensureCli,
|
||||
isOrcaCliAvailableOnPath: (status: CliInstallStatus | null | undefined) =>
|
||||
status?.state === 'installed' && status.pathConfigured
|
||||
}))
|
||||
|
||||
vi.mock('../settings/CliSkillRuntimeSetup', () => ({
|
||||
buildSkillCommandForRuntime: (
|
||||
command: string,
|
||||
_runtime: { runtime: string; wslDistro?: string | null }
|
||||
) => command,
|
||||
ensureWslCliAvailableForAgentSkillTerminal: mocks.ensureWslCli,
|
||||
getWslCliDistroRequest: (runtime?: { runtime: string; wslDistro?: string | null }) =>
|
||||
runtime?.runtime === 'wsl' && runtime.wslDistro?.trim()
|
||||
? { distro: runtime.wslDistro.trim() }
|
||||
: undefined
|
||||
) => command
|
||||
}))
|
||||
|
||||
vi.mock('../settings/AgentSkillSetupPanel', () => ({
|
||||
@@ -85,7 +71,14 @@ vi.mock('../settings/AgentSkillSetupPanel', () => ({
|
||||
<h2>{String(props.title)}</h2>
|
||||
<p>{String(props.description)}</p>
|
||||
<code>{String(props.command)}</code>
|
||||
<button type="button" onClick={() => void (props.onBeforeOpenTerminal as () => void)()}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (typeof props.onBeforeOpenTerminal === 'function') {
|
||||
void props.onBeforeOpenTerminal()
|
||||
}
|
||||
}}
|
||||
>
|
||||
Mock install
|
||||
</button>
|
||||
<button
|
||||
@@ -101,6 +94,33 @@ vi.mock('../settings/AgentSkillSetupPanel', () => ({
|
||||
}
|
||||
}))
|
||||
|
||||
function wslSettings(distro: string): LinearAgentSkillPromptSettings {
|
||||
return {
|
||||
localAgentRuntime: 'wsl',
|
||||
localAgentWslDistro: distro,
|
||||
terminalWindowsShell: 'wsl.exe',
|
||||
activeRuntimeEnvironmentId: null
|
||||
}
|
||||
}
|
||||
|
||||
function wslPromptProps(
|
||||
distro: string,
|
||||
surface: 'inline' | 'modal' = 'inline'
|
||||
): ComponentProps<typeof LinearAgentSkillSetupPrompt> {
|
||||
return {
|
||||
linked: true,
|
||||
remote: false,
|
||||
surface,
|
||||
currentPlatform: 'win32',
|
||||
settings: wslSettings(distro)
|
||||
}
|
||||
}
|
||||
|
||||
function expectNoCliStatusQuery(): void {
|
||||
expect(mocks.getCliStatus).not.toHaveBeenCalled()
|
||||
expect(mocks.getWslCliStatus).not.toHaveBeenCalled()
|
||||
}
|
||||
|
||||
let root: Root | null = null
|
||||
let container: HTMLDivElement | null = null
|
||||
|
||||
@@ -117,24 +137,6 @@ function installLocalStorageShim(): void {
|
||||
})
|
||||
}
|
||||
|
||||
function cliStatus(overrides: Partial<CliInstallStatus>): CliInstallStatus {
|
||||
return {
|
||||
platform: 'darwin',
|
||||
commandName: 'orca',
|
||||
commandPath: '/usr/local/bin/orca',
|
||||
pathDirectory: '/usr/local/bin',
|
||||
pathConfigured: true,
|
||||
launcherPath: '/Applications/Orca.app/Contents/MacOS/Orca',
|
||||
installMethod: 'symlink',
|
||||
supported: true,
|
||||
state: 'installed',
|
||||
currentTarget: '/Applications/Orca.app/Contents/MacOS/Orca',
|
||||
unsupportedReason: null,
|
||||
detail: null,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
async function renderPrompt(
|
||||
props: ComponentProps<typeof LinearAgentSkillSetupPrompt>
|
||||
): Promise<HTMLDivElement> {
|
||||
@@ -186,7 +188,6 @@ async function settleRender(): Promise<void> {
|
||||
async function showSuccessfulModalRecheck(): Promise<void> {
|
||||
await renderPrompt({ linked: true, remote: false, surface: 'modal' })
|
||||
|
||||
mocks.getCliStatus.mockResolvedValue(cliStatus({}))
|
||||
mocks.skillState.refresh.mockImplementationOnce(async () => {
|
||||
mocks.skillState.installed = true
|
||||
})
|
||||
@@ -204,15 +205,7 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
mocks.useInstalledAgentSkillNames.mockReset()
|
||||
mocks.useInstalledAgentSkillNames.mockReturnValue(mocks.skillState)
|
||||
mocks.getCliStatus.mockReset()
|
||||
mocks.getCliStatus.mockResolvedValue(
|
||||
cliStatus({ state: 'not_installed', pathConfigured: false })
|
||||
)
|
||||
mocks.getWslCliStatus.mockReset()
|
||||
mocks.getWslCliStatus.mockResolvedValue(
|
||||
cliStatus({ state: 'not_installed', pathConfigured: false })
|
||||
)
|
||||
mocks.ensureCli.mockClear()
|
||||
mocks.ensureWslCli.mockClear()
|
||||
mocks.panelProps.length = 0
|
||||
installLocalStorageShim()
|
||||
window.localStorage.clear()
|
||||
@@ -235,12 +228,14 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
Reflect.deleteProperty(window, 'api')
|
||||
})
|
||||
|
||||
it('shows a compact setup prompt when a linked Linear worktree is missing CLI or skill setup', async () => {
|
||||
it('shows a compact host setup prompt about only the skill when it is missing', async () => {
|
||||
const rendered = await renderPrompt({ linked: true, remote: false })
|
||||
|
||||
expect(rendered.textContent).toContain('Set up Linear agent skill')
|
||||
expect(rendered.textContent).toContain('Orca CLI and Linear agent skill are missing')
|
||||
expect(rendered.textContent).toContain('Linear agent skill is missing.')
|
||||
expect(rendered.textContent).not.toContain('Orca CLI')
|
||||
expect(rendered.textContent).toContain('Install it for host agent handoffs')
|
||||
expectNoCliStatusQuery()
|
||||
expect(mocks.useInstalledAgentSkillNames).toHaveBeenCalledWith(
|
||||
LINEAR_AGENT_SKILL_NAMES,
|
||||
expect.objectContaining({ enabled: true, sourceKinds: ['home'] })
|
||||
@@ -248,7 +243,6 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
})
|
||||
|
||||
it('hides when the prompt is not linked or both prerequisites are ready', async () => {
|
||||
mocks.getCliStatus.mockResolvedValue(cliStatus({}))
|
||||
mocks.skillState.installed = true
|
||||
|
||||
const unlinked = await renderPrompt({ linked: false, remote: false })
|
||||
@@ -260,6 +254,25 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
expect(ready.textContent).not.toContain('Set up Linear agent skill')
|
||||
})
|
||||
|
||||
it.each([false, true])(
|
||||
'treats installed skills as ready without registration (WSL: %s)',
|
||||
async (managedWsl) => {
|
||||
const props = managedWsl ? wslPromptProps('Fedora') : { linked: true, remote: false }
|
||||
mocks.skillState.installed = true
|
||||
|
||||
const inline = await renderPrompt(props)
|
||||
expect(inline.textContent).not.toContain('Set up Linear agent skill')
|
||||
|
||||
await unmountPrompt()
|
||||
|
||||
await renderPrompt({ ...props, surface: 'modal' })
|
||||
expect(document.body.textContent).not.toContain(
|
||||
'Enable agents to read and edit the attached Linear ticket.'
|
||||
)
|
||||
expectNoCliStatusQuery()
|
||||
}
|
||||
)
|
||||
|
||||
it('persists host dismissal forever for the host setup target', async () => {
|
||||
const rendered = await renderPrompt({ linked: true, remote: false })
|
||||
|
||||
@@ -287,8 +300,7 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
})
|
||||
|
||||
expect(rendered.textContent).toContain('remote agent environments may need separate setup')
|
||||
expect(mocks.getCliStatus).toHaveBeenCalled()
|
||||
expect(mocks.getWslCliStatus).not.toHaveBeenCalled()
|
||||
expectNoCliStatusQuery()
|
||||
expect(mocks.useInstalledAgentSkillNames).toHaveBeenCalledWith(
|
||||
LINEAR_AGENT_SKILL_NAMES,
|
||||
expect.objectContaining({
|
||||
@@ -308,21 +320,16 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
expect(rendered.textContent).not.toContain('Set up Linear agent skill')
|
||||
})
|
||||
|
||||
it('uses WSL discovery, status, command, and prerequisite setup together', async () => {
|
||||
it('uses WSL skill discovery and commands without CLI registration', async () => {
|
||||
const rendered = await renderPrompt({
|
||||
linked: true,
|
||||
remote: false,
|
||||
currentPlatform: 'win32',
|
||||
settings: {
|
||||
localAgentRuntime: 'wsl',
|
||||
localAgentWslDistro: 'Fedora',
|
||||
terminalWindowsShell: 'wsl.exe',
|
||||
activeRuntimeEnvironmentId: null
|
||||
}
|
||||
settings: wslSettings('Fedora')
|
||||
})
|
||||
|
||||
expect(mocks.getCliStatus).not.toHaveBeenCalled()
|
||||
expect(mocks.getWslCliStatus).toHaveBeenCalledWith({ distro: 'Fedora' })
|
||||
expect(mocks.getWslCliStatus).not.toHaveBeenCalled()
|
||||
expect(mocks.useInstalledAgentSkillNames).toHaveBeenCalledWith(
|
||||
LINEAR_AGENT_SKILL_NAMES,
|
||||
expect.objectContaining({
|
||||
@@ -331,6 +338,7 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
sourceKinds: ['home']
|
||||
})
|
||||
)
|
||||
expect(rendered.textContent).toContain('Linear agent skill is missing.')
|
||||
expect(rendered.textContent).toContain('Install it for WSL agent handoffs')
|
||||
|
||||
const setupButton = Array.from(rendered.querySelectorAll('button')).find(
|
||||
@@ -346,14 +354,11 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
expect.objectContaining({
|
||||
installedCommand: 'npx skills update orca-linear --global',
|
||||
terminalShellOverride: 'powershell.exe',
|
||||
terminalRuntime: expect.objectContaining({ runtime: 'wsl', wslDistro: 'Fedora' }),
|
||||
getPrerequisiteStatus: expect.any(Function)
|
||||
terminalRuntime: expect.objectContaining({ runtime: 'wsl', wslDistro: 'Fedora' })
|
||||
})
|
||||
)
|
||||
const getPrerequisiteStatus = mocks.panelProps.at(-1)?.getPrerequisiteStatus
|
||||
expect(getPrerequisiteStatus).toEqual(expect.any(Function))
|
||||
await (getPrerequisiteStatus as () => Promise<unknown>)()
|
||||
expect(mocks.getWslCliStatus).toHaveBeenLastCalledWith({ distro: 'Fedora' })
|
||||
expect(mocks.panelProps.at(-1)?.getPrerequisiteStatus).toBeUndefined()
|
||||
expect(mocks.panelProps.at(-1)?.preInstallNotice).toBeUndefined()
|
||||
|
||||
const installButton = Array.from(document.body.querySelectorAll('button')).find(
|
||||
(button) => button.textContent === 'Mock install'
|
||||
@@ -361,11 +366,6 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
await act(async () => {
|
||||
installButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
})
|
||||
|
||||
expect(mocks.ensureWslCli).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ runtime: 'wsl', wslDistro: 'Fedora' })
|
||||
)
|
||||
expect(mocks.ensureCli).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('persists WSL dismissal by selected distro', async () => {
|
||||
@@ -373,12 +373,7 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
linked: true,
|
||||
remote: false,
|
||||
currentPlatform: 'win32',
|
||||
settings: {
|
||||
localAgentRuntime: 'wsl',
|
||||
localAgentWslDistro: 'Fedora',
|
||||
terminalWindowsShell: 'wsl.exe',
|
||||
activeRuntimeEnvironmentId: null
|
||||
}
|
||||
settings: wslSettings('Fedora')
|
||||
})
|
||||
|
||||
await act(async () => {
|
||||
@@ -404,7 +399,7 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
}
|
||||
})
|
||||
|
||||
expect(mocks.getWslCliStatus).toHaveBeenCalledWith(undefined)
|
||||
expect(mocks.getWslCliStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps stale terminal WSL settings on host when project runtime is absent', async () => {
|
||||
@@ -418,8 +413,8 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
}
|
||||
})
|
||||
|
||||
expect(mocks.getCliStatus).toHaveBeenCalled()
|
||||
expect(mocks.getWslCliStatus).not.toHaveBeenCalled()
|
||||
expect(container?.textContent).toContain('Install it for host agent handoffs')
|
||||
expectNoCliStatusQuery()
|
||||
})
|
||||
|
||||
it('keeps the prompt usable and loads the lazy setup dialog only when requested', async () => {
|
||||
@@ -445,9 +440,9 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
installButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
})
|
||||
|
||||
expect(mocks.ensureCli).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ onStatusChange: expect.any(Function) })
|
||||
)
|
||||
expect(mocks.panelProps.at(-1)?.preInstallNotice).toBeUndefined()
|
||||
expect(mocks.panelProps.at(-1)?.getPrerequisiteStatus).toBeUndefined()
|
||||
expectNoCliStatusQuery()
|
||||
})
|
||||
|
||||
it('auto-opens as a modal-only prompt and treats the × close as a casual snooze', async () => {
|
||||
@@ -457,16 +452,12 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
expect(document.body.textContent).toContain(
|
||||
'Enable agents to read and edit the attached Linear ticket.'
|
||||
)
|
||||
expect(document.body.textContent).toContain('Orca CLI and Linear agent skill are missing.')
|
||||
expect(document.body.textContent).toContain('Linear agent skill is missing.')
|
||||
expect(document.body.textContent).toContain('Mock install')
|
||||
// Why: the permanent opt-out is an EyeOff icon (no visible text); the casual
|
||||
// dismiss is the dialog ×. Neither "Not now" nor any dismiss label shows as text.
|
||||
expect(document.body.textContent).not.toContain('Not now')
|
||||
expect(mocks.panelProps.at(-1)).toEqual(
|
||||
expect.objectContaining({
|
||||
preInstallNotice: 'CLI registration notice'
|
||||
})
|
||||
)
|
||||
expect(mocks.panelProps.at(-1)?.preInstallNotice).toBeUndefined()
|
||||
|
||||
// Why: the × must snooze for the session, not persist a permanent dismissal.
|
||||
const closeButton = Array.from(document.body.querySelectorAll('button')).find(
|
||||
@@ -512,9 +503,6 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
container?.remove()
|
||||
container = null
|
||||
|
||||
mocks.getCliStatus.mockResolvedValue(
|
||||
cliStatus({ state: 'not_installed', pathConfigured: false })
|
||||
)
|
||||
mocks.skillState.installed = false
|
||||
|
||||
await renderPrompt({ linked: true, remote: false, surface: 'modal' })
|
||||
@@ -540,9 +528,6 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
container?.remove()
|
||||
container = null
|
||||
|
||||
mocks.getCliStatus.mockResolvedValue(
|
||||
cliStatus({ state: 'not_installed', pathConfigured: false })
|
||||
)
|
||||
mocks.skillState.installed = false
|
||||
|
||||
await renderPrompt({ linked: true, remote: false, surface: 'modal' })
|
||||
@@ -581,7 +566,6 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
it('still removes the inline prompt after an inline Re-check succeeds', async () => {
|
||||
const rendered = await renderPrompt({ linked: true, remote: false })
|
||||
|
||||
mocks.getCliStatus.mockResolvedValue(cliStatus({}))
|
||||
mocks.skillState.refresh.mockImplementationOnce(async () => {
|
||||
mocks.skillState.installed = true
|
||||
})
|
||||
@@ -592,15 +576,16 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
await act(async () => {
|
||||
recheckButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
})
|
||||
await settleRender()
|
||||
// Why: the real skill hook re-renders on refresh; this mock only mutates a shared object.
|
||||
await updatePrompt({ linked: true, remote: false })
|
||||
|
||||
expect(mocks.skillState.refresh).toHaveBeenCalled()
|
||||
expect(rendered.textContent).not.toContain('Set up Linear agent skill')
|
||||
expectNoCliStatusQuery()
|
||||
})
|
||||
|
||||
it('keeps the missing setup modal visible after a partial Re-check', async () => {
|
||||
await renderPrompt({ linked: true, remote: false, surface: 'modal' })
|
||||
|
||||
mocks.getCliStatus.mockResolvedValue(cliStatus({}))
|
||||
await renderPrompt(wslPromptProps('Fedora', 'modal'))
|
||||
|
||||
await act(async () => {
|
||||
findBodyButton('Re-check')?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
@@ -615,215 +600,64 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
})
|
||||
|
||||
it('keeps the modal mounted and the Re-check action loading during a slow modal check', async () => {
|
||||
await renderPrompt({ linked: true, remote: false, surface: 'modal' })
|
||||
|
||||
let resolveCliStatus: (status: CliInstallStatus) => void = () => {}
|
||||
const props = wslPromptProps('Fedora', 'modal')
|
||||
await renderPrompt(props)
|
||||
let resolveSkillRefresh: () => void = () => {}
|
||||
mocks.getCliStatus.mockReturnValue(
|
||||
new Promise<CliInstallStatus>((resolve) => {
|
||||
resolveCliStatus = resolve
|
||||
})
|
||||
)
|
||||
mocks.skillState.refresh.mockReturnValue(
|
||||
new Promise<void>((resolve) => {
|
||||
mocks.skillState.refresh.mockImplementation(() => {
|
||||
mocks.skillState.loading = true
|
||||
return new Promise<void>((resolve) => {
|
||||
resolveSkillRefresh = resolve
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
await act(async () => {
|
||||
findBodyButton('Re-check')?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
})
|
||||
|
||||
await updatePrompt(props)
|
||||
expect(document.body.textContent).toContain(
|
||||
'Enable agents to read and edit the attached Linear ticket.'
|
||||
)
|
||||
expect(mocks.panelProps.at(-1)).toEqual(expect.objectContaining({ loading: true }))
|
||||
|
||||
resolveCliStatus(cliStatus({}))
|
||||
mocks.skillState.installed = true
|
||||
mocks.skillState.loading = false
|
||||
resolveSkillRefresh()
|
||||
await settleRender()
|
||||
await updatePrompt(props)
|
||||
expect(document.body.textContent).toContain('Linear ticket access is ready')
|
||||
})
|
||||
|
||||
it('ignores stale CLI success after the runtime context changes during Re-check', async () => {
|
||||
await renderPrompt({
|
||||
linked: true,
|
||||
remote: false,
|
||||
surface: 'modal',
|
||||
currentPlatform: 'win32',
|
||||
settings: {
|
||||
localAgentRuntime: 'wsl',
|
||||
localAgentWslDistro: 'Fedora',
|
||||
terminalWindowsShell: 'wsl.exe',
|
||||
activeRuntimeEnvironmentId: null
|
||||
}
|
||||
})
|
||||
|
||||
let resolveWslStatus: (status: CliInstallStatus) => void = () => {}
|
||||
mocks.getWslCliStatus.mockReturnValueOnce(
|
||||
new Promise<CliInstallStatus>((resolve) => {
|
||||
resolveWslStatus = resolve
|
||||
it('does not keep a new runtime disabled while the previous skill refresh is pending', async () => {
|
||||
await renderPrompt(wslPromptProps('Fedora', 'modal'))
|
||||
let finishRefresh: () => void = () => {}
|
||||
mocks.skillState.refresh.mockImplementation(() => {
|
||||
mocks.skillState.loading = true
|
||||
return new Promise<void>((resolve) => {
|
||||
finishRefresh = resolve
|
||||
})
|
||||
)
|
||||
mocks.skillState.refresh.mockReturnValueOnce(Promise.resolve())
|
||||
|
||||
})
|
||||
await act(async () => {
|
||||
findBodyButton('Re-check')?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
})
|
||||
expect(mocks.panelProps.at(-1)).toEqual(expect.objectContaining({ loading: true }))
|
||||
|
||||
mocks.getCliStatus.mockResolvedValue(
|
||||
cliStatus({ state: 'not_installed', pathConfigured: false })
|
||||
)
|
||||
mocks.skillState.installed = true
|
||||
await updatePrompt({
|
||||
linked: true,
|
||||
remote: false,
|
||||
surface: 'modal',
|
||||
currentPlatform: 'win32',
|
||||
settings: {
|
||||
localAgentRuntime: 'host',
|
||||
terminalWindowsShell: 'powershell.exe',
|
||||
activeRuntimeEnvironmentId: null
|
||||
}
|
||||
})
|
||||
|
||||
resolveWslStatus(cliStatus({}))
|
||||
// The skill hook owns loading for the new target, independently of the old request.
|
||||
mocks.skillState.loading = false
|
||||
await updatePrompt(wslPromptProps('Ubuntu', 'modal'))
|
||||
expect(mocks.panelProps.at(-1)).toEqual(expect.objectContaining({ loading: false }))
|
||||
finishRefresh()
|
||||
await settleRender()
|
||||
|
||||
expect(document.body.textContent).toContain(
|
||||
'Enable agents to read and edit the attached Linear ticket.'
|
||||
)
|
||||
expect(document.body.textContent).toContain('Orca CLI is missing.')
|
||||
expect(document.body.textContent).not.toContain('Linear ticket access is ready')
|
||||
})
|
||||
|
||||
it('ignores stale prerequisite CLI status callbacks after the runtime context changes', async () => {
|
||||
let reportHostCliStatus: ((status: CliInstallStatus) => void) | null = null
|
||||
mocks.ensureCli.mockImplementationOnce(
|
||||
async (options?: { onStatusChange?: (status: CliInstallStatus) => void }) => {
|
||||
reportHostCliStatus = options?.onStatusChange ?? null
|
||||
return null
|
||||
}
|
||||
)
|
||||
await renderPrompt({ linked: true, remote: false, surface: 'modal' })
|
||||
|
||||
await act(async () => {
|
||||
findBodyButton('Mock install')?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
})
|
||||
|
||||
mocks.getWslCliStatus.mockResolvedValue(
|
||||
cliStatus({ state: 'not_installed', pathConfigured: false })
|
||||
)
|
||||
mocks.skillState.installed = true
|
||||
await updatePrompt({
|
||||
linked: true,
|
||||
remote: false,
|
||||
surface: 'modal',
|
||||
currentPlatform: 'win32',
|
||||
settings: {
|
||||
localAgentRuntime: 'wsl',
|
||||
localAgentWslDistro: 'Fedora',
|
||||
terminalWindowsShell: 'wsl.exe',
|
||||
activeRuntimeEnvironmentId: null
|
||||
}
|
||||
})
|
||||
|
||||
await act(async () => {
|
||||
reportHostCliStatus?.(cliStatus({}))
|
||||
})
|
||||
await settleRender()
|
||||
|
||||
expect(document.body.textContent).toContain(
|
||||
'Enable agents to read and edit the attached Linear ticket.'
|
||||
)
|
||||
expect(document.body.textContent).toContain('Orca CLI is missing.')
|
||||
expect(document.body.textContent).not.toContain('Linear ticket access is ready')
|
||||
})
|
||||
|
||||
it('accepts same-context prerequisite CLI status callbacks after a newer Re-check', async () => {
|
||||
let reportHostCliStatus: ((status: CliInstallStatus) => void) | null = null
|
||||
let resolveEnsureCli: () => void = () => {}
|
||||
mocks.ensureCli.mockImplementationOnce(
|
||||
async (options?: { onStatusChange?: (status: CliInstallStatus) => void }) => {
|
||||
reportHostCliStatus = options?.onStatusChange ?? null
|
||||
await new Promise<void>((resolve) => {
|
||||
resolveEnsureCli = resolve
|
||||
})
|
||||
return null
|
||||
}
|
||||
)
|
||||
await renderPrompt({ linked: true, remote: false, surface: 'modal' })
|
||||
|
||||
await act(async () => {
|
||||
findBodyButton('Mock install')?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
})
|
||||
|
||||
mocks.getCliStatus.mockResolvedValue(
|
||||
cliStatus({ state: 'not_installed', pathConfigured: false })
|
||||
)
|
||||
await act(async () => {
|
||||
findBodyButton('Re-check')?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
})
|
||||
await settleRender()
|
||||
|
||||
await act(async () => {
|
||||
reportHostCliStatus?.(cliStatus({}))
|
||||
resolveEnsureCli()
|
||||
})
|
||||
await settleRender()
|
||||
|
||||
expect(document.body.textContent).toContain(
|
||||
'Enable agents to read and edit the attached Linear ticket.'
|
||||
)
|
||||
expect(document.body.textContent).toContain('Linear agent skill is missing.')
|
||||
expect(document.body.textContent).not.toContain('Orca CLI is missing.')
|
||||
})
|
||||
|
||||
it('ignores older same-context CLI refreshes that finish after a newer Re-check', async () => {
|
||||
const rendered = await renderPrompt({ linked: true, remote: false })
|
||||
const recheckButton = Array.from(rendered.querySelectorAll('button')).find(
|
||||
(button) => button.textContent === 'Re-check'
|
||||
)
|
||||
let resolveOlderCliStatus: (status: CliInstallStatus) => void = () => {}
|
||||
mocks.getCliStatus.mockReturnValueOnce(
|
||||
new Promise<CliInstallStatus>((resolve) => {
|
||||
resolveOlderCliStatus = resolve
|
||||
})
|
||||
)
|
||||
mocks.getCliStatus.mockResolvedValue(cliStatus({}))
|
||||
mocks.skillState.refresh.mockImplementation(async () => {
|
||||
mocks.skillState.installed = true
|
||||
})
|
||||
|
||||
await act(async () => {
|
||||
recheckButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
recheckButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
})
|
||||
await settleRender()
|
||||
|
||||
expect(rendered.textContent).not.toContain('Set up Linear agent skill')
|
||||
|
||||
resolveOlderCliStatus(cliStatus({ state: 'not_installed', pathConfigured: false }))
|
||||
await settleRender()
|
||||
|
||||
expect(rendered.textContent).not.toContain('Set up Linear agent skill')
|
||||
})
|
||||
|
||||
it('uses WSL-specific success copy for a selected WSL runtime', async () => {
|
||||
await renderPrompt({
|
||||
linked: true,
|
||||
remote: false,
|
||||
surface: 'modal',
|
||||
currentPlatform: 'win32',
|
||||
settings: {
|
||||
localAgentRuntime: 'wsl',
|
||||
localAgentWslDistro: 'Fedora',
|
||||
terminalWindowsShell: 'wsl.exe',
|
||||
activeRuntimeEnvironmentId: null
|
||||
}
|
||||
settings: wslSettings('Fedora')
|
||||
})
|
||||
|
||||
mocks.getWslCliStatus.mockResolvedValue(cliStatus({}))
|
||||
mocks.skillState.refresh.mockImplementationOnce(async () => {
|
||||
mocks.skillState.installed = true
|
||||
})
|
||||
@@ -844,12 +678,7 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
remote: false,
|
||||
currentPlatform: 'win32',
|
||||
projectRuntime: projectHostRuntime,
|
||||
settings: {
|
||||
localAgentRuntime: 'wsl',
|
||||
localAgentWslDistro: 'Fedora',
|
||||
terminalWindowsShell: 'wsl.exe',
|
||||
activeRuntimeEnvironmentId: null
|
||||
}
|
||||
settings: wslSettings('Fedora')
|
||||
})
|
||||
|
||||
expect(mocks.useInstalledAgentSkillNames).toHaveBeenLastCalledWith(
|
||||
@@ -858,11 +687,10 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
discoveryTarget: { projectRuntime: projectHostRuntime }
|
||||
})
|
||||
)
|
||||
expect(mocks.getCliStatus).toHaveBeenCalled()
|
||||
expect(mocks.getWslCliStatus).not.toHaveBeenCalled()
|
||||
expectNoCliStatusQuery()
|
||||
})
|
||||
|
||||
it('uses selected project WSL runtime for skill discovery and CLI status', async () => {
|
||||
it('uses selected project WSL runtime for skill discovery without checking CLI status', async () => {
|
||||
const rendered = await renderPrompt({
|
||||
linked: true,
|
||||
remote: false,
|
||||
@@ -881,7 +709,7 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
discoveryTarget: { projectRuntime: projectWslRuntime }
|
||||
})
|
||||
)
|
||||
expect(mocks.getWslCliStatus).toHaveBeenCalledWith({ distro: 'Ubuntu' })
|
||||
expect(mocks.getWslCliStatus).not.toHaveBeenCalled()
|
||||
expect(mocks.getCliStatus).not.toHaveBeenCalled()
|
||||
await act(async () => {
|
||||
Array.from(rendered.querySelectorAll('button'))
|
||||
@@ -898,7 +726,6 @@ describe('LinearAgentSkillSetupPrompt', () => {
|
||||
it('uses remote-safe success copy for remote workspaces', async () => {
|
||||
await renderPrompt({ linked: true, remote: true, surface: 'modal' })
|
||||
|
||||
mocks.getCliStatus.mockResolvedValue(cliStatus({}))
|
||||
mocks.skillState.refresh.mockImplementationOnce(async () => {
|
||||
mocks.skillState.installed = true
|
||||
})
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Suspense, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { RefreshCw, TicketCheck, X } from 'lucide-react'
|
||||
import type { CliInstallStatus } from '../../../../shared/cli-install-types'
|
||||
import type { ProjectExecutionRuntimeResolution } from '../../../../shared/project-execution-runtime'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
@@ -12,17 +11,9 @@ import {
|
||||
ORCA_LINEAR_SKILL_INSTALL_COMMAND
|
||||
} from '@/lib/agent-feature-install-commands'
|
||||
import { getLinearAgentSkillUpdateCommand } from '@/lib/linear-agent-skill-update-command'
|
||||
import {
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal,
|
||||
isOrcaCliAvailableOnPath
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import { lazyWithRetry } from '@/lib/lazy-with-retry'
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
buildSkillCommandForRuntime,
|
||||
ensureWslCliAvailableForAgentSkillTerminal,
|
||||
getWslCliDistroRequest
|
||||
} from '../settings/CliSkillRuntimeSetup'
|
||||
import { buildSkillCommandForRuntime } from '../settings/CliSkillRuntimeSetup'
|
||||
import {
|
||||
getLinearAgentSkillSetupInlineRuntimeCopy,
|
||||
getLinearAgentSkillSetupMissingLabel,
|
||||
@@ -79,8 +70,6 @@ export function LinearAgentSkillSetupPrompt({
|
||||
currentPlatform = getCurrentPlatform(),
|
||||
className
|
||||
}: LinearAgentSkillSetupPromptProps): React.JSX.Element | null {
|
||||
const [cliStatus, setCliStatus] = useState<CliInstallStatus | null>(null)
|
||||
const [cliLoading, setCliLoading] = useState(linked)
|
||||
const [setupDialogOpen, setSetupDialogOpen] = useState(false)
|
||||
const [setupCheckResult, setSetupCheckResult] = useState<SetupCheckResult>('idle')
|
||||
const [activeSetupCheckIdentity, setActiveSetupCheckIdentity] = useState<string | null>(null)
|
||||
@@ -98,9 +87,6 @@ export function LinearAgentSkillSetupPrompt({
|
||||
}),
|
||||
[agentRuntime, projectRuntime, remote, settings?.activeRuntimeEnvironmentId]
|
||||
)
|
||||
const currentSetupCheckIdentityRef = useRef(setupCheckIdentity)
|
||||
const cliRefreshGenerationRef = useRef(0)
|
||||
currentSetupCheckIdentityRef.current = setupCheckIdentity
|
||||
const skillDiscoveryTarget = useMemo(
|
||||
() => getLinearPromptSkillDiscoveryTarget(agentRuntime, projectRuntime),
|
||||
[agentRuntime, projectRuntime]
|
||||
@@ -138,57 +124,8 @@ export function LinearAgentSkillSetupPrompt({
|
||||
settings,
|
||||
agentRuntime
|
||||
)
|
||||
const writeCliStatusIfCurrent = useCallback(
|
||||
(requestIdentity: string, requestGeneration: number, write: () => void): void => {
|
||||
if (
|
||||
requestGeneration === cliRefreshGenerationRef.current &&
|
||||
currentSetupCheckIdentityRef.current === requestIdentity
|
||||
) {
|
||||
write()
|
||||
}
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
const writeCliStatusForIdentity = useCallback((requestIdentity: string, write: () => void) => {
|
||||
if (currentSetupCheckIdentityRef.current === requestIdentity) {
|
||||
write()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const refreshCliStatus = useCallback(async (): Promise<void> => {
|
||||
const requestIdentity = setupCheckIdentity
|
||||
const requestGeneration = ++cliRefreshGenerationRef.current
|
||||
const writeIfCurrent = (write: () => void): void => {
|
||||
writeCliStatusIfCurrent(requestIdentity, requestGeneration, write)
|
||||
}
|
||||
if (!linked) {
|
||||
writeIfCurrent(() => {
|
||||
setCliStatus(null)
|
||||
setCliLoading(false)
|
||||
})
|
||||
return
|
||||
}
|
||||
setCliLoading(true)
|
||||
try {
|
||||
const nextStatus = await (agentRuntime.runtime === 'wsl'
|
||||
? window.api.cli.getWslInstallStatus(getWslCliDistroRequest(agentRuntime))
|
||||
: window.api.cli.getInstallStatus())
|
||||
writeIfCurrent(() => setCliStatus(nextStatus))
|
||||
} catch {
|
||||
writeIfCurrent(() => setCliStatus(null))
|
||||
} finally {
|
||||
writeIfCurrent(() => setCliLoading(false))
|
||||
}
|
||||
}, [agentRuntime, linked, setupCheckIdentity, writeCliStatusIfCurrent])
|
||||
|
||||
useEffect(() => {
|
||||
void refreshCliStatus()
|
||||
}, [refreshCliStatus])
|
||||
|
||||
const cliAvailable = isOrcaCliAvailableOnPath(cliStatus)
|
||||
const setupReady = linked && !cliLoading && !skill.loading && cliAvailable && skill.installed
|
||||
const missingSetup = linked && !localDismissed && !cliLoading && !skill.loading && !setupReady
|
||||
const setupReady = linked && !skill.loading && skill.installed
|
||||
const missingSetup = linked && !localDismissed && !skill.loading && !setupReady
|
||||
const explicitCheckMatchesContext = activeSetupCheckIdentity === setupCheckIdentity
|
||||
const showCheckingModal =
|
||||
surface === 'modal' &&
|
||||
@@ -211,8 +148,7 @@ export function LinearAgentSkillSetupPrompt({
|
||||
setActiveSetupCheckIdentity(null)
|
||||
return
|
||||
}
|
||||
// Why: refreshes update CLI and skill state independently, so success is
|
||||
// promoted only after the current render observes both ready for this target.
|
||||
// Wait until the refreshed skill state is ready for this target.
|
||||
if (setupCheckResult === 'checking' && setupReady) {
|
||||
setSetupCheckResult('ready')
|
||||
return
|
||||
@@ -252,16 +188,11 @@ export function LinearAgentSkillSetupPrompt({
|
||||
setSetupDialogOpen(false)
|
||||
}
|
||||
|
||||
const missingLabel = getLinearAgentSkillSetupMissingLabel(cliAvailable, skill.installed)
|
||||
const missingLabel = getLinearAgentSkillSetupMissingLabel()
|
||||
|
||||
const toastTitle = getLinearAgentSkillSetupToastTitle(cliAvailable, skill.installed)
|
||||
const toastTitle = getLinearAgentSkillSetupToastTitle()
|
||||
|
||||
const toastDescription = getLinearAgentSkillSetupToastDescription(
|
||||
cliAvailable,
|
||||
skill.installed,
|
||||
remote,
|
||||
agentRuntime
|
||||
)
|
||||
const toastDescription = getLinearAgentSkillSetupToastDescription(remote, agentRuntime)
|
||||
const openSetupDialog = useCallback(() => setSetupDialogOpen(true), [])
|
||||
|
||||
useLinearAgentSkillSetupReminderToast({
|
||||
@@ -294,38 +225,15 @@ export function LinearAgentSkillSetupPrompt({
|
||||
terminalShellOverride={terminalShellOverride}
|
||||
terminalRuntime={agentRuntime}
|
||||
installed={skill.installed}
|
||||
loading={showCheckingModal || cliLoading || skill.loading}
|
||||
loading={showCheckingModal || skill.loading}
|
||||
error={skill.error}
|
||||
getPrerequisiteStatus={
|
||||
agentRuntime.runtime === 'wsl'
|
||||
? () => window.api.cli.getWslInstallStatus(getWslCliDistroRequest(agentRuntime))
|
||||
: undefined
|
||||
}
|
||||
onBeforeOpenTerminal={async () => {
|
||||
const requestIdentity = setupCheckIdentity
|
||||
const writeIfCurrent = (write: () => void): void => {
|
||||
writeCliStatusForIdentity(requestIdentity, write)
|
||||
}
|
||||
const nextStatus =
|
||||
agentRuntime.runtime === 'wsl'
|
||||
? await ensureWslCliAvailableForAgentSkillTerminal(agentRuntime)
|
||||
: await ensureOrcaCliAvailableForAgentSkillTerminal({
|
||||
onStatusChange: (nextCliStatus) => {
|
||||
writeIfCurrent(() => setCliStatus(nextCliStatus))
|
||||
}
|
||||
})
|
||||
if (agentRuntime.runtime === 'wsl') {
|
||||
writeIfCurrent(() => setCliStatus(nextStatus))
|
||||
}
|
||||
}}
|
||||
onRecheck={async () => {
|
||||
if (surface === 'modal') {
|
||||
setActiveSetupCheckIdentity(setupCheckIdentity)
|
||||
setSetupCheckResult('checking')
|
||||
await Promise.all([refreshCliStatus(), skill.refresh()])
|
||||
await skill.refresh()
|
||||
return
|
||||
}
|
||||
await refreshCliStatus()
|
||||
await skill.refresh()
|
||||
}}
|
||||
onOpenChange={(open) => {
|
||||
@@ -399,7 +307,6 @@ export function LinearAgentSkillSetupPrompt({
|
||||
size="xs"
|
||||
className="gap-1"
|
||||
onClick={() => {
|
||||
void refreshCliStatus()
|
||||
void skill.refresh()
|
||||
}}
|
||||
>
|
||||
|
||||
+38
-43
@@ -3,7 +3,7 @@
|
||||
import { join } from 'node:path'
|
||||
import { act, type ComponentProps, type ReactNode } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import type { CliInstallStatus } from '../../../../shared/cli-install-types'
|
||||
import type { ProjectExecutionRuntimeResolution } from '../../../../shared/project-execution-runtime'
|
||||
import type { DiscoveredSkill } from '../../../../shared/skills'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
@@ -21,6 +21,7 @@ const mocks = vi.hoisted(() => ({
|
||||
},
|
||||
useInstalledAgentSkillNames: vi.fn(),
|
||||
getCliStatus: vi.fn(),
|
||||
getWslCliStatus: vi.fn(),
|
||||
panelProps: [] as Record<string, unknown>[]
|
||||
}))
|
||||
|
||||
@@ -29,17 +30,8 @@ vi.mock('@/hooks/useInstalledAgentSkills', async (importOriginal) => ({
|
||||
useInstalledAgentSkillNames: mocks.useInstalledAgentSkillNames
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/agent-skill-cli-prerequisite', () => ({
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE: 'CLI registration notice',
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal: vi.fn(async () => null),
|
||||
isOrcaCliAvailableOnPath: (status: CliInstallStatus | null | undefined) =>
|
||||
status?.state === 'installed' && status.pathConfigured
|
||||
}))
|
||||
|
||||
vi.mock('../settings/CliSkillRuntimeSetup', () => ({
|
||||
buildSkillCommandForRuntime: (command: string) => command,
|
||||
ensureWslCliAvailableForAgentSkillTerminal: vi.fn(async () => null),
|
||||
getWslCliDistroRequest: () => undefined
|
||||
buildSkillCommandForRuntime: (command: string) => command
|
||||
}))
|
||||
|
||||
vi.mock('../settings/AgentSkillSetupPanel', () => ({
|
||||
@@ -49,26 +41,23 @@ vi.mock('../settings/AgentSkillSetupPanel', () => ({
|
||||
}
|
||||
}))
|
||||
|
||||
let root: Root | null = null
|
||||
let container: HTMLDivElement | null = null
|
||||
|
||||
function cliStatus(): CliInstallStatus {
|
||||
return {
|
||||
platform: 'darwin',
|
||||
commandName: 'orca',
|
||||
commandPath: null,
|
||||
pathDirectory: null,
|
||||
pathConfigured: false,
|
||||
launcherPath: '/Applications/Orca.app/Contents/MacOS/Orca',
|
||||
installMethod: null,
|
||||
supported: true,
|
||||
state: 'not_installed',
|
||||
currentTarget: null,
|
||||
unsupportedReason: null,
|
||||
detail: null
|
||||
// Why: host terminals need no CLI registration, so an installed skill only still
|
||||
// shows the prompt (and its update command) on a WSL runtime with the CLI missing.
|
||||
const projectWslRuntime: ProjectExecutionRuntimeResolution = {
|
||||
status: 'resolved',
|
||||
runtime: {
|
||||
kind: 'wsl',
|
||||
hostPlatform: 'wsl',
|
||||
projectId: 'repo-1',
|
||||
distro: 'Ubuntu',
|
||||
reason: 'project-override',
|
||||
cacheKey: 'repo-1:wsl:Ubuntu'
|
||||
}
|
||||
}
|
||||
|
||||
let root: Root | null = null
|
||||
let container: HTMLDivElement | null = null
|
||||
|
||||
function discoveredSkill(overrides: Partial<DiscoveredSkill>): DiscoveredSkill {
|
||||
return {
|
||||
id: 'skill-1',
|
||||
@@ -103,7 +92,14 @@ async function renderPrompt(
|
||||
root = createRoot(container)
|
||||
await act(async () => {
|
||||
root?.render(
|
||||
<LinearAgentSkillSetupPrompt linked={true} remote={false} surface="modal" {...props} />
|
||||
<LinearAgentSkillSetupPrompt
|
||||
linked={true}
|
||||
remote={false}
|
||||
surface="modal"
|
||||
currentPlatform="win32"
|
||||
projectRuntime={projectWslRuntime}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
await import('./LinearAgentSkillSetupDialog')
|
||||
@@ -120,11 +116,13 @@ describe('LinearAgentSkillSetupPrompt update command', () => {
|
||||
mocks.useInstalledAgentSkillNames.mockReset()
|
||||
mocks.useInstalledAgentSkillNames.mockReturnValue(mocks.skillState)
|
||||
mocks.getCliStatus.mockReset()
|
||||
mocks.getCliStatus.mockResolvedValue(cliStatus())
|
||||
mocks.getWslCliStatus.mockReset()
|
||||
mocks.panelProps.length = 0
|
||||
Object.defineProperty(window, 'api', {
|
||||
configurable: true,
|
||||
value: { cli: { getInstallStatus: mocks.getCliStatus } }
|
||||
value: {
|
||||
cli: { getInstallStatus: mocks.getCliStatus, getWslInstallStatus: mocks.getWslCliStatus }
|
||||
}
|
||||
})
|
||||
Object.defineProperty(window, 'localStorage', {
|
||||
configurable: true,
|
||||
@@ -150,33 +148,30 @@ describe('LinearAgentSkillSetupPrompt update command', () => {
|
||||
_linearAgentSkillSetupPromptInternalsForTests.resetSessionReminders()
|
||||
})
|
||||
|
||||
it('uses the canonical update command when the canonical Linear skill is installed', async () => {
|
||||
it('does not request setup when the canonical Linear skill is installed', async () => {
|
||||
mocks.skillState.skills = [discoveredSkill({ name: 'orca-linear' })]
|
||||
|
||||
await renderPrompt()
|
||||
|
||||
expect(mocks.panelProps.at(-1)).toEqual(
|
||||
expect.objectContaining({ installedCommand: 'npx skills update orca-linear --global' })
|
||||
)
|
||||
expect(mocks.panelProps).toHaveLength(0)
|
||||
expect(mocks.getWslCliStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('uses the legacy update command when only the legacy Linear skill is installed', async () => {
|
||||
it('does not request setup when the legacy Linear skill is installed', async () => {
|
||||
mocks.skillState.skills = [legacyLinearSkillPath()]
|
||||
|
||||
await renderPrompt()
|
||||
|
||||
expect(mocks.panelProps.at(-1)).toEqual(
|
||||
expect.objectContaining({ installedCommand: 'npx skills update linear-tickets --global' })
|
||||
)
|
||||
expect(mocks.panelProps).toHaveLength(0)
|
||||
expect(mocks.getWslCliStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('prefers the canonical update command when both Linear skill names are installed', async () => {
|
||||
it('does not request setup when both Linear skill names are installed', async () => {
|
||||
mocks.skillState.skills = [discoveredSkill({ name: 'orca-linear' }), legacyLinearSkillPath()]
|
||||
|
||||
await renderPrompt()
|
||||
|
||||
expect(mocks.panelProps.at(-1)).toEqual(
|
||||
expect.objectContaining({ installedCommand: 'npx skills update orca-linear --global' })
|
||||
)
|
||||
expect(mocks.panelProps).toHaveLength(0)
|
||||
expect(mocks.getWslCliStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,44 +1,14 @@
|
||||
import type { LocalAgentRuntime } from '../settings/CliSkillRuntimeSetup'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
export function getLinearAgentSkillSetupMissingLabel(
|
||||
cliAvailable: boolean,
|
||||
skillInstalled: boolean
|
||||
): string {
|
||||
if (!cliAvailable && !skillInstalled) {
|
||||
return translate(
|
||||
'auto.components.sidebar.LinearAgentSkillSetupPrompt.missingCliAndSkill',
|
||||
'Orca CLI and Linear agent skill are missing.'
|
||||
)
|
||||
}
|
||||
if (!cliAvailable) {
|
||||
return translate(
|
||||
'auto.components.sidebar.LinearAgentSkillSetupPrompt.missingCli',
|
||||
'Orca CLI is missing.'
|
||||
)
|
||||
}
|
||||
export function getLinearAgentSkillSetupMissingLabel(): string {
|
||||
return translate(
|
||||
'auto.components.sidebar.LinearAgentSkillSetupPrompt.missingSkill',
|
||||
'Linear agent skill is missing.'
|
||||
)
|
||||
}
|
||||
|
||||
export function getLinearAgentSkillSetupToastTitle(
|
||||
cliAvailable: boolean,
|
||||
skillInstalled: boolean
|
||||
): string {
|
||||
if (!cliAvailable && !skillInstalled) {
|
||||
return translate(
|
||||
'auto.components.sidebar.LinearAgentSkillSetupPrompt.toastMissingCliAndSkill',
|
||||
'Orca CLI and Linear skill are missing'
|
||||
)
|
||||
}
|
||||
if (!cliAvailable) {
|
||||
return translate(
|
||||
'auto.components.sidebar.LinearAgentSkillSetupPrompt.toastMissingCli',
|
||||
'Orca CLI is missing'
|
||||
)
|
||||
}
|
||||
export function getLinearAgentSkillSetupToastTitle(): string {
|
||||
return translate(
|
||||
'auto.components.sidebar.LinearAgentSkillSetupPrompt.toastMissingSkill',
|
||||
'Linear skill is missing'
|
||||
@@ -46,12 +16,10 @@ export function getLinearAgentSkillSetupToastTitle(
|
||||
}
|
||||
|
||||
export function getLinearAgentSkillSetupToastDescription(
|
||||
cliAvailable: boolean,
|
||||
skillInstalled: boolean,
|
||||
remote: boolean,
|
||||
agentRuntime: LocalAgentRuntime
|
||||
): string {
|
||||
const baseDescription = getLinearAgentSkillSetupToastBaseDescription(cliAvailable, skillInstalled)
|
||||
const baseDescription = getLinearAgentSkillSetupToastBaseDescription()
|
||||
if (remote) {
|
||||
return translate(
|
||||
'auto.components.sidebar.LinearAgentSkillSetupPrompt.toastRemoteDescription',
|
||||
@@ -69,22 +37,7 @@ export function getLinearAgentSkillSetupToastDescription(
|
||||
return baseDescription
|
||||
}
|
||||
|
||||
function getLinearAgentSkillSetupToastBaseDescription(
|
||||
cliAvailable: boolean,
|
||||
skillInstalled: boolean
|
||||
): string {
|
||||
if (!cliAvailable && !skillInstalled) {
|
||||
return translate(
|
||||
'auto.components.sidebar.LinearAgentSkillSetupPrompt.toastInstallCliAndSkillDescription',
|
||||
'Install the Orca CLI and the Linear skill to enable your agents to read and edit Linear tasks.'
|
||||
)
|
||||
}
|
||||
if (!cliAvailable) {
|
||||
return translate(
|
||||
'auto.components.sidebar.LinearAgentSkillSetupPrompt.toastInstallCliDescription',
|
||||
'Install the Orca CLI to enable your agents to read and edit Linear tasks.'
|
||||
)
|
||||
}
|
||||
function getLinearAgentSkillSetupToastBaseDescription(): string {
|
||||
return translate(
|
||||
'auto.components.sidebar.LinearAgentSkillSetupPrompt.toastInstallSkillDescription',
|
||||
'Install the Linear skill to enable your agents to read and edit Linear tasks through the Orca CLI.'
|
||||
|
||||
+1
@@ -1151,6 +1151,7 @@
|
||||
"d420c43729": "Imported {{value0}} cookies from {{value1}}{{value2}} into {{value3}}."
|
||||
},
|
||||
"BrowserUsePane": {
|
||||
"de9b2f32f3": "Enable",
|
||||
"e44c5d681e": "From"
|
||||
},
|
||||
"CliSection": {
|
||||
|
||||
@@ -6135,13 +6135,11 @@
|
||||
"6e7f8a9b0c": "Remove host…"
|
||||
},
|
||||
"LinearAgentSkillSetupPrompt": {
|
||||
"missingCliAndSkill": "Orca CLI and Linear agent skill are missing.",
|
||||
"modalTitle": "Enable Linear ticket access",
|
||||
"modalDescription": "Install the Linear skill from a terminal.",
|
||||
"modalPrompt": "Enable agents to read and edit the attached Linear ticket.",
|
||||
"dontShowAgain": "Don't show again",
|
||||
"missingBoth": "Orca CLI and Linear agent skill are missing.",
|
||||
"missingCli": "Orca CLI is missing.",
|
||||
"missingSkill": "Linear agent skill is missing.",
|
||||
"title": "Set up Linear agent skill",
|
||||
"remoteCopy": "This installs host setup; remote agent environments may need separate setup.",
|
||||
@@ -6153,7 +6151,6 @@
|
||||
"panelDescription": "Install the host agent skill for linked Linear task handoffs.",
|
||||
"terminalTitle": "Install Linear agent skill",
|
||||
"terminalAria": "Linear agent skill installer terminal",
|
||||
"install": "Install CLI & Skill",
|
||||
"successTitle": "Linear ticket access is ready",
|
||||
"successDescription": "Agents can now read and update linked Linear tickets from this workspace.",
|
||||
"successDescriptionWsl": "WSL agents can now use linked Linear tickets from this workspace.",
|
||||
@@ -6162,11 +6159,7 @@
|
||||
"done": "Done",
|
||||
"wslCopy": "Install it for WSL agent handoffs from linked Linear work.",
|
||||
"wslLabel": "WSL default",
|
||||
"toastMissingCliAndSkill": "Orca CLI and Linear skill are missing",
|
||||
"toastMissingCli": "Orca CLI is missing",
|
||||
"toastMissingSkill": "Linear skill is missing",
|
||||
"toastInstallCliAndSkillDescription": "Install the Orca CLI and the Linear skill to enable your agents to read and edit Linear tasks.",
|
||||
"toastInstallCliDescription": "Install the Orca CLI to enable your agents to read and edit Linear tasks.",
|
||||
"toastInstallSkillDescription": "Install the Linear skill to enable your agents to read and edit Linear tasks through the Orca CLI.",
|
||||
"toastRemoteDescription": "{{value0}} Remote agent environments may need their own setup.",
|
||||
"toastWslDescription": "{{value0}} This setup runs in the selected WSL agent runtime."
|
||||
@@ -6528,7 +6521,6 @@
|
||||
"SetupGuideModal": {
|
||||
"3598a3ca0c": "Finish the core workflows that make Orca useful for parallel agent work.",
|
||||
"48a9e5ef2d": "Getting started",
|
||||
"28cf59fcb4": "This will hide the checklist from the sidebar",
|
||||
"f3b5ffb2a6": "Hide checklist from sidebar"
|
||||
},
|
||||
"SetupGuideProgressRing": {
|
||||
@@ -7017,24 +7009,15 @@
|
||||
"af8c83ed61": "Import cookies from Chrome, Edge, or other browsers so agents can reuse your logins.",
|
||||
"68ea76eb71": "Install the Browser Use skill so agents can operate Orca's browser.",
|
||||
"2d6ead9ab2": "Install Browser Use Skill",
|
||||
"e9f3f3b488": "Installed at",
|
||||
"9fca1f7f5d": "Registers the Orca CLI command so agents can orchestrate the browser from their shell.",
|
||||
"c6065d205d": "Enable Orca CLI",
|
||||
"c79eff0213": "Register the Orca CLI so agents can drive the browser.",
|
||||
"702488a5f7": "Let coding agents drive this browser with your logins. Finish the three steps below.",
|
||||
"b8a1f2d84d": "Agent Browser Use",
|
||||
"96b91c6349": "Let coding agents drive this browser with your logins.",
|
||||
"2ea4617e3a": "Imported {{value0}} cookies from {{value1}}{{value2}}.",
|
||||
"721aee31b4": "Registered the Orca CLI in PATH.",
|
||||
"180a9abf3a": "Failed to load CLI status.",
|
||||
"2ccfc9cff8": "Import",
|
||||
"0462565413": "Re-import",
|
||||
"de9b2f32f3": "Enable",
|
||||
"ad8cb0ee22": "Fix PATH",
|
||||
"0289434ed6": "Enabled",
|
||||
"8b3054dac7": "Registering...",
|
||||
"8f2675c2f3": "Imported {{value0}} cookies from file.",
|
||||
"5301857d88": "From {{value0}}"
|
||||
"5301857d88": "From {{value0}}",
|
||||
"finishSteps": "Let coding agents drive this browser with your logins. Finish the steps below."
|
||||
},
|
||||
"BrowserUseSkillStep": {
|
||||
"0871b6998d": "Enables agents to navigate and verify pages in Orca's browser.",
|
||||
@@ -7053,7 +7036,7 @@
|
||||
"5dae812f50": "Refresh",
|
||||
"52e640f3a0": "Refresh CLI status",
|
||||
"38edbb5721": "Shell command",
|
||||
"6930feda9e": "Use Orca from your terminal to open the app, manage worktrees, and interact with Orca terminals.",
|
||||
"outsideOrcaDescription": "Orca terminals already have `orca`. Turn this on to use `orca` from other terminals outside Orca.",
|
||||
"c5c0f2641d": "Orca CLI",
|
||||
"d77352f2df": "Failed to remove `{{value0}}` from PATH.",
|
||||
"af5540930c": "Removed `{{value0}}` from PATH.",
|
||||
@@ -7702,15 +7685,9 @@
|
||||
"c7f3fe0a6e": "Common emulator commands",
|
||||
"d94ca6a623": "Enables agents to use Orca CLI commands, including mobile emulator control.",
|
||||
"67e19ee03c": "Orca CLI skill",
|
||||
"aaf62a3dd2": "Installed at",
|
||||
"2fef055608": "Registers the Orca CLI command so agents can control the active emulator from their shell.",
|
||||
"4f2205f3b6": "Enable Orca CLI",
|
||||
"ff4b7e65d6": "Let coding agents control the active mobile emulator with Orca CLI commands.",
|
||||
"2a674aa810": "Agent Mobile Emulator Control",
|
||||
"cdeaed9e37": "Registered the Orca CLI in PATH.",
|
||||
"3d34423e88": "Registering the Orca CLI",
|
||||
"3be27641c9": "so emulator commands can run from agent shells.",
|
||||
"3941719a56": "Checking Orca CLI before opening skill setup."
|
||||
"cdeaed9e37": "Registered the Orca CLI in PATH."
|
||||
},
|
||||
"MobileEmulatorExamples": {
|
||||
"edf13dd03b": "Copy",
|
||||
@@ -9557,16 +9534,7 @@
|
||||
"9d97446873": "agent",
|
||||
"a2d489263e": "skill",
|
||||
"a7e82445fa": "Install the Browser Use skill so agents can operate Orca's browser.",
|
||||
"a1414dcefb": "Install Browser Use Skill",
|
||||
"e56c7b55c9": "setup",
|
||||
"034c5e8d7f": "enable",
|
||||
"7e0dcb257a": "shell",
|
||||
"3ffafc9b95": "command",
|
||||
"30c74aaa1f": "path",
|
||||
"ff05cbc344": "orca",
|
||||
"85fab5e12c": "cli",
|
||||
"890ddf943d": "Register the Orca CLI so agents can drive the browser.",
|
||||
"50f0860e18": "Enable Orca CLI"
|
||||
"a1414dcefb": "Install Browser Use Skill"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -13825,10 +13793,9 @@
|
||||
"69af7e9c1c": "isn't on your PATH yet. Orca will set it as your default and you can install it any time.",
|
||||
"1eee1c7bd8": "No agents detected on your PATH. Pick one to install later, or continue with a blank terminal.",
|
||||
"hideAgents": "Hide agents",
|
||||
"showMoreAgents": "Show {{value0}} more agents→",
|
||||
"yoloPermissionsLabel": "Yolo / Dangerously skip permissions",
|
||||
"yoloPermissionsInfo": "Agent permission info",
|
||||
"yoloPermissionsTooltip": "Skip permission checks for agents for less interruptions"
|
||||
"showMoreAgents": "Show {{value0}} more agents →",
|
||||
"yoloModeLabel": "Yolo mode",
|
||||
"yoloPermissionsDescription": "Agents run commands and edit files without asking, and some also bypass their sandbox. Use only in projects you trust."
|
||||
},
|
||||
"FeatureSetupInlineTerminal": {
|
||||
"789b59936e": "Press Enter to run the command and confirm npx if asked. You can also set this up later in Settings.",
|
||||
@@ -13861,9 +13828,8 @@
|
||||
"a74c6d6b18": "not-installed"
|
||||
},
|
||||
"NotificationStep": {
|
||||
"3bede04483": "Send Test Notification",
|
||||
"3bede04483": "Send test notification",
|
||||
"dc897423e1": "Choose notification sound",
|
||||
"53aaffe49a": "Notification Sound",
|
||||
"0fe570690c": "Pick the alert Orca plays after a desktop notification is delivered.",
|
||||
"0af746e41f": "Choose a sound",
|
||||
"8124d085a6": "Open Mac Settings",
|
||||
@@ -13872,8 +13838,8 @@
|
||||
"e52aacf380": "Loading notification settings…",
|
||||
"3cd5374e22": "Notification settings are still loading",
|
||||
"b6a994e36e": "Notification sound could not be played",
|
||||
"c0692baa52": "Choose Custom File",
|
||||
"ac80d97e02": "Change Custom File",
|
||||
"c0692baa52": "Choose custom file",
|
||||
"ac80d97e02": "Change custom file",
|
||||
"56b836215c": "Checking notification permission…",
|
||||
"fd84d3e9b8": "Notifications are enabled",
|
||||
"4f7bce5644": "macOS will alert you when agents finish or terminals need attention.",
|
||||
@@ -14387,7 +14353,8 @@
|
||||
"3a59452a67": "Skill command copied and inserted below for review.",
|
||||
"c605f51f2b": "Capability setup ready",
|
||||
"1aa657d8f4": "Some capability setup needs attention",
|
||||
"c89534cbe9": "Install CLI & Skills"
|
||||
"allInstalled": "All skills installed",
|
||||
"installSkills": "Install skills"
|
||||
},
|
||||
"AiCommitPrSettingsCard": {
|
||||
"8d4152701a": "e.g. ollama run llama3.1 {{value0}}",
|
||||
@@ -14480,7 +14447,6 @@
|
||||
"1a6a7d6c80": "Setup",
|
||||
"713cc529a5": "Milestones",
|
||||
"b1f1981c5e": "See tasks",
|
||||
"0235b268b2": "Not done yet",
|
||||
"13294d3405": "Done"
|
||||
},
|
||||
"FeatureWallSetupWorkflowActions": {
|
||||
@@ -14591,13 +14557,13 @@
|
||||
"setup": {
|
||||
"status": {
|
||||
"8eccfcb314": "Installed",
|
||||
"21d4f79c93": "click Install CLI & Skills to open macOS access settings",
|
||||
"5c9293e51a": "checking app access",
|
||||
"aae94eeb52": "Click Install CLI & Skills",
|
||||
"aa8e143a2f": "Could not check install",
|
||||
"9b33e7fb13": "Checking install",
|
||||
"4c8e1f92a7": "open Orca Desktop on this Mac",
|
||||
"6d2b0a84e1": "Unavailable in this build"
|
||||
"6d2b0a84e1": "Unavailable in this build",
|
||||
"notInstalled": "Not installed",
|
||||
"needsMacAccess": "Needs macOS access",
|
||||
"requiresOrcaDesktop": "Requires Orca Desktop"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14608,7 +14574,7 @@
|
||||
"checklist": {
|
||||
"localized": {
|
||||
"copy": {
|
||||
"ec0a363633": "Multi-task",
|
||||
"workOnTwoTasks": "Work on two tasks at once",
|
||||
"62bac8f43c": "Work in 2 different worktrees at once. Each one is isolated (even in the same project). Perfect for working on 2 features at once.",
|
||||
"908898c3ee": "Use Orca's browser",
|
||||
"43781563c3": "Browse your web app without leaving Orca. Grab any element and send its exact source and styles to an agent with one click.",
|
||||
@@ -14616,14 +14582,14 @@
|
||||
"71bd9a8c95": "Know the moment an agent finishes, needs attention, or gets blocked.",
|
||||
"46db810da8": "Choose your default agent",
|
||||
"b8e5bae17f": "Start new work faster with your preferred agent already selected.",
|
||||
"fee5557b02": "Enable Orca CLI",
|
||||
"7bcb4097fa": "Register the Orca shell command and install agent skills for browser, computer, and orchestration workflows.",
|
||||
"ad342dd4c6": "Connect integrations",
|
||||
"06fe30fdb0": "Start an agent from a task in one click and keep PR status in view.",
|
||||
"eddc532e58": "Automate workspace setup",
|
||||
"56049b74c2": "Run install and setup commands automatically so every new worktree is ready for agents.",
|
||||
"2cf795433b": "Start work in multiple repos",
|
||||
"42525ba8a4": "Bring your key repos into Orca so you can start agent work without hunting for folders."
|
||||
"42525ba8a4": "Bring your key repos into Orca so you can start agent work without hunting for folders.",
|
||||
"agentSkillsName": "Give agents Orca skills",
|
||||
"agentSkillsDescription": "Install the skills agents use to drive Orca's browser, control your computer, and coordinate multi-step work."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14743,8 +14709,7 @@
|
||||
"d6d15077df": "Skill command copied and inserted below for review.",
|
||||
"78e65f19d9": "Browser setup failed",
|
||||
"b7345c18db": "An unexpected error occurred.",
|
||||
"5f97caf76b": "Installing…",
|
||||
"c2df599513": "Install CLI & Skill"
|
||||
"5f97caf76b": "Installing…"
|
||||
},
|
||||
"ConnectIntegrationsList": {
|
||||
"3dddb2d565": "connected for tasks",
|
||||
@@ -14886,17 +14851,9 @@
|
||||
"agent": {
|
||||
"setup": {
|
||||
"state": {
|
||||
"fdcca1ec75": "Registering...",
|
||||
"69fb2c2289": "Enabled",
|
||||
"c6705092ba": "Fix PATH",
|
||||
"7c1b6bdb1e": "Enable",
|
||||
"51074ccb05": "Failed to load CLI status.",
|
||||
"35dea1ae12": "Agent control is ready.",
|
||||
"9dff3a6338": "Skill is installed. Enable the Orca CLI to finish setup.",
|
||||
"15986a1080": "Orca CLI is ready. Install the skill to finish setup.",
|
||||
"4c26913def": "Still not set up. Complete both steps to enable agent control.",
|
||||
"c94ff11e91": "Could not re-check setup status.",
|
||||
"2b519eed94": "Registered the Orca CLI in PATH."
|
||||
"skillNotInstalled": "The Orca CLI skill is not installed yet.",
|
||||
"c94ff11e91": "Could not re-check setup status."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -14914,23 +14871,18 @@
|
||||
"2fda9ff015": "Set up agent control",
|
||||
"0ac0fef514": "Agent control is ready.",
|
||||
"2bdfff8763": "Agent control (optional).",
|
||||
"72736b051f": "Set up Orca CLI + skill when you want agents to drive this simulator.",
|
||||
"d10ae98046": "Done",
|
||||
"3756cbeca7": "Not now",
|
||||
"6d950431d2": "Hide",
|
||||
"ebceac65a4": "Set up",
|
||||
"installSkillPrompt": "Install the Orca CLI skill when you want agents to drive this simulator.",
|
||||
"3f003507f4": "Open full setup in Settings"
|
||||
},
|
||||
"MobileEmulatorAgentSetupGuideSteps": {
|
||||
"9b49d892e3": "Enable Orca CLI",
|
||||
"3d8dc52c93": "Registers the orca command for emulator control in agent shells.",
|
||||
"21f5687c07": "Orca CLI skill",
|
||||
"64fb057667": "Teaches agents the orca emulator commands for this worktree.",
|
||||
"5c59ea96ca": "Mobile emulator Orca CLI skill setup",
|
||||
"bff5341ac3": "Mobile emulator Orca CLI skill install terminal",
|
||||
"3d34423e88": "Registering the Orca CLI",
|
||||
"3be27641c9": "so emulator commands can run from agent shells.",
|
||||
"3941719a56": "Checking Orca CLI before opening skill setup."
|
||||
"bff5341ac3": "Mobile emulator Orca CLI skill install terminal"
|
||||
},
|
||||
"MobileEmulatorTabIntroCallout": {
|
||||
"1924982130": "Dismiss",
|
||||
|
||||
@@ -5129,13 +5129,11 @@
|
||||
"b826a98b6f": "ocupado"
|
||||
},
|
||||
"LinearAgentSkillSetupPrompt": {
|
||||
"missingCliAndSkill": "Faltan la CLI de Orca y el skill de Linear para Agents.",
|
||||
"modalTitle": "Habilitar acceso a issues de Linear",
|
||||
"modalDescription": "Instala el skill de Linear desde una terminal.",
|
||||
"modalPrompt": "Permite que los Agents lean y editen el issue de Linear adjunto.",
|
||||
"dontShowAgain": "No volver a mostrar",
|
||||
"missingBoth": "Faltan la CLI de Orca y el skill de Linear para Agents.",
|
||||
"missingCli": "Falta la CLI de Orca.",
|
||||
"missingSkill": "Falta el skill de Linear para Agents.",
|
||||
"title": "Configurar skill de Linear para Agents",
|
||||
"remoteCopy": "Esto instala el setup del host; los entornos de Agents remotos pueden necesitar setup propio.",
|
||||
@@ -5147,7 +5145,6 @@
|
||||
"panelDescription": "Instala el skill de Agents del host para handoffs desde tareas de Linear enlazadas.",
|
||||
"terminalTitle": "Instalar skill de Linear para Agents",
|
||||
"terminalAria": "Terminal de instalación del skill de Linear para Agents",
|
||||
"install": "Instalar CLI y skill",
|
||||
"successTitle": "El acceso a issues de Linear está listo",
|
||||
"successDescription": "Los Agents ya pueden leer y actualizar issues de Linear enlazados desde este espacio de trabajo.",
|
||||
"successDescriptionWsl": "Los Agents de WSL ya pueden usar issues de Linear enlazados desde este espacio de trabajo.",
|
||||
@@ -5156,11 +5153,7 @@
|
||||
"done": "Listo",
|
||||
"wslCopy": "Instálalo para handoffs de Agents en WSL desde trabajo de Linear enlazado.",
|
||||
"wslLabel": "WSL predeterminado",
|
||||
"toastMissingCliAndSkill": "Faltan la CLI de Orca y el skill de Linear",
|
||||
"toastMissingCli": "Falta la CLI de Orca",
|
||||
"toastMissingSkill": "Falta el skill de Linear",
|
||||
"toastInstallCliAndSkillDescription": "Instala la CLI de Orca y el skill de Linear para permitir que tus Agents lean y editen tareas de Linear.",
|
||||
"toastInstallCliDescription": "Instala la CLI de Orca para permitir que tus Agents lean y editen tareas de Linear.",
|
||||
"toastInstallSkillDescription": "Instala el skill de Linear para permitir que tus Agents lean y editen tareas de Linear a través de la CLI de Orca.",
|
||||
"toastRemoteDescription": "{{value0}} Los entornos de Agents remotos pueden necesitar setup propio.",
|
||||
"toastWslDescription": "{{value0}} Este setup se ejecuta en el entorno de Agent WSL seleccionado."
|
||||
@@ -5416,7 +5409,6 @@
|
||||
"SetupGuideModal": {
|
||||
"3598a3ca0c": "Completa los flujos de trabajo principales que hacen útil a Orca para trabajar con agentes en paralelo.",
|
||||
"48a9e5ef2d": "Primeros pasos",
|
||||
"28cf59fcb4": "Esto ocultará el checklist de la barra lateral.",
|
||||
"f3b5ffb2a6": "Ocultar checklist de la barra lateral"
|
||||
},
|
||||
"SetupGuideProgressRing": {
|
||||
@@ -5872,24 +5864,15 @@
|
||||
"af8c83ed61": "Importa cookies de Chrome, Edge u otros navegadores para que los agents puedan reutilizar tus logins.",
|
||||
"68ea76eb71": "Instala el skill Browser Use para que los agents puedan operar el navegador de Orca.",
|
||||
"2d6ead9ab2": "Instalar skill Browser Use",
|
||||
"e9f3f3b488": "Instalado en",
|
||||
"9fca1f7f5d": "Registra el comando Orca CLI para que los agents puedan orquestar el navegador desde su shell.",
|
||||
"c6065d205d": "Activar Orca CLI",
|
||||
"c79eff0213": "Registra Orca CLI para que los agents puedan controlar el navegador.",
|
||||
"702488a5f7": "Deja que los coding agents controlen este navegador con tus logins. Completa los tres pasos de abajo.",
|
||||
"b8a1f2d84d": "Agent Browser Use",
|
||||
"96b91c6349": "Deja que los coding agents controlen este navegador con tus logins.",
|
||||
"2ea4617e3a": "Se importaron {{value0}} cookies de {{value1}}{{value2}}.",
|
||||
"721aee31b4": "Se registró Orca CLI en PATH.",
|
||||
"180a9abf3a": "No se pudo cargar el estado de CLI.",
|
||||
"2ccfc9cff8": "Importar",
|
||||
"0462565413": "Reimportar",
|
||||
"de9b2f32f3": "Activar",
|
||||
"ad8cb0ee22": "Corregir PATH",
|
||||
"0289434ed6": "Activado",
|
||||
"8b3054dac7": "Registrando...",
|
||||
"8f2675c2f3": "Se importaron {{value0}} cookies desde un archivo.",
|
||||
"5301857d88": "Desde {{value0}}"
|
||||
"5301857d88": "Desde {{value0}}",
|
||||
"finishSteps": "Permite que los agentes de programación usen este navegador con tus sesiones iniciadas. Completa los pasos siguientes."
|
||||
},
|
||||
"BrowserUseSkillStep": {
|
||||
"0871b6998d": "Permite a los agents navegar y verificar páginas en el navegador de Orca.",
|
||||
@@ -5910,7 +5893,6 @@
|
||||
"5dae812f50": "Actualizar",
|
||||
"52e640f3a0": "Actualizar estado de la CLI",
|
||||
"38edbb5721": "Comando de shell",
|
||||
"6930feda9e": "Usa Orca desde tu terminal para abrir la app, administrar worktrees e interactuar con terminales de Orca.",
|
||||
"c5c0f2641d": "Orca CLI",
|
||||
"d77352f2df": "No se pudo eliminar `{{value0}}` de PATH.",
|
||||
"af5540930c": "Se eliminó `{{value0}}` de PATH.",
|
||||
@@ -5934,7 +5916,8 @@
|
||||
"cbe55e4d48": "Mostrar en el Explorador",
|
||||
"9fd4023db0": "Mostrar en el administrador de archivos",
|
||||
"installFailureUnknownReason": "Orca no pudo completar el registro de CLI y no reportó ninguna razón.",
|
||||
"installFailureConflictRemedy": "Elimina {{value0}} y regístrate nuevamente si ya no es necesario."
|
||||
"installFailureConflictRemedy": "Elimina {{value0}} y regístrate nuevamente si ya no es necesario.",
|
||||
"outsideOrcaDescription": "Las terminales de Orca ya incluyen `orca`. Actívalo para usar `orca` desde otras terminales fuera de Orca."
|
||||
},
|
||||
"CliSkillRuntimeSetup": {
|
||||
"04325573f8": "WSL",
|
||||
@@ -6467,15 +6450,9 @@
|
||||
"c7f3fe0a6e": "Comandos comunes del emulador",
|
||||
"d94ca6a623": "Permite a los agentes usar comandos de la CLI de Orca, incluido el control del emulador móvil.",
|
||||
"67e19ee03c": "Skill Orca CLI",
|
||||
"aaf62a3dd2": "Instalado en",
|
||||
"2fef055608": "Registra el comando de la CLI de Orca para que los agentes puedan controlar el emulador activo desde su shell.",
|
||||
"4f2205f3b6": "Habilitar CLI de Orca",
|
||||
"ff4b7e65d6": "Permite que los agentes de codificación controlen el emulador móvil activo con comandos de la CLI de Orca.",
|
||||
"2a674aa810": "Control del emulador móvil para agentes",
|
||||
"cdeaed9e37": "Registré Orca CLI en PATH.",
|
||||
"3d34423e88": "Registrando la CLI de Orca",
|
||||
"3be27641c9": "para que los comandos del emulador se puedan ejecutar desde shells de agentes.",
|
||||
"3941719a56": "Comprobando la CLI de Orca antes de abrir la configuración de la skill."
|
||||
"cdeaed9e37": "Registré Orca CLI en PATH."
|
||||
},
|
||||
"MobileEmulatorExamples": {
|
||||
"edf13dd03b": "Copiar",
|
||||
@@ -8202,16 +8179,7 @@
|
||||
"9d97446873": "agent",
|
||||
"a2d489263e": "skill",
|
||||
"a7e82445fa": "Instala el skill Browser Use para que los agentes puedan controlar el navegador de Orca.",
|
||||
"a1414dcefb": "Instalar skill Browser Use",
|
||||
"e56c7b55c9": "configuración",
|
||||
"034c5e8d7f": "habilitar",
|
||||
"7e0dcb257a": "shell",
|
||||
"3ffafc9b95": "comando",
|
||||
"30c74aaa1f": "PATH",
|
||||
"ff05cbc344": "orca",
|
||||
"85fab5e12c": "CLI",
|
||||
"890ddf943d": "Registra la CLI de Orca para que los agentes puedan controlar el navegador.",
|
||||
"50f0860e18": "Habilitar CLI de Orca"
|
||||
"a1414dcefb": "Instalar skill Browser Use"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -11891,9 +11859,8 @@
|
||||
"1eee1c7bd8": "No se detectaron agentes en tu PATH. Elige uno para instalarlo más tarde o continúa con una terminal en blanco.",
|
||||
"hideAgents": "Ocultar agentes",
|
||||
"showMoreAgents": "Mostrar {{value0}} agentes más→",
|
||||
"yoloPermissionsLabel": "Yolo / Omitir permisos de forma peligrosa",
|
||||
"yoloPermissionsInfo": "Información de permisos del agente",
|
||||
"yoloPermissionsTooltip": "Omite las comprobaciones de permisos de los agentes para reducir interrupciones"
|
||||
"yoloModeLabel": "Modo Yolo",
|
||||
"yoloPermissionsDescription": "Los agentes ejecutan comandos y editan archivos sin preguntar, y algunos también omiten su sandbox. Úsalo solo en proyectos de confianza."
|
||||
},
|
||||
"FeatureSetupInlineTerminal": {
|
||||
"789b59936e": "Pulsa Enter para ejecutar el comando y confirma npx si se te solicita. También puedes configurarlo más tarde en Configuración.",
|
||||
@@ -11928,7 +11895,6 @@
|
||||
"NotificationStep": {
|
||||
"3bede04483": "Enviar notificación de prueba",
|
||||
"dc897423e1": "Elige sonido de notificación",
|
||||
"53aaffe49a": "Sonido de notificación",
|
||||
"0fe570690c": "Elija la alerta que Orca reproduce después de que se envía una notificación de escritorio.",
|
||||
"0af746e41f": "Elige un sonido",
|
||||
"8124d085a6": "Abra la configuración de Mac",
|
||||
@@ -12411,7 +12377,8 @@
|
||||
"3a59452a67": "Comando de skill copiado e insertado abajo para revisarlo.",
|
||||
"c605f51f2b": "Configuración de capacidades lista",
|
||||
"1aa657d8f4": "La configuración de algunas capacidades necesita atención",
|
||||
"c89534cbe9": "Instalar CLI y skills"
|
||||
"allInstalled": "Todas las skills instaladas",
|
||||
"installSkills": "Instalar skills"
|
||||
},
|
||||
"AiCommitPrSettingsCard": {
|
||||
"8d4152701a": "p. ej. ollama run llama3.1 {{value0}}",
|
||||
@@ -12504,7 +12471,6 @@
|
||||
"1a6a7d6c80": "Configuración",
|
||||
"713cc529a5": "Hitos",
|
||||
"b1f1981c5e": "Ver tareas",
|
||||
"0235b268b2": "Aún no hecho",
|
||||
"13294d3405": "Hecho"
|
||||
},
|
||||
"FeatureWallSetupWorkflowActions": {
|
||||
@@ -12615,13 +12581,13 @@
|
||||
"setup": {
|
||||
"status": {
|
||||
"8eccfcb314": "Instalado",
|
||||
"21d4f79c93": "haz clic en Instalar CLI y skills para abrir la configuración de acceso de macOS",
|
||||
"5c9293e51a": "comprobando acceso a la app",
|
||||
"aae94eeb52": "Haz clic en Instalar CLI y skills",
|
||||
"aa8e143a2f": "No se pudo comprobar la instalación",
|
||||
"9b33e7fb13": "Comprobando instalación",
|
||||
"4c8e1f92a7": "abre Orca Desktop en este Mac",
|
||||
"6d2b0a84e1": "No disponible en esta compilación"
|
||||
"6d2b0a84e1": "No disponible en esta compilación",
|
||||
"needsMacAccess": "Requiere acceso de macOS",
|
||||
"notInstalled": "No instalada",
|
||||
"requiresOrcaDesktop": "Requiere Orca Desktop"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12634,6 +12600,17 @@
|
||||
"cc39a87288": "Conectado · Sistema predeterminado",
|
||||
"00087eecb2": "Conectado · {{value0}}"
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"checklist": {
|
||||
"localized": {
|
||||
"copy": {
|
||||
"workOnTwoTasks": "Trabaja en dos tareas a la vez",
|
||||
"agentSkillsName": "Da a los agentes las skills de Orca",
|
||||
"agentSkillsDescription": "Instala las skills que los agentes usan para manejar el navegador de Orca, controlar tu computadora y coordinar trabajo de varios pasos."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -12742,8 +12719,7 @@
|
||||
"d6d15077df": "Comando de skill copiado e insertado abajo para revisarlo.",
|
||||
"78e65f19d9": "La configuración del navegador falló",
|
||||
"b7345c18db": "Se produjo un error inesperado.",
|
||||
"5f97caf76b": "Instalando…",
|
||||
"c2df599513": "Instalar CLI y skill"
|
||||
"5f97caf76b": "Instalando…"
|
||||
},
|
||||
"ConnectIntegrationsList": {
|
||||
"3dddb2d565": "conectado para tareas",
|
||||
@@ -12885,17 +12861,9 @@
|
||||
"agent": {
|
||||
"setup": {
|
||||
"state": {
|
||||
"fdcca1ec75": "Registrando…",
|
||||
"69fb2c2289": "Habilitado",
|
||||
"c6705092ba": "Corregir PATH",
|
||||
"7c1b6bdb1e": "Habilitar",
|
||||
"51074ccb05": "No se pudo cargar el estado de la CLI.",
|
||||
"35dea1ae12": "El control de agentes está listo.",
|
||||
"9dff3a6338": "La skill está instalada. Habilita la CLI de Orca para completar la configuración.",
|
||||
"15986a1080": "La CLI de Orca está lista. Instala la skill para completar la configuración.",
|
||||
"4c26913def": "Aún no está configurado. Completa ambos pasos para habilitar el control de agentes.",
|
||||
"c94ff11e91": "No se pudo volver a verificar el estado de la configuración.",
|
||||
"2b519eed94": "Se registró la CLI de Orca en PATH."
|
||||
"skillNotInstalled": "La skill de Orca CLI aún no está instalada."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -12913,23 +12881,18 @@
|
||||
"2fda9ff015": "Configurar control de agentes",
|
||||
"0ac0fef514": "El control de agentes está listo.",
|
||||
"2bdfff8763": "Control de agentes (opcional).",
|
||||
"72736b051f": "Configura la CLI de Orca + skill cuando quieras que los agentes controlen este simulador.",
|
||||
"d10ae98046": "Listo",
|
||||
"3756cbeca7": "Ahora no",
|
||||
"6d950431d2": "Ocultar",
|
||||
"ebceac65a4": "Configurar",
|
||||
"3f003507f4": "Abrir configuración completa en Ajustes"
|
||||
"3f003507f4": "Abrir configuración completa en Ajustes",
|
||||
"installSkillPrompt": "Instala la skill de Orca CLI cuando quieras que los agentes controlen este simulador."
|
||||
},
|
||||
"MobileEmulatorAgentSetupGuideSteps": {
|
||||
"9b49d892e3": "Habilitar la CLI de Orca",
|
||||
"3d8dc52c93": "Registra el comando orca para controlar el emulador en los shells de los agentes.",
|
||||
"21f5687c07": "Skill del CLI de Orca",
|
||||
"64fb057667": "Enseña a los agentes los comandos orca del emulador para este worktree.",
|
||||
"5c59ea96ca": "Configuración de la skill de Orca CLI para Mobile Emulator",
|
||||
"bff5341ac3": "Terminal de instalación de la skill de Orca CLI para Mobile Emulator",
|
||||
"3d34423e88": "Registrando la CLI de Orca",
|
||||
"3be27641c9": "para que los comandos del emulador se puedan ejecutar desde shells de agentes.",
|
||||
"3941719a56": "Verificando la CLI de Orca antes de abrir la configuración de la skill."
|
||||
"bff5341ac3": "Terminal de instalación de la skill de Orca CLI para Mobile Emulator"
|
||||
},
|
||||
"MobileEmulatorTabIntroCallout": {
|
||||
"1924982130": "Descartar",
|
||||
|
||||
@@ -6070,13 +6070,11 @@
|
||||
"6e7f8a9b0c": "Retirer l'hôte…"
|
||||
},
|
||||
"LinearAgentSkillSetupPrompt": {
|
||||
"missingCliAndSkill": "La CLI Orca et le skill d'agent Linear sont manquants.",
|
||||
"modalTitle": "Activer l'accès aux tickets Linear",
|
||||
"modalDescription": "Installez le skill Linear depuis un terminal.",
|
||||
"modalPrompt": "Permet aux agents de lire et de modifier le ticket Linear attaché.",
|
||||
"dontShowAgain": "Ne plus afficher",
|
||||
"missingBoth": "La CLI Orca et le skill d'agent Linear sont manquants.",
|
||||
"missingCli": "La CLI Orca est manquante.",
|
||||
"missingSkill": "Le skill d'agent Linear est manquant.",
|
||||
"title": "Configurer le skill d'agent Linear",
|
||||
"remoteCopy": "Ceci installe la configuration côté hôte ; les environnements d'agents distants peuvent nécessiter une configuration séparée.",
|
||||
@@ -6088,7 +6086,6 @@
|
||||
"panelDescription": "Installez le skill d'agent hôte pour les passations de tâches Linear liées.",
|
||||
"terminalTitle": "Installer le skill d'agent Linear",
|
||||
"terminalAria": "Terminal d'installation du skill d'agent Linear",
|
||||
"install": "Installer la CLI et le skill",
|
||||
"successTitle": "L'accès aux tickets Linear est prêt",
|
||||
"successDescription": "Les agents peuvent désormais lire et mettre à jour les tickets Linear liés depuis cet espace de travail.",
|
||||
"successDescriptionWsl": "Les agents WSL peuvent désormais utiliser les tickets Linear liés depuis cet espace de travail.",
|
||||
@@ -6097,11 +6094,7 @@
|
||||
"done": "Terminé",
|
||||
"wslCopy": "Installez-le pour les passations d'agents WSL depuis le travail Linear lié.",
|
||||
"wslLabel": "WSL par défaut",
|
||||
"toastMissingCliAndSkill": "La CLI Orca et le skill Linear sont manquants",
|
||||
"toastMissingCli": "La CLI Orca est manquante",
|
||||
"toastMissingSkill": "Le skill Linear est manquant",
|
||||
"toastInstallCliAndSkillDescription": "Installez la CLI Orca et le skill Linear pour permettre à vos agents de lire et de modifier les tâches Linear.",
|
||||
"toastInstallCliDescription": "Installez la CLI Orca pour permettre à vos agents de lire et de modifier les tâches Linear.",
|
||||
"toastInstallSkillDescription": "Installez le skill Linear pour permettre à vos agents de lire et de modifier les tâches Linear via la CLI Orca.",
|
||||
"toastRemoteDescription": "{{value0}} Les environnements d'agents distants peuvent nécessiter leur propre configuration.",
|
||||
"toastWslDescription": "{{value0}} Cette configuration s'exécute dans le runtime d'agent WSL sélectionné."
|
||||
@@ -6463,7 +6456,6 @@
|
||||
"SetupGuideModal": {
|
||||
"3598a3ca0c": "Terminez les workflows essentiels qui rendent Orca utile au travail parallèle avec des agents.",
|
||||
"48a9e5ef2d": "Premiers pas",
|
||||
"28cf59fcb4": "Cela masquera la checklist de la barre latérale",
|
||||
"f3b5ffb2a6": "Masquer la checklist de la barre latérale"
|
||||
},
|
||||
"SetupGuideProgressRing": {
|
||||
@@ -6952,24 +6944,15 @@
|
||||
"af8c83ed61": "Importez les cookies depuis Chrome, Edge ou d'autres navigateurs pour que les agents réutilisent vos connexions.",
|
||||
"68ea76eb71": "Installez la skill Browser Use pour que les agents pilotent le navigateur d'Orca.",
|
||||
"2d6ead9ab2": "Installer la skill Browser Use",
|
||||
"e9f3f3b488": "Installé dans",
|
||||
"9fca1f7f5d": "Enregistre la commande CLI d'Orca pour que les agents orchestrent le navigateur depuis leur shell.",
|
||||
"c6065d205d": "Activer Orca CLI",
|
||||
"c79eff0213": "Enregistrer Orca CLI pour que les agents pilotent le navigateur.",
|
||||
"702488a5f7": "Permettez aux agents de code de piloter ce navigateur avec vos connexions. Terminez les trois étapes ci-dessous.",
|
||||
"b8a1f2d84d": "Agent Browser Use",
|
||||
"96b91c6349": "Permettez aux agents de code de piloter ce navigateur avec vos connexions.",
|
||||
"2ea4617e3a": "{{value0}} cookies importés depuis {{value1}}{{value2}}.",
|
||||
"721aee31b4": "Orca CLI enregistré dans PATH.",
|
||||
"180a9abf3a": "Échec du chargement de l'état de la CLI.",
|
||||
"2ccfc9cff8": "Importer",
|
||||
"0462565413": "Réimporter",
|
||||
"de9b2f32f3": "Activer",
|
||||
"ad8cb0ee22": "Corriger PATH",
|
||||
"0289434ed6": "Activé",
|
||||
"8b3054dac7": "Enregistrement...",
|
||||
"8f2675c2f3": "{{value0}} cookies importés depuis un fichier.",
|
||||
"5301857d88": "Depuis {{value0}}"
|
||||
"5301857d88": "Depuis {{value0}}",
|
||||
"finishSteps": "Laissez les agents de code piloter ce navigateur avec vos connexions. Suivez les étapes ci-dessous."
|
||||
},
|
||||
"BrowserUseSkillStep": {
|
||||
"0871b6998d": "Permet aux agents de naviguer et de vérifier des pages dans le navigateur d'Orca.",
|
||||
@@ -6988,7 +6971,6 @@
|
||||
"5dae812f50": "Actualiser",
|
||||
"52e640f3a0": "Actualiser l'état de la CLI",
|
||||
"38edbb5721": "Commande shell",
|
||||
"6930feda9e": "Utilisez Orca depuis votre terminal pour ouvrir l'app, gérer les worktrees et interagir avec les terminaux Orca.",
|
||||
"c5c0f2641d": "Orca CLI",
|
||||
"d77352f2df": "Échec de la suppression de `{{value0}}` de PATH.",
|
||||
"af5540930c": "`{{value0}}` supprimé de PATH.",
|
||||
@@ -7014,7 +6996,8 @@
|
||||
"cbe55e4d48": "Afficher dans l'Explorateur",
|
||||
"9fd4023db0": "Afficher dans le Gestionnaire de fichiers",
|
||||
"installFailureUnknownReason": "Orca n'a pas pu terminer l'enregistrement de l'interface CLI et n'a signalé aucune raison.",
|
||||
"installFailureConflictRemedy": "Supprimez {{value0}} s'il n'est plus nécessaire, puis réenregistrez la CLI."
|
||||
"installFailureConflictRemedy": "Supprimez {{value0}} s'il n'est plus nécessaire, puis réenregistrez la CLI.",
|
||||
"outsideOrcaDescription": "Les terminaux Orca disposent déjà de `orca`. Activez cette option pour utiliser `orca` depuis d'autres terminaux en dehors d'Orca."
|
||||
},
|
||||
"CliSkillRuntimeSetup": {
|
||||
"04325573f8": "WSL",
|
||||
@@ -7632,15 +7615,9 @@
|
||||
"c7f3fe0a6e": "Commandes d'émulateur courantes",
|
||||
"d94ca6a623": "Permet aux agents d'utiliser les commandes CLI d'Orca, y compris le contrôle de l'émulateur mobile.",
|
||||
"67e19ee03c": "Compétence CLI Orca",
|
||||
"aaf62a3dd2": "Installé dans",
|
||||
"2fef055608": "Enregistre la commande CLI Orca pour que les agents puissent contrôler l'émulateur actif depuis leur shell.",
|
||||
"4f2205f3b6": "Activer Orca CLI",
|
||||
"ff4b7e65d6": "Laisser les agents de codage contrôler l'émulateur mobile actif avec des commandes CLI Orca.",
|
||||
"2a674aa810": "Contrôle de l'émulateur mobile par les agents",
|
||||
"cdeaed9e37": "Orca CLI enregistré dans PATH.",
|
||||
"3d34423e88": "Enregistrement de la CLI Orca",
|
||||
"3be27641c9": "afin que les commandes d'émulateur puissent s'exécuter depuis les shells des agents.",
|
||||
"3941719a56": "Vérification de la CLI Orca avant d'ouvrir la configuration du skill."
|
||||
"cdeaed9e37": "Orca CLI enregistré dans PATH."
|
||||
},
|
||||
"MobileEmulatorExamples": {
|
||||
"edf13dd03b": "Copier",
|
||||
@@ -9477,16 +9454,7 @@
|
||||
"9d97446873": "agent",
|
||||
"a2d489263e": "skill",
|
||||
"a7e82445fa": "Installez la skill Browser Use pour que les agents pilotent le navigateur d'Orca.",
|
||||
"a1414dcefb": "Installer la skill Browser Use",
|
||||
"e56c7b55c9": "configuration",
|
||||
"034c5e8d7f": "activer",
|
||||
"7e0dcb257a": "shell",
|
||||
"3ffafc9b95": "command",
|
||||
"30c74aaa1f": "chemin",
|
||||
"ff05cbc344": "orca",
|
||||
"85fab5e12c": "cli",
|
||||
"890ddf943d": "Enregistrer Orca CLI pour que les agents pilotent le navigateur.",
|
||||
"50f0860e18": "Activer Orca CLI"
|
||||
"a1414dcefb": "Installer la skill Browser Use"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -13751,9 +13719,8 @@
|
||||
"1eee1c7bd8": "Aucun agent détecté dans votre PATH. Choisissez-en un à installer plus tard, ou continuez avec un terminal vide.",
|
||||
"hideAgents": "Masquer les agents",
|
||||
"showMoreAgents": "Afficher {{value0}} autres agents→",
|
||||
"yoloPermissionsLabel": "Yolo / Ignorer dangereusement les permissions",
|
||||
"yoloPermissionsInfo": "Informations sur les permissions des agents",
|
||||
"yoloPermissionsTooltip": "Ignorer les vérifications de permissions des agents pour moins d'interruptions"
|
||||
"yoloModeLabel": "Mode Yolo",
|
||||
"yoloPermissionsDescription": "Les agents exécutent des commandes et modifient des fichiers sans demander, et certains contournent aussi leur sandbox. À utiliser uniquement dans des projets de confiance."
|
||||
},
|
||||
"FeatureSetupInlineTerminal": {
|
||||
"789b59936e": "Appuyez sur Entrée pour exécuter la commande et confirmez npx si demandé. Vous pouvez aussi configurer cela plus tard dans les paramètres.",
|
||||
@@ -13788,7 +13755,6 @@
|
||||
"NotificationStep": {
|
||||
"3bede04483": "Envoyer une notification de test",
|
||||
"dc897423e1": "Choisir le son de notification",
|
||||
"53aaffe49a": "Son de notification",
|
||||
"0fe570690c": "Choisissez l'alerte jouée par Orca après l'envoi d'une notification bureau.",
|
||||
"0af746e41f": "Choisir un son",
|
||||
"8124d085a6": "Ouvrir les réglages Mac",
|
||||
@@ -14312,7 +14278,8 @@
|
||||
"3a59452a67": "Commande de skill copiée et insérée ci-dessous pour vérification.",
|
||||
"c605f51f2b": "Configuration des capacités prête",
|
||||
"1aa657d8f4": "Certaines configurations de capacités demandent votre attention",
|
||||
"c89534cbe9": "Installer CLI & Skills"
|
||||
"allInstalled": "Toutes les skills sont installées",
|
||||
"installSkills": "Installer les skills"
|
||||
},
|
||||
"AiCommitPrSettingsCard": {
|
||||
"8d4152701a": "ex. ollama run llama3.1 {{value0}}",
|
||||
@@ -14405,7 +14372,6 @@
|
||||
"1a6a7d6c80": "Configuration",
|
||||
"713cc529a5": "Jalons",
|
||||
"b1f1981c5e": "Voir les tâches",
|
||||
"0235b268b2": "Pas encore terminé",
|
||||
"13294d3405": "Terminé"
|
||||
},
|
||||
"FeatureWallSetupWorkflowActions": {
|
||||
@@ -14516,13 +14482,13 @@
|
||||
"setup": {
|
||||
"status": {
|
||||
"8eccfcb314": "Installés",
|
||||
"21d4f79c93": "cliquez sur Installer CLI & Skills pour ouvrir les réglages d'accès macOS",
|
||||
"5c9293e51a": "vérification de l'accès à l'app",
|
||||
"aae94eeb52": "Cliquez sur Installer CLI & Skills",
|
||||
"aa8e143a2f": "Impossible de vérifier l'installation",
|
||||
"9b33e7fb13": "Vérification de l'installation",
|
||||
"4c8e1f92a7": "ouvrez Orca Desktop sur ce Mac",
|
||||
"6d2b0a84e1": "Indisponible dans ce build"
|
||||
"6d2b0a84e1": "Indisponible dans ce build",
|
||||
"needsMacAccess": "Nécessite l'accès macOS",
|
||||
"notInstalled": "Non installée",
|
||||
"requiresOrcaDesktop": "Nécessite Orca Desktop"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14540,7 +14506,6 @@
|
||||
"checklist": {
|
||||
"localized": {
|
||||
"copy": {
|
||||
"ec0a363633": "Multitâche",
|
||||
"62bac8f43c": "Travaillez dans 2 arbres de travail différents à la fois. Chacun est isolé (même dans le même projet). Parfait pour travailler sur 2 fonctionnalités à la fois.",
|
||||
"908898c3ee": "Utiliser le navigateur d'Orca",
|
||||
"43781563c3": "Parcourez votre application Web sans quitter Orca. Saisissez n'importe quel élément et envoyez sa source exacte et ses styles à un agent en un seul clic.",
|
||||
@@ -14548,14 +14513,15 @@
|
||||
"71bd9a8c95": "Sachez à quel moment un agent termine son travail, a besoin d'attention ou est bloqué.",
|
||||
"46db810da8": "Choisissez votre agent par défaut",
|
||||
"b8e5bae17f": "Démarrez un nouveau travail plus rapidement avec votre agent préféré déjà sélectionné.",
|
||||
"fee5557b02": "Activer la CLI Orca",
|
||||
"7bcb4097fa": "Enregistrez la commande shell Orca et installez les compétences d'agent pour les workflows de navigateur, d'ordinateur et d'orchestration.",
|
||||
"ad342dd4c6": "Connecter les intégrations",
|
||||
"06fe30fdb0": "Démarrez un agent à partir d'une tâche en un clic et gardez l'état des relations publiques en vue.",
|
||||
"eddc532e58": "Automatisez la configuration de l'espace de travail",
|
||||
"56049b74c2": "Exécutez automatiquement les commandes d'installation et de configuration afin que chaque nouvelle arborescence de travail soit prête pour les agents.",
|
||||
"2cf795433b": "Commencez à travailler dans plusieurs dépôts",
|
||||
"42525ba8a4": "Importez vos dépôts de clés dans Orca afin de pouvoir démarrer le travail d'agent sans rechercher de dossiers."
|
||||
"42525ba8a4": "Importez vos dépôts de clés dans Orca afin de pouvoir démarrer le travail d'agent sans rechercher de dossiers.",
|
||||
"workOnTwoTasks": "Travaillez sur deux tâches à la fois",
|
||||
"agentSkillsName": "Donnez aux agents les skills Orca",
|
||||
"agentSkillsDescription": "Installez les skills que les agents utilisent pour piloter le navigateur d'Orca, contrôler votre ordinateur et coordonner des tâches en plusieurs étapes."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14668,8 +14634,7 @@
|
||||
"d6d15077df": "Commande de skill copiée et insérée ci-dessous pour vérification.",
|
||||
"78e65f19d9": "Échec de la configuration du navigateur",
|
||||
"b7345c18db": "Une erreur inattendue s'est produite.",
|
||||
"5f97caf76b": "Installation…",
|
||||
"c2df599513": "Installer la CLI et le skill"
|
||||
"5f97caf76b": "Installation…"
|
||||
},
|
||||
"ConnectIntegrationsList": {
|
||||
"3dddb2d565": "connecté pour les tâches",
|
||||
@@ -14811,17 +14776,9 @@
|
||||
"agent": {
|
||||
"setup": {
|
||||
"state": {
|
||||
"fdcca1ec75": "Enregistrement...",
|
||||
"69fb2c2289": "Activé",
|
||||
"c6705092ba": "Corriger PATH",
|
||||
"7c1b6bdb1e": "Activer",
|
||||
"51074ccb05": "Échec du chargement de l'état de la CLI.",
|
||||
"35dea1ae12": "Le contrôle des agents est prêt.",
|
||||
"9dff3a6338": "La skill est installée. Activez l'Orca CLI pour terminer la configuration.",
|
||||
"15986a1080": "L'Orca CLI est prêt. Installez la skill pour terminer la configuration.",
|
||||
"4c26913def": "Toujours pas configuré. Terminez les deux étapes pour activer le contrôle des agents.",
|
||||
"c94ff11e91": "Impossible de revérifier l'état de la configuration.",
|
||||
"2b519eed94": "Orca CLI enregistré dans PATH."
|
||||
"skillNotInstalled": "La skill Orca CLI n'est pas encore installée."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -14839,23 +14796,18 @@
|
||||
"2fda9ff015": "Configurer le contrôle des agents",
|
||||
"0ac0fef514": "Le contrôle des agents est prêt.",
|
||||
"2bdfff8763": "Contrôle des agents (facultatif).",
|
||||
"72736b051f": "Configurez Orca CLI + skill quand vous voulez que des agents pilotent ce simulateur.",
|
||||
"d10ae98046": "Terminé",
|
||||
"3756cbeca7": "Pas maintenant",
|
||||
"6d950431d2": "Masquer",
|
||||
"ebceac65a4": "Configurer",
|
||||
"3f003507f4": "Ouvrir la configuration complète dans les paramètres"
|
||||
"3f003507f4": "Ouvrir la configuration complète dans les paramètres",
|
||||
"installSkillPrompt": "Installez la skill Orca CLI lorsque vous voulez que les agents pilotent ce simulateur."
|
||||
},
|
||||
"MobileEmulatorAgentSetupGuideSteps": {
|
||||
"9b49d892e3": "Activer Orca CLI",
|
||||
"3d8dc52c93": "Enregistre la commande orca de contrôle de l'émulateur dans les shells des agents.",
|
||||
"21f5687c07": "Compétence CLI Orca",
|
||||
"64fb057667": "Apprend aux agents les commandes orca de l'émulateur pour ce worktree.",
|
||||
"5c59ea96ca": "Configuration de la skill Orca CLI de l'émulateur mobile",
|
||||
"bff5341ac3": "Terminal d'installation de la skill Orca CLI de l'émulateur mobile",
|
||||
"3d34423e88": "Enregistrement de la CLI Orca",
|
||||
"3be27641c9": "afin que les commandes d'émulateur puissent s'exécuter depuis les shells des agents.",
|
||||
"3941719a56": "Vérification de la CLI Orca avant d'ouvrir la configuration du skill."
|
||||
"bff5341ac3": "Terminal d'installation de la skill Orca CLI de l'émulateur mobile"
|
||||
},
|
||||
"MobileEmulatorTabIntroCallout": {
|
||||
"1924982130": "Ignorer",
|
||||
|
||||
@@ -5928,13 +5928,11 @@
|
||||
}
|
||||
},
|
||||
"LinearAgentSkillSetupPrompt": {
|
||||
"missingCliAndSkill": "Orca CLI と Linear Agent スキルがありません。",
|
||||
"modalTitle": "Linear チケットアクセスを有効にする",
|
||||
"modalDescription": "ターミナルから Linear スキルをインストールします。",
|
||||
"modalPrompt": "Agent が添付された Linear チケットを読み書きできるようにします。",
|
||||
"dontShowAgain": "今後表示しない",
|
||||
"missingBoth": "Orca CLI と Linear Agent スキルがありません。",
|
||||
"missingCli": "Orca CLI がありません。",
|
||||
"missingSkill": "Linear Agent スキルがありません。",
|
||||
"title": "Linear Agent スキルを設定",
|
||||
"remoteCopy": "これはホスト側の設定をインストールします。リモート Agent 環境では別途設定が必要な場合があります。",
|
||||
@@ -5946,7 +5944,6 @@
|
||||
"panelDescription": "リンクされた Linear タスクの引き継ぎ用にホスト Agent スキルをインストールします。",
|
||||
"terminalTitle": "Linear Agent スキルをインストール",
|
||||
"terminalAria": "Linear Agent スキルのインストールターミナル",
|
||||
"install": "CLI とスキルをインストール",
|
||||
"successTitle": "Linear チケットアクセスの準備ができました",
|
||||
"successDescription": "Agent はこのワークスペースからリンクされた Linear チケットを読み取り、更新できるようになりました。",
|
||||
"successDescriptionWsl": "WSL Agent はこのワークスペースからリンクされた Linear チケットを使用できるようになりました。",
|
||||
@@ -5955,11 +5952,7 @@
|
||||
"done": "完了",
|
||||
"wslCopy": "リンクされた Linear 作業からの WSL Agent 引き継ぎ用にインストールします。",
|
||||
"wslLabel": "WSL のデフォルト",
|
||||
"toastMissingCliAndSkill": "Orca CLI と Linear スキルがありません",
|
||||
"toastMissingCli": "Orca CLI がありません",
|
||||
"toastMissingSkill": "Linear スキルがありません",
|
||||
"toastInstallCliAndSkillDescription": "Orca CLI と Linear スキルをインストールして、Agent が Linear タスクを読み書きできるようにします。",
|
||||
"toastInstallCliDescription": "Orca CLI をインストールして、Agent が Linear タスクを読み書きできるようにします。",
|
||||
"toastInstallSkillDescription": "Linear スキルをインストールして、Agent が Orca CLI を通じて Linear タスクを読み書きできるようにします。",
|
||||
"toastRemoteDescription": "{{value0}} リモート Agent 環境では独自の設定が必要な場合があります。",
|
||||
"toastWslDescription": "{{value0}} この設定は選択した WSL Agent ランタイムで実行されます。"
|
||||
@@ -6360,7 +6353,6 @@
|
||||
"SetupGuideModal": {
|
||||
"3598a3ca0c": "Orca を並行 Agent 作業に活用するためのコアワークフローを完了します。",
|
||||
"48a9e5ef2d": "はじめる",
|
||||
"28cf59fcb4": "これにより、サイドバーからチェックリストが非表示になります",
|
||||
"f3b5ffb2a6": "サイドバーからチェックリストを非表示にする"
|
||||
},
|
||||
"SetupGuideProgressRing": {
|
||||
@@ -6834,24 +6826,15 @@
|
||||
"af8c83ed61": "Chrome、Edge、またはその他のブラウザから Cookie をインポートすると、Agent がログインを再利用できるようになります。",
|
||||
"68ea76eb71": "Browser Use スキルをインストールすると、Agent が Orca のブラウザを操作できるようになります。",
|
||||
"2d6ead9ab2": "ブラウザ使用スキルをインストールする",
|
||||
"e9f3f3b488": "設置場所",
|
||||
"9fca1f7f5d": "Orca CLI コマンドを登録して、Agent がシェルからブラウザを調整できるようにします。",
|
||||
"c6065d205d": "Orca CLI を有効化",
|
||||
"c79eff0213": "Agent がブラウザを操作できるように Orca CLI を登録します。",
|
||||
"702488a5f7": "コーディング Agent がログイン情報を使用してこのブラウザを操作できるようにします。以下の 3 つの手順を完了します。",
|
||||
"b8a1f2d84d": "Agent ブラウザの使用",
|
||||
"96b91c6349": "コーディング Agent がログイン情報を使用してこのブラウザを操作できるようにします。",
|
||||
"2ea4617e3a": "{{value1}}{{value2}} から {{value0}} Cookie をインポートしました。",
|
||||
"721aee31b4": "Orca CLI を PATH に登録しました。",
|
||||
"180a9abf3a": "CLI ステータスの読み込みに失敗しました。",
|
||||
"2ccfc9cff8": "インポート",
|
||||
"0462565413": "再インポート",
|
||||
"de9b2f32f3": "有効化",
|
||||
"ad8cb0ee22": "PATH を修正",
|
||||
"0289434ed6": "有効",
|
||||
"8b3054dac7": "登録中…",
|
||||
"8f2675c2f3": "ファイルから {{value0}} 個の Cookie をインポートしました。",
|
||||
"5301857d88": "{{value0}} から"
|
||||
"5301857d88": "{{value0}} から",
|
||||
"finishSteps": "コーディング Agent がログイン状態のままこのブラウザを操作できるようにします。以下の手順を完了してください。"
|
||||
},
|
||||
"BrowserUseSkillStep": {
|
||||
"0871b6998d": "Agent が Orca のブラウザでページに移動して確認できるようにします。",
|
||||
@@ -6872,7 +6855,6 @@
|
||||
"5dae812f50": "更新",
|
||||
"52e640f3a0": "CLI ステータスを更新する",
|
||||
"38edbb5721": "シェルコマンド",
|
||||
"6930feda9e": "ターミナルから Orca を使用してアプリを開き、ワークツリーを管理し、Orca ターミナルと対話します。",
|
||||
"c5c0f2641d": "Orca CLI",
|
||||
"d77352f2df": "PATH から {{value0}} を削除できませんでした。",
|
||||
"af5540930c": "PATH から {{value0}} を削除しました。",
|
||||
@@ -6896,7 +6878,8 @@
|
||||
"cbe55e4d48": "エクスプローラーで表示",
|
||||
"9fd4023db0": "ファイルマネージャーで表示",
|
||||
"installFailureUnknownReason": "Orca は CLI 登録を完了できず、理由を報告しませんでした。",
|
||||
"installFailureConflictRemedy": "{{value0}} を削除して、不要になった場合は再度登録してください。"
|
||||
"installFailureConflictRemedy": "{{value0}} を削除して、不要になった場合は再度登録してください。",
|
||||
"outsideOrcaDescription": "Orca のターミナルでは `orca` がすでに使えます。Orca 以外のターミナルから `orca` を使うにはオンにしてください。"
|
||||
},
|
||||
"CliSkillRuntimeSetup": {
|
||||
"04325573f8": "WSL",
|
||||
@@ -7519,15 +7502,9 @@
|
||||
"c7f3fe0a6e": "よく使うエミュレータコマンド",
|
||||
"d94ca6a623": "Agent がモバイルエミュレータ制御を含む Orca CLI コマンドを使用できるようにします。",
|
||||
"67e19ee03c": "Orca CLI スキル",
|
||||
"aaf62a3dd2": "設置場所",
|
||||
"2fef055608": "Orca CLI コマンドを登録して、Agent がシェルからアクティブなエミュレータを制御できるようにします。",
|
||||
"4f2205f3b6": "Orca CLI を有効化",
|
||||
"ff4b7e65d6": "コーディング Agent が Orca CLI コマンドを使用してアクティブなモバイルエミュレータを制御できるようにします。",
|
||||
"2a674aa810": "Agent モバイルエミュレータ制御",
|
||||
"cdeaed9e37": "Orca CLI を PATH に登録しました。",
|
||||
"3d34423e88": "Orca CLI を登録しています",
|
||||
"3be27641c9": "Agent のシェルからエミュレータコマンドを実行できるようにします。",
|
||||
"3941719a56": "スキル設定を開く前に Orca CLI を確認しています。"
|
||||
"cdeaed9e37": "Orca CLI を PATH に登録しました。"
|
||||
},
|
||||
"MobileEmulatorExamples": {
|
||||
"edf13dd03b": "コピー",
|
||||
@@ -9354,16 +9331,7 @@
|
||||
"9d97446873": "Agent",
|
||||
"a2d489263e": "スキル",
|
||||
"a7e82445fa": "Browser Use スキルをインストールすると、Agent が Orca のブラウザを操作できるようになります。",
|
||||
"a1414dcefb": "ブラウザ使用スキルをインストールする",
|
||||
"e56c7b55c9": "セットアップ",
|
||||
"034c5e8d7f": "有効化",
|
||||
"7e0dcb257a": "シェル",
|
||||
"3ffafc9b95": "コマンド",
|
||||
"30c74aaa1f": "パス",
|
||||
"ff05cbc344": "orca",
|
||||
"85fab5e12c": "CLI",
|
||||
"890ddf943d": "Agent がブラウザを操作できるように Orca CLI を登録します。",
|
||||
"50f0860e18": "Orca CLI を有効化"
|
||||
"a1414dcefb": "ブラウザ使用スキルをインストールする"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -13648,9 +13616,8 @@
|
||||
"1eee1c7bd8": "PATH 上に Agent が検出されませんでした。後でインストールするものを選択するか、空のターミナルを使用して続行します。",
|
||||
"hideAgents": "Agent を非表示にする",
|
||||
"showMoreAgents": "{{value0}} 件の Agent をさらに表示→",
|
||||
"yoloPermissionsLabel": "Yolo / 危険: 権限チェックをスキップ",
|
||||
"yoloPermissionsInfo": "Agent 権限情報",
|
||||
"yoloPermissionsTooltip": "Agent の権限チェックをスキップして割り込みを減らす"
|
||||
"yoloModeLabel": "Yolo モード",
|
||||
"yoloPermissionsDescription": "Agent は確認なしでコマンドを実行しファイルを編集します。一部はサンドボックスも回避します。信頼できるプロジェクトでのみ使用してください。"
|
||||
},
|
||||
"FeatureSetupInlineTerminal": {
|
||||
"789b59936e": "Enter キーを押してコマンドを実行し、要求されたら npx を確認します。これは、後で [設定] で設定することもできます。",
|
||||
@@ -13685,7 +13652,6 @@
|
||||
"NotificationStep": {
|
||||
"3bede04483": "テスト通知を送信する",
|
||||
"dc897423e1": "通知音を選択する",
|
||||
"53aaffe49a": "通知音",
|
||||
"0fe570690c": "デスクトップ通知が配信された後に Orca が再生するアラートを選択します。",
|
||||
"0af746e41f": "サウンドを選択",
|
||||
"8124d085a6": "Macの設定を開く",
|
||||
@@ -14209,7 +14175,8 @@
|
||||
"3a59452a67": "スキルコマンドをコピーし、確認用に以下へ挿入しました。",
|
||||
"c605f51f2b": "機能セットアップの準備完了",
|
||||
"1aa657d8f4": "一部の機能の設定には注意が必要です",
|
||||
"c89534cbe9": "CLIとスキルのインストール"
|
||||
"allInstalled": "すべてのスキルがインストール済み",
|
||||
"installSkills": "スキルをインストール"
|
||||
},
|
||||
"AiCommitPrSettingsCard": {
|
||||
"8d4152701a": "例: ollama run llama3.1 {{value0}}",
|
||||
@@ -14302,7 +14269,6 @@
|
||||
"1a6a7d6c80": "設定",
|
||||
"713cc529a5": "マイルストーン",
|
||||
"b1f1981c5e": "タスクを参照",
|
||||
"0235b268b2": "まだ終わっていない",
|
||||
"13294d3405": "完了"
|
||||
},
|
||||
"FeatureWallSetupWorkflowActions": {
|
||||
@@ -14413,13 +14379,13 @@
|
||||
"setup": {
|
||||
"status": {
|
||||
"8eccfcb314": "インストール済み",
|
||||
"21d4f79c93": "[CLI とスキルのインストール] をクリックして macOS アクセス設定を開きます",
|
||||
"5c9293e51a": "アプリのアクセスを確認する",
|
||||
"aae94eeb52": "「CLI とスキルのインストール」をクリックします",
|
||||
"aa8e143a2f": "インストールを確認できませんでした",
|
||||
"9b33e7fb13": "インストールを確認しています",
|
||||
"4c8e1f92a7": "この Mac で Orca Desktop を開いてください",
|
||||
"6d2b0a84e1": "このビルドでは利用できません"
|
||||
"6d2b0a84e1": "このビルドでは利用できません",
|
||||
"needsMacAccess": "macOS のアクセス許可が必要",
|
||||
"notInstalled": "未インストール",
|
||||
"requiresOrcaDesktop": "Orca Desktop が必要"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14437,7 +14403,6 @@
|
||||
"checklist": {
|
||||
"localized": {
|
||||
"copy": {
|
||||
"ec0a363633": "マルチタスク",
|
||||
"62bac8f43c": "2 つの異なるワークツリーで同時に作業します。それぞれは (同じプロジェクト内であっても) 分離されています。 2 つの機能を同時に作業するのに最適です。",
|
||||
"908898c3ee": "Orca のブラウザを使用する",
|
||||
"43781563c3": "Orca を離れることなく Web アプリを閲覧できます。任意の要素を取得し、ワンクリックでその正確なソースとスタイルを Agent に送信します。",
|
||||
@@ -14445,14 +14410,15 @@
|
||||
"71bd9a8c95": "Agent が終了した瞬間、注意が必要な瞬間、またはブロックされた瞬間を把握します。",
|
||||
"46db810da8": "デフォルトの Agent を選択してください",
|
||||
"b8e5bae17f": "優先 Agent がすでに選択されているため、新規作業をより迅速に開始できます。",
|
||||
"fee5557b02": "Orca CLI を有効化",
|
||||
"7bcb4097fa": "Orca シェルコマンドを登録し、ブラウザ、コンピュータ、およびオーケストレーションワークフローの Agent スキルをインストールします。",
|
||||
"ad342dd4c6": "接続連携",
|
||||
"06fe30fdb0": "ワンクリックでタスクから Agent を開始し、PR ステータスを表示したままにします。",
|
||||
"eddc532e58": "ワークスペースのセットアップを自動化する",
|
||||
"56049b74c2": "インストールおよびセットアップのコマンドを自動的に実行することで、すべての新規ワークツリーが Agent に対応できるようになります。",
|
||||
"2cf795433b": "複数のリポジトリで作業を開始する",
|
||||
"42525ba8a4": "キーリポジトリを Orca に取り込むと、フォルダーを探さずに Agent の作業を開始できます。"
|
||||
"42525ba8a4": "キーリポジトリを Orca に取り込むと、フォルダーを探さずに Agent の作業を開始できます。",
|
||||
"workOnTwoTasks": "2つのタスクを同時に進める",
|
||||
"agentSkillsName": "Agent に Orca スキルを追加",
|
||||
"agentSkillsDescription": "Agent が Orca のブラウザを操作し、コンピューターを制御し、複数ステップの作業を調整するためのスキルをインストールします。"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14565,8 +14531,7 @@
|
||||
"d6d15077df": "スキルコマンドをコピーし、確認用に以下へ挿入しました。",
|
||||
"78e65f19d9": "ブラウザのセットアップに失敗しました",
|
||||
"b7345c18db": "予期しないエラーが発生しました。",
|
||||
"5f97caf76b": "インストール中…",
|
||||
"c2df599513": "CLI とスキルをインストール"
|
||||
"5f97caf76b": "インストール中…"
|
||||
},
|
||||
"ConnectIntegrationsList": {
|
||||
"3dddb2d565": "タスク用に接続済み",
|
||||
@@ -14708,17 +14673,9 @@
|
||||
"agent": {
|
||||
"setup": {
|
||||
"state": {
|
||||
"fdcca1ec75": "登録中…",
|
||||
"69fb2c2289": "有効",
|
||||
"c6705092ba": "PATH を修正",
|
||||
"7c1b6bdb1e": "有効化",
|
||||
"51074ccb05": "CLI ステータスの読み込みに失敗しました。",
|
||||
"35dea1ae12": "Agent 制御の準備ができました。",
|
||||
"9dff3a6338": "スキルがインストールされています。Orca CLI を有効化してセットアップを完了してください。",
|
||||
"15986a1080": "Orca CLI の準備ができました。スキルをインストールしてセットアップを完了してください。",
|
||||
"4c26913def": "まだセットアップが完了していません。Agent 制御を有効にするには、両方のステップを完了してください。",
|
||||
"c94ff11e91": "セットアップ状況を再確認できませんでした。",
|
||||
"2b519eed94": "Orca CLI を PATH に登録しました。"
|
||||
"skillNotInstalled": "Orca CLI スキルはまだインストールされていません。"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -14736,23 +14693,18 @@
|
||||
"2fda9ff015": "Agent 制御をセットアップ",
|
||||
"0ac0fef514": "Agent 制御の準備ができました。",
|
||||
"2bdfff8763": "Agent 制御(オプション)。",
|
||||
"72736b051f": "Agent にこのシミュレーターを操作させたい場合は、Orca CLI + スキルをセットアップしてください。",
|
||||
"d10ae98046": "完了",
|
||||
"3756cbeca7": "後で",
|
||||
"6d950431d2": "非表示",
|
||||
"ebceac65a4": "セットアップ",
|
||||
"3f003507f4": "設定で完全なセットアップを開く"
|
||||
"3f003507f4": "設定で完全なセットアップを開く",
|
||||
"installSkillPrompt": "Agent にこのシミュレーターを操作させたいときは、Orca CLI スキルをインストールしてください。"
|
||||
},
|
||||
"MobileEmulatorAgentSetupGuideSteps": {
|
||||
"9b49d892e3": "Orca CLI を有効化",
|
||||
"3d8dc52c93": "Agent シェルでエミュレータ制御用の orca コマンドを登録します。",
|
||||
"21f5687c07": "Orca CLI スキル",
|
||||
"64fb057667": "このワークツリーの orca エミュレータコマンドを Agent に教えます。",
|
||||
"5c59ea96ca": "モバイルエミュレータ用 Orca CLI スキルのセットアップ",
|
||||
"bff5341ac3": "モバイルエミュレータ用 Orca CLI スキルのインストール用ターミナル",
|
||||
"3d34423e88": "Orca CLI を登録しています",
|
||||
"3be27641c9": "Agent のシェルからエミュレータコマンドを実行できるようにします。",
|
||||
"3941719a56": "スキル設定を開く前に Orca CLI を確認しています。"
|
||||
"bff5341ac3": "モバイルエミュレータ用 Orca CLI スキルのインストール用ターミナル"
|
||||
},
|
||||
"MobileEmulatorTabIntroCallout": {
|
||||
"1924982130": "閉じる",
|
||||
|
||||
@@ -5928,13 +5928,11 @@
|
||||
}
|
||||
},
|
||||
"LinearAgentSkillSetupPrompt": {
|
||||
"missingCliAndSkill": "Orca CLI와 Linear agent 스킬이 없습니다.",
|
||||
"modalTitle": "Linear 티켓 접근 활성화",
|
||||
"modalDescription": "terminal에서 Linear 스킬을 설치합니다.",
|
||||
"modalPrompt": "agents가 첨부된 Linear 티켓을 읽고 편집할 수 있게 합니다.",
|
||||
"dontShowAgain": "다시 표시하지 않음",
|
||||
"missingBoth": "Orca CLI와 Linear agent 스킬이 없습니다.",
|
||||
"missingCli": "Orca CLI가 없습니다.",
|
||||
"missingSkill": "Linear agent 스킬이 없습니다.",
|
||||
"title": "Linear agent 스킬 설정",
|
||||
"remoteCopy": "호스트 설정을 설치합니다. 원격 agent 환경에는 별도 설정이 필요할 수 있습니다.",
|
||||
@@ -5946,7 +5944,6 @@
|
||||
"panelDescription": "연결된 Linear 작업 인계를 위해 호스트 agent 스킬을 설치합니다.",
|
||||
"terminalTitle": "Linear agent 스킬 설치",
|
||||
"terminalAria": "Linear agent 스킬 설치 terminal",
|
||||
"install": "CLI 및 스킬 설치",
|
||||
"successTitle": "Linear 티켓 접근이 준비되었습니다",
|
||||
"successDescription": "이제 agents가 이 워크스페이스에서 연결된 Linear 티켓을 읽고 업데이트할 수 있습니다.",
|
||||
"successDescriptionWsl": "이제 WSL agents가 이 워크스페이스에서 연결된 Linear 티켓을 사용할 수 있습니다.",
|
||||
@@ -5955,11 +5952,7 @@
|
||||
"done": "완료",
|
||||
"wslCopy": "연결된 Linear 작업에서 WSL agent 인계를 위해 설치합니다.",
|
||||
"wslLabel": "WSL 기본값",
|
||||
"toastMissingCliAndSkill": "Orca CLI와 Linear 스킬이 없습니다",
|
||||
"toastMissingCli": "Orca CLI가 없습니다",
|
||||
"toastMissingSkill": "Linear 스킬이 없습니다",
|
||||
"toastInstallCliAndSkillDescription": "Orca CLI와 Linear 스킬을 설치하여 agents가 Linear 작업을 읽고 편집할 수 있게 하세요.",
|
||||
"toastInstallCliDescription": "Orca CLI를 설치하여 agents가 Linear 작업을 읽고 편집할 수 있게 하세요.",
|
||||
"toastInstallSkillDescription": "Linear 스킬을 설치하여 agents가 Orca CLI를 통해 Linear 작업을 읽고 편집할 수 있게 하세요.",
|
||||
"toastRemoteDescription": "{{value0}} 원격 agent 환경에는 자체 설정이 필요할 수 있습니다.",
|
||||
"toastWslDescription": "{{value0}} 이 설정은 선택한 WSL agent 런타임에서 실행됩니다."
|
||||
@@ -6360,7 +6353,6 @@
|
||||
"SetupGuideModal": {
|
||||
"3598a3ca0c": "Orca를 병렬 agent 작업에 유용하게 만드는 핵심 워크플로를 완료합니다.",
|
||||
"48a9e5ef2d": "시작하기",
|
||||
"28cf59fcb4": "이렇게 하면 사이드바에서 체크리스트가 숨겨집니다.",
|
||||
"f3b5ffb2a6": "사이드바에서 체크리스트 숨기기"
|
||||
},
|
||||
"SetupGuideProgressRing": {
|
||||
@@ -6834,24 +6826,15 @@
|
||||
"af8c83ed61": "agents가 로그인을 재사용할 수 있도록 Chrome, Edge 또는 기타 브라우저에서 쿠키를 가져옵니다.",
|
||||
"68ea76eb71": "agents가 Orca의 브라우저를 작동할 수 있도록 브라우저 사용 스킬을 설치하세요.",
|
||||
"2d6ead9ab2": "브라우저 사용 스킬 설치",
|
||||
"e9f3f3b488": "설치 위치",
|
||||
"9fca1f7f5d": "agents가 셸에서 브라우저를 조정할 수 있도록 Orca CLI 명령을 등록합니다.",
|
||||
"c6065d205d": "Orca CLI 활성화",
|
||||
"c79eff0213": "agents가 브라우저를 구동할 수 있도록 Orca CLI를 등록하세요.",
|
||||
"702488a5f7": "코딩 agents가 로그인을 통해 이 브라우저를 구동하게 하세요. 아래 세 단계를 완료하세요.",
|
||||
"b8a1f2d84d": "Agent 브라우저 사용",
|
||||
"96b91c6349": "코딩 agents가 로그인을 통해 이 브라우저를 구동하게 하세요.",
|
||||
"2ea4617e3a": "{{value1}}{{value2}}에서 {{value0}} 쿠키를 가져왔습니다.",
|
||||
"721aee31b4": "PATH에 Orca CLI를 등록했습니다.",
|
||||
"180a9abf3a": "CLI 상태를 로드하지 못했습니다.",
|
||||
"2ccfc9cff8": "가져오기",
|
||||
"0462565413": "다시 가져오기",
|
||||
"de9b2f32f3": "활성화",
|
||||
"ad8cb0ee22": "경로 수정",
|
||||
"0289434ed6": "활성화됨",
|
||||
"8b3054dac7": "등록 중...",
|
||||
"8f2675c2f3": "파일에서 {{value0}}개의 쿠키를 가져왔습니다.",
|
||||
"5301857d88": "{{value0}}에서"
|
||||
"5301857d88": "{{value0}}에서",
|
||||
"finishSteps": "코딩 agents가 로그인된 상태로 이 브라우저를 조작하게 합니다. 아래 단계를 완료하세요."
|
||||
},
|
||||
"BrowserUseSkillStep": {
|
||||
"0871b6998d": "agents가 Orca 브라우저에서 페이지를 탐색하고 확인할 수 있습니다.",
|
||||
@@ -6872,7 +6855,6 @@
|
||||
"5dae812f50": "새로고침",
|
||||
"52e640f3a0": "CLI 상태 새로 고침",
|
||||
"38edbb5721": "셸 명령",
|
||||
"6930feda9e": "terminals에서 Orca를 사용하여 앱을 열고, 작업 트리를 관리하고, Orca terminals과 상호 작용할 수 있습니다.",
|
||||
"c5c0f2641d": "Orca CLI",
|
||||
"d77352f2df": "PATH에서 `{{value0}}`을(를) 제거하지 못했습니다.",
|
||||
"af5540930c": "PATH에서 `{{value0}}`을 제거했습니다.",
|
||||
@@ -6896,7 +6878,8 @@
|
||||
"cbe55e4d48": "탐색기에서 보기",
|
||||
"9fd4023db0": "파일 관리자에서 보기",
|
||||
"installFailureUnknownReason": "Orca가 CLI 등록을 완료하지 못했고 이유를 보고하지 않았습니다.",
|
||||
"installFailureConflictRemedy": "{{value0}}을(를) 제거하고 더 이상 필요하지 않으면 다시 등록하세요."
|
||||
"installFailureConflictRemedy": "{{value0}}을(를) 제거하고 더 이상 필요하지 않으면 다시 등록하세요.",
|
||||
"outsideOrcaDescription": "Orca 터미널에서는 이미 `orca`를 쓸 수 있습니다. Orca 밖의 다른 터미널에서 `orca`를 쓰려면 켜세요."
|
||||
},
|
||||
"CliSkillRuntimeSetup": {
|
||||
"04325573f8": "WSL",
|
||||
@@ -7482,15 +7465,9 @@
|
||||
"c7f3fe0a6e": "일반 에뮬레이터 명령",
|
||||
"d94ca6a623": "agents가 모바일 에뮬레이터 제어를 포함하여 Orca CLI 명령을 사용할 수 있도록 합니다.",
|
||||
"67e19ee03c": "Orca CLI 스킬",
|
||||
"aaf62a3dd2": "설치 위치",
|
||||
"2fef055608": "agents가 셸에서 활성 에뮬레이터를 제어할 수 있도록 Orca CLI 명령을 등록합니다.",
|
||||
"4f2205f3b6": "Orca CLI 활성화",
|
||||
"ff4b7e65d6": "코딩 agents가 Orca CLI 명령을 사용하여 활성 모바일 에뮬레이터를 제어하도록 합니다.",
|
||||
"2a674aa810": "Agent 모바일 에뮬레이터 제어",
|
||||
"cdeaed9e37": "PATH에 Orca CLI를 등록했습니다.",
|
||||
"3d34423e88": "Orca CLI를 등록하는 중",
|
||||
"3be27641c9": "agent 셸에서 에뮬레이터 명령을 실행할 수 있도록 합니다.",
|
||||
"3941719a56": "스킬 설정을 열기 전에 Orca CLI를 확인하고 있습니다."
|
||||
"cdeaed9e37": "PATH에 Orca CLI를 등록했습니다."
|
||||
},
|
||||
"MobileEmulatorExamples": {
|
||||
"edf13dd03b": "복사",
|
||||
@@ -9317,16 +9294,7 @@
|
||||
"9d97446873": "agent",
|
||||
"a2d489263e": "스킬",
|
||||
"a7e82445fa": "agents가 Orca의 브라우저를 작동할 수 있도록 브라우저 사용 스킬을 설치하세요.",
|
||||
"a1414dcefb": "브라우저 사용 스킬 설치",
|
||||
"e56c7b55c9": "설정",
|
||||
"034c5e8d7f": "활성화",
|
||||
"7e0dcb257a": "셸",
|
||||
"3ffafc9b95": "명령",
|
||||
"30c74aaa1f": "경로",
|
||||
"ff05cbc344": "orca",
|
||||
"85fab5e12c": "CLI",
|
||||
"890ddf943d": "agents가 브라우저를 구동할 수 있도록 Orca CLI를 등록하세요.",
|
||||
"50f0860e18": "Orca CLI 활성화"
|
||||
"a1414dcefb": "브라우저 사용 스킬 설치"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -13648,9 +13616,8 @@
|
||||
"1eee1c7bd8": "PATH에서 agents를 찾지 못했습니다. 나중에 설치할 항목을 선택하거나 빈 terminal로 계속 진행하세요.",
|
||||
"hideAgents": "agents 숨기기",
|
||||
"showMoreAgents": "agents {{value0}}개 더 보기→",
|
||||
"yoloPermissionsLabel": "Yolo / 권한 확인 위험하게 건너뛰기",
|
||||
"yoloPermissionsInfo": "Agent 권한 정보",
|
||||
"yoloPermissionsTooltip": "중단을 줄이기 위해 agents 권한 확인을 건너뜁니다."
|
||||
"yoloModeLabel": "Yolo 모드",
|
||||
"yoloPermissionsDescription": "agents가 묻지 않고 명령을 실행하고 파일을 편집하며, 일부는 샌드박스도 우회합니다. 신뢰할 수 있는 프로젝트에서만 사용하세요."
|
||||
},
|
||||
"FeatureSetupInlineTerminal": {
|
||||
"789b59936e": "Enter를 눌러 명령을 실행하고 요청 시 npx를 확인합니다. 나중에 설정에서 설정할 수도 있습니다.",
|
||||
@@ -13685,7 +13652,6 @@
|
||||
"NotificationStep": {
|
||||
"3bede04483": "테스트 알림 보내기",
|
||||
"dc897423e1": "알림 소리 선택",
|
||||
"53aaffe49a": "알림음",
|
||||
"0fe570690c": "데스크톱 알림이 전달된 후 Orca가 재생하는 경고를 선택합니다.",
|
||||
"0af746e41f": "소리를 선택하세요",
|
||||
"8124d085a6": "맥 설정 열기",
|
||||
@@ -14209,7 +14175,8 @@
|
||||
"3a59452a67": "리뷰를 위해 스킬 명령을 아래에 복사하여 삽입했습니다.",
|
||||
"c605f51f2b": "기능 설정 준비 완료",
|
||||
"1aa657d8f4": "일부 기능 설정에 주의가 필요합니다.",
|
||||
"c89534cbe9": "CLI 및 스킬 설치"
|
||||
"allInstalled": "모든 스킬 설치됨",
|
||||
"installSkills": "스킬 설치"
|
||||
},
|
||||
"AiCommitPrSettingsCard": {
|
||||
"8d4152701a": "예: ollama run llama3.1 {{value0}}",
|
||||
@@ -14302,7 +14269,6 @@
|
||||
"1a6a7d6c80": "설정",
|
||||
"713cc529a5": "마일스톤",
|
||||
"b1f1981c5e": "작업 보기",
|
||||
"0235b268b2": "아직 완료되지 않음",
|
||||
"13294d3405": "완료"
|
||||
},
|
||||
"FeatureWallSetupWorkflowActions": {
|
||||
@@ -14413,13 +14379,13 @@
|
||||
"setup": {
|
||||
"status": {
|
||||
"8eccfcb314": "설치됨",
|
||||
"21d4f79c93": "CLI 및 스킬 설치를 클릭하여 macOS 액세스 설정을 엽니다.",
|
||||
"5c9293e51a": "앱 액세스 확인 중",
|
||||
"aae94eeb52": "CLI 및 스킬 설치를 클릭하세요.",
|
||||
"aa8e143a2f": "설치를 확인할 수 없습니다.",
|
||||
"9b33e7fb13": "설치 확인 중",
|
||||
"4c8e1f92a7": "이 Mac에서 Orca Desktop을 여세요",
|
||||
"6d2b0a84e1": "이 빌드에서는 사용할 수 없습니다"
|
||||
"6d2b0a84e1": "이 빌드에서는 사용할 수 없습니다",
|
||||
"needsMacAccess": "macOS 접근 권한 필요",
|
||||
"notInstalled": "설치되지 않음",
|
||||
"requiresOrcaDesktop": "Orca Desktop 필요"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14430,7 +14396,6 @@
|
||||
"checklist": {
|
||||
"localized": {
|
||||
"copy": {
|
||||
"ec0a363633": "동시 작업",
|
||||
"62bac8f43c": "동시에 2개의 서로 다른 워크트리에서 작업하세요. 같은 프로젝트 내에서도 각각 완벽히 격리되어 있어 2개의 기능을 동시에 작업하기에 적합합니다.",
|
||||
"908898c3ee": "Orca 브라우저 사용",
|
||||
"43781563c3": "Orca를 벗어나지 않고 웹 앱을 탐색하세요. 원하는 요소를 선택하고 클릭 한 번으로 정확한 소스와 스타일을 에이전트에 전달할 수 있습니다.",
|
||||
@@ -14438,14 +14403,15 @@
|
||||
"71bd9a8c95": "에이전트가 작업을 완료했거나, 확인이 필요하거나, 차단된 순간을 즉시 알 수 있습니다.",
|
||||
"46db810da8": "기본 에이전트 선택",
|
||||
"b8e5bae17f": "선호하는 에이전트를 미리 선택하여 새로운 작업을 더 빠르게 시작하세요.",
|
||||
"fee5557b02": "Orca CLI 활성화",
|
||||
"7bcb4097fa": "Orca 셸 명령을 등록하고 브라우저, 컴퓨터 및 오케스트레이션 워크플로를 위한 에이전트 스킬을 설치하세요.",
|
||||
"ad342dd4c6": "서비스 연동",
|
||||
"06fe30fdb0": "클릭 한 번으로 작업에서 에이전트를 시작하고 PR 상태를 한눈에 확인하세요.",
|
||||
"eddc532e58": "워크스페이스 설정 자동화",
|
||||
"56049b74c2": "설치 및 설정 명령을 자동으로 실행하여 모든 새 워크트리가 에이전트 작업을 바로 수행할 수 있도록 준비합니다.",
|
||||
"2cf795433b": "여러 저장소에서 작업 시작",
|
||||
"42525ba8a4": "주요 저장소를 Orca로 가져와 폴더를 일일이 찾을 필요 없이 에이전트 작업을 시작하세요."
|
||||
"42525ba8a4": "주요 저장소를 Orca로 가져와 폴더를 일일이 찾을 필요 없이 에이전트 작업을 시작하세요.",
|
||||
"agentSkillsName": "에이전트에 Orca 스킬 제공",
|
||||
"agentSkillsDescription": "에이전트가 Orca 브라우저를 조작하고, 컴퓨터를 제어하고, 여러 단계 작업을 조율하는 데 쓰는 스킬을 설치하세요.",
|
||||
"workOnTwoTasks": "두 작업을 동시에 진행하기"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14565,8 +14531,7 @@
|
||||
"d6d15077df": "리뷰를 위해 스킬 명령을 아래에 복사하여 삽입했습니다.",
|
||||
"78e65f19d9": "브라우저 설정에 실패했습니다",
|
||||
"b7345c18db": "예기치 않은 오류가 발생했습니다.",
|
||||
"5f97caf76b": "설치 중…",
|
||||
"c2df599513": "CLI 및 스킬 설치"
|
||||
"5f97caf76b": "설치 중…"
|
||||
},
|
||||
"ConnectIntegrationsList": {
|
||||
"3dddb2d565": "작업용으로 연결됨",
|
||||
@@ -14708,17 +14673,9 @@
|
||||
"agent": {
|
||||
"setup": {
|
||||
"state": {
|
||||
"fdcca1ec75": "등록 중...",
|
||||
"69fb2c2289": "활성화됨",
|
||||
"c6705092ba": "PATH 수정",
|
||||
"7c1b6bdb1e": "활성화",
|
||||
"51074ccb05": "CLI 상태를 불러오지 못했습니다.",
|
||||
"35dea1ae12": "agent 제어가 준비되었습니다.",
|
||||
"9dff3a6338": "스킬이 설치되었습니다. Orca CLI를 활성화하여 설정을 완료하세요.",
|
||||
"15986a1080": "Orca CLI가 준비되었습니다. 스킬을 설치하여 설정을 완료하세요.",
|
||||
"4c26913def": "아직 설정되지 않았습니다. agent 제어를 활성화하려면 두 단계를 모두 완료하세요.",
|
||||
"c94ff11e91": "설정 상태를 다시 확인할 수 없습니다.",
|
||||
"2b519eed94": "Orca CLI가 PATH에 등록되었습니다."
|
||||
"skillNotInstalled": "Orca CLI 스킬이 아직 설치되지 않았습니다."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -14736,23 +14693,18 @@
|
||||
"2fda9ff015": "agent 제어 설정",
|
||||
"0ac0fef514": "agent 제어가 준비되었습니다.",
|
||||
"2bdfff8763": "agent 제어(선택 사항).",
|
||||
"72736b051f": "agents가 이 시뮬레이터를 제어하도록 하려면 Orca CLI + 스킬을 설정하세요.",
|
||||
"d10ae98046": "완료",
|
||||
"3756cbeca7": "나중에",
|
||||
"6d950431d2": "숨기기",
|
||||
"ebceac65a4": "설정",
|
||||
"3f003507f4": "설정에서 전체 설정 열기"
|
||||
"3f003507f4": "설정에서 전체 설정 열기",
|
||||
"installSkillPrompt": "agents가 이 시뮬레이터를 조작하게 하려면 Orca CLI 스킬을 설치하세요."
|
||||
},
|
||||
"MobileEmulatorAgentSetupGuideSteps": {
|
||||
"9b49d892e3": "Orca CLI 활성화",
|
||||
"3d8dc52c93": "agents 셸에서 에뮬레이터 제어를 위해 orca 명령을 등록합니다.",
|
||||
"21f5687c07": "Orca CLI 스킬",
|
||||
"64fb057667": "이 워크트리의 orca 에뮬레이터 명령을 agents에게 알려줍니다.",
|
||||
"5c59ea96ca": "모바일 에뮬레이터 Orca CLI 스킬 설정",
|
||||
"bff5341ac3": "모바일 에뮬레이터 Orca CLI 스킬 설치 terminal",
|
||||
"3d34423e88": "Orca CLI를 등록하는 중",
|
||||
"3be27641c9": "agent 셸에서 에뮬레이터 명령을 실행할 수 있도록 합니다.",
|
||||
"3941719a56": "스킬 설정을 열기 전에 Orca CLI를 확인하고 있습니다."
|
||||
"bff5341ac3": "모바일 에뮬레이터 Orca CLI 스킬 설치 terminal"
|
||||
},
|
||||
"MobileEmulatorTabIntroCallout": {
|
||||
"1924982130": "닫기",
|
||||
|
||||
@@ -5928,13 +5928,11 @@
|
||||
}
|
||||
},
|
||||
"LinearAgentSkillSetupPrompt": {
|
||||
"missingCliAndSkill": "缺少 Orca CLI 和 Linear 智能体技能。",
|
||||
"modalTitle": "启用 Linear ticket 访问",
|
||||
"modalDescription": "从终端安装 Linear 技能。",
|
||||
"modalPrompt": "允许智能体读取和编辑已附加的 Linear ticket。",
|
||||
"dontShowAgain": "不再显示",
|
||||
"missingBoth": "缺少 Orca CLI 和 Linear 智能体技能。",
|
||||
"missingCli": "缺少 Orca CLI。",
|
||||
"missingSkill": "缺少 Linear 智能体技能。",
|
||||
"title": "设置 Linear 智能体技能",
|
||||
"remoteCopy": "这会安装主机设置;远程智能体环境可能需要单独设置。",
|
||||
@@ -5946,7 +5944,6 @@
|
||||
"panelDescription": "安装主机智能体技能,用于已链接 Linear 任务交接。",
|
||||
"terminalTitle": "安装 Linear 智能体技能",
|
||||
"terminalAria": "Linear 智能体技能安装终端",
|
||||
"install": "安装 CLI 和技能",
|
||||
"successTitle": "Linear 议题访问已就绪",
|
||||
"successDescription": "智能体现在可以从此工作区读取和更新已链接的 Linear 议题。",
|
||||
"successDescriptionWsl": "WSL 智能体现在可以从此工作区使用已链接的 Linear 议题。",
|
||||
@@ -5955,11 +5952,7 @@
|
||||
"done": "完成",
|
||||
"wslCopy": "为已链接 Linear 工作的 WSL 智能体交接安装此项。",
|
||||
"wslLabel": "WSL 默认值",
|
||||
"toastMissingCliAndSkill": "缺少 Orca CLI 和 Linear 技能",
|
||||
"toastMissingCli": "缺少 Orca CLI",
|
||||
"toastMissingSkill": "缺少 Linear 技能",
|
||||
"toastInstallCliAndSkillDescription": "安装 Orca CLI 和 Linear 技能,让智能体可以读取并编辑 Linear 任务。",
|
||||
"toastInstallCliDescription": "安装 Orca CLI,让智能体可以读取并编辑 Linear 任务。",
|
||||
"toastInstallSkillDescription": "安装 Linear 技能,让智能体可以通过 Orca CLI 读取并编辑 Linear 任务。",
|
||||
"toastRemoteDescription": "{{value0}} 远程智能体环境可能仍需要自己的设置。",
|
||||
"toastWslDescription": "{{value0}} 此设置会在所选 WSL 智能体运行时中执行。"
|
||||
@@ -6360,7 +6353,6 @@
|
||||
"SetupGuideModal": {
|
||||
"3598a3ca0c": "完成使 Orca 对于并行智能体工作有用的核心工作流程。",
|
||||
"48a9e5ef2d": "入门",
|
||||
"28cf59fcb4": "这将从侧边栏隐藏清单",
|
||||
"f3b5ffb2a6": "从侧边栏隐藏清单"
|
||||
},
|
||||
"SetupGuideProgressRing": {
|
||||
@@ -6834,24 +6826,15 @@
|
||||
"af8c83ed61": "从 Chrome、Edge 或其他浏览器导入 cookie,以便智能体可以重复使用您的登录信息。",
|
||||
"68ea76eb71": "安装浏览器使用技能,以便智能体可以操作 Orca 的浏览器。",
|
||||
"2d6ead9ab2": "安装浏览器使用技能",
|
||||
"e9f3f3b488": "安装于",
|
||||
"9fca1f7f5d": "注册 Orca CLI 命令,以便智能体可以从其 shell 编排浏览器。",
|
||||
"c6065d205d": "启用 Orca CLI",
|
||||
"c79eff0213": "注册 Orca CLI,以便智能体可以驱动浏览器。",
|
||||
"702488a5f7": "让编码智能体通过您的登录来驱动此浏览器。完成以下三个步骤。",
|
||||
"b8a1f2d84d": "智能体浏览器使用",
|
||||
"96b91c6349": "让编码智能体通过您的登录来驱动此浏览器。",
|
||||
"2ea4617e3a": "从 {{value1}}{{value2}} 导入 {{value0}} cookie。",
|
||||
"721aee31b4": "在 PATH 中注册 Orca CLI。",
|
||||
"180a9abf3a": "无法加载 CLI 状态。",
|
||||
"2ccfc9cff8": "导入",
|
||||
"0462565413": "重新导入",
|
||||
"de9b2f32f3": "启用",
|
||||
"ad8cb0ee22": "修复路径",
|
||||
"0289434ed6": "启用",
|
||||
"8b3054dac7": "注册...",
|
||||
"8f2675c2f3": "已从文件导入 {{value0}} 个 Cookie。",
|
||||
"5301857d88": "从 {{value0}}"
|
||||
"5301857d88": "从 {{value0}}",
|
||||
"finishSteps": "让编程智能体使用你的登录状态操作此浏览器。请完成以下步骤。"
|
||||
},
|
||||
"BrowserUseSkillStep": {
|
||||
"0871b6998d": "使智能体能够在 Orca 浏览器中导航和验证页面。",
|
||||
@@ -6872,7 +6855,6 @@
|
||||
"5dae812f50": "刷新",
|
||||
"52e640f3a0": "刷新 CLI 状态",
|
||||
"38edbb5721": "Shell 命令",
|
||||
"6930feda9e": "从终端使用 Orca 打开应用程序、管理工作树并与 Orca 终端交互。",
|
||||
"c5c0f2641d": "Orca CLI",
|
||||
"d77352f2df": "无法从 PATH 中删除 `{{value0}}`。",
|
||||
"af5540930c": "从路径中删除了“{{value0}}”。",
|
||||
@@ -6896,7 +6878,8 @@
|
||||
"cbe55e4d48": "在资源管理器中显示",
|
||||
"9fd4023db0": "在文件管理器中显示",
|
||||
"installFailureUnknownReason": "Orca 无法完成 CLI 注册,且未报告任何原因。",
|
||||
"installFailureConflictRemedy": "删除 {{value0}} 并在不再需要时重新注册。"
|
||||
"installFailureConflictRemedy": "删除 {{value0}} 并在不再需要时重新注册。",
|
||||
"outsideOrcaDescription": "Orca 终端已内置 `orca`。开启后可在 Orca 以外的其他终端中使用 `orca`。"
|
||||
},
|
||||
"CliSkillRuntimeSetup": {
|
||||
"04325573f8": "WSL",
|
||||
@@ -7482,15 +7465,9 @@
|
||||
"c7f3fe0a6e": "常用模拟器命令",
|
||||
"d94ca6a623": "使智能体能够使用 Orca CLI 命令,包括手机模拟器控制。",
|
||||
"67e19ee03c": "Orca CLI 技能",
|
||||
"aaf62a3dd2": "安装于",
|
||||
"2fef055608": "注册 Orca CLI 命令,以便智能体可以从其 shell 控制活动模拟器。",
|
||||
"4f2205f3b6": "启用 Orca CLI",
|
||||
"ff4b7e65d6": "让编码智能体使用 Orca CLI 命令控制活动手机模拟器。",
|
||||
"2a674aa810": "智能体手机模拟器控制",
|
||||
"cdeaed9e37": "在 PATH 中注册 Orca CLI。",
|
||||
"3d34423e88": "正在注册 Orca CLI",
|
||||
"3be27641c9": "以便智能体 shell 可以运行模拟器命令。",
|
||||
"3941719a56": "打开技能设置前正在检查 Orca CLI。"
|
||||
"cdeaed9e37": "在 PATH 中注册 Orca CLI。"
|
||||
},
|
||||
"MobileEmulatorExamples": {
|
||||
"edf13dd03b": "复制",
|
||||
@@ -9317,16 +9294,7 @@
|
||||
"9d97446873": "智能体",
|
||||
"a2d489263e": "技能",
|
||||
"a7e82445fa": "安装浏览器使用技能,以便智能体可以操作 Orca 的浏览器。",
|
||||
"a1414dcefb": "安装浏览器使用技能",
|
||||
"e56c7b55c9": "设置",
|
||||
"034c5e8d7f": "启用",
|
||||
"7e0dcb257a": "Shell",
|
||||
"3ffafc9b95": "命令",
|
||||
"30c74aaa1f": "路径",
|
||||
"ff05cbc344": "orca",
|
||||
"85fab5e12c": "CLI",
|
||||
"890ddf943d": "注册 Orca CLI,以便智能体可以驱动浏览器。",
|
||||
"50f0860e18": "启用 Orca CLI"
|
||||
"a1414dcefb": "安装浏览器使用技能"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -13648,9 +13616,8 @@
|
||||
"1eee1c7bd8": "PATH 中未检测到智能体。可选择一个稍后安装,或使用空白终端继续。",
|
||||
"hideAgents": "隐藏智能体",
|
||||
"showMoreAgents": "显示另外 {{value0}} 个智能体→",
|
||||
"yoloPermissionsLabel": "Yolo / 危险地跳过权限检查",
|
||||
"yoloPermissionsInfo": "智能体权限信息",
|
||||
"yoloPermissionsTooltip": "跳过智能体权限检查,减少打断"
|
||||
"yoloModeLabel": "Yolo 模式",
|
||||
"yoloPermissionsDescription": "智能体会在不询问的情况下运行命令和编辑文件,部分智能体还会绕过沙箱。仅在你信任的项目中使用。"
|
||||
},
|
||||
"FeatureSetupInlineTerminal": {
|
||||
"789b59936e": "按 Enter 运行命令并在询问时确认 npx。您也可以稍后在“设置”中进行设置。",
|
||||
@@ -13685,7 +13652,6 @@
|
||||
"NotificationStep": {
|
||||
"3bede04483": "发送测试通知",
|
||||
"dc897423e1": "选择通知声音",
|
||||
"53aaffe49a": "通知声音",
|
||||
"0fe570690c": "选择发送桌面通知后 Orca 播放的警报。",
|
||||
"0af746e41f": "选择声音",
|
||||
"8124d085a6": "打开 Mac 设置",
|
||||
@@ -14209,7 +14175,8 @@
|
||||
"3a59452a67": "技能命令已复制并插入到下方以供评审。",
|
||||
"c605f51f2b": "能力设置就绪",
|
||||
"1aa657d8f4": "一些能力设置需要注意",
|
||||
"c89534cbe9": "安装 CLI 和技能"
|
||||
"allInstalled": "所有技能均已安装",
|
||||
"installSkills": "安装技能"
|
||||
},
|
||||
"AiCommitPrSettingsCard": {
|
||||
"8d4152701a": "例如llama 运行 llama3.1 {{value0}}",
|
||||
@@ -14302,7 +14269,6 @@
|
||||
"1a6a7d6c80": "设置",
|
||||
"713cc529a5": "里程碑",
|
||||
"b1f1981c5e": "查看任务",
|
||||
"0235b268b2": "还没有完成",
|
||||
"13294d3405": "完成"
|
||||
},
|
||||
"FeatureWallSetupWorkflowActions": {
|
||||
@@ -14413,13 +14379,13 @@
|
||||
"setup": {
|
||||
"status": {
|
||||
"8eccfcb314": "已安装",
|
||||
"21d4f79c93": "单击安装 CLI 和技能以打开 macOS 访问设置",
|
||||
"5c9293e51a": "检查应用程序访问权限",
|
||||
"aae94eeb52": "单击安装 CLI 和技能",
|
||||
"aa8e143a2f": "无法检查安装",
|
||||
"9b33e7fb13": "检查安装",
|
||||
"4c8e1f92a7": "在此 Mac 上打开 Orca Desktop",
|
||||
"6d2b0a84e1": "此版本不可用"
|
||||
"6d2b0a84e1": "此版本不可用",
|
||||
"needsMacAccess": "需要 macOS 访问权限",
|
||||
"notInstalled": "未安装",
|
||||
"requiresOrcaDesktop": "需要 Orca Desktop"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14437,7 +14403,6 @@
|
||||
"checklist": {
|
||||
"localized": {
|
||||
"copy": {
|
||||
"ec0a363633": "多任务",
|
||||
"62bac8f43c": "同时在 2 个不同的工作树中工作。每个项目都是独立的(即使在同一个项目中)。非常适合同时处理 2 个功能。",
|
||||
"908898c3ee": "使用 Orca 浏览器",
|
||||
"43781563c3": "无需离开 Orca 即可浏览您的网络应用程序。抓取任何元素,然后一键将其确切的来源和样式发送给代理。",
|
||||
@@ -14445,14 +14410,15 @@
|
||||
"71bd9a8c95": "了解代理完成、需要关注或被阻止的时刻。",
|
||||
"46db810da8": "选择您的默认代理",
|
||||
"b8e5bae17f": "通过已选择的首选代理,更快地开始新工作。",
|
||||
"fee5557b02": "启用 Orca CLI",
|
||||
"7bcb4097fa": "注册 Orca shell 命令并安装浏览器、计算机和编排工作流程的代理技能。",
|
||||
"ad342dd4c6": "连接集成",
|
||||
"06fe30fdb0": "一键从任务启动代理并查看 PR 状态。",
|
||||
"eddc532e58": "自动化工作区设置",
|
||||
"56049b74c2": "自动运行安装和设置命令,以便每个新工作树都为代理做好准备。",
|
||||
"2cf795433b": "在多个存储库中开始工作",
|
||||
"42525ba8a4": "将您的关键存储库带入 Orca,这样您就可以开始代理工作,而无需寻找文件夹。"
|
||||
"42525ba8a4": "将您的关键存储库带入 Orca,这样您就可以开始代理工作,而无需寻找文件夹。",
|
||||
"workOnTwoTasks": "同时处理两项任务",
|
||||
"agentSkillsName": "为智能体提供 Orca 技能",
|
||||
"agentSkillsDescription": "安装智能体用于操作 Orca 浏览器、控制你的电脑以及协调多步骤工作的技能。"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14565,8 +14531,7 @@
|
||||
"d6d15077df": "技能命令已复制并插入到下方供查看。",
|
||||
"78e65f19d9": "浏览器设置失败",
|
||||
"b7345c18db": "发生了意外错误。",
|
||||
"5f97caf76b": "正在安装…",
|
||||
"c2df599513": "安装 CLI 和技能"
|
||||
"5f97caf76b": "正在安装…"
|
||||
},
|
||||
"ConnectIntegrationsList": {
|
||||
"3dddb2d565": "已连接,可用于任务",
|
||||
@@ -14708,17 +14673,9 @@
|
||||
"agent": {
|
||||
"setup": {
|
||||
"state": {
|
||||
"fdcca1ec75": "正在注册...",
|
||||
"69fb2c2289": "已启用",
|
||||
"c6705092ba": "修复 PATH",
|
||||
"7c1b6bdb1e": "启用",
|
||||
"51074ccb05": "无法加载 CLI 状态。",
|
||||
"35dea1ae12": "智能体控制已就绪。",
|
||||
"9dff3a6338": "技能已安装。启用 Orca CLI 以完成设置。",
|
||||
"15986a1080": "Orca CLI 已就绪。安装技能以完成设置。",
|
||||
"4c26913def": "尚未设置完成。请完成两个步骤以启用智能体控制。",
|
||||
"c94ff11e91": "无法重新检查设置状态。",
|
||||
"2b519eed94": "已在 PATH 中注册 Orca CLI。"
|
||||
"skillNotInstalled": "Orca CLI 技能尚未安装。"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -14736,23 +14693,18 @@
|
||||
"2fda9ff015": "设置智能体控制",
|
||||
"0ac0fef514": "智能体控制已就绪。",
|
||||
"2bdfff8763": "智能体控制(可选)。",
|
||||
"72736b051f": "当您希望智能体控制此模拟器时,设置 Orca CLI + 技能。",
|
||||
"d10ae98046": "完成",
|
||||
"3756cbeca7": "暂不",
|
||||
"6d950431d2": "隐藏",
|
||||
"ebceac65a4": "设置",
|
||||
"3f003507f4": "在设置中打开完整设置"
|
||||
"3f003507f4": "在设置中打开完整设置",
|
||||
"installSkillPrompt": "如需让智能体操作此模拟器,请安装 Orca CLI 技能。"
|
||||
},
|
||||
"MobileEmulatorAgentSetupGuideSteps": {
|
||||
"9b49d892e3": "启用 Orca CLI",
|
||||
"3d8dc52c93": "在智能体 shell 中注册用于模拟器控制的 orca 命令。",
|
||||
"21f5687c07": "Orca CLI 技能",
|
||||
"64fb057667": "教授智能体此工作区的 orca 模拟器命令。",
|
||||
"5c59ea96ca": "移动端模拟器 Orca CLI 技能设置",
|
||||
"bff5341ac3": "移动端模拟器 Orca CLI 技能安装终端",
|
||||
"3d34423e88": "正在注册 Orca CLI",
|
||||
"3be27641c9": "以便智能体 shell 可以运行模拟器命令。",
|
||||
"3941719a56": "打开技能设置前正在检查 Orca CLI。"
|
||||
"bff5341ac3": "移动端模拟器 Orca CLI 技能安装终端"
|
||||
},
|
||||
"MobileEmulatorTabIntroCallout": {
|
||||
"1924982130": "关闭",
|
||||
|
||||
@@ -25,8 +25,8 @@ export type FeatureWallSetupSectionId = 'parallel-work' | 'setup'
|
||||
export const FEATURE_WALL_SETUP_STEPS: readonly FeatureWallSetupStep[] = [
|
||||
{
|
||||
id: 'two-worktrees',
|
||||
name: 'Multi-task',
|
||||
subtitle: 'Multi-task',
|
||||
name: 'Work on two tasks at once',
|
||||
subtitle: 'Work on two tasks at once',
|
||||
description:
|
||||
'Work in 2 different worktrees at once. Each one is isolated (even in the same project). Perfect for working on 2 features at once.'
|
||||
},
|
||||
@@ -51,10 +51,10 @@ export const FEATURE_WALL_SETUP_STEPS: readonly FeatureWallSetupStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'agent-capabilities',
|
||||
name: 'Enable Orca CLI',
|
||||
subtitle: 'Enable Orca CLI',
|
||||
name: 'Give agents Orca skills',
|
||||
subtitle: 'Give agents Orca skills',
|
||||
description:
|
||||
'Register the Orca shell command and install agent skills for browser, computer, and orchestration workflows.'
|
||||
"Install the skills agents use to drive Orca's browser, control your computer, and coordinate multi-step work."
|
||||
},
|
||||
{
|
||||
id: 'task-sources',
|
||||
|
||||
Reference in New Issue
Block a user