Snapshot the code-bearing fields of routes (verb/path/handler), templates (name/content/test_object), and db libraries (name/library) on every content-changing save, so nothing edited is ever lost. Recorded in a new `versions` table via recordVersion() hooked into the create/update functions in db.js (the single write choke point), deduped against the latest snapshot so no-op saves, error-flag-only route writes, and server-restart WS re-bootstraps don't pile up. The live row stays current; old snapshots are the undo trail. Read-only, no revert UI yet. - migrations/004_add_versions.sql: versions table + indexes - db.js: recordVersion/getVersions/getVersion + create/update hooks - plumbing.js: /versions/:id and per-entity .../versions list endpoints - bliss: `versions <type> <sid> <id>` and `version <id>` reads; `update-settings` (edit route_prefix/head_injection, preserving the untouched field); `use <url>` sticky target persisted to ~/.bliss/target so BLISS_URL needn't be re-exported each call - client.js: target-file precedence (BLISS_URL > ~/.bliss/target > localhost) - SKILL.md: document the above Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
148 lines
5.8 KiB
Markdown
148 lines
5.8 KiB
Markdown
---
|
|
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).
|
|
|
|
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
|
|
```
|
|
|
|
## 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.
|
|
|
|
## 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)
|
|
|
|
bliss versions <route|template|db> <sid> <id> # save history (newest first)
|
|
bliss version <versionId> # one version incl. full snapshot
|
|
```
|
|
|
|
All emit JSON, so: `bliss structure 3 | jq '.routes[].path'`.
|
|
|
|
## 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
|
|
```
|
|
|
|
## Writes
|
|
|
|
```bash
|
|
bliss create-structure "<name>" # -> {location, id}
|
|
bliss update-settings <sid> [--route-prefix <p>] [--head-injection <s>|--head-injection-file <f>]
|
|
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
|
|
```
|
|
|
|
## 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).
|
|
|
|
## 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.
|