@ascorbic/pds 0.2.1 → 0.2.2

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/cli.js CHANGED
@@ -339,6 +339,63 @@ const secretCommand = defineCommand({
339
339
  }
340
340
  });
341
341
 
342
+ //#endregion
343
+ //#region src/cli/utils/cli-helpers.ts
344
+ /**
345
+ * Shared CLI utilities for PDS commands
346
+ */
347
+ /**
348
+ * Prompt for text input, exiting on cancel
349
+ */
350
+ async function promptText(options) {
351
+ const result = await p.text(options);
352
+ if (p.isCancel(result)) {
353
+ p.cancel("Cancelled");
354
+ process.exit(0);
355
+ }
356
+ return result;
357
+ }
358
+ /**
359
+ * Prompt for confirmation, exiting on cancel
360
+ */
361
+ async function promptConfirm(options) {
362
+ const result = await p.confirm(options);
363
+ if (p.isCancel(result)) {
364
+ p.cancel("Cancelled");
365
+ process.exit(0);
366
+ }
367
+ return result;
368
+ }
369
+ /**
370
+ * Prompt for selection, exiting on cancel
371
+ */
372
+ async function promptSelect(options) {
373
+ const result = await p.select(options);
374
+ if (p.isCancel(result)) {
375
+ p.cancel("Cancelled");
376
+ process.exit(0);
377
+ }
378
+ return result;
379
+ }
380
+ /**
381
+ * Get target PDS URL based on mode
382
+ */
383
+ function getTargetUrl(isDev, pdsHostname) {
384
+ if (isDev) return `http://localhost:${process.env.PORT ? parseInt(process.env.PORT) ?? "5173" : "5173"}`;
385
+ if (!pdsHostname) throw new Error("PDS_HOSTNAME not configured in wrangler.jsonc");
386
+ return `https://${pdsHostname}`;
387
+ }
388
+ /**
389
+ * Extract domain from URL
390
+ */
391
+ function getDomain(url) {
392
+ try {
393
+ return new URL(url).hostname;
394
+ } catch {
395
+ return url;
396
+ }
397
+ }
398
+
342
399
  //#endregion
343
400
  //#region src/cli/utils/handle-resolver.ts
344
401
  /**
@@ -456,6 +513,22 @@ var DidResolver = class {
456
513
  function slugifyHandle(handle) {
457
514
  return handle.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") + "-pds";
458
515
  }
516
+ const defaultWorkerName = "my-pds";
517
+ /**
518
+ * Prompt for worker name with validation
519
+ */
520
+ async function promptWorkerName(handle, currentWorkerName) {
521
+ const placeholder = currentWorkerName && currentWorkerName !== defaultWorkerName ? currentWorkerName : slugifyHandle(handle);
522
+ return promptText({
523
+ message: "Cloudflare Worker name:",
524
+ placeholder,
525
+ initialValue: placeholder,
526
+ validate: (v) => {
527
+ if (!v) return "Worker name is required";
528
+ if (!/^[a-z0-9-]+$/.test(v)) return "Worker name can only contain lowercase letters, numbers, and hyphens";
529
+ }
530
+ });
531
+ }
459
532
  /**
460
533
  * Run wrangler types to regenerate TypeScript types
461
534
  */
@@ -486,28 +559,24 @@ const initCommand = defineCommand({
486
559
  },
487
560
  args: { production: {
488
561
  type: "boolean",
489
- description: "Deploy secrets to Cloudflare (prompts to reuse .dev.vars values)",
562
+ description: "Deploy secrets to Cloudflare?",
490
563
  default: false
491
564
  } },
492
565
  async run({ args }) {
493
566
  p.intro("🦋 PDS Setup");
494
567
  const isProduction = args.production;
495
- if (isProduction) p.log.info("Production mode: secrets will be deployed via wrangler");
496
- else p.log.info("Let's set up your new home in the Atmosphere!");
568
+ if (isProduction) p.log.info("Production mode: secrets will be deployed to Cloudflare");
569
+ p.log.info("Let's set up your new home in the Atmosphere!");
497
570
  const wranglerVars = getVars();
498
571
  const devVars = readDevVars();
499
572
  const currentVars = {
500
573
  ...devVars,
501
574
  ...wranglerVars
502
575
  };
503
- const isMigrating = await p.confirm({
504
- message: "Are you migrating an existing Bluesky account? 🦋",
576
+ const isMigrating = await promptConfirm({
577
+ message: "Are you migrating an existing Bluesky/ATProto account?",
505
578
  initialValue: false
506
579
  });
507
- if (p.isCancel(isMigrating)) {
508
- p.cancel("Setup cancelled");
509
- process.exit(0);
510
- }
511
580
  let did;
512
581
  let handle;
513
582
  let hostname;
@@ -516,28 +585,24 @@ const initCommand = defineCommand({
516
585
  const currentWorkerName = getWorkerName();
517
586
  if (isMigrating) {
518
587
  p.log.info("Time to pack your bags! 🧳");
519
- p.log.info("Your account will be inactive until you've moved your data over.");
588
+ p.log.info("Your new account will be inactive until you're ready to go live.");
520
589
  let hostedDomains = [
521
590
  ".bsky.social",
522
591
  ".bsky.network",
523
592
  ".bsky.team"
524
593
  ];
525
- const isHostedHandle = (h) => hostedDomains.some((domain) => h.endsWith(domain));
594
+ const isHostedHandle = (h) => hostedDomains.some((domain) => h?.endsWith(domain));
526
595
  let resolvedDid = null;
527
596
  let existingHandle = null;
528
597
  let attempts = 0;
529
598
  const MAX_ATTEMPTS = 3;
530
599
  while (!resolvedDid && attempts < MAX_ATTEMPTS) {
531
600
  attempts++;
532
- const currentHandle = await p.text({
601
+ const currentHandle = await promptText({
533
602
  message: "Your current Bluesky/ATProto handle:",
534
603
  placeholder: "example.bsky.social",
535
604
  validate: (v) => !v ? "Handle is required" : void 0
536
605
  });
537
- if (p.isCancel(currentHandle)) {
538
- p.cancel("Cancelled");
539
- process.exit(0);
540
- }
541
606
  existingHandle = currentHandle;
542
607
  const spinner$1 = p.spinner();
543
608
  spinner$1.start("Finding you in the Atmosphere...");
@@ -545,7 +610,7 @@ const initCommand = defineCommand({
545
610
  if (!resolvedDid) {
546
611
  spinner$1.stop("Not found");
547
612
  p.log.error(`Failed to resolve handle "${currentHandle}"`);
548
- const action = await p.select({
613
+ if (await promptSelect({
549
614
  message: "What would you like to do?",
550
615
  options: [{
551
616
  value: "retry",
@@ -554,26 +619,14 @@ const initCommand = defineCommand({
554
619
  value: "manual",
555
620
  label: "Enter DID manually"
556
621
  }]
557
- });
558
- if (p.isCancel(action)) {
559
- p.cancel("Cancelled");
560
- process.exit(0);
561
- }
562
- if (action === "manual") {
563
- const manualDid = await p.text({
564
- message: "Enter your DID:",
565
- placeholder: "did:plc:...",
566
- validate: (v) => {
567
- if (!v) return "DID is required";
568
- if (!v.startsWith("did:")) return "DID must start with did:";
569
- }
570
- });
571
- if (p.isCancel(manualDid)) {
572
- p.cancel("Cancelled");
573
- process.exit(0);
622
+ }) === "manual") resolvedDid = await promptText({
623
+ message: "Enter your DID:",
624
+ placeholder: "did:plc:...",
625
+ validate: (v) => {
626
+ if (!v) return "DID is required";
627
+ if (!v.startsWith("did:")) return "DID must start with did:";
574
628
  }
575
- resolvedDid = manualDid;
576
- }
629
+ });
577
630
  } else {
578
631
  try {
579
632
  const pdsService = (await new DidResolver().resolve(resolvedDid))?.service?.find((s) => s.type === "AtprotoPersonalDataServer" || s.id === "#atproto_pds");
@@ -587,7 +640,7 @@ const initCommand = defineCommand({
587
640
  } catch {}
588
641
  spinner$1.stop(`Found you! ${resolvedDid}`);
589
642
  if (isHostedHandle(existingHandle)) {
590
- const theirDomain = hostedDomains.find((d) => existingHandle.endsWith(d));
643
+ const theirDomain = hostedDomains.find((d) => existingHandle?.endsWith(d));
591
644
  const domainExample = theirDomain ? `*${theirDomain}` : "*.bsky.social";
592
645
  p.log.warn(`You'll need a custom domain for your new handle (not ${domainExample}). You can set this up after transferring your data.`);
593
646
  }
@@ -604,95 +657,48 @@ const initCommand = defineCommand({
604
657
  }
605
658
  }
606
659
  did = resolvedDid;
607
- const defaultHandle = existingHandle && !isHostedHandle(existingHandle) ? existingHandle : currentVars.HANDLE || "";
608
- handle = await p.text({
660
+ handle = await promptText({
609
661
  message: "New account handle (must be a domain you control):",
610
662
  placeholder: "example.com",
611
- initialValue: defaultHandle,
663
+ initialValue: existingHandle && !isHostedHandle(existingHandle) ? existingHandle : currentVars.HANDLE || "",
612
664
  validate: (v) => {
613
665
  if (!v) return "Handle is required";
614
666
  if (isHostedHandle(v)) return "You need a custom domain - hosted handles like *.bsky.social won't work";
615
667
  }
616
668
  });
617
- if (p.isCancel(handle)) {
618
- p.cancel("Cancelled");
619
- process.exit(0);
620
- }
621
- hostname = await p.text({
669
+ hostname = await promptText({
622
670
  message: "Domain where you'll deploy your PDS:",
623
671
  placeholder: handle,
624
672
  initialValue: currentVars.PDS_HOSTNAME || handle,
625
673
  validate: (v) => !v ? "Hostname is required" : void 0
626
674
  });
627
- if (p.isCancel(hostname)) {
628
- p.cancel("Cancelled");
629
- process.exit(0);
630
- }
631
- const defaultWorkerName = currentWorkerName || slugifyHandle(handle);
632
- workerName = await p.text({
633
- message: "Cloudflare Worker name:",
634
- placeholder: defaultWorkerName,
635
- initialValue: defaultWorkerName,
636
- validate: (v) => {
637
- if (!v) return "Worker name is required";
638
- if (!/^[a-z0-9-]+$/.test(v)) return "Worker name can only contain lowercase letters, numbers, and hyphens";
639
- }
640
- });
641
- if (p.isCancel(workerName)) {
642
- p.cancel("Cancelled");
643
- process.exit(0);
644
- }
675
+ workerName = await promptWorkerName(handle, currentWorkerName);
645
676
  initialActive = "false";
646
677
  } else {
647
678
  p.log.info("A fresh start in the Atmosphere! ✨");
648
- hostname = await p.text({
679
+ hostname = await promptText({
649
680
  message: "Domain where you'll deploy your PDS:",
650
681
  placeholder: "pds.example.com",
651
682
  initialValue: currentVars.PDS_HOSTNAME || "",
652
683
  validate: (v) => !v ? "Hostname is required" : void 0
653
684
  });
654
- if (p.isCancel(hostname)) {
655
- p.cancel("Cancelled");
656
- process.exit(0);
657
- }
658
- handle = await p.text({
685
+ handle = await promptText({
659
686
  message: "Account handle:",
660
687
  placeholder: hostname,
661
688
  initialValue: currentVars.HANDLE || hostname,
662
689
  validate: (v) => !v ? "Handle is required" : void 0
663
690
  });
664
- if (p.isCancel(handle)) {
665
- p.cancel("Cancelled");
666
- process.exit(0);
667
- }
668
691
  const didDefault = "did:web:" + hostname;
669
- did = await p.text({
692
+ did = await promptText({
670
693
  message: "Account DID:",
671
694
  placeholder: didDefault,
672
695
  initialValue: currentVars.DID || didDefault,
673
696
  validate: (v) => {
674
697
  if (!v) return "DID is required";
675
- if (!v.startsWith("did:")) return "DID must start with did:";
676
- }
677
- });
678
- if (p.isCancel(did)) {
679
- p.cancel("Cancelled");
680
- process.exit(0);
681
- }
682
- const defaultWorkerName = currentWorkerName || slugifyHandle(handle);
683
- workerName = await p.text({
684
- message: "Cloudflare Worker name:",
685
- placeholder: defaultWorkerName,
686
- initialValue: defaultWorkerName,
687
- validate: (v) => {
688
- if (!v) return "Worker name is required";
689
- if (!/^[a-z0-9-]+$/.test(v)) return "Worker name can only contain lowercase letters, numbers, and hyphens";
698
+ if (!v.startsWith("did:")) return "DID must start with 'did:'";
690
699
  }
691
700
  });
692
- if (p.isCancel(workerName)) {
693
- p.cancel("Cancelled");
694
- process.exit(0);
695
- }
701
+ workerName = await promptWorkerName(handle, currentWorkerName);
696
702
  initialActive = "true";
697
703
  if (handle === hostname) p.note([
698
704
  "Your handle matches your PDS hostname, so your PDS will",
@@ -719,55 +725,32 @@ const initCommand = defineCommand({
719
725
  ].join("\n"), "Identity Setup 🪪");
720
726
  }
721
727
  const spinner = p.spinner();
722
- let authToken;
723
- let signingKey;
724
- let signingKeyPublic;
725
- let jwtSecret;
726
- let passwordHash;
727
- if (isProduction) {
728
- authToken = await getOrGenerateSecret("AUTH_TOKEN", devVars, async () => {
729
- spinner.start("Generating auth token...");
730
- const token = generateAuthToken();
731
- spinner.stop("Auth token generated");
732
- return token;
733
- });
734
- signingKey = await getOrGenerateSecret("SIGNING_KEY", devVars, async () => {
735
- spinner.start("Generating signing keypair...");
736
- const { privateKey } = await generateSigningKeypair();
737
- spinner.stop("Signing keypair generated");
738
- return privateKey;
739
- });
740
- signingKeyPublic = await derivePublicKey(signingKey);
741
- jwtSecret = await getOrGenerateSecret("JWT_SECRET", devVars, async () => {
742
- spinner.start("Generating JWT secret...");
743
- const secret = generateJwtSecret();
744
- spinner.stop("JWT secret generated");
745
- return secret;
746
- });
747
- passwordHash = await getOrGenerateSecret("PASSWORD_HASH", devVars, async () => {
748
- const password = await promptPassword(handle);
749
- spinner.start("Hashing password...");
750
- const hash = await hashPassword(password);
751
- spinner.stop("Password hashed");
752
- return hash;
753
- });
754
- } else {
755
- const password = await promptPassword(handle);
756
- spinner.start("Hashing password...");
757
- passwordHash = await hashPassword(password);
758
- spinner.stop("Password hashed");
759
- spinner.start("Generating JWT secret...");
760
- jwtSecret = generateJwtSecret();
761
- spinner.stop("JWT secret generated");
728
+ const authToken = await getOrGenerateSecret("AUTH_TOKEN", devVars, async () => {
762
729
  spinner.start("Generating auth token...");
763
- authToken = generateAuthToken();
730
+ const token = generateAuthToken();
764
731
  spinner.stop("Auth token generated");
732
+ return token;
733
+ });
734
+ const signingKey = await getOrGenerateSecret("SIGNING_KEY", devVars, async () => {
765
735
  spinner.start("Generating signing keypair...");
766
- const keypair = await generateSigningKeypair();
767
- signingKey = keypair.privateKey;
768
- signingKeyPublic = keypair.publicKey;
736
+ const { privateKey } = await generateSigningKeypair();
769
737
  spinner.stop("Signing keypair generated");
770
- }
738
+ return privateKey;
739
+ });
740
+ const signingKeyPublic = await derivePublicKey(signingKey);
741
+ const jwtSecret = await getOrGenerateSecret("JWT_SECRET", devVars, async () => {
742
+ spinner.start("Generating JWT secret...");
743
+ const secret = generateJwtSecret();
744
+ spinner.stop("JWT secret generated");
745
+ return secret;
746
+ });
747
+ const passwordHash = await getOrGenerateSecret("PASSWORD_HASH", devVars, async () => {
748
+ const password = await promptPassword(handle);
749
+ spinner.start("Hashing password...");
750
+ const hash = await hashPassword(password);
751
+ spinner.stop("Password hashed");
752
+ return hash;
753
+ });
771
754
  spinner.start("Updating wrangler.jsonc...");
772
755
  setWorkerName(workerName);
773
756
  setVars({
@@ -844,15 +827,10 @@ const initCommand = defineCommand({
844
827
  */
845
828
  async function getOrGenerateSecret(name, devVars, generate) {
846
829
  if (devVars[name]) {
847
- const useExisting = await p.confirm({
830
+ if (await p.confirm({
848
831
  message: `Use ${name} from .dev.vars?`,
849
832
  initialValue: true
850
- });
851
- if (p.isCancel(useExisting)) {
852
- p.cancel("Cancelled");
853
- process.exit(0);
854
- }
855
- if (useExisting) return devVars[name];
833
+ }) === true) return devVars[name];
856
834
  }
857
835
  return generate();
858
836
  }
@@ -1063,31 +1041,6 @@ var PDSClient = class {
1063
1041
  }
1064
1042
  };
1065
1043
 
1066
- //#endregion
1067
- //#region src/cli/utils/cli-helpers.ts
1068
- /**
1069
- * Shared CLI utilities for PDS commands
1070
- */
1071
- /**
1072
- * Get target PDS URL based on mode
1073
- */
1074
- function getTargetUrl(isDev, pdsHostname) {
1075
- const LOCAL_PDS_URL = "http://localhost:5173";
1076
- if (isDev) return LOCAL_PDS_URL;
1077
- if (!pdsHostname) throw new Error("PDS_HOSTNAME not configured in wrangler.jsonc");
1078
- return `https://${pdsHostname}`;
1079
- }
1080
- /**
1081
- * Extract domain from URL
1082
- */
1083
- function getDomain(url) {
1084
- try {
1085
- return new URL(url).hostname;
1086
- } catch {
1087
- return url;
1088
- }
1089
- }
1090
-
1091
1044
  //#endregion
1092
1045
  //#region src/cli/commands/migrate.ts
1093
1046
  function detectPackageManager() {
@@ -1098,11 +1051,10 @@ function detectPackageManager() {
1098
1051
  return "npm";
1099
1052
  }
1100
1053
  const brightNote$1 = (lines) => lines.map((l) => `\x1b[0m${l}`).join("\n");
1101
- const bold$1 = (text) => pc.bold(text);
1102
1054
  /**
1103
1055
  * Format number with commas
1104
1056
  */
1105
- function formatNumber(n) {
1057
+ function num(n) {
1106
1058
  return n.toLocaleString();
1107
1059
  }
1108
1060
  /**
@@ -1131,7 +1083,8 @@ const migrateCommand = defineCommand({
1131
1083
  }
1132
1084
  },
1133
1085
  async run({ args }) {
1134
- const pm = detectPackageManager();
1086
+ const packageManager = detectPackageManager();
1087
+ const pm = packageManager === "npm" ? "npm run" : packageManager;
1135
1088
  const isDev = args.dev;
1136
1089
  const vars = getVars();
1137
1090
  let targetUrl;
@@ -1198,6 +1151,7 @@ const migrateCommand = defineCommand({
1198
1151
  const sourceDomain = getDomain(sourcePdsUrl);
1199
1152
  spinner.stop(`Found your account at ${sourceDomain}`);
1200
1153
  spinner.start("Checking account status...");
1154
+ const pdsDisplayName = sourceDomain.endsWith(".bsky.network") ? "bsky.social" : sourceDomain;
1201
1155
  let status;
1202
1156
  try {
1203
1157
  status = await targetClient.getAccountStatus();
@@ -1212,7 +1166,7 @@ const migrateCommand = defineCommand({
1212
1166
  if (status.active) {
1213
1167
  p.log.error("Cannot reset: account is active");
1214
1168
  p.log.info("The --clean flag only works on deactivated accounts.");
1215
- p.log.info("Your account is already live in the Atmosphere.");
1169
+ p.log.info("Your account is already live");
1216
1170
  p.log.info("");
1217
1171
  p.log.info("If you need to re-import, first deactivate:");
1218
1172
  p.log.info(" pnpm pds deactivate");
@@ -1220,13 +1174,13 @@ const migrateCommand = defineCommand({
1220
1174
  process.exit(1);
1221
1175
  }
1222
1176
  p.note(brightNote$1([
1223
- bold$1("This will permanently delete from your new PDS:"),
1177
+ pc.bold("This will permanently delete from your new PDS:"),
1224
1178
  "",
1225
- ` • ${formatNumber(status.repoBlocks)} repository blocks`,
1226
- ` • ${formatNumber(status.importedBlobs)} imported images`,
1179
+ ` • ${num(status.repoBlocks)} repository blocks`,
1180
+ ` • ${num(status.importedBlobs)} imported images`,
1227
1181
  " • All blob tracking data",
1228
1182
  "",
1229
- bold$1(`Your data on ${sourceDomain} is NOT affected.`),
1183
+ pc.bold(`Your data on ${pdsDisplayName} is NOT affected.`),
1230
1184
  "You'll need to re-import everything."
1231
1185
  ]), "⚠️ Reset Migration Data");
1232
1186
  const confirmReset = await p.confirm({
@@ -1240,7 +1194,7 @@ const migrateCommand = defineCommand({
1240
1194
  spinner.start("Resetting migration state...");
1241
1195
  try {
1242
1196
  const result = await targetClient.resetMigration();
1243
- spinner.stop(`Deleted ${formatNumber(result.blocksDeleted)} blocks, ${formatNumber(result.blobsCleared)} blobs`);
1197
+ spinner.stop(`Deleted ${num(result.blocksDeleted)} blocks, ${num(result.blobsCleared)} blobs`);
1244
1198
  } catch (err) {
1245
1199
  spinner.stop("Reset failed");
1246
1200
  p.log.error(err instanceof Error ? err.message : "Could not reset migration");
@@ -1251,12 +1205,12 @@ const migrateCommand = defineCommand({
1251
1205
  status = await targetClient.getAccountStatus();
1252
1206
  }
1253
1207
  if (status.active) {
1254
- p.log.warn("Your account is already active in the Atmosphere!");
1208
+ p.log.warn(`Your account is already active at ${targetDomain}!`);
1255
1209
  p.log.info("No migration needed - your PDS is live.");
1256
- p.outro("All good! 🦋");
1210
+ p.outro("All good!");
1257
1211
  return;
1258
1212
  }
1259
- spinner.start(`Fetching your account details from ${sourceDomain}...`);
1213
+ spinner.start(`Fetching your account details from ${pdsDisplayName}...`);
1260
1214
  const sourceClient = new PDSClient(sourcePdsUrl);
1261
1215
  try {
1262
1216
  await sourceClient.describeRepo(did);
@@ -1277,10 +1231,10 @@ const migrateCommand = defineCommand({
1277
1231
  `@${handle} (${did.slice(0, 20)}...)`,
1278
1232
  "",
1279
1233
  "✓ Repository imported",
1280
- `◐ Images: ${formatNumber(status.importedBlobs)}/${formatNumber(status.expectedBlobs)} transferred`
1234
+ `◐ Media: ${num(status.importedBlobs)}/${num(status.expectedBlobs)} images and videos transferred`
1281
1235
  ].join("\n"), "Migration Progress");
1282
1236
  const continueTransfer = await p.confirm({
1283
- message: "Continue transferring images?",
1237
+ message: "Continue transferring images and video?",
1284
1238
  initialValue: true
1285
1239
  });
1286
1240
  if (p.isCancel(continueTransfer) || !continueTransfer) {
@@ -1289,14 +1243,14 @@ const migrateCommand = defineCommand({
1289
1243
  }
1290
1244
  } else if (needsRepoImport) {
1291
1245
  p.log.info("Time to pack your bags!");
1292
- p.log.info("Let's move your Bluesky account to its new home in the Atmosphere.");
1246
+ p.log.info("Let's clone your account to its new home in the Atmosphere.");
1293
1247
  const statsLines = profileStats ? [
1294
- ` 📝 ${formatNumber(profileStats.postsCount)} posts`,
1295
- ` 👥 ${formatNumber(profileStats.followsCount)} follows`,
1296
- ` ...plus all your images, likes, and blocks`
1297
- ] : [` 📝 Posts, follows, images, likes, and blocks`];
1248
+ ` 📝 ${num(profileStats.postsCount)} posts`,
1249
+ ` 👥 ${num(profileStats.followsCount)} follows`,
1250
+ ` ...plus all your images, likes and preferences`
1251
+ ] : [` 📝 Posts, follows, images, likes and preferences`];
1298
1252
  p.note(brightNote$1([
1299
- bold$1(`@${handle}`) + ` (${did.slice(0, 20)}...)`,
1253
+ pc.bold(`@${handle}`) + ` (${did.slice(0, 20)}...)`,
1300
1254
  "",
1301
1255
  `Currently at: ${sourceDomain}`,
1302
1256
  `Moving to: ${targetDomain}`,
@@ -1304,9 +1258,9 @@ const migrateCommand = defineCommand({
1304
1258
  "What you're bringing:",
1305
1259
  ...statsLines
1306
1260
  ]), "Your Bluesky Account 🦋");
1307
- p.log.info("This will copy your data - nothing is changed or deleted on Bluesky.");
1261
+ p.log.info(`This will copy your data - nothing is changed or deleted on your current PDS.`);
1308
1262
  const proceed = await p.confirm({
1309
- message: "Ready to start packing?",
1263
+ message: "Ready to start moving?",
1310
1264
  initialValue: true
1311
1265
  });
1312
1266
  if (p.isCancel(proceed) || !proceed) {
@@ -1314,19 +1268,17 @@ const migrateCommand = defineCommand({
1314
1268
  process.exit(0);
1315
1269
  }
1316
1270
  } else {
1317
- p.log.success("All packed and moved! 🦋");
1318
- showNextSteps(pm, sourceDomain);
1271
+ p.log.success("Everything looks good!");
1272
+ showNextSteps(pm, pdsDisplayName);
1319
1273
  p.outro("Welcome to your new home in the Atmosphere! 🦋");
1320
1274
  return;
1321
1275
  }
1322
- const isBlueskyPds = sourceDomain.endsWith(".bsky.network");
1323
- const passwordPrompt = isBlueskyPds ? "Your current Bluesky password:" : `Your ${sourceDomain} password:`;
1324
- const password = await p.password({ message: passwordPrompt });
1276
+ const password = await p.password({ message: `Your password for ${pdsDisplayName}:` });
1325
1277
  if (p.isCancel(password)) {
1326
1278
  p.cancel("Migration cancelled.");
1327
1279
  process.exit(0);
1328
1280
  }
1329
- spinner.start(`Logging in to ${isBlueskyPds ? "Bluesky" : sourceDomain}...`);
1281
+ spinner.start(`Logging in to ${pdsDisplayName}...`);
1330
1282
  try {
1331
1283
  const session = await sourceClient.createSession(did, password);
1332
1284
  sourceClient.setAuthToken(session.accessJwt);
@@ -1339,7 +1291,7 @@ const migrateCommand = defineCommand({
1339
1291
  process.exit(1);
1340
1292
  }
1341
1293
  if (needsRepoImport) {
1342
- spinner.start("Packing your repository...");
1294
+ spinner.start(`Exporting your repository from ${pdsDisplayName}...`);
1343
1295
  let carBytes;
1344
1296
  try {
1345
1297
  carBytes = await sourceClient.getRepo(did);
@@ -1350,7 +1302,7 @@ const migrateCommand = defineCommand({
1350
1302
  p.outro("Migration cancelled.");
1351
1303
  process.exit(1);
1352
1304
  }
1353
- spinner.start(`Unpacking at ${targetDomain}...`);
1305
+ spinner.start(`Importing to ${targetDomain}...`);
1354
1306
  try {
1355
1307
  await targetClient.importRepo(carBytes);
1356
1308
  spinner.stop("Repository imported");
@@ -1391,7 +1343,7 @@ const migrateCommand = defineCommand({
1391
1343
  totalBlobs += page.blobs.length;
1392
1344
  countCursor = page.cursor;
1393
1345
  } while (countCursor);
1394
- spinner.message(`Transferring images ${progressBar(0, totalBlobs)}`);
1346
+ spinner.message(`Transferring media ${progressBar(0, totalBlobs)}`);
1395
1347
  do {
1396
1348
  const page = await targetClient.listMissingBlobs(100, cursor);
1397
1349
  cursor = page.cursor;
@@ -1399,40 +1351,40 @@ const migrateCommand = defineCommand({
1399
1351
  const { bytes, mimeType } = await sourceClient.getBlob(did, blob.cid);
1400
1352
  await targetClient.uploadBlob(bytes, mimeType);
1401
1353
  synced++;
1402
- spinner.message(`Transferring images ${progressBar(synced, totalBlobs)}`);
1354
+ spinner.message(`Transferring media ${progressBar(synced, totalBlobs)}`);
1403
1355
  } catch (err) {
1404
1356
  synced++;
1405
1357
  failedBlobs.push(blob.cid);
1406
- spinner.message(`Transferring images ${progressBar(synced, totalBlobs)}`);
1358
+ spinner.message(`Transferring media ${progressBar(synced, totalBlobs)}`);
1407
1359
  }
1408
1360
  } while (cursor);
1409
1361
  if (failedBlobs.length > 0) {
1410
- spinner.stop(`Transferred ${formatNumber(synced - failedBlobs.length)} images (${failedBlobs.length} failed)`);
1362
+ spinner.stop(`Transferred ${num(synced - failedBlobs.length)} images and videos (${failedBlobs.length} failed)`);
1411
1363
  p.log.warn(`Run 'pds migrate' again to retry failed transfers.`);
1412
- } else spinner.stop(`Transferred ${formatNumber(synced)} images`);
1364
+ } else spinner.stop(`Transferred ${num(synced)} images and videos`);
1413
1365
  }
1414
1366
  spinner.start("Verifying migration...");
1415
1367
  const finalStatus = await targetClient.getAccountStatus();
1416
1368
  spinner.stop("Verification complete");
1417
- if (finalStatus.importedBlobs >= finalStatus.expectedBlobs) p.log.success("All packed and moved! 🦋");
1369
+ if (finalStatus.importedBlobs >= finalStatus.expectedBlobs) p.log.success("All packed and moved!");
1418
1370
  else {
1419
1371
  p.log.warn(`Migration partially complete. ${finalStatus.expectedBlobs - finalStatus.importedBlobs} images remaining.`);
1420
1372
  p.log.info("Run 'pds migrate' again to continue.");
1421
1373
  }
1422
- showNextSteps(pm, sourceDomain);
1374
+ showNextSteps(pm, pdsDisplayName);
1423
1375
  p.outro("Welcome to your new home in the Atmosphere! 🦋");
1424
1376
  }
1425
1377
  });
1426
1378
  function showNextSteps(pm, sourceDomain) {
1427
1379
  p.note(brightNote$1([
1428
- bold$1("Your data is safe in your new PDS."),
1429
- "Two more steps to go live in the Atmosphere:",
1380
+ pc.bold("Your data is safe in your new PDS."),
1381
+ "Two more steps to go live:",
1430
1382
  "",
1431
- bold$1("1. Update your identity"),
1383
+ pc.bold("1. Update your identity"),
1432
1384
  " Tell the network where you live now.",
1433
1385
  ` (Requires email verification from ${sourceDomain})`,
1434
1386
  "",
1435
- bold$1("2. Flip the switch"),
1387
+ pc.bold("2. Flip the switch"),
1436
1388
  ` ${pm} pds activate`,
1437
1389
  "",
1438
1390
  "Docs: https://atproto.com/guides/account-migration"