34 const fetchExistingLog = async () => {
35 try {
36 const response = await fetch(`/api/daily-logs/${date}`);
37
38 if (response.ok) {
111
112 try {
113 const response = await fetch('/api/daily-logs', {
114 method: 'POST',
115 headers: {
131 // Trigger email
132 try {
133 await fetch('/api/send-daily-email', {
134 method: 'POST'
135 });
156
157 try {
158 const response = await fetch('/api/daily-standup', {
159 method: 'POST'
160 });
30 try {
31 // Try to get user status
32 const response = await fetch('/api/user-status');
33
34 if (response.status === 404) {
56 try {
57 setIsLoading(true);
58 const response = await fetch('/api/migrate', {
59 method: 'POST',
60 headers: {
79 async function fetchInfo() {
80 try {
81 const response = await fetch('/api/info');
82 if (response.ok) {
83 const data = await response.json();
26 // Fetch track and tasks in parallel
27 const [trackResponse, tasksResponse] = await Promise.all([
28 fetch(`/api/tracks`),
29 fetch(`/api/tracks/${trackId}/tasks`)
30 ]);
31
55 const fetchTrackPrompt = async () => {
56 try {
57 const response = await fetch(`/api/tracks/${trackId}/prompt`);
58
59 if (response.ok) {
72
73 try {
74 const response = await fetch(`/api/tracks/${trackId}/guidance`, {
75 method: 'POST'
76 });
93 const handleTaskStatusChange = async (taskId, newStatus) => {
94 try {
95 const response = await fetch(`/api/tasks/${taskId}/status`, {
96 method: 'PUT',
97 headers: {
119 const handleTaskNotesChange = async (taskId, notes) => {
120 try {
121 const response = await fetch(`/api/tasks/${taskId}/notes`, {
122 method: 'PUT',
123 headers: {
145 const handleUpdateTrackStatus = async (newStatus) => {
146 try {
147 const response = await fetch(`/api/tracks/${trackId}`, {
148 method: 'PUT',
149 headers: {
170 const handlePromptUpdate = async () => {
171 try {
172 const response = await fetch(`/api/tracks/${trackId}/prompt`, {
173 method: 'PUT',
174 headers: {
33
34- ValTown SQLite for data storage
35- Hono framework for API endpoints
36- Vercel AI SDK for AI interactions using Gemini models
37- Val Town email service for notifications
524. Perform weekly reviews every Sunday
53
54## API Endpoints
55
56- `/api/migrate` - Run database migrations
57- `/api/tracks` - Get and update tracks
58- `/api/tasks` - Manage tasks
59- `/api/daily-logs` - Daily progress tracking
60- `/api/weekly-reviews` - Weekly strategic reviews
61- `/api/user-status` - Personal situation management
62- `/api/track-prompts` - Customizable AI prompts
63- `/api/quick-guidance` - Get immediate AI guidance
64- `/api/send-daily-email` - Trigger daily email notifications
65- `/api/send-weekly-email` - Trigger weekly email notifications
66
67## AI Guidance Features
80
81 try {
82 const response = await fetch('/api/user-status', {
83 method: 'POST',
84 headers: {
111 const handleSendEmail = async (type) => {
112 try {
113 const endpoint = type === 'daily' ? '/api/send-daily-email' : '/api/send-weekly-email';
114 const response = await fetch(endpoint, {
115 method: 'POST'
24 // Fetch tracks and tasks in parallel
25 const [tracksResponse, tasksResponse] = await Promise.all([
26 fetch('/api/tracks'),
27 fetch('/api/tasks')
28 ]);
29
48 const handleTaskStatusChange = async (taskId, newStatus) => {
49 try {
50 const response = await fetch(`/api/tasks/${taskId}/status`, {
51 method: 'PUT',
52 headers: {
74 const handleTaskNotesChange = async (taskId, notes) => {
75 try {
76 const response = await fetch(`/api/tasks/${taskId}/notes`, {
77 method: 'PUT',
78 headers: {
97
98export async function sendDailyBriefing(chatId?: string, today?: DateTime) {
99 // Get API keys from environment
100 const apiKey = Deno.env.get("ANTHROPIC_API_KEY");
101 const telegramToken = Deno.env.get("TELEGRAM_TOKEN");
102
106 }
107
108 if (!apiKey) {
109 console.error("Anthropic API key is not configured.");
110 return;
111 }
122
123 // Initialize Anthropic client
124 const anthropic = new Anthropic({ apiKey });
125
126 // Initialize Telegram bot
162
163 // disabled title for now, it seemes unnecessary...
164 // await bot.api.sendMessage(chatId, `*${title}*`, { parse_mode: "Markdown" });
165
166 // Then send the main content
169
170 if (content.length <= MAX_LENGTH) {
171 await bot.api.sendMessage(chatId, content, { parse_mode: "Markdown" });
172 // Store the briefing in chat history
173 await storeChatMessage(
198 // Send each chunk as a separate message and store in chat history
199 for (const chunk of chunks) {
200 await bot.api.sendMessage(chatId, chunk, { parse_mode: "Markdown" });
201 // Store each chunk in chat history
202 await storeChatMessage(
53You'll need to set up some environment variables to make it run.
54
55- `ANTHROPIC_API_KEY` for LLM calls
56- You'll need to follow [these instructions](https://docs.val.town/integrations/telegram/) to make a telegram bot, and set `TELEGRAM_TOKEN`. You'll also need to get a `TELEGRAM_CHAT_ID` in order to have the bot remember chat contents.
57- For the Google Calendar integration you'll need `GOOGLE_CALENDAR_ACCOUNT_ID` and `GOOGLE_CALENDAR_CALENDAR_ID`. See [these instuctions](https://www.val.town/v/stevekrouse/pipedream) for details.
8## Hono
9
10This app uses [Hono](https://hono.dev/) as the API framework. You can think of Hono as a replacement for [ExpressJS](https://expressjs.com/) that works in serverless environments like Val Town or Cloudflare Workers. If you come from Python or Ruby, Hono is also a lot like [Flask](https://github.com/pallets/flask) or [Sinatra](https://github.com/sinatra/sinatra), respectively.
11
12## Serving assets to the frontend
20### `index.html`
21
22The most complicated part of this backend API is serving index.html. In this app (like most apps) we serve it at the root, ie `GET /`.
23
24We *bootstrap* `index.html` with some initial data from the server, so that it gets dynamically injected JSON data without having to make another round-trip request to the server to get that data on the frontend. This is a common pattern for client-side rendered apps.
25
26## CRUD API Routes
27
28This app has two CRUD API routes: for reading and inserting into the messages table. They both speak JSON, which is standard. They import their functions from `/backend/database/queries.ts`. These routes are called from the React app to refresh and update data.
29
30## Errors
31
32Hono and other API frameworks have a habit of swallowing up Errors. We turn off this default behavior by re-throwing errors, because we think most of the time you'll want to see the full stack trace instead of merely "Internal Server Error". You can customize how you want errors to appear.
8import { type Memory } from "../../shared/types.ts";
9
10const API_BASE = "/api/memories";
11const MEMORIES_PER_PAGE = 20;
12
71 setError(null);
72 try {
73 const response = await fetch(API_BASE);
74 if (!response.ok) {
75 throw new Error(`HTTP error! status: ${response.status}`);
100
101 try {
102 const response = await fetch(API_BASE, {
103 method: "POST",
104 headers: { "Content-Type": "application/json" },
123
124 try {
125 const response = await fetch(`${API_BASE}/${id}`, {
126 method: "DELETE",
127 });
155
156 try {
157 const response = await fetch(`${API_BASE}/${editingMemory.id}`, {
158 method: "PUT",
159 headers: { "Content-Type": "application/json" },