@axeptio/cmp-agents
v2.0.0
Published
Reverse-engineered interfaces able to interact with Consent Management Platforms online
Readme
CMP Agents
A collection of reverse-engineered JavaScript interfaces to interact with Consent Management Platforms (CMPs) online.
This library is intended to run in the browser. It detects and drives CMP UIs in the page context. When using NPM you must either (1) bundle your code and inject that script into the browser, or (2) inject the pre-built bundle (dist/cmp-agents.js) into the page and then run code that uses window.CMPAgents. It does not run in Node.js alone.
Overview
CMP Agents provides a unified API to detect, read, and interact with 40+ different Consent Management Platforms. Whether you're building a cookie scanner, consent automation tool, or privacy assistant, this library gives you the building blocks to work with CMPs programmatically.
Supported CMPs
| CMP | Detection | Accept/Reject | Granular Consent | |-----|-----------|---------------|------------------| | Axeptio | ✅ | ✅ | ✅ | | CookieBot | ✅ | ✅ | ✅ | | Didomi | ✅ | ✅ | ✅ | | OneTrust | ✅ | ✅ | ✅ | | UserCentrics | ✅ | ✅ | ✅ | | TrustArc | ✅ | ✅ | ✅ | | iubenda | ✅ | ✅ | ✅ | | CookieYes | ✅ | ✅ | ✅ | | Complianz | ✅ | ✅ | ✅ | | TarteAuCitron | ✅ | ✅ | ✅ | | ...and 30+ more | ✅ | ✅ | Varies |
See docs/integrations.md for the complete list.
Installation
NPM
npm install @axeptio/cmp-agentsUse the library in code that will run in the browser: bundle that code (e.g. with Webpack, Rollup) and inject the resulting script into the page, or inject the pre-built bundle (node_modules/@axeptio/cmp-agents/dist/cmp-agents.js) and call CMPAgents.create() from your own injected script.
CDN (Script tag)
<!-- Latest version -->
<script src="https://unpkg.com/@axeptio/cmp-agents/dist/cmp-agents.min.js"></script>
<!-- Or from jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@axeptio/cmp-agents/dist/cmp-agents.min.js"></script>
<!-- Specific version -->
<script src="https://unpkg.com/@axeptio/[email protected]/dist/cmp-agents.min.js"></script>Requirements
- Browser: The library runs in the browser (script tag or injected bundle).
- Node.js >= 22.17.0 < 23.0.0 and npm >= 10 for development, CLI, and building your own bundle.
Quick Start
Browser Usage (CDN)
<script src="https://unpkg.com/@axeptio/cmp-agents/dist/cmp-agents.min.js"></script>
<script>
const agent = CMPAgents.create();
agent.on('cmp', async (cmp) => {
console.log('Detected:', cmp.getName());
const vendors = await cmp.getVendors();
console.log('Vendors:', vendors);
});
agent.run();
</script>NPM / Bundled script (browser)
Bundle this code and inject the bundle into the page so it runs in the browser:
import { CMPDetector, CookieBot, OneTrust, Didomi, CONSENT_INTENT } from '@axeptio/cmp-agents';
// Create a detector and register CMPs (runs in browser)
const cmpDetector = new CMPDetector();
cmpDetector.registerIntegrations(new CookieBot(), new OneTrust(), new Didomi());
// Listen for CMP detection
cmpDetector.on('cmp', async (cmp) => {
console.log(`Detected: ${cmp.getName()}`);
// Get vendors
const vendors = await cmp.getVendors();
console.log('Vendors:', vendors);
// Accept all using the new API
await cmp.applyPreferences({ intent: CONSENT_INTENT.ACCEPT_ALL });
});
// Start detection
cmpDetector.run();Using Individual Integrations
import CookieBot from '@axeptio/cmp-agents/integrations/cookiebot';
import { CONSENT_INTENT } from '@axeptio/cmp-agents';
const cookiebot = new CookieBot();
// Check if CookieBot is present
if (window.Cookiebot) {
cookiebot.trigger('detection');
// Get current consent status
await cookiebot.retrieveConsent();
console.log('Consent:', cookiebot.consent);
// Accept all vendors
await cookiebot.applyPreferences({ intent: CONSENT_INTENT.ACCEPT_ALL });
}Granular Vendor Consent
import { CONSENT_INTENT } from '@axeptio/cmp-agents';
const cmp = cmpDetector.getCMP();
const vendors = await cmp.getVendors();
// Build a map of vendor preferences
const vendorConsents = {};
// Allow Analytics vendor, deny Advertising
const analyticsVendor = vendors.find(v => v.name.includes('Analytics'));
const adsVendor = vendors.find(v => v.name.includes('Advertising'));
if (analyticsVendor) vendorConsents[analyticsVendor.name] = true;
if (adsVendor) vendorConsents[adsVendor.name] = false;
// Apply custom choices by passing vendors directly
await cmp.applyPreferences({
intent: CONSENT_INTENT.APPLY_CUSTOM,
vendors: vendorConsents
});Architecture
┌─────────────────────────────────────────────────────────────┐
│ CMPDetector │
│ - Registers CMP integrations │
│ - Manages detection lifecycle │
│ - Selects highest-priority detected CMP │
└─────────────────────┬───────────────────────────────────────┘
│
┌───────────┴───────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ CMP (Base) │ │ TCF_CMP │
│ │ │ (extends CMP) │
│ - getName() │ │ │
│ - getVendors() │ │ - TCF v2 API │
│ - applyPrefs() │ │ - getVendorList │
│ - acceptAll() │ │ - getTCData() │
│ - rejectAll() │ │ │
└────────┬────────┘ └────────┬────────┘
│ │
└───────────┬───────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│CookieBot│ │ OneTrust│ │ Didomi │
└─────────┘ └─────────┘ └─────────┘API Reference
CMP Base Class
All CMP integrations extend the CMP base class:
class CMP {
// Detection
getName(): string
getMatchingResources(): RegExp[]
onTick(): void
// Configuration
getLayout(): string // 'banner' | 'popup' | 'widget' | etc.
getBehaviors(): string[] // ['ACCEPTABLE', 'REJECTABLE', etc.]
getGranularity(): string // 'NONE' | 'GLOBAL' | 'VENDOR' | 'PURPOSE'
// Data retrieval
getVendors(): Promise<Vendor[]>
getPurposes(): Promise<Purpose[]>
retrieveConsent(): void
hasExistingConsent(): boolean
// Binary actions
acceptAll(): Promise<void>
rejectAll(): Promise<void>
// Lifecycle methods
applyPreferences(options): Promise<void>
show(screenType): Promise<void>
dismiss(): Promise<void>
}
// applyPreferences options:
// {
// intent: CONSENT_INTENT, // ACCEPT_ALL | REJECT_ALL | APPLY_CUSTOM
// vendors: { [id]: boolean }, // Vendor consent map (for APPLY_CUSTOM)
// purposes: { [id]: boolean }, // Purpose consent map (for APPLY_CUSTOM)
// autoDismiss: boolean // Auto-close CMP UI (default: true)
// }Constants
import {
BEHAVIORS,
LAYOUTS,
GRANULARITY,
CONSENT_INTENT,
SCREEN_TYPE
} from '@axeptio/cmp-agents';
// Behaviors
BEHAVIORS.ACCEPTABLE // User can accept
BEHAVIORS.REJECTABLE // User can reject
BEHAVIORS.CLOSABLE // CMP can be closed
BEHAVIORS.OBSTRUCTIVE // CMP blocks content
// Layouts
LAYOUTS.BANNER
LAYOUTS.POPUP
LAYOUTS.WIDGET
LAYOUTS.PAGE
// Granularity
GRANULARITY.NONE // No choice possible
GRANULARITY.GLOBAL // Accept/reject all only
GRANULARITY.VENDOR // Per-vendor choice
GRANULARITY.PURPOSE // Per-purpose choice
// Consent Intent (for applyPreferences)
CONSENT_INTENT.ACCEPT_ALL // Accept all vendors
CONSENT_INTENT.REJECT_ALL // Reject all vendors
CONSENT_INTENT.APPLY_CUSTOM // Apply vendor-specific choices
// Screen Type (for show)
SCREEN_TYPE.DEFAULT // Initial consent banner
SCREEN_TYPE.VENDORS // Vendor list view
SCREEN_TYPE.PURPOSES // Purpose/category view
SCREEN_TYPE.SETTINGS // Settings panelAdding a New Integration
Use the CLI to scaffold a new integration:
npm run cli:createOr create manually in src/integrations/yourCmp/:
import { CMP } from '../../cmp.js';
import { GRANULARITY, SCREEN_TYPE } from '../../constants.js';
export default class YourCMP extends CMP {
getName() {
return 'YourCMP';
}
getGranularity() {
return GRANULARITY.VENDOR;
}
getMatchingResources() {
return [/yourcmp\.com\/sdk/];
}
onTick() {
if (window.YourCMPSDK) {
this.trigger('detection');
}
}
async getVendors() {
return window.YourCMPSDK.getVendors().map(v => ({
name: v.name,
iabId: v.id
}));
}
async acceptAll() {
window.YourCMPSDK.acceptAll();
}
async rejectAll() {
window.YourCMPSDK.rejectAll();
}
retrieveConsent() {
this.consent = window.YourCMPSDK.hasConsent();
this.consentDate = window.YourCMPSDK.getConsentDate();
}
// Override for granular consent support
async _applyCustomConsent(options = {}) {
const { vendors = {}, purposes = {} } = options;
Object.entries(vendors).forEach(([id, enabled]) => {
window.YourCMPSDK.setVendorConsent(id, enabled);
});
Object.entries(purposes).forEach(([id, enabled]) => {
window.YourCMPSDK.setPurposeConsent(id, enabled);
});
window.YourCMPSDK.save();
}
// Override for UI control
async show(screenType = SCREEN_TYPE.DEFAULT) {
window.YourCMPSDK.open();
}
async dismiss() {
window.YourCMPSDK.close();
}
}See CONTRIBUTING.md for detailed guidelines.
Testing
# Run unit tests (fast, no external dependencies)
npm run test:unit
# Run unit tests in watch mode
npm run test:unit:watch
# Run unit tests with coverage
npm run test:unit:coverage
# Run E2E tests (requires build first)
npm test
# Run E2E tests for a specific CMP
npx playwright test --config=test/config.js -g "CookieBot"
# Run in complete mode (all test websites + extended flow tests)
TEST_MODE=complete npm testTest Structure
test/
├── unit/ # Unit tests (Vitest + jsdom)
│ ├── cmp.unit.test.js # CMP base class tests
│ ├── tcf-cmp.unit.test.js # TCF_CMP class tests
│ ├── vendor.unit.test.js # Vendor class tests
│ ├── cmpDetector.unit.test.js # CMPDetector + create() factory tests
│ ├── consent-signals.unit.test.js # Consent signal detection tests
│ └── consentManager.unit.test.js # ConsentManager integration tests
├── common.js # Shared E2E test helpers
└── config.js # Playwright configurationSee the Testing Guide for detailed instructions.
Documentation
Guides
- Development Guide - Project setup, build, debug, and manual testing
- Testing Guide - Unit tests (Vitest) and E2E tests (Playwright)
- Contributing - How to contribute
- v2.0 Migration Guide - Upgrading from v1.x
Reference
- CMP Class Reference - Base class API and usage examples
- Constants Reference - Layouts, behaviors, granularity, consent intent, screen types
- Consent Signals - Google GCM v2, Shopify, Microsoft UET, Meta LDU, Pinterest
- Integration List - All supported CMPs and their capabilities
- CLI Usage - Command-line tools
- Glossary - CMP terminology and concepts
Internal
- Shake (Cookie Scanner) - How Shake uses CMP Agents
- Taste (Browser Extension) - How Taste uses CMP Agents
- CI/CD - Continuous integration and release process
Use Cases
1. Cookie Scanner (Shake)
Detect and analyze CMPs on websites during automated scans:
// In Playwright test
await page.addScriptTag({ path: 'dist/cmp-agents.js' });
await page.evaluate(() => {
const agent = CMPAgents.create();
window.cmpAgent = agent;
agent.run();
});
const cmp = await page.evaluate(() => window.cmpAgent.getCMP());
const vendors = await page.evaluate(() => window.cmpAgent.getCMP().getVendors());2. Privacy Assistant (Taste)
Auto-consent on behalf of users in a browser extension:
import { CONSENT_INTENT, BEHAVIORS } from '@axeptio/cmp-agents';
// Content script
const cmp = cmpDetector.getCMP();
if (cmp && cmp.getBehaviors().includes(BEHAVIORS.REJECTABLE)) {
// Reject all vendors
await cmp.applyPreferences({ intent: CONSENT_INTENT.REJECT_ALL });
}3. Granular Consent Management
Apply specific vendor preferences:
import { CONSENT_INTENT } from '@axeptio/cmp-agents';
const cmp = cmpDetector.getCMP();
const vendors = await cmp.getVendors();
// Build vendor consent map
const vendorConsents = {};
// Find and configure specific vendors
const metaVendor = vendors.find(v => v.name.includes('Meta'));
const analyticsVendor = vendors.find(v => v.name.includes('Analytics'));
if (metaVendor) vendorConsents[metaVendor.name] = false; // Deny Meta
if (analyticsVendor) vendorConsents[analyticsVendor.name] = true; // Allow Analytics
// Apply custom choices by passing vendors directly
await cmp.applyPreferences({
intent: CONSENT_INTENT.APPLY_CUSTOM,
vendors: vendorConsents
});4. UI Control for Testing
Open specific CMP views for manual testing or screenshots:
import { SCREEN_TYPE, CONSENT_INTENT } from '@axeptio/cmp-agents';
const cmp = cmpDetector.getCMP();
// Open vendor list for inspection
await cmp.show(SCREEN_TYPE.VENDORS);
// Apply preferences but keep UI open
await cmp.applyPreferences({
intent: CONSENT_INTENT.APPLY_CUSTOM,
autoDismiss: false
});
// Take screenshot, then close
await cmp.dismiss();5. Research & Compliance
Analyze CMP configurations across websites:
const analysis = {
name: cmp.getName(),
layout: cmp.getLayout(),
behaviors: cmp.getBehaviors(),
granularity: cmp.getGranularity(),
vendorCount: (await cmp.getVendors()).length,
hasExistingConsent: cmp.hasExistingConsent()
};License
This project is licensed under a custom license. See LICENSE for details.
TL;DR: Free for CMP industry peers to use, modify, and contribute. Redistribution as a competing product requires permission.
Credits
Developed and maintained by Axeptio.

