@pentoshi/clai 0.5.10 → 0.7.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 (122) hide show
  1. package/dist/agent/context-manager.d.ts +27 -0
  2. package/dist/agent/context-manager.js +75 -0
  3. package/dist/agent/context-manager.js.map +1 -0
  4. package/dist/agent/runner.d.ts +21 -1
  5. package/dist/agent/runner.js +120 -30
  6. package/dist/agent/runner.js.map +1 -1
  7. package/dist/commands/doctor.js +21 -3
  8. package/dist/commands/doctor.js.map +1 -1
  9. package/dist/commands/update.js +11 -2
  10. package/dist/commands/update.js.map +1 -1
  11. package/dist/context/manager.d.ts +4 -0
  12. package/dist/context/manager.js +48 -0
  13. package/dist/context/manager.js.map +1 -0
  14. package/dist/index.js +156 -5
  15. package/dist/index.js.map +1 -1
  16. package/dist/llm/anthropic.js +29 -38
  17. package/dist/llm/anthropic.js.map +1 -1
  18. package/dist/llm/gemini.js +31 -40
  19. package/dist/llm/gemini.js.map +1 -1
  20. package/dist/llm/http.d.ts +21 -0
  21. package/dist/llm/http.js +140 -1
  22. package/dist/llm/http.js.map +1 -1
  23. package/dist/llm/ollama.js +18 -27
  24. package/dist/llm/ollama.js.map +1 -1
  25. package/dist/llm/router.d.ts +7 -0
  26. package/dist/llm/router.js +15 -9
  27. package/dist/llm/router.js.map +1 -1
  28. package/dist/modes/agent.d.ts +4 -2
  29. package/dist/modes/agent.js +2 -2
  30. package/dist/modes/agent.js.map +1 -1
  31. package/dist/os/pkgmgr.d.ts +7 -1
  32. package/dist/os/pkgmgr.js +97 -18
  33. package/dist/os/pkgmgr.js.map +1 -1
  34. package/dist/prompts/index.d.ts +7 -0
  35. package/dist/prompts/index.js +12 -4
  36. package/dist/prompts/index.js.map +1 -1
  37. package/dist/repl.d.ts +1 -0
  38. package/dist/repl.js +283 -18
  39. package/dist/repl.js.map +1 -1
  40. package/dist/safety/classifier.d.ts +5 -1
  41. package/dist/safety/classifier.js +254 -29
  42. package/dist/safety/classifier.js.map +1 -1
  43. package/dist/safety/patterns.d.ts +48 -1
  44. package/dist/safety/patterns.js +129 -13
  45. package/dist/safety/patterns.js.map +1 -1
  46. package/dist/store/config.d.ts +21 -1
  47. package/dist/store/config.js +28 -7
  48. package/dist/store/config.js.map +1 -1
  49. package/dist/store/history.d.ts +9 -0
  50. package/dist/store/history.js +58 -1
  51. package/dist/store/history.js.map +1 -1
  52. package/dist/store/keys.d.ts +2 -1
  53. package/dist/store/keys.js +8 -4
  54. package/dist/store/keys.js.map +1 -1
  55. package/dist/store/logs.d.ts +7 -0
  56. package/dist/store/logs.js +39 -1
  57. package/dist/store/logs.js.map +1 -1
  58. package/dist/store/project.d.ts +1 -0
  59. package/dist/store/project.js +34 -7
  60. package/dist/store/project.js.map +1 -1
  61. package/dist/store/scope.d.ts +29 -0
  62. package/dist/store/scope.js +113 -0
  63. package/dist/store/scope.js.map +1 -0
  64. package/dist/tools/artifacts.d.ts +9 -0
  65. package/dist/tools/artifacts.js +38 -0
  66. package/dist/tools/artifacts.js.map +1 -0
  67. package/dist/tools/fs.d.ts +6 -2
  68. package/dist/tools/fs.js +95 -17
  69. package/dist/tools/fs.js.map +1 -1
  70. package/dist/tools/http.d.ts +5 -2
  71. package/dist/tools/http.js +177 -8
  72. package/dist/tools/http.js.map +1 -1
  73. package/dist/tools/policies/output-policy.d.ts +13 -0
  74. package/dist/tools/policies/output-policy.js +56 -0
  75. package/dist/tools/policies/output-policy.js.map +1 -0
  76. package/dist/tools/reducers/ffuf.d.ts +6 -0
  77. package/dist/tools/reducers/ffuf.js +74 -0
  78. package/dist/tools/reducers/ffuf.js.map +1 -0
  79. package/dist/tools/reducers/generic.d.ts +2 -0
  80. package/dist/tools/reducers/generic.js +60 -0
  81. package/dist/tools/reducers/generic.js.map +1 -0
  82. package/dist/tools/reducers/gobuster.d.ts +2 -0
  83. package/dist/tools/reducers/gobuster.js +36 -0
  84. package/dist/tools/reducers/gobuster.js.map +1 -0
  85. package/dist/tools/reducers/httpx.d.ts +2 -0
  86. package/dist/tools/reducers/httpx.js +38 -0
  87. package/dist/tools/reducers/httpx.js.map +1 -0
  88. package/dist/tools/reducers/nmap.d.ts +7 -0
  89. package/dist/tools/reducers/nmap.js +82 -0
  90. package/dist/tools/reducers/nmap.js.map +1 -0
  91. package/dist/tools/reducers/nuclei.d.ts +2 -0
  92. package/dist/tools/reducers/nuclei.js +51 -0
  93. package/dist/tools/reducers/nuclei.js.map +1 -0
  94. package/dist/tools/reducers/sqlmap.d.ts +2 -0
  95. package/dist/tools/reducers/sqlmap.js +39 -0
  96. package/dist/tools/reducers/sqlmap.js.map +1 -0
  97. package/dist/tools/reducers/subdomains.d.ts +6 -0
  98. package/dist/tools/reducers/subdomains.js +31 -0
  99. package/dist/tools/reducers/subdomains.js.map +1 -0
  100. package/dist/tools/reducers/types.d.ts +14 -0
  101. package/dist/tools/reducers/types.js +2 -0
  102. package/dist/tools/reducers/types.js.map +1 -0
  103. package/dist/tools/registry.d.ts +1 -1
  104. package/dist/tools/registry.js +224 -43
  105. package/dist/tools/registry.js.map +1 -1
  106. package/dist/tools/shell.d.ts +45 -0
  107. package/dist/tools/shell.js +430 -12
  108. package/dist/tools/shell.js.map +1 -1
  109. package/dist/tools/validate.d.ts +37 -0
  110. package/dist/tools/validate.js +144 -0
  111. package/dist/tools/validate.js.map +1 -0
  112. package/dist/types.d.ts +8 -0
  113. package/dist/ui/keys.d.ts +21 -0
  114. package/dist/ui/keys.js +13 -0
  115. package/dist/ui/keys.js.map +1 -0
  116. package/dist/ui/output-pane.d.ts +31 -0
  117. package/dist/ui/output-pane.js +81 -0
  118. package/dist/ui/output-pane.js.map +1 -0
  119. package/dist/ui/tool-output.d.ts +18 -0
  120. package/dist/ui/tool-output.js +135 -0
  121. package/dist/ui/tool-output.js.map +1 -0
  122. package/package.json +1 -1
@@ -1,18 +1,187 @@
1
+ import net from "node:net";
2
+ import { lookup } from "node:dns/promises";
3
+ const DEFAULT_MAX_BYTES = 256 * 1024;
4
+ const ALLOWED_METHODS = new Set([
5
+ "GET",
6
+ "HEAD",
7
+ "POST",
8
+ "PUT",
9
+ "PATCH",
10
+ "DELETE",
11
+ "OPTIONS",
12
+ ]);
13
+ /**
14
+ * Block (or require explicit ownership confirmation for) requests that
15
+ * target loopback, private, link-local, or cloud-metadata addresses.
16
+ * This stops the agent from being tricked into SSRF against the host's
17
+ * intranet via a "fetch this URL" prompt.
18
+ */
19
+ function isBlockedAddress(host) {
20
+ const lower = host.toLowerCase();
21
+ if (lower === "localhost" || lower === "localhost.localdomain")
22
+ return true;
23
+ if (lower === "ip6-localhost" || lower === "ip6-loopback")
24
+ return true;
25
+ if (net.isIPv4(host)) {
26
+ const parts = host.split(".").map((p) => Number(p));
27
+ const [a, b] = parts;
28
+ if (a === 127)
29
+ return true; // loopback
30
+ if (a === 10)
31
+ return true; // RFC1918
32
+ if (a === 172 && b !== undefined && b >= 16 && b <= 31)
33
+ return true; // RFC1918
34
+ if (a === 192 && b === 168)
35
+ return true; // RFC1918
36
+ if (a === 169 && b === 254)
37
+ return true; // link-local + cloud metadata
38
+ if (a === 0)
39
+ return true; // 0.0.0.0/8
40
+ if (a === 100 && b !== undefined && b >= 64 && b <= 127)
41
+ return true; // CGNAT
42
+ return false;
43
+ }
44
+ if (net.isIPv6(host)) {
45
+ if (lower === "::1")
46
+ return true;
47
+ if (lower.startsWith("fe80:"))
48
+ return true; // link-local
49
+ if (lower.startsWith("fc") || lower.startsWith("fd"))
50
+ return true; // ULA
51
+ if (lower.startsWith("::ffff:")) {
52
+ // IPv4-mapped IPv6 — re-check the embedded v4 address.
53
+ const v4 = lower.slice("::ffff:".length);
54
+ return isBlockedAddress(v4);
55
+ }
56
+ return false;
57
+ }
58
+ return false;
59
+ }
60
+ async function resolveHost(host) {
61
+ if (net.isIP(host))
62
+ return host;
63
+ try {
64
+ const result = await lookup(host);
65
+ return result.address;
66
+ }
67
+ catch {
68
+ return undefined;
69
+ }
70
+ }
1
71
  export async function httpFetch(url, options = {}) {
2
- const init = {
3
- method: options.method ?? "GET",
4
- body: options.body ?? null,
5
- };
72
+ let target;
73
+ try {
74
+ target = new URL(url);
75
+ }
76
+ catch {
77
+ return { ok: false, output: `Invalid URL: ${url}`, exitCode: 1 };
78
+ }
79
+ if (target.protocol !== "http:" && target.protocol !== "https:") {
80
+ return {
81
+ ok: false,
82
+ output: `Refusing non-http(s) scheme: ${target.protocol}`,
83
+ exitCode: 1,
84
+ };
85
+ }
86
+ const method = (options.method ?? "GET").toUpperCase();
87
+ if (!ALLOWED_METHODS.has(method)) {
88
+ return {
89
+ ok: false,
90
+ output: `Unsupported HTTP method: ${method}`,
91
+ exitCode: 1,
92
+ };
93
+ }
94
+ // SSRF guard: refuse loopback/private/link-local/metadata destinations
95
+ // unless the caller explicitly attested ownership of the target.
96
+ const hostname = target.hostname.replace(/^\[|\]$/g, "");
97
+ const literalBlocked = isBlockedAddress(hostname);
98
+ let resolvedBlocked = false;
99
+ if (!literalBlocked) {
100
+ const resolved = await resolveHost(hostname);
101
+ resolvedBlocked = Boolean(resolved && isBlockedAddress(resolved));
102
+ }
103
+ if ((literalBlocked || resolvedBlocked) && !options.iOwnThis) {
104
+ return {
105
+ ok: false,
106
+ output: `Refusing to fetch private/loopback/metadata address ${hostname}. Pass iOwnThis=true to override.`,
107
+ exitCode: 1,
108
+ };
109
+ }
110
+ const init = { method };
111
+ if (options.body !== undefined && method !== "GET" && method !== "HEAD") {
112
+ init.body = options.body;
113
+ }
6
114
  if (options.headers) {
7
115
  init.headers = options.headers;
8
116
  }
9
- const response = await fetch(url, init);
10
- const limit = options.maxBytes ?? 1_000_000;
11
- const text = await response.text();
117
+ let response;
118
+ try {
119
+ response = await fetch(url, init);
120
+ }
121
+ catch (error) {
122
+ return {
123
+ ok: false,
124
+ output: `Network error: ${error instanceof Error ? error.message : String(error)}`,
125
+ exitCode: 1,
126
+ };
127
+ }
128
+ const limit = options.maxBytes ?? DEFAULT_MAX_BYTES;
129
+ const decoder = new TextDecoder("utf-8", { fatal: false });
130
+ let collected = "";
131
+ let bytesRead = 0;
132
+ let truncated = false;
133
+ const reader = response.body?.getReader();
134
+ if (reader) {
135
+ try {
136
+ while (bytesRead < limit) {
137
+ const { done, value } = await reader.read();
138
+ if (done)
139
+ break;
140
+ if (!value)
141
+ continue;
142
+ const remaining = limit - bytesRead;
143
+ if (value.byteLength > remaining) {
144
+ collected += decoder.decode(value.subarray(0, remaining), { stream: true });
145
+ bytesRead += remaining;
146
+ truncated = true;
147
+ try {
148
+ await reader.cancel();
149
+ }
150
+ catch {
151
+ // ignore — we're abandoning the body deliberately
152
+ }
153
+ break;
154
+ }
155
+ collected += decoder.decode(value, { stream: true });
156
+ bytesRead += value.byteLength;
157
+ }
158
+ collected += decoder.decode();
159
+ }
160
+ finally {
161
+ try {
162
+ reader.releaseLock();
163
+ }
164
+ catch {
165
+ // already released
166
+ }
167
+ }
168
+ }
169
+ else {
170
+ // No streaming body (eg HEAD or empty 204). Fall through with empty text.
171
+ collected = "";
172
+ }
173
+ const headerLines = [];
174
+ response.headers.forEach((v, k) => headerLines.push(`${k}: ${v}`));
175
+ const headerBlock = headerLines.length > 0 ? `Headers:\n${headerLines.join("\n")}\n\n` : "";
176
+ const truncNote = truncated
177
+ ? `\n... (truncated at ${limit.toLocaleString()} bytes)`
178
+ : "";
179
+ const body = method === "HEAD" ? "" : collected;
12
180
  return {
13
181
  ok: response.ok,
14
- output: text.slice(0, limit),
182
+ output: `${response.status} ${response.statusText} ${response.url}\n${headerBlock}${body}${truncNote}`,
15
183
  exitCode: response.status,
184
+ truncated,
16
185
  };
17
186
  }
18
187
  //# sourceMappingURL=http.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/tools/http.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAW,EACX,UAKI,EAAE;IAEN,MAAM,IAAI,GAAgB;QACxB,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;QAC/B,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,IAAI;KAC3B,CAAC;IACF,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACjC,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,IAAI,SAAS,CAAC;IAC5C,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,OAAO;QACL,EAAE,EAAE,QAAQ,CAAC,EAAE;QACf,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;QAC5B,QAAQ,EAAE,QAAQ,CAAC,MAAM;KAC1B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/tools/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Reducer, ReducerOutput } from "../reducers/types.js";
2
+ interface PolicyContext {
3
+ toolName: string;
4
+ command?: string | undefined;
5
+ argv?: string[] | undefined;
6
+ }
7
+ /**
8
+ * Pick a reducer based on (a) the tool name and (b) what binary is being
9
+ * invoked. Falls back to the generic line-ranking reducer.
10
+ */
11
+ export declare function pickReducer(context: PolicyContext): Reducer;
12
+ export declare function reduceToolOutput(raw: string, context: PolicyContext): ReducerOutput;
13
+ export {};
@@ -0,0 +1,56 @@
1
+ import { ffufReducer } from "../reducers/ffuf.js";
2
+ import { genericReducer } from "../reducers/generic.js";
3
+ import { gobusterReducer } from "../reducers/gobuster.js";
4
+ import { httpxReducer } from "../reducers/httpx.js";
5
+ import { nmapReducer } from "../reducers/nmap.js";
6
+ import { nucleiReducer } from "../reducers/nuclei.js";
7
+ import { sqlmapReducer } from "../reducers/sqlmap.js";
8
+ import { subdomainsReducer } from "../reducers/subdomains.js";
9
+ function commandHead(command) {
10
+ return command.trim().split(/\s+/)[0]?.replace(/^.*\//, "") ?? "";
11
+ }
12
+ /**
13
+ * Pick a reducer based on (a) the tool name and (b) what binary is being
14
+ * invoked. Falls back to the generic line-ranking reducer.
15
+ */
16
+ export function pickReducer(context) {
17
+ if (context.toolName === "net.scan" || context.toolName === "pentest.recon") {
18
+ // Both produce nmap-style text; for recon we still want nmap-shaped parsing
19
+ // for the nmap sub-step.
20
+ return nmapReducer;
21
+ }
22
+ const head = context.command ? commandHead(context.command) : "";
23
+ switch (head) {
24
+ case "nmap":
25
+ return nmapReducer;
26
+ case "ffuf":
27
+ return ffufReducer;
28
+ case "gobuster":
29
+ case "feroxbuster":
30
+ case "dirb":
31
+ case "dirsearch":
32
+ return gobusterReducer;
33
+ case "subfinder":
34
+ case "amass":
35
+ case "sublist3r":
36
+ case "assetfinder":
37
+ return subdomainsReducer;
38
+ case "httpx":
39
+ case "httprobe":
40
+ return httpxReducer;
41
+ case "nuclei":
42
+ return nucleiReducer;
43
+ case "sqlmap":
44
+ return sqlmapReducer;
45
+ default:
46
+ return genericReducer;
47
+ }
48
+ }
49
+ export function reduceToolOutput(raw, context) {
50
+ const reducer = pickReducer(context);
51
+ return reducer(raw, {
52
+ command: context.command ?? context.toolName,
53
+ argv: context.argv,
54
+ });
55
+ }
56
+ //# sourceMappingURL=output-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-policy.js","sourceRoot":"","sources":["../../../src/tools/policies/output-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAS9D,SAAS,WAAW,CAAC,OAAe;IAClC,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;AACpE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,OAAsB;IAChD,IAAI,OAAO,CAAC,QAAQ,KAAK,UAAU,IAAI,OAAO,CAAC,QAAQ,KAAK,eAAe,EAAE,CAAC;QAC5E,4EAA4E;QAC5E,yBAAyB;QACzB,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjE,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,MAAM;YACT,OAAO,WAAW,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,WAAW,CAAC;QACrB,KAAK,UAAU,CAAC;QAChB,KAAK,aAAa,CAAC;QACnB,KAAK,MAAM,CAAC;QACZ,KAAK,WAAW;YACd,OAAO,eAAe,CAAC;QACzB,KAAK,WAAW,CAAC;QACjB,KAAK,OAAO,CAAC;QACb,KAAK,WAAW,CAAC;QACjB,KAAK,aAAa;YAChB,OAAO,iBAAiB,CAAC;QAC3B,KAAK,OAAO,CAAC;QACb,KAAK,UAAU;YACb,OAAO,YAAY,CAAC;QACtB,KAAK,QAAQ;YACX,OAAO,aAAa,CAAC;QACvB,KAAK,QAAQ;YACX,OAAO,aAAa,CAAC;QACvB;YACE,OAAO,cAAc,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,GAAW,EACX,OAAsB;IAEtB,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,QAAQ;QAC5C,IAAI,EAAE,OAAO,CAAC,IAAI;KACnB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Reducer } from "./types.js";
2
+ /**
3
+ * ffuf supports JSON output (`-of json`) but the default is plain text. We
4
+ * parse both, group by status, and surface the most interesting clusters.
5
+ */
6
+ export declare const ffufReducer: Reducer;
@@ -0,0 +1,74 @@
1
+ const LINE_RE = /^([^\s]+)\s+\[Status:\s*(\d+),\s*Size:\s*(\d+),\s*Words:\s*(\d+),\s*Lines:\s*(\d+).*\]/;
2
+ /**
3
+ * ffuf supports JSON output (`-of json`) but the default is plain text. We
4
+ * parse both, group by status, and surface the most interesting clusters.
5
+ */
6
+ export const ffufReducer = (raw) => {
7
+ const results = [];
8
+ // Try JSON first (works when the agent already used `-of json`).
9
+ const jsonStart = raw.indexOf("{");
10
+ if (jsonStart >= 0) {
11
+ try {
12
+ const parsed = JSON.parse(raw.slice(jsonStart));
13
+ if (parsed.results) {
14
+ for (const r of parsed.results)
15
+ results.push(r);
16
+ }
17
+ }
18
+ catch {
19
+ // fall through to text parsing
20
+ }
21
+ }
22
+ if (results.length === 0) {
23
+ for (const line of raw.split(/\r?\n/)) {
24
+ const match = LINE_RE.exec(line);
25
+ if (!match)
26
+ continue;
27
+ results.push({
28
+ url: match[1],
29
+ status: Number(match[2]),
30
+ length: Number(match[3]),
31
+ words: Number(match[4]),
32
+ lines: Number(match[5]),
33
+ });
34
+ }
35
+ }
36
+ if (results.length === 0) {
37
+ return { summary: "# ffuf — no results parsed" };
38
+ }
39
+ // Cluster by (status, length) so likely-templated wildcard responses fold up.
40
+ const clusters = new Map();
41
+ for (const r of results) {
42
+ const key = `${r.status ?? "?"}:${r.length ?? "?"}`;
43
+ const c = clusters.get(key) ??
44
+ { status: r.status, length: r.length, samples: [] };
45
+ c.samples.push(r);
46
+ clusters.set(key, c);
47
+ }
48
+ const sorted = [...clusters.values()].sort((a, b) => b.samples.length - a.samples.length);
49
+ const lines = [
50
+ `# ffuf reduced summary — ${results.length} result(s), ${clusters.size} (status,length) cluster(s)`,
51
+ ];
52
+ for (const c of sorted.slice(0, 25)) {
53
+ lines.push("");
54
+ lines.push(`## status=${c.status ?? "?"} length=${c.length ?? "?"} — ${c.samples.length} hit(s)`);
55
+ for (const sample of c.samples.slice(0, 5)) {
56
+ lines.push(`- ${sample.url ?? JSON.stringify(sample.input)}`);
57
+ }
58
+ if (c.samples.length > 5) {
59
+ lines.push(`- ... ${c.samples.length - 5} more`);
60
+ }
61
+ }
62
+ return {
63
+ summary: lines.join("\n"),
64
+ findings: {
65
+ total: results.length,
66
+ clusters: sorted.map((c) => ({
67
+ status: c.status,
68
+ length: c.length,
69
+ count: c.samples.length,
70
+ })),
71
+ },
72
+ };
73
+ };
74
+ //# sourceMappingURL=ffuf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ffuf.js","sourceRoot":"","sources":["../../../src/tools/reducers/ffuf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Reducer } from "./types.js";
2
+ export declare const genericReducer: Reducer;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Catch-all reducer that ranks lines by signal so important findings survive
3
+ * truncation. Used when no command-specific reducer matches.
4
+ */
5
+ const SIGNAL_PATTERNS = [
6
+ { tag: "credential", re: /\b(?:password|passwd|secret|token|api[_-]?key|bearer)\b/i, weight: 5 },
7
+ { tag: "vulnerable", re: /\b(?:vulnerable|exploitable|exploit|cve-\d{4}-\d+)/i, weight: 5 },
8
+ { tag: "error", re: /\b(?:error|failed|denied|forbidden|refused|fatal)\b/i, weight: 4 },
9
+ { tag: "success", re: /\b(?:found|success|matched|positive|admin)\b/i, weight: 3 },
10
+ { tag: "open-port", re: /\b\d+\/(?:tcp|udp)\s+open\b/i, weight: 3 },
11
+ { tag: "http-2xx", re: /\s2\d{2}\b/, weight: 2 },
12
+ { tag: "http-403", re: /\s403\b/, weight: 2 },
13
+ { tag: "warning", re: /\bwarning\b/i, weight: 1 },
14
+ ];
15
+ function scoreLine(line) {
16
+ let score = 0;
17
+ const tags = [];
18
+ for (const pattern of SIGNAL_PATTERNS) {
19
+ if (pattern.re.test(line)) {
20
+ score += pattern.weight;
21
+ tags.push(pattern.tag);
22
+ }
23
+ }
24
+ return { score, tags };
25
+ }
26
+ export const genericReducer = (raw, _ctx) => {
27
+ const lines = raw.split(/\r?\n/);
28
+ const scored = lines.map((line, index) => ({
29
+ line,
30
+ index,
31
+ ...scoreLine(line),
32
+ }));
33
+ const interesting = scored.filter((x) => x.score > 0);
34
+ const totalLines = lines.length;
35
+ if (interesting.length === 0) {
36
+ // Fall back to head + tail when nothing matched.
37
+ const head = lines.slice(0, 20).join("\n");
38
+ const tail = lines.slice(-10).join("\n");
39
+ return {
40
+ summary: head + (lines.length > 30 ? `\n... (${lines.length} lines total)\n` : "\n") + tail,
41
+ };
42
+ }
43
+ const topRanked = interesting
44
+ .sort((a, b) => b.score - a.score || a.index - b.index)
45
+ .slice(0, 40);
46
+ const lineSet = new Set(topRanked.map((x) => x.index));
47
+ // Always include the very last 5 lines so trailing summaries (e.g. "X hosts
48
+ // up", "scan complete") are not dropped.
49
+ for (let i = Math.max(0, lines.length - 5); i < lines.length; i++) {
50
+ lineSet.add(i);
51
+ }
52
+ const ordered = [...lineSet].sort((a, b) => a - b);
53
+ const summaryLines = ordered.map((i) => lines[i]).filter((l) => l !== undefined);
54
+ const dropped = totalLines - ordered.length;
55
+ const header = `# Reduced output (${ordered.length} of ${totalLines} lines, ${dropped} omitted)`;
56
+ return {
57
+ summary: [header, ...summaryLines].join("\n"),
58
+ };
59
+ };
60
+ //# sourceMappingURL=generic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generic.js","sourceRoot":"","sources":["../../../src/tools/reducers/generic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Reducer } from "./types.js";
2
+ export declare const gobusterReducer: Reducer;
@@ -0,0 +1,36 @@
1
+ const LINE_RE = /^(?<path>\S+)\s+\(Status:\s*(?<status>\d+)\)\s+\[Size:\s*(?<size>\d+)\]/;
2
+ export const gobusterReducer = (raw) => {
3
+ const groups = new Map();
4
+ for (const line of raw.split(/\r?\n/)) {
5
+ const match = LINE_RE.exec(line);
6
+ if (!match || !match.groups)
7
+ continue;
8
+ const status = Number(match.groups.status);
9
+ const path = match.groups.path;
10
+ const size = Number(match.groups.size);
11
+ const list = groups.get(status) ?? [];
12
+ list.push({ path, size });
13
+ groups.set(status, list);
14
+ }
15
+ if (groups.size === 0) {
16
+ return { summary: "# gobuster — no paths discovered" };
17
+ }
18
+ const sortedStatuses = [...groups.keys()].sort((a, b) => a - b);
19
+ const lines = [`# gobuster reduced summary — ${sortedStatuses.length} status code(s)`];
20
+ for (const status of sortedStatuses) {
21
+ const entries = groups.get(status);
22
+ lines.push("");
23
+ lines.push(`## Status ${status} — ${entries.length} path(s)`);
24
+ for (const entry of entries.slice(0, 25)) {
25
+ lines.push(`- ${entry.path} (size=${entry.size})`);
26
+ }
27
+ if (entries.length > 25) {
28
+ lines.push(`- ... ${entries.length - 25} more`);
29
+ }
30
+ }
31
+ return {
32
+ summary: lines.join("\n"),
33
+ findings: { byStatus: Object.fromEntries(groups) },
34
+ };
35
+ };
36
+ //# sourceMappingURL=gobuster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gobuster.js","sourceRoot":"","sources":["../../../src/tools/reducers/gobuster.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,GAAG,yEAAyE,CAAC;AAE1F,MAAM,CAAC,MAAM,eAAe,GAAY,CAAC,GAAG,EAAiB,EAAE;IAC7D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAiD,CAAC;IACxE,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,SAAS;QACtC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,IAAK,CAAC;QAChC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3B,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,OAAO,EAAE,kCAAkC,EAAE,CAAC;IACzD,CAAC;IACD,MAAM,cAAc,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,KAAK,GAAa,CAAC,gCAAgC,cAAc,CAAC,MAAM,iBAAiB,CAAC,CAAC;IACjG,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,MAAM,OAAO,CAAC,MAAM,UAAU,CAAC,CAAC;QAC9D,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,UAAU,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;QACrD,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,MAAM,GAAG,EAAE,OAAO,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IACD,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QACzB,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE;KACnD,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Reducer } from "./types.js";
2
+ export declare const httpxReducer: Reducer;
@@ -0,0 +1,38 @@
1
+ export const httpxReducer = (raw) => {
2
+ const rows = [];
3
+ for (const line of raw.split(/\r?\n/)) {
4
+ const trimmed = line.trim();
5
+ if (!trimmed.startsWith("{"))
6
+ continue;
7
+ try {
8
+ rows.push(JSON.parse(trimmed));
9
+ }
10
+ catch {
11
+ // skip malformed lines
12
+ }
13
+ }
14
+ if (rows.length === 0) {
15
+ return { summary: "# httpx — no JSONL rows parsed (pass -json to get structured output)" };
16
+ }
17
+ const lines = [
18
+ `# httpx reduced summary — ${rows.length} URL(s)`,
19
+ ];
20
+ for (const row of rows.slice(0, 50)) {
21
+ const url = row.final_url ?? row.url ?? row.input ?? row.host ?? "?";
22
+ const status = row.status_code ?? row.status ?? "?";
23
+ const tech = (row.tech ?? row.technologies ?? []).join(", ");
24
+ const title = row.title ? ` title=${JSON.stringify(row.title)}` : "";
25
+ const cdn = row.cdn_name ?? row.cdn;
26
+ const cdnNote = cdn ? ` cdn=${cdn}` : "";
27
+ const len = row.content_length !== undefined ? ` len=${row.content_length}` : "";
28
+ lines.push(`- ${url} [${status}]${title}${len}${tech ? ` tech=[${tech}]` : ""}${cdnNote}`);
29
+ }
30
+ if (rows.length > 50) {
31
+ lines.push(`- ... ${rows.length - 50} more in artifact`);
32
+ }
33
+ return {
34
+ summary: lines.join("\n"),
35
+ findings: { count: rows.length },
36
+ };
37
+ };
38
+ //# sourceMappingURL=httpx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpx.js","sourceRoot":"","sources":["../../../src/tools/reducers/httpx.ts"],"names":[],"mappings":"AAkBA,MAAM,CAAC,MAAM,YAAY,GAAY,CAAC,GAAG,EAAiB,EAAE;IAC1D,MAAM,IAAI,GAAe,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACvC,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAa,CAAC,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,uBAAuB;QACzB,CAAC;IACH,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,OAAO,EAAE,sEAAsE,EAAE,CAAC;IAC7F,CAAC;IACD,MAAM,KAAK,GAAa;QACtB,6BAA6B,IAAI,CAAC,MAAM,SAAS;KAClD,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC;QACrE,MAAM,MAAM,GAAG,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC;QACpD,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,GAAG,CAAC;QACpC,MAAM,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,MAAM,IAAI,KAAK,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IAC7F,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,GAAG,EAAE,mBAAmB,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QACzB,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE;KACjC,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Reducer } from "./types.js";
2
+ /**
3
+ * Parse plain "nmap -sV" text output into structured findings. We avoid
4
+ * forcing -oX here so the agent can still pass any flags it wants — but if
5
+ * the output looks like XML we just include it verbatim.
6
+ */
7
+ export declare const nmapReducer: Reducer;
@@ -0,0 +1,82 @@
1
+ const PORT_LINE_RE = /^\s*(\d+)\/(tcp|udp)\s+(open|closed|filtered|open\|filtered)\s+(\S+)(?:\s+(.*))?$/i;
2
+ const HOST_LINE_RE = /^Nmap scan report for\s+(.+)$/i;
3
+ const STATUS_LINE_RE = /^Host is\s+(.+?)(?:\s+\(.*\))?\.?$/i;
4
+ const OS_LINE_RE = /^(?:OS details|Running):\s+(.+)$/i;
5
+ const SUMMARY_RE = /Nmap done:.+/i;
6
+ /**
7
+ * Parse plain "nmap -sV" text output into structured findings. We avoid
8
+ * forcing -oX here so the agent can still pass any flags it wants — but if
9
+ * the output looks like XML we just include it verbatim.
10
+ */
11
+ export const nmapReducer = (raw) => {
12
+ if (raw.trim().startsWith("<?xml")) {
13
+ return { summary: raw.slice(0, 8_000) };
14
+ }
15
+ const hosts = [];
16
+ let current;
17
+ let summaryLine = "";
18
+ for (const line of raw.split(/\r?\n/)) {
19
+ const hostMatch = HOST_LINE_RE.exec(line);
20
+ if (hostMatch) {
21
+ current = { host: hostMatch[1].trim(), ports: [] };
22
+ hosts.push(current);
23
+ continue;
24
+ }
25
+ if (!current)
26
+ continue;
27
+ const statusMatch = STATUS_LINE_RE.exec(line);
28
+ if (statusMatch) {
29
+ current.status = statusMatch[1];
30
+ continue;
31
+ }
32
+ const osMatch = OS_LINE_RE.exec(line);
33
+ if (osMatch) {
34
+ current.os = osMatch[1];
35
+ continue;
36
+ }
37
+ const portMatch = PORT_LINE_RE.exec(line);
38
+ if (portMatch) {
39
+ current.ports.push({
40
+ port: Number(portMatch[1]),
41
+ protocol: portMatch[2].toLowerCase(),
42
+ state: portMatch[3].toLowerCase(),
43
+ service: portMatch[4],
44
+ version: portMatch[5]?.trim() || undefined,
45
+ });
46
+ continue;
47
+ }
48
+ if (SUMMARY_RE.test(line)) {
49
+ summaryLine = line.trim();
50
+ }
51
+ }
52
+ const totalHosts = hosts.length;
53
+ const totalOpen = hosts.reduce((n, host) => n + host.ports.filter((p) => p.state === "open").length, 0);
54
+ const lines = [];
55
+ lines.push(`# nmap reduced summary — ${totalHosts} host(s), ${totalOpen} open port(s)`);
56
+ if (summaryLine)
57
+ lines.push(summaryLine);
58
+ for (const host of hosts) {
59
+ lines.push("");
60
+ lines.push(`## ${host.host}${host.status ? ` (${host.status})` : ""}`);
61
+ if (host.os)
62
+ lines.push(`OS: ${host.os}`);
63
+ if (host.ports.length === 0) {
64
+ lines.push("(no ports parsed)");
65
+ continue;
66
+ }
67
+ for (const port of host.ports) {
68
+ const parts = [
69
+ `${port.port}/${port.protocol}`,
70
+ port.state,
71
+ port.service ?? "",
72
+ port.version ?? "",
73
+ ];
74
+ lines.push(parts.filter(Boolean).join(" — "));
75
+ }
76
+ }
77
+ return {
78
+ summary: lines.join("\n"),
79
+ findings: { hosts, totalHosts, totalOpen },
80
+ };
81
+ };
82
+ //# sourceMappingURL=nmap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nmap.js","sourceRoot":"","sources":["../../../src/tools/reducers/nmap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Reducer } from "./types.js";
2
+ export declare const nucleiReducer: Reducer;