@tiwater/office-mcp 0.20.3 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_shared/large-json-result.mjs +62 -0
- package/office/contracts/docx_compare.schema.json +12 -1
- package/office/contracts/docx_export_json.schema.json +5 -1
- package/office/contracts/docx_inspect.schema.json +6 -2
- package/office/contracts/docx_list_objects.schema.json +6 -1
- package/office/contracts/docx_read_object.schema.json +6 -1
- package/office/contracts/docx_read_table.schema.json +7 -2
- package/office/contracts/docx_table_index.schema.json +7 -3
- package/office/contracts/docx_validate.schema.json +12 -1
- package/office/contracts/docx_validate_font_policy.schema.json +12 -1
- package/office/contracts/docx_validate_toc_style_policy.schema.json +12 -1
- package/office/contracts/pptx_export_json.schema.json +5 -1
- package/office/contracts/pptx_inspect.schema.json +5 -1
- package/office/contracts/pptx_validate.schema.json +12 -1
- package/office/contracts/tiwater-office-provider-contract-manifest-v1.json +33 -33
- package/office/contracts/xlsx_export_json.schema.json +5 -1
- package/office/contracts/xlsx_inspect.schema.json +5 -1
- package/office/contracts/xlsx_validate.schema.json +12 -1
- package/office/index.mjs +215 -140
- package/package.json +2 -1
package/office/index.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { mkdir, readFile, rename, rm, stat } from 'node:fs/promises';
|
|
5
4
|
import path from 'node:path';
|
|
6
5
|
import { isDeepStrictEqual } from 'node:util';
|
|
7
6
|
import { McpServer } from '@modelcontextprotocol/server';
|
|
@@ -15,6 +14,13 @@ import {
|
|
|
15
14
|
runJsonCandidateChain,
|
|
16
15
|
withTempJsonFile,
|
|
17
16
|
} from '../_shared/tool-runtime.mjs';
|
|
17
|
+
import {
|
|
18
|
+
deliverLargeJsonResult,
|
|
19
|
+
fileArtifact,
|
|
20
|
+
resultChannels,
|
|
21
|
+
returnedContentBudgetBytes,
|
|
22
|
+
writeJsonArtifact,
|
|
23
|
+
} from '../_shared/large-json-result.mjs';
|
|
18
24
|
|
|
19
25
|
const packageMetadata = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
|
|
20
26
|
const inputContractManifest = JSON.parse(await readFile(
|
|
@@ -157,8 +163,30 @@ function fixedEditOutput(tool) {
|
|
|
157
163
|
}).strict();
|
|
158
164
|
}
|
|
159
165
|
|
|
160
|
-
function
|
|
161
|
-
return z.object({
|
|
166
|
+
function largeResultOutput(tool) {
|
|
167
|
+
return z.object({
|
|
168
|
+
tool: z.literal(tool),
|
|
169
|
+
runtime: runtimeIdentity,
|
|
170
|
+
sources: z.array(artifact).min(1).max(2),
|
|
171
|
+
returnContent: z.boolean(),
|
|
172
|
+
artifact: artifact.nullable(),
|
|
173
|
+
receipt: z.object({
|
|
174
|
+
contentBytes: z.number().int().nonnegative(),
|
|
175
|
+
contentReturned: z.boolean(),
|
|
176
|
+
contentWritten: z.boolean(),
|
|
177
|
+
}).strict(),
|
|
178
|
+
content: z.unknown().optional(),
|
|
179
|
+
}).strict();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function docxObservationOutput(tool) {
|
|
183
|
+
return z.object({
|
|
184
|
+
tool: z.literal(tool),
|
|
185
|
+
runtime: runtimeIdentity,
|
|
186
|
+
source: artifact,
|
|
187
|
+
returnContent: z.boolean(),
|
|
188
|
+
artifact: artifact.nullable(),
|
|
189
|
+
}).strict();
|
|
162
190
|
}
|
|
163
191
|
|
|
164
192
|
const docxAddress = z.object({
|
|
@@ -167,14 +195,14 @@ const docxAddress = z.object({
|
|
|
167
195
|
}).strict();
|
|
168
196
|
|
|
169
197
|
function docxInspectionOutput(tool) {
|
|
170
|
-
return
|
|
198
|
+
return docxObservationOutput(tool).extend({
|
|
171
199
|
summary: z.object({
|
|
172
200
|
tableCount: z.number().int().nonnegative(),
|
|
173
201
|
openingParagraphs: z.array(z.object({
|
|
174
202
|
address: docxAddress,
|
|
175
203
|
textPreview: z.string().min(1).max(240),
|
|
176
204
|
}).strict()).max(6),
|
|
177
|
-
}).strict(),
|
|
205
|
+
}).strict().optional(),
|
|
178
206
|
}).strict();
|
|
179
207
|
}
|
|
180
208
|
|
|
@@ -211,14 +239,14 @@ const docxObservationReceipt = z.object({
|
|
|
211
239
|
nextOffset: z.number().int().nonnegative().nullable(),
|
|
212
240
|
}).strict();
|
|
213
241
|
|
|
214
|
-
const docxListObjectsOutput =
|
|
242
|
+
const docxListObjectsOutput = docxObservationOutput('docx_list_objects').extend({
|
|
215
243
|
schema: z.literal('tiwater.docx-observation-list/v1'),
|
|
216
244
|
receipt: docxObservationReceipt,
|
|
217
|
-
objects: z.array(docxObjectIdentity),
|
|
245
|
+
objects: z.array(docxObjectIdentity).optional(),
|
|
218
246
|
runtime: runtimeIdentity,
|
|
219
247
|
}).strict();
|
|
220
248
|
|
|
221
|
-
const docxTableIndexOutput =
|
|
249
|
+
const docxTableIndexOutput = docxObservationOutput('docx_table_index').extend({
|
|
222
250
|
schema: z.literal('tiwater.docx-table-index/v1'),
|
|
223
251
|
receipt: z.object({
|
|
224
252
|
schema: z.literal('tiwater.docx-table-index-receipt/v1'),
|
|
@@ -244,10 +272,10 @@ const docxTableIndexOutput = artifactOutput('docx_table_index').extend({
|
|
|
244
272
|
textPreview: z.string(),
|
|
245
273
|
textLength: z.number().int().nonnegative(),
|
|
246
274
|
}).strict().nullable(),
|
|
247
|
-
}).strict()),
|
|
275
|
+
}).strict()).optional(),
|
|
248
276
|
}).strict();
|
|
249
277
|
|
|
250
|
-
const docxTableReadOutput =
|
|
278
|
+
const docxTableReadOutput = docxObservationOutput('docx_read_table').extend({
|
|
251
279
|
schema: z.literal('tiwater.docx-table-page/v1'),
|
|
252
280
|
receipt: z.object({
|
|
253
281
|
schema: z.literal('tiwater.docx-table-page-receipt/v1'),
|
|
@@ -255,7 +283,7 @@ const docxTableReadOutput = artifactOutput('docx_read_table').extend({
|
|
|
255
283
|
returnedRowCount: z.number().int().nonnegative(),
|
|
256
284
|
remaining: z.number().int().nonnegative(),
|
|
257
285
|
nextOffset: z.number().int().nonnegative().nullable(),
|
|
258
|
-
detailPageRetained: z.
|
|
286
|
+
detailPageRetained: z.boolean(),
|
|
259
287
|
narrowingRequired: z.boolean(),
|
|
260
288
|
}).strict(),
|
|
261
289
|
address: docxAddress,
|
|
@@ -290,10 +318,10 @@ const docxTableReadOutput = artifactOutput('docx_read_table').extend({
|
|
|
290
318
|
text: z.string(),
|
|
291
319
|
logicalText: z.string(),
|
|
292
320
|
}).strict()),
|
|
293
|
-
}).strict()),
|
|
321
|
+
}).strict()).optional(),
|
|
294
322
|
}).strict();
|
|
295
323
|
|
|
296
|
-
const docxReadObjectOutput =
|
|
324
|
+
const docxReadObjectOutput = docxObservationOutput('docx_read_object').extend({
|
|
297
325
|
receipt: z.object({
|
|
298
326
|
schema: z.literal('tiwater.docx-read-object-receipt/v1'),
|
|
299
327
|
observationCount: z.number().int().positive(),
|
|
@@ -301,13 +329,13 @@ const docxReadObjectOutput = artifactOutput('docx_read_object').extend({
|
|
|
301
329
|
responseComplete: z.boolean(),
|
|
302
330
|
narrowingRequired: z.boolean(),
|
|
303
331
|
}).strict(),
|
|
304
|
-
observations: z.array(docxObservationNode),
|
|
332
|
+
observations: z.array(docxObservationNode).optional(),
|
|
305
333
|
}).strict();
|
|
306
334
|
|
|
307
335
|
const tools = [
|
|
308
336
|
{
|
|
309
337
|
name: 'docx_inspect',
|
|
310
|
-
description: 'Inspect one current DOCX
|
|
338
|
+
description: 'Inspect one current DOCX. Set returnContent true to return the compact identity summary. Provide output to retain the complete machine observation and return its artifact receipt. These channels are independent and may be used together. At least one channel is required. Use list and read operations to traverse selected document objects in native structure order.',
|
|
311
339
|
inputSchema: inputContract('docx_inspect'),
|
|
312
340
|
outputSchema: docxInspectionOutput('docx_inspect'),
|
|
313
341
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
@@ -315,7 +343,7 @@ const tools = [
|
|
|
315
343
|
},
|
|
316
344
|
{
|
|
317
345
|
name: 'docx_list_objects',
|
|
318
|
-
description: 'Page through mixed nearest-child OpenXML objects when document order or paragraph relationships are required.
|
|
346
|
+
description: 'Page through mixed nearest-child OpenXML objects only when document order or paragraph relationships are required. Set returnContent true to return a byte-bounded object page. Provide output to store the complete requested page and return its artifact receipt. These channels are independent and may be used together; at least one is required. Continue from receipt.nextOffset only when an unreturned object is needed for the current decision. Do not use this tool to locate tables or read a whole document: use docx_table_index to locate tables, then a narrow table or object read.',
|
|
319
347
|
inputSchema: inputContract('docx_list_objects'),
|
|
320
348
|
outputSchema: docxListObjectsOutput,
|
|
321
349
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
@@ -323,7 +351,7 @@ const tools = [
|
|
|
323
351
|
},
|
|
324
352
|
{
|
|
325
353
|
name: 'docx_table_index',
|
|
326
|
-
description: 'Locate tables in one current DOCX
|
|
354
|
+
description: 'Locate tables in one current DOCX without returning full cell content or deciding table semantics. Set returnContent true to return a bounded page of addresses, shapes, short previews, and nearest non-empty paragraphs. Provide output to store the complete index and return its artifact receipt. These channels are independent and may be used together; at least one is required. Continue from receipt.nextOffset only when an unreturned table is needed for the current decision, then read one selected native address.',
|
|
327
355
|
inputSchema: inputContract('docx_table_index'),
|
|
328
356
|
outputSchema: docxTableIndexOutput,
|
|
329
357
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
@@ -331,7 +359,7 @@ const tools = [
|
|
|
331
359
|
},
|
|
332
360
|
{
|
|
333
361
|
name: 'docx_read_object',
|
|
334
|
-
description: 'Read selected rows, cells, or paragraphs from one native DOCX
|
|
362
|
+
description: 'Read explicitly selected rows, cells, or paragraphs from one native DOCX. Set returnContent true to return compact requested descendants; if receipt.narrowingRequired is true, request fewer addresses or descendant kinds. Provide output to store the complete selected observation and return its artifact receipt. These channels are independent and may be used together; at least one is required. A selected cell exposes its vertical-merge owner and logical text, so a continue cell keeps its physical identity while resolving the restart cell value. Use docx_read_table for a table range.',
|
|
335
363
|
inputSchema: inputContract('docx_read_object'),
|
|
336
364
|
outputSchema: docxReadObjectOutput,
|
|
337
365
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
@@ -339,7 +367,7 @@ const tools = [
|
|
|
339
367
|
},
|
|
340
368
|
{
|
|
341
369
|
name: 'docx_read_table',
|
|
342
|
-
description: 'Read exactly one table selected
|
|
370
|
+
description: 'Read an explicit row range from exactly one table selected by native OpenXML address; it never builds another whole-table data object. Set returnContent true to return a byte-bounded compact row page. Provide output to store full paragraph and text-node detail for the selected row page and return its artifact receipt. These channels are independent and may be used together; at least one is required. Request only rows needed for the current decision; receipt.remaining is navigation information, not an obligation to read unused rows, and blank template rows need not be paged through. Each returned row and cell keeps its reusable native address, zero-based logical gridColumnStart, gridSpan, vertical-merge owner, physical text, and logical text. Match columns across rows by gridColumnStart; a tc[n] path or array position is only that row\'s physical cell ordinal and is not a column identity when gridSpan or gridBefore is present. In a vertical merge, restart begins one logical cell and a continue cell is not an independent row value: logicalText resolves the restart cell value while text remains the physical cell value. Use docx_read_object when one exact object needs a narrower descendant view. The provider reports physical structure only; the Agent decides template and business meaning.',
|
|
343
371
|
inputSchema: inputContract('docx_read_table'),
|
|
344
372
|
outputSchema: docxTableReadOutput,
|
|
345
373
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
@@ -375,7 +403,7 @@ const tools = [
|
|
|
375
403
|
},
|
|
376
404
|
{
|
|
377
405
|
name: 'docx_merge_cells',
|
|
378
|
-
description: 'Merge selected current DOCX cells when they form one closed rectangle.',
|
|
406
|
+
description: 'Merge selected current DOCX cells when they form one closed rectangle. A one-column, multi-row rectangle creates a vertical merge whose first cell is the restart owner and whose later cells are continuations. All selected cell content moves into the top-left owner, so the selected content must already be correct for that one logical cell.',
|
|
379
407
|
inputSchema: inputContract('docx_merge_cells'),
|
|
380
408
|
outputSchema: fixedEditOutput('docx_merge_cells'),
|
|
381
409
|
handler: args => fixedEdit('docx_merge_cells', args),
|
|
@@ -403,30 +431,33 @@ const tools = [
|
|
|
403
431
|
},
|
|
404
432
|
{
|
|
405
433
|
name: 'docx_compare',
|
|
406
|
-
description: 'Compare two DOCX files and report package, metric, and style differences.',
|
|
434
|
+
description: 'Compare two DOCX files and report package, metric, and style differences. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
407
435
|
inputSchema: inputContract('docx_compare'),
|
|
436
|
+
outputSchema: largeResultOutput('docx_compare'),
|
|
408
437
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
409
438
|
handler: docxCompare,
|
|
410
439
|
},
|
|
411
440
|
{
|
|
412
441
|
name: 'docx_export_json',
|
|
413
|
-
description: '
|
|
442
|
+
description: 'Produce a body-only DOCX JSON projection only when a downstream consumer explicitly requires that format. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required. This does not replace bounded list and read operations.',
|
|
414
443
|
inputSchema: inputContract('docx_export_json'),
|
|
415
|
-
outputSchema:
|
|
444
|
+
outputSchema: largeResultOutput('docx_export_json'),
|
|
416
445
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
417
446
|
handler: docxExportJson,
|
|
418
447
|
},
|
|
419
448
|
{
|
|
420
449
|
name: 'docx_validate',
|
|
421
|
-
description: 'Validate a current DOCX package against the published OpenXML contract.',
|
|
450
|
+
description: 'Validate a current DOCX package against the published OpenXML contract. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
422
451
|
inputSchema: inputContract('docx_validate'),
|
|
452
|
+
outputSchema: largeResultOutput('docx_validate'),
|
|
423
453
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
424
454
|
handler: docxValidate,
|
|
425
455
|
},
|
|
426
456
|
{
|
|
427
457
|
name: 'docx_validate_font_policy',
|
|
428
|
-
description: 'Validate current DOCX text against an explicit font policy.',
|
|
458
|
+
description: 'Validate current DOCX text against an explicit font policy. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
429
459
|
inputSchema: inputContract('docx_validate_font_policy'),
|
|
460
|
+
outputSchema: largeResultOutput('docx_validate_font_policy'),
|
|
430
461
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
431
462
|
handler: docxValidateFontPolicy,
|
|
432
463
|
},
|
|
@@ -439,8 +470,9 @@ const tools = [
|
|
|
439
470
|
},
|
|
440
471
|
{
|
|
441
472
|
name: 'docx_validate_toc_style_policy',
|
|
442
|
-
description: 'Validate current DOCX table-of-contents paragraph styles against an explicit policy.',
|
|
473
|
+
description: 'Validate current DOCX table-of-contents paragraph styles against an explicit policy. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
443
474
|
inputSchema: inputContract('docx_validate_toc_style_policy'),
|
|
475
|
+
outputSchema: largeResultOutput('docx_validate_toc_style_policy'),
|
|
444
476
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
445
477
|
handler: docxValidateTocStylePolicy,
|
|
446
478
|
},
|
|
@@ -485,41 +517,42 @@ const tools = [
|
|
|
485
517
|
},
|
|
486
518
|
{
|
|
487
519
|
name: 'xlsx_inspect',
|
|
488
|
-
description: 'Inspect a current XLSX workbook or legacy XLS workbook
|
|
520
|
+
description: 'Inspect a current XLSX workbook or legacy XLS workbook, including workbook structure, exported values, formulas, styles, merged ranges, and published legacy-format conversion evidence. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
489
521
|
inputSchema: inputContract('xlsx_inspect'),
|
|
490
|
-
outputSchema:
|
|
522
|
+
outputSchema: largeResultOutput('xlsx_inspect'),
|
|
491
523
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
492
524
|
handler: xlsxInspect,
|
|
493
525
|
},
|
|
494
526
|
{
|
|
495
527
|
name: 'xlsx_export_json',
|
|
496
|
-
description: 'Export workbook sheet data from XLSX as structured JSON.',
|
|
528
|
+
description: 'Export workbook sheet data from XLSX as structured JSON. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
497
529
|
inputSchema: inputContract('xlsx_export_json'),
|
|
498
|
-
outputSchema:
|
|
530
|
+
outputSchema: largeResultOutput('xlsx_export_json'),
|
|
499
531
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
500
532
|
handler: xlsxExportJson,
|
|
501
533
|
},
|
|
502
534
|
...fixedToolDefinitions(xlsxFixedTools),
|
|
503
535
|
{
|
|
504
536
|
name: 'xlsx_validate',
|
|
505
|
-
description: 'Validate an XLSX workbook package and
|
|
537
|
+
description: 'Validate an XLSX workbook package and produce OpenXML validation evidence. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
506
538
|
inputSchema: inputContract('xlsx_validate'),
|
|
539
|
+
outputSchema: largeResultOutput('xlsx_validate'),
|
|
507
540
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
508
541
|
handler: xlsxValidate,
|
|
509
542
|
},
|
|
510
543
|
{
|
|
511
544
|
name: 'pptx_inspect',
|
|
512
|
-
description: 'Inspect a PPTX file
|
|
545
|
+
description: 'Inspect a PPTX file, including slides, masters, layouts, shapes, transforms, paragraphs, runs, and placeholders. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
513
546
|
inputSchema: inputContract('pptx_inspect'),
|
|
514
|
-
outputSchema:
|
|
547
|
+
outputSchema: largeResultOutput('pptx_inspect'),
|
|
515
548
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
516
549
|
handler: pptxInspect,
|
|
517
550
|
},
|
|
518
551
|
{
|
|
519
552
|
name: 'pptx_export_json',
|
|
520
|
-
description: 'Export PPTX slide text, notes, and placeholder hints to a new JSON
|
|
553
|
+
description: 'Export PPTX slide text, notes, and placeholder hints as structured JSON. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
521
554
|
inputSchema: inputContract('pptx_export_json'),
|
|
522
|
-
outputSchema:
|
|
555
|
+
outputSchema: largeResultOutput('pptx_export_json'),
|
|
523
556
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
524
557
|
handler: pptxExportJson,
|
|
525
558
|
},
|
|
@@ -553,8 +586,9 @@ const tools = [
|
|
|
553
586
|
},
|
|
554
587
|
{
|
|
555
588
|
name: 'pptx_validate',
|
|
556
|
-
description: 'Validate a current PPTX package against the published OpenXML contract.',
|
|
589
|
+
description: 'Validate a current PPTX package against the published OpenXML contract. Set returnContent true to return the complete result when it fits the response limit. Provide output to write the complete result to a new JSON file. The two choices are independent and may be used together; at least one is required.',
|
|
557
590
|
inputSchema: inputContract('pptx_validate'),
|
|
591
|
+
outputSchema: largeResultOutput('pptx_validate'),
|
|
558
592
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
559
593
|
handler: pptxValidate,
|
|
560
594
|
},
|
|
@@ -618,6 +652,24 @@ function compactDocxObjectIdentity(object) {
|
|
|
618
652
|
};
|
|
619
653
|
}
|
|
620
654
|
|
|
655
|
+
function compactTableIndexEntry(table) {
|
|
656
|
+
const paragraph = value => value === null ? null : {
|
|
657
|
+
address: value.address,
|
|
658
|
+
textPreview: value.textPreview.trim().replace(/\s+/gu, ' ').slice(0, 160),
|
|
659
|
+
textLength: value.textLength,
|
|
660
|
+
};
|
|
661
|
+
return {
|
|
662
|
+
address: table.address,
|
|
663
|
+
parentAddress: table.parentAddress,
|
|
664
|
+
rowCount: table.rowCount,
|
|
665
|
+
columnCount: table.columnCount,
|
|
666
|
+
textPreview: table.textPreview.trim().replace(/\s+/gu, ' ').slice(0, 240),
|
|
667
|
+
textLength: table.textLength,
|
|
668
|
+
precedingParagraph: paragraph(table.precedingParagraph),
|
|
669
|
+
followingParagraph: paragraph(table.followingParagraph),
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
|
|
621
673
|
function compactDocxObservation(observation) {
|
|
622
674
|
if (!observation?.object) throw new Error('docx-read-object-missing-observation');
|
|
623
675
|
function compactNode(node) {
|
|
@@ -642,20 +694,24 @@ function compactDocxObservation(observation) {
|
|
|
642
694
|
|
|
643
695
|
async function docxInspect(args) {
|
|
644
696
|
const input = path.resolve(requireString(args.input, 'input'));
|
|
697
|
+
const delivery = resultChannels(args);
|
|
645
698
|
const result = await runJsonCandidateChain(docxCandidates, ['inspect', input, '--json']);
|
|
646
699
|
return {
|
|
647
700
|
tool: 'docx_inspect',
|
|
648
701
|
runtime: commandRuntime(result),
|
|
649
702
|
source: await fileArtifact(input),
|
|
650
|
-
|
|
651
|
-
|
|
703
|
+
returnContent: delivery.returnContent,
|
|
704
|
+
artifact: delivery.output === null
|
|
705
|
+
? null
|
|
706
|
+
: await writeJsonArtifact(delivery.output, result.json),
|
|
707
|
+
...(delivery.returnContent ? compactDocxInspection(result.json) : {}),
|
|
652
708
|
};
|
|
653
709
|
}
|
|
654
710
|
|
|
655
711
|
async function docxObservation(tool, args) {
|
|
656
712
|
const input = path.resolve(requireString(args.input, 'input'));
|
|
657
|
-
const
|
|
658
|
-
const { output: _output, ...request } = args;
|
|
713
|
+
const delivery = resultChannels(args);
|
|
714
|
+
const { output: _output, returnContent: _returnContent, ...request } = args;
|
|
659
715
|
return withTempJsonFile(request, async requestPath => {
|
|
660
716
|
const result = await runJsonCandidateChain(docxCandidates, [tool, requestPath]);
|
|
661
717
|
const payload = { ...result.json, runtime: commandRuntime(result) };
|
|
@@ -663,22 +719,33 @@ async function docxObservation(tool, args) {
|
|
|
663
719
|
tool,
|
|
664
720
|
runtime: payload.runtime,
|
|
665
721
|
source: await fileArtifact(input),
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
}),
|
|
722
|
+
returnContent: delivery.returnContent,
|
|
723
|
+
artifact: null,
|
|
669
724
|
};
|
|
670
725
|
if (tool === 'docx_list_objects') {
|
|
671
726
|
const totalCount = payload.receipt.totalCount;
|
|
672
727
|
const offset = Math.min(args.offset ?? 0, totalCount);
|
|
728
|
+
const withArtifact = delivery.output === null ? retained : {
|
|
729
|
+
...retained,
|
|
730
|
+
artifact: await writeJsonArtifact(delivery.output, result.json),
|
|
731
|
+
};
|
|
732
|
+
if (!delivery.returnContent) {
|
|
733
|
+
return {
|
|
734
|
+
...withArtifact,
|
|
735
|
+
schema: payload.schema,
|
|
736
|
+
receipt: payload.receipt,
|
|
737
|
+
};
|
|
738
|
+
}
|
|
673
739
|
const objects = [];
|
|
674
740
|
for (const sourceObject of payload.objects.map(compactDocxObjectIdentity)) {
|
|
675
741
|
const candidate = [...objects, sourceObject];
|
|
676
|
-
if (objects.length > 0
|
|
742
|
+
if (objects.length > 0
|
|
743
|
+
&& Buffer.byteLength(JSON.stringify(candidate)) > returnedContentBudgetBytes) break;
|
|
677
744
|
objects.push(sourceObject);
|
|
678
745
|
}
|
|
679
746
|
const nextOffset = offset + objects.length < totalCount ? offset + objects.length : null;
|
|
680
747
|
return {
|
|
681
|
-
...
|
|
748
|
+
...withArtifact,
|
|
682
749
|
schema: payload.schema,
|
|
683
750
|
receipt: {
|
|
684
751
|
schema: 'tiwater.docx-observation-receipt/v1',
|
|
@@ -693,17 +760,36 @@ async function docxObservation(tool, args) {
|
|
|
693
760
|
}
|
|
694
761
|
if (tool === 'docx_table_index') {
|
|
695
762
|
const totalCount = payload.tables.length;
|
|
763
|
+
const withArtifact = delivery.output === null ? retained : {
|
|
764
|
+
...retained,
|
|
765
|
+
artifact: await writeJsonArtifact(delivery.output, result.json),
|
|
766
|
+
};
|
|
767
|
+
if (!delivery.returnContent) {
|
|
768
|
+
return {
|
|
769
|
+
...withArtifact,
|
|
770
|
+
schema: payload.schema,
|
|
771
|
+
receipt: {
|
|
772
|
+
schema: 'tiwater.docx-table-index-receipt/v1',
|
|
773
|
+
totalCount,
|
|
774
|
+
returnedCount: totalCount,
|
|
775
|
+
remaining: 0,
|
|
776
|
+
nextOffset: null,
|
|
777
|
+
},
|
|
778
|
+
};
|
|
779
|
+
}
|
|
696
780
|
const offset = Math.min(args.offset ?? 0, totalCount);
|
|
697
781
|
const requestedLimit = args.limit ?? totalCount;
|
|
698
782
|
const tables = [];
|
|
699
|
-
for (const
|
|
783
|
+
for (const sourceTable of payload.tables.slice(offset, offset + requestedLimit)) {
|
|
784
|
+
const table = compactTableIndexEntry(sourceTable);
|
|
700
785
|
const candidate = [...tables, table];
|
|
701
|
-
if (tables.length > 0
|
|
786
|
+
if (tables.length > 0
|
|
787
|
+
&& Buffer.byteLength(JSON.stringify(candidate)) > returnedContentBudgetBytes) break;
|
|
702
788
|
tables.push(table);
|
|
703
789
|
}
|
|
704
790
|
const nextOffset = offset + tables.length < totalCount ? offset + tables.length : null;
|
|
705
791
|
return {
|
|
706
|
-
...
|
|
792
|
+
...withArtifact,
|
|
707
793
|
schema: payload.schema,
|
|
708
794
|
receipt: {
|
|
709
795
|
schema: 'tiwater.docx-table-index-receipt/v1',
|
|
@@ -719,9 +805,52 @@ async function docxObservation(tool, args) {
|
|
|
719
805
|
const totalRowCount = payload.rows.length;
|
|
720
806
|
const offset = Math.min(args.offset ?? 0, totalRowCount);
|
|
721
807
|
const requestedLimit = args.limit ?? totalRowCount;
|
|
808
|
+
const selectedRows = payload.rows.slice(offset, offset + requestedLimit);
|
|
809
|
+
const selectedNextOffset = offset + selectedRows.length < totalRowCount
|
|
810
|
+
? offset + selectedRows.length
|
|
811
|
+
: null;
|
|
812
|
+
const selectedPageReceipt = {
|
|
813
|
+
schema: 'tiwater.docx-table-page-receipt/v1',
|
|
814
|
+
totalRowCount,
|
|
815
|
+
returnedRowCount: selectedRows.length,
|
|
816
|
+
remaining: totalRowCount - offset - selectedRows.length,
|
|
817
|
+
nextOffset: selectedNextOffset,
|
|
818
|
+
};
|
|
819
|
+
const detailPage = {
|
|
820
|
+
schema: 'tiwater.docx-table-detail-page/v1',
|
|
821
|
+
address: payload.address,
|
|
822
|
+
rowCount: payload.rowCount,
|
|
823
|
+
columnCount: payload.columnCount,
|
|
824
|
+
gridColumns: payload.gridColumns,
|
|
825
|
+
precedingParagraph: payload.precedingParagraph,
|
|
826
|
+
followingParagraph: payload.followingParagraph,
|
|
827
|
+
receipt: selectedPageReceipt,
|
|
828
|
+
rows: selectedRows,
|
|
829
|
+
};
|
|
830
|
+
const withArtifact = delivery.output === null ? retained : {
|
|
831
|
+
...retained,
|
|
832
|
+
artifact: await writeJsonArtifact(delivery.output, detailPage),
|
|
833
|
+
};
|
|
834
|
+
if (!delivery.returnContent) {
|
|
835
|
+
return {
|
|
836
|
+
...withArtifact,
|
|
837
|
+
schema: 'tiwater.docx-table-page/v1',
|
|
838
|
+
address: payload.address,
|
|
839
|
+
rowCount: payload.rowCount,
|
|
840
|
+
columnCount: payload.columnCount,
|
|
841
|
+
gridColumns: payload.gridColumns,
|
|
842
|
+
precedingParagraph: payload.precedingParagraph,
|
|
843
|
+
followingParagraph: payload.followingParagraph,
|
|
844
|
+
receipt: {
|
|
845
|
+
...selectedPageReceipt,
|
|
846
|
+
detailPageRetained: true,
|
|
847
|
+
narrowingRequired: false,
|
|
848
|
+
},
|
|
849
|
+
};
|
|
850
|
+
}
|
|
722
851
|
const rows = [];
|
|
723
852
|
let narrowingRequired = false;
|
|
724
|
-
for (const sourceRow of
|
|
853
|
+
for (const sourceRow of selectedRows) {
|
|
725
854
|
const row = {
|
|
726
855
|
address: sourceRow.address,
|
|
727
856
|
repeatHeader: sourceRow.repeatHeader,
|
|
@@ -739,7 +868,7 @@ async function docxObservation(tool, args) {
|
|
|
739
868
|
})),
|
|
740
869
|
};
|
|
741
870
|
const candidate = [...rows, row];
|
|
742
|
-
if (Buffer.byteLength(JSON.stringify(candidate)) >
|
|
871
|
+
if (Buffer.byteLength(JSON.stringify(candidate)) > returnedContentBudgetBytes) {
|
|
743
872
|
narrowingRequired = rows.length === 0;
|
|
744
873
|
break;
|
|
745
874
|
}
|
|
@@ -755,20 +884,8 @@ async function docxObservation(tool, args) {
|
|
|
755
884
|
remaining: totalRowCount - offset - rows.length,
|
|
756
885
|
nextOffset,
|
|
757
886
|
};
|
|
758
|
-
const detailPage = {
|
|
759
|
-
schema: 'tiwater.docx-table-detail-page/v1',
|
|
760
|
-
address: payload.address,
|
|
761
|
-
rowCount: payload.rowCount,
|
|
762
|
-
columnCount: payload.columnCount,
|
|
763
|
-
gridColumns: payload.gridColumns,
|
|
764
|
-
precedingParagraph: payload.precedingParagraph,
|
|
765
|
-
followingParagraph: payload.followingParagraph,
|
|
766
|
-
receipt: pageReceipt,
|
|
767
|
-
rows: payload.rows.slice(offset, offset + rows.length),
|
|
768
|
-
};
|
|
769
887
|
return {
|
|
770
|
-
...
|
|
771
|
-
artifact: await writeJsonArtifact(output, detailPage),
|
|
888
|
+
...withArtifact,
|
|
772
889
|
schema: 'tiwater.docx-table-page/v1',
|
|
773
890
|
address: payload.address,
|
|
774
891
|
rowCount: payload.rowCount,
|
|
@@ -778,7 +895,7 @@ async function docxObservation(tool, args) {
|
|
|
778
895
|
followingParagraph: payload.followingParagraph,
|
|
779
896
|
receipt: {
|
|
780
897
|
...pageReceipt,
|
|
781
|
-
detailPageRetained:
|
|
898
|
+
detailPageRetained: delivery.output !== null,
|
|
782
899
|
narrowingRequired,
|
|
783
900
|
},
|
|
784
901
|
rows,
|
|
@@ -790,46 +907,53 @@ async function docxObservation(tool, args) {
|
|
|
790
907
|
|
|
791
908
|
async function docxReadObject(args) {
|
|
792
909
|
const input = path.resolve(requireString(args.input, 'input'));
|
|
793
|
-
const
|
|
794
|
-
const { output: _output, ...request } = args;
|
|
910
|
+
const delivery = resultChannels(args);
|
|
911
|
+
const { output: _output, returnContent: _returnContent, ...request } = args;
|
|
795
912
|
return withTempJsonFile(request, async requestPath => {
|
|
796
913
|
const result = await runJsonCandidateChain(docxCandidates, ['docx_read_object', requestPath]);
|
|
797
914
|
const observations = result.json.observations.map(compactDocxObservation);
|
|
798
|
-
const responseComplete =
|
|
915
|
+
const responseComplete = delivery.returnContent
|
|
916
|
+
&& Buffer.byteLength(JSON.stringify(observations)) <= returnedContentBudgetBytes;
|
|
799
917
|
return {
|
|
800
918
|
tool: 'docx_read_object',
|
|
801
919
|
runtime: commandRuntime(result),
|
|
802
920
|
source: await fileArtifact(input),
|
|
803
|
-
|
|
921
|
+
returnContent: delivery.returnContent,
|
|
922
|
+
artifact: delivery.output === null
|
|
923
|
+
? null
|
|
924
|
+
: await writeJsonArtifact(delivery.output, result.json),
|
|
804
925
|
receipt: {
|
|
805
926
|
schema: 'tiwater.docx-read-object-receipt/v1',
|
|
806
927
|
observationCount: observations.length,
|
|
807
928
|
returnedCount: responseComplete ? observations.length : 0,
|
|
808
929
|
responseComplete,
|
|
809
|
-
narrowingRequired: !responseComplete,
|
|
930
|
+
narrowingRequired: delivery.returnContent && !responseComplete,
|
|
810
931
|
},
|
|
811
|
-
|
|
932
|
+
...(responseComplete ? { observations } : {}),
|
|
812
933
|
};
|
|
813
934
|
});
|
|
814
935
|
}
|
|
815
936
|
|
|
816
937
|
async function docxValidate(args) {
|
|
817
|
-
const
|
|
818
|
-
|
|
938
|
+
const input = path.resolve(requireString(args.input, 'input'));
|
|
939
|
+
const result = await runJsonCandidateChain(docxCandidates, ['validate-openxml', input], { allowedExitCodes: [0, 1] });
|
|
940
|
+
return deliverLargeJsonResult({ tool: 'docx_validate', args, runtime: commandRuntime(result), payload: result.json, sourcePaths: [input] });
|
|
819
941
|
}
|
|
820
942
|
|
|
821
943
|
async function docxValidateFontPolicy(args) {
|
|
822
944
|
return withTempJsonFile(args.policy, async policyPath => {
|
|
823
|
-
const
|
|
824
|
-
|
|
945
|
+
const input = path.resolve(requireString(args.input, 'input'));
|
|
946
|
+
const result = await runJsonCandidateChain(docxCandidates, ['validate-font-policy', input, policyPath], { allowedExitCodes: [0, 1] });
|
|
947
|
+
return deliverLargeJsonResult({ tool: 'docx_validate_font_policy', args, runtime: commandRuntime(result), payload: result.json, sourcePaths: [input] });
|
|
825
948
|
});
|
|
826
949
|
}
|
|
827
950
|
|
|
828
951
|
async function docxValidateTocStylePolicy(args) {
|
|
952
|
+
const input = path.resolve(requireString(args.input, 'input'));
|
|
829
953
|
const result = await runJsonCandidateChain(docxCandidates, [
|
|
830
|
-
'validate-toc-style-policy',
|
|
954
|
+
'validate-toc-style-policy', input, String(args.italic), String(args.indentCharactersPerLevel),
|
|
831
955
|
], { allowedExitCodes: [0, 1] });
|
|
832
|
-
return { tool: 'docx_validate_toc_style_policy', runtime: commandRuntime(result),
|
|
956
|
+
return deliverLargeJsonResult({ tool: 'docx_validate_toc_style_policy', args, runtime: commandRuntime(result), payload: result.json, sourcePaths: [input] });
|
|
833
957
|
}
|
|
834
958
|
|
|
835
959
|
async function docxStripDirectFormatting(args) {
|
|
@@ -925,21 +1049,16 @@ async function fixedEdit(tool, args) {
|
|
|
925
1049
|
}
|
|
926
1050
|
|
|
927
1051
|
async function docxCompare(args) {
|
|
928
|
-
const baseline = requireString(args.baseline, 'baseline');
|
|
929
|
-
const updated = requireString(args.updated, 'updated');
|
|
1052
|
+
const baseline = path.resolve(requireString(args.baseline, 'baseline'));
|
|
1053
|
+
const updated = path.resolve(requireString(args.updated, 'updated'));
|
|
930
1054
|
const result = await runJsonCandidateChain(docxCandidates, ['compare', baseline, updated, '--json']);
|
|
931
|
-
return { tool: 'docx_compare', runtime: commandRuntime(result),
|
|
1055
|
+
return deliverLargeJsonResult({ tool: 'docx_compare', args, runtime: commandRuntime(result), payload: result.json, sourcePaths: [baseline, updated] });
|
|
932
1056
|
}
|
|
933
1057
|
|
|
934
1058
|
async function docxExportJson(args) {
|
|
935
1059
|
const input = path.resolve(requireString(args.input, 'input'));
|
|
936
1060
|
const result = await runJsonCandidateChain(docxCandidates, ['export-json', input]);
|
|
937
|
-
return {
|
|
938
|
-
tool: 'docx_export_json',
|
|
939
|
-
runtime: commandRuntime(result),
|
|
940
|
-
source: await fileArtifact(input),
|
|
941
|
-
artifact: await writeJsonArtifact(requireString(args.output, 'output'), result.json),
|
|
942
|
-
};
|
|
1061
|
+
return deliverLargeJsonResult({ tool: 'docx_export_json', args, runtime: commandRuntime(result), payload: result.json, sourcePaths: [input] });
|
|
943
1062
|
}
|
|
944
1063
|
|
|
945
1064
|
async function officeRenderPdf(args) {
|
|
@@ -1027,12 +1146,7 @@ function nativeRenderBackend(sourceFormat) {
|
|
|
1027
1146
|
async function xlsxInspect(args) {
|
|
1028
1147
|
const input = path.resolve(requireString(args.input, 'input'));
|
|
1029
1148
|
const result = await runJsonCandidateChain(xlsxCandidates, ['inspect', input, '--json']);
|
|
1030
|
-
return {
|
|
1031
|
-
tool: 'xlsx_inspect',
|
|
1032
|
-
runtime: commandRuntime(result),
|
|
1033
|
-
source: await fileArtifact(input),
|
|
1034
|
-
artifact: await writeJsonArtifact(requireString(args.output, 'output'), result.json),
|
|
1035
|
-
};
|
|
1149
|
+
return deliverLargeJsonResult({ tool: 'xlsx_inspect', args, runtime: commandRuntime(result), payload: result.json, sourcePaths: [input] });
|
|
1036
1150
|
}
|
|
1037
1151
|
|
|
1038
1152
|
async function xlsxExportJson(args) {
|
|
@@ -1042,45 +1156,31 @@ async function xlsxExportJson(args) {
|
|
|
1042
1156
|
cmdArgs.push('--resolve-merged-cells');
|
|
1043
1157
|
}
|
|
1044
1158
|
const result = await runJsonCandidateChain(xlsxCandidates, cmdArgs);
|
|
1045
|
-
return {
|
|
1046
|
-
tool: 'xlsx_export_json',
|
|
1047
|
-
runtime: commandRuntime(result),
|
|
1048
|
-
source: await fileArtifact(input),
|
|
1049
|
-
artifact: await writeJsonArtifact(requireString(args.output, 'output'), result.json),
|
|
1050
|
-
};
|
|
1159
|
+
return deliverLargeJsonResult({ tool: 'xlsx_export_json', args, runtime: commandRuntime(result), payload: result.json, sourcePaths: [input] });
|
|
1051
1160
|
}
|
|
1052
1161
|
|
|
1053
1162
|
async function xlsxValidate(args) {
|
|
1054
|
-
const input = requireString(args.input, 'input');
|
|
1163
|
+
const input = path.resolve(requireString(args.input, 'input'));
|
|
1055
1164
|
const result = await runJsonCandidateChain(xlsxCandidates, ['validate', input], { allowedExitCodes: [0, 1] });
|
|
1056
|
-
return { tool: 'xlsx_validate', runtime: commandRuntime(result),
|
|
1165
|
+
return deliverLargeJsonResult({ tool: 'xlsx_validate', args, runtime: commandRuntime(result), payload: result.json, sourcePaths: [input] });
|
|
1057
1166
|
}
|
|
1058
1167
|
|
|
1059
1168
|
async function pptxInspect(args) {
|
|
1060
1169
|
const input = path.resolve(requireString(args.input, 'input'));
|
|
1061
1170
|
const result = await runJsonCandidateChain(pptxCandidates, ['inspect', input, '--json']);
|
|
1062
|
-
return {
|
|
1063
|
-
tool: 'pptx_inspect',
|
|
1064
|
-
runtime: commandRuntime(result),
|
|
1065
|
-
source: await fileArtifact(input),
|
|
1066
|
-
artifact: await writeJsonArtifact(requireString(args.output, 'output'), result.json),
|
|
1067
|
-
};
|
|
1171
|
+
return deliverLargeJsonResult({ tool: 'pptx_inspect', args, runtime: commandRuntime(result), payload: result.json, sourcePaths: [input] });
|
|
1068
1172
|
}
|
|
1069
1173
|
|
|
1070
1174
|
async function pptxExportJson(args) {
|
|
1071
1175
|
const input = path.resolve(requireString(args.input, 'input'));
|
|
1072
1176
|
const result = await runJsonCandidateChain(pptxCandidates, ['export-json', input]);
|
|
1073
|
-
return {
|
|
1074
|
-
tool: 'pptx_export_json',
|
|
1075
|
-
runtime: commandRuntime(result),
|
|
1076
|
-
source: await fileArtifact(input),
|
|
1077
|
-
artifact: await writeJsonArtifact(requireString(args.output, 'output'), result.json),
|
|
1078
|
-
};
|
|
1177
|
+
return deliverLargeJsonResult({ tool: 'pptx_export_json', args, runtime: commandRuntime(result), payload: result.json, sourcePaths: [input] });
|
|
1079
1178
|
}
|
|
1080
1179
|
|
|
1081
1180
|
async function pptxValidate(args) {
|
|
1082
|
-
const
|
|
1083
|
-
|
|
1181
|
+
const input = path.resolve(requireString(args.input, 'input'));
|
|
1182
|
+
const result = await runJsonCandidateChain(pptxCandidates, ['validate', input], { allowedExitCodes: [0, 1] });
|
|
1183
|
+
return deliverLargeJsonResult({ tool: 'pptx_validate', args, runtime: commandRuntime(result), payload: result.json, sourcePaths: [input] });
|
|
1084
1184
|
}
|
|
1085
1185
|
|
|
1086
1186
|
async function requireReturnedArtifact(returned, expectedPath, label) {
|
|
@@ -1093,31 +1193,6 @@ async function requireReturnedArtifact(returned, expectedPath, label) {
|
|
|
1093
1193
|
}
|
|
1094
1194
|
}
|
|
1095
1195
|
|
|
1096
|
-
async function writeJsonArtifact(output, payload) {
|
|
1097
|
-
const fullPath = path.resolve(output);
|
|
1098
|
-
await mkdir(path.dirname(fullPath), { recursive: true });
|
|
1099
|
-
const bytes = Buffer.from(`${JSON.stringify(payload, null, 2)}\n`, 'utf8');
|
|
1100
|
-
await writeFile(fullPath, bytes, { flag: 'wx' });
|
|
1101
|
-
return {
|
|
1102
|
-
path: fullPath,
|
|
1103
|
-
sha256: createHash('sha256').update(bytes).digest('hex'),
|
|
1104
|
-
bytes: bytes.length,
|
|
1105
|
-
};
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
async function fileArtifact(filePath) {
|
|
1109
|
-
const hash = createHash('sha256');
|
|
1110
|
-
for await (const chunk of createReadStream(filePath)) {
|
|
1111
|
-
hash.update(chunk);
|
|
1112
|
-
}
|
|
1113
|
-
const file = await stat(filePath);
|
|
1114
|
-
return {
|
|
1115
|
-
path: path.resolve(filePath),
|
|
1116
|
-
sha256: hash.digest('hex'),
|
|
1117
|
-
bytes: file.size,
|
|
1118
|
-
};
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
1196
|
async function requireNewFile(filePath, label) {
|
|
1122
1197
|
try {
|
|
1123
1198
|
await stat(filePath);
|