@cairn-tool/cairn 3.1.0 → 3.3.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 (91) hide show
  1. package/README.md +74 -28
  2. package/dist/agent/audit/index.d.ts +6 -6
  3. package/dist/agent/audit/index.js +6 -17
  4. package/dist/agent/audit/index.js.map +1 -1
  5. package/dist/archive/run.d.ts +14 -7
  6. package/dist/archive/run.js +4 -1
  7. package/dist/archive/run.js.map +1 -1
  8. package/dist/archive/sets.d.ts +26 -1
  9. package/dist/archive/sets.js +108 -0
  10. package/dist/archive/sets.js.map +1 -1
  11. package/dist/atomic-write.d.ts +22 -0
  12. package/dist/atomic-write.js +42 -0
  13. package/dist/atomic-write.js.map +1 -0
  14. package/dist/binary-kind.d.ts +13 -0
  15. package/dist/binary-kind.js +25 -0
  16. package/dist/binary-kind.js.map +1 -0
  17. package/dist/cli.js +48 -0
  18. package/dist/cli.js.map +1 -1
  19. package/dist/commands/archive.js +11 -5
  20. package/dist/commands/archive.js.map +1 -1
  21. package/dist/commands/jira.js +1 -21
  22. package/dist/commands/jira.js.map +1 -1
  23. package/dist/commands/pdf.d.ts +29 -0
  24. package/dist/commands/pdf.js +345 -0
  25. package/dist/commands/pdf.js.map +1 -0
  26. package/dist/contract/registry.js +65 -1
  27. package/dist/contract/registry.js.map +1 -1
  28. package/dist/contract/schemas/index.js +2 -0
  29. package/dist/contract/schemas/index.js.map +1 -1
  30. package/dist/contract/schemas/pdf.d.ts +2 -0
  31. package/dist/contract/schemas/pdf.js +292 -0
  32. package/dist/contract/schemas/pdf.js.map +1 -0
  33. package/dist/jira/adf/to-markdown.js +2 -32
  34. package/dist/jira/adf/to-markdown.js.map +1 -1
  35. package/dist/markdown-stringify.d.ts +35 -0
  36. package/dist/markdown-stringify.js +44 -0
  37. package/dist/markdown-stringify.js.map +1 -0
  38. package/dist/pdf/attachments.d.ts +50 -0
  39. package/dist/pdf/attachments.js +229 -0
  40. package/dist/pdf/attachments.js.map +1 -0
  41. package/dist/pdf/diagnostics.d.ts +110 -0
  42. package/dist/pdf/diagnostics.js +137 -0
  43. package/dist/pdf/diagnostics.js.map +1 -0
  44. package/dist/pdf/document.d.ts +159 -0
  45. package/dist/pdf/document.js +272 -0
  46. package/dist/pdf/document.js.map +1 -0
  47. package/dist/pdf/forms.d.ts +9 -0
  48. package/dist/pdf/forms.js +101 -0
  49. package/dist/pdf/forms.js.map +1 -0
  50. package/dist/pdf/inspect.d.ts +42 -0
  51. package/dist/pdf/inspect.js +232 -0
  52. package/dist/pdf/inspect.js.map +1 -0
  53. package/dist/pdf/layout.d.ts +78 -0
  54. package/dist/pdf/layout.js +524 -0
  55. package/dist/pdf/layout.js.map +1 -0
  56. package/dist/pdf/outline.d.ts +30 -0
  57. package/dist/pdf/outline.js +127 -0
  58. package/dist/pdf/outline.js.map +1 -0
  59. package/dist/pdf/read.d.ts +62 -0
  60. package/dist/pdf/read.js +193 -0
  61. package/dist/pdf/read.js.map +1 -0
  62. package/dist/pdf/struct.d.ts +35 -0
  63. package/dist/pdf/struct.js +226 -0
  64. package/dist/pdf/struct.js.map +1 -0
  65. package/dist/pdf/text.d.ts +71 -0
  66. package/dist/pdf/text.js +166 -0
  67. package/dist/pdf/text.js.map +1 -0
  68. package/dist/pdf/to-markdown.d.ts +22 -0
  69. package/dist/pdf/to-markdown.js +289 -0
  70. package/dist/pdf/to-markdown.js.map +1 -0
  71. package/dist/pdf/types.d.ts +205 -0
  72. package/dist/pdf/types.js +2 -0
  73. package/dist/pdf/types.js.map +1 -0
  74. package/dist/pdf/validate.d.ts +51 -0
  75. package/dist/pdf/validate.js +136 -0
  76. package/dist/pdf/validate.js.map +1 -0
  77. package/dist/serve/pdf-tools.d.ts +2 -0
  78. package/dist/serve/pdf-tools.js +196 -0
  79. package/dist/serve/pdf-tools.js.map +1 -0
  80. package/dist/serve/tools.d.ts +2 -17
  81. package/dist/serve/tools.js +4 -0
  82. package/dist/serve/tools.js.map +1 -1
  83. package/dist/serve/types.d.ts +22 -0
  84. package/dist/serve/types.js +2 -0
  85. package/dist/serve/types.js.map +1 -0
  86. package/dist/usage/providers/cursor.d.ts +2 -0
  87. package/dist/usage/providers/cursor.js +570 -0
  88. package/dist/usage/providers/cursor.js.map +1 -0
  89. package/dist/usage/providers/index.js +2 -0
  90. package/dist/usage/providers/index.js.map +1 -1
  91. package/package.json +2 -1
@@ -0,0 +1,205 @@
1
+ import type { DiagnosticSeverity, MappingQuality } from "../mapping-quality.js";
2
+ export type { MappingQuality } from "../mapping-quality.js";
3
+ /**
4
+ * A PDF finding.
5
+ *
6
+ * The third finding family, after `AB###` for agent bundles and `AD###` for ADF
7
+ * conversion. It is structurally a `ConversionDiagnostic` with `node` and
8
+ * `location` replaced by `page` and `construct`, because a PDF finding is
9
+ * positioned by page rather than by a node ancestry trail — stuffing a page
10
+ * number into a field documented as "slash-joined ancestor node types" would be
11
+ * worse than a third shape. `quality` and the quality-to-severity rule are
12
+ * shared with the other two through `src/mapping-quality.ts`.
13
+ */
14
+ export interface PdfDiagnostic {
15
+ code: string;
16
+ severity: DiagnosticSeverity;
17
+ message: string;
18
+ quality: MappingQuality;
19
+ /** 1-based page the finding concerns, when it concerns one page. */
20
+ page?: number;
21
+ /** The PDF construct: a structure role, a filter name, a font name. */
22
+ construct?: string;
23
+ remediation?: string;
24
+ }
25
+ export type PdfCommand = "inspect" | "text" | "outline" | "validate" | "to-markdown" | "attachments" | "forms";
26
+ /** How much text a page carries, relative to its area. */
27
+ export type TextLayer = "present" | "sparse" | "absent";
28
+ /**
29
+ * Document-level facts, present on every command that opened the document.
30
+ *
31
+ * A deliberate divergence from `AdfResult`, where `inventory` is `inspect`-only:
32
+ * a conversion from a tagged document and one from a scan are not the same kind
33
+ * of artifact and must not look identical in the payload. `jq .document.tagged`
34
+ * has to work on a `to-markdown` result.
35
+ */
36
+ export interface PdfDocumentInfo {
37
+ pageCount: number;
38
+ /**
39
+ * The document declares `/MarkInfo <</Marked true>>`.
40
+ *
41
+ * Not the same question as whether a usable structure tree exists — plenty of
42
+ * producers set this and ship an empty tree — which is what `structured`
43
+ * answers by probing.
44
+ */
45
+ tagged: boolean;
46
+ /**
47
+ * Whether any page yielded a structure tree with content.
48
+ *
49
+ * Optional because only the commands that walk pages can answer it; `outline`
50
+ * never opens one. Omitted rather than defaulted, on the rule that a field
51
+ * nobody measured must not read as a measurement.
52
+ */
53
+ structured?: "struct" | "partial" | "none";
54
+ encrypted: boolean;
55
+ /** Omitted for the same reason as `structured`. */
56
+ textLayer?: TextLayer;
57
+ pdfVersion?: string;
58
+ title?: string;
59
+ author?: string;
60
+ subject?: string;
61
+ keywords?: string;
62
+ creator?: string;
63
+ producer?: string;
64
+ /** ISO-8601, when the document's own `D:` string parsed. Omitted, never guessed. */
65
+ created?: string;
66
+ modified?: string;
67
+ }
68
+ export interface PdfPageInfo {
69
+ page: number;
70
+ /** Points, 72 to the inch, with `/Rotate` already applied. */
71
+ width: number;
72
+ height: number;
73
+ rotation: number;
74
+ /** Non-whitespace code points on the page: the evidence behind `textLayer`. */
75
+ characters: number;
76
+ /** Characters per square inch, rounded to 2 dp. The other half of the evidence. */
77
+ density: number;
78
+ textLayer: TextLayer;
79
+ }
80
+ export interface PdfTextPage {
81
+ page: number;
82
+ text: string;
83
+ characters: number;
84
+ }
85
+ export interface PdfOutlineEntry {
86
+ title: string;
87
+ /** 1-based nesting depth. */
88
+ level: number;
89
+ /** 1-based destination page, or null when it did not resolve. */
90
+ page: number | null;
91
+ /** Present only when pdf.js validated the URL's scheme. */
92
+ url?: string;
93
+ children: PdfOutlineEntry[];
94
+ }
95
+ /** One embedded file. Emitted by `pdf attachments`. */
96
+ export interface PdfAttachment {
97
+ /** The name-tree key: the identifier the content lookup takes. */
98
+ id: string;
99
+ /**
100
+ * The basename pdf.js derived. Never used as a path without re-checking —
101
+ * the sanitization this command applies is its own, not pdf.js's.
102
+ */
103
+ filename: string;
104
+ /** The stored name verbatim, including any traversal it carries. */
105
+ rawFilename: string;
106
+ description?: string;
107
+ /** Byte length of the decoded file. Absent when the content could not be read. */
108
+ bytes?: number;
109
+ sha256?: string;
110
+ /** Executable format, when the magic bytes say so. */
111
+ binary?: "elf" | "pe" | "macho";
112
+ /** Absolute path written. Present only under `--extract`. */
113
+ written?: string;
114
+ }
115
+ /** One AcroForm field. Emitted by `pdf forms`. */
116
+ export interface PdfFormField {
117
+ /** Fully-qualified field name. */
118
+ name: string;
119
+ type: string;
120
+ /** 1-based, converted from the 0-based index pdf.js reports. */
121
+ page: number | null;
122
+ value?: string;
123
+ defaultValue?: string;
124
+ readOnly: boolean;
125
+ hidden: boolean;
126
+ /**
127
+ * The field's password flag. The value is still reported: the same bytes are
128
+ * reachable through `pdf text`, so withholding them would be theatre.
129
+ */
130
+ password: boolean;
131
+ charLimit?: number;
132
+ exportValues?: string;
133
+ /** Widgets this field renders as; one field can appear on several pages. */
134
+ widgets: number;
135
+ }
136
+ export interface PdfForm {
137
+ /** `xfa` means the values live in an XML packet this does not read. */
138
+ type: "acroform" | "xfa" | "none";
139
+ fieldCount: number;
140
+ fields: PdfFormField[];
141
+ }
142
+ export interface PdfResult {
143
+ command: PdfCommand;
144
+ ok: boolean;
145
+ /** Input path, or `-` for stdin. */
146
+ source: string;
147
+ document?: PdfDocumentInfo;
148
+ /** inspect */
149
+ pages?: PdfPageInfo[];
150
+ /** text */
151
+ text?: PdfTextPage[];
152
+ /** outline */
153
+ outline?: PdfOutlineEntry[];
154
+ /** to-markdown */
155
+ markdown?: string;
156
+ /** attachments */
157
+ attachments?: PdfAttachment[];
158
+ /** forms */
159
+ form?: PdfForm;
160
+ /** text, to-markdown — present only when `--pages` narrowed the document. */
161
+ selectedPages?: number[];
162
+ /** Where `--output` wrote. */
163
+ output?: string;
164
+ diagnostics: PdfDiagnostic[];
165
+ }
166
+ export type BlockKind = "heading" | "paragraph" | "list" | "listItem" | "table" | "tableRow" | "tableCell" | "code" | "blockquote" | "figure" | "caption";
167
+ export interface InlineSpan {
168
+ text: string;
169
+ bold: boolean;
170
+ italic: boolean;
171
+ code: boolean;
172
+ }
173
+ export interface BlockProvenance {
174
+ path: "struct" | "geometric";
175
+ /** Per block, not per document: one conversion mixes exact and approximate. */
176
+ quality: MappingQuality;
177
+ /** Pages the block's runs came from. A stitched paragraph has two. */
178
+ pages: number[];
179
+ /** The structure-tree role, when `path` is `"struct"`. */
180
+ role?: string;
181
+ }
182
+ /**
183
+ * One block of converted content.
184
+ *
185
+ * `spans` and `children` are both always present and mutually exclusive by
186
+ * kind, rather than a discriminated union, for the reason `AdfNode` is
187
+ * structural: a new kind must be able to reach a diagnostic instead of failing
188
+ * to compile at forty call sites.
189
+ *
190
+ * A flattened table is a run of sibling `paragraph` blocks — never a `table`
191
+ * with one cell. The emitter must be structurally unable to produce a table
192
+ * that was not one.
193
+ */
194
+ export interface Block {
195
+ kind: BlockKind;
196
+ /** heading only, 1..6. */
197
+ level?: number;
198
+ /** list only. */
199
+ ordered?: boolean;
200
+ /** tableCell only. */
201
+ header?: boolean;
202
+ spans: InlineSpan[];
203
+ children: Block[];
204
+ provenance: BlockProvenance;
205
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/pdf/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,51 @@
1
+ import type { OpenDocument } from "./document.js";
2
+ import type { PdfDiagnostic } from "./types.js";
3
+ /**
4
+ * Structural validation, bounded by what pdf.js can actually see.
5
+ *
6
+ * Deliberately **not** a PDF/A, PDF/UA, or PDF/X conformance checker. Full
7
+ * conformance validation is veraPDF's job and is a Java program; claiming it
8
+ * here would be a lie. This is the same line `jira adf validate` draws when it
9
+ * reports `AD100` for a node type it does not model rather than pretending to be
10
+ * Atlassian's schema.
11
+ *
12
+ * Specifically **not** checked, and the docs say so in these words: PDF/A or
13
+ * PDF/UA conformance at any level; digital signature validity, certificate
14
+ * chains, or timestamps; byte-level spec conformance such as `/Length`
15
+ * mismatches or trailing garbage, which pdf.js repairs below the warning
16
+ * threshold; whether the document *renders* correctly, since nothing here
17
+ * rasterizes; whether a glyph in the text layer actually exists in the embedded
18
+ * subset, which is only discoverable at render time; colour spaces, transparency
19
+ * groups, and output intents; and accessibility beyond "a structure tree
20
+ * exists".
21
+ */
22
+ export interface WarningPattern {
23
+ pattern: RegExp;
24
+ code: string;
25
+ message: string;
26
+ quality: "approximate" | "unsupported";
27
+ }
28
+ /**
29
+ * pdf.js warnings this command reports as findings.
30
+ *
31
+ * **The most fragile coupling in the toolset**: these exist only because pdf.js
32
+ * prints a particular English string. That is contained rather than accepted —
33
+ * `tests/unit/pdf-validate.test.ts` drives one case per row from a deliberately
34
+ * damaged fixture, so a pdfjs bump that rewords a message fails the suite
35
+ * instead of quietly turning the check off.
36
+ *
37
+ * An unmatched warning is **not** dropped. It is reported once as `AP120` with
38
+ * the raw text, so a reworded message degrades to "something was recovered here"
39
+ * rather than to silence.
40
+ */
41
+ export declare const WARNING_PATTERNS: readonly WarningPattern[];
42
+ export interface ValidateResult {
43
+ diagnostics: PdfDiagnostic[];
44
+ }
45
+ /**
46
+ * Walks every page and reports what the parser could not do.
47
+ *
48
+ * No `--pages`: a partial validation that reported "valid" would be a lie, so
49
+ * the command always covers the whole document.
50
+ */
51
+ export declare function validateDocument(handle: OpenDocument): Promise<ValidateResult>;
@@ -0,0 +1,136 @@
1
+ import { CODES, DiagnosticSink } from "./diagnostics.js";
2
+ import { hasStructContent } from "./inspect.js";
3
+ /**
4
+ * pdf.js warnings this command reports as findings.
5
+ *
6
+ * **The most fragile coupling in the toolset**: these exist only because pdf.js
7
+ * prints a particular English string. That is contained rather than accepted —
8
+ * `tests/unit/pdf-validate.test.ts` drives one case per row from a deliberately
9
+ * damaged fixture, so a pdfjs bump that rewords a message fails the suite
10
+ * instead of quietly turning the check off.
11
+ *
12
+ * An unmatched warning is **not** dropped. It is reported once as `AP120` with
13
+ * the raw text, so a reworded message degrades to "something was recovered here"
14
+ * rather than to silence.
15
+ */
16
+ export const WARNING_PATTERNS = [
17
+ {
18
+ pattern: /Indexing all PDF objects/i,
19
+ code: CODES.xrefReconstructed,
20
+ message: "The cross-reference table was unusable and the parser rebuilt it by scanning every object",
21
+ quality: "approximate",
22
+ },
23
+ {
24
+ pattern: /(fallback|standard) font|Failed to load font|Cannot load system font|font.*not found/i,
25
+ code: CODES.fontSubstituted,
26
+ message: "A font is not embedded or failed to load, and a substitute was used",
27
+ quality: "approximate",
28
+ },
29
+ {
30
+ pattern: /Unsupported (filter|image|stream)|filter.*not supported|JPX|JBIG2/i,
31
+ code: CODES.filterUnsupported,
32
+ message: "A stream uses a filter the parser could not decode",
33
+ quality: "unsupported",
34
+ },
35
+ {
36
+ pattern: /Unable to read mark info|Invalid Metadata|Skipping invalid Metadata/i,
37
+ code: CODES.metadataUnreadable,
38
+ message: "The document's metadata could not be read",
39
+ quality: "approximate",
40
+ },
41
+ ];
42
+ /**
43
+ * Walks every page and reports what the parser could not do.
44
+ *
45
+ * No `--pages`: a partial validation that reported "valid" would be a lie, so
46
+ * the command always covers the whole document.
47
+ */
48
+ export async function validateDocument(handle) {
49
+ const sink = new DiagnosticSink();
50
+ const { doc } = handle;
51
+ let taggedClaim;
52
+ try {
53
+ const markInfo = await handle.within(() => doc.getMarkInfo());
54
+ taggedClaim = markInfo instanceof Map && markInfo.get("Marked") === true;
55
+ }
56
+ catch {
57
+ taggedClaim = false;
58
+ }
59
+ let structured = 0;
60
+ let probed = 0;
61
+ for (let page = 1; page <= doc.numPages; page += 1) {
62
+ try {
63
+ const proxy = await handle.within(() => doc.getPage(page));
64
+ try {
65
+ await handle.within(() => proxy.getTextContent());
66
+ }
67
+ catch (error) {
68
+ sink.add({
69
+ code: CODES.contentUndecodable,
70
+ severity: "error",
71
+ quality: "unsupported",
72
+ message: `The page's content stream could not be decoded: ${error.message}`,
73
+ page,
74
+ });
75
+ }
76
+ try {
77
+ const tree = await handle.within(() => proxy.getStructTree());
78
+ probed += 1;
79
+ if (hasStructContent(tree))
80
+ structured += 1;
81
+ }
82
+ catch {
83
+ probed += 1;
84
+ }
85
+ proxy.cleanup();
86
+ }
87
+ catch (error) {
88
+ // The raw message, not `classify()`'s: that prefixes "Page N:" for callers
89
+ // with nowhere else to put it, and the finding already carries `page`.
90
+ sink.add({
91
+ code: CODES.pageUnreadable,
92
+ severity: "error",
93
+ quality: "unsupported",
94
+ message: error.message,
95
+ page,
96
+ });
97
+ }
98
+ }
99
+ if (taggedClaim && structured === 0)
100
+ sink.add({
101
+ code: CODES.taggedButEmpty,
102
+ quality: "approximate",
103
+ message: "The document declares /MarkInfo <</Marked true>> but no page carries a usable structure tree",
104
+ });
105
+ if (structured > 0 && structured < probed)
106
+ sink.add({
107
+ code: CODES.structPartial,
108
+ quality: "exact",
109
+ message: `A structure tree is present on ${structured} of ${probed} pages`,
110
+ });
111
+ if (handle.encrypted)
112
+ sink.add({
113
+ code: CODES.encryptedOpenPassword,
114
+ quality: "exact",
115
+ message: "The document is encrypted but opened without a password, so its restrictions are advisory only",
116
+ });
117
+ // The captured-warning channel. Everything above is a structural probe; these
118
+ // are conditions only the parser can see, and it only reports them in prose.
119
+ const unmatched = [];
120
+ for (const notice of handle.notices) {
121
+ const match = WARNING_PATTERNS.find((candidate) => candidate.pattern.test(notice));
122
+ if (match)
123
+ sink.add({ code: match.code, quality: match.quality, message: match.message });
124
+ else if (/^Warning:/i.test(notice))
125
+ unmatched.push(notice);
126
+ }
127
+ for (const notice of unmatched.slice(0, 10))
128
+ sink.add({
129
+ code: CODES.parserWarning,
130
+ quality: "approximate",
131
+ message: `The parser recovered from a condition this tool does not classify: ${notice.slice(0, 300)}`,
132
+ construct: notice.slice(0, 60),
133
+ });
134
+ return { diagnostics: sink.all() };
135
+ }
136
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/pdf/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { ServeTool } from "./types.js";
2
+ export declare const PDF_SERVE_TOOLS: readonly ServeTool[];
@@ -0,0 +1,196 @@
1
+ import fs from "node:fs";
2
+ import { readAttachments } from "../pdf/attachments.js";
3
+ import { withDocument } from "../pdf/document.js";
4
+ import { readForm } from "../pdf/forms.js";
5
+ import { documentSummary, inspectDocument } from "../pdf/inspect.js";
6
+ import { readOutline } from "../pdf/outline.js";
7
+ import { MAX_INPUT_BYTES, parsePageRange, readInput } from "../pdf/read.js";
8
+ import { extractPage, runsToText } from "../pdf/text.js";
9
+ import { toMarkdown } from "../pdf/to-markdown.js";
10
+ import { confine, PathRejected, relativeTo } from "./paths.js";
11
+ /**
12
+ * Read-only PDF tools.
13
+ *
14
+ * Kept out of `tools.ts` because that file is uniformly the Markdown workspace
15
+ * engine and these are not; `SERVE_TOOLS` spreads this array so registration
16
+ * stays one list.
17
+ *
18
+ * Three properties hold across every tool here:
19
+ *
20
+ * **Confined like everything else.** A PDF must live under `--root`. That is a
21
+ * real narrowing — a PDF handed to the CLI has nothing to do with a workspace —
22
+ * but the alternative is a second boundary on a surface whose whole claim is
23
+ * that there is one.
24
+ *
25
+ * **Nothing writes.** `list_pdf_attachments` inventories and there is no path
26
+ * to `--extract` here, the same line that keeps `scripts run` off this surface.
27
+ *
28
+ * **No command action is ever called.** Those terminate the process on a
29
+ * finding, which on a long-lived stdio server would end the session; these call
30
+ * the `src/pdf` modules directly, all of which are pure.
31
+ */
32
+ /**
33
+ * Bounds, as constants rather than flags: `ServeContext` carries none, and a
34
+ * host cannot pass `--max-bytes`. They match the CLI defaults so the same
35
+ * document behaves the same way on both surfaces.
36
+ */
37
+ const LIMITS = { maxBytes: MAX_INPUT_BYTES, timeoutMs: 30_000, maxPages: 5_000 };
38
+ /** A confined, existing, regular file. Mirrors `fileArgument` in `tools.ts`. */
39
+ function pdfArgument(args, context) {
40
+ const value = args.file;
41
+ if (typeof value !== "string" || value === "")
42
+ throw new PathRejected("file is required");
43
+ // `confine` refuses "-" before it can reach `readInput`, whose stdin branch
44
+ // would otherwise read fd 0 — the JSON-RPC channel — and deadlock the server.
45
+ const target = confine(context.root, value, "file");
46
+ if (!fs.existsSync(target) || !fs.statSync(target).isFile())
47
+ throw new PathRejected(`File not found: ${relativeTo(context.root, target)}`);
48
+ return target;
49
+ }
50
+ /**
51
+ * Opens the document, applying the same bounded read the CLI does.
52
+ *
53
+ * Project configuration is deliberately not consulted, even though
54
+ * `context.config` is populated on this surface: a `.cairn.yml` has no say over
55
+ * how a document named in a tool call is parsed, which is the same rule that
56
+ * keeps `pdf` out of `servesWorkspace`.
57
+ */
58
+ async function open(args, context, body) {
59
+ const file = pdfArgument(args, context);
60
+ const read = await readInput(file, LIMITS);
61
+ if (!read.ok)
62
+ throw new Error(read.diagnostic.message);
63
+ return withDocument(read.bytes, LIMITS, (handle) => body(handle, file));
64
+ }
65
+ function pages(args, handle) {
66
+ const spec = args.pages;
67
+ if (typeof spec !== "string" || spec === "")
68
+ return undefined;
69
+ const parsed = parsePageRange(spec, handle.doc.numPages);
70
+ if (!parsed.ok)
71
+ throw new Error(parsed.diagnostic.message);
72
+ return parsed.pages;
73
+ }
74
+ const FILE = {
75
+ type: "string",
76
+ description: "PDF file relative to the served root.",
77
+ };
78
+ const PAGES = {
79
+ type: "string",
80
+ description: "1-based pages to read, e.g. 1,3,5-8. Every page by default.",
81
+ };
82
+ const inspectPdf = {
83
+ name: "inspect_pdf",
84
+ description: "Report a PDF's page count, metadata, tagging, and per-page text-layer classification. Read this before converting: `tagged` says whether structure can be trusted, and a page classified `absent` carries no text to extract. Mirrors `pdf inspect`.",
85
+ inputSchema: { type: "object", properties: { file: FILE }, required: ["file"] },
86
+ handler: (args, context) => open(args, context, async (handle, file) => {
87
+ const inspected = await inspectDocument(handle, { probeStructure: true });
88
+ return {
89
+ file: relativeTo(context.root, file),
90
+ document: inspected.document,
91
+ pages: inspected.pages,
92
+ diagnostics: inspected.diagnostics,
93
+ };
94
+ }),
95
+ };
96
+ const readPdfText = {
97
+ name: "read_pdf_text",
98
+ description: "Extract the text layer a PDF already carries, page by page. Never recognizes text in an image: a scanned page comes back empty with a diagnostic saying so rather than silently blank. Mirrors `pdf text`.",
99
+ inputSchema: { type: "object", properties: { file: FILE, pages: PAGES }, required: ["file"] },
100
+ handler: (args, context) => open(args, context, async (handle, file) => {
101
+ const summary = await documentSummary(handle);
102
+ const selected = pages(args, handle);
103
+ const wanted = selected ?? Array.from({ length: handle.doc.numPages }, (_, i) => i + 1);
104
+ const text = [];
105
+ const diagnostics = [...summary.diagnostics];
106
+ for (const page of wanted) {
107
+ const extracted = await extractPage(handle, page);
108
+ const body = runsToText(extracted.runs);
109
+ text.push({ page, text: body, characters: extracted.characters });
110
+ }
111
+ return {
112
+ file: relativeTo(context.root, file),
113
+ document: summary.document,
114
+ ...(selected ? { selectedPages: selected } : {}),
115
+ text,
116
+ diagnostics,
117
+ };
118
+ }),
119
+ };
120
+ const convertPdfToMarkdown = {
121
+ name: "convert_pdf_to_markdown",
122
+ description: "Convert a PDF's content to Markdown, reporting per construct what was inferred or lost. On an untagged page every block boundary is inferred from geometry, so read `document.tagged` and the diagnostics before trusting the structure. Mirrors `pdf to-markdown`.",
123
+ inputSchema: { type: "object", properties: { file: FILE, pages: PAGES }, required: ["file"] },
124
+ handler: (args, context) => open(args, context, async (handle, file) => {
125
+ const summary = await documentSummary(handle);
126
+ const selected = pages(args, handle);
127
+ const outline = await readOutline(handle);
128
+ const converted = await toMarkdown(handle, {
129
+ ...(selected ? { pages: selected } : {}),
130
+ headingLevels: outline.headingLevels,
131
+ });
132
+ return {
133
+ file: relativeTo(context.root, file),
134
+ document: summary.document,
135
+ ...(selected ? { selectedPages: selected } : {}),
136
+ markdown: converted.markdown,
137
+ diagnostics: [...summary.diagnostics, ...converted.diagnostics],
138
+ };
139
+ }),
140
+ };
141
+ const getPdfOutline = {
142
+ name: "get_pdf_outline",
143
+ description: "Read a PDF's declared outline (bookmarks) as a heading tree with resolved page numbers. A document with no outline returns an empty tree, which is an answer rather than a failure. Mirrors `pdf outline`.",
144
+ inputSchema: { type: "object", properties: { file: FILE }, required: ["file"] },
145
+ handler: (args, context) => open(args, context, async (handle, file) => {
146
+ const summary = await documentSummary(handle);
147
+ const outline = await readOutline(handle);
148
+ return {
149
+ file: relativeTo(context.root, file),
150
+ document: summary.document,
151
+ outline: outline.outline,
152
+ diagnostics: [...summary.diagnostics, ...outline.diagnostics],
153
+ };
154
+ }),
155
+ };
156
+ const listPdfAttachments = {
157
+ name: "list_pdf_attachments",
158
+ description: "List the files embedded inside a PDF, with size and SHA-256. Inventory only: this surface never writes, so there is no way to extract them here. Mirrors `pdf attachments` without --extract.",
159
+ inputSchema: { type: "object", properties: { file: FILE }, required: ["file"] },
160
+ handler: (args, context) => open(args, context, async (handle, file) => {
161
+ const summary = await documentSummary(handle);
162
+ // No `extract` option is passed, and none can be: the schema declares no
163
+ // such property, so a host cannot reach the writing path from here.
164
+ const read = await readAttachments(handle);
165
+ return {
166
+ file: relativeTo(context.root, file),
167
+ document: summary.document,
168
+ attachments: read.attachments,
169
+ diagnostics: [...summary.diagnostics, ...read.diagnostics],
170
+ };
171
+ }),
172
+ };
173
+ const listPdfFormFields = {
174
+ name: "list_pdf_form_fields",
175
+ description: "List a PDF's AcroForm field names, types, and current values. Reads and never writes. An XFA form reports type `xfa` with no fields rather than an empty list, because its values are not readable here. Mirrors `pdf forms`.",
176
+ inputSchema: { type: "object", properties: { file: FILE }, required: ["file"] },
177
+ handler: (args, context) => open(args, context, async (handle, file) => {
178
+ const summary = await documentSummary(handle);
179
+ const read = await readForm(handle);
180
+ return {
181
+ file: relativeTo(context.root, file),
182
+ document: summary.document,
183
+ form: read.form,
184
+ diagnostics: [...summary.diagnostics, ...read.diagnostics],
185
+ };
186
+ }),
187
+ };
188
+ export const PDF_SERVE_TOOLS = [
189
+ inspectPdf,
190
+ readPdfText,
191
+ convertPdfToMarkdown,
192
+ getPdfOutline,
193
+ listPdfAttachments,
194
+ listPdfFormFields,
195
+ ];
196
+ //# sourceMappingURL=pdf-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-tools.js","sourceRoot":"","sources":["../../src/serve/pdf-tools.ts"],"names":[],"mappings":"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"}
@@ -1,19 +1,4 @@
1
- import type { Tool } from "@modelcontextprotocol/sdk/types.js";
2
- import type { ResolvedConfig } from "../config.js";
3
- import type { Workspace } from "../workspace.js";
4
- export interface ServeContext {
5
- workspace: Workspace;
6
- config: ResolvedConfig;
7
- /** Confinement boundary, already resolved through symlinks. */
8
- root: string;
9
- /** Upper bound on parallel lints, so a large audit cannot starve the transport. */
10
- concurrency: number;
11
- }
12
- export interface ServeTool {
13
- name: string;
14
- description: string;
15
- inputSchema: Tool["inputSchema"];
16
- handler: (args: Record<string, unknown>, context: ServeContext) => Promise<unknown> | unknown;
17
- }
1
+ import type { ServeTool } from "./types.js";
2
+ export type { ServeContext, ServeTool } from "./types.js";
18
3
  export declare const SERVE_TOOLS: readonly ServeTool[];
19
4
  export declare const TOOL_BY_NAME: ReadonlyMap<string, ServeTool>;
@@ -12,6 +12,7 @@ import { buildPlan } from "../query/plan.js";
12
12
  import { executePlan } from "../query/execute.js";
13
13
  import { ENTITY_KINDS } from "../query/entities.js";
14
14
  import { confine, PathRejected, relativeTo } from "./paths.js";
15
+ import { PDF_SERVE_TOOLS } from "./pdf-tools.js";
15
16
  // --- argument readers -------------------------------------------------------
16
17
  // Arguments have already been validated against the tool's schema, so these
17
18
  // only narrow types; they never re-report a shape error.
@@ -592,6 +593,9 @@ export const SERVE_TOOLS = [
592
593
  listTasks,
593
594
  listCodeBlocks,
594
595
  findReferences,
596
+ // The PDF tools live in their own module: this file is uniformly the Markdown
597
+ // workspace engine, and registration is still one list.
598
+ ...PDF_SERVE_TOOLS,
595
599
  ];
596
600
  export const TOOL_BY_NAME = new Map(SERVE_TOOLS.map((tool) => [tool.name, tool]));
597
601
  //# sourceMappingURL=tools.js.map