@cortexkit/aft-bridge 0.18.5 → 0.19.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.
- package/dist/active-logger.js +44 -0
- package/dist/active-logger.js.map +1 -0
- package/dist/bridge.d.ts +17 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +671 -0
- package/dist/bridge.js.map +1 -0
- package/dist/downloader.js +193 -0
- package/dist/downloader.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -1387
- package/dist/index.js.map +1 -0
- package/dist/logger.js +2 -0
- package/dist/logger.js.map +1 -0
- package/dist/onnx-runtime.d.ts +20 -0
- package/dist/onnx-runtime.d.ts.map +1 -1
- package/dist/onnx-runtime.js +722 -0
- package/dist/onnx-runtime.js.map +1 -0
- package/dist/platform.js +31 -0
- package/dist/platform.js.map +1 -0
- package/dist/pool.d.ts +21 -0
- package/dist/pool.d.ts.map +1 -1
- package/dist/pool.js +189 -0
- package/dist/pool.js.map +1 -0
- package/dist/protocol.js +10 -0
- package/dist/protocol.js.map +1 -0
- package/dist/resolver.js +186 -0
- package/dist/resolver.js.map +1 -0
- package/dist/url-fetch.d.ts +33 -0
- package/dist/url-fetch.d.ts.map +1 -0
- package/dist/url-fetch.js +444 -0
- package/dist/url-fetch.js.map +1 -0
- package/dist/zoom-format.d.ts +70 -0
- package/dist/zoom-format.d.ts.map +1 -0
- package/dist/zoom-format.js +98 -0
- package/dist/zoom-format.js.map +1 -0
- package/package.json +6 -3
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { lookup } from "node:dns/promises";
|
|
3
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
4
|
+
import { isIP } from "node:net";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { Agent, fetch as undiciFetch } from "undici";
|
|
7
|
+
import { log, warn } from "./active-logger.js";
|
|
8
|
+
/** Max response body size (10 MB) */
|
|
9
|
+
const MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
10
|
+
/** Cache TTL: 1 day */
|
|
11
|
+
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
12
|
+
/** Fetch timeout: 30 seconds */
|
|
13
|
+
const FETCH_TIMEOUT_MS = 30_000;
|
|
14
|
+
const MAX_REDIRECTS = 5;
|
|
15
|
+
function cacheDir(storageDir) {
|
|
16
|
+
return join(storageDir, "url_cache");
|
|
17
|
+
}
|
|
18
|
+
function hashUrl(url) {
|
|
19
|
+
return createHash("sha256").update(url).digest("hex").slice(0, 16);
|
|
20
|
+
}
|
|
21
|
+
function metaPath(storageDir, hash) {
|
|
22
|
+
return join(cacheDir(storageDir), `${hash}.meta.json`);
|
|
23
|
+
}
|
|
24
|
+
function contentPath(storageDir, hash, extension) {
|
|
25
|
+
return join(cacheDir(storageDir), `${hash}${extension}`);
|
|
26
|
+
}
|
|
27
|
+
/** Exported for unit tests. */
|
|
28
|
+
export function _isPrivateIpv4(address) {
|
|
29
|
+
const parts = address.split(".").map((part) => Number.parseInt(part, 10));
|
|
30
|
+
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part)))
|
|
31
|
+
return true;
|
|
32
|
+
const [a, b] = parts;
|
|
33
|
+
return (a === 0 || // 0.0.0.0/8 — wildcard, blocked to prevent local-host bypass
|
|
34
|
+
a === 10 ||
|
|
35
|
+
a === 127 ||
|
|
36
|
+
(a === 172 && b >= 16 && b <= 31) ||
|
|
37
|
+
(a === 192 && b === 168) ||
|
|
38
|
+
(a === 169 && b === 254) ||
|
|
39
|
+
a >= 224 // multicast (224/4) and reserved (240/4)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Expand a possibly-compressed IPv6 address to 8 hextets (numbers).
|
|
44
|
+
* Returns null on parse failure.
|
|
45
|
+
*/
|
|
46
|
+
function expandIpv6(addr) {
|
|
47
|
+
if (addr === "::")
|
|
48
|
+
return [0, 0, 0, 0, 0, 0, 0, 0];
|
|
49
|
+
// Reject more than one "::"
|
|
50
|
+
const dcMatches = addr.match(/::/g);
|
|
51
|
+
if (dcMatches && dcMatches.length > 1)
|
|
52
|
+
return null;
|
|
53
|
+
// Handle dotted-quad tail by converting to two hextets.
|
|
54
|
+
let normalized = addr;
|
|
55
|
+
const lastColon = normalized.lastIndexOf(":");
|
|
56
|
+
if (lastColon !== -1) {
|
|
57
|
+
const tail = normalized.slice(lastColon + 1);
|
|
58
|
+
if (tail.includes(".")) {
|
|
59
|
+
const octets = tail.split(".").map((p) => Number.parseInt(p, 10));
|
|
60
|
+
if (octets.length !== 4 || octets.some((o) => Number.isNaN(o) || o < 0 || o > 255)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const h1 = ((octets[0] << 8) | octets[1]).toString(16);
|
|
64
|
+
const h2 = ((octets[2] << 8) | octets[3]).toString(16);
|
|
65
|
+
normalized = `${normalized.slice(0, lastColon)}:${h1}:${h2}`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
let parts;
|
|
69
|
+
if (normalized.includes("::")) {
|
|
70
|
+
const [left, right] = normalized.split("::");
|
|
71
|
+
const leftParts = left ? left.split(":") : [];
|
|
72
|
+
const rightParts = right ? right.split(":") : [];
|
|
73
|
+
const fill = 8 - leftParts.length - rightParts.length;
|
|
74
|
+
if (fill < 0)
|
|
75
|
+
return null;
|
|
76
|
+
parts = [...leftParts, ...Array(fill).fill("0"), ...rightParts];
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
parts = normalized.split(":");
|
|
80
|
+
}
|
|
81
|
+
if (parts.length !== 8)
|
|
82
|
+
return null;
|
|
83
|
+
const hextets = parts.map((p) => {
|
|
84
|
+
const n = Number.parseInt(p, 16);
|
|
85
|
+
return Number.isNaN(n) || n < 0 || n > 0xffff ? -1 : n;
|
|
86
|
+
});
|
|
87
|
+
if (hextets.some((h) => h === -1))
|
|
88
|
+
return null;
|
|
89
|
+
return hextets;
|
|
90
|
+
}
|
|
91
|
+
function isPrivateIp(address) {
|
|
92
|
+
if (!address.includes(":")) {
|
|
93
|
+
return _isPrivateIpv4(address);
|
|
94
|
+
}
|
|
95
|
+
const lower = address.toLowerCase();
|
|
96
|
+
// Strip optional zone identifier (fe80::1%eth0 → fe80::1).
|
|
97
|
+
const noZone = lower.split("%")[0];
|
|
98
|
+
// Expand to 8 hextets so we can reliably detect IPv4-mapped/compatible
|
|
99
|
+
// forms even after URL canonicalization (e.g. [::ffff:127.0.0.1] →
|
|
100
|
+
// [::ffff:7f00:1]).
|
|
101
|
+
const hextets = expandIpv6(noZone);
|
|
102
|
+
if (hextets) {
|
|
103
|
+
// IPv4-mapped (::ffff:X.X.X.X) and IPv4-compatible (::X.X.X.X) — extract
|
|
104
|
+
// the embedded IPv4 from the last two hextets and check against IPv4 ranges.
|
|
105
|
+
const top6Zero = hextets.slice(0, 6).every((h) => h === 0);
|
|
106
|
+
const isMapped = hextets[0] === 0 &&
|
|
107
|
+
hextets[1] === 0 &&
|
|
108
|
+
hextets[2] === 0 &&
|
|
109
|
+
hextets[3] === 0 &&
|
|
110
|
+
hextets[4] === 0 &&
|
|
111
|
+
hextets[5] === 0xffff;
|
|
112
|
+
if (isMapped || top6Zero) {
|
|
113
|
+
const a = (hextets[6] >> 8) & 0xff;
|
|
114
|
+
const b = hextets[6] & 0xff;
|
|
115
|
+
const c = (hextets[7] >> 8) & 0xff;
|
|
116
|
+
const d = hextets[7] & 0xff;
|
|
117
|
+
const ipv4 = `${a}.${b}.${c}.${d}`;
|
|
118
|
+
// ::1 (loopback) and :: (unspecified) both fall into top6Zero and last
|
|
119
|
+
// hextet 0 or 1 — _isPrivateIpv4 already blocks 0.0.0.0 and 127/8.
|
|
120
|
+
if (top6Zero && hextets[6] === 0 && hextets[7] <= 1) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
return _isPrivateIpv4(ipv4);
|
|
124
|
+
}
|
|
125
|
+
const firstHextet = hextets[0];
|
|
126
|
+
return (
|
|
127
|
+
// fe80::/10 link-local
|
|
128
|
+
(firstHextet >= 0xfe80 && firstHextet <= 0xfebf) ||
|
|
129
|
+
// fc00::/7 unique local
|
|
130
|
+
(firstHextet >= 0xfc00 && firstHextet <= 0xfdff) ||
|
|
131
|
+
// ff00::/8 multicast
|
|
132
|
+
firstHextet >= 0xff00);
|
|
133
|
+
}
|
|
134
|
+
// Could not expand — be conservative and block.
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
async function assertPublicUrl(url, allowPrivate, dnsLookup = lookup) {
|
|
138
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
139
|
+
throw new Error(`Only http:// and https:// URLs are supported, got: ${url.protocol}`);
|
|
140
|
+
}
|
|
141
|
+
if (allowPrivate)
|
|
142
|
+
return undefined;
|
|
143
|
+
// URL.hostname returns IPv6 literals wrapped in brackets ("[::1]") per WHATWG.
|
|
144
|
+
// Strip them before checking the address.
|
|
145
|
+
const hostname = url.hostname.replace(/^\[|\]$/g, "");
|
|
146
|
+
// If the hostname is already a literal IP, check it directly — skip DNS so we
|
|
147
|
+
// also catch malformed IPv6 forms (::127.0.0.1, ::ffff:127.0.0.1) that some
|
|
148
|
+
// resolvers reject as "not found" instead of returning the embedded IPv4.
|
|
149
|
+
if (isIP(hostname) || hostname.includes(":")) {
|
|
150
|
+
if (isPrivateIp(hostname)) {
|
|
151
|
+
throw new Error(`Blocked private URL host ${url.hostname} (${hostname})`);
|
|
152
|
+
}
|
|
153
|
+
return hostname;
|
|
154
|
+
}
|
|
155
|
+
const addresses = await dnsLookup(hostname, { all: true, verbatim: true });
|
|
156
|
+
for (const { address } of addresses) {
|
|
157
|
+
if (isPrivateIp(address)) {
|
|
158
|
+
throw new Error(`Blocked private URL host ${url.hostname} (${address})`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (addresses.length === 0) {
|
|
162
|
+
throw new Error(`Failed to resolve URL host ${url.hostname}`);
|
|
163
|
+
}
|
|
164
|
+
// Prefer IPv4 when available. AAAA can come first on many resolvers, but
|
|
165
|
+
// many user networks (corporate, home, mobile) don't have IPv6 transit, so
|
|
166
|
+
// pinning to AAAA causes EHOSTUNREACH / connect timeouts that surface as a
|
|
167
|
+
// bare `fetch failed`. Falls back to whatever DNS gave us if no IPv4 record.
|
|
168
|
+
// `family` is documented as numeric (4/6) but be defensive — some runtimes
|
|
169
|
+
// have surfaced it as the string "IPv4"/"IPv6" historically.
|
|
170
|
+
const ipv4 = addresses.find((entry) => entry.family === 4 ||
|
|
171
|
+
entry.family === "IPv4" ||
|
|
172
|
+
(typeof entry.address === "string" && isIP(entry.address) === 4));
|
|
173
|
+
const chosen = ipv4 ?? addresses[0];
|
|
174
|
+
if (!chosen || typeof chosen.address !== "string" || chosen.address.length === 0) {
|
|
175
|
+
// Should be unreachable given the length>0 check above, but the bare
|
|
176
|
+
// `Invalid IP address: undefined` failure surfaced by undici when an
|
|
177
|
+
// empty/undefined IP slips into connect.lookup is opaque enough that
|
|
178
|
+
// we want a clear, structured failure instead.
|
|
179
|
+
throw new Error(`DNS lookup for ${url.hostname} returned no usable address (got: ${JSON.stringify(addresses)})`);
|
|
180
|
+
}
|
|
181
|
+
return chosen.address;
|
|
182
|
+
}
|
|
183
|
+
function createPinnedDispatcher(validatedIp) {
|
|
184
|
+
const family = validatedIp.includes(":") ? 6 : 4;
|
|
185
|
+
// Node 18+ calls the lookup with `opts.all: true` (especially via TLS /
|
|
186
|
+
// happy-eyeballs paths). When `all` is true the callback expects an *array*
|
|
187
|
+
// of `{address, family}` objects, not the legacy `(err, address, family)`
|
|
188
|
+
// 3-arg shape. Calling the legacy form there causes Node to surface
|
|
189
|
+
// `TypeError [ERR_INVALID_IP_ADDRESS]: Invalid IP address: undefined` from
|
|
190
|
+
// net:emitLookup, wrapped by undici as a bare `fetch failed`. Honor
|
|
191
|
+
// whichever shape `opts.all` requested.
|
|
192
|
+
// (Cast to `any` because undici's `LookupFunction` type only models the
|
|
193
|
+
// single-address callback overload, not the `all: true` variadic.)
|
|
194
|
+
// biome-ignore lint/suspicious/noExplicitAny: dual-overload lookup callback
|
|
195
|
+
const pinnedLookup = (_hostname, opts,
|
|
196
|
+
// biome-ignore lint/suspicious/noExplicitAny: dual-overload lookup callback
|
|
197
|
+
callback) => {
|
|
198
|
+
if (opts?.all) {
|
|
199
|
+
callback(null, [{ address: validatedIp, family }]);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
callback(null, validatedIp, family);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
return new Agent({
|
|
206
|
+
connect: { lookup: pinnedLookup },
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
function resolveRedirectUrl(currentUrl, location) {
|
|
210
|
+
if (!location) {
|
|
211
|
+
throw new Error(`Redirect from ${currentUrl.href} missing Location header`);
|
|
212
|
+
}
|
|
213
|
+
return new URL(location, currentUrl);
|
|
214
|
+
}
|
|
215
|
+
async function fetchWithRedirects(startUrl, allowPrivate, deps = {}) {
|
|
216
|
+
let currentUrl = startUrl;
|
|
217
|
+
const fetchImpl = deps.fetchImpl ?? undiciFetch;
|
|
218
|
+
const dnsLookup = deps.lookup ?? lookup;
|
|
219
|
+
for (let redirectCount = 0; redirectCount <= MAX_REDIRECTS; redirectCount++) {
|
|
220
|
+
const validatedIp = await assertPublicUrl(currentUrl, allowPrivate, dnsLookup);
|
|
221
|
+
const dispatcher = validatedIp
|
|
222
|
+
? (deps.dispatcherFactory ?? createPinnedDispatcher)(validatedIp)
|
|
223
|
+
: undefined;
|
|
224
|
+
const controller = new AbortController();
|
|
225
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
226
|
+
let response;
|
|
227
|
+
try {
|
|
228
|
+
response = await fetchImpl(currentUrl.href, {
|
|
229
|
+
signal: controller.signal,
|
|
230
|
+
redirect: "manual",
|
|
231
|
+
dispatcher,
|
|
232
|
+
headers: {
|
|
233
|
+
"user-agent": "aft-opencode-plugin",
|
|
234
|
+
// Prioritize markdown for content-negotiating servers (GitHub API, many docs sites).
|
|
235
|
+
// `application/vnd.github.raw` is GitHub's custom type — returns raw markdown from
|
|
236
|
+
// repo file and readme endpoints. Falls back to HTML if markdown is not available.
|
|
237
|
+
accept: "application/vnd.github.raw, text/markdown, text/x-markdown, text/html;q=0.9, text/plain;q=0.5",
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
// undici wraps the underlying network error (ECONNREFUSED / ETIMEDOUT /
|
|
243
|
+
// EHOSTUNREACH / ENOTFOUND / cert errors) inside `.cause` and exposes a
|
|
244
|
+
// bare "fetch failed" on the outer Error. Surfacing the cause's `code`
|
|
245
|
+
// and message turns an unactionable failure into something the agent
|
|
246
|
+
// (and the user) can actually triage.
|
|
247
|
+
const error = err;
|
|
248
|
+
const cause = error.cause;
|
|
249
|
+
const causeCode = cause?.code ?? error.code;
|
|
250
|
+
const causeMsg = cause?.message ?? "";
|
|
251
|
+
const detail = causeCode
|
|
252
|
+
? causeMsg
|
|
253
|
+
? `${causeCode}: ${causeMsg}`
|
|
254
|
+
: causeCode
|
|
255
|
+
: (error.message ?? "unknown error");
|
|
256
|
+
// Log the full chain so triage doesn't depend on the agent surface
|
|
257
|
+
// forwarding the wrapped error message.
|
|
258
|
+
warn(`URL fetch failed: ${currentUrl.href} — ${detail}`, {
|
|
259
|
+
outerMessage: error.message,
|
|
260
|
+
causeName: cause?.name,
|
|
261
|
+
causeCode,
|
|
262
|
+
causeErrno: cause?.errno,
|
|
263
|
+
causeSyscall: cause?.syscall,
|
|
264
|
+
});
|
|
265
|
+
throw new Error(`Failed to fetch ${currentUrl.href}: ${detail}`);
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
clearTimeout(timer);
|
|
269
|
+
}
|
|
270
|
+
if (response.status < 300 || response.status >= 400) {
|
|
271
|
+
return response;
|
|
272
|
+
}
|
|
273
|
+
if (redirectCount === MAX_REDIRECTS) {
|
|
274
|
+
throw new Error(`Too many redirects fetching ${startUrl.href}`);
|
|
275
|
+
}
|
|
276
|
+
currentUrl = resolveRedirectUrl(currentUrl, response.headers.get("location"));
|
|
277
|
+
}
|
|
278
|
+
throw new Error(`Too many redirects fetching ${startUrl.href}`);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Map a Content-Type header to a file extension AFT can parse.
|
|
282
|
+
* Returns null for unsupported types.
|
|
283
|
+
*/
|
|
284
|
+
function resolveExtension(contentType) {
|
|
285
|
+
// Normalize: strip parameters (after `;`) AND pick the first value if the server
|
|
286
|
+
// echoed back a comma-separated list (GitHub API does this for /readme endpoints).
|
|
287
|
+
const lower = contentType.toLowerCase().split(";")[0].split(",")[0].trim();
|
|
288
|
+
if (lower === "text/html" ||
|
|
289
|
+
lower === "application/xhtml+xml" ||
|
|
290
|
+
lower === "application/vnd.github.html" ||
|
|
291
|
+
lower === "application/vnd.github+html") {
|
|
292
|
+
return ".html";
|
|
293
|
+
}
|
|
294
|
+
if (lower === "text/markdown" ||
|
|
295
|
+
lower === "text/x-markdown" ||
|
|
296
|
+
lower === "application/markdown" ||
|
|
297
|
+
// GitHub API raw content type — returns raw markdown for README endpoints
|
|
298
|
+
lower === "application/vnd.github.raw" ||
|
|
299
|
+
lower === "application/vnd.github+raw" ||
|
|
300
|
+
lower === "application/vnd.github.v3.raw") {
|
|
301
|
+
return ".md";
|
|
302
|
+
}
|
|
303
|
+
if (lower === "text/plain") {
|
|
304
|
+
// treat plain text as markdown so aft_outline can show headings if present
|
|
305
|
+
return ".md";
|
|
306
|
+
}
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Fetch a URL to a cached temp file. Uses disk cache with 1-day TTL.
|
|
311
|
+
* Returns the cached file path the Rust outline/zoom command can read.
|
|
312
|
+
* Throws on errors (invalid URL, network failure, unsupported content type, oversized body).
|
|
313
|
+
*/
|
|
314
|
+
export async function fetchUrlToTempFile(url, storageDir, options = {}) {
|
|
315
|
+
let parsed;
|
|
316
|
+
try {
|
|
317
|
+
parsed = new URL(url);
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
throw new Error(`Invalid URL: ${url}`);
|
|
321
|
+
}
|
|
322
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
323
|
+
throw new Error(`Only http:// and https:// URLs are supported, got: ${parsed.protocol}`);
|
|
324
|
+
}
|
|
325
|
+
const allowPrivate = options.allowPrivate === true;
|
|
326
|
+
const dir = cacheDir(storageDir);
|
|
327
|
+
mkdirSync(dir, { recursive: true });
|
|
328
|
+
const hash = hashUrl(url);
|
|
329
|
+
const metaFile = metaPath(storageDir, hash);
|
|
330
|
+
// Check cache
|
|
331
|
+
if (existsSync(metaFile)) {
|
|
332
|
+
try {
|
|
333
|
+
const meta = JSON.parse(readFileSync(metaFile, "utf8"));
|
|
334
|
+
const age = Date.now() - meta.fetchedAt;
|
|
335
|
+
const cached = contentPath(storageDir, hash, meta.extension);
|
|
336
|
+
if (age < CACHE_TTL_MS && existsSync(cached)) {
|
|
337
|
+
log(`URL cache hit: ${url} (${Math.round(age / 1000)}s old)`);
|
|
338
|
+
return cached;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
// corrupted meta, re-fetch
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
log(`Fetching URL: ${url}`);
|
|
346
|
+
const response = await fetchWithRedirects(parsed, allowPrivate, options);
|
|
347
|
+
if (!response.ok) {
|
|
348
|
+
throw new Error(`HTTP ${response.status} ${response.statusText} fetching ${url}`);
|
|
349
|
+
}
|
|
350
|
+
const contentType = response.headers.get("content-type") || "text/plain";
|
|
351
|
+
const extension = resolveExtension(contentType);
|
|
352
|
+
if (!extension) {
|
|
353
|
+
throw new Error(`Unsupported content type '${contentType}' for ${url}. Supported: text/html, text/markdown, text/plain`);
|
|
354
|
+
}
|
|
355
|
+
const lengthHeader = response.headers.get("content-length");
|
|
356
|
+
if (lengthHeader) {
|
|
357
|
+
const length = Number.parseInt(lengthHeader, 10);
|
|
358
|
+
if (Number.isFinite(length) && length > MAX_RESPONSE_BYTES) {
|
|
359
|
+
throw new Error(`Response too large: ${length} bytes (max ${MAX_RESPONSE_BYTES})`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
// Stream with size cap
|
|
363
|
+
const reader = response.body?.getReader();
|
|
364
|
+
if (!reader) {
|
|
365
|
+
throw new Error(`Failed to read response body for ${url}`);
|
|
366
|
+
}
|
|
367
|
+
const chunks = [];
|
|
368
|
+
let total = 0;
|
|
369
|
+
while (true) {
|
|
370
|
+
const { done, value } = await reader.read();
|
|
371
|
+
if (done)
|
|
372
|
+
break;
|
|
373
|
+
if (value) {
|
|
374
|
+
total += value.length;
|
|
375
|
+
if (total > MAX_RESPONSE_BYTES) {
|
|
376
|
+
reader.cancel().catch(() => { });
|
|
377
|
+
throw new Error(`Response exceeded ${MAX_RESPONSE_BYTES} bytes, aborted`);
|
|
378
|
+
}
|
|
379
|
+
chunks.push(value);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// Write content and meta atomically
|
|
383
|
+
const body = Buffer.concat(chunks);
|
|
384
|
+
const contentFile = contentPath(storageDir, hash, extension);
|
|
385
|
+
const tmpContent = `${contentFile}.tmp-${process.pid}`;
|
|
386
|
+
writeFileSync(tmpContent, body);
|
|
387
|
+
const { renameSync } = await import("node:fs");
|
|
388
|
+
renameSync(tmpContent, contentFile);
|
|
389
|
+
const meta = {
|
|
390
|
+
url,
|
|
391
|
+
contentType,
|
|
392
|
+
extension,
|
|
393
|
+
fetchedAt: Date.now(),
|
|
394
|
+
};
|
|
395
|
+
const tmpMeta = `${metaFile}.tmp-${process.pid}`;
|
|
396
|
+
writeFileSync(tmpMeta, JSON.stringify(meta));
|
|
397
|
+
renameSync(tmpMeta, metaFile);
|
|
398
|
+
log(`URL cached (${total} bytes): ${url}`);
|
|
399
|
+
return contentFile;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Remove cache entries older than TTL. Called periodically at plugin startup.
|
|
403
|
+
*/
|
|
404
|
+
export function cleanupUrlCache(storageDir) {
|
|
405
|
+
const dir = cacheDir(storageDir);
|
|
406
|
+
if (!existsSync(dir))
|
|
407
|
+
return;
|
|
408
|
+
let removed = 0;
|
|
409
|
+
try {
|
|
410
|
+
for (const entry of readdirSync(dir)) {
|
|
411
|
+
if (!entry.endsWith(".meta.json"))
|
|
412
|
+
continue;
|
|
413
|
+
const metaFile = join(dir, entry);
|
|
414
|
+
try {
|
|
415
|
+
const meta = JSON.parse(readFileSync(metaFile, "utf8"));
|
|
416
|
+
const age = Date.now() - meta.fetchedAt;
|
|
417
|
+
if (age > CACHE_TTL_MS) {
|
|
418
|
+
const hash = entry.slice(0, -".meta.json".length);
|
|
419
|
+
const content = contentPath(storageDir, hash, meta.extension);
|
|
420
|
+
if (existsSync(content))
|
|
421
|
+
unlinkSync(content);
|
|
422
|
+
unlinkSync(metaFile);
|
|
423
|
+
removed++;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
// corrupted meta, remove it too
|
|
428
|
+
try {
|
|
429
|
+
unlinkSync(metaFile);
|
|
430
|
+
removed++;
|
|
431
|
+
}
|
|
432
|
+
catch { }
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
catch (err) {
|
|
437
|
+
warn(`URL cache cleanup failed: ${err.message}`);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
if (removed > 0) {
|
|
441
|
+
log(`URL cache cleanup: removed ${removed} stale entries`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
//# sourceMappingURL=url-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url-fetch.js","sourceRoot":"","sources":["../src/url-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EACL,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACZ,UAAU,EACV,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAmB,KAAK,IAAI,WAAW,EAAE,MAAM,QAAQ,CAAC;AACtE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE/C,qCAAqC;AACrC,MAAM,kBAAkB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAC5C,uBAAuB;AACvB,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACzC,gCAAgC;AAChC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,MAAM,aAAa,GAAG,CAAC,CAAC;AA6BxB,SAAS,QAAQ,CAAC,UAAkB;IAClC,OAAO,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,OAAO,CAAC,GAAW;IAC1B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,QAAQ,CAAC,UAAkB,EAAE,IAAY;IAChD,OAAO,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,GAAG,IAAI,YAAY,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,WAAW,CAAC,UAAkB,EAAE,IAAY,EAAE,SAAiB;IACtE,OAAO,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IAC1E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACrF,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC;IACrB,OAAO,CACL,CAAC,KAAK,CAAC,IAAI,6DAA6D;QACxE,CAAC,KAAK,EAAE;QACR,CAAC,KAAK,GAAG;QACT,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QACjC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC;QACxB,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC;QACxB,CAAC,IAAI,GAAG,CAAC,yCAAyC;KACnD,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACnD,4BAA4B;IAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnD,wDAAwD;IACxD,IAAI,UAAU,GAAG,IAAI,CAAC;IACtB,MAAM,SAAS,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC9C,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;QAC7C,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAClE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;gBACnF,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACvD,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACvD,UAAU,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,IAAI,KAAe,CAAC;IACpB,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,GAAG,CAAC,GAAG,SAAS,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;QACtD,IAAI,IAAI,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1B,KAAK,GAAG,CAAC,GAAG,SAAS,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,UAAU,CAAC,CAAC;IAClE,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC9B,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACjC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAClC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACpC,2DAA2D;IAC3D,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAEnC,uEAAuE;IACvE,mEAAmE;IACnE,oBAAoB;IACpB,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,OAAO,EAAE,CAAC;QACZ,yEAAyE;QACzE,6EAA6E;QAC7E,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3D,MAAM,QAAQ,GACZ,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAChB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAChB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAChB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAChB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAChB,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC;QACxB,IAAI,QAAQ,IAAI,QAAQ,EAAE,CAAC;YACzB,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;YACnC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;YACnC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;YAC5B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,uEAAuE;YACvE,mEAAmE;YACnE,IAAI,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpD,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;QAED,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QAC/B,OAAO;QACL,uBAAuB;QACvB,CAAC,WAAW,IAAI,MAAM,IAAI,WAAW,IAAI,MAAM,CAAC;YAChD,wBAAwB;YACxB,CAAC,WAAW,IAAI,MAAM,IAAI,WAAW,IAAI,MAAM,CAAC;YAChD,qBAAqB;YACrB,WAAW,IAAI,MAAM,CACtB,CAAC;IACJ,CAAC;IAED,gDAAgD;IAChD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,eAAe,CAC5B,GAAQ,EACR,YAAqB,EACrB,YAAsB,MAAM;IAE5B,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,sDAAsD,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,IAAI,YAAY;QAAE,OAAO,SAAS,CAAC;IAEnC,+EAA+E;IAC/E,0CAA0C;IAC1C,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAEtD,8EAA8E;IAC9E,4EAA4E;IAC5E,0EAA0E;IAC1E,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C,IAAI,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,CAAC,QAAQ,KAAK,QAAQ,GAAG,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,SAAS,EAAE,CAAC;QACpC,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,CAAC,QAAQ,KAAK,OAAO,GAAG,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,yEAAyE;IACzE,2EAA2E;IAC3E,2EAA2E;IAC3E,6EAA6E;IAC7E,2EAA2E;IAC3E,6DAA6D;IAC7D,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CACzB,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,MAAM,KAAK,CAAC;QACjB,KAAK,CAAC,MAAkB,KAAK,MAAM;QACpC,CAAC,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CACnE,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjF,qEAAqE;QACrE,qEAAqE;QACrE,qEAAqE;QACrE,+CAA+C;QAC/C,MAAM,IAAI,KAAK,CACb,kBAAkB,GAAG,CAAC,QAAQ,qCAAqC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,CAChG,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,OAAO,CAAC;AACxB,CAAC;AAED,SAAS,sBAAsB,CAAC,WAAmB;IACjD,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,wEAAwE;IACxE,4EAA4E;IAC5E,0EAA0E;IAC1E,oEAAoE;IACpE,2EAA2E;IAC3E,oEAAoE;IACpE,wCAAwC;IACxC,wEAAwE;IACxE,mEAAmE;IACnE,4EAA4E;IAC5E,MAAM,YAAY,GAAQ,CACxB,SAAiB,EACjB,IAAmC;IACnC,4EAA4E;IAC5E,QAAa,EACb,EAAE;QACF,IAAI,IAAI,EAAE,GAAG,EAAE,CAAC;YACd,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QACrD,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CAAC;IACF,OAAO,IAAI,KAAK,CAAC;QACf,OAAO,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE;KAClC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,kBAAkB,CAAC,UAAe,EAAE,QAAuB;IAClE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,iBAAiB,UAAU,CAAC,IAAI,0BAA0B,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,IAAI,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AACvC,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,QAAa,EACb,YAAqB,EACrB,OAA4E,EAAE;IAE9E,IAAI,UAAU,GAAG,QAAQ,CAAC;IAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAK,WAAyB,CAAC;IAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC;IAExC,KAAK,IAAI,aAAa,GAAG,CAAC,EAAE,aAAa,IAAI,aAAa,EAAE,aAAa,EAAE,EAAE,CAAC;QAC5E,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,UAAU,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;QAC/E,MAAM,UAAU,GAAG,WAAW;YAC5B,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,IAAI,sBAAsB,CAAC,CAAC,WAAW,CAAC;YACjE,CAAC,CAAC,SAAS,CAAC;QAEd,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,gBAAgB,CAAC,CAAC;QACrE,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE;gBAC1C,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,QAAQ,EAAE,QAAQ;gBAClB,UAAU;gBACV,OAAO,EAAE;oBACP,YAAY,EAAE,qBAAqB;oBACnC,qFAAqF;oBACrF,mFAAmF;oBACnF,mFAAmF;oBACnF,MAAM,EACJ,+FAA+F;iBAClG;aACF,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,wEAAwE;YACxE,wEAAwE;YACxE,uEAAuE;YACvE,qEAAqE;YACrE,sCAAsC;YACtC,MAAM,KAAK,GAAG,GAAiD,CAAC;YAChE,MAAM,KAAK,GAAG,KAAK,CAAC,KAEP,CAAC;YACd,MAAM,SAAS,GAAG,KAAK,EAAE,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC;YAC5C,MAAM,QAAQ,GAAG,KAAK,EAAE,OAAO,IAAI,EAAE,CAAC;YACtC,MAAM,MAAM,GAAG,SAAS;gBACtB,CAAC,CAAC,QAAQ;oBACR,CAAC,CAAC,GAAG,SAAS,KAAK,QAAQ,EAAE;oBAC7B,CAAC,CAAC,SAAS;gBACb,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,eAAe,CAAC,CAAC;YACvC,mEAAmE;YACnE,wCAAwC;YACxC,IAAI,CAAC,qBAAqB,UAAU,CAAC,IAAI,MAAM,MAAM,EAAE,EAAE;gBACvD,YAAY,EAAE,KAAK,CAAC,OAAO;gBAC3B,SAAS,EAAE,KAAK,EAAE,IAAI;gBACtB,SAAS;gBACT,UAAU,EAAE,KAAK,EAAE,KAAK;gBACxB,YAAY,EAAE,KAAK,EAAE,OAAO;aAC7B,CAAC,CAAC;YACH,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC;QACnE,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;YACpD,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,IAAI,aAAa,KAAK,aAAa,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,UAAU,GAAG,kBAAkB,CAAC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,WAAmB;IAC3C,iFAAiF;IACjF,mFAAmF;IACnF,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3E,IACE,KAAK,KAAK,WAAW;QACrB,KAAK,KAAK,uBAAuB;QACjC,KAAK,KAAK,6BAA6B;QACvC,KAAK,KAAK,6BAA6B,EACvC,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IACE,KAAK,KAAK,eAAe;QACzB,KAAK,KAAK,iBAAiB;QAC3B,KAAK,KAAK,sBAAsB;QAChC,0EAA0E;QAC1E,KAAK,KAAK,4BAA4B;QACtC,KAAK,KAAK,4BAA4B;QACtC,KAAK,KAAK,+BAA+B,EACzC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,KAAK,YAAY,EAAE,CAAC;QAC3B,2EAA2E;QAC3E,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,GAAW,EACX,UAAkB,EAClB,UAA2B,EAAE;IAE7B,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,gBAAgB,GAAG,EAAE,CAAC,CAAC;IACzC,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,sDAAsD,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3F,CAAC;IACD,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,KAAK,IAAI,CAAC;IAEnD,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IACjC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAE5C,cAAc;IACd,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAc,CAAC;YACrE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YACxC,MAAM,MAAM,GAAG,WAAW,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7D,IAAI,GAAG,GAAG,YAAY,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC7C,GAAG,CAAC,kBAAkB,GAAG,KAAK,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC9D,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,2BAA2B;QAC7B,CAAC;IACH,CAAC;IAED,GAAG,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAC;IAC5B,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IAEzE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,aAAa,GAAG,EAAE,CAAC,CAAC;IACpF,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,YAAY,CAAC;IACzE,MAAM,SAAS,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAChD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,6BAA6B,WAAW,SAAS,GAAG,mDAAmD,CACxG,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC5D,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QACjD,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,kBAAkB,EAAE,CAAC;YAC3D,MAAM,IAAI,KAAK,CAAC,uBAAuB,MAAM,eAAe,kBAAkB,GAAG,CAAC,CAAC;QACrF,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;IAC1C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,oCAAoC,GAAG,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,IAAI;YAAE,MAAM;QAChB,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;YACtB,IAAI,KAAK,GAAG,kBAAkB,EAAE,CAAC;gBAC/B,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAChC,MAAM,IAAI,KAAK,CAAC,qBAAqB,kBAAkB,iBAAiB,CAAC,CAAC;YAC5E,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,oCAAoC;IACpC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,WAAW,GAAG,WAAW,CAAC,UAAU,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,GAAG,WAAW,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAC;IACvD,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAChC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/C,UAAU,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAEpC,MAAM,IAAI,GAAc;QACtB,GAAG;QACH,WAAW;QACX,SAAS;QACT,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;IACF,MAAM,OAAO,GAAG,GAAG,QAAQ,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAC;IACjD,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAE9B,GAAG,CAAC,eAAe,KAAK,YAAY,GAAG,EAAE,CAAC,CAAC;IAC3C,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB;IAChD,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IACjC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO;IAE7B,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,CAAC;QACH,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC;gBAAE,SAAS;YAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAClC,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAc,CAAC;gBACrE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;gBACxC,IAAI,GAAG,GAAG,YAAY,EAAE,CAAC;oBACvB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;oBAClD,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;oBAC9D,IAAI,UAAU,CAAC,OAAO,CAAC;wBAAE,UAAU,CAAC,OAAO,CAAC,CAAC;oBAC7C,UAAU,CAAC,QAAQ,CAAC,CAAC;oBACrB,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,gCAAgC;gBAChC,IAAI,CAAC;oBACH,UAAU,CAAC,QAAQ,CAAC,CAAC;oBACrB,OAAO,EAAE,CAAC;gBACZ,CAAC;gBAAC,MAAM,CAAC,CAAA,CAAC;YACZ,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,6BAA8B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5D,OAAO;IACT,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,GAAG,CAAC,8BAA8B,OAAO,gBAAgB,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plain-text formatter for `aft_zoom` responses.
|
|
3
|
+
*
|
|
4
|
+
* Both @cortexkit/aft-opencode and @cortexkit/aft-pi consume the same Rust
|
|
5
|
+
* zoom command response shape — keep one formatter so both hosts produce
|
|
6
|
+
* byte-identical output.
|
|
7
|
+
*
|
|
8
|
+
* The previous output was JSON.stringify of the raw response, which left
|
|
9
|
+
* the agent decoding `\n` and `\"` escapes to read the source. The new
|
|
10
|
+
* format inlines content with line numbers and only renders annotation
|
|
11
|
+
* sections when non-empty.
|
|
12
|
+
*
|
|
13
|
+
* Output shape (single symbol):
|
|
14
|
+
*
|
|
15
|
+
* src/foo.ts:8-43 [function resolveParentContext]
|
|
16
|
+
*
|
|
17
|
+
* 5: import { resolveMessageContext } from "...";
|
|
18
|
+
* 6: import { getSessionAgent } from "...";
|
|
19
|
+
* 7:
|
|
20
|
+
* 8: export async function resolveParentContext(
|
|
21
|
+
* 9: ctx: ToolContextWithMetadata,
|
|
22
|
+
* ...
|
|
23
|
+
* 43: }
|
|
24
|
+
*
|
|
25
|
+
* ──── calls_out
|
|
26
|
+
* resolveMessageContext (line 13)
|
|
27
|
+
* getSessionAgent (line 16)
|
|
28
|
+
*
|
|
29
|
+
* ──── called_by
|
|
30
|
+
* handleTaskRequest (line 87)
|
|
31
|
+
*
|
|
32
|
+
* Annotation sections are omitted when empty. Context-before/after lines are
|
|
33
|
+
* included when present (their line numbers continue the gutter).
|
|
34
|
+
*/
|
|
35
|
+
interface RangeShape {
|
|
36
|
+
start_line: number;
|
|
37
|
+
end_line: number;
|
|
38
|
+
start_col?: number;
|
|
39
|
+
end_col?: number;
|
|
40
|
+
}
|
|
41
|
+
interface CallRefShape {
|
|
42
|
+
name: string;
|
|
43
|
+
line: number;
|
|
44
|
+
}
|
|
45
|
+
interface AnnotationsShape {
|
|
46
|
+
calls_out?: CallRefShape[];
|
|
47
|
+
called_by?: CallRefShape[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Subset of the Rust ZoomResponse shape this formatter cares about. Extra
|
|
51
|
+
* fields (id, command, success, ...) are ignored.
|
|
52
|
+
*/
|
|
53
|
+
export interface ZoomResponseLike {
|
|
54
|
+
name?: string;
|
|
55
|
+
kind?: string;
|
|
56
|
+
range?: RangeShape;
|
|
57
|
+
content?: string;
|
|
58
|
+
context_before?: string[];
|
|
59
|
+
context_after?: string[];
|
|
60
|
+
annotations?: AnnotationsShape;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Format a single Rust zoom response as plain text.
|
|
64
|
+
*
|
|
65
|
+
* `targetLabel` is what the agent passed in (filePath or url) — used for the
|
|
66
|
+
* header. Avoids leaking internal cache paths when the agent zoomed into a URL.
|
|
67
|
+
*/
|
|
68
|
+
export declare function formatZoomText(targetLabel: string, response: ZoomResponseLike): string;
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=zoom-format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zoom-format.d.ts","sourceRoot":"","sources":["../src/zoom-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,UAAU,UAAU;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,gBAAgB;IACxB,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAgEtF"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plain-text formatter for `aft_zoom` responses.
|
|
3
|
+
*
|
|
4
|
+
* Both @cortexkit/aft-opencode and @cortexkit/aft-pi consume the same Rust
|
|
5
|
+
* zoom command response shape — keep one formatter so both hosts produce
|
|
6
|
+
* byte-identical output.
|
|
7
|
+
*
|
|
8
|
+
* The previous output was JSON.stringify of the raw response, which left
|
|
9
|
+
* the agent decoding `\n` and `\"` escapes to read the source. The new
|
|
10
|
+
* format inlines content with line numbers and only renders annotation
|
|
11
|
+
* sections when non-empty.
|
|
12
|
+
*
|
|
13
|
+
* Output shape (single symbol):
|
|
14
|
+
*
|
|
15
|
+
* src/foo.ts:8-43 [function resolveParentContext]
|
|
16
|
+
*
|
|
17
|
+
* 5: import { resolveMessageContext } from "...";
|
|
18
|
+
* 6: import { getSessionAgent } from "...";
|
|
19
|
+
* 7:
|
|
20
|
+
* 8: export async function resolveParentContext(
|
|
21
|
+
* 9: ctx: ToolContextWithMetadata,
|
|
22
|
+
* ...
|
|
23
|
+
* 43: }
|
|
24
|
+
*
|
|
25
|
+
* ──── calls_out
|
|
26
|
+
* resolveMessageContext (line 13)
|
|
27
|
+
* getSessionAgent (line 16)
|
|
28
|
+
*
|
|
29
|
+
* ──── called_by
|
|
30
|
+
* handleTaskRequest (line 87)
|
|
31
|
+
*
|
|
32
|
+
* Annotation sections are omitted when empty. Context-before/after lines are
|
|
33
|
+
* included when present (their line numbers continue the gutter).
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Format a single Rust zoom response as plain text.
|
|
37
|
+
*
|
|
38
|
+
* `targetLabel` is what the agent passed in (filePath or url) — used for the
|
|
39
|
+
* header. Avoids leaking internal cache paths when the agent zoomed into a URL.
|
|
40
|
+
*/
|
|
41
|
+
export function formatZoomText(targetLabel, response) {
|
|
42
|
+
const range = response.range;
|
|
43
|
+
const startLine = range?.start_line ?? 1;
|
|
44
|
+
const endLine = range?.end_line ?? startLine;
|
|
45
|
+
const kind = response.kind ?? "symbol";
|
|
46
|
+
const name = response.name ?? "";
|
|
47
|
+
const contentText = typeof response.content === "string" ? response.content : "";
|
|
48
|
+
const ctxBefore = Array.isArray(response.context_before) ? response.context_before : [];
|
|
49
|
+
const ctxAfter = Array.isArray(response.context_after) ? response.context_after : [];
|
|
50
|
+
// Header. For "lines" kind (range fallback when no symbol matches) drop the
|
|
51
|
+
// redundant "[lines lines X-Y]" decoration and just show path:start-end.
|
|
52
|
+
const header = kind === "lines"
|
|
53
|
+
? `${targetLabel}:${startLine}-${endLine}`
|
|
54
|
+
: `${targetLabel}:${startLine}-${endLine} [${kind} ${name}]`.trimEnd();
|
|
55
|
+
// Strip a trailing empty line introduced by content.split("\n") on a body
|
|
56
|
+
// that ends with "\n". Real zoom content always ends with the symbol's
|
|
57
|
+
// closing brace + a newline.
|
|
58
|
+
const contentLines = contentText.split("\n");
|
|
59
|
+
if (contentLines.length > 0 && contentLines[contentLines.length - 1] === "") {
|
|
60
|
+
contentLines.pop();
|
|
61
|
+
}
|
|
62
|
+
const lastDisplayedLine = endLine + ctxAfter.length;
|
|
63
|
+
const gutterWidth = String(Math.max(lastDisplayedLine, 1)).length;
|
|
64
|
+
const fmt = (lineNo, text) => `${String(lineNo).padStart(gutterWidth)}: ${text}`;
|
|
65
|
+
const out = [header, ""];
|
|
66
|
+
// context_before sits BEFORE startLine. ctxBefore.length entries → numbers
|
|
67
|
+
// start at startLine - ctxBefore.length and run up to startLine - 1.
|
|
68
|
+
let lineNo = startLine - ctxBefore.length;
|
|
69
|
+
for (const text of ctxBefore) {
|
|
70
|
+
out.push(fmt(lineNo, text));
|
|
71
|
+
lineNo += 1;
|
|
72
|
+
}
|
|
73
|
+
for (const text of contentLines) {
|
|
74
|
+
out.push(fmt(lineNo, text));
|
|
75
|
+
lineNo += 1;
|
|
76
|
+
}
|
|
77
|
+
for (const text of ctxAfter) {
|
|
78
|
+
out.push(fmt(lineNo, text));
|
|
79
|
+
lineNo += 1;
|
|
80
|
+
}
|
|
81
|
+
// Annotations (only when non-empty)
|
|
82
|
+
const callsOut = response.annotations?.calls_out ?? [];
|
|
83
|
+
const calledBy = response.annotations?.called_by ?? [];
|
|
84
|
+
if (callsOut.length > 0) {
|
|
85
|
+
out.push("", "──── calls_out");
|
|
86
|
+
for (const ref of callsOut) {
|
|
87
|
+
out.push(` ${ref.name} (line ${ref.line})`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (calledBy.length > 0) {
|
|
91
|
+
out.push("", "──── called_by");
|
|
92
|
+
for (const ref of calledBy) {
|
|
93
|
+
out.push(` ${ref.name} (line ${ref.line})`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return out.join("\n");
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=zoom-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zoom-format.js","sourceRoot":"","sources":["../src/zoom-format.ts"],"names":[],"mappings":"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"}
|