remarkable-cli
v0.3.1
Published
Command line over rmscene-ts and rmcommunication-ts for reMarkable tablets
Downloads
1,191
Maintainers
Readme
remarkable-cli
The rmcli command line over rmscene-ts and rmcommunication-ts for
reMarkable tablets in developer mode. It is a thin mapping of those libraries: every capability lives in
them, and rmcli only parses arguments, calls one library function, and prints the result.
Install
npm install -g remarkable-cli
rmcli --helpThe package is named remarkable-cli on npm because the shorter name is taken; the command it installs
is rmcli.
To work on it from a checkout instead:
npm install
npm run build
node dist/cli.js --helpNode 22 or newer, the floor rmindex-ts sets through better-sqlite3.
SKILL.md in this package is the agent-facing guide: what to run for a given request, and what not
to do. Point an AI assistant at it rather than at this README.
Configure
export RMCLI_HOST=<tablet-wifi-address>
export RMCLI_PASSWORD='...'
rmcli device fingerprint # prints the tablet's host key and trusts nothing
export RMCLI_FINGERPRINT=SHA256:...device fingerprint is the only command that runs before a host key is pinned. Check what it prints
against the tablet before exporting it; every other command then refuses to connect if the key
changes.
The SSH password is never a command argument. Set RMCLI_PASSWORD for a one-off run, or leave it unset
and set RMCLI_PASSWORD_COMMAND to a command whose stdout is the password, which is how a password
manager or a secret store supplies it:
export RMCLI_PASSWORD_COMMAND='pass show remarkable/root'The command runs through the platform shell, cmd.exe on Windows and /bin/sh elsewhere, and only
its stdout is read.
Either way the value reaches the connection through a provider function and is never printed, not even inside an error.
RMCLI_TIMEOUT_MS sets the Web Interface request timeout, default 180000.
Commands that leave the tablet running
These go through a pinned WiFi SSH tunnel to the tablet's internal Web Interface. Xochitl keeps running and the user is not interrupted.
rmcli device status [--json]
rmcli device identity [--json]
rmcli device capabilities [--json]
rmcli device enable-wifi-ssh
rmcli documents current [--json]
rmcli documents list [--json]
rmcli documents get <documentId> [--json]
rmcli documents download <documentId> --backup-dir <dir> [--json]
rmcli documents upload <file.pdf|file.epub|image> --name <name> --backup-dir <dir> [--folder <folderId>] [--json]
rmcli pages list <documentId|current> --backup-dir <dir> | --archive <file.rmdoc> [--raw] [--json]
rmcli pages render <documentId|current> <page|current|pageId> --output <file.svg|file.png>
--backup-dir <dir> | --archive <file.rmdoc>
[--template <name>] [--background white|transparent] [--width <px>] [--json]
rmcli templates read <name> --output <file.json> [--json]documents current reads LastOpen from the tablet's configuration and the open page from the
document's own content file. Both are plain reads and neither stops the tablet UI. It reports
documentId: null when the user is in the library list.
pageSource says where the page number came from: content is the live pointer the tablet rewrites on
every page flip, metadata is the page of the last save, and only-page means the document has a single
page and can be showing nothing else. A one-page notebook usually needs the second or third of those,
because the firmware leaves its live pointer empty. When no source answers, pageId, pageNumber and
pageIndex are null together and pageCount still reports the real count.
pages render downloads the document as a verified rmdoc backup and renders from that archive, which is
the only page source that does not stop the tablet UI. Pass --archive instead of --backup-dir to
reuse a download you already have. Pass --template to draw the ruled background; the template itself
is read over SFTP, which does not interrupt anything either.
Page numbers
A document positional accepts current, meaning whatever the tablet has open. A page positional
accepts current, a page ID, or the page number the tablet shows, counting from 1.
current is resolved by the command itself, on the connection it has already opened, so
pages render current current is the whole "render the page I am looking at" flow in one run. Calling
documents current first and passing its documentId on costs a second process and a second SSH
handshake for an answer the command already has.
pages list and pages render report the document name, and pages list reports the page a
current document token resolved to: pageId, pageNumber, pageIndex and pageSource, the fields
documents current prints. The four are null together when the document was named outright, and when
the tablet named no page. A caller that holds a snapshot of the open document therefore fills it from
pages list current alone.
pages list prints both PAGE and INDEX. INDEX is the raw position inside the document file and a
deleted page leaves a gap in it, so only PAGE is the number a person sees. pages render --json
reports svg and png sizes separately, because SVG user units and raster pixels are unrelated
numbers.
Commands that interrupt the tablet
These run inside a guarded offline session: the library stops Xochitl, works, and starts it again. Each
one refuses to run until --service is added.
rmcli service documents list --service [--json]
rmcli service page read <documentId> <pageId> --output <file.rm> --service
rmcli service page write <documentId> <pageId> --input <file.rm> --expected-revision <rev> --backup-dir <dir> --service
rmcli service snapshot <documentId> --backup-dir <dir> --service
rmcli service mirror --mirror-dir <dir> --serviceOutput
--json writes stable JSON to stdout; without it the same data is printed as a table. Errors go to
stderr and the exit code is 1. Binary output always goes to the file named by --output, never to
stdout, so an image never lands in the context of the tool that invoked the command.
There is no delete
The tablet's Web Interface exposes exactly three endpoints, GET /documents/{folder}, POST /upload
and GET /download/{id}/{format}. Deleting a document is a tablet-UI action.
One run at a time
Upload targets the container listed most recently, and that selection is global tablet state rather
than per-connection. rmcli therefore takes a lock file per host for the whole invocation, so two runs
on this machine cannot interleave a folder selection with someone else's upload. Runs from different
machines are not covered; a single owning service is the real answer to that.
The four packages
| Package | What it does |
| --- | --- |
| rmscene-ts (npm) | Reads, writes and renders .rm version 6 scene files. No filesystem, no network, browser-safe. |
| rmcommunication-ts (npm) | Talks to the tablet over pinned SSH: listings, verified rmdoc backups, page rendering, templates, PNG, PDF and EPUB import. |
| rmindex-ts (npm) | Turns a local mirror into a catalog: SQLite index, FTS5 full-text search and a page-image cache. Reads the mirror only. |
| remarkable-cli (npm) | The rmcli command line over these libraries. |
None of them implements the reMarkable Cloud protocol.
License and credits
MIT.
rmcli is a thin command line over rmcommunication-ts
and rmscene-ts. The .rm scene format itself was worked
out by rmscene, the Python library that rmscene-ts was
rewritten from.
