@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.
Files changed (84) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/neo4j/schema.cypher +50 -0
  3. package/payload/platform/package-lock.json +56 -1
  4. package/payload/platform/package.json +1 -0
  5. package/payload/platform/plugins/docs/references/outlook-guide.md +69 -0
  6. package/payload/platform/plugins/outlook/PLUGIN.md +48 -0
  7. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.d.ts +2 -0
  8. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.d.ts.map +1 -0
  9. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js +94 -0
  10. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js.map +1 -0
  11. package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.d.ts +2 -0
  12. package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.d.ts.map +1 -0
  13. package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.js +31 -0
  14. package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.js.map +1 -0
  15. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.d.ts +2 -0
  16. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.d.ts.map +1 -0
  17. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js +213 -0
  18. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js.map +1 -0
  19. package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.d.ts +2 -0
  20. package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.d.ts.map +1 -0
  21. package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js +130 -0
  22. package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js.map +1 -0
  23. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts +65 -0
  24. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts.map +1 -0
  25. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js +261 -0
  26. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js.map +1 -0
  27. package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts +61 -0
  28. package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts.map +1 -0
  29. package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js +170 -0
  30. package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js.map +1 -0
  31. package/payload/platform/plugins/outlook/mcp/dist/index.d.ts +18 -0
  32. package/payload/platform/plugins/outlook/mcp/dist/index.d.ts.map +1 -0
  33. package/payload/platform/plugins/outlook/mcp/dist/index.js +152 -0
  34. package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -0
  35. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +60 -0
  36. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -0
  37. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +189 -0
  38. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -0
  39. package/payload/platform/plugins/outlook/mcp/dist/lib/log.d.ts +23 -0
  40. package/payload/platform/plugins/outlook/mcp/dist/lib/log.d.ts.map +1 -0
  41. package/payload/platform/plugins/outlook/mcp/dist/lib/log.js +53 -0
  42. package/payload/platform/plugins/outlook/mcp/dist/lib/log.js.map +1 -0
  43. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts +26 -0
  44. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts.map +1 -0
  45. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js +50 -0
  46. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js.map +1 -0
  47. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.d.ts +12 -0
  48. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.d.ts.map +1 -0
  49. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.js +32 -0
  50. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.js.map +1 -0
  51. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.d.ts +59 -0
  52. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.d.ts.map +1 -0
  53. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.js +54 -0
  54. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.js.map +1 -0
  55. package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.d.ts +14 -0
  56. package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.d.ts.map +1 -0
  57. package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.js +45 -0
  58. package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.js.map +1 -0
  59. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts +15 -0
  60. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts.map +1 -0
  61. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js +48 -0
  62. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js.map +1 -0
  63. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts +8 -0
  64. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts.map +1 -0
  65. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js +49 -0
  66. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js.map +1 -0
  67. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts +19 -0
  68. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts.map +1 -0
  69. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js +58 -0
  70. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js.map +1 -0
  71. package/payload/platform/plugins/outlook/mcp/package.json +20 -0
  72. package/payload/platform/plugins/outlook/mcp/scripts/verify-doc-impl.sh +109 -0
  73. package/payload/platform/plugins/outlook/references/auth.md +118 -0
  74. package/payload/platform/plugins/outlook/references/graph-surfaces.md +114 -0
  75. package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +65 -0
  76. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  77. package/payload/server/chunk-EIQT6QDH.js +9562 -0
  78. package/payload/server/chunk-S3M2NZMA.js +3136 -0
  79. package/payload/server/chunk-SGBNY4NP.js +9540 -0
  80. package/payload/server/client-pool-5V5GX3UT.js +28 -0
  81. package/payload/server/maxy-edge.js +2 -2
  82. package/payload/server/public/assets/{admin-7vGwd7wu.js → admin-V6NDkEoR.js} +2 -2
  83. package/payload/server/public/index.html +1 -1
  84. package/payload/server/server.js +104 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy",
3
- "version": "1.0.762",
3
+ "version": "1.0.764",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy": "./dist/index.js"
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=graph-client.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=log.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pkce-flow.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkce-flow.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/pkce-flow.test.ts"],"names":[],"mappings":""}