@thinkable-labs/music-generator 1.0.0
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/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/suno-client.d.ts +39 -0
- package/dist/suno-client.js +297 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.js +4 -0
- package/package.json +20 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { MusicGenerator } from "./suno-client";
|
|
2
|
+
export type { MusicGeneratorConfig, SimpleGenerateRequest, AdvancedGenerateRequest, GenerateResponse, TaskInfo, TaskStatus, SongData, GenerationRecord, DownloadResult, CallbackType, CallbackPayload, } from "./types";
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MusicGenerator = void 0;
|
|
4
|
+
// ─── Client ─────────────────────────────────────────────────────────
|
|
5
|
+
var suno_client_1 = require("./suno-client");
|
|
6
|
+
Object.defineProperty(exports, "MusicGenerator", { enumerable: true, get: function () { return suno_client_1.MusicGenerator; } });
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { MusicGeneratorConfig, SimpleGenerateRequest, AdvancedGenerateRequest, TaskInfo, SongData, GenerationRecord, DownloadResult } from "./types";
|
|
2
|
+
export declare class MusicGenerator {
|
|
3
|
+
private apiKey;
|
|
4
|
+
private pollInterval;
|
|
5
|
+
private pollTimeout;
|
|
6
|
+
private history;
|
|
7
|
+
constructor(config: MusicGeneratorConfig);
|
|
8
|
+
/**
|
|
9
|
+
* Generate an instrumental song from a text description.
|
|
10
|
+
* Returns 2 songs. Polls until complete or timeout.
|
|
11
|
+
*/
|
|
12
|
+
generateSimple(request: SimpleGenerateRequest): Promise<GenerationRecord>;
|
|
13
|
+
/**
|
|
14
|
+
* Generate a song with custom lyrics, style, and title.
|
|
15
|
+
* If lyrics is empty, generates an instrumental.
|
|
16
|
+
* Returns 2 songs. Polls until complete or timeout.
|
|
17
|
+
*/
|
|
18
|
+
generateAdvanced(request: AdvancedGenerateRequest): Promise<GenerationRecord>;
|
|
19
|
+
/** Check the status of a generation task */
|
|
20
|
+
getTaskStatus(taskId: string): Promise<TaskInfo>;
|
|
21
|
+
/** Get all generation records from this session */
|
|
22
|
+
getHistory(): GenerationRecord[];
|
|
23
|
+
/** Clear session history */
|
|
24
|
+
clearHistory(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Download a generated song to a local folder.
|
|
27
|
+
* @param song - The song to download
|
|
28
|
+
* @param outputDir - Target directory (must exist)
|
|
29
|
+
* @param filename - Optional custom filename (without extension)
|
|
30
|
+
*/
|
|
31
|
+
downloadSong(song: SongData, outputDir: string, filename?: string): Promise<DownloadResult>;
|
|
32
|
+
private pollUntilComplete;
|
|
33
|
+
private post;
|
|
34
|
+
private get;
|
|
35
|
+
private handleResponse;
|
|
36
|
+
private downloadFile;
|
|
37
|
+
private sleep;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=suno-client.d.ts.map
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.MusicGenerator = void 0;
|
|
37
|
+
const https = __importStar(require("https"));
|
|
38
|
+
const http = __importStar(require("http"));
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const API_BASE = "https://api.sunoapi.org";
|
|
42
|
+
const MODEL = "V5_5";
|
|
43
|
+
const FAILED_STATUSES = [
|
|
44
|
+
"CREATE_TASK_FAILED",
|
|
45
|
+
"GENERATE_AUDIO_FAILED",
|
|
46
|
+
"CALLBACK_EXCEPTION",
|
|
47
|
+
"SENSITIVE_WORD_ERROR",
|
|
48
|
+
];
|
|
49
|
+
class MusicGenerator {
|
|
50
|
+
constructor(config) {
|
|
51
|
+
this.history = [];
|
|
52
|
+
if (!config.apiKey) {
|
|
53
|
+
throw new Error("API key is required");
|
|
54
|
+
}
|
|
55
|
+
this.apiKey = config.apiKey;
|
|
56
|
+
this.pollInterval = config.pollInterval ?? 5000;
|
|
57
|
+
this.pollTimeout = config.pollTimeout ?? 300000;
|
|
58
|
+
}
|
|
59
|
+
// ─── Simple Mode ────────────────────────────────────────────────
|
|
60
|
+
/**
|
|
61
|
+
* Generate an instrumental song from a text description.
|
|
62
|
+
* Returns 2 songs. Polls until complete or timeout.
|
|
63
|
+
*/
|
|
64
|
+
async generateSimple(request) {
|
|
65
|
+
if (!request.prompt || request.prompt.trim().length === 0) {
|
|
66
|
+
throw new Error("Song description is required");
|
|
67
|
+
}
|
|
68
|
+
if (request.prompt.length > 500) {
|
|
69
|
+
throw new Error("Song description must be 500 characters or less");
|
|
70
|
+
}
|
|
71
|
+
const body = {
|
|
72
|
+
prompt: request.prompt,
|
|
73
|
+
customMode: false,
|
|
74
|
+
instrumental: true,
|
|
75
|
+
model: MODEL,
|
|
76
|
+
...(request.callBackUrl && { callBackUrl: request.callBackUrl }),
|
|
77
|
+
};
|
|
78
|
+
const response = await this.post("/api/v1/generate", body);
|
|
79
|
+
const taskId = response.data.taskId;
|
|
80
|
+
const taskInfo = await this.pollUntilComplete(taskId);
|
|
81
|
+
const songs = taskInfo.data.response?.sunoData ?? [];
|
|
82
|
+
const record = {
|
|
83
|
+
taskId,
|
|
84
|
+
status: taskInfo.data.status,
|
|
85
|
+
mode: "simple",
|
|
86
|
+
prompt: request.prompt,
|
|
87
|
+
songs,
|
|
88
|
+
createdAt: new Date().toISOString(),
|
|
89
|
+
};
|
|
90
|
+
this.history.unshift(record);
|
|
91
|
+
return record;
|
|
92
|
+
}
|
|
93
|
+
// ─── Advanced Mode ──────────────────────────────────────────────
|
|
94
|
+
/**
|
|
95
|
+
* Generate a song with custom lyrics, style, and title.
|
|
96
|
+
* If lyrics is empty, generates an instrumental.
|
|
97
|
+
* Returns 2 songs. Polls until complete or timeout.
|
|
98
|
+
*/
|
|
99
|
+
async generateAdvanced(request) {
|
|
100
|
+
if (!request.style || request.style.trim().length === 0) {
|
|
101
|
+
throw new Error("Style is required in advanced mode");
|
|
102
|
+
}
|
|
103
|
+
if (request.style.length > 1000) {
|
|
104
|
+
throw new Error("Style must be 1000 characters or less");
|
|
105
|
+
}
|
|
106
|
+
if (request.lyrics && request.lyrics.length > 3000) {
|
|
107
|
+
throw new Error("Lyrics must be 3000 characters or less");
|
|
108
|
+
}
|
|
109
|
+
if (request.title && request.title.length > 100) {
|
|
110
|
+
throw new Error("Title must be 100 characters or less");
|
|
111
|
+
}
|
|
112
|
+
const isInstrumental = !request.lyrics || request.lyrics.trim().length === 0;
|
|
113
|
+
const body = {
|
|
114
|
+
customMode: true,
|
|
115
|
+
instrumental: isInstrumental,
|
|
116
|
+
style: request.style,
|
|
117
|
+
model: MODEL,
|
|
118
|
+
};
|
|
119
|
+
if (!isInstrumental) {
|
|
120
|
+
body.prompt = request.lyrics;
|
|
121
|
+
}
|
|
122
|
+
if (request.title) {
|
|
123
|
+
body.title = request.title;
|
|
124
|
+
}
|
|
125
|
+
if (request.callBackUrl) {
|
|
126
|
+
body.callBackUrl = request.callBackUrl;
|
|
127
|
+
}
|
|
128
|
+
const response = await this.post("/api/v1/generate", body);
|
|
129
|
+
const taskId = response.data.taskId;
|
|
130
|
+
const taskInfo = await this.pollUntilComplete(taskId);
|
|
131
|
+
const songs = taskInfo.data.response?.sunoData ?? [];
|
|
132
|
+
const record = {
|
|
133
|
+
taskId,
|
|
134
|
+
status: taskInfo.data.status,
|
|
135
|
+
mode: "advanced",
|
|
136
|
+
prompt: isInstrumental ? `[instrumental] ${request.style}` : (request.lyrics ?? ""),
|
|
137
|
+
style: request.style,
|
|
138
|
+
title: request.title,
|
|
139
|
+
lyrics: request.lyrics,
|
|
140
|
+
songs,
|
|
141
|
+
createdAt: new Date().toISOString(),
|
|
142
|
+
};
|
|
143
|
+
this.history.unshift(record);
|
|
144
|
+
return record;
|
|
145
|
+
}
|
|
146
|
+
// ─── Task Status ────────────────────────────────────────────────
|
|
147
|
+
/** Check the status of a generation task */
|
|
148
|
+
async getTaskStatus(taskId) {
|
|
149
|
+
return this.get(`/api/v1/generate/record-info?taskId=${taskId}`);
|
|
150
|
+
}
|
|
151
|
+
// ─── History ────────────────────────────────────────────────────
|
|
152
|
+
/** Get all generation records from this session */
|
|
153
|
+
getHistory() {
|
|
154
|
+
return [...this.history];
|
|
155
|
+
}
|
|
156
|
+
/** Clear session history */
|
|
157
|
+
clearHistory() {
|
|
158
|
+
this.history = [];
|
|
159
|
+
}
|
|
160
|
+
// ─── Download ───────────────────────────────────────────────────
|
|
161
|
+
/**
|
|
162
|
+
* Download a generated song to a local folder.
|
|
163
|
+
* @param song - The song to download
|
|
164
|
+
* @param outputDir - Target directory (must exist)
|
|
165
|
+
* @param filename - Optional custom filename (without extension)
|
|
166
|
+
*/
|
|
167
|
+
async downloadSong(song, outputDir, filename) {
|
|
168
|
+
if (!outputDir || outputDir.trim().length === 0) {
|
|
169
|
+
return { success: false, error: "Please select a download folder" };
|
|
170
|
+
}
|
|
171
|
+
if (!fs.existsSync(outputDir)) {
|
|
172
|
+
return { success: false, error: `Folder does not exist: ${outputDir}` };
|
|
173
|
+
}
|
|
174
|
+
if (!song.audioUrl) {
|
|
175
|
+
return { success: false, error: "Song has no download URL yet" };
|
|
176
|
+
}
|
|
177
|
+
const safeName = filename
|
|
178
|
+
? filename.replace(/[^a-zA-Z0-9_\-\s]/g, "")
|
|
179
|
+
: (song.title || `suno-${song.id}`).replace(/[^a-zA-Z0-9_\-\s]/g, "");
|
|
180
|
+
const filePath = path.join(outputDir, `${safeName}.mp3`);
|
|
181
|
+
try {
|
|
182
|
+
await this.downloadFile(song.audioUrl, filePath);
|
|
183
|
+
return { success: true, filePath };
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
187
|
+
return { success: false, error: `Download failed: ${message}` };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// ─── Polling ────────────────────────────────────────────────────
|
|
191
|
+
async pollUntilComplete(taskId) {
|
|
192
|
+
const start = Date.now();
|
|
193
|
+
while (Date.now() - start < this.pollTimeout) {
|
|
194
|
+
const info = await this.getTaskStatus(taskId);
|
|
195
|
+
const status = info.data.status;
|
|
196
|
+
if (status === "SUCCESS") {
|
|
197
|
+
return info;
|
|
198
|
+
}
|
|
199
|
+
if (FAILED_STATUSES.includes(status)) {
|
|
200
|
+
throw new Error(`Generation failed: ${status}${info.data.errorMessage ? ` — ${info.data.errorMessage}` : ""}`);
|
|
201
|
+
}
|
|
202
|
+
await this.sleep(this.pollInterval);
|
|
203
|
+
}
|
|
204
|
+
throw new Error(`Generation timed out after ${this.pollTimeout / 1000}s`);
|
|
205
|
+
}
|
|
206
|
+
// ─── HTTP Helpers ───────────────────────────────────────────────
|
|
207
|
+
async post(endpoint, body) {
|
|
208
|
+
return new Promise((resolve, reject) => {
|
|
209
|
+
const url = new URL(endpoint, API_BASE);
|
|
210
|
+
const data = JSON.stringify(body);
|
|
211
|
+
const req = https.request({
|
|
212
|
+
hostname: url.hostname,
|
|
213
|
+
port: url.port || 443,
|
|
214
|
+
path: url.pathname,
|
|
215
|
+
method: "POST",
|
|
216
|
+
headers: {
|
|
217
|
+
"Content-Type": "application/json",
|
|
218
|
+
"Content-Length": Buffer.byteLength(data),
|
|
219
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
220
|
+
},
|
|
221
|
+
}, (res) => this.handleResponse(res, resolve, reject));
|
|
222
|
+
req.on("error", reject);
|
|
223
|
+
req.write(data);
|
|
224
|
+
req.end();
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
async get(endpoint) {
|
|
228
|
+
return new Promise((resolve, reject) => {
|
|
229
|
+
const url = new URL(endpoint, API_BASE);
|
|
230
|
+
const req = https.request({
|
|
231
|
+
hostname: url.hostname,
|
|
232
|
+
port: url.port || 443,
|
|
233
|
+
path: `${url.pathname}${url.search}`,
|
|
234
|
+
method: "GET",
|
|
235
|
+
headers: {
|
|
236
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
237
|
+
},
|
|
238
|
+
}, (res) => this.handleResponse(res, resolve, reject));
|
|
239
|
+
req.on("error", reject);
|
|
240
|
+
req.end();
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
handleResponse(res, resolve, reject) {
|
|
244
|
+
let body = "";
|
|
245
|
+
res.on("data", (chunk) => {
|
|
246
|
+
body += chunk.toString();
|
|
247
|
+
});
|
|
248
|
+
res.on("end", () => {
|
|
249
|
+
try {
|
|
250
|
+
const parsed = JSON.parse(body);
|
|
251
|
+
if (parsed.code && parsed.code !== 200) {
|
|
252
|
+
reject(new Error(`API error ${parsed.code}: ${parsed.msg || body}`));
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
resolve(parsed);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
reject(new Error(`Failed to parse API response: ${body.substring(0, 200)}`));
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
async downloadFile(url, filePath) {
|
|
264
|
+
return new Promise((resolve, reject) => {
|
|
265
|
+
const parsedUrl = new URL(url);
|
|
266
|
+
const client = parsedUrl.protocol === "https:" ? https : http;
|
|
267
|
+
client.get(url, (res) => {
|
|
268
|
+
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
269
|
+
const redirectUrl = res.headers.location;
|
|
270
|
+
if (redirectUrl) {
|
|
271
|
+
this.downloadFile(redirectUrl, filePath).then(resolve).catch(reject);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (res.statusCode !== 200) {
|
|
276
|
+
reject(new Error(`HTTP ${res.statusCode} downloading file`));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const fileStream = fs.createWriteStream(filePath);
|
|
280
|
+
res.pipe(fileStream);
|
|
281
|
+
fileStream.on("finish", () => {
|
|
282
|
+
fileStream.close();
|
|
283
|
+
resolve();
|
|
284
|
+
});
|
|
285
|
+
fileStream.on("error", (err) => {
|
|
286
|
+
fs.unlink(filePath, () => { }); // clean up partial file
|
|
287
|
+
reject(err);
|
|
288
|
+
});
|
|
289
|
+
}).on("error", reject);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
sleep(ms) {
|
|
293
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
exports.MusicGenerator = MusicGenerator;
|
|
297
|
+
//# sourceMappingURL=suno-client.js.map
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** Simple mode: just a text description, always instrumental */
|
|
2
|
+
export interface SimpleGenerateRequest {
|
|
3
|
+
/** Plain text description of the song (max 500 chars) */
|
|
4
|
+
prompt: string;
|
|
5
|
+
/** Webhook URL for completion notifications */
|
|
6
|
+
callBackUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Advanced mode: lyrics, style, and optional title */
|
|
9
|
+
export interface AdvancedGenerateRequest {
|
|
10
|
+
/** Lyrics for the song (max 3000 chars). Leave empty for instrumental. */
|
|
11
|
+
lyrics?: string;
|
|
12
|
+
/** Music style/genre tags (max 1000 chars) e.g. "broken beats, unplugged, funky guitar riffs" */
|
|
13
|
+
style: string;
|
|
14
|
+
/** Song title (max 100 chars, optional) */
|
|
15
|
+
title?: string;
|
|
16
|
+
/** Webhook URL for completion notifications */
|
|
17
|
+
callBackUrl?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface GenerateResponse {
|
|
20
|
+
code: number;
|
|
21
|
+
msg: string;
|
|
22
|
+
data: {
|
|
23
|
+
taskId: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface SongData {
|
|
27
|
+
id: string;
|
|
28
|
+
audioUrl: string;
|
|
29
|
+
streamAudioUrl: string;
|
|
30
|
+
imageUrl: string;
|
|
31
|
+
prompt: string;
|
|
32
|
+
title: string;
|
|
33
|
+
tags: string;
|
|
34
|
+
duration: number;
|
|
35
|
+
createTime: string;
|
|
36
|
+
}
|
|
37
|
+
export type TaskStatus = "PENDING" | "TEXT_SUCCESS" | "FIRST_SUCCESS" | "SUCCESS" | "CREATE_TASK_FAILED" | "GENERATE_AUDIO_FAILED" | "CALLBACK_EXCEPTION" | "SENSITIVE_WORD_ERROR";
|
|
38
|
+
export interface TaskInfo {
|
|
39
|
+
code: number;
|
|
40
|
+
msg: string;
|
|
41
|
+
data: {
|
|
42
|
+
taskId: string;
|
|
43
|
+
status: TaskStatus;
|
|
44
|
+
operationType: string;
|
|
45
|
+
response: {
|
|
46
|
+
sunoData: SongData[];
|
|
47
|
+
} | null;
|
|
48
|
+
errorCode: string | null;
|
|
49
|
+
errorMessage: string | null;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export type CallbackType = "text" | "first" | "complete";
|
|
53
|
+
export interface CallbackPayload {
|
|
54
|
+
type: CallbackType;
|
|
55
|
+
taskId: string;
|
|
56
|
+
data: SongData[];
|
|
57
|
+
}
|
|
58
|
+
export interface GenerationRecord {
|
|
59
|
+
taskId: string;
|
|
60
|
+
status: TaskStatus;
|
|
61
|
+
mode: "simple" | "advanced";
|
|
62
|
+
prompt: string;
|
|
63
|
+
style?: string;
|
|
64
|
+
title?: string;
|
|
65
|
+
lyrics?: string;
|
|
66
|
+
songs: SongData[];
|
|
67
|
+
createdAt: string;
|
|
68
|
+
}
|
|
69
|
+
export interface DownloadResult {
|
|
70
|
+
success: boolean;
|
|
71
|
+
filePath?: string;
|
|
72
|
+
error?: string;
|
|
73
|
+
}
|
|
74
|
+
export interface MusicGeneratorConfig {
|
|
75
|
+
/** Suno API key (provided by desktop app) */
|
|
76
|
+
apiKey: string;
|
|
77
|
+
/** Polling interval in ms when waiting for generation (default: 5000) */
|
|
78
|
+
pollInterval?: number;
|
|
79
|
+
/** Max polling time in ms before giving up (default: 300000 = 5 min) */
|
|
80
|
+
pollTimeout?: number;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@thinkable-labs/music-generator",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Suno AI music generation wrapper for Thinkable Labs desktop app",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/**/*.js",
|
|
9
|
+
"dist/**/*.d.ts"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"prepublishOnly": "tsc"
|
|
14
|
+
},
|
|
15
|
+
"license": "UNLICENSED",
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/node": "^22.0.0",
|
|
18
|
+
"typescript": "^5.9.3"
|
|
19
|
+
}
|
|
20
|
+
}
|