ask-marcel-office-cli 1.0.0 → 1.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 (121) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +153 -384
  3. package/dist/cli.js +13222 -94112
  4. package/dist/commands.json +1500 -135
  5. package/dist/composition/build-deps.d.ts +13 -0
  6. package/dist/composition/cli.d.ts +8 -0
  7. package/dist/domain/iso-datetime.d.ts +30 -0
  8. package/dist/domain/utilities/archive-status.d.ts +18 -0
  9. package/dist/domain/utilities/site-url-classifier.d.ts +15 -0
  10. package/dist/index.js +10912 -92275
  11. package/dist/infra/auth.d.ts +56 -3
  12. package/dist/infra/browser-auth.d.ts +100 -10
  13. package/dist/infra/graph-client.d.ts +40 -0
  14. package/dist/infra/legacy-doc-extractor.d.ts +14 -0
  15. package/dist/infra/msg-reader-adapter.d.ts +83 -0
  16. package/dist/infra/network-error.d.ts +9 -0
  17. package/dist/infra/ooxml-media-extractor.d.ts +27 -0
  18. package/dist/infra/ooxml-zip-adapter.d.ts +28 -0
  19. package/dist/infra/pdf-image-extractor.d.ts +18 -0
  20. package/dist/infra/pdf-text-extractor.d.ts +41 -0
  21. package/dist/infra/png-encode.d.ts +15 -0
  22. package/dist/infra/sheetjs-adapter.d.ts +1 -1
  23. package/dist/infra/system-browser-auth.d.ts +35 -0
  24. package/dist/infra/system-browser-loader.d.ts +3 -0
  25. package/dist/infra/token-callback-server.d.ts +31 -0
  26. package/dist/infra/zip-reader.d.ts +32 -0
  27. package/dist/presenter/error-hints.d.ts +41 -0
  28. package/dist/presenter/output.d.ts +2 -1
  29. package/dist/use-cases/commands/build-command.d.ts +30 -8
  30. package/dist/use-cases/commands/command-types.d.ts +53 -1
  31. package/dist/use-cases/commands/convert-calendar-event-attachment-to-markdown.d.ts +15 -0
  32. package/dist/use-cases/commands/{download-drive-item-version-as-pdf.d.ts → convert-calendar-event-attachment-to-pdf.d.ts} +2 -3
  33. package/dist/use-cases/commands/convert-drive-item-zip.d.ts +26 -0
  34. package/dist/use-cases/commands/convert-local-file.d.ts +37 -0
  35. package/dist/use-cases/commands/convert-mail-attachment-to-markdown.d.ts +7 -1
  36. package/dist/use-cases/commands/convert-mail-attachment-to-pdf.d.ts +2 -1
  37. package/dist/use-cases/commands/convert-mail-attachment-zip.d.ts +24 -0
  38. package/dist/use-cases/commands/convert-mail-to-markdown.d.ts +8 -0
  39. package/dist/use-cases/commands/create-mail-draft.d.ts +22 -0
  40. package/dist/use-cases/commands/doc-to-markdown.d.ts +14 -0
  41. package/dist/use-cases/commands/docs-render.d.ts +7 -2
  42. package/dist/use-cases/commands/docs.d.ts +44 -1
  43. package/dist/use-cases/commands/docx-comment-anchors.d.ts +22 -0
  44. package/dist/use-cases/commands/docx-metadata-to-markdown.d.ts +3 -0
  45. package/dist/use-cases/commands/docx-metadata.d.ts +69 -0
  46. package/dist/use-cases/commands/docx-to-markdown.d.ts +8 -3
  47. package/dist/use-cases/commands/download-drive-item-as-markdown.d.ts +9 -0
  48. package/dist/use-cases/commands/download-drive-item-version.d.ts +21 -0
  49. package/dist/use-cases/commands/{download-drive-item-version-as-markdown.d.ts → extract-drive-item-images.d.ts} +0 -1
  50. package/dist/use-cases/commands/extract-local-file-images.d.ts +21 -0
  51. package/dist/use-cases/commands/{download-drive-item-version-content.d.ts → extract-mail-attachment-images.d.ts} +2 -3
  52. package/dist/use-cases/commands/extract-sharepoint-links-in-documents.d.ts +33 -0
  53. package/dist/use-cases/commands/extract-sharepoint-links-in-mail.d.ts +1 -8
  54. package/dist/use-cases/commands/fetch-raw-bytes.d.ts +2 -1
  55. package/dist/use-cases/commands/file-counts.d.ts +7 -0
  56. package/dist/use-cases/commands/filter-archived-sites.d.ts +17 -0
  57. package/dist/use-cases/commands/find-chats-with-user.d.ts +10 -0
  58. package/dist/use-cases/commands/first-sentence.d.ts +12 -0
  59. package/dist/use-cases/commands/get-chat.d.ts +1 -5
  60. package/dist/use-cases/commands/get-excel-chart-image.d.ts +20 -0
  61. package/dist/use-cases/commands/get-excel-used-range.d.ts +7 -2
  62. package/dist/use-cases/commands/get-onenote-page-as-markdown.d.ts +8 -0
  63. package/dist/use-cases/commands/get-teams-chat-message.d.ts +9 -0
  64. package/dist/use-cases/commands/get-todo-task.d.ts +9 -2
  65. package/dist/use-cases/commands/image-extraction.d.ts +10 -0
  66. package/dist/use-cases/commands/iso-datetime-schema.d.ts +21 -0
  67. package/dist/use-cases/commands/link-shape.d.ts +13 -0
  68. package/dist/use-cases/commands/list-accessible-drives.d.ts +14 -0
  69. package/dist/use-cases/commands/list-calendar-event-attachments.d.ts +14 -0
  70. package/dist/use-cases/commands/list-calendar-view-delta.d.ts +2 -2
  71. package/dist/use-cases/commands/list-teams-chat-history.d.ts +16 -0
  72. package/dist/use-cases/commands/list-teams-chat-messages.d.ts +8 -0
  73. package/dist/use-cases/commands/list-teams-chats-with-messages.d.ts +9 -0
  74. package/dist/use-cases/commands/mail-quote-stripper.d.ts +25 -0
  75. package/dist/use-cases/commands/markdown-dispatch.d.ts +29 -0
  76. package/dist/use-cases/commands/markdown-pipeline.d.ts +6 -2
  77. package/dist/use-cases/commands/media-files.d.ts +20 -0
  78. package/dist/use-cases/commands/msg-to-markdown.d.ts +28 -0
  79. package/dist/use-cases/commands/odf-content-to-markdown.d.ts +5 -0
  80. package/dist/use-cases/commands/odf-metadata-to-markdown.d.ts +3 -0
  81. package/dist/use-cases/commands/odf-metadata.d.ts +26 -0
  82. package/dist/use-cases/commands/odf-to-markdown.d.ts +17 -0
  83. package/dist/use-cases/commands/office-extensions.d.ts +13 -0
  84. package/dist/use-cases/commands/office-to-markdown.d.ts +6 -2
  85. package/dist/use-cases/commands/onenote-metadata.d.ts +21 -0
  86. package/dist/use-cases/commands/onenote-resource-embedder.d.ts +3 -0
  87. package/dist/use-cases/commands/ooxml-metadata-to-markdown.d.ts +16 -0
  88. package/dist/use-cases/commands/ooxml-metadata.d.ts +23 -0
  89. package/dist/use-cases/commands/ooxml-xml-walker.d.ts +36 -0
  90. package/dist/use-cases/commands/option-descriptions.d.ts +5 -0
  91. package/dist/use-cases/commands/output-path.d.ts +23 -0
  92. package/dist/use-cases/commands/pdf-to-markdown.d.ts +19 -0
  93. package/dist/use-cases/commands/pptx-comments.d.ts +24 -0
  94. package/dist/use-cases/commands/pptx-metadata-to-markdown.d.ts +3 -0
  95. package/dist/use-cases/commands/pptx-metadata.d.ts +33 -0
  96. package/dist/use-cases/commands/pptx-slides.d.ts +17 -0
  97. package/dist/use-cases/commands/pptx-to-markdown.d.ts +7 -0
  98. package/dist/use-cases/commands/read-mail-attachment.d.ts +15 -0
  99. package/dist/use-cases/commands/resolve-calendar-link.d.ts +8 -0
  100. package/dist/use-cases/commands/resolve-drive-share-link.d.ts +8 -0
  101. package/dist/use-cases/commands/resolve-mail-link.d.ts +8 -0
  102. package/dist/use-cases/commands/resolve-teams-link.d.ts +8 -0
  103. package/dist/use-cases/commands/search-all-accessible-sites.d.ts +12 -0
  104. package/dist/use-cases/commands/search-index-total.d.ts +3 -0
  105. package/dist/use-cases/commands/search-sharepoint-sites-by-name.d.ts +3 -2
  106. package/dist/use-cases/commands/sharepoint-link-extractor.d.ts +17 -12
  107. package/dist/use-cases/commands/text-passthrough.d.ts +15 -2
  108. package/dist/use-cases/commands/todo-parse-uri-rewrite.d.ts +7 -0
  109. package/dist/use-cases/commands/update-mail-draft.d.ts +22 -0
  110. package/dist/use-cases/commands/version-id.d.ts +7 -6
  111. package/dist/use-cases/commands/xlsx-comments.d.ts +29 -0
  112. package/dist/use-cases/commands/xlsx-metadata-to-markdown.d.ts +3 -0
  113. package/dist/use-cases/commands/xlsx-metadata.d.ts +39 -0
  114. package/dist/use-cases/commands/xlsx-to-markdown.d.ts +9 -2
  115. package/dist/use-cases/commands/zip-archive-to-markdown.d.ts +30 -0
  116. package/dist/use-cases/ports/filesystem.d.ts +10 -0
  117. package/docs/COMMANDS.md +259 -0
  118. package/docs/USAGE.md +266 -0
  119. package/docs/commands.json +1500 -135
  120. package/package.json +16 -8
  121. /package/dist/use-cases/commands/{download-onedrive-file-content.d.ts → download-drive-item-content.d.ts} +0 -0
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Renders the `## OneNote metadata` block appended to `get-onenote-page-as-markdown`
3
+ * output under `--include-metadata true`. Mirrors the docx/xlsx/odf metadata-append
4
+ * pattern: a fenced section listing the page's title, created / last-modified
5
+ * timestamps, and the parent section + notebook names (expanded by the command).
6
+ * Pure formatter — every field is optional and omitted when absent.
7
+ */
8
+ type OnenotePage = {
9
+ readonly title?: string;
10
+ readonly createdDateTime?: string;
11
+ readonly lastModifiedDateTime?: string;
12
+ readonly parentSection?: {
13
+ readonly displayName?: string;
14
+ };
15
+ readonly parentNotebook?: {
16
+ readonly displayName?: string;
17
+ };
18
+ };
19
+ declare const formatOnenoteMetadata: (page: OnenotePage) => string;
20
+ export { formatOnenoteMetadata };
21
+ export type { OnenotePage };
@@ -0,0 +1,3 @@
1
+ import type { GraphClient } from '../../infra/graph-client.js';
2
+ declare const embedOnenoteResources: (graph: GraphClient, html: string) => Promise<string>;
3
+ export { embedOnenoteResources };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Shared markdown render primitives for the per-format metadata blocks
3
+ * (docx, xlsx, …). Sections with no entries emit `_(none)_` so the output
4
+ * stays grep-stable — an LLM can ask "is there a Comments section?" and get
5
+ * a yes/no answer without ambiguity.
6
+ *
7
+ * Pipe characters in cell values are escaped as `\|` so the rendered tables
8
+ * stay valid even when property values or comment bodies contain literal pipes.
9
+ */
10
+ declare const NONE = "_(none)_";
11
+ declare const escapeCell: (s: string) => string;
12
+ declare const renderKv: (props: Readonly<Record<string, string>>) => string;
13
+ declare const renderTable: (rows: ReadonlyArray<ReadonlyArray<string>>, headers: ReadonlyArray<string>) => string;
14
+ declare const renderBullets: (items: ReadonlyArray<string>) => string;
15
+ declare const renderMacros: (macros: ReadonlyArray<string>) => string;
16
+ export { escapeCell, NONE, renderBullets, renderKv, renderMacros, renderTable };
@@ -0,0 +1,23 @@
1
+ import type { OoxmlZip } from '../../infra/ooxml-zip-adapter.js';
2
+ /**
3
+ * The OOXML package-level metadata that is identical across .docx / .xlsx /
4
+ * .pptx: the three `docProps/*` property parts and every external
5
+ * relationship in the package. Per-format modules (docx-metadata,
6
+ * xlsx-metadata, …) compose these with their own body-specific extractors.
7
+ */
8
+ type CustomProp = {
9
+ readonly name: string;
10
+ readonly value: string;
11
+ };
12
+ type ExternalRel = {
13
+ readonly source: string;
14
+ readonly type: string;
15
+ readonly target: string;
16
+ };
17
+ declare const extractCoreProps: (zip: OoxmlZip) => Readonly<Record<string, string>>;
18
+ declare const extractAppProps: (zip: OoxmlZip) => Readonly<Record<string, string>>;
19
+ declare const extractCustomProps: (zip: OoxmlZip) => ReadonlyArray<CustomProp>;
20
+ declare const extractExternalRels: (zip: OoxmlZip) => ReadonlyArray<ExternalRel>;
21
+ declare const extractMacros: (zip: OoxmlZip) => ReadonlyArray<string>;
22
+ export { extractAppProps, extractCoreProps, extractCustomProps, extractExternalRels, extractMacros };
23
+ export type { CustomProp, ExternalRel };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Tiny generic walkers over a fast-xml-parser tree. OOXML is namespace-
3
+ * heavy (`w:`, `cp:`, `dc:`, `dcterms:`, `vt:`, `w15:`, `x:`, `p:`, ...) and
4
+ * the only traversal primitives we need are: find-every-element-by-tag-name,
5
+ * read a single attribute, read the leaf text content. Keeping these here
6
+ * lets the per-format metadata modules focus on what to extract, not how to
7
+ * traverse.
8
+ *
9
+ * fast-xml-parser tree shape:
10
+ * - element names are object keys (with `w:` etc. prefix preserved)
11
+ * - attributes are keys prefixed with `@_` (e.g. `@_w:val`, `@_TargetMode`)
12
+ * - text content sits under `#text` when the element also has attributes,
13
+ * OR directly as a string when the element has only text and no attrs
14
+ * - repeated same-named children come as an array; single occurrences as
15
+ * a bare object — every walker must handle both shapes
16
+ */
17
+ type XmlObject = Record<string, unknown>;
18
+ declare const parseXml: (xml: string | undefined) => unknown;
19
+ declare const findAll: (root: unknown, tagName: string) => ReadonlyArray<XmlObject>;
20
+ declare const textOf: (node: unknown) => string;
21
+ declare const attrOf: (node: XmlObject, name: string) => string;
22
+ /**
23
+ * Yield each occurrence of `tagName` inside `node` as its own text value.
24
+ * Used for leaf-text elements like `<w:instrText>` where each instance is a
25
+ * distinct entry (one MERGEFIELD per occurrence) — unlike collectText, which
26
+ * flattens every match into a single string for "the visible text of this run".
27
+ */
28
+ declare const findAllTexts: (root: unknown, tagName: string) => ReadonlyArray<string>;
29
+ /**
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
+ */
34
+ declare const collectText: (node: unknown, tagName: string) => string;
35
+ export { attrOf, collectText, findAll, findAllTexts, parseXml, textOf };
36
+ export type { XmlObject };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * `--drive-id` description for every generic OneDrive / SharePoint drive-item
3
+ * command. Always points the caller at how to obtain a drive id.
4
+ */
5
+ export declare const DRIVE_ID_DESCRIPTION = "Microsoft Graph drive ID. Use `ask-marcel list-drives` for the personal OneDrive, or `ask-marcel list-sharepoint-site-drives --site-id <id>` for a SharePoint document library.";
@@ -33,5 +33,28 @@ export type OutputPathError = {
33
33
  readonly type: 'passthrough_extension_mismatch';
34
34
  readonly contentType: string;
35
35
  readonly requestedExtension: string;
36
+ } | {
37
+ readonly type: 'inline_too_large';
38
+ readonly base64Length: number;
39
+ };
40
+ export type OutputDirError = {
41
+ readonly type: 'no_media';
42
+ } | {
43
+ readonly type: 'empty_path';
44
+ } | {
45
+ readonly type: 'write_failed';
46
+ readonly message: string;
36
47
  };
37
48
  export declare const persistIfRequested: (fs: FileSystem, outputPath: string | undefined, data: unknown) => Promise<Result<unknown, OutputPathError>>;
49
+ /**
50
+ * Sibling of `persistIfRequested` for the global `--output-dir` flag. When a
51
+ * command returns a `media` array (`{ count, media: [{ path, base64, ... }] }`,
52
+ * from the image-extraction commands) and `--output-dir` is set, write each
53
+ * image to `<dir>/<flattened-path>` and replace its `base64` with `savedTo`.
54
+ * The media `path` is flattened (`pdf/page2/Im0.png` → `pdf_page2_Im0.png`)
55
+ * rather than reduced to its basename, because PDF page-image keys (`Im0`, …)
56
+ * repeat across pages — `basename` alone would collide and silently overwrite.
57
+ * The filesystem port auto-creates the directory. Anything without a media
58
+ * array returns `no_media` so the CLI can surface a clear error.
59
+ */
60
+ export declare const persistMediaIfRequested: (fs: FileSystem, outputDir: string | undefined, data: unknown) => Promise<Result<unknown, OutputDirError>>;
@@ -0,0 +1,19 @@
1
+ import type { Result } from '../../domain/result.js';
2
+ import type { GraphError } from '../../infra/graph-client.js';
3
+ type PdfTextEnvelope = {
4
+ readonly contentType: 'text/plain';
5
+ readonly size: number;
6
+ readonly text: string;
7
+ readonly pageCount: number;
8
+ };
9
+ /**
10
+ * Convert a PDF to a plain-text envelope by extracting its text layer (via unpdf).
11
+ * A born-digital PDF yields its reading-order text; a scanned / image-only PDF has
12
+ * no text layer, so the caller's `noTextHint` (pointing at the matching *-as-pdf
13
+ * command + a vision model) is returned as a 415 rather than an empty body. Output
14
+ * is text/plain, not structured markdown — pdfjs flattens layout into reading order.
15
+ * `size` is the byte length of the produced text (what `--output-path` writes),
16
+ * consistent with the other converters — NOT the source PDF's byte count.
17
+ */
18
+ declare const pdfToMarkdown: (bytes: Uint8Array, noTextHint: string) => Promise<Result<PdfTextEnvelope, GraphError>>;
19
+ export { pdfToMarkdown };
@@ -0,0 +1,24 @@
1
+ import type { OoxmlZip } from '../../infra/ooxml-zip-adapter.js';
2
+ /**
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.
9
+ */
10
+ type CommentAuthor = {
11
+ readonly id: string;
12
+ readonly name: string;
13
+ readonly initials: string;
14
+ };
15
+ type PptxComment = {
16
+ readonly author: string;
17
+ readonly date: string;
18
+ readonly text: string;
19
+ readonly slide?: string;
20
+ };
21
+ declare const extractCommentAuthors: (zip: OoxmlZip) => ReadonlyArray<CommentAuthor>;
22
+ declare const extractComments: (zip: OoxmlZip, authors: ReadonlyArray<CommentAuthor>) => ReadonlyArray<PptxComment>;
23
+ export { extractCommentAuthors, extractComments };
24
+ export type { CommentAuthor, PptxComment };
@@ -0,0 +1,3 @@
1
+ import type { PptxMetadata } from './pptx-metadata.js';
2
+ declare const formatPptxMetadata: (meta: PptxMetadata) => string;
3
+ export { formatPptxMetadata };
@@ -0,0 +1,33 @@
1
+ import type { Result } from '../../domain/result.js';
2
+ import type { GraphError } from '../../infra/graph-client.js';
3
+ import type { CustomProp, ExternalRel } from './ooxml-metadata.js';
4
+ import type { CommentAuthor, PptxComment } from './pptx-comments.js';
5
+ import type { Slide } from './pptx-slides.js';
6
+ /**
7
+ * Pulls the side-channel content out of a .pptx zip — everything a user can
8
+ * author that the rendered slide PDF never shows: core / app / custom doc
9
+ * properties, external relationships, slide tags (key/value), comment authors
10
+ * and comments (legacy + modern), and per-slide title / speaker notes / hidden
11
+ * flag. Package-level parts come from the shared ooxml-metadata module; this
12
+ * file owns the presentation-specific parts.
13
+ */
14
+ type PropMap = Readonly<Record<string, string>>;
15
+ type SlideTag = {
16
+ readonly source: string;
17
+ readonly name: string;
18
+ readonly value: string;
19
+ };
20
+ type PptxMetadata = {
21
+ readonly core: PropMap;
22
+ readonly app: PropMap;
23
+ readonly custom: ReadonlyArray<CustomProp>;
24
+ readonly externalRels: ReadonlyArray<ExternalRel>;
25
+ readonly slideTags: ReadonlyArray<SlideTag>;
26
+ readonly commentAuthors: ReadonlyArray<CommentAuthor>;
27
+ readonly comments: ReadonlyArray<PptxComment>;
28
+ readonly slides: ReadonlyArray<Slide>;
29
+ readonly macros: ReadonlyArray<string>;
30
+ };
31
+ declare const extractPptxMetadata: (bytes: Uint8Array) => Promise<Result<PptxMetadata, GraphError>>;
32
+ export { extractPptxMetadata };
33
+ export type { CommentAuthor, PptxComment, PptxMetadata, Slide, SlideTag };
@@ -0,0 +1,17 @@
1
+ import type { OoxmlZip } from '../../infra/ooxml-zip-adapter.js';
2
+ /**
3
+ * Per-slide extraction: the hidden flag (`<p:sld show="0">`), the title
4
+ * placeholder text, and the speaker notes resolved through the slide's
5
+ * relationship part. Speaker notes are the headline pptx side-channel —
6
+ * presenter-authored text that never appears on the rendered slide.
7
+ */
8
+ type Slide = {
9
+ readonly name: string;
10
+ readonly hidden: boolean;
11
+ readonly title: string;
12
+ readonly notes: string;
13
+ readonly text: string;
14
+ };
15
+ declare const extractSlides: (zip: OoxmlZip) => ReadonlyArray<Slide>;
16
+ export { extractSlides };
17
+ export type { Slide };
@@ -0,0 +1,7 @@
1
+ import type { Result } from '../../domain/result.js';
2
+ import type { GraphError } from '../../infra/graph-client.js';
3
+ import type { MarkdownEnvelope } from './docx-to-markdown.js';
4
+ declare const pptxToMarkdown: (bytes: Uint8Array, options?: {
5
+ readonly includeMetadata?: boolean;
6
+ }) => Promise<Result<MarkdownEnvelope, GraphError>>;
7
+ export { pptxToMarkdown };
@@ -0,0 +1,15 @@
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
+ messageId: z.ZodString;
7
+ attachmentId: z.ZodString;
8
+ includeMetadata: z.ZodOptional<z.ZodEnum<{
9
+ true: "true";
10
+ false: "false";
11
+ }>>;
12
+ }, z.core.$strip>;
13
+ declare const execute: (graph: GraphClient, params: Record<string, string>) => Promise<Result<unknown, GraphError>>;
14
+ declare const meta: CommandMeta;
15
+ export { execute, meta, schema };
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ import type { Command, CommandMeta } from './command-types.js';
3
+ declare const schema: z.ZodObject<{
4
+ url: z.ZodURL;
5
+ }, z.core.$strip>;
6
+ declare const execute: Command['execute'];
7
+ declare const meta: CommandMeta;
8
+ export { execute, meta, schema };
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ import type { Command, CommandMeta } from './command-types.js';
3
+ declare const schema: z.ZodObject<{
4
+ url: z.ZodURL;
5
+ }, z.core.$strip>;
6
+ declare const execute: Command['execute'];
7
+ declare const meta: CommandMeta;
8
+ export { execute, meta, schema };
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ import type { Command, CommandMeta } from './command-types.js';
3
+ declare const schema: z.ZodObject<{
4
+ url: z.ZodURL;
5
+ }, z.core.$strip>;
6
+ declare const execute: Command['execute'];
7
+ declare const meta: CommandMeta;
8
+ export { execute, meta, schema };
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ import type { Command, CommandMeta } from './command-types.js';
3
+ declare const schema: z.ZodObject<{
4
+ url: z.ZodURL;
5
+ }, z.core.$strip>;
6
+ declare const execute: Command['execute'];
7
+ declare const meta: CommandMeta;
8
+ export { execute, meta, schema };
@@ -0,0 +1,12 @@
1
+ import { z } from 'zod';
2
+ import type { Command, CommandMeta } from './command-types.js';
3
+ declare const schema: z.ZodObject<{
4
+ query: z.ZodOptional<z.ZodString>;
5
+ countFiles: z.ZodOptional<z.ZodEnum<{
6
+ true: "true";
7
+ false: "false";
8
+ }>>;
9
+ }, z.core.$strip>;
10
+ declare const execute: Command['execute'];
11
+ declare const meta: CommandMeta;
12
+ export { execute, meta, schema };
@@ -0,0 +1,3 @@
1
+ import type { GraphClient } from '../../infra/graph-client.js';
2
+ declare const searchIndexTotal: (graph: GraphClient, entityType: string, queryString?: string) => Promise<number | undefined>;
3
+ export { searchIndexTotal };
@@ -1,4 +1,5 @@
1
- import type { CommandMeta } from './command-types.js';
2
- declare const execute: import("./command-types.js").CommandExecute, schema: import("./command-types.js").CommandSchema;
1
+ import type { Command, CommandMeta } from './command-types.js';
2
+ declare const schema: import("./command-types.js").CommandSchema;
3
+ declare const execute: Command['execute'];
3
4
  declare const meta: CommandMeta;
4
5
  export { execute, meta, schema };
@@ -1,14 +1,19 @@
1
- /**
2
- * Pure helpers for `extract-sharepoint-links-in-mail`.
3
- *
4
- * `extractSharepointUrls(htmlBody)` finds every `https://*.sharepoint.com/...`
5
- * URL inside an HTML mail body — both `<a href=...>` and bare-text
6
- * occurrences — and returns the deduplicated list.
7
- *
8
- * `buildShareToken(url)` encodes a URL for Graph's `/shares/{token}`
9
- * resolver per [shares-get](https://learn.microsoft.com/en-us/graph/api/shares-get):
10
- * `u!` + base64url(url) with no padding.
11
- */
1
+ import type { GraphClient } from '../../infra/graph-client.js';
2
+ type ResolvedLink = {
3
+ readonly url: string;
4
+ readonly driveId?: string;
5
+ readonly itemId?: string;
6
+ readonly name?: string;
7
+ readonly webUrl?: string;
8
+ readonly error?: string;
9
+ };
10
+ type ResolvedLinks = {
11
+ readonly links: ReadonlyArray<ResolvedLink>;
12
+ readonly truncated: boolean;
13
+ readonly skippedCount: number;
14
+ };
12
15
  declare const extractSharepointUrls: (htmlBody: string) => ReadonlyArray<string>;
13
16
  declare const buildShareToken: (url: string) => string;
14
- export { buildShareToken, extractSharepointUrls };
17
+ declare const resolveSharepointUrls: (graph: GraphClient, urls: ReadonlyArray<string>) => Promise<ResolvedLinks>;
18
+ export { buildShareToken, extractSharepointUrls, resolveSharepointUrls };
19
+ export type { ResolvedLink, ResolvedLinks };
@@ -18,7 +18,20 @@
18
18
  * The user wanted a PDF and the source IS a PDF, so return its
19
19
  * bytes directly.
20
20
  */
21
- declare const PLAIN_TEXT_EXTENSIONS: ReadonlySet<string>;
21
+ declare const extensionOf: (name: string) => string;
22
22
  declare const isPlainTextFilename: (name: string) => boolean;
23
23
  declare const isPdfSource: (name: string) => boolean;
24
- export { isPdfSource, isPlainTextFilename, PLAIN_TEXT_EXTENSIONS };
24
+ /**
25
+ * Content-sniff for text — decode bytes STRICTLY as UTF-8. Returns the decoded
26
+ * string when the bytes are valid UTF-8, `undefined` when they are not (i.e.
27
+ * binary). The conversion / content commands use this instead of trusting a
28
+ * file extension: it detects any text file regardless of name AND can never
29
+ * mangle binary into `�` (the non-fatal decoder's silent corruption — the bug
30
+ * a binary file named `.txt` triggered). Non-UTF-8-encoded text (UTF-16, etc.)
31
+ * is reported as binary, which is faithful (base64) rather than lossy.
32
+ *
33
+ * `{ fatal: true }` makes `decode` throw on the first invalid sequence; the
34
+ * try/catch is a pure-domain fallback around a native synchronous thrower.
35
+ */
36
+ declare const decodeUtf8Text: (bytes: Uint8Array) => string | undefined;
37
+ export { decodeUtf8Text, extensionOf, isPdfSource, isPlainTextFilename };
@@ -0,0 +1,7 @@
1
+ import type { GraphError } from '../../infra/graph-client.js';
2
+ type TodoQuirkParams = {
3
+ readonly select?: string;
4
+ readonly orderby?: string;
5
+ };
6
+ declare const rewriteTodoTitleQuirk: (error: GraphError, params: TodoQuirkParams) => GraphError | undefined;
7
+ export { rewriteTodoTitleQuirk };
@@ -0,0 +1,22 @@
1
+ import { z } from 'zod';
2
+ import type { Command, CommandMeta } from './command-types.js';
3
+ declare const schema: z.ZodObject<{
4
+ messageId: z.ZodString;
5
+ subject: z.ZodOptional<z.ZodString>;
6
+ bodyContent: z.ZodOptional<z.ZodString>;
7
+ bodyContentType: z.ZodOptional<z.ZodEnum<{
8
+ Text: "Text";
9
+ HTML: "HTML";
10
+ }>>;
11
+ toRecipients: z.ZodOptional<z.ZodString>;
12
+ ccRecipients: z.ZodOptional<z.ZodString>;
13
+ bccRecipients: z.ZodOptional<z.ZodString>;
14
+ importance: z.ZodOptional<z.ZodEnum<{
15
+ Low: "Low";
16
+ Normal: "Normal";
17
+ High: "High";
18
+ }>>;
19
+ }, z.core.$strip>;
20
+ declare const execute: Command['execute'];
21
+ declare const meta: CommandMeta;
22
+ export { execute, meta, schema };
@@ -1,16 +1,17 @@
1
1
  /**
2
2
  * Graph's historical-version endpoints template `{version-id}` as a string
3
3
  * that must look like a stringified float (`"79.0"`, `"4.0"`). An LLM
4
- * (reasonably) types `79`, and the response is wildly inconsistent across
5
- * the three sibling commands:
4
+ * (reasonably) types `79`, and Graph's response is wildly inconsistent
5
+ * across the three internal pipelines that the consolidated
6
+ * `download-drive-item-version` command dispatches to:
6
7
  *
7
- * - download-drive-item-version-content → `invalidRequest`
8
- * - download-drive-item-version-as-markdown → `invalidRequest`
9
- * - download-drive-item-version-as-pdf → accepts (Graph quirk)
8
+ * - --format original → `invalidRequest`
9
+ * - --format markdown → `invalidRequest`
10
+ * - --format pdf → accepts (Graph quirk)
10
11
  *
11
12
  * Audit round-6 §1.4 caught the inconsistency. Normalize at the CLI
12
13
  * boundary: a pure-integer version-id gets `.0` appended before path
13
- * substitution, so all three commands accept both spellings.
14
+ * substitution, so all three formats accept both spellings.
14
15
  *
15
16
  * Already-floated values (`79.0`, `12.5`, `4.1`) and non-numeric values
16
17
  * (a bad input the schema will then reject) pass through unchanged.
@@ -0,0 +1,29 @@
1
+ import type { OoxmlZip } from '../../infra/ooxml-zip-adapter.js';
2
+ /**
3
+ * Workbook annotations + the identities behind them. xlsx has two comment
4
+ * formats: legacy cell comments (xl/comments*.xml, author by index into an
5
+ * <authors> list) and modern threaded comments (xl/threadedComments/*, author
6
+ * by personId resolved through xl/persons/person.xml). Both are user-authored
7
+ * and invisible in the value-rendered body.
8
+ */
9
+ type CellComment = {
10
+ readonly cell: string;
11
+ readonly author: string;
12
+ readonly text: string;
13
+ };
14
+ type ThreadedComment = {
15
+ readonly cell: string;
16
+ readonly author: string;
17
+ readonly date: string;
18
+ readonly text: string;
19
+ };
20
+ type Person = {
21
+ readonly id: string;
22
+ readonly displayName: string;
23
+ readonly userId: string;
24
+ };
25
+ declare const extractPeople: (zip: OoxmlZip) => ReadonlyArray<Person>;
26
+ declare const extractLegacyComments: (zip: OoxmlZip) => ReadonlyArray<CellComment>;
27
+ declare const extractThreadedComments: (zip: OoxmlZip, people: ReadonlyArray<Person>) => ReadonlyArray<ThreadedComment>;
28
+ export { extractLegacyComments, extractPeople, extractThreadedComments };
29
+ export type { CellComment, Person, ThreadedComment };
@@ -0,0 +1,3 @@
1
+ import type { XlsxMetadata } from './xlsx-metadata.js';
2
+ declare const formatXlsxMetadata: (meta: XlsxMetadata) => string;
3
+ export { formatXlsxMetadata };
@@ -0,0 +1,39 @@
1
+ import type { Result } from '../../domain/result.js';
2
+ import type { GraphError } from '../../infra/graph-client.js';
3
+ import type { CustomProp, ExternalRel } from './ooxml-metadata.js';
4
+ import type { CellComment, Person, ThreadedComment } from './xlsx-comments.js';
5
+ /**
6
+ * Pulls the side-channel content out of a .xlsx zip — everything a user can
7
+ * author that the value-rendered markdown body (cells per sheet) never shows:
8
+ * core / app / custom doc properties, external relationships, defined names
9
+ * (named ranges & formulas), hidden / very-hidden sheets, legacy cell
10
+ * comments, threaded comments, and the persons registry behind them.
11
+ *
12
+ * Package-level parts (docProps/*, every *.rels) come from the shared
13
+ * ooxml-metadata module; this file owns the workbook-specific parts.
14
+ */
15
+ type PropMap = Readonly<Record<string, string>>;
16
+ type DefinedName = {
17
+ readonly name: string;
18
+ readonly refersTo: string;
19
+ readonly hidden: boolean;
20
+ };
21
+ type Sheet = {
22
+ readonly name: string;
23
+ readonly state: string;
24
+ };
25
+ type XlsxMetadata = {
26
+ readonly core: PropMap;
27
+ readonly app: PropMap;
28
+ readonly custom: ReadonlyArray<CustomProp>;
29
+ readonly externalRels: ReadonlyArray<ExternalRel>;
30
+ readonly definedNames: ReadonlyArray<DefinedName>;
31
+ readonly hiddenSheets: ReadonlyArray<Sheet>;
32
+ readonly comments: ReadonlyArray<CellComment>;
33
+ readonly threadedComments: ReadonlyArray<ThreadedComment>;
34
+ readonly people: ReadonlyArray<Person>;
35
+ readonly macros: ReadonlyArray<string>;
36
+ };
37
+ declare const extractXlsxMetadata: (bytes: Uint8Array) => Promise<Result<XlsxMetadata, GraphError>>;
38
+ export { extractXlsxMetadata };
39
+ export type { CellComment, DefinedName, Person, Sheet, ThreadedComment, XlsxMetadata };
@@ -1,6 +1,13 @@
1
1
  import type { Result } from '../../domain/result.js';
2
2
  import type { GraphError } from '../../infra/graph-client.js';
3
3
  import type { MarkdownEnvelope } from './docx-to-markdown.js';
4
+ type XlsxToMarkdownOptions = {
5
+ readonly includeMetadata?: boolean;
6
+ readonly maxCells?: number;
7
+ };
4
8
  declare const csvToMarkdownTable: (csv: string) => string;
5
- declare const xlsxToMarkdown: (bytes: Uint8Array) => Result<MarkdownEnvelope, GraphError>;
6
- export { csvToMarkdownTable, xlsxToMarkdown };
9
+ declare const renderCsvCapped: (csv: string, maxCells?: number) => string;
10
+ declare const csvToMarkdownSection: (name: string, csv: string, maxCells?: number) => string;
11
+ declare const xlsxToMarkdown: (bytes: Uint8Array, opts?: XlsxToMarkdownOptions) => Promise<Result<MarkdownEnvelope, GraphError>>;
12
+ export { csvToMarkdownSection, csvToMarkdownTable, renderCsvCapped, xlsxToMarkdown };
13
+ export type { XlsxToMarkdownOptions };
@@ -0,0 +1,30 @@
1
+ import type { Result } from '../../domain/result.js';
2
+ import type { GraphError } from '../../infra/graph-client.js';
3
+ /**
4
+ * Shared "unzip + convert every contained file" core behind
5
+ * `convert-drive-item-zip` (a OneDrive / SharePoint .zip),
6
+ * `convert-mail-attachment-zip` (an Outlook .zip attachment), and
7
+ * `convert-local-file` (a .zip on disk). Each entry is run through the same
8
+ * `bytesToMarkdown` dispatch the markdown commands use; an entry the dispatch
9
+ * can't convert (image, binary, nested archive, scanned PDF) is LISTED with a
10
+ * note instead of failing the whole archive. Notes use the container-neutral
11
+ * NESTED_HINTS (QA-007): entries live INSIDE the zip, so caller-specific
12
+ * sibling-command pointers (`extract-drive-item-images`, …) cannot reach them.
13
+ */
14
+ declare const MAX_ENTRIES = 100;
15
+ type FileResult = {
16
+ readonly path: string;
17
+ readonly contentType?: string;
18
+ readonly size?: number;
19
+ readonly text?: string;
20
+ readonly note?: string;
21
+ };
22
+ type ZipArchiveResult = {
23
+ readonly count: number;
24
+ readonly truncated?: true;
25
+ readonly totalEntries?: number;
26
+ readonly files: ReadonlyArray<FileResult>;
27
+ };
28
+ declare const convertZipArchive: (bytes: Uint8Array, includeMetadata: boolean) => Promise<Result<ZipArchiveResult, GraphError>>;
29
+ export { convertZipArchive, MAX_ENTRIES };
30
+ export type { FileResult, ZipArchiveResult };
@@ -10,8 +10,18 @@ export type FileSystemError = {
10
10
  };
11
11
  export type FileSystem = {
12
12
  readonly readJson: <T>(path: string) => Promise<Result<T, FileSystemError>>;
13
+ /**
14
+ * Read a file's raw bytes. Used by `convert-local-file` to feed a local
15
+ * document into the same conversion dispatch the Graph-backed commands use.
16
+ */
17
+ readonly readBytes: (path: string) => Promise<Result<Uint8Array, FileSystemError>>;
13
18
  readonly writeText: (path: string, content: string) => Promise<Result<void, FileSystemError>>;
14
19
  readonly writeBytes: (path: string, bytes: Uint8Array) => Promise<Result<void, FileSystemError>>;
20
+ /**
21
+ * Restrict a file's permission bits (e.g. 0o600 on the token cache so
22
+ * other local users cannot read cached secrets — QA-001).
23
+ */
24
+ readonly chmod: (path: string, mode: number) => Promise<Result<void, FileSystemError>>;
15
25
  readonly deleteIfExists: (path: string) => Promise<Result<void, FileSystemError>>;
16
26
  /**
17
27
  * Recursively delete a directory (and all its contents). Used by `logout`