@agentcomms/resend 0.13.1 → 0.14.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.
package/README.md CHANGED
@@ -19,10 +19,11 @@ says this on every run.
19
19
 
20
20
  **Nothing is sent without a person.** A send is prepared, previewed — every recipient, BCC included, the reach (unique
21
21
  recipients), the From domain and whether it is verified — and approved: a yes in the conversation under the account's
22
- `chat` policy, or the approve command the result gives and a typed code under `confirm`. Above ten recipients, or to
23
- an address that arrived in mail read here, a person at a terminal approves, whatever the policy. The approval id is
24
- the request's `Idempotency-Key` and an `agentcomms_approval` tag; a send whose outcome is unknown is never repeated,
25
- only checked.
22
+ `chat` policy, or the approve command the result gives and a typed code under `confirm` — which the agent learns of
23
+ with `resend_send_wait`. Above ten recipients, or to an address that arrived in mail read here, a person at a terminal
24
+ approves, whatever the policy. The approval id is the request's `Idempotency-Key` and an `agentcomms_approval` tag; a
25
+ send whose outcome is unknown (`SEND_OUTCOME_UNKNOWN`) is never repeated, only checked, and a scheduled one is
26
+ "accepted", never "sent", until Resend's own last event for it says so.
26
27
 
27
28
  **The key is typed by a person, at a terminal.** `agent-resend account add` reads it from a hidden prompt, or from
28
29
  `RESEND_API_KEY` in that terminal, and stores it in the system keychain. No MCP tool accepts a key: a key typed into
@@ -96,6 +97,7 @@ terminal), `approve` (under `confirm`, a person at a terminal) and `mcp` (it sta
96
97
  | `agent-resend send prepare` | `resend_send_prepare` |
97
98
  | `agent-resend send execute <approvalId>` | `resend_send_execute` |
98
99
  | `agent-resend send status <approvalId>` | `resend_send_status` |
100
+ | `agent-resend send wait <approvalId>` | `resend_send_wait` |
99
101
  | `agent-resend scheduled list` | `resend_scheduled_list` |
100
102
  | `agent-resend scheduled cancel <id>` | `resend_scheduled_cancel` |
101
103
  | `agent-resend approve <approvalId>` | — a person at a terminal, with the command the result gives |
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { B as UPDATE_CHECK_CHILD_COMMAND, C as policyChange, D as sendPolicyOf, Dt as VERSION, E as removeAccountChange, F as CHANNEL_SERVERS, G as canPrompt, H as approvalKind, I as CommsError, J as cliHandoffs, L as EXIT_CODES, N as checkNewName, O as showAccount, Q as exemptFromUpdateGate, R as PATH_OPTIONS, S as policyApprovalRefusal, St as RESEND_CALLER, T as policyWanted, U as approvalsOf, V as agentMarker, W as approveChangeAtTerminal, X as commandPathOf, Y as colorEnabled, Z as escapeForDisplay, _ as runDoctor, _t as truncateDisplay, a as revokeSendApproval, at as paint, b as listAccounts, bt as writeResult, c as downloadReceived, ct as renderInstall, d as listReceived, dt as runCommand, et as gatedChangeAtTerminal, f as listScheduled, ft as runUpdateCheckChild, g as showSentEmail, h as showReceived, ht as terminalUpdateHooks, i as prepareSend, it as openCore, k as ResendContext, l as getMetrics, lt as renderPrune, m as listSuppressions, mt as serverPruneChange, n as executeSend, nt as handoffSentenceToFill, o as sendStatus, ot as pathOverridesFromCliOptions, p as listSentEmails, pt as serverInstallChange, r as finishSendApproval, rt as installExitStatus, s as cancelScheduledChange, st as refuseUnclaimedApproval, t as beginSendApproval, tt as handoffSentence, u as listDomains, ut as resolvePaths, v as addAccountChange, vt as updateGateAtTerminal, w as policyReport, x as modeOf, y as inspectKey, z as SEND_LOOKUP } from "./send-DtBNDnE4.mjs";
2
+ import { $ as exemptFromUpdateGate, B as UPDATE_CHECK_CHILD_COMMAND, C as policyChange, D as sendPolicyOf, E as removeAccountChange, Et as writeResult, F as CHANNEL_SERVERS, G as canPrompt, H as approvalsOf, I as CommsError, J as cliHandoffs, L as EXIT_CODES, N as checkNewName, Nt as VERSION, O as showAccount, Ot as RESEND_CALLER, Q as escapeForDisplay, R as PATH_OPTIONS, S as policyApprovalRefusal, T as policyWanted, Tt as wholeNumber, U as approveChangeAtTerminal, V as agentMarker, W as approveRefusedHint, X as commandPathOf, Y as colorEnabled, Z as describeFenceReport, _ as runDoctor, _t as serverPruneChange, a as revokeSendApproval, at as kindOf, b as listAccounts, bt as truncateDisplay, c as downloadReceived, ct as pathOverridesFromCliOptions, d as listReceived, dt as renderInstall, f as listScheduled, ft as renderPrune, g as showSentEmail, gt as serverInstallChange, h as showReceived, ht as runUpdateCheckChild, i as prepareSend, it as installExitStatus, k as ResendContext, l as getMetrics, lt as refuseUnclaimedApproval, m as listSuppressions, mt as runCommand, n as executeSend, nt as handoffSentence, o as sendStatus, ot as openCore, p as listSentEmails, pt as resolvePaths, r as finishSendApproval, rt as handoffSentenceToFill, s as cancelScheduledChange, st as paint, t as beginSendApproval, tt as gatedChangeAtTerminal, u as listDomains, ut as renderApprovalWait, v as addAccountChange, vt as terminalUpdateHooks, w as policyReport, wt as waitForApproval, x as modeOf, xt as updateGateAtTerminal, y as inspectKey, z as SEND_LOOKUP } from "./send-D0xGQynj.mjs";
3
3
  import path from "node:path";
4
4
  import { createInterface } from "node:readline/promises";
5
5
  import { stripVTControlCharacters } from "node:util";
@@ -3089,7 +3089,8 @@ function renderPolicy(report) {
3089
3089
  return [
3090
3090
  `${report.name}: ${report.mode} mode, send policy ${report.sendPolicy}${report.sendPolicyFrom === "default" ? " (the machine default)" : ""}`,
3091
3091
  `Changes to it are approved under the machine's change policy: ${report.changePolicy}.`,
3092
- `A send to more than ${report.confirmAboveRecipients} people needs a person at a terminal, whatever the policy.`
3092
+ `A send to more than ${report.confirmAboveRecipients} people needs a person at a terminal, whatever the policy.`,
3093
+ ...report.fenced === void 0 ? [] : describeFenceReport(report.fenced)
3093
3094
  ].join("\n");
3094
3095
  }
3095
3096
  function renderDoctor(result, color) {
@@ -3115,15 +3116,22 @@ function renderRead(result, color) {
3115
3116
  function renderPrepared(result) {
3116
3117
  return `${result.preview}\n\nNext: ${escapeForDisplay(result.nextStep)}`;
3117
3118
  }
3119
+ /**
3120
+ * What Resend's acceptance means (`said`), with its id when it gave one: a scheduled send is accepted for its time and
3121
+ * never called sent; one with no id says so, and names none.
3122
+ */
3118
3123
  function renderSent(result) {
3119
- const sent = result.state === "scheduled" ? `Scheduled for ${result.scheduledAt ?? "?"} as ${result.resendId}, to ${truncateDisplay(result.to.join(", "), 200)}.` : `Sent as ${result.resendId}, to ${truncateDisplay(result.to.join(", "), 200)}.`;
3124
+ const to = truncateDisplay(result.to.join(", "), 200);
3125
+ const said = `${result.said.charAt(0).toUpperCase()}${result.said.slice(1)}`;
3126
+ const sent = result.resendId === void 0 ? result.state === "scheduled" ? `${said} — scheduled for ${result.scheduledAt ?? "?"}, to ${to}.` : `${said} — to ${to}.` : result.state === "scheduled" ? `${said}, as ${result.resendId}, to ${to}.` : `Sent as ${result.resendId}, to ${to}.`;
3120
3127
  return result.note ? `${sent}\nNote: ${escapeForDisplay(result.note)}.` : sent;
3121
3128
  }
3122
3129
  function renderStatus(result) {
3123
3130
  return `${result.approvalId}: ${escapeForDisplay(result.verdict)}`;
3124
3131
  }
3125
3132
  function renderCancelled(result) {
3126
- return `Cancelled ${result.id} (${result.recipients} recipient(s)). It cannot be rescheduled.`;
3133
+ const cancelled = `Cancelled ${result.id} (${result.recipients} recipient(s)). It cannot be rescheduled.`;
3134
+ return result.hint ? `${cancelled}\nNote: ${escapeForDisplay(result.hint)}` : cancelled;
3127
3135
  }
3128
3136
  //#endregion
3129
3137
  //#region src/cli/program.ts
@@ -3234,8 +3242,9 @@ is out: update first, or put it off (the stop names both commands) · 64 usage
3234
3242
  noInput: false,
3235
3243
  streams,
3236
3244
  approvals: approvalsOf(command),
3237
- approvalClaim: path.join(" ") === "send status" ? SEND_LOOKUP : void 0,
3245
+ approvalClaim: path.join(" ") === "send status" ? SEND_LOOKUP : path.join(" ") === "send wait" ? { lookup: true } : void 0,
3238
3246
  ...terminalUpdateHooks(core, env, {
3247
+ channel: "resend",
3239
3248
  output: output(),
3240
3249
  streams
3241
3250
  })
@@ -3266,6 +3275,7 @@ is out: update first, or put it off (the stop names both commands) · 64 usage
3266
3275
  * after the program, which core locates as Resend's own and runs again with `--approval <id>`.
3267
3276
  */
3268
3277
  const changeAt = (context, change, flags, rerun) => gatedChangeAtTerminal(context.core, change, {
3278
+ channel: "resend",
3269
3279
  approvalId: flags.approval === void 0 ? void 0 : String(flags.approval),
3270
3280
  env,
3271
3281
  output: output(),
@@ -3430,6 +3440,17 @@ is out: update first, or put it off (the stop names both commands) · 64 usage
3430
3440
  const status = await sendStatus(context, String(flags.account), approvalId);
3431
3441
  writeResult(status, output(), renderStatus, streams);
3432
3442
  }));
3443
+ send.command("wait <approvalId>").description("wait for an approval to be usable or finished, and say where it stands — it only looks, and never sends").option("--wait-seconds <n>", `how long to wait: 30 when left out, 300 at most, 0 for the status now`).action(act(async (context, _options, approvalId, flags) => {
3444
+ const result = await waitForApproval(context.core, approvalId, {
3445
+ waitSeconds: wholeNumber(flags.waitSeconds, {
3446
+ name: "--wait-seconds",
3447
+ min: 0,
3448
+ max: 300
3449
+ }),
3450
+ channel: "resend"
3451
+ });
3452
+ writeResult(result, output(), renderApprovalWait, streams);
3453
+ }));
3433
3454
  const scheduled = program.command("scheduled").description("emails waiting to be sent at a later time");
3434
3455
  accountOption(scheduled.command("list")).description("scheduled emails among the most recent 300 sent").action(act(async (context, options, flags) => {
3435
3456
  const result = await listScheduled(context, String(flags.account));
@@ -3449,12 +3470,12 @@ is out: update first, or put it off (the stop names both commands) · 64 usage
3449
3470
  const marker = agentMarker(env);
3450
3471
  const approve = context.handoffs.own(["approve", approvalId]);
3451
3472
  if (marker) throw new CommsError("APPROVAL_REQUIRED", "only a person can approve a send or a change, not an agent", {
3452
- hint: handoffSentence(approve, (command) => `Ask the user to run ${command} in their own terminal.`),
3473
+ hint: approveRefusedHint(context.handoffs, approvalId),
3453
3474
  details: { marker }
3454
3475
  });
3455
3476
  if (!canPrompt(env, streams, { json: options.json })) throw new CommsError("APPROVAL_REQUIRED", "approving needs an interactive terminal", { hint: handoffSentence(approve, (command) => `Run ${command} directly in a terminal.`) });
3456
3477
  const pending = await context.core.approvals.get(approvalId);
3457
- if (pending && approvalKind(pending) === "change") {
3478
+ if (kindOf(pending) === "change") {
3458
3479
  const outcome = await approveChangeAtTerminal(context.core, approvalId, env, {
3459
3480
  json: options.json,
3460
3481
  color: options.color,
@@ -3476,7 +3497,7 @@ is out: update first, or put it off (the stop names both commands) · 64 usage
3476
3497
  }));
3477
3498
  const mcp = program.command("mcp").description("run the MCP server on stdio, for a coding agent to connect to").option("--account <name>", "pin the server to one account; every tool then acts on it and no other").action(async (flags) => {
3478
3499
  ran = true;
3479
- const startResendStdioServer = deps.startMcp ?? (await import("./stdio-entry-CUFTANFM.mjs")).startResendStdioServer;
3500
+ const startResendStdioServer = deps.startMcp ?? (await import("./stdio-entry-CtSxbgFr.mjs")).startResendStdioServer;
3480
3501
  const core = coreForInvocation();
3481
3502
  await startResendStdioServer({
3482
3503
  ...deps,