#!/usr/bin/env node /** * generate_catalog.js — Genera catalogo markdown dai metadata * * Usage: * node generate_catalog.js --client * node generate_catalog.js --client demo_co_srl * * Options: * --input Path metadata JSON * --output Path output catalog.md * --verbose Log dettagliato */ const fs = require('fs'); const path = require('path'); const os = require('os'); function formatSize(bytes) { const units = ['B', 'KB', 'MB', 'GB']; let size = bytes; for (const unit of units) { if (size < 1024) return `${size.toFixed(1)} ${unit}`; size /= 1024; } return `${size.toFixed(1)} TB`; } function loadMetadata(inputPath) { const content = fs.readFileSync(inputPath, 'utf8'); return JSON.parse(content); } function groupByType(resources) { const grouped = { images: [], videos: [], documents: [], other: [] }; for (const res of resources) { const mime = res.mime_type || ''; const ext = res.extension || ''; if (mime.startsWith('image/')) { grouped.images.push(res); } else if (mime.startsWith('video/')) { grouped.videos.push(res); } else if (['pdf', 'doc', 'docx', 'txt', 'md', 'ppt', 'pptx', 'xls', 'xlsx'].includes(ext)) { grouped.documents.push(res); } else { grouped.other.push(res); } } return grouped; } function generateSummary(grouped) { const rows = []; for (const [type, label] of [['images', 'Immagini'], ['videos', 'Video'], ['documents', 'Documenti']]) { const resources = grouped[type] || []; const count = resources.length; const totalSize = resources.reduce((sum, r) => sum + (r.size_bytes || 0), 0); rows.push(`| ${label} | ${count} | ${formatSize(totalSize)} |`); } return rows.join('\n'); } function generateImagesTable(resources) { if (resources.length === 0) return '_Nessuna immagine trovata_'; const rows = []; const header = '| File | Tipo | Dimensioni | Risoluzione | Descrizione | Tag | Use Case |'; const separator = '|------|------|------------|-------------|-------------|-----|----------|'; const sorted = [...resources].sort((a, b) => a.filename.localeCompare(b.filename)); for (const res of sorted) { const filename = res.filename || 'Unknown'; const ext = (res.extension || '?').toUpperCase(); const size = res.size_formatted || '?'; const resolution = res.resolution || '-'; const description = res.description || filename; const tags = (res.tags || []).slice(0, 5).map(t => `#${t}`).join(', '); const useCases = (res.use_cases || []).slice(0, 2).join(', '); rows.push(`| \`${filename}\` | ${ext} | ${size} | ${resolution} | ${description} | ${tags} | ${useCases} |`); } return [header, separator, ...rows].join('\n'); } function generateVideosTable(resources) { if (resources.length === 0) return '_Nessun video trovato_'; const rows = []; const header = '| File | Tipo | Dimensioni | Durata | Risoluzione | Descrizione | Tag | Use Case |'; const separator = '|------|------|------------|--------|-------------|-------------|-----|----------|'; const sorted = [...resources].sort((a, b) => a.filename.localeCompare(b.filename)); for (const res of sorted) { const filename = res.filename || 'Unknown'; const ext = (res.extension || '?').toUpperCase(); const size = res.size_formatted || '?'; const resolution = res.resolution || '-'; const duration = res.duration ? `${Math.floor(res.duration / 60)}:${(res.duration % 60).toFixed(0).padStart(2, '0')}` : '-'; const description = res.description || filename; const tags = (res.tags || []).slice(0, 5).map(t => `#${t}`).join(', '); const useCases = (res.use_cases || []).slice(0, 2).join(', '); rows.push(`| \`{filename}\` | ${ext} | ${size} | ${duration} | ${resolution} | ${description} | ${tags} | ${useCases} |`); } return [header, separator, ...rows].join('\n'); } function generateDocumentsTable(resources) { if (resources.length === 0) return '_Nessun documento trovato_'; const rows = []; const header = '| File | Tipo | Dimensioni | Descrizione | Tag | Use Case |'; const separator = '|------|------|------------|-------------|-----|----------|'; const sorted = [...resources].sort((a, b) => a.filename.localeCompare(b.filename)); for (const res of sorted) { const filename = res.filename || 'Unknown'; const ext = (res.extension || '?').toUpperCase(); const size = res.size_formatted || '?'; const description = res.description || filename; const tags = (res.tags || []).slice(0, 5).map(t => `#${t}`).join(', '); const useCases = (res.use_cases || []).slice(0, 2).join(', '); rows.push(`| \`${filename}\` | ${ext} | ${size} | ${description} | ${tags} | ${useCases} |`); } return [header, separator, ...rows].join('\n'); } function generateGlobalTags(resources) { const allTags = new Set(); for (const res of resources) { for (const tag of (res.tags || [])) { allTags.add(tag); } } if (allTags.size === 0) return '_Nessun tag generato_'; const sortedTags = [...allTags].sort().slice(0, 20); return sortedTags.map(t => `#${t}`).join(' '); } function generateCatalog(clientName, metadata, outputPath, verbose = false) { const resources = metadata.resources || []; const generated = (metadata.generated || new Date().toISOString()).split('T')[0]; const grouped = groupByType(resources); const catalog = `# Asset Catalog — ${clientName.replace(/_/g, ' ').split(' ').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')} _Generato: ${generated} | Totale: ${resources.length} risorse_ ## Riepilogo | Tipo | Count | Dimensione Totale | |------|-------|-------------------| ${generateSummary(grouped)} --- ## Immagini (${grouped.images.length}) ${generateImagesTable(grouped.images)} --- ## Video (${grouped.videos.length}) ${generateVideosTable(grouped.videos)} --- ## Documenti (${grouped.documents.length}) ${generateDocumentsTable(grouped.documents)} --- ## Tag Globali ${generateGlobalTags(resources)} --- ## Note - **Ultimo aggiornamento:** ${generated} - **Archivi originali:** \`assets/archive/\` - **Per richiedere risorse:** Contatta @agency-archivist - **Metadata completi:** \`assets/.metadata.json\` `; fs.writeFileSync(outputPath, catalog, 'utf8'); if (verbose) { console.log(`✅ Catalogo generato: ${outputPath}`); } return outputPath; } function main() { const args = process.argv.slice(2); let client = null; let inputPath = null; let outputPath = null; let verbose = false; for (let i = 0; i < args.length; i++) { if (args[i] === '--client' && args[i + 1]) { client = args[++i]; } else if (args[i] === '--input' && args[i + 1]) { inputPath = args[++i]; } else if (args[i] === '--output' && args[i + 1]) { outputPath = args[++i]; } else if (args[i] === '--verbose') { verbose = true; } } if (!client) { console.error('Usage: node generate_catalog.js --client '); console.error('Options: --input, --output, --verbose'); process.exit(1); } // Path const workspace = path.join(os.homedir(), '.openclaw', 'workspace', 'agency-skills-suite'); const clientDir = path.join(workspace, 'clients', client); const assetsDir = path.join(clientDir, 'assets'); if (!fs.existsSync(clientDir)) { console.error(`❌ Cartella cliente non trovata: ${clientDir}`); process.exit(1); } if (!fs.existsSync(assetsDir)) { console.error(`❌ Cartella assets non trovata: ${assetsDir}`); process.exit(1); } // Input/Output path if (!inputPath) { inputPath = path.join(assetsDir, '.metadata.json'); } if (!outputPath) { outputPath = path.join(assetsDir, 'catalog.md'); } if (!fs.existsSync(inputPath)) { console.error(`❌ Metadata non trovati: ${inputPath}`); console.error(' Esegui prima: node scripts/scan_resources.js'); process.exit(1); } if (verbose) { console.log(`📥 Input: ${inputPath}`); console.log(`📝 Output: ${outputPath}`); console.log(); } // Carica metadata const metadata = loadMetadata(inputPath); // Genera catalogo generateCatalog(client, metadata, outputPath, verbose); // Riepilogo const resources = metadata.resources || []; console.log(`\n✅ Catalogo generato!`); console.log(` 📊 Risorse catalogate: ${resources.length}`); console.log(` 📁 Catalogo: ${outputPath}`); console.log(`\n👉 Il catalogo è pronto per essere usato dalle altre skill!`); } main();