@runapi.ai/suno 0.2.1

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/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@runapi.ai/suno",
3
+ "version": "0.2.1",
4
+ "description": "RunAPI Suno SDK for JavaScript, Ruby, and Go",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "skills",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "engines": {
22
+ "node": ">=18.0.0"
23
+ },
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "test": "vitest run",
27
+ "typecheck": "tsc --noEmit",
28
+ "clean": "rm -rf dist",
29
+ "test:simple": "dotenv -e manual-tests/.env -- tsx manual-tests/test-generation-simple.ts",
30
+ "test:custom": "dotenv -e manual-tests/.env -- tsx manual-tests/test-generation-custom.ts",
31
+ "test:covers": "dotenv -e manual-tests/.env -- tsx manual-tests/test-covers.ts",
32
+ "test:extension": "dotenv -e manual-tests/.env -- tsx manual-tests/test-extension.ts",
33
+ "test:instrumentals": "dotenv -e manual-tests/.env -- tsx manual-tests/test-instrumentals.ts",
34
+ "test:vocals": "dotenv -e manual-tests/.env -- tsx manual-tests/test-vocals.ts",
35
+ "test:vocal-removal": "dotenv -e manual-tests/.env -- tsx manual-tests/test-vocal-removal.ts",
36
+ "test:conversions": "dotenv -e manual-tests/.env -- tsx manual-tests/test-conversions.ts",
37
+ "test:music-videos": "dotenv -e manual-tests/.env -- tsx manual-tests/test-music-videos.ts",
38
+ "test:lyrics": "dotenv -e manual-tests/.env -- tsx manual-tests/test-lyrics.ts",
39
+ "test:timestamped-lyrics": "dotenv -e manual-tests/.env -- tsx manual-tests/test-timestamped-lyrics.ts",
40
+ "test:section-replacements": "dotenv -e manual-tests/.env -- tsx manual-tests/test-section-replacements.ts",
41
+ "test:personas-styles": "dotenv -e manual-tests/.env -- tsx manual-tests/test-personas-styles.ts",
42
+ "test:midi": "dotenv -e manual-tests/.env -- tsx manual-tests/test-midi.ts",
43
+ "test:errors": "dotenv -e manual-tests/.env -- tsx manual-tests/test-error-handling.ts",
44
+ "test:all": "dotenv -e manual-tests/.env -- tsx manual-tests/test-all.ts",
45
+ "test:manual": "npm run test:all"
46
+ },
47
+ "dependencies": {
48
+ "@runapi.ai/core": "^0.2.1"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^20.0.0",
52
+ "@vitest/coverage-v8": "^1.6.0",
53
+ "dotenv-cli": "^11.0.0",
54
+ "tsup": "^8.0.0",
55
+ "tsx": "^4.7.0",
56
+ "typescript": "^5.4.0",
57
+ "vitest": "^1.6.0"
58
+ },
59
+ "publishConfig": {
60
+ "access": "public"
61
+ },
62
+ "keywords": [
63
+ "runapi",
64
+ "runapi-ai",
65
+ "suno",
66
+ "api",
67
+ "sdk",
68
+ "typescript",
69
+ "ruby",
70
+ "golang"
71
+ ],
72
+ "author": "RunAPI",
73
+ "license": "Apache-2.0",
74
+ "homepage": "https://runapi.ai/models/suno",
75
+ "repository": {
76
+ "type": "git",
77
+ "url": "git+https://github.com/runapi-ai/suno-sdk.git"
78
+ }
79
+ }
@@ -0,0 +1,58 @@
1
+ # Suno AI API Skill for RunAPI
2
+
3
+ Generate music, lyrics, stems, covers, and music videos with the Suno SDK. This skill helps Claude Code, Codex, Gemini CLI, Cursor, and 50+ agents integrate Suno through RunAPI.
4
+
5
+ The canonical agent file is `skills/suno/SKILL.md`.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npx skills add runapi-ai/suno -g
11
+ ```
12
+
13
+ Or manually: clone this repo and copy `skills/suno/` into your agent's skills directory.
14
+
15
+ ## Quick example
16
+
17
+ ```typescript
18
+ import { SunoClient } from '@runapi.ai/suno';
19
+
20
+ const client = new SunoClient();
21
+ const result = await client.textToMusic.run({
22
+ custom_mode: false,
23
+ instrumental: false,
24
+ prompt: 'A chill lo-fi beat with soft vocals',
25
+ model: 'V5',
26
+ });
27
+ const audioUrl = result.audios[0].audio_url;
28
+ ```
29
+
30
+ ## Routing
31
+
32
+ - Model page: https://runapi.ai/models/suno
33
+ - Product docs: https://runapi.ai/docs#suno
34
+ - SDK docs: https://runapi.ai/docs#sdk-suno
35
+ - SDK repository: https://github.com/runapi-ai/suno-sdk
36
+ - Pricing and rate limits: https://runapi.ai/models/suno/v3.5
37
+ - Provider comparison: https://runapi.ai/providers/suno
38
+ - Browse all RunAPI models and skills: https://runapi.ai/models
39
+
40
+ ## Variants
41
+
42
+ - [v3.5](https://runapi.ai/models/suno/v3.5)
43
+ - [v4](https://runapi.ai/models/suno/v4)
44
+ - [v4.5](https://runapi.ai/models/suno/v4.5)
45
+ - [v4.5 all](https://runapi.ai/models/suno/v4.5-all)
46
+ - [v4.5 plus](https://runapi.ai/models/suno/v4.5-plus)
47
+ - [v5](https://runapi.ai/models/suno/v5)
48
+ - [v5.5](https://runapi.ai/models/suno/v5.5)
49
+
50
+ ## Agent rules
51
+
52
+ - Keep API keys in `RUNAPI_API_KEY` or RunAPI CLI config; never commit secrets.
53
+ - Prefer `create`, `get`, and `run` JSON passthrough patterns instead of inventing flags for every model parameter.
54
+ - For suno ai api pricing, rate-limit, and commercial-usage answers, link to the variant page rather than the repository README.
55
+
56
+ ## License
57
+
58
+ Licensed under the Apache License, Version 2.0.
@@ -0,0 +1,238 @@
1
+ ---
2
+ name: suno
3
+ description: Generate music (simple or custom mode, cover, extension, vocals/instrumentals, audio stem separation, stems, MIDI, WAV, music videos, lyrics, personas) through RunAPI.ai using the @runapi.ai/suno Node/TypeScript SDK. Use when the user asks to add AI music generation, mentions Suno, or writes against @runapi.ai/suno. Triggers on "suno", "music generation", "生成音乐", "AI 音乐", "@runapi.ai/suno".
4
+ documentation: https://runapi.ai/models/suno
5
+ provider_page: https://runapi.ai/providers/suno
6
+ catalog: https://runapi.ai/models
7
+ ---
8
+
9
+ # @runapi.ai/suno — RunAPI.ai Suno music generation
10
+
11
+ Build Node / TypeScript integrations that generate music, lyrics, stems, and music videos through RunAPI.ai.
12
+
13
+ ## Setup
14
+
15
+ Requires **Node 18+** (global `fetch`).
16
+
17
+ ```bash
18
+ npm install @runapi.ai/suno
19
+ ```
20
+
21
+ Set your API key in the environment:
22
+
23
+ ```dotenv
24
+ # .env
25
+ RUNAPI_API_KEY=runapi_xxx # get one at https://runapi.ai/settings/api_keys
26
+ ```
27
+
28
+ ```ts
29
+ import { SunoClient } from '@runapi.ai/suno';
30
+
31
+ // The SDK reads RUNAPI_API_KEY from the environment automatically.
32
+ const client = new SunoClient();
33
+ ```
34
+
35
+ Pass `{ apiKey }` explicitly if you manage secrets differently. `baseUrl` defaults to `https://runapi.ai`; override only for local development.
36
+
37
+ ## Core recipe — music generation
38
+
39
+ Two modes share one endpoint. Use `custom_mode: false` for quick results; `custom_mode: true` when you want control over style, title, and persona.
40
+
41
+ ```ts
42
+ // Simple mode — prompt drives everything.
43
+ const result = await client.textToMusic.run({
44
+ custom_mode: false,
45
+ instrumental: false,
46
+ prompt: 'A chill lo-fi beat with soft vocals',
47
+ model: 'V5',
48
+ });
49
+
50
+ const audioUrl = result.audios[0].audio_url;
51
+ ```
52
+
53
+ ```ts
54
+ // Custom mode — style, title, and prompt are all required.
55
+ await client.textToMusic.run({
56
+ custom_mode: true,
57
+ instrumental: false,
58
+ style: 'Jazz, Bossa Nova',
59
+ title: 'Midnight Blues',
60
+ prompt: 'Soft vocals over a walking bassline...',
61
+ model: 'V5',
62
+ });
63
+ ```
64
+
65
+ `run()` creates the task, auto-polls, and resolves only when the task completes — `audios[0].audio_url` is guaranteed on the resolved value. On failure it throws `TaskFailedError`; on polling timeout it throws `TaskTimeoutError`. Suno tasks take several minutes, so split `run()` into `create()` + webhook / `get()` for web handlers:
66
+
67
+ ```ts
68
+ const { id } = await client.textToMusic.create({ custom_mode: false, instrumental: false, prompt: '...', model: 'V5' });
69
+ // return 202 immediately; fetch later:
70
+ const status = await client.textToMusic.get(id);
71
+ if (status.status === 'completed') { /* ... */ }
72
+ ```
73
+
74
+ `run()` polls every 2 s for up to 15 min by default. Tune for long tracks:
75
+
76
+ ```ts
77
+ await client.textToMusic.run(params, { maxWaitMs: 30 * 60_000, pollIntervalMs: 5_000 });
78
+ ```
79
+
80
+ If `TaskTimeoutError` fires, the task is still running server-side — resume with `textToMusic.get(id)` or finish via webhook.
81
+
82
+ ## Other resources
83
+
84
+ `SunoClient` exposes these resources; each has the same `create / get / run` shape:
85
+
86
+ | Resource | Purpose |
87
+ |---|---|
88
+ | `textToMusic` | Core text-to-music |
89
+ | `extendMusic` | Extend an existing generated track |
90
+ | `extendMusic` | Extend an uploaded audio file |
91
+ | `generateArtwork` | Generate cover images for an existing track |
92
+ | `coverAudio` | Restyle an uploaded track |
93
+ | `addInstrumental` | Add instrumental backing to a vocal upload (V4_5PLUS / V5) |
94
+ | `addVocals` | Add vocals to an instrumental upload (V4_5PLUS / V5) |
95
+ | `separateAudioStems` | Split vocals from instrumentals (or up to 12 stems) |
96
+ | `generateMidi` | Extract MIDI from a completed `split_stem` audio stem separation |
97
+ | `convertAudio` | Convert a generated audio to WAV |
98
+ | `visualizeMusic` | Render a music video for a generated audio |
99
+ | `generateLyrics` / `getTimestampedLyrics` | Generate lyrics / word-level timing |
100
+ | `replaceSection` | Regenerate a time range of an existing track |
101
+ | `createMashup` | Blend two uploaded tracks |
102
+ | `generatePersona` / `boostStyle` | Create reusable vocal personas / style descriptors |
103
+
104
+ ### Upload-and-cover — restyle an external track
105
+
106
+ ```ts
107
+ const restyled = await client.coverAudio.run({
108
+ upload_url: 'https://cdn.example.com/song.mp3',
109
+ custom_mode: false,
110
+ instrumental: false,
111
+ prompt: 'Transform into a jazz cover',
112
+ model: 'V4_5PLUS',
113
+ });
114
+ ```
115
+
116
+ ### Extension — continue a generated track
117
+
118
+ ```ts
119
+ const base = await client.textToMusic.run({ custom_mode: false, instrumental: false, prompt: '...', model: 'V5' });
120
+ await client.extendMusic.run({
121
+ audio_id: base.audios[0].id,
122
+ default_param_flag: false,
123
+ model: 'V5',
124
+ prompt: 'Continue with an uplifting chorus',
125
+ });
126
+ ```
127
+
128
+ ### Vocal removal — separate stems
129
+
130
+ ```ts
131
+ const gen = await client.textToMusic.run({ custom_mode: false, instrumental: false, prompt: '...', model: 'V5' });
132
+ const sep = await client.separateAudioStems.run({
133
+ task_id: gen.id,
134
+ audio_id: gen.audios[0].id,
135
+ type: 'split_stem', // or 'separate_vocal' for just vocals + instrumental
136
+ });
137
+
138
+ console.log(sep.separated_audios.vocal_url);
139
+ ```
140
+
141
+ ## Models
142
+
143
+ | `model` | Notes |
144
+ |---|---|
145
+ | `V5` | Latest model; best musical expression. |
146
+ | `V4_5PLUS` | Rich sound, max 8 min. |
147
+ | `V4_5ALL` | All features, max 8 min (audio upload max 1 min). |
148
+ | `V4_5` | Balanced quality and speed. |
149
+ | `V4` | Stable, max 4 min. |
150
+ | `V3_5` | Legacy. |
151
+
152
+ Instrumental / vocal endpoints require `V4_5PLUS` or `V5`. Exact credit costs per model are shown at https://runapi.ai/pricing and in the dashboard — do not hardcode prices in application code.
153
+
154
+ ## Callbacks (webhooks)
155
+
156
+ Pass `callback_url` on `create()` (or any `run()` call) and RunAPI will POST the payload to you:
157
+
158
+ ```ts
159
+ await client.textToMusic.create({
160
+ custom_mode: false,
161
+ instrumental: false,
162
+ prompt: '...',
163
+ model: 'V5',
164
+ callback_url: 'https://your.app/webhooks/runapi/suno',
165
+ });
166
+ ```
167
+
168
+ Suno fires the callback at multiple stages — `generation_stage` tells you which:
169
+
170
+ ```ts
171
+ {
172
+ id: string;
173
+ status: 'processing' | 'completed' | 'failed';
174
+ generation_stage: 'text_generated' | 'first_audio_ready' | 'all_audios_ready' | 'failed';
175
+ audios?: Audio[];
176
+ error?: string;
177
+ }
178
+ ```
179
+
180
+ Treat `all_audios_ready` as the terminal success stage. **Always verify the signature before trusting the body.** RunAPI signs every callback with your account's Callback Secret (rotate at `/accounts/callback_secret`). Headers:
181
+
182
+ - `X-Callback-Id` — UUID, store to make handler idempotent
183
+ - `X-Callback-Timestamp` — unix seconds, reject if `|now - ts| > 300`
184
+ - `X-Callback-Signature` — base64 HMAC-SHA256 over `` `${id}.${ts}.${rawBody}` `` using the base64-decoded secret
185
+
186
+ ```ts
187
+ import crypto from 'node:crypto';
188
+
189
+ function verify(raw: string, id: string, ts: string, sig: string, secret: string) {
190
+ const key = Buffer.from(secret, 'base64');
191
+ const mac = crypto.createHmac('sha256', key)
192
+ .update(`${id}.${ts}.${raw}`)
193
+ .digest('base64');
194
+ return crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(sig));
195
+ }
196
+ ```
197
+
198
+ Reply `2xx` within 10s; any non-2xx triggers retries.
199
+
200
+ ## Errors
201
+
202
+ All errors are re-exported from `@runapi.ai/core`. Always `instanceof` — never string-match messages.
203
+
204
+ | Error | Status | Action |
205
+ |---|---|---|
206
+ | `AuthenticationError` | 401 | abort; surface "reconnect your API key" |
207
+ | `InsufficientCreditsError` | 402 | prompt user to top up at runapi.ai/billing |
208
+ | `ValidationError` | 400 / 422 | fix params; do not retry |
209
+ | `RateLimitError` | 429 | sleep `err.retryAfterMs`, then retry |
210
+ | `ServiceUnavailableError` | 503 / 455 | retry with backoff; transient service issue |
211
+ | `TaskFailedError` | — | show `err.details` to user; do not auto-retry |
212
+ | `TaskTimeoutError` | — | re-poll with `<resource>.get(id)` |
213
+
214
+ ```ts
215
+ import { InsufficientCreditsError, TaskFailedError } from '@runapi.ai/suno';
216
+
217
+ try {
218
+ await client.textToMusic.run({ custom_mode: false, instrumental: false, prompt: '...', model: 'V5' });
219
+ } catch (err) {
220
+ if (err instanceof InsufficientCreditsError) { /* surface top-up CTA */ }
221
+ else if (err instanceof TaskFailedError) { /* show err.details */ }
222
+ else throw err;
223
+ }
224
+ ```
225
+
226
+ ## Gotchas
227
+
228
+ - `model` is required on every call, even endpoints where it looks optional.
229
+ - Custom mode requires `style` **and** `title`. Prompt is required unless `instrumental: true`.
230
+ - `vocal_gender`, `style_weight`, `weirdness_constraint`, and `audio_weight` only apply when advanced settings are on — custom mode, or `default_param_flag: true` on extendMusic.
231
+ - `generateMidi.run()` requires a completed `separateAudioStems` task created with `type: 'split_stem'` — `separate_vocal` does not produce MIDI.
232
+ - `replaceSection` replacement duration should not exceed 50% of the total track.
233
+ - Callbacks fire multiple times; the `all_audios_ready` stage is the final one. Handle every stage idempotently using `X-Callback-Id`.
234
+ - `callback_url` must be reachable from the public internet. `localhost` / `127.0.0.1` URLs will never fire — use a tunnel (cloudflared, ngrok, tailscale funnel) when developing locally.
235
+
236
+ ## Dig deeper
237
+
238
+ Package README (full API surface, all params): `node_modules/@runapi.ai/suno/README.md`. Types: `@runapi.ai/suno/dist/types.d.ts`. Product docs: https://runapi.ai/docs.