Commit Graph

59 Commits

Author SHA1 Message Date
Keysat 8d9b384e32 Bump version to 0.1.16
Release notes:
  Add free mode for unlicensed users (one video at a time, no library).
  Online license revocation check via Keysat /v1/validate.
  Fix blank-screen bug after processing long videos.
  Ship deploy-script polish (no double-bump prompt).
2026-05-08 11:34:28 -05:00
Keysat 1e030a24c6 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.
2026-05-08 11:32:30 -05:00
Keysat 25b1c3a366 Add free tier (unlicensed users get one-at-a-time summarization)
Unlicensed users can now summarize a single video at a time using their
own Gemini API key. The result renders in the UI exactly like a paid
summary, but is not persisted — there's no library entry, no history,
and a second submission while one is in flight is rejected.

Server (server/index.js):
  • /api/process is now in LICENSE_OPEN_PATHS. The route handler
    distinguishes free users (state !== "licensed" || no "core") and:
      - rejects USE_SERVER_KEY / empty key with 402 byo_key_required
        (so the bundled Gemini key stays paid-only)
      - rejects a second concurrent job with 409 processing_in_progress
        via a module-level freeJobInFlight flag, released in finally
      - skips saveToHistory so the host's library stays clean
  • Pro feature gates (history/library/subscriptions) unchanged — still
    return 402 feature_not_in_tier for unlicensed callers.

Frontend (public/index.html):
  • New state.activationSkipped flag (persisted to localStorage). The
    activation screen still appears on first launch, but now offers a
    "Skip — use free mode" button alongside Activate / Buy a key.
    Once skipped, the main app renders normally.
  • Free-mode upgrade banner under the top bar with Upgrade and "I have
    a key" buttons (the latter routes back to the activation screen).
  • handleLibraryClick / handleSubscribeClick wrappers — for unlicensed
    users, the library (clock) icon and the channel-URL Subscribe
    submission show a toast explaining the upgrade rather than opening
    an empty sidebar / hitting a 402.
  • Submit button enforces BYO key for unlicensed users (the bundled
    state.hasServerKey doesn't enable submit). handleSubmit shows a
    toast when an unlicensed user tries to queue a second video.
2026-05-08 11:16:02 -05:00
Keysat 7d71150439 Fix SSE event type lost across reader chunks (blank screen post-process)
Symptom: after a long video finishes processing, the screen goes blank
even though the video and chunks save correctly to history (visible
after a refresh + library click).

Cause: the manual SSE parser in processUrl declared `let eventType = ""`
inside the `while (await reader.read())` loop, so the variable reset on
every chunk. The server emits each event as a single write of
`event: X\ndata: Y\n\n`, but for a long video the result payload (entries
+ chunks + logs) is tens of KB and gets split across reader chunks by
the browser. When the split landed between the `event: result\n` line
and the `data: ...` line, the event type was lost and `handleSSE("",
data)` matched no branch — the result event was silently dropped, and
state.chunks stayed at the empty array set during processUrl reset.

Fix: hoist eventType outside the loop so it persists across chunks, and
reset it after each dispatch (per the SSE spec, event type returns to
default after an event is fired).

Short videos with small result payloads fit in a single chunk and so
were unaffected — which is why the bug looked intermittent.
2026-05-08 11:04:50 -05:00
Keysat 2621f2cdbe Add online license revocation check (Keysat /v1/validate)
Without this, a license revoked in the Keysat admin UI keeps unlocking
the app on the customer's machine — Ed25519 signatures are perpetually
valid, so the offline-only check never sees the revocation.

What this adds:

  • license.js: validateOnline() calls licensing.keysat.xyz/v1/validate
    via @keysat/licensing-client's Client. Hard rejections (revoked,
    suspended, expired, not_found, product_mismatch, fingerprint_mismatch,
    too_many_machines, invalid_state) immediately flip state to "invalid"
    and persist the verdict to <license>.state.json so it survives
    restarts. rate_limited and unknown reasons are treated as transient.

  • Network errors keep the prior state for up to MAX_OFFLINE_DAYS
    (default 7, env-overridable) since the last successful validate.
    Past the ceiling, lock out with reason=validation_overdue. This
    avoids breaking customers when Keysat is briefly down while still
    catching revocations on machines that go offline forever.

  • license.js: deactivate() helper that removes both license.txt and
    its sidecar state file (idempotent). publicView() now exposes
    lastValidatedAt, serverStatus, graceUntil for the UI.

  • index.js: refreshLicenseOnline() runs on startup (async, non-
    blocking), every 6h thereafter (env-overridable), and at activation
    time with an 8s timeout cap so a slow Keysat doesn't hang the
    activation UI. State changes are logged.

  • index.js: /api/license/activate now awaits an online confirmation
    after the offline signature check passes. A revoked key pasted into
    the activation modal fails fast instead of working until the next
    poll.
2026-05-08 10:39:11 -05:00
Keysat 154d692371 Clean up legacy 0.3.5 scaffolding and standalone-mode artifacts
- Removes start9/0.4/ — the StartOS 0.3.5-style YAML manifest folder,
  superseded by the TypeScript-based startos/ package.
- Removes pre-StartOS standalone-mode artifacts: setup.sh, create-app.sh,
  Start Summarizer.command, build-guide-pdf.py, GET-STARTED.md/.pdf.
- Removes conversion-era design docs (START9_PACKAGING_GAMEPLAN.md,
  UPGRADE-DESIGN.md, KEYSAT_INTEGRATION.md). Recoverable from history if
  needed.
- Tightens .gitignore: untracks node_modules, javascript/ build output,
  *.s9pk, history/ user data, cookies.txt, library export, .env, .DS_Store,
  and .claude/ worktree state. Files remain on disk; just removed from
  the index.
2026-05-08 09:41:42 -05:00
Keysat 574a16d9fa Save in-progress keysat integration and StartOS 0.4 work
Snapshot of the working tree before cleanup. Captures:
- Keysat licensing: server/license.js, /api/license/* endpoints in
  server/index.js, activation modal in public/index.html, embedded
  Ed25519 issuer key (assets/issuer.pub).
- StartOS 0.4 expansion: setApiKey action, version files v0.1.1
  through v0.1.15, file-models/config.json.ts, manifest updates.
- Self-hosted registry server (startos-registry/).
- Build/deploy scripts (bin/bump-version.sh, bin/deploy.sh, vendored
  yt-dlp binary), .gitignore, .deploy.env.example.
- Recent design docs (KEYSAT_INTEGRATION.md, UPGRADE-DESIGN.md) —
  retained here so they remain recoverable when removed in the
  follow-up cleanup commit.
2026-05-08 09:39:17 -05:00
MacPro 8298c083c7 Fix StartOS 0.4 TypeScript packaging to match SDK API 2026-04-09 15:10:44 -05:00
MacPro 68ec875ee7 Add StartOS 0.4.0 packaging 2026-04-09 15:03:31 -05:00