+
Use your saved Claude subscription for this agent.
- ) : connectStepHasNoSandbox ? ( + ) : hasSavedSubscription || localLogin.status === "ready" ? null : connectStepHasNoSandbox ? ( canUseLocalLogin && managedProvider ? ( -This environment does not support browser sign-in. Choose another sign-in environment or connect with an API key.
) : null} @@ -2805,13 +2852,7 @@ function OnboardingWizardInner({ way to judge one — and the agent's model is changeable later, where its work gives the choice meaning. */} - {/* The environment check runs without being shown: Connect - probes the adapter before hiring (see handleGiveHeartbeat) - and blocks the hire on a fail. The idle card — probe - explainer plus a "Test now" button — is gone from this - step, so this block renders only when a probe has actually - found something: the checks the blocking error tells the - customer to fix have to be visible somewhere. */} + {/* Progress is shown above; failed checks remain actionable here. */} {/* Not while the hire is in flight. The probe's result lands before the hire it gates has finished, so a warn that does not block — the identity and target INFO checks, which diff --git a/ui/storybook/.storybook/preview.tsx b/ui/storybook/.storybook/preview.tsx index ebabff60004..0f465155f20 100644 --- a/ui/storybook/.storybook/preview.tsx +++ b/ui/storybook/.storybook/preview.tsx @@ -151,10 +151,14 @@ function installStorybookApiFixtures() { // The cloud-tenant shape, and what the onboarding connect step resolves // its login environment through: without it the step looks for a local // default and never finds the managed sandbox. - enableManagedSandboxOnly: true, + enableManagedSandboxOnly: onboardingFixtureState.environments !== "local", }); } + if (url.pathname === "/api/health") { + return Response.json({ deploymentMode: "authenticated", localAiLoginSupported: onboardingFixtureState.environments === "local" }); + } + if (url.pathname === "/api/instance/settings") { return Response.json({}); } @@ -307,10 +311,31 @@ function installStorybookApiFixtures() { ? Response.json({ secretId: "saved-claude-subscription", latestVersion: 1 }) : new Response(null, { status: 404 }); } + if (/^\/api\/companies\/[^/]+\/ai-connections\/local\/attempts$/.test(url.pathname)) { + const body = typeof init?.body === "string" ? JSON.parse(init.body) : {}; + return Response.json({ sessionId: "local-storybook", expiresAt: new Date(Date.now() + 1_800_000).toISOString(), command: body.provider === "anthropic" + ? "CLAUDE_CONFIG_DIR='/paperclip/login' claude auth login" + : "CODEX_HOME='/paperclip/login' codex login --device-auth" }); + } + if (/^\/api\/companies\/[^/]+\/ai-connections\/local\/check$/.test(url.pathname)) { + return Response.json({ status: onboardingFixtureState.localLoginStatus }); + } + if (/^\/api\/companies\/[^/]+\/ai-connections\/local$/.test(url.pathname)) { + const body = typeof init?.body === "string" ? JSON.parse(init.body) : {}; + if (onboardingFixtureState.localLoginStatus !== "ready") return Response.json({ error: "Finish signing in, then try Connect again." }, { status: 422 }); + if (onboardingFixtureState.connectPending) await new Promise(() => {}); + onboardingFixtureState.savedManagedSubscription = body.provider; + return Response.json({ connectionId: "managed-storybook", grantId: "grant-storybook" }); + } if (/^\/api\/companies\/[^/]+\/ai-connections$/.test(url.pathname)) { + const provider = onboardingFixtureState.savedManagedSubscription; return init?.method === "POST" ? Response.json({ connectionId: "managed-storybook", grantId: "grant-storybook" }) - : Response.json({ currentUserId: "user-storybook", connections: [] }); + : Response.json({ currentUserId: "user-storybook", connections: provider ? [{ + id: "managed-storybook", grantId: "grant-storybook", companyId: "company-storybook", + provider, method: "subscription", name: provider === "anthropic" ? "My Claude subscription" : "My OpenAI subscription", + ownership: "personal", ownerUserId: "user-storybook", status: "connected", isDefault: true, + }] : [] }); } if (/^\/api\/companies\/[^/]+\/me\/user-secrets$/.test(url.pathname)) { return Response.json(onboardingFixtureState.savedApiKeys ? ["ANTHROPIC_API_KEY", "OPENAI_API_KEY"].map((key) => ({ @@ -333,12 +358,18 @@ function installStorybookApiFixtures() { /^\/api\/companies\/[^/]+\/adapters\/([^/]+)\/test-environment$/, ); if (testEnvMatch) { + if (onboardingFixtureState.testPending) await new Promise(() => {}); + await new Promise(resolve => setTimeout(resolve, onboardingFixtureState.testDelayMs)); + if (onboardingFixtureState.testFailuresRemaining > 0) { + onboardingFixtureState.testFailuresRemaining--; + return Response.json({ error: "The provider did not respond. Try connecting again." }, { status: 502 }); + } const body = typeof init?.body === "string" ? JSON.parse(init.body) : {}; const env = body.adapterConfig?.env ?? {}; const usesSavedCodex = onboardingFixtureState.savedCodexLogin && env.CODEX_HOME?.secretId === "saved-codex-home"; const usesSavedKey = onboardingFixtureState.savedApiKeys && ["ANTHROPIC_API_KEY", "OPENAI_API_KEY"].some((key) => env[key]?.type === "user_secret_ref" && env[key]?.key === `${key}.setup.storybook`); const usesSavedClaude = onboardingFixtureState.savedClaudeLogin && env.CLAUDE_CODE_OAUTH_TOKEN?.type === "user_secret_ref" && env.CLAUDE_CODE_OAUTH_TOKEN?.key === "CLAUDE_CODE_OAUTH_TOKEN"; - return Response.json(usesSavedCodex || usesSavedKey || usesSavedClaude ? { adapterType: testEnvMatch[1], status: "pass", checks: [], testedAt: new Date(0).toISOString() } : storybookEnvironmentTest(testEnvMatch[1])); + return Response.json((body.aiConnection?.provider === onboardingFixtureState.savedManagedSubscription) || usesSavedCodex || usesSavedKey || usesSavedClaude ? { adapterType: testEnvMatch[1], status: "pass", checks: [], testedAt: new Date(0).toISOString() } : storybookEnvironmentTest(testEnvMatch[1])); } if (/^\/api\/companies\/[^/]+\/agent-hires$/.test(url.pathname)) { // `approval: null` on purpose. A hire that returns one sends the wizard @@ -524,7 +555,7 @@ function installStorybookApiFixtures() { if (secretsListMatch) { const [, companyId] = secretsListMatch; return Response.json( - companyId === "company-storybook" ? [...storybookSecrets, ...(onboardingFixtureState.savedCodexLogin ? [{ ...storybookSecrets[0], id: "saved-codex-home", key: "codex_home_saved", name: "CODEX_HOME_team-account" }] : [])] : [], + companyId === "company-storybook" && onboardingFixtureState.environments !== "local" ? [...storybookSecrets, ...(onboardingFixtureState.savedCodexLogin ? [{ ...storybookSecrets[0], id: "saved-codex-home", key: "codex_home_saved", name: "CODEX_HOME_team-account" }] : [])] : [], ); } @@ -749,7 +780,7 @@ const preview: Preview = { (Story, context) => { const theme = context.globals.theme === "light" ? "light" : "dark"; return ( -