Package Exports
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (walla-page) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Walla Page
Walla Page is a CLI-first wall runtime for agents.
One room maps to one Cloudflare Durable Object. A controller (CLI/API/agent) schedules scenes and speech. Display clients subscribe over WebSocket and render the live state.
Package Manager
This project uses npm.
- Install dependencies with
npm install - Run the CLI locally with
npm run walla -- ... - Publish the package to npm as
walla-page
Keeping one lockfile is simpler for contributors, and npm is the most common default in open source JavaScript projects.
Install
The published npm package is just the Node CLI. Installing it does not require Wrangler.
For CLI users:
npm install -g walla-page
walla helpFor local development in this repo:
npm installWrangler is only needed if you are developing or deploying the Cloudflare worker from this repository.
Contributing
npm install
npm run walla -- helpScope (Current Build)
- Control plane is CLI/API only
- Display UI is receiver-only (no room creation web app)
- No R2 dependency
This keeps the hackathon surface area small and testable.
Core Capabilities
- Show fullscreen HTML now (
show, alias:push) - Schedule fullscreen HTML for later (
schedule) - Generate ElevenLabs speech and schedule it (
say) - Delete room state and disconnect clients (
delete,delete --force) - Rotate display access token (
display)
Current Behavior Notes
delete --forceis the explicit “disconnect clients and remove room state” path.
Security Model
- Capability token auth for room operations
- Separate room-control and display token roles
- Display page requires user click (
Load Wall) before connecting/playing audio - Scene HTML is rendered in a sandboxed iframe (
srcdoc) - TTS is rejected unless at least one display websocket is connected
- TTS has server-side word limit (
TTS_MAX_WORDS) - CLI validates
sayword count up front (--max-words)
Room Model
One room contains:
- Current scene
- Scheduled upcoming scenes
- Pair tokens for room control and display access
- Live websocket sessions
- Room settings (for example display websocket limit)
Display Fanout and Limits
Displays are multi-screen by default.
- Default: public display link per room
- Default: unlimited display websocket connections per room
- Optional cap at room creation via
displayLimit - When capped and full, new display ws attempts return
429
CLI flag:
walla create --display-limit <n>walla create --private-displayn >= 1sets a cap0means unlimited--private-displayrequires a display token instead of a bare room link
Alarm + Lifecycle
Each room maintains one DO alarm set to the next relevant timestamp:
- next scheduled scene start
- current scene end
- cleanup deadline
On alarm:
- expire finished scene
- activate due scenes
- evaluate cleanup conditions
- set next alarm
Cleanup Semantics
Automatic
- When last socket disconnects, room sets
idleSinceandcleanupAt - Default idle TTL is 7 days (
ROOM_IDLE_TTL_MS=604800000) - If no sockets and no future scenes at cleanup time, room storage is deleted
Explicit
DELETE /api/rooms/:roomIdwalla delete [--force]
Without --force, delete fails with 409 if sockets are connected.
With --force, all sockets are closed and room state is removed.
CLI
Run:
npm run walla -- helpCommands:
walla create [--display-limit <n>] [--private-display]walla delete [--force]walla displaywalla configwalla logoutwalla statuswalla quickstartwalla show <file.html> [--title <title>] [--duration <seconds>]walla push <file.html> [--title <title>] [--duration <seconds>]walla schedule <file.html> --at <time> [--title <title>] [--duration <seconds>]walla say <text> [--at <time>] [--title <title>] [--duration <seconds>] [--voice-id <id>] [--max-words <n>]
Config file:
~/.config/wallapage/config.jsonHTTP API
Room lifecycle:
POST /api/roomsDELETE /api/rooms/:roomId(?force=1for forced delete)
Room operations:
GET /api/rooms/:roomId/statePOST /api/rooms/:roomId/pairPOST /api/rooms/:roomId/schedulePOST /api/rooms/:roomId/ttsPOST /api/rooms/:roomId/showcaseGET /api/rooms/:roomId/ws
Create payload:
{
"displayLimit": 0,
"publicDisplay": true
}displayLimit >= 1: cap displaysdisplayLimit = 0or omitted: unlimitedpublicDisplay = trueor omitted: bare room display linkpublicDisplay = false: token-protected display link
Display route:
- Public:
GET /rooms/:roomId - Private:
GET /rooms/:roomId?token=...
Built-in audio route (allow-listed asset endpoint):
GET /audio/campfire-demo.mp3
Quickstart
Install and run local worker:
npm install
npm run devCreate room (unlimited displays):
npm run walla -- createOr create room with display cap:
npm run walla -- create --display-limit 4Open printed display URL on wall screens and click Load Wall.
Drive the room:
npm run walla -- status
npm run walla -- show ./examples/campfire.html --duration 120
npm run walla -- push ./examples/campfire.html --duration 120
npm run walla -- say "Dinner in five minutes."
npm run walla -- delete
# if displays are still connected:
npm run walla -- delete --forceEnvironment
Worker vars:
BASE_URLROOM_IDLE_TTL_MSCREATE_ROOM_LIMITCREATE_ROOM_WINDOW_MSTTS_LIMITTTS_WINDOW_MSTTS_MAX_WORDSDISPLAY_TOKEN_TTL_MSPRODUCER_TOKEN_TTL_MS
Secrets:
ELEVENLABS_API_KEY(required forsay//tts)ELEVENLABS_VOICE_ID(optional default voice)
Set secret:
npx wrangler secret put ELEVENLABS_API_KEYDeploy:
npm run deployTesting
Typecheck:
npm run checkSmoke test:
npm run test:smokeFuture (Post-Hackathon)
- Multi-tenant room ownership and room listing
- PIN-based short display URLs
- Expanded allow-listed audio catalog
- More policy controls (rate/quotas by room/tenant)