Configuration Options for moeru‑ai/airi: A Complete Guide to the Defu‑Based Config System

The moeru‑ai/airi repository uses a hierarchical, defu‑based configuration system that merges default TypeScript objects, environment variables, JSON files, and runtime updates through a singleton ConfigManager.

Understanding the configuration options for moeru‑ai/airi is essential for customizing the Twitter automation and AI agent behaviors. The project implements a unified configuration layer in services/twitter-services/src/config/ that supports deep merging via defu, allowing granular overrides without breaking the default schema.

How the Configuration System Works

The configuration hierarchy follows a strict precedence order: hard‑coded defaults → environment variables → JSON file → runtime updates. All values flow through the ConfigManager singleton exposed by useConfigManager().

In services/twitter-services/src/config/index.ts, the manager initializes by calling getDefaultConfig() from services/twitter-services/src/config/types.ts, then conditionally loads a JSON file specified by the CONFIG_PATH environment variable (defaulting to ./twitter-config.json). The merge uses defu from @moeru/std, ensuring that partial objects only override specific keys rather than replacing entire sections.

Configuration Options Reference

The top‑level Config interface defined in services/twitter-services/src/config/types.ts organizes options into logical groups.

Browser Configuration (BrowserConfig)

Controls Puppeteer/Playwright behavior via Stagehand and BrowserBase:

browser: {
  apiKey: string,              // BROWSERBASE_API_KEY
  endpoint?: string,           // Optional Stagehand endpoint
  headless: boolean,           // BROWSER_HEADLESS
  userAgent: string,           // BROWSER_USER_AGENT
  viewport: { width: number, height: number },  // BROWSER_VIEWPORT_*
  timeout: number,             // BROWSER_TIMEOUT
  requestTimeout: number,      // BROWSER_REQUEST_TIMEOUT
  requestRetries: number       // BROWSER_REQUEST_RETRIES
}

Twitter API Credentials

Optional OAuth 1.0a credentials for API v1.1 or v2 endpoints:

credentials?: {
  apiKey?: string,             // TWITTER_API_KEY
  apiSecret?: string,          // TWITTER_API_SECRET
  accessToken?: string,        // TWITTER_ACCESS_TOKEN
  accessTokenSecret?: string   // TWITTER_ACCESS_TOKEN_SECRET
}

Twitter Request Options

Default parameters for timeline and search operations:

twitter: {
  defaultOptions?: {
    timeline?: { count: number, includeReplies: boolean, includeRetweets: boolean }
    search?: SearchOptions
  }
}

Adapter Settings (Airi and MCP)

Toggles for external service integration:

adapters: {
  airi?: {
    url?: string,              // AIRI_URL (default: http://localhost:3000)
    token?: string,            // AIRI_TOKEN
    enabled: boolean           // ENABLE_AIRI
  }
  mcp?: {
    port?: number,             // MCP_PORT (default: 8080)
    enabled: boolean           // ENABLE_MCP
  }
}

System-Wide Settings

Global runtime behavior:

system: {
  logLevel: 'error' | 'warn' | 'info' | 'verbose' | 'debug'
  logFormat?: 'json' | 'pretty'
  concurrency: number         // CONCURRENCY (default: 1)
}

Loading and Merging Configuration Files

The ConfigManager constructor in services/twitter-services/src/config/index.ts handles file loading:

const configPath = process.env.CONFIG_PATH || path.join(process.cwd(), 'twitter-config.json')
configInstance = new ConfigManager(fs.existsSync(configPath) ? configPath : undefined)

If the file exists, ConfigManager.loadFromFile() reads the JSON and applies defu to merge it with the defaults. Errors during parsing are caught and logged via logger.config.errorWithError.

Runtime Configuration Updates

You can modify configuration without restarting the process using updateConfig():

import { useConfigManager } from '@/services/twitter-services/src/config'

function onUserChanges(newOpts: Partial<Config>) {
  useConfigManager().updateConfig(newOpts)
}

This method uses defu to deep‑merge the new partial object onto the existing configuration, ensuring that only specified keys change while preserving the rest of the state.

Environment Variable Mapping

The getDefaultConfig() function in services/twitter-services/src/config/types.ts maps specific environment variables to configuration keys:

Environment Variable Config Path Default Value
BROWSERBASE_API_KEY browser.apiKey ''
BROWSER_HEADLESS browser.headless false
BROWSER_USER_AGENT browser.userAgent Mozilla/5.0...
BROWSER_VIEWPORT_WIDTH browser.viewport.width 1280
BROWSER_TIMEOUT browser.timeout 30000
BROWSER_REQUEST_TIMEOUT browser.requestTimeout 20000
BROWSER_REQUEST_RETRIES browser.requestRetries 2
TWITTER_API_KEY credentials.apiKey undefined
TWITTER_API_SECRET credentials.apiSecret undefined
TWITTER_ACCESS_TOKEN credentials.accessToken undefined
TWITTER_ACCESS_TOKEN_SECRET credentials.accessTokenSecret undefined
AIRI_URL adapters.airi.url http://localhost:3000
AIRI_TOKEN adapters.airi.token ''
ENABLE_AIRI adapters.airi.enabled false
MCP_PORT adapters.mcp.port 8080
ENABLE_MCP adapters.mcp.enabled true
CONCURRENCY system.concurrency 1
CONFIG_PATH N/A (loader path) ./twitter-config.json

Additionally, the server‑runtime package provides optionOrEnv() in packages/server-runtime/src/config/config.ts for CLI tools that need to fall back to environment variables when explicit options are not provided.

Accessing Configuration in Code

Always use the useConfigManager() singleton rather than importing raw JSON:

import { useConfigManager } from '@/services/twitter-services/src/config'

// Read current configuration
const cfg = useConfigManager().getConfig()

// Access nested values safely
console.log('Browser endpoint →', cfg.browser.endpoint)
console.log('Enabled adapters →', 
  Object.entries(cfg.adapters)
    .filter(([, v]) => v?.enabled)
    .map(([k]) => k)
)

This ensures you receive the fully resolved configuration that includes all merges from defaults, environment variables, and runtime updates.

Summary

  • Hierarchical merging – Configuration options for moeru‑ai/airi are resolved through a four‑layer hierarchy: hard‑coded defaults → environment variables → JSON file → runtime updates.
  • Deep merge via defu – The ConfigManager uses defu from @moeru/std to perform deep merges, ensuring partial objects only override specific keys.
  • Singleton access – Always retrieve settings through useConfigManager() in services/twitter-services/src/config/index.ts to guarantee a single source of truth.
  • Environment mapping – Dozens of environment variables (prefixed with BROWSER_, TWITTER_, AIRI_, MCP_, etc.) map directly to nested configuration keys.
  • Runtime mutability – Call updateConfig() to modify settings without restarting the process, enabling dynamic UI‑driven configuration changes.

Frequently Asked Questions

What is the default configuration file path for airi?

By default, the ConfigManager looks for twitter-config.json in the current working directory. You can override this by setting the CONFIG_PATH environment variable to point to any JSON file location.

How does airi handle configuration conflicts between environment variables and JSON files?

The system uses a precedence stack: environment variables are baked into the default configuration object first, then the JSON file (if present) is deep‑merged on top using defu. This means JSON file values override environment defaults, and runtime updates override both.

Can I update airi configuration options at runtime without restarting?

Yes. The ConfigManager exposes updateConfig(partialConfig) which performs a live deep‑merge into the existing configuration singleton. This is used by UI panels and plugins to toggle adapters or adjust browser settings on the fly.

Where is the ConfigManager singleton defined in the moeru-ai/airi repository?

The singleton is defined and exported from services/twitter-services/src/config/index.ts. It instantiates the ConfigManager class, which loads the initial state from getDefaultConfig() (located in services/twitter-services/src/config/types.ts) and optionally merges a JSON configuration file.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →