2026-08-02 23:00:26 -04:00
---
name: bliss
description: Drive a remote Bliss instance from the command line the way a human uses the /workshop UI — create/edit structures, routes, templates, databases, run the db REPL, read logs. Use when asked to build, inspect, or modify Bliss Structures against a running instance.
---
# bliss CLI
`bliss` talks to a running Bliss instance exactly as a human does through the
workshop UI. **Reads** come back as JSON from the `/plumbing` API (pipe into
`jq` ). **Writes** go through the same `/workshop/*` endpoints the UI posts to.
## Setup
The CLI is at `bliss-cli/bliss` in the repo. Configure via env vars:
```bash
export BLISS_URL=https://your-instance.example.com # default http://localhost:3000
export BLISS_USER=you
export BLISS_PASS=secret
```
It logs in once and caches the session cookie under `~/.bliss/` , re-logging-in
automatically when it expires. Run it as `node bliss-cli/bliss <command>` (or
`./bliss-cli/bliss` if on PATH).
2026-08-03 00:27:24 -04:00
Instead of re-exporting `BLISS_URL` every time, set a **sticky target** once —
it persists to `~/.bliss/target` and is used until you change it (a `BLISS_URL`
env var still overrides it when set):
```bash
bliss use https://your-instance.example.com # set the target instance
bliss use # print the current target
```
2026-08-02 23:00:26 -04:00
## The model (what you're editing)
A **Structure** is a mini web app. It has **routes** (a VERB + path + a JS
`handler` string run in a sandbox), **templates** (Eta, with a `test_object`
used for preview), and **databases** (each with a `library` JS module and its
own SQLite file). Ids are integers. Build a structure by: create it → add a db →
write its library → add routes whose handlers call the library → add templates
the handlers render.
2026-08-08 17:57:14 -04:00
**Templates are hypermedia, not escaped-string templates.** Default to `<%~ %>`
(raw) over `<%= %>` (escaped) in Eta content — only use `<%= %>` inside an HTML
attribute value, where an unescaped `"` would break the tag. Reach for htmx
attributes (`hx-trigger="every 10s"` , `hx-swap-oob` , etc.) instead of JS
`setTimeout` /`fetch` polling loops. See CLAUDE.md's "hypermedia homestead"
section for the full rationale.
2026-08-02 23:00:26 -04:00
## Reads (JSON)
```bash
bliss structures # list all structures
bliss structure < sid > # structure + its routes, templates, dbs, files
bliss route < sid > < rid > # one route incl. its handler source
bliss template < sid > < tid > # one template incl. content + test_object
bliss db < sid > < dbid > # one db incl. its library source
bliss files < sid > # files attached to a structure
bliss logs < sid > < rid > [--since < id > ] # route logs (console.log output + errors)
2026-08-03 00:27:24 -04:00
bliss versions < route | template | db > < sid > < id > # save history (newest first)
bliss version < versionId > # one version incl. full snapshot
2026-08-02 23:00:26 -04:00
```
All emit JSON, so: `bliss structure 3 | jq '.routes[].path'` .
2026-08-03 00:27:24 -04:00
## Version history (undo trail)
Every content-changing save of a route handler, template, or db library records
an append-only snapshot. The live row is always the current version; old ones
are kept so nothing edited is ever lost. `versions` lists the summaries (id,
`created_at` , `bytes` ) newest-first; `version <id>` returns the full snapshot
(the versioned fields). No-op re-saves are deduped, so identical content doesn't
pile up. There's no revert command yet — to roll back, fetch an old snapshot and
re-`update-*` its content:
```bash
bliss versions route 3 12 # what saves exist
bliss version 24 | jq -r .snapshot.handler > /tmp/old.js
bliss update-route 3 12 --handler-file /tmp/old.js # restore it as a new save
```
2026-08-02 23:00:26 -04:00
## Writes
```bash
bliss create-structure "< name > " # -> {location, id}
2026-08-03 00:27:24 -04:00
bliss update-settings < sid > [--route-prefix < p > ] [--head-injection < s > |--head-injection-file < f > ]
2026-08-02 23:00:26 -04:00
bliss clone < sid > --name "< n > " --prefix /p [--clone-dbs]
bliss create-route < sid > < VERB > < path > # VERB: GET|POST|PUT|DELETE|WS ; -> {id}
bliss update-route < sid > < rid > --handler-file < f | - > # replace handler code
[--scaffold-file < f > ] [--path < p > ] [--verb < v > ]
bliss create-template < sid > "< name > " # -> {id}
bliss update-template < sid > < tid > --content-file < f | - > [--test-object-file < f > ]
bliss create-db < sid > "< name > " # -> {id}
bliss attach-db < sid > < dbid > < alias > # share an existing db under an alias
bliss update-library < sid > < dbid > --file < f | - > # returns eval output/errors
bliss repl < sid > < dbid > '< code > ' # run JS/SQL against the db; --file < f | - > also works
bliss upload < sid > < path > # upload a static file
```
## Previews
```bash
bliss preview-route < sid > < rid > # rendered HTML of a GET route
bliss preview-template < sid > < tid > # rendered HTML using the template's test_object
```
2026-08-02 23:19:24 -04:00
## Links
Jump straight into the workshop editor for something instead of constructing
the URL by hand:
```bash
bliss link structure < sid >
bliss link route < sid > < rid >
bliss link template < sid > < tid >
bliss link db < sid > < dbId > # by numeric db id
bliss link db < sid > < alias > # or by the alias used in require('db')(alias)
```
`bliss link db` resolves an alias (e.g. `chat` , as in `require('db')('chat')` )
by looking at the structure's attached dbs — handy since the same alias name
often points to a different db per structure (aliased/cloned dbs).
2026-08-02 23:00:26 -04:00
## Editing code (handlers, templates, library)
Handlers/templates/library are multiline code. Write the code to a temp file and
pass `--*-file` , or pipe via `-` (stdin):
```bash
cat > /tmp/handler.js < < 'EOF'
function handler(req, res) {
const { library } = require('db')('main');
res.render('home', { items: library.all() });
}
EOF
bliss update-route 3 12 --handler-file /tmp/handler.js
echo "function handler(req,res){ res.send('hi') }" | bliss update-route 3 12 --handler-file -
```
The db `library` runs with `sql` (the better-sqlite3 instance) in scope and sets
`module.exports` . Test it fast with `bliss repl <sid> <dbid> 'library.someFn()'` .
## Typical loop
1. `bliss structures` / `bliss structure <sid>` to orient.
2. Edit a handler/template/library into a temp file, push with the matching
`update-*` command.
3. `bliss preview-route` / `bliss repl` to see the result; `bliss logs` to debug.