thumbnailgpt 1.0.6 → 1.0.8
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/README.md +22 -27
- package/dist/client.js +1 -1
- package/dist/index.js +76 -28
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,14 +15,14 @@ Generate, iterate, download, and manage viral, high-CTR YouTube thumbnails direc
|
|
|
15
15
|
|
|
16
16
|
## Highlights
|
|
17
17
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
18
|
+
- **Zero-Install Execution**: Run any command immediately via `npx thumbnailgpt`.
|
|
19
|
+
- **Multi-Resolution Rendering**: Generate in standard **1K**, HD **2K**, or Ultra-HD **4K**.
|
|
20
|
+
- **Dual Aspect Ratios**: Native support for **16:9** (YouTube standard) and **9:16** (YouTube Shorts / TikTok / Reels).
|
|
21
|
+
- **Creator Persona Consistency**: Feature your trained creator persona across any generation using `--persona <id>`.
|
|
22
|
+
- **Conversational Iteration**: Thread your edits using `--chat <chatId>` to refine text, lighting, facial expression, or background details.
|
|
23
|
+
- **Interactive Terminal REPL**: Full interactive terminal chat session with live preview links (`thumbnailgpt chat`).
|
|
24
|
+
- **Scripting & Automation Ready**: Output raw image URLs with `--url-only` or machine-readable JSON with `--json` for shell pipes, webhooks, and GitHub Actions.
|
|
25
|
+
- **Account Deletion Guardrails**: Enforces production security; destructive deletions are restricted to the web dashboard to prevent accidental loss from automated scripts or autonomous AI agents.
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
@@ -62,20 +62,16 @@ npx thumbnailgpt login
|
|
|
62
62
|
npx thumbnailgpt login --key tgpt_live_YOUR_API_KEY
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
### 2. Verify Your Account
|
|
65
|
+
### 2. Verify Your Account Status
|
|
66
66
|
```bash
|
|
67
67
|
npx thumbnailgpt whoami
|
|
68
68
|
```
|
|
69
69
|
*Output:*
|
|
70
70
|
```text
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Pricing Tiers:
|
|
76
|
-
1K (Standard): 10 credits
|
|
77
|
-
2K (HD): 20 credits
|
|
78
|
-
4K (Ultra HD): 30 credits
|
|
71
|
+
ThumbnailGPT Account
|
|
72
|
+
Account: user@example.com
|
|
73
|
+
Plan Status: ACTIVE
|
|
74
|
+
Credits: 583 available
|
|
79
75
|
```
|
|
80
76
|
|
|
81
77
|
### 3. Generate a Thumbnail
|
|
@@ -84,7 +80,7 @@ npx thumbnailgpt generate "Extreme 100 Days Hardcore Survival in Minecraft" --re
|
|
|
84
80
|
```
|
|
85
81
|
*Output:*
|
|
86
82
|
```text
|
|
87
|
-
|
|
83
|
+
Generation complete (14s)
|
|
88
84
|
|
|
89
85
|
Thumbnail URL: https://assets.thumbnailgpt.com/generations/.../thumbnail.png
|
|
90
86
|
Chat Thread: https://thumbnailgpt.com/studio/3c9d1e5f-...
|
|
@@ -109,7 +105,7 @@ npx thumbnailgpt generate [options] <prompt>
|
|
|
109
105
|
| Flag | Short | Default | Description |
|
|
110
106
|
| :--- | :---: | :---: | :--- |
|
|
111
107
|
| `--ratio <ratio>` | `-r` | `16:9` | Aspect ratio: `16:9` (YouTube standard) or `9:16` (Shorts / Reels) |
|
|
112
|
-
| `--res <resolution>` | `-q` | `1K` | Resolution quality: `1K
|
|
108
|
+
| `--res <resolution>` | `-q` | `1K` | Resolution quality: `1K`, `2K`, or `4K` |
|
|
113
109
|
| `--ref <urls...>` | | | Up to 5 public image URLs or base64 data URIs as visual references |
|
|
114
110
|
| `--yt <url>` | | | YouTube video URL to extract title, context, and current thumbnail |
|
|
115
111
|
| `--persona <personaId>` | `-p` | | Creator Persona UUID to feature in the generated thumbnail |
|
|
@@ -172,10 +168,9 @@ npx thumbnailgpt chat 3c9d1e5f-c648-4b3c-a6f0-811d073c22c2
|
|
|
172
168
|
```
|
|
173
169
|
|
|
174
170
|
Commands available inside interactive chat mode:
|
|
175
|
-
- Type your prompt
|
|
171
|
+
- Type your prompt to iterate.
|
|
176
172
|
- `/url` — Display the latest generated thumbnail URL.
|
|
177
173
|
- `/open` — Open the current thumbnail in your default web browser.
|
|
178
|
-
- `/cost` — View credit cost tiers.
|
|
179
174
|
- `/help` — Display interactive mode shortcuts.
|
|
180
175
|
- `/exit` or `exit` — Exit interactive session.
|
|
181
176
|
|
|
@@ -183,7 +178,7 @@ Commands available inside interactive chat mode:
|
|
|
183
178
|
|
|
184
179
|
### `thumbnailgpt whoami`
|
|
185
180
|
|
|
186
|
-
Display your authenticated email, subscription
|
|
181
|
+
Display your authenticated email, subscription status, and available balance.
|
|
187
182
|
|
|
188
183
|
```bash
|
|
189
184
|
npx thumbnailgpt whoami
|
|
@@ -304,17 +299,17 @@ Now you can instruct Claude or Cursor:
|
|
|
304
299
|
|
|
305
300
|
To prevent accidental data loss or unauthorized bulk deletions by third-party scripts and autonomous AI agents:
|
|
306
301
|
- Destructive deletions (`DELETE` operations) are disabled over Personal API Keys, the CLI, and the MCP server.
|
|
307
|
-
- Resource creation, iteration,
|
|
302
|
+
- Resource creation, iteration, account balance checks, persona styling, and asset retrievals are fully enabled.
|
|
308
303
|
- Deletions can be performed safely by the account owner inside the **[ThumbnailGPT Web Studio](https://thumbnailgpt.com/studio)**.
|
|
309
304
|
|
|
310
305
|
---
|
|
311
306
|
|
|
312
307
|
## Support & Resources
|
|
313
308
|
|
|
314
|
-
-
|
|
315
|
-
-
|
|
316
|
-
-
|
|
317
|
-
-
|
|
309
|
+
- Web Studio: [https://thumbnailgpt.com](https://thumbnailgpt.com)
|
|
310
|
+
- API & CLI Docs: [https://thumbnailgpt.com/docs](https://thumbnailgpt.com/docs)
|
|
311
|
+
- API Key Management: [https://thumbnailgpt.com/studio?settings=api-key](https://thumbnailgpt.com/studio?settings=api-key)
|
|
312
|
+
- Support Email: [support@thumbnailgpt.com](mailto:support@thumbnailgpt.com)
|
|
318
313
|
|
|
319
314
|
---
|
|
320
315
|
|
package/dist/client.js
CHANGED
|
@@ -17,7 +17,7 @@ export class ThumbnailGPTClient {
|
|
|
17
17
|
"Content-Type": "application/json",
|
|
18
18
|
Authorization: `Bearer ${this.apiKey}`,
|
|
19
19
|
Origin: this.apiUrl,
|
|
20
|
-
"User-Agent": "thumbnailgpt-cli/1.0.
|
|
20
|
+
"User-Agent": "thumbnailgpt-cli/1.0.8",
|
|
21
21
|
...options.headers,
|
|
22
22
|
};
|
|
23
23
|
const res = await fetch(url, { ...options, headers });
|
package/dist/index.js
CHANGED
|
@@ -4,11 +4,58 @@ import ora from "ora";
|
|
|
4
4
|
import pc from "picocolors";
|
|
5
5
|
import { setApiKey, clearApiKey, setApiUrl } from "./config.js";
|
|
6
6
|
import { ThumbnailGPTClient } from "./client.js";
|
|
7
|
+
/**
|
|
8
|
+
* Safely handle errors in CLI commands without crashing libuv timers or exposing internal server details
|
|
9
|
+
*/
|
|
10
|
+
function handleError(err, spinner) {
|
|
11
|
+
if (spinner) {
|
|
12
|
+
try {
|
|
13
|
+
spinner.stop();
|
|
14
|
+
}
|
|
15
|
+
catch { }
|
|
16
|
+
}
|
|
17
|
+
const rawMsg = String(err?.message || err || "").trim();
|
|
18
|
+
const status = Number(err?.status || 0);
|
|
19
|
+
let friendlyMessage = rawMsg;
|
|
20
|
+
// Mask database, internal, or raw HTML errors
|
|
21
|
+
if (status === 401 ||
|
|
22
|
+
rawMsg.toLowerCase().includes("api key") ||
|
|
23
|
+
rawMsg.toLowerCase().includes("unauthorized") ||
|
|
24
|
+
rawMsg.toLowerCase().includes("revoked")) {
|
|
25
|
+
friendlyMessage = "Authentication failed: Invalid, expired, or revoked API key. Please run 'thumbnailgpt login' to update your key.";
|
|
26
|
+
}
|
|
27
|
+
else if (status === 403 || rawMsg.toLowerCase().includes("forbidden")) {
|
|
28
|
+
friendlyMessage = "Access restricted: You do not have permission to access this resource.";
|
|
29
|
+
}
|
|
30
|
+
else if (status === 429 ||
|
|
31
|
+
rawMsg.toLowerCase().includes("rate limit") ||
|
|
32
|
+
rawMsg.toLowerCase().includes("too many requests")) {
|
|
33
|
+
friendlyMessage = "Request rate limit reached. Please wait a moment before trying again.";
|
|
34
|
+
}
|
|
35
|
+
else if (status >= 500 ||
|
|
36
|
+
rawMsg.toLowerCase().includes("internal server error") ||
|
|
37
|
+
rawMsg.toLowerCase().includes("service unavailable")) {
|
|
38
|
+
friendlyMessage = "ThumbnailGPT services are temporarily busy. Please try again in a few moments.";
|
|
39
|
+
}
|
|
40
|
+
else if (rawMsg.includes("ECONNREFUSED") ||
|
|
41
|
+
rawMsg.includes("ENOTFOUND") ||
|
|
42
|
+
rawMsg.includes("fetch failed")) {
|
|
43
|
+
friendlyMessage = "Unable to connect to ThumbnailGPT. Please check your internet connection.";
|
|
44
|
+
}
|
|
45
|
+
else if (rawMsg.includes("<!DOCTYPE") ||
|
|
46
|
+
rawMsg.includes("<html") ||
|
|
47
|
+
rawMsg.includes("SQL") ||
|
|
48
|
+
rawMsg.includes("Postgres")) {
|
|
49
|
+
friendlyMessage = "An unexpected error occurred. Please try again or visit https://thumbnailgpt.com for status updates.";
|
|
50
|
+
}
|
|
51
|
+
console.error(pc.red(`\nError: ${friendlyMessage}\n`));
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
}
|
|
7
54
|
const program = new Command();
|
|
8
55
|
program
|
|
9
56
|
.name("thumbnailgpt")
|
|
10
57
|
.description("Official CLI for ThumbnailGPT — Generate, iterate, and manage thumbnails from your terminal")
|
|
11
|
-
.version("1.0.
|
|
58
|
+
.version("1.0.8");
|
|
12
59
|
// 1. Auth Login
|
|
13
60
|
program
|
|
14
61
|
.command("login")
|
|
@@ -54,19 +101,21 @@ program
|
|
|
54
101
|
.command("whoami")
|
|
55
102
|
.description("Display your account and available credit balance")
|
|
56
103
|
.action(async () => {
|
|
104
|
+
let spinner = null;
|
|
57
105
|
try {
|
|
58
106
|
const client = new ThumbnailGPTClient();
|
|
59
|
-
|
|
107
|
+
spinner = ora("Fetching account details...").start();
|
|
60
108
|
const credits = await client.getCredits();
|
|
61
109
|
spinner.stop();
|
|
62
110
|
console.log(pc.bold("\nThumbnailGPT Account"));
|
|
111
|
+
if (credits.email) {
|
|
112
|
+
console.log(`${pc.gray("Account:")} ${pc.white(pc.bold(credits.email))}`);
|
|
113
|
+
}
|
|
63
114
|
console.log(`${pc.gray("Plan Status:")} ${pc.cyan(credits.subscription_status.toUpperCase())}`);
|
|
64
|
-
console.log(`${pc.gray("Credits:")} ${pc.green(pc.bold(String(credits.credits)))} available`);
|
|
65
|
-
console.log(`${pc.gray("Cost Tiers:")} 1K = 10 cr | 2K = 20 cr | 4K = 30 cr\n`);
|
|
115
|
+
console.log(`${pc.gray("Credits:")} ${pc.green(pc.bold(String(credits.credits)))} available\n`);
|
|
66
116
|
}
|
|
67
117
|
catch (err) {
|
|
68
|
-
|
|
69
|
-
process.exit(1);
|
|
118
|
+
handleError(err, spinner);
|
|
70
119
|
}
|
|
71
120
|
});
|
|
72
121
|
// 4. One-shot Generate
|
|
@@ -83,6 +132,7 @@ program
|
|
|
83
132
|
.option("--json", "Output response as raw JSON")
|
|
84
133
|
.option("--async", "Dispatch job and return immediately without waiting")
|
|
85
134
|
.action(async (prompt, options) => {
|
|
135
|
+
let spinner = null;
|
|
86
136
|
try {
|
|
87
137
|
if (options.ref && options.ref.length > 5) {
|
|
88
138
|
console.error(pc.red("\nError: A maximum of 5 reference images is allowed."));
|
|
@@ -95,12 +145,12 @@ program
|
|
|
95
145
|
}
|
|
96
146
|
const res = (options.res || "1K").toUpperCase().trim();
|
|
97
147
|
if (res !== "1K" && res !== "2K" && res !== "4K") {
|
|
98
|
-
console.error(pc.red(`\nError: Invalid resolution "${res}". Supported resolutions: 1K
|
|
148
|
+
console.error(pc.red(`\nError: Invalid resolution "${res}". Supported resolutions: 1K, 2K, 4K`));
|
|
99
149
|
process.exit(1);
|
|
100
150
|
}
|
|
101
151
|
const client = new ThumbnailGPTClient();
|
|
102
152
|
if (options.async) {
|
|
103
|
-
|
|
153
|
+
spinner = !options.json && !options.urlOnly ? ora("Dispatching to ThumbnailGPT...").start() : null;
|
|
104
154
|
const dispatch = await client.dispatchStudio({
|
|
105
155
|
prompt,
|
|
106
156
|
chatId: options.chat,
|
|
@@ -121,7 +171,7 @@ program
|
|
|
121
171
|
console.log(`Check status with: ${pc.cyan(`thumbnailgpt chats show ${dispatch.chatId}`)}\n`);
|
|
122
172
|
return;
|
|
123
173
|
}
|
|
124
|
-
|
|
174
|
+
spinner = !options.json && !options.urlOnly ? ora("Validating credits & dispatching...").start() : null;
|
|
125
175
|
const result = await client.generateAndWait({
|
|
126
176
|
prompt,
|
|
127
177
|
chatId: options.chat,
|
|
@@ -167,8 +217,7 @@ program
|
|
|
167
217
|
console.log(`${pc.bold("Web App:")} https://thumbnailgpt.com/studio/${result.chatId}\n`);
|
|
168
218
|
}
|
|
169
219
|
catch (err) {
|
|
170
|
-
|
|
171
|
-
process.exit(1);
|
|
220
|
+
handleError(err, spinner);
|
|
172
221
|
}
|
|
173
222
|
});
|
|
174
223
|
// 5. Interactive Chat REPL
|
|
@@ -230,8 +279,7 @@ program
|
|
|
230
279
|
askPrompt();
|
|
231
280
|
}
|
|
232
281
|
catch (err) {
|
|
233
|
-
|
|
234
|
-
process.exit(1);
|
|
282
|
+
handleError(err);
|
|
235
283
|
}
|
|
236
284
|
});
|
|
237
285
|
// 6. Chats Subcommand
|
|
@@ -240,9 +288,10 @@ chats
|
|
|
240
288
|
.command("list")
|
|
241
289
|
.description("List recent chat conversations")
|
|
242
290
|
.action(async () => {
|
|
291
|
+
let spinner = null;
|
|
243
292
|
try {
|
|
244
293
|
const client = new ThumbnailGPTClient();
|
|
245
|
-
|
|
294
|
+
spinner = ora("Fetching conversations...").start();
|
|
246
295
|
const res = await client.listChats();
|
|
247
296
|
spinner.stop();
|
|
248
297
|
const chatList = res.chats || [];
|
|
@@ -258,17 +307,17 @@ chats
|
|
|
258
307
|
console.log();
|
|
259
308
|
}
|
|
260
309
|
catch (err) {
|
|
261
|
-
|
|
262
|
-
process.exit(1);
|
|
310
|
+
handleError(err, spinner);
|
|
263
311
|
}
|
|
264
312
|
});
|
|
265
313
|
chats
|
|
266
314
|
.command("show <chatId>")
|
|
267
315
|
.description("View conversation history and thumbnails")
|
|
268
316
|
.action(async (chatId) => {
|
|
317
|
+
let spinner = null;
|
|
269
318
|
try {
|
|
270
319
|
const client = new ThumbnailGPTClient();
|
|
271
|
-
|
|
320
|
+
spinner = ora("Fetching thread...").start();
|
|
272
321
|
const res = await client.getChat(chatId);
|
|
273
322
|
spinner.stop();
|
|
274
323
|
console.log(pc.bold(`\nThread: ${res.chat?.title || chatId}\n`));
|
|
@@ -285,8 +334,7 @@ chats
|
|
|
285
334
|
console.log();
|
|
286
335
|
}
|
|
287
336
|
catch (err) {
|
|
288
|
-
|
|
289
|
-
process.exit(1);
|
|
337
|
+
handleError(err, spinner);
|
|
290
338
|
}
|
|
291
339
|
});
|
|
292
340
|
chats
|
|
@@ -304,9 +352,10 @@ thumbnailsCmd
|
|
|
304
352
|
.command("get <thumbnailId>")
|
|
305
353
|
.description("Get detailed information about a thumbnail by ID")
|
|
306
354
|
.action(async (thumbnailId) => {
|
|
355
|
+
let spinner = null;
|
|
307
356
|
try {
|
|
308
357
|
const client = new ThumbnailGPTClient();
|
|
309
|
-
|
|
358
|
+
spinner = ora("Fetching thumbnail details...").start();
|
|
310
359
|
const thumb = await client.getThumbnail(thumbnailId);
|
|
311
360
|
spinner.stop();
|
|
312
361
|
console.log(pc.bold("\nThumbnail Details:"));
|
|
@@ -329,8 +378,7 @@ thumbnailsCmd
|
|
|
329
378
|
console.log(pc.dim("--------------------------------------------------------------------------------\n"));
|
|
330
379
|
}
|
|
331
380
|
catch (err) {
|
|
332
|
-
|
|
333
|
-
process.exit(1);
|
|
381
|
+
handleError(err, spinner);
|
|
334
382
|
}
|
|
335
383
|
});
|
|
336
384
|
thumbnailsCmd
|
|
@@ -342,13 +390,13 @@ thumbnailsCmd
|
|
|
342
390
|
const thumb = await client.getThumbnail(thumbnailId);
|
|
343
391
|
if (!thumb.thumbnail_url) {
|
|
344
392
|
console.error(pc.yellow(`Thumbnail is not ready yet (status: ${thumb.status || "processing"})`));
|
|
345
|
-
process.
|
|
393
|
+
process.exitCode = 1;
|
|
394
|
+
return;
|
|
346
395
|
}
|
|
347
396
|
console.log(thumb.thumbnail_url);
|
|
348
397
|
}
|
|
349
398
|
catch (err) {
|
|
350
|
-
|
|
351
|
-
process.exit(1);
|
|
399
|
+
handleError(err);
|
|
352
400
|
}
|
|
353
401
|
});
|
|
354
402
|
thumbnailsCmd
|
|
@@ -366,9 +414,10 @@ personasCmd
|
|
|
366
414
|
.command("list")
|
|
367
415
|
.description("List your active creator personas (only ready personas are shown)")
|
|
368
416
|
.action(async () => {
|
|
417
|
+
let spinner = null;
|
|
369
418
|
try {
|
|
370
419
|
const client = new ThumbnailGPTClient();
|
|
371
|
-
|
|
420
|
+
spinner = ora("Fetching active personas...").start();
|
|
372
421
|
const personas = await client.getPersonas();
|
|
373
422
|
spinner.stop();
|
|
374
423
|
if (personas.length === 0) {
|
|
@@ -386,8 +435,7 @@ personasCmd
|
|
|
386
435
|
console.log(`Use in generation: ${pc.green('npx thumbnailgpt generate "..." --persona <id>')}\n`);
|
|
387
436
|
}
|
|
388
437
|
catch (err) {
|
|
389
|
-
|
|
390
|
-
process.exit(1);
|
|
438
|
+
handleError(err, spinner);
|
|
391
439
|
}
|
|
392
440
|
});
|
|
393
441
|
personasCmd
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED