llm-exe 3.0.0 → 3.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadConfigFromUrl
4
+ } from "../chunk-7W6GBE2Y.mjs";
5
+ import {
6
+ loadConfigFromFile
7
+ } from "../chunk-PX2FFNZQ.mjs";
8
+ import {
9
+ executorFromConfig,
10
+ isLlmExeError
11
+ } from "../chunk-2BUGTOKA.mjs";
12
+
13
+ // src/cli/cli.ts
14
+ import { readFileSync } from "node:fs";
15
+ import { dirname, resolve } from "node:path";
16
+
17
+ // src/cli/args.ts
18
+ var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
19
+ var BOOLEAN_FLAGS = {
20
+ json: "json",
21
+ debug: "debug",
22
+ remote: "remote",
23
+ help: "help",
24
+ h: "help",
25
+ version: "version",
26
+ v: "version"
27
+ };
28
+ var VALUE_FLAGS = /* @__PURE__ */ new Set(["model", "provider", "parser", "stdin"]);
29
+ function isPlainObject(value) {
30
+ return !!value && typeof value === "object" && !Array.isArray(value);
31
+ }
32
+ function setNested(target, dottedKey, value) {
33
+ const parts = dottedKey.split(".").filter((p) => p.length > 0);
34
+ if (parts.length === 0) {
35
+ throw new Error("Use --data.<key> <value>, e.g. --data.name World");
36
+ }
37
+ for (const part of parts) {
38
+ if (FORBIDDEN_KEYS.has(part)) {
39
+ throw new Error(`Illegal key "${part}" in --data.${dottedKey}`);
40
+ }
41
+ }
42
+ let node = target;
43
+ for (let i = 0; i < parts.length - 1; i++) {
44
+ const part = parts[i];
45
+ if (!isPlainObject(node[part])) {
46
+ node[part] = {};
47
+ }
48
+ node = node[part];
49
+ }
50
+ node[parts[parts.length - 1]] = value;
51
+ }
52
+ function parseArgs(argv) {
53
+ const result = {
54
+ data: {},
55
+ json: false,
56
+ debug: false,
57
+ remote: false,
58
+ help: false,
59
+ version: false
60
+ };
61
+ for (let i = 0; i < argv.length; i++) {
62
+ const token = argv[i];
63
+ if (!token.startsWith("-")) {
64
+ if (result.path !== void 0) {
65
+ throw new Error(`Unexpected argument: ${token}`);
66
+ }
67
+ result.path = token;
68
+ continue;
69
+ }
70
+ const raw = token.replace(/^--?/, "");
71
+ const eq = raw.indexOf("=");
72
+ const name = eq === -1 ? raw : raw.slice(0, eq);
73
+ const inlineValue = eq === -1 ? void 0 : raw.slice(eq + 1);
74
+ if (name in BOOLEAN_FLAGS) {
75
+ if (inlineValue !== void 0) {
76
+ throw new Error(`Option --${name} does not take a value`);
77
+ }
78
+ result[BOOLEAN_FLAGS[name]] = true;
79
+ continue;
80
+ }
81
+ const takeValue = () => {
82
+ if (inlineValue !== void 0) return inlineValue;
83
+ i++;
84
+ if (i >= argv.length) {
85
+ throw new Error(`Option --${name} requires a value`);
86
+ }
87
+ return argv[i];
88
+ };
89
+ if (name === "data") {
90
+ throw new Error("Use --data.<key> <value>, e.g. --data.name World");
91
+ }
92
+ if (name.startsWith("data.")) {
93
+ setNested(result.data, name.slice("data.".length), takeValue());
94
+ continue;
95
+ }
96
+ if (VALUE_FLAGS.has(name)) {
97
+ const value = takeValue();
98
+ if (name === "stdin") result.stdinKey = value;
99
+ else if (name === "model") result.model = value;
100
+ else if (name === "provider") result.provider = value;
101
+ else if (name === "parser") result.parser = value;
102
+ continue;
103
+ }
104
+ throw new Error(`Unknown option: ${token}`);
105
+ }
106
+ return result;
107
+ }
108
+
109
+ // src/cli/runCli.ts
110
+ var HELP = `llm-exe \u2014 run an llm-exe config file (no code required)
111
+
112
+ Usage:
113
+ llm-exe <path> [options]
114
+ cat input | llm-exe <path> --stdin <key>
115
+
116
+ Arguments:
117
+ <path> Path to a config file (.json, .yml, .yaml, .md),
118
+ or an http(s) URL with --remote
119
+
120
+ Options:
121
+ --data.<key> <value> Set a template variable (dotted keys nest)
122
+ --model <value> Override the config's model
123
+ --provider <value> Override the config's provider
124
+ --parser <value> Override the config's parser
125
+ --stdin <key> Bind piped stdin to a data variable
126
+ --json Print { result, metadata } as JSON to stdout
127
+ --debug Print execution metadata to stderr
128
+ --remote Allow fetching a config from an http(s) URL
129
+ -h, --help Show this help
130
+ -v, --version Show version
131
+
132
+ Auth uses the same environment variables as the package
133
+ (OPENAI_API_KEY, ANTHROPIC_API_KEY, ...). The result goes to stdout;
134
+ metadata and errors go to stderr, so it composes with pipes.`;
135
+ function isUrl(value) {
136
+ return /^https?:\/\//i.test(value);
137
+ }
138
+ function formatResult(result) {
139
+ if (typeof result === "string") return result;
140
+ return JSON.stringify(result, null, 2);
141
+ }
142
+ async function runCli(argv, io) {
143
+ let args;
144
+ try {
145
+ args = parseArgs(argv);
146
+ } catch (error) {
147
+ io.stderr(error.message);
148
+ io.stderr("Run `llm-exe --help` for usage.");
149
+ return 1;
150
+ }
151
+ if (args.help) {
152
+ io.stdout(HELP);
153
+ return 0;
154
+ }
155
+ if (args.version) {
156
+ io.stdout(io.version);
157
+ return 0;
158
+ }
159
+ if (!args.path) {
160
+ io.stderr("Missing config path.");
161
+ io.stderr("Usage: llm-exe <path> [options] \u2014 see `llm-exe --help`.");
162
+ return 1;
163
+ }
164
+ if (isUrl(args.path) && !args.remote) {
165
+ io.stderr(
166
+ `Refusing to fetch a remote config without --remote: ${args.path}`
167
+ );
168
+ return 1;
169
+ }
170
+ try {
171
+ const data = { ...args.data };
172
+ if (args.stdinKey) {
173
+ data[args.stdinKey] = await io.readStdin();
174
+ }
175
+ const patch = {};
176
+ if (args.model !== void 0) patch.model = args.model;
177
+ if (args.provider !== void 0) patch.provider = args.provider;
178
+ if (args.parser !== void 0) patch.parser = args.parser;
179
+ if (Object.keys(data).length > 0) patch.data = data;
180
+ const config = isUrl(args.path) ? await loadConfigFromUrl(args.path, patch) : await loadConfigFromFile(args.path, patch);
181
+ let metadata;
182
+ const executor = executorFromConfig(config, {
183
+ hooks: {
184
+ onComplete(meta) {
185
+ metadata = meta;
186
+ }
187
+ }
188
+ });
189
+ const result = await executor.execute(
190
+ config.data ?? {},
191
+ config.executorOptions
192
+ );
193
+ if (args.debug) {
194
+ io.stderr(JSON.stringify(metadata, null, 2));
195
+ }
196
+ if (args.json) {
197
+ io.stdout(JSON.stringify({ result, metadata }, null, 2));
198
+ } else {
199
+ io.stdout(formatResult(result));
200
+ }
201
+ return 0;
202
+ } catch (error) {
203
+ if (isLlmExeError(error)) {
204
+ io.stderr(`Error [${error.code}]: ${error.message}`);
205
+ } else {
206
+ io.stderr(`Error: ${error?.message ?? String(error)}`);
207
+ }
208
+ return 1;
209
+ }
210
+ }
211
+
212
+ // src/cli/cli.ts
213
+ function readVersion() {
214
+ try {
215
+ const scriptPath = process.argv[1];
216
+ const pkgPath = resolve(dirname(scriptPath), "../../package.json");
217
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
218
+ return pkg.version ?? "unknown";
219
+ } catch {
220
+ return "unknown";
221
+ }
222
+ }
223
+ function readStdin() {
224
+ return new Promise((resolveFn) => {
225
+ if (process.stdin.isTTY) {
226
+ resolveFn("");
227
+ return;
228
+ }
229
+ let data = "";
230
+ process.stdin.setEncoding("utf8");
231
+ process.stdin.on("data", (chunk) => data += chunk);
232
+ process.stdin.on("end", () => resolveFn(data));
233
+ process.stdin.on("error", () => resolveFn(data));
234
+ });
235
+ }
236
+ function write(stream, text) {
237
+ stream.write(text.endsWith("\n") ? text : `${text}
238
+ `);
239
+ }
240
+ runCli(process.argv.slice(2), {
241
+ stdout: (text) => write(process.stdout, text),
242
+ stderr: (text) => write(process.stderr, text),
243
+ readStdin,
244
+ version: readVersion()
245
+ }).then((code) => {
246
+ process.exitCode = code;
247
+ }).catch((error) => {
248
+ process.stderr.write(`Fatal: ${error?.message ?? String(error)}
249
+ `);
250
+ process.exitCode = 1;
251
+ });
@@ -0,0 +1,15 @@
1
+ import { E as ExecutorConfigPatch, a as ExecutorConfig, b as ExecutorCreateOptions, L as LlmExecutor, R as RunOverrides } from '../types-CDOBItt6.mjs';
2
+ import 'json-schema-to-ts';
3
+
4
+ /**
5
+ * Read and normalize a config from a file path. Format is inferred from the
6
+ * extension, falling back to "auto". NODE-ONLY (`node:fs/promises`) — reached
7
+ * via the `llm-exe/node` subpath, never from the browser-safe root barrel.
8
+ */
9
+ declare function loadConfigFromFile(path: string, patch?: ExecutorConfigPatch): Promise<ExecutorConfig>;
10
+ /** Assemble a native executor from a config file. Mirrors `executorFromConfig`. */
11
+ declare function executorFromFile(path: string, patch?: ExecutorConfigPatch, createOptions?: ExecutorCreateOptions): Promise<LlmExecutor<any, any, any, any>>;
12
+ /** Run a config file once, binding config defaults. The CLI's primitive. */
13
+ declare function runFile(path: string, patch?: ExecutorConfigPatch, overrides?: RunOverrides, createOptions?: ExecutorCreateOptions): Promise<unknown>;
14
+
15
+ export { executorFromFile, loadConfigFromFile, runFile };
@@ -0,0 +1,15 @@
1
+ import { E as ExecutorConfigPatch, a as ExecutorConfig, b as ExecutorCreateOptions, L as LlmExecutor, R as RunOverrides } from '../types-CDOBItt6.js';
2
+ import 'json-schema-to-ts';
3
+
4
+ /**
5
+ * Read and normalize a config from a file path. Format is inferred from the
6
+ * extension, falling back to "auto". NODE-ONLY (`node:fs/promises`) — reached
7
+ * via the `llm-exe/node` subpath, never from the browser-safe root barrel.
8
+ */
9
+ declare function loadConfigFromFile(path: string, patch?: ExecutorConfigPatch): Promise<ExecutorConfig>;
10
+ /** Assemble a native executor from a config file. Mirrors `executorFromConfig`. */
11
+ declare function executorFromFile(path: string, patch?: ExecutorConfigPatch, createOptions?: ExecutorCreateOptions): Promise<LlmExecutor<any, any, any, any>>;
12
+ /** Run a config file once, binding config defaults. The CLI's primitive. */
13
+ declare function runFile(path: string, patch?: ExecutorConfigPatch, overrides?: RunOverrides, createOptions?: ExecutorCreateOptions): Promise<unknown>;
14
+
15
+ export { executorFromFile, loadConfigFromFile, runFile };