ask-marcel-office-cli 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +274 -0
- package/README.md +45 -26
- package/dist/cli.js +3681 -2370
- package/dist/commands.json +481 -30
- package/dist/composition/run-registry-command.d.ts +4 -4
- package/dist/domain/utilities/base64.d.ts +11 -0
- package/dist/index.js +2441 -1638
- package/dist/infra/auth.d.ts +30 -4
- package/dist/infra/browser-auth.d.ts +21 -1
- package/dist/presenter/output-text.d.ts +1 -1
- package/dist/presenter/output.d.ts +2 -2
- package/dist/presenter/render-to-string.d.ts +14 -6
- package/dist/use-cases/commands/convert-group-post-attachment-to-markdown.d.ts +21 -0
- package/dist/use-cases/commands/convert-group-post-to-markdown.d.ts +20 -0
- package/dist/use-cases/commands/convert-mail-attachment-to-markdown.d.ts +6 -3
- package/dist/use-cases/commands/convert-mail-to-markdown.d.ts +35 -3
- package/dist/use-cases/commands/docx-metadata.d.ts +33 -2
- package/dist/use-cases/commands/get-group-post-attachment.d.ts +13 -0
- package/dist/use-cases/commands/get-group-post.d.ts +4 -0
- package/dist/use-cases/commands/include-hidden-folders.d.ts +2 -0
- package/dist/use-cases/commands/list-group-post-attachments.d.ts +4 -0
- package/dist/use-cases/commands/list-group-thread-posts.d.ts +4 -0
- package/dist/use-cases/commands/list-shared-mailbox-child-folders.d.ts +4 -0
- package/dist/use-cases/commands/list-shared-mailbox-folders.d.ts +4 -0
- package/dist/use-cases/commands/login-status.d.ts +2 -0
- package/dist/use-cases/commands/login.d.ts +18 -8
- package/dist/use-cases/commands/next-page.d.ts +1 -0
- package/dist/use-cases/commands/ooxml-xml-walker.d.ts +31 -6
- package/dist/use-cases/commands/pptx-comments.d.ts +8 -5
- package/dist/use-cases/commands/token-tier-capability.d.ts +5 -0
- package/docs/COMMANDS.md +20 -12
- package/docs/USAGE.md +8 -8
- package/package.json +26 -9
package/dist/commands.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "ask-marcel-office-cli",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"generatedAt": "2026-
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"generatedAt": "2026-09-06T07:44:37.476Z",
|
|
5
5
|
"commands": [
|
|
6
6
|
{
|
|
7
7
|
"name": "convert-calendar-event-attachment-to-markdown",
|
|
@@ -135,6 +135,126 @@
|
|
|
135
135
|
"example": "ask-marcel-office convert-drive-item-zip-to-markdown --drive-id 'b!1234' --item-id '01ABC'",
|
|
136
136
|
"responseShape": "`{ count, files: [{ path, contentType, size, text }] }` — one entry per file in the archive (sorted by path). Convertible files carry `{ contentType, size, text }` (the markdown); unsupported / failed entries carry `{ path, note }` instead. When the archive has more than 100 entries the response adds `truncated: true` + `totalEntries` and only the first 100 are converted."
|
|
137
137
|
},
|
|
138
|
+
{
|
|
139
|
+
"name": "convert-group-post-attachment-to-markdown",
|
|
140
|
+
"summary": "Convert an attachment on one post of a unified (Microsoft 365) group thread to markdown, the `convert-mail-attachment-to-markdown` sibling for a group inbox. Polymorphic on the attachment’s `@odata.type` and sharing the mail pipeline: fileAttachment decodes the inline bytes and converts them locally (docx, xlsx, csv, odt/ods/odp, pptx as per-slide text, pdf text layer, legacy .xls/.doc, an Outlook `.msg` rendered recursively with its quoted chain stripped unless `--keep-quoted true`, plain text passed through); referenceAttachment resolves via `/shares/{token}/driveItem`; an embedded mail, event or contact is rendered locally. There is no PDF sibling here, so an image, a scanned PDF, a legacy `.ppt` and any other unsupported format return a 415 pointing at `get-group-post-attachment` for the raw bytes.",
|
|
141
|
+
"category": "mail",
|
|
142
|
+
"graphMethod": "GET",
|
|
143
|
+
"graphPathTemplate": "/groups/{group-id}/threads/{thread-id}/posts/{post-id}/attachments/{attachment-id}",
|
|
144
|
+
"graphDocsUrl": "https://learn.microsoft.com/en-us/graph/api/attachment-get",
|
|
145
|
+
"options": [
|
|
146
|
+
{
|
|
147
|
+
"name": "group-id",
|
|
148
|
+
"key": "groupId",
|
|
149
|
+
"required": true,
|
|
150
|
+
"description": "Azure AD group object ID for a unified (Microsoft 365) group the signed-in user belongs to."
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"name": "thread-id",
|
|
154
|
+
"key": "threadId",
|
|
155
|
+
"required": true,
|
|
156
|
+
"description": "Conversation thread ID, the `id` of a `list-group-threads` entry."
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"name": "post-id",
|
|
160
|
+
"key": "postId",
|
|
161
|
+
"required": true,
|
|
162
|
+
"description": "Post ID inside that thread. Returned by `list-group-thread-posts`."
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"name": "attachment-id",
|
|
166
|
+
"key": "attachmentId",
|
|
167
|
+
"required": true,
|
|
168
|
+
"description": "Attachment ID inside that post. Returned by `list-group-post-attachments`."
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"name": "include-metadata",
|
|
172
|
+
"key": "includeMetadata",
|
|
173
|
+
"required": false,
|
|
174
|
+
"description": "Pass `--include-metadata true` to append the Office side-channel metadata block, exactly as `convert-mail-attachment-to-markdown` documents it. No-op on other attachment types.",
|
|
175
|
+
"argumentHint": {
|
|
176
|
+
"kind": "magicValue",
|
|
177
|
+
"values": [
|
|
178
|
+
"true",
|
|
179
|
+
"false"
|
|
180
|
+
]
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"name": "keep-quoted",
|
|
185
|
+
"key": "keepQuoted",
|
|
186
|
+
"required": false,
|
|
187
|
+
"description": "Applies to Outlook `.msg` input only. The quoted reply chain / forwarded-message block is stripped by default (it duplicates history and inflates the context budget) and replaced with a single visible marker naming this flag, so nothing is removed silently. Pass `--keep-quoted true` to render the full body. The recognized markers are the same set `convert-mail-to-markdown` uses — see `ask-marcel-office docs convert-mail-to-markdown`.",
|
|
188
|
+
"argumentHint": {
|
|
189
|
+
"kind": "magicValue",
|
|
190
|
+
"values": [
|
|
191
|
+
"true",
|
|
192
|
+
"false"
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
],
|
|
197
|
+
"example": "ask-marcel-office convert-group-post-attachment-to-markdown --group-id 'a1b2c3d4-...' --thread-id 'AAQkAD...' --post-id 'AQMkAD...' --attachment-id 'AAMkAD...'",
|
|
198
|
+
"responseShape": "`{ contentType: \"text/markdown\", size, text }` on success (file and reference attachments run through the conversion dispatch; an embedded item is rendered locally). Plain-text sources return the raw-bytes envelope, and a PDF source carries `pageCount`. Unsupported types return an api_error with status 415 naming `get-group-post-attachment` as the way to the bytes.",
|
|
199
|
+
"producesBytes": true
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"name": "convert-group-post-to-markdown",
|
|
203
|
+
"summary": "Render one post of a unified (Microsoft 365) group thread as markdown, the way `convert-mail-to-markdown` renders an Outlook message: a `**From:**` line, a `**Date:**` line, then the HTML body through turndown with quoted reply chains stripped. A post arrives from the group's own address with the writer in `sender`, so the author line reads `Robin Chen <robin.chen@contoso.com> on behalf of Support <support@contoso.com>`. There is no subject line: the thread `topic` is the subject and lives on `list-group-threads`. By default no image bytes are fetched; inline `cid:` images render as `[inline image: <name>]` placeholders unless `--inline-images true`. File attachments are listed below the body by name, size and id and their bytes are never fetched here; read one with `convert-group-post-attachment-to-markdown` or fetch it with `get-group-post-attachment`. Same staged-fetch design as the mail command: one call for the post, one for the attachment list when `hasAttachments` is true or the body references a `cid:` image (Graph reports false for a post whose only attachments are inline), and with `--inline-images true` one per small inline image.",
|
|
204
|
+
"category": "mail",
|
|
205
|
+
"graphMethod": "GET",
|
|
206
|
+
"graphPathTemplate": "/groups/{group-id}/threads/{thread-id}/posts/{post-id}",
|
|
207
|
+
"graphDocsUrl": "https://learn.microsoft.com/en-us/graph/api/post-get",
|
|
208
|
+
"options": [
|
|
209
|
+
{
|
|
210
|
+
"name": "group-id",
|
|
211
|
+
"key": "groupId",
|
|
212
|
+
"required": true,
|
|
213
|
+
"description": "Azure AD group object ID for a unified (Microsoft 365) group the signed-in user belongs to."
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"name": "thread-id",
|
|
217
|
+
"key": "threadId",
|
|
218
|
+
"required": true,
|
|
219
|
+
"description": "Conversation thread ID, the `id` of a `list-group-threads` entry."
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"name": "post-id",
|
|
223
|
+
"key": "postId",
|
|
224
|
+
"required": true,
|
|
225
|
+
"description": "Post ID inside that thread. Returned by `list-group-thread-posts`."
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"name": "inline-images",
|
|
229
|
+
"key": "inlineImages",
|
|
230
|
+
"required": false,
|
|
231
|
+
"description": "Pass `--inline-images true` to fetch small inline images (≤ 2 MB, `image/*` only) and embed them as base64 `data:` URIs. Default is `false`: no per-image bytes fetch, and every inline `cid:` image renders as a `[inline image: <name>]` placeholder while still appearing in the attachments list. Same rule as `convert-mail-to-markdown`.",
|
|
232
|
+
"argumentHint": {
|
|
233
|
+
"kind": "magicValue",
|
|
234
|
+
"values": [
|
|
235
|
+
"true",
|
|
236
|
+
"false"
|
|
237
|
+
]
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"name": "keep-quoted",
|
|
242
|
+
"key": "keepQuoted",
|
|
243
|
+
"required": false,
|
|
244
|
+
"description": "Quoted reply chains and forwarded-message blocks are stripped by default and replaced with a single visible marker naming this flag; the `note` reports the share of the body text that went with them. Pass `--keep-quoted true` to preserve the full body. The markers recognised are the ones `convert-mail-to-markdown` documents.",
|
|
245
|
+
"argumentHint": {
|
|
246
|
+
"kind": "magicValue",
|
|
247
|
+
"values": [
|
|
248
|
+
"true",
|
|
249
|
+
"false"
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
"example": "ask-marcel-office convert-group-post-to-markdown --group-id 'a1b2c3d4-...' --thread-id 'AAQkAD...' --post-id 'AQMkAD...'",
|
|
255
|
+
"responseShape": "`{ contentType: \"text/markdown\", size, text, note? }`, the same envelope as `convert-mail-to-markdown`: headers, the turndown-rendered body and, when present, an attachments list. The optional `note` carries the attachments-list failure hint and/or the quoted-chain notice with the share of the body text it removed.",
|
|
256
|
+
"producesBytes": true
|
|
257
|
+
},
|
|
138
258
|
{
|
|
139
259
|
"name": "convert-local-file-to-markdown",
|
|
140
260
|
"summary": "Convert a file ON DISK to markdown — the only command that never calls Microsoft Graph (works offline, no login). Runs the same local pipelines as `download-drive-item-as-markdown`: docx (mammoth → turndown), xlsx (sheetjs tables, `--max-cells` OOM cap), pptx (per-slide text), odt/ods/odp, csv, pdf (text layer via unpdf), legacy OLE .xls / .doc, Outlook .msg (headers + body with the quoted reply chain stripped — `--keep-quoted true` restores it — and inline `cid:` images shown as placeholders, attachments converted recursively), plain-text passthrough — and a `.zip` is unpacked with every contained file converted in one call (legacy GBK / CP437 entry names decoded, not mojibaked). What it canNOT do locally: convert TO pdf, and Loop/Fluid/Whiteboard sources — both need a Graph server round-trip (upload to OneDrive and use the drive-item siblings). Pass `--include-metadata true` for the Office side-channel metadata blocks; `--inline-images true` to embed docx images as base64 data URIs.",
|
|
@@ -340,7 +460,7 @@
|
|
|
340
460
|
},
|
|
341
461
|
{
|
|
342
462
|
"name": "convert-mail-to-markdown",
|
|
343
|
-
"summary": "Render a single Outlook email as markdown — headers (`**Subject:**`, `**From:**`, `**To:**`, `**Cc:**` only when present, `**Date:**`), followed by the body run through turndown. By default NO image bytes are fetched: every inline `cid:` image renders as a readable `[inline image: <name>]` placeholder and the images surface in the file-attachments list, so the output stays close to the text size (an email whose 6 KB body carried 30 KB of signature-image base64 now ships at ~6 KB). Pass `--inline-images true` to embed inline images (`isInline:true` + `image/*` content-type, size ≤ 2 MB) as base64 `data:` URIs for self-contained output (non-image inline attachments are never embedded; oversize inline images keep a placeholder note; a cid whose per-image fetch fails degrades to the placeholder too). File attachments are always listed below the body by name + size + id; their bytes are NOT fetched here — call `convert-mail-attachment-to-pdf` or `get-mail-attachment` with the id when you actually need them. Staged-fetch design: one call for the body, one for the attachments-metadata list (
|
|
463
|
+
"summary": "Render a single Outlook email as markdown — headers (`**Subject:**`, `**From:**`, `**To:**`, `**Cc:**` only when present, `**Date:**`), followed by the body run through turndown. By default NO image bytes are fetched: every inline `cid:` image renders as a readable `[inline image: <name>]` placeholder and the images surface in the file-attachments list, so the output stays close to the text size (an email whose 6 KB body carried 30 KB of signature-image base64 now ships at ~6 KB). Pass `--inline-images true` to embed inline images (`isInline:true` + `image/*` content-type, size ≤ 2 MB) as base64 `data:` URIs for self-contained output (non-image inline attachments are never embedded; oversize inline images keep a placeholder note; a cid whose per-image fetch fails degrades to the placeholder too). File attachments are always listed below the body by name + size + id; their bytes are NOT fetched here — call `convert-mail-attachment-to-pdf` or `get-mail-attachment` with the id when you actually need them. Staged-fetch design: one call for the body, one for the attachments-metadata list (when `hasAttachments` is true or the body references a `cid:` image, since Graph reports false for inline-only mail), and with `--inline-images true` one per small inline image — replaces the old `?$expand=attachments` which timed out / truncated on messages with multi-MB attachments.",
|
|
344
464
|
"category": "mail",
|
|
345
465
|
"graphMethod": "GET",
|
|
346
466
|
"graphPathTemplate": "/me/messages/{message-id}",
|
|
@@ -362,7 +482,7 @@
|
|
|
362
482
|
"name": "keep-quoted",
|
|
363
483
|
"key": "keepQuoted",
|
|
364
484
|
"required": false,
|
|
365
|
-
"description": "Quoted reply chains and forwarded-message blocks are stripped by default (they duplicate content already present in earlier messages and inflate the context budget). The stripped tail is replaced with a single visible marker naming this flag, so nothing is removed silently. Pass `--keep-quoted true` to preserve the full body. Only well-known structural markers are cut: in HTML bodies Outlook `divRplyFwdMsg` / `appendonsend` / `stopSpelling`, Gmail `gmail_quote`, the Outlook reply border separator (`#E1E1E1` on desktop, `#B5C4DF` on Mac/mobile), and a bold `From:`/`Sent:` header-label pair (localized variants recognized: 发件人/发送时间, De/Envoyé, Von/Gesendet, Da/Inviato, De/Enviado, Van/Verzonden, 差出人/送信日時, 보낸 사람/보낸 날짜 — a lone bolded \"From:\" without its companion label never cuts); in plain-text bodies the `Original Message` banner, the `On … wrote:` attribution line, leading `>` quote lines, and the same localized `From:`+`Sent:` line pairs.",
|
|
485
|
+
"description": "Quoted reply chains and forwarded-message blocks are stripped by default (they duplicate content already present in earlier messages and inflate the context budget). The stripped tail is replaced with a single visible marker naming this flag, so nothing is removed silently. Pass `--keep-quoted true` to preserve the full body. Only well-known structural markers are cut: in HTML bodies Outlook `divRplyFwdMsg` / `appendonsend` / `stopSpelling`, Gmail `gmail_quote`, the Outlook reply border separator (`#E1E1E1` on desktop, `#B5C4DF` on Mac/mobile, in either the hex or the `border-color:rgb(…)` longhand new Outlook writes), the new-Outlook `mail-editor-reference-message-container` but only when a header block opens inside it (the same id also wraps a body that quotes nothing, so the id alone never cuts), and a bold `From:`/`Sent:` header-label pair (localized variants recognized: 发件人/发送时间, De/Envoyé, Von/Gesendet, Da/Inviato, De/Enviado, Van/Verzonden, 差出人/送信日時, 보낸 사람/보낸 날짜 — a lone bolded \"From:\" without its companion label never cuts); in plain-text bodies the `Original Message` banner, the `On … wrote:` attribution line, leading `>` quote lines, and the same localized `From:`+`Sent:` line pairs. When a chain is stripped the `note` reports what share of the body’s readable text went with it (`removed 62% of the body text`), so a caller can tell a normal strip from one that swallowed the message without refetching to compare.",
|
|
366
486
|
"argumentHint": {
|
|
367
487
|
"kind": "magicValue",
|
|
368
488
|
"values": [
|
|
@@ -373,7 +493,7 @@
|
|
|
373
493
|
}
|
|
374
494
|
],
|
|
375
495
|
"example": "ask-marcel-office convert-mail-to-markdown --message-id 'AAMkAD...'",
|
|
376
|
-
"responseShape": "`{ contentType: \"text/markdown\", size, text, note? }` — headers + turndown-rendered body + (when present) a file-attachments list. The optional `note` carries a partial-success hint when the attachments-metadata fetch fails after the body succeeded, and/or a flag that a quoted reply chain was stripped (
|
|
496
|
+
"responseShape": "`{ contentType: \"text/markdown\", size, text, note? }` — headers + turndown-rendered body + (when present) a file-attachments list. The optional `note` carries a partial-success hint when the attachments-metadata fetch fails after the body succeeded, and/or a flag that a quoted reply chain was stripped, carrying the share of the body’s readable text that went with it (`quoted reply chain stripped (removed 62% of the body text) — pass --keep-quoted true to include it`). A share near 100% on a message that should have had a real reply above the quote is the signature of a mis-detected boundary: refetch with `--keep-quoted true` to confirm.",
|
|
377
497
|
"producesBytes": true
|
|
378
498
|
},
|
|
379
499
|
{
|
|
@@ -607,7 +727,7 @@
|
|
|
607
727
|
"name": "include-metadata",
|
|
608
728
|
"key": "includeMetadata",
|
|
609
729
|
"required": false,
|
|
610
|
-
"description": "Pass `--include-metadata true` to surface the side-channel content the rendered body hides. For docx (`## DOCX metadata`): core/app/custom document properties, people registry, external hyperlinks, comments, tracked changes (
|
|
730
|
+
"description": "Pass `--include-metadata true` to surface the side-channel content the rendered body hides. For docx (`## DOCX metadata`): core/app/custom document properties, people registry, external hyperlinks, comments, tracked changes (a deletion sitting next to an insertion by the same author is reported once as a `replacement` carrying `before` + `after`; the halves that pair with nothing stay under `insertions` / `deletions`; `moves` joins the two ends of a moved span by its range name; `formatChanges` names which run, paragraph, table, row or cell properties a reviewer altered, and a property whose values hang off child elements rather than its own attributes (`w:tblBorders` is the common one) is reported with its author and scope but names no property. Structural revisions — cell insert / delete / merge, numbering and section properties — are still unreported), hidden-formatted text (w:vanish), field instructions (MERGEFIELD / HYPERLINK / DOCVARIABLE), bookmarks. For xlsx (`## Workbook metadata`): core/app/custom properties, external relationships, defined names, hidden / very-hidden sheets, legacy cell comments, threaded comments, persons. For pptx (`## PPTX metadata`): properties, external relationships, slide tags, comment authors + comments (legacy + modern), and per-slide title / speaker notes / hidden flag — returned as a standalone document since pptx has no convertible body (use `download-drive-item-as-pdf` for slide visuals). For OpenDocument (`.odt`/`.ods`/`.odp`, `## OpenDocument metadata`): Dublin Core + ODF properties, keywords, user-defined custom fields — appended after the converted body. Each OOXML family also covers its macro-enabled (`.docm` / `.xlsm` / `.pptm`) and template (`.dotx` / `.xltx` / `.potx`, etc.) variants, with a `### Macros (VBA)` section flagging an embedded `vbaProject.bin`. No-op on other sources.",
|
|
611
731
|
"argumentHint": {
|
|
612
732
|
"kind": "magicValue",
|
|
613
733
|
"values": [
|
|
@@ -1572,6 +1692,97 @@
|
|
|
1572
1692
|
"example": "ask-marcel-office get-group --group-id 'a1b2c3d4-...' --select 'id,displayName,mail'",
|
|
1573
1693
|
"responseShape": "single Microsoft Graph `group` resource"
|
|
1574
1694
|
},
|
|
1695
|
+
{
|
|
1696
|
+
"name": "get-group-post",
|
|
1697
|
+
"summary": "Get a single post of a unified (Microsoft 365) group thread by ID, the sibling of `get-mail-message` for a group inbox: the full `post` resource including the HTML `body`. `--select` trims the projection. `--expand attachments` returns every attachment inline with its base64 `contentBytes`, which is convenient for a small post and the wrong shape for one carrying a multi-MB file: list them with `list-group-post-attachments` and fetch one with `get-group-post-attachment` instead. Post IDs come from `list-group-thread-posts`; use `convert-group-post-to-markdown` for a readable rendering.",
|
|
1698
|
+
"category": "mail",
|
|
1699
|
+
"graphMethod": "GET",
|
|
1700
|
+
"graphPathTemplate": "/groups/{group-id}/threads/{thread-id}/posts/{post-id}",
|
|
1701
|
+
"graphDocsUrl": "https://learn.microsoft.com/en-us/graph/api/post-get",
|
|
1702
|
+
"options": [
|
|
1703
|
+
{
|
|
1704
|
+
"name": "group-id",
|
|
1705
|
+
"key": "groupId",
|
|
1706
|
+
"required": true,
|
|
1707
|
+
"description": "Azure AD group object ID for a unified (Microsoft 365) group the signed-in user belongs to."
|
|
1708
|
+
},
|
|
1709
|
+
{
|
|
1710
|
+
"name": "thread-id",
|
|
1711
|
+
"key": "threadId",
|
|
1712
|
+
"required": true,
|
|
1713
|
+
"description": "Conversation thread ID, the `id` of a `list-group-threads` entry."
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
"name": "post-id",
|
|
1717
|
+
"key": "postId",
|
|
1718
|
+
"required": true,
|
|
1719
|
+
"description": "Post ID inside that thread. Returned by `list-group-thread-posts`."
|
|
1720
|
+
},
|
|
1721
|
+
{
|
|
1722
|
+
"name": "select",
|
|
1723
|
+
"key": "select",
|
|
1724
|
+
"required": false,
|
|
1725
|
+
"description": "OData $select: comma-separated list of fields to include in each item (e.g. `id,subject,from`). May shrink payloads dramatically — Graph honors $select on most endpoints, but some collections (notably `/me/mailboxSettings`, `/me/outlook/masterCategories`, `/me/mailFolders/inbox/messageRules`) silently ignore it and always return the full resource. Bogus field names are silently dropped by Graph — if a field you asked for is missing from the response, double-check the spelling against the documented `responseShape`."
|
|
1726
|
+
},
|
|
1727
|
+
{
|
|
1728
|
+
"name": "expand",
|
|
1729
|
+
"key": "expand",
|
|
1730
|
+
"required": false,
|
|
1731
|
+
"description": "OData $expand: navigation properties to include inline (e.g. `attachments`). Increases response size; use sparingly."
|
|
1732
|
+
}
|
|
1733
|
+
],
|
|
1734
|
+
"example": "ask-marcel-office get-group-post --group-id 'a1b2c3d4-...' --thread-id 'AAQkAD...' --post-id 'AQMkAD...'",
|
|
1735
|
+
"responseShape": "single Microsoft Graph `post` resource: `id`, `createdDateTime`, `lastModifiedDateTime`, `changeKey`, `categories`, `receivedDateTime`, `hasAttachments`, `body { contentType, content }`, `from` (normally the group address), `sender` (the person who wrote it). With `--expand attachments`, an `attachments[]` array of `fileAttachment` / `itemAttachment` / `referenceAttachment` entries, file attachments carrying `contentBytes` inline."
|
|
1736
|
+
},
|
|
1737
|
+
{
|
|
1738
|
+
"name": "get-group-post-attachment",
|
|
1739
|
+
"summary": "Get a single attachment on one post of a unified (Microsoft 365) group thread, the `get-mail-attachment` sibling for a group inbox. Prefer it over `get-group-post --expand attachments`, which expands every attachment at once. fileAttachments carry a `base64` mirror of `contentBytes` so the global output-path flag lands the bytes on disk in one call; with an output-path set both byte fields are stripped from stdout in favour of `savedTo`. Pass `--select id,name,contentType,size` for metadata only. This is also the route to an image attached to a post: fetch the bytes and feed them to a vision-capable model.",
|
|
1740
|
+
"category": "mail",
|
|
1741
|
+
"graphMethod": "GET",
|
|
1742
|
+
"graphPathTemplate": "/groups/{group-id}/threads/{thread-id}/posts/{post-id}/attachments/{attachment-id}",
|
|
1743
|
+
"graphDocsUrl": "https://learn.microsoft.com/en-us/graph/api/attachment-get",
|
|
1744
|
+
"options": [
|
|
1745
|
+
{
|
|
1746
|
+
"name": "group-id",
|
|
1747
|
+
"key": "groupId",
|
|
1748
|
+
"required": true,
|
|
1749
|
+
"description": "Azure AD group object ID for a unified (Microsoft 365) group the signed-in user belongs to."
|
|
1750
|
+
},
|
|
1751
|
+
{
|
|
1752
|
+
"name": "thread-id",
|
|
1753
|
+
"key": "threadId",
|
|
1754
|
+
"required": true,
|
|
1755
|
+
"description": "Conversation thread ID, the `id` of a `list-group-threads` entry."
|
|
1756
|
+
},
|
|
1757
|
+
{
|
|
1758
|
+
"name": "post-id",
|
|
1759
|
+
"key": "postId",
|
|
1760
|
+
"required": true,
|
|
1761
|
+
"description": "Post ID inside that thread. Returned by `list-group-thread-posts`."
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
"name": "attachment-id",
|
|
1765
|
+
"key": "attachmentId",
|
|
1766
|
+
"required": true,
|
|
1767
|
+
"description": "Attachment ID inside that post. Returned by `list-group-post-attachments`."
|
|
1768
|
+
},
|
|
1769
|
+
{
|
|
1770
|
+
"name": "select",
|
|
1771
|
+
"key": "select",
|
|
1772
|
+
"required": false,
|
|
1773
|
+
"description": "OData $select: comma-separated list of fields to include in each item (e.g. `id,subject,from`). May shrink payloads dramatically — Graph honors $select on most endpoints, but some collections (notably `/me/mailboxSettings`, `/me/outlook/masterCategories`, `/me/mailFolders/inbox/messageRules`) silently ignore it and always return the full resource. Bogus field names are silently dropped by Graph — if a field you asked for is missing from the response, double-check the spelling against the documented `responseShape`."
|
|
1774
|
+
},
|
|
1775
|
+
{
|
|
1776
|
+
"name": "expand",
|
|
1777
|
+
"key": "expand",
|
|
1778
|
+
"required": false,
|
|
1779
|
+
"description": "OData $expand: navigation properties to include inline (e.g. `attachments`). Increases response size; use sparingly."
|
|
1780
|
+
}
|
|
1781
|
+
],
|
|
1782
|
+
"example": "ask-marcel-office get-group-post-attachment --group-id 'a1b2c3d4-...' --thread-id 'AAQkAD...' --post-id 'AQMkAD...' --attachment-id 'AAMkAD...'",
|
|
1783
|
+
"responseShape": "single Microsoft Graph `attachment` resource. fileAttachments include `contentBytes` (Graph) AND `base64` (CLI mirror) so `--output-path` works; with `--output-path` set, both byte fields are stripped from stdout and replaced by `savedTo`. itemAttachments and referenceAttachments are returned unchanged.",
|
|
1784
|
+
"producesBytes": true
|
|
1785
|
+
},
|
|
1575
1786
|
{
|
|
1576
1787
|
"name": "get-mail-attachment",
|
|
1577
1788
|
"summary": "Get a single attachment on an Outlook message (metadata, plus the base64 `contentBytes` for file attachments). For fileAttachments, the response also carries a `base64` mirror of `contentBytes` so the global output-path flag can land the bytes on disk in one call — and when an output-path is set the CLI strips BOTH `contentBytes` and `base64` from stdout, leaving a compact metadata envelope with `savedTo` (the file is on disk; no multi-MB base64 in the terminal). When you only want metadata, use `--select id,name,contentType,size` to skip the `contentBytes` payload.",
|
|
@@ -1974,7 +2185,7 @@
|
|
|
1974
2185
|
},
|
|
1975
2186
|
{
|
|
1976
2187
|
"name": "get-shared-mailbox-message",
|
|
1977
|
-
"summary": "Return a single message from a shared / delegated mailbox. Use `--select` to fetch only specific fields (e.g. `--select id,subject,from,receivedDateTime`) — sibling to `get-mail-message` for /me.",
|
|
2188
|
+
"summary": "Return a single message from a shared / delegated mailbox. Use `--select` to fetch only specific fields (e.g. `--select id,subject,from,receivedDateTime`) — sibling to `get-mail-message` for /me. Requires the delegated `Mail.Read.Shared` scope, which neither token this CLI can mint carries (verified live on two tenants, 2026-08-30), so any mailbox other than the signed-in user's own is expected to answer `ErrorAccessDenied` whatever delegation Exchange holds. Your own UPN works; a Microsoft 365 group's mailbox is the shared-mail path that does (`list-group-conversations`, `list-group-thread-posts`).",
|
|
1978
2189
|
"category": "mail",
|
|
1979
2190
|
"graphMethod": "GET",
|
|
1980
2191
|
"graphPathTemplate": "/users/{user-id}/messages/{message-id}",
|
|
@@ -2466,7 +2677,7 @@
|
|
|
2466
2677
|
},
|
|
2467
2678
|
{
|
|
2468
2679
|
"name": "get-user",
|
|
2469
|
-
"summary": "Look up a directory user. Pass an Azure AD id, UPN, or email as --user-id and get that user's FULL profile (displayName, mail, jobTitle, department, officeLocation, phones) via GET /users/{id} on the elevated M365 token
|
|
2680
|
+
"summary": "Look up a directory user. Pass an Azure AD id, UPN, or email as --user-id and get that user's FULL profile (displayName, mail, jobTitle, department, officeLocation, phones) via GET /users/{id} on the basic token — no elevated login needed. On a tenant that restricts basic directory reads the id path falls back to the elevated M365 token; re-capture that with `ask-marcel-office login` (preflight tiers with `ask-marcel-office scopes-check`, no Graph call). An email resolves even when it is the user's `mail` rather than their sign-in UPN: guest / B2B users carry a `#EXT#` UPN whose local part is NOT their email address, so when the direct lookup 404s the command falls back to `GET /users?$filter=mail eq '<email>'` and returns the single match. Only THIS tenant's directory is queried: a person's home-tenant object id (e.g. a cross-tenant Teams `8:orgid:<home-id>` participant, whose id lives in their own tenant) and any email that is not their `mail`/UPN here are unresolvable by design — reach an external person via their LOCAL guest projection (by name, or by their real `mail`), never by their home id. Pass a NAME instead and it searches your relevant-people graph (GET /me/people) and returns candidate matches (id, displayName, mail, jobTitle, department) so you can pick the right person and re-query. Re-query by the candidate's `id` when it is a directory GUID; an EXTERNAL contact's candidate carries a base64-ish People-API id instead, which nothing can resolve — re-query those by the candidate's `mail` (the CLI rejects an opaque contact id with that remedy rather than returning empty matches). Name search covers colleagues in your people graph, not the whole tenant directory; use `microsoft-search-query` for a broader tenant-wide person search. Without `--select`, the profile ships a default projection of id, displayName, userPrincipalName, mail, jobTitle, department, officeLocation, businessPhones, mobilePhone.",
|
|
2470
2681
|
"category": "user",
|
|
2471
2682
|
"graphMethod": "GET",
|
|
2472
2683
|
"graphPathTemplate": "/users/{user-id} (id / UPN; an email that misses falls back to /users?$filter=mail eq) OR /me/people?$search=\"{user-id}\" (a bare name)",
|
|
@@ -2476,7 +2687,7 @@
|
|
|
2476
2687
|
"name": "user-id",
|
|
2477
2688
|
"key": "userId",
|
|
2478
2689
|
"required": true,
|
|
2479
|
-
"description": "Azure AD user ID, UPN, or email (returns the full profile via the elevated token), OR a display name (returns relevant-people candidates on the basic token). Discover ids via `list-relevant-people` or `microsoft-search-query`."
|
|
2690
|
+
"description": "Azure AD user ID, UPN, or email (returns the full profile via the basic token, falling back to the elevated token only on tenants that restrict basic directory reads), OR a display name (returns relevant-people candidates on the basic token). Discover ids via `list-relevant-people` or `microsoft-search-query`."
|
|
2480
2691
|
},
|
|
2481
2692
|
{
|
|
2482
2693
|
"name": "select",
|
|
@@ -2492,7 +2703,7 @@
|
|
|
2492
2703
|
}
|
|
2493
2704
|
],
|
|
2494
2705
|
"example": "ask-marcel-office get-user --user-id 'alice@contoso.com' --select 'id,displayName,mail,jobTitle,department'",
|
|
2495
|
-
"responseShape": "For an id / UPN / email: a single Microsoft Graph `user` resource projected to the default `$select` (id, displayName, userPrincipalName, mail, jobTitle, department, officeLocation, businessPhones, mobilePhone) unless `--select` overrides it; honours `--expand`. An email is tried against the sign-in UPN first, then falls back to the `mail` attribute (`$filter=mail eq`) so guest/B2B users resolve; an email that matches nobody returns the direct-path 404. For a name: `{ query, matches: [{ id, displayName, mail, jobTitle, department }] }` from the People API (empty `matches` when nobody in your relevant-people graph matches; re-query by a chosen GUID `id`, or by `mail` when the candidate is an external contact with a base64-ish People-API id — passing that id back is rejected with the same remedy). The id path
|
|
2706
|
+
"responseShape": "For an id / UPN / email: a single Microsoft Graph `user` resource projected to the default `$select` (id, displayName, userPrincipalName, mail, jobTitle, department, officeLocation, businessPhones, mobilePhone) unless `--select` overrides it; honours `--expand`. An email is tried against the sign-in UPN first, then falls back to the `mail` attribute (`$filter=mail eq`) so guest/B2B users resolve; an email that matches nobody returns the direct-path 404. For a name: `{ query, matches: [{ id, displayName, mail, jobTitle, department }] }` from the People API (empty `matches` when nobody in your relevant-people graph matches; re-query by a chosen GUID `id`, or by `mail` when the candidate is an external contact with a base64-ish People-API id — passing that id back is rejected with the same remedy). The id path resolves on the basic token; on a tenant that restricts basic directory reads it falls back to the elevated M365 token, which fail-fasts with `secondary_token_unavailable` when that token is cold (preflight with `ask-marcel-office scopes-check`, re-capture with `ask-marcel-office login`).",
|
|
2496
2707
|
"needsElevatedToken": true
|
|
2497
2708
|
},
|
|
2498
2709
|
{
|
|
@@ -3166,7 +3377,7 @@
|
|
|
3166
3377
|
}
|
|
3167
3378
|
],
|
|
3168
3379
|
"example": "ask-marcel-office list-conversation-messages --conversation-id 'AAQkAD...=' --top 5 --select id,subject,receivedDateTime",
|
|
3169
|
-
"responseShape": "collection of Microsoft Graph `message` resources under `value[]` (unordered)",
|
|
3380
|
+
"responseShape": "collection of Microsoft Graph `message` resources under `value[]` (unordered). This command ships no default `$select`, so an unflagged call returns the full `message` resource. `internetMessageHeaders` IS honored here (verified live 2026-08-29): naming it in `--select` returns the raw RFC 5322 headers for every message in the thread in ONE call, so a per-message `get-mail-message --select internetMessageHeaders` follow-up is unnecessary. Use `get-mail-message-mime` when the complete raw source, not just the headers, is needed.",
|
|
3170
3381
|
"pagination": true
|
|
3171
3382
|
},
|
|
3172
3383
|
{
|
|
@@ -4032,7 +4243,7 @@
|
|
|
4032
4243
|
},
|
|
4033
4244
|
{
|
|
4034
4245
|
"name": "list-group-conversations",
|
|
4035
|
-
"summary": "List conversations in a unified (Microsoft 365) group inbox. Each conversation aggregates one or more threads. Only Microsoft 365 groups have a mailbox — security and distribution groups return `MailboxNotEnabledForRESTAPI`. Verify the group is unified before calling.",
|
|
4246
|
+
"summary": "List conversations in a unified (Microsoft 365) group inbox. Each conversation aggregates one or more threads. Only Microsoft 365 groups have a mailbox — security and distribution groups return `MailboxNotEnabledForRESTAPI`. Verify the group is unified before calling. Bodies live two levels down: `list-group-threads` then `list-group-thread-posts`, or `--expand 'threads($expand=posts)'` here to fetch conversations, threads and posts in one call.",
|
|
4036
4247
|
"category": "mail",
|
|
4037
4248
|
"graphMethod": "GET",
|
|
4038
4249
|
"graphPathTemplate": "/groups/{group-id}/conversations",
|
|
@@ -4062,12 +4273,6 @@
|
|
|
4062
4273
|
"required": false,
|
|
4063
4274
|
"description": "OData $select: comma-separated list of fields to include in each item (e.g. `id,subject,from`). May shrink payloads dramatically — Graph honors $select on most endpoints, but some collections (notably `/me/mailboxSettings`, `/me/outlook/masterCategories`, `/me/mailFolders/inbox/messageRules`) silently ignore it and always return the full resource. Bogus field names are silently dropped by Graph — if a field you asked for is missing from the response, double-check the spelling against the documented `responseShape`."
|
|
4064
4275
|
},
|
|
4065
|
-
{
|
|
4066
|
-
"name": "filter",
|
|
4067
|
-
"key": "filter",
|
|
4068
|
-
"required": false,
|
|
4069
|
-
"description": "OData $filter: predicate to narrow results server-side. Quoting rules: string literals MUST use SINGLE quotes (`subject eq 'invoice'`), NOT double quotes — Graph rejects `subject eq \"invoice\"` with `InvalidFilterClause`. To embed a single quote inside a string, double it (`subject eq 'O''Brien'`). Booleans, numbers, and dates are unquoted (`isRead eq false`, `receivedDateTime ge 2026-01-01T00:00:00Z`). Wrap the whole flag value in shell DOUBLE quotes so the inner single quotes survive (`--filter \"subject eq 'invoice'\"`). Same syntax Graph documents per resource type."
|
|
4070
|
-
},
|
|
4071
4276
|
{
|
|
4072
4277
|
"name": "orderby",
|
|
4073
4278
|
"key": "orderby",
|
|
@@ -4250,9 +4455,87 @@
|
|
|
4250
4455
|
"responseShape": "collection of Microsoft Graph `directoryObject` resources under `value[]`",
|
|
4251
4456
|
"pagination": true
|
|
4252
4457
|
},
|
|
4458
|
+
{
|
|
4459
|
+
"name": "list-group-post-attachments",
|
|
4460
|
+
"summary": "List the attachments (file, item, reference) on one post of a unified (Microsoft 365) group thread. Ships the slim default `--select=id,name,contentType,size,isInline` the mail and calendar siblings use, so a caller sees what is attached without pulling any bytes — the staged alternative to `get-group-post --expand attachments`, which inlines EVERY attachment at once and times out on a post carrying a multi-MB file. Graph returns the whole collection in one response and silently ignores `$top`, `$skip`, `$orderby` and `$filter` (probed live 2026-09-03), so only `--select` and `--expand` are exposed. A post whose only attachments are inline images reports `hasAttachments: false`, so call this whenever the body shows `cid:` references. Read one with `convert-group-post-attachment-to-markdown`, or fetch its bytes with `get-group-post-attachment`.",
|
|
4461
|
+
"category": "mail",
|
|
4462
|
+
"graphMethod": "GET",
|
|
4463
|
+
"graphPathTemplate": "/groups/{group-id}/threads/{thread-id}/posts/{post-id}/attachments",
|
|
4464
|
+
"graphDocsUrl": "https://learn.microsoft.com/en-us/graph/api/post-list-attachments",
|
|
4465
|
+
"options": [
|
|
4466
|
+
{
|
|
4467
|
+
"name": "group-id",
|
|
4468
|
+
"key": "groupId",
|
|
4469
|
+
"required": true,
|
|
4470
|
+
"description": "Azure AD group object ID for a unified (Microsoft 365) group you belong to."
|
|
4471
|
+
},
|
|
4472
|
+
{
|
|
4473
|
+
"name": "thread-id",
|
|
4474
|
+
"key": "threadId",
|
|
4475
|
+
"required": true,
|
|
4476
|
+
"description": "Conversation thread ID, the `id` of a `list-group-threads` entry."
|
|
4477
|
+
},
|
|
4478
|
+
{
|
|
4479
|
+
"name": "post-id",
|
|
4480
|
+
"key": "postId",
|
|
4481
|
+
"required": true,
|
|
4482
|
+
"description": "Post ID inside that thread. Returned by `list-group-thread-posts`."
|
|
4483
|
+
},
|
|
4484
|
+
{
|
|
4485
|
+
"name": "select",
|
|
4486
|
+
"key": "select",
|
|
4487
|
+
"required": false,
|
|
4488
|
+
"description": "OData $select: comma-separated list of fields to include in each item (e.g. `id,subject,from`). May shrink payloads dramatically — Graph honors $select on most endpoints, but some collections (notably `/me/mailboxSettings`, `/me/outlook/masterCategories`, `/me/mailFolders/inbox/messageRules`) silently ignore it and always return the full resource. Bogus field names are silently dropped by Graph — if a field you asked for is missing from the response, double-check the spelling against the documented `responseShape`."
|
|
4489
|
+
},
|
|
4490
|
+
{
|
|
4491
|
+
"name": "expand",
|
|
4492
|
+
"key": "expand",
|
|
4493
|
+
"required": false,
|
|
4494
|
+
"description": "OData $expand: navigation properties to include inline (e.g. `attachments`). Increases response size; use sparingly."
|
|
4495
|
+
}
|
|
4496
|
+
],
|
|
4497
|
+
"example": "ask-marcel-office list-group-post-attachments --group-id 'a1b2c3d4-...' --thread-id 'AAQkAD...' --post-id 'AQMkAD...'",
|
|
4498
|
+
"responseShape": "collection of Microsoft Graph `attachment` resources under `value[]` (slim metadata by default — see summary), with no page cursor. Graph always includes `@odata.type` and `@odata.mediaContentType` on every entry regardless of `--select`; that discriminator is what the converting sibling branches on. An inline image carries `isInline: true` and a `contentId` matching a `cid:` reference in the post body."
|
|
4499
|
+
},
|
|
4500
|
+
{
|
|
4501
|
+
"name": "list-group-thread-posts",
|
|
4502
|
+
"summary": "List every post in one thread of a unified (Microsoft 365) group inbox: the full `post` resources with the HTML `body.content`, `from`, `sender`, `receivedDateTime` and `hasAttachments`, where `list-group-threads` stops at a truncated `preview`. Graph returns the whole thread in one call with no page cursor, and it silently ignores `$top`, `$skip` and `$orderby` while rejecting `$filter` (probed live 2026-09-03), so only `--select` and `--expand` are exposed; sort on `receivedDateTime` client-side if order matters. `sender` is the person who wrote the post and `from` is normally the group's own address. Render one post as markdown with `convert-group-post-to-markdown`. Access is membership-gated, not scope-gated: a group the signed-in user does not belong to answers `ErrorAccessDenied` even though `list-groups` lists it.",
|
|
4503
|
+
"category": "mail",
|
|
4504
|
+
"graphMethod": "GET",
|
|
4505
|
+
"graphPathTemplate": "/groups/{group-id}/threads/{thread-id}/posts",
|
|
4506
|
+
"graphDocsUrl": "https://learn.microsoft.com/en-us/graph/api/conversationthread-list-posts",
|
|
4507
|
+
"options": [
|
|
4508
|
+
{
|
|
4509
|
+
"name": "group-id",
|
|
4510
|
+
"key": "groupId",
|
|
4511
|
+
"required": true,
|
|
4512
|
+
"description": "Azure AD group object ID for a unified (Microsoft 365) group the signed-in user belongs to."
|
|
4513
|
+
},
|
|
4514
|
+
{
|
|
4515
|
+
"name": "thread-id",
|
|
4516
|
+
"key": "threadId",
|
|
4517
|
+
"required": true,
|
|
4518
|
+
"description": "Conversation thread ID, the `id` of a `list-group-threads` entry (also inlined by `list-group-conversations --expand threads`)."
|
|
4519
|
+
},
|
|
4520
|
+
{
|
|
4521
|
+
"name": "select",
|
|
4522
|
+
"key": "select",
|
|
4523
|
+
"required": false,
|
|
4524
|
+
"description": "OData $select: comma-separated list of fields to include in each item (e.g. `id,subject,from`). May shrink payloads dramatically — Graph honors $select on most endpoints, but some collections (notably `/me/mailboxSettings`, `/me/outlook/masterCategories`, `/me/mailFolders/inbox/messageRules`) silently ignore it and always return the full resource. Bogus field names are silently dropped by Graph — if a field you asked for is missing from the response, double-check the spelling against the documented `responseShape`."
|
|
4525
|
+
},
|
|
4526
|
+
{
|
|
4527
|
+
"name": "expand",
|
|
4528
|
+
"key": "expand",
|
|
4529
|
+
"required": false,
|
|
4530
|
+
"description": "OData $expand: navigation properties to include inline (e.g. `attachments`). Increases response size; use sparingly."
|
|
4531
|
+
}
|
|
4532
|
+
],
|
|
4533
|
+
"example": "ask-marcel-office list-group-thread-posts --group-id 'a1b2c3d4-...' --thread-id 'AAQkAD...'",
|
|
4534
|
+
"responseShape": "collection of Microsoft Graph `post` resources under `value[]`: `id`, `createdDateTime`, `lastModifiedDateTime`, `changeKey`, `categories`, `receivedDateTime`, `hasAttachments`, `body { contentType, content }`, `from`, `sender`. No `nextLink` is ever emitted. `hasAttachments` is false for a post whose only attachments are inline images. `--expand attachments` inlines every attachment of every post with its base64 `contentBytes`, so use it sparingly."
|
|
4535
|
+
},
|
|
4253
4536
|
{
|
|
4254
4537
|
"name": "list-group-threads",
|
|
4255
|
-
"summary": "List threads in a unified (Microsoft 365) group inbox. Threads are flatter than conversations — one per topic, useful when conversation-level grouping isn't needed. Only Microsoft 365 groups have a mailbox — security and distribution groups return `MailboxNotEnabledForRESTAPI`.",
|
|
4538
|
+
"summary": "List threads in a unified (Microsoft 365) group inbox. Threads are flatter than conversations — one per topic, useful when conversation-level grouping isn't needed. Only Microsoft 365 groups have a mailbox — security and distribution groups return `MailboxNotEnabledForRESTAPI`. Each thread carries only a truncated `preview` of its latest post: read the full posts with `list-group-thread-posts`, or pass `--expand posts` here to inline them.",
|
|
4256
4539
|
"category": "mail",
|
|
4257
4540
|
"graphMethod": "GET",
|
|
4258
4541
|
"graphPathTemplate": "/groups/{group-id}/threads",
|
|
@@ -4282,12 +4565,6 @@
|
|
|
4282
4565
|
"required": false,
|
|
4283
4566
|
"description": "OData $select: comma-separated list of fields to include in each item (e.g. `id,subject,from`). May shrink payloads dramatically — Graph honors $select on most endpoints, but some collections (notably `/me/mailboxSettings`, `/me/outlook/masterCategories`, `/me/mailFolders/inbox/messageRules`) silently ignore it and always return the full resource. Bogus field names are silently dropped by Graph — if a field you asked for is missing from the response, double-check the spelling against the documented `responseShape`."
|
|
4284
4567
|
},
|
|
4285
|
-
{
|
|
4286
|
-
"name": "filter",
|
|
4287
|
-
"key": "filter",
|
|
4288
|
-
"required": false,
|
|
4289
|
-
"description": "OData $filter: predicate to narrow results server-side. Quoting rules: string literals MUST use SINGLE quotes (`subject eq 'invoice'`), NOT double quotes — Graph rejects `subject eq \"invoice\"` with `InvalidFilterClause`. To embed a single quote inside a string, double it (`subject eq 'O''Brien'`). Booleans, numbers, and dates are unquoted (`isRead eq false`, `receivedDateTime ge 2026-01-01T00:00:00Z`). Wrap the whole flag value in shell DOUBLE quotes so the inner single quotes survive (`--filter \"subject eq 'invoice'\"`). Same syntax Graph documents per resource type."
|
|
4290
|
-
},
|
|
4291
4568
|
{
|
|
4292
4569
|
"name": "orderby",
|
|
4293
4570
|
"key": "orderby",
|
|
@@ -4533,6 +4810,19 @@
|
|
|
4533
4810
|
"required": true,
|
|
4534
4811
|
"description": "mailFolder ID. Returned by `ask-marcel-office list-mail-folders`. Well-known names also work, e.g. `inbox`, `sentitems`, `drafts`."
|
|
4535
4812
|
},
|
|
4813
|
+
{
|
|
4814
|
+
"name": "include-hidden-folders",
|
|
4815
|
+
"key": "includeHiddenFolders",
|
|
4816
|
+
"required": false,
|
|
4817
|
+
"description": "Graph HIDES folders flagged as hidden from this listing by default (a mailbox typically hides system folders such as `Social Activity Notifications`, and an Outlook client can hide any folder). Pass `true` to append the plain `includeHiddenFolders=true` query parameter and get them as well; every returned folder then carries `isHidden` to tell the two apart. Omit it for the default visible-only listing.",
|
|
4818
|
+
"argumentHint": {
|
|
4819
|
+
"kind": "magicValue",
|
|
4820
|
+
"values": [
|
|
4821
|
+
"true",
|
|
4822
|
+
"false"
|
|
4823
|
+
]
|
|
4824
|
+
}
|
|
4825
|
+
},
|
|
4536
4826
|
{
|
|
4537
4827
|
"name": "top",
|
|
4538
4828
|
"key": "top",
|
|
@@ -4680,6 +4970,19 @@
|
|
|
4680
4970
|
"graphPathTemplate": "/me/mailFolders",
|
|
4681
4971
|
"graphDocsUrl": "https://learn.microsoft.com/en-us/graph/api/user-list-mailfolders",
|
|
4682
4972
|
"options": [
|
|
4973
|
+
{
|
|
4974
|
+
"name": "include-hidden-folders",
|
|
4975
|
+
"key": "includeHiddenFolders",
|
|
4976
|
+
"required": false,
|
|
4977
|
+
"description": "Graph HIDES folders flagged as hidden from this listing by default (a mailbox typically hides system folders such as `Social Activity Notifications`, and an Outlook client can hide any folder). Pass `true` to append the plain `includeHiddenFolders=true` query parameter and get them as well; every returned folder then carries `isHidden` to tell the two apart. Omit it for the default visible-only listing.",
|
|
4978
|
+
"argumentHint": {
|
|
4979
|
+
"kind": "magicValue",
|
|
4980
|
+
"values": [
|
|
4981
|
+
"true",
|
|
4982
|
+
"false"
|
|
4983
|
+
]
|
|
4984
|
+
}
|
|
4985
|
+
},
|
|
4683
4986
|
{
|
|
4684
4987
|
"name": "top",
|
|
4685
4988
|
"key": "top",
|
|
@@ -5688,9 +5991,83 @@
|
|
|
5688
5991
|
"responseShape": "collection of Microsoft Graph `sharedInsight` resources under `value[]`",
|
|
5689
5992
|
"pagination": true
|
|
5690
5993
|
},
|
|
5994
|
+
{
|
|
5995
|
+
"name": "list-shared-mailbox-child-folders",
|
|
5996
|
+
"summary": "List the subfolders of one mail folder in a shared or delegated mailbox. The `/me` sibling is `list-mail-child-folders`. Walk it from the folder IDs `list-shared-mailbox-folders` returns to reach nested custom folders. Requires the delegated `Mail.Read.Shared` scope, which neither token this CLI can mint carries (verified live on two tenants, 2026-08-30), so any mailbox other than the signed-in user's own is expected to answer `ErrorAccessDenied` whatever delegation Exchange holds. Your own UPN works; a Microsoft 365 group's mailbox is the shared-mail path that does (`list-group-conversations`, `list-group-thread-posts`).",
|
|
5997
|
+
"category": "mail",
|
|
5998
|
+
"graphMethod": "GET",
|
|
5999
|
+
"graphPathTemplate": "/users/{user-id}/mailFolders/{mail-folder-id}/childFolders",
|
|
6000
|
+
"graphDocsUrl": "https://learn.microsoft.com/en-us/graph/api/mailfolder-list-childfolders",
|
|
6001
|
+
"options": [
|
|
6002
|
+
{
|
|
6003
|
+
"name": "user-id",
|
|
6004
|
+
"key": "userId",
|
|
6005
|
+
"required": true,
|
|
6006
|
+
"description": "Azure AD user ID or UPN of the shared mailbox or delegated user. The signed-in user must have `Mail.Read.Shared` access (granted by the mailbox owner)."
|
|
6007
|
+
},
|
|
6008
|
+
{
|
|
6009
|
+
"name": "mail-folder-id",
|
|
6010
|
+
"key": "mailFolderId",
|
|
6011
|
+
"required": true,
|
|
6012
|
+
"description": "Mail folder ID or well-known name (`inbox`, `sentitems`, etc.) inside that mailbox. Returned by `ask-marcel-office list-shared-mailbox-folders`."
|
|
6013
|
+
},
|
|
6014
|
+
{
|
|
6015
|
+
"name": "include-hidden-folders",
|
|
6016
|
+
"key": "includeHiddenFolders",
|
|
6017
|
+
"required": false,
|
|
6018
|
+
"description": "Graph HIDES folders flagged as hidden from this listing by default (a mailbox typically hides system folders such as `Social Activity Notifications`, and an Outlook client can hide any folder). Pass `true` to append the plain `includeHiddenFolders=true` query parameter and get them as well; every returned folder then carries `isHidden` to tell the two apart. Omit it for the default visible-only listing.",
|
|
6019
|
+
"argumentHint": {
|
|
6020
|
+
"kind": "magicValue",
|
|
6021
|
+
"values": [
|
|
6022
|
+
"true",
|
|
6023
|
+
"false"
|
|
6024
|
+
]
|
|
6025
|
+
}
|
|
6026
|
+
},
|
|
6027
|
+
{
|
|
6028
|
+
"name": "top",
|
|
6029
|
+
"key": "top",
|
|
6030
|
+
"required": false,
|
|
6031
|
+
"description": "OData $top: maximum number of items to return on this page (positive integer, ≤ 1000). Graph silently caps at 1000 on every collection endpoint, so the CLI rejects larger values with a clear validation error rather than letting the request silently truncate. Combine with `next-page` to paginate beyond the cap."
|
|
6032
|
+
},
|
|
6033
|
+
{
|
|
6034
|
+
"name": "skip",
|
|
6035
|
+
"key": "skip",
|
|
6036
|
+
"required": false,
|
|
6037
|
+
"description": "OData $skip: skip the first N items before returning results (non-negative integer). Useful with $top for offset paging."
|
|
6038
|
+
},
|
|
6039
|
+
{
|
|
6040
|
+
"name": "select",
|
|
6041
|
+
"key": "select",
|
|
6042
|
+
"required": false,
|
|
6043
|
+
"description": "OData $select: comma-separated list of fields to include in each item (e.g. `id,subject,from`). May shrink payloads dramatically — Graph honors $select on most endpoints, but some collections (notably `/me/mailboxSettings`, `/me/outlook/masterCategories`, `/me/mailFolders/inbox/messageRules`) silently ignore it and always return the full resource. Bogus field names are silently dropped by Graph — if a field you asked for is missing from the response, double-check the spelling against the documented `responseShape`."
|
|
6044
|
+
},
|
|
6045
|
+
{
|
|
6046
|
+
"name": "filter",
|
|
6047
|
+
"key": "filter",
|
|
6048
|
+
"required": false,
|
|
6049
|
+
"description": "OData $filter: predicate to narrow results server-side. Quoting rules: string literals MUST use SINGLE quotes (`subject eq 'invoice'`), NOT double quotes — Graph rejects `subject eq \"invoice\"` with `InvalidFilterClause`. To embed a single quote inside a string, double it (`subject eq 'O''Brien'`). Booleans, numbers, and dates are unquoted (`isRead eq false`, `receivedDateTime ge 2026-01-01T00:00:00Z`). Wrap the whole flag value in shell DOUBLE quotes so the inner single quotes survive (`--filter \"subject eq 'invoice'\"`). Same syntax Graph documents per resource type."
|
|
6050
|
+
},
|
|
6051
|
+
{
|
|
6052
|
+
"name": "orderby",
|
|
6053
|
+
"key": "orderby",
|
|
6054
|
+
"required": false,
|
|
6055
|
+
"description": "OData $orderby: sort expression with optional asc/desc (e.g. `receivedDateTime desc`). Some Graph filter combinations are rejected; remove $orderby if InefficientFilter occurs."
|
|
6056
|
+
},
|
|
6057
|
+
{
|
|
6058
|
+
"name": "expand",
|
|
6059
|
+
"key": "expand",
|
|
6060
|
+
"required": false,
|
|
6061
|
+
"description": "OData $expand: navigation properties to include inline (e.g. `attachments`). Increases response size; use sparingly."
|
|
6062
|
+
}
|
|
6063
|
+
],
|
|
6064
|
+
"example": "ask-marcel-office list-shared-mailbox-child-folders --user-id 'shared-mailbox@contoso.com' --mail-folder-id 'inbox'",
|
|
6065
|
+
"responseShape": "collection of Microsoft Graph `mailFolder` resources under `value[]`",
|
|
6066
|
+
"pagination": true
|
|
6067
|
+
},
|
|
5691
6068
|
{
|
|
5692
6069
|
"name": "list-shared-mailbox-folder-messages",
|
|
5693
|
-
"summary": "List messages in a single folder of a shared / delegated mailbox.",
|
|
6070
|
+
"summary": "List messages in a single folder of a shared / delegated mailbox. Requires the delegated `Mail.Read.Shared` scope, which neither token this CLI can mint carries (verified live on two tenants, 2026-08-30), so any mailbox other than the signed-in user's own is expected to answer `ErrorAccessDenied` whatever delegation Exchange holds. Your own UPN works; a Microsoft 365 group's mailbox is the shared-mail path that does (`list-group-conversations`, `list-group-thread-posts`).",
|
|
5694
6071
|
"category": "mail",
|
|
5695
6072
|
"graphMethod": "GET",
|
|
5696
6073
|
"graphPathTemplate": "/users/{user-id}/mailFolders/{mail-folder-id}/messages",
|
|
@@ -5749,9 +6126,77 @@
|
|
|
5749
6126
|
"responseShape": "collection of Microsoft Graph `message` resources under `value[]`",
|
|
5750
6127
|
"pagination": true
|
|
5751
6128
|
},
|
|
6129
|
+
{
|
|
6130
|
+
"name": "list-shared-mailbox-folders",
|
|
6131
|
+
"summary": "List the top-level mail folders of a shared or delegated mailbox. The `/me` sibling is `list-mail-folders`. Use it to discover the folder IDs that `list-shared-mailbox-folder-messages` needs: without it only the well-known names (`inbox`, `sentitems`, `drafts`, …) are reachable, so custom folders are invisible. Requires the delegated `Mail.Read.Shared` scope, which neither token this CLI can mint carries (verified live on two tenants, 2026-08-30), so any mailbox other than the signed-in user's own is expected to answer `ErrorAccessDenied` whatever delegation Exchange holds. Your own UPN works; a Microsoft 365 group's mailbox is the shared-mail path that does (`list-group-conversations`, `list-group-thread-posts`).",
|
|
6132
|
+
"category": "mail",
|
|
6133
|
+
"graphMethod": "GET",
|
|
6134
|
+
"graphPathTemplate": "/users/{user-id}/mailFolders",
|
|
6135
|
+
"graphDocsUrl": "https://learn.microsoft.com/en-us/graph/api/user-list-mailfolders",
|
|
6136
|
+
"options": [
|
|
6137
|
+
{
|
|
6138
|
+
"name": "user-id",
|
|
6139
|
+
"key": "userId",
|
|
6140
|
+
"required": true,
|
|
6141
|
+
"description": "Azure AD user ID or UPN of the shared mailbox or delegated user. The signed-in user must have `Mail.Read.Shared` access (granted by the mailbox owner)."
|
|
6142
|
+
},
|
|
6143
|
+
{
|
|
6144
|
+
"name": "include-hidden-folders",
|
|
6145
|
+
"key": "includeHiddenFolders",
|
|
6146
|
+
"required": false,
|
|
6147
|
+
"description": "Graph HIDES folders flagged as hidden from this listing by default (a mailbox typically hides system folders such as `Social Activity Notifications`, and an Outlook client can hide any folder). Pass `true` to append the plain `includeHiddenFolders=true` query parameter and get them as well; every returned folder then carries `isHidden` to tell the two apart. Omit it for the default visible-only listing.",
|
|
6148
|
+
"argumentHint": {
|
|
6149
|
+
"kind": "magicValue",
|
|
6150
|
+
"values": [
|
|
6151
|
+
"true",
|
|
6152
|
+
"false"
|
|
6153
|
+
]
|
|
6154
|
+
}
|
|
6155
|
+
},
|
|
6156
|
+
{
|
|
6157
|
+
"name": "top",
|
|
6158
|
+
"key": "top",
|
|
6159
|
+
"required": false,
|
|
6160
|
+
"description": "OData $top: maximum number of items to return on this page (positive integer, ≤ 1000). Graph silently caps at 1000 on every collection endpoint, so the CLI rejects larger values with a clear validation error rather than letting the request silently truncate. Combine with `next-page` to paginate beyond the cap."
|
|
6161
|
+
},
|
|
6162
|
+
{
|
|
6163
|
+
"name": "skip",
|
|
6164
|
+
"key": "skip",
|
|
6165
|
+
"required": false,
|
|
6166
|
+
"description": "OData $skip: skip the first N items before returning results (non-negative integer). Useful with $top for offset paging."
|
|
6167
|
+
},
|
|
6168
|
+
{
|
|
6169
|
+
"name": "select",
|
|
6170
|
+
"key": "select",
|
|
6171
|
+
"required": false,
|
|
6172
|
+
"description": "OData $select: comma-separated list of fields to include in each item (e.g. `id,subject,from`). May shrink payloads dramatically — Graph honors $select on most endpoints, but some collections (notably `/me/mailboxSettings`, `/me/outlook/masterCategories`, `/me/mailFolders/inbox/messageRules`) silently ignore it and always return the full resource. Bogus field names are silently dropped by Graph — if a field you asked for is missing from the response, double-check the spelling against the documented `responseShape`."
|
|
6173
|
+
},
|
|
6174
|
+
{
|
|
6175
|
+
"name": "filter",
|
|
6176
|
+
"key": "filter",
|
|
6177
|
+
"required": false,
|
|
6178
|
+
"description": "OData $filter: predicate to narrow results server-side. Quoting rules: string literals MUST use SINGLE quotes (`subject eq 'invoice'`), NOT double quotes — Graph rejects `subject eq \"invoice\"` with `InvalidFilterClause`. To embed a single quote inside a string, double it (`subject eq 'O''Brien'`). Booleans, numbers, and dates are unquoted (`isRead eq false`, `receivedDateTime ge 2026-01-01T00:00:00Z`). Wrap the whole flag value in shell DOUBLE quotes so the inner single quotes survive (`--filter \"subject eq 'invoice'\"`). Same syntax Graph documents per resource type."
|
|
6179
|
+
},
|
|
6180
|
+
{
|
|
6181
|
+
"name": "orderby",
|
|
6182
|
+
"key": "orderby",
|
|
6183
|
+
"required": false,
|
|
6184
|
+
"description": "OData $orderby: sort expression with optional asc/desc (e.g. `receivedDateTime desc`). Some Graph filter combinations are rejected; remove $orderby if InefficientFilter occurs."
|
|
6185
|
+
},
|
|
6186
|
+
{
|
|
6187
|
+
"name": "expand",
|
|
6188
|
+
"key": "expand",
|
|
6189
|
+
"required": false,
|
|
6190
|
+
"description": "OData $expand: navigation properties to include inline (e.g. `attachments`). Increases response size; use sparingly."
|
|
6191
|
+
}
|
|
6192
|
+
],
|
|
6193
|
+
"example": "ask-marcel-office list-shared-mailbox-folders --user-id 'shared-mailbox@contoso.com'",
|
|
6194
|
+
"responseShape": "collection of Microsoft Graph `mailFolder` resources under `value[]`",
|
|
6195
|
+
"pagination": true
|
|
6196
|
+
},
|
|
5752
6197
|
{
|
|
5753
6198
|
"name": "list-shared-mailbox-messages",
|
|
5754
|
-
"summary": "List messages from a shared or delegated mailbox the signed-in user has read access to. Same shape as `list-mail-messages` but scoped to a specific mailbox owner.
|
|
6199
|
+
"summary": "List messages from a shared or delegated mailbox the signed-in user has read access to. Same shape as `list-mail-messages` but scoped to a specific mailbox owner. Requires the delegated `Mail.Read.Shared` scope, which neither token this CLI can mint carries (verified live on two tenants, 2026-08-30), so any mailbox other than the signed-in user's own is expected to answer `ErrorAccessDenied` whatever delegation Exchange holds. Your own UPN works; a Microsoft 365 group's mailbox is the shared-mail path that does (`list-group-conversations`, `list-group-thread-posts`).",
|
|
5755
6200
|
"category": "mail",
|
|
5756
6201
|
"graphMethod": "GET",
|
|
5757
6202
|
"graphPathTemplate": "/users/{user-id}/messages",
|
|
@@ -6947,7 +7392,7 @@
|
|
|
6947
7392
|
},
|
|
6948
7393
|
{
|
|
6949
7394
|
"name": "next-page",
|
|
6950
|
-
"summary": "Fetch the next page of a paginated Graph response. Pass the cursor the previous command emitted — in text mode the `---` footer prints the whole ready-to-run command (`next: ask-marcel-office next-page --url '<url>'`), so copy the line as-is (the URL is single-quoted because it contains `$`); in JSON mode use the top-level `nextLink` field. Never reach into `data[\"@odata.nextLink\"]`; the CLI strips that and surfaces it as a first-class envelope/footer field. Automatically signs `/me/chats` and `/chats/...` cursors with the M365ChatClient elevated token to match the chat-metadata commands.",
|
|
7395
|
+
"summary": "Fetch the next page of a paginated Graph response. Pass the cursor the previous command emitted — in text mode the `---` footer prints the whole ready-to-run command (`next: ask-marcel-office next-page --url '<url>'`), so copy the line as-is (the URL is single-quoted because it contains `$`); in JSON mode use the top-level `nextLink` field. Never reach into `data[\"@odata.nextLink\"]`; the CLI strips that and surfaces it as a first-class envelope/footer field. Automatically signs `/me/chats` and `/chats/...` cursors with the M365ChatClient elevated token to match the chat-metadata commands. When the cursor came from a partner-tenant (guest) drive listing, pass the same `--tenant-id` you used on the originating command, since the cursor carries no tenant and without it page 2 fails with `invalidAudienceUri`.",
|
|
6951
7396
|
"category": "meta",
|
|
6952
7397
|
"graphMethod": "GET",
|
|
6953
7398
|
"graphPathTemplate": "{url}",
|
|
@@ -6961,6 +7406,12 @@
|
|
|
6961
7406
|
"argumentHint": {
|
|
6962
7407
|
"kind": "graphSubpath"
|
|
6963
7408
|
}
|
|
7409
|
+
},
|
|
7410
|
+
{
|
|
7411
|
+
"name": "tenant-id",
|
|
7412
|
+
"key": "tenantId",
|
|
7413
|
+
"required": false,
|
|
7414
|
+
"description": "Tenant GUID of a PARTNER tenant you are a guest in. Pass it only to continue a partner-tenant drive listing (a folder / `*-drive-item` cursor whose file lives in another tenant), using the same `--tenant-id` you gave the originating command (ultimately from `resolve-drive-share-link`). The page is then signed with a guest token for that tenant; without it a partner-tenant cursor 401s with `invalidAudienceUri` on page 2 even though page 1 succeeded. Omit it for your own tenant and for every `/me/...` and chat cursor (the normal case)."
|
|
6964
7415
|
}
|
|
6965
7416
|
],
|
|
6966
7417
|
"example": "ask-marcel-office next-page --url 'https://graph.microsoft.com/v1.0/me/messages?$skip=10'",
|
|
@@ -7091,14 +7542,14 @@
|
|
|
7091
7542
|
},
|
|
7092
7543
|
{
|
|
7093
7544
|
"name": "scopes-check",
|
|
7094
|
-
"summary": "Decode the cached Teams web client access token and return its scopes, audience, and expiry without making a Graph call. Use this as a self-test before running a command an LLM expects to fail with `accessDenied` — if the required scope isn't in the returned list, the call will reject regardless of tenant config. Each command's `scopesRequired` field in `help-json` lists the scopes that command needs; intersect with the array returned here for a pre-flight check (pipe both through `jq` and diff). The `expiresInSeconds` field lets an LLM decide pre-emptively to `login` again — typically worth doing under ~5 minutes (300 s) so a long-running session doesn't hit the wall mid-command. The `elevated` block reports whether the *separate* M365ChatClient-elevated token (needed by the historical-version download / convert commands) is cached and still usable — so a fresh process can pre-flight `deep-scan`-style workloads instead of discovering a 403 mid-run; `available:false` when it is absent, expired, or within the same 5-minute buffer the download path applies. The `chatsvcagg` and `ic3` blocks report the two Teams-chat substrate tokens (used by `list-teams-chat*` / `find-chats-with-user`) the same way; both self-heal from the shared refresh token, so they are informational rather than a preflight gate. Every tier block also lists that token's OWN granted scopes (decoded from its `scp` claim, distinct per token) and its `refresh` route (`automatic` = self-heals from the shared refresh token; `interactive` = the elevated tier, which carries no refresh token of its own and is re-captured by a browser login); the `hint` field says how to refresh them.",
|
|
7545
|
+
"summary": "Decode the cached Teams web client access token and return its scopes, audience, and expiry without making a Graph call. It never opens a browser: an expired or missing session is reported as such (negative `expiresInSeconds`, or a not-signed-in error), and `login` is the command that refreshes it. Use this as a self-test before running a command an LLM expects to fail with `accessDenied` — if the required scope isn't in the returned list, the call will reject regardless of tenant config. Each command's `scopesRequired` field in `help-json` lists the scopes that command needs; intersect with the array returned here for a pre-flight check (pipe both through `jq` and diff). The `expiresInSeconds` field lets an LLM decide pre-emptively to `login` again — typically worth doing under ~5 minutes (300 s) so a long-running session doesn't hit the wall mid-command. The `elevated` block reports whether the *separate* M365ChatClient-elevated token (needed by the historical-version download / convert commands) is cached and still usable — so a fresh process can pre-flight `deep-scan`-style workloads instead of discovering a 403 mid-run; `available:false` when it is absent, expired, or within the same 5-minute buffer the download path applies. The `chatsvcagg` and `ic3` blocks report the two Teams-chat substrate tokens (used by `list-teams-chat*` / `find-chats-with-user`) the same way; both self-heal from the shared refresh token, so they are informational rather than a preflight gate. Every tier block also lists that token's OWN granted scopes (decoded from its `scp` claim, distinct per token) and its `refresh` route (`automatic` = self-heals from the shared refresh token; `interactive` = the elevated tier, which carries no refresh token of its own and is re-captured by a browser login); the `hint` field says how to refresh them.",
|
|
7095
7546
|
"category": "meta",
|
|
7096
7547
|
"graphMethod": "GET",
|
|
7097
7548
|
"graphPathTemplate": "(meta) cached-token introspection — no Graph endpoint",
|
|
7098
7549
|
"graphDocsUrl": "https://learn.microsoft.com/en-us/graph/permissions-reference",
|
|
7099
7550
|
"options": [],
|
|
7100
7551
|
"example": "ask-marcel-office scopes-check",
|
|
7101
|
-
"responseShape": "`{ scopes: string[], audience: string, expiresAt: string (ISO 8601), expiresInSeconds: number, elevated: TokenTier, chatsvcagg: TokenTier, ic3: TokenTier, hint: string }` where `TokenTier = { available: boolean, expiresInSeconds?: number, scopes: string[], refresh: \"automatic\" | \"interactive\", reason?: string }`. Top-level `scopes`/`audience`/`expiresAt`/`expiresInSeconds` describe the basic Teams token (back-compat). Each tier block also carries that token's OWN `scopes` (decoded from its `scp` claim: elevated ~20 Graph scopes, chatsvcagg `user_impersonation`, ic3 `Teams.AccessAsUser.All`) and its `refresh` route (`automatic` = self-heals from the shared refresh token; `interactive` = the elevated token, re-captured only by a browser login). `available` is `true` only when the token is present and beyond the 5-minute buffer; `expiresInSeconds` is the raw remaining seconds (negative when expired) and is omitted when the token is absent. `reason` is present ONLY when `available` is `false` — a plain-language note on why the tier is missing and how to restore it (so an empty `scopes: []` on an absent token is not mistaken for \"no scopes\"); it is omitted when the token is available. `hint` names `login --force` as the single refresh action."
|
|
7552
|
+
"responseShape": "`{ scopes: string[], audience: string, expiresAt: string (ISO 8601), expiresInSeconds: number, elevated: TokenTier, chatsvcagg: TokenTier, ic3: TokenTier, hint: string }` where `TokenTier = { available: boolean, expiresInSeconds?: number, scopes: string[], refresh: \"automatic\" | \"interactive\", reason?: string }`. Top-level `scopes`/`audience`/`expiresAt`/`expiresInSeconds` describe the basic Teams token (back-compat). Each tier block also carries that token's OWN `scopes` (decoded from its `scp` claim: elevated ~20 Graph scopes, chatsvcagg `user_impersonation`, ic3 `Teams.AccessAsUser.All`) and its `refresh` route (`automatic` = self-heals from the shared refresh token; `interactive` = the elevated token, re-captured only by a browser login). `available` is `true` only when the token is present and beyond the 5-minute buffer; `expiresInSeconds` is the raw remaining seconds (negative when expired) and is omitted when the token is absent. `reason` is present ONLY when `available` is `false` — a plain-language note on why the tier is missing and how to restore it (so an empty `scopes: []` on an absent token is not mistaken for \"no scopes\"); it is omitted when the token is available. `hint` names `login --force` as the single refresh action. EVERY tier — `basic` included — is reported as its own block with the SAME shape, and each block carries a `reads` line: a plain-language description of what that tier lets you read (e.g. `chatsvcagg` -> \"Teams chat message content\"), so a caller never has to know what a tier codename means. The `basic` block mirrors the loose top-level `scopes`/`expiresAt`/`expiresInSeconds` fields, which are still emitted unchanged for back-compat; prefer the block. `basic.available` applies the same 300-second freshness buffer as the other tiers, so `available` means the same thing everywhere."
|
|
7102
7553
|
},
|
|
7103
7554
|
{
|
|
7104
7555
|
"name": "search-all-accessible-sites",
|