@sirmrmarty/n8n-nodes-tmux-orchestrator
Version:
n8n nodes for orchestrating Claude AI agents through tmux sessions
342 lines • 14.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.PathResolver = void 0;
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const fs_1 = require("fs");
class PathResolver {
constructor() {
this.packageRoot = path.resolve(__dirname, '../..');
}
async getScriptPath(scriptName, externalDir) {
if (!scriptName || typeof scriptName !== 'string') {
throw new Error('Invalid script name provided');
}
if (externalDir && typeof externalDir !== 'string') {
throw new Error('Invalid external directory provided');
}
const checkedLocations = [];
if (externalDir) {
const externalPath = path.join(externalDir, scriptName);
checkedLocations.push(`External: ${externalPath}`);
if (await this.fileExistsAsync(externalPath) && await this.isScriptAvailableAsync(externalPath)) {
return externalPath;
}
}
const bundledPath = path.join(this.packageRoot, 'scripts', scriptName);
checkedLocations.push(`Bundled: ${bundledPath}`);
if (await this.fileExistsAsync(bundledPath) && await this.isScriptAvailableAsync(bundledPath)) {
return bundledPath;
}
const utilsPath = path.join(this.packageRoot, 'utils', scriptName);
checkedLocations.push(`Utils: ${utilsPath}`);
if (await this.fileExistsAsync(utilsPath) && await this.isScriptAvailableAsync(utilsPath)) {
return utilsPath;
}
const npmPackagePath = await this.findInNodeModulesAsync(scriptName);
if (npmPackagePath) {
checkedLocations.push(`NPM Package: ${npmPackagePath}`);
if (await this.fileExistsAsync(npmPackagePath) && await this.isScriptAvailableAsync(npmPackagePath)) {
return npmPackagePath;
}
}
const fallbacks = [
process.env.TMUX_ORCHESTRATOR_PATH,
process.env.TMUX_SCRIPTS_PATH,
path.join(process.env.HOME || '', 'n8n_claude_tmux', 'scripts'),
path.join(process.env.HOME || '', 'n8n_claude_tmux', 'Tmux-Orchestrator'),
path.join(process.env.HOME || '', 'n8n_claude_tmux'),
'/usr/local/share/tmux-orchestrator/scripts',
'/usr/local/share/tmux-orchestrator',
'/opt/tmux-orchestrator/scripts'
].filter(Boolean);
for (const dir of fallbacks) {
const fallbackPath = path.join(dir, scriptName);
checkedLocations.push(`Fallback: ${fallbackPath}`);
if (await this.fileExistsAsync(fallbackPath) && await this.isScriptAvailableAsync(fallbackPath)) {
return fallbackPath;
}
}
throw new Error(`Script ${scriptName} not found. Checked locations:\n${checkedLocations.map(loc => ` - ${loc}`).join('\n')}\n\nTroubleshooting:\n - Ensure tmux and python3 are installed\n - Set TMUX_ORCHESTRATOR_PATH environment variable to script directory\n - Check that scripts have execute permissions\n - Verify n8n node installation is complete`);
}
getProjectBasePath(configuredPath) {
if (configuredPath) {
if (configuredPath.startsWith('~')) {
return path.join(process.env.HOME || '', configuredPath.slice(1));
}
return configuredPath;
}
if (process.env.TMUX_PROJECTS_PATH) {
return process.env.TMUX_PROJECTS_PATH;
}
return path.join(process.env.HOME || '', 'Coding');
}
async findInNodeModulesAsync(scriptName) {
let currentDir = __dirname;
for (let i = 0; i < 10; i++) {
const nodeModulesPath = path.join(currentDir, 'node_modules', '@sirmrmarty', 'n8n-nodes-tmux-orchestrator', 'scripts', scriptName);
if (await this.fileExistsAsync(nodeModulesPath)) {
return nodeModulesPath;
}
const nodeModulesUtilsPath = path.join(currentDir, 'node_modules', '@sirmrmarty', 'n8n-nodes-tmux-orchestrator', 'utils', scriptName);
if (await this.fileExistsAsync(nodeModulesUtilsPath)) {
return nodeModulesUtilsPath;
}
const parentDir = path.dirname(currentDir);
if (parentDir === currentDir)
break;
currentDir = parentDir;
}
return null;
}
findInNodeModules(scriptName) {
let currentDir = __dirname;
for (let i = 0; i < 10; i++) {
const nodeModulesPath = path.join(currentDir, 'node_modules', '@sirmrmarty', 'n8n-nodes-tmux-orchestrator', 'scripts', scriptName);
if (fs.existsSync(nodeModulesPath)) {
return nodeModulesPath;
}
const nodeModulesUtilsPath = path.join(currentDir, 'node_modules', '@sirmrmarty', 'n8n-nodes-tmux-orchestrator', 'utils', scriptName);
if (fs.existsSync(nodeModulesUtilsPath)) {
return nodeModulesUtilsPath;
}
const parentDir = path.dirname(currentDir);
if (parentDir === currentDir)
break;
currentDir = parentDir;
}
return null;
}
async isScriptAvailableAsync(scriptPath) {
try {
const stats = await fs_1.promises.stat(scriptPath);
return stats.isFile() && ((stats.mode & 0o100) !== 0 || (stats.mode & 0o400) !== 0);
}
catch {
return false;
}
}
isScriptAvailable(scriptPath) {
try {
const stats = fs.statSync(scriptPath);
return stats.isFile() && ((stats.mode & 0o100) !== 0 || (stats.mode & 0o400) !== 0);
}
catch {
return false;
}
}
async getAllScriptPathsAsync(externalDir) {
const scripts = [
'tmux_wrapper.py',
'schedule_with_note.sh',
'send-claude-message.sh',
'suggest_subagent.sh'
];
const paths = {};
for (const script of scripts) {
try {
paths[script] = await this.getScriptPath(script, externalDir);
}
catch {
paths[script] = null;
}
}
return paths;
}
getAllScriptPaths(externalDir) {
const scripts = [
'tmux_wrapper.py',
'schedule_with_note.sh',
'send-claude-message.sh',
'suggest_subagent.sh'
];
const paths = {};
for (const script of scripts) {
try {
paths[script] = this.getScriptPathSync(script, externalDir);
}
catch {
paths[script] = null;
}
}
return paths;
}
isPathSafe(targetPath, basePath) {
try {
const normalizedTarget = path.resolve(path.normalize(targetPath));
const normalizedBase = path.resolve(path.normalize(basePath));
if (!normalizedTarget.startsWith(normalizedBase + path.sep) && normalizedTarget !== normalizedBase) {
return false;
}
return this.validatePathSecurity(targetPath, normalizedTarget);
}
catch {
return false;
}
}
validatePathSecurity(originalPath, resolvedPath) {
const dangerousPatterns = [
/\.\.[\/\\]/,
/[\/\\]\.\./,
/\.\.$/,
/^\.\.[\/\\]/,
/\0/,
/[\r\n]/,
/[<>:"|?*]/
];
for (const pattern of dangerousPatterns) {
if (pattern.test(originalPath)) {
return false;
}
}
const suspiciousPaths = [
'/etc/',
'/usr/bin/',
'/bin/',
'/sbin/',
'/var/log/',
'/proc/',
'/sys/'
];
for (const suspiciousPath of suspiciousPaths) {
if (resolvedPath.startsWith(suspiciousPath)) {
return false;
}
}
if (resolvedPath.length > 4096) {
return false;
}
return true;
}
validateAndResolvePath(projectPath, relativePath) {
if (!projectPath || !relativePath) {
throw new Error('Path validation requires both project path and relative path');
}
const absoluteProjectPath = path.resolve(projectPath);
const targetPath = path.resolve(absoluteProjectPath, relativePath);
if (!this.isPathSafe(targetPath, absoluteProjectPath)) {
throw new Error(`Path traversal detected: ${relativePath} resolves outside project boundary`);
}
return targetPath;
}
async safeCreateDirectoryAsync(projectPath, relativePath) {
const safePath = this.validateAndResolvePath(projectPath, relativePath);
const parentDir = path.dirname(safePath);
try {
await fs_1.promises.access(parentDir);
}
catch {
if (!this.isPathSafe(parentDir, path.resolve(projectPath))) {
throw new Error('Cannot create directory: parent directory outside project bounds');
}
await fs_1.promises.mkdir(parentDir, { recursive: true, mode: 0o755 });
}
return safePath;
}
safeCreateDirectory(projectPath, relativePath) {
const safePath = this.validateAndResolvePath(projectPath, relativePath);
const parentDir = path.dirname(safePath);
if (!fs.existsSync(parentDir)) {
if (!this.isPathSafe(parentDir, path.resolve(projectPath))) {
throw new Error('Cannot create directory: parent directory outside project bounds');
}
fs.mkdirSync(parentDir, { recursive: true, mode: 0o755 });
}
return safePath;
}
async fileExistsAsync(filePath) {
try {
await fs_1.promises.access(filePath);
return true;
}
catch {
return false;
}
}
getScriptPathSync(scriptName, externalDir) {
if (!scriptName || typeof scriptName !== 'string') {
throw new Error('Invalid script name provided');
}
if (externalDir && typeof externalDir !== 'string') {
throw new Error('Invalid external directory provided');
}
const checkedLocations = [];
if (externalDir) {
const externalPath = path.join(externalDir, scriptName);
checkedLocations.push(`External: ${externalPath}`);
if (fs.existsSync(externalPath) && this.isScriptAvailable(externalPath)) {
return externalPath;
}
}
const bundledPath = path.join(this.packageRoot, 'scripts', scriptName);
checkedLocations.push(`Bundled: ${bundledPath}`);
if (fs.existsSync(bundledPath) && this.isScriptAvailable(bundledPath)) {
return bundledPath;
}
const utilsPath = path.join(this.packageRoot, 'utils', scriptName);
checkedLocations.push(`Utils: ${utilsPath}`);
if (fs.existsSync(utilsPath) && this.isScriptAvailable(utilsPath)) {
return utilsPath;
}
const npmPackagePath = this.findInNodeModules(scriptName);
if (npmPackagePath) {
checkedLocations.push(`NPM Package: ${npmPackagePath}`);
if (fs.existsSync(npmPackagePath) && this.isScriptAvailable(npmPackagePath)) {
return npmPackagePath;
}
}
const fallbacks = [
process.env.TMUX_ORCHESTRATOR_PATH,
process.env.TMUX_SCRIPTS_PATH,
path.join(process.env.HOME || '', 'n8n_claude_tmux', 'scripts'),
path.join(process.env.HOME || '', 'n8n_claude_tmux', 'Tmux-Orchestrator'),
path.join(process.env.HOME || '', 'n8n_claude_tmux'),
'/usr/local/share/tmux-orchestrator/scripts',
'/usr/local/share/tmux-orchestrator',
'/opt/tmux-orchestrator/scripts'
].filter(Boolean);
for (const dir of fallbacks) {
const fallbackPath = path.join(dir, scriptName);
checkedLocations.push(`Fallback: ${fallbackPath}`);
if (fs.existsSync(fallbackPath) && this.isScriptAvailable(fallbackPath)) {
return fallbackPath;
}
}
throw new Error(`Script ${scriptName} not found. Checked locations:\n${checkedLocations.map(loc => ` - ${loc}`).join('\n')}\n\nTroubleshooting:\n - Ensure tmux and python3 are installed\n - Set TMUX_ORCHESTRATOR_PATH environment variable to script directory\n - Check that scripts have execute permissions\n - Verify n8n node installation is complete`);
}
}
exports.PathResolver = PathResolver;
//# sourceMappingURL=paths.js.map