AgentSkillsCN

base-client-guardian

针对 core/base-client.js 的编辑设置防护措施,涵盖身份验证注入、重试机制、熔断器行为以及请求/响应事件。适用于修改通用 API 客户端,或调整其身份验证、重试与错误处理逻辑时使用。

SKILL.md
--- frontmatter
name: base-client-guardian
description: "Guardrails for edits to core/base-client.js covering auth injection, retries, circuit breaker behavior, and request/response events. Use when modifying the universal API client or its auth/retry/error handling."

Skill: Base Client Guardian

Purpose & Scope

This skill applies when modifying the Universal API Client (core/base-client.js).

The Base Client provides:

  • HTTP request handling with axios
  • Multi-protocol authentication (Bearer, API Key, Basic, HMAC, OAuth2)
  • Exponential backoff retry logic
  • Circuit breaker pattern for fault tolerance
  • Rate limiting management
  • EventEmitter-based request/response/error logging

Critical Rules - NEVER Do

Public Method Stability

  • NEVER remove or rename these public methods:
    • request() - Main entry point for API calls
    • retryRequest() - Handles retry logic with backoff
    • healthCheck() - Service health verification
    • generateMethods() - Dynamic method generation from endpoints
    • addAuthentication() - Auth injection into requests

EventEmitter Events

  • NEVER remove or rename these event names:
    • request - Emitted before each request
    • response - Emitted on successful response
    • error - Emitted on request failure
    • circuit-breaker-open - Emitted when circuit opens

Authentication Types

  • NEVER change these auth type identifiers:
    • bearer - Bearer token auth
    • apikey - API key (header or query)
    • basic - Basic auth
    • hmac - HMAC signature auth
    • oauth2 - OAuth 2.0 auth

Security

  • NEVER log sensitive data (auth tokens, credentials, API keys)
  • NEVER disable TLS/SSL verification
  • NEVER expose raw credentials in error messages
  • NEVER store credentials in plain text

Circuit Breaker

  • NEVER change the state machine flow: CLOSED -> OPEN -> HALF_OPEN -> CLOSED
  • NEVER remove the 5-failure threshold for circuit opening
  • NEVER remove the 60-second recovery timeout

Required Patterns - MUST Follow

Retry Logic

javascript
// MUST use exponential backoff
const delay = this.config.retryDelay * Math.pow(2, attempt - 1);

// MUST check shouldRetry() before retrying
if (attempt < this.config.retryAttempts && this.shouldRetry(error)) {
    await this.sleep(delay);
    return this.retryRequest(config, attempt + 1);
}

Event Emission

javascript
// MUST emit events after state changes
this.emit('request', { service, method, url, timestamp });
this.emit('response', { service, status, statusText, timestamp });
this.emit('error', { service, type, message, status, timestamp });

Circuit Breaker State Management

javascript
// MUST follow state machine
// On success: reset to CLOSED
this.circuitBreaker.failures = 0;
this.circuitBreaker.state = 'CLOSED';

// On failure: increment and potentially open
this.circuitBreaker.failures++;
if (this.circuitBreaker.failures >= 5) {
    this.circuitBreaker.state = 'OPEN';
    this.emit('circuit-breaker-open', { service, failures });
}

// On timeout: transition to HALF_OPEN
if (timeSinceLastFailure > 60000) {
    this.circuitBreaker.state = 'HALF_OPEN';
}

Authentication Handling

javascript
// MUST handle all auth types in switch statement
switch (auth.type) {
    case 'bearer':
    case 'apikey':
    case 'basic':
    case 'hmac':
    case 'oauth2':
        // Implementation here
        break;
}

Safe Modification Examples

Adding a New Authentication Type

javascript
// Add to the switch statement in addAuthentication()
case 'custom_auth_type':
    config.headers['X-Custom-Auth'] = auth.config.customValue;
    break;

Adding New Retry Conditions

javascript
// Extend shouldRetry() - do NOT replace existing conditions
shouldRetry(error) {
    return (
        error.code === 'ECONNRESET' ||
        error.code === 'ETIMEDOUT' ||
        error.code === 'ENOTFOUND' ||
        error.code === 'YOUR_NEW_CODE' ||  // ADD here
        (error.response && error.response.status >= 500)
    );
}

Adding New Events

javascript
// Add new events - do NOT modify existing event signatures
this.emit('your-new-event', { service, customData, timestamp });

Integration Points

ComponentIntegration Method
Metrics CollectorListen to request, response, error events
Compliance ManagerIntercept in addAuthentication()
Version ManagerPass version in request options
Vendor AbstractionUses request() method

Testing Requirements

Before any changes to this file:

bash
# 1. Run existing tests
npm test -- --grep "BaseClient"

# 2. Verify authentication works for all types
node -e "
const BaseClient = require('./core/base-client');
const client = new BaseClient({ name: 'test', baseUrl: 'https://httpbin.org' });
console.log('Client initialized:', client.config.name);
"

# 3. Test circuit breaker states
# Verify: CLOSED -> OPEN (after 5 failures) -> HALF_OPEN (after 60s)

# 4. Test retry logic
# Verify exponential backoff: delay * 2^(attempt-1)

Rollback Procedure

If changes cause issues:

  1. Immediate Rollback

    bash
    git checkout HEAD~1 -- core/base-client.js
    
  2. Verify Recovery

    bash
    # Restart the server
    bun run dev
    
    # Check health endpoint
    curl http://localhost:3001/health
    
  3. Event Listener Check

    javascript
    // Verify all components can still receive events
    client.on('request', () => console.log('Request event works'));
    client.on('response', () => console.log('Response event works'));
    client.on('error', () => console.log('Error event works'));