refactor(v2/providers): self-registration barrel + host container-config registry
Providers now mirror the channels pattern: each module calls registerProvider() at top level, and providers/index.ts is a barrel of side-effect imports. createProvider() becomes a thin registry lookup; the closed ProviderName union is gone (now a string alias, since the env var is a runtime string anyway). Also adds a host-side provider-container-registry so providers can declare their own mounts and env passthrough in src/providers/<name>.ts instead of the container-runner having to know about each one. The resolver runs once per spawn and threads provider + contribution through buildMounts and buildContainerArgs so side effects (mkdir, etc.) fire exactly once. Both barrels are append-only — adding a new provider is a new file + one import line per barrel, no edits to existing files. The built-in providers (claude, mock) don't need host-side config, so src/providers/ ships with an empty barrel; the container-side barrel imports both. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,8 @@
|
||||
* - SESSION_INBOUND_DB_PATH: path to host-owned inbound DB (default: /workspace/inbound.db)
|
||||
* - SESSION_OUTBOUND_DB_PATH: path to container-owned outbound DB (default: /workspace/outbound.db)
|
||||
* - SESSION_HEARTBEAT_PATH: heartbeat file path (default: /workspace/.heartbeat)
|
||||
* - AGENT_PROVIDER: 'claude' | 'mock' (default: claude)
|
||||
* - AGENT_PROVIDER: any registered provider name (default: claude). The
|
||||
* set of registered providers is whatever `providers/index.ts` imports.
|
||||
* - NANOCLAW_ASSISTANT_NAME: assistant name for transcript archiving
|
||||
* - NANOCLAW_ADMIN_USER_IDS: comma-separated user IDs allowed to run admin commands
|
||||
*
|
||||
@@ -27,6 +28,9 @@ import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import { buildSystemPromptAddendum } from './destinations.js';
|
||||
// Providers barrel — each enabled provider self-registers on import.
|
||||
// Provider skills append imports to providers/index.ts.
|
||||
import './providers/index.js';
|
||||
import { createProvider, type ProviderName } from './providers/factory.js';
|
||||
import { runPollLoop } from './poll-loop.js';
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import path from 'path';
|
||||
|
||||
import { query as sdkQuery, type HookCallback, type PreCompactHookInput } from '@anthropic-ai/claude-agent-sdk';
|
||||
|
||||
import { registerProvider } from './provider-registry.js';
|
||||
import type { AgentProvider, AgentQuery, McpServerConfig, ProviderEvent, ProviderOptions, QueryInput } from './types.js';
|
||||
|
||||
function log(msg: string): void {
|
||||
@@ -271,3 +272,5 @@ export class ClaudeProvider implements AgentProvider {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
registerProvider('claude', (opts) => new ClaudeProvider(opts));
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import type { AgentProvider, ProviderOptions } from './types.js';
|
||||
import { ClaudeProvider } from './claude.js';
|
||||
import { MockProvider } from './mock.js';
|
||||
import { getProviderFactory } from './provider-registry.js';
|
||||
|
||||
export type ProviderName = 'claude' | 'mock';
|
||||
/**
|
||||
* Any registered provider name. Kept as a named alias for readability; the
|
||||
* set of valid names is open and determined at runtime by whichever provider
|
||||
* modules the `providers/index.ts` barrel imports.
|
||||
*/
|
||||
export type ProviderName = string;
|
||||
|
||||
export function createProvider(name: ProviderName, options: ProviderOptions = {}): AgentProvider {
|
||||
switch (name) {
|
||||
case 'claude':
|
||||
return new ClaudeProvider(options);
|
||||
case 'mock':
|
||||
return new MockProvider(options);
|
||||
default:
|
||||
throw new Error(`Unknown provider: ${name}`);
|
||||
}
|
||||
return getProviderFactory(name)(options);
|
||||
}
|
||||
|
||||
6
container/agent-runner/src/providers/index.ts
Normal file
6
container/agent-runner/src/providers/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
// Provider self-registration barrel.
|
||||
// Each import triggers the provider module's registerProvider() call at top
|
||||
// level. Skills add a new provider by appending one import line below.
|
||||
|
||||
import './claude.js';
|
||||
import './mock.js';
|
||||
@@ -1,3 +1,4 @@
|
||||
import { registerProvider } from './provider-registry.js';
|
||||
import type { AgentProvider, AgentQuery, ProviderEvent, ProviderOptions, QueryInput } from './types.js';
|
||||
|
||||
/**
|
||||
@@ -72,3 +73,5 @@ export class MockProvider implements AgentProvider {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
registerProvider('mock', (opts) => new MockProvider(opts));
|
||||
|
||||
33
container/agent-runner/src/providers/provider-registry.ts
Normal file
33
container/agent-runner/src/providers/provider-registry.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Provider self-registration registry.
|
||||
*
|
||||
* Mirrors `src/channels/channel-registry.ts` on the host. Each provider module
|
||||
* calls `registerProvider()` at top level; the barrel (`providers/index.ts`)
|
||||
* imports every provider module for its side effect so registrations fire
|
||||
* before `createProvider()` is called.
|
||||
*/
|
||||
import type { AgentProvider, ProviderOptions } from './types.js';
|
||||
|
||||
export type ProviderFactory = (options: ProviderOptions) => AgentProvider;
|
||||
|
||||
const registry = new Map<string, ProviderFactory>();
|
||||
|
||||
export function registerProvider(name: string, factory: ProviderFactory): void {
|
||||
if (registry.has(name)) {
|
||||
throw new Error(`Provider already registered: ${name}`);
|
||||
}
|
||||
registry.set(name, factory);
|
||||
}
|
||||
|
||||
export function getProviderFactory(name: string): ProviderFactory {
|
||||
const factory = registry.get(name);
|
||||
if (!factory) {
|
||||
const known = [...registry.keys()].join(', ') || '(none)';
|
||||
throw new Error(`Unknown provider: ${name}. Registered: ${known}`);
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
||||
export function listProviderNames(): string[] {
|
||||
return [...registry.keys()];
|
||||
}
|
||||
Reference in New Issue
Block a user