@rubytech/create-maxy-code 0.1.439 → 0.1.441
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/package.json +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +41 -12
- package/payload/platform/plugins/cloudflare/skills/calendar-site/SKILL.md +15 -5
- package/payload/platform/plugins/cloudflare/skills/calendar-site/template/functions/api/calendar/free-busy.ts +152 -0
- package/payload/platform/plugins/cloudflare/skills/calendar-site/template/public/index.html +6 -3
- package/payload/platform/plugins/cloudflare/skills/calendar-site/template/schema.sql +11 -0
- package/payload/platform/plugins/docs/references/admin-identity-gate.md +32 -17
- package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
- package/payload/platform/plugins/docs/references/calendar-booking.md +1 -1
- package/payload/platform/plugins/docs/references/graph.md +19 -3
- package/payload/platform/plugins/docs/references/internals.md +2 -2
- package/payload/platform/plugins/docs/references/outlook-guide.md +9 -4
- package/payload/platform/plugins/docs/references/troubleshooting.md +8 -0
- package/payload/platform/plugins/graph/.claude-plugin/plugin.json +19 -0
- package/payload/platform/plugins/graph/PLUGIN.md +10 -4
- package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/outlook/PLUGIN.md +7 -3
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/account-register.test.js +100 -10
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/account-register.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-list.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-list.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-list.test.js +55 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-list.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js +64 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-registry.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-registry.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-registry.test.js +193 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-registry.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js +58 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts +81 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js +171 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts +16 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js +25 -3
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/index.js +119 -50
- package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts +8 -4
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js +22 -3
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts +17 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js +84 -21
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts +26 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js +28 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js.map +1 -0
- package/payload/platform/plugins/outlook/references/auth.md +36 -8
- package/payload/platform/plugins/outlook/references/graph-surfaces.md +15 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.d.ts +27 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js +241 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js.map +1 -0
- package/payload/platform/scripts/__tests__/account-schema-owned-dirs.test.sh +100 -0
- package/payload/platform/scripts/lib/account-schema-owned-dirs.py +207 -0
- package/payload/platform/scripts/lib/account-schema-owned-dirs.sh +33 -0
- package/payload/platform/scripts/lib/provision-account-dir.sh +7 -0
- package/payload/platform/scripts/setup-account.sh +10 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +6 -4
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/tool-registration-drift.d.ts +7 -6
- package/payload/platform/services/claude-session-manager/dist/tool-registration-drift.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/tool-registration-drift.js +56 -7
- package/payload/platform/services/claude-session-manager/dist/tool-registration-drift.js.map +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/{chunk-UGPQCVIW.js → chunk-WC4WJR6X.js} +0 -6
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/{AdminLoginScreens-DeR71H29.js → AdminLoginScreens-CukwQufW.js} +1 -1
- package/payload/server/public/assets/AdminShell-BwEBH6tT.js +2 -0
- package/payload/server/public/assets/{admin-C15h-NL0.js → admin-CdFRCUUZ.js} +1 -1
- package/payload/server/public/assets/{browser-B7xQLIRK.js → browser-BWR59IkB.js} +1 -1
- package/payload/server/public/assets/{calendar-A-vGm6pL.js → calendar-kuTcxX1t.js} +1 -1
- package/payload/server/public/assets/chat-ejnq2rck.js +1 -0
- package/payload/server/public/assets/data-dZbOyLdT.js +1 -0
- package/payload/server/public/assets/{graph-D0orIRAh.js → graph-CboWM2_-.js} +2 -2
- package/payload/server/public/assets/{operator-DZzuotDB.js → operator-oSaB1mya.js} +1 -1
- package/payload/server/public/assets/{page-Ck_pLhAk.js → page-DEkI-Gl1.js} +1 -1
- package/payload/server/public/assets/{page-3g0A19Rv.js → page-DMeJHVfA.js} +5 -5
- package/payload/server/public/assets/{public-WFUQw9AD.js → public-C54tl6V4.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-CSk9KjhY.js → useCopyFeedback-DHoYHiK9.js} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-DCyJQkFK.js → useVoiceRecorder-b7HzTFs0.js} +1 -1
- package/payload/server/public/browser.html +2 -2
- package/payload/server/public/calendar.html +2 -2
- package/payload/server/public/chat.html +5 -5
- package/payload/server/public/data.html +3 -3
- package/payload/server/public/graph.html +3 -3
- package/payload/server/public/index.html +5 -5
- package/payload/server/public/operator.html +6 -6
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +255 -112
- package/payload/server/public/assets/AdminShell-DlAVQUda.js +0 -2
- package/payload/server/public/assets/chat-DAae6Z51.js +0 -1
- package/payload/server/public/assets/data-BNWpKx1d.js +0 -1
|
@@ -489,7 +489,7 @@ For subagents, the same spawn pushes `--add-dir` for every bundled plugin agents
|
|
|
489
489
|
|
|
490
490
|
A boot gate refuses to start the manager when any admin-allowlisted tool `mcp__<plugin>__*` lacks a registered server. The signal is `boot-failed reason=mcp-allowlist-without-server plugin=<p> tool=<t>` followed by `process.exit(1)`. The remediation is a one-line edit to the named `PLUGIN.md`: add the `mcp:` block. The complementary observability emit `mcp-config-allowlist-coverage admin-tools=A admin-registered=R` (where `A === R`) confirms the invariant per boot.
|
|
491
491
|
|
|
492
|
-
**Whether a plugin's server mounts is decided by the generated manifest, not the `mcp:` block.** A plugin ships its MCP as `mcp/`; the manifest generator ([`scripts/generate-plugin-manifests.mjs`](../../../../scripts/generate-plugin-manifests.mjs), run at bundle time over the payload) reads the plugin's `mcp-manifest:` frontmatter. `auto` emits an `mcpServers` block into the generated `.claude-plugin/plugin.json` — routed through `lib/mcp-spawn-tee`, carrying no per-session env (`LOG_DIR`/`SESSION_ID`/`ACCOUNT_ID`/`NEO4J_*`/`PLATFORM_ROOT` ride the PTY env block described above) — and copies the tee into the plugin's `lib/`; `skip` emits nothing, so Claude Code never spawns the server. The generator
|
|
492
|
+
**Whether a plugin's server mounts is decided by the generated manifest, not the `mcp:` block.** A plugin ships its MCP as `mcp/`; the manifest generator ([`scripts/generate-plugin-manifests.mjs`](../../../../scripts/generate-plugin-manifests.mjs), run at bundle time over the payload) reads the plugin's `mcp-manifest:` frontmatter. `auto` emits an `mcpServers` block into the generated `.claude-plugin/plugin.json` — routed through `lib/mcp-spawn-tee`, carrying no per-session env (`LOG_DIR`/`SESSION_ID`/`ACCOUNT_ID`/`NEO4J_*`/`PLATFORM_ROOT` ride the PTY env block described above) — and copies the tee into the plugin's `lib/`; `skip` emits nothing, so Claude Code never spawns the server. The generator recognises two server shapes. A plugin-local `mcp/` (its `hasMcp` check: `mcp/dist/index.js` or `mcp/package.json`) uses `auto` or `skip`. A tool-plugin whose `mcp:` server is a shared `platform/lib/<name>` binary rather than a plugin-local `mcp/` (e.g. `graph`, served by `platform/lib/graph-mcp`) is annotated `mcp-manifest: shared-lib`: the generator emits that plugin's `mcp:` block verbatim into `mcpServers` — command, args (anchored on `${PLATFORM_ROOT}`, routed through the shared `lib/mcp-spawn-tee`), and env — which Claude Code expands at spawn from the PTY env block. No tee copy is made into the plugin, because both the tee and the server resolve from `${PLATFORM_ROOT}` outside the per-plugin cache. `graph` is the one shared-lib tool-plugin; every other tool-plugin ships a plugin-local `mcp/` and uses `auto`. A plugin with an `mcp:` block, no plugin-local `mcp/`, and no `mcp-manifest: shared-lib` annotation is a FATAL at generation time. Premium plugins import platform libs (`mcp-lifeline`, etc.) via a source-tree relative path; the bundler's premium-lib shipper (`packages/create-maxy-code/scripts/bundle.js`) rewrites that path and vendors each referenced `platform/lib/<name>` into `premium-plugins/<bundle>/lib/<name>/`, so the auto-mounted dist resolves its imports. The `mcp-allowlist-without-server` gate above proves only that a plugin's PLUGIN.md `mcp:` block matches its allowlist — it reads source, not the generated manifest — so a plugin left on `skip` passes that gate while its server silently never mounts (its tools are listed but not live). The `tool-registration` gate below closes that blind spot.
|
|
493
493
|
|
|
494
494
|
A second boot gate walks every specialist `.md` under `platform/templates/specialists/agents/`, every bundled `<plugin>/agents/` directory, and the per-account `<accountDir>/specialists/agents/` directory, parses each file's `tools:` frontmatter line (canonical long-prefix names), and classifies every tool name as one of: CC-native (Read, Bash, …), a tool the loaded `PLUGIN.md` set actually serves (matched as the long canonical name in `toolSurface.all`), a third-party MCP bridge (a `mcp__plugin_*` name whose plugin segment is NOT a maxy platform plugin — Playwright etc., upstream-owned, passes unconditionally), `unknown-tool-in-plugin` (maxy plugin namespace served but tool name absent), `unknown-plugin-namespace` (namespace served by nothing), `brand-excluded-plugin` (namespace served by nothing on this brand, **but** the brand's `brand.json#plugins.excluded` list names it), or `malformed-name` (not CC-native and not `mcp__`-shaped). The first three pass. The next two refuse boot with one `boot-failed reason=specialist-tool-drift specialist=<name> tool=<t> drift=<class> path=<…>` line per defect, then `process.exit(1)`. A maxy-plugin `mcp__plugin_*` name is validated against `toolSurface.all`, so a typo or stale long-prefix tool name still refuses boot rather than passing as a bridge; the build-time `check-canonical-tool-names.mjs` gate catches the same drift in instruction files before publish. `brand-excluded-plugin` is a structural pass: it lands in a per-specialist strip-list, the manager continues to boot, and at spawn time `pty-spawner` removes those tool names from the `--agent <name>` spawn's `--allowed-tools` argv. The complementary observability emit `specialist-tool-strip specialist=<name> plugin=<p> tools=<csv> reason=brand-excluded` fires one line per stripped (specialist, plugin) pair so an operator who reads `server.log` sees the brand filter doing work without cross-referencing `brand.json` against the template. The startup-self-test line `startup-self-test specialist-tool-drift=ok inspected=<N> stripped-specialists=<M>` confirms the gate ran and how many specialists carry strip-lists.
|
|
495
495
|
|
|
@@ -501,7 +501,7 @@ The `brand-excluded` branch closes the recurring crash-restart loop on brands th
|
|
|
501
501
|
|
|
502
502
|
**Boot-time skill-availability gate.** A manager boot gate ([`skill-registration-drift.ts`](../../../services/claude-session-manager/src/skill-registration-drift.ts)) closes the silent divergence between that Maxy intent and the Claude Code registration surfaces. It resolves each premium sub named in every account's `account.json enabledPlugins` to its expected `name@marketplace` (nested sub → `maxy-premium-<bundle>`, standalone → `maxy-premium`) by descending the on-disk premium tree, then checks all three surfaces. For each enabled-but-unregistered sub it emits one `[skill-registration] op=drift FATAL accountId=<id> plugin=<sub> expected=<name@marketplace> missing=<surfaces>` line (best-effort mirrored to journald via `systemd-cat -t maxy-csm`), naming exactly which surface lacks the entry. Unlike the specialist-tool-drift gate this one does **not** `process.exit` — a mis-registered sub-plugin must not brick the whole install, so boot continues and the operator keeps a working admin for repair (re-running the corrected installer for the brand). A clean pass emits `startup-self-test skill-registration=ok inspected=<N>`.
|
|
503
503
|
|
|
504
|
-
**Boot-time tool-availability gate.** The twin of the skill gate for the MCP surface ([`tool-registration-drift.ts`](../../../services/claude-session-manager/src/tool-registration-drift.ts)). Where the skill gate reconciles a premium sub's *skills*, this one reconciles a tool-plugin's *server mount*. Its tool-plugin set is `toolSurface.mcpServers`
|
|
504
|
+
**Boot-time tool-availability gate.** The twin of the skill gate for the MCP surface ([`tool-registration-drift.ts`](../../../services/claude-session-manager/src/tool-registration-drift.ts)). Where the skill gate reconciles a premium sub's *skills*, this one reconciles a tool-plugin's *server mount*. Its tool-plugin set is `toolSurface.mcpServers` — every plugin that declares tools and an `mcp:` block, which includes specialist-only tool-plugins like `loop` whose tools are all `adminAllowlist: false` (the admin allowlist never lists them, so an admin-allowlist-derived check would miss them), and shared-lib-server plugins like `graph` (now that the generator emits an `mcpServers` block for them). For each account's `enabledPlugins` that names such a tool-plugin, it inspects the **generated** `.claude-plugin/plugin.json` rather than the PLUGIN.md source: an absent manifest is `reason=not-installed`, a manifest with no `mcpServers` is `reason=no-mcpServers-manifest` (the `skip` signature), and an `mcpServers` manifest whose server dist is missing is `reason=dist-missing` — for a plugin-local `mcp/` that dist is `mcp/dist/index.js`; for a shared-lib plugin the gate resolves the manifest's shared server arg via `${PLATFORM_ROOT}` and checks that binary. Each defect emits one `[tool-registration] op=drift FATAL accountId=<id> plugin=<name> expected-server=<name> reason=<…>` line (best-effort mirrored to journald via `systemd-cat -t maxy-csm`); like the skill gate it does **not** `process.exit`, so a single unmounted tool-plugin does not brick the install. A clean pass emits `startup-self-test tool-registration=ok inspected=<N>`. This is the standing check the no-event failure needs: an unmounted server emits no `[mcp-helper] op=spawn` line, so only a gate that converts its absence into `op=drift` makes it loud.
|
|
505
505
|
|
|
506
506
|
**Structured journald mirror for boot-failed.** Every `boot-failed reason=specialist-tool-drift …` line is mirrored to journald via `systemd-cat -t maxy-csm -p err` with the fields `specialist=`, `tool=`, `drift_reason=`, `agent_path=` so `journalctl --user -u <brand>-claude-session-manager.service -t maxy-csm` can filter by any of them without grep on `server.log`. The stdout line stays unchanged so the existing diagnostic one-liners keep working. `systemd-cat` absence (e.g. macOS dev box) is swallowed — the stdout line is the primary surface; the structured emit is auxiliary.
|
|
507
507
|
|
|
@@ -8,6 +8,7 @@ The `outlook` plugin gives the admin agent access to Microsoft 365 / Outlook.com
|
|
|
8
8
|
2. **Per account: register the Outlook account** — in admin chat, ask the agent to "register my Outlook account". The agent runs `outlook-account-register`, which returns a short user code and the URL `microsoft.com/devicelogin` and relays both to you.
|
|
9
9
|
3. **Consent on any device** — open `microsoft.com/devicelogin` on your phone or laptop, enter the code, sign in to your Microsoft account, and consent to the requested scopes (`offline_access`, `User.Read`, `Mail.ReadWrite`, `Mail.Send`, `Calendars.ReadWrite`, `Contacts.Read`). `Mail.Send` enables sending and `Calendars.ReadWrite` enables calendar control; a mailbox last consented on an older read-only scope set must re-register before send or any calendar write succeeds. The agent finishes by calling `outlook-account-register-poll`.
|
|
10
10
|
4. **Done.** Subsequent tool calls (mail, calendar, contacts) use the persisted refresh token transparently.
|
|
11
|
+
5. **Several mailboxes on one account.** Register again to add a second mailbox; it is stored alongside the first, not in place of it. Every operational tool then takes an optional `mailbox` argument — an email address or graphUserId. With one mailbox, omit it. With two or more, name it; omitting it returns a refusal listing the connected mailboxes. `outlook-mailbox-list` shows every connected mailbox. Only one registration runs at a time per account; starting another while one is live returns an in-progress signal.
|
|
11
12
|
|
|
12
13
|
## Tools
|
|
13
14
|
|
|
@@ -15,7 +16,7 @@ The `outlook` plugin gives the admin agent access to Microsoft 365 / Outlook.com
|
|
|
15
16
|
|------|---------|
|
|
16
17
|
| `outlook-account-register` | Start the device-code flow for this account. Returns a user code + `microsoft.com/devicelogin` URL immediately; does not block. |
|
|
17
18
|
| `outlook-account-register-poll` | Finish the device-code flow. One check per call: returns pending, registered, expired, or denied. |
|
|
18
|
-
| `outlook-mail-list` | Recent mail. Default top=25, folder=Inbox. |
|
|
19
|
+
| `outlook-mail-list` | Recent mail. Default top=25, folder=Inbox. `folder` takes a well-known name or alias (case/space-insensitive): Inbox, Sent Items/sent, Drafts, Deleted Items/trash, Junk Email/junk/spam, Archive, Outbox — or a raw folder id. Custom folder names are not resolved. |
|
|
19
20
|
| `outlook-mail-search` | Microsoft Graph `$search` over the mailbox. |
|
|
20
21
|
| `outlook-calendar-list` | Calendar events in next rangeDays days (default 7, max 365). |
|
|
21
22
|
| `outlook-calendar-event` | Full detail of a single event by id. |
|
|
@@ -25,7 +26,8 @@ The `outlook` plugin gives the admin agent access to Microsoft 365 / Outlook.com
|
|
|
25
26
|
| `outlook-calendar-respond` | Accept, decline, or tentatively accept an invite. |
|
|
26
27
|
| `outlook-calendar-freebusy` | Availability for one or more addresses over a window. |
|
|
27
28
|
| `outlook-contacts-list` | Top contacts. Default top=50. |
|
|
28
|
-
| `outlook-mailbox-info` | Health probe — auth state, refresh-window, folder count. |
|
|
29
|
+
| `outlook-mailbox-info` | Health probe for one mailbox — auth state, refresh-window, folder count. Name the mailbox with `mailbox` when several are attached. |
|
|
30
|
+
| `outlook-mailbox-list` | Every connected mailbox with its email, scopes, and token health. Local only — no network — so it also flags a mailbox whose stored credentials cannot be read. |
|
|
29
31
|
| `outlook-mail-send` | Send a message (`POST /me/sendMail`). to/cc/bcc arrays, subject, body, isHtml. Success asserted on Graph 202. Needs `Mail.Send`. |
|
|
30
32
|
| `outlook-draft` | Create a draft in Drafts (`POST /me/messages`). Same body model. Returns the draft id. Needs `Mail.ReadWrite`. |
|
|
31
33
|
| `outlook-draft-send` | Send an existing draft by id (`POST /me/messages/{id}/send`). Graph consumes the draft, returns 202. Needs `Mail.Send`. |
|
|
@@ -43,10 +45,12 @@ All log lines start with `[outlook-mcp]` and write to `server.log`. They are key
|
|
|
43
45
|
| Pending-store census | `pending-store-census account=<id> live=<N>` |
|
|
44
46
|
| Pending cleared | `pending-cleared account=<id> verified=<bool>` |
|
|
45
47
|
| /me failed post-token | `me-failed account=<id> status=<N>` |
|
|
46
|
-
| Auth ok | `auth-ok account=<id> graphUserId=<id> scopes=<csv> tokenExpSec=<N>` |
|
|
48
|
+
| Auth ok | `auth-ok account=<id> graphUserId=<id> mail=<addr> scopes=<csv> tokenExpSec=<N>` |
|
|
49
|
+
| Registration already in flight | `register-in-progress account=<id>` (start refused; one device-code flow at a time) |
|
|
50
|
+
| Credential resolve | `cred-resolve account=<id> mailbox=<id-or-empty> source=arg|sole|none|refused` — names the resolved mailbox and how it was chosen; `source=refused` is the ambiguity/not-found refusal |
|
|
47
51
|
| Token refreshed | `token-refreshed account=<id> oldExpSec=<N> newExpSec=<N>` |
|
|
48
52
|
| Refresh failed | `token-refresh-failed account=<id> reason=<err>` (terminal) |
|
|
49
|
-
| Mail list | `mail-list account=<id>
|
|
53
|
+
| Mail list | `mail-list op=resolve account=<id> folderIn=<name> folderResolved=<token> asRawId=<bool> count=<N> elapsedMs=<N>` (on error, `error=<msg>` replaces `count`) |
|
|
50
54
|
| Mail search | `mail-search account=<id> query=<trunc-32> count=<N> elapsedMs=<N>` |
|
|
51
55
|
| Calendar list | `calendar-list account=<id> rangeDays=<N> count=<N> elapsedMs=<N>` |
|
|
52
56
|
| Calendar event | `calendar-event account=<id> eventId=<trunc-12> elapsedMs=<N>` |
|
|
@@ -58,6 +62,7 @@ All log lines start with `[outlook-mcp]` and write to `server.log`. They are key
|
|
|
58
62
|
| Scope insufficient | `scope-insufficient tool=<t> account=<id> missing=Calendars.ReadWrite` (stale read-only consent) |
|
|
59
63
|
| Contacts list | `contacts-list account=<id> count=<N> elapsedMs=<N>` |
|
|
60
64
|
| Mailbox info | `mailbox-info account=<id> tokenWithinRefreshWindow=<bool> folderCount=<N>` |
|
|
65
|
+
| Mailbox list | `mailbox-list account=<id> count=<N> unreadable=<N>` (unreadable>0 flags an orphaned or corrupt mailbox store) |
|
|
61
66
|
| Graph error | `graph-error account=<id> status=<N> code=<graphErrorCode> retryAfterMs=<N-or-null>` — writes flow through the same `callGraph` loop, so a failed write logs `graph-error` too |
|
|
62
67
|
| On-prem rejected | `on-prem-rejected account=<id> mailServer=<host>` (terminal) |
|
|
63
68
|
| Scope insufficient | `scope-insufficient account=<id> tool=<t> missing=Mail.Send` (terminal — re-register to grant send) |
|
|
@@ -306,3 +306,11 @@ sudo systemctl --user start maxy-ui
|
|
|
306
306
|
- `grep -c '\[admin-ui\] sidebar-meta-pane-reconcile' ~/.maxy/logs/admin-ui-console.log` should equal the count of End / Resume / Purge clicks while the metadata pane was open. A `to=gone` line without a paired Close click means the pane's auto-close logic regressed.
|
|
307
307
|
|
|
308
308
|
**Why this matters.** The render-body log was misleading: it read as "the admin agent is checking onboarding state continuously", when in fact `onboardingComplete` had not changed at all. The fix moved the log into `useEffect(…, [])` then dropped the per-tick poll entirely, so a quiet console is now the steady state. With both fixes in place, console output is a faithful record of what the page actually did each operator click.
|
|
309
|
+
|
|
310
|
+
## Typing in the `/chat` composer is slow to render
|
|
311
|
+
|
|
312
|
+
**Symptom.** In the admin/operator `/chat` webchat, typing into the message box lags. Characters appear behind the keystrokes, worse on a long conversation. CPU, RAM, and network show no pressure.
|
|
313
|
+
|
|
314
|
+
**Cause.** A controlled-input re-render storm. The composer text lives in the chat host, and `<Transcript>` is a sibling under that host, so each keystroke re-renders the host and reconciles the whole transcript tree. Cost scales with transcript length. The "no CPU/RAM spike" reading is consistent, not contradictory: React reconciliation is short main-thread bursts, invisible in Activity Monitor averages; the felt symptom is per-keystroke input latency. See the webchat composer/transcript render contract in `.docs/platform.md`.
|
|
315
|
+
|
|
316
|
+
**Diagnostic.** Open the surface with `?diag=composer` appended to the URL and watch the browser console while typing on a long conversation. Each `[diag:composer]` line is a transcript reconcile attributed to a keystroke, with its `actualDuration` (reconcile cost) and `latency` (keystroke to commit). Correct behaviour logs zero lines per keystroke and the transcript still streams and follows the tail. One or more lines per keystroke, or a latency that grows with conversation length, means the decoupling regressed: a keystroke state re-hoisted above the `<Transcript>` sibling, or a `<Transcript>` prop identity that now churns on a keystroke-only render.
|
|
@@ -4,5 +4,24 @@
|
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|
|
7
|
+
},
|
|
8
|
+
"mcpServers": {
|
|
9
|
+
"graph": {
|
|
10
|
+
"type": "stdio",
|
|
11
|
+
"command": "node",
|
|
12
|
+
"args": [
|
|
13
|
+
"${PLATFORM_ROOT}/lib/mcp-spawn-tee/dist/index.js",
|
|
14
|
+
"${PLATFORM_ROOT}/lib/graph-mcp/dist/index.js"
|
|
15
|
+
],
|
|
16
|
+
"env": {
|
|
17
|
+
"MCP_SPAWN_TEE_NAME": "graph",
|
|
18
|
+
"LOG_DIR": "${LOG_DIR}",
|
|
19
|
+
"PLATFORM_ROOT": "${PLATFORM_ROOT}",
|
|
20
|
+
"ACCOUNT_ID": "${ACCOUNT_ID}",
|
|
21
|
+
"SESSION_ID": "${SESSION_ID}",
|
|
22
|
+
"NEO4J_URI": "${NEO4J_URI}",
|
|
23
|
+
"NEO4J_PASSWORD": "${NEO4J_PASSWORD}"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
7
26
|
}
|
|
8
27
|
}
|
|
@@ -10,6 +10,7 @@ tools:
|
|
|
10
10
|
- name: maxy-graph-write_neo4j_cypher
|
|
11
11
|
publicAllowlist: false
|
|
12
12
|
adminAllowlist: false
|
|
13
|
+
mcp-manifest: shared-lib
|
|
13
14
|
mcp:
|
|
14
15
|
command: node
|
|
15
16
|
args:
|
|
@@ -28,10 +29,15 @@ description: "Raw Neo4j Cypher proxy. Wraps the upstream `mcp-neo4j-cypher` serv
|
|
|
28
29
|
|
|
29
30
|
# graph
|
|
30
31
|
|
|
31
|
-
Cypher proxy plugin. The server is `platform/lib/graph-mcp`,
|
|
32
|
-
Python `mcp-neo4j-cypher` server per brand and proxies
|
|
33
|
-
`maxy-graph` namespace.
|
|
34
|
-
|
|
32
|
+
Cypher proxy plugin. The server is the shared binary `platform/lib/graph-mcp`,
|
|
33
|
+
which adopts the upstream Python `mcp-neo4j-cypher` server per brand and proxies
|
|
34
|
+
its JSON-RPC stdio under the `maxy-graph` namespace. Unlike plugins that ship a
|
|
35
|
+
plugin-local `mcp/` dir, graph's server is a shared `platform/lib/<name>` binary,
|
|
36
|
+
so it mounts via the generator's `shared-lib` disposition: `mcp-manifest: shared-lib`
|
|
37
|
+
tells `scripts/generate-plugin-manifests.mjs` to emit graph's `mcp:` block into the
|
|
38
|
+
generated `.claude-plugin/plugin.json` `mcpServers`, routed through the shared
|
|
39
|
+
`mcp-spawn-tee`. Its tools then surface to admin and specialist spawns as
|
|
40
|
+
`mcp__plugin_graph_graph__maxy-graph-*`.
|
|
35
41
|
|
|
36
42
|
Write Cypher is intercepted (not forwarded) so the shim can validate tokens against the
|
|
37
43
|
live schema cache, stamp `createdBy` provenance, and roll back orphan-producing writes.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "outlook",
|
|
3
|
-
"description": "Microsoft 365 / Outlook.com via Microsoft Graph. Mail is read + draft + send; contacts are read-only; calendar is read + control. Per-account OAuth device-code flow; no client secret. Tools — outlook-account-register / outlook-account-register-poll: device-code register (start + poll); outlook-mail-list / outlook-mail-search: inbox; outlook-mail-send / outlook-draft / outlook-draft-send: compose and send; outlook-calendar-list / outlook-calendar-event: read calendar; outlook-calendar-create / outlook-calendar-update / outlook-calendar-cancel / outlook-calendar-respond / outlook-calendar-freebusy: control calendar; outlook-contacts-list: contacts; outlook-mailbox-info: auth state + folder count.",
|
|
3
|
+
"description": "Microsoft 365 / Outlook.com via Microsoft Graph. Mail is read + draft + send; contacts are read-only; calendar is read + control. Per-account OAuth device-code flow; no client secret. One account can hold several mailboxes at once; every operational tool takes an optional mailbox selector (email or graphUserId) and refuses ambiguously when two or more are attached and none is named. Tools — outlook-account-register / outlook-account-register-poll: device-code register (start + poll); outlook-mail-list / outlook-mail-search: inbox; outlook-mail-send / outlook-draft / outlook-draft-send: compose and send; outlook-calendar-list / outlook-calendar-event: read calendar; outlook-calendar-create / outlook-calendar-update / outlook-calendar-cancel / outlook-calendar-respond / outlook-calendar-freebusy: control calendar; outlook-contacts-list: contacts; outlook-mailbox-info: auth state + folder count for one mailbox; outlook-mailbox-list: every connected mailbox.",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: outlook
|
|
3
|
-
description: Microsoft 365 / Outlook.com via Microsoft Graph. Mail is read + draft + send; contacts are read-only; calendar is read + control. Per-account OAuth device-code flow; no client secret. Tools — outlook-account-register / outlook-account-register-poll: device-code register (start + poll); outlook-mail-list / outlook-mail-search: inbox; outlook-mail-send / outlook-draft / outlook-draft-send: compose and send; outlook-calendar-list / outlook-calendar-event: read calendar; outlook-calendar-create / outlook-calendar-update / outlook-calendar-cancel / outlook-calendar-respond / outlook-calendar-freebusy: control calendar; outlook-contacts-list: contacts; outlook-mailbox-info: auth state + folder count.
|
|
3
|
+
description: Microsoft 365 / Outlook.com via Microsoft Graph. Mail is read + draft + send; contacts are read-only; calendar is read + control. Per-account OAuth device-code flow; no client secret. One account can hold several mailboxes at once; every operational tool takes an optional mailbox selector (email or graphUserId) and refuses ambiguously when two or more are attached and none is named. Tools — outlook-account-register / outlook-account-register-poll: device-code register (start + poll); outlook-mail-list / outlook-mail-search: inbox; outlook-mail-send / outlook-draft / outlook-draft-send: compose and send; outlook-calendar-list / outlook-calendar-event: read calendar; outlook-calendar-create / outlook-calendar-update / outlook-calendar-cancel / outlook-calendar-respond / outlook-calendar-freebusy: control calendar; outlook-contacts-list: contacts; outlook-mailbox-info: auth state + folder count for one mailbox; outlook-mailbox-list: every connected mailbox.
|
|
4
4
|
tools:
|
|
5
5
|
- name: outlook-account-register
|
|
6
6
|
publicAllowlist: false
|
|
@@ -50,6 +50,9 @@ tools:
|
|
|
50
50
|
- name: outlook-mailbox-info
|
|
51
51
|
publicAllowlist: false
|
|
52
52
|
adminAllowlist: false
|
|
53
|
+
- name: outlook-mailbox-list
|
|
54
|
+
publicAllowlist: false
|
|
55
|
+
adminAllowlist: false
|
|
53
56
|
always: false
|
|
54
57
|
embed: ["admin"]
|
|
55
58
|
metadata: {"platform":{"optional":true}}
|
|
@@ -75,12 +78,13 @@ Microsoft Graph access for Outlook.com / Microsoft 365 mailboxes. Mail is read,
|
|
|
75
78
|
|
|
76
79
|
## Capabilities
|
|
77
80
|
|
|
78
|
-
- **Register:** `outlook-account-register` — starts the OAuth device-code flow against the brand's Entra app and returns a user code + verification URL immediately. The operator opens `microsoft.com/devicelogin` on any device, enters the code, and consents. `outlook-account-register-poll` completes registration — one check per call.
|
|
81
|
+
- **Register:** `outlook-account-register` — starts the OAuth device-code flow against the brand's Entra app and returns a user code + verification URL immediately. The operator opens `microsoft.com/devicelogin` on any device, enters the code, and consents. `outlook-account-register-poll` completes registration — one check per call. Registering a second mailbox on the same account adds it alongside the first rather than replacing it; each mailbox holds its own encrypted tokens (AES-256-CBC) under a shared account key. One device-code flow runs at a time per account; starting another while one is live returns an `in-progress` signal.
|
|
82
|
+
- **Multiple mailboxes:** one account can hold several mailboxes. Every operational tool takes an optional `mailbox` argument (email or graphUserId). With one mailbox, omit it. With two or more, name it — omitting it returns a refusal listing the connected mailboxes. `outlook-mailbox-list` shows them all.
|
|
79
83
|
- **Mail (read):** `outlook-mail-list` returns recent messages; `outlook-mail-search` runs a Graph `$search` query.
|
|
80
84
|
- **Mail (send / draft):** `outlook-mail-send` sends via `POST /me/sendMail` (success asserted on Graph 202); `outlook-draft` creates a draft and returns its id; `outlook-draft-send` sends an existing draft by id. Recipients are to/cc/bcc arrays; body is HTML or plain text via `isHtml`. Requires `Mail.Send` + `Mail.ReadWrite`; a mailbox on the old read-only consent returns an actionable "re-register to grant send" signal.
|
|
81
85
|
- **Calendar (read + control):** `outlook-calendar-list` and `outlook-calendar-event` read events; `outlook-calendar-create` books an event; `outlook-calendar-update` reschedules or edits one; `outlook-calendar-cancel` cancels a meeting (notifying attendees when the account organises it) or deletes a personal appointment; `outlook-calendar-respond` accepts, declines, or tentatively accepts an invite; `outlook-calendar-freebusy` reports availability for one or more addresses over a window. Writes require the `Calendars.ReadWrite` consent; a mailbox on stale read-only consent returns a distinct signal naming the scope and `outlook-account-register`.
|
|
82
86
|
- **Contacts (read-only):** `outlook-contacts-list` returns contacts.
|
|
83
|
-
- **Health:** `outlook-mailbox-info` reports auth state, refresh-window status, and top-level folder count. Use
|
|
87
|
+
- **Health:** `outlook-mailbox-info` reports auth state, refresh-window status, and top-level folder count for one mailbox (name it with `mailbox` when several are attached). `outlook-mailbox-list` lists every connected mailbox with its email, scopes, and token health, reading local stores only — no network — so it also flags a mailbox whose stored credentials cannot be read. Use these to answer "did mailbox X auth?" without grepping logs.
|
|
84
88
|
|
|
85
89
|
## Out of scope
|
|
86
90
|
|
|
@@ -4,7 +4,7 @@ import { mkdtempSync, rmSync } from "node:fs";
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { PendingStore } from "../auth/pending-store.js";
|
|
7
|
-
import {
|
|
7
|
+
import { MailboxRegistry } from "../auth/mailbox-registry.js";
|
|
8
8
|
import { runAccountRegisterStart, runAccountRegisterPoll } from "../tools/account-register.js";
|
|
9
9
|
const mk = () => {
|
|
10
10
|
const dir = mkdtempSync(join(tmpdir(), "outlook-reg-"));
|
|
@@ -64,7 +64,7 @@ test("poll authorization_pending -> pending", async () => {
|
|
|
64
64
|
ps.write(pendingEntry());
|
|
65
65
|
const restore = withFetch([() => json({ error: "authorization_pending" }, 400)]);
|
|
66
66
|
try {
|
|
67
|
-
const out = await runAccountRegisterPoll({ accountId: "acct-2", ...cfg,
|
|
67
|
+
const out = await runAccountRegisterPoll({ accountId: "acct-2", ...cfg, registry: new MailboxRegistry("acct-2", dir), pendingStore: ps });
|
|
68
68
|
assert.equal(out.status, "pending");
|
|
69
69
|
}
|
|
70
70
|
finally {
|
|
@@ -72,21 +72,26 @@ test("poll authorization_pending -> pending", async () => {
|
|
|
72
72
|
cleanup();
|
|
73
73
|
}
|
|
74
74
|
});
|
|
75
|
-
test("poll success -> registered,
|
|
75
|
+
test("poll success -> registered under the /me mailbox, mail stored, pending cleared", async () => {
|
|
76
76
|
const { dir, cleanup } = mk();
|
|
77
77
|
const ps = new PendingStore("acct-3", dir);
|
|
78
78
|
ps.write(pendingEntry());
|
|
79
|
-
const
|
|
79
|
+
const reg = new MailboxRegistry("acct-3", dir);
|
|
80
80
|
const restore = withFetch([
|
|
81
81
|
() => json({ access_token: "AT", refresh_token: "RT", expires_in: 3600, token_type: "Bearer", scope: "offline_access User.Read Mail.ReadWrite Mail.Send Calendars.ReadWrite Contacts.Read" }),
|
|
82
82
|
() => json({ id: "graph-123", mail: "a@b.com" }),
|
|
83
83
|
]);
|
|
84
84
|
try {
|
|
85
|
-
const out = await runAccountRegisterPoll({ accountId: "acct-3", ...cfg,
|
|
85
|
+
const out = await runAccountRegisterPoll({ accountId: "acct-3", ...cfg, registry: reg, pendingStore: ps });
|
|
86
86
|
assert.equal(out.status, "registered");
|
|
87
|
-
if (out.status === "registered")
|
|
87
|
+
if (out.status === "registered") {
|
|
88
88
|
assert.equal(out.graphUserId, "graph-123");
|
|
89
|
-
|
|
89
|
+
assert.equal(out.mail, "a@b.com");
|
|
90
|
+
}
|
|
91
|
+
// Tokens persisted under the resolved mailbox, addressable and carrying mail.
|
|
92
|
+
const store = reg.storeFor("graph-123");
|
|
93
|
+
assert.equal(store.read()?.accessToken, "AT");
|
|
94
|
+
assert.equal(store.read()?.mail, "a@b.com");
|
|
90
95
|
assert.equal(ps.census(), 0);
|
|
91
96
|
}
|
|
92
97
|
finally {
|
|
@@ -100,7 +105,7 @@ test("poll expired_token -> expired and pending cleared", async () => {
|
|
|
100
105
|
ps.write(pendingEntry());
|
|
101
106
|
const restore = withFetch([() => json({ error: "expired_token" }, 400)]);
|
|
102
107
|
try {
|
|
103
|
-
const out = await runAccountRegisterPoll({ accountId: "acct-4", ...cfg,
|
|
108
|
+
const out = await runAccountRegisterPoll({ accountId: "acct-4", ...cfg, registry: new MailboxRegistry("acct-4", dir), pendingStore: ps });
|
|
104
109
|
assert.equal(out.status, "expired");
|
|
105
110
|
assert.equal(ps.census(), 0);
|
|
106
111
|
}
|
|
@@ -115,7 +120,7 @@ test("poll access_denied -> denied and pending cleared", async () => {
|
|
|
115
120
|
ps.write(pendingEntry());
|
|
116
121
|
const restore = withFetch([() => json({ error: "access_denied" }, 400)]);
|
|
117
122
|
try {
|
|
118
|
-
const out = await runAccountRegisterPoll({ accountId: "acct-5", ...cfg,
|
|
123
|
+
const out = await runAccountRegisterPoll({ accountId: "acct-5", ...cfg, registry: new MailboxRegistry("acct-5", dir), pendingStore: ps });
|
|
119
124
|
assert.equal(out.status, "denied");
|
|
120
125
|
assert.equal(ps.census(), 0);
|
|
121
126
|
}
|
|
@@ -127,11 +132,96 @@ test("poll access_denied -> denied and pending cleared", async () => {
|
|
|
127
132
|
test("poll with no pending entry -> expired", async () => {
|
|
128
133
|
const { dir, cleanup } = mk();
|
|
129
134
|
try {
|
|
130
|
-
const out = await runAccountRegisterPoll({ accountId: "acct-6", ...cfg,
|
|
135
|
+
const out = await runAccountRegisterPoll({ accountId: "acct-6", ...cfg, registry: new MailboxRegistry("acct-6", dir), pendingStore: new PendingStore("acct-6", dir) });
|
|
131
136
|
assert.equal(out.status, "expired");
|
|
132
137
|
}
|
|
133
138
|
finally {
|
|
134
139
|
cleanup();
|
|
135
140
|
}
|
|
136
141
|
});
|
|
142
|
+
test("start refuses with in-progress when a live registration is already pending", async () => {
|
|
143
|
+
const { dir, cleanup } = mk();
|
|
144
|
+
const ps = new PendingStore("acct-ip", dir);
|
|
145
|
+
ps.write(pendingEntry()); // live: expiresAtMs is now + 900_000
|
|
146
|
+
const before = ps.read()?.deviceCode;
|
|
147
|
+
try {
|
|
148
|
+
const res = await runAccountRegisterStart({ accountId: "acct-ip", ...cfg, pendingStore: ps });
|
|
149
|
+
assert.equal(res.status, "in-progress");
|
|
150
|
+
// Existing pending entry is untouched — not overwritten by a new device code.
|
|
151
|
+
assert.equal(ps.read()?.deviceCode, before);
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
cleanup();
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
test("start proceeds when the existing pending entry has expired", async () => {
|
|
158
|
+
const { dir, cleanup } = mk();
|
|
159
|
+
const ps = new PendingStore("acct-exp", dir);
|
|
160
|
+
ps.write({ ...pendingEntry(), deviceCode: "OLD", expiresAtMs: Date.now() - 1000 });
|
|
161
|
+
const restore = withFetch([() => json({
|
|
162
|
+
device_code: "NEW", user_code: "AAAA-1111",
|
|
163
|
+
verification_uri: "https://microsoft.com/devicelogin", expires_in: 900, interval: 5,
|
|
164
|
+
})]);
|
|
165
|
+
try {
|
|
166
|
+
const res = await runAccountRegisterStart({ accountId: "acct-exp", ...cfg, pendingStore: ps });
|
|
167
|
+
assert.equal(res.status, "pending");
|
|
168
|
+
assert.equal(ps.read()?.deviceCode, "NEW", "expired pending must be replaced");
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
restore();
|
|
172
|
+
cleanup();
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
test("a second distinct mailbox is added, not evicted (Rob then info@)", async () => {
|
|
176
|
+
const { dir, cleanup } = mk();
|
|
177
|
+
const reg = new MailboxRegistry("acct-two", dir);
|
|
178
|
+
// Rob completes.
|
|
179
|
+
let ps = new PendingStore("acct-two", dir);
|
|
180
|
+
ps.write(pendingEntry());
|
|
181
|
+
let restore = withFetch([
|
|
182
|
+
() => json({ access_token: "AT-rob", refresh_token: "RT", expires_in: 3600, token_type: "Bearer", scope: "Mail.Read" }),
|
|
183
|
+
() => json({ id: "rob-guid", mail: "rob@ecolecltd.co.uk" }),
|
|
184
|
+
]);
|
|
185
|
+
await runAccountRegisterPoll({ accountId: "acct-two", ...cfg, registry: reg, pendingStore: ps });
|
|
186
|
+
restore();
|
|
187
|
+
// info@ completes.
|
|
188
|
+
ps = new PendingStore("acct-two", dir);
|
|
189
|
+
ps.write(pendingEntry());
|
|
190
|
+
restore = withFetch([
|
|
191
|
+
() => json({ access_token: "AT-info", refresh_token: "RT", expires_in: 3600, token_type: "Bearer", scope: "Mail.Read" }),
|
|
192
|
+
() => json({ id: "info-guid", mail: "info@ecolecltd.co.uk" }),
|
|
193
|
+
]);
|
|
194
|
+
try {
|
|
195
|
+
await runAccountRegisterPoll({ accountId: "acct-two", ...cfg, registry: reg, pendingStore: ps });
|
|
196
|
+
const boxes = new MailboxRegistry("acct-two", dir).list();
|
|
197
|
+
assert.equal(boxes.length, 2, "both mailboxes must remain attached");
|
|
198
|
+
assert.equal(reg.storeFor("rob-guid").read()?.accessToken, "AT-rob", "Rob not evicted");
|
|
199
|
+
assert.equal(reg.storeFor("info-guid").read()?.accessToken, "AT-info");
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
restore();
|
|
203
|
+
cleanup();
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
test("re-registering the same mailbox updates in place (no duplicate)", async () => {
|
|
207
|
+
const { dir, cleanup } = mk();
|
|
208
|
+
const reg = new MailboxRegistry("acct-re", dir);
|
|
209
|
+
for (const at of ["AT-1", "AT-2"]) {
|
|
210
|
+
const ps = new PendingStore("acct-re", dir);
|
|
211
|
+
ps.write(pendingEntry());
|
|
212
|
+
const restore = withFetch([
|
|
213
|
+
() => json({ access_token: at, refresh_token: "RT", expires_in: 3600, token_type: "Bearer", scope: "Mail.Read" }),
|
|
214
|
+
() => json({ id: "same-guid", mail: "same@x.com" }),
|
|
215
|
+
]);
|
|
216
|
+
await runAccountRegisterPoll({ accountId: "acct-re", ...cfg, registry: reg, pendingStore: ps });
|
|
217
|
+
restore();
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
assert.equal(new MailboxRegistry("acct-re", dir).list().length, 1);
|
|
221
|
+
assert.equal(reg.storeFor("same-guid").read()?.accessToken, "AT-2");
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
cleanup();
|
|
225
|
+
}
|
|
226
|
+
});
|
|
137
227
|
//# sourceMappingURL=account-register.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-register.test.js","sourceRoot":"","sources":["../../src/__tests__/account-register.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"account-register.test.js","sourceRoot":"","sources":["../../src/__tests__/account-register.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail-list.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/mail-list.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { resolveFolder } from "../tools/mail-list.js";
|
|
4
|
+
// resolveFolder maps a caller-supplied folder value to the Graph
|
|
5
|
+
// `/me/mailFolders/{id}` segment. Graph accepts only a well-known token
|
|
6
|
+
// (single word, no spaces) or a real folder id there; a display name like
|
|
7
|
+
// "Sent Items" is rejected with "Id is malformed". The function is
|
|
8
|
+
// case-insensitive and space-insensitive over the well-known set, and passes
|
|
9
|
+
// anything it does not recognize through unchanged as a raw folder id.
|
|
10
|
+
test("sent-folder aliases all resolve to sentitems", () => {
|
|
11
|
+
for (const input of ["Sent Items", "SentItems", "sent items", "sent", "SENT ITEMS"]) {
|
|
12
|
+
assert.deepEqual(resolveFolder(input), { token: "sentitems", asRawId: false }, input);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
test("deleted-items aliases resolve to deleteditems", () => {
|
|
16
|
+
for (const input of ["Deleted Items", "deleteditems", "trash"]) {
|
|
17
|
+
assert.deepEqual(resolveFolder(input), { token: "deleteditems", asRawId: false }, input);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
test("junk aliases resolve to junkemail", () => {
|
|
21
|
+
for (const input of ["Junk Email", "junkemail", "junk", "spam"]) {
|
|
22
|
+
assert.deepEqual(resolveFolder(input), { token: "junkemail", asRawId: false }, input);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
test("inbox and empty string resolve to Inbox", () => {
|
|
26
|
+
assert.deepEqual(resolveFolder("Inbox"), { token: "Inbox", asRawId: false });
|
|
27
|
+
assert.deepEqual(resolveFolder(""), { token: "Inbox", asRawId: false });
|
|
28
|
+
assert.deepEqual(resolveFolder("inbox"), { token: "Inbox", asRawId: false });
|
|
29
|
+
});
|
|
30
|
+
test("the remaining well-known folders resolve to their tokens", () => {
|
|
31
|
+
assert.deepEqual(resolveFolder("Drafts"), { token: "drafts", asRawId: false });
|
|
32
|
+
assert.deepEqual(resolveFolder("Archive"), { token: "archive", asRawId: false });
|
|
33
|
+
assert.deepEqual(resolveFolder("Outbox"), { token: "outbox", asRawId: false });
|
|
34
|
+
});
|
|
35
|
+
test("an unrecognized value is treated as a raw folder id and passed through unchanged", () => {
|
|
36
|
+
const rawId = "AAMkAGI2TG93AAAAAAA=AQMkADAwATM3ZmYAZS1hNjQ3LWJmYjMtMDACLTAwCgAuAAADAAAA";
|
|
37
|
+
const out = resolveFolder(rawId);
|
|
38
|
+
assert.equal(out.asRawId, true);
|
|
39
|
+
assert.equal(out.token, rawId, "raw id must pass through byte-for-byte");
|
|
40
|
+
});
|
|
41
|
+
test("Object.prototype member names do not resolve — they pass through as raw ids", () => {
|
|
42
|
+
// A bare object-index lookup would read inherited members, so these would
|
|
43
|
+
// wrongly resolve to an Object.prototype value with asRawId=false.
|
|
44
|
+
for (const input of ["constructor", "__proto__", "toString", "hasOwnProperty", "valueOf"]) {
|
|
45
|
+
assert.deepEqual(resolveFolder(input), { token: input, asRawId: true }, input);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
test("a custom folder display name is not in the well-known set (out of scope) and passes through as a raw id", () => {
|
|
49
|
+
// "Projects" is a legitimate custom folder name, but resolving custom folders
|
|
50
|
+
// by display name needs a /me/mailFolders lookup and is out of scope. It is
|
|
51
|
+
// therefore treated as a raw id (and Graph will reject it) rather than
|
|
52
|
+
// silently mis-mapped to a well-known token.
|
|
53
|
+
assert.deepEqual(resolveFolder("Projects"), { token: "Projects", asRawId: true });
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=mail-list.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail-list.test.js","sourceRoot":"","sources":["../../src/__tests__/mail-list.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mailbox-list.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/mailbox-list.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { TokenStore } from "../auth/token-store.js";
|
|
7
|
+
import { runMailboxList } from "../tools/mailbox-list.js";
|
|
8
|
+
function makeAccountsDir() {
|
|
9
|
+
const dir = mkdtempSync(join(tmpdir(), "outlook-list-"));
|
|
10
|
+
return { dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
|
|
11
|
+
}
|
|
12
|
+
test("runMailboxList reports zero mailboxes for a fresh account", () => {
|
|
13
|
+
const { dir, cleanup } = makeAccountsDir();
|
|
14
|
+
try {
|
|
15
|
+
const result = runMailboxList("acct", dir);
|
|
16
|
+
assert.equal(result.count, 0);
|
|
17
|
+
assert.deepEqual(result.mailboxes, []);
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
cleanup();
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
test("runMailboxList lists connected mailboxes with identity and token health", () => {
|
|
24
|
+
const { dir, cleanup } = makeAccountsDir();
|
|
25
|
+
try {
|
|
26
|
+
new TokenStore("acct", dir, "rob-guid").store("a", "r", 3600, {
|
|
27
|
+
graphUserId: "rob-guid", mail: "rob@ecolecltd.co.uk", scopes: ["Mail.Read"],
|
|
28
|
+
});
|
|
29
|
+
new TokenStore("acct", dir, "info-guid").store("a", "r", 3600, {
|
|
30
|
+
graphUserId: "info-guid", mail: "info@ecolecltd.co.uk", scopes: ["Mail.Read"],
|
|
31
|
+
});
|
|
32
|
+
const result = runMailboxList("acct", dir);
|
|
33
|
+
assert.equal(result.count, 2);
|
|
34
|
+
const rob = result.mailboxes.find((m) => m.mailbox === "rob@ecolecltd.co.uk");
|
|
35
|
+
assert.ok(rob);
|
|
36
|
+
assert.equal(rob.graphUserId, "rob-guid");
|
|
37
|
+
assert.equal(rob.readable, true);
|
|
38
|
+
assert.deepEqual(rob.scopes, ["Mail.Read"]);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
cleanup();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
test("runMailboxList reconciles an unreadable blob rather than hiding it", () => {
|
|
45
|
+
const { dir, cleanup } = makeAccountsDir();
|
|
46
|
+
try {
|
|
47
|
+
new TokenStore("acct", dir, "good-guid").store("a", "r", 3600, {
|
|
48
|
+
graphUserId: "good-guid", mail: "good@x.com", scopes: [],
|
|
49
|
+
});
|
|
50
|
+
const badDir = join(dir, "acct", "secrets", "outlook", "mailboxes", "bad-guid");
|
|
51
|
+
mkdirSync(badDir, { recursive: true });
|
|
52
|
+
writeFileSync(join(badDir, "tokens.enc"), "garbage");
|
|
53
|
+
const result = runMailboxList("acct", dir);
|
|
54
|
+
assert.equal(result.count, 2);
|
|
55
|
+
const bad = result.mailboxes.find((m) => m.mailboxKey === "bad-guid");
|
|
56
|
+
assert.ok(bad);
|
|
57
|
+
assert.equal(bad.readable, false);
|
|
58
|
+
assert.equal(result.unreadable, 1);
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
cleanup();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=mailbox-list.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mailbox-list.test.js","sourceRoot":"","sources":["../../src/__tests__/mailbox-list.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mailbox-registry.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/mailbox-registry.test.ts"],"names":[],"mappings":""}
|