src / toolsProvidertest.ts
src / toolsProvidertest.ts
// src/toolsProvider.ts
import { tool, Tool } from "@lmstudio/sdk";
import { z } from "zod";
import { executeFileTool } from "./tools/fileTools";
import { executeMemoryTool } from "./tools/memoryTools";
import { executeSystemTool } from "./tools/systemTools";
import { executeWebTool } from "./tools/webTools";
import { executeExcelTool } from "./tools/excelTools";
// ============================================================
// SERVER REGISTRY — 4 Cognitive Arms
// ============================================================
const SERVERS = {
core: "http://127.0.0.1:6075", // Unified Server (31 tools)
code: "http://127.0.0.1:6041", // Code Server (13 tools)
terminal: "http://127.0.0.1:6090", // Terminal Server (8 tools)
memory: "http://127.0.0.1:5077", // Memory Server (3 tools)
} as const;
type ServerName = keyof typeof SERVERS;
// ============================================================
// TOOL ALIASES — Normalize model's notations (ضابط الاتصال)
// ============================================================
const TOOL_ALIASES: Record<string, string> = {
// Time (✅ إصلاح أزمة الوقت)
getTime: "getNow",
getCurrentTime: "getNow",
time: "getNow",
getDate: "getCurrentDate",
getCurrentDate: "getCurrentDate",
date: "getCurrentDate",
// Files
deleteFile: "moveToTrash",
listFolder: "listFiles",
appendFile: "appendToFile",
// Code
runCode: "runPythonScript",
// Terminal
terminal: "exec",
// Memory
semanticMemory: "semanticSearch",
memory_search: "semanticSearch",
// Compatibility with local names
read_file: "readFile",
write_file: "writeFile",
list_folder: "listFiles",
};
// ============================================================
// LOCAL RESERVE TOOLS (الجيش الاحتياطي)
// ============================================================
const LOCAL_TOOLS = [
{ name: "getNow", description: "Get current time (local fallback)", server: "local" },
{ name: "getCurrentDate", description: "Get current date (local fallback)", server: "local" },
{ name: "readFile", description: "Read file (local fallback)", server: "local" },
{ name: "writeFile", description: "Write file (local fallback)", server: "local" },
{ name: "searchFiles", description: "Search files (local fallback)", server: "local" },
{ name: "markAwareness", description: "Record awareness insight (local fallback)", server: "local" },
{ name: "saveSession", description: "Save session to memory (local fallback)", server: "local" },
{ name: "runCode", description: "Run code locally (local fallback)", server: "local" },
{ name: "terminal", description: "Execute terminal command (local fallback)", server: "local" },
];
// ============================================================
// CATALOG CACHE
// ============================================================
let cachedCatalog: any[] | null = null;
let cachedToolToServer: Map<string, ServerName | "local"> = new Map();
let catalogPromise: Promise<void> | null = null;
async function refreshCatalog(): Promise<void> {
if (cachedCatalog) return;
if (catalogPromise) return catalogPromise;
catalogPromise = (async () => {
const fetchServer = async (name: ServerName, url: string) => {
try {
const res = await fetch(`${url}/tools`, { signal: AbortSignal.timeout(2000) });
if (!res.ok) return [];
const data = await res.json() as { descriptions?: Record<string, any> };
return Object.entries(data.descriptions || {}).map(([toolName, details]) => ({
name: toolName,
description: details.description,
server: name,
}));
} catch {
console.warn(`[Catalog] Server '${name}' unreachable`);
return [];
}
};
const results = await Promise.all(
Object.entries(SERVERS).map(([n, u]) => fetchServer(n as ServerName, u))
);
cachedCatalog = results.flat();
cachedToolToServer = new Map();
for (const entry of cachedCatalog) {
if (entry.server) cachedToolToServer.set(entry.name, entry.server as ServerName);
}
// ✅ إضافة الجيش الاحتياطي إلى الكتالوج
for (const t of LOCAL_TOOLS) {
cachedCatalog.push(t);
cachedToolToServer.set(t.name, "local");
}
console.info(`[Catalog] Cached ${cachedCatalog.length} tools from ${Object.keys(SERVERS).length} servers + local reserve`);
})();
return catalogPromise;
}
// ============================================================
// EXECUTE VIA SERVER
// ============================================================
async function executeViaServer(toolName: string, params: any): Promise<any | null> {
const serverName = cachedToolToServer.get(toolName);
if (!serverName || serverName === "local") return null;
try {
const res = await fetch(`${SERVERS[serverName]}/tool/${encodeURIComponent(toolName)}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params || {}),
signal: AbortSignal.timeout(10000),
});
if (!res.ok) return null;
const data = await res.json() as any;
if (data.status === "success" || data.success) {
return { success: true, ...data, source: `server_${serverName}` };
}
} catch (err) {
console.warn(`[Server] ${serverName}/${toolName}: ${(err as Error).message}`);
}
return null;
}
// ============================================================
// EXECUTE LOCALLY — Fallback (الجيش الاحتياطي)
// ============================================================
async function executeLocally(toolName: string, params: any): Promise<any | null> {
let result = await executeSystemTool(toolName, params);
if (result) return result;
result = await executeFileTool(toolName, params);
if (result) return result;
result = await executeMemoryTool(toolName, params);
if (result) return result;
result = await executeWebTool(toolName, params);
if (result) return result;
result = await executeExcelTool(toolName, params);
if (result) return result;
return null;
}
// ============================================================
// EXECUTE TOOL — Main dispatcher
// ============================================================
export async function executeTool(rawToolName: string, args: any = {}): Promise<any> {
const toolName = TOOL_ALIASES[rawToolName] || rawToolName;
// 1. Try remote servers first (if not marked local)
const serverType = cachedToolToServer.get(toolName);
if (serverType !== "local") {
const serverResult = await executeViaServer(toolName, args);
if (serverResult) return serverResult;
}
// 2. Local fallback (الجيش الاحتياطي يتولى المسؤولية)
const localResult = await executeLocally(toolName, args);
if (localResult) return localResult;
return { success: false, error: `Unknown tool: ${toolName}` };
}
// ============================================================
// TOOLS PROVIDER — Single Gateway
// ============================================================
export async function toolsProvider(): Promise<Tool[]> {
// Prime the catalog (non-blocking if fails)
try {
await refreshCatalog();
} catch {
console.warn("[toolsProvider] Catalog refresh failed — gateway will still work");
}
return [
tool({
name: "bido_gateway",
description: `Execute any local tool by exact name.
## Core Tools (Server 6075)
getTime() | getDate() | readFile(path, full?) | writeFile(path, content) | appendToFile(path, content) | copyFile(from, to) | renameFile(from, to) | moveToTrash(path) | listFiles(path) | getFileInfo(path) | eagleEye(path, mode?) | listDirectoryStructure(path, depth?) | makeDirectory(path) | copyDirectory(from, to) | renameDirectory(from, to) | searchFiles(path, query, type) | extractTextFromImage(image, language?) | runPythonScript(code, args?, timeout?) | createDynamicTool(name, language, code, description) | runDynamicTool(name, args?, timeout?) | semanticSearch(query, limit?) | saveSession(title, mainIdea, summary, rawContent, tags) | readRawSession(sessionId) | addMemory(namespace, content, tags) | getMemory(namespace, id?, tags?, limit?) | markAwareness(insight, tags, context) | recallAwareness(query?, tags?, limit?) | searchWeb(query, maxResults?) | fetchArticle(url, filename) | readConsciousness() | appendToDynamicPrompt(content)
## Code Tools (Server 6041)
readFileLines(path, start?, end?) | getFileOutline(path) | searchInFile(path, query, regex?, caseSensitive?, maxResults?) | readFileWindow(path, line, context?) | replaceLines(path, start, end, content) | insertAtLine(path, line, content, before?) | deleteLines(path, start, end) | replaceInFile(path, find, replace, regex?, all?) | replaceBlock(path, newContent, startTag?, endTag?, occurrence?) | findSymbols(path, type?) | findReferences(symbol, path?, maxResults?) | getDependencies(path) | codeStats(path?)
## Terminal Tools (Server 6090)
exec(cmd, cwd?, timeout?) | health() | history() | download(url, output?) | listPorts() | killProcess(pid, force?) | which(cmd) | env()
## Memory Tools (Server 5077)
semanticSearch(query, limit?) | saveSession(title, mainIdea, summary, rawContent, tags) | readRawSession(sessionId)
## Local Fallback Tools (always available)
readFile(file) | writeFile(file, content) | appendFile(file, content) | deleteFile(file) | copyFile(source, destination) | renameFile(oldName, newName) | listFolder(folder) | createFolder(folder) | searchFiles(query, searchPath?, searchType?) | getNow() | terminal(cmd, cwd?) | runCode(code, language?) | eagleEye(file) | ocrImage(imagePath, language?) | saveSession(title, summary, tags?) | getMemory(query) | semanticMemory(query) | markAwareness(insight) | recallAwareness(query?) | searchWeb(query, maxResults?) | fetchArticle(url) | readExcelSheet(file, sheet?) | appendExcelRow(file, sheet?, row)
## Rules
- Use the EXACT tool name from the lists above.
- Pass all parameters as a JSON object.
- If a tool is in both a server and the local fallback, the server takes priority.
- If you're unsure which tool to use, choose the closest name match.`,
parameters: {
tool: z.string().describe("Exact tool name (case-sensitive)"),
params: z.record(z.string(), z.unknown()).optional().describe("Tool parameters as JSON object"),
},
implementation: async ({ tool: toolName, params }) => {
const result = await executeTool(toolName, params || {});
return typeof result === "object" ? JSON.stringify(result, null, 2) : String(result);
},
}),
];
}// src/toolsProvider.ts
import { tool, Tool } from "@lmstudio/sdk";
import { z } from "zod";
import { executeFileTool } from "./tools/fileTools";
import { executeMemoryTool } from "./tools/memoryTools";
import { executeSystemTool } from "./tools/systemTools";
import { executeWebTool } from "./tools/webTools";
import { executeExcelTool } from "./tools/excelTools";
// ============================================================
// SERVER REGISTRY — 4 Cognitive Arms
// ============================================================
const SERVERS = {
core: "http://127.0.0.1:6075", // Unified Server (31 tools)
code: "http://127.0.0.1:6041", // Code Server (13 tools)
terminal: "http://127.0.0.1:6090", // Terminal Server (8 tools)
memory: "http://127.0.0.1:5077", // Memory Server (3 tools)
} as const;
type ServerName = keyof typeof SERVERS;
// ============================================================
// TOOL ALIASES — Normalize model's notations (ضابط الاتصال)
// ============================================================
const TOOL_ALIASES: Record<string, string> = {
// Time (✅ إصلاح أزمة الوقت)
getTime: "getNow",
getCurrentTime: "getNow",
time: "getNow",
getDate: "getCurrentDate",
getCurrentDate: "getCurrentDate",
date: "getCurrentDate",
// Files
deleteFile: "moveToTrash",
listFolder: "listFiles",
appendFile: "appendToFile",
// Code
runCode: "runPythonScript",
// Terminal
terminal: "exec",
// Memory
semanticMemory: "semanticSearch",
memory_search: "semanticSearch",
// Compatibility with local names
read_file: "readFile",
write_file: "writeFile",
list_folder: "listFiles",
};
// ============================================================
// LOCAL RESERVE TOOLS (الجيش الاحتياطي)
// ============================================================
const LOCAL_TOOLS = [
{ name: "getNow", description: "Get current time (local fallback)", server: "local" },
{ name: "getCurrentDate", description: "Get current date (local fallback)", server: "local" },
{ name: "readFile", description: "Read file (local fallback)", server: "local" },
{ name: "writeFile", description: "Write file (local fallback)", server: "local" },
{ name: "searchFiles", description: "Search files (local fallback)", server: "local" },
{ name: "markAwareness", description: "Record awareness insight (local fallback)", server: "local" },
{ name: "saveSession", description: "Save session to memory (local fallback)", server: "local" },
{ name: "runCode", description: "Run code locally (local fallback)", server: "local" },
{ name: "terminal", description: "Execute terminal command (local fallback)", server: "local" },
];
// ============================================================
// CATALOG CACHE
// ============================================================
let cachedCatalog: any[] | null = null;
let cachedToolToServer: Map<string, ServerName | "local"> = new Map();
let catalogPromise: Promise<void> | null = null;
async function refreshCatalog(): Promise<void> {
if (cachedCatalog) return;
if (catalogPromise) return catalogPromise;
catalogPromise = (async () => {
const fetchServer = async (name: ServerName, url: string) => {
try {
const res = await fetch(`${url}/tools`, { signal: AbortSignal.timeout(2000) });
if (!res.ok) return [];
const data = await res.json() as { descriptions?: Record<string, any> };
return Object.entries(data.descriptions || {}).map(([toolName, details]) => ({
name: toolName,
description: details.description,
server: name,
}));
} catch {
console.warn(`[Catalog] Server '${name}' unreachable`);
return [];
}
};
const results = await Promise.all(
Object.entries(SERVERS).map(([n, u]) => fetchServer(n as ServerName, u))
);
cachedCatalog = results.flat();
cachedToolToServer = new Map();
for (const entry of cachedCatalog) {
if (entry.server) cachedToolToServer.set(entry.name, entry.server as ServerName);
}
// ✅ إضافة الجيش الاحتياطي إلى الكتالوج
for (const t of LOCAL_TOOLS) {
cachedCatalog.push(t);
cachedToolToServer.set(t.name, "local");
}
console.info(`[Catalog] Cached ${cachedCatalog.length} tools from ${Object.keys(SERVERS).length} servers + local reserve`);
})();
return catalogPromise;
}
// ============================================================
// EXECUTE VIA SERVER
// ============================================================
async function executeViaServer(toolName: string, params: any): Promise<any | null> {
const serverName = cachedToolToServer.get(toolName);
if (!serverName || serverName === "local") return null;
try {
const res = await fetch(`${SERVERS[serverName]}/tool/${encodeURIComponent(toolName)}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params || {}),
signal: AbortSignal.timeout(10000),
});
if (!res.ok) return null;
const data = await res.json() as any;
if (data.status === "success" || data.success) {
return { success: true, ...data, source: `server_${serverName}` };
}
} catch (err) {
console.warn(`[Server] ${serverName}/${toolName}: ${(err as Error).message}`);
}
return null;
}
// ============================================================
// EXECUTE LOCALLY — Fallback (الجيش الاحتياطي)
// ============================================================
async function executeLocally(toolName: string, params: any): Promise<any | null> {
let result = await executeSystemTool(toolName, params);
if (result) return result;
result = await executeFileTool(toolName, params);
if (result) return result;
result = await executeMemoryTool(toolName, params);
if (result) return result;
result = await executeWebTool(toolName, params);
if (result) return result;
result = await executeExcelTool(toolName, params);
if (result) return result;
return null;
}
// ============================================================
// EXECUTE TOOL — Main dispatcher
// ============================================================
export async function executeTool(rawToolName: string, args: any = {}): Promise<any> {
const toolName = TOOL_ALIASES[rawToolName] || rawToolName;
// 1. Try remote servers first (if not marked local)
const serverType = cachedToolToServer.get(toolName);
if (serverType !== "local") {
const serverResult = await executeViaServer(toolName, args);
if (serverResult) return serverResult;
}
// 2. Local fallback (الجيش الاحتياطي يتولى المسؤولية)
const localResult = await executeLocally(toolName, args);
if (localResult) return localResult;
return { success: false, error: `Unknown tool: ${toolName}` };
}
// ============================================================
// TOOLS PROVIDER — Single Gateway
// ============================================================
export async function toolsProvider(): Promise<Tool[]> {
// Prime the catalog (non-blocking if fails)
try {
await refreshCatalog();
} catch {
console.warn("[toolsProvider] Catalog refresh failed — gateway will still work");
}
return [
tool({
name: "bido_gateway",
description: `Execute any local tool by exact name.
## Core Tools (Server 6075)
getTime() | getDate() | readFile(path, full?) | writeFile(path, content) | appendToFile(path, content) | copyFile(from, to) | renameFile(from, to) | moveToTrash(path) | listFiles(path) | getFileInfo(path) | eagleEye(path, mode?) | listDirectoryStructure(path, depth?) | makeDirectory(path) | copyDirectory(from, to) | renameDirectory(from, to) | searchFiles(path, query, type) | extractTextFromImage(image, language?) | runPythonScript(code, args?, timeout?) | createDynamicTool(name, language, code, description) | runDynamicTool(name, args?, timeout?) | semanticSearch(query, limit?) | saveSession(title, mainIdea, summary, rawContent, tags) | readRawSession(sessionId) | addMemory(namespace, content, tags) | getMemory(namespace, id?, tags?, limit?) | markAwareness(insight, tags, context) | recallAwareness(query?, tags?, limit?) | searchWeb(query, maxResults?) | fetchArticle(url, filename) | readConsciousness() | appendToDynamicPrompt(content)
## Code Tools (Server 6041)
readFileLines(path, start?, end?) | getFileOutline(path) | searchInFile(path, query, regex?, caseSensitive?, maxResults?) | readFileWindow(path, line, context?) | replaceLines(path, start, end, content) | insertAtLine(path, line, content, before?) | deleteLines(path, start, end) | replaceInFile(path, find, replace, regex?, all?) | replaceBlock(path, newContent, startTag?, endTag?, occurrence?) | findSymbols(path, type?) | findReferences(symbol, path?, maxResults?) | getDependencies(path) | codeStats(path?)
## Terminal Tools (Server 6090)
exec(cmd, cwd?, timeout?) | health() | history() | download(url, output?) | listPorts() | killProcess(pid, force?) | which(cmd) | env()
## Memory Tools (Server 5077)
semanticSearch(query, limit?) | saveSession(title, mainIdea, summary, rawContent, tags) | readRawSession(sessionId)
## Local Fallback Tools (always available)
readFile(file) | writeFile(file, content) | appendFile(file, content) | deleteFile(file) | copyFile(source, destination) | renameFile(oldName, newName) | listFolder(folder) | createFolder(folder) | searchFiles(query, searchPath?, searchType?) | getNow() | terminal(cmd, cwd?) | runCode(code, language?) | eagleEye(file) | ocrImage(imagePath, language?) | saveSession(title, summary, tags?) | getMemory(query) | semanticMemory(query) | markAwareness(insight) | recallAwareness(query?) | searchWeb(query, maxResults?) | fetchArticle(url) | readExcelSheet(file, sheet?) | appendExcelRow(file, sheet?, row)
## Rules
- Use the EXACT tool name from the lists above.
- Pass all parameters as a JSON object.
- If a tool is in both a server and the local fallback, the server takes priority.
- If you're unsure which tool to use, choose the closest name match.`,
parameters: {
tool: z.string().describe("Exact tool name (case-sensitive)"),
params: z.record(z.string(), z.unknown()).optional().describe("Tool parameters as JSON object"),
},
implementation: async ({ tool: toolName, params }) => {
const result = await executeTool(toolName, params || {});
return typeof result === "object" ? JSON.stringify(result, null, 2) : String(result);
},
}),
];
}