| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
|
|
| export interface PluginContext { |
| |
| requestId: string; |
| |
| body: any; |
| |
| model: string; |
| |
| provider?: string; |
| |
| apiKeyInfo?: any; |
| |
| metadata: Record<string, any>; |
| } |
|
|
| export interface PluginResult { |
| |
| blocked?: boolean; |
| |
| response?: any; |
| |
| body?: any; |
| |
| metadata?: Record<string, any>; |
| } |
|
|
| export interface Plugin { |
| |
| name: string; |
| |
| priority?: number; |
| |
| enabled?: boolean; |
| |
| onRequest?: (ctx: PluginContext) => Promise<PluginResult | void> | PluginResult | void; |
| |
| onResponse?: (ctx: PluginContext, response: any) => Promise<any | void> | any | void; |
| |
| onError?: (ctx: PluginContext, error: Error) => Promise<any | void> | any | void; |
| } |
|
|
| |
|
|
| const _plugins: Plugin[] = []; |
|
|
| |
| |
| |
| export function registerPlugin(plugin: Plugin): void { |
| |
| plugin.priority = plugin.priority ?? 100; |
| plugin.enabled = plugin.enabled ?? true; |
|
|
| |
| const idx = _plugins.findIndex((p) => p.name === plugin.name); |
| if (idx !== -1) _plugins.splice(idx, 1); |
|
|
| _plugins.push(plugin); |
| _plugins.sort((a, b) => (a.priority || 100) - (b.priority || 100)); |
|
|
| console.log( |
| `[Plugins] Registered "${plugin.name}" (priority: ${plugin.priority}, enabled: ${plugin.enabled})` |
| ); |
| } |
|
|
| |
| |
| |
| export function unregisterPlugin(name: string): boolean { |
| const idx = _plugins.findIndex((p) => p.name === name); |
| if (idx === -1) return false; |
| _plugins.splice(idx, 1); |
| return true; |
| } |
|
|
| |
| |
| |
| export function setPluginEnabled(name: string, enabled: boolean): boolean { |
| const plugin = _plugins.find((p) => p.name === name); |
| if (!plugin) return false; |
| plugin.enabled = enabled; |
| return true; |
| } |
|
|
| |
| |
| |
| export function listPlugins(): Array<{ |
| name: string; |
| priority: number; |
| enabled: boolean; |
| hooks: string[]; |
| }> { |
| return _plugins.map((p) => ({ |
| name: p.name, |
| priority: p.priority || 100, |
| enabled: p.enabled !== false, |
| hooks: [ |
| p.onRequest ? "onRequest" : "", |
| p.onResponse ? "onResponse" : "", |
| p.onError ? "onError" : "", |
| ].filter(Boolean), |
| })); |
| } |
|
|
| |
|
|
| |
| |
| |
| |
| export async function runOnRequest( |
| ctx: PluginContext |
| ): Promise<{ blocked: boolean; response?: any; ctx: PluginContext }> { |
| let currentCtx = { ...ctx }; |
|
|
| for (const plugin of _plugins) { |
| if (!plugin.enabled || !plugin.onRequest) continue; |
|
|
| try { |
| const result = await plugin.onRequest(currentCtx); |
| if (result) { |
| if (result.blocked) { |
| console.log(`[Plugins] Request blocked by "${plugin.name}"`); |
| return { blocked: true, response: result.response, ctx: currentCtx }; |
| } |
| if (result.body) currentCtx.body = result.body; |
| if (result.metadata) { |
| currentCtx.metadata = { ...currentCtx.metadata, ...result.metadata }; |
| } |
| } |
| } catch (err: any) { |
| console.error(`[Plugins] onRequest error in "${plugin.name}": ${err.message}`); |
| |
| } |
| } |
|
|
| return { blocked: false, ctx: currentCtx }; |
| } |
|
|
| |
| |
| |
| export async function runOnResponse(ctx: PluginContext, response: any): Promise<any> { |
| let currentResponse = response; |
|
|
| for (const plugin of _plugins) { |
| if (!plugin.enabled || !plugin.onResponse) continue; |
|
|
| try { |
| const modified = await plugin.onResponse(ctx, currentResponse); |
| if (modified !== undefined && modified !== null) { |
| currentResponse = modified; |
| } |
| } catch (err: any) { |
| console.error(`[Plugins] onResponse error in "${plugin.name}": ${err.message}`); |
| } |
| } |
|
|
| return currentResponse; |
| } |
|
|
| |
| |
| |
| |
| export async function runOnError(ctx: PluginContext, error: Error): Promise<any | null> { |
| for (const plugin of _plugins) { |
| if (!plugin.enabled || !plugin.onError) continue; |
|
|
| try { |
| const recovery = await plugin.onError(ctx, error); |
| if (recovery !== undefined && recovery !== null) { |
| console.log(`[Plugins] Error recovered by "${plugin.name}"`); |
| return recovery; |
| } |
| } catch (err: any) { |
| console.error(`[Plugins] onError error in "${plugin.name}": ${err.message}`); |
| } |
| } |
|
|
| return null; |
| } |
|
|
| |
| |
| |
| export function resetPlugins(): void { |
| _plugins.length = 0; |
| } |
|
|