Files
recap/bin/bump-version.sh
T
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

176 lines
5.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Interactive version bump.
#
# Shows the current version, asks for the new version (with a sensible default:
# patch-level bump), and asks for release notes. Then:
# 1. Creates startos/versions/v<new>.ts
# 2. Updates startos/versions/index.ts (adds import, updates `current:`,
# pushes old current into `other:`)
#
# If the file .release-notes-pending.txt exists in the project root, its
# contents are shown as the default release notes (just press Enter to accept).
# This is the convention Claude uses after making code changes. The file is
# deleted on a successful bump.
#
# Flags:
# --from-deploy Treat the absence of .release-notes-pending.txt as the
# signal that no new work needs to ship — exit 0 without
# bumping. Lets `make deploy` always include this step
# without forcing a redundant bump when the user (or a
# prior run) already bumped.
#
# Run standalone with `make bump`, or as the first step of `make deploy`.
set -euo pipefail
FROM_DEPLOY=0
for arg in "$@"; do
case "$arg" in
--from-deploy) FROM_DEPLOY=1 ;;
esac
done
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
VERSIONS_DIR="$PROJECT_ROOT/startos/versions"
INDEX_FILE="$VERSIONS_DIR/index.ts"
PENDING_NOTES_FILE="$PROJECT_ROOT/.release-notes-pending.txt"
# When invoked from `make deploy`, treat a missing pending-notes file as
# "nothing new to ship — current version is already fresh, skip the bump."
# Standalone `make bump` always prompts.
if [ "$FROM_DEPLOY" = "1" ] && [ ! -f "$PENDING_NOTES_FILE" ]; then
echo ""
echo " (No .release-notes-pending.txt — current version is already bumped. Skipping.)"
echo ""
exit 0
fi
# --- Discover current version ---
CURRENT_VAR="$(sed -nE "s/.*current:[[:space:]]*(v_[0-9_]+).*/\1/p" "$INDEX_FILE" | head -1)"
if [ -z "$CURRENT_VAR" ]; then
echo "X Could not find current version in $INDEX_FILE" >&2
exit 1
fi
CURRENT_DOT="$(echo "$CURRENT_VAR" | sed 's/^v_//; s/_/./g')"
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_DOT"
SUGGESTED="$MAJOR.$MINOR.$((PATCH + 1))"
echo ""
echo "═══════════════════════════════════════════"
echo " Bumping version"
echo "═══════════════════════════════════════════"
echo ""
echo " Current: $CURRENT_DOT"
echo ""
# --- Prompt for new version ---
read -r -p " New version [$SUGGESTED]: " NEW_VERSION
NEW_VERSION="${NEW_VERSION:-$SUGGESTED}"
if ! [[ "$NEW_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "X Invalid version: '$NEW_VERSION' (expected format: x.y.z, e.g. 0.1.10)" >&2
exit 1
fi
NEW_FILE="$VERSIONS_DIR/v${NEW_VERSION}.ts"
if [ -f "$NEW_FILE" ]; then
echo "X Version file already exists: $NEW_FILE" >&2
echo " (If you want to re-run, delete that file first.)" >&2
exit 1
fi
NEW_VAR="v_$(echo "$NEW_VERSION" | tr '.' '_')"
# --- Prompt for release notes ---
# If Claude (or you) left suggested notes in .release-notes-pending.txt, show
# them as the default. Press Enter to accept, or type something different.
SUGGESTED_NOTES=""
if [ -f "$PENDING_NOTES_FILE" ]; then
# Read the file, trim leading/trailing whitespace, collapse interior newlines
# into spaces so it fits the one-line release-notes format.
SUGGESTED_NOTES="$(tr '\n' ' ' < "$PENDING_NOTES_FILE" | sed -e 's/[[:space:]]\{1,\}/ /g' -e 's/^ //' -e 's/ $//')"
fi
echo ""
if [ -n "$SUGGESTED_NOTES" ]; then
echo " Suggested release notes (from .release-notes-pending.txt):"
echo " $SUGGESTED_NOTES"
echo ""
read -r -p " Release notes (Enter to accept, or type new): " RELEASE_NOTES
RELEASE_NOTES="${RELEASE_NOTES:-$SUGGESTED_NOTES}"
else
read -r -p " Release notes (one-liner, what changed in $NEW_VERSION?): " RELEASE_NOTES
fi
if [ -z "$RELEASE_NOTES" ]; then
echo "X Release notes are required" >&2
exit 1
fi
# Escape for TypeScript single-quoted string: backslash first, then single quote
ESCAPED_NOTES="$(printf '%s' "$RELEASE_NOTES" | sed -e 's/\\/\\\\/g' -e "s/'/\\\\'/g")"
# Clean up partial state if anything below fails
cleanup_on_error() {
[ -f "$NEW_FILE" ] && rm -f "$NEW_FILE"
[ -f "$INDEX_FILE.tmp" ] && rm -f "$INDEX_FILE.tmp"
[ -f "$INDEX_FILE.bak" ] && rm -f "$INDEX_FILE.bak"
}
trap cleanup_on_error ERR
# --- 1. Create new version file ---
cat > "$NEW_FILE" <<EOF
import { VersionInfo } from '@start9labs/start-sdk'
export const $NEW_VAR = VersionInfo.of({
version: '$NEW_VERSION:0',
releaseNotes: {
en_US: '$ESCAPED_NOTES',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
EOF
# --- 2. Update index.ts ---
# Insert `import { v_NEW } from './vNEW'` right after the last existing version
# import, so the imports stay contiguous.
NEW_IMPORT="import { $NEW_VAR } from './v$NEW_VERSION'"
awk -v new_import="$NEW_IMPORT" '
/^import \{ v_[0-9_]+ \} from/ { last_imp = NR }
{ lines[NR] = $0 }
END {
for (i = 1; i <= NR; i++) {
print lines[i]
if (i == last_imp) print new_import
}
}
' "$INDEX_FILE" > "$INDEX_FILE.tmp"
mv "$INDEX_FILE.tmp" "$INDEX_FILE"
# Point `current:` at the new version, and prepend the old current into `other:`.
# Use -i.bak for macOS/BSD + Linux/GNU sed compatibility.
sed -i.bak \
-e "s/current: $CURRENT_VAR/current: $NEW_VAR/" \
-e "s/other: \[/other: [$CURRENT_VAR, /" \
"$INDEX_FILE"
rm -f "$INDEX_FILE.bak"
trap - ERR
# --- Clean up the pending-notes scratch file now that we've consumed it ---
if [ -f "$PENDING_NOTES_FILE" ]; then
rm -f "$PENDING_NOTES_FILE"
fi
echo ""
echo " ✓ Created startos/versions/v${NEW_VERSION}.ts"
echo " ✓ Wired up in startos/versions/index.ts"
echo " ✓ Version: $CURRENT_DOT$NEW_VERSION"
echo ""