Delivery
One application, three ways to run it. Nothing is rewritten between them.
Command line#
npm install
node index.js 8000bin/totem.js (bin of package.json, Node.js 22 or newer) wraps it for users:
| Command | Does |
|---|---|
totem [port] | Starts the server on port 8000, or the one you give (totem 4524), in the background, shows the banner and the address, opens the browser and gives the terminal back. The process, the port and the log are in totem-server.json and totem.log of the data folder |
totem start [port] | The same, without opening the browser |
totem stop, totem status, totem restart | Stop the server that totem started, say where it runs, or both |
totem serve [port] | Runs the server in this terminal until Ctrl+C. --verbose shows the whole output |
totem run "prompt" [--project path] [--model name] [--url server] [--token t] [--auto-accept] [--yes] | Headless: creates a session, subscribes to its events, sends the prompt, prints the answer on stdout and tool calls on stderr, exits 0 when the run ends idle and 1 otherwise. Without --url it starts a temporary local server. Permission questions are rejected unless --yes (allow once) or --auto-accept (file edits) is given |
totem config | Prints the path of the config and data folders |
Today the data folder is databases/ inside the application folder (totem config prints it). Moving it to a per-user folder is an open decision. A project can carry .totem/ for skills.
Docker#
The image is the application and nothing else (Dockerfile, .dockerignore):
- Base: the maintained Node.js LTS on Alpine, pinned by digest (Dependabot opens a pull request with the new digest when Node.js publishes a release), plus
python3for the interactive shell andgitfor the Git panel. - Projects are bind-mounted (
/workspace), data is a volume (/data, linked todatabases/), the access token is a Docker secret (/run/secrets/totem_token, read whentokenin the config is empty). - Runs as the non-root user
node, in release mode (--release: without the watcher). Theconfigof the image is the one of the application with$ip : 0.0.0.0andtokenfileset at build time. The server refuses to start (exit code 1) when it listens beyond this computer without an access token: that rule is in the application, so it also holds outside Docker. - Because commands run inside the container, this is also the isolated mode recommended for untrusted models (security).
docs/docker-compose.ymlis the example for the common case. The registry and the tags per version belong to the release workflow (not built yet).
docker build -t totem .
echo "a-long-random-token" > token.txt
docker run --rm -p 127.0.0.1:8000:8000 -v "$PWD/token.txt:/run/secrets/totem_token:ro" -v "$PWD:/workspace/project" -v totem-data:/data totemnode scripts/docker-smoke.js [image] [folder] is the smoke test: no token means no start; with a token it serves, refuses a request without it, opens the mounted project, lists its files and runs a command in the shell as a user that is not root. CI runs it (.github/workflows/test.yml) next to the unit tests. On Colima and Docker Desktop the folder for the temporary files must be shared with the virtual machine (the home folder, not /tmp).
Native windows#
A native app is a thin shell: it starts the same server on a random local port with a one-time token, loads http://127.0.0.1:<port> in a web view, and stops the server when the window closes. It adds only what a web page cannot do: a folder chooser, menu bar, notifications, file association, auto-start of the server, and update checks against the project's own release feed.
Shell technology is an open decision (decisions). Whatever it is, it must keep these properties:
- The server inside is the unmodified application (bundled Node.js runtime).
- macOS builds are signed and notarized; Windows and Linux artifacts are checksummed.
- The release workflow builds from a tag, per operating system and architecture, and publishes checksums with the installers.
Releases#
- One version number for the application, the image and the native builds:
package.json, theconfigfile and a heading inCHANGELOG.mdsay the same. - A release published on GitHub with the tag
v<version>starts.github/workflows/release.yml; nothing publishes from a branch. The workflow checks the release (
node scripts/release-check.js v<version>: version, tag, changelog, exacttotal5, and a README that tells how to install it), runs the unit tests and the smoke test of the image, and then publishes two things:- The package
totaljs-totemon npm, with trusted publishing and the provenance of the package: no secret. The package ships the bundle of components (npm run bundle), so the installed application does not download anything or write in its own folder. Installed from npm, the data goes to a folder of the user (~/Library/Application Support/Totem,~/.local/share/totem,%APPDATA%\Totem;totem configprints it,--data <folder>changes it). - The image
ghcr.io/totaljs-es/totemforlinux/amd64andlinux/arm64, taggedlatest,<version>and<major>.<minor>. It uses theGITHUB_TOKENof the repository.
- The package
- Release notes list user-visible changes only.
Releasing#
- Change the version in
package.jsonandconfig, add its heading toCHANGELOG.md, update the README (how to install it, the Status) and wait for the continuous integration to pass. The first time only, npm needs a way in. A trusted publisher is configured on a package that exists, and
totaljs-totemdoes not exist until its first version is published. Either:- add the secret
NPM_TOKENto the repository (or to the organization) with a token that can publish packages: the first release publishes from GitHub, with the provenance of the package; or - publish 0.1.0 by hand from your computer:
npm run release-check,npm publish --access public.
Then, in the settings of the package on npmjs.com, add the trusted publisher: GitHub Actions, organization
totaljs-es, repositorytotem, workflowrelease.yml, no environment. From then on the workflow publishes without a token, and the secret can be deleted.- add the secret
- Create a release on GitHub with the tag
v<version>. The workflow skips the version that npm already has (the first one), and publishes the image. - The first image only: the package
totemappears in the packages of the organization as private. Make it public in its settings (Change visibility), ordocker pullasks for a login. - From then on, every release publishes both without anything by hand.
The website#
https://totaljs-es.github.io/totem/ is made from two places, so that the application and its website stay apart.
- The branch
sitehas the website: the templates, the styles and images (the look of the Spanish Total.js websites: the styles of the CDN andcss/corp.css, the header and the footer of those websites, Toto), the screenshots, and the scripts that build it (scripts/build-site.js), take the screenshots (scripts/screenshots.js) and make the card for the social networks (scripts/og.js). It has no history in common withmain, and nothing of it goes in the npm package or in the Docker image. - The branch
mainkeeps the text: every page ofdocs/, the changelog and the version are the pages of the website. They live next to the code that they describe, and the website is built from them.
The workflow .github/workflows/pages.yml of main checks out both branches, runs node scripts/build-site.js --app <main> of the branch site and publishes the result. It runs when the documentation, the README, the changelog or the version change, and when the workflow of the branch site asks for it (a push to that branch). A link, an image or a section that leads nowhere, an icon that the font of the websites does not have, or a placeholder that was not filled stops the build. Set up once: Settings of the repository, Pages, Source: GitHub Actions.
To work on the website: git worktree add ../totem-site site, then node scripts/build-site.js --app ../totem in that folder writes dist/.