@fitchmultz/pi-mcp-adapter 5.0.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/OAUTH.md +3 -3
  3. package/README.md +160 -88
  4. package/dist/app-bridge.bundle.js +75 -33
  5. package/dist/commands.js +9 -6
  6. package/dist/config.js +41 -16
  7. package/dist/direct-tools.d.ts +2 -4
  8. package/dist/direct-tools.js +21 -319
  9. package/dist/gateway-arguments.d.ts +33 -0
  10. package/dist/gateway-arguments.js +63 -0
  11. package/dist/index.js +181 -276
  12. package/dist/init.js +6 -20
  13. package/dist/lifecycle.d.ts +2 -2
  14. package/dist/lifecycle.js +2 -2
  15. package/dist/mcp-auth-flow.js +2 -2
  16. package/dist/mcp-auth.d.ts +1 -1
  17. package/dist/mcp-auth.js +3 -3
  18. package/dist/mcp-code.d.ts +7 -6
  19. package/dist/mcp-code.js +98 -51
  20. package/dist/mcp-oauth-provider.d.ts +5 -5
  21. package/dist/mcp-oauth-provider.js +5 -5
  22. package/dist/mcp-output-guard.d.ts +57 -2
  23. package/dist/mcp-output-guard.js +129 -24
  24. package/dist/mcp-panel.d.ts +3 -3
  25. package/dist/mcp-panel.js +36 -99
  26. package/dist/mcp-script-worker.mjs +5 -2
  27. package/dist/mcp-setup-panel.d.ts +5 -4
  28. package/dist/mcp-setup-panel.js +15 -7
  29. package/dist/mcp-status.js +2 -1
  30. package/dist/mcp-trace.d.ts +1 -1
  31. package/dist/mcp-trace.js +1 -1
  32. package/dist/metadata-cache.d.ts +1 -1
  33. package/dist/metadata-cache.js +25 -88
  34. package/dist/npx-resolver.js +8 -4
  35. package/dist/prompts.d.ts +1 -1
  36. package/dist/prompts.js +1 -1
  37. package/dist/proxy-modes.d.ts +41 -7
  38. package/dist/proxy-modes.js +275 -490
  39. package/dist/resource-tools.d.ts +15 -0
  40. package/dist/resource-tools.js +26 -1
  41. package/dist/sampling-handler.js +5 -1
  42. package/dist/search-ranking.d.ts +0 -2
  43. package/dist/search-ranking.js +70 -137
  44. package/dist/server-manager.d.ts +2 -2
  45. package/dist/server-manager.js +44 -27
  46. package/dist/session-recovery.d.ts +7 -4
  47. package/dist/session-recovery.js +76 -10
  48. package/dist/state.d.ts +1 -1
  49. package/dist/tool-loader.d.ts +32 -0
  50. package/dist/tool-loader.js +152 -0
  51. package/dist/tool-metadata.d.ts +67 -0
  52. package/dist/tool-metadata.js +23 -17
  53. package/dist/tool-registrar.d.ts +4 -4
  54. package/dist/tool-registrar.js +18 -13
  55. package/dist/tool-result-renderer.d.ts +6 -3
  56. package/dist/tool-result-renderer.js +11 -6
  57. package/dist/types.d.ts +11 -36
  58. package/dist/ui-resource-handler.d.ts +2 -2
  59. package/dist/ui-resource-handler.js +2 -2
  60. package/dist/ui-server.js +4 -3
  61. package/dist/ui-stream-types.d.ts +8 -8
  62. package/dist/unix-socket-transport.d.ts +1 -1
  63. package/dist/unix-socket-transport.js +1 -1
  64. package/dist/utils.d.ts +1 -0
  65. package/dist/utils.js +16 -4
  66. package/package.json +37 -96
  67. package/skills/mcp-scripting/SKILL.md +67 -20
  68. package/abort.ts +0 -34
  69. package/agent-dir.ts +0 -24
  70. package/app-bridge.bundle.js +0 -67
  71. package/banner.png +0 -0
  72. package/checkpoint.ts +0 -55
  73. package/commands.ts +0 -619
  74. package/config.ts +0 -1204
  75. package/consent-manager.ts +0 -25
  76. package/direct-tools.ts +0 -408
  77. package/dist/ts-shape.d.ts +0 -2
  78. package/dist/ts-shape.js +0 -147
  79. package/elicitation-handler.ts +0 -349
  80. package/error-signal.ts +0 -21
  81. package/glimpse-ui.ts +0 -80
  82. package/host-html-template.ts +0 -456
  83. package/index.ts +0 -900
  84. package/init.ts +0 -627
  85. package/legacy-migration.ts +0 -47
  86. package/lifecycle.ts +0 -165
  87. package/logger.ts +0 -127
  88. package/mcp-auth-flow.ts +0 -975
  89. package/mcp-auth.ts +0 -793
  90. package/mcp-callback-server.ts +0 -450
  91. package/mcp-code.ts +0 -386
  92. package/mcp-keyring-helper.cjs +0 -89
  93. package/mcp-oauth-provider.ts +0 -774
  94. package/mcp-output-guard.ts +0 -412
  95. package/mcp-panel.ts +0 -978
  96. package/mcp-script-worker.mjs +0 -128
  97. package/mcp-setup-panel.ts +0 -666
  98. package/mcp-status.ts +0 -109
  99. package/mcp-trace.ts +0 -295
  100. package/metadata-cache.ts +0 -333
  101. package/npx-resolver.ts +0 -488
  102. package/onboarding-state.ts +0 -58
  103. package/panel-keys.ts +0 -37
  104. package/prompts.ts +0 -337
  105. package/proxy-modes.ts +0 -1361
  106. package/resource-tools.ts +0 -17
  107. package/runtime-owner.ts +0 -123
  108. package/sampling-handler.ts +0 -273
  109. package/scripts/build.mjs +0 -131
  110. package/scripts/prepare.mjs +0 -102
  111. package/search-ranking.ts +0 -196
  112. package/server-manager.ts +0 -1320
  113. package/session-recovery.ts +0 -316
  114. package/state.ts +0 -64
  115. package/tool-approval.ts +0 -78
  116. package/tool-metadata.ts +0 -235
  117. package/tool-registrar.ts +0 -69
  118. package/tool-result-renderer.ts +0 -286
  119. package/ts-shape.ts +0 -145
  120. package/tsconfig.build.json +0 -13
  121. package/tsconfig.json +0 -16
  122. package/types.ts +0 -810
  123. package/ui-resource-handler.ts +0 -249
  124. package/ui-server.ts +0 -808
  125. package/ui-session.ts +0 -568
  126. package/ui-stream-types.ts +0 -89
  127. package/ui-tool-visibility.ts +0 -25
  128. package/unix-socket-transport.ts +0 -85
  129. package/utils.ts +0 -309
package/tool-registrar.ts DELETED
@@ -1,69 +0,0 @@
1
- // tool-registrar.ts - MCP content transformation
2
- // NOTE: Tools are NOT registered with Pi - only the unified `mcp` proxy tool is registered.
3
- // This keeps the LLM context small (1 tool instead of 100s).
4
-
5
- import type { McpContent, ContentBlock } from "./types.ts";
6
-
7
- /**
8
- * Transform MCP content types to Pi content blocks.
9
- */
10
- export function transformMcpContent(content: McpContent[]): ContentBlock[] {
11
- return content.map(c => {
12
- if (c.type === "text") {
13
- return { type: "text" as const, text: c.text ?? "" };
14
- }
15
- if (c.type === "image") {
16
- return {
17
- type: "image" as const,
18
- data: c.data ?? "",
19
- mimeType: c.mimeType ?? "image/png",
20
- };
21
- }
22
- if (c.type === "resource") {
23
- const resourceUri = c.resource?.uri ?? "(no URI)";
24
- const resourceContent = c.resource?.text ?? (c.resource ? JSON.stringify(c.resource) : "(no content)");
25
- return {
26
- type: "text" as const,
27
- text: `[Resource: ${resourceUri}]\n${resourceContent}`,
28
- };
29
- }
30
- if (c.type === "resource_link") {
31
- const linkName = c.name ?? c.uri ?? "unknown";
32
- const linkUri = c.uri ?? "(no URI)";
33
- return {
34
- type: "text" as const,
35
- text: `[Resource Link: ${linkName}]\nURI: ${linkUri}`,
36
- };
37
- }
38
- if (c.type === "audio") {
39
- return {
40
- type: "text" as const,
41
- text: `[Audio content: ${c.mimeType ?? "audio/*"}]`,
42
- };
43
- }
44
- return { type: "text" as const, text: JSON.stringify(c) };
45
- });
46
- }
47
-
48
- /**
49
- * Resolve a tool result's content blocks, falling back to structuredContent
50
- * when content is empty.
51
- */
52
- export function resolveMcpResultContent(result: Record<string, unknown>): ContentBlock[] {
53
- const blocks = transformMcpContent((Array.isArray(result.content) ? result.content : []) as McpContent[]);
54
- if (blocks.length > 0) return blocks;
55
-
56
- if (result.structuredContent !== undefined) {
57
- return [{ type: "text" as const, text: stringifyStructuredContent(result.structuredContent) }];
58
- }
59
-
60
- return [];
61
- }
62
-
63
- function stringifyStructuredContent(value: unknown): string {
64
- try {
65
- return JSON.stringify(value, null, 2) ?? String(value);
66
- } catch {
67
- return String(value);
68
- }
69
- }
@@ -1,286 +0,0 @@
1
- import { keyHint, type AgentToolResult, type Theme, type ToolRenderResultOptions } from "@earendil-works/pi-coding-agent";
2
- import { type Component, Text } from "@earendil-works/pi-tui";
3
-
4
- type McpToolResultDetails = Record<string, unknown> & { error?: unknown };
5
- type McpToolContentBlock = AgentToolResult<McpToolResultDetails>["content"][number];
6
-
7
- type RenderTheme = Pick<Theme, "fg" | "bold">;
8
-
9
- const plainTheme: RenderTheme = {
10
- fg: (_name, text) => text,
11
- bold: (text) => text,
12
- };
13
-
14
- export interface McpProxyToolCallInput {
15
- tool?: string;
16
- args?: string | object;
17
- connect?: string;
18
- describe?: string;
19
- search?: string;
20
- includeSchemas?: boolean;
21
- server?: string;
22
- action?: string;
23
- }
24
-
25
- interface McpToolRenderContext {
26
- isError: boolean;
27
- }
28
-
29
- export interface McpToolResultDisplay {
30
- lines: string[];
31
- truncated: boolean;
32
- }
33
-
34
- const DEFAULT_MAX_CALL_INPUT_CHARS = 1500;
35
- const DEFAULT_MAX_COLLAPSED_LINES = 3;
36
- const DEFAULT_MAX_COLLAPSED_CHARS = 8000;
37
- const COLLAPSED_RENDER_CHAR_SLACK = 8;
38
-
39
- class CollapsibleText implements Component {
40
- private readonly fullText: Text;
41
- private readonly footerText: Text;
42
- private collapsedText: { charBudget: number; fullyIncluded: boolean; text: Text } | null = null;
43
-
44
- constructor(
45
- private readonly text: string,
46
- private readonly expanded: boolean,
47
- private readonly maxCollapsedLines: number,
48
- private readonly ellipsis: string,
49
- private readonly expandHint: string,
50
- private readonly preTruncated = false,
51
- ) {
52
- this.fullText = new Text(text, 0, 0);
53
- this.footerText = new Text(`${ellipsis}\n${expandHint}`, 0, 0);
54
- }
55
-
56
- render(width: number): string[] {
57
- if (this.expanded) {
58
- return this.fullText.render(width);
59
- }
60
-
61
- const safeWidth = Math.max(1, Math.floor(width));
62
- const charBudget = safeWidth * (this.maxCollapsedLines + 1) * COLLAPSED_RENDER_CHAR_SLACK;
63
- if (!this.collapsedText || this.collapsedText.charBudget !== charBudget) {
64
- const prefix = this.text.length > charBudget
65
- ? this.text.slice(0, charBudget)
66
- : this.text;
67
- this.collapsedText = {
68
- charBudget,
69
- fullyIncluded: prefix === this.text,
70
- text: new Text(prefix, 0, 0),
71
- };
72
- }
73
-
74
- const lines = this.collapsedText.text.render(width);
75
- if (!this.preTruncated && this.collapsedText.fullyIncluded && lines.length <= this.maxCollapsedLines) return lines;
76
-
77
- return [
78
- ...lines.slice(0, this.maxCollapsedLines),
79
- ...this.footerText.render(width),
80
- ];
81
- }
82
-
83
- invalidate(): void {}
84
- }
85
-
86
- function truncateText(value: string, maxChars: number): string {
87
- if (value.length <= maxChars) return value;
88
- return `${value.slice(0, Math.max(0, maxChars - 1))}…`;
89
- }
90
-
91
- function formatJsonish(value: unknown, maxChars: number): string {
92
- if (typeof value === "string") {
93
- try {
94
- return truncateText(JSON.stringify(JSON.parse(value), null, 2), maxChars);
95
- } catch {
96
- return truncateText(value, maxChars);
97
- }
98
- }
99
-
100
- try {
101
- return truncateText(JSON.stringify(value, null, 2), maxChars);
102
- } catch {
103
- return truncateText(String(value), maxChars);
104
- }
105
- }
106
-
107
- function hasUsefulObjectContent(value: unknown): boolean {
108
- return typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).length > 0;
109
- }
110
-
111
- export function formatMcpProxyToolCallLines(
112
- args: McpProxyToolCallInput,
113
- maxInputChars = DEFAULT_MAX_CALL_INPUT_CHARS,
114
- ): string[] {
115
- if (args.action === "ui-messages") return [`mcp ${args.action}`];
116
-
117
- if (args.tool) {
118
- const target = args.server ? `${args.tool} @ ${args.server}` : args.tool;
119
- const lines = [`mcp call ${target}`];
120
- if (args.args) lines.push(formatJsonish(args.args, maxInputChars));
121
- return lines;
122
- }
123
-
124
- if (args.connect) return [`mcp connect ${args.connect}`];
125
- if (args.describe) return [`mcp describe ${args.describe}`];
126
-
127
- if (args.search) {
128
- let line = `mcp search ${args.search}`;
129
- if (args.server) line += ` @ ${args.server}`;
130
- if (args.includeSchemas === false) line += " (schemas hidden)";
131
- return [line];
132
- }
133
-
134
- if (args.server) return [`mcp list ${args.server}`];
135
- if (args.action) return [`mcp ${args.action}`];
136
-
137
- return ["mcp status"];
138
- }
139
-
140
- export function formatMcpDirectToolCallLines(
141
- displayName: string,
142
- args: Record<string, unknown>,
143
- maxInputChars = DEFAULT_MAX_CALL_INPUT_CHARS,
144
- ): string[] {
145
- if (!hasUsefulObjectContent(args)) return [displayName];
146
- return [displayName, formatJsonish(args, maxInputChars)];
147
- }
148
-
149
- function renderToolCallLines(lines: string[], theme?: RenderTheme) {
150
- const activeTheme = theme ?? plainTheme;
151
- const [title = "mcp", ...rest] = lines;
152
- const styledTitle = activeTheme.fg("toolTitle", activeTheme.bold(title));
153
- const styledRest = rest.map(line => activeTheme.fg("muted", line));
154
- return new Text([styledTitle, ...styledRest].join("\n"), 0, 0);
155
- }
156
-
157
- export function renderMcpProxyToolCall(args: McpProxyToolCallInput, theme?: RenderTheme) {
158
- return renderToolCallLines(formatMcpProxyToolCallLines(args), theme);
159
- }
160
-
161
- export function createMcpDirectToolCallRenderer(displayName: string) {
162
- return (args: Record<string, unknown>, theme?: RenderTheme) => {
163
- return renderToolCallLines(formatMcpDirectToolCallLines(displayName, args), theme);
164
- };
165
- }
166
-
167
- function blockToLines(block: McpToolContentBlock): string[] {
168
- if (block.type === "text") {
169
- return block.text.split("\n");
170
- }
171
- return [`[image: ${block.mimeType}]`];
172
- }
173
-
174
- function collectCollapsedResultLines(
175
- content: AgentToolResult<McpToolResultDetails>["content"],
176
- maxLines: number,
177
- maxChars: number,
178
- ): McpToolResultDisplay {
179
- if (content.length === 0) return { lines: ["(empty result)"], truncated: false };
180
-
181
- const lines: string[] = [];
182
- let remainingChars = maxChars;
183
- let truncated = false;
184
-
185
- const appendLine = (line: string) => {
186
- if (lines.length >= maxLines || remainingChars <= 0) {
187
- truncated = true;
188
- return false;
189
- }
190
-
191
- if (line.length > remainingChars) {
192
- lines.push(line.slice(0, remainingChars));
193
- truncated = true;
194
- remainingChars = 0;
195
- return false;
196
- }
197
-
198
- lines.push(line);
199
- remainingChars -= line.length + 1;
200
- return true;
201
- };
202
-
203
- for (const block of content) {
204
- if (block.type !== "text") {
205
- if (!appendLine(`[image: ${block.mimeType}]`)) break;
206
- continue;
207
- }
208
-
209
- let start = 0;
210
- while (start <= block.text.length) {
211
- const newline = block.text.indexOf("\n", start);
212
- const line = newline === -1 ? block.text.slice(start) : block.text.slice(start, newline);
213
- if (!appendLine(line)) break;
214
- if (newline === -1) break;
215
- start = newline + 1;
216
- }
217
-
218
- if (truncated) break;
219
- }
220
-
221
- if (lines.length === 0) lines.push("");
222
- if (truncated && lines.length >= maxLines) lines.push("…");
223
- return { lines, truncated };
224
- }
225
-
226
- export function formatMcpToolResultIdentity(details: McpToolResultDetails | undefined): string | null {
227
- if (details?.mode !== "call") return null;
228
- const server = typeof details.server === "string"
229
- ? details.server
230
- : typeof details.hintServer === "string"
231
- ? details.hintServer
232
- : null;
233
- if (!server) return null;
234
- if (typeof details.tool === "string") return `MCP ${server}/${details.tool}`;
235
- if (typeof details.resourceUri === "string") return `MCP ${server} resource ${details.resourceUri}`;
236
- if (typeof details.requestedTool === "string") return `MCP ${server}/${details.requestedTool}`;
237
- return null;
238
- }
239
-
240
- export function formatMcpToolResultLines(
241
- result: Pick<AgentToolResult<McpToolResultDetails>, "content">,
242
- expanded: boolean,
243
- maxCollapsedLines = 3,
244
- maxCollapsedChars = DEFAULT_MAX_COLLAPSED_CHARS,
245
- ): McpToolResultDisplay {
246
- if (!expanded) {
247
- return collectCollapsedResultLines(result.content, maxCollapsedLines, maxCollapsedChars);
248
- }
249
-
250
- const allLines = result.content.flatMap(blockToLines);
251
- const lines = allLines.length > 0 ? allLines : ["(empty result)"];
252
- return { lines, truncated: false };
253
- }
254
-
255
- export function renderMcpToolResult(
256
- result: AgentToolResult<unknown>,
257
- options: ToolRenderResultOptions,
258
- theme?: RenderTheme,
259
- context?: McpToolRenderContext,
260
- ) {
261
- const activeTheme = theme ?? plainTheme;
262
- if (options.isPartial) {
263
- return new Text(activeTheme.fg("warning", "Running MCP tool..."), 0, 0);
264
- }
265
-
266
- const details = typeof result.details === "object" && result.details !== null
267
- ? result.details as McpToolResultDetails
268
- : {};
269
- const hasErrorDetails = Boolean(details.error);
270
- const expanded = options.expanded || context?.isError === true || hasErrorDetails;
271
- const display = formatMcpToolResultLines(result, expanded);
272
- const identity = formatMcpToolResultIdentity(details);
273
- const output = [
274
- ...(identity ? [activeTheme.fg("muted", identity)] : []),
275
- ...display.lines.map((line) => activeTheme.fg("toolOutput", line)),
276
- ].join("\n");
277
-
278
- return new CollapsibleText(
279
- output,
280
- expanded,
281
- DEFAULT_MAX_COLLAPSED_LINES + (identity ? 1 : 0),
282
- activeTheme.fg("muted", "…"),
283
- activeTheme.fg("muted", `(${keyHint("app.tools.expand", "to expand")})`),
284
- display.truncated,
285
- );
286
- }
package/ts-shape.ts DELETED
@@ -1,145 +0,0 @@
1
- type Schema = Record<string, unknown>;
2
-
3
- const UNSUPPORTED_KEYWORDS = ["if", "then", "else", "allOf", "not", "patternProperties", "additionalProperties"];
4
-
5
- /** Renders the useful JSON Schema subset as TypeScript, or null for unsupported schemas. */
6
- export function renderTsShape(inputSchema: unknown): string | null {
7
- try {
8
- if (!isSchema(inputSchema)) return null;
9
-
10
- const definitions = new Map<string, Schema>();
11
- for (const key of ["$defs", "definitions"]) {
12
- const rawDefinitions = inputSchema[key];
13
- if (rawDefinitions === undefined) continue;
14
- if (!isSchema(rawDefinitions)) return null;
15
- for (const [name, definition] of Object.entries(rawDefinitions)) {
16
- if (!isSchema(definition)) return null;
17
- definitions.set(`${key}/${decodePointerToken(name)}`, definition);
18
- }
19
- }
20
-
21
- const aliases = new Map<string, string>();
22
- const usedAliases = new Set<string>();
23
- let aliasIndex = 0;
24
- const aliasFor = (definitionKey: string) => {
25
- let alias = aliases.get(definitionKey);
26
- if (alias) return alias;
27
- const name = definitionKey.slice(definitionKey.indexOf("/") + 1);
28
- alias = /^[A-Za-z_$][\w$]*$/.test(name) && !usedAliases.has(name) ? name : `Definition${++aliasIndex}`;
29
- while (usedAliases.has(alias)) alias = `Definition${++aliasIndex}`;
30
- aliases.set(definitionKey, alias);
31
- usedAliases.add(alias);
32
- return alias;
33
- };
34
-
35
- const render = (schema: unknown): string | null => {
36
- if (!isSchema(schema) || hasUnsupportedKeyword(schema)) return null;
37
-
38
- if ("$ref" in schema) {
39
- if (typeof schema.$ref !== "string") return null;
40
- const match = schema.$ref.match(/^#\/(\$defs|definitions)\/([^/]+)$/);
41
- if (!match) return null;
42
- const definitionGroup = match[1];
43
- const definitionName = match[2];
44
- if (definitionGroup === undefined || definitionName === undefined) return null;
45
- const definitionKey = `${definitionGroup}/${decodePointerToken(definitionName)}`;
46
- if (!definitions.has(definitionKey)) return null;
47
- return aliasFor(definitionKey);
48
- }
49
-
50
- if (Array.isArray(schema.enum)) {
51
- const values = schema.enum.map(renderLiteral);
52
- return values.every((value): value is string => value !== null) ? values.join(" | ") : null;
53
- }
54
- if (Object.hasOwn(schema, "const")) return renderLiteral(schema.const);
55
-
56
- if (Array.isArray(schema.anyOf) || Array.isArray(schema.oneOf)) {
57
- const variants = (schema.anyOf ?? schema.oneOf) as unknown[];
58
- if (variants.length === 0) return null;
59
- const rendered = variants.map(render);
60
- return rendered.every((value): value is string => value !== null) ? rendered.join(" | ") : null;
61
- }
62
-
63
- if (schema.type === "object" || schema.properties !== undefined) {
64
- if (schema.properties === undefined) return "{}";
65
- if (!isSchema(schema.properties)) return null;
66
- const required = new Set(Array.isArray(schema.required)
67
- ? schema.required.filter((name): name is string => typeof name === "string")
68
- : []);
69
- const properties: string[] = [];
70
- for (const [name, property] of Object.entries(schema.properties)) {
71
- const rendered = render(property);
72
- if (rendered === null) return null;
73
- properties.push(`${formatPropertyName(name)}${required.has(name) ? "" : "?"}: ${rendered};`);
74
- }
75
- return properties.length === 0 ? "{}" : `{ ${properties.join(" ")} }`;
76
- }
77
-
78
- if (schema.type === "array") {
79
- if (schema.items === undefined) return "unknown[]";
80
- const item = render(schema.items);
81
- return item === null ? null : `${needsParentheses(item) ? `(${item})` : item}[]`;
82
- }
83
-
84
- if (Array.isArray(schema.type)) {
85
- const types = schema.type.map(renderType);
86
- return types.every((type): type is string => type !== null) ? types.join(" | ") : null;
87
- }
88
- if (typeof schema.type === "string") return renderType(schema.type);
89
- return "unknown";
90
- };
91
-
92
- const root = render(inputSchema);
93
- if (root === null) return null;
94
-
95
- const definitionsText: string[] = [];
96
- for (const [key, alias] of aliases) {
97
- const definition = definitions.get(key);
98
- if (!definition) return null;
99
- const rendered = render(definition);
100
- if (rendered === null) return null;
101
- definitionsText.push(`type ${alias} = ${rendered};`);
102
- }
103
- return definitionsText.length > 0 ? `${definitionsText.join("\n")}\n\n${root}` : root;
104
- } catch {
105
- return null;
106
- }
107
- }
108
-
109
- function isSchema(value: unknown): value is Schema {
110
- return typeof value === "object" && value !== null && !Array.isArray(value);
111
- }
112
-
113
- function hasUnsupportedKeyword(schema: Schema): boolean {
114
- return UNSUPPORTED_KEYWORDS.some(keyword => Object.hasOwn(schema, keyword));
115
- }
116
-
117
- function decodePointerToken(token: string): string {
118
- return token.replace(/~1/g, "/").replace(/~0/g, "~");
119
- }
120
-
121
- function renderType(type: unknown): string | null {
122
- switch (type) {
123
- case "string": return "string";
124
- case "number":
125
- case "integer": return "number";
126
- case "boolean": return "boolean";
127
- case "null": return "null";
128
- case "object": return "{}";
129
- case "array": return "unknown[]";
130
- default: return null;
131
- }
132
- }
133
-
134
- function renderLiteral(value: unknown): string | null {
135
- if (value === null || typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
136
- return typeof value === "number" && Number.isFinite(value) ? String(value) : null;
137
- }
138
-
139
- function formatPropertyName(name: string): string {
140
- return /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name);
141
- }
142
-
143
- function needsParentheses(type: string): boolean {
144
- return type.includes(" | ");
145
- }
@@ -1,13 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "noEmit": false,
5
- "declaration": true,
6
- "rewriteRelativeImportExtensions": true,
7
- "outDir": "dist",
8
- "rootDir": ".",
9
- "sourceMap": false
10
- },
11
- "include": ["*.ts"],
12
- "exclude": ["*.test.ts", "vitest.config.ts", "dist", "node_modules"]
13
- }
package/tsconfig.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "allowImportingTsExtensions": true,
7
- "esModuleInterop": true,
8
- "strict": true,
9
- "exactOptionalPropertyTypes": true,
10
- "noUncheckedIndexedAccess": true,
11
- "skipLibCheck": true,
12
- "noEmit": true,
13
- "resolveJsonModule": true
14
- },
15
- "include": ["*.ts"]
16
- }