@makefinks/daemon 0.8.0 → 0.9.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.
@@ -8,6 +8,10 @@ function normalizeWhitespace(text: string): string {
8
8
  return text.replace(/\r\n/g, "\n").replace(/\t/g, " ");
9
9
  }
10
10
 
11
+ function escapeXmlAttribute(value: string): string {
12
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
13
+ }
14
+
11
15
  function truncateText(text: string, maxChars: number): { text: string; truncated: boolean } {
12
16
  if (text.length <= maxChars) return { text, truncated: false };
13
17
  if (maxChars <= 1) return { text: "…", truncated: true };
@@ -105,43 +109,123 @@ function formatExaSearchResult(result: unknown): string | null {
105
109
  }
106
110
 
107
111
  function formatExaFetchResult(result: unknown): string | null {
112
+ if (typeof result === "string") {
113
+ const trimmed = result.trim();
114
+ if (!trimmed) return null;
115
+ if (trimmed.startsWith("<fetchUrls") && trimmed.includes("error=") && !trimmed.includes("</fetchUrls>")) {
116
+ return trimmed;
117
+ }
118
+
119
+ const rawLines = trimmed
120
+ .split("\n")
121
+ .map((line) => line.trimEnd())
122
+ .filter((line) => line.trim().length > 0);
123
+ const hasFetchHeader = rawLines.some((line) => line.includes("<fetchUrls"));
124
+ if (!hasFetchHeader) return trimmed;
125
+
126
+ const headerLine = rawLines.find((line) => line.includes("<fetchUrls"));
127
+ const closingLine = rawLines.find((line) => line.includes("</fetchUrls>"));
128
+ const urlLines = rawLines.filter((line) => line.includes("<url"));
129
+ const errorLines = urlLines.filter((line) => line.includes("error="));
130
+ const normalLines = urlLines.filter((line) => !line.includes("error="));
131
+
132
+ let contentLine: string | undefined;
133
+ if (normalLines.length === 1) {
134
+ const firstUrlIdx = rawLines.findIndex((line) => line.includes("<url") && line.includes("href="));
135
+ if (firstUrlIdx >= 0) {
136
+ contentLine = rawLines
137
+ .slice(firstUrlIdx + 1)
138
+ .find(
139
+ (line) => !line.includes("</url>") && !line.includes("<url") && !line.includes("</fetchUrls>")
140
+ )
141
+ ?.trim();
142
+ }
143
+ }
144
+
145
+ const lines: string[] = [];
146
+ if (headerLine) lines.push(headerLine);
147
+ if (normalLines[0]) lines.push(normalLines[0]);
148
+
149
+ if (errorLines.length > 0) {
150
+ lines.push(errorLines[0]);
151
+ } else if (contentLine) {
152
+ lines.push(contentLine);
153
+ }
154
+
155
+ if (contentLine && errorLines.length > 0 && lines.length < 4) {
156
+ lines.push(contentLine);
157
+ }
158
+
159
+ if (closingLine && lines.length < 4) {
160
+ lines.push(closingLine);
161
+ }
162
+
163
+ return lines.length > 0 ? lines.join("\n") : trimmed;
164
+ }
108
165
  if (!isRecord(result)) return null;
109
166
  if (result.success === false && typeof result.error === "string") {
110
167
  return `error: ${result.error}`;
111
168
  }
112
169
  if (result.success !== true) return null;
113
170
  const data = extractToolDataContainer(result);
114
- const candidate = isRecord(data) ? (data as ExaLikeItem & { remainingLines?: unknown }) : {};
115
- const label = formatExaItemLabel(candidate);
116
- const url = typeof candidate.url === "string" ? candidate.url : "";
117
- const title = typeof candidate.title === "string" ? candidate.title : "";
118
- const lineOffset = typeof candidate.lineOffset === "number" ? candidate.lineOffset : undefined;
119
- const lineLimit = typeof candidate.lineLimit === "number" ? candidate.lineLimit : undefined;
120
- const remainingLines =
121
- typeof (candidate as { remainingLines?: unknown }).remainingLines === "number" ||
122
- (candidate as { remainingLines?: unknown }).remainingLines === null
123
- ? (candidate as { remainingLines: number | null }).remainingLines
124
- : undefined;
125
- const rangeParts: string[] = [];
126
- if (lineOffset !== undefined) rangeParts.push(`lineOffset=${lineOffset}`);
127
- if (lineLimit !== undefined) rangeParts.push(`lineLimit=${lineLimit}`);
128
- if (remainingLines !== undefined) {
129
- rangeParts.push(remainingLines === null ? "remainingLines=unknown" : `remainingLines=${remainingLines}`);
130
- }
131
- const remainingSuffix = rangeParts.length > 0 ? ` (${rangeParts.join(", ")})` : "";
171
+ const container = isRecord(data) ? data : {};
172
+ const results = Array.isArray(container.results) ? container.results : null;
173
+ if (!results) return null;
174
+
175
+ const lines: string[] = ["<fetchUrls>"];
176
+ const maxItems = 3;
177
+ const MAX_LINES = 2;
178
+ const MAX_CHARS = 160;
179
+
180
+ for (const item of results.slice(0, maxItems)) {
181
+ if (!isRecord(item)) continue;
182
+
183
+ const candidate = item as ExaLikeItem & {
184
+ remainingLines?: unknown;
185
+ totalLines?: unknown;
186
+ error?: unknown;
187
+ };
188
+ const url = typeof candidate.url === "string" ? candidate.url : "";
189
+ if (!url) continue;
190
+
191
+ const attributes: string[] = [`href="${escapeXmlAttribute(url)}"`];
192
+ if (typeof candidate.lineOffset === "number") attributes.push(`lineOffset="${candidate.lineOffset}"`);
193
+ if (typeof candidate.lineLimit === "number") attributes.push(`lineLimit="${candidate.lineLimit}"`);
194
+ if (typeof candidate.totalLines === "number") attributes.push(`totalLines="${candidate.totalLines}"`);
195
+ if (typeof candidate.remainingLines === "number") {
196
+ attributes.push(`remainingLines="${candidate.remainingLines}"`);
197
+ } else if (candidate.remainingLines === null) {
198
+ attributes.push(`remainingLines="unknown"`);
199
+ }
132
200
 
133
- const headerBase = url && title ? `${label} ${url}` : label;
134
- const header = `${headerBase}${remainingSuffix}`;
201
+ if (candidate.success === false && typeof candidate.error === "string") {
202
+ attributes.push(`error="${escapeXmlAttribute(candidate.error)}"`);
203
+ lines.push(` <url ${attributes.join(" ")} />`);
204
+ continue;
205
+ }
135
206
 
136
- const text = typeof candidate.text === "string" ? candidate.text : "";
137
- if (!text.trim()) return header;
207
+ const text = typeof candidate.text === "string" ? candidate.text : "";
208
+ if (!text.trim()) {
209
+ lines.push(` <url ${attributes.join(" ")} />`);
210
+ continue;
211
+ }
138
212
 
139
- // Provide a small snippet; downstream truncation enforces the hard caps.
140
- const snippet = normalizeWhitespace(text)
141
- .replace(/\n{3,}/g, "\n\n")
142
- .trim();
213
+ const snippetLines = normalizeWhitespace(text)
214
+ .replace(/\n{3,}/g, "\n\n")
215
+ .trim()
216
+ .split("\n")
217
+ .slice(0, MAX_LINES)
218
+ .map((l) => (l.length > MAX_CHARS ? `${l.slice(0, MAX_CHARS - 1)}…` : l));
143
219
 
144
- return `${header}\n${snippet}`;
220
+ lines.push(` <url ${attributes.join(" ")}>`);
221
+ for (const line of snippetLines) {
222
+ lines.push(` ${escapeXmlAttribute(line)}`);
223
+ }
224
+ lines.push(" </url>");
225
+ }
226
+
227
+ lines.push("</fetchUrls>");
228
+ return lines.length > 2 ? lines.join("\n") : null;
145
229
  }
146
230
 
147
231
  function formatRenderUrlResult(result: unknown): string | null {