llmist 0.6.2 → 0.8.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-TSR25DAY.js → chunk-4IMGADVY.js} +2 -2
- package/dist/{chunk-DVK6ZQOV.js → chunk-62M4TDAK.js} +501 -78
- package/dist/chunk-62M4TDAK.js.map +1 -0
- package/dist/cli.cjs +946 -197
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +436 -110
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +511 -88
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -9
- package/dist/index.d.ts +6 -9
- package/dist/index.js +2 -2
- package/dist/{mock-stream-B5R6XPif.d.cts → mock-stream-CjmvWDc3.d.cts} +91 -20
- package/dist/{mock-stream-B5R6XPif.d.ts → mock-stream-CjmvWDc3.d.ts} +91 -20
- package/dist/testing/index.cjs +497 -74
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +2 -2
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/package.json +2 -1
- package/dist/chunk-DVK6ZQOV.js.map +0 -1
- /package/dist/{chunk-TSR25DAY.js.map → chunk-4IMGADVY.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
init_model_shortcuts,
|
|
23
23
|
init_registry,
|
|
24
24
|
resolveModel
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-62M4TDAK.js";
|
|
26
26
|
|
|
27
27
|
// src/cli/constants.ts
|
|
28
28
|
var CLI_NAME = "llmist";
|
|
@@ -47,7 +47,8 @@ var OPTION_FLAGS = {
|
|
|
47
47
|
logFile: "--log-file <path>",
|
|
48
48
|
logReset: "--log-reset",
|
|
49
49
|
noBuiltins: "--no-builtins",
|
|
50
|
-
noBuiltinInteraction: "--no-builtin-interaction"
|
|
50
|
+
noBuiltinInteraction: "--no-builtin-interaction",
|
|
51
|
+
quiet: "-q, --quiet"
|
|
51
52
|
};
|
|
52
53
|
var OPTION_DESCRIPTIONS = {
|
|
53
54
|
model: "Model identifier, e.g. openai:gpt-5-nano or anthropic:claude-sonnet-4-5.",
|
|
@@ -61,7 +62,8 @@ var OPTION_DESCRIPTIONS = {
|
|
|
61
62
|
logFile: "Path to log file. When set, logs are written to file instead of stderr.",
|
|
62
63
|
logReset: "Reset (truncate) the log file at session start instead of appending.",
|
|
63
64
|
noBuiltins: "Disable built-in gadgets (AskUser, TellUser).",
|
|
64
|
-
noBuiltinInteraction: "Disable interactive gadgets (AskUser) while keeping TellUser."
|
|
65
|
+
noBuiltinInteraction: "Disable interactive gadgets (AskUser) while keeping TellUser.",
|
|
66
|
+
quiet: "Suppress all output except content (text and TellUser messages)."
|
|
65
67
|
};
|
|
66
68
|
var SUMMARY_PREFIX = "[llmist]";
|
|
67
69
|
|
|
@@ -71,7 +73,7 @@ import { Command, InvalidArgumentError as InvalidArgumentError3 } from "commande
|
|
|
71
73
|
// package.json
|
|
72
74
|
var package_default = {
|
|
73
75
|
name: "llmist",
|
|
74
|
-
version: "0.
|
|
76
|
+
version: "0.7.0",
|
|
75
77
|
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.",
|
|
76
78
|
type: "module",
|
|
77
79
|
main: "dist/index.cjs",
|
|
@@ -155,6 +157,7 @@ var package_default = {
|
|
|
155
157
|
"@google/genai": "^1.27.0",
|
|
156
158
|
chalk: "^5.6.2",
|
|
157
159
|
commander: "^12.1.0",
|
|
160
|
+
eta: "^4.4.1",
|
|
158
161
|
"js-toml": "^1.0.2",
|
|
159
162
|
"js-yaml": "^4.1.0",
|
|
160
163
|
marked: "^15.0.12",
|
|
@@ -219,7 +222,7 @@ var tellUser = createGadget({
|
|
|
219
222
|
name: "TellUser",
|
|
220
223
|
description: "Tell the user something important. Set done=true when your work is complete and you want to end the conversation.",
|
|
221
224
|
schema: z.object({
|
|
222
|
-
message: z.string().describe("The message to display to the user in Markdown"),
|
|
225
|
+
message: z.string().optional().describe("The message to display to the user in Markdown"),
|
|
223
226
|
done: z.boolean().default(false).describe("Set to true to end the conversation, false to continue"),
|
|
224
227
|
type: z.enum(["info", "success", "warning", "error"]).default("info").describe("Message type: info, success, warning, or error")
|
|
225
228
|
}),
|
|
@@ -239,9 +242,20 @@ var tellUser = createGadget({
|
|
|
239
242
|
done: false,
|
|
240
243
|
type: "warning"
|
|
241
244
|
}
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
comment: "Share detailed analysis with bullet points (use heredoc for multiline)",
|
|
248
|
+
params: {
|
|
249
|
+
message: "Here's what I found in the codebase:\n\n1. **Main entry point**: `src/index.ts` exports all public APIs\n2. **Core logic**: Located in `src/core/` with 5 modules\n3. **Tests**: Good coverage in `src/__tests__/`\n\nI'll continue exploring the core modules.",
|
|
250
|
+
done: false,
|
|
251
|
+
type: "info"
|
|
252
|
+
}
|
|
242
253
|
}
|
|
243
254
|
],
|
|
244
255
|
execute: ({ message, done, type }) => {
|
|
256
|
+
if (!message || message.trim() === "") {
|
|
257
|
+
return "\u26A0\uFE0F TellUser was called without a message. Please provide content in the 'message' field.";
|
|
258
|
+
}
|
|
245
259
|
const prefixes = {
|
|
246
260
|
info: "\u2139\uFE0F ",
|
|
247
261
|
success: "\u2705 ",
|
|
@@ -263,12 +277,19 @@ import fs from "node:fs";
|
|
|
263
277
|
import path from "node:path";
|
|
264
278
|
import { pathToFileURL } from "node:url";
|
|
265
279
|
var PATH_PREFIXES = [".", "/", "~"];
|
|
280
|
+
function isGadgetLike(value) {
|
|
281
|
+
if (typeof value !== "object" || value === null) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
const obj = value;
|
|
285
|
+
return typeof obj.execute === "function" && typeof obj.description === "string" && ("parameterSchema" in obj || "schema" in obj);
|
|
286
|
+
}
|
|
266
287
|
function isGadgetConstructor(value) {
|
|
267
288
|
if (typeof value !== "function") {
|
|
268
289
|
return false;
|
|
269
290
|
}
|
|
270
291
|
const prototype = value.prototype;
|
|
271
|
-
return Boolean(prototype) && prototype instanceof BaseGadget;
|
|
292
|
+
return Boolean(prototype) && (prototype instanceof BaseGadget || isGadgetLike(prototype));
|
|
272
293
|
}
|
|
273
294
|
function expandHomePath(input) {
|
|
274
295
|
if (!input.startsWith("~")) {
|
|
@@ -305,7 +326,7 @@ function extractGadgetsFromModule(moduleExports) {
|
|
|
305
326
|
return;
|
|
306
327
|
}
|
|
307
328
|
visited.add(value);
|
|
308
|
-
if (value instanceof BaseGadget) {
|
|
329
|
+
if (value instanceof BaseGadget || isGadgetLike(value)) {
|
|
309
330
|
results.push(value);
|
|
310
331
|
return;
|
|
311
332
|
}
|
|
@@ -430,8 +451,14 @@ function renderSummary(metadata) {
|
|
|
430
451
|
parts.push(chalk.magenta(metadata.model));
|
|
431
452
|
}
|
|
432
453
|
if (metadata.usage) {
|
|
433
|
-
const { inputTokens, outputTokens } = metadata.usage;
|
|
454
|
+
const { inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens } = metadata.usage;
|
|
434
455
|
parts.push(chalk.dim("\u2191") + chalk.yellow(` ${formatTokens(inputTokens)}`));
|
|
456
|
+
if (cachedInputTokens && cachedInputTokens > 0) {
|
|
457
|
+
parts.push(chalk.dim("\u27F3") + chalk.blue(` ${formatTokens(cachedInputTokens)}`));
|
|
458
|
+
}
|
|
459
|
+
if (cacheCreationInputTokens && cacheCreationInputTokens > 0) {
|
|
460
|
+
parts.push(chalk.dim("\u270E") + chalk.magenta(` ${formatTokens(cacheCreationInputTokens)}`));
|
|
461
|
+
}
|
|
435
462
|
parts.push(chalk.dim("\u2193") + chalk.green(` ${formatTokens(outputTokens)}`));
|
|
436
463
|
}
|
|
437
464
|
if (metadata.elapsedSeconds !== void 0 && metadata.elapsedSeconds > 0) {
|
|
@@ -523,53 +550,6 @@ ${rendered}`;
|
|
|
523
550
|
}
|
|
524
551
|
|
|
525
552
|
// src/cli/utils.ts
|
|
526
|
-
var RARE_EMOJI = [
|
|
527
|
-
"\u{1F531}",
|
|
528
|
-
"\u2697\uFE0F",
|
|
529
|
-
"\u{1F9FF}",
|
|
530
|
-
"\u{1F530}",
|
|
531
|
-
"\u269B\uFE0F",
|
|
532
|
-
"\u{1F3FA}",
|
|
533
|
-
"\u{1F9EB}",
|
|
534
|
-
"\u{1F52C}",
|
|
535
|
-
"\u2695\uFE0F",
|
|
536
|
-
"\u{1F5DD}\uFE0F",
|
|
537
|
-
"\u2696\uFE0F",
|
|
538
|
-
"\u{1F52E}",
|
|
539
|
-
"\u{1FAAC}",
|
|
540
|
-
"\u{1F9EC}",
|
|
541
|
-
"\u2699\uFE0F",
|
|
542
|
-
"\u{1F529}",
|
|
543
|
-
"\u{1FA9B}",
|
|
544
|
-
"\u26CF\uFE0F",
|
|
545
|
-
"\u{1FA83}",
|
|
546
|
-
"\u{1F3F9}",
|
|
547
|
-
"\u{1F6E1}\uFE0F",
|
|
548
|
-
"\u2694\uFE0F",
|
|
549
|
-
"\u{1F5E1}\uFE0F",
|
|
550
|
-
"\u{1FA93}",
|
|
551
|
-
"\u{1F5C3}\uFE0F",
|
|
552
|
-
"\u{1F4DC}",
|
|
553
|
-
"\u{1F4EF}",
|
|
554
|
-
"\u{1F3B4}",
|
|
555
|
-
"\u{1F004}",
|
|
556
|
-
"\u{1F3B2}"
|
|
557
|
-
];
|
|
558
|
-
function generateMarkers() {
|
|
559
|
-
const pick = (count) => {
|
|
560
|
-
const result = [];
|
|
561
|
-
const pool = [...RARE_EMOJI];
|
|
562
|
-
for (let i = 0; i < count && pool.length > 0; i++) {
|
|
563
|
-
const idx = Math.floor(Math.random() * pool.length);
|
|
564
|
-
result.push(pool.splice(idx, 1)[0]);
|
|
565
|
-
}
|
|
566
|
-
return result.join("");
|
|
567
|
-
};
|
|
568
|
-
return {
|
|
569
|
-
startPrefix: pick(5),
|
|
570
|
-
endPrefix: pick(5)
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
553
|
function createNumericParser({
|
|
574
554
|
label,
|
|
575
555
|
integer = false,
|
|
@@ -647,6 +627,9 @@ var StreamProgress = class {
|
|
|
647
627
|
callOutputTokensEstimated = true;
|
|
648
628
|
callOutputChars = 0;
|
|
649
629
|
isStreaming = false;
|
|
630
|
+
// Cache token tracking for live cost estimation during streaming
|
|
631
|
+
callCachedInputTokens = 0;
|
|
632
|
+
callCacheCreationInputTokens = 0;
|
|
650
633
|
// Cumulative stats (cumulative mode)
|
|
651
634
|
totalStartTime = Date.now();
|
|
652
635
|
totalTokens = 0;
|
|
@@ -672,11 +655,13 @@ var StreamProgress = class {
|
|
|
672
655
|
this.callOutputTokensEstimated = true;
|
|
673
656
|
this.callOutputChars = 0;
|
|
674
657
|
this.isStreaming = false;
|
|
658
|
+
this.callCachedInputTokens = 0;
|
|
659
|
+
this.callCacheCreationInputTokens = 0;
|
|
675
660
|
this.start();
|
|
676
661
|
}
|
|
677
662
|
/**
|
|
678
663
|
* Ends the current LLM call. Updates cumulative stats and switches to cumulative mode.
|
|
679
|
-
* @param usage - Final token usage from the call
|
|
664
|
+
* @param usage - Final token usage from the call (including cached tokens if available)
|
|
680
665
|
*/
|
|
681
666
|
endCall(usage) {
|
|
682
667
|
this.iterations++;
|
|
@@ -688,7 +673,9 @@ var StreamProgress = class {
|
|
|
688
673
|
const cost = this.modelRegistry.estimateCost(
|
|
689
674
|
modelName,
|
|
690
675
|
usage.inputTokens,
|
|
691
|
-
usage.outputTokens
|
|
676
|
+
usage.outputTokens,
|
|
677
|
+
usage.cachedInputTokens ?? 0,
|
|
678
|
+
usage.cacheCreationInputTokens ?? 0
|
|
692
679
|
);
|
|
693
680
|
if (cost) {
|
|
694
681
|
this.totalCost += cost.totalCost;
|
|
@@ -728,6 +715,16 @@ var StreamProgress = class {
|
|
|
728
715
|
this.callOutputTokens = tokens;
|
|
729
716
|
this.callOutputTokensEstimated = estimated;
|
|
730
717
|
}
|
|
718
|
+
/**
|
|
719
|
+
* Sets cached token counts for the current call (from stream metadata).
|
|
720
|
+
* Used for live cost estimation during streaming.
|
|
721
|
+
* @param cachedInputTokens - Number of tokens read from cache (cheaper)
|
|
722
|
+
* @param cacheCreationInputTokens - Number of tokens written to cache (more expensive)
|
|
723
|
+
*/
|
|
724
|
+
setCachedTokens(cachedInputTokens, cacheCreationInputTokens) {
|
|
725
|
+
this.callCachedInputTokens = cachedInputTokens;
|
|
726
|
+
this.callCacheCreationInputTokens = cacheCreationInputTokens;
|
|
727
|
+
}
|
|
731
728
|
/**
|
|
732
729
|
* Get total elapsed time in seconds since the first call started.
|
|
733
730
|
* @returns Elapsed time in seconds with 1 decimal place
|
|
@@ -792,11 +789,32 @@ var StreamProgress = class {
|
|
|
792
789
|
parts.push(chalk2.dim("\u2193") + chalk2.green(` ${prefix}${formatTokens(outTokens)}`));
|
|
793
790
|
}
|
|
794
791
|
parts.push(chalk2.dim(`${elapsed}s`));
|
|
795
|
-
|
|
796
|
-
|
|
792
|
+
const callCost = this.calculateCurrentCallCost(outTokens);
|
|
793
|
+
if (callCost > 0) {
|
|
794
|
+
parts.push(chalk2.cyan(`$${formatCost(callCost)}`));
|
|
797
795
|
}
|
|
798
796
|
this.target.write(`\r${parts.join(chalk2.dim(" | "))} ${chalk2.cyan(spinner)}`);
|
|
799
797
|
}
|
|
798
|
+
/**
|
|
799
|
+
* Calculates live cost estimate for the current streaming call.
|
|
800
|
+
* Uses current input/output tokens and cached token counts.
|
|
801
|
+
*/
|
|
802
|
+
calculateCurrentCallCost(outputTokens) {
|
|
803
|
+
if (!this.modelRegistry || !this.model) return 0;
|
|
804
|
+
try {
|
|
805
|
+
const modelName = this.model.includes(":") ? this.model.split(":")[1] : this.model;
|
|
806
|
+
const cost = this.modelRegistry.estimateCost(
|
|
807
|
+
modelName,
|
|
808
|
+
this.callInputTokens,
|
|
809
|
+
outputTokens,
|
|
810
|
+
this.callCachedInputTokens,
|
|
811
|
+
this.callCacheCreationInputTokens
|
|
812
|
+
);
|
|
813
|
+
return cost?.totalCost ?? 0;
|
|
814
|
+
} catch {
|
|
815
|
+
return 0;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
800
818
|
renderCumulativeMode(spinner) {
|
|
801
819
|
const elapsed = ((Date.now() - this.totalStartTime) / 1e3).toFixed(1);
|
|
802
820
|
const parts = [];
|
|
@@ -945,7 +963,7 @@ function addCompleteOptions(cmd, defaults) {
|
|
|
945
963
|
OPTION_DESCRIPTIONS.maxTokens,
|
|
946
964
|
createNumericParser({ label: "Max tokens", integer: true, min: 1 }),
|
|
947
965
|
defaults?.["max-tokens"]
|
|
948
|
-
);
|
|
966
|
+
).option(OPTION_FLAGS.quiet, OPTION_DESCRIPTIONS.quiet, defaults?.quiet);
|
|
949
967
|
}
|
|
950
968
|
function addAgentOptions(cmd, defaults) {
|
|
951
969
|
const gadgetAccumulator = (value, previous = []) => [
|
|
@@ -974,7 +992,7 @@ function addAgentOptions(cmd, defaults) {
|
|
|
974
992
|
OPTION_FLAGS.noBuiltinInteraction,
|
|
975
993
|
OPTION_DESCRIPTIONS.noBuiltinInteraction,
|
|
976
994
|
defaults?.["builtin-interaction"] !== false
|
|
977
|
-
);
|
|
995
|
+
).option(OPTION_FLAGS.quiet, OPTION_DESCRIPTIONS.quiet, defaults?.quiet);
|
|
978
996
|
}
|
|
979
997
|
function configToCompleteOptions(config) {
|
|
980
998
|
const result = {};
|
|
@@ -982,6 +1000,7 @@ function configToCompleteOptions(config) {
|
|
|
982
1000
|
if (config.system !== void 0) result.system = config.system;
|
|
983
1001
|
if (config.temperature !== void 0) result.temperature = config.temperature;
|
|
984
1002
|
if (config["max-tokens"] !== void 0) result.maxTokens = config["max-tokens"];
|
|
1003
|
+
if (config.quiet !== void 0) result.quiet = config.quiet;
|
|
985
1004
|
return result;
|
|
986
1005
|
}
|
|
987
1006
|
function configToAgentOptions(config) {
|
|
@@ -995,6 +1014,11 @@ function configToAgentOptions(config) {
|
|
|
995
1014
|
if (config.builtins !== void 0) result.builtins = config.builtins;
|
|
996
1015
|
if (config["builtin-interaction"] !== void 0)
|
|
997
1016
|
result.builtinInteraction = config["builtin-interaction"];
|
|
1017
|
+
if (config["gadget-start-prefix"] !== void 0)
|
|
1018
|
+
result.gadgetStartPrefix = config["gadget-start-prefix"];
|
|
1019
|
+
if (config["gadget-end-prefix"] !== void 0)
|
|
1020
|
+
result.gadgetEndPrefix = config["gadget-end-prefix"];
|
|
1021
|
+
if (config.quiet !== void 0) result.quiet = config.quiet;
|
|
998
1022
|
return result;
|
|
999
1023
|
}
|
|
1000
1024
|
|
|
@@ -1040,9 +1064,10 @@ async function executeAgent(promptArg, options, env) {
|
|
|
1040
1064
|
const prompt = await resolvePrompt(promptArg, env);
|
|
1041
1065
|
const client = env.createClient();
|
|
1042
1066
|
const registry = new GadgetRegistry();
|
|
1067
|
+
const stdinIsInteractive = isInteractive(env.stdin);
|
|
1043
1068
|
if (options.builtins !== false) {
|
|
1044
1069
|
for (const gadget of builtinGadgets) {
|
|
1045
|
-
if (
|
|
1070
|
+
if (gadget.name === "AskUser" && (options.builtinInteraction === false || !stdinIsInteractive)) {
|
|
1046
1071
|
continue;
|
|
1047
1072
|
}
|
|
1048
1073
|
registry.registerByClass(gadget);
|
|
@@ -1100,6 +1125,10 @@ async function executeAgent(promptArg, options, env) {
|
|
|
1100
1125
|
if (context.usage.outputTokens) {
|
|
1101
1126
|
progress.setOutputTokens(context.usage.outputTokens, false);
|
|
1102
1127
|
}
|
|
1128
|
+
progress.setCachedTokens(
|
|
1129
|
+
context.usage.cachedInputTokens ?? 0,
|
|
1130
|
+
context.usage.cacheCreationInputTokens ?? 0
|
|
1131
|
+
);
|
|
1103
1132
|
}
|
|
1104
1133
|
},
|
|
1105
1134
|
// onLLMCallComplete: Finalize metrics after each LLM call
|
|
@@ -1118,11 +1147,13 @@ async function executeAgent(promptArg, options, env) {
|
|
|
1118
1147
|
let callCost;
|
|
1119
1148
|
if (context.usage && client.modelRegistry) {
|
|
1120
1149
|
try {
|
|
1121
|
-
const modelName = options.model.includes(":") ? options.model.split(":")[1] : options.model;
|
|
1150
|
+
const modelName = context.options.model.includes(":") ? context.options.model.split(":")[1] : context.options.model;
|
|
1122
1151
|
const costResult = client.modelRegistry.estimateCost(
|
|
1123
1152
|
modelName,
|
|
1124
1153
|
context.usage.inputTokens,
|
|
1125
|
-
context.usage.outputTokens
|
|
1154
|
+
context.usage.outputTokens,
|
|
1155
|
+
context.usage.cachedInputTokens ?? 0,
|
|
1156
|
+
context.usage.cacheCreationInputTokens ?? 0
|
|
1126
1157
|
);
|
|
1127
1158
|
if (costResult) callCost = costResult.totalCost;
|
|
1128
1159
|
} catch {
|
|
@@ -1130,7 +1161,7 @@ async function executeAgent(promptArg, options, env) {
|
|
|
1130
1161
|
}
|
|
1131
1162
|
const callElapsed = progress.getCallElapsedSeconds();
|
|
1132
1163
|
progress.endCall(context.usage);
|
|
1133
|
-
if (
|
|
1164
|
+
if (!options.quiet) {
|
|
1134
1165
|
const summary = renderSummary({
|
|
1135
1166
|
iterations: context.iteration + 1,
|
|
1136
1167
|
model: options.model,
|
|
@@ -1209,9 +1240,27 @@ Command rejected by user with message: "${response}"`
|
|
|
1209
1240
|
builder.withGadgets(...gadgets);
|
|
1210
1241
|
}
|
|
1211
1242
|
builder.withParameterFormat(options.parameterFormat);
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1243
|
+
if (options.gadgetStartPrefix) {
|
|
1244
|
+
builder.withGadgetStartPrefix(options.gadgetStartPrefix);
|
|
1245
|
+
}
|
|
1246
|
+
if (options.gadgetEndPrefix) {
|
|
1247
|
+
builder.withGadgetEndPrefix(options.gadgetEndPrefix);
|
|
1248
|
+
}
|
|
1249
|
+
builder.withSyntheticGadgetCall(
|
|
1250
|
+
"TellUser",
|
|
1251
|
+
{
|
|
1252
|
+
message: "\u{1F44B} Hello! I'm ready to help.\n\nHere's what I can do:\n- Analyze your codebase\n- Execute commands\n- Answer questions\n\nWhat would you like me to work on?",
|
|
1253
|
+
done: false,
|
|
1254
|
+
type: "info"
|
|
1255
|
+
},
|
|
1256
|
+
"\u2139\uFE0F \u{1F44B} Hello! I'm ready to help.\n\nHere's what I can do:\n- Analyze your codebase\n- Execute commands\n- Answer questions\n\nWhat would you like me to work on?"
|
|
1257
|
+
);
|
|
1258
|
+
builder.withTextOnlyHandler("acknowledge");
|
|
1259
|
+
builder.withTextWithGadgetsHandler({
|
|
1260
|
+
gadgetName: "TellUser",
|
|
1261
|
+
parameterMapping: (text) => ({ message: text, done: false, type: "info" }),
|
|
1262
|
+
resultMapping: (text) => `\u2139\uFE0F ${text}`
|
|
1263
|
+
});
|
|
1215
1264
|
const agent = builder.ask(prompt);
|
|
1216
1265
|
for await (const event of agent.run()) {
|
|
1217
1266
|
if (event.type === "text") {
|
|
@@ -1219,7 +1268,14 @@ Command rejected by user with message: "${response}"`
|
|
|
1219
1268
|
printer.write(event.content);
|
|
1220
1269
|
} else if (event.type === "gadget_result") {
|
|
1221
1270
|
progress.pause();
|
|
1222
|
-
if (
|
|
1271
|
+
if (options.quiet) {
|
|
1272
|
+
if (event.result.gadgetName === "TellUser" && event.result.parameters?.message) {
|
|
1273
|
+
const message = String(event.result.parameters.message);
|
|
1274
|
+
const rendered = renderMarkdown(message);
|
|
1275
|
+
env.stdout.write(`${rendered}
|
|
1276
|
+
`);
|
|
1277
|
+
}
|
|
1278
|
+
} else {
|
|
1223
1279
|
const tokenCount = await countGadgetOutputTokens(event.result.result);
|
|
1224
1280
|
env.stderr.write(`${formatGadgetSummary({ ...event.result, tokenCount })}
|
|
1225
1281
|
`);
|
|
@@ -1228,7 +1284,7 @@ Command rejected by user with message: "${response}"`
|
|
|
1228
1284
|
}
|
|
1229
1285
|
progress.complete();
|
|
1230
1286
|
printer.ensureNewline();
|
|
1231
|
-
if (
|
|
1287
|
+
if (!options.quiet && iterations > 1) {
|
|
1232
1288
|
env.stderr.write(`${chalk3.dim("\u2500".repeat(40))}
|
|
1233
1289
|
`);
|
|
1234
1290
|
const summary = renderOverallSummary({
|
|
@@ -1301,7 +1357,7 @@ async function executeComplete(promptArg, options, env) {
|
|
|
1301
1357
|
progress.endCall(usage);
|
|
1302
1358
|
progress.complete();
|
|
1303
1359
|
printer.ensureNewline();
|
|
1304
|
-
if (stderrTTY) {
|
|
1360
|
+
if (stderrTTY && !options.quiet) {
|
|
1305
1361
|
const summary = renderSummary({ finishReason, usage, cost: progress.getTotalCost() });
|
|
1306
1362
|
if (summary) {
|
|
1307
1363
|
env.stderr.write(`${summary}
|
|
@@ -1322,9 +1378,102 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
1322
1378
|
import { homedir } from "node:os";
|
|
1323
1379
|
import { join } from "node:path";
|
|
1324
1380
|
import { load as parseToml } from "js-toml";
|
|
1381
|
+
|
|
1382
|
+
// src/cli/templates.ts
|
|
1383
|
+
import { Eta } from "eta";
|
|
1384
|
+
var TemplateError = class extends Error {
|
|
1385
|
+
constructor(message, promptName, configPath) {
|
|
1386
|
+
super(promptName ? `[prompts.${promptName}]: ${message}` : message);
|
|
1387
|
+
this.promptName = promptName;
|
|
1388
|
+
this.configPath = configPath;
|
|
1389
|
+
this.name = "TemplateError";
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
1392
|
+
function createTemplateEngine(prompts, configPath) {
|
|
1393
|
+
const eta = new Eta({
|
|
1394
|
+
views: "/",
|
|
1395
|
+
// Required but we use named templates
|
|
1396
|
+
autoEscape: false,
|
|
1397
|
+
// Don't escape - these are prompts, not HTML
|
|
1398
|
+
autoTrim: false
|
|
1399
|
+
// Preserve whitespace in prompts
|
|
1400
|
+
});
|
|
1401
|
+
for (const [name, template] of Object.entries(prompts)) {
|
|
1402
|
+
try {
|
|
1403
|
+
eta.loadTemplate(`@${name}`, template);
|
|
1404
|
+
} catch (error) {
|
|
1405
|
+
throw new TemplateError(
|
|
1406
|
+
error instanceof Error ? error.message : String(error),
|
|
1407
|
+
name,
|
|
1408
|
+
configPath
|
|
1409
|
+
);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
return eta;
|
|
1413
|
+
}
|
|
1414
|
+
function resolveTemplate(eta, template, context = {}, configPath) {
|
|
1415
|
+
try {
|
|
1416
|
+
const fullContext = {
|
|
1417
|
+
...context,
|
|
1418
|
+
env: process.env
|
|
1419
|
+
};
|
|
1420
|
+
return eta.renderString(template, fullContext);
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
throw new TemplateError(
|
|
1423
|
+
error instanceof Error ? error.message : String(error),
|
|
1424
|
+
void 0,
|
|
1425
|
+
configPath
|
|
1426
|
+
);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
function validatePrompts(prompts, configPath) {
|
|
1430
|
+
const eta = createTemplateEngine(prompts, configPath);
|
|
1431
|
+
for (const [name, template] of Object.entries(prompts)) {
|
|
1432
|
+
try {
|
|
1433
|
+
eta.renderString(template, { env: {} });
|
|
1434
|
+
} catch (error) {
|
|
1435
|
+
throw new TemplateError(
|
|
1436
|
+
error instanceof Error ? error.message : String(error),
|
|
1437
|
+
name,
|
|
1438
|
+
configPath
|
|
1439
|
+
);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
function validateEnvVars(template, promptName, configPath) {
|
|
1444
|
+
const envVarPattern = /<%=\s*it\.env\.(\w+)\s*%>/g;
|
|
1445
|
+
const matches = template.matchAll(envVarPattern);
|
|
1446
|
+
for (const match of matches) {
|
|
1447
|
+
const varName = match[1];
|
|
1448
|
+
if (process.env[varName] === void 0) {
|
|
1449
|
+
throw new TemplateError(
|
|
1450
|
+
`Environment variable '${varName}' is not set`,
|
|
1451
|
+
promptName,
|
|
1452
|
+
configPath
|
|
1453
|
+
);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
function hasTemplateSyntax(str) {
|
|
1458
|
+
return str.includes("<%");
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
// src/cli/config.ts
|
|
1325
1462
|
var GLOBAL_CONFIG_KEYS = /* @__PURE__ */ new Set(["log-level", "log-file", "log-reset"]);
|
|
1326
1463
|
var VALID_LOG_LEVELS = ["silly", "trace", "debug", "info", "warn", "error", "fatal"];
|
|
1327
|
-
var COMPLETE_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
1464
|
+
var COMPLETE_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
1465
|
+
"model",
|
|
1466
|
+
"system",
|
|
1467
|
+
"temperature",
|
|
1468
|
+
"max-tokens",
|
|
1469
|
+
"quiet",
|
|
1470
|
+
"inherits",
|
|
1471
|
+
"log-level",
|
|
1472
|
+
"log-file",
|
|
1473
|
+
"log-reset",
|
|
1474
|
+
"type"
|
|
1475
|
+
// Allowed for inheritance compatibility, ignored for built-in commands
|
|
1476
|
+
]);
|
|
1328
1477
|
var AGENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
1329
1478
|
"model",
|
|
1330
1479
|
"system",
|
|
@@ -1333,16 +1482,22 @@ var AGENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
1333
1482
|
"gadget",
|
|
1334
1483
|
"parameter-format",
|
|
1335
1484
|
"builtins",
|
|
1336
|
-
"builtin-interaction"
|
|
1485
|
+
"builtin-interaction",
|
|
1486
|
+
"gadget-start-prefix",
|
|
1487
|
+
"gadget-end-prefix",
|
|
1488
|
+
"quiet",
|
|
1489
|
+
"inherits",
|
|
1490
|
+
"log-level",
|
|
1491
|
+
"log-file",
|
|
1492
|
+
"log-reset",
|
|
1493
|
+
"type"
|
|
1494
|
+
// Allowed for inheritance compatibility, ignored for built-in commands
|
|
1337
1495
|
]);
|
|
1338
1496
|
var CUSTOM_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
1339
1497
|
...COMPLETE_CONFIG_KEYS,
|
|
1340
1498
|
...AGENT_CONFIG_KEYS,
|
|
1341
1499
|
"type",
|
|
1342
|
-
"description"
|
|
1343
|
-
"log-level",
|
|
1344
|
-
"log-file",
|
|
1345
|
-
"log-reset"
|
|
1500
|
+
"description"
|
|
1346
1501
|
]);
|
|
1347
1502
|
var VALID_PARAMETER_FORMATS = ["json", "yaml", "toml", "auto"];
|
|
1348
1503
|
function getConfigPath() {
|
|
@@ -1393,6 +1548,39 @@ function validateStringArray(value, key, section) {
|
|
|
1393
1548
|
}
|
|
1394
1549
|
return value;
|
|
1395
1550
|
}
|
|
1551
|
+
function validateInherits(value, section) {
|
|
1552
|
+
if (typeof value === "string") {
|
|
1553
|
+
return value;
|
|
1554
|
+
}
|
|
1555
|
+
if (Array.isArray(value)) {
|
|
1556
|
+
for (let i = 0; i < value.length; i++) {
|
|
1557
|
+
if (typeof value[i] !== "string") {
|
|
1558
|
+
throw new ConfigError(`[${section}].inherits[${i}] must be a string`);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
return value;
|
|
1562
|
+
}
|
|
1563
|
+
throw new ConfigError(`[${section}].inherits must be a string or array of strings`);
|
|
1564
|
+
}
|
|
1565
|
+
function validateLoggingConfig(raw, section) {
|
|
1566
|
+
const result = {};
|
|
1567
|
+
if ("log-level" in raw) {
|
|
1568
|
+
const level = validateString(raw["log-level"], "log-level", section);
|
|
1569
|
+
if (!VALID_LOG_LEVELS.includes(level)) {
|
|
1570
|
+
throw new ConfigError(
|
|
1571
|
+
`[${section}].log-level must be one of: ${VALID_LOG_LEVELS.join(", ")}`
|
|
1572
|
+
);
|
|
1573
|
+
}
|
|
1574
|
+
result["log-level"] = level;
|
|
1575
|
+
}
|
|
1576
|
+
if ("log-file" in raw) {
|
|
1577
|
+
result["log-file"] = validateString(raw["log-file"], "log-file", section);
|
|
1578
|
+
}
|
|
1579
|
+
if ("log-reset" in raw) {
|
|
1580
|
+
result["log-reset"] = validateBoolean(raw["log-reset"], "log-reset", section);
|
|
1581
|
+
}
|
|
1582
|
+
return result;
|
|
1583
|
+
}
|
|
1396
1584
|
function validateBaseConfig(raw, section) {
|
|
1397
1585
|
const result = {};
|
|
1398
1586
|
if ("model" in raw) {
|
|
@@ -1407,6 +1595,9 @@ function validateBaseConfig(raw, section) {
|
|
|
1407
1595
|
max: 2
|
|
1408
1596
|
});
|
|
1409
1597
|
}
|
|
1598
|
+
if ("inherits" in raw) {
|
|
1599
|
+
result.inherits = validateInherits(raw.inherits, section);
|
|
1600
|
+
}
|
|
1410
1601
|
return result;
|
|
1411
1602
|
}
|
|
1412
1603
|
function validateGlobalConfig(raw, section) {
|
|
@@ -1419,23 +1610,7 @@ function validateGlobalConfig(raw, section) {
|
|
|
1419
1610
|
throw new ConfigError(`[${section}].${key} is not a valid option`);
|
|
1420
1611
|
}
|
|
1421
1612
|
}
|
|
1422
|
-
|
|
1423
|
-
if ("log-level" in rawObj) {
|
|
1424
|
-
const level = validateString(rawObj["log-level"], "log-level", section);
|
|
1425
|
-
if (!VALID_LOG_LEVELS.includes(level)) {
|
|
1426
|
-
throw new ConfigError(
|
|
1427
|
-
`[${section}].log-level must be one of: ${VALID_LOG_LEVELS.join(", ")}`
|
|
1428
|
-
);
|
|
1429
|
-
}
|
|
1430
|
-
result["log-level"] = level;
|
|
1431
|
-
}
|
|
1432
|
-
if ("log-file" in rawObj) {
|
|
1433
|
-
result["log-file"] = validateString(rawObj["log-file"], "log-file", section);
|
|
1434
|
-
}
|
|
1435
|
-
if ("log-reset" in rawObj) {
|
|
1436
|
-
result["log-reset"] = validateBoolean(rawObj["log-reset"], "log-reset", section);
|
|
1437
|
-
}
|
|
1438
|
-
return result;
|
|
1613
|
+
return validateLoggingConfig(rawObj, section);
|
|
1439
1614
|
}
|
|
1440
1615
|
function validateCompleteConfig(raw, section) {
|
|
1441
1616
|
if (typeof raw !== "object" || raw === null) {
|
|
@@ -1447,13 +1622,19 @@ function validateCompleteConfig(raw, section) {
|
|
|
1447
1622
|
throw new ConfigError(`[${section}].${key} is not a valid option`);
|
|
1448
1623
|
}
|
|
1449
1624
|
}
|
|
1450
|
-
const result = {
|
|
1625
|
+
const result = {
|
|
1626
|
+
...validateBaseConfig(rawObj, section),
|
|
1627
|
+
...validateLoggingConfig(rawObj, section)
|
|
1628
|
+
};
|
|
1451
1629
|
if ("max-tokens" in rawObj) {
|
|
1452
1630
|
result["max-tokens"] = validateNumber(rawObj["max-tokens"], "max-tokens", section, {
|
|
1453
1631
|
integer: true,
|
|
1454
1632
|
min: 1
|
|
1455
1633
|
});
|
|
1456
1634
|
}
|
|
1635
|
+
if ("quiet" in rawObj) {
|
|
1636
|
+
result.quiet = validateBoolean(rawObj.quiet, "quiet", section);
|
|
1637
|
+
}
|
|
1457
1638
|
return result;
|
|
1458
1639
|
}
|
|
1459
1640
|
function validateAgentConfig(raw, section) {
|
|
@@ -1466,7 +1647,10 @@ function validateAgentConfig(raw, section) {
|
|
|
1466
1647
|
throw new ConfigError(`[${section}].${key} is not a valid option`);
|
|
1467
1648
|
}
|
|
1468
1649
|
}
|
|
1469
|
-
const result = {
|
|
1650
|
+
const result = {
|
|
1651
|
+
...validateBaseConfig(rawObj, section),
|
|
1652
|
+
...validateLoggingConfig(rawObj, section)
|
|
1653
|
+
};
|
|
1470
1654
|
if ("max-iterations" in rawObj) {
|
|
1471
1655
|
result["max-iterations"] = validateNumber(rawObj["max-iterations"], "max-iterations", section, {
|
|
1472
1656
|
integer: true,
|
|
@@ -1495,6 +1679,23 @@ function validateAgentConfig(raw, section) {
|
|
|
1495
1679
|
section
|
|
1496
1680
|
);
|
|
1497
1681
|
}
|
|
1682
|
+
if ("gadget-start-prefix" in rawObj) {
|
|
1683
|
+
result["gadget-start-prefix"] = validateString(
|
|
1684
|
+
rawObj["gadget-start-prefix"],
|
|
1685
|
+
"gadget-start-prefix",
|
|
1686
|
+
section
|
|
1687
|
+
);
|
|
1688
|
+
}
|
|
1689
|
+
if ("gadget-end-prefix" in rawObj) {
|
|
1690
|
+
result["gadget-end-prefix"] = validateString(
|
|
1691
|
+
rawObj["gadget-end-prefix"],
|
|
1692
|
+
"gadget-end-prefix",
|
|
1693
|
+
section
|
|
1694
|
+
);
|
|
1695
|
+
}
|
|
1696
|
+
if ("quiet" in rawObj) {
|
|
1697
|
+
result.quiet = validateBoolean(rawObj.quiet, "quiet", section);
|
|
1698
|
+
}
|
|
1498
1699
|
return result;
|
|
1499
1700
|
}
|
|
1500
1701
|
function validateCustomConfig(raw, section) {
|
|
@@ -1550,26 +1751,42 @@ function validateCustomConfig(raw, section) {
|
|
|
1550
1751
|
section
|
|
1551
1752
|
);
|
|
1552
1753
|
}
|
|
1754
|
+
if ("gadget-start-prefix" in rawObj) {
|
|
1755
|
+
result["gadget-start-prefix"] = validateString(
|
|
1756
|
+
rawObj["gadget-start-prefix"],
|
|
1757
|
+
"gadget-start-prefix",
|
|
1758
|
+
section
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1761
|
+
if ("gadget-end-prefix" in rawObj) {
|
|
1762
|
+
result["gadget-end-prefix"] = validateString(
|
|
1763
|
+
rawObj["gadget-end-prefix"],
|
|
1764
|
+
"gadget-end-prefix",
|
|
1765
|
+
section
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1553
1768
|
if ("max-tokens" in rawObj) {
|
|
1554
1769
|
result["max-tokens"] = validateNumber(rawObj["max-tokens"], "max-tokens", section, {
|
|
1555
1770
|
integer: true,
|
|
1556
1771
|
min: 1
|
|
1557
1772
|
});
|
|
1558
1773
|
}
|
|
1559
|
-
if ("
|
|
1560
|
-
|
|
1561
|
-
if (!VALID_LOG_LEVELS.includes(level)) {
|
|
1562
|
-
throw new ConfigError(
|
|
1563
|
-
`[${section}].log-level must be one of: ${VALID_LOG_LEVELS.join(", ")}`
|
|
1564
|
-
);
|
|
1565
|
-
}
|
|
1566
|
-
result["log-level"] = level;
|
|
1774
|
+
if ("quiet" in rawObj) {
|
|
1775
|
+
result.quiet = validateBoolean(rawObj.quiet, "quiet", section);
|
|
1567
1776
|
}
|
|
1568
|
-
|
|
1569
|
-
|
|
1777
|
+
Object.assign(result, validateLoggingConfig(rawObj, section));
|
|
1778
|
+
return result;
|
|
1779
|
+
}
|
|
1780
|
+
function validatePromptsConfig(raw, section) {
|
|
1781
|
+
if (typeof raw !== "object" || raw === null) {
|
|
1782
|
+
throw new ConfigError(`[${section}] must be a table`);
|
|
1570
1783
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1784
|
+
const result = {};
|
|
1785
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
1786
|
+
if (typeof value !== "string") {
|
|
1787
|
+
throw new ConfigError(`[${section}].${key} must be a string`);
|
|
1788
|
+
}
|
|
1789
|
+
result[key] = value;
|
|
1573
1790
|
}
|
|
1574
1791
|
return result;
|
|
1575
1792
|
}
|
|
@@ -1587,6 +1804,8 @@ function validateConfig(raw, configPath) {
|
|
|
1587
1804
|
result.complete = validateCompleteConfig(value, key);
|
|
1588
1805
|
} else if (key === "agent") {
|
|
1589
1806
|
result.agent = validateAgentConfig(value, key);
|
|
1807
|
+
} else if (key === "prompts") {
|
|
1808
|
+
result.prompts = validatePromptsConfig(value, key);
|
|
1590
1809
|
} else {
|
|
1591
1810
|
result[key] = validateCustomConfig(value, key);
|
|
1592
1811
|
}
|
|
@@ -1622,12 +1841,119 @@ function loadConfig() {
|
|
|
1622
1841
|
configPath
|
|
1623
1842
|
);
|
|
1624
1843
|
}
|
|
1625
|
-
|
|
1844
|
+
const validated = validateConfig(raw, configPath);
|
|
1845
|
+
const inherited = resolveInheritance(validated, configPath);
|
|
1846
|
+
return resolveTemplatesInConfig(inherited, configPath);
|
|
1626
1847
|
}
|
|
1627
1848
|
function getCustomCommandNames(config) {
|
|
1628
|
-
const reserved = /* @__PURE__ */ new Set(["global", "complete", "agent"]);
|
|
1849
|
+
const reserved = /* @__PURE__ */ new Set(["global", "complete", "agent", "prompts"]);
|
|
1629
1850
|
return Object.keys(config).filter((key) => !reserved.has(key));
|
|
1630
1851
|
}
|
|
1852
|
+
function resolveTemplatesInConfig(config, configPath) {
|
|
1853
|
+
const prompts = config.prompts ?? {};
|
|
1854
|
+
const hasPrompts = Object.keys(prompts).length > 0;
|
|
1855
|
+
let hasTemplates = false;
|
|
1856
|
+
for (const [sectionName, section] of Object.entries(config)) {
|
|
1857
|
+
if (sectionName === "global" || sectionName === "prompts") continue;
|
|
1858
|
+
if (!section || typeof section !== "object") continue;
|
|
1859
|
+
const sectionObj = section;
|
|
1860
|
+
if (typeof sectionObj.system === "string" && hasTemplateSyntax(sectionObj.system)) {
|
|
1861
|
+
hasTemplates = true;
|
|
1862
|
+
break;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
for (const template of Object.values(prompts)) {
|
|
1866
|
+
if (hasTemplateSyntax(template)) {
|
|
1867
|
+
hasTemplates = true;
|
|
1868
|
+
break;
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
if (!hasPrompts && !hasTemplates) {
|
|
1872
|
+
return config;
|
|
1873
|
+
}
|
|
1874
|
+
try {
|
|
1875
|
+
validatePrompts(prompts, configPath);
|
|
1876
|
+
} catch (error) {
|
|
1877
|
+
if (error instanceof TemplateError) {
|
|
1878
|
+
throw new ConfigError(error.message, configPath);
|
|
1879
|
+
}
|
|
1880
|
+
throw error;
|
|
1881
|
+
}
|
|
1882
|
+
for (const [name, template] of Object.entries(prompts)) {
|
|
1883
|
+
try {
|
|
1884
|
+
validateEnvVars(template, name, configPath);
|
|
1885
|
+
} catch (error) {
|
|
1886
|
+
if (error instanceof TemplateError) {
|
|
1887
|
+
throw new ConfigError(error.message, configPath);
|
|
1888
|
+
}
|
|
1889
|
+
throw error;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
const eta = createTemplateEngine(prompts, configPath);
|
|
1893
|
+
const result = { ...config };
|
|
1894
|
+
for (const [sectionName, section] of Object.entries(config)) {
|
|
1895
|
+
if (sectionName === "global" || sectionName === "prompts") continue;
|
|
1896
|
+
if (!section || typeof section !== "object") continue;
|
|
1897
|
+
const sectionObj = section;
|
|
1898
|
+
if (typeof sectionObj.system === "string" && hasTemplateSyntax(sectionObj.system)) {
|
|
1899
|
+
try {
|
|
1900
|
+
validateEnvVars(sectionObj.system, void 0, configPath);
|
|
1901
|
+
} catch (error) {
|
|
1902
|
+
if (error instanceof TemplateError) {
|
|
1903
|
+
throw new ConfigError(`[${sectionName}].system: ${error.message}`, configPath);
|
|
1904
|
+
}
|
|
1905
|
+
throw error;
|
|
1906
|
+
}
|
|
1907
|
+
try {
|
|
1908
|
+
const resolved = resolveTemplate(eta, sectionObj.system, {}, configPath);
|
|
1909
|
+
result[sectionName] = {
|
|
1910
|
+
...sectionObj,
|
|
1911
|
+
system: resolved
|
|
1912
|
+
};
|
|
1913
|
+
} catch (error) {
|
|
1914
|
+
if (error instanceof TemplateError) {
|
|
1915
|
+
throw new ConfigError(`[${sectionName}].system: ${error.message}`, configPath);
|
|
1916
|
+
}
|
|
1917
|
+
throw error;
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
return result;
|
|
1922
|
+
}
|
|
1923
|
+
function resolveInheritance(config, configPath) {
|
|
1924
|
+
const resolved = {};
|
|
1925
|
+
const resolving = /* @__PURE__ */ new Set();
|
|
1926
|
+
function resolveSection(name) {
|
|
1927
|
+
if (name in resolved) {
|
|
1928
|
+
return resolved[name];
|
|
1929
|
+
}
|
|
1930
|
+
if (resolving.has(name)) {
|
|
1931
|
+
throw new ConfigError(`Circular inheritance detected: ${name}`, configPath);
|
|
1932
|
+
}
|
|
1933
|
+
const section = config[name];
|
|
1934
|
+
if (section === void 0 || typeof section !== "object") {
|
|
1935
|
+
throw new ConfigError(`Cannot inherit from unknown section: ${name}`, configPath);
|
|
1936
|
+
}
|
|
1937
|
+
resolving.add(name);
|
|
1938
|
+
const sectionObj = section;
|
|
1939
|
+
const inheritsRaw = sectionObj.inherits;
|
|
1940
|
+
const inheritsList = inheritsRaw ? Array.isArray(inheritsRaw) ? inheritsRaw : [inheritsRaw] : [];
|
|
1941
|
+
let merged = {};
|
|
1942
|
+
for (const parent of inheritsList) {
|
|
1943
|
+
const parentResolved = resolveSection(parent);
|
|
1944
|
+
merged = { ...merged, ...parentResolved };
|
|
1945
|
+
}
|
|
1946
|
+
const { inherits: _inherits, ...ownValues } = sectionObj;
|
|
1947
|
+
merged = { ...merged, ...ownValues };
|
|
1948
|
+
resolving.delete(name);
|
|
1949
|
+
resolved[name] = merged;
|
|
1950
|
+
return merged;
|
|
1951
|
+
}
|
|
1952
|
+
for (const name of Object.keys(config)) {
|
|
1953
|
+
resolveSection(name);
|
|
1954
|
+
}
|
|
1955
|
+
return resolved;
|
|
1956
|
+
}
|
|
1631
1957
|
|
|
1632
1958
|
// src/cli/models-command.ts
|
|
1633
1959
|
import chalk4 from "chalk";
|