File size: 3,070 Bytes
b9a3ef2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// ---------------------------------------------------------------------------
// Tool Registry -- MCP-like typed tool system
// ---------------------------------------------------------------------------

import fs from 'fs';
import path from 'path';

export interface ToolParams {
    url?: string;
    prompt?: string;
    input?: string;
    raw?: string;
    captures?: string[];
    [key: string]: unknown;
}

export interface ToolResult {
    transcript?: string;
    downloadUrl?: string;
    filename?: string;
    method?: string;
    message?: string;
    mock?: boolean;
    error?: string;
    [key: string]: unknown;
}

export type ProgressEmitter = (message: string) => void;

export interface ToolDefinition {
    name: string;
    description: string;
    syntax: string;
    pattern: RegExp;
    mock: boolean;
    execute: (params: ToolParams, emitProgress: ProgressEmitter) => Promise<ToolResult>;
}

export interface ToolSummary {
    name: string;
    description: string;
    syntax: string;
    mock: boolean;
}

export interface ToolMatch {
    tool: ToolDefinition;
    params: ToolParams;
}

export class ToolRegistry {
    private tools = new Map<string, ToolDefinition>();

    register(tool: ToolDefinition): void {
        if (!tool.name || !tool.execute) {
            throw new Error('Tool must have a name and execute function.');
        }
        this.tools.set(tool.name, tool);
        console.log(`  [ToolRegistry] Registered: ${tool.name}`);
    }

    listTools(): ToolSummary[] {
        return Array.from(this.tools.values()).map((t) => ({
            name: t.name,
            description: t.description,
            syntax: t.syntax,
            mock: t.mock,
        }));
    }

    getTool(name: string): ToolDefinition | null {
        return this.tools.get(name) ?? null;
    }

    matchTool(message: string): ToolMatch | null {
        for (const tool of this.tools.values()) {
            if (!tool.pattern) continue;
            const match = message.match(tool.pattern);
            if (match) {
                return {
                    tool,
                    params: { ...match.groups, raw: message, captures: match.slice(1) },
                };
            }
        }
        return null;
    }
}

export function setupToolRegistry(): ToolRegistry {
    const registry = new ToolRegistry();
    const toolsDir = path.join(__dirname, 'tools');

    if (fs.existsSync(toolsDir)) {
        const files = fs.readdirSync(toolsDir).filter((f) => f.endsWith('.ts') || f.endsWith('.js'));
        for (const file of files) {
            try {
                const mod = require(path.join(toolsDir, file));
                if (typeof mod.register === 'function') {
                    mod.register(registry);
                }
            } catch (err: any) {
                console.error(`  [ToolRegistry] Failed to load ${file}: ${err.message}`);
            }
        }
    }

    return registry;
}