@easynet/agent-tool-buildin 0.0.2 → 0.0.4

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.js CHANGED
@@ -103,62 +103,19 @@ var DEFAULT_CORE_TOOLS_CONFIG = {
103
103
 
104
104
  // fs/readText.ts
105
105
  import { readFile, stat } from "fs/promises";
106
-
107
- // security/sandbox.ts
108
- import { resolve, normalize, dirname, basename } from "path";
109
- import { realpath, access } from "fs/promises";
106
+ import { resolveSandboxedPath } from "@easynet/agent-tool";
110
107
  import { createTaggedError } from "@easynet/agent-tool";
111
- async function resolveSandboxedPath(inputPath, sandboxRoot) {
112
- let normalizedRoot;
113
- try {
114
- normalizedRoot = await realpath(resolve(sandboxRoot));
115
- } catch {
116
- normalizedRoot = normalize(resolve(sandboxRoot));
117
- }
118
- const resolved = resolve(normalizedRoot, inputPath);
119
- let real;
120
- try {
121
- await access(resolved);
122
- real = await realpath(resolved);
123
- } catch {
124
- const parentDir = dirname(resolved);
125
- let realParent;
126
- try {
127
- await access(parentDir);
128
- realParent = await realpath(parentDir);
129
- } catch {
130
- realParent = normalize(parentDir);
131
- }
132
- real = resolve(realParent, basename(resolved));
133
- }
134
- if (!isWithinRoot(real, normalizedRoot)) {
135
- throw createTaggedError(
136
- "PATH_OUTSIDE_SANDBOX",
137
- `Path "${inputPath}" resolves to "${real}" which is outside sandbox "${normalizedRoot}"`,
138
- { inputPath, resolvedPath: real, sandboxRoot: normalizedRoot }
139
- );
140
- }
141
- return real;
142
- }
143
- function isWithinRoot(path, root) {
144
- const normalizedPath = normalize(path);
145
- const normalizedRoot = normalize(root);
146
- return normalizedPath === normalizedRoot || normalizedPath.startsWith(normalizedRoot + "/");
147
- }
148
-
149
- // fs/readText.ts
150
- import { createTaggedError as createTaggedError2 } from "@easynet/agent-tool";
151
108
  var readTextHandler = (async (args) => {
152
109
  const ctx = getBuiltinContext();
153
110
  const inputPath = (args.path ?? args.filePath)?.trim();
154
111
  if (!inputPath) {
155
- throw createTaggedError2("FS_INVALID", "path is required (pass 'path' or 'filePath')", {});
112
+ throw createTaggedError("FS_INVALID", "path is required (pass 'path' or 'filePath')", {});
156
113
  }
157
114
  const maxBytes = args.maxBytes ?? ctx.config.maxReadBytes;
158
115
  const resolvedPath = await resolveSandboxedPath(inputPath, ctx.config.sandboxRoot);
159
116
  const fileStat = await stat(resolvedPath);
160
117
  if (fileStat.size > maxBytes) {
161
- throw createTaggedError2(
118
+ throw createTaggedError(
162
119
  "FILE_TOO_LARGE",
163
120
  `File size ${fileStat.size} bytes exceeds limit of ${maxBytes} bytes`,
164
121
  { path: resolvedPath, size: fileStat.size, limit: maxBytes }
@@ -185,7 +142,8 @@ var readTextHandler = (async (args) => {
185
142
  // fs/writeText.ts
186
143
  import { writeFile, mkdir } from "fs/promises";
187
144
  import { createHash } from "crypto";
188
- import { dirname as dirname2 } from "path";
145
+ import { dirname } from "path";
146
+ import { resolveSandboxedPath as resolveSandboxedPath2 } from "@easynet/agent-tool";
189
147
  var writeTextHandler = (async (args) => {
190
148
  const ctx = getBuiltinContext();
191
149
  const inputPath = (args.path ?? args.filePath)?.trim();
@@ -195,11 +153,11 @@ var writeTextHandler = (async (args) => {
195
153
  const text = args.text ?? args.content ?? "";
196
154
  const overwrite = args.overwrite ?? false;
197
155
  const mkdirp = args.mkdirp ?? true;
198
- const resolvedPath = await resolveSandboxedPath(inputPath, ctx.config.sandboxRoot);
156
+ const resolvedPath = await resolveSandboxedPath2(inputPath, ctx.config.sandboxRoot);
199
157
  if (!overwrite) {
200
- const { access: access2 } = await import("fs/promises");
158
+ const { access } = await import("fs/promises");
201
159
  try {
202
- await access2(resolvedPath);
160
+ await access(resolvedPath);
203
161
  throw new Error(
204
162
  `File already exists: ${resolvedPath}. Set overwrite=true to allow overwriting.`
205
163
  );
@@ -211,7 +169,7 @@ var writeTextHandler = (async (args) => {
211
169
  }
212
170
  }
213
171
  if (mkdirp) {
214
- await mkdir(dirname2(resolvedPath), { recursive: true });
172
+ await mkdir(dirname(resolvedPath), { recursive: true });
215
173
  }
216
174
  await writeFile(resolvedPath, text, "utf-8");
217
175
  const bytes = Buffer.byteLength(text, "utf-8");
@@ -235,7 +193,8 @@ var writeTextHandler = (async (args) => {
235
193
 
236
194
  // fs/listDir.ts
237
195
  import { readdir, stat as stat2 } from "fs/promises";
238
- import { resolve as resolve2, join } from "path";
196
+ import { resolve, join } from "path";
197
+ import { resolveSandboxedPath as resolveSandboxedPath3 } from "@easynet/agent-tool";
239
198
  var listDirHandler = (async (args) => {
240
199
  const ctx = getBuiltinContext();
241
200
  const inputPath = (args.path ?? args.filePath ?? args.dir ?? args.directory)?.trim();
@@ -246,7 +205,7 @@ var listDirHandler = (async (args) => {
246
205
  const includeHidden = args.includeHidden ?? false;
247
206
  const recursive = args.recursive ?? false;
248
207
  const maxDepth = args.maxDepth ?? 5;
249
- const resolvedPath = await resolveSandboxedPath(inputPath, ctx.config.sandboxRoot);
208
+ const resolvedPath = await resolveSandboxedPath3(inputPath, ctx.config.sandboxRoot);
250
209
  const entries = [];
251
210
  let truncated = false;
252
211
  await walkDir(resolvedPath, "", entries, {
@@ -281,7 +240,7 @@ async function walkDir(basePath, relativePath, entries, options) {
281
240
  options.onTruncate();
282
241
  return;
283
242
  }
284
- const fullPath = relativePath ? resolve2(basePath, relativePath) : basePath;
243
+ const fullPath = relativePath ? resolve(basePath, relativePath) : basePath;
285
244
  const dirEntries = await readdir(fullPath, { withFileTypes: true });
286
245
  for (const dirent of dirEntries) {
287
246
  if (entries.length >= options.maxEntries) {
@@ -331,6 +290,7 @@ import { readdir as readdir2, stat as stat3 } from "fs/promises";
331
290
  import { createReadStream } from "fs";
332
291
  import { createInterface } from "readline";
333
292
  import { join as join2, relative } from "path";
293
+ import { resolveSandboxedPath as resolveSandboxedPath4 } from "@easynet/agent-tool";
334
294
  var searchTextHandler = (async (args) => {
335
295
  const ctx = getBuiltinContext();
336
296
  const rootPath = (args.root ?? args.path ?? args.dir ?? args.directory)?.trim();
@@ -344,7 +304,7 @@ var searchTextHandler = (async (args) => {
344
304
  const glob = args.glob ?? "**/*.{md,txt,log,json,ts,js,py,java,scala}";
345
305
  const maxMatches = args.maxMatches ?? 100;
346
306
  const maxFiles = args.maxFiles ?? 500;
347
- const resolvedRoot = await resolveSandboxedPath(rootPath, ctx.config.sandboxRoot);
307
+ const resolvedRoot = await resolveSandboxedPath4(rootPath, ctx.config.sandboxRoot);
348
308
  let regex;
349
309
  try {
350
310
  regex = new RegExp(query, "i");
@@ -459,20 +419,21 @@ function escapeRegExp(str) {
459
419
  import { createReadStream as createReadStream2 } from "fs";
460
420
  import { stat as stat4 } from "fs/promises";
461
421
  import { createHash as createHash2 } from "crypto";
462
- import { createTaggedError as createTaggedError3 } from "@easynet/agent-tool";
422
+ import { resolveSandboxedPath as resolveSandboxedPath5 } from "@easynet/agent-tool";
423
+ import { createTaggedError as createTaggedError2 } from "@easynet/agent-tool";
463
424
  var sha256Handler = (async (args) => {
464
425
  const ctx = getBuiltinContext();
465
426
  const inputPath = (args.path ?? args.filePath)?.trim();
466
427
  if (!inputPath) {
467
- throw createTaggedError3("FS_INVALID", "path is required (pass 'path' or 'filePath')", {});
428
+ throw createTaggedError2("FS_INVALID", "path is required (pass 'path' or 'filePath')", {});
468
429
  }
469
- const resolvedPath = await resolveSandboxedPath(inputPath, ctx.config.sandboxRoot);
430
+ const resolvedPath = await resolveSandboxedPath5(inputPath, ctx.config.sandboxRoot);
470
431
  const fileStat = await stat4(resolvedPath);
471
- const hash = await new Promise((resolve3, reject) => {
432
+ const hash = await new Promise((resolve2, reject) => {
472
433
  const hasher = createHash2("sha256");
473
434
  const stream = createReadStream2(resolvedPath);
474
435
  stream.on("data", (chunk) => hasher.update(chunk));
475
- stream.on("end", () => resolve3(hasher.digest("hex")));
436
+ stream.on("end", () => resolve2(hasher.digest("hex")));
476
437
  stream.on("error", reject);
477
438
  });
478
439
  return {
@@ -494,6 +455,7 @@ var sha256Handler = (async (args) => {
494
455
 
495
456
  // fs/deletePath.ts
496
457
  import { rm, unlink, rmdir, stat as stat5 } from "fs/promises";
458
+ import { resolveSandboxedPath as resolveSandboxedPath6 } from "@easynet/agent-tool";
497
459
  var deletePathHandler = (async (args) => {
498
460
  const ctx = getBuiltinContext();
499
461
  const inputPath = (args.path ?? args.filePath)?.trim();
@@ -507,7 +469,7 @@ var deletePathHandler = (async (args) => {
507
469
  "Deletion not confirmed. Set confirm=true to proceed with deletion."
508
470
  );
509
471
  }
510
- const resolvedPath = await resolveSandboxedPath(inputPath, ctx.config.sandboxRoot);
472
+ const resolvedPath = await resolveSandboxedPath6(inputPath, ctx.config.sandboxRoot);
511
473
  let realSandboxRoot;
512
474
  try {
513
475
  const { realpath: rp } = await import("fs/promises");
@@ -546,172 +508,25 @@ var deletePathHandler = (async (args) => {
546
508
  };
547
509
  });
548
510
 
549
- // security/ssrf.ts
550
- import { lookup } from "dns/promises";
551
- import { createTaggedError as createTaggedError4 } from "@easynet/agent-tool";
552
- async function validateUrl(url, allowedHosts, blockedCidrs) {
553
- let parsed;
554
- try {
555
- parsed = new URL(url);
556
- } catch {
557
- throw createTaggedError4(
558
- "HTTP_DISALLOWED_HOST",
559
- `Invalid URL: ${url}`,
560
- { url }
561
- );
562
- }
563
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
564
- throw createTaggedError4(
565
- "HTTP_DISALLOWED_HOST",
566
- `Protocol not allowed: ${parsed.protocol}. Only http: and https: are supported.`,
567
- { url, protocol: parsed.protocol }
568
- );
569
- }
570
- const hostname = parsed.hostname;
571
- if (!isHostAllowed(hostname, allowedHosts)) {
572
- throw createTaggedError4(
573
- "HTTP_DISALLOWED_HOST",
574
- `Host "${hostname}" is not in the allowed hosts list`,
575
- { url, hostname, allowedHosts }
576
- );
577
- }
578
- try {
579
- const { address } = await lookup(hostname);
580
- if (isIpInBlockedCidrs(address, blockedCidrs)) {
581
- throw createTaggedError4(
582
- "HTTP_DISALLOWED_HOST",
583
- `Host "${hostname}" resolves to blocked IP: ${address}`,
584
- { url, hostname, resolvedIp: address }
585
- );
586
- }
587
- } catch (err) {
588
- if (err instanceof Error && err.kind === "HTTP_DISALLOWED_HOST") {
589
- throw err;
590
- }
591
- throw createTaggedError4(
592
- "HTTP_DISALLOWED_HOST",
593
- `DNS resolution failed for host "${hostname}": ${err instanceof Error ? err.message : String(err)}`,
594
- { url, hostname }
595
- );
596
- }
597
- return parsed;
598
- }
599
- function isHostAllowed(hostname, allowedHosts) {
600
- for (const pattern of allowedHosts) {
601
- if (pattern.startsWith("*.")) {
602
- const suffix = pattern.slice(1);
603
- if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {
604
- return true;
605
- }
606
- } else if (hostname === pattern) {
607
- return true;
608
- }
609
- }
610
- return false;
611
- }
612
- function isIpInBlockedCidrs(ip, cidrs) {
613
- const normalizedIp = normalizeIp(ip);
614
- if (!normalizedIp) return false;
615
- for (const cidr of cidrs) {
616
- if (cidr.includes(":")) {
617
- if (!ip.includes(":")) continue;
618
- if (isIpv6InCidr(ip, cidr)) return true;
619
- } else {
620
- if (isIpv4InCidr(normalizedIp, cidr)) return true;
621
- }
622
- }
623
- return false;
624
- }
625
- function normalizeIp(ip) {
626
- if (ip.startsWith("::ffff:")) {
627
- return ip.slice(7);
628
- }
629
- if (/^\d+\.\d+\.\d+\.\d+$/.test(ip)) {
630
- return ip;
631
- }
632
- return null;
633
- }
634
- function isIpv4InCidr(ip, cidr) {
635
- const [cidrIp, prefixStr] = cidr.split("/");
636
- if (!cidrIp || !prefixStr) return false;
637
- const prefix = parseInt(prefixStr, 10);
638
- if (isNaN(prefix) || prefix < 0 || prefix > 32) return false;
639
- const ipNum = ipv4ToNum(ip);
640
- const cidrNum = ipv4ToNum(cidrIp);
641
- if (ipNum === null || cidrNum === null) return false;
642
- const mask = prefix === 0 ? 0 : ~0 << 32 - prefix >>> 0;
643
- return (ipNum & mask) === (cidrNum & mask);
644
- }
645
- function ipv4ToNum(ip) {
646
- const parts = ip.split(".");
647
- if (parts.length !== 4) return null;
648
- let num = 0;
649
- for (const part of parts) {
650
- const n = parseInt(part, 10);
651
- if (isNaN(n) || n < 0 || n > 255) return null;
652
- num = num << 8 | n;
653
- }
654
- return num >>> 0;
655
- }
656
- function isIpv6InCidr(ip, cidr) {
657
- const [cidrIp, prefixStr] = cidr.split("/");
658
- if (!cidrIp || !prefixStr) return false;
659
- const prefix = parseInt(prefixStr, 10);
660
- if (isNaN(prefix)) return false;
661
- const ipBytes = expandIpv6(ip);
662
- const cidrBytes = expandIpv6(cidrIp);
663
- if (!ipBytes || !cidrBytes) return false;
664
- const fullBytes = Math.floor(prefix / 8);
665
- for (let i = 0; i < fullBytes && i < 16; i++) {
666
- if (ipBytes[i] !== cidrBytes[i]) return false;
667
- }
668
- const remainingBits = prefix % 8;
669
- if (remainingBits > 0 && fullBytes < 16) {
670
- const mask = ~0 << 8 - remainingBits & 255;
671
- if ((ipBytes[fullBytes] & mask) !== (cidrBytes[fullBytes] & mask)) return false;
672
- }
673
- return true;
674
- }
675
- function expandIpv6(ip) {
676
- const zoneIdx = ip.indexOf("%");
677
- if (zoneIdx !== -1) ip = ip.slice(0, zoneIdx);
678
- const parts = ip.split("::");
679
- if (parts.length > 2) return null;
680
- const bytes = new Array(16).fill(0);
681
- const expandGroup = (group) => {
682
- if (!group) return [];
683
- return group.split(":").flatMap((hex) => {
684
- const val = parseInt(hex || "0", 16);
685
- return [val >> 8 & 255, val & 255];
686
- });
687
- };
688
- if (parts.length === 1) {
689
- const expanded = expandGroup(parts[0]);
690
- if (expanded.length !== 16) return null;
691
- return expanded;
692
- }
693
- const left = expandGroup(parts[0]);
694
- const right = expandGroup(parts[1]);
695
- if (left.length + right.length > 16) return null;
696
- for (let i = 0; i < left.length; i++) bytes[i] = left[i];
697
- for (let i = 0; i < right.length; i++) bytes[16 - right.length + i] = right[i];
698
- return bytes;
699
- }
700
-
701
511
  // http/fetchText.ts
702
- import { createTaggedError as createTaggedError5 } from "@easynet/agent-tool";
512
+ import { validateUrl } from "@easynet/agent-tool";
513
+ import { createTaggedError as createTaggedError3 } from "@easynet/agent-tool";
703
514
  var fetchTextHandler = (async (args) => {
704
515
  const ctx = getBuiltinContext();
705
516
  const url = (args.url ?? args.uri)?.trim();
706
517
  if (!url) {
707
- throw createTaggedError5("HTTP_INVALID", "url is required (pass 'url' or 'uri')", {});
518
+ throw createTaggedError3("HTTP_INVALID", "url is required (pass 'url' or 'uri')", {});
708
519
  }
709
520
  const method = args.method ?? "GET";
710
521
  const headers = args.headers ?? {};
711
522
  const body = args.body ?? void 0;
712
523
  const timeoutMs = args.timeoutMs ?? ctx.config.defaultTimeoutMs;
713
524
  const maxBytes = args.maxBytes ?? ctx.config.maxHttpBytes;
714
- await validateUrl(url, ctx.config.allowedHosts, ctx.config.blockedCidrs);
525
+ await validateUrl(url, {
526
+ allowedHosts: ctx.config.allowedHosts,
527
+ blockedHosts: ctx.config.blockedHosts,
528
+ blockedCidrs: ctx.config.blockedCidrs
529
+ });
715
530
  if (!headers["User-Agent"] && !headers["user-agent"]) {
716
531
  headers["User-Agent"] = ctx.config.httpUserAgent;
717
532
  }
@@ -727,13 +542,13 @@ var fetchTextHandler = (async (args) => {
727
542
  });
728
543
  } catch (err) {
729
544
  if (err instanceof Error && err.name === "AbortError") {
730
- throw createTaggedError5(
545
+ throw createTaggedError3(
731
546
  "HTTP_TIMEOUT",
732
547
  `Request to ${url} timed out after ${timeoutMs}ms`,
733
548
  { url, timeoutMs }
734
549
  );
735
550
  }
736
- throw createTaggedError5(
551
+ throw createTaggedError3(
737
552
  "UPSTREAM_ERROR",
738
553
  `Fetch failed for ${url}: ${err instanceof Error ? err.message : String(err)}`,
739
554
  { url }
@@ -743,7 +558,7 @@ var fetchTextHandler = (async (args) => {
743
558
  }
744
559
  const contentLength = response.headers.get("content-length");
745
560
  if (contentLength && parseInt(contentLength, 10) > maxBytes) {
746
- throw createTaggedError5(
561
+ throw createTaggedError3(
747
562
  "HTTP_TOO_LARGE",
748
563
  `Response Content-Length ${contentLength} exceeds limit of ${maxBytes} bytes`,
749
564
  { url, contentLength: parseInt(contentLength, 10), limit: maxBytes }
@@ -788,7 +603,7 @@ async function readResponseWithLimit(response, maxBytes, url) {
788
603
  totalBytes += value.byteLength;
789
604
  if (totalBytes > maxBytes) {
790
605
  reader.cancel();
791
- throw createTaggedError5(
606
+ throw createTaggedError3(
792
607
  "HTTP_TOO_LARGE",
793
608
  `Response body exceeded limit of ${maxBytes} bytes while reading from ${url}`,
794
609
  { url, bytesRead: totalBytes, limit: maxBytes }
@@ -804,19 +619,24 @@ async function readResponseWithLimit(response, maxBytes, url) {
804
619
  }
805
620
 
806
621
  // http/fetchJson.ts
807
- import { createTaggedError as createTaggedError6 } from "@easynet/agent-tool";
622
+ import { validateUrl as validateUrl2 } from "@easynet/agent-tool";
623
+ import { createTaggedError as createTaggedError4 } from "@easynet/agent-tool";
808
624
  var fetchJsonHandler = (async (args) => {
809
625
  const ctx = getBuiltinContext();
810
626
  const url = (args.url ?? args.uri)?.trim();
811
627
  if (!url) {
812
- throw createTaggedError6("HTTP_INVALID", "url is required (pass 'url' or 'uri')", {});
628
+ throw createTaggedError4("HTTP_INVALID", "url is required (pass 'url' or 'uri')", {});
813
629
  }
814
630
  const method = args.method ?? "GET";
815
631
  const headers = args.headers ?? {};
816
632
  const body = args.body ?? void 0;
817
633
  const timeoutMs = args.timeoutMs ?? ctx.config.defaultTimeoutMs;
818
634
  const maxBytes = args.maxBytes ?? ctx.config.maxHttpBytes;
819
- await validateUrl(url, ctx.config.allowedHosts, ctx.config.blockedCidrs);
635
+ await validateUrl2(url, {
636
+ allowedHosts: ctx.config.allowedHosts,
637
+ blockedHosts: ctx.config.blockedHosts,
638
+ blockedCidrs: ctx.config.blockedCidrs
639
+ });
820
640
  if (!headers["Accept"] && !headers["accept"]) {
821
641
  headers["Accept"] = "application/json";
822
642
  }
@@ -835,13 +655,13 @@ var fetchJsonHandler = (async (args) => {
835
655
  });
836
656
  } catch (err) {
837
657
  if (err instanceof Error && err.name === "AbortError") {
838
- throw createTaggedError6(
658
+ throw createTaggedError4(
839
659
  "HTTP_TIMEOUT",
840
660
  `Request to ${url} timed out after ${timeoutMs}ms`,
841
661
  { url, timeoutMs }
842
662
  );
843
663
  }
844
- throw createTaggedError6(
664
+ throw createTaggedError4(
845
665
  "UPSTREAM_ERROR",
846
666
  `Fetch failed for ${url}: ${err instanceof Error ? err.message : String(err)}`,
847
667
  { url }
@@ -851,7 +671,7 @@ var fetchJsonHandler = (async (args) => {
851
671
  }
852
672
  const contentLength = response.headers.get("content-length");
853
673
  if (contentLength && parseInt(contentLength, 10) > maxBytes) {
854
- throw createTaggedError6(
674
+ throw createTaggedError4(
855
675
  "HTTP_TOO_LARGE",
856
676
  `Response Content-Length ${contentLength} exceeds limit of ${maxBytes} bytes`,
857
677
  { url, contentLength: parseInt(contentLength, 10), limit: maxBytes }
@@ -860,7 +680,7 @@ var fetchJsonHandler = (async (args) => {
860
680
  const text = await response.text();
861
681
  const bytes = Buffer.byteLength(text, "utf-8");
862
682
  if (bytes > maxBytes) {
863
- throw createTaggedError6(
683
+ throw createTaggedError4(
864
684
  "HTTP_TOO_LARGE",
865
685
  `Response body ${bytes} bytes exceeds limit of ${maxBytes} bytes`,
866
686
  { url, bytes, limit: maxBytes }
@@ -870,7 +690,7 @@ var fetchJsonHandler = (async (args) => {
870
690
  try {
871
691
  json = JSON.parse(text);
872
692
  } catch {
873
- throw createTaggedError6(
693
+ throw createTaggedError4(
874
694
  "UPSTREAM_ERROR",
875
695
  `Failed to parse JSON response from ${url}: ${text.slice(0, 200)}`,
876
696
  { url, status: response.status, textPreview: text.slice(0, 500) }
@@ -897,28 +717,33 @@ var fetchJsonHandler = (async (args) => {
897
717
  // http/downloadFile.ts
898
718
  import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
899
719
  import { createHash as createHash3 } from "crypto";
900
- import { dirname as dirname3 } from "path";
901
- import { createTaggedError as createTaggedError7 } from "@easynet/agent-tool";
720
+ import { dirname as dirname2 } from "path";
721
+ import { validateUrl as validateUrl3, resolveSandboxedPath as resolveSandboxedPath7 } from "@easynet/agent-tool";
722
+ import { createTaggedError as createTaggedError5 } from "@easynet/agent-tool";
902
723
  var downloadFileHandler = (async (args) => {
903
724
  const ctx = getBuiltinContext();
904
725
  const url = (args.url ?? args.uri)?.trim();
905
726
  if (!url) {
906
- throw createTaggedError7("HTTP_INVALID", "url is required (pass 'url' or 'uri')", {});
727
+ throw createTaggedError5("HTTP_INVALID", "url is required (pass 'url' or 'uri')", {});
907
728
  }
908
729
  const destPath = (args.destPath ?? args.destination ?? args.filePath)?.trim();
909
730
  if (!destPath) {
910
- throw createTaggedError7("HTTP_INVALID", "destPath is required (pass 'destPath', 'destination', or 'filePath')", {});
731
+ throw createTaggedError5("HTTP_INVALID", "destPath is required (pass 'destPath', 'destination', or 'filePath')", {});
911
732
  }
912
733
  const headers = args.headers ?? {};
913
734
  const timeoutMs = args.timeoutMs ?? ctx.config.defaultTimeoutMs;
914
735
  const maxBytes = args.maxBytes ?? ctx.config.maxDownloadBytes;
915
736
  const overwrite = args.overwrite ?? false;
916
- await validateUrl(url, ctx.config.allowedHosts, ctx.config.blockedCidrs);
917
- const resolvedDest = await resolveSandboxedPath(destPath, ctx.config.sandboxRoot);
737
+ await validateUrl3(url, {
738
+ allowedHosts: ctx.config.allowedHosts,
739
+ blockedHosts: ctx.config.blockedHosts,
740
+ blockedCidrs: ctx.config.blockedCidrs
741
+ });
742
+ const resolvedDest = await resolveSandboxedPath7(destPath, ctx.config.sandboxRoot);
918
743
  if (!overwrite) {
919
- const { access: access2 } = await import("fs/promises");
744
+ const { access } = await import("fs/promises");
920
745
  try {
921
- await access2(resolvedDest);
746
+ await access(resolvedDest);
922
747
  throw new Error(
923
748
  `File already exists: ${resolvedDest}. Set overwrite=true to allow overwriting.`
924
749
  );
@@ -943,13 +768,13 @@ var downloadFileHandler = (async (args) => {
943
768
  });
944
769
  } catch (err) {
945
770
  if (err instanceof Error && err.name === "AbortError") {
946
- throw createTaggedError7(
771
+ throw createTaggedError5(
947
772
  "HTTP_TIMEOUT",
948
773
  `Download from ${url} timed out after ${timeoutMs}ms`,
949
774
  { url, timeoutMs }
950
775
  );
951
776
  }
952
- throw createTaggedError7(
777
+ throw createTaggedError5(
953
778
  "UPSTREAM_ERROR",
954
779
  `Download failed for ${url}: ${err instanceof Error ? err.message : String(err)}`,
955
780
  { url }
@@ -959,14 +784,14 @@ var downloadFileHandler = (async (args) => {
959
784
  }
960
785
  const contentLength = response.headers.get("content-length");
961
786
  if (contentLength && parseInt(contentLength, 10) > maxBytes) {
962
- throw createTaggedError7(
787
+ throw createTaggedError5(
963
788
  "HTTP_TOO_LARGE",
964
789
  `Download Content-Length ${contentLength} exceeds limit of ${maxBytes} bytes`,
965
790
  { url, contentLength: parseInt(contentLength, 10), limit: maxBytes }
966
791
  );
967
792
  }
968
793
  if (!response.body) {
969
- throw createTaggedError7("UPSTREAM_ERROR", `No response body from ${url}`, { url });
794
+ throw createTaggedError5("UPSTREAM_ERROR", `No response body from ${url}`, { url });
970
795
  }
971
796
  const reader = response.body.getReader();
972
797
  const chunks = [];
@@ -979,7 +804,7 @@ var downloadFileHandler = (async (args) => {
979
804
  totalBytes += value.byteLength;
980
805
  if (totalBytes > maxBytes) {
981
806
  reader.cancel();
982
- throw createTaggedError7(
807
+ throw createTaggedError5(
983
808
  "HTTP_TOO_LARGE",
984
809
  `Download from ${url} exceeded limit of ${maxBytes} bytes (received ${totalBytes})`,
985
810
  { url, bytesRead: totalBytes, limit: maxBytes }
@@ -992,7 +817,7 @@ var downloadFileHandler = (async (args) => {
992
817
  reader.releaseLock();
993
818
  }
994
819
  const sha256 = hasher.digest("hex");
995
- await mkdir2(dirname3(resolvedDest), { recursive: true });
820
+ await mkdir2(dirname2(resolvedDest), { recursive: true });
996
821
  const buffer = Buffer.concat(chunks);
997
822
  await writeFile2(resolvedDest, buffer);
998
823
  return {
@@ -1021,16 +846,21 @@ var downloadFileHandler = (async (args) => {
1021
846
  });
1022
847
 
1023
848
  // http/head.ts
1024
- import { createTaggedError as createTaggedError8 } from "@easynet/agent-tool";
849
+ import { validateUrl as validateUrl4 } from "@easynet/agent-tool";
850
+ import { createTaggedError as createTaggedError6 } from "@easynet/agent-tool";
1025
851
  var headHandler = (async (args) => {
1026
852
  const ctx = getBuiltinContext();
1027
853
  const url = (args.url ?? args.uri)?.trim();
1028
854
  if (!url) {
1029
- throw createTaggedError8("HTTP_INVALID", "url is required (pass 'url' or 'uri')", {});
855
+ throw createTaggedError6("HTTP_INVALID", "url is required (pass 'url' or 'uri')", {});
1030
856
  }
1031
857
  const headers = args.headers ?? {};
1032
858
  const timeoutMs = args.timeoutMs ?? ctx.config.defaultTimeoutMs;
1033
- await validateUrl(url, ctx.config.allowedHosts, ctx.config.blockedCidrs);
859
+ await validateUrl4(url, {
860
+ allowedHosts: ctx.config.allowedHosts,
861
+ blockedHosts: ctx.config.blockedHosts,
862
+ blockedCidrs: ctx.config.blockedCidrs
863
+ });
1034
864
  if (!headers["User-Agent"] && !headers["user-agent"]) {
1035
865
  headers["User-Agent"] = ctx.config.httpUserAgent;
1036
866
  }
@@ -1045,13 +875,13 @@ var headHandler = (async (args) => {
1045
875
  });
1046
876
  } catch (err) {
1047
877
  if (err instanceof Error && err.name === "AbortError") {
1048
- throw createTaggedError8(
878
+ throw createTaggedError6(
1049
879
  "HTTP_TIMEOUT",
1050
880
  `HEAD request to ${url} timed out after ${timeoutMs}ms`,
1051
881
  { url, timeoutMs }
1052
882
  );
1053
883
  }
1054
- throw createTaggedError8(
884
+ throw createTaggedError6(
1055
885
  "UPSTREAM_ERROR",
1056
886
  `HEAD request failed for ${url}: ${err instanceof Error ? err.message : String(err)}`,
1057
887
  { url }
@@ -1081,18 +911,23 @@ var headHandler = (async (args) => {
1081
911
  });
1082
912
 
1083
913
  // http/duckduckgoSearch.ts
1084
- import { createTaggedError as createTaggedError9 } from "@easynet/agent-tool";
914
+ import { validateUrl as validateUrl5 } from "@easynet/agent-tool";
915
+ import { createTaggedError as createTaggedError7 } from "@easynet/agent-tool";
1085
916
  var DUCKDUCKGO_API = "https://api.duckduckgo.com/";
1086
917
  var duckduckgoSearchHandler = (async (args) => {
1087
918
  const ctx = getBuiltinContext();
1088
919
  const query = (args.query ?? args.q)?.trim();
1089
920
  if (!query) {
1090
- throw createTaggedError9("DUCKDUCKGO_INVALID", "query is required (pass 'query' or 'q')", {});
921
+ throw createTaggedError7("DUCKDUCKGO_INVALID", "query is required (pass 'query' or 'q')", {});
1091
922
  }
1092
923
  const timeoutMs = args.timeoutMs ?? ctx.config.defaultTimeoutMs;
1093
924
  const maxResults = args.maxResults ?? 10;
1094
925
  const url = `${DUCKDUCKGO_API}?q=${encodeURIComponent(query)}&format=json`;
1095
- await validateUrl(url, ctx.config.allowedHosts, ctx.config.blockedCidrs);
926
+ await validateUrl5(url, {
927
+ allowedHosts: ctx.config.allowedHosts,
928
+ blockedHosts: ctx.config.blockedHosts,
929
+ blockedCidrs: ctx.config.blockedCidrs
930
+ });
1096
931
  const controller = new AbortController();
1097
932
  const timer = setTimeout(() => controller.abort(), timeoutMs);
1098
933
  let response;
@@ -1105,13 +940,13 @@ var duckduckgoSearchHandler = (async (args) => {
1105
940
  } catch (err) {
1106
941
  clearTimeout(timer);
1107
942
  if (err instanceof Error && err.name === "AbortError") {
1108
- throw createTaggedError9(
943
+ throw createTaggedError7(
1109
944
  "HTTP_TIMEOUT",
1110
945
  `DuckDuckGo search timed out after ${timeoutMs}ms`,
1111
946
  { query, timeoutMs }
1112
947
  );
1113
948
  }
1114
- throw createTaggedError9(
949
+ throw createTaggedError7(
1115
950
  "UPSTREAM_ERROR",
1116
951
  `DuckDuckGo search failed: ${err instanceof Error ? err.message : String(err)}`,
1117
952
  { query }
@@ -1122,7 +957,7 @@ var duckduckgoSearchHandler = (async (args) => {
1122
957
  const text = await response.text();
1123
958
  const bytes = Buffer.byteLength(text, "utf-8");
1124
959
  if (bytes > maxBytes) {
1125
- throw createTaggedError9(
960
+ throw createTaggedError7(
1126
961
  "HTTP_TOO_LARGE",
1127
962
  `DuckDuckGo response ${bytes} bytes exceeds limit of ${maxBytes} bytes`,
1128
963
  { query, bytes, limit: maxBytes }
@@ -1132,7 +967,7 @@ var duckduckgoSearchHandler = (async (args) => {
1132
967
  try {
1133
968
  raw = JSON.parse(text);
1134
969
  } catch {
1135
- throw createTaggedError9(
970
+ throw createTaggedError7(
1136
971
  "UPSTREAM_ERROR",
1137
972
  `DuckDuckGo returned invalid JSON`,
1138
973
  { query, textPreview: text.slice(0, 200) }
@@ -1183,7 +1018,8 @@ var duckduckgoSearchHandler = (async (args) => {
1183
1018
 
1184
1019
  // http/fetchPageMainContent.ts
1185
1020
  import { parse } from "node-html-parser";
1186
- import { createTaggedError as createTaggedError10 } from "@easynet/agent-tool";
1021
+ import { validateUrl as validateUrl6 } from "@easynet/agent-tool";
1022
+ import { createTaggedError as createTaggedError8 } from "@easynet/agent-tool";
1187
1023
  var MAIN_SELECTORS = [
1188
1024
  "main",
1189
1025
  "article",
@@ -1226,11 +1062,15 @@ var fetchPageMainContentHandler = (async (args) => {
1226
1062
  const ctx = getBuiltinContext();
1227
1063
  const url = (args.url ?? args.uri)?.trim();
1228
1064
  if (!url) {
1229
- throw createTaggedError10("HTTP_INVALID", "url is required (pass 'url' or 'uri')", {});
1065
+ throw createTaggedError8("HTTP_INVALID", "url is required (pass 'url' or 'uri')", {});
1230
1066
  }
1231
1067
  const timeoutMs = args.timeoutMs ?? ctx.config.defaultTimeoutMs;
1232
1068
  const maxBytes = args.maxBytes ?? ctx.config.maxHttpBytes;
1233
- await validateUrl(url, ctx.config.allowedHosts, ctx.config.blockedCidrs);
1069
+ await validateUrl6(url, {
1070
+ allowedHosts: ctx.config.allowedHosts,
1071
+ blockedHosts: ctx.config.blockedHosts,
1072
+ blockedCidrs: ctx.config.blockedCidrs
1073
+ });
1234
1074
  const controller = new AbortController();
1235
1075
  const timer = setTimeout(() => controller.abort(), timeoutMs);
1236
1076
  let response;
@@ -1243,13 +1083,13 @@ var fetchPageMainContentHandler = (async (args) => {
1243
1083
  } catch (err) {
1244
1084
  clearTimeout(timer);
1245
1085
  if (err instanceof Error && err.name === "AbortError") {
1246
- throw createTaggedError10(
1086
+ throw createTaggedError8(
1247
1087
  "HTTP_TIMEOUT",
1248
1088
  `Request to ${url} timed out after ${timeoutMs}ms`,
1249
1089
  { url, timeoutMs }
1250
1090
  );
1251
1091
  }
1252
- throw createTaggedError10(
1092
+ throw createTaggedError8(
1253
1093
  "UPSTREAM_ERROR",
1254
1094
  `Fetch failed for ${url}: ${err instanceof Error ? err.message : String(err)}`,
1255
1095
  { url }
@@ -1258,7 +1098,7 @@ var fetchPageMainContentHandler = (async (args) => {
1258
1098
  clearTimeout(timer);
1259
1099
  const contentLength = response.headers.get("content-length");
1260
1100
  if (contentLength && parseInt(contentLength, 10) > maxBytes) {
1261
- throw createTaggedError10(
1101
+ throw createTaggedError8(
1262
1102
  "HTTP_TOO_LARGE",
1263
1103
  `Response Content-Length ${contentLength} exceeds limit of ${maxBytes} bytes`,
1264
1104
  { url, contentLength: parseInt(contentLength, 10), limit: maxBytes }
@@ -1267,7 +1107,7 @@ var fetchPageMainContentHandler = (async (args) => {
1267
1107
  const rawText = await response.text();
1268
1108
  const rawBytes = Buffer.byteLength(rawText, "utf-8");
1269
1109
  if (rawBytes > maxBytes) {
1270
- throw createTaggedError10(
1110
+ throw createTaggedError8(
1271
1111
  "HTTP_TOO_LARGE",
1272
1112
  `Response body ${rawBytes} bytes exceeds limit of ${maxBytes} bytes`,
1273
1113
  { url, bytes: rawBytes, limit: maxBytes }
@@ -1310,14 +1150,15 @@ var fetchPageMainContentHandler = (async (args) => {
1310
1150
  });
1311
1151
 
1312
1152
  // http/yahooFinance.ts
1313
- import { createTaggedError as createTaggedError11 } from "@easynet/agent-tool";
1153
+ import { validateUrl as validateUrl7 } from "@easynet/agent-tool";
1154
+ import { createTaggedError as createTaggedError9 } from "@easynet/agent-tool";
1314
1155
  var YAHOO_CHART_BASE = "https://query1.finance.yahoo.com/v8/finance/chart/";
1315
1156
  var yahooFinanceQuoteHandler = (async (args) => {
1316
1157
  const ctx = getBuiltinContext();
1317
1158
  const rawSymbols = args.symbols ?? args.symbol ?? args.tickers;
1318
1159
  const symbols = Array.isArray(rawSymbols) ? rawSymbols.map((s) => String(s).trim().toUpperCase()).filter(Boolean) : rawSymbols != null ? [String(rawSymbols).trim().toUpperCase()].filter(Boolean) : [];
1319
1160
  if (symbols.length === 0) {
1320
- throw createTaggedError11(
1161
+ throw createTaggedError9(
1321
1162
  "YAHOO_INVALID",
1322
1163
  "At least one symbol is required",
1323
1164
  {}
@@ -1329,7 +1170,11 @@ var yahooFinanceQuoteHandler = (async (args) => {
1329
1170
  const quotes = [];
1330
1171
  for (const symbol of symbols) {
1331
1172
  const url2 = `${YAHOO_CHART_BASE}${encodeURIComponent(symbol)}?interval=1d&range=${range}`;
1332
- await validateUrl(url2, ctx.config.allowedHosts, ctx.config.blockedCidrs);
1173
+ await validateUrl7(url2, {
1174
+ allowedHosts: ctx.config.allowedHosts,
1175
+ blockedHosts: ctx.config.blockedHosts,
1176
+ blockedCidrs: ctx.config.blockedCidrs
1177
+ });
1333
1178
  const controller = new AbortController();
1334
1179
  const timer = setTimeout(() => controller.abort(), timeoutMs);
1335
1180
  let response;
@@ -1345,13 +1190,13 @@ var yahooFinanceQuoteHandler = (async (args) => {
1345
1190
  } catch (err) {
1346
1191
  clearTimeout(timer);
1347
1192
  if (err instanceof Error && err.name === "AbortError") {
1348
- throw createTaggedError11(
1193
+ throw createTaggedError9(
1349
1194
  "HTTP_TIMEOUT",
1350
1195
  `Yahoo Finance request for ${symbol} timed out after ${timeoutMs}ms`,
1351
1196
  { symbol, timeoutMs }
1352
1197
  );
1353
1198
  }
1354
- throw createTaggedError11(
1199
+ throw createTaggedError9(
1355
1200
  "UPSTREAM_ERROR",
1356
1201
  `Yahoo Finance request failed: ${err instanceof Error ? err.message : String(err)}`,
1357
1202
  { symbol }
@@ -1361,7 +1206,7 @@ var yahooFinanceQuoteHandler = (async (args) => {
1361
1206
  const text = await response.text();
1362
1207
  const bytes = Buffer.byteLength(text, "utf-8");
1363
1208
  if (bytes > maxBytes) {
1364
- throw createTaggedError11(
1209
+ throw createTaggedError9(
1365
1210
  "HTTP_TOO_LARGE",
1366
1211
  `Yahoo Finance response ${bytes} bytes exceeds limit of ${maxBytes} bytes`,
1367
1212
  { symbol, bytes, limit: maxBytes }
@@ -1371,7 +1216,7 @@ var yahooFinanceQuoteHandler = (async (args) => {
1371
1216
  try {
1372
1217
  data = JSON.parse(text);
1373
1218
  } catch {
1374
- throw createTaggedError11(
1219
+ throw createTaggedError9(
1375
1220
  "UPSTREAM_ERROR",
1376
1221
  "Yahoo Finance returned invalid JSON",
1377
1222
  { symbol, textPreview: text.slice(0, 200) }
@@ -1379,7 +1224,7 @@ var yahooFinanceQuoteHandler = (async (args) => {
1379
1224
  }
1380
1225
  const error = data.chart?.error;
1381
1226
  if (error?.description) {
1382
- throw createTaggedError11(
1227
+ throw createTaggedError9(
1383
1228
  "YAHOO_ERROR",
1384
1229
  `Yahoo Finance error for ${symbol}: ${error.description}`,
1385
1230
  { symbol, code: error.code }
@@ -1592,12 +1437,13 @@ var templateRenderHandler = (async (args) => {
1592
1437
  // exec/runCommand.ts
1593
1438
  import { spawn } from "child_process";
1594
1439
  import { resolve as pathResolve } from "path";
1595
- import { createTaggedError as createTaggedError12 } from "@easynet/agent-tool";
1440
+ import { resolveSandboxedPath as resolveSandboxedPath8 } from "@easynet/agent-tool";
1441
+ import { createTaggedError as createTaggedError10 } from "@easynet/agent-tool";
1596
1442
  var runCommandHandler = (async (args) => {
1597
1443
  const ctx = getBuiltinContext();
1598
1444
  const { allowedCommands, maxCommandOutputBytes, commandTimeoutMs } = ctx.config;
1599
1445
  if (!allowedCommands.length) {
1600
- throw createTaggedError12(
1446
+ throw createTaggedError10(
1601
1447
  "EXEC_DISABLED",
1602
1448
  "Exec is disabled: allowedCommands is empty",
1603
1449
  {}
@@ -1605,18 +1451,18 @@ var runCommandHandler = (async (args) => {
1605
1451
  }
1606
1452
  const rawCommand = (args.command ?? args.cmd)?.trim();
1607
1453
  if (!rawCommand) {
1608
- throw createTaggedError12("EXEC_INVALID", "command is required (pass 'command' or 'cmd')", {});
1454
+ throw createTaggedError10("EXEC_INVALID", "command is required (pass 'command' or 'cmd')", {});
1609
1455
  }
1610
1456
  const baseName = rawCommand.replace(/^.*\//, "").trim();
1611
1457
  if (baseName !== rawCommand || /[;&|$`\s]/.test(rawCommand)) {
1612
- throw createTaggedError12(
1458
+ throw createTaggedError10(
1613
1459
  "EXEC_INVALID",
1614
1460
  "command must be a single executable name (no path, no shell chars)",
1615
1461
  { command: rawCommand }
1616
1462
  );
1617
1463
  }
1618
1464
  if (!allowedCommands.includes(baseName)) {
1619
- throw createTaggedError12(
1465
+ throw createTaggedError10(
1620
1466
  "EXEC_NOT_ALLOWED",
1621
1467
  `Command "${baseName}" is not in allowedCommands`,
1622
1468
  { command: baseName, allowed: allowedCommands }
@@ -1626,7 +1472,7 @@ var runCommandHandler = (async (args) => {
1626
1472
  const timeoutMs = args.timeoutMs ?? commandTimeoutMs;
1627
1473
  let cwd = pathResolve(ctx.config.sandboxRoot);
1628
1474
  if (args.cwd != null && args.cwd !== "") {
1629
- cwd = await resolveSandboxedPath(args.cwd, ctx.config.sandboxRoot);
1475
+ cwd = await resolveSandboxedPath8(args.cwd, ctx.config.sandboxRoot);
1630
1476
  }
1631
1477
  return new Promise((resolvePromise, rejectPromise) => {
1632
1478
  const proc = spawn(baseName, cmdArgs, {
@@ -1643,7 +1489,7 @@ var runCommandHandler = (async (args) => {
1643
1489
  if (totalBytes + len > maxCommandOutputBytes) {
1644
1490
  proc.kill("SIGKILL");
1645
1491
  rejectPromise(
1646
- createTaggedError12(
1492
+ createTaggedError10(
1647
1493
  "EXEC_OUTPUT_TOO_LARGE",
1648
1494
  `Command output exceeded ${maxCommandOutputBytes} bytes`,
1649
1495
  { maxBytes: maxCommandOutputBytes }
@@ -1681,7 +1527,7 @@ var runCommandHandler = (async (args) => {
1681
1527
  proc.on("error", (err) => {
1682
1528
  clearTimeout(timeout);
1683
1529
  rejectPromise(
1684
- createTaggedError12("EXEC_SPAWN_ERROR", err.message, { command: baseName })
1530
+ createTaggedError10("EXEC_SPAWN_ERROR", err.message, { command: baseName })
1685
1531
  );
1686
1532
  });
1687
1533
  proc.on("close", (code, signal) => {
@@ -1915,7 +1761,8 @@ var CORE_GROUP_PREFIX = {
1915
1761
  function registerCoreTools(registry, userConfig, options) {
1916
1762
  const config = {
1917
1763
  ...DEFAULT_CORE_TOOLS_CONFIG,
1918
- ...userConfig
1764
+ ...userConfig,
1765
+ blockedHosts: userConfig.blockedHosts ?? []
1919
1766
  };
1920
1767
  const adapter = new CoreAdapter(config);
1921
1768
  const onlySet = options?.only?.length ? new Set(options.only) : null;
@@ -1932,6 +1779,9 @@ function registerCoreTools(registry, userConfig, options) {
1932
1779
  }
1933
1780
  return adapter;
1934
1781
  }
1782
+
1783
+ // index.ts
1784
+ import { resolveSandboxedPath as resolveSandboxedPath9, validateUrl as validateUrl8, isIpInBlockedCidrs } from "@easynet/agent-tool";
1935
1785
  export {
1936
1786
  CoreAdapter,
1937
1787
  DEFAULT_CORE_TOOLS_CONFIG,
@@ -1951,14 +1801,14 @@ export {
1951
1801
  nowSpec,
1952
1802
  readTextSpec,
1953
1803
  registerCoreTools,
1954
- resolveSandboxedPath,
1804
+ resolveSandboxedPath9 as resolveSandboxedPath,
1955
1805
  runCommandSpec,
1956
1806
  runWithBuiltinContext,
1957
1807
  searchTextSpec,
1958
1808
  sha256Spec,
1959
1809
  templateRenderSpec,
1960
1810
  truncateSpec,
1961
- validateUrl,
1811
+ validateUrl8 as validateUrl,
1962
1812
  writeTextSpec,
1963
1813
  yahooFinanceQuoteSpec
1964
1814
  };