From c312a4c8080ab4bc22804dcaa981f50b26341734 Mon Sep 17 00:00:00 2001 From: Wayne Sutton Date: Fri, 26 Dec 2025 22:14:04 -0800 Subject: [PATCH] updated anthropic production no key warning message --- ...il_newsletter_integration_03119c86.plan.md | 92 ++++++++----------- AGENTS.md | 2 +- convex/aiChatActions.ts | 2 +- public/llms.txt | 2 +- 4 files changed, 42 insertions(+), 56 deletions(-) diff --git a/.cursor/plans/agentmail_newsletter_integration_03119c86.plan.md b/.cursor/plans/agentmail_newsletter_integration_03119c86.plan.md index 336c87b..8f35539 100644 --- a/.cursor/plans/agentmail_newsletter_integration_03119c86.plan.md +++ b/.cursor/plans/agentmail_newsletter_integration_03119c86.plan.md @@ -86,22 +86,20 @@ Integrate AgentMail as an optional newsletter system with email subscriptions, a flowchart TD User[User] -->|Subscribe| NewsletterForm[NewsletterSignup Component] NewsletterForm -->|Mutation| ConvexDB[(Convex Database)] - + NewPost[New Blog Post Published] -->|Trigger| SendScript[Send Newsletter Script] SendScript -->|Query Subscribers| ConvexDB SendScript -->|Send Emails| AgentMail[AgentMail API] AgentMail -->|Deliver| Subscribers[Subscriber Inboxes] - + EmailIn[Email to Inbox] -->|Webhook| AgentMail AgentMail -->|Webhook| ConvexWebhook[Convex Webhook Handler] ConvexWebhook -->|Create Draft| ConvexDB - + ContactForm[Contact Form] -->|Send Email| AgentMail AgentMail -->|Notify| Developer[Developer Email] ``` - - ## Implementation Steps ### Step 1: Database Schema Updates @@ -146,8 +144,6 @@ emailDrafts: defineTable({ .index("by_createdAt", ["createdAt"]), ``` - - ### Step 2: Site Configuration **File:** `src/config/siteConfig.ts`Add newsletter configuration interface and default config: @@ -166,7 +162,7 @@ export interface NewsletterConfig { // Form field configuration requireName: boolean; // If true, show name field; if false, email only showNameOptional: boolean; // If true, show name as optional field - + // Home page signup home: { enabled: boolean; @@ -174,7 +170,7 @@ export interface NewsletterConfig { title: string; description: string; }; - + // Blog post signup (not pages) posts: { enabled: boolean; @@ -182,7 +178,7 @@ export interface NewsletterConfig { title: string; description: string; }; - + // Dedicated newsletter page page: { enabled: boolean; @@ -195,7 +191,7 @@ export interface NewsletterConfig { // Auto-send new blog posts autoSendNewPosts: boolean; // If true, automatically send when post published sendOnSync: boolean; // Send during npm run sync if new post detected - + // Email template fromName: string; fromEmail: string; // Uses inboxUsername@inboxDomain @@ -294,8 +290,6 @@ export const siteConfig: SiteConfig = { }; ``` - - ### Step 3: Newsletter Signup Component **File:** `src/components/NewsletterSignup.tsx`Create reusable signup form component matching existing UI patterns: @@ -416,22 +410,31 @@ export default function NewsletterSignup({ } ``` - - ### Step 4: Convex Newsletter Functions **File:** `convex/newsletter.ts`Create backend functions following Convex best practices: ```typescript -import { query, mutation, internalMutation, internalAction, action } from "./_generated/server"; +import { + query, + mutation, + internalMutation, + internalAction, + action, +} from "./_generated/server"; import { v } from "convex/values"; import { internal } from "./_generated/api"; import crypto from "crypto"; // Generate unsubscribe token function generateUnsubscribeToken(email: string): string { - const secret = process.env.UNSUBSCRIBE_SECRET || "default-secret-change-in-production"; - return crypto.createHash("sha256").update(email + secret).digest("hex").substring(0, 32); + const secret = + process.env.UNSUBSCRIBE_SECRET || "default-secret-change-in-production"; + return crypto + .createHash("sha256") + .update(email + secret) + .digest("hex") + .substring(0, 32); } // Subscribe to newsletter @@ -508,7 +511,7 @@ export const unsubscribe = mutation({ }), handler: async (ctx, args) => { const email = args.email.toLowerCase().trim(); - + const subscriber = await ctx.db .query("newsletterSubscribers") .withIndex("by_email", (q) => q.eq("email", email)) @@ -584,7 +587,7 @@ export const getActiveSubscribers = internalQuery({ .query("newsletterSubscribers") .withIndex("by_subscribed", (q) => q.eq("subscribed", true)) .collect(); - + return subscribers.map((sub) => ({ email: sub.email, name: sub.name, @@ -636,9 +639,12 @@ export const sendPostNewsletter = internalAction({ }), handler: async (ctx, args) => { // Check if already sent - const alreadySent = await ctx.runQuery(internal.newsletter.hasPostBeenSent, { - postSlug: args.postSlug, - }); + const alreadySent = await ctx.runQuery( + internal.newsletter.hasPostBeenSent, + { + postSlug: args.postSlug, + }, + ); if (alreadySent) { return { @@ -662,7 +668,9 @@ export const sendPostNewsletter = internalAction({ } // Get subscribers - const subscribers = await ctx.runQuery(internal.newsletter.getActiveSubscribers); + const subscribers = await ctx.runQuery( + internal.newsletter.getActiveSubscribers, + ); if (subscribers.length === 0) { return { @@ -681,7 +689,7 @@ export const sendPostNewsletter = internalAction({ // Build email content const siteUrl = process.env.SITE_URL || "https://markdown.fast"; const postUrl = `${siteUrl}/${post.slug}`; - + let emailContent = `

${post.title}

`; if (post.description) { emailContent += `

${post.description}

`; @@ -743,9 +751,9 @@ export const getPostBySlugInternal = internalQuery({ .query("posts") .withIndex("by_slug", (q) => q.eq("slug", args.slug)) .first(); - + if (!post) return null; - + return { slug: post.slug, title: post.title, @@ -770,8 +778,6 @@ export const getPostBySlugInternal = internalQuery({ }); ``` - - ### Step 5: Newsletter Send Script **File:** `scripts/send-newsletter.ts`Create script to send newsletters (similar to `sync-posts.ts`): @@ -816,8 +822,6 @@ const postSlug = args[0]; sendNewsletterForPost(postSlug); ``` - - ### Step 6: Auto-Send Integration with Sync Script **File:** `scripts/sync-posts.ts`Modify sync script to detect new posts and auto-send if enabled: @@ -834,9 +838,7 @@ async function checkAndSendNewPosts( if (!autoSend) return; // Get all synced post slugs - const syncedSlugs = syncedPosts - .filter((p) => p.published) - .map((p) => p.slug); + const syncedSlugs = syncedPosts.filter((p) => p.published).map((p) => p.slug); // Check which posts haven't been sent yet for (const slug of syncedSlugs) { @@ -862,8 +864,6 @@ async function checkAndSendNewPosts( await checkAndSendNewPosts(parsedPosts, client); ``` - - ### Step 7: Newsletter Page **File:** `content/pages/newsletter.md`Create dedicated newsletter signup page: @@ -900,8 +900,6 @@ if (page && page.slug === siteConfig.newsletter.signup.page.slug) { } ``` - - ### Step 8: Unsubscribe Page **File:** `src/pages/Unsubscribe.tsx`Create unsubscribe page: @@ -973,8 +971,6 @@ export default function Unsubscribe() { } /> ``` - - ### Step 9: Integration Points **File:** `src/pages/Home.tsx`Add newsletter signup above footer: @@ -1014,8 +1010,6 @@ export default function Unsubscribe() { ``` - - ### Step 10: Webhook Handler (Future Email-to-Post) **File:** `convex/webhooks.ts`Create webhook handler for AgentMail events: @@ -1030,7 +1024,7 @@ export const agentmailWebhook = httpAction(async (ctx, request) => { // Add signature verification logic here const body = await request.json(); - + // Handle different event types if (body.type === "message.received") { // Process incoming email for email-to-post workflow @@ -1068,7 +1062,7 @@ export const handleIncomingEmail = internalMutation({ const subjectLower = args.subject.toLowerCase(); const isDraft = subjectLower.includes("[draft]"); const isPublish = subjectLower.includes("[publish]"); - + const status = isPublish ? "published" : isDraft ? "draft" : "draft"; // Create draft post @@ -1097,8 +1091,6 @@ http.route({ }); ``` - - ### Step 11: Package.json and Environment Variables **File:** `package.json`Add newsletter scripts: @@ -1124,8 +1116,6 @@ AGENTMAIL_FROM_EMAIL=newsletter@agentmail.to UNSUBSCRIBE_SECRET=change-this-in-production ``` - - ### Step 12: CSS Styling **File:** `src/styles/global.css`Add newsletter component styles matching existing theme: @@ -1213,8 +1203,6 @@ UNSUBSCRIBE_SECRET=change-this-in-production } ``` - - ### Step 13: Fork Configuration **File:** `fork-config.json.example`Add newsletter configuration: @@ -1274,8 +1262,6 @@ The newsletter feature is optional and disabled by default. To enable: See `prds/agentmailplan-v1.md` for full documentation. ``` - - ## Future Features (Not in Initial Implementation) These features are planned but not included in steps 1-11: @@ -1298,4 +1284,4 @@ These can be added incrementally after the core newsletter functionality is work - [ ] Subscription saves to Convex database - [ ] Unsubscribe link works with token verification - [ ] `npm run newsletter:send -- ` sends email to all subscribers -- [ ] Auto-send works during `npm run sync` if enabled \ No newline at end of file +- [ ] Auto-send works during `npm run sync` if enabled diff --git a/AGENTS.md b/AGENTS.md index ff834f1..7d8e73f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,7 +22,7 @@ Your content is instantly available to browsers, LLMs, and AI agents.. Write mar - **Total Posts**: 12 - **Total Pages**: 4 - **Latest Post**: 2025-12-25 -- **Last Updated**: 2025-12-26T20:30:35.290Z +- **Last Updated**: 2025-12-27T06:13:31.118Z ## Tech stack diff --git a/convex/aiChatActions.ts b/convex/aiChatActions.ts index 56ef9ab..76311f7 100644 --- a/convex/aiChatActions.ts +++ b/convex/aiChatActions.ts @@ -109,7 +109,7 @@ export const generateResponse = action({ const apiKey = process.env.ANTHROPIC_API_KEY; if (!apiKey) { const notConfiguredMessage = - "**AI chat is not configured.**\n\n" + + "**AI chat is not configured on production.**\n\n" + "To enable AI responses, add your `ANTHROPIC_API_KEY` to the Convex environment variables.\n\n" + "**Setup steps:**\n" + "1. Get an API key from [Anthropic Console](https://console.anthropic.com/)\n" + diff --git a/public/llms.txt b/public/llms.txt index 513d627..4b90f0f 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -1,6 +1,6 @@ # llms.txt - Information for AI assistants and LLMs # Learn more: https://llmstxt.org/ -# Last updated: 2025-12-26T20:30:35.292Z +# Last updated: 2025-12-27T06:13:31.119Z > Your content is instantly available to browsers, LLMs, and AI agents.