@pattern-js/mod-email 0.4.0 → 0.5.0-main.79cf721

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,7 +19,7 @@ npm install @pattern-js/mod-email @pattern-js/mod-email-resend
19
19
 
20
20
  Reach for it when your app sends email — sign-in links, notifications, agent
21
21
  tools — and you want the sender to be swappable config, not code: accounts
22
- live in admin → **System → Email**, credentials in the vault or env vars, and
22
+ live in admin → **Resources → Email**, credentials in the vault or env vars, and
23
23
  re-pointing an account re-targets every workflow using it. Skip it if your app
24
24
  never sends email; if you only need dev sign-in, the magic-link console
25
25
  fallback already covers you with zero mods.
@@ -31,11 +31,14 @@ fallback already covers you with zero mods.
31
31
  `html`/`text`. Attachments from media bytes, blob refs, or literal files.
32
32
  - **`email.account`** — resolve a named account to a ref (like `ai.alias` for
33
33
  models); `required: false` turns it into a probe to branch on.
34
- - **Admin → System → Email** — accounts CRUD with per-driver fields, secrets
34
+ - **Admin → Resources → Email** — accounts CRUD with per-driver fields, secrets
35
35
  from the vault or env, and a Test button that sends a REAL email.
36
36
  - **`email.deliver-token`** — a visible, forkable workflow subscribed to
37
37
  identity's `identity.deliverToken` hook: sign-in links email themselves once
38
38
  a `default` account exists.
39
+ - **`email.alert-failed-run`** — failure alerts: set `PATTERN_ALERTS_TO` and any
40
+ failed run emails that address a deep link to the run (an ordinary workflow on
41
+ the `run.failed` event, so you can open and change it).
39
42
  - A tiny **driver SPI** — `registerDriver({ id, label, secrets, options, send })`
40
43
  in your mod's `ready()`; the admin form generates itself from the field specs.
41
44
 
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @pattern-js/mod-email — the packaged failure-alert workflow.
3
+ *
4
+ * The engine emits one `run.failed` event per failed top-level run (0.5); this
5
+ * workflow turns it into an email to `PATTERN_ALERTS_TO`. Zero-config posture:
6
+ * with the env unset it gates out silently, and with no email account
7
+ * configured the send console-logs — so it ships on, costs nothing, and starts
8
+ * alerting the moment both exist. `internal: false`: open it in the editor to
9
+ * reword the copy, filter workflows, or reroute to another channel entirely.
10
+ *
11
+ * (A failing run that was itself triggered by `run.failed` never re-emits —
12
+ * the engine's recursion guard — so a broken alert can't alert about itself.)
13
+ *
14
+ * A `core.flow.throttle` keyed by workflow id caps the mail at one per failing
15
+ * workflow per 10 minutes: a route failing on every request (or someone
16
+ * hammering it) costs one email, not one per request. Raise or drop it in the
17
+ * editor.
18
+ */
19
+ import type { Workflow } from "@pattern-js/core";
20
+ export declare function alertFailedRunWorkflow(): Workflow;
21
+ //# sourceMappingURL=alerts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../src/alerts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,wBAAgB,sBAAsB,IAAI,QAAQ,CAiDjD"}
package/dist/alerts.js ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @pattern-js/mod-email — the packaged failure-alert workflow.
3
+ *
4
+ * The engine emits one `run.failed` event per failed top-level run (0.5); this
5
+ * workflow turns it into an email to `PATTERN_ALERTS_TO`. Zero-config posture:
6
+ * with the env unset it gates out silently, and with no email account
7
+ * configured the send console-logs — so it ships on, costs nothing, and starts
8
+ * alerting the moment both exist. `internal: false`: open it in the editor to
9
+ * reword the copy, filter workflows, or reroute to another channel entirely.
10
+ *
11
+ * (A failing run that was itself triggered by `run.failed` never re-emits —
12
+ * the engine's recursion guard — so a broken alert can't alert about itself.)
13
+ *
14
+ * A `core.flow.throttle` keyed by workflow id caps the mail at one per failing
15
+ * workflow per 10 minutes: a route failing on every request (or someone
16
+ * hammering it) costs one email, not one per request. Raise or drop it in the
17
+ * editor.
18
+ */
19
+ export function alertFailedRunWorkflow() {
20
+ return {
21
+ id: "email.alert-failed-run",
22
+ name: "Email · alert on failed runs (run.failed)",
23
+ description: "Emails PATTERN_ALERTS_TO whenever a top-level run fails, with the error and a deep link to the " +
24
+ "trace; at most one email per failing workflow every 10 minutes (the throttle node). Unset the env " +
25
+ "(or edit this workflow) to change the posture.",
26
+ source: "code",
27
+ internal: false,
28
+ nodes: [
29
+ { id: "in", op: "boundary.event", config: { event: "run.failed" } },
30
+ // Recipient(s): unset/empty → the gate stops everything, silently.
31
+ { id: "to", op: "core.env", config: { name: "PATTERN_ALERTS_TO", default: "" } },
32
+ { id: "wanted", op: "core.cast.toBoolean" },
33
+ { id: "gate", op: "core.flow.gate" },
34
+ // One email per failing workflow per window; the rest are dropped.
35
+ { id: "which", op: "core.object.get", config: { path: "workflowId" } },
36
+ { id: "throttle", op: "core.flow.throttle", config: { max: 1, window: "10m" } },
37
+ // The admin deep link needs a public origin; localhost is the honest dev default.
38
+ { id: "origin", op: "core.env", config: { name: "PATTERN_PUBLIC_URL", default: "http://localhost:3000" } },
39
+ { id: "info", op: "core.object.set", config: { path: "origin" } },
40
+ { id: "subj", op: "core.string.template", config: { template: "Run failed: {{workflowId}}" } },
41
+ {
42
+ id: "body",
43
+ op: "core.string.template",
44
+ config: {
45
+ template: "# Run failed: {{workflowId}}\n\n> {{error.message}}\n\nInspect the trace (durable runs can resume from the failing node):\n\n{{origin}}/admin/runs/{{runId}}\n\nYou get this because PATTERN_ALERTS_TO is set — edit the \"email.alert-failed-run\" workflow to reword or reroute it.",
46
+ },
47
+ },
48
+ { id: "send", op: "email.send" },
49
+ ],
50
+ edges: [
51
+ { from: { node: "to", port: "out" }, to: { node: "wanted", port: "value" } },
52
+ { from: { node: "wanted", port: "out" }, to: { node: "gate", port: "condition" } },
53
+ { from: { node: "in", port: "payload" }, to: { node: "info", port: "object" } },
54
+ { from: { node: "origin", port: "out" }, to: { node: "info", port: "value" } },
55
+ { from: { node: "in", port: "payload" }, to: { node: "subj", port: "data" } },
56
+ { from: { node: "info", port: "out" }, to: { node: "body", port: "data" } },
57
+ { from: { node: "to", port: "out" }, to: { node: "send", port: "to" } },
58
+ { from: { node: "subj", port: "out" }, to: { node: "send", port: "subject" } },
59
+ { from: { node: "body", port: "out" }, to: { node: "send", port: "markdown" } },
60
+ { from: { node: "in", port: "payload" }, to: { node: "which", port: "object" } },
61
+ { from: { node: "which", port: "out" }, to: { node: "throttle", port: "key" } },
62
+ { from: { node: "gate", port: "out" }, to: { node: "throttle", port: "in" } },
63
+ { from: { node: "throttle", port: "out" }, to: { node: "send", port: "in" } },
64
+ ],
65
+ };
66
+ }
67
+ //# sourceMappingURL=alerts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.js","sourceRoot":"","sources":["../src/alerts.ts"],"names":[],"mappings":"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"}
package/dist/app.d.ts CHANGED
@@ -9,5 +9,5 @@
9
9
  * email so the operator sees end-to-end delivery, not a wiring check. The
10
10
  * "default" account is what the packaged sign-in delivery workflow uses.
11
11
  */
12
- export declare const REMOTE = "\nconst { React, api, ui } = globalThis.__PATTERN_ADMIN__;\nconst { GlassPanel, NeonButton, Badge, Modal } = ui;\nconst h = React.createElement;\nconst inputCls = \"glass w-full rounded-lg px-3 py-2 text-sm\";\n\nfunction Field({ label, hint, children }) {\n return h(\"label\", { className: \"block text-sm space-y-1\" },\n h(\"span\", { className: \"text-muted\" }, label),\n children,\n hint && h(\"span\", { className: \"block text-xs text-muted/70\" }, hint));\n}\n\n// Normalize an api.call result that may be the array directly or { key: [...] }.\nfunction arr(r, key) { return Array.isArray(r) ? r : ((r && r[key]) || []); }\n\n// One secret field: a vault|env source toggle + the matching key picker/input.\nfunction SecretRow({ field, refValue, secrets, onChange }) {\n const src = (refValue && refValue.source) || \"vault\";\n const key = (refValue && refValue.key) || \"\";\n const set = (patch) => onChange({ source: src, key, ...patch });\n const vaultPicker = h(\"select\", { className: inputCls, value: key, onChange: (e) => set({ key: e.target.value }) },\n h(\"option\", { value: \"\" }, \"\u2014 pick a vault secret \u2014\"),\n secrets.map((s) => h(\"option\", { key: s, value: s }, s)),\n key && secrets.indexOf(key) < 0 ? h(\"option\", { key: key, value: key }, key + \" (missing)\") : null);\n const envInput = h(\"input\", { className: inputCls, value: key, placeholder: \"ENV_VAR_NAME\", onChange: (e) => set({ key: e.target.value }) });\n return h(Field, { key: field.field, label: \"Secret \u00B7 \" + (field.label || field.field) + (field.required === false ? \" (optional)\" : \"\") },\n h(\"div\", { className: \"flex gap-2\" },\n h(\"select\", { className: \"glass rounded-lg px-2 py-2 text-sm\", style: { width: \"5.5rem\" }, value: src, onChange: (e) => set({ source: e.target.value }) },\n h(\"option\", { value: \"vault\" }, \"vault\"),\n h(\"option\", { value: \"env\" }, \"env\")),\n h(\"div\", { className: \"flex-1\" }, src === \"env\" ? envInput : vaultPicker)));\n}\n\n// The Test flow: sends a REAL email to an operator-supplied address.\nfunction TestModal({ state, setState }) {\n const [to, setTo] = React.useState(\"\");\n if (!state) return null;\n const result = state.result;\n function run() {\n setState({ payload: state.payload, busy: true });\n api.call(\"POST\", \"/email/test\", { ...state.payload, to })\n .then((r) => setState({ payload: state.payload, result: r }))\n .catch((e) => setState({ payload: state.payload, result: { ok: false, detail: String(e) } }));\n }\n return h(Modal, { open: true, onClose: () => setState(null), title: \"Send a test email\" },\n h(\"div\", { className: \"space-y-3\" },\n h(\"p\", { className: \"text-xs text-muted\" },\n \"Sends a REAL email through this account so you can verify end-to-end delivery \u2014 driver, credentials, From address and inbox placement in one go.\"),\n h(Field, { label: \"Send to\" },\n h(\"input\", { className: inputCls, type: \"email\", value: to, placeholder: \"you@example.com\", onChange: (e) => setTo(e.target.value) })),\n h(\"div\", { className: \"flex items-center gap-3\" },\n h(NeonButton, { onClick: run, disabled: state.busy || !to }, state.busy ? \"Sending\u2026\" : \"Send test email\")),\n result && h(\"div\", { className: \"space-y-2\" },\n h(\"p\", { className: \"text-sm font-medium\", style: { color: result.ok ? \"var(--color-neon-lime)\" : \"var(--color-neon-pink)\" } },\n result.ok ? \"\u2713 Sent \u2014 check the inbox.\" + (result.messageId ? \" (id \" + result.messageId + \")\" : \"\") : \"\u2717 Failed\"),\n !result.ok && h(\"pre\", { className: \"glass rounded-lg p-3 text-xs whitespace-pre-wrap\", style: { maxHeight: \"16rem\", overflow: \"auto\" } }, result.detail || \"unknown error\"))));\n}\n\n// The form that creates/edits one account.\nfunction AccountForm({ providers, secrets, form, setForm, reload, onTest }) {\n const [busy, setBusy] = React.useState(false);\n const blank = { name: \"\", provider: (providers[0] && providers[0].id) || \"\", from: \"\", secrets: {}, options: {} };\n const spec = providers.find((p) => p.id === form.provider) || providers[0] || { secrets: [], options: [] };\n\n function pickProvider(id) { setForm({ ...form, provider: id, secrets: {}, options: {} }); }\n function setSecret(field, refValue) { setForm({ ...form, secrets: { ...form.secrets, [field]: refValue } }); }\n function setOption(field, val) { setForm({ ...form, options: { ...form.options, [field]: val } }); }\n\n // Drop empty secret rows so we never persist a sourceless reference.\n function payload() {\n const s = {};\n for (const k of Object.keys(form.secrets)) { const v = form.secrets[k]; if (v && v.key) s[k] = { source: v.source || \"vault\", key: v.key }; }\n const provider = form.provider || (spec && spec.id) || \"\";\n return { name: form.name, provider, from: form.from, secrets: s, options: form.options };\n }\n function save() {\n setBusy(true);\n api.call(\"POST\", \"/email/accounts\", payload()).then(() => { setForm(blank); reload(); }).finally(() => setBusy(false));\n }\n\n return h(GlassPanel, { className: \"p-6 space-y-4\" },\n h(\"div\", null,\n h(\"h3\", { className: \"font-semibold\" }, form.name ? \"Edit account\" : \"New account\"),\n h(\"p\", { className: \"text-xs text-muted mt-1\" },\n \"An account is a named sender \u2014 a driver, a From address and the credentials it needs. Wire one with an \",\n h(\"span\", { className: \"font-mono\" }, \"email.account\"),\n \" node, or leave a node's account unset and the \", h(\"span\", { className: \"font-mono\" }, \"default\"),\n \" account is used. The packaged sign-in delivery workflow emails magic links the moment a default account exists.\")),\n h(\"div\", { className: \"space-y-3\" },\n h(Field, { label: \"Account name\", hint: 'the \"default\" account powers sign-in link delivery.' },\n h(\"input\", { className: inputCls, value: form.name, onChange: (e) => setForm({ ...form, name: e.target.value }), placeholder: \"default\" })),\n h(Field, { label: \"Driver\" },\n h(\"select\", { className: inputCls, value: form.provider || (spec && spec.id) || \"\", onChange: (e) => pickProvider(e.target.value) },\n providers.map((p) => h(\"option\", { key: p.id, value: p.id }, p.label)))),\n h(Field, { label: \"From\", hint: \"the default sender; email.send's from input overrides per message.\" },\n h(\"input\", { className: inputCls, value: form.from, onChange: (e) => setForm({ ...form, from: e.target.value }), placeholder: \"App <hello@example.com>\" })),\n ((spec && spec.secrets) || []).map((f) => h(SecretRow, { key: f.field, field: f, refValue: form.secrets[f.field], secrets: secrets, onChange: (refValue) => setSecret(f.field, refValue) })),\n ((spec && spec.options) || []).map((f) => h(Field, { key: f.field, label: (f.label || f.field) + (f.required ? \"\" : \" (optional)\") },\n h(\"input\", { className: inputCls, value: form.options[f.field] || \"\", placeholder: f.placeholder || \"\", onChange: (e) => setOption(f.field, e.target.value) }))),\n h(\"div\", { className: \"flex items-center gap-3 pt-1\" },\n h(NeonButton, { onClick: save, disabled: busy || !form.name || !form.from }, \"Save account\"),\n h(NeonButton, { onClick: () => onTest(payload()), disabled: busy || !form.from }, \"Test\"),\n form.name && h(NeonButton, { onClick: () => setForm(blank) }, \"New\")),\n h(\"p\", { className: \"text-xs text-muted\" }, \"Secrets come from the vault (System \u2192 Secrets) or an env var \u2014 pick the source per field.\")));\n}\n\n// The list of saved accounts (click to edit, delete).\nfunction AccountList({ accounts, onEdit, reload }) {\n function del(name) { api.call(\"DELETE\", \"/email/accounts/\" + encodeURIComponent(name)).then(reload); }\n return h(GlassPanel, { className: \"p-6 space-y-3\" },\n h(\"div\", { className: \"flex items-center justify-between\" },\n h(\"h3\", { className: \"font-semibold\" }, \"Accounts\"),\n h(\"span\", { className: \"text-xs text-muted\" }, accounts.length + \" configured\")),\n accounts.length === 0\n ? h(\"p\", { className: \"text-sm text-muted\" }, 'No accounts yet. Create one named \"default\" and sign-in links start emailing automatically.')\n : h(\"div\", { className: \"space-y-1\" }, accounts.map((a) =>\n h(\"div\", { key: a.name, className: \"flex items-center justify-between rounded-lg px-2 py-1.5 hover:bg-white/5\" },\n h(\"button\", { className: \"text-left text-sm flex-1 min-w-0\", onClick: () => onEdit(a) },\n h(Badge, { hue: a.name === \"default\" ? 140 : 200 }, a.name), \" \",\n h(\"span\", { className: \"font-mono text-xs text-muted\" }, a.provider + \" \u00B7 \" + a.from)),\n h(\"button\", { className: \"text-xs text-muted hover:text-[var(--color-neon-pink)] shrink-0\", onClick: () => del(a.name) }, \"Delete\")))));\n}\n\nexport default function EmailPage() {\n const blank = { name: \"\", provider: \"\", from: \"\", secrets: {}, options: {} };\n const [providers, setProviders] = React.useState([]);\n const [secrets, setSecrets] = React.useState([]);\n const [accounts, setAccounts] = React.useState([]);\n const [form, setForm] = React.useState(blank);\n const [test, setTest] = React.useState(null);\n\n const reloadAccounts = () => api.call(\"GET\", \"/email/accounts\").then((r) => setAccounts(arr(r, \"accounts\")));\n\n React.useEffect(() => {\n api.call(\"GET\", \"/email/providers\").then((r) => setProviders(arr(r, \"providers\"))).catch(() => {});\n api.call(\"GET\", \"/vault/secrets\").then((r) => setSecrets(arr(r, \"secrets\").map((s) => (typeof s === \"string\" ? s : s.name)))).catch(() => {});\n reloadAccounts().catch(() => {});\n }, []);\n\n function edit(a) { setForm({ name: a.name, provider: a.provider, from: a.from, secrets: { ...(a.secrets || {}) }, options: { ...(a.options || {}) } }); }\n function reloadAndReset() { reloadAccounts(); setForm(blank); }\n\n if (providers.length === 0) {\n return h(GlassPanel, { className: \"p-6 space-y-2\" },\n h(\"h3\", { className: \"font-semibold\" }, \"No email drivers installed\"),\n h(\"p\", { className: \"text-sm text-muted\" },\n \"mod-email is the contract; a driver mod does the sending. Install \",\n h(\"span\", { className: \"font-mono\" }, \"@pattern-js/mod-email-resend\"),\n \" or \", h(\"span\", { className: \"font-mono\" }, \"@pattern-js/mod-email-smtp\"),\n \" and list it in pattern.config.json, then reload this page.\"));\n }\n\n return h(\"div\", null,\n h(\"div\", { className: \"grid gap-6 lg:grid-cols-2\" },\n h(AccountForm, { providers, secrets, form, setForm, reload: reloadAndReset, onTest: (payload) => setTest({ payload }) }),\n h(AccountList, { accounts, onEdit: edit, reload: reloadAccounts })),\n h(TestModal, { state: test, setState: setTest }));\n}\n";
12
+ export declare const REMOTE = "\nconst { React, api, ui } = globalThis.__PATTERN_ADMIN__;\nconst { GlassPanel, NeonButton, Badge, Modal } = ui;\nconst h = React.createElement;\nconst inputCls = \"glass w-full rounded-lg px-3 py-2 text-sm\";\n\nfunction Field({ label, hint, children }) {\n return h(\"label\", { className: \"block text-sm space-y-1\" },\n h(\"span\", { className: \"text-muted\" }, label),\n children,\n hint && h(\"span\", { className: \"block text-xs text-muted/70\" }, hint));\n}\n\n// Normalize an api.call result that may be the array directly or { key: [...] }.\nfunction arr(r, key) { return Array.isArray(r) ? r : ((r && r[key]) || []); }\n\n// One secret field: a vault|env source toggle + the matching key picker/input.\nfunction SecretRow({ field, refValue, secrets, onChange }) {\n const src = (refValue && refValue.source) || \"vault\";\n const key = (refValue && refValue.key) || \"\";\n const set = (patch) => onChange({ source: src, key, ...patch });\n const vaultPicker = h(\"select\", { className: inputCls, value: key, onChange: (e) => set({ key: e.target.value }) },\n h(\"option\", { value: \"\" }, \"\u2014 pick a vault secret \u2014\"),\n secrets.map((s) => h(\"option\", { key: s, value: s }, s)),\n key && secrets.indexOf(key) < 0 ? h(\"option\", { key: key, value: key }, key + \" (missing)\") : null);\n const envInput = h(\"input\", { className: inputCls, value: key, placeholder: \"ENV_VAR_NAME\", onChange: (e) => set({ key: e.target.value }) });\n return h(Field, { key: field.field, label: \"Secret \u00B7 \" + (field.label || field.field) + (field.required === false ? \" (optional)\" : \"\") },\n h(\"div\", { className: \"flex gap-2\" },\n h(\"select\", { className: \"glass rounded-lg px-2 py-2 text-sm\", style: { width: \"5.5rem\" }, value: src, onChange: (e) => set({ source: e.target.value }) },\n h(\"option\", { value: \"vault\" }, \"vault\"),\n h(\"option\", { value: \"env\" }, \"env\")),\n h(\"div\", { className: \"flex-1\" }, src === \"env\" ? envInput : vaultPicker)));\n}\n\n// The Test flow: sends a REAL email to an operator-supplied address.\nfunction TestModal({ state, setState }) {\n const [to, setTo] = React.useState(\"\");\n if (!state) return null;\n const result = state.result;\n function run() {\n setState({ payload: state.payload, busy: true });\n api.call(\"POST\", \"/email/test\", { ...state.payload, to })\n .then((r) => setState({ payload: state.payload, result: r }))\n .catch((e) => setState({ payload: state.payload, result: { ok: false, detail: String(e) } }));\n }\n return h(Modal, { open: true, onClose: () => setState(null), title: \"Send a test email\" },\n h(\"div\", { className: \"space-y-3\" },\n h(\"p\", { className: \"text-xs text-muted\" },\n \"Sends a REAL email through this account so you can verify end-to-end delivery \u2014 driver, credentials, From address and inbox placement in one go.\"),\n h(Field, { label: \"Send to\" },\n h(\"input\", { className: inputCls, type: \"email\", value: to, placeholder: \"you@example.com\", onChange: (e) => setTo(e.target.value) })),\n h(\"div\", { className: \"flex items-center gap-3\" },\n h(NeonButton, { onClick: run, disabled: state.busy || !to }, state.busy ? \"Sending\u2026\" : \"Send test email\")),\n result && h(\"div\", { className: \"space-y-2\" },\n h(\"p\", { className: \"text-sm font-medium\", style: { color: result.ok ? \"var(--color-neon-lime)\" : \"var(--color-neon-pink)\" } },\n result.ok ? \"\u2713 Sent \u2014 check the inbox.\" + (result.messageId ? \" (id \" + result.messageId + \")\" : \"\") : \"\u2717 Failed\"),\n !result.ok && h(\"pre\", { className: \"glass rounded-lg p-3 text-xs whitespace-pre-wrap\", style: { maxHeight: \"16rem\", overflow: \"auto\" } }, result.detail || \"unknown error\"))));\n}\n\n// The form that creates/edits one account.\nfunction AccountForm({ providers, secrets, form, setForm, reload, onTest }) {\n const [busy, setBusy] = React.useState(false);\n const blank = { name: \"\", provider: (providers[0] && providers[0].id) || \"\", from: \"\", secrets: {}, options: {} };\n const spec = providers.find((p) => p.id === form.provider) || providers[0] || { secrets: [], options: [] };\n\n function pickProvider(id) { setForm({ ...form, provider: id, secrets: {}, options: {} }); }\n function setSecret(field, refValue) { setForm({ ...form, secrets: { ...form.secrets, [field]: refValue } }); }\n function setOption(field, val) { setForm({ ...form, options: { ...form.options, [field]: val } }); }\n\n // Drop empty secret rows so we never persist a sourceless reference.\n function payload() {\n const s = {};\n for (const k of Object.keys(form.secrets)) { const v = form.secrets[k]; if (v && v.key) s[k] = { source: v.source || \"vault\", key: v.key }; }\n const provider = form.provider || (spec && spec.id) || \"\";\n return { name: form.name, provider, from: form.from, secrets: s, options: form.options };\n }\n function save() {\n setBusy(true);\n api.call(\"POST\", \"/email/accounts\", payload()).then(() => { setForm(blank); reload(); }).finally(() => setBusy(false));\n }\n\n return h(GlassPanel, { className: \"p-6 space-y-4\" },\n h(\"div\", null,\n h(\"h3\", { className: \"font-semibold\" }, form.name ? \"Edit account\" : \"New account\"),\n h(\"p\", { className: \"text-xs text-muted mt-1\" },\n \"An account is a named sender \u2014 a driver, a From address and the credentials it needs. Wire one with an \",\n h(\"span\", { className: \"font-mono\" }, \"email.account\"),\n \" node, or leave a node's account unset and the \", h(\"span\", { className: \"font-mono\" }, \"default\"),\n \" account is used. The packaged sign-in delivery workflow emails magic links the moment a default account exists.\")),\n h(\"div\", { className: \"space-y-3\" },\n h(Field, { label: \"Account name\", hint: 'the \"default\" account powers sign-in link delivery.' },\n h(\"input\", { className: inputCls, value: form.name, onChange: (e) => setForm({ ...form, name: e.target.value }), placeholder: \"default\" })),\n h(Field, { label: \"Driver\" },\n h(\"select\", { className: inputCls, value: form.provider || (spec && spec.id) || \"\", onChange: (e) => pickProvider(e.target.value) },\n providers.map((p) => h(\"option\", { key: p.id, value: p.id }, p.label)))),\n h(Field, { label: \"From\", hint: \"the default sender; email.send's from input overrides per message.\" },\n h(\"input\", { className: inputCls, value: form.from, onChange: (e) => setForm({ ...form, from: e.target.value }), placeholder: \"App <hello@example.com>\" })),\n ((spec && spec.secrets) || []).map((f) => h(SecretRow, { key: f.field, field: f, refValue: form.secrets[f.field], secrets: secrets, onChange: (refValue) => setSecret(f.field, refValue) })),\n ((spec && spec.options) || []).map((f) => h(Field, { key: f.field, label: (f.label || f.field) + (f.required ? \"\" : \" (optional)\") },\n h(\"input\", { className: inputCls, value: form.options[f.field] || \"\", placeholder: f.placeholder || \"\", onChange: (e) => setOption(f.field, e.target.value) }))),\n h(\"div\", { className: \"flex items-center gap-3 pt-1\" },\n h(NeonButton, { onClick: save, disabled: busy || !form.name || !form.from }, \"Save account\"),\n h(NeonButton, { onClick: () => onTest(payload()), disabled: busy || !form.from }, \"Test\"),\n form.name && h(NeonButton, { onClick: () => setForm(blank) }, \"New\")),\n h(\"p\", { className: \"text-xs text-muted\" }, \"Secrets come from the vault (Resources \u2192 Secrets) or an env var \u2014 pick the source per field.\")));\n}\n\n// The list of saved accounts (click to edit, delete).\nfunction AccountList({ accounts, onEdit, reload }) {\n function del(name) { api.call(\"DELETE\", \"/email/accounts/\" + encodeURIComponent(name)).then(reload); }\n return h(GlassPanel, { className: \"p-6 space-y-3\" },\n h(\"div\", { className: \"flex items-center justify-between\" },\n h(\"h3\", { className: \"font-semibold\" }, \"Accounts\"),\n h(\"span\", { className: \"text-xs text-muted\" }, accounts.length + \" configured\")),\n accounts.length === 0\n ? h(\"p\", { className: \"text-sm text-muted\" }, 'No accounts yet. Create one named \"default\" and sign-in links start emailing automatically.')\n : h(\"div\", { className: \"space-y-1\" }, accounts.map((a) =>\n h(\"div\", { key: a.name, className: \"flex items-center justify-between rounded-lg px-2 py-1.5 hover:bg-white/5\" },\n h(\"button\", { className: \"text-left text-sm flex-1 min-w-0\", onClick: () => onEdit(a) },\n h(Badge, { hue: a.name === \"default\" ? 140 : 200 }, a.name), \" \",\n h(\"span\", { className: \"font-mono text-xs text-muted\" }, a.provider + \" \u00B7 \" + a.from)),\n h(\"button\", { className: \"text-xs text-muted hover:text-[var(--color-neon-pink)] shrink-0\", onClick: () => del(a.name) }, \"Delete\")))));\n}\n\nexport default function EmailPage() {\n const blank = { name: \"\", provider: \"\", from: \"\", secrets: {}, options: {} };\n const [providers, setProviders] = React.useState([]);\n const [secrets, setSecrets] = React.useState([]);\n const [accounts, setAccounts] = React.useState([]);\n const [form, setForm] = React.useState(blank);\n const [test, setTest] = React.useState(null);\n\n const reloadAccounts = () => api.call(\"GET\", \"/email/accounts\").then((r) => setAccounts(arr(r, \"accounts\")));\n\n React.useEffect(() => {\n api.call(\"GET\", \"/email/providers\").then((r) => setProviders(arr(r, \"providers\"))).catch(() => {});\n api.call(\"GET\", \"/vault/secrets\").then((r) => setSecrets(arr(r, \"secrets\").map((s) => (typeof s === \"string\" ? s : s.name)))).catch(() => {});\n reloadAccounts().catch(() => {});\n }, []);\n\n function edit(a) { setForm({ name: a.name, provider: a.provider, from: a.from, secrets: { ...(a.secrets || {}) }, options: { ...(a.options || {}) } }); }\n function reloadAndReset() { reloadAccounts(); setForm(blank); }\n\n if (providers.length === 0) {\n return h(GlassPanel, { className: \"p-6 space-y-2\" },\n h(\"h3\", { className: \"font-semibold\" }, \"No email drivers installed\"),\n h(\"p\", { className: \"text-sm text-muted\" },\n \"mod-email is the contract; a driver mod does the sending. Install \",\n h(\"span\", { className: \"font-mono\" }, \"@pattern-js/mod-email-resend\"),\n \" or \", h(\"span\", { className: \"font-mono\" }, \"@pattern-js/mod-email-smtp\"),\n \" and list it in pattern.config.json, then reload this page.\"));\n }\n\n return h(\"div\", null,\n h(\"div\", { className: \"grid gap-6 lg:grid-cols-2\" },\n h(AccountForm, { providers, secrets, form, setForm, reload: reloadAndReset, onTest: (payload) => setTest({ payload }) }),\n h(AccountList, { accounts, onEdit: edit, reload: reloadAccounts })),\n h(TestModal, { state: test, setState: setTest }));\n}\n";
13
13
  //# sourceMappingURL=app.d.ts.map
package/dist/app.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,MAAM,y7VA+JlB,CAAC"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,MAAM,47VA+JlB,CAAC"}
package/dist/app.js CHANGED
@@ -113,7 +113,7 @@ function AccountForm({ providers, secrets, form, setForm, reload, onTest }) {
113
113
  h(NeonButton, { onClick: save, disabled: busy || !form.name || !form.from }, "Save account"),
114
114
  h(NeonButton, { onClick: () => onTest(payload()), disabled: busy || !form.from }, "Test"),
115
115
  form.name && h(NeonButton, { onClick: () => setForm(blank) }, "New")),
116
- h("p", { className: "text-xs text-muted" }, "Secrets come from the vault (System → Secrets) or an env var — pick the source per field.")));
116
+ h("p", { className: "text-xs text-muted" }, "Secrets come from the vault (Resources → Secrets) or an env var — pick the source per field.")));
117
117
  }
118
118
 
119
119
  // The list of saved accounts (click to edit, delete).
package/dist/mod.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAU3E,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAYD,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,UAAU,CAqBlE;AAED,gEAAgE;;AAChE,wBAA0B"}
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAW3E,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAYD,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,UAAU,CAsBlE;AAED,gEAAgE;;AAChE,wBAA0B"}
package/dist/mod.js CHANGED
@@ -19,6 +19,7 @@ import { accountOp } from "./ops/account.js";
19
19
  import { inboundOps } from "./ops/inbound.js";
20
20
  import { sendOp } from "./ops/send.js";
21
21
  import { deliverTokenWorkflow } from "./delivery.js";
22
+ import { alertFailedRunWorkflow } from "./alerts.js";
22
23
  import { emailAdminRoutes, emailFrontend, settingsOps } from "./settings.js";
23
24
  /** The packaged docs/ chapter (the `docs` contribution points at "email-docs"). */
24
25
  function packagedDocs(engine) {
@@ -40,8 +41,9 @@ export function emailMod(options = {}) {
40
41
  ops: [sendOp, accountOp, ...inboundOps, ...settingsOps],
41
42
  // The delivery workflow registers unconditionally: without mod-identity its
42
43
  // hook is auto-declared and never invoked (inert); with it, delivery starts
43
- // the moment a "default" account exists.
44
- workflows: [deliverTokenWorkflow(), ...emailAdminRoutes()],
44
+ // the moment a "default" account exists. Same posture for failure alerts:
45
+ // inert until PATTERN_ALERTS_TO is set.
46
+ workflows: [deliverTokenWorkflow(), alertFailedRunWorkflow(), ...emailAdminRoutes()],
45
47
  frontend: emailFrontend(),
46
48
  setup: (engine) => {
47
49
  packagedDocs(engine);
package/dist/mod.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mod.js","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,SAAS,EAAgC,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAO7E,mFAAmF;AACnF,SAAS,YAAY,CAAC,MAAc;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,IAAI,UAAU,CAAC,GAAG,CAAC;YAAE,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7E,CAAC;IAAC,MAAM,CAAC;QACP,gEAAgE;IAClE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,UAA2B,EAAE;IACpD,MAAM,MAAM,GAAG,IAAI,kBAAkB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAChD,OAAO,SAAS,CAAC;QACf,IAAI,EAAE,uBAAuB;QAC7B,IAAI,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;QAC7D,GAAG,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,UAAU,EAAE,GAAG,WAAW,CAAC;QACvD,4EAA4E;QAC5E,4EAA4E;QAC5E,yCAAyC;QACzC,SAAS,EAAE,CAAC,oBAAoB,EAAE,EAAE,GAAG,gBAAgB,EAAE,CAAC;QAC1D,QAAQ,EAAE,aAAa,EAAE;QACzB,KAAK,EAAE,CAAC,MAAc,EAAE,EAAE;YACxB,YAAY,CAAC,MAAM,CAAC,CAAC;YACrB,MAAM,CAAC,cAAc,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;YACpD,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC;QACD,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACtB,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,gEAAgE;AAChE,eAAe,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"mod.js","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,SAAS,EAAgC,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAO7E,mFAAmF;AACnF,SAAS,YAAY,CAAC,MAAc;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,IAAI,UAAU,CAAC,GAAG,CAAC;YAAE,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7E,CAAC;IAAC,MAAM,CAAC;QACP,gEAAgE;IAClE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,UAA2B,EAAE;IACpD,MAAM,MAAM,GAAG,IAAI,kBAAkB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAChD,OAAO,SAAS,CAAC;QACf,IAAI,EAAE,uBAAuB;QAC7B,IAAI,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;QAC7D,GAAG,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,UAAU,EAAE,GAAG,WAAW,CAAC;QACvD,4EAA4E;QAC5E,4EAA4E;QAC5E,0EAA0E;QAC1E,wCAAwC;QACxC,SAAS,EAAE,CAAC,oBAAoB,EAAE,EAAE,sBAAsB,EAAE,EAAE,GAAG,gBAAgB,EAAE,CAAC;QACpF,QAAQ,EAAE,aAAa,EAAE;QACzB,KAAK,EAAE,CAAC,MAAc,EAAE,EAAE;YACxB,YAAY,CAAC,MAAM,CAAC,CAAC;YACrB,MAAM,CAAC,cAAc,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;YACpD,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC;QACD,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACtB,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,gEAAgE;AAChE,eAAe,QAAQ,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/ops/account.ts"],"names":[],"mappings":"AAAA,qGAAqG;AAErG,OAAO,EAAY,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAK/D,eAAO,MAAM,SAAS,EAAE,YA2BvB,CAAC"}
1
+ {"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/ops/account.ts"],"names":[],"mappings":"AAAA,qGAAqG;AAErG,OAAO,EAAY,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAK/D,eAAO,MAAM,SAAS,EAAE,YA4BvB,CAAC"}
@@ -5,8 +5,9 @@ import { DEFAULT_ACCOUNT } from "../config.js";
5
5
  import { emailConfig, maybe } from "./shared.js";
6
6
  export const accountOp = {
7
7
  type: "email.account",
8
+ effects: "pure",
8
9
  title: "email.account",
9
- description: "Resolve a named email account (configured in admin → System → Email) to an account reference. " +
10
+ description: "Resolve a named email account (configured in admin → Resources → Email) to an account reference. " +
10
11
  "Re-pointing the account in admin instantly re-targets every workflow using it. Defaults to " +
11
12
  '"default". With required=false it probes instead of throwing: `configured` reports whether the ' +
12
13
  "account exists — the packaged delivery workflow branches on it.",
@@ -25,7 +26,7 @@ export const accountOp = {
25
26
  const name = (await maybe(ctx, "account")) ?? cfg.account;
26
27
  const ref = emailConfig(ctx).resolveAccount(name);
27
28
  if (!ref && cfg.required) {
28
- throw new Error(`email.account: no account "${name}" is configured — add it in admin → System → Email.`);
29
+ throw new Error(`email.account: no account "${name}" is configured — add it in admin → Resources → Email.`);
29
30
  }
30
31
  return { account: ref ?? null, configured: Boolean(ref) };
31
32
  },
@@ -1 +1 @@
1
- {"version":3,"file":"account.js","sourceRoot":"","sources":["../../src/ops/account.ts"],"names":[],"mappings":"AAAA,qGAAqG;AAErG,OAAO,EAAE,KAAK,EAAE,CAAC,EAAqB,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,CAAC,MAAM,SAAS,GAAiB;IACrC,IAAI,EAAE,eAAe;IACrB,KAAK,EAAE,eAAe;IACtB,WAAW,EACT,gGAAgG;QAChG,6FAA6F;QAC7F,iGAAiG;QACjG,iEAAiE;IACnE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;QACnD,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;KACpC,CAAC;IACF,YAAY,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;IAC5C,MAAM,EAAE,EAAE;IACV,OAAO,EAAE;QACP,OAAO,EAAE,KAAK,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC;QAC3C,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;KAC/B;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACrB,MAAM,GAAG,GAAG,GAAG,CAAC,MAAgD,CAAC;QACjE,MAAM,IAAI,GAAG,CAAC,MAAM,KAAK,CAAS,GAAG,EAAE,SAAS,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC;QAClE,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,qDAAqD,CAAC,CAAC;QAC3G,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;IAC5D,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"account.js","sourceRoot":"","sources":["../../src/ops/account.ts"],"names":[],"mappings":"AAAA,qGAAqG;AAErG,OAAO,EAAE,KAAK,EAAE,CAAC,EAAqB,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,CAAC,MAAM,SAAS,GAAiB;IACrC,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,MAAM;IACf,KAAK,EAAE,eAAe;IACtB,WAAW,EACT,mGAAmG;QACnG,6FAA6F;QAC7F,iGAAiG;QACjG,iEAAiE;IACnE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;QACnD,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;KACpC,CAAC;IACF,YAAY,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;IAC5C,MAAM,EAAE,EAAE;IACV,OAAO,EAAE;QACP,OAAO,EAAE,KAAK,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC;QAC3C,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;KAC/B;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACrB,MAAM,GAAG,GAAG,GAAG,CAAC,MAAgD,CAAC;QACjE,MAAM,IAAI,GAAG,CAAC,MAAM,KAAK,CAAS,GAAG,EAAE,SAAS,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC;QAClE,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,wDAAwD,CAAC,CAAC;QAC9G,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;IAC5D,CAAC;CACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,EAAmB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,KAAK,EAEV,YAAY,EAEZ,eAAe,EACf,eAAe,EAGf,mBAAmB,EACnB,YAAY,EACZ,SAAS,EACV,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,kFAAkF;IAClF,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IAC5C,OAAO,IAAI,eAAe,EAAE,CAAC;IAC7B,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IAChD,qFAAqF;IACrF,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC5D,mFAAmF;IACnF,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAClF;;;;;OAKG;IACH,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACjF,iGAAiG;IACjG,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAChG;AAED,qBAAa,mBAAoB,YAAW,YAAY;IAG1C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;gBAElC,MAAM,EAAE,kBAAkB;IAEvD,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI;IAI3C,OAAO,IAAI,eAAe,EAAE;IAI5B,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAIzC,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IAS3D,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IAqBjF,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA8ChF,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;YAStF,OAAO;IAcrB,8FAA8F;YAChF,cAAc;IAmB5B,4GAA4G;YAC9F,SAAS;YAoCT,iBAAiB;CAqBhC"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAA4B,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE5E,OAAO,EAAmB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,KAAK,EAEV,YAAY,EAEZ,eAAe,EACf,eAAe,EAGf,mBAAmB,EACnB,YAAY,EACZ,SAAS,EACV,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,kFAAkF;IAClF,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IAC5C,OAAO,IAAI,eAAe,EAAE,CAAC;IAC7B,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IAChD,qFAAqF;IACrF,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC5D,mFAAmF;IACnF,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAClF;;;;;OAKG;IACH,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACjF,iGAAiG;IACjG,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAChG;AAED,qBAAa,mBAAoB,YAAW,YAAY;IAG1C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;gBAElC,MAAM,EAAE,kBAAkB;IAEvD,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI;IAI3C,OAAO,IAAI,eAAe,EAAE;IAI5B,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAIzC,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IAW3D,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IAqBjF,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA8ChF,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;YAStF,OAAO;IA0BrB,8FAA8F;YAChF,cAAc;IAmB5B,4GAA4G;YAC9F,SAAS;YAoCT,iBAAiB;CAqBhC"}
package/dist/service.js CHANGED
@@ -8,7 +8,7 @@
8
8
  * secrets (vault/env), markdown → html+text, attachments → bytes, then the
9
9
  * driver. Secret VALUES never sit in workflow values or persisted config.
10
10
  */
11
- import { resolveSourced } from "@pattern-js/core";
11
+ import { noEffect, resolveSourced } from "@pattern-js/core";
12
12
  import { renderEmailMarkdown } from "./markdown.js";
13
13
  import { DEFAULT_ACCOUNT } from "./config.js";
14
14
  import { blobStore, STORE_SERVICE_KEY } from "./well-known.js";
@@ -31,7 +31,9 @@ export class DefaultEmailService {
31
31
  const name = typeof input.account === "string" ? input.account : (input.account?.account ?? DEFAULT_ACCOUNT);
32
32
  const account = this.config.account(name);
33
33
  if (!account) {
34
- throw new Error(`mod-email: no account "${name}" is configured — add it in admin → System → Email.`);
34
+ // Preflight failures never reach a provider — stamp them so durable
35
+ // resume re-runs the node without asking (see core's `noEffect`).
36
+ throw noEffect(new Error(`mod-email: no account "${name}" is configured — add it in admin → Resources → Email.`));
35
37
  }
36
38
  return this.sendVia(account, input, ctx);
37
39
  }
@@ -104,11 +106,22 @@ export class DefaultEmailService {
104
106
  async sendVia(account, input, ctx) {
105
107
  const driver = this.registry.get(account.provider);
106
108
  if (!driver) {
107
- throw new Error(`mod-email: account "${account.name}" uses provider "${account.provider}" but no such driver is registered — ` +
108
- `install its mod (e.g. @pattern-js/mod-email-${account.provider}) and list it in pattern.config.json.`);
109
+ throw noEffect(new Error(`mod-email: account "${account.name}" uses provider "${account.provider}" but no such driver is registered — ` +
110
+ `install its mod (e.g. @pattern-js/mod-email-${account.provider}) and list it in pattern.config.json.`));
111
+ }
112
+ // Everything up to `driver.send` is preflight: a missing secret, an empty
113
+ // recipient list, a missing body, an unknown blob — nothing left the
114
+ // process, so those errors carry the no-effect verdict. The driver call
115
+ // itself is the ambiguous zone (drivers classify what they can).
116
+ let message;
117
+ let creds;
118
+ try {
119
+ creds = await this.resolveSecrets(driver, account, ctx);
120
+ message = await this.normalize(input, account, ctx);
121
+ }
122
+ catch (err) {
123
+ throw err instanceof Error ? noEffect(err) : err;
109
124
  }
110
- const creds = await this.resolveSecrets(driver, account, ctx);
111
- const message = await this.normalize(input, account, ctx);
112
125
  const { messageId } = await driver.send(message, creds, account.options, ctx);
113
126
  return { messageId, provider: account.provider, account: account.name };
114
127
  }
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAOL,KAAK,oBAAoB,EAEzB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACd,MAAM,kBAAkB,CAAC;AAmG1B,eAAO,MAAM,WAAW,EAAE,YAAY,EAA8E,CAAC;AAIrH,wBAAgB,gBAAgB,IAAI,QAAQ,EAAE,CAU7C;AAED,wBAAgB,aAAa,IAAI,oBAAoB,CAMpD"}
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAOL,KAAK,oBAAoB,EAEzB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACd,MAAM,kBAAkB,CAAC;AAiH1B,eAAO,MAAM,WAAW,EAAE,YAAY,EAA8E,CAAC;AAIrH,wBAAgB,gBAAgB,IAAI,QAAQ,EAAE,CAU7C;AAED,wBAAgB,aAAa,IAAI,oBAAoB,CAMpD"}
package/dist/settings.js CHANGED
@@ -18,6 +18,7 @@ import { REMOTE } from "./app.js";
18
18
  // ───────────────────────────── Providers ──────────────────────────────
19
19
  const providersList = {
20
20
  type: "email.providers.list",
21
+ effects: "pure",
21
22
  title: "email.providers.list",
22
23
  description: "List the registered email drivers + their secret/option field specs (drives the account form).",
23
24
  reusable: false,
@@ -42,10 +43,21 @@ async function readAccountFields(ctx) {
42
43
  maybe(ctx, "secrets"),
43
44
  maybe(ctx, "options"),
44
45
  ]);
45
- return accountSchema.parse({ name, provider, from, secrets: secrets ?? {}, options: options ?? {} });
46
+ // A partial write — `secrets` or `options` not wired / not in the body —
47
+ // keeps what the account already has. An omitted field must never wipe a
48
+ // secret ref; sending an explicit `{}` is how you clear one.
49
+ const existing = emailConfig(ctx).account(name);
50
+ return accountSchema.parse({
51
+ name,
52
+ provider,
53
+ from,
54
+ secrets: secrets ?? existing?.secrets ?? {},
55
+ options: options ?? existing?.options ?? {},
56
+ });
46
57
  }
47
58
  const accountsRead = {
48
59
  type: "email.accounts.read",
60
+ effects: "pure",
49
61
  title: "email.accounts.read",
50
62
  description: "List the configured email accounts (secret NAMES/sources only, never values).",
51
63
  reusable: false,
@@ -57,6 +69,7 @@ const accountsRead = {
57
69
  };
58
70
  const accountsWrite = {
59
71
  type: "email.accounts.write",
72
+ effects: "idempotent",
60
73
  title: "email.accounts.write",
61
74
  description: "Create or update an email account (upsert by name).",
62
75
  reusable: false,
@@ -72,6 +85,7 @@ const accountsWrite = {
72
85
  };
73
86
  const accountsDelete = {
74
87
  type: "email.accounts.delete",
88
+ effects: "idempotent",
75
89
  title: "email.accounts.delete",
76
90
  description: "Delete an email account by name.",
77
91
  reusable: false,
@@ -114,7 +128,7 @@ export function emailAdminRoutes() {
114
128
  }
115
129
  export function emailFrontend() {
116
130
  return {
117
- menu: [{ category: "System", label: "Email", icon: "mail", path: "/x/email/accounts", order: 21 }],
131
+ menu: [{ category: "Resources", label: "Email", icon: "mail", path: "/x/email/accounts", order: 50 }],
118
132
  // The Tier-2 page is just its source; the admin serves + imports it (no workflow).
119
133
  pages: [{ path: "/x/email/accounts", title: "Email", module: REMOTE }],
120
134
  };
@@ -1 +1 @@
1
- {"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -88,7 +88,7 @@ export interface EmailMessage {
88
88
  /**
89
89
  * A driver, registered by a provider mod (`mod-email-resend`, `mod-email-smtp`,
90
90
  * …) via `EmailService.registerDriver` in its `ready()`. The `secrets`/`options`
91
- * field lists drive the auto-generated account form in admin → System → Email.
91
+ * field lists drive the auto-generated account form in admin → Resources → Email.
92
92
  * (Field descriptors use `field` for the key — this mod's admin page is new
93
93
  * code, so it doesn't inherit mod-ai's `name` naming for the same idea.)
94
94
  */
@@ -21,9 +21,9 @@ SMTP at the "Sign-in link delivery?" question.
21
21
 
22
22
  ## 2. Create the `default` account
23
23
 
24
- Admin → **System → Email** → new account named `default`:
24
+ Admin → **Resources → Email** → new account named `default`:
25
25
 
26
- - **Resend**: the API key, from the vault (System → Secrets) or an env var
26
+ - **Resend**: the API key, from the vault (Resources → Secrets) or an env var
27
27
  such as `RESEND_API_KEY`. The From address must be on a domain you verified
28
28
  with Resend.
29
29
  - **SMTP**: host (+ port/TLS/username as options) and the password as the one
package/docs/index.md CHANGED
@@ -17,7 +17,7 @@ The one persisted concept is the **account** — a memorable name bound to a
17
17
  driver, a From address, sourced secrets and driver options. Two accounts of
18
18
  the same driver with different credentials are just two records (prod + a
19
19
  staging relay, a marketing sender + an alerts sender). Manage them in admin →
20
- **System → Email**; the form generates itself from whatever fields the
20
+ **Resources → Email**; the form generates itself from whatever fields the
21
21
  registered drivers declare, and the **Test** button sends a real email so you
22
22
  verify end-to-end delivery, not a wiring check.
23
23
 
@@ -55,6 +55,25 @@ anyway. Open the workflow in the admin editor to reword the email (two
55
55
  `core.string.template` nodes) or fork it entirely — see the
56
56
  [delivery guide](guides/delivery.md).
57
57
 
58
+ ## Failure alerts (0.5)
59
+
60
+ A run fails, you get an email. The engine emits one `run.failed` event per
61
+ failed top-level run (sub-run failures fold into their parent; cancels don't
62
+ count), and this mod ships **`email.alert-failed-run`** subscribed to it: set
63
+ `PATTERN_ALERTS_TO=ops@example.com` and every failure lands in that inbox
64
+ with the workflow name, the error, and a deep link to the run's trace
65
+ (`PATTERN_PUBLIC_URL` shapes the link; durable runs can resume right from
66
+ that page). Unset, the workflow gates out silently — installing mod-email
67
+ changes nothing until you opt in. It's an ordinary editable workflow: open it
68
+ to reword the copy, alert only on certain workflows, or reroute to another
69
+ channel. A failing alert can't alert about itself — the engine's recursion
70
+ guard skips runs that `run.failed` itself triggered.
71
+
72
+ A `core.flow.throttle` node keyed on the workflow id holds the mail to one
73
+ email per failing workflow every 10 minutes, so a route that fails on every
74
+ request (or someone hammering one) costs one email. Change `max` and `window`
75
+ on that node to taste.
76
+
58
77
  ## Writing a driver
59
78
 
60
79
  A driver mod registers an `EmailDriverSpec` on the email service in `ready()`:
@@ -1 +1 @@
1
- Resolve an account name (configured in admin → System → Email) to an account reference for `email.send` — the ref carries the NAME, never the credentials, so re-pointing the account in admin instantly re-targets every workflow using it. Defaults to `"default"`. Two modes: `required: true` (the default) throws with an admin hint when the account is missing — the right failure for a workflow that must send; `required: false` probes instead, outputting `account: null` + `configured: false` so a `core.flow.branch` can take a fallback path — exactly how the packaged `email.deliver-token` workflow decides between emailing a sign-in link and leaving the console fallback alone.
1
+ Resolve an account name (configured in admin → Resources → Email) to an account reference for `email.send` — the ref carries the NAME, never the credentials, so re-pointing the account in admin instantly re-targets every workflow using it. Defaults to `"default"`. Two modes: `required: true` (the default) throws with an admin hint when the account is missing — the right failure for a workflow that must send; `required: false` probes instead, outputting `account: null` + `configured: false` so a `core.flow.branch` can take a fallback path — exactly how the packaged `email.deliver-token` workflow decides between emailing a sign-in link and leaving the console fallback alone.
@@ -0,0 +1,12 @@
1
+ Proves an account works by sending a real email: a short "It works" message
2
+ with subject "Pattern test email" goes to the `to` address through the
3
+ account draft you pass (same fields as `email.accounts.write`). The draft is
4
+ not saved, so you can test before committing; omitted `secrets` or `options`
5
+ fall back to the saved account of that name. The result is `{ ok: true,
6
+ messageId? }` or `{ ok: false, detail }` with the driver's error message,
7
+ and the op itself does not throw.
8
+
9
+ It sends for real because for email only end-to-end delivery means
10
+ anything; use an address you control, and expect it to count against the
11
+ provider's quota. Privileged: served at `POST /admin/api/email/test` behind
12
+ the `admin` scope.
@@ -0,0 +1,7 @@
1
+ Remove an email account by `name` (the admin route takes it from the path:
2
+ `DELETE /admin/api/email/accounts/:name`). Deleting a name that does not
3
+ exist still answers `{ ok: true }`. The vault secrets the account pointed at
4
+ stay where they are. Workflows that name the account fail at their next
5
+ `email.send` with a "no account configured" error, and `email.account` with
6
+ `required: false` starts reporting `configured: false`. Privileged: gate the
7
+ trigger with the `admin` scope.
@@ -0,0 +1,6 @@
1
+ Every configured email account as stored: `{ name, provider, from, secrets,
2
+ options }`. `secrets` maps each field to its source (`{ source: "vault" |
3
+ "env", key }`), so the output names where a credential lives and never holds
4
+ its value. Backs the admin Email page at `GET /admin/api/email/accounts`.
5
+ Privileged: gate the trigger with the `admin` scope. To turn one name into a
6
+ ref a workflow can send through, use `email.account`.
@@ -0,0 +1,13 @@
1
+ Create or update an email account, upserted by `name`. Requires `name`,
2
+ `provider` (a driver id from `email.providers.list`) and `from` (for example
3
+ `App <hello@example.com>`); `secrets` is an object of `{ source, key }` refs
4
+ and `options` an object of strings. A field you leave out keeps the saved
5
+ value, so re-saving `from` never wipes a secret ref; send `{}` to clear one.
6
+ Idempotent.
7
+
8
+ Name the main account `default`: the packaged delivery workflow and
9
+ `email.account` fall back to it. Workflows hold the account name, so editing
10
+ it here re-targets every sender at the next send. Accounts persist in
11
+ `.pattern-data/email-config.json` (in memory only on a read-only filesystem).
12
+ Privileged: the admin route carries the `admin` scope. Prove a draft with
13
+ `email.account.test` before saving it.
@@ -0,0 +1,9 @@
1
+ The email drivers registered in this installation, each as `{ id, label,
2
+ secrets, options }` where `secrets` and `options` list field descriptors
3
+ (`{ field, label, required? }`; option fields may add a `placeholder`). The admin Email page builds its
4
+ account form from this, at `GET /admin/api/email/providers` behind the `admin`
5
+ scope. A driver appears once its mod (`@pattern-js/mod-email-resend`,
6
+ `@pattern-js/mod-email-smtp`, or your own `registerDriver` call) is
7
+ installed; an empty list means nothing can send yet. Use the field names here
8
+ as the keys of an account's `secrets` and `options` when you call
9
+ `email.accounts.write`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pattern-js/mod-email",
3
- "version": "0.4.0",
3
+ "version": "0.5.0-main.79cf721",
4
4
  "description": "Transactional email for Pattern — the contract mod. Admin-configured accounts (provider + sourced secrets + from), driver mods plug in (Resend, SMTP, …), one email.send op, and a packaged workflow that delivers identity sign-in links the moment a default account exists.",
5
5
  "keywords": [
6
6
  "pattern",
@@ -26,15 +26,15 @@
26
26
  "README.md"
27
27
  ],
28
28
  "dependencies": {
29
- "zod": "^4.0.0",
30
- "@pattern-js/core": "0.4.0",
31
- "@pattern-js/runtime-node": "0.4.0"
29
+ "zod": "^4.6.7",
30
+ "@pattern-js/core": "0.5.0-main.79cf721",
31
+ "@pattern-js/runtime-node": "0.5.0-main.79cf721"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^22.0.0",
35
35
  "typescript": "^5.6.0",
36
- "@pattern-js/mod-auth-magic-link": "0.4.0",
37
- "@pattern-js/mod-identity": "0.4.0"
36
+ "@pattern-js/mod-auth-magic-link": "0.5.0-main.79cf721",
37
+ "@pattern-js/mod-identity": "0.5.0-main.79cf721"
38
38
  },
39
39
  "engines": {
40
40
  "node": ">=22.5"