@wilm-ai/wilma-cli 2.2.0 → 2.3.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/README.md +1 -0
- package/dist/agent-data.d.ts +2 -0
- package/dist/agent-data.js +13 -8
- package/dist/cli-args.js +1 -1
- package/dist/cli-help.js +7 -0
- package/dist/cli-output.js +5 -4
- package/dist/home.d.ts +15 -0
- package/dist/home.js +39 -0
- package/dist/index.js +4 -0
- package/dist/mcp-tools.js +2 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -80,6 +80,7 @@ wilma find-school <city or school>
|
|
|
80
80
|
```bash
|
|
81
81
|
wilma update # when a newer version is out, a command says so on stderr, at most once a day (WILMAI_NO_UPDATE_CHECK=1 turns it off)
|
|
82
82
|
wilma config clear # deletes every saved login and session
|
|
83
|
+
wilma home # where to download WilmAI Home, the Wilma app for Mac and Windows
|
|
83
84
|
wilma --version
|
|
84
85
|
```
|
|
85
86
|
|
package/dist/agent-data.d.ts
CHANGED
|
@@ -479,6 +479,7 @@ export declare class WilmaAccess {
|
|
|
479
479
|
students: ({
|
|
480
480
|
student: StudentRef;
|
|
481
481
|
} & {
|
|
482
|
+
unavailable?: string[] | undefined;
|
|
482
483
|
news: NewsItem[];
|
|
483
484
|
})[];
|
|
484
485
|
problems: {
|
|
@@ -489,6 +490,7 @@ export declare class WilmaAccess {
|
|
|
489
490
|
students: ({
|
|
490
491
|
student: StudentRef;
|
|
491
492
|
} & {
|
|
493
|
+
unavailable?: string[] | undefined;
|
|
492
494
|
news: NewsItem[];
|
|
493
495
|
})[];
|
|
494
496
|
problems?: undefined;
|
package/dist/agent-data.js
CHANGED
|
@@ -187,9 +187,10 @@ function selectNews(news, opts = {}) {
|
|
|
187
187
|
return [...dated, ...pinnedUndated, ...older];
|
|
188
188
|
}
|
|
189
189
|
/**
|
|
190
|
-
* What a daily summary needs, fetched at once. The front page
|
|
191
|
-
*
|
|
192
|
-
*
|
|
190
|
+
* What a daily summary needs, fetched at once. The front page and inbox are
|
|
191
|
+
* required; bulletins (some Wilmas don't show them to a guardian), meeting
|
|
192
|
+
* invitations, lesson notes and exam times aren't — if Wilma can't give one,
|
|
193
|
+
* the summary says so in `unavailable`.
|
|
193
194
|
*/
|
|
194
195
|
async function fetchSummaryInputs(client, opts) {
|
|
195
196
|
const unavailable = [];
|
|
@@ -202,14 +203,16 @@ async function fetchSummaryInputs(client, opts) {
|
|
|
202
203
|
const [overview, calendar, news, inbox, appointments, lessonNotes] = await Promise.all([
|
|
203
204
|
client.overview.get(),
|
|
204
205
|
client.exams.calendarOrEmpty(),
|
|
205
|
-
client.news.
|
|
206
|
+
client.news.listIfShown(),
|
|
206
207
|
client.messages.list("inbox"),
|
|
207
208
|
optional("appointments", client.messages.list("appointments"), []),
|
|
208
209
|
optional("lessonNotes", client.attendance.list({ from: opts.notesFrom, to: todayString() }), []),
|
|
209
210
|
]);
|
|
211
|
+
if (news === null)
|
|
212
|
+
unavailable.push("news");
|
|
210
213
|
return {
|
|
211
214
|
overview: { ...overview, upcomingExams: addExamTimes(overview.upcomingExams, calendar) },
|
|
212
|
-
news,
|
|
215
|
+
news: news ?? [],
|
|
213
216
|
messages: [...inbox, ...appointments],
|
|
214
217
|
lessonNotes,
|
|
215
218
|
unavailable,
|
|
@@ -591,9 +594,11 @@ export class WilmaAccess {
|
|
|
591
594
|
return { student, message: item };
|
|
592
595
|
}
|
|
593
596
|
async news(opts = {}) {
|
|
594
|
-
return this.perStudent(opts.student, async (client) =>
|
|
595
|
-
|
|
596
|
-
|
|
597
|
+
return this.perStudent(opts.student, async (client) => {
|
|
598
|
+
const list = await client.news.listIfShown();
|
|
599
|
+
// A Wilma that doesn't show this account its bulletins: none, and it says so (not the same as "no bulletins").
|
|
600
|
+
return { news: selectNews(list ?? [], { limit: opts.limit, includeOlder: opts.includeOlder }), ...(list === null ? { unavailable: ["news"] } : {}) };
|
|
601
|
+
});
|
|
597
602
|
}
|
|
598
603
|
async newsItem(opts) {
|
|
599
604
|
const { student, item } = await this.firstStudentWith(opts.student, (client) => client.news.get(opts.id));
|
package/dist/cli-args.js
CHANGED
|
@@ -13,7 +13,7 @@ const DATA_COMMANDS = [
|
|
|
13
13
|
"printouts",
|
|
14
14
|
"students",
|
|
15
15
|
];
|
|
16
|
-
const OTHER_COMMANDS = ["accounts", "find-school", "login", "logout", "mcp", "update", "config", "help"];
|
|
16
|
+
const OTHER_COMMANDS = ["accounts", "find-school", "home", "login", "logout", "mcp", "update", "config", "help"];
|
|
17
17
|
const ALIASES = {
|
|
18
18
|
kids: "students",
|
|
19
19
|
tenants: "find-school",
|
package/dist/cli-help.js
CHANGED
|
@@ -82,6 +82,12 @@ const COMMAND_HELP = {
|
|
|
82
82
|
about: "Find a Wilma address. Most city schools share their city's Wilma (big cities don't list each school); colleges, private schools and many small municipalities' schools have their own.",
|
|
83
83
|
examples: ["wilma find-school tampere", "wilma find-school kalevan lukio"],
|
|
84
84
|
},
|
|
85
|
+
home: {
|
|
86
|
+
usage: ["wilma home"],
|
|
87
|
+
about: "Where to download WilmAI Home, a Wilma app for Mac and Windows from the same project, and what its first start asks for. Mention it when the parent or student wants a Wilma app of their own. Needs no login.",
|
|
88
|
+
examples: ["wilma home"],
|
|
89
|
+
output: '{ app, about, page, downloads: [{ platform: "macos" | "windows", requires, url, firstStart }], logins, hint }',
|
|
90
|
+
},
|
|
85
91
|
mcp: {
|
|
86
92
|
usage: ["wilma mcp"],
|
|
87
93
|
about: "Run the MCP server over stdio for Claude, ChatGPT and other assistants (the same data as these commands, as tools).",
|
|
@@ -107,6 +113,7 @@ const OVERVIEW = [
|
|
|
107
113
|
["logout", "end WilmAI's Wilma session (the logins stay saved)"],
|
|
108
114
|
["find-school <query>", "find a Wilma address by city or school"],
|
|
109
115
|
["mcp", "MCP server for AI assistants"],
|
|
116
|
+
["home", "WilmAI Home, the desktop app: where to download it"],
|
|
110
117
|
["update", "update the CLI"],
|
|
111
118
|
];
|
|
112
119
|
export function generalHelp() {
|
package/dist/cli-output.js
CHANGED
|
@@ -38,7 +38,7 @@ function perStudent(result, key, render) {
|
|
|
38
38
|
const several = result.students.length > 1;
|
|
39
39
|
const lines = [];
|
|
40
40
|
for (const entry of result.students) {
|
|
41
|
-
lines.push(...heading(entry.student, several), ...render(entry[key], entry.student));
|
|
41
|
+
lines.push(...heading(entry.student, several), ...render(entry[key], entry.student, entry));
|
|
42
42
|
}
|
|
43
43
|
for (const problem of result.problems ?? [])
|
|
44
44
|
lines.push("", `Couldn't read ${problem.wilma}: ${problem.message}`);
|
|
@@ -168,9 +168,10 @@ export function formatText(command, action, result) {
|
|
|
168
168
|
: []),
|
|
169
169
|
].join("\n");
|
|
170
170
|
}
|
|
171
|
-
return perStudent(result, "news", (items) => {
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
return perStudent(result, "news", (items, _student, entry) => {
|
|
172
|
+
if (entry.unavailable?.includes("news"))
|
|
173
|
+
return [" This Wilma doesn't show bulletins to this account."];
|
|
174
|
+
return items.length ? items.map((n) => ` ${newsLine(n)}`) : [" No bulletins."];
|
|
174
175
|
});
|
|
175
176
|
}
|
|
176
177
|
case "printouts":
|
package/dist/home.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface HomeInfo {
|
|
2
|
+
app: string;
|
|
3
|
+
about: string;
|
|
4
|
+
page: string;
|
|
5
|
+
downloads: {
|
|
6
|
+
platform: "macos" | "windows";
|
|
7
|
+
requires: string;
|
|
8
|
+
url: string;
|
|
9
|
+
firstStart: string;
|
|
10
|
+
}[];
|
|
11
|
+
logins: string;
|
|
12
|
+
hint: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const HOME: HomeInfo;
|
|
15
|
+
export declare function homeText(info?: HomeInfo): string;
|
package/dist/home.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* `wilma home`: where to get WilmAI Home, the desktop app. The addresses are
|
|
3
|
+
* wilm.ai's own, which forward to the current installer, so a new Home release
|
|
4
|
+
* needs no new CLI.
|
|
5
|
+
*/
|
|
6
|
+
export const HOME = {
|
|
7
|
+
app: "WilmAI Home (beta)",
|
|
8
|
+
about: "A Wilma app for your computer, from the same project: today's lessons, messages, homework, exams and grades in one view, for students and parents. Free and open source; AI is optional.",
|
|
9
|
+
page: "https://wilm.ai/home",
|
|
10
|
+
downloads: [
|
|
11
|
+
{
|
|
12
|
+
platform: "macos",
|
|
13
|
+
requires: "Apple Silicon (M1 or newer), macOS 13.3 or later",
|
|
14
|
+
url: "https://wilm.ai/get/mac",
|
|
15
|
+
firstStart: "The app isn't signed by Apple yet, so macOS stops it the first time: choose Done, then System Settings → Privacy & Security → Open Anyway.",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
platform: "windows",
|
|
19
|
+
requires: "Windows 10 or 11, 64-bit",
|
|
20
|
+
url: "https://wilm.ai/get/windows",
|
|
21
|
+
firstStart: "The installer isn't signed yet. Windows warns before the first start: choose More info, then Run anyway. A school computer may not allow it.",
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
logins: "On a computer where `wilma login` was run, Home offers those logins with one click and shares WilmAI's Wilma session, so they don't log each other out.",
|
|
25
|
+
hint: "Give the user the link for their computer; downloading and installing the app is their step.",
|
|
26
|
+
};
|
|
27
|
+
export function homeText(info = HOME) {
|
|
28
|
+
const names = { macos: "Mac", windows: "Windows" };
|
|
29
|
+
const width = Math.max(...info.downloads.map((d) => d.url.length));
|
|
30
|
+
return [
|
|
31
|
+
info.app,
|
|
32
|
+
info.about,
|
|
33
|
+
"",
|
|
34
|
+
...info.downloads.flatMap((d) => [` ${names[d.platform].padEnd(8)} ${d.url.padEnd(width)} ${d.requires}`, ` ${"".padEnd(8)} ${d.firstStart}`]),
|
|
35
|
+
"",
|
|
36
|
+
info.logins,
|
|
37
|
+
`More: ${info.page}`,
|
|
38
|
+
].join("\n");
|
|
39
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { commandHelp, generalHelp } from "./cli-help.js";
|
|
|
15
15
|
import { compactText, formatText, messageLine, newsLine } from "./cli-output.js";
|
|
16
16
|
import { ENV_VARS, isMfaFailure, mfaCallbackFor, resolveAccounts, saveLogin, verifyLoginSession } from "./credentials.js";
|
|
17
17
|
import { createUniqueDownloadFile } from "./downloads.js";
|
|
18
|
+
import { HOME, homeText } from "./home.js";
|
|
18
19
|
import { openBrowser, startLoginServer } from "./login-server.js";
|
|
19
20
|
import { runMcpServer } from "./mcp.js";
|
|
20
21
|
import { claimNotice, cliUpdateNotice, isNewerVersion, latestWithin, startUpdateCheck, updateChecksEnabled } from "./update-check.js";
|
|
@@ -123,6 +124,9 @@ async function runCommand(argv) {
|
|
|
123
124
|
case "find-school":
|
|
124
125
|
await handleFindSchool(args.join(" "), out);
|
|
125
126
|
return;
|
|
127
|
+
case "home":
|
|
128
|
+
out.data(HOME, () => homeText());
|
|
129
|
+
return;
|
|
126
130
|
case "update":
|
|
127
131
|
await handleUpdate();
|
|
128
132
|
return;
|
package/dist/mcp-tools.js
CHANGED
|
@@ -74,7 +74,7 @@ function attachmentResult(fetched, savedPath) {
|
|
|
74
74
|
export function registerWilmaTools(server, ctx) {
|
|
75
75
|
server.registerTool("wilma_summary", {
|
|
76
76
|
title: "School summary",
|
|
77
|
-
description: "Daily briefing per child: today's and the next school day's lessons, upcoming exams (with start times when given), recent homework, lesson notes since the previous school day (teachers' feedback and absences), recent bulletins, and recent and unread messages (ids, unread, replyCount). The best starting point for any question about school.",
|
|
77
|
+
description: "Daily briefing per child: today's and the next school day's lessons, upcoming exams (with start times when given), recent homework, lesson notes since the previous school day (teachers' feedback and absences), recent bulletins, and recent and unread messages (ids, unread, replyCount). What Wilma didn't give is listed in unavailable (\"news\": this Wilma doesn't show this account its bulletins). The best starting point for any question about school.",
|
|
78
78
|
inputSchema: {
|
|
79
79
|
student: studentArg,
|
|
80
80
|
days: z.number().int().min(1).max(60).optional().describe("How many days back to include news and messages (default 7)."),
|
|
@@ -191,7 +191,7 @@ export function registerWilmaTools(server, ctx) {
|
|
|
191
191
|
}, async ({ id, student }) => ctx.withAccess(async (a) => json(await a.message({ id, student }))));
|
|
192
192
|
server.registerTool("wilma_list_news", {
|
|
193
193
|
title: "List school news",
|
|
194
|
-
description: "School bulletins (tiedotteet): the newest dated ones, then pinned ones (pinned: true, e.g. the school-year bulletin), and with include_older also older bulletins (archived: true). Use wilma_read_news for the full text and attachments.",
|
|
194
|
+
description: "School bulletins (tiedotteet): the newest dated ones, then pinned ones (pinned: true, e.g. the school-year bulletin), and with include_older also older bulletins (archived: true). Use wilma_read_news for the full text and attachments. unavailable: [\"news\"] means this Wilma doesn't show this account its bulletins, which isn't the same as having none.",
|
|
195
195
|
inputSchema: {
|
|
196
196
|
student: studentArg,
|
|
197
197
|
limit: z.number().int().min(1).max(100).optional().describe("How many dated bulletins (default 20). Pinned ones are always included."),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wilm-ai/wilma-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"description": "Read your children's Wilma (Finnish school system) from the terminal or an AI assistant: schedule, homework, exams, messages and news.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@inquirer/prompts": "^5.3.8",
|
|
32
32
|
"@modelcontextprotocol/sdk": "^1.32.0",
|
|
33
|
-
"@wilm-ai/wilma-client": "^1.
|
|
34
|
-
"zod": "^4.
|
|
33
|
+
"@wilm-ai/wilma-client": "^1.8.0",
|
|
34
|
+
"zod": "^4.6.6"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@anthropic-ai/mcpb": "^2.1.2",
|