@wraps.dev/cli 3.5.1 → 3.5.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
@@ -2651,6 +2651,7 @@ __export(metadata_exports, {
2651
2651
  applyConfigUpdates: () => applyConfigUpdates,
2652
2652
  buildEmailStackConfig: () => buildEmailStackConfig,
2653
2653
  connectionExists: () => connectionExists,
2654
+ createAdoptedConnectionMetadata: () => createAdoptedConnectionMetadata,
2654
2655
  createConnectionMetadata: () => createConnectionMetadata,
2655
2656
  deleteConnectionMetadata: () => deleteConnectionMetadata,
2656
2657
  findConnectionsForAccount: () => findConnectionsForAccount,
@@ -2885,6 +2886,16 @@ function createConnectionMetadata(accountId, region, provider, emailConfig, pres
2885
2886
  }
2886
2887
  };
2887
2888
  }
2889
+ function createAdoptedConnectionMetadata(accountId, region, provider) {
2890
+ return {
2891
+ version: "1.0.0",
2892
+ accountId,
2893
+ region,
2894
+ provider,
2895
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2896
+ services: {}
2897
+ };
2898
+ }
2888
2899
  function setConfigValue(config2, key, value) {
2889
2900
  config2[key] = value;
2890
2901
  }
@@ -32445,6 +32456,21 @@ var remediations = {
32445
32456
  id: "aws.env-vars-incomplete",
32446
32457
  level: "informational",
32447
32458
  summary: "AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must both be set, or neither."
32459
+ }),
32460
+ /**
32461
+ * Resources exist in AWS but this machine has no connection record.
32462
+ * `listConnections()` reads only the local ~/.wraps directory — it never
32463
+ * syncs the S3 state bucket that `loadConnectionMetadata()` falls back to —
32464
+ * so this fires for a CloudFormation-first connection AND for a second
32465
+ * machine. `wraps platform connect` adopts the existing deployment: it
32466
+ * registers the connection, repairs the trust policy and writes the local
32467
+ * record, without deploying anything.
32468
+ */
32469
+ adoptConnection: (region) => ({
32470
+ id: "platform.connect.adopt",
32471
+ level: "auto",
32472
+ command: withRegion("wraps platform connect", region),
32473
+ summary: "Register this account and write a local connection record \u2014 adopts the existing AWS resources without deploying anything."
32448
32474
  })
32449
32475
  };
32450
32476
  function formatRemediation(remediation) {
@@ -37357,6 +37383,15 @@ async function wrapsDoctor(options) {
37357
37383
  })
37358
37384
  );
37359
37385
  findings.push(...email.findings);
37386
+ if (email.totalResources > 0 && emailConnections.length === 0) {
37387
+ findings.push({
37388
+ status: "warn",
37389
+ category: "Email",
37390
+ name: "Wraps resources found in AWS, but no connection record on this machine",
37391
+ details: `Found ${email.totalResources} wraps-* resource(s) in ${region}, with no local deployment record for account ${state2.accountId}. CLI commands that resolve connection metadata will not see this deployment.`,
37392
+ remediation: remediations.adoptConnection(region)
37393
+ });
37394
+ }
37360
37395
  } catch (error) {
37361
37396
  findings.push({
37362
37397
  status: "warn",
@@ -56548,22 +56583,47 @@ async function validateAndLoadMetadata(options, progress) {
56548
56583
  optionRegion: options.region,
56549
56584
  label: "connection"
56550
56585
  });
56551
- const metadata = await loadConnectionMetadata(identity.accountId, region);
56586
+ let metadata = await loadConnectionMetadata(identity.accountId, region);
56587
+ let adopted = false;
56552
56588
  if (!metadata) {
56553
- progress.stop();
56554
- log41.error(
56555
- `No Wraps deployment found for account ${pc47.cyan(identity.accountId)} in region ${pc47.cyan(region)}`
56556
- );
56557
- console.log(
56558
- `
56589
+ if (!isJsonMode()) {
56590
+ progress.stop();
56591
+ log41.warn(
56592
+ `No Wraps deployment found for account ${pc47.cyan(identity.accountId)} in region ${pc47.cyan(region)}`
56593
+ );
56594
+ console.log(
56595
+ `
56596
+ This is expected if infrastructure here was deployed another way \u2014 for example, through the dashboard's CloudFormation flow instead of ${pc47.cyan("wraps email init")}.
56597
+ `
56598
+ );
56599
+ }
56600
+ const shouldAdopt = options.yes === true || isJsonMode() || await confirm21({
56601
+ message: `Register AWS account ${identity.accountId} with Wraps without deploying new infrastructure?`,
56602
+ initialValue: true
56603
+ });
56604
+ if (isCancel29(shouldAdopt) || !shouldAdopt) {
56605
+ if (!isJsonMode()) {
56606
+ log41.error(
56607
+ `No Wraps deployment found for account ${identity.accountId}.`
56608
+ );
56609
+ console.log(
56610
+ `
56559
56611
  Run ${pc47.cyan("wraps email init")} to deploy infrastructure first.
56560
56612
  `
56613
+ );
56614
+ }
56615
+ process.exit(0);
56616
+ }
56617
+ metadata = createAdoptedConnectionMetadata(
56618
+ identity.accountId,
56619
+ region,
56620
+ "other"
56561
56621
  );
56562
- process.exit(1);
56622
+ adopted = true;
56563
56623
  }
56564
56624
  const hasEmail = !!metadata.services.email?.config;
56565
56625
  const hasSms = !!metadata.services.sms?.config;
56566
- if (!(hasEmail || hasSms)) {
56626
+ if (!(adopted || hasEmail || hasSms)) {
56567
56627
  progress.stop();
56568
56628
  log41.error("No services deployed in this region.");
56569
56629
  console.log(
@@ -56573,10 +56633,16 @@ Run ${pc47.cyan("wraps email init")} or ${pc47.cyan("wraps sms init")} first.
56573
56633
  );
56574
56634
  process.exit(1);
56575
56635
  }
56576
- progress.info(
56577
- `Found services: ${[hasEmail && "email", hasSms && "sms"].filter(Boolean).join(", ")}`
56578
- );
56579
- return { identity, region, metadata };
56636
+ if (adopted) {
56637
+ progress.info(
56638
+ "Adopting existing AWS infrastructure \u2014 no new resources will be deployed."
56639
+ );
56640
+ } else {
56641
+ progress.info(
56642
+ `Found services: ${[hasEmail && "email", hasSms && "sms"].filter(Boolean).join(", ")}`
56643
+ );
56644
+ }
56645
+ return { identity, region, metadata, adopted };
56580
56646
  }
56581
56647
  async function deployEventBridge(metadata, region, identity, webhookSecret, progress, selfhostTarget) {
56582
56648
  if (metadata.provider === "vercel" && !metadata.vercel) {
@@ -56649,17 +56715,20 @@ async function updatePlatformRole(metadata, progress, externalId, selfhosted) {
56649
56715
  }
56650
56716
  const emailConfig = metadata.services.email?.config;
56651
56717
  const smsConfig = metadata.services.sms?.config;
56652
- const policy = buildConsolePolicyDocument(emailConfig, smsConfig);
56718
+ const hasPolicySource = Boolean(emailConfig || smsConfig);
56653
56719
  if (roleExists2) {
56654
- await progress.execute("Updating platform access role", async () => {
56655
- await iam13.send(
56656
- new PutRolePolicyCommand({
56657
- RoleName: roleName,
56658
- PolicyName: "wraps-console-access-policy",
56659
- PolicyDocument: JSON.stringify(policy, null, 2)
56660
- })
56661
- );
56662
- });
56720
+ if (hasPolicySource) {
56721
+ const policy = buildConsolePolicyDocument(emailConfig, smsConfig);
56722
+ await progress.execute("Updating platform access role", async () => {
56723
+ await iam13.send(
56724
+ new PutRolePolicyCommand({
56725
+ RoleName: roleName,
56726
+ PolicyName: "wraps-console-access-policy",
56727
+ PolicyDocument: JSON.stringify(policy, null, 2)
56728
+ })
56729
+ );
56730
+ });
56731
+ }
56663
56732
  if (externalId) {
56664
56733
  const trustPolicy = {
56665
56734
  Version: "2012-10-17",
@@ -56718,6 +56787,7 @@ async function updatePlatformRole(metadata, progress, externalId, selfhosted) {
56718
56787
  ]
56719
56788
  })
56720
56789
  );
56790
+ const policy = buildConsolePolicyDocument(emailConfig, smsConfig);
56721
56791
  await iam13.send(
56722
56792
  new PutRolePolicyCommand({
56723
56793
  RoleName: roleName,
@@ -56789,10 +56859,7 @@ async function authenticatedConnect(options, saasToken) {
56789
56859
  }
56790
56860
  const progress = new DeploymentProgress();
56791
56861
  try {
56792
- const { identity, region, metadata } = await validateAndLoadMetadata(
56793
- options,
56794
- progress
56795
- );
56862
+ const { identity, region, metadata, adopted } = await validateAndLoadMetadata(options, progress);
56796
56863
  const selfhostService = metadata.services.selfhost;
56797
56864
  if (selfhosted && selfhostService) {
56798
56865
  await reconcileSelfhostApiUrl(metadata, region);
@@ -56961,7 +57028,7 @@ You can try the manual flow: ${pc47.cyan("wraps auth logout")} then ${pc47.cyan(
56961
57028
  }
56962
57029
  }
56963
57030
  await saveConnectionMetadata(metadata);
56964
- if (hasEmail) {
57031
+ if (hasEmail && !adopted) {
56965
57032
  await deployEventBridge(
56966
57033
  metadata,
56967
57034
  region,
@@ -56995,9 +57062,25 @@ You can try the manual flow: ${pc47.cyan("wraps auth logout")} then ${pc47.cyan(
56995
57062
  region,
56996
57063
  organizationId: org.id,
56997
57064
  connectionId: result.connectionId,
56998
- webhookConnected: true,
56999
- selfhosted
57065
+ webhookConnected: !adopted,
57066
+ selfhosted,
57067
+ adopted
57000
57068
  });
57069
+ } else if (adopted) {
57070
+ outro26(pc47.green("AWS account adopted!"));
57071
+ console.log();
57072
+ console.log(
57073
+ pc47.dim(
57074
+ "Registered this account and repaired the IAM trust policy so the dashboard can reach it."
57075
+ )
57076
+ );
57077
+ console.log(
57078
+ pc47.dim(
57079
+ "No infrastructure was deployed and no event wiring was changed."
57080
+ )
57081
+ );
57082
+ console.log(` Dashboard: ${pc47.cyan(dashboardUrl)}`);
57083
+ console.log();
57001
57084
  } else {
57002
57085
  outro26(
57003
57086
  pc47.green(