Pluggable AI providers, relay credit system, picker UX overhaul
Captures roughly forty version bumps (v0.2.6 → v0.2.47) of work that
accumulated without commits.
- Pluggable provider system under server/providers/: gemini, anthropic,
openai, openai-compatible, ollama, whisper-compatible, relay. Mix and
match transcription + analysis per request via the picker UI.
- Relay backend integration. Hardcoded relay URL in server/relay-default.js
(operator-controlled at build time, not user-configurable). New
/api/relay/{status,policy} endpoints proxy to the relay; balance pings
populate a cached credit display.
- Per-install identity in server/install-id.js for relay credit accounting.
Sent to the relay as X-Recap-Install-Id; persists across upgrades, lost
on a full uninstall + reinstall. Not surfaced in the UI.
- Admin login gate (server/admin-auth.js + setAdminPassword action). Scrypt
password hash + HMAC-signed session cookie.
- Entitlement scheme rename: pro / max (each paired with subscriptions and
relay_pro / relay_max), replacing the misleading "core" entitlement
that conflicted with the user-facing "Core" tier name.
- Activation screen: dynamic credit count pulled from /api/relay/policy,
"Skip — use free mode" button, accurate paid-feature list.
- Top toolbar: inline credit-balance pill (or "BYO configured" fallback),
Upgrade + "I have a key" buttons.
- Picker UI: per-provider sections with Save/Test/Delete buttons, sections
collapsible by chevron, default-collapsed unless currently selected,
"Use comped credits (reset to relay)" link when the user has strayed,
green hint under inputs whose values are server-configured.
- Activity log: chevron-collapsible groups per video, refresh-survival via
localStorage + a 500-entry server-side buffer, explicit Clear button.
- YouTube captions fast-path with user toggle (skips audio download + AI
transcription when captions are available — uncheck for speaker labels).
- Cancel button: AbortController plumbed through every provider SDK call;
retryAPI short-circuits on AbortError; cancellation events surface in
the activity log instead of silent retries.
- Long-video analysis: auto-coalesce transcript entries before building the
analysis prompt so local-model context windows (32k-ish) don't overflow.
Original entries preserved for transcript display via an index map; the
analyzer sees a coarser view but click-to-seek timestamps stay precise.
- StartOS action grouping (Setup / AI Providers) so the actions list is
navigable.
- Manifest description rewritten to reflect multi-provider support and
free-tier relay credits.
- Smaller fixes: summarize-button enablement no longer requires a Gemini
key when other providers are configured; analysis fallback chain handles
context-length and 503 capacity errors; single-segment expansion for
providers that don't return per-segment timestamps (Parakeet et al.);
many other UX polish items.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
// In-memory cache of the most recent relay-reported credit balance + tier.
|
||||
// Updated every time a relay provider call lands (success or 4xx error
|
||||
// that includes the standard envelope). Exposed via /api/relay/status
|
||||
// so the UI can render the "N credits remaining · Tier: X" banner
|
||||
// without re-hitting the relay just for status.
|
||||
//
|
||||
// Not persisted to disk — the relay is the source of truth. We just cache
|
||||
// the last response so the UI doesn't have to wait for the next request
|
||||
// to refresh the display. On a fresh boot the cache is empty until the
|
||||
// first /api/relay/status call, which can optionally probe the relay.
|
||||
|
||||
let lastSnapshot = {
|
||||
creditsRemaining: null, // number | null
|
||||
tier: null, // "core" | "pro" | "max" | null
|
||||
lastUpdated: null, // ms-epoch | null
|
||||
lastError: null, // string | null
|
||||
};
|
||||
|
||||
// Called by the relay provider on every response (including error
|
||||
// responses that the relay annotated with the standard envelope).
|
||||
// `envelope` is the parsed JSON shape: { credits_remaining, tier, ... }.
|
||||
export function updateRelayState(envelope) {
|
||||
if (!envelope || typeof envelope !== "object") return;
|
||||
if (typeof envelope.credits_remaining === "number") {
|
||||
lastSnapshot.creditsRemaining = envelope.credits_remaining;
|
||||
}
|
||||
if (typeof envelope.tier === "string") {
|
||||
lastSnapshot.tier = envelope.tier;
|
||||
}
|
||||
lastSnapshot.lastUpdated = Date.now();
|
||||
lastSnapshot.lastError = null;
|
||||
}
|
||||
|
||||
// Record a relay error (network failure, 5xx with no envelope, etc.).
|
||||
// Surfaced in the UI status so the user knows the balance display is stale.
|
||||
export function recordRelayError(message) {
|
||||
lastSnapshot.lastError = (message || "Unknown relay error").slice(0, 300);
|
||||
lastSnapshot.lastUpdated = Date.now();
|
||||
}
|
||||
|
||||
export function getRelayState() {
|
||||
return { ...lastSnapshot };
|
||||
}
|
||||
|
||||
export function resetRelayState() {
|
||||
lastSnapshot = {
|
||||
creditsRemaining: null,
|
||||
tier: null,
|
||||
lastUpdated: null,
|
||||
lastError: null,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user