@rubytech/create-maxy-code 0.1.451 → 0.1.453
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/dist/__tests__/preserve-house-cloudflare.test.js +81 -2
- package/dist/index.js +10 -1
- package/dist/preserve-house-cloudflare.js +49 -2
- package/package.json +1 -1
- package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js +24 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/__tests__/house-scoped-token.test.d.ts +2 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/house-scoped-token.test.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/house-scoped-token.test.js +48 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/house-scoped-token.test.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/remediate.test.js +6 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/remediate.test.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/bin/remediate-run.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/bin/remediate-run.js +14 -4
- package/payload/platform/lib/storage-broker/dist/bin/remediate-run.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts +1 -0
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/cf-exec.js +18 -0
- package/payload/platform/lib/storage-broker/dist/cf-exec.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/house-scoped-token.d.ts +9 -0
- package/payload/platform/lib/storage-broker/dist/house-scoped-token.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/house-scoped-token.js +60 -0
- package/payload/platform/lib/storage-broker/dist/house-scoped-token.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/index.d.ts +1 -0
- package/payload/platform/lib/storage-broker/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/index.js +1 -0
- package/payload/platform/lib/storage-broker/dist/index.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/remediate.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/remediate.js +14 -1
- package/payload/platform/lib/storage-broker/dist/remediate.js.map +1 -1
- package/payload/platform/lib/storage-broker/src/__tests__/cf-exec.test.ts +28 -1
- package/payload/platform/lib/storage-broker/src/__tests__/house-scoped-token.test.ts +48 -0
- package/payload/platform/lib/storage-broker/src/__tests__/remediate.test.ts +8 -0
- package/payload/platform/lib/storage-broker/src/bin/remediate-run.ts +17 -5
- package/payload/platform/lib/storage-broker/src/cf-exec.ts +22 -0
- package/payload/platform/lib/storage-broker/src/house-scoped-token.ts +72 -0
- package/payload/platform/lib/storage-broker/src/index.ts +1 -0
- package/payload/platform/lib/storage-broker/src/remediate.ts +14 -1
- package/payload/platform/plugins/admin/PLUGIN.md +1 -0
- package/payload/platform/plugins/admin/hooks/__tests__/quote-render-gate.test.sh +107 -0
- package/payload/platform/plugins/admin/hooks/quote-render-gate.sh +74 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +4 -4
- package/payload/platform/plugins/cloudflare/bin/__tests__/cf-token.test.sh +25 -1
- package/payload/platform/plugins/cloudflare/bin/cf-token.sh +9 -1
- package/payload/platform/plugins/cloudflare/references/api.md +3 -1
- package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +1 -1
- package/payload/platform/plugins/docs/references/outlook-guide.md +3 -3
- package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/outlook/PLUGIN.md +3 -3
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/complete-registration.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/complete-registration.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/complete-registration.test.js +131 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/complete-registration.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-paged.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-paged.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-paged.test.js +55 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-paged.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-list-paging.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-list-paging.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-list-paging.test.js +120 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-list-paging.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-search-paging.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-search-paging.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-search-paging.test.js +66 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-search-paging.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pending-scan.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pending-scan.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pending-scan.test.js +48 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pending-scan.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/complete-registration.d.ts +31 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/complete-registration.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/complete-registration.js +47 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/complete-registration.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pending-scan.d.ts +11 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pending-scan.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pending-scan.js +31 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pending-scan.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.js +13 -9
- package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +19 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +14 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/scripts/complete-registration.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/scripts/complete-registration.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/scripts/complete-registration.js +111 -0
- package/payload/platform/plugins/outlook/mcp/dist/scripts/complete-registration.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts +16 -1
- 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 +37 -7
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts +12 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js +16 -12
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js.map +1 -1
- package/payload/platform/plugins/outlook/references/auth.md +2 -0
- package/payload/platform/plugins/outlook/references/graph-surfaces.md +20 -9
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +7 -7
- package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/house-scoped-token.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/house-scoped-token.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/house-scoped-token.test.js +36 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/house-scoped-token.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/house-scoped-token.d.ts +9 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/house-scoped-token.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/house-scoped-token.js +51 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/house-scoped-token.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js +16 -2
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +16 -2
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
- package/payload/platform/scripts/lib/provision-account-dir.sh +6 -0
- package/payload/platform/scripts/smoke-boot-services.sh +1 -0
- package/payload/platform/scripts/voice-mirror-audit.sh +70 -0
- package/payload/platform/services/claude-session-manager/dist/config.d.ts +5 -0
- package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/config.js +7 -1
- package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts +15 -0
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js +38 -3
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.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 +21 -10
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +20 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/plugin-enum.d.ts +34 -0
- package/payload/platform/services/claude-session-manager/dist/plugin-enum.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/plugin-enum.js +66 -0
- package/payload/platform/services/claude-session-manager/dist/plugin-enum.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +8 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +7 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/server/{chunk-TTKYHVA7.js → chunk-SDYRKIYY.js} +70 -3
- package/payload/server/server.js +59 -10
- package/payload/server/{src-FBUXV2FV.js → src-554BYJMN.js} +5 -1
|
@@ -20,7 +20,7 @@ RETRIES="${CF_VERIFY_RETRIES:-10}"
|
|
|
20
20
|
die() { echo "[cf-token] error: $*" >&2; exit 1; }
|
|
21
21
|
|
|
22
22
|
scope="${1:-}"; secrets="${2:-}"
|
|
23
|
-
case "$scope" in pages|d1|dns|access) ;; *) die "unknown scope '${scope}'; use pages|d1|dns|access";; esac
|
|
23
|
+
case "$scope" in pages|d1|storage|calendar-d1|dns|access) ;; *) die "unknown scope '${scope}'; use pages|d1|storage|calendar-d1|dns|access";; esac
|
|
24
24
|
[ -n "$secrets" ] && [ -r "$secrets" ] || die "secrets file not readable: '${secrets}'"
|
|
25
25
|
|
|
26
26
|
# House-credential fallback (Task 1662). After the 1631 storage-isolation
|
|
@@ -76,6 +76,14 @@ case "$scope" in
|
|
|
76
76
|
pages|d1) suffix="pages-d1"; key="CF_PAGES_D1_TOKEN"; legacy="CF_PAGES_TOKEN"
|
|
77
77
|
# exact permission-group names separated by ';'
|
|
78
78
|
names="Pages Write;D1 Write"; res='{"com.cloudflare.api.account.'"$ACC"'":"*"}';;
|
|
79
|
+
storage) suffix="storage"; key="CF_STORAGE_TOKEN"
|
|
80
|
+
# Task 1670 — the storage broker mints one data token from the house
|
|
81
|
+
# minter carrying both D1 and R2 write. Account-scoped like pages/d1.
|
|
82
|
+
names="D1 Write;Workers R2 Storage Write"; res='{"com.cloudflare.api.account.'"$ACC"'":"*"}';;
|
|
83
|
+
calendar-d1) suffix="calendar-d1"; key="CF_CALENDAR_D1_TOKEN"
|
|
84
|
+
# Task 1670 — the booking calendar mints a D1-only token from the
|
|
85
|
+
# house minter (no R2; least privilege for reconcile/publish).
|
|
86
|
+
names="D1 Write"; res='{"com.cloudflare.api.account.'"$ACC"'":"*"}';;
|
|
79
87
|
dns) suffix="dns"; key="CF_DNS_TOKEN"
|
|
80
88
|
# zone-scoped: res is set after the master-prefix routing (nesting differs)
|
|
81
89
|
names="DNS Write"; zone_scoped=1;;
|
|
@@ -11,7 +11,7 @@ Canonical reference (always the source of truth for request shapes, never mirror
|
|
|
11
11
|
There are two distinct tokens in play. Keep them separate.
|
|
12
12
|
|
|
13
13
|
- **Master token** — fully-scoped, long-lived, provisioned **once** by the operator in the dashboard (see § Provisioning the master token, below). Broad enough to manage Pages, D1, DNS, **and** create API tokens. The agent reads it only to mint per-scope tokens. It is never passed to `wrangler`, never put on a command line that gets echoed, never printed.
|
|
14
|
-
- **Per-scope narrow token** — scoped to exactly one operation class (a single-zone DNS edit, a one-project Pages deploy, a D1 query), with a **deterministic name** (`<brand>-pages-d1`, `<brand>-dns`, `<brand>-access`). There is **one** such token per scope. The agent loads it from the secrets file; **if absent, it mints it once** from the master — correctly scoped, **no expiry — permanent and reused** — and persists it back to the secrets file. Thereafter it is loaded and reused, never re-minted. Exported into the environment for the one `wrangler` / API call; never echoed into chat, never written into a project tree.
|
|
14
|
+
- **Per-scope narrow token** — scoped to exactly one operation class (a single-zone DNS edit, a one-project Pages deploy, a D1 query), with a **deterministic name** (`<brand>-pages-d1`, `<brand>-dns`, `<brand>-access`, and the house-code data scopes `<brand>-storage` = D1+R2 and `<brand>-calendar-d1` = D1). There is **one** such token per scope. The agent loads it from the secrets file; **if absent, it mints it once** from the master — correctly scoped, **no expiry — permanent and reused** — and persists it back to the secrets file. Thereafter it is loaded and reused, never re-minted. Exported into the environment for the one `wrangler` / API call; never echoed into chat, never written into a project tree.
|
|
15
15
|
|
|
16
16
|
Why one-per-scope instead of one-per-operation: the operator provisions the master once; the agent provisions each narrow token once and reuses it, so the account never fills with throwaway tokens, no broad token is ever exported to a tool that could log it, and decay is handled structurally by the reconcile pass (§ Reconcile) rather than by per-token TTL. The narrow token persists alongside the master in the same `600`-mode secrets file but is **strictly narrower** than the master — so the binding "never export the master to `wrangler`" is preserved while the narrow token gains reuse.
|
|
17
17
|
|
|
@@ -162,6 +162,8 @@ No `expires_on`: the per-scope token is permanent and reused, so decay is the jo
|
|
|
162
162
|
|
|
163
163
|
**Per-scope token resources.** Each reused per-scope token carries the resource that matches its operation class. The Pages-and-D1 token (`<brand>-pages-d1`) and the Access token (`<brand>-access`) are account-scoped: `"resources": { "com.cloudflare.api.account.${CLOUDFLARE_ACCOUNT_ID}": "*" }`. The DNS token (`<brand>-dns`) is zone-scoped, and since it is a single stable token reused across every zone the install routes, its resource covers the account's zones — but the exact shape depends on the master's prefix (see § Token type — route endpoints by prefix): a `cfut_…` master takes the flat `"resources": { "com.cloudflare.api.account.zone.*": "*" }`, while a `cfat_…` master requires the zone nested under the account, `"resources": { "com.cloudflare.api.account.${CLOUDFLARE_ACCOUNT_ID}": { "com.cloudflare.api.account.zone.*": "*" } }` (the flat shape returns `1001` on an account-owned master). A per-zone DNS token would defeat the one-stable-token-per-scope model (one token, reused, not one per zone); the minted token is still strictly narrower than the master, which already carries Zone DNS Edit over the zones in scope.
|
|
164
164
|
|
|
165
|
+
**House-code data scopes.** The house credential in `cloudflare-house.env` is a **minter**, not a data token, on **every** install, not only multi-tenant ones: the booking calendar and storage broker read it house-only fleet-wide (no per-account fallback), and mint their own account-scoped data tokens from that minter rather than use it directly (a minter returns `10000` on D1/R2). The multi-tenant install adds isolation on top of this same file (§ Multi-tenant installs); the single-tenant install simply reads its calendar/broker credential here. The scopes are — `<brand>-storage` (`D1 Write` + `Workers R2 Storage Write`, key `CF_STORAGE_TOKEN`) and `<brand>-calendar-d1` (`D1 Write`, key `CF_CALENDAR_D1_TOKEN`) — persisted house-side in `cloudflare-house.env` and reused. This is the same mint-or-reuse `cf-token.sh` machinery the hosting scopes use; the only difference is the consumer is trusted house code rather than the agent.
|
|
166
|
+
|
|
165
167
|
**Redaction is binding.** Every example above pipes the token into a variable or an `Authorization` header and never to stdout. The agent surfaces the operation as verb + target — "minting a Pages-deploy token", "creating CNAME `chat.example.com`" — never the secret. A failure surfaces the API error body with any `Authorization` value masked.
|
|
166
168
|
|
|
167
169
|
### A freshly-minted token is not instantly usable — verify with backoff
|
|
@@ -29,7 +29,7 @@ This is the entry point for every Cloudflare task on the install. Pick the opera
|
|
|
29
29
|
|
|
30
30
|
Neither the master token nor any per-scope token is ever written into a project tree, committed, or echoed into chat — the per-scope tokens persist only in the account-scoped secrets file, never in a deployable project tree.
|
|
31
31
|
|
|
32
|
-
On a multi-client install where client sub-accounts share one Cloudflare account, the master lives house-only at `config/cloudflare-house.env` and sub-account D1/R2 data work goes through the storage broker, not raw `wrangler`. This skill's tunnel operations are per-brand isolated and unchanged. The admin-run scopes (Pages, DNS, Access) stay admin/house-run: because the per-account secrets file no longer carries the master, `cf-token.sh` resolves and mints those scope tokens against the house credential (`config/cloudflare-house.env`) and persists them there, surfacing `src=house` on its `[cf-token]` lifeline. Hosting a client site to Pages is therefore an admin/house action, never a sub-account one, consistent with the isolation posture. See `references/api.md` and `.docs/cloudflare-storage-isolation.md`.
|
|
32
|
+
On a multi-client install where client sub-accounts share one Cloudflare account, the master lives house-only at `config/cloudflare-house.env` and sub-account D1/R2 data work goes through the storage broker, not raw `wrangler`. This skill's tunnel operations are per-brand isolated and unchanged. The admin-run scopes (Pages, DNS, Access) stay admin/house-run: because the per-account secrets file no longer carries the master, `cf-token.sh` resolves and mints those scope tokens against the house credential (`config/cloudflare-house.env`) and persists them there, surfacing `src=house` on its `[cf-token]` lifeline. Hosting a client site to Pages is therefore an admin/house action, never a sub-account one, consistent with the isolation posture. The house credential in `config/cloudflare-house.env` is a **minter**, not a data token, on **every** install, not only multi-tenant ones: the booking calendar and storage broker read it house-only fleet-wide (no per-account fallback), so they mint their own account-scoped data tokens from that minter — `CF_STORAGE_TOKEN` (D1 + R2) and `CF_CALENDAR_D1_TOKEN` (D1) — rather than use the minter directly, which returns `10000` on D1/R2. Multi-tenant installs add per-account isolation on top of this same file; single-tenant installs simply read their calendar/broker credential here. See `references/api.md` and `.docs/cloudflare-storage-isolation.md`.
|
|
33
33
|
|
|
34
34
|
### Resolve the per-scope token before any `wrangler` / API call (binding)
|
|
35
35
|
|
|
@@ -6,7 +6,7 @@ The `outlook` plugin gives the admin agent access to Microsoft 365 / Outlook.com
|
|
|
6
6
|
|
|
7
7
|
1. **No app to register.** The client ID ships with the brand (`brand.json#outlookClientId`), and the installer stamps it onto the session-manager env, so there is zero operator Azure work. On a brand that ships an empty value the plugin stays inert until its vendor app exists — see `platform/plugins/outlook/references/auth.md` for the one-time-per-brand vendor provisioning runbook (a vendor step, not an operator or end-user step), including setting **Allow public client flows** to Yes.
|
|
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
|
-
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.
|
|
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. Nothing else to do: the server finishes the sign-in automatically once you consent, usually within about half a minute, and the mailbox is then connected.
|
|
10
10
|
4. **Done.** Subsequent tool calls (mail, calendar, contacts) use the persisted refresh token transparently.
|
|
11
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.
|
|
12
12
|
|
|
@@ -16,8 +16,8 @@ The `outlook` plugin gives the admin agent access to Microsoft 365 / Outlook.com
|
|
|
16
16
|
|------|---------|
|
|
17
17
|
| `outlook-account-register` | Start the device-code flow for this account. Returns a user code + `microsoft.com/devicelogin` URL immediately; does not block. |
|
|
18
18
|
| `outlook-account-register-poll` | Finish the device-code flow. One check per call: returns pending, registered, expired, or denied. |
|
|
19
|
-
| `outlook-mail-list` |
|
|
20
|
-
| `outlook-mail-search` | Microsoft Graph `$search` over the mailbox. Preview-only, like `outlook-mail-list`. |
|
|
19
|
+
| `outlook-mail-list` | Mail newest-first. 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. `since`/`before` (ISO date-times) narrow by receipt date. The 250-per-page figure is not a ceiling: the reply carries `nextCursor` when older mail remains — pass it back as `cursor` to page further back. Filter by sender/subject via `outlook-mail-search`. |
|
|
20
|
+
| `outlook-mail-search` | Microsoft Graph `$search` (KQL) over the mailbox. Preview-only, like `outlook-mail-list`, and pages the same way via `nextCursor`/`cursor`. Put sender/subject/recipient/date filters in the query: `from:`, `to:`, `subject:`, `received>=`. Graph forbids `$search`+`$filter`, so a strict date window is on `outlook-mail-list`. |
|
|
21
21
|
| `outlook-mail-fetch-body` | Complete body of one message by id (`GET /me/messages/{id}?$select=body`). HTML decoded to text, no preview cap. The full-read path — list/search return Microsoft's ~255-char preview only. Returns the envelope plus the whole body. |
|
|
22
22
|
| `outlook-mail-reply` | Reply in-thread (`createReply` / `createReplyAll` + send). Threads natively via conversationId. `replyAll`, added cc/bcc, and account-scoped file attachments (up to 25 MB per file; files over Graph's ~3 MB inline limit use a chunked upload session). Body is plain text, prepended above the quote. Needs `Mail.ReadWrite` + `Mail.Send`. |
|
|
23
23
|
| `outlook-mail-delete` | Move messages to Deleted Items (`POST /me/messages/{id}/move`). Recoverable — never hard-deletes. An id already gone is counted not-moved. Needs `Mail.ReadWrite`. |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "outlook",
|
|
3
|
-
"description": "Microsoft 365 / Outlook.com via Microsoft Graph. Mail is read (list, search, full-body), reply, delete, and compose (draft, draft-edit, 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 triage (~255-char preview); outlook-mail-fetch-body: full body of one message by id; outlook-mail-reply: in-thread reply (replyAll, cc/bcc, attachments); outlook-mail-delete: move to Deleted Items (recoverable); outlook-mail-otp-extract: poll for a one-time code; outlook-mail-send / outlook-draft / outlook-draft-edit / outlook-draft-send: compose, edit, 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.",
|
|
3
|
+
"description": "Microsoft 365 / Outlook.com via Microsoft Graph. Mail is read (list, search, full-body), reply, delete, and compose (draft, draft-edit, 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 triage (~255-char preview) with a date window (list) or KQL from:/subject: filters (search) and a nextCursor for paging beyond the newest page; outlook-mail-fetch-body: full body of one message by id; outlook-mail-reply: in-thread reply (replyAll, cc/bcc, attachments); outlook-mail-delete: move to Deleted Items (recoverable); outlook-mail-otp-extract: poll for a one-time code; outlook-mail-send / outlook-draft / outlook-draft-edit / outlook-draft-send: compose, edit, 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 (list, search, full-body), reply, delete, and compose (draft, draft-edit, 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 triage (~255-char preview); outlook-mail-fetch-body: full body of one message by id; outlook-mail-reply: in-thread reply (replyAll, cc/bcc, attachments); outlook-mail-delete: move to Deleted Items (recoverable); outlook-mail-otp-extract: poll for a one-time code; outlook-mail-send / outlook-draft / outlook-draft-edit / outlook-draft-send: compose, edit, 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.
|
|
3
|
+
description: Microsoft 365 / Outlook.com via Microsoft Graph. Mail is read (list, search, full-body), reply, delete, and compose (draft, draft-edit, 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 triage (~255-char preview) with a date window (list) or KQL from:/subject: filters (search) and a nextCursor for paging beyond the newest page; outlook-mail-fetch-body: full body of one message by id; outlook-mail-reply: in-thread reply (replyAll, cc/bcc, attachments); outlook-mail-delete: move to Deleted Items (recoverable); outlook-mail-otp-extract: poll for a one-time code; outlook-mail-send / outlook-draft / outlook-draft-edit / outlook-draft-send: compose, edit, 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
|
|
@@ -93,9 +93,9 @@ Microsoft Graph access for Outlook.com / Microsoft 365 mailboxes. Mail is read (
|
|
|
93
93
|
|
|
94
94
|
## Capabilities
|
|
95
95
|
|
|
96
|
-
- **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`
|
|
96
|
+
- **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. The always-running server then completes registration on its own: a self-contained dispatcher (`dist/scripts/complete-registration.js`), armed on a 30 s interval, polls every live pending code to a terminal outcome, so a mailbox connects once the operator consents with no agent action. `outlook-account-register-poll` remains the manual path and the fallback, 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. Observability: `devicecode-autopoll` per poll cycle and an independent `devicecode-stranded` audit for any pending code left un-harvested past its lifetime.
|
|
97
97
|
- **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.
|
|
98
|
-
- **Mail (read):** `outlook-mail-list` returns
|
|
98
|
+
- **Mail (read):** `outlook-mail-list` returns messages newest-first with an optional `since`/`before` date window, and `outlook-mail-search` runs a Graph `$search` (KQL) query where `from:`/`to:`/`subject:`/`received>=` filters live — both return Microsoft's short `bodyPreview` (~255 chars) for cheap triage and a `nextCursor` for paging beyond the 250-per-page window (the cursor is an opaque `@odata.nextLink`; pass it back as `cursor`). Graph forbids `$search`+`$filter`, so the strict date window is on the list tool and sender/subject filtering is on the search tool. `outlook-mail-fetch-body` reads the COMPLETE body of one message by id (HTML decoded to text, no preview cap) — the full-read path.
|
|
99
99
|
- **Mail (reply / delete):** `outlook-mail-reply` replies in-thread (native `conversationId` threading) with `replyAll`, added cc/bcc, and account-scoped file attachments (up to 25 MB per file — files over Graph's ~3 MB inline limit stream through a chunked upload session); the body is prepended above the quoted original. `outlook-mail-delete` moves messages to Deleted Items (recoverable — never hard-deletes). Both require `Mail.ReadWrite` (reply also needs `Mail.Send`).
|
|
100
100
|
- **Mail (OTP):** `outlook-mail-otp-extract` polls the inbox for a one-time verification code from a named sender, reading full bodies to extract it — used during service authentication.
|
|
101
101
|
- **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-edit` updates an existing draft in place (Graph drafts are mutable); `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.
|
package/payload/platform/plugins/outlook/mcp/dist/__tests__/complete-registration.test.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complete-registration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/complete-registration.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { PendingStore } from "../auth/pending-store.js";
|
|
7
|
+
import { MailboxRegistry } from "../auth/mailbox-registry.js";
|
|
8
|
+
import { pollRegistrationToTerminal } from "../auth/complete-registration.js";
|
|
9
|
+
const mk = () => {
|
|
10
|
+
const dir = mkdtempSync(join(tmpdir(), "outlook-complete-"));
|
|
11
|
+
return { dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
|
|
12
|
+
};
|
|
13
|
+
function withFetch(seq) {
|
|
14
|
+
const original = globalThis.fetch;
|
|
15
|
+
let i = 0;
|
|
16
|
+
globalThis.fetch = (async () => seq[Math.min(i++, seq.length - 1)]());
|
|
17
|
+
return () => {
|
|
18
|
+
globalThis.fetch = original;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function captureStderr() {
|
|
22
|
+
const chunks = [];
|
|
23
|
+
const orig = process.stderr.write.bind(process.stderr);
|
|
24
|
+
process.stderr.write = (s) => {
|
|
25
|
+
chunks.push(String(s));
|
|
26
|
+
return true;
|
|
27
|
+
};
|
|
28
|
+
return { text: () => chunks.join(""), restore: () => { process.stderr.write = orig; } };
|
|
29
|
+
}
|
|
30
|
+
const json = (o, status = 200) => new Response(JSON.stringify(o), { status, headers: { "Content-Type": "application/json" } });
|
|
31
|
+
const cfg = { clientId: "cid", tenantId: "common" };
|
|
32
|
+
const pendingEntry = () => ({
|
|
33
|
+
deviceCode: "DEV",
|
|
34
|
+
scopes: ["Mail.Read"],
|
|
35
|
+
intervalSec: 5,
|
|
36
|
+
expiresAtMs: Date.now() + 900_000,
|
|
37
|
+
issuedAtMs: Date.now(),
|
|
38
|
+
});
|
|
39
|
+
const noSleep = async () => { };
|
|
40
|
+
test("polls to registered with no manual poll call and logs devicecode-autopoll", async () => {
|
|
41
|
+
const { dir, cleanup } = mk();
|
|
42
|
+
const ps = new PendingStore("acct-1", dir);
|
|
43
|
+
ps.write(pendingEntry());
|
|
44
|
+
const reg = new MailboxRegistry("acct-1", dir);
|
|
45
|
+
const restore = withFetch([
|
|
46
|
+
() => json({ error: "authorization_pending" }, 400), // one wait...
|
|
47
|
+
() => json({ access_token: "AT", refresh_token: "RT", expires_in: 3600, token_type: "Bearer", scope: "Mail.Read" }),
|
|
48
|
+
() => json({ id: "gid-1", mail: "info@ecolecltd.co.uk" }), // /me
|
|
49
|
+
]);
|
|
50
|
+
const cap = captureStderr();
|
|
51
|
+
try {
|
|
52
|
+
const res = await pollRegistrationToTerminal({ accountId: "acct-1", ...cfg, registry: reg, pendingStore: ps, sleep: noSleep });
|
|
53
|
+
cap.restore();
|
|
54
|
+
assert.equal(res.status, "registered");
|
|
55
|
+
assert.equal(ps.read(), null, "pending cleared on completion");
|
|
56
|
+
assert.equal(reg.storeFor("gid-1").read()?.accessToken, "AT");
|
|
57
|
+
assert.match(cap.text(), /devicecode-autopoll account=acct-1/);
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
cap.restore();
|
|
61
|
+
restore();
|
|
62
|
+
cleanup();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
test("expired_token terminates as expired with pending cleared", async () => {
|
|
66
|
+
const { dir, cleanup } = mk();
|
|
67
|
+
const ps = new PendingStore("acct-2", dir);
|
|
68
|
+
ps.write(pendingEntry());
|
|
69
|
+
const restore = withFetch([() => json({ error: "expired_token" }, 400)]);
|
|
70
|
+
try {
|
|
71
|
+
const res = await pollRegistrationToTerminal({ accountId: "acct-2", ...cfg, registry: new MailboxRegistry("acct-2", dir), pendingStore: ps, sleep: noSleep });
|
|
72
|
+
assert.equal(res.status, "expired");
|
|
73
|
+
assert.equal(ps.census(), 0);
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
restore();
|
|
77
|
+
cleanup();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
test("access_denied terminates as denied with pending cleared", async () => {
|
|
81
|
+
const { dir, cleanup } = mk();
|
|
82
|
+
const ps = new PendingStore("acct-3", dir);
|
|
83
|
+
ps.write(pendingEntry());
|
|
84
|
+
const restore = withFetch([() => json({ error: "access_denied" }, 400)]);
|
|
85
|
+
try {
|
|
86
|
+
const res = await pollRegistrationToTerminal({ accountId: "acct-3", ...cfg, registry: new MailboxRegistry("acct-3", dir), pendingStore: ps, sleep: noSleep });
|
|
87
|
+
assert.equal(res.status, "denied");
|
|
88
|
+
assert.equal(ps.census(), 0);
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
restore();
|
|
92
|
+
cleanup();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
test("slow_down bumps the wait interval before the next poll", async () => {
|
|
96
|
+
const { dir, cleanup } = mk();
|
|
97
|
+
const ps = new PendingStore("acct-4", dir);
|
|
98
|
+
ps.write(pendingEntry()); // intervalSec 5
|
|
99
|
+
const sleeps = [];
|
|
100
|
+
const restore = withFetch([
|
|
101
|
+
() => json({ error: "slow_down" }, 400), // bumps stored interval 5 -> 10
|
|
102
|
+
() => json({ error: "authorization_pending" }, 400),
|
|
103
|
+
() => json({ access_token: "AT", refresh_token: "RT", expires_in: 3600, token_type: "Bearer", scope: "Mail.Read" }),
|
|
104
|
+
() => json({ id: "gid-4", mail: "a@b.com" }),
|
|
105
|
+
]);
|
|
106
|
+
try {
|
|
107
|
+
await pollRegistrationToTerminal({
|
|
108
|
+
accountId: "acct-4", ...cfg, registry: new MailboxRegistry("acct-4", dir), pendingStore: ps,
|
|
109
|
+
sleep: async (ms) => { sleeps.push(ms); },
|
|
110
|
+
});
|
|
111
|
+
assert.equal(sleeps[0], 10_000, "slow_down must bump the first wait from 5s to 10s");
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
restore();
|
|
115
|
+
cleanup();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
test("no pending entry terminates as expired without throwing", async () => {
|
|
119
|
+
const { dir, cleanup } = mk();
|
|
120
|
+
try {
|
|
121
|
+
const res = await pollRegistrationToTerminal({
|
|
122
|
+
accountId: "acct-5", ...cfg, registry: new MailboxRegistry("acct-5", dir),
|
|
123
|
+
pendingStore: new PendingStore("acct-5", dir), sleep: noSleep,
|
|
124
|
+
});
|
|
125
|
+
assert.equal(res.status, "expired");
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
cleanup();
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
//# sourceMappingURL=complete-registration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complete-registration.test.js","sourceRoot":"","sources":["../../src/__tests__/complete-registration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-client-paged.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/graph-client-paged.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync } 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 { callGraphPaged } from "../lib/graph-client.js";
|
|
8
|
+
function configWithToken(accountId) {
|
|
9
|
+
const dir = mkdtempSync(join(tmpdir(), "outlook-paged-"));
|
|
10
|
+
const tokenStore = new TokenStore(accountId, dir);
|
|
11
|
+
tokenStore.store("access-token", "refresh-token", 3600, { scopes: ["Mail.Read"] });
|
|
12
|
+
return { clientId: "test-client", tenantId: "common", accountId, tokenStore };
|
|
13
|
+
}
|
|
14
|
+
function mockGraph(status, bodyObj) {
|
|
15
|
+
const calls = [];
|
|
16
|
+
const original = globalThis.fetch;
|
|
17
|
+
globalThis.fetch = (async (input, init) => {
|
|
18
|
+
const req = input instanceof Request ? input : null;
|
|
19
|
+
const url = req ? req.url : typeof input === "string" ? input : input.toString();
|
|
20
|
+
if (!url.includes("graph.microsoft.com"))
|
|
21
|
+
return original(input, init);
|
|
22
|
+
calls.push(url);
|
|
23
|
+
const payload = bodyObj === null ? null : JSON.stringify(bodyObj);
|
|
24
|
+
const headers = bodyObj === null ? undefined : { "content-type": "application/json" };
|
|
25
|
+
return new Response(payload, { status, headers });
|
|
26
|
+
});
|
|
27
|
+
return { restore: () => { globalThis.fetch = original; }, calls };
|
|
28
|
+
}
|
|
29
|
+
test("callGraphPaged returns value and nextLink when present", async () => {
|
|
30
|
+
const mock = mockGraph(200, {
|
|
31
|
+
value: [{ id: "A" }, { id: "B" }],
|
|
32
|
+
"@odata.nextLink": "https://graph.microsoft.com/v1.0/me/messages?$skiptoken=XYZ",
|
|
33
|
+
});
|
|
34
|
+
try {
|
|
35
|
+
const page = await callGraphPaged(configWithToken("acct-p1"), (client) => client.api("/me/messages").top(2), { tool: "test" });
|
|
36
|
+
assert.deepEqual(page.value, [{ id: "A" }, { id: "B" }]);
|
|
37
|
+
assert.equal(page.nextLink, "https://graph.microsoft.com/v1.0/me/messages?$skiptoken=XYZ");
|
|
38
|
+
assert.equal(mock.calls.length, 1);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
mock.restore();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
test("callGraphPaged returns nextLink null and empty value defaults", async () => {
|
|
45
|
+
const mock = mockGraph(200, {});
|
|
46
|
+
try {
|
|
47
|
+
const page = await callGraphPaged(configWithToken("acct-p2"), (client) => client.api("/me/messages"), { tool: "test" });
|
|
48
|
+
assert.deepEqual(page.value, []);
|
|
49
|
+
assert.equal(page.nextLink, null);
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
mock.restore();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=graph-client-paged.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-client-paged.test.js","sourceRoot":"","sources":["../../src/__tests__/graph-client-paged.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail-list-paging.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/mail-list-paging.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync } 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 { runMailList } from "../tools/mail-list.js";
|
|
8
|
+
function configWithToken(accountId) {
|
|
9
|
+
const dir = mkdtempSync(join(tmpdir(), "outlook-mllist-"));
|
|
10
|
+
const tokenStore = new TokenStore(accountId, dir);
|
|
11
|
+
tokenStore.store("access-token", "refresh-token", 3600, { scopes: ["Mail.Read"] });
|
|
12
|
+
return { clientId: "test-client", tenantId: "common", accountId, tokenStore };
|
|
13
|
+
}
|
|
14
|
+
function mockGraph(bodyObj) {
|
|
15
|
+
const urls = [];
|
|
16
|
+
const original = globalThis.fetch;
|
|
17
|
+
globalThis.fetch = (async (input, init) => {
|
|
18
|
+
const req = input instanceof Request ? input : null;
|
|
19
|
+
const url = req ? req.url : typeof input === "string" ? input : input.toString();
|
|
20
|
+
if (!url.includes("graph.microsoft.com"))
|
|
21
|
+
return original(input, init);
|
|
22
|
+
urls.push(url);
|
|
23
|
+
return new Response(JSON.stringify(bodyObj), { status: 200, headers: { "content-type": "application/json" } });
|
|
24
|
+
});
|
|
25
|
+
return { restore: () => { globalThis.fetch = original; }, urls };
|
|
26
|
+
}
|
|
27
|
+
const EMPTY = { value: [] };
|
|
28
|
+
test("since builds a receivedDateTime ge filter", async () => {
|
|
29
|
+
const mock = mockGraph(EMPTY);
|
|
30
|
+
try {
|
|
31
|
+
await runMailList(configWithToken("a1"), { since: "2024-01-01" });
|
|
32
|
+
const filter = new URL(mock.urls[0]).searchParams.get("$filter");
|
|
33
|
+
assert.equal(filter, "receivedDateTime ge 2024-01-01T00:00:00.000Z");
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
mock.restore();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
test("since and before AND-combine into one filter", async () => {
|
|
40
|
+
const mock = mockGraph(EMPTY);
|
|
41
|
+
try {
|
|
42
|
+
await runMailList(configWithToken("a2"), { since: "2024-01-01", before: "2024-02-01" });
|
|
43
|
+
const filter = new URL(mock.urls[0]).searchParams.get("$filter");
|
|
44
|
+
assert.equal(filter, "receivedDateTime ge 2024-01-01T00:00:00.000Z and receivedDateTime le 2024-02-01T00:00:00.000Z");
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
mock.restore();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
test("no date args -> no $filter (unchanged behaviour)", async () => {
|
|
51
|
+
const mock = mockGraph(EMPTY);
|
|
52
|
+
try {
|
|
53
|
+
const r = await runMailList(configWithToken("a3"), {});
|
|
54
|
+
assert.equal(new URL(mock.urls[0]).searchParams.get("$filter"), null);
|
|
55
|
+
assert.equal(r.nextCursor, null);
|
|
56
|
+
assert.deepEqual(r.items, []);
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
mock.restore();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
test("nextCursor surfaced from @odata.nextLink", async () => {
|
|
63
|
+
const link = "https://graph.microsoft.com/v1.0/me/mailFolders/Inbox/messages?$skiptoken=ABC";
|
|
64
|
+
const mock = mockGraph({ value: [], "@odata.nextLink": link });
|
|
65
|
+
try {
|
|
66
|
+
const r = await runMailList(configWithToken("a4"), {});
|
|
67
|
+
assert.equal(r.nextCursor, link);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
mock.restore();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
test("cursor delegates to the nextLink URL and builds no filter", async () => {
|
|
74
|
+
const cursor = "https://graph.microsoft.com/v1.0/me/mailFolders/Inbox/messages?$skiptoken=ABC";
|
|
75
|
+
const mock = mockGraph(EMPTY);
|
|
76
|
+
try {
|
|
77
|
+
await runMailList(configWithToken("a5"), { cursor, since: "2024-01-01" });
|
|
78
|
+
assert.equal(mock.urls.length, 1);
|
|
79
|
+
assert.match(mock.urls[0], /\$skiptoken=ABC/);
|
|
80
|
+
assert.equal(new URL(mock.urls[0]).searchParams.get("$filter"), null, "cursor path must not add a filter");
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
mock.restore();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
test("unparseable since throws before any Graph call", async () => {
|
|
87
|
+
const mock = mockGraph(EMPTY);
|
|
88
|
+
try {
|
|
89
|
+
await assert.rejects(runMailList(configWithToken("a6"), { since: "not-a-date" }), /not a valid date/);
|
|
90
|
+
assert.equal(mock.urls.length, 0);
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
mock.restore();
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
test("unparseable before throws before any Graph call", async () => {
|
|
97
|
+
const mock = mockGraph(EMPTY);
|
|
98
|
+
try {
|
|
99
|
+
await assert.rejects(runMailList(configWithToken("a7"), { before: "nope" }), /not a valid date/);
|
|
100
|
+
assert.equal(mock.urls.length, 0);
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
mock.restore();
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
test("cursor path ignores an invalid since (all other filter args are ignored)", async () => {
|
|
107
|
+
const cursor = "https://graph.microsoft.com/v1.0/me/mailFolders/Inbox/messages?$skiptoken=ABC";
|
|
108
|
+
const mock = mockGraph(EMPTY);
|
|
109
|
+
try {
|
|
110
|
+
// A paging call carries a cursor; a stale/garbage since must not abort it,
|
|
111
|
+
// because the nextLink already bakes in the original window.
|
|
112
|
+
await runMailList(configWithToken("a8"), { cursor, since: "not-a-date" });
|
|
113
|
+
assert.equal(mock.urls.length, 1);
|
|
114
|
+
assert.match(mock.urls[0], /\$skiptoken=ABC/);
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
mock.restore();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
//# sourceMappingURL=mail-list-paging.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail-list-paging.test.js","sourceRoot":"","sources":["../../src/__tests__/mail-list-paging.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail-search-paging.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/mail-search-paging.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync } 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 { runMailSearch } from "../tools/mail-search.js";
|
|
8
|
+
function configWithToken(accountId) {
|
|
9
|
+
const dir = mkdtempSync(join(tmpdir(), "outlook-mlsearch-"));
|
|
10
|
+
const tokenStore = new TokenStore(accountId, dir);
|
|
11
|
+
tokenStore.store("access-token", "refresh-token", 3600, { scopes: ["Mail.Read"] });
|
|
12
|
+
return { clientId: "test-client", tenantId: "common", accountId, tokenStore };
|
|
13
|
+
}
|
|
14
|
+
function mockGraph(bodyObj) {
|
|
15
|
+
const urls = [];
|
|
16
|
+
const original = globalThis.fetch;
|
|
17
|
+
globalThis.fetch = (async (input, init) => {
|
|
18
|
+
const req = input instanceof Request ? input : null;
|
|
19
|
+
const url = req ? req.url : typeof input === "string" ? input : input.toString();
|
|
20
|
+
if (!url.includes("graph.microsoft.com"))
|
|
21
|
+
return original(input, init);
|
|
22
|
+
urls.push(url);
|
|
23
|
+
return new Response(JSON.stringify(bodyObj), { status: 200, headers: { "content-type": "application/json" } });
|
|
24
|
+
});
|
|
25
|
+
return { restore: () => { globalThis.fetch = original; }, urls };
|
|
26
|
+
}
|
|
27
|
+
test("search surfaces nextCursor from @odata.nextLink", async () => {
|
|
28
|
+
const link = "https://graph.microsoft.com/v1.0/me/messages?$search=%22invoice%22&$skiptoken=ABC";
|
|
29
|
+
const mock = mockGraph({ value: [], "@odata.nextLink": link });
|
|
30
|
+
try {
|
|
31
|
+
const r = await runMailSearch(configWithToken("s1"), { query: "invoice" });
|
|
32
|
+
assert.equal(r.nextCursor, link);
|
|
33
|
+
assert.match(mock.urls[0], /\$search=/);
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
mock.restore();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
test("cursor delegates to the nextLink and re-applies ConsistencyLevel", async () => {
|
|
40
|
+
const cursor = "https://graph.microsoft.com/v1.0/me/messages?$search=%22invoice%22&$skiptoken=ABC";
|
|
41
|
+
let sawConsistency = false;
|
|
42
|
+
const original = globalThis.fetch;
|
|
43
|
+
const urls = [];
|
|
44
|
+
globalThis.fetch = (async (input, init) => {
|
|
45
|
+
const req = input instanceof Request ? input : null;
|
|
46
|
+
const url = req ? req.url : typeof input === "string" ? input : input.toString();
|
|
47
|
+
if (!url.includes("graph.microsoft.com"))
|
|
48
|
+
return original(input, init);
|
|
49
|
+
urls.push(url);
|
|
50
|
+
const headerVal = req
|
|
51
|
+
? req.headers.get("ConsistencyLevel")
|
|
52
|
+
: init?.headers?.["ConsistencyLevel"];
|
|
53
|
+
if (headerVal === "eventual")
|
|
54
|
+
sawConsistency = true;
|
|
55
|
+
return new Response(JSON.stringify({ value: [] }), { status: 200, headers: { "content-type": "application/json" } });
|
|
56
|
+
});
|
|
57
|
+
try {
|
|
58
|
+
await runMailSearch(configWithToken("s2"), { query: "ignored-when-cursor", cursor });
|
|
59
|
+
assert.match(urls[0], /\$skiptoken=ABC/);
|
|
60
|
+
assert.equal(sawConsistency, true, "ConsistencyLevel: eventual must be re-applied on the cursor request");
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
globalThis.fetch = original;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
//# sourceMappingURL=mail-search-paging.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail-search-paging.test.js","sourceRoot":"","sources":["../../src/__tests__/mail-search-paging.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-scan.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/pending-scan.test.ts"],"names":[],"mappings":""}
|