@rubytech/create-maxy 1.0.762 → 1.0.764
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/neo4j/schema.cypher +50 -0
- package/payload/platform/package-lock.json +56 -1
- package/payload/platform/package.json +1 -0
- package/payload/platform/plugins/docs/references/outlook-guide.md +69 -0
- package/payload/platform/plugins/outlook/PLUGIN.md +48 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js +94 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.js +31 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js +213 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js +130 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts +65 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js +261 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts +61 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js +170 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.d.ts +18 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.js +152 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +60 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +189 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/log.d.ts +23 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/log.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/log.js +53 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/log.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts +26 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js +50 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.d.ts +12 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.js +32 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.d.ts +59 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.js +54 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.d.ts +14 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.js +45 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts +15 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js +48 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts +8 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js +49 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts +19 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js +58 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/package.json +20 -0
- package/payload/platform/plugins/outlook/mcp/scripts/verify-doc-impl.sh +109 -0
- package/payload/platform/plugins/outlook/references/auth.md +118 -0
- package/payload/platform/plugins/outlook/references/graph-surfaces.md +114 -0
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +65 -0
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/chunk-EIQT6QDH.js +9562 -0
- package/payload/server/chunk-S3M2NZMA.js +3136 -0
- package/payload/server/chunk-SGBNY4NP.js +9540 -0
- package/payload/server/client-pool-5V5GX3UT.js +28 -0
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/public/assets/{admin-7vGwd7wu.js → admin-V6NDkEoR.js} +2 -2
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +104 -6
package/package.json
CHANGED
|
@@ -802,6 +802,56 @@ FOR (tc:ToolCall) REQUIRE tc.callId IS UNIQUE;
|
|
|
802
802
|
CREATE INDEX tool_call_account_started IF NOT EXISTS
|
|
803
803
|
FOR (tc:ToolCall) ON (tc.accountId, tc.startedAt);
|
|
804
804
|
|
|
805
|
+
// ----------------------------------------------------------
|
|
806
|
+
// Component — Task 815 admin-resume rehydration.
|
|
807
|
+
//
|
|
808
|
+
// Every render-component event emitted on an assistant turn
|
|
809
|
+
// produces one Component node linked to its parent Message via
|
|
810
|
+
// (Message)-[:HAS_COMPONENT]->(Component). The agent's narrative
|
|
811
|
+
// text stays on Message.content; component name + data JSON +
|
|
812
|
+
// position-relative-to-text live here. Sibling-node design (not
|
|
813
|
+
// JSON-on-Message) avoids Neo4j's 128KB property soft-limit when
|
|
814
|
+
// document-editor bodies are large.
|
|
815
|
+
//
|
|
816
|
+
// Atomicity: persisted in the same Cypher transaction as the
|
|
817
|
+
// parent Message — no orphan Component possible. Reader is a
|
|
818
|
+
// single round-trip via OPTIONAL MATCH + collect (sort-before-
|
|
819
|
+
// collect: WITH m, c ORDER BY c.ordinal ASC, then collect).
|
|
820
|
+
//
|
|
821
|
+
// Properties:
|
|
822
|
+
// componentId — UUID, primary key
|
|
823
|
+
// conversationId / accountId / messageId — denormalised for
|
|
824
|
+
// account-scoped reads and cross-account isolation audits
|
|
825
|
+
// name — component name (document-editor, action-buttons,
|
|
826
|
+
// single-select, etc.)
|
|
827
|
+
// data — JSON-encoded payload string. Validated structurally
|
|
828
|
+
// on read (parseable, non-null object); per-component
|
|
829
|
+
// schema validation lives in validateComponentData.
|
|
830
|
+
// ordinal — 0-indexed sequence within the parent Message,
|
|
831
|
+
// tiebreak for components with the same textOffset.
|
|
832
|
+
// textOffset — length of Message.content at the moment this
|
|
833
|
+
// component arrived in the SSE stream. Reader uses
|
|
834
|
+
// it to interleave text-runs and components when
|
|
835
|
+
// reconstructing events: AdminEvent[].
|
|
836
|
+
// submitted — true once a PERSISTENT_COMPONENT (action-list,
|
|
837
|
+
// document-editor, rich-content-editor, grid-editor)
|
|
838
|
+
// was submitted by the user. Server-side detection
|
|
839
|
+
// of `_componentDone:true` user turns flips this.
|
|
840
|
+
// submittedAt — datetime when submitted=true was set (null otherwise)
|
|
841
|
+
// createdAt — datetime when the component was persisted
|
|
842
|
+
//
|
|
843
|
+
// (:Message)-[:HAS_COMPONENT]->(:Component)
|
|
844
|
+
// ----------------------------------------------------------
|
|
845
|
+
|
|
846
|
+
CREATE CONSTRAINT component_id_unique IF NOT EXISTS
|
|
847
|
+
FOR (c:Component) REQUIRE c.componentId IS UNIQUE;
|
|
848
|
+
|
|
849
|
+
CREATE INDEX component_message IF NOT EXISTS
|
|
850
|
+
FOR (c:Component) ON (c.messageId);
|
|
851
|
+
|
|
852
|
+
CREATE INDEX component_account IF NOT EXISTS
|
|
853
|
+
FOR (c:Component) ON (c.accountId);
|
|
854
|
+
|
|
805
855
|
// ----------------------------------------------------------
|
|
806
856
|
// :Trashed — Task 576 soft-delete primitive.
|
|
807
857
|
//
|
|
@@ -207,6 +207,10 @@
|
|
|
207
207
|
"resolved": "plugins/telegram/mcp",
|
|
208
208
|
"link": true
|
|
209
209
|
},
|
|
210
|
+
"node_modules/@maxy/outlook": {
|
|
211
|
+
"resolved": "plugins/outlook/mcp",
|
|
212
|
+
"link": true
|
|
213
|
+
},
|
|
210
214
|
"node_modules/@maxy/replicate": {
|
|
211
215
|
"resolved": "plugins/replicate/mcp",
|
|
212
216
|
"link": true
|
|
@@ -231,6 +235,33 @@
|
|
|
231
235
|
"resolved": "plugins/workflows/mcp",
|
|
232
236
|
"link": true
|
|
233
237
|
},
|
|
238
|
+
"node_modules/@microsoft/microsoft-graph-client": {
|
|
239
|
+
"version": "3.0.7",
|
|
240
|
+
"resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-client/-/microsoft-graph-client-3.0.7.tgz",
|
|
241
|
+
"integrity": "sha512-/AazAV/F+HK4LIywF9C+NYHcJo038zEnWkteilcxC1FM/uK/4NVGDKGrxx7nNq1ybspAroRKT4I1FHfxQzxkUw==",
|
|
242
|
+
"license": "MIT",
|
|
243
|
+
"dependencies": {
|
|
244
|
+
"@babel/runtime": "^7.12.5",
|
|
245
|
+
"tslib": "^2.2.0"
|
|
246
|
+
},
|
|
247
|
+
"engines": {
|
|
248
|
+
"node": ">=12.0.0"
|
|
249
|
+
},
|
|
250
|
+
"peerDependenciesMeta": {
|
|
251
|
+
"@azure/identity": {
|
|
252
|
+
"optional": true
|
|
253
|
+
},
|
|
254
|
+
"@azure/msal-browser": {
|
|
255
|
+
"optional": true
|
|
256
|
+
},
|
|
257
|
+
"buffer": {
|
|
258
|
+
"optional": true
|
|
259
|
+
},
|
|
260
|
+
"stream-browserify": {
|
|
261
|
+
"optional": true
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
},
|
|
234
265
|
"node_modules/@modelcontextprotocol/sdk": {
|
|
235
266
|
"version": "1.27.1",
|
|
236
267
|
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.27.1.tgz",
|
|
@@ -3485,11 +3516,34 @@
|
|
|
3485
3516
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
3486
3517
|
"neo4j-driver": "^5.28.1"
|
|
3487
3518
|
},
|
|
3519
|
+
"devDependencies": {
|
|
3520
|
+
"@types/node": "^22.0.0",
|
|
3521
|
+
"typescript": "^5.7.0",
|
|
3522
|
+
"vitest": "^4.1.2"
|
|
3523
|
+
}
|
|
3524
|
+
},
|
|
3525
|
+
"plugins/outlook/mcp": {
|
|
3526
|
+
"name": "@maxy/outlook",
|
|
3527
|
+
"version": "0.1.0",
|
|
3528
|
+
"dependencies": {
|
|
3529
|
+
"@microsoft/microsoft-graph-client": "^3.0.7",
|
|
3530
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
3531
|
+
"zod": "^3.24.0"
|
|
3532
|
+
},
|
|
3488
3533
|
"devDependencies": {
|
|
3489
3534
|
"@types/node": "^22.0.0",
|
|
3490
3535
|
"typescript": "^5.7.0"
|
|
3491
3536
|
}
|
|
3492
3537
|
},
|
|
3538
|
+
"plugins/outlook/mcp/node_modules/zod": {
|
|
3539
|
+
"version": "3.25.76",
|
|
3540
|
+
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
|
3541
|
+
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
|
3542
|
+
"license": "MIT",
|
|
3543
|
+
"funding": {
|
|
3544
|
+
"url": "https://github.com/sponsors/colinhacks"
|
|
3545
|
+
}
|
|
3546
|
+
},
|
|
3493
3547
|
"plugins/replicate/mcp": {
|
|
3494
3548
|
"name": "@maxy/replicate",
|
|
3495
3549
|
"version": "0.1.0",
|
|
@@ -3524,7 +3578,8 @@
|
|
|
3524
3578
|
},
|
|
3525
3579
|
"devDependencies": {
|
|
3526
3580
|
"@types/node": "^22.0.0",
|
|
3527
|
-
"typescript": "^5.7.0"
|
|
3581
|
+
"typescript": "^5.7.0",
|
|
3582
|
+
"vitest": "^4.1.2"
|
|
3528
3583
|
}
|
|
3529
3584
|
},
|
|
3530
3585
|
"plugins/scheduling/mcp/node_modules/zod": {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"build:cloudflare": "tsc -p plugins/cloudflare/mcp/tsconfig.json",
|
|
16
16
|
"build:tasks": "tsc -p plugins/tasks/mcp/tsconfig.json",
|
|
17
17
|
"build:email": "tsc -p plugins/email/mcp/tsconfig.json",
|
|
18
|
+
"build:outlook": "tsc -p plugins/outlook/mcp/tsconfig.json",
|
|
18
19
|
"build:workflows": "tsc -p plugins/workflows/mcp/tsconfig.json",
|
|
19
20
|
"build:stubs": "tsc -p plugins/scheduling/mcp/tsconfig.json",
|
|
20
21
|
"build:replicate": "tsc -p plugins/replicate/mcp/tsconfig.json"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Outlook Plugin — Operator Guide
|
|
2
|
+
|
|
3
|
+
The `outlook` plugin gives the admin agent read-only access to Microsoft 365 / Outlook.com via Microsoft Graph. Per-account OAuth (Auth Code + PKCE), encrypted token storage, automatic refresh.
|
|
4
|
+
|
|
5
|
+
## Quickstart
|
|
6
|
+
|
|
7
|
+
1. **Register an Entra app once per Maxy install** — see `platform/plugins/outlook/references/auth.md` for full steps. Set `OUTLOOK_CLIENT_ID` (and `OUTLOOK_TENANT_ID`, default `common`) in the operator's environment.
|
|
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 prints an authorization URL.
|
|
9
|
+
3. **Open the URL in the VNC browser** — sign in to your Microsoft account, consent to the requested scopes (`offline_access`, `User.Read`, `Mail.Read`, `Calendars.Read`, `Contacts.Read`).
|
|
10
|
+
4. **Done.** Subsequent tool calls (mail, calendar, contacts) use the persisted refresh token transparently.
|
|
11
|
+
|
|
12
|
+
## Tools
|
|
13
|
+
|
|
14
|
+
| Tool | Purpose |
|
|
15
|
+
|------|---------|
|
|
16
|
+
| `outlook-account-register` | Run the PKCE flow for this account. One-time per account; re-run if tokens expire (90 days) or consent is revoked. |
|
|
17
|
+
| `outlook-mail-list` | Recent mail. Default top=25, folder=Inbox. |
|
|
18
|
+
| `outlook-mail-search` | Microsoft Graph `$search` over the mailbox. |
|
|
19
|
+
| `outlook-calendar-list` | Calendar events in next rangeDays days (default 7, max 365). |
|
|
20
|
+
| `outlook-calendar-event` | Full detail of a single event by id. |
|
|
21
|
+
| `outlook-contacts-list` | Top contacts. Default top=50. |
|
|
22
|
+
| `outlook-mailbox-info` | Health probe — auth state, refresh-window, folder count. |
|
|
23
|
+
|
|
24
|
+
## Observability
|
|
25
|
+
|
|
26
|
+
All log lines start with `[outlook-mcp]` and write to `server.log`. They are key=value, account-scoped:
|
|
27
|
+
|
|
28
|
+
| Event | Line shape |
|
|
29
|
+
|-------|------------|
|
|
30
|
+
| Auth init | `auth-init account=<id> codeChallenge=<sha256-prefix-8> redirectPath=<callback-path>` |
|
|
31
|
+
| Auth callback | `auth-callback account=<id> elapsedMs=<N>` |
|
|
32
|
+
| Auth ok | `auth-ok account=<id> graphUserId=<id> scopes=<csv> tokenExpSec=<N>` |
|
|
33
|
+
| Token refreshed | `token-refreshed account=<id> oldExpSec=<N> newExpSec=<N>` |
|
|
34
|
+
| Refresh failed | `token-refresh-failed account=<id> reason=<err>` (terminal) |
|
|
35
|
+
| Mail list | `mail-list account=<id> folder=<id-or-Inbox> count=<N> elapsedMs=<N>` |
|
|
36
|
+
| Mail search | `mail-search account=<id> query=<trunc-32> count=<N> elapsedMs=<N>` |
|
|
37
|
+
| Calendar list | `calendar-list account=<id> rangeDays=<N> count=<N> elapsedMs=<N>` |
|
|
38
|
+
| Calendar event | `calendar-event account=<id> eventId=<trunc-12> elapsedMs=<N>` |
|
|
39
|
+
| Contacts list | `contacts-list account=<id> count=<N> elapsedMs=<N>` |
|
|
40
|
+
| Mailbox info | `mailbox-info account=<id> tokenWithinRefreshWindow=<bool> folderCount=<N>` |
|
|
41
|
+
| Graph error | `graph-error account=<id> status=<N> code=<graphErrorCode> retryAfterMs=<N-or-null>` |
|
|
42
|
+
| On-prem rejected | `on-prem-rejected account=<id> mailServer=<host>` (terminal) |
|
|
43
|
+
|
|
44
|
+
## Diagnostic paths
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# All outlook lines for one account, last 50
|
|
48
|
+
ssh laptop 'grep -E "^\[outlook-mcp\]" ~/.maxy/logs/server.log | grep "account=<id>" | tail -50'
|
|
49
|
+
|
|
50
|
+
# Token-leak audit — must always return zero
|
|
51
|
+
grep -rn -iE "Bearer |access_token=" ~/.maxy/logs/server.log | head
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Latency triage: `mail-list count=0 elapsedMs<200` consistent → permissions issue; `elapsedMs > 5000` → Graph slowness or DNS.
|
|
55
|
+
|
|
56
|
+
## Failure modes
|
|
57
|
+
|
|
58
|
+
| Operator-visible message | Cause | Fix |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `Outlook not connected for account=X; run outlook-account-register` | Tokens never saved | Run register tool |
|
|
61
|
+
| `Outlook refresh token expired for account=X; run outlook-account-register` | >90 days since last refresh, or consent revoked | Run register tool |
|
|
62
|
+
| `Outlook token refresh failed for account=X; re-auth required` | Network down at refresh time, or refresh token invalidated | Verify network; re-register |
|
|
63
|
+
| `Outlook auth expired for account=X; run outlook-account-register` | Refresh-then-retry still got 401 | Re-register |
|
|
64
|
+
| `Outlook rate-limited without Retry-After hint` | Graph 429 with no backoff guidance | Wait + retry; if persistent, file bug |
|
|
65
|
+
| `Microsoft Graph does not support on-premises Exchange. Use Task 769 (IMAP).` | Mailbox is on hybrid Exchange | Use the `email` plugin |
|
|
66
|
+
|
|
67
|
+
## Out of scope
|
|
68
|
+
|
|
69
|
+
Write tools (send, draft, move, flag), OneDrive / Files, push notifications, on-premises Exchange, M365 admin scopes (`User.Read.All`, `AuditLog.Read.All`), public-agent exposure, multi-tenant federation. See `platform/plugins/outlook/PLUGIN.md` for the full out-of-scope list.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: outlook
|
|
3
|
+
description: Microsoft 365 / Outlook.com via Microsoft Graph (read-only first cut). Per-account OAuth Auth Code + PKCE; no client secret. Tools — outlook-account-register: PKCE register; outlook-mail-list / outlook-mail-search: inbox; outlook-calendar-list / outlook-calendar-event: calendar; outlook-contacts-list: contacts; outlook-mailbox-info: auth state + folder count.
|
|
4
|
+
tools:
|
|
5
|
+
- outlook-account-register
|
|
6
|
+
- outlook-mail-list
|
|
7
|
+
- outlook-mail-search
|
|
8
|
+
- outlook-calendar-list
|
|
9
|
+
- outlook-calendar-event
|
|
10
|
+
- outlook-contacts-list
|
|
11
|
+
- outlook-mailbox-info
|
|
12
|
+
always: false
|
|
13
|
+
embed: ["admin"]
|
|
14
|
+
metadata: {"platform":{"optional":true}}
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Outlook
|
|
18
|
+
|
|
19
|
+
Read-only Microsoft Graph access for Outlook.com / Microsoft 365 mailboxes. Admin agent only — public agent surface is explicitly excluded.
|
|
20
|
+
|
|
21
|
+
## Capabilities
|
|
22
|
+
|
|
23
|
+
- **Register:** `outlook-account-register` — initiates OAuth Auth Code + PKCE flow against the operator's Entra app. Returns an authorization URL the operator opens in the VNC browser to consent. Tokens persist per-account, encrypted on disk (AES-256-CBC).
|
|
24
|
+
- **Mail (read-only):** `outlook-mail-list` returns recent messages; `outlook-mail-search` runs a Graph `$search` query.
|
|
25
|
+
- **Calendar (read-only):** `outlook-calendar-list` returns events in a future range; `outlook-calendar-event` returns a single event with full body + attendees.
|
|
26
|
+
- **Contacts (read-only):** `outlook-contacts-list` returns contacts.
|
|
27
|
+
- **Health:** `outlook-mailbox-info` reports auth state, refresh-window status, and top-level folder count. Use this to answer "did account X auth?" without grepping logs.
|
|
28
|
+
|
|
29
|
+
## Out of scope
|
|
30
|
+
|
|
31
|
+
- Send / draft / move / flag — write surfaces are deferred to a follow-up task.
|
|
32
|
+
- OneDrive, Files, Sites — separate plugin; different endpoints.
|
|
33
|
+
- Push notifications via `/subscriptions` — first cut polls.
|
|
34
|
+
- On-premises Exchange — Microsoft Graph deprecated hybrid REST 2023-07. The plugin detects on-prem mailboxes and routes the operator to the IMAP path (`email` plugin).
|
|
35
|
+
- M365 admin scopes (`User.Read.All`, `AuditLog.Read.All`) — separate plugin.
|
|
36
|
+
|
|
37
|
+
## References
|
|
38
|
+
|
|
39
|
+
| Reference | Topics |
|
|
40
|
+
|-----------|--------|
|
|
41
|
+
| [auth.md](references/auth.md) | Entra app registration, PKCE flow, vendored upstream SHA, re-vendor procedure |
|
|
42
|
+
| [graph-surfaces.md](references/graph-surfaces.md) | Graph endpoint and response shape per tool |
|
|
43
|
+
|
|
44
|
+
## Skills
|
|
45
|
+
|
|
46
|
+
| Skill | Purpose |
|
|
47
|
+
|-------|---------|
|
|
48
|
+
| [outlook](skills/outlook/SKILL.md) | When to activate; how to register a new Outlook account |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/graph-client.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { classifyGraphError } from "../lib/graph-client.js";
|
|
4
|
+
test("classifyGraphError 401 → auth", () => {
|
|
5
|
+
const cls = classifyGraphError({
|
|
6
|
+
statusCode: 401,
|
|
7
|
+
code: "InvalidAuthenticationToken",
|
|
8
|
+
body: '{"error":{"code":"InvalidAuthenticationToken"}}',
|
|
9
|
+
});
|
|
10
|
+
assert.equal(cls.kind, "auth");
|
|
11
|
+
assert.equal(cls.statusCode, 401);
|
|
12
|
+
});
|
|
13
|
+
test("classifyGraphError 429 with Retry-After → rate-limit-recoverable", () => {
|
|
14
|
+
const cls = classifyGraphError({
|
|
15
|
+
statusCode: 429,
|
|
16
|
+
headers: { "Retry-After": "30" },
|
|
17
|
+
});
|
|
18
|
+
assert.equal(cls.kind, "rate-limit-recoverable");
|
|
19
|
+
if (cls.kind === "rate-limit-recoverable") {
|
|
20
|
+
assert.equal(cls.retryAfterMs, 30000);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
test("classifyGraphError 429 lowercase Retry-After header still parsed", () => {
|
|
24
|
+
const cls = classifyGraphError({
|
|
25
|
+
statusCode: 429,
|
|
26
|
+
headers: { "retry-after": "5" },
|
|
27
|
+
});
|
|
28
|
+
assert.equal(cls.kind, "rate-limit-recoverable");
|
|
29
|
+
if (cls.kind === "rate-limit-recoverable") {
|
|
30
|
+
assert.equal(cls.retryAfterMs, 5000);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
test("classifyGraphError 429 without Retry-After → rate-limit-terminal", () => {
|
|
34
|
+
const cls = classifyGraphError({
|
|
35
|
+
statusCode: 429,
|
|
36
|
+
headers: {},
|
|
37
|
+
});
|
|
38
|
+
assert.equal(cls.kind, "rate-limit-terminal");
|
|
39
|
+
});
|
|
40
|
+
test("classifyGraphError 503 + MailboxNotEnabledForRESTAPI → on-prem", () => {
|
|
41
|
+
const cls = classifyGraphError({
|
|
42
|
+
statusCode: 503,
|
|
43
|
+
code: "MailboxNotEnabledForRESTAPI",
|
|
44
|
+
body: '{"error":{"code":"MailboxNotEnabledForRESTAPI","message":"REST API is not yet supported for this mailbox."}}',
|
|
45
|
+
headers: { "x-mailbox-server": "ex2019.acme.local" },
|
|
46
|
+
});
|
|
47
|
+
assert.equal(cls.kind, "on-prem");
|
|
48
|
+
if (cls.kind === "on-prem") {
|
|
49
|
+
assert.equal(cls.statusCode, 503);
|
|
50
|
+
assert.equal(cls.mailServer, "ex2019.acme.local");
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
test("classifyGraphError 503 without MailboxNotEnabledForRESTAPI → 5xx (NOT on-prem)", () => {
|
|
54
|
+
// Critical: on-prem detection keys off the structured code field, not body
|
|
55
|
+
// prose. A generic 503 must NOT be misclassified as on-prem.
|
|
56
|
+
const cls = classifyGraphError({
|
|
57
|
+
statusCode: 503,
|
|
58
|
+
code: "ServiceUnavailable",
|
|
59
|
+
body: '{"error":{"code":"ServiceUnavailable","message":"REST API on-premises mailboxes are temporarily unavailable for everyone, somehow"}}',
|
|
60
|
+
});
|
|
61
|
+
assert.equal(cls.kind, "5xx", "must not infer on-prem from prose body");
|
|
62
|
+
});
|
|
63
|
+
test("classifyGraphError parses code from body when not on top-level", () => {
|
|
64
|
+
const cls = classifyGraphError({
|
|
65
|
+
statusCode: 503,
|
|
66
|
+
body: '{"error":{"code":"MailboxNotEnabledForRESTAPI","message":"foo"}}',
|
|
67
|
+
});
|
|
68
|
+
assert.equal(cls.kind, "on-prem");
|
|
69
|
+
});
|
|
70
|
+
test("classifyGraphError 500 → 5xx", () => {
|
|
71
|
+
const cls = classifyGraphError({ statusCode: 500, code: "InternalServerError" });
|
|
72
|
+
assert.equal(cls.kind, "5xx");
|
|
73
|
+
});
|
|
74
|
+
test("classifyGraphError 404 → other", () => {
|
|
75
|
+
const cls = classifyGraphError({ statusCode: 404, code: "ItemNotFound" });
|
|
76
|
+
assert.equal(cls.kind, "other");
|
|
77
|
+
if (cls.kind === "other") {
|
|
78
|
+
assert.equal(cls.statusCode, 404);
|
|
79
|
+
assert.equal(cls.code, "ItemNotFound");
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
test("classifyGraphError reads Retry-After via fetch-style response.headers.get", () => {
|
|
83
|
+
const cls = classifyGraphError({
|
|
84
|
+
statusCode: 429,
|
|
85
|
+
response: {
|
|
86
|
+
headers: { get: (h) => (h.toLowerCase() === "retry-after" ? "10" : null) },
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
assert.equal(cls.kind, "rate-limit-recoverable");
|
|
90
|
+
if (cls.kind === "rate-limit-recoverable") {
|
|
91
|
+
assert.equal(cls.retryAfterMs, 10000);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
//# sourceMappingURL=graph-client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-client.test.js","sourceRoot":"","sources":["../../src/__tests__/graph-client.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/log.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { redact, trunc } from "../lib/log.js";
|
|
4
|
+
test("redact masks Bearer tokens", () => {
|
|
5
|
+
const input = "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.payload.signature";
|
|
6
|
+
assert.match(redact(input), /Bearer <REDACTED>/);
|
|
7
|
+
assert.doesNotMatch(redact(input), /eyJhbGciOiJIUzI1NiJ9/);
|
|
8
|
+
});
|
|
9
|
+
test("redact masks access_token query strings", () => {
|
|
10
|
+
const input = "https://login.microsoftonline.com/cb?access_token=abc123_secret&state=xyz";
|
|
11
|
+
const out = redact(input);
|
|
12
|
+
assert.match(out, /access_token=<REDACTED>/);
|
|
13
|
+
assert.doesNotMatch(out, /abc123_secret/);
|
|
14
|
+
// Other query params survive.
|
|
15
|
+
assert.match(out, /state=xyz/);
|
|
16
|
+
});
|
|
17
|
+
test("redact is a no-op on safe text", () => {
|
|
18
|
+
assert.equal(redact("user logged in account=abc count=5"), "user logged in account=abc count=5");
|
|
19
|
+
});
|
|
20
|
+
test("redact handles multiple Bearer tokens in one string", () => {
|
|
21
|
+
const input = "first Bearer aaaaa second Bearer bbbbb";
|
|
22
|
+
const out = redact(input);
|
|
23
|
+
assert.match(out, /first Bearer <REDACTED> second Bearer <REDACTED>/);
|
|
24
|
+
});
|
|
25
|
+
test("trunc preserves short values verbatim", () => {
|
|
26
|
+
assert.equal(trunc("short", 10), "short");
|
|
27
|
+
});
|
|
28
|
+
test("trunc shortens long values with ellipsis", () => {
|
|
29
|
+
assert.equal(trunc("abcdefghijklmnop", 10), "abcdefghi…");
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=log.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.test.js","sourceRoot":"","sources":["../../src/__tests__/log.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE9C,IAAI,CAAC,4BAA4B,EAAE,GAAG,EAAE;IACtC,MAAM,KAAK,GAAG,8DAA8D,CAAC;IAC7E,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,mBAAmB,CAAC,CAAC;IACjD,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,sBAAsB,CAAC,CAAC;AAC7D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACnD,MAAM,KAAK,GAAG,2EAA2E,CAAC;IAC1F,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;IAC7C,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAC1C,8BAA8B;IAC9B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;AACjC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC1C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,oCAAoC,CAAC,EAAE,oCAAoC,CAAC,CAAC;AACnG,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,qDAAqD,EAAE,GAAG,EAAE;IAC/D,MAAM,KAAK,GAAG,wCAAwC,CAAC;IACvD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,kDAAkD,CAAC,CAAC;AACxE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;IACjD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0CAA0C,EAAE,GAAG,EAAE;IACpD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,kBAAkB,EAAE,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC;AAC5D,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce-flow.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/pkce-flow.test.ts"],"names":[],"mappings":""}
|