@rankcli/agent-runtime
Core audit engine for RankCLI. Runs 280+ SEO checks and powers both the CLI and SaaS edge functions.
Architecture
This package is isomorphic - it works in both Node.js and Deno environments:
- Node.js: Used by the CLI (
packages/cli)
- Deno: Used by Supabase Edge Functions (
packages/saas/supabase/functions)
The isomorphic design uses native fetch API instead of Node-specific modules like axios or https.
Directory Structure
src/
├── audit/
│ ├── engine.ts # Main audit orchestrator
│ ├── types.ts # Issue definitions & types
│ ├── deno-entry.ts # Deno-specific entry point
│ └── checks/ # Individual check modules
│ ├── crawlability.ts
│ ├── on-page.ts
│ ├── performance.ts
│ ├── security.ts
│ ├── ai-readiness.ts
│ └── ... (40+ check modules)
├── utils/
│ └── http.ts # Isomorphic HTTP utilities
├── content/ # Content generation
├── geo/ # GEO tracking
├── git/ # Git/PR helpers
└── index.ts # Main entry point
Development
Prerequisites
Commands
pnpm install
pnpm dev
pnpm test
pnpm test:run
pnpm build
pnpm build:deno
Development Workflow
When you run pnpm dev, it:
- Builds the Deno bundle first
- Starts tsup in watch mode for Node.js
- Rebuilds the Deno bundle on every successful Node.js build
This ensures both the CLI (Node.js) and edge functions (Deno) stay in sync during development.
Available Scripts
pnpm dev | Watch mode - rebuilds Node + Deno on changes |
pnpm dev:node | Watch mode - Node.js only |
pnpm dev:deno | Watch mode - Deno bundle only |
pnpm build | Production build (Node.js + Deno) |
pnpm build:deno | Build Deno bundle only |
pnpm test | Run tests in watch mode |
pnpm test:run | Run tests once |
Deno Bundle
The Deno bundle is generated at:
packages/saas/supabase/functions/_shared/audit/
├── engine.bundle.js # Bundled audit engine
└── index.ts # Wrapper with cheerio import
Edge functions import from the shared bundle:
import { runFullAudit } from '../_shared/audit/index.ts';
How It Works
scripts/build-deno.ts uses esbuild to bundle src/audit/deno-entry.ts
- The bundle excludes
cheerio (imported from esm.sh at runtime)
- Node-specific APIs are polyfilled or replaced with web-compatible alternatives
Isomorphic Considerations
When adding new features to the audit engine:
- Use
fetch instead of axios or Node's http/https
- Use the helpers in
src/utils/http.ts for HTTP requests
- Avoid Node-specific modules (
fs, path, dns, crypto, etc.)
- For DNS lookups, use DNS-over-HTTPS (see
additional-checks.ts)
- Test in both Node.js (
pnpm test) and edge functions
VS Code Tasks
If using VS Code, these tasks are available (Cmd/Ctrl+Shift+P → "Tasks: Run Task"):
| Dev: Agent Runtime (with Deno watch) | Watches and rebuilds both bundles |
| Dev: SaaS Frontend | Runs the Vite dev server |
| Dev: Full Stack | Runs both in parallel |
| Build: Deno Bundle | One-time Deno bundle build |
| Deploy: Edge Functions | Deploys to Supabase |
DevContainer
When using the devcontainer:
- The Deno bundle is built automatically on container creation
- Run
pnpm dev in packages/agent-runtime to start watching for changes
- The bundle is rebuilt automatically when you modify audit code
API
runFullAudit
Main entry point for running audits:
import { runFullAudit } from '@rankcli/agent-runtime';
const report = await runFullAudit('https://example.com', {
maxPages: 10,
includeAdvanced: true,
includeAI: false,
});
console.log(report.overallScore);
console.log(report.issues);
console.log(report.healthScores);
console.log(report.checksRun);
Issue Structure
interface AuditIssue {
code: string;
severity: 'error' | 'warning' | 'notice';
category: string;
title: string;
description?: string;
impact?: string;
howToFix?: string;
affectedUrls?: string[];
details?: Record<string, unknown>;
}
Health Scores
interface HealthScores {
crawlability: number;
onPage: number;
content: number;
performance: number;
security: number;
socialMeta: number;
aiReadiness: number;
mobile: number;
}
Adding New Checks
- Create a new file in
src/audit/checks/ or add to an existing one
- Export the check function
- Add to
src/audit/engine.ts to include in the audit flow
- Add to
src/audit/deno-entry.ts to export for Deno
- Run
pnpm build:deno to regenerate the bundle
- Add tests in a
.test.ts file
Example check:
import { httpGet } from '../../utils/http.js';
import type { AuditIssue } from '../types.js';
export async function checkMyThing(url: string): Promise<AuditIssue[]> {
const issues: AuditIssue[] = [];
const response = await httpGet(url);
if () {
issues.push({
code: 'MY_ISSUE_CODE',
severity: 'warning',
category: 'my-category',
title: 'Issue title',
description: 'What this means',
howToFix: 'How to fix it',
affectedUrls: [url],
});
}
return issues;
}
Testing
Tests use Vitest:
pnpm test:run
pnpm test:run src/audit/checks/social-meta.test.ts
pnpm test:coverage
License
MIT