driverge-mcp 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +179 -0
  3. package/dist/codegen/esp32.d.ts +4 -0
  4. package/dist/codegen/esp32.js +78 -0
  5. package/dist/codegen/esp32.js.map +1 -0
  6. package/dist/codegen/ident.d.ts +10 -0
  7. package/dist/codegen/ident.js +31 -0
  8. package/dist/codegen/ident.js.map +1 -0
  9. package/dist/codegen/index.d.ts +15 -0
  10. package/dist/codegen/index.js +30 -0
  11. package/dist/codegen/index.js.map +1 -0
  12. package/dist/codegen/lint.d.ts +4 -0
  13. package/dist/codegen/lint.js +112 -0
  14. package/dist/codegen/lint.js.map +1 -0
  15. package/dist/codegen/portable.d.ts +4 -0
  16. package/dist/codegen/portable.js +258 -0
  17. package/dist/codegen/portable.js.map +1 -0
  18. package/dist/codegen/stm32.d.ts +4 -0
  19. package/dist/codegen/stm32.js +64 -0
  20. package/dist/codegen/stm32.js.map +1 -0
  21. package/dist/codegen/types.d.ts +20 -0
  22. package/dist/codegen/types.js +11 -0
  23. package/dist/codegen/types.js.map +1 -0
  24. package/dist/mcp/cache.d.ts +12 -0
  25. package/dist/mcp/cache.js +24 -0
  26. package/dist/mcp/cache.js.map +1 -0
  27. package/dist/mcp/register.d.ts +6 -0
  28. package/dist/mcp/register.js +195 -0
  29. package/dist/mcp/register.js.map +1 -0
  30. package/dist/mcp/summary.d.ts +2 -0
  31. package/dist/mcp/summary.js +36 -0
  32. package/dist/mcp/summary.js.map +1 -0
  33. package/dist/pdf/analyze.d.ts +4 -0
  34. package/dist/pdf/analyze.js +29 -0
  35. package/dist/pdf/analyze.js.map +1 -0
  36. package/dist/pdf/classify.d.ts +8 -0
  37. package/dist/pdf/classify.js +35 -0
  38. package/dist/pdf/classify.js.map +1 -0
  39. package/dist/pdf/command.d.ts +12 -0
  40. package/dist/pdf/command.js +96 -0
  41. package/dist/pdf/command.js.map +1 -0
  42. package/dist/pdf/extract.d.ts +2 -0
  43. package/dist/pdf/extract.js +47 -0
  44. package/dist/pdf/extract.js.map +1 -0
  45. package/dist/pdf/index.d.ts +11 -0
  46. package/dist/pdf/index.js +13 -0
  47. package/dist/pdf/index.js.map +1 -0
  48. package/dist/pdf/interface-kind.d.ts +2 -0
  49. package/dist/pdf/interface-kind.js +46 -0
  50. package/dist/pdf/interface-kind.js.map +1 -0
  51. package/dist/pdf/manufacturer.d.ts +2 -0
  52. package/dist/pdf/manufacturer.js +86 -0
  53. package/dist/pdf/manufacturer.js.map +1 -0
  54. package/dist/pdf/page-map.d.ts +5 -0
  55. package/dist/pdf/page-map.js +52 -0
  56. package/dist/pdf/page-map.js.map +1 -0
  57. package/dist/pdf/part.d.ts +3 -0
  58. package/dist/pdf/part.js +41 -0
  59. package/dist/pdf/part.js.map +1 -0
  60. package/dist/pdf/register-table.d.ts +5 -0
  61. package/dist/pdf/register-table.js +234 -0
  62. package/dist/pdf/register-table.js.map +1 -0
  63. package/dist/pdf/table.d.ts +18 -0
  64. package/dist/pdf/table.js +33 -0
  65. package/dist/pdf/table.js.map +1 -0
  66. package/dist/pdf/types.d.ts +82 -0
  67. package/dist/pdf/types.js +3 -0
  68. package/dist/pdf/types.js.map +1 -0
  69. package/dist/schema/assemble.d.ts +4 -0
  70. package/dist/schema/assemble.js +48 -0
  71. package/dist/schema/assemble.js.map +1 -0
  72. package/dist/schema/index.d.ts +2 -0
  73. package/dist/schema/index.js +4 -0
  74. package/dist/schema/index.js.map +1 -0
  75. package/dist/schema/types.d.ts +51 -0
  76. package/dist/schema/types.js +7 -0
  77. package/dist/schema/types.js.map +1 -0
  78. package/dist/schema/validate.d.ts +2 -0
  79. package/dist/schema/validate.js +84 -0
  80. package/dist/schema/validate.js.map +1 -0
  81. package/dist/server.d.ts +7 -0
  82. package/dist/server.js +49 -0
  83. package/dist/server.js.map +1 -0
  84. package/package.json +59 -0
  85. package/schemas/.gitkeep +0 -0
  86. package/schemas/datasheet.schema.json +109 -0
@@ -0,0 +1,234 @@
1
+ // L4a (register path) — reconstruct a register map table from positioned text.
2
+ //
3
+ // Two layouts are handled behind one entry point:
4
+ //
5
+ // * BME280 (Bosch) style — a single-line header ("Register Name | Address |
6
+ // bit7..bit0 | Reset state") whose bit fields are written as `name[hi:lo]`
7
+ // spans. Field positions are recovered geometrically: each field's center is
8
+ // matched to the bit-column span (of the width implied by `[hi:lo]`) it best
9
+ // aligns with, which correctly handles reserved-bit gaps.
10
+ //
11
+ // * MCP23017 (Microchip) style — a header stacked across three physical text
12
+ // lines ("Register"/"Name", "Address"/"(hex)", "bit 7".."bit 0",
13
+ // "POR/RST"/"value") that pdfjs emits as separate rows too far apart for the
14
+ // row clusterer to merge, so we merge the header *band* explicitly. Each
15
+ // register names every bit individually (IO7..IO0) rather than using spans,
16
+ // addresses are bare hex ("00") and resets are binary ("1111 1111"); both are
17
+ // normalized to the "0x.." forms the schema/validator expect.
18
+ import { centerX, clusterRows } from "./table.js";
19
+ const HEX = /0x[0-9A-Fa-f]+/;
20
+ // Bare Microchip address cell: 1-2 hex digits, optional trailing "h" ("00", "1A").
21
+ const BARE_HEX = /^([0-9A-Fa-f]{1,2})h?$/;
22
+ // name[hi:lo] | name[n] | name<hi:lo> | name<n>
23
+ const BITFIELD = /^(.+?)[<[](\d+)(?::(\d+))?[>\]]$/;
24
+ // Max vertical span (px) of a header whose column labels stack over a few lines.
25
+ const HEADER_BAND = 14;
26
+ const norm = (s) => s.toLowerCase().replace(/\s+/g, "");
27
+ function isHeader(row) {
28
+ const cells = row.items.map((it) => norm(it.str));
29
+ return (
30
+ // "Register Name" (BME280, one cell) or a split "Register" cell (Microchip).
31
+ (cells.includes("registername") || cells.includes("register")) &&
32
+ cells.includes("address") &&
33
+ cells.includes("bit7") &&
34
+ cells.includes("bit0"));
35
+ }
36
+ /**
37
+ * Find the header, merging a stacked multi-line header into one synthetic row.
38
+ * clusterRows already groups baselines within ~5px; here we additionally fold in
39
+ * up to two more nearby rows when a single cluster doesn't yet carry all the
40
+ * header tokens (the Microchip header lines sit ~5-6px apart, just past the row
41
+ * tolerance). Returns the merged header plus the y of the band's lowest line so
42
+ * the caller can exclude the folded-in sub-rows from the table body.
43
+ */
44
+ function findHeaderBand(rows) {
45
+ for (let i = 0; i < rows.length; i++) {
46
+ let merged = [...rows[i].items];
47
+ let bandBottom = rows[i].y;
48
+ for (let k = 0; k < 3; k++) {
49
+ if (isHeader({ y: rows[i].y, items: merged })) {
50
+ const items = [...merged].sort((a, b) => a.x - b.x);
51
+ return { header: { y: rows[i].y, items }, bandBottom };
52
+ }
53
+ const next = rows[i + k + 1];
54
+ if (!next || rows[i].y - next.y > HEADER_BAND)
55
+ break;
56
+ merged = merged.concat(next.items);
57
+ bandBottom = next.y;
58
+ }
59
+ }
60
+ return undefined;
61
+ }
62
+ function columnsFromHeader(header) {
63
+ const bitCenters = [];
64
+ let nameCenter;
65
+ let addrCenter;
66
+ const resetCenters = [];
67
+ let perBit = false;
68
+ for (const it of header.items) {
69
+ const t = norm(it.str);
70
+ const bit = /^bit([0-7])$/.exec(t);
71
+ if (bit) {
72
+ bitCenters[Number(bit[1])] = centerX(it);
73
+ }
74
+ else if (t === "registername" || t === "register" || t === "name") {
75
+ // BME280 has one "Register Name" cell; Microchip stacks "Register"/"Name".
76
+ nameCenter ??= centerX(it);
77
+ }
78
+ else if (t === "address") {
79
+ addrCenter = centerX(it);
80
+ }
81
+ else if (t === "reset" || t === "state" || t === "resetstate") {
82
+ resetCenters.push(centerX(it));
83
+ }
84
+ else if (t === "por/rst" || t === "value") {
85
+ resetCenters.push(centerX(it));
86
+ perBit = true; // Microchip's reset header — signals the per-bit layout.
87
+ }
88
+ }
89
+ if (nameCenter === undefined ||
90
+ addrCenter === undefined ||
91
+ bitCenters[7] === undefined ||
92
+ bitCenters[0] === undefined) {
93
+ return undefined;
94
+ }
95
+ const resetCenter = resetCenters.length > 0
96
+ ? resetCenters.reduce((a, b) => a + b, 0) / resetCenters.length
97
+ : bitCenters[0] + 40;
98
+ return {
99
+ nameMax: (nameCenter + addrCenter) / 2,
100
+ addrMax: (addrCenter + bitCenters[7]) / 2,
101
+ bitMax: (bitCenters[0] + resetCenter) / 2,
102
+ bitCenters,
103
+ perBit,
104
+ };
105
+ }
106
+ function bitPosition(fieldCenter, width, bitCenters) {
107
+ let best = { msb: width - 1, lsb: 0, err: Infinity };
108
+ for (let msb = width - 1; msb <= 7; msb++) {
109
+ const lsb = msb - width + 1;
110
+ const hiC = bitCenters[msb];
111
+ const loC = bitCenters[lsb];
112
+ if (hiC === undefined || loC === undefined)
113
+ continue;
114
+ const spanCenter = (hiC + loC) / 2;
115
+ const err = Math.abs(fieldCenter - spanCenter);
116
+ if (err < best.err)
117
+ best = { msb, lsb, err };
118
+ }
119
+ return { msb: best.msb, lsb: best.lsb };
120
+ }
121
+ /** BME280 span layout: `name[hi:lo]` cells matched to bit-column spans. */
122
+ function parseBitFields(cells, cols) {
123
+ const fields = [];
124
+ for (const cell of cells) {
125
+ const m = BITFIELD.exec(cell.str.trim());
126
+ if (!m)
127
+ continue; // lone "0"/"1" reserved markers and prose are ignored
128
+ const name = m[1].trim();
129
+ const hi = Number(m[2]);
130
+ const width = m[3] !== undefined ? hi - Number(m[3]) + 1 : 1;
131
+ if (!name || width < 1 || width > 8)
132
+ continue;
133
+ const pos = bitPosition(centerX(cell), width, cols.bitCenters);
134
+ fields.push({ name, msb: pos.msb, lsb: pos.lsb });
135
+ }
136
+ fields.sort((a, b) => b.msb - a.msb);
137
+ return fields;
138
+ }
139
+ /** Microchip per-bit layout: each named cell snaps to its nearest bit column. */
140
+ function parseBitFieldsPerBit(cells, cols) {
141
+ const fields = [];
142
+ for (const cell of cells) {
143
+ const name = cell.str.trim();
144
+ // Skip unimplemented-bit dashes and lone reserved "0"/"1" markers.
145
+ if (!name || name === "—" || name === "-" || /^[01]$/.test(name))
146
+ continue;
147
+ const c = centerX(cell);
148
+ let idx = -1;
149
+ let err = Infinity;
150
+ for (let i = 0; i <= 7; i++) {
151
+ if (cols.bitCenters[i] === undefined)
152
+ continue;
153
+ const e = Math.abs(c - cols.bitCenters[i]);
154
+ if (e < err) {
155
+ err = e;
156
+ idx = i;
157
+ }
158
+ }
159
+ if (idx < 0)
160
+ continue;
161
+ fields.push({ name, msb: idx, lsb: idx });
162
+ }
163
+ fields.sort((a, b) => b.msb - a.msb);
164
+ return fields;
165
+ }
166
+ /** "00"/"1A" -> "0x00"/"0x1A". */
167
+ function normalizeAddress(raw) {
168
+ const m = BARE_HEX.exec(raw);
169
+ return m ? `0x${m[1].toUpperCase().padStart(2, "0")}` : raw;
170
+ }
171
+ /** "1111 1111"/"0000 0000" -> "0xFF"/"0x00". */
172
+ function normalizeReset(raw) {
173
+ const bits = raw.replace(/\s+/g, "");
174
+ if (/^[01]{8}$/.test(bits)) {
175
+ return `0x${Number.parseInt(bits, 2).toString(16).toUpperCase().padStart(2, "0")}`;
176
+ }
177
+ return raw;
178
+ }
179
+ /** Parse a register map table from a single page, if it holds one. */
180
+ export function parseRegisterTable(page) {
181
+ const rows = clusterRows(page.items);
182
+ const band = findHeaderBand(rows);
183
+ if (!band)
184
+ return undefined;
185
+ const cols = columnsFromHeader(band.header);
186
+ if (!cols)
187
+ return undefined;
188
+ const below = rows
189
+ .filter((r) => r.y < band.bandBottom)
190
+ .sort((a, b) => b.y - a.y);
191
+ const registers = [];
192
+ for (const row of below) {
193
+ const name = row.items
194
+ .filter((it) => centerX(it) < cols.nameMax)
195
+ .map((it) => it.str)
196
+ .join(" ")
197
+ .replace(/\s+/g, " ")
198
+ .trim();
199
+ const rawAddress = row.items
200
+ .filter((it) => centerX(it) >= cols.nameMax && centerX(it) < cols.addrMax)
201
+ .map((it) => it.str)
202
+ .join("")
203
+ .trim();
204
+ // Leave the table body once a row has no recognizable address.
205
+ const addressOk = cols.perBit ? BARE_HEX.test(rawAddress) : HEX.test(rawAddress);
206
+ if (!addressOk)
207
+ break;
208
+ const rawReset = row.items
209
+ .filter((it) => centerX(it) >= cols.bitMax)
210
+ .map((it) => it.str)
211
+ .join(" ")
212
+ .trim();
213
+ const bitCells = row.items.filter((it) => centerX(it) >= cols.addrMax && centerX(it) < cols.bitMax);
214
+ registers.push({
215
+ name,
216
+ address: cols.perBit ? normalizeAddress(rawAddress) : rawAddress,
217
+ reset: cols.perBit ? normalizeReset(rawReset) : rawReset,
218
+ bitFields: cols.perBit
219
+ ? parseBitFieldsPerBit(bitCells, cols)
220
+ : parseBitFields(bitCells, cols),
221
+ });
222
+ }
223
+ return { page: page.index, registers };
224
+ }
225
+ /** Scan pages and return the first register map table found. */
226
+ export function findRegisterTable(pages) {
227
+ for (const page of pages) {
228
+ const table = parseRegisterTable(page);
229
+ if (table && table.registers.length > 0)
230
+ return table;
231
+ }
232
+ return undefined;
233
+ }
234
+ //# sourceMappingURL=register-table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-table.js","sourceRoot":"","sources":["../../src/pdf/register-table.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { PositionedText } from "./types.js";
2
+ export interface TableRow {
3
+ /** Representative y (the top-most y of the cluster). */
4
+ y: number;
5
+ /** Row items, left-to-right. */
6
+ items: PositionedText[];
7
+ }
8
+ /**
9
+ * Group positioned text into rows. Items whose y is within `yTolerance` of an
10
+ * open row join it (datasheet rows often split a pixel or two across baselines).
11
+ * Rows come back top-to-bottom; items within a row left-to-right.
12
+ */
13
+ export declare function clusterRows(items: PositionedText[], yTolerance?: number): TableRow[];
14
+ /** Horizontal center of a positioned item. */
15
+ export declare function centerX(it: {
16
+ x: number;
17
+ width: number;
18
+ }): number;
@@ -0,0 +1,33 @@
1
+ // Generic table reconstruction over positioned text: cluster items into rows by
2
+ // vertical proximity. Column assignment is caller-specific (see register-table).
3
+ /**
4
+ * Group positioned text into rows. Items whose y is within `yTolerance` of an
5
+ * open row join it (datasheet rows often split a pixel or two across baselines).
6
+ * Rows come back top-to-bottom; items within a row left-to-right.
7
+ */
8
+ export function clusterRows(items, yTolerance = 5) {
9
+ const sorted = items
10
+ .filter((it) => it.str.trim() !== "")
11
+ .sort((a, b) => b.y - a.y);
12
+ // Group by the gap to the *previous* item (not a fixed anchor), so a header
13
+ // like "Reset" / "state" split a few px apart still lands in one row.
14
+ const rows = [];
15
+ let prevY;
16
+ for (const it of sorted) {
17
+ if (rows.length > 0 && prevY !== undefined && prevY - it.y <= yTolerance) {
18
+ rows[rows.length - 1].items.push(it);
19
+ }
20
+ else {
21
+ rows.push({ y: it.y, items: [it] });
22
+ }
23
+ prevY = it.y;
24
+ }
25
+ for (const row of rows)
26
+ row.items.sort((a, b) => a.x - b.x);
27
+ return rows;
28
+ }
29
+ /** Horizontal center of a positioned item. */
30
+ export function centerX(it) {
31
+ return it.x + it.width / 2;
32
+ }
33
+ //# sourceMappingURL=table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.js","sourceRoot":"","sources":["../../src/pdf/table.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,iFAAiF;AAWjF;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,KAAuB,EACvB,UAAU,GAAG,CAAC;IAEd,MAAM,MAAM,GAAG,KAAK;SACjB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;SACpC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAE7B,4EAA4E;IAC5E,sEAAsE;IACtE,MAAM,IAAI,GAAe,EAAE,CAAC;IAC5B,IAAI,KAAyB,CAAC;IAC9B,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;QACxB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC,IAAI,UAAU,EAAE,CAAC;YACzE,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACtC,CAAC;QACD,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC;IACf,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,IAAI;QAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,OAAO,CAAC,EAAgC;IACtD,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,82 @@
1
+ /** Overall classification of a datasheet PDF. */
2
+ export type PdfType = "text_based" | "scanned" | "mixed";
3
+ /** Per-page classification. */
4
+ export type PageKind = "text" | "scanned" | "blank";
5
+ /** A single positioned text run extracted from a page. */
6
+ export interface PositionedText {
7
+ str: string;
8
+ /** X of the text origin, in PDF user-space units (origin bottom-left). */
9
+ x: number;
10
+ /** Y of the text origin, in PDF user-space units. */
11
+ y: number;
12
+ width: number;
13
+ height: number;
14
+ }
15
+ /** Everything extracted from one page. */
16
+ export interface PageContent {
17
+ /** 1-based page number. */
18
+ index: number;
19
+ /** Whitespace-normalized concatenation of the page's text runs. */
20
+ text: string;
21
+ items: PositionedText[];
22
+ /** Whether the page paints a raster image (a "scanned" signal). */
23
+ hasImage: boolean;
24
+ }
25
+ /** Minimal per-page signal the classifier needs — kept PDF-free so it is pure. */
26
+ export interface PageSignal {
27
+ textLength: number;
28
+ hasImage: boolean;
29
+ }
30
+ export interface PdfClassification {
31
+ type: PdfType;
32
+ pageKinds: PageKind[];
33
+ }
34
+ /** Label -> 1-based page numbers where that section was detected. */
35
+ export type PageMap = Record<string, number[]>;
36
+ /** One bit field within a register, positioned by absolute bit index. */
37
+ export interface BitField {
38
+ name: string;
39
+ /** Most-significant bit index within the register (0-7). */
40
+ msb: number;
41
+ /** Least-significant bit index within the register (0-7). */
42
+ lsb: number;
43
+ }
44
+ /** A single register row reconstructed from a memory-map table. */
45
+ export interface Register {
46
+ name: string;
47
+ /** Verbatim address cell, e.g. "0xF4" or a range like "0x88…0xA1". */
48
+ address: string;
49
+ /** Verbatim reset/default cell, e.g. "0x00", "0x60", "individual". */
50
+ reset: string;
51
+ bitFields: BitField[];
52
+ }
53
+ /** The register map extracted from one table. */
54
+ export interface RegisterTable {
55
+ /** 1-based page the table was found on. */
56
+ page: number;
57
+ registers: Register[];
58
+ }
59
+ /** L3a — deterministic manufacturer detection result. */
60
+ export interface ManufacturerDetection {
61
+ /** Vendor name, or "generic" when no confident match. */
62
+ manufacturer: string;
63
+ /** 0-1; 0 means generic fallback. */
64
+ confidence: number;
65
+ /** Which signals fired, e.g. ["copyright", "doc-number", "url"]. */
66
+ signals: string[];
67
+ }
68
+ /** L3b — control-interface classification. */
69
+ export type InterfaceKind = "register_map" | "command_set" | "unknown";
70
+ export interface InterfaceKindDetection {
71
+ kind: InterfaceKind;
72
+ confidence: number;
73
+ signals: string[];
74
+ }
75
+ /** Result of L1 + L2 over a whole PDF. */
76
+ export interface PdfAnalysis {
77
+ type: PdfType;
78
+ pageCount: number;
79
+ pageMap: PageMap;
80
+ pages: PageContent[];
81
+ warnings: string[];
82
+ }
@@ -0,0 +1,3 @@
1
+ // Shared types for the PDF parsing pipeline (L1 format detection + L2 page map).
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/pdf/types.ts"],"names":[],"mappings":"AAAA,iFAAiF"}
@@ -0,0 +1,4 @@
1
+ import type { PdfAnalysis } from "../pdf/types.js";
2
+ import type { DatasheetJson } from "./types.js";
3
+ /** Assemble + validate the datasheet contract from a completed PDF analysis. */
4
+ export declare function assembleDatasheet(analysis: PdfAnalysis): DatasheetJson;
@@ -0,0 +1,48 @@
1
+ // Glue: run the L3/L4 extractors over an analyzed PDF and assemble the frozen
2
+ // DatasheetJson contract, then validate it (L5). This is the single place the
3
+ // scattered pipeline stages (manufacturer, interface-kind, register table,
4
+ // command/protocol/CRC extraction) come together into the object handed to the
5
+ // host AI by ref. See wiki: json-schema-as-contract, mcp-tool-usage-flow.
6
+ import { extractCommands, extractProtocol } from "../pdf/command.js";
7
+ import { detectInterfaceKind } from "../pdf/interface-kind.js";
8
+ import { detectManufacturer } from "../pdf/manufacturer.js";
9
+ import { detectPart } from "../pdf/part.js";
10
+ import { findRegisterTable } from "../pdf/register-table.js";
11
+ import { validateDatasheet } from "./validate.js";
12
+ function buildInterface(pages, kind) {
13
+ if (kind === "command_set") {
14
+ return { kind: "command_set", commands: extractCommands(pages) };
15
+ }
16
+ // register_map (and "unknown", which we treat as register-first): fall back to
17
+ // a command set only if no register table is found but commands are present.
18
+ const table = findRegisterTable(pages);
19
+ if (!table && kind === "unknown") {
20
+ const commands = extractCommands(pages);
21
+ if (commands.length > 0)
22
+ return { kind: "command_set", commands };
23
+ }
24
+ return { kind: "register_map", registers: table?.registers ?? [] };
25
+ }
26
+ /** Assemble + validate the datasheet contract from a completed PDF analysis. */
27
+ export function assembleDatasheet(analysis) {
28
+ const { pages } = analysis;
29
+ const manufacturer = detectManufacturer(pages);
30
+ const kind = detectInterfaceKind(pages).kind;
31
+ const partial = {
32
+ metadata: {
33
+ part: detectPart(pages),
34
+ manufacturer: manufacturer.manufacturer,
35
+ manufacturerConfidence: manufacturer.confidence,
36
+ pdfType: analysis.type,
37
+ pageCount: analysis.pageCount,
38
+ },
39
+ protocol: extractProtocol(pages),
40
+ interface: buildInterface(pages, kind),
41
+ };
42
+ const validation = validateDatasheet({
43
+ ...partial,
44
+ validation: { valid: true, errors: [], warnings: [] },
45
+ });
46
+ return { ...partial, validation };
47
+ }
48
+ //# sourceMappingURL=assemble.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../src/schema/assemble.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,8EAA8E;AAC9E,2EAA2E;AAC3E,+EAA+E;AAC/E,0EAA0E;AAE1E,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,SAAS,cAAc,CAAC,KAAoB,EAAE,IAAY;IACxD,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;QAC3B,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;IACnE,CAAC;IACD,+EAA+E;IAC/E,6EAA6E;IAC7E,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,CAAC,KAAK,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;IACpE,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,CAAC;AACrE,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,iBAAiB,CAAC,QAAqB;IACrD,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC;IAC3B,MAAM,YAAY,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC;IAE7C,MAAM,OAAO,GAAG;QACd,QAAQ,EAAE;YACR,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC;YACvB,YAAY,EAAE,YAAY,CAAC,YAAY;YACvC,sBAAsB,EAAE,YAAY,CAAC,UAAU;YAC/C,OAAO,EAAE,QAAQ,CAAC,IAAI;YACtB,SAAS,EAAE,QAAQ,CAAC,SAAS;SAC9B;QACD,QAAQ,EAAE,eAAe,CAAC,KAAK,CAAC;QAChC,SAAS,EAAE,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC;KACvC,CAAC;IAEF,MAAM,UAAU,GAAG,iBAAiB,CAAC;QACnC,GAAG,OAAO;QACV,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;KACtD,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,CAAC;AACpC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from "./types.js";
2
+ export { validateDatasheet } from "./validate.js";
@@ -0,0 +1,4 @@
1
+ // Public surface of the datasheet JSON contract + validator.
2
+ export * from "./types.js";
3
+ export { validateDatasheet } from "./validate.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAE7D,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,51 @@
1
+ import type { PdfType, Register } from "../pdf/types.js";
2
+ export type Bus = "I2C" | "SPI" | "UART" | "unknown";
3
+ export interface DatasheetMetadata {
4
+ part: string;
5
+ manufacturer: string;
6
+ /** 0-1 confidence from L3a manufacturer detection. */
7
+ manufacturerConfidence: number;
8
+ pdfType: PdfType;
9
+ pageCount: number;
10
+ }
11
+ export interface Protocol {
12
+ bus: Bus;
13
+ /** Bus addresses, e.g. ["0x76", "0x77"]. */
14
+ addresses?: string[];
15
+ maxClockHz?: number;
16
+ }
17
+ /** A command for a command-set device (see command-set-interface). */
18
+ export interface Command {
19
+ name: string;
20
+ /** Hex command word, e.g. "0x2C06". */
21
+ code: string;
22
+ params?: string[];
23
+ /** Number of response words (each usually a data word + CRC). */
24
+ responseWords?: number;
25
+ crc?: {
26
+ poly: string;
27
+ init: string;
28
+ width: number;
29
+ };
30
+ }
31
+ export interface RegisterInterface {
32
+ kind: "register_map";
33
+ registers: Register[];
34
+ }
35
+ export interface CommandInterface {
36
+ kind: "command_set";
37
+ commands: Command[];
38
+ }
39
+ export type DeviceInterface = RegisterInterface | CommandInterface;
40
+ export interface ValidationResult {
41
+ valid: boolean;
42
+ errors: string[];
43
+ warnings: string[];
44
+ }
45
+ /** The full parsed datasheet handed (by ref) to the host AI. */
46
+ export interface DatasheetJson {
47
+ metadata: DatasheetMetadata;
48
+ protocol: Protocol;
49
+ interface: DeviceInterface;
50
+ validation: ValidationResult;
51
+ }
@@ -0,0 +1,7 @@
1
+ // The datasheet JSON contract — the frozen interface between the deterministic
2
+ // parser and the host AI (see wiki: json-schema-as-contract). The `interface`
3
+ // field is a discriminated union on `kind`, which is the primary strategy axis
4
+ // (device-class-before-manufacturer). The JSON-Schema mirror lives at
5
+ // schemas/datasheet.schema.json.
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/schema/types.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,+EAA+E;AAC/E,sEAAsE;AACtE,iCAAiC"}
@@ -0,0 +1,2 @@
1
+ import type { DatasheetJson, ValidationResult } from "./types.js";
2
+ export declare function validateDatasheet(d: DatasheetJson): ValidationResult;
@@ -0,0 +1,84 @@
1
+ // L5 — datasheet validator. Deterministic logic checks, per interface kind, that
2
+ // gate malformed/incomplete JSON before it reaches the host AI (see wiki:
3
+ // validate-before-sending-to-claude). Errors fail validation; warnings do not.
4
+ const HEX_ONLY = /^0x[0-9a-f]+$/i;
5
+ export function validateDatasheet(d) {
6
+ const errors = [];
7
+ const warnings = [];
8
+ if (!d.metadata.part)
9
+ warnings.push("metadata.part is empty");
10
+ if (d.protocol.bus === "unknown")
11
+ warnings.push("protocol.bus is unknown");
12
+ if (d.interface.kind === "register_map") {
13
+ validateRegisters(d.interface.registers, errors);
14
+ }
15
+ else {
16
+ validateCommands(d.interface.commands, errors, warnings);
17
+ }
18
+ return { valid: errors.length === 0, errors, warnings };
19
+ }
20
+ function validateRegisters(registers, errors) {
21
+ if (registers.length === 0) {
22
+ errors.push("register_map has no registers");
23
+ return;
24
+ }
25
+ const addressCount = new Map();
26
+ for (const r of registers) {
27
+ // Duplicate single addresses (ranges like "0x88…0xA1" are skipped here).
28
+ if (HEX_ONLY.test(r.address)) {
29
+ addressCount.set(r.address, (addressCount.get(r.address) ?? 0) + 1);
30
+ }
31
+ const occupied = new Array(8).fill(false);
32
+ for (const field of r.bitFields) {
33
+ if (field.msb < 0 || field.msb > 7 || field.lsb < 0 || field.lsb > 7) {
34
+ errors.push(`${r.name}.${field.name}: bit index out of range (${field.msb}:${field.lsb})`);
35
+ continue;
36
+ }
37
+ if (field.msb < field.lsb) {
38
+ errors.push(`${r.name}.${field.name}: msb < lsb (${field.msb}:${field.lsb})`);
39
+ continue;
40
+ }
41
+ for (let b = field.lsb; b <= field.msb; b++) {
42
+ if (occupied[b]) {
43
+ errors.push(`${r.name}: bit ${b} claimed by more than one field`);
44
+ }
45
+ occupied[b] = true;
46
+ }
47
+ }
48
+ if (HEX_ONLY.test(r.reset) && Number.parseInt(r.reset, 16) > 0xff) {
49
+ errors.push(`${r.name}: reset ${r.reset} exceeds the 8-bit register width`);
50
+ }
51
+ }
52
+ for (const [address, count] of addressCount) {
53
+ if (count > 1) {
54
+ errors.push(`duplicate register address ${address} (${count}x)`);
55
+ }
56
+ }
57
+ }
58
+ function validateCommands(commands, errors, warnings) {
59
+ if (commands.length === 0) {
60
+ errors.push("command_set has no commands");
61
+ return;
62
+ }
63
+ const codeCount = new Map();
64
+ for (const c of commands) {
65
+ if (!HEX_ONLY.test(c.code)) {
66
+ errors.push(`${c.name}: invalid command code "${c.code}"`);
67
+ }
68
+ else {
69
+ codeCount.set(c.code, (codeCount.get(c.code) ?? 0) + 1);
70
+ }
71
+ if (c.responseWords === undefined) {
72
+ warnings.push(`${c.name}: response length not declared`);
73
+ }
74
+ if (!c.crc) {
75
+ warnings.push(`${c.name}: CRC parameters missing`);
76
+ }
77
+ }
78
+ for (const [code, count] of codeCount) {
79
+ if (count > 1) {
80
+ errors.push(`duplicate command code ${code} (${count}x)`);
81
+ }
82
+ }
83
+ }
84
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/schema/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ /**
4
+ * Build a Driverge MCP server with its tools registered. Kept separate from the
5
+ * transport wiring so tests can drive it over an in-memory transport.
6
+ */
7
+ export declare function createServer(): McpServer;