overmind-mcp
Version:
Orchestrateur universel agents IA multi-modeles via MCP. Inclut le protocole 'Custom-Nickname' pour identifier vos agents avec des surnoms originaux (The Chaos Prophet, Shadow Sniper, etc.), l'isolation mémoire (Private Memory Context) et le support pour
193 lines • 7.31 kB
JavaScript
import os from 'os';
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import { loadEnvQuietly } from './loadEnv.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Version read from package.json at build time
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
let PKG_VERSION = '2.7.0';
try {
const pkg = require('../../package.json');
PKG_VERSION = pkg.version || PKG_VERSION;
}
catch {
/* fallback */
}
export { PKG_VERSION };
export const DEFAULT_CONFIG = {
TIMEOUT_MS: 900000, // 15 minutes
KEEPALIVE_INTERVAL_MS: 300000, // 5 minutes (must be < TIMEOUT_MS to actually extend)
HARD_TIMEOUT_MS: 60000, // 1 minute extra after keepalive
CLAUDE: {
CORE: '--output-format json',
PERMISSIONS: process.env.OVERMIND_CLAUDE_PERMISSIONS || '--dangerously-skip-permissions',
PATHS: {
SETTINGS: './.claude/settings.json',
MCP: '.mcp.json',
},
},
KILO: {
CORE: '--auto',
DEFAULT_MODEL: 'step 3.5 flash',
PATHS: {
SETTINGS: './.claude/settings.json',
},
},
HERMES: {
CORE: 'chat -q',
DEFAULT_MODEL: 'MiniMax-M3',
PATHS: {
SETTINGS: './.claude/settings.json',
},
},
};
// Deep clone to prevent shared references with DEFAULT_CONFIG
export const CONFIG = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
let cachedWorkspaceDir = null;
export function resetWorkspaceCache() {
cachedWorkspaceDir = null;
}
/** Validate agent name to prevent path traversal */
export function isValidAgentName(name) {
return /^[a-zA-Z0-9_-]+$/.test(name) && name.length > 0 && name.length <= 128;
}
export function getWorkspaceDir() {
if (cachedWorkspaceDir && process.env.NODE_ENV !== 'test')
return cachedWorkspaceDir;
let workspaceDir = '';
// 1. Explicit env var (systemd, shell, or already loaded)
if (process.env.OVERMIND_WORKSPACE) {
workspaceDir = path.resolve(process.env.OVERMIND_WORKSPACE);
}
else {
// 2. Try loading ~/.overmind/.env to find OVERMIND_WORKSPACE
const homeDir = os.homedir();
const overmindEnvPath = path.join(homeDir, '.overmind', '.env');
loadEnvQuietly(overmindEnvPath);
// Expand tilde if present (Node.js does NOT auto-expand ~)
const rawWs = process.env.OVERMIND_WORKSPACE;
if (rawWs) {
workspaceDir = rawWs.startsWith('~/')
? path.join(homeDir, rawWs.slice(2))
: rawWs.startsWith('~')
? path.join(homeDir, rawWs.slice(1))
: path.resolve(rawWs);
}
}
// 3. Still not found — search CWD and parents for .mcp.json
if (!workspaceDir) {
const cwd = process.cwd();
if (fs.existsSync(path.join(cwd, '.mcp.json')) ||
fs.existsSync(path.join(cwd, '.mcp.local.json'))) {
workspaceDir = cwd;
}
else {
let current = cwd;
while (path.dirname(current) !== current) {
if (fs.existsSync(path.join(current, '.mcp.json')) ||
fs.existsSync(path.join(current, '.mcp.local.json'))) {
workspaceDir = current;
break;
}
current = path.dirname(current);
}
if (!workspaceDir) {
const codeRoot = path.resolve(__dirname, '../..');
if (fs.existsSync(path.join(codeRoot, '.mcp.json')) ||
fs.existsSync(path.join(codeRoot, '.mcp.local.json'))) {
workspaceDir = codeRoot;
}
else {
const homedir = os.homedir();
workspaceDir = path.join(homedir, '.overmind');
try {
if (!fs.existsSync(workspaceDir)) {
fs.mkdirSync(workspaceDir, { recursive: true });
}
}
catch {
workspaceDir = cwd;
}
}
}
}
}
// Load environment from workspace and related projects
loadEnvQuietly(path.join(workspaceDir, '.env'));
// Allow an optional external .env to be loaded via env var (prevents hardcoded path assumptions)
const externalEnvPath = process.env.OVERMIND_EXTERNAL_ENV_PATH;
if (externalEnvPath) {
loadEnvQuietly(path.resolve(externalEnvPath));
}
cachedWorkspaceDir = workspaceDir;
return workspaceDir;
}
// NOTE: getWorkspaceDir() is NOT called automatically at import time.
// It is called lazily on first access (see getWorkspaceDir() body above).
// This prevents the cache from being polluted by premature cwd resolution
// when the module is imported by an MCP server before OVERMIND_WORKSPACE is set.
// Callers MUST call resetWorkspaceCache() if OVERMIND_WORKSPACE changes.
export function resolveConfigPath(configPath, workspaceDirOverride) {
if (path.isAbsolute(configPath))
return configPath;
const workspaceDir = workspaceDirOverride || getWorkspaceDir();
const fullPath = path.resolve(workspaceDir, configPath);
// Prevent path traversal beyond workspace
if (!fullPath.startsWith(path.resolve(workspaceDir))) {
throw new Error(`Path traversal detected: ${configPath} resolves outside workspace`);
}
// Special handling for MCP config to support .local variant
if (configPath === '.mcp.json' && !fs.existsSync(fullPath)) {
const localPath = path.resolve(workspaceDir, '.mcp.local.json');
if (fs.existsSync(localPath))
return localPath;
}
return fullPath;
}
export function updateConfig(newSettingsPath, newMcpPath) {
if (newSettingsPath)
CONFIG.CLAUDE.PATHS.SETTINGS = newSettingsPath;
if (newMcpPath)
CONFIG.CLAUDE.PATHS.MCP = newMcpPath;
}
/**
* Resolve the canonical Hermes *profile home* directory for an Overmind agent.
*
* v3.1 architecture:
* ~/.overmind/hermes/profiles/<name>/
*
* Resolution order:
* 1. OVERMIND_HERMES_HOME env var (operator override)
* 2. ~/.overmind/hermes/profiles/<name>/ (canonical v3.1)
*/
export function getAgentHermesHome(agentName) {
const base = getSharedHermesHome();
const name = agentName || 'central';
return path.join(base, 'profiles', name);
}
/**
* The SHARED Hermes home for this Overmind install.
* v3.1: ~/.overmind/hermes/
*/
export function getSharedHermesHome() {
// 1. Explicit operator override
if (process.env.OVERMIND_HERMES_HOME) {
return process.env.OVERMIND_HERMES_HOME;
}
// 2. Canonical: ~/.overmind/hermes/
const homeBase = process.env.LOCALAPPDATA || process.env.USERPROFILE || os.homedir();
const overmindRoot = process.platform === 'win32'
? path.join(homeBase, 'overmind', 'hermes')
: path.join(homeBase, '.overmind', 'hermes');
try {
fs.mkdirSync(overmindRoot, { recursive: true });
}
catch {
return process.env.HOME || process.env.USERPROFILE || process.cwd();
}
return overmindRoot;
}
//# sourceMappingURL=config.js.map