myskillshub 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myskillshub",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "CLI tool for SkillHub skill management",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -12,6 +12,7 @@ const FormData = require('form-data');
12
12
  const { createWriteStream } = require('fs');
13
13
 
14
14
  const program = new Command();
15
+ const DEFAULT_CATEGORY_SLUG = 'dev-tools';
15
16
 
16
17
  program
17
18
  .name('publish')
@@ -73,7 +74,7 @@ program
73
74
  console.log(chalk.cyan(`Skill URL: ${options.apiUrl}/skills/${result.slug}`));
74
75
 
75
76
  } catch (error) {
76
- spinner.fail(chalk.red(`Publish failed: ${error.message}`));
77
+ spinner.fail(chalk.red(`Publish failed: ${formatErrorMessage(error)}`));
77
78
  if (options.verbose) {
78
79
  console.error(error);
79
80
  }
@@ -229,18 +230,25 @@ async function resolveCategoryId(apiUrl, options, frontmatter) {
229
230
  }
230
231
  }
231
232
 
232
- const slugCandidate = options.categorySlug || frontmatter.category_slug || frontmatter.category;
233
- if (!slugCandidate) {
234
- throw new Error('category_id is required. Use --category-id <id> or --category-slug <slug>.');
235
- }
233
+ const slugCandidate = options.categorySlug
234
+ || frontmatter.category_slug
235
+ || frontmatter.category
236
+ || DEFAULT_CATEGORY_SLUG;
236
237
 
237
- const response = await axios.get(`${apiUrl}/api/categories/${encodeURIComponent(String(slugCandidate).trim())}`);
238
- const category = response.data?.data?.category;
239
- if (!response.data?.success || !category?.id) {
240
- throw new Error(`Failed to resolve category slug: ${slugCandidate}`);
241
- }
238
+ try {
239
+ const response = await axios.get(`${apiUrl}/api/categories/${encodeURIComponent(String(slugCandidate).trim())}`);
240
+ const category = response.data?.data?.category;
241
+ if (!response.data?.success || !category?.id) {
242
+ throw new Error(`Failed to resolve category slug: ${slugCandidate}`);
243
+ }
242
244
 
243
- return category.id;
245
+ return category.id;
246
+ } catch (error) {
247
+ if (error.response?.status === 404 && slugCandidate === DEFAULT_CATEGORY_SLUG) {
248
+ throw new Error(`Default category "${DEFAULT_CATEGORY_SLUG}" not found. Use --category-id or --category-slug.`);
249
+ }
250
+ throw new Error(`Failed to resolve category slug: ${slugCandidate}. ${formatErrorMessage(error)}`);
251
+ }
244
252
  }
245
253
 
246
254
  async function getAuthCookie(apiUrl, options, spinner) {
@@ -260,9 +268,38 @@ async function getAuthCookie(apiUrl, options, spinner) {
260
268
  }
261
269
  }
262
270
 
263
- const credentials = await resolveCredentials(options, spinner);
264
- const cookie = await loginAndGetCookie(normalizedApiUrl, credentials.username, credentials.password);
271
+ let usedStoredCredentials = false;
272
+ let credentials = hasCompleteExplicitCredentials
273
+ ? { username: resolvedUsername, password: resolvedPassword }
274
+ : null;
275
+
276
+ if (!credentials) {
277
+ const storedCredentials = getCachedCredentials(normalizedApiUrl);
278
+ if (storedCredentials) {
279
+ credentials = storedCredentials;
280
+ usedStoredCredentials = true;
281
+ }
282
+ }
283
+
284
+ if (!credentials) {
285
+ credentials = await resolveCredentials(options, spinner);
286
+ }
287
+
288
+ let cookie;
289
+ try {
290
+ cookie = await loginAndGetCookie(normalizedApiUrl, credentials.username, credentials.password);
291
+ } catch (error) {
292
+ if (usedStoredCredentials) {
293
+ // Stored credentials may be stale, fallback to interactive prompt once.
294
+ credentials = await resolveCredentials(options, spinner);
295
+ cookie = await loginAndGetCookie(normalizedApiUrl, credentials.username, credentials.password);
296
+ } else {
297
+ throw error;
298
+ }
299
+ }
300
+
265
301
  saveCachedCookie(normalizedApiUrl, cookie);
302
+ saveCachedCredentials(normalizedApiUrl, credentials.username, credentials.password);
266
303
  return { cookie, fromCache: false };
267
304
  }
268
305
 
@@ -304,20 +341,43 @@ function getCachedCookie(apiUrl) {
304
341
  return store.sessions?.[apiUrl]?.cookie || null;
305
342
  }
306
343
 
307
- function saveCachedCookie(apiUrl, cookie) {
344
+ function getCachedCredentials(apiUrl) {
345
+ const store = readSessionStore();
346
+ const username = store.sessions?.[apiUrl]?.username || '';
347
+ const password = store.sessions?.[apiUrl]?.password || '';
348
+ if (!username || !password) {
349
+ return null;
350
+ }
351
+ return { username, password };
352
+ }
353
+
354
+ function saveSessionData(apiUrl, patch) {
308
355
  const store = readSessionStore();
309
356
  store.sessions = store.sessions || {};
310
357
  store.sessions[apiUrl] = {
311
- cookie,
312
- updatedAt: new Date().toISOString()
358
+ ...(store.sessions[apiUrl] || {}),
359
+ ...patch,
360
+ updatedAt: new Date().toISOString(),
313
361
  };
314
362
  writeSessionStore(store);
315
363
  }
316
364
 
365
+ function saveCachedCookie(apiUrl, cookie) {
366
+ saveSessionData(apiUrl, { cookie });
367
+ }
368
+
369
+ function saveCachedCredentials(apiUrl, username, password) {
370
+ saveSessionData(apiUrl, { username, password });
371
+ }
372
+
317
373
  function removeCachedCookie(apiUrl) {
318
374
  const store = readSessionStore();
319
375
  if (store.sessions && store.sessions[apiUrl]) {
320
- delete store.sessions[apiUrl];
376
+ delete store.sessions[apiUrl].cookie;
377
+ store.sessions[apiUrl].updatedAt = new Date().toISOString();
378
+ if (!store.sessions[apiUrl].username || !store.sessions[apiUrl].password) {
379
+ delete store.sessions[apiUrl];
380
+ }
321
381
  writeSessionStore(store);
322
382
  }
323
383
  }
@@ -396,7 +456,7 @@ function promptPassword(question) {
396
456
  terminal: true
397
457
  });
398
458
 
399
- rl.stdoutMuted = true;
459
+ rl.stdoutMuted = false;
400
460
  const originalWriteToOutput = rl._writeToOutput;
401
461
  rl._writeToOutput = function writeToOutput(stringToWrite) {
402
462
  if (rl.stdoutMuted) {
@@ -415,6 +475,7 @@ function promptPassword(question) {
415
475
  rl.close();
416
476
  resolve(String(password || '').trim());
417
477
  });
478
+ rl.stdoutMuted = true;
418
479
  });
419
480
  }
420
481
 
@@ -435,7 +496,7 @@ async function loginAndGetCookie(apiUrl, username, password) {
435
496
  function createZipPackage(directory) {
436
497
  return new Promise((resolve, reject) => {
437
498
  const baseName = path.basename(path.resolve(directory));
438
- const zipPath = path.join(process.cwd(), `${baseName}-package-${Date.now()}.zip`);
499
+ const zipPath = path.join(os.tmpdir(), `${baseName}-package-${Date.now()}.zip`);
439
500
  const output = createWriteStream(zipPath);
440
501
  const archive = archiver('zip', {
441
502
  zlib: { level: 9 }
@@ -461,7 +522,10 @@ function createZipPackage(directory) {
461
522
  ignore: [
462
523
  '**/node_modules/**',
463
524
  '**/.git/**',
464
- '**/.DS_Store'
525
+ '**/.DS_Store',
526
+ '**/*-package-*.zip',
527
+ '**/*-package.zip',
528
+ '**/.-package.zip'
465
529
  ]
466
530
  });
467
531
 
@@ -493,4 +557,28 @@ async function uploadSkill(zipPath, skillData, categoryId, apiUrl, authCookie) {
493
557
  }
494
558
  }
495
559
 
560
+ function formatErrorMessage(error) {
561
+ if (!error) {
562
+ return 'Unknown error';
563
+ }
564
+
565
+ const status = error.response?.status;
566
+ const apiError = error.response?.data?.error;
567
+ const directMessage = error.response?.data?.message;
568
+
569
+ if (apiError?.message) {
570
+ return status ? `HTTP ${status}: ${apiError.message}` : apiError.message;
571
+ }
572
+
573
+ if (directMessage) {
574
+ return status ? `HTTP ${status}: ${directMessage}` : directMessage;
575
+ }
576
+
577
+ if (status) {
578
+ return `HTTP ${status}: ${error.message}`;
579
+ }
580
+
581
+ return error.message || 'Unknown error';
582
+ }
583
+
496
584
  module.exports = program;