@wilm-ai/wilma-cli 1.6.2 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Wilm.ai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -3,101 +3,90 @@
3
3
  Command line interface for Wilma (Finnish school system), built for parents and AI agents.
4
4
 
5
5
  ## Install
6
+ Needs Node.js 20.18.1 or newer.
6
7
  ```bash
7
8
  npm i -g @wilm-ai/wilma-cli
8
9
  # or
9
10
  pnpm add -g @wilm-ai/wilma-cli
10
11
  ```
11
12
 
12
- ## Run
13
+ ## Log in
13
14
  ```bash
14
- wilma
15
- # or
16
- wilmai
15
+ wilma login
17
16
  ```
17
+ Opens a one-time login page in your browser: pick your school's Wilma and log in. The login is saved to `~/.config/wilmai/config.json`.
18
18
 
19
- ## Commands
20
-
21
- ### Daily briefing
22
- ```bash
23
- wilma summary [--days 7] [--student <id|name>] [--all-students] [--json]
24
- ```
25
- Combines today's and tomorrow's schedule, upcoming exams, recent homework, news, and messages into one view. Designed for AI agents to surface what matters.
19
+ Without a browser: `wilma login --tenant <url|city> --username <name>` with the password in `WILMA_PASSWORD` or piped via `--password-stdin`. Or set `WILMA_TENANT`, `WILMA_USERNAME`, `WILMA_PASSWORD` (and `WILMA_TOTP_SECRET`) and skip the saved login.
26
20
 
27
- ### Schedule
21
+ ## Use it
28
22
  ```bash
29
- wilma schedule list [--when today|tomorrow|week] [--date YYYY-MM-DD] [--weekday mon|tue|wed|thu|fri|sat|sun] [--student <id|name>] [--all-students] [--json]
23
+ wilma summary # the daily briefing for every child — start here
24
+ wilma schedule tomorrow # today | tomorrow | week | next-week | 2026-10-07 | thu
25
+ wilma exams # upcoming exams, with topics and start times
26
+ wilma homework
27
+ wilma notes --days 7 # lesson notes: absences and teachers' feedback
28
+ wilma notes summary # those counted by kind for the school year
29
+ wilma messages # newest first; [new] and reply counts
30
+ wilma messages 27164611 # one message with its whole thread
31
+ wilma news # dated bulletins plus pinned ones; --older for the rest
32
+ wilma news 73291 # one bulletin with its links
33
+ wilma news 73291 download 1 # a file linked from it
34
+ wilma grades # recent exam grades
35
+ wilma gradebook # course and report-card grades
36
+ wilma printouts # PDFs such as report cards; `wilma printouts <id>` saves one
37
+ wilma students
38
+ wilma help <command> # usage, examples and the JSON shape
30
39
  ```
31
40
 
32
- Examples:
33
- ```bash
34
- # Specific day by date
35
- wilma schedule list --date 2026-02-25 --student "Stella" --json
36
-
37
- # Next Thursday (also accepts Finnish short forms like to/ke/pe)
38
- wilma schedule list --weekday thu --student "Stella" --json
41
+ - **Every child by default.** `--student <number|name>` narrows to one (a number, the full name, or the start of a first or last name — never a loose match).
42
+ - **JSON for programs, text for people.** Output is JSON when another program reads it (agents, pipes) and text in a terminal; `--json` or `--text` forces either. The JSON is the same as the MCP tools' — `{ students: [{ student, … }] }` — compact, with times in Finnish time and their offset (`2026-10-02T13:37:00+03:00`).
43
+ - **Dates** are `YYYY-MM-DD`, `today`, `yesterday` or `tomorrow`.
44
+ - **One login, not one per command.** Commands continue the last Wilma session (saved next to the login, readable only by you) and log in again only when Wilma has ended it. Each login would otherwise log you out of Wilma in your browser and, with two-step verification, need a new code. `WILMAI_NO_SESSION_CACHE=1` turns this off.
45
+ - **For daily runs:** `wilma summary --since yesterday` returns only what is new: bulletins, messages, homework and lesson notes from that day on, plus anything unread.
46
+ - **Errors** are JSON too: `{ "status": "error", "code": "unknown_student", "message": "…" }`, with exit code 2 for usage errors, 3 when not logged in, and 1 otherwise. Codes include `invalid_argument`, `unknown_command`, `not_logged_in`, `unknown_student`, `ambiguous_student`, `not_found`, `login_failed`, `mfa_required`, `network` and `wilma_error`.
39
47
 
40
- # Tomorrow
41
- wilma schedule list --when tomorrow --student "Stella" --json
42
- ```
48
+ The 1.x spellings still work: `kids list`, `tenants`, `attendance list`, `<command> list`, `messages read <id>`, `news read <id>`, `news resource download <id> <resource>`, `printouts download <id>` and `--all-students`.
43
49
 
44
- ### Homework
45
- ```bash
46
- wilma homework list [--limit 10] [--student <id|name>] [--all-students] [--json]
47
- ```
50
+ Running `wilma` without arguments in a terminal opens an interactive menu.
48
51
 
49
- ### Upcoming exams
52
+ ## Use with AI assistants (MCP)
50
53
  ```bash
51
- wilma exams list [--limit 20] [--student <id|name>] [--all-students] [--json]
54
+ wilma mcp
52
55
  ```
56
+ Starts a stdio MCP server with read-only Wilma tools (`wilma_summary`, `wilma_schedule`, `wilma_read_news`, …) that return the same data as the commands. Add it to any MCP client as `npx -y @wilm-ai/wilma-cli@2 mcp`. Setup guides for Claude, ChatGPT, Grok Bot and OpenClaw: https://wilm.ai
53
57
 
54
- ### Exam grades
55
- ```bash
56
- wilma grades list [--limit 20] [--student <id|name>] [--all-students] [--json]
57
- ```
58
+ ## Bulletin attachments
59
+ `wilma news <id>` lists every link in a bulletin (`resources`). Any resource can be downloaded with `wilma news <id> download <resource> [--output <directory>]` (the resource id also accepts a bare number, e.g. `1` for `resource-1`). Wilma-hosted files download through the session; external links are fetched without your Wilma login, and only from public websites. The result's `status` says what happened: `downloaded` (use the returned `path`) or `not_a_file` when the link answered with a web page (for example a sharing link that needs signing in — open it in a browser instead). `--output` defaults to the current directory; existing files are never overwritten.
58
60
 
59
- ### News and messages
61
+ ## Several Wilmas
62
+ Children at schools on different Wilmas need one login per Wilma. Run `wilma login` again (or press "Add another Wilma" on the login page); commands then cover every child, and each child carries the `wilma` it belongs to.
60
63
  ```bash
61
- wilma news list [--limit 20] [--student <id|name>] [--all-students] [--json]
62
- wilma news read <id> [--student <id|name>] [--json]
63
- wilma news resource download <news-id> <resource-id> [--student <id|name>] [--output <directory>] [--json]
64
- wilma messages list [--folder inbox] [--limit 20] [--student <id|name>] [--all-students] [--json]
65
- wilma messages read <id> [--student <id|name>] [--json]
64
+ wilma accounts # saved logins, numbered
65
+ wilma accounts remove <number> # or the Wilma name or username
66
66
  ```
67
67
 
68
- `news read --json` includes a `resources` array with every link in the bulletin. Any resource can be passed to the download command (the resource id also accepts a bare number, e.g. `1` for `resource-1`). Wilma-hosted files download through the authenticated session; external URLs are fetched with an isolated, unauthenticated request — no Wilma credentials are ever sent to external hosts. The result `status` reports what actually happened: `downloaded` (use the returned `path`), or `not_a_file` when the URL answered with a web page instead of a file (for example a sharing link that requires signing in — open it in a browser instead). `--output` defaults to the current directory; existing files are never overwritten.
69
-
70
- ### Attendance / lesson notes
68
+ ## Find your Wilma
71
69
  ```bash
72
- wilma attendance list [--date YYYY-MM-DD] [--student <id|name>] [--all-students] [--json]
70
+ wilma find-school <city or school>
73
71
  ```
74
72
 
75
- Shows lesson notes (type labels, subjects, teachers) for a given date. Defaults to today if --date is omitted.
76
-
77
- ### Other
73
+ ## Other
78
74
  ```bash
79
- wilma kids list [--json]
80
75
  wilma update
81
- wilma config clear
76
+ wilma config clear # deletes every saved login and session
77
+ wilma --version
82
78
  ```
83
79
 
84
80
  ## MFA (Multi-Factor Authentication)
85
81
 
86
82
  If your Wilma account has MFA/TOTP enabled:
87
83
 
88
- **Interactive (recommended):** Run `wilma` and choose "Save TOTP secret for automatic login" when prompted. Paste your base32 key or `otpauth://` URI from your authenticator app. Future logins auto-authenticate.
89
-
90
- **Non-interactive:** Pass the secret directly:
91
- ```bash
92
- wilma schedule list --totp-secret <base32-key> --json
93
- wilma schedule list --totp-secret 'otpauth://totp/...' --json
94
- ```
84
+ **Recommended:** run `wilma login`. When Wilma asks for a code, the login page asks for your authenticator's setup key (base32 or `otpauth://` URI) and saves it with the login, so later logins make their own codes. In the interactive menu (`wilma`), choose "Save TOTP secret for automatic login".
95
85
 
96
- If you've saved your TOTP secret via interactive setup, `--totp-secret` is not needed.
86
+ **Without a saved login:** set `WILMA_TOTP_SECRET` along with the other `WILMA_*` variables. `--totp-secret <key>` also works, but a key on the command line ends up in shell history and is visible to other programs on the computer.
97
87
 
98
88
  ## Config
99
- Local config is stored in `~/.config/wilmai/config.json` (or `$XDG_CONFIG_HOME/wilmai/config.json`).
100
- Use `wilma config clear` to remove it. Override with `WILMAI_CONFIG_PATH`.
89
+ Logins are saved in `~/.config/wilmai/config.json` (or `$XDG_CONFIG_HOME/wilmai/config.json`; override with `WILMAI_CONFIG_PATH`) and the current Wilma sessions in `wilmai-sessions.json` next to it, both readable only by you. `wilma config clear` deletes both.
101
90
 
102
91
  ## Troubleshooting: TLS errors on a managed laptop
103
92
 
@@ -108,26 +97,27 @@ A successful `npm install` is not evidence that TLS is healthy — the npm regis
108
97
  commonly exempt from inspection.
109
98
 
110
99
  ```bash
111
- NODE_USE_SYSTEM_CA=1 wilma summary --json # Node >=22.19 / >=24.6
112
- node --use-system-ca "$(command -v wilma)" summary --json # Node >=22.15
113
- NODE_EXTRA_CA_CERTS=/path/to/root-ca.pem wilma summary --json # explicit root
100
+ NODE_USE_SYSTEM_CA=1 wilma summary # Node >=22.19 / >=24.6
101
+ node --use-system-ca "$(command -v wilma)" summary # Node >=22.15
102
+ NODE_EXTRA_CA_CERTS=/path/to/root-ca.pem wilma summary # explicit root
114
103
  ```
115
104
 
116
105
  Never use `NODE_TLS_REJECT_UNAUTHORIZED=0` — it turns verification off entirely.
117
106
 
118
- With `--json`, transport failures carry a `code` and a `hint` alongside `status` and
119
- `message`, so agents can branch on the cause:
107
+ In JSON, transport failures have the code `network`, with the underlying `cause` and a
108
+ `hint`, so agents can branch on it:
120
109
 
121
110
  ```json
122
111
  {
123
112
  "status": "error",
113
+ "code": "network",
124
114
  "message": "TLS certificate verification failed for https://example.inschool.fi (UNABLE_TO_GET_ISSUER_CERT_LOCALLY)",
125
- "code": "UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
115
+ "cause": "UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
126
116
  "hint": "..."
127
117
  }
128
118
  ```
129
119
 
130
120
  ## Notes
131
- - Credentials and TOTP secrets are stored with lightweight obfuscation for convenience.
132
- - For multi-child accounts, you can pass `--student <id|name>` or `--all-students`.
133
- - All list commands support `--json` for agent-friendly structured output.
121
+ - Credentials and TOTP secrets are stored with lightweight obfuscation (not encryption) in a file only your user can read.
122
+ - Bulletin links are fetched only from public websites (never local or private network addresses), without your Wilma login.
123
+ - Commands cover every child; `--student <number|name>` narrows to one.
@@ -0,0 +1,12 @@
1
+ import { WilmaAccess } from "./agent-data.js";
2
+ import { type ActiveAccount } from "./credentials.js";
3
+ /**
4
+ * Wilma access for the saved (or environment) logins — the same for CLI
5
+ * commands and the local MCP server. Refreshed child lists are written back
6
+ * to the config, changing only that field, so parallel commands and logins
7
+ * aren't overwritten.
8
+ */
9
+ export declare function createAccess(accounts: ActiveAccount[], opts?: {
10
+ debug?: boolean;
11
+ totpSecret?: string;
12
+ }): WilmaAccess;
package/dist/access.js ADDED
@@ -0,0 +1,33 @@
1
+ import { WilmaAccess } from "./agent-data.js";
2
+ import { loadConfig, saveConfig } from "./config.js";
3
+ import { mfaCallbackFor } from "./credentials.js";
4
+ /**
5
+ * Wilma access for the saved (or environment) logins — the same for CLI
6
+ * commands and the local MCP server. Refreshed child lists are written back
7
+ * to the config, changing only that field, so parallel commands and logins
8
+ * aren't overwritten.
9
+ */
10
+ export function createAccess(accounts, opts = {}) {
11
+ return new WilmaAccess(accounts.map((account, index) => ({
12
+ profile: { ...account.profile, debug: opts.debug ?? account.profile.debug },
13
+ // A --totp-secret given on the command line belongs to the first (active) login.
14
+ mfa: mfaCallbackFor(index === 0 && opts.totpSecret ? opts.totpSecret : account.totpSecret),
15
+ label: account.stored?.tenantName ?? account.profile.baseUrl,
16
+ knownStudents: account.stored?.students,
17
+ onStudents: async (students) => {
18
+ const stored = account.stored;
19
+ // An empty answer from Wilma keeps the saved list (see knownStudents).
20
+ if (!stored || !students.length)
21
+ return;
22
+ const fresh = students.map((s) => ({ studentNumber: s.studentNumber, name: s.name }));
23
+ if (JSON.stringify(fresh) === JSON.stringify(stored.students ?? []))
24
+ return;
25
+ const latest = await loadConfig();
26
+ const target = latest.profiles.find((p) => p.id === stored.id);
27
+ if (!target)
28
+ return;
29
+ target.students = fresh;
30
+ await saveConfig(latest);
31
+ },
32
+ })));
33
+ }