@pithy-sh/support 0.6.2 → 0.7.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 +2 -0
- package/dist/capability.d.ts +1 -1
- package/dist/capability.d.ts.map +1 -1
- package/dist/capability.js +5 -4
- package/dist/config/config.d.ts +7 -5
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +6 -5
- package/dist/http/handlers.d.ts.map +1 -1
- package/dist/http/handlers.js +2 -1
- package/dist/http/responses.d.ts +5 -0
- package/dist/http/responses.d.ts.map +1 -1
- package/dist/http/responses.js +3 -2
- package/dist/http/schemas.d.ts.map +1 -1
- package/dist/http/schemas.js +1 -1
- package/dist/provision/provisionSupport.d.ts +19 -20
- package/dist/provision/provisionSupport.d.ts.map +1 -1
- package/dist/provision/provisionSupport.js +14 -17
- package/dist/provision/sendingCheck.d.ts +7 -0
- package/dist/provision/sendingCheck.d.ts.map +1 -0
- package/dist/provision/sendingCheck.js +63 -0
- package/dist/reply/fromName.d.ts +20 -0
- package/dist/reply/fromName.d.ts.map +1 -0
- package/dist/reply/fromName.js +29 -0
- package/dist/reply/send.d.ts +10 -1
- package/dist/reply/send.d.ts.map +1 -1
- package/dist/reply/send.js +16 -2
- package/dist/reply/sender.d.ts +16 -0
- package/dist/reply/sender.d.ts.map +1 -0
- package/dist/reply/sender.js +27 -0
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/package.json +8 -8
- package/pithy.manifest.json +1 -1
- package/src/capability.ts +18 -15
- package/src/config/config.ts +13 -5
- package/src/http/handlers.ts +1 -0
- package/src/http/responses.ts +9 -1
- package/src/http/schemas.ts +4 -1
- package/src/provision/provisionSupport.ts +20 -21
- package/src/provision/sendingCheck.ts +69 -0
- package/src/reply/fromName.ts +37 -0
- package/src/reply/send.ts +31 -3
- package/src/reply/sender.ts +35 -0
- package/src/version.generated.ts +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ pithy add support
|
|
|
14
14
|
|
|
15
15
|
**Search, by words and by meaning.** `GET /support/threads/search` takes `q`, a `mode` (`keyword`, `semantic` or `hybrid`), a `limit` (20, at most 50) and the inbox list's filters, and returns ranked threads, each named by its best-matching message with that message's raw `bm25` and similarity. Keyword is FTS5 with `search.fts` on and the `LIKE` scan otherwise; hybrid fuses it with semantic search by rank, so a paraphrase is found and an order number still comes first. The inbox list no longer takes `q`. **Semantic search costs a second inference per inbound message on your own Workers AI binding**, on top of classification — roughly double the per-message AI cost — so `search: { semantic: true }` is off until you turn it on. `pithy support provision` creates the Vectorize index and backfills it once, and `pithy vector reprocess --index support-messages` re-embeds after a model change, with no `vector()` composed. Vectors carry message ids only. See [`docs/commands/support.md`](../../docs/commands/support.md#search).
|
|
16
16
|
|
|
17
|
+
**Replies come from the inbox.** A mailed reply is sent from the address the customer wrote to, under the name of whoever answered: `"Jim Scott" <support@help.example.com>`, with that inbox as the `Reply-To`. `reply.fromName` is the display name, with `{agent}` replaced by the agent's name (default `{agent}`); with no agent name, the email capability's `fromName` is used. A fixed `reply.fromName` with no `{agent}` is used for every reply. Support declares the addresses it sends as, `inboundAddresses` and `reply.replyToAddress`, through `email().enqueueAs` (`@pithy-sh/email` ^0.9.0; an older one is refused at compose). A thread whose inbox is no longer claimed is answered from `reply.replyToAddress`, or refused when none is set. A domain Email Service cannot send from fails the job with the domain named, and nothing is sent from another address in its place. `pithy doctor` names each sending domain, inbound or reply-to, that cannot send.
|
|
18
|
+
|
|
17
19
|
_Everything else is on the site. `pithy.sh/docs` is canonical — new prose goes there, not here._
|
|
18
20
|
|
|
19
21
|
## A person's data
|
package/dist/capability.d.ts
CHANGED
|
@@ -46,7 +46,7 @@ export interface SupportWiring {
|
|
|
46
46
|
enqueueEmail: EmailCapability["enqueue"] | undefined;
|
|
47
47
|
/**
|
|
48
48
|
* The optional peers the sender link reads through — `auth()`'s and `payments()`'s surfaces, set by
|
|
49
|
-
* `compose` when each is composed. Found, never imported
|
|
49
|
+
* `compose` when each is composed. Found, never imported: see `link/sender.ts`.
|
|
50
50
|
*/
|
|
51
51
|
peers: SenderPeers;
|
|
52
52
|
/**
|
package/dist/capability.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAGnE,OAAO,EAAE,KAAK,UAAU,EAAoB,MAAM,0CAA0C,CAAC;AAC7F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAE3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAGnE,OAAO,EAAE,KAAK,UAAU,EAAoB,MAAM,0CAA0C,CAAC;AAC7F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAE3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGtE,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EAGxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAO9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAMjD,OAAO,EAAkB,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAW7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,+FAA+F;AAC/F,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG;IAChD,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,8BAA8B;IAC9B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B;;;;;OAKG;IACH,YAAY,EAAE,eAAe,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;IACrD;;;OAGG;IACH,KAAK,EAAE,WAAW,CAAC;IACnB;;;;;;;;OAQG;IACH,IAAI,EAAE,SAAS,CAAC;CACjB;AA4ED;;;;;GAKG;AACH,MAAM,WAAW,iBACf,SAAQ,UAAU,CAAC,eAAe,EAAE,kBAAkB,EAAE,SAAS,EAAE,OAAO,gBAAgB,CAAC;IAC3F,mCAAmC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,wGAAwG;IACxG,UAAU,EAAE,iBAAiB,CAAC;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,iBAAiB,CAyIvE;AAED,2GAA2G;AAC3G,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,IAAI,iBAAiB,CAE3F"}
|
package/dist/capability.js
CHANGED
|
@@ -15,13 +15,14 @@ import { createSupportEmailHandler } from "./inbound/handler.js";
|
|
|
15
15
|
import { senderPeers } from "./link/peers.js";
|
|
16
16
|
import { SUPPORT_MIGRATIONS } from "./migrations/set.js";
|
|
17
17
|
import { supportPresence } from "./presence/room.js";
|
|
18
|
+
import { supportSendingSettings } from "./provision/sendingCheck.js";
|
|
18
19
|
import { supportUserPurge } from "./purge/contribution.js";
|
|
20
|
+
import { replySender } from "./reply/sender.js";
|
|
19
21
|
import { supportExampleSeed } from "./seeds/example.js";
|
|
20
22
|
import { PACKAGE_NAME, PACKAGE_VERSION } from "./version.generated.js";
|
|
21
23
|
import { noopEmit } from "@pithy-sh/core/src/audit/recorder";
|
|
22
24
|
import { defineCapability } from "@pithy-sh/core/src/capability/capability";
|
|
23
25
|
import { workflowBindings } from "@pithy-sh/core/src/workflow/bindings";
|
|
24
|
-
import { isEmailCapability } from "@pithy-sh/email/src/capability";
|
|
25
26
|
//#region src/capability.ts
|
|
26
27
|
/**
|
|
27
28
|
* Sort order of the support migrations within the app database, relative to other capabilities.
|
|
@@ -164,7 +165,8 @@ function support(options = {}) {
|
|
|
164
165
|
inapplicableSecrets: inapplicableAttachmentSecrets(resolved),
|
|
165
166
|
workflows: supportWorkflows,
|
|
166
167
|
requiredBindings,
|
|
167
|
-
|
|
168
|
+
inboundRouting: { addresses: resolved.inboundAddresses },
|
|
169
|
+
settings: supportSendingSettings(resolved),
|
|
168
170
|
databases: { app: {
|
|
169
171
|
binding: "DB",
|
|
170
172
|
tables: supportTables,
|
|
@@ -182,8 +184,7 @@ function support(options = {}) {
|
|
|
182
184
|
...resolved.staff ? { powers: [resolved.staff.readPower, resolved.staff.assignPower] } : {},
|
|
183
185
|
compose: ({ capabilities }) => {
|
|
184
186
|
assertStaffComposition(resolved.staff, capabilities);
|
|
185
|
-
|
|
186
|
-
wiring.enqueueEmail = email?.enqueue;
|
|
187
|
+
wiring.enqueueEmail = replySender(capabilities, resolved);
|
|
187
188
|
wiring.peers = senderPeers(capabilities, resolved);
|
|
188
189
|
},
|
|
189
190
|
client: () => clientProjection(resolved, mountPath),
|
package/dist/config/config.d.ts
CHANGED
|
@@ -128,6 +128,7 @@ export type SupportSubmissionConfig = z.output<typeof SupportSubmissionConfig>;
|
|
|
128
128
|
export declare const SupportReplyConfig: z.ZodObject<{
|
|
129
129
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
130
130
|
replyToAddress: z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<string | undefined, string | undefined>>;
|
|
131
|
+
fromName: z.ZodDefault<z.ZodString>;
|
|
131
132
|
deliverInApp: z.ZodDefault<z.ZodBoolean>;
|
|
132
133
|
snippets: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
133
134
|
label: z.ZodString;
|
|
@@ -179,7 +180,7 @@ export type SupportSearchConfig = z.output<typeof SupportSearchConfig>;
|
|
|
179
180
|
export declare function supportSemantic(search: SupportSearchConfig): SupportSemanticConfig | undefined;
|
|
180
181
|
/** The full support configuration. */
|
|
181
182
|
/**
|
|
182
|
-
* **Who of the app's own signed-in users works the inbox
|
|
183
|
+
* **Who of the app's own signed-in users works the inbox.** Off until named.
|
|
183
184
|
*
|
|
184
185
|
* The inbox holds every customer's correspondence, so a signed-in user reaches it only through a power the
|
|
185
186
|
* adopter names in their own role catalog, held in the organization the request acts for — the same seam
|
|
@@ -238,6 +239,7 @@ export declare const SupportConfig: z.ZodObject<{
|
|
|
238
239
|
reply: z.ZodPrefault<z.ZodObject<{
|
|
239
240
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
240
241
|
replyToAddress: z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<string | undefined, string | undefined>>;
|
|
242
|
+
fromName: z.ZodDefault<z.ZodString>;
|
|
241
243
|
deliverInApp: z.ZodDefault<z.ZodBoolean>;
|
|
242
244
|
snippets: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
243
245
|
label: z.ZodString;
|
|
@@ -269,10 +271,10 @@ export type SupportConfigInput = z.input<typeof SupportConfig>;
|
|
|
269
271
|
* All three default `true`, so the ordinary project is unaffected by this predicate existing.
|
|
270
272
|
*
|
|
271
273
|
* Two callers, and they must agree: `capability.ts` declares the binding only when this is true, and
|
|
272
|
-
* `pithy support provision` creates the bucket only when this is true.
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
274
|
+
* `pithy support provision` creates the bucket only when this is true. Were they to ask different
|
|
275
|
+
* questions, a project that wanted uploads but no mail attachments would get a binding pointing at a
|
|
276
|
+
* bucket nothing had created, and every submitted file would be dropped with a warning. One predicate is
|
|
277
|
+
* what makes that unrepresentable.
|
|
276
278
|
*/
|
|
277
279
|
export declare function supportNeedsBucket(config: SupportConfig): boolean;
|
|
278
280
|
//# sourceMappingURL=config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;GAUG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,mCAAmC,CAAC;AAEvE,0DAA0D;AAC1D,eAAO,MAAM,eAAe;;;;;iBA+BkF,CAAC;AAC/G,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAE/D,+DAA+D;AAC/D,eAAO,MAAM,wBAAwB;;;;;iBA6BkF,CAAC;AACxH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEjF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB;;;;;;;iBA8C8D,CAAC;AAC9F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE;;;;;;;;;;;;;;;GAeG;AACH;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C,eAAO,MAAM,kCAAkC;;;;;iBAkC5C,CAAC;AACJ,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAErG;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,2FAA2F;AAC3F,eAAO,MAAM,yBAAyB,SAAU,CAAC;AAEjD;;;;;;;;;;;;;GAaG;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;iBAwCjC,CAAC;AACJ,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE/E,uCAAuC;AACvC,eAAO,MAAM,kBAAkB
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;GAUG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,mCAAmC,CAAC;AAEvE,0DAA0D;AAC1D,eAAO,MAAM,eAAe;;;;;iBA+BkF,CAAC;AAC/G,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAE/D,+DAA+D;AAC/D,eAAO,MAAM,wBAAwB;;;;;iBA6BkF,CAAC;AACxH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEjF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB;;;;;;;iBA8C8D,CAAC;AAC9F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE;;;;;;;;;;;;;;;GAeG;AACH;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C,eAAO,MAAM,kCAAkC;;;;;iBAkC5C,CAAC;AACJ,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAErG;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,2FAA2F;AAC3F,eAAO,MAAM,yBAAyB,SAAU,CAAC;AAEjD;;;;;;;;;;;;;GAaG;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;iBAwCjC,CAAC;AACJ,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE/E,uCAAuC;AACvC,eAAO,MAAM,kBAAkB;;;;;;;;;;iBA2C5B,CAAC;AACJ,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,oBAAoB,CAAC;AAEzD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,oGAAoG;AACpG,eAAO,MAAM,qBAAqB;;;iBAmB2B,CAAC;AAC9D,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE3E;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;;;;;;iBAiB7B,CAAC;AACJ,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEvE;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,qBAAqB,GAAG,SAAS,CAI9F;AAED,sCAAsC;AACtC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,kBAAkB;;;iBAiB5B,CAAC;AACJ,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoCvB,CAAC;AACJ,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC;AAC3D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE/D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAEjE"}
|
package/dist/config/config.js
CHANGED
|
@@ -128,6 +128,7 @@ const SupportSubmissionConfig = z.object({
|
|
|
128
128
|
const SupportReplyConfig = z.object({
|
|
129
129
|
enabled: z.boolean().default(true).describe("Allow replies from the control-plane route. Off makes the inbox read-only, which is a reasonable setting for a project that answers support from its own mail client."),
|
|
130
130
|
replyToAddress: z.string().optional().transform((address) => address === void 0 ? void 0 : parseAddress(address) ?? normalizeAddress(address)).describe("The `Reply-To` a reply carries — the address the customer's answer comes back to, which must be one of `inboundAddresses` or the conversation ends there. Defaults to the inbox address the thread arrived on, which is almost always what you want."),
|
|
131
|
+
fromName: z.string().min(1).max(120).default("{agent}").describe("The display name a mailed reply is sent under, with `{agent}` replaced by the answering agent's name: `{agent} at Acme` sends `\"Jim Scott at Acme\" <support@help.example.com>`. The address is the inbox the thread arrived at, or `reply.replyToAddress` for a thread whose inbox is not in `inboundAddresses` or that arrived at none. With no agent name the email capability's `fromName` is used instead; a name with no `{agent}`, such as `Acme Support`, is used for every reply."),
|
|
131
132
|
deliverInApp: z.boolean().default(false).describe("Answer an `app` thread by storing the reply for the submitter to read in the app, instead of mailing it. **A choice, not a fallback.** Turning on Email Routing takes over the zone's MX, so a project already running mail on that domain cannot receive support replies without consequences everywhere else on it — and for a submitter who is a signed-in user sitting on the screen they wrote from, the answer is better placed there anyway. In-app delivery also happens automatically when there is no address to reply from and no email capability to send with, whatever this says; the setting is what makes it reachable for a project whose mail works fine. It never applies to an `email` thread: a mail thread's sender has no read-back, so a stored answer there is one nobody would ever see."),
|
|
132
133
|
snippets: z.record(z.string(), SupportReplySnippet).default({}).describe("Your own canned replies, merged over the ones Pithy ships and winning on a key collision. These are *starting points a human picks and edits* in the dashboard, not automatic replies — nothing here is ever sent without somebody pressing send. Author them with `defineSupportReplies` so a malformed one fails where it is written.")
|
|
133
134
|
}).describe("Reply settings — whether replies are allowed, where the answer goes, and what to offer as a starting point.");
|
|
@@ -176,7 +177,7 @@ function supportSemantic(search) {
|
|
|
176
177
|
}
|
|
177
178
|
/** The full support configuration. */
|
|
178
179
|
/**
|
|
179
|
-
* **Who of the app's own signed-in users works the inbox
|
|
180
|
+
* **Who of the app's own signed-in users works the inbox.** Off until named.
|
|
180
181
|
*
|
|
181
182
|
* The inbox holds every customer's correspondence, so a signed-in user reaches it only through a power the
|
|
182
183
|
* adopter names in their own role catalog, held in the organization the request acts for — the same seam
|
|
@@ -217,10 +218,10 @@ const SupportConfig = z.object({
|
|
|
217
218
|
* All three default `true`, so the ordinary project is unaffected by this predicate existing.
|
|
218
219
|
*
|
|
219
220
|
* Two callers, and they must agree: `capability.ts` declares the binding only when this is true, and
|
|
220
|
-
* `pithy support provision` creates the bucket only when this is true.
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
221
|
+
* `pithy support provision` creates the bucket only when this is true. Were they to ask different
|
|
222
|
+
* questions, a project that wanted uploads but no mail attachments would get a binding pointing at a
|
|
223
|
+
* bucket nothing had created, and every submitted file would be dropped with a warning. One predicate is
|
|
224
|
+
* what makes that unrepresentable.
|
|
224
225
|
*/
|
|
225
226
|
function supportNeedsBucket(config) {
|
|
226
227
|
return config.attachments.enabled || config.attachments.retainRaw || config.submission.attachments.enabled;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/http/handlers.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/http/handlers.ts"],"names":[],"mappings":"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"}
|
package/dist/http/handlers.js
CHANGED
package/dist/http/responses.d.ts
CHANGED
|
@@ -138,6 +138,7 @@ export declare const SupportAttachmentView: z.ZodObject<{
|
|
|
138
138
|
sha256: z.ZodString;
|
|
139
139
|
inline: z.ZodBoolean;
|
|
140
140
|
url: z.ZodNullable<z.ZodString>;
|
|
141
|
+
messageId: z.ZodOptional<z.ZodString>;
|
|
141
142
|
}, z.core.$strip>;
|
|
142
143
|
export type SupportAttachmentView = z.output<typeof SupportAttachmentView>;
|
|
143
144
|
/** One of the sender's purchases, flattened to what a support console renders. */
|
|
@@ -503,6 +504,7 @@ export declare const SupportThreadResponse: z.ZodObject<{
|
|
|
503
504
|
sha256: z.ZodString;
|
|
504
505
|
inline: z.ZodBoolean;
|
|
505
506
|
url: z.ZodNullable<z.ZodString>;
|
|
507
|
+
messageId: z.ZodOptional<z.ZodString>;
|
|
506
508
|
}, z.core.$strip>>;
|
|
507
509
|
sender: z.ZodObject<{
|
|
508
510
|
authenticated: z.ZodBoolean;
|
|
@@ -953,6 +955,7 @@ export declare const SupportThreadResponseAsRead: z.ZodObject<{
|
|
|
953
955
|
sha256: z.ZodString;
|
|
954
956
|
inline: z.ZodBoolean;
|
|
955
957
|
url: z.ZodNullable<z.ZodString>;
|
|
958
|
+
messageId: z.ZodOptional<z.ZodString>;
|
|
956
959
|
}, z.core.$strip>>;
|
|
957
960
|
sender: z.ZodObject<{
|
|
958
961
|
authenticated: z.ZodBoolean;
|
|
@@ -1111,6 +1114,7 @@ export declare const SupportMyMessageView: z.ZodObject<{
|
|
|
1111
1114
|
sha256: z.ZodString;
|
|
1112
1115
|
inline: z.ZodBoolean;
|
|
1113
1116
|
url: z.ZodNullable<z.ZodString>;
|
|
1117
|
+
messageId: z.ZodOptional<z.ZodString>;
|
|
1114
1118
|
}, z.core.$strip>>;
|
|
1115
1119
|
sentAt: z.ZodISODateTime;
|
|
1116
1120
|
}, z.core.$strip>;
|
|
@@ -1168,6 +1172,7 @@ export declare const SupportMyThreadResponse: z.ZodObject<{
|
|
|
1168
1172
|
sha256: z.ZodString;
|
|
1169
1173
|
inline: z.ZodBoolean;
|
|
1170
1174
|
url: z.ZodNullable<z.ZodString>;
|
|
1175
|
+
messageId: z.ZodOptional<z.ZodString>;
|
|
1171
1176
|
}, z.core.$strip>>;
|
|
1172
1177
|
sentAt: z.ZodISODateTime;
|
|
1173
1178
|
}, z.core.$strip>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"responses.d.ts","sourceRoot":"","sources":["../../src/http/responses.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmDxB,mEAAmE;AACnE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0F0C,CAAC;AACzE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAEnE;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAGkE,CAAC;AACvG,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE/E,qCAAqC;AACrC,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;iBAqCoD,CAAC;AACpF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE,+EAA+E;AAC/E,eAAO,MAAM,qBAAqB
|
|
1
|
+
{"version":3,"file":"responses.d.ts","sourceRoot":"","sources":["../../src/http/responses.ts"],"names":[],"mappings":"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"}
|
package/dist/http/responses.js
CHANGED
|
@@ -104,7 +104,8 @@ const SupportAttachmentView = z.object({
|
|
|
104
104
|
size: z.number().int().describe("The size in bytes."),
|
|
105
105
|
sha256: z.string().describe("The content digest, so a client can tell two identical attachments apart from one."),
|
|
106
106
|
inline: z.boolean().describe("Whether it was referenced inline in the HTML body rather than attached."),
|
|
107
|
-
url: z.string().nullable().describe("A signed URL valid for a few minutes, or null when no credentials were available to sign one. The `storageKey` is never here: it is server-derived precisely so a client cannot name an object or guess the one beside it.")
|
|
107
|
+
url: z.string().nullable().describe("A signed URL valid for a few minutes, or null when no credentials were available to sign one. The `storageKey` is never here: it is server-derived precisely so a client cannot name an object or guess the one beside it."),
|
|
108
|
+
messageId: z.string().optional().describe("The id of the message it arrived on, so a client can show each file under the message that carried it. Absent from a Worker that predates it.")
|
|
108
109
|
}).describe("One attachment as a client receives it — metadata and a signed URL, never the storage key.");
|
|
109
110
|
/** One of the sender's purchases, flattened to what a support console renders. */
|
|
110
111
|
const SenderPurchaseView = z.object({
|
|
@@ -218,7 +219,7 @@ const SupportAssignmentView = z.object({
|
|
|
218
219
|
const SupportThreadResponse = z.object({
|
|
219
220
|
thread: SupportThreadView.describe("The conversation."),
|
|
220
221
|
messages: z.array(SupportMessageView).describe("Its messages, oldest first — a conversation reads downward."),
|
|
221
|
-
attachments: z.array(SupportAttachmentView).describe("Its attachments, across every message
|
|
222
|
+
attachments: z.array(SupportAttachmentView).describe("Its attachments, across every message, each naming the message it arrived on in `messageId`."),
|
|
222
223
|
sender: SenderContextView.describe("What the app already knows about the sender."),
|
|
223
224
|
replies: z.array(SupportReplyView).describe("The canned replies, ordered for this thread's category."),
|
|
224
225
|
assignments: z.array(SupportAssignmentView).optional().describe("Every change of the assignee, oldest first. Empty when nobody was ever assigned; absent from a Worker that predates assignment.")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/http/schemas.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB;;;;;;;;;;;GAWG;AAEH,+BAA+B;AAC/B,eAAO,MAAM,aAAa;;iBAOsC,CAAC;AACjE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAyC1D;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;kBAiBmE,CAAC;AACjG,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,yBAAyB;AACzB,eAAO,MAAM,iBAAiB;;;;EAI3B,CAAC;AACJ,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,wBAAwB;AACxB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAuBqF,CAAC;AACrH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,kCAAkC;AAClC,eAAO,MAAM,kBAAkB;;iBAQuB,CAAC;AACvD,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,mCAAmC;AACnC,eAAO,MAAM,aAAa;;;iBAOoE,CAAC;AAC/F,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D,yDAAyD;AACzD,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,iGAAiG;AACjG,eAAO,MAAM,iBAAiB;;;;;;iBAeiC,CAAC;AAChE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,sBAAsB;AACtB,eAAO,MAAM,UAAU;;;
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/http/schemas.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB;;;;;;;;;;;GAWG;AAEH,+BAA+B;AAC/B,eAAO,MAAM,aAAa;;iBAOsC,CAAC;AACjE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAyC1D;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;kBAiBmE,CAAC;AACjG,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,yBAAyB;AACzB,eAAO,MAAM,iBAAiB;;;;EAI3B,CAAC;AACJ,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,wBAAwB;AACxB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAuBqF,CAAC;AACrH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,kCAAkC;AAClC,eAAO,MAAM,kBAAkB;;iBAQuB,CAAC;AACvD,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,mCAAmC;AACnC,eAAO,MAAM,aAAa;;;iBAOoE,CAAC;AAC/F,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D,yDAAyD;AACzD,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,iGAAiG;AACjG,eAAO,MAAM,iBAAiB;;;;;;iBAeiC,CAAC;AAChE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,sBAAsB;AACtB,eAAO,MAAM,UAAU;;;iBAmB0B,CAAC;AAClD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,iCAAiC;AACjC,eAAO,MAAM,UAAU;;;iBAWmC,CAAC;AAC3D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB;;;;iBA4B8D,CAAC;AACpG,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEhF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;iBAiCqF,CAAC;AACtH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,uCAAuC;AACvC,eAAO,MAAM,cAAc;;;iBAiBxB,CAAC;AACJ,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,sCAAsC;AACtC,eAAO,MAAM,YAAY;;iBAM4B,CAAC;AACtD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
|
package/dist/http/schemas.js
CHANGED
|
@@ -79,7 +79,7 @@ const AssignThreadInput = z.object({
|
|
|
79
79
|
/** The reply body. */
|
|
80
80
|
const ReplyInput = z.object({
|
|
81
81
|
body: z.string().min(1).max(5e4).describe("The reply text, as a human wrote and edited it. Rendered HTML-escaped into the adopter's email shell — it is prose, never markup."),
|
|
82
|
-
agentName: z.string().min(1).max(80).optional().describe("Who is answering, signed at the bottom. Omitted rather than guessed.")
|
|
82
|
+
agentName: z.string().min(1).max(80).regex(/^[^@<>"]*$/, "A name may not contain @, <, > or a double quote.").optional().describe("Who is answering, signed at the bottom and written into the reply's From line. Omitted rather than guessed. May not contain `@`, `<`, `>` or `\"`, so the display name cannot pass for an address.")
|
|
83
83
|
}).describe("An answer to send to the customer.");
|
|
84
84
|
/** The per-viewer flags body. */
|
|
85
85
|
const FlagsInput = z.object({
|
|
@@ -15,32 +15,29 @@ import { type DeprovisionTarget, type ManagedEnvironment } from "@pithy-sh/secre
|
|
|
15
15
|
* orchestration (order, idempotency, per-env fan-out) is unit-tested without touching Cloudflare.
|
|
16
16
|
* Every step is idempotent; re-running is a no-op.
|
|
17
17
|
*
|
|
18
|
-
* ## The routing rule
|
|
18
|
+
* ## The routing rule never enables Email Routing
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* the address, and the target worker are all explicit flags, and a project that has not decided yet
|
|
25
|
-
* provisions everything else and adds the rule later.
|
|
20
|
+
* **Enabling Email Routing for a domain points its MX at Cloudflare**, so it stays the adopter's act. The CLI's
|
|
21
|
+
* routing step finds the zone that holds each environment's inbound address, refuses a domain Email Routing is
|
|
22
|
+
* not enabled for, and only then creates or updates the exact-address rule that sends it to that environment's
|
|
23
|
+
* app worker. It never enables routing and never touches DNS.
|
|
26
24
|
*
|
|
27
|
-
*
|
|
28
|
-
* `<project>-global-
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* each other, with the loser's customer mail delivered to the winner's Worker.
|
|
25
|
+
* A rule is per environment, named `<project>-<env>-support-inbound`: each environment's address goes to its own
|
|
26
|
+
* app worker. The project-global `<project>-global-support-inbound` is what the explicit `--routing-zone`,
|
|
27
|
+
* `--inbound-address` and `--app-worker` override creates, distinct from `@pithy-sh/email`'s
|
|
28
|
+
* `<project>-global-email-bounce`, because that path keys idempotency on the rule's name.
|
|
32
29
|
*/
|
|
33
30
|
/**
|
|
34
|
-
* The name of
|
|
35
|
-
*
|
|
36
|
-
*
|
|
31
|
+
* The name of an Email Routing rule this capability creates, for one project: `<project>-<env>-support-inbound`
|
|
32
|
+
* for one environment's address, or `<project>-global-support-inbound` for the override that routes one address
|
|
33
|
+
* for the whole project. Distinct from the bounce handler's either way.
|
|
37
34
|
*
|
|
38
35
|
* **Still on the generic composer**, and deliberately: an Email Routing rule is not a namespace
|
|
39
36
|
* `@pithy-sh/core/src/naming/limits` carries a verified Cloudflare cap for, and the facade exists so a
|
|
40
37
|
* kind of thing brings its own number rather than borrowing one. It takes the conservative default
|
|
41
38
|
* until that namespace lands — the same call `@pithy-sh/email`'s bounce rule makes.
|
|
42
39
|
*/
|
|
43
|
-
export declare function supportRoutingRuleName(project: string): string;
|
|
40
|
+
export declare function supportRoutingRuleName(project: string, env?: ManagedEnvironment): string;
|
|
44
41
|
/**
|
|
45
42
|
* The deployed Worker name for a project's environment — also its resolved config basename. Composed
|
|
46
43
|
* through core's naming facade under the **`worker`** namespace, so it is measured against a Worker
|
|
@@ -121,8 +118,9 @@ export interface SupportProvisioner {
|
|
|
121
118
|
backfilled: boolean;
|
|
122
119
|
}>;
|
|
123
120
|
/**
|
|
124
|
-
* Ensure the inbound Email Routing
|
|
125
|
-
*
|
|
121
|
+
* Ensure the inbound Email Routing rules that deliver the support addresses to the app worker: each provisioned
|
|
122
|
+
* environment's own, or the one the override names. Idempotent. Returns `skipped: true` when there was nothing
|
|
123
|
+
* to route.
|
|
126
124
|
*/
|
|
127
125
|
ensureRoutingRule(): Promise<{
|
|
128
126
|
created: boolean;
|
|
@@ -192,8 +190,9 @@ export interface SupportDeprovisioner {
|
|
|
192
190
|
/** Delete the env's classification worker. Idempotent. */
|
|
193
191
|
deleteWorker(env: ManagedEnvironment): Promise<void>;
|
|
194
192
|
/**
|
|
195
|
-
* Remove the inbound routing rule, so mail stops being delivered here.
|
|
196
|
-
* was asked to remove it (`removeRouting`).
|
|
193
|
+
* Remove the project-global inbound routing rule the override created, so mail stops being delivered here.
|
|
194
|
+
* Idempotent. Called only when the teardown was asked to remove it (`removeRouting`). An environment's own rule
|
|
195
|
+
* is the CLI's to remove, with that environment.
|
|
197
196
|
*/
|
|
198
197
|
removeRoutingRule(): Promise<{
|
|
199
198
|
removed: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provisionSupport.d.ts","sourceRoot":"","sources":["../../src/provision/provisionSupport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAKlF,OAAO,EAEL,KAAK,iBAAiB,EAEtB,KAAK,kBAAkB,EAExB,MAAM,6BAA6B,CAAC;AAGrC
|
|
1
|
+
{"version":3,"file":"provisionSupport.d.ts","sourceRoot":"","sources":["../../src/provision/provisionSupport.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,35 +20,32 @@ import { assertSharedLeavesLast, deprovisionTarget, managedEnvironments } from "
|
|
|
20
20
|
* orchestration (order, idempotency, per-env fan-out) is unit-tested without touching Cloudflare.
|
|
21
21
|
* Every step is idempotent; re-running is a no-op.
|
|
22
22
|
*
|
|
23
|
-
* ## The routing rule
|
|
23
|
+
* ## The routing rule never enables Email Routing
|
|
24
24
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* the address, and the target worker are all explicit flags, and a project that has not decided yet
|
|
30
|
-
* provisions everything else and adds the rule later.
|
|
25
|
+
* **Enabling Email Routing for a domain points its MX at Cloudflare**, so it stays the adopter's act. The CLI's
|
|
26
|
+
* routing step finds the zone that holds each environment's inbound address, refuses a domain Email Routing is
|
|
27
|
+
* not enabled for, and only then creates or updates the exact-address rule that sends it to that environment's
|
|
28
|
+
* app worker. It never enables routing and never touches DNS.
|
|
31
29
|
*
|
|
32
|
-
*
|
|
33
|
-
* `<project>-global-
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* each other, with the loser's customer mail delivered to the winner's Worker.
|
|
30
|
+
* A rule is per environment, named `<project>-<env>-support-inbound`: each environment's address goes to its own
|
|
31
|
+
* app worker. The project-global `<project>-global-support-inbound` is what the explicit `--routing-zone`,
|
|
32
|
+
* `--inbound-address` and `--app-worker` override creates, distinct from `@pithy-sh/email`'s
|
|
33
|
+
* `<project>-global-email-bounce`, because that path keys idempotency on the rule's name.
|
|
37
34
|
*/
|
|
38
35
|
/**
|
|
39
|
-
* The name of
|
|
40
|
-
*
|
|
41
|
-
*
|
|
36
|
+
* The name of an Email Routing rule this capability creates, for one project: `<project>-<env>-support-inbound`
|
|
37
|
+
* for one environment's address, or `<project>-global-support-inbound` for the override that routes one address
|
|
38
|
+
* for the whole project. Distinct from the bounce handler's either way.
|
|
42
39
|
*
|
|
43
40
|
* **Still on the generic composer**, and deliberately: an Email Routing rule is not a namespace
|
|
44
41
|
* `@pithy-sh/core/src/naming/limits` carries a verified Cloudflare cap for, and the facade exists so a
|
|
45
42
|
* kind of thing brings its own number rather than borrowing one. It takes the conservative default
|
|
46
43
|
* until that namespace lands — the same call `@pithy-sh/email`'s bounce rule makes.
|
|
47
44
|
*/
|
|
48
|
-
function supportRoutingRuleName(project) {
|
|
45
|
+
function supportRoutingRuleName(project, env) {
|
|
49
46
|
return resourceName({
|
|
50
47
|
project,
|
|
51
|
-
env: GLOBAL_SCOPE,
|
|
48
|
+
env: env ?? GLOBAL_SCOPE,
|
|
52
49
|
thing: `${SUPPORT_CAPABILITY}-inbound`
|
|
53
50
|
});
|
|
54
51
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import type { CapabilitySettings } from "@pithy-sh/core/src/capability/settings";
|
|
4
|
+
import type { SupportConfig } from "../config/config";
|
|
5
|
+
/** Support's sending check, built from one composition's resolved config. */
|
|
6
|
+
export declare function supportSendingSettings(config: SupportConfig): CapabilitySettings;
|
|
7
|
+
//# sourceMappingURL=sendingCheck.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sendingCheck.d.ts","sourceRoot":"","sources":["../../src/provision/sendingCheck.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAmB,MAAM,wCAAwC,CAAC;AAElG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAoBtD,6EAA6E;AAC7E,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,aAAa,GAAG,kBAAkB,CA0ChF"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { InternalError } from "@pithy-sh/core/src/error/pithyError";
|
|
4
|
+
//#region src/provision/sendingCheck.ts
|
|
5
|
+
/**
|
|
6
|
+
* Whether each address a reply is sent from can send, as `pithy doctor` asks it.
|
|
7
|
+
*
|
|
8
|
+
* A mailed reply is sent from the inbox the thread arrived at, so an inbox on a domain Email Service cannot send
|
|
9
|
+
* from receives mail and cannot answer it: every reply fails at send with the domain named. Asked once per domain,
|
|
10
|
+
* only while replies are on. Nothing here writes.
|
|
11
|
+
*/
|
|
12
|
+
/** The domain half of an address, lowercased, or `null` where it carries none. */
|
|
13
|
+
function domainOf(address) {
|
|
14
|
+
const at = address.lastIndexOf("@");
|
|
15
|
+
if (at < 0 || at === address.length - 1) return null;
|
|
16
|
+
return address.slice(at + 1).trim().toLowerCase();
|
|
17
|
+
}
|
|
18
|
+
/** Support's sending check, built from one composition's resolved config. */
|
|
19
|
+
function supportSendingSettings(config) {
|
|
20
|
+
return {
|
|
21
|
+
local: () => [],
|
|
22
|
+
account: async (context) => {
|
|
23
|
+
if (!config.reply.enabled) return [];
|
|
24
|
+
const senders = [...config.inboundAddresses.map((address) => ({
|
|
25
|
+
address,
|
|
26
|
+
setting: "inboundAddresses"
|
|
27
|
+
})), ...config.reply.replyToAddress ? [{
|
|
28
|
+
address: config.reply.replyToAddress,
|
|
29
|
+
setting: "reply.replyToAddress"
|
|
30
|
+
}] : []];
|
|
31
|
+
const byDomain = /* @__PURE__ */ new Map();
|
|
32
|
+
for (const { address, setting } of senders) {
|
|
33
|
+
const domain = domainOf(address);
|
|
34
|
+
if (domain === null) continue;
|
|
35
|
+
const entry = byDomain.get(domain) ?? {
|
|
36
|
+
setting,
|
|
37
|
+
addresses: []
|
|
38
|
+
};
|
|
39
|
+
if (!entry.addresses.includes(address)) entry.addresses.push(address);
|
|
40
|
+
byDomain.set(domain, entry);
|
|
41
|
+
}
|
|
42
|
+
if (byDomain.size === 0) return [];
|
|
43
|
+
const ask = context.account.sendingDomain;
|
|
44
|
+
if (!ask) throw new InternalError({
|
|
45
|
+
message: "This doctor cannot ask whether a domain can send.",
|
|
46
|
+
detail: "SettingsAccountReader.sendingDomain is absent."
|
|
47
|
+
});
|
|
48
|
+
const findings = [];
|
|
49
|
+
for (const [domain, { setting, addresses }] of byDomain) {
|
|
50
|
+
if (await ask.call(context.account, domain)) continue;
|
|
51
|
+
findings.push({
|
|
52
|
+
setting,
|
|
53
|
+
environment: null,
|
|
54
|
+
problem: `${domain} cannot send on Email Service, so a reply from ${addresses.join(", ")} fails.`,
|
|
55
|
+
action: `Onboard ${domain} for sending in the Cloudflare dashboard under Email Service, then rerun doctor.`
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return findings;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { supportSendingSettings };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import type { SupportConfig } from "../config/config";
|
|
4
|
+
/** The placeholder `reply.fromName` writes the answering agent's name into. */
|
|
5
|
+
export declare const AGENT_PLACEHOLDER = "{agent}";
|
|
6
|
+
/**
|
|
7
|
+
* The display name a reply is sent under: `reply.fromName` with the agent's name written in.
|
|
8
|
+
*
|
|
9
|
+
* A pattern with no placeholder is a fixed name, used whoever answered. A pattern with one is undefined when no
|
|
10
|
+
* agent name is given, so the email capability's own `fromName` is used. Line breaks and control characters are
|
|
11
|
+
* folded to spaces, because the result is a header value.
|
|
12
|
+
*/
|
|
13
|
+
export declare function replyFromName(pattern: string, agentName: string | undefined): string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Every address support may send a reply as: its `inboundAddresses` and `reply.replyToAddress`, normalized and
|
|
16
|
+
* once each. `compose` binds the email capability's `enqueueAs` to exactly these, so a reply cannot be sent as
|
|
17
|
+
* an address this inbox does not claim.
|
|
18
|
+
*/
|
|
19
|
+
export declare function claimedSenders(config: SupportConfig): string[];
|
|
20
|
+
//# sourceMappingURL=fromName.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fromName.d.ts","sourceRoot":"","sources":["../../src/reply/fromName.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAWhG;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CAG9D"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
//#region src/reply/fromName.ts
|
|
4
|
+
/** The placeholder `reply.fromName` writes the answering agent's name into. */
|
|
5
|
+
const AGENT_PLACEHOLDER = "{agent}";
|
|
6
|
+
/**
|
|
7
|
+
* The display name a reply is sent under: `reply.fromName` with the agent's name written in.
|
|
8
|
+
*
|
|
9
|
+
* A pattern with no placeholder is a fixed name, used whoever answered. A pattern with one is undefined when no
|
|
10
|
+
* agent name is given, so the email capability's own `fromName` is used. Line breaks and control characters are
|
|
11
|
+
* folded to spaces, because the result is a header value.
|
|
12
|
+
*/
|
|
13
|
+
function replyFromName(pattern, agentName) {
|
|
14
|
+
const agent = agentName?.trim() ?? "";
|
|
15
|
+
if (pattern.includes("{agent}") && !agent) return void 0;
|
|
16
|
+
const name = pattern.split(AGENT_PLACEHOLDER).join(agent).replace(/[\u0000-\u001f\u007f]+/g, " ").replace(/ {2,}/g, " ").trim();
|
|
17
|
+
return name === "" ? void 0 : name;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Every address support may send a reply as: its `inboundAddresses` and `reply.replyToAddress`, normalized and
|
|
21
|
+
* once each. `compose` binds the email capability's `enqueueAs` to exactly these, so a reply cannot be sent as
|
|
22
|
+
* an address this inbox does not claim.
|
|
23
|
+
*/
|
|
24
|
+
function claimedSenders(config) {
|
|
25
|
+
const addresses = [...config.inboundAddresses, ...config.reply.replyToAddress ? [config.reply.replyToAddress] : []];
|
|
26
|
+
return [...new Set(addresses)];
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { AGENT_PLACEHOLDER, claimedSenders, replyFromName };
|
package/dist/reply/send.d.ts
CHANGED
|
@@ -71,6 +71,15 @@ export interface ReplyDeps {
|
|
|
71
71
|
template: string;
|
|
72
72
|
payload: unknown;
|
|
73
73
|
replyTo?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Who the reply is sent as: the thread's inbox, under `reply.fromName` with the agent's name written in.
|
|
76
|
+
* `name` is absent when no agent name was given, and the email capability's `fromName` is used. The seam
|
|
77
|
+
* `compose` binds refuses an address support does not claim.
|
|
78
|
+
*/
|
|
79
|
+
from?: {
|
|
80
|
+
address: string;
|
|
81
|
+
name?: string;
|
|
82
|
+
};
|
|
74
83
|
/** The language to write the shell in — the reporter's, taken off their submission context. */
|
|
75
84
|
locale?: string;
|
|
76
85
|
inReplyTo?: string;
|
|
@@ -93,7 +102,7 @@ export interface ReplyInput {
|
|
|
93
102
|
threadId: string;
|
|
94
103
|
/** The reply text, as a human wrote and edited it. */
|
|
95
104
|
body: string;
|
|
96
|
-
/** Who is answering
|
|
105
|
+
/** Who is answering: signed at the bottom, and written into the From line through `reply.fromName`. Optional. */
|
|
97
106
|
agentName?: string;
|
|
98
107
|
/** The verified control-plane subject sending it — the audit actor. */
|
|
99
108
|
viewer: string;
|