toolcraft 0.0.17 → 0.0.19
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/dist/cli.d.ts +2 -0
- package/dist/cli.js +833 -124
- package/dist/error-report.d.ts +39 -0
- package/dist/error-report.js +330 -0
- package/dist/human-in-loop/approval-tasks.js +11 -8
- package/dist/human-in-loop/approvals-commands.js +21 -20
- package/dist/human-in-loop/default-provider.js +5 -3
- package/dist/human-in-loop/runner.js +45 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +55 -35
- package/dist/json-schema-converter.d.ts +1 -0
- package/dist/json-schema-converter.js +102 -52
- package/dist/mcp-proxy.d.ts +1 -0
- package/dist/mcp-proxy.js +13 -6
- package/dist/mcp.d.ts +2 -0
- package/dist/mcp.js +131 -55
- package/dist/sdk.d.ts +4 -2
- package/dist/sdk.js +132 -48
- package/dist/source-snippet.d.ts +8 -0
- package/dist/source-snippet.js +42 -0
- package/dist/stack-trim.d.ts +4 -0
- package/dist/stack-trim.js +70 -0
- package/dist/suggest.d.ts +4 -0
- package/dist/suggest.js +46 -0
- package/dist/user-error.d.ts +3 -0
- package/dist/user-error.js +7 -1
- package/dist/validation-errors.d.ts +5 -0
- package/dist/validation-errors.js +18 -0
- package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +3 -3
- package/node_modules/@poe-code/config-mutations/dist/mutations/template-mutation.d.ts +3 -3
- package/node_modules/@poe-code/config-mutations/dist/template/render.d.ts +0 -1
- package/node_modules/@poe-code/config-mutations/dist/template/render.js +2 -22
- package/node_modules/@poe-code/config-mutations/package.json +1 -4
- package/node_modules/@poe-code/design-system/dist/acp/components.js +15 -13
- package/node_modules/@poe-code/design-system/dist/components/color.d.ts +31 -0
- package/node_modules/@poe-code/design-system/dist/components/color.js +101 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.js +1 -1
- package/node_modules/@poe-code/design-system/dist/components/index.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/components/index.js +2 -0
- package/node_modules/@poe-code/design-system/dist/components/logger.js +2 -2
- package/node_modules/@poe-code/design-system/dist/components/symbols.js +3 -3
- package/node_modules/@poe-code/design-system/dist/components/table.js +191 -40
- package/node_modules/@poe-code/design-system/dist/components/template.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/components/template.js +271 -0
- package/node_modules/@poe-code/design-system/dist/components/text.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/components/text.js +11 -3
- package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +20 -13
- package/node_modules/@poe-code/design-system/dist/dashboard/keymap.d.ts +5 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/keymap.js +146 -12
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal.js +31 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/actions.d.ts +16 -0
- package/node_modules/@poe-code/design-system/dist/explorer/actions.js +39 -0
- package/node_modules/@poe-code/design-system/dist/explorer/demo.d.ts +13 -0
- package/node_modules/@poe-code/design-system/dist/explorer/demo.js +297 -0
- package/node_modules/@poe-code/design-system/dist/explorer/events.d.ts +61 -0
- package/node_modules/@poe-code/design-system/dist/explorer/events.js +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/filter.d.ts +10 -0
- package/node_modules/@poe-code/design-system/dist/explorer/filter.js +95 -0
- package/node_modules/@poe-code/design-system/dist/explorer/index.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/explorer/index.js +8 -0
- package/node_modules/@poe-code/design-system/dist/explorer/jobs.d.ts +7 -0
- package/node_modules/@poe-code/design-system/dist/explorer/jobs.js +59 -0
- package/node_modules/@poe-code/design-system/dist/explorer/keymap.d.ts +21 -0
- package/node_modules/@poe-code/design-system/dist/explorer/keymap.js +363 -0
- package/node_modules/@poe-code/design-system/dist/explorer/layout.d.ts +20 -0
- package/node_modules/@poe-code/design-system/dist/explorer/layout.js +73 -0
- package/node_modules/@poe-code/design-system/dist/explorer/reducer.d.ts +9 -0
- package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +704 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/detail.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +96 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/footer.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/footer.js +49 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/header.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/header.js +56 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/index.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/index.js +61 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/list.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/list.js +106 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/modal.d.ts +3 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/modal.js +91 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/test-fixtures.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/test-fixtures.js +156 -0
- package/node_modules/@poe-code/design-system/dist/explorer/runtime.d.ts +2 -0
- package/node_modules/@poe-code/design-system/dist/explorer/runtime.js +282 -0
- package/node_modules/@poe-code/design-system/dist/explorer/runtime.test-helpers.d.ts +50 -0
- package/node_modules/@poe-code/design-system/dist/explorer/runtime.test-helpers.js +101 -0
- package/node_modules/@poe-code/design-system/dist/explorer/state.d.ts +130 -0
- package/node_modules/@poe-code/design-system/dist/explorer/state.js +87 -0
- package/node_modules/@poe-code/design-system/dist/explorer/theme.d.ts +27 -0
- package/node_modules/@poe-code/design-system/dist/explorer/theme.js +97 -0
- package/node_modules/@poe-code/design-system/dist/index.d.ts +7 -0
- package/node_modules/@poe-code/design-system/dist/index.js +5 -0
- package/node_modules/@poe-code/design-system/dist/internal/color-support.d.ts +9 -0
- package/node_modules/@poe-code/design-system/dist/internal/color-support.js +12 -0
- package/node_modules/@poe-code/design-system/dist/prompts/index.js +2 -2
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/cancel.js +2 -2
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +2 -2
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +4 -4
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +5 -5
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/outro.js +2 -2
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/spinner.js +3 -3
- package/node_modules/@poe-code/design-system/dist/static/menu.js +5 -5
- package/node_modules/@poe-code/design-system/dist/static/spinner.js +8 -8
- package/node_modules/@poe-code/design-system/dist/tokens/colors.js +29 -29
- package/node_modules/@poe-code/design-system/dist/tokens/typography.js +6 -6
- package/node_modules/@poe-code/design-system/package.json +6 -3
- package/package.json +6 -5
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
import { ApprovalDeclinedError } from "./human-in-loop/types.js";
|
|
3
3
|
import { mergeHumanInLoopFromGroup, validateHumanInLoopOnDefine } from "./human-in-loop/config.js";
|
|
4
|
-
import { UserError } from "./user-error.js";
|
|
4
|
+
import { ToolcraftBugError, UserError } from "./user-error.js";
|
|
5
|
+
import { suggest } from "./suggest.js";
|
|
5
6
|
const commandConfigSymbol = Symbol("toolcraft.command.config");
|
|
6
7
|
const groupConfigSymbol = Symbol("toolcraft.group.config");
|
|
7
8
|
const commandSourcePathSymbol = Symbol("toolcraft.command.sourcePath");
|
|
@@ -12,7 +13,7 @@ function cloneSecretDefinition(secret) {
|
|
|
12
13
|
return {
|
|
13
14
|
env: secret.env,
|
|
14
15
|
description: secret.description,
|
|
15
|
-
optional: secret.optional
|
|
16
|
+
optional: secret.optional
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
19
|
function cloneSecrets(secrets) {
|
|
@@ -28,7 +29,7 @@ function cloneRequires(requires) {
|
|
|
28
29
|
return {
|
|
29
30
|
auth: requires.auth,
|
|
30
31
|
apiVersion: requires.apiVersion,
|
|
31
|
-
check: requires.check
|
|
32
|
+
check: requires.check
|
|
32
33
|
};
|
|
33
34
|
}
|
|
34
35
|
function cloneStringArray(values) {
|
|
@@ -46,13 +47,13 @@ function cloneMcpServerConfig(config) {
|
|
|
46
47
|
transport: "stdio",
|
|
47
48
|
command: config.command,
|
|
48
49
|
args: cloneStringArray(config.args),
|
|
49
|
-
env: cloneStringRecord(config.env)
|
|
50
|
+
env: cloneStringRecord(config.env)
|
|
50
51
|
};
|
|
51
52
|
}
|
|
52
53
|
return {
|
|
53
54
|
transport: "http",
|
|
54
55
|
url: config.url,
|
|
55
|
-
headers: cloneStringRecord(config.headers)
|
|
56
|
+
headers: cloneStringRecord(config.headers)
|
|
56
57
|
};
|
|
57
58
|
}
|
|
58
59
|
function cloneRenameMap(rename) {
|
|
@@ -156,11 +157,9 @@ function mergeRequires(parent, child) {
|
|
|
156
157
|
const merged = {
|
|
157
158
|
auth: child?.auth ?? parent?.auth,
|
|
158
159
|
apiVersion: child?.apiVersion ?? parent?.apiVersion,
|
|
159
|
-
check: composeChecks(parent?.check, child?.check)
|
|
160
|
+
check: composeChecks(parent?.check, child?.check)
|
|
160
161
|
};
|
|
161
|
-
if (merged.auth === undefined &&
|
|
162
|
-
merged.apiVersion === undefined &&
|
|
163
|
-
merged.check === undefined) {
|
|
162
|
+
if (merged.auth === undefined && merged.apiVersion === undefined && merged.check === undefined) {
|
|
164
163
|
return undefined;
|
|
165
164
|
}
|
|
166
165
|
return merged;
|
|
@@ -207,12 +206,33 @@ export function resolveCommandSecrets(command, env = process.env) {
|
|
|
207
206
|
const value = env[secret.env];
|
|
208
207
|
if (value === undefined && secret.optional !== true) {
|
|
209
208
|
const details = secret.description ? `\n ${secret.description}` : "";
|
|
210
|
-
|
|
209
|
+
const candidates = Object.keys(env).filter((candidate) => candidate !== secret.env && env[candidate] !== undefined);
|
|
210
|
+
const suggestions = suggestSecretEnv(secret.env, candidates);
|
|
211
|
+
const suggestionLine = suggestions.length > 0 ? `\nDid you mean: ${suggestions.join(", ")}?` : "";
|
|
212
|
+
throw new UserError(`Missing required secret ${secret.env}${details}${suggestionLine}`);
|
|
211
213
|
}
|
|
212
214
|
secrets[name] = value;
|
|
213
215
|
}
|
|
214
216
|
return secrets;
|
|
215
217
|
}
|
|
218
|
+
function suggestSecretEnv(input, candidates) {
|
|
219
|
+
const directSuggestions = suggest(input, candidates);
|
|
220
|
+
if (!input.includes("_")) {
|
|
221
|
+
return directSuggestions;
|
|
222
|
+
}
|
|
223
|
+
const inputParts = input.split("_");
|
|
224
|
+
const firstPart = inputParts[0];
|
|
225
|
+
const lastPart = inputParts[inputParts.length - 1];
|
|
226
|
+
const relatedCandidates = candidates.filter((candidate) => {
|
|
227
|
+
const candidateParts = candidate.split("_");
|
|
228
|
+
return (candidateParts[0] === firstPart &&
|
|
229
|
+
candidateParts[candidateParts.length - 1] === lastPart);
|
|
230
|
+
});
|
|
231
|
+
const expandedSuggestions = suggest(input, relatedCandidates, {
|
|
232
|
+
threshold: Math.max(4, Math.floor(input.length / 4))
|
|
233
|
+
});
|
|
234
|
+
return [...new Set([...directSuggestions, ...expandedSuggestions])].slice(0, 3);
|
|
235
|
+
}
|
|
216
236
|
export async function assertCommandRequirements(command, context, options = {}) {
|
|
217
237
|
const requires = command.requires;
|
|
218
238
|
if (requires === undefined) {
|
|
@@ -221,22 +241,22 @@ export async function assertCommandRequirements(command, context, options = {})
|
|
|
221
241
|
const env = options.env ?? process.env;
|
|
222
242
|
const authEnvVar = options.authEnvVar ?? "POE_API_KEY";
|
|
223
243
|
if (requires.auth === true && env[authEnvVar] === undefined) {
|
|
224
|
-
throw new UserError(`
|
|
244
|
+
throw new UserError(`Command "${command.name}" requires authentication.\n Run 'poe-code login' first.`);
|
|
225
245
|
}
|
|
226
246
|
if (requires.apiVersion !== undefined) {
|
|
227
247
|
const minimumVersion = parseMinimumApiVersion(requires.apiVersion);
|
|
228
248
|
if (minimumVersion === undefined) {
|
|
229
|
-
throw new UserError(`
|
|
249
|
+
throw new UserError(`Command "${command.name}" has invalid apiVersion requirement "${requires.apiVersion}". Expected format ">=X.Y.Z".`);
|
|
230
250
|
}
|
|
231
251
|
if (options.apiVersion === undefined) {
|
|
232
|
-
throw new UserError(`
|
|
252
|
+
throw new UserError(`Command "${command.name}" requires API version ${requires.apiVersion}, but no runner API version was provided.`);
|
|
233
253
|
}
|
|
234
254
|
const runnerVersion = parseSimpleSemver(options.apiVersion);
|
|
235
255
|
if (runnerVersion === undefined) {
|
|
236
|
-
throw new UserError(`
|
|
256
|
+
throw new UserError(`Command "${command.name}" requires API version ${requires.apiVersion}, but runner API version "${options.apiVersion}" is not valid semver.`);
|
|
237
257
|
}
|
|
238
258
|
if (compareSemver(runnerVersion, minimumVersion) < 0) {
|
|
239
|
-
throw new UserError(`
|
|
259
|
+
throw new UserError(`Command "${command.name}" requires API version ${requires.apiVersion}, but runner API version is ${options.apiVersion}.`);
|
|
240
260
|
}
|
|
241
261
|
}
|
|
242
262
|
const checkResult = await requires.check?.(context);
|
|
@@ -247,7 +267,7 @@ export async function assertCommandRequirements(command, context, options = {})
|
|
|
247
267
|
function mergeSecrets(parent, child) {
|
|
248
268
|
return cloneSecrets({
|
|
249
269
|
...parent,
|
|
250
|
-
...child
|
|
270
|
+
...child
|
|
251
271
|
});
|
|
252
272
|
}
|
|
253
273
|
function resolveCommandScope(ownScope, inheritedScope) {
|
|
@@ -270,7 +290,7 @@ function createBaseCommand(config) {
|
|
|
270
290
|
humanInLoop: config.humanInLoop,
|
|
271
291
|
requires: cloneRequires(config.requires),
|
|
272
292
|
handler: config.handler,
|
|
273
|
-
render: config.render
|
|
293
|
+
render: config.render
|
|
274
294
|
};
|
|
275
295
|
Object.defineProperty(command, commandConfigSymbol, {
|
|
276
296
|
value: {
|
|
@@ -278,8 +298,8 @@ function createBaseCommand(config) {
|
|
|
278
298
|
humanInLoop: config.humanInLoop,
|
|
279
299
|
secrets: cloneSecrets(config.secrets),
|
|
280
300
|
requires: cloneRequires(config.requires),
|
|
281
|
-
sourcePath: inferCommandSourcePath()
|
|
282
|
-
}
|
|
301
|
+
sourcePath: inferCommandSourcePath()
|
|
302
|
+
}
|
|
283
303
|
});
|
|
284
304
|
return command;
|
|
285
305
|
}
|
|
@@ -294,7 +314,7 @@ function createBaseGroup(config) {
|
|
|
294
314
|
secrets: cloneSecrets(config.secrets),
|
|
295
315
|
requires: cloneRequires(config.requires),
|
|
296
316
|
children: [],
|
|
297
|
-
default: undefined
|
|
317
|
+
default: undefined
|
|
298
318
|
};
|
|
299
319
|
Object.defineProperty(group, groupConfigSymbol, {
|
|
300
320
|
value: {
|
|
@@ -306,8 +326,8 @@ function createBaseGroup(config) {
|
|
|
306
326
|
rename: cloneRenameMap(config.rename),
|
|
307
327
|
requires: cloneRequires(config.requires),
|
|
308
328
|
children: [...config.children],
|
|
309
|
-
default: config.default
|
|
310
|
-
}
|
|
329
|
+
default: config.default
|
|
330
|
+
}
|
|
311
331
|
});
|
|
312
332
|
return group;
|
|
313
333
|
}
|
|
@@ -332,7 +352,7 @@ function materializeCommand(command, inherited) {
|
|
|
332
352
|
humanInLoop: mergeHumanInLoopFromGroup(inherited.humanInLoop, internal.humanInLoop),
|
|
333
353
|
requires: mergeRequires(inherited.requires, internal.requires),
|
|
334
354
|
handler: command.handler,
|
|
335
|
-
render: command.render
|
|
355
|
+
render: command.render
|
|
336
356
|
};
|
|
337
357
|
Object.defineProperty(materialized, commandConfigSymbol, {
|
|
338
358
|
value: {
|
|
@@ -340,11 +360,11 @@ function materializeCommand(command, inherited) {
|
|
|
340
360
|
humanInLoop: internal.humanInLoop,
|
|
341
361
|
secrets: cloneSecrets(internal.secrets),
|
|
342
362
|
requires: cloneRequires(internal.requires),
|
|
343
|
-
sourcePath: internal.sourcePath
|
|
344
|
-
}
|
|
363
|
+
sourcePath: internal.sourcePath
|
|
364
|
+
}
|
|
345
365
|
});
|
|
346
366
|
Object.defineProperty(materialized, commandSourcePathSymbol, {
|
|
347
|
-
value: internal.sourcePath
|
|
367
|
+
value: internal.sourcePath
|
|
348
368
|
});
|
|
349
369
|
return materialized;
|
|
350
370
|
}
|
|
@@ -353,7 +373,7 @@ function mergeInheritedMetadata(group, inherited) {
|
|
|
353
373
|
scope: resolveGroupScope(group.scope, inherited.scope),
|
|
354
374
|
humanInLoop: mergeHumanInLoopFromGroup(inherited.humanInLoop, group.humanInLoop),
|
|
355
375
|
secrets: mergeSecrets(inherited.secrets, group.secrets),
|
|
356
|
-
requires: mergeRequires(inherited.requires, group.requires)
|
|
376
|
+
requires: mergeRequires(inherited.requires, group.requires)
|
|
357
377
|
};
|
|
358
378
|
}
|
|
359
379
|
function materializeGroup(group, inherited) {
|
|
@@ -364,11 +384,11 @@ function materializeGroup(group, inherited) {
|
|
|
364
384
|
if (internal.default !== undefined) {
|
|
365
385
|
const defaultIndex = internal.children.indexOf(internal.default);
|
|
366
386
|
if (defaultIndex === -1) {
|
|
367
|
-
throw new
|
|
387
|
+
throw new ToolcraftBugError(`Default command "${internal.default.name}" must be listed in children.`);
|
|
368
388
|
}
|
|
369
389
|
const resolvedDefault = materializedChildren[defaultIndex];
|
|
370
390
|
if (resolvedDefault?.kind !== "command") {
|
|
371
|
-
throw new
|
|
391
|
+
throw new ToolcraftBugError(`Default child "${internal.default.name}" must be a command.`);
|
|
372
392
|
}
|
|
373
393
|
defaultChild = resolvedDefault;
|
|
374
394
|
}
|
|
@@ -382,7 +402,7 @@ function materializeGroup(group, inherited) {
|
|
|
382
402
|
secrets: mergedInherited.secrets,
|
|
383
403
|
requires: mergedInherited.requires,
|
|
384
404
|
children: materializedChildren,
|
|
385
|
-
default: defaultChild
|
|
405
|
+
default: defaultChild
|
|
386
406
|
};
|
|
387
407
|
Object.defineProperty(materialized, groupConfigSymbol, {
|
|
388
408
|
value: {
|
|
@@ -394,8 +414,8 @@ function materializeGroup(group, inherited) {
|
|
|
394
414
|
rename: cloneRenameMap(internal.rename),
|
|
395
415
|
requires: cloneRequires(internal.requires),
|
|
396
416
|
children: [...internal.children],
|
|
397
|
-
default: internal.default
|
|
398
|
-
}
|
|
417
|
+
default: internal.default
|
|
418
|
+
}
|
|
399
419
|
});
|
|
400
420
|
return materialized;
|
|
401
421
|
}
|
|
@@ -411,7 +431,7 @@ export function defineCommand(config) {
|
|
|
411
431
|
scope: undefined,
|
|
412
432
|
humanInLoop: undefined,
|
|
413
433
|
secrets: {},
|
|
414
|
-
requires: undefined
|
|
434
|
+
requires: undefined
|
|
415
435
|
});
|
|
416
436
|
}
|
|
417
437
|
export function defineGroup(config) {
|
|
@@ -421,12 +441,12 @@ export function defineGroup(config) {
|
|
|
421
441
|
scope: undefined,
|
|
422
442
|
humanInLoop: undefined,
|
|
423
443
|
secrets: {},
|
|
424
|
-
requires: undefined
|
|
444
|
+
requires: undefined
|
|
425
445
|
});
|
|
426
446
|
}
|
|
427
447
|
export function getCommandSourcePath(command) {
|
|
428
448
|
return command[commandSourcePathSymbol];
|
|
429
449
|
}
|
|
430
450
|
export { S, toJsonSchema } from "toolcraft-schema";
|
|
431
|
-
export { ApprovalDeclinedError, UserError };
|
|
451
|
+
export { ApprovalDeclinedError, ToolcraftBugError, UserError };
|
|
432
452
|
export { findPackageMetadata, packageMetadata } from "./package-metadata.js";
|
|
@@ -2,17 +2,17 @@ import { S } from "toolcraft-schema";
|
|
|
2
2
|
export function convertJsonSchema(schema) {
|
|
3
3
|
if (hasSelfReferencingRef(schema, schema)) {
|
|
4
4
|
return applyMetadata(S.Json(), schema, {
|
|
5
|
-
nullable: schema.nullable === true
|
|
5
|
+
nullable: schema.nullable === true
|
|
6
6
|
});
|
|
7
7
|
}
|
|
8
|
-
return convertSchema(schema, schema);
|
|
8
|
+
return convertSchema(schema, schema, []);
|
|
9
9
|
}
|
|
10
|
-
function convertSchema(schema, root) {
|
|
11
|
-
const resolvedSchema = resolveReferencedSchema(schema, root);
|
|
10
|
+
function convertSchema(schema, root, path) {
|
|
11
|
+
const resolvedSchema = resolveReferencedSchema(schema, root, path);
|
|
12
12
|
const normalizedSchema = normalizeNullability(resolvedSchema);
|
|
13
|
-
const composition = normalizedSchema.schema
|
|
13
|
+
const composition = getComposition(normalizedSchema.schema);
|
|
14
14
|
if (Array.isArray(normalizedSchema.schema.type)) {
|
|
15
|
-
throw new Error(`
|
|
15
|
+
throw new Error(`JSON Schema "${formatJsonSchemaPath(path)}" has an unsupported type "${formatJsonSchemaType(normalizedSchema.schema.type)}". Supported: string, number, integer, boolean, array, object.`);
|
|
16
16
|
}
|
|
17
17
|
if (resolvedSchema.const !== undefined) {
|
|
18
18
|
return convertConstSchema(resolvedSchema, normalizedSchema.nullable);
|
|
@@ -21,11 +21,14 @@ function convertSchema(schema, root) {
|
|
|
21
21
|
return convertEnumSchema(resolvedSchema, normalizedSchema.nullable);
|
|
22
22
|
}
|
|
23
23
|
if (composition !== undefined) {
|
|
24
|
-
return convertCompositionSchema(normalizedSchema.schema, root, normalizedSchema.nullable);
|
|
24
|
+
return convertCompositionSchema(normalizedSchema.schema, root, normalizedSchema.nullable, path);
|
|
25
25
|
}
|
|
26
26
|
if (isRecordSchema(normalizedSchema.schema)) {
|
|
27
|
-
return applyMetadata(S.Record(convertSchema(normalizedSchema.schema.additionalProperties, root
|
|
28
|
-
|
|
27
|
+
return applyMetadata(S.Record(convertSchema(normalizedSchema.schema.additionalProperties, root, [
|
|
28
|
+
...path,
|
|
29
|
+
"additionalProperties"
|
|
30
|
+
])), normalizedSchema.schema, {
|
|
31
|
+
nullable: normalizedSchema.nullable
|
|
29
32
|
});
|
|
30
33
|
}
|
|
31
34
|
switch (normalizedSchema.schema.type) {
|
|
@@ -34,40 +37,41 @@ function convertSchema(schema, root) {
|
|
|
34
37
|
...createCommonOptions(normalizedSchema.schema, normalizedSchema.nullable, getStringDefault(normalizedSchema.schema.default)),
|
|
35
38
|
...(normalizedSchema.schema.pattern === undefined
|
|
36
39
|
? {}
|
|
37
|
-
: { pattern: normalizedSchema.schema.pattern })
|
|
40
|
+
: { pattern: normalizedSchema.schema.pattern })
|
|
38
41
|
});
|
|
39
42
|
case "number":
|
|
40
43
|
return S.Number(createCommonOptions(normalizedSchema.schema, normalizedSchema.nullable, getNumberDefault(normalizedSchema.schema.default)));
|
|
41
44
|
case "integer":
|
|
42
45
|
return S.Number({
|
|
43
46
|
...createCommonOptions(normalizedSchema.schema, normalizedSchema.nullable, getIntegerDefault(normalizedSchema.schema.default)),
|
|
44
|
-
jsonType: "integer"
|
|
47
|
+
jsonType: "integer"
|
|
45
48
|
});
|
|
46
49
|
case "boolean":
|
|
47
50
|
return S.Boolean(createCommonOptions(normalizedSchema.schema, normalizedSchema.nullable, getBooleanDefault(normalizedSchema.schema.default)));
|
|
48
51
|
case "array":
|
|
49
52
|
if (normalizedSchema.schema.items === undefined) {
|
|
50
|
-
throw new Error(
|
|
53
|
+
throw new Error(`JSON Schema "${formatJsonSchemaPath(path)}" is an array but is missing the "items" field. Add "items": { ... } to declare the element type.`);
|
|
51
54
|
}
|
|
52
|
-
return S.Array(convertSchema(normalizedSchema.schema.items, root), createCommonOptions(normalizedSchema.schema, normalizedSchema.nullable, getArrayDefault(normalizedSchema.schema.default)));
|
|
55
|
+
return S.Array(convertSchema(normalizedSchema.schema.items, root, [...path, "items"]), createCommonOptions(normalizedSchema.schema, normalizedSchema.nullable, getArrayDefault(normalizedSchema.schema.default)));
|
|
53
56
|
case "object":
|
|
54
57
|
return convertObjectSchema(normalizedSchema.schema, root, {
|
|
55
58
|
nullable: normalizedSchema.nullable,
|
|
59
|
+
path
|
|
56
60
|
});
|
|
57
61
|
case "null":
|
|
58
62
|
return applyMetadata(S.Json(), normalizedSchema.schema, {
|
|
59
63
|
default: getJsonDefault(normalizedSchema.schema.default) ?? null,
|
|
60
|
-
nullable: true
|
|
64
|
+
nullable: true
|
|
61
65
|
});
|
|
62
66
|
case undefined:
|
|
63
67
|
if (normalizedSchema.nullable) {
|
|
64
68
|
return applyMetadata(S.Json(), normalizedSchema.schema, {
|
|
65
|
-
nullable: true
|
|
69
|
+
nullable: true
|
|
66
70
|
});
|
|
67
71
|
}
|
|
68
|
-
throw new Error("
|
|
72
|
+
throw new Error(`JSON Schema "${formatJsonSchemaPath(path)}" must declare one of: "type", "enum", "const", "oneOf", "anyOf", or "allOf".`);
|
|
69
73
|
}
|
|
70
|
-
throw new Error(`
|
|
74
|
+
throw new Error(`JSON Schema "${formatJsonSchemaPath(path)}" has an unsupported type "${formatJsonSchemaType(normalizedSchema.schema.type)}". Supported: string, number, integer, boolean, array, object.`);
|
|
71
75
|
}
|
|
72
76
|
function convertConstSchema(schema, nullable) {
|
|
73
77
|
if (isPrimitiveEnumValue(schema.const)) {
|
|
@@ -76,13 +80,13 @@ function convertConstSchema(schema, nullable) {
|
|
|
76
80
|
default: schema.const,
|
|
77
81
|
...(schema.type === "integer" && typeof schema.const === "number"
|
|
78
82
|
? { jsonType: "integer" }
|
|
79
|
-
: {})
|
|
83
|
+
: {})
|
|
80
84
|
});
|
|
81
85
|
}
|
|
82
86
|
return applyMetadata(S.Json(), schema, {
|
|
83
87
|
default: schema.const,
|
|
84
88
|
nullable: nullable || schema.const === null,
|
|
85
|
-
description: appendDescription(schema.description, `Constant JSON value: ${JSON.stringify(schema.const)}.`)
|
|
89
|
+
description: appendDescription(schema.description, `Constant JSON value: ${JSON.stringify(schema.const)}.`)
|
|
86
90
|
});
|
|
87
91
|
}
|
|
88
92
|
function convertEnumSchema(schema, nullable) {
|
|
@@ -94,64 +98,75 @@ function convertEnumSchema(schema, nullable) {
|
|
|
94
98
|
...createCommonOptions(schema, nullable || hasNull, getPrimitiveEnumDefault(schema.default, nonNullValues)),
|
|
95
99
|
...(schema.type === "integer" && nonNullValues.every((value) => Number.isInteger(value))
|
|
96
100
|
? { jsonType: "integer" }
|
|
97
|
-
: {})
|
|
101
|
+
: {})
|
|
98
102
|
});
|
|
99
103
|
}
|
|
100
104
|
return applyMetadata(S.Json(), schema, {
|
|
101
105
|
nullable: nullable || hasNull,
|
|
102
|
-
description: appendDescription(schema.description, `Allowed JSON values: ${values.map((value) => JSON.stringify(value)).join(", ")}.`)
|
|
106
|
+
description: appendDescription(schema.description, `Allowed JSON values: ${values.map((value) => JSON.stringify(value)).join(", ")}.`)
|
|
103
107
|
});
|
|
104
108
|
}
|
|
105
|
-
function convertCompositionSchema(schema, root, nullable) {
|
|
106
|
-
const
|
|
107
|
-
const
|
|
109
|
+
function convertCompositionSchema(schema, root, nullable, path) {
|
|
110
|
+
const composition = getComposition(schema);
|
|
111
|
+
const branchSchemas = composition?.branches ?? [];
|
|
112
|
+
const keyword = composition?.keyword ?? "oneOf";
|
|
113
|
+
const branches = branchSchemas.map((branch, index) => resolveReferencedSchema(branch, root, [...path, keyword, String(index)]));
|
|
114
|
+
const discriminator = findDiscriminator(branches, root, path);
|
|
108
115
|
if (discriminator !== undefined) {
|
|
109
|
-
const convertedBranches = Object.fromEntries(branches.map((branch) => [
|
|
116
|
+
const convertedBranches = Object.fromEntries(branches.map((branch, index) => [
|
|
110
117
|
getDiscriminatorLiteral(branch, discriminator, root),
|
|
111
118
|
convertObjectSchema(branch, root, {
|
|
112
119
|
omitProperty: discriminator,
|
|
113
|
-
|
|
120
|
+
path: [...path, keyword, String(index)]
|
|
121
|
+
})
|
|
114
122
|
]));
|
|
115
123
|
return applyMetadata(S.OneOf({
|
|
116
124
|
discriminator,
|
|
117
|
-
branches: convertedBranches
|
|
125
|
+
branches: convertedBranches
|
|
118
126
|
}), schema, {
|
|
119
|
-
nullable
|
|
127
|
+
nullable
|
|
120
128
|
});
|
|
121
129
|
}
|
|
122
|
-
return applyMetadata(S.Union(branches.map((branch) => convertObjectSchema(branch, root, {
|
|
123
|
-
|
|
130
|
+
return applyMetadata(S.Union(branches.map((branch, index) => convertObjectSchema(branch, root, {
|
|
131
|
+
path: [...path, keyword, String(index)]
|
|
132
|
+
}))), schema, {
|
|
133
|
+
nullable
|
|
124
134
|
});
|
|
125
135
|
}
|
|
126
136
|
function convertObjectSchema(schema, root, options) {
|
|
127
|
-
const resolvedSchema = resolveReferencedSchema(schema, root);
|
|
137
|
+
const resolvedSchema = resolveReferencedSchema(schema, root, options.path);
|
|
128
138
|
const normalizedSchema = normalizeNullability(resolvedSchema);
|
|
129
139
|
const properties = normalizedSchema.schema.properties ?? {};
|
|
130
140
|
const requiredKeys = new Set(normalizedSchema.schema.required ?? []);
|
|
131
141
|
const shape = {};
|
|
132
|
-
if (normalizedSchema.schema.type !== "object" &&
|
|
133
|
-
|
|
142
|
+
if (normalizedSchema.schema.type !== "object" &&
|
|
143
|
+
normalizedSchema.schema.properties === undefined) {
|
|
144
|
+
throw new Error(`Expected "${formatJsonSchemaPath(options.path)}" to be an object schema (got "${describeObjectSchemaKind(normalizedSchema.schema)}").`);
|
|
134
145
|
}
|
|
135
146
|
for (const [key, propertySchema] of Object.entries(properties)) {
|
|
136
147
|
if (key === options.omitProperty) {
|
|
137
148
|
continue;
|
|
138
149
|
}
|
|
139
|
-
const convertedProperty = convertSchema(propertySchema, root
|
|
150
|
+
const convertedProperty = convertSchema(propertySchema, root, [
|
|
151
|
+
...options.path,
|
|
152
|
+
"properties",
|
|
153
|
+
key
|
|
154
|
+
]);
|
|
140
155
|
shape[key] = requiredKeys.has(key) ? convertedProperty : S.Optional(convertedProperty);
|
|
141
156
|
}
|
|
142
157
|
return applyMetadata(S.Object(shape, {
|
|
143
158
|
...(typeof normalizedSchema.schema.additionalProperties === "boolean"
|
|
144
159
|
? { additionalProperties: normalizedSchema.schema.additionalProperties }
|
|
145
|
-
: {})
|
|
160
|
+
: {})
|
|
146
161
|
}), normalizedSchema.schema, {
|
|
147
|
-
nullable: options.nullable ?? normalizedSchema.nullable
|
|
162
|
+
nullable: options.nullable ?? normalizedSchema.nullable
|
|
148
163
|
});
|
|
149
164
|
}
|
|
150
165
|
function createCommonOptions(schema, nullable, defaultValue) {
|
|
151
166
|
return {
|
|
152
167
|
...(schema.description === undefined ? {} : { description: schema.description }),
|
|
153
168
|
...(defaultValue === undefined ? {} : { default: defaultValue }),
|
|
154
|
-
...(nullable ? { nullable: true } : {})
|
|
169
|
+
...(nullable ? { nullable: true } : {})
|
|
155
170
|
};
|
|
156
171
|
}
|
|
157
172
|
function applyMetadata(schema, source, overrides) {
|
|
@@ -174,25 +189,53 @@ function normalizeNullability(schema) {
|
|
|
174
189
|
if (!Array.isArray(schema.type)) {
|
|
175
190
|
return {
|
|
176
191
|
schema,
|
|
177
|
-
nullable: schema.nullable === true
|
|
192
|
+
nullable: schema.nullable === true
|
|
178
193
|
};
|
|
179
194
|
}
|
|
180
195
|
const nextTypes = schema.type.filter((value) => value !== "null");
|
|
181
196
|
if (nextTypes.length === schema.type.length) {
|
|
182
197
|
return {
|
|
183
198
|
schema,
|
|
184
|
-
nullable: schema.nullable === true
|
|
199
|
+
nullable: schema.nullable === true
|
|
185
200
|
};
|
|
186
201
|
}
|
|
187
202
|
return {
|
|
188
203
|
schema: {
|
|
189
204
|
...schema,
|
|
190
205
|
type: nextTypes.length === 0 ? undefined : nextTypes.length === 1 ? nextTypes[0] : nextTypes,
|
|
191
|
-
nullable: undefined
|
|
206
|
+
nullable: undefined
|
|
192
207
|
},
|
|
193
|
-
nullable: true
|
|
208
|
+
nullable: true
|
|
194
209
|
};
|
|
195
210
|
}
|
|
211
|
+
function getComposition(schema) {
|
|
212
|
+
if (schema.oneOf !== undefined) {
|
|
213
|
+
return { keyword: "oneOf", branches: schema.oneOf };
|
|
214
|
+
}
|
|
215
|
+
if (schema.anyOf !== undefined) {
|
|
216
|
+
return { keyword: "anyOf", branches: schema.anyOf };
|
|
217
|
+
}
|
|
218
|
+
if (schema.allOf !== undefined) {
|
|
219
|
+
return { keyword: "allOf", branches: schema.allOf };
|
|
220
|
+
}
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
function formatJsonSchemaPath(path) {
|
|
224
|
+
if (path.length === 0) {
|
|
225
|
+
return "#";
|
|
226
|
+
}
|
|
227
|
+
return `#/${path.map(escapeJsonPointerSegment).join("/")}`;
|
|
228
|
+
}
|
|
229
|
+
function formatJsonSchemaType(type) {
|
|
230
|
+
return Array.isArray(type) ? JSON.stringify(type) : String(type);
|
|
231
|
+
}
|
|
232
|
+
function describeObjectSchemaKind(schema) {
|
|
233
|
+
const type = schema.type;
|
|
234
|
+
if (typeof type === "string") {
|
|
235
|
+
return type;
|
|
236
|
+
}
|
|
237
|
+
return type === undefined ? "unknown" : JSON.stringify(type);
|
|
238
|
+
}
|
|
196
239
|
function isRecordSchema(schema) {
|
|
197
240
|
const propertyKeys = Object.keys(schema.properties ?? {});
|
|
198
241
|
return (schema.type === "object" &&
|
|
@@ -200,10 +243,10 @@ function isRecordSchema(schema) {
|
|
|
200
243
|
typeof schema.additionalProperties === "object" &&
|
|
201
244
|
schema.additionalProperties !== null);
|
|
202
245
|
}
|
|
203
|
-
function findDiscriminator(branches, root) {
|
|
246
|
+
function findDiscriminator(branches, root, path) {
|
|
204
247
|
const [firstBranch] = branches;
|
|
205
248
|
if (firstBranch === undefined) {
|
|
206
|
-
throw new Error(
|
|
249
|
+
throw new Error(`JSON Schema "${formatJsonSchemaPath(path)}" uses oneOf/anyOf/allOf but has no branches.`);
|
|
207
250
|
}
|
|
208
251
|
const candidateKeys = Object.keys(firstBranch.properties ?? {});
|
|
209
252
|
for (const candidate of candidateKeys) {
|
|
@@ -233,7 +276,7 @@ function getDiscriminatorLiteral(branch, key, root) {
|
|
|
233
276
|
if (propertySchema === undefined) {
|
|
234
277
|
return undefined;
|
|
235
278
|
}
|
|
236
|
-
const resolvedProperty = resolveReferencedSchema(propertySchema, root);
|
|
279
|
+
const resolvedProperty = resolveReferencedSchema(propertySchema, root, []);
|
|
237
280
|
if (typeof resolvedProperty.const === "string") {
|
|
238
281
|
return resolvedProperty.const;
|
|
239
282
|
}
|
|
@@ -244,17 +287,17 @@ function getDiscriminatorLiteral(branch, key, root) {
|
|
|
244
287
|
}
|
|
245
288
|
return undefined;
|
|
246
289
|
}
|
|
247
|
-
function resolveReferencedSchema(schema, root) {
|
|
290
|
+
function resolveReferencedSchema(schema, root, path) {
|
|
248
291
|
if (schema.$ref === undefined) {
|
|
249
292
|
return schema;
|
|
250
293
|
}
|
|
251
294
|
const resolvedTarget = resolveLocalRef(root, schema.$ref);
|
|
252
295
|
if (resolvedTarget === undefined) {
|
|
253
|
-
throw new Error(`
|
|
296
|
+
throw new Error(`JSON Schema "${formatJsonSchemaPath(path)}" uses "$ref": ${schema.$ref}. toolcraft only supports internal refs like "#/components/schemas/Foo".`);
|
|
254
297
|
}
|
|
255
298
|
const { $ref: ignoredRef, ...siblingKeywords } = schema;
|
|
256
299
|
void ignoredRef;
|
|
257
|
-
const resolvedSchema = resolveReferencedSchema(resolvedTarget, root);
|
|
300
|
+
const resolvedSchema = resolveReferencedSchema(resolvedTarget, root, path);
|
|
258
301
|
if (Object.keys(siblingKeywords).length === 0) {
|
|
259
302
|
return resolvedSchema;
|
|
260
303
|
}
|
|
@@ -265,13 +308,13 @@ function mergeJsonSchemas(base, overlay) {
|
|
|
265
308
|
? undefined
|
|
266
309
|
: {
|
|
267
310
|
...(base.properties ?? {}),
|
|
268
|
-
...(overlay.properties ?? {})
|
|
311
|
+
...(overlay.properties ?? {})
|
|
269
312
|
};
|
|
270
313
|
const mergedDefs = base.$defs === undefined && overlay.$defs === undefined
|
|
271
314
|
? undefined
|
|
272
315
|
: {
|
|
273
316
|
...(base.$defs ?? {}),
|
|
274
|
-
...(overlay.$defs ?? {})
|
|
317
|
+
...(overlay.$defs ?? {})
|
|
275
318
|
};
|
|
276
319
|
const mergedRequired = base.required === undefined && overlay.required === undefined
|
|
277
320
|
? undefined
|
|
@@ -281,7 +324,7 @@ function mergeJsonSchemas(base, overlay) {
|
|
|
281
324
|
...overlay,
|
|
282
325
|
...(mergedDefs === undefined ? {} : { $defs: mergedDefs }),
|
|
283
326
|
...(mergedProperties === undefined ? {} : { properties: mergedProperties }),
|
|
284
|
-
...(mergedRequired === undefined ? {} : { required: mergedRequired })
|
|
327
|
+
...(mergedRequired === undefined ? {} : { required: mergedRequired })
|
|
285
328
|
};
|
|
286
329
|
}
|
|
287
330
|
function hasSelfReferencingRef(schema, root, path = "#", activePaths = new Set()) {
|
|
@@ -293,11 +336,13 @@ function hasSelfReferencingRef(schema, root, path = "#", activePaths = new Set()
|
|
|
293
336
|
return true;
|
|
294
337
|
}
|
|
295
338
|
const target = resolveLocalRef(root, localRefPath);
|
|
296
|
-
if (target !== undefined &&
|
|
339
|
+
if (target !== undefined &&
|
|
340
|
+
hasSelfReferencingRef(target, root, localRefPath, nextActivePaths)) {
|
|
297
341
|
return true;
|
|
298
342
|
}
|
|
299
343
|
}
|
|
300
|
-
if (schema.items !== undefined &&
|
|
344
|
+
if (schema.items !== undefined &&
|
|
345
|
+
hasSelfReferencingRef(schema.items, root, `${path}/items`, nextActivePaths)) {
|
|
301
346
|
return true;
|
|
302
347
|
}
|
|
303
348
|
if (typeof schema.additionalProperties === "object" &&
|
|
@@ -325,6 +370,11 @@ function hasSelfReferencingRef(schema, root, path = "#", activePaths = new Set()
|
|
|
325
370
|
return true;
|
|
326
371
|
}
|
|
327
372
|
}
|
|
373
|
+
for (const [index, childSchema] of (schema.allOf ?? []).entries()) {
|
|
374
|
+
if (hasSelfReferencingRef(childSchema, root, `${path}/allOf/${index}`, nextActivePaths)) {
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
328
378
|
return false;
|
|
329
379
|
}
|
|
330
380
|
function getLocalRefPath(ref) {
|
package/dist/mcp-proxy.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface ResolveMcpProxyOptions {
|
|
|
5
5
|
projectRoot?: string;
|
|
6
6
|
}
|
|
7
7
|
export declare function hasMcpProxyGroups(root: Group<any>): boolean;
|
|
8
|
+
export declare function findProjectRoot(from?: string): string | undefined;
|
|
8
9
|
export declare function resolveCachePath(name: string, projectRoot?: string): string;
|
|
9
10
|
export declare function parseRefreshEnv(value: string | undefined): "all" | Set<string> | undefined;
|
|
10
11
|
export declare function dialUpstream(name: string, config: McpServerConfig): Promise<McpClient>;
|
package/dist/mcp-proxy.js
CHANGED
|
@@ -327,22 +327,29 @@ function collectProxyGroups(root) {
|
|
|
327
327
|
export function hasMcpProxyGroups(root) {
|
|
328
328
|
return collectProxyGroups(root).length > 0;
|
|
329
329
|
}
|
|
330
|
-
export function
|
|
331
|
-
if (projectRoot !== undefined) {
|
|
332
|
-
return path.join(projectRoot, ".toolcraft", "mcp", `${name}.json`);
|
|
333
|
-
}
|
|
330
|
+
export function findProjectRoot(from = process.cwd()) {
|
|
334
331
|
let current = process.cwd();
|
|
332
|
+
if (from !== current) {
|
|
333
|
+
current = path.resolve(from);
|
|
334
|
+
}
|
|
335
335
|
while (true) {
|
|
336
336
|
if (existsSync(path.join(current, "package.json"))) {
|
|
337
|
-
return
|
|
337
|
+
return current;
|
|
338
338
|
}
|
|
339
339
|
const parent = path.dirname(current);
|
|
340
340
|
if (parent === current) {
|
|
341
|
-
|
|
341
|
+
return undefined;
|
|
342
342
|
}
|
|
343
343
|
current = parent;
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
|
+
export function resolveCachePath(name, projectRoot) {
|
|
347
|
+
const resolvedProjectRoot = projectRoot ?? findProjectRoot();
|
|
348
|
+
if (resolvedProjectRoot === undefined) {
|
|
349
|
+
throw new Error(`Could not find package.json above "${process.cwd()}" while resolving MCP cache path.`);
|
|
350
|
+
}
|
|
351
|
+
return path.join(resolvedProjectRoot, ".toolcraft", "mcp", `${name}.json`);
|
|
352
|
+
}
|
|
346
353
|
export function parseRefreshEnv(value) {
|
|
347
354
|
const trimmed = value?.trim();
|
|
348
355
|
if (trimmed === undefined || trimmed.length === 0) {
|