llmist 1.0.0 → 1.1.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.
- package/dist/{chunk-53MM55JS.js → chunk-OIPLYP7M.js} +2 -2
- package/dist/{chunk-T24KLXY4.js → chunk-VXPZQZF5.js} +108 -21
- package/dist/chunk-VXPZQZF5.js.map +1 -0
- package/dist/cli.cjs +568 -52
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +471 -34
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +103 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -4
- package/dist/index.d.ts +15 -4
- package/dist/index.js +2 -2
- package/dist/testing/index.cjs +103 -20
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-T24KLXY4.js.map +0 -1
- /package/dist/{chunk-53MM55JS.js.map → chunk-OIPLYP7M.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -21,8 +21,12 @@ import {
|
|
|
21
21
|
init_messages,
|
|
22
22
|
init_model_shortcuts,
|
|
23
23
|
init_registry,
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
init_schema_to_json,
|
|
25
|
+
init_schema_validator,
|
|
26
|
+
resolveModel,
|
|
27
|
+
schemaToJSONSchema,
|
|
28
|
+
validateGadgetSchema
|
|
29
|
+
} from "./chunk-VXPZQZF5.js";
|
|
26
30
|
|
|
27
31
|
// src/cli/constants.ts
|
|
28
32
|
var CLI_NAME = "llmist";
|
|
@@ -30,7 +34,8 @@ var CLI_DESCRIPTION = "Command line utilities for llmist agents and direct LLM a
|
|
|
30
34
|
var COMMANDS = {
|
|
31
35
|
complete: "complete",
|
|
32
36
|
agent: "agent",
|
|
33
|
-
models: "models"
|
|
37
|
+
models: "models",
|
|
38
|
+
gadget: "gadget"
|
|
34
39
|
};
|
|
35
40
|
var LOG_LEVELS = ["silly", "trace", "debug", "info", "warn", "error", "fatal"];
|
|
36
41
|
var DEFAULT_MODEL = "openai:gpt-5-nano";
|
|
@@ -74,7 +79,7 @@ import { Command, InvalidArgumentError as InvalidArgumentError2 } from "commande
|
|
|
74
79
|
// package.json
|
|
75
80
|
var package_default = {
|
|
76
81
|
name: "llmist",
|
|
77
|
-
version: "0.
|
|
82
|
+
version: "1.0.0",
|
|
78
83
|
description: "Universal TypeScript LLM client with streaming-first agent framework. Works with any model - no structured outputs or native tool calling required. Implements its own flexible grammar for function calling.",
|
|
79
84
|
type: "module",
|
|
80
85
|
main: "dist/index.cjs",
|
|
@@ -2061,8 +2066,439 @@ function resolveInheritance(config, configPath) {
|
|
|
2061
2066
|
return resolved;
|
|
2062
2067
|
}
|
|
2063
2068
|
|
|
2064
|
-
// src/cli/
|
|
2069
|
+
// src/cli/gadget-command.ts
|
|
2070
|
+
init_schema_to_json();
|
|
2071
|
+
init_schema_validator();
|
|
2072
|
+
import chalk5 from "chalk";
|
|
2073
|
+
|
|
2074
|
+
// src/cli/gadget-prompts.ts
|
|
2075
|
+
init_schema_to_json();
|
|
2076
|
+
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
2065
2077
|
import chalk4 from "chalk";
|
|
2078
|
+
async function promptForParameters(schema, ctx) {
|
|
2079
|
+
if (!schema) {
|
|
2080
|
+
return {};
|
|
2081
|
+
}
|
|
2082
|
+
const jsonSchema = schemaToJSONSchema(schema, { target: "draft-7" });
|
|
2083
|
+
if (!jsonSchema.properties || Object.keys(jsonSchema.properties).length === 0) {
|
|
2084
|
+
return {};
|
|
2085
|
+
}
|
|
2086
|
+
const rl = createInterface2({ input: ctx.stdin, output: ctx.stdout });
|
|
2087
|
+
const params = {};
|
|
2088
|
+
try {
|
|
2089
|
+
for (const [key, prop] of Object.entries(jsonSchema.properties)) {
|
|
2090
|
+
const value = await promptForField(rl, key, prop, jsonSchema.required ?? []);
|
|
2091
|
+
if (value !== void 0) {
|
|
2092
|
+
params[key] = value;
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
} finally {
|
|
2096
|
+
rl.close();
|
|
2097
|
+
}
|
|
2098
|
+
const result = schema.safeParse(params);
|
|
2099
|
+
if (!result.success) {
|
|
2100
|
+
const issues = result.error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
2101
|
+
throw new Error(`Invalid parameters:
|
|
2102
|
+
${issues}`);
|
|
2103
|
+
}
|
|
2104
|
+
return result.data;
|
|
2105
|
+
}
|
|
2106
|
+
async function promptForField(rl, key, prop, required) {
|
|
2107
|
+
const isRequired = required.includes(key);
|
|
2108
|
+
const typeHint = formatTypeHint(prop);
|
|
2109
|
+
const defaultHint = prop.default !== void 0 ? chalk4.dim(` [default: ${JSON.stringify(prop.default)}]`) : "";
|
|
2110
|
+
const requiredMarker = isRequired ? chalk4.red("*") : "";
|
|
2111
|
+
let prompt = `
|
|
2112
|
+
${chalk4.cyan.bold(key)}${requiredMarker}`;
|
|
2113
|
+
if (prop.description) {
|
|
2114
|
+
prompt += chalk4.dim(` - ${prop.description}`);
|
|
2115
|
+
}
|
|
2116
|
+
prompt += `
|
|
2117
|
+
${typeHint}${defaultHint}
|
|
2118
|
+
${chalk4.green(">")} `;
|
|
2119
|
+
const answer = await rl.question(prompt);
|
|
2120
|
+
const trimmed = answer.trim();
|
|
2121
|
+
if (!trimmed) {
|
|
2122
|
+
if (prop.default !== void 0) {
|
|
2123
|
+
return void 0;
|
|
2124
|
+
}
|
|
2125
|
+
if (!isRequired) {
|
|
2126
|
+
return void 0;
|
|
2127
|
+
}
|
|
2128
|
+
throw new Error(`Parameter '${key}' is required.`);
|
|
2129
|
+
}
|
|
2130
|
+
return parseValue(trimmed, prop, key);
|
|
2131
|
+
}
|
|
2132
|
+
function formatTypeHint(prop) {
|
|
2133
|
+
if (prop.enum) {
|
|
2134
|
+
return chalk4.yellow(`(${prop.enum.join(" | ")})`);
|
|
2135
|
+
}
|
|
2136
|
+
if (prop.type === "array") {
|
|
2137
|
+
const items = prop.items;
|
|
2138
|
+
if (items?.enum) {
|
|
2139
|
+
return chalk4.yellow(`(${items.enum.join(" | ")})[] comma-separated`);
|
|
2140
|
+
}
|
|
2141
|
+
const itemType = items?.type ?? "any";
|
|
2142
|
+
return chalk4.yellow(`(${itemType}[]) comma-separated`);
|
|
2143
|
+
}
|
|
2144
|
+
if (prop.type === "object" && prop.properties) {
|
|
2145
|
+
return chalk4.yellow("(object) enter as JSON");
|
|
2146
|
+
}
|
|
2147
|
+
return chalk4.yellow(`(${prop.type ?? "any"})`);
|
|
2148
|
+
}
|
|
2149
|
+
function parseValue(input, prop, key) {
|
|
2150
|
+
const type = prop.type;
|
|
2151
|
+
if (type === "number" || type === "integer") {
|
|
2152
|
+
const num = Number(input);
|
|
2153
|
+
if (Number.isNaN(num)) {
|
|
2154
|
+
throw new Error(`Invalid number for '${key}': ${input}`);
|
|
2155
|
+
}
|
|
2156
|
+
if (type === "integer" && !Number.isInteger(num)) {
|
|
2157
|
+
throw new Error(`Expected integer for '${key}', got: ${input}`);
|
|
2158
|
+
}
|
|
2159
|
+
return num;
|
|
2160
|
+
}
|
|
2161
|
+
if (type === "boolean") {
|
|
2162
|
+
const lower = input.toLowerCase();
|
|
2163
|
+
if (["true", "yes", "1", "y"].includes(lower)) return true;
|
|
2164
|
+
if (["false", "no", "0", "n"].includes(lower)) return false;
|
|
2165
|
+
throw new Error(`Invalid boolean for '${key}': ${input} (use true/false, yes/no, 1/0)`);
|
|
2166
|
+
}
|
|
2167
|
+
if (type === "array") {
|
|
2168
|
+
const items = input.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2169
|
+
const itemType = prop.items?.type;
|
|
2170
|
+
if (itemType === "number" || itemType === "integer") {
|
|
2171
|
+
return items.map((item) => {
|
|
2172
|
+
const num = Number(item);
|
|
2173
|
+
if (Number.isNaN(num)) throw new Error(`Invalid number in '${key}' array: ${item}`);
|
|
2174
|
+
return num;
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2177
|
+
if (itemType === "boolean") {
|
|
2178
|
+
return items.map((item) => {
|
|
2179
|
+
const lower = item.toLowerCase();
|
|
2180
|
+
if (["true", "yes", "1", "y"].includes(lower)) return true;
|
|
2181
|
+
if (["false", "no", "0", "n"].includes(lower)) return false;
|
|
2182
|
+
throw new Error(`Invalid boolean in '${key}' array: ${item}`);
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
2185
|
+
return items;
|
|
2186
|
+
}
|
|
2187
|
+
if (type === "object") {
|
|
2188
|
+
try {
|
|
2189
|
+
return JSON.parse(input);
|
|
2190
|
+
} catch {
|
|
2191
|
+
throw new Error(`Invalid JSON for '${key}': ${input}`);
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
return input;
|
|
2195
|
+
}
|
|
2196
|
+
async function readStdinJson(stdin) {
|
|
2197
|
+
const chunks = [];
|
|
2198
|
+
for await (const chunk of stdin) {
|
|
2199
|
+
if (typeof chunk === "string") {
|
|
2200
|
+
chunks.push(chunk);
|
|
2201
|
+
} else {
|
|
2202
|
+
chunks.push(chunk.toString("utf8"));
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
const content = chunks.join("").trim();
|
|
2206
|
+
if (!content) {
|
|
2207
|
+
return {};
|
|
2208
|
+
}
|
|
2209
|
+
try {
|
|
2210
|
+
const parsed = JSON.parse(content);
|
|
2211
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
2212
|
+
throw new Error("Stdin must contain a JSON object, not an array or primitive.");
|
|
2213
|
+
}
|
|
2214
|
+
return parsed;
|
|
2215
|
+
} catch (error) {
|
|
2216
|
+
if (error instanceof SyntaxError) {
|
|
2217
|
+
throw new Error(`Invalid JSON from stdin: ${error.message}`);
|
|
2218
|
+
}
|
|
2219
|
+
throw error;
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
// src/cli/gadget-command.ts
|
|
2224
|
+
async function selectGadget(file, nameOption, cwd) {
|
|
2225
|
+
const gadgets = await loadGadgets([file], cwd);
|
|
2226
|
+
if (gadgets.length === 0) {
|
|
2227
|
+
throw new Error(
|
|
2228
|
+
`No gadgets found in '${file}'.
|
|
2229
|
+
Ensure the file exports a Gadget class or instance.`
|
|
2230
|
+
);
|
|
2231
|
+
}
|
|
2232
|
+
if (gadgets.length === 1) {
|
|
2233
|
+
const gadget = gadgets[0];
|
|
2234
|
+
const name = gadget.name ?? gadget.constructor.name;
|
|
2235
|
+
return { gadget, name };
|
|
2236
|
+
}
|
|
2237
|
+
const names = gadgets.map((g) => g.name ?? g.constructor.name);
|
|
2238
|
+
if (!nameOption) {
|
|
2239
|
+
throw new Error(
|
|
2240
|
+
`File '${file}' exports ${gadgets.length} gadgets.
|
|
2241
|
+
Use --name to select one:
|
|
2242
|
+
` + names.map((n) => ` - ${n}`).join("\n")
|
|
2243
|
+
);
|
|
2244
|
+
}
|
|
2245
|
+
const found = gadgets.find((g) => (g.name ?? g.constructor.name) === nameOption);
|
|
2246
|
+
if (!found) {
|
|
2247
|
+
throw new Error(
|
|
2248
|
+
`Gadget '${nameOption}' not found in '${file}'.
|
|
2249
|
+
Available gadgets:
|
|
2250
|
+
` + names.map((n) => ` - ${n}`).join("\n")
|
|
2251
|
+
);
|
|
2252
|
+
}
|
|
2253
|
+
return { gadget: found, name: nameOption };
|
|
2254
|
+
}
|
|
2255
|
+
async function executeGadgetRun(file, options, env) {
|
|
2256
|
+
const cwd = process.cwd();
|
|
2257
|
+
const { gadget, name } = await selectGadget(file, options.name, cwd);
|
|
2258
|
+
env.stderr.write(chalk5.cyan.bold(`
|
|
2259
|
+
\u{1F527} Running gadget: ${name}
|
|
2260
|
+
`));
|
|
2261
|
+
let params;
|
|
2262
|
+
if (env.isTTY) {
|
|
2263
|
+
params = await promptForParameters(gadget.parameterSchema, {
|
|
2264
|
+
stdin: env.stdin,
|
|
2265
|
+
stdout: env.stderr
|
|
2266
|
+
// Prompts go to stderr to keep stdout clean
|
|
2267
|
+
});
|
|
2268
|
+
} else {
|
|
2269
|
+
env.stderr.write(chalk5.dim("Reading parameters from stdin...\n"));
|
|
2270
|
+
const stdinParams = await readStdinJson(env.stdin);
|
|
2271
|
+
if (gadget.parameterSchema) {
|
|
2272
|
+
const result2 = gadget.parameterSchema.safeParse(stdinParams);
|
|
2273
|
+
if (!result2.success) {
|
|
2274
|
+
const issues = result2.error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
2275
|
+
throw new Error(`Invalid parameters:
|
|
2276
|
+
${issues}`);
|
|
2277
|
+
}
|
|
2278
|
+
params = result2.data;
|
|
2279
|
+
} else {
|
|
2280
|
+
params = stdinParams;
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
env.stderr.write(chalk5.dim("\nExecuting...\n"));
|
|
2284
|
+
const startTime = Date.now();
|
|
2285
|
+
let result;
|
|
2286
|
+
try {
|
|
2287
|
+
if (gadget.timeoutMs && gadget.timeoutMs > 0) {
|
|
2288
|
+
result = await Promise.race([
|
|
2289
|
+
Promise.resolve(gadget.execute(params)),
|
|
2290
|
+
new Promise(
|
|
2291
|
+
(_, reject) => setTimeout(
|
|
2292
|
+
() => reject(new Error(`Gadget timed out after ${gadget.timeoutMs}ms`)),
|
|
2293
|
+
gadget.timeoutMs
|
|
2294
|
+
)
|
|
2295
|
+
)
|
|
2296
|
+
]);
|
|
2297
|
+
} else {
|
|
2298
|
+
result = await Promise.resolve(gadget.execute(params));
|
|
2299
|
+
}
|
|
2300
|
+
} catch (error) {
|
|
2301
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2302
|
+
throw new Error(`Execution failed: ${message}`);
|
|
2303
|
+
}
|
|
2304
|
+
const elapsed = Date.now() - startTime;
|
|
2305
|
+
env.stderr.write(chalk5.green(`
|
|
2306
|
+
\u2713 Completed in ${elapsed}ms
|
|
2307
|
+
|
|
2308
|
+
`));
|
|
2309
|
+
formatOutput(result, options, env.stdout);
|
|
2310
|
+
}
|
|
2311
|
+
function formatOutput(result, options, stdout) {
|
|
2312
|
+
if (options.raw) {
|
|
2313
|
+
stdout.write(result);
|
|
2314
|
+
if (!result.endsWith("\n")) stdout.write("\n");
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
if (options.json || looksLikeJson(result)) {
|
|
2318
|
+
try {
|
|
2319
|
+
const parsed = JSON.parse(result);
|
|
2320
|
+
stdout.write(JSON.stringify(parsed, null, 2) + "\n");
|
|
2321
|
+
return;
|
|
2322
|
+
} catch {
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
stdout.write(result);
|
|
2326
|
+
if (!result.endsWith("\n")) stdout.write("\n");
|
|
2327
|
+
}
|
|
2328
|
+
function looksLikeJson(str) {
|
|
2329
|
+
const trimmed = str.trim();
|
|
2330
|
+
return trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]");
|
|
2331
|
+
}
|
|
2332
|
+
async function executeGadgetInfo(file, options, env) {
|
|
2333
|
+
const cwd = process.cwd();
|
|
2334
|
+
const { gadget, name } = await selectGadget(file, options.name, cwd);
|
|
2335
|
+
if (options.json) {
|
|
2336
|
+
const info = buildGadgetInfo(gadget, name);
|
|
2337
|
+
env.stdout.write(JSON.stringify(info, null, 2) + "\n");
|
|
2338
|
+
return;
|
|
2339
|
+
}
|
|
2340
|
+
env.stdout.write("\n");
|
|
2341
|
+
env.stdout.write(chalk5.cyan.bold(`${name}
|
|
2342
|
+
`));
|
|
2343
|
+
env.stdout.write(chalk5.cyan("\u2550".repeat(name.length)) + "\n\n");
|
|
2344
|
+
env.stdout.write(chalk5.bold("Description:\n"));
|
|
2345
|
+
env.stdout.write(` ${gadget.description}
|
|
2346
|
+
|
|
2347
|
+
`);
|
|
2348
|
+
if (gadget.parameterSchema) {
|
|
2349
|
+
env.stdout.write(chalk5.bold("Parameters:\n"));
|
|
2350
|
+
const jsonSchema = schemaToJSONSchema(gadget.parameterSchema, { target: "draft-7" });
|
|
2351
|
+
env.stdout.write(formatSchemaAsText(jsonSchema, " ") + "\n\n");
|
|
2352
|
+
} else {
|
|
2353
|
+
env.stdout.write(chalk5.dim("No parameters required.\n\n"));
|
|
2354
|
+
}
|
|
2355
|
+
if (gadget.timeoutMs) {
|
|
2356
|
+
env.stdout.write(chalk5.bold("Timeout:\n"));
|
|
2357
|
+
env.stdout.write(` ${gadget.timeoutMs}ms
|
|
2358
|
+
|
|
2359
|
+
`);
|
|
2360
|
+
}
|
|
2361
|
+
if (gadget.examples && gadget.examples.length > 0) {
|
|
2362
|
+
env.stdout.write(chalk5.bold("Examples:\n"));
|
|
2363
|
+
for (const example of gadget.examples) {
|
|
2364
|
+
if (example.comment) {
|
|
2365
|
+
env.stdout.write(chalk5.dim(` # ${example.comment}
|
|
2366
|
+
`));
|
|
2367
|
+
}
|
|
2368
|
+
env.stdout.write(` Input: ${chalk5.cyan(JSON.stringify(example.params))}
|
|
2369
|
+
`);
|
|
2370
|
+
if (example.output !== void 0) {
|
|
2371
|
+
env.stdout.write(` Output: ${chalk5.green(example.output)}
|
|
2372
|
+
`);
|
|
2373
|
+
}
|
|
2374
|
+
env.stdout.write("\n");
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
function buildGadgetInfo(gadget, name) {
|
|
2379
|
+
const info = {
|
|
2380
|
+
name,
|
|
2381
|
+
description: gadget.description
|
|
2382
|
+
};
|
|
2383
|
+
if (gadget.parameterSchema) {
|
|
2384
|
+
info.schema = schemaToJSONSchema(gadget.parameterSchema, { target: "draft-7" });
|
|
2385
|
+
}
|
|
2386
|
+
if (gadget.timeoutMs) {
|
|
2387
|
+
info.timeoutMs = gadget.timeoutMs;
|
|
2388
|
+
}
|
|
2389
|
+
if (gadget.examples && gadget.examples.length > 0) {
|
|
2390
|
+
info.examples = gadget.examples;
|
|
2391
|
+
}
|
|
2392
|
+
return info;
|
|
2393
|
+
}
|
|
2394
|
+
function formatSchemaAsText(schema, indent = "") {
|
|
2395
|
+
const lines = [];
|
|
2396
|
+
const properties = schema.properties || {};
|
|
2397
|
+
const required = schema.required || [];
|
|
2398
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
2399
|
+
const type = prop.type;
|
|
2400
|
+
const description = prop.description;
|
|
2401
|
+
const isRequired = required.includes(key);
|
|
2402
|
+
const enumValues = prop.enum;
|
|
2403
|
+
const defaultValue = prop.default;
|
|
2404
|
+
let line = `${indent}${chalk5.cyan(key)}`;
|
|
2405
|
+
if (isRequired) {
|
|
2406
|
+
line += chalk5.red("*");
|
|
2407
|
+
}
|
|
2408
|
+
if (type === "array") {
|
|
2409
|
+
const items = prop.items;
|
|
2410
|
+
const itemType = items?.type || "any";
|
|
2411
|
+
line += chalk5.dim(` (${itemType}[])`);
|
|
2412
|
+
} else if (type === "object" && prop.properties) {
|
|
2413
|
+
line += chalk5.dim(" (object)");
|
|
2414
|
+
} else {
|
|
2415
|
+
line += chalk5.dim(` (${type})`);
|
|
2416
|
+
}
|
|
2417
|
+
if (defaultValue !== void 0) {
|
|
2418
|
+
line += chalk5.dim(` [default: ${JSON.stringify(defaultValue)}]`);
|
|
2419
|
+
}
|
|
2420
|
+
if (description) {
|
|
2421
|
+
line += `: ${description}`;
|
|
2422
|
+
}
|
|
2423
|
+
if (enumValues) {
|
|
2424
|
+
line += chalk5.yellow(` - one of: ${enumValues.join(", ")}`);
|
|
2425
|
+
}
|
|
2426
|
+
lines.push(line);
|
|
2427
|
+
if (type === "object" && prop.properties) {
|
|
2428
|
+
lines.push(formatSchemaAsText(prop, indent + " "));
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
return lines.join("\n");
|
|
2432
|
+
}
|
|
2433
|
+
async function executeGadgetValidate(file, env) {
|
|
2434
|
+
const cwd = process.cwd();
|
|
2435
|
+
try {
|
|
2436
|
+
const gadgets = await loadGadgets([file], cwd);
|
|
2437
|
+
if (gadgets.length === 0) {
|
|
2438
|
+
throw new Error(
|
|
2439
|
+
"No gadgets exported from file.\nA valid gadget must have:\n - execute() method\n - description property\n - parameterSchema (optional)"
|
|
2440
|
+
);
|
|
2441
|
+
}
|
|
2442
|
+
const issues = [];
|
|
2443
|
+
for (const gadget of gadgets) {
|
|
2444
|
+
const name = gadget.name ?? gadget.constructor.name;
|
|
2445
|
+
if (!gadget.description) {
|
|
2446
|
+
issues.push(`${name}: Missing 'description' property.`);
|
|
2447
|
+
}
|
|
2448
|
+
if (gadget.parameterSchema) {
|
|
2449
|
+
try {
|
|
2450
|
+
validateGadgetSchema(gadget.parameterSchema, name);
|
|
2451
|
+
} catch (schemaError) {
|
|
2452
|
+
const message = schemaError instanceof Error ? schemaError.message : String(schemaError);
|
|
2453
|
+
issues.push(`${name}: ${message}`);
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
if (typeof gadget.execute !== "function") {
|
|
2457
|
+
issues.push(`${name}: Missing 'execute()' method.`);
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
if (issues.length > 0) {
|
|
2461
|
+
throw new Error(`Validation issues:
|
|
2462
|
+
${issues.map((i) => ` - ${i}`).join("\n")}`);
|
|
2463
|
+
}
|
|
2464
|
+
env.stdout.write(chalk5.green.bold("\n\u2713 Valid\n\n"));
|
|
2465
|
+
env.stdout.write(chalk5.bold("Gadgets found:\n"));
|
|
2466
|
+
for (const gadget of gadgets) {
|
|
2467
|
+
const name = gadget.name ?? gadget.constructor.name;
|
|
2468
|
+
const schemaInfo = gadget.parameterSchema ? chalk5.cyan("(with schema)") : chalk5.dim("(no schema)");
|
|
2469
|
+
env.stdout.write(` ${chalk5.bold(name)} ${schemaInfo}
|
|
2470
|
+
`);
|
|
2471
|
+
env.stdout.write(chalk5.dim(` ${gadget.description}
|
|
2472
|
+
`));
|
|
2473
|
+
}
|
|
2474
|
+
env.stdout.write("\n");
|
|
2475
|
+
} catch (error) {
|
|
2476
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2477
|
+
env.stdout.write(chalk5.red.bold(`
|
|
2478
|
+
\u2717 Invalid
|
|
2479
|
+
|
|
2480
|
+
`));
|
|
2481
|
+
env.stdout.write(`${message}
|
|
2482
|
+
|
|
2483
|
+
`);
|
|
2484
|
+
env.setExitCode(1);
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
function registerGadgetCommand(program, env) {
|
|
2488
|
+
const gadgetCmd = program.command("gadget").description("Test and inspect gadgets outside the agent loop.");
|
|
2489
|
+
gadgetCmd.command("run <file>").description("Execute a gadget with interactive prompts or stdin JSON.").option("--name <gadget>", "Select gadget by name (required if file exports multiple)").option("--json", "Format output as pretty-printed JSON").option("--raw", "Output result as raw string without formatting").action(
|
|
2490
|
+
(file, options) => executeAction(() => executeGadgetRun(file, options, env), env)
|
|
2491
|
+
);
|
|
2492
|
+
gadgetCmd.command("info <file>").description("Display gadget description, schema, and examples.").option("--name <gadget>", "Select gadget by name (required if file exports multiple)").option("--json", "Output as JSON instead of formatted text").action(
|
|
2493
|
+
(file, options) => executeAction(() => executeGadgetInfo(file, options, env), env)
|
|
2494
|
+
);
|
|
2495
|
+
gadgetCmd.command("validate <file>").description("Check if file exports valid gadget(s).").action(
|
|
2496
|
+
(file) => executeAction(() => executeGadgetValidate(file, env), env)
|
|
2497
|
+
);
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
// src/cli/models-command.ts
|
|
2501
|
+
import chalk6 from "chalk";
|
|
2066
2502
|
init_model_shortcuts();
|
|
2067
2503
|
async function handleModelsCommand(options, env) {
|
|
2068
2504
|
const client = env.createClient();
|
|
@@ -2082,13 +2518,13 @@ function renderTable(models, verbose, stream) {
|
|
|
2082
2518
|
}
|
|
2083
2519
|
grouped.get(provider).push(model);
|
|
2084
2520
|
}
|
|
2085
|
-
stream.write(
|
|
2086
|
-
stream.write(
|
|
2521
|
+
stream.write(chalk6.bold.cyan("\nAvailable Models\n"));
|
|
2522
|
+
stream.write(chalk6.cyan("=".repeat(80)) + "\n\n");
|
|
2087
2523
|
const providers = Array.from(grouped.keys()).sort();
|
|
2088
2524
|
for (const provider of providers) {
|
|
2089
2525
|
const providerModels = grouped.get(provider);
|
|
2090
2526
|
const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
2091
|
-
stream.write(
|
|
2527
|
+
stream.write(chalk6.bold.yellow(`${providerName} Models
|
|
2092
2528
|
`));
|
|
2093
2529
|
if (verbose) {
|
|
2094
2530
|
renderVerboseTable(providerModels, stream);
|
|
@@ -2097,11 +2533,11 @@ function renderTable(models, verbose, stream) {
|
|
|
2097
2533
|
}
|
|
2098
2534
|
stream.write("\n");
|
|
2099
2535
|
}
|
|
2100
|
-
stream.write(
|
|
2101
|
-
stream.write(
|
|
2536
|
+
stream.write(chalk6.bold.magenta("Model Shortcuts\n"));
|
|
2537
|
+
stream.write(chalk6.dim("\u2500".repeat(80)) + "\n");
|
|
2102
2538
|
const shortcuts = Object.entries(MODEL_ALIASES).sort((a, b) => a[0].localeCompare(b[0]));
|
|
2103
2539
|
for (const [shortcut, fullName] of shortcuts) {
|
|
2104
|
-
stream.write(
|
|
2540
|
+
stream.write(chalk6.cyan(` ${shortcut.padEnd(15)}`) + chalk6.dim(" \u2192 ") + chalk6.white(fullName) + "\n");
|
|
2105
2541
|
}
|
|
2106
2542
|
stream.write("\n");
|
|
2107
2543
|
}
|
|
@@ -2111,45 +2547,45 @@ function renderCompactTable(models, stream) {
|
|
|
2111
2547
|
const contextWidth = 13;
|
|
2112
2548
|
const inputWidth = 10;
|
|
2113
2549
|
const outputWidth = 10;
|
|
2114
|
-
stream.write(
|
|
2550
|
+
stream.write(chalk6.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
|
|
2115
2551
|
stream.write(
|
|
2116
|
-
|
|
2552
|
+
chalk6.bold(
|
|
2117
2553
|
"Model ID".padEnd(idWidth) + " " + "Display Name".padEnd(nameWidth) + " " + "Context".padEnd(contextWidth) + " " + "Input".padEnd(inputWidth) + " " + "Output".padEnd(outputWidth)
|
|
2118
2554
|
) + "\n"
|
|
2119
2555
|
);
|
|
2120
|
-
stream.write(
|
|
2556
|
+
stream.write(chalk6.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
|
|
2121
2557
|
for (const model of models) {
|
|
2122
2558
|
const contextFormatted = formatTokens2(model.contextWindow);
|
|
2123
2559
|
const inputPrice = `$${model.pricing.input.toFixed(2)}`;
|
|
2124
2560
|
const outputPrice = `$${model.pricing.output.toFixed(2)}`;
|
|
2125
2561
|
stream.write(
|
|
2126
|
-
|
|
2562
|
+
chalk6.green(model.modelId.padEnd(idWidth)) + " " + chalk6.white(model.displayName.padEnd(nameWidth)) + " " + chalk6.yellow(contextFormatted.padEnd(contextWidth)) + " " + chalk6.cyan(inputPrice.padEnd(inputWidth)) + " " + chalk6.cyan(outputPrice.padEnd(outputWidth)) + "\n"
|
|
2127
2563
|
);
|
|
2128
2564
|
}
|
|
2129
|
-
stream.write(
|
|
2130
|
-
stream.write(
|
|
2565
|
+
stream.write(chalk6.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
|
|
2566
|
+
stream.write(chalk6.dim(` * Prices are per 1M tokens
|
|
2131
2567
|
`));
|
|
2132
2568
|
}
|
|
2133
2569
|
function renderVerboseTable(models, stream) {
|
|
2134
2570
|
for (const model of models) {
|
|
2135
|
-
stream.write(
|
|
2571
|
+
stream.write(chalk6.bold.green(`
|
|
2136
2572
|
${model.modelId}
|
|
2137
2573
|
`));
|
|
2138
|
-
stream.write(
|
|
2139
|
-
stream.write(` ${
|
|
2574
|
+
stream.write(chalk6.dim(" " + "\u2500".repeat(60)) + "\n");
|
|
2575
|
+
stream.write(` ${chalk6.dim("Name:")} ${chalk6.white(model.displayName)}
|
|
2140
2576
|
`);
|
|
2141
|
-
stream.write(` ${
|
|
2577
|
+
stream.write(` ${chalk6.dim("Context:")} ${chalk6.yellow(formatTokens2(model.contextWindow))}
|
|
2142
2578
|
`);
|
|
2143
|
-
stream.write(` ${
|
|
2579
|
+
stream.write(` ${chalk6.dim("Max Output:")} ${chalk6.yellow(formatTokens2(model.maxOutputTokens))}
|
|
2144
2580
|
`);
|
|
2145
|
-
stream.write(` ${
|
|
2581
|
+
stream.write(` ${chalk6.dim("Pricing:")} ${chalk6.cyan(`$${model.pricing.input.toFixed(2)} input`)} ${chalk6.dim("/")} ${chalk6.cyan(`$${model.pricing.output.toFixed(2)} output`)} ${chalk6.dim("(per 1M tokens)")}
|
|
2146
2582
|
`);
|
|
2147
2583
|
if (model.pricing.cachedInput !== void 0) {
|
|
2148
|
-
stream.write(` ${
|
|
2584
|
+
stream.write(` ${chalk6.dim("Cached Input:")} ${chalk6.cyan(`$${model.pricing.cachedInput.toFixed(2)} per 1M tokens`)}
|
|
2149
2585
|
`);
|
|
2150
2586
|
}
|
|
2151
2587
|
if (model.knowledgeCutoff) {
|
|
2152
|
-
stream.write(` ${
|
|
2588
|
+
stream.write(` ${chalk6.dim("Knowledge:")} ${model.knowledgeCutoff}
|
|
2153
2589
|
`);
|
|
2154
2590
|
}
|
|
2155
2591
|
const features = [];
|
|
@@ -2160,20 +2596,20 @@ function renderVerboseTable(models, stream) {
|
|
|
2160
2596
|
if (model.features.structuredOutputs) features.push("structured-outputs");
|
|
2161
2597
|
if (model.features.fineTuning) features.push("fine-tuning");
|
|
2162
2598
|
if (features.length > 0) {
|
|
2163
|
-
stream.write(` ${
|
|
2599
|
+
stream.write(` ${chalk6.dim("Features:")} ${chalk6.blue(features.join(", "))}
|
|
2164
2600
|
`);
|
|
2165
2601
|
}
|
|
2166
2602
|
if (model.metadata) {
|
|
2167
2603
|
if (model.metadata.family) {
|
|
2168
|
-
stream.write(` ${
|
|
2604
|
+
stream.write(` ${chalk6.dim("Family:")} ${model.metadata.family}
|
|
2169
2605
|
`);
|
|
2170
2606
|
}
|
|
2171
2607
|
if (model.metadata.releaseDate) {
|
|
2172
|
-
stream.write(` ${
|
|
2608
|
+
stream.write(` ${chalk6.dim("Released:")} ${model.metadata.releaseDate}
|
|
2173
2609
|
`);
|
|
2174
2610
|
}
|
|
2175
2611
|
if (model.metadata.notes) {
|
|
2176
|
-
stream.write(` ${
|
|
2612
|
+
stream.write(` ${chalk6.dim("Notes:")} ${chalk6.italic(model.metadata.notes)}
|
|
2177
2613
|
`);
|
|
2178
2614
|
}
|
|
2179
2615
|
}
|
|
@@ -2225,7 +2661,7 @@ function registerModelsCommand(program, env) {
|
|
|
2225
2661
|
init_client();
|
|
2226
2662
|
init_logger();
|
|
2227
2663
|
import readline from "node:readline";
|
|
2228
|
-
import
|
|
2664
|
+
import chalk7 from "chalk";
|
|
2229
2665
|
var LOG_LEVEL_MAP = {
|
|
2230
2666
|
silly: 0,
|
|
2231
2667
|
trace: 1,
|
|
@@ -2272,14 +2708,14 @@ function createPromptFunction(stdin, stdout) {
|
|
|
2272
2708
|
output: stdout
|
|
2273
2709
|
});
|
|
2274
2710
|
stdout.write("\n");
|
|
2275
|
-
stdout.write(`${
|
|
2711
|
+
stdout.write(`${chalk7.cyan("\u2500".repeat(60))}
|
|
2276
2712
|
`);
|
|
2277
|
-
stdout.write(
|
|
2713
|
+
stdout.write(chalk7.cyan.bold("\u{1F916} Agent asks:\n"));
|
|
2278
2714
|
stdout.write(`${question}
|
|
2279
2715
|
`);
|
|
2280
|
-
stdout.write(`${
|
|
2716
|
+
stdout.write(`${chalk7.cyan("\u2500".repeat(60))}
|
|
2281
2717
|
`);
|
|
2282
|
-
rl.question(
|
|
2718
|
+
rl.question(chalk7.green.bold("You: "), (answer) => {
|
|
2283
2719
|
rl.close();
|
|
2284
2720
|
resolve(answer);
|
|
2285
2721
|
});
|
|
@@ -2369,6 +2805,7 @@ function createProgram(env, config) {
|
|
|
2369
2805
|
registerCompleteCommand(program, env, config?.complete);
|
|
2370
2806
|
registerAgentCommand(program, env, config?.agent);
|
|
2371
2807
|
registerModelsCommand(program, env);
|
|
2808
|
+
registerGadgetCommand(program, env);
|
|
2372
2809
|
if (config) {
|
|
2373
2810
|
const customNames = getCustomCommandNames(config);
|
|
2374
2811
|
for (const name of customNames) {
|