ask-marcel-office-cli 2.1.0 → 2.2.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 +203 -0
- package/README.md +178 -119
- package/dist/cli.js +44806 -27142
- package/dist/commands.json +371 -52
- package/dist/composition/build-deps.d.ts +8 -0
- package/dist/composition/mcp.d.ts +20 -0
- package/dist/composition/run-registry-command.d.ts +34 -0
- package/dist/domain/tenant-id.d.ts +9 -0
- package/dist/domain/utilities/spo-tenant.d.ts +20 -0
- package/dist/index.js +2735 -1674
- package/dist/infra/auth.d.ts +36 -14
- package/dist/infra/browser-auth.d.ts +7 -2
- package/dist/infra/graph-client.d.ts +52 -16
- package/dist/presenter/graph-cursor.d.ts +2 -0
- package/dist/presenter/output.d.ts +1 -1
- package/dist/presenter/render-to-string.d.ts +15 -0
- package/dist/use-cases/commands/build-command.d.ts +19 -2
- package/dist/use-cases/commands/command-types.d.ts +16 -11
- package/dist/use-cases/commands/convert-calendar-event-attachment-to-markdown.d.ts +4 -0
- package/dist/use-cases/commands/{convert-drive-item-zip.d.ts → convert-drive-item-zip-to-markdown.d.ts} +5 -0
- package/dist/use-cases/commands/{convert-local-file.d.ts → convert-local-file-to-markdown.d.ts} +4 -0
- package/dist/use-cases/commands/convert-mail-attachment-to-markdown.d.ts +10 -2
- package/dist/use-cases/commands/{convert-mail-attachment-zip.d.ts → convert-mail-attachment-zip-to-markdown.d.ts} +5 -1
- package/dist/use-cases/commands/convert-mail-to-markdown.d.ts +35 -1
- package/dist/use-cases/commands/create-forward-draft.d.ts +4 -0
- package/dist/use-cases/commands/create-reply-draft.d.ts +8 -0
- package/dist/use-cases/commands/download-drive-item-as-markdown.d.ts +7 -2
- package/dist/use-cases/commands/download-drive-item-as-pdf.d.ts +1 -0
- package/dist/use-cases/commands/download-drive-item-content.d.ts +1 -0
- package/dist/use-cases/commands/draft-comment-splicer.d.ts +29 -0
- package/dist/use-cases/commands/draft-dedup.d.ts +18 -0
- package/dist/use-cases/commands/draft-response.d.ts +4 -0
- package/dist/use-cases/commands/extract-drive-item-images.d.ts +1 -0
- package/dist/use-cases/commands/extract-local-file-images.d.ts +1 -1
- package/dist/use-cases/commands/fetch-raw-bytes.d.ts +13 -0
- package/dist/use-cases/commands/find-mail-drafts.d.ts +9 -0
- package/dist/use-cases/commands/get-mail-signature.d.ts +8 -0
- package/dist/use-cases/commands/get-user.d.ts +10 -0
- package/dist/use-cases/commands/inline-image-embedder.d.ts +2 -1
- package/dist/use-cases/commands/login-status.d.ts +9 -27
- package/dist/use-cases/commands/login.d.ts +21 -0
- package/dist/use-cases/commands/mail-message-select.d.ts +1 -0
- package/dist/use-cases/commands/mail-quote-stripper.d.ts +16 -15
- package/dist/use-cases/commands/markdown-dispatch.d.ts +2 -1
- package/dist/use-cases/commands/msg-to-markdown.d.ts +10 -1
- package/dist/use-cases/commands/odata-query.d.ts +15 -1
- package/dist/use-cases/commands/office-to-markdown.d.ts +1 -0
- package/dist/use-cases/commands/read-mail-attachment.d.ts +4 -0
- package/dist/use-cases/commands/resolve-command.d.ts +25 -0
- package/dist/use-cases/commands/search-all-files.d.ts +8 -0
- package/dist/use-cases/commands/search-escape.d.ts +23 -0
- package/dist/use-cases/commands/signature-extractor.d.ts +2 -0
- package/dist/use-cases/commands/tenant-option.d.ts +44 -0
- package/dist/use-cases/commands/update-mail-draft.d.ts +1 -0
- package/dist/use-cases/commands/zip-archive-to-markdown.d.ts +10 -5
- package/dist/use-cases/ports/filesystem.d.ts +1 -1
- package/docs/COMMANDS.md +34 -29
- package/docs/USAGE.md +65 -7
- package/package.json +2 -1
package/dist/infra/auth.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AccessToken } from '../domain/access-token.js';
|
|
2
2
|
import type { Result } from '../domain/result.js';
|
|
3
|
+
import type { TenantId } from '../domain/tenant-id.js';
|
|
3
4
|
import type { FileSystem } from '../use-cases/ports/filesystem.js';
|
|
4
5
|
import type { Logger } from '../use-cases/ports/logger.js';
|
|
5
6
|
import type { BrowserAuth, ElevatedFailureReason } from './browser-auth.js';
|
|
@@ -24,6 +25,11 @@ type ElevatedOutcome = {
|
|
|
24
25
|
captured: false;
|
|
25
26
|
reason: ElevatedFailureReason | 'unknown_error';
|
|
26
27
|
};
|
|
28
|
+
type CachedTierInfo = {
|
|
29
|
+
readonly available: boolean;
|
|
30
|
+
readonly expiresInSeconds: number | undefined;
|
|
31
|
+
readonly scopes: ReadonlyArray<string>;
|
|
32
|
+
};
|
|
27
33
|
type AuthManager = {
|
|
28
34
|
getAccessToken: (options?: {
|
|
29
35
|
force?: boolean;
|
|
@@ -34,6 +40,14 @@ type AuthManager = {
|
|
|
34
40
|
* via headless Playwright. Used by the 3 historical-version commands.
|
|
35
41
|
*/
|
|
36
42
|
getElevatedAccessToken: () => Promise<Result<AccessToken, AuthError>>;
|
|
43
|
+
/**
|
|
44
|
+
* Returns a Graph token issued by a PARTNER tenant's authority, for a user
|
|
45
|
+
* who is a guest there. Without it, every call against that tenant's
|
|
46
|
+
* SharePoint dies at `401 invalidAudienceUri` — home-tenant Graph cannot mint
|
|
47
|
+
* an SPO token for a foreign tenant. Cache -> headless redemption of the
|
|
48
|
+
* shared refresh token; never a browser.
|
|
49
|
+
*/
|
|
50
|
+
getGuestAccessToken: (tenantId: TenantId) => Promise<Result<AccessToken, AuthError>>;
|
|
37
51
|
/**
|
|
38
52
|
* Returns a chatsvcagg-audience token (same Teams web client identity
|
|
39
53
|
* as `getAccessToken`, but issued for the chatsvcagg resource). Falls
|
|
@@ -81,26 +95,32 @@ type AuthManager = {
|
|
|
81
95
|
* Optional: only the real manager implements it; a minimal fake omits it
|
|
82
96
|
* and callers treat that as unavailable. Never captures or refreshes.
|
|
83
97
|
*/
|
|
84
|
-
getCachedElevatedInfo?: () => Promise<
|
|
85
|
-
available: boolean;
|
|
86
|
-
expiresInSeconds: number | undefined;
|
|
87
|
-
}>;
|
|
98
|
+
getCachedElevatedInfo?: () => Promise<CachedTierInfo>;
|
|
88
99
|
/**
|
|
89
100
|
* Same decode-only preflight as `getCachedElevatedInfo`, for the chatsvcagg /
|
|
90
101
|
* ic3 Teams-chat substrate tokens. `login`'s four-token status and
|
|
91
102
|
* `scopes-check` read these; a minimal fake omits them and callers treat that
|
|
92
103
|
* as unavailable.
|
|
93
104
|
*/
|
|
94
|
-
getCachedChatsvcaggInfo?: () => Promise<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
105
|
+
getCachedChatsvcaggInfo?: () => Promise<CachedTierInfo>;
|
|
106
|
+
getCachedIc3Info?: () => Promise<CachedTierInfo>;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Command names quoted in the secondary-token error messages, per token kind.
|
|
110
|
+
* The composition root derives these from the command registry
|
|
111
|
+
* (`needsElevatedToken` / `needsSubstrateToken` flags) and injects them; the
|
|
112
|
+
* defaults below are the corrected registry sets at the time of writing, so
|
|
113
|
+
* direct `createAuthManagerFromApi` callers still get accurate messages. The
|
|
114
|
+
* registry sets are pinned in meta.test.ts and the wiring in
|
|
115
|
+
* build-deps.test.ts, so drift surfaces there, not in a stale user message
|
|
116
|
+
* (the old hardcoded elevated list omitted `get-user`).
|
|
117
|
+
*/
|
|
118
|
+
type SecondaryTokenCommands = {
|
|
119
|
+
readonly elevated: ReadonlyArray<string>;
|
|
120
|
+
readonly chatsvcagg: ReadonlyArray<string>;
|
|
121
|
+
readonly ic3: ReadonlyArray<string>;
|
|
102
122
|
};
|
|
103
|
-
declare const createAuthManagerFromApi: (browserAuth: BrowserAuth, cachePath: string, browserProfileDir: string, logger: Logger, fs: FileSystem, recaptureSecondaryViaBrowser?: boolean) => AuthManager;
|
|
123
|
+
declare const createAuthManagerFromApi: (browserAuth: BrowserAuth, cachePath: string, browserProfileDir: string, logger: Logger, fs: FileSystem, recaptureSecondaryViaBrowser?: boolean, secondaryTokenCommands?: SecondaryTokenCommands, acquireBasicViaBrowser?: boolean) => AuthManager;
|
|
104
124
|
/**
|
|
105
125
|
* Probe the token cache for a fresh access token. Handed to the browser
|
|
106
126
|
* capture so its poll loop can short-circuit the multi-minute dance
|
|
@@ -116,6 +136,8 @@ declare const createAuthManager: (deps: {
|
|
|
116
136
|
fs?: FileSystem;
|
|
117
137
|
browserProfileDir?: string;
|
|
118
138
|
recaptureSecondaryViaBrowser?: boolean;
|
|
139
|
+
secondaryTokenCommands?: SecondaryTokenCommands;
|
|
140
|
+
acquireBasicViaBrowser?: boolean;
|
|
119
141
|
}) => AuthManager;
|
|
120
142
|
export { createAuthManager, createAuthManagerFromApi, createFreshCachedTokenProbe, stderrProgress };
|
|
121
|
-
export type { AuthError, AuthManager, ElevatedOutcome };
|
|
143
|
+
export type { AuthError, AuthManager, CachedTierInfo, ElevatedOutcome, SecondaryTokenCommands };
|
|
@@ -97,8 +97,13 @@ type BrowserAuth = {
|
|
|
97
97
|
* different Microsoft web app whose first-party app identity is on
|
|
98
98
|
* Microsoft's allow-list for ODSP `logicalPermissions` (the scope our
|
|
99
99
|
* Teams web client token lacks for historical-version stream
|
|
100
|
-
* content).
|
|
101
|
-
*
|
|
100
|
+
* content). Launches a HEADED browser (`launchContext(false)`) and
|
|
101
|
+
* relies, best-effort, on the persistent profile's cookies to complete
|
|
102
|
+
* sign-in silently. This is NOT guaranteed: on tenants that do not
|
|
103
|
+
* persist SSO cookies (or once they lapse) the navigation lands on a
|
|
104
|
+
* sign-in wall and the capture times out (verified headed AND headless
|
|
105
|
+
* 2026-07-19). That is why it stays headed and why the caller treats a
|
|
106
|
+
* miss as non-fatal and points the user at an interactive `login`.
|
|
102
107
|
*
|
|
103
108
|
* Returns a discriminated union so the caller can distinguish the three
|
|
104
109
|
* failure modes (browser-launch hang, navigation failure, silent-SSO
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Result } from '../domain/result.js';
|
|
2
2
|
import type { AuthManager } from '../infra/auth.js';
|
|
3
|
+
import type { TenantId } from '../domain/tenant-id.js';
|
|
3
4
|
type GraphError = {
|
|
4
5
|
type: 'api_error';
|
|
5
6
|
status: number;
|
|
@@ -36,6 +37,34 @@ type GraphClient = {
|
|
|
36
37
|
* elevated token).
|
|
37
38
|
*/
|
|
38
39
|
getElevated: (path: string) => Promise<Result<unknown, GraphError>>;
|
|
40
|
+
/**
|
|
41
|
+
* JSON-GET signed with a PARTNER tenant's guest token instead of the home
|
|
42
|
+
* token. Required for any path that touches a tenant the user is only a guest
|
|
43
|
+
* in: home-tenant Graph cannot mint a SharePoint token for a foreign tenant,
|
|
44
|
+
* so those calls die at `401 invalidAudienceUri` no matter which home tier
|
|
45
|
+
* signs them.
|
|
46
|
+
*
|
|
47
|
+
* Get the `tenantId` from `resolve-drive-share-link` (it discovers it from the
|
|
48
|
+
* sharing URL) or from the caller's `--tenant-id`.
|
|
49
|
+
*/
|
|
50
|
+
getGuest: (path: string, tenantId: TenantId) => Promise<Result<unknown, GraphError>>;
|
|
51
|
+
/**
|
|
52
|
+
* Binary twin of `getGuest`: follows the Graph 302 to the partner tenant's CDN
|
|
53
|
+
* and returns the bytes. The `fetchUrl` allow-list already admits any
|
|
54
|
+
* `*.sharepoint.com` / `*.svc.ms` host, so a partner tenant's download URL
|
|
55
|
+
* needs no special casing.
|
|
56
|
+
*/
|
|
57
|
+
getBinaryGuest: (path: string, tenantId: TenantId) => Promise<Result<unknown, GraphError>>;
|
|
58
|
+
/**
|
|
59
|
+
* Resolves a SharePoint host to the Entra tenant that owns it, via the tenant's
|
|
60
|
+
* public OIDC discovery document. Unauthenticated: it asks "who owns this
|
|
61
|
+
* host?", not "what may I read?".
|
|
62
|
+
*
|
|
63
|
+
* This is what makes a bare sharing URL enough to cross tenants — the URL
|
|
64
|
+
* carries the host, the host names the tenant, and the tenant is the one thing
|
|
65
|
+
* `driveId` + `itemId` do not tell you.
|
|
66
|
+
*/
|
|
67
|
+
discoverTenantId: (spoHost: string) => Promise<Result<TenantId, GraphError>>;
|
|
39
68
|
/**
|
|
40
69
|
* JSON-GET against the Teams chat substrate (post-2026-05:
|
|
41
70
|
* `teams.microsoft.com/api/csa/<region>/api/v{N}/...` — see
|
|
@@ -102,6 +131,23 @@ type GraphClient = {
|
|
|
102
131
|
*/
|
|
103
132
|
getCachedTokenInfo: () => Promise<Result<TokenInfo, GraphError>>;
|
|
104
133
|
};
|
|
134
|
+
/**
|
|
135
|
+
* Decode-only status for one non-basic token tier: availability, remaining runway,
|
|
136
|
+
* the scopes granted to that token (decoded from its `scp`), and how it refreshes
|
|
137
|
+
* (`automatic` = rides the shared refresh token; `interactive` = elevated, needs a login).
|
|
138
|
+
*/
|
|
139
|
+
type TokenTierInfo = {
|
|
140
|
+
readonly available: boolean;
|
|
141
|
+
readonly expiresInSeconds: number | undefined;
|
|
142
|
+
readonly scopes: ReadonlyArray<string>;
|
|
143
|
+
readonly refresh: 'automatic' | 'interactive';
|
|
144
|
+
/**
|
|
145
|
+
* Present ONLY when `available` is `false`: a one-line, jargon-free reason the
|
|
146
|
+
* tier is absent + how to restore it. Stops the empty `scopes: []` on a missing
|
|
147
|
+
* token from reading as a bug. Omitted entirely when the token is available.
|
|
148
|
+
*/
|
|
149
|
+
readonly reason?: string;
|
|
150
|
+
};
|
|
105
151
|
type TokenInfo = {
|
|
106
152
|
readonly scopes: ReadonlyArray<string>;
|
|
107
153
|
readonly audience: string | undefined;
|
|
@@ -122,24 +168,14 @@ type TokenInfo = {
|
|
|
122
168
|
* preflight elevated access in a fresh process instead of turning every
|
|
123
169
|
* version download into a `403`.
|
|
124
170
|
*/
|
|
125
|
-
readonly elevated:
|
|
126
|
-
readonly available: boolean;
|
|
127
|
-
readonly expiresInSeconds: number | undefined;
|
|
128
|
-
};
|
|
171
|
+
readonly elevated: TokenTierInfo;
|
|
129
172
|
/**
|
|
130
|
-
* The two Teams-chat substrate tokens (chatsvcagg / ic3), same
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* shared refresh token, so they are informational rather than a preflight gate.
|
|
173
|
+
* The two Teams-chat substrate tokens (chatsvcagg / ic3), same `TokenTierInfo`
|
|
174
|
+
* shape as `elevated`. Both self-heal from the shared refresh token (refresh:
|
|
175
|
+
* automatic), so they are informational rather than a preflight gate.
|
|
134
176
|
*/
|
|
135
|
-
readonly chatsvcagg:
|
|
136
|
-
|
|
137
|
-
readonly expiresInSeconds: number | undefined;
|
|
138
|
-
};
|
|
139
|
-
readonly ic3: {
|
|
140
|
-
readonly available: boolean;
|
|
141
|
-
readonly expiresInSeconds: number | undefined;
|
|
142
|
-
};
|
|
177
|
+
readonly chatsvcagg: TokenTierInfo;
|
|
178
|
+
readonly ic3: TokenTierInfo;
|
|
143
179
|
};
|
|
144
180
|
type FetchFn = (url: string, init?: RequestInit) => Promise<Response>;
|
|
145
181
|
declare const createGraphClient: (auth: AuthManager, fetchFn?: FetchFn) => GraphClient;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Logger } from '../use-cases/ports/logger.js';
|
|
2
2
|
import type { ErrorSource } from './error-hints.js';
|
|
3
|
-
type OutputFormat
|
|
3
|
+
import type { OutputFormat } from './render-to-string.js';
|
|
4
4
|
declare const render: (data: unknown, logger: Logger, format: OutputFormat) => void;
|
|
5
5
|
declare const renderError: (message: string, format: OutputFormat, errorCode?: string, explicitSource?: ErrorSource, retryAfterSeconds?: number) => void;
|
|
6
6
|
export { render, renderError };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ErrorSource } from './error-hints.js';
|
|
2
|
+
type OutputFormat = 'text' | 'json';
|
|
3
|
+
/**
|
|
4
|
+
* Render a use-case success value to its final string, newline included.
|
|
5
|
+
* `output.ts` writes this to stdout; `mcp.ts` returns it as tool content.
|
|
6
|
+
*/
|
|
7
|
+
declare const renderToString: (data: unknown, format: OutputFormat) => string;
|
|
8
|
+
/**
|
|
9
|
+
* Render an error to its final string, newline included. The `hint` / `source`
|
|
10
|
+
* lookup is shared with the CLI so an MCP consumer sees the same curated
|
|
11
|
+
* remedies (`error-hints.ts`) the terminal does.
|
|
12
|
+
*/
|
|
13
|
+
declare const renderErrorToString: (message: string, format: OutputFormat, errorCode?: string, explicitSource?: ErrorSource, retryAfterSeconds?: number) => string;
|
|
14
|
+
export { renderErrorToString, renderToString };
|
|
15
|
+
export type { OutputFormat };
|
|
@@ -14,9 +14,26 @@ import { type ODataKey } from './odata-query.js';
|
|
|
14
14
|
type SelectDefaults = {
|
|
15
15
|
readonly defaultSelect?: string;
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* `defaultTop`, when set and the user did NOT pass `--top`, injects a `$top`
|
|
19
|
+
* into the OData query string. Its purpose is NOT payload-slimming (that is
|
|
20
|
+
* `defaultSelect`) but cursor correctness: a handful of `$skip`-offset-paged
|
|
21
|
+
* Graph endpoints (`/me/people` is the canonical one) only emit a
|
|
22
|
+
* self-advancing `@odata.nextLink` when `$top` is present. Without it Graph
|
|
23
|
+
* echoes back the SAME `$skip` on every page (`?$skip=0` → `?$skip=0` → …), so
|
|
24
|
+
* following the cursor loops forever on page 1. Forcing a default `$top` makes
|
|
25
|
+
* Graph increment `$skip` by the page size. Only meaningful on list builders
|
|
26
|
+
* whose OData shape carries `top`. User-supplied `--top` always wins.
|
|
27
|
+
*/
|
|
28
|
+
type ListDefaults = SelectDefaults & {
|
|
29
|
+
readonly defaultTop?: string;
|
|
30
|
+
};
|
|
31
|
+
declare const withDefaultSelect: <T extends {
|
|
32
|
+
readonly select?: string;
|
|
33
|
+
}>(data: T, defaultSelect: string | undefined) => T;
|
|
17
34
|
declare const buildCommand: (pathFn: (params: Record<string, string>) => string, schema: z.ZodType) => Pick<Command, "schema" | "execute">;
|
|
18
35
|
declare const buildElevatedCommand: (pathFn: (params: Record<string, string>) => string, schema: z.ZodType) => Pick<Command, "schema" | "execute">;
|
|
19
|
-
declare const buildListCommand: <Shape extends z.ZodRawShape>(pathFn: (params: z.infer<z.ZodObject<Shape>>) => string, schema: z.ZodObject<Shape>, options?:
|
|
36
|
+
declare const buildListCommand: <Shape extends z.ZodRawShape>(pathFn: (params: z.infer<z.ZodObject<Shape>>) => string, schema: z.ZodObject<Shape>, options?: ListDefaults) => Pick<Command, "schema" | "execute">;
|
|
20
37
|
declare const buildElevatedListCommand: <Shape extends z.ZodRawShape>(pathFn: (params: z.infer<z.ZodObject<Shape>>) => string, schema: z.ZodObject<Shape>, options?: SelectDefaults) => Pick<Command, "schema" | "execute">;
|
|
21
38
|
/**
|
|
22
39
|
* Single-resource GET that supports the OData `$select` and `$expand` query
|
|
@@ -67,4 +84,4 @@ declare const buildPickODataListCommand: <Shape extends z.ZodRawShape, K extends
|
|
|
67
84
|
* `$orderby` / `$expand` with `BadRequest`, so the picker is the right tool.
|
|
68
85
|
*/
|
|
69
86
|
declare const buildElevatedPickODataListCommand: <Shape extends z.ZodRawShape, K extends ODataKey>(pathFn: (params: z.infer<z.ZodObject<Shape>>) => string, schema: z.ZodObject<Shape>, keys: ReadonlyArray<K>, options?: SelectDefaults) => Pick<Command, "schema" | "execute">;
|
|
70
|
-
export { buildCommand, buildElevatedCommand, buildElevatedListCommand, buildElevatedPickODataListCommand, buildElevatedSelectableCommand, buildFilterSelectListCommand, buildListCommand, buildNoSkipListCommand, buildPickODataListCommand, buildSelectableCommand, };
|
|
87
|
+
export { buildCommand, buildElevatedCommand, buildElevatedListCommand, buildElevatedPickODataListCommand, buildElevatedSelectableCommand, buildFilterSelectListCommand, buildListCommand, buildNoSkipListCommand, buildPickODataListCommand, buildSelectableCommand, withDefaultSelect, };
|
|
@@ -109,20 +109,25 @@ type CommandMeta = {
|
|
|
109
109
|
readonly scopesRequired?: ReadonlyArray<string>;
|
|
110
110
|
/**
|
|
111
111
|
* `true` if the command needs the M365ChatClient elevated token (captured
|
|
112
|
-
* at login from `m365.cloud.microsoft`, ODSP allow-list).
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
112
|
+
* at login from `m365.cloud.microsoft`, ODSP allow-list). The exact set is
|
|
113
|
+
* pinned in meta.test.ts, and the composition root derives the auth
|
|
114
|
+
* fail-fast message's command list from this flag — a new elevated command
|
|
115
|
+
* MUST carry it or the remedy message will omit the command. An LLM should
|
|
116
|
+
* check this field before invoking; if the elevated capture failed at
|
|
117
|
+
* login, these commands fail fast with `secondary_token_unavailable`.
|
|
116
118
|
*/
|
|
117
119
|
readonly needsElevatedToken?: true;
|
|
118
120
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
121
|
+
* Which Teams substrate token the command needs: `'chatsvcagg'`
|
|
122
|
+
* (teams.microsoft.com/api/csa) or `'ic3'` (chat history), both captured
|
|
123
|
+
* at login from `teams.microsoft.com`. Truthy = "needs a substrate token"
|
|
124
|
+
* (the historical boolean semantics); the service value additionally
|
|
125
|
+
* routes the command into the right auth fail-fast list, derived by the
|
|
126
|
+
* composition root. Like `needsElevatedToken`, an LLM should check this
|
|
127
|
+
* before invoking and warm up an interactive `login`; a headless or stale
|
|
128
|
+
* session times out on these (the non-interactive silent-SSO limitation).
|
|
124
129
|
*/
|
|
125
|
-
readonly needsSubstrateToken?:
|
|
130
|
+
readonly needsSubstrateToken?: 'chatsvcagg' | 'ic3';
|
|
126
131
|
/**
|
|
127
132
|
* `true` if the command returns inlined bytes (`{contentType, size, base64}`
|
|
128
133
|
* or `{contentType, size, text}`) and is therefore a valid target for the
|
|
@@ -167,7 +172,7 @@ type Command = {
|
|
|
167
172
|
readonly meta: CommandMeta;
|
|
168
173
|
/**
|
|
169
174
|
* Present on the rare command whose input is the LOCAL filesystem instead of
|
|
170
|
-
* Graph (`convert-local-file`). The CLI routes execution here, passing its
|
|
175
|
+
* Graph (`convert-local-file-to-markdown`). The CLI routes execution here, passing its
|
|
171
176
|
* composition-selected FileSystem; `execute` stays as the registry-typed
|
|
172
177
|
* fallback that redirects library consumers to this variant.
|
|
173
178
|
*/
|
|
@@ -9,6 +9,10 @@ declare const schema: z.ZodObject<{
|
|
|
9
9
|
true: "true";
|
|
10
10
|
false: "false";
|
|
11
11
|
}>>;
|
|
12
|
+
keepQuoted: z.ZodOptional<z.ZodEnum<{
|
|
13
|
+
true: "true";
|
|
14
|
+
false: "false";
|
|
15
|
+
}>>;
|
|
12
16
|
}, z.core.$strip>;
|
|
13
17
|
declare const execute: (graph: GraphClient, params: Record<string, string>) => Promise<Result<unknown, GraphError>>;
|
|
14
18
|
declare const meta: CommandMeta;
|
|
@@ -14,12 +14,17 @@ import type { CommandMeta } from './command-types.js';
|
|
|
14
14
|
* rather than failing the whole archive.
|
|
15
15
|
*/
|
|
16
16
|
declare const schema: z.ZodObject<{
|
|
17
|
+
tenantId: z.ZodOptional<z.ZodString>;
|
|
17
18
|
driveId: z.ZodString;
|
|
18
19
|
itemId: z.ZodString;
|
|
19
20
|
includeMetadata: z.ZodOptional<z.ZodEnum<{
|
|
20
21
|
true: "true";
|
|
21
22
|
false: "false";
|
|
22
23
|
}>>;
|
|
24
|
+
keepQuoted: z.ZodOptional<z.ZodEnum<{
|
|
25
|
+
true: "true";
|
|
26
|
+
false: "false";
|
|
27
|
+
}>>;
|
|
23
28
|
}, z.core.$strip>;
|
|
24
29
|
declare const execute: (graph: GraphClient, params: Record<string, string>) => Promise<Result<unknown, GraphError>>;
|
|
25
30
|
declare const meta: CommandMeta;
|
|
@@ -9,9 +9,17 @@ declare const schema: z.ZodObject<{
|
|
|
9
9
|
true: "true";
|
|
10
10
|
false: "false";
|
|
11
11
|
}>>;
|
|
12
|
+
keepQuoted: z.ZodOptional<z.ZodEnum<{
|
|
13
|
+
true: "true";
|
|
14
|
+
false: "false";
|
|
15
|
+
}>>;
|
|
12
16
|
}, z.core.$strip>;
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
type ConvertOptions = {
|
|
18
|
+
readonly includeMetadata: boolean;
|
|
19
|
+
readonly keepQuoted: boolean;
|
|
20
|
+
};
|
|
21
|
+
declare const convertFetchedAttachment: (graph: GraphClient, a: Record<string, unknown>, opts: ConvertOptions) => Promise<Result<unknown, GraphError>> | Result<unknown, GraphError>;
|
|
22
|
+
declare const convertAttachmentToMarkdown: (graph: GraphClient, attachmentPath: string, opts: ConvertOptions) => Promise<Result<unknown, GraphError>>;
|
|
15
23
|
declare const execute: (graph: GraphClient, params: Record<string, string>) => Promise<Result<unknown, GraphError>>;
|
|
16
24
|
declare const meta: CommandMeta;
|
|
17
25
|
export { convertAttachmentToMarkdown, convertFetchedAttachment, execute, meta, schema };
|
|
@@ -4,7 +4,7 @@ import type { GraphClient, GraphError } from '../../infra/graph-client.js';
|
|
|
4
4
|
import type { CommandMeta } from './command-types.js';
|
|
5
5
|
/**
|
|
6
6
|
* Unzips a `.zip` Outlook mail attachment and converts every contained file in one
|
|
7
|
-
* call — the mail-side mirror of `convert-drive-item-zip`. Without it, reading a
|
|
7
|
+
* call — the mail-side mirror of `convert-drive-item-zip-to-markdown`. Without it, reading a
|
|
8
8
|
* zipped vendor deck meant: `get-mail-attachment --output-path x.zip` → manual
|
|
9
9
|
* `unzip` (with `-O GBK` for Chinese names) → convert each file. This collapses all
|
|
10
10
|
* of that into one command: it pulls the fileAttachment bytes, unzips them (legacy
|
|
@@ -18,6 +18,10 @@ declare const schema: z.ZodObject<{
|
|
|
18
18
|
true: "true";
|
|
19
19
|
false: "false";
|
|
20
20
|
}>>;
|
|
21
|
+
keepQuoted: z.ZodOptional<z.ZodEnum<{
|
|
22
|
+
true: "true";
|
|
23
|
+
false: "false";
|
|
24
|
+
}>>;
|
|
21
25
|
}, z.core.$strip>;
|
|
22
26
|
declare const execute: (graph: GraphClient, params: Record<string, string>) => Promise<Result<unknown, GraphError>>;
|
|
23
27
|
declare const meta: CommandMeta;
|
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import type { Result } from '../../domain/result.js';
|
|
3
3
|
import type { GraphClient, GraphError } from '../../infra/graph-client.js';
|
|
4
4
|
import type { CommandMeta } from './command-types.js';
|
|
5
|
+
import { type InlineAttachment } from './inline-image-embedder.js';
|
|
5
6
|
declare const schema: z.ZodObject<{
|
|
6
7
|
messageId: z.ZodString;
|
|
7
8
|
inlineImages: z.ZodOptional<z.ZodEnum<{
|
|
@@ -13,6 +14,39 @@ declare const schema: z.ZodObject<{
|
|
|
13
14
|
false: "false";
|
|
14
15
|
}>>;
|
|
15
16
|
}, z.core.$strip>;
|
|
17
|
+
declare const ATTACHMENT_METADATA_SELECT = "$select=id,name,contentType,size,isInline,microsoft.graph.fileAttachment/contentId";
|
|
18
|
+
declare const attachmentMetaSchema: z.ZodObject<{
|
|
19
|
+
id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
20
|
+
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
21
|
+
contentType: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
22
|
+
size: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
23
|
+
isInline: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
24
|
+
contentId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
25
|
+
}, z.core.$strip>;
|
|
26
|
+
declare const attachmentsListSchema: z.ZodObject<{
|
|
27
|
+
value: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
28
|
+
id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
29
|
+
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
30
|
+
contentType: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
31
|
+
size: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
32
|
+
isInline: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
33
|
+
contentId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
34
|
+
}, z.core.$strip>>>;
|
|
35
|
+
}, z.core.$strip>;
|
|
36
|
+
type AttachmentMeta = z.infer<typeof attachmentMetaSchema>;
|
|
37
|
+
declare const formatBytes: (n: number) => string;
|
|
38
|
+
type InlineImageCandidate = AttachmentMeta & {
|
|
39
|
+
readonly contentType: string;
|
|
40
|
+
readonly contentId: string;
|
|
41
|
+
};
|
|
42
|
+
declare const isInlineImage: (a: AttachmentMeta) => a is InlineImageCandidate;
|
|
43
|
+
type EmbedFetchResult = {
|
|
44
|
+
readonly meta: InlineImageCandidate;
|
|
45
|
+
readonly inline?: InlineAttachment;
|
|
46
|
+
readonly oversize: boolean;
|
|
47
|
+
};
|
|
48
|
+
declare const fetchInlineImageBytes: (graph: GraphClient, messageId: string, meta: InlineImageCandidate) => Promise<EmbedFetchResult>;
|
|
16
49
|
declare const execute: (graph: GraphClient, params: Record<string, string>) => Promise<Result<unknown, GraphError>>;
|
|
17
50
|
declare const meta: CommandMeta;
|
|
18
|
-
export { execute, meta, schema };
|
|
51
|
+
export { ATTACHMENT_METADATA_SELECT, attachmentsListSchema, execute, fetchInlineImageBytes, formatBytes, isInlineImage, meta, schema };
|
|
52
|
+
export type { AttachmentMeta, InlineImageCandidate };
|
|
@@ -6,6 +6,10 @@ declare const schema: z.ZodObject<{
|
|
|
6
6
|
ccRecipients: z.ZodOptional<z.ZodString>;
|
|
7
7
|
bodyContent: z.ZodString;
|
|
8
8
|
subject: z.ZodOptional<z.ZodString>;
|
|
9
|
+
bodyContentType: z.ZodOptional<z.ZodEnum<{
|
|
10
|
+
Text: "Text";
|
|
11
|
+
HTML: "HTML";
|
|
12
|
+
}>>;
|
|
9
13
|
}, z.core.$strip>;
|
|
10
14
|
declare const execute: Command['execute'];
|
|
11
15
|
declare const meta: CommandMeta;
|
|
@@ -4,6 +4,14 @@ declare const schema: z.ZodObject<{
|
|
|
4
4
|
replyToMessageId: z.ZodString;
|
|
5
5
|
bodyContent: z.ZodString;
|
|
6
6
|
subject: z.ZodOptional<z.ZodString>;
|
|
7
|
+
replyAll: z.ZodOptional<z.ZodEnum<{
|
|
8
|
+
true: "true";
|
|
9
|
+
false: "false";
|
|
10
|
+
}>>;
|
|
11
|
+
bodyContentType: z.ZodOptional<z.ZodEnum<{
|
|
12
|
+
Text: "Text";
|
|
13
|
+
HTML: "HTML";
|
|
14
|
+
}>>;
|
|
7
15
|
}, z.core.$strip>;
|
|
8
16
|
declare const execute: Command['execute'];
|
|
9
17
|
declare const meta: CommandMeta;
|
|
@@ -3,8 +3,6 @@ import type { Result } from '../../domain/result.js';
|
|
|
3
3
|
import type { GraphClient, GraphError } from '../../infra/graph-client.js';
|
|
4
4
|
import type { CommandMeta } from './command-types.js';
|
|
5
5
|
declare const schema: z.ZodObject<{
|
|
6
|
-
driveId: z.ZodString;
|
|
7
|
-
itemId: z.ZodString;
|
|
8
6
|
includeMetadata: z.ZodOptional<z.ZodEnum<{
|
|
9
7
|
true: "true";
|
|
10
8
|
false: "false";
|
|
@@ -13,7 +11,14 @@ declare const schema: z.ZodObject<{
|
|
|
13
11
|
true: "true";
|
|
14
12
|
false: "false";
|
|
15
13
|
}>>;
|
|
14
|
+
keepQuoted: z.ZodOptional<z.ZodEnum<{
|
|
15
|
+
true: "true";
|
|
16
|
+
false: "false";
|
|
17
|
+
}>>;
|
|
16
18
|
maxCells: z.ZodOptional<z.ZodString>;
|
|
19
|
+
tenantId: z.ZodOptional<z.ZodString>;
|
|
20
|
+
driveId: z.ZodString;
|
|
21
|
+
itemId: z.ZodString;
|
|
17
22
|
}, z.core.$strip>;
|
|
18
23
|
declare const execute: (graph: GraphClient, params: Record<string, string>) => Promise<Result<unknown, GraphError>>;
|
|
19
24
|
declare const meta: CommandMeta;
|
|
@@ -3,6 +3,7 @@ import type { Result } from '../../domain/result.js';
|
|
|
3
3
|
import type { GraphClient, GraphError } from '../../infra/graph-client.js';
|
|
4
4
|
import type { CommandMeta } from './command-types.js';
|
|
5
5
|
declare const schema: z.ZodObject<{
|
|
6
|
+
tenantId: z.ZodOptional<z.ZodString>;
|
|
6
7
|
driveId: z.ZodString;
|
|
7
8
|
itemId: z.ZodString;
|
|
8
9
|
}, z.core.$strip>;
|
|
@@ -3,6 +3,7 @@ import type { Result } from '../../domain/result.js';
|
|
|
3
3
|
import type { GraphClient, GraphError } from '../../infra/graph-client.js';
|
|
4
4
|
import type { CommandMeta } from './command-types.js';
|
|
5
5
|
declare const schema: z.ZodObject<{
|
|
6
|
+
tenantId: z.ZodOptional<z.ZodString>;
|
|
6
7
|
driveId: z.ZodString;
|
|
7
8
|
itemId: z.ZodString;
|
|
8
9
|
}, z.core.$strip>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
type SpliceResult = {
|
|
2
|
+
readonly html: string;
|
|
3
|
+
readonly boundaryFound: boolean;
|
|
4
|
+
};
|
|
5
|
+
type PlainTextSpliceResult = {
|
|
6
|
+
readonly text: string;
|
|
7
|
+
readonly boundaryFound: boolean;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Renders an author's plain text as HTML: markup they typed shows as characters
|
|
11
|
+
* rather than taking effect, and their newlines survive as breaks. No wrapper
|
|
12
|
+
* element, so the caller decides the block context.
|
|
13
|
+
*/
|
|
14
|
+
declare const escapeTextAsHtml: (text: string) => string;
|
|
15
|
+
/** Index just past the `<body>` open tag, or 0 for a fragment that has none. */
|
|
16
|
+
declare const findBodyInsertStart: (html: string) => number;
|
|
17
|
+
/**
|
|
18
|
+
* True when the author's own markup carries a quote boundary marker, e.g. they
|
|
19
|
+
* pasted a reply chain into it. Such a comment must be refused: the splice would
|
|
20
|
+
* keep the marker verbatim, and the NEXT revision would cut the draft AT it,
|
|
21
|
+
* silently dropping the real quoted history below.
|
|
22
|
+
*/
|
|
23
|
+
declare const commentCarriesQuoteBoundary: (commentHtml: string) => boolean;
|
|
24
|
+
/** The refusal copy for `commentCarriesQuoteBoundary`, named for the flag that carried it. */
|
|
25
|
+
declare const boundaryMarkerRefusal: (flagName: string) => string;
|
|
26
|
+
declare const insertCommentAboveQuote: (html: string, commentHtml: string) => SpliceResult;
|
|
27
|
+
declare const replaceCommentAboveQuote: (html: string, commentHtml: string) => SpliceResult;
|
|
28
|
+
declare const replacePlainTextCommentAboveQuote: (text: string, comment: string) => PlainTextSpliceResult;
|
|
29
|
+
export { boundaryMarkerRefusal, commentCarriesQuoteBoundary, escapeTextAsHtml, findBodyInsertStart, insertCommentAboveQuote, replaceCommentAboveQuote, replacePlainTextCommentAboveQuote, };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
declare const normalizeThreadSubject: (subject: string) => string;
|
|
2
|
+
type DraftRecipient = {
|
|
3
|
+
readonly emailAddress?: {
|
|
4
|
+
readonly address?: string;
|
|
5
|
+
};
|
|
6
|
+
};
|
|
7
|
+
type DraftForMatch = {
|
|
8
|
+
readonly subject?: string;
|
|
9
|
+
readonly toRecipients?: ReadonlyArray<DraftRecipient>;
|
|
10
|
+
readonly ccRecipients?: ReadonlyArray<DraftRecipient>;
|
|
11
|
+
};
|
|
12
|
+
type DraftMatchCriteria = {
|
|
13
|
+
readonly subject: string;
|
|
14
|
+
readonly recipients?: ReadonlyArray<string>;
|
|
15
|
+
};
|
|
16
|
+
declare const matchExistingDrafts: <T extends DraftForMatch>(drafts: ReadonlyArray<T>, criteria: DraftMatchCriteria) => ReadonlyArray<T>;
|
|
17
|
+
export { matchExistingDrafts, normalizeThreadSubject };
|
|
18
|
+
export type { DraftForMatch, DraftMatchCriteria };
|
|
@@ -3,6 +3,7 @@ import type { Result } from '../../domain/result.js';
|
|
|
3
3
|
import type { GraphClient, GraphError } from '../../infra/graph-client.js';
|
|
4
4
|
import type { CommandMeta } from './command-types.js';
|
|
5
5
|
declare const schema: z.ZodObject<{
|
|
6
|
+
tenantId: z.ZodOptional<z.ZodString>;
|
|
6
7
|
driveId: z.ZodString;
|
|
7
8
|
itemId: z.ZodString;
|
|
8
9
|
}, z.core.$strip>;
|
|
@@ -9,7 +9,7 @@ import type { CommandMeta } from './command-types.js';
|
|
|
9
9
|
* (OOXML media parts / unpdf page walk). It completes two flows the drive
|
|
10
10
|
* command cannot reach: a Graph-rendered PDF saved with the global
|
|
11
11
|
* output-path flag (the legacy-.ppt route), and Office files unpacked from a
|
|
12
|
-
* local archive. Like `convert-local-file`, it never touches Graph and is
|
|
12
|
+
* local archive. Like `convert-local-file-to-markdown`, it never touches Graph and is
|
|
13
13
|
* executed via `executeLocal(fs, params)`.
|
|
14
14
|
*/
|
|
15
15
|
declare const schema: z.ZodObject<{
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Result } from '../../domain/result.js';
|
|
2
|
+
import type { TenantId } from '../../domain/tenant-id.js';
|
|
2
3
|
import type { GraphClient, GraphError } from '../../infra/graph-client.js';
|
|
3
4
|
/**
|
|
4
5
|
* Helpers that consolidate the "Graph hands you a 302, follow the CDN
|
|
@@ -26,8 +27,20 @@ import type { GraphClient, GraphError } from '../../infra/graph-client.js';
|
|
|
26
27
|
* redirects to a streamContent URL whose embedded tempauth is signed
|
|
27
28
|
* by Teams web client identity and rejected by SharePoint with 403.
|
|
28
29
|
*/
|
|
30
|
+
/**
|
|
31
|
+
* `elevated` picks the ODSP-allow-listed home identity (M365ChatClient).
|
|
32
|
+
* `tenantId` picks a PARTNER tenant's guest identity — for a file in a tenant the
|
|
33
|
+
* user is only a guest in, which no home-tier token can read (Graph answers `401
|
|
34
|
+
* invalidAudienceUri`).
|
|
35
|
+
*
|
|
36
|
+
* They are mutually exclusive by nature: elevated is a home-tenant identity, so
|
|
37
|
+
* "elevated in a partner tenant" does not exist. `tenantId` wins if both arrive,
|
|
38
|
+
* because a partner-tenant file is unreadable on ANY home token — including the
|
|
39
|
+
* elevated one — so honouring `elevated` there would guarantee failure.
|
|
40
|
+
*/
|
|
29
41
|
export type FetchOptions = {
|
|
30
42
|
readonly elevated?: boolean;
|
|
43
|
+
readonly tenantId?: TenantId;
|
|
31
44
|
};
|
|
32
45
|
export type InlineBinary = {
|
|
33
46
|
readonly contentType: string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Command, CommandMeta } from './command-types.js';
|
|
3
|
+
declare const schema: z.ZodObject<{
|
|
4
|
+
subject: z.ZodString;
|
|
5
|
+
toRecipients: z.ZodOptional<z.ZodString>;
|
|
6
|
+
}, z.core.$strip>;
|
|
7
|
+
declare const execute: Command['execute'];
|
|
8
|
+
declare const meta: CommandMeta;
|
|
9
|
+
export { execute, meta, schema };
|