Skip to content

Release 0.10.0-beta.1: migration framework + credential layer + SDK routing + update banner#174

Merged
luokerenx4 merged 7 commits intomasterfrom
dev
May 9, 2026
Merged

Release 0.10.0-beta.1: migration framework + credential layer + SDK routing + update banner#174
luokerenx4 merged 7 commits intomasterfrom
dev

Conversation

@luokerenx4
Copy link
Copy Markdown
Contributor

Summary

This PR introduces the migration / version-awareness substrate the
project was missing, decouples the AI credential layer from SDK
implementation choice, and ships an update-notification banner — all
foundation for the Electron packaging that's coming next, but useful
on its own to anyone running OpenAlice from source today.

Headlines:

  • Drizzle-style versioned migration framework with snapshots and a
    build-time INDEX.md.
  • Credential layer split out from profile records (vendor + auth as
    first-class storage; profile points in via credentialSlug).
  • Preset-driven SDK adapter selection — every preset declares which
    SDK adapters its credential can drive; the test path now uses the
    lightest available (skipping the Claude Code subprocess for non-
    subscription cases).
  • AI Provider page rebuilt around credentials + SDKs with bidirectional
    click-to-highlight.
  • GitHub Releases update detection + top-of-app banner.
  • Bumped to 0.10.0-beta.1.

Per-session contributions

2026-05-09 — Migration framework + credential layer + SDK routing + update banner + 0.10.0-beta.1

  • Migration framework (src/migrations/): journal in
    data/config/_meta.json, per-migration data/_backup/{ts}-pre-{id}/
    snapshots, static registry (asar-safe), build-time INDEX.md
    generator wired to prebuild. The 5 historical ad-hoc migration
    ifblocks in loadConfig() collapse into 0001_initial_unified;
    0002_extract_credentials introduces the credentials map;
    0003_backfill_credentials cleans up stragglers.
  • Credential layer is now a first-class top-level field on
    aiProviderSchema. Profile gains credentialSlug pointer.
    resolveProfile() joins on read (inline fields kept as transitional
    fallback). writeProfile extracts eagerly with dedup;
    deleteProfile GCs orphaned credentials.
  • Preset-driven SDK adapter selection: each preset declares
    sdkAdapters: { available: [{id, config: cred=>sdk-shaped}], test }.
    Preset's config builder spells out per-SDK field names explicitly
    (vercel uses baseURL, agent-sdk uses baseUrl) so providers don't
    reverse-search credential fields. Light invokers (vercel-anthropic /
    vercel-openai / vercel-google) skip tools + system prompt for
    bare-minimum test-path round trips. Test on DeepSeek/MiniMax now
    takes 1-7s (HTTP) instead of 5s+ (Claude Code subprocess spawn).
  • AI Provider page rebuild: Credentials column (editable, with
    profiles nested as collapsible children) + Available SDKs column
    (read-only, lists compatible presets with solid/hollow dots for
    configured/unconfigured). Click either side to dim incompatible
    cards on the other.
  • Update detection + banner: new src/core/version.ts reads
    package.json#version, parses owner/repo from the repository field,
    hits GitHub Releases API with TTL cache (1h success / 5min error),
    compares with semver. GET /api/version exposes the result.
    UpdateBanner shows when hasUpdate=true with "Release notes →" /
    "Skip this version" / session-dismiss. Hits /releases (not
    /releases/latest) so prerelease tags like v0.10.0-beta.0 surface.
  • TODO additions: native Anthropic full provider + native OpenAI
    Chat Completions full provider — bigger scope than the test-path
    swap (parallel to CodexProvider size each), deferred.
  • Version bump: 0.10.0-beta.00.10.0-beta.1.

Key commits: 546a295, 1afd316, 740b548, cbc6a53, 48f3354, c14ba5a

Full commit log

c14ba5a chore(release): 0.10.0-beta.1
48f3354 fix(version): hit /releases (not /releases/latest) so prerelease tags surface
cbc6a53 feat(version): GitHub Releases update detection + banner UI
740b548 feat(ai-provider page): credentials + SDKs two-region layout with bidirectional highlight
1afd316 feat(ai-providers): preset-driven SDK adapter selection for test path
546a295 feat(migration): introduce versioned migration framework + extract AI credentials as 0002
91d1470 feat(ui): cross-cutting design polish — typography scale, lucide icons, PnL color discipline, live-pulse indicator, Portfolio polish

Test plan

  • npx tsc --noEmit clean
  • pnpm test passes (1592 tests, +88 new this session)
  • Migration framework smoke: real data/config/ migrates idempotently across reruns; backups land in data/_backup/
  • Test path smoke: DeepSeek and MiniMax test buttons go through vercel-anthropic end-to-end (verified with real credentials, ~2-7s)
  • Version detection: /api/version returns v0.10.0-beta.0 from GitHub Releases (post prerelease fix)
  • Update banner: confirmed renders with hasUpdate=true, "Skip this version" persists in localStorage
  • After merge: trigger release CI, verify v0.10.0-beta.1 appears on GitHub Releases page

🤖 Generated with Claude Code

Ame and others added 7 commits May 9, 2026 14:29
…s, PnL color discipline, live-pulse indicator, Portfolio polish

Lifts the rest of the app to the polish bar set by the recent UTA pages
redesign (commit b0b79ba). No new pages, no behavioural changes — only
visual / design-system gaps.

**Typography scale (index.css)**
- New semantic classes `.text-display` (28-36) `.text-title` (16)
  `.text-body` (13) `.text-caption` (12) `.text-micro` (11). New code
  should opt into these instead of inventing arbitrary `text-[Npx]`
  values.

**LiveIndicator + PageHeader live prop**
- New `<LiveIndicator>` — small breathing green dot + "updated Xs ago"
  microcopy that ticks every 5s on its own
- `<PageHeader live={{ lastUpdated }}>` opt-in renders the dot next to
  the title and the relative time in the description row
- Wired into TradingPage / UTADetailPage / PortfolioPage. New
  `@keyframes live-pulse` defines the breathing motion (2.4s cycle —
  ambient, not anxious)

**PnL color discipline**
- Replaced `text-green-400` / `text-emerald-*` / `text-red-400` /
  `bg-red-500` etc. with semantic tokens (`text-green` / `text-red` /
  `bg-red`) across PushApprovalPanel, ContextMenu, ChatMessage,
  ChatChannelList, ChatChannelListContainer, DevPage, MarketSidebar,
  market/QuoteHeader/SearchBox/{Profile,KeyMetrics,FinancialStatements,
  TradeableContracts}Panel, NotificationsInboxPage. Three green shades
  collapsed to one; categorical asset-class chips that previously used
  emerald now share the canonical green token.

**lucide-react (high-traffic icons)**
- Added `lucide-react` dependency
- ActivityBar's 9 nav icons replaced with lucide equivalents
  (MessageSquare, LineChart, GitBranch, BarChart3, Newspaper, Notebook,
  Zap, Settings, Code2). Removed the inline `INDICATOR_STYLE` constant
  with hardcoded `#58a6ff` — active indicator now uses `bg-accent`.
- TabStrip × button + ChatChannelList settings/delete buttons swapped
  for `<X>` and `<Settings>` from lucide. Smaller files, consistent
  stroke weights, future icon adds become trivial.
- In-page custom SVGs (in market panels, dialogs, etc.) deferred to
  later passes; this PR only touches the always-visible shell.

**Portfolio page polish**
- Hero now uses the shared `<Metric>` component with today PnL delta
  arrow (▲/▼ + sign-colored percent), same primitive as TradingPage
- Per-account `<Sparkline>` on each card in AccountStrip — 24h
  trend at a glance per UTA
- AccountStrip rebuilt as 2-column card grid (was inline chips); each
  card shows label · NLV · today PnL · sparkline
- Inlined fmt/fmtPnl/fmtNum helpers replaced with imports from
  `lib/format` (DRY with TradingPage / UTADetailPage)
- Page-level live indicator wired in via the new PageHeader prop

No backend changes. No test changes (all 1520 still passing). Vite
build clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… credentials as 0002

Drizzle-style framework: journal-based runner (data/config/_meta.json),
per-migration data/_backup snapshots, static registry (asar-safe). 0001
folds the four ad-hoc loadConfig() ifblocks into one idempotent migration;
ifblocks deleted. 0002 extracts apiKey/baseUrl into a top-level
credentials map — profiles gain credentialSlug, inline fields kept as
transitional fallback. resolveProfile() joins on read; ResolvedProfile
shape unchanged so providers don't change.

Build-time INDEX.md generator wired to prebuild — PR diffs surface
migration additions explicitly.

Foundation for vendor-shaped preset catalog and non-chat LLM utilities.
Out of scope: preset reshape, /api/config/credentials CRUD, update
notification UI, Electron auto-update.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each preset now declares the SDK adapters its credential can drive,
with explicit per-adapter config builders that map credential fields
into each SDK's standardized config (vercel uses baseURL, agent-sdk
uses baseUrl — no normalization, the preset spells it out).

Test path goes through the preset's declared `test` adapter, falling
back to honoring profile.backend for Custom/legacy profiles. Light
presets (Claude API, OpenAI API, Gemini, MiniMax, GLM, Kimi, DeepSeek)
now skip the agent-sdk subprocess entirely and run a bare-minimum
generateText call via @ai-sdk/anthropic|openai|google.

Per-vendor baseURL quirk: MiniMax/GLM/Kimi serve Anthropic API at
`/anthropic/v1/messages`; their config builder appends `/v1` to the
user's baseUrl. DeepSeek serves at `/anthropic/messages`; its builder
passes baseUrl through unchanged. Verified end-to-end with real
credentials for MiniMax (~7s) + DeepSeek (~2s).

Subscription presets (Claude OAuth, Codex OAuth) keep agent-sdk/codex
as their test adapter — the heavy harness is physically required.

Chat path unchanged this round (still routes via profile.backend).
Foundation for chat path going preset-driven in a future plan.

42 new tests cover preset declarations, vendor-inference round-trip,
adapter dispatch (mocked SDK imports), fallback synthesis for Custom.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…irectional highlight

Page rebuild from "flat profile list" to "credentials (left, editable)
+ available SDKs (right, read-only)". Profiles nest inside their
credential card. Clicking either side dims incompatible cards on the
other — bidirectional discovery of the credential ↔ SDK relationship.

Backend changes that the new UI depends on:

- writeProfile now eagerly extracts credentials: new profiles land
  with credentialSlug + a (possibly deduped) entry in the credentials
  map. Same-vendor/auth/apiKey/baseUrl reuses an existing slug.
- deleteProfile garbage-collects orphaned credentials when the last
  referencing profile is removed.
- 0003_backfill_credentials migration cleans up profiles added between
  0002 landing and writeProfile going eager (e.g. user's DeepSeek).
- SDK_ADAPTER_LABELS map + getSdkAdapterInfo() helper computes
  human-readable adapter info from PRESET_CATALOG; surfaced via new
  GET /api/config/sdk-adapters endpoint. /profiles response gains a
  `credentials` field so one fetch covers everything the page needs.

Frontend:

- Two new components: CredentialCard (collapsible, profile rows
  nested) and SdkAdapterCard (read-only, lists compatible presets
  with solid/hollow dots for configured/unconfigured).
- Selection state in the page drives dim logic: clicking a credential
  dims SDKs not in its preset's available list; clicking an SDK dims
  credentials whose preset doesn't register that SDK.

15 new tests (extractCredentialFromProfile dedup + slug generation,
0003 backfill + idempotency, getSdkAdapterInfo shape).

Out of scope: credential CRUD HTTP endpoints, wizard refactor to
"credential first" two-step, removing inline credential fields from
profileSchema, chat path going preset-driven.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
App now polls GitHub Releases for newer versions and surfaces them as
a top-of-app banner. Self-hosted source distribution: when user sees
the banner, they manually run \`git pull && pnpm build\` and restart.
Auto-execute is out of scope (Electron auto-update will handle that
path natively when packaging lands).

Backend (\`src/core/version.ts\`):
- getCurrentVersion reads package.json#version at module load
- getRepoSlug parses owner/repo from package.json#repository.url so
  fork users don't poll the upstream repo
- fetchLatestRelease hits GitHub Releases API with TTL cache (1h on
  success, 5min on error) — keeps us under the 60 req/h unauthenticated
  rate limit even if many UI tabs poll
- compareVersions handles MAJOR.MINOR.PATCH plus prerelease suffix
  (release > prerelease for same core; lex compare among prereleases)
- New GET /api/version endpoint returns the combined VersionInfo

Frontend:
- UpdateBanner: shown when hasUpdate=true; "Release notes" link to
  GitHub, "Skip this version" persists in localStorage so the user
  isn't bugged about the same version twice, "×" dismisses for the
  current session only
- Wired into AppShell as a top row above the activity bar + main
  panel — only takes vertical space when a banner is actually showing

14 new tests cover compare, cache, fetch error paths, hasUpdate
computation. Two follow-up TODO items added for native Anthropic /
OpenAI Chat Completions full providers (deferred — much bigger scope
than test-path swap).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… surface

GitHub's /releases/latest endpoint silently excludes any release flagged
as prerelease — so a project shipping only -beta.X tags would always
get 404 and the update banner would never trigger. Switch to /releases
(returns all in published_at desc), skip drafts explicitly, take the
first.

Confirmed against TraderAlice/OpenAlice: now picks up v0.10.0-beta.0
correctly. Tests updated to mock the array shape; +3 cases for
draft-skip / prerelease-acceptance / empty-list error path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Highlights of this beta:
- Versioned migration framework (Drizzle-style journal + per-migration
  snapshots, INDEX.md auto-generated from registry metadata).
- Credential layer split out from profiles — vendor + auth as a
  first-class stored concept; profiles point in via credentialSlug.
- Preset-driven SDK adapter selection: every preset declares which
  SDK adapters its credential can drive plus a `test` default; test
  path goes light (vercel-anthropic / codex / vercel-google) instead
  of always spawning a Claude Code subprocess.
- AI Provider page rebuild — credentials column (editable, profiles
  nested inside) + SDKs column (read-only, lists compatible presets
  with bidirectional click-to-highlight).
- GitHub Releases update detection + top-of-app banner with "skip
  this version" / "release notes" / session-dismiss.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@luokerenx4 luokerenx4 merged commit 8ce0e6c into master May 9, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant