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
JavaScript
#!/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'
];