browser-debugger-cli
Version:
DevTools telemetry in your terminal. For humans and agents. Direct WebSocket to Chrome's debugging port.
254 lines • 7.26 kB
JavaScript
/**
* CDP Schema Introspection
*
* Provides structured, agent-friendly schema information for all CDP domains and methods.
* Follows principles from docs/AGENT_FRIENDLY_TOOLS.md:
* - Machine-readable output (JSON schema)
* - Self-describing tools
* - Structured context without verbosity
*/
import { loadProtocol, findDomain, findCommand } from './protocol.js';
/**
* Get structured schema for a specific method.
*
* @param domainName - Domain name (case-insensitive)
* @param methodName - Method name (case-insensitive)
* @returns Method schema or undefined if not found
*
* @example
* ```typescript
* const schema = getMethodSchema('Network', 'getCookies');
* console.log(schema.parameters); // [{ name: 'urls', type: 'array', required: false, ... }]
* ```
*/
export function getMethodSchema(domainName, methodName) {
const domain = findDomain(domainName);
if (!domain) {
return undefined;
}
const command = findCommand(domain.domain, methodName);
if (!command) {
return undefined;
}
return buildMethodSchema(domain.domain, command);
}
/**
* Build method schema from protocol command.
*
* @param domainName - Domain name
* @param command - Command from protocol
* @returns Structured method schema
*/
function buildMethodSchema(domainName, command) {
const parameters = command.parameters?.map(paramToSchema) ?? [];
const returns = command.returns?.map(returnToSchema) ?? [];
const example = {
command: `bdg cdp ${domainName}.${command.name}`,
};
if (parameters.length > 0) {
const exampleParams = {};
parameters.forEach((p) => {
if (!p.required)
return; // Skip optional params in example
exampleParams[p.name] = getExampleValue(p);
});
if (Object.keys(exampleParams).length > 0) {
example.params = exampleParams;
example.command += ` --params '${JSON.stringify(exampleParams)}'`;
}
}
const schema = {
name: `${domainName}.${command.name}`,
domain: domainName,
method: command.name,
parameters,
returns,
example,
};
if (command.description)
schema.description = command.description;
if (command.experimental)
schema.experimental = command.experimental;
if (command.deprecated)
schema.deprecated = command.deprecated;
return schema;
}
/**
* Convert protocol parameter to schema.
*/
function paramToSchema(param) {
const schema = {
name: param.name,
type: resolveType(param),
required: !param.optional,
};
if (param.description)
schema.description = param.description;
if (param.deprecated)
schema.deprecated = param.deprecated;
if (param.enum)
schema.enum = param.enum;
if (param.items)
schema.items = resolveType(param.items);
return schema;
}
/**
* Convert protocol return value to schema.
*/
function returnToSchema(ret) {
const schema = {
name: ret.name,
type: resolveType(ret),
optional: ret.optional ?? false,
};
if (ret.description)
schema.description = ret.description;
if (ret.items)
schema.items = resolveType(ret.items);
return schema;
}
/**
* Resolve type from parameter/return value.
*
* @param typeRef - Type reference from protocol
* @returns Type string (e.g., 'string', 'integer', 'Network.Cookie')
*/
function resolveType(typeRef) {
if (typeRef.$ref) {
return typeRef.$ref;
}
return typeRef.type ?? 'any';
}
/**
* Get example value for a parameter type.
*
* @param param - Parameter schema
* @returns Example value
*/
function getExampleValue(param) {
if (param.enum && param.enum.length > 0) {
return param.enum[0];
}
switch (param.type) {
case 'string':
return 'example';
case 'integer':
case 'number':
return 0;
case 'boolean':
return true;
case 'array':
return [];
case 'object':
return {};
default:
return null;
}
}
/**
* Get all methods in a domain.
*
* @param domainName - Domain name (case-insensitive)
* @returns Array of method schemas
*
* @example
* ```typescript
* const methods = getDomainMethods('Network');
* console.log(methods.length); // 39
* console.log(methods[0].name); // 'Network.getIPProtectionProxyStatus'
* ```
*/
export function getDomainMethods(domainName) {
const domain = findDomain(domainName);
if (!domain?.commands) {
return [];
}
return domain.commands.map((cmd) => buildMethodSchema(domain.domain, cmd));
}
/**
* Get summary information for a domain.
*
* @param domainName - Domain name (case-insensitive)
* @returns Domain summary or undefined if not found
*
* @example
* ```typescript
* const summary = getDomainSummary('Network');
* console.log(summary.commandCount); // 39
* console.log(summary.eventCount); // 12
* ```
*/
export function getDomainSummary(domainName) {
const domain = findDomain(domainName);
if (!domain) {
return undefined;
}
return buildDomainSummary(domain);
}
/**
* Build domain summary from protocol domain.
*/
function buildDomainSummary(domain) {
const summary = {
name: domain.domain,
commandCount: domain.commands?.length ?? 0,
eventCount: domain.events?.length ?? 0,
};
if (domain.description)
summary.description = domain.description;
if (domain.experimental)
summary.experimental = domain.experimental;
if (domain.deprecated)
summary.deprecated = domain.deprecated;
if (domain.dependencies)
summary.dependencies = domain.dependencies;
return summary;
}
/**
* Get summaries for all domains.
*
* @returns Array of domain summaries
*
* @example
* ```typescript
* const summaries = getAllDomainSummaries();
* console.log(summaries.length); // 53
* console.log(summaries.find(d => d.name === 'Network').commandCount); // 39
* ```
*/
export function getAllDomainSummaries() {
const protocol = loadProtocol();
return protocol.domains.map(buildDomainSummary);
}
/**
* Search methods by keyword (case-insensitive).
*
* Searches in method names and descriptions.
*
* @param query - Search query
* @returns Array of matching method schemas
*
* @example
* ```typescript
* const cookies = searchMethods('cookie');
* // Returns: Network.getCookies, Network.setCookie, Network.deleteCookies, etc.
* ```
*/
export function searchMethods(query) {
const protocol = loadProtocol();
const results = [];
const lowerQuery = query.toLowerCase();
protocol.domains.forEach((domain) => {
if (!domain.commands)
return;
domain.commands.forEach((command) => {
const nameMatch = command.name.toLowerCase().includes(lowerQuery);
const descMatch = command.description?.toLowerCase().includes(lowerQuery) ?? false;
if (nameMatch || descMatch) {
results.push(buildMethodSchema(domain.domain, command));
}
});
});
return results;
}
//# sourceMappingURL=schema.js.map