mirror of
https://github.com/stablyai/orca.git
synced 2026-10-16 08:03:09 +00:00
Use shared host registry for project setup choices
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -510,6 +510,8 @@ Already partially landed:
|
||||
metadata from hosts that have no setup
|
||||
- workspace creation target resolution reports `setup-not-ready` when a
|
||||
selected host has non-ready setup metadata
|
||||
- project settings setup-host choices now use the shared execution host
|
||||
registry, including hydrated runtime hosts that are not currently focused
|
||||
|
||||
Not complete yet:
|
||||
|
||||
|
||||
@@ -514,6 +514,8 @@ Already partially landed:
|
||||
metadata from hosts that have no setup
|
||||
- workspace creation target resolution reports `setup-not-ready` when a
|
||||
selected host has non-ready setup metadata
|
||||
- project settings setup-host choices now use the shared execution host
|
||||
registry, including hydrated runtime hosts that are not currently focused
|
||||
|
||||
Not complete yet:
|
||||
|
||||
|
||||
@@ -432,6 +432,8 @@ Already partially landed:
|
||||
metadata from hosts that have no setup
|
||||
- workspace creation target resolution reports `setup-not-ready` when a
|
||||
selected host has non-ready setup metadata
|
||||
- project settings setup-host choices now use the shared execution host
|
||||
registry, including hydrated runtime hosts that are not currently focused
|
||||
|
||||
Still needed:
|
||||
|
||||
|
||||
@@ -794,6 +794,10 @@ Landed so far:
|
||||
- Updated workspace creation target resolution so selecting a host with
|
||||
non-ready setup metadata reports `setup-not-ready` instead of
|
||||
`project-not-set-up-on-host`.
|
||||
- Broadened project settings setup-host choices to use the shared execution
|
||||
host registry. The setup flow can now offer hydrated runtime hosts even when
|
||||
they are not the currently focused Orca server, and host labels stay aligned
|
||||
with sidebar rename overrides.
|
||||
- Added tests for local repos, SSH repos, same-provider multi-host grouping,
|
||||
no-identity same-name non-grouping, selector cache behavior, persistence
|
||||
backfill, repo mutation synchronization, renderer hydration, runtime RPC
|
||||
@@ -829,7 +833,8 @@ Important limitation:
|
||||
|
||||
Remaining end-to-end work:
|
||||
|
||||
- broaden setup-on-host flows beyond known local, SSH, and active runtime hosts
|
||||
- continue broadening setup-on-host flows beyond the shared known-host registry
|
||||
toward bulk setup and newly added host onboarding
|
||||
- finish SSH clone streamed-progress parity, provisioning, and bulk setup-on-host
|
||||
flows
|
||||
- add actual provisioning execution APIs instead of only recording independent
|
||||
|
||||
@@ -320,4 +320,78 @@ describe('RepositoryHostSetupsSection', () => {
|
||||
setupMethod: 'provisioned'
|
||||
})
|
||||
})
|
||||
|
||||
it('offers inactive runtime hosts discovered from hydrated runtime status', async () => {
|
||||
const createProjectHostSetup = vi.fn().mockResolvedValue({
|
||||
project: makeProject({ id: 'github:stablyai/orca' }),
|
||||
setup: makeSetup({
|
||||
id: 'gpu-setup',
|
||||
projectId: 'github:stablyai/orca',
|
||||
repoId: '',
|
||||
hostId: 'runtime:gpu',
|
||||
path: '',
|
||||
setupState: 'not-set-up',
|
||||
setupMethod: 'provisioned'
|
||||
})
|
||||
})
|
||||
const localRepo = makeRepo({
|
||||
id: 'local-repo',
|
||||
displayName: 'Orca',
|
||||
path: '/Users/alice/orca'
|
||||
})
|
||||
useAppStore.setState({
|
||||
repos: [localRepo],
|
||||
projects: [makeProject({ id: 'github:stablyai/orca' })],
|
||||
projectHostSetups: [
|
||||
makeSetup({
|
||||
id: 'local-repo',
|
||||
projectId: 'github:stablyai/orca',
|
||||
repoId: 'local-repo',
|
||||
hostId: 'local',
|
||||
path: '/Users/alice/orca'
|
||||
})
|
||||
],
|
||||
settings: { activeRuntimeEnvironmentId: null } as never,
|
||||
runtimeStatusByEnvironmentId: new Map([
|
||||
[
|
||||
'gpu',
|
||||
{
|
||||
checkedAt: 1,
|
||||
appVersion: '1.8.0',
|
||||
status: {
|
||||
runtimeId: 'runtime-gpu',
|
||||
rendererGraphEpoch: 1,
|
||||
graphStatus: 'ready',
|
||||
authoritativeWindowId: 1,
|
||||
liveTabCount: 0,
|
||||
liveLeafCount: 0,
|
||||
runtimeProtocolVersion: 1,
|
||||
minCompatibleRuntimeClientVersion: 1,
|
||||
capabilities: ['project-host-setup.v1']
|
||||
}
|
||||
}
|
||||
]
|
||||
]),
|
||||
createProjectHostSetup
|
||||
})
|
||||
|
||||
renderSection(localRepo)
|
||||
|
||||
expect(container.textContent).toContain('gpu')
|
||||
const trackButton = Array.from(container.querySelectorAll('button')).find(
|
||||
(button) => button.textContent === 'Track setup'
|
||||
)
|
||||
|
||||
await act(async () => {
|
||||
trackButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
})
|
||||
|
||||
expect(createProjectHostSetup).toHaveBeenCalledWith({
|
||||
projectId: 'github:stablyai/orca',
|
||||
hostId: 'runtime:gpu',
|
||||
displayName: 'Orca',
|
||||
setupState: 'not-set-up',
|
||||
setupMethod: 'provisioned'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useState } from 'react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { getExecutionHostLabel } from '../../../../shared/execution-host'
|
||||
import { type ExecutionHostId } from '../../../../shared/execution-host'
|
||||
import {
|
||||
LOCAL_EXECUTION_HOST_ID,
|
||||
toRuntimeExecutionHostId,
|
||||
toSshExecutionHostId,
|
||||
type ExecutionHostId
|
||||
} from '../../../../shared/execution-host'
|
||||
buildExecutionHostRegistry,
|
||||
type ExecutionHostRegistryEntry
|
||||
} from '../../../../shared/execution-host-registry'
|
||||
import { getHostDisplayLabelOverrides } from '../../../../shared/host-setting-overrides'
|
||||
import type { ProjectHostSetup, ProjectHostSetupState, Repo } from '../../../../shared/types'
|
||||
import { useAppStore } from '../../store'
|
||||
import { getProjectHostSetupProjectionFromState } from '../../store/selectors'
|
||||
@@ -58,35 +58,18 @@ function getSetupStateLabel(setupState: ProjectHostSetupState): string {
|
||||
|
||||
function buildSetupHostOptions({
|
||||
projectHostSetups,
|
||||
sshTargetLabels,
|
||||
activeRuntimeEnvironmentId
|
||||
hostOptions
|
||||
}: {
|
||||
projectHostSetups: ProjectHostSetup[]
|
||||
sshTargetLabels: Map<string, string>
|
||||
activeRuntimeEnvironmentId: string | null | undefined
|
||||
hostOptions: readonly ExecutionHostRegistryEntry[]
|
||||
}): SetupHostOption[] {
|
||||
const setupHostIds = new Set(projectHostSetups.map((setup) => setup.hostId))
|
||||
const options: SetupHostOption[] = []
|
||||
if (!setupHostIds.has(LOCAL_EXECUTION_HOST_ID)) {
|
||||
options.push({
|
||||
id: LOCAL_EXECUTION_HOST_ID,
|
||||
label: getExecutionHostLabel(LOCAL_EXECUTION_HOST_ID)
|
||||
})
|
||||
}
|
||||
for (const [targetId, label] of sshTargetLabels) {
|
||||
const id = toSshExecutionHostId(targetId)
|
||||
if (!setupHostIds.has(id)) {
|
||||
options.push({ id, label })
|
||||
}
|
||||
}
|
||||
const runtimeEnvironmentId = activeRuntimeEnvironmentId?.trim()
|
||||
if (runtimeEnvironmentId) {
|
||||
const id = toRuntimeExecutionHostId(runtimeEnvironmentId)
|
||||
if (!setupHostIds.has(id)) {
|
||||
options.push({ id, label: runtimeEnvironmentId })
|
||||
}
|
||||
}
|
||||
return options
|
||||
return hostOptions
|
||||
.filter((host) => !setupHostIds.has(host.id))
|
||||
.map((host) => ({
|
||||
id: host.id,
|
||||
label: host.label || getExecutionHostLabel(host.id)
|
||||
}))
|
||||
}
|
||||
|
||||
export function RepositoryHostSetupsSection({
|
||||
@@ -100,9 +83,30 @@ export function RepositoryHostSetupsSection({
|
||||
const setupProjectExistingFolder = useAppStore((state) => state.setupProjectExistingFolder)
|
||||
const createProjectHostSetup = useAppStore((state) => state.createProjectHostSetup)
|
||||
const deleteProjectHostSetup = useAppStore((state) => state.deleteProjectHostSetup)
|
||||
const repos = useAppStore((state) => state.repos)
|
||||
const sshTargetLabels = useAppStore((state) => state.sshTargetLabels)
|
||||
const activeRuntimeEnvironmentId = useAppStore(
|
||||
(state) => state.settings?.activeRuntimeEnvironmentId
|
||||
const sshConnectionStates = useAppStore((state) => state.sshConnectionStates)
|
||||
const settings = useAppStore((state) => state.settings)
|
||||
const runtimeStatusByEnvironmentId = useAppStore((state) => state.runtimeStatusByEnvironmentId)
|
||||
const hostLabelOverrides = useMemo(() => getHostDisplayLabelOverrides(settings), [settings])
|
||||
const hostOptions = useMemo(
|
||||
() =>
|
||||
buildExecutionHostRegistry({
|
||||
repos,
|
||||
settings,
|
||||
sshTargetLabels,
|
||||
sshConnectionStates,
|
||||
runtimeStatusByEnvironmentId,
|
||||
hostLabelOverrides
|
||||
}),
|
||||
[
|
||||
repos,
|
||||
settings,
|
||||
sshTargetLabels,
|
||||
sshConnectionStates,
|
||||
runtimeStatusByEnvironmentId,
|
||||
hostLabelOverrides
|
||||
]
|
||||
)
|
||||
const projectHostSetupProjection = useAppStore((state) =>
|
||||
getProjectHostSetupProjectionFromState(state)
|
||||
@@ -117,8 +121,7 @@ export function RepositoryHostSetupsSection({
|
||||
: []
|
||||
const setupHostOptions = buildSetupHostOptions({
|
||||
projectHostSetups,
|
||||
sshTargetLabels,
|
||||
activeRuntimeEnvironmentId
|
||||
hostOptions
|
||||
})
|
||||
const [selectedSetupHostId, setSelectedSetupHostId] = useState<ExecutionHostId | null>(null)
|
||||
const [setupPath, setSetupPath] = useState('')
|
||||
|
||||
@@ -162,4 +162,43 @@ describe('execution host registry', () => {
|
||||
{ id: 'runtime:env-2', kind: 'runtime', label: 'env-2', health: 'available' }
|
||||
])
|
||||
})
|
||||
|
||||
it('includes runtime hosts from hydrated status even when they are not focused', () => {
|
||||
const hosts = buildExecutionHostRegistry({
|
||||
repos: [],
|
||||
settings: { activeRuntimeEnvironmentId: null },
|
||||
runtimeStatusByEnvironmentId: new Map([
|
||||
[
|
||||
'gpu',
|
||||
{
|
||||
appVersion: '1.8.0',
|
||||
status: {
|
||||
runtimeId: 'runtime-gpu',
|
||||
rendererGraphEpoch: 1,
|
||||
graphStatus: 'ready',
|
||||
authoritativeWindowId: 1,
|
||||
liveTabCount: 0,
|
||||
liveLeafCount: 0,
|
||||
runtimeProtocolVersion: RUNTIME_PROTOCOL_VERSION,
|
||||
minCompatibleRuntimeClientVersion: 1,
|
||||
capabilities: ['project-host-setup.v1'],
|
||||
hostPlatform: 'linux'
|
||||
}
|
||||
}
|
||||
]
|
||||
])
|
||||
})
|
||||
|
||||
expect(hosts).toMatchObject([
|
||||
{ id: 'local', health: 'local' },
|
||||
{
|
||||
id: 'runtime:gpu',
|
||||
kind: 'runtime',
|
||||
label: 'gpu',
|
||||
health: 'available',
|
||||
capabilities: ['project-host-setup.v1'],
|
||||
platform: 'linux'
|
||||
}
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -161,6 +161,9 @@ export function buildExecutionHostRegistry(args: {
|
||||
args.runtimeStatusByEnvironmentId
|
||||
)
|
||||
}
|
||||
for (const environmentId of args.runtimeStatusByEnvironmentId?.keys() ?? []) {
|
||||
addRuntimeHost(hosts, environmentId, environmentId, args.runtimeStatusByEnvironmentId)
|
||||
}
|
||||
|
||||
const focusedHost = getSettingsFocusedExecutionHostId(args.settings)
|
||||
const parsedFocusedHost = parseExecutionHostId(focusedHost)
|
||||
|
||||
Reference in New Issue
Block a user