Skip to content

Live builder updates in Tiny Sunny with @base44/platform - #50

Merged
amitbardos merged 1 commit into
mainfrom
amitb/platform-package
Oct 4, 2026
Merged

amitbardos merged 1 commit into
mainfrom
amitb/platform-package

Conversation

@amitbardos

@amitbardos amitbardos commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Replaces chat polling in Tiny Sunny (examples/white-label-minimal) with live updates over the Base44 platform socket, through @base44/platform (base44/javascript-sdk#301). Based on Oz's #31 and cut down to the minimal integration. Sunny itself (src/) is unchanged.

components/useBuilderSocket.ts → lib/chat/live-updates.ts → @base44/platform
lib/chat/builder-api.ts → app/api/base44/route.ts → lib/base44/socket-session.ts
  • Server (lib/base44/socket-session.ts, 20 lines): a new openBuilderSession action on the existing route, behind the existing sign-in and app-ownership checks. It opens a read-only session for one app with the workspace key (BASE44_SVC_KEY needs apps:watch). Only the session token reaches the browser.
  • Browser (lib/chat/live-updates.ts, 50 lines): subscribes. The snapshot replaces the chat on every (re)connect, then message.updated, message.removed and app.status_changed apply.
  • React (components/useBuilderSocket.ts): same interface as the polling hook it replaces, so Builder.tsx only swaps the hook. There's one error with Reconnect live updates.
  • lib/chat/live-messages.ts converts socket messages to the shape the existing chat UI renders, so the UI components are untouched.

Left out on purpose (listed in the README for production): closing the session on leave (it expires after an hour), history beyond the snapshot's 50 messages, and replacing image placeholders (image.resolved).

Tests: typecheck, unit tests (35, including new server and message-conversion tests) and the 22 Playwright tests pass locally against a local build of @base44/platform. The browser tests now run against a fake Base44 socket that serves each fixture's conversation.

package-lock.json pins the published @base44/platform@0.1.0.

🤖 Generated with Claude Code

@amitbardos
amitbardos force-pushed the amitb/platform-package branch from cfb4048 to eeb4267 Compare October 4, 2026 08:59
@amitbardos amitbardos changed the title Use @base44/platform in the minimal white-label example Live builder updates with @base44/platform in the minimal white-label example Oct 4, 2026
@amitbardos
amitbardos changed the base branch from codex/white-label-builder-socket to main October 4, 2026 08:59
@amitbardos
amitbardos force-pushed the amitb/platform-package branch from eeb4267 to 8eaf865 Compare October 4, 2026 13:05
@amitbardos amitbardos changed the title Live builder updates with @base44/platform in the minimal white-label example Live builder updates in Tiny Sunny with @base44/platform Oct 4, 2026
@netlify

netlify Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for base44-platform-starter ready!

Name Link
🔨 Latest commit 5037c5a
🔍 Latest deploy log https://app.netlify.com/projects/base44-platform-starter/deploys/6ac257856659fa000905b8c8
😎 Deploy Preview https://deploy-preview-50--base44-platform-starter.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for sunny44-builder ready!

Name Link
🔨 Latest commit 5037c5a
🔍 Latest deploy log https://app.netlify.com/projects/sunny44-builder/deploys/6ac257859335f2000821275f
😎 Deploy Preview https://deploy-preview-50--sunny44-builder.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@amitbardos
amitbardos force-pushed the amitb/platform-package branch from 8eaf865 to 2309245 Compare October 4, 2026 13:41
Replace chat polling in examples/white-label-minimal with the Base44
platform socket, through the Platform SDK (@base44/platform). Kept
minimal on purpose:

- server: lib/base44/socket-session.ts opens a read-only session for one
  app with the workspace key, behind the existing sign-in and ownership
  checks (openBuilderSession action).
- browser: lib/chat/live-updates.ts subscribes; the snapshot replaces the
  chat, then message.updated / message.removed / app.status_changed.
- React: components/useBuilderSocket.ts, same interface as the polling
  hook it replaces, so the UI is unchanged.

Socket messages are converted to the shape the existing chat UI renders
(lib/chat/live-messages.ts). Production follow-ups (closing the session,
history beyond 50 messages, image placeholders) are listed in the README.

Based on Oz Sayag's work in #31.

Co-Authored-By: Oz Sayag <ozs@wix.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@amitbardos
amitbardos force-pushed the amitb/platform-package branch from 2309245 to 5037c5a Compare October 4, 2026 13:41
@amitbardos
amitbardos marked this pull request as ready for review October 4, 2026 14:04
@amitbardos
amitbardos merged commit f2408c4 into main Oct 4, 2026
8 checks passed
amitbardos added a commit that referenced this pull request Oct 4, 2026
- client.ts is ordered by the six steps of "The build turn", one commented
  section per step with a link to the docs
- Publish button on the ready card: deploy, then read back the published URL
  and link to it (step 6), with a browser test
- remove the unused REST conversation read (getConversation and the backward
  pager); the chat arrives over the live-updates socket since #50
- README opens with a table from each docs page to the file that implements it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
amitbardos added a commit that referenced this pull request Oct 4, 2026
* Tiny Sunny: slim the integration files so they read easily

- api-handler: one action table replaces the switch; unread fields are rejected
- client and identity share one fetch helper (http.ts)
- static preview derivation and probe move to static-preview.ts; the probe
  cache is now module-level, so it survives across requests
- smaller config check, named page size, README copy list updated

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: simplify the UI components

- Workspace: derive the active name from the stage app, drop an unused ref,
  plain async load, connect call moves to builder-api
- Question: one buildPayload helper (null while empty) and a toggle helper
- Builder: one composerDisabled flag for the guard and the composer
- Base44Preview: drop the onStatusChange prop nobody passed
- remove unused CSS rules and an unreachable ToolActivity branch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: make client.ts a pure 1:1 Base44 wrapper

- encode app IDs in every path instead of relying on the handler's check
- status hints move to http.ts, next to the transport
- static preview is Tiny's own derivation, so it moves to app-service
- share the conversation page size with the browser paging helper
- link the Base44 OpenAPI spec from the client and the README

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: re-mint service-user tokens instead of refreshing them

- the stored token is a cache: reuse it until a minute before expiry, then
  mint a new one with the workspace key (same approach as main Sunny's remint)
- drops the OAuth refresh flow and the row lock (identity-repository.ts)
- one workspace key; .env.example and README name the two scopes it needs
- connection status returns only { linked }
- Disconnect button in the topbar; disconnect still revokes the refresh token
  and keeps the service user so reconnecting restores the same apps
- README: mint rate limit and encrypt-at-rest note

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: use the documented single-account model

Base44's tenancy docs describe one integration account that owns every app,
called with its personal access token. Tiny now follows that model:

- every call sends Authorization: Bearer <PAT> and X-Active-Workspace-Id;
  createApp sends organization_id so apps use the workspace's design system
- client.ts is a plain module object; no per-user client
- removes service users: identity.ts, the connection route, the Base44Link
  model, the connect/disconnect UI and its tests
- builder isolation stays where the docs put it: Tiny's ownership table,
  checked by the API handler before every app call
- README and .env.example describe BASE44_ACCESS_TOKEN and BASE44_WORKSPACE_ID

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: shorter origin check and body read in the API handler

- origin check is one rule: BUILDER_ORIGIN when hosted, a loopback Host locally
  (refuses DNS-rebinding pages; request.url is not used because Next rewrites it)
- read the body with request.text() plus a size check instead of a chunked reader
- log unexpected errors before replacing them with a safe message

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: render chat parts with assistant-ui's component API

- ThreadPrimitive.Messages with UserMessage / AssistantMessage, and
  MessagePrimitive.Parts components={{ Text, tools: { Fallback } }} instead of
  one render function with role and part-type branches
- tool parts read appId and onAnswer from a small context, because
  assistant-ui renders part components itself
- answers still go through onAnswer, not assistant-ui's resume(), so failures
  reach the Question and its retry-the-original-answer flow keeps working

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: say why a live-updates session was refused

Every 401/403 used to read "needs apps:watch", which hid the other causes.
Base44's error code now picks the message: missing scope, sockets not enabled
for the workspace, or a key that cannot watch the app. A missing
BASE44_SVC_KEY is reported before any request is made.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: follow the docs' build turn, add Publish

- client.ts is ordered by the six steps of "The build turn", one commented
  section per step with a link to the docs
- Publish button on the ready card: deploy, then read back the published URL
  and link to it (step 6), with a browser test
- remove the unused REST conversation read (getConversation and the backward
  pager); the chat arrives over the live-updates socket since #50
- README opens with a table from each docs page to the file that implements it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: show the latest build through the documented embed API

App cards used a static preview URL Tiny derived from the slug and then probed.
They now ask Base44 for it, as "Embed the app" documents: provision the builder
as an app user, mint a one-time sign-in token with target latest_preview, and
load the returned embed_url. Private apps open signed in as the builder.

- new lib/base44/embed.ts; removes lib/server/static-preview.ts and
  BASE44_STATIC_PREVIEW_DOMAIN
- PreviewFrame fetches a fresh URL on mount and after each build; the static
  iframe is no longer lazy, since the token expires in 60 seconds
- before the first build (app_has_no_slug) the card shows the screenshot
- BASE44_SVC_KEY also needs Provision app users and Mint embed sign-in tokens

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: sign the builder into the live preview too

The live preview now comes from "Embed the app" with target live_preview, like
the latest build in the cards. The builder is no Base44 user, so a private app
would otherwise show its own sign-in page inside the frame. Removes the
sandbox/preview-url call and the undocumented URL parameters Tiny added to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: every Base44 call in client.ts

- the embed and live-updates calls move into client.ts as their own sections,
  next to the build-turn steps; embed.ts and socket-session.ts are removed
- http.ts had one caller left, so its request() moves to the bottom of
  client.ts; it takes an optional auth header for the workspace-key calls
- Base44Error keeps Base44's error.code, which picks the live-updates message
- lib/base44 is now client.ts plus config, error and custom instructions

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: one name for live updates, end to end

- openSocketSession / openBuilderSession -> openLiveUpdates (server function,
  API action and browser call); watchApp -> watchLiveUpdates;
  useBuilderSocket -> useLiveUpdates, whose resume() is now reconnect()
- live-messages.ts merges into live-updates.ts: one browser file for the flow
- the hook names its state for what it is (session, readySession) and says
  when it is loading; behaviour is unchanged

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: a smaller Builder

- the ready card and its Publish step move to ReadyCard.tsx
- busy is the kind of action (create, send, answer), not its display text
- remove the "resume an existing app" flow: after an uncertain create the
  ownership was never saved, so there was nothing it could resume; the
  server's error already says the operation may still be running
- builder-api: drop the unused abort signal and notStarted flag

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: client.ts written like a guide

- AGENTS.md: style rules for the example (one call, one function; plain ifs;
  guard only what matters; comments say why)
- client.ts: one exported function per Base44 call, each showing its path,
  method and body; build-turn calls name their 120-second timeout; the
  request helper is a plain fetch with the token, workspace header and
  safe errors; status hints and defensive checks for impossible states removed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: one file per docs page in lib/base44

- build-turn.ts: steps 1-6 of "The build turn"
- embed.ts: "Embed the app"
- live-updates.ts: the live-updates socket session
- request.ts: the fetch all three share, and the workspace key

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: the API handler as a plain switch

- one case per action: allowed fields, checked values, then the call
- small named checks (checkOrigin, readBody, onlyFields, checkId, checkText,
  checkSkip) replace the field-tracking body reader and the action table
- the "outcome" field is gone: nothing reads it since the resume flow went

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: the builder reads one chat state

- lib/chat/chat-state.ts: getChatState() names where the build turn stands
  (loading, paused, question, building, failed, ready, idle); the "has the
  agent written code" check moves in from build-readiness.ts
- Builder decides with plain ifs on that state; the status line is a small
  Progress component with one if per message
- BuilderChat takes the state instead of five booleans

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: server actions instead of a generic API endpoint

Pages call app/actions.ts directly. Each action checks the signed-in builder
and, for an app, that they own it, then calls Base44. Next.js checks the
request origin and decodes the arguments, so the endpoint and its checks go:

- removes app/api/base44/route.ts, lib/server/api-handler.ts,
  lib/server/app-service.ts, lib/chat/builder-api.ts and BUILDER_ORIGIN
- actions return { data } or { error }, since Next.js hides thrown messages
  in production; lib/chat/unwrap.ts turns an error back into an exception
- browser tests: the fixture aliases app/actions to fake-actions.ts, which
  still sends each action to the mocked /api/base44 route
- server tests call the Base44 functions and the ownership check directly

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: each server action shows its checks

- every action reads: signed-in user, requireOwner for an app, then the Base44
  call; the ownApp/signedIn wrappers are gone, one returnErrors remains
- lib/storage/ownership.ts replaces the app repository with four plain
  functions: saveOwner, requireOwner (which also checks the ID is plain),
  listOwnedApps, removeOwner

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: remove what server actions left behind

- App drops slug, logo_url and user_description: nothing shows them
- remove the unused AppPage type and the unused @/ path aliases
- stop exporting types and classes only their own file uses
- lib/server held only auth.ts, which moves to lib/auth.ts
- README's folder table names app/actions.ts and the current lib folders

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: one form per kind of question

Question.tsx follows "Answer the agent's questions": one small form for each
waiting_on.kind (ChoiceForm, SecretsForm, approve/reject), and Reject only
for a question it does not recognize. The main component sends the answer
and, after a failure, resends the same answer with the same request ID.
parseQuestion reads each kind with plain loops instead of throw-and-catch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: server/ and client/ folders

- server/: Base44 calls (server/base44/), server actions, sign-in, ownership
  and the database
- client/: everything that runs in the browser, components included
- app/: Next.js routes only (page, layout, the sign-in callback)
- types.ts: what both sides share
- the fixture aliases server/actions to its fake, as before

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: the browser's build turn in one hook

client/useBuildTurn.ts holds everything the browser does with Base44, in the
order of the docs: open live updates (the platform client is created there,
in plain sight), send a prompt or create the app, answer a question, and the
chat state. Builder.tsx only renders it.

- removes client/live-updates.ts and client/components/useLiveUpdates.ts
- message conversion and the pending prompt move to client/messages.ts
- the fixture also aliases ../server/actions, imported by the hook

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: a readable Workspace

- useApps: the builder's app list (load, remove, add, update) moves out
- AppsPage: loading, empty and list states as plain ifs
- EditorPanel: the assistant panel and its mobile open/close and focus
- Workspace keeps one "app in the assistant" state; the separate stage app
  always held the same value
- a new app focuses the composer through autoFocus instead of a DOM query

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: one chat component

- BuilderChat merges into Builder: the nine pass-through props go; parts that
  assistant-ui renders read the turn from one context
- no hidden-message filtering: live updates never send hidden messages
- the assistant-ui message conversion joins client/messages.ts
- tool arguments are formatted once, when a live message is converted
- ToolActivity and the status line read as plain ifs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: one preview component

AppPreview replaces Base44Preview and PreviewFrame. It shows the latest build
and, while editing, loads the live sandbox behind it until it has loaded.
Refresh preview asks for new one-time URLs.

Removed with it: recovery through the undocumented preview:requestRefresh
message, its retries and timeout, and carrying URL parameters over from the
old sandbox preview URL. None of it applies to one-time embed URLs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tiny Sunny: link the API reference, not the platform host

Netlify's secrets scan failed every deploy preview since the README linked the
OpenAPI spec on the platform host, whose origin is the value of the secret
BASE44_PLATFORM_HOST. The README now links the Apps API reference in the docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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