Search
Code3,144
export function Login() { const [bearerToken, setBearerToken] = useLocalStorage("bearer", ""); const [anthropicApiKey, setAnthropicApiKey] = useLocalStorage("anthropic_api_key", ""); return ( <label className="block text-sm font-medium text-gray-700"> <div className="flex items-center mb-1"> <span>Anthropic API Key</span> <a href="https://console.anthropic.com/settings/keys" target="_blank" className="ml-1 text-xs text-indigo-600 hover:text-indigo-800 transition-colors duration-200" <input type="password" value={anthropicApiKey} onChange={(e: any) => setAnthropicApiKey(e.target.value)} placeholder="sk-ant-xxxxx" autoComplete="off" project, bearerToken, anthropicApiKey, setProject,}: { project: any; bearerToken: string; anthropicApiKey: string; setProject: (project: any) => void;}) { project, branchId, anthropicApiKey, bearerToken, selectedFiles, <div className="p-6 flex flex-col h-full w-full"> <ApiKeyWarning show={!anthropicApiKey} /> <ChatMessagesexport function App() { const [bearerToken, setBearerToken] = useLocalStorage("bearer", ""); const [anthropicApiKey, setAnthropicApiKey] = useLocalStorage("anthropic_api_key", ""); const [projectJSON, setProjectJSON] = useLocalStorage("project", ""); const [project, setProject_] = useState(safeParse(projectJSON)); function handleLogout() { setBearerToken(""); // Keep the anthropic API key in case the user wants to reuse it } <Chat bearerToken={bearerToken} anthropicApiKey={anthropicApiKey} project={project} setProject={setProject} return ( <div className="bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 mb-4 rounded"> <p className="font-bold">Anthropic API Key Missing</p> <p>Please log out and add your Anthropic API key to use this app.</p> </div> ); project: any; branchId: string | undefined; anthropicApiKey: string; bearerToken: string; selectedFiles: string[]; project, branchId, anthropicApiKey, bearerToken, selectedFiles, project, branchId, anthropicApiKey, selectedFiles, images: images- [ ] Give it a replace_file command (instead of delete & create)?- [ ] Add other LLMs ie Gemini, Deepseek, o3-mini, etc. Figure out how to get a text-editing to work in an LLM agnostic way- [x] Figure out a convention to teach in the anthropic prompt mod where the LLM always checks the readme for the scope (if not provided) and keeps it up to take with every change- [x] Ability to create new projects from the interface- [x] Figure out why OpenTownie can't create HTTP vals. Maybe give it a seperate tool for it?- [x] Start a timer for messages- [x] Add more indicators that it's "still working"- [x] Require users supply their own Anthropic token?- [x] Add cost indications on messages- [x] Add a bell noise when the message is done to let us knowimport { anthropic } from "npm:@ai-sdk/anthropic";import { ValTown } from "npm:@valtown/sdk@0.37.0";import fileWithLinesNumbers from "../utils/fileWithLinesNumbers.ts";export function getTextEditorTool(bearerToken: string, project: any, branch_id: string | undefined) { const vt = new ValTown({ bearerToken }); return anthropic.tools.textEditor_20250124({ execute: async ({ command,import { readFile } from "https://esm.town/v/std/utils@71-main/index.ts";import { createAnthropic } from "npm:@ai-sdk/anthropic";import { ValTown } from "npm:@valtown/sdk@0.37.0";import { convertToCoreMessages, type CoreUserMessage, streamText } from "npm:ai"; } const { messages, project, branchId, anthropicApiKey, selectedFiles, images } = await c.req.json(); console.log("Original messages:", JSON.stringify(messages, null, 2)); console.log("Images received:", JSON.stringify(images, null, 2)); // Check if API key is available if (!anthropicApiKey) { return Response.json({ error: "Anthropic API key is required. Please log out and add your Anthropic API key to use this app.", }, { status: 400 }); } let apiKey; if (!anthropicApiKey) { return Response.json({ error: "Anthropic API key is required. Please log out and add your Anthropic API key to use this app.", }, { status: 400 }); } else if (anthropicApiKey === Deno.env.get("PASSWORD")) { apiKey = Deno.env.get("PROVIDED_ANTHROPIC_API_KEY"); } else { apiKey = anthropicApiKey; } const anthropic = createAnthropic({ apiKey, }); // @ts-ignore lastMessage.content.at(-1).providerOptions = { anthropic: { cacheControl: { type: "ephemeral" } }, }; } const result = await streamText({ model: anthropic("claude-3-7-sonnet-20250219"), messages: coreMessages, system: system + "\n\n" + open_townie_prompt_additions,## How It Works1. **Login**: Authenticate with your Val Town API token and Anthropic API key2. **Select a Project**: Choose which Val Town project you want to work on3. **Select Files**: Browse your project files and select which ones to include in the context window- A Val Town account with API access- An Anthropic API key (Claude 3.7 Sonnet)### Setup1. Visit the OpenTownie app2. Enter your Val Town API token (with `projects:write` and `users:read` permissions)3. Enter your Anthropic API key4. Click "Login" to access your projects- AI SDK for Claude integrationThe application proxies requests to the Anthropic API and Val Town API, allowing Claude to view and edit your project files directly.## Privacy & Security- Your Val Town API token and Anthropic API key are stored locally in your browser- No data is stored on our servers- All communication with the APIs is done directly from your browserexport function Login() { const [bearerToken, setBearerToken] = useLocalStorage("bearer", ""); const [anthropicApiKey, setAnthropicApiKey] = useLocalStorage("anthropic_api_key", ""); return ( <label className="block text-sm font-medium text-gray-700"> <div className="flex items-center mb-1"> <span>Anthropic API Key</span> <a href="https://console.anthropic.com/settings/keys" target="_blank" className="ml-1 text-xs text-indigo-600 hover:text-indigo-800 transition-colors duration-200" <input type="password" value={anthropicApiKey} onChange={(e: any) => setAnthropicApiKey(e.target.value)} placeholder="sk-ant-xxxxx" autoComplete="off"