Developer overview
Floor Walker is one monorepo. The Flex plugin started from the Twilio Professional Services Flex Project Template and keeps its feature system, Flex hooks and utilities; the backend services, the dialer and the desktop recorder were added next to it. For how the parts fit together at runtime, see Architecture.
Repository layout
| Directory | What it is |
|---|---|
plugin-tokin-flex/ | The Flex plugin (React, TypeScript). Features live in src/feature-library/. |
serverless-functions/ | Twilio Functions used by the plugin and Studio |
flex-config/ | Flex ui_attributes and TaskRouter skills per environment |
tokin-api/ | Backend API (Fastify, Postgres, MinIO): CRM, recordings, insights, calendars, wallboard, supervisor console, reports |
flex-dialer/ | Outbound dialer engine (engine/), its scripts and design notes |
packages/service-kit/ | @tokin/service-kit: configuration, auth, database, errors and health checks shared by tokin-api and the dialer engine |
flex-agent-screen-recorder/ | Electron desktop app for screen recording |
wallboard-kiosk/ | Vite app for the TV wallboard; the renderer itself lives in the plugin's feature-library/wallboard/board/ |
dialer-console/ | Vite app for the dialer console (/dialer/) and, built with --mode reports, the reports console (/reports/) |
deploy/, docker-compose.yml | The backend stack: Caddy, Postgres and MinIO setup |
infra-as-code/ | Optional Terraform for TaskRouter and Studio resources |
addons/ | Extra deployable packages (empty by default); see Add-ons |
scripts/ | Setup, deploy and maintenance scripts used by npm scripts and CI |
docs/ | This documentation site |
packages/service-kit, tokin-api and flex-dialer/engine are npm workspaces of the root
package.json. The plugin, serverless functions, recorder, kiosk and consoles have their own
package.json and lock file.
Prerequisites
- Node.js 20 or later (the backend images use Node 22).
- Docker with the compose plugin, for the backend.
- The Twilio CLI with a profile for a Flex account you can
change. The root
npm installinstalls a pinnedtwilio-clilocally, andnpm run install-serverless-plugin/npm run install-flex-plugin(inplugin-tokin-flex/) add the CLI plugins.
First-time setup
twilio profiles:use <your-profile>
npm install
The root npm install runs the setup script (scripts/setup-environment.mjs). For the active Twilio
profile it fills the .env files of the serverless functions and flex-config, creates
plugin-tokin-flex/public/appConfig.js from appConfig.example.js with the feature settings of
flex-config/ui_attributes.common.json (an existing appConfig.js is left alone), and installs each package. Run
npm run generate-env to repeat only the configuration step, and see
Configuration for its options.
Then the backend:
cp .env.example .env # fill the <random> values and the Twilio credentials
docker compose up -d --build # Postgres, MinIO, tokin-api, Caddy; add COMPOSE_PROFILES=dialer for the dialer
The API is on http://localhost:8088 (through Caddy) and http://localhost:8090 (direct). Put that URL in
custom_data.features.screen_recording.api_url in appConfig.js. For the dialer, copy
flex-dialer/.env.example to flex-dialer/.env first; see Outbound Dialer.
All backend settings are listed in the configuration reference.
For hot reload of the backend services inside Docker, add the dev overlay:
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
Or run a service on the host against the compose Postgres and MinIO: npm run dev -w tokin-api (reads the root
.env, where DATABASE_URL and S3_ENDPOINT point at the published ports).
Running Flex locally
npm start
This starts, in parallel:
| Process | Port | Script |
|---|---|---|
Serverless functions (twilio serverless:start, with the Node inspector) | 3001 | npm run start:serverless |
| Flex plugin dev server | 3000 | npm run start:plugin |
| Flex UI dev proxy (for Flex Insights) | npm run start:insights |
Open http://localhost:3000 and log in to your Flex account. The local plugin reads its feature settings from
public/appConfig.js, which overrides the hosted Flex configuration; the local serverless functions are used
because appConfig.js points the plugin at localhost:3001.
Callbacks from Twilio and Soniox need a public URL. The compose tunnel profile starts an ngrok tunnel to Caddy
(NGROK_AUTHTOKEN, NGROK_API_DOMAIN in .env); set PUBLIC_BASE_URL and S3_PUBLIC_URL to the tunnel URL.
The other apps:
| App | Command |
|---|---|
| Screen recorder | cd flex-agent-screen-recorder && npm run dev |
| Wallboard kiosk | cd wallboard-kiosk && npm run dev |
| Dialer console / reports console | cd dialer-console && npm run dev / npm run dev:reports |
| Docs | npm run start:docs (port 3010) |
Tests
| Package | Command | Notes |
|---|---|---|
| Plugin | cd plugin-tokin-flex && npm test | Jest with jsdom; Flex mocks in __mocks__/. npm run test:watch while working. |
| tokin-api | TEST_DATABASE_URL=... npm test -w tokin-api | Vitest. Integration tests need a throwaway Postgres; the harness recreates the schema for each test file and fakes MinIO, OpenRouter, Soniox and Twilio. |
| Dialer engine | TEST_DATABASE_URL=... npm test -w flex-dialer-engine | Vitest. Run npm run migrate -w flex-dialer-engine against the test database first (with DATABASE_URL set to it). Database tests are skipped without TEST_DATABASE_URL. |
| service-kit | npm test -w @tokin/service-kit | Vitest |
| Screen recorder | cd flex-agent-screen-recorder && npm test | Jest unit tests; npm run test:integration runs against a running tokin-api |
| Lint | npm run lint / npm run lint:fix | ESLint over the plugin, serverless functions and add-ons |
A throwaway Postgres for the backend tests:
docker run -d --rm --name tokin-test-pg -p 127.0.0.1:55432:5432 \
-e POSTGRES_USER=test -e POSTGRES_PASSWORD=test -e POSTGRES_DB=tokin_test postgres:16-alpine
TEST_DATABASE_URL=postgres://test:test@localhost:55432/tokin_test npm test -w tokin-api
Use a separate database for the dialer engine. Type checks: npm run typecheck -w <workspace>.
After changing a drizzle schema, run npm run db:generate -w tokin-api (or -w flex-dialer-engine) and commit the
generated SQL in drizzle/.
The same checks run on every pull request; see CI/CD.
Conventions
- Localization. Every user-facing string in the plugin goes through a string hook, with an English text and a
Hebrew translation in the feature's
he.json. Hebrew switches the layout to right-to-left; use theuseDirectionhook fromsrc/utils/rtlonly when logic depends on direction. See Strings. - UI. Floor Walker screens use Ant Design inside
ThemedLayout(src/utils/design/), which also handles RTL; Twilio Paste is available for Flex-styled elements. - Backend calls. Plugin code calls tokin-api through
TokinApiClient(src/utils/tokin-api/), which sends the agent's Flex token. Use serverless functions only for work that needs Twilio credentials.
In this section
- Developer setup: editor, formatting and debugging.
- Backend API: tokin-api modules, routes and auth, and the dialer engine API.
- Feature management: how features are structured, added and removed.
- Flex hooks: every hook type a feature can register.
- Plugin utilities: configuration, logging, serverless helpers and more.
- Add-ons: extra deployable packages.