mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
449 lines
15 KiB
JavaScript
449 lines
15 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* OpenClaw Framework Formatter
|
|
*
|
|
* Formats discovered tools into OpenClaw-compliant documentation
|
|
* with professional presentation, examples, and usage guidelines.
|
|
*/
|
|
|
|
/**
|
|
* Environment configurations for OpenClaw compliance
|
|
*/
|
|
export const ENVIRONMENTS = {
|
|
development: {
|
|
name: 'Development',
|
|
description: 'Local development environment with full access',
|
|
accessLevel: 'Full access to all tools',
|
|
rateLimits: 'Relaxed for testing',
|
|
authentication: 'Development credentials',
|
|
logging: 'Verbose logging enabled',
|
|
},
|
|
production: {
|
|
name: 'Production',
|
|
description: 'Production environment with security restrictions',
|
|
accessLevel: 'Production-safe tools only',
|
|
rateLimits: 'Standard API limits enforced',
|
|
authentication: 'Production credentials required',
|
|
logging: 'Essential logs only',
|
|
},
|
|
testing: {
|
|
name: 'Testing',
|
|
description: 'Testing environment for automated validation',
|
|
accessLevel: 'Safe tools for automated testing',
|
|
rateLimits: 'Testing-specific limits',
|
|
authentication: 'Test credentials',
|
|
logging: 'Test execution logs',
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Tool categories for better organization
|
|
*/
|
|
export const TOOL_CATEGORIES = {
|
|
communication: {
|
|
name: 'Communication',
|
|
description: 'Tools for messaging, email, and social interaction',
|
|
skills: ['telegram', 'gmail', 'discord', 'slack'],
|
|
},
|
|
productivity: {
|
|
name: 'Productivity',
|
|
description: 'Tools for task management, note-taking, and organization',
|
|
skills: ['notion', 'todoist', 'calendar', 'trello'],
|
|
},
|
|
automation: {
|
|
name: 'Automation',
|
|
description: 'Tools for workflow automation and task scheduling',
|
|
skills: ['zapier', 'ifttt', 'scheduler', 'webhook'],
|
|
},
|
|
data: {
|
|
name: 'Data & Analytics',
|
|
description: 'Tools for data processing, analysis, and storage',
|
|
skills: ['database', 'csv', 'json', 'analytics'],
|
|
},
|
|
media: {
|
|
name: 'Media & Content',
|
|
description: 'Tools for image, video, and content processing',
|
|
skills: ['image', 'video', 'audio', 'pdf'],
|
|
},
|
|
utility: {
|
|
name: 'Utilities',
|
|
description: 'General-purpose utility tools and helpers',
|
|
skills: ['file', 'text', 'crypto', 'converter'],
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Converts JSON Schema to markdown parameter documentation
|
|
* @param {Object} schema - JSON Schema object
|
|
* @returns {string} Formatted markdown for parameters
|
|
*/
|
|
export function formatParameters(schema) {
|
|
if (!schema || !schema.properties || Object.keys(schema.properties).length === 0) {
|
|
return '- *None*';
|
|
}
|
|
|
|
const params = [];
|
|
const required = schema.required || [];
|
|
|
|
for (const [name, prop] of Object.entries(schema.properties)) {
|
|
const isRequired = required.includes(name);
|
|
const requiredMark = isRequired ? ' **(required)**' : '';
|
|
const type = prop.type || 'any';
|
|
const description = prop.description || 'No description available';
|
|
|
|
let paramLine = `- **${name}** (${type})${requiredMark}: ${description}`;
|
|
|
|
// Add enum values if present
|
|
if (prop.enum) {
|
|
paramLine += ` Options: ${prop.enum.map(v => `\`${v}\``).join(', ')}`;
|
|
}
|
|
|
|
// Add format information
|
|
if (prop.format) {
|
|
paramLine += ` (Format: ${prop.format})`;
|
|
}
|
|
|
|
// Add constraints
|
|
if (prop.minLength || prop.maxLength) {
|
|
const constraints = [];
|
|
if (prop.minLength) constraints.push(`min: ${prop.minLength}`);
|
|
if (prop.maxLength) constraints.push(`max: ${prop.maxLength}`);
|
|
paramLine += ` [${constraints.join(', ')}]`;
|
|
}
|
|
|
|
params.push(paramLine);
|
|
}
|
|
|
|
return params.join('\n');
|
|
}
|
|
|
|
/**
|
|
* Generates example usage for a tool
|
|
* @param {Object} tool - Tool definition
|
|
* @returns {string} Formatted example
|
|
*/
|
|
export function generateToolExample(tool) {
|
|
const params = {};
|
|
const schema = tool.inputSchema;
|
|
|
|
if (schema && schema.properties) {
|
|
// Generate example values for the first few parameters
|
|
for (const [name, prop] of Object.entries(schema.properties)) {
|
|
if (Object.keys(params).length >= 3) break; // Limit to 3 params for brevity
|
|
|
|
let exampleValue;
|
|
switch (prop.type) {
|
|
case 'string':
|
|
exampleValue = prop.enum ? prop.enum[0] : `example_${name}`;
|
|
break;
|
|
case 'number':
|
|
exampleValue = prop.default || 10;
|
|
break;
|
|
case 'boolean':
|
|
exampleValue = prop.default || true;
|
|
break;
|
|
case 'array':
|
|
exampleValue = [];
|
|
break;
|
|
case 'object':
|
|
exampleValue = {};
|
|
break;
|
|
default:
|
|
exampleValue = `example_${name}`;
|
|
}
|
|
|
|
params[name] = exampleValue;
|
|
}
|
|
}
|
|
|
|
return JSON.stringify({ tool: tool.name, parameters: params }, null, 2);
|
|
}
|
|
|
|
/**
|
|
* Groups tools by skill for better organization
|
|
* @param {Array} tools - Array of tool objects
|
|
* @returns {Object} Grouped tools by skill
|
|
*/
|
|
export function groupToolsBySkill(tools) {
|
|
const grouped = {};
|
|
|
|
for (const tool of tools) {
|
|
const skillId = tool.skillId;
|
|
if (!grouped[skillId]) {
|
|
grouped[skillId] = {
|
|
skillId,
|
|
name: formatSkillName(skillId),
|
|
category: categorizeSkill(skillId),
|
|
tools: [],
|
|
};
|
|
}
|
|
grouped[skillId].tools.push(tool);
|
|
}
|
|
|
|
return grouped;
|
|
}
|
|
|
|
/**
|
|
* Formats skill names for display
|
|
* @param {string} skillId - Skill identifier
|
|
* @returns {string} Formatted name
|
|
*/
|
|
function formatSkillName(skillId) {
|
|
return skillId
|
|
.split(/[-_]/)
|
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join(' ');
|
|
}
|
|
|
|
/**
|
|
* Categorizes a skill based on its ID
|
|
* @param {string} skillId - Skill identifier
|
|
* @returns {string} Category name
|
|
*/
|
|
function categorizeSkill(skillId) {
|
|
for (const [category, config] of Object.entries(TOOL_CATEGORIES)) {
|
|
if (config.skills.some(skill => skillId.includes(skill))) {
|
|
return category;
|
|
}
|
|
}
|
|
return 'utility';
|
|
}
|
|
|
|
/**
|
|
* Generates environment configuration section
|
|
* @returns {string} Environment documentation
|
|
*/
|
|
export function generateEnvironmentSection() {
|
|
let section = '## Environment Configuration\n\n';
|
|
section += 'Tools are available in different environments with varying capabilities:\n\n';
|
|
|
|
for (const [envId, env] of Object.entries(ENVIRONMENTS)) {
|
|
section += `### ${env.name} Environment\n\n`;
|
|
section += `${env.description}\n\n`;
|
|
section += `- **Access Level**: ${env.accessLevel}\n`;
|
|
section += `- **Rate Limits**: ${env.rateLimits}\n`;
|
|
section += `- **Authentication**: ${env.authentication}\n`;
|
|
section += `- **Logging**: ${env.logging}\n\n`;
|
|
}
|
|
|
|
return section;
|
|
}
|
|
|
|
/**
|
|
* Generates tool categories section
|
|
* @param {Object} groupedTools - Tools grouped by skill
|
|
* @returns {string} Categories documentation
|
|
*/
|
|
export function generateCategoriesSection(groupedTools) {
|
|
const categoryCounts = {};
|
|
|
|
// Count tools by category
|
|
for (const skill of Object.values(groupedTools)) {
|
|
const category = skill.category;
|
|
if (!categoryCounts[category]) {
|
|
categoryCounts[category] = { skills: [], toolCount: 0 };
|
|
}
|
|
categoryCounts[category].skills.push(skill.skillId);
|
|
categoryCounts[category].toolCount += skill.tools.length;
|
|
}
|
|
|
|
let section = '## Tool Categories\n\n';
|
|
|
|
for (const [categoryId, categoryConfig] of Object.entries(TOOL_CATEGORIES)) {
|
|
const counts = categoryCounts[categoryId];
|
|
if (!counts) continue;
|
|
|
|
section += `### ${categoryConfig.name}\n\n`;
|
|
section += `${categoryConfig.description}\n\n`;
|
|
section += `- **Skills**: ${counts.skills.length}\n`;
|
|
section += `- **Tools**: ${counts.toolCount}\n`;
|
|
section += `- **Available Skills**: ${counts.skills.map(formatSkillName).join(', ')}\n\n`;
|
|
}
|
|
|
|
return section;
|
|
}
|
|
|
|
/**
|
|
* Generates complete tools section with skills and tools
|
|
* @param {Object} groupedTools - Tools grouped by skill
|
|
* @returns {string} Tools documentation
|
|
*/
|
|
export function generateToolsSection(groupedTools) {
|
|
const skillNames = Object.keys(groupedTools).sort();
|
|
|
|
let section = '## Available Tools\n\n';
|
|
|
|
for (const skillId of skillNames) {
|
|
const skill = groupedTools[skillId];
|
|
const categoryConfig = TOOL_CATEGORIES[skill.category];
|
|
|
|
section += `### ${skill.name} Tools\n\n`;
|
|
|
|
if (categoryConfig) {
|
|
section += `**Category**: ${categoryConfig.name}\n\n`;
|
|
}
|
|
|
|
section += `This skill provides ${skill.tools.length} tool${skill.tools.length === 1 ? '' : 's'} for ${skillId} integration.\n\n`;
|
|
|
|
for (const tool of skill.tools) {
|
|
section += `#### ${tool.name}\n\n`;
|
|
section += `**Description**: ${tool.description}\n\n`;
|
|
section += `**Parameters**:\n${formatParameters(tool.inputSchema)}\n\n`;
|
|
section += `**Usage Context**: Available in all environments\n\n`;
|
|
section += `**Example**:\n\`\`\`json\n${generateToolExample(tool)}\n\`\`\`\n\n`;
|
|
section += '---\n\n';
|
|
}
|
|
}
|
|
|
|
return section;
|
|
}
|
|
|
|
/**
|
|
* Generates usage guidelines section
|
|
* @returns {string} Guidelines documentation
|
|
*/
|
|
export function generateGuidelinesSection() {
|
|
return `## Tool Usage Guidelines
|
|
|
|
### Authentication
|
|
- All tools require proper authentication setup through the Skills system
|
|
- OAuth credentials are managed securely and refreshed automatically
|
|
- API keys are stored encrypted in the application keychain
|
|
- Test credentials are available for development and testing environments
|
|
|
|
### Rate Limiting
|
|
- Tools automatically respect API rate limits of external services
|
|
- Intelligent retry logic handles temporary failures with exponential backoff
|
|
- Bulk operations are automatically chunked to avoid hitting limits
|
|
- Rate limit status is monitored and reported in real-time
|
|
|
|
### Error Handling
|
|
- All tools return structured error responses with detailed information
|
|
- Network failures trigger automatic retry with configurable attempts
|
|
- Invalid parameters return clear validation messages with examples
|
|
- Tool execution timeouts are handled gracefully with partial results
|
|
|
|
### Security & Privacy
|
|
- Input validation is performed on all parameters using JSON Schema
|
|
- Output sanitization prevents injection attacks and data leakage
|
|
- Sensitive data is never logged or exposed in error messages
|
|
- All API communications use secure protocols (HTTPS/TLS)
|
|
|
|
### Performance Optimization
|
|
- Tool results are cached when appropriate to reduce API calls
|
|
- Parallel execution is used for independent operations
|
|
- Connection pooling minimizes overhead for repeated API calls
|
|
- Background sync keeps data fresh without blocking operations
|
|
|
|
### Monitoring & Observability
|
|
- Tool execution metrics are collected for performance analysis
|
|
- Error rates and response times are monitored continuously
|
|
- Debug logging is available in development environments
|
|
- Tool usage analytics help optimize integration performance
|
|
|
|
## Skill Management
|
|
|
|
Tools are provided by Skills, which are JavaScript modules running in a secure V8 runtime:
|
|
|
|
- **Discovery**: Tools are automatically discovered at build time from running skills
|
|
- **Lifecycle**: Skills can be enabled/disabled independently without affecting others
|
|
- **Configuration**: Each skill has its own configuration panel with setup wizards
|
|
- **Updates**: Skills are updated through Git submodules and the Skills management system
|
|
- **Security**: Skills run in sandboxed environments with limited system access
|
|
|
|
## Integration Architecture
|
|
|
|
### V8 Runtime
|
|
- Skills execute in isolated V8 JavaScript contexts on desktop platforms
|
|
- Mobile platforms use lightweight alternatives with server-side execution
|
|
- Memory limits and execution timeouts prevent resource exhaustion
|
|
- Inter-skill communication is managed through secure message passing
|
|
|
|
### API Bridge
|
|
- Tools communicate with external services through standardized API bridges
|
|
- Rate limiting, retry logic, and error handling are implemented at the bridge level
|
|
- Authentication tokens are managed centrally and shared across tools
|
|
- Response caching and optimization are handled transparently
|
|
|
|
### Data Flow
|
|
1. Tool request received from AI agent
|
|
2. Input validation and parameter processing
|
|
3. Skill execution in secure V8 context
|
|
4. API calls through standardized bridges
|
|
5. Response processing and formatting
|
|
6. Result delivery to AI agent
|
|
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Generates footer section with metadata
|
|
* @param {Array} tools - Array of all tools
|
|
* @returns {string} Footer content
|
|
*/
|
|
export function generateFooter(tools) {
|
|
const skillCount = new Set(tools.map(t => t.skillId)).size;
|
|
|
|
return `## Support & Troubleshooting
|
|
|
|
### Common Issues
|
|
1. **Tool Not Available**: Check if the associated skill is enabled in Settings → Skills
|
|
2. **Authentication Errors**: Verify credentials in the skill's configuration panel
|
|
3. **Rate Limit Exceeded**: Wait for the limit to reset or upgrade your API plan
|
|
4. **Invalid Parameters**: Review the parameter documentation and examples above
|
|
|
|
### Getting Help
|
|
- **Skill Documentation**: Each skill has detailed setup and usage instructions
|
|
- **Debug Logs**: Enable verbose logging in development mode for detailed error information
|
|
- **Community Support**: Join our Discord community for help from other users
|
|
- **Technical Support**: Contact our support team for critical issues
|
|
|
|
### Contributing
|
|
- **New Tools**: Submit tool requests through our GitHub repository
|
|
- **Bug Reports**: Report issues with specific tools and include error logs
|
|
- **Improvements**: Suggest enhancements to existing tools and their documentation
|
|
|
|
---
|
|
|
|
**Tool Statistics**
|
|
- Total Tools: ${tools.length}
|
|
- Active Skills: ${skillCount}
|
|
- Categories: ${Object.keys(TOOL_CATEGORIES).length}
|
|
- Last Updated: ${new Date().toISOString()}
|
|
|
|
*This file was automatically generated at build time from the V8 skills runtime.*
|
|
*For the most up-to-date information, regenerate this file by running \`pnpm tools:generate\`.*
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Generates complete OpenClaw-compliant TOOLS.md content
|
|
* @param {Array} tools - Array of discovered tools
|
|
* @returns {string} Complete TOOLS.md content
|
|
*/
|
|
export function generateOpenClawMarkdown(tools) {
|
|
const grouped = groupToolsBySkill(tools);
|
|
const skillNames = Object.keys(grouped);
|
|
|
|
let content = `# OpenHuman Tools
|
|
|
|
This document lists all available tools that OpenHuman can use to interact with external services and perform actions. Tools are organized by integration and automatically updated during build time.
|
|
|
|
## Overview
|
|
|
|
OpenHuman has access to **${tools.length} tools** across **${skillNames.length} integrations** organized into **${Object.keys(TOOL_CATEGORIES).length} categories**.
|
|
|
|
**Quick Statistics:**
|
|
${skillNames.map(skill => `- **${grouped[skill].name}**: ${grouped[skill].tools.length} tools`).join('\n')}
|
|
|
|
`;
|
|
|
|
content += generateEnvironmentSection();
|
|
content += generateCategoriesSection(grouped);
|
|
content += generateToolsSection(grouped);
|
|
content += generateGuidelinesSection();
|
|
content += generateFooter(tools);
|
|
|
|
return content;
|
|
}
|