touchdesigner-mcp-server 1.2.0 ā 1.3.1
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/README.ja.md +46 -0
- package/README.md +46 -0
- package/dist/cli.js +1 -1
- package/dist/core/compatibility.js +236 -0
- package/dist/core/constants.js +5 -3
- package/dist/core/errorHandling.js +18 -3
- package/dist/core/logger.js +17 -32
- package/dist/core/result.js +2 -2
- package/dist/core/version.js +24 -0
- package/dist/features/prompts/handlers/td_prompts.js +24 -18
- package/dist/features/tools/handlers/tdTools.js +75 -18
- package/dist/features/tools/metadata/touchDesignerToolMetadata.js +246 -172
- package/dist/features/tools/presenter/classListFormatter.js +22 -22
- package/dist/features/tools/presenter/index.js +2 -0
- package/dist/features/tools/presenter/moduleHelpFormatter.js +315 -0
- package/dist/features/tools/presenter/nodeDetailsFormatter.js +17 -17
- package/dist/features/tools/presenter/nodeErrorsFormatter.js +68 -0
- package/dist/features/tools/presenter/nodeListFormatter.js +12 -12
- package/dist/features/tools/presenter/operationFormatter.js +26 -19
- package/dist/features/tools/presenter/responseFormatter.js +4 -4
- package/dist/features/tools/presenter/scriptResultFormatter.js +14 -14
- package/dist/features/tools/presenter/toolMetadataFormatter.js +8 -8
- package/dist/gen/endpoints/TouchDesignerAPI.js +22 -4
- package/dist/gen/mcp/touchDesignerAPI.zod.js +52 -12
- package/dist/server/connectionManager.js +11 -28
- package/dist/server/touchDesignerServer.js +4 -3
- package/dist/tdClient/touchDesignerClient.js +267 -46
- package/package.json +31 -17
|
@@ -28,9 +28,9 @@ export function formatClassList(data, options) {
|
|
|
28
28
|
: formatClassListSummary(classes, modules, opts.limit);
|
|
29
29
|
const ctx = context;
|
|
30
30
|
return finalizeFormattedText(text, opts, {
|
|
31
|
-
template: "classListSummary",
|
|
32
31
|
context: ctx,
|
|
33
32
|
structured: ctx,
|
|
33
|
+
template: "classListSummary",
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
@@ -49,9 +49,9 @@ export function formatClassDetails(data, options) {
|
|
|
49
49
|
: formatClassDetailsSummary(data, opts.limit);
|
|
50
50
|
const ctx = context;
|
|
51
51
|
return finalizeFormattedText(text, opts, {
|
|
52
|
-
template: "classDetailsSummary",
|
|
53
52
|
context: ctx,
|
|
54
53
|
structured: ctx,
|
|
54
|
+
template: "classDetailsSummary",
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
function formatClassListMinimal(classes, modules, limit) {
|
|
@@ -66,8 +66,8 @@ function formatClassListMinimal(classes, modules, limit) {
|
|
|
66
66
|
text += `\nModules (${modules.length}): ${modules.join(", ")}`;
|
|
67
67
|
}
|
|
68
68
|
return {
|
|
69
|
-
text,
|
|
70
69
|
context: buildClassListContext(classes, modules),
|
|
70
|
+
text,
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
function formatClassListSummary(classes, modules, limit) {
|
|
@@ -78,37 +78,37 @@ function formatClassListSummary(classes, modules, limit) {
|
|
|
78
78
|
.map((m) => `- ${m}`)
|
|
79
79
|
.join("\n")}`;
|
|
80
80
|
return {
|
|
81
|
-
text,
|
|
82
81
|
context: buildClassListContext(classes, modules),
|
|
82
|
+
text,
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
function buildClassListContext(classes, modules) {
|
|
86
86
|
return {
|
|
87
87
|
classCount: classes.length,
|
|
88
|
-
moduleCount: modules.length,
|
|
89
88
|
classes: classes.map((cls) => ({
|
|
90
|
-
name: cls.name,
|
|
91
89
|
description: cls.description,
|
|
90
|
+
name: cls.name,
|
|
92
91
|
})),
|
|
92
|
+
moduleCount: modules.length,
|
|
93
93
|
modules,
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
96
|
function formatClassDetailsMinimal(data) {
|
|
97
97
|
const text = `Class: ${data.name}\nType: ${data.type}`;
|
|
98
98
|
return {
|
|
99
|
-
text,
|
|
100
99
|
context: {
|
|
101
|
-
name: data.name,
|
|
102
|
-
type: data.type,
|
|
103
100
|
description: data.description,
|
|
101
|
+
methods: [],
|
|
104
102
|
methodsShown: 0,
|
|
105
103
|
methodsTotal: data.methods?.length ?? 0,
|
|
106
|
-
|
|
104
|
+
name: data.name,
|
|
105
|
+
properties: [],
|
|
107
106
|
propertiesShown: 0,
|
|
108
107
|
propertiesTotal: data.properties?.length ?? 0,
|
|
109
|
-
properties: [],
|
|
110
108
|
truncated: false,
|
|
109
|
+
type: data.type,
|
|
111
110
|
},
|
|
111
|
+
text,
|
|
112
112
|
};
|
|
113
113
|
}
|
|
114
114
|
function formatClassDetailsSummary(data, limit) {
|
|
@@ -148,25 +148,25 @@ function formatClassDetailsSummary(data, limit) {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
return {
|
|
151
|
-
text,
|
|
152
151
|
context: {
|
|
153
|
-
name: data.name,
|
|
154
|
-
type: data.type,
|
|
155
152
|
description: data.description,
|
|
156
|
-
methodsShown: limitedMethods.length,
|
|
157
|
-
methodsTotal: methods.length,
|
|
158
153
|
methods: limitedMethods.map((method) => ({
|
|
159
154
|
signature: method.signature || `${method.name}()`,
|
|
160
155
|
summary: method.description?.split("\n")[0] ?? "",
|
|
161
156
|
})),
|
|
162
|
-
|
|
163
|
-
|
|
157
|
+
methodsShown: limitedMethods.length,
|
|
158
|
+
methodsTotal: methods.length,
|
|
159
|
+
name: data.name,
|
|
164
160
|
properties: limitedProps.map((prop) => ({
|
|
165
161
|
name: prop.name,
|
|
166
162
|
type: prop.type,
|
|
167
163
|
})),
|
|
164
|
+
propertiesShown: limitedProps.length,
|
|
165
|
+
propertiesTotal: properties.length,
|
|
168
166
|
truncated: methodsTruncated || propsTruncated,
|
|
167
|
+
type: data.type,
|
|
169
168
|
},
|
|
169
|
+
text,
|
|
170
170
|
};
|
|
171
171
|
}
|
|
172
172
|
function formatDetailed(data, format) {
|
|
@@ -175,13 +175,13 @@ function formatDetailed(data, format) {
|
|
|
175
175
|
: "TouchDesigner Classes";
|
|
176
176
|
const payloadFormat = format ?? DEFAULT_PRESENTER_FORMAT;
|
|
177
177
|
return presentStructuredData({
|
|
178
|
-
text: title,
|
|
179
|
-
detailLevel: "detailed",
|
|
180
|
-
structured: data,
|
|
181
178
|
context: {
|
|
182
|
-
title,
|
|
183
179
|
payloadFormat,
|
|
180
|
+
title,
|
|
184
181
|
},
|
|
182
|
+
detailLevel: "detailed",
|
|
183
|
+
structured: data,
|
|
185
184
|
template: "detailedPayload",
|
|
185
|
+
text: title,
|
|
186
186
|
}, payloadFormat);
|
|
187
187
|
}
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* Central export point for all response formatters
|
|
5
5
|
*/
|
|
6
6
|
export { formatClassDetails, formatClassList } from "./classListFormatter.js";
|
|
7
|
+
export { formatModuleHelp } from "./moduleHelpFormatter.js";
|
|
7
8
|
export { formatNodeDetails } from "./nodeDetailsFormatter.js";
|
|
9
|
+
export { formatNodeErrors } from "./nodeErrorsFormatter.js";
|
|
8
10
|
export { formatNodeList } from "./nodeListFormatter.js";
|
|
9
11
|
export { formatCreateNodeResult, formatDeleteNodeResult, formatExecNodeMethodResult, formatTdInfo, formatUpdateNodeResult, } from "./operationFormatter.js";
|
|
10
12
|
export { formatScriptResult } from "./scriptResultFormatter.js";
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module Help Formatter
|
|
3
|
+
*
|
|
4
|
+
* Formats TouchDesigner module help information with token optimization.
|
|
5
|
+
* Used by GET_MODULE_HELP tool.
|
|
6
|
+
*/
|
|
7
|
+
import { DEFAULT_PRESENTER_FORMAT, presentStructuredData, } from "./presenter.js";
|
|
8
|
+
import { finalizeFormattedText, mergeFormatterOptions, } from "./responseFormatter.js";
|
|
9
|
+
/**
|
|
10
|
+
* Format module help result
|
|
11
|
+
*/
|
|
12
|
+
export function formatModuleHelp(data, options) {
|
|
13
|
+
const opts = mergeFormatterOptions(options);
|
|
14
|
+
if (!data?.helpText) {
|
|
15
|
+
return "No help information available.";
|
|
16
|
+
}
|
|
17
|
+
const moduleName = data.moduleName ?? "Unknown";
|
|
18
|
+
const helpText = data.helpText;
|
|
19
|
+
const members = extractModuleMembers(helpText);
|
|
20
|
+
const classInfo = extractClassSummary(helpText);
|
|
21
|
+
if (opts.detailLevel === "detailed") {
|
|
22
|
+
return formatDetailed(moduleName, helpText, opts.responseFormat);
|
|
23
|
+
}
|
|
24
|
+
let formattedText = "";
|
|
25
|
+
let context;
|
|
26
|
+
switch (opts.detailLevel) {
|
|
27
|
+
case "minimal":
|
|
28
|
+
case "summary": {
|
|
29
|
+
const summary = formatSummary(moduleName, helpText, members, classInfo);
|
|
30
|
+
formattedText = summary.text;
|
|
31
|
+
context = summary.context;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
default:
|
|
35
|
+
formattedText = helpText;
|
|
36
|
+
context = buildHelpContext(moduleName, helpText, members, classInfo);
|
|
37
|
+
}
|
|
38
|
+
const ctx = context;
|
|
39
|
+
return finalizeFormattedText(formattedText, opts, {
|
|
40
|
+
context: ctx,
|
|
41
|
+
structured: ctx,
|
|
42
|
+
template: "moduleHelp",
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Summary mode: Module name with key sections
|
|
47
|
+
*/
|
|
48
|
+
function formatSummary(moduleName, helpText, members, classInfo) {
|
|
49
|
+
const sections = extractHelpSections(helpText);
|
|
50
|
+
const preview = extractHelpPreview(helpText, 500);
|
|
51
|
+
const memberSummary = formatMemberSummary(members);
|
|
52
|
+
const lines = [`ā Help information for ${moduleName}`];
|
|
53
|
+
if (classInfo?.definition) {
|
|
54
|
+
lines.push(`Class: ${classInfo.definition}`);
|
|
55
|
+
}
|
|
56
|
+
if (classInfo?.description) {
|
|
57
|
+
lines.push(classInfo.description);
|
|
58
|
+
}
|
|
59
|
+
if (classInfo?.methodResolutionOrder?.length) {
|
|
60
|
+
lines.push(`MRO: ${classInfo.methodResolutionOrder.join(" ā ")}`);
|
|
61
|
+
}
|
|
62
|
+
lines.push("");
|
|
63
|
+
if (sections.length > 0) {
|
|
64
|
+
lines.push(`Sections: ${sections.join(", ")}`, "");
|
|
65
|
+
}
|
|
66
|
+
lines.push(preview);
|
|
67
|
+
if (memberSummary) {
|
|
68
|
+
lines.push("", memberSummary);
|
|
69
|
+
}
|
|
70
|
+
if (helpText.length > 500) {
|
|
71
|
+
lines.push("", `š” Use detailLevel='detailed' to see full documentation (${helpText.length} chars total).`);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
context: {
|
|
75
|
+
classInfo,
|
|
76
|
+
fullLength: helpText.length,
|
|
77
|
+
helpPreview: preview,
|
|
78
|
+
members,
|
|
79
|
+
moduleName,
|
|
80
|
+
sections,
|
|
81
|
+
},
|
|
82
|
+
text: lines.join("\n"),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Detailed mode: Full help text
|
|
87
|
+
*/
|
|
88
|
+
function formatDetailed(moduleName, helpText, format) {
|
|
89
|
+
const title = `Help for ${moduleName}`;
|
|
90
|
+
const payloadFormat = format ?? DEFAULT_PRESENTER_FORMAT;
|
|
91
|
+
// For detailed view, return formatted markdown
|
|
92
|
+
let formatted = `# ${title}\n\n`;
|
|
93
|
+
formatted += "```\n";
|
|
94
|
+
formatted += helpText;
|
|
95
|
+
formatted += "\n```";
|
|
96
|
+
return presentStructuredData({
|
|
97
|
+
context: {
|
|
98
|
+
payloadFormat,
|
|
99
|
+
title,
|
|
100
|
+
},
|
|
101
|
+
detailLevel: "detailed",
|
|
102
|
+
structured: {
|
|
103
|
+
helpText,
|
|
104
|
+
length: helpText.length,
|
|
105
|
+
moduleName,
|
|
106
|
+
},
|
|
107
|
+
template: "moduleHelpDetailed",
|
|
108
|
+
text: formatted,
|
|
109
|
+
}, payloadFormat);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Build help context
|
|
113
|
+
*/
|
|
114
|
+
function buildHelpContext(moduleName, helpText, members, classInfo) {
|
|
115
|
+
return {
|
|
116
|
+
classInfo,
|
|
117
|
+
fullLength: helpText.length,
|
|
118
|
+
helpPreview: extractHelpPreview(helpText, 200),
|
|
119
|
+
members,
|
|
120
|
+
moduleName,
|
|
121
|
+
sections: extractHelpSections(helpText),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Extract preview from help text
|
|
126
|
+
*/
|
|
127
|
+
function extractHelpPreview(helpText, maxChars) {
|
|
128
|
+
const trimmed = helpText.trim();
|
|
129
|
+
if (trimmed.length <= maxChars) {
|
|
130
|
+
return trimmed;
|
|
131
|
+
}
|
|
132
|
+
// Try to cut at a natural break point (newline)
|
|
133
|
+
const firstPart = trimmed.substring(0, maxChars);
|
|
134
|
+
const lastNewline = firstPart.lastIndexOf("\n");
|
|
135
|
+
if (lastNewline > maxChars * 0.7) {
|
|
136
|
+
return `${firstPart.substring(0, lastNewline)}...`;
|
|
137
|
+
}
|
|
138
|
+
return `${firstPart}...`;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Extract section headers from help text
|
|
142
|
+
*/
|
|
143
|
+
function extractHelpSections(helpText) {
|
|
144
|
+
const sections = [];
|
|
145
|
+
const lines = helpText.split("\n");
|
|
146
|
+
// Common help section patterns
|
|
147
|
+
const sectionPatterns = [
|
|
148
|
+
/^([A-Z][A-Za-z\s]+):$/,
|
|
149
|
+
/^\s*([A-Z][A-Z\s]+)$/,
|
|
150
|
+
/^-+\s*$/,
|
|
151
|
+
];
|
|
152
|
+
let lastSection = "";
|
|
153
|
+
for (const line of lines) {
|
|
154
|
+
const trimmed = line.trim();
|
|
155
|
+
// Check for section headers
|
|
156
|
+
for (const pattern of sectionPatterns) {
|
|
157
|
+
const match = trimmed.match(pattern);
|
|
158
|
+
if (match?.[1]) {
|
|
159
|
+
const section = match[1].trim();
|
|
160
|
+
if (section && section !== lastSection && section.length < 50) {
|
|
161
|
+
sections.push(section);
|
|
162
|
+
lastSection = section;
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Limit to first 10 sections
|
|
168
|
+
if (sections.length >= 10) {
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return sections;
|
|
173
|
+
}
|
|
174
|
+
function extractModuleMembers(helpText) {
|
|
175
|
+
const methods = [];
|
|
176
|
+
const properties = [];
|
|
177
|
+
const seenMethods = new Set();
|
|
178
|
+
const seenProperties = new Set();
|
|
179
|
+
const lines = helpText.split("\n");
|
|
180
|
+
let currentCategory;
|
|
181
|
+
for (const line of lines) {
|
|
182
|
+
const trimmed = line.trim();
|
|
183
|
+
if (!trimmed)
|
|
184
|
+
continue;
|
|
185
|
+
const normalized = trimmed.replace(/^\|/, "").trim();
|
|
186
|
+
const headerMatch = normalized.match(/^(.*?):$/);
|
|
187
|
+
if (headerMatch) {
|
|
188
|
+
const newCategory = categorizeSection(headerMatch[1]);
|
|
189
|
+
if (newCategory) {
|
|
190
|
+
currentCategory = newCategory;
|
|
191
|
+
}
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (!currentCategory)
|
|
195
|
+
continue;
|
|
196
|
+
const entryMatch = trimmed.match(/^\|\s{2,4}([A-Za-z_][\w]*)/);
|
|
197
|
+
if (!entryMatch)
|
|
198
|
+
continue;
|
|
199
|
+
const name = entryMatch[1];
|
|
200
|
+
if (!name)
|
|
201
|
+
continue;
|
|
202
|
+
if (currentCategory === "method") {
|
|
203
|
+
if (!seenMethods.has(name)) {
|
|
204
|
+
seenMethods.add(name);
|
|
205
|
+
methods.push(name);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else if (currentCategory === "property") {
|
|
209
|
+
if (!seenProperties.has(name)) {
|
|
210
|
+
seenProperties.add(name);
|
|
211
|
+
properties.push(name);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return { methods, properties };
|
|
216
|
+
}
|
|
217
|
+
function extractClassSummary(helpText) {
|
|
218
|
+
const lines = helpText.split("\n");
|
|
219
|
+
let definition;
|
|
220
|
+
const descriptionLines = [];
|
|
221
|
+
const methodResolutionOrder = [];
|
|
222
|
+
let inDescription = false;
|
|
223
|
+
let inMro = false;
|
|
224
|
+
for (let i = 0; i < lines.length; i++) {
|
|
225
|
+
const raw = lines[i];
|
|
226
|
+
const trimmed = raw.trim();
|
|
227
|
+
if (!definition) {
|
|
228
|
+
const defMatch = trimmed.match(/^class\s+(.+)$/);
|
|
229
|
+
if (defMatch) {
|
|
230
|
+
definition = defMatch[1];
|
|
231
|
+
inDescription = true;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (inDescription) {
|
|
236
|
+
if (!trimmed || trimmed.startsWith("| Methods defined here:")) {
|
|
237
|
+
inDescription = false;
|
|
238
|
+
continue; // Skip further processing of this line
|
|
239
|
+
}
|
|
240
|
+
if (trimmed.startsWith("|")) {
|
|
241
|
+
const desc = trimmed.replace(/^\|\s*/, "");
|
|
242
|
+
if (desc) {
|
|
243
|
+
descriptionLines.push(desc);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (trimmed.startsWith("| Method resolution order:")) {
|
|
248
|
+
inMro = true;
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (inMro) {
|
|
252
|
+
if (!trimmed.startsWith("|")) {
|
|
253
|
+
// MRO section ended - fall through to the exit check below
|
|
254
|
+
inMro = false;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
const entry = trimmed.replace(/^\|\s*/, "");
|
|
258
|
+
if (entry) {
|
|
259
|
+
methodResolutionOrder.push(entry.trim());
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// Exit early once we have collected the MRO and we're done with both sections
|
|
264
|
+
if (methodResolutionOrder.length > 0 && !inMro && !inDescription) {
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (!definition &&
|
|
269
|
+
descriptionLines.length === 0 &&
|
|
270
|
+
methodResolutionOrder.length === 0) {
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
definition,
|
|
275
|
+
description: descriptionLines.slice(0, 3).join(" "),
|
|
276
|
+
methodResolutionOrder: methodResolutionOrder.length
|
|
277
|
+
? methodResolutionOrder
|
|
278
|
+
: undefined,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
function categorizeSection(sectionName) {
|
|
282
|
+
const normalized = sectionName.toLowerCase();
|
|
283
|
+
if (normalized.includes("method")) {
|
|
284
|
+
return "method";
|
|
285
|
+
}
|
|
286
|
+
if (normalized.includes("descriptor") ||
|
|
287
|
+
normalized.includes("attribute") ||
|
|
288
|
+
normalized.includes("property")) {
|
|
289
|
+
return "property";
|
|
290
|
+
}
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
function formatMemberSummary(members, limitPerGroup) {
|
|
294
|
+
const segments = [];
|
|
295
|
+
const methodSummary = formatMemberGroup("Methods", members.methods, limitPerGroup);
|
|
296
|
+
if (methodSummary) {
|
|
297
|
+
segments.push(methodSummary);
|
|
298
|
+
}
|
|
299
|
+
const propertySummary = formatMemberGroup("Properties", members.properties, limitPerGroup);
|
|
300
|
+
if (propertySummary) {
|
|
301
|
+
segments.push(propertySummary);
|
|
302
|
+
}
|
|
303
|
+
return segments.join("\n\n");
|
|
304
|
+
}
|
|
305
|
+
function formatMemberGroup(label, items, limit) {
|
|
306
|
+
if (items.length === 0) {
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
const effectiveLimit = typeof limit === "number" && Number.isFinite(limit)
|
|
310
|
+
? Math.max(limit, 0)
|
|
311
|
+
: items.length;
|
|
312
|
+
const displayed = items.slice(0, effectiveLimit);
|
|
313
|
+
const suffix = items.length > effectiveLimit ? ", ā¦" : "";
|
|
314
|
+
return `${label} (${items.length}): ${displayed.join(", ")}${suffix}`;
|
|
315
|
+
}
|
|
@@ -32,9 +32,9 @@ export function formatNodeDetails(data, options) {
|
|
|
32
32
|
}
|
|
33
33
|
const context = result.context;
|
|
34
34
|
return finalizeFormattedText(result.text, opts, {
|
|
35
|
-
template: "nodeDetailsSummary",
|
|
36
35
|
context,
|
|
37
36
|
structured: context,
|
|
37
|
+
template: "nodeDetailsSummary",
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
@@ -47,18 +47,18 @@ function formatMinimal(nodePath, propertyKeys, limit) {
|
|
|
47
47
|
text += `\nš” ${propertyKeys.length - items.length} more properties omitted.`;
|
|
48
48
|
}
|
|
49
49
|
return {
|
|
50
|
-
text,
|
|
51
50
|
context: {
|
|
52
|
-
|
|
53
|
-
type: "",
|
|
51
|
+
displayed: items.length,
|
|
54
52
|
id: 0,
|
|
55
53
|
name: "",
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
nodePath,
|
|
55
|
+
omittedCount: Math.max(propertyKeys.length - items.length, 0),
|
|
58
56
|
properties: items.map((name) => ({ name, value: "" })),
|
|
57
|
+
total: propertyKeys.length,
|
|
59
58
|
truncated,
|
|
60
|
-
|
|
59
|
+
type: "",
|
|
61
60
|
},
|
|
61
|
+
text,
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
@@ -82,18 +82,18 @@ function formatSummary(nodePath, data, limit) {
|
|
|
82
82
|
text += `\nš” ${propertyEntries.length - items.length} more properties omitted.`;
|
|
83
83
|
}
|
|
84
84
|
return {
|
|
85
|
-
text,
|
|
86
85
|
context: {
|
|
87
|
-
|
|
88
|
-
type: data.opType,
|
|
86
|
+
displayed: items.length,
|
|
89
87
|
id: data.id,
|
|
90
88
|
name: data.name,
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
nodePath,
|
|
90
|
+
omittedCount: Math.max(propertyEntries.length - items.length, 0),
|
|
93
91
|
properties: propsForContext,
|
|
92
|
+
total: propertyEntries.length,
|
|
94
93
|
truncated,
|
|
95
|
-
|
|
94
|
+
type: data.opType,
|
|
96
95
|
},
|
|
96
|
+
text,
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
@@ -103,14 +103,14 @@ function formatDetailed(data, format) {
|
|
|
103
103
|
const title = `Node ${data.path ?? data.name ?? "details"}`;
|
|
104
104
|
const payloadFormat = format ?? DEFAULT_PRESENTER_FORMAT;
|
|
105
105
|
return presentStructuredData({
|
|
106
|
-
text: title,
|
|
107
|
-
detailLevel: "detailed",
|
|
108
|
-
structured: data,
|
|
109
106
|
context: {
|
|
110
|
-
title,
|
|
111
107
|
payloadFormat,
|
|
108
|
+
title,
|
|
112
109
|
},
|
|
110
|
+
detailLevel: "detailed",
|
|
111
|
+
structured: data,
|
|
113
112
|
template: "detailedPayload",
|
|
113
|
+
text: title,
|
|
114
114
|
}, payloadFormat);
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { DEFAULT_PRESENTER_FORMAT, presentStructuredData, } from "./presenter.js";
|
|
2
|
+
import { finalizeFormattedText, limitArray, mergeFormatterOptions, } from "./responseFormatter.js";
|
|
3
|
+
export function formatNodeErrors(data, options) {
|
|
4
|
+
const opts = mergeFormatterOptions(options);
|
|
5
|
+
if (!data) {
|
|
6
|
+
return "No node error information is available.";
|
|
7
|
+
}
|
|
8
|
+
if (opts.detailLevel === "detailed") {
|
|
9
|
+
return formatDetailed(data, opts.responseFormat);
|
|
10
|
+
}
|
|
11
|
+
const errors = data.errors ?? [];
|
|
12
|
+
if (errors.length === 0 || !data.hasErrors || data.errorCount === 0) {
|
|
13
|
+
const noErrorText = `Node ${data.nodePath} has no reported errors.`;
|
|
14
|
+
return finalizeFormattedText(noErrorText, opts, {
|
|
15
|
+
context: {
|
|
16
|
+
errorCount: 0,
|
|
17
|
+
nodeName: data.nodeName,
|
|
18
|
+
nodePath: data.nodePath,
|
|
19
|
+
},
|
|
20
|
+
structured: data,
|
|
21
|
+
template: "nodeErrorSummary",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
const { items, truncated } = limitArray(errors, opts.limit);
|
|
25
|
+
const header = `Node: ${data.nodePath}\nOperator: ${data.opType} (${data.nodeName})\n${data.errorCount} error(s) found\n`;
|
|
26
|
+
const body = opts.detailLevel === "minimal"
|
|
27
|
+
? formatMinimal(items)
|
|
28
|
+
: formatSummary(items);
|
|
29
|
+
let text = `${header}\n${body}`;
|
|
30
|
+
if (truncated) {
|
|
31
|
+
text += `\nš” ${data.errorCount - items.length} more errors omitted.`;
|
|
32
|
+
}
|
|
33
|
+
return finalizeFormattedText(text, opts, {
|
|
34
|
+
context: {
|
|
35
|
+
displayed: items.length,
|
|
36
|
+
errorCount: data.errorCount,
|
|
37
|
+
nodeName: data.nodeName,
|
|
38
|
+
nodePath: data.nodePath,
|
|
39
|
+
opType: data.opType,
|
|
40
|
+
},
|
|
41
|
+
structured: data,
|
|
42
|
+
template: "nodeErrorSummary",
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function formatMinimal(errors) {
|
|
46
|
+
return errors
|
|
47
|
+
.map((entry) => `- ${entry.nodePath}: ${entry.message}`)
|
|
48
|
+
.join("\n");
|
|
49
|
+
}
|
|
50
|
+
function formatSummary(errors) {
|
|
51
|
+
return errors
|
|
52
|
+
.map((entry) => `- ${entry.nodePath} (${entry.opType}): ${entry.message}`)
|
|
53
|
+
.join("\n");
|
|
54
|
+
}
|
|
55
|
+
function formatDetailed(data, format) {
|
|
56
|
+
const title = `Node error report for ${data.nodePath}`;
|
|
57
|
+
const payloadFormat = format ?? DEFAULT_PRESENTER_FORMAT;
|
|
58
|
+
return presentStructuredData({
|
|
59
|
+
context: {
|
|
60
|
+
payloadFormat,
|
|
61
|
+
title,
|
|
62
|
+
},
|
|
63
|
+
detailLevel: "detailed",
|
|
64
|
+
structured: data,
|
|
65
|
+
template: "detailedPayload",
|
|
66
|
+
text: title,
|
|
67
|
+
}, payloadFormat);
|
|
68
|
+
}
|
|
@@ -40,9 +40,9 @@ export function formatNodeList(data, options) {
|
|
|
40
40
|
context.omittedCount = 0;
|
|
41
41
|
}
|
|
42
42
|
return finalizeFormattedText(output, opts, {
|
|
43
|
-
template: "nodeListSummary",
|
|
44
43
|
context,
|
|
45
44
|
structured: context,
|
|
45
|
+
template: "nodeListSummary",
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
@@ -53,8 +53,8 @@ function formatMinimal(nodes, parentPath, totalCount, truncated) {
|
|
|
53
53
|
const text = `Found ${nodes.length} nodes in ${parentPath}:
|
|
54
54
|
${paths.join("\n")}`;
|
|
55
55
|
return {
|
|
56
|
-
text,
|
|
57
56
|
context: buildNodeListContext(nodes, parentPath, totalCount, truncated),
|
|
57
|
+
text,
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
@@ -71,14 +71,14 @@ function formatSummary(nodes, parentPath, totalCount, truncated) {
|
|
|
71
71
|
${nodeLines.join("\n")}`;
|
|
72
72
|
});
|
|
73
73
|
return {
|
|
74
|
-
text: header + sections.join("\n\n"),
|
|
75
74
|
context: {
|
|
75
|
+
groups,
|
|
76
|
+
omittedCount: Math.max(totalCount - nodes.length, 0),
|
|
76
77
|
parentPath,
|
|
77
78
|
totalCount,
|
|
78
|
-
groups,
|
|
79
79
|
truncated,
|
|
80
|
-
omittedCount: Math.max(totalCount - nodes.length, 0),
|
|
81
80
|
},
|
|
81
|
+
text: header + sections.join("\n\n"),
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
@@ -88,23 +88,23 @@ function formatDetailed(nodes, data, format, parentPath) {
|
|
|
88
88
|
const title = `Nodes in ${parentPath}`;
|
|
89
89
|
const payloadFormat = format ?? DEFAULT_PRESENTER_FORMAT;
|
|
90
90
|
return presentStructuredData({
|
|
91
|
-
text: title,
|
|
92
|
-
detailLevel: "detailed",
|
|
93
|
-
structured: { ...data, nodes },
|
|
94
91
|
context: {
|
|
95
|
-
title,
|
|
96
92
|
payloadFormat,
|
|
93
|
+
title,
|
|
97
94
|
},
|
|
95
|
+
detailLevel: "detailed",
|
|
96
|
+
structured: { ...data, nodes },
|
|
98
97
|
template: "detailedPayload",
|
|
98
|
+
text: title,
|
|
99
99
|
}, payloadFormat);
|
|
100
100
|
}
|
|
101
101
|
function buildNodeListContext(nodes, parentPath, totalCount, truncated) {
|
|
102
102
|
return {
|
|
103
|
+
groups: buildGroups(nodes),
|
|
104
|
+
omittedCount: Math.max(totalCount - nodes.length, 0),
|
|
103
105
|
parentPath,
|
|
104
106
|
totalCount,
|
|
105
|
-
groups: buildGroups(nodes),
|
|
106
107
|
truncated,
|
|
107
|
-
omittedCount: Math.max(totalCount - nodes.length, 0),
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
110
|
function buildGroups(nodes) {
|
|
@@ -117,8 +117,8 @@ function buildGroups(nodes) {
|
|
|
117
117
|
byType.get(type)?.push(node);
|
|
118
118
|
}
|
|
119
119
|
return Array.from(byType.entries()).map(([type, typeNodes]) => ({
|
|
120
|
-
type,
|
|
121
120
|
count: typeNodes.length,
|
|
122
121
|
nodes: typeNodes.map((n) => ({ name: n.name, path: n.path })),
|
|
122
|
+
type,
|
|
123
123
|
}));
|
|
124
124
|
}
|