0f027e863b
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>
40 lines
1.6 KiB
TypeScript
40 lines
1.6 KiB
TypeScript
// 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);
|
|
}
|