You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
6.7 KiB
124 lines
6.7 KiB
'use strict'; |
|
// Реестр MCP-приложений ArtCraft: discovery бинарников, порты, флаги коннекта к окну. |
|
const fs = require('fs'); |
|
const path = require('path'); |
|
|
|
// Имя инструмента -> папка, CLI-бинарь, GUI-бинарь, порт, аргументы коннекта CLI к окну. |
|
// connect: null = флаг в help не найден, CLI стартует без него (headless или дефолтный коннект). |
|
// В connect плейсхолдер {port} заменяется на порт приложения. |
|
const DEFAULT_APPS = { |
|
vectorcraft: { dir: 'VectorCraft', bin: 'vectorcraft-cli.exe', gui: 'vectorcraft.exe', port: 7979, connect: ['--connect', '127.0.0.1:{port}'] }, |
|
photocraft: { dir: 'PhotoCraft', bin: 'photocraft-cli.exe', gui: 'photocraft.exe', port: 7980, connect: ['--bridge', '127.0.0.1:{port}'] }, |
|
figcraft: { dir: 'FigCraft', bin: 'figcraft-cli.exe', gui: 'figcraft.exe', port: 7981, connect: ['--attach', '{port}'] }, |
|
designcraft: { dir: 'DesignCraft', bin: 'designcraft-cli.exe', gui: 'designcraft.exe', port: 7982, connect: ['--connect', '{port}'] }, |
|
deckcraft: { dir: 'DeckCraft', bin: 'deckcraft-cli.exe', gui: 'deckcraft.exe', port: 7983, connect: ['--connect', '{port}'] }, |
|
cadcraft: { dir: 'CADCraft', bin: 'cadcraft-cli.exe', gui: 'cadcraft.exe', port: 7984, connect: ['--connect', '127.0.0.1:{port}'] }, |
|
craftanything: { dir: 'Craft Anything', bin: 'craft-anything-cli.exe', gui: 'craft-anything.exe', port: 7985, connect: null }, |
|
effectcraft: { dir: 'EffectCraft', bin: 'effectcraft-cli.exe', gui: 'effectcraft.exe', port: 7986, connect: ['--bridge', '{port}'] }, |
|
filmcraft: { dir: 'FilmCraft', bin: 'filmcraft-cli.exe', gui: 'filmcraft.exe', port: 7987, connect: ['--bridge', '127.0.0.1:{port}'] }, |
|
gridcraft: { dir: 'GridCraft', bin: 'gridcraft-cli.exe', gui: 'gridcraft.exe', port: 7988, connect: ['--connect', '{port}'] }, |
|
lightcraft: { dir: 'LightCraft', bin: 'lightcraft-cli.exe', gui: 'lightcraft.exe', port: 7989, connect: ['--connect', '127.0.0.1:{port}'] }, |
|
materialcraft: { dir: 'MaterialCraft', bin: 'materialcraft-cli.exe', gui: 'materialcraft.exe', port: 7990, connect: ['--bridge', '{port}'] }, |
|
pdfcraft: { dir: 'PdfCraft', bin: 'pdfcraft-cli.exe', gui: 'pdfcraft.exe', port: 7991, connect: null }, |
|
solidcraft: { dir: 'SolidCraft', bin: 'solidcraft-cli.exe', gui: 'solidcraft.exe', port: 7992, connect: ['--connect', '127.0.0.1:{port}'] }, |
|
soundcraft: { dir: 'SoundCraft', bin: 'soundcraft-cli.exe', gui: 'soundcraft.exe', port: 7993, connect: ['--connect', '{port}'] }, |
|
texturecraft: { dir: 'TextureCraft', bin: 'texturecraft-cli.exe', gui: 'texturecraft.exe', port: 7994, connect: ['--bridge', '{port}'] }, |
|
wordcraft: { dir: 'WordCraft', bin: 'wordcraft-cli.exe', gui: 'wordcraft.exe', port: 7995, connect: ['--connect', '127.0.0.1:{port}'] }, |
|
}; |
|
|
|
function artcraftCfg(config) { |
|
return (config && config.mcpServers && config.mcpServers.artcraft) || {}; |
|
} |
|
|
|
function resolveRoot(config) { |
|
const c = artcraftCfg(config); |
|
return c.root || process.env.ARTCRAFT_ROOT || 'V:\\artcraft'; |
|
} |
|
|
|
function resolveDisabled(config) { |
|
const list = artcraftCfg(config).disabled || []; |
|
return Array.isArray(list) ? list.map(String) : []; |
|
} |
|
|
|
function resolvePorts(config) { |
|
const p = artcraftCfg(config).ports || {}; |
|
return (p && typeof p === 'object') ? p : {}; |
|
} |
|
|
|
function discover(root, spec) { |
|
const full = path.join(root, spec.dir, spec.bin); |
|
try { return fs.existsSync(full) ? full : null; } catch { return null; } |
|
} |
|
|
|
function discoverGui(root, spec) { |
|
if (!spec.gui) return null; |
|
const full = path.join(root, spec.dir, spec.gui); |
|
try { return fs.existsSync(full) ? full : null; } catch { return null; } |
|
} |
|
|
|
function toolNameFromDir(dirName) { |
|
return String(dirName).toLowerCase().replace(/[^a-z]/g, ''); |
|
} |
|
|
|
// Автообнаружение: папка с единственным *-cli.exe, которой нет в явном списке. |
|
// Порт и флаг коннекта неизвестны -> null, работает headless. |
|
function autodiscover(root, known) { |
|
const found = {}; |
|
let entries = []; |
|
try { entries = fs.readdirSync(root, { withFileTypes: true }); } catch { return found; } |
|
for (const it of entries) { |
|
if (!it.isDirectory()) continue; |
|
const dir = path.join(root, it.name); |
|
let exes = []; |
|
try { exes = fs.readdirSync(dir).filter(n => /-cli\.exe$/i.test(n)); } catch { continue; } |
|
if (exes.length !== 1) continue; |
|
const bin = path.join(dir, exes[0]); |
|
if (Object.values(known).some(s => path.join(root, s.dir, s.bin).toLowerCase() === bin.toLowerCase())) continue; |
|
const name = toolNameFromDir(it.name); |
|
if (!name || known[name] || found[name]) continue; |
|
const guiName = it.name.toLowerCase().replace(/[^a-z0-9]/g, '') + '.exe'; |
|
let gui = null; |
|
try { |
|
const guiMatch = fs.readdirSync(dir).find(n => /\.exe$/i.test(n) && !/-cli\.exe$/i.test(n)); |
|
if (guiMatch) gui = guiMatch; |
|
} catch {} |
|
found[name] = { dir: it.name, bin: exes[0], gui, port: null, connect: null, auto: true }; |
|
} |
|
return found; |
|
} |
|
|
|
function buildSpecs(config) { |
|
const root = resolveRoot(config); |
|
const disabled = resolveDisabled(config); |
|
const portOverride = resolvePorts(config); |
|
const known = { ...DEFAULT_APPS, ...autodiscover(root, DEFAULT_APPS) }; |
|
const out = {}; |
|
for (const [name, spec] of Object.entries(known)) { |
|
if (disabled.includes(name)) { out[name] = { name, enabled: false, reason: 'disabled in config' }; continue; } |
|
const bin = discover(root, spec); |
|
if (!bin) { out[name] = { name, enabled: false, reason: 'not installed: ' + path.join(root, spec.dir, spec.bin) }; continue; } |
|
const port = portOverride[name] != null ? Number(portOverride[name]) : spec.port; |
|
const connect = Array.isArray(spec.connect) ? spec.connect.map(a => String(a).replace('{port}', String(port))) : null; |
|
out[name] = { |
|
name, |
|
enabled: true, |
|
bin, |
|
gui: discoverGui(root, spec), |
|
args: ['mcp'].concat(connect || []), |
|
connectKnown: !!connect, |
|
port, |
|
cwd: path.dirname(bin), |
|
auto: !!spec.auto, |
|
}; |
|
} |
|
return { root, apps: out }; |
|
} |
|
|
|
// Аргументы для запуска GUI с управляющим портом. |
|
function guiLaunchArgs(spec) { |
|
if (!spec || !spec.gui) return null; |
|
if (spec.port == null) return []; |
|
return ['--control', String(spec.port)]; |
|
} |
|
|
|
module.exports = { DEFAULT_APPS, resolveRoot, resolveDisabled, buildSpecs, toolNameFromDir, guiLaunchArgs };
|
|
|