This file provides guidelines for AI agents operating in this repository.
OpenCode authentication plugin for the OmniRoute API. Features /connect command setup, API key auth, dynamic model fetching (/v1/models), and TTL caching.
npm install # Install dependencies
npm run build # Build the project (TypeScript compilation)
npm run dev # Watch mode for development
npm run clean # Clean build output
npm run prepublishOnly # Build before publishingnpx tsc --noEmit src/plugin.ts # Type-check a single file
npx tsc --build --verbose # Build with verbose outputNote: No test suite currently exists. When implemented, use:
npm test # Run all tests
npx jest src/plugin.test.ts # Run a single test file
npm run test:watch # Run tests in watch mode- Target: ES2022, Module: NodeNext (ESM).
- Strict Mode: Enabled. Never disable strict checks.
- Formatting: 2 spaces, max 100 chars/line, semicolons required, single quotes for strings, trailing commas in multi-line objects/arrays.
- Constants:
UPPER_SNAKE_CASE(e.g.,OMNIROUTE_PROVIDER_ID) - Variables/Functions:
camelCase(e.g.,modelCache,fetchModels()) - Classes/Interfaces/Types:
PascalCase(e.g.,OmniRouteConfig) - Files:
kebab-case(e.g.,opencode-plugin.d.ts)
- CRITICAL: Always use explicit
.jsextensions for relative imports (e.g.,import { x } from './file.js'). - Group imports: external → internal → types.
- Use named exports only (no default exports).
- Never use
any. Useunknownif uncertain, then narrow. - Always type function parameters and return types.
- Prefer runtime validation over unsafe type assertions.
// ✅ Correct
const rawData = await response.json();
if (!rawData || typeof rawData !== 'object' || !Array.isArray(rawData.data)) {
throw new Error('Invalid response structure');
}
const data = rawData as OmniRouteModelsResponse;- Always use
try/catch/finallyfor resource cleanup (e.g.,clearTimeout). - Provide meaningful error messages.
- Security: Sanitize error logs. Never log full API responses or sensitive keys (e.g., log "Cache cleared for provided config" instead of logging the API key).
// ✅ Correct
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
try {
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
return await response.json();
} finally {
clearTimeout(timeoutId);
}- Headers: Use the
Headersconstructor for proper normalization.const headers = new Headers(init?.headers); headers.set('Authorization', `Bearer ${apiKey}`); headers.set('Content-Type', 'application/json');
- URLs: Handle both
Requestobjects and string URLs safely.const url = input instanceof Request ? input.url : input.toString();
- Security: When intercepting requests, ensure
baseUrlends with a slash for safe prefix matching to prevent domain spoofing. Validate endpoint URLs strictly (requirehttp:orhttps:).
src/plugin.ts: Main plugin implementation &/connectcommand.src/models.ts: Model fetching, caching, and validation.src/constants.ts: Configuration constants (OMNIROUTE_ENDPOINTS, etc.).src/types.ts: TypeScript definitions.index.ts: Main exports.
- Adding Exports: Add in source file, re-export in
index.ts(with.js), runnpm run build. - Debugging: Look for
[OmniRoute]prefix in console logs.