UNPKG

defarm-sdk

Version:

DeFarm SDK - Git for traceability with multi-role permissions and global item discovery for agriculture supply chain

1,521 lines (1,282 loc) 215 kB
#!/usr/bin/env node const { DeFarmSDK } = require('./index'); const { DatabaseManager } = require('./lib/db-manager'); const { DeFarmAPIClient } = require('./lib/api-client'); const fs = require('fs').promises; const path = require('path'); const readline = require('readline'); const { spawn } = require('child_process'); // ANSI color codes for better CLI experience const colors = { reset: '\x1b[0m', bright: '\x1b[1m', dim: '\x1b[2m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', magenta: '\x1b[35m', cyan: '\x1b[36m', white: '\x1b[37m' }; // Helper functions for colored output const log = { success: (msg) => console.log(`${colors.green}✓${colors.reset} ${msg}`), error: (msg) => console.log(`${colors.red}✗${colors.reset} ${msg}`), warning: (msg) => console.log(`${colors.yellow}⚠${colors.reset} ${msg}`), info: (msg) => console.log(`${colors.blue}ℹ${colors.reset} ${msg}`), header: (msg) => console.log(`\n${colors.bright}${colors.cyan}${msg}${colors.reset}`), subheader: (msg) => console.log(`${colors.bright}${msg}${colors.reset}`), dim: (msg) => console.log(`${colors.dim}${msg}${colors.reset}`) }; // Progress bar for long operations class ProgressBar { constructor(total, label = 'Progress') { this.total = total; this.current = 0; this.label = label; this.barLength = 30; } update(current) { this.current = current; const progress = Math.min(this.current / this.total, 1); const filled = Math.round(this.barLength * progress); const empty = this.barLength - filled; const bar = '█'.repeat(filled) + '░'.repeat(empty); const percent = (progress * 100).toFixed(1); process.stdout.write(`\r${this.label}: [${bar}] ${percent}% (${this.current}/${this.total})`); if (this.current >= this.total) { console.log(); } } complete() { this.update(this.total); } } // Get version status for display in menus async function getVersionStatus() { const pkg = require(path.join(__dirname, 'package.json')); const currentVersion = pkg.version; try { const https = require('https'); const registryUrl = 'https://registry.npmjs.org/defarm-sdk/latest'; const latestVersion = await Promise.race([ new Promise((resolve, reject) => { const req = https.get(registryUrl, (res) => { let data = ''; res.on('data', (chunk) => data += chunk); res.on('end', () => { try { const packageInfo = JSON.parse(data); resolve(packageInfo.version); } catch (err) { reject(err); } }); }); req.on('error', reject); req.setTimeout(3000, () => { req.destroy(); reject(new Error('Request timeout')); }); }), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 3000) ) ]); const semverCompare = (a, b) => { const aParts = a.split('.').map(n => parseInt(n)); const bParts = b.split('.').map(n => parseInt(n)); for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) { const aPart = aParts[i] || 0; const bPart = bParts[i] || 0; if (aPart < bPart) return -1; if (aPart > bPart) return 1; } return 0; }; const comparison = semverCompare(currentVersion, latestVersion); if (comparison < 0) { return { version: currentVersion, status: 'outdated', latest: latestVersion, updateCommand: 'npm update -g defarm-sdk' }; } else if (comparison === 0) { return { version: currentVersion, status: 'latest', latest: latestVersion }; } else { return { version: currentVersion, status: 'ahead', latest: latestVersion }; } } catch (error) { return { version: currentVersion, status: 'unknown', error: error.message }; } } // Version information and update checking async function showVersionInfo() { const pkg = require('./package.json'); const currentVersion = pkg.version; console.log(`\n${colors.bright}${colors.green}🌾 DeFarm SDK${colors.reset}`); console.log(`${colors.cyan}Current Version:${colors.reset} v${currentVersion}`); // Check for updates try { log.info('Checking for updates...'); const https = require('https'); const registryUrl = 'https://registry.npmjs.org/defarm-sdk/latest'; const latestVersion = await new Promise((resolve, reject) => { const req = https.get(registryUrl, (res) => { let data = ''; res.on('data', (chunk) => data += chunk); res.on('end', () => { try { const packageInfo = JSON.parse(data); resolve(packageInfo.version); } catch (err) { reject(err); } }); }); req.on('error', reject); req.setTimeout(5000, () => { req.destroy(); reject(new Error('Request timeout')); }); }); const semverCompare = (a, b) => { const aParts = a.split('.').map(n => parseInt(n)); const bParts = b.split('.').map(n => parseInt(n)); for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) { const aPart = aParts[i] || 0; const bPart = bParts[i] || 0; if (aPart < bPart) return -1; if (aPart > bPart) return 1; } return 0; }; const comparison = semverCompare(currentVersion, latestVersion); if (comparison < 0) { console.log(`${colors.yellow}Latest Version:${colors.reset} v${latestVersion} ${colors.green}(update available!)${colors.reset}`); console.log(`\n${colors.bright}${colors.green}To update:${colors.reset}`); console.log(`${colors.cyan} npm install -g defarm-sdk@latest${colors.reset}`); console.log(`${colors.dim} or${colors.reset}`); console.log(`${colors.cyan} npm update -g defarm-sdk${colors.reset}`); } else if (comparison === 0) { console.log(`${colors.green}✓ You're running the latest version!${colors.reset}`); } else { console.log(`${colors.yellow}Latest Version:${colors.reset} v${latestVersion} ${colors.dim}(you're ahead of npm)${colors.reset}`); } } catch (error) { log.warning('Could not check for updates: ' + error.message); console.log(`${colors.dim}Check manually: npm info defarm-sdk version${colors.reset}`); } // Additional info console.log(`\n${colors.bright}System Information:${colors.reset}`); console.log(`${colors.cyan}Node.js:${colors.reset} ${process.version}`); console.log(`${colors.cyan}Platform:${colors.reset} ${process.platform} ${process.arch}`); // Check for WASM processor try { await fs.access(path.join(__dirname, 'pkg', 'defarm_processor_bg.wasm')); console.log(`${colors.green}✓ WASM Processor:${colors.reset} Available`); } catch { console.log(`${colors.yellow}⚠ WASM Processor:${colors.reset} Not built (run: ${colors.cyan}npm run build:wasm${colors.reset})`); } console.log(`\n${colors.dim}For help: ${colors.cyan}defarm --help${colors.reset}`); console.log(`${colors.dim}Documentation: ${colors.cyan}https://docs.defarm.net${colors.reset}`); } // Interactive prompt helper async function prompt(question, defaultValue = '') { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { const questionText = defaultValue ? `${question} ${colors.dim}(${defaultValue})${colors.reset}: ` : `${question}: `; rl.question(questionText, (answer) => { rl.close(); resolve(answer || defaultValue); }); }); } // Select from options async function select(question, options) { console.log(`\n${question}`); options.forEach((opt, i) => { console.log(` ${colors.cyan}${i + 1})${colors.reset} ${opt.label || opt}`); }); const answer = await prompt('Select option', '1'); const index = parseInt(answer) - 1; if (index >= 0 && index < options.length) { return options[index].value || options[index]; } log.error('Invalid selection'); return select(question, options); } // Confirm action async function confirm(question) { const answer = await prompt(`${question} (y/N)`, 'n'); return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes'; } // ASCII Art Banner // Generate dynamic banner with version information async function getDeFarmBanner() { const versionStatus = await getVersionStatus(); let versionDisplay = ''; let updateNotice = ''; switch (versionStatus.status) { case 'outdated': versionDisplay = `${colors.yellow}v${versionStatus.version}${colors.reset} ${colors.red}(Update available: v${versionStatus.latest})${colors.reset}`; updateNotice = `\n${colors.bright}${colors.green}💡 Update now:${colors.reset} ${colors.cyan}${versionStatus.updateCommand}${colors.reset}`; break; case 'latest': versionDisplay = `${colors.green}v${versionStatus.version}${colors.reset} ${colors.dim}(Latest)${colors.reset}`; break; case 'ahead': versionDisplay = `${colors.blue}v${versionStatus.version}${colors.reset} ${colors.dim}(Development)${colors.reset}`; break; default: versionDisplay = `${colors.white}v${versionStatus.version}${colors.reset}`; } return `${colors.green} ██████╗ ███████╗███████╗ █████╗ ██████╗ ███╗ ███╗ ██╔══██╗██╔════╝██╔════╝██╔══██╗██╔══██╗████╗ ████║ ██║ ██║█████╗ █████╗ ███████║██████╔╝██╔████╔██║ ██║ ██║██╔══╝ ██�══╝ ██╔══██║██╔══██╗██║╚██╔╝██║ ██████╔╝███████╗██║ ██║ ██║██║ ██║██║ ╚═╝ ██║ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ${colors.reset} ${colors.cyan} 🌾 Agricultural Blockchain Infrastructure Platform${colors.reset} ${colors.bright}Version:${colors.reset} ${versionDisplay}${updateNotice} ${colors.dim} • On-premise data processing with blockchain tokenization • Gas-free transactions sponsored by DeFarm • Enterprise-grade security with WASM protection • Complete supply chain traceability ${colors.reset} `; } // Function to generate interactive menu with conditional update async function generateInteractiveMenu() { const versionInfo = await getVersionStatus(); const needsUpdate = versionInfo.needsUpdate; let menu = ` ${colors.bright}Available Actions:${colors.reset} ${colors.green}🚀 Quick Start${colors.reset} ${colors.cyan}1)${colors.reset} ${colors.bright}dashboard${colors.reset} Start web dashboard ${colors.cyan}2)${colors.reset} ${colors.bright}init${colors.reset} Initialize new project ${colors.cyan}3)${colors.reset} ${colors.bright}validate${colors.reset} Validate configuration ${colors.magenta}🔐 Authentication${colors.reset} ${colors.cyan}4)${colors.reset} ${colors.bright}login${colors.reset} Login to DeFarm account ${colors.cyan}5)${colors.reset} ${colors.bright}logout${colors.reset} Logout from DeFarm ${colors.cyan}6)${colors.reset} ${colors.bright}apikey${colors.reset} Manage API keys ${colors.blue}🔧 Development${colors.reset} ${colors.cyan}7)${colors.reset} ${colors.bright}monitor${colors.reset} Real-time monitoring ${colors.cyan}8)${colors.reset} ${colors.bright}test${colors.reset} Run integration tests ${colors.cyan}9)${colors.reset} ${colors.bright}benchmark${colors.reset} Performance tests ${colors.green}🌾 Git-like Operations${colors.reset} ${colors.cyan}10)${colors.reset} ${colors.bright}workspace${colors.reset} Manage workspace ${colors.cyan}11)${colors.reset} ${colors.bright}item${colors.reset} Create/manage items ${colors.cyan}12)${colors.reset} ${colors.bright}event${colors.reset} Propose/endorse events ${colors.cyan}13)${colors.reset} ${colors.bright}commit${colors.reset} Create snapshot ${colors.cyan}14)${colors.reset} ${colors.bright}status${colors.reset} Show workspace status ${colors.cyan}15)${colors.reset} ${colors.bright}log${colors.reset} Show commit history ${colors.yellow}👤 Ownership Management${colors.reset} ${colors.cyan}16)${colors.reset} ${colors.bright}transfer${colors.reset} Transfer item ownership ${colors.cyan}17)${colors.reset} ${colors.bright}owner${colors.reset} Check ownership & history ${colors.magenta}🔒 Privacy & Access${colors.reset} ${colors.cyan}18)${colors.reset} ${colors.bright}access${colors.reset} Manage access control ${colors.cyan}19)${colors.reset} ${colors.bright}prove${colors.reset} Zero-knowledge proofs ${colors.blue}💰 Financial Operations${colors.reset} ${colors.cyan}20)${colors.reset} ${colors.bright}token${colors.reset} NFT tokenization ${colors.cyan}21)${colors.reset} ${colors.bright}finance${colors.reset} Collateral & fractionalization ${colors.cyan}22)${colors.reset} ${colors.bright}insure${colors.reset} Insurance binding ${colors.yellow}⚙️ Management${colors.reset} ${colors.cyan}23)${colors.reset} ${colors.bright}migrate${colors.reset} Migrate existing data ${colors.cyan}24)${colors.reset} ${colors.bright}doctor${colors.reset} Diagnose system issues ${colors.cyan}25)${colors.reset} ${colors.bright}config${colors.reset} Manage configuration ${colors.yellow}🔄 Circuit Participation${colors.reset} ${colors.cyan}26)${colors.reset} ${colors.bright}circuits${colors.reset} List all circuits (owned & joined) ${colors.cyan}27)${colors.reset} ${colors.bright}ws-list${colors.reset} List available workspaces ${colors.cyan}28)${colors.reset} ${colors.bright}ws-switch${colors.reset} Switch workspace ${colors.red}Other${colors.reset}`; // Add update option only if needed if (needsUpdate) { menu += ` ${colors.cyan}u)${colors.reset} ${colors.yellow}Update SDK${colors.reset} `; } menu += ` ${colors.cyan}h)${colors.reset} Help ${colors.cyan}q)${colors.reset} Quit ${colors.dim}(Persistent mode is now default)${colors.reset}`; return menu; } /** * Handler functions for new features */ async function handleCircuitsList() { log.header('🔄 Circuit Participation'); console.log('Loading circuit information...\n'); try { // This would integrate with actual API in the future const mockCircuits = [ { id: 'CIRCUIT-GOV-SP-001', name: 'São Paulo State Agriculture', role: 'producer', status: 'active', type: 'government', owner: 'governo-sp', joined: '2024-12-15' }, { id: 'CIRCUIT-CERT-ORG-001', name: 'Organic Certification Brazil', role: 'member', status: 'pending', type: 'certification', owner: 'cert-org-brasil', joined: 'requested' }, { id: 'CIRCUIT-COOP-RS-001', name: 'Rio Grande Sul Cooperative', role: 'producer', status: 'active', type: 'cooperative', owner: 'self', joined: '2025-01-10' } ]; console.log(`${colors.bright}Circuits You Participate In:${colors.reset}\n`); mockCircuits.forEach((circuit, index) => { const statusIcon = circuit.status === 'active' ? '🟢' : circuit.status === 'pending' ? '🟡' : '🔴'; const typeIcon = circuit.type === 'government' ? '🏛️' : circuit.type === 'certification' ? '✅' : circuit.type === 'cooperative' ? '🤝' : '🔄'; console.log(`${colors.cyan}${index + 1}.${colors.reset} ${typeIcon} ${colors.bright}${circuit.name}${colors.reset}`); console.log(` ID: ${circuit.id}`); console.log(` Role: ${colors.yellow}${circuit.role}${colors.reset} | Status: ${statusIcon} ${circuit.status}`); console.log(` Owner: ${circuit.owner === 'self' ? colors.green + 'You' + colors.reset : circuit.owner}`); console.log(` Joined: ${circuit.joined}`); console.log(); }); console.log(`${colors.dim}Use 'defarm remote add <circuit-id>' to sync with circuits${colors.reset}\n`); } catch (error) { log.error(`Failed to load circuits: ${error.message}`); } } async function handleConfigShow() { log.header('⚙️ DeFarm Configuration'); try { const fs = require('fs').promises; const path = require('path'); const os = require('os'); // Check global config const globalConfigPath = path.join(os.homedir(), '.defarm', 'config.json'); let globalConfig = {}; try { const globalConfigData = await fs.readFile(globalConfigPath, 'utf8'); globalConfig = JSON.parse(globalConfigData); console.log(`📁 Global config: ${globalConfigPath}`); } catch (error) { console.log('📁 Global config: Not found'); } // Check workspace config const workspaceConfigPath = path.join(process.cwd(), '.defarm', 'config.json'); let workspaceConfig = {}; try { const workspaceConfigData = await fs.readFile(workspaceConfigPath, 'utf8'); workspaceConfig = JSON.parse(workspaceConfigData); console.log(`📂 Workspace config: ${workspaceConfigPath}`); } catch (error) { console.log('📂 Workspace config: Not found'); } console.log('\n🔧 Current Settings:'); // Show authentication info if (globalConfig.auth) { console.log(` Authentication: ${colors.green}Configured${colors.reset}`); console.log(` User: ${globalConfig.auth.user?.name || 'Unknown'}`); console.log(` Email: ${globalConfig.auth.user?.email || 'Unknown'}`); } else { console.log(` Authentication: ${colors.red}Not configured${colors.reset}`); } // Show workspace info if (workspaceConfig.workspace) { console.log(` Workspace: ${colors.green}${workspaceConfig.workspace.name}${colors.reset}`); console.log(` Role: ${workspaceConfig.workspace.role || 'Unknown'}`); console.log(` Organization: ${workspaceConfig.workspace.organization || 'Unknown'}`); } else { console.log(` Workspace: ${colors.red}Not initialized${colors.reset}`); } // Show API endpoint const apiEndpoint = process.env.DEFARM_API_ENDPOINT || globalConfig.apiEndpoint || 'https://api.platform.defarm.net'; console.log(` API Endpoint: ${apiEndpoint}`); // Show environment const env = process.env.NODE_ENV || 'production'; console.log(` Environment: ${env}`); console.log(`\n💡 To modify settings, use: ${colors.cyan}defarm workspace init${colors.reset} or ${colors.cyan}defarm login${colors.reset}`); } catch (error) { console.log(`❌ Error loading configuration: ${error.message}`); } } async function handleWorkspaceList() { log.header('📁 Available Workspaces'); console.log('Scanning for DeFarm workspaces...\n'); try { const fs = require('fs').promises; const path = require('path'); const os = require('os'); // Look for workspaces in common locations const searchPaths = [ process.cwd(), path.join(os.homedir(), '.defarm'), path.join(os.homedir(), 'defarm-workspaces'), '/tmp/defarm-workspaces' ]; const workspaces = []; for (const searchPath of searchPaths) { try { const entries = await fs.readdir(searchPath, { withFileTypes: true }); for (const entry of entries) { if (entry.isDirectory()) { const workspacePath = path.join(searchPath, entry.name); const configPath = path.join(workspacePath, '.defarm', 'config.json'); try { const config = JSON.parse(await fs.readFile(configPath, 'utf8')); workspaces.push({ name: entry.name, path: workspacePath, role: config.workspace?.role || 'unknown', org: config.workspace?.name || 'unknown', created: config.created || 'unknown', current: workspacePath === process.cwd() }); } catch (e) { // Not a DeFarm workspace } } } } catch (e) { // Directory doesn't exist or can't be read } } if (workspaces.length === 0) { console.log(`${colors.yellow}No DeFarm workspaces found.${colors.reset}`); console.log(`${colors.dim}Create one with: defarm workspace init${colors.reset}\n`); return; } console.log(`${colors.bright}Found ${workspaces.length} workspace(s):${colors.reset}\n`); workspaces.forEach((ws, index) => { const currentIndicator = ws.current ? ` ${colors.green}(current)${colors.reset}` : ''; const roleColor = ws.role === 'authority' ? colors.magenta : ws.role === 'producer' ? colors.green : colors.cyan; console.log(`${colors.cyan}${index + 1}.${colors.reset} ${colors.bright}${ws.name}${colors.reset}${currentIndicator}`); console.log(` Organization: ${ws.org}`); console.log(` Role: ${roleColor}${ws.role}${colors.reset}`); console.log(` Path: ${colors.dim}${ws.path}${colors.reset}`); console.log(` Created: ${ws.created}`); console.log(); }); } catch (error) { log.error(`Failed to list workspaces: ${error.message}`); } } async function handleWorkspaceSwitch() { log.header('🔄 Switch Workspace'); console.log('This will change your current working directory to the selected workspace.\n'); // For now, show instructions - actual implementation would need more complex logic console.log(`${colors.yellow}To switch workspace:${colors.reset}`); console.log(`1. Use: ${colors.cyan}cd /path/to/workspace${colors.reset}`); console.log(`2. Or: ${colors.cyan}defarm workspace init${colors.reset} in desired directory`); console.log(`3. Run: ${colors.cyan}defarm status${colors.reset} to verify workspace\n`); console.log(`${colors.dim}Note: Workspace switching will be enhanced in future versions${colors.reset}\n`); } /** * Interactive CLI Interface */ async function startInteractiveMode() { console.clear(); const banner = await getDeFarmBanner(); console.log(banner); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); while (true) { const menu = await generateInteractiveMenu(); console.log(menu); const choice = await new Promise(resolve => { rl.question(`${colors.green}❯${colors.reset} Select an option: `, resolve); }); console.log(''); // Add spacing switch (choice.toLowerCase().trim()) { case '1': case 'dashboard': await commands.dashboard.handler({}); break; case '2': case 'init': await commands.init.handler({}); break; case '3': case 'validate': await commands.validate.handler({}); break; case '4': case 'login': await commands.login.handler({}); break; case '5': case 'logout': await commands.logout.handler({}); break; case '6': case 'apikey': await commands.apikey.handler({}); break; case '7': case 'monitor': await commands.monitor.handler({}); break; case '8': case 'test': await commands.test.handler({}); break; case '9': case 'benchmark': await commands.benchmark.handler({}); break; case '10': case 'workspace': // Ask for workspace action const workspaceAction = await select('Workspace action:', [ { label: 'Initialize workspace', value: 'init' }, { label: 'Show status', value: 'status' }, { label: 'Show config', value: 'config' }, { label: 'Create circuit (authority only)', value: 'circuit' } ]); await commands.workspace.handler({ _: ['workspace', workspaceAction] }); break; case '11': case 'item': // Ask for item action const itemAction = await select('Item action:', [ { label: 'Create new item', value: 'new' }, { label: 'List items', value: 'list' }, { label: 'Show item details', value: 'show' } ]); await commands.item.handler({ _: [itemAction] }); break; case '12': case 'event': // Ask for event action const eventAction = await select('Event action:', [ { label: 'Propose new event', value: 'propose' }, { label: 'Endorse existing event', value: 'endorse' }, { label: 'List events', value: 'list' } ]); await commands.event.handler({ _: [eventAction] }); break; case '13': case 'commit': await commands.commit.handler({}); break; case '14': case 'status': await commands.status.handler({}); break; case '15': case 'log': await commands.log.handler({}); break; case '16': case 'transfer': // Ask for item ID and new owner const transferItemId = await prompt('Item ID to transfer'); const newOwner = await prompt('New owner'); const transferReason = await prompt('Reason (optional)', ''); await commands.transfer.handler({ _: ['transfer', transferItemId, newOwner], reason: transferReason || undefined }); break; case '17': case 'owner': // Ask for owner action const ownerAction = await select('Owner action:', [ { label: 'Show current owner', value: 'show' }, { label: 'Show ownership history', value: 'history' } ]); const ownerItemId = await prompt('Item ID'); await commands.owner.handler({ _: ['owner', ownerAction, ownerItemId] }); break; case '24': case 'access': // Ask for access action const accessAction = await select('Access action:', [ { label: 'Grant access', value: 'grant' }, { label: 'Revoke access', value: 'revoke' }, { label: 'List permissions', value: 'list' } ]); await commands.access.handler({ _: ['access', accessAction] }); break; case '25': case 'prove': // Ask for prove action const proveAction = await select('ZK Proof action:', [ { label: 'Generate proof', value: 'add' }, { label: 'Verify proof', value: 'verify' }, { label: 'List proofs', value: 'list' } ]); await commands.prove.handler({ _: ['prove', proveAction] }); break; case '24': case 'token': // Ask for token action const tokenAction = await select('Token action:', [ { label: 'Mint NFT from commit', value: 'mint' }, { label: 'List owned tokens', value: 'list' } ]); await commands.token.handler({ _: ['token', tokenAction] }); break; case '25': case 'finance': // Ask for finance action const financeAction = await select('Finance action:', [ { label: 'Create collateral agreement', value: 'collateralize' }, { label: 'Fractionalize NFT', value: 'fraction' }, { label: 'List contracts', value: 'list' } ]); await commands.finance.handler({ _: ['finance', financeAction] }); break; case '24': case 'insure': // Ask for insurance action const insureAction = await select('Insurance action:', [ { label: 'Bind insurance coverage', value: 'bind' }, { label: 'List policies', value: 'list' } ]); await commands.insure.handler({ _: ['insure', insureAction] }); break; case '25': case 'migrate': await commands.migrate.handler({}); break; case '24': case 'doctor': await commands.doctor.handler({}); break; case '25': case 'config': await handleConfigShow(); break; case '26': case 'circuits': await handleCircuitsList(); break; case '27': case 'ws-list': await handleWorkspaceList(); break; case '28': case 'ws-switch': await handleWorkspaceSwitch(); break; case 'u': case 'update': await commands.update.handler({}); break; case 'p': case 'persistent': console.log(`${colors.yellow}⚠️ Persistent mode is now the default when running 'defarm'${colors.reset}`); console.log(`${colors.dim}Just run 'defarm' to start the persistent CLI interface.${colors.reset}`); break; case 'h': case 'help': commands.help.handler({}); break; case 'q': case 'quit': case 'exit': console.log(`${colors.green}👋 Thanks for using DeFarm SDK!${colors.reset}`); rl.close(); process.exit(0); break; default: console.log(`${colors.red}❌ Unknown option: "${choice}"${colors.reset}\n`); break; } // Wait for user to press enter before showing menu again await new Promise(resolve => { rl.question(`\n${colors.dim}Press Enter to continue...${colors.reset}`, resolve); }); console.clear(); const banner = await getDeFarmBanner(); console.log(banner); } } /** * DeFarm SDK CLI - Enhanced Version */ const commands = { interactive: { description: 'Start legacy interactive mode (persistent mode is now default)', usage: 'defarm interactive', handler: startInteractiveMode }, dashboard: { description: 'Start the web dashboard', usage: 'defarm dashboard [--port 3456]', handler: async (args) => { try { const { DashboardServer } = require('./lib/dashboard/server'); const port = args.port ? parseInt(args.port) : 3456; log.header('Starting DeFarm Dashboard...'); const server = new DashboardServer(port); await server.start(); log.success(`Dashboard running at http://localhost:${port}`); log.info('Press Ctrl+C to stop'); // Keep process alive process.on('SIGINT', async () => { log.info('\nStopping dashboard...'); await server.stop(); process.exit(0); }); // Keep the process running await new Promise(() => {}); } catch (error) { log.error(`Failed to start dashboard: ${error.message}`); process.exit(1); } } }, init: { description: 'Initialize a new DeFarm project', usage: 'defarm init [project-name]', handler: async (args) => { log.header('🚀 Initialize DeFarm Project'); const projectName = args._[0] || await prompt('Project name', 'my-defarm-project'); const projectPath = path.join(process.cwd(), projectName); // Check if directory exists try { await fs.access(projectPath); if (!await confirm(`Directory ${projectName} already exists. Continue?`)) { return; } } catch { await fs.mkdir(projectPath, { recursive: true }); } // Select deployment mode const deploymentMode = await select('Select deployment mode:', [ { label: 'On-premise (100% isolated)', value: 'on-premise' }, { label: 'Cloud (SaaS with gas-free blockchain)', value: 'cloud' }, { label: 'Hybrid (Local processing + cloud features)', value: 'hybrid' } ]); // Select database const database = await select('Select database type:', [ { label: 'PostgreSQL', value: 'postgresql' }, { label: 'MySQL', value: 'mysql' }, { label: 'Oracle', value: 'oracle' }, { label: 'SQL Server', value: 'sqlserver' }, { label: 'SQLite (for testing)', value: 'sqlite' } ]); // Select blockchain network let blockchain = 'none'; if (deploymentMode !== 'on-premise') { blockchain = await select('Select blockchain network:', [ { label: 'Stellar (Recommended - lowest fees)', value: 'stellar' }, { label: 'Polygon', value: 'polygon' }, { label: 'Ethereum', value: 'ethereum' }, { label: 'None (disable blockchain)', value: 'none' } ]); } // Generate configuration files log.info('Generating configuration files...'); // package.json const packageJson = { name: projectName, version: '1.0.0', description: 'DeFarm SDK Project', main: 'index.js', scripts: { 'start': 'node index.js', 'dev': 'nodemon index.js', 'test': 'jest', 'validate': 'defarm validate', 'process': 'defarm process', 'monitor': 'defarm monitor' }, dependencies: { 'defarm-sdk': '^1.0.2' }, devDependencies: { 'nodemon': '^3.0.0', 'jest': '^29.0.0' } }; await fs.writeFile( path.join(projectPath, 'package.json'), JSON.stringify(packageJson, null, 2) ); // .env file const envContent = `# DeFarm SDK Configuration # Generated by: defarm init # Deployment Mode DEFARM_DEPLOYMENT_MODE=${deploymentMode} # Database Configuration DEFARM_DB_TYPE=${database} DEFARM_DB_HOST=localhost DEFARM_DB_PORT=${database === 'postgresql' ? 5432 : database === 'mysql' ? 3306 : 1433} DEFARM_DB_NAME=${projectName.replace(/-/g, '_')}_db DEFARM_DB_USER=defarm_user DEFARM_DB_PASSWORD=change_this_password # Blockchain Configuration DEFARM_BLOCKCHAIN_ENABLED=${blockchain !== 'none'} DEFARM_BLOCKCHAIN_NETWORK=${blockchain} ${blockchain === 'stellar' ? `DEFARM_STELLAR_NETWORK=testnet DEFARM_STELLAR_HORIZON=https://horizon-testnet.stellar.org` : ''} ${blockchain !== 'none' && blockchain !== 'stellar' ? `DEFARM_RPC_URL= DEFARM_PRIVATE_KEY=your_private_key_here DEFARM_CONTRACT_ADDRESS=` : ''} # Relay Configuration (for gas-free transactions) ${deploymentMode !== 'on-premise' ? `DEFARM_RELAY_ENABLED=true DEFARM_RELAY_URL=https://relay.defarm.net DEFARM_RELAY_API_KEY=request_at_platform.defarm.net` : 'DEFARM_RELAY_ENABLED=false'} # IPFS Configuration DEFARM_IPFS_ENABLED=${deploymentMode !== 'on-premise'} DEFARM_IPFS_HOST=ipfs.infura.io DEFARM_IPFS_PORT=5001 DEFARM_IPFS_PROTOCOL=https # Enterprise Features DEFARM_ENTERPRISE_ENABLED=true DEFARM_STRICT_VALIDATION=true # Monitoring DEFARM_MONITORING_ENABLED=true DEFARM_MONITORING_PORT=9090 `; await fs.writeFile(path.join(projectPath, '.env'), envContent); // defarm.config.js const configJs = `/** * DeFarm SDK Configuration * @type {import('defarm-sdk').DeFarmConfig} */ module.exports = { // Deployment mode: 'on-premise', 'cloud', or 'hybrid' deploymentMode: '${deploymentMode}', // Database configuration database: { type: '${database}', // Additional database options connectionPool: { min: 2, max: 10 } }, // Blockchain configuration blockchain: { enabled: ${blockchain !== 'none'}, network: '${blockchain}', // Gas sponsorship via relay useRelay: ${deploymentMode !== 'on-premise'} }, // Processing options processing: { // Use WASM for protected business logic useWasm: true, // Batch size for bulk operations batchSize: 100, // Concurrent workers workers: 4 }, // Verification settings verification: { // Duplicate detection threshold duplicateThreshold: 0.85, // Strict mode for validation strictMode: true }, // Provenance tracking provenance: { // Enable blockchain recording blockchain: ${blockchain !== 'none'}, // Hash algorithm hashAlgorithm: 'sha256' }, // API settings api: { // Enable REST API enabled: true, port: 3000, // CORS settings cors: { origin: '*', credentials: true } }, // Monitoring monitoring: { enabled: true, // Metrics export metrics: { format: 'prometheus', port: 9090 }, // Health check endpoint healthCheck: { enabled: true, path: '/health' } }, // Custom modules modules: { // Add your custom modules here } }; `; await fs.writeFile(path.join(projectPath, 'defarm.config.js'), configJs); // index.js - main application file const indexJs = `const { DeFarmSDK } = require('defarm-sdk'); const config = require('./defarm.config'); async function main() { console.log('🌾 Starting DeFarm SDK Application...'); // Initialize SDK const sdk = new DeFarmSDK(config); await sdk.initialize(); console.log('✅ SDK initialized successfully'); // Example: Process agriculture data const exampleData = { asset_type: 'livestock', breed: 'Angus', weight: 450, location: { latitude: -23.550520, longitude: -46.633308, country: 'Brazil' } }; // Validate data const validation = await sdk.validateAsset(exampleData); console.log('Validation result:', validation); // Check for duplicates const duplicates = await sdk.checkForDuplicates(exampleData); console.log('Duplicate check:', duplicates); // Process and tokenize const result = await sdk.processAgricultureData(exampleData, { tokenize: true, recordProvenance: true }); console.log('Processing result:', result); // Start API server if configured if (config.api?.enabled) { const express = require('express'); const app = express(); app.use(express.json()); app.post('/process', async (req, res) => { try { const result = await sdk.processAgricultureData(req.body); res.json(result); } catch (error) { res.status(400).json({ error: error.message }); } }); app.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: Date.now() }); }); const port = config.api.port || 3000; app.listen(port, () => { console.log(\`🚀 API server running on port \${port}\`); }); } // Graceful shutdown process.on('SIGINT', async () => { console.log('\\n👋 Shutting down gracefully...'); await sdk.shutdown(); process.exit(0); }); } main().catch(console.error); `; await fs.writeFile(path.join(projectPath, 'index.js'), indexJs); // .gitignore const gitignore = `# Dependencies node_modules/ # Environment .env .env.local .env.*.local # Logs logs/ *.log # Database *.sqlite *.db # Build output dist/ build/ pkg/ # IDE .vscode/ .idea/ *.swp *.swo # OS .DS_Store Thumbs.db # Testing coverage/ .nyc_output/ # Temporary tmp/ temp/ `; await fs.writeFile(path.join(projectPath, '.gitignore'), gitignore); // README.md const readme = `# ${projectName} A DeFarm SDK project for agricultural supply chain management. ## Setup 1. Install dependencies: \`\`\`bash npm install \`\`\` 2. Configure environment: - Edit \`.env\` file with your database credentials - Add blockchain keys if using blockchain features 3. Initialize database: \`\`\`bash npx defarm setup \`\`\` 4. Run the application: \`\`\`bash npm start \`\`\` ## Available Commands - \`npm start\` - Start the application - \`npm run dev\` - Start in development mode with auto-reload - \`npm test\` - Run tests - \`npm run validate\` - Validate configuration - \`npm run process\` - Process agriculture data - \`npm run monitor\` - Start monitoring dashboard ## Configuration Edit \`defarm.config.js\` to customize SDK behavior. ## API Endpoints - \`POST /process\` - Process agriculture data - \`GET /health\` - Health check ## Support - Documentation: https://platform.defarm.net/docs - Issues: https://github.com/defarm/sdk/issues `; await fs.writeFile(path.join(projectPath, 'README.md'), readme); log.success(`Project created at: ${projectPath}`); // Display next steps console.log('\n' + colors.bright + 'Next steps:' + colors.reset); console.log(` cd ${projectName}`); console.log(' npm install'); console.log(' npx defarm setup'); console.log(' npm start'); // Offer to install dependencies if (await confirm('\nInstall dependencies now?')) { log.info('Installing dependencies...'); const npmInstall = spawn('npm', ['install'], { cwd: projectPath, stdio: 'inherit' }); await new Promise((resolve) => { npmInstall.on('close', resolve); }); log.success('Dependencies installed'); } } }, config: { description: 'Configure DeFarm SDK settings', usage: 'defarm config <action> [options]', handler: async (args) => { const action = args._[1]; const apiClient = new DeFarmAPIClient(); await apiClient.init(); switch (action) { case 'set': if (args._[2] && args._[3]) { const key = args._[2]; const value = args._[3]; log.header('⚙️ Configure DeFarm SDK'); const configUpdate = {}; if (key === 'api-endpoint') { configUpdate.apiEndpoint = value; } else if (key === 'storage-mode') { if (!['hosted', 'on-premise'].includes(value)) { log.error('Invalid storage mode. Use: hosted or on-premise'); break; } configUpdate.storageMode = value; } else if (key === 'api-key') { configUpdate.apiKey = value; } else { configUpdate[key] = value; } await apiClient.configure({ ...configUpdate, global: args.global }); log.success(`Configuration updated: ${key} = ${value}`); if (key === 'storage-mode') { log.info('\n💡 Storage mode configuration:'); console.log(' - hosted: Use DeFarm cloud infrastructure'); console.log(' - on-premise: Use your own API server'); if (value === 'on-premise') { log.info('\n📋 For on-premise setup:'); console.log(' 1. Deploy apps/api on your server'); console.log(' 2. Set API endpoint: defarm config set api-endpoint http://your-server:3000'); console.log(' 3. Ensure your database is configured'); } } } else { log.error('Usage: defarm config set <key> <value>'); console.log('Available keys: api-endpoint, storage-mode, api-key'); } break; case 'get': if (args._[2]) { const config = apiClient.getConfig(); const key = args._[2]; console.log(`${key}: ${config[key] || 'not set'}`); } else { log.error('Usage: defarm config get <key>'); } break; case 'show': case 'list': log.header('📋 Current Configuration'); const config = apiClient.getConfig(); console.log(`API Endpoint: ${config.apiEndpoint}`); console.log(`Storage Mode: ${config.storageMode || 'not set'}`); console.log(`Organization: ${config.organization || 'not set'}`); console.log(`Has API Key: ${config.hasApiKey ? 'Yes' : 'No'}`); break; case 'health': case 'test': log.header('🏥 API Health Check'); try { const health = await apiClient.healthCheck(); if (health.status === 'healthy') { log.success(`API is healthy at ${apiClient.apiBaseUrl}`); console.log(`Response time: ${health.responseTime || 'N/A'}`); } else { log.error(`API is unhealthy: ${health.error}`); log.info('💡 Check your API endpoint configuration'); } } catch (error) { log.error(`Health check failed: ${error.message}`); log.info('💡 Verify your API endpoint and connection'); } break; default: log.error('Unknown config action. Use: set, get, show, health'); console.log('\nExamples:'); console.log(' defarm config set api-endpoint http://localhost:3000'); console.log(' defarm config set storage-mode on-premise'); console.log(' defarm config show'); console.log(' defarm config health'); break; } } }, circuit: { description: 'Manage government circuits', usage: 'defarm circuit <action> [options]', handler: async (args) => { const action = args._[1]; const apiClient = new DeFarmAPIClient(); await apiClient.init(); switch (action) { case 'create': log.header('🏛️ Create Government Circuit'); const circuitName = args.name || await prompt('Circuit name', 'Central Valley Agriculture Circuit'); const jurisdiction = args.jurisdiction || await prompt('Jurisdiction', 'Central Valley State'); const domain = args.domain || await prompt('Domain (optional)', `https://${circuitName.toLowerCase().replace(/\s+/g, '-')}.gov`); const securityLevel = args['security-level'] || await select('Security level:', [ { label: 'High (recommended for government)', value: 'high' }, { label: 'Medium', value: 'medium' }, { label: 'Low', value: 'low' } ]); const dataResidency = args['data-residency'] || await prompt('Data residency', 'local'); try { log.info('Creating circuit via API...'); const circuitData = { name: circuitName, jurisdiction, domain, hosting: 'self-hosted', infrastructure: 'on-premise', security_level: securityLevel, data_residency: dataResidency, public_access: ['basic-info', 'certifications'], producer_access: ['own-items', 'health-records'], authority_access: ['full-regulatory-data'], encryption_required: true }; const result = await apiClient.createCircuit(circuitData); log.success('Circuit created successfully!'); console.log(`Circuit ID: ${result.data.circuit_id}`); console.log(`Name: ${circuitName}`); console.log(`Jurisdiction: ${jurisdiction}`); console.log(`Domain: ${domain}`); console.log(`Security Level: ${securityLevel}`); console.log('\n📡 API Endpoints:'); console.log(` Push: ${result.data.api_endpoints.push}`); console.log(` Pull: ${result.data.api_endpoints.pull}`); console.log(` Access Request: ${result.data.api_endpoints.access_request}`); log.info('\n💡 Next steps:'); console.log(' 1. Issue credentials: defarm circuit credential issue'); console.log(' 2. Review access requests: defarm circuit access list'); console.log(' 3. Configure API on your government server'); } catch (error) { log.error(`Failed to create circuit: ${error.message}`); if (error.message.includes('Cannot connect to DeFarm API')) { log.info('💡 Configure API endpoint: defarm config set api-endpoint http://localhost:3000'); log.info(' For on-premise: defarm config set storage-mode on-premise'); } } break; case 'list': try { log.header('📋 Government Circuits'); const circuits = await apiClient.getCircuits(); if (circuits.data.length === 0) { log.info('No circuits found. Create one with: defarm circuit create'); } else { circuits.data.forEach(circuit => { console.log(`\n${colors.bright}${circuit.name}${colors.reset}`); console.log(` ID: ${circuit.circuit_id}`); console.log(` Jurisdiction: ${circuit.jurisdiction}`); console.log(` Status: ${circuit.status}`); console.log(` Created: ${new Date(circuit.created_at).toLocaleDateString()}`); }); } } catch (error) { log.error(`Failed to list circuits: ${error.message}`); } break; case 'credential': const credentialAction = args._[2]; if (credentialAction === 'issue') { try { const circuitId = args['circuit-id'] || await prompt('Circuit ID'); const orgId = args['org-id'] || await prompt('Organization ID'); const permissions = args.permissions ? args.permissions.split(',') : [ 'submit_livestock_data', 'update_health_records', 'request_certificates' ];