Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 60 additions & 44 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
"type": "module",
"cloudflare": {
"label": "Agentic Inbox",
"products": ["Workers", "Durable Objects", "R2", "Workers AI"],
"products": [
"Workers",
"Durable Objects",
"R2",
"Workers AI"
],
"bindings": {
"DOMAINS": {
"description": "Your domain with [Email Routing](https://developers.cloudflare.com/email-routing/) enabled (e.g. `example.com`). For multiple domains, pass a comma-separated list (e.g. `example.com,another.com`). After deploying, create a catch-all Email Routing rule pointing to this Worker for each domain."
Expand Down Expand Up @@ -50,7 +55,7 @@
"react-router": "^7.18.0",
"remark-gfm": "^4.0.1",
"workers-ai-provider": "^3.2.0",
"zod": "^3.25.76",
"zod": "^4.4.3",
"zustand": "^5.0.14"
},
"overrides": {
Expand Down Expand Up @@ -88,11 +93,11 @@
"@react-router/dev": "^7.18.0",
"@tailwindcss/vite": "^4.3.1",
"@types/dompurify": "^3.0.5",
"@types/node": "^20.19.37",
"@types/node": "^26.0.1",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.1.2",
"tailwindcss": "^4.3.1",
"typescript": "^5.8.3",
"typescript": "^6.0.3",
"vite": "^6.3.3",
"vite-tsconfig-paths": "^5.1.4",
"wrangler": "^4.101.0"
Expand Down
1 change: 1 addition & 0 deletions tsconfig.cloudflare.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"module": "ES2022",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"ignoreDeprecations": "6.0",
"baseUrl": ".",
"rootDirs": [".", "./.react-router/types"],
"paths": {
Expand Down
2 changes: 1 addition & 1 deletion workers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type AppContext = Context<MailboxContext>;
const CreateMailboxBody = z.object({
email: z.string().email(),
name: z.string().min(1),
settings: z.record(z.any()).optional(), // unvalidated — agentSystemPrompt goes straight to AI
settings: z.record(z.string(), z.any()).optional(), // unvalidated — agentSystemPrompt goes straight to AI
});

const DraftBody = z.object({
Expand Down
2 changes: 1 addition & 1 deletion workers/lib/ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export async function isPromptInjection(ai: Ai, bodyHtml: string | null | undefi

try {
const response = (await ai.run(
// @ts-expect-error — model string not in generated union

"@cf/meta/llama-3.1-8b-instruct-fast",
{
messages: [
Expand Down
6 changes: 3 additions & 3 deletions workers/lib/resendKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function domainFromAddress(

/** Parse `RESEND_DOMAIN_KEYS` into a domain→prefix map. Malformed/empty → {}. */
export function parseResendDomainKeys(env: Env): Record<string, string> {
const raw = (env as DynamicEnv).RESEND_DOMAIN_KEYS;
const raw = (env as unknown as DynamicEnv).RESEND_DOMAIN_KEYS;
if (!raw) return {};
try {
const parsed = JSON.parse(raw) as unknown;
Expand Down Expand Up @@ -78,7 +78,7 @@ export function resolveResendApiKey(
const domain = domainFromAddress(from);
const prefix = parseResendDomainKeys(env)[domain];
if (prefix) {
const key = (env as DynamicEnv)[`${prefix}_API_KEY`];
const key = (env as unknown as DynamicEnv)[`${prefix}_API_KEY`];
if (key) return key;
console.error(
`RESEND_DOMAIN_KEYS maps ${domain} → ${prefix} but ${prefix}_API_KEY is unset; falling back to RESEND_API_KEY`,
Expand All @@ -97,7 +97,7 @@ export function resendWebhookSecrets(env: Env): string[] {
const secrets: string[] = [];
if (env.RESEND_WEBHOOK_SECRET) secrets.push(env.RESEND_WEBHOOK_SECRET);
for (const prefix of Object.values(parseResendDomainKeys(env))) {
const s = (env as DynamicEnv)[`${prefix}_WEBHOOK_SECRET`];
const s = (env as unknown as DynamicEnv)[`${prefix}_WEBHOOK_SECRET`];
if (s) secrets.push(s);
}
return secrets;
Expand Down
2 changes: 1 addition & 1 deletion workers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface Env extends Cloudflare.Env {
// verification) — both Worker secrets. Unmapped domains use RESEND_API_KEY /
// RESEND_WEBHOOK_SECRET. The dynamically-named secrets are not declared here;
// see lib/resendKeys.ts. Leave unset for a single shared Resend account.
RESEND_DOMAIN_KEYS?: string;
RESEND_DOMAIN_KEYS: Cloudflare.Env["RESEND_DOMAIN_KEYS"];
// NOTE: the `DELIVERY_MAP` KV binding (Resend `re_…` id → {mailboxId,
// emailId, threadId}) is declared in wrangler.jsonc and therefore generated
// into Cloudflare.Env by `wrangler types` — do not redeclare it here.
Expand Down