revit-cli
Version:
A scalable CLI tool for Revit communication and data manipulation
438 lines • 18 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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const chalk_1 = __importDefault(require("chalk"));
const plugin_json_1 = __importDefault(require("./plugin.json"));
/**
* Extract elements tool
*/
const extractTool = {
name: 'extract',
description: 'Extract elements from Revit model',
category: 'data',
options: [
{
flags: '-c, --categories <categories>',
description: 'Comma-separated list of categories to extract'
},
{
flags: '-p, --parameters <parameters>',
description: 'Comma-separated list of parameters to include'
},
{
flags: '-g, --geometry',
description: 'Include geometry data',
defaultValue: false
},
{
flags: '-f, --format <format>',
description: 'Output format (json, csv, xml)',
defaultValue: 'json'
},
{
flags: '-o, --output <path>',
description: 'Output file path'
}
],
async execute(_options, context) {
const { logger, revitConnector } = context;
try {
logger.header('ELEMENT EXTRACTION DEBUG');
logger.debug('=== EXTRACT TOOL EXECUTION START ===');
logger.debug('Raw options received:', _options);
logger.debug('Context available:', {
hasLogger: !!logger,
hasRevitConnector: !!revitConnector,
loggerLevel: logger.getLevel(),
loggerFile: logger.getLogFile()
});
logger.info('Starting element extraction...');
// Validate required options
logger.debug('Validating extraction options...');
if (!_options['categories'] && !_options['all']) {
logger.error('Validation failed: Either --categories or --all flag is required');
throw new Error('Either --categories or --all flag is required');
}
logger.debug('Options validation passed');
// Build extraction options
logger.debug('Building extraction options...');
const extractionOptions = {
categories: _options['categories'] ? _options['categories'].split(',').map((c) => c.trim()) : undefined,
parameters: _options['parameters'] ? _options['parameters'].split(',').map((p) => p.trim()) : undefined,
includeGeometry: _options['geometry'] || false,
limit: _options['limit'] ? parseInt(_options['limit']) : undefined
};
logger.info('Extraction options built:', extractionOptions);
logger.debug('Categories to extract:', extractionOptions.categories);
logger.debug('Parameters to include:', extractionOptions.parameters);
logger.debug('Include geometry:', extractionOptions.includeGeometry);
logger.debug('Element limit:', extractionOptions.limit);
// Test connection before extraction
logger.debug('Testing Revit connection before extraction...');
const connectionStatus = await revitConnector.getConnectionStatus();
logger.debug('Connection status:', connectionStatus);
if (!connectionStatus.connected) {
logger.error('Revit connection failed:', connectionStatus);
throw new Error(`Cannot connect to Revit: ${connectionStatus.error || 'Unknown error'}`);
}
logger.debug('Revit connection verified successfully');
// Extract elements
logger.debug('Calling revitConnector.extractElements...');
const startTime = Date.now();
const elements = await revitConnector.extractElements(extractionOptions);
const endTime = Date.now();
logger.debug(`Element extraction completed in ${endTime - startTime}ms`);
logger.debug(`Extracted ${elements?.length || 0} elements`);
if (elements && elements.length > 0) {
logger.debug('Sample extracted element:', elements[0]);
}
// Handle output
logger.debug('Processing output options...');
if (_options['output']) {
logger.debug('Saving to file:', _options['output']);
const format = _options['format'] || 'json';
logger.debug('Output format:', format);
await saveExtractedData(elements, _options['output'], format, logger);
}
else {
logger.debug('Displaying results to console');
// Display summary or full data
if (_options['summary']) {
logger.debug('Showing summary view');
displayExtractionSummary(elements);
}
else {
logger.debug('Showing full data');
console.log(JSON.stringify(elements, null, 2));
}
}
logger.success(`Successfully extracted ${elements.length} elements`);
logger.debug('=== EXTRACT TOOL EXECUTION END ===');
}
catch (error) {
logger.error('=== EXTRACT TOOL EXECUTION FAILED ===');
logger.error('Error details:', {
message: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
name: error instanceof Error ? error.name : undefined
});
logger.error('Element extraction failed:', error);
throw error;
}
}
};
/**
* List categories tool
*/
const listCategoriesTool = {
name: 'categories',
description: 'List all available Revit categories',
category: 'info',
async execute(_options, context) {
const { logger, revitConnector } = context;
try {
logger.header('CATEGORIES LIST DEBUG');
logger.debug('=== CATEGORIES TOOL EXECUTION START ===');
logger.debug('Raw options received:', _options);
logger.debug('Context available:', {
hasLogger: !!logger,
hasRevitConnector: !!revitConnector,
loggerLevel: logger.getLevel(),
loggerFile: logger.getLogFile()
});
logger.info('Fetching Revit categories...');
// Test connection before fetching categories
logger.debug('Testing Revit connection before fetching categories...');
const connectionStatus = await revitConnector.getConnectionStatus();
logger.debug('Connection status:', connectionStatus);
if (!connectionStatus.connected) {
logger.error('Revit connection failed:', connectionStatus);
throw new Error(`Cannot connect to Revit: ${connectionStatus.error || 'Unknown error'}`);
}
logger.debug('Revit connection verified successfully');
logger.debug('Calling revitConnector.getCategories...');
const startTime = Date.now();
const result = await revitConnector.getCategories();
const endTime = Date.now();
logger.debug(`Categories fetch completed in ${endTime - startTime}ms`);
if (!result.success) {
logger.error('Categories fetch failed:', result.error);
throw new Error(result.error || 'Failed to get categories');
}
const categories = result.data || [];
logger.debug(`Retrieved ${categories?.length || 0} categories`);
if (categories && categories.length > 0) {
logger.debug('Sample category:', categories[0]);
logger.debug('All categories:', categories);
}
console.log(chalk_1.default.blue('\nAvailable Categories:'));
// Handle both string array and object array formats
if (categories.length > 0) {
const categoryNames = categories.map((category, index) => {
// If category is an object with name property, extract the name
if (typeof category === 'object' && category.name) {
const categoryName = category.name;
const elementCount = category.elementCount || 0;
console.log(` ${index + 1}. ${chalk_1.default.green(categoryName)} ${chalk_1.default.gray(`(${elementCount} elements)`)}`);
return categoryName;
}
// If category is a string, use it directly
else if (typeof category === 'string') {
console.log(` ${index + 1}. ${chalk_1.default.green(category)}`);
return category;
}
return null;
}).filter(Boolean);
logger.success(`Found ${categoryNames.length} categories`);
}
else {
logger.success('Found 0 categories');
}
logger.debug('=== CATEGORIES TOOL EXECUTION END ===');
}
catch (error) {
logger.error('=== CATEGORIES TOOL EXECUTION FAILED ===');
logger.error('Error details:', {
message: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
name: error instanceof Error ? error.name : undefined
});
logger.error('Failed to list categories:', error);
throw error;
}
}
};
/**
* Project info tool
*/
const projectInfoTool = {
name: 'info',
description: 'Get Revit project information',
category: 'info',
async execute(_options, context) {
const { logger, revitConnector } = context;
try {
logger.info('Fetching project information...');
const result = await revitConnector.getProjectInfo();
if (!result.success) {
throw new Error(result.error || 'Failed to get project info');
}
const projectInfo = result.data;
console.log(chalk_1.default.blue('\nProject Information:'));
console.log(JSON.stringify(projectInfo, null, 2));
logger.success('Project information retrieved successfully');
}
catch (error) {
logger.error('Failed to get project info:', error);
throw error;
}
}
};
/**
* Parse file tool
*/
const parseTool = {
name: 'parse',
description: 'Parse and analyze Revit data files',
category: 'data',
options: [
{
flags: '-f, --file <path>',
description: 'Path to the file to parse'
},
{
flags: '-t, --type <type>',
description: 'File type (json, csv, xml)',
defaultValue: 'json'
},
{
flags: '-s, --summary',
description: 'Show summary statistics',
defaultValue: false
}
],
async execute(_options, context) {
const { logger } = context;
try {
if (!_options['file']) {
throw new Error('File path is required. Use -f or --file option.');
}
const filePath = path.resolve(_options['file']);
if (!await fs.pathExists(filePath)) {
throw new Error(`File not found: ${filePath}`);
}
logger.info(`Parsing file: ${filePath}`);
const fileContent = await fs.readFile(filePath, 'utf-8');
let data;
// Parse based on file type
switch (_options['type'].toLowerCase()) {
case 'json':
data = JSON.parse(fileContent);
break;
case 'csv':
// Simple CSV parsing (for demo purposes)
const lines = fileContent.split('\n');
const headers = lines[0]?.split(',') || [];
data = lines.slice(1).map(line => {
const values = line.split(',');
const obj = {};
headers.forEach((header, index) => {
obj[header.trim()] = values[index]?.trim();
});
return obj;
});
break;
default:
throw new Error(`Unsupported file type: ${_options['type']}`);
}
if (_options['summary']) {
displayDataSummary(data);
}
else {
console.log(chalk_1.default.blue('\nParsed Data:'));
console.log(JSON.stringify(data, null, 2));
}
logger.success('File parsed successfully');
}
catch (error) {
logger.error('Failed to parse file:', error);
throw error;
}
}
};
/**
* Helper function to save extracted data
*/
async function saveExtractedData(data, outputPath, format, logger) {
const resolvedPath = path.resolve(outputPath);
await fs.ensureDir(path.dirname(resolvedPath));
let content;
switch (format.toLowerCase()) {
case 'json':
content = JSON.stringify(data, null, 2);
break;
case 'csv':
if (data.length === 0) {
content = '';
}
else {
const headers = Object.keys(data[0] || {});
const csvRows = [headers.join(',')];
data.forEach(item => {
const row = headers.map(header => {
const value = item[header];
return typeof value === 'string' && value.includes(',') ? `"${value}"` : value;
});
csvRows.push(row.join(','));
});
content = csvRows.join('\n');
}
break;
default:
throw new Error(`Unsupported format: ${format}`);
}
await fs.writeFile(resolvedPath, content, 'utf-8');
logger.success(`Data saved to: ${resolvedPath}`);
}
/**
* Helper function to display extraction summary
*/
function displayExtractionSummary(elements) {
console.log(chalk_1.default.blue('\nExtraction Summary:'));
console.log(` Total Elements: ${chalk_1.default.green(elements.length)}`);
if (elements.length > 0) {
// Group by category
const categoryCount = {};
elements.forEach(element => {
const category = element.category || 'Unknown';
categoryCount[category] = (categoryCount[category] || 0) + 1;
});
console.log('\n By Category:');
Object.entries(categoryCount).forEach(([category, count]) => {
console.log(` ${category}: ${chalk_1.default.yellow(count)}`);
});
// Show sample element
console.log('\n Sample Element:');
console.log(JSON.stringify(elements[0], null, 4));
}
}
/**
* Helper function to display data summary
*/
function displayDataSummary(data) {
console.log(chalk_1.default.blue('\nData Summary:'));
if (Array.isArray(data)) {
console.log(` Type: Array`);
console.log(` Length: ${chalk_1.default.green(data.length)}`);
if (data.length > 0) {
console.log(` Sample Item:`);
console.log(JSON.stringify(data[0], null, 4));
}
}
else if (typeof data === 'object' && data !== null) {
console.log(` Type: Object`);
console.log(` Keys: ${chalk_1.default.green(Object.keys(data).length)}`);
console.log(` Properties: ${Object.keys(data).join(', ')}`);
}
else {
console.log(` Type: ${typeof data}`);
console.log(` Value: ${data}`);
}
}
/**
* Core plugin definition
*/
const corePlugin = {
metadata: plugin_json_1.default,
tools: [
extractTool,
listCategoriesTool,
projectInfoTool,
parseTool
],
async initialize(context) {
context.logger.debug('Core plugin initialized');
},
async cleanup() {
// Cleanup if needed
}
};
exports.default = corePlugin;
//# sourceMappingURL=index.js.map