@tiwater/office-mcp 0.3.0 → 0.4.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/tool-runtime.mjs +34 -1
- package/office/index.mjs +74 -22
- package/package.json +1 -1
package/_shared/tool-runtime.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
|
+
import { constants as fsConstants } from 'node:fs';
|
|
2
3
|
import os from 'node:os';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import { fileURLToPath } from 'node:url';
|
|
@@ -78,7 +79,7 @@ export function requireString(value, label) {
|
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
async function runCommand(candidate, args, options) {
|
|
81
|
-
const env = { ...process.env, ...(candidate.env || {}), ...(options.env || {}) };
|
|
82
|
+
const env = await withDotnetRoot({ ...process.env, ...(candidate.env || {}), ...(options.env || {}) });
|
|
82
83
|
const cwd = candidate.cwd || options.cwd || repoRoot;
|
|
83
84
|
const commandArgs = [...(candidate.argsPrefix || []), ...args];
|
|
84
85
|
|
|
@@ -106,3 +107,35 @@ async function runCommand(candidate, args, options) {
|
|
|
106
107
|
});
|
|
107
108
|
});
|
|
108
109
|
}
|
|
110
|
+
|
|
111
|
+
async function withDotnetRoot(env) {
|
|
112
|
+
const architectureVariable = process.arch === 'arm64'
|
|
113
|
+
? 'DOTNET_ROOT_ARM64'
|
|
114
|
+
: process.arch === 'x64'
|
|
115
|
+
? 'DOTNET_ROOT_X64'
|
|
116
|
+
: null;
|
|
117
|
+
if (env.DOTNET_ROOT || (architectureVariable && env[architectureVariable])) return env;
|
|
118
|
+
|
|
119
|
+
const dotnet = await findOnPath(process.platform === 'win32' ? 'dotnet.exe' : 'dotnet', env.PATH);
|
|
120
|
+
if (!dotnet) return env;
|
|
121
|
+
|
|
122
|
+
const root = path.dirname(dotnet);
|
|
123
|
+
return {
|
|
124
|
+
...env,
|
|
125
|
+
DOTNET_ROOT: root,
|
|
126
|
+
...(architectureVariable ? { [architectureVariable]: root } : {}),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function findOnPath(command, pathValue) {
|
|
131
|
+
for (const directory of String(pathValue ?? '').split(path.delimiter).filter(Boolean)) {
|
|
132
|
+
const candidate = path.join(directory, command);
|
|
133
|
+
try {
|
|
134
|
+
await fs.access(candidate, fsConstants.X_OK);
|
|
135
|
+
return candidate;
|
|
136
|
+
} catch {
|
|
137
|
+
// Continue to the next PATH entry.
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
package/office/index.mjs
CHANGED
|
@@ -72,6 +72,7 @@ const templateMigrationInput = z.object({
|
|
|
72
72
|
source: pathInput.describe('Path to the current source DOCX.'),
|
|
73
73
|
baseline: pathInput.describe('Path to the selected current baseline DOCX.'),
|
|
74
74
|
output: pathInput.describe('Path to the migrated output DOCX.'),
|
|
75
|
+
receiptOutput: pathInput.describe('New JSON receipt artifact path. Existing files are never overwritten.'),
|
|
75
76
|
choices: z.array(migrationChoiceInput).describe('Exactly one business choice for every source id returned by docx_list_migration_choices.'),
|
|
76
77
|
templateCleanup: z.array(templateCleanupInput).optional().describe('Optional baseline-owned placeholders or example rows to clear.'),
|
|
77
78
|
}).strict();
|
|
@@ -92,16 +93,49 @@ const migrationChoiceOutput = z.object({
|
|
|
92
93
|
allowedActions: z.array(z.string()),
|
|
93
94
|
}).strict();
|
|
94
95
|
|
|
96
|
+
const migrationCatalog = z.object({
|
|
97
|
+
schema: z.string(),
|
|
98
|
+
pass: z.boolean(),
|
|
99
|
+
sourceSha256: z.string(),
|
|
100
|
+
baselineSha256: z.string(),
|
|
101
|
+
sources: z.array(migrationChoiceOutput),
|
|
102
|
+
targets: z.array(migrationChoiceOutput),
|
|
103
|
+
}).strict();
|
|
104
|
+
|
|
105
|
+
const migrationReceipt = z.object({
|
|
106
|
+
schema: z.string(),
|
|
107
|
+
toolVersion: z.string(),
|
|
108
|
+
status: z.enum(['pass', 'review-required', 'failed']),
|
|
109
|
+
pass: z.boolean(),
|
|
110
|
+
reviewRequired: z.boolean(),
|
|
111
|
+
outputVerified: z.boolean(),
|
|
112
|
+
output: z.string().nullable(),
|
|
113
|
+
plan: z.string().nullable(),
|
|
114
|
+
failures: z.array(z.unknown()),
|
|
115
|
+
}).passthrough();
|
|
116
|
+
|
|
117
|
+
const inputOnly = z.object({ input: pathInput }).strict();
|
|
118
|
+
const artifactInput = z.object({
|
|
119
|
+
input: pathInput,
|
|
120
|
+
output: pathInput.describe('New JSON artifact path. Existing files are never overwritten.'),
|
|
121
|
+
}).strict();
|
|
122
|
+
const artifact = z.object({
|
|
123
|
+
path: z.string(),
|
|
124
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/),
|
|
125
|
+
bytes: z.number().int().nonnegative(),
|
|
126
|
+
}).strict();
|
|
127
|
+
|
|
95
128
|
const migrationCatalogOutput = z.object({
|
|
96
129
|
tool: z.literal('docx_list_migration_choices'),
|
|
97
130
|
runtime: runtimeIdentity,
|
|
98
|
-
|
|
131
|
+
artifact,
|
|
132
|
+
summary: z.object({
|
|
99
133
|
schema: z.string(),
|
|
100
134
|
pass: z.boolean(),
|
|
101
135
|
sourceSha256: z.string(),
|
|
102
136
|
baselineSha256: z.string(),
|
|
103
|
-
|
|
104
|
-
|
|
137
|
+
sourceCount: z.number().int().nonnegative(),
|
|
138
|
+
targetCount: z.number().int().nonnegative(),
|
|
105
139
|
}).strict(),
|
|
106
140
|
}).strict();
|
|
107
141
|
|
|
@@ -109,7 +143,8 @@ function migrationReceiptOutput(tool) {
|
|
|
109
143
|
return z.object({
|
|
110
144
|
tool: z.literal(tool),
|
|
111
145
|
runtime: runtimeIdentity,
|
|
112
|
-
|
|
146
|
+
artifact,
|
|
147
|
+
summary: z.object({
|
|
113
148
|
schema: z.string(),
|
|
114
149
|
toolVersion: z.string(),
|
|
115
150
|
status: z.enum(['pass', 'review-required', 'failed']),
|
|
@@ -118,22 +153,11 @@ function migrationReceiptOutput(tool) {
|
|
|
118
153
|
outputVerified: z.boolean(),
|
|
119
154
|
output: z.string().nullable(),
|
|
120
155
|
plan: z.string().nullable(),
|
|
121
|
-
|
|
122
|
-
}).
|
|
156
|
+
failureCount: z.number().int().nonnegative(),
|
|
157
|
+
}).strict(),
|
|
123
158
|
}).strict();
|
|
124
159
|
}
|
|
125
160
|
|
|
126
|
-
const inputOnly = z.object({ input: pathInput }).strict();
|
|
127
|
-
const artifactInput = z.object({
|
|
128
|
-
input: pathInput,
|
|
129
|
-
output: pathInput.describe('New JSON artifact path. Existing files are never overwritten.'),
|
|
130
|
-
}).strict();
|
|
131
|
-
const artifact = z.object({
|
|
132
|
-
path: z.string(),
|
|
133
|
-
sha256: z.string().regex(/^[0-9a-f]{64}$/),
|
|
134
|
-
bytes: z.number().int().nonnegative(),
|
|
135
|
-
}).strict();
|
|
136
|
-
|
|
137
161
|
function artifactOutput(tool) {
|
|
138
162
|
return z.object({ tool: z.literal(tool), runtime: runtimeIdentity, artifact }).strict();
|
|
139
163
|
}
|
|
@@ -148,13 +172,13 @@ const tools = [
|
|
|
148
172
|
},
|
|
149
173
|
{
|
|
150
174
|
name: 'docx_list_migration_choices',
|
|
151
|
-
description: '
|
|
175
|
+
description: 'Write every current source item that still needs a business choice and the selectable current baseline targets to a run-local JSON artifact. Returns only artifact metadata and counts; it does not recommend a choice.',
|
|
152
176
|
inputSchema: z.object({
|
|
153
177
|
source: pathInput.describe('Path to the current source DOCX.'),
|
|
154
178
|
baseline: pathInput.describe('Path to the selected current baseline DOCX.'),
|
|
179
|
+
output: pathInput.describe('New JSON artifact path. Existing files are never overwritten.'),
|
|
155
180
|
}).strict(),
|
|
156
181
|
outputSchema: migrationCatalogOutput,
|
|
157
|
-
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
158
182
|
handler: docxListMigrationChoices,
|
|
159
183
|
},
|
|
160
184
|
{
|
|
@@ -169,7 +193,6 @@ const tools = [
|
|
|
169
193
|
description: 'Independently re-resolve the same business choices and verify a migrated DOCX against the current source and baseline. This does not trust the migration receipt.',
|
|
170
194
|
inputSchema: templateMigrationInput,
|
|
171
195
|
outputSchema: migrationReceiptOutput('docx_verify_migration'),
|
|
172
|
-
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
173
196
|
handler: docxVerifyMigration,
|
|
174
197
|
},
|
|
175
198
|
{
|
|
@@ -270,7 +293,20 @@ async function docxListMigrationChoices(args) {
|
|
|
270
293
|
const source = requireString(args.source, 'source');
|
|
271
294
|
const baseline = requireString(args.baseline, 'baseline');
|
|
272
295
|
const result = await runJsonCandidateChain(docxCandidates, ['list-template-migration-choices', source, baseline]);
|
|
273
|
-
|
|
296
|
+
const catalog = migrationCatalog.parse(result.json);
|
|
297
|
+
return {
|
|
298
|
+
tool: 'docx_list_migration_choices',
|
|
299
|
+
runtime: commandRuntime(result),
|
|
300
|
+
artifact: await writeJsonArtifact(requireString(args.output, 'output'), catalog),
|
|
301
|
+
summary: {
|
|
302
|
+
schema: catalog.schema,
|
|
303
|
+
pass: catalog.pass,
|
|
304
|
+
sourceSha256: catalog.sourceSha256,
|
|
305
|
+
baselineSha256: catalog.baselineSha256,
|
|
306
|
+
sourceCount: catalog.sources.length,
|
|
307
|
+
targetCount: catalog.targets.length,
|
|
308
|
+
},
|
|
309
|
+
};
|
|
274
310
|
}
|
|
275
311
|
|
|
276
312
|
async function docxMigrateTemplate(args) {
|
|
@@ -298,7 +334,23 @@ async function runTemplateMigrationCommand(tool, command, args) {
|
|
|
298
334
|
docxCandidates,
|
|
299
335
|
[command, source, baseline, choicesPath, output],
|
|
300
336
|
{ allowedExitCodes: [0, 1] });
|
|
301
|
-
|
|
337
|
+
const receipt = migrationReceipt.parse(result.json);
|
|
338
|
+
return {
|
|
339
|
+
tool,
|
|
340
|
+
runtime: commandRuntime(result),
|
|
341
|
+
artifact: await writeJsonArtifact(requireString(args.receiptOutput, 'receiptOutput'), receipt),
|
|
342
|
+
summary: {
|
|
343
|
+
schema: receipt.schema,
|
|
344
|
+
toolVersion: receipt.toolVersion,
|
|
345
|
+
status: receipt.status,
|
|
346
|
+
pass: receipt.pass,
|
|
347
|
+
reviewRequired: receipt.reviewRequired,
|
|
348
|
+
outputVerified: receipt.outputVerified,
|
|
349
|
+
output: receipt.output,
|
|
350
|
+
plan: receipt.plan,
|
|
351
|
+
failureCount: receipt.failures.length,
|
|
352
|
+
},
|
|
353
|
+
};
|
|
302
354
|
});
|
|
303
355
|
}
|
|
304
356
|
|