UNPKG

framework

Version:

The (AI) Framework: turnkey, zero-config AI orchestration that wraps a coding-agent CLI (Claude Code) as a black box and takes you from an idea to a running app. Vite for AI.

53 lines 2.8 kB
import { readSecrets, writeSecrets } from './registry.js'; import { CREDENTIALS, ENV_KEYS, SECRET_KEYS, discordCredentialStatus, validateCredential, } from './discord-credentials.js'; // The registry-backed half of the Discord credentials (#1095), split from the rules beside it // because those are browser-safe and this reaches the home file: `client.test.ts` walks the // import graph, so a `node:*` edge here must not be reachable from what the dashboard imports. /** * A {@link DiscordCredentialsStore} backed by the registry file. `onChange` is what makes this * more than a write: the daemon passes its background services' reload, so a pasted token starts * the bot on the spot instead of at the next restart (which was the other half of what made this * step unfinishable from the dashboard). */ export function registryDiscordCredentialsStore(opts = {}) { const env = opts.env ?? process.env; const fs = opts.fs; return { async status() { return discordCredentialStatus(env, await readSecrets(fs, env).catch(() => ({}))); }, async save(patch) { const write = {}; for (const key of CREDENTIALS) { const value = patch[key]; if (value === undefined) continue; // A value set in the environment cannot be edited here: the write would land in the file // and be shadowed on the next read, which is worse than saying no. if (env[ENV_KEYS[key]]?.trim()) return { ok: false, error: `${ENV_KEYS[key]} is set on the daemon, so this is not editable here.` }; const invalid = value === null ? undefined : validateCredential(key, value); if (invalid) return { ok: false, error: invalid }; write[SECRET_KEYS[key]] = value; } if (Object.keys(write).length === 0) return { ok: true }; try { await writeSecrets(write, fs, env); } catch { return { ok: false, error: 'failed to save' }; } // After the write, so a reload can never read the value it is replacing. A reload that // throws is not a failed save: the credential is stored, and the next daemon start uses it. // `.then(...)` rather than `Promise.resolve(onChange())`: a synchronous throw happens while // building that argument, so it would escape the catch and fail a save that already landed. await Promise.resolve() .then(() => opts.onChange?.()) .catch(() => { }); return { ok: true }; }, }; } //# sourceMappingURL=discord-credentials-store.js.map