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.
Files changed (33) hide show
  1. package/CHANGELOG.md +274 -0
  2. package/README.md +45 -26
  3. package/dist/cli.js +3681 -2370
  4. package/dist/commands.json +481 -30
  5. package/dist/composition/run-registry-command.d.ts +4 -4
  6. package/dist/domain/utilities/base64.d.ts +11 -0
  7. package/dist/index.js +2441 -1638
  8. package/dist/infra/auth.d.ts +30 -4
  9. package/dist/infra/browser-auth.d.ts +21 -1
  10. package/dist/presenter/output-text.d.ts +1 -1
  11. package/dist/presenter/output.d.ts +2 -2
  12. package/dist/presenter/render-to-string.d.ts +14 -6
  13. package/dist/use-cases/commands/convert-group-post-attachment-to-markdown.d.ts +21 -0
  14. package/dist/use-cases/commands/convert-group-post-to-markdown.d.ts +20 -0
  15. package/dist/use-cases/commands/convert-mail-attachment-to-markdown.d.ts +6 -3
  16. package/dist/use-cases/commands/convert-mail-to-markdown.d.ts +35 -3
  17. package/dist/use-cases/commands/docx-metadata.d.ts +33 -2
  18. package/dist/use-cases/commands/get-group-post-attachment.d.ts +13 -0
  19. package/dist/use-cases/commands/get-group-post.d.ts +4 -0
  20. package/dist/use-cases/commands/include-hidden-folders.d.ts +2 -0
  21. package/dist/use-cases/commands/list-group-post-attachments.d.ts +4 -0
  22. package/dist/use-cases/commands/list-group-thread-posts.d.ts +4 -0
  23. package/dist/use-cases/commands/list-shared-mailbox-child-folders.d.ts +4 -0
  24. package/dist/use-cases/commands/list-shared-mailbox-folders.d.ts +4 -0
  25. package/dist/use-cases/commands/login-status.d.ts +2 -0
  26. package/dist/use-cases/commands/login.d.ts +18 -8
  27. package/dist/use-cases/commands/next-page.d.ts +1 -0
  28. package/dist/use-cases/commands/ooxml-xml-walker.d.ts +31 -6
  29. package/dist/use-cases/commands/pptx-comments.d.ts +8 -5
  30. package/dist/use-cases/commands/token-tier-capability.d.ts +5 -0
  31. package/docs/COMMANDS.md +20 -12
  32. package/docs/USAGE.md +8 -8
  33. package/package.json +26 -9
@@ -39,7 +39,9 @@ type AuthManager = {
39
39
  * `logicalPermissions` allow-list. Falls through cache → re-capture
40
40
  * via headless Playwright. Used by the 3 historical-version commands.
41
41
  */
42
- getElevatedAccessToken: () => Promise<Result<AccessToken, AuthError>>;
42
+ getElevatedAccessToken: (options?: {
43
+ readonly awaitSignIn?: boolean;
44
+ }) => Promise<Result<AccessToken, AuthError>>;
43
45
  /**
44
46
  * Returns a Graph token issued by a PARTNER tenant's authority, for a user
45
47
  * who is a guest there. Without it, every call against that tenant's
@@ -54,7 +56,9 @@ type AuthManager = {
54
56
  * through cache → re-capture via headless Playwright. Used by the
55
57
  * `list-teams-chats-with-messages` family of commands.
56
58
  */
57
- getChatsvcaggAccessToken: () => Promise<Result<AccessToken, AuthError>>;
59
+ getChatsvcaggAccessToken: (options?: {
60
+ readonly ignoreCache?: boolean;
61
+ }) => Promise<Result<AccessToken, AuthError>>;
58
62
  /**
59
63
  * Returns the regional segment used to construct chatsvcagg substrate
60
64
  * URLs (`teams.microsoft.com/api/csa/<region>/api/...`). Captured at
@@ -71,7 +75,17 @@ type AuthManager = {
71
75
  * via headless Playwright. Used by `list-teams-chat-history` to walk
72
76
  * paginated chat-message history beyond the 200-message chatsvcagg cap.
73
77
  */
74
- getIc3AccessToken: () => Promise<Result<AccessToken, AuthError>>;
78
+ getIc3AccessToken: (options?: {
79
+ readonly ignoreCache?: boolean;
80
+ }) => Promise<Result<AccessToken, AuthError>>;
81
+ /**
82
+ * Redeem the shared refresh token for any COLD substrate tier, over HTTP, with
83
+ * no browser on any path. `login` calls this so a warm-cache sign-in leaves all
84
+ * four tiers usable; without it the two substrate tiers stay cold until some
85
+ * Teams-chat command pays for them, and `login` reports them missing while
86
+ * having done nothing about it.
87
+ */
88
+ warmSubstrateTokens?: () => Promise<void>;
75
89
  logout: () => Promise<Result<void, AuthError>>;
76
90
  /**
77
91
  * Inspect the elevated-capture outcome from the most recent
@@ -95,6 +109,16 @@ type AuthManager = {
95
109
  * Optional: only the real manager implements it; a minimal fake omits it
96
110
  * and callers treat that as unavailable. Never captures or refreshes.
97
111
  */
112
+ /**
113
+ * The cached BASIC token, decoded by `scopes-check` and never acquired: no
114
+ * refresh, no browser. The acquiring getter heals a dead session by opening
115
+ * a browser and, when the persistent profile is already signed in, wiping it
116
+ * so the grant re-fires (2026-09-02: a diagnostic did exactly that). A stale
117
+ * token comes back as-is so its expiry can be reported; no cache at all is
118
+ * `undefined`. Optional: a bring-your-own-token manager omits it and callers
119
+ * fall back to `getAccessToken`, which is then the caller's own function.
120
+ */
121
+ getCachedBasicToken?: () => Promise<AccessToken | undefined>;
98
122
  getCachedElevatedInfo?: () => Promise<CachedTierInfo>;
99
123
  /**
100
124
  * Same decode-only preflight as `getCachedElevatedInfo`, for the chatsvcagg /
@@ -120,7 +144,8 @@ type SecondaryTokenCommands = {
120
144
  readonly chatsvcagg: ReadonlyArray<string>;
121
145
  readonly ic3: ReadonlyArray<string>;
122
146
  };
123
- declare const createAuthManagerFromApi: (browserAuth: BrowserAuth, cachePath: string, browserProfileDir: string, logger: Logger, fs: FileSystem, recaptureSecondaryViaBrowser?: boolean, secondaryTokenCommands?: SecondaryTokenCommands, acquireBasicViaBrowser?: boolean) => AuthManager;
147
+ type FetchFn = (url: string, init?: RequestInit) => Promise<Response>;
148
+ declare const createAuthManagerFromApi: (browserAuth: BrowserAuth, cachePath: string, browserProfileDir: string, logger: Logger, fs: FileSystem, recaptureSecondaryViaBrowser?: boolean, secondaryTokenCommands?: SecondaryTokenCommands, acquireBasicViaBrowser?: boolean, recaptureElevatedViaBrowser?: boolean, fetchFn?: FetchFn) => AuthManager;
124
149
  /**
125
150
  * Probe the token cache for a fresh access token. Handed to the browser
126
151
  * capture so its poll loop can short-circuit the multi-minute dance
@@ -138,6 +163,7 @@ declare const createAuthManager: (deps: {
138
163
  recaptureSecondaryViaBrowser?: boolean;
139
164
  secondaryTokenCommands?: SecondaryTokenCommands;
140
165
  acquireBasicViaBrowser?: boolean;
166
+ recaptureElevatedViaBrowser?: boolean;
141
167
  }) => AuthManager;
142
168
  export { createAuthManager, createAuthManagerFromApi, createFreshCachedTokenProbe, stderrProgress };
143
169
  export type { AuthError, AuthManager, CachedTierInfo, ElevatedOutcome, SecondaryTokenCommands };
@@ -115,8 +115,16 @@ type BrowserAuth = {
115
115
  * cached elevated token expires and the Teams session is already in
116
116
  * the cache (no fresh sign-in needed). On `login`, use
117
117
  * `acquireBothTokens` instead so the user sees only one browser.
118
+ *
119
+ * `awaitSignIn` says a human asked for this and is watching, so the capture
120
+ * gets the interactive `pollDeadlineMs` rather than the short silent cap:
121
+ * long enough for a slow redirect chain, and long enough to fill in a
122
+ * sign-in form if one appears. Without it the short cap applies and the
123
+ * capture fails fast, which is what a background command needs.
118
124
  */
119
- acquireElevatedToken: () => Promise<ElevatedTokenResult>;
125
+ acquireElevatedToken: (options?: {
126
+ readonly awaitSignIn?: boolean;
127
+ }) => Promise<ElevatedTokenResult>;
120
128
  /**
121
129
  * Capture a chatsvcagg-audience bearer (Teams basic identity, but the
122
130
  * `chatsvcagg.teams.microsoft.com` resource instead of Graph) by
@@ -223,6 +231,12 @@ type BrowserAuthConfig = {
223
231
  readonly initialSettleMs?: number;
224
232
  readonly postReloginSettleMs?: number;
225
233
  readonly pollIntervalMs?: number;
234
+ /**
235
+ * How long to keep listening for the PREFERRED elevated identity after an
236
+ * acceptable-but-weaker one has already been captured. See
237
+ * `PREFERRED_ELEVATED_APP_ID`.
238
+ */
239
+ readonly elevatedPreferenceGraceMs?: number;
226
240
  readonly pollDeadlineMs?: number;
227
241
  readonly navigationTimeoutMs?: number;
228
242
  /**
@@ -234,6 +248,12 @@ type BrowserAuthConfig = {
234
248
  * the LLM tool-call window. With a tight cap, the flow either yields
235
249
  * a token quickly or fails with `auth_failed: elevated token capture
236
250
  * timed out — run `ask-marcel-office login` to refresh.`
251
+ *
252
+ * The cap applies only when nobody is waiting. A caller passing
253
+ * `awaitSignIn` (today just `login`) gets `pollDeadlineMs` instead, because
254
+ * a silent capture that merely runs SLOW would otherwise fail here and send
255
+ * `login` into the forced dance, whose cookie wipe costs the 90-day sign-in
256
+ * session and guarantees the next run prompts.
237
257
  */
238
258
  readonly elevatedRecaptureTimeoutMs?: number;
239
259
  /**
@@ -1,2 +1,2 @@
1
- declare const renderTextOutput: (data: unknown) => string;
1
+ declare const renderTextOutput: (data: unknown, tenantId?: string) => string;
2
2
  export { renderTextOutput };
@@ -1,7 +1,7 @@
1
1
  import type { Logger } from '../use-cases/ports/logger.js';
2
2
  import type { ErrorSource } from './error-hints.js';
3
- import type { OutputFormat, SizeHintContext } from './render-to-string.js';
4
- declare const render: (data: unknown, logger: Logger, format: OutputFormat, context?: SizeHintContext) => void;
3
+ import type { OutputFormat, RenderContext } from './render-to-string.js';
4
+ declare const render: (data: unknown, logger: Logger, format: OutputFormat, context?: RenderContext) => void;
5
5
  declare const renderError: (message: string, format: OutputFormat, errorCode?: string, explicitSource?: ErrorSource, retryAfterSeconds?: number) => void;
6
6
  export { render, renderError };
7
7
  export type { OutputFormat };
@@ -9,9 +9,10 @@ type OutputFormat = 'text' | 'json';
9
9
  type RenderSurface = 'cli' | 'mcp';
10
10
  /**
11
11
  * What the presenter needs in order to name a remedy the caller can actually
12
- * use. Built in composition (which owns the command manifest); omitted by the
13
- * renders that have no command behind them at all — the 548 KB `list-commands`
14
- * manifest, a login summary, a logout status.
12
+ * use, and to render a follow-up command the caller can actually run. Built in
13
+ * composition (which owns the command manifest AND the invoked params); omitted
14
+ * by the renders that have no command behind them at all — the 548 KB
15
+ * `list-commands` manifest, a login summary, a logout status.
15
16
  *
16
17
  * 2026-07-23: the banner used to call `--output-path` a "universal remedy
17
18
  * (works on every command)". It is not. Plain-JSON commands REFUSE the flag,
@@ -20,13 +21,20 @@ type RenderSurface = 'cli' | 'mcp';
20
21
  * banner named was a dead end there, and an agentic caller following it burned
21
22
  * a call to find out.
22
23
  */
23
- type SizeHintContext = {
24
+ type RenderContext = {
24
25
  readonly commandName: string;
25
26
  /** `meta.producesBytes` — the manifest flag that decides whether --output-path is accepted. */
26
27
  readonly producesBytes: boolean;
27
28
  readonly supportsSelect: boolean;
28
29
  readonly supportsTop: boolean;
29
30
  readonly surface: RenderSurface;
31
+ /**
32
+ * The `--tenant-id` the call was made with, when it carried one. Unlike every
33
+ * field above it comes from the INVOCATION, not the manifest: a Graph cursor
34
+ * carries no tenant, so the footer is the only place the next call can learn
35
+ * which identity signed this page.
36
+ */
37
+ readonly tenantId?: string;
30
38
  };
31
39
  /**
32
40
  * Render a use-case success value to its final string, newline included.
@@ -36,7 +44,7 @@ type SizeHintContext = {
36
44
  * whenever a registry command produced the data. Omit it for the renders that
37
45
  * have no command behind them; the banner then claims no flag-level remedy.
38
46
  */
39
- declare const renderToString: (data: unknown, format: OutputFormat, context?: SizeHintContext) => string;
47
+ declare const renderToString: (data: unknown, format: OutputFormat, context?: RenderContext) => string;
40
48
  /**
41
49
  * Render an error to its final string, newline included. The `hint` / `source`
42
50
  * lookup is shared with the CLI so an MCP consumer sees the same curated
@@ -44,4 +52,4 @@ declare const renderToString: (data: unknown, format: OutputFormat, context?: Si
44
52
  */
45
53
  declare const renderErrorToString: (message: string, format: OutputFormat, errorCode?: string, explicitSource?: ErrorSource, retryAfterSeconds?: number) => string;
46
54
  export { renderErrorToString, renderToString };
47
- export type { OutputFormat, RenderSurface, SizeHintContext };
55
+ export type { OutputFormat, RenderContext, RenderSurface };
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+ import type { Result } from '../../domain/result.js';
3
+ import type { GraphClient, GraphError } from '../../infra/graph-client.js';
4
+ import type { CommandMeta } from './command-types.js';
5
+ declare const schema: z.ZodObject<{
6
+ groupId: z.ZodString;
7
+ threadId: z.ZodString;
8
+ postId: z.ZodString;
9
+ attachmentId: z.ZodString;
10
+ includeMetadata: z.ZodOptional<z.ZodEnum<{
11
+ true: "true";
12
+ false: "false";
13
+ }>>;
14
+ keepQuoted: z.ZodOptional<z.ZodEnum<{
15
+ true: "true";
16
+ false: "false";
17
+ }>>;
18
+ }, z.core.$strip>;
19
+ declare const execute: (graph: GraphClient, params: Record<string, string>) => Promise<Result<unknown, GraphError>>;
20
+ declare const meta: CommandMeta;
21
+ export { execute, meta, schema };
@@ -0,0 +1,20 @@
1
+ import { z } from 'zod';
2
+ import type { Result } from '../../domain/result.js';
3
+ import type { GraphClient, GraphError } from '../../infra/graph-client.js';
4
+ import type { CommandMeta } from './command-types.js';
5
+ declare const schema: z.ZodObject<{
6
+ groupId: z.ZodString;
7
+ threadId: z.ZodString;
8
+ postId: z.ZodString;
9
+ inlineImages: z.ZodOptional<z.ZodEnum<{
10
+ true: "true";
11
+ false: "false";
12
+ }>>;
13
+ keepQuoted: z.ZodOptional<z.ZodEnum<{
14
+ true: "true";
15
+ false: "false";
16
+ }>>;
17
+ }, z.core.$strip>;
18
+ declare const execute: (graph: GraphClient, params: Record<string, string>) => Promise<Result<unknown, GraphError>>;
19
+ declare const meta: CommandMeta;
20
+ export { execute, meta, schema };
@@ -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 { ConversionHints } from './markdown-dispatch.js';
5
6
  declare const schema: z.ZodObject<{
6
7
  messageId: z.ZodString;
7
8
  attachmentId: z.ZodString;
@@ -18,8 +19,10 @@ type ConvertOptions = {
18
19
  readonly includeMetadata: boolean;
19
20
  readonly keepQuoted: boolean;
20
21
  };
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>>;
22
+ declare const MAIL_HINTS: ConversionHints;
23
+ declare const convertFetchedAttachment: (graph: GraphClient, a: Record<string, unknown>, opts: ConvertOptions, hints: ConversionHints) => Promise<Result<unknown, GraphError>> | Result<unknown, GraphError>;
24
+ declare const convertAttachmentToMarkdown: (graph: GraphClient, attachmentPath: string, opts: ConvertOptions, hints: ConversionHints) => Promise<Result<unknown, GraphError>>;
23
25
  declare const execute: (graph: GraphClient, params: Record<string, string>) => Promise<Result<unknown, GraphError>>;
24
26
  declare const meta: CommandMeta;
25
- export { convertAttachmentToMarkdown, convertFetchedAttachment, execute, meta, schema };
27
+ export { convertAttachmentToMarkdown, convertFetchedAttachment, execute, MAIL_HINTS, meta, schema };
28
+ export type { ConvertOptions };
@@ -15,6 +15,30 @@ declare const schema: z.ZodObject<{
15
15
  }>>;
16
16
  }, z.core.$strip>;
17
17
  declare const ATTACHMENT_METADATA_SELECT = "$select=id,name,contentType,size,isInline,microsoft.graph.fileAttachment/contentId";
18
+ declare const nonEmpty: (v: unknown) => v is string;
19
+ type Recipient = {
20
+ readonly emailAddress?: {
21
+ readonly name?: string;
22
+ readonly address?: string;
23
+ };
24
+ };
25
+ type MailLikeResource = {
26
+ readonly subject?: string;
27
+ readonly from?: Recipient;
28
+ readonly sender?: Recipient;
29
+ readonly toRecipients?: ReadonlyArray<Recipient>;
30
+ readonly ccRecipients?: ReadonlyArray<Recipient>;
31
+ readonly receivedDateTime?: string;
32
+ readonly body?: {
33
+ readonly contentType?: string;
34
+ readonly content?: string;
35
+ };
36
+ readonly hasAttachments?: boolean;
37
+ };
38
+ declare const formatAddress: (a: {
39
+ readonly name?: string;
40
+ readonly address?: string;
41
+ } | undefined) => string | undefined;
18
42
  declare const attachmentMetaSchema: z.ZodObject<{
19
43
  id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
20
44
  name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -45,8 +69,16 @@ type EmbedFetchResult = {
45
69
  readonly inline?: InlineAttachment;
46
70
  readonly oversize: boolean;
47
71
  };
48
- declare const fetchInlineImageBytes: (graph: GraphClient, messageId: string, meta: InlineImageCandidate) => Promise<EmbedFetchResult>;
72
+ declare const fetchInlineImageBytes: (graph: GraphClient, resourcePath: string, meta: InlineImageCandidate) => Promise<EmbedFetchResult>;
73
+ type MarkdownRenderOptions = {
74
+ readonly inlineImages: boolean;
75
+ readonly keepQuoted: boolean;
76
+ /** Closing line of the attachments list, naming the command that fetches their bytes. */
77
+ readonly attachmentHint: string;
78
+ readonly renderHeaders: (m: MailLikeResource) => string;
79
+ };
80
+ declare const renderMessageAsMarkdown: (graph: GraphClient, resourcePath: string, options: MarkdownRenderOptions) => Promise<Result<unknown, GraphError>>;
49
81
  declare const execute: (graph: GraphClient, params: Record<string, string>) => Promise<Result<unknown, GraphError>>;
50
82
  declare const meta: CommandMeta;
51
- export { ATTACHMENT_METADATA_SELECT, attachmentsListSchema, execute, fetchInlineImageBytes, formatBytes, isInlineImage, meta, schema };
52
- export type { AttachmentMeta, InlineImageCandidate };
83
+ export { ATTACHMENT_METADATA_SELECT, attachmentsListSchema, execute, fetchInlineImageBytes, formatAddress, formatBytes, isInlineImage, meta, nonEmpty, renderMessageAsMarkdown, schema, };
84
+ export type { AttachmentMeta, InlineImageCandidate, MailLikeResource, MarkdownRenderOptions };
@@ -4,7 +4,9 @@ import type { CustomProp, ExternalRel } from './ooxml-metadata.js';
4
4
  /**
5
5
  * Pulls the side-channel content out of a .docx zip — every text-bearing
6
6
  * surface mammoth drops on the floor: core / app / custom doc properties,
7
- * people registry, external hyperlinks, comments, tracked changes (ins + del),
7
+ * people registry, external hyperlinks, comments, tracked changes (replacements,
8
+ * the insertions and deletions that pair with nothing, moves, and run,
9
+ * paragraph, table, row and cell formatting changes),
8
10
  * hidden text (w:vanish), text-box / shape text (w:txbxContent), header/footer
9
11
  * body prose, field instructions (MERGEFIELD / HYPERLINK / DOCVARIABLE), bookmarks.
10
12
  *
@@ -36,6 +38,29 @@ type TrackedChange = {
36
38
  readonly date: string;
37
39
  readonly text: string;
38
40
  };
41
+ type Replacement = {
42
+ readonly deletionId: string;
43
+ readonly insertionId: string;
44
+ readonly author: string;
45
+ readonly date: string;
46
+ readonly before: string;
47
+ readonly after: string;
48
+ };
49
+ type Move = {
50
+ readonly name: string;
51
+ readonly author: string;
52
+ readonly date: string;
53
+ readonly text: string;
54
+ readonly halves: 'both' | 'from-only' | 'to-only';
55
+ };
56
+ type FormatChangeScope = 'run' | 'paragraph' | 'table' | 'row' | 'cell';
57
+ type FormatChange = {
58
+ readonly scope: FormatChangeScope;
59
+ readonly author: string;
60
+ readonly date: string;
61
+ readonly text: string;
62
+ readonly properties: ReadonlyArray<string>;
63
+ };
39
64
  type Field = {
40
65
  readonly source: string;
41
66
  readonly instruction: string;
@@ -57,6 +82,12 @@ type DocxMetadata = {
57
82
  readonly comments: ReadonlyArray<Comment>;
58
83
  readonly insertions: ReadonlyArray<TrackedChange>;
59
84
  readonly deletions: ReadonlyArray<TrackedChange>;
85
+ /** A deletion and the insertion beside it, reported as the one edit they are. */
86
+ readonly replacements: ReadonlyArray<Replacement>;
87
+ /** Text moved elsewhere, joined by the range name that brackets both halves. */
88
+ readonly moves: ReadonlyArray<Move>;
89
+ /** Run, paragraph, table, row or cell properties changed under revision marking. */
90
+ readonly formatChanges: ReadonlyArray<FormatChange>;
60
91
  readonly hiddenText: ReadonlyArray<string>;
61
92
  readonly textBoxes: ReadonlyArray<string>;
62
93
  readonly headersFooters: ReadonlyArray<HeaderFooter>;
@@ -66,4 +97,4 @@ type DocxMetadata = {
66
97
  };
67
98
  declare const extractDocxMetadata: (bytes: Uint8Array) => Promise<Result<DocxMetadata, GraphError>>;
68
99
  export { extractDocxMetadata };
69
- export type { Bookmark, Comment, CustomProp, DocxMetadata, ExternalRel, Field, HeaderFooter, Person, TrackedChange };
100
+ export type { Bookmark, Comment, CustomProp, DocxMetadata, ExternalRel, Field, FormatChange, HeaderFooter, Move, Person, Replacement, TrackedChange };
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod';
2
+ import type { Command, CommandMeta } from './command-types.js';
3
+ declare const schema: z.ZodObject<{
4
+ groupId: z.ZodString;
5
+ threadId: z.ZodString;
6
+ postId: z.ZodString;
7
+ attachmentId: z.ZodString;
8
+ select: z.ZodOptional<z.ZodString>;
9
+ expand: z.ZodOptional<z.ZodString>;
10
+ }, z.core.$strip>;
11
+ declare const execute: Command['execute'];
12
+ declare const meta: CommandMeta;
13
+ export { execute, meta, schema };
@@ -0,0 +1,4 @@
1
+ import type { CommandMeta } from './command-types.js';
2
+ declare const execute: import("./command-types.js").CommandExecute, schema: import("./command-types.js").CommandSchema;
3
+ declare const meta: CommandMeta;
4
+ export { execute, meta, schema };
@@ -0,0 +1,2 @@
1
+ import type { CommandOptionMeta } from './command-types.js';
2
+ export declare const INCLUDE_HIDDEN_FOLDERS_OPTION: CommandOptionMeta;
@@ -0,0 +1,4 @@
1
+ import type { CommandMeta } from './command-types.js';
2
+ declare const execute: import("./command-types.js").CommandExecute, schema: import("./command-types.js").CommandSchema;
3
+ declare const meta: CommandMeta;
4
+ export { execute, meta, schema };
@@ -0,0 +1,4 @@
1
+ import type { CommandMeta } from './command-types.js';
2
+ declare const execute: import("./command-types.js").CommandExecute, schema: import("./command-types.js").CommandSchema;
3
+ declare const meta: CommandMeta;
4
+ export { execute, meta, schema };
@@ -0,0 +1,4 @@
1
+ import type { CommandMeta } from './command-types.js';
2
+ declare const execute: import("./command-types.js").CommandExecute, schema: import("./command-types.js").CommandSchema;
3
+ declare const meta: CommandMeta;
4
+ export { execute, meta, schema };
@@ -0,0 +1,4 @@
1
+ import type { CommandMeta } from './command-types.js';
2
+ declare const execute: import("./command-types.js").CommandExecute, schema: import("./command-types.js").CommandSchema;
3
+ declare const meta: CommandMeta;
4
+ export { execute, meta, schema };
@@ -1,6 +1,8 @@
1
1
  type LoginSummary = {
2
2
  status: 'authenticated';
3
3
  available: ReadonlyArray<string>;
4
+ unlocked: Readonly<Record<string, string>>;
5
+ missing: Readonly<Record<string, string>>;
4
6
  hint: string;
5
7
  };
6
8
  type LoginSummaryInput = {
@@ -13,15 +13,25 @@ declare const schema: z.ZodObject<{}, z.core.$strict>;
13
13
  * loop with no exit: the command that needs elevated says "run login", login
14
14
  * says "authenticated", the command fails identically, forever.
15
15
  *
16
- * When elevated is confirmed missing we escalate through the SAME
17
- * `{ force: true }` rung the `--force` flag uses. That matters: the browser
18
- * adapter has its own `freshCachedToken` probe that short-circuits the dance
19
- * when a valid token is on disk, and only the forced path suppresses it — a
20
- * hand-rolled re-capture here would silently no-op (LESSONS 2026-07-13).
16
+ * When elevated is confirmed missing we try `getElevatedAccessToken` FIRST. It
17
+ * drives a silent SSO against the persistent browser profile and leaves cookies
18
+ * untouched, so the tenant's 90-day ESTSAUTHPERSISTENT session survives and the
19
+ * user sees a window flash rather than a sign-in page. The `{ force: true }`
20
+ * dance calls `context.clearCookies()` to make the OAuth grant re-fire for the
21
+ * BASIC token, and that wipe deletes ESTSAUTHPERSISTENT — which is why routing
22
+ * every elevated expiry through it produced a credential prompt each time, and
23
+ * then destroyed the very session the prompt had just established.
21
24
  *
22
- * Supersedes the 2026-07-13 decision that `login` is a slim confirmation and
23
- * `--force` the only re-capture mechanism: correct about the mechanism, but it
24
- * left the signpost pointing at a command that could not do the job.
25
+ * The silent route is not always available (expired profile cookies, a failed
26
+ * launch, a blocked navigation), so any failure falls back to the forced dance:
27
+ * it can prompt, but it polls for five minutes and a human can finish it.
28
+ *
29
+ * Supersedes the 2026-07-16 decision to escalate straight to `{ force: true }`.
30
+ * That entry feared a hand-rolled re-capture would no-op against the browser
31
+ * adapter's `freshCachedToken` probe; the probe lives in `acquireBothTokens`,
32
+ * not in `acquireElevatedToken`, and elevated freshness is decided upstream by
33
+ * `freshElevatedToken`. Verified live 2026-08-30: silent capture in 17s with no
34
+ * prompt, and the same call failing against a profile a forced login had wiped.
25
35
  */
26
36
  declare const execute: (auth: AuthManager, options?: {
27
37
  force?: boolean;
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import type { Command, CommandMeta } from './command-types.js';
3
3
  declare const schema: z.ZodObject<{
4
+ tenantId: z.ZodOptional<z.ZodString>;
4
5
  url: z.ZodString;
5
6
  }, z.core.$strip>;
6
7
  declare const execute: Command['execute'];
@@ -17,6 +17,7 @@
17
17
  type XmlObject = Record<string, unknown>;
18
18
  declare const parseXml: (xml: string | undefined) => unknown;
19
19
  declare const findAll: (root: unknown, tagName: string) => ReadonlyArray<XmlObject>;
20
+ declare const findAllLocal: (root: unknown, localName: string) => ReadonlyArray<XmlObject>;
20
21
  declare const textOf: (node: unknown) => string;
21
22
  declare const attrOf: (node: XmlObject, name: string) => string;
22
23
  /**
@@ -26,11 +27,35 @@ declare const attrOf: (node: XmlObject, name: string) => string;
26
27
  * flattens every match into a single string for "the visible text of this run".
27
28
  */
28
29
  declare const findAllTexts: (root: unknown, tagName: string) => ReadonlyArray<string>;
30
+ declare const collectText: (node: unknown, tagName: string) => string;
31
+ declare const collectTextLocal: (node: unknown, localName: string) => string;
29
32
  /**
30
- * Concatenate the text content of every element matching `tagName` inside `node`,
31
- * regardless of nesting depth. Used to flatten a `<w:p>` (or `<w:ins>` / `<w:comment>`)
32
- * down to its visible text by gathering every `<w:t>` (or `<w:delText>`) descendant.
33
+ * One element in the `preserveOrder` shape: its tag name, and the node itself
34
+ * (whose `[tag]` holds the ordered children and whose `:@` holds attributes).
33
35
  */
34
- declare const collectText: (node: unknown, tagName: string) => string;
35
- export { attrOf, collectText, findAll, findAllTexts, parseXml, textOf };
36
- export type { XmlObject };
36
+ type OrderedNode = {
37
+ readonly tag: string;
38
+ readonly node: XmlObject;
39
+ };
40
+ declare const parseXmlOrdered: (xml: string | undefined) => unknown;
41
+ /**
42
+ * Every sibling list in the tree, each in document order, elements only.
43
+ *
44
+ * Sibling lists rather than one flat stream: adjacency only means anything
45
+ * WITHIN a parent. A deletion ending one paragraph and an insertion opening the
46
+ * next are consecutive in a flat walk and unrelated in the document.
47
+ */
48
+ declare const orderedSiblingGroups: (root: unknown) => ReadonlyArray<ReadonlyArray<OrderedNode>>;
49
+ /**
50
+ * Every element in true document order, pre-order (an element, then what it
51
+ * contains). Unlike `orderedSiblingGroups`, which reports a whole sibling level
52
+ * before descending, this is the order the start tags appear in the file, which
53
+ * is what reading flat range markers requires: `<w:moveFromRangeStart>` opens a
54
+ * span that a later element sits inside without being its child.
55
+ */
56
+ declare const orderedElements: (root: unknown) => ReadonlyArray<OrderedNode>;
57
+ declare const orderedAttrOf: (node: XmlObject, name: string) => string;
58
+ /** The `collectText` of the ordered shape: flatten every `tagName` descendant to one string. */
59
+ declare const collectOrderedText: (node: XmlObject, tagName: string) => string;
60
+ export { attrOf, collectOrderedText, collectText, collectTextLocal, findAll, findAllLocal, findAllTexts, orderedAttrOf, orderedElements, orderedSiblingGroups, parseXml, parseXmlOrdered, textOf, };
61
+ export type { OrderedNode, XmlObject };
@@ -1,16 +1,19 @@
1
1
  import type { OoxmlZip } from '../../infra/ooxml-zip-adapter.js';
2
2
  /**
3
3
  * PowerPoint comments come in two formats: legacy (`ppt/commentAuthors.xml`
4
- * authors by integer id + `ppt/comments/comment*.xml` `<p:cm authorId dt>`
5
- * with `<p:text>` body) and modern (`ppt/authors.xml` authors by GUID +
6
- * `ppt/comments/*.xml` `<p188:cm authorId created>` with DrawingML `<a:t>`
7
- * body). Both are scanned; authors are resolved by id in either scheme.
8
- * Modern-comment support is best-effort against the p188 (2018/8) schema.
4
+ * authors by integer id + `ppt/comments/comment*.xml` `<cm authorId dt>` with a
5
+ * `<text>` body) and modern (`ppt/authors.xml` authors by GUID +
6
+ * `ppt/comments/*.xml` `<cm authorId created>` with a DrawingML `<t>` body).
7
+ * Elements are matched by local name, so the prefixes a writer happens to bind
8
+ * (`p:` / `p188:` / `a:` from PowerPoint, anything else from a third-party tool)
9
+ * never decide whether a comment is found. Both generations share the local name
10
+ * `cm`, so one pass reads them all; authors are resolved by id in either scheme.
9
11
  */
10
12
  type CommentAuthor = {
11
13
  readonly id: string;
12
14
  readonly name: string;
13
15
  readonly initials: string;
16
+ readonly email: string;
14
17
  };
15
18
  type PptxComment = {
16
19
  readonly author: string;
@@ -0,0 +1,5 @@
1
+ declare const TIER_CAPABILITY: Readonly<Record<string, string>>;
2
+ declare const OPTIONAL_TIERS: readonly ["elevated", "chatsvcagg", "ic3"];
3
+ type OptionalTier = (typeof OPTIONAL_TIERS)[number];
4
+ export { OPTIONAL_TIERS, TIER_CAPABILITY };
5
+ export type { OptionalTier };