Files
stack/apps/orchestrator/src/api/providers/providers.module.ts
Jason Woltje 495d78115e
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
feat(orchestrator): add OpenClaw SSE bridge streaming
2026-03-07 16:20:27 -06:00

96 lines
2.8 KiB
TypeScript

import { HttpModule } from "@nestjs/axios";
import { Logger, Module, OnModuleInit } from "@nestjs/common";
import type { AgentProviderConfig } from "@prisma/client";
import { PrismaModule } from "../../prisma/prisma.module";
import { PrismaService } from "../../prisma/prisma.service";
import { EncryptionService } from "../../security/encryption.service";
import { AgentProviderRegistry } from "../agents/agent-provider.registry";
import { AgentsModule } from "../agents/agents.module";
import { OpenClawProviderFactory } from "./openclaw/openclaw.provider-factory";
import { OpenClawSseBridge } from "./openclaw/openclaw-sse.bridge";
const OPENCLAW_PROVIDER_TYPE = "openclaw";
@Module({
imports: [
AgentsModule,
PrismaModule,
HttpModule.register({
timeout: 10000,
maxRedirects: 5,
}),
],
providers: [EncryptionService, OpenClawSseBridge, OpenClawProviderFactory],
})
export class ProvidersModule implements OnModuleInit {
private readonly logger = new Logger(ProvidersModule.name);
constructor(
private readonly prisma: PrismaService,
private readonly registry: AgentProviderRegistry,
private readonly openClawProviderFactory: OpenClawProviderFactory
) {}
async onModuleInit(): Promise<void> {
const configs = await this.prisma.agentProviderConfig.findMany({
where: {
provider: OPENCLAW_PROVIDER_TYPE,
isActive: true,
},
orderBy: [{ createdAt: "asc" }, { id: "asc" }],
});
for (const config of configs) {
await this.registerProvider(config);
}
}
private async registerProvider(config: AgentProviderConfig): Promise<void> {
const provider = this.openClawProviderFactory.createProvider(config);
try {
provider.validateBaseUrl();
} catch (error) {
this.logger.warn(
`Skipping OpenClaw provider ${config.name}: invalid configuration (${this.toErrorMessage(error)})`
);
return;
}
try {
provider.validateToken();
} catch (error) {
this.logger.error(
`Skipping OpenClaw provider ${config.name}: token decryption failed (${this.toErrorMessage(error)})`
);
return;
}
try {
const available = await provider.isAvailable();
if (!available) {
this.logger.warn(
`Skipping OpenClaw provider ${config.name}: gateway ${config.gatewayUrl} is unreachable`
);
return;
}
} catch (error) {
this.logger.warn(
`Skipping OpenClaw provider ${config.name}: gateway ${config.gatewayUrl} is unreachable (${this.toErrorMessage(error)})`
);
return;
}
this.registry.registerProvider(provider);
this.logger.log(`Registered OpenClaw provider ${provider.providerId}`);
}
private toErrorMessage(error: unknown): string {
if (error instanceof Error) {
return error.message;
}
return String(error);
}
}