thumbnailgpt 1.0.6 → 1.0.7

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.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.6",
20
+ "User-Agent": "thumbnailgpt-cli/1.0.7",
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.6");
58
+ .version("1.0.7");
12
59
  // 1. Auth Login
13
60
  program
14
61
  .command("login")
@@ -54,9 +101,10 @@ 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
- const spinner = ora("Fetching account details...").start();
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"));
@@ -65,8 +113,7 @@ program
65
113
  console.log(`${pc.gray("Cost Tiers:")} 1K = 10 cr | 2K = 20 cr | 4K = 30 cr\n`);
66
114
  }
67
115
  catch (err) {
68
- console.error(pc.red(`\nError: ${err.message}`));
69
- process.exit(1);
116
+ handleError(err, spinner);
70
117
  }
71
118
  });
72
119
  // 4. One-shot Generate
@@ -83,6 +130,7 @@ program
83
130
  .option("--json", "Output response as raw JSON")
84
131
  .option("--async", "Dispatch job and return immediately without waiting")
85
132
  .action(async (prompt, options) => {
133
+ let spinner = null;
86
134
  try {
87
135
  if (options.ref && options.ref.length > 5) {
88
136
  console.error(pc.red("\nError: A maximum of 5 reference images is allowed."));
@@ -100,7 +148,7 @@ program
100
148
  }
101
149
  const client = new ThumbnailGPTClient();
102
150
  if (options.async) {
103
- const spinner = !options.json && !options.urlOnly ? ora("Dispatching to ThumbnailGPT...").start() : null;
151
+ spinner = !options.json && !options.urlOnly ? ora("Dispatching to ThumbnailGPT...").start() : null;
104
152
  const dispatch = await client.dispatchStudio({
105
153
  prompt,
106
154
  chatId: options.chat,
@@ -121,7 +169,7 @@ program
121
169
  console.log(`Check status with: ${pc.cyan(`thumbnailgpt chats show ${dispatch.chatId}`)}\n`);
122
170
  return;
123
171
  }
124
- const spinner = !options.json && !options.urlOnly ? ora("Validating credits & dispatching...").start() : null;
172
+ spinner = !options.json && !options.urlOnly ? ora("Validating credits & dispatching...").start() : null;
125
173
  const result = await client.generateAndWait({
126
174
  prompt,
127
175
  chatId: options.chat,
@@ -167,8 +215,7 @@ program
167
215
  console.log(`${pc.bold("Web App:")} https://thumbnailgpt.com/studio/${result.chatId}\n`);
168
216
  }
169
217
  catch (err) {
170
- console.error(pc.red(`\nError: ${err.message}`));
171
- process.exit(1);
218
+ handleError(err, spinner);
172
219
  }
173
220
  });
174
221
  // 5. Interactive Chat REPL
@@ -230,8 +277,7 @@ program
230
277
  askPrompt();
231
278
  }
232
279
  catch (err) {
233
- console.error(pc.red(`\nError: ${err.message}`));
234
- process.exit(1);
280
+ handleError(err);
235
281
  }
236
282
  });
237
283
  // 6. Chats Subcommand
@@ -240,9 +286,10 @@ chats
240
286
  .command("list")
241
287
  .description("List recent chat conversations")
242
288
  .action(async () => {
289
+ let spinner = null;
243
290
  try {
244
291
  const client = new ThumbnailGPTClient();
245
- const spinner = ora("Fetching conversations...").start();
292
+ spinner = ora("Fetching conversations...").start();
246
293
  const res = await client.listChats();
247
294
  spinner.stop();
248
295
  const chatList = res.chats || [];
@@ -258,17 +305,17 @@ chats
258
305
  console.log();
259
306
  }
260
307
  catch (err) {
261
- console.error(pc.red(`\nError: ${err.message}`));
262
- process.exit(1);
308
+ handleError(err, spinner);
263
309
  }
264
310
  });
265
311
  chats
266
312
  .command("show <chatId>")
267
313
  .description("View conversation history and thumbnails")
268
314
  .action(async (chatId) => {
315
+ let spinner = null;
269
316
  try {
270
317
  const client = new ThumbnailGPTClient();
271
- const spinner = ora("Fetching thread...").start();
318
+ spinner = ora("Fetching thread...").start();
272
319
  const res = await client.getChat(chatId);
273
320
  spinner.stop();
274
321
  console.log(pc.bold(`\nThread: ${res.chat?.title || chatId}\n`));
@@ -285,8 +332,7 @@ chats
285
332
  console.log();
286
333
  }
287
334
  catch (err) {
288
- console.error(pc.red(`\nError: ${err.message}`));
289
- process.exit(1);
335
+ handleError(err, spinner);
290
336
  }
291
337
  });
292
338
  chats
@@ -304,9 +350,10 @@ thumbnailsCmd
304
350
  .command("get <thumbnailId>")
305
351
  .description("Get detailed information about a thumbnail by ID")
306
352
  .action(async (thumbnailId) => {
353
+ let spinner = null;
307
354
  try {
308
355
  const client = new ThumbnailGPTClient();
309
- const spinner = ora("Fetching thumbnail details...").start();
356
+ spinner = ora("Fetching thumbnail details...").start();
310
357
  const thumb = await client.getThumbnail(thumbnailId);
311
358
  spinner.stop();
312
359
  console.log(pc.bold("\nThumbnail Details:"));
@@ -329,8 +376,7 @@ thumbnailsCmd
329
376
  console.log(pc.dim("--------------------------------------------------------------------------------\n"));
330
377
  }
331
378
  catch (err) {
332
- console.error(pc.red(`\nError: ${err.message}`));
333
- process.exit(1);
379
+ handleError(err, spinner);
334
380
  }
335
381
  });
336
382
  thumbnailsCmd
@@ -342,13 +388,13 @@ thumbnailsCmd
342
388
  const thumb = await client.getThumbnail(thumbnailId);
343
389
  if (!thumb.thumbnail_url) {
344
390
  console.error(pc.yellow(`Thumbnail is not ready yet (status: ${thumb.status || "processing"})`));
345
- process.exit(1);
391
+ process.exitCode = 1;
392
+ return;
346
393
  }
347
394
  console.log(thumb.thumbnail_url);
348
395
  }
349
396
  catch (err) {
350
- console.error(pc.red(`\nError: ${err.message}`));
351
- process.exit(1);
397
+ handleError(err);
352
398
  }
353
399
  });
354
400
  thumbnailsCmd
@@ -366,9 +412,10 @@ personasCmd
366
412
  .command("list")
367
413
  .description("List your active creator personas (only ready personas are shown)")
368
414
  .action(async () => {
415
+ let spinner = null;
369
416
  try {
370
417
  const client = new ThumbnailGPTClient();
371
- const spinner = ora("Fetching active personas...").start();
418
+ spinner = ora("Fetching active personas...").start();
372
419
  const personas = await client.getPersonas();
373
420
  spinner.stop();
374
421
  if (personas.length === 0) {
@@ -386,8 +433,7 @@ personasCmd
386
433
  console.log(`Use in generation: ${pc.green('npx thumbnailgpt generate "..." --persona <id>')}\n`);
387
434
  }
388
435
  catch (err) {
389
- console.error(pc.red(`\nError: ${err.message}`));
390
- process.exit(1);
436
+ handleError(err, spinner);
391
437
  }
392
438
  });
393
439
  personasCmd
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbnailgpt",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Official CLI for ThumbnailGPT — Generate, iterate, and manage viral YouTube thumbnails directly from your terminal",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",