Free tier: drop spurious BYO key gate; clarify bundled vs BYO

The previous free-tier commit (c0975fe) blocked USE_SERVER_KEY for
unlicensed users on the theory that this protected a "bundled key."
That conflated two different things:

  • USE_SERVER_KEY = the user's OWN Gemini key, just stored server-side
    via the StartOS configuration action (vs. browser localStorage).
    Both paths are BYO — the user pays Google directly either way.

  • Bundled key = a future relay where paid users' /api/process requests
    are proxied through the operator's service and the operator absorbs
    the API cost. Sketched in UPGRADE-DESIGN.md (deleted, in git history)
    but NOT YET BUILT.

Blocking USE_SERVER_KEY broke a legitimate flow: a free user installs
the app on their own StartOS, sets their Gemini key via the config
action, then summarizes from the web UI without re-entering it.

This commit:
  • Drops the BYO/USE_SERVER_KEY rejection in /api/process. Free users
    can use a key from either path; the existing `if (!apiKey)` check
    still catches the no-key-anywhere case with a helpful message.
  • Reverts the frontend submit-button and handleSubmit checks to the
    same key requirement for both tiers (state.apiKey OR state.hasServerKey).
  • Drops "bundled API key" from the activation-screen subtitle and
    "bring your own Gemini key" from the free-mode banner. Until the
    relay is built, paid users still BYO too — promising otherwise in
    upgrade copy is misleading.
  • Keeps the parts that ARE legitimate free-vs-paid differentiators:
    the one-at-a-time concurrency lock and skipping saveToHistory.

Also fixes the `make deploy` redundancy:
  • bin/bump-version.sh accepts --from-deploy. When set, if there is no
    .release-notes-pending.txt (consumed by a prior bump or never
    written), exit 0 without prompting — the current version is already
    fresh.
  • Makefile passes --from-deploy from the deploy target. Standalone
    `make bump` is unchanged (always prompts).

Result: `make bump` then `make deploy` no longer double-prompts. And
calling `make deploy` twice in a row (no new work) is idempotent on
the bump step.
This commit is contained in:
Keysat
2026-05-08 11:32:30 -05:00
parent 25b1c3a366
commit 1e030a24c6
4 changed files with 54 additions and 28 deletions
+15 -14
View File
@@ -1324,21 +1324,24 @@
// ── Process ──────────────────────────────────────────────────────────────
async function handleSubmit() {
// Free tier requires BYO Gemini key — bundled key is licensed-only.
const free = !isLicensed();
const hasKey = free ? !!state.apiKey.trim() : (state.apiKey.trim() || state.hasServerKey);
// Both tiers need a Gemini key — either entered in the web UI
// (state.apiKey, localStorage) or set on the server via the StartOS
// configuration action (state.hasServerKey). The free-vs-paid line
// is concurrency + features, not key sourcing.
const hasKey = state.apiKey.trim() || state.hasServerKey;
if (!state.url.trim() || !hasKey) {
if (free && !state.apiKey.trim() && state.url.trim()) {
showToast("Free mode needs your own Gemini API key — open Settings to enter one.", "🔑");
if (!hasKey && state.url.trim()) {
showToast("Add your Gemini API key in Settings (or via the StartOS configuration action) to start summarizing.", "🔑");
}
return;
}
const url = state.url.trim();
// If already processing — free tier blocks, paid tier queues.
// If already processing — free tier blocks (one at a time),
// paid tier queues for batch.
if (state.loading) {
if (free) {
if (!isLicensed()) {
showToast("Free mode handles one video at a time. Wait for the current one to finish.", "⏳");
return;
}
@@ -1625,7 +1628,7 @@
<p class="activation-sub">
${loading
? "Checking license…"
: "Activate a Keysat license to unlock the full app — library, subscriptions, channel auto-queue, and the bundled API key. Or skip to use free mode (one video at a time, bring your own Gemini key)."
: "Activate a Keysat license to unlock the full app — saved library, channel & podcast subscriptions, and auto-queue. Or skip to use free mode (one video at a time, no library)."
}
</p>
${loading ? "" : `
@@ -1681,7 +1684,7 @@
">
<span style="flex:1; min-width: 220px;">
<strong style="color:#c4b5fd;">Free mode</strong>
&middot; one video at a time, bring your own Gemini key &middot;
&middot; one video at a time &middot;
no library, no subscriptions
</span>
<a href="${escHtml(buyUrl)}" target="_blank" rel="noopener"
@@ -1734,12 +1737,10 @@
const __prevHistoryListEl = document.querySelector(".history-list");
const __prevHistoryScroll = __prevHistoryListEl ? __prevHistoryListEl.scrollTop : 0;
const free = !isLicensed();
const submitNeedsBYO = free; // bundled key is licensed-only
// Same key requirement for both tiers today — either web-UI key or
// a server-side key set via the StartOS config action.
const submitDisabled = !state.url.trim()
|| (!isSubscribeUrl(state.url)
&& (submitNeedsBYO
? !state.apiKey.trim()
: (!state.apiKey.trim() && !state.hasServerKey)));
|| (!isSubscribeUrl(state.url) && !state.apiKey.trim() && !state.hasServerKey);
app.innerHTML = `
<!-- Top bar: title + action icons -->
<div class="top-bar">