Install
openclaw skills install helmetManage your Helmet library accounts (Helsinki capital region) from an AI agent — renew loans, place and cancel holds, view fines, and search the catalog across one card or a whole family at once.
openclaw skills install helmetAccess the Helmet library system (Helsinki Metropolitan Area libraries) from an AI agent. Query loans, renew books, check holds and fines — for one account or many family accounts at once, via the helmet CLI with --json output.
# Install
npm install -g @helmet-ai/helmet
# First-time login — run once per library card (saves credentials locally)
helmet login
# Full account overview (single account)
helmet summary --json
# Family overview (all saved profiles)
helmet summary --all-profiles --json
helmet status --json
helmet status --all-profiles --json
Verifies each saved profile's session is live. Exit code 0 means authenticated; 2 means the user needs to run helmet login. Run this once at the start of a multi-step task to avoid partial failures halfway through.
The CLI stores one or more library accounts as profiles. Use these when acting for a user or a family:
--profile or --all-profiles.| Flag | Purpose |
|---|---|
--profile <selector> | Target one profile. Selector is any of: display name (Alice), unique display-name prefix (al), card number, or full id (helmet|<card>). |
--all-profiles | Fan out across every saved profile. Works on summary and loans list. Mutually exclusive with --profile. |
--all-profiles is not supported on loans renew, holds place, holds cancel (all destructive — always target one profile), search (unauthenticated), or login.
--all-profiles --json wraps output in a per-profile array. Each row is independent: one profile failing does not block others.
[
{"profile": {"id": "helmet|...", "displayName": "Alice"}, "ok": true, "data": { /* same shape as single-profile --json */ }},
{"profile": {"id": "helmet|...", "displayName": "Bob"}, "ok": false, "error": "AuthenticationError: ...", "errorCode": "AUTH_REQUIRED"}
]
Exit code is 0 if at least one profile succeeded, 1 if all failed. When reporting partial failures to the user, key on errorCode (stable) rather than the free-form error string (which embeds the raw exception message).
helmet profiles list --jsonEnumerate saved profiles. Returns id, displayName, cardNumber, and lastUsedAt for each.
helmet profiles rename <selector> <new-name> / helmet profiles remove <selector>Local-only management of saved profiles (no Helmet API calls).
Each profile's authenticated cookie jar is persisted at ~/.config/helmet/sessions/<id>.json (mode 0600). The first command per profile walks the full Finna login handshake (~2.7s); subsequent commands skip straight to the data request (~1.3s). Stale cookies are handled transparently — if Finna redirects to the login page, the session auto-re-authenticates using the stored PIN. You do not need to manage the cache explicitly; it is cleared automatically on helmet login, helmet profiles remove, or an unrecoverable AuthenticationError.
All commands accept --json for machine-readable output. Always use --json when calling from an agent.
helmet summary --jsonReturns a complete account snapshot: loans (with overdue/due-soon flags), holds (with pickup-ready status), and fines. Start here — one call gives you everything needed for triage. Add --profile <selector> for a specific person or --all-profiles for a whole family.
helmet loans list --jsonList checked-out items with title, author, due date, due status (ok, due, overdue), and whether renewable. Supports --profile and --all-profiles.
helmet loans renew <id> --jsonRenew a specific loan by its ID. Returns success/failure with new due date or error code. Requires --profile <selector> when multiple profiles exist (the CLI will not auto-pick a profile for destructive operations).
helmet loans renew --all --jsonRenew every renewable item on one profile. Same profile-targeting rule as above — pass --profile <selector> explicitly.
helmet holds list --jsonList current holds. helmet holds (no subcommand) is an alias. Each row has shape:
{
"id": "12345678",
"title": "Sample Book Title",
"author": "Last, First",
"pickupLocation": "Sample Branch",
"queuePosition": null,
"pickupDeadline": "15.1.2026",
"createdDate": "1.1.2026",
"shelfLocation": "A 000",
"status": "available_for_pickup",
"cancelable": true,
"fetchedAt": "2026-01-01T12:00:00.000Z"
}
Field meanings:
status: "pending" (in queue) / "in_transit" (on its way to the pickup branch) / "available_for_pickup" (arrived — the user must go collect it).pickupDeadline (Finnish "nouto viimeistään"): the date the hold will be cancelled and passed to the next patron if uncollected. Only populated when status === "available_for_pickup" — null otherwise. Date is in Finnish format D.M.YYYY. If the user misses this date, Helmet charges a no-pickup fee and may suspend borrowing for a period — agents should surface this date prominently in reminders.shelfLocation (Finnish "Varaushylly"): the exact shelf code inside the branch where the arrived hold is waiting (e.g. A 000). Only populated for arrived holds. Include it in pickup reminders so the user can walk straight to the shelf.createdDate (Finnish "Luotu"): when the hold was placed. Informational, not actionable.queuePosition: for pending holds, the user's position in the reservation queue (null once the hold is in transit or arrived).helmet holds place <record-id> [--comment <text>] --jsonPlace a title-level hold on a Helmet catalog record. The record-id comes from helmet search (e.g. helmet.2613471). The CLI uses the default pickup location preselected by Helmet in the hold form. The --comment field is used only for bookmobile pickup stops. Returns { success, message }; on success, message is typically "Varauspyyntö onnistui.". Requires --profile <selector> when multiple profiles exist — the CLI will not auto-pick a profile for a destructive/state-changing operation.
helmet holds cancel <hold-id> --jsonCancel an active hold. The hold-id is the id field from helmet holds list --json. Returns { success, message }; on success, message is typically "1 varaus(ta) poistettu.". Same profile-targeting rule as place — pass --profile <selector> explicitly.
helmet fines --jsonList individual fines and total amount owed.
helmet search <query> --jsonSearch the Helmet catalog. Unauthenticated — --profile has no effect and --all-profiles is rejected.
helmet status [--json] [--all-profiles]Lightweight preflight: reports whether the selected profile (or all profiles) has a live authenticated session. Exit code 0 if authenticated, 2 if helmet login is needed. With --json:
{
"version": "0.3.0",
"profile": {"id": "helmet|...", "displayName": "Alice"},
"authenticated": true,
"checkedAt": "2026-04-14T12:34:56Z"
}
On --all-profiles, emits {version, checkedAt, profiles: [...]} where each row carries authenticated: bool and, on failure, errorCode: "AUTH_REQUIRED". Run this before multi-step tasks.
helmet version / helmet --version / helmet -VPrint the CLI version (e.g. 0.3.0). No auth, no network. Useful when an agent needs to record which helmet build produced a report.
When the stored session expires, the PIN has changed, or Finna invalidates the session for any reason, helmet exits with code 2 and (with --json) emits:
{
"ok": false,
"errorCode": "AUTH_REQUIRED",
"errorName": "AuthenticationError",
"message": "Helmet session expired and re-authentication failed — run `helmet login` to refresh credentials.",
"recovery": "Run `helmet login` to refresh your library credentials."
}
On --all-profiles, one row may fail with errorCode: "AUTH_REQUIRED" while others succeed. Report the affected person by displayName and continue with the rest.
errorCode: "AUTH_REQUIRED" field in the JSON output.displayName (not card number).helmet login yourself. It is interactive and prompts for a PIN that only the user knows. Ask the user to run helmet login locally.AUTH_REQUIRED, stop and ask the user — do not keep retrying. The most likely cause is a changed PIN that the stored credential no longer matches, which only a fresh helmet login can fix.When reporting to the user, prioritize items by actionability:
| Priority | Condition | Action |
|---|---|---|
| URGENT | Overdue items (dueStatus: "overdue") | Renew immediately or alert user |
| URGENT | Fines > 0 EUR | Alert user — fines block borrowing |
| URGENT | Ready-for-pickup hold whose pickupDeadline is today, tomorrow, or in the past | Alert user to collect today — missing the deadline cancels the hold and charges a no-pickup fee |
| HIGH | Loans due within 3 days (dueStatus: "due" or in loansDueSoon) | Renew if possible, otherwise warn |
| HIGH | Ready-for-pickup hold (status: "available_for_pickup") with pickupDeadline within 7 days | Remind user to pick up. Always cite the exact deadline date and the shelfLocation so they can go straight to the shelf at pickupLocation. |
| MEDIUM | Loans due within 7 days | Mention in summary |
| LOW | Holds with queue position ≤ 2 | Mention — pickup may come soon |
When surfacing a ready-for-pickup hold, prefer a line like:
"Pick up Sample Book Title at Sample Branch (shelf A 000) by 15.1.2026 — otherwise the hold is cancelled and a no-pickup fee applies."
Always include: title, pickupLocation, shelfLocation (when present), and pickupDeadline. Dates are already in Finnish D.M.YYYY format — keep them as-is; do not reformat to ISO unless the user is clearly a developer consuming JSON.
helmet summary --json (or with --profile <name>).helmet summary --all-profiles --json — one call covers everyone.ok: true. For each ok: false row, mention the person's name and that their data could not be fetched (don't abort the whole report).helmet loans renew --all --profile <name> per person that has renewable overdue loans. Never fan out renew.displayName in user-facing output; keep id for internal routing.The wrapper script at scripts/helmet-cli.sh can be used as a fallback when the helmet binary is not on PATH:
bash skills/helmet/scripts/helmet-cli.sh summary --all-profiles --json
Empty holds / loans / fines arrays with no error — this was a bug in helmet ≤ 0.1.1 where an expired session was parsed as "no data" instead of raising an auth error. Upgrade to 0.2.0+:
npm install -g @helmet-ai/helmet@latest
helmet --version # must be ≥ 0.2.0
If you still see empty results after upgrading, run helmet status --json — if it reports authenticated: false, the user needs to run helmet login. Real empty results (no holds, no loans) after 0.2.0 are legitimate; only pre-0.2.0 masked auth failures as empty.