toolcraft 0.0.17 → 0.0.18
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/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/text.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/components/text.js +8 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +8 -1
- 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 +3 -0
- package/node_modules/@poe-code/design-system/dist/index.js +3 -0
- package/node_modules/@poe-code/design-system/package.json +1 -0
- package/package.json +6 -2
package/dist/sdk.js
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import { access, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import {
|
|
2
|
+
import { ToolcraftBugError, assertCommandRequirements, resolveCommandSecrets } from "./index.js";
|
|
3
|
+
import { writeErrorReport } from "./error-report.js";
|
|
3
4
|
import { mergeApprovalsGroup } from "./human-in-loop/approvals-commands.js";
|
|
4
5
|
import { invokeWithHumanInLoop } from "./human-in-loop/index.js";
|
|
5
6
|
import { hasMcpProxyGroups, resolveMcpProxies } from "./mcp-proxy.js";
|
|
6
7
|
import { getExpectedNumberDescription, isValidNumberSchemaValue } from "./number-schema.js";
|
|
7
8
|
import { filterSchemaForScope } from "./schema-scope.js";
|
|
8
|
-
|
|
9
|
+
import { enableSourceMaps } from "./stack-trim.js";
|
|
10
|
+
import { suggest } from "./suggest.js";
|
|
11
|
+
import { throwValidationErrors } from "./validation-errors.js";
|
|
12
|
+
const RESERVED_SERVICE_NAMES = new Set([
|
|
13
|
+
"params",
|
|
14
|
+
"secrets",
|
|
15
|
+
"fetch",
|
|
16
|
+
"fs",
|
|
17
|
+
"env",
|
|
18
|
+
"progress",
|
|
19
|
+
"runtimeOptions",
|
|
20
|
+
"root"
|
|
21
|
+
]);
|
|
22
|
+
const RESERVED_SERVICE_NAMES_MESSAGE = "Available reserved names: params, secrets, fetch, fs, env, progress, runtimeOptions, root.";
|
|
9
23
|
function splitWords(value) {
|
|
10
24
|
const words = [];
|
|
11
25
|
let current = "";
|
|
@@ -24,7 +38,9 @@ function splitWords(value) {
|
|
|
24
38
|
const isUppercase = char !== lower && char === upper;
|
|
25
39
|
const previous = value[index - 1];
|
|
26
40
|
const next = value[index + 1];
|
|
27
|
-
const previousIsLowercase = previous !== undefined &&
|
|
41
|
+
const previousIsLowercase = previous !== undefined &&
|
|
42
|
+
previous === previous.toLowerCase() &&
|
|
43
|
+
previous !== previous.toUpperCase();
|
|
28
44
|
const nextIsLowercase = next !== undefined && next === next.toLowerCase() && next !== next.toUpperCase();
|
|
29
45
|
if (isUppercase && current.length > 0 && (previousIsLowercase || nextIsLowercase)) {
|
|
30
46
|
words.push(current.toLowerCase());
|
|
@@ -69,30 +85,52 @@ function createFs() {
|
|
|
69
85
|
catch {
|
|
70
86
|
return false;
|
|
71
87
|
}
|
|
72
|
-
}
|
|
88
|
+
}
|
|
73
89
|
};
|
|
74
90
|
}
|
|
75
91
|
function createEnv(values = process.env) {
|
|
76
92
|
return {
|
|
77
93
|
get(key) {
|
|
78
94
|
return values[key];
|
|
79
|
-
}
|
|
95
|
+
}
|
|
80
96
|
};
|
|
81
97
|
}
|
|
82
98
|
function validateServices(services) {
|
|
83
99
|
for (const name of Object.keys(services)) {
|
|
84
100
|
if (RESERVED_SERVICE_NAMES.has(name)) {
|
|
85
|
-
throw new Error(`Service name "${name}" is reserved. Choose a different name
|
|
101
|
+
throw new Error(`Service name "${name}" is reserved. Choose a different name. ${RESERVED_SERVICE_NAMES_MESSAGE}`);
|
|
86
102
|
}
|
|
87
103
|
}
|
|
88
104
|
}
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
|
|
105
|
+
function formatAvailableList(values) {
|
|
106
|
+
return `Available: ${[...values].sort().join(", ")}.`;
|
|
107
|
+
}
|
|
108
|
+
function formatEnumError(value, schema, label) {
|
|
109
|
+
const suggestionLine = typeof value === "string"
|
|
110
|
+
? formatEnumSuggestionLine(value, schema.values.map((candidate) => String(candidate)))
|
|
111
|
+
: " ";
|
|
112
|
+
return `Invalid value for "${label}".${suggestionLine}Expected one of: ${schema.values.map((candidate) => String(candidate)).join(", ")}, got ${describeReceived(value)}.`;
|
|
113
|
+
}
|
|
114
|
+
function formatEnumSuggestionLine(value, values) {
|
|
115
|
+
const suggestions = suggest(value, values);
|
|
116
|
+
return suggestions.length > 0 ? ` Did you mean: ${suggestions.join(", ")}?\n` : " ";
|
|
117
|
+
}
|
|
118
|
+
function describeReceived(value) {
|
|
119
|
+
if (value === null)
|
|
120
|
+
return "null";
|
|
121
|
+
if (value === undefined)
|
|
122
|
+
return "missing";
|
|
123
|
+
if (Array.isArray(value))
|
|
124
|
+
return `array(${value.length})`;
|
|
125
|
+
if (typeof value === "object")
|
|
126
|
+
return "object";
|
|
127
|
+
if (typeof value === "string") {
|
|
128
|
+
const s = value.length > 40 ? `${value.slice(0, 40)}…` : value;
|
|
129
|
+
return `${JSON.stringify(s)}`;
|
|
92
130
|
}
|
|
93
|
-
return value;
|
|
131
|
+
return JSON.stringify(value);
|
|
94
132
|
}
|
|
95
|
-
function validateSchemaValue(schema, value, label) {
|
|
133
|
+
function validateSchemaValue(schema, value, label, errors) {
|
|
96
134
|
const unwrappedSchema = unwrapOptional(schema);
|
|
97
135
|
if (value === null && unwrappedSchema.nullable === true) {
|
|
98
136
|
return null;
|
|
@@ -100,33 +138,53 @@ function validateSchemaValue(schema, value, label) {
|
|
|
100
138
|
switch (unwrappedSchema.kind) {
|
|
101
139
|
case "string":
|
|
102
140
|
if (typeof value !== "string") {
|
|
103
|
-
|
|
141
|
+
errors.push({
|
|
142
|
+
path: label,
|
|
143
|
+
message: `Invalid value for "${label}". Expected a string, got ${describeReceived(value)}.`
|
|
144
|
+
});
|
|
104
145
|
}
|
|
105
146
|
return value;
|
|
106
147
|
case "number":
|
|
107
148
|
if (!isValidNumberSchemaValue(value, unwrappedSchema)) {
|
|
108
|
-
|
|
149
|
+
errors.push({
|
|
150
|
+
path: label,
|
|
151
|
+
message: `Invalid value for "${label}". Expected ${getExpectedNumberDescription(unwrappedSchema)}, got ${describeReceived(value)}.`
|
|
152
|
+
});
|
|
109
153
|
}
|
|
110
154
|
return value;
|
|
111
155
|
case "boolean":
|
|
112
156
|
if (typeof value !== "boolean") {
|
|
113
|
-
|
|
157
|
+
errors.push({
|
|
158
|
+
path: label,
|
|
159
|
+
message: `Invalid value for "${label}". Expected a boolean, got ${describeReceived(value)}.`
|
|
160
|
+
});
|
|
114
161
|
}
|
|
115
162
|
return value;
|
|
116
163
|
case "enum":
|
|
117
|
-
|
|
164
|
+
if (!unwrappedSchema.values.includes(value)) {
|
|
165
|
+
errors.push({ path: label, message: formatEnumError(value, unwrappedSchema, label) });
|
|
166
|
+
}
|
|
167
|
+
return value;
|
|
118
168
|
case "array":
|
|
119
169
|
if (!Array.isArray(value)) {
|
|
120
|
-
|
|
170
|
+
errors.push({
|
|
171
|
+
path: label,
|
|
172
|
+
message: `Invalid value for "${label}". Expected an array, got ${describeReceived(value)}.`
|
|
173
|
+
});
|
|
174
|
+
return value;
|
|
121
175
|
}
|
|
122
|
-
return value.map((item, index) => validateSchemaValue(unwrappedSchema.item, item, `${label}[${index}]
|
|
176
|
+
return value.map((item, index) => validateSchemaValue(unwrappedSchema.item, item, `${label}[${index}]`, errors));
|
|
123
177
|
case "object":
|
|
124
|
-
return validateObjectSchema(unwrappedSchema, value, label);
|
|
178
|
+
return validateObjectSchema(unwrappedSchema, value, label, errors);
|
|
125
179
|
}
|
|
126
180
|
}
|
|
127
|
-
function validateObjectSchema(schema, value, label) {
|
|
181
|
+
function validateObjectSchema(schema, value, label, errors) {
|
|
128
182
|
if (!isPlainObject(value)) {
|
|
129
|
-
|
|
183
|
+
errors.push({
|
|
184
|
+
path: label,
|
|
185
|
+
message: `Invalid value for "${label}". Expected an object, got ${describeReceived(value)}.`
|
|
186
|
+
});
|
|
187
|
+
return {};
|
|
130
188
|
}
|
|
131
189
|
const result = {};
|
|
132
190
|
const expectedKeys = new Map();
|
|
@@ -136,7 +194,10 @@ function validateObjectSchema(schema, value, label) {
|
|
|
136
194
|
for (const key of Object.keys(value)) {
|
|
137
195
|
if (!expectedKeys.has(key)) {
|
|
138
196
|
const fieldLabel = label.length === 0 ? key : `${label}.${key}`;
|
|
139
|
-
|
|
197
|
+
errors.push({
|
|
198
|
+
path: fieldLabel,
|
|
199
|
+
message: `Unexpected parameter "${fieldLabel}". ${formatAvailableList([...expectedKeys.keys()].map((expectedKey) => label.length === 0 ? expectedKey : `${label}.${expectedKey}`))}`
|
|
200
|
+
});
|
|
140
201
|
}
|
|
141
202
|
}
|
|
142
203
|
for (const [inputKey, [outputKey, rawChildSchema]] of expectedKeys.entries()) {
|
|
@@ -151,14 +212,18 @@ function validateObjectSchema(schema, value, label) {
|
|
|
151
212
|
if (isOptional(rawChildSchema)) {
|
|
152
213
|
continue;
|
|
153
214
|
}
|
|
154
|
-
|
|
215
|
+
errors.push({ path: fieldLabel, message: `Missing required parameter "${fieldLabel}".` });
|
|
216
|
+
continue;
|
|
155
217
|
}
|
|
156
|
-
result[outputKey] = validateSchemaValue(rawChildSchema, value[inputKey], fieldLabel);
|
|
218
|
+
result[outputKey] = validateSchemaValue(rawChildSchema, value[inputKey], fieldLabel, errors);
|
|
157
219
|
}
|
|
158
220
|
return result;
|
|
159
221
|
}
|
|
160
222
|
function validateSDKArguments(schema, argumentsValue) {
|
|
161
|
-
|
|
223
|
+
const errors = [];
|
|
224
|
+
const result = validateObjectSchema(schema, argumentsValue ?? {}, "", errors);
|
|
225
|
+
throwValidationErrors(errors);
|
|
226
|
+
return result;
|
|
162
227
|
}
|
|
163
228
|
function defineMember(target, key, value) {
|
|
164
229
|
if (Object.prototype.hasOwnProperty.call(target, key)) {
|
|
@@ -168,10 +233,11 @@ function defineMember(target, key, value) {
|
|
|
168
233
|
value,
|
|
169
234
|
enumerable: true,
|
|
170
235
|
configurable: false,
|
|
171
|
-
writable: false
|
|
236
|
+
writable: false
|
|
172
237
|
});
|
|
173
238
|
}
|
|
174
239
|
export function createSDK(root, options = {}) {
|
|
240
|
+
enableSourceMaps();
|
|
175
241
|
const mergedRoot = mergeApprovalsGroup(root);
|
|
176
242
|
if (!hasMcpProxyGroups(mergedRoot)) {
|
|
177
243
|
return createResolvedSDK(mergedRoot, options);
|
|
@@ -186,29 +252,47 @@ function createResolvedSDK(root, options = {}) {
|
|
|
186
252
|
function build(node, path) {
|
|
187
253
|
if (node.kind === "command") {
|
|
188
254
|
return async (params) => {
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
255
|
+
const commandPath = [...path, node.name].join(".");
|
|
256
|
+
let secrets;
|
|
257
|
+
let validatedParams;
|
|
258
|
+
try {
|
|
259
|
+
secrets = resolveCommandSecrets(node);
|
|
260
|
+
const baseContext = {
|
|
261
|
+
...services,
|
|
262
|
+
runtimeOptions,
|
|
263
|
+
root,
|
|
264
|
+
secrets,
|
|
265
|
+
fetch: globalThis.fetch,
|
|
266
|
+
fs: createFs(),
|
|
267
|
+
env: createEnv(),
|
|
268
|
+
progress() {
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
await assertCommandRequirements(node, { ...baseContext, params: undefined });
|
|
273
|
+
const paramsSchema = filterSchemaForScope(node.params, "sdk");
|
|
274
|
+
if (paramsSchema === undefined || paramsSchema.kind !== "object") {
|
|
275
|
+
throw new ToolcraftBugError(`command "${node.name}" must define an object params schema for SDK.`);
|
|
276
|
+
}
|
|
277
|
+
validatedParams = validateSDKArguments(paramsSchema, params);
|
|
278
|
+
return await invokeWithHumanInLoop(node, {
|
|
279
|
+
...baseContext,
|
|
280
|
+
params: validatedParams
|
|
281
|
+
}, runtimeOptions, commandPath);
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
await writeErrorReport({
|
|
285
|
+
command: node,
|
|
286
|
+
commandPath,
|
|
287
|
+
env: process.env,
|
|
288
|
+
error,
|
|
289
|
+
errorReports: options.errorReports,
|
|
290
|
+
params: validatedParams,
|
|
291
|
+
projectRoot: options.projectRoot,
|
|
292
|
+
secrets
|
|
293
|
+
});
|
|
294
|
+
throw error;
|
|
206
295
|
}
|
|
207
|
-
const validatedParams = validateSDKArguments(paramsSchema, params);
|
|
208
|
-
return invokeWithHumanInLoop(node, {
|
|
209
|
-
...baseContext,
|
|
210
|
-
params: validatedParams,
|
|
211
|
-
}, runtimeOptions, [...path, node.name].join("."));
|
|
212
296
|
};
|
|
213
297
|
}
|
|
214
298
|
const output = {};
|
|
@@ -263,7 +347,7 @@ function createDeferredSDK(root, options) {
|
|
|
263
347
|
return path.length === 0 ? resolveSDK().then.bind(resolveSDK()) : undefined;
|
|
264
348
|
}
|
|
265
349
|
return createPathProxy([...path, property]);
|
|
266
|
-
}
|
|
350
|
+
}
|
|
267
351
|
});
|
|
268
352
|
return createPathProxy([]);
|
|
269
353
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { text } from "@poe-code/design-system";
|
|
2
|
+
export function renderSourceSnippet(opts) {
|
|
3
|
+
const lines = opts.source.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
|
|
4
|
+
const line = clampInteger(opts.line, 1, Math.max(lines.length, 1));
|
|
5
|
+
const context = Math.max(0, Math.floor(opts.context ?? 2));
|
|
6
|
+
const startLine = Math.max(1, line - context);
|
|
7
|
+
const endLine = Math.min(lines.length, line + context);
|
|
8
|
+
const gutterWidth = String(endLine).length;
|
|
9
|
+
const output = [];
|
|
10
|
+
if (opts.filePath !== undefined) {
|
|
11
|
+
output.push(muted(`--> ${opts.filePath}:${line}${opts.column === undefined ? "" : `:${Math.max(1, opts.column)}`}`));
|
|
12
|
+
}
|
|
13
|
+
output.push(renderDivider(gutterWidth));
|
|
14
|
+
for (let currentLine = startLine; currentLine <= endLine; currentLine += 1) {
|
|
15
|
+
const sourceLine = lines[currentLine - 1] ?? "";
|
|
16
|
+
output.push(`${muted(String(currentLine).padStart(gutterWidth, " "))} | ${sourceLine}`);
|
|
17
|
+
if (currentLine === line && opts.column !== undefined) {
|
|
18
|
+
const column = Math.max(1, Math.floor(opts.column));
|
|
19
|
+
output.push(`${muted(" ".repeat(gutterWidth))} | ${" ".repeat(column - 1)}${error("^")}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
output.push(renderDivider(gutterWidth));
|
|
23
|
+
return output.join("\n");
|
|
24
|
+
}
|
|
25
|
+
function renderDivider(gutterWidth) {
|
|
26
|
+
return `${muted(" ".repeat(gutterWidth))} |`;
|
|
27
|
+
}
|
|
28
|
+
function clampInteger(value, min, max) {
|
|
29
|
+
if (!Number.isFinite(value)) {
|
|
30
|
+
return min;
|
|
31
|
+
}
|
|
32
|
+
return Math.min(max, Math.max(min, Math.floor(value)));
|
|
33
|
+
}
|
|
34
|
+
function muted(value) {
|
|
35
|
+
return shouldStyleStderr() ? text.muted(value) : value;
|
|
36
|
+
}
|
|
37
|
+
function error(value) {
|
|
38
|
+
return shouldStyleStderr() ? text.error(value) : value;
|
|
39
|
+
}
|
|
40
|
+
function shouldStyleStderr() {
|
|
41
|
+
return process.stderr.isTTY === true;
|
|
42
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const HIDDEN_FRAME_SUMMARY_PREFIX = " … (";
|
|
2
|
+
const HIDDEN_FRAME_SUMMARY_SUFFIX = " hidden — pass --debug=raw to show)";
|
|
3
|
+
export function enableSourceMaps() {
|
|
4
|
+
process.setSourceMapsEnabled?.(true);
|
|
5
|
+
}
|
|
6
|
+
export function formatDebugStack(stack, mode) {
|
|
7
|
+
return mode === "raw" ? stack : trimStack(stack);
|
|
8
|
+
}
|
|
9
|
+
export function trimStack(stack) {
|
|
10
|
+
const sections = splitStackSections(stack);
|
|
11
|
+
const trimmed = sections.map((section) => trimStackSection(section));
|
|
12
|
+
const hiddenFrameCount = trimmed.reduce((count, section) => count + section.hiddenFrameCount, 0);
|
|
13
|
+
if (hiddenFrameCount === 0) {
|
|
14
|
+
return stack;
|
|
15
|
+
}
|
|
16
|
+
return trimmed.flatMap((section) => section.lines).join("\n");
|
|
17
|
+
}
|
|
18
|
+
function splitStackSections(stack) {
|
|
19
|
+
const lines = stack.split("\n");
|
|
20
|
+
const firstLine = lines[0];
|
|
21
|
+
if (firstLine === undefined) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
const sections = [{ header: firstLine, lines: [] }];
|
|
25
|
+
for (const line of lines.slice(1)) {
|
|
26
|
+
if (isCauseHeader(line)) {
|
|
27
|
+
sections.push({ header: line, lines: [] });
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
sections[sections.length - 1]?.lines.push(line);
|
|
31
|
+
}
|
|
32
|
+
return sections;
|
|
33
|
+
}
|
|
34
|
+
function trimStackSection(section) {
|
|
35
|
+
const userFrames = [];
|
|
36
|
+
const skippedFrames = [];
|
|
37
|
+
for (const line of section.lines) {
|
|
38
|
+
if (isFrameworkOrRuntimeFrame(line)) {
|
|
39
|
+
skippedFrames.push(line);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
userFrames.push(line);
|
|
43
|
+
}
|
|
44
|
+
if (skippedFrames.length === 0) {
|
|
45
|
+
return {
|
|
46
|
+
lines: [section.header, ...section.lines],
|
|
47
|
+
hiddenFrameCount: 0
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
lines: [section.header, ...userFrames, formatHiddenFrameSummary(skippedFrames.length)],
|
|
52
|
+
hiddenFrameCount: skippedFrames.length
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function isCauseHeader(line) {
|
|
56
|
+
return line.trimStart().startsWith("[cause]:");
|
|
57
|
+
}
|
|
58
|
+
function isFrameworkOrRuntimeFrame(line) {
|
|
59
|
+
const normalized = line.replaceAll("\\", "/");
|
|
60
|
+
return (normalized.includes("node_modules/toolcraft/") ||
|
|
61
|
+
normalized.includes("node_modules/toolcraft-openapi/") ||
|
|
62
|
+
normalized.includes("node_modules/toolcraft-schema/") ||
|
|
63
|
+
normalized.includes("node_modules/commander/") ||
|
|
64
|
+
normalized.includes("node:internal/") ||
|
|
65
|
+
normalized.includes("/packages/toolcraft/src/"));
|
|
66
|
+
}
|
|
67
|
+
function formatHiddenFrameSummary(count) {
|
|
68
|
+
const plural = count === 1 ? "" : "s";
|
|
69
|
+
return `${HIDDEN_FRAME_SUMMARY_PREFIX}${count} framework / runtime frame${plural}${HIDDEN_FRAME_SUMMARY_SUFFIX}`;
|
|
70
|
+
}
|
package/dist/suggest.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export function suggest(input, candidates, opts = {}) {
|
|
2
|
+
if (input.length === 0) {
|
|
3
|
+
return [];
|
|
4
|
+
}
|
|
5
|
+
const max = opts.max ?? 3;
|
|
6
|
+
const threshold = opts.threshold ?? Math.max(1, Math.floor(input.length / 4));
|
|
7
|
+
return candidates
|
|
8
|
+
.map((candidate) => ({
|
|
9
|
+
candidate,
|
|
10
|
+
distance: damerauLevenshtein(input, candidate)
|
|
11
|
+
}))
|
|
12
|
+
.filter(({ distance }) => distance <= threshold)
|
|
13
|
+
.sort((left, right) => {
|
|
14
|
+
if (left.distance !== right.distance) {
|
|
15
|
+
return left.distance - right.distance;
|
|
16
|
+
}
|
|
17
|
+
return left.candidate.localeCompare(right.candidate);
|
|
18
|
+
})
|
|
19
|
+
.slice(0, max)
|
|
20
|
+
.map(({ candidate }) => candidate);
|
|
21
|
+
}
|
|
22
|
+
function damerauLevenshtein(left, right) {
|
|
23
|
+
const distances = Array.from({ length: left.length + 1 }, () => Array.from({ length: right.length + 1 }, () => 0));
|
|
24
|
+
for (let row = 0; row <= left.length; row += 1) {
|
|
25
|
+
distances[row][0] = row;
|
|
26
|
+
}
|
|
27
|
+
for (let column = 0; column <= right.length; column += 1) {
|
|
28
|
+
distances[0][column] = column;
|
|
29
|
+
}
|
|
30
|
+
for (let row = 1; row <= left.length; row += 1) {
|
|
31
|
+
for (let column = 1; column <= right.length; column += 1) {
|
|
32
|
+
const substitutionCost = left[row - 1] === right[column - 1] ? 0 : 1;
|
|
33
|
+
const deletion = distances[row - 1][column] + 1;
|
|
34
|
+
const insertion = distances[row][column - 1] + 1;
|
|
35
|
+
const substitution = distances[row - 1][column - 1] + substitutionCost;
|
|
36
|
+
distances[row][column] = Math.min(deletion, insertion, substitution);
|
|
37
|
+
if (row > 1 &&
|
|
38
|
+
column > 1 &&
|
|
39
|
+
left[row - 1] === right[column - 2] &&
|
|
40
|
+
left[row - 2] === right[column - 1]) {
|
|
41
|
+
distances[row][column] = Math.min(distances[row][column], distances[row - 2][column - 2] + 1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return distances[left.length][right.length];
|
|
46
|
+
}
|
package/dist/user-error.d.ts
CHANGED
package/dist/user-error.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export class UserError extends Error {
|
|
2
|
+
constructor(message, options) {
|
|
3
|
+
super(message, options);
|
|
4
|
+
this.name = "UserError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class ToolcraftBugError extends Error {
|
|
2
8
|
constructor(message) {
|
|
3
9
|
super(message);
|
|
4
|
-
this.name = "
|
|
10
|
+
this.name = "ToolcraftBugError";
|
|
5
11
|
}
|
|
6
12
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { UserError } from "./user-error.js";
|
|
2
|
+
const MAX_RENDERED_VALIDATION_ERRORS = 10;
|
|
3
|
+
export function throwValidationErrors(errors) {
|
|
4
|
+
if (errors.length === 0) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
if (errors.length === 1) {
|
|
8
|
+
throw new UserError(errors[0]?.message ?? "Invalid parameters.");
|
|
9
|
+
}
|
|
10
|
+
const rendered = errors
|
|
11
|
+
.slice(0, MAX_RENDERED_VALIDATION_ERRORS)
|
|
12
|
+
.map((error) => ` - ${error.path}: ${error.message}`);
|
|
13
|
+
const remaining = errors.length - rendered.length;
|
|
14
|
+
if (remaining > 0) {
|
|
15
|
+
rendered.push(` … and ${remaining} more`);
|
|
16
|
+
}
|
|
17
|
+
throw new UserError(`${errors.length} parameter errors:\n${rendered.join("\n")}`);
|
|
18
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CommandInfo, FormatColumnsOptions, OptionInfo } from "./help-formatter.js";
|
|
2
|
+
export declare function stripAnsi(value: string): string;
|
|
2
3
|
export declare function formatColumns(opts: FormatColumnsOptions): string;
|
|
3
4
|
export declare function formatCommandList(commands: CommandInfo[]): string;
|
|
4
5
|
export declare function formatOptionList(options: OptionInfo[]): string;
|
|
@@ -10,6 +10,7 @@ export declare const text: {
|
|
|
10
10
|
readonly usageCommand: (content: string) => string;
|
|
11
11
|
readonly link: (content: string) => string;
|
|
12
12
|
readonly muted: (content: string) => string;
|
|
13
|
+
readonly error: (content: string) => string;
|
|
13
14
|
readonly badge: (content: string) => string;
|
|
14
15
|
readonly selectLabel: (label: string, detail?: string) => string;
|
|
15
16
|
};
|
|
@@ -91,6 +91,14 @@ export const text = {
|
|
|
91
91
|
return `*${content}*`;
|
|
92
92
|
return getTheme().muted(content);
|
|
93
93
|
},
|
|
94
|
+
error(content) {
|
|
95
|
+
const format = resolveOutputFormat();
|
|
96
|
+
if (format === "json")
|
|
97
|
+
return content;
|
|
98
|
+
if (format === "markdown")
|
|
99
|
+
return `**${content}**`;
|
|
100
|
+
return getTheme().error(content);
|
|
101
|
+
},
|
|
94
102
|
badge(content) {
|
|
95
103
|
const format = resolveOutputFormat();
|
|
96
104
|
if (format === "json")
|
|
@@ -126,6 +126,9 @@ export function cellToAnsi(cell) {
|
|
|
126
126
|
if (style.dim) {
|
|
127
127
|
painter = painter.dim;
|
|
128
128
|
}
|
|
129
|
+
if (style.underline) {
|
|
130
|
+
painter = painter.underline;
|
|
131
|
+
}
|
|
129
132
|
if (style.fg) {
|
|
130
133
|
painter = applyForegroundColor(painter, style.fg);
|
|
131
134
|
}
|
|
@@ -158,6 +161,9 @@ function normalizeStyle(style) {
|
|
|
158
161
|
if (style?.dim !== undefined) {
|
|
159
162
|
next.dim = style.dim;
|
|
160
163
|
}
|
|
164
|
+
if (style?.underline !== undefined) {
|
|
165
|
+
next.underline = style.underline;
|
|
166
|
+
}
|
|
161
167
|
return next;
|
|
162
168
|
}
|
|
163
169
|
function normalizeSize(value) {
|
|
@@ -168,7 +174,8 @@ function cellsEqual(left, right) {
|
|
|
168
174
|
&& left.style.fg === right.style.fg
|
|
169
175
|
&& left.style.bg === right.style.bg
|
|
170
176
|
&& left.style.bold === right.style.bold
|
|
171
|
-
&& left.style.dim === right.style.dim
|
|
177
|
+
&& left.style.dim === right.style.dim
|
|
178
|
+
&& left.style.underline === right.style.underline;
|
|
172
179
|
}
|
|
173
180
|
function applyForegroundColor(instance, color) {
|
|
174
181
|
if (color.startsWith("#")) {
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import type { KeypressEvent } from "./terminal.js";
|
|
2
2
|
import type { Command } from "./types.js";
|
|
3
3
|
export declare function createKeymap(overrides?: Partial<Record<Command, string[]>>): (event: KeypressEvent) => Command | undefined;
|
|
4
|
+
export declare function createKeymap<TCommand extends string>(overrides: Partial<Record<TCommand, string[]>> | undefined, options: {
|
|
5
|
+
commands: readonly TCommand[];
|
|
6
|
+
defaultBindings: Record<TCommand, readonly string[]>;
|
|
7
|
+
}): (event: KeypressEvent) => TCommand | undefined;
|
|
8
|
+
export declare function canonicalizeBinding(binding: string): string | undefined;
|