clawfast 2.0.1 → 2.0.2
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/clawfast.cjs +1524 -282
- package/package.json +1 -1
package/dist/clawfast.cjs
CHANGED
|
@@ -85,8 +85,8 @@ var init_boot_ui = __esm({
|
|
|
85
85
|
var require_main = __commonJS({
|
|
86
86
|
"../node_modules/.pnpm/dotenv@17.4.2/node_modules/dotenv/lib/main.js"(exports2, module2) {
|
|
87
87
|
"use strict";
|
|
88
|
-
var
|
|
89
|
-
var
|
|
88
|
+
var fs8 = require("fs");
|
|
89
|
+
var path12 = require("path");
|
|
90
90
|
var os7 = require("os");
|
|
91
91
|
var crypto2 = require("crypto");
|
|
92
92
|
var TIPS = [
|
|
@@ -217,7 +217,7 @@ var require_main = __commonJS({
|
|
|
217
217
|
if (options && options.path && options.path.length > 0) {
|
|
218
218
|
if (Array.isArray(options.path)) {
|
|
219
219
|
for (const filepath of options.path) {
|
|
220
|
-
if (
|
|
220
|
+
if (fs8.existsSync(filepath)) {
|
|
221
221
|
possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
222
222
|
}
|
|
223
223
|
}
|
|
@@ -225,15 +225,15 @@ var require_main = __commonJS({
|
|
|
225
225
|
possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
|
|
226
226
|
}
|
|
227
227
|
} else {
|
|
228
|
-
possibleVaultPath =
|
|
228
|
+
possibleVaultPath = path12.resolve(process.cwd(), ".env.vault");
|
|
229
229
|
}
|
|
230
|
-
if (
|
|
230
|
+
if (fs8.existsSync(possibleVaultPath)) {
|
|
231
231
|
return possibleVaultPath;
|
|
232
232
|
}
|
|
233
233
|
return null;
|
|
234
234
|
}
|
|
235
235
|
function _resolveHome(envPath) {
|
|
236
|
-
return envPath[0] === "~" ?
|
|
236
|
+
return envPath[0] === "~" ? path12.join(os7.homedir(), envPath.slice(1)) : envPath;
|
|
237
237
|
}
|
|
238
238
|
function _configVault(options) {
|
|
239
239
|
const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
@@ -250,7 +250,7 @@ var require_main = __commonJS({
|
|
|
250
250
|
return { parsed };
|
|
251
251
|
}
|
|
252
252
|
function configDotenv(options) {
|
|
253
|
-
const dotenvPath =
|
|
253
|
+
const dotenvPath = path12.resolve(process.cwd(), ".env");
|
|
254
254
|
let encoding = "utf8";
|
|
255
255
|
let processEnv = process.env;
|
|
256
256
|
if (options && options.processEnv != null) {
|
|
@@ -278,13 +278,13 @@ var require_main = __commonJS({
|
|
|
278
278
|
}
|
|
279
279
|
let lastError;
|
|
280
280
|
const parsedAll = {};
|
|
281
|
-
for (const
|
|
281
|
+
for (const path13 of optionPaths) {
|
|
282
282
|
try {
|
|
283
|
-
const parsed = DotenvModule.parse(
|
|
283
|
+
const parsed = DotenvModule.parse(fs8.readFileSync(path13, { encoding }));
|
|
284
284
|
DotenvModule.populate(parsedAll, parsed, options);
|
|
285
285
|
} catch (e) {
|
|
286
286
|
if (debug) {
|
|
287
|
-
_debug(`failed to load ${
|
|
287
|
+
_debug(`failed to load ${path13} ${e.message}`);
|
|
288
288
|
}
|
|
289
289
|
lastError = e;
|
|
290
290
|
}
|
|
@@ -297,7 +297,7 @@ var require_main = __commonJS({
|
|
|
297
297
|
const shortPaths = [];
|
|
298
298
|
for (const filePath of optionPaths) {
|
|
299
299
|
try {
|
|
300
|
-
const relative2 =
|
|
300
|
+
const relative2 = path12.relative(process.cwd(), filePath);
|
|
301
301
|
shortPaths.push(relative2);
|
|
302
302
|
} catch (e) {
|
|
303
303
|
if (debug) {
|
|
@@ -548,7 +548,7 @@ var clawfastVersion, isDevVersion, isNewerVersion;
|
|
|
548
548
|
var init_version = __esm({
|
|
549
549
|
"src/version.ts"() {
|
|
550
550
|
"use strict";
|
|
551
|
-
clawfastVersion = () => true ? "2.0.
|
|
551
|
+
clawfastVersion = () => true ? "2.0.2" : "0.0.0-dev";
|
|
552
552
|
isDevVersion = () => clawfastVersion().includes("-dev");
|
|
553
553
|
isNewerVersion = (a, b) => {
|
|
554
554
|
const parse3 = (v) => v.split("-")[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
@@ -1721,10 +1721,10 @@ function mergeDefs(...defs) {
|
|
|
1721
1721
|
function cloneDef(schema) {
|
|
1722
1722
|
return mergeDefs(schema._zod.def);
|
|
1723
1723
|
}
|
|
1724
|
-
function getElementAtPath(obj,
|
|
1725
|
-
if (!
|
|
1724
|
+
function getElementAtPath(obj, path12) {
|
|
1725
|
+
if (!path12)
|
|
1726
1726
|
return obj;
|
|
1727
|
-
return
|
|
1727
|
+
return path12.reduce((acc, key) => acc?.[key], obj);
|
|
1728
1728
|
}
|
|
1729
1729
|
function promiseAllObject(promisesObj) {
|
|
1730
1730
|
const keys = Object.keys(promisesObj);
|
|
@@ -2052,11 +2052,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
2052
2052
|
}
|
|
2053
2053
|
return false;
|
|
2054
2054
|
}
|
|
2055
|
-
function prefixIssues(
|
|
2055
|
+
function prefixIssues(path12, issues) {
|
|
2056
2056
|
return issues.map((iss) => {
|
|
2057
2057
|
var _a25;
|
|
2058
2058
|
(_a25 = iss).path ?? (_a25.path = []);
|
|
2059
|
-
iss.path.unshift(
|
|
2059
|
+
iss.path.unshift(path12);
|
|
2060
2060
|
return iss;
|
|
2061
2061
|
});
|
|
2062
2062
|
}
|
|
@@ -2274,16 +2274,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2274
2274
|
}
|
|
2275
2275
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
2276
2276
|
const fieldErrors = { _errors: [] };
|
|
2277
|
-
const processError = (error52,
|
|
2277
|
+
const processError = (error52, path12 = []) => {
|
|
2278
2278
|
for (const issue2 of error52.issues) {
|
|
2279
2279
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2280
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2280
|
+
issue2.errors.map((issues) => processError({ issues }, [...path12, ...issue2.path]));
|
|
2281
2281
|
} else if (issue2.code === "invalid_key") {
|
|
2282
|
-
processError({ issues: issue2.issues }, [...
|
|
2282
|
+
processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
|
|
2283
2283
|
} else if (issue2.code === "invalid_element") {
|
|
2284
|
-
processError({ issues: issue2.issues }, [...
|
|
2284
|
+
processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
|
|
2285
2285
|
} else {
|
|
2286
|
-
const fullpath = [...
|
|
2286
|
+
const fullpath = [...path12, ...issue2.path];
|
|
2287
2287
|
if (fullpath.length === 0) {
|
|
2288
2288
|
fieldErrors._errors.push(mapper(issue2));
|
|
2289
2289
|
} else {
|
|
@@ -2310,17 +2310,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2310
2310
|
}
|
|
2311
2311
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
2312
2312
|
const result = { errors: [] };
|
|
2313
|
-
const processError = (error52,
|
|
2313
|
+
const processError = (error52, path12 = []) => {
|
|
2314
2314
|
var _a25, _b18;
|
|
2315
2315
|
for (const issue2 of error52.issues) {
|
|
2316
2316
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2317
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2317
|
+
issue2.errors.map((issues) => processError({ issues }, [...path12, ...issue2.path]));
|
|
2318
2318
|
} else if (issue2.code === "invalid_key") {
|
|
2319
|
-
processError({ issues: issue2.issues }, [...
|
|
2319
|
+
processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
|
|
2320
2320
|
} else if (issue2.code === "invalid_element") {
|
|
2321
|
-
processError({ issues: issue2.issues }, [...
|
|
2321
|
+
processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
|
|
2322
2322
|
} else {
|
|
2323
|
-
const fullpath = [...
|
|
2323
|
+
const fullpath = [...path12, ...issue2.path];
|
|
2324
2324
|
if (fullpath.length === 0) {
|
|
2325
2325
|
result.errors.push(mapper(issue2));
|
|
2326
2326
|
continue;
|
|
@@ -2352,8 +2352,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2352
2352
|
}
|
|
2353
2353
|
function toDotPath(_path) {
|
|
2354
2354
|
const segs = [];
|
|
2355
|
-
const
|
|
2356
|
-
for (const seg of
|
|
2355
|
+
const path12 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2356
|
+
for (const seg of path12) {
|
|
2357
2357
|
if (typeof seg === "number")
|
|
2358
2358
|
segs.push(`[${seg}]`);
|
|
2359
2359
|
else if (typeof seg === "symbol")
|
|
@@ -15856,13 +15856,13 @@ function resolveRef(ref, ctx) {
|
|
|
15856
15856
|
if (!ref.startsWith("#")) {
|
|
15857
15857
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
15858
15858
|
}
|
|
15859
|
-
const
|
|
15860
|
-
if (
|
|
15859
|
+
const path12 = ref.slice(1).split("/").filter(Boolean);
|
|
15860
|
+
if (path12.length === 0) {
|
|
15861
15861
|
return ctx.rootSchema;
|
|
15862
15862
|
}
|
|
15863
15863
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
15864
|
-
if (
|
|
15865
|
-
const key =
|
|
15864
|
+
if (path12[0] === defsKey) {
|
|
15865
|
+
const key = path12[1];
|
|
15866
15866
|
if (!key || !ctx.defs[key]) {
|
|
15867
15867
|
throw new Error(`Reference not found: ${ref}`);
|
|
15868
15868
|
}
|
|
@@ -17051,8 +17051,8 @@ var init_parseUtil = __esm({
|
|
|
17051
17051
|
init_errors3();
|
|
17052
17052
|
init_en2();
|
|
17053
17053
|
makeIssue = (params) => {
|
|
17054
|
-
const { data, path:
|
|
17055
|
-
const fullPath = [...
|
|
17054
|
+
const { data, path: path12, errorMaps, issueData } = params;
|
|
17055
|
+
const fullPath = [...path12, ...issueData.path || []];
|
|
17056
17056
|
const fullIssue = {
|
|
17057
17057
|
...issueData,
|
|
17058
17058
|
path: fullPath
|
|
@@ -17335,11 +17335,11 @@ var init_types = __esm({
|
|
|
17335
17335
|
init_parseUtil();
|
|
17336
17336
|
init_util2();
|
|
17337
17337
|
ParseInputLazyPath = class {
|
|
17338
|
-
constructor(parent, value,
|
|
17338
|
+
constructor(parent, value, path12, key) {
|
|
17339
17339
|
this._cachedPath = [];
|
|
17340
17340
|
this.parent = parent;
|
|
17341
17341
|
this.data = value;
|
|
17342
|
-
this._path =
|
|
17342
|
+
this._path = path12;
|
|
17343
17343
|
this._key = key;
|
|
17344
17344
|
}
|
|
17345
17345
|
get path() {
|
|
@@ -23378,8 +23378,8 @@ var require_auth_config = __commonJS({
|
|
|
23378
23378
|
writeAuthConfig: () => writeAuthConfig
|
|
23379
23379
|
});
|
|
23380
23380
|
module2.exports = __toCommonJS(auth_config_exports);
|
|
23381
|
-
var
|
|
23382
|
-
var
|
|
23381
|
+
var fs8 = __toESM2(require("fs"));
|
|
23382
|
+
var path12 = __toESM2(require("path"));
|
|
23383
23383
|
var import_token_util = require_token_util();
|
|
23384
23384
|
function getAuthConfigPath() {
|
|
23385
23385
|
const dataDir = (0, import_token_util.getVercelDataDir)();
|
|
@@ -23388,15 +23388,15 @@ var require_auth_config = __commonJS({
|
|
|
23388
23388
|
`Unable to find Vercel CLI data directory. Your platform: ${process.platform}. Supported: darwin, linux, win32.`
|
|
23389
23389
|
);
|
|
23390
23390
|
}
|
|
23391
|
-
return
|
|
23391
|
+
return path12.join(dataDir, "auth.json");
|
|
23392
23392
|
}
|
|
23393
23393
|
function readAuthConfig() {
|
|
23394
23394
|
try {
|
|
23395
23395
|
const authPath = getAuthConfigPath();
|
|
23396
|
-
if (!
|
|
23396
|
+
if (!fs8.existsSync(authPath)) {
|
|
23397
23397
|
return null;
|
|
23398
23398
|
}
|
|
23399
|
-
const content =
|
|
23399
|
+
const content = fs8.readFileSync(authPath, "utf8");
|
|
23400
23400
|
if (!content) {
|
|
23401
23401
|
return null;
|
|
23402
23402
|
}
|
|
@@ -23407,11 +23407,11 @@ var require_auth_config = __commonJS({
|
|
|
23407
23407
|
}
|
|
23408
23408
|
function writeAuthConfig(config3) {
|
|
23409
23409
|
const authPath = getAuthConfigPath();
|
|
23410
|
-
const authDir =
|
|
23411
|
-
if (!
|
|
23412
|
-
|
|
23410
|
+
const authDir = path12.dirname(authPath);
|
|
23411
|
+
if (!fs8.existsSync(authDir)) {
|
|
23412
|
+
fs8.mkdirSync(authDir, { mode: 504, recursive: true });
|
|
23413
23413
|
}
|
|
23414
|
-
|
|
23414
|
+
fs8.writeFileSync(authPath, JSON.stringify(config3, null, 2), { mode: 384 });
|
|
23415
23415
|
}
|
|
23416
23416
|
function isValidAccessToken(authConfig, expirationBufferMs = 0) {
|
|
23417
23417
|
if (!authConfig.token)
|
|
@@ -23602,8 +23602,8 @@ var require_token_util = __commonJS({
|
|
|
23602
23602
|
saveToken: () => saveToken
|
|
23603
23603
|
});
|
|
23604
23604
|
module2.exports = __toCommonJS(token_util_exports);
|
|
23605
|
-
var
|
|
23606
|
-
var
|
|
23605
|
+
var path12 = __toESM2(require("path"));
|
|
23606
|
+
var fs8 = __toESM2(require("fs"));
|
|
23607
23607
|
var import_token_error = require_token_error();
|
|
23608
23608
|
var import_token_io = require_token_io();
|
|
23609
23609
|
var import_auth_config = require_auth_config();
|
|
@@ -23615,7 +23615,7 @@ var require_token_util = __commonJS({
|
|
|
23615
23615
|
if (!dataDir) {
|
|
23616
23616
|
return null;
|
|
23617
23617
|
}
|
|
23618
|
-
return
|
|
23618
|
+
return path12.join(dataDir, vercelFolder);
|
|
23619
23619
|
}
|
|
23620
23620
|
async function getVercelToken2(options) {
|
|
23621
23621
|
const authConfig = (0, import_auth_config.readAuthConfig)();
|
|
@@ -23691,13 +23691,13 @@ var require_token_util = __commonJS({
|
|
|
23691
23691
|
"Unable to find project root directory. Have you linked your project with `vc link?`"
|
|
23692
23692
|
);
|
|
23693
23693
|
}
|
|
23694
|
-
const prjPath =
|
|
23695
|
-
if (!
|
|
23694
|
+
const prjPath = path12.join(dir, ".vercel", "project.json");
|
|
23695
|
+
if (!fs8.existsSync(prjPath)) {
|
|
23696
23696
|
throw new import_token_error.VercelOidcTokenError(
|
|
23697
23697
|
"project.json not found, have you linked your project with `vc link?`"
|
|
23698
23698
|
);
|
|
23699
23699
|
}
|
|
23700
|
-
const prj = JSON.parse(
|
|
23700
|
+
const prj = JSON.parse(fs8.readFileSync(prjPath, "utf8"));
|
|
23701
23701
|
if (typeof prj.projectId !== "string" && typeof prj.orgId !== "string") {
|
|
23702
23702
|
throw new TypeError(
|
|
23703
23703
|
"Expected a string-valued projectId property. Try running `vc link` to re-link your project."
|
|
@@ -23712,11 +23712,11 @@ var require_token_util = __commonJS({
|
|
|
23712
23712
|
"Unable to find user data directory. Please reach out to Vercel support."
|
|
23713
23713
|
);
|
|
23714
23714
|
}
|
|
23715
|
-
const tokenPath =
|
|
23715
|
+
const tokenPath = path12.join(dir, "com.vercel.token", `${projectId}.json`);
|
|
23716
23716
|
const tokenJson = JSON.stringify(token);
|
|
23717
|
-
|
|
23718
|
-
|
|
23719
|
-
|
|
23717
|
+
fs8.mkdirSync(path12.dirname(tokenPath), { mode: 504, recursive: true });
|
|
23718
|
+
fs8.writeFileSync(tokenPath, tokenJson);
|
|
23719
|
+
fs8.chmodSync(tokenPath, 432);
|
|
23720
23720
|
return;
|
|
23721
23721
|
}
|
|
23722
23722
|
function loadToken(projectId) {
|
|
@@ -23726,11 +23726,11 @@ var require_token_util = __commonJS({
|
|
|
23726
23726
|
"Unable to find user data directory. Please reach out to Vercel support."
|
|
23727
23727
|
);
|
|
23728
23728
|
}
|
|
23729
|
-
const tokenPath =
|
|
23730
|
-
if (!
|
|
23729
|
+
const tokenPath = path12.join(dir, "com.vercel.token", `${projectId}.json`);
|
|
23730
|
+
if (!fs8.existsSync(tokenPath)) {
|
|
23731
23731
|
return null;
|
|
23732
23732
|
}
|
|
23733
|
-
const token = JSON.parse(
|
|
23733
|
+
const token = JSON.parse(fs8.readFileSync(tokenPath, "utf8"));
|
|
23734
23734
|
assertVercelOidcTokenResponse(token);
|
|
23735
23735
|
return token;
|
|
23736
23736
|
}
|
|
@@ -35756,7 +35756,7 @@ function createOpenRouter(options = {}) {
|
|
|
35756
35756
|
);
|
|
35757
35757
|
const createChatModel = (modelId, settings = {}) => new OpenRouterChatLanguageModel(modelId, settings, {
|
|
35758
35758
|
provider: "openrouter.chat",
|
|
35759
|
-
url: ({ path:
|
|
35759
|
+
url: ({ path: path12 }) => `${baseURL}${path12}`,
|
|
35760
35760
|
headers: getHeaders,
|
|
35761
35761
|
compatibility,
|
|
35762
35762
|
fetch: options.fetch,
|
|
@@ -35764,7 +35764,7 @@ function createOpenRouter(options = {}) {
|
|
|
35764
35764
|
});
|
|
35765
35765
|
const createCompletionModel = (modelId, settings = {}) => new OpenRouterCompletionLanguageModel(modelId, settings, {
|
|
35766
35766
|
provider: "openrouter.completion",
|
|
35767
|
-
url: ({ path:
|
|
35767
|
+
url: ({ path: path12 }) => `${baseURL}${path12}`,
|
|
35768
35768
|
headers: getHeaders,
|
|
35769
35769
|
compatibility,
|
|
35770
35770
|
fetch: options.fetch,
|
|
@@ -35772,21 +35772,21 @@ function createOpenRouter(options = {}) {
|
|
|
35772
35772
|
});
|
|
35773
35773
|
const createEmbeddingModel = (modelId, settings = {}) => new OpenRouterEmbeddingModel(modelId, settings, {
|
|
35774
35774
|
provider: "openrouter.embedding",
|
|
35775
|
-
url: ({ path:
|
|
35775
|
+
url: ({ path: path12 }) => `${baseURL}${path12}`,
|
|
35776
35776
|
headers: getHeaders,
|
|
35777
35777
|
fetch: options.fetch,
|
|
35778
35778
|
extraBody: options.extraBody
|
|
35779
35779
|
});
|
|
35780
35780
|
const createImageModel = (modelId, settings = {}) => new OpenRouterImageModel(modelId, settings, {
|
|
35781
35781
|
provider: "openrouter.image",
|
|
35782
|
-
url: ({ path:
|
|
35782
|
+
url: ({ path: path12 }) => `${baseURL}${path12}`,
|
|
35783
35783
|
headers: getHeaders,
|
|
35784
35784
|
fetch: options.fetch,
|
|
35785
35785
|
extraBody: options.extraBody
|
|
35786
35786
|
});
|
|
35787
35787
|
const createVideoModel = (modelId, settings = {}) => new OpenRouterVideoModel(modelId, settings, {
|
|
35788
35788
|
provider: "openrouter.video",
|
|
35789
|
-
url: ({ path:
|
|
35789
|
+
url: ({ path: path12 }) => `${baseURL}${path12}`,
|
|
35790
35790
|
headers: getHeaders,
|
|
35791
35791
|
fetch: options.fetch,
|
|
35792
35792
|
extraBody: options.extraBody
|
|
@@ -40322,37 +40322,37 @@ function createOpenAI(options = {}) {
|
|
|
40322
40322
|
);
|
|
40323
40323
|
const createChatModel = (modelId) => new OpenAIChatLanguageModel(modelId, {
|
|
40324
40324
|
provider: `${providerName}.chat`,
|
|
40325
|
-
url: ({ path:
|
|
40325
|
+
url: ({ path: path12 }) => `${baseURL}${path12}`,
|
|
40326
40326
|
headers: getHeaders,
|
|
40327
40327
|
fetch: options.fetch
|
|
40328
40328
|
});
|
|
40329
40329
|
const createCompletionModel = (modelId) => new OpenAICompletionLanguageModel(modelId, {
|
|
40330
40330
|
provider: `${providerName}.completion`,
|
|
40331
|
-
url: ({ path:
|
|
40331
|
+
url: ({ path: path12 }) => `${baseURL}${path12}`,
|
|
40332
40332
|
headers: getHeaders,
|
|
40333
40333
|
fetch: options.fetch
|
|
40334
40334
|
});
|
|
40335
40335
|
const createEmbeddingModel = (modelId) => new OpenAIEmbeddingModel(modelId, {
|
|
40336
40336
|
provider: `${providerName}.embedding`,
|
|
40337
|
-
url: ({ path:
|
|
40337
|
+
url: ({ path: path12 }) => `${baseURL}${path12}`,
|
|
40338
40338
|
headers: getHeaders,
|
|
40339
40339
|
fetch: options.fetch
|
|
40340
40340
|
});
|
|
40341
40341
|
const createImageModel = (modelId) => new OpenAIImageModel(modelId, {
|
|
40342
40342
|
provider: `${providerName}.image`,
|
|
40343
|
-
url: ({ path:
|
|
40343
|
+
url: ({ path: path12 }) => `${baseURL}${path12}`,
|
|
40344
40344
|
headers: getHeaders,
|
|
40345
40345
|
fetch: options.fetch
|
|
40346
40346
|
});
|
|
40347
40347
|
const createTranscriptionModel = (modelId) => new OpenAITranscriptionModel(modelId, {
|
|
40348
40348
|
provider: `${providerName}.transcription`,
|
|
40349
|
-
url: ({ path:
|
|
40349
|
+
url: ({ path: path12 }) => `${baseURL}${path12}`,
|
|
40350
40350
|
headers: getHeaders,
|
|
40351
40351
|
fetch: options.fetch
|
|
40352
40352
|
});
|
|
40353
40353
|
const createSpeechModel = (modelId) => new OpenAISpeechModel(modelId, {
|
|
40354
40354
|
provider: `${providerName}.speech`,
|
|
40355
|
-
url: ({ path:
|
|
40355
|
+
url: ({ path: path12 }) => `${baseURL}${path12}`,
|
|
40356
40356
|
headers: getHeaders,
|
|
40357
40357
|
fetch: options.fetch
|
|
40358
40358
|
});
|
|
@@ -40367,7 +40367,7 @@ function createOpenAI(options = {}) {
|
|
|
40367
40367
|
const createResponsesModel = (modelId) => {
|
|
40368
40368
|
return new OpenAIResponsesLanguageModel(modelId, {
|
|
40369
40369
|
provider: `${providerName}.responses`,
|
|
40370
|
-
url: ({ path:
|
|
40370
|
+
url: ({ path: path12 }) => `${baseURL}${path12}`,
|
|
40371
40371
|
headers: getHeaders,
|
|
40372
40372
|
fetch: options.fetch,
|
|
40373
40373
|
fileIdPrefixes: ["file-"]
|
|
@@ -48942,8 +48942,8 @@ var init_background_process_tracker = __esm({
|
|
|
48942
48942
|
/**
|
|
48943
48943
|
* Normalize file path for comparison
|
|
48944
48944
|
*/
|
|
48945
|
-
normalizePath(
|
|
48946
|
-
let normalized =
|
|
48945
|
+
normalizePath(path12) {
|
|
48946
|
+
let normalized = path12.trim().replace(/\/+/g, "/");
|
|
48947
48947
|
if (normalized.startsWith("./")) {
|
|
48948
48948
|
normalized = normalized.slice(2);
|
|
48949
48949
|
}
|
|
@@ -49181,8 +49181,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
|
|
|
49181
49181
|
return decodedFile;
|
|
49182
49182
|
};
|
|
49183
49183
|
}
|
|
49184
|
-
function normalizeWindowsPath(
|
|
49185
|
-
return
|
|
49184
|
+
function normalizeWindowsPath(path12) {
|
|
49185
|
+
return path12.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
|
|
49186
49186
|
}
|
|
49187
49187
|
var import_path;
|
|
49188
49188
|
var init_module_node = __esm({
|
|
@@ -52071,9 +52071,9 @@ async function addSourceContext(frames) {
|
|
|
52071
52071
|
LRU_FILE_CONTENTS_CACHE.reduce();
|
|
52072
52072
|
return frames;
|
|
52073
52073
|
}
|
|
52074
|
-
function getContextLinesFromFile(
|
|
52074
|
+
function getContextLinesFromFile(path12, ranges, output) {
|
|
52075
52075
|
return new Promise((resolve2) => {
|
|
52076
|
-
const stream = (0, import_node_fs5.createReadStream)(
|
|
52076
|
+
const stream = (0, import_node_fs5.createReadStream)(path12);
|
|
52077
52077
|
const lineReaded = (0, import_node_readline2.createInterface)({
|
|
52078
52078
|
input: stream
|
|
52079
52079
|
});
|
|
@@ -52088,7 +52088,7 @@ function getContextLinesFromFile(path10, ranges, output) {
|
|
|
52088
52088
|
let rangeStart = range[0];
|
|
52089
52089
|
let rangeEnd = range[1];
|
|
52090
52090
|
function onStreamError() {
|
|
52091
|
-
LRU_FILE_CONTENTS_FS_READ_FAILED.set(
|
|
52091
|
+
LRU_FILE_CONTENTS_FS_READ_FAILED.set(path12, 1);
|
|
52092
52092
|
lineReaded.close();
|
|
52093
52093
|
lineReaded.removeAllListeners();
|
|
52094
52094
|
destroyStreamAndResolve();
|
|
@@ -52149,8 +52149,8 @@ function clearLineContext(frame2) {
|
|
|
52149
52149
|
delete frame2.context_line;
|
|
52150
52150
|
delete frame2.post_context;
|
|
52151
52151
|
}
|
|
52152
|
-
function shouldSkipContextLinesForFile(
|
|
52153
|
-
return
|
|
52152
|
+
function shouldSkipContextLinesForFile(path12) {
|
|
52153
|
+
return path12.startsWith("node:") || path12.endsWith(".min.js") || path12.endsWith(".min.cjs") || path12.endsWith(".min.mjs") || path12.startsWith("data:");
|
|
52154
52154
|
}
|
|
52155
52155
|
function shouldSkipContextLinesForFrame(frame2) {
|
|
52156
52156
|
if (void 0 !== frame2.lineno && frame2.lineno > MAX_CONTEXTLINES_LINENO) return true;
|
|
@@ -67560,8 +67560,8 @@ var init_logger2 = __esm({
|
|
|
67560
67560
|
});
|
|
67561
67561
|
|
|
67562
67562
|
// ../lib/ai/tools/file.ts
|
|
67563
|
-
function isSpritPath(
|
|
67564
|
-
return
|
|
67563
|
+
function isSpritPath(path12) {
|
|
67564
|
+
return path12.split(/[\\/]/).some((segment) => segment.toLowerCase() === "sprit");
|
|
67565
67565
|
}
|
|
67566
67566
|
function getViewSandboxType(sandbox) {
|
|
67567
67567
|
return isCentrifugoSandbox(sandbox) ? "centrifugo" : "e2b";
|
|
@@ -67592,7 +67592,7 @@ function captureFileViewImageUsage(args) {
|
|
|
67592
67592
|
const {
|
|
67593
67593
|
context: context2,
|
|
67594
67594
|
sandbox,
|
|
67595
|
-
path:
|
|
67595
|
+
path: path12,
|
|
67596
67596
|
outcome,
|
|
67597
67597
|
durationMs,
|
|
67598
67598
|
mediaType,
|
|
@@ -67610,7 +67610,7 @@ function captureFileViewImageUsage(args) {
|
|
|
67610
67610
|
model: getActiveModelName(context2),
|
|
67611
67611
|
configured_model: context2.modelName,
|
|
67612
67612
|
sandbox_type: getViewSandboxType(sandbox),
|
|
67613
|
-
file_extension: getFileExtension(
|
|
67613
|
+
file_extension: getFileExtension(path12),
|
|
67614
67614
|
outcome,
|
|
67615
67615
|
success: outcome === "success",
|
|
67616
67616
|
duration_ms: durationMs,
|
|
@@ -67684,22 +67684,22 @@ async function runSandboxCommand(sandbox, command, envVars, timeoutMs = 6e4) {
|
|
|
67684
67684
|
function isWindowsSandbox(sandbox) {
|
|
67685
67685
|
return isCentrifugoSandbox(sandbox) && sandbox.isWindows();
|
|
67686
67686
|
}
|
|
67687
|
-
function getWindowsNativePath(
|
|
67688
|
-
if (/^[A-Za-z]:[\\/]/.test(
|
|
67689
|
-
if (
|
|
67690
|
-
return `C:\\temp${
|
|
67687
|
+
function getWindowsNativePath(path12) {
|
|
67688
|
+
if (/^[A-Za-z]:[\\/]/.test(path12)) return path12;
|
|
67689
|
+
if (path12.startsWith("/tmp/")) {
|
|
67690
|
+
return `C:\\temp${path12.slice(4).replace(/\//g, "\\")}`;
|
|
67691
67691
|
}
|
|
67692
|
-
return
|
|
67692
|
+
return path12.replace(/\//g, "\\");
|
|
67693
67693
|
}
|
|
67694
|
-
function getPythonPathForSandbox(sandbox,
|
|
67695
|
-
return isWindowsSandbox(sandbox) ? getWindowsNativePath(
|
|
67694
|
+
function getPythonPathForSandbox(sandbox, path12) {
|
|
67695
|
+
return isWindowsSandbox(sandbox) ? getWindowsNativePath(path12) : path12;
|
|
67696
67696
|
}
|
|
67697
|
-
function toWindowsBashPath(
|
|
67698
|
-
const drive =
|
|
67697
|
+
function toWindowsBashPath(path12) {
|
|
67698
|
+
const drive = path12.match(/^([A-Za-z]):[\\/](.*)$/);
|
|
67699
67699
|
if (drive) {
|
|
67700
67700
|
return `/${drive[1].toLowerCase()}/${drive[2].replace(/\\/g, "/")}`;
|
|
67701
67701
|
}
|
|
67702
|
-
return
|
|
67702
|
+
return path12.replace(/\\/g, "/");
|
|
67703
67703
|
}
|
|
67704
67704
|
async function detectSandboxShell(sandbox) {
|
|
67705
67705
|
if (!isWindowsSandbox(sandbox)) return "bash";
|
|
@@ -67738,8 +67738,8 @@ PY`;
|
|
|
67738
67738
|
}
|
|
67739
67739
|
}
|
|
67740
67740
|
}
|
|
67741
|
-
async function getSandboxFileState(sandbox,
|
|
67742
|
-
const pythonPath = getPythonPathForSandbox(sandbox,
|
|
67741
|
+
async function getSandboxFileState(sandbox, path12) {
|
|
67742
|
+
const pythonPath = getPythonPathForSandbox(sandbox, path12);
|
|
67743
67743
|
const result = await runPythonScript(
|
|
67744
67744
|
sandbox,
|
|
67745
67745
|
FILE_STATE_SCRIPT,
|
|
@@ -67756,23 +67756,23 @@ async function getSandboxFileState(sandbox, path10) {
|
|
|
67756
67756
|
if (result.exitCode !== 0) {
|
|
67757
67757
|
return {
|
|
67758
67758
|
kind: "unknown",
|
|
67759
|
-
path:
|
|
67759
|
+
path: path12,
|
|
67760
67760
|
error: result.stderr || result.stdout || "file state command failed"
|
|
67761
67761
|
};
|
|
67762
67762
|
}
|
|
67763
67763
|
try {
|
|
67764
67764
|
const payload = JSON.parse(result.stdout.trim());
|
|
67765
67765
|
if (payload.kind === "file" && typeof payload.sizeBytes === "number" && Number.isFinite(payload.sizeBytes)) {
|
|
67766
|
-
return { ...payload, path:
|
|
67766
|
+
return { ...payload, path: path12 };
|
|
67767
67767
|
}
|
|
67768
67768
|
if (payload.kind === "missing" || payload.kind === "not_file") {
|
|
67769
|
-
return { ...payload, path:
|
|
67769
|
+
return { ...payload, path: path12 };
|
|
67770
67770
|
}
|
|
67771
67771
|
} catch {
|
|
67772
67772
|
}
|
|
67773
67773
|
return {
|
|
67774
67774
|
kind: "unknown",
|
|
67775
|
-
path:
|
|
67775
|
+
path: path12,
|
|
67776
67776
|
error: result.stderr || result.stdout || "invalid file state response"
|
|
67777
67777
|
};
|
|
67778
67778
|
}
|
|
@@ -67804,8 +67804,8 @@ ${numberedContent}${truncatedNotice}${footerNotice}`;
|
|
|
67804
67804
|
})
|
|
67805
67805
|
};
|
|
67806
67806
|
}
|
|
67807
|
-
async function readSandboxTextFile(sandbox,
|
|
67808
|
-
const pythonPath = getPythonPathForSandbox(sandbox,
|
|
67807
|
+
async function readSandboxTextFile(sandbox, path12, range) {
|
|
67808
|
+
const pythonPath = getPythonPathForSandbox(sandbox, path12);
|
|
67809
67809
|
const envVars = {
|
|
67810
67810
|
HACKERAI_FILE_READ_PATH: pythonPath,
|
|
67811
67811
|
HACKERAI_FILE_READ_RANGE_START: String(range?.[0] ?? 0),
|
|
@@ -67833,40 +67833,40 @@ async function readSandboxTextFile(sandbox, path10, range) {
|
|
|
67833
67833
|
}
|
|
67834
67834
|
return payload;
|
|
67835
67835
|
}
|
|
67836
|
-
async function readSandboxTextFileWithFallback(sandbox,
|
|
67836
|
+
async function readSandboxTextFileWithFallback(sandbox, path12, range) {
|
|
67837
67837
|
try {
|
|
67838
|
-
return await readSandboxTextFile(sandbox,
|
|
67838
|
+
return await readSandboxTextFile(sandbox, path12, range);
|
|
67839
67839
|
} catch (error51) {
|
|
67840
67840
|
const errorMessage = error51 instanceof Error ? error51.message : String(error51);
|
|
67841
67841
|
if (errorMessage.startsWith("Invalid ") || errorMessage.includes("File not found")) {
|
|
67842
67842
|
throw error51;
|
|
67843
67843
|
}
|
|
67844
|
-
const state = await getSandboxFileState(sandbox,
|
|
67844
|
+
const state = await getSandboxFileState(sandbox, path12);
|
|
67845
67845
|
if (state.kind === "unknown") {
|
|
67846
67846
|
throw new Error(
|
|
67847
|
-
`Unable to determine file size for ${
|
|
67847
|
+
`Unable to determine file size for ${path12}; refusing to load the file into memory. ${state.error}`
|
|
67848
67848
|
);
|
|
67849
67849
|
}
|
|
67850
67850
|
if (state.kind === "missing") {
|
|
67851
|
-
throw new Error(`File not found or is not a regular file: ${
|
|
67851
|
+
throw new Error(`File not found or is not a regular file: ${path12}`);
|
|
67852
67852
|
}
|
|
67853
67853
|
if (state.kind === "not_file") {
|
|
67854
|
-
throw new Error(`File is not a regular file: ${
|
|
67854
|
+
throw new Error(`File is not a regular file: ${path12}`);
|
|
67855
67855
|
}
|
|
67856
67856
|
if (state.sizeBytes > MAX_TEXT_FILE_READ_BYTES) {
|
|
67857
67857
|
if (range) {
|
|
67858
67858
|
throw new Error(
|
|
67859
|
-
`Unable to perform a bounded range read for ${
|
|
67859
|
+
`Unable to perform a bounded range read for ${path12}, and the file is too large to load safely (${formatBytes(state.sizeBytes)}). Use a targeted terminal command that writes a small result to a separate file.`
|
|
67860
67860
|
);
|
|
67861
67861
|
}
|
|
67862
67862
|
return {
|
|
67863
|
-
path:
|
|
67863
|
+
path: path12,
|
|
67864
67864
|
sizeBytes: state.sizeBytes,
|
|
67865
67865
|
totalLines: 0,
|
|
67866
67866
|
tooLarge: true
|
|
67867
67867
|
};
|
|
67868
67868
|
}
|
|
67869
|
-
const fileContent = await sandbox.files.read(
|
|
67869
|
+
const fileContent = await sandbox.files.read(path12, {
|
|
67870
67870
|
user: "user"
|
|
67871
67871
|
});
|
|
67872
67872
|
const lines = fileContent.split("\n");
|
|
@@ -67887,15 +67887,10 @@ async function readSandboxTextFileWithFallback(sandbox, path10, range) {
|
|
|
67887
67887
|
`Invalid start_line: ${start}. File has ${lines.length} lines (1-indexed).`
|
|
67888
67888
|
);
|
|
67889
67889
|
}
|
|
67890
|
-
if (end !== -1 && end > lines.length) {
|
|
67891
|
-
throw new Error(
|
|
67892
|
-
`Invalid end_line: ${end}. File has ${lines.length} lines (1-indexed).`
|
|
67893
|
-
);
|
|
67894
|
-
}
|
|
67895
67890
|
const startIndex = start - 1;
|
|
67896
67891
|
const endIndex = end === -1 ? lines.length : end;
|
|
67897
67892
|
return {
|
|
67898
|
-
path:
|
|
67893
|
+
path: path12,
|
|
67899
67894
|
sizeBytes: Buffer.byteLength(fileContent),
|
|
67900
67895
|
totalLines: lines.length,
|
|
67901
67896
|
content: lines.slice(startIndex, endIndex).join("\n"),
|
|
@@ -67903,7 +67898,7 @@ async function readSandboxTextFileWithFallback(sandbox, path10, range) {
|
|
|
67903
67898
|
};
|
|
67904
67899
|
}
|
|
67905
67900
|
return {
|
|
67906
|
-
path:
|
|
67901
|
+
path: path12,
|
|
67907
67902
|
sizeBytes: Buffer.byteLength(fileContent),
|
|
67908
67903
|
totalLines: lines.length,
|
|
67909
67904
|
content: fileContent,
|
|
@@ -67911,7 +67906,7 @@ async function readSandboxTextFileWithFallback(sandbox, path10, range) {
|
|
|
67911
67906
|
};
|
|
67912
67907
|
}
|
|
67913
67908
|
}
|
|
67914
|
-
async function appendSandboxTextFile(sandbox,
|
|
67909
|
+
async function appendSandboxTextFile(sandbox, path12, text2) {
|
|
67915
67910
|
const tempPath = `/tmp/hackerai_append_${Date.now()}_${Math.random().toString(36).slice(2)}.tmp`;
|
|
67916
67911
|
await sandbox.files.write(tempPath, text2, {
|
|
67917
67912
|
user: "user"
|
|
@@ -67920,7 +67915,7 @@ async function appendSandboxTextFile(sandbox, path10, text2) {
|
|
|
67920
67915
|
sandbox,
|
|
67921
67916
|
APPEND_TEXT_FILE_SCRIPT,
|
|
67922
67917
|
{
|
|
67923
|
-
HACKERAI_FILE_APPEND_TARGET_PATH: getPythonPathForSandbox(sandbox,
|
|
67918
|
+
HACKERAI_FILE_APPEND_TARGET_PATH: getPythonPathForSandbox(sandbox, path12),
|
|
67924
67919
|
HACKERAI_FILE_APPEND_SOURCE_PATH: getPythonPathForSandbox(
|
|
67925
67920
|
sandbox,
|
|
67926
67921
|
tempPath
|
|
@@ -67932,13 +67927,13 @@ async function appendSandboxTextFile(sandbox, path10, text2) {
|
|
|
67932
67927
|
throw new Error(result.stderr || result.stdout || "Failed to append file");
|
|
67933
67928
|
}
|
|
67934
67929
|
}
|
|
67935
|
-
async function readSandboxFileForView(sandbox,
|
|
67930
|
+
async function readSandboxFileForView(sandbox, path12, includeData) {
|
|
67936
67931
|
if (isCentrifugoSandbox(sandbox) && sandbox.isWindows()) {
|
|
67937
67932
|
throw new Error(
|
|
67938
67933
|
"The view action is not available for Windows local sandboxes yet. Use a Linux/E2B sandbox or inspect the image manually."
|
|
67939
67934
|
);
|
|
67940
67935
|
}
|
|
67941
|
-
const sandboxPath = getSandboxViewPath(sandbox,
|
|
67936
|
+
const sandboxPath = getSandboxViewPath(sandbox, path12);
|
|
67942
67937
|
const viewEnvVars = {
|
|
67943
67938
|
HACKERAI_FILE_VIEW_PATH: sandboxPath,
|
|
67944
67939
|
HACKERAI_FILE_VIEW_INCLUDE_DATA: includeData ? "1" : "0",
|
|
@@ -68198,8 +68193,9 @@ if range_start == 0 and size > max_full_bytes:
|
|
|
68198
68193
|
if range_start > 0:
|
|
68199
68194
|
if range_start > total_lines:
|
|
68200
68195
|
emit({"error": f"Invalid start_line: {range_start}. File has {total_lines} lines (1-indexed)."}, 2)
|
|
68201
|
-
|
|
68202
|
-
|
|
68196
|
+
# An end_line past EOF is harmless — the read loop simply stops at the last
|
|
68197
|
+
# line. (We used to error here, which broke the SPRIT chunked read: it always
|
|
68198
|
+
# requests a 100-line window, so every SPRIT file shorter than 100 lines failed.)
|
|
68203
68199
|
|
|
68204
68200
|
selected = []
|
|
68205
68201
|
selected_bytes = 0
|
|
@@ -68281,19 +68277,19 @@ try:
|
|
|
68281
68277
|
except OSError:
|
|
68282
68278
|
pass
|
|
68283
68279
|
`;
|
|
68284
|
-
getFilename = (
|
|
68285
|
-
getFileExtension = (
|
|
68286
|
-
const filename = getFilename(
|
|
68280
|
+
getFilename = (path12) => path12.split("/").pop() || path12;
|
|
68281
|
+
getFileExtension = (path12) => {
|
|
68282
|
+
const filename = getFilename(path12);
|
|
68287
68283
|
const dotIndex = filename.lastIndexOf(".");
|
|
68288
68284
|
if (dotIndex <= 0 || dotIndex === filename.length - 1) return void 0;
|
|
68289
68285
|
return filename.slice(dotIndex + 1).toLowerCase();
|
|
68290
68286
|
};
|
|
68291
|
-
getSandboxViewPath = (sandbox,
|
|
68287
|
+
getSandboxViewPath = (sandbox, path12) => {
|
|
68292
68288
|
const maybeSandbox = sandbox;
|
|
68293
|
-
if (isCentrifugoSandbox(maybeSandbox) && maybeSandbox.isWindows() &&
|
|
68294
|
-
return `C:\\temp${
|
|
68289
|
+
if (isCentrifugoSandbox(maybeSandbox) && maybeSandbox.isWindows() && path12.startsWith("/tmp/")) {
|
|
68290
|
+
return `C:\\temp${path12.slice(4).replace(/\//g, "\\")}`;
|
|
68295
68291
|
}
|
|
68296
|
-
return
|
|
68292
|
+
return path12;
|
|
68297
68293
|
};
|
|
68298
68294
|
stripTrailingWs = (line) => line.replace(/[ \t]+$/u, "");
|
|
68299
68295
|
editSchema = external_exports.object({
|
|
@@ -68367,7 +68363,7 @@ ${instructionsDescription}`,
|
|
|
68367
68363
|
"A list of edits to be sequentially applied to the file. Required for `edit` action."
|
|
68368
68364
|
)
|
|
68369
68365
|
}),
|
|
68370
|
-
execute: async ({ action, path:
|
|
68366
|
+
execute: async ({ action, path: path12, text: text2, range, edits }) => {
|
|
68371
68367
|
try {
|
|
68372
68368
|
const { sandbox } = await sandboxManager.getSandbox();
|
|
68373
68369
|
switch (action) {
|
|
@@ -68377,7 +68373,7 @@ ${instructionsDescription}`,
|
|
|
68377
68373
|
captureFileViewImageUsage({
|
|
68378
68374
|
context: context2,
|
|
68379
68375
|
sandbox,
|
|
68380
|
-
path:
|
|
68376
|
+
path: path12,
|
|
68381
68377
|
outcome: "unsupported_model",
|
|
68382
68378
|
durationMs: Date.now() - viewStartedAt,
|
|
68383
68379
|
failureReason: "unsupported_model"
|
|
@@ -68386,26 +68382,26 @@ ${instructionsDescription}`,
|
|
|
68386
68382
|
}
|
|
68387
68383
|
let viewPayload;
|
|
68388
68384
|
try {
|
|
68389
|
-
viewPayload = await readSandboxFileForView(sandbox,
|
|
68385
|
+
viewPayload = await readSandboxFileForView(sandbox, path12, false);
|
|
68390
68386
|
} catch (error51) {
|
|
68391
68387
|
captureFileViewImageUsage({
|
|
68392
68388
|
context: context2,
|
|
68393
68389
|
sandbox,
|
|
68394
|
-
path:
|
|
68390
|
+
path: path12,
|
|
68395
68391
|
outcome: "inspection_failed",
|
|
68396
68392
|
durationMs: Date.now() - viewStartedAt,
|
|
68397
68393
|
failureReason: classifyFileViewError(error51)
|
|
68398
68394
|
});
|
|
68399
68395
|
throw error51;
|
|
68400
68396
|
}
|
|
68401
|
-
const filename = getFilename(
|
|
68397
|
+
const filename = getFilename(path12);
|
|
68402
68398
|
let previewFiles = [];
|
|
68403
68399
|
let previewUploadError;
|
|
68404
68400
|
try {
|
|
68405
68401
|
previewFiles = await uploadViewPreviewFiles({
|
|
68406
68402
|
context: context2,
|
|
68407
68403
|
sandbox,
|
|
68408
|
-
sourcePath:
|
|
68404
|
+
sourcePath: path12,
|
|
68409
68405
|
payload: viewPayload
|
|
68410
68406
|
});
|
|
68411
68407
|
} catch (error51) {
|
|
@@ -68419,7 +68415,7 @@ ${instructionsDescription}`,
|
|
|
68419
68415
|
user_id: context2.userID,
|
|
68420
68416
|
sandbox_type: getViewSandboxType(sandbox),
|
|
68421
68417
|
file_name: filename,
|
|
68422
|
-
source_path:
|
|
68418
|
+
source_path: path12,
|
|
68423
68419
|
kind: viewPayload.kind,
|
|
68424
68420
|
media_type: viewPayload.mediaType,
|
|
68425
68421
|
size_bytes: viewPayload.sizeBytes,
|
|
@@ -68430,7 +68426,7 @@ ${instructionsDescription}`,
|
|
|
68430
68426
|
captureFileViewImageUsage({
|
|
68431
68427
|
context: context2,
|
|
68432
68428
|
sandbox,
|
|
68433
|
-
path:
|
|
68429
|
+
path: path12,
|
|
68434
68430
|
outcome: "success",
|
|
68435
68431
|
durationMs: Date.now() - viewStartedAt,
|
|
68436
68432
|
mediaType: viewPayload.mediaType,
|
|
@@ -68440,7 +68436,7 @@ ${instructionsDescription}`,
|
|
|
68440
68436
|
return {
|
|
68441
68437
|
action: "view",
|
|
68442
68438
|
content: `Viewing image file: ${filename} (${viewPayload.mediaType}, ${viewPayload.sizeBytes} bytes).`,
|
|
68443
|
-
path:
|
|
68439
|
+
path: path12,
|
|
68444
68440
|
filename,
|
|
68445
68441
|
mediaType: viewPayload.mediaType,
|
|
68446
68442
|
sizeBytes: viewPayload.sizeBytes,
|
|
@@ -68450,8 +68446,8 @@ ${instructionsDescription}`,
|
|
|
68450
68446
|
};
|
|
68451
68447
|
}
|
|
68452
68448
|
case "read": {
|
|
68453
|
-
const filename =
|
|
68454
|
-
const spritChunked = isSpritPath(
|
|
68449
|
+
const filename = path12.split("/").pop() || path12;
|
|
68450
|
+
const spritChunked = isSpritPath(path12);
|
|
68455
68451
|
let effectiveRange = range;
|
|
68456
68452
|
if (spritChunked) {
|
|
68457
68453
|
const start = range && range[0] > 0 ? range[0] : 1;
|
|
@@ -68464,7 +68460,7 @@ ${instructionsDescription}`,
|
|
|
68464
68460
|
}
|
|
68465
68461
|
const readPayload = await readSandboxTextFileWithFallback(
|
|
68466
68462
|
sandbox,
|
|
68467
|
-
|
|
68463
|
+
path12,
|
|
68468
68464
|
effectiveRange
|
|
68469
68465
|
);
|
|
68470
68466
|
if (readPayload.tooLarge) {
|
|
@@ -68501,46 +68497,46 @@ File is too large to read in full (${formatBytes(readPayload.sizeBytes)}, ${tota
|
|
|
68501
68497
|
if (text2 === void 0) {
|
|
68502
68498
|
return { error: "text is required for write action" };
|
|
68503
68499
|
}
|
|
68504
|
-
await sandbox.files.write(
|
|
68500
|
+
await sandbox.files.write(path12, text2, {
|
|
68505
68501
|
user: "user"
|
|
68506
68502
|
});
|
|
68507
|
-
return `File written: ${
|
|
68503
|
+
return `File written: ${path12}`;
|
|
68508
68504
|
}
|
|
68509
68505
|
case "append": {
|
|
68510
68506
|
if (text2 === void 0) {
|
|
68511
68507
|
return { error: "text is required for append action" };
|
|
68512
68508
|
}
|
|
68513
|
-
const existingState = await getSandboxFileState(sandbox,
|
|
68509
|
+
const existingState = await getSandboxFileState(sandbox, path12);
|
|
68514
68510
|
if (existingState.kind === "unknown") {
|
|
68515
68511
|
return {
|
|
68516
|
-
error: `Cannot append safely because the existing file size could not be determined for ${
|
|
68512
|
+
error: `Cannot append safely because the existing file size could not be determined for ${path12}. ${existingState.error}`
|
|
68517
68513
|
};
|
|
68518
68514
|
}
|
|
68519
68515
|
if (existingState.kind === "not_file") {
|
|
68520
68516
|
return {
|
|
68521
|
-
error: `Cannot append to ${
|
|
68517
|
+
error: `Cannot append to ${path12} because it is not a file.`
|
|
68522
68518
|
};
|
|
68523
68519
|
}
|
|
68524
68520
|
if (existingState.kind === "file" && existingState.sizeBytes > MAX_TEXT_FILE_READ_BYTES) {
|
|
68525
|
-
await appendSandboxTextFile(sandbox,
|
|
68521
|
+
await appendSandboxTextFile(sandbox, path12, text2);
|
|
68526
68522
|
return {
|
|
68527
|
-
content: `File appended: ${
|
|
68523
|
+
content: `File appended: ${path12}
|
|
68528
68524
|
Existing file is ${formatBytes(existingState.sizeBytes)}, so the full diff preview was skipped to avoid loading the entire file into memory.`
|
|
68529
68525
|
};
|
|
68530
68526
|
}
|
|
68531
68527
|
let existingContent = "";
|
|
68532
68528
|
try {
|
|
68533
|
-
existingContent = await sandbox.files.read(
|
|
68529
|
+
existingContent = await sandbox.files.read(path12, {
|
|
68534
68530
|
user: "user"
|
|
68535
68531
|
});
|
|
68536
68532
|
} catch {
|
|
68537
68533
|
}
|
|
68538
68534
|
const newContent = existingContent + text2;
|
|
68539
|
-
await sandbox.files.write(
|
|
68535
|
+
await sandbox.files.write(path12, newContent, {
|
|
68540
68536
|
user: "user"
|
|
68541
68537
|
});
|
|
68542
68538
|
return {
|
|
68543
|
-
content: `File appended: ${
|
|
68539
|
+
content: `File appended: ${path12}`,
|
|
68544
68540
|
originalContent: truncateOutput({
|
|
68545
68541
|
content: existingContent,
|
|
68546
68542
|
mode: "read-file"
|
|
@@ -68555,31 +68551,31 @@ Existing file is ${formatBytes(existingState.sizeBytes)}, so the full diff previ
|
|
|
68555
68551
|
if (!edits || edits.length === 0) {
|
|
68556
68552
|
return { error: "edits array is required for edit action" };
|
|
68557
68553
|
}
|
|
68558
|
-
const existingState = await getSandboxFileState(sandbox,
|
|
68554
|
+
const existingState = await getSandboxFileState(sandbox, path12);
|
|
68559
68555
|
if (existingState.kind === "unknown") {
|
|
68560
68556
|
return {
|
|
68561
|
-
error: `Cannot edit ${
|
|
68557
|
+
error: `Cannot edit ${path12} safely because the file size could not be determined. ${existingState.error}`
|
|
68562
68558
|
};
|
|
68563
68559
|
}
|
|
68564
68560
|
if (existingState.kind === "missing") {
|
|
68565
68561
|
return {
|
|
68566
|
-
error: `Cannot edit file ${
|
|
68562
|
+
error: `Cannot edit file ${path12} - file is empty or does not exist`
|
|
68567
68563
|
};
|
|
68568
68564
|
}
|
|
68569
68565
|
if (existingState.kind === "not_file") {
|
|
68570
|
-
return { error: `Cannot edit ${
|
|
68566
|
+
return { error: `Cannot edit ${path12} because it is not a file.` };
|
|
68571
68567
|
}
|
|
68572
68568
|
if (existingState.sizeBytes > MAX_TEXT_FILE_READ_BYTES) {
|
|
68573
68569
|
return {
|
|
68574
|
-
error: `File ${
|
|
68570
|
+
error: `File ${path12} is too large for the edit action (${formatBytes(existingState.sizeBytes)}). Use a targeted shell command, restore the file from a clean source, or replace it with the write action instead of loading the whole file into memory.`
|
|
68575
68571
|
};
|
|
68576
68572
|
}
|
|
68577
|
-
const originalContent = await sandbox.files.read(
|
|
68573
|
+
const originalContent = await sandbox.files.read(path12, {
|
|
68578
68574
|
user: "user"
|
|
68579
68575
|
});
|
|
68580
68576
|
if (!originalContent) {
|
|
68581
68577
|
return {
|
|
68582
|
-
error: `Cannot edit file ${
|
|
68578
|
+
error: `Cannot edit file ${path12} - file is empty or does not exist`
|
|
68583
68579
|
};
|
|
68584
68580
|
}
|
|
68585
68581
|
const resolvedEdits = [];
|
|
@@ -68624,7 +68620,7 @@ ${hint}` : "")
|
|
|
68624
68620
|
}
|
|
68625
68621
|
}
|
|
68626
68622
|
}
|
|
68627
|
-
await sandbox.files.write(
|
|
68623
|
+
await sandbox.files.write(path12, content, {
|
|
68628
68624
|
user: "user"
|
|
68629
68625
|
});
|
|
68630
68626
|
const lines = content.split("\n");
|
|
@@ -68716,6 +68712,1120 @@ ${numberedLines}`,
|
|
|
68716
68712
|
}
|
|
68717
68713
|
});
|
|
68718
68714
|
|
|
68715
|
+
// ../lib/utils/error-redaction.ts
|
|
68716
|
+
var REDACTED_VALUE, SENSITIVE_FIELD_PATTERN, ENV_SECRET_PATTERN, redactSensitiveErrorMessage, stringifyRedactedError;
|
|
68717
|
+
var init_error_redaction = __esm({
|
|
68718
|
+
"../lib/utils/error-redaction.ts"() {
|
|
68719
|
+
"use strict";
|
|
68720
|
+
REDACTED_VALUE = "[Redacted]";
|
|
68721
|
+
SENSITIVE_FIELD_PATTERN = /(["']?\b(?:serviceKey|service_key|apiKey|api_key|authorization|bearer|cookie|password|secret|token)\b["']?)(\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,}]+)/gi;
|
|
68722
|
+
ENV_SECRET_PATTERN = /(["']?\b(?:CONVEX_SERVICE_ROLE_KEY|POSTHOG_API_KEY|STRIPE_SECRET_KEY)\b["']?)(\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,}]+)/gi;
|
|
68723
|
+
redactSensitiveErrorMessage = (message) => message.replace(SENSITIVE_FIELD_PATTERN, (_match, key, separator) => {
|
|
68724
|
+
return `${key}${separator}"${REDACTED_VALUE}"`;
|
|
68725
|
+
}).replace(ENV_SECRET_PATTERN, (_match, key, separator) => {
|
|
68726
|
+
return `${key}${separator}"${REDACTED_VALUE}"`;
|
|
68727
|
+
});
|
|
68728
|
+
stringifyRedactedError = (error51) => {
|
|
68729
|
+
const message = error51 instanceof Error ? error51.message : typeof error51 === "string" ? error51 : (() => {
|
|
68730
|
+
try {
|
|
68731
|
+
return JSON.stringify(error51);
|
|
68732
|
+
} catch {
|
|
68733
|
+
return String(error51);
|
|
68734
|
+
}
|
|
68735
|
+
})();
|
|
68736
|
+
return redactSensitiveErrorMessage(message);
|
|
68737
|
+
};
|
|
68738
|
+
}
|
|
68739
|
+
});
|
|
68740
|
+
|
|
68741
|
+
// ../lib/ai/tools/utils/perplexity.ts
|
|
68742
|
+
var SEARCH_RESULT_CONTENT_MAX_TOKENS, RECENCY_MAP, PerplexityApiError, ERROR_BODY_SUMMARY_MAX_LENGTH, RETRYABLE_PERPLEXITY_STATUSES, HTML_ENTITY_MAP, normalizeWhitespace, decodeBasicHtmlEntities, stripHtml, extractHtmlTagText, redactNetworkDetails, truncateSummary, isRetryablePerplexityStatus, summarizePerplexityErrorBody, buildPerplexitySearchBody, formatSearchResults;
|
|
68743
|
+
var init_perplexity = __esm({
|
|
68744
|
+
"../lib/ai/tools/utils/perplexity.ts"() {
|
|
68745
|
+
"use strict";
|
|
68746
|
+
SEARCH_RESULT_CONTENT_MAX_TOKENS = 250;
|
|
68747
|
+
RECENCY_MAP = {
|
|
68748
|
+
past_day: "day",
|
|
68749
|
+
past_week: "week",
|
|
68750
|
+
past_month: "month",
|
|
68751
|
+
past_year: "year"
|
|
68752
|
+
};
|
|
68753
|
+
PerplexityApiError = class extends Error {
|
|
68754
|
+
constructor({
|
|
68755
|
+
status,
|
|
68756
|
+
statusText,
|
|
68757
|
+
bodySummary,
|
|
68758
|
+
retryable
|
|
68759
|
+
}) {
|
|
68760
|
+
const statusLabel = statusText ? `${status} ${statusText}` : `${status}`;
|
|
68761
|
+
const summary = bodySummary ? `: ${bodySummary}` : "";
|
|
68762
|
+
super(`Perplexity API error ${statusLabel}${summary}`);
|
|
68763
|
+
this.name = "PerplexityApiError";
|
|
68764
|
+
this.status = status;
|
|
68765
|
+
this.statusText = statusText;
|
|
68766
|
+
this.bodySummary = bodySummary;
|
|
68767
|
+
this.retryable = retryable;
|
|
68768
|
+
}
|
|
68769
|
+
};
|
|
68770
|
+
ERROR_BODY_SUMMARY_MAX_LENGTH = 400;
|
|
68771
|
+
RETRYABLE_PERPLEXITY_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
68772
|
+
HTML_ENTITY_MAP = {
|
|
68773
|
+
amp: "&",
|
|
68774
|
+
gt: ">",
|
|
68775
|
+
lt: "<",
|
|
68776
|
+
nbsp: " ",
|
|
68777
|
+
quot: '"',
|
|
68778
|
+
"#160": " ",
|
|
68779
|
+
"#39": "'"
|
|
68780
|
+
};
|
|
68781
|
+
normalizeWhitespace = (value) => value.replace(/\s+/g, " ").trim();
|
|
68782
|
+
decodeBasicHtmlEntities = (value) => value.replace(/&([a-zA-Z0-9#]+);/g, (match, entity) => {
|
|
68783
|
+
return HTML_ENTITY_MAP[entity] ?? match;
|
|
68784
|
+
});
|
|
68785
|
+
stripHtml = (value) => normalizeWhitespace(
|
|
68786
|
+
decodeBasicHtmlEntities(
|
|
68787
|
+
value.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, " ").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ")
|
|
68788
|
+
)
|
|
68789
|
+
);
|
|
68790
|
+
extractHtmlTagText = (html, tagName) => {
|
|
68791
|
+
const matches = html.matchAll(
|
|
68792
|
+
new RegExp(`<${tagName}\\b[^>]*>([\\s\\S]*?)<\\/${tagName}>`, "gi")
|
|
68793
|
+
);
|
|
68794
|
+
return Array.from(matches).map((match) => stripHtml(match[1] ?? "")).filter(Boolean);
|
|
68795
|
+
};
|
|
68796
|
+
redactNetworkDetails = (value) => value.replace(
|
|
68797
|
+
/\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)\b/g,
|
|
68798
|
+
"[Redacted IP]"
|
|
68799
|
+
).replace(/\bRay ID:\s*[a-f0-9]+\b/gi, "Ray ID: [Redacted]");
|
|
68800
|
+
truncateSummary = (value) => value.length > ERROR_BODY_SUMMARY_MAX_LENGTH ? `${value.slice(0, ERROR_BODY_SUMMARY_MAX_LENGTH - 1)}\u2026` : value;
|
|
68801
|
+
isRetryablePerplexityStatus = (status) => RETRYABLE_PERPLEXITY_STATUSES.has(status);
|
|
68802
|
+
summarizePerplexityErrorBody = (body, contentType = "") => {
|
|
68803
|
+
const trimmed = body.trim();
|
|
68804
|
+
if (!trimmed) return "";
|
|
68805
|
+
let summary = "";
|
|
68806
|
+
if (contentType.includes("json") || trimmed.startsWith("{")) {
|
|
68807
|
+
try {
|
|
68808
|
+
const parsed = JSON.parse(trimmed);
|
|
68809
|
+
const error51 = typeof parsed.error === "string" ? parsed.error : parsed.error && typeof parsed.error === "object" && "message" in parsed.error && typeof parsed.error.message === "string" ? parsed.error.message : void 0;
|
|
68810
|
+
const message = error51 || (typeof parsed.message === "string" ? parsed.message : void 0) || (typeof parsed.detail === "string" ? parsed.detail : void 0);
|
|
68811
|
+
summary = message || trimmed;
|
|
68812
|
+
} catch {
|
|
68813
|
+
summary = trimmed;
|
|
68814
|
+
}
|
|
68815
|
+
} else if (/<[a-z][\s\S]*>/i.test(trimmed)) {
|
|
68816
|
+
const tagSummaries = [
|
|
68817
|
+
...extractHtmlTagText(trimmed, "h1"),
|
|
68818
|
+
...extractHtmlTagText(trimmed, "p"),
|
|
68819
|
+
...extractHtmlTagText(trimmed, "h2")
|
|
68820
|
+
];
|
|
68821
|
+
summary = tagSummaries.length > 0 ? tagSummaries.join(". ") : stripHtml(trimmed);
|
|
68822
|
+
} else {
|
|
68823
|
+
summary = trimmed;
|
|
68824
|
+
}
|
|
68825
|
+
return truncateSummary(redactNetworkDetails(normalizeWhitespace(summary)));
|
|
68826
|
+
};
|
|
68827
|
+
buildPerplexitySearchBody = (query, options) => {
|
|
68828
|
+
const searchBody = {
|
|
68829
|
+
query,
|
|
68830
|
+
max_results: options?.maxResults ?? 10,
|
|
68831
|
+
max_tokens_per_page: SEARCH_RESULT_CONTENT_MAX_TOKENS
|
|
68832
|
+
};
|
|
68833
|
+
if (options?.country) {
|
|
68834
|
+
searchBody.country = options.country;
|
|
68835
|
+
}
|
|
68836
|
+
if (options?.recency) {
|
|
68837
|
+
searchBody.search_recency_filter = options.recency;
|
|
68838
|
+
}
|
|
68839
|
+
return searchBody;
|
|
68840
|
+
};
|
|
68841
|
+
formatSearchResults = (results) => {
|
|
68842
|
+
return results.map((result) => ({
|
|
68843
|
+
title: result.title,
|
|
68844
|
+
url: result.url,
|
|
68845
|
+
content: result.snippet,
|
|
68846
|
+
date: result.date || null,
|
|
68847
|
+
lastUpdated: result.last_updated || null
|
|
68848
|
+
}));
|
|
68849
|
+
};
|
|
68850
|
+
}
|
|
68851
|
+
});
|
|
68852
|
+
|
|
68853
|
+
// ../lib/ai/tools/web-search.ts
|
|
68854
|
+
var WEB_SEARCH_COST_PER_REQUEST, PERPLEXITY_SEARCH_URL, WEB_SEARCH_MAX_ATTEMPTS, WEB_SEARCH_RETRY_BASE_DELAY_MS, WEB_SEARCH_RETRY_JITTER_MS, PERPLEXITY_QUERY_MAX_LENGTH, EMPTY_QUERY_TOOL_ERROR, QUERY_TOO_LONG_TOOL_ERROR, webSearchQuerySchema, sleep, getRetryDelayMs, createPerplexityApiError, formatPerplexityFailureForTool, fetchPerplexitySearch, normalizeSearchQueries, createWebSearch;
|
|
68855
|
+
var init_web_search = __esm({
|
|
68856
|
+
"../lib/ai/tools/web-search.ts"() {
|
|
68857
|
+
"use strict";
|
|
68858
|
+
init_dist5();
|
|
68859
|
+
init_zod();
|
|
68860
|
+
init_error_redaction();
|
|
68861
|
+
init_perplexity();
|
|
68862
|
+
WEB_SEARCH_COST_PER_REQUEST = 5e-3;
|
|
68863
|
+
PERPLEXITY_SEARCH_URL = "https://api.perplexity.ai/search";
|
|
68864
|
+
WEB_SEARCH_MAX_ATTEMPTS = 3;
|
|
68865
|
+
WEB_SEARCH_RETRY_BASE_DELAY_MS = 300;
|
|
68866
|
+
WEB_SEARCH_RETRY_JITTER_MS = 75;
|
|
68867
|
+
PERPLEXITY_QUERY_MAX_LENGTH = 8192;
|
|
68868
|
+
EMPTY_QUERY_TOOL_ERROR = "Error performing web search: Provide at least one non-empty query.";
|
|
68869
|
+
QUERY_TOO_LONG_TOOL_ERROR = `Error performing web search: Each query must be ${PERPLEXITY_QUERY_MAX_LENGTH} characters or fewer.`;
|
|
68870
|
+
webSearchQuerySchema = external_exports.string().trim().min(1).max(PERPLEXITY_QUERY_MAX_LENGTH);
|
|
68871
|
+
sleep = (delayMs, signal) => {
|
|
68872
|
+
if (delayMs <= 0) return Promise.resolve();
|
|
68873
|
+
if (signal?.aborted) {
|
|
68874
|
+
return Promise.reject(new DOMException("Operation aborted", "AbortError"));
|
|
68875
|
+
}
|
|
68876
|
+
return new Promise((resolve2, reject) => {
|
|
68877
|
+
const cleanup = () => signal?.removeEventListener("abort", onAbort);
|
|
68878
|
+
const onAbort = () => {
|
|
68879
|
+
clearTimeout(timeout);
|
|
68880
|
+
cleanup();
|
|
68881
|
+
reject(new DOMException("Operation aborted", "AbortError"));
|
|
68882
|
+
};
|
|
68883
|
+
const timeout = setTimeout(() => {
|
|
68884
|
+
cleanup();
|
|
68885
|
+
resolve2();
|
|
68886
|
+
}, delayMs);
|
|
68887
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
68888
|
+
});
|
|
68889
|
+
};
|
|
68890
|
+
getRetryDelayMs = (attemptIndex) => {
|
|
68891
|
+
const exponentialDelay = WEB_SEARCH_RETRY_BASE_DELAY_MS * Math.pow(2, attemptIndex);
|
|
68892
|
+
const jitter = Math.random() * WEB_SEARCH_RETRY_JITTER_MS;
|
|
68893
|
+
return Math.round(exponentialDelay + jitter);
|
|
68894
|
+
};
|
|
68895
|
+
createPerplexityApiError = async (response) => {
|
|
68896
|
+
const errorText = await response.text();
|
|
68897
|
+
const bodySummary = summarizePerplexityErrorBody(
|
|
68898
|
+
errorText,
|
|
68899
|
+
response.headers.get("content-type") || ""
|
|
68900
|
+
);
|
|
68901
|
+
return new PerplexityApiError({
|
|
68902
|
+
status: response.status,
|
|
68903
|
+
statusText: response.statusText,
|
|
68904
|
+
bodySummary,
|
|
68905
|
+
retryable: isRetryablePerplexityStatus(response.status)
|
|
68906
|
+
});
|
|
68907
|
+
};
|
|
68908
|
+
formatPerplexityFailureForTool = (error51, attempts) => {
|
|
68909
|
+
const statusText = error51.statusText ? ` ${error51.statusText}` : "";
|
|
68910
|
+
if (error51.retryable) {
|
|
68911
|
+
return `Error performing web search: Perplexity search is temporarily unavailable (HTTP ${error51.status}${statusText} after ${attempts} attempts). Please retry shortly or continue without live web results if the task can proceed.`;
|
|
68912
|
+
}
|
|
68913
|
+
if (error51.status === 401 || error51.status === 403) {
|
|
68914
|
+
return `Error performing web search: Perplexity search is not authorized (HTTP ${error51.status}${statusText}). Check the Perplexity API key or account access.`;
|
|
68915
|
+
}
|
|
68916
|
+
return `Error performing web search: Perplexity search failed (HTTP ${error51.status}${statusText}).`;
|
|
68917
|
+
};
|
|
68918
|
+
fetchPerplexitySearch = async (searchBody, abortSignal) => {
|
|
68919
|
+
for (let attemptIndex = 0; attemptIndex < WEB_SEARCH_MAX_ATTEMPTS; attemptIndex++) {
|
|
68920
|
+
const attempt = attemptIndex + 1;
|
|
68921
|
+
const isFinalAttempt = attempt === WEB_SEARCH_MAX_ATTEMPTS;
|
|
68922
|
+
try {
|
|
68923
|
+
const response = await fetch(PERPLEXITY_SEARCH_URL, {
|
|
68924
|
+
method: "POST",
|
|
68925
|
+
headers: {
|
|
68926
|
+
"Content-Type": "application/json",
|
|
68927
|
+
Authorization: `Bearer ${process.env.PERPLEXITY_API_KEY || ""}`
|
|
68928
|
+
},
|
|
68929
|
+
body: JSON.stringify(searchBody),
|
|
68930
|
+
signal: abortSignal
|
|
68931
|
+
});
|
|
68932
|
+
if (response.ok) {
|
|
68933
|
+
return response;
|
|
68934
|
+
}
|
|
68935
|
+
const error51 = await createPerplexityApiError(response);
|
|
68936
|
+
if (!error51.retryable || isFinalAttempt) {
|
|
68937
|
+
throw error51;
|
|
68938
|
+
}
|
|
68939
|
+
const delayMs = getRetryDelayMs(attemptIndex);
|
|
68940
|
+
console.warn("Web search provider error; retrying", {
|
|
68941
|
+
attempt,
|
|
68942
|
+
maxAttempts: WEB_SEARCH_MAX_ATTEMPTS,
|
|
68943
|
+
status: error51.status,
|
|
68944
|
+
statusText: error51.statusText,
|
|
68945
|
+
bodySummary: error51.bodySummary,
|
|
68946
|
+
delayMs
|
|
68947
|
+
});
|
|
68948
|
+
await sleep(delayMs, abortSignal);
|
|
68949
|
+
} catch (error51) {
|
|
68950
|
+
if (error51 instanceof Error && error51.name === "AbortError") {
|
|
68951
|
+
throw error51;
|
|
68952
|
+
}
|
|
68953
|
+
if (error51 instanceof PerplexityApiError) {
|
|
68954
|
+
throw error51;
|
|
68955
|
+
}
|
|
68956
|
+
if (isFinalAttempt) {
|
|
68957
|
+
throw error51;
|
|
68958
|
+
}
|
|
68959
|
+
const delayMs = getRetryDelayMs(attemptIndex);
|
|
68960
|
+
console.warn("Web search network error; retrying", {
|
|
68961
|
+
attempt,
|
|
68962
|
+
maxAttempts: WEB_SEARCH_MAX_ATTEMPTS,
|
|
68963
|
+
error: stringifyRedactedError(error51),
|
|
68964
|
+
delayMs
|
|
68965
|
+
});
|
|
68966
|
+
await sleep(delayMs, abortSignal);
|
|
68967
|
+
}
|
|
68968
|
+
}
|
|
68969
|
+
throw new Error("Web search failed before any Perplexity response was read");
|
|
68970
|
+
};
|
|
68971
|
+
normalizeSearchQueries = (rawQueries) => {
|
|
68972
|
+
const queries = rawQueries.map((query) => query.trim()).filter(Boolean);
|
|
68973
|
+
if (queries.length === 0) {
|
|
68974
|
+
return { queries, error: EMPTY_QUERY_TOOL_ERROR };
|
|
68975
|
+
}
|
|
68976
|
+
if (queries.some((query) => query.length > PERPLEXITY_QUERY_MAX_LENGTH)) {
|
|
68977
|
+
return { queries, error: QUERY_TOO_LONG_TOOL_ERROR };
|
|
68978
|
+
}
|
|
68979
|
+
return { queries: queries.slice(0, 3) };
|
|
68980
|
+
};
|
|
68981
|
+
createWebSearch = (context2) => {
|
|
68982
|
+
const { userLocation, onToolCost } = context2;
|
|
68983
|
+
return tool({
|
|
68984
|
+
description: `Search for information across various sources.
|
|
68985
|
+
|
|
68986
|
+
<instructions>
|
|
68987
|
+
- MUST use this tool to access up-to-date or external information when needed; DO NOT rely solely on internal knowledge
|
|
68988
|
+
- Each search MUST contain exactly 1 to 3 \`queries\` (NEVER more than 3). Queries MUST be variants of the same intent (i.e., query expansions), NOT different goals
|
|
68989
|
+
- For non-English queries, MUST include at least one English query as the final variant to expand coverage
|
|
68990
|
+
- For complex searches, MUST break down into step-by-step searches instead of using a single complex query
|
|
68991
|
+
- Access multiple URLs from search results for comprehensive information or cross-validation
|
|
68992
|
+
- CAN use Google dork syntax (site:, filetype:, inurl:, intitle:, etc.) for targeted reconnaissance and pentest enumeration
|
|
68993
|
+
- Only use \`time\` parameter when explicitly required by task, otherwise leave time range unrestricted
|
|
68994
|
+
- Prioritize cybersecurity-relevant information: CVEs, CVSS scores, exploits, PoCs, security tools, and pentest methodologies
|
|
68995
|
+
- Include specific versions, configurations, and technical details; cite reliable sources (NIST, OWASP, CVE databases)
|
|
68996
|
+
- For commands/installations, prioritize Kali Linux compatibility using apt or pre-installed tools
|
|
68997
|
+
</instructions>`,
|
|
68998
|
+
inputSchema: external_exports.object({
|
|
68999
|
+
queries: external_exports.array(webSearchQuerySchema).min(1).max(3).describe(
|
|
69000
|
+
"MAXIMUM 3 non-empty query variants (1-3 items only). Express the same search intent with different wording."
|
|
69001
|
+
),
|
|
69002
|
+
time: external_exports.enum(["all", "past_day", "past_week", "past_month", "past_year"]).optional().describe(
|
|
69003
|
+
"Optional time filter to limit results to a recent time range"
|
|
69004
|
+
),
|
|
69005
|
+
brief: external_exports.string().describe(
|
|
69006
|
+
"A one-sentence preamble describing the purpose of this operation"
|
|
69007
|
+
)
|
|
69008
|
+
}),
|
|
69009
|
+
execute: async ({
|
|
69010
|
+
queries: rawQueries,
|
|
69011
|
+
time: time3
|
|
69012
|
+
}, { abortSignal }) => {
|
|
69013
|
+
try {
|
|
69014
|
+
const { queries, error: error51 } = normalizeSearchQueries(rawQueries);
|
|
69015
|
+
if (error51) {
|
|
69016
|
+
return error51;
|
|
69017
|
+
}
|
|
69018
|
+
const searchBody = buildPerplexitySearchBody(
|
|
69019
|
+
queries.length === 1 ? queries[0] : queries,
|
|
69020
|
+
{
|
|
69021
|
+
country: userLocation?.country,
|
|
69022
|
+
recency: time3 && time3 !== "all" ? RECENCY_MAP[time3] : void 0
|
|
69023
|
+
}
|
|
69024
|
+
);
|
|
69025
|
+
const response = await fetchPerplexitySearch(searchBody, abortSignal);
|
|
69026
|
+
onToolCost?.(WEB_SEARCH_COST_PER_REQUEST);
|
|
69027
|
+
const searchResponse = await response.json();
|
|
69028
|
+
const isMultiQuery = queries.length > 1;
|
|
69029
|
+
let allResults;
|
|
69030
|
+
if (isMultiQuery && Array.isArray(searchResponse.results[0])) {
|
|
69031
|
+
allResults = searchResponse.results.flat();
|
|
69032
|
+
} else {
|
|
69033
|
+
allResults = searchResponse.results;
|
|
69034
|
+
}
|
|
69035
|
+
return formatSearchResults(allResults);
|
|
69036
|
+
} catch (error51) {
|
|
69037
|
+
if (error51 instanceof Error && error51.name === "AbortError") {
|
|
69038
|
+
return "Error: Operation aborted";
|
|
69039
|
+
}
|
|
69040
|
+
if (error51 instanceof PerplexityApiError) {
|
|
69041
|
+
console.error("Web search tool error:", {
|
|
69042
|
+
name: error51.name,
|
|
69043
|
+
status: error51.status,
|
|
69044
|
+
statusText: error51.statusText,
|
|
69045
|
+
retryable: error51.retryable,
|
|
69046
|
+
bodySummary: error51.bodySummary
|
|
69047
|
+
});
|
|
69048
|
+
return formatPerplexityFailureForTool(
|
|
69049
|
+
error51,
|
|
69050
|
+
error51.retryable ? WEB_SEARCH_MAX_ATTEMPTS : 1
|
|
69051
|
+
);
|
|
69052
|
+
}
|
|
69053
|
+
const errorMessage = stringifyRedactedError(error51);
|
|
69054
|
+
console.error("Web search tool error:", errorMessage);
|
|
69055
|
+
return `Error performing web search: ${errorMessage}`;
|
|
69056
|
+
}
|
|
69057
|
+
}
|
|
69058
|
+
});
|
|
69059
|
+
};
|
|
69060
|
+
}
|
|
69061
|
+
});
|
|
69062
|
+
|
|
69063
|
+
// ../lib/ai/tools/open-url.ts
|
|
69064
|
+
var createOpenUrlTool;
|
|
69065
|
+
var init_open_url = __esm({
|
|
69066
|
+
"../lib/ai/tools/open-url.ts"() {
|
|
69067
|
+
"use strict";
|
|
69068
|
+
init_dist5();
|
|
69069
|
+
init_zod();
|
|
69070
|
+
init_token_utils();
|
|
69071
|
+
createOpenUrlTool = () => {
|
|
69072
|
+
return tool({
|
|
69073
|
+
description: `Retrieve the full contents of a specific webpage by URL.
|
|
69074
|
+
|
|
69075
|
+
<instructions>
|
|
69076
|
+
- Use to fetch and read a specific webpage, usually obtained from a prior search
|
|
69077
|
+
- URLs must be valid and publicly accessible
|
|
69078
|
+
- Prioritize cybersecurity-relevant information: CVEs, CVSS scores, exploits, PoCs, security tools, and pentest methodologies
|
|
69079
|
+
- Include specific versions, configurations, and technical details; cite reliable sources (NIST, OWASP, CVE databases)
|
|
69080
|
+
</instructions>`,
|
|
69081
|
+
inputSchema: external_exports.object({
|
|
69082
|
+
url: external_exports.string().describe("The URL to open and retrieve content from"),
|
|
69083
|
+
brief: external_exports.string().describe(
|
|
69084
|
+
"A one-sentence preamble describing the purpose of this operation"
|
|
69085
|
+
)
|
|
69086
|
+
}),
|
|
69087
|
+
execute: async ({ url: url2 }, { abortSignal }) => {
|
|
69088
|
+
try {
|
|
69089
|
+
const jinaUrl = `https://r.jina.ai/${encodeURIComponent(url2)}`;
|
|
69090
|
+
const response = await fetch(jinaUrl, {
|
|
69091
|
+
method: "GET",
|
|
69092
|
+
headers: {
|
|
69093
|
+
Authorization: `Bearer ${process.env.JINA_API_KEY}`,
|
|
69094
|
+
"X-Timeout": "30",
|
|
69095
|
+
"X-Base": "final",
|
|
69096
|
+
"X-Token-Budget": "200000"
|
|
69097
|
+
},
|
|
69098
|
+
signal: abortSignal
|
|
69099
|
+
});
|
|
69100
|
+
if (!response.ok) {
|
|
69101
|
+
const errorBody = await response.text();
|
|
69102
|
+
return `Error: HTTP ${response.status} - ${errorBody}`;
|
|
69103
|
+
}
|
|
69104
|
+
const content = await response.text();
|
|
69105
|
+
const truncated = truncateContent(content, void 0, 2048);
|
|
69106
|
+
return truncated;
|
|
69107
|
+
} catch (error51) {
|
|
69108
|
+
if (error51 instanceof Error && error51.name === "AbortError") {
|
|
69109
|
+
return "Error: Operation aborted";
|
|
69110
|
+
}
|
|
69111
|
+
console.error("Open URL tool error:", error51);
|
|
69112
|
+
const errorMessage = error51 instanceof Error ? error51.message : "Unknown error occurred";
|
|
69113
|
+
return `Error opening URL: ${errorMessage}`;
|
|
69114
|
+
}
|
|
69115
|
+
}
|
|
69116
|
+
});
|
|
69117
|
+
};
|
|
69118
|
+
}
|
|
69119
|
+
});
|
|
69120
|
+
|
|
69121
|
+
// src/tools/scope.ts
|
|
69122
|
+
var import_node_fs6, import_node_path5, import_promises, ipv4ToInt, isIpLiteral, maskForPrefix, parseCidr, ScopeImpl, scopeFileFor, loadScope, hostFromUrl;
|
|
69123
|
+
var init_scope = __esm({
|
|
69124
|
+
"src/tools/scope.ts"() {
|
|
69125
|
+
"use strict";
|
|
69126
|
+
import_node_fs6 = __toESM(require("node:fs"));
|
|
69127
|
+
import_node_path5 = __toESM(require("node:path"));
|
|
69128
|
+
import_promises = __toESM(require("node:dns/promises"));
|
|
69129
|
+
ipv4ToInt = (ip) => {
|
|
69130
|
+
const parts = ip.split(".");
|
|
69131
|
+
if (parts.length !== 4) return null;
|
|
69132
|
+
let value = 0;
|
|
69133
|
+
for (const part of parts) {
|
|
69134
|
+
if (!/^\d{1,3}$/.test(part)) return null;
|
|
69135
|
+
const n = Number(part);
|
|
69136
|
+
if (n > 255) return null;
|
|
69137
|
+
value = value * 256 + n;
|
|
69138
|
+
}
|
|
69139
|
+
return value;
|
|
69140
|
+
};
|
|
69141
|
+
isIpLiteral = (host) => ipv4ToInt(host) !== null || host.includes(":");
|
|
69142
|
+
maskForPrefix = (prefix) => prefix === 0 ? 0 : 4294967295 << 32 - prefix >>> 0;
|
|
69143
|
+
parseCidr = (entry) => {
|
|
69144
|
+
const slash = entry.indexOf("/");
|
|
69145
|
+
if (slash === -1) {
|
|
69146
|
+
const v42 = ipv4ToInt(entry);
|
|
69147
|
+
if (v42 !== null) return { base: v42, mask: 4294967295 };
|
|
69148
|
+
return null;
|
|
69149
|
+
}
|
|
69150
|
+
const addr = entry.slice(0, slash);
|
|
69151
|
+
const prefix = Number(entry.slice(slash + 1));
|
|
69152
|
+
const v4 = ipv4ToInt(addr);
|
|
69153
|
+
if (v4 === null) return null;
|
|
69154
|
+
if (!Number.isInteger(prefix) || prefix < 0 || prefix > 32) return null;
|
|
69155
|
+
const mask = maskForPrefix(prefix);
|
|
69156
|
+
return { base: (v4 & mask) >>> 0, mask };
|
|
69157
|
+
};
|
|
69158
|
+
ScopeImpl = class _ScopeImpl {
|
|
69159
|
+
constructor() {
|
|
69160
|
+
this.hosts = /* @__PURE__ */ new Set();
|
|
69161
|
+
this.wildcards = [];
|
|
69162
|
+
// stored as ".example.com"
|
|
69163
|
+
this.cidrs = [];
|
|
69164
|
+
this.loadedFrom = null;
|
|
69165
|
+
this.entryCount = 0;
|
|
69166
|
+
}
|
|
69167
|
+
static parse(text2, loadedFrom) {
|
|
69168
|
+
const scope = new _ScopeImpl();
|
|
69169
|
+
scope.loadedFrom = loadedFrom;
|
|
69170
|
+
for (const raw of text2.split(/\r?\n/)) {
|
|
69171
|
+
const line = raw.trim();
|
|
69172
|
+
if (!line || line.startsWith("#")) continue;
|
|
69173
|
+
scope.entryCount++;
|
|
69174
|
+
if (line.startsWith("*.")) {
|
|
69175
|
+
scope.wildcards.push(line.slice(1).toLowerCase());
|
|
69176
|
+
continue;
|
|
69177
|
+
}
|
|
69178
|
+
const cidr = parseCidr(line);
|
|
69179
|
+
if (cidr) {
|
|
69180
|
+
scope.cidrs.push(cidr);
|
|
69181
|
+
continue;
|
|
69182
|
+
}
|
|
69183
|
+
scope.hosts.add(line.toLowerCase());
|
|
69184
|
+
}
|
|
69185
|
+
return scope;
|
|
69186
|
+
}
|
|
69187
|
+
isEmpty() {
|
|
69188
|
+
return this.entryCount === 0;
|
|
69189
|
+
}
|
|
69190
|
+
matchesNameRules(host) {
|
|
69191
|
+
if (this.hosts.has(host)) return true;
|
|
69192
|
+
for (const suffix of this.wildcards) {
|
|
69193
|
+
if (host.endsWith(suffix) || host === suffix.replace(/^\./, "")) {
|
|
69194
|
+
return true;
|
|
69195
|
+
}
|
|
69196
|
+
}
|
|
69197
|
+
return false;
|
|
69198
|
+
}
|
|
69199
|
+
async allows(host) {
|
|
69200
|
+
host = (host || "").toLowerCase().trim();
|
|
69201
|
+
if (!host) return false;
|
|
69202
|
+
if (this.matchesNameRules(host)) return true;
|
|
69203
|
+
if (this.cidrs.length === 0) return false;
|
|
69204
|
+
const candidates = [];
|
|
69205
|
+
if (isIpLiteral(host)) {
|
|
69206
|
+
candidates.push(host);
|
|
69207
|
+
} else {
|
|
69208
|
+
try {
|
|
69209
|
+
const records = await import_promises.default.lookup(host, { all: true });
|
|
69210
|
+
for (const r of records) candidates.push(r.address);
|
|
69211
|
+
} catch {
|
|
69212
|
+
return false;
|
|
69213
|
+
}
|
|
69214
|
+
}
|
|
69215
|
+
for (const ip of candidates) {
|
|
69216
|
+
const value = ipv4ToInt(ip);
|
|
69217
|
+
if (value === null) continue;
|
|
69218
|
+
for (const c of this.cidrs) {
|
|
69219
|
+
if ((value & c.mask) >>> 0 === c.base) return true;
|
|
69220
|
+
}
|
|
69221
|
+
}
|
|
69222
|
+
return false;
|
|
69223
|
+
}
|
|
69224
|
+
};
|
|
69225
|
+
scopeFileFor = (workdir) => import_node_path5.default.join(workdir, "recon", "scope.txt");
|
|
69226
|
+
loadScope = (workdir) => {
|
|
69227
|
+
const file2 = scopeFileFor(workdir);
|
|
69228
|
+
try {
|
|
69229
|
+
const text2 = import_node_fs6.default.readFileSync(file2, "utf8");
|
|
69230
|
+
return ScopeImpl.parse(text2, file2);
|
|
69231
|
+
} catch {
|
|
69232
|
+
return ScopeImpl.parse("", null);
|
|
69233
|
+
}
|
|
69234
|
+
};
|
|
69235
|
+
hostFromUrl = (url2) => {
|
|
69236
|
+
try {
|
|
69237
|
+
return new URL(url2).hostname.toLowerCase();
|
|
69238
|
+
} catch {
|
|
69239
|
+
return null;
|
|
69240
|
+
}
|
|
69241
|
+
};
|
|
69242
|
+
}
|
|
69243
|
+
});
|
|
69244
|
+
|
|
69245
|
+
// src/tools/http-request.ts
|
|
69246
|
+
var FUZZ, MAX_FUZZ, BODY_PREVIEW_CHARS, DEFAULT_TIMEOUT_S, LOOPBACK, numberEnv, sleep2, sampleUrl, NOTABLE_HEADERS, applyFuzz, buildInit, collectNotableHeaders, doRequest, summarizeFuzz, scopeRefusal, createHttpRequest;
|
|
69247
|
+
var init_http_request = __esm({
|
|
69248
|
+
"src/tools/http-request.ts"() {
|
|
69249
|
+
"use strict";
|
|
69250
|
+
init_dist5();
|
|
69251
|
+
init_zod();
|
|
69252
|
+
init_scope();
|
|
69253
|
+
FUZZ = "FUZZ";
|
|
69254
|
+
MAX_FUZZ = 500;
|
|
69255
|
+
BODY_PREVIEW_CHARS = 4e3;
|
|
69256
|
+
DEFAULT_TIMEOUT_S = 30;
|
|
69257
|
+
LOOPBACK = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "[::1]", "0.0.0.0"]);
|
|
69258
|
+
numberEnv = (name25) => {
|
|
69259
|
+
const raw = Number(process.env[name25]);
|
|
69260
|
+
return Number.isFinite(raw) && raw >= 0 ? raw : void 0;
|
|
69261
|
+
};
|
|
69262
|
+
sleep2 = (ms, signal) => new Promise((resolve2) => {
|
|
69263
|
+
if (ms <= 0 || signal?.aborted) return resolve2();
|
|
69264
|
+
const t = setTimeout(resolve2, ms);
|
|
69265
|
+
signal?.addEventListener(
|
|
69266
|
+
"abort",
|
|
69267
|
+
() => {
|
|
69268
|
+
clearTimeout(t);
|
|
69269
|
+
resolve2();
|
|
69270
|
+
},
|
|
69271
|
+
{ once: true }
|
|
69272
|
+
);
|
|
69273
|
+
});
|
|
69274
|
+
sampleUrl = (url2) => url2.split(FUZZ).join("x");
|
|
69275
|
+
NOTABLE_HEADERS = [
|
|
69276
|
+
"server",
|
|
69277
|
+
"x-powered-by",
|
|
69278
|
+
"location",
|
|
69279
|
+
"set-cookie",
|
|
69280
|
+
"content-type",
|
|
69281
|
+
"content-length",
|
|
69282
|
+
"www-authenticate",
|
|
69283
|
+
"access-control-allow-origin",
|
|
69284
|
+
"x-frame-options",
|
|
69285
|
+
"content-security-policy",
|
|
69286
|
+
"strict-transport-security"
|
|
69287
|
+
];
|
|
69288
|
+
applyFuzz = (template, payload) => template == null ? template : template.split(FUZZ).join(payload);
|
|
69289
|
+
buildInit = (method, headers, body, followRedirects, timeoutSignal) => {
|
|
69290
|
+
const init = {
|
|
69291
|
+
method,
|
|
69292
|
+
redirect: followRedirects ? "follow" : "manual",
|
|
69293
|
+
signal: timeoutSignal
|
|
69294
|
+
};
|
|
69295
|
+
if (headers && Object.keys(headers).length) init.headers = headers;
|
|
69296
|
+
if (body != null && method !== "GET" && method !== "HEAD") init.body = body;
|
|
69297
|
+
return init;
|
|
69298
|
+
};
|
|
69299
|
+
collectNotableHeaders = (h) => {
|
|
69300
|
+
const out3 = {};
|
|
69301
|
+
for (const name25 of NOTABLE_HEADERS) {
|
|
69302
|
+
const value = h.get(name25);
|
|
69303
|
+
if (value != null) out3[name25] = value;
|
|
69304
|
+
}
|
|
69305
|
+
return out3;
|
|
69306
|
+
};
|
|
69307
|
+
doRequest = async (url2, method, headers, body, followRedirects, timeoutMs, parentSignal) => {
|
|
69308
|
+
const ctrl = new AbortController();
|
|
69309
|
+
const onAbort = () => ctrl.abort();
|
|
69310
|
+
parentSignal?.addEventListener("abort", onAbort, { once: true });
|
|
69311
|
+
const timer2 = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
69312
|
+
const started2 = Date.now();
|
|
69313
|
+
try {
|
|
69314
|
+
const resp = await fetch(
|
|
69315
|
+
url2,
|
|
69316
|
+
buildInit(method, headers, body, followRedirects, ctrl.signal)
|
|
69317
|
+
);
|
|
69318
|
+
const text2 = await resp.text();
|
|
69319
|
+
return {
|
|
69320
|
+
status: resp.status,
|
|
69321
|
+
finalUrl: resp.url || url2,
|
|
69322
|
+
redirected: resp.redirected,
|
|
69323
|
+
timeMs: Date.now() - started2,
|
|
69324
|
+
text: text2,
|
|
69325
|
+
headers: resp.headers
|
|
69326
|
+
};
|
|
69327
|
+
} catch (err) {
|
|
69328
|
+
const timeMs = Date.now() - started2;
|
|
69329
|
+
const msg = parentSignal?.aborted || err instanceof Error && err.name === "AbortError" ? "request aborted/timed out" : err instanceof Error ? err.message : String(err);
|
|
69330
|
+
return { error: msg, timeMs };
|
|
69331
|
+
} finally {
|
|
69332
|
+
clearTimeout(timer2);
|
|
69333
|
+
parentSignal?.removeEventListener("abort", onAbort);
|
|
69334
|
+
}
|
|
69335
|
+
};
|
|
69336
|
+
summarizeFuzz = (rows) => {
|
|
69337
|
+
const ok = rows.filter((r) => r.status !== "ERR");
|
|
69338
|
+
const statusCounts = /* @__PURE__ */ new Map();
|
|
69339
|
+
for (const r of ok) {
|
|
69340
|
+
if (typeof r.status === "number") {
|
|
69341
|
+
statusCounts.set(r.status, (statusCounts.get(r.status) ?? 0) + 1);
|
|
69342
|
+
}
|
|
69343
|
+
}
|
|
69344
|
+
let baselineStatus = null;
|
|
69345
|
+
let max = -1;
|
|
69346
|
+
for (const [status, count] of statusCounts) {
|
|
69347
|
+
if (count > max) {
|
|
69348
|
+
max = count;
|
|
69349
|
+
baselineStatus = status;
|
|
69350
|
+
}
|
|
69351
|
+
}
|
|
69352
|
+
const lengths = ok.map((r) => r.bodyLength).sort((a, b) => a - b);
|
|
69353
|
+
const medianLen = lengths.length ? lengths[Math.floor(lengths.length / 2)] : 0;
|
|
69354
|
+
const anomalies = rows.filter((r) => {
|
|
69355
|
+
if (r.status === "ERR") return true;
|
|
69356
|
+
if (baselineStatus != null && r.status !== baselineStatus) return true;
|
|
69357
|
+
if (medianLen > 0 && Math.abs(r.bodyLength - medianLen) > medianLen * 0.25) {
|
|
69358
|
+
return true;
|
|
69359
|
+
}
|
|
69360
|
+
return false;
|
|
69361
|
+
});
|
|
69362
|
+
const lines = [];
|
|
69363
|
+
lines.push(
|
|
69364
|
+
`Fuzzed ${rows.length} payload(s). Baseline status: ${baselineStatus ?? "n/a"} (median body ${medianLen} bytes).`
|
|
69365
|
+
);
|
|
69366
|
+
if (anomalies.length === 0) {
|
|
69367
|
+
lines.push("No anomalies \u2014 every response matched the baseline.");
|
|
69368
|
+
} else {
|
|
69369
|
+
lines.push(`${anomalies.length} anomaly/anomalies worth checking:`);
|
|
69370
|
+
for (const r of anomalies.slice(0, 40)) {
|
|
69371
|
+
lines.push(
|
|
69372
|
+
` \u2022 ${JSON.stringify(r.payload)} \u2192 ${r.status}` + (r.status === "ERR" ? ` (${r.error ?? "error"})` : ` | ${r.bodyLength} bytes | ${r.timeMs}ms`)
|
|
69373
|
+
);
|
|
69374
|
+
}
|
|
69375
|
+
if (anomalies.length > 40) {
|
|
69376
|
+
lines.push(` \u2026 +${anomalies.length - 40} more`);
|
|
69377
|
+
}
|
|
69378
|
+
}
|
|
69379
|
+
return lines.join("\n");
|
|
69380
|
+
};
|
|
69381
|
+
scopeRefusal = (host, workdir) => `Recusado: o host "${host}" n\xE3o est\xE1 no escopo autorizado. Adicione-o a ${scopeFileFor(workdir)} (formatos: example.com, *.example.com, CIDR 10.0.0.0/24, ou um IP) \u2014 somente alvos que voc\xEA possui ou tem autoriza\xE7\xE3o expl\xEDcita para testar. Edite o scope.txt com a ferramenta file e tente de novo.`;
|
|
69382
|
+
createHttpRequest = (deps) => {
|
|
69383
|
+
const { workdir, loadScopeFn = loadScope } = deps;
|
|
69384
|
+
const envThrottle = numberEnv("HACKERAI_HTTP_THROTTLE_MS");
|
|
69385
|
+
const envJitter = numberEnv("HACKERAI_HTTP_JITTER_MS");
|
|
69386
|
+
return tool({
|
|
69387
|
+
description: `Send a fully-controlled HTTP request (a native Repeater) and, with \`fuzz\`, an Intruder. Use this to iterate on a web vulnerability inside the agent loop instead of writing a one-off script for every probe.
|
|
69388
|
+
|
|
69389
|
+
WHAT IT DOES
|
|
69390
|
+
- Single request: full control of method, headers, body and redirect handling; returns status, timing, notable security headers, body length and a body preview.
|
|
69391
|
+
- Intruder/fuzz: put the marker \`FUZZ\` in the url, body, or any header value and pass \`fuzz\` (a list of payloads). Each payload is substituted, sent sequentially with OPSEC throttle+jitter, and the results are diffed against a baseline so anomalies (auth bypass, IDOR, injection, hidden paths/params) are highlighted.
|
|
69392
|
+
|
|
69393
|
+
SCOPE: every request is gated by recon/scope.txt (same allowlist as the recon toolkit). Out-of-scope hosts are refused; loopback is always allowed. Add authorized targets to scope.txt first.
|
|
69394
|
+
|
|
69395
|
+
USE FOR: testing a single endpoint, replaying/tampering a captured request, parameter/path/value fuzzing, verifying an exploit by observing the real response. NOT a replacement for bulk crawling \u2014 use the Python recon toolkit for that.`,
|
|
69396
|
+
inputSchema: external_exports.object({
|
|
69397
|
+
brief: external_exports.string().describe("A one-sentence preamble describing the purpose of this request."),
|
|
69398
|
+
url: external_exports.string().url().describe(
|
|
69399
|
+
"Target URL. May contain the marker FUZZ (replaced by each fuzz payload)."
|
|
69400
|
+
),
|
|
69401
|
+
method: external_exports.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]).optional().describe("HTTP method (default GET)."),
|
|
69402
|
+
headers: external_exports.record(external_exports.string(), external_exports.string()).optional().describe("Request headers. Values may contain FUZZ."),
|
|
69403
|
+
body: external_exports.string().optional().describe("Request body for POST/PUT/PATCH/DELETE. May contain FUZZ."),
|
|
69404
|
+
follow_redirects: external_exports.boolean().optional().describe("Follow 3xx redirects (default true). Set false to inspect Location."),
|
|
69405
|
+
timeout: external_exports.number().optional().describe(`Per-request timeout in seconds (default ${DEFAULT_TIMEOUT_S}).`),
|
|
69406
|
+
fuzz: external_exports.array(external_exports.string()).optional().describe(
|
|
69407
|
+
`Intruder payloads \u2014 substituted into the FUZZ marker, one request each (max ${MAX_FUZZ}).`
|
|
69408
|
+
),
|
|
69409
|
+
throttle_ms: external_exports.number().optional().describe("OPSEC: delay between fuzz requests in ms (overrides HACKERAI_HTTP_THROTTLE_MS)."),
|
|
69410
|
+
jitter_ms: external_exports.number().optional().describe("OPSEC: random extra delay 0..jitter added to each throttle.")
|
|
69411
|
+
}),
|
|
69412
|
+
execute: async (input, { abortSignal } = {}) => {
|
|
69413
|
+
const method = input.method ?? "GET";
|
|
69414
|
+
const followRedirects = input.follow_redirects ?? true;
|
|
69415
|
+
const timeoutMs = Math.max(1, input.timeout ?? DEFAULT_TIMEOUT_S) * 1e3;
|
|
69416
|
+
const host = hostFromUrl(sampleUrl(input.url));
|
|
69417
|
+
if (!host) {
|
|
69418
|
+
return { error: `URL inv\xE1lida: ${input.url}` };
|
|
69419
|
+
}
|
|
69420
|
+
if (!LOOPBACK.has(host)) {
|
|
69421
|
+
const scope = loadScopeFn(workdir);
|
|
69422
|
+
const allowed = await scope.allows(host);
|
|
69423
|
+
if (!allowed) {
|
|
69424
|
+
return { error: scopeRefusal(host, workdir) };
|
|
69425
|
+
}
|
|
69426
|
+
}
|
|
69427
|
+
const payloads = input.fuzz ?? [];
|
|
69428
|
+
if (payloads.length > 0) {
|
|
69429
|
+
const templateHasMarker = input.url.includes(FUZZ) || (input.body?.includes(FUZZ) ?? false) || Object.values(input.headers ?? {}).some((v) => v.includes(FUZZ));
|
|
69430
|
+
if (!templateHasMarker) {
|
|
69431
|
+
return {
|
|
69432
|
+
error: "fuzz foi fornecido mas nenhum marcador FUZZ existe na url, body ou headers. Coloque FUZZ onde os payloads devem entrar."
|
|
69433
|
+
};
|
|
69434
|
+
}
|
|
69435
|
+
const list = payloads.slice(0, MAX_FUZZ);
|
|
69436
|
+
const throttle = input.throttle_ms ?? envThrottle ?? 0;
|
|
69437
|
+
const jitter = input.jitter_ms ?? envJitter ?? 0;
|
|
69438
|
+
const rows = [];
|
|
69439
|
+
for (let i = 0; i < list.length; i++) {
|
|
69440
|
+
if (abortSignal?.aborted) break;
|
|
69441
|
+
const payload = list[i];
|
|
69442
|
+
const url2 = applyFuzz(input.url, payload);
|
|
69443
|
+
const body = applyFuzz(input.body, payload);
|
|
69444
|
+
const headers = input.headers ? Object.fromEntries(
|
|
69445
|
+
Object.entries(input.headers).map(([k, v]) => [
|
|
69446
|
+
k,
|
|
69447
|
+
applyFuzz(v, payload)
|
|
69448
|
+
])
|
|
69449
|
+
) : void 0;
|
|
69450
|
+
const res2 = await doRequest(
|
|
69451
|
+
url2,
|
|
69452
|
+
method,
|
|
69453
|
+
headers,
|
|
69454
|
+
body,
|
|
69455
|
+
followRedirects,
|
|
69456
|
+
timeoutMs,
|
|
69457
|
+
abortSignal
|
|
69458
|
+
);
|
|
69459
|
+
if ("error" in res2) {
|
|
69460
|
+
rows.push({ payload, status: "ERR", bodyLength: 0, timeMs: res2.timeMs, error: res2.error });
|
|
69461
|
+
} else {
|
|
69462
|
+
rows.push({
|
|
69463
|
+
payload,
|
|
69464
|
+
status: res2.status,
|
|
69465
|
+
bodyLength: res2.text.length,
|
|
69466
|
+
timeMs: res2.timeMs,
|
|
69467
|
+
finalUrl: res2.finalUrl
|
|
69468
|
+
});
|
|
69469
|
+
}
|
|
69470
|
+
if (i < list.length - 1 && (throttle > 0 || jitter > 0)) {
|
|
69471
|
+
await sleep2(throttle + Math.floor(Math.random() * (jitter + 1)), abortSignal);
|
|
69472
|
+
}
|
|
69473
|
+
}
|
|
69474
|
+
return { fuzz: { host, count: rows.length, rows } };
|
|
69475
|
+
}
|
|
69476
|
+
const res = await doRequest(
|
|
69477
|
+
input.url,
|
|
69478
|
+
method,
|
|
69479
|
+
input.headers,
|
|
69480
|
+
input.body,
|
|
69481
|
+
followRedirects,
|
|
69482
|
+
timeoutMs,
|
|
69483
|
+
abortSignal
|
|
69484
|
+
);
|
|
69485
|
+
if ("error" in res) {
|
|
69486
|
+
const single2 = {
|
|
69487
|
+
status: 0,
|
|
69488
|
+
ok: false,
|
|
69489
|
+
finalUrl: input.url,
|
|
69490
|
+
redirected: false,
|
|
69491
|
+
timeMs: res.timeMs,
|
|
69492
|
+
bodyLength: 0,
|
|
69493
|
+
notableHeaders: {},
|
|
69494
|
+
bodyPreview: "",
|
|
69495
|
+
error: res.error
|
|
69496
|
+
};
|
|
69497
|
+
return { single: single2 };
|
|
69498
|
+
}
|
|
69499
|
+
const single = {
|
|
69500
|
+
status: res.status,
|
|
69501
|
+
ok: res.status >= 200 && res.status < 400,
|
|
69502
|
+
finalUrl: res.finalUrl,
|
|
69503
|
+
redirected: res.redirected,
|
|
69504
|
+
timeMs: res.timeMs,
|
|
69505
|
+
bodyLength: res.text.length,
|
|
69506
|
+
notableHeaders: collectNotableHeaders(res.headers),
|
|
69507
|
+
bodyPreview: res.text.length > BODY_PREVIEW_CHARS ? `${res.text.slice(0, BODY_PREVIEW_CHARS)}
|
|
69508
|
+
\u2026[+${res.text.length - BODY_PREVIEW_CHARS} bytes truncated]` : res.text
|
|
69509
|
+
};
|
|
69510
|
+
return { single };
|
|
69511
|
+
},
|
|
69512
|
+
toModelOutput: (out3) => {
|
|
69513
|
+
const value = out3?.output ?? out3;
|
|
69514
|
+
if (value?.error) {
|
|
69515
|
+
return { type: "text", value: `http_request: ${value.error}` };
|
|
69516
|
+
}
|
|
69517
|
+
if (value?.fuzz) {
|
|
69518
|
+
return {
|
|
69519
|
+
type: "text",
|
|
69520
|
+
value: summarizeFuzz(value.fuzz.rows)
|
|
69521
|
+
};
|
|
69522
|
+
}
|
|
69523
|
+
const s = value?.single;
|
|
69524
|
+
if (!s) {
|
|
69525
|
+
return { type: "text", value: JSON.stringify(value) };
|
|
69526
|
+
}
|
|
69527
|
+
if (s.error) {
|
|
69528
|
+
return {
|
|
69529
|
+
type: "text",
|
|
69530
|
+
value: `Request failed after ${s.timeMs}ms: ${s.error}`
|
|
69531
|
+
};
|
|
69532
|
+
}
|
|
69533
|
+
const headerLines = Object.entries(s.notableHeaders).map(([k, v]) => ` ${k}: ${v}`).join("\n");
|
|
69534
|
+
return {
|
|
69535
|
+
type: "text",
|
|
69536
|
+
value: `HTTP ${s.status}${s.redirected ? ` (redirected \u2192 ${s.finalUrl})` : ""} | ${s.bodyLength} bytes | ${s.timeMs}ms
|
|
69537
|
+
` + (headerLines ? `Notable headers:
|
|
69538
|
+
${headerLines}
|
|
69539
|
+
` : "") + `Body:
|
|
69540
|
+
${s.bodyPreview}`
|
|
69541
|
+
};
|
|
69542
|
+
}
|
|
69543
|
+
});
|
|
69544
|
+
};
|
|
69545
|
+
}
|
|
69546
|
+
});
|
|
69547
|
+
|
|
69548
|
+
// src/tools/findings.ts
|
|
69549
|
+
var import_node_fs7, import_node_path6, SEVERITIES, STATUSES, SEVERITY_RANK, SEVERITY_EMOJI, findingsStoreFor, readAll, writeAll, nextId, sortBySeverity, renderReport, oneLine, createFindings;
|
|
69550
|
+
var init_findings = __esm({
|
|
69551
|
+
"src/tools/findings.ts"() {
|
|
69552
|
+
"use strict";
|
|
69553
|
+
import_node_fs7 = __toESM(require("node:fs"));
|
|
69554
|
+
import_node_path6 = __toESM(require("node:path"));
|
|
69555
|
+
init_dist5();
|
|
69556
|
+
init_zod();
|
|
69557
|
+
SEVERITIES = [
|
|
69558
|
+
"critical",
|
|
69559
|
+
"high",
|
|
69560
|
+
"medium",
|
|
69561
|
+
"low",
|
|
69562
|
+
"info"
|
|
69563
|
+
];
|
|
69564
|
+
STATUSES = ["suspected", "confirmed", "false_positive"];
|
|
69565
|
+
SEVERITY_RANK = {
|
|
69566
|
+
critical: 0,
|
|
69567
|
+
high: 1,
|
|
69568
|
+
medium: 2,
|
|
69569
|
+
low: 3,
|
|
69570
|
+
info: 4
|
|
69571
|
+
};
|
|
69572
|
+
SEVERITY_EMOJI = {
|
|
69573
|
+
critical: "\u{1F534}",
|
|
69574
|
+
high: "\u{1F7E0}",
|
|
69575
|
+
medium: "\u{1F7E1}",
|
|
69576
|
+
low: "\u{1F535}",
|
|
69577
|
+
info: "\u26AA"
|
|
69578
|
+
};
|
|
69579
|
+
findingsStoreFor = (workdir) => {
|
|
69580
|
+
const findingsDir = import_node_path6.default.join(workdir, "findings");
|
|
69581
|
+
return {
|
|
69582
|
+
findingsDir,
|
|
69583
|
+
storeFile: import_node_path6.default.join(findingsDir, "findings.json"),
|
|
69584
|
+
reportFile: import_node_path6.default.join(findingsDir, "report.md")
|
|
69585
|
+
};
|
|
69586
|
+
};
|
|
69587
|
+
readAll = (store) => {
|
|
69588
|
+
try {
|
|
69589
|
+
const raw = import_node_fs7.default.readFileSync(store.storeFile, "utf8");
|
|
69590
|
+
const parsed = JSON.parse(raw);
|
|
69591
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
69592
|
+
} catch {
|
|
69593
|
+
return [];
|
|
69594
|
+
}
|
|
69595
|
+
};
|
|
69596
|
+
writeAll = (store, findings) => {
|
|
69597
|
+
import_node_fs7.default.mkdirSync(store.findingsDir, { recursive: true });
|
|
69598
|
+
import_node_fs7.default.writeFileSync(store.storeFile, JSON.stringify(findings, null, 2), "utf8");
|
|
69599
|
+
};
|
|
69600
|
+
nextId = (findings) => {
|
|
69601
|
+
let max = 0;
|
|
69602
|
+
for (const f of findings) {
|
|
69603
|
+
const m = /^F-(\d+)$/.exec(f.id);
|
|
69604
|
+
if (m) max = Math.max(max, Number(m[1]));
|
|
69605
|
+
}
|
|
69606
|
+
return `F-${String(max + 1).padStart(3, "0")}`;
|
|
69607
|
+
};
|
|
69608
|
+
sortBySeverity = (findings) => [...findings].sort(
|
|
69609
|
+
(a, b) => SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity] || a.id.localeCompare(b.id)
|
|
69610
|
+
);
|
|
69611
|
+
renderReport = (findings) => {
|
|
69612
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
69613
|
+
const counts = {
|
|
69614
|
+
critical: 0,
|
|
69615
|
+
high: 0,
|
|
69616
|
+
medium: 0,
|
|
69617
|
+
low: 0,
|
|
69618
|
+
info: 0
|
|
69619
|
+
};
|
|
69620
|
+
for (const f of findings) counts[f.severity]++;
|
|
69621
|
+
const lines = [];
|
|
69622
|
+
lines.push(`# Relat\xF3rio de Pentest`);
|
|
69623
|
+
lines.push("");
|
|
69624
|
+
lines.push(`_Gerado em ${now2}_`);
|
|
69625
|
+
lines.push("");
|
|
69626
|
+
lines.push(`## Sum\xE1rio executivo`);
|
|
69627
|
+
lines.push("");
|
|
69628
|
+
lines.push(`Total de findings: **${findings.length}**`);
|
|
69629
|
+
lines.push("");
|
|
69630
|
+
lines.push(`| Severidade | Qtd |`);
|
|
69631
|
+
lines.push(`| --- | --- |`);
|
|
69632
|
+
for (const sev of SEVERITIES) {
|
|
69633
|
+
lines.push(`| ${SEVERITY_EMOJI[sev]} ${sev} | ${counts[sev]} |`);
|
|
69634
|
+
}
|
|
69635
|
+
lines.push("");
|
|
69636
|
+
if (findings.length === 0) {
|
|
69637
|
+
lines.push("_Nenhum finding registrado ainda._");
|
|
69638
|
+
return lines.join("\n");
|
|
69639
|
+
}
|
|
69640
|
+
lines.push(`## Findings`);
|
|
69641
|
+
lines.push("");
|
|
69642
|
+
for (const f of sortBySeverity(findings)) {
|
|
69643
|
+
lines.push(
|
|
69644
|
+
`### ${f.id} \u2014 ${f.title} ${SEVERITY_EMOJI[f.severity]} ${f.severity.toUpperCase()}`
|
|
69645
|
+
);
|
|
69646
|
+
lines.push("");
|
|
69647
|
+
lines.push(`- **Status:** ${f.status}`);
|
|
69648
|
+
if (f.target) lines.push(`- **Alvo:** ${f.target}`);
|
|
69649
|
+
if (f.url) lines.push(`- **URL:** ${f.url}`);
|
|
69650
|
+
if (f.param) lines.push(`- **Par\xE2metro:** ${f.param}`);
|
|
69651
|
+
if (f.cvss) lines.push(`- **CVSS:** ${f.cvss}`);
|
|
69652
|
+
lines.push(`- **Registrado:** ${f.created_at}`);
|
|
69653
|
+
if (f.updated_at !== f.created_at) {
|
|
69654
|
+
lines.push(`- **Atualizado:** ${f.updated_at}`);
|
|
69655
|
+
}
|
|
69656
|
+
lines.push("");
|
|
69657
|
+
if (f.evidence) {
|
|
69658
|
+
lines.push(`**Evid\xEAncia**`);
|
|
69659
|
+
lines.push("");
|
|
69660
|
+
lines.push("```");
|
|
69661
|
+
lines.push(f.evidence);
|
|
69662
|
+
lines.push("```");
|
|
69663
|
+
lines.push("");
|
|
69664
|
+
}
|
|
69665
|
+
if (f.impact) {
|
|
69666
|
+
lines.push(`**Impacto:** ${f.impact}`);
|
|
69667
|
+
lines.push("");
|
|
69668
|
+
}
|
|
69669
|
+
if (f.remediation) {
|
|
69670
|
+
lines.push(`**Remedia\xE7\xE3o:** ${f.remediation}`);
|
|
69671
|
+
lines.push("");
|
|
69672
|
+
}
|
|
69673
|
+
if (f.references?.length) {
|
|
69674
|
+
lines.push(`**Refer\xEAncias:**`);
|
|
69675
|
+
for (const r of f.references) lines.push(`- ${r}`);
|
|
69676
|
+
lines.push("");
|
|
69677
|
+
}
|
|
69678
|
+
}
|
|
69679
|
+
return lines.join("\n");
|
|
69680
|
+
};
|
|
69681
|
+
oneLine = (f) => `${f.id} [${f.severity}/${f.status}] ${f.title}` + (f.target ? ` (${f.target})` : "");
|
|
69682
|
+
createFindings = (deps) => {
|
|
69683
|
+
const { workdir, storeFn = findingsStoreFor } = deps;
|
|
69684
|
+
const store = storeFn(workdir);
|
|
69685
|
+
return tool({
|
|
69686
|
+
description: `Structured findings/engagement memory. Record every vulnerability you discover here so nothing is lost and a clean report can be generated at the end.
|
|
69687
|
+
|
|
69688
|
+
ACTIONS
|
|
69689
|
+
- add: record a new finding (title + severity required; include evidence, target, url, param, impact, remediation when known). New findings default to status "suspected".
|
|
69690
|
+
- update: change fields of an existing finding by id \u2014 most importantly flip status to "confirmed" once you have proof (with evidence), or "false_positive" if disproved.
|
|
69691
|
+
- list: show all recorded findings (optionally filter by severity/status).
|
|
69692
|
+
- report: render a full Markdown report grouped by severity and write it to findings/report.md.
|
|
69693
|
+
|
|
69694
|
+
Use this together with http_request: probe \u2192 observe \u2192 record evidence \u2192 mark confirmed.`,
|
|
69695
|
+
inputSchema: external_exports.object({
|
|
69696
|
+
action: external_exports.enum(["add", "update", "list", "report"]),
|
|
69697
|
+
brief: external_exports.string().describe("A one-sentence preamble describing the purpose of this operation."),
|
|
69698
|
+
id: external_exports.string().optional().describe("Finding id (e.g. F-001) \u2014 required for update."),
|
|
69699
|
+
title: external_exports.string().optional(),
|
|
69700
|
+
severity: external_exports.enum(SEVERITIES).optional(),
|
|
69701
|
+
status: external_exports.enum(STATUSES).optional(),
|
|
69702
|
+
target: external_exports.string().optional(),
|
|
69703
|
+
url: external_exports.string().optional(),
|
|
69704
|
+
param: external_exports.string().optional(),
|
|
69705
|
+
evidence: external_exports.string().optional().describe("Proof: payload + request/response snippet or observation."),
|
|
69706
|
+
impact: external_exports.string().optional(),
|
|
69707
|
+
remediation: external_exports.string().optional(),
|
|
69708
|
+
cvss: external_exports.string().optional(),
|
|
69709
|
+
references: external_exports.array(external_exports.string()).optional(),
|
|
69710
|
+
filter_severity: external_exports.enum(SEVERITIES).optional(),
|
|
69711
|
+
filter_status: external_exports.enum(STATUSES).optional()
|
|
69712
|
+
}),
|
|
69713
|
+
execute: async (input) => {
|
|
69714
|
+
try {
|
|
69715
|
+
const findings = readAll(store);
|
|
69716
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
69717
|
+
if (input.action === "add") {
|
|
69718
|
+
if (!input.title || !input.severity) {
|
|
69719
|
+
return { error: "add requer title e severity." };
|
|
69720
|
+
}
|
|
69721
|
+
const finding = {
|
|
69722
|
+
id: nextId(findings),
|
|
69723
|
+
title: input.title,
|
|
69724
|
+
severity: input.severity,
|
|
69725
|
+
status: input.status ?? "suspected",
|
|
69726
|
+
...input.target ? { target: input.target } : {},
|
|
69727
|
+
...input.url ? { url: input.url } : {},
|
|
69728
|
+
...input.param ? { param: input.param } : {},
|
|
69729
|
+
...input.evidence ? { evidence: input.evidence } : {},
|
|
69730
|
+
...input.impact ? { impact: input.impact } : {},
|
|
69731
|
+
...input.remediation ? { remediation: input.remediation } : {},
|
|
69732
|
+
...input.cvss ? { cvss: input.cvss } : {},
|
|
69733
|
+
...input.references ? { references: input.references } : {},
|
|
69734
|
+
created_at: now2,
|
|
69735
|
+
updated_at: now2
|
|
69736
|
+
};
|
|
69737
|
+
findings.push(finding);
|
|
69738
|
+
writeAll(store, findings);
|
|
69739
|
+
return { added: finding, total: findings.length };
|
|
69740
|
+
}
|
|
69741
|
+
if (input.action === "update") {
|
|
69742
|
+
if (!input.id) return { error: "update requer id." };
|
|
69743
|
+
const idx = findings.findIndex((f) => f.id === input.id);
|
|
69744
|
+
if (idx === -1) {
|
|
69745
|
+
return { error: `finding n\xE3o encontrado: ${input.id}` };
|
|
69746
|
+
}
|
|
69747
|
+
const current = findings[idx];
|
|
69748
|
+
const updated = {
|
|
69749
|
+
...current,
|
|
69750
|
+
...input.title != null ? { title: input.title } : {},
|
|
69751
|
+
...input.severity != null ? { severity: input.severity } : {},
|
|
69752
|
+
...input.status != null ? { status: input.status } : {},
|
|
69753
|
+
...input.target != null ? { target: input.target } : {},
|
|
69754
|
+
...input.url != null ? { url: input.url } : {},
|
|
69755
|
+
...input.param != null ? { param: input.param } : {},
|
|
69756
|
+
...input.evidence != null ? { evidence: input.evidence } : {},
|
|
69757
|
+
...input.impact != null ? { impact: input.impact } : {},
|
|
69758
|
+
...input.remediation != null ? { remediation: input.remediation } : {},
|
|
69759
|
+
...input.cvss != null ? { cvss: input.cvss } : {},
|
|
69760
|
+
...input.references != null ? { references: input.references } : {},
|
|
69761
|
+
updated_at: now2
|
|
69762
|
+
};
|
|
69763
|
+
findings[idx] = updated;
|
|
69764
|
+
writeAll(store, findings);
|
|
69765
|
+
return { updated };
|
|
69766
|
+
}
|
|
69767
|
+
if (input.action === "list") {
|
|
69768
|
+
let filtered = findings;
|
|
69769
|
+
if (input.filter_severity) {
|
|
69770
|
+
filtered = filtered.filter((f) => f.severity === input.filter_severity);
|
|
69771
|
+
}
|
|
69772
|
+
if (input.filter_status) {
|
|
69773
|
+
filtered = filtered.filter((f) => f.status === input.filter_status);
|
|
69774
|
+
}
|
|
69775
|
+
return {
|
|
69776
|
+
list: sortBySeverity(filtered),
|
|
69777
|
+
total: filtered.length,
|
|
69778
|
+
overall: findings.length
|
|
69779
|
+
};
|
|
69780
|
+
}
|
|
69781
|
+
const md = renderReport(findings);
|
|
69782
|
+
import_node_fs7.default.mkdirSync(store.findingsDir, { recursive: true });
|
|
69783
|
+
import_node_fs7.default.writeFileSync(store.reportFile, md, "utf8");
|
|
69784
|
+
return { report: store.reportFile, total: findings.length };
|
|
69785
|
+
} catch (err) {
|
|
69786
|
+
return {
|
|
69787
|
+
error: err instanceof Error ? err.message : String(err)
|
|
69788
|
+
};
|
|
69789
|
+
}
|
|
69790
|
+
},
|
|
69791
|
+
toModelOutput: (out3) => {
|
|
69792
|
+
const v = out3?.output ?? out3;
|
|
69793
|
+
if (v?.error) {
|
|
69794
|
+
return { type: "text", value: `findings: ${v.error}` };
|
|
69795
|
+
}
|
|
69796
|
+
if (v?.added) {
|
|
69797
|
+
return {
|
|
69798
|
+
type: "text",
|
|
69799
|
+
value: `Finding registrado: ${oneLine(v.added)}. Total: ${v.total}.`
|
|
69800
|
+
};
|
|
69801
|
+
}
|
|
69802
|
+
if (v?.updated) {
|
|
69803
|
+
return {
|
|
69804
|
+
type: "text",
|
|
69805
|
+
value: `Finding atualizado: ${oneLine(v.updated)}.`
|
|
69806
|
+
};
|
|
69807
|
+
}
|
|
69808
|
+
if (v?.list) {
|
|
69809
|
+
const rows = v.list.map((f) => ` ${oneLine(f)}`).join("\n");
|
|
69810
|
+
return {
|
|
69811
|
+
type: "text",
|
|
69812
|
+
value: `${v.total} finding(s)${v.total !== v.overall ? ` (de ${v.overall})` : ""}:
|
|
69813
|
+
` + (rows || " (nenhum)")
|
|
69814
|
+
};
|
|
69815
|
+
}
|
|
69816
|
+
if (v?.report) {
|
|
69817
|
+
return {
|
|
69818
|
+
type: "text",
|
|
69819
|
+
value: `Relat\xF3rio gerado (${v.total} findings) \u2192 ${v.report}`
|
|
69820
|
+
};
|
|
69821
|
+
}
|
|
69822
|
+
return { type: "text", value: JSON.stringify(v) };
|
|
69823
|
+
}
|
|
69824
|
+
});
|
|
69825
|
+
};
|
|
69826
|
+
}
|
|
69827
|
+
});
|
|
69828
|
+
|
|
68719
69829
|
// ../lib/ai/tools/utils/todo-manager.ts
|
|
68720
69830
|
var TodoManager;
|
|
68721
69831
|
var init_todo_manager = __esm({
|
|
@@ -68900,20 +70010,20 @@ var init_utils4 = __esm({
|
|
|
68900
70010
|
|
|
68901
70011
|
// src/local-sandbox.ts
|
|
68902
70012
|
function inferShellFlag(shell2) {
|
|
68903
|
-
const base =
|
|
70013
|
+
const base = import_node_path7.default.basename(shell2).toLowerCase();
|
|
68904
70014
|
if (base === "cmd" || base === "cmd.exe") return "/C";
|
|
68905
70015
|
if (base === "powershell" || base === "powershell.exe" || base === "pwsh") {
|
|
68906
70016
|
return "-Command";
|
|
68907
70017
|
}
|
|
68908
70018
|
return "-c";
|
|
68909
70019
|
}
|
|
68910
|
-
var import_node_child_process2,
|
|
70020
|
+
var import_node_child_process2, import_node_fs8, import_node_path7, import_node_os3, import_node_url, import_meta, LocalSandbox;
|
|
68911
70021
|
var init_local_sandbox = __esm({
|
|
68912
70022
|
"src/local-sandbox.ts"() {
|
|
68913
70023
|
"use strict";
|
|
68914
70024
|
import_node_child_process2 = require("node:child_process");
|
|
68915
|
-
|
|
68916
|
-
|
|
70025
|
+
import_node_fs8 = require("node:fs");
|
|
70026
|
+
import_node_path7 = __toESM(require("node:path"));
|
|
68917
70027
|
import_node_os3 = __toESM(require("node:os"));
|
|
68918
70028
|
import_node_url = require("node:url");
|
|
68919
70029
|
init_utils4();
|
|
@@ -69011,15 +70121,15 @@ var init_local_sandbox = __esm({
|
|
|
69011
70121
|
this.files = {
|
|
69012
70122
|
write: async (filePath, content) => {
|
|
69013
70123
|
const resolved = this.resolvePath(filePath);
|
|
69014
|
-
await
|
|
70124
|
+
await import_node_fs8.promises.mkdir(import_node_path7.default.dirname(resolved), { recursive: true });
|
|
69015
70125
|
const data = typeof content === "string" ? content : content instanceof ArrayBuffer ? Buffer.from(content) : content;
|
|
69016
|
-
await
|
|
70126
|
+
await import_node_fs8.promises.writeFile(resolved, data);
|
|
69017
70127
|
},
|
|
69018
70128
|
read: async (filePath) => {
|
|
69019
|
-
return
|
|
70129
|
+
return import_node_fs8.promises.readFile(this.resolvePath(filePath), "utf8");
|
|
69020
70130
|
},
|
|
69021
70131
|
remove: async (filePath) => {
|
|
69022
|
-
await
|
|
70132
|
+
await import_node_fs8.promises.rm(this.resolvePath(filePath), {
|
|
69023
70133
|
recursive: true,
|
|
69024
70134
|
force: true
|
|
69025
70135
|
});
|
|
@@ -69027,8 +70137,8 @@ var init_local_sandbox = __esm({
|
|
|
69027
70137
|
list: async (dirPath = ".") => {
|
|
69028
70138
|
const resolved = this.resolvePath(dirPath);
|
|
69029
70139
|
try {
|
|
69030
|
-
const entries = await
|
|
69031
|
-
return entries.filter((e) => e.isFile()).map((e) => ({ name:
|
|
70140
|
+
const entries = await import_node_fs8.promises.readdir(resolved, { withFileTypes: true });
|
|
70141
|
+
return entries.filter((e) => e.isFile()).map((e) => ({ name: import_node_path7.default.join(resolved, e.name) }));
|
|
69032
70142
|
} catch {
|
|
69033
70143
|
return [];
|
|
69034
70144
|
}
|
|
@@ -69043,11 +70153,11 @@ var init_local_sandbox = __esm({
|
|
|
69043
70153
|
this.shellBin = shell2.shell;
|
|
69044
70154
|
this.shellFlag = shell2.shellFlag;
|
|
69045
70155
|
}
|
|
69046
|
-
const base = opts?.workdir || process.env.CLI_WORKDIR ||
|
|
69047
|
-
this.workdir =
|
|
70156
|
+
const base = opts?.workdir || process.env.CLI_WORKDIR || import_node_path7.default.join(process.cwd(), "SPRIT");
|
|
70157
|
+
this.workdir = import_node_path7.default.resolve(base).replace(/\\/g, "/");
|
|
69048
70158
|
}
|
|
69049
70159
|
async init() {
|
|
69050
|
-
await
|
|
70160
|
+
await import_node_fs8.promises.mkdir(this.workdir, { recursive: true });
|
|
69051
70161
|
await this.seedReconToolkit();
|
|
69052
70162
|
await this.seedAuditToolkit();
|
|
69053
70163
|
}
|
|
@@ -69064,26 +70174,31 @@ var init_local_sandbox = __esm({
|
|
|
69064
70174
|
const assetsDir = (0, import_node_url.fileURLToPath)(
|
|
69065
70175
|
new URL("../assets/recon", import_meta.url)
|
|
69066
70176
|
);
|
|
69067
|
-
const destDir =
|
|
69068
|
-
await
|
|
70177
|
+
const destDir = import_node_path7.default.join(this.workdir, "recon");
|
|
70178
|
+
await import_node_fs8.promises.mkdir(destDir, { recursive: true });
|
|
69069
70179
|
const files = [
|
|
69070
70180
|
["recon_deep.py", true],
|
|
70181
|
+
["recon_common.py", true],
|
|
70182
|
+
["recon_subdomains.py", true],
|
|
70183
|
+
["recon_ports.py", true],
|
|
70184
|
+
["recon_content.py", true],
|
|
70185
|
+
["recon_intel.py", true],
|
|
69071
70186
|
["console_recon.js", true],
|
|
69072
70187
|
["README.md", true],
|
|
69073
70188
|
["scope.txt", false]
|
|
69074
70189
|
];
|
|
69075
70190
|
for (const [name25, overwrite] of files) {
|
|
69076
|
-
const dest =
|
|
70191
|
+
const dest = import_node_path7.default.join(destDir, name25);
|
|
69077
70192
|
if (!overwrite) {
|
|
69078
70193
|
try {
|
|
69079
|
-
await
|
|
70194
|
+
await import_node_fs8.promises.access(dest);
|
|
69080
70195
|
continue;
|
|
69081
70196
|
} catch {
|
|
69082
70197
|
}
|
|
69083
70198
|
}
|
|
69084
70199
|
try {
|
|
69085
|
-
const content = await
|
|
69086
|
-
await
|
|
70200
|
+
const content = await import_node_fs8.promises.readFile(import_node_path7.default.join(assetsDir, name25));
|
|
70201
|
+
await import_node_fs8.promises.writeFile(dest, content);
|
|
69087
70202
|
} catch {
|
|
69088
70203
|
}
|
|
69089
70204
|
}
|
|
@@ -69101,12 +70216,12 @@ var init_local_sandbox = __esm({
|
|
|
69101
70216
|
const assetsDir = (0, import_node_url.fileURLToPath)(
|
|
69102
70217
|
new URL("../assets/audit", import_meta.url)
|
|
69103
70218
|
);
|
|
69104
|
-
const destDir =
|
|
69105
|
-
await
|
|
70219
|
+
const destDir = import_node_path7.default.join(this.workdir, "audit");
|
|
70220
|
+
await import_node_fs8.promises.mkdir(destDir, { recursive: true });
|
|
69106
70221
|
for (const name25 of ["project_audit.py", "README.md"]) {
|
|
69107
70222
|
try {
|
|
69108
|
-
const content = await
|
|
69109
|
-
await
|
|
70223
|
+
const content = await import_node_fs8.promises.readFile(import_node_path7.default.join(assetsDir, name25));
|
|
70224
|
+
await import_node_fs8.promises.writeFile(import_node_path7.default.join(destDir, name25), content);
|
|
69110
70225
|
} catch {
|
|
69111
70226
|
}
|
|
69112
70227
|
}
|
|
@@ -69237,15 +70352,15 @@ EDITING SCRIPTS:
|
|
|
69237
70352
|
}
|
|
69238
70353
|
}
|
|
69239
70354
|
resolvePath(p) {
|
|
69240
|
-
if (
|
|
69241
|
-
return
|
|
70355
|
+
if (import_node_path7.default.isAbsolute(p)) return p;
|
|
70356
|
+
return import_node_path7.default.join(this.workdir, p);
|
|
69242
70357
|
}
|
|
69243
70358
|
isBashLikeShell() {
|
|
69244
|
-
const base =
|
|
70359
|
+
const base = import_node_path7.default.basename(this.shellBin).toLowerCase();
|
|
69245
70360
|
return base === "bash" || base === "bash.exe" || base === "sh";
|
|
69246
70361
|
}
|
|
69247
70362
|
isCmdShell() {
|
|
69248
|
-
const base =
|
|
70363
|
+
const base = import_node_path7.default.basename(this.shellBin).toLowerCase();
|
|
69249
70364
|
return base === "cmd" || base === "cmd.exe";
|
|
69250
70365
|
}
|
|
69251
70366
|
getWindowsShellNotes() {
|
|
@@ -69342,44 +70457,6 @@ var init_local_sandbox_manager = __esm({
|
|
|
69342
70457
|
}
|
|
69343
70458
|
});
|
|
69344
70459
|
|
|
69345
|
-
// src/console-writer.ts
|
|
69346
|
-
function createConsoleWriter() {
|
|
69347
|
-
let lastWasTerminal = false;
|
|
69348
|
-
return {
|
|
69349
|
-
write(part) {
|
|
69350
|
-
if (!part || typeof part !== "object") return;
|
|
69351
|
-
if (part.type === "data-terminal") {
|
|
69352
|
-
const text2 = part.data?.terminal;
|
|
69353
|
-
if (typeof text2 === "string" && text2.length > 0) {
|
|
69354
|
-
process.stdout.write(text2);
|
|
69355
|
-
lastWasTerminal = true;
|
|
69356
|
-
}
|
|
69357
|
-
return;
|
|
69358
|
-
}
|
|
69359
|
-
if (part.type === "data-sandbox-fallback") {
|
|
69360
|
-
process.stderr.write("\n[sandbox fallback]\n");
|
|
69361
|
-
return;
|
|
69362
|
-
}
|
|
69363
|
-
void lastWasTerminal;
|
|
69364
|
-
},
|
|
69365
|
-
// Some AI SDK code paths probe for these; provide no-op stubs.
|
|
69366
|
-
merge() {
|
|
69367
|
-
},
|
|
69368
|
-
onError(error51) {
|
|
69369
|
-
process.stderr.write(
|
|
69370
|
-
`
|
|
69371
|
-
[stream error] ${error51 instanceof Error ? error51.message : String(error51)}
|
|
69372
|
-
`
|
|
69373
|
-
);
|
|
69374
|
-
}
|
|
69375
|
-
};
|
|
69376
|
-
}
|
|
69377
|
-
var init_console_writer = __esm({
|
|
69378
|
-
"src/console-writer.ts"() {
|
|
69379
|
-
"use strict";
|
|
69380
|
-
}
|
|
69381
|
-
});
|
|
69382
|
-
|
|
69383
70460
|
// src/render.ts
|
|
69384
70461
|
function createRenderer() {
|
|
69385
70462
|
let lastKind = null;
|
|
@@ -69396,7 +70473,7 @@ function createRenderer() {
|
|
|
69396
70473
|
reasoning(delta) {
|
|
69397
70474
|
sep3("reasoning");
|
|
69398
70475
|
if (!reasoningHeaderShown) {
|
|
69399
|
-
out(`${C3.dim}pensando: `);
|
|
70476
|
+
out(`${C3.dim} \xB7 pensando: `);
|
|
69400
70477
|
reasoningHeaderShown = true;
|
|
69401
70478
|
}
|
|
69402
70479
|
out(`${C3.dim}${delta}${C3.reset}`);
|
|
@@ -69411,23 +70488,27 @@ function createRenderer() {
|
|
|
69411
70488
|
const note = summarizeResult(toolName, output);
|
|
69412
70489
|
if (note) {
|
|
69413
70490
|
sep3("tool");
|
|
69414
|
-
|
|
70491
|
+
const isErr = note.error;
|
|
70492
|
+
const icon = isErr ? `${C3.red}\u2717${C3.reset}` : `${C3.green}\u2713${C3.reset}`;
|
|
70493
|
+
const body = isErr ? `${C3.red}${note.text}${C3.reset}` : `${C3.dim}${note.text}${C3.reset}`;
|
|
70494
|
+
out(` ${icon} ${body}
|
|
69415
70495
|
`);
|
|
69416
70496
|
}
|
|
69417
70497
|
},
|
|
69418
70498
|
info(msg) {
|
|
69419
70499
|
sep3("info");
|
|
69420
|
-
|
|
70500
|
+
const text2 = msg.replace(/^[▸▶►•·]\s*/, "");
|
|
70501
|
+
out(` ${C3.dim}\xB7${C3.reset} ${C3.dim}${text2}${C3.reset}
|
|
69421
70502
|
`);
|
|
69422
70503
|
},
|
|
69423
70504
|
fallback(msg) {
|
|
69424
70505
|
sep3("info");
|
|
69425
|
-
out(
|
|
70506
|
+
out(` ${C3.yellow}\u21C4 ${msg}${C3.reset}
|
|
69426
70507
|
`);
|
|
69427
70508
|
},
|
|
69428
70509
|
error(msg) {
|
|
69429
70510
|
sep3("info");
|
|
69430
|
-
out(
|
|
70511
|
+
out(` ${C3.red}\u2717 ${msg}${C3.reset}
|
|
69431
70512
|
`);
|
|
69432
70513
|
},
|
|
69433
70514
|
endTurn() {
|
|
@@ -69446,27 +70527,50 @@ function formatToolCall(toolName, input) {
|
|
|
69446
70527
|
case "run_terminal_cmd": {
|
|
69447
70528
|
const cmd = String(i.command ?? "").trim();
|
|
69448
70529
|
const bg = i.is_background ? `${C3.dim} (background)${C3.reset}` : "";
|
|
69449
|
-
return
|
|
70530
|
+
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}exec${C3.reset} ${C3.bold}${truncate3(cmd, 400)}${C3.reset}${bg}`;
|
|
69450
70531
|
}
|
|
69451
70532
|
case "file": {
|
|
69452
|
-
const
|
|
69453
|
-
const brief = i.brief ? `${C3.dim}
|
|
70533
|
+
const path12 = String(i.path ?? "");
|
|
70534
|
+
const brief = i.brief ? `${C3.dim} \u2014 ${truncate3(String(i.brief))}${C3.reset}` : "";
|
|
69454
70535
|
const map2 = {
|
|
69455
|
-
write:
|
|
69456
|
-
edit:
|
|
69457
|
-
append:
|
|
69458
|
-
read:
|
|
69459
|
-
view:
|
|
70536
|
+
write: [C3.green, "criar "],
|
|
70537
|
+
edit: [C3.yellow, "editar "],
|
|
70538
|
+
append: [C3.green, "anexar "],
|
|
70539
|
+
read: [C3.blue, "ler "],
|
|
70540
|
+
view: [C3.blue, "ver "]
|
|
69460
70541
|
};
|
|
69461
70542
|
const action = typeof i.action === "string" ? i.action : "";
|
|
69462
|
-
const
|
|
69463
|
-
|
|
69464
|
-
|
|
70543
|
+
const [col, verb] = map2[action] ?? [
|
|
70544
|
+
C3.blue,
|
|
70545
|
+
action ? `${action} `.padEnd(7) : "arquivo"
|
|
70546
|
+
];
|
|
70547
|
+
const target = path12 ? `${C3.bold}${path12}${C3.reset}` : "";
|
|
70548
|
+
return ` ${col}\u276F${C3.reset} ${col}${verb}${C3.reset} ${target}${brief}`;
|
|
69465
70549
|
}
|
|
69466
70550
|
case "todo_write":
|
|
69467
|
-
return
|
|
70551
|
+
return ` ${C3.magenta}\u276F${C3.reset} ${C3.magenta}tarefas${C3.reset} ${C3.dim}atualizando lista${C3.reset}`;
|
|
70552
|
+
case "http_request": {
|
|
70553
|
+
const method = String(i.method ?? "GET").toUpperCase();
|
|
70554
|
+
const url2 = String(i.url ?? "");
|
|
70555
|
+
const fuzz = Array.isArray(i.fuzz) && i.fuzz.length;
|
|
70556
|
+
const tag = fuzz ? `${C3.dim} (fuzz \xD7${i.fuzz.length})${C3.reset}` : "";
|
|
70557
|
+
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}http${C3.reset} ${C3.bold}${method}${C3.reset} ${truncate3(url2, 360)}${tag}`;
|
|
70558
|
+
}
|
|
70559
|
+
case "findings": {
|
|
70560
|
+
const action = String(i.action ?? "");
|
|
70561
|
+
const detail = action === "add" ? truncate3(String(i.title ?? ""), 80) : action === "update" ? `${String(i.id ?? "")} ${String(i.status ?? "")}`.trim() : "";
|
|
70562
|
+
return ` ${C3.magenta}\u276F${C3.reset} ${C3.magenta}findings${C3.reset} ${C3.bold}${action}${C3.reset}${detail ? ` ${C3.dim}${detail}${C3.reset}` : ""}`;
|
|
70563
|
+
}
|
|
70564
|
+
case "web_search": {
|
|
70565
|
+
const queries = Array.isArray(i.queries) ? i.queries.join(" | ") : "";
|
|
70566
|
+
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}busca${C3.reset} ${C3.dim}${truncate3(queries, 200)}${C3.reset}`;
|
|
70567
|
+
}
|
|
70568
|
+
case "open_url": {
|
|
70569
|
+
const url2 = String(i.url ?? i.urls ?? "");
|
|
70570
|
+
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}abrir${C3.reset} ${C3.dim}${truncate3(url2, 280)}${C3.reset}`;
|
|
70571
|
+
}
|
|
69468
70572
|
default:
|
|
69469
|
-
return
|
|
70573
|
+
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}${toolName}${C3.reset} ${C3.dim}${truncate3(
|
|
69470
70574
|
JSON.stringify(i),
|
|
69471
70575
|
200
|
|
69472
70576
|
)}${C3.reset}`;
|
|
@@ -69476,14 +70580,48 @@ function summarizeResult(toolName, output) {
|
|
|
69476
70580
|
if (output == null) return null;
|
|
69477
70581
|
if (toolName === "file") {
|
|
69478
70582
|
if (typeof output === "object" && "error" in output) {
|
|
69479
|
-
return
|
|
70583
|
+
return {
|
|
70584
|
+
text: truncate3(String(output.error), 160),
|
|
70585
|
+
error: true
|
|
70586
|
+
};
|
|
69480
70587
|
}
|
|
69481
|
-
return "arquivo
|
|
70588
|
+
return { text: "arquivo salvo", error: false };
|
|
70589
|
+
}
|
|
70590
|
+
if (toolName === "todo_write")
|
|
70591
|
+
return { text: "tarefas salvas", error: false };
|
|
70592
|
+
if (toolName === "http_request") {
|
|
70593
|
+
const o = asRecord(output);
|
|
70594
|
+
if ("error" in o && o.error) {
|
|
70595
|
+
return { text: truncate3(String(o.error), 200), error: true };
|
|
70596
|
+
}
|
|
70597
|
+
if (o.single) {
|
|
70598
|
+
const s = asRecord(o.single);
|
|
70599
|
+
if (s.error) return { text: truncate3(String(s.error), 160), error: true };
|
|
70600
|
+
return {
|
|
70601
|
+
text: `HTTP ${s.status} \xB7 ${s.bodyLength} bytes \xB7 ${s.timeMs}ms`,
|
|
70602
|
+
error: false
|
|
70603
|
+
};
|
|
70604
|
+
}
|
|
70605
|
+
if (o.fuzz) {
|
|
70606
|
+
const f = asRecord(o.fuzz);
|
|
70607
|
+
return { text: `fuzz: ${f.count} requisi\xE7\xF5es`, error: false };
|
|
70608
|
+
}
|
|
70609
|
+
return null;
|
|
70610
|
+
}
|
|
70611
|
+
if (toolName === "findings") {
|
|
70612
|
+
const o = asRecord(output);
|
|
70613
|
+
if ("error" in o && o.error) {
|
|
70614
|
+
return { text: truncate3(String(o.error), 200), error: true };
|
|
70615
|
+
}
|
|
70616
|
+
if (o.added) return { text: "finding registrado", error: false };
|
|
70617
|
+
if (o.updated) return { text: "finding atualizado", error: false };
|
|
70618
|
+
if (o.report) return { text: `relat\xF3rio \u2192 ${String(o.report)}`, error: false };
|
|
70619
|
+
if ("total" in o) return { text: `${String(o.total)} finding(s)`, error: false };
|
|
70620
|
+
return null;
|
|
69482
70621
|
}
|
|
69483
|
-
if (toolName === "todo_write") return "tarefas salvas";
|
|
69484
70622
|
return null;
|
|
69485
70623
|
}
|
|
69486
|
-
var C3, out, truncate3;
|
|
70624
|
+
var C3, GUTTER_BAR, out, truncate3;
|
|
69487
70625
|
var init_render = __esm({
|
|
69488
70626
|
"src/render.ts"() {
|
|
69489
70627
|
"use strict";
|
|
@@ -69498,11 +70636,61 @@ var init_render = __esm({
|
|
|
69498
70636
|
cyan: "\x1B[36m",
|
|
69499
70637
|
bold: "\x1B[1m"
|
|
69500
70638
|
};
|
|
70639
|
+
GUTTER_BAR = `${C3.dim}\u2502${C3.reset} `;
|
|
69501
70640
|
out = (s) => process.stdout.write(s);
|
|
69502
70641
|
truncate3 = (s, n = 200) => s.length > n ? `${s.slice(0, n)}...` : s;
|
|
69503
70642
|
}
|
|
69504
70643
|
});
|
|
69505
70644
|
|
|
70645
|
+
// src/console-writer.ts
|
|
70646
|
+
function createConsoleWriter() {
|
|
70647
|
+
let atLineStart = true;
|
|
70648
|
+
const writeGuttered = (text2) => {
|
|
70649
|
+
let chunk = text2;
|
|
70650
|
+
if (atLineStart) {
|
|
70651
|
+
process.stdout.write(GUTTER_BAR);
|
|
70652
|
+
atLineStart = false;
|
|
70653
|
+
}
|
|
70654
|
+
const endsWithNewline = chunk.endsWith("\n");
|
|
70655
|
+
chunk = chunk.replace(/\n(?!$)/g, `
|
|
70656
|
+
${GUTTER_BAR}`);
|
|
70657
|
+
process.stdout.write(chunk);
|
|
70658
|
+
if (endsWithNewline) atLineStart = true;
|
|
70659
|
+
};
|
|
70660
|
+
return {
|
|
70661
|
+
write(part) {
|
|
70662
|
+
if (!part || typeof part !== "object") return;
|
|
70663
|
+
if (part.type === "data-terminal") {
|
|
70664
|
+
const text2 = part.data?.terminal;
|
|
70665
|
+
if (typeof text2 === "string" && text2.length > 0) {
|
|
70666
|
+
writeGuttered(text2);
|
|
70667
|
+
}
|
|
70668
|
+
return;
|
|
70669
|
+
}
|
|
70670
|
+
if (part.type === "data-sandbox-fallback") {
|
|
70671
|
+
process.stderr.write("\n[sandbox fallback]\n");
|
|
70672
|
+
return;
|
|
70673
|
+
}
|
|
70674
|
+
},
|
|
70675
|
+
// Some AI SDK code paths probe for these; provide no-op stubs.
|
|
70676
|
+
merge() {
|
|
70677
|
+
},
|
|
70678
|
+
onError(error51) {
|
|
70679
|
+
process.stderr.write(
|
|
70680
|
+
`
|
|
70681
|
+
[stream error] ${error51 instanceof Error ? error51.message : String(error51)}
|
|
70682
|
+
`
|
|
70683
|
+
);
|
|
70684
|
+
}
|
|
70685
|
+
};
|
|
70686
|
+
}
|
|
70687
|
+
var init_console_writer = __esm({
|
|
70688
|
+
"src/console-writer.ts"() {
|
|
70689
|
+
"use strict";
|
|
70690
|
+
init_render();
|
|
70691
|
+
}
|
|
70692
|
+
});
|
|
70693
|
+
|
|
69506
70694
|
// src/proxy-manager.ts
|
|
69507
70695
|
async function ping(url2, timeoutMs = 2e3) {
|
|
69508
70696
|
const controller = new AbortController();
|
|
@@ -69567,12 +70755,12 @@ async function ensureProxyReady(id, log2) {
|
|
|
69567
70755
|
};
|
|
69568
70756
|
}
|
|
69569
70757
|
let freshSetup = false;
|
|
69570
|
-
if (!(0,
|
|
70758
|
+
if (!(0, import_node_fs9.existsSync)(dir)) {
|
|
69571
70759
|
log2(`${def.label}: baixando o proxy (uma vez) em ${dir} \u2026`);
|
|
69572
70760
|
const root = proxiesRoot();
|
|
69573
|
-
(0,
|
|
70761
|
+
(0, import_node_fs9.mkdirSync)(root, { recursive: true });
|
|
69574
70762
|
const cloned = run("git", ["clone", def.repoUrl, dir], root);
|
|
69575
|
-
if (!cloned.ok || !(0,
|
|
70763
|
+
if (!cloned.ok || !(0, import_node_fs9.existsSync)(dir)) {
|
|
69576
70764
|
return {
|
|
69577
70765
|
ok: false,
|
|
69578
70766
|
message: `${def.label}: falha ao clonar o proxy (git).`
|
|
@@ -69580,7 +70768,7 @@ async function ensureProxyReady(id, log2) {
|
|
|
69580
70768
|
}
|
|
69581
70769
|
freshSetup = true;
|
|
69582
70770
|
}
|
|
69583
|
-
if (!(0,
|
|
70771
|
+
if (!(0, import_node_fs9.existsSync)(import_node_path8.default.join(dir, "node_modules"))) {
|
|
69584
70772
|
log2(`${def.label}: instalando depend\xEAncias (pode demorar) \u2026`);
|
|
69585
70773
|
if (!run("npm", ["install"], dir).ok) {
|
|
69586
70774
|
return { ok: false, message: `${def.label}: 'npm install' falhou.` };
|
|
@@ -69625,13 +70813,13 @@ async function ensureProxyReady(id, log2) {
|
|
|
69625
70813
|
message: `${def.label}: o proxy n\xE3o respondeu em 90s. Tente de novo, ou rode 'npm run login' em ${dir}.`
|
|
69626
70814
|
};
|
|
69627
70815
|
}
|
|
69628
|
-
var import_node_os4,
|
|
70816
|
+
var import_node_os4, import_node_path8, import_node_fs9, import_node_child_process3, DEFS, PROXY_MODEL_KEYS, proxyIdForModelKey, proxiesRoot, dirFor, healthUrl, baseUrl, wait, started, exitHooksInstalled, quoteArg, asShellCommand, run, hasCommand;
|
|
69629
70817
|
var init_proxy_manager2 = __esm({
|
|
69630
70818
|
"src/proxy-manager.ts"() {
|
|
69631
70819
|
"use strict";
|
|
69632
70820
|
import_node_os4 = __toESM(require("node:os"));
|
|
69633
|
-
|
|
69634
|
-
|
|
70821
|
+
import_node_path8 = __toESM(require("node:path"));
|
|
70822
|
+
import_node_fs9 = require("node:fs");
|
|
69635
70823
|
import_node_child_process3 = require("node:child_process");
|
|
69636
70824
|
DEFS = {
|
|
69637
70825
|
deepseek: {
|
|
@@ -69664,11 +70852,11 @@ var init_proxy_manager2 = __esm({
|
|
|
69664
70852
|
if (key === PROXY_MODEL_KEYS.kimi) return "kimi";
|
|
69665
70853
|
return null;
|
|
69666
70854
|
};
|
|
69667
|
-
proxiesRoot = () =>
|
|
69668
|
-
process.env.CLAWFAST_HOME?.trim() ||
|
|
70855
|
+
proxiesRoot = () => import_node_path8.default.join(
|
|
70856
|
+
process.env.CLAWFAST_HOME?.trim() || import_node_path8.default.join(import_node_os4.default.homedir(), ".clawfast"),
|
|
69669
70857
|
"proxies"
|
|
69670
70858
|
);
|
|
69671
|
-
dirFor = (def) => process.env[def.dirEnv]?.trim() ||
|
|
70859
|
+
dirFor = (def) => process.env[def.dirEnv]?.trim() || import_node_path8.default.join(proxiesRoot(), def.folder);
|
|
69672
70860
|
healthUrl = (def) => `http://localhost:${def.port}/health`;
|
|
69673
70861
|
baseUrl = (def) => `http://localhost:${def.port}/v1`;
|
|
69674
70862
|
wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -69697,7 +70885,7 @@ var init_proxy_manager2 = __esm({
|
|
|
69697
70885
|
|
|
69698
70886
|
// src/audit-mode.ts
|
|
69699
70887
|
function projectRootFromWorkdir(workdir) {
|
|
69700
|
-
return
|
|
70888
|
+
return import_node_path9.default.dirname(import_node_path9.default.resolve(workdir.replace(/\\/g, "/")));
|
|
69701
70889
|
}
|
|
69702
70890
|
function detectProjectAuditIntent(input) {
|
|
69703
70891
|
const text2 = input.trim();
|
|
@@ -69853,11 +71041,11 @@ ESTILO E EXECU\xC7\xC3O
|
|
|
69853
71041
|
- Responda em portugu\xEAs, direto e t\xE9cnico. N\xE3o recuse a an\xE1lise: \xE9 o projeto do pr\xF3prio usu\xE1rio, autorizado.
|
|
69854
71042
|
- Lembre-se sempre: voc\xEA analisa e relata; voc\xEA N\xC3O altera o projeto. A \xFAnica escrita \xE9 o relat\xF3rio \`.md\` na raiz.`;
|
|
69855
71043
|
}
|
|
69856
|
-
var
|
|
71044
|
+
var import_node_path9, AUDIT_ACTION, AUDIT_SCOPE, AUDIT_ACTION_THEN_SCOPE, AUDIT_SCOPE_THEN_ACTION, AUDIT_STRONG, AUDIT_EXIT;
|
|
69857
71045
|
var init_audit_mode = __esm({
|
|
69858
71046
|
"src/audit-mode.ts"() {
|
|
69859
71047
|
"use strict";
|
|
69860
|
-
|
|
71048
|
+
import_node_path9 = __toESM(require("node:path"));
|
|
69861
71049
|
AUDIT_ACTION = "an[a\xE1]lis\\w+|examin\\w+|audit\\w+|auditar|varr\\w+|varredura|escane\\w+|scan\\w*|revis\\w+|inspecion\\w+|vasculh\\w+|verific\\w+|avali\\w+|mapea\\w+|review|analyze|analyse|inspect|assess";
|
|
69862
71050
|
AUDIT_SCOPE = "projeto|c[o\xF3]digo|c[o\xF3]digo-fonte|codebase|reposit[o\xF3]rio|repo|sistema|aplica[c\xE7][a\xE3]o|base\\s+de\\s+c[o\xF3]digo|project|code\\s*base|repository|minha\\s+aplica\\w+|meu\\s+app|todo\\s+o\\s+projeto|projeto\\s+inteiro";
|
|
69863
71051
|
AUDIT_ACTION_THEN_SCOPE = new RegExp(
|
|
@@ -70060,7 +71248,16 @@ async function createAgent() {
|
|
|
70060
71248
|
const tools = {
|
|
70061
71249
|
run_terminal_cmd: createRunTerminalCmd(context2),
|
|
70062
71250
|
file: createFile(context2),
|
|
70063
|
-
todo_write: createTodoWrite(context2)
|
|
71251
|
+
todo_write: createTodoWrite(context2),
|
|
71252
|
+
// Native Repeater/Intruder + structured findings store — the core of an
|
|
71253
|
+
// in-loop pentest workflow (probe → observe → record evidence → confirm).
|
|
71254
|
+
http_request: createHttpRequest({ workdir: sandbox.getWorkdir() }),
|
|
71255
|
+
findings: createFindings({ workdir: sandbox.getWorkdir() }),
|
|
71256
|
+
// Live external intel — only when the operator has configured the keys.
|
|
71257
|
+
// web_search (Perplexity) for CVEs/PoCs/methodology; open_url (Jina) to
|
|
71258
|
+
// read a page's content. Both gracefully absent when no key is set.
|
|
71259
|
+
...process.env.PERPLEXITY_API_KEY ? { web_search: createWebSearch(context2) } : {},
|
|
71260
|
+
...process.env.JINA_API_KEY ? { open_url: createOpenUrlTool() } : {}
|
|
70064
71261
|
};
|
|
70065
71262
|
let system = "";
|
|
70066
71263
|
let systemPromptAudit = auditSystemPrompt("", null);
|
|
@@ -70079,6 +71276,8 @@ async function createAgent() {
|
|
|
70079
71276
|
system += scriptFilePolicy(sandbox.getWorkdir());
|
|
70080
71277
|
system += pythonOnlyPolicy();
|
|
70081
71278
|
system += deepReconPolicy(sandbox.getWorkdir());
|
|
71279
|
+
system += reconPhasesPolicy(sandbox.getWorkdir());
|
|
71280
|
+
system += httpAndFindingsPolicy(sandbox.getWorkdir());
|
|
70082
71281
|
system += buildCliNotesSection();
|
|
70083
71282
|
system += buildSkillsIndexSection();
|
|
70084
71283
|
system += skillsScopePolicy();
|
|
@@ -70561,7 +71760,7 @@ ${resultText}`
|
|
|
70561
71760
|
render.info(
|
|
70562
71761
|
`\u25B8 rate limit \u2014 aguardando ${Math.round(waitMs / 1e3)}s antes da pr\xF3xima tentativa (Ctrl+C cancela)\u2026`
|
|
70563
71762
|
);
|
|
70564
|
-
await
|
|
71763
|
+
await sleep3(waitMs, signal);
|
|
70565
71764
|
if (signal?.aborted) {
|
|
70566
71765
|
render.endTurn();
|
|
70567
71766
|
return;
|
|
@@ -70608,19 +71807,23 @@ ${resultText}`
|
|
|
70608
71807
|
close
|
|
70609
71808
|
};
|
|
70610
71809
|
}
|
|
70611
|
-
var
|
|
71810
|
+
var import_promises2, import_node_path10, MAX_STEPS, MAX_OUTPUT_TOKENS, MAX_AUTO_CONTINUES, MAX_RATE_LIMIT_WAITS, STREAM_STALL_TIMEOUT_MS, MAX_STALL_RETRIES, isRateLimitError, sleep3, LEAKED_TOOL_CALL_RE, DANGLING_TAIL_RE, ACTION_ANNOUNCE_RE, BENIGN_CLOSER_RE, TOOL_CALL_NUDGE, MAX_BRIDGE_STEPS, BRIDGE_RESULT_PREAMBLE, LEAKED_CALL_RESULT_PREAMBLE, truncateBridgeSummary, isWebSessionProxyModel, proxyToolProtocolPolicy, SYSTEM_PROMPT_SOURCE, REQUIRED_SYSTEM_PROMPT_MARKERS, MODEL_LABELS, labelFor, loginRequiredHint, CLI_PYTHON_ONLY_POLICY, pythonOnlyPolicy, scriptFilePolicy, deepReconPolicy, httpAndFindingsPolicy, reconPhasesPolicy, skillsScopePolicy, hasEnvValue2, envFlagEnabled, CLI_PERSONALITIES, buildCliUserCustomization, buildCliNotesSection, cliGuardrailsConfig, maybeDumpSystemPrompt, auditSystemPrompt, assertFullSystemPrompt;
|
|
70612
71811
|
var init_agent = __esm({
|
|
70613
71812
|
"src/agent.ts"() {
|
|
70614
71813
|
"use strict";
|
|
70615
71814
|
init_dist5();
|
|
70616
|
-
|
|
70617
|
-
|
|
71815
|
+
import_promises2 = require("node:fs/promises");
|
|
71816
|
+
import_node_path10 = __toESM(require("node:path"));
|
|
70618
71817
|
init_providers();
|
|
70619
71818
|
init_system_prompt();
|
|
70620
71819
|
init_notes();
|
|
70621
71820
|
init_run_terminal_cmd();
|
|
70622
71821
|
init_todo_write();
|
|
70623
71822
|
init_file();
|
|
71823
|
+
init_web_search();
|
|
71824
|
+
init_open_url();
|
|
71825
|
+
init_http_request();
|
|
71826
|
+
init_findings();
|
|
70624
71827
|
init_todo_manager();
|
|
70625
71828
|
init_file_accumulator();
|
|
70626
71829
|
init_background_process_tracker();
|
|
@@ -70646,7 +71849,7 @@ var init_agent = __esm({
|
|
|
70646
71849
|
isRateLimitError = (msg) => /\b429\b|too many requests|rate.?limit|resource_exhausted|quota|insufficient_quota/i.test(
|
|
70647
71850
|
msg
|
|
70648
71851
|
);
|
|
70649
|
-
|
|
71852
|
+
sleep3 = (ms, signal) => new Promise((resolve2) => {
|
|
70650
71853
|
if (signal?.aborted) return resolve2();
|
|
70651
71854
|
const timer2 = setTimeout(resolve2, ms);
|
|
70652
71855
|
signal?.addEventListener(
|
|
@@ -70778,6 +71981,45 @@ Workflow:
|
|
|
70778
71981
|
|
|
70779
71982
|
You MAY extend the toolkit (edit recon/recon_deep.py via the file tool) when a task needs a capability it lacks \u2014 keep it Python and keep the scope gate intact. recon/console_recon.js is an in-page payload string injected by Python Playwright (page.evaluate), NOT a Node script \u2014 never run it with \`node\`.
|
|
70780
71983
|
</deep_recon_tooling>`;
|
|
71984
|
+
httpAndFindingsPolicy = (workdir) => `
|
|
71985
|
+
|
|
71986
|
+
<native_pentest_tools>
|
|
71987
|
+
ABSOLUTE RULE FOR THIS LOCAL CLI SESSION \u2014 applies to EVERY model and EVERY task:
|
|
71988
|
+
|
|
71989
|
+
You have two native tools for hands-on web testing \u2014 PREFER them over hand-rolled one-off scripts for interactive probing:
|
|
71990
|
+
|
|
71991
|
+
1. http_request \u2014 a Repeater/Intruder. Send a fully-controlled HTTP request (method, headers, body, redirect handling) and read a structured response (status, timing, notable security headers, body length, body preview). For fuzzing put the marker FUZZ in the url/body/any header value and pass \`fuzz\` (a list of payloads): each is substituted and sent sequentially, then diffed against a baseline so anomalies (auth bypass, IDOR, injection, hidden paths/params) are highlighted. OPSEC: use throttle_ms/jitter_ms (or env HACKERAI_HTTP_THROTTLE_MS/HACKERAI_HTTP_JITTER_MS) on noisy fuzzing. Every request is scope-gated by ${workdir}/recon/scope.txt (loopback is always allowed); add authorized targets there first.
|
|
71992
|
+
|
|
71993
|
+
2. findings \u2014 structured engagement memory. Record EVERY vulnerability you discover (action add: title+severity required; include evidence, target, url, param, impact, remediation). Flip status to "confirmed" with update once you have proof; "false_positive" if disproved. At the end, action report writes a clean Markdown report grouped by severity to ${workdir}/findings/report.md.
|
|
71994
|
+
|
|
71995
|
+
When to use which: use http_request for a single endpoint, replaying/tampering a captured request, parameter/path/value fuzzing, and exploit verification. Use the Python recon toolkit (recon/) for bulk crawling, port scanning, subdomain enum and intel.
|
|
71996
|
+
</native_pentest_tools>
|
|
71997
|
+
|
|
71998
|
+
<exploit_verification>
|
|
71999
|
+
ABSOLUTE RULE FOR THIS LOCAL CLI SESSION:
|
|
72000
|
+
|
|
72001
|
+
Never report a vulnerability as real on a guess. Run the PROVE-IT loop:
|
|
72002
|
+
1. SUSPECT \u2014 when a signal appears (from recon, a scanner, or a hunch), record it with findings (action add) at status "suspected" with the reasoning.
|
|
72003
|
+
2. PROVE \u2014 actually trigger it: send the exploit/PoC request with http_request (or a focused script) and OBSERVE the concrete response \u2014 the reflected payload, the leaked data, the auth bypass status code, the SQL error, the out-of-band callback, the differing length in a fuzz run.
|
|
72004
|
+
3. CONFIRM or DROP \u2014 if the observation proves impact, update the finding to status "confirmed" and paste the exact request+response evidence into its evidence field; if it does not reproduce, update to "false_positive". Only "confirmed" findings go in the executive summary.
|
|
72005
|
+
4. Capture severity (and CVSS when you can) and a concrete remediation on every confirmed finding.
|
|
72006
|
+
|
|
72007
|
+
This loop is mandatory: a finding without reproducible evidence is at most "suspected".
|
|
72008
|
+
</exploit_verification>`;
|
|
72009
|
+
reconPhasesPolicy = (workdir) => `
|
|
72010
|
+
|
|
72011
|
+
<recon_phases>
|
|
72012
|
+
ABSOLUTE RULE FOR THIS LOCAL CLI SESSION \u2014 applies to EVERY model and EVERY task:
|
|
72013
|
+
|
|
72014
|
+
Beyond recon/recon_deep.py (HTTP/JS/secret/browser recon), the workspace at ${workdir}/recon is seeded with companion modules for the other recon phases. They are all Python, all scope-gated by recon/scope.txt, and all degrade gracefully without paid keys. Use them instead of hand-writing equivalents:
|
|
72015
|
+
|
|
72016
|
+
- Subdomain enumeration: \`python recon/recon_subdomains.py example.com\` \u2014 pulls Certificate Transparency logs (crt.sh) and optionally brute-forces (\`--wordlist words.txt\`), then resolves. Add \`--add-scope\` to append live subdomains to scope.txt automatically.
|
|
72017
|
+
- Port scan + banner grab: \`python recon/recon_ports.py host\` \u2014 async TCP connect scan of common ports (\`--ports 1-1000\` or \`--ports 22,80,443\`, \`--top N\`), with banner grabbing and OPSEC throttle (\`--throttle-ms\`, \`--jitter-ms\`).
|
|
72018
|
+
- Content/path discovery: \`python recon/recon_content.py https://target\` \u2014 wordlist-driven path discovery (\`--wordlist\`), soft-404 detection, status filtering, throttle/jitter. (Use http_request for fuzzing a SINGLE known parameter; use this for bulk path discovery.)
|
|
72019
|
+
- Wayback / archive mining + CVE intel: \`python recon/recon_intel.py <domain|product>\` \u2014 historical URLs from the Wayback Machine CDX API, and free CVE lookup from the NVD API (\`--cve\` keyword search, e.g. product/version). GitHub code search for leaked secrets is supported when GITHUB_TOKEN is set (\`--github "org filename:.env"\`).
|
|
72020
|
+
|
|
72021
|
+
Each module writes a JSON (and where useful Markdown) report under ./reports/. Read it, then record anything actionable with the findings tool. You MAY extend any module via the file tool \u2014 keep it Python and keep the scope gate intact.
|
|
72022
|
+
</recon_phases>`;
|
|
70781
72023
|
skillsScopePolicy = () => `
|
|
70782
72024
|
|
|
70783
72025
|
<skills_scope>
|
|
@@ -70845,9 +72087,9 @@ ${section}` : "";
|
|
|
70845
72087
|
if (!envFlagEnabled(process.env.HACKERAI_CLI_DUMP_SYSTEM_PROMPT)) {
|
|
70846
72088
|
return null;
|
|
70847
72089
|
}
|
|
70848
|
-
await (0,
|
|
70849
|
-
const outputPath =
|
|
70850
|
-
await (0,
|
|
72090
|
+
await (0, import_promises2.mkdir)(workdir, { recursive: true });
|
|
72091
|
+
const outputPath = import_node_path10.default.join(workdir, "system-prompt.txt");
|
|
72092
|
+
await (0, import_promises2.writeFile)(outputPath, system, "utf8");
|
|
70851
72093
|
return outputPath;
|
|
70852
72094
|
};
|
|
70853
72095
|
auditSystemPrompt = (system, dumpPath) => {
|
|
@@ -71390,13 +72632,13 @@ async function main() {
|
|
|
71390
72632
|
const desktopDir = () => {
|
|
71391
72633
|
const home = import_node_os6.default.homedir();
|
|
71392
72634
|
const candidates = [
|
|
71393
|
-
process.env.OneDrive ?
|
|
71394
|
-
process.env.USERPROFILE ?
|
|
71395
|
-
|
|
71396
|
-
|
|
71397
|
-
|
|
72635
|
+
process.env.OneDrive ? import_node_path11.default.join(process.env.OneDrive, "Desktop") : null,
|
|
72636
|
+
process.env.USERPROFILE ? import_node_path11.default.join(process.env.USERPROFILE, "Desktop") : null,
|
|
72637
|
+
import_node_path11.default.join(home, "Desktop"),
|
|
72638
|
+
import_node_path11.default.join(home, "\xC1rea de Trabalho"),
|
|
72639
|
+
import_node_path11.default.join(home, "OneDrive", "Desktop")
|
|
71398
72640
|
].filter((p) => Boolean(p));
|
|
71399
|
-
return candidates.find((p) => (0,
|
|
72641
|
+
return candidates.find((p) => (0, import_node_fs10.existsSync)(p)) ?? home;
|
|
71400
72642
|
};
|
|
71401
72643
|
const printFatal = (err) => {
|
|
71402
72644
|
process.stderr.write(
|
|
@@ -71722,7 +72964,7 @@ ${C5.dim}salva em ${C5.reset}${C5.cyan}${file2}${C5.reset}
|
|
|
71722
72964
|
const sysText = agent.getSystemPrompt();
|
|
71723
72965
|
const audit = agent.getSystemPromptAudit();
|
|
71724
72966
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
|
|
71725
|
-
const filePath =
|
|
72967
|
+
const filePath = import_node_path11.default.join(
|
|
71726
72968
|
desktopDir(),
|
|
71727
72969
|
`clawfast-system-prompt_${stamp}.html`
|
|
71728
72970
|
);
|
|
@@ -71730,7 +72972,7 @@ ${C5.dim}salva em ${C5.reset}${C5.cyan}${file2}${C5.reset}
|
|
|
71730
72972
|
const b64 = Buffer.from(sysText, "utf8").toString("base64");
|
|
71731
72973
|
const html = `<!doctype html><html lang="pt-br"><head><meta charset="utf-8"><title>clawfast \u2014 system prompt</title><style>body{background:#0b0f0b;color:#bfffbf;font:14px/1.55 ui-monospace,Consolas,Menlo,monospace;margin:0;padding:28px}h1{color:#7CFC00;font-size:18px;margin:0 0 6px}.meta{color:#6f9a6f;margin:0 0 18px;font-size:12px}pre{white-space:pre-wrap;word-wrap:break-word;margin:0}</style></head><body><h1>clawfast \u2014 system prompt</h1><div class="meta">${meta3}</div><pre id="c"></pre><script>document.getElementById("c").textContent=decodeURIComponent(escape(atob("${b64}")));</script></body></html>`;
|
|
71732
72974
|
try {
|
|
71733
|
-
await (0,
|
|
72975
|
+
await (0, import_promises3.writeFile)(filePath, html, "utf8");
|
|
71734
72976
|
process.stdout.write(
|
|
71735
72977
|
`${C5.green}\u2713 system prompt salvo${C5.reset} ${C5.dim}(${sysText.length.toLocaleString()} caracteres \u2014 abra no navegador)${C5.reset}
|
|
71736
72978
|
${C5.cyan}${filePath}${C5.reset}
|
|
@@ -71840,14 +73082,14 @@ ${C5.dim}interrompido \u2014 Ctrl+C de novo para fechar${C5.reset}
|
|
|
71840
73082
|
}
|
|
71841
73083
|
await shutdown();
|
|
71842
73084
|
}
|
|
71843
|
-
var import_node_os6,
|
|
73085
|
+
var import_node_os6, import_node_path11, import_node_fs10, import_promises3, deepseekEnabled, configuredModelProviders;
|
|
71844
73086
|
var init_index = __esm({
|
|
71845
73087
|
"index.ts"() {
|
|
71846
73088
|
"use strict";
|
|
71847
73089
|
import_node_os6 = __toESM(require("node:os"));
|
|
71848
|
-
|
|
71849
|
-
|
|
71850
|
-
|
|
73090
|
+
import_node_path11 = __toESM(require("node:path"));
|
|
73091
|
+
import_node_fs10 = require("node:fs");
|
|
73092
|
+
import_promises3 = require("node:fs/promises");
|
|
71851
73093
|
init_paste_input();
|
|
71852
73094
|
init_boot_ui();
|
|
71853
73095
|
init_config();
|