@wrongstack/tools 0.310.1 → 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 (135) hide show
  1. package/dist/_shell-pick.d.ts +5 -10
  2. package/dist/audit.js +10 -4055
  3. package/dist/bash.js +9 -2332
  4. package/dist/batch-tool-use.js +2 -187
  5. package/dist/browser/index.js +25 -1339
  6. package/dist/builtin.js +69 -33985
  7. package/dist/chunk-2CTB3UK5.js +197 -0
  8. package/dist/chunk-2ME7ZWDD.js +1272 -0
  9. package/dist/chunk-2TFQDEPU.js +113 -0
  10. package/dist/chunk-2ZCJNKFE.js +315 -0
  11. package/dist/chunk-3PVYQFRQ.js +472 -0
  12. package/dist/chunk-3QMQBO5Z.js +89 -0
  13. package/dist/chunk-42AKKX7O.js +385 -0
  14. package/dist/chunk-42SKEETB.js +113 -0
  15. package/dist/chunk-4BHUSMNL.js +104 -0
  16. package/dist/chunk-4I4LWFOO.js +431 -0
  17. package/dist/chunk-553IQUMO.js +478 -0
  18. package/dist/chunk-55KF77OK.js +1944 -0
  19. package/dist/chunk-6HLZK37X.js +359 -0
  20. package/dist/chunk-6RNIFLG4.js +305 -0
  21. package/dist/chunk-A6BRWIXN.js +317 -0
  22. package/dist/chunk-AHHDWNXC.js +200 -0
  23. package/dist/chunk-B2CIOKWP.js +899 -0
  24. package/dist/chunk-BTSFTGIL.js +7 -0
  25. package/dist/chunk-D33S4RQG.js +222 -0
  26. package/dist/chunk-DIJKHSUO.js +134 -0
  27. package/dist/chunk-DPL47QGS.js +3515 -0
  28. package/dist/chunk-EMRGN4KM.js +170 -0
  29. package/dist/chunk-FGOABE4Z.js +955 -0
  30. package/dist/chunk-FW65TP4G.js +520 -0
  31. package/dist/chunk-GOIZNNOA.js +221 -0
  32. package/dist/chunk-H5MJUAA6.js +295 -0
  33. package/dist/chunk-H5OHH6F4.js +223 -0
  34. package/dist/chunk-HC5RM4WY.js +199 -0
  35. package/dist/chunk-HEQFMLZ7.js +190 -0
  36. package/dist/chunk-HFXSNKCV.js +196 -0
  37. package/dist/chunk-HXSBQ2SE.js +1723 -0
  38. package/dist/chunk-ILESNGNM.js +163 -0
  39. package/dist/chunk-INF5CGAO.js +137 -0
  40. package/dist/chunk-IWDFZ6XR.js +337 -0
  41. package/dist/chunk-IXMNTNW5.js +238 -0
  42. package/dist/chunk-J4SRJKBW.js +428 -0
  43. package/dist/chunk-J5SKD6LN.js +235 -0
  44. package/dist/chunk-JRJJX3L5.js +424 -0
  45. package/dist/chunk-KXNDHMUB.js +366 -0
  46. package/dist/chunk-LIJQMYYX.js +59 -0
  47. package/dist/chunk-MAN26PZ5.js +260 -0
  48. package/dist/chunk-MKRQMLZA.js +474 -0
  49. package/dist/chunk-NBDZMMG2.js +169 -0
  50. package/dist/chunk-NHA6BEIH.js +101 -0
  51. package/dist/chunk-OA6YJIWL.js +4412 -0
  52. package/dist/chunk-OZC255IC.js +2830 -0
  53. package/dist/chunk-PUR6WU2B.js +154 -0
  54. package/dist/chunk-Q5JPPCZC.js +12 -0
  55. package/dist/chunk-QBV725YG.js +128 -0
  56. package/dist/chunk-QHVW4AAM.js +892 -0
  57. package/dist/chunk-QNJ7AQ4B.js +1000 -0
  58. package/dist/chunk-RGISBZBD.js +231 -0
  59. package/dist/chunk-RJZQWEMS.js +84 -0
  60. package/dist/chunk-RP2ZA47V.js +15 -0
  61. package/dist/chunk-RXISBFR5.js +315 -0
  62. package/dist/chunk-S5CCLZL3.js +99 -0
  63. package/dist/chunk-TVTCSBJF.js +122 -0
  64. package/dist/chunk-U4D5S3VQ.js +21 -0
  65. package/dist/chunk-U4VAATKP.js +193 -0
  66. package/dist/chunk-UKU2ZX4C.js +203 -0
  67. package/dist/chunk-UNSKG6DC.js +159 -0
  68. package/dist/chunk-URFKEIHM.js +1091 -0
  69. package/dist/chunk-UTV7YAW7.js +178 -0
  70. package/dist/chunk-V43EQ6VR.js +68 -0
  71. package/dist/chunk-VB3MJGCV.js +610 -0
  72. package/dist/chunk-WHIIIREL.js +223 -0
  73. package/dist/chunk-XMIFYKUT.js +1371 -0
  74. package/dist/chunk-YF2RSV34.js +54 -0
  75. package/dist/chunk-Z6DAKQ3U.js +1 -0
  76. package/dist/chunk-ZIGAZFWE.js +522 -0
  77. package/dist/circuit-breaker.js +3 -191
  78. package/dist/codebase-index/index.js +84 -13688
  79. package/dist/codebase-index/parser-worker-script.js +8 -3995
  80. package/dist/codebase-index/project-server.js +60 -8548
  81. package/dist/codebase-index/worker.js +20 -8416
  82. package/dist/design.js +2 -299
  83. package/dist/diff.js +4 -256
  84. package/dist/document.js +4 -222
  85. package/dist/e2e.js +5 -476
  86. package/dist/edit.js +14 -10200
  87. package/dist/exec.js +16 -3131
  88. package/dist/fetch.js +8 -398
  89. package/dist/format.js +10 -3750
  90. package/dist/generic-parser-VUXDIAOZ.js +13 -0
  91. package/dist/git.js +4 -380
  92. package/dist/glob.js +6 -280
  93. package/dist/go-parser-6CA77WZ4.js +469 -0
  94. package/dist/grep.js +7 -604
  95. package/dist/index.js +318 -35423
  96. package/dist/install.js +10 -4091
  97. package/dist/json-parser-7YMDJSBJ.js +213 -0
  98. package/dist/json.js +4 -665
  99. package/dist/kanban.js +3 -2096
  100. package/dist/languages/index.js +23 -4664
  101. package/dist/legacy-bridge-7UKZT46G.js +18 -0
  102. package/dist/lint.js +10 -3706
  103. package/dist/logs.js +4 -287
  104. package/dist/memory.js +6 -215
  105. package/dist/mode.js +3 -99
  106. package/dist/outdated.js +4 -5170
  107. package/dist/pack.js +65 -33933
  108. package/dist/parser-dispatch-VPOFDO2U.js +14 -0
  109. package/dist/patch.js +13 -9935
  110. package/dist/plan.js +5 -3286
  111. package/dist/process-registry.js +8 -651
  112. package/dist/ps-slash.js +12 -1439
  113. package/dist/py-parser-GPM5VYRG.js +16 -0
  114. package/dist/read.js +13 -9816
  115. package/dist/replace.js +15 -10014
  116. package/dist/rs-parser-WVQ4KYCX.js +81 -0
  117. package/dist/scaffold.js +4 -253
  118. package/dist/search.js +5 -585
  119. package/dist/session-kanban.js +30 -972
  120. package/dist/skill.js +2 -215
  121. package/dist/task.js +5 -3410
  122. package/dist/test.js +10 -3773
  123. package/dist/todo.js +4 -2891
  124. package/dist/tool-help.js +3 -123
  125. package/dist/tool-search.js +3 -79
  126. package/dist/tool-tier.js +68 -34020
  127. package/dist/tool-use.js +2 -93
  128. package/dist/tree-sitter-parser-YCILHGZD.js +17 -0
  129. package/dist/tree.js +4 -288
  130. package/dist/ts-parser-YH4ZMWMH.js +232 -0
  131. package/dist/typecheck.js +10 -3753
  132. package/dist/win32.js +6 -60
  133. package/dist/write.js +14 -9882
  134. package/dist/yaml-parser-GFAOKV53.js +157 -0
  135. 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,