thumbnailgpt 1.0.8 → 1.0.9
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/client.d.ts +16 -8
- package/dist/client.js +51 -61
- package/dist/index.js +42 -11
- package/dist/types.d.ts +25 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type { CreditBalanceResponse, StudioGenerateOptions, StudioDispatchResult, StudioConversationResponse, GenerationSettledResult, PersonaItem, ThumbnailItem } from "./types.js";
|
|
1
|
+
import type { CreditBalanceResponse, StudioGenerateOptions, StudioDispatchResult, StudioConversationResponse, GenerationSettledResult, PersonaItem, ThumbnailItem, ListChatsResponse, ListPersonasResponse } from "./types.js";
|
|
2
2
|
export declare class ThumbnailGPTClient {
|
|
3
3
|
private apiKey;
|
|
4
4
|
private apiUrl;
|
|
5
5
|
constructor(apiKey?: string, apiUrl?: string);
|
|
6
6
|
private request;
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Fetch current credit balance and subscription status
|
|
9
9
|
*/
|
|
10
10
|
getCredits(): Promise<CreditBalanceResponse>;
|
|
11
11
|
/**
|
|
12
|
-
* Dispatch
|
|
12
|
+
* Dispatch studio thumbnail generation task
|
|
13
13
|
*/
|
|
14
14
|
dispatchStudio(options: StudioGenerateOptions): Promise<StudioDispatchResult>;
|
|
15
15
|
/**
|
|
@@ -17,13 +17,21 @@ export declare class ThumbnailGPTClient {
|
|
|
17
17
|
*/
|
|
18
18
|
getChat(chatId: string): Promise<StudioConversationResponse>;
|
|
19
19
|
/**
|
|
20
|
-
* List recent studio conversations
|
|
20
|
+
* List recent studio conversations with pagination
|
|
21
21
|
*/
|
|
22
|
-
listChats(
|
|
22
|
+
listChats(options?: {
|
|
23
|
+
page?: number;
|
|
24
|
+
limit?: number;
|
|
25
|
+
}): Promise<ListChatsResponse>;
|
|
23
26
|
/**
|
|
24
|
-
* Retrieve active, ready-to-use personas for the user
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
* Retrieve active, ready-to-use personas for the user with pagination
|
|
28
|
+
*/
|
|
29
|
+
listPersonas(options?: {
|
|
30
|
+
page?: number;
|
|
31
|
+
limit?: number;
|
|
32
|
+
}): Promise<ListPersonasResponse>;
|
|
33
|
+
/**
|
|
34
|
+
* Retrieve active, ready-to-use personas (returns array for backward compatibility)
|
|
27
35
|
*/
|
|
28
36
|
getPersonas(): Promise<PersonaItem[]>;
|
|
29
37
|
/**
|
package/dist/client.js
CHANGED
|
@@ -14,74 +14,51 @@ export class ThumbnailGPTClient {
|
|
|
14
14
|
async request(endpoint, options = {}) {
|
|
15
15
|
const url = `${this.apiUrl}${endpoint.startsWith("/") ? "" : "/"}${endpoint}`;
|
|
16
16
|
const headers = {
|
|
17
|
-
"Content-Type": "application/json",
|
|
18
17
|
Authorization: `Bearer ${this.apiKey}`,
|
|
19
|
-
|
|
20
|
-
"User-Agent": "thumbnailgpt-cli/1.0.8",
|
|
18
|
+
"Content-Type": "application/json",
|
|
21
19
|
...options.headers,
|
|
22
20
|
};
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
let res;
|
|
22
|
+
try {
|
|
23
|
+
res = await fetch(url, { ...options, headers });
|
|
24
|
+
}
|
|
25
|
+
catch (netErr) {
|
|
26
|
+
throw new Error(`Failed to reach ThumbnailGPT backend at ${this.apiUrl}: ${netErr.message}`);
|
|
27
|
+
}
|
|
25
28
|
if (!res.ok) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
let errMsg = `Request failed (${res.status} ${res.statusText})`;
|
|
30
|
+
try {
|
|
31
|
+
const errorJson = await res.json();
|
|
32
|
+
errMsg = errorJson.error || errorJson.message || errMsg;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Fallback to text status
|
|
36
|
+
}
|
|
37
|
+
throw new Error(errMsg);
|
|
33
38
|
}
|
|
34
|
-
return
|
|
39
|
+
return res.json();
|
|
35
40
|
}
|
|
36
41
|
/**
|
|
37
|
-
*
|
|
42
|
+
* Fetch current credit balance and subscription status
|
|
38
43
|
*/
|
|
39
44
|
async getCredits() {
|
|
40
45
|
return this.request("/api/credits/balance");
|
|
41
46
|
}
|
|
42
47
|
/**
|
|
43
|
-
* Dispatch
|
|
48
|
+
* Dispatch studio thumbnail generation task
|
|
44
49
|
*/
|
|
45
50
|
async dispatchStudio(options) {
|
|
46
|
-
if (options.referenceImages && options.referenceImages.length > 5) {
|
|
47
|
-
throw new Error("A maximum of 5 reference images is allowed.");
|
|
48
|
-
}
|
|
49
|
-
const rawRefImages = (options.referenceImages || []).slice(0, 5);
|
|
50
|
-
const processedRefImages = [];
|
|
51
|
-
for (const img of rawRefImages) {
|
|
52
|
-
if (typeof img === "string") {
|
|
53
|
-
if (img.startsWith("http://") || img.startsWith("https://") || img.startsWith("data:")) {
|
|
54
|
-
processedRefImages.push(img);
|
|
55
|
-
}
|
|
56
|
-
else if (fs.existsSync(img)) {
|
|
57
|
-
const ext = path.extname(img).toLowerCase().replace(".", "");
|
|
58
|
-
const mimeType = ext === "jpg" || ext === "jpeg"
|
|
59
|
-
? "image/jpeg"
|
|
60
|
-
: ext === "png"
|
|
61
|
-
? "image/png"
|
|
62
|
-
: ext === "webp"
|
|
63
|
-
? "image/webp"
|
|
64
|
-
: "image/jpeg";
|
|
65
|
-
const buffer = fs.readFileSync(img);
|
|
66
|
-
const base64 = buffer.toString("base64");
|
|
67
|
-
processedRefImages.push(`data:${mimeType};base64,${base64}`);
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
processedRefImages.push(img);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
51
|
return this.request("/api/studio", {
|
|
75
52
|
method: "POST",
|
|
76
53
|
body: JSON.stringify({
|
|
77
|
-
chatId: options.chatId,
|
|
78
54
|
feature: "recreate_thumbnail",
|
|
79
55
|
prompt: options.prompt,
|
|
56
|
+
chatId: options.chatId,
|
|
80
57
|
aspectRatio: options.aspectRatio || "16:9",
|
|
81
58
|
resolution: options.resolution || "1K",
|
|
82
|
-
referenceImages:
|
|
83
|
-
youtubeUrl: options.youtubeUrl
|
|
84
|
-
personaId: options.personaId
|
|
59
|
+
referenceImages: options.referenceImages || [],
|
|
60
|
+
youtubeUrl: options.youtubeUrl,
|
|
61
|
+
personaId: options.personaId,
|
|
85
62
|
baseImage: options.baseImage,
|
|
86
63
|
}),
|
|
87
64
|
});
|
|
@@ -93,23 +70,36 @@ export class ThumbnailGPTClient {
|
|
|
93
70
|
return this.request(`/api/studio/${chatId}`);
|
|
94
71
|
}
|
|
95
72
|
/**
|
|
96
|
-
* List recent studio conversations
|
|
73
|
+
* List recent studio conversations with pagination
|
|
97
74
|
*/
|
|
98
|
-
async listChats() {
|
|
99
|
-
|
|
75
|
+
async listChats(options) {
|
|
76
|
+
const page = Math.max(1, options?.page ?? 1);
|
|
77
|
+
const limit = Math.min(100, Math.max(1, options?.limit ?? 20));
|
|
78
|
+
return this.request(`/api/studio?page=${page}&limit=${limit}`);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Retrieve active, ready-to-use personas for the user with pagination
|
|
82
|
+
*/
|
|
83
|
+
async listPersonas(options) {
|
|
84
|
+
const page = Math.max(1, options?.page ?? 1);
|
|
85
|
+
const limit = Math.min(50, Math.max(1, options?.limit ?? 20));
|
|
86
|
+
const res = await this.request(`/api/personas?page=${page}&limit=${limit}`);
|
|
87
|
+
return {
|
|
88
|
+
success: res.success ?? true,
|
|
89
|
+
personas: (res.personas || []).map((p) => ({
|
|
90
|
+
id: p.id,
|
|
91
|
+
name: p.name,
|
|
92
|
+
created_at: p.created_at,
|
|
93
|
+
})),
|
|
94
|
+
pagination: res.pagination,
|
|
95
|
+
};
|
|
100
96
|
}
|
|
101
97
|
/**
|
|
102
|
-
* Retrieve active, ready-to-use personas for
|
|
103
|
-
* Only 'ready' personas are returned. Raw image URLs, character sheets,
|
|
104
|
-
* error traces, and database internals are strictly redacted.
|
|
98
|
+
* Retrieve active, ready-to-use personas (returns array for backward compatibility)
|
|
105
99
|
*/
|
|
106
100
|
async getPersonas() {
|
|
107
|
-
const res = await this.
|
|
108
|
-
return
|
|
109
|
-
id: p.id,
|
|
110
|
-
name: p.name,
|
|
111
|
-
created_at: p.created_at,
|
|
112
|
-
}));
|
|
101
|
+
const res = await this.listPersonas({ page: 1, limit: 50 });
|
|
102
|
+
return res.personas;
|
|
113
103
|
}
|
|
114
104
|
/**
|
|
115
105
|
* Asynchronously dispatch and poll until completion
|
|
@@ -117,8 +107,8 @@ export class ThumbnailGPTClient {
|
|
|
117
107
|
async generateAndWait(options, onProgress, timeoutSec = 120) {
|
|
118
108
|
// 1. Credit Pre-check
|
|
119
109
|
const credits = await this.getCredits();
|
|
120
|
-
const resolutionCosts = { "1K":
|
|
121
|
-
const required = resolutionCosts[options.resolution || "1K"] ||
|
|
110
|
+
const resolutionCosts = { "1K": 20, "2K": 30, "4K": 40 };
|
|
111
|
+
const required = resolutionCosts[options.resolution || "1K"] || 20;
|
|
122
112
|
if (credits.credits < required) {
|
|
123
113
|
throw new Error(`Insufficient credits. Required: ${required}, Available: ${credits.credits}. Top up at https://thumbnailgpt.com/pricing`);
|
|
124
114
|
}
|
package/dist/index.js
CHANGED
|
@@ -55,7 +55,7 @@ const program = new Command();
|
|
|
55
55
|
program
|
|
56
56
|
.name("thumbnailgpt")
|
|
57
57
|
.description("Official CLI for ThumbnailGPT — Generate, iterate, and manage thumbnails from your terminal")
|
|
58
|
-
.version("1.0.
|
|
58
|
+
.version("1.0.9");
|
|
59
59
|
// 1. Auth Login
|
|
60
60
|
program
|
|
61
61
|
.command("login")
|
|
@@ -286,14 +286,25 @@ program
|
|
|
286
286
|
const chats = program.command("chats").description("Manage chat conversations");
|
|
287
287
|
chats
|
|
288
288
|
.command("list")
|
|
289
|
-
.description("List recent chat conversations")
|
|
290
|
-
.
|
|
289
|
+
.description("List recent chat conversations with pagination")
|
|
290
|
+
.option("-p, --page <number>", "Page number", "1")
|
|
291
|
+
.option("-l, --limit <number>", "Items per page (max 100)", "20")
|
|
292
|
+
.option("--json", "Output response as raw JSON", false)
|
|
293
|
+
.action(async (opts) => {
|
|
291
294
|
let spinner = null;
|
|
292
295
|
try {
|
|
296
|
+
const page = Math.max(1, parseInt(opts.page, 10) || 1);
|
|
297
|
+
const limit = Math.min(100, Math.max(1, parseInt(opts.limit, 10) || 20));
|
|
298
|
+
if (!opts.json) {
|
|
299
|
+
spinner = ora("Fetching conversations...").start();
|
|
300
|
+
}
|
|
293
301
|
const client = new ThumbnailGPTClient();
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
302
|
+
const res = await client.listChats({ page, limit });
|
|
303
|
+
if (opts.json) {
|
|
304
|
+
console.log(JSON.stringify(res, null, 2));
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
spinner?.stop();
|
|
297
308
|
const chatList = res.chats || [];
|
|
298
309
|
if (chatList.length === 0) {
|
|
299
310
|
console.log(pc.gray("No conversations found."));
|
|
@@ -304,6 +315,10 @@ chats
|
|
|
304
315
|
console.log(`• ${pc.bold(c.title || "Untitled")} ${pc.gray(`(${c.id})`)}`);
|
|
305
316
|
console.log(` ${pc.gray("Updated:")} ${new Date(c.updated_at).toLocaleString()}`);
|
|
306
317
|
});
|
|
318
|
+
if (res.pagination) {
|
|
319
|
+
const { page: currPage, totalPages, total, hasMore } = res.pagination;
|
|
320
|
+
console.log(pc.dim(`\nPage ${currPage} of ${totalPages} (${total} total conversations)${hasMore ? ` • Run with --page ${currPage + 1} to view more` : ""}`));
|
|
321
|
+
}
|
|
307
322
|
console.log();
|
|
308
323
|
}
|
|
309
324
|
catch (err) {
|
|
@@ -412,14 +427,26 @@ const personasCmd = program
|
|
|
412
427
|
.description("Inspect active creator personas for thumbnail generation");
|
|
413
428
|
personasCmd
|
|
414
429
|
.command("list")
|
|
415
|
-
.description("List your active creator personas (only ready personas are shown)")
|
|
416
|
-
.
|
|
430
|
+
.description("List your active creator personas with pagination (only ready personas are shown)")
|
|
431
|
+
.option("-p, --page <number>", "Page number", "1")
|
|
432
|
+
.option("-l, --limit <number>", "Items per page (max 50)", "20")
|
|
433
|
+
.option("--json", "Output response as raw JSON", false)
|
|
434
|
+
.action(async (opts) => {
|
|
417
435
|
let spinner = null;
|
|
418
436
|
try {
|
|
437
|
+
const page = Math.max(1, parseInt(opts.page, 10) || 1);
|
|
438
|
+
const limit = Math.min(50, Math.max(1, parseInt(opts.limit, 10) || 20));
|
|
439
|
+
if (!opts.json) {
|
|
440
|
+
spinner = ora("Fetching active personas...").start();
|
|
441
|
+
}
|
|
419
442
|
const client = new ThumbnailGPTClient();
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
443
|
+
const res = await client.listPersonas({ page, limit });
|
|
444
|
+
if (opts.json) {
|
|
445
|
+
console.log(JSON.stringify(res, null, 2));
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
spinner?.stop();
|
|
449
|
+
const personas = res.personas || [];
|
|
423
450
|
if (personas.length === 0) {
|
|
424
451
|
console.log(pc.yellow("\nNo active personas found."));
|
|
425
452
|
console.log(`Create your first persona in ThumbnailGPT at: ${pc.cyan("https://thumbnailgpt.com/studio")}\n`);
|
|
@@ -432,6 +459,10 @@ personasCmd
|
|
|
432
459
|
console.log(`${pc.cyan(p.id)} ${pc.bold(p.name)} ${dateStr}`);
|
|
433
460
|
});
|
|
434
461
|
console.log(pc.dim("--------------------------------------------------------------------------------"));
|
|
462
|
+
if (res.pagination) {
|
|
463
|
+
const { page: currPage, totalPages, total, hasMore } = res.pagination;
|
|
464
|
+
console.log(pc.dim(`Page ${currPage} of ${totalPages} (${total} total personas)${hasMore ? ` • Run with --page ${currPage + 1} to view more` : ""}\n`));
|
|
465
|
+
}
|
|
435
466
|
console.log(`Use in generation: ${pc.green('npx thumbnailgpt generate "..." --persona <id>')}\n`);
|
|
436
467
|
}
|
|
437
468
|
catch (err) {
|
package/dist/types.d.ts
CHANGED
|
@@ -73,3 +73,28 @@ export interface PersonaItem {
|
|
|
73
73
|
name: string;
|
|
74
74
|
created_at?: string;
|
|
75
75
|
}
|
|
76
|
+
export interface PaginationMetadata {
|
|
77
|
+
page: number;
|
|
78
|
+
limit: number;
|
|
79
|
+
total: number;
|
|
80
|
+
totalPages: number;
|
|
81
|
+
hasMore: boolean;
|
|
82
|
+
}
|
|
83
|
+
export interface ListChatsResponse {
|
|
84
|
+
success: boolean;
|
|
85
|
+
chats: Array<{
|
|
86
|
+
id: string;
|
|
87
|
+
title: string;
|
|
88
|
+
created_at: string;
|
|
89
|
+
updated_at: string;
|
|
90
|
+
thumbnails?: ThumbnailItem[];
|
|
91
|
+
}>;
|
|
92
|
+
pagination?: PaginationMetadata;
|
|
93
|
+
error?: string;
|
|
94
|
+
}
|
|
95
|
+
export interface ListPersonasResponse {
|
|
96
|
+
success: boolean;
|
|
97
|
+
personas: PersonaItem[];
|
|
98
|
+
pagination?: PaginationMetadata;
|
|
99
|
+
error?: string;
|
|
100
|
+
}
|
package/package.json
CHANGED