Skip to content

Dependency Injection

The OpenClaw CLI uses a straightforward dependency injection pattern. Through the CliDeps interface, channel send functions and other infrastructure capabilities are injected into commands.

CliDeps Interface

Source: src/cli/deps.ts

typescript
// src/cli/deps.ts (simplified)
interface CliDeps {
  sendMessageWhatsApp: SendFunction;
  sendMessageTelegram: SendFunction;
  sendMessageDiscord: SendFunction;
  sendMessageSlack: SendFunction;
  sendMessageSignal: SendFunction;
  sendMessageIMessage: SendFunction;
}

CliDeps is a simple interface containing send functions for all channels.

Creating Dependencies

typescript
// src/cli/deps.ts (simplified)
function createDefaultDeps(): CliDeps {
  return {
    sendMessageWhatsApp: createWhatsAppSender(),
    sendMessageTelegram: createTelegramSender(),
    sendMessageDiscord: createDiscordSender(),
    sendMessageSlack: createSlackSender(),
    sendMessageSignal: createSignalSender(),
    sendMessageIMessage: createIMessageSender(),
  };
}

The createDefaultDeps() factory function creates concrete implementations for all channel senders.

Bridging to the Infrastructure Layer

Source: src/cli/deps.ts

typescript
// Bridging CLI deps to infra layer
function createOutboundSendDeps(deps: CliDeps): OutboundSendDeps {
  return {
    sendWhatsApp: deps.sendMessageWhatsApp,
    sendTelegram: deps.sendMessageTelegram,
    sendDiscord: deps.sendMessageDiscord,
    sendSlack: deps.sendMessageSlack,
    sendSignal: deps.sendMessageSignal,
    sendIMessage: deps.sendMessageIMessage,
  };
}

The bridging function converts the CLI layer's CliDeps into the infrastructure layer's OutboundSendDeps. Field names may differ, but the functionality is identical.

Why Dependency Injection?

  1. Testability -- Tests can inject mock send functions
  2. Decoupling -- CLI commands do not directly depend on specific channel SDKs
  3. Flexibility -- Implementations can be replaced without modifying command code
  4. Consistency -- All commands access channel capabilities through a unified interface

Test Example

typescript
// In tests, inject mock dependencies
const mockDeps: CliDeps = {
  sendMessageTelegram: vi.fn().mockResolvedValue({ messageId: 1 }),
  sendMessageDiscord: vi.fn().mockResolvedValue({ messageId: 2 }),
  // ...
};

// Command uses injected deps
await sendCommand(mockDeps, { to: "123", message: "test" });

// Verify mock was called
expect(mockDeps.sendMessageTelegram).toHaveBeenCalledWith(/* ... */);

Summary

  • CliDeps interface contains send functions for all channels
  • createDefaultDeps() creates concrete implementations
  • createOutboundSendDeps() bridges to the infrastructure layer
  • Dependency injection provides testability, decoupling, and flexibility

Next: Media Processing

OpenClaw Source Code Tutorial