BMO: a living desktop buddy with a Claude brain
Standalone React + Vite app where BMO lives in a little garden: - Redrawn avatar (src/avatar) with depth, cartoon controls and expressive faces; original bmo-preview avatar and scenery kept in src/bmo - Idle life: wandering, chatter, boredom, sleep, pokes, weather and time-of-day reactions - Chat via a local Node server streaming Claude replies (Haiku by default), with live scene awareness and tools to change color, outfit, sky, weather, move and perform - Side menu for API key, appearance, environment and chat log Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
// Saves the Anthropic API key the user pastes into the side menu. The key is
|
||||
// written to the project's .env (gitignored, owner-only permissions) so it
|
||||
// survives restarts, and applied to process.env right away. It is never sent
|
||||
// back to the browser — only a masked hint is.
|
||||
|
||||
import { chmodSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
export const ENV_PATH = path.resolve(here, "../.env");
|
||||
|
||||
const KEY_LINE = /^\s*ANTHROPIC_API_KEY\s*=.*$/m;
|
||||
|
||||
export function currentKey(): string | undefined {
|
||||
return process.env.ANTHROPIC_API_KEY?.trim() || undefined;
|
||||
}
|
||||
|
||||
export function keyHint(key = currentKey()): string | null {
|
||||
if (!key) return null;
|
||||
return `${key.slice(0, 7)}…${key.slice(-4)}`;
|
||||
}
|
||||
|
||||
/** Writes (or with `null`, clears) ANTHROPIC_API_KEY in .env and process.env. */
|
||||
export function storeKey(key: string | null) {
|
||||
const existing = existsSync(ENV_PATH) ? readFileSync(ENV_PATH, "utf8") : "";
|
||||
const line = `ANTHROPIC_API_KEY=${key ?? ""}`;
|
||||
let next = KEY_LINE.test(existing) ? existing.replace(KEY_LINE, line) : `${line}\n${existing}`;
|
||||
if (!next.endsWith("\n")) next += "\n";
|
||||
writeFileSync(ENV_PATH, next, { mode: 0o600 });
|
||||
chmodSync(ENV_PATH, 0o600);
|
||||
if (key) process.env.ANTHROPIC_API_KEY = key;
|
||||
else delete process.env.ANTHROPIC_API_KEY;
|
||||
}
|
||||
|
||||
/** Rough shape check so obvious paste mistakes get a friendly error. */
|
||||
export function looksLikeKey(key: string) {
|
||||
return /^sk-ant-[A-Za-z0-9_-]{20,}$/.test(key);
|
||||
}
|
||||
Reference in New Issue
Block a user