@dokebi/cli
v0.4.33
Published
Project scaffolding, plugin catalog, recipes, install and verification CLI for Dokebi.
Maintainers
Readme
@dokebi/cli
The tool that creates Dokebi projects and installs plugins, with the installed version recorded in package.json (0.4.0 was the stage-3 release, D-0067). The command is dokebi. What changed since 0.1.1 (YAML settings, migrate, multi-app and module commands) is in the CHANGELOG. Node.js >=24.0.0, ESM; the default CI runs Node 24.
Running it
- Outside a project:
npx -y @dokebi/cli@<version> init <new folder>(the version may be left out). Inside a project:npx dokebi …(init puts@dokebi/cliin devDependencies, so that copy runs). - The npm package named
dokebiis not this CLI:npx -y dokebiruns someone else's code, never use it. - Use
--jsonfor one machine-readable document on stdout, except for interactivedev,logsandstart --foreground. Use--dry-runonly on commands that list it in help (plan only; it may download and install into a temporary folder to read packages, but never applies managed files or node_modules).dokebi --helplists every command;dokebi <command> --helpexplains each flag of one command with an example.
AI entry (D-0065)
dokebi guide: the AI first page (steps, rules, recipes, what Dokebi does not have, commands).dokebi --helpstarts by pointing to it.dokebi recipe listanddokebi recipe <name>(blog,api,site): without answers they install nothing and return the questions withwaitForUser(ask the user, end your turn); with answers they plan and apply. Applying needs the source of the answers,--answers-from useror--defaults;--answers-from useronly for choices the user or the request explicitly made, and anything the AI decided goes in--ai-chose(with--ai-reason), so each answer records who made it (D-0095). The result'sreport(answers, defaults, how the forms send the CSRF token, where the admin login is) goes to the user. Theblogrecipe installs @dokebi/csrf for its write forms, and--admin loginsets up @dokebi/auth with the app's own user table, a login page and a first admin (D-0076 §6).dokebi plugins list|search|info|doc: the catalog inside this CLI (catalog.json) and what Dokebi does not have (absent: two-factor login, OAuth, markdown, rss, sqlite, React, ORM, admin generator, search).plugins docprints the AI.md of the installed version.dokebi verify: doctor, check, typecheck, build, test (the project'snpm test; with node:test a result per test, a leading[ID]of a name as itsid), start, HTTP checks and stop; say done only when it isok. HTTP counts only answers of the app verify started (thex-dokebi-verifytoken), and stop uses that pid withoutps.dokebi describe: what the app has, without serving (D-0077): routes with their id, schema and login rule, pages (EJS views, Inertia components), jobs, schedules, error codes, option keys and environment variable names (never a value). It loads the project likestartin core's describe mode: every setup runs, nothing listens or connects.--jsonfollowsdescribe.schema.jsonin this package.dokebi describe --openapi [--app <name>]: an OpenAPI 3.1 document per app from the same description (D-0077 C):operationIdis the route id, request and response schemas come from the route schemas, the error answers use the D-0074 body (422VALIDATION_FAILED, 401, 403 for roles,canand CSRF),cookieAuthandbearerAuthfollow the login rules, the app's error codes arex-dokebi-errors. Several apps without--app:{ apps: { <name>: document } }.dokebi check: user-code convention rules (D-0072):empty-catch,missing-cause(a catch that throws a new error without the caught one, D-0118),floating-promise,non-erasable-syntax,private-import,missing-package,generated-file,secret-in-config; the warninginertia-form-only(D-0074); and, with @dokebi/auth or @dokebi/csrf installed,plain-password-compare,form-without-csrfandcsrf-exempt-unverified(D-0076 §6);background-timer(periodic work on the app's own timers) and the warningsresponsibility-sizeandmemory-filtered-page(D-0105). Each problem has file, line and fix;// dokebi-check: allow <rule> <reason>silences an intended case.dokebi config show|set|unset|secretanddokebi dev-infra(compose.yaml and the URL of the development database in dokebi.yaml, with its password; no .env is written).config unset <id>.<path>...removes options from dokebi.yaml and dokebi.example.yaml without printing them (D-0153; the fix of an unknown key names it).config show [<id>[.<path>]]prints the options with every secret hidden (<secret: set>): use it instead of cat or sed on dokebi.yaml.config secret <id>.<path> --rotatereplaces a secret; a list keeps the previous two after the new one, so what they signed still verifies.- Secrets live in
dokebi.yaml(0600, kept out of Git; D-0073 addendum 2):addwrites the secrets a plugin declares (for example @dokebi/session'ssecrets) and reports only their names (generatedSecrets);config secret <id>.<path>generates one later. An environment variable is only an optional production override where a plugin documents one. An existing.envis still passed to the app bydev,startandverify(a variable already set wins; a malformed line isENV_FILE_INVALID). - The full contract is in AI.md §0.
Commands
- dokebi init [--core ] [--dry-run]: prepares a project with core and the required config. devDependencies get this CLI (same version), typescript and @types/node, scripts
startandtypecheck, AGENTS.md (with the installed list) and CLAUDE.md, and the result names the next steps (nextSteps). The folder must be new or empty (a folder with only.git,.npmrc,.npm,.npm-cache,.DS_Store,Thumbs.db,.idea,.vscodeworks too:init .). Anything else isPROJECT_EXISTS. - dokebi add [--set =] [--adapter ] [--startup <required|optional>] [--no-first-app] [--dry-run]: installs and registers an optional plugin (and the plugins it requires). Without a version it picks the newest version for this core.
- dokebi upgrade [--to <version|package|tgz>] [--dry-run]: changes a plugin's version. A changed manifest under the same version is applied again.
- dokebi remove [--dry-run]: removes an optional plugin nothing depends on. Required parts cannot be removed.
- dokebi app add|remove|link|unlink|list:
src/apps/<app>and its domains (@dokebi/app).app add --inertia [--ssr]also prepares the Vue skeleton, the build and server rendering. - dokebi module add|remove|list: feature modules
src/shared/modules/<module>(@dokebi/module). - dokebi migrate [--dry-run]: moves an existing JSON project to the YAML contract, explicitly.
- dokebi inspect, dokebi doctor: read-only checks of installed versions, manifests, schemas and settings (
--verbosefor everything;--jsonis always complete). - dokebi dev: a development run that restarts when code or settings change (with a Vite config and a current template-inertia, the CLI shares one Vite dev host with HMR across app workers).
- dokebi start [--foreground] [--log-max-mb ] [--log-files ] · stop [--force] · status · logs [-n ] [-f]: background run, stop, state and logs in
.dokebi/run/app.pidandapp.log. Wherepsis blocked (agent sandboxes),stopandstatusprove the recorded pid is this app through its log writer's loopback control port (proof: log-writer) or by an HTTP challenge the app answers (@dokebi/web,proof: http), both keyed with the per-startDOKEBI_INSTANCE_TOKEN; without a proof they refuse withPROCESS_INFO_FAILEDas before. Where this shell may not even signal the app (a sandbox per command), the log writer, which started the app, stops it on request (via: log-writer). A separate writer process takes the log through a pipe, loses no line and rotates by size (default 50 MB,app.log.1…app.log.5, D-0053).logs -nreads rotated files too, and-ffollows across rotations (it tracks files by inode, so it skips no line even when several rotations happen between two checks). There is no automatic restart, so in production usestart --foregroundunder systemd or Docker (D-0044).
The generated app runs with npm start. Call the commands with the installed CLI from the project root (or an npm workspace member folder).
Start screen
When a person runs dokebi (no arguments), dokebi --help, dokebi guide or dokebi init in a terminal, a logo comes first: the Dokebi symbol on the left (half-block ▀▄ characters, 20 columns × 10 lines: two horns, a round head, a white face, two eyes), a large DOKEBI in a cyan → blue → purple gradient on the right, and below them Dokebi CLI v<version> (the name in bold white, the version in gray) and one dim line New here? Run: dokebi guide · Help: dokebi --help. Other commands stay quiet.
- The logo goes to stderr only. stdout (help, result JSON, the
--jsonenvelope) is byte for byte the same. - It appears only when stderr is a TTY, and never with
--json,CI,NO_COLOR,TERM=dumborDOKEBI_NO_BANNER=1. - Colors: 24-bit with
COLORTERM=truecolor(or24bit), otherwise a 256-color approximation; a terminal without colors gets the single lineDokebi CLI v<version>. - Width: below 70 columns only the symbol and the title line, below 30 columns only the title line.
- The picture is made at build time:
node scripts/brand/render-logo.mjsreadspackages/cli/brand/symbol.png(not in the npm archive) and writessrc/banner-art.ts(a generated file). No image library is used (only node:zlib).npm run checkconfirms that a fresh render equals the file.
Settings and running
The real settings are one file, dokebi.yaml: schemaVersion: 1 and an options object; app.config is the whole of options. init creates the real YAML with mode 0600, keeps it out of Git and prepares an empty dokebi.example.yaml. Real secrets are never copied into the sample, the state or generated code.
On install, the safe defaults, descriptions and required-value notes the schema states go into the YAML, and existing values and comments are kept. Secrets and connection URLs are never made up. writeOnly: true is the main mark of a sensitive setting; sensitive key names and connection URLs are recognized as a fallback. Defaults of composite schemas are not guessed, and a missing optional object is not created from its children's defaults alone. Runtime validation does not insert defaults, convert types or delete fields. Schema checks (JSON Schema, Ajv) run only in CLI commands (add, config set, doctor); at start each plugin factory checks its own options against its declaration (the generated code has no validator, D-0070). A wrong value stops the start with INVALID_PLUGIN_OPTIONS, naming options.<plugin>.<path>.
When the only errors after preparing a new plugin's settings are missing required values, the install finishes and configRequired reports the unfinished settings (at most 20 shown, with the total errorCount). doctor and the app fail until the values are filled. Type, range, additional-key and composite errors, or setting errors of plugins already installed, block the install. The check collects all errors, so other errors after a missing required value are not hidden.
After a remove the user's options are kept, and unregistered keys the app may use as its own settings are never deleted. The generated entry point runs YAML loading → Application → option checks → create and register plugins → start. A factory's app and context.app in setup and start are the same ApplicationView: one frozen view per app with only state, config and logger (no start, stop, register or get). Plugin factories take { app, options?, entry? }.
Moving a JSON project
migrate needs core 0.2.0 or newer. When YAML and JSON both exist it stops on the conflict and does not merge. The JSON original is kept in .dokebi/dokebi.json.backup with mode 0600 and the active JSON is removed. An existing backup is never overwritten. The generated registration code, the Git exclusions and the default entry point are updated in one transaction.
An entry point the user edited is never guessed at and overwritten: move it to the new loading and registration flow the CLI describes, explicitly. Read the exact conditions and errors; never blindly retry after a failure, and never delete the lock or backups.
Plugin files
A plugin can declare empty folders with dokebi.install.directories and template copies with dokebi.install.files. Each file's owner says who edits it: user (the default: a skeleton the user edits; doctor does not warn and upgrade keeps an edited file) or cli (a file the plugin writes again; editing it is doctor MODIFIED_TEMPLATE and check generated-file). Entry files are always user. Targets are under src/, except reserved files. Manifest schema 2 can also declare dokebi.install.entry (the user entry file) and configDefaults; the top-level dokebi.files of schema 1 is merged only for reading old manifests. Schema-based YAML setup is handled in the same step. The CLI has no special case for any plugin ID: app and module commands use the plugin that declares dokebi.hosts (apps, modules), and dokebi.contributes and dokebi.collects must equal the contribution points the code declares (D-0070). A declared installModule runs planInstall, planUpgrade and check isolated under the Node permission model (no writes, no child processes, no environment variables). npm lifecycle scripts never run. The details are in the install lifecycle. A failure rolls back the empty folders it created; remove keeps folders.
The catalog entry of a package is dokebiCatalog in its package.json (outside the manifest): kind, an English one-line summary, maturity, provides (capabilities from a closed dictionary), variants, doesNot and recipes. The repository builds catalog.json from it and checks it (AI.md §6).
User-edited templates are kept on remove. Install scripts never run, but package code runs when the app runs, so use plugins you trust. git or URL dependencies and automatic recovery after a forced kill are not supported. Windows and production hardening are outside the verified scope, as the repository records say.
MIT license. See the AI contract and the repository.
Development mail (D-0078 MP4)
The blog and API recipes ask --mail none|mailpit|smtp|ses (default none). mailpit starts a local Mailpit inbox and NATS queue using compose.mail.yaml, with free loopback ports selected and persisted in Compose by the CLI so restart keeps the saved YAML addresses. Existing compose.yaml is preserved. Find the inbox with docker compose -f compose.mail.yaml port mailpit 8025; stop it with docker compose -f compose.mail.yaml down (-v also deletes queued messages). SMTP and SES require an installed @dokebi/mail with the selected transport and sender configured in dokebi.yaml; credentials never belong on a command line. Missing settings produce MAIL_CONFIGURATION_REQUIRED before applying. Recovery with mail also needs a provisioned job queue (MAIL_QUEUE_REQUIRED). The initial admin name has no email: map an email in the auth extension before using recovery.
| Command | Purpose |
|---|---|
| dokebi mail preview <name> [--locale <locale>] | Render the template's preview.json in describe mode, without connecting, sending or running data hooks. --json returns subject, html, text and resolved locale in the standard envelope. Missing names are MAIL_TEMPLATE_NOT_FOUND. |
Mail templates live in src/shared/mail/; install writes a user-owned welcome sample. describe lists their names, locales and files. With mail installed, check adds two warnings: mail-send-in-request recommends queue in route handlers, and mail-secret-in-data recommends sealed for sensitive token/password/link-like fields. These name-based source heuristics do not replace a security review.
Deployment skeleton
dokebi init <directory> --deploy also writes Dockerfile, .dockerignore, compose.deploy.yaml and DEPLOY.md inside the new project transaction. --dry-run --json reports deployment.files without writing or installing. It does not deploy or start services. Read DEPLOY.md before building: keep runtime secrets out of the image, mount dokebi.yaml read-only, configure the container web host/port and public domains, and prepare databases, queues and migrations separately. A core-only app exits when idle. Use a new folder; init never overlays an existing project.
Multi-app site recipe
dokebi recipe site asks --apps (web/admin/api combinations, or none), --domains (app=host pairs, or auto), --realms (app=realm pairs, or auto), --view and --worker none|thread|job. Text questions have input: "text" and an empty options list; present their question and accept a text answer. The same answer-source and wait-for-user rules apply. auto selects separate <app>.<project>.localhost domains (unique to this project, so another project's cookies never collide) and realms; it is used only when explicitly answered or delegated through defaults. Duplicate domains, missing mappings and invalid realm names fail before mutation.
The site recipe creates a new project or fills a project without existing apps. Its .dokebi/site-plan.json checkpoint permits repeating the same choices after a partial failure; different choices require explicit app/config edits. Admin and API routes require authentication; public web routes are optional. Generated user lookups deny all logins until connected to your own user store. No account database or working login UI is claimed. Shared realms do not automatically share host-only cookies. Read SITE.md before running the site. A thread worker uses the generated CPU task module; a job worker needs a configured NATS endpoint and npx dokebi-job-provision --queue siteWorker. Both run in the same process; --apps none makes a worker-only project.
HTTP verification treats 401/403 from this run's instance on an app domain as reachable and reports accessDenied: true; it does not verify login. Readiness endpoints still require 2xx/3xx, and 400/404/5xx domain responses fail. Application tests must cover authentication and authorization.
