@wraps.dev/cli 3.6.0 → 3.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/dist/cli.js CHANGED
@@ -30520,20 +30520,21 @@ async function checkVercelDomain(token, domain, teamId) {
30520
30520
  }
30521
30521
  }
30522
30522
  async function validateCloudflareCredentials(token) {
30523
- try {
30524
- const response = await fetch(
30525
- "https://api.cloudflare.com/client/v4/user/tokens/verify",
30526
- {
30527
- headers: {
30528
- Authorization: `Bearer ${token}`
30529
- }
30530
- }
30531
- );
30532
- const data = await response.json();
30533
- return data.success === true;
30534
- } catch {
30535
- return false;
30523
+ const probe = async (url) => {
30524
+ try {
30525
+ const response = await fetch(url, {
30526
+ headers: { Authorization: `Bearer ${token}` }
30527
+ });
30528
+ const data = await response.json();
30529
+ return data.success === true;
30530
+ } catch {
30531
+ return false;
30532
+ }
30533
+ };
30534
+ if (await probe("https://api.cloudflare.com/client/v4/user/tokens/verify")) {
30535
+ return true;
30536
30536
  }
30537
+ return probe("https://api.cloudflare.com/client/v4/zones?per_page=1");
30537
30538
  }
30538
30539
  async function findCloudflareZoneId(token, domain) {
30539
30540
  try {
@@ -44911,10 +44912,14 @@ async function putTrackingDomain(sesv22, configSetName, trackingDomain, httpsPol
44911
44912
  new PutConfigurationSetTrackingOptionsCommand({
44912
44913
  ConfigurationSetName: configSetName,
44913
44914
  CustomRedirectDomain: trackingDomain,
44914
- ...httpsPolicy ? { HttpsPolicy: httpsPolicy } : {}
44915
+ HttpsPolicy: httpsPolicy
44915
44916
  })
44916
44917
  );
44917
44918
  }
44919
+ function trackingHttpsPolicy(trackingHttps) {
44920
+ return trackingHttps?.status === "active" ? "REQUIRE" : "OPTIONAL";
44921
+ }
44922
+ var HTTP_TRACKING_CONSEQUENCE = "Until then, https:// links in your emails will show recipients a certificate warning when clicked.";
44918
44923
  async function clearTrackingDomain(region, configSetName) {
44919
44924
  const ses = new SESClient4({ region });
44920
44925
  await ses.send(
@@ -45206,19 +45211,18 @@ async function provisionTrackingHttps(args) {
45206
45211
  );
45207
45212
  if (cert.status !== "ISSUED") {
45208
45213
  const dnsRecordsToShow = [];
45214
+ let validationRecordPushed = false;
45209
45215
  if (cert.validationRecord) {
45210
45216
  dnsRecordsToShow.push(cert.validationRecord);
45211
45217
  if (metadataDnsProvider && metadataDnsProvider !== "manual") {
45212
- await progress.execute(
45218
+ validationRecordPushed = await progress.execute(
45213
45219
  "Creating ACM validation DNS record",
45214
- async () => {
45215
- await pushValidationRecord(
45216
- metadataDnsProvider,
45217
- domain,
45218
- sesRegion,
45219
- cert.validationRecord
45220
- );
45221
- }
45220
+ async () => pushValidationRecord(
45221
+ metadataDnsProvider,
45222
+ domain,
45223
+ sesRegion,
45224
+ cert.validationRecord
45225
+ )
45222
45226
  );
45223
45227
  }
45224
45228
  }
@@ -45229,7 +45233,8 @@ async function provisionTrackingHttps(args) {
45229
45233
  validationRecord: cert.validationRecord
45230
45234
  },
45231
45235
  cnameTarget: awstrackTarget,
45232
- dnsRecordsToShow
45236
+ dnsRecordsToShow,
45237
+ validationRecordPushed
45233
45238
  };
45234
45239
  }
45235
45240
  const distribution = await progress.execute(
@@ -45252,7 +45257,8 @@ async function provisionTrackingHttps(args) {
45252
45257
  distributionDomain: distribution.domainName
45253
45258
  },
45254
45259
  cnameTarget: distribution.domainName,
45255
- dnsRecordsToShow: []
45260
+ dnsRecordsToShow: [],
45261
+ validationRecordPushed: true
45256
45262
  };
45257
45263
  }
45258
45264
 
@@ -45900,7 +45906,12 @@ async function applyTrackingDomain(ctx, value) {
45900
45906
  const host = value.toLowerCase();
45901
45907
  let appliedAt;
45902
45908
  try {
45903
- await putTrackingDomain(ctx.sesClient, ctx.candidate.configSetName, host);
45909
+ await putTrackingDomain(
45910
+ ctx.sesClient,
45911
+ ctx.candidate.configSetName,
45912
+ host,
45913
+ trackingHttpsPolicy(ctx.candidate.trackingHttps)
45914
+ );
45904
45915
  appliedAt = (/* @__PURE__ */ new Date()).toISOString();
45905
45916
  } catch (error) {
45906
45917
  if (!isTrackingDomainNotReady(error)) {
@@ -45924,6 +45935,7 @@ async function offerTrackingCname(ctx, trackingDomain, cnameTarget, replaceExist
45924
45935
  value: targetValue
45925
45936
  };
45926
45937
  const provider = ctx.metadata.services.email?.dnsProvider;
45938
+ let degradedReason;
45927
45939
  if (provider && provider !== "manual") {
45928
45940
  const parts = ctx.candidate.domain.split(".");
45929
45941
  const rootDomain = parts.length > 2 ? parts.slice(-2).join(".") : ctx.candidate.domain;
@@ -45947,8 +45959,14 @@ async function offerTrackingCname(ctx, trackingDomain, cnameTarget, replaceExist
45947
45959
  );
45948
45960
  return;
45949
45961
  }
45962
+ degradedReason = result.errors?.join("; ") ?? `${getDNSProviderDisplayName(provider)} created no record`;
45963
+ } else {
45964
+ degradedReason = cred.error ?? `${getDNSProviderDisplayName(provider)} credentials`;
45950
45965
  }
45951
45966
  }
45967
+ if (degradedReason) {
45968
+ clack24.log.warn(`Could not update DNS automatically: ${degradedReason}`);
45969
+ }
45952
45970
  clack24.log.info(
45953
45971
  `Add this DNS record:
45954
45972
  ${pc26.cyan(record.name)}
@@ -46010,7 +46028,30 @@ async function applyTrackingHttps(ctx, enable) {
46010
46028
  if (result.trackingHttps.status === "active" && !wasActive) {
46011
46029
  await offerTrackingCname(ctx, trackingDomain, result.cnameTarget, true);
46012
46030
  }
46013
- return { pendingValidationRecord: result.dnsRecordsToShow[0] };
46031
+ return {
46032
+ pendingValidationRecord: result.dnsRecordsToShow[0],
46033
+ validationRecordPushed: result.validationRecordPushed
46034
+ };
46035
+ }
46036
+ function reportTrackingHttpsEnabled(candidate, httpsResult, reopenHint) {
46037
+ if (candidate.trackingHttps?.status === "active") {
46038
+ clack24.log.success(
46039
+ `HTTPS active (${candidate.trackingHttps.distributionDomain})`
46040
+ );
46041
+ return;
46042
+ }
46043
+ clack24.log.info(
46044
+ pc26.dim(
46045
+ `Certificate validation usually takes 5\u201330 minutes. ${reopenHint} ${HTTP_TRACKING_CONSEQUENCE}`
46046
+ )
46047
+ );
46048
+ if (httpsResult.pendingValidationRecord) {
46049
+ clack24.log.info(
46050
+ `${httpsResult.validationRecordPushed ? "Certificate validation record (created for you):" : "Add this DNS record to validate the certificate:"}
46051
+ ${pc26.cyan(httpsResult.pendingValidationRecord.name)}
46052
+ Type: ${httpsResult.pendingValidationRecord.type} Value: ${httpsResult.pendingValidationRecord.value}`
46053
+ );
46054
+ }
46014
46055
  }
46015
46056
  async function applyFlagMode(args) {
46016
46057
  const {
@@ -46142,6 +46183,25 @@ async function applyFlagMode(args) {
46142
46183
  } else {
46143
46184
  clack24.log.success(`Tracking domain set to ${trackingDomainFlag}`);
46144
46185
  await offerTrackingCname(ctx, trackingDomainFlag.toLowerCase());
46186
+ if (trackingHttpsFlag === void 0) {
46187
+ try {
46188
+ const httpsResult = await applyTrackingHttps(ctx, true);
46189
+ reportTrackingHttpsEnabled(
46190
+ candidate,
46191
+ httpsResult,
46192
+ `Re-run ${pc26.cyan(`wraps email domains config --domain ${candidate.domain} --tracking-https`)} once it's ISSUED.`
46193
+ );
46194
+ } catch (error) {
46195
+ clack24.log.warn(
46196
+ `Could not enable HTTPS for tracking links: ${describeTrackingHttpsError(error)}`
46197
+ );
46198
+ clack24.log.info(
46199
+ pc26.dim(
46200
+ `Retry later with: ${pc26.cyan(`wraps email domains config --domain ${candidate.domain} --tracking-https`)}. ${HTTP_TRACKING_CONSEQUENCE}`
46201
+ )
46202
+ );
46203
+ }
46204
+ }
46145
46205
  }
46146
46206
  trackCommand("email:domains:config", {
46147
46207
  success: true,
@@ -46183,7 +46243,7 @@ async function applyFlagMode(args) {
46183
46243
  );
46184
46244
  if (httpsResult.pendingValidationRecord) {
46185
46245
  clack24.log.info(
46186
- `Add this DNS record to validate the certificate:
46246
+ `${httpsResult.validationRecordPushed ? "Certificate validation record (created for you):" : "Add this DNS record to validate the certificate:"}
46187
46247
  ${pc26.cyan(httpsResult.pendingValidationRecord.name)}
46188
46248
  Type: ${httpsResult.pendingValidationRecord.type} Value: ${httpsResult.pendingValidationRecord.value}`
46189
46249
  );
@@ -46381,24 +46441,11 @@ async function applyInteractiveMode(args) {
46381
46441
  }
46382
46442
  httpsProgress.stop();
46383
46443
  if (enabling) {
46384
- if (candidate.trackingHttps?.status === "active") {
46385
- clack24.log.success(
46386
- `HTTPS active (${candidate.trackingHttps.distributionDomain})`
46387
- );
46388
- } else {
46389
- clack24.log.info(
46390
- pc26.dim(
46391
- "Certificate validation usually takes 5\u201330 minutes. Reopen this menu once it's ISSUED."
46392
- )
46393
- );
46394
- if (httpsResult.pendingValidationRecord) {
46395
- clack24.log.info(
46396
- `Add this DNS record to validate the certificate:
46397
- ${pc26.cyan(httpsResult.pendingValidationRecord.name)}
46398
- Type: ${httpsResult.pendingValidationRecord.type} Value: ${httpsResult.pendingValidationRecord.value}`
46399
- );
46400
- }
46401
- }
46444
+ reportTrackingHttpsEnabled(
46445
+ candidate,
46446
+ httpsResult,
46447
+ "Reopen this menu once it's ISSUED."
46448
+ );
46402
46449
  } else {
46403
46450
  clack24.log.success("HTTPS disabled for tracking links");
46404
46451
  }
@@ -46429,6 +46476,34 @@ async function applyInteractiveMode(args) {
46429
46476
  `Tracking domain set to ${value.toLowerCase()}`
46430
46477
  );
46431
46478
  await offerTrackingCname(ctx, value.toLowerCase());
46479
+ if (!candidate.trackingHttps) {
46480
+ const wantsHttps = await clack24.confirm({
46481
+ message: `Enable HTTPS for tracking links? ${pc26.dim("(creates a CloudFront distribution + certificate in your account)")}`,
46482
+ initialValue: true
46483
+ });
46484
+ if (!clack24.isCancel(wantsHttps) && wantsHttps) {
46485
+ const setupHttpsProgress = new DeploymentProgress();
46486
+ try {
46487
+ const httpsResult = await setupHttpsProgress.execute(
46488
+ "Enabling HTTPS",
46489
+ async () => applyTrackingHttps(ctx, true)
46490
+ );
46491
+ setupHttpsProgress.stop();
46492
+ reportTrackingHttpsEnabled(
46493
+ candidate,
46494
+ httpsResult,
46495
+ "Reopen this menu once it's ISSUED."
46496
+ );
46497
+ } catch (error) {
46498
+ setupHttpsProgress.stop();
46499
+ if (error instanceof WrapsError) {
46500
+ clack24.log.error(error.message);
46501
+ } else {
46502
+ throw error;
46503
+ }
46504
+ }
46505
+ }
46506
+ }
46432
46507
  break;
46433
46508
  }
46434
46509
  case "delivery": {
@@ -46862,7 +46937,8 @@ async function verifyDomain(options) {
46862
46937
  await putTrackingDomain(
46863
46938
  sesClient,
46864
46939
  targetConfigSetName,
46865
- trackedEntry.trackingDomain
46940
+ trackedEntry.trackingDomain,
46941
+ trackingHttpsPolicy(trackedEntry.trackingHttps)
46866
46942
  );
46867
46943
  trackedEntry.trackingDomainAppliedAt = (/* @__PURE__ */ new Date()).toISOString();
46868
46944
  addDomainToMetadata(metadata, trackedEntry);
@@ -47251,7 +47327,12 @@ Run ${pc27.cyan("wraps email init")} first to deploy email infrastructure.
47251
47327
  if (trackingDomain) {
47252
47328
  await progress.execute("Setting custom tracking domain", async () => {
47253
47329
  try {
47254
- await putTrackingDomain(sesClient, configSetName, trackingDomain);
47330
+ await putTrackingDomain(
47331
+ sesClient,
47332
+ configSetName,
47333
+ trackingDomain,
47334
+ "OPTIONAL"
47335
+ );
47255
47336
  trackingDomainAppliedAt = (/* @__PURE__ */ new Date()).toISOString();
47256
47337
  } catch (error) {
47257
47338
  if (!isTrackingDomainNotReady(error)) throw error;
@@ -47305,7 +47386,7 @@ Run ${pc27.cyan("wraps email init")} first to deploy email infrastructure.
47305
47386
  );
47306
47387
  clack25.log.info(
47307
47388
  pc27.dim(
47308
- `Retry later with: ${pc27.cyan(`wraps email domains config --domain ${domain} --tracking-https`)}`
47389
+ `Retry later with: ${pc27.cyan(`wraps email domains config --domain ${domain} --tracking-https`)}. ${HTTP_TRACKING_CONSEQUENCE}`
47309
47390
  )
47310
47391
  );
47311
47392
  }
@@ -47313,7 +47394,7 @@ Run ${pc27.cyan("wraps email init")} first to deploy email infrastructure.
47313
47394
  if (trackingHttpsResult?.trackingHttps.status === "pending" && !isJsonMode()) {
47314
47395
  clack25.log.info(
47315
47396
  pc27.dim(
47316
- `Certificate validation usually takes 5\u201330 minutes. Then run: ${pc27.cyan(`wraps email domains config --domain ${domain} --tracking-https`)}`
47397
+ `Certificate validation usually takes 5\u201330 minutes. Then run: ${pc27.cyan(`wraps email domains config --domain ${domain} --tracking-https`)}. ${HTTP_TRACKING_CONSEQUENCE}`
47317
47398
  )
47318
47399
  );
47319
47400
  }
@@ -47396,7 +47477,9 @@ Run ${pc27.cyan("wraps email init")} first to deploy email infrastructure.
47396
47477
  progress.stop();
47397
47478
  console.log();
47398
47479
  clack25.log.info(
47399
- pc27.bold("Add this DNS record to validate the certificate:")
47480
+ pc27.bold(
47481
+ trackingHttpsResult.validationRecordPushed ? "Certificate validation record (created for you):" : "Add this DNS record to validate the certificate:"
47482
+ )
47400
47483
  );
47401
47484
  console.log();
47402
47485
  for (const record of trackingHttpsResult.dnsRecordsToShow) {
@@ -47498,6 +47581,7 @@ async function listDomains() {
47498
47581
  (identity) => identity.IdentityType === "DOMAIN" || identity.IdentityName && !identity.IdentityName.includes("@")
47499
47582
  );
47500
47583
  let trackedDomains = [];
47584
+ let trackingHttpsByDomain = /* @__PURE__ */ new Map();
47501
47585
  try {
47502
47586
  const awsIdentity = await validateAWSCredentials();
47503
47587
  const metadata = await loadConnectionMetadata(
@@ -47506,6 +47590,15 @@ async function listDomains() {
47506
47590
  );
47507
47591
  if (metadata) {
47508
47592
  trackedDomains = getAllTrackedDomains(metadata);
47593
+ const emailConfig = metadata.services.email?.config;
47594
+ trackingHttpsByDomain = new Map(
47595
+ (emailConfig?.additionalDomains ?? []).map(
47596
+ (d6) => [d6.domain, d6.trackingHttps]
47597
+ )
47598
+ );
47599
+ if (emailConfig?.domain && emailConfig.tracking?.httpsEnabled) {
47600
+ trackingHttpsByDomain.set(emailConfig.domain, { status: "active" });
47601
+ }
47509
47602
  }
47510
47603
  } catch {
47511
47604
  }
@@ -47560,7 +47653,8 @@ Run ${pc27.cyan("wraps email domains add")} to add a domain.
47560
47653
  managed: !!tracked,
47561
47654
  isPrimary: tracked?.isPrimary ?? false,
47562
47655
  purpose: tracked?.purpose,
47563
- trackingDomain: tracked?.trackingDomain ?? null
47656
+ trackingDomain: tracked?.trackingDomain ?? null,
47657
+ trackingHttps: trackingHttpsByDomain.get(d6.name)?.status ?? null
47564
47658
  };
47565
47659
  }),
47566
47660
  totalCount: sesDomains.length,
@@ -47574,7 +47668,7 @@ Run ${pc27.cyan("wraps email domains add")} to add a domain.
47574
47668
  const statusIcon = d6.verified ? pc27.green("\u2713") : pc27.yellow("\u23F1");
47575
47669
  const dkimIcon = d6.dkimStatus === "SUCCESS" ? pc27.green("\u2713 SUCCESS") : pc27.yellow(`\u23F1 ${d6.dkimStatus}`);
47576
47670
  const label = tracked.isPrimary ? pc27.dim("Primary") : pc27.dim(PURPOSE_LABELS[tracked.purpose || "other"] || "General");
47577
- const trackingSuffix = tracked.trackingDomain ? ` ${pc27.dim("\u2192")} ${tracked.trackingDomain}` : "";
47671
+ const trackingSuffix = tracked.trackingDomain ? ` ${pc27.dim("\u2192")} ${tracked.trackingDomain} ${trackingHttpsByDomain.get(d6.name)?.status === "active" ? pc27.dim("(https)") : pc27.yellow("(http)")}` : "";
47578
47672
  return ` ${statusIcon} ${pc27.bold(d6.name.padEnd(30))} ${label.padEnd(24)} DKIM: ${dkimIcon}${trackingSuffix}`;
47579
47673
  });
47580
47674
  clack25.note(managedLines.join("\n"), "Managed by Wraps");
@@ -68331,50 +68425,90 @@ function showHelp() {
68331
68425
  );
68332
68426
  console.log("Email Commands:");
68333
68427
  console.log(
68334
- ` ${pc72.cyan("email init")} Deploy new email infrastructure`
68428
+ ` ${pc72.cyan("email init")} Deploy new email infrastructure`
68429
+ );
68430
+ console.log(
68431
+ ` ${pc72.cyan("email check")} Check email deliverability for a domain`
68335
68432
  );
68336
68433
  console.log(
68337
- ` ${pc72.cyan("email check")} Check email deliverability for a domain`
68434
+ ` ${pc72.cyan("email connect")} Connect to existing AWS SES`
68338
68435
  );
68339
68436
  console.log(
68340
- ` ${pc72.cyan("email connect")} Connect to existing AWS SES`
68437
+ ` ${pc72.cyan("email status")} Show email infrastructure details`
68341
68438
  );
68342
68439
  console.log(
68343
- ` ${pc72.cyan("email status")} Show email infrastructure details`
68440
+ ` ${pc72.cyan("email plan")} Show SES pricing plan and cheaper options`
68344
68441
  );
68442
+ console.log(` ${pc72.cyan("email test")} Send a test email`);
68345
68443
  console.log(
68346
- ` ${pc72.cyan("email plan")} Show SES pricing plan and cheaper options`
68444
+ ` ${pc72.cyan("email verify")} Verify domain DNS records`
68347
68445
  );
68348
- console.log(` ${pc72.cyan("email test")} Send a test email`);
68349
- console.log(` ${pc72.cyan("email verify")} Verify domain DNS records`);
68350
68446
  console.log(
68351
- ` ${pc72.cyan("email sync")} Apply CLI updates to infrastructure`
68447
+ ` ${pc72.cyan("email sync")} Apply CLI updates to infrastructure`
68352
68448
  );
68353
- console.log(` ${pc72.cyan("email upgrade")} Add features`);
68449
+ console.log(` ${pc72.cyan("email upgrade")} Add features`);
68354
68450
  console.log(
68355
- ` ${pc72.cyan("email restore")} Restore original configuration`
68451
+ ` ${pc72.cyan("email restore")} Restore original configuration`
68356
68452
  );
68357
68453
  console.log(
68358
- ` ${pc72.cyan("email destroy")} Remove email infrastructure`
68454
+ ` ${pc72.cyan("email destroy")} Remove email infrastructure`
68359
68455
  );
68360
68456
  console.log(
68361
- ` ${pc72.cyan("email doctor")} Diagnose and clean up email infrastructure`
68457
+ ` ${pc72.cyan("email doctor")} Diagnose and clean up email infrastructure`
68362
68458
  );
68363
- console.log(` ${pc72.cyan("email domains add")} Add a domain to SES`);
68364
- console.log(` ${pc72.cyan("email domains list")} List all domains`);
68365
- console.log(` ${pc72.cyan("email domains remove")} Remove a domain`);
68459
+ console.log(` ${pc72.cyan("email domains add")} Add a domain to SES`);
68460
+ console.log(` ${pc72.cyan("email domains list")} List all domains`);
68461
+ console.log(` ${pc72.cyan("email domains remove")} Remove a domain`);
68366
68462
  console.log(
68367
- ` ${pc72.cyan("email inbound init")} Enable inbound email receiving`
68463
+ ` ${pc72.cyan("email domains verify")} Check domain verification status`
68368
68464
  );
68369
- console.log(` ${pc72.cyan("email inbound status")} Show inbound email status`);
68370
68465
  console.log(
68371
- ` ${pc72.cyan("email inbound verify")} Verify inbound DNS records`
68466
+ ` ${pc72.cyan("email domains get-dkim")} Show DKIM tokens for a domain`
68372
68467
  );
68373
68468
  console.log(
68374
- ` ${pc72.cyan("email inbound test")} Send test email and verify receipt`
68469
+ ` ${pc72.cyan("email domains config")} Configure domain tracking, TLS, and archiving`
68375
68470
  );
68376
68471
  console.log(
68377
- ` ${pc72.cyan("email inbound destroy")} Remove inbound email infrastructure
68472
+ ` ${pc72.cyan("email inbound init")} Enable inbound email receiving`
68473
+ );
68474
+ console.log(
68475
+ ` ${pc72.cyan("email inbound status")} Show inbound email status`
68476
+ );
68477
+ console.log(
68478
+ ` ${pc72.cyan("email inbound verify")} Verify inbound DNS records`
68479
+ );
68480
+ console.log(
68481
+ ` ${pc72.cyan("email inbound test")} Send test email and verify receipt`
68482
+ );
68483
+ console.log(
68484
+ ` ${pc72.cyan("email inbound add")} Add an inbound receiving domain`
68485
+ );
68486
+ console.log(
68487
+ ` ${pc72.cyan("email inbound remove")} Remove an inbound receiving domain`
68488
+ );
68489
+ console.log(
68490
+ ` ${pc72.cyan("email inbound destroy")} Remove inbound email infrastructure`
68491
+ );
68492
+ console.log(
68493
+ ` ${pc72.cyan("email reply init")} Set up signed reply-to threading`
68494
+ );
68495
+ console.log(
68496
+ ` ${pc72.cyan("email reply rotate")} Rotate the reply-threading signing secret`
68497
+ );
68498
+ console.log(
68499
+ ` ${pc72.cyan("email reply status")} Show reply-threading status`
68500
+ );
68501
+ console.log(
68502
+ ` ${pc72.cyan("email reply destroy")} Remove reply-threading infrastructure`
68503
+ );
68504
+ console.log(
68505
+ ` ${pc72.cyan("email reply decode")} Decode a reply-to address`
68506
+ );
68507
+ console.log(
68508
+ ` ${pc72.cyan("email logs list")} List recent email sends`
68509
+ );
68510
+ console.log(
68511
+ ` ${pc72.cyan("email logs get")} Show details for one message
68378
68512
  `
68379
68513
  );
68380
68514
  console.log("Agent Commands:");
@@ -68408,7 +68542,10 @@ function showHelp() {
68408
68542
  ` ${pc72.cyan("email workflows validate")} Validate workflow files`
68409
68543
  );
68410
68544
  console.log(
68411
- ` ${pc72.cyan("email workflows push")} Push workflows to dashboard
68545
+ ` ${pc72.cyan("email workflows push")} Push workflows to dashboard`
68546
+ );
68547
+ console.log(
68548
+ ` ${pc72.cyan("workflow init")} Scaffold a cross-channel workflow (email \u2192 SMS)
68412
68549
  `
68413
68550
  );
68414
68551
  console.log("SMS Commands:");