From fd63d1527893860697d9148d962cbc7648a7cfd7 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Thu, 14 May 2026 23:59:37 -0600 Subject: [PATCH] docs: add standalone debugger documentation - Add guide page for the standalone debugger (getting started, configuration, sessions, MCP mode, troubleshooting) - Add package reference page covering WebSocket protocol, MCP tools, Effect services, and architecture - Update bridge package page with attachDebugger section - Update getting-started with standalone debugger quick start - Update repository-structure with standalone in package map and dependency graph - Add standalone package card to homepage - Add standalone to architecture diagram and package relationships Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/docs/app/Route/Architecture.elm | 15 +- apps/docs/app/Route/Index.elm | 6 + .../contributing/repository-structure.md | 6 + apps/docs/content/docs/getting-started.md | 13 ++ apps/docs/content/docs/standalone-debugger.md | 155 ++++++++++++++++++ apps/docs/content/packages/devtools-bridge.md | 40 ++++- .../content/packages/devtools-standalone.md | 136 +++++++++++++++ 7 files changed, 365 insertions(+), 6 deletions(-) create mode 100644 apps/docs/content/docs/standalone-debugger.md create mode 100644 apps/docs/content/packages/devtools-standalone.md diff --git a/apps/docs/app/Route/Architecture.elm b/apps/docs/app/Route/Architecture.elm index 9f4f764..fd11681 100644 --- a/apps/docs/app/Route/Architecture.elm +++ b/apps/docs/app/Route/Architecture.elm @@ -96,7 +96,10 @@ view app _ = [ Html.text "Shared annotators (OIDC phase detection, CORS, DPoP, PAR), diagnosis engine, event store, and export/redaction logic. Used by both the browser extension and VS Code extension." ] , Html.dt [] [ Html.text "@wolfcola/devtools-bridge" ] , Html.dd [] - [ Html.text "SDK adapter for emitting events from DaVinci, Journey, and OIDC clients. Depends on devtools-types." ] + [ Html.text "SDK adapter for emitting events from DaVinci, Journey, and OIDC clients. Connects to the browser extension or standalone debugger. Depends on devtools-types." ] + , Html.dt [] [ Html.text "@wolfcola/devtools-standalone" ] + , Html.dd [] + [ Html.text "Standalone Electron debugger with WebSocket server and MCP integration. Uses devtools-core for event processing and devtools-ui for the Elm panel." ] , Html.dt [] [ Html.text "@wolfcola/devtools-ui" ] , Html.dd [] [ Html.text "Elm UI components for Timeline, Flow, and Learn views. Provides the panel interface with inspector tabs, playback controls, and diagnosis display." ] @@ -132,19 +135,20 @@ view app _ = viewDiagram : Html.Html (PagesMsg Msg) viewDiagram = Svg.svg - [ SvgAttr.viewBox "0 0 900 480" + [ SvgAttr.viewBox "0 0 900 580" , SvgAttr.width "900" - , SvgAttr.height "480" + , SvgAttr.height "580" , SvgAttr.style "max-width: 100%; height: auto;" ] [ -- OIDC DevTools group - svgGroup 20 20 520 440 "OIDC DevTools" + svgGroup 20 20 520 540 "OIDC DevTools" , svgBox 180 60 200 50 "devtools-types" , svgBox 40 160 200 50 "devtools-core" , svgBox 300 160 200 50 "devtools-bridge" , svgBox 40 270 200 50 "devtools-ui" , svgBox 40 370 200 50 "devtools-extension" , svgBox 300 370 200 50 "vscode-extension" + , svgBox 300 470 200 50 "devtools-standalone" -- Tree-Shake Tools group , svgGroup 570 20 310 200 "Tree-Shake Tools" @@ -171,6 +175,9 @@ viewDiagram = -- Arrows: devtools-core -> vscode-extension , svgArrow 200 210 380 370 + -- Arrows: devtools-core -> devtools-standalone + , svgArrow 200 210 380 470 + -- Arrows: treeshake-check -> eslint-plugin-treeshake , svgArrow 720 110 720 160 ] diff --git a/apps/docs/app/Route/Index.elm b/apps/docs/app/Route/Index.elm index 0a89006..2264040 100644 --- a/apps/docs/app/Route/Index.elm +++ b/apps/docs/app/Route/Index.elm @@ -125,6 +125,12 @@ viewPackageGrid = , route = Route.Packages__Slug_ { slug = "dead-export-finder" } , tag = "Published" } + , viewPackageCard + { name = "@wolfcola/devtools-standalone" + , description = "Standalone Electron debugger with WebSocket server and MCP integration" + , route = Route.Packages__Slug_ { slug = "devtools-standalone" } + , tag = "New" + } ] diff --git a/apps/docs/content/contributing/repository-structure.md b/apps/docs/content/contributing/repository-structure.md index 2b2059b..b6ad169 100644 --- a/apps/docs/content/contributing/repository-structure.md +++ b/apps/docs/content/contributing/repository-structure.md @@ -20,6 +20,7 @@ The wolfcola-devtools repository is a pnpm workspace monorepo. All publishable p | `@wolfcola/devtools-core` | `packages/devtools-core` | Shared annotators, diagnosis engine, event store | | `@wolfcola/devtools-ui` | `packages/devtools-ui` | Elm UI components for Timeline, Flow, and Learn views | | `@wolfcola/devtools-extension` | `packages/devtools-extension` | Browser extension for Chrome and Firefox | +| `@wolfcola/devtools-standalone` | `packages/devtools-standalone` | Standalone Electron debugger with WebSocket and MCP | | `oidc-devtools` | `packages/vscode-extension` | VS Code extension with CDP connection | | `@wolfcola/dead-export-finder` | `packages/dead-export-finder` | CLI to find unused exports across monorepo boundaries | | `@wolfcola/changeset-sync-manifest` | `packages/changeset-sync-manifest` | Syncs package version from changesets to manifest files | @@ -60,6 +61,11 @@ devtools-extension ├── devtools-ui (Elm) └── devtools-types +devtools-standalone + ├── devtools-core + ├── devtools-ui (Elm) + └── devtools-types + devtools-bridge └── devtools-types diff --git a/apps/docs/content/docs/getting-started.md b/apps/docs/content/docs/getting-started.md index 1833023..7338b44 100644 --- a/apps/docs/content/docs/getting-started.md +++ b/apps/docs/content/docs/getting-started.md @@ -61,9 +61,22 @@ Scan your monorepo for unused exports: npx dead-export-finder ``` +## Standalone Debugger + +For environments where a browser extension is not available (Node.js servers, React Native, Electron apps), use the standalone debugger: + +```typescript +import { attachDebugger } from '@wolfcola/devtools-bridge'; + +const handle = await attachDebugger({ name: 'my-app' }); +``` + +The standalone debugger connects via WebSocket and can auto-launch if installed on your PATH. See the [Standalone Debugger](/docs/standalone-debugger) guide for details. + ## Next Steps - Read the [Tree-Shaking Guide](/docs/tree-shaking) - Explore the [Architecture](/architecture) - Learn about the [DevTools Extension](/docs/devtools-extension) - Check out the [VS Code Extension](/docs/vscode-extension) +- Try the [Standalone Debugger](/docs/standalone-debugger) for non-browser environments diff --git a/apps/docs/content/docs/standalone-debugger.md b/apps/docs/content/docs/standalone-debugger.md new file mode 100644 index 0000000..d37e2b7 --- /dev/null +++ b/apps/docs/content/docs/standalone-debugger.md @@ -0,0 +1,155 @@ +--- +title: 'Standalone Debugger' +description: 'Use the standalone Electron debugger for OIDC/OAuth2 debugging without a browser extension' +section: guides +order: 3 +--- + +# Standalone Debugger + +The standalone debugger is an Electron desktop app that provides the same OIDC debugging UI as the browser extension, but connects via WebSocket instead of browser APIs. This makes it suitable for: + +- **Node.js servers and backend services** — no browser extension available +- **React Native and mobile webviews** — can't install browser extensions +- **Electron apps** — debug your own Electron app's auth flows +- **Any environment** where installing a browser extension is impractical + +## Quick Start + +### 1. Start the debugger + +```bash +# From the monorepo +pnpm --filter @wolfcola/devtools-standalone build +pnpm --filter @wolfcola/devtools-standalone start +``` + +The debugger opens an Electron window and starts a WebSocket server on `localhost:19417`. + +### 2. Connect your app + +Install the bridge in your application: + +```bash +npm install @wolfcola/devtools-bridge +``` + +Then call `attachDebugger`: + +```typescript +import { attachDebugger } from '@wolfcola/devtools-bridge'; + +const handle = await attachDebugger({ + name: 'my-app', +}); + +// Events are now forwarded to the standalone debugger. +// When done: +handle.detach(); +``` + +That's it. The bridge connects via WebSocket, installs a fetch interceptor to capture auth-related network requests, and forwards everything to the debugger. + +## Configuration + +`attachDebugger` accepts these options: + +| Option | Type | Default | Purpose | +| ------------ | --------- | ------- | ---------------------------------------------------------- | +| `name` | `string` | — | App name shown in the session list (required) | +| `port` | `number` | `19417` | WebSocket server port | +| `autoLaunch` | `boolean` | `true` | Launch the debugger binary if not already running | +| `network` | `boolean` | `true` | Install fetch interceptor to capture auth-related requests | +| `pid` | `number` | — | Process ID (optional metadata) | +| `framework` | `string` | — | Framework name (optional metadata) | + +### Auto-launch + +When `autoLaunch` is enabled (the default), the bridge searches your PATH for `wolfcola-devtools`, launches it as a background process, and retries the WebSocket connection with exponential backoff. If the binary isn't found or launch fails, the bridge logs a warning and returns `{ connected: false }`. + +### Custom port + +```typescript +// Start the debugger on a custom port +// electron dist/src/main.cjs --port 9000 + +const handle = await attachDebugger({ + name: 'my-app', + port: 9000, +}); +``` + +## Node.js HTTP Interceptor + +For server-side apps that use Node's `http`/`https` modules instead of `fetch`, install the Node HTTP interceptor separately: + +```typescript +import { + attachDebugger, + installNodeHttpInterceptor, + uninstallNodeHttpInterceptor, +} from '@wolfcola/devtools-bridge'; + +const handle = await attachDebugger({ name: 'my-server', network: false }); + +installNodeHttpInterceptor((entry) => { + // Forward captured HTTP requests to the debugger + client.sendNetworkEvent(entry); +}); + +// Cleanup +uninstallNodeHttpInterceptor(); +handle.detach(); +``` + +The interceptor patches `http.request` and `https.request` to capture request/response data for auth-related URLs. + +## Sessions + +Each connected app appears as a session in the debugger. Sessions track: + +- **Name** — from the `name` option in `attachDebugger` +- **Status** — connected or disconnected +- **Framework** and **PID** — optional metadata +- **Events** — all `AuthEvent` objects received from the app + +When an app disconnects and reconnects, the debugger can either preserve or clear the previous session's events (controlled by the "clear on reconnect" setting per session). + +## MCP Mode + +The standalone debugger can also run as a headless MCP server for AI agent integration: + +```bash +electron dist/src/main.cjs --mcp +``` + +In this mode there is no UI — the debugger communicates via stdio using the Model Context Protocol. This is useful for automated debugging with Claude or other AI assistants. See the [package reference](/packages/devtools-standalone) for the full list of MCP tools. + +## Views + +The standalone debugger uses the same Elm UI as the browser extension. You get the same three views: + +- **Timeline** — chronological list of all auth events +- **Flow** — state diagram of the current OIDC flow +- **Learn** — contextual documentation for OIDC concepts + +See the [DevTools Extension](/docs/devtools-extension) guide for details on each view. + +## Troubleshooting + +**WebSocket connection fails:** + +1. Verify the debugger is running: look for the Electron window or check if port 19417 is in use +2. Check that no firewall is blocking localhost connections +3. Try a custom port if 19417 is taken: `--port 9000` + +**No events appearing:** + +1. Confirm `handle.connected` is `true` after calling `attachDebugger` +2. Verify your app is making auth-related requests (the interceptor filters by URL pattern) +3. Check the browser/Node console for warnings from the bridge + +**Auto-launch not working:** + +1. Ensure `wolfcola-devtools` is on your PATH +2. Try launching the debugger manually first to rule out startup errors diff --git a/apps/docs/content/packages/devtools-bridge.md b/apps/docs/content/packages/devtools-bridge.md index 21d5fb8..3994f05 100644 --- a/apps/docs/content/packages/devtools-bridge.md +++ b/apps/docs/content/packages/devtools-bridge.md @@ -1,13 +1,13 @@ --- title: '@wolfcola/devtools-bridge' -description: 'SDK adapter for emitting auth events to the Ping DevTools extension' +description: 'SDK adapter for emitting auth events to the DevTools extension or standalone debugger' section: packages order: 3 --- # @wolfcola/devtools-bridge -The devtools bridge connects your OIDC client to the Ping DevTools browser extension. It monitors SDK state changes and emits `AuthEvent` objects via `CustomEvent` on the window, where the extension picks them up. +The devtools bridge connects your OIDC client to Ping DevTools — either the browser extension, the VS Code extension, or the [standalone debugger](/docs/standalone-debugger). It monitors SDK state changes and emits `AuthEvent` objects via `CustomEvent` on the window (for extensions) or WebSocket (for the standalone debugger). ## Installation @@ -179,4 +179,40 @@ Call `detach()` to unsubscribe the bridge from the SDK client. Events are stored on `window.__PING_DEVTOOLS_STATE__` as an array of `AuthEvent` objects. The array is capped at 500 entries; when the limit is exceeded, the oldest entries are removed via `splice`. +## Standalone Debugger — `attachDebugger` + +Connects your app to the [standalone Electron debugger](/docs/standalone-debugger) via WebSocket. Works in both browser and Node.js environments. + +```typescript +import { attachDebugger } from '@wolfcola/devtools-bridge'; + +const handle = await attachDebugger({ + name: 'my-spa', + port: 19417, + autoLaunch: true, + network: true, + framework: 'react', +}); + +handle.detach(); +``` + +Returns a `DebuggerHandle`: + +```typescript +interface DebuggerHandle { + connected: boolean; + detach(): void; +} +``` + +**What happens on `attachDebugger()`:** + +1. Opens a WebSocket to `ws://localhost:{port}` and sends a handshake +2. If not connected and `autoLaunch` is enabled, finds `wolfcola-devtools` in PATH, spawns it, and retries with exponential backoff +3. If connected and `network` is enabled, installs a fetch interceptor that forwards auth-related requests +4. Returns `{ connected, detach() }` + +See the [Standalone Debugger guide](/docs/standalone-debugger) for full configuration options and Node.js HTTP interceptor usage. + Always call `handle.detach()` when you are done. Failing to do so may cause memory leaks from lingering subscription listeners. diff --git a/apps/docs/content/packages/devtools-standalone.md b/apps/docs/content/packages/devtools-standalone.md new file mode 100644 index 0000000..0f7f427 --- /dev/null +++ b/apps/docs/content/packages/devtools-standalone.md @@ -0,0 +1,136 @@ +--- +title: '@wolfcola/devtools-standalone' +description: 'Standalone Electron OIDC/OAuth2 debugger with WebSocket server and MCP integration' +section: packages +order: 5 +--- + +# @wolfcola/devtools-standalone + +Standalone Electron desktop app for OIDC/OAuth2 debugging. Apps connect via WebSocket using `@wolfcola/devtools-bridge`. Also runs as a headless MCP server for AI agent integration. + +## Installation + +This is a private workspace package — it is not published to npm. Build and run it from the monorepo: + +```bash +pnpm --filter @wolfcola/devtools-ui build +pnpm --filter @wolfcola/devtools-standalone build +pnpm --filter @wolfcola/devtools-standalone start +``` + +The Elm UI must be built before the standalone package, since the build copies `elm.js` and `panel.css` from devtools-ui. + +**Dependencies:** `effect`, `@effect/platform`, `@effect/platform-node`, `@effect/ai`, `ws`, `electron`, `@wolfcola/devtools-core`, `@wolfcola/devtools-types`, `@wolfcola/devtools-ui` + +## Modes + +### GUI Mode (default) + +Opens an Electron window with the same Elm UI as the browser extension. A WebSocket server binds to `127.0.0.1:19417` (localhost only). + +```bash +electron dist/src/main.cjs # default port +electron dist/src/main.cjs --port 9000 # custom port +``` + +### MCP Mode + +Headless stdio-based MCP server for Claude and other AI agents. Same session management and tools, no UI. + +```bash +electron dist/src/main.cjs --mcp +``` + +## WebSocket Protocol + +All messages are JSON, validated with Effect Schema on the server side. + +### Handshake + +```json +// Client → +{ "type": "HANDSHAKE", "name": "my-app", "pid": 1234, "framework": "react" } + +// Server → +{ "type": "CONNECTED", "sessionId": "550e8400-..." } +``` + +### Event Ingestion + +```json +// SDK event +{ "type": "SDK_EVENT", "payload": { ... } } + +// Network event (HAR entry format) +{ "type": "NETWORK_EVENT", "payload": { "request": {}, "response": {}, "time": 0 } } +``` + +### Clear and Error + +```json +// Clear session events +{ "type": "CLEAR" } + +// Server error response +{ "type": "ERROR", "message": "Failed to process message" } +``` + +## MCP Tools + +When running in MCP mode, the following tools are available: + +| Tool | Purpose | +| ------------------------ | ----------------------------------------------------- | +| `list-sessions` | List all connected and disconnected sessions | +| `get-events` | Get events with optional type/time filtering | +| `get-flow-summary` | Summary metrics (node count, error count, etc.) | +| `get-diagnosis` | Run diagnosis engine on session events | +| `get-event-detail` | Full event details (headers, body, OIDC semantics) | +| `search-events` | Find events by URL pattern, error-only, or OIDC phase | +| `clear-flow` | Clear all events in a session | +| `export-json` | Redacted JSON export | +| `export-markdown` | Redacted Markdown export with diagnosis | +| `set-clear-on-reconnect` | Toggle auto-clear on session reconnect | + +All queries apply `redactFlowState` before returning data — sensitive values (tokens, passwords, credentials) are never exposed through MCP. + +## Effect Services + +| Service | Layer | Purpose | +| ----------------- | --------------------- | ---------------------------------------------------------------------------- | +| `SessionManager` | `SessionManagerLive` | In-memory session state (`Ref`) with per-session `ManagedRuntime` | +| `WsServer` | `WsServerLive` | `@effect/platform-node` WebSocket server with Schema-validated protocol | +| `WolfcolaToolkit` | `WolfcolaToolkitLive` | `@effect/ai` MCP tool definitions | + +The `SessionManager` creates a fresh `ManagedRuntime` with its own `EventStoreInMemory` for each session. This isolates session state and allows clean disposal on disconnect. + +## Architecture + +``` +┌─────────────────────────────────────────────────┐ +│ Electron Main │ +│ │ +│ SessionManager (Ref) │ +│ • create / reconnect / disconnect │ +│ • per-session ManagedRuntime + EventStore │ +│ │ +│ ┌────────────┐ ┌───────────┐ ┌───────────┐ │ +│ │ WsServer │ │ IpcBridge │ │ MCP Server│ │ +│ │ (Effect) │ │ (Electron)│ │ (Stdio) │ │ +│ └─────┬──────┘ └─────┬─────┘ └───────────┘ │ +│ │ │ │ +└────────┼───────────────┼────────────────────────┘ + │ │ + WebSocket IPC Channels + │ │ + ▼ ▼ + ┌──────────┐ ┌─────────────┐ + │ Your App │ │ Elm UI │ + │ │ │ (Renderer) │ + └──────────┘ └─────────────┘ +``` + +Events flow through: `StandaloneClient → WsServer → SessionManager.ingestEvent → handleMessage → EventStoreService → DiagnosisEngine → IPC → Elm UI` + +All state is transient — in-memory only. Closing the debugger loses all captured events. Use the export tools (JSON or Markdown) to save session data before closing.