Two command-line tools for working with Blues devices and services:
notecardcommunicates with a Notecard attached to your computer.notehubmanages Notehub projects, devices, data, firmware, and project skills.
Install both tools with Homebrew:
brew install --cask blues/note-cli/note-cliOr download the appropriate binary archive from the releases page.
Important
When upgrading a Homebrew installation older than v1.9.1, first run
brew uninstall note-cli.
This README describes the code in this branch. To use changes that are not yet in a
release, build from source. Check your installed version with
notehub --version or notecard --version.
Start by reading the guide embedded in the installed CLI:
notehub --help-agentThe same guide is available in notehub/help-agent.md.
It covers authentication, API discovery, request bodies, responses, and modification
cursors. An agent with command execution can use notehub directly; an MCP connection
is not required.
notehub auth status --jsonRead signed_in in the returned object. Exit code 0 alone does not mean the user
is signed in. If signed_in is true, reuse that connection. Otherwise inspect
reason and action; a network failure calls for retrying the check, not starting
a new login.
If authorization is needed, start this command in a persistent execution session:
notehub login --name "My Agent" --jsonThe CLI does not open a browser or prompt for input. The user approves access in their own browser:
- Read stdout while the command is running. It emits one JSON object per line.
- As soon as an object contains
url, give the user that exact link and ask them to open it, sign in, and approve access for your named agent. They do not need to copy anything back. - Keep monitoring the same process. Progress messages contain
expires_in_seconds; allow that window, normally up to ten minutes. Do not wait for the process to exit before showing the link or start duplicate logins. - Continue with the task only after a final object contains
success: trueand the command exits successfully. On failure, explainstatus. Respect cancellation; if the link expires, start again only if the user wants to proceed.
An existing valid login can produce the final success object immediately. The CLI saves credentials and renews OAuth access tokens when the service provides a refresh token. Reuse the login across tasks.
For unattended use, the user can supply a personal access token through the agent
runtime's secret settings as NOTEHUB_TOKEN. It overrides saved credentials;
check auth status --json again, and no login command is needed. Have the user
replace or remove a rejected environment token before trying browser sign-in.
Keep credentials out of chat and command output; let the CLI add authentication.
If the service reports that polling sign-in is unavailable, follow its instructions
for interactive sign-in on the machine running the CLI, or use NOTEHUB_TOKEN.
notehub api --spec > notehub-openapi.yaml
notehub api GET /v1/projects--spec fetches the selected hub's public OpenAPI specification without requiring
login. On the default service, it is available at
api.notefile.net/openapi.yaml.
Read the relevant operation and its referenced schemas before constructing a request.
Use project and device identifiers returned by Notehub, and resolve ambiguous targets
with the user before making changes.
If the project has skills, read them for context about its devices and data:
notehub skills --project app:PROJECT_UIDExamples below use placeholder identifiers such as app:PROJECT_UID and
dev:DEVICE_UID; replace them with real identifiers. For additional guidance about
working with a user's Notehub project, notehub --agent-chat fetches the public
guide from chat.notehub.io.
notehub <mode> [options] [arguments]
notehub [options] [request]
| Mode | Purpose |
|---|---|
login, logout |
Sign in to or out of a hub |
auth |
Check and manage saved logins |
api |
Make authenticated HTTP requests to the Notehub API |
firmware |
Upload host firmware and manage device updates |
skills |
Read, edit, back up, and restore a project's skills |
| No mode | Send Notecard-format requests and manage notefiles, variables, and provisioning |
Run notehub for a command overview and saved settings, notehub --help for the
default mode's options, or notehub <mode> --help for a mode's commands and examples.
Put the mode first. Within a mode, options may appear before or after arguments;
in the default mode, put options before the request. A standalone -- ends option
parsing. Values accept either --project app:PROJECT_UID or
--project=app:PROJECT_UID; booleans accept --pretty or --pretty=false.
For interactive use:
notehub login --name "My laptop"
notehub auth status
notehub auth status --all
notehub logoutBrowser logins appear in Notehub's API Access screen as Notehub CLI - <name>.
Without --name, the CLI uses the computer's name. notehub auth login and
notehub auth logout are aliases for login and logout.
The CLI stores one login per hub. --hub selects a hub for one command, and logging
in to another hub does not change the active one:
notehub login --hub example.com
notehub auth switch example.comnotehub --hub example.com by itself also saves the active hub. auth status --all
checks every saved login; --all --json returns an object with active and a hubs
array. --all cannot be combined with --hub.
To save a personal access token supplied through a secret variable, pass it on stdin:
printf '%s\n' "$SECRET" | notehub login --with-tokenNOTEHUB_TOKEN takes precedence over saved logins and must be cleared before
changing them or signing out. OAuth logins renew automatically when possible;
personal access tokens are not refreshed. A revoked login requires new authorization.
Name the HTTP method explicitly so adding fields does not change the operation:
notehub api GET '/v1/projects/{project}/devices' --project app:PROJECT_UID
notehub api GET '/v1/projects/{project}/devices/{device}/environment_variables' \
--project app:PROJECT_UID --device dev:DEVICE_UID
notehub api PUT '/v1/projects/{project}/devices/{device}/environment_variables' \
--project app:PROJECT_UID --device dev:DEVICE_UID --input vars.jsonPass a path, not a full URL. Quote paths containing braces or query strings.
--project, --product, and --device fill their corresponding placeholders;
actual identifiers can also be written directly into the path. Without an explicit
method, the default is GET, or POST when fields or a body are supplied.
--field key=valueconverts numbers, booleans, andnull; other values remain strings.@fileand@-read a file or stdin as a string value.--raw-field key=valuealways supplies a literal string. Use it for cursors.- With GET or HEAD, fields become query parameters. With other methods, they form
a JSON body. Use
--input FILEor--input -for nested JSON, arrays, or a complete body; it cannot be combined with field options. --header 'Name: value'adds a header. JSON is the default body content type.
The response body goes to stdout and diagnostics to stderr. JSON responses need no
--json flag. HTTP errors retain their response body and return a nonzero exit code;
successful responses may be empty. Check the exit code before treating a request as
successful. After an uncertain write result, inspect the resource before retrying.
--include adds the HTTP status and headers. --pretty formats JSON but buffers the
response; omit it for streaming responses. --verbose writes request details and
response status to stderr.
Use --paginate on GET endpoints whose specification declares a cursor parameter,
including project events and devices:
notehub api GET '/v1/projects/{project}/events' --project app:PROJECT_UID \
--paginate --field pageSize=1000The CLI starts at cursor=0, follows the returned next_cursor or X-Next-Cursor
header, and stops when has_more or X-Has-More is false. It writes each response
separately, preserving the response bodies and continuation metadata rather than
combining them into one JSON document. Use --include to retain cursor headers for
endpoints whose bodies are arrays or other formats.
To start at a particular modification time, supply a UTC Unix epoch timestamp in microseconds. For subsequent reads, pass the exact returned cursor as a string:
notehub api GET '/v1/projects/{project}/events' --project app:PROJECT_UID \
--paginate --raw-field cursor="$CURSOR" --field pageSize=1000Results arrive in ascending modification order. A returned cursor may include more
than the timestamp; preserve it exactly so records sharing a timestamp are not skipped.
Save the final cursor after processing the results, then reuse it with the same
endpoint and filters to read later changes. An empty response can still carry a useful
cursor. Apply updates by resource identifier and handle deleted: true where supported.
For processing and checkpointing one batch at a time, omit --paginate and advance
the cursor yourself after each successful batch. Automatic pagination stops with an
error on missing continuation metadata or a repeated cursor while more results remain.
Do not combine it with a page number beyond 1 or ordering options that conflict with
the endpoint's modification order.
notehub firmware list --project app:PROJECT_UID --json
notehub firmware upload ./build/app.bin --version 1.2.3 --project app:PROJECT_UID
notehub firmware show 1.2.3 --project app:PROJECT_UID
notehub firmware get 1.2.3 ./app-1.2.3.bin --project app:PROJECT_UID
notehub firmware update 1.2.3 --scope "Field Units" --project app:PROJECT_UID --dry-run
notehub firmware update 1.2.3 --scope "Field Units" --project app:PROJECT_UID
notehub firmware status --scope "Field Units" --project app:PROJECT_UID --jsonIdentify firmware by its stored filename, original filename, or an unambiguous version.
Quote stored names containing $, such as 'app$20260324190911.bin'. Upload reads the
version from the binary when available, skips identical content, and replaces firmware
with the same name and version unless --no-replace is set. set is an alias for
upload. Use edit to change metadata, delete to remove firmware, cancel to cancel
an update, and history to inspect past updates.
--scope accepts device or fleet UIDs, fleet names, or @file containing targets.
--tag, --sn, and --sku narrow the selection. Updating or cancelling across
--scope all requires --force. --dry-run previews uploads, edits, deletes, updates,
and cancellations. Downloads verify MD5 and require --force to overwrite a different
local file. --json is available for listings, metadata, status, and history.
Skills are Markdown files that explain a project's product and data to an agent. To create them, point an agent at notehub.md and ask it to train your project.
notehub skills --project app:PROJECT_UID
notehub skills list --project app:PROJECT_UID
notehub skills show index.md --project app:PROJECT_UID
notehub skills get all ./skills --project app:PROJECT_UID
notehub skills set ./skills --project app:PROJECT_UID --dry-run
notehub skills set ./skills --project app:PROJECT_UID
notehub skills backup skills.zip --project app:PROJECT_UID
notehub skills restore skills.zip --project app:PROJECT_UID --dry-runWith no command, skills --project ... assembles the full set into one linked document.
show displays one skill; get <name> - writes its exact contents to stdout.
set uploads changed files without deleting other skills. rename changes a skill's
name, and delete removes it. Kinds are stored as tags from the kind:, kinds:, or
tags: field in Markdown front matter and preserved in backups.
restore makes the project match the ZIP archive, deleting skills absent from it.
Preview it with --dry-run, then omit that flag to apply the changes. --dry-run also
works with set, rename, delete, and backup. Overwriting different local files
with get or backup, replacing another skill with rename, and delete all require
--force.
The CLI uses the current Skills API and handles names with nested paths and punctuation.
Writes check the revision that was read; on a conflict, reread and review before
retrying. Rename and restore involve multiple operations and can finish only partially
if an operation fails. Restore verifies the resulting set before reporting success.
If a full-set read exceeds the service's size limit, use list and named reads.
The default mode accepts Notecard-format JSON requests and device-management options:
notehub --projects --pretty
notehub --project app:PROJECT_UID --device dev:DEVICE_UID \
'{"req":"note.get","file":"hub.db","note":"config"}'
notehub --project app:PROJECT_UID --device dev:DEVICE_UID --explore
notehub --project app:PROJECT_UID --scope "Field Units" --get-vars --pretty
notehub --project app:PROJECT_UID --scope dev:DEVICE_UID \
--set-vars '{"interval":"60","debug":"-"}'
notehub --product com.example:sensor --scope dev:DEVICE_UID --provision --sn SN-0042Give the request as the last argument or with --req; @file reads it from a file.
Responses are JSON, formatted with --pretty when requested. --scope accepts device
or fleet UIDs, fleet names, @fleet for a fleet's devices, @file listing targets, and
devices or fleets for all of them. Separate multiple targets with commas. Setting
an environment variable to - removes it.
notecard [options] ['{"req":"..."}']
Run notecard to see saved settings and available ports, or notecard --help for
all options and examples. Select your attached Notecard's actual port:
notecard --interface serial --port /dev/ttyUSB0
notecard '{"req":"card.version"}'
notecard --pretty --req '{"req":"card.version"}'
notecard --info
notecard --explore
notecard --traceSetting only the interface and port saves them for later commands. When combined
with a request or another action, they apply only to that invocation.
NOTE_INTERFACE, NOTE_PORT, and NOTE_PORT_CONFIG override saved settings.
Put options before the JSON request. Replies use the Notecard's JSON format; check
both the exit code and any err field. A standalone -- ends option parsing.
From the repository root with Go 1.25 or later, build both tools using the published dependencies pinned in the module files:
GOWORK=off go build -o ./dist/notecard ./notecard
GOWORK=off go build -o ./dist/notehub ./notehub
./dist/notehub --help-agentThe examples use POSIX shell syntax. Add dist to your PATH or invoke the binaries
by their paths. GOWORK=off ensures the build uses the pinned dependencies even if
you have a local development workspace.
To run the audit checks and tests, including the separate library module:
GOWORK=off go mod verify
GOWORK=off go build ./...
GOWORK=off go vet ./...
GOWORK=off go test ./...
(cd lib && GOWORK=off go mod verify && GOWORK=off go vet ./... && GOWORK=off go test ./...)Copyright (c) 2017 Blues Inc. Released under the MIT license. See LICENSE.