@townspot/cli
v0.1.4
Published
TownSpot operator and agent CLI.
Maintainers
Readme
TownSpot CLI
TownSpot operator and agent CLI package.
Install
For published npm installs:
npm install -g @townspot/cli
townspotRunning townspot with no command opens the human operator home screen (Town Square). On a real terminal it follows with a numbered next-move menu; Enter defaults to the first recommended action. Use townspot help or townspot --json help for the full command reference.
What It Can Do
The CLI is an authenticated operator tool for TownSpot event sourcing and admin work. It is safe to give to a trusted local editor when that editor is a TownSpot admin assigned to specific towns. It is also used by agent tools such as Claude/Codex when they need machine-readable JSON output.
Main workflows:
- Sign in through the browser and store a refreshable local terminal session.
- List towns and inspect the towns the signed-in admin can manage.
- Resolve venues by name, match them to a town polygon, and optionally create the venue.
- List, add, update, and inspect event sources for assigned towns.
- Create sourcing runs so a batch of agent writes can be audited together.
- Preflight duplicates by venue/source before writing events.
- Create events from structured fields or from a source URL extraction.
- Update event emoji, categories, times, recurrence, and RDATEs.
- Soft-delete events with an explicit
--confirm <uuid>guard. - Read action receipts for accessible runs/events.
It does not bypass server authorization. The CLI sends the browser-login bearer token to TownSpot APIs, and the server decides what the signed-in user can do.
Permissions
TownSpot has two admin roles for CLI purposes:
super_admin: global operator. Can perform platform setup and unrestricted admin work.admin: town-scoped operator. Can work only inside towns/zones assigned to that admin account.
Authentication levels:
| Level | Who | What works |
| --- | --- | --- |
| No login | Anyone | townspot help, public towns list, public venues search, local sources parse-date |
| Signed-in non-admin | TownSpot user without admin record | Auth commands only; admin commands fail with 403 |
| Town admin | Active admin assigned to one or more towns | Source, venue, sourcing-run, receipt, and event operations inside assigned towns |
| Super admin | Active super_admin | All town-admin operations plus country, region, town creation/visibility, global setup, and unrestricted diagnostics |
Town admin boundary:
| Area | Town admin can do | Town admin cannot do |
| --- | --- | --- |
| Towns | List assigned admin towns with towns admin-list | Create towns, hide/show towns, activate/deactivate towns |
| Venues | Search venues; resolve and create venues inside assigned towns | Create or edit venues outside assigned towns; change venue multi-town membership |
| Sources | List/add/update sources in assigned towns; view source-linked events | Run unrestricted global URL checks or hard-delete source provenance |
| Events | Create/update/delete events in assigned towns; update recurrence; run duplicate preflight for accessible venues | Move events to venues outside assigned towns; edit events in unassigned towns |
| Runs/receipts | Create/get/finish sourcing runs for assigned towns; query receipts by accessible run or event | Query global receipts by arbitrary source URL/status/tool unless super admin |
| Platform setup | None | Countries, regions, launch-country, town creation, admin management |
Super admin-only commands:
townspot countries upsert ...
townspot regions upsert ...
townspot launch country ...
townspot towns create ...
townspot towns set-active ...
townspot towns set-hidden ...
townspot sources check-url --url <url>Town admin-safe commands, when scoped to an assigned town/event/venue:
townspot towns admin-list
townspot venues resolve --query <name> --zone-id <id> --create --confirm-production
townspot sources list --zone-id <id>
townspot sources add --url <url> --zone-id <id>
townspot sources update --id <source_id> ...
townspot sources events --id <source_id>
townspot events runs create --label <label> --zone-id <id>
townspot events open-url --url <url> --zone-id <id> --venue-id <venue_id> --run-id <run_id>
townspot events create --title <title> --zone-id <id> --venue-id <venue_id> ...
townspot events set-emoji --event-uuid <uuid> --emoji <emoji>
townspot events set-categories --event-uuid <uuid> --category <category>
townspot events set-times --event-uuid <uuid> --start <local> --end <local> --timezone <iana>
townspot events set-rrule --event-uuid <uuid> --rrule <RRULE> --timezone <iana>
townspot events set-rdates --event-uuid <uuid> --rdate <local> --timezone <iana>
townspot events delete --uuid <uuid> --confirm <same uuid>Read-only or local commands:
townspot help
townspot auth login
townspot auth import --payload <encoded-session>
townspot auth status
townspot auth token --header
townspot towns list --country-code ES
townspot venues search --query "Venue name" --country-code ES
townspot sources parse-date --text "next Friday 7pm" --default-year 2026Agent Usage
Use --json whenever Claude, Codex, or another agent is calling the CLI. JSON mode returns parseable success/error payloads and stable action names.
townspot --json auth status
townspot --json towns admin-list --country-code ES
townspot --json events runs create --label "Poblenou weekly source pass" --zone-id 123The normal sourcing loop for a town admin is:
townspot auth login- If the browser cannot reach the agent-owned localhost callback, run the
townspot auth import --payload ...command shown in the browser. townspot towns admin-list --country-code EStownspot events runs create --label "Poblenou weekly source pass" --zone-id <zone_id>townspot sources list --zone-id <zone_id>townspot venues resolve --query "<venue>" --zone-id <zone_id> --create --confirm-productiontownspot events preflight --venue-id <venue_id> --source-url <url>townspot events open-url --url <url> --zone-id <zone_id> --venue-id <venue_id> --run-id <run_id>townspot events receipts --run-id <run_id>townspot events runs finish --run-id <run_id> --status completed --report-json '{"created":3,"skipped":2}'
For production writes, keep --confirm-production on venue creation. For duplicate exceptions, use --allow-possible-duplicate only after checking receipts/preflight output.
Entrypoints
The web repo compatibility entrypoint remains:
npm run townspot -- <command>The package workspace entrypoint is:
npm --workspace @townspot/cli exec townspot -- <command>The package-local binary is still useful while developing:
node packages/townspot-cli/bin/townspot.mjs <command>For local shell testing:
cd packages/townspot-cli
npm link
townspot --json helpBoundary
This package should stay a narrow client over authenticated TownSpot server workflows.
- Do not write directly to the database.
- Do not silently fall back to static admin-token routes.
- Keep event creation, recurrence, venue resolution, provenance, duplicate checks, and quotas server-backed.
- Keep machine/agent CLI behavior separate from human operator UX.
- Use
--jsonfor agent mode; every command should return parseable success/error payloads. - Do not expose destructive source hard-delete from the CLI. Source provenance must be preserved server-side.
- Country and region setup commands are service-backed wrappers. Use
launch countryfor the first country plus first region, then add hyperlocal towns withtowns createor the town editor. - Town setup commands are server-backed wrappers.
towns createdeliberately sends onlyname,countryCode, and optional polygon data; slug generation, coordinates, activation defaults, permissions, and audit logging belong to the server.
Japan baseline:
townspot launch country \
--country-code jp \
--name Japan \
--currency-code JPY \
--default-locale ja-JP \
--timezone Asia/Tokyo \
--flag 🇯🇵 \
--region-name TokyoPackage Checks
Run these from the web repo root before touching release/distribution:
npm run townspot-cli:test
npm run townspot-cli:smoke
npm run townspot-cli:packThe package-local equivalents are:
cd packages/townspot-cli
npm run test:unit
npm run smoke
npm run pack:checkPackage-local npm test is intentionally a dependency-free smoke test so it still works from an installed or packed CLI. Use npm run test:unit in the monorepo for the Jest command suite.
townspot-cli:release-check is the real npm release gate. It runs the unit suite, smoke test, pack dry-run, and npm publish dry-run without publishing:
npm run townspot-cli:release-checkThe release gate also enforces the package metadata needed for first-class npm publication:
publishConfig.accessmust staypublicpublishConfig.provenancemust staytrue
Runtime Config
The CLI reads runtime config in this order:
- process env
- repo-root
.env.local - repo-root
.env
Supported environment variables:
NEXT_PUBLIC_SITE_URLNEXT_PUBLIC_API_BASE_URLNEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYNO_COLORCI
The auth session is stored at:
~/.config/townspot/agent-session.jsonThat file contains the browser-login handoff session and is refreshed in place with the stored refresh token. The CLI writes it with mode 0600.
Release
First-time local validation:
npm run townspot-cli:release-checkRelease path:
- Bump
packages/townspot-cli/package.json. - Push
main. - Create and push a tag that exactly matches the package version. The recommended path is to tag
origin/maindirectly so you release the current remote main tip, for exampletownspot-cli-v0.1.0. - Let
.github/workflows/publish-townspot-cli.ymlpublish with npm provenance.
Example:
git fetch origin main
git tag townspot-cli-v0.1.0 origin/main
git push origin townspot-cli-v0.1.0The workflow requires NPM_TOKEN in GitHub Actions secrets. It runs the same workspace publish dry-run as CI, rejects tag/version mismatches, requires the tagged commit to already be contained in origin/main, and then publishes with --access public --provenance. That ancestor check avoids false failures if main advances after the release tag is pushed.
Exit Codes
0: success1: unknown error2: validation failure (validation_failed,unknown_command)3: auth failure (auth_failed)4: duplicate blocked (duplicate_blocked)5: quota/rate-limit blocked (quota_blocked)6: server error (server_error)7: extraction uncertain (extraction_uncertain)
