sweetrpg-cli

module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2026 License: MIT

README

sweetrpg-cli

CI Coverage License Issues PRs Dependabot

sweetrpg is a command-line client for the SweetRPG platform: one authenticated session usable against every service. sweetrpg catalog covers add/edit/view/delete/link for catalog entities (volume, publisher, studio, person, system, license, review, contribution); sweetrpg api is a generic authenticated request passthrough for any configured service.

Install

go install github.com/sweetrpg/sweetrpg-cli/cmd/sweetrpg@latest

Configuration

Each service's base URL resolves in this order:

  1. --api-url flag (catalog only today) - a full URL
  2. SWEETRPG_<SERVICE>_API_URL environment variable (e.g. SWEETRPG_CATALOG_API_URL, SWEETRPG_GAME_ROOM_API_URL) - also a full URL
  3. ~/.config/sweetrpg/cli.yaml's baseURL plus a services.<service> path

A service's path defaults to /api/0/<service> (/assets for assetsWeb) when services.<service> isn't set, so the config file only needs a services entry for a service whose path deviates from that convention:

baseURL: https://dev.sweetrpg.com

is enough on its own; the longer form overrides specific paths:

baseURL: https://dev.sweetrpg.com
services:
  catalog: /api/0/catalog
  gameRoom: /api/0/game-room
  assetsWeb: /assets

A service that lives on a different host entirely (a local port during dev, say) skips baseURL for that one service via its env var, which always takes a full URL and overrides the config file's path-under-baseURL resolution.

Authentication

Commands that write require a login. A release build ships with its Auth0 tenant baked in, but that's a default, not a hardcode - it resolves in this order:

  1. SWEETRPG_AUTH_DOMAIN / SWEETRPG_AUTH_CLIENT_ID / SWEETRPG_AUTH_AUDIENCE environment variables
  2. ~/.config/sweetrpg/cli.yaml's authTenant section
  3. The values baked in via -ldflags at release time

For dev runs against plain go run (nothing baked in), set one of the first two:

export SWEETRPG_AUTH_DOMAIN=dev-xxxx.us.auth0.com
export SWEETRPG_AUTH_CLIENT_ID=...
export SWEETRPG_AUTH_AUDIENCE=https://catalog-api

or in the config file:

authTenant:
  domain: dev-xxxx.us.auth0.com
  clientId: ...
  audience: https://catalog-api

Run once per machine:

sweetrpg auth login

This opens the Auth0 device-flow login (visit the printed URL and enter the code). The session is shared across every command namespace (catalog, api, game-room, ...) - one login covers all of them. Tokens are stored in your OS keychain under service name sweetrpg-cli; access tokens refresh automatically. auth logout removes them. Auth failures exit with code 3.

Reads don't require a login: catalog view (and name resolution it performs) hits public endpoints and works with no stored session. Writes (add, edit, delete, link, unlink) require one.

Catalog commands

Entity commands share one shape; <type> is one of the entity types above:

sweetrpg catalog add <type> <name> [property flags]
sweetrpg catalog edit <type> <name-or-id> [property flags]
sweetrpg catalog view <type> <name-or-id> [--json | --yaml]
sweetrpg catalog delete <type> <name-or-id>

Name arguments match case-insensitively and partially (exact matches win when both kinds hit); 24-hex IDs are used as-is. When a name matches several records an interactive picker lists each candidate's ID, or (with --yes) the command fails and prints the candidates.

catalog view volume prints a viewable coverURL alongside a volume's own fields when it has a cover and assets-web-url is configured; --json/--yaml stay the server's raw representation.

To see what a fuzzy query will hit before resolving, use search:

sweetrpg catalog search <type> <query>    # prints "ID<TAB>name" per hit

Links connect two entities in either argument order:

sweetrpg catalog link volume "Dungeon World" publisher "Evil Hat Productions"
sweetrpg catalog link person "John Wick" volume 507f1f77bcf86cd799439011 --role artist
sweetrpg catalog unlink volume "Dungeon World" person "John Wick"

Linkable pairs: volume-publisher, volume-studio, volume-system, volume-person. Person links to volumes create or update contribution credits (--role, default author). Relinking an existing pair is idempotent.

Volumes also support staged-asset upload for covers:

sweetrpg catalog edit volume "Dungeon World" --cover ./dw-cover.png

--cover accepts png, jpeg, or webp files and can be combined with property flags. Uploads require a session and an assets-web base URL (--assets-web-url flag, SWEETRPG_ASSETS_WEB_URL env var, or services.assetsWeb in the config file); they talk to assets-web directly, so a --curl run previews the linking PATCH but not the upload itself.

DriveThruRPG login

sweetrpg dtrpg login/sweetrpg dtrpg logout manage one DriveThruRPG application key, shared by every command that imports from your DriveThruRPG library (catalog import dtrpg library, game-room import dtrpg). It's one external account either way, so there's one login:

sweetrpg dtrpg login                 # paste a key from your DTRPG account settings
sweetrpg dtrpg login --credentials   # or enter email + password to mint one

The key is kept in the OS keychain under service sweetrpg-cli, account dtrpg-app-key - separate from the platform session. It is exchanged for a short-lived session on every run; the session token is never written to disk. Passwords are read at a masked prompt and discarded after the exchange. sweetrpg dtrpg logout deletes the stored key.

Importing a DriveThruRPG library into the catalog

catalog import dtrpg library bulk-loads the volumes in your DriveThruRPG library into the catalog. It drives the same POST /volumes and POST /publishers endpoints as catalog add, so imported records land as submitted versions for normal review.

Run the import (requires both a platform login and sweetrpg dtrpg login):

sweetrpg catalog import dtrpg library --dry-run     # show the plan, write nothing
sweetrpg catalog import dtrpg library               # create volumes and publishers

Each product maps to a volume: title, short description, and category filters as tags. The DriveThruRPG product ID and ISBN (when present) are stored as dtrpg_* properties - purchase date and order ID are not, since they're personal-order facts rather than catalog data. The product's cover image is downloaded and stored as the volume's own cover asset, not referenced by URL. Publisher names resolve case-insensitively to existing publisher records, creating one on a miss. Re-runs are idempotent - a product whose dtrpg_product_id already appears on a volume is skipped.

catalog import dtrpg library is meant to be run by an admin or editor: created volumes, publishers, and cover links land as live records, not review-queue submissions - a bulk import can create hundreds or thousands of records, and routing all of that through review would make the queue unusable. There's no separate "publish immediately" flag; it follows from the caller's role the same way POST /publishers and PATCH /volumes already do. A submitter-role token still works for the writes that support it, but expect it to behave differently than documented here.

Flags:

  • --dry-run - fetch the library and print the plan (to import / already imported / skipped) without any write.
  • --include-archived - also import products whose DriveThruRPG files are archived (skipped by default).
  • --page-size - DriveThruRPG retrieval page size; 0 uses the server default.

A per-product failure is isolated: the run continues, the failure is listed in the summary, and the command exits 1. Missing platform session exits 3; missing DriveThruRPG key exits 1 with a pointer to sweetrpg dtrpg login.

Populating your Game Room library from DriveThruRPG

game-room import dtrpg matches your DriveThruRPG library against volumes already in the SweetRPG catalog and adds every match to your own Game Room library. It never creates a catalog record - a product with no matching catalog volume is skipped and reported, not imported. Use catalog import dtrpg library (an admin/editor tool, see above) to populate the shared catalog itself first. Uses the same DriveThruRPG login as the catalog import - run sweetrpg dtrpg login once and both commands can use it.

Run the match-and-add:

sweetrpg game-room import dtrpg --dry-run     # show what would be added, write nothing
sweetrpg game-room import dtrpg               # add every matched volume to your library

Matching is by the dtrpg_product_id property the catalog import records on each volume. The completion summary reports counts of products added, already in your library, and skipped because no catalog volume matches yet (with their titles), so you understand why your full DriveThruRPG library may not fully populate your Game Room library. game-room import dtrpg logout deletes the stored key.

sweetrpg api: generic authenticated requests

For endpoints the typed catalog commands don't cover, api sends an authenticated request against any configured service, in the spirit of gh api:

sweetrpg api GET /volumes/123 --service catalog
sweetrpg api POST /publishers --service catalog --field name="Evil Hat Productions"
sweetrpg api GET /users/me --service users -H "X-Request-Id: abc123"

--field key=value type-sniffs the value (true/false/numeric encode as their JSON type, everything else as a string); --raw-field key=value always encodes a string. The method defaults to GET with no body, POST when --field/--raw-field is present. Combine with --curl to preview the request instead of sending it.

Scripting

  • Pass --yes to skip all interactive prompts; ambiguous name resolutions then fail instead of prompting. Deletes additionally require --force when stdin is not a TTY - --yes alone never deletes in a script.
  • Use catalog view <type> <id> --json for machine-readable output.
  • Pass --curl to print the equivalent cURL command(s) instead of calling the API. Nothing is sent; the bearer token is printed as <redacted>. Flows that need server data to continue (name resolution feeding later requests) stop after their first request, so pass IDs instead of names to see write requests directly.
  • Exit codes: 0 success, 1 general error, 2 usage error, 3 authentication failure.

Shell Completion

source <(sweetrpg completion bash)   # add to .bashrc
source <(sweetrpg completion zsh)    # add to .zshrc
sweetrpg completion fish | source
sweetrpg completion powershell

Documentation

See RELEASE.md for how versions get cut and CONTRIBUTING.md for the development workflow.

Directories

Path Synopsis
cmd
sweetrpg command
internal
auth
Package auth implements login against Auth0 using the OAuth device authorization grant, refresh-token storage in the OS keychain, and a per-session TokenSource that refreshes transparently.
Package auth implements login against Auth0 using the OAuth device authorization grant, refresh-token storage in the OS keychain, and a per-session TokenSource that refreshes transparently.
client
Package client is a thin JSON:API client for catalog-api, typed against catalog-objects.go value objects.
Package client is a thin JSON:API client for catalog-api, typed against catalog-objects.go value objects.
cmd
config
Package config resolves CLI configuration from flags, environment, and the config file.
Package config resolves CLI configuration from flags, environment, and the config file.
dtrpg
Package dtrpg wraps github.com/pilgrimagesoftware/dtrpg-sdk.go for the CLI's `import dtrpg` commands: application-key storage in the OS keychain, session exchange, paged library retrieval, and product-to-volume mapping.
Package dtrpg wraps github.com/pilgrimagesoftware/dtrpg-sdk.go for the CLI's `import dtrpg` commands: application-key storage in the OS keychain, session exchange, paged library retrieval, and product-to-volume mapping.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL