diff --git a/src/renderer/src/components/orca-cloud-setup/CloudSetupPage.tsx b/src/renderer/src/components/orca-cloud-setup/CloudSetupPage.tsx
index 2c92bc3ba0b..136b2899d67 100644
--- a/src/renderer/src/components/orca-cloud-setup/CloudSetupPage.tsx
+++ b/src/renderer/src/components/orca-cloud-setup/CloudSetupPage.tsx
@@ -12,6 +12,7 @@ import type { CloudSetupStep } from '@/components/new-workspace/run-on/cloud-sta
import { CloudSetupErrorNotice } from '@/components/new-workspace/cloud-setup/CloudSetupErrorNotice'
import { sessionDetailCopy } from '@/components/new-workspace/run-on/cloud-setup-session-copy'
import type { CloudSetupFlow } from './use-cloud-setup-flow'
+import { openOrcaCloudAgentAccounts } from '@/components/new-workspace/OrcaCloudAgentSetupRow'
export type CloudSetupPageProps = {
flow: CloudSetupFlow
@@ -286,11 +287,9 @@ function StepActions({ flow }: { flow: CloudSetupFlow }): React.JSX.Element | nu
case 'agent':
return (
- {flow.current?.action ? (
-
- ) : null}
+
diff --git a/src/renderer/src/components/orca-cloud-setup/cloud-setup-page-model.test.ts b/src/renderer/src/components/orca-cloud-setup/cloud-setup-page-model.test.ts
index f6864620616..29903001e3d 100644
--- a/src/renderer/src/components/orca-cloud-setup/cloud-setup-page-model.test.ts
+++ b/src/renderer/src/components/orca-cloud-setup/cloud-setup-page-model.test.ts
@@ -1,7 +1,6 @@
import { describe, expect, it } from 'vitest'
import type { CloudSetupStep } from '@/components/new-workspace/run-on/cloud-start-model'
import { actionForStep, cloudSetupPageModel, sessionNeedsFrom } from './cloud-setup-page-model'
-import { effectiveAvailability } from './use-cloud-setup-flow'
describe('cloudSetupPageModel', () => {
it('walks one step at a time: the first unfinished step is current', () => {
@@ -44,14 +43,3 @@ describe('cloudSetupPageModel', () => {
expect(actionForStep({ id: 'github', state: 'loading' })).toBe('checking')
})
})
-
-describe('effectiveAvailability', () => {
- it('a sign-in that lands upgrades the snapshot from Start', () => {
- expect(effectiveAvailability('needs-sign-in', { state: 'signed-out' })).toBe('needs-sign-in')
- expect(effectiveAvailability('needs-sign-in', { state: 'loading' })).toBe('needs-sign-in')
- expect(
- effectiveAvailability('needs-sign-in', { state: 'entitled', remainingSeconds: 60 })
- ).toBe('offered')
- expect(effectiveAvailability('offered', { state: 'signed-out' })).toBe('offered')
- })
-})
diff --git a/src/renderer/src/components/orca-cloud-setup/use-cloud-setup-flow.ts b/src/renderer/src/components/orca-cloud-setup/use-cloud-setup-flow.ts
index 1dcc361fdd5..bac1d3e1a45 100644
--- a/src/renderer/src/components/orca-cloud-setup/use-cloud-setup-flow.ts
+++ b/src/renderer/src/components/orca-cloud-setup/use-cloud-setup-flow.ts
@@ -33,15 +33,42 @@ import { cloudSetupPageModel, sessionNeedsFrom, type CloudSetupPageModel } from
/** Billing rows re-read this often while the page waits on a browser step (focus also re-reads). */
const BILLING_POLL_MS = 4_000
-/** Main's availability is a snapshot from Start; once billing answers for a signed-in account, it wins. */
-export function effectiveAvailability(
- requested: OrcaCloudAvailability,
- access: OrcaCloudRunTargetAccess
+/**
+ * Main's Orca Cloud availability for the project (needs-sign-in, needs-capability, offered), read
+ * the way the composer reads it and re-read while the page waits, so a sign-in in the browser (or
+ * the dev sign-in) moves the page on. Starts from the composer's answer at Start.
+ */
+function useLiveAvailability(
+ repoId: string,
+ initial: OrcaCloudAvailability,
+ enabled: boolean
): OrcaCloudAvailability {
- if (requested === 'needs-sign-in' && access.state !== 'signed-out' && access.state !== 'loading') {
- return 'offered'
- }
- return requested
+ const [availability, setAvailability] = useState(initial)
+ useEffect(() => {
+ if (!enabled) {
+ return
+ }
+ let cancelled = false
+ const read = (): void => {
+ void Promise.resolve()
+ .then(() => window.api.ephemeralVm.listRecipes({ repoId }))
+ .then((result) => {
+ if (!cancelled && result.orcaCloud) {
+ setAvailability(result.orcaCloud)
+ }
+ })
+ .catch(() => undefined)
+ }
+ read()
+ const timer = window.setInterval(read, BILLING_POLL_MS)
+ window.addEventListener('focus', read)
+ return () => {
+ cancelled = true
+ window.clearInterval(timer)
+ window.removeEventListener('focus', read)
+ }
+ }, [enabled, repoId])
+ return availability
}
export type CloudSetupFlow = CloudSetupPageModel & {
@@ -69,7 +96,8 @@ export function useCloudSetupFlow(
): CloudSetupFlow {
const fetched = useOrcaCloudRunTargetAccess(!accessOverride)
const access = accessOverride ?? fetched
- const availability = effectiveAvailability(request.availability, access)
+ const [settled, setSettled] = useState(false)
+ const availability = useLiveAvailability(request.repoId, request.availability, !settled && !accessOverride)
const [sessionId, setSessionId] = useState(null)
const [inBrowser, setInBrowser] = useState(false)
const [busy, setBusy] = useState(false)
@@ -89,6 +117,11 @@ export function useCloudSetupFlow(
})
const { steps, held: switchTeam } = useSwitchTeamSteps(rowSteps, !session)
const model = cloudSetupPageModel(steps, { agentSkipped, teamSwitch: switchTeam !== null })
+ useEffect(() => {
+ if (model.action === 'done') {
+ setSettled(true)
+ }
+ }, [model.action])
const error = useMemo(() => {
const parsed = parseCloudSetupError(session?.error)
return parsed && session?.teamName ? { ...parsed, teamName: session.teamName } : parsed