A cross-browser extension that provides content management information for SF.gov pages. The extension displays a side panel when users navigate SF.gov pages, showing metadata and administrative links retrieved from the Wagtail CMS API.
Install the production version from the Chrome webstore.
Or download the nightly build and load it into Chrome as an unpacked extension.
This project is organized as an npm workspaces monorepo with three packages:
sf-gov-companion/
├── packages/
│ ├── extension/ # Browser extension workspace
│ │ ├── src/
│ │ │ ├── api/ # API clients, auth, page data transformer
│ │ │ ├── background/ # Background service worker
│ │ │ ├── content/ # Content scripts (DOM extraction, admin preview)
│ │ │ ├── lib/ # Shared utilities (analytics, console)
│ │ │ ├── sidepanel/ # Side panel UI (React app)
│ │ │ │ ├── components/ # React components
│ │ │ │ └── hooks/ # Custom React hooks
│ │ │ └── test/ # Test setup
│ │ ├── public/ # Public assets (icons, etc.)
│ │ ├── dist/ # Build output (generated)
│ │ ├── release/ # Distribution zip files (generated)
│ │ ├── package.json # Extension dependencies
│ │ ├── vite.config.ts # Vite build configuration
│ │ ├── tailwind.config.ts # Tailwind CSS configuration
│ │ └── manifest.config.ts # Extension manifest configuration
│ │
│ ├── server/ # Vercel API workspace
│ │ ├── api/ # Serverless functions
│ │ │ ├── auth/ # Token exchange endpoint
│ │ │ ├── feedback.ts # User feedback proxy endpoint
│ │ │ └── link-check.ts # Server-side link checking (SSE)
│ │ ├── lib/ # Shared utilities (auth, token, logging)
│ │ ├── dev-server.ts # Lightweight local dev server
│ │ ├── package.json # API dependencies
│ │ ├── tsconfig.json # TypeScript configuration
│ │ └── vercel.json # Vercel configuration
│ │
│ └── shared/ # Shared types workspace
│ ├── src/
│ │ ├── types/ # Shared TypeScript types
│ │ │ ├── wagtail.ts # Wagtail API types
│ │ │ ├── airtable.ts # Airtable API types
│ │ │ ├── auth.ts # Token exchange types
│ │ │ ├── link-check.ts # Link check types
│ │ │ └── index.ts
│ │ └── index.ts # Main export file
│ ├── package.json # Shared package config
│ └── tsconfig.json # TypeScript configuration
│
├── .kiro/ # Kiro AI assistant configuration
│ ├── specs/ # Feature specifications
│ └── steering/ # AI assistant steering rules
├── node_modules/ # Hoisted dependencies
├── package.json # Root workspace configuration
├── tsconfig.json # Root TypeScript configuration
└── README.md # This file
Install dependencies for all workspaces:
npm installThis will install dependencies for all workspaces and link them together.
For local development, you need to run both the extension and server:
-
Start the API server (in one terminal):
cd packages/server npm run devThis starts a lightweight Node dev server on
http://localhost:3000.Note: There's also
npm run dev:vercelwhich usesvercel dev, but it has significant performance issues on Windows (5+ second response delays due to a libuv bug). The Node dev server is recommended for local development. -
Configure the extension to use the local API:
cd packages/extension cp .env.example .env.localThe
.env.localfile should contain:VITE_API_BASE_URL=http://localhost:3000 -
Start the extension dev server (in another terminal):
npm run dev:extension
-
Load the extension in Chrome:
- Navigate to
chrome://extensions/ - Enable "Developer mode"
- Click "Load unpacked"
- Select the
packages/extension/dist/directory
- Navigate to
The extension will now use your local API server instead of the production Vercel deployment.
Individual workspace dev servers:
# Extension only (Vite dev server with HMR)
npm run dev:extension
# API server (lightweight Node server on port 3000)
cd packages/server && npm run dev
# API via Vercel dev (slower on Windows)
cd packages/server && npm run dev:vercelBuild all workspaces:
npm run buildOr build individual workspaces:
# Extension only
npm run build:extension
# API only
npm run build:serverThe extension build output will be in packages/extension/dist/ with a distribution zip in packages/extension/release/.
- Build the extension:
npm run build:extension - Open Chrome and navigate to
chrome://extensions/ - Enable "Developer mode"
- Click "Load unpacked"
- Select the
packages/extension/dist/directory
Add dependencies to specific workspaces using the --workspace flag:
# Add a dependency to the extension
npm install <package-name> --workspace=@sf-gov/extension
# Add a dev dependency to the extension
npm install <package-name> --save-dev --workspace=@sf-gov/extension
# Add a dependency to the API
npm install <package-name> --workspace=@sf-gov/server
# Add a dependency to shared types
npm install <package-name> --workspace=@sf-gov/sharedRun scripts in specific workspaces:
# Run a script in the extension workspace
npm run <script-name> --workspace=@sf-gov/extension
# Run a script in the API workspace
npm run <script-name> --workspace=@sf-gov/serverCheck types across all workspaces:
npm run type-checkOr check types in a specific workspace:
npm run type-check --workspace=@sf-gov/extensionThe browser extension built with React, Vite, and CRXJS. Contains the side panel UI and background service worker.
Key Scripts:
npm run dev:extension- Start Vite dev server with HMRnpm run build:extension- Build production extensionnpm run preview- Preview production buildnpm run release- Build and package for distribution
Dependencies:
- React 19 for UI components
- Vite 7 with CRXJS plugin for extension development
- Tailwind CSS 4 for styling
@sf-gov/sharedfor shared types
Vercel serverless functions for token exchange, feedback proxy, and link checking. Uses HMAC-signed tokens for authentication and Redis for feedback caching.
Key Scripts:
npm run dev- Start lightweight Node dev server (recommended)npm run dev:vercel- Start Vercel dev server (slower on Windows)npm run deploy- Deploy to Vercel production
API Endpoints:
/api/auth/token- Exchanges Wagtail session for a short-lived API token/api/feedback- Proxies user feedback data from Airtable/api/link-check- Server-side link validation with SSE streaming
Dependencies:
@vercel/nodefor serverless function runtime@upstash/redisfor feedback data cachingtsxfor local TypeScript execution@sf-gov/sharedfor shared types
Common TypeScript types and interfaces used by both extension and API workspaces.
Exports:
- Wagtail API types
- Airtable API types
- Token exchange types (
TokenResponse,TokenErrorResponse) - Link check types
The project uses TypeScript with strict mode enabled across all workspaces:
strict: true- Enables all strict type checking optionsnoUnusedLocals: true- Reports errors on unused local variablesnoUnusedParameters: true- Reports errors on unused parametersnoFallthroughCasesInSwitch: true- Reports errors for fallthrough cases in switch statements
Each workspace extends the root tsconfig.json for consistent configuration.
- CRXJS: Chrome extension framework with Vite integration
- TypeScript 5.9: Type-safe JavaScript
- React 19: UI library for side panel
- Vite 7: Build tool and development server
- Tailwind CSS 4: Utility-first CSS framework
- Vercel: Serverless function platform
- @vercel/node: Serverless function runtime
- Upstash Redis: Serverless Redis database for caching and rate limiting
- TypeScript 5.9: Type definitions only
The extension uses a token exchange system for API authentication:
- Extension reads the Wagtail
sessionidcookie viachrome.cookies - Extension sends the session ID to
POST /api/auth/token - Server validates the session against Wagtail and returns an HMAC-signed token (15 min TTL)
- Extension caches the token (in-memory +
chrome.storage.session) and uses it as aBearertoken for subsequent API calls - On 401 responses, the token is cleared and re-exchanged automatically
See Token Authentication Deployment Guide for migration details.
Public SF.gov pages use Next.js, which embeds page data in a __NEXT_DATA__ script tag. The extension extracts this data directly from the DOM via a content script (next-data-extractor.ts), transforming it into the WagtailPage format. This eliminates network round-trips to the Wagtail API for public pages.
Admin/preview pages continue using direct Wagtail API calls.
All requests to api.sf.gov include custom headers for logging and tracking:
User-Agent: SF-Gov-Companion-Extension/1.0
X-SF-Gov-Extension: companion
These headers are sent in:
- Extension → api.sf.gov: Direct Wagtail API calls (admin pages only)
- Server → api.sf.gov: Session validation during token exchange
- Extension → Server: Feedback and link-check requests (via Bearer token)
Server administrators can filter logs using these headers to identify extension traffic.
See .kiro/specs/monorepo-conversion/requirements.md for detailed monorepo requirements.
