llmist 17.2.1 → 17.3.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/index.cjs +157 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -10
- package/dist/index.d.ts +4 -10
- package/dist/index.js +157 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4660,6 +4660,9 @@ var init_create_gadget = __esm({
|
|
|
4660
4660
|
});
|
|
4661
4661
|
|
|
4662
4662
|
// src/gadgets/output-viewer.ts
|
|
4663
|
+
function pluralize(count, singular, plural = `${singular}s`) {
|
|
4664
|
+
return count === 1 ? singular : plural;
|
|
4665
|
+
}
|
|
4663
4666
|
function applyPattern(lines, pattern) {
|
|
4664
4667
|
const regex = new RegExp(pattern.regex);
|
|
4665
4668
|
if (!pattern.include) {
|
|
@@ -4684,80 +4687,169 @@ function applyPatterns(lines, patterns) {
|
|
|
4684
4687
|
}
|
|
4685
4688
|
return result;
|
|
4686
4689
|
}
|
|
4687
|
-
function
|
|
4690
|
+
function parseLimitWindow(limit) {
|
|
4688
4691
|
const trimmed = limit.trim();
|
|
4689
4692
|
if (trimmed.endsWith("-") && !trimmed.startsWith("-")) {
|
|
4690
4693
|
const n = parseInt(trimmed.slice(0, -1), 10);
|
|
4691
|
-
if (!isNaN(n) && n > 0) {
|
|
4692
|
-
return
|
|
4694
|
+
if (!Number.isNaN(n) && n > 0) {
|
|
4695
|
+
return { kind: "first", count: n };
|
|
4693
4696
|
}
|
|
4694
4697
|
}
|
|
4695
4698
|
if (trimmed.startsWith("-") && !trimmed.includes("-", 1)) {
|
|
4696
4699
|
const n = parseInt(trimmed, 10);
|
|
4697
|
-
if (!isNaN(n) && n < 0) {
|
|
4698
|
-
return
|
|
4700
|
+
if (!Number.isNaN(n) && n < 0) {
|
|
4701
|
+
return { kind: "last", count: Math.abs(n) };
|
|
4699
4702
|
}
|
|
4700
4703
|
}
|
|
4701
4704
|
const rangeMatch = trimmed.match(/^(\d+)-(\d+)$/);
|
|
4702
4705
|
if (rangeMatch) {
|
|
4703
4706
|
const start = parseInt(rangeMatch[1], 10);
|
|
4704
4707
|
const end = parseInt(rangeMatch[2], 10);
|
|
4705
|
-
if (!isNaN(start) && !isNaN(end) && start > 0 && end >= start) {
|
|
4706
|
-
return
|
|
4708
|
+
if (!Number.isNaN(start) && !Number.isNaN(end) && start > 0 && end >= start) {
|
|
4709
|
+
return { kind: "range", start, end };
|
|
4707
4710
|
}
|
|
4708
4711
|
}
|
|
4709
|
-
return
|
|
4712
|
+
return null;
|
|
4713
|
+
}
|
|
4714
|
+
function applyLineLimit(lines, limit) {
|
|
4715
|
+
const window = parseLimitWindow(limit);
|
|
4716
|
+
if (!window) return lines;
|
|
4717
|
+
switch (window.kind) {
|
|
4718
|
+
case "first":
|
|
4719
|
+
return lines.slice(0, window.count);
|
|
4720
|
+
case "last":
|
|
4721
|
+
return lines.slice(-window.count);
|
|
4722
|
+
case "range":
|
|
4723
|
+
return lines.slice(window.start - 1, window.end);
|
|
4724
|
+
}
|
|
4725
|
+
}
|
|
4726
|
+
function applyCharacterLimit(content, limit, maxOutputChars) {
|
|
4727
|
+
const total = content.length;
|
|
4728
|
+
if (total === 0) {
|
|
4729
|
+
return { text: "", start: 0, end: 0, total: 0, truncatedBySize: false, hasMoreAfter: false };
|
|
4730
|
+
}
|
|
4731
|
+
let startIndex = 0;
|
|
4732
|
+
let endExclusive = total;
|
|
4733
|
+
const window = limit ? parseLimitWindow(limit) : null;
|
|
4734
|
+
if (window) {
|
|
4735
|
+
switch (window.kind) {
|
|
4736
|
+
case "first":
|
|
4737
|
+
endExclusive = Math.min(window.count, total);
|
|
4738
|
+
break;
|
|
4739
|
+
case "last":
|
|
4740
|
+
startIndex = Math.max(0, total - window.count);
|
|
4741
|
+
break;
|
|
4742
|
+
case "range":
|
|
4743
|
+
startIndex = Math.min(window.start - 1, total);
|
|
4744
|
+
endExclusive = Math.min(window.end, total);
|
|
4745
|
+
break;
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4748
|
+
let text3 = content.slice(startIndex, endExclusive);
|
|
4749
|
+
let truncatedBySize = false;
|
|
4750
|
+
if (text3.length > maxOutputChars) {
|
|
4751
|
+
text3 = window?.kind === "last" ? text3.slice(-maxOutputChars) : text3.slice(0, maxOutputChars);
|
|
4752
|
+
if (window?.kind === "last") {
|
|
4753
|
+
startIndex = endExclusive - text3.length;
|
|
4754
|
+
}
|
|
4755
|
+
truncatedBySize = true;
|
|
4756
|
+
}
|
|
4757
|
+
return {
|
|
4758
|
+
text: text3,
|
|
4759
|
+
start: text3.length === 0 ? 0 : startIndex + 1,
|
|
4760
|
+
end: text3.length === 0 ? 0 : startIndex + text3.length,
|
|
4761
|
+
total,
|
|
4762
|
+
truncatedBySize,
|
|
4763
|
+
hasMoreAfter: startIndex + text3.length < total
|
|
4764
|
+
};
|
|
4765
|
+
}
|
|
4766
|
+
function buildCharacterRangeHint(start, total) {
|
|
4767
|
+
if (total <= 0 || start > total) return null;
|
|
4768
|
+
const end = Math.min(total, start + CHARACTER_HINT_WINDOW - 1);
|
|
4769
|
+
return `${start}-${end}`;
|
|
4770
|
+
}
|
|
4771
|
+
function buildCharacterModeSuggestion(stored, opts = {}) {
|
|
4772
|
+
const hint = buildCharacterRangeHint(opts.start ?? 1, stored.charCount);
|
|
4773
|
+
const action = opts.removePatterns ? "Remove patterns and then try" : "Try";
|
|
4774
|
+
const lineLabel = pluralize(stored.lineCount, "line");
|
|
4775
|
+
return `This output is dense (${stored.lineCount.toLocaleString()} ${lineLabel}; longest line ${stored.maxLineLength.toLocaleString()} chars). ${action} mode: "character"` + (hint ? `, limit: "${hint}"` : "") + ".";
|
|
4776
|
+
}
|
|
4777
|
+
function shouldSuggestCharacterMode(stored, maxOutputChars = DEFAULT_MAX_OUTPUT_CHARS) {
|
|
4778
|
+
return stored.lineCount <= 3 && (stored.maxLineLength > maxOutputChars || stored.maxLineLength >= DENSE_LINE_THRESHOLD);
|
|
4710
4779
|
}
|
|
4711
4780
|
function createGadgetOutputViewer(store, maxOutputChars = DEFAULT_MAX_OUTPUT_CHARS) {
|
|
4712
4781
|
return createGadget({
|
|
4713
4782
|
name: "GadgetOutputViewer",
|
|
4714
|
-
description:
|
|
4783
|
+
description: 'View stored output from gadgets that returned too much data. Use mode "line" for grep-like filtering and mode "character" for raw chunked browsing when the output is dense or effectively single-line. Patterns work only in line mode.',
|
|
4715
4784
|
schema: import_zod.z.object({
|
|
4716
4785
|
id: import_zod.z.string().describe("ID of the stored output (from the truncation message)"),
|
|
4786
|
+
mode: import_zod.z.enum(["line", "character"]).default("line").describe(
|
|
4787
|
+
'Browse by "line" (supports patterns) or by "character" (raw windows for dense output).'
|
|
4788
|
+
),
|
|
4717
4789
|
patterns: import_zod.z.array(patternSchema).optional().describe(
|
|
4718
|
-
|
|
4790
|
+
'Line-mode filter patterns applied in order (like piping through grep). Not supported in mode "character".'
|
|
4719
4791
|
),
|
|
4720
4792
|
limit: import_zod.z.string().optional().describe(
|
|
4721
|
-
"
|
|
4793
|
+
`Pagination window. In mode "line" it is a line range; in mode "character" it is a character range. Formats: "100-" (first 100), "-25" (last 25), "50-100" (inclusive range).`
|
|
4722
4794
|
)
|
|
4723
4795
|
}),
|
|
4724
4796
|
examples: [
|
|
4725
4797
|
{
|
|
4726
4798
|
comment: "View first 50 lines of stored output",
|
|
4727
|
-
params: { id: "Search_abc12345", limit: "50-" }
|
|
4799
|
+
params: { id: "Search_abc12345", mode: "line", limit: "50-" }
|
|
4728
4800
|
},
|
|
4729
4801
|
{
|
|
4730
4802
|
comment: "Filter for error lines with context",
|
|
4731
4803
|
params: {
|
|
4732
4804
|
id: "Search_abc12345",
|
|
4805
|
+
mode: "line",
|
|
4733
4806
|
patterns: [{ regex: "error|Error|ERROR", include: true, before: 2, after: 5 }]
|
|
4734
4807
|
}
|
|
4735
4808
|
},
|
|
4736
4809
|
{
|
|
4737
|
-
comment: "Exclude blank lines, then show first 100",
|
|
4810
|
+
comment: "Exclude blank lines, then show first 100 lines",
|
|
4738
4811
|
params: {
|
|
4739
4812
|
id: "Search_abc12345",
|
|
4813
|
+
mode: "line",
|
|
4740
4814
|
patterns: [{ regex: "^\\s*$", include: false, before: 0, after: 0 }],
|
|
4741
4815
|
limit: "100-"
|
|
4742
4816
|
}
|
|
4743
4817
|
},
|
|
4744
4818
|
{
|
|
4745
|
-
comment: "
|
|
4819
|
+
comment: "Browse the raw output by character window when line mode is too dense",
|
|
4746
4820
|
params: {
|
|
4747
4821
|
id: "Search_abc12345",
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
{ regex: "test|spec", include: false, before: 0, after: 0 }
|
|
4751
|
-
],
|
|
4752
|
-
limit: "50-"
|
|
4822
|
+
mode: "character",
|
|
4823
|
+
limit: "1-2000"
|
|
4753
4824
|
}
|
|
4754
4825
|
}
|
|
4755
4826
|
],
|
|
4756
|
-
execute: ({ id, patterns, limit }) => {
|
|
4827
|
+
execute: ({ id, mode, patterns, limit }) => {
|
|
4757
4828
|
const stored = store.get(id);
|
|
4758
4829
|
if (!stored) {
|
|
4759
4830
|
return `Error: No stored output with id "${id}". Available IDs: ${store.getIds().join(", ") || "(none)"}`;
|
|
4760
4831
|
}
|
|
4832
|
+
const suggestCharacterMode = shouldSuggestCharacterMode(stored, maxOutputChars);
|
|
4833
|
+
if (mode === "character") {
|
|
4834
|
+
if (patterns && patterns.length > 0) {
|
|
4835
|
+
return 'Error: patterns are only supported in mode "line". Remove patterns or switch back to mode: "line".';
|
|
4836
|
+
}
|
|
4837
|
+
const window = applyCharacterLimit(stored.content, limit, maxOutputChars);
|
|
4838
|
+
if (window.total === 0) {
|
|
4839
|
+
return "[Mode: character | Output is empty]";
|
|
4840
|
+
}
|
|
4841
|
+
const header2 = [
|
|
4842
|
+
`[Mode: character | Showing chars ${window.start.toLocaleString()}-${window.end.toLocaleString()} of ${window.total.toLocaleString()}${window.truncatedBySize ? " (truncated due to viewer size limit)" : ""}]`
|
|
4843
|
+
];
|
|
4844
|
+
if (window.hasMoreAfter) {
|
|
4845
|
+
const nextRange = buildCharacterRangeHint(window.end + 1, window.total);
|
|
4846
|
+
if (nextRange) {
|
|
4847
|
+
header2.push(`[Next chunk: mode: "character", limit: "${nextRange}"]`);
|
|
4848
|
+
}
|
|
4849
|
+
}
|
|
4850
|
+
return `${header2.join("\n")}
|
|
4851
|
+
${window.text}`;
|
|
4852
|
+
}
|
|
4761
4853
|
let lines = stored.content.split("\n");
|
|
4762
4854
|
if (patterns && patterns.length > 0) {
|
|
4763
4855
|
lines = applyPatterns(
|
|
@@ -4773,55 +4865,77 @@ function createGadgetOutputViewer(store, maxOutputChars = DEFAULT_MAX_OUTPUT_CHA
|
|
|
4773
4865
|
if (limit) {
|
|
4774
4866
|
lines = applyLineLimit(lines, limit);
|
|
4775
4867
|
}
|
|
4776
|
-
let output = lines.join("\n");
|
|
4777
4868
|
const totalLines = stored.lineCount;
|
|
4869
|
+
const totalLineLabel = pluralize(totalLines, "line");
|
|
4778
4870
|
const returnedLines = lines.length;
|
|
4779
4871
|
if (returnedLines === 0) {
|
|
4780
|
-
|
|
4872
|
+
const base = `No lines matched the filters. Original output had ${totalLines.toLocaleString()} lines.`;
|
|
4873
|
+
if (!suggestCharacterMode) return base;
|
|
4874
|
+
return `${base} ${buildCharacterModeSuggestion(stored, {
|
|
4875
|
+
removePatterns: Boolean(patterns && patterns.length > 0)
|
|
4876
|
+
})}`;
|
|
4781
4877
|
}
|
|
4878
|
+
let output = lines.join("\n");
|
|
4782
4879
|
let truncatedBySize = false;
|
|
4783
4880
|
let linesIncluded = returnedLines;
|
|
4881
|
+
let clippedFirstLine = false;
|
|
4784
4882
|
if (output.length > maxOutputChars) {
|
|
4785
4883
|
truncatedBySize = true;
|
|
4786
4884
|
let truncatedOutput = "";
|
|
4787
4885
|
linesIncluded = 0;
|
|
4788
4886
|
for (const line of lines) {
|
|
4789
|
-
|
|
4790
|
-
|
|
4887
|
+
const addition = linesIncluded === 0 ? line : `
|
|
4888
|
+
${line}`;
|
|
4889
|
+
if (truncatedOutput.length + addition.length > maxOutputChars) break;
|
|
4890
|
+
truncatedOutput += addition;
|
|
4791
4891
|
linesIncluded++;
|
|
4792
4892
|
}
|
|
4893
|
+
if (linesIncluded === 0) {
|
|
4894
|
+
clippedFirstLine = true;
|
|
4895
|
+
linesIncluded = 1;
|
|
4896
|
+
truncatedOutput = lines[0].slice(0, maxOutputChars);
|
|
4897
|
+
}
|
|
4793
4898
|
output = truncatedOutput;
|
|
4794
4899
|
}
|
|
4795
4900
|
let header;
|
|
4796
|
-
if (
|
|
4901
|
+
if (clippedFirstLine) {
|
|
4902
|
+
header = `[Mode: line | Showing 1 partial line of ${totalLines.toLocaleString()} ${totalLineLabel} (the selected line exceeds the viewer size limit)]
|
|
4903
|
+
`;
|
|
4904
|
+
} else if (truncatedBySize) {
|
|
4797
4905
|
const remainingLines = returnedLines - linesIncluded;
|
|
4798
|
-
header = `[Showing ${linesIncluded} of ${totalLines}
|
|
4799
|
-
[... ${remainingLines.toLocaleString()} more
|
|
4906
|
+
header = `[Mode: line | Showing ${linesIncluded.toLocaleString()} of ${totalLines.toLocaleString()} ${totalLineLabel} (truncated due to size limit)]
|
|
4907
|
+
[... ${remainingLines.toLocaleString()} more ${pluralize(remainingLines, "line")}. Use limit parameter to paginate, e.g., limit: "${linesIncluded + 1}-${linesIncluded + 200}"]
|
|
4800
4908
|
`;
|
|
4801
4909
|
} else if (returnedLines < totalLines) {
|
|
4802
|
-
header = `[Showing ${returnedLines} of ${totalLines}
|
|
4910
|
+
header = `[Mode: line | Showing ${returnedLines.toLocaleString()} of ${totalLines.toLocaleString()} ${totalLineLabel}]
|
|
4803
4911
|
`;
|
|
4804
4912
|
} else {
|
|
4805
|
-
header = `[Showing all ${totalLines}
|
|
4913
|
+
header = `[Mode: line | Showing all ${totalLines.toLocaleString()} ${totalLineLabel}]
|
|
4806
4914
|
`;
|
|
4807
4915
|
}
|
|
4808
|
-
|
|
4916
|
+
const footer = suggestCharacterMode || clippedFirstLine ? `
|
|
4917
|
+
[Tip: ${buildCharacterModeSuggestion(stored, {
|
|
4918
|
+
removePatterns: Boolean(patterns && patterns.length > 0)
|
|
4919
|
+
})}]` : "";
|
|
4920
|
+
return header + output + footer;
|
|
4809
4921
|
}
|
|
4810
4922
|
});
|
|
4811
4923
|
}
|
|
4812
|
-
var import_zod,
|
|
4924
|
+
var import_zod, DEFAULT_MAX_OUTPUT_CHARS, CHARACTER_HINT_WINDOW, DENSE_LINE_THRESHOLD, patternSchema;
|
|
4813
4925
|
var init_output_viewer = __esm({
|
|
4814
4926
|
"src/gadgets/output-viewer.ts"() {
|
|
4815
4927
|
"use strict";
|
|
4816
4928
|
import_zod = require("zod");
|
|
4817
4929
|
init_create_gadget();
|
|
4930
|
+
DEFAULT_MAX_OUTPUT_CHARS = 76800;
|
|
4931
|
+
CHARACTER_HINT_WINDOW = 2e3;
|
|
4932
|
+
DENSE_LINE_THRESHOLD = 4e3;
|
|
4818
4933
|
patternSchema = import_zod.z.object({
|
|
4819
4934
|
regex: import_zod.z.string().describe("Regular expression to match"),
|
|
4820
4935
|
include: import_zod.z.boolean().default(true).describe("true = keep matching lines, false = exclude matching lines"),
|
|
4821
4936
|
before: import_zod.z.number().int().min(0).default(0).describe("Context lines before each match (like grep -B)"),
|
|
4822
4937
|
after: import_zod.z.number().int().min(0).default(0).describe("Context lines after each match (like grep -A)")
|
|
4823
4938
|
});
|
|
4824
|
-
DEFAULT_MAX_OUTPUT_CHARS = 76800;
|
|
4825
4939
|
}
|
|
4826
4940
|
});
|
|
4827
4941
|
|
|
@@ -4843,12 +4957,15 @@ var init_gadget_output_store = __esm({
|
|
|
4843
4957
|
store(gadgetName, content) {
|
|
4844
4958
|
const id = this.generateId(gadgetName);
|
|
4845
4959
|
const encoder = new TextEncoder();
|
|
4960
|
+
const lines = content.split("\n");
|
|
4846
4961
|
const stored = {
|
|
4847
4962
|
id,
|
|
4848
4963
|
gadgetName,
|
|
4849
4964
|
content,
|
|
4965
|
+
charCount: content.length,
|
|
4850
4966
|
byteSize: encoder.encode(content).length,
|
|
4851
|
-
lineCount:
|
|
4967
|
+
lineCount: lines.length,
|
|
4968
|
+
maxLineLength: lines.reduce((max, line) => Math.max(max, line.length), 0),
|
|
4852
4969
|
timestamp: /* @__PURE__ */ new Date()
|
|
4853
4970
|
};
|
|
4854
4971
|
this.outputs.set(id, stored);
|
|
@@ -4952,16 +5069,20 @@ var init_output_limit_manager = __esm({
|
|
|
4952
5069
|
}
|
|
4953
5070
|
if (result.length > this.charLimit) {
|
|
4954
5071
|
const id = this.outputStore.store(ctx.gadgetName, result);
|
|
4955
|
-
const
|
|
4956
|
-
const
|
|
5072
|
+
const stored = this.outputStore.get(id);
|
|
5073
|
+
const lines = stored?.lineCount ?? result.split("\n").length;
|
|
5074
|
+
const bytes = stored?.byteSize ?? new TextEncoder().encode(result).length;
|
|
5075
|
+
const denseSuggestion = stored && shouldSuggestCharacterMode(stored, this.charLimit) ? ` ${buildCharacterModeSuggestion(stored)}` : "";
|
|
4957
5076
|
this.logger.info("Gadget output exceeded limit, stored for browsing", {
|
|
4958
5077
|
gadgetName: ctx.gadgetName,
|
|
4959
5078
|
outputId: id,
|
|
4960
5079
|
bytes,
|
|
4961
5080
|
lines,
|
|
5081
|
+
charCount: stored?.charCount,
|
|
5082
|
+
maxLineLength: stored?.maxLineLength,
|
|
4962
5083
|
charLimit: this.charLimit
|
|
4963
5084
|
});
|
|
4964
|
-
return `[Gadget "${ctx.gadgetName}" returned too much data: ${bytes.toLocaleString()} bytes, ${lines.toLocaleString()} lines. Use GadgetOutputViewer with id "${id}" to read it]
|
|
5085
|
+
return `[Gadget "${ctx.gadgetName}" returned too much data: ${bytes.toLocaleString()} bytes, ${lines.toLocaleString()} lines. Use GadgetOutputViewer with id "${id}" to read it.]` + denseSuggestion;
|
|
4965
5086
|
}
|
|
4966
5087
|
return result;
|
|
4967
5088
|
};
|