Data Flow
What happens during one daily agent run
launchd, or you manually, runsrun-agent.sh, which setsPATH, loads.env, and callsnpx tsx agents/news/run.ts.run.tscreates aNewsRunLogrow in Postgres with statusRUNNING, so the dashboard has something to show even if the run later fails partway.run.tsloads theAgentConfigrow from Postgres: topics, minimum score, and max articles. If this is the very first run ever, it creates that row using the defaults fromagents/news/config.ts.fetchFeeds.tsdownloads each RSS feed URL and parses out title, link, publish date, and a short snippet for every article.dedupe.tscompares article titles to each other and drops near-duplicates.run.tschecks which of the remaining articles already exist in theArticletable, matched by their source URL, and skips those so reruns do not waste AI time reprocessing old stories.classify.tssends each new article to the AI model, asking for category, companies, keywords, an importance score, and sentiment, as JSON. If the AI's answer is not valid JSON, it is asked once more, more strictly.rank.tsfilters out anything matching an excluded topic, filters out anything not matching an included topic when any are set, filters out anything below the minimum score, sorts by score, and keeps only the configured maximum number.generateContent.tsasks the AI model to write one Twitter/X post and one LinkedIn post for each surviving article, using the prompt templates currently stored in thePromptTemplatetable.run.tssaves each article and its generated posts to Postgres, withGeneratedContentrows starting inDRAFTstatus.run.tsupdates theNewsRunLogrow with final counts andSUCCESS, orFAILEDwith the error message if something went wrong.
What happens when you use the dashboard
- Logging in: you submit your email and password on
/login. NextAuth checks the email againstADMIN_EMAILand the password againstADMIN_PASSWORD_HASHusing bcrypt, and if they match, sets a signed session cookie in your browser. Every dashboard page and API route checks for a valid session before doing anything. - Viewing Articles: the page calls
GET /api/articles, which readsArticlerows and their relatedGeneratedContentrows straight from Postgres and returns them as JSON, which the page then renders into a table. - Reviewing drafts: the Review Queue page calls
GET /api/generated-content, defaulting to statusDRAFT, to show what needs a decision. Clicking Approve or Reject callsPATCH /api/generated-content/{id}with the new status, which updates that one row. - Publishing: only shown for
APPROVEDposts. Clicking Publish callsPOST /api/generated-content/{id}/publish, which looks up the real platform, Twitter or LinkedIn, and calls the matching function inlib/social/, using whatever credentials are currently in that environment's variables. - If publishing succeeds, the row is updated to
PUBLISHEDwith the real post's link. If it fails, including simply not being configured yet, the exact error is saved to that row and shown directly in the dashboard rather than a generic failure message. - Editing Agent Config: the Config page loads the current
AgentConfigrow viaGET /api/agent-configand saves changes viaPUTto the same route. This does not affect anything until the next time the local agent runs and reads that row fresh. - Editing Prompts: works the same way but never overwrites. Each save creates a new
PromptTemplaterow with an incremented version number, and the agent always reads whichever version is highest.