clawfast 2.2.2 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -6
- package/dist/clawfast.cjs +1589 -699
- package/package.json +1 -1
package/dist/clawfast.cjs
CHANGED
|
@@ -199,10 +199,10 @@ var init_boot_ui = __esm({
|
|
|
199
199
|
var require_main = __commonJS({
|
|
200
200
|
"../node_modules/.pnpm/dotenv@17.4.2/node_modules/dotenv/lib/main.js"(exports2, module2) {
|
|
201
201
|
"use strict";
|
|
202
|
-
var
|
|
203
|
-
var
|
|
204
|
-
var
|
|
205
|
-
var
|
|
202
|
+
var fs10 = require("fs");
|
|
203
|
+
var path14 = require("path");
|
|
204
|
+
var os8 = require("os");
|
|
205
|
+
var crypto3 = require("crypto");
|
|
206
206
|
var TIPS = [
|
|
207
207
|
"\u25C8 encrypted .env [www.dotenvx.com]",
|
|
208
208
|
"\u25C8 secrets for agents [www.dotenvx.com]",
|
|
@@ -331,7 +331,7 @@ var require_main = __commonJS({
|
|
|
331
331
|
if (options && options.path && options.path.length > 0) {
|
|
332
332
|
if (Array.isArray(options.path)) {
|
|
333
333
|
for (const filepath of options.path) {
|
|
334
|
-
if (
|
|
334
|
+
if (fs10.existsSync(filepath)) {
|
|
335
335
|
possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
336
336
|
}
|
|
337
337
|
}
|
|
@@ -339,15 +339,15 @@ var require_main = __commonJS({
|
|
|
339
339
|
possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
|
|
340
340
|
}
|
|
341
341
|
} else {
|
|
342
|
-
possibleVaultPath =
|
|
342
|
+
possibleVaultPath = path14.resolve(process.cwd(), ".env.vault");
|
|
343
343
|
}
|
|
344
|
-
if (
|
|
344
|
+
if (fs10.existsSync(possibleVaultPath)) {
|
|
345
345
|
return possibleVaultPath;
|
|
346
346
|
}
|
|
347
347
|
return null;
|
|
348
348
|
}
|
|
349
349
|
function _resolveHome(envPath) {
|
|
350
|
-
return envPath[0] === "~" ?
|
|
350
|
+
return envPath[0] === "~" ? path14.join(os8.homedir(), envPath.slice(1)) : envPath;
|
|
351
351
|
}
|
|
352
352
|
function _configVault(options) {
|
|
353
353
|
const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
@@ -364,7 +364,7 @@ var require_main = __commonJS({
|
|
|
364
364
|
return { parsed };
|
|
365
365
|
}
|
|
366
366
|
function configDotenv(options) {
|
|
367
|
-
const dotenvPath =
|
|
367
|
+
const dotenvPath = path14.resolve(process.cwd(), ".env");
|
|
368
368
|
let encoding = "utf8";
|
|
369
369
|
let processEnv = process.env;
|
|
370
370
|
if (options && options.processEnv != null) {
|
|
@@ -392,13 +392,13 @@ var require_main = __commonJS({
|
|
|
392
392
|
}
|
|
393
393
|
let lastError;
|
|
394
394
|
const parsedAll = {};
|
|
395
|
-
for (const
|
|
395
|
+
for (const path15 of optionPaths) {
|
|
396
396
|
try {
|
|
397
|
-
const parsed = DotenvModule.parse(
|
|
397
|
+
const parsed = DotenvModule.parse(fs10.readFileSync(path15, { encoding }));
|
|
398
398
|
DotenvModule.populate(parsedAll, parsed, options);
|
|
399
399
|
} catch (e) {
|
|
400
400
|
if (debug) {
|
|
401
|
-
_debug(`failed to load ${
|
|
401
|
+
_debug(`failed to load ${path15} ${e.message}`);
|
|
402
402
|
}
|
|
403
403
|
lastError = e;
|
|
404
404
|
}
|
|
@@ -411,7 +411,7 @@ var require_main = __commonJS({
|
|
|
411
411
|
const shortPaths = [];
|
|
412
412
|
for (const filePath of optionPaths) {
|
|
413
413
|
try {
|
|
414
|
-
const relative2 =
|
|
414
|
+
const relative2 = path14.relative(process.cwd(), filePath);
|
|
415
415
|
shortPaths.push(relative2);
|
|
416
416
|
} catch (e) {
|
|
417
417
|
if (debug) {
|
|
@@ -446,7 +446,7 @@ var require_main = __commonJS({
|
|
|
446
446
|
const authTag = ciphertext.subarray(-16);
|
|
447
447
|
ciphertext = ciphertext.subarray(12, -16);
|
|
448
448
|
try {
|
|
449
|
-
const aesgcm =
|
|
449
|
+
const aesgcm = crypto3.createDecipheriv("aes-256-gcm", key, nonce);
|
|
450
450
|
aesgcm.setAuthTag(authTag);
|
|
451
451
|
return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
|
|
452
452
|
} catch (error51) {
|
|
@@ -565,7 +565,7 @@ async function testNvidiaKey(key) {
|
|
|
565
565
|
"Content-Type": "application/json"
|
|
566
566
|
},
|
|
567
567
|
body: JSON.stringify({
|
|
568
|
-
model: "
|
|
568
|
+
model: "mistralai/mistral-medium-3.5-128b",
|
|
569
569
|
messages: [{ role: "user", content: "hi" }],
|
|
570
570
|
max_tokens: 1
|
|
571
571
|
})
|
|
@@ -689,7 +689,7 @@ var clawfastVersion, isDevVersion, isNewerVersion;
|
|
|
689
689
|
var init_version = __esm({
|
|
690
690
|
"src/version.ts"() {
|
|
691
691
|
"use strict";
|
|
692
|
-
clawfastVersion = () => true ? "2.
|
|
692
|
+
clawfastVersion = () => true ? "2.4.1" : devVersionFromPackageJson();
|
|
693
693
|
isDevVersion = () => clawfastVersion().includes("-dev");
|
|
694
694
|
isNewerVersion = (a, b) => {
|
|
695
695
|
const parse3 = (v) => v.split("-")[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
@@ -1138,7 +1138,9 @@ function bannerSections(opts) {
|
|
|
1138
1138
|
}
|
|
1139
1139
|
function buildBanner(_systemPromptChars, version3 = "1") {
|
|
1140
1140
|
const { artRows, slogan, panel } = bannerSections({ version: version3 });
|
|
1141
|
-
return "\n" + ["", ...artRows, "", center(slogan, vlen(panel[0])), "", ...panel].join(
|
|
1141
|
+
return "\n" + ["", ...artRows, "", center(slogan, vlen(panel[0])), "", ...panel].join(
|
|
1142
|
+
"\n"
|
|
1143
|
+
) + "\n";
|
|
1142
1144
|
}
|
|
1143
1145
|
async function playBanner(opts = {}) {
|
|
1144
1146
|
const { artRows, slogan, panel } = bannerSections(opts);
|
|
@@ -1146,7 +1148,9 @@ async function playBanner(opts = {}) {
|
|
|
1146
1148
|
process.stdout.write("\n");
|
|
1147
1149
|
await revealLines(artRows, 55);
|
|
1148
1150
|
process.stdout.write("\n");
|
|
1149
|
-
const padLeft = " ".repeat(
|
|
1151
|
+
const padLeft = " ".repeat(
|
|
1152
|
+
Math.max(0, Math.floor((width - vlen(slogan)) / 2))
|
|
1153
|
+
);
|
|
1150
1154
|
await typeOut(SLOGAN, {
|
|
1151
1155
|
perChar: 14,
|
|
1152
1156
|
render: (s) => padLeft + gradient(s, { bold: true })
|
|
@@ -1154,8 +1158,10 @@ async function playBanner(opts = {}) {
|
|
|
1154
1158
|
process.stdout.write("\n");
|
|
1155
1159
|
await sweepRule(width);
|
|
1156
1160
|
for (const check2 of BOOT_CHECKS) {
|
|
1157
|
-
process.stdout.write(
|
|
1158
|
-
`)
|
|
1161
|
+
process.stdout.write(
|
|
1162
|
+
` ${paint("[\u2713]", PAL.green, { bold: true })} ${dim(check2)}
|
|
1163
|
+
`
|
|
1164
|
+
);
|
|
1159
1165
|
await sleep(110);
|
|
1160
1166
|
}
|
|
1161
1167
|
process.stdout.write("\n");
|
|
@@ -1261,6 +1267,12 @@ var init_news = __esm({
|
|
|
1261
1267
|
init_ui();
|
|
1262
1268
|
init_theme();
|
|
1263
1269
|
NEWS = {
|
|
1270
|
+
"2.3.0": [
|
|
1271
|
+
"Linux liberado e turbinado: o clawfast detecta Kali/Linux (e se e root) e entra em modo FULL-POWER \u2014 shell real, sockets, apt/pip/pipx, systemctl e todo o arsenal Kali (nmap, masscan, sqlmap, metasploit, hydra, hashcat, nuclei, gobuster/ffuf...).",
|
|
1272
|
+
"Python 100% liberado e scan a vontade: portas, faixas de IP/CIDR, servicos/versoes e ate numeros (OSINT de telefone com phoneinfoga). Tudo o que o SO permite, ele faz.",
|
|
1273
|
+
'Prompt do Kali repassado pra voce: quando uma ferramenta/instalador/login para e pede algo (criar conta, y/n, senha do sudo, OTP), o clawfast avisa exatamente o que "o kali" esta pedindo; voce digita aqui e ele repassa pro programa pelo stdin e segue.',
|
|
1274
|
+
"Escopo liberado por padrao no Linux: http_request e o toolkit de recon nao exigem mais editar scope.txt. Volte ao restrito com CLAWFAST_STRICT_SCOPE=1; force liberado em qualquer SO com CLAWFAST_UNRESTRICTED=1."
|
|
1275
|
+
],
|
|
1264
1276
|
"2.1.0": [
|
|
1265
1277
|
'Novo visual "Verde Matrix Elite": gradiente verde\u2192teal\u2192ciano em todo o terminal. Banner cinematografico no boot \u2014 o wordmark desce em cortina, o slogan se datilografa e o painel "ACESSO CONCEDIDO" abaixa.',
|
|
1266
1278
|
"Header de cada turno animado, spinner de boot pulsando em gradiente e caixas (prompt, /model, /nov) com bordas pesadas realcadas.",
|
|
@@ -1937,10 +1949,10 @@ function mergeDefs(...defs) {
|
|
|
1937
1949
|
function cloneDef(schema) {
|
|
1938
1950
|
return mergeDefs(schema._zod.def);
|
|
1939
1951
|
}
|
|
1940
|
-
function getElementAtPath(obj,
|
|
1941
|
-
if (!
|
|
1952
|
+
function getElementAtPath(obj, path14) {
|
|
1953
|
+
if (!path14)
|
|
1942
1954
|
return obj;
|
|
1943
|
-
return
|
|
1955
|
+
return path14.reduce((acc, key) => acc?.[key], obj);
|
|
1944
1956
|
}
|
|
1945
1957
|
function promiseAllObject(promisesObj) {
|
|
1946
1958
|
const keys = Object.keys(promisesObj);
|
|
@@ -2268,11 +2280,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
2268
2280
|
}
|
|
2269
2281
|
return false;
|
|
2270
2282
|
}
|
|
2271
|
-
function prefixIssues(
|
|
2283
|
+
function prefixIssues(path14, issues) {
|
|
2272
2284
|
return issues.map((iss) => {
|
|
2273
2285
|
var _a25;
|
|
2274
2286
|
(_a25 = iss).path ?? (_a25.path = []);
|
|
2275
|
-
iss.path.unshift(
|
|
2287
|
+
iss.path.unshift(path14);
|
|
2276
2288
|
return iss;
|
|
2277
2289
|
});
|
|
2278
2290
|
}
|
|
@@ -2490,16 +2502,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2490
2502
|
}
|
|
2491
2503
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
2492
2504
|
const fieldErrors = { _errors: [] };
|
|
2493
|
-
const processError = (error52,
|
|
2505
|
+
const processError = (error52, path14 = []) => {
|
|
2494
2506
|
for (const issue2 of error52.issues) {
|
|
2495
2507
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2496
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2508
|
+
issue2.errors.map((issues) => processError({ issues }, [...path14, ...issue2.path]));
|
|
2497
2509
|
} else if (issue2.code === "invalid_key") {
|
|
2498
|
-
processError({ issues: issue2.issues }, [...
|
|
2510
|
+
processError({ issues: issue2.issues }, [...path14, ...issue2.path]);
|
|
2499
2511
|
} else if (issue2.code === "invalid_element") {
|
|
2500
|
-
processError({ issues: issue2.issues }, [...
|
|
2512
|
+
processError({ issues: issue2.issues }, [...path14, ...issue2.path]);
|
|
2501
2513
|
} else {
|
|
2502
|
-
const fullpath = [...
|
|
2514
|
+
const fullpath = [...path14, ...issue2.path];
|
|
2503
2515
|
if (fullpath.length === 0) {
|
|
2504
2516
|
fieldErrors._errors.push(mapper(issue2));
|
|
2505
2517
|
} else {
|
|
@@ -2526,17 +2538,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2526
2538
|
}
|
|
2527
2539
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
2528
2540
|
const result = { errors: [] };
|
|
2529
|
-
const processError = (error52,
|
|
2541
|
+
const processError = (error52, path14 = []) => {
|
|
2530
2542
|
var _a25, _b18;
|
|
2531
2543
|
for (const issue2 of error52.issues) {
|
|
2532
2544
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2533
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2545
|
+
issue2.errors.map((issues) => processError({ issues }, [...path14, ...issue2.path]));
|
|
2534
2546
|
} else if (issue2.code === "invalid_key") {
|
|
2535
|
-
processError({ issues: issue2.issues }, [...
|
|
2547
|
+
processError({ issues: issue2.issues }, [...path14, ...issue2.path]);
|
|
2536
2548
|
} else if (issue2.code === "invalid_element") {
|
|
2537
|
-
processError({ issues: issue2.issues }, [...
|
|
2549
|
+
processError({ issues: issue2.issues }, [...path14, ...issue2.path]);
|
|
2538
2550
|
} else {
|
|
2539
|
-
const fullpath = [...
|
|
2551
|
+
const fullpath = [...path14, ...issue2.path];
|
|
2540
2552
|
if (fullpath.length === 0) {
|
|
2541
2553
|
result.errors.push(mapper(issue2));
|
|
2542
2554
|
continue;
|
|
@@ -2568,8 +2580,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2568
2580
|
}
|
|
2569
2581
|
function toDotPath(_path) {
|
|
2570
2582
|
const segs = [];
|
|
2571
|
-
const
|
|
2572
|
-
for (const seg of
|
|
2583
|
+
const path14 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2584
|
+
for (const seg of path14) {
|
|
2573
2585
|
if (typeof seg === "number")
|
|
2574
2586
|
segs.push(`[${seg}]`);
|
|
2575
2587
|
else if (typeof seg === "symbol")
|
|
@@ -16072,13 +16084,13 @@ function resolveRef(ref, ctx) {
|
|
|
16072
16084
|
if (!ref.startsWith("#")) {
|
|
16073
16085
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
16074
16086
|
}
|
|
16075
|
-
const
|
|
16076
|
-
if (
|
|
16087
|
+
const path14 = ref.slice(1).split("/").filter(Boolean);
|
|
16088
|
+
if (path14.length === 0) {
|
|
16077
16089
|
return ctx.rootSchema;
|
|
16078
16090
|
}
|
|
16079
16091
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
16080
|
-
if (
|
|
16081
|
-
const key =
|
|
16092
|
+
if (path14[0] === defsKey) {
|
|
16093
|
+
const key = path14[1];
|
|
16082
16094
|
if (!key || !ctx.defs[key]) {
|
|
16083
16095
|
throw new Error(`Reference not found: ${ref}`);
|
|
16084
16096
|
}
|
|
@@ -17267,8 +17279,8 @@ var init_parseUtil = __esm({
|
|
|
17267
17279
|
init_errors3();
|
|
17268
17280
|
init_en2();
|
|
17269
17281
|
makeIssue = (params) => {
|
|
17270
|
-
const { data, path:
|
|
17271
|
-
const fullPath = [...
|
|
17282
|
+
const { data, path: path14, errorMaps, issueData } = params;
|
|
17283
|
+
const fullPath = [...path14, ...issueData.path || []];
|
|
17272
17284
|
const fullIssue = {
|
|
17273
17285
|
...issueData,
|
|
17274
17286
|
path: fullPath
|
|
@@ -17551,11 +17563,11 @@ var init_types = __esm({
|
|
|
17551
17563
|
init_parseUtil();
|
|
17552
17564
|
init_util2();
|
|
17553
17565
|
ParseInputLazyPath = class {
|
|
17554
|
-
constructor(parent, value,
|
|
17566
|
+
constructor(parent, value, path14, key) {
|
|
17555
17567
|
this._cachedPath = [];
|
|
17556
17568
|
this.parent = parent;
|
|
17557
17569
|
this.data = value;
|
|
17558
|
-
this._path =
|
|
17570
|
+
this._path = path14;
|
|
17559
17571
|
this._key = key;
|
|
17560
17572
|
}
|
|
17561
17573
|
get path() {
|
|
@@ -23594,8 +23606,8 @@ var require_auth_config = __commonJS({
|
|
|
23594
23606
|
writeAuthConfig: () => writeAuthConfig
|
|
23595
23607
|
});
|
|
23596
23608
|
module2.exports = __toCommonJS(auth_config_exports);
|
|
23597
|
-
var
|
|
23598
|
-
var
|
|
23609
|
+
var fs10 = __toESM2(require("fs"));
|
|
23610
|
+
var path14 = __toESM2(require("path"));
|
|
23599
23611
|
var import_token_util = require_token_util();
|
|
23600
23612
|
function getAuthConfigPath() {
|
|
23601
23613
|
const dataDir = (0, import_token_util.getVercelDataDir)();
|
|
@@ -23604,15 +23616,15 @@ var require_auth_config = __commonJS({
|
|
|
23604
23616
|
`Unable to find Vercel CLI data directory. Your platform: ${process.platform}. Supported: darwin, linux, win32.`
|
|
23605
23617
|
);
|
|
23606
23618
|
}
|
|
23607
|
-
return
|
|
23619
|
+
return path14.join(dataDir, "auth.json");
|
|
23608
23620
|
}
|
|
23609
23621
|
function readAuthConfig() {
|
|
23610
23622
|
try {
|
|
23611
23623
|
const authPath = getAuthConfigPath();
|
|
23612
|
-
if (!
|
|
23624
|
+
if (!fs10.existsSync(authPath)) {
|
|
23613
23625
|
return null;
|
|
23614
23626
|
}
|
|
23615
|
-
const content =
|
|
23627
|
+
const content = fs10.readFileSync(authPath, "utf8");
|
|
23616
23628
|
if (!content) {
|
|
23617
23629
|
return null;
|
|
23618
23630
|
}
|
|
@@ -23623,11 +23635,11 @@ var require_auth_config = __commonJS({
|
|
|
23623
23635
|
}
|
|
23624
23636
|
function writeAuthConfig(config3) {
|
|
23625
23637
|
const authPath = getAuthConfigPath();
|
|
23626
|
-
const authDir =
|
|
23627
|
-
if (!
|
|
23628
|
-
|
|
23638
|
+
const authDir = path14.dirname(authPath);
|
|
23639
|
+
if (!fs10.existsSync(authDir)) {
|
|
23640
|
+
fs10.mkdirSync(authDir, { mode: 504, recursive: true });
|
|
23629
23641
|
}
|
|
23630
|
-
|
|
23642
|
+
fs10.writeFileSync(authPath, JSON.stringify(config3, null, 2), { mode: 384 });
|
|
23631
23643
|
}
|
|
23632
23644
|
function isValidAccessToken(authConfig, expirationBufferMs = 0) {
|
|
23633
23645
|
if (!authConfig.token)
|
|
@@ -23818,8 +23830,8 @@ var require_token_util = __commonJS({
|
|
|
23818
23830
|
saveToken: () => saveToken
|
|
23819
23831
|
});
|
|
23820
23832
|
module2.exports = __toCommonJS(token_util_exports);
|
|
23821
|
-
var
|
|
23822
|
-
var
|
|
23833
|
+
var path14 = __toESM2(require("path"));
|
|
23834
|
+
var fs10 = __toESM2(require("fs"));
|
|
23823
23835
|
var import_token_error = require_token_error();
|
|
23824
23836
|
var import_token_io = require_token_io();
|
|
23825
23837
|
var import_auth_config = require_auth_config();
|
|
@@ -23831,7 +23843,7 @@ var require_token_util = __commonJS({
|
|
|
23831
23843
|
if (!dataDir) {
|
|
23832
23844
|
return null;
|
|
23833
23845
|
}
|
|
23834
|
-
return
|
|
23846
|
+
return path14.join(dataDir, vercelFolder);
|
|
23835
23847
|
}
|
|
23836
23848
|
async function getVercelToken2(options) {
|
|
23837
23849
|
const authConfig = (0, import_auth_config.readAuthConfig)();
|
|
@@ -23907,13 +23919,13 @@ var require_token_util = __commonJS({
|
|
|
23907
23919
|
"Unable to find project root directory. Have you linked your project with `vc link?`"
|
|
23908
23920
|
);
|
|
23909
23921
|
}
|
|
23910
|
-
const prjPath =
|
|
23911
|
-
if (!
|
|
23922
|
+
const prjPath = path14.join(dir, ".vercel", "project.json");
|
|
23923
|
+
if (!fs10.existsSync(prjPath)) {
|
|
23912
23924
|
throw new import_token_error.VercelOidcTokenError(
|
|
23913
23925
|
"project.json not found, have you linked your project with `vc link?`"
|
|
23914
23926
|
);
|
|
23915
23927
|
}
|
|
23916
|
-
const prj = JSON.parse(
|
|
23928
|
+
const prj = JSON.parse(fs10.readFileSync(prjPath, "utf8"));
|
|
23917
23929
|
if (typeof prj.projectId !== "string" && typeof prj.orgId !== "string") {
|
|
23918
23930
|
throw new TypeError(
|
|
23919
23931
|
"Expected a string-valued projectId property. Try running `vc link` to re-link your project."
|
|
@@ -23928,11 +23940,11 @@ var require_token_util = __commonJS({
|
|
|
23928
23940
|
"Unable to find user data directory. Please reach out to Vercel support."
|
|
23929
23941
|
);
|
|
23930
23942
|
}
|
|
23931
|
-
const tokenPath =
|
|
23943
|
+
const tokenPath = path14.join(dir, "com.vercel.token", `${projectId}.json`);
|
|
23932
23944
|
const tokenJson = JSON.stringify(token);
|
|
23933
|
-
|
|
23934
|
-
|
|
23935
|
-
|
|
23945
|
+
fs10.mkdirSync(path14.dirname(tokenPath), { mode: 504, recursive: true });
|
|
23946
|
+
fs10.writeFileSync(tokenPath, tokenJson);
|
|
23947
|
+
fs10.chmodSync(tokenPath, 432);
|
|
23936
23948
|
return;
|
|
23937
23949
|
}
|
|
23938
23950
|
function loadToken(projectId) {
|
|
@@ -23942,11 +23954,11 @@ var require_token_util = __commonJS({
|
|
|
23942
23954
|
"Unable to find user data directory. Please reach out to Vercel support."
|
|
23943
23955
|
);
|
|
23944
23956
|
}
|
|
23945
|
-
const tokenPath =
|
|
23946
|
-
if (!
|
|
23957
|
+
const tokenPath = path14.join(dir, "com.vercel.token", `${projectId}.json`);
|
|
23958
|
+
if (!fs10.existsSync(tokenPath)) {
|
|
23947
23959
|
return null;
|
|
23948
23960
|
}
|
|
23949
|
-
const token = JSON.parse(
|
|
23961
|
+
const token = JSON.parse(fs10.readFileSync(tokenPath, "utf8"));
|
|
23950
23962
|
assertVercelOidcTokenResponse(token);
|
|
23951
23963
|
return token;
|
|
23952
23964
|
}
|
|
@@ -35972,7 +35984,7 @@ function createOpenRouter(options = {}) {
|
|
|
35972
35984
|
);
|
|
35973
35985
|
const createChatModel = (modelId, settings = {}) => new OpenRouterChatLanguageModel(modelId, settings, {
|
|
35974
35986
|
provider: "openrouter.chat",
|
|
35975
|
-
url: ({ path:
|
|
35987
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
35976
35988
|
headers: getHeaders,
|
|
35977
35989
|
compatibility,
|
|
35978
35990
|
fetch: options.fetch,
|
|
@@ -35980,7 +35992,7 @@ function createOpenRouter(options = {}) {
|
|
|
35980
35992
|
});
|
|
35981
35993
|
const createCompletionModel = (modelId, settings = {}) => new OpenRouterCompletionLanguageModel(modelId, settings, {
|
|
35982
35994
|
provider: "openrouter.completion",
|
|
35983
|
-
url: ({ path:
|
|
35995
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
35984
35996
|
headers: getHeaders,
|
|
35985
35997
|
compatibility,
|
|
35986
35998
|
fetch: options.fetch,
|
|
@@ -35988,21 +36000,21 @@ function createOpenRouter(options = {}) {
|
|
|
35988
36000
|
});
|
|
35989
36001
|
const createEmbeddingModel = (modelId, settings = {}) => new OpenRouterEmbeddingModel(modelId, settings, {
|
|
35990
36002
|
provider: "openrouter.embedding",
|
|
35991
|
-
url: ({ path:
|
|
36003
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
35992
36004
|
headers: getHeaders,
|
|
35993
36005
|
fetch: options.fetch,
|
|
35994
36006
|
extraBody: options.extraBody
|
|
35995
36007
|
});
|
|
35996
36008
|
const createImageModel = (modelId, settings = {}) => new OpenRouterImageModel(modelId, settings, {
|
|
35997
36009
|
provider: "openrouter.image",
|
|
35998
|
-
url: ({ path:
|
|
36010
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
35999
36011
|
headers: getHeaders,
|
|
36000
36012
|
fetch: options.fetch,
|
|
36001
36013
|
extraBody: options.extraBody
|
|
36002
36014
|
});
|
|
36003
36015
|
const createVideoModel = (modelId, settings = {}) => new OpenRouterVideoModel(modelId, settings, {
|
|
36004
36016
|
provider: "openrouter.video",
|
|
36005
|
-
url: ({ path:
|
|
36017
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
36006
36018
|
headers: getHeaders,
|
|
36007
36019
|
fetch: options.fetch,
|
|
36008
36020
|
extraBody: options.extraBody
|
|
@@ -40538,37 +40550,37 @@ function createOpenAI(options = {}) {
|
|
|
40538
40550
|
);
|
|
40539
40551
|
const createChatModel = (modelId) => new OpenAIChatLanguageModel(modelId, {
|
|
40540
40552
|
provider: `${providerName}.chat`,
|
|
40541
|
-
url: ({ path:
|
|
40553
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
40542
40554
|
headers: getHeaders,
|
|
40543
40555
|
fetch: options.fetch
|
|
40544
40556
|
});
|
|
40545
40557
|
const createCompletionModel = (modelId) => new OpenAICompletionLanguageModel(modelId, {
|
|
40546
40558
|
provider: `${providerName}.completion`,
|
|
40547
|
-
url: ({ path:
|
|
40559
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
40548
40560
|
headers: getHeaders,
|
|
40549
40561
|
fetch: options.fetch
|
|
40550
40562
|
});
|
|
40551
40563
|
const createEmbeddingModel = (modelId) => new OpenAIEmbeddingModel(modelId, {
|
|
40552
40564
|
provider: `${providerName}.embedding`,
|
|
40553
|
-
url: ({ path:
|
|
40565
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
40554
40566
|
headers: getHeaders,
|
|
40555
40567
|
fetch: options.fetch
|
|
40556
40568
|
});
|
|
40557
40569
|
const createImageModel = (modelId) => new OpenAIImageModel(modelId, {
|
|
40558
40570
|
provider: `${providerName}.image`,
|
|
40559
|
-
url: ({ path:
|
|
40571
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
40560
40572
|
headers: getHeaders,
|
|
40561
40573
|
fetch: options.fetch
|
|
40562
40574
|
});
|
|
40563
40575
|
const createTranscriptionModel = (modelId) => new OpenAITranscriptionModel(modelId, {
|
|
40564
40576
|
provider: `${providerName}.transcription`,
|
|
40565
|
-
url: ({ path:
|
|
40577
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
40566
40578
|
headers: getHeaders,
|
|
40567
40579
|
fetch: options.fetch
|
|
40568
40580
|
});
|
|
40569
40581
|
const createSpeechModel = (modelId) => new OpenAISpeechModel(modelId, {
|
|
40570
40582
|
provider: `${providerName}.speech`,
|
|
40571
|
-
url: ({ path:
|
|
40583
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
40572
40584
|
headers: getHeaders,
|
|
40573
40585
|
fetch: options.fetch
|
|
40574
40586
|
});
|
|
@@ -40583,7 +40595,7 @@ function createOpenAI(options = {}) {
|
|
|
40583
40595
|
const createResponsesModel = (modelId) => {
|
|
40584
40596
|
return new OpenAIResponsesLanguageModel(modelId, {
|
|
40585
40597
|
provider: `${providerName}.responses`,
|
|
40586
|
-
url: ({ path:
|
|
40598
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
40587
40599
|
headers: getHeaders,
|
|
40588
40600
|
fetch: options.fetch,
|
|
40589
40601
|
fileIdPrefixes: ["file-"]
|
|
@@ -46085,7 +46097,6 @@ var init_providers = __esm({
|
|
|
46085
46097
|
"model-nvidia-mistral-medium-3.5": nvidia.chat(
|
|
46086
46098
|
"mistralai/mistral-medium-3.5-128b"
|
|
46087
46099
|
),
|
|
46088
|
-
"model-nvidia-gpt-oss-120b": nvidia.chat("openai/gpt-oss-120b"),
|
|
46089
46100
|
"model-nvidia-glm-5.1": nvidia.chat("z-ai/glm-5.1"),
|
|
46090
46101
|
"model-nvidia-qwen3.5-397b": nvidia.chat("qwen/qwen3.5-397b-a17b"),
|
|
46091
46102
|
// Extra explicit keys for the CLI.
|
|
@@ -46123,8 +46134,6 @@ var init_providers = __esm({
|
|
|
46123
46134
|
...hasEnvValue("NVIDIA_API_KEY") ? [
|
|
46124
46135
|
"model-nvidia-mistral-medium-3.5",
|
|
46125
46136
|
// NVIDIA mistralai/mistral-medium-3.5-128b
|
|
46126
|
-
"model-nvidia-gpt-oss-120b",
|
|
46127
|
-
// NVIDIA openai/gpt-oss-120b
|
|
46128
46137
|
"model-nvidia-glm-5.1",
|
|
46129
46138
|
// NVIDIA z-ai/glm-5.1
|
|
46130
46139
|
"model-nvidia-qwen3.5-397b"
|
|
@@ -46150,7 +46159,6 @@ var init_providers = __esm({
|
|
|
46150
46159
|
"model-opus-4.6": "May 2025",
|
|
46151
46160
|
"model-kimi-k2.6": "April 2024",
|
|
46152
46161
|
"model-nvidia-mistral-medium-3.5": "Unknown",
|
|
46153
|
-
"model-nvidia-gpt-oss-120b": "June 2024",
|
|
46154
46162
|
"model-nvidia-glm-5.1": "Unknown",
|
|
46155
46163
|
"model-nvidia-qwen3.5-397b": "Unknown",
|
|
46156
46164
|
"model-deepseek-proxy": "July 2024",
|
|
@@ -46174,7 +46182,6 @@ var init_providers = __esm({
|
|
|
46174
46182
|
"model-opus-4.6": "Anthropic Claude Opus 4.6",
|
|
46175
46183
|
"model-kimi-k2.6": "Moonshot Kimi K2.6",
|
|
46176
46184
|
"model-nvidia-mistral-medium-3.5": "NVIDIA - Mistral Medium 3.5",
|
|
46177
|
-
"model-nvidia-gpt-oss-120b": "NVIDIA - OpenAI GPT-OSS 120B",
|
|
46178
46185
|
"model-nvidia-glm-5.1": "NVIDIA - Z.ai GLM 5.1",
|
|
46179
46186
|
"model-nvidia-qwen3.5-397b": "NVIDIA - Qwen3.5 397B A17B",
|
|
46180
46187
|
"model-deepseek-proxy": "DeepSeek (sessao web logada / deepsproxy)",
|
|
@@ -49218,8 +49225,8 @@ var init_background_process_tracker = __esm({
|
|
|
49218
49225
|
/**
|
|
49219
49226
|
* Normalize file path for comparison
|
|
49220
49227
|
*/
|
|
49221
|
-
normalizePath(
|
|
49222
|
-
let normalized =
|
|
49228
|
+
normalizePath(path14) {
|
|
49229
|
+
let normalized = path14.trim().replace(/\/+/g, "/");
|
|
49223
49230
|
if (normalized.startsWith("./")) {
|
|
49224
49231
|
normalized = normalized.slice(2);
|
|
49225
49232
|
}
|
|
@@ -49457,8 +49464,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
|
|
|
49457
49464
|
return decodedFile;
|
|
49458
49465
|
};
|
|
49459
49466
|
}
|
|
49460
|
-
function normalizeWindowsPath(
|
|
49461
|
-
return
|
|
49467
|
+
function normalizeWindowsPath(path14) {
|
|
49468
|
+
return path14.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
|
|
49462
49469
|
}
|
|
49463
49470
|
var import_path;
|
|
49464
49471
|
var init_module_node = __esm({
|
|
@@ -52347,9 +52354,9 @@ async function addSourceContext(frames) {
|
|
|
52347
52354
|
LRU_FILE_CONTENTS_CACHE.reduce();
|
|
52348
52355
|
return frames;
|
|
52349
52356
|
}
|
|
52350
|
-
function getContextLinesFromFile(
|
|
52357
|
+
function getContextLinesFromFile(path14, ranges, output) {
|
|
52351
52358
|
return new Promise((resolve2) => {
|
|
52352
|
-
const stream = (0, import_node_fs5.createReadStream)(
|
|
52359
|
+
const stream = (0, import_node_fs5.createReadStream)(path14);
|
|
52353
52360
|
const lineReaded = (0, import_node_readline2.createInterface)({
|
|
52354
52361
|
input: stream
|
|
52355
52362
|
});
|
|
@@ -52364,7 +52371,7 @@ function getContextLinesFromFile(path12, ranges, output) {
|
|
|
52364
52371
|
let rangeStart = range[0];
|
|
52365
52372
|
let rangeEnd = range[1];
|
|
52366
52373
|
function onStreamError() {
|
|
52367
|
-
LRU_FILE_CONTENTS_FS_READ_FAILED.set(
|
|
52374
|
+
LRU_FILE_CONTENTS_FS_READ_FAILED.set(path14, 1);
|
|
52368
52375
|
lineReaded.close();
|
|
52369
52376
|
lineReaded.removeAllListeners();
|
|
52370
52377
|
destroyStreamAndResolve();
|
|
@@ -52425,8 +52432,8 @@ function clearLineContext(frame3) {
|
|
|
52425
52432
|
delete frame3.context_line;
|
|
52426
52433
|
delete frame3.post_context;
|
|
52427
52434
|
}
|
|
52428
|
-
function shouldSkipContextLinesForFile(
|
|
52429
|
-
return
|
|
52435
|
+
function shouldSkipContextLinesForFile(path14) {
|
|
52436
|
+
return path14.startsWith("node:") || path14.endsWith(".min.js") || path14.endsWith(".min.cjs") || path14.endsWith(".min.mjs") || path14.startsWith("data:");
|
|
52430
52437
|
}
|
|
52431
52438
|
function shouldSkipContextLinesForFrame(frame3) {
|
|
52432
52439
|
if (void 0 !== frame3.lineno && frame3.lineno > MAX_CONTEXTLINES_LINENO) return true;
|
|
@@ -67836,8 +67843,8 @@ var init_logger2 = __esm({
|
|
|
67836
67843
|
});
|
|
67837
67844
|
|
|
67838
67845
|
// ../lib/ai/tools/file.ts
|
|
67839
|
-
function isSpritPath(
|
|
67840
|
-
return
|
|
67846
|
+
function isSpritPath(path14) {
|
|
67847
|
+
return path14.split(/[\\/]/).some((segment) => segment.toLowerCase() === "sprit");
|
|
67841
67848
|
}
|
|
67842
67849
|
function getViewSandboxType(sandbox) {
|
|
67843
67850
|
return isCentrifugoSandbox(sandbox) ? "centrifugo" : "e2b";
|
|
@@ -67868,7 +67875,7 @@ function captureFileViewImageUsage(args) {
|
|
|
67868
67875
|
const {
|
|
67869
67876
|
context: context2,
|
|
67870
67877
|
sandbox,
|
|
67871
|
-
path:
|
|
67878
|
+
path: path14,
|
|
67872
67879
|
outcome,
|
|
67873
67880
|
durationMs,
|
|
67874
67881
|
mediaType,
|
|
@@ -67886,7 +67893,7 @@ function captureFileViewImageUsage(args) {
|
|
|
67886
67893
|
model: getActiveModelName(context2),
|
|
67887
67894
|
configured_model: context2.modelName,
|
|
67888
67895
|
sandbox_type: getViewSandboxType(sandbox),
|
|
67889
|
-
file_extension: getFileExtension(
|
|
67896
|
+
file_extension: getFileExtension(path14),
|
|
67890
67897
|
outcome,
|
|
67891
67898
|
success: outcome === "success",
|
|
67892
67899
|
duration_ms: durationMs,
|
|
@@ -67960,22 +67967,22 @@ async function runSandboxCommand(sandbox, command, envVars, timeoutMs = 6e4) {
|
|
|
67960
67967
|
function isWindowsSandbox(sandbox) {
|
|
67961
67968
|
return isCentrifugoSandbox(sandbox) && sandbox.isWindows();
|
|
67962
67969
|
}
|
|
67963
|
-
function getWindowsNativePath(
|
|
67964
|
-
if (/^[A-Za-z]:[\\/]/.test(
|
|
67965
|
-
if (
|
|
67966
|
-
return `C:\\temp${
|
|
67970
|
+
function getWindowsNativePath(path14) {
|
|
67971
|
+
if (/^[A-Za-z]:[\\/]/.test(path14)) return path14;
|
|
67972
|
+
if (path14.startsWith("/tmp/")) {
|
|
67973
|
+
return `C:\\temp${path14.slice(4).replace(/\//g, "\\")}`;
|
|
67967
67974
|
}
|
|
67968
|
-
return
|
|
67975
|
+
return path14.replace(/\//g, "\\");
|
|
67969
67976
|
}
|
|
67970
|
-
function getPythonPathForSandbox(sandbox,
|
|
67971
|
-
return isWindowsSandbox(sandbox) ? getWindowsNativePath(
|
|
67977
|
+
function getPythonPathForSandbox(sandbox, path14) {
|
|
67978
|
+
return isWindowsSandbox(sandbox) ? getWindowsNativePath(path14) : path14;
|
|
67972
67979
|
}
|
|
67973
|
-
function toWindowsBashPath(
|
|
67974
|
-
const drive =
|
|
67980
|
+
function toWindowsBashPath(path14) {
|
|
67981
|
+
const drive = path14.match(/^([A-Za-z]):[\\/](.*)$/);
|
|
67975
67982
|
if (drive) {
|
|
67976
67983
|
return `/${drive[1].toLowerCase()}/${drive[2].replace(/\\/g, "/")}`;
|
|
67977
67984
|
}
|
|
67978
|
-
return
|
|
67985
|
+
return path14.replace(/\\/g, "/");
|
|
67979
67986
|
}
|
|
67980
67987
|
async function detectSandboxShell(sandbox) {
|
|
67981
67988
|
if (!isWindowsSandbox(sandbox)) return "bash";
|
|
@@ -68014,8 +68021,8 @@ PY`;
|
|
|
68014
68021
|
}
|
|
68015
68022
|
}
|
|
68016
68023
|
}
|
|
68017
|
-
async function getSandboxFileState(sandbox,
|
|
68018
|
-
const pythonPath = getPythonPathForSandbox(sandbox,
|
|
68024
|
+
async function getSandboxFileState(sandbox, path14) {
|
|
68025
|
+
const pythonPath = getPythonPathForSandbox(sandbox, path14);
|
|
68019
68026
|
const result = await runPythonScript(
|
|
68020
68027
|
sandbox,
|
|
68021
68028
|
FILE_STATE_SCRIPT,
|
|
@@ -68032,23 +68039,23 @@ async function getSandboxFileState(sandbox, path12) {
|
|
|
68032
68039
|
if (result.exitCode !== 0) {
|
|
68033
68040
|
return {
|
|
68034
68041
|
kind: "unknown",
|
|
68035
|
-
path:
|
|
68042
|
+
path: path14,
|
|
68036
68043
|
error: result.stderr || result.stdout || "file state command failed"
|
|
68037
68044
|
};
|
|
68038
68045
|
}
|
|
68039
68046
|
try {
|
|
68040
68047
|
const payload = JSON.parse(result.stdout.trim());
|
|
68041
68048
|
if (payload.kind === "file" && typeof payload.sizeBytes === "number" && Number.isFinite(payload.sizeBytes)) {
|
|
68042
|
-
return { ...payload, path:
|
|
68049
|
+
return { ...payload, path: path14 };
|
|
68043
68050
|
}
|
|
68044
68051
|
if (payload.kind === "missing" || payload.kind === "not_file") {
|
|
68045
|
-
return { ...payload, path:
|
|
68052
|
+
return { ...payload, path: path14 };
|
|
68046
68053
|
}
|
|
68047
68054
|
} catch {
|
|
68048
68055
|
}
|
|
68049
68056
|
return {
|
|
68050
68057
|
kind: "unknown",
|
|
68051
|
-
path:
|
|
68058
|
+
path: path14,
|
|
68052
68059
|
error: result.stderr || result.stdout || "invalid file state response"
|
|
68053
68060
|
};
|
|
68054
68061
|
}
|
|
@@ -68080,8 +68087,8 @@ ${numberedContent}${truncatedNotice}${footerNotice}`;
|
|
|
68080
68087
|
})
|
|
68081
68088
|
};
|
|
68082
68089
|
}
|
|
68083
|
-
async function readSandboxTextFile(sandbox,
|
|
68084
|
-
const pythonPath = getPythonPathForSandbox(sandbox,
|
|
68090
|
+
async function readSandboxTextFile(sandbox, path14, range) {
|
|
68091
|
+
const pythonPath = getPythonPathForSandbox(sandbox, path14);
|
|
68085
68092
|
const envVars = {
|
|
68086
68093
|
clawfast_FILE_READ_PATH: pythonPath,
|
|
68087
68094
|
clawfast_FILE_READ_RANGE_START: String(range?.[0] ?? 0),
|
|
@@ -68109,40 +68116,40 @@ async function readSandboxTextFile(sandbox, path12, range) {
|
|
|
68109
68116
|
}
|
|
68110
68117
|
return payload;
|
|
68111
68118
|
}
|
|
68112
|
-
async function readSandboxTextFileWithFallback(sandbox,
|
|
68119
|
+
async function readSandboxTextFileWithFallback(sandbox, path14, range) {
|
|
68113
68120
|
try {
|
|
68114
|
-
return await readSandboxTextFile(sandbox,
|
|
68121
|
+
return await readSandboxTextFile(sandbox, path14, range);
|
|
68115
68122
|
} catch (error51) {
|
|
68116
68123
|
const errorMessage = error51 instanceof Error ? error51.message : String(error51);
|
|
68117
68124
|
if (errorMessage.startsWith("Invalid ") || errorMessage.includes("File not found")) {
|
|
68118
68125
|
throw error51;
|
|
68119
68126
|
}
|
|
68120
|
-
const state = await getSandboxFileState(sandbox,
|
|
68127
|
+
const state = await getSandboxFileState(sandbox, path14);
|
|
68121
68128
|
if (state.kind === "unknown") {
|
|
68122
68129
|
throw new Error(
|
|
68123
|
-
`Unable to determine file size for ${
|
|
68130
|
+
`Unable to determine file size for ${path14}; refusing to load the file into memory. ${state.error}`
|
|
68124
68131
|
);
|
|
68125
68132
|
}
|
|
68126
68133
|
if (state.kind === "missing") {
|
|
68127
|
-
throw new Error(`File not found or is not a regular file: ${
|
|
68134
|
+
throw new Error(`File not found or is not a regular file: ${path14}`);
|
|
68128
68135
|
}
|
|
68129
68136
|
if (state.kind === "not_file") {
|
|
68130
|
-
throw new Error(`File is not a regular file: ${
|
|
68137
|
+
throw new Error(`File is not a regular file: ${path14}`);
|
|
68131
68138
|
}
|
|
68132
68139
|
if (state.sizeBytes > MAX_TEXT_FILE_READ_BYTES) {
|
|
68133
68140
|
if (range) {
|
|
68134
68141
|
throw new Error(
|
|
68135
|
-
`Unable to perform a bounded range read for ${
|
|
68142
|
+
`Unable to perform a bounded range read for ${path14}, 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.`
|
|
68136
68143
|
);
|
|
68137
68144
|
}
|
|
68138
68145
|
return {
|
|
68139
|
-
path:
|
|
68146
|
+
path: path14,
|
|
68140
68147
|
sizeBytes: state.sizeBytes,
|
|
68141
68148
|
totalLines: 0,
|
|
68142
68149
|
tooLarge: true
|
|
68143
68150
|
};
|
|
68144
68151
|
}
|
|
68145
|
-
const fileContent = await sandbox.files.read(
|
|
68152
|
+
const fileContent = await sandbox.files.read(path14, {
|
|
68146
68153
|
user: "user"
|
|
68147
68154
|
});
|
|
68148
68155
|
const lines = fileContent.split("\n");
|
|
@@ -68166,7 +68173,7 @@ async function readSandboxTextFileWithFallback(sandbox, path12, range) {
|
|
|
68166
68173
|
const startIndex = start - 1;
|
|
68167
68174
|
const endIndex = end === -1 ? lines.length : end;
|
|
68168
68175
|
return {
|
|
68169
|
-
path:
|
|
68176
|
+
path: path14,
|
|
68170
68177
|
sizeBytes: Buffer.byteLength(fileContent),
|
|
68171
68178
|
totalLines: lines.length,
|
|
68172
68179
|
content: lines.slice(startIndex, endIndex).join("\n"),
|
|
@@ -68174,7 +68181,7 @@ async function readSandboxTextFileWithFallback(sandbox, path12, range) {
|
|
|
68174
68181
|
};
|
|
68175
68182
|
}
|
|
68176
68183
|
return {
|
|
68177
|
-
path:
|
|
68184
|
+
path: path14,
|
|
68178
68185
|
sizeBytes: Buffer.byteLength(fileContent),
|
|
68179
68186
|
totalLines: lines.length,
|
|
68180
68187
|
content: fileContent,
|
|
@@ -68182,7 +68189,7 @@ async function readSandboxTextFileWithFallback(sandbox, path12, range) {
|
|
|
68182
68189
|
};
|
|
68183
68190
|
}
|
|
68184
68191
|
}
|
|
68185
|
-
async function appendSandboxTextFile(sandbox,
|
|
68192
|
+
async function appendSandboxTextFile(sandbox, path14, text2) {
|
|
68186
68193
|
const tempPath = `/tmp/clawfast_append_${Date.now()}_${Math.random().toString(36).slice(2)}.tmp`;
|
|
68187
68194
|
await sandbox.files.write(tempPath, text2, {
|
|
68188
68195
|
user: "user"
|
|
@@ -68191,7 +68198,7 @@ async function appendSandboxTextFile(sandbox, path12, text2) {
|
|
|
68191
68198
|
sandbox,
|
|
68192
68199
|
APPEND_TEXT_FILE_SCRIPT,
|
|
68193
68200
|
{
|
|
68194
|
-
clawfast_FILE_APPEND_TARGET_PATH: getPythonPathForSandbox(sandbox,
|
|
68201
|
+
clawfast_FILE_APPEND_TARGET_PATH: getPythonPathForSandbox(sandbox, path14),
|
|
68195
68202
|
clawfast_FILE_APPEND_SOURCE_PATH: getPythonPathForSandbox(
|
|
68196
68203
|
sandbox,
|
|
68197
68204
|
tempPath
|
|
@@ -68203,13 +68210,13 @@ async function appendSandboxTextFile(sandbox, path12, text2) {
|
|
|
68203
68210
|
throw new Error(result.stderr || result.stdout || "Failed to append file");
|
|
68204
68211
|
}
|
|
68205
68212
|
}
|
|
68206
|
-
async function readSandboxFileForView(sandbox,
|
|
68213
|
+
async function readSandboxFileForView(sandbox, path14, includeData) {
|
|
68207
68214
|
if (isCentrifugoSandbox(sandbox) && sandbox.isWindows()) {
|
|
68208
68215
|
throw new Error(
|
|
68209
68216
|
"The view action is not available for Windows local sandboxes yet. Use a Linux/E2B sandbox or inspect the image manually."
|
|
68210
68217
|
);
|
|
68211
68218
|
}
|
|
68212
|
-
const sandboxPath = getSandboxViewPath(sandbox,
|
|
68219
|
+
const sandboxPath = getSandboxViewPath(sandbox, path14);
|
|
68213
68220
|
const viewEnvVars = {
|
|
68214
68221
|
clawfast_FILE_VIEW_PATH: sandboxPath,
|
|
68215
68222
|
clawfast_FILE_VIEW_INCLUDE_DATA: includeData ? "1" : "0",
|
|
@@ -68553,19 +68560,19 @@ try:
|
|
|
68553
68560
|
except OSError:
|
|
68554
68561
|
pass
|
|
68555
68562
|
`;
|
|
68556
|
-
getFilename = (
|
|
68557
|
-
getFileExtension = (
|
|
68558
|
-
const filename = getFilename(
|
|
68563
|
+
getFilename = (path14) => path14.split("/").pop() || path14;
|
|
68564
|
+
getFileExtension = (path14) => {
|
|
68565
|
+
const filename = getFilename(path14);
|
|
68559
68566
|
const dotIndex = filename.lastIndexOf(".");
|
|
68560
68567
|
if (dotIndex <= 0 || dotIndex === filename.length - 1) return void 0;
|
|
68561
68568
|
return filename.slice(dotIndex + 1).toLowerCase();
|
|
68562
68569
|
};
|
|
68563
|
-
getSandboxViewPath = (sandbox,
|
|
68570
|
+
getSandboxViewPath = (sandbox, path14) => {
|
|
68564
68571
|
const maybeSandbox = sandbox;
|
|
68565
|
-
if (isCentrifugoSandbox(maybeSandbox) && maybeSandbox.isWindows() &&
|
|
68566
|
-
return `C:\\temp${
|
|
68572
|
+
if (isCentrifugoSandbox(maybeSandbox) && maybeSandbox.isWindows() && path14.startsWith("/tmp/")) {
|
|
68573
|
+
return `C:\\temp${path14.slice(4).replace(/\//g, "\\")}`;
|
|
68567
68574
|
}
|
|
68568
|
-
return
|
|
68575
|
+
return path14;
|
|
68569
68576
|
};
|
|
68570
68577
|
stripTrailingWs = (line) => line.replace(/[ \t]+$/u, "");
|
|
68571
68578
|
editSchema = external_exports.object({
|
|
@@ -68639,7 +68646,7 @@ ${instructionsDescription}`,
|
|
|
68639
68646
|
"A list of edits to be sequentially applied to the file. Required for `edit` action."
|
|
68640
68647
|
)
|
|
68641
68648
|
}),
|
|
68642
|
-
execute: async ({ action, path:
|
|
68649
|
+
execute: async ({ action, path: path14, text: text2, range, edits }) => {
|
|
68643
68650
|
try {
|
|
68644
68651
|
const { sandbox } = await sandboxManager.getSandbox();
|
|
68645
68652
|
switch (action) {
|
|
@@ -68649,7 +68656,7 @@ ${instructionsDescription}`,
|
|
|
68649
68656
|
captureFileViewImageUsage({
|
|
68650
68657
|
context: context2,
|
|
68651
68658
|
sandbox,
|
|
68652
|
-
path:
|
|
68659
|
+
path: path14,
|
|
68653
68660
|
outcome: "unsupported_model",
|
|
68654
68661
|
durationMs: Date.now() - viewStartedAt,
|
|
68655
68662
|
failureReason: "unsupported_model"
|
|
@@ -68658,26 +68665,26 @@ ${instructionsDescription}`,
|
|
|
68658
68665
|
}
|
|
68659
68666
|
let viewPayload;
|
|
68660
68667
|
try {
|
|
68661
|
-
viewPayload = await readSandboxFileForView(sandbox,
|
|
68668
|
+
viewPayload = await readSandboxFileForView(sandbox, path14, false);
|
|
68662
68669
|
} catch (error51) {
|
|
68663
68670
|
captureFileViewImageUsage({
|
|
68664
68671
|
context: context2,
|
|
68665
68672
|
sandbox,
|
|
68666
|
-
path:
|
|
68673
|
+
path: path14,
|
|
68667
68674
|
outcome: "inspection_failed",
|
|
68668
68675
|
durationMs: Date.now() - viewStartedAt,
|
|
68669
68676
|
failureReason: classifyFileViewError(error51)
|
|
68670
68677
|
});
|
|
68671
68678
|
throw error51;
|
|
68672
68679
|
}
|
|
68673
|
-
const filename = getFilename(
|
|
68680
|
+
const filename = getFilename(path14);
|
|
68674
68681
|
let previewFiles = [];
|
|
68675
68682
|
let previewUploadError;
|
|
68676
68683
|
try {
|
|
68677
68684
|
previewFiles = await uploadViewPreviewFiles({
|
|
68678
68685
|
context: context2,
|
|
68679
68686
|
sandbox,
|
|
68680
|
-
sourcePath:
|
|
68687
|
+
sourcePath: path14,
|
|
68681
68688
|
payload: viewPayload
|
|
68682
68689
|
});
|
|
68683
68690
|
} catch (error51) {
|
|
@@ -68691,7 +68698,7 @@ ${instructionsDescription}`,
|
|
|
68691
68698
|
user_id: context2.userID,
|
|
68692
68699
|
sandbox_type: getViewSandboxType(sandbox),
|
|
68693
68700
|
file_name: filename,
|
|
68694
|
-
source_path:
|
|
68701
|
+
source_path: path14,
|
|
68695
68702
|
kind: viewPayload.kind,
|
|
68696
68703
|
media_type: viewPayload.mediaType,
|
|
68697
68704
|
size_bytes: viewPayload.sizeBytes,
|
|
@@ -68702,7 +68709,7 @@ ${instructionsDescription}`,
|
|
|
68702
68709
|
captureFileViewImageUsage({
|
|
68703
68710
|
context: context2,
|
|
68704
68711
|
sandbox,
|
|
68705
|
-
path:
|
|
68712
|
+
path: path14,
|
|
68706
68713
|
outcome: "success",
|
|
68707
68714
|
durationMs: Date.now() - viewStartedAt,
|
|
68708
68715
|
mediaType: viewPayload.mediaType,
|
|
@@ -68712,7 +68719,7 @@ ${instructionsDescription}`,
|
|
|
68712
68719
|
return {
|
|
68713
68720
|
action: "view",
|
|
68714
68721
|
content: `Viewing image file: ${filename} (${viewPayload.mediaType}, ${viewPayload.sizeBytes} bytes).`,
|
|
68715
|
-
path:
|
|
68722
|
+
path: path14,
|
|
68716
68723
|
filename,
|
|
68717
68724
|
mediaType: viewPayload.mediaType,
|
|
68718
68725
|
sizeBytes: viewPayload.sizeBytes,
|
|
@@ -68722,8 +68729,8 @@ ${instructionsDescription}`,
|
|
|
68722
68729
|
};
|
|
68723
68730
|
}
|
|
68724
68731
|
case "read": {
|
|
68725
|
-
const filename =
|
|
68726
|
-
const spritChunked = isSpritPath(
|
|
68732
|
+
const filename = path14.split("/").pop() || path14;
|
|
68733
|
+
const spritChunked = isSpritPath(path14);
|
|
68727
68734
|
let effectiveRange = range;
|
|
68728
68735
|
if (spritChunked) {
|
|
68729
68736
|
const start = range && range[0] > 0 ? range[0] : 1;
|
|
@@ -68736,7 +68743,7 @@ ${instructionsDescription}`,
|
|
|
68736
68743
|
}
|
|
68737
68744
|
const readPayload = await readSandboxTextFileWithFallback(
|
|
68738
68745
|
sandbox,
|
|
68739
|
-
|
|
68746
|
+
path14,
|
|
68740
68747
|
effectiveRange
|
|
68741
68748
|
);
|
|
68742
68749
|
if (readPayload.tooLarge) {
|
|
@@ -68773,46 +68780,46 @@ File is too large to read in full (${formatBytes(readPayload.sizeBytes)}, ${tota
|
|
|
68773
68780
|
if (text2 === void 0) {
|
|
68774
68781
|
return { error: "text is required for write action" };
|
|
68775
68782
|
}
|
|
68776
|
-
await sandbox.files.write(
|
|
68783
|
+
await sandbox.files.write(path14, text2, {
|
|
68777
68784
|
user: "user"
|
|
68778
68785
|
});
|
|
68779
|
-
return `File written: ${
|
|
68786
|
+
return `File written: ${path14}`;
|
|
68780
68787
|
}
|
|
68781
68788
|
case "append": {
|
|
68782
68789
|
if (text2 === void 0) {
|
|
68783
68790
|
return { error: "text is required for append action" };
|
|
68784
68791
|
}
|
|
68785
|
-
const existingState = await getSandboxFileState(sandbox,
|
|
68792
|
+
const existingState = await getSandboxFileState(sandbox, path14);
|
|
68786
68793
|
if (existingState.kind === "unknown") {
|
|
68787
68794
|
return {
|
|
68788
|
-
error: `Cannot append safely because the existing file size could not be determined for ${
|
|
68795
|
+
error: `Cannot append safely because the existing file size could not be determined for ${path14}. ${existingState.error}`
|
|
68789
68796
|
};
|
|
68790
68797
|
}
|
|
68791
68798
|
if (existingState.kind === "not_file") {
|
|
68792
68799
|
return {
|
|
68793
|
-
error: `Cannot append to ${
|
|
68800
|
+
error: `Cannot append to ${path14} because it is not a file.`
|
|
68794
68801
|
};
|
|
68795
68802
|
}
|
|
68796
68803
|
if (existingState.kind === "file" && existingState.sizeBytes > MAX_TEXT_FILE_READ_BYTES) {
|
|
68797
|
-
await appendSandboxTextFile(sandbox,
|
|
68804
|
+
await appendSandboxTextFile(sandbox, path14, text2);
|
|
68798
68805
|
return {
|
|
68799
|
-
content: `File appended: ${
|
|
68806
|
+
content: `File appended: ${path14}
|
|
68800
68807
|
Existing file is ${formatBytes(existingState.sizeBytes)}, so the full diff preview was skipped to avoid loading the entire file into memory.`
|
|
68801
68808
|
};
|
|
68802
68809
|
}
|
|
68803
68810
|
let existingContent = "";
|
|
68804
68811
|
try {
|
|
68805
|
-
existingContent = await sandbox.files.read(
|
|
68812
|
+
existingContent = await sandbox.files.read(path14, {
|
|
68806
68813
|
user: "user"
|
|
68807
68814
|
});
|
|
68808
68815
|
} catch {
|
|
68809
68816
|
}
|
|
68810
68817
|
const newContent = existingContent + text2;
|
|
68811
|
-
await sandbox.files.write(
|
|
68818
|
+
await sandbox.files.write(path14, newContent, {
|
|
68812
68819
|
user: "user"
|
|
68813
68820
|
});
|
|
68814
68821
|
return {
|
|
68815
|
-
content: `File appended: ${
|
|
68822
|
+
content: `File appended: ${path14}`,
|
|
68816
68823
|
originalContent: truncateOutput({
|
|
68817
68824
|
content: existingContent,
|
|
68818
68825
|
mode: "read-file"
|
|
@@ -68827,31 +68834,31 @@ Existing file is ${formatBytes(existingState.sizeBytes)}, so the full diff previ
|
|
|
68827
68834
|
if (!edits || edits.length === 0) {
|
|
68828
68835
|
return { error: "edits array is required for edit action" };
|
|
68829
68836
|
}
|
|
68830
|
-
const existingState = await getSandboxFileState(sandbox,
|
|
68837
|
+
const existingState = await getSandboxFileState(sandbox, path14);
|
|
68831
68838
|
if (existingState.kind === "unknown") {
|
|
68832
68839
|
return {
|
|
68833
|
-
error: `Cannot edit ${
|
|
68840
|
+
error: `Cannot edit ${path14} safely because the file size could not be determined. ${existingState.error}`
|
|
68834
68841
|
};
|
|
68835
68842
|
}
|
|
68836
68843
|
if (existingState.kind === "missing") {
|
|
68837
68844
|
return {
|
|
68838
|
-
error: `Cannot edit file ${
|
|
68845
|
+
error: `Cannot edit file ${path14} - file is empty or does not exist`
|
|
68839
68846
|
};
|
|
68840
68847
|
}
|
|
68841
68848
|
if (existingState.kind === "not_file") {
|
|
68842
|
-
return { error: `Cannot edit ${
|
|
68849
|
+
return { error: `Cannot edit ${path14} because it is not a file.` };
|
|
68843
68850
|
}
|
|
68844
68851
|
if (existingState.sizeBytes > MAX_TEXT_FILE_READ_BYTES) {
|
|
68845
68852
|
return {
|
|
68846
|
-
error: `File ${
|
|
68853
|
+
error: `File ${path14} 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.`
|
|
68847
68854
|
};
|
|
68848
68855
|
}
|
|
68849
|
-
const originalContent = await sandbox.files.read(
|
|
68856
|
+
const originalContent = await sandbox.files.read(path14, {
|
|
68850
68857
|
user: "user"
|
|
68851
68858
|
});
|
|
68852
68859
|
if (!originalContent) {
|
|
68853
68860
|
return {
|
|
68854
|
-
error: `Cannot edit file ${
|
|
68861
|
+
error: `Cannot edit file ${path14} - file is empty or does not exist`
|
|
68855
68862
|
};
|
|
68856
68863
|
}
|
|
68857
68864
|
const resolvedEdits = [];
|
|
@@ -68896,7 +68903,7 @@ ${hint}` : "")
|
|
|
68896
68903
|
}
|
|
68897
68904
|
}
|
|
68898
68905
|
}
|
|
68899
|
-
await sandbox.files.write(
|
|
68906
|
+
await sandbox.files.write(path14, content, {
|
|
68900
68907
|
user: "user"
|
|
68901
68908
|
});
|
|
68902
68909
|
const lines = content.split("\n");
|
|
@@ -68988,419 +68995,483 @@ ${numberedLines}`,
|
|
|
68988
68995
|
}
|
|
68989
68996
|
});
|
|
68990
68997
|
|
|
68991
|
-
//
|
|
68992
|
-
|
|
68993
|
-
|
|
68994
|
-
|
|
68995
|
-
|
|
68996
|
-
|
|
68997
|
-
SENSITIVE_FIELD_PATTERN = /(["']?\b(?:serviceKey|service_key|apiKey|api_key|authorization|bearer|cookie|password|secret|token)\b["']?)(\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,}]+)/gi;
|
|
68998
|
-
ENV_SECRET_PATTERN = /(["']?\b(?:CONVEX_SERVICE_ROLE_KEY|POSTHOG_API_KEY|STRIPE_SECRET_KEY)\b["']?)(\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,}]+)/gi;
|
|
68999
|
-
redactSensitiveErrorMessage = (message) => message.replace(SENSITIVE_FIELD_PATTERN, (_match, key, separator) => {
|
|
69000
|
-
return `${key}${separator}"${REDACTED_VALUE}"`;
|
|
69001
|
-
}).replace(ENV_SECRET_PATTERN, (_match, key, separator) => {
|
|
69002
|
-
return `${key}${separator}"${REDACTED_VALUE}"`;
|
|
69003
|
-
});
|
|
69004
|
-
stringifyRedactedError = (error51) => {
|
|
69005
|
-
const message = error51 instanceof Error ? error51.message : typeof error51 === "string" ? error51 : (() => {
|
|
68998
|
+
// src/tools/web-research.ts
|
|
68999
|
+
function decodeHtmlEntities(input) {
|
|
69000
|
+
return input.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (whole, code) => {
|
|
69001
|
+
if (code[0] === "#") {
|
|
69002
|
+
const num = code[1] === "x" || code[1] === "X" ? Number.parseInt(code.slice(2), 16) : Number.parseInt(code.slice(1), 10);
|
|
69003
|
+
if (Number.isFinite(num) && num > 0 && num <= 1114111) {
|
|
69006
69004
|
try {
|
|
69007
|
-
return
|
|
69005
|
+
return String.fromCodePoint(num);
|
|
69008
69006
|
} catch {
|
|
69009
|
-
return
|
|
69007
|
+
return whole;
|
|
69010
69008
|
}
|
|
69011
|
-
})();
|
|
69012
|
-
return redactSensitiveErrorMessage(message);
|
|
69013
|
-
};
|
|
69014
|
-
}
|
|
69015
|
-
});
|
|
69016
|
-
|
|
69017
|
-
// ../lib/ai/tools/utils/perplexity.ts
|
|
69018
|
-
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;
|
|
69019
|
-
var init_perplexity = __esm({
|
|
69020
|
-
"../lib/ai/tools/utils/perplexity.ts"() {
|
|
69021
|
-
"use strict";
|
|
69022
|
-
SEARCH_RESULT_CONTENT_MAX_TOKENS = 250;
|
|
69023
|
-
RECENCY_MAP = {
|
|
69024
|
-
past_day: "day",
|
|
69025
|
-
past_week: "week",
|
|
69026
|
-
past_month: "month",
|
|
69027
|
-
past_year: "year"
|
|
69028
|
-
};
|
|
69029
|
-
PerplexityApiError = class extends Error {
|
|
69030
|
-
constructor({
|
|
69031
|
-
status,
|
|
69032
|
-
statusText,
|
|
69033
|
-
bodySummary,
|
|
69034
|
-
retryable
|
|
69035
|
-
}) {
|
|
69036
|
-
const statusLabel = statusText ? `${status} ${statusText}` : `${status}`;
|
|
69037
|
-
const summary = bodySummary ? `: ${bodySummary}` : "";
|
|
69038
|
-
super(`Perplexity API error ${statusLabel}${summary}`);
|
|
69039
|
-
this.name = "PerplexityApiError";
|
|
69040
|
-
this.status = status;
|
|
69041
|
-
this.statusText = statusText;
|
|
69042
|
-
this.bodySummary = bodySummary;
|
|
69043
|
-
this.retryable = retryable;
|
|
69044
69009
|
}
|
|
69010
|
+
return whole;
|
|
69011
|
+
}
|
|
69012
|
+
return NAMED_ENTITIES[code] != null ? NAMED_ENTITIES[code] : whole;
|
|
69013
|
+
});
|
|
69014
|
+
}
|
|
69015
|
+
function htmlToText(html) {
|
|
69016
|
+
let s = html;
|
|
69017
|
+
s = s.replace(/<!--[\s\S]*?-->/g, " ");
|
|
69018
|
+
s = s.replace(/<(script|style|noscript|svg|head|template)[\s\S]*?<\/\1>/gi, " ");
|
|
69019
|
+
s = s.replace(
|
|
69020
|
+
/<\/(p|div|section|article|header|footer|li|ul|ol|tr|table|h[1-6]|blockquote|pre|nav|aside)>/gi,
|
|
69021
|
+
"\n"
|
|
69022
|
+
);
|
|
69023
|
+
s = s.replace(/<br\s*\/?>/gi, "\n");
|
|
69024
|
+
s = s.replace(/<[^>]+>/g, " ");
|
|
69025
|
+
s = decodeHtmlEntities(s);
|
|
69026
|
+
s = s.replace(/[ \t\f\v\r]+/g, " ");
|
|
69027
|
+
s = s.replace(/\n{3,}/g, "\n\n");
|
|
69028
|
+
s = s.split("\n").map((line) => line.trim()).join("\n").trim();
|
|
69029
|
+
return s;
|
|
69030
|
+
}
|
|
69031
|
+
function extractTitle(html) {
|
|
69032
|
+
const m = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(html);
|
|
69033
|
+
return m ? htmlToText(m[1]).slice(0, 200) : "";
|
|
69034
|
+
}
|
|
69035
|
+
function decodeDdgHref(href) {
|
|
69036
|
+
let h = (href || "").trim();
|
|
69037
|
+
if (!h) return "";
|
|
69038
|
+
if (h.startsWith("//")) h = "https:" + h;
|
|
69039
|
+
try {
|
|
69040
|
+
const u = new URL(h, "https://duckduckgo.com");
|
|
69041
|
+
const uddg = u.searchParams.get("uddg");
|
|
69042
|
+
if (uddg) return uddg;
|
|
69043
|
+
return u.href;
|
|
69044
|
+
} catch {
|
|
69045
|
+
return h;
|
|
69046
|
+
}
|
|
69047
|
+
}
|
|
69048
|
+
function parseDuckDuckGoHtml(html) {
|
|
69049
|
+
const snippets = [];
|
|
69050
|
+
const snippetRe = /<a[^>]+class="[^"]*result__snippet[^"]*"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
69051
|
+
let sm;
|
|
69052
|
+
while (sm = snippetRe.exec(html)) snippets.push(htmlToText(sm[1]));
|
|
69053
|
+
const results = [];
|
|
69054
|
+
const linkRe = /<a[^>]+class="[^"]*result__a[^"]*"[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
69055
|
+
let m;
|
|
69056
|
+
let i = 0;
|
|
69057
|
+
while (m = linkRe.exec(html)) {
|
|
69058
|
+
const url2 = decodeDdgHref(m[1]);
|
|
69059
|
+
const title = htmlToText(m[2]);
|
|
69060
|
+
if (url2 && title && /^https?:\/\//i.test(url2)) {
|
|
69061
|
+
results.push({ title, url: url2, snippet: snippets[i] || "" });
|
|
69062
|
+
}
|
|
69063
|
+
i++;
|
|
69064
|
+
}
|
|
69065
|
+
return results;
|
|
69066
|
+
}
|
|
69067
|
+
function parseDuckDuckGoLite(html) {
|
|
69068
|
+
const results = [];
|
|
69069
|
+
const linkRe = /<a[^>]+class="[^"]*result-link[^"]*"[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
69070
|
+
let m;
|
|
69071
|
+
while (m = linkRe.exec(html)) {
|
|
69072
|
+
const url2 = decodeDdgHref(m[1]);
|
|
69073
|
+
const title = htmlToText(m[2]);
|
|
69074
|
+
if (url2 && title && /^https?:\/\//i.test(url2)) {
|
|
69075
|
+
results.push({ title, url: url2, snippet: "" });
|
|
69076
|
+
}
|
|
69077
|
+
}
|
|
69078
|
+
return results;
|
|
69079
|
+
}
|
|
69080
|
+
function canonicalUrl(raw) {
|
|
69081
|
+
try {
|
|
69082
|
+
const u = new URL(raw.trim());
|
|
69083
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") return null;
|
|
69084
|
+
u.hash = "";
|
|
69085
|
+
let s = u.href;
|
|
69086
|
+
if (s.endsWith("/")) s = s.slice(0, -1);
|
|
69087
|
+
return s;
|
|
69088
|
+
} catch {
|
|
69089
|
+
return null;
|
|
69090
|
+
}
|
|
69091
|
+
}
|
|
69092
|
+
function dedupeResults(results) {
|
|
69093
|
+
const seen = /* @__PURE__ */ new Set();
|
|
69094
|
+
const out3 = [];
|
|
69095
|
+
for (const r2 of results) {
|
|
69096
|
+
const key = canonicalUrl(r2.url);
|
|
69097
|
+
if (!key || seen.has(key)) continue;
|
|
69098
|
+
seen.add(key);
|
|
69099
|
+
out3.push(r2);
|
|
69100
|
+
}
|
|
69101
|
+
return out3;
|
|
69102
|
+
}
|
|
69103
|
+
async function mapPool(items, concurrency, fn) {
|
|
69104
|
+
const results = new Array(items.length);
|
|
69105
|
+
if (items.length === 0) return results;
|
|
69106
|
+
const workers = [];
|
|
69107
|
+
const n = Math.min(Math.max(1, concurrency), items.length);
|
|
69108
|
+
let next = 0;
|
|
69109
|
+
for (let w = 0; w < n; w++) {
|
|
69110
|
+
workers.push(
|
|
69111
|
+
(async () => {
|
|
69112
|
+
for (; ; ) {
|
|
69113
|
+
const i = next++;
|
|
69114
|
+
if (i >= items.length) break;
|
|
69115
|
+
try {
|
|
69116
|
+
results[i] = await fn(items[i], i);
|
|
69117
|
+
} catch (err) {
|
|
69118
|
+
results[i] = {
|
|
69119
|
+
error: err instanceof Error ? err.message : String(err)
|
|
69120
|
+
};
|
|
69121
|
+
}
|
|
69122
|
+
}
|
|
69123
|
+
})()
|
|
69124
|
+
);
|
|
69125
|
+
}
|
|
69126
|
+
await Promise.all(workers);
|
|
69127
|
+
return results;
|
|
69128
|
+
}
|
|
69129
|
+
async function fetchText(url2, opts) {
|
|
69130
|
+
const ctrl = new AbortController();
|
|
69131
|
+
const onAbort = () => ctrl.abort();
|
|
69132
|
+
if (opts.signal) {
|
|
69133
|
+
if (opts.signal.aborted) ctrl.abort();
|
|
69134
|
+
else opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
69135
|
+
}
|
|
69136
|
+
const timer2 = setTimeout(() => ctrl.abort(), opts.timeoutMs);
|
|
69137
|
+
try {
|
|
69138
|
+
const resp = await fetch(url2, {
|
|
69139
|
+
method: "GET",
|
|
69140
|
+
headers: {
|
|
69141
|
+
"User-Agent": UA,
|
|
69142
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
69143
|
+
"Accept-Language": "en-US,en;q=0.9,pt-BR;q=0.8"
|
|
69144
|
+
},
|
|
69145
|
+
redirect: "follow",
|
|
69146
|
+
signal: ctrl.signal
|
|
69147
|
+
});
|
|
69148
|
+
let text2 = await resp.text();
|
|
69149
|
+
if (text2.length > MAX_RAW_BYTES) text2 = text2.slice(0, MAX_RAW_BYTES);
|
|
69150
|
+
return {
|
|
69151
|
+
status: resp.status,
|
|
69152
|
+
finalUrl: resp.url || url2,
|
|
69153
|
+
contentType: resp.headers.get("content-type") || "",
|
|
69154
|
+
text: text2
|
|
69045
69155
|
};
|
|
69046
|
-
|
|
69047
|
-
|
|
69048
|
-
|
|
69049
|
-
|
|
69050
|
-
|
|
69051
|
-
|
|
69052
|
-
|
|
69053
|
-
|
|
69054
|
-
|
|
69055
|
-
|
|
69056
|
-
|
|
69057
|
-
|
|
69058
|
-
|
|
69059
|
-
|
|
69156
|
+
} finally {
|
|
69157
|
+
clearTimeout(timer2);
|
|
69158
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
69159
|
+
}
|
|
69160
|
+
}
|
|
69161
|
+
async function searchOneQuery(query, signal, df) {
|
|
69162
|
+
const enc = encodeURIComponent(query);
|
|
69163
|
+
const dfParam = df ? `&df=${df}` : "";
|
|
69164
|
+
try {
|
|
69165
|
+
const r2 = await fetchText(
|
|
69166
|
+
`https://html.duckduckgo.com/html/?q=${enc}${dfParam}`,
|
|
69167
|
+
{ timeoutMs: SEARCH_TIMEOUT_MS, signal }
|
|
69168
|
+
);
|
|
69169
|
+
const parsed = parseDuckDuckGoHtml(r2.text);
|
|
69170
|
+
if (parsed.length) return parsed;
|
|
69171
|
+
} catch (err) {
|
|
69172
|
+
if (isAbort(err)) throw err;
|
|
69173
|
+
}
|
|
69174
|
+
try {
|
|
69175
|
+
const r2 = await fetchText(
|
|
69176
|
+
`https://lite.duckduckgo.com/lite/?q=${enc}${dfParam}`,
|
|
69177
|
+
{ timeoutMs: SEARCH_TIMEOUT_MS, signal }
|
|
69178
|
+
);
|
|
69179
|
+
return parseDuckDuckGoLite(r2.text);
|
|
69180
|
+
} catch (err) {
|
|
69181
|
+
if (isAbort(err)) throw err;
|
|
69182
|
+
return [];
|
|
69183
|
+
}
|
|
69184
|
+
}
|
|
69185
|
+
async function searchQueries(queries, signal, df) {
|
|
69186
|
+
const lists = await mapPool(
|
|
69187
|
+
queries,
|
|
69188
|
+
Math.min(queries.length, 5),
|
|
69189
|
+
(q) => searchOneQuery(q, signal, df)
|
|
69190
|
+
);
|
|
69191
|
+
const merged = [];
|
|
69192
|
+
for (const item of lists) {
|
|
69193
|
+
if (Array.isArray(item)) merged.push(...item);
|
|
69194
|
+
}
|
|
69195
|
+
return dedupeResults(merged);
|
|
69196
|
+
}
|
|
69197
|
+
async function readPage(url2, signal) {
|
|
69198
|
+
const r2 = await fetchText(url2, { timeoutMs: READ_TIMEOUT_MS, signal });
|
|
69199
|
+
const text2 = isTextual(r2.contentType) ? htmlToText(r2.text) : "";
|
|
69200
|
+
return {
|
|
69201
|
+
url: url2,
|
|
69202
|
+
finalUrl: r2.finalUrl,
|
|
69203
|
+
status: r2.status,
|
|
69204
|
+
title: extractTitle(r2.text) || r2.finalUrl,
|
|
69205
|
+
text: text2,
|
|
69206
|
+
bytes: r2.text.length,
|
|
69207
|
+
contentType: r2.contentType
|
|
69208
|
+
};
|
|
69209
|
+
}
|
|
69210
|
+
async function jinaRead(url2, signal) {
|
|
69211
|
+
const key = process.env.JINA_API_KEY;
|
|
69212
|
+
if (!key) return null;
|
|
69213
|
+
try {
|
|
69214
|
+
const resp = await fetch(`https://r.jina.ai/${url2}`, {
|
|
69215
|
+
headers: { Authorization: `Bearer ${key}`, "X-Timeout": "25" },
|
|
69216
|
+
signal
|
|
69060
69217
|
});
|
|
69061
|
-
|
|
69062
|
-
|
|
69063
|
-
|
|
69064
|
-
|
|
69218
|
+
if (!resp.ok) return null;
|
|
69219
|
+
return await resp.text();
|
|
69220
|
+
} catch {
|
|
69221
|
+
return null;
|
|
69222
|
+
}
|
|
69223
|
+
}
|
|
69224
|
+
function formatSearchResults(results) {
|
|
69225
|
+
if (!results.length) {
|
|
69226
|
+
return "Nenhum resultado encontrado. Refine a query (sin\xF4nimos, termos em ingl\xEAs, operadores site:/filetype:) e tente de novo.";
|
|
69227
|
+
}
|
|
69228
|
+
const shown = results.slice(0, SEARCH_MAX_RESULTS);
|
|
69229
|
+
const lines = [`${shown.length} resultado(s):`];
|
|
69230
|
+
shown.forEach((r2, i) => {
|
|
69231
|
+
lines.push("");
|
|
69232
|
+
lines.push(`${i + 1}. ${r2.title}`);
|
|
69233
|
+
lines.push(` ${r2.url}`);
|
|
69234
|
+
if (r2.snippet) lines.push(` ${truncate3(r2.snippet, 300)}`);
|
|
69235
|
+
});
|
|
69236
|
+
return lines.join("\n");
|
|
69237
|
+
}
|
|
69238
|
+
function formatResearchDigest(pages, stats) {
|
|
69239
|
+
const header = `Pesquisa profunda: ${stats.totalFound} candidato(s), ${stats.fetched} buscado(s) (${stats.conc ?? 1} em paralelo), ${stats.ok} ok, ${stats.failures} falha(s)` + (stats.capped ? `, capado em ${stats.maxPages} p\xE1ginas` : "") + ".";
|
|
69240
|
+
if (!pages.length) {
|
|
69241
|
+
return header + "\nNenhuma p\xE1gina retornou texto. Refine as queries ou forne\xE7a urls e tente de novo.";
|
|
69242
|
+
}
|
|
69243
|
+
const INLINE = 40;
|
|
69244
|
+
const lines = [header, ""];
|
|
69245
|
+
pages.slice(0, INLINE).forEach((p, i) => {
|
|
69246
|
+
lines.push(`### ${i + 1}. ${p.title}`);
|
|
69247
|
+
lines.push(`${p.finalUrl} (HTTP ${p.status})`);
|
|
69248
|
+
const extract = p.text.replace(/\n+/g, " ").trim();
|
|
69249
|
+
if (extract) lines.push(truncate3(extract, 600));
|
|
69250
|
+
lines.push("");
|
|
69251
|
+
});
|
|
69252
|
+
if (pages.length > INLINE) {
|
|
69253
|
+
lines.push(`\u2026 +${pages.length - INLINE} fonte(s) no relat\xF3rio completo.`);
|
|
69254
|
+
}
|
|
69255
|
+
if (stats.reportPath) {
|
|
69256
|
+
lines.push(`Relat\xF3rio completo salvo em: ${stats.reportPath}`);
|
|
69257
|
+
}
|
|
69258
|
+
return lines.join("\n");
|
|
69259
|
+
}
|
|
69260
|
+
async function saveResearchReport(workdir, queries, pages, stats) {
|
|
69261
|
+
if (!workdir || !pages.length) return null;
|
|
69262
|
+
try {
|
|
69263
|
+
const dir = import_node_path5.default.join(workdir, "reports");
|
|
69264
|
+
await import_node_fs6.promises.mkdir(dir, { recursive: true });
|
|
69265
|
+
const slug = (queries[0] || "research").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "research";
|
|
69266
|
+
const file2 = import_node_path5.default.join(dir, `research_${slug}_${Date.now()}.md`);
|
|
69267
|
+
const parts = [];
|
|
69268
|
+
parts.push(`# Pesquisa: ${queries.join(" / ") || "(seed urls)"}`);
|
|
69269
|
+
parts.push("");
|
|
69270
|
+
parts.push(
|
|
69271
|
+
`Candidatos: ${stats.totalFound} \xB7 Buscados: ${stats.fetched} \xB7 OK: ${stats.ok} \xB7 Falhas: ${stats.failures}` + (stats.capped ? ` \xB7 capado em ${stats.maxPages}` : "")
|
|
69065
69272
|
);
|
|
69066
|
-
|
|
69067
|
-
|
|
69068
|
-
|
|
69069
|
-
);
|
|
69070
|
-
|
|
69071
|
-
|
|
69072
|
-
|
|
69073
|
-
|
|
69074
|
-
|
|
69075
|
-
|
|
69076
|
-
|
|
69077
|
-
|
|
69078
|
-
|
|
69079
|
-
const trimmed = body.trim();
|
|
69080
|
-
if (!trimmed) return "";
|
|
69081
|
-
let summary = "";
|
|
69082
|
-
if (contentType.includes("json") || trimmed.startsWith("{")) {
|
|
69083
|
-
try {
|
|
69084
|
-
const parsed = JSON.parse(trimmed);
|
|
69085
|
-
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;
|
|
69086
|
-
const message = error51 || (typeof parsed.message === "string" ? parsed.message : void 0) || (typeof parsed.detail === "string" ? parsed.detail : void 0);
|
|
69087
|
-
summary = message || trimmed;
|
|
69088
|
-
} catch {
|
|
69089
|
-
summary = trimmed;
|
|
69090
|
-
}
|
|
69091
|
-
} else if (/<[a-z][\s\S]*>/i.test(trimmed)) {
|
|
69092
|
-
const tagSummaries = [
|
|
69093
|
-
...extractHtmlTagText(trimmed, "h1"),
|
|
69094
|
-
...extractHtmlTagText(trimmed, "p"),
|
|
69095
|
-
...extractHtmlTagText(trimmed, "h2")
|
|
69096
|
-
];
|
|
69097
|
-
summary = tagSummaries.length > 0 ? tagSummaries.join(". ") : stripHtml(trimmed);
|
|
69098
|
-
} else {
|
|
69099
|
-
summary = trimmed;
|
|
69100
|
-
}
|
|
69101
|
-
return truncateSummary(redactNetworkDetails(normalizeWhitespace(summary)));
|
|
69102
|
-
};
|
|
69103
|
-
buildPerplexitySearchBody = (query, options) => {
|
|
69104
|
-
const searchBody = {
|
|
69105
|
-
query,
|
|
69106
|
-
max_results: options?.maxResults ?? 10,
|
|
69107
|
-
max_tokens_per_page: SEARCH_RESULT_CONTENT_MAX_TOKENS
|
|
69108
|
-
};
|
|
69109
|
-
if (options?.country) {
|
|
69110
|
-
searchBody.country = options.country;
|
|
69111
|
-
}
|
|
69112
|
-
if (options?.recency) {
|
|
69113
|
-
searchBody.search_recency_filter = options.recency;
|
|
69114
|
-
}
|
|
69115
|
-
return searchBody;
|
|
69116
|
-
};
|
|
69117
|
-
formatSearchResults = (results) => {
|
|
69118
|
-
return results.map((result) => ({
|
|
69119
|
-
title: result.title,
|
|
69120
|
-
url: result.url,
|
|
69121
|
-
content: result.snippet,
|
|
69122
|
-
date: result.date || null,
|
|
69123
|
-
lastUpdated: result.last_updated || null
|
|
69124
|
-
}));
|
|
69125
|
-
};
|
|
69273
|
+
parts.push("");
|
|
69274
|
+
pages.forEach((p, i) => {
|
|
69275
|
+
parts.push(`## ${i + 1}. ${p.title}`);
|
|
69276
|
+
parts.push(`- URL: ${p.finalUrl}`);
|
|
69277
|
+
parts.push(`- HTTP: ${p.status} \xB7 ${p.bytes} bytes`);
|
|
69278
|
+
parts.push("");
|
|
69279
|
+
parts.push(truncate3(p.text, 4e3));
|
|
69280
|
+
parts.push("");
|
|
69281
|
+
});
|
|
69282
|
+
await import_node_fs6.promises.writeFile(file2, parts.join("\n"), "utf8");
|
|
69283
|
+
return file2;
|
|
69284
|
+
} catch {
|
|
69285
|
+
return null;
|
|
69126
69286
|
}
|
|
69127
|
-
}
|
|
69128
|
-
|
|
69129
|
-
|
|
69130
|
-
|
|
69131
|
-
var init_web_search = __esm({
|
|
69132
|
-
"../lib/ai/tools/web-search.ts"() {
|
|
69287
|
+
}
|
|
69288
|
+
var import_node_fs6, import_node_path5, UA, SEARCH_TIMEOUT_MS, READ_TIMEOUT_MS, MAX_RAW_BYTES, OPEN_URL_MAX_CHARS, SEARCH_MAX_RESULTS, HARD_PAGE_CEILING, TIME_TO_DDG_DF, NAMED_ENTITIES, truncate3, isAbort, isTextual, TIME_ENUM, createWebSearch, createOpenUrl, createDeepResearch;
|
|
69289
|
+
var init_web_research = __esm({
|
|
69290
|
+
"src/tools/web-research.ts"() {
|
|
69133
69291
|
"use strict";
|
|
69134
69292
|
init_dist5();
|
|
69135
69293
|
init_zod();
|
|
69136
|
-
|
|
69137
|
-
|
|
69138
|
-
|
|
69139
|
-
|
|
69140
|
-
|
|
69141
|
-
|
|
69142
|
-
|
|
69143
|
-
|
|
69144
|
-
|
|
69145
|
-
|
|
69146
|
-
|
|
69147
|
-
|
|
69148
|
-
|
|
69149
|
-
|
|
69150
|
-
|
|
69151
|
-
|
|
69152
|
-
|
|
69153
|
-
|
|
69154
|
-
|
|
69155
|
-
|
|
69156
|
-
|
|
69157
|
-
|
|
69158
|
-
|
|
69159
|
-
const timeout = setTimeout(() => {
|
|
69160
|
-
cleanup();
|
|
69161
|
-
resolve2();
|
|
69162
|
-
}, delayMs);
|
|
69163
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
69164
|
-
});
|
|
69165
|
-
};
|
|
69166
|
-
getRetryDelayMs = (attemptIndex) => {
|
|
69167
|
-
const exponentialDelay = WEB_SEARCH_RETRY_BASE_DELAY_MS * Math.pow(2, attemptIndex);
|
|
69168
|
-
const jitter = Math.random() * WEB_SEARCH_RETRY_JITTER_MS;
|
|
69169
|
-
return Math.round(exponentialDelay + jitter);
|
|
69170
|
-
};
|
|
69171
|
-
createPerplexityApiError = async (response) => {
|
|
69172
|
-
const errorText = await response.text();
|
|
69173
|
-
const bodySummary = summarizePerplexityErrorBody(
|
|
69174
|
-
errorText,
|
|
69175
|
-
response.headers.get("content-type") || ""
|
|
69176
|
-
);
|
|
69177
|
-
return new PerplexityApiError({
|
|
69178
|
-
status: response.status,
|
|
69179
|
-
statusText: response.statusText,
|
|
69180
|
-
bodySummary,
|
|
69181
|
-
retryable: isRetryablePerplexityStatus(response.status)
|
|
69182
|
-
});
|
|
69183
|
-
};
|
|
69184
|
-
formatPerplexityFailureForTool = (error51, attempts) => {
|
|
69185
|
-
const statusText = error51.statusText ? ` ${error51.statusText}` : "";
|
|
69186
|
-
if (error51.retryable) {
|
|
69187
|
-
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.`;
|
|
69188
|
-
}
|
|
69189
|
-
if (error51.status === 401 || error51.status === 403) {
|
|
69190
|
-
return `Error performing web search: Perplexity search is not authorized (HTTP ${error51.status}${statusText}). Check the Perplexity API key or account access.`;
|
|
69191
|
-
}
|
|
69192
|
-
return `Error performing web search: Perplexity search failed (HTTP ${error51.status}${statusText}).`;
|
|
69294
|
+
import_node_fs6 = require("node:fs");
|
|
69295
|
+
import_node_path5 = __toESM(require("node:path"));
|
|
69296
|
+
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36";
|
|
69297
|
+
SEARCH_TIMEOUT_MS = 15e3;
|
|
69298
|
+
READ_TIMEOUT_MS = 25e3;
|
|
69299
|
+
MAX_RAW_BYTES = 15e5;
|
|
69300
|
+
OPEN_URL_MAX_CHARS = 8e3;
|
|
69301
|
+
SEARCH_MAX_RESULTS = 30;
|
|
69302
|
+
HARD_PAGE_CEILING = 500;
|
|
69303
|
+
TIME_TO_DDG_DF = {
|
|
69304
|
+
past_day: "d",
|
|
69305
|
+
past_week: "w",
|
|
69306
|
+
past_month: "m",
|
|
69307
|
+
past_year: "y"
|
|
69308
|
+
};
|
|
69309
|
+
NAMED_ENTITIES = {
|
|
69310
|
+
amp: "&",
|
|
69311
|
+
lt: "<",
|
|
69312
|
+
gt: ">",
|
|
69313
|
+
quot: '"',
|
|
69314
|
+
apos: "'",
|
|
69315
|
+
nbsp: " ",
|
|
69316
|
+
"#39": "'"
|
|
69193
69317
|
};
|
|
69194
|
-
|
|
69195
|
-
|
|
69196
|
-
|
|
69197
|
-
|
|
69318
|
+
truncate3 = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
69319
|
+
isAbort = (err) => err instanceof Error && err.name === "AbortError";
|
|
69320
|
+
isTextual = (ct) => !ct || /text|html|json|xml|javascript|csv/i.test(ct);
|
|
69321
|
+
TIME_ENUM = external_exports.enum(["all", "past_day", "past_week", "past_month", "past_year"]).optional();
|
|
69322
|
+
createWebSearch = (_context) => tool({
|
|
69323
|
+
description: `Search the web for free (no API key required, DuckDuckGo-backed). Returns ranked results (title, url, snippet).
|
|
69324
|
+
|
|
69325
|
+
<instructions>
|
|
69326
|
+
- Give 1\u20135 \`queries\` that are VARIANTS of the SAME intent (query expansions), not different goals. For non-English topics add one English variant.
|
|
69327
|
+
- Break a complex need into step-by-step searches instead of one giant query.
|
|
69328
|
+
- You CAN use operators: site:, filetype:, inurl:, intitle:.
|
|
69329
|
+
- After searching, OPEN the most relevant results with open_url to actually read them; cross-check facts across 2+ sources.
|
|
69330
|
+
- If results are weak, refine the wording and search again.
|
|
69331
|
+
</instructions>`,
|
|
69332
|
+
inputSchema: external_exports.object({
|
|
69333
|
+
brief: external_exports.string().describe("A one-sentence preamble describing the purpose of this search."),
|
|
69334
|
+
queries: external_exports.array(external_exports.string().trim().min(1).max(2e3)).min(1).max(5).describe("1\u20135 query variants of the same intent."),
|
|
69335
|
+
time: TIME_ENUM.describe(
|
|
69336
|
+
"Optional recency filter (past_day/week/month/year)."
|
|
69337
|
+
)
|
|
69338
|
+
}),
|
|
69339
|
+
execute: async ({ queries, time: time3 }, { abortSignal } = {}) => {
|
|
69340
|
+
const norm = Array.from(
|
|
69341
|
+
new Set(queries.map((q) => q.trim()).filter(Boolean))
|
|
69342
|
+
).slice(0, 5);
|
|
69343
|
+
if (!norm.length) return "Erro: forne\xE7a ao menos uma query n\xE3o vazia.";
|
|
69198
69344
|
try {
|
|
69199
|
-
const
|
|
69200
|
-
|
|
69201
|
-
|
|
69202
|
-
|
|
69203
|
-
|
|
69204
|
-
|
|
69205
|
-
body: JSON.stringify(searchBody),
|
|
69206
|
-
signal: abortSignal
|
|
69207
|
-
});
|
|
69208
|
-
if (response.ok) {
|
|
69209
|
-
return response;
|
|
69210
|
-
}
|
|
69211
|
-
const error51 = await createPerplexityApiError(response);
|
|
69212
|
-
if (!error51.retryable || isFinalAttempt) {
|
|
69213
|
-
throw error51;
|
|
69214
|
-
}
|
|
69215
|
-
const delayMs = getRetryDelayMs(attemptIndex);
|
|
69216
|
-
console.warn("Web search provider error; retrying", {
|
|
69217
|
-
attempt,
|
|
69218
|
-
maxAttempts: WEB_SEARCH_MAX_ATTEMPTS,
|
|
69219
|
-
status: error51.status,
|
|
69220
|
-
statusText: error51.statusText,
|
|
69221
|
-
bodySummary: error51.bodySummary,
|
|
69222
|
-
delayMs
|
|
69223
|
-
});
|
|
69224
|
-
await sleep2(delayMs, abortSignal);
|
|
69225
|
-
} catch (error51) {
|
|
69226
|
-
if (error51 instanceof Error && error51.name === "AbortError") {
|
|
69227
|
-
throw error51;
|
|
69228
|
-
}
|
|
69229
|
-
if (error51 instanceof PerplexityApiError) {
|
|
69230
|
-
throw error51;
|
|
69231
|
-
}
|
|
69232
|
-
if (isFinalAttempt) {
|
|
69233
|
-
throw error51;
|
|
69234
|
-
}
|
|
69235
|
-
const delayMs = getRetryDelayMs(attemptIndex);
|
|
69236
|
-
console.warn("Web search network error; retrying", {
|
|
69237
|
-
attempt,
|
|
69238
|
-
maxAttempts: WEB_SEARCH_MAX_ATTEMPTS,
|
|
69239
|
-
error: stringifyRedactedError(error51),
|
|
69240
|
-
delayMs
|
|
69241
|
-
});
|
|
69242
|
-
await sleep2(delayMs, abortSignal);
|
|
69345
|
+
const df = time3 && time3 !== "all" ? TIME_TO_DDG_DF[time3] : void 0;
|
|
69346
|
+
const results = await searchQueries(norm, abortSignal, df);
|
|
69347
|
+
return formatSearchResults(results);
|
|
69348
|
+
} catch (err) {
|
|
69349
|
+
if (isAbort(err)) return "Erro: opera\xE7\xE3o abortada.";
|
|
69350
|
+
return `Erro na busca: ${err instanceof Error ? err.message : String(err)}`;
|
|
69243
69351
|
}
|
|
69244
69352
|
}
|
|
69245
|
-
|
|
69246
|
-
|
|
69247
|
-
|
|
69248
|
-
const queries = rawQueries.map((query) => query.trim()).filter(Boolean);
|
|
69249
|
-
if (queries.length === 0) {
|
|
69250
|
-
return { queries, error: EMPTY_QUERY_TOOL_ERROR };
|
|
69251
|
-
}
|
|
69252
|
-
if (queries.some((query) => query.length > PERPLEXITY_QUERY_MAX_LENGTH)) {
|
|
69253
|
-
return { queries, error: QUERY_TOO_LONG_TOOL_ERROR };
|
|
69254
|
-
}
|
|
69255
|
-
return { queries: queries.slice(0, 3) };
|
|
69256
|
-
};
|
|
69257
|
-
createWebSearch = (context2) => {
|
|
69258
|
-
const { userLocation, onToolCost } = context2;
|
|
69259
|
-
return tool({
|
|
69260
|
-
description: `Search for information across various sources.
|
|
69353
|
+
});
|
|
69354
|
+
createOpenUrl = (_opts) => tool({
|
|
69355
|
+
description: `Open a specific URL and return its readable text content (free, no API key). Use it to actually READ a link \u2014 the one the user pasted, or one from a prior search \u2014 before you act on it.
|
|
69261
69356
|
|
|
69262
69357
|
<instructions>
|
|
69263
|
-
-
|
|
69264
|
-
-
|
|
69265
|
-
-
|
|
69266
|
-
- For complex searches, MUST break down into step-by-step searches instead of using a single complex query
|
|
69267
|
-
- Access multiple URLs from search results for comprehensive information or cross-validation
|
|
69268
|
-
- CAN use Google dork syntax (site:, filetype:, inurl:, intitle:, etc.) for targeted reconnaissance and pentest enumeration
|
|
69269
|
-
- Only use \`time\` parameter when explicitly required by task, otherwise leave time range unrestricted
|
|
69270
|
-
- Prioritize cybersecurity-relevant information: CVEs, CVSS scores, exploits, PoCs, security tools, and pentest methodologies
|
|
69271
|
-
- Include specific versions, configurations, and technical details; cite reliable sources (NIST, OWASP, CVE databases)
|
|
69272
|
-
- For commands/installations, prioritize Kali Linux compatibility using apt or pre-installed tools
|
|
69358
|
+
- The URL must be public and http(s).
|
|
69359
|
+
- Returns the page title + extracted text (JS is not executed; for JS-heavy pages use the recon toolkit's --browser path).
|
|
69360
|
+
- Read carefully, then verify important facts against another source.
|
|
69273
69361
|
</instructions>`,
|
|
69274
|
-
|
|
69275
|
-
|
|
69276
|
-
|
|
69277
|
-
|
|
69278
|
-
|
|
69279
|
-
|
|
69280
|
-
|
|
69281
|
-
|
|
69282
|
-
|
|
69283
|
-
)
|
|
69284
|
-
|
|
69285
|
-
|
|
69286
|
-
|
|
69287
|
-
|
|
69288
|
-
|
|
69289
|
-
|
|
69290
|
-
|
|
69291
|
-
|
|
69292
|
-
|
|
69293
|
-
|
|
69294
|
-
|
|
69295
|
-
|
|
69296
|
-
{
|
|
69297
|
-
country: userLocation?.country,
|
|
69298
|
-
recency: time3 && time3 !== "all" ? RECENCY_MAP[time3] : void 0
|
|
69299
|
-
}
|
|
69300
|
-
);
|
|
69301
|
-
const response = await fetchPerplexitySearch(searchBody, abortSignal);
|
|
69302
|
-
onToolCost?.(WEB_SEARCH_COST_PER_REQUEST);
|
|
69303
|
-
const searchResponse = await response.json();
|
|
69304
|
-
const isMultiQuery = queries.length > 1;
|
|
69305
|
-
let allResults;
|
|
69306
|
-
if (isMultiQuery && Array.isArray(searchResponse.results[0])) {
|
|
69307
|
-
allResults = searchResponse.results.flat();
|
|
69308
|
-
} else {
|
|
69309
|
-
allResults = searchResponse.results;
|
|
69310
|
-
}
|
|
69311
|
-
return formatSearchResults(allResults);
|
|
69312
|
-
} catch (error51) {
|
|
69313
|
-
if (error51 instanceof Error && error51.name === "AbortError") {
|
|
69314
|
-
return "Error: Operation aborted";
|
|
69315
|
-
}
|
|
69316
|
-
if (error51 instanceof PerplexityApiError) {
|
|
69317
|
-
console.error("Web search tool error:", {
|
|
69318
|
-
name: error51.name,
|
|
69319
|
-
status: error51.status,
|
|
69320
|
-
statusText: error51.statusText,
|
|
69321
|
-
retryable: error51.retryable,
|
|
69322
|
-
bodySummary: error51.bodySummary
|
|
69323
|
-
});
|
|
69324
|
-
return formatPerplexityFailureForTool(
|
|
69325
|
-
error51,
|
|
69326
|
-
error51.retryable ? WEB_SEARCH_MAX_ATTEMPTS : 1
|
|
69327
|
-
);
|
|
69328
|
-
}
|
|
69329
|
-
const errorMessage = stringifyRedactedError(error51);
|
|
69330
|
-
console.error("Web search tool error:", errorMessage);
|
|
69331
|
-
return `Error performing web search: ${errorMessage}`;
|
|
69332
|
-
}
|
|
69362
|
+
inputSchema: external_exports.object({
|
|
69363
|
+
brief: external_exports.string().describe("A one-sentence preamble describing the purpose of this fetch."),
|
|
69364
|
+
url: external_exports.string().describe("The http(s) URL to open and read.")
|
|
69365
|
+
}),
|
|
69366
|
+
execute: async ({ url: url2 }, { abortSignal } = {}) => {
|
|
69367
|
+
const canon = canonicalUrl(url2);
|
|
69368
|
+
if (!canon) return `URL inv\xE1lida: ${url2}`;
|
|
69369
|
+
try {
|
|
69370
|
+
const jina = await jinaRead(canon, abortSignal);
|
|
69371
|
+
if (jina) return truncate3(jina, OPEN_URL_MAX_CHARS * 2);
|
|
69372
|
+
const page = await readPage(canon, abortSignal);
|
|
69373
|
+
if (!isTextual(page.contentType)) {
|
|
69374
|
+
return `(${page.finalUrl}) conte\xFAdo n\xE3o textual (${page.contentType}, ${page.bytes} bytes) \u2014 n\xE3o extra\xEDdo.`;
|
|
69375
|
+
}
|
|
69376
|
+
const head = `# ${page.title}
|
|
69377
|
+
${page.finalUrl} (HTTP ${page.status}, ${page.bytes} bytes)
|
|
69378
|
+
`;
|
|
69379
|
+
const body = page.text || "(sem texto extra\xEDvel \u2014 pode ser JS puro; use a recon toolkit com --browser)";
|
|
69380
|
+
return head + "\n" + truncate3(body, OPEN_URL_MAX_CHARS);
|
|
69381
|
+
} catch (err) {
|
|
69382
|
+
if (isAbort(err)) return "Erro: opera\xE7\xE3o abortada.";
|
|
69383
|
+
return `Erro ao abrir URL: ${err instanceof Error ? err.message : String(err)}`;
|
|
69333
69384
|
}
|
|
69334
|
-
}
|
|
69335
|
-
};
|
|
69336
|
-
|
|
69337
|
-
|
|
69338
|
-
|
|
69339
|
-
// ../lib/ai/tools/open-url.ts
|
|
69340
|
-
var createOpenUrlTool;
|
|
69341
|
-
var init_open_url = __esm({
|
|
69342
|
-
"../lib/ai/tools/open-url.ts"() {
|
|
69343
|
-
"use strict";
|
|
69344
|
-
init_dist5();
|
|
69345
|
-
init_zod();
|
|
69346
|
-
init_token_utils();
|
|
69347
|
-
createOpenUrlTool = () => {
|
|
69348
|
-
return tool({
|
|
69349
|
-
description: `Retrieve the full contents of a specific webpage by URL.
|
|
69385
|
+
}
|
|
69386
|
+
});
|
|
69387
|
+
createDeepResearch = (opts) => tool({
|
|
69388
|
+
description: `Deep web research: search + fetch MANY pages concurrently and return a synthesized digest with sources. Free, no API key.
|
|
69350
69389
|
|
|
69351
69390
|
<instructions>
|
|
69352
|
-
-
|
|
69353
|
-
-
|
|
69354
|
-
-
|
|
69355
|
-
-
|
|
69391
|
+
- Provide \`queries\` (1\u20136 variants) and/or seed \`urls\`. It searches, collects candidate links, and fetches up to \`max_pages\` of them in parallel.
|
|
69392
|
+
- Tune to the SUBJECT: a quick fact needs a handful of pages; a broad survey can sweep up to 500. Do NOT fetch 500 for something small.
|
|
69393
|
+
- Returns: per-source title + url + a short extract, plus counts. A full Markdown report is saved under the workspace's reports/ when possible.
|
|
69394
|
+
- Use this for broad/unknown topics and cross-validation; use open_url to drill into a single page, web_search for a quick lookup.
|
|
69356
69395
|
</instructions>`,
|
|
69357
|
-
|
|
69358
|
-
|
|
69359
|
-
|
|
69360
|
-
|
|
69361
|
-
|
|
69362
|
-
|
|
69363
|
-
|
|
69364
|
-
|
|
69365
|
-
|
|
69366
|
-
|
|
69367
|
-
|
|
69368
|
-
|
|
69369
|
-
|
|
69370
|
-
|
|
69371
|
-
|
|
69372
|
-
|
|
69373
|
-
|
|
69374
|
-
|
|
69375
|
-
|
|
69376
|
-
|
|
69377
|
-
|
|
69378
|
-
|
|
69379
|
-
|
|
69380
|
-
|
|
69381
|
-
|
|
69382
|
-
|
|
69383
|
-
|
|
69384
|
-
|
|
69385
|
-
|
|
69386
|
-
|
|
69387
|
-
|
|
69388
|
-
|
|
69389
|
-
|
|
69390
|
-
|
|
69396
|
+
inputSchema: external_exports.object({
|
|
69397
|
+
brief: external_exports.string().describe("A one-sentence preamble describing the research goal."),
|
|
69398
|
+
queries: external_exports.array(external_exports.string().trim().min(1).max(2e3)).max(6).optional().describe("1\u20136 search query variants of the same intent."),
|
|
69399
|
+
urls: external_exports.array(external_exports.string()).max(HARD_PAGE_CEILING).optional().describe("Optional seed URLs to include directly."),
|
|
69400
|
+
max_pages: external_exports.number().int().min(1).max(HARD_PAGE_CEILING).optional().describe("Max pages to fetch (default 12, ceiling 500). Match to the subject."),
|
|
69401
|
+
concurrency: external_exports.number().int().min(1).max(HARD_PAGE_CEILING).optional().describe("Parallel fetches (default 8, up to 500)."),
|
|
69402
|
+
time: TIME_ENUM.describe("Optional recency filter for the search step.")
|
|
69403
|
+
}),
|
|
69404
|
+
execute: async (input, { abortSignal } = {}) => {
|
|
69405
|
+
const queries = Array.from(
|
|
69406
|
+
new Set((input.queries || []).map((q) => q.trim()).filter(Boolean))
|
|
69407
|
+
).slice(0, 6);
|
|
69408
|
+
const seeds = [];
|
|
69409
|
+
for (const u of input.urls || []) {
|
|
69410
|
+
const c = canonicalUrl(u);
|
|
69411
|
+
if (c) seeds.push({ title: c, url: c, snippet: "" });
|
|
69412
|
+
}
|
|
69413
|
+
if (!queries.length && !seeds.length) {
|
|
69414
|
+
return "Erro: forne\xE7a queries e/ou urls para pesquisar.";
|
|
69415
|
+
}
|
|
69416
|
+
const maxPages = Math.min(
|
|
69417
|
+
Math.max(1, input.max_pages ?? 12),
|
|
69418
|
+
HARD_PAGE_CEILING
|
|
69419
|
+
);
|
|
69420
|
+
const requestedConc = Math.min(
|
|
69421
|
+
Math.max(1, input.concurrency ?? 8),
|
|
69422
|
+
HARD_PAGE_CEILING
|
|
69423
|
+
);
|
|
69424
|
+
try {
|
|
69425
|
+
const df = input.time && input.time !== "all" ? TIME_TO_DDG_DF[input.time] : void 0;
|
|
69426
|
+
const searched = queries.length ? await searchQueries(queries, abortSignal, df) : [];
|
|
69427
|
+
const candidates = dedupeResults([...seeds, ...searched]);
|
|
69428
|
+
const totalFound = candidates.length;
|
|
69429
|
+
const capped = totalFound > maxPages;
|
|
69430
|
+
const toFetch = candidates.slice(0, maxPages);
|
|
69431
|
+
const conc = Math.min(requestedConc, toFetch.length || 1);
|
|
69432
|
+
const fetched = await mapPool(
|
|
69433
|
+
toFetch,
|
|
69434
|
+
conc,
|
|
69435
|
+
(r2) => readPage(r2.url, abortSignal)
|
|
69436
|
+
);
|
|
69437
|
+
const pages = [];
|
|
69438
|
+
let failures = 0;
|
|
69439
|
+
fetched.forEach((p) => {
|
|
69440
|
+
if (p && "error" in p) failures++;
|
|
69441
|
+
else if (p) pages.push(p);
|
|
69442
|
+
});
|
|
69443
|
+
const reportPath = await saveResearchReport(
|
|
69444
|
+
opts.workdir,
|
|
69445
|
+
queries,
|
|
69446
|
+
pages,
|
|
69447
|
+
{ totalFound, fetched: toFetch.length, ok: pages.length, failures, capped, maxPages }
|
|
69448
|
+
);
|
|
69449
|
+
return formatResearchDigest(pages, {
|
|
69450
|
+
totalFound,
|
|
69451
|
+
fetched: toFetch.length,
|
|
69452
|
+
ok: pages.length,
|
|
69453
|
+
failures,
|
|
69454
|
+
capped,
|
|
69455
|
+
maxPages,
|
|
69456
|
+
conc,
|
|
69457
|
+
reportPath
|
|
69458
|
+
});
|
|
69459
|
+
} catch (err) {
|
|
69460
|
+
if (isAbort(err)) return "Erro: opera\xE7\xE3o abortada.";
|
|
69461
|
+
return `Erro na pesquisa profunda: ${err instanceof Error ? err.message : String(err)}`;
|
|
69391
69462
|
}
|
|
69392
|
-
}
|
|
69393
|
-
};
|
|
69463
|
+
}
|
|
69464
|
+
});
|
|
69394
69465
|
}
|
|
69395
69466
|
});
|
|
69396
69467
|
|
|
69397
69468
|
// src/tools/scope.ts
|
|
69398
|
-
var
|
|
69469
|
+
var import_node_fs7, import_node_path6, import_promises, ipv4ToInt, isIpLiteral, maskForPrefix, parseCidr, ScopeImpl, scopeFileFor, loadScope, hostFromUrl;
|
|
69399
69470
|
var init_scope = __esm({
|
|
69400
69471
|
"src/tools/scope.ts"() {
|
|
69401
69472
|
"use strict";
|
|
69402
|
-
|
|
69403
|
-
|
|
69473
|
+
import_node_fs7 = __toESM(require("node:fs"));
|
|
69474
|
+
import_node_path6 = __toESM(require("node:path"));
|
|
69404
69475
|
import_promises = __toESM(require("node:dns/promises"));
|
|
69405
69476
|
ipv4ToInt = (ip) => {
|
|
69406
69477
|
const parts = ip.split(".");
|
|
@@ -69498,11 +69569,11 @@ var init_scope = __esm({
|
|
|
69498
69569
|
return false;
|
|
69499
69570
|
}
|
|
69500
69571
|
};
|
|
69501
|
-
scopeFileFor = (workdir) =>
|
|
69572
|
+
scopeFileFor = (workdir) => import_node_path6.default.join(workdir, "recon", "scope.txt");
|
|
69502
69573
|
loadScope = (workdir) => {
|
|
69503
69574
|
const file2 = scopeFileFor(workdir);
|
|
69504
69575
|
try {
|
|
69505
|
-
const text2 =
|
|
69576
|
+
const text2 = import_node_fs7.default.readFileSync(file2, "utf8");
|
|
69506
69577
|
return ScopeImpl.parse(text2, file2);
|
|
69507
69578
|
} catch {
|
|
69508
69579
|
return ScopeImpl.parse("", null);
|
|
@@ -69519,7 +69590,7 @@ var init_scope = __esm({
|
|
|
69519
69590
|
});
|
|
69520
69591
|
|
|
69521
69592
|
// src/tools/http-request.ts
|
|
69522
|
-
var FUZZ, MAX_FUZZ, BODY_PREVIEW_CHARS, DEFAULT_TIMEOUT_S, LOOPBACK, numberEnv,
|
|
69593
|
+
var FUZZ, MAX_FUZZ, BODY_PREVIEW_CHARS, DEFAULT_TIMEOUT_S, LOOPBACK, numberEnv, sleep2, sampleUrl, NOTABLE_HEADERS, applyFuzz, buildInit, collectNotableHeaders, doRequest, summarizeFuzz, scopeRefusal, createHttpRequest;
|
|
69523
69594
|
var init_http_request = __esm({
|
|
69524
69595
|
"src/tools/http-request.ts"() {
|
|
69525
69596
|
"use strict";
|
|
@@ -69535,7 +69606,7 @@ var init_http_request = __esm({
|
|
|
69535
69606
|
const raw = Number(process.env[name25]);
|
|
69536
69607
|
return Number.isFinite(raw) && raw >= 0 ? raw : void 0;
|
|
69537
69608
|
};
|
|
69538
|
-
|
|
69609
|
+
sleep2 = (ms, signal) => new Promise((resolve2) => {
|
|
69539
69610
|
if (ms <= 0 || signal?.aborted) return resolve2();
|
|
69540
69611
|
const t = setTimeout(resolve2, ms);
|
|
69541
69612
|
signal?.addEventListener(
|
|
@@ -69656,7 +69727,7 @@ var init_http_request = __esm({
|
|
|
69656
69727
|
};
|
|
69657
69728
|
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.`;
|
|
69658
69729
|
createHttpRequest = (deps) => {
|
|
69659
|
-
const { workdir, loadScopeFn = loadScope } = deps;
|
|
69730
|
+
const { workdir, loadScopeFn = loadScope, unrestricted = false } = deps;
|
|
69660
69731
|
const envThrottle = numberEnv("clawfast_HTTP_THROTTLE_MS");
|
|
69661
69732
|
const envJitter = numberEnv("clawfast_HTTP_JITTER_MS");
|
|
69662
69733
|
return tool({
|
|
@@ -69670,20 +69741,30 @@ SCOPE: every request is gated by recon/scope.txt (same allowlist as the recon to
|
|
|
69670
69741
|
|
|
69671
69742
|
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.`,
|
|
69672
69743
|
inputSchema: external_exports.object({
|
|
69673
|
-
brief: external_exports.string().describe(
|
|
69744
|
+
brief: external_exports.string().describe(
|
|
69745
|
+
"A one-sentence preamble describing the purpose of this request."
|
|
69746
|
+
),
|
|
69674
69747
|
url: external_exports.string().url().describe(
|
|
69675
69748
|
"Target URL. May contain the marker FUZZ (replaced by each fuzz payload)."
|
|
69676
69749
|
),
|
|
69677
69750
|
method: external_exports.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]).optional().describe("HTTP method (default GET)."),
|
|
69678
69751
|
headers: external_exports.record(external_exports.string(), external_exports.string()).optional().describe("Request headers. Values may contain FUZZ."),
|
|
69679
69752
|
body: external_exports.string().optional().describe("Request body for POST/PUT/PATCH/DELETE. May contain FUZZ."),
|
|
69680
|
-
follow_redirects: external_exports.boolean().optional().describe(
|
|
69681
|
-
|
|
69753
|
+
follow_redirects: external_exports.boolean().optional().describe(
|
|
69754
|
+
"Follow 3xx redirects (default true). Set false to inspect Location."
|
|
69755
|
+
),
|
|
69756
|
+
timeout: external_exports.number().optional().describe(
|
|
69757
|
+
`Per-request timeout in seconds (default ${DEFAULT_TIMEOUT_S}).`
|
|
69758
|
+
),
|
|
69682
69759
|
fuzz: external_exports.array(external_exports.string()).optional().describe(
|
|
69683
69760
|
`Intruder payloads \u2014 substituted into the FUZZ marker, one request each (max ${MAX_FUZZ}).`
|
|
69684
69761
|
),
|
|
69685
|
-
throttle_ms: external_exports.number().optional().describe(
|
|
69686
|
-
|
|
69762
|
+
throttle_ms: external_exports.number().optional().describe(
|
|
69763
|
+
"OPSEC: delay between fuzz requests in ms (overrides clawfast_HTTP_THROTTLE_MS)."
|
|
69764
|
+
),
|
|
69765
|
+
jitter_ms: external_exports.number().optional().describe(
|
|
69766
|
+
"OPSEC: random extra delay 0..jitter added to each throttle."
|
|
69767
|
+
)
|
|
69687
69768
|
}),
|
|
69688
69769
|
execute: async (input, { abortSignal } = {}) => {
|
|
69689
69770
|
const method = input.method ?? "GET";
|
|
@@ -69693,7 +69774,7 @@ USE FOR: testing a single endpoint, replaying/tampering a captured request, para
|
|
|
69693
69774
|
if (!host) {
|
|
69694
69775
|
return { error: `URL inv\xE1lida: ${input.url}` };
|
|
69695
69776
|
}
|
|
69696
|
-
if (!LOOPBACK.has(host)) {
|
|
69777
|
+
if (!unrestricted && !LOOPBACK.has(host)) {
|
|
69697
69778
|
const scope = loadScopeFn(workdir);
|
|
69698
69779
|
const allowed = await scope.allows(host);
|
|
69699
69780
|
if (!allowed) {
|
|
@@ -69733,7 +69814,13 @@ USE FOR: testing a single endpoint, replaying/tampering a captured request, para
|
|
|
69733
69814
|
abortSignal
|
|
69734
69815
|
);
|
|
69735
69816
|
if ("error" in res2) {
|
|
69736
|
-
rows.push({
|
|
69817
|
+
rows.push({
|
|
69818
|
+
payload,
|
|
69819
|
+
status: "ERR",
|
|
69820
|
+
bodyLength: 0,
|
|
69821
|
+
timeMs: res2.timeMs,
|
|
69822
|
+
error: res2.error
|
|
69823
|
+
});
|
|
69737
69824
|
} else {
|
|
69738
69825
|
rows.push({
|
|
69739
69826
|
payload,
|
|
@@ -69744,7 +69831,10 @@ USE FOR: testing a single endpoint, replaying/tampering a captured request, para
|
|
|
69744
69831
|
});
|
|
69745
69832
|
}
|
|
69746
69833
|
if (i < list.length - 1 && (throttle > 0 || jitter > 0)) {
|
|
69747
|
-
await
|
|
69834
|
+
await sleep2(
|
|
69835
|
+
throttle + Math.floor(Math.random() * (jitter + 1)),
|
|
69836
|
+
abortSignal
|
|
69837
|
+
);
|
|
69748
69838
|
}
|
|
69749
69839
|
}
|
|
69750
69840
|
return { fuzz: { host, count: rows.length, rows } };
|
|
@@ -69822,12 +69912,12 @@ ${s.bodyPreview}`
|
|
|
69822
69912
|
});
|
|
69823
69913
|
|
|
69824
69914
|
// src/tools/findings.ts
|
|
69825
|
-
var
|
|
69915
|
+
var import_node_fs8, import_node_path7, SEVERITIES, STATUSES, SEVERITY_RANK, SEVERITY_EMOJI, findingsStoreFor, readAll, writeAll, nextId, sortBySeverity, renderReport, oneLine, createFindings;
|
|
69826
69916
|
var init_findings = __esm({
|
|
69827
69917
|
"src/tools/findings.ts"() {
|
|
69828
69918
|
"use strict";
|
|
69829
|
-
|
|
69830
|
-
|
|
69919
|
+
import_node_fs8 = __toESM(require("node:fs"));
|
|
69920
|
+
import_node_path7 = __toESM(require("node:path"));
|
|
69831
69921
|
init_dist5();
|
|
69832
69922
|
init_zod();
|
|
69833
69923
|
SEVERITIES = [
|
|
@@ -69853,16 +69943,16 @@ var init_findings = __esm({
|
|
|
69853
69943
|
info: "\u26AA"
|
|
69854
69944
|
};
|
|
69855
69945
|
findingsStoreFor = (workdir) => {
|
|
69856
|
-
const findingsDir =
|
|
69946
|
+
const findingsDir = import_node_path7.default.join(workdir, "findings");
|
|
69857
69947
|
return {
|
|
69858
69948
|
findingsDir,
|
|
69859
|
-
storeFile:
|
|
69860
|
-
reportFile:
|
|
69949
|
+
storeFile: import_node_path7.default.join(findingsDir, "findings.json"),
|
|
69950
|
+
reportFile: import_node_path7.default.join(findingsDir, "report.md")
|
|
69861
69951
|
};
|
|
69862
69952
|
};
|
|
69863
69953
|
readAll = (store) => {
|
|
69864
69954
|
try {
|
|
69865
|
-
const raw =
|
|
69955
|
+
const raw = import_node_fs8.default.readFileSync(store.storeFile, "utf8");
|
|
69866
69956
|
const parsed = JSON.parse(raw);
|
|
69867
69957
|
return Array.isArray(parsed) ? parsed : [];
|
|
69868
69958
|
} catch {
|
|
@@ -69870,8 +69960,8 @@ var init_findings = __esm({
|
|
|
69870
69960
|
}
|
|
69871
69961
|
};
|
|
69872
69962
|
writeAll = (store, findings) => {
|
|
69873
|
-
|
|
69874
|
-
|
|
69963
|
+
import_node_fs8.default.mkdirSync(store.findingsDir, { recursive: true });
|
|
69964
|
+
import_node_fs8.default.writeFileSync(store.storeFile, JSON.stringify(findings, null, 2), "utf8");
|
|
69875
69965
|
};
|
|
69876
69966
|
nextId = (findings) => {
|
|
69877
69967
|
let max = 0;
|
|
@@ -69970,7 +70060,9 @@ ACTIONS
|
|
|
69970
70060
|
Use this together with http_request: probe \u2192 observe \u2192 record evidence \u2192 mark confirmed.`,
|
|
69971
70061
|
inputSchema: external_exports.object({
|
|
69972
70062
|
action: external_exports.enum(["add", "update", "list", "report"]),
|
|
69973
|
-
brief: external_exports.string().describe(
|
|
70063
|
+
brief: external_exports.string().describe(
|
|
70064
|
+
"A one-sentence preamble describing the purpose of this operation."
|
|
70065
|
+
),
|
|
69974
70066
|
id: external_exports.string().optional().describe("Finding id (e.g. F-001) \u2014 required for update."),
|
|
69975
70067
|
title: external_exports.string().optional(),
|
|
69976
70068
|
severity: external_exports.enum(SEVERITIES).optional(),
|
|
@@ -70043,7 +70135,9 @@ Use this together with http_request: probe \u2192 observe \u2192 record evidence
|
|
|
70043
70135
|
if (input.action === "list") {
|
|
70044
70136
|
let filtered = findings;
|
|
70045
70137
|
if (input.filter_severity) {
|
|
70046
|
-
filtered = filtered.filter(
|
|
70138
|
+
filtered = filtered.filter(
|
|
70139
|
+
(f) => f.severity === input.filter_severity
|
|
70140
|
+
);
|
|
70047
70141
|
}
|
|
70048
70142
|
if (input.filter_status) {
|
|
70049
70143
|
filtered = filtered.filter((f) => f.status === input.filter_status);
|
|
@@ -70055,8 +70149,8 @@ Use this together with http_request: probe \u2192 observe \u2192 record evidence
|
|
|
70055
70149
|
};
|
|
70056
70150
|
}
|
|
70057
70151
|
const md = renderReport(findings);
|
|
70058
|
-
|
|
70059
|
-
|
|
70152
|
+
import_node_fs8.default.mkdirSync(store.findingsDir, { recursive: true });
|
|
70153
|
+
import_node_fs8.default.writeFileSync(store.reportFile, md, "utf8");
|
|
70060
70154
|
return { report: store.reportFile, total: findings.length };
|
|
70061
70155
|
} catch (err) {
|
|
70062
70156
|
return {
|
|
@@ -70286,20 +70380,48 @@ var init_utils4 = __esm({
|
|
|
70286
70380
|
|
|
70287
70381
|
// src/local-sandbox.ts
|
|
70288
70382
|
function inferShellFlag(shell2) {
|
|
70289
|
-
const base =
|
|
70383
|
+
const base = import_node_path8.default.basename(shell2).toLowerCase();
|
|
70290
70384
|
if (base === "cmd" || base === "cmd.exe") return "/C";
|
|
70291
70385
|
if (base === "powershell" || base === "powershell.exe" || base === "pwsh") {
|
|
70292
70386
|
return "-Command";
|
|
70293
70387
|
}
|
|
70294
70388
|
return "-c";
|
|
70295
70389
|
}
|
|
70296
|
-
|
|
70390
|
+
function detectLinux(platform) {
|
|
70391
|
+
if (platform !== "linux") {
|
|
70392
|
+
return { distroId: "", distroName: "", isKali: false };
|
|
70393
|
+
}
|
|
70394
|
+
try {
|
|
70395
|
+
const text2 = (0, import_node_fs9.readFileSync)("/etc/os-release", "utf8");
|
|
70396
|
+
const map2 = {};
|
|
70397
|
+
for (const raw of text2.split(/\r?\n/)) {
|
|
70398
|
+
const eq = raw.indexOf("=");
|
|
70399
|
+
if (eq === -1) continue;
|
|
70400
|
+
const key = raw.slice(0, eq).trim();
|
|
70401
|
+
const val = raw.slice(eq + 1).trim().replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
|
|
70402
|
+
if (key) map2[key] = val;
|
|
70403
|
+
}
|
|
70404
|
+
const id = (map2.ID || "").toLowerCase();
|
|
70405
|
+
const idLike = (map2.ID_LIKE || "").toLowerCase();
|
|
70406
|
+
const distroName = map2.PRETTY_NAME || map2.NAME || "Linux";
|
|
70407
|
+
const isKali = id === "kali" || idLike.includes("kali") || /kali/i.test(distroName);
|
|
70408
|
+
return { distroId: id, distroName, isKali };
|
|
70409
|
+
} catch {
|
|
70410
|
+
return { distroId: "", distroName: "Linux", isKali: false };
|
|
70411
|
+
}
|
|
70412
|
+
}
|
|
70413
|
+
function computeUnrestricted(platform) {
|
|
70414
|
+
if (truthyEnv("CLAWFAST_UNRESTRICTED")) return true;
|
|
70415
|
+
if (truthyEnv("CLAWFAST_STRICT_SCOPE")) return false;
|
|
70416
|
+
return platform === "linux";
|
|
70417
|
+
}
|
|
70418
|
+
var import_node_child_process2, import_node_fs9, import_node_path8, import_node_os3, import_node_url, import_meta, LocalSandbox, truthyEnv;
|
|
70297
70419
|
var init_local_sandbox = __esm({
|
|
70298
70420
|
"src/local-sandbox.ts"() {
|
|
70299
70421
|
"use strict";
|
|
70300
70422
|
import_node_child_process2 = require("node:child_process");
|
|
70301
|
-
|
|
70302
|
-
|
|
70423
|
+
import_node_fs9 = require("node:fs");
|
|
70424
|
+
import_node_path8 = __toESM(require("node:path"));
|
|
70303
70425
|
import_node_os3 = __toESM(require("node:os"));
|
|
70304
70426
|
import_node_url = require("node:url");
|
|
70305
70427
|
init_utils4();
|
|
@@ -70311,6 +70433,10 @@ var init_local_sandbox = __esm({
|
|
|
70311
70433
|
// The most recent foreground child. Lets the CLI forward user-typed lines to
|
|
70312
70434
|
// a command that is waiting for stdin (interactive prompts, REPLs, etc.).
|
|
70313
70435
|
this.activeForegroundChild = null;
|
|
70436
|
+
// Host fingerprint, computed once at construction. Linux/Kali unlock the
|
|
70437
|
+
// "full-power" sandbox context; root status flips sudo guidance; unrestricted
|
|
70438
|
+
// opens the native http_request / recon scope gate (default ON for Linux).
|
|
70439
|
+
this.platform = import_node_os3.default.platform();
|
|
70314
70440
|
this.commands = {
|
|
70315
70441
|
run: (command, opts) => {
|
|
70316
70442
|
return new Promise((resolve2, reject) => {
|
|
@@ -70397,15 +70523,15 @@ var init_local_sandbox = __esm({
|
|
|
70397
70523
|
this.files = {
|
|
70398
70524
|
write: async (filePath, content) => {
|
|
70399
70525
|
const resolved = this.resolvePath(filePath);
|
|
70400
|
-
await
|
|
70526
|
+
await import_node_fs9.promises.mkdir(import_node_path8.default.dirname(resolved), { recursive: true });
|
|
70401
70527
|
const data = typeof content === "string" ? content : content instanceof ArrayBuffer ? Buffer.from(content) : content;
|
|
70402
|
-
await
|
|
70528
|
+
await import_node_fs9.promises.writeFile(resolved, data);
|
|
70403
70529
|
},
|
|
70404
70530
|
read: async (filePath) => {
|
|
70405
|
-
return
|
|
70531
|
+
return import_node_fs9.promises.readFile(this.resolvePath(filePath), "utf8");
|
|
70406
70532
|
},
|
|
70407
70533
|
remove: async (filePath) => {
|
|
70408
|
-
await
|
|
70534
|
+
await import_node_fs9.promises.rm(this.resolvePath(filePath), {
|
|
70409
70535
|
recursive: true,
|
|
70410
70536
|
force: true
|
|
70411
70537
|
});
|
|
@@ -70413,8 +70539,8 @@ var init_local_sandbox = __esm({
|
|
|
70413
70539
|
list: async (dirPath = ".") => {
|
|
70414
70540
|
const resolved = this.resolvePath(dirPath);
|
|
70415
70541
|
try {
|
|
70416
|
-
const entries = await
|
|
70417
|
-
return entries.filter((e) => e.isFile()).map((e) => ({ name:
|
|
70542
|
+
const entries = await import_node_fs9.promises.readdir(resolved, { withFileTypes: true });
|
|
70543
|
+
return entries.filter((e) => e.isFile()).map((e) => ({ name: import_node_path8.default.join(resolved, e.name) }));
|
|
70418
70544
|
} catch {
|
|
70419
70545
|
return [];
|
|
70420
70546
|
}
|
|
@@ -70430,12 +70556,15 @@ var init_local_sandbox = __esm({
|
|
|
70430
70556
|
this.shellFlag = shell2.shellFlag;
|
|
70431
70557
|
}
|
|
70432
70558
|
const explicit = opts?.workdir || process.env.CLI_WORKDIR;
|
|
70433
|
-
const base = explicit ||
|
|
70559
|
+
const base = explicit || import_node_path8.default.join(process.cwd(), "SPRIT");
|
|
70434
70560
|
this.autoCreated = !explicit;
|
|
70435
|
-
this.workdir =
|
|
70561
|
+
this.workdir = import_node_path8.default.resolve(base).replace(/\\/g, "/");
|
|
70562
|
+
this.linux = detectLinux(this.platform);
|
|
70563
|
+
this.root = typeof process.getuid === "function" ? process.getuid() === 0 : false;
|
|
70564
|
+
this.unrestricted = computeUnrestricted(this.platform);
|
|
70436
70565
|
}
|
|
70437
70566
|
async init() {
|
|
70438
|
-
await
|
|
70567
|
+
await import_node_fs9.promises.mkdir(this.workdir, { recursive: true });
|
|
70439
70568
|
await this.seedReconToolkit();
|
|
70440
70569
|
await this.seedAuditToolkit();
|
|
70441
70570
|
}
|
|
@@ -70452,8 +70581,8 @@ var init_local_sandbox = __esm({
|
|
|
70452
70581
|
const assetsDir = (0, import_node_url.fileURLToPath)(
|
|
70453
70582
|
new URL("../assets/recon", import_meta.url)
|
|
70454
70583
|
);
|
|
70455
|
-
const destDir =
|
|
70456
|
-
await
|
|
70584
|
+
const destDir = import_node_path8.default.join(this.workdir, "recon");
|
|
70585
|
+
await import_node_fs9.promises.mkdir(destDir, { recursive: true });
|
|
70457
70586
|
const files = [
|
|
70458
70587
|
["recon_deep.py", true],
|
|
70459
70588
|
["recon_common.py", true],
|
|
@@ -70469,17 +70598,17 @@ var init_local_sandbox = __esm({
|
|
|
70469
70598
|
["scope.txt", false]
|
|
70470
70599
|
];
|
|
70471
70600
|
for (const [name25, overwrite] of files) {
|
|
70472
|
-
const dest =
|
|
70601
|
+
const dest = import_node_path8.default.join(destDir, name25);
|
|
70473
70602
|
if (!overwrite) {
|
|
70474
70603
|
try {
|
|
70475
|
-
await
|
|
70604
|
+
await import_node_fs9.promises.access(dest);
|
|
70476
70605
|
continue;
|
|
70477
70606
|
} catch {
|
|
70478
70607
|
}
|
|
70479
70608
|
}
|
|
70480
70609
|
try {
|
|
70481
|
-
const content = await
|
|
70482
|
-
await
|
|
70610
|
+
const content = await import_node_fs9.promises.readFile(import_node_path8.default.join(assetsDir, name25));
|
|
70611
|
+
await import_node_fs9.promises.writeFile(dest, content);
|
|
70483
70612
|
} catch {
|
|
70484
70613
|
}
|
|
70485
70614
|
}
|
|
@@ -70497,12 +70626,12 @@ var init_local_sandbox = __esm({
|
|
|
70497
70626
|
const assetsDir = (0, import_node_url.fileURLToPath)(
|
|
70498
70627
|
new URL("../assets/audit", import_meta.url)
|
|
70499
70628
|
);
|
|
70500
|
-
const destDir =
|
|
70501
|
-
await
|
|
70629
|
+
const destDir = import_node_path8.default.join(this.workdir, "audit");
|
|
70630
|
+
await import_node_fs9.promises.mkdir(destDir, { recursive: true });
|
|
70502
70631
|
for (const name25 of ["project_audit.py", "README.md"]) {
|
|
70503
70632
|
try {
|
|
70504
|
-
const content = await
|
|
70505
|
-
await
|
|
70633
|
+
const content = await import_node_fs9.promises.readFile(import_node_path8.default.join(assetsDir, name25));
|
|
70634
|
+
await import_node_fs9.promises.writeFile(import_node_path8.default.join(destDir, name25), content);
|
|
70506
70635
|
} catch {
|
|
70507
70636
|
}
|
|
70508
70637
|
}
|
|
@@ -70521,6 +70650,30 @@ var init_local_sandbox = __esm({
|
|
|
70521
70650
|
isWindows() {
|
|
70522
70651
|
return import_node_os3.default.platform() === "win32" && !this.isBashLikeShell();
|
|
70523
70652
|
}
|
|
70653
|
+
/** True on any Linux host — unlocks the full-power sandbox context. */
|
|
70654
|
+
isLinux() {
|
|
70655
|
+
return this.platform === "linux";
|
|
70656
|
+
}
|
|
70657
|
+
/** True when /etc/os-release identifies this host as Kali Linux. */
|
|
70658
|
+
isKali() {
|
|
70659
|
+
return this.linux.isKali;
|
|
70660
|
+
}
|
|
70661
|
+
/** Pretty distro name (PRETTY_NAME/NAME), e.g. "Kali GNU/Linux Rolling". */
|
|
70662
|
+
getDistroName() {
|
|
70663
|
+
return this.linux.distroName;
|
|
70664
|
+
}
|
|
70665
|
+
/** True when the session runs as uid 0 (no sudo needed). */
|
|
70666
|
+
isRoot() {
|
|
70667
|
+
return this.root;
|
|
70668
|
+
}
|
|
70669
|
+
/**
|
|
70670
|
+
* True when the native http_request tool and the recon toolkit run WITHOUT
|
|
70671
|
+
* the scope.txt allowlist gate. Defaults to ON for Linux ("tudo liberado"),
|
|
70672
|
+
* OFF elsewhere; flip with CLAWFAST_UNRESTRICTED / CLAWFAST_STRICT_SCOPE.
|
|
70673
|
+
*/
|
|
70674
|
+
isUnrestricted() {
|
|
70675
|
+
return this.unrestricted;
|
|
70676
|
+
}
|
|
70524
70677
|
supportsPty() {
|
|
70525
70678
|
return false;
|
|
70526
70679
|
}
|
|
@@ -70529,10 +70682,12 @@ var init_local_sandbox = __esm({
|
|
|
70529
70682
|
const shellInvocation = `${this.shellBin} ${this.shellFlag}`;
|
|
70530
70683
|
const windowsNotes = import_node_os3.default.platform() === "win32" ? this.getWindowsShellNotes() : "";
|
|
70531
70684
|
const pentestToolingNotes = this.getLocalPentestToolingNotes();
|
|
70532
|
-
|
|
70685
|
+
const linuxPowerNotes = this.isLinux() ? this.getLinuxPowerNotes() : "";
|
|
70686
|
+
const hostLabel = this.isLinux() ? `${this.getDistroName()} ${import_node_os3.default.arch()}, ${this.root ? "root" : "user"}@${import_node_os3.default.hostname()}` : `${platform}, hostname "${import_node_os3.default.hostname()}"`;
|
|
70687
|
+
return `You are executing commands directly on the user's local host (${hostLabel}) in DANGEROUS MODE: there is NO sandbox isolation. This is the operator's OWN machine and they have authorized you to use its FULL power.
|
|
70533
70688
|
Commands are invoked via \`${shellInvocation}\`. The working directory is "${this.workdir}".
|
|
70534
70689
|
Be careful: file system, network and process operations all affect the real host system.
|
|
70535
|
-
A real human user is present at this terminal. When a command prompts for input \u2014 a password, a y/n confirmation, an interactive installer, a REPL prompt (python, mysql, ftp), an ssh/sudo prompt, etc. \u2014 the user types the answer and it is forwarded to the command's stdin. So you MAY run commands that prompt for input; just run the command and the user will respond when asked. Prefer non-interactive flags (like --yes) when they exist and are convenient, but interactive prompts are fully supported. Only full-screen / raw-mode TUI programs (vim, top, htop, less without \`| cat\`) are unsupported, since there is no PTY \u2014 avoid those and use line-oriented alternatives.
|
|
70690
|
+
A real human user is present at this terminal. When a command prompts for input \u2014 a password, a y/n confirmation, an interactive installer, a REPL prompt (python, mysql, ftp), an ssh/sudo prompt, an account/registration step, etc. \u2014 the user types the answer and it is forwarded to the command's stdin. So you MAY run commands that prompt for input; just run the command and the user will respond when asked. Prefer non-interactive flags (like --yes) when they exist and are convenient, but interactive prompts are fully supported. Only full-screen / raw-mode TUI programs (vim, top, htop, less without \`| cat\`) are unsupported, since there is no PTY \u2014 avoid those and use line-oriented alternatives.
|
|
70536
70691
|
|
|
70537
70692
|
WORKSPACE - STRICT SCOPE:
|
|
70538
70693
|
- Your entire workspace is the directory: ${this.workdir}
|
|
@@ -70549,7 +70704,28 @@ EDITING SCRIPTS:
|
|
|
70549
70704
|
- When a script you wrote produces an error, fix the specific broken part.
|
|
70550
70705
|
- Do not recreate or fully overwrite a file just to fix one error.
|
|
70551
70706
|
- Use write only to create a brand-new file, or when changes to a short file are extensive.
|
|
70552
|
-
- Iterate on your own existing files across steps; treat them as persistent.${windowsNotes}${pentestToolingNotes}`;
|
|
70707
|
+
- Iterate on your own existing files across steps; treat them as persistent.${linuxPowerNotes}${windowsNotes}${pentestToolingNotes}`;
|
|
70708
|
+
}
|
|
70709
|
+
/**
|
|
70710
|
+
* The Linux "full-power" block: tells the model it is on a real Linux/Kali box
|
|
70711
|
+
* with no capability artificially withheld, how root/sudo works here, that
|
|
70712
|
+
* Python and scanning are fully unlocked, and — critically — the protocol for
|
|
70713
|
+
* relaying a Kali tool's interactive prompt to the human and forwarding their
|
|
70714
|
+
* typed answer back to the program's stdin.
|
|
70715
|
+
*/
|
|
70716
|
+
getLinuxPowerNotes() {
|
|
70717
|
+
const who = this.root ? "root (uid 0) \u2014 no sudo needed; you can install packages, bind low ports, sniff, and read/write anywhere" : "a normal user \u2014 prefix privileged actions with sudo; the human will type the sudo password when prompted";
|
|
70718
|
+
const apt = this.root ? "apt" : "sudo apt";
|
|
70719
|
+
const kaliLine = this.isKali() ? "This is Kali Linux: treat the full offensive toolset as installed and ready." : "Treat this as a capable Linux pentest host; install whatever you need.";
|
|
70720
|
+
return `
|
|
70721
|
+
|
|
70722
|
+
LINUX FULL-POWER MODE (${this.getDistroName()}, running as ${who}):
|
|
70723
|
+
- ${kaliLine} Everything the OS permits, you may do \u2014 no capability is off-limits because of the platform. This box belongs to the operator and full use is authorized.
|
|
70724
|
+
- You have a REAL shell with REAL reach: full filesystem, raw sockets, package managers (\`${apt} install -y <pkg>\`, \`pip install\`, \`pipx install\`, \`go install\`), service control (\`systemctl\`), and the entire installed toolset.
|
|
70725
|
+
- Python is 100% unlocked: \`python3\`, \`pip\`, \`venv\`, any library. Build and run whatever the task needs \u2014 there is no Python restriction here.
|
|
70726
|
+
- Scanning is fully allowed: port scans, host/network range sweeps, service/version detection, and number/identifier enumeration (IP ranges, port ranges, phone-number OSINT with phoneinfoga, etc.). Pick the right tool (nmap, masscan, the recon/ toolkit) and apply reasonable OPSEC throttling on noisy work.
|
|
70727
|
+
- KALI / INTERACTIVE PROMPTS \u2014 IMPORTANT: many tools and installers stop to ask for input mid-run (msfconsole, an \`apt\` confirmation, an account/registration/sign-up step, an ssh/sudo/password prompt, a y/n, a setup wizard, a REPL). A real human is at this terminal. When the underlying program is waiting for input, STOP and tell the user in ONE short line EXACTLY what the program ("o kali") is asking and what to type \u2014 e.g. "o kali est\xE1 pedindo o nome de usu\xE1rio da conta; digite ele aqui". The user types the answer here, the CLI forwards it to the program's stdin, and you continue. You drive the tool; the human supplies the human answers.
|
|
70728
|
+
- Account creation / sign-ups a tool requires: run the step, surface each prompt to the user, forward their typed answers \u2014 NEVER invent credentials, never skip the human, never fabricate the tool's output.${this.unrestricted ? "\n- UNRESTRICTED scope is ACTIVE: the native http_request tool and the recon/ Python modules run WITHOUT the scope.txt allowlist (pass `--allow-out-of-scope` to the recon scripts). Still act only against targets the operator pointed you at." : ""}`;
|
|
70553
70729
|
}
|
|
70554
70730
|
getOsContext() {
|
|
70555
70731
|
return this.getSandboxContext();
|
|
@@ -70572,6 +70748,10 @@ EDITING SCRIPTS:
|
|
|
70572
70748
|
getWorkdir() {
|
|
70573
70749
|
return this.workdir;
|
|
70574
70750
|
}
|
|
70751
|
+
/** The shell binary + exec flag this sandbox runs commands through. */
|
|
70752
|
+
getShell() {
|
|
70753
|
+
return { bin: this.shellBin, flag: this.shellFlag };
|
|
70754
|
+
}
|
|
70575
70755
|
getHost(port) {
|
|
70576
70756
|
return `localhost:${port}`;
|
|
70577
70757
|
}
|
|
@@ -70605,9 +70785,9 @@ EDITING SCRIPTS:
|
|
|
70605
70785
|
if (!this.autoCreated) return null;
|
|
70606
70786
|
const keep = (process.env.CLAWFAST_KEEP_SPRIT || "").trim().toLowerCase();
|
|
70607
70787
|
if (keep === "1" || keep === "true" || keep === "yes") return null;
|
|
70608
|
-
if (
|
|
70788
|
+
if (import_node_path8.default.basename(this.workdir).toUpperCase() !== "SPRIT") return null;
|
|
70609
70789
|
try {
|
|
70610
|
-
await
|
|
70790
|
+
await import_node_fs9.promises.rm(this.workdir, { recursive: true, force: true });
|
|
70611
70791
|
return this.workdir;
|
|
70612
70792
|
} catch {
|
|
70613
70793
|
return null;
|
|
@@ -70658,15 +70838,15 @@ EDITING SCRIPTS:
|
|
|
70658
70838
|
}
|
|
70659
70839
|
}
|
|
70660
70840
|
resolvePath(p) {
|
|
70661
|
-
if (
|
|
70662
|
-
return
|
|
70841
|
+
if (import_node_path8.default.isAbsolute(p)) return p;
|
|
70842
|
+
return import_node_path8.default.join(this.workdir, p);
|
|
70663
70843
|
}
|
|
70664
70844
|
isBashLikeShell() {
|
|
70665
|
-
const base =
|
|
70845
|
+
const base = import_node_path8.default.basename(this.shellBin).toLowerCase();
|
|
70666
70846
|
return base === "bash" || base === "bash.exe" || base === "sh";
|
|
70667
70847
|
}
|
|
70668
70848
|
isCmdShell() {
|
|
70669
|
-
const base =
|
|
70849
|
+
const base = import_node_path8.default.basename(this.shellBin).toLowerCase();
|
|
70670
70850
|
return base === "cmd" || base === "cmd.exe";
|
|
70671
70851
|
}
|
|
70672
70852
|
getWindowsShellNotes() {
|
|
@@ -70708,12 +70888,14 @@ LOCAL PENTEST TOOLING:
|
|
|
70708
70888
|
- In cmd.exe, do not use raw Bash constructs like \`export\`, \`$(pwd)\`, heredocs, or single-quote escaping. Wrap those commands with \`wsl.exe ... bash -lc "..."\` or rewrite them in cmd syntax.
|
|
70709
70889
|
- For OWASP ZAP on Windows without WSL, prefer Docker: \`docker pull ghcr.io/zaproxy/zaproxy:stable\` and use a cmd.exe volume mount like \`-v "%cd%:/zap/wrk/:rw"\`.`;
|
|
70710
70890
|
}
|
|
70891
|
+
const sudo = this.root ? "" : "sudo ";
|
|
70711
70892
|
return `
|
|
70712
70893
|
|
|
70713
|
-
LOCAL PENTEST TOOLING:
|
|
70714
|
-
-
|
|
70715
|
-
-
|
|
70716
|
-
-
|
|
70894
|
+
LOCAL PENTEST TOOLING (${this.isLinux() ? this.getDistroName() : "Linux"}):
|
|
70895
|
+
- Treat the full Kali/Debian arsenal as available; verify a specific tool with \`command -v <tool>\` and install anything missing with \`${sudo}apt update && ${sudo}apt install -y <pkg>\` (or \`pipx install\` / \`pip install\`).
|
|
70896
|
+
- Common arsenal: nmap, masscan, nikto, nuclei, sqlmap, wpscan, gobuster / ffuf / feroxbuster, hydra, john, hashcat, metasploit (\`msfconsole\`), netcat, whois, dig, dnsrecon, amass, subfinder, theHarvester, wfuzz, zaproxy, burpsuite, responder, enum4linux, smbclient, phoneinfoga.
|
|
70897
|
+
- No PTY here: prefer line-oriented invocation \u2014 \`msfconsole -q -x "use ...; set ...; run; exit"\`, \`sqlmap --batch\`, and pipe pagers through \`| cat\`. Interactive prompts still work (the human types answers), but avoid full-screen TUIs.
|
|
70898
|
+
- For OWASP ZAP / containerized scans, Docker works too: \`docker run ... -v "$(pwd):/zap/wrk/:rw" ...\` so reports are preserved.`;
|
|
70717
70899
|
}
|
|
70718
70900
|
getWslWorkdir() {
|
|
70719
70901
|
const match = this.workdir.match(/^([A-Za-z]):\/(.*)$/);
|
|
@@ -70721,6 +70903,10 @@ LOCAL PENTEST TOOLING:
|
|
|
70721
70903
|
return `/mnt/${match[1].toLowerCase()}/${match[2]}`;
|
|
70722
70904
|
}
|
|
70723
70905
|
};
|
|
70906
|
+
truthyEnv = (name25) => {
|
|
70907
|
+
const v = (process.env[name25] || "").trim().toLowerCase();
|
|
70908
|
+
return v === "1" || v === "true" || v === "yes" || v === "on";
|
|
70909
|
+
};
|
|
70724
70910
|
}
|
|
70725
70911
|
});
|
|
70726
70912
|
|
|
@@ -70763,6 +70949,577 @@ var init_local_sandbox_manager = __esm({
|
|
|
70763
70949
|
}
|
|
70764
70950
|
});
|
|
70765
70951
|
|
|
70952
|
+
// src/message-tail.ts
|
|
70953
|
+
function ensureGeneratableTail(messages, continuation = "continue") {
|
|
70954
|
+
const last = messages[messages.length - 1];
|
|
70955
|
+
if (last && last.role === "assistant") {
|
|
70956
|
+
messages.push({ role: "user", content: continuation });
|
|
70957
|
+
}
|
|
70958
|
+
return messages;
|
|
70959
|
+
}
|
|
70960
|
+
var init_message_tail = __esm({
|
|
70961
|
+
"src/message-tail.ts"() {
|
|
70962
|
+
"use strict";
|
|
70963
|
+
}
|
|
70964
|
+
});
|
|
70965
|
+
|
|
70966
|
+
// src/memory.ts
|
|
70967
|
+
function trimHistoryForSave(history, max = MAX_SAVED_MESSAGES) {
|
|
70968
|
+
let slice = history.length > max ? history.slice(-max) : history.slice();
|
|
70969
|
+
let start = 0;
|
|
70970
|
+
while (start < slice.length && slice[start].role !== "user") start++;
|
|
70971
|
+
slice = start < slice.length ? slice.slice(start) : [];
|
|
70972
|
+
return slice;
|
|
70973
|
+
}
|
|
70974
|
+
var import_node_fs10, import_node_fs11, import_node_path9, import_node_crypto, FACTS_FILE, CONVO_FILE, MAX_SAVED_MESSAGES, MAX_FACTS, MAX_RENDER_PER_KIND, memoryEnabled, slugForScope, isFact, EngagementMemory;
|
|
70975
|
+
var init_memory = __esm({
|
|
70976
|
+
"src/memory.ts"() {
|
|
70977
|
+
"use strict";
|
|
70978
|
+
import_node_fs10 = require("node:fs");
|
|
70979
|
+
import_node_fs11 = __toESM(require("node:fs"));
|
|
70980
|
+
import_node_path9 = __toESM(require("node:path"));
|
|
70981
|
+
import_node_crypto = __toESM(require("node:crypto"));
|
|
70982
|
+
init_config();
|
|
70983
|
+
FACTS_FILE = "facts.json";
|
|
70984
|
+
CONVO_FILE = "conversation.json";
|
|
70985
|
+
MAX_SAVED_MESSAGES = 80;
|
|
70986
|
+
MAX_FACTS = 500;
|
|
70987
|
+
MAX_RENDER_PER_KIND = 50;
|
|
70988
|
+
memoryEnabled = () => (process.env.CLAWFAST_MEMORY || "").trim().toLowerCase() !== "off";
|
|
70989
|
+
slugForScope = (scope) => {
|
|
70990
|
+
const base = import_node_path9.default.basename(scope).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32) || "scope";
|
|
70991
|
+
const hash2 = import_node_crypto.default.createHash("sha1").update(import_node_path9.default.resolve(scope)).digest("hex").slice(0, 8);
|
|
70992
|
+
return `${base}-${hash2}`;
|
|
70993
|
+
};
|
|
70994
|
+
isFact = (x) => !!x && typeof x.id === "string" && typeof x.kind === "string" && typeof x.title === "string";
|
|
70995
|
+
EngagementMemory = class {
|
|
70996
|
+
constructor(scope) {
|
|
70997
|
+
this.facts = [];
|
|
70998
|
+
this.seq = 0;
|
|
70999
|
+
this.enabled = memoryEnabled();
|
|
71000
|
+
this.dir = import_node_path9.default.join(clawfastHome(), "memory", slugForScope(scope));
|
|
71001
|
+
if (this.enabled) this.loadFactsSync();
|
|
71002
|
+
}
|
|
71003
|
+
loadFactsSync() {
|
|
71004
|
+
try {
|
|
71005
|
+
const parsed = JSON.parse(
|
|
71006
|
+
import_node_fs11.default.readFileSync(import_node_path9.default.join(this.dir, FACTS_FILE), "utf8")
|
|
71007
|
+
);
|
|
71008
|
+
if (Array.isArray(parsed)) {
|
|
71009
|
+
this.facts = parsed.filter(isFact);
|
|
71010
|
+
for (const f of this.facts) {
|
|
71011
|
+
const n = Number(String(f.id).replace(/^F-/, ""));
|
|
71012
|
+
if (Number.isFinite(n) && n > this.seq) this.seq = n;
|
|
71013
|
+
}
|
|
71014
|
+
}
|
|
71015
|
+
} catch {
|
|
71016
|
+
}
|
|
71017
|
+
}
|
|
71018
|
+
async persistFacts() {
|
|
71019
|
+
if (!this.enabled) return;
|
|
71020
|
+
try {
|
|
71021
|
+
await import_node_fs10.promises.mkdir(this.dir, { recursive: true });
|
|
71022
|
+
await import_node_fs10.promises.writeFile(
|
|
71023
|
+
import_node_path9.default.join(this.dir, FACTS_FILE),
|
|
71024
|
+
JSON.stringify(this.facts, null, 2),
|
|
71025
|
+
"utf8"
|
|
71026
|
+
);
|
|
71027
|
+
} catch {
|
|
71028
|
+
}
|
|
71029
|
+
}
|
|
71030
|
+
list(filter3) {
|
|
71031
|
+
return this.facts.filter(
|
|
71032
|
+
(f) => (!filter3?.kind || f.kind === filter3.kind) && (!filter3?.target || f.target === filter3.target)
|
|
71033
|
+
);
|
|
71034
|
+
}
|
|
71035
|
+
async add(input) {
|
|
71036
|
+
const key = (f) => `${f.kind}|${f.title}|${f.target || ""}`.toLowerCase();
|
|
71037
|
+
const existing = this.facts.find((f) => key(f) === key(input));
|
|
71038
|
+
if (existing) {
|
|
71039
|
+
if (input.detail !== void 0) existing.detail = input.detail;
|
|
71040
|
+
if (input.source !== void 0) existing.source = input.source;
|
|
71041
|
+
existing.ts = Date.now();
|
|
71042
|
+
await this.persistFacts();
|
|
71043
|
+
return existing;
|
|
71044
|
+
}
|
|
71045
|
+
const fact = {
|
|
71046
|
+
id: `F-${String(++this.seq).padStart(3, "0")}`,
|
|
71047
|
+
kind: input.kind,
|
|
71048
|
+
title: input.title,
|
|
71049
|
+
detail: input.detail,
|
|
71050
|
+
target: input.target,
|
|
71051
|
+
source: input.source,
|
|
71052
|
+
ts: Date.now()
|
|
71053
|
+
};
|
|
71054
|
+
this.facts.push(fact);
|
|
71055
|
+
if (this.facts.length > MAX_FACTS) {
|
|
71056
|
+
this.facts = this.facts.slice(-MAX_FACTS);
|
|
71057
|
+
}
|
|
71058
|
+
await this.persistFacts();
|
|
71059
|
+
return fact;
|
|
71060
|
+
}
|
|
71061
|
+
async update(id, patch) {
|
|
71062
|
+
const f = this.facts.find((x) => x.id === id);
|
|
71063
|
+
if (!f) return null;
|
|
71064
|
+
const target = f;
|
|
71065
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
71066
|
+
if (v !== void 0 && k !== "id") target[k] = v;
|
|
71067
|
+
}
|
|
71068
|
+
f.ts = Date.now();
|
|
71069
|
+
await this.persistFacts();
|
|
71070
|
+
return f;
|
|
71071
|
+
}
|
|
71072
|
+
async remove(id) {
|
|
71073
|
+
const before = this.facts.length;
|
|
71074
|
+
this.facts = this.facts.filter((f) => f.id !== id);
|
|
71075
|
+
if (this.facts.length === before) return false;
|
|
71076
|
+
await this.persistFacts();
|
|
71077
|
+
return true;
|
|
71078
|
+
}
|
|
71079
|
+
/** Compact `<engagement_memory>` block for the system prompt, or "" if empty. */
|
|
71080
|
+
renderSection() {
|
|
71081
|
+
if (!this.enabled || this.facts.length === 0) return "";
|
|
71082
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
71083
|
+
for (const f of this.facts) {
|
|
71084
|
+
const arr = byKind.get(f.kind) || [];
|
|
71085
|
+
arr.push(f);
|
|
71086
|
+
byKind.set(f.kind, arr);
|
|
71087
|
+
}
|
|
71088
|
+
const lines = [];
|
|
71089
|
+
for (const [kind, arr] of byKind) {
|
|
71090
|
+
lines.push(`[${kind}]`);
|
|
71091
|
+
for (const f of arr.slice(-MAX_RENDER_PER_KIND)) {
|
|
71092
|
+
lines.push(
|
|
71093
|
+
`- ${f.id} ${f.title}` + (f.target ? ` (${f.target})` : "") + (f.detail ? `: ${f.detail}` : "")
|
|
71094
|
+
);
|
|
71095
|
+
}
|
|
71096
|
+
}
|
|
71097
|
+
return `
|
|
71098
|
+
|
|
71099
|
+
<engagement_memory>
|
|
71100
|
+
Mem\xF3ria de engajamento PERSISTENTE deste alvo/projeto \u2014 sobrevive \xE0 troca de modelo E ao rein\xEDcio do CLI. Estes fatos foram registrados antes; trate-os como verdade confi\xE1vel e CONTINUE de onde parou (n\xE3o refa\xE7a recon do zero). Para gravar algo novo, use a ferramenta \`memory\` (action add).
|
|
71101
|
+
${lines.join("\n")}
|
|
71102
|
+
</engagement_memory>`;
|
|
71103
|
+
}
|
|
71104
|
+
// ── Conversation persistence ────────────────────────────────────────────────
|
|
71105
|
+
saveConversation(history) {
|
|
71106
|
+
if (!this.enabled) return;
|
|
71107
|
+
try {
|
|
71108
|
+
import_node_fs11.default.mkdirSync(this.dir, { recursive: true });
|
|
71109
|
+
import_node_fs11.default.writeFileSync(
|
|
71110
|
+
import_node_path9.default.join(this.dir, CONVO_FILE),
|
|
71111
|
+
JSON.stringify(trimHistoryForSave(history)),
|
|
71112
|
+
"utf8"
|
|
71113
|
+
);
|
|
71114
|
+
} catch {
|
|
71115
|
+
}
|
|
71116
|
+
}
|
|
71117
|
+
loadConversation() {
|
|
71118
|
+
if (!this.enabled) return [];
|
|
71119
|
+
try {
|
|
71120
|
+
const parsed = JSON.parse(
|
|
71121
|
+
import_node_fs11.default.readFileSync(import_node_path9.default.join(this.dir, CONVO_FILE), "utf8")
|
|
71122
|
+
);
|
|
71123
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
71124
|
+
} catch {
|
|
71125
|
+
return [];
|
|
71126
|
+
}
|
|
71127
|
+
}
|
|
71128
|
+
clearConversation() {
|
|
71129
|
+
try {
|
|
71130
|
+
import_node_fs11.default.rmSync(import_node_path9.default.join(this.dir, CONVO_FILE), { force: true });
|
|
71131
|
+
} catch {
|
|
71132
|
+
}
|
|
71133
|
+
}
|
|
71134
|
+
};
|
|
71135
|
+
}
|
|
71136
|
+
});
|
|
71137
|
+
|
|
71138
|
+
// src/tools/memory-tool.ts
|
|
71139
|
+
var createMemory;
|
|
71140
|
+
var init_memory_tool = __esm({
|
|
71141
|
+
"src/tools/memory-tool.ts"() {
|
|
71142
|
+
"use strict";
|
|
71143
|
+
init_dist5();
|
|
71144
|
+
init_zod();
|
|
71145
|
+
createMemory = (deps) => {
|
|
71146
|
+
const { memory } = deps;
|
|
71147
|
+
return tool({
|
|
71148
|
+
description: `Persistent engagement memory for THIS target/project. Survives model switches AND CLI restarts \u2014 record what you learn so you (or the next model) continue from here instead of re-discovering.
|
|
71149
|
+
|
|
71150
|
+
WHAT TO STORE (action add): hosts/IPs, open services + versions, credentials/tokens, confirmed vulns, tech stack, and especially what WORKED or what was BLOCKED (WAF, rate limit, auth). kind + title are required; detail/target/source optional.
|
|
71151
|
+
|
|
71152
|
+
ACTIONS:
|
|
71153
|
+
- add \u2014 store a fact.
|
|
71154
|
+
- update \u2014 change a fact by id.
|
|
71155
|
+
- list \u2014 read stored facts (optional kind/target filter). Do this before re-running recon you may have already done.
|
|
71156
|
+
- forget \u2014 remove a fact by id.`,
|
|
71157
|
+
inputSchema: external_exports.object({
|
|
71158
|
+
brief: external_exports.string().describe("A one-sentence preamble describing the purpose of this call."),
|
|
71159
|
+
action: external_exports.enum(["add", "update", "list", "forget"]),
|
|
71160
|
+
kind: external_exports.string().optional().describe("host|service|cred|vuln|tech|note|url|\u2026 (for add, or list filter)"),
|
|
71161
|
+
title: external_exports.string().optional().describe("Short label (required for add)."),
|
|
71162
|
+
detail: external_exports.string().optional().describe("Longer detail / evidence."),
|
|
71163
|
+
target: external_exports.string().optional().describe("Host/URL this fact is about."),
|
|
71164
|
+
source: external_exports.string().optional().describe("Where it came from."),
|
|
71165
|
+
id: external_exports.string().optional().describe("Fact id (required for update/forget).")
|
|
71166
|
+
}),
|
|
71167
|
+
execute: async (input) => {
|
|
71168
|
+
try {
|
|
71169
|
+
switch (input.action) {
|
|
71170
|
+
case "add": {
|
|
71171
|
+
if (!input.kind || !input.title) {
|
|
71172
|
+
return "Erro: 'kind' e 'title' s\xE3o obrigat\xF3rios para add.";
|
|
71173
|
+
}
|
|
71174
|
+
const f = await memory.add({
|
|
71175
|
+
kind: input.kind,
|
|
71176
|
+
title: input.title,
|
|
71177
|
+
detail: input.detail,
|
|
71178
|
+
target: input.target,
|
|
71179
|
+
source: input.source
|
|
71180
|
+
});
|
|
71181
|
+
return `Mem\xF3ria gravada: ${f.id} [${f.kind}] ${f.title}`;
|
|
71182
|
+
}
|
|
71183
|
+
case "update": {
|
|
71184
|
+
if (!input.id) return "Erro: 'id' \xE9 obrigat\xF3rio para update.";
|
|
71185
|
+
const f = await memory.update(input.id, {
|
|
71186
|
+
kind: input.kind,
|
|
71187
|
+
title: input.title,
|
|
71188
|
+
detail: input.detail,
|
|
71189
|
+
target: input.target,
|
|
71190
|
+
source: input.source
|
|
71191
|
+
});
|
|
71192
|
+
return f ? `Atualizado: ${f.id}` : `N\xE3o achei o fato ${input.id}.`;
|
|
71193
|
+
}
|
|
71194
|
+
case "forget": {
|
|
71195
|
+
if (!input.id) return "Erro: 'id' \xE9 obrigat\xF3rio para forget.";
|
|
71196
|
+
return await memory.remove(input.id) ? `Removido: ${input.id}` : `N\xE3o achei o fato ${input.id}.`;
|
|
71197
|
+
}
|
|
71198
|
+
case "list":
|
|
71199
|
+
default: {
|
|
71200
|
+
const facts = memory.list({
|
|
71201
|
+
kind: input.kind,
|
|
71202
|
+
target: input.target
|
|
71203
|
+
});
|
|
71204
|
+
if (!facts.length) {
|
|
71205
|
+
return "Mem\xF3ria vazia (nenhum fato gravado ainda).";
|
|
71206
|
+
}
|
|
71207
|
+
return facts.map(
|
|
71208
|
+
(f) => `${f.id} [${f.kind}] ${f.title}` + (f.target ? ` (${f.target})` : "") + (f.detail ? `: ${f.detail}` : "")
|
|
71209
|
+
).join("\n");
|
|
71210
|
+
}
|
|
71211
|
+
}
|
|
71212
|
+
} catch (err) {
|
|
71213
|
+
return `Erro na mem\xF3ria: ${err instanceof Error ? err.message : String(err)}`;
|
|
71214
|
+
}
|
|
71215
|
+
}
|
|
71216
|
+
});
|
|
71217
|
+
};
|
|
71218
|
+
}
|
|
71219
|
+
});
|
|
71220
|
+
|
|
71221
|
+
// src/sessions.ts
|
|
71222
|
+
function appendRing(text2, cursor, chunk, max = MAX_BUFFER_CHARS) {
|
|
71223
|
+
let next = text2 + chunk;
|
|
71224
|
+
if (next.length <= max) return { text: next, cursor, dropped: false };
|
|
71225
|
+
const drop = next.length - max;
|
|
71226
|
+
next = next.slice(drop);
|
|
71227
|
+
return { text: next, cursor: Math.max(0, cursor - drop), dropped: true };
|
|
71228
|
+
}
|
|
71229
|
+
var import_node_child_process3, import_node_os4, MAX_SESSIONS, MAX_BUFFER_CHARS, SESSION_IDLE_MS, SESSION_LIFETIME_MS, shortId, sleep3, LocalSessionManager;
|
|
71230
|
+
var init_sessions = __esm({
|
|
71231
|
+
"src/sessions.ts"() {
|
|
71232
|
+
"use strict";
|
|
71233
|
+
import_node_child_process3 = require("node:child_process");
|
|
71234
|
+
import_node_os4 = __toESM(require("node:os"));
|
|
71235
|
+
MAX_SESSIONS = 10;
|
|
71236
|
+
MAX_BUFFER_CHARS = 2e5;
|
|
71237
|
+
SESSION_IDLE_MS = 15 * 6e4;
|
|
71238
|
+
SESSION_LIFETIME_MS = 60 * 6e4;
|
|
71239
|
+
shortId = (taken) => {
|
|
71240
|
+
for (let i = 0; i < 6; i++) {
|
|
71241
|
+
const id = Math.random().toString(36).slice(2, 8);
|
|
71242
|
+
if (!taken.has(id)) return id;
|
|
71243
|
+
}
|
|
71244
|
+
return `${Date.now().toString(36).slice(-6)}`;
|
|
71245
|
+
};
|
|
71246
|
+
sleep3 = (ms, signal) => new Promise((resolve2) => {
|
|
71247
|
+
if (ms <= 0 || signal?.aborted) return resolve2();
|
|
71248
|
+
const t = setTimeout(resolve2, ms);
|
|
71249
|
+
signal?.addEventListener(
|
|
71250
|
+
"abort",
|
|
71251
|
+
() => {
|
|
71252
|
+
clearTimeout(t);
|
|
71253
|
+
resolve2();
|
|
71254
|
+
},
|
|
71255
|
+
{ once: true }
|
|
71256
|
+
);
|
|
71257
|
+
});
|
|
71258
|
+
LocalSessionManager = class {
|
|
71259
|
+
constructor(opts) {
|
|
71260
|
+
this.opts = opts;
|
|
71261
|
+
this.sessions = /* @__PURE__ */ new Map();
|
|
71262
|
+
}
|
|
71263
|
+
/**
|
|
71264
|
+
* Open a session. With `command`, the program runs through the shell
|
|
71265
|
+
* (`shell -c "<command>"`) with its stdin held open. Without it, a bare
|
|
71266
|
+
* interactive shell is spawned. Returns the new session id.
|
|
71267
|
+
*/
|
|
71268
|
+
open(command, cwd) {
|
|
71269
|
+
if (this.sessions.size >= MAX_SESSIONS) {
|
|
71270
|
+
return { id: "", error: `limite de ${MAX_SESSIONS} sess\xF5es abertas atingido \u2014 feche alguma antes.` };
|
|
71271
|
+
}
|
|
71272
|
+
const id = shortId(new Set(this.sessions.keys()));
|
|
71273
|
+
const args = command ? [this.opts.shellFlag, command] : [];
|
|
71274
|
+
let child;
|
|
71275
|
+
try {
|
|
71276
|
+
child = (0, import_node_child_process3.spawn)(this.opts.shell, args, {
|
|
71277
|
+
cwd: cwd || this.opts.cwd,
|
|
71278
|
+
env: process.env,
|
|
71279
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
71280
|
+
windowsHide: true
|
|
71281
|
+
});
|
|
71282
|
+
} catch (err) {
|
|
71283
|
+
return { id: "", error: err instanceof Error ? err.message : String(err) };
|
|
71284
|
+
}
|
|
71285
|
+
const now2 = Date.now();
|
|
71286
|
+
const session = {
|
|
71287
|
+
id,
|
|
71288
|
+
command: command || `${this.opts.shell} (shell interativo)`,
|
|
71289
|
+
child,
|
|
71290
|
+
output: "",
|
|
71291
|
+
readCursor: 0,
|
|
71292
|
+
truncated: false,
|
|
71293
|
+
createdAt: now2,
|
|
71294
|
+
lastActivity: now2,
|
|
71295
|
+
alive: true,
|
|
71296
|
+
exitCode: null,
|
|
71297
|
+
idleTimer: null,
|
|
71298
|
+
lifetimeTimer: null
|
|
71299
|
+
};
|
|
71300
|
+
const onChunk = (d) => this.append(session, d.toString());
|
|
71301
|
+
child.stdout?.on("data", onChunk);
|
|
71302
|
+
child.stderr?.on("data", onChunk);
|
|
71303
|
+
child.on("error", (err) => {
|
|
71304
|
+
this.append(session, `
|
|
71305
|
+
[erro do processo: ${err.message}]
|
|
71306
|
+
`);
|
|
71307
|
+
});
|
|
71308
|
+
child.on("close", (code, sig) => {
|
|
71309
|
+
session.alive = false;
|
|
71310
|
+
session.exitCode = code != null ? code : sig ? 137 : null;
|
|
71311
|
+
this.clearTimers(session);
|
|
71312
|
+
});
|
|
71313
|
+
this.armIdle(session);
|
|
71314
|
+
session.lifetimeTimer = setTimeout(
|
|
71315
|
+
() => this.close(id),
|
|
71316
|
+
SESSION_LIFETIME_MS
|
|
71317
|
+
);
|
|
71318
|
+
session.lifetimeTimer.unref?.();
|
|
71319
|
+
this.sessions.set(id, session);
|
|
71320
|
+
return { id };
|
|
71321
|
+
}
|
|
71322
|
+
/** Write input to a session's stdin (appends a newline unless `enter` is false). */
|
|
71323
|
+
send(id, data, enter = true) {
|
|
71324
|
+
const s = this.sessions.get(id);
|
|
71325
|
+
if (!s) return { ok: false, error: `sess\xE3o ${id} n\xE3o existe.` };
|
|
71326
|
+
if (!s.alive || !s.child.stdin?.writable) {
|
|
71327
|
+
return { ok: false, error: `sess\xE3o ${id} n\xE3o est\xE1 mais ativa.` };
|
|
71328
|
+
}
|
|
71329
|
+
try {
|
|
71330
|
+
s.child.stdin.write(enter && !data.endsWith("\n") ? `${data}
|
|
71331
|
+
` : data);
|
|
71332
|
+
s.lastActivity = Date.now();
|
|
71333
|
+
this.armIdle(s);
|
|
71334
|
+
return { ok: true };
|
|
71335
|
+
} catch (err) {
|
|
71336
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
71337
|
+
}
|
|
71338
|
+
}
|
|
71339
|
+
/**
|
|
71340
|
+
* Read new output, waiting until it goes quiet (no new bytes for `quietMs`)
|
|
71341
|
+
* or `maxMs` elapses, or the process exits. Returns the delta since the last
|
|
71342
|
+
* read.
|
|
71343
|
+
*/
|
|
71344
|
+
async readUntilQuiet(id, opts = {}) {
|
|
71345
|
+
const s = this.sessions.get(id);
|
|
71346
|
+
if (!s) return `[sess\xE3o ${id} n\xE3o existe]`;
|
|
71347
|
+
const quietMs = opts.quietMs ?? 500;
|
|
71348
|
+
const maxMs = opts.maxMs ?? 15e3;
|
|
71349
|
+
const start = Date.now();
|
|
71350
|
+
let lastLen = s.output.length;
|
|
71351
|
+
while (Date.now() - start < maxMs) {
|
|
71352
|
+
await sleep3(quietMs, opts.signal);
|
|
71353
|
+
if (opts.signal?.aborted) break;
|
|
71354
|
+
if (!s.alive) break;
|
|
71355
|
+
if (s.output.length === lastLen) break;
|
|
71356
|
+
lastLen = s.output.length;
|
|
71357
|
+
}
|
|
71358
|
+
return this.readDelta(id);
|
|
71359
|
+
}
|
|
71360
|
+
/** Return output appended since the last read, advancing the cursor. */
|
|
71361
|
+
readDelta(id) {
|
|
71362
|
+
const s = this.sessions.get(id);
|
|
71363
|
+
if (!s) return `[sess\xE3o ${id} n\xE3o existe]`;
|
|
71364
|
+
const delta = s.output.slice(s.readCursor);
|
|
71365
|
+
s.readCursor = s.output.length;
|
|
71366
|
+
return delta;
|
|
71367
|
+
}
|
|
71368
|
+
info(id) {
|
|
71369
|
+
const s = this.sessions.get(id);
|
|
71370
|
+
if (!s) return null;
|
|
71371
|
+
return {
|
|
71372
|
+
id: s.id,
|
|
71373
|
+
command: s.command,
|
|
71374
|
+
alive: s.alive,
|
|
71375
|
+
exitCode: s.exitCode,
|
|
71376
|
+
ageMs: Date.now() - s.createdAt,
|
|
71377
|
+
truncated: s.truncated
|
|
71378
|
+
};
|
|
71379
|
+
}
|
|
71380
|
+
list() {
|
|
71381
|
+
return [...this.sessions.keys()].map((id) => this.info(id)).filter((x) => x !== null);
|
|
71382
|
+
}
|
|
71383
|
+
close(id) {
|
|
71384
|
+
const s = this.sessions.get(id);
|
|
71385
|
+
if (!s) return false;
|
|
71386
|
+
this.clearTimers(s);
|
|
71387
|
+
this.killTree(s.child);
|
|
71388
|
+
this.sessions.delete(id);
|
|
71389
|
+
return true;
|
|
71390
|
+
}
|
|
71391
|
+
closeAll() {
|
|
71392
|
+
for (const id of [...this.sessions.keys()]) this.close(id);
|
|
71393
|
+
}
|
|
71394
|
+
append(s, chunk) {
|
|
71395
|
+
const r2 = appendRing(s.output, s.readCursor, chunk);
|
|
71396
|
+
s.output = r2.text;
|
|
71397
|
+
s.readCursor = r2.cursor;
|
|
71398
|
+
if (r2.dropped) s.truncated = true;
|
|
71399
|
+
s.lastActivity = Date.now();
|
|
71400
|
+
this.armIdle(s);
|
|
71401
|
+
}
|
|
71402
|
+
armIdle(s) {
|
|
71403
|
+
if (s.idleTimer) clearTimeout(s.idleTimer);
|
|
71404
|
+
s.idleTimer = setTimeout(() => this.close(s.id), SESSION_IDLE_MS);
|
|
71405
|
+
s.idleTimer.unref?.();
|
|
71406
|
+
}
|
|
71407
|
+
clearTimers(s) {
|
|
71408
|
+
if (s.idleTimer) clearTimeout(s.idleTimer);
|
|
71409
|
+
if (s.lifetimeTimer) clearTimeout(s.lifetimeTimer);
|
|
71410
|
+
s.idleTimer = null;
|
|
71411
|
+
s.lifetimeTimer = null;
|
|
71412
|
+
}
|
|
71413
|
+
killTree(child) {
|
|
71414
|
+
const pid = child.pid;
|
|
71415
|
+
if (pid && import_node_os4.default.platform() === "win32") {
|
|
71416
|
+
try {
|
|
71417
|
+
(0, import_node_child_process3.spawn)("taskkill", ["/PID", String(pid), "/T", "/F"], {
|
|
71418
|
+
windowsHide: true
|
|
71419
|
+
});
|
|
71420
|
+
return;
|
|
71421
|
+
} catch {
|
|
71422
|
+
}
|
|
71423
|
+
}
|
|
71424
|
+
try {
|
|
71425
|
+
child.kill("SIGTERM");
|
|
71426
|
+
} catch {
|
|
71427
|
+
}
|
|
71428
|
+
}
|
|
71429
|
+
};
|
|
71430
|
+
}
|
|
71431
|
+
});
|
|
71432
|
+
|
|
71433
|
+
// src/tools/session-tool.ts
|
|
71434
|
+
var aliveTag, createTerminalSession;
|
|
71435
|
+
var init_session_tool = __esm({
|
|
71436
|
+
"src/tools/session-tool.ts"() {
|
|
71437
|
+
"use strict";
|
|
71438
|
+
init_dist5();
|
|
71439
|
+
init_zod();
|
|
71440
|
+
aliveTag = (alive, exitCode) => alive ? "[sess\xE3o ativa]" : `[sess\xE3o encerrada${exitCode != null ? `, exit ${exitCode}` : ""}]`;
|
|
71441
|
+
createTerminalSession = (deps) => {
|
|
71442
|
+
const { sessions } = deps;
|
|
71443
|
+
return tool({
|
|
71444
|
+
description: `Drive a PERSISTENT interactive process that stays alive across calls (unlike run_terminal_cmd, a fresh shell each time). Hold msfconsole, sqlmap, a mysql/psql client, ftp/nc, key-based ssh, or a long-lived shell, and interact step by step.
|
|
71445
|
+
|
|
71446
|
+
ACTIONS:
|
|
71447
|
+
- open \u2014 start a session. With \`command\` it runs that program (e.g. "msfconsole -q", "mysql -h 10.0.0.5 -u root", "nc -lvnp 4444"); without it you get an interactive shell. Returns a session id + initial output.
|
|
71448
|
+
- send \u2014 send \`input\` to the session's stdin (a newline is added unless enter=false), then return the new output once it settles.
|
|
71449
|
+
- read \u2014 poll for new output of a running/long command (returns the delta since last read + whether it's still alive).
|
|
71450
|
+
- list \u2014 list open sessions.
|
|
71451
|
+
- close \u2014 terminate a session.
|
|
71452
|
+
|
|
71453
|
+
NO REAL TTY: programs that read a secret straight from /dev/tty (interactive ssh/sudo passwords, full-screen TUIs) won't see piped input \u2014 use sshpass, \`sudo -S\` (reads stdin), key auth, or non-interactive flags. Most CLIs (msfconsole, sqlmap, db clients, nc, ftp) work fine.`,
|
|
71454
|
+
inputSchema: external_exports.object({
|
|
71455
|
+
brief: external_exports.string().describe("A one-sentence preamble describing the purpose."),
|
|
71456
|
+
action: external_exports.enum(["open", "send", "read", "list", "close"]),
|
|
71457
|
+
id: external_exports.string().optional().describe("Session id (for send/read/close)."),
|
|
71458
|
+
command: external_exports.string().optional().describe("Program to launch (for open). Omit for an interactive shell."),
|
|
71459
|
+
input: external_exports.string().optional().describe("Text to send to stdin (for send)."),
|
|
71460
|
+
enter: external_exports.boolean().optional().describe("Append a newline after input (default true)."),
|
|
71461
|
+
timeout: external_exports.number().optional().describe("Max seconds to wait for output to settle (send/read; default 15).")
|
|
71462
|
+
}),
|
|
71463
|
+
execute: async (input, { abortSignal } = {}) => {
|
|
71464
|
+
const maxMs = Math.max(1, input.timeout ?? 15) * 1e3;
|
|
71465
|
+
switch (input.action) {
|
|
71466
|
+
case "open": {
|
|
71467
|
+
const res = sessions.open(input.command);
|
|
71468
|
+
if (res.error) return `Erro ao abrir sess\xE3o: ${res.error}`;
|
|
71469
|
+
const out3 = await sessions.readUntilQuiet(res.id, {
|
|
71470
|
+
quietMs: 500,
|
|
71471
|
+
maxMs: Math.min(maxMs, 8e3),
|
|
71472
|
+
signal: abortSignal
|
|
71473
|
+
});
|
|
71474
|
+
const info = sessions.info(res.id);
|
|
71475
|
+
return `Sess\xE3o aberta: ${res.id}
|
|
71476
|
+
${aliveTag(info?.alive ?? true, info?.exitCode ?? null)}
|
|
71477
|
+
${out3 || "(sem sa\xEDda inicial)"}`;
|
|
71478
|
+
}
|
|
71479
|
+
case "send": {
|
|
71480
|
+
if (!input.id) return "Erro: 'id' \xE9 obrigat\xF3rio para send.";
|
|
71481
|
+
if (input.input == null) return "Erro: 'input' \xE9 obrigat\xF3rio para send.";
|
|
71482
|
+
const sent = sessions.send(input.id, input.input, input.enter ?? true);
|
|
71483
|
+
if (!sent.ok) return `Erro ao enviar: ${sent.error}`;
|
|
71484
|
+
const out3 = await sessions.readUntilQuiet(input.id, {
|
|
71485
|
+
quietMs: 500,
|
|
71486
|
+
maxMs,
|
|
71487
|
+
signal: abortSignal
|
|
71488
|
+
});
|
|
71489
|
+
const info = sessions.info(input.id);
|
|
71490
|
+
return `${aliveTag(info?.alive ?? false, info?.exitCode ?? null)}${info?.truncated ? " [sa\xEDda truncada \u2014 buffer cheio]" : ""}
|
|
71491
|
+
${out3 || "(sem nova sa\xEDda)"}`;
|
|
71492
|
+
}
|
|
71493
|
+
case "read": {
|
|
71494
|
+
if (!input.id) return "Erro: 'id' \xE9 obrigat\xF3rio para read.";
|
|
71495
|
+
const out3 = await sessions.readUntilQuiet(input.id, {
|
|
71496
|
+
quietMs: 600,
|
|
71497
|
+
maxMs,
|
|
71498
|
+
signal: abortSignal
|
|
71499
|
+
});
|
|
71500
|
+
const info = sessions.info(input.id);
|
|
71501
|
+
if (!info) return `Sess\xE3o ${input.id} n\xE3o existe.`;
|
|
71502
|
+
return `${aliveTag(info.alive, info.exitCode)}
|
|
71503
|
+
${out3 || "(sem nova sa\xEDda)"}`;
|
|
71504
|
+
}
|
|
71505
|
+
case "list": {
|
|
71506
|
+
const list = sessions.list();
|
|
71507
|
+
if (!list.length) return "Nenhuma sess\xE3o aberta.";
|
|
71508
|
+
return list.map(
|
|
71509
|
+
(s) => `${s.id} \u2014 ${s.command} \u2014 ${aliveTag(s.alive, s.exitCode)} \u2014 ${Math.round(s.ageMs / 1e3)}s`
|
|
71510
|
+
).join("\n");
|
|
71511
|
+
}
|
|
71512
|
+
case "close": {
|
|
71513
|
+
if (!input.id) return "Erro: 'id' \xE9 obrigat\xF3rio para close.";
|
|
71514
|
+
return sessions.close(input.id) ? `Sess\xE3o ${input.id} encerrada.` : `Sess\xE3o ${input.id} n\xE3o existe.`;
|
|
71515
|
+
}
|
|
71516
|
+
}
|
|
71517
|
+
}
|
|
71518
|
+
});
|
|
71519
|
+
};
|
|
71520
|
+
}
|
|
71521
|
+
});
|
|
71522
|
+
|
|
70766
71523
|
// src/render.ts
|
|
70767
71524
|
function createRenderer() {
|
|
70768
71525
|
let lastKind = null;
|
|
@@ -70833,11 +71590,11 @@ function formatToolCall(toolName, input) {
|
|
|
70833
71590
|
case "run_terminal_cmd": {
|
|
70834
71591
|
const cmd = String(i.command ?? "").trim();
|
|
70835
71592
|
const bg = i.is_background ? `${C3.dim} (background)${C3.reset}` : "";
|
|
70836
|
-
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}exec${C3.reset} ${C3.bold}${
|
|
71593
|
+
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}exec${C3.reset} ${C3.bold}${truncate4(cmd, 400)}${C3.reset}${bg}`;
|
|
70837
71594
|
}
|
|
70838
71595
|
case "file": {
|
|
70839
|
-
const
|
|
70840
|
-
const brief = i.brief ? `${C3.dim} \u2014 ${
|
|
71596
|
+
const path14 = String(i.path ?? "");
|
|
71597
|
+
const brief = i.brief ? `${C3.dim} \u2014 ${truncate4(String(i.brief))}${C3.reset}` : "";
|
|
70841
71598
|
const map2 = {
|
|
70842
71599
|
write: [C3.green, "criar "],
|
|
70843
71600
|
edit: [C3.yellow, "editar "],
|
|
@@ -70850,7 +71607,7 @@ function formatToolCall(toolName, input) {
|
|
|
70850
71607
|
C3.blue,
|
|
70851
71608
|
action ? `${action} `.padEnd(7) : "arquivo"
|
|
70852
71609
|
];
|
|
70853
|
-
const target =
|
|
71610
|
+
const target = path14 ? `${C3.bold}${path14}${C3.reset}` : "";
|
|
70854
71611
|
return ` ${col}\u276F${C3.reset} ${col}${verb}${C3.reset} ${target}${brief}`;
|
|
70855
71612
|
}
|
|
70856
71613
|
case "todo_write":
|
|
@@ -70860,23 +71617,40 @@ function formatToolCall(toolName, input) {
|
|
|
70860
71617
|
const url2 = String(i.url ?? "");
|
|
70861
71618
|
const fuzz = Array.isArray(i.fuzz) && i.fuzz.length;
|
|
70862
71619
|
const tag = fuzz ? `${C3.dim} (fuzz \xD7${i.fuzz.length})${C3.reset}` : "";
|
|
70863
|
-
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}http${C3.reset} ${C3.bold}${method}${C3.reset} ${
|
|
71620
|
+
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}http${C3.reset} ${C3.bold}${method}${C3.reset} ${truncate4(url2, 360)}${tag}`;
|
|
70864
71621
|
}
|
|
70865
71622
|
case "findings": {
|
|
70866
71623
|
const action = String(i.action ?? "");
|
|
70867
|
-
const detail = action === "add" ?
|
|
71624
|
+
const detail = action === "add" ? truncate4(String(i.title ?? ""), 80) : action === "update" ? `${String(i.id ?? "")} ${String(i.status ?? "")}`.trim() : "";
|
|
70868
71625
|
return ` ${C3.magenta}\u276F${C3.reset} ${C3.magenta}findings${C3.reset} ${C3.bold}${action}${C3.reset}${detail ? ` ${C3.dim}${detail}${C3.reset}` : ""}`;
|
|
70869
71626
|
}
|
|
71627
|
+
case "terminal_session": {
|
|
71628
|
+
const action = String(i.action ?? "");
|
|
71629
|
+
const detail = action === "open" ? truncate4(String(i.command ?? "shell"), 120) : action === "send" ? truncate4(String(i.input ?? ""), 120) : String(i.id ?? "");
|
|
71630
|
+
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}sess\xE3o${C3.reset} ${C3.bold}${action}${C3.reset}${detail ? ` ${C3.dim}${detail}${C3.reset}` : ""}`;
|
|
71631
|
+
}
|
|
71632
|
+
case "memory": {
|
|
71633
|
+
const action = String(i.action ?? "");
|
|
71634
|
+
const detail = action === "add" || action === "update" ? truncate4(String(i.title ?? i.id ?? ""), 80) : action === "forget" ? String(i.id ?? "") : String(i.kind ?? i.target ?? "");
|
|
71635
|
+
return ` ${C3.magenta}\u276F${C3.reset} ${C3.magenta}mem\xF3ria${C3.reset} ${C3.bold}${action}${C3.reset}${detail ? ` ${C3.dim}${detail}${C3.reset}` : ""}`;
|
|
71636
|
+
}
|
|
70870
71637
|
case "web_search": {
|
|
70871
71638
|
const queries = Array.isArray(i.queries) ? i.queries.join(" | ") : "";
|
|
70872
|
-
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}busca${C3.reset} ${C3.dim}${
|
|
71639
|
+
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}busca${C3.reset} ${C3.dim}${truncate4(queries, 200)}${C3.reset}`;
|
|
70873
71640
|
}
|
|
70874
71641
|
case "open_url": {
|
|
70875
71642
|
const url2 = String(i.url ?? i.urls ?? "");
|
|
70876
|
-
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}abrir${C3.reset} ${C3.dim}${
|
|
71643
|
+
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}abrir${C3.reset} ${C3.dim}${truncate4(url2, 280)}${C3.reset}`;
|
|
71644
|
+
}
|
|
71645
|
+
case "deep_research": {
|
|
71646
|
+
const qs = Array.isArray(i.queries) ? i.queries.join(" | ") : "";
|
|
71647
|
+
const nUrls = Array.isArray(i.urls) ? i.urls.length : 0;
|
|
71648
|
+
const pages = i.max_pages != null ? `${i.max_pages}p` : "";
|
|
71649
|
+
const detail = [qs, nUrls ? `${nUrls} url(s)` : "", pages].filter(Boolean).join(" ") || "(sem alvo)";
|
|
71650
|
+
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}pesquisa${C3.reset} ${C3.dim}${truncate4(detail, 220)}${C3.reset}`;
|
|
70877
71651
|
}
|
|
70878
71652
|
default:
|
|
70879
|
-
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}${toolName}${C3.reset} ${C3.dim}${
|
|
71653
|
+
return ` ${C3.cyan}\u276F${C3.reset} ${C3.cyan}${toolName}${C3.reset} ${C3.dim}${truncate4(
|
|
70880
71654
|
JSON.stringify(i),
|
|
70881
71655
|
200
|
|
70882
71656
|
)}${C3.reset}`;
|
|
@@ -70887,7 +71661,7 @@ function summarizeResult(toolName, output) {
|
|
|
70887
71661
|
if (toolName === "file") {
|
|
70888
71662
|
if (typeof output === "object" && "error" in output) {
|
|
70889
71663
|
return {
|
|
70890
|
-
text:
|
|
71664
|
+
text: truncate4(String(output.error), 160),
|
|
70891
71665
|
error: true
|
|
70892
71666
|
};
|
|
70893
71667
|
}
|
|
@@ -70898,11 +71672,11 @@ function summarizeResult(toolName, output) {
|
|
|
70898
71672
|
if (toolName === "http_request") {
|
|
70899
71673
|
const o = asRecord(output);
|
|
70900
71674
|
if ("error" in o && o.error) {
|
|
70901
|
-
return { text:
|
|
71675
|
+
return { text: truncate4(String(o.error), 200), error: true };
|
|
70902
71676
|
}
|
|
70903
71677
|
if (o.single) {
|
|
70904
71678
|
const s = asRecord(o.single);
|
|
70905
|
-
if (s.error) return { text:
|
|
71679
|
+
if (s.error) return { text: truncate4(String(s.error), 160), error: true };
|
|
70906
71680
|
return {
|
|
70907
71681
|
text: `HTTP ${s.status} \xB7 ${s.bodyLength} bytes \xB7 ${s.timeMs}ms`,
|
|
70908
71682
|
error: false
|
|
@@ -70917,17 +71691,19 @@ function summarizeResult(toolName, output) {
|
|
|
70917
71691
|
if (toolName === "findings") {
|
|
70918
71692
|
const o = asRecord(output);
|
|
70919
71693
|
if ("error" in o && o.error) {
|
|
70920
|
-
return { text:
|
|
71694
|
+
return { text: truncate4(String(o.error), 200), error: true };
|
|
70921
71695
|
}
|
|
70922
71696
|
if (o.added) return { text: "finding registrado", error: false };
|
|
70923
71697
|
if (o.updated) return { text: "finding atualizado", error: false };
|
|
70924
|
-
if (o.report)
|
|
70925
|
-
|
|
71698
|
+
if (o.report)
|
|
71699
|
+
return { text: `relat\xF3rio \u2192 ${String(o.report)}`, error: false };
|
|
71700
|
+
if ("total" in o)
|
|
71701
|
+
return { text: `${String(o.total)} finding(s)`, error: false };
|
|
70926
71702
|
return null;
|
|
70927
71703
|
}
|
|
70928
71704
|
return null;
|
|
70929
71705
|
}
|
|
70930
|
-
var VIOLET, C3, GUTTER_BAR, out,
|
|
71706
|
+
var VIOLET, C3, GUTTER_BAR, out, truncate4;
|
|
70931
71707
|
var init_render = __esm({
|
|
70932
71708
|
"src/render.ts"() {
|
|
70933
71709
|
"use strict";
|
|
@@ -70946,7 +71722,7 @@ var init_render = __esm({
|
|
|
70946
71722
|
};
|
|
70947
71723
|
GUTTER_BAR = `${fg(PAL.frame)}\u2502${C3.reset} `;
|
|
70948
71724
|
out = (s) => process.stdout.write(s);
|
|
70949
|
-
|
|
71725
|
+
truncate4 = (s, n = 200) => s.length > n ? `${s.slice(0, n)}...` : s;
|
|
70950
71726
|
}
|
|
70951
71727
|
});
|
|
70952
71728
|
|
|
@@ -71032,7 +71808,7 @@ function killProxy(child) {
|
|
|
71032
71808
|
started.delete(child);
|
|
71033
71809
|
try {
|
|
71034
71810
|
if (process.platform === "win32") {
|
|
71035
|
-
(0,
|
|
71811
|
+
(0, import_node_child_process4.spawnSync)("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
|
|
71036
71812
|
stdio: "ignore"
|
|
71037
71813
|
});
|
|
71038
71814
|
} else {
|
|
@@ -71063,12 +71839,12 @@ async function ensureProxyReady(id, log2) {
|
|
|
71063
71839
|
};
|
|
71064
71840
|
}
|
|
71065
71841
|
let freshSetup = false;
|
|
71066
|
-
if (!(0,
|
|
71842
|
+
if (!(0, import_node_fs12.existsSync)(dir)) {
|
|
71067
71843
|
log2(`${def.label}: baixando o proxy (uma vez) em ${dir} \u2026`);
|
|
71068
71844
|
const root = proxiesRoot();
|
|
71069
|
-
(0,
|
|
71845
|
+
(0, import_node_fs12.mkdirSync)(root, { recursive: true });
|
|
71070
71846
|
const cloned = run("git", ["clone", def.repoUrl, dir], root);
|
|
71071
|
-
if (!cloned.ok || !(0,
|
|
71847
|
+
if (!cloned.ok || !(0, import_node_fs12.existsSync)(dir)) {
|
|
71072
71848
|
return {
|
|
71073
71849
|
ok: false,
|
|
71074
71850
|
message: `${def.label}: falha ao clonar o proxy (git).`
|
|
@@ -71076,7 +71852,7 @@ async function ensureProxyReady(id, log2) {
|
|
|
71076
71852
|
}
|
|
71077
71853
|
freshSetup = true;
|
|
71078
71854
|
}
|
|
71079
|
-
if (!(0,
|
|
71855
|
+
if (!(0, import_node_fs12.existsSync)(import_node_path10.default.join(dir, "node_modules"))) {
|
|
71080
71856
|
log2(`${def.label}: instalando depend\xEAncias (pode demorar) \u2026`);
|
|
71081
71857
|
if (!run("npm", ["install"], dir).ok) {
|
|
71082
71858
|
return { ok: false, message: `${def.label}: 'npm install' falhou.` };
|
|
@@ -71094,7 +71870,7 @@ async function ensureProxyReady(id, log2) {
|
|
|
71094
71870
|
}
|
|
71095
71871
|
}
|
|
71096
71872
|
log2(`${def.label}: iniciando o proxy\u2026`);
|
|
71097
|
-
const child = (0,
|
|
71873
|
+
const child = (0, import_node_child_process4.spawn)(asShellCommand("npm", ["start"]), {
|
|
71098
71874
|
cwd: dir,
|
|
71099
71875
|
shell: true,
|
|
71100
71876
|
stdio: ["ignore", "ignore", "ignore"],
|
|
@@ -71121,14 +71897,14 @@ async function ensureProxyReady(id, log2) {
|
|
|
71121
71897
|
message: `${def.label}: o proxy n\xE3o respondeu em 90s. Tente de novo, ou rode 'npm run login' em ${dir}.`
|
|
71122
71898
|
};
|
|
71123
71899
|
}
|
|
71124
|
-
var
|
|
71900
|
+
var import_node_os5, import_node_path10, import_node_fs12, import_node_child_process4, DEFS, PROXY_MODEL_KEYS, proxyIdForModelKey, proxiesRoot, dirFor, healthUrl, baseUrl, wait, started, exitHooksInstalled, quoteArg, asShellCommand, run, hasCommand;
|
|
71125
71901
|
var init_proxy_manager2 = __esm({
|
|
71126
71902
|
"src/proxy-manager.ts"() {
|
|
71127
71903
|
"use strict";
|
|
71128
|
-
|
|
71129
|
-
|
|
71130
|
-
|
|
71131
|
-
|
|
71904
|
+
import_node_os5 = __toESM(require("node:os"));
|
|
71905
|
+
import_node_path10 = __toESM(require("node:path"));
|
|
71906
|
+
import_node_fs12 = require("node:fs");
|
|
71907
|
+
import_node_child_process4 = require("node:child_process");
|
|
71132
71908
|
DEFS = {
|
|
71133
71909
|
deepseek: {
|
|
71134
71910
|
id: "deepseek",
|
|
@@ -71160,11 +71936,11 @@ var init_proxy_manager2 = __esm({
|
|
|
71160
71936
|
if (key === PROXY_MODEL_KEYS.kimi) return "kimi";
|
|
71161
71937
|
return null;
|
|
71162
71938
|
};
|
|
71163
|
-
proxiesRoot = () =>
|
|
71164
|
-
process.env.CLAWFAST_HOME?.trim() ||
|
|
71939
|
+
proxiesRoot = () => import_node_path10.default.join(
|
|
71940
|
+
process.env.CLAWFAST_HOME?.trim() || import_node_path10.default.join(import_node_os5.default.homedir(), ".clawfast"),
|
|
71165
71941
|
"proxies"
|
|
71166
71942
|
);
|
|
71167
|
-
dirFor = (def) => process.env[def.dirEnv]?.trim() ||
|
|
71943
|
+
dirFor = (def) => process.env[def.dirEnv]?.trim() || import_node_path10.default.join(proxiesRoot(), def.folder);
|
|
71168
71944
|
healthUrl = (def) => `http://localhost:${def.port}/health`;
|
|
71169
71945
|
baseUrl = (def) => `http://localhost:${def.port}/v1`;
|
|
71170
71946
|
wait = (ms) => new Promise((r2) => setTimeout(r2, ms));
|
|
@@ -71173,7 +71949,7 @@ var init_proxy_manager2 = __esm({
|
|
|
71173
71949
|
quoteArg = (s) => /[\s"&|<>^()]/.test(s) ? `"${s.replace(/"/g, '\\"')}"` : s;
|
|
71174
71950
|
asShellCommand = (cmd, args) => [cmd, ...args.map(quoteArg)].join(" ");
|
|
71175
71951
|
run = (cmd, args, cwd) => {
|
|
71176
|
-
const res = (0,
|
|
71952
|
+
const res = (0, import_node_child_process4.spawnSync)(asShellCommand(cmd, args), {
|
|
71177
71953
|
cwd,
|
|
71178
71954
|
shell: true,
|
|
71179
71955
|
stdio: "inherit",
|
|
@@ -71183,7 +71959,7 @@ var init_proxy_manager2 = __esm({
|
|
|
71183
71959
|
};
|
|
71184
71960
|
hasCommand = (cmd) => {
|
|
71185
71961
|
const probe = process.platform === "win32" ? "where" : "which";
|
|
71186
|
-
return (0,
|
|
71962
|
+
return (0, import_node_child_process4.spawnSync)(asShellCommand(probe, [cmd]), {
|
|
71187
71963
|
stdio: "ignore",
|
|
71188
71964
|
shell: true
|
|
71189
71965
|
}).status === 0;
|
|
@@ -71193,7 +71969,7 @@ var init_proxy_manager2 = __esm({
|
|
|
71193
71969
|
|
|
71194
71970
|
// src/audit-mode.ts
|
|
71195
71971
|
function projectRootFromWorkdir(workdir) {
|
|
71196
|
-
return
|
|
71972
|
+
return import_node_path11.default.dirname(import_node_path11.default.resolve(workdir.replace(/\\/g, "/")));
|
|
71197
71973
|
}
|
|
71198
71974
|
function detectProjectAuditIntent(input) {
|
|
71199
71975
|
const text2 = input.trim();
|
|
@@ -71395,11 +72171,11 @@ ESTILO E EXECU\xC7\xC3O
|
|
|
71395
72171
|
- Responda em portugu\xEAs, direto e t\xE9cnico. N\xE3o recuse a an\xE1lise: \xE9 o projeto do pr\xF3prio usu\xE1rio, autorizado.
|
|
71396
72172
|
- 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.`;
|
|
71397
72173
|
}
|
|
71398
|
-
var
|
|
72174
|
+
var import_node_path11, AUDIT_ACTION, AUDIT_SCOPE, AUDIT_ACTION_THEN_SCOPE, AUDIT_SCOPE_THEN_ACTION, AUDIT_STRONG, AUDIT_EXIT, POC_VERIFY;
|
|
71399
72175
|
var init_audit_mode = __esm({
|
|
71400
72176
|
"src/audit-mode.ts"() {
|
|
71401
72177
|
"use strict";
|
|
71402
|
-
|
|
72178
|
+
import_node_path11 = __toESM(require("node:path"));
|
|
71403
72179
|
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";
|
|
71404
72180
|
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";
|
|
71405
72181
|
AUDIT_ACTION_THEN_SCOPE = new RegExp(
|
|
@@ -71643,19 +72419,39 @@ async function createAgent() {
|
|
|
71643
72419
|
onToolCost: void 0,
|
|
71644
72420
|
onCaidoReady: void 0
|
|
71645
72421
|
};
|
|
72422
|
+
const memory = new EngagementMemory(
|
|
72423
|
+
projectRootFromWorkdir(sandbox.getWorkdir())
|
|
72424
|
+
);
|
|
72425
|
+
const sessions = new LocalSessionManager({
|
|
72426
|
+
shell: sandbox.getShell().bin,
|
|
72427
|
+
shellFlag: sandbox.getShell().flag,
|
|
72428
|
+
cwd: sandbox.getWorkdir()
|
|
72429
|
+
});
|
|
71646
72430
|
const tools = {
|
|
71647
72431
|
run_terminal_cmd: createRunTerminalCmd(context2),
|
|
71648
72432
|
file: createFile(context2),
|
|
71649
72433
|
todo_write: createTodoWrite(context2),
|
|
72434
|
+
// Durable, model-agnostic knowledge base for this target (hosts, creds,
|
|
72435
|
+
// services, confirmed vulns, what worked/was blocked). Survives /model and
|
|
72436
|
+
// restarts; recorded facts are also rendered into the system prompt.
|
|
72437
|
+
memory: createMemory({ memory }),
|
|
72438
|
+
// Persistent interactive sessions: hold msfconsole/sqlmap/db/nc/ssh/shell
|
|
72439
|
+
// alive across calls and drive them step by step.
|
|
72440
|
+
terminal_session: createTerminalSession({ sessions }),
|
|
71650
72441
|
// Native Repeater/Intruder + structured findings store — the core of an
|
|
71651
72442
|
// in-loop pentest workflow (probe → observe → record evidence → confirm).
|
|
71652
|
-
http_request: createHttpRequest({
|
|
72443
|
+
http_request: createHttpRequest({
|
|
72444
|
+
workdir: sandbox.getWorkdir(),
|
|
72445
|
+
unrestricted: sandbox.isUnrestricted()
|
|
72446
|
+
}),
|
|
71653
72447
|
findings: createFindings({ workdir: sandbox.getWorkdir() }),
|
|
71654
|
-
//
|
|
71655
|
-
//
|
|
71656
|
-
//
|
|
71657
|
-
|
|
71658
|
-
|
|
72448
|
+
// Free, key-less web research — ALWAYS on. web_search (DuckDuckGo) finds
|
|
72449
|
+
// sources; open_url fetches+reads one page; deep_research sweeps many pages
|
|
72450
|
+
// concurrently (up to 500) into a digest. The agent is steered to use them
|
|
72451
|
+
// proactively via webResearchPolicy (search → read → verify → retry → act).
|
|
72452
|
+
web_search: createWebSearch(),
|
|
72453
|
+
open_url: createOpenUrl(),
|
|
72454
|
+
deep_research: createDeepResearch({ workdir: sandbox.getWorkdir() })
|
|
71659
72455
|
};
|
|
71660
72456
|
let system = "";
|
|
71661
72457
|
let systemPromptAudit = auditSystemPrompt("", null);
|
|
@@ -71677,9 +72473,14 @@ async function createAgent() {
|
|
|
71677
72473
|
system += reconPhasesPolicy(sandbox.getWorkdir());
|
|
71678
72474
|
system += attackChainPolicy(sandbox.getWorkdir());
|
|
71679
72475
|
system += httpAndFindingsPolicy(sandbox.getWorkdir());
|
|
72476
|
+
system += webResearchPolicy();
|
|
72477
|
+
system += persistentSessionPolicy();
|
|
72478
|
+
system += engagementMemoryPolicy();
|
|
72479
|
+
system += memory.renderSection();
|
|
71680
72480
|
system += buildCliNotesSection();
|
|
71681
72481
|
system += buildSkillsIndexSection();
|
|
71682
72482
|
system += skillsScopePolicy();
|
|
72483
|
+
system += linuxFullPowerPolicy(sandbox);
|
|
71683
72484
|
if (isWebSessionProxyModel(modelName)) {
|
|
71684
72485
|
system += proxyToolProtocolPolicy();
|
|
71685
72486
|
}
|
|
@@ -71691,12 +72492,29 @@ async function createAgent() {
|
|
|
71691
72492
|
assertFullSystemPrompt(systemPromptAudit);
|
|
71692
72493
|
};
|
|
71693
72494
|
await rebuildSystemPrompt(initialModelName);
|
|
72495
|
+
if (sandbox.isLinux()) {
|
|
72496
|
+
const bits = [
|
|
72497
|
+
`modo Linux full-power ativo${sandbox.isKali() ? " (Kali)" : ""}`,
|
|
72498
|
+
sandbox.isRoot() ? "root" : "user",
|
|
72499
|
+
sandbox.isUnrestricted() ? "escopo liberado" : "escopo restrito (scope.txt)"
|
|
72500
|
+
];
|
|
72501
|
+
render.info(bits.join(" \xB7 "));
|
|
72502
|
+
}
|
|
71694
72503
|
if (systemPromptAudit.dumpPath) {
|
|
71695
72504
|
render.info(
|
|
71696
72505
|
`system prompt completo salvo em: ${systemPromptAudit.dumpPath}`
|
|
71697
72506
|
);
|
|
71698
72507
|
}
|
|
71699
72508
|
const history = [];
|
|
72509
|
+
if (!envFlagEnabled(process.env.CLAWFAST_FRESH)) {
|
|
72510
|
+
const prior = memory.loadConversation();
|
|
72511
|
+
if (prior.length) {
|
|
72512
|
+
history.push(...prior);
|
|
72513
|
+
render.info(
|
|
72514
|
+
`\u25B8 mem\xF3ria: retomando ${prior.length} mensagem(ns) da sess\xE3o anterior (CLAWFAST_FRESH=1 come\xE7a limpo)`
|
|
72515
|
+
);
|
|
72516
|
+
}
|
|
72517
|
+
}
|
|
71700
72518
|
let auditMode = false;
|
|
71701
72519
|
let pocVerify = false;
|
|
71702
72520
|
const projectRoot = projectRootFromWorkdir(sandbox.getWorkdir());
|
|
@@ -71726,7 +72544,6 @@ async function createAgent() {
|
|
|
71726
72544
|
reason: "faltando NVIDIA_API_KEY em .env.local (https://build.nvidia.com/)",
|
|
71727
72545
|
models: [
|
|
71728
72546
|
"model-nvidia-mistral-medium-3.5",
|
|
71729
|
-
"model-nvidia-gpt-oss-120b",
|
|
71730
72547
|
"model-nvidia-glm-5.1",
|
|
71731
72548
|
"model-nvidia-qwen3.5-397b"
|
|
71732
72549
|
].map((key) => ({ key, label: labelFor(key) }))
|
|
@@ -71813,7 +72630,7 @@ async function createAgent() {
|
|
|
71813
72630
|
if (!slug) {
|
|
71814
72631
|
return {
|
|
71815
72632
|
ok: false,
|
|
71816
|
-
message: "informe o modelo NVIDIA (ex.: nvidia:
|
|
72633
|
+
message: "informe o modelo NVIDIA (ex.: nvidia:z-ai/glm-5.1)",
|
|
71817
72634
|
selection: getModelSelection()
|
|
71818
72635
|
};
|
|
71819
72636
|
}
|
|
@@ -72066,6 +72883,7 @@ ${segs.join(
|
|
|
72066
72883
|
}, STREAM_STALL_TIMEOUT_MS);
|
|
72067
72884
|
};
|
|
72068
72885
|
try {
|
|
72886
|
+
ensureGeneratableTail(history);
|
|
72069
72887
|
const result = streamText({
|
|
72070
72888
|
model: resolveLanguageModel2(modelKey),
|
|
72071
72889
|
system: turnSystem,
|
|
@@ -72206,6 +73024,7 @@ ${resultText}`
|
|
|
72206
73024
|
if (modelKey === PROXY_MODEL_KEYS.kimi) {
|
|
72207
73025
|
await reportKimiSession();
|
|
72208
73026
|
}
|
|
73027
|
+
memory.saveConversation(history);
|
|
72209
73028
|
render.endTurn();
|
|
72210
73029
|
return;
|
|
72211
73030
|
} catch (err) {
|
|
@@ -72306,6 +73125,7 @@ ${resultText}`
|
|
|
72306
73125
|
return sandbox.sendStdin(line);
|
|
72307
73126
|
}
|
|
72308
73127
|
async function close() {
|
|
73128
|
+
sessions.closeAll();
|
|
72309
73129
|
await sandbox.close();
|
|
72310
73130
|
try {
|
|
72311
73131
|
const removed = await sandbox.cleanupWorkspace();
|
|
@@ -72330,21 +73150,20 @@ ${resultText}`
|
|
|
72330
73150
|
close
|
|
72331
73151
|
};
|
|
72332
73152
|
}
|
|
72333
|
-
var import_promises2,
|
|
73153
|
+
var import_promises2, import_node_path12, MAX_STEPS, AUDIT_MAX_STEPS, MAX_OUTPUT_TOKENS, MAX_AUTO_CONTINUES, AUDIT_MAX_AUTO_CONTINUES, MAX_RATE_LIMIT_WAITS, STREAM_STALL_TIMEOUT_MS, MAX_STALL_RETRIES, isRateLimitError, sleep4, LEAKED_TOOL_CALL_RE, DANGLING_TAIL_RE, ACTION_ANNOUNCE_RE, BENIGN_CLOSER_RE, TOOL_CALL_NUDGE, MAX_BRIDGE_STEPS, AUDIT_MAX_BRIDGE_STEPS, BRIDGE_RESULT_PREAMBLE, LEAKED_CALL_RESULT_PREAMBLE, BRIDGEABLE_TOOLS, FINDINGS_ACTIONS, truncateBridgeSummary, isWebSessionProxyModel, proxyToolProtocolPolicy, SYSTEM_PROMPT_SOURCE, REQUIRED_SYSTEM_PROMPT_MARKERS, MODEL_LABELS, labelFor, loginRequiredHint, CLI_PYTHON_ONLY_POLICY, pythonOnlyPolicy, scriptFilePolicy, deepReconPolicy, httpAndFindingsPolicy, reconPhasesPolicy, attackChainPolicy, skillsScopePolicy, webResearchPolicy, engagementMemoryPolicy, persistentSessionPolicy, linuxFullPowerPolicy, hasEnvValue2, envFlagEnabled, CLI_PERSONALITIES, buildCliUserCustomization, buildCliNotesSection, cliGuardrailsConfig, maybeDumpSystemPrompt, auditSystemPrompt, assertFullSystemPrompt;
|
|
72334
73154
|
var init_agent = __esm({
|
|
72335
73155
|
"src/agent.ts"() {
|
|
72336
73156
|
"use strict";
|
|
72337
73157
|
init_dist5();
|
|
72338
73158
|
import_promises2 = require("node:fs/promises");
|
|
72339
|
-
|
|
73159
|
+
import_node_path12 = __toESM(require("node:path"));
|
|
72340
73160
|
init_providers();
|
|
72341
73161
|
init_system_prompt();
|
|
72342
73162
|
init_notes();
|
|
72343
73163
|
init_run_terminal_cmd();
|
|
72344
73164
|
init_todo_write();
|
|
72345
73165
|
init_file();
|
|
72346
|
-
|
|
72347
|
-
init_open_url();
|
|
73166
|
+
init_web_research();
|
|
72348
73167
|
init_http_request();
|
|
72349
73168
|
init_findings();
|
|
72350
73169
|
init_todo_manager();
|
|
@@ -72355,6 +73174,11 @@ var init_agent = __esm({
|
|
|
72355
73174
|
init_guardrails();
|
|
72356
73175
|
init_local_sandbox();
|
|
72357
73176
|
init_local_sandbox_manager();
|
|
73177
|
+
init_message_tail();
|
|
73178
|
+
init_memory();
|
|
73179
|
+
init_memory_tool();
|
|
73180
|
+
init_sessions();
|
|
73181
|
+
init_session_tool();
|
|
72358
73182
|
init_console_writer();
|
|
72359
73183
|
init_render();
|
|
72360
73184
|
init_skills();
|
|
@@ -72443,7 +73267,6 @@ Regras:
|
|
|
72443
73267
|
];
|
|
72444
73268
|
MODEL_LABELS = {
|
|
72445
73269
|
"model-nvidia-mistral-medium-3.5": "NVIDIA - mistralai/mistral-medium-3.5-128b",
|
|
72446
|
-
"model-nvidia-gpt-oss-120b": "NVIDIA - openai/gpt-oss-120b",
|
|
72447
73270
|
"model-nvidia-glm-5.1": "NVIDIA - z-ai/glm-5.1",
|
|
72448
73271
|
"model-nvidia-qwen3.5-397b": "NVIDIA - qwen/qwen3.5-397b-a17b",
|
|
72449
73272
|
"model-openai-chat-latest": "OpenAI - chat-latest",
|
|
@@ -72589,6 +73412,65 @@ Your skills are INTERNAL to you and live ONLY in ${skillsDir()}. The skills curr
|
|
|
72589
73412
|
- NEVER treat any other skills directory on this machine as yours. Folders such as ~/.agents/skills, ~/.claude/skills, ~/.augment/skills, ~/.cursor, and any other agent/tool skill folder belong to OTHER tools and are OUT OF SCOPE \u2014 ignore them completely and never read, list, summarize, or load them as your skills.
|
|
72590
73413
|
- Do NOT scan the filesystem hunting for "skills". When asked about your skills, answer from your internal skills above, not from a disk search.
|
|
72591
73414
|
</skills_scope>`;
|
|
73415
|
+
webResearchPolicy = () => `
|
|
73416
|
+
|
|
73417
|
+
<web_research>
|
|
73418
|
+
ABSOLUTE RULE FOR THIS LOCAL CLI SESSION \u2014 applies to EVERY model and EVERY task:
|
|
73419
|
+
|
|
73420
|
+
You have FREE, key-less web access. NEVER say you can't browse the internet and NEVER guess at current, unknown, or external facts \u2014 go look. Three tools:
|
|
73421
|
+
|
|
73422
|
+
- web_search \u2014 free web search (DuckDuckGo). Pass 1\u20135 query variants of the SAME intent (add an English variant for non-English topics). Returns ranked results (title, url, snippet). Operators site:/filetype:/inurl:/intitle: are allowed.
|
|
73423
|
+
- open_url \u2014 fetch and READ one specific page (the link the user pasted, or one from a search). Returns the page's readable text. Use it to actually open a link and understand it before acting.
|
|
73424
|
+
- deep_research \u2014 search + fetch MANY pages at once and return a synthesized digest with sources (a full report is saved under reports/). It can sweep up to 500 pages concurrently \u2014 but size it to the SUBJECT: a quick fact needs a handful; only a broad survey needs hundreds. Do NOT fetch 500 for something small.
|
|
73425
|
+
|
|
73426
|
+
The research loop (do this on your own, without being told):
|
|
73427
|
+
1. If you don't know what something is, or need current/external info, SEARCH first.
|
|
73428
|
+
2. OPEN the most relevant results (or the user's link) and READ them.
|
|
73429
|
+
3. VERIFY across 2+ independent sources before relying on a fact.
|
|
73430
|
+
4. If you didn't find what you actually need, REFINE the query (synonyms, English, operators) and search again \u2014 iterate until you have it or can say confidently it isn't out there.
|
|
73431
|
+
5. THEN act on what you learned, citing the URLs you used.
|
|
73432
|
+
|
|
73433
|
+
This is unrestricted reading of the PUBLIC web \u2014 no scope.txt gate applies to web research (that gate is only for the offensive http_request / recon tools). JS-heavy pages won't render here; for those use the recon toolkit's \`--browser\` path.
|
|
73434
|
+
</web_research>`;
|
|
73435
|
+
engagementMemoryPolicy = () => `
|
|
73436
|
+
|
|
73437
|
+
<engagement_memory_policy>
|
|
73438
|
+
ABSOLUTE RULE FOR THIS LOCAL CLI SESSION \u2014 applies to EVERY model and EVERY task:
|
|
73439
|
+
|
|
73440
|
+
You have a PERSISTENT \`memory\` tool scoped to this target/project. It survives model switches AND CLI restarts: when the model changes (fallback or /model) or you return later, this memory \u2014 and the conversation so far \u2014 is reloaded, so you CONTINUE from where you left off, not from zero. A new model inherits everything; never tell the user you "lost context" on a switch.
|
|
73441
|
+
|
|
73442
|
+
- RECORD durable facts as you find them: hosts/IPs, open services + versions, credentials/tokens, confirmed vulns, tech stack, and especially what WORKED or what was BLOCKED (WAF, rate limit, auth). Use \`memory\` action add (kind + title; detail/target/source optional).
|
|
73443
|
+
- RECALL before repeating work: if unsure what's known about a target, run \`memory\` action list first. Don't re-run recon you already did in a past session \u2014 build on it.
|
|
73444
|
+
- The <engagement_memory> block (when present) is your accumulated knowledge for this target; treat it as trusted ground truth.
|
|
73445
|
+
</engagement_memory_policy>`;
|
|
73446
|
+
persistentSessionPolicy = () => `
|
|
73447
|
+
|
|
73448
|
+
<persistent_sessions>
|
|
73449
|
+
ABSOLUTE RULE FOR THIS LOCAL CLI SESSION \u2014 applies to EVERY model and EVERY task:
|
|
73450
|
+
|
|
73451
|
+
For anything INTERACTIVE or STATEFUL, use the \`terminal_session\` tool, NOT run_terminal_cmd. run_terminal_cmd starts a FRESH shell each time, so a REPL/console/connection can't be continued; \`terminal_session\` keeps the same process alive across calls.
|
|
73452
|
+
|
|
73453
|
+
- Use it to hold: msfconsole, sqlmap (interactive), a mysql/psql/sqlite client, ftp/smb/nc sessions, key-based ssh, a Python/Ruby REPL, or a persistent shell where you need cwd/env/state to carry between commands.
|
|
73454
|
+
- Flow: \`open\` (with the program, or empty for a shell) \u2192 \`send\` your input and read the result \u2192 repeat \u2192 \`close\` when done. Use \`read\` to poll a long-running command's output. Reuse ONE session for a connection instead of opening many.
|
|
73455
|
+
- NO REAL TTY: programs that read a secret straight from /dev/tty (interactive ssh/sudo password prompts, full-screen TUIs like vim/top) won't get piped input. Use \`sudo -S\` (reads stdin), sshpass, key auth, or non-interactive flags. When a held tool prompts the human for something, relay it as usual.
|
|
73456
|
+
</persistent_sessions>`;
|
|
73457
|
+
linuxFullPowerPolicy = (sandbox) => {
|
|
73458
|
+
if (!sandbox.isLinux() && !sandbox.isUnrestricted()) return "";
|
|
73459
|
+
const distro = sandbox.getDistroName();
|
|
73460
|
+
const asWho = sandbox.isRoot() ? "root (uid 0, no sudo needed)" : "a normal user (use sudo when needed)";
|
|
73461
|
+
const scopeLine = sandbox.isUnrestricted() ? 'SCOPE IS OPEN this session: http_request needs NO scope.txt edit and the recon/ Python modules accept `--allow-out-of-scope`. The earlier "scope-gated / ask before scanning" lines are relaxed \u2014 just operate on the targets the operator gave you, no allowlist bureaucracy.' : "Scope.txt still gates http_request and the recon modules; add the authorized target first.";
|
|
73462
|
+
return `
|
|
73463
|
+
|
|
73464
|
+
<linux_full_power>
|
|
73465
|
+
ABSOLUTE RULE FOR THIS LOCAL CLI SESSION \u2014 you are running on ${distro} as ${asWho}. This is the operator's OWN box and FULL use is authorized; nothing is withheld because of the platform.
|
|
73466
|
+
|
|
73467
|
+
- Full power: real shell, full filesystem, raw sockets, package managers (apt/pip/pipx/go), systemctl, and the complete Kali/Debian toolset (nmap, masscan, sqlmap, metasploit, hydra, john, hashcat, gobuster/ffuf, nuclei, nikto, wpscan, phoneinfoga, \u2026). Install anything missing instead of giving up.
|
|
73468
|
+
- Python is 100% unlocked \u2014 build and run any Python you need, no restriction.
|
|
73469
|
+
- Scanning is fully allowed: ports, hosts, IP/CIDR ranges, services/versions, and number/identifier enumeration (incl. phone-number OSINT). Apply sane OPSEC throttling on noisy sweeps.
|
|
73470
|
+
- ${scopeLine}
|
|
73471
|
+
- KALI INTERACTIVE PROMPTS: when a tool/installer/wizard/REPL/login (msfconsole, apt, an account or sign-up step, ssh/sudo/password, a y/n) is WAITING for input, STOP and tell the user in ONE short line exactly what the program ("o kali") is asking and what to type. The human types it here and the CLI forwards it to the program's stdin; then you continue. Drive the tool yourself; let the human supply the human answers (credentials, OTPs, confirmations). NEVER invent credentials or fabricate a tool's output.
|
|
73472
|
+
</linux_full_power>`;
|
|
73473
|
+
};
|
|
72592
73474
|
hasEnvValue2 = (name25) => Boolean(process.env[name25]?.trim());
|
|
72593
73475
|
envFlagEnabled = (value) => {
|
|
72594
73476
|
const normalized = value?.trim().toLowerCase();
|
|
@@ -72646,7 +73528,7 @@ ${section}` : "";
|
|
|
72646
73528
|
return null;
|
|
72647
73529
|
}
|
|
72648
73530
|
await (0, import_promises2.mkdir)(workdir, { recursive: true });
|
|
72649
|
-
const outputPath =
|
|
73531
|
+
const outputPath = import_node_path12.default.join(workdir, "system-prompt.txt");
|
|
72650
73532
|
await (0, import_promises2.writeFile)(outputPath, system, "utf8");
|
|
72651
73533
|
return outputPath;
|
|
72652
73534
|
};
|
|
@@ -72676,19 +73558,19 @@ var interactive_input_exports = {};
|
|
|
72676
73558
|
__export(interactive_input_exports, {
|
|
72677
73559
|
InteractiveInput: () => InteractiveInput
|
|
72678
73560
|
});
|
|
72679
|
-
var import_node_readline3,
|
|
73561
|
+
var import_node_readline3, import_node_os6, C4, frame2, out2, cols, truncate5, InteractiveInput;
|
|
72680
73562
|
var init_interactive_input = __esm({
|
|
72681
73563
|
"src/interactive-input.ts"() {
|
|
72682
73564
|
"use strict";
|
|
72683
73565
|
import_node_readline3 = __toESM(require("node:readline"));
|
|
72684
|
-
|
|
73566
|
+
import_node_os6 = __toESM(require("node:os"));
|
|
72685
73567
|
init_ui();
|
|
72686
73568
|
init_theme();
|
|
72687
73569
|
C4 = ui.C;
|
|
72688
73570
|
frame2 = (s) => paint(s, PAL.frame);
|
|
72689
73571
|
out2 = (s) => process.stdout.write(s);
|
|
72690
73572
|
cols = () => process.stdout.columns || 80;
|
|
72691
|
-
|
|
73573
|
+
truncate5 = (s, n) => vlen(s) > n ? [...s].slice(0, Math.max(0, n - 1)).join("") + "\u2026" : s;
|
|
72692
73574
|
InteractiveInput = class {
|
|
72693
73575
|
// row the caret sits on within the drawn block
|
|
72694
73576
|
constructor(paste, commands) {
|
|
@@ -73094,14 +73976,14 @@ var init_interactive_input = __esm({
|
|
|
73094
73976
|
return;
|
|
73095
73977
|
}
|
|
73096
73978
|
const width = cols();
|
|
73097
|
-
const user = (
|
|
73098
|
-
const host = (
|
|
73979
|
+
const user = (import_node_os6.default.userInfo().username || "hacker").toLowerCase();
|
|
73980
|
+
const host = (import_node_os6.default.hostname() || "localhost").split(".")[0].toLowerCase();
|
|
73099
73981
|
const label = " \u2709 mensagem ";
|
|
73100
73982
|
const boxW = Math.min(54, Math.max(28, width - 2));
|
|
73101
73983
|
const topFill = "\u2500".repeat(Math.max(0, boxW - vlen(label) - 3));
|
|
73102
73984
|
const top = frame2("\u256D\u2500") + gradient(label, { bold: true }) + frame2(topFill + "\u256E");
|
|
73103
73985
|
const id = `${user}\u327F${host} \xB7 ${shortCwd()}`;
|
|
73104
|
-
const idLine = frame2("\u2502 ") + `${C4.dim}${
|
|
73986
|
+
const idLine = frame2("\u2502 ") + `${C4.dim}${truncate5(id, boxW - 4)}${C4.reset}`;
|
|
73105
73987
|
const prefix = frame2("\u2570\u2500") + gradient("\u276F", { bold: true }) + " ";
|
|
73106
73988
|
const prefixLen = 4;
|
|
73107
73989
|
const avail = Math.max(8, width - prefixLen - 1);
|
|
@@ -73124,7 +74006,7 @@ var init_interactive_input = __esm({
|
|
|
73124
74006
|
const name25 = active2 ? gradient(it.name, { bold: true }) : `${C4.cyan}${it.name}`;
|
|
73125
74007
|
const pad = " ".repeat(Math.max(1, nameW - vlen(it.name) + 2));
|
|
73126
74008
|
const descRoom = ddInner - 2 - nameW - 2;
|
|
73127
|
-
const desc = `${C4.dim}${
|
|
74009
|
+
const desc = `${C4.dim}${truncate5(it.desc, Math.max(4, descRoom))}`;
|
|
73128
74010
|
const body = `${marker25}${name25}${C4.reset}${pad}${desc}${C4.reset}`;
|
|
73129
74011
|
const padded = body + " ".repeat(Math.max(0, ddInner - 1 - vlen(body)));
|
|
73130
74012
|
lines.push(` ${frame2("\u2502")}${C4.reset}${padded}${frame2("\u2502")}`);
|
|
@@ -73206,7 +74088,7 @@ var init_interactive_input = __esm({
|
|
|
73206
74088
|
const active2 = i === sel;
|
|
73207
74089
|
const marker25 = active2 ? gradient("\u276F ", { bold: true }) : `${C4.dim} `;
|
|
73208
74090
|
const text2 = active2 ? gradient(it.label, { bold: true }) : `${C4.reset}${it.label}`;
|
|
73209
|
-
const hint = it.hint ? ` ${C4.dim}${
|
|
74091
|
+
const hint = it.hint ? ` ${C4.dim}${truncate5(it.hint, Math.max(6, inner - labelW - 6))}${C4.reset}` : "";
|
|
73210
74092
|
lines.push(`${frame2("\u2502 ")}${C4.reset}${marker25}${text2}${C4.reset}${hint}`);
|
|
73211
74093
|
});
|
|
73212
74094
|
lines.push(
|
|
@@ -73236,13 +74118,11 @@ var init_interactive_input = __esm({
|
|
|
73236
74118
|
const marker25 = active2 ? gradient("\u276F ", { bold: true }) : `${C4.dim} `;
|
|
73237
74119
|
const text2 = active2 ? gradient(it.label, { bold: true }) : `${C4.reset}${it.label}`;
|
|
73238
74120
|
const labelW = vlen(it.label);
|
|
73239
|
-
const hint = it.hint ? ` ${C4.dim}${
|
|
74121
|
+
const hint = it.hint ? ` ${C4.dim}${truncate5(it.hint, Math.max(6, inner - labelW - 6))}${C4.reset}` : "";
|
|
73240
74122
|
lines.push(`${frame2("\u2502 ")}${C4.reset}${marker25}${text2}${C4.reset}${hint}`);
|
|
73241
74123
|
}
|
|
73242
74124
|
if (list.length > pageSize) {
|
|
73243
|
-
lines.push(
|
|
73244
|
-
`${frame2("\u2502 ")}${C4.dim}${sel + 1}/${list.length}${C4.reset}`
|
|
73245
|
-
);
|
|
74125
|
+
lines.push(`${frame2("\u2502 ")}${C4.dim}${sel + 1}/${list.length}${C4.reset}`);
|
|
73246
74126
|
}
|
|
73247
74127
|
}
|
|
73248
74128
|
lines.push(
|
|
@@ -73278,8 +74158,10 @@ async function main() {
|
|
|
73278
74158
|
const { InteractiveInput: InteractiveInput2 } = await Promise.resolve().then(() => (init_interactive_input(), interactive_input_exports));
|
|
73279
74159
|
const C5 = ui2.C;
|
|
73280
74160
|
if (!bootQuiet) {
|
|
73281
|
-
process.stdout.write(
|
|
73282
|
-
|
|
74161
|
+
process.stdout.write(
|
|
74162
|
+
`${C5.dim} booting local sandbox + agent\u2026${C5.reset}
|
|
74163
|
+
`
|
|
74164
|
+
);
|
|
73283
74165
|
}
|
|
73284
74166
|
const agent = await createAgent2();
|
|
73285
74167
|
const sys = agent.getSystemPrompt();
|
|
@@ -73307,11 +74189,17 @@ async function main() {
|
|
|
73307
74189
|
`
|
|
73308
74190
|
);
|
|
73309
74191
|
const COMMANDS = [
|
|
73310
|
-
{
|
|
74192
|
+
{
|
|
74193
|
+
name: "/model",
|
|
74194
|
+
desc: "trocar o modelo (setas; NVIDIA/OpenRouter c/ busca)"
|
|
74195
|
+
},
|
|
73311
74196
|
{ name: "/api", desc: "trocar chave de API (NVIDIA / OpenRouter)" },
|
|
73312
74197
|
{ name: "/skills", desc: "listar as skills instaladas" },
|
|
73313
74198
|
{ name: "/skillcreator", desc: "criar uma nova skill" },
|
|
73314
|
-
{
|
|
74199
|
+
{
|
|
74200
|
+
name: "/system",
|
|
74201
|
+
desc: "salvar o system prompt (HTML) na \xC1rea de Trabalho"
|
|
74202
|
+
},
|
|
73315
74203
|
{ name: "/nov", desc: "novidades desta vers\xE3o" },
|
|
73316
74204
|
{ name: "/exit", desc: "fechar o clawfast" }
|
|
73317
74205
|
];
|
|
@@ -73341,15 +74229,15 @@ async function main() {
|
|
|
73341
74229
|
process.exit(0);
|
|
73342
74230
|
};
|
|
73343
74231
|
const desktopDir = () => {
|
|
73344
|
-
const home =
|
|
74232
|
+
const home = import_node_os7.default.homedir();
|
|
73345
74233
|
const candidates = [
|
|
73346
|
-
process.env.OneDrive ?
|
|
73347
|
-
process.env.USERPROFILE ?
|
|
73348
|
-
|
|
73349
|
-
|
|
73350
|
-
|
|
74234
|
+
process.env.OneDrive ? import_node_path13.default.join(process.env.OneDrive, "Desktop") : null,
|
|
74235
|
+
process.env.USERPROFILE ? import_node_path13.default.join(process.env.USERPROFILE, "Desktop") : null,
|
|
74236
|
+
import_node_path13.default.join(home, "Desktop"),
|
|
74237
|
+
import_node_path13.default.join(home, "\xC1rea de Trabalho"),
|
|
74238
|
+
import_node_path13.default.join(home, "OneDrive", "Desktop")
|
|
73351
74239
|
].filter((p) => Boolean(p));
|
|
73352
|
-
return candidates.find((p) => (0,
|
|
74240
|
+
return candidates.find((p) => (0, import_node_fs13.existsSync)(p)) ?? home;
|
|
73353
74241
|
};
|
|
73354
74242
|
const printFatal = (err) => {
|
|
73355
74243
|
process.stderr.write(
|
|
@@ -73415,8 +74303,10 @@ ${C5.cyan}nome da skill?${C5.reset} ${C5.dim}(ex: xss-recon)${C5.reset}
|
|
|
73415
74303
|
}
|
|
73416
74304
|
if (sub === "delete" || sub === "rm" || sub === "remove") {
|
|
73417
74305
|
if (!arg) {
|
|
73418
|
-
process.stdout.write(
|
|
73419
|
-
|
|
74306
|
+
process.stdout.write(
|
|
74307
|
+
`${C5.yellow}uso: /skill delete <nome>${C5.reset}
|
|
74308
|
+
`
|
|
74309
|
+
);
|
|
73420
74310
|
return;
|
|
73421
74311
|
}
|
|
73422
74312
|
if (deleteSkill(arg)) {
|
|
@@ -73611,8 +74501,10 @@ ${C5.dim}ja disponivel para todos os modelos nesta sessao.${C5.reset}
|
|
|
73611
74501
|
);
|
|
73612
74502
|
return false;
|
|
73613
74503
|
}
|
|
73614
|
-
process.stdout.write(
|
|
73615
|
-
|
|
74504
|
+
process.stdout.write(
|
|
74505
|
+
`${C5.dim}testando a chave na ${provider.name}\u2026${C5.reset}
|
|
74506
|
+
`
|
|
74507
|
+
);
|
|
73616
74508
|
const test = await provider.test(key);
|
|
73617
74509
|
if (!test.ok) {
|
|
73618
74510
|
if (test.status === 401 || test.status === 403) {
|
|
@@ -73720,11 +74612,9 @@ ${C5.dim}salva em ${C5.reset}${C5.cyan}${file2}${C5.reset}
|
|
|
73720
74612
|
label: m.name,
|
|
73721
74613
|
hint: m.id
|
|
73722
74614
|
}));
|
|
73723
|
-
const choice2 = await inputUI.searchSelect(
|
|
73724
|
-
"
|
|
73725
|
-
|
|
73726
|
-
{ placeholder: "digite para filtrar (nome ou slug)" }
|
|
73727
|
-
);
|
|
74615
|
+
const choice2 = await inputUI.searchSelect("NVIDIA \u2014 buscar modelo", items, {
|
|
74616
|
+
placeholder: "digite para filtrar (nome ou slug)"
|
|
74617
|
+
});
|
|
73728
74618
|
if (choice2 === null) {
|
|
73729
74619
|
process.stdout.write(`${C5.dim}sele\xE7\xE3o de modelo cancelada${C5.reset}
|
|
73730
74620
|
`);
|
|
@@ -73852,7 +74742,7 @@ ${C5.dim}salva em ${C5.reset}${C5.cyan}${file2}${C5.reset}
|
|
|
73852
74742
|
const sysText = agent.getSystemPrompt();
|
|
73853
74743
|
const audit = agent.getSystemPromptAudit();
|
|
73854
74744
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
|
|
73855
|
-
const filePath =
|
|
74745
|
+
const filePath = import_node_path13.default.join(
|
|
73856
74746
|
desktopDir(),
|
|
73857
74747
|
`clawfast-system-prompt_${stamp}.html`
|
|
73858
74748
|
);
|
|
@@ -73974,13 +74864,13 @@ ${C5.dim}interrompido \u2014 Ctrl+C de novo para fechar${C5.reset}
|
|
|
73974
74864
|
}
|
|
73975
74865
|
await shutdown();
|
|
73976
74866
|
}
|
|
73977
|
-
var
|
|
74867
|
+
var import_node_os7, import_node_path13, import_node_fs13, import_promises3, deepseekEnabled, configuredModelProviders;
|
|
73978
74868
|
var init_index = __esm({
|
|
73979
74869
|
"index.ts"() {
|
|
73980
74870
|
"use strict";
|
|
73981
|
-
|
|
73982
|
-
|
|
73983
|
-
|
|
74871
|
+
import_node_os7 = __toESM(require("node:os"));
|
|
74872
|
+
import_node_path13 = __toESM(require("node:path"));
|
|
74873
|
+
import_node_fs13 = require("node:fs");
|
|
73984
74874
|
import_promises3 = require("node:fs/promises");
|
|
73985
74875
|
init_paste_input();
|
|
73986
74876
|
init_boot_ui();
|