ask-marcel-office-cli 1.5.1 → 2.0.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 +85 -3
- package/README.md +21 -58
- package/dist/cli.js +1620 -1753
- package/dist/commands.json +385 -277
- package/dist/composition/build-deps.d.ts +7 -8
- package/dist/composition/cli.d.ts +5 -4
- package/dist/domain/iso-datetime.d.ts +1 -1
- package/dist/index.js +1503 -1640
- package/dist/infra/auth.d.ts +6 -20
- package/dist/infra/browser-auth.d.ts +11 -12
- package/dist/infra/graph-client.d.ts +2 -2
- package/dist/presenter/error-hints.d.ts +9 -9
- package/dist/use-cases/commands/build-command.d.ts +1 -1
- package/dist/use-cases/commands/command-types.d.ts +5 -5
- package/dist/use-cases/commands/create-reply-draft.d.ts +14 -0
- package/dist/use-cases/commands/docs.d.ts +1 -1
- package/dist/use-cases/commands/get-schedule.d.ts +11 -0
- package/dist/use-cases/commands/graph-scopes.d.ts +1 -1
- package/dist/use-cases/commands/iso-datetime-schema.d.ts +1 -1
- package/dist/use-cases/commands/markdown-pipeline.d.ts +5 -5
- package/dist/use-cases/commands/option-descriptions.d.ts +1 -1
- package/dist/use-cases/commands/output-path.d.ts +4 -4
- package/dist/use-cases/commands/zip-archive-to-markdown.d.ts +1 -1
- package/dist/use-cases/ports/filesystem.d.ts +1 -1
- package/docs/COMMANDS.md +14 -12
- package/docs/USAGE.md +23 -23
- package/package.json +3 -4
- package/dist/composition/env.d.ts +0 -2
- package/dist/infra/system-browser-auth.d.ts +0 -35
- package/dist/infra/system-browser-loader.d.ts +0 -3
- package/dist/infra/token-callback-server.d.ts +0 -31
- package/docs/commands.json +0 -7677
package/docs/USAGE.md
CHANGED
|
@@ -15,15 +15,15 @@ npm i -g ask-marcel-office-cli
|
|
|
15
15
|
bun add -g ask-marcel-office-cli
|
|
16
16
|
|
|
17
17
|
# authenticate (cached → refresh → browser fallback)
|
|
18
|
-
ask-marcel login
|
|
18
|
+
ask-marcel-office login
|
|
19
19
|
|
|
20
20
|
# the rest is discoverable
|
|
21
|
-
ask-marcel --help # ~34 KB, one-sentence summaries
|
|
22
|
-
ask-marcel help-json --terse --category mail # ~6 KB JSON for one category
|
|
23
|
-
ask-marcel docs list-mail-messages # full per-command Markdown
|
|
21
|
+
ask-marcel-office --help # ~34 KB, one-sentence summaries
|
|
22
|
+
ask-marcel-office help-json --terse --category mail # ~6 KB JSON for one category
|
|
23
|
+
ask-marcel-office docs list-mail-messages # full per-command Markdown
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
`ask-marcel update` auto-detects whether the CLI was installed via npm or bun (based on the bin path) and reinstalls globally with the matching tool. From a clone you can keep using `bun run src/main.ts <command>` directly.
|
|
26
|
+
`ask-marcel-office update` auto-detects whether the CLI was installed via npm or bun (based on the bin path) and reinstalls globally with the matching tool. From a clone you can keep using `bun run src/main.ts <command>` directly.
|
|
27
27
|
|
|
28
28
|
The first launch prints a one-time notice if a newer version is on npm.
|
|
29
29
|
|
|
@@ -36,12 +36,12 @@ Every command writes its output as a single document to **stdout** (success or e
|
|
|
36
36
|
YAML-ish `key: value` lines, generally smaller than the JSON envelope on long listings (the win grows with page size and shrinks toward parity on small projected pages — a 3-message page is ~3.3 KB in either format). Errors render as `error: <message>` followed by `hint:`, `source:`, and (when Graph throttles) `retryAfter: Ns` lines so an LLM can match the line shape without parsing JSON. Designed for LLMs reading and summarising; not for piping into other tools.
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
$ ask-marcel get-current-user
|
|
39
|
+
$ ask-marcel-office get-current-user
|
|
40
40
|
id: 0c1d2e3f-…
|
|
41
|
-
displayName:
|
|
42
|
-
mail:
|
|
41
|
+
displayName: Jordan Avery
|
|
42
|
+
mail: jordan.avery@example.com
|
|
43
43
|
|
|
44
|
-
$ ask-marcel list-mail-folder-messages --mail-folder-id inbox --top 2
|
|
44
|
+
$ ask-marcel-office list-mail-folder-messages --mail-folder-id inbox --top 2
|
|
45
45
|
id: AAMkAGI2…
|
|
46
46
|
subject: Re: Q2 planning
|
|
47
47
|
from: alice@example.com
|
|
@@ -52,7 +52,7 @@ from: bob@example.com
|
|
|
52
52
|
|
|
53
53
|
--- next: https://graph.microsoft.com/v1.0/me/messages?$skip=2
|
|
54
54
|
|
|
55
|
-
$ ask-marcel get-mail-message --message-id "bad-id"
|
|
55
|
+
$ ask-marcel-office get-mail-message --message-id "bad-id"
|
|
56
56
|
error: ErrorInvalidIdMalformed: Id is malformed.
|
|
57
57
|
hint: The ID you passed isn't valid for this endpoint. Source IDs from a sibling `list-*` command (e.g. `list-mail-messages`, `list-folder-files`, `list-chats`) — never construct them by hand.
|
|
58
58
|
source: graph
|
|
@@ -102,9 +102,9 @@ The stable `{ok, data, nextLink?, deltaLink?, count?}` envelope, unambiguous for
|
|
|
102
102
|
Most `list-*`, `search-*`, and `*-delta` commands accept the standard OData query parameters as optional flags. Use them to shrink large responses on the fly — particularly important for context-window-bound LLM consumers:
|
|
103
103
|
|
|
104
104
|
```bash
|
|
105
|
-
ask-marcel list-mail-messages --top 5 --select id,subject,from,receivedDateTime
|
|
106
|
-
ask-marcel list-recent-files --filter "name eq 'budget.xlsx'" --orderby lastModifiedDateTime desc
|
|
107
|
-
ask-marcel list-folder-files --drive-id b!abc --item-id 01DEF --select id,name --top 10
|
|
105
|
+
ask-marcel-office list-mail-messages --top 5 --select id,subject,from,receivedDateTime
|
|
106
|
+
ask-marcel-office list-recent-files --filter "name eq 'budget.xlsx'" --orderby lastModifiedDateTime desc
|
|
107
|
+
ask-marcel-office list-folder-files --drive-id b!abc --item-id 01DEF --select id,name --top 10
|
|
108
108
|
```
|
|
109
109
|
|
|
110
110
|
The canonical set is `--top <n>`, `--skip <n>`, `--select <csv>`, `--filter <kql>`, `--orderby <kql>`, `--expand <nav>`. `--top` is capped at 1000 with a clear validation error (Graph silently truncates beyond that on every endpoint). **The CLI advertises only the flags the underlying Graph endpoint honors — flags Graph silently rejects or ignores are dropped from the option set, so the manifest never lies.**
|
|
@@ -124,10 +124,10 @@ Narrower variants (a sample — see each command's `--help` for the exact list):
|
|
|
124
124
|
Every `--start-date-time` / `--end-date-time` flag on the calendar-view family (`list-calendar-view`, `list-calendar-view-delta`, `list-specific-calendar-view`, `list-shared-calendar-view`, `list-group-calendar-view`, `list-calendar-event-instances`) accepts strict ISO 8601 (`2026-04-01T00:00:00Z`) AND a relative vocabulary, so an LLM doesn't have to compute timestamps before answering "what's on my calendar this week":
|
|
125
125
|
|
|
126
126
|
```bash
|
|
127
|
-
ask-marcel list-calendar-view --start-date-time "start-of-week" --end-date-time "end-of-week"
|
|
128
|
-
ask-marcel list-calendar-view --start-date-time "today" --end-date-time "+7d"
|
|
129
|
-
ask-marcel list-calendar-view --start-date-time "monday" --end-date-time "next-monday"
|
|
130
|
-
ask-marcel list-calendar-view --start-date-time "start-of-month" --end-date-time "end-of-month"
|
|
127
|
+
ask-marcel-office list-calendar-view --start-date-time "start-of-week" --end-date-time "end-of-week"
|
|
128
|
+
ask-marcel-office list-calendar-view --start-date-time "today" --end-date-time "+7d"
|
|
129
|
+
ask-marcel-office list-calendar-view --start-date-time "monday" --end-date-time "next-monday"
|
|
130
|
+
ask-marcel-office list-calendar-view --start-date-time "start-of-month" --end-date-time "end-of-month"
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
Accepted shapes (UTC, week starts Monday): strict ISO; date-only (`2026-04-01` → midnight UTC); past offsets `7d` / `1w` / `2h` / `30m`; future offsets `+7d` / `+1w`; named `now` / `today` / `yesterday` / `tomorrow`; weekday names (`monday`-`sunday` — most-recent occurrence including today); `last-<weekday>` / `next-<weekday>`; boundary anchors `start-of-week|month|year`, `end-of-week|month|year`. An unrecognised input returns a structured validation error listing every accepted shape — no second round-trip needed.
|
|
@@ -137,7 +137,7 @@ Accepted shapes (UTC, week starts Monday): strict ISO; date-only (`2026-04-01`
|
|
|
137
137
|
Every download / convert command (PDF, image, raw bytes, MIME, OneNote HTML, the markdown converters) returns its bytes as `{ contentType, size, base64 }` (binary) or `{ contentType, size, text }` (text). In default text mode the binary variant prints `binary: <contentType>, <size> bytes — use --output-path to save` rather than spilling base64 to stdout. For multi-MB payloads — a 5 MB PDF round-tripped through stdout would blow most LLM context windows — pass the **global** `--output-path <path>` flag and the CLI lands the bytes locally:
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
|
-
ask-marcel convert-mail-attachment-to-pdf \
|
|
140
|
+
ask-marcel-office convert-mail-attachment-to-pdf \
|
|
141
141
|
--message-id "AAMkAD..." --attachment-id "AAMkAD...attach1" \
|
|
142
142
|
--output-path /tmp/deck.pdf
|
|
143
143
|
# Text mode:
|
|
@@ -158,21 +158,21 @@ When a response contains a `nextLink` cursor, feed that URL back through `next-p
|
|
|
158
158
|
|
|
159
159
|
```bash
|
|
160
160
|
# page 1
|
|
161
|
-
ask-marcel --output json list-mail-folders > p1.json
|
|
161
|
+
ask-marcel-office --output json list-mail-folders > p1.json
|
|
162
162
|
|
|
163
163
|
# page 2..N — loop until nextLink is gone
|
|
164
164
|
next=$(jq -r '.nextLink // empty' p1.json)
|
|
165
165
|
while [ -n "$next" ]; do
|
|
166
|
-
ask-marcel --output json next-page --url "$next" > pN.json
|
|
166
|
+
ask-marcel-office --output json next-page --url "$next" > pN.json
|
|
167
167
|
next=$(jq -r '.nextLink // empty' pN.json)
|
|
168
168
|
done
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
-
Every paginated command advertises this in three places: `ask-marcel <cmd> --help` prints a `Pagination:` line, `ask-marcel docs <cmd>` adds a `**Pagination:**` field, and [`docs/commands.json`](commands.json) ships `"pagination": true` on each entry so agents can detect it programmatically.
|
|
171
|
+
Every paginated command advertises this in three places: `ask-marcel-office <cmd> --help` prints a `Pagination:` line, `ask-marcel-office docs <cmd>` adds a `**Pagination:**` field, and [`docs/commands.json`](commands.json) ships `"pagination": true` on each entry so agents can detect it programmatically.
|
|
172
172
|
|
|
173
173
|
## Quick context
|
|
174
174
|
|
|
175
|
-
`ask-marcel my-quick-context` returns `{ user, primaryDriveId, inboxId, todoLists, primaryCalendarId }` in a single round trip — five Graph calls in parallel. Use it as the first call in any LLM session that needs per-user IDs to feed into other commands.
|
|
175
|
+
`ask-marcel-office my-quick-context` returns `{ user, primaryDriveId, inboxId, todoLists, primaryCalendarId }` in a single round trip — five Graph calls in parallel. Use it as the first call in any LLM session that needs per-user IDs to feed into other commands.
|
|
176
176
|
|
|
177
177
|
## Library API
|
|
178
178
|
|
|
@@ -231,7 +231,7 @@ src/
|
|
|
231
231
|
|
|
232
232
|
`download-drive-item-version --format <original|pdf|markdown>` needs a Graph token whose `appid` is on Microsoft's ODSP allow-list — the Teams web client token returns 403 with `logicalPermissionAccessDenied` against historical-version bytes.
|
|
233
233
|
|
|
234
|
-
Login captures a *second* Graph token from `https://m365.cloud.microsoft/search` whose first-party identity is M365ChatClient (`c0ab8ce9-e9a0-42e7-b064-33d422df41f1`) — an app on the ODSP allow-list. It is stored alongside the Teams token (`elevated_access_token` / `elevated_expires_on` fields in the cache) and used only by the historical-version command. Refresh path is re-capture via a brief Edge launch — the persistent profile cookies do silent SSO when fresh; if the federated IdP session has lapsed
|
|
234
|
+
Login captures a *second* Graph token from `https://m365.cloud.microsoft/search` whose first-party identity is M365ChatClient (`c0ab8ce9-e9a0-42e7-b064-33d422df41f1`) — an app on the ODSP allow-list. It is stored alongside the Teams token (`elevated_access_token` / `elevated_expires_on` fields in the cache) and used only by the historical-version command. Refresh path is re-capture via a brief Edge launch — the persistent profile cookies do silent SSO when fresh; if the federated IdP session has lapsed, interactive sign-in completes inside the popup. If the elevated capture fails at login, every other command (including `list-chats` / `get-chat`, which use the regular Teams token) still works.
|
|
235
235
|
|
|
236
236
|
## Configuration
|
|
237
237
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ask-marcel-office-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Microsoft Graph CLI + library \u2014 typed Bun/TypeScript wrapper around 150+ Graph operations (read + on-the-fly PDF/markdown conversion + federated Microsoft Search) reachable from a Teams browser-OAuth token.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Vincent Delacourt <vincent.delacourt@adama-development.com>",
|
|
@@ -25,11 +25,10 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"bin": {
|
|
28
|
-
"ask-marcel": "./dist/cli.js"
|
|
28
|
+
"ask-marcel-office": "./dist/cli.js"
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
31
31
|
"dist/",
|
|
32
|
-
"docs/commands.json",
|
|
33
32
|
"docs/COMMANDS.md",
|
|
34
33
|
"docs/USAGE.md",
|
|
35
34
|
"CHANGELOG.md",
|
|
@@ -69,7 +68,7 @@
|
|
|
69
68
|
"docs:gen": "bun run scripts/gen-docs.ts",
|
|
70
69
|
"build": "rm -rf dist && bun run docs:gen && bun run build:js && bun run build:types && bun run scripts/fix-dts-extensions.ts && bun run scripts/add-shebang.ts && cp docs/commands.json dist/commands.json",
|
|
71
70
|
"prepublishOnly": "bun run build && bun test && bun run lint:strict && bun run typecheck && bun run coverage",
|
|
72
|
-
"build:bin": "mkdir -p dist/bin && bun build src/main.ts --compile --target=bun-darwin-arm64 --external playwright --outfile dist/bin/ask-marcel-mac-arm64 && bun build src/main.ts --compile --target=bun-darwin-x64 --external playwright --outfile dist/bin/ask-marcel-mac-x64 && bun build src/main.ts --compile --target=bun-linux-x64 --external playwright --outfile dist/bin/ask-marcel-linux && bun build src/main.ts --compile --target=bun-windows-x64 --external playwright --outfile dist/bin/ask-marcel-windows.exe"
|
|
71
|
+
"build:bin": "mkdir -p dist/bin && bun build src/main.ts --compile --target=bun-darwin-arm64 --external playwright --outfile dist/bin/ask-marcel-office-mac-arm64 && bun build src/main.ts --compile --target=bun-darwin-x64 --external playwright --outfile dist/bin/ask-marcel-office-mac-x64 && bun build src/main.ts --compile --target=bun-linux-x64 --external playwright --outfile dist/bin/ask-marcel-office-linux && bun build src/main.ts --compile --target=bun-windows-x64 --external playwright --outfile dist/bin/ask-marcel-office-windows.exe"
|
|
73
72
|
},
|
|
74
73
|
"devDependencies": {
|
|
75
74
|
"@eslint/js": "^9.28.0",
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { AccessToken } from '../domain/access-token.js';
|
|
2
|
-
import type { Result } from '../domain/result.js';
|
|
3
|
-
import type { Logger } from '../use-cases/ports/logger.js';
|
|
4
|
-
type SystemBrowserAuthDeps = {
|
|
5
|
-
readonly logger: Logger;
|
|
6
|
-
readonly timeoutMs?: number;
|
|
7
|
-
readonly extensionTimeoutMs?: number;
|
|
8
|
-
};
|
|
9
|
-
type SystemBrowserTokenResult = {
|
|
10
|
-
readonly accessToken: AccessToken;
|
|
11
|
-
readonly refreshToken: string | null;
|
|
12
|
-
readonly elevatedAccessToken?: AccessToken | null;
|
|
13
|
-
readonly chatsvcaggAccessToken?: AccessToken | null;
|
|
14
|
-
readonly ic3AccessToken?: AccessToken | null;
|
|
15
|
-
readonly chatsvcaggRegion?: string;
|
|
16
|
-
};
|
|
17
|
-
type SystemBrowserAuthError = {
|
|
18
|
-
type: 'server_bind_failed';
|
|
19
|
-
message: string;
|
|
20
|
-
} | {
|
|
21
|
-
type: 'browser_open_failed';
|
|
22
|
-
message: string;
|
|
23
|
-
} | {
|
|
24
|
-
type: 'extension_timeout';
|
|
25
|
-
message: string;
|
|
26
|
-
} | {
|
|
27
|
-
type: 'invalid_token';
|
|
28
|
-
message: string;
|
|
29
|
-
} | {
|
|
30
|
-
type: 'cancelled';
|
|
31
|
-
message: string;
|
|
32
|
-
};
|
|
33
|
-
declare const authenticateViaSystemBrowser: (deps: SystemBrowserAuthDeps) => Promise<Result<SystemBrowserTokenResult, SystemBrowserAuthError>>;
|
|
34
|
-
export { authenticateViaSystemBrowser };
|
|
35
|
-
export type { SystemBrowserAuthDeps, SystemBrowserAuthError, SystemBrowserTokenResult };
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { Result } from '../domain/result.js';
|
|
2
|
-
import type { Logger } from '../use-cases/ports/logger.js';
|
|
3
|
-
type TokenCallbackPayload = {
|
|
4
|
-
readonly access_token: string;
|
|
5
|
-
readonly refresh_token?: string;
|
|
6
|
-
readonly elevated_access_token?: string;
|
|
7
|
-
readonly chatsvcagg_access_token?: string;
|
|
8
|
-
readonly ic3_access_token?: string;
|
|
9
|
-
readonly chatsvcagg_region?: string;
|
|
10
|
-
};
|
|
11
|
-
type TokenCallbackServer = {
|
|
12
|
-
readonly port: number;
|
|
13
|
-
readonly start: () => Promise<Result<TokenCallbackPayload, TokenCallbackError>>;
|
|
14
|
-
readonly stop: () => Promise<void>;
|
|
15
|
-
};
|
|
16
|
-
type TokenCallbackError = {
|
|
17
|
-
type: 'bind_failed';
|
|
18
|
-
message: string;
|
|
19
|
-
} | {
|
|
20
|
-
type: 'timeout';
|
|
21
|
-
message: string;
|
|
22
|
-
} | {
|
|
23
|
-
type: 'invalid_payload';
|
|
24
|
-
message: string;
|
|
25
|
-
} | {
|
|
26
|
-
type: 'server_closed';
|
|
27
|
-
message: string;
|
|
28
|
-
};
|
|
29
|
-
declare const createTokenCallbackServer: (logger: Logger, timeoutMs?: number) => TokenCallbackServer;
|
|
30
|
-
export { createTokenCallbackServer };
|
|
31
|
-
export type { TokenCallbackError, TokenCallbackPayload, TokenCallbackServer };
|