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.
Files changed (2) hide show
  1. package/dist/clawfast.cjs +1524 -282
  2. 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 fs6 = require("fs");
89
- var path10 = require("path");
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 (fs6.existsSync(filepath)) {
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 = path10.resolve(process.cwd(), ".env.vault");
228
+ possibleVaultPath = path12.resolve(process.cwd(), ".env.vault");
229
229
  }
230
- if (fs6.existsSync(possibleVaultPath)) {
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] === "~" ? path10.join(os7.homedir(), envPath.slice(1)) : envPath;
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 = path10.resolve(process.cwd(), ".env");
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 path11 of optionPaths) {
281
+ for (const path13 of optionPaths) {
282
282
  try {
283
- const parsed = DotenvModule.parse(fs6.readFileSync(path11, { encoding }));
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 ${path11} ${e.message}`);
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 = path10.relative(process.cwd(), filePath);
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.1" : "0.0.0-dev";
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, path10) {
1725
- if (!path10)
1724
+ function getElementAtPath(obj, path12) {
1725
+ if (!path12)
1726
1726
  return obj;
1727
- return path10.reduce((acc, key) => acc?.[key], obj);
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(path10, issues) {
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(path10);
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, path10 = []) => {
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 }, [...path10, ...issue2.path]));
2280
+ issue2.errors.map((issues) => processError({ issues }, [...path12, ...issue2.path]));
2281
2281
  } else if (issue2.code === "invalid_key") {
2282
- processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
2282
+ processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
2283
2283
  } else if (issue2.code === "invalid_element") {
2284
- processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
2284
+ processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
2285
2285
  } else {
2286
- const fullpath = [...path10, ...issue2.path];
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, path10 = []) => {
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 }, [...path10, ...issue2.path]));
2317
+ issue2.errors.map((issues) => processError({ issues }, [...path12, ...issue2.path]));
2318
2318
  } else if (issue2.code === "invalid_key") {
2319
- processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
2319
+ processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
2320
2320
  } else if (issue2.code === "invalid_element") {
2321
- processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
2321
+ processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
2322
2322
  } else {
2323
- const fullpath = [...path10, ...issue2.path];
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 path10 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2356
- for (const seg of path10) {
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 path10 = ref.slice(1).split("/").filter(Boolean);
15860
- if (path10.length === 0) {
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 (path10[0] === defsKey) {
15865
- const key = path10[1];
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: path10, errorMaps, issueData } = params;
17055
- const fullPath = [...path10, ...issueData.path || []];
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, path10, key) {
17338
+ constructor(parent, value, path12, key) {
17339
17339
  this._cachedPath = [];
17340
17340
  this.parent = parent;
17341
17341
  this.data = value;
17342
- this._path = path10;
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 fs6 = __toESM2(require("fs"));
23382
- var path10 = __toESM2(require("path"));
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 path10.join(dataDir, "auth.json");
23391
+ return path12.join(dataDir, "auth.json");
23392
23392
  }
23393
23393
  function readAuthConfig() {
23394
23394
  try {
23395
23395
  const authPath = getAuthConfigPath();
23396
- if (!fs6.existsSync(authPath)) {
23396
+ if (!fs8.existsSync(authPath)) {
23397
23397
  return null;
23398
23398
  }
23399
- const content = fs6.readFileSync(authPath, "utf8");
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 = path10.dirname(authPath);
23411
- if (!fs6.existsSync(authDir)) {
23412
- fs6.mkdirSync(authDir, { mode: 504, recursive: true });
23410
+ const authDir = path12.dirname(authPath);
23411
+ if (!fs8.existsSync(authDir)) {
23412
+ fs8.mkdirSync(authDir, { mode: 504, recursive: true });
23413
23413
  }
23414
- fs6.writeFileSync(authPath, JSON.stringify(config3, null, 2), { mode: 384 });
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 path10 = __toESM2(require("path"));
23606
- var fs6 = __toESM2(require("fs"));
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 path10.join(dataDir, vercelFolder);
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 = path10.join(dir, ".vercel", "project.json");
23695
- if (!fs6.existsSync(prjPath)) {
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(fs6.readFileSync(prjPath, "utf8"));
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 = path10.join(dir, "com.vercel.token", `${projectId}.json`);
23715
+ const tokenPath = path12.join(dir, "com.vercel.token", `${projectId}.json`);
23716
23716
  const tokenJson = JSON.stringify(token);
23717
- fs6.mkdirSync(path10.dirname(tokenPath), { mode: 504, recursive: true });
23718
- fs6.writeFileSync(tokenPath, tokenJson);
23719
- fs6.chmodSync(tokenPath, 432);
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 = path10.join(dir, "com.vercel.token", `${projectId}.json`);
23730
- if (!fs6.existsSync(tokenPath)) {
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(fs6.readFileSync(tokenPath, "utf8"));
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: path10 }) => `${baseURL}${path10}`,
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: path10 }) => `${baseURL}${path10}`,
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: path10 }) => `${baseURL}${path10}`,
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: path10 }) => `${baseURL}${path10}`,
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: path10 }) => `${baseURL}${path10}`,
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: path10 }) => `${baseURL}${path10}`,
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: path10 }) => `${baseURL}${path10}`,
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: path10 }) => `${baseURL}${path10}`,
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: path10 }) => `${baseURL}${path10}`,
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: path10 }) => `${baseURL}${path10}`,
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: path10 }) => `${baseURL}${path10}`,
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: path10 }) => `${baseURL}${path10}`,
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(path10) {
48946
- let normalized = path10.trim().replace(/\/+/g, "/");
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(path10) {
49185
- return path10.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
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(path10, ranges, output) {
52074
+ function getContextLinesFromFile(path12, ranges, output) {
52075
52075
  return new Promise((resolve2) => {
52076
- const stream = (0, import_node_fs5.createReadStream)(path10);
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(path10, 1);
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(path10) {
52153
- return path10.startsWith("node:") || path10.endsWith(".min.js") || path10.endsWith(".min.cjs") || path10.endsWith(".min.mjs") || path10.startsWith("data:");
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(path10) {
67564
- return path10.split(/[\\/]/).some((segment) => segment.toLowerCase() === "sprit");
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: path10,
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(path10),
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(path10) {
67688
- if (/^[A-Za-z]:[\\/]/.test(path10)) return path10;
67689
- if (path10.startsWith("/tmp/")) {
67690
- return `C:\\temp${path10.slice(4).replace(/\//g, "\\")}`;
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 path10.replace(/\//g, "\\");
67692
+ return path12.replace(/\//g, "\\");
67693
67693
  }
67694
- function getPythonPathForSandbox(sandbox, path10) {
67695
- return isWindowsSandbox(sandbox) ? getWindowsNativePath(path10) : path10;
67694
+ function getPythonPathForSandbox(sandbox, path12) {
67695
+ return isWindowsSandbox(sandbox) ? getWindowsNativePath(path12) : path12;
67696
67696
  }
67697
- function toWindowsBashPath(path10) {
67698
- const drive = path10.match(/^([A-Za-z]):[\\/](.*)$/);
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 path10.replace(/\\/g, "/");
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, path10) {
67742
- const pythonPath = getPythonPathForSandbox(sandbox, path10);
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: path10,
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: path10 };
67766
+ return { ...payload, path: path12 };
67767
67767
  }
67768
67768
  if (payload.kind === "missing" || payload.kind === "not_file") {
67769
- return { ...payload, path: path10 };
67769
+ return { ...payload, path: path12 };
67770
67770
  }
67771
67771
  } catch {
67772
67772
  }
67773
67773
  return {
67774
67774
  kind: "unknown",
67775
- path: path10,
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, path10, range) {
67808
- const pythonPath = getPythonPathForSandbox(sandbox, path10);
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, path10, range) {
67836
+ async function readSandboxTextFileWithFallback(sandbox, path12, range) {
67837
67837
  try {
67838
- return await readSandboxTextFile(sandbox, path10, range);
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, path10);
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 ${path10}; refusing to load the file into memory. ${state.error}`
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: ${path10}`);
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: ${path10}`);
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 ${path10}, 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.`
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: path10,
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(path10, {
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: path10,
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: path10,
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, path10, text2) {
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, path10),
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, path10, includeData) {
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, path10);
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
- if range_end != -1 and range_end > total_lines:
68202
- emit({"error": f"Invalid end_line: {range_end}. File has {total_lines} lines (1-indexed)."}, 2)
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 = (path10) => path10.split("/").pop() || path10;
68285
- getFileExtension = (path10) => {
68286
- const filename = getFilename(path10);
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, path10) => {
68287
+ getSandboxViewPath = (sandbox, path12) => {
68292
68288
  const maybeSandbox = sandbox;
68293
- if (isCentrifugoSandbox(maybeSandbox) && maybeSandbox.isWindows() && path10.startsWith("/tmp/")) {
68294
- return `C:\\temp${path10.slice(4).replace(/\//g, "\\")}`;
68289
+ if (isCentrifugoSandbox(maybeSandbox) && maybeSandbox.isWindows() && path12.startsWith("/tmp/")) {
68290
+ return `C:\\temp${path12.slice(4).replace(/\//g, "\\")}`;
68295
68291
  }
68296
- return path10;
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: path10, text: text2, range, edits }) => {
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: path10,
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, path10, false);
68385
+ viewPayload = await readSandboxFileForView(sandbox, path12, false);
68390
68386
  } catch (error51) {
68391
68387
  captureFileViewImageUsage({
68392
68388
  context: context2,
68393
68389
  sandbox,
68394
- path: path10,
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(path10);
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: path10,
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: path10,
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: path10,
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: path10,
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 = path10.split("/").pop() || path10;
68454
- const spritChunked = isSpritPath(path10);
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
- path10,
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(path10, text2, {
68500
+ await sandbox.files.write(path12, text2, {
68505
68501
  user: "user"
68506
68502
  });
68507
- return `File written: ${path10}`;
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, path10);
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 ${path10}. ${existingState.error}`
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 ${path10} because it is not a file.`
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, path10, text2);
68521
+ await appendSandboxTextFile(sandbox, path12, text2);
68526
68522
  return {
68527
- content: `File appended: ${path10}
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(path10, {
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(path10, newContent, {
68535
+ await sandbox.files.write(path12, newContent, {
68540
68536
  user: "user"
68541
68537
  });
68542
68538
  return {
68543
- content: `File appended: ${path10}`,
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, path10);
68554
+ const existingState = await getSandboxFileState(sandbox, path12);
68559
68555
  if (existingState.kind === "unknown") {
68560
68556
  return {
68561
- error: `Cannot edit ${path10} safely because the file size could not be determined. ${existingState.error}`
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 ${path10} - file is empty or does not exist`
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 ${path10} because it is not a file.` };
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 ${path10} 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.`
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(path10, {
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 ${path10} - file is empty or does not exist`
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(path10, content, {
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 = import_node_path5.default.basename(shell2).toLowerCase();
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, import_node_fs6, import_node_path5, import_node_os3, import_node_url, import_meta, LocalSandbox;
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
- import_node_fs6 = require("node:fs");
68916
- import_node_path5 = __toESM(require("node:path"));
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 import_node_fs6.promises.mkdir(import_node_path5.default.dirname(resolved), { recursive: true });
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 import_node_fs6.promises.writeFile(resolved, data);
70126
+ await import_node_fs8.promises.writeFile(resolved, data);
69017
70127
  },
69018
70128
  read: async (filePath) => {
69019
- return import_node_fs6.promises.readFile(this.resolvePath(filePath), "utf8");
70129
+ return import_node_fs8.promises.readFile(this.resolvePath(filePath), "utf8");
69020
70130
  },
69021
70131
  remove: async (filePath) => {
69022
- await import_node_fs6.promises.rm(this.resolvePath(filePath), {
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 import_node_fs6.promises.readdir(resolved, { withFileTypes: true });
69031
- return entries.filter((e) => e.isFile()).map((e) => ({ name: import_node_path5.default.join(resolved, 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 || import_node_path5.default.join(process.cwd(), "SPRIT");
69047
- this.workdir = import_node_path5.default.resolve(base).replace(/\\/g, "/");
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 import_node_fs6.promises.mkdir(this.workdir, { recursive: true });
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 = import_node_path5.default.join(this.workdir, "recon");
69068
- await import_node_fs6.promises.mkdir(destDir, { recursive: true });
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 = import_node_path5.default.join(destDir, name25);
70191
+ const dest = import_node_path7.default.join(destDir, name25);
69077
70192
  if (!overwrite) {
69078
70193
  try {
69079
- await import_node_fs6.promises.access(dest);
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 import_node_fs6.promises.readFile(import_node_path5.default.join(assetsDir, name25));
69086
- await import_node_fs6.promises.writeFile(dest, content);
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 = import_node_path5.default.join(this.workdir, "audit");
69105
- await import_node_fs6.promises.mkdir(destDir, { recursive: true });
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 import_node_fs6.promises.readFile(import_node_path5.default.join(assetsDir, name25));
69109
- await import_node_fs6.promises.writeFile(import_node_path5.default.join(destDir, name25), content);
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 (import_node_path5.default.isAbsolute(p)) return p;
69241
- return import_node_path5.default.join(this.workdir, p);
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 = import_node_path5.default.basename(this.shellBin).toLowerCase();
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 = import_node_path5.default.basename(this.shellBin).toLowerCase();
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
- out(`${C3.dim} ok ${note}${C3.reset}
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
- out(`${C3.dim}${msg}${C3.reset}
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(`${C3.yellow}-> ${msg}${C3.reset}
70506
+ out(` ${C3.yellow}\u21C4 ${msg}${C3.reset}
69426
70507
  `);
69427
70508
  },
69428
70509
  error(msg) {
69429
70510
  sep3("info");
69430
- out(`${C3.red}x ${msg}${C3.reset}
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 `${C3.cyan}executando${C3.reset} ${C3.bold}$ ${truncate3(cmd, 400)}${C3.reset}${bg}`;
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 path10 = String(i.path ?? "");
69453
- const brief = i.brief ? `${C3.dim} - ${truncate3(String(i.brief))}${C3.reset}` : "";
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: `${C3.green}criando arquivo${C3.reset}`,
69456
- edit: `${C3.yellow}editando arquivo${C3.reset}`,
69457
- append: `${C3.green}anexando arquivo${C3.reset}`,
69458
- read: `${C3.blue}lendo arquivo${C3.reset}`,
69459
- view: `${C3.blue}visualizando arquivo${C3.reset}`
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 label = map2[action] ?? (action ? `${C3.blue}arquivo (${action})${C3.reset}` : `${C3.blue}preparando operacao de arquivo${C3.reset}`);
69463
- const target = path10 ? ` ${C3.bold}${path10}${C3.reset}` : "";
69464
- return `${label}${target}${brief}`;
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 `${C3.magenta}atualizando lista de tarefas${C3.reset}`;
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 `${C3.cyan}${toolName}${C3.reset} ${C3.dim}${truncate3(
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 `erro: ${truncate3(String(output.error), 160)}`;
70583
+ return {
70584
+ text: truncate3(String(output.error), 160),
70585
+ error: true
70586
+ };
69480
70587
  }
69481
- return "arquivo atualizado";
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, import_node_fs7.existsSync)(dir)) {
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, import_node_fs7.mkdirSync)(root, { recursive: true });
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, import_node_fs7.existsSync)(dir)) {
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, import_node_fs7.existsSync)(import_node_path6.default.join(dir, "node_modules"))) {
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, import_node_path6, import_node_fs7, import_node_child_process3, DEFS, PROXY_MODEL_KEYS, proxyIdForModelKey, proxiesRoot, dirFor, healthUrl, baseUrl, wait, started, exitHooksInstalled, quoteArg, asShellCommand, run, hasCommand;
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
- import_node_path6 = __toESM(require("node:path"));
69634
- import_node_fs7 = require("node:fs");
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 = () => import_node_path6.default.join(
69668
- process.env.CLAWFAST_HOME?.trim() || import_node_path6.default.join(import_node_os4.default.homedir(), ".clawfast"),
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() || import_node_path6.default.join(proxiesRoot(), def.folder);
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 import_node_path7.default.dirname(import_node_path7.default.resolve(workdir.replace(/\\/g, "/")));
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 import_node_path7, AUDIT_ACTION, AUDIT_SCOPE, AUDIT_ACTION_THEN_SCOPE, AUDIT_SCOPE_THEN_ACTION, AUDIT_STRONG, AUDIT_EXIT;
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
- import_node_path7 = __toESM(require("node:path"));
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 sleep(waitMs, signal);
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 import_promises, import_node_path8, MAX_STEPS, MAX_OUTPUT_TOKENS, MAX_AUTO_CONTINUES, MAX_RATE_LIMIT_WAITS, STREAM_STALL_TIMEOUT_MS, MAX_STALL_RETRIES, isRateLimitError, sleep, 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, skillsScopePolicy, hasEnvValue2, envFlagEnabled, CLI_PERSONALITIES, buildCliUserCustomization, buildCliNotesSection, cliGuardrailsConfig, maybeDumpSystemPrompt, auditSystemPrompt, assertFullSystemPrompt;
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
- import_promises = require("node:fs/promises");
70617
- import_node_path8 = __toESM(require("node:path"));
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
- sleep = (ms, signal) => new Promise((resolve2) => {
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, import_promises.mkdir)(workdir, { recursive: true });
70849
- const outputPath = import_node_path8.default.join(workdir, "system-prompt.txt");
70850
- await (0, import_promises.writeFile)(outputPath, system, "utf8");
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 ? import_node_path9.default.join(process.env.OneDrive, "Desktop") : null,
71394
- process.env.USERPROFILE ? import_node_path9.default.join(process.env.USERPROFILE, "Desktop") : null,
71395
- import_node_path9.default.join(home, "Desktop"),
71396
- import_node_path9.default.join(home, "\xC1rea de Trabalho"),
71397
- import_node_path9.default.join(home, "OneDrive", "Desktop")
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, import_node_fs8.existsSync)(p)) ?? home;
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 = import_node_path9.default.join(
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, import_promises2.writeFile)(filePath, html, "utf8");
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, import_node_path9, import_node_fs8, import_promises2, deepseekEnabled, configuredModelProviders;
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
- import_node_path9 = __toESM(require("node:path"));
71849
- import_node_fs8 = require("node:fs");
71850
- import_promises2 = require("node:fs/promises");
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();