#!/usr/bin/env node
// Removed deprecated character-pipeline.js import - using corrected AffogatoClient from guidance notes
import fs from 'fs';
import path from 'path';
import { createInterface } from 'node:readline';
// Load environment variables
import dotenv from 'dotenv';
dotenv.config();
// Configuration
const CONFIG = {
DATABASES: {
CHARACTERS_MASTER: process.env.CHARACTERS_MASTER_DB || '5b96c172-96b2-4b53-a52c-60d4874779f4',
IMAGE_ASSETS: process.env.IMAGE_ASSETS_DB || '33335cfb-1cb4-4bb0-88ba-b5188456749d'
}
};
class CharacterCreator {
constructor() {
// Legacy pipeline removed - using corrected Affogato integration following user guidance notes
// this.pipeline = new CharacterProductionPipeline(process.env.AFFOGATO_API_KEY);
if (!process.env.AFFOGATO_API_KEY) {
console.error('❌ AFFOGATO_API_KEY not found in environment variables');
process.exit(1);
}
}
async createCharacter(characterData) {
console.log('🎭 CHARACTER CREATOR TOOL');
console.log('========================\n');
// Validate input
if (!characterData.name) {
throw new Error('Character name is required');
}
if (!characterData.referenceImagePath) {
throw new Error('Reference image path is required');
}
if (!fs.existsSync(characterData.referenceImagePath)) {
throw new Error(`Reference image not found: ${characterData.referenceImagePath}`);
}
return await this.pipeline.createCharacterFromDescription(characterData);
}
async createScene(characterName, sceneData) {
console.log('🎬 SCENE CREATOR TOOL');
console.log('====================\n');
return await this.pipeline.createSceneFromNotion(characterName, sceneData);
}
// Interactive character creation
async interactiveCreate() {
const readline = createInterface({
input: process.stdin,
output: process.stdout
});
const ask = (question) => new Promise((resolve) => {
readline.question(question, resolve);
});
try {
console.log('🎭 Interactive Character Creator');
console.log('================================\n');
const name = await ask('Character name: ');
const description = await ask('Character description: ');
const imagePath = await ask('Reference image path: ');
const typeInput = await ask('Character type (Main Character/Supporting Character/Background Character/Narrator) [Main Character]: ');
const traitsInput = await ask('Personality traits (comma separated): ');
const characterType = typeInput || 'Main Character';
const personalityTraits = traitsInput ? traitsInput.split(',').map(t => t.trim()) : [];
const characterData = {
name: name,
description: description,
referenceImagePath: imagePath,
characterType: characterType,
personalityTraits: personalityTraits
};
console.log('\n🚀 Starting character creation...\n');
const result = await this.createCharacter(characterData);
if (result.success) {
console.log('\n✅ Character creation successful!');
console.log(`Character "${result.character.name}" is ready for production.`);
} else {
console.log('\n❌ Character creation failed:', result.error);
}
} catch (error) {
console.error('❌ Error:', error.message);
} finally {
readline.close();
}
}
// List existing characters
async listCharacters() {
try {
const notion = await this.pipeline.getNotionClient();
const response = await notion.databases.query({
database_id: CONFIG.DATABASES.CHARACTERS_MASTER
});
console.log('🎭 EXISTING CHARACTERS');
console.log('=====================\n');
if (response.results.length === 0) {
console.log('No characters found in database.');
return;
}
response.results.forEach((character, index) => {
const name = character.properties['Character Name']?.title[0]?.text?.content || 'Unnamed';
const type = character.properties['Character Type']?.select?.name || 'Unknown';
const status = character.properties['Status']?.select?.name || 'Unknown';
const affogatoId = character.properties['Voice ID']?.rich_text[0]?.text?.content || 'Not set';
console.log(`${index + 1}. ${name}`);
console.log(` Type: ${type}`);
console.log(` Status: ${status}`);
console.log(` Affogato ID: ${affogatoId}`);
console.log('');
});
} catch (error) {
console.error('❌ Error listing characters:', error.message);
}
}
// Quick character creation from command line args
async quickCreate(name, description, imagePath) {
const result = await this.createCharacter({
name: name,
description: description,
referenceImagePath: imagePath,
characterType: 'Main Character',
personalityTraits: []
});
return result;
}
}
// CLI Interface
async function main() {
const creator = new CharacterCreator();
const args = process.argv.slice(2);
const command = args[0];
try {
switch (command) {
case 'interactive':
case 'i':
await creator.interactiveCreate();
break;
case 'list':
case 'l':
await creator.listCharacters();
break;
case 'quick':
case 'q':
if (args.length < 4) {
console.error('Usage: npm run create quick <name> <description> <image-path>');
console.error('Example: npm run create quick "Maya" "A curious scientist" "./maya.png"');
process.exit(1);
}
const result = await creator.quickCreate(args[1], args[2], args[3]);
if (result.success) {
console.log('✅ Character created successfully!');
} else {
console.log('❌ Character creation failed:', result.error);
}
break;
case 'scene':
case 's':
if (args.length < 2) {
console.error('Usage: npm run create scene <character-name> [scene-title] [scene-description]');
process.exit(1);
}
const characterName = args[1];
const sceneTitle = args[2] || 'Untitled Scene';
const sceneDescription = args[3] || 'Character in a scene';
const sceneResult = await creator.createScene(characterName, {
sceneTitle: sceneTitle,
sceneDescription: sceneDescription,
generateVideo: true
});
if (sceneResult.success) {
console.log('✅ Scene created successfully!');
} else {
console.log('❌ Scene creation failed:', sceneResult.error);
}
break;
case 'test':
case 't':
// Test the system with sample data (if test image exists)
const testImagePath = path.join('attached_assets', 'test-character.png');
if (fs.existsSync(testImagePath)) {
const testResult = await creator.createCharacter({
name: 'Test Character',
description: 'A friendly test character for pipeline validation',
referenceImagePath: testImagePath,
characterType: 'Main Character',
personalityTraits: ['Friendly', 'Helpful']
});
if (testResult.success) {
console.log('✅ Pipeline test successful!');
} else {
console.log('❌ Pipeline test failed:', testResult.error);
}
} else {
console.log('❌ Test image not found. Please upload a test-character.png to attached_assets folder.');
console.log('You can test with any image file instead:');
console.log('npm run create quick "Test" "A test character" "path/to/your/image.png"');
}
break;
default:
console.log('🎭 Character Production System');
console.log('==============================\n');
console.log('Available commands:');
console.log(' npm run create interactive - Interactive character creation');
console.log(' npm run create list - List existing characters');
console.log(' npm run create quick <name> <description> <image-path>');
console.log(' npm run create scene <name> [title] [description]');
console.log(' npm run create test - Test the pipeline');
console.log('\nShort versions:');
console.log(' npm run create i - Interactive mode');
console.log(' npm run create l - List characters');
console.log(' npm run create q ... - Quick create');
console.log(' npm run create s ... - Create scene');
console.log(' npm run create t - Test');
console.log('\nExamples:');
console.log(' npm run create quick "Maya" "A curious scientist" "./maya.png"');
console.log(' npm run create scene "Maya" "Happy Scene" "Maya smiling in garden"');
break;
}
} catch (error) {
console.error('❌ Error:', error.message);
process.exit(1);
}
}
// Run if called directly
if (import.meta.url === `file://${process.argv[1]}`) {
main().catch((error) => {
console.error('❌ Fatal error:', error.message);
process.exit(1);
});
}
export { CharacterCreator };