tiendu 0.6.0 → 0.7.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/lib/preview.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import * as p from "@clack/prompts";
2
1
  import { loadConfigOrFail, writeConfig } from "./config.mjs";
3
2
  import { apiFetch, fetchPreview } from "./api.mjs";
3
+ import * as ui from "./ui.mjs";
4
4
 
5
5
  export const buildPreviewUrl = (apiBaseUrl, previewHostname) => {
6
6
  const base = new URL(apiBaseUrl);
@@ -215,24 +215,41 @@ export const resolvePreviewKeyInteractively = async ({ config, credentials }) =>
215
215
  return config.previewKey;
216
216
  }
217
217
 
218
- p.log.warn(`Stored preview ${config.previewKey} was not found. Please select a preview.`);
218
+ ui.log.warn(`Stored preview ${config.previewKey} was not found. Please select a preview.`);
219
219
  const { previewKey: _, ...rest } = config;
220
220
  await writeConfig(rest);
221
221
  } else {
222
- p.log.warn("No preview attached.");
222
+ ui.log.warn("No preview attached.");
223
223
  }
224
224
 
225
225
  // 2. List previews
226
226
  const listResult = await listPreviews(apiBaseUrl, apiKey, storeId);
227
227
  if (!listResult.ok) {
228
- p.log.error(listResult.error);
228
+ ui.log.error(listResult.error);
229
229
  process.exit(1);
230
230
  }
231
231
 
232
232
  const previews = listResult.data;
233
+ const singlePreview = resolveActivePreview(previews);
234
+
235
+ if (singlePreview) {
236
+ await writeConfig({ ...config, previewKey: singlePreview.previewKey });
237
+ const displayName = getPreviewDisplayName(singlePreview);
238
+ ui.log.success(`Using preview "${displayName}" (${singlePreview.previewKey})`);
239
+ return singlePreview.previewKey;
240
+ }
233
241
 
234
242
  if (previews.length === 0) {
235
- p.log.info("No previews found for this store.");
243
+ ui.log.info("No previews found for this store.");
244
+ if (!ui.isInteractive()) {
245
+ ui.log.error("Create a preview first with tiendu preview create [name].");
246
+ process.exit(1);
247
+ }
248
+ }
249
+
250
+ if (!ui.isInteractive()) {
251
+ ui.log.error("No preview selected. Provide a preview key or attach one with tiendu preview attach <key>.");
252
+ process.exit(1);
236
253
  }
237
254
 
238
255
  // 3. Show picker
@@ -248,37 +265,37 @@ export const resolvePreviewKeyInteractively = async ({ config, credentials }) =>
248
265
  },
249
266
  ];
250
267
 
251
- const selected = await p.select({
268
+ const selected = await ui.select({
252
269
  message: "Select a preview",
253
270
  options,
254
271
  });
255
272
 
256
- if (p.isCancel(selected)) {
257
- p.cancel("Cancelled.");
273
+ if (ui.isCancel(selected)) {
274
+ ui.cancel("Cancelled.");
258
275
  process.exit(0);
259
276
  }
260
277
 
261
278
  // 4. Handle create new
262
279
  if (selected === CREATE_NEW_VALUE) {
263
- const nameInput = await p.text({
280
+ const nameInput = await ui.text({
264
281
  message: "Preview name (optional)",
265
282
  placeholder: "Press Enter to skip",
266
283
  defaultValue: "",
267
284
  });
268
285
 
269
- if (p.isCancel(nameInput)) {
270
- p.cancel("Cancelled.");
286
+ if (ui.isCancel(nameInput)) {
287
+ ui.cancel("Cancelled.");
271
288
  process.exit(0);
272
289
  }
273
290
 
274
291
  const name = (nameInput ?? "").trim();
275
- const spinner = p.spinner();
292
+ const spinner = ui.spinner();
276
293
  spinner.start("Creating preview...");
277
294
 
278
295
  const createResult = await createPreview(apiBaseUrl, apiKey, storeId, name);
279
296
  if (!createResult.ok) {
280
297
  spinner.stop("Failed to create preview.", 1);
281
- p.log.error(createResult.error);
298
+ ui.log.error(createResult.error);
282
299
  process.exit(1);
283
300
  }
284
301
 
@@ -286,7 +303,7 @@ export const resolvePreviewKeyInteractively = async ({ config, credentials }) =>
286
303
  const displayName = getPreviewDisplayName(preview);
287
304
  const url = getPreviewUrl(apiBaseUrl, preview);
288
305
  spinner.stop(`Preview "${displayName}" created (${preview.previewKey})`);
289
- p.log.message(` ${url}`);
306
+ ui.log.message(` ${url}`);
290
307
 
291
308
  await writeConfig({ ...config, previewKey: preview.previewKey });
292
309
  return preview.previewKey;
@@ -296,7 +313,7 @@ export const resolvePreviewKeyInteractively = async ({ config, credentials }) =>
296
313
  await writeConfig({ ...config, previewKey: selected });
297
314
  const selectedPreview = previews.find((p) => p.previewKey === selected);
298
315
  const displayName = selectedPreview ? getPreviewDisplayName(selectedPreview) : selected;
299
- p.log.success(`Attached to "${displayName}" (${selected})`);
316
+ ui.log.success(`Attached to "${displayName}" (${selected})`);
300
317
 
301
318
  return selected;
302
319
  };
@@ -308,7 +325,7 @@ export const resolvePreviewKeyInteractively = async ({ config, credentials }) =>
308
325
  export const previewCreate = async (name) => {
309
326
  const { config, credentials } = await loadConfigOrFail();
310
327
 
311
- const spinner = p.spinner();
328
+ const spinner = ui.spinner();
312
329
  spinner.start("Creating preview...");
313
330
 
314
331
  const result = await createPreview(
@@ -320,7 +337,7 @@ export const previewCreate = async (name) => {
320
337
 
321
338
  if (!result.ok) {
322
339
  spinner.stop("Failed to create preview.", 1);
323
- p.log.error(result.error);
340
+ ui.log.error(result.error);
324
341
  process.exit(1);
325
342
  }
326
343
 
@@ -328,7 +345,7 @@ export const previewCreate = async (name) => {
328
345
  const url = getPreviewUrl(config.apiBaseUrl, preview);
329
346
  const displayName = getPreviewDisplayName(preview);
330
347
  spinner.stop(`Preview "${displayName}" created (${preview.previewKey})`);
331
- p.log.message(` ${url}`);
348
+ ui.log.message(` ${url}`);
332
349
 
333
350
  await writeConfig({ ...config, previewKey: preview.previewKey });
334
351
  };
@@ -336,7 +353,7 @@ export const previewCreate = async (name) => {
336
353
  export const previewList = async () => {
337
354
  const { config, credentials } = await loadConfigOrFail();
338
355
 
339
- const spinner = p.spinner();
356
+ const spinner = ui.spinner();
340
357
  spinner.start("Fetching previews...");
341
358
 
342
359
  const result = await listPreviews(
@@ -347,7 +364,7 @@ export const previewList = async () => {
347
364
 
348
365
  if (!result.ok) {
349
366
  spinner.stop("Failed to fetch previews.", 1);
350
- p.log.error(result.error);
367
+ ui.log.error(result.error);
351
368
  process.exit(1);
352
369
  }
353
370
 
@@ -365,10 +382,10 @@ export const previewList = async () => {
365
382
  const indicator = isAttached ? " \u2190 attached" : "";
366
383
  const url = buildPreviewUrl(config.apiBaseUrl, preview.previewHostname);
367
384
  const displayName = getPreviewDisplayName(preview);
368
- p.log.message(` ${displayName} ${url}${indicator}`);
385
+ ui.log.message(` ${displayName} ${url}${indicator}`);
369
386
  }
370
387
 
371
- p.log.info("Tip: run tiendu preview attach <key> to switch previews.");
388
+ ui.log.info("Tip: run tiendu preview attach <key> to switch previews.");
372
389
  };
373
390
 
374
391
  const formatRelativeDate = (value) => {
@@ -392,7 +409,7 @@ export const previewShow = async () => {
392
409
  const { config, credentials } = await loadConfigOrFail();
393
410
 
394
411
  if (!config.previewKey) {
395
- p.log.warn("No preview attached. Run tiendu preview list or tiendu preview create.");
412
+ ui.log.warn("No preview attached. Run tiendu preview list or tiendu preview create.");
396
413
  process.exit(0);
397
414
  }
398
415
 
@@ -404,8 +421,8 @@ export const previewShow = async () => {
404
421
  );
405
422
 
406
423
  if (!result.ok) {
407
- p.log.warn(`Stored preview ${config.previewKey} was not found.`);
408
- p.log.info("Run tiendu preview list to see available previews.");
424
+ ui.log.warn(`Stored preview ${config.previewKey} was not found.`);
425
+ ui.log.info("Run tiendu preview list to see available previews.");
409
426
  process.exit(1);
410
427
  }
411
428
 
@@ -413,7 +430,7 @@ export const previewShow = async () => {
413
430
  const url = getPreviewUrl(config.apiBaseUrl, preview);
414
431
  const displayName = getPreviewDisplayName(preview);
415
432
 
416
- p.note(
433
+ ui.note(
417
434
  [
418
435
  `Name: ${displayName}`,
419
436
  `Key: ${preview.previewKey}`,
@@ -428,11 +445,15 @@ export const previewAttach = async (keyArg) => {
428
445
  const { config, credentials } = await loadConfigOrFail();
429
446
 
430
447
  if (!keyArg) {
448
+ if (!ui.isInteractive()) {
449
+ ui.log.error("Preview key required in non-interactive mode. Use: tiendu preview attach <key>");
450
+ process.exit(1);
451
+ }
431
452
  await resolvePreviewKeyInteractively({ config, credentials });
432
453
  return;
433
454
  }
434
455
 
435
- const spinner = p.spinner();
456
+ const spinner = ui.spinner();
436
457
  spinner.start("Validating preview...");
437
458
 
438
459
  const result = await fetchPreview(
@@ -444,7 +465,7 @@ export const previewAttach = async (keyArg) => {
444
465
 
445
466
  if (!result.ok) {
446
467
  spinner.stop("Preview not found.", 1);
447
- p.log.error("Preview not found. Run tiendu preview list to see available previews.");
468
+ ui.log.error("Preview not found. Run tiendu preview list to see available previews.");
448
469
  process.exit(1);
449
470
  }
450
471
 
@@ -452,7 +473,7 @@ export const previewAttach = async (keyArg) => {
452
473
  const url = getPreviewUrl(config.apiBaseUrl, preview);
453
474
  const displayName = getPreviewDisplayName(preview);
454
475
  spinner.stop(`Attached to preview "${displayName}" (${preview.previewKey})`);
455
- p.log.message(` ${url}`);
476
+ ui.log.message(` ${url}`);
456
477
 
457
478
  await writeConfig({ ...config, previewKey: preview.previewKey });
458
479
  };
@@ -461,7 +482,7 @@ export const previewDetach = async () => {
461
482
  const { config } = await loadConfigOrFail();
462
483
 
463
484
  if (!config.previewKey) {
464
- p.log.warn("No preview is currently attached.");
485
+ ui.log.warn("No preview is currently attached.");
465
486
  process.exit(0);
466
487
  }
467
488
 
@@ -469,7 +490,7 @@ export const previewDetach = async () => {
469
490
  const { previewKey: _, ...rest } = config;
470
491
  await writeConfig(rest);
471
492
 
472
- p.log.success(`Detached from preview ${detachedKey}. No active preview.`);
493
+ ui.log.success(`Detached from preview ${detachedKey}. No active preview.`);
473
494
  };
474
495
 
475
496
  export const previewDelete = async (keyArg) => {
@@ -479,8 +500,8 @@ export const previewDelete = async (keyArg) => {
479
500
 
480
501
  if (!previewKey) {
481
502
  if (!config.previewKey) {
482
- p.log.warn("No preview attached and no key provided.");
483
- p.log.info("Run tiendu preview delete <key> or tiendu preview attach first.");
503
+ ui.log.warn("No preview attached and no key provided.");
504
+ ui.log.info("Run tiendu preview delete <key> or tiendu preview attach first.");
484
505
  process.exit(1);
485
506
  }
486
507
  previewKey = config.previewKey;
@@ -495,23 +516,23 @@ export const previewDelete = async (keyArg) => {
495
516
  );
496
517
 
497
518
  if (!fetchResult.ok) {
498
- p.log.error(`Preview ${previewKey} not found.`);
519
+ ui.log.error(`Preview ${previewKey} not found.`);
499
520
  process.exit(1);
500
521
  }
501
522
 
502
523
  const displayName = getPreviewDisplayName(fetchResult.data);
503
524
  const url = getPreviewUrl(config.apiBaseUrl, fetchResult.data);
504
525
 
505
- const confirmed = await p.confirm({
526
+ const confirmed = await ui.confirm({
506
527
  message: `Delete preview ${previewKey} "${displayName}" (${url})?`,
507
528
  });
508
529
 
509
- if (p.isCancel(confirmed) || !confirmed) {
510
- p.cancel("Cancelled.");
530
+ if (ui.isCancel(confirmed) || !confirmed) {
531
+ ui.cancel("Cancelled.");
511
532
  process.exit(0);
512
533
  }
513
534
 
514
- const spinner = p.spinner();
535
+ const spinner = ui.spinner();
515
536
  spinner.start("Deleting preview...");
516
537
 
517
538
  const result = await deletePreview(
@@ -523,7 +544,7 @@ export const previewDelete = async (keyArg) => {
523
544
 
524
545
  if (!result.ok) {
525
546
  spinner.stop("Failed to delete preview.", 1);
526
- p.log.error(result.error);
547
+ ui.log.error(result.error);
527
548
  process.exit(1);
528
549
  }
529
550
 
@@ -539,11 +560,11 @@ export const previewOpen = async () => {
539
560
  const { config, credentials } = await loadConfigOrFail();
540
561
 
541
562
  if (!config.previewKey) {
542
- p.log.warn("No preview attached. Run tiendu preview attach or tiendu preview create.");
563
+ ui.log.warn("No preview attached. Run tiendu preview attach or tiendu preview create.");
543
564
  process.exit(1);
544
565
  }
545
566
 
546
- const spinner = p.spinner();
567
+ const spinner = ui.spinner();
547
568
  spinner.start("Fetching preview URL...");
548
569
 
549
570
  const result = await fetchPreview(
@@ -555,7 +576,7 @@ export const previewOpen = async () => {
555
576
 
556
577
  if (!result.ok) {
557
578
  spinner.stop("Preview not found.", 1);
558
- p.log.error("Stored preview was not found. Run tiendu preview list.");
579
+ ui.log.error("Stored preview was not found. Run tiendu preview list.");
559
580
  process.exit(1);
560
581
  }
561
582
 
package/lib/publish.mjs CHANGED
@@ -1,11 +1,11 @@
1
- import * as p from "@clack/prompts";
2
- import { loadConfigOrFail, isBuiltTheme } from "./config.mjs";
1
+ import { loadConfigOrFail } from "./config.mjs";
3
2
  import {
4
3
  fetchPreviewDetails,
5
4
  publishPreview,
6
5
  resolvePreviewKeyInteractively,
7
6
  } from "./preview.mjs";
8
7
  import { push } from "./push.mjs";
8
+ import * as ui from "./ui.mjs";
9
9
 
10
10
  export const publish = async ({ skipBuild = false, previewKey: previewKeyArg } = {}) => {
11
11
  const { config, credentials } = await loadConfigOrFail();
@@ -26,27 +26,25 @@ export const publish = async ({ skipBuild = false, previewKey: previewKeyArg } =
26
26
  : previewKey;
27
27
  const previewUrl = fetchResult.ok ? fetchResult.data.url : null;
28
28
 
29
- const confirmed = await p.confirm({
29
+ const confirmed = await ui.confirm({
30
30
  message: previewUrl
31
31
  ? `Publish preview "${displayName}" (${previewKey}) at ${previewUrl} to the live storefront?`
32
32
  : `Publish preview "${displayName}" (${previewKey}) to the live storefront?`,
33
33
  });
34
34
 
35
- if (p.isCancel(confirmed) || !confirmed) {
36
- p.cancel("Publish cancelled.");
35
+ if (ui.isCancel(confirmed) || !confirmed) {
36
+ ui.cancel("Publish cancelled.");
37
37
  process.exit(0);
38
38
  }
39
39
 
40
- if (await isBuiltTheme()) {
41
- p.log.info(
42
- skipBuild
43
- ? "Syncing existing dist/ output to the preview before publishing..."
44
- : "Building and syncing the latest dist/ output before publishing...",
45
- );
46
- await push({ skipBuild, previewKey });
47
- }
40
+ ui.log.info(
41
+ skipBuild
42
+ ? "Syncing existing dist/ output to the preview before publishing..."
43
+ : "Building and syncing the latest dist/ output before publishing...",
44
+ );
45
+ await push({ skipBuild, previewKey });
48
46
 
49
- const spinner = p.spinner();
47
+ const spinner = ui.spinner();
50
48
  spinner.start("Publishing preview to live storefront...");
51
49
 
52
50
  const result = await publishPreview(
@@ -58,12 +56,12 @@ export const publish = async ({ skipBuild = false, previewKey: previewKeyArg } =
58
56
 
59
57
  if (!result.ok) {
60
58
  spinner.stop("Publish failed.", 1);
61
- p.log.error(result.error);
59
+ ui.log.error(result.error);
62
60
  process.exit(1);
63
61
  }
64
62
 
65
63
  spinner.stop(`Preview ${previewKey} published. Your live storefront has been updated.`);
66
64
  if (previewUrl) {
67
- p.log.message(` ${previewUrl}`);
65
+ ui.log.message(` ${previewUrl}`);
68
66
  }
69
67
  };
package/lib/pull.mjs CHANGED
@@ -1,8 +1,9 @@
1
- import * as p from "@clack/prompts";
2
- import { loadConfigOrFail, isBuiltTheme, getDistDir } from "./config.mjs";
1
+ import { mkdir, rm } from "node:fs/promises";
2
+ import { getDistDir, loadConfigOrFail } from "./config.mjs";
3
3
  import { downloadStorefrontArchive, downloadPreviewArchive } from "./api.mjs";
4
4
  import { fetchPreviewDetails } from "./preview.mjs";
5
5
  import { extractZip } from "./zip.mjs";
6
+ import * as ui from "./ui.mjs";
6
7
 
7
8
  /** @param {number} bytes */
8
9
  const formatBytes = (bytes) => {
@@ -22,7 +23,7 @@ export const pull = async ({ previewKey } = {}) => {
22
23
  )
23
24
  : null;
24
25
 
25
- const spinner = p.spinner();
26
+ const spinner = ui.spinner();
26
27
  const isPreviewPull = Boolean(previewKey);
27
28
 
28
29
  spinner.start(
@@ -46,13 +47,15 @@ export const pull = async ({ previewKey } = {}) => {
46
47
 
47
48
  if (!result.ok) {
48
49
  spinner.stop("Download failed.", 1);
49
- p.log.error(result.error);
50
+ ui.log.error(result.error);
50
51
  process.exit(1);
51
52
  }
52
53
 
53
54
  spinner.message(`Extracting archive (${formatBytes(result.data.length)})...`);
54
55
 
55
- const outputDir = (await isBuiltTheme()) ? getDistDir() : process.cwd();
56
+ const outputDir = getDistDir();
57
+ await rm(outputDir, { recursive: true, force: true });
58
+ await mkdir(outputDir, { recursive: true });
56
59
  const extractedFiles = await extractZip(result.data, outputDir);
57
60
 
58
61
  const suffix = isPreviewPull ? ` from preview ${previewKey}` : "";
@@ -61,6 +64,6 @@ export const pull = async ({ previewKey } = {}) => {
61
64
  );
62
65
 
63
66
  if (previewDetails?.ok) {
64
- p.log.message(` ${previewDetails.data.url}`);
67
+ ui.log.message(` ${previewDetails.data.url}`);
65
68
  }
66
69
  };
package/lib/push.mjs CHANGED
@@ -1,5 +1,4 @@
1
- import * as p from "@clack/prompts";
2
- import { loadConfigOrFail, isBuiltTheme, getDistDir } from "./config.mjs";
1
+ import { getDistDir, loadConfigOrFail } from "./config.mjs";
3
2
  import { uploadPreviewZip } from "./api.mjs";
4
3
  import { createZipFromDirectory } from "./archive.mjs";
5
4
  import { build } from "./build.mjs";
@@ -8,6 +7,7 @@ import {
8
7
  resolvePreviewKeyInteractively,
9
8
  } from "./preview.mjs";
10
9
  import { retryAsync } from "./retry.mjs";
10
+ import * as ui from "./ui.mjs";
11
11
 
12
12
  /** @param {number} bytes */
13
13
  const formatBytes = (bytes) => {
@@ -52,9 +52,7 @@ export const pushPreparedDirectoryToPreview = async ({
52
52
  export const push = async ({ skipBuild = false, previewKey: previewKeyArg } = {}) => {
53
53
  const { config, credentials } = await loadConfigOrFail();
54
54
 
55
- const builtTheme = await isBuiltTheme();
56
-
57
- if (builtTheme && !skipBuild) {
55
+ if (!skipBuild) {
58
56
  const result = await build();
59
57
  if (!result.ok) {
60
58
  process.exit(1);
@@ -71,12 +69,12 @@ export const push = async ({ skipBuild = false, previewKey: previewKeyArg } = {}
71
69
  );
72
70
 
73
71
  if (!previewDetails.ok) {
74
- p.log.error(`Preview ${previewKey} not found.`);
72
+ ui.log.error(`Preview ${previewKey} not found.`);
75
73
  process.exit(1);
76
74
  }
77
75
 
78
- const rootDir = builtTheme ? getDistDir() : process.cwd();
79
- const spinner = p.spinner();
76
+ const rootDir = getDistDir();
77
+ const spinner = ui.spinner();
80
78
  spinner.start("Compressing files...");
81
79
 
82
80
  const result = await pushPreparedDirectoryToPreview({
@@ -90,10 +88,10 @@ export const push = async ({ skipBuild = false, previewKey: previewKeyArg } = {}
90
88
 
91
89
  if (!result.ok) {
92
90
  spinner.stop("Upload failed.", 1);
93
- p.log.error(result.error);
91
+ ui.log.error(result.error);
94
92
  process.exit(1);
95
93
  }
96
94
 
97
95
  spinner.stop(`Files uploaded to preview ${previewKey}.`);
98
- p.log.message(` ${previewDetails.data.url}`);
96
+ ui.log.message(` ${previewDetails.data.url}`);
99
97
  };
package/lib/stores.mjs ADDED
@@ -0,0 +1,91 @@
1
+ import { fetchUserStores } from "./api.mjs";
2
+ import { loadConfigOrFail, writeConfig } from "./config.mjs";
3
+ import * as ui from "./ui.mjs";
4
+
5
+ const formatStoreLabel = (store, { active = false } = {}) =>
6
+ `${store.name} (ID: ${store.id})${active ? " [active]" : ""}`;
7
+
8
+ const getStoresOrFail = async () => {
9
+ const { config, credentials } = await loadConfigOrFail({ requireStore: false });
10
+ const result = await fetchUserStores(config.apiBaseUrl, credentials.apiKey);
11
+ if (!result.ok) {
12
+ ui.log.error(result.error);
13
+ process.exit(1);
14
+ }
15
+
16
+ return { config, credentials, stores: result.data };
17
+ };
18
+
19
+ export const storesList = async () => {
20
+ const spinner = ui.spinner();
21
+ spinner.start("Fetching stores...");
22
+
23
+ const { config, stores } = await getStoresOrFail();
24
+ spinner.stop(`Found ${stores.length} store${stores.length === 1 ? "" : "s"}.`);
25
+
26
+ if (stores.length === 0) {
27
+ ui.log.warn("No stores available for this API key.");
28
+ return;
29
+ }
30
+
31
+ ui.log.message("Stores:");
32
+ for (const store of stores) {
33
+ ui.log.message(`- ${formatStoreLabel(store, { active: config.storeId === store.id })}`);
34
+ }
35
+
36
+ if (!config.storeId) {
37
+ ui.log.info("No active store selected.");
38
+ ui.log.info("Next step: tiendu stores set <store-id>");
39
+ }
40
+ };
41
+
42
+ export const storesSet = async (storeIdArg) => {
43
+ const storeId = Number(storeIdArg);
44
+ if (!Number.isInteger(storeId) || storeId <= 0) {
45
+ ui.log.error("Invalid store id. Use: tiendu stores set <store-id>");
46
+ process.exit(1);
47
+ }
48
+
49
+ const spinner = ui.spinner();
50
+ spinner.start("Validating store...");
51
+
52
+ const { config, stores } = await getStoresOrFail();
53
+ const selectedStore = stores.find((store) => store.id === storeId);
54
+
55
+ if (!selectedStore) {
56
+ spinner.stop("Store not found.", 1);
57
+ ui.log.error("Store not found for this API key. Run tiendu stores list to see available stores.");
58
+ process.exit(1);
59
+ }
60
+
61
+ const nextConfig = config.storeId === storeId
62
+ ? config
63
+ : { apiBaseUrl: config.apiBaseUrl, storeId };
64
+
65
+ await writeConfig(nextConfig);
66
+ spinner.stop(`Active store set to ${selectedStore.name} (ID: ${selectedStore.id}).`);
67
+ };
68
+
69
+ export const formatInitSummary = ({ apiBaseUrl, usedDefaultBaseUrl, stores, selectedStore }) => {
70
+ const lines = ["Status: Connected."];
71
+
72
+ if (usedDefaultBaseUrl) {
73
+ lines.push(`Base URL: ${apiBaseUrl} (default)`);
74
+ } else {
75
+ lines.push(`Base URL: ${apiBaseUrl}`);
76
+ }
77
+
78
+ if (selectedStore) {
79
+ lines.push(`Store: ${selectedStore.name} (ID: ${selectedStore.id}) [auto-selected]`);
80
+ return lines.join("\n");
81
+ }
82
+
83
+ lines.push(`Possible stores to select: ${stores.length}`);
84
+ for (const store of stores) {
85
+ lines.push(`- ${store.name} (ID: ${store.id})`);
86
+ }
87
+ lines.push("No active store selected.");
88
+ lines.push("Continue by running: tiendu stores set <store-id>");
89
+
90
+ return lines.join("\n");
91
+ };