assetmax
Version:
Manifest-driven asset management system with contract-based generation
259 lines ⢠11.4 kB
JavaScript
;
/**
* Asset Generation CLI
* Reads manifest and generates missing assets using AI models
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AssetCLI = void 0;
const fs_1 = require("fs");
const path = __importStar(require("path"));
const toml = __importStar(require("@iarna/toml"));
const chalk_1 = __importDefault(require("chalk"));
const ora_1 = __importDefault(require("ora"));
const universal_generator_js_1 = require("../models/universal-generator.js");
const model_registry_js_1 = require("../models/model-registry.js");
class AssetCLI {
manifest = null;
generator;
progress = {
total: 0,
completed: 0,
failed: 0,
totalCost: 0
};
constructor() {
this.generator = new universal_generator_js_1.UniversalImageGenerator();
}
async run(options = {}) {
const manifestFile = options.manifestFile || 'asset-manifest.toml';
console.log(chalk_1.default.blue.bold('šØ AssetMax Generator Starting...\n'));
await this.loadManifest(manifestFile);
await this.initModels();
const assetsToGenerate = await this.getAssetsToGenerate(options.force);
if (assetsToGenerate.length === 0) {
console.log(chalk_1.default.green('ā
All assets already exist!'));
return;
}
if (options.dryRun) {
this.showDryRun(assetsToGenerate);
return;
}
await this.generateAssets(assetsToGenerate);
this.showSummary();
}
async loadManifest(manifestPath) {
if (!(await fs_1.promises.access(manifestPath).then(() => true).catch(() => false))) {
throw new Error(`Manifest not found: ${manifestPath}`);
}
const content = await fs_1.promises.readFile(manifestPath, 'utf-8');
this.manifest = toml.parse(content);
console.log(`š Loaded manifest: ${this.manifest.meta.name} v${this.manifest.meta.version}`);
}
async initModels() {
// Check if API token is available
if (!process.env.REPLICATE_API_TOKEN) {
console.warn('ā ļø REPLICATE_API_TOKEN environment variable is required');
return;
}
// List available models
const allModels = (0, model_registry_js_1.getAllModels)();
console.log(`ā
${allModels.length} image generation models available`);
// Show model categories for user reference
const fastest = allModels.filter(m => m.costPerImage <= 0.01);
if (fastest.length > 0) {
console.log(`ā” Fastest/cheapest: ${fastest.map(m => m.name).join(', ')}`);
}
}
async getAssetsToGenerate(force = false) {
const assets = [];
for (const [groupName, groupConfig] of Object.entries(this.manifest.assets)) {
const category = groupConfig.category;
const subcategory = groupConfig.subcategory;
const format = groupConfig.format;
const model = groupConfig.generation_model;
for (const [assetName, assetConfig] of Object.entries(groupConfig)) {
if (typeof assetConfig === 'object' && 'prompt' in assetConfig) {
const fileName = `${assetName}.${format}`;
const relativePath = subcategory
? `${category}/${subcategory}/${fileName}`
: `${category}/${fileName}`;
const outputPath = path.join(this.manifest.cli.output_dir, relativePath);
const exists = await fs_1.promises.access(outputPath).then(() => true).catch(() => false);
if (!exists || force) {
assets.push({
name: assetName,
groupName,
outputPath,
relativePath,
prompt: assetConfig.prompt,
model,
aspectRatio: assetConfig.aspect_ratio || groupConfig.aspect_ratio || '1:1',
format,
cost: (() => {
try {
return (0, model_registry_js_1.getModelConfig)(model).costPerImage;
}
catch {
return this.manifest?.cli?.pricing?.[model] || 0;
}
})()
});
}
}
}
}
return assets;
}
showDryRun(assets) {
console.log(chalk_1.default.cyan.bold('š DRY RUN - Assets to generate:\n'));
const totalCost = assets.reduce((sum, asset) => sum + asset.cost, 0);
const modelCounts = assets.reduce((counts, asset) => {
counts[asset.model] = (counts[asset.model] || 0) + 1;
return counts;
}, {});
console.log(`š Summary:`);
console.log(` Total assets: ${assets.length}`);
console.log(` Estimated cost: $${totalCost.toFixed(3)}`);
console.log(` Models:`);
for (const [model, count] of Object.entries(modelCounts)) {
let unitCost = 0;
try {
unitCost = (0, model_registry_js_1.getModelConfig)(model).costPerImage;
}
catch {
unitCost = this.manifest?.cli?.pricing?.[model] || 0;
}
const modelCost = count * unitCost;
console.log(` ${model}: ${count} assets ($${modelCost.toFixed(3)})`);
}
console.log('\nš Assets:');
for (const asset of assets) {
console.log(` ${chalk_1.default.yellow(asset.name)} ā ${asset.relativePath} (${asset.model})`);
}
}
async generateAssets(assets) {
this.progress.total = assets.length;
const spinner = (0, ora_1.default)(`Generating ${assets.length} assets...`).start();
for (const asset of assets) {
this.progress.currentAsset = asset.name;
spinner.text = `Generating ${asset.name} (${this.progress.completed + 1}/${this.progress.total})`;
try {
const result = await this.generateSingleAsset(asset);
if (result.success) {
this.progress.completed++;
this.progress.totalCost += result.cost;
}
else {
this.progress.failed++;
console.warn(`\nā ļø Failed to generate ${asset.name}: ${result.error || 'Unknown error'}`);
}
}
catch (error) {
this.progress.failed++;
console.error(`\nā Error generating ${asset.name}:`, error.message);
}
}
spinner.succeed(`Generated ${this.progress.completed} assets`);
}
async generateSingleAsset(asset) {
const startTime = Date.now();
try {
// Ensure output directory exists
await fs_1.promises.mkdir(path.dirname(asset.outputPath), { recursive: true });
// Validate model exists
(0, model_registry_js_1.getModelConfig)(asset.model);
// Generate asset using universal generator
const result = await this.generator.generate({
prompt: asset.prompt,
model: asset.model,
aspectRatio: asset.aspectRatio,
outputFormat: asset.format === 'png' ? 'png' : 'jpg'
});
// Download generated asset
await this.downloadAsset(result.url, asset.outputPath);
const duration = Date.now() - startTime;
return {
success: true,
assetPath: asset.outputPath,
cost: result.cost,
duration
};
}
catch (error) {
return {
success: false,
assetPath: asset.outputPath,
cost: 0,
duration: Date.now() - startTime,
error: error.message
};
}
}
async downloadAsset(url, outputPath) {
const axios = (await Promise.resolve().then(() => __importStar(require('axios')))).default;
const response = await axios.get(url, { responseType: 'stream' });
const writer = require('fs').createWriteStream(outputPath);
response.data.pipe(writer);
return new Promise((resolve, reject) => {
writer.on('finish', resolve);
writer.on('error', reject);
});
}
async convertToPng(filePath) {
// Use sips on macOS to convert to PNG
if (process.platform === 'darwin') {
const { spawn } = require('child_process');
const newPath = filePath.replace(/\.[^.]+$/, '.png');
return new Promise((resolve, reject) => {
const sips = spawn('sips', ['-s', 'format', 'png', filePath, '--out', newPath]);
sips.on('close', (code) => {
if (code === 0) {
// Remove original file
fs_1.promises.unlink(filePath).then(resolve).catch(reject);
}
else {
reject(new Error(`sips conversion failed with code ${code}`));
}
});
});
}
}
showSummary() {
console.log(chalk_1.default.green.bold('\nš Generation Complete!\n'));
console.log(`š Summary:`);
console.log(` ā
Generated: ${chalk_1.default.green(this.progress.completed)} assets`);
console.log(` ā Failed: ${chalk_1.default.red(this.progress.failed)} assets`);
console.log(` š° Total cost: ${chalk_1.default.yellow(`$${this.progress.totalCost.toFixed(3)}`)}`);
if (this.progress.failed > 0) {
console.log(chalk_1.default.yellow('\nā ļø Some assets failed to generate. Check the logs above for details.'));
}
}
}
exports.AssetCLI = AssetCLI;
//# sourceMappingURL=asset-cli.js.map