@wraps.dev/cli 3.5.1 → 3.5.3

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
  }
@@ -31752,8 +31763,8 @@ async function fetchLatestVersion() {
31752
31763
  }
31753
31764
  const releases = await res.json();
31754
31765
  for (const release2 of releases) {
31755
- if (release2.tag_name.startsWith("cli@")) {
31756
- const version = release2.tag_name.replace("cli@", "");
31766
+ if (release2.tag_name.startsWith(CLI_TAG_PREFIX)) {
31767
+ const version = release2.tag_name.slice(CLI_TAG_PREFIX.length);
31757
31768
  return { version, release: release2 };
31758
31769
  }
31759
31770
  }
@@ -31871,13 +31882,14 @@ async function update(currentVersion) {
31871
31882
  rmSync(tmp, { recursive: true, force: true });
31872
31883
  }
31873
31884
  }
31874
- var REPO, INSTALL_DIR, WHITESPACE_RE;
31885
+ var REPO, CLI_TAG_PREFIX, INSTALL_DIR, WHITESPACE_RE;
31875
31886
  var init_update = __esm({
31876
31887
  "src/commands/update.ts"() {
31877
31888
  "use strict";
31878
31889
  init_esm_shims();
31879
31890
  init_output();
31880
31891
  REPO = "wraps-team/wraps";
31892
+ CLI_TAG_PREFIX = "cli-v";
31881
31893
  INSTALL_DIR = join24(homedir6(), ".wraps");
31882
31894
  WHITESPACE_RE = /\s+/;
31883
31895
  }
@@ -32445,6 +32457,21 @@ var remediations = {
32445
32457
  id: "aws.env-vars-incomplete",
32446
32458
  level: "informational",
32447
32459
  summary: "AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must both be set, or neither."
32460
+ }),
32461
+ /**
32462
+ * Resources exist in AWS but this machine has no connection record.
32463
+ * `listConnections()` reads only the local ~/.wraps directory — it never
32464
+ * syncs the S3 state bucket that `loadConnectionMetadata()` falls back to —
32465
+ * so this fires for a CloudFormation-first connection AND for a second
32466
+ * machine. `wraps platform connect` adopts the existing deployment: it
32467
+ * registers the connection, repairs the trust policy and writes the local
32468
+ * record, without deploying anything.
32469
+ */
32470
+ adoptConnection: (region) => ({
32471
+ id: "platform.connect.adopt",
32472
+ level: "auto",
32473
+ command: withRegion("wraps platform connect", region),
32474
+ summary: "Register this account and write a local connection record \u2014 adopts the existing AWS resources without deploying anything."
32448
32475
  })
32449
32476
  };
32450
32477
  function formatRemediation(remediation) {
@@ -37357,6 +37384,15 @@ async function wrapsDoctor(options) {
37357
37384
  })
37358
37385
  );
37359
37386
  findings.push(...email.findings);
37387
+ if (email.totalResources > 0 && emailConnections.length === 0) {
37388
+ findings.push({
37389
+ status: "warn",
37390
+ category: "Email",
37391
+ name: "Wraps resources found in AWS, but no connection record on this machine",
37392
+ 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.`,
37393
+ remediation: remediations.adoptConnection(region)
37394
+ });
37395
+ }
37360
37396
  } catch (error) {
37361
37397
  findings.push({
37362
37398
  status: "warn",
@@ -56548,22 +56584,47 @@ async function validateAndLoadMetadata(options, progress) {
56548
56584
  optionRegion: options.region,
56549
56585
  label: "connection"
56550
56586
  });
56551
- const metadata = await loadConnectionMetadata(identity.accountId, region);
56587
+ let metadata = await loadConnectionMetadata(identity.accountId, region);
56588
+ let adopted = false;
56552
56589
  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
- `
56590
+ if (!isJsonMode()) {
56591
+ progress.stop();
56592
+ log41.warn(
56593
+ `No Wraps deployment found for account ${pc47.cyan(identity.accountId)} in region ${pc47.cyan(region)}`
56594
+ );
56595
+ console.log(
56596
+ `
56597
+ 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")}.
56598
+ `
56599
+ );
56600
+ }
56601
+ const shouldAdopt = options.yes === true || isJsonMode() || await confirm21({
56602
+ message: `Register AWS account ${identity.accountId} with Wraps without deploying new infrastructure?`,
56603
+ initialValue: true
56604
+ });
56605
+ if (isCancel29(shouldAdopt) || !shouldAdopt) {
56606
+ if (!isJsonMode()) {
56607
+ log41.error(
56608
+ `No Wraps deployment found for account ${identity.accountId}.`
56609
+ );
56610
+ console.log(
56611
+ `
56559
56612
  Run ${pc47.cyan("wraps email init")} to deploy infrastructure first.
56560
56613
  `
56614
+ );
56615
+ }
56616
+ process.exit(0);
56617
+ }
56618
+ metadata = createAdoptedConnectionMetadata(
56619
+ identity.accountId,
56620
+ region,
56621
+ "other"
56561
56622
  );
56562
- process.exit(1);
56623
+ adopted = true;
56563
56624
  }
56564
56625
  const hasEmail = !!metadata.services.email?.config;
56565
56626
  const hasSms = !!metadata.services.sms?.config;
56566
- if (!(hasEmail || hasSms)) {
56627
+ if (!(adopted || hasEmail || hasSms)) {
56567
56628
  progress.stop();
56568
56629
  log41.error("No services deployed in this region.");
56569
56630
  console.log(
@@ -56573,10 +56634,16 @@ Run ${pc47.cyan("wraps email init")} or ${pc47.cyan("wraps sms init")} first.
56573
56634
  );
56574
56635
  process.exit(1);
56575
56636
  }
56576
- progress.info(
56577
- `Found services: ${[hasEmail && "email", hasSms && "sms"].filter(Boolean).join(", ")}`
56578
- );
56579
- return { identity, region, metadata };
56637
+ if (adopted) {
56638
+ progress.info(
56639
+ "Adopting existing AWS infrastructure \u2014 no new resources will be deployed."
56640
+ );
56641
+ } else {
56642
+ progress.info(
56643
+ `Found services: ${[hasEmail && "email", hasSms && "sms"].filter(Boolean).join(", ")}`
56644
+ );
56645
+ }
56646
+ return { identity, region, metadata, adopted };
56580
56647
  }
56581
56648
  async function deployEventBridge(metadata, region, identity, webhookSecret, progress, selfhostTarget) {
56582
56649
  if (metadata.provider === "vercel" && !metadata.vercel) {
@@ -56649,17 +56716,20 @@ async function updatePlatformRole(metadata, progress, externalId, selfhosted) {
56649
56716
  }
56650
56717
  const emailConfig = metadata.services.email?.config;
56651
56718
  const smsConfig = metadata.services.sms?.config;
56652
- const policy = buildConsolePolicyDocument(emailConfig, smsConfig);
56719
+ const hasPolicySource = Boolean(emailConfig || smsConfig);
56653
56720
  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
- });
56721
+ if (hasPolicySource) {
56722
+ const policy = buildConsolePolicyDocument(emailConfig, smsConfig);
56723
+ await progress.execute("Updating platform access role", async () => {
56724
+ await iam13.send(
56725
+ new PutRolePolicyCommand({
56726
+ RoleName: roleName,
56727
+ PolicyName: "wraps-console-access-policy",
56728
+ PolicyDocument: JSON.stringify(policy, null, 2)
56729
+ })
56730
+ );
56731
+ });
56732
+ }
56663
56733
  if (externalId) {
56664
56734
  const trustPolicy = {
56665
56735
  Version: "2012-10-17",
@@ -56718,6 +56788,7 @@ async function updatePlatformRole(metadata, progress, externalId, selfhosted) {
56718
56788
  ]
56719
56789
  })
56720
56790
  );
56791
+ const policy = buildConsolePolicyDocument(emailConfig, smsConfig);
56721
56792
  await iam13.send(
56722
56793
  new PutRolePolicyCommand({
56723
56794
  RoleName: roleName,
@@ -56789,10 +56860,7 @@ async function authenticatedConnect(options, saasToken) {
56789
56860
  }
56790
56861
  const progress = new DeploymentProgress();
56791
56862
  try {
56792
- const { identity, region, metadata } = await validateAndLoadMetadata(
56793
- options,
56794
- progress
56795
- );
56863
+ const { identity, region, metadata, adopted } = await validateAndLoadMetadata(options, progress);
56796
56864
  const selfhostService = metadata.services.selfhost;
56797
56865
  if (selfhosted && selfhostService) {
56798
56866
  await reconcileSelfhostApiUrl(metadata, region);
@@ -56961,7 +57029,7 @@ You can try the manual flow: ${pc47.cyan("wraps auth logout")} then ${pc47.cyan(
56961
57029
  }
56962
57030
  }
56963
57031
  await saveConnectionMetadata(metadata);
56964
- if (hasEmail) {
57032
+ if (hasEmail && !adopted) {
56965
57033
  await deployEventBridge(
56966
57034
  metadata,
56967
57035
  region,
@@ -56995,9 +57063,25 @@ You can try the manual flow: ${pc47.cyan("wraps auth logout")} then ${pc47.cyan(
56995
57063
  region,
56996
57064
  organizationId: org.id,
56997
57065
  connectionId: result.connectionId,
56998
- webhookConnected: true,
56999
- selfhosted
57066
+ webhookConnected: !adopted,
57067
+ selfhosted,
57068
+ adopted
57000
57069
  });
57070
+ } else if (adopted) {
57071
+ outro26(pc47.green("AWS account adopted!"));
57072
+ console.log();
57073
+ console.log(
57074
+ pc47.dim(
57075
+ "Registered this account and repaired the IAM trust policy so the dashboard can reach it."
57076
+ )
57077
+ );
57078
+ console.log(
57079
+ pc47.dim(
57080
+ "No infrastructure was deployed and no event wiring was changed."
57081
+ )
57082
+ );
57083
+ console.log(` Dashboard: ${pc47.cyan(dashboardUrl)}`);
57084
+ console.log();
57001
57085
  } else {
57002
57086
  outro26(
57003
57087
  pc47.green(