@j-o-r/hello-dave 0.1.1 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -25
- package/README.md +81 -221
- package/TODO.md +173 -35
- package/agents/agent_creator.js +105 -0
- package/agents/agent_creator.prompt.md +371 -0
- package/agents/ask_agent.js +64 -127
- package/agents/claude_agent.js +68 -0
- package/agents/code_agent.js +55 -135
- package/agents/code_agent.prompt.md +50 -0
- package/agents/echo_agent.js +76 -0
- package/agents/financial_expert.js +75 -0
- package/agents/gpt_agent.js +52 -103
- package/agents/gpt_code.js +81 -0
- package/agents/grok_agent.js +58 -114
- package/agents/minimax_agent.js +92 -0
- package/agents/mureka_agent.js +77 -0
- package/agents/planner_agent.js +172 -0
- package/agents/stability_agent.js +87 -0
- package/agents/test_agent.js +75 -157
- package/agents/weather_agent.js +73 -0
- package/agents/workflow_agent.js +189 -0
- package/bin/dave.js +436 -184
- package/docs/bin-dave.md +85 -35
- package/docs/cdn-ssh.md +100 -0
- package/docs/creating-agents.md +301 -0
- package/docs/creating-toolsets.md +336 -0
- package/docs/docs-organization.md +48 -0
- package/docs/project-overview.md +86 -51
- package/lib/API/elevenlabs.io/music.compose.md +441 -0
- package/lib/API/elevenlabs.io/music.create-composition-plan.md +370 -0
- package/lib/API/elevenlabs.io/music.stream.md +425 -0
- package/lib/API/lalal.ai/lalal.js +445 -0
- package/lib/API/lalal.ai/openapi.json +2614 -0
- package/lib/API/minimax/ImageToolset.js +82 -37
- package/lib/API/minimax/MusicToolset.js +125 -79
- package/lib/API/minimax/VideoToolset.js +170 -167
- package/lib/API/minimax/image.js +5 -1
- package/lib/API/minimax/music.js +210 -23
- package/lib/API/minimax/video.js +242 -53
- package/lib/API/mureka/MusicToolset.js +646 -0
- package/lib/API/mureka/README.md +41 -0
- package/lib/API/mureka/index.js +7 -0
- package/lib/API/mureka/music.js +658 -0
- package/lib/API/openai.com/index.js +7 -0
- package/lib/API/openai.com/{reponses/text.js → responses.js} +64 -18
- package/lib/API/openai.com/video.create.character.md +40 -0
- package/lib/API/openai.com/video.create.md +219 -0
- package/lib/API/openai.com/video.delete.md +44 -0
- package/lib/API/openai.com/video.download.md +31 -0
- package/lib/API/openai.com/video.edit.md +155 -0
- package/lib/API/openai.com/video.extend.md +166 -0
- package/lib/API/openai.com/video.fetch.character.md +43 -0
- package/lib/API/openai.com/video.js +784 -0
- package/lib/API/openai.com/video.list.md +201 -0
- package/lib/API/openai.com/video.remix.md +175 -0
- package/lib/API/openai.com/video.retrieve.md +139 -0
- package/lib/API/openai.com/videoToolset.js +616 -0
- package/lib/API/stability.ai/ImageToolset.js +131 -40
- package/lib/API/stability.ai/MusicToolset.js +79 -47
- package/lib/API/stability.ai/audio.js +63 -131
- package/lib/API/x.ai/chat.responses.md +1040 -0
- package/lib/API/x.ai/image.js +229 -59
- package/lib/API/x.ai/imageToolset.js +376 -0
- package/lib/API/x.ai/index.js +1 -1
- package/lib/API/x.ai/responses.js +9 -18
- package/lib/Agent.js +271 -0
- package/lib/Agent.js.old +284 -0
- package/lib/AgentLauncher.js +593 -0
- package/lib/Cli.js +87 -13
- package/lib/Prompt.js +23 -1
- package/lib/Session.js +5 -4
- package/lib/ToolSet.js +102 -6
- package/lib/agentLoader.js +369 -0
- package/lib/cdn.js +67 -231
- package/lib/{CdnToolset.js → cdnToolset.js} +47 -64
- package/lib/defaultToolsets.js +43 -0
- package/lib/fafs.js +1 -1
- package/lib/genericToolset.js +442 -119
- package/lib/handOffToolset.js +179 -0
- package/lib/index.js +34 -27
- package/lib/toolsetLoader.js +248 -0
- package/package.json +10 -4
- package/types/API/lalal.ai/lalal.d.ts +116 -0
- package/types/API/minimax/image.d.ts +2 -1
- package/types/API/minimax/music.d.ts +189 -26
- package/types/API/minimax/video.d.ts +100 -31
- package/types/API/mureka/index.d.ts +7 -0
- package/types/API/mureka/music.d.ts +472 -0
- package/types/API/openai.com/index.d.ts +7 -0
- package/types/API/openai.com/{reponses/text.d.ts → responses.d.ts} +11 -11
- package/types/API/openai.com/video.d.ts +409 -0
- package/types/API/openai.com/videoToolset.d.ts +24 -0
- package/types/API/stability.ai/audio.d.ts +14 -103
- package/types/API/stability.ai/image.d.ts +2 -2
- package/types/API/x.ai/image.d.ts +138 -26
- package/types/API/x.ai/imageToolset.d.ts +3 -0
- package/types/API/x.ai/index.d.ts +1 -1
- package/types/API/x.ai/responses.d.ts +4 -4
- package/types/Agent.d.ts +123 -0
- package/types/AgentLauncher.d.ts +250 -0
- package/types/Cli.d.ts +28 -8
- package/types/Prompt.d.ts +23 -5
- package/types/Session.d.ts +1 -1
- package/types/ToolSet.d.ts +10 -0
- package/types/agentLoader.d.ts +78 -0
- package/types/cdn.d.ts +15 -90
- package/types/defaultToolsets.d.ts +9 -0
- package/types/fafs.d.ts +1 -1
- package/types/genericToolset.d.ts +1 -1
- package/types/handOffToolset.d.ts +28 -0
- package/types/index.d.ts +19 -17
- package/types/toolsetLoader.d.ts +114 -0
- package/utils/format_log.js +101 -23
- package/utils/launch_agent.js +18 -0
- package/utils/list_sessions.sh +13 -5
- package/utils/search_sessions.sh +65 -29
- package/utils/toolsets.js +33 -0
- package/README.md.bak.1779452127 +0 -240
- package/agents/codeserver.sh +0 -47
- package/agents/daisy_agent.js +0 -173
- package/agents/docs_agent.js +0 -148
- package/agents/memory_agent.js +0 -263
- package/agents/minimax.js +0 -173
- package/agents/npm_agent.js +0 -202
- package/agents/prompt_agent.js +0 -133
- package/agents/readme_agent.js +0 -148
- package/agents/spawn_agent.js +0 -160
- package/agents/stability.js +0 -173
- package/agents/todo_agent.js +0 -175
- package/bin/codeDave +0 -58
- package/docs/agent-dave-websocket-protocol.md +0 -180
- package/docs/agent-manager.md +0 -244
- package/docs/codeserver-pattern.md +0 -191
- package/docs/generic-toolset.md +0 -326
- package/docs/howtos/agent-networking.md +0 -253
- package/docs/howtos/spawn-agents.md.bak +0 -200
- package/docs/howtos/spawn-agents.md.bak_new +0 -200
- package/docs/multi-agent-clusters.md +0 -265
- package/docs/music-toolsets.md +0 -137
- package/docs/path-resolution-best-practices.md +0 -104
- package/docs/plans/minimax-music-generation.md +0 -80
- package/docs/plans/unified-agent-architecture.md +0 -146
- package/docs/plans/websocket-streaming-plan.md.bak +0 -317
- package/docs/prompt/spawn_agent.md +0 -175
- package/docs/prompt/spawn_agent.md.bak +0 -201
- package/docs/prompt/task_clarification_and_documentation.md +0 -35
- package/docs/prompt-class.md +0 -141
- package/docs/todo-archive-infra-2026-04-21.md +0 -15
- package/docs/todo-archive-v0.0.8.md +0 -1
- package/docs/todo-archive-v0.1.0.md +0 -32
- package/docs/todo-archive.md +0 -44
- package/docs/tools-syntax-validation.md +0 -121
- package/docs/toolset.md +0 -164
- package/docs/xai-responses.md +0 -111
- package/docs/xai_collections.md +0 -106
- package/lib/API/x.ai/ImageToolset.js +0 -165
- package/lib/API/x.ai/text.js +0 -415
- package/lib/AgentClient.js +0 -248
- package/lib/AgentManager.js +0 -245
- package/lib/AgentServer.js +0 -404
- package/lib/wsCli.js +0 -287
- package/lib/wsIO.js +0 -90
- package/types/API/x.ai/text.d.ts +0 -286
- package/types/AgentClient.d.ts +0 -109
- package/types/AgentManager.d.ts +0 -100
- package/types/AgentServer.d.ts +0 -89
- package/types/wsCli.d.ts +0 -17
- package/types/wsIO.d.ts +0 -30
- package/utils/test.sh +0 -46
- /package/docs/{suggestions.md → _notes/token-counts.md} +0 -0
- /package/lib/API/openai.com/{reponses/MESSAGES.md → MESSAGES.md} +0 -0
- /package/types/API/{x.ai/ImageToolset.d.ts → mureka/MusicToolset.d.ts} +0 -0
- /package/types/{CdnToolset.d.ts → cdnToolset.d.ts} +0 -0
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file lib/API/mureka/music.js
|
|
3
|
+
* @module mureka/music
|
|
4
|
+
* @description Comprehensive pure HTTP wrapper for the official Mureka AI Music API.
|
|
5
|
+
*
|
|
6
|
+
* This module provides typed, well-documented access to **all major Mureka operations**.
|
|
7
|
+
* Most generation methods support automatic polling and return rich results.
|
|
8
|
+
*
|
|
9
|
+
* @see https://platform.mureka.ai/docs/
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* ============================================================
|
|
13
|
+
TYPE DEFINITIONS
|
|
14
|
+
============================================================ */
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {Object} MurekaWord
|
|
18
|
+
* @property {number} start - Start time in milliseconds
|
|
19
|
+
* @property {number} end - End time in milliseconds
|
|
20
|
+
* @property {string} text - The word text
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {Object} MurekaLine
|
|
25
|
+
* @property {number} start - Start time in milliseconds
|
|
26
|
+
* @property {number} end - End time in milliseconds
|
|
27
|
+
* @property {string} text - Full line text
|
|
28
|
+
* @property {MurekaWord[]} [words] - Word-level timing (optional)
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {Object} MurekaLyricsSection
|
|
33
|
+
* @property {string} section_type - e.g. "intro", "verse", "chorus", "bridge"
|
|
34
|
+
* @property {number} [start] - Section start time (ms)
|
|
35
|
+
* @property {number} [end] - Section end time (ms)
|
|
36
|
+
* @property {MurekaLine[]} [lines] - Lines with timing
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {Object} MurekaChoice
|
|
41
|
+
* @property {string} url - Primary MP3 URL
|
|
42
|
+
* @property {string} [flac_url] - FLAC version
|
|
43
|
+
* @property {string} [wav_url] - WAV version
|
|
44
|
+
* @property {number} duration - Duration in milliseconds
|
|
45
|
+
* @property {string} [id] - Choice-specific ID
|
|
46
|
+
* @property {number} [index] - Choice index (0-based)
|
|
47
|
+
* @property {MurekaLyricsSection[]} [lyrics_sections] - Detailed timed lyrics
|
|
48
|
+
* @property {string} [local_path] - Locally saved MP3 path (added by wrapper)
|
|
49
|
+
* @property {string} [local_flac_path] - Locally saved FLAC path (added by wrapper)
|
|
50
|
+
* @property {string} [local_wav_path] - Locally saved WAV path (added by wrapper)
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @typedef {Object} MurekaGenerationResult
|
|
55
|
+
* @property {string} id - Main task/result ID
|
|
56
|
+
* @property {number} created_at - Unix timestamp (seconds)
|
|
57
|
+
* @property {number} [finished_at] - Unix timestamp when finished
|
|
58
|
+
* @property {string} [model] - Model used (e.g. "mureka-9")
|
|
59
|
+
* @property {string} status - "succeeded", "processing", "failed", etc.
|
|
60
|
+
* @property {MurekaChoice[]} [choices] - Array of generated audio variants (ALL auto-saved)
|
|
61
|
+
* @property {string} [trace_id] - Request trace ID for debugging
|
|
62
|
+
* @property {string} [mp3_url] - Legacy top-level MP3 (for backward compatibility)
|
|
63
|
+
* @property {string} [audio_url] - Legacy top-level audio URL
|
|
64
|
+
* @property {string[]} [local_paths] - Array of all locally saved MP3 paths (added by wrapper)
|
|
65
|
+
* @property {Object} [raw] - Original raw API response
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/* ============================================================
|
|
69
|
+
CONSTANTS & CORE HELPERS
|
|
70
|
+
============================================================ */
|
|
71
|
+
|
|
72
|
+
import { request as doRequest } from '@j-o-r/apiserver';
|
|
73
|
+
import fs from 'fs/promises';
|
|
74
|
+
import path from 'path';
|
|
75
|
+
|
|
76
|
+
const BASE_URL = 'https://api.mureka.ai';
|
|
77
|
+
const TMP_DIR = path.join(process.cwd(), '.cache', 'mureka');
|
|
78
|
+
|
|
79
|
+
const POLL_INTERVAL_MS = 10000;
|
|
80
|
+
const MAX_POLL_ATTEMPTS = 24;
|
|
81
|
+
const MAX_RETRIES = 3;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Official valid track generate types (Title Case).
|
|
85
|
+
* These are the only accepted values for `type` / `generate_type`.
|
|
86
|
+
*/
|
|
87
|
+
const TRACK_GENERATE_TYPES = [
|
|
88
|
+
'Vocals',
|
|
89
|
+
'Instrumental',
|
|
90
|
+
'Drums',
|
|
91
|
+
'Bass',
|
|
92
|
+
'Guitar',
|
|
93
|
+
'Keyboard',
|
|
94
|
+
'Percussion',
|
|
95
|
+
'Strings',
|
|
96
|
+
'Synth',
|
|
97
|
+
'FX',
|
|
98
|
+
'Brass',
|
|
99
|
+
'Woodwinds'
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Validates a track generate type.
|
|
104
|
+
* Throws a clear error if the type is not in TRACK_GENERATE_TYPES.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} type
|
|
107
|
+
*/
|
|
108
|
+
function validateTrackGenerateType(type) {
|
|
109
|
+
if (!type) {
|
|
110
|
+
throw new Error('track generate type is required');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const valid = TRACK_GENERATE_TYPES.find(t => t === type);
|
|
114
|
+
|
|
115
|
+
if (!valid) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Invalid track generate type: "${type}". ` +
|
|
118
|
+
`Valid types: ${TRACK_GENERATE_TYPES.join(', ')}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return valid; // return the correct Title Case version
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Returns authentication headers for Mureka API requests.
|
|
128
|
+
* @returns {{'Authorization': string}}
|
|
129
|
+
* @throws {Error} If MUREKA_API_KEY is not set.
|
|
130
|
+
* @see https://platform.mureka.ai/docs/
|
|
131
|
+
*/
|
|
132
|
+
const getHeaders = () => {
|
|
133
|
+
if (!process.env.MUREKA_API_KEY) {
|
|
134
|
+
throw new Error('Missing MUREKA_API_KEY! Please export MUREKA_API_KEY=your_key');
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
'Authorization': `Bearer ${process.env.MUREKA_API_KEY}`
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Saves audio data (URL or base64) to a local file.
|
|
143
|
+
* @param {string|Buffer} audioData
|
|
144
|
+
* @param {string} [filenamePrefix='mureka']
|
|
145
|
+
* @returns {Promise<string>} Local file path
|
|
146
|
+
*/
|
|
147
|
+
/**
|
|
148
|
+
* Saves audio data (URL or base64) to a local file in the .cache/mureka directory.
|
|
149
|
+
* @param {string|Buffer} audioData - URL, base64 string, or Buffer of the audio.
|
|
150
|
+
* @param {string} [filenamePrefix="mureka"] - Prefix for the saved filename.
|
|
151
|
+
* @returns {Promise<string>} The local file path of the saved audio.
|
|
152
|
+
*/
|
|
153
|
+
async function saveAudioToLocal(audioData, filenamePrefix = 'mureka') {
|
|
154
|
+
await fs.mkdir(TMP_DIR, { recursive: true });
|
|
155
|
+
const filename = `${filenamePrefix}-${Date.now()}.mp3`;
|
|
156
|
+
const localPath = path.join(TMP_DIR, filename);
|
|
157
|
+
|
|
158
|
+
if (typeof audioData === 'string' && audioData.startsWith('http')) {
|
|
159
|
+
const response = await fetch(audioData);
|
|
160
|
+
if (!response.ok) throw new Error(`Download failed: ${response.status}`);
|
|
161
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
162
|
+
await fs.writeFile(localPath, buffer);
|
|
163
|
+
} else if (typeof audioData === 'string') {
|
|
164
|
+
const buffer = Buffer.from(audioData, 'base64');
|
|
165
|
+
await fs.writeFile(localPath, buffer);
|
|
166
|
+
} else {
|
|
167
|
+
await fs.writeFile(localPath, audioData);
|
|
168
|
+
}
|
|
169
|
+
return localPath;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Saves all audio variants (MP3, FLAC, WAV) from a generation result to local files.
|
|
174
|
+
* @private
|
|
175
|
+
* @param {Object} result - The API response containing choices.
|
|
176
|
+
* @returns {Promise<Object>} The result with local_path fields added.
|
|
177
|
+
*/
|
|
178
|
+
async function saveAllAudioChoices(result) {
|
|
179
|
+
const localPaths = [];
|
|
180
|
+
if (result.choices && Array.isArray(result.choices)) {
|
|
181
|
+
for (let i = 0; i < result.choices.length; i++) {
|
|
182
|
+
const choice = result.choices[i];
|
|
183
|
+
if (choice.url) {
|
|
184
|
+
choice.local_path = await saveAudioToLocal(choice.url, `choice-${i}`);
|
|
185
|
+
localPaths.push(choice.local_path);
|
|
186
|
+
}
|
|
187
|
+
if (choice.flac_url) choice.local_flac_path = await saveAudioToLocal(choice.flac_url, `choice-${i}-flac`);
|
|
188
|
+
if (choice.wav_url) choice.local_wav_path = await saveAudioToLocal(choice.wav_url, `choice-${i}-wav`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (localPaths.length > 0) result.local_paths = localPaths;
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Internal request helper with retry logic on 429 rate limits.
|
|
197
|
+
* Always includes the full URL in error messages.
|
|
198
|
+
* @private
|
|
199
|
+
* @param {string} endpoint - API endpoint (e.g. "/v1/song/generate").
|
|
200
|
+
* @param {string} [method="POST"] - HTTP method.
|
|
201
|
+
* @param {Object} [body={}] - Request body.
|
|
202
|
+
* @param {number} [retryCount=0] - Current retry attempt.
|
|
203
|
+
* @returns {Promise<Object>} The API response.
|
|
204
|
+
*/
|
|
205
|
+
async function requestInternal(endpoint, method = 'POST', body = {}, retryCount = 0) {
|
|
206
|
+
const headers = getHeaders();
|
|
207
|
+
const url = `${BASE_URL}${endpoint}`;
|
|
208
|
+
|
|
209
|
+
const res = await doRequest(url, method, headers, body);
|
|
210
|
+
|
|
211
|
+
if (res.status === 429 && retryCount < MAX_RETRIES) {
|
|
212
|
+
const wait = Math.pow(2, retryCount) * 1000;
|
|
213
|
+
await new Promise(r => setTimeout(r, wait));
|
|
214
|
+
return requestInternal(endpoint, method, body, retryCount + 1);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (res.status >= 400) {
|
|
218
|
+
const errMsg = JSON.stringify(res, null, 2);
|
|
219
|
+
throw new Error(`Mureka API error ${res.status} at ${url}:\n${errMsg}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { ...res.response, duration: Date.now() - Date.now(), raw: res.response };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Poll until task completes.
|
|
227
|
+
* @param {string} taskId
|
|
228
|
+
* @param {string} [type='song']
|
|
229
|
+
* @returns {Promise<MurekaGenerationResult>}
|
|
230
|
+
*/
|
|
231
|
+
/**
|
|
232
|
+
* Polls a task until it completes or fails.
|
|
233
|
+
* @param {string} taskId - The task ID to poll.
|
|
234
|
+
* @param {string} [type="song"] - Task type (song, instrumental, tts, etc.).
|
|
235
|
+
* @returns {Promise<MurekaGenerationResult>} The completed result.
|
|
236
|
+
*/
|
|
237
|
+
async function pollUntilDone(taskId, type = 'song') {
|
|
238
|
+
for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
|
|
239
|
+
await new Promise(r => setTimeout(r, POLL_INTERVAL_MS));
|
|
240
|
+
const status = await requestInternal(`/v1/${type}/query/${taskId}`, 'GET');
|
|
241
|
+
if (['completed', 'success', 'succeeded'].includes(status.status)) {
|
|
242
|
+
return await saveAllAudioChoices(status);
|
|
243
|
+
}
|
|
244
|
+
if (['failed', 'error'].includes(status.status)) {
|
|
245
|
+
throw new Error(`Task failed: ${status.error || status.message}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
throw new Error(`Task ${taskId} timed out`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* ============================================================
|
|
252
|
+
AUTO-POLL HELPER
|
|
253
|
+
============================================================ */
|
|
254
|
+
/**
|
|
255
|
+
* Wrapper that automatically polls async generation tasks to completion.
|
|
256
|
+
* @private
|
|
257
|
+
* @param {Function} fn - The API call function.
|
|
258
|
+
* @param {Object} params - Parameters to pass.
|
|
259
|
+
* @param {string} [defaultType="song"] - Default task type for polling.
|
|
260
|
+
* @returns {Promise<MurekaGenerationResult>} Final result after polling.
|
|
261
|
+
*/
|
|
262
|
+
async function withAutoPoll(fn, params, defaultType = 'song') {
|
|
263
|
+
const result = await fn(params);
|
|
264
|
+
if (result.task_id || result.id) {
|
|
265
|
+
return await pollUntilDone(result.task_id || result.id, defaultType);
|
|
266
|
+
}
|
|
267
|
+
return await saveAllAudioChoices(result);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/* ============================================================
|
|
271
|
+
SONG OPERATIONS
|
|
272
|
+
============================================================ */
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Generate a complete song with both lyrics and vocals (text-to-song).
|
|
276
|
+
* Supports reference_id, vocal_id, motif_id, etc.
|
|
277
|
+
* Automatically polls until completion and saves audio locally.
|
|
278
|
+
*
|
|
279
|
+
* @param {Object} [params={}]
|
|
280
|
+
* @param {string} params.prompt - Style/mood/genre description (required).
|
|
281
|
+
* @param {string} [params.lyrics] - Structured lyrics with [Verse], [Chorus], etc.
|
|
282
|
+
* @param {string} [params.model="auto"] - Model to use.
|
|
283
|
+
* @param {string} [params.reference_id] - Reference audio file_id (purpose=reference).
|
|
284
|
+
* @param {string} [params.vocal_id] - Cloned voice ID.
|
|
285
|
+
* @param {string} [params.motif_id] - Melody motif file_id.
|
|
286
|
+
* @returns {Promise<MurekaGenerationResult>} Generation result with choices and local paths.
|
|
287
|
+
*/
|
|
288
|
+
async function generateSong(params = {}) {
|
|
289
|
+
return withAutoPoll((p) => requestInternal('/v1/song/generate', 'POST', p), params, 'song');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Generate pure instrumental music (no vocals).
|
|
294
|
+
*
|
|
295
|
+
* @param {Object} [params={}]
|
|
296
|
+
* @param {string} params.prompt - Instrumental style/mood description (required).
|
|
297
|
+
* @param {string} [params.model="auto"]
|
|
298
|
+
* @param {string} [params.reference_id] - Reference audio (purpose=reference or instrumental).
|
|
299
|
+
* @returns {Promise<MurekaGenerationResult>}
|
|
300
|
+
*/
|
|
301
|
+
async function generateInstrumental(params = {}) {
|
|
302
|
+
return withAutoPoll((p) => requestInternal('/v1/instrumental/generate', 'POST', p), params, 'instrumental');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Generate a musical soundtrack matching an image or video.
|
|
307
|
+
*
|
|
308
|
+
* @param {Object} [params={}]
|
|
309
|
+
* @param {string} [params.image] - Image URL or file_id.
|
|
310
|
+
* @param {string} [params.video] - Video URL or file_id.
|
|
311
|
+
* @param {string} [params.prompt] - Optional style guidance.
|
|
312
|
+
* @returns {Promise<MurekaGenerationResult>}
|
|
313
|
+
*/
|
|
314
|
+
async function generateSoundtrack(params = {}) {
|
|
315
|
+
return withAutoPoll((p) => requestInternal('/v1/soundtrack/generate', 'POST', p), params, 'soundtrack');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Analyze an existing audio file and return structured description (genre, mood, structure, etc.).
|
|
320
|
+
*
|
|
321
|
+
* @param {Object} [params={}]
|
|
322
|
+
* @param {string} params.url - Audio URL or base64 (required).
|
|
323
|
+
* @returns {Promise<Object>} Description result.
|
|
324
|
+
*/
|
|
325
|
+
async function describeSong(params = {}) {
|
|
326
|
+
return withAutoPoll((p) => requestInternal('/v1/song/describe', 'POST', p), params, 'song');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Extend an existing song by adding more sections.
|
|
331
|
+
*
|
|
332
|
+
* @param {Object} [params={}]
|
|
333
|
+
* @param {string} params.song_id - ID from previous generation (required).
|
|
334
|
+
* @param {string} [params.lyrics] - Additional lyrics.
|
|
335
|
+
* @returns {Promise<MurekaGenerationResult>}
|
|
336
|
+
*/
|
|
337
|
+
async function extendSong(params = {}) {
|
|
338
|
+
return withAutoPoll((p) => requestInternal('/v1/song/extend', 'POST', p), params, 'song');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Regenerate a song (or specific parts).
|
|
343
|
+
*
|
|
344
|
+
* @param {Object} [params={}]
|
|
345
|
+
* @returns {Promise<MurekaGenerationResult>}
|
|
346
|
+
*/
|
|
347
|
+
async function regenerateSong(params = {}) {
|
|
348
|
+
return withAutoPoll((p) => requestInternal('/v1/song/regenerate', 'POST', p), params, 'song');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Separate a song into stems (vocals, drums, bass, etc.).
|
|
353
|
+
*
|
|
354
|
+
* @param {Object} [params={}]
|
|
355
|
+
* @param {string} params.url - Audio URL or base64 (required).
|
|
356
|
+
* @param {string} [params.model] - Separation model.
|
|
357
|
+
* @returns {Promise<MurekaGenerationResult>}
|
|
358
|
+
*/
|
|
359
|
+
async function stemSong(params = {}) {
|
|
360
|
+
return withAutoPoll((p) => requestInternal('/v1/song/stem', 'POST', p), params, 'song');
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Recognize/identify a song from audio.
|
|
365
|
+
*
|
|
366
|
+
* @param {Object} [params={}]
|
|
367
|
+
* @param {string} params.url - Audio URL or base64 (required).
|
|
368
|
+
* @returns {Promise<Object>}
|
|
369
|
+
*/
|
|
370
|
+
async function recognizeSong(params = {}) {
|
|
371
|
+
return withAutoPoll((p) => requestInternal('/v1/song/recognize', 'POST', p), params, 'song');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Edit a specific time region of a song (replace lyrics/melody in a section).
|
|
376
|
+
*
|
|
377
|
+
* @param {Object} [params={}]
|
|
378
|
+
* @param {string} params.song_id - Song ID (required).
|
|
379
|
+
* @param {number} params.start_milliseconds - Start time (ms).
|
|
380
|
+
* @param {number} params.end_milliseconds - End time (ms).
|
|
381
|
+
* @param {string} [params.lyrics] - New lyrics for the region.
|
|
382
|
+
* @returns {Promise<MurekaGenerationResult>}
|
|
383
|
+
*/
|
|
384
|
+
async function regionEditSong(params = {}) {
|
|
385
|
+
return withAutoPoll((p) => requestInternal('/v1/song/region-edit', 'POST', p), params, 'song');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Generate a lyrics video from lyrics and background image.
|
|
390
|
+
*
|
|
391
|
+
* @param {Object} [params={}]
|
|
392
|
+
* @returns {Promise<MurekaGenerationResult>}
|
|
393
|
+
*/
|
|
394
|
+
async function generateLyricsVideo(params = {}) {
|
|
395
|
+
return withAutoPoll((p) => requestInternal('/v1/lyrics-video/generate', 'POST', p), params, 'lyrics-video');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Generate a specific track type (Vocals, Instrumental, Drums, etc.) from a song or uploaded audio.
|
|
400
|
+
*
|
|
401
|
+
* @param {Object} [params={}]
|
|
402
|
+
* @param {string} [params.song_id] - Song ID (mutually exclusive with upload_audio_id).
|
|
403
|
+
* @param {string} [params.upload_audio_id] - Upload ID from purpose="audio".
|
|
404
|
+
* @param {string} params.type - Track type (Vocals, Instrumental, Drums, Bass, Guitar, Keyboard, Percussion, Strings, Synth, FX, Brass, Woodwinds).
|
|
405
|
+
* @param {string} [params.generate_type] - Legacy alias for type.
|
|
406
|
+
* @param {string} params.prompt - Control prompt (required).
|
|
407
|
+
* @param {number} [params.generate_start] - Start time (ms).
|
|
408
|
+
* @param {number} [params.generate_end] - End time (ms).
|
|
409
|
+
* @param {string} [params.lyrics] - Lyrics (for Vocals).
|
|
410
|
+
* @param {string} [params.vocal_gender] - "male" or "female".
|
|
411
|
+
* @returns {Promise<MurekaGenerationResult>}
|
|
412
|
+
*/
|
|
413
|
+
async function generateTrack(params = {}) {
|
|
414
|
+
console.log(JSON.stringify(params, null, ' ') );
|
|
415
|
+
// This will throw if invalid
|
|
416
|
+
// validateTrackGenerateType(params.generate_type);
|
|
417
|
+
|
|
418
|
+
return withAutoPoll((p) => requestInternal('/v1/track/generate', 'POST', p), params, 'song');
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Create a remix of an existing song.
|
|
423
|
+
*
|
|
424
|
+
* @param {Object} [params={}]
|
|
425
|
+
* @param {string} [params.song_id] - Song ID.
|
|
426
|
+
* @param {string} [params.upload_audio_id] - Upload ID from purpose="remix".
|
|
427
|
+
* @param {string} params.prompt - Remix style prompt (required).
|
|
428
|
+
* @param {string} params.lyrics - Full lyrics of the target song (required).
|
|
429
|
+
* @param {number} [params.n=2] - Number of variants.
|
|
430
|
+
* @param {string} [params.model="auto"]
|
|
431
|
+
* @returns {Promise<MurekaGenerationResult>}
|
|
432
|
+
*/
|
|
433
|
+
async function remixSong(params = {}) {
|
|
434
|
+
return withAutoPoll((p) => requestInternal('/v1/song/remix', 'POST', p), params, 'song');
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/* ============================================================
|
|
438
|
+
LYRICS
|
|
439
|
+
============================================================ */
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Generate brand new song lyrics from a theme or style prompt.
|
|
443
|
+
*
|
|
444
|
+
* @param {Object} [params={}]
|
|
445
|
+
* @param {string} params.prompt - Theme/style instructions (required).
|
|
446
|
+
* @returns {Promise<Object>} Generated lyrics.
|
|
447
|
+
*/
|
|
448
|
+
async function generateLyrics(params = {}) {
|
|
449
|
+
return requestInternal('/v1/lyrics/generate', 'POST', params);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Extend or continue existing lyrics.
|
|
454
|
+
*
|
|
455
|
+
* @param {Object} [params={}]
|
|
456
|
+
* @param {string} params.lyrics - Existing lyrics (required).
|
|
457
|
+
* @param {string} params.prompt - Instructions for extension (required).
|
|
458
|
+
* @returns {Promise<Object>}
|
|
459
|
+
*/
|
|
460
|
+
async function extendLyrics(params = {}) {
|
|
461
|
+
return requestInternal('/v1/lyrics/extend', 'POST', params);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/* ============================================================
|
|
465
|
+
TTS / SPEECH
|
|
466
|
+
============================================================ */
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Convert text to natural-sounding speech (single-speaker or multi-speaker).
|
|
470
|
+
*
|
|
471
|
+
* @param {Object} [params={}]
|
|
472
|
+
* @param {string} [params.text] - Text for single-speaker mode.
|
|
473
|
+
* @param {Array} [params.script] - Array of {speaker, text} for podcast mode.
|
|
474
|
+
* @param {string} [params.voice_id] - Voice ID for single-speaker.
|
|
475
|
+
* @param {Object} [params.voices] - Map of speaker -> voice_id for podcast.
|
|
476
|
+
* @param {number} [params.speed=1.0]
|
|
477
|
+
* @param {number} [params.pitch=0]
|
|
478
|
+
* @returns {Promise<MurekaGenerationResult>}
|
|
479
|
+
*/
|
|
480
|
+
async function generateTTS(params = {}) {
|
|
481
|
+
return withAutoPoll((p) => requestInternal('/v1/tts/generate', 'POST', p), params, 'tts');
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Generate a multi-speaker podcast.
|
|
486
|
+
*
|
|
487
|
+
* @param {Object} [params={}]
|
|
488
|
+
* @returns {Promise<MurekaGenerationResult>}
|
|
489
|
+
*/
|
|
490
|
+
async function generatePodcast(params = {}) {
|
|
491
|
+
return withAutoPoll((p) => requestInternal('/v1/tts/podcast', 'POST', p), params, 'tts');
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/* ============================================================
|
|
495
|
+
VOCAL CLONING
|
|
496
|
+
============================================================ */
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Create a custom voice clone from a short vocal sample.
|
|
500
|
+
*
|
|
501
|
+
* @param {Object} [params={}]
|
|
502
|
+
* @param {string} params.file - URL or base64 of vocal sample (5-15s recommended).
|
|
503
|
+
* @param {string} [params.name] - Friendly name for the voice.
|
|
504
|
+
* @returns {Promise<Object>} Result containing voice_id.
|
|
505
|
+
*/
|
|
506
|
+
async function cloneVocal(params = {}) {
|
|
507
|
+
return withAutoPoll((p) => requestInternal('/v1/song/vocal-clone', 'POST', p), params, 'vocal');
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/* ============================================================
|
|
511
|
+
FILE UPLOADS
|
|
512
|
+
============================================================ */
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Upload a file for use as reference (audio, image, video, MIDI).
|
|
516
|
+
* Supports multiple purposes (reference, vocal, melody, audio, remix, soundtrack, lyrics-video, etc.).
|
|
517
|
+
*
|
|
518
|
+
* @param {Object} [params={}]
|
|
519
|
+
* @param {string} params.url - Public URL of the file (required).
|
|
520
|
+
* @param {string} params.purpose - Purpose of upload (required).
|
|
521
|
+
* @returns {Promise<Object>} Upload result with file_id.
|
|
522
|
+
*/
|
|
523
|
+
async function uploadFile(params = {}) {
|
|
524
|
+
const formData = new FormData();
|
|
525
|
+
for (const [key, value] of Object.entries(params)) {
|
|
526
|
+
if (value !== undefined && value !== null) {
|
|
527
|
+
formData.append(key, value);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return requestInternal('/v1/files/upload', 'POST', formData);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Create a new upload session.
|
|
535
|
+
*
|
|
536
|
+
* @param {Object} [params={}]
|
|
537
|
+
* @returns {Promise<Object>}
|
|
538
|
+
*/
|
|
539
|
+
async function uploadsCreate(params = {}) {
|
|
540
|
+
return requestInternal('/v1/uploads/create', 'POST', params);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Add data to an upload session.
|
|
545
|
+
*
|
|
546
|
+
* @param {Object} [params={}]
|
|
547
|
+
* @returns {Promise<Object>}
|
|
548
|
+
*/
|
|
549
|
+
async function uploadsAdd(params = {}) {
|
|
550
|
+
return requestInternal('/v1/uploads/add', 'POST', params);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Complete an upload session.
|
|
555
|
+
*
|
|
556
|
+
* @param {Object} [params={}]
|
|
557
|
+
* @returns {Promise<Object>}
|
|
558
|
+
*/
|
|
559
|
+
async function uploadsComplete(params = {}) {
|
|
560
|
+
return requestInternal('/v1/uploads/complete', 'POST', params);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/* ============================================================
|
|
564
|
+
TASK POLLING
|
|
565
|
+
============================================================ */
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Manually query the status of a task.
|
|
569
|
+
*
|
|
570
|
+
* @param {string} taskId - Task ID.
|
|
571
|
+
* @param {string} [type="song"] - Task type.
|
|
572
|
+
* @returns {Promise<Object>} Task status.
|
|
573
|
+
*/
|
|
574
|
+
async function queryTask(taskId, type = 'song') {
|
|
575
|
+
return requestInternal(`/v1/${type}/query/${taskId}`, 'GET');
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/* ============================================================
|
|
579
|
+
ACCOUNT & BILLING
|
|
580
|
+
============================================================ */
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Retrieve current account billing information (balance, credits, usage).
|
|
584
|
+
*
|
|
585
|
+
* @returns {Promise<Object>}
|
|
586
|
+
*/
|
|
587
|
+
async function getBilling() {
|
|
588
|
+
return requestInternal('/v1/account/billing', 'GET');
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Retrieve account profile information.
|
|
593
|
+
*
|
|
594
|
+
* @returns {Promise<Object>}
|
|
595
|
+
*/
|
|
596
|
+
async function getProfile() {
|
|
597
|
+
return requestInternal('/v1/account/profile', 'GET');
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/* ============================================================
|
|
601
|
+
FINE-TUNING
|
|
602
|
+
============================================================ */
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Create a fine-tuning task.
|
|
606
|
+
*
|
|
607
|
+
* @param {Object} [params={}]
|
|
608
|
+
* @returns {Promise<MurekaGenerationResult>}
|
|
609
|
+
*/
|
|
610
|
+
async function createFineTune(params = {}) {
|
|
611
|
+
return withAutoPoll((p) => requestInternal('/v1/finetuning/create', 'POST', p), params, 'finetuning');
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Query the status of a fine-tuning task.
|
|
616
|
+
*
|
|
617
|
+
* @param {string} taskId - Fine-tune task ID.
|
|
618
|
+
* @returns {Promise<Object>}
|
|
619
|
+
*/
|
|
620
|
+
async function queryFineTune(taskId) {
|
|
621
|
+
return requestInternal(`/v1/finetuning/query/${taskId}`, 'GET');
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/* ============================================================
|
|
625
|
+
EXPORTS
|
|
626
|
+
============================================================ */
|
|
627
|
+
|
|
628
|
+
export {
|
|
629
|
+
getHeaders,
|
|
630
|
+
saveAudioToLocal,
|
|
631
|
+
pollUntilDone,
|
|
632
|
+
generateSong,
|
|
633
|
+
generateInstrumental,
|
|
634
|
+
generateSoundtrack,
|
|
635
|
+
describeSong,
|
|
636
|
+
extendSong,
|
|
637
|
+
regenerateSong,
|
|
638
|
+
stemSong,
|
|
639
|
+
recognizeSong,
|
|
640
|
+
regionEditSong,
|
|
641
|
+
generateLyricsVideo,
|
|
642
|
+
generateTrack,
|
|
643
|
+
remixSong,
|
|
644
|
+
generateLyrics,
|
|
645
|
+
extendLyrics,
|
|
646
|
+
generateTTS,
|
|
647
|
+
generatePodcast,
|
|
648
|
+
cloneVocal,
|
|
649
|
+
uploadFile,
|
|
650
|
+
uploadsCreate,
|
|
651
|
+
uploadsAdd,
|
|
652
|
+
uploadsComplete,
|
|
653
|
+
queryTask,
|
|
654
|
+
getBilling,
|
|
655
|
+
getProfile,
|
|
656
|
+
createFineTune,
|
|
657
|
+
queryFineTune
|
|
658
|
+
};
|