@wrongstack/tools 0.313.0 → 0.313.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.
Files changed (134) hide show
  1. package/dist/audit.js +10 -4055
  2. package/dist/bash.js +9 -2308
  3. package/dist/batch-tool-use.js +2 -187
  4. package/dist/browser/index.js +25 -1339
  5. package/dist/builtin.js +69 -33949
  6. package/dist/chunk-2CTB3UK5.js +197 -0
  7. package/dist/chunk-2ME7ZWDD.js +1272 -0
  8. package/dist/chunk-2TFQDEPU.js +113 -0
  9. package/dist/chunk-2ZCJNKFE.js +315 -0
  10. package/dist/chunk-3PVYQFRQ.js +472 -0
  11. package/dist/chunk-3QMQBO5Z.js +89 -0
  12. package/dist/chunk-42AKKX7O.js +385 -0
  13. package/dist/chunk-42SKEETB.js +113 -0
  14. package/dist/chunk-4BHUSMNL.js +104 -0
  15. package/dist/chunk-4I4LWFOO.js +431 -0
  16. package/dist/chunk-553IQUMO.js +478 -0
  17. package/dist/chunk-55KF77OK.js +1944 -0
  18. package/dist/chunk-6HLZK37X.js +359 -0
  19. package/dist/chunk-6RNIFLG4.js +305 -0
  20. package/dist/chunk-A6BRWIXN.js +317 -0
  21. package/dist/chunk-AHHDWNXC.js +200 -0
  22. package/dist/chunk-B2CIOKWP.js +899 -0
  23. package/dist/chunk-BTSFTGIL.js +7 -0
  24. package/dist/chunk-D33S4RQG.js +222 -0
  25. package/dist/chunk-DIJKHSUO.js +134 -0
  26. package/dist/chunk-DPL47QGS.js +3515 -0
  27. package/dist/chunk-EMRGN4KM.js +170 -0
  28. package/dist/chunk-FGOABE4Z.js +955 -0
  29. package/dist/chunk-FW65TP4G.js +520 -0
  30. package/dist/chunk-GOIZNNOA.js +221 -0
  31. package/dist/chunk-H5MJUAA6.js +295 -0
  32. package/dist/chunk-H5OHH6F4.js +223 -0
  33. package/dist/chunk-HC5RM4WY.js +199 -0
  34. package/dist/chunk-HEQFMLZ7.js +190 -0
  35. package/dist/chunk-HFXSNKCV.js +196 -0
  36. package/dist/chunk-HXSBQ2SE.js +1723 -0
  37. package/dist/chunk-ILESNGNM.js +163 -0
  38. package/dist/chunk-INF5CGAO.js +137 -0
  39. package/dist/chunk-IWDFZ6XR.js +337 -0
  40. package/dist/chunk-IXMNTNW5.js +238 -0
  41. package/dist/chunk-J4SRJKBW.js +428 -0
  42. package/dist/chunk-J5SKD6LN.js +235 -0
  43. package/dist/chunk-JRJJX3L5.js +424 -0
  44. package/dist/chunk-KXNDHMUB.js +366 -0
  45. package/dist/chunk-LIJQMYYX.js +59 -0
  46. package/dist/chunk-MAN26PZ5.js +260 -0
  47. package/dist/chunk-MKRQMLZA.js +474 -0
  48. package/dist/chunk-NBDZMMG2.js +169 -0
  49. package/dist/chunk-NHA6BEIH.js +101 -0
  50. package/dist/chunk-OA6YJIWL.js +4412 -0
  51. package/dist/chunk-OZC255IC.js +2830 -0
  52. package/dist/chunk-PUR6WU2B.js +154 -0
  53. package/dist/chunk-Q5JPPCZC.js +12 -0
  54. package/dist/chunk-QBV725YG.js +128 -0
  55. package/dist/chunk-QHVW4AAM.js +892 -0
  56. package/dist/chunk-QNJ7AQ4B.js +1000 -0
  57. package/dist/chunk-RGISBZBD.js +231 -0
  58. package/dist/chunk-RJZQWEMS.js +84 -0
  59. package/dist/chunk-RP2ZA47V.js +15 -0
  60. package/dist/chunk-RXISBFR5.js +315 -0
  61. package/dist/chunk-S5CCLZL3.js +99 -0
  62. package/dist/chunk-TVTCSBJF.js +122 -0
  63. package/dist/chunk-U4D5S3VQ.js +21 -0
  64. package/dist/chunk-U4VAATKP.js +193 -0
  65. package/dist/chunk-UKU2ZX4C.js +203 -0
  66. package/dist/chunk-UNSKG6DC.js +159 -0
  67. package/dist/chunk-URFKEIHM.js +1091 -0
  68. package/dist/chunk-UTV7YAW7.js +178 -0
  69. package/dist/chunk-V43EQ6VR.js +68 -0
  70. package/dist/chunk-VB3MJGCV.js +610 -0
  71. package/dist/chunk-WHIIIREL.js +223 -0
  72. package/dist/chunk-XMIFYKUT.js +1371 -0
  73. package/dist/chunk-YF2RSV34.js +54 -0
  74. package/dist/chunk-Z6DAKQ3U.js +1 -0
  75. package/dist/chunk-ZIGAZFWE.js +522 -0
  76. package/dist/circuit-breaker.js +3 -191
  77. package/dist/codebase-index/index.js +84 -13688
  78. package/dist/codebase-index/parser-worker-script.js +8 -3995
  79. package/dist/codebase-index/project-server.js +60 -8548
  80. package/dist/codebase-index/worker.js +20 -8416
  81. package/dist/design.js +2 -299
  82. package/dist/diff.js +4 -256
  83. package/dist/document.js +4 -222
  84. package/dist/e2e.js +5 -476
  85. package/dist/edit.js +14 -10200
  86. package/dist/exec.js +16 -3131
  87. package/dist/fetch.js +8 -398
  88. package/dist/format.js +10 -3750
  89. package/dist/generic-parser-VUXDIAOZ.js +13 -0
  90. package/dist/git.js +4 -380
  91. package/dist/glob.js +6 -280
  92. package/dist/go-parser-6CA77WZ4.js +469 -0
  93. package/dist/grep.js +7 -604
  94. package/dist/index.js +318 -35387
  95. package/dist/install.js +10 -4091
  96. package/dist/json-parser-7YMDJSBJ.js +213 -0
  97. package/dist/json.js +4 -665
  98. package/dist/kanban.js +3 -2096
  99. package/dist/languages/index.js +23 -4664
  100. package/dist/legacy-bridge-7UKZT46G.js +18 -0
  101. package/dist/lint.js +10 -3706
  102. package/dist/logs.js +4 -287
  103. package/dist/memory.js +6 -215
  104. package/dist/mode.js +3 -99
  105. package/dist/outdated.js +4 -5170
  106. package/dist/pack.js +65 -33897
  107. package/dist/parser-dispatch-VPOFDO2U.js +14 -0
  108. package/dist/patch.js +13 -9935
  109. package/dist/plan.js +5 -3286
  110. package/dist/process-registry.js +8 -651
  111. package/dist/ps-slash.js +12 -1439
  112. package/dist/py-parser-GPM5VYRG.js +16 -0
  113. package/dist/read.js +13 -9816
  114. package/dist/replace.js +15 -10014
  115. package/dist/rs-parser-WVQ4KYCX.js +81 -0
  116. package/dist/scaffold.js +4 -253
  117. package/dist/search.js +5 -585
  118. package/dist/session-kanban.js +30 -972
  119. package/dist/skill.js +2 -215
  120. package/dist/task.js +5 -3410
  121. package/dist/test.js +10 -3773
  122. package/dist/todo.js +4 -2891
  123. package/dist/tool-help.js +3 -123
  124. package/dist/tool-search.js +3 -79
  125. package/dist/tool-tier.js +68 -33984
  126. package/dist/tool-use.js +2 -93
  127. package/dist/tree-sitter-parser-YCILHGZD.js +17 -0
  128. package/dist/tree.js +4 -288
  129. package/dist/ts-parser-YH4ZMWMH.js +232 -0
  130. package/dist/typecheck.js +10 -3753
  131. package/dist/win32.js +6 -60
  132. package/dist/write.js +14 -9882
  133. package/dist/yaml-parser-GFAOKV53.js +157 -0
  134. package/package.json +5 -5
package/dist/fetch.js CHANGED
@@ -1,401 +1,11 @@
1
- // src/fetch.ts
2
- import { FetchError as FetchError2, ToolError, ToolValidationError as ToolValidationError2 } from "@wrongstack/core/types";
3
- import TurndownService from "turndown";
4
-
5
- // src/_fetch-guard.ts
6
- import * as dns from "node:dns/promises";
7
- import * as net from "node:net";
8
- import { isPrivateIPv4, isPrivateIPv6 } from "@wrongstack/core/utils";
9
- import { FetchError, ToolValidationError } from "@wrongstack/core/types";
10
- import { Agent, fetch as undiciFetch } from "undici";
11
- var nativeGlobalFetch = globalThis.fetch;
12
- var ALLOW_PRIVATE = process.env["WRONGSTACK_FETCH_ALLOW_PRIVATE"] === "1";
13
- if (ALLOW_PRIVATE && !process.env["CI"]) {
14
- console.warn(
15
- "[WrongStack] WARNING: WRONGSTACK_FETCH_ALLOW_PRIVATE=1 is active \u2014\n fetch tool can now access private IPs (10.x, 192.168.x, 169.254.x),\n cloud metadata endpoints, and plaintext HTTP. Use only on isolated networks."
16
- );
17
- }
18
- function guardedLookup(hostname, options, callback) {
19
- dns.lookup(hostname, { all: true }).then((records) => {
20
- const family = options?.family;
21
- const byFamily = family === 4 || family === 6 ? records.filter((r) => r.family === family) : records;
22
- const list = byFamily.length > 0 ? byFamily : records;
23
- if (!ALLOW_PRIVATE) {
24
- for (const r of list) {
25
- const bad = r.family === 4 ? isPrivateIPv4(r.address) : isPrivateIPv6(r.address);
26
- if (bad) {
27
- callback(
28
- Object.assign(new Error(`fetch: resolved to private address ${r.address}`), {
29
- code: "EAI_FAIL"
30
- })
31
- );
32
- return;
33
- }
34
- }
35
- }
36
- if (options?.all) {
37
- callback(
38
- null,
39
- list.map((r) => ({ address: r.address, family: r.family }))
40
- );
41
- return;
42
- }
43
- const first = list.at(0);
44
- if (!first) {
45
- callback(
46
- Object.assign(new Error(`fetch: no address for ${hostname}`), { code: "ENOTFOUND" })
47
- );
48
- return;
49
- }
50
- callback(null, first.address, first.family);
51
- }).catch((err) => callback(err));
52
- }
53
- var pinnedAgent;
54
- function getPinnedDispatcher() {
55
- if (!pinnedAgent) {
56
- pinnedAgent = new Agent({ allowH2: false, connect: { lookup: guardedLookup } });
57
- }
58
- return pinnedAgent;
59
- }
60
- function dispatcherFetch() {
61
- return globalThis.fetch === nativeGlobalFetch ? undiciFetch : globalThis.fetch;
62
- }
63
- var _beforeExitRegistered = false;
64
- if (!_beforeExitRegistered) {
65
- _beforeExitRegistered = true;
66
- process.on("beforeExit", () => {
67
- pinnedAgent?.destroy();
68
- pinnedAgent = void 0;
69
- });
70
- }
71
- async function guardedFetch(url, maxRedirects, signal, headers = {
72
- "user-agent": "WrongStack/1.0 (+https://wrongstack.com)",
73
- accept: "text/html,application/json;q=0.9,text/plain;q=0.8,*/*;q=0.1"
74
- }) {
75
- let redirectCount = 0;
76
- let currentUrl = url;
77
- for (; ; ) {
78
- const parsed = new URL(currentUrl);
79
- if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
80
- throw new ToolValidationError({
81
- message: `fetch: redirect to unsupported protocol "${parsed.protocol}"`,
82
- field: "url"
83
- });
84
- }
85
- if (parsed.protocol === "http:" && !ALLOW_PRIVATE) {
86
- throw new ToolValidationError({
87
- message: "fetch: redirect to http:// blocked (HTTPS required by default)",
88
- field: "url"
89
- });
90
- }
91
- await assertNotPrivate(parsed.hostname);
92
- const init = {
93
- redirect: "manual",
94
- signal,
95
- headers,
96
- dispatcher: getPinnedDispatcher()
97
- };
98
- const res = await dispatcherFetch()(currentUrl, init);
99
- if (res.status < 300 || res.status > 399) {
100
- return res;
101
- }
102
- try {
103
- await res.body?.cancel();
104
- } catch {
105
- }
106
- redirectCount++;
107
- if (redirectCount > maxRedirects) {
108
- throw new FetchError({
109
- message: `fetch: exceeded ${maxRedirects} redirects`,
110
- status: res.status,
111
- context: { url: currentUrl, maxRedirects, redirectCount }
112
- });
113
- }
114
- const location = res.headers.get("location");
115
- if (!location) {
116
- throw new FetchError({
117
- message: "fetch: redirect status with no location header",
118
- status: res.status,
119
- context: { url: currentUrl, redirectCount }
120
- });
121
- }
122
- currentUrl = new URL(location, currentUrl).toString();
123
- }
124
- }
125
- async function assertNotPrivate(hostname) {
126
- if (ALLOW_PRIVATE) return;
127
- const host = hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
128
- if (host === "localhost" || host.endsWith(".localhost")) {
129
- throw new ToolValidationError({
130
- message: "fetch: blocked localhost target",
131
- field: "url"
132
- });
133
- }
134
- const ipVersion = net.isIP(host);
135
- if (ipVersion === 4) {
136
- if (isPrivateIPv4(host)) {
137
- throw new ToolValidationError({
138
- message: `fetch: blocked private/loopback address "${host}"`,
139
- field: "url"
140
- });
141
- }
142
- } else if (ipVersion === 6) {
143
- if (isPrivateIPv6(host)) {
144
- throw new ToolValidationError({
145
- message: `fetch: blocked private/loopback address "${host}"`,
146
- field: "url"
147
- });
148
- }
149
- } else {
150
- try {
151
- const records = await dns.lookup(host, { all: true });
152
- for (const r of records) {
153
- const bad = r.family === 4 ? isPrivateIPv4(r.address) : isPrivateIPv6(r.address);
154
- if (bad) {
155
- throw new ToolValidationError({
156
- message: `fetch: resolved to private address ${r.address}`,
157
- field: "url"
158
- });
159
- }
160
- }
161
- } catch (err) {
162
- if (err instanceof ToolValidationError) throw err;
163
- }
164
- }
165
- }
166
-
167
- // src/_util.ts
168
- import * as Core from "@wrongstack/core/utils";
169
- function truncateMiddle(s, max) {
170
- if (Buffer.byteLength(s, "utf8") <= max) return s;
171
- const half = Math.floor(max / 2);
172
- return s.slice(0, half) + `
173
- \u2026[truncated ${Buffer.byteLength(s, "utf8") - max} bytes from middle]\u2026
174
- ` + s.slice(-half);
175
- }
176
-
177
- // src/fetch.ts
178
- var TD = new TurndownService({
179
- // Use `# Title` for headings, not setext underline style (`Title\n=====`).
180
- headingStyle: "atx",
181
- // Don't wrap code blocks in <pre> — render them as triple-backtick blocks.
182
- codeBlockStyle: "fenced"
183
- });
184
- TD.addRule("stripDangerousElements", {
185
- filter: ["script", "style", "noscript"],
186
- replacement: () => ""
187
- });
188
- var PRUNED_BOILERPLATE_TAGS = /* @__PURE__ */ new Set(["nav", "header", "footer", "aside", "svg", "iframe"]);
189
- TD.remove((node) => PRUNED_BOILERPLATE_TAGS.has(node.nodeName.toLowerCase()));
190
- var MAX_BYTES = 131072;
191
- var TIMEOUT_MS = 2e4;
192
- var combineSignals = (signals) => AbortSignal.any(signals);
193
- var fetchTool = {
194
- name: "fetch",
195
- category: "Network",
196
- description: "Fetch a URL and return its content. HTML pages are automatically converted to clean markdown. This tool has strong SSRF protections (private IPs, localhost, and cloud metadata endpoints are blocked by default).",
197
- usageHint: "Use this when you need external information (documentation, API responses, web pages, etc.).\n\nSecurity notes:\n- Only HTTPS is allowed by default.\n- Internal/private networks are blocked unless explicitly enabled via environment variable.\n- Redirects are followed but re-validated at each hop.\n- Output is capped (128KB by default) to avoid flooding context.\nPrefer this over raw `bash curl` or `bash wget`.",
198
- permission: "auto",
199
- mutating: false,
200
- capabilities: ["net.outbound"],
201
- icon: "web",
202
- // Trust rules for fetch match on the literal URL — declare it explicitly
203
- // so a user can trust `https://api.example.com/*` without accidentally
204
- // matching that pattern on any other tool that happens to have a `url`
205
- // input field.
206
- subjectKey: "url",
207
- timeoutMs: TIMEOUT_MS,
208
- maxOutputBytes: MAX_BYTES,
209
- inputSchema: {
210
- type: "object",
211
- properties: {
212
- url: {
213
- type: "string",
214
- description: "The target URL (must use https://)."
215
- },
216
- format: {
217
- type: "string",
218
- enum: ["markdown", "text", "raw"],
219
- description: 'Output format. "markdown" is recommended for HTML pages; for non-HTML content types it falls back to plain text (JSON is pretty-printed).'
220
- }
221
- },
222
- required: ["url"]
223
- },
224
- async execute(input, ctx, opts) {
225
- let final;
226
- const executeStream = fetchTool.executeStream;
227
- if (!executeStream) {
228
- throw new ToolError({
229
- message: "fetchTool: stream execution unavailable",
230
- code: "TOOL_EXECUTION_FAILED",
231
- toolName: "fetch"
232
- });
233
- }
234
- for await (const ev of executeStream(input, ctx, opts)) {
235
- if (ev.type === "final") final = ev.output;
236
- }
237
- if (!final) {
238
- throw new ToolError({
239
- message: "fetch: stream ended without final event",
240
- code: "TOOL_EXECUTION_FAILED",
241
- toolName: "fetch"
242
- });
243
- }
244
- return final;
245
- },
246
- async *executeStream(input, ctx, opts) {
247
- if (!input?.url) {
248
- throw new ToolValidationError2({
249
- message: "fetch: url is required",
250
- field: "url"
251
- });
252
- }
253
- const u = new URL(input.url);
254
- if (u.username || u.password) {
255
- throw new ToolValidationError2({
256
- message: "fetch: URLs with embedded credentials (user:pass@host) are not allowed",
257
- field: "url"
258
- });
259
- }
260
- if (u.protocol !== "https:" && u.protocol !== "http:") {
261
- throw new ToolValidationError2({
262
- message: `fetch: unsupported protocol "${u.protocol}"`,
263
- field: "url"
264
- });
265
- }
266
- if (u.protocol === "http:" && !ALLOW_PRIVATE) {
267
- throw new ToolValidationError2({
268
- message: "fetch: http:// blocked (HTTPS required by default)",
269
- field: "url"
270
- });
271
- }
272
- await assertNotPrivate(u.hostname);
273
- yield { type: "log", text: `GET ${input.url}` };
274
- const ctrl = new AbortController();
275
- const timer = setTimeout(
276
- () => ctrl.abort(
277
- new ToolError({
278
- message: "fetch timeout",
279
- code: "TOOL_TIMEOUT",
280
- toolName: "fetch"
281
- })
282
- ),
283
- TIMEOUT_MS
284
- );
285
- const combined = combineSignals([opts.signal, ctrl.signal]);
286
- try {
287
- let res;
288
- try {
289
- res = await guardedFetch(input.url, 5, combined);
290
- } catch (err) {
291
- if (opts.signal.aborted) throw err;
292
- throw describeFetchError(err, input.url, ctrl.signal.aborted);
293
- }
294
- const ct = res.headers.get("content-type") ?? "application/octet-stream";
295
- if (/^image\/|^audio\/|^video\/|application\/octet-stream/.test(ct)) {
296
- throw new FetchError2({
297
- message: `fetch: refusing to read binary content-type "${ct}"`,
298
- status: res.status,
299
- context: { url: res.url, contentType: ct }
300
- });
301
- }
302
- yield {
303
- type: "log",
304
- text: `HTTP ${res.status} ${ct}`,
305
- data: { status: res.status, contentType: ct }
306
- };
307
- const reader = res.body?.getReader();
308
- let received = 0;
309
- const chunks = [];
310
- let pendingBytes = 0;
311
- const FLUSH_AT = 4 * 1024;
312
- if (reader) {
313
- for (; ; ) {
314
- const { value, done } = await reader.read();
315
- if (done) break;
316
- if (!value) continue;
317
- received += value.byteLength;
318
- pendingBytes += value.byteLength;
319
- chunks.push(value);
320
- if (pendingBytes >= FLUSH_AT) {
321
- const recent = Buffer.from(value).toString("utf-8");
322
- yield {
323
- type: "partial_output",
324
- text: recent,
325
- data: { received }
326
- };
327
- pendingBytes = 0;
328
- }
329
- if (received > MAX_BYTES) break;
330
- }
331
- }
332
- const text = Buffer.concat(chunks.map((c) => Buffer.from(c))).toString("utf8");
333
- const format = input.format ?? (ct.includes("text/html") ? "markdown" : "text");
334
- let content;
335
- if (format === "raw") content = text;
336
- else if (format === "markdown" && ct.includes("text/html")) content = TD.turndown(text);
337
- else if (ct.includes("application/json")) content = prettyJson(text);
338
- else content = text;
339
- yield {
340
- type: "final",
341
- output: {
342
- content: truncateMiddle(content, MAX_BYTES),
343
- status: res.status,
344
- content_type: ct,
345
- url: res.url
346
- }
347
- };
348
- ctx.recordSideEffect?.({
349
- toolUseId: `fetch-${Date.now()}`,
350
- toolName: "fetch",
351
- ts: (/* @__PURE__ */ new Date()).toISOString(),
352
- input: { url: input.url, format: input.format },
353
- outcome: `HTTP ${res.status} (${ct})`,
354
- risk: "network"
355
- });
356
- } finally {
357
- clearTimeout(timer);
358
- }
359
- }
360
- };
361
- function describeFetchError(err, url, timedOut) {
362
- if (timedOut) {
363
- return new ToolError({
364
- message: `fetch: GET ${url} timed out after ${TIMEOUT_MS}ms`,
365
- code: "TOOL_TIMEOUT",
366
- toolName: "fetch",
367
- context: { url, timedOut: true, timeoutMs: TIMEOUT_MS },
368
- cause: err
369
- });
370
- }
371
- const parts = [];
372
- const seen = /* @__PURE__ */ new Set();
373
- let cur = err;
374
- while (cur instanceof Error && !seen.has(cur)) {
375
- seen.add(cur);
376
- const code = cur.code;
377
- const label = code ? `${code}: ${cur.message}` : cur.message;
378
- if (label && label !== "fetch failed" && !parts.includes(label)) parts.push(label);
379
- cur = cur.cause;
380
- }
381
- const detail = parts.length > 0 ? parts.join(" \u2192 ") : "fetch failed";
382
- return new FetchError2({
383
- message: `fetch: GET ${url} failed \u2014 ${detail}`,
384
- status: 502,
385
- context: { url, timedOut: false, transportErrors: parts },
386
- // Preserve the original undici / DNS / TLS chain so callers can inspect
387
- // it via `err.cause` and structured `instanceof` checks. The flattened
388
- // text version stays in the message for human readability.
389
- cause: err
390
- });
391
- }
392
- function prettyJson(s) {
393
- try {
394
- return JSON.stringify(JSON.parse(s), null, 2);
395
- } catch {
396
- return s;
397
- }
398
- }
1
+ import {
2
+ fetchTool
3
+ } from "./chunk-IXMNTNW5.js";
4
+ import {
5
+ guardedFetch,
6
+ guardedLookup
7
+ } from "./chunk-NBDZMMG2.js";
8
+ import "./chunk-D33S4RQG.js";
399
9
  export {
400
10
  fetchTool,
401
11
  guardedFetch,