@agentcomms/resend 0.15.2 → 0.16.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/README.md +4 -0
- package/dist/cli.mjs +2 -2
- package/dist/index.d.mts +301 -7
- package/dist/index.mjs +202 -3
- package/dist/{send-V6GB4P5p.mjs → send-BwyJM23a.mjs} +760 -115
- package/dist/{server-CwVG8XOL.mjs → server-BFAo5PpV.mjs} +1 -1
- package/dist/{stdio-entry-DGMgTUnO.mjs → stdio-entry-BGq1Q5V_.mjs} +1 -1
- package/package.json +13 -3
package/README.md
CHANGED
|
@@ -9,6 +9,10 @@ npm install -g @agentcomms/resend # or run it with npx @agentcomms/resend <co
|
|
|
9
9
|
|
|
10
10
|
New in 0.7.0.
|
|
11
11
|
|
|
12
|
+
The local event daemon, [`@agentcomms/events-daemon`](../events-daemon/README.md), may call this package's internal
|
|
13
|
+
read-only event operation. It adds no Resend command
|
|
14
|
+
or MCP tool, and it cannot send.
|
|
15
|
+
|
|
12
16
|
## What it promises, and who enforces it
|
|
13
17
|
|
|
14
18
|
**Read-only is enforced by this package, not by the key.** Resend has no read-only API key. A full-access key can
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { A as removeAccountChange, At as updateGateAtTerminal, C as inspectKey, Ct as runUpdateCheckChild, D as policyChange, Dt as terminalUpdateHooks, E as policyApprovalRefusal, Ft as writeResult, G as SEND_LOOKUP, H as CommsError, Ht as VERSION, J as approvalsOf, K as UPDATE_CHECK_CHILD_COMMAND, Lt as RESEND_CALLER, M as showAccount, Nt as waitForApproval, O as policyReport, P as ResendContext, Pt as wholeNumber, R as checkNewName, S as addAccountChange, St as runCommand, T as modeOf, Tt as serverPruneChange, U as EXIT_CODES, V as CHANNEL_SERVERS, W as PATH_OPTIONS, X as approveRefusedHint, Y as approveChangeAtTerminal, Z as canPrompt, _ as showSentEmail, _t as refuseUnclaimedApproval, a as revokeSendApproval, at as exemptFromUpdateGate, bt as renderPrune, c as downloadReceived, ct as handoffSentence, d as listReceived, dt as kindOf, et as cliHandoffs, f as listScheduled, g as showReceived, gt as pathOverridesFromCliOptions, ht as paint, i as prepareSend, it as escapeForDisplay, j as sendPolicyOf, k as policyWanted, kt as truncateDisplay, l as getMetrics, lt as handoffSentenceToFill, m as listSuppressions, mt as openCore, n as executeSend, nt as commandPathOf, o as sendStatus, p as listSentEmails, q as agentMarker, r as finishSendApproval, rt as describeFenceReport, s as cancelScheduledChange, st as gatedChangeAtTerminal, t as beginSendApproval, tt as colorEnabled, u as listDomains, ut as installExitStatus, vt as renderApprovalWait, w as listAccounts, wt as serverInstallChange, x as runDoctor, xt as resolvePaths, yt as renderInstall } from "./send-BwyJM23a.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { createInterface } from "node:readline/promises";
|
|
5
5
|
import { stripVTControlCharacters } from "node:util";
|
|
@@ -3497,7 +3497,7 @@ is out: update first, or put it off (the stop names both commands) · 64 usage
|
|
|
3497
3497
|
}));
|
|
3498
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) => {
|
|
3499
3499
|
ran = true;
|
|
3500
|
-
const startResendStdioServer = deps.startMcp ?? (await import("./stdio-entry-
|
|
3500
|
+
const startResendStdioServer = deps.startMcp ?? (await import("./stdio-entry-BGq1Q5V_.mjs")).startResendStdioServer;
|
|
3501
3501
|
const core = coreForInvocation();
|
|
3502
3502
|
await startResendStdioServer({
|
|
3503
3503
|
...deps,
|
package/dist/index.d.mts
CHANGED
|
@@ -4322,7 +4322,7 @@ declare namespace external_d_exports {
|
|
|
4322
4322
|
export { $brand, $input, $output, BRAND, CompileOptions, GlobalMeta, INVALID, Infer$1 as Infer, IssueData, JSONType, NEVER, $RefinementCtx as RefinementCtx, SafeExtendShape, Schema, TimePrecision, TypeOf, ZodAny, ZodArray, ZodBase64, ZodBase64URL, ZodBigInt, ZodBigIntFormat, ZodBoolean, ZodCIDRv4, ZodCIDRv6, ZodCUID, ZodCUID2, ZodCatch, ZodCodec, ZodCoercedBigInt, ZodCoercedBoolean, ZodCoercedDate, ZodCoercedNumber, ZodCoercedString, ZodCompileAsyncError, ZodCompileUnsupportedError, ZodCreditCard, ZodCustom, ZodCustomStringFormat, ZodDate, ZodDefault, ZodDiscriminatedUnion, ZodE164, ZodEmail, ZodEmoji, ZodEnum, ZodError, ZodErrorMap, ZodExactOptional, ZodFile, ZodFirstPartySchemaTypes, ZodFirstPartyTypeKind, ZodFlattenedError, ZodFloat32, ZodFloat64, ZodFormattedError, ZodFunction, ZodGUID, ZodIBAN, ZodIPv4, ZodIPv6, ZodISODate, ZodISODateTime, ZodISODuration, ZodISOTime, ZodInstanceOf, ZodInt, ZodInt32, ZodInt64, ZodIntersection, ZodIssue, ZodIssueCode, ZodJSONSchema, ZodJSONSchemaInternals, ZodJWT, ZodKSUID, ZodLazy, ZodLiteral, ZodMAC, ZodMap, ZodNaN, ZodNanoID, ZodNever, ZodNonOptional, ZodNull, ZodNullable, ZodNumber, ZodNumberFormat, ZodObject, ZodOptional, ZodPipe, ZodPrefault, ZodPreprocess, ZodPromise, ZodRawShape$1 as ZodRawShape, ZodReadonly, ZodRealError, ZodRecord, ZodSafeParseError, ZodSafeParseResult, ZodSafeParseSuccess, ZodSchema, ZodSet, ZodStandardSchemaWithJSON, ZodString, ZodStringFormat, ZodSuccess, ZodSymbol, ZodTemplateLiteral, ZodTransform, ZodTuple, ZodType, ZodTypeAny, ZodUInt32, ZodUInt64, ZodULID, ZodURL, ZodUUID, ZodUndefined, ZodUnion, ZodUnknown, ZodVoid, ZodXID, ZodXor, _ZodBigInt, _ZodBoolean, _ZodDate, _ZodNumber, _ZodString, _ZodType, _default, _function, any, array, base64, base64url, bigint$1 as bigint, boolean$1 as boolean, _catch as catch, check, cidrv4, cidrv6, clone, codec, coerce_d_exports as coerce, compile, config, index_d_exports$1 as core, creditCard, cuid, cuid2, currencyCode, custom, date$2 as date, decode, decodeAsync, deepPartial, describe, discriminatedUnion, e164, email, emoji, encode, encodeAsync, _endsWith as endsWith, _enum as enum, exactOptional, file, flattenError, float32, float64, formatError, fromJSONSchema, _function as function, getDiscriminatedOption, getErrorMap, globalRegistry, _gt as gt, _gte as gte, guid, hash, hex, hostname, httpUrl, iban, _includes as includes, output$1 as infer, inferFlattenedErrors, inferFormattedError, input, _instanceof as instanceof, int, int32, int64, intersection, invertCodec, ipv4, ipv6, iso_d_exports as iso, json, jwt, keyof, ksuid, lazy, _length as length, literal, index_d_exports$2 as locales, looseObject, looseRecord, _lowercase as lowercase, _lt as lt, _lte as lte, mac, map, _maxLength as maxLength, _maxSize as maxSize, memoizer, meta, _mime as mime, _minLength as minLength, _minSize as minSize, _multipleOf as multipleOf, nan, nanoid, nativeEnum, _negative as negative, never, _nonnegative as nonnegative, nonoptional, _nonpositive as nonpositive, _normalize as normalize, _null as null, nullable, nullish, number$1 as number, object, optional, output, _overwrite as overwrite, parse, parseAsync, partialRecord, pipe, _positive as positive, prefault, preprocess, prettifyError, promise, _properties as properties, _property as property, readonly, record, refine, _regex as regex, regexes_d_exports as regexes, registry, safeDecode, safeDecodeAsync, safeEncode, safeEncodeAsync, safeParse, safeParseAsync, set, setErrorMap, _size as size, _slugify as slugify, _startsWith as startsWith, strictObject, string$1 as string, stringFormat, stringbool, success, superRefine, symbol, templateLiteral, toJSONSchema, _toLowerCase as toLowerCase, _toUpperCase as toUpperCase, toZod, transform, treeifyError, _trim as trim, tuple, uint32, uint64, ulid, _undefined as undefined, union, unknown, _uppercase as uppercase, url, util_d_exports as util, uuid, uuidv4, uuidv6, uuidv7, validate, validateAsync, _void as void, withParser, xid, xor };
|
|
4323
4323
|
}
|
|
4324
4324
|
//#endregion
|
|
4325
|
-
//#region ../core/dist/change-flow-
|
|
4325
|
+
//#region ../core/dist/change-flow-B_bGbrkO.d.mts
|
|
4326
4326
|
//#region src/config-hooks.d.ts
|
|
4327
4327
|
/**
|
|
4328
4328
|
* Test-only pauses inside `ConfigStore.convertToVersion3`, for the tests that prove a write racing the conversion is
|
|
@@ -4414,7 +4414,7 @@ interface ResolvedPaths {
|
|
|
4414
4414
|
*/
|
|
4415
4415
|
downloadsDir: string;
|
|
4416
4416
|
}
|
|
4417
|
-
type ErrorCode = "UNEXPECTED" | "APPROVAL_REQUIRED" | "APPROVAL_PENDING" | "APPROVAL_EXPIRED" | "APPROVAL_VOID" | "SEND_OUTCOME_UNKNOWN" | "SEND_REFUSED" | "POLICY_NEVER" | "RATE_CAPPED" | "UNSENDABLE_HTML" | "LOOSENING_REFUSED" | "UPDATE_REQUIRED" | "USAGE" | "CURSOR_MISMATCH" | "BAD_DATA" | "REPLY_INVALID" | "NOT_FOUND" | "PROVIDER_UNAVAILABLE" | "SECRET_STORE_UNAVAILABLE" | "TRANSIENT" | "KEYCHAIN_APPROVAL_PENDING" | "LOCK_TIMEOUT" | "AUTH_REQUIRED" | "SCOPE_MISSING" | "CONFIG";
|
|
4417
|
+
type ErrorCode = "UNEXPECTED" | "APPROVAL_REQUIRED" | "APPROVAL_PENDING" | "APPROVAL_EXPIRED" | "APPROVAL_VOID" | "SEND_OUTCOME_UNKNOWN" | "SEND_REFUSED" | "POLICY_NEVER" | "RATE_CAPPED" | "UNSENDABLE_HTML" | "LOOSENING_REFUSED" | "UPDATE_REQUIRED" | "USAGE" | "CURSOR_MISMATCH" | "BAD_DATA" | "REPLY_INVALID" | "NOT_FOUND" | "PROVIDER_UNAVAILABLE" | "SOURCE_UNAVAILABLE" | "SECRET_STORE_UNAVAILABLE" | "TRANSIENT" | "KEYCHAIN_APPROVAL_PENDING" | "LOCK_TIMEOUT" | "AUTH_REQUIRED" | "SCOPE_MISSING" | "WHATSAPP_VISIBILITY_SEAM_REQUIRED" | "CONFIG";
|
|
4418
4418
|
interface CommsErrorOptions {
|
|
4419
4419
|
hint?: string;
|
|
4420
4420
|
details?: Record<string, unknown>;
|
|
@@ -5044,6 +5044,28 @@ interface LooseningConsent {
|
|
|
5044
5044
|
* change's is the live change policy. It never changes when the configuration does.
|
|
5045
5045
|
*/
|
|
5046
5046
|
type ApprovalRoute = "chat" | "confirm";
|
|
5047
|
+
/** The four activation documents that may receive standing disclosure authority. */
|
|
5048
|
+
type DisclosureActivationKind = "rule" | "judge-budget" | "judge-kind" | "enable-all";
|
|
5049
|
+
/** An immutable version named by a standing disclosure authorisation. */
|
|
5050
|
+
type DisclosureVersionKind = "rule" | "target" | "subscriber" | "judge" | "judge-budget" | "judge-kind";
|
|
5051
|
+
interface DisclosureVersion {
|
|
5052
|
+
readonly kind: DisclosureVersionKind;
|
|
5053
|
+
readonly id: string;
|
|
5054
|
+
readonly version: number;
|
|
5055
|
+
}
|
|
5056
|
+
/**
|
|
5057
|
+
* The content-free, canonical authority for a future disclosure (local-event design D2).
|
|
5058
|
+
*
|
|
5059
|
+
* The event daemon derives this from one canonical activation document. Core does not construct
|
|
5060
|
+
* that document; it verifies the closed shape that makes an independently supplied version list
|
|
5061
|
+
* impossible to smuggle into a disclosure record.
|
|
5062
|
+
*/
|
|
5063
|
+
interface DisclosureBinding {
|
|
5064
|
+
readonly digest: string;
|
|
5065
|
+
readonly activationIntentId: string;
|
|
5066
|
+
readonly activationKind: DisclosureActivationKind;
|
|
5067
|
+
readonly versions: readonly DisclosureVersion[];
|
|
5068
|
+
}
|
|
5047
5069
|
/**
|
|
5048
5070
|
* What a record's `inboxId` names. `owner` — a mailbox or account that existed when it was made (every send and
|
|
5049
5071
|
* download, and a change to one). `prospective` — a change that creates its owner, which has no id yet. `global` — a
|
|
@@ -5175,7 +5197,10 @@ interface AuditRecord {
|
|
|
5175
5197
|
recipients?: string[];
|
|
5176
5198
|
approvalId?: string;
|
|
5177
5199
|
reason?: string;
|
|
5178
|
-
|
|
5200
|
+
/** The process that performed the work; daemon work keeps its caller separately in `origin`. */
|
|
5201
|
+
surface?: "cli" | "mcp" | "app" | "daemon";
|
|
5202
|
+
/** The requesting client surface; autonomous work is `daemon`/`daemon`. */
|
|
5203
|
+
origin?: "cli" | "mcp" | "app" | "daemon";
|
|
5179
5204
|
/**
|
|
5180
5205
|
* The policy that decided how this was approved. Written by change approvals, where it is the whole question
|
|
5181
5206
|
* afterwards: a loosening a person typed a code for and one an agent claimed after a yes in chat look the same in
|
|
@@ -5295,7 +5320,7 @@ interface CorruptStub {
|
|
|
5295
5320
|
readonly reason: UnreadableReason | IntegrityReason;
|
|
5296
5321
|
}
|
|
5297
5322
|
/** What an attribution-verified corrupt record may still show its owner: the fields its binding was made over. */
|
|
5298
|
-
interface
|
|
5323
|
+
interface OrdinarySafeFields {
|
|
5299
5324
|
readonly kind: ApprovalKind;
|
|
5300
5325
|
readonly channel: string;
|
|
5301
5326
|
readonly ownerScope: OwnerScope;
|
|
@@ -5305,6 +5330,11 @@ interface SafeFields {
|
|
|
5305
5330
|
readonly draftMessageId: string;
|
|
5306
5331
|
readonly expect: Expectation;
|
|
5307
5332
|
}
|
|
5333
|
+
/** A disclosure record has no channel owner or sender-controlled operational fields to expose. */
|
|
5334
|
+
interface DisclosureSafeFields {
|
|
5335
|
+
readonly kind: "disclosure";
|
|
5336
|
+
}
|
|
5337
|
+
type SafeFields = OrdinarySafeFields | DisclosureSafeFields;
|
|
5308
5338
|
type StoredApproval = {
|
|
5309
5339
|
readonly form: "v2";
|
|
5310
5340
|
readonly record: ApprovalRecord;
|
|
@@ -5430,7 +5460,7 @@ type ApprovalChannel = "elicitation" | "terminal";
|
|
|
5430
5460
|
* and a kind on every record, so that none can be spent as another. Without it, a person who approved a post at a
|
|
5431
5461
|
* terminal would also have approved whatever change an agent claimed under the same id.
|
|
5432
5462
|
*/
|
|
5433
|
-
type ApprovalKind = "send" | "change" | "download";
|
|
5463
|
+
type ApprovalKind = "send" | "change" | "download" | "disclosure";
|
|
5434
5464
|
/** The inbox or account a change is about. `id` is absent when the change connects it, and it does not exist yet. */
|
|
5435
5465
|
interface ChangeTarget {
|
|
5436
5466
|
kind: "inbox" | "account";
|
|
@@ -5607,6 +5637,17 @@ interface ApprovalRecord {
|
|
|
5607
5637
|
change?: ChangeBinding | undefined;
|
|
5608
5638
|
/** For a download's question: what it asked about, and the folders it offered. `contentDigest` is `downloadDigest`. */
|
|
5609
5639
|
download?: DownloadBinding | undefined;
|
|
5640
|
+
/** For a standing disclosure: the canonical, content-free event activation authority. */
|
|
5641
|
+
disclosure?: DisclosureBinding | undefined;
|
|
5642
|
+
}
|
|
5643
|
+
/**
|
|
5644
|
+
* The stored branch for standing disclosure. Its runtime decoder refuses every field belonging to send, change or
|
|
5645
|
+
* download records; this type is what the three disclosure-only methods return.
|
|
5646
|
+
*/
|
|
5647
|
+
interface DisclosureApprovalRecord extends Omit<ApprovalRecord, "kind" | "disclosure" | "approvedVia" | "channel" | "ownerScope" | "route" | "inboxId" | "inboxSub" | "draftId" | "draftMessageId" | "contentDigest" | "sendEpoch" | "policy" | "requiredPolicy" | "riskFlags" | "expect" | "approvedBindingDigest" | "sendingAt" | "sendingHeartbeatAt" | "sentAt" | "sentMessageId" | "failedAt" | "change" | "download"> {
|
|
5648
|
+
kind: "disclosure";
|
|
5649
|
+
disclosure: DisclosureBinding;
|
|
5650
|
+
approvedVia?: "terminal" | "app" | undefined;
|
|
5610
5651
|
}
|
|
5611
5652
|
interface CreateApprovalInput {
|
|
5612
5653
|
/** The manifest channel preparing the send. A channel this release does not know is refused. */
|
|
@@ -5751,6 +5792,8 @@ declare class ApprovalStore {
|
|
|
5751
5792
|
*/
|
|
5752
5793
|
ensurePruned(options?: MaintenanceOptions): Promise<MaintenanceStatus>;
|
|
5753
5794
|
create(input: CreateApprovalInput): Promise<ApprovalRecord>;
|
|
5795
|
+
/** Creates only a pending standing disclosure approval from a daemon-derived canonical binding. */
|
|
5796
|
+
createDisclosure(binding: DisclosureBinding): Promise<DisclosureApprovalRecord>;
|
|
5754
5797
|
/**
|
|
5755
5798
|
* One approval, in whichever of its four forms it is (`StoredApproval`), read against one configuration snapshot —
|
|
5756
5799
|
* or null when there is none. Nothing is written.
|
|
@@ -5793,6 +5836,21 @@ declare class ApprovalStore {
|
|
|
5793
5836
|
issueChallenge(approvalId: string, kind?: ApprovalKind, _platform?: NodeJS.Platform, options?: {
|
|
5794
5837
|
usedSaid?: UsedSaid | undefined;
|
|
5795
5838
|
}): Promise<string>;
|
|
5839
|
+
/** Issues the typed challenge that a terminal or the trusted desktop app must answer for disclosure. */
|
|
5840
|
+
issueDisclosureChallenge(approvalId: string): Promise<string>;
|
|
5841
|
+
/**
|
|
5842
|
+
* Approves one exact standing disclosure authority. MCP/chat forms are never an approval channel here: `app` means
|
|
5843
|
+
* the desktop application's trusted typed-challenge bridge, not a client form.
|
|
5844
|
+
*/
|
|
5845
|
+
approveDisclosure(approvalId: string, liveBinding: DisclosureBinding, answer: string, via: "terminal" | "app"): Promise<DisclosureApprovalRecord>;
|
|
5846
|
+
/**
|
|
5847
|
+
* Claims an approved disclosure once, persisting the exact `usedAt` that recovery must subsequently reuse.
|
|
5848
|
+
* A specialised caller may impose a shorter deadline; it is evaluated while this record's transition lock is held,
|
|
5849
|
+
* so a check made before waiting on that lock cannot accidentally consume the approval after it has expired.
|
|
5850
|
+
*/
|
|
5851
|
+
claimForDisclosure(approvalId: string, liveBinding: DisclosureBinding, options?: {
|
|
5852
|
+
readonly deadlineMs?: number | undefined;
|
|
5853
|
+
}): Promise<DisclosureApprovalRecord>;
|
|
5796
5854
|
/**
|
|
5797
5855
|
* A human approved through a confirm channel by typing the issued challenge. The draft must still be exactly what the
|
|
5798
5856
|
* record was prepared for; otherwise the record is voided, because the human would be approving content the record
|
|
@@ -6114,9 +6172,13 @@ interface TaintHandle {
|
|
|
6114
6172
|
id: string;
|
|
6115
6173
|
}
|
|
6116
6174
|
type TaintSource = "header" | "body";
|
|
6175
|
+
/** Why the content reached this process. This is separate from `TaintSource`, which remains the cap priority. */
|
|
6176
|
+
type TaintOrigin = "read" | "event";
|
|
6117
6177
|
interface TaintObservation {
|
|
6118
6178
|
address: string;
|
|
6119
6179
|
source: TaintSource;
|
|
6180
|
+
/** Old readers did not record this, so an omitted origin remains an ordinary read. */
|
|
6181
|
+
origin?: TaintOrigin | undefined;
|
|
6120
6182
|
inboxId: string;
|
|
6121
6183
|
messageId?: string | undefined;
|
|
6122
6184
|
}
|
|
@@ -6124,6 +6186,8 @@ interface TaintObservation {
|
|
|
6124
6186
|
interface TaintHandleObservation {
|
|
6125
6187
|
handle: TaintHandle;
|
|
6126
6188
|
source: TaintSource;
|
|
6189
|
+
/** Old readers did not record this, so an omitted origin remains an ordinary read. */
|
|
6190
|
+
origin?: TaintOrigin | undefined;
|
|
6127
6191
|
inboxId: string;
|
|
6128
6192
|
messageId?: string | undefined;
|
|
6129
6193
|
}
|
|
@@ -6147,6 +6211,8 @@ interface TaintAggregate {
|
|
|
6147
6211
|
readonly at: string;
|
|
6148
6212
|
readonly source: TaintSource;
|
|
6149
6213
|
readonly inboxIds: readonly string[];
|
|
6214
|
+
/** Sorted, duplicate-free provenance. A base entry with no sidecar record is a legacy read. */
|
|
6215
|
+
readonly origins: readonly TaintOrigin[];
|
|
6150
6216
|
}
|
|
6151
6217
|
interface TaintCheck {
|
|
6152
6218
|
address: boolean;
|
|
@@ -6156,10 +6222,15 @@ interface TaintCheck {
|
|
|
6156
6222
|
/** Its domain's, when that counts — not a public mailbox provider — and is in the window. */
|
|
6157
6223
|
domainSeen?: TaintAggregate | undefined;
|
|
6158
6224
|
}
|
|
6225
|
+
/** Testable interruption point for the crash-safe, two-lock sidecar protocol. */
|
|
6226
|
+
interface TaintStoreHooks {
|
|
6227
|
+
afterOriginsCommit?: (() => Promise<void>) | undefined;
|
|
6228
|
+
onLockAcquired?: ((lock: "origins" | "taint" | "handles") => void) | undefined;
|
|
6229
|
+
}
|
|
6159
6230
|
declare class TaintStore {
|
|
6160
6231
|
#private;
|
|
6161
6232
|
readonly directory: string;
|
|
6162
|
-
constructor(stateDir: string, now?: () => Date);
|
|
6233
|
+
constructor(stateDir: string, now?: () => Date, hooks?: TaintStoreHooks);
|
|
6163
6234
|
/**
|
|
6164
6235
|
* Records observations. Throws if it cannot — callers must fail the read rather than return content whose taint
|
|
6165
6236
|
* was not recorded.
|
|
@@ -6177,6 +6248,8 @@ declare class TaintStore {
|
|
|
6177
6248
|
* carries a domain: two ids sharing a workspace says nothing about either of them.
|
|
6178
6249
|
*/
|
|
6179
6250
|
checkHandle(handle: TaintHandle): Promise<boolean>;
|
|
6251
|
+
/** The merged provenance of a retained handle, or no origins when it is not in the taint window. */
|
|
6252
|
+
checkHandleOrigins(handle: TaintHandle): Promise<readonly TaintOrigin[]>;
|
|
6180
6253
|
}
|
|
6181
6254
|
//#endregion
|
|
6182
6255
|
//#region src/core.d.ts
|
|
@@ -13415,10 +13488,68 @@ type RegisteredPrompt = {
|
|
|
13415
13488
|
remove(): void;
|
|
13416
13489
|
};
|
|
13417
13490
|
//#endregion
|
|
13491
|
+
//#region src/accounts.d.ts
|
|
13492
|
+
declare const MODES: readonly ["read", "send"];
|
|
13493
|
+
type Mode = (typeof MODES)[number];
|
|
13494
|
+
/** A Resend account: core's generic record, checked, with Resend's one key of its own. */
|
|
13495
|
+
interface ResendAccount extends AccountConfig {
|
|
13496
|
+
platform: "resend";
|
|
13497
|
+
/** `read` or `send`, and nothing else: a record with another word is refused. `tier` says the same. */
|
|
13498
|
+
mode: Mode;
|
|
13499
|
+
/** For a `sending_access` key: the one domain the person said it is restricted to. Declared, not detected. */
|
|
13500
|
+
domainLock?: string | undefined;
|
|
13501
|
+
}
|
|
13502
|
+
interface NamedAccount {
|
|
13503
|
+
name: string;
|
|
13504
|
+
account: ResendAccount;
|
|
13505
|
+
}
|
|
13506
|
+
/**
|
|
13507
|
+
* Reading accounts from core's configuration as it is now. Writes go through `core.config.update`, which validates
|
|
13508
|
+
* the file and refuses any loosening the caller holds no consent for.
|
|
13509
|
+
*/
|
|
13510
|
+
declare class AccountStore {
|
|
13511
|
+
#private;
|
|
13512
|
+
constructor(load: () => Promise<Config$1>, handoffs: CliHandoffs);
|
|
13513
|
+
list(): Promise<NamedAccount[]>;
|
|
13514
|
+
find(name: string): Promise<NamedAccount | null>;
|
|
13515
|
+
findById(id: string): Promise<NamedAccount | null>;
|
|
13516
|
+
require(name: string): Promise<NamedAccount>;
|
|
13517
|
+
}
|
|
13518
|
+
//#endregion
|
|
13418
13519
|
//#region src/api/guard.d.ts
|
|
13520
|
+
/**
|
|
13521
|
+
* The one door every Resend request goes through.
|
|
13522
|
+
*
|
|
13523
|
+
* The Slack package's guard, for Resend: it sits under every operation, on the `fetch` the transport actually calls,
|
|
13524
|
+
* so a request added anywhere in this package meets it whether or not its author knew this file existed. It fails
|
|
13525
|
+
* closed in every direction:
|
|
13526
|
+
*
|
|
13527
|
+
* - **Two origins, hardcoded, and no argument for either.** `https://api.resend.com` for the API and
|
|
13528
|
+
* `https://inbound-cdn.resend.com` for a received attachment's bytes. A test reaches a fake Resend by rewriting an
|
|
13529
|
+
* already-approved URL in the *inner* fetch, after this has approved it, so there is no mode in which the check is
|
|
13530
|
+
* off.
|
|
13531
|
+
* - **A closed route table** (`routes.ts`). Anything not in it is refused, the most tempting ones with a reason.
|
|
13532
|
+
* - **Writes need a one-shot permit**, opened by the one operation that owns the route, for exactly one request.
|
|
13533
|
+
* `emails.send` must also carry the approval id as its `Idempotency-Key` and as the `agentcomms_approval` tag, so
|
|
13534
|
+
* a send that somehow reached here without going through the gate's own request builder is refused too.
|
|
13535
|
+
* - **The key never goes to the CDN.** A download carrying an `Authorization` header is refused: the signed link is
|
|
13536
|
+
* its own credential, and the API key belongs to `api.resend.com` alone.
|
|
13537
|
+
* - **No redirects.** A 30x is a second request nothing here checked, and Resend's API does not redirect.
|
|
13538
|
+
*/
|
|
13539
|
+
interface WritePermit {
|
|
13540
|
+
/**
|
|
13541
|
+
* What the open permit is for, or null when no write is allowed — which is almost always.
|
|
13542
|
+
*
|
|
13543
|
+
* For `emails.send` the approval id; for `emails.cancel` the id of the one email it may cancel.
|
|
13544
|
+
*/
|
|
13545
|
+
grant: string | null;
|
|
13546
|
+
/** The route the permit opens. A permit to cancel does not open the door to send, nor the other way round. */
|
|
13547
|
+
route: string | null;
|
|
13548
|
+
}
|
|
13419
13549
|
type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
13420
13550
|
//#endregion
|
|
13421
13551
|
//#region src/api/throttle.d.ts
|
|
13552
|
+
type ThrottlePriority = "interactive" | "background-event";
|
|
13422
13553
|
interface ThrottleOptions {
|
|
13423
13554
|
now?: () => number;
|
|
13424
13555
|
sleep?: (ms: number) => Promise<void>;
|
|
@@ -13429,6 +13560,47 @@ interface ThrottleOptions {
|
|
|
13429
13560
|
/** How long a request may wait for the lock in all: `LOCK_MAX_WAIT_MS`. Tests shorten this too. */
|
|
13430
13561
|
lockMaxWaitMs?: number;
|
|
13431
13562
|
}
|
|
13563
|
+
declare class Throttle {
|
|
13564
|
+
#private;
|
|
13565
|
+
readonly path: string;
|
|
13566
|
+
/** Takes no account on purpose: see above. Every throttle on a state directory is the same one. */
|
|
13567
|
+
constructor(stateDir: string, options?: ThrottleOptions);
|
|
13568
|
+
/** When Resend will next be asked anything from this machine, or null when it is not holding off. */
|
|
13569
|
+
blockedUntil(): Promise<string | null>;
|
|
13570
|
+
/**
|
|
13571
|
+
* Waits for this request's slot, or refuses at once while Resend has asked for none.
|
|
13572
|
+
*
|
|
13573
|
+
* The slot is reserved under the lock and waited for outside it, so a slow request does not hold every other
|
|
13574
|
+
* process's reservation. **And the hold is looked at again after the wait**: a request queued behind another is
|
|
13575
|
+
* still waiting when that one comes back 429, and the stop Resend asked for covers it as much as anything asked
|
|
13576
|
+
* later.
|
|
13577
|
+
*/
|
|
13578
|
+
before(priority?: ThrottlePriority): Promise<void>;
|
|
13579
|
+
/**
|
|
13580
|
+
* Reads what Resend said about the budget, and stops every account on this machine when it says to — whichever
|
|
13581
|
+
* account's request it answered. Returns the stop, if any.
|
|
13582
|
+
*/
|
|
13583
|
+
after(status: number, headers: Headers): Promise<number | null>;
|
|
13584
|
+
}
|
|
13585
|
+
//#endregion
|
|
13586
|
+
//#region src/api/client.d.ts
|
|
13587
|
+
/**
|
|
13588
|
+
* One Resend call: through the throttle, through the guard, with Resend's failures turned into this repository's codes
|
|
13589
|
+
* — and with the key taken out of anything that could be printed.
|
|
13590
|
+
*
|
|
13591
|
+
* The key appears in exactly one place in this package's requests, the `Authorization` header built below, and in
|
|
13592
|
+
* no message, detail or hint: every text that came back from Resend or from the network passes through `redact` on
|
|
13593
|
+
* its way into an error, in case either ever echoes it.
|
|
13594
|
+
*/
|
|
13595
|
+
interface ResendTransport {
|
|
13596
|
+
/** The inner fetch: the real one in production, a loopback fake in tests. Always wrapped by the guard. */
|
|
13597
|
+
readonly fetch?: FetchLike | undefined;
|
|
13598
|
+
readonly key: string;
|
|
13599
|
+
readonly throttle: Throttle;
|
|
13600
|
+
/** Closed except inside the one operation that opens it for one request. */
|
|
13601
|
+
readonly permit?: WritePermit | undefined;
|
|
13602
|
+
readonly timeoutMs?: number | undefined;
|
|
13603
|
+
}
|
|
13432
13604
|
//#endregion
|
|
13433
13605
|
//#region src/context.d.ts
|
|
13434
13606
|
/**
|
|
@@ -13453,6 +13625,43 @@ interface ResendContextOptions {
|
|
|
13453
13625
|
/** How a throttled send waits (design 2026-10-08 §R2): core's pacing; a test injects one on the throttle's clock. */
|
|
13454
13626
|
sendPacing?: (() => SendPacing) | undefined;
|
|
13455
13627
|
}
|
|
13628
|
+
declare class ResendContext {
|
|
13629
|
+
#private;
|
|
13630
|
+
readonly core: Core;
|
|
13631
|
+
readonly env: NodeJS.ProcessEnv;
|
|
13632
|
+
readonly now: () => Date;
|
|
13633
|
+
readonly platform: NodeJS.Platform;
|
|
13634
|
+
readonly surface: "cli" | "mcp";
|
|
13635
|
+
/**
|
|
13636
|
+
* What every result, refusal and hint names for a person to run: Resend's own CLI as this installation runs it, or
|
|
13637
|
+
* core's through its installed dependency (`own`, `core`), quoted for `platform` — or why there is none here.
|
|
13638
|
+
*/
|
|
13639
|
+
readonly handoffs: CliHandoffs;
|
|
13640
|
+
readonly accounts: AccountStore;
|
|
13641
|
+
/** A fresh pacing for each send: how a throttled one waits before it tries again. */
|
|
13642
|
+
readonly sendPacing: () => SendPacing;
|
|
13643
|
+
constructor(options?: ResendContextOptions);
|
|
13644
|
+
config(): Promise<Config$1>;
|
|
13645
|
+
/** The secret store this configuration chose, or the keychain before anything has been stored. */
|
|
13646
|
+
secrets(): Promise<SecretStore>;
|
|
13647
|
+
/** The machine's default send policy, which an account without one of its own inherits. */
|
|
13648
|
+
defaultSendPolicy(): Promise<SendPolicy>;
|
|
13649
|
+
/**
|
|
13650
|
+
* The machine's one Resend throttle. Not one per account: Resend's budget is the team's, and nothing here can tell
|
|
13651
|
+
* which accounts share a team, so every account — and a key being checked before it is stored — shares one.
|
|
13652
|
+
*/
|
|
13653
|
+
throttle(): Throttle;
|
|
13654
|
+
/**
|
|
13655
|
+
* The team's verified domains for an account, from `load` at most every five minutes in this process: received mail
|
|
13656
|
+
* is read a page at a time, and asking Resend for the same list before every page would spend the team's budget on
|
|
13657
|
+
* an answer that changes when a person adds a domain.
|
|
13658
|
+
*/
|
|
13659
|
+
teamDomains(accountId: string, load: () => Promise<readonly string[]>): Promise<readonly string[]>;
|
|
13660
|
+
/** A transport for a key that is not stored yet — `account add` checking what it was given. */
|
|
13661
|
+
transportForKey(key: string): ResendTransport;
|
|
13662
|
+
/** A transport for a connected account: its key from the secret store, the machine's throttle, and any open permit. */
|
|
13663
|
+
transport(named: NamedAccount, permit?: WritePermit): Promise<ResendTransport>;
|
|
13664
|
+
}
|
|
13456
13665
|
//#endregion
|
|
13457
13666
|
//#region src/mcp/server.d.ts
|
|
13458
13667
|
/**
|
|
@@ -13481,4 +13690,89 @@ interface ResendMcpServer {
|
|
|
13481
13690
|
}
|
|
13482
13691
|
export declare function createResendMcpServer(options?: ResendMcpOptions): Promise<ResendMcpServer>;
|
|
13483
13692
|
//#endregion
|
|
13484
|
-
|
|
13693
|
+
//#region src/operations/events.d.ts
|
|
13694
|
+
interface ResendEventReceivedListItem {
|
|
13695
|
+
readonly id: string;
|
|
13696
|
+
}
|
|
13697
|
+
interface ResendEventAddress {
|
|
13698
|
+
readonly address: string;
|
|
13699
|
+
readonly name: string | null;
|
|
13700
|
+
}
|
|
13701
|
+
interface ResendEventReceivedAttachment {
|
|
13702
|
+
readonly id: string;
|
|
13703
|
+
readonly filename: string;
|
|
13704
|
+
readonly riskFlags: readonly string[];
|
|
13705
|
+
readonly contentType: string | null;
|
|
13706
|
+
readonly size: number | null;
|
|
13707
|
+
readonly inline: boolean;
|
|
13708
|
+
}
|
|
13709
|
+
interface ResendEventAuthentication {
|
|
13710
|
+
readonly spf: string | null;
|
|
13711
|
+
readonly dkim: string | null;
|
|
13712
|
+
readonly dmarc: string | null;
|
|
13713
|
+
readonly evaluatedBy: "resend" | null;
|
|
13714
|
+
}
|
|
13715
|
+
interface ResendEventReceivedCandidate {
|
|
13716
|
+
readonly emailId: string;
|
|
13717
|
+
readonly receivedAt: string;
|
|
13718
|
+
readonly subject: string;
|
|
13719
|
+
readonly body?: string | undefined;
|
|
13720
|
+
readonly bodyTruncated?: boolean | undefined;
|
|
13721
|
+
readonly attachments: readonly ResendEventReceivedAttachment[];
|
|
13722
|
+
readonly attachmentCount: number;
|
|
13723
|
+
readonly from: ResendEventAddress | null;
|
|
13724
|
+
readonly replyTo: readonly ResendEventAddress[];
|
|
13725
|
+
readonly to: readonly string[];
|
|
13726
|
+
readonly cc: readonly string[];
|
|
13727
|
+
readonly receivedFor: readonly string[];
|
|
13728
|
+
readonly messageId: string | null;
|
|
13729
|
+
readonly authentication: ResendEventAuthentication;
|
|
13730
|
+
}
|
|
13731
|
+
interface ResendEventSentItem {
|
|
13732
|
+
readonly id: string;
|
|
13733
|
+
readonly lastEvent: string;
|
|
13734
|
+
readonly from: ResendEventAddress | null;
|
|
13735
|
+
readonly to: readonly string[];
|
|
13736
|
+
readonly cc: readonly string[];
|
|
13737
|
+
readonly bcc: readonly string[];
|
|
13738
|
+
readonly subject: string;
|
|
13739
|
+
readonly createdAt: string | null;
|
|
13740
|
+
readonly scheduledAt: string | null;
|
|
13741
|
+
readonly messageId: string | null;
|
|
13742
|
+
}
|
|
13743
|
+
interface ResendEventReader {
|
|
13744
|
+
listReceived(after?: string): Promise<Readonly<{
|
|
13745
|
+
emails: readonly ResendEventReceivedListItem[];
|
|
13746
|
+
next: string | null;
|
|
13747
|
+
}>>;
|
|
13748
|
+
getReceived(id: string): Promise<Readonly<{
|
|
13749
|
+
kind: "candidate";
|
|
13750
|
+
candidate: ResendEventReceivedCandidate;
|
|
13751
|
+
}> | Readonly<{
|
|
13752
|
+
kind: "vanished";
|
|
13753
|
+
}>>;
|
|
13754
|
+
listSent(after?: string): Promise<Readonly<{
|
|
13755
|
+
emails: readonly ResendEventSentItem[];
|
|
13756
|
+
next: string | null;
|
|
13757
|
+
}>>;
|
|
13758
|
+
}
|
|
13759
|
+
/**
|
|
13760
|
+
* Converts the already-capped body produced by the Resend reader into the
|
|
13761
|
+
* event representation. `bodyTruncated` is that reader's fact: this layer
|
|
13762
|
+
* must not recompute it from Unicode code points.
|
|
13763
|
+
*/
|
|
13764
|
+
export declare function normaliseResendEventBody(value: string, bodyTruncated: boolean): Readonly<{
|
|
13765
|
+
body: string;
|
|
13766
|
+
bodyTruncated: boolean;
|
|
13767
|
+
}>;
|
|
13768
|
+
/** A structural read adapter: no permit, no sender, and each request is labelled background-event. */
|
|
13769
|
+
export declare function createResendEventReader(context: ResendContext, account: string, accountId?: string): ResendEventReader;
|
|
13770
|
+
/** Opens Resend's guarded, read-only context for the held event owner. */
|
|
13771
|
+
export declare function createResendEventReaderForPaths(input: Readonly<{
|
|
13772
|
+
account: string;
|
|
13773
|
+
/** A daemon binding: resolve every request by this stable id, never by its presentation alias. */
|
|
13774
|
+
accountId?: string | undefined;
|
|
13775
|
+
pathOverrides: PathOverrides;
|
|
13776
|
+
}>): ResendEventReader;
|
|
13777
|
+
//#endregion
|
|
13778
|
+
export type { ResendEventAddress, ResendEventAuthentication, ResendEventReader, ResendEventReceivedAttachment, ResendEventReceivedCandidate, ResendEventReceivedListItem, ResendEventSentItem, ResendMcpOptions, ResendMcpServer, Route, RouteKind };
|