@wraps.dev/cli 2.26.0 → 2.26.1

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.
Binary file
package/dist/cli.js CHANGED
@@ -16120,9 +16120,38 @@ async function deployEmailStack(config2) {
16120
16120
  };
16121
16121
  }
16122
16122
 
16123
+ // src/utils/shared/agent-api.ts
16124
+ init_esm_shims();
16125
+ init_config();
16126
+ async function createAgentApiClient(tokenFlag) {
16127
+ const token = await resolveTokenAsync({ token: tokenFlag });
16128
+ if (!token) {
16129
+ return { ok: false, reason: "not-authenticated" };
16130
+ }
16131
+ const apiBase = getApiBaseUrl();
16132
+ const orgHeader = {};
16133
+ const orgs = (await readAuthConfig())?.auth?.organizations;
16134
+ if (orgs && orgs.length === 1) {
16135
+ orgHeader["X-Organization-Id"] = orgs[0].id;
16136
+ }
16137
+ const call = (method) => (path3, body) => fetch(`${apiBase}${path3}`, {
16138
+ method,
16139
+ headers: {
16140
+ Authorization: `Bearer ${token}`,
16141
+ ...body ? { "Content-Type": "application/json" } : {},
16142
+ ...orgHeader
16143
+ },
16144
+ ...body ? { body: JSON.stringify(body) } : {}
16145
+ });
16146
+ return { ok: true, get: call("GET"), post: call("POST") };
16147
+ }
16148
+ async function parseAgentApiError(resp) {
16149
+ const data = await resp.json().catch(() => ({}));
16150
+ return data.error ?? `HTTP ${resp.status}`;
16151
+ }
16152
+
16123
16153
  // src/commands/email/agent.ts
16124
16154
  init_aws();
16125
- init_config();
16126
16155
  init_fs();
16127
16156
  init_json_output();
16128
16157
  init_metadata();
@@ -16169,32 +16198,6 @@ var DEFAULT_AGENT_POLICY = {
16169
16198
  allowedRecipientDomains: []
16170
16199
  };
16171
16200
  var AGENT_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,51}$/;
16172
- async function createAgentApiClient(tokenFlag) {
16173
- const token = await resolveTokenAsync({ token: tokenFlag });
16174
- if (!token) {
16175
- return { ok: false, reason: "not-authenticated" };
16176
- }
16177
- const apiBase = getApiBaseUrl();
16178
- const orgHeader = {};
16179
- const orgs = (await readAuthConfig())?.auth?.organizations;
16180
- if (orgs && orgs.length === 1) {
16181
- orgHeader["X-Organization-Id"] = orgs[0].id;
16182
- }
16183
- const call = (method) => (path3, body) => fetch(`${apiBase}${path3}`, {
16184
- method,
16185
- headers: {
16186
- Authorization: `Bearer ${token}`,
16187
- ...body ? { "Content-Type": "application/json" } : {},
16188
- ...orgHeader
16189
- },
16190
- ...body ? { body: JSON.stringify(body) } : {}
16191
- });
16192
- return { ok: true, get: call("GET"), post: call("POST") };
16193
- }
16194
- async function parseError(resp) {
16195
- const data = await resp.json().catch(() => ({}));
16196
- return data.error ?? `HTTP ${resp.status}`;
16197
- }
16198
16201
  async function agentCreate(options) {
16199
16202
  if (!isJsonMode()) {
16200
16203
  clack15.intro(pc17.bold("Create Agent Mailbox"));
@@ -16250,7 +16253,7 @@ Sign in first: ${pc17.cyan("wraps auth login")}
16250
16253
  async () => {
16251
16254
  const resp = await api.get("/v1/connections");
16252
16255
  if (!resp.ok) {
16253
- throw new Error(await parseError(resp));
16256
+ throw new Error(await parseAgentApiError(resp));
16254
16257
  }
16255
16258
  const data = await resp.json();
16256
16259
  return data.connections.some((c) => c.accountId === identity.accountId);
@@ -16396,7 +16399,7 @@ Connect (or reconnect) first so it is stored locally:
16396
16399
  return { kind: "created", agent: await resp.json() };
16397
16400
  }
16398
16401
  if (resp.status !== 409) {
16399
- throw new Error(await parseError(resp));
16402
+ throw new Error(await parseAgentApiError(resp));
16400
16403
  }
16401
16404
  const listResp = await api.get("/v1/agents");
16402
16405
  const existing = listResp.ok ? (await listResp.json()).agents.find(
@@ -16567,7 +16570,7 @@ ${suggestion}
16567
16570
  awsAccountId: identity.accountId
16568
16571
  });
16569
16572
  if (!resp.ok) {
16570
- throw new Error(await parseError(resp));
16573
+ throw new Error(await parseAgentApiError(resp));
16571
16574
  }
16572
16575
  });
16573
16576
  if (isJsonMode()) {
@@ -16630,7 +16633,7 @@ async function agentList(options) {
16630
16633
  }
16631
16634
  const resp = await api.get("/v1/agents");
16632
16635
  if (!resp.ok) {
16633
- const message = await parseError(resp);
16636
+ const message = await parseAgentApiError(resp);
16634
16637
  if (isJsonMode()) {
16635
16638
  jsonError("email.agent.list", { code: "API_ERROR", message });
16636
16639
  } else {
@@ -16670,7 +16673,7 @@ async function agentKill(options) {
16670
16673
  }
16671
16674
  const listResp = await api.get("/v1/agents");
16672
16675
  if (!listResp.ok) {
16673
- const message = await parseError(listResp);
16676
+ const message = await parseAgentApiError(listResp);
16674
16677
  if (isJsonMode()) {
16675
16678
  jsonError("email.agent.kill", { code: "API_ERROR", message });
16676
16679
  } else {
@@ -16729,7 +16732,7 @@ async function agentKill(options) {
16729
16732
  }
16730
16733
  const resp = await api.post(`/v1/agents/${target.id}/kill`);
16731
16734
  if (!resp.ok) {
16732
- const message = await parseError(resp);
16735
+ const message = await parseAgentApiError(resp);
16733
16736
  if (isJsonMode()) {
16734
16737
  jsonError("email.agent.kill", { code: "API_ERROR", message });
16735
16738
  } else {
@@ -20535,6 +20538,9 @@ ${pc20.dim("Example:")}`);
20535
20538
  init_esm_shims();
20536
20539
  init_events();
20537
20540
  init_route53();
20541
+ import * as clack19 from "@clack/prompts";
20542
+ import * as pulumi18 from "@pulumi/pulumi";
20543
+ import pc21 from "picocolors";
20538
20544
  init_aws();
20539
20545
  init_errors();
20540
20546
  init_fs();
@@ -20542,9 +20548,6 @@ init_json_output();
20542
20548
  init_metadata();
20543
20549
  init_output();
20544
20550
  init_pulumi();
20545
- import * as clack19 from "@clack/prompts";
20546
- import * as pulumi18 from "@pulumi/pulumi";
20547
- import pc21 from "picocolors";
20548
20551
  async function getEmailIdentityInfo(domain, region) {
20549
20552
  try {
20550
20553
  const { SESv2Client: SESv2Client10, GetEmailIdentityCommand: GetEmailIdentityCommand7 } = await import("@aws-sdk/client-sesv2");
@@ -20563,6 +20566,30 @@ async function getEmailIdentityInfo(domain, region) {
20563
20566
  throw error;
20564
20567
  }
20565
20568
  }
20569
+ function selectAgentsToDestroy(emailConfig) {
20570
+ const agentsConfig = emailConfig?.agents;
20571
+ if (!agentsConfig?.enabled) {
20572
+ return [];
20573
+ }
20574
+ return (agentsConfig.agents ?? []).map((a) => ({
20575
+ id: a.id,
20576
+ name: a.name,
20577
+ emailAddress: a.emailAddress
20578
+ }));
20579
+ }
20580
+ function buildAgentDestroySummaryLines(agents) {
20581
+ if (agents.length === 0) {
20582
+ return [];
20583
+ }
20584
+ const addresses = agents.map((a) => a.emailAddress).join(", ");
20585
+ return [
20586
+ "",
20587
+ `Agent mailboxes (${agents.length}): ${addresses}`,
20588
+ pc21.yellow(
20589
+ "Agent scoped credentials are revoked (IAM users wraps-agent-* deleted), the wraps-agent-enforcer Lambda and wraps-email-agent-policy table are destroyed, and any pending approvals become undeliverable."
20590
+ )
20591
+ ];
20592
+ }
20566
20593
  async function emailDestroy(options) {
20567
20594
  const startTime = Date.now();
20568
20595
  if (isJsonMode() && !options.force) {
@@ -20623,6 +20650,7 @@ async function emailDestroy(options) {
20623
20650
  const emailConfig = emailService?.config;
20624
20651
  const domain = emailConfig?.domain;
20625
20652
  const storedStackName = emailService?.pulumiStackName;
20653
+ const agentsToDestroy = selectAgentsToDestroy(emailConfig);
20626
20654
  if (!(options.force || options.preview)) {
20627
20655
  const additionalDomainNames = (emailConfig?.additionalDomains ?? []).map((d) => d.domain).filter(Boolean);
20628
20656
  const domainList = [domain, ...additionalDomainNames].filter(Boolean);
@@ -20635,6 +20663,7 @@ async function emailDestroy(options) {
20635
20663
  "Event streaming stops: the Wraps dashboard email timeline and event history for THIS ENTIRE AWS ACCOUNT stop receiving events. Historical events in DynamoDB are destroyed with the table. This cannot be undone."
20636
20664
  )
20637
20665
  ];
20666
+ summaryLines.push(...buildAgentDestroySummaryLines(agentsToDestroy));
20638
20667
  if (emailService?.webhookSecret) {
20639
20668
  summaryLines.push(
20640
20669
  "",
@@ -20731,6 +20760,13 @@ async function emailDestroy(options) {
20731
20760
  );
20732
20761
  }
20733
20762
  }
20763
+ const agentSummaryLines = buildAgentDestroySummaryLines(agentsToDestroy);
20764
+ if (agentSummaryLines.length > 0) {
20765
+ clack19.note(
20766
+ agentSummaryLines.filter(Boolean).join("\n"),
20767
+ "Agent mailboxes"
20768
+ );
20769
+ }
20734
20770
  clack19.outro(
20735
20771
  pc21.green("Preview complete. Run without --preview to destroy.")
20736
20772
  );
@@ -20850,6 +20886,33 @@ async function emailDestroy(options) {
20850
20886
  }
20851
20887
  } catch {
20852
20888
  }
20889
+ if (agentsToDestroy.length > 0) {
20890
+ try {
20891
+ const agentApi = await createAgentApiClient();
20892
+ if (agentApi.ok) {
20893
+ let killed = 0;
20894
+ for (const agent of agentsToDestroy) {
20895
+ try {
20896
+ const resp = await agentApi.post(`/v1/agents/${agent.id}/kill`);
20897
+ if (resp.ok) {
20898
+ killed++;
20899
+ }
20900
+ } catch {
20901
+ }
20902
+ }
20903
+ if (!isJsonMode()) {
20904
+ clack19.log.info(
20905
+ `Marked ${killed}/${agentsToDestroy.length} agent(s) as killed in the Wraps dashboard.`
20906
+ );
20907
+ }
20908
+ } else if (!isJsonMode()) {
20909
+ clack19.log.warn(
20910
+ "Could not reach the Wraps Platform to update agent status \u2014 your agents will still show as active in the dashboard. Kill them there: wraps email agent kill (or the dashboard)."
20911
+ );
20912
+ }
20913
+ } catch {
20914
+ }
20915
+ }
20853
20916
  await deleteConnectionMetadata(identity.accountId, region);
20854
20917
  progress.stop();
20855
20918
  if (isJsonMode()) {
@@ -20857,7 +20920,12 @@ async function emailDestroy(options) {
20857
20920
  destroyed: !destroyFailed,
20858
20921
  region,
20859
20922
  dns_cleaned: shouldCleanDNS,
20860
- partial_failure: destroyFailed
20923
+ partial_failure: destroyFailed,
20924
+ agents: agentsToDestroy.map((a) => ({
20925
+ id: a.id,
20926
+ name: a.name,
20927
+ emailAddress: a.emailAddress
20928
+ }))
20861
20929
  });
20862
20930
  trackServiceRemoved("email", {
20863
20931
  reason: "user_initiated",