diff --git a/doc/openai-dot-runner.md b/doc/openai-dot-runner.md index 59b35935482..13b0dabbaae 100644 --- a/doc/openai-dot-runner.md +++ b/doc/openai-dot-runner.md @@ -155,3 +155,12 @@ an unexpected exit. A real Rust regression reproduces the failing order and passes with the fix. Four driver tests and 11 integration/pairing tests passed, including a failed connection refresh after successful revocation. Revocation clears the cached binding before refetch so that failure cannot restore it. + +Visual review uses the production `DotRunnerConnection` component in the +`Assistant connections/Dot Runner` Storybook stories. Pairing, event-test +waiting and revocation were exercised in the browser with synthetic API +responses. These screenshots show preview data, not a qualified Dot account: + + + + diff --git a/doc/screenshots/openai-dot-runner/connected.jpg b/doc/screenshots/openai-dot-runner/connected.jpg new file mode 100644 index 00000000000..458d610bc1f Binary files /dev/null and b/doc/screenshots/openai-dot-runner/connected.jpg differ diff --git a/doc/screenshots/openai-dot-runner/pairing.jpg b/doc/screenshots/openai-dot-runner/pairing.jpg new file mode 100644 index 00000000000..89e81c93254 Binary files /dev/null and b/doc/screenshots/openai-dot-runner/pairing.jpg differ diff --git a/ui/storybook/stories/dot-runner-connection.stories.tsx b/ui/storybook/stories/dot-runner-connection.stories.tsx new file mode 100644 index 00000000000..fd00dd211b6 --- /dev/null +++ b/ui/storybook/stories/dot-runner-connection.stories.tsx @@ -0,0 +1,53 @@ +import { useState } from "react"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { expect, userEvent, within } from "storybook/test"; +import { DotRunnerConnection } from "@/components/DotRunnerConnection"; + +/** All connection mutations and pairing codes are synthetic and local. */ +function installFixture(connected: boolean) { + const original = window.fetch; + let binding = connected ? connection("connected") : null; + window.fetch = async (input, init) => { + const url = new URL(typeof input === "string" ? input : input instanceof URL ? input.href : input.url, location.origin); + if (!url.pathname.startsWith("/api/companies/dot-preview/agents/dot-preview/dot-binding")) return original(input, init); + const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase(); + if (method === "DELETE") { binding = null; return new Response(null, { status: 204 }); } + if (method === "POST") { + if (url.pathname.endsWith("/event-test")) { if (binding) binding = { ...binding, hasPendingChallenge: true }; return Response.json({}); } + binding = connection("pairing"); + return Response.json({ bindingId: binding.id, pairingCode: "dot_pair_SYNTHETIC_PREVIEW_CODE", expiresAt: new Date(Date.now() + 600_000).toISOString() }); + } + return Response.json({ enabled: true, resourceUrl: "https://paperclip.example/mcp/runner", binding }); + }; + return () => { window.fetch = original; }; +} +function connection(status: "pairing" | "connected") { + return { id: "synthetic-dot-binding", status, connected: status === "connected", + subscriptionVerified: status === "connected", hasPendingChallenge: false }; +} +function ConnectionForm() { + const [bindingId, onBinding] = useState(""); + return