@tiwater/office-mcp 0.21.4 → 0.21.6
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { createReadStream } from 'node:fs';
|
|
3
|
-
import { mkdir, stat, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
|
|
6
6
|
import { requireString } from './tool-runtime.mjs';
|
|
@@ -31,7 +31,7 @@ export async function deliverLargeJsonResult({ tool, args, runtime, payload, sou
|
|
|
31
31
|
runtime,
|
|
32
32
|
sources: await Promise.all(sourcePaths.map(fileArtifact)),
|
|
33
33
|
returnContent: channels.returnContent,
|
|
34
|
-
artifact: channels.output === null ? null : await
|
|
34
|
+
artifact: channels.output === null ? null : await writeIdempotentJsonArtifact(channels.output, payload),
|
|
35
35
|
receipt: {
|
|
36
36
|
contentBytes,
|
|
37
37
|
contentReturned,
|
|
@@ -47,6 +47,28 @@ export async function writeJsonArtifact(output, payload) {
|
|
|
47
47
|
const bytes = Buffer.from(`${JSON.stringify(payload, null, 2)}\n`, 'utf8');
|
|
48
48
|
await mkdir(path.dirname(fullPath), { recursive: true });
|
|
49
49
|
await writeFile(fullPath, bytes, { flag: 'wx' });
|
|
50
|
+
return describeJsonArtifact(fullPath, bytes);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function writeIdempotentJsonArtifact(output, payload) {
|
|
54
|
+
const fullPath = path.resolve(output);
|
|
55
|
+
const bytes = Buffer.from(`${JSON.stringify(payload, null, 2)}\n`, 'utf8');
|
|
56
|
+
await mkdir(path.dirname(fullPath), { recursive: true });
|
|
57
|
+
try {
|
|
58
|
+
await writeFile(fullPath, bytes, { flag: 'wx' });
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (error?.code !== 'EEXIST') throw error;
|
|
61
|
+
const existing = await readFile(fullPath);
|
|
62
|
+
if (!existing.equals(bytes)) {
|
|
63
|
+
throw Object.assign(new Error(`artifact-path-already-exists-with-different-content:${fullPath}`), {
|
|
64
|
+
code: 'EEXIST',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return describeJsonArtifact(fullPath, bytes);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function describeJsonArtifact(fullPath, bytes) {
|
|
50
72
|
return {
|
|
51
73
|
path: fullPath,
|
|
52
74
|
sha256: createHash('sha256').update(bytes).digest('hex'),
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"rows": {
|
|
70
70
|
"type": "array",
|
|
71
71
|
"minItems": 0,
|
|
72
|
-
"description": "Exact final rows; an empty array removes the declared range when at least one table row remains. Each final row names one current row in existingRows as its style prototype
|
|
72
|
+
"description": "Exact final physical rows; an empty array removes the declared range when at least one table row remains. Each final row names one current row in existingRows as its style prototype, and its cells cover the complete target grid exactly once.",
|
|
73
73
|
"items": {
|
|
74
74
|
"type": "object",
|
|
75
75
|
"properties": {
|
|
@@ -95,11 +95,10 @@
|
|
|
95
95
|
"items": { "type": "string", "minLength": 1, "maxLength": 80 }
|
|
96
96
|
},
|
|
97
97
|
"text": { "type": "string" },
|
|
98
|
-
"
|
|
99
|
-
"type": "
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"description": "Number of final rows occupied by this cell; omitted means one row."
|
|
98
|
+
"verticalMerge": {
|
|
99
|
+
"type": "string",
|
|
100
|
+
"enum": ["restart", "continue"],
|
|
101
|
+
"description": "Native OpenXML vertical merge state. A restart cell owns the text; the same columns in the next physical row use continue with empty text. Omit for an unmerged cell."
|
|
103
102
|
}
|
|
104
103
|
},
|
|
105
104
|
"required": ["columns", "text"],
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema": "tiwater.office-provider-contract-manifest/v1",
|
|
3
3
|
"provider": {
|
|
4
4
|
"id": "@tiwater/office-mcp",
|
|
5
|
-
"version": "0.21.
|
|
5
|
+
"version": "0.21.6"
|
|
6
6
|
},
|
|
7
7
|
"tools": [
|
|
8
8
|
{
|
|
@@ -185,11 +185,11 @@
|
|
|
185
185
|
"name": "docx_set_table_body",
|
|
186
186
|
"providerContract": {
|
|
187
187
|
"source": "packages/docx-cli/contracts/mcp-input/docx_set_table_body.schema.json",
|
|
188
|
-
"sha256": "
|
|
188
|
+
"sha256": "e1f6c96d71dc42904fb1d0c8b049c1398e1155d76db481b58064cede9e30fad6"
|
|
189
189
|
},
|
|
190
190
|
"inputContract": {
|
|
191
191
|
"path": "office/contracts/docx_set_table_body.schema.json",
|
|
192
|
-
"sha256": "
|
|
192
|
+
"sha256": "e1f6c96d71dc42904fb1d0c8b049c1398e1155d76db481b58064cede9e30fad6"
|
|
193
193
|
}
|
|
194
194
|
},
|
|
195
195
|
{
|
package/office/index.mjs
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
fileArtifact,
|
|
20
20
|
resultChannels,
|
|
21
21
|
returnedContentBudgetBytes,
|
|
22
|
+
writeIdempotentJsonArtifact,
|
|
22
23
|
writeJsonArtifact,
|
|
23
24
|
} from '../_shared/large-json-result.mjs';
|
|
24
25
|
import { withOutputWriteLock } from '../_shared/output-write-lock.mjs';
|
|
@@ -423,7 +424,7 @@ const tools = [
|
|
|
423
424
|
},
|
|
424
425
|
{
|
|
425
426
|
name: 'docx_set_table_body',
|
|
426
|
-
description: 'Atomically replace one exact current target-table row range with final text rows while retaining the table, target styles, grid widths, and all rows and surrounding content outside the range. Name every target grid column in native order, choose one current row as the style prototype for each final row, and
|
|
427
|
+
description: 'Atomically replace one exact current target-table row range with final physical text rows while retaining the table, target styles, grid widths, and all rows and surrounding content outside the range. Name every target grid column in native order, choose one current row as the style prototype for each final row, and cover every row completely with explicit cells. Horizontal spans use contiguous column IDs. Native vertical merges use a restart cell followed by continue cells on the same columns; continue cells have empty physical text. An empty final row array removes the range when another table row remains. The provider commits once and returns structural readback. It does not read a source table, map source to target, derive text, choose business columns, identify headers, or accept non-text cell content.',
|
|
427
428
|
inputSchema: inputContract('docx_set_table_body'),
|
|
428
429
|
outputSchema: fixedEditOutput('docx_set_table_body'),
|
|
429
430
|
handler: args => fixedEdit('docx_set_table_body', args),
|
|
@@ -746,7 +747,7 @@ async function docxInspect(args) {
|
|
|
746
747
|
returnContent: delivery.returnContent,
|
|
747
748
|
artifact: delivery.output === null
|
|
748
749
|
? null
|
|
749
|
-
: await
|
|
750
|
+
: await writeIdempotentJsonArtifact(delivery.output, result.json),
|
|
750
751
|
...(delivery.returnContent ? compactDocxInspection(result.json) : {}),
|
|
751
752
|
};
|
|
752
753
|
}
|
|
@@ -770,7 +771,7 @@ async function docxObservation(tool, args) {
|
|
|
770
771
|
const offset = Math.min(args.offset ?? 0, totalCount);
|
|
771
772
|
const withArtifact = delivery.output === null ? retained : {
|
|
772
773
|
...retained,
|
|
773
|
-
artifact: await
|
|
774
|
+
artifact: await writeIdempotentJsonArtifact(delivery.output, result.json),
|
|
774
775
|
};
|
|
775
776
|
if (!delivery.returnContent) {
|
|
776
777
|
return {
|
|
@@ -805,7 +806,7 @@ async function docxObservation(tool, args) {
|
|
|
805
806
|
const totalCount = payload.tables.length;
|
|
806
807
|
const withArtifact = delivery.output === null ? retained : {
|
|
807
808
|
...retained,
|
|
808
|
-
artifact: await
|
|
809
|
+
artifact: await writeIdempotentJsonArtifact(delivery.output, result.json),
|
|
809
810
|
};
|
|
810
811
|
if (!delivery.returnContent) {
|
|
811
812
|
return {
|
|
@@ -872,7 +873,7 @@ async function docxObservation(tool, args) {
|
|
|
872
873
|
};
|
|
873
874
|
const withArtifact = delivery.output === null ? retained : {
|
|
874
875
|
...retained,
|
|
875
|
-
artifact: await
|
|
876
|
+
artifact: await writeIdempotentJsonArtifact(delivery.output, detailPage),
|
|
876
877
|
};
|
|
877
878
|
if (!delivery.returnContent) {
|
|
878
879
|
return {
|
|
@@ -964,7 +965,7 @@ async function docxReadObject(args) {
|
|
|
964
965
|
returnContent: delivery.returnContent,
|
|
965
966
|
artifact: delivery.output === null
|
|
966
967
|
? null
|
|
967
|
-
: await
|
|
968
|
+
: await writeIdempotentJsonArtifact(delivery.output, result.json),
|
|
968
969
|
receipt: {
|
|
969
970
|
schema: 'tiwater.docx-read-object-receipt/v1',
|
|
970
971
|
observationCount: observations.length,
|