proofscan 0.10.62 → 0.11.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 +1 -0
- package/README.md +2 -0
- package/dist/a2a/agent-card.d.ts +2 -0
- package/dist/a2a/agent-card.d.ts.map +1 -1
- package/dist/a2a/agent-card.js +2 -2
- package/dist/a2a/agent-card.js.map +1 -1
- package/dist/a2a/client.d.ts +74 -12
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +228 -29
- package/dist/a2a/client.js.map +1 -1
- package/dist/a2a/normalizer.d.ts +4 -0
- package/dist/a2a/normalizer.d.ts.map +1 -1
- package/dist/a2a/normalizer.js +7 -4
- package/dist/a2a/normalizer.js.map +1 -1
- package/dist/a2a/session-manager.d.ts +81 -0
- package/dist/a2a/session-manager.d.ts.map +1 -0
- package/dist/a2a/session-manager.js +176 -0
- package/dist/a2a/session-manager.js.map +1 -0
- package/dist/a2a/types.d.ts +60 -0
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +35 -10
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +12 -10
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/connectors.js +2 -2
- package/dist/commands/connectors.js.map +1 -1
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +1 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/plans.js +1 -1
- package/dist/commands/plans.js.map +1 -1
- package/dist/commands/record.js +5 -4
- package/dist/commands/record.js.map +1 -1
- package/dist/commands/rpc.d.ts.map +1 -1
- package/dist/commands/rpc.js +90 -28
- package/dist/commands/rpc.js.map +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +8 -10
- package/dist/commands/scan.js.map +1 -1
- package/dist/commands/secrets.d.ts.map +1 -1
- package/dist/commands/secrets.js +11 -10
- package/dist/commands/secrets.js.map +1 -1
- package/dist/commands/sessions.js +2 -2
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/summary.d.ts.map +1 -1
- package/dist/commands/summary.js +4 -2
- package/dist/commands/summary.js.map +1 -1
- package/dist/commands/task.d.ts +14 -0
- package/dist/commands/task.d.ts.map +1 -0
- package/dist/commands/task.js +520 -0
- package/dist/commands/task.js.map +1 -0
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +68 -21
- package/dist/db/connection.js.map +1 -1
- package/dist/db/events-store.d.ts +307 -8
- package/dist/db/events-store.d.ts.map +1 -1
- package/dist/db/events-store.js +620 -26
- package/dist/db/events-store.js.map +1 -1
- package/dist/db/proofs-store.d.ts +8 -1
- package/dist/db/proofs-store.d.ts.map +1 -1
- package/dist/db/proofs-store.js +18 -8
- package/dist/db/proofs-store.js.map +1 -1
- package/dist/db/schema.d.ts +15 -3
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +150 -5
- package/dist/db/schema.js.map +1 -1
- package/dist/db/tool-analysis.d.ts +15 -3
- package/dist/db/tool-analysis.d.ts.map +1 -1
- package/dist/db/tool-analysis.js +35 -17
- package/dist/db/tool-analysis.js.map +1 -1
- package/dist/db/types.d.ts +64 -1
- package/dist/db/types.d.ts.map +1 -1
- package/dist/filter/fields.d.ts.map +1 -1
- package/dist/filter/fields.js +22 -0
- package/dist/filter/fields.js.map +1 -1
- package/dist/filter/parser.js +2 -2
- package/dist/filter/parser.js.map +1 -1
- package/dist/filter/types.d.ts +1 -1
- package/dist/filter/types.d.ts.map +1 -1
- package/dist/html/analytics.test.ts +682 -0
- package/dist/html/analytics.ts +499 -0
- package/dist/html/browser.ts +39 -0
- package/dist/html/index.ts +97 -0
- package/dist/html/rpc-inspector.test.ts +529 -0
- package/dist/html/rpc-inspector.ts +1700 -0
- package/dist/html/templates.js +4 -4
- package/dist/html/templates.js.map +1 -1
- package/dist/html/templates.test.ts +861 -0
- package/dist/html/templates.ts +3163 -0
- package/dist/html/trace-viewer.html +624 -0
- package/dist/html/types.d.ts +3 -3
- package/dist/html/types.d.ts.map +1 -1
- package/dist/html/types.ts +491 -0
- package/dist/html/utils.ts +107 -0
- package/dist/monitor/data/connectors.d.ts.map +1 -1
- package/dist/monitor/data/connectors.js +113 -8
- package/dist/monitor/data/connectors.js.map +1 -1
- package/dist/monitor/data/popl.js +2 -2
- package/dist/monitor/data/popl.js.map +1 -1
- package/dist/monitor/routes/api.js +2 -2
- package/dist/monitor/routes/api.js.map +1 -1
- package/dist/monitor/routes/connectors.js +15 -15
- package/dist/monitor/routes/connectors.js.map +1 -1
- package/dist/monitor/routes/popl.js +5 -5
- package/dist/monitor/routes/popl.js.map +1 -1
- package/dist/monitor/templates/components.js +2 -2
- package/dist/monitor/templates/components.js.map +1 -1
- package/dist/monitor/templates/popl.js +4 -4
- package/dist/monitor/templates/popl.js.map +1 -1
- package/dist/monitor/types.d.ts +2 -2
- package/dist/monitor/types.d.ts.map +1 -1
- package/dist/proxy/bridge-utils.d.ts +41 -0
- package/dist/proxy/bridge-utils.d.ts.map +1 -0
- package/dist/proxy/bridge-utils.js +60 -0
- package/dist/proxy/bridge-utils.js.map +1 -0
- package/dist/proxy/ipc-client.d.ts.map +1 -1
- package/dist/proxy/ipc-client.js +1 -2
- package/dist/proxy/ipc-client.js.map +1 -1
- package/dist/proxy/ipc-server.d.ts.map +1 -1
- package/dist/proxy/ipc-server.js +4 -2
- package/dist/proxy/ipc-server.js.map +1 -1
- package/dist/proxy/mcp-server.d.ts +31 -0
- package/dist/proxy/mcp-server.d.ts.map +1 -1
- package/dist/proxy/mcp-server.js +393 -4
- package/dist/proxy/mcp-server.js.map +1 -1
- package/dist/proxy/types.d.ts +95 -0
- package/dist/proxy/types.d.ts.map +1 -1
- package/dist/secrets/management.d.ts +2 -2
- package/dist/secrets/management.d.ts.map +1 -1
- package/dist/secrets/management.js +7 -7
- package/dist/secrets/management.js.map +1 -1
- package/dist/shell/completer.d.ts.map +1 -1
- package/dist/shell/completer.js +16 -0
- package/dist/shell/completer.js.map +1 -1
- package/dist/shell/context-applicator.d.ts.map +1 -1
- package/dist/shell/context-applicator.js +32 -0
- package/dist/shell/context-applicator.js.map +1 -1
- package/dist/shell/filter-mappers.d.ts +5 -1
- package/dist/shell/filter-mappers.d.ts.map +1 -1
- package/dist/shell/filter-mappers.js +12 -0
- package/dist/shell/filter-mappers.js.map +1 -1
- package/dist/shell/find-command.js +13 -13
- package/dist/shell/find-command.js.map +1 -1
- package/dist/shell/inscribe-commands.js +5 -5
- package/dist/shell/inscribe-commands.js.map +1 -1
- package/dist/shell/pager/less-pager.d.ts +1 -1
- package/dist/shell/pager/less-pager.d.ts.map +1 -1
- package/dist/shell/pager/less-pager.js +5 -2
- package/dist/shell/pager/less-pager.js.map +1 -1
- package/dist/shell/pager/more-pager.d.ts +1 -1
- package/dist/shell/pager/more-pager.d.ts.map +1 -1
- package/dist/shell/pager/more-pager.js +3 -2
- package/dist/shell/pager/more-pager.js.map +1 -1
- package/dist/shell/pager/renderer.d.ts.map +1 -1
- package/dist/shell/pager/renderer.js +66 -15
- package/dist/shell/pager/renderer.js.map +1 -1
- package/dist/shell/pager/types.d.ts +5 -2
- package/dist/shell/pager/types.d.ts.map +1 -1
- package/dist/shell/pager/utils.d.ts +5 -2
- package/dist/shell/pager/utils.d.ts.map +1 -1
- package/dist/shell/pager/utils.js +14 -17
- package/dist/shell/pager/utils.js.map +1 -1
- package/dist/shell/pipeline-types.d.ts +12 -4
- package/dist/shell/pipeline-types.d.ts.map +1 -1
- package/dist/shell/ref-commands.js +7 -7
- package/dist/shell/ref-commands.js.map +1 -1
- package/dist/shell/ref-resolver.d.ts +15 -15
- package/dist/shell/ref-resolver.d.ts.map +1 -1
- package/dist/shell/ref-resolver.js +34 -20
- package/dist/shell/ref-resolver.js.map +1 -1
- package/dist/shell/repl.d.ts +25 -0
- package/dist/shell/repl.d.ts.map +1 -1
- package/dist/shell/repl.js +285 -51
- package/dist/shell/repl.js.map +1 -1
- package/dist/shell/router-commands.d.ts +30 -0
- package/dist/shell/router-commands.d.ts.map +1 -1
- package/dist/shell/router-commands.js +1011 -62
- package/dist/shell/router-commands.js.map +1 -1
- package/dist/shell/selector.d.ts +1 -1
- package/dist/shell/selector.d.ts.map +1 -1
- package/dist/shell/selector.js +1 -1
- package/dist/shell/selector.js.map +1 -1
- package/dist/shell/types.d.ts.map +1 -1
- package/dist/shell/types.js +3 -1
- package/dist/shell/types.js.map +1 -1
- package/dist/shell/where-command.d.ts.map +1 -1
- package/dist/shell/where-command.js +19 -3
- package/dist/shell/where-command.js.map +1 -1
- package/dist/utils/output.d.ts.map +1 -1
- package/dist/utils/output.js +7 -1
- package/dist/utils/output.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,1700 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RPC Inspector - Wireshark-style JSON viewer with path tracking
|
|
3
|
+
*
|
|
4
|
+
* Phase 11.5: 2-column layout with Summary View (left) and Raw JSON View (right)
|
|
5
|
+
* - JSON rendered with data-path attributes for click-to-navigate
|
|
6
|
+
* - Method-aware summary generation (tools/list, etc.)
|
|
7
|
+
* - RFC 6901 JSON Pointer paths
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { SummaryRow, MethodSummaryHandler } from './types.js';
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// HTML Escaping Utilities
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Escape HTML special characters and JavaScript string special chars
|
|
18
|
+
* This is critical because the rendered HTML is embedded in JavaScript string concatenation
|
|
19
|
+
*/
|
|
20
|
+
function escapeHtml(text: string): string {
|
|
21
|
+
return text
|
|
22
|
+
.replace(/\\/g, '\') // Escape backslashes as HTML entity
|
|
23
|
+
.replace(/&/g, '&')
|
|
24
|
+
.replace(/</g, '<')
|
|
25
|
+
.replace(/>/g, '>')
|
|
26
|
+
.replace(/"/g, '"')
|
|
27
|
+
.replace(/'/g, ''')
|
|
28
|
+
.replace(/\n/g, ' ') // Escape newlines for JS string safety
|
|
29
|
+
.replace(/\r/g, ' ') // Escape carriage returns
|
|
30
|
+
.replace(/\u2028/g, '
') // Line Separator (breaks JS strings)
|
|
31
|
+
.replace(/\u2029/g, '
'); // Paragraph Separator (breaks JS strings)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Escape attribute value for data-path
|
|
36
|
+
*/
|
|
37
|
+
function escapeAttr(text: string): string {
|
|
38
|
+
return escapeHtml(text);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// JSON Pointer Utilities (RFC 6901)
|
|
43
|
+
// ============================================================================
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Escape string for JSON Pointer (RFC 6901)
|
|
47
|
+
* ~ → ~0, / → ~1
|
|
48
|
+
*/
|
|
49
|
+
export function escapeJsonPointer(str: string): string {
|
|
50
|
+
return str.replace(/~/g, '~0').replace(/\//g, '~1');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check if value is a primitive (string, number, boolean, null)
|
|
55
|
+
*/
|
|
56
|
+
function isPrimitive(value: unknown): boolean {
|
|
57
|
+
return value === null || typeof value !== 'object';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// JSON Renderer with Path Tracking
|
|
62
|
+
// ============================================================================
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Render primitive value inline (for same-line key-value pairs)
|
|
66
|
+
*/
|
|
67
|
+
function renderPrimitiveInline(value: unknown): string {
|
|
68
|
+
if (value === null) {
|
|
69
|
+
return '<span class="json-null">null</span>';
|
|
70
|
+
}
|
|
71
|
+
if (typeof value === 'boolean') {
|
|
72
|
+
return `<span class="json-bool">${value}</span>`;
|
|
73
|
+
}
|
|
74
|
+
if (typeof value === 'number') {
|
|
75
|
+
return `<span class="json-number">${value}</span>`;
|
|
76
|
+
}
|
|
77
|
+
if (typeof value === 'string') {
|
|
78
|
+
return `<span class="json-string">"${escapeHtml(value)}"</span>`;
|
|
79
|
+
}
|
|
80
|
+
return escapeHtml(String(value));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Render JSON value recursively with path tracking
|
|
85
|
+
*/
|
|
86
|
+
function renderValue(
|
|
87
|
+
value: unknown,
|
|
88
|
+
path: string,
|
|
89
|
+
indent: number,
|
|
90
|
+
lines: string[]
|
|
91
|
+
): void {
|
|
92
|
+
const indentStr = ' '.repeat(indent);
|
|
93
|
+
|
|
94
|
+
if (value === null) {
|
|
95
|
+
lines.push(
|
|
96
|
+
`<span class="json-line" data-path="${escapeAttr(path)}">${indentStr}<span class="json-null">null</span></span>`
|
|
97
|
+
);
|
|
98
|
+
} else if (typeof value === 'boolean') {
|
|
99
|
+
lines.push(
|
|
100
|
+
`<span class="json-line" data-path="${escapeAttr(path)}">${indentStr}<span class="json-bool">${value}</span></span>`
|
|
101
|
+
);
|
|
102
|
+
} else if (typeof value === 'number') {
|
|
103
|
+
lines.push(
|
|
104
|
+
`<span class="json-line" data-path="${escapeAttr(path)}">${indentStr}<span class="json-number">${value}</span></span>`
|
|
105
|
+
);
|
|
106
|
+
} else if (typeof value === 'string') {
|
|
107
|
+
lines.push(
|
|
108
|
+
`<span class="json-line" data-path="${escapeAttr(path)}">${indentStr}<span class="json-string">"${escapeHtml(value)}"</span></span>`
|
|
109
|
+
);
|
|
110
|
+
} else if (Array.isArray(value)) {
|
|
111
|
+
renderArray(value, path, indent, lines);
|
|
112
|
+
} else if (typeof value === 'object') {
|
|
113
|
+
renderObject(value as Record<string, unknown>, path, indent, lines);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Render object with path tracking
|
|
119
|
+
*/
|
|
120
|
+
function renderObject(
|
|
121
|
+
obj: Record<string, unknown>,
|
|
122
|
+
path: string,
|
|
123
|
+
indent: number,
|
|
124
|
+
lines: string[]
|
|
125
|
+
): void {
|
|
126
|
+
const indentStr = ' '.repeat(indent);
|
|
127
|
+
const keys = Object.keys(obj);
|
|
128
|
+
|
|
129
|
+
lines.push(
|
|
130
|
+
`<span class="json-line json-bracket" data-path="${escapeAttr(path)}">${indentStr}{</span>`
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
keys.forEach((key, idx) => {
|
|
134
|
+
const keyPath = `${path}/${escapeJsonPointer(key)}`;
|
|
135
|
+
const value = obj[key];
|
|
136
|
+
const comma = idx < keys.length - 1 ? ',' : '';
|
|
137
|
+
const keyIndent = ' '.repeat(indent + 1);
|
|
138
|
+
|
|
139
|
+
// For primitives, render key and value on same line
|
|
140
|
+
if (isPrimitive(value)) {
|
|
141
|
+
const valueHtml = renderPrimitiveInline(value);
|
|
142
|
+
lines.push(
|
|
143
|
+
`<span class="json-line" data-path="${escapeAttr(keyPath)}">${keyIndent}<span class="json-key">"${escapeHtml(key)}"</span>: ${valueHtml}${comma}</span>`
|
|
144
|
+
);
|
|
145
|
+
} else {
|
|
146
|
+
// For objects/arrays, key on its own line with nested content
|
|
147
|
+
lines.push(
|
|
148
|
+
`<span class="json-line json-key-line" data-path="${escapeAttr(keyPath)}">${keyIndent}<span class="json-key">"${escapeHtml(key)}"</span>:</span>`
|
|
149
|
+
);
|
|
150
|
+
renderValue(value, keyPath, indent + 1, lines);
|
|
151
|
+
// Add comma to last line if needed
|
|
152
|
+
if (comma && lines.length > 0) {
|
|
153
|
+
const lastIdx = lines.length - 1;
|
|
154
|
+
lines[lastIdx] = lines[lastIdx].replace(/<\/span>$/, `${comma}</span>`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
lines.push(
|
|
160
|
+
`<span class="json-line json-bracket" data-path="${escapeAttr(path)}">${indentStr}}</span>`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Render array with path tracking
|
|
166
|
+
*/
|
|
167
|
+
function renderArray(
|
|
168
|
+
arr: unknown[],
|
|
169
|
+
path: string,
|
|
170
|
+
indent: number,
|
|
171
|
+
lines: string[]
|
|
172
|
+
): void {
|
|
173
|
+
const indentStr = ' '.repeat(indent);
|
|
174
|
+
|
|
175
|
+
lines.push(
|
|
176
|
+
`<span class="json-line json-bracket" data-path="${escapeAttr(path)}">${indentStr}[</span>`
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
arr.forEach((item, idx) => {
|
|
180
|
+
const itemPath = `${path}/${idx}`;
|
|
181
|
+
const comma = idx < arr.length - 1 ? ',' : '';
|
|
182
|
+
|
|
183
|
+
if (isPrimitive(item)) {
|
|
184
|
+
const itemIndent = ' '.repeat(indent + 1);
|
|
185
|
+
const valueHtml = renderPrimitiveInline(item);
|
|
186
|
+
lines.push(
|
|
187
|
+
`<span class="json-line" data-path="${escapeAttr(itemPath)}">${itemIndent}${valueHtml}${comma}</span>`
|
|
188
|
+
);
|
|
189
|
+
} else {
|
|
190
|
+
renderValue(item, itemPath, indent + 1, lines);
|
|
191
|
+
if (comma && lines.length > 0) {
|
|
192
|
+
const lastIdx = lines.length - 1;
|
|
193
|
+
lines[lastIdx] = lines[lastIdx].replace(/<\/span>$/, `${comma}</span>`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
lines.push(
|
|
199
|
+
`<span class="json-line json-bracket" data-path="${escapeAttr(path)}">${indentStr}]</span>`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Render JSON as HTML with line-level data-path attributes
|
|
205
|
+
*
|
|
206
|
+
* @param json - The JSON object to render
|
|
207
|
+
* @param pathPrefix - Base path (e.g., "#" for root)
|
|
208
|
+
* @returns HTML string with span elements containing data-path
|
|
209
|
+
*/
|
|
210
|
+
export function renderJsonWithPaths(
|
|
211
|
+
json: unknown,
|
|
212
|
+
pathPrefix: string = '#'
|
|
213
|
+
): string {
|
|
214
|
+
if (json === null || json === undefined) {
|
|
215
|
+
return '<span class="json-line json-null" data-path="#">(no data)</span>';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const lines: string[] = [];
|
|
219
|
+
renderValue(json, pathPrefix, 0, lines);
|
|
220
|
+
// Join without newlines - each json-line span has display:block in CSS
|
|
221
|
+
// This avoids JavaScript syntax errors when HTML is embedded in string concatenation
|
|
222
|
+
return lines.join('');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ============================================================================
|
|
226
|
+
// Method Summary Registry
|
|
227
|
+
// ============================================================================
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Registry of method-specific summary handlers
|
|
231
|
+
*/
|
|
232
|
+
const methodHandlers: MethodSummaryHandler[] = [];
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Register a method-specific summary handler
|
|
236
|
+
*/
|
|
237
|
+
export function registerMethodHandler(handler: MethodSummaryHandler): void {
|
|
238
|
+
methodHandlers.push(handler);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Render method-specific summary (combined request + response)
|
|
243
|
+
*/
|
|
244
|
+
export function renderMethodSummary(
|
|
245
|
+
method: string,
|
|
246
|
+
request: unknown,
|
|
247
|
+
response: unknown
|
|
248
|
+
): SummaryRow[] {
|
|
249
|
+
for (const handler of methodHandlers) {
|
|
250
|
+
if (typeof handler.method === 'string' && handler.method === method) {
|
|
251
|
+
return handler.render(request, response);
|
|
252
|
+
}
|
|
253
|
+
if (handler.method instanceof RegExp && handler.method.test(method)) {
|
|
254
|
+
return handler.render(request, response);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// Default: show generic summary
|
|
258
|
+
return renderGenericSummary(method, request, response);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Render request-specific summary
|
|
263
|
+
*/
|
|
264
|
+
/**
|
|
265
|
+
* Extended method summary handler with separate request/response renderers
|
|
266
|
+
*/
|
|
267
|
+
interface MethodSummaryHandlerExtended extends MethodSummaryHandler {
|
|
268
|
+
renderRequest?: (request: unknown) => SummaryRow[];
|
|
269
|
+
renderResponse?: (response: unknown) => SummaryRow[];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function renderRequestSummary(
|
|
273
|
+
method: string,
|
|
274
|
+
request: unknown
|
|
275
|
+
): SummaryRow[] {
|
|
276
|
+
// Check for method-specific request handler
|
|
277
|
+
for (const handler of methodHandlers) {
|
|
278
|
+
const extended = handler as MethodSummaryHandlerExtended;
|
|
279
|
+
if (typeof handler.method === 'string' && handler.method === method) {
|
|
280
|
+
if (extended.renderRequest) {
|
|
281
|
+
return extended.renderRequest(request);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (handler.method instanceof RegExp && handler.method.test(method)) {
|
|
285
|
+
if (extended.renderRequest) {
|
|
286
|
+
return extended.renderRequest(request);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Default: show generic request summary
|
|
291
|
+
return renderGenericRequestSummary(method, request);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Render response-specific summary
|
|
296
|
+
*/
|
|
297
|
+
export function renderResponseSummary(
|
|
298
|
+
method: string,
|
|
299
|
+
response: unknown
|
|
300
|
+
): SummaryRow[] {
|
|
301
|
+
// Check for method-specific response handler
|
|
302
|
+
for (const handler of methodHandlers) {
|
|
303
|
+
const extended = handler as MethodSummaryHandlerExtended;
|
|
304
|
+
if (typeof handler.method === 'string' && handler.method === method) {
|
|
305
|
+
if (extended.renderResponse) {
|
|
306
|
+
return extended.renderResponse(response);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (handler.method instanceof RegExp && handler.method.test(method)) {
|
|
310
|
+
if (extended.renderResponse) {
|
|
311
|
+
return extended.renderResponse(response);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Default: show generic response summary
|
|
316
|
+
return renderGenericResponseSummary(method, response);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Generic summary for unknown methods - returns separate request and response summaries
|
|
321
|
+
*/
|
|
322
|
+
function renderGenericSummary(
|
|
323
|
+
method: string,
|
|
324
|
+
request: unknown,
|
|
325
|
+
response: unknown
|
|
326
|
+
): SummaryRow[] {
|
|
327
|
+
// This returns combined rows for backward compatibility
|
|
328
|
+
// New code should use renderGenericRequestSummary and renderGenericResponseSummary
|
|
329
|
+
const reqRows = renderGenericRequestSummary(method, request);
|
|
330
|
+
const resRows = renderGenericResponseSummary(method, response);
|
|
331
|
+
return [...reqRows, ...resRows];
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Render request-only summary
|
|
336
|
+
*/
|
|
337
|
+
export function renderGenericRequestSummary(
|
|
338
|
+
method: string,
|
|
339
|
+
request: unknown
|
|
340
|
+
): SummaryRow[] {
|
|
341
|
+
const rows: SummaryRow[] = [];
|
|
342
|
+
|
|
343
|
+
rows.push({
|
|
344
|
+
type: 'header',
|
|
345
|
+
label: `Method: ${method}`,
|
|
346
|
+
cssClass: 'summary-method-header',
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// Show request params if present
|
|
350
|
+
const req = request as { params?: Record<string, unknown> } | null;
|
|
351
|
+
if (req?.params && typeof req.params === 'object') {
|
|
352
|
+
const paramKeys = Object.keys(req.params);
|
|
353
|
+
if (paramKeys.length > 0) {
|
|
354
|
+
rows.push({
|
|
355
|
+
type: 'header',
|
|
356
|
+
label: 'Parameters',
|
|
357
|
+
cssClass: 'summary-section-header',
|
|
358
|
+
});
|
|
359
|
+
paramKeys.forEach((key) => {
|
|
360
|
+
rows.push({
|
|
361
|
+
type: 'property',
|
|
362
|
+
label: key,
|
|
363
|
+
value: summarizeValue(req.params![key]),
|
|
364
|
+
pointer: {
|
|
365
|
+
target: 'request',
|
|
366
|
+
path: `#/params/${escapeJsonPointer(key)}`,
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
} else {
|
|
372
|
+
rows.push({
|
|
373
|
+
type: 'property',
|
|
374
|
+
label: '(no parameters)',
|
|
375
|
+
cssClass: 'summary-empty',
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return rows;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Render response-only summary
|
|
384
|
+
*/
|
|
385
|
+
export function renderGenericResponseSummary(
|
|
386
|
+
method: string,
|
|
387
|
+
response: unknown
|
|
388
|
+
): SummaryRow[] {
|
|
389
|
+
const rows: SummaryRow[] = [];
|
|
390
|
+
|
|
391
|
+
rows.push({
|
|
392
|
+
type: 'header',
|
|
393
|
+
label: `Method: ${method}`,
|
|
394
|
+
cssClass: 'summary-method-header',
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
// Show response result summary
|
|
398
|
+
const res = response as { result?: unknown; error?: unknown } | null;
|
|
399
|
+
if (res?.result !== undefined) {
|
|
400
|
+
rows.push({
|
|
401
|
+
type: 'header',
|
|
402
|
+
label: 'Result',
|
|
403
|
+
cssClass: 'summary-section-header',
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// If result is an object, show its keys
|
|
407
|
+
if (res.result && typeof res.result === 'object' && !Array.isArray(res.result)) {
|
|
408
|
+
const resultObj = res.result as Record<string, unknown>;
|
|
409
|
+
Object.keys(resultObj).forEach((key) => {
|
|
410
|
+
rows.push({
|
|
411
|
+
type: 'property',
|
|
412
|
+
label: key,
|
|
413
|
+
value: summarizeValue(resultObj[key]),
|
|
414
|
+
pointer: {
|
|
415
|
+
target: 'response',
|
|
416
|
+
path: `#/result/${escapeJsonPointer(key)}`,
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
} else {
|
|
421
|
+
rows.push({
|
|
422
|
+
type: 'property',
|
|
423
|
+
label: 'result',
|
|
424
|
+
value: summarizeValue(res.result),
|
|
425
|
+
pointer: {
|
|
426
|
+
target: 'response',
|
|
427
|
+
path: '#/result',
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (res?.error !== undefined) {
|
|
434
|
+
rows.push({
|
|
435
|
+
type: 'header',
|
|
436
|
+
label: 'Error',
|
|
437
|
+
cssClass: 'summary-section-header summary-error',
|
|
438
|
+
});
|
|
439
|
+
const errorObj = res.error as Record<string, unknown> | null;
|
|
440
|
+
if (errorObj && typeof errorObj === 'object') {
|
|
441
|
+
Object.keys(errorObj).forEach((key) => {
|
|
442
|
+
rows.push({
|
|
443
|
+
type: 'property',
|
|
444
|
+
label: key,
|
|
445
|
+
value: summarizeValue(errorObj[key]),
|
|
446
|
+
pointer: {
|
|
447
|
+
target: 'response',
|
|
448
|
+
path: `#/error/${escapeJsonPointer(key)}`,
|
|
449
|
+
},
|
|
450
|
+
cssClass: 'summary-error-item',
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
} else {
|
|
454
|
+
rows.push({
|
|
455
|
+
type: 'property',
|
|
456
|
+
label: 'error',
|
|
457
|
+
value: summarizeValue(res.error),
|
|
458
|
+
pointer: {
|
|
459
|
+
target: 'response',
|
|
460
|
+
path: '#/error',
|
|
461
|
+
},
|
|
462
|
+
cssClass: 'summary-error-item',
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (res?.result === undefined && res?.error === undefined) {
|
|
468
|
+
rows.push({
|
|
469
|
+
type: 'property',
|
|
470
|
+
label: '(pending or no response)',
|
|
471
|
+
cssClass: 'summary-empty',
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return rows;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Summarize a value for display (truncate if needed)
|
|
480
|
+
*/
|
|
481
|
+
function summarizeValue(value: unknown): string {
|
|
482
|
+
if (value === null) return 'null';
|
|
483
|
+
if (value === undefined) return 'undefined';
|
|
484
|
+
if (typeof value === 'string') {
|
|
485
|
+
return value.length > 50 ? `"${value.slice(0, 47)}..."` : `"${value}"`;
|
|
486
|
+
}
|
|
487
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
488
|
+
return String(value);
|
|
489
|
+
}
|
|
490
|
+
if (Array.isArray(value)) {
|
|
491
|
+
return `Array(${value.length})`;
|
|
492
|
+
}
|
|
493
|
+
if (typeof value === 'object') {
|
|
494
|
+
const keys = Object.keys(value);
|
|
495
|
+
return `Object(${keys.length} keys)`;
|
|
496
|
+
}
|
|
497
|
+
return String(value);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// ============================================================================
|
|
501
|
+
// tools/list Summary Handler
|
|
502
|
+
// ============================================================================
|
|
503
|
+
|
|
504
|
+
interface ToolInfo {
|
|
505
|
+
name: string;
|
|
506
|
+
description?: string;
|
|
507
|
+
inputSchema?: {
|
|
508
|
+
type?: string;
|
|
509
|
+
properties?: Record<
|
|
510
|
+
string,
|
|
511
|
+
{
|
|
512
|
+
type?: string;
|
|
513
|
+
description?: string;
|
|
514
|
+
default?: unknown;
|
|
515
|
+
deprecated?: boolean;
|
|
516
|
+
}
|
|
517
|
+
>;
|
|
518
|
+
required?: string[];
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Register tools/list handler with separate request/response renderers
|
|
524
|
+
*/
|
|
525
|
+
registerMethodHandler({
|
|
526
|
+
method: 'tools/list',
|
|
527
|
+
render: (_request: unknown, response: unknown): SummaryRow[] => {
|
|
528
|
+
// Combined view - just show response (tools list)
|
|
529
|
+
return renderToolsListResponse(response);
|
|
530
|
+
},
|
|
531
|
+
renderRequest: (_request: unknown): SummaryRow[] => {
|
|
532
|
+
const rows: SummaryRow[] = [];
|
|
533
|
+
rows.push({
|
|
534
|
+
type: 'header',
|
|
535
|
+
label: 'Method: tools/list',
|
|
536
|
+
cssClass: 'summary-method-header',
|
|
537
|
+
});
|
|
538
|
+
rows.push({
|
|
539
|
+
type: 'property',
|
|
540
|
+
label: '(no parameters required)',
|
|
541
|
+
cssClass: 'summary-empty',
|
|
542
|
+
});
|
|
543
|
+
return rows;
|
|
544
|
+
},
|
|
545
|
+
renderResponse: (response: unknown): SummaryRow[] => {
|
|
546
|
+
return renderToolsListResponse(response);
|
|
547
|
+
},
|
|
548
|
+
} as MethodSummaryHandlerExtended);
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Render tools/list response summary
|
|
552
|
+
*/
|
|
553
|
+
function renderToolsListResponse(response: unknown): SummaryRow[] {
|
|
554
|
+
const rows: SummaryRow[] = [];
|
|
555
|
+
|
|
556
|
+
rows.push({
|
|
557
|
+
type: 'header',
|
|
558
|
+
label: 'Method: tools/list',
|
|
559
|
+
cssClass: 'summary-method-header',
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
// Extract tools from response.result.tools
|
|
563
|
+
const res = response as { result?: { tools?: ToolInfo[] } } | null;
|
|
564
|
+
const tools = res?.result?.tools ?? [];
|
|
565
|
+
|
|
566
|
+
if (tools.length === 0) {
|
|
567
|
+
rows.push({
|
|
568
|
+
type: 'property',
|
|
569
|
+
label: '(no tools available)',
|
|
570
|
+
cssClass: 'summary-empty',
|
|
571
|
+
});
|
|
572
|
+
return rows;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Table header with collapse controls (only if many tools)
|
|
576
|
+
const showCollapseControls = tools.length > 5;
|
|
577
|
+
rows.push({
|
|
578
|
+
type: 'header',
|
|
579
|
+
label: `Tools (${tools.length})`,
|
|
580
|
+
cssClass: `summary-table-header${showCollapseControls ? ' summary-collapsible-header' : ''}`,
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
// Tool rows
|
|
584
|
+
tools.forEach((tool, idx) => {
|
|
585
|
+
const toolRow: SummaryRow = {
|
|
586
|
+
type: 'item',
|
|
587
|
+
label: tool.name,
|
|
588
|
+
value: tool.description || '(no description)',
|
|
589
|
+
pointer: {
|
|
590
|
+
target: 'response',
|
|
591
|
+
path: `#/result/tools/${idx}`,
|
|
592
|
+
},
|
|
593
|
+
cssClass: 'summary-tool-row',
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
// Add inputSchema properties as children
|
|
597
|
+
if (tool.inputSchema?.properties) {
|
|
598
|
+
toolRow.children = renderInputSchemaRows(
|
|
599
|
+
tool.inputSchema,
|
|
600
|
+
`#/result/tools/${idx}/inputSchema`
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
rows.push(toolRow);
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
return rows;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Render inputSchema properties as summary rows
|
|
612
|
+
*/
|
|
613
|
+
function renderInputSchemaRows(
|
|
614
|
+
schema: NonNullable<ToolInfo['inputSchema']>,
|
|
615
|
+
basePath: string
|
|
616
|
+
): SummaryRow[] {
|
|
617
|
+
const rows: SummaryRow[] = [];
|
|
618
|
+
const props = schema.properties ?? {};
|
|
619
|
+
const required = new Set(schema.required ?? []);
|
|
620
|
+
|
|
621
|
+
Object.entries(props).forEach(([name, propSchema]) => {
|
|
622
|
+
const isRequired = required.has(name);
|
|
623
|
+
const isDeprecated = propSchema.deprecated === true;
|
|
624
|
+
const hasDefault = propSchema.default !== undefined;
|
|
625
|
+
const typeStr = propSchema.type ?? 'any';
|
|
626
|
+
|
|
627
|
+
// Build value string with badges
|
|
628
|
+
const badges: string[] = [];
|
|
629
|
+
if (isRequired) badges.push('required');
|
|
630
|
+
if (hasDefault) badges.push(`default: ${JSON.stringify(propSchema.default)}`);
|
|
631
|
+
if (isDeprecated) badges.push('deprecated');
|
|
632
|
+
|
|
633
|
+
const valueStr = badges.length > 0 ? `${typeStr} (${badges.join(', ')})` : typeStr;
|
|
634
|
+
|
|
635
|
+
// Build CSS class
|
|
636
|
+
const cssClasses: string[] = [];
|
|
637
|
+
if (isRequired) cssClasses.push('schema-required');
|
|
638
|
+
else cssClasses.push('schema-optional');
|
|
639
|
+
if (hasDefault) cssClasses.push('schema-has-default');
|
|
640
|
+
if (isDeprecated) cssClasses.push('schema-deprecated');
|
|
641
|
+
|
|
642
|
+
rows.push({
|
|
643
|
+
type: 'property',
|
|
644
|
+
label: name,
|
|
645
|
+
value: valueStr,
|
|
646
|
+
pointer: {
|
|
647
|
+
target: 'response',
|
|
648
|
+
path: `${basePath}/properties/${escapeJsonPointer(name)}`,
|
|
649
|
+
},
|
|
650
|
+
cssClass: cssClasses.join(' '),
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
return rows;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// ============================================================================
|
|
658
|
+
// initialize Summary Handler
|
|
659
|
+
// ============================================================================
|
|
660
|
+
|
|
661
|
+
interface InitializeResult {
|
|
662
|
+
protocolVersion?: string;
|
|
663
|
+
serverInfo?: {
|
|
664
|
+
name?: string;
|
|
665
|
+
version?: string;
|
|
666
|
+
};
|
|
667
|
+
capabilities?: Record<string, unknown>;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
interface InitializeRequest {
|
|
671
|
+
params?: {
|
|
672
|
+
protocolVersion?: string;
|
|
673
|
+
capabilities?: Record<string, unknown>;
|
|
674
|
+
clientInfo?: {
|
|
675
|
+
name?: string;
|
|
676
|
+
version?: string;
|
|
677
|
+
};
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Register initialize handler with separate request/response renderers
|
|
683
|
+
*/
|
|
684
|
+
registerMethodHandler({
|
|
685
|
+
method: 'initialize',
|
|
686
|
+
render: (request: unknown, response: unknown): SummaryRow[] => {
|
|
687
|
+
// Combined view (for backward compatibility)
|
|
688
|
+
const reqRows = renderInitializeRequest(request);
|
|
689
|
+
const resRows = renderInitializeResponse(response);
|
|
690
|
+
return [...reqRows, ...resRows];
|
|
691
|
+
},
|
|
692
|
+
renderRequest: (request: unknown): SummaryRow[] => {
|
|
693
|
+
return renderInitializeRequest(request);
|
|
694
|
+
},
|
|
695
|
+
renderResponse: (response: unknown): SummaryRow[] => {
|
|
696
|
+
return renderInitializeResponse(response);
|
|
697
|
+
},
|
|
698
|
+
} as MethodSummaryHandlerExtended);
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Render initialize request summary
|
|
702
|
+
*/
|
|
703
|
+
function renderInitializeRequest(request: unknown): SummaryRow[] {
|
|
704
|
+
const rows: SummaryRow[] = [];
|
|
705
|
+
const req = request as InitializeRequest | null;
|
|
706
|
+
|
|
707
|
+
rows.push({
|
|
708
|
+
type: 'header',
|
|
709
|
+
label: 'Method: initialize',
|
|
710
|
+
cssClass: 'summary-method-header',
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
const params = req?.params;
|
|
714
|
+
if (!params) {
|
|
715
|
+
rows.push({
|
|
716
|
+
type: 'property',
|
|
717
|
+
label: '(no parameters)',
|
|
718
|
+
cssClass: 'summary-empty',
|
|
719
|
+
});
|
|
720
|
+
return rows;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// Protocol Version
|
|
724
|
+
if (params.protocolVersion) {
|
|
725
|
+
rows.push({
|
|
726
|
+
type: 'header',
|
|
727
|
+
label: 'Protocol',
|
|
728
|
+
cssClass: 'summary-section-header',
|
|
729
|
+
});
|
|
730
|
+
rows.push({
|
|
731
|
+
type: 'property',
|
|
732
|
+
label: 'protocolVersion',
|
|
733
|
+
value: params.protocolVersion,
|
|
734
|
+
pointer: {
|
|
735
|
+
target: 'request',
|
|
736
|
+
path: '#/params/protocolVersion',
|
|
737
|
+
},
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// Client Info
|
|
742
|
+
if (params.clientInfo) {
|
|
743
|
+
rows.push({
|
|
744
|
+
type: 'header',
|
|
745
|
+
label: 'Client Info',
|
|
746
|
+
cssClass: 'summary-section-header',
|
|
747
|
+
});
|
|
748
|
+
if (params.clientInfo.name) {
|
|
749
|
+
rows.push({
|
|
750
|
+
type: 'property',
|
|
751
|
+
label: 'name',
|
|
752
|
+
value: params.clientInfo.name,
|
|
753
|
+
pointer: {
|
|
754
|
+
target: 'request',
|
|
755
|
+
path: '#/params/clientInfo/name',
|
|
756
|
+
},
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
if (params.clientInfo.version) {
|
|
760
|
+
rows.push({
|
|
761
|
+
type: 'property',
|
|
762
|
+
label: 'version',
|
|
763
|
+
value: params.clientInfo.version,
|
|
764
|
+
pointer: {
|
|
765
|
+
target: 'request',
|
|
766
|
+
path: '#/params/clientInfo/version',
|
|
767
|
+
},
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// Client Capabilities
|
|
773
|
+
if (params.capabilities) {
|
|
774
|
+
rows.push({
|
|
775
|
+
type: 'header',
|
|
776
|
+
label: 'Client Capabilities',
|
|
777
|
+
cssClass: 'summary-section-header',
|
|
778
|
+
});
|
|
779
|
+
renderCapabilitiesRows(params.capabilities, '#/params/capabilities', 'request', rows);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return rows;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Render initialize response summary
|
|
787
|
+
*/
|
|
788
|
+
function renderInitializeResponse(response: unknown): SummaryRow[] {
|
|
789
|
+
const rows: SummaryRow[] = [];
|
|
790
|
+
const res = response as { result?: InitializeResult; error?: unknown } | null;
|
|
791
|
+
|
|
792
|
+
rows.push({
|
|
793
|
+
type: 'header',
|
|
794
|
+
label: 'Method: initialize',
|
|
795
|
+
cssClass: 'summary-method-header',
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
if (res?.error) {
|
|
799
|
+
rows.push({
|
|
800
|
+
type: 'header',
|
|
801
|
+
label: 'Error',
|
|
802
|
+
cssClass: 'summary-section-header summary-error',
|
|
803
|
+
});
|
|
804
|
+
rows.push({
|
|
805
|
+
type: 'property',
|
|
806
|
+
label: 'error',
|
|
807
|
+
value: summarizeValue(res.error),
|
|
808
|
+
pointer: {
|
|
809
|
+
target: 'response',
|
|
810
|
+
path: '#/error',
|
|
811
|
+
},
|
|
812
|
+
cssClass: 'summary-error-item',
|
|
813
|
+
});
|
|
814
|
+
return rows;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
const result = res?.result;
|
|
818
|
+
if (!result) {
|
|
819
|
+
rows.push({
|
|
820
|
+
type: 'property',
|
|
821
|
+
label: '(pending or no response)',
|
|
822
|
+
cssClass: 'summary-empty',
|
|
823
|
+
});
|
|
824
|
+
return rows;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// Protocol Version
|
|
828
|
+
if (result.protocolVersion) {
|
|
829
|
+
rows.push({
|
|
830
|
+
type: 'header',
|
|
831
|
+
label: 'Protocol',
|
|
832
|
+
cssClass: 'summary-section-header',
|
|
833
|
+
});
|
|
834
|
+
rows.push({
|
|
835
|
+
type: 'property',
|
|
836
|
+
label: 'protocolVersion',
|
|
837
|
+
value: result.protocolVersion,
|
|
838
|
+
pointer: {
|
|
839
|
+
target: 'response',
|
|
840
|
+
path: '#/result/protocolVersion',
|
|
841
|
+
},
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Server Info
|
|
846
|
+
if (result.serverInfo) {
|
|
847
|
+
rows.push({
|
|
848
|
+
type: 'header',
|
|
849
|
+
label: 'Server Info',
|
|
850
|
+
cssClass: 'summary-section-header',
|
|
851
|
+
});
|
|
852
|
+
if (result.serverInfo.name) {
|
|
853
|
+
rows.push({
|
|
854
|
+
type: 'property',
|
|
855
|
+
label: 'name',
|
|
856
|
+
value: result.serverInfo.name,
|
|
857
|
+
pointer: {
|
|
858
|
+
target: 'response',
|
|
859
|
+
path: '#/result/serverInfo/name',
|
|
860
|
+
},
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
if (result.serverInfo.version) {
|
|
864
|
+
rows.push({
|
|
865
|
+
type: 'property',
|
|
866
|
+
label: 'version',
|
|
867
|
+
value: result.serverInfo.version,
|
|
868
|
+
pointer: {
|
|
869
|
+
target: 'response',
|
|
870
|
+
path: '#/result/serverInfo/version',
|
|
871
|
+
},
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// Server Capabilities
|
|
877
|
+
if (result.capabilities) {
|
|
878
|
+
rows.push({
|
|
879
|
+
type: 'header',
|
|
880
|
+
label: 'Server Capabilities',
|
|
881
|
+
cssClass: 'summary-section-header',
|
|
882
|
+
});
|
|
883
|
+
renderCapabilitiesRows(result.capabilities, '#/result/capabilities', 'response', rows);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
return rows;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* Render capabilities object as summary rows
|
|
891
|
+
*/
|
|
892
|
+
function renderCapabilitiesRows(
|
|
893
|
+
capabilities: Record<string, unknown>,
|
|
894
|
+
basePath: string,
|
|
895
|
+
target: 'request' | 'response',
|
|
896
|
+
rows: SummaryRow[]
|
|
897
|
+
): void {
|
|
898
|
+
Object.entries(capabilities).forEach(([key, value]) => {
|
|
899
|
+
const path = `${basePath}/${escapeJsonPointer(key)}`;
|
|
900
|
+
|
|
901
|
+
// Determine if capability is enabled
|
|
902
|
+
let displayValue: string;
|
|
903
|
+
let cssClass = '';
|
|
904
|
+
|
|
905
|
+
if (value === undefined || value === null) {
|
|
906
|
+
displayValue = 'disabled';
|
|
907
|
+
cssClass = 'capability-disabled';
|
|
908
|
+
} else if (typeof value === 'boolean') {
|
|
909
|
+
displayValue = value ? 'enabled' : 'disabled';
|
|
910
|
+
cssClass = value ? 'capability-enabled' : 'capability-disabled';
|
|
911
|
+
} else if (typeof value === 'object') {
|
|
912
|
+
// Has options - show as enabled with details
|
|
913
|
+
const optionCount = Object.keys(value as object).length;
|
|
914
|
+
displayValue = optionCount > 0 ? `enabled (${optionCount} options)` : 'enabled';
|
|
915
|
+
cssClass = 'capability-enabled';
|
|
916
|
+
} else {
|
|
917
|
+
displayValue = String(value);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
rows.push({
|
|
921
|
+
type: 'property',
|
|
922
|
+
label: key,
|
|
923
|
+
value: displayValue,
|
|
924
|
+
pointer: {
|
|
925
|
+
target,
|
|
926
|
+
path,
|
|
927
|
+
},
|
|
928
|
+
cssClass,
|
|
929
|
+
});
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// ============================================================================
|
|
934
|
+
// Summary Row HTML Renderer
|
|
935
|
+
// ============================================================================
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Render summary rows to HTML
|
|
939
|
+
* Note: Join without newlines to avoid JS string syntax errors when embedded in templates
|
|
940
|
+
*/
|
|
941
|
+
export function renderSummaryRowsHtml(rows: SummaryRow[]): string {
|
|
942
|
+
const html: string[] = [];
|
|
943
|
+
|
|
944
|
+
for (const row of rows) {
|
|
945
|
+
html.push(renderSummaryRow(row));
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
return html.join('');
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/**
|
|
952
|
+
* Render a single summary row
|
|
953
|
+
*/
|
|
954
|
+
function renderSummaryRow(row: SummaryRow): string {
|
|
955
|
+
const cssClass = row.cssClass || '';
|
|
956
|
+
const pointerAttrs = row.pointer
|
|
957
|
+
? ` data-pointer-target="${row.pointer.target}" data-pointer-path="${escapeAttr(row.pointer.path)}"`
|
|
958
|
+
: '';
|
|
959
|
+
const clickable = row.pointer ? ' clickable' : '';
|
|
960
|
+
// Add title attribute for tooltip on truncated values
|
|
961
|
+
const valueTitle = row.value ? ` title="${escapeAttr(row.value)}"` : '';
|
|
962
|
+
|
|
963
|
+
if (row.type === 'header') {
|
|
964
|
+
// Add collapse controls if header has collapsible class
|
|
965
|
+
if (cssClass.includes('summary-collapsible-header')) {
|
|
966
|
+
// Single line to avoid JS string syntax issues when embedded in templates
|
|
967
|
+
return `<div class="summary-row summary-header ${cssClass}"><span>${escapeHtml(row.label)}</span><span class="collapse-controls"><button class="collapse-btn collapse-all" title="Collapse all">−</button><button class="collapse-btn expand-all" title="Expand all">+</button></span></div>`;
|
|
968
|
+
}
|
|
969
|
+
return `<div class="summary-row summary-header ${cssClass}">${escapeHtml(row.label)}</div>`;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
if (row.type === 'item') {
|
|
973
|
+
const hasChildren = row.children && row.children.length > 0;
|
|
974
|
+
let childHtml = '';
|
|
975
|
+
let toggleHtml = '';
|
|
976
|
+
|
|
977
|
+
if (hasChildren) {
|
|
978
|
+
// Join without newlines to avoid JS string syntax errors
|
|
979
|
+
childHtml = `<div class="summary-children">${row.children!.map((c) => renderSummaryRow(c)).join('')}</div>`;
|
|
980
|
+
toggleHtml = '<span class="item-toggle" title="Toggle properties">▼</span>';
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// Single line to avoid JS string syntax issues
|
|
984
|
+
return `<div class="summary-row summary-item ${cssClass}${clickable}${hasChildren ? ' has-children' : ''}"${pointerAttrs}>${toggleHtml}<span class="summary-label">${escapeHtml(row.label)}</span><span class="summary-value"${valueTitle}>${escapeHtml(row.value || '')}</span></div>${childHtml}`;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// property type - single line to avoid JS string syntax issues
|
|
988
|
+
return `<div class="summary-row summary-property ${cssClass}${clickable}"${pointerAttrs}><span class="summary-prop-name">${escapeHtml(row.label)}</span><span class="summary-prop-value"${valueTitle}>${escapeHtml(row.value || '')}</span></div>`;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// ============================================================================
|
|
992
|
+
// Sensitive Key Detection (Phase 12.x-c)
|
|
993
|
+
// ============================================================================
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* Patterns for detecting sensitive keys in JSON data
|
|
997
|
+
* These patterns match common names for authentication, secrets, and credentials
|
|
998
|
+
*/
|
|
999
|
+
const SENSITIVE_PATTERNS: RegExp[] = [
|
|
1000
|
+
/authorization/i,
|
|
1001
|
+
/api[_-]?key/i,
|
|
1002
|
+
/token/i,
|
|
1003
|
+
/secret/i,
|
|
1004
|
+
/password/i,
|
|
1005
|
+
/private[_-]?key/i,
|
|
1006
|
+
/bearer/i,
|
|
1007
|
+
/credential/i,
|
|
1008
|
+
/signature/i,
|
|
1009
|
+
/access[_-]?token/i,
|
|
1010
|
+
/refresh[_-]?token/i,
|
|
1011
|
+
/session[_-]?id/i,
|
|
1012
|
+
/cookie/i,
|
|
1013
|
+
/^auth$/i, // Exact match for 'auth' key (avoids 'author', 'authorized_users')
|
|
1014
|
+
/^auth_/i, // Prefix match for auth_token, auth_header, etc.
|
|
1015
|
+
/client[_-]?secret/i,
|
|
1016
|
+
/jwt/i,
|
|
1017
|
+
/oauth/i,
|
|
1018
|
+
/x-api-key/i,
|
|
1019
|
+
/x-auth/i,
|
|
1020
|
+
];
|
|
1021
|
+
|
|
1022
|
+
/**
|
|
1023
|
+
* Detect sensitive keys in JSON data
|
|
1024
|
+
* Walks the object tree and returns paths to keys matching sensitive patterns
|
|
1025
|
+
*
|
|
1026
|
+
* @param json - The JSON object to scan
|
|
1027
|
+
* @returns Array of paths to sensitive keys (e.g., "headers.authorization")
|
|
1028
|
+
*/
|
|
1029
|
+
export function detectSensitiveKeys(json: unknown): string[] {
|
|
1030
|
+
const found: string[] = [];
|
|
1031
|
+
|
|
1032
|
+
function walk(obj: unknown, path: string = ''): void {
|
|
1033
|
+
if (!obj || typeof obj !== 'object') return;
|
|
1034
|
+
|
|
1035
|
+
if (Array.isArray(obj)) {
|
|
1036
|
+
obj.forEach((v, i) => walk(v, path ? `${path}[${i}]` : `[${i}]`));
|
|
1037
|
+
} else {
|
|
1038
|
+
Object.entries(obj as Record<string, unknown>).forEach(([k, v]) => {
|
|
1039
|
+
const currentPath = path ? `${path}.${k}` : k;
|
|
1040
|
+
if (SENSITIVE_PATTERNS.some((pat) => pat.test(k))) {
|
|
1041
|
+
found.push(currentPath);
|
|
1042
|
+
}
|
|
1043
|
+
walk(v, currentPath);
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
walk(json);
|
|
1049
|
+
return found;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* Check if JSON contains any sensitive keys
|
|
1054
|
+
*
|
|
1055
|
+
* @param json - The JSON object to check
|
|
1056
|
+
* @returns true if sensitive keys are detected
|
|
1057
|
+
*/
|
|
1058
|
+
export function hasSensitiveContent(json: unknown): boolean {
|
|
1059
|
+
return detectSensitiveKeys(json).length > 0;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// ============================================================================
|
|
1063
|
+
// CSS Styles for RPC Inspector
|
|
1064
|
+
// ============================================================================
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* Get RPC Inspector CSS styles
|
|
1068
|
+
*/
|
|
1069
|
+
export function getRpcInspectorStyles(): string {
|
|
1070
|
+
return `
|
|
1071
|
+
/* RPC Info horizontal layout */
|
|
1072
|
+
.rpc-info-grid {
|
|
1073
|
+
display: flex;
|
|
1074
|
+
flex-wrap: wrap;
|
|
1075
|
+
gap: 16px 32px;
|
|
1076
|
+
align-items: center;
|
|
1077
|
+
margin-bottom: 16px;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
.rpc-info-item {
|
|
1081
|
+
display: flex;
|
|
1082
|
+
align-items: center;
|
|
1083
|
+
gap: 8px;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
.rpc-info-item dt {
|
|
1087
|
+
font-size: 12px;
|
|
1088
|
+
color: var(--text-secondary);
|
|
1089
|
+
font-weight: 400;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
.rpc-info-item dd {
|
|
1093
|
+
margin: 0;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
/* Right pane layout - RPC Info header fixed, inspector scrollable */
|
|
1097
|
+
.right-pane {
|
|
1098
|
+
display: flex;
|
|
1099
|
+
flex-direction: column;
|
|
1100
|
+
overflow: hidden;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/* Container for pre-rendered RPC details */
|
|
1104
|
+
.rpc-details-container {
|
|
1105
|
+
flex: 1;
|
|
1106
|
+
position: relative;
|
|
1107
|
+
min-height: 0;
|
|
1108
|
+
overflow: hidden;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/* Individual RPC detail wrapper - absolute positioned */
|
|
1112
|
+
.rpc-detail-content {
|
|
1113
|
+
position: absolute;
|
|
1114
|
+
top: 0;
|
|
1115
|
+
left: 0;
|
|
1116
|
+
right: 0;
|
|
1117
|
+
bottom: 0;
|
|
1118
|
+
display: flex;
|
|
1119
|
+
flex-direction: column;
|
|
1120
|
+
overflow: hidden;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/* RPC Info header section - fixed at top */
|
|
1124
|
+
.rpc-detail-content > .detail-section:first-child {
|
|
1125
|
+
flex-shrink: 0;
|
|
1126
|
+
overflow: visible;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
/* Inspector section - takes remaining space */
|
|
1130
|
+
.rpc-detail-content > .detail-section:last-child {
|
|
1131
|
+
flex: 1;
|
|
1132
|
+
display: flex;
|
|
1133
|
+
flex-direction: column;
|
|
1134
|
+
min-height: 0;
|
|
1135
|
+
overflow: hidden;
|
|
1136
|
+
margin-bottom: 0;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/* 2-column RPC detail layout - each column scrolls independently */
|
|
1140
|
+
.rpc-inspector {
|
|
1141
|
+
display: flex;
|
|
1142
|
+
gap: 16px;
|
|
1143
|
+
flex: 1;
|
|
1144
|
+
min-height: 0;
|
|
1145
|
+
overflow: hidden;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/* Summary pane - independent vertical scroll */
|
|
1149
|
+
.rpc-inspector-summary {
|
|
1150
|
+
flex: 0 0 320px;
|
|
1151
|
+
max-width: 400px;
|
|
1152
|
+
overflow-y: auto;
|
|
1153
|
+
overflow-x: hidden;
|
|
1154
|
+
border-right: 1px solid var(--border-color);
|
|
1155
|
+
padding-right: 16px;
|
|
1156
|
+
overscroll-behavior: contain;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
.rpc-inspector-summary h3 {
|
|
1160
|
+
position: sticky;
|
|
1161
|
+
top: 0;
|
|
1162
|
+
background: var(--bg-secondary);
|
|
1163
|
+
padding: 8px 0;
|
|
1164
|
+
margin: 0 0 8px 0;
|
|
1165
|
+
z-index: 1;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/* RAW pane - independent vertical scroll */
|
|
1169
|
+
.rpc-inspector-raw {
|
|
1170
|
+
flex: 1;
|
|
1171
|
+
min-width: 0;
|
|
1172
|
+
display: flex;
|
|
1173
|
+
flex-direction: column;
|
|
1174
|
+
min-height: 0;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/* Toggle buttons */
|
|
1178
|
+
.rpc-toggle-bar {
|
|
1179
|
+
display: flex;
|
|
1180
|
+
gap: 8px;
|
|
1181
|
+
margin-bottom: 12px;
|
|
1182
|
+
flex-shrink: 0;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
.rpc-toggle-btn {
|
|
1186
|
+
padding: 6px 16px;
|
|
1187
|
+
background: var(--bg-secondary);
|
|
1188
|
+
border: 1px solid var(--border-color);
|
|
1189
|
+
border-radius: 4px;
|
|
1190
|
+
color: var(--text-secondary);
|
|
1191
|
+
cursor: pointer;
|
|
1192
|
+
font-size: 12px;
|
|
1193
|
+
font-weight: 500;
|
|
1194
|
+
transition: all 0.15s;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
.rpc-toggle-btn:hover {
|
|
1198
|
+
border-color: var(--accent-blue);
|
|
1199
|
+
color: var(--text-primary);
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
.rpc-toggle-btn.active {
|
|
1203
|
+
background: rgba(0, 212, 255, 0.15);
|
|
1204
|
+
border-color: var(--accent-blue);
|
|
1205
|
+
color: var(--accent-blue);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
/* Raw JSON container - independent scroll */
|
|
1209
|
+
.rpc-raw-json {
|
|
1210
|
+
flex: 1;
|
|
1211
|
+
min-height: 0;
|
|
1212
|
+
overflow-y: auto;
|
|
1213
|
+
overflow-x: auto;
|
|
1214
|
+
overscroll-behavior: contain;
|
|
1215
|
+
background: var(--bg-primary);
|
|
1216
|
+
border: 1px solid var(--border-color);
|
|
1217
|
+
border-radius: 6px;
|
|
1218
|
+
padding: 12px;
|
|
1219
|
+
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
1220
|
+
font-size: 12px;
|
|
1221
|
+
line-height: 1.6;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
/* JSON line styling */
|
|
1225
|
+
.json-line {
|
|
1226
|
+
display: block;
|
|
1227
|
+
white-space: pre;
|
|
1228
|
+
padding: 1px 4px;
|
|
1229
|
+
border-radius: 2px;
|
|
1230
|
+
transition: background-color 0.15s;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
.json-line:hover {
|
|
1234
|
+
background: rgba(0, 212, 255, 0.05);
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
.json-line.highlighted {
|
|
1238
|
+
background: rgba(0, 212, 255, 0.2);
|
|
1239
|
+
outline: 1px solid var(--accent-blue);
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
/* JSON syntax highlighting */
|
|
1243
|
+
.json-key { color: #79c0ff; }
|
|
1244
|
+
.json-string { color: #a5d6ff; }
|
|
1245
|
+
.json-number { color: #ffa657; }
|
|
1246
|
+
.json-bool { color: #ff7b72; }
|
|
1247
|
+
.json-null { color: var(--text-secondary); }
|
|
1248
|
+
.json-bracket { color: var(--text-secondary); }
|
|
1249
|
+
|
|
1250
|
+
/* Summary view styles */
|
|
1251
|
+
.summary-row {
|
|
1252
|
+
padding: 4px 0;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
.summary-header {
|
|
1256
|
+
font-weight: 600;
|
|
1257
|
+
font-size: 14px;
|
|
1258
|
+
color: var(--text-primary);
|
|
1259
|
+
padding: 8px 0 4px 0;
|
|
1260
|
+
border-bottom: 1px solid var(--border-color);
|
|
1261
|
+
margin-bottom: 8px;
|
|
1262
|
+
margin-top: 12px;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
.summary-header:first-child {
|
|
1266
|
+
margin-top: 0;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
.summary-table-header {
|
|
1270
|
+
font-size: 13px;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
.summary-item {
|
|
1274
|
+
display: flex;
|
|
1275
|
+
justify-content: space-between;
|
|
1276
|
+
align-items: flex-start;
|
|
1277
|
+
padding: 8px 12px;
|
|
1278
|
+
margin: 4px 0;
|
|
1279
|
+
background: var(--bg-secondary);
|
|
1280
|
+
border: 1px solid transparent;
|
|
1281
|
+
border-radius: 4px;
|
|
1282
|
+
transition: border-color 0.15s, background 0.15s;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
.summary-item.clickable {
|
|
1286
|
+
cursor: pointer;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
.summary-item.clickable:hover {
|
|
1290
|
+
border-color: var(--accent-blue);
|
|
1291
|
+
background: rgba(0, 212, 255, 0.05);
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
.summary-item.selected {
|
|
1295
|
+
border-color: var(--accent-blue);
|
|
1296
|
+
background: rgba(0, 212, 255, 0.1);
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
.summary-label {
|
|
1300
|
+
font-family: 'SFMono-Regular', Consolas, monospace;
|
|
1301
|
+
font-size: 13px;
|
|
1302
|
+
color: var(--accent-blue);
|
|
1303
|
+
font-weight: 500;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
.summary-value {
|
|
1307
|
+
font-size: 12px;
|
|
1308
|
+
color: var(--text-secondary);
|
|
1309
|
+
max-width: 55%;
|
|
1310
|
+
overflow: hidden;
|
|
1311
|
+
text-overflow: ellipsis;
|
|
1312
|
+
white-space: nowrap;
|
|
1313
|
+
text-align: right;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
/* Schema properties (children) */
|
|
1317
|
+
.summary-children {
|
|
1318
|
+
margin-left: 16px;
|
|
1319
|
+
padding-left: 12px;
|
|
1320
|
+
border-left: 2px solid var(--border-color);
|
|
1321
|
+
margin-top: 4px;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
.summary-property {
|
|
1325
|
+
display: flex;
|
|
1326
|
+
gap: 8px;
|
|
1327
|
+
padding: 4px 8px;
|
|
1328
|
+
margin: 2px 0;
|
|
1329
|
+
font-size: 12px;
|
|
1330
|
+
border-radius: 3px;
|
|
1331
|
+
transition: background 0.15s;
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
.summary-property.clickable {
|
|
1335
|
+
cursor: pointer;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
.summary-property.clickable:hover {
|
|
1339
|
+
background: rgba(0, 212, 255, 0.05);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
.summary-prop-name {
|
|
1343
|
+
font-family: 'SFMono-Regular', Consolas, monospace;
|
|
1344
|
+
color: var(--text-primary);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
.schema-required .summary-prop-name::after {
|
|
1348
|
+
content: '*';
|
|
1349
|
+
color: #f85149;
|
|
1350
|
+
margin-left: 2px;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
.schema-required .summary-prop-name {
|
|
1354
|
+
font-weight: 600;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
.schema-has-default .summary-prop-value::before {
|
|
1358
|
+
content: '=';
|
|
1359
|
+
color: #7ee787;
|
|
1360
|
+
margin-right: 4px;
|
|
1361
|
+
font-weight: 500;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
.schema-deprecated {
|
|
1365
|
+
opacity: 0.6;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
.schema-deprecated .summary-prop-name {
|
|
1369
|
+
text-decoration: line-through;
|
|
1370
|
+
color: #f0883e;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
.schema-deprecated .summary-prop-value::after {
|
|
1374
|
+
content: 'deprecated';
|
|
1375
|
+
background: rgba(240, 136, 62, 0.2);
|
|
1376
|
+
color: #f0883e;
|
|
1377
|
+
font-size: 10px;
|
|
1378
|
+
padding: 1px 4px;
|
|
1379
|
+
border-radius: 3px;
|
|
1380
|
+
margin-left: 6px;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
.summary-prop-value {
|
|
1384
|
+
color: var(--text-secondary);
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
/* Collapse controls */
|
|
1388
|
+
.summary-collapsible-header {
|
|
1389
|
+
display: flex;
|
|
1390
|
+
justify-content: space-between;
|
|
1391
|
+
align-items: center;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
.collapse-controls {
|
|
1395
|
+
display: flex;
|
|
1396
|
+
gap: 4px;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
.collapse-btn {
|
|
1400
|
+
width: 20px;
|
|
1401
|
+
height: 20px;
|
|
1402
|
+
border: 1px solid var(--border-color);
|
|
1403
|
+
border-radius: 3px;
|
|
1404
|
+
background: var(--bg-secondary);
|
|
1405
|
+
color: var(--text-secondary);
|
|
1406
|
+
cursor: pointer;
|
|
1407
|
+
font-size: 12px;
|
|
1408
|
+
line-height: 1;
|
|
1409
|
+
display: flex;
|
|
1410
|
+
align-items: center;
|
|
1411
|
+
justify-content: center;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
.collapse-btn:hover {
|
|
1415
|
+
border-color: var(--accent-blue);
|
|
1416
|
+
color: var(--accent-blue);
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
/* Item toggle */
|
|
1420
|
+
.item-toggle {
|
|
1421
|
+
cursor: pointer;
|
|
1422
|
+
color: var(--text-secondary);
|
|
1423
|
+
font-size: 10px;
|
|
1424
|
+
margin-right: 8px;
|
|
1425
|
+
transition: transform 0.15s;
|
|
1426
|
+
user-select: none;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
.summary-item.collapsed .item-toggle {
|
|
1430
|
+
transform: rotate(-90deg);
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
.summary-item.collapsed + .summary-children {
|
|
1434
|
+
display: none;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
/* Error styling */
|
|
1438
|
+
.summary-error {
|
|
1439
|
+
color: #f85149;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
.summary-error-item {
|
|
1443
|
+
border-left: 3px solid #f85149;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
/* Empty state styling */
|
|
1447
|
+
.summary-empty {
|
|
1448
|
+
color: var(--text-secondary);
|
|
1449
|
+
font-style: italic;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
/* Capability styling (for initialize method) */
|
|
1453
|
+
.capability-enabled {
|
|
1454
|
+
color: var(--accent-blue);
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
.capability-enabled .summary-prop-value {
|
|
1458
|
+
color: #3fb950;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
.capability-disabled {
|
|
1462
|
+
color: var(--text-secondary);
|
|
1463
|
+
opacity: 0.6;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
/* No-JS fallback */
|
|
1467
|
+
noscript + .rpc-toggle-bar {
|
|
1468
|
+
display: none;
|
|
1469
|
+
}
|
|
1470
|
+
`;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
// ============================================================================
|
|
1474
|
+
// JavaScript for RPC Inspector Interaction
|
|
1475
|
+
// ============================================================================
|
|
1476
|
+
|
|
1477
|
+
/**
|
|
1478
|
+
* Get RPC Inspector JavaScript
|
|
1479
|
+
* Updated to work with both ID-based (single RPC) and class-based (pre-rendered) DOM structures
|
|
1480
|
+
*/
|
|
1481
|
+
export function getRpcInspectorScript(): string {
|
|
1482
|
+
return `
|
|
1483
|
+
// RPC Inspector - Toggle and Navigation
|
|
1484
|
+
(function() {
|
|
1485
|
+
let currentTarget = 'request';
|
|
1486
|
+
|
|
1487
|
+
// Get the currently visible RPC detail container
|
|
1488
|
+
function getVisibleDetail() {
|
|
1489
|
+
// For pre-rendered multiple RPC details (Connector page)
|
|
1490
|
+
const visibleDetail = document.querySelector('.rpc-detail-content[style*="display: block"]');
|
|
1491
|
+
if (visibleDetail) return visibleDetail;
|
|
1492
|
+
// Fallback for single RPC view
|
|
1493
|
+
return document;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
// Initialize toggle buttons
|
|
1497
|
+
function initInspectorToggle() {
|
|
1498
|
+
// ID-based (legacy single-RPC view)
|
|
1499
|
+
const reqBtnById = document.getElementById('toggle-req');
|
|
1500
|
+
const resBtnById = document.getElementById('toggle-res');
|
|
1501
|
+
if (reqBtnById) {
|
|
1502
|
+
reqBtnById.addEventListener('click', function() { switchTarget('request'); });
|
|
1503
|
+
}
|
|
1504
|
+
if (resBtnById) {
|
|
1505
|
+
resBtnById.addEventListener('click', function() { switchTarget('response'); });
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
// Class-based with data-target (pre-rendered multi-RPC view)
|
|
1509
|
+
document.querySelectorAll('.rpc-toggle-btn[data-target]').forEach(function(btn) {
|
|
1510
|
+
btn.addEventListener('click', function() {
|
|
1511
|
+
const target = btn.dataset.target;
|
|
1512
|
+
const container = btn.closest('.rpc-detail-content') || document;
|
|
1513
|
+
switchTargetInContainer(target, container);
|
|
1514
|
+
});
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
// Switch between request and response view (both Summary and Raw JSON) - global
|
|
1519
|
+
function switchTarget(target) {
|
|
1520
|
+
currentTarget = target;
|
|
1521
|
+
|
|
1522
|
+
// Update button states (ID-based)
|
|
1523
|
+
const reqBtn = document.getElementById('toggle-req');
|
|
1524
|
+
const resBtn = document.getElementById('toggle-res');
|
|
1525
|
+
if (reqBtn) reqBtn.classList.toggle('active', target === 'request');
|
|
1526
|
+
if (resBtn) resBtn.classList.toggle('active', target === 'response');
|
|
1527
|
+
|
|
1528
|
+
// Update Summary display (ID-based)
|
|
1529
|
+
const reqSummary = document.getElementById('summary-request');
|
|
1530
|
+
const resSummary = document.getElementById('summary-response');
|
|
1531
|
+
if (reqSummary) reqSummary.style.display = target === 'request' ? 'block' : 'none';
|
|
1532
|
+
if (resSummary) resSummary.style.display = target === 'response' ? 'block' : 'none';
|
|
1533
|
+
|
|
1534
|
+
// Update raw JSON display (ID-based)
|
|
1535
|
+
const reqJson = document.getElementById('raw-json-request');
|
|
1536
|
+
const resJson = document.getElementById('raw-json-response');
|
|
1537
|
+
if (reqJson) reqJson.style.display = target === 'request' ? 'block' : 'none';
|
|
1538
|
+
if (resJson) resJson.style.display = target === 'response' ? 'block' : 'none';
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// Switch target within a specific container (for pre-rendered multi-RPC)
|
|
1542
|
+
function switchTargetInContainer(target, container) {
|
|
1543
|
+
currentTarget = target;
|
|
1544
|
+
|
|
1545
|
+
// Update button states within container
|
|
1546
|
+
container.querySelectorAll('.rpc-toggle-btn[data-target]').forEach(function(btn) {
|
|
1547
|
+
btn.classList.toggle('active', btn.dataset.target === target);
|
|
1548
|
+
});
|
|
1549
|
+
|
|
1550
|
+
// Update Summary display (class-based)
|
|
1551
|
+
const reqSummary = container.querySelector('.summary-request');
|
|
1552
|
+
const resSummary = container.querySelector('.summary-response');
|
|
1553
|
+
if (reqSummary) reqSummary.style.display = target === 'request' ? 'block' : 'none';
|
|
1554
|
+
if (resSummary) resSummary.style.display = target === 'response' ? 'block' : 'none';
|
|
1555
|
+
|
|
1556
|
+
// Update raw JSON display (class-based)
|
|
1557
|
+
const reqJson = container.querySelector('.raw-json-request');
|
|
1558
|
+
const resJson = container.querySelector('.raw-json-response');
|
|
1559
|
+
if (reqJson) reqJson.style.display = target === 'request' ? 'block' : 'none';
|
|
1560
|
+
if (resJson) resJson.style.display = target === 'response' ? 'block' : 'none';
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
// Navigate to JSON path and highlight
|
|
1564
|
+
function navigateToPath(target, path, container) {
|
|
1565
|
+
container = container || getVisibleDetail();
|
|
1566
|
+
|
|
1567
|
+
// Switch to correct target first
|
|
1568
|
+
if (container === document) {
|
|
1569
|
+
switchTarget(target);
|
|
1570
|
+
} else {
|
|
1571
|
+
switchTargetInContainer(target, container);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// Find container for this target (class-based or ID-based)
|
|
1575
|
+
var jsonContainer = container.querySelector('.raw-json-' + target) || document.getElementById('raw-json-' + target);
|
|
1576
|
+
if (!jsonContainer) return;
|
|
1577
|
+
|
|
1578
|
+
// Clear previous highlights
|
|
1579
|
+
jsonContainer.querySelectorAll('.highlighted').forEach(function(el) {
|
|
1580
|
+
el.classList.remove('highlighted');
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
// Find and highlight the target line
|
|
1584
|
+
var targetLine = jsonContainer.querySelector('[data-path="' + path + '"]');
|
|
1585
|
+
if (targetLine) {
|
|
1586
|
+
targetLine.classList.add('highlighted');
|
|
1587
|
+
targetLine.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
// Summary row click handler
|
|
1592
|
+
function initSummaryClicks() {
|
|
1593
|
+
document.querySelectorAll('[data-pointer-target]').forEach(function(el) {
|
|
1594
|
+
el.addEventListener('click', function(e) {
|
|
1595
|
+
// Don't navigate if clicking on toggle
|
|
1596
|
+
if (e.target.classList.contains('item-toggle')) return;
|
|
1597
|
+
|
|
1598
|
+
var target = el.dataset.pointerTarget;
|
|
1599
|
+
var path = el.dataset.pointerPath;
|
|
1600
|
+
if (target && path) {
|
|
1601
|
+
var container = el.closest('.rpc-detail-content') || document;
|
|
1602
|
+
navigateToPath(target, path, container);
|
|
1603
|
+
|
|
1604
|
+
// Mark as selected within the same container
|
|
1605
|
+
container.querySelectorAll('.summary-item.selected, .summary-property.selected').forEach(function(s) {
|
|
1606
|
+
s.classList.remove('selected');
|
|
1607
|
+
});
|
|
1608
|
+
el.classList.add('selected');
|
|
1609
|
+
}
|
|
1610
|
+
});
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// Item toggle (expand/collapse single tool)
|
|
1615
|
+
function initItemToggles() {
|
|
1616
|
+
document.querySelectorAll('.summary-item.has-children .item-toggle').forEach(function(toggle) {
|
|
1617
|
+
toggle.addEventListener('click', function(e) {
|
|
1618
|
+
e.stopPropagation();
|
|
1619
|
+
var item = toggle.closest('.summary-item');
|
|
1620
|
+
if (item) {
|
|
1621
|
+
item.classList.toggle('collapsed');
|
|
1622
|
+
}
|
|
1623
|
+
});
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
// Collapse/Expand all buttons
|
|
1628
|
+
function initCollapseControls() {
|
|
1629
|
+
document.querySelectorAll('.collapse-all').forEach(function(btn) {
|
|
1630
|
+
btn.addEventListener('click', function() {
|
|
1631
|
+
var container = btn.closest('.rpc-detail-content') || document;
|
|
1632
|
+
container.querySelectorAll('.summary-item.has-children').forEach(function(item) {
|
|
1633
|
+
item.classList.add('collapsed');
|
|
1634
|
+
});
|
|
1635
|
+
});
|
|
1636
|
+
});
|
|
1637
|
+
|
|
1638
|
+
document.querySelectorAll('.expand-all').forEach(function(btn) {
|
|
1639
|
+
btn.addEventListener('click', function() {
|
|
1640
|
+
var container = btn.closest('.rpc-detail-content') || document;
|
|
1641
|
+
container.querySelectorAll('.summary-item.has-children').forEach(function(item) {
|
|
1642
|
+
item.classList.remove('collapsed');
|
|
1643
|
+
});
|
|
1644
|
+
});
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
// Prevent scroll chaining between Summary and RAW panes
|
|
1649
|
+
function initScrollIsolation() {
|
|
1650
|
+
document.querySelectorAll('.rpc-inspector-summary, .rpc-raw-json').forEach(function(pane) {
|
|
1651
|
+
pane.addEventListener('wheel', function(e) {
|
|
1652
|
+
var atTop = pane.scrollTop === 0;
|
|
1653
|
+
var atBottom = pane.scrollTop + pane.clientHeight >= pane.scrollHeight;
|
|
1654
|
+
|
|
1655
|
+
// If scrolling up at top or scrolling down at bottom, prevent propagation
|
|
1656
|
+
if ((e.deltaY < 0 && atTop) || (e.deltaY > 0 && atBottom)) {
|
|
1657
|
+
e.preventDefault();
|
|
1658
|
+
}
|
|
1659
|
+
}, { passive: false });
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
// Set max-height dynamically based on available space
|
|
1664
|
+
function initPaneHeights() {
|
|
1665
|
+
function updateHeights() {
|
|
1666
|
+
document.querySelectorAll('.rpc-inspector').forEach(function(inspector) {
|
|
1667
|
+
var rect = inspector.getBoundingClientRect();
|
|
1668
|
+
var availableHeight = window.innerHeight - rect.top - 20;
|
|
1669
|
+
if (availableHeight > 200) {
|
|
1670
|
+
inspector.style.maxHeight = availableHeight + 'px';
|
|
1671
|
+
var summary = inspector.querySelector('.rpc-inspector-summary');
|
|
1672
|
+
var rawJson = inspector.querySelector('.rpc-raw-json');
|
|
1673
|
+
if (summary) summary.style.maxHeight = availableHeight + 'px';
|
|
1674
|
+
if (rawJson) rawJson.style.maxHeight = (availableHeight - 50) + 'px';
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
updateHeights();
|
|
1679
|
+
window.addEventListener('resize', updateHeights);
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
// Expose for re-initialization after dynamic content update
|
|
1683
|
+
window.initRpcInspector = function() {
|
|
1684
|
+
initInspectorToggle();
|
|
1685
|
+
initSummaryClicks();
|
|
1686
|
+
initItemToggles();
|
|
1687
|
+
initCollapseControls();
|
|
1688
|
+
initScrollIsolation();
|
|
1689
|
+
initPaneHeights();
|
|
1690
|
+
};
|
|
1691
|
+
|
|
1692
|
+
// Initialize on DOM ready
|
|
1693
|
+
if (document.readyState === 'loading') {
|
|
1694
|
+
document.addEventListener('DOMContentLoaded', window.initRpcInspector);
|
|
1695
|
+
} else {
|
|
1696
|
+
window.initRpcInspector();
|
|
1697
|
+
}
|
|
1698
|
+
})();
|
|
1699
|
+
`;
|
|
1700
|
+
}
|