@wilm-ai/wilma-cli 2.3.2 → 2.5.0
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 +20 -3
- package/dist/access.d.ts +40 -1
- package/dist/access.js +105 -6
- package/dist/agent-data.d.ts +200 -21
- package/dist/agent-data.js +261 -56
- package/dist/cli-args.js +12 -3
- package/dist/cli-help.js +29 -13
- package/dist/cli-output.d.ts +4 -1
- package/dist/cli-output.js +19 -5
- package/dist/config.d.ts +36 -6
- package/dist/config.js +26 -0
- package/dist/credentials.d.ts +49 -2
- package/dist/credentials.js +199 -64
- package/dist/index.js +214 -108
- package/dist/login-server.d.ts +28 -6
- package/dist/login-server.js +468 -239
- package/dist/mcp-tools.d.ts +2 -2
- package/dist/mcp-tools.js +46 -15
- package/dist/mcp.js +26 -15
- package/dist/secret-moves.d.ts +44 -0
- package/dist/secret-moves.js +297 -0
- package/dist/secret-store.d.ts +45 -0
- package/dist/secret-store.js +487 -0
- package/dist/session-keys.d.ts +19 -0
- package/dist/session-keys.js +26 -0
- package/dist/session-store.d.ts +16 -0
- package/dist/session-store.js +69 -0
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ pnpm add -g @wilm-ai/wilma-cli
|
|
|
14
14
|
```bash
|
|
15
15
|
wilma login
|
|
16
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
|
|
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` (the password can go to the keychain instead: see Config).
|
|
18
18
|
|
|
19
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.
|
|
20
20
|
|
|
@@ -65,7 +65,7 @@ Starts a stdio MCP server with read-only Wilma tools (`wilma_summary`, `wilma_sc
|
|
|
65
65
|
`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.
|
|
66
66
|
|
|
67
67
|
## Several Wilmas
|
|
68
|
-
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.
|
|
68
|
+
Children at schools on different Wilmas need one login per Wilma. Run `wilma login` again (or press "Add another school's Wilma" on the login page); commands then cover every child, and each child carries the `wilma` it belongs to.
|
|
69
69
|
```bash
|
|
70
70
|
wilma accounts # saved logins, numbered
|
|
71
71
|
wilma accounts remove <number> # or the Wilma name or username; also ends that login's Wilma session
|
|
@@ -79,6 +79,7 @@ wilma find-school <city or school>
|
|
|
79
79
|
## Other
|
|
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
|
+
wilma config secrets # where saved passwords are kept; `wilma config secrets auto` moves them to the keychain
|
|
82
83
|
wilma config clear # deletes every saved login and session
|
|
83
84
|
wilma home # where to download WilmAI Home, the Wilma app for Mac and Windows
|
|
84
85
|
wilma --version
|
|
@@ -95,6 +96,22 @@ If your Wilma account has MFA/TOTP enabled:
|
|
|
95
96
|
## Config
|
|
96
97
|
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 logout` ends the sessions and deletes them, keeping the logins; `wilma config clear` deletes both.
|
|
97
98
|
|
|
99
|
+
### Passwords in the keychain (optional)
|
|
100
|
+
By default the password and authenticator key are in that file too, scrambled (not encrypted), like aws, npm or kubectl keep theirs. To keep them in the operating system's keychain instead:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
wilma config secrets auto # the keychain where there is one, the file where there isn't
|
|
104
|
+
wilma config secrets keychain # the keychain only; saving fails where there is none
|
|
105
|
+
wilma config secrets file # back to the file
|
|
106
|
+
wilma config secrets # where each login's password is now
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The file stays the default, and stays available: on a server or anything unattended (cron, CI, an agent such as OpenClaw) there's no logged-in user to unlock a keychain, so keep the file there (`auto` does that by itself). Changing the setting moves the saved logins without logging in again. The keychain is the macOS Keychain (through `/usr/bin/security`, so no prompts after a Node.js upgrade), Windows Credential Manager, or a Linux desktop's Secret Service (headless Linux has none: `auto` keeps the file there). `WILMAI_SECRET_STORE=file|auto|keychain` decides where new logins go for one process, for tools that run the CLI; it never moves saved logins.
|
|
110
|
+
|
|
111
|
+
What stays in files: the rest of the login (Wilma, username, children) and the current Wilma sessions, which give the same access as the password while they last (Wilma ends them after a few hours). The keychain keeps the password out of backups, synced dotfiles and anything that reads files; it doesn't protect against a program running as you, which can ask the keychain too. On Windows, saved credentials roam with a roaming profile. Older copies of the config (backups, synced dotfiles) still hold the password; changing the Wilma password makes them useless. A keychain entry left behind by a config deleted by hand is named `ai.wilm.wilma-cli` in Keychain Access or Credential Manager.
|
|
112
|
+
|
|
113
|
+
Errors: `secret_missing` (the entry is gone: `wilma login`), `secret_refused` (the keychain is locked or said no), `secret_unavailable` (no keychain this copy can use), all with exit code 4. The Linux binaries of the optional `@napi-rs/keyring` dependency statically link libdbus (`AFL-2.1 OR GPL-2.0-or-later`).
|
|
114
|
+
|
|
98
115
|
## Troubleshooting: TLS errors on a managed laptop
|
|
99
116
|
|
|
100
117
|
A TLS certificate error means the network is intercepting TLS and re-signing with a
|
|
@@ -125,6 +142,6 @@ In JSON, transport failures have the code `network`, with the underlying `cause`
|
|
|
125
142
|
```
|
|
126
143
|
|
|
127
144
|
## Notes
|
|
128
|
-
- Credentials and TOTP secrets are stored with lightweight obfuscation (not encryption) in a file only your user can read.
|
|
145
|
+
- Credentials and TOTP secrets are stored with lightweight obfuscation (not encryption) in a file only your user can read, or in the OS keychain with `wilma config secrets auto` (see Config).
|
|
129
146
|
- Bulletin links are fetched only from public websites (never local or private network addresses), without your Wilma login.
|
|
130
147
|
- Commands cover every child; `--student <number|name>` narrows to one.
|
package/dist/access.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { StudentInfo, WilmaClient } from "@wilm-ai/wilma-client";
|
|
1
2
|
import { WilmaAccess } from "./agent-data.js";
|
|
2
|
-
import { type
|
|
3
|
+
import { type StoredProfile } from "./config.js";
|
|
4
|
+
import { type ActiveAccount, type StoreChoice } from "./credentials.js";
|
|
3
5
|
/**
|
|
4
6
|
* Wilma access for the saved (or environment) logins — the same for CLI
|
|
5
7
|
* commands and the local MCP server. Refreshed child lists are written back
|
|
@@ -24,3 +26,40 @@ export declare function logoutAccounts(accounts: ActiveAccount[]): Promise<{
|
|
|
24
26
|
}[];
|
|
25
27
|
hint: string;
|
|
26
28
|
}>;
|
|
29
|
+
export type RemovedSession = ({
|
|
30
|
+
session: "ended" | "none";
|
|
31
|
+
} | {
|
|
32
|
+
session: "not_ended";
|
|
33
|
+
message: string;
|
|
34
|
+
}) & {
|
|
35
|
+
/** Its keychain entry couldn't be deleted (it stays recorded, so `wilma config clear` can try again). */
|
|
36
|
+
keychainEntryLeft?: string;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Remove a saved login (`wilma accounts remove`, the login page's Remove) and
|
|
40
|
+
* end its Wilma session; the other logins keep theirs. The login is removed
|
|
41
|
+
* even when Wilma can't be reached: its saved session is forgotten anyway,
|
|
42
|
+
* and Wilma ends the session when it expires.
|
|
43
|
+
*/
|
|
44
|
+
export declare function removeLogin(id: string): Promise<{
|
|
45
|
+
removed: StoredProfile;
|
|
46
|
+
} & RemovedSession>;
|
|
47
|
+
/** Which of a login's children aren't read (unticked on the login page); only children the login lists count. */
|
|
48
|
+
export declare function setHiddenChildren(id: string, hidden: string[]): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* After Wilma accepted a login: save it where `choice` (chooseSecretStore,
|
|
51
|
+
* decided before contacting Wilma) says, and only then keep the verified
|
|
52
|
+
* session, under the saved login's keys, so the next command or tool call
|
|
53
|
+
* needs no second login (or code). A login that couldn't be saved leaves no
|
|
54
|
+
* session behind.
|
|
55
|
+
*/
|
|
56
|
+
export declare function saveVerifiedLogin(input: {
|
|
57
|
+
tenantUrl: string;
|
|
58
|
+
tenantName?: string | null;
|
|
59
|
+
username: string;
|
|
60
|
+
password: string;
|
|
61
|
+
totpSecret?: string | null;
|
|
62
|
+
students: StudentInfo[];
|
|
63
|
+
}, client: WilmaClient, choice: StoreChoice): Promise<StoredProfile & {
|
|
64
|
+
note?: string;
|
|
65
|
+
}>;
|
package/dist/access.js
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
|
-
import { WilmaAccess, logoutSession } from "./agent-data.js";
|
|
2
|
-
import { loadConfig, saveConfig } from "./config.js";
|
|
3
|
-
import { mfaCallbackFor } from "./credentials.js";
|
|
1
|
+
import { WilmaAccess, WilmaAiError, adoptSession, forgetSession, logoutSession } from "./agent-data.js";
|
|
2
|
+
import { editConfig, loadConfig, saveConfig, savedStudent } from "./config.js";
|
|
3
|
+
import { accountFor, mfaCallbackFor, saveLogin } from "./credentials.js";
|
|
4
|
+
import { deleteLoginSecrets } from "./secret-store.js";
|
|
5
|
+
/**
|
|
6
|
+
* Codes from the saved authenticator key. A keychain login's key comes from
|
|
7
|
+
* the same single read as its password (no second keychain access), and one
|
|
8
|
+
* code generator serves the whole process, so a rejected code waits for the
|
|
9
|
+
* next one instead of repeating it.
|
|
10
|
+
*/
|
|
11
|
+
function mfaFor(account) {
|
|
12
|
+
if (!account.secrets)
|
|
13
|
+
return mfaCallbackFor(account.totpSecret);
|
|
14
|
+
if (!account.hasTotp)
|
|
15
|
+
return undefined;
|
|
16
|
+
const read = account.secrets;
|
|
17
|
+
let generator = null;
|
|
18
|
+
return async (formkey) => {
|
|
19
|
+
const pending = (generator ??= read().then((secrets) => mfaCallbackFor(secrets.totpSecret)));
|
|
20
|
+
pending.catch(() => {
|
|
21
|
+
if (generator === pending)
|
|
22
|
+
generator = null;
|
|
23
|
+
});
|
|
24
|
+
const callback = await pending;
|
|
25
|
+
if (!callback)
|
|
26
|
+
throw new WilmaAiError("mfa_required", "Wilma asks for a two-step verification code, and no authenticator key is saved for this login. Run `wilma login`.");
|
|
27
|
+
return callback(formkey);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
4
30
|
/**
|
|
5
31
|
* Wilma access for the saved (or environment) logins — the same for CLI
|
|
6
32
|
* commands and the local MCP server. Refreshed child lists are written back
|
|
@@ -10,16 +36,19 @@ import { mfaCallbackFor } from "./credentials.js";
|
|
|
10
36
|
export function createAccess(accounts, opts = {}) {
|
|
11
37
|
return new WilmaAccess(accounts.map((account, index) => ({
|
|
12
38
|
profile: { ...account.profile, debug: opts.debug ?? account.profile.debug },
|
|
39
|
+
password: account.secrets ? async () => (await account.secrets()).password : undefined,
|
|
40
|
+
keys: account.keys,
|
|
13
41
|
// A --totp-secret given on the command line belongs to the first (active) login.
|
|
14
|
-
mfa:
|
|
42
|
+
mfa: index === 0 && opts.totpSecret ? mfaCallbackFor(opts.totpSecret) : mfaFor(account),
|
|
15
43
|
label: account.stored?.tenantName ?? account.profile.baseUrl,
|
|
16
44
|
knownStudents: account.stored?.students,
|
|
45
|
+
hidden: account.stored?.hidden,
|
|
17
46
|
onStudents: async (students) => {
|
|
18
47
|
const stored = account.stored;
|
|
19
48
|
// An empty answer from Wilma keeps the saved list (see knownStudents).
|
|
20
49
|
if (!stored || !students.length)
|
|
21
50
|
return;
|
|
22
|
-
const fresh = students.map(
|
|
51
|
+
const fresh = students.map(savedStudent);
|
|
23
52
|
if (JSON.stringify(fresh) === JSON.stringify(stored.students ?? []))
|
|
24
53
|
return;
|
|
25
54
|
const latest = await loadConfig();
|
|
@@ -37,7 +66,7 @@ export function createAccess(accounts, opts = {}) {
|
|
|
37
66
|
* (different Wilmas don't affect each other), then throws the first failure.
|
|
38
67
|
*/
|
|
39
68
|
export async function logoutAccounts(accounts) {
|
|
40
|
-
const results = await Promise.allSettled(accounts.map((account) => logoutSession(account.profile)));
|
|
69
|
+
const results = await Promise.allSettled(accounts.map((account) => logoutSession(account.profile, account.keys)));
|
|
41
70
|
const failed = results.find((result) => result.status === "rejected");
|
|
42
71
|
if (failed)
|
|
43
72
|
throw failed.reason;
|
|
@@ -54,3 +83,73 @@ export async function logoutAccounts(accounts) {
|
|
|
54
83
|
: "No saved Wilma login, so there was nothing to log out.",
|
|
55
84
|
};
|
|
56
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Remove a saved login (`wilma accounts remove`, the login page's Remove) and
|
|
88
|
+
* end its Wilma session; the other logins keep theirs. The login is removed
|
|
89
|
+
* even when Wilma can't be reached: its saved session is forgotten anyway,
|
|
90
|
+
* and Wilma ends the session when it expires.
|
|
91
|
+
*/
|
|
92
|
+
export async function removeLogin(id) {
|
|
93
|
+
const config = await loadConfig();
|
|
94
|
+
const removed = config.profiles.find((p) => p.id === id);
|
|
95
|
+
if (!removed)
|
|
96
|
+
throw new WilmaAiError("unknown_account", "That login isn't saved any more.");
|
|
97
|
+
config.profiles = config.profiles.filter((p) => p.id !== id);
|
|
98
|
+
if (config.lastProfileId === id)
|
|
99
|
+
config.lastProfileId = config.profiles[0]?.id ?? null;
|
|
100
|
+
const entry = removed.secretId ?? config.keychainLogins?.[id];
|
|
101
|
+
await saveConfig(config);
|
|
102
|
+
// The session first (it needs no password), then the keychain entry; its record goes only once it's deleted.
|
|
103
|
+
const session = await endSession(removed);
|
|
104
|
+
if (!entry)
|
|
105
|
+
return { removed, ...session };
|
|
106
|
+
try {
|
|
107
|
+
await deleteLoginSecrets(entry);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
return { removed, ...session, keychainEntryLeft: err instanceof Error ? err.message : String(err) };
|
|
111
|
+
}
|
|
112
|
+
await editConfig((fresh) => {
|
|
113
|
+
if (fresh.keychainLogins?.[id] !== entry)
|
|
114
|
+
return false;
|
|
115
|
+
const { [id]: _gone, ...rest } = fresh.keychainLogins;
|
|
116
|
+
fresh.keychainLogins = rest;
|
|
117
|
+
});
|
|
118
|
+
return { removed, ...session };
|
|
119
|
+
}
|
|
120
|
+
async function endSession(removed) {
|
|
121
|
+
const { profile, keys } = accountFor(removed);
|
|
122
|
+
try {
|
|
123
|
+
return { session: (await logoutSession(profile, keys)) ? "ended" : "none" };
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
await forgetSession(profile, keys);
|
|
127
|
+
return { session: "not_ended", message: err instanceof Error ? err.message : String(err) };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Which of a login's children aren't read (unticked on the login page); only children the login lists count. */
|
|
131
|
+
export async function setHiddenChildren(id, hidden) {
|
|
132
|
+
const config = await loadConfig();
|
|
133
|
+
const target = config.profiles.find((p) => p.id === id);
|
|
134
|
+
if (!target)
|
|
135
|
+
throw new WilmaAiError("unknown_account", "That login isn't saved any more.");
|
|
136
|
+
const known = new Set((target.students ?? []).map((s) => s.studentNumber));
|
|
137
|
+
const kept = [...new Set(hidden)].filter((number) => known.has(number));
|
|
138
|
+
if (kept.length)
|
|
139
|
+
target.hidden = kept;
|
|
140
|
+
else
|
|
141
|
+
delete target.hidden;
|
|
142
|
+
await saveConfig(config);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* After Wilma accepted a login: save it where `choice` (chooseSecretStore,
|
|
146
|
+
* decided before contacting Wilma) says, and only then keep the verified
|
|
147
|
+
* session, under the saved login's keys, so the next command or tool call
|
|
148
|
+
* needs no second login (or code). A login that couldn't be saved leaves no
|
|
149
|
+
* session behind.
|
|
150
|
+
*/
|
|
151
|
+
export async function saveVerifiedLogin(input, client, choice) {
|
|
152
|
+
const stored = await saveLogin(await loadConfig(), input, choice);
|
|
153
|
+
adoptSession(accountFor(stored).keys, client);
|
|
154
|
+
return stored;
|
|
155
|
+
}
|
package/dist/agent-data.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { WilmaClient, type LessonNote, type Message, type MessageFolder, type MfaCallback, type NewsItem, type NewsResource, type OverviewData, type StudentInfo, type WilmaProfile } from "@wilm-ai/wilma-client";
|
|
1
|
+
import { WilmaClient, type LessonNote, type Message, type MessageFolder, type MfaCallback, type NewsItem, type NewsResource, type OverviewData, type ScheduleLesson, type StudentInfo, type WilmaProfile } from "@wilm-ai/wilma-client";
|
|
2
2
|
import type { SessionStore } from "./session-store.js";
|
|
3
|
+
import { type SessionKeys } from "./session-keys.js";
|
|
3
4
|
/**
|
|
4
5
|
* An error with a stable code for agents and scripts (the CLI's JSON errors
|
|
5
6
|
* and exit codes use it): e.g. "unknown_student", "invalid_argument".
|
|
@@ -19,11 +20,22 @@ export type ScheduleDateSelection = {
|
|
|
19
20
|
outputWhen: string;
|
|
20
21
|
};
|
|
21
22
|
export declare const SCHEDULE_WHEN: readonly ["today", "tomorrow", "week", "next-week"];
|
|
23
|
+
/** The longest period one schedule request covers. */
|
|
24
|
+
export declare const SCHEDULE_MAX_DAYS = 31;
|
|
25
|
+
/**
|
|
26
|
+
* An event invitation as listed: the message, plus the family's answer as
|
|
27
|
+
* Wilma's list shows it ("Applied" with the chosen time, "ExpiredReg"…;
|
|
28
|
+
* null when it shows none).
|
|
29
|
+
*/
|
|
30
|
+
export type ListedMessage = Message & {
|
|
31
|
+
invitationStatus?: string | null;
|
|
32
|
+
invitationInfo?: string | null;
|
|
33
|
+
};
|
|
22
34
|
/** Everything a daily summary is built from (see fetchSummaryInputs). */
|
|
23
35
|
export interface SummaryInputs {
|
|
24
36
|
overview: OverviewData;
|
|
25
37
|
news: NewsItem[];
|
|
26
|
-
messages:
|
|
38
|
+
messages: ListedMessage[];
|
|
27
39
|
lessonNotes: LessonNote[];
|
|
28
40
|
/** Parts Wilma couldn't give this time (the rest of the summary is still right). */
|
|
29
41
|
unavailable: string[];
|
|
@@ -35,7 +47,12 @@ export type StudentRef = {
|
|
|
35
47
|
};
|
|
36
48
|
/** One Wilma login. A family can have several (children on different Wilmas). */
|
|
37
49
|
export interface AccessAccount {
|
|
50
|
+
/** For a keychain login the password is empty here and `password` gives it. */
|
|
38
51
|
profile: WilmaProfile;
|
|
52
|
+
/** The password when Wilma needs a new login (a keychain read), never while a saved session works. */
|
|
53
|
+
password?: () => Promise<string>;
|
|
54
|
+
/** Where its session is saved and pooled; by default today's key (address, username and password). */
|
|
55
|
+
keys?: SessionKeys;
|
|
39
56
|
mfa?: MfaCallback;
|
|
40
57
|
/** The Wilma's name, shown to tell children apart when there are several logins. */
|
|
41
58
|
label?: string | null;
|
|
@@ -44,7 +61,10 @@ export interface AccessAccount {
|
|
|
44
61
|
knownStudents?: {
|
|
45
62
|
studentNumber: string;
|
|
46
63
|
name: string;
|
|
64
|
+
role?: StudentInfo["role"];
|
|
47
65
|
}[];
|
|
66
|
+
/** Student numbers unticked on the login page (a profile Wilma lists twice): not read. */
|
|
67
|
+
hidden?: string[];
|
|
48
68
|
}
|
|
49
69
|
type AccountStudent = StudentInfo & {
|
|
50
70
|
account: number;
|
|
@@ -70,26 +90,29 @@ export declare function useSessionStore(store: SessionStore | undefined): void;
|
|
|
70
90
|
/**
|
|
71
91
|
* Keep a session that is already logged in — e.g. the one the login page just
|
|
72
92
|
* verified — so the first tool call needs no second login (and, with two-step
|
|
73
|
-
* verification, no second code).
|
|
93
|
+
* verification, no second code). `keys` are the saved login's (v2 for a
|
|
94
|
+
* keychain login); without them, today's keys from the profile.
|
|
74
95
|
*/
|
|
75
|
-
export declare function adoptSession(
|
|
96
|
+
export declare function adoptSession(profileOrKeys: WilmaProfile | SessionKeys, client: WilmaClient): void;
|
|
76
97
|
/**
|
|
77
98
|
* Finish saving sessions (call before a short-lived process exits). Only
|
|
78
99
|
* logins made here are saved, never an older session over a newer one
|
|
79
100
|
* another process saved meanwhile.
|
|
80
101
|
*/
|
|
81
102
|
export declare function flushSessions(): Promise<void>;
|
|
103
|
+
/** Drop this process's client for a login (after its secrets moved, so the next use starts from its new keys). */
|
|
104
|
+
export declare function dropPooledSession(keys: SessionKeys): void;
|
|
82
105
|
/**
|
|
83
106
|
* End WilmAI's Wilma session for one login (Wilma's own "Log out") and forget
|
|
84
107
|
* it, in this process and in the session store, so nothing continues it. The
|
|
85
108
|
* login itself stays saved; the next command or tool call logs in again.
|
|
86
|
-
* Never logs in. Returns true when a live session
|
|
87
|
-
* was none. If Wilma can't be reached, throws and
|
|
88
|
-
* logging out can be tried again.
|
|
109
|
+
* Never logs in (so it needs no password). Returns true when a live session
|
|
110
|
+
* was ended, false when there was none. If Wilma can't be reached, throws and
|
|
111
|
+
* keeps the saved session, so logging out can be tried again.
|
|
89
112
|
*/
|
|
90
|
-
export declare function logoutSession(profile: WilmaProfile): Promise<boolean>;
|
|
113
|
+
export declare function logoutSession(profile: WilmaProfile, keys?: SessionKeys): Promise<boolean>;
|
|
91
114
|
/** Forget one login's session without ending it in Wilma (when the login itself is removed). */
|
|
92
|
-
export declare function forgetSession(profile: WilmaProfile): Promise<void>;
|
|
115
|
+
export declare function forgetSession(profile: WilmaProfile, keys?: SessionKeys): Promise<void>;
|
|
93
116
|
/**
|
|
94
117
|
* Read-only Wilma access for agent tools (MCP). Covers every Wilma login the
|
|
95
118
|
* family has saved; every method defaults to all children, and a student
|
|
@@ -106,9 +129,17 @@ export declare function matchStudents<T extends {
|
|
|
106
129
|
studentNumber: string;
|
|
107
130
|
name: string;
|
|
108
131
|
}>(students: T[], student: string): T[];
|
|
132
|
+
/** Whose login it is: a student's own when Wilma lists only the student themself, a guardian's when it says so. */
|
|
133
|
+
export declare function roleOf(students: {
|
|
134
|
+
role?: StudentInfo["role"];
|
|
135
|
+
}[]): StudentInfo["role"];
|
|
109
136
|
export declare class WilmaAccess {
|
|
110
137
|
private readonly accounts;
|
|
111
138
|
private studentsCache;
|
|
139
|
+
/** The children each login lists (by login index), including those read through another login. */
|
|
140
|
+
private readonly listed;
|
|
141
|
+
/** Why a login failed (by login index), while others worked. */
|
|
142
|
+
private readonly failures;
|
|
112
143
|
/** One logged-in session per Wilma login, shared by every child on it. */
|
|
113
144
|
private readonly sessions;
|
|
114
145
|
/** Logins that failed while other logins worked. */
|
|
@@ -122,8 +153,37 @@ export declare class WilmaAccess {
|
|
|
122
153
|
private labelOf;
|
|
123
154
|
private ref;
|
|
124
155
|
students(): Promise<AccountStudent[]>;
|
|
125
|
-
/**
|
|
126
|
-
|
|
156
|
+
/**
|
|
157
|
+
* The children as tools and commands show them (with their Wilma when there
|
|
158
|
+
* are several), and whose login reads each: a guardian's, or the student's own.
|
|
159
|
+
*/
|
|
160
|
+
studentList(): Promise<(StudentRef & {
|
|
161
|
+
role?: StudentInfo["role"];
|
|
162
|
+
})[]>;
|
|
163
|
+
/**
|
|
164
|
+
* Each login: its Wilma, username, whose it is (a guardian's, or a
|
|
165
|
+
* student's own; missing when Wilma doesn't say) and the children it lists.
|
|
166
|
+
* A child another login on the same Wilma also lists is read through one of
|
|
167
|
+
* them only; `readVia` names the other login's username. A child unticked
|
|
168
|
+
* on the login page has `hidden: true` and isn't read.
|
|
169
|
+
*/
|
|
170
|
+
logins(): Promise<{
|
|
171
|
+
problem?: string | undefined;
|
|
172
|
+
children: ({
|
|
173
|
+
name: string;
|
|
174
|
+
studentNumber: string;
|
|
175
|
+
hidden: boolean;
|
|
176
|
+
} | {
|
|
177
|
+
readVia?: string | undefined;
|
|
178
|
+
name: string;
|
|
179
|
+
studentNumber: string;
|
|
180
|
+
hidden?: undefined;
|
|
181
|
+
})[];
|
|
182
|
+
role?: "guardian" | "student" | undefined;
|
|
183
|
+
wilma: string;
|
|
184
|
+
username: string;
|
|
185
|
+
}[]>;
|
|
186
|
+
private sameWilma;
|
|
127
187
|
selectStudents(student?: string): Promise<AccountStudent[]>;
|
|
128
188
|
private client;
|
|
129
189
|
private perStudent;
|
|
@@ -142,8 +202,8 @@ export declare class WilmaAccess {
|
|
|
142
202
|
unavailable?: string[] | undefined;
|
|
143
203
|
today: string;
|
|
144
204
|
tomorrow: string;
|
|
145
|
-
todaySchedule:
|
|
146
|
-
tomorrowSchedule:
|
|
205
|
+
todaySchedule: ScheduleLesson[];
|
|
206
|
+
tomorrowSchedule: ScheduleLesson[];
|
|
147
207
|
upcomingExams: import("@wilm-ai/wilma-client").UpcomingExam[];
|
|
148
208
|
homework: import("@wilm-ai/wilma-client").HomeworkItem[];
|
|
149
209
|
lessonNotes: LessonNote[];
|
|
@@ -154,6 +214,8 @@ export declare class WilmaAccess {
|
|
|
154
214
|
pinned: boolean;
|
|
155
215
|
}[];
|
|
156
216
|
messages: {
|
|
217
|
+
invitationStatus?: string | null | undefined;
|
|
218
|
+
invitationInfo?: string | null | undefined;
|
|
157
219
|
wilmaId: number;
|
|
158
220
|
subject: string;
|
|
159
221
|
sentAt: Date;
|
|
@@ -179,8 +241,8 @@ export declare class WilmaAccess {
|
|
|
179
241
|
unavailable?: string[] | undefined;
|
|
180
242
|
today: string;
|
|
181
243
|
tomorrow: string;
|
|
182
|
-
todaySchedule:
|
|
183
|
-
tomorrowSchedule:
|
|
244
|
+
todaySchedule: ScheduleLesson[];
|
|
245
|
+
tomorrowSchedule: ScheduleLesson[];
|
|
184
246
|
upcomingExams: import("@wilm-ai/wilma-client").UpcomingExam[];
|
|
185
247
|
homework: import("@wilm-ai/wilma-client").HomeworkItem[];
|
|
186
248
|
lessonNotes: LessonNote[];
|
|
@@ -191,6 +253,8 @@ export declare class WilmaAccess {
|
|
|
191
253
|
pinned: boolean;
|
|
192
254
|
}[];
|
|
193
255
|
messages: {
|
|
256
|
+
invitationStatus?: string | null | undefined;
|
|
257
|
+
invitationInfo?: string | null | undefined;
|
|
194
258
|
wilmaId: number;
|
|
195
259
|
subject: string;
|
|
196
260
|
sentAt: Date;
|
|
@@ -211,17 +275,50 @@ export declare class WilmaAccess {
|
|
|
211
275
|
when?: (typeof SCHEDULE_WHEN)[number];
|
|
212
276
|
date?: string;
|
|
213
277
|
weekday?: string;
|
|
278
|
+
from?: string;
|
|
279
|
+
to?: string;
|
|
214
280
|
}): Promise<{
|
|
215
281
|
students: ({
|
|
216
282
|
student: StudentRef;
|
|
217
283
|
} & {
|
|
218
|
-
lessons:
|
|
284
|
+
lessons: ScheduleLesson[];
|
|
285
|
+
})[];
|
|
286
|
+
problems: {
|
|
287
|
+
wilma: string;
|
|
288
|
+
message: string;
|
|
289
|
+
}[];
|
|
290
|
+
from: string;
|
|
291
|
+
to: string;
|
|
292
|
+
date?: undefined;
|
|
293
|
+
weekStart?: undefined;
|
|
294
|
+
weekEnd?: undefined;
|
|
295
|
+
when: string;
|
|
296
|
+
} | {
|
|
297
|
+
students: ({
|
|
298
|
+
student: StudentRef;
|
|
299
|
+
} & {
|
|
300
|
+
lessons: ScheduleLesson[];
|
|
301
|
+
})[];
|
|
302
|
+
problems?: undefined;
|
|
303
|
+
from: string;
|
|
304
|
+
to: string;
|
|
305
|
+
date?: undefined;
|
|
306
|
+
weekStart?: undefined;
|
|
307
|
+
weekEnd?: undefined;
|
|
308
|
+
when: string;
|
|
309
|
+
} | {
|
|
310
|
+
students: ({
|
|
311
|
+
student: StudentRef;
|
|
312
|
+
} & {
|
|
313
|
+
lessons: ScheduleLesson[];
|
|
219
314
|
})[];
|
|
220
315
|
problems: {
|
|
221
316
|
wilma: string;
|
|
222
317
|
message: string;
|
|
223
318
|
}[];
|
|
224
319
|
date: string;
|
|
320
|
+
from?: undefined;
|
|
321
|
+
to?: undefined;
|
|
225
322
|
weekStart?: undefined;
|
|
226
323
|
weekEnd?: undefined;
|
|
227
324
|
when: string;
|
|
@@ -229,10 +326,12 @@ export declare class WilmaAccess {
|
|
|
229
326
|
students: ({
|
|
230
327
|
student: StudentRef;
|
|
231
328
|
} & {
|
|
232
|
-
lessons:
|
|
329
|
+
lessons: ScheduleLesson[];
|
|
233
330
|
})[];
|
|
234
331
|
problems?: undefined;
|
|
235
332
|
date: string;
|
|
333
|
+
from?: undefined;
|
|
334
|
+
to?: undefined;
|
|
236
335
|
weekStart?: undefined;
|
|
237
336
|
weekEnd?: undefined;
|
|
238
337
|
when: string;
|
|
@@ -240,7 +339,7 @@ export declare class WilmaAccess {
|
|
|
240
339
|
students: ({
|
|
241
340
|
student: StudentRef;
|
|
242
341
|
} & {
|
|
243
|
-
lessons:
|
|
342
|
+
lessons: ScheduleLesson[];
|
|
244
343
|
})[];
|
|
245
344
|
problems: {
|
|
246
345
|
wilma: string;
|
|
@@ -248,17 +347,21 @@ export declare class WilmaAccess {
|
|
|
248
347
|
}[];
|
|
249
348
|
weekStart: string;
|
|
250
349
|
weekEnd: string;
|
|
350
|
+
from?: undefined;
|
|
351
|
+
to?: undefined;
|
|
251
352
|
date?: undefined;
|
|
252
353
|
when: string;
|
|
253
354
|
} | {
|
|
254
355
|
students: ({
|
|
255
356
|
student: StudentRef;
|
|
256
357
|
} & {
|
|
257
|
-
lessons:
|
|
358
|
+
lessons: ScheduleLesson[];
|
|
258
359
|
})[];
|
|
259
360
|
problems?: undefined;
|
|
260
361
|
weekStart: string;
|
|
261
362
|
weekEnd: string;
|
|
363
|
+
from?: undefined;
|
|
364
|
+
to?: undefined;
|
|
262
365
|
date?: undefined;
|
|
263
366
|
when: string;
|
|
264
367
|
}>;
|
|
@@ -304,9 +407,16 @@ export declare class WilmaAccess {
|
|
|
304
407
|
})[];
|
|
305
408
|
problems?: undefined;
|
|
306
409
|
}>;
|
|
410
|
+
/**
|
|
411
|
+
* Exam grades: the newest the front page lists, or with `from` (to `to`,
|
|
412
|
+
* default today) Wilma's list of past exams for that period (Kokeet >
|
|
413
|
+
* Menneet), including exams still waiting for a grade (grade: null).
|
|
414
|
+
*/
|
|
307
415
|
grades(opts?: {
|
|
308
416
|
student?: string;
|
|
309
417
|
limit?: number;
|
|
418
|
+
from?: string;
|
|
419
|
+
to?: string;
|
|
310
420
|
}): Promise<{
|
|
311
421
|
students: ({
|
|
312
422
|
student: StudentRef;
|
|
@@ -324,6 +434,27 @@ export declare class WilmaAccess {
|
|
|
324
434
|
grades: import("@wilm-ai/wilma-client").ExamGrade[];
|
|
325
435
|
})[];
|
|
326
436
|
problems?: undefined;
|
|
437
|
+
} | {
|
|
438
|
+
students: ({
|
|
439
|
+
student: StudentRef;
|
|
440
|
+
} & {
|
|
441
|
+
grades: import("@wilm-ai/wilma-client").PastExam[];
|
|
442
|
+
})[];
|
|
443
|
+
problems: {
|
|
444
|
+
wilma: string;
|
|
445
|
+
message: string;
|
|
446
|
+
}[];
|
|
447
|
+
from: string;
|
|
448
|
+
to: string;
|
|
449
|
+
} | {
|
|
450
|
+
students: ({
|
|
451
|
+
student: StudentRef;
|
|
452
|
+
} & {
|
|
453
|
+
grades: import("@wilm-ai/wilma-client").PastExam[];
|
|
454
|
+
})[];
|
|
455
|
+
problems?: undefined;
|
|
456
|
+
from: string;
|
|
457
|
+
to: string;
|
|
327
458
|
}>;
|
|
328
459
|
/** Lesson notes for one day (default today), the last `days` days, or `from`–`to` (to defaults to today). */
|
|
329
460
|
lessonNotes(opts?: {
|
|
@@ -416,6 +547,54 @@ export declare class WilmaAccess {
|
|
|
416
547
|
})[];
|
|
417
548
|
problems?: undefined;
|
|
418
549
|
}>;
|
|
550
|
+
/**
|
|
551
|
+
* The child's class, class teacher and other supervisors (front page), and
|
|
552
|
+
* their own teachers (Wilma's recipient list) with what each teaches them,
|
|
553
|
+
* from this and next week's timetable. Parts Wilma can't give are listed in
|
|
554
|
+
* `unavailable`.
|
|
555
|
+
*/
|
|
556
|
+
contacts(opts?: {
|
|
557
|
+
student?: string;
|
|
558
|
+
}): Promise<{
|
|
559
|
+
students: ({
|
|
560
|
+
student: StudentRef;
|
|
561
|
+
} & {
|
|
562
|
+
contacts: {
|
|
563
|
+
unavailable?: string[] | undefined;
|
|
564
|
+
className: string | null;
|
|
565
|
+
supervisors: {
|
|
566
|
+
name: string;
|
|
567
|
+
role: string;
|
|
568
|
+
}[];
|
|
569
|
+
teachers: {
|
|
570
|
+
name: string;
|
|
571
|
+
subjects: string[];
|
|
572
|
+
}[];
|
|
573
|
+
};
|
|
574
|
+
})[];
|
|
575
|
+
problems: {
|
|
576
|
+
wilma: string;
|
|
577
|
+
message: string;
|
|
578
|
+
}[];
|
|
579
|
+
} | {
|
|
580
|
+
students: ({
|
|
581
|
+
student: StudentRef;
|
|
582
|
+
} & {
|
|
583
|
+
contacts: {
|
|
584
|
+
unavailable?: string[] | undefined;
|
|
585
|
+
className: string | null;
|
|
586
|
+
supervisors: {
|
|
587
|
+
name: string;
|
|
588
|
+
role: string;
|
|
589
|
+
}[];
|
|
590
|
+
teachers: {
|
|
591
|
+
name: string;
|
|
592
|
+
subjects: string[];
|
|
593
|
+
}[];
|
|
594
|
+
};
|
|
595
|
+
})[];
|
|
596
|
+
problems?: undefined;
|
|
597
|
+
}>;
|
|
419
598
|
printouts(opts?: {
|
|
420
599
|
student?: string;
|
|
421
600
|
}): Promise<{
|
|
@@ -450,7 +629,7 @@ export declare class WilmaAccess {
|
|
|
450
629
|
students: ({
|
|
451
630
|
student: StudentRef;
|
|
452
631
|
} & {
|
|
453
|
-
messages:
|
|
632
|
+
messages: ListedMessage[];
|
|
454
633
|
})[];
|
|
455
634
|
problems: {
|
|
456
635
|
wilma: string;
|
|
@@ -460,7 +639,7 @@ export declare class WilmaAccess {
|
|
|
460
639
|
students: ({
|
|
461
640
|
student: StudentRef;
|
|
462
641
|
} & {
|
|
463
|
-
messages:
|
|
642
|
+
messages: ListedMessage[];
|
|
464
643
|
})[];
|
|
465
644
|
problems?: undefined;
|
|
466
645
|
}>;
|