import fs from 'node:fs'; import path from 'node:path'; const root = process.argv[2]; if (!root) throw new Error('source root argument is required'); function patch(relativePath, transform) { const filename = path.join(root, relativePath); const before = fs.readFileSync(filename, 'utf8'); const after = transform(before); if (after === before) throw new Error(`patch made no change: ${relativePath}`); fs.writeFileSync(filename, after); } function replaceOnce(text, before, after, label) { const first = text.indexOf(before); if (first < 0) throw new Error(`patch anchor missing: ${label}`); if (text.indexOf(before, first + 1) >= 0) throw new Error(`patch anchor repeated: ${label}`); return text.slice(0, first) + after + text.slice(first + before.length); } patch('server/src/services/acestep.ts', (text) => { text = replaceOnce( text, "const AUDIO_DIR = path.join(__dirname, '../../public/audio');", 'const AUDIO_DIR = config.storage.audioDir;', 'persistent generated audio', ); const oldArgs = ` params.instruction || 'Fill the audio semantic mask with the style described in the text prompt.', // 17: Instruction params.audioCoverStrength ?? 1.0, // 18: Audio Cover Strength 0.0, // 19: Cover Noise Strength (ACE-Step v1.5 new param, default 0.0) (params.taskType === 'audio2audio' ? 'cover' : params.taskType) || 'text2music', // 20: Task Type params.useAdg ?? false, // 21: Use ADG params.cfgIntervalStart ?? 0.0, // 22: CFG Interval Start params.cfgIntervalEnd ?? 1.0, // 23: CFG Interval End params.shift ?? 3.0, // 24: Shift params.inferMethod || 'ode', // 25: Inference Method params.customTimesteps || '', // 26: Custom Timesteps params.audioFormat || 'mp3', // 27: Audio Format params.lmTemperature ?? 0.85, // 28: LM Temperature isThinking, // 29: Think params.lmCfgScale ?? 2.0, // 30: LM CFG Scale params.lmTopK ?? 0, // 31: LM Top-K params.lmTopP ?? 0.9, // 32: LM Top-P params.lmNegativePrompt || 'NO USER INPUT', // 33: LM Negative Prompt useCot ? (params.useCotMetas ?? true) : false, // 34: CoT Metas useCot ? (params.useCotCaption ?? true) : false, // 35: CaptionRewrite useCot ? (params.useCotLanguage ?? true) : false, // 36: CoT Language params.isFormatCaption ?? false, // 37: Is Format Caption State params.constrainedDecodingDebug ?? false, // 38: Constrained Decoding Debug params.allowLmBatch ?? true, // 39: ParallelThinking params.getScores ?? false, // 40: Auto Score params.getLrc ?? false, // 41: Auto LRC (timestamped lyrics) params.scoreScale ?? 0.5, // 42: Quality Score Sensitivity (0.01-1.0) params.lmBatchChunkSize ?? 8, // 43: LM Batch Chunk Size params.trackName || null, // 44: Track Name params.completeTrackClasses || [], // 45: Track Names true, // 46: Enable Normalization (ACE-Step v1.5, default true) -1.0, // 47: Normalization DB (ACE-Step v1.5, default -1.0) 0.0, // 48: Latent Shift (ACE-Step v1.5, default 0.0) 1.0, // 49: Latent Rescale (ACE-Step v1.5, default 1.0) params.autogen ?? false, // 50: AutoGen`; const newArgs = ` params.instruction || 'Fill the audio semantic mask based on the given conditions:', // 17: Instruction params.audioCoverStrength ?? 1.0, // 18: LM Codes Strength 0.0, // 19: Cover Strength false, // 20: no_fsq params.useAdg ?? false, // 21: Use ADG params.cfgIntervalStart ?? 0.0, // 22: CFG Interval Start params.cfgIntervalEnd ?? 1.0, // 23: CFG Interval End params.shift ?? 1.0, // 24: Shift params.inferMethod || 'ode', // 25: Inference Method 'euler', // 26: Sampler Mode 0.0, // 27: Velocity Norm Threshold 0.0, // 28: Velocity EMA Factor false, // 29: Enable DCW 'double', // 30: DCW Mode 0.02, // 31: DCW Scaler 0.06, // 32: DCW High Scaler 'haar', // 33: DCW Wavelet params.customTimesteps || '', // 34: Custom Timesteps params.audioFormat || 'flac', // 35: Audio Format '320k', // 36: MP3 Bitrate 48000, // 37: MP3 Sample Rate params.lmTemperature ?? 0.85, // 38: LM Temperature isThinking, // 39: Think params.lmCfgScale ?? 2.0, // 40: LM CFG Scale params.lmTopK ?? 0, // 41: LM Top-K params.lmTopP ?? 0.9, // 42: LM Top-P params.lmNegativePrompt || 'NO USER INPUT', // 43: LM Negative Prompt useCot ? (params.useCotMetas ?? true) : false, // 44: CoT Metas useCot ? (params.useCotCaption ?? true) : false, // 45: CaptionRewrite useCot ? (params.useCotLanguage ?? true) : false, // 46: CoT Language params.constrainedDecodingDebug ?? false, // 47: Constrained Decoding Debug params.allowLmBatch ?? true, // 48: ParallelThinking params.getScores ?? false, // 49: Auto Score params.getLrc ?? false, // 50: Auto LRC params.scoreScale ?? 0.5, // 51: Quality Score Sensitivity params.lmBatchChunkSize ?? 8, // 52: LM Batch Chunk Size params.trackName || null, // 53: Track Name params.completeTrackClasses || [], // 54: Track Names true, // 55: Enable Normalization -1.0, // 56: Target Peak (dB) 0.0, // 57: Fade In 0.0, // 58: Fade Out 0.0, // 59: Latent Shift 1.0, // 60: Latent Rescale 'balanced', // 61: Repaint Mode 0.5, // 62: Repaint Strength 0.0, // 63: variance '', // 64: repaint seed false, // 65: Edit null, // 66: source caption null, // 67: source lyrics 0.0, // 68: n_min 1.0, // 69: n_max 1, // 70: n_avg params.autogen ?? false, // 71: AutoGen`; return replaceOnce(text, oldArgs, newArgs, 'Athena Gradio argument schema'); }); patch('server/src/services/storage/local.ts', (text) => { text = replaceOnce( text, "import type { StorageProvider } from './index.js';", "import type { StorageProvider } from './index.js';\nimport { config } from '../../config/index.js';", 'storage config import', ); return replaceOnce( text, "const AUDIO_DIR = path.join(__dirname, '../../../public/audio');", 'const AUDIO_DIR = config.storage.audioDir;', 'persistent uploaded audio', ); }); patch('server/src/index.ts', (text) => replaceOnce( text, "app.use('/audio', express.static(path.join(__dirname, '../public/audio')));", "app.use('/audio', express.static(config.storage.audioDir));", 'persistent audio static route', )); patch('server/src/routes/referenceTrack.ts', (text) => { text = replaceOnce( text, "import { spawn } from 'child_process';", "import { spawn } from 'child_process';\nimport { config } from '../config/index.js';", 'reference audio config import', ); return replaceOnce( text, "const AUDIO_DIR = path.join(__dirname, '../../public/audio');", 'const AUDIO_DIR = config.storage.audioDir;', 'persistent reference audio', ); }); patch('server/src/routes/generate.ts', (text) => { text = replaceOnce( text, " const ALL_DIT_MODELS = [\n 'acestep-v15-turbo',", " const ALL_DIT_MODELS = [\n 'acestep-v15-xl-sft', // Athena production model\n 'acestep-v15-turbo',", 'XL-SFT model list', ); const start = text.indexOf("router.get('/limits'"); const end = text.indexOf("router.get('/debug/", start); if (start < 0 || end < 0) throw new Error('limits route anchors missing'); const limits = `router.get('/limits', async (_req, res: Response) => { // The UI container intentionally has no CUDA or ACE-Step Python runtime. // These are the limits reported by Athena's dedicated RTX 5080 worker. res.json({ tier: process.env.ACESTEP_TIER || 'tier5', gpu_memory_gb: Number(process.env.ACESTEP_GPU_MEMORY_GB || 15.5), max_duration_with_lm: Number(process.env.ACESTEP_MAX_DURATION_WITH_LM || 480), max_duration_without_lm: Number(process.env.ACESTEP_MAX_DURATION_WITHOUT_LM || 600), max_batch_size_with_lm: Number(process.env.ACESTEP_MAX_BATCH_WITH_LM || 4), max_batch_size_without_lm: Number(process.env.ACESTEP_MAX_BATCH_WITHOUT_LM || 4), }); }); `; return text.slice(0, start) + limits + text.slice(end); });