clawfast 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clawfast.cjs +569 -94
- package/package.json +1 -1
package/dist/clawfast.cjs
CHANGED
|
@@ -548,11 +548,11 @@ function fileDefines(filePath, key) {
|
|
|
548
548
|
return false;
|
|
549
549
|
}
|
|
550
550
|
}
|
|
551
|
-
function
|
|
551
|
+
function swapProviderKey(envVar, key) {
|
|
552
552
|
const local = import_node_path.default.resolve(process.cwd(), ".env.local");
|
|
553
|
-
const target = fileDefines(local,
|
|
554
|
-
setEnvVar(target,
|
|
555
|
-
process.env
|
|
553
|
+
const target = fileDefines(local, envVar) ? local : clawfastEnvPath();
|
|
554
|
+
setEnvVar(target, envVar, key);
|
|
555
|
+
process.env[envVar] = key;
|
|
556
556
|
return target;
|
|
557
557
|
}
|
|
558
558
|
async function testNvidiaKey(key) {
|
|
@@ -586,6 +586,27 @@ async function testNvidiaKey(key) {
|
|
|
586
586
|
};
|
|
587
587
|
}
|
|
588
588
|
}
|
|
589
|
+
async function testOpenRouterKey(key) {
|
|
590
|
+
try {
|
|
591
|
+
const res = await fetch("https://openrouter.ai/api/v1/key", {
|
|
592
|
+
headers: { Authorization: `Bearer ${key}` }
|
|
593
|
+
});
|
|
594
|
+
if (res.ok) return { ok: true, status: res.status, detail: "OK" };
|
|
595
|
+
let detail = res.statusText || `HTTP ${res.status}`;
|
|
596
|
+
try {
|
|
597
|
+
const body = await res.json();
|
|
598
|
+
detail = body.error?.message || body.message || detail;
|
|
599
|
+
} catch {
|
|
600
|
+
}
|
|
601
|
+
return { ok: false, status: res.status, detail };
|
|
602
|
+
} catch (err) {
|
|
603
|
+
return {
|
|
604
|
+
ok: false,
|
|
605
|
+
status: 0,
|
|
606
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
}
|
|
589
610
|
function promptLine(question) {
|
|
590
611
|
const rl = import_node_readline.default.createInterface({
|
|
591
612
|
input: process.stdin,
|
|
@@ -634,7 +655,7 @@ ${C.dim}Para come\xE7ar, preciso de uma chave de modelo. Use a NVIDIA build (mod
|
|
|
634
655
|
);
|
|
635
656
|
return true;
|
|
636
657
|
}
|
|
637
|
-
var import_dotenv, import_node_os, import_node_path, import_node_fs, import_node_readline, clawfastHome, clawfastEnvPath, PROVIDER_KEYS, hasAnyProviderKey, C;
|
|
658
|
+
var import_dotenv, import_node_os, import_node_path, import_node_fs, import_node_readline, clawfastHome, clawfastEnvPath, PROVIDER_KEYS, hasAnyProviderKey, swapNvidiaKey, swapOpenRouterKey, C;
|
|
638
659
|
var init_config = __esm({
|
|
639
660
|
"src/config.ts"() {
|
|
640
661
|
"use strict";
|
|
@@ -645,8 +666,14 @@ var init_config = __esm({
|
|
|
645
666
|
import_node_readline = __toESM(require("node:readline"));
|
|
646
667
|
clawfastHome = () => process.env.CLAWFAST_HOME?.trim() || import_node_path.default.join(import_node_os.default.homedir(), ".clawfast");
|
|
647
668
|
clawfastEnvPath = () => import_node_path.default.join(clawfastHome(), ".env");
|
|
648
|
-
PROVIDER_KEYS = [
|
|
669
|
+
PROVIDER_KEYS = [
|
|
670
|
+
"NVIDIA_API_KEY",
|
|
671
|
+
"OPENAI_API_KEY",
|
|
672
|
+
"OPENROUTER_API_KEY"
|
|
673
|
+
];
|
|
649
674
|
hasAnyProviderKey = () => PROVIDER_KEYS.some((name25) => Boolean(process.env[name25]?.trim()));
|
|
675
|
+
swapNvidiaKey = (key) => swapProviderKey("NVIDIA_API_KEY", key);
|
|
676
|
+
swapOpenRouterKey = (key) => swapProviderKey("OPENROUTER_API_KEY", key);
|
|
650
677
|
C = {
|
|
651
678
|
reset: "\x1B[0m",
|
|
652
679
|
dim: "\x1B[90m",
|
|
@@ -662,7 +689,7 @@ var clawfastVersion, isDevVersion, isNewerVersion;
|
|
|
662
689
|
var init_version = __esm({
|
|
663
690
|
"src/version.ts"() {
|
|
664
691
|
"use strict";
|
|
665
|
-
clawfastVersion = () => true ? "2.
|
|
692
|
+
clawfastVersion = () => true ? "2.2.0" : devVersionFromPackageJson();
|
|
666
693
|
isDevVersion = () => clawfastVersion().includes("-dev");
|
|
667
694
|
isNewerVersion = (a, b) => {
|
|
668
695
|
const parse3 = (v) => v.split("-")[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
@@ -715,7 +742,7 @@ var init_update = __esm({
|
|
|
715
742
|
if (isDevVersion()) return null;
|
|
716
743
|
const cache = readCache();
|
|
717
744
|
if (cache && isNewerVersion(cache.latest, current)) {
|
|
718
|
-
return `nova versao ${cache.latest} disponivel (voce tem ${current}) \u2014 rode: clawfast update`;
|
|
745
|
+
return `nova versao ${cache.latest} disponivel (voce tem ${current}) \u2014 rode: clawfast update e depois clawfast para usar a nova versao`;
|
|
719
746
|
}
|
|
720
747
|
return null;
|
|
721
748
|
};
|
|
@@ -45777,7 +45804,36 @@ function supportsMultimodalToolResults(modelName) {
|
|
|
45777
45804
|
const normalized = modelName.toLowerCase();
|
|
45778
45805
|
return normalized === "ask-model" || normalized.includes("gemini") || normalized.includes("google/") || isAnthropicModel(normalized) || normalized.includes("anthropic/") || normalized.includes("claude") || normalized.includes("openai") || normalized.includes("openai/") || normalized.includes("gpt-") || normalized.includes("o1") || normalized.includes("o3") || normalized.includes("o4") || normalized.includes("x-ai/") || normalized.includes("grok");
|
|
45779
45806
|
}
|
|
45780
|
-
|
|
45807
|
+
function resolveLanguageModel2(key) {
|
|
45808
|
+
if (isOpenRouterDynamicKey(key)) {
|
|
45809
|
+
return openrouter2(openRouterSlugFromKey(key));
|
|
45810
|
+
}
|
|
45811
|
+
return myProvider.languageModel(key);
|
|
45812
|
+
}
|
|
45813
|
+
async function listOpenRouterModels(signal) {
|
|
45814
|
+
const key = process.env.OPENROUTER_API_KEY?.trim();
|
|
45815
|
+
if (!key) throw new Error("OPENROUTER_API_KEY n\xE3o configurada");
|
|
45816
|
+
const res = await fetch("https://openrouter.ai/api/v1/models", {
|
|
45817
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
45818
|
+
signal
|
|
45819
|
+
});
|
|
45820
|
+
if (!res.ok) {
|
|
45821
|
+
throw new Error(`OpenRouter /models falhou: HTTP ${res.status}`);
|
|
45822
|
+
}
|
|
45823
|
+
const json3 = await res.json();
|
|
45824
|
+
const data = Array.isArray(json3.data) ? json3.data : [];
|
|
45825
|
+
return data.map((raw) => {
|
|
45826
|
+
if (!isRecord(raw) || typeof raw.id !== "string") return null;
|
|
45827
|
+
const pricing = isRecord(raw.pricing) ? raw.pricing : void 0;
|
|
45828
|
+
return {
|
|
45829
|
+
id: raw.id,
|
|
45830
|
+
name: typeof raw.name === "string" && raw.name.trim() ? raw.name : raw.id,
|
|
45831
|
+
contextLength: typeof raw.context_length === "number" ? raw.context_length : void 0,
|
|
45832
|
+
promptPrice: typeof pricing?.prompt === "string" ? pricing.prompt : void 0
|
|
45833
|
+
};
|
|
45834
|
+
}).filter((m) => m !== null).sort((a, b) => a.name.localeCompare(b.name));
|
|
45835
|
+
}
|
|
45836
|
+
var isRecord, isXaiModelSlug, isGeminiModelSlug, requestCanRouteToXai, requestCanRouteToGemini, hasOwnEncryptedContent, stripEncryptedContent, sanitizeOpenRouterRequestForXai, hasJsonRefKey, wrapToolContentIfGeminiRefSensitive, sanitizeOpenRouterRequestForGeminiFunctionResponses, patchKimiReasoningToolCalls, OPENROUTER_METADATA_HEADER, withOpenRouterMetadataHeader, openrouterPatchFetch, openrouter2, openai2, isNvidiaMistralModel, applyNvidiaMistralConfig, nvidiaPatchFetch, nvidia, deepseek, kimi, buildProviderMap, hasEnvValue, isDeepSeekEnabled, isKimiEnabled, CLI_MODEL_CHAIN, baseProviders, modelCutoffDates, modelDisplayNames, getModelDisplayName, getModelCutoffDate, myProvider, OPENROUTER_KEY_PREFIX, hasOpenRouterKey, isOpenRouterDynamicKey, openRouterSlugFromKey, openRouterKeyForSlug;
|
|
45781
45837
|
var init_providers = __esm({
|
|
45782
45838
|
"../lib/ai/providers.ts"() {
|
|
45783
45839
|
"use strict";
|
|
@@ -46119,6 +46175,11 @@ var init_providers = __esm({
|
|
|
46119
46175
|
myProvider = customProvider({
|
|
46120
46176
|
languageModels: baseProviders
|
|
46121
46177
|
});
|
|
46178
|
+
OPENROUTER_KEY_PREFIX = "openrouter:";
|
|
46179
|
+
hasOpenRouterKey = () => Boolean(process.env.OPENROUTER_API_KEY?.trim());
|
|
46180
|
+
isOpenRouterDynamicKey = (key) => key.startsWith(OPENROUTER_KEY_PREFIX);
|
|
46181
|
+
openRouterSlugFromKey = (key) => key.slice(OPENROUTER_KEY_PREFIX.length);
|
|
46182
|
+
openRouterKeyForSlug = (slug) => `${OPENROUTER_KEY_PREFIX}${slug}`;
|
|
46122
46183
|
}
|
|
46123
46184
|
});
|
|
46124
46185
|
|
|
@@ -70372,6 +70433,9 @@ var init_local_sandbox = __esm({
|
|
|
70372
70433
|
["recon_ports.py", true],
|
|
70373
70434
|
["recon_content.py", true],
|
|
70374
70435
|
["recon_intel.py", true],
|
|
70436
|
+
["recon_routes.py", true],
|
|
70437
|
+
["exploit_engine.py", true],
|
|
70438
|
+
["harden.py", true],
|
|
70375
70439
|
["console_recon.js", true],
|
|
70376
70440
|
["README.md", true],
|
|
70377
70441
|
["scope.txt", false]
|
|
@@ -71335,12 +71399,23 @@ function balancedSpan(text2, open, close) {
|
|
|
71335
71399
|
function entryToBridgedCall(entry) {
|
|
71336
71400
|
if (!entry || typeof entry !== "object") return null;
|
|
71337
71401
|
const e = entry;
|
|
71402
|
+
const brief = typeof e.brief === "string" ? e.brief : "";
|
|
71403
|
+
const explicit = typeof e.tool === "string" ? e.tool : typeof e.tool_name === "string" ? e.tool_name : null;
|
|
71404
|
+
if (explicit && BRIDGEABLE_TOOLS.has(explicit)) {
|
|
71405
|
+
const nested = [e.input, e.args, e.parameters].find(
|
|
71406
|
+
(v) => v && typeof v === "object" && !Array.isArray(v)
|
|
71407
|
+
);
|
|
71408
|
+
const input = { brief, ...nested ?? e };
|
|
71409
|
+
delete input.tool;
|
|
71410
|
+
delete input.tool_name;
|
|
71411
|
+
return { toolName: explicit, input };
|
|
71412
|
+
}
|
|
71338
71413
|
if (typeof e.command === "string" && e.command.trim()) {
|
|
71339
71414
|
return {
|
|
71340
71415
|
toolName: "run_terminal_cmd",
|
|
71341
71416
|
input: {
|
|
71342
71417
|
command: e.command,
|
|
71343
|
-
brief
|
|
71418
|
+
brief,
|
|
71344
71419
|
is_background: e.is_background === true,
|
|
71345
71420
|
interactive: e.interactive === true,
|
|
71346
71421
|
timeout: typeof e.timeout === "number" ? e.timeout : 60
|
|
@@ -71353,13 +71428,25 @@ function entryToBridgedCall(entry) {
|
|
|
71353
71428
|
input: {
|
|
71354
71429
|
action: e.action,
|
|
71355
71430
|
path: e.path,
|
|
71356
|
-
brief
|
|
71431
|
+
brief,
|
|
71357
71432
|
...typeof e.text === "string" ? { text: e.text } : {},
|
|
71358
71433
|
...Array.isArray(e.range) ? { range: e.range } : {},
|
|
71359
71434
|
...Array.isArray(e.edits) ? { edits: e.edits } : {}
|
|
71360
71435
|
}
|
|
71361
71436
|
};
|
|
71362
71437
|
}
|
|
71438
|
+
if (typeof e.action === "string" && FINDINGS_ACTIONS.has(e.action)) {
|
|
71439
|
+
return { toolName: "findings", input: { ...e, brief } };
|
|
71440
|
+
}
|
|
71441
|
+
if (typeof e.url === "string" && e.url.trim()) {
|
|
71442
|
+
return { toolName: "http_request", input: { ...e, brief } };
|
|
71443
|
+
}
|
|
71444
|
+
if (Array.isArray(e.todos)) {
|
|
71445
|
+
return {
|
|
71446
|
+
toolName: "todo_write",
|
|
71447
|
+
input: { merge: e.merge === true, todos: e.todos }
|
|
71448
|
+
};
|
|
71449
|
+
}
|
|
71363
71450
|
return null;
|
|
71364
71451
|
}
|
|
71365
71452
|
function validateBatch(raw) {
|
|
@@ -71396,12 +71483,27 @@ function parseProxyToolCalls(text2) {
|
|
|
71396
71483
|
}
|
|
71397
71484
|
function summarizeBridgedCalls(calls) {
|
|
71398
71485
|
return calls.map((call) => {
|
|
71399
|
-
|
|
71400
|
-
|
|
71486
|
+
const i = call.input;
|
|
71487
|
+
switch (call.toolName) {
|
|
71488
|
+
case "run_terminal_cmd":
|
|
71489
|
+
return `$ ${truncateBridgeSummary(String(i.command ?? ""))}`;
|
|
71490
|
+
case "file":
|
|
71491
|
+
return `${String(i.action ?? "file")} ${truncateBridgeSummary(
|
|
71492
|
+
String(i.path ?? ""),
|
|
71493
|
+
120
|
|
71494
|
+
)}`.trim();
|
|
71495
|
+
case "http_request":
|
|
71496
|
+
return `${String(i.method ?? "GET")} ${truncateBridgeSummary(
|
|
71497
|
+
String(i.url ?? ""),
|
|
71498
|
+
120
|
|
71499
|
+
)}`.trim();
|
|
71500
|
+
case "findings":
|
|
71501
|
+
return `findings ${String(i.action ?? "")}`.trim();
|
|
71502
|
+
case "todo_write":
|
|
71503
|
+
return `todo_write (${Array.isArray(i.todos) ? i.todos.length : 0} itens)`;
|
|
71504
|
+
default:
|
|
71505
|
+
return call.toolName;
|
|
71401
71506
|
}
|
|
71402
|
-
const action = String(call.input.action ?? "file");
|
|
71403
|
-
const target = String(call.input.path ?? "");
|
|
71404
|
-
return `${action} ${truncateBridgeSummary(target, 120)}`.trim();
|
|
71405
71507
|
}).join("; ");
|
|
71406
71508
|
}
|
|
71407
71509
|
async function createAgent() {
|
|
@@ -71468,6 +71570,7 @@ async function createAgent() {
|
|
|
71468
71570
|
system += pythonOnlyPolicy();
|
|
71469
71571
|
system += deepReconPolicy(sandbox.getWorkdir());
|
|
71470
71572
|
system += reconPhasesPolicy(sandbox.getWorkdir());
|
|
71573
|
+
system += attackChainPolicy(sandbox.getWorkdir());
|
|
71471
71574
|
system += httpAndFindingsPolicy(sandbox.getWorkdir());
|
|
71472
71575
|
system += buildCliNotesSection();
|
|
71473
71576
|
system += buildSkillsIndexSection();
|
|
@@ -71565,6 +71668,33 @@ async function createAgent() {
|
|
|
71565
71668
|
selection: getModelSelection()
|
|
71566
71669
|
};
|
|
71567
71670
|
}
|
|
71671
|
+
if (isOpenRouterDynamicKey(raw)) {
|
|
71672
|
+
if (!hasOpenRouterKey()) {
|
|
71673
|
+
return {
|
|
71674
|
+
ok: false,
|
|
71675
|
+
message: "OpenRouter indispon\xEDvel: defina OPENROUTER_API_KEY em .env.local (https://openrouter.ai/keys)",
|
|
71676
|
+
selection: getModelSelection()
|
|
71677
|
+
};
|
|
71678
|
+
}
|
|
71679
|
+
const slug = openRouterSlugFromKey(raw).trim();
|
|
71680
|
+
if (!slug) {
|
|
71681
|
+
return {
|
|
71682
|
+
ok: false,
|
|
71683
|
+
message: "informe o modelo OpenRouter (ex.: openrouter:openai/gpt-4o)",
|
|
71684
|
+
selection: getModelSelection()
|
|
71685
|
+
};
|
|
71686
|
+
}
|
|
71687
|
+
const key = openRouterKeyForSlug(slug);
|
|
71688
|
+
selectedModelKey = key;
|
|
71689
|
+
currentModelName = key;
|
|
71690
|
+
context2.modelName = key;
|
|
71691
|
+
await rebuildSystemPrompt(key);
|
|
71692
|
+
return {
|
|
71693
|
+
ok: true,
|
|
71694
|
+
message: `modelo fixado: ${labelFor(key)}`,
|
|
71695
|
+
selection: getModelSelection()
|
|
71696
|
+
};
|
|
71697
|
+
}
|
|
71568
71698
|
const choices = getModelChoices();
|
|
71569
71699
|
let match;
|
|
71570
71700
|
if (/^\d+$/.test(normalized)) {
|
|
@@ -71674,6 +71804,28 @@ ${seen}`);
|
|
|
71674
71804
|
}
|
|
71675
71805
|
return sections.join("\n\n");
|
|
71676
71806
|
}
|
|
71807
|
+
let lastKimiChatId = null;
|
|
71808
|
+
async function reportKimiSession() {
|
|
71809
|
+
const base = process.env.KIMI_BASE_URL?.trim();
|
|
71810
|
+
if (!base) return;
|
|
71811
|
+
try {
|
|
71812
|
+
const url2 = `${base.replace(/\/+$/, "")}/session`;
|
|
71813
|
+
const controller = new AbortController();
|
|
71814
|
+
const timer2 = setTimeout(() => controller.abort(), 1500);
|
|
71815
|
+
const res = await fetch(url2, { signal: controller.signal }).finally(
|
|
71816
|
+
() => clearTimeout(timer2)
|
|
71817
|
+
);
|
|
71818
|
+
if (!res.ok) return;
|
|
71819
|
+
const info = await res.json();
|
|
71820
|
+
if (!info?.active || !info.chatId) return;
|
|
71821
|
+
const isNew = info.chatId !== lastKimiChatId;
|
|
71822
|
+
lastKimiChatId = info.chatId;
|
|
71823
|
+
render.info(
|
|
71824
|
+
`\u25B8 kimi: ${isNew ? "conversa \xFAnica ativa" : "mesma conversa"} (turno ${info.turns ?? "?"}) \u2014 ${info.url ?? info.chatId}`
|
|
71825
|
+
);
|
|
71826
|
+
} catch {
|
|
71827
|
+
}
|
|
71828
|
+
}
|
|
71677
71829
|
async function send(userInput, signal) {
|
|
71678
71830
|
history.push({ role: "user", content: userInput });
|
|
71679
71831
|
if (!auditMode && detectProjectAuditIntent(userInput)) {
|
|
@@ -71724,6 +71876,31 @@ ${seen}`);
|
|
|
71724
71876
|
}
|
|
71725
71877
|
let streamError = null;
|
|
71726
71878
|
let stalled = false;
|
|
71879
|
+
let turnText = "";
|
|
71880
|
+
const turnToolSummaries = [];
|
|
71881
|
+
const preservePartialWork = () => {
|
|
71882
|
+
const segs = [];
|
|
71883
|
+
const text2 = turnText.trim();
|
|
71884
|
+
if (text2) {
|
|
71885
|
+
segs.push(text2.length > 4e3 ? `...${text2.slice(-4e3)}` : text2);
|
|
71886
|
+
}
|
|
71887
|
+
if (turnToolSummaries.length) {
|
|
71888
|
+
segs.push(
|
|
71889
|
+
"A\xE7\xF5es j\xE1 executadas neste turno:\n" + turnToolSummaries.map((s) => `- ${s}`).join("\n")
|
|
71890
|
+
);
|
|
71891
|
+
}
|
|
71892
|
+
if (!segs.length) return;
|
|
71893
|
+
history.push({
|
|
71894
|
+
role: "assistant",
|
|
71895
|
+
content: `[Trabalho parcial de ${labelFor(
|
|
71896
|
+
modelKey
|
|
71897
|
+
)} antes de falhar \u2014 N\xC3O recomece do zero; continue exatamente a partir daqui:]
|
|
71898
|
+
|
|
71899
|
+
${segs.join(
|
|
71900
|
+
"\n\n"
|
|
71901
|
+
)}`
|
|
71902
|
+
});
|
|
71903
|
+
};
|
|
71727
71904
|
const stallController = new AbortController();
|
|
71728
71905
|
const onUserAbort = () => stallController.abort();
|
|
71729
71906
|
if (signal) {
|
|
@@ -71746,7 +71923,7 @@ ${seen}`);
|
|
|
71746
71923
|
};
|
|
71747
71924
|
try {
|
|
71748
71925
|
const result = streamText({
|
|
71749
|
-
model:
|
|
71926
|
+
model: resolveLanguageModel2(modelKey),
|
|
71750
71927
|
system: turnSystem,
|
|
71751
71928
|
messages: history,
|
|
71752
71929
|
tools,
|
|
@@ -71758,9 +71935,8 @@ ${seen}`);
|
|
|
71758
71935
|
}
|
|
71759
71936
|
});
|
|
71760
71937
|
let lastStepText = "";
|
|
71761
|
-
let bufferedProxyText = "";
|
|
71762
71938
|
let turnToolCalls = 0;
|
|
71763
|
-
const
|
|
71939
|
+
const isProxyModel = isWebSessionProxyModel(modelKey);
|
|
71764
71940
|
let pendingTools = 0;
|
|
71765
71941
|
const noteActivity = () => {
|
|
71766
71942
|
if (pendingTools === 0) armStallTimer();
|
|
@@ -71772,11 +71948,8 @@ ${seen}`);
|
|
|
71772
71948
|
case "text-delta": {
|
|
71773
71949
|
const delta = part.text ?? part.delta ?? "";
|
|
71774
71950
|
lastStepText += delta;
|
|
71775
|
-
|
|
71776
|
-
|
|
71777
|
-
} else {
|
|
71778
|
-
render.text(delta);
|
|
71779
|
-
}
|
|
71951
|
+
turnText += delta;
|
|
71952
|
+
render.text(delta);
|
|
71780
71953
|
noteActivity();
|
|
71781
71954
|
break;
|
|
71782
71955
|
}
|
|
@@ -71784,12 +71957,16 @@ ${seen}`);
|
|
|
71784
71957
|
render.reasoning(part.text ?? part.delta ?? "");
|
|
71785
71958
|
noteActivity();
|
|
71786
71959
|
break;
|
|
71787
|
-
case "tool-call":
|
|
71960
|
+
case "tool-call": {
|
|
71788
71961
|
turnToolCalls++;
|
|
71789
71962
|
pendingTools++;
|
|
71790
71963
|
clearStallTimer();
|
|
71791
|
-
|
|
71964
|
+
const input = part.input ?? part.args ?? {};
|
|
71965
|
+
const brief = typeof input.command === "string" ? `$ ${input.command}` : typeof input.path === "string" ? `${String(input.action ?? "file")} ${input.path}` : String(part.toolName);
|
|
71966
|
+
turnToolSummaries.push(truncateBridgeSummary(brief, 160));
|
|
71967
|
+
render.toolCall(part.toolName, input);
|
|
71792
71968
|
break;
|
|
71969
|
+
}
|
|
71793
71970
|
case "tool-result":
|
|
71794
71971
|
if (pendingTools > 0) pendingTools--;
|
|
71795
71972
|
render.toolResult(part.toolName, part.output ?? part.result);
|
|
@@ -71801,7 +71978,6 @@ ${seen}`);
|
|
|
71801
71978
|
break;
|
|
71802
71979
|
case "start-step":
|
|
71803
71980
|
lastStepText = "";
|
|
71804
|
-
bufferedProxyText = "";
|
|
71805
71981
|
noteActivity();
|
|
71806
71982
|
break;
|
|
71807
71983
|
case "error":
|
|
@@ -71834,8 +72010,8 @@ ${seen}`);
|
|
|
71834
72010
|
const finishReason = String(
|
|
71835
72011
|
await result.finishReason.catch(() => "unknown")
|
|
71836
72012
|
);
|
|
71837
|
-
const leakedCall = !
|
|
71838
|
-
if (finishReason === "stop" && turnToolCalls === 0 && bridgeSteps < MAX_BRIDGE_STEPS && (
|
|
72013
|
+
const leakedCall = !isProxyModel && hasLeakedToolCallMarker(lastStepText);
|
|
72014
|
+
if (finishReason === "stop" && turnToolCalls === 0 && bridgeSteps < MAX_BRIDGE_STEPS && (isProxyModel || leakedCall)) {
|
|
71839
72015
|
const bridged = parseProxyToolCalls(lastStepText);
|
|
71840
72016
|
if (bridged) {
|
|
71841
72017
|
bridgeSteps++;
|
|
@@ -71877,14 +72053,14 @@ ${resultText}`
|
|
|
71877
72053
|
idx--;
|
|
71878
72054
|
continue;
|
|
71879
72055
|
}
|
|
71880
|
-
if (shouldBufferProxyText && bufferedProxyText) {
|
|
71881
|
-
render.text(bufferedProxyText);
|
|
71882
|
-
}
|
|
71883
72056
|
if (finishReason !== "stop") {
|
|
71884
72057
|
render.info(
|
|
71885
72058
|
`\u25B8 fim do turno: ${finishReason}` + (autoContinues >= MAX_AUTO_CONTINUES ? " (limite de retomadas autom\xE1ticas atingido)" : "")
|
|
71886
72059
|
);
|
|
71887
72060
|
}
|
|
72061
|
+
if (modelKey === PROXY_MODEL_KEYS.kimi) {
|
|
72062
|
+
await reportKimiSession();
|
|
72063
|
+
}
|
|
71888
72064
|
render.endTurn();
|
|
71889
72065
|
return;
|
|
71890
72066
|
} catch (err) {
|
|
@@ -71902,6 +72078,7 @@ ${resultText}`
|
|
|
71902
72078
|
while (history.length && history[history.length - 1]?.role !== "user") {
|
|
71903
72079
|
history.pop();
|
|
71904
72080
|
}
|
|
72081
|
+
preservePartialWork();
|
|
71905
72082
|
const secs = Math.round(STREAM_STALL_TIMEOUT_MS / 1e3);
|
|
71906
72083
|
lastError = new Error(
|
|
71907
72084
|
`stream travou (${secs}s sem dados) em ${labelFor(modelKey)}`
|
|
@@ -71945,6 +72122,7 @@ ${resultText}`
|
|
|
71945
72122
|
while (history.length && history[history.length - 1]?.role !== "user") {
|
|
71946
72123
|
history.pop();
|
|
71947
72124
|
}
|
|
72125
|
+
preservePartialWork();
|
|
71948
72126
|
if (rateLimited && rateLimitWaits < MAX_RATE_LIMIT_WAITS) {
|
|
71949
72127
|
const waitMs = Math.min(3e3 * 2 ** rateLimitWaits, 15e3);
|
|
71950
72128
|
rateLimitWaits++;
|
|
@@ -71995,10 +72173,12 @@ ${resultText}`
|
|
|
71995
72173
|
getModelChoices,
|
|
71996
72174
|
getModelSelection,
|
|
71997
72175
|
setModelSelection,
|
|
72176
|
+
isOpenRouterAvailable: hasOpenRouterKey,
|
|
72177
|
+
listOpenRouterModels: (signal) => listOpenRouterModels(signal),
|
|
71998
72178
|
close
|
|
71999
72179
|
};
|
|
72000
72180
|
}
|
|
72001
|
-
var import_promises2, import_node_path10, MAX_STEPS, MAX_OUTPUT_TOKENS, MAX_AUTO_CONTINUES, MAX_RATE_LIMIT_WAITS, STREAM_STALL_TIMEOUT_MS, MAX_STALL_RETRIES, isRateLimitError, sleep4, LEAKED_TOOL_CALL_RE, DANGLING_TAIL_RE, ACTION_ANNOUNCE_RE, BENIGN_CLOSER_RE, TOOL_CALL_NUDGE, MAX_BRIDGE_STEPS, BRIDGE_RESULT_PREAMBLE, LEAKED_CALL_RESULT_PREAMBLE, truncateBridgeSummary, isWebSessionProxyModel, proxyToolProtocolPolicy, SYSTEM_PROMPT_SOURCE, REQUIRED_SYSTEM_PROMPT_MARKERS, MODEL_LABELS, labelFor, loginRequiredHint, CLI_PYTHON_ONLY_POLICY, pythonOnlyPolicy, scriptFilePolicy, deepReconPolicy, httpAndFindingsPolicy, reconPhasesPolicy, skillsScopePolicy, hasEnvValue2, envFlagEnabled, CLI_PERSONALITIES, buildCliUserCustomization, buildCliNotesSection, cliGuardrailsConfig, maybeDumpSystemPrompt, auditSystemPrompt, assertFullSystemPrompt;
|
|
72181
|
+
var import_promises2, import_node_path10, MAX_STEPS, MAX_OUTPUT_TOKENS, MAX_AUTO_CONTINUES, MAX_RATE_LIMIT_WAITS, STREAM_STALL_TIMEOUT_MS, MAX_STALL_RETRIES, isRateLimitError, sleep4, LEAKED_TOOL_CALL_RE, DANGLING_TAIL_RE, ACTION_ANNOUNCE_RE, BENIGN_CLOSER_RE, TOOL_CALL_NUDGE, MAX_BRIDGE_STEPS, BRIDGE_RESULT_PREAMBLE, LEAKED_CALL_RESULT_PREAMBLE, 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, hasEnvValue2, envFlagEnabled, CLI_PERSONALITIES, buildCliUserCustomization, buildCliNotesSection, cliGuardrailsConfig, maybeDumpSystemPrompt, auditSystemPrompt, assertFullSystemPrompt;
|
|
72002
72182
|
var init_agent = __esm({
|
|
72003
72183
|
"src/agent.ts"() {
|
|
72004
72184
|
"use strict";
|
|
@@ -72060,6 +72240,14 @@ var init_agent = __esm({
|
|
|
72060
72240
|
MAX_BRIDGE_STEPS = 50;
|
|
72061
72241
|
BRIDGE_RESULT_PREAMBLE = "Voc\xEA roda via proxy de sess\xE3o web (sem function-calling nativo), ent\xE3o o runtime do CLI consumiu o bloco JSON anterior como chamada interna e EXECUTOU de verdade. N\xE3o repita o bloco nem o texto anterior. Os resultados REAIS est\xE3o abaixo. Continue a tarefa: se precisar de mais a\xE7\xF5es, emita SOMENTE o PR\xD3XIMO bloco ```json (um array de {brief, command, timeout} para terminal, ou {action, path, text} para arquivo). Um bloco por vez. Quando a tarefa terminar, responda em texto normal SEM bloco json.";
|
|
72062
72242
|
LEAKED_CALL_RESULT_PREAMBLE = "Sua chamada de ferramenta anterior chegou como TEXTO (n\xE3o como function call nativo), ent\xE3o o runtime do CLI a executou mesmo assim. Os resultados REAIS est\xE3o abaixo. Continue a tarefa chamando as ferramentas NORMALMENTE (run_terminal_cmd, file, todo_write) como function calls de verdade \u2014 N\xC3O cole markup de tool call (`call_tool_function`, `<tool_call>`, etc.) nem JSON de chamada como texto na resposta.";
|
|
72243
|
+
BRIDGEABLE_TOOLS = /* @__PURE__ */ new Set([
|
|
72244
|
+
"run_terminal_cmd",
|
|
72245
|
+
"file",
|
|
72246
|
+
"findings",
|
|
72247
|
+
"http_request",
|
|
72248
|
+
"todo_write"
|
|
72249
|
+
]);
|
|
72250
|
+
FINDINGS_ACTIONS = /* @__PURE__ */ new Set(["add", "update", "list", "report"]);
|
|
72063
72251
|
truncateBridgeSummary = (value, max = 180) => value.length > max ? `${value.slice(0, max)}...` : value;
|
|
72064
72252
|
isWebSessionProxyModel = (modelName) => modelName === "model-deepseek-proxy" || modelName === "model-kimi-proxy";
|
|
72065
72253
|
proxyToolProtocolPolicy = () => `
|
|
@@ -72067,17 +72255,21 @@ var init_agent = __esm({
|
|
|
72067
72255
|
<web_session_tool_protocol>
|
|
72068
72256
|
ATIVO NESTA SESS\xC3O: o modelo atual roda por um proxy de sess\xE3o web e N\xC3O tem function-calling nativo. Para EXECUTAR qualquer a\xE7\xE3o voc\xEA n\xE3o chama ferramentas de fun\xE7\xE3o \u2014 voc\xEA EMITE um \xFAnico bloco de c\xF3digo \`\`\`json com um array de chamadas, e o runtime do CLI executa de verdade e te devolve os resultados como uma mensagem do usu\xE1rio. Repita o ciclo at\xE9 concluir.
|
|
72069
72257
|
|
|
72070
|
-
Formato EXATO (sempre um array, mesmo para uma \xFAnica a\xE7\xE3o):
|
|
72258
|
+
Formato EXATO (sempre um array, mesmo para uma \xFAnica a\xE7\xE3o). Voc\xEA pode SEMPRE incluir "tool": "<nome>" para deixar expl\xEDcito qual ferramenta chamar (recomendado para findings/http_request/todo_write):
|
|
72071
72259
|
- Terminal: {"brief": "uma frase", "command": "<comando shell>", "timeout": 60, "is_background": false}
|
|
72072
72260
|
- Arquivo: {"action": "write"|"edit"|"append"|"read", "path": "<caminho absoluto>", "brief": "uma frase", "text": "<conte\xFAdo completo para write/append>"}
|
|
72261
|
+
- HTTP (Repeater/Intruder): {"tool": "http_request", "brief": "uma frase", "url": "https://alvo/...", "method": "GET", "headers": {"X":"y"}, "body": "...", "fuzz": ["a","b"]}
|
|
72262
|
+
- Findings (mem\xF3ria estruturada de vulns): {"tool": "findings", "action": "add"|"update"|"list"|"report", "brief": "uma frase", "title": "...", "severity": "critical"|"high"|"medium"|"low"|"info", "evidence": "...", "id": "F-001"}
|
|
72263
|
+
- Tarefas (todo): {"tool": "todo_write", "merge": true, "todos": [{"id": "1", "content": "...", "status": "pending"|"in_progress"|"completed"}]}
|
|
72073
72264
|
|
|
72074
72265
|
Regras:
|
|
72075
72266
|
1. Para agir, a resposta DEVE conter um bloco \`\`\`json com o array. Esse bloco ser\xE1 consumido pelo CLI como chamada interna e n\xE3o deve ser tratado como texto de terminal.
|
|
72076
72267
|
2. UM bloco por turno; aguarde os resultados antes de emitir o pr\xF3ximo.
|
|
72077
|
-
3. Cada entrada do array deve mapear para uma a\xE7\xE3o (
|
|
72268
|
+
3. Cada entrada do array deve mapear para uma a\xE7\xE3o: terminal ("command"), arquivo ("action"+"path"), ou outra ferramenta via "tool" ("http_request" com "url", "findings" com "action" add/update/list/report, "todo_write" com "todos"). N\xE3o misture com dados que n\xE3o s\xE3o a\xE7\xF5es.
|
|
72078
72269
|
4. Crie/edite scripts e c\xF3digo-fonte SOMENTE via {"action":"write"} / {"action":"edit"} \u2014 nunca por redirecionamento de shell (echo >, cat <<EOF, tee, Out-File...).
|
|
72079
72270
|
5. Durante a\xE7\xF5es, coloque o conte\xFAdo operacional apenas dentro do JSON. Fora do JSON use no m\xE1ximo uma frase curta.
|
|
72080
72271
|
6. Quando a tarefa estiver conclu\xEDda, responda em texto normal SEM bloco json.
|
|
72272
|
+
7. VOC\xCA N\xC3O TEM AMBIENTE PR\xD3PRIO. Voc\xEA n\xE3o possui sandbox, m\xE1quina, terminal ou sistema de arquivos seu. Caminhos como /mnt, /mnt/agents, /mnt/kimi N\xC3O existem. A \xDANICA forma de listar pastas, ler arquivos ou rodar comandos \xE9 emitindo o bloco \`\`\`json acima \u2014 que o runtime executa na m\xE1quina REAL do operador (use o caminho/diret\xF3rio de trabalho informado no system prompt). NUNCA invente sa\xEDda de comando, conte\xFAdo de arquivo ou estrutura de diret\xF3rios; s\xF3 use o que voltar como resultado real. Para "analisar/olhar/listar <caminho>", sua PRIMEIRA resposta deve ser um bloco json, sem pre\xE2mbulo.
|
|
72081
72273
|
</web_session_tool_protocol>`;
|
|
72082
72274
|
SYSTEM_PROMPT_SOURCE = "lib/system-prompt.ts";
|
|
72083
72275
|
REQUIRED_SYSTEM_PROMPT_MARKERS = [
|
|
@@ -72105,7 +72297,7 @@ Regras:
|
|
|
72105
72297
|
"fallback-openai-chat-latest": "OpenAI - chat-latest",
|
|
72106
72298
|
"model-nvidia-nemotron": "NVIDIA - nemotron-3-ultra-550b"
|
|
72107
72299
|
};
|
|
72108
|
-
labelFor = (key) => MODEL_LABELS[key] ?? key;
|
|
72300
|
+
labelFor = (key) => isOpenRouterDynamicKey(key) ? `OpenRouter - ${openRouterSlugFromKey(key)}` : MODEL_LABELS[key] ?? key;
|
|
72109
72301
|
loginRequiredHint = (msg) => {
|
|
72110
72302
|
if (/account is suspended|violation of user policies|account has been suspended/i.test(
|
|
72111
72303
|
msg
|
|
@@ -72211,6 +72403,26 @@ Beyond recon/recon_deep.py (HTTP/JS/secret/browser recon), the workspace at ${wo
|
|
|
72211
72403
|
|
|
72212
72404
|
Each module writes a JSON (and where useful Markdown) report under ./reports/. Read it, then record anything actionable with the findings tool. You MAY extend any module via the file tool \u2014 keep it Python and keep the scope gate intact.
|
|
72213
72405
|
</recon_phases>`;
|
|
72406
|
+
attackChainPolicy = (workdir) => `
|
|
72407
|
+
|
|
72408
|
+
<attack_and_remediation_chain>
|
|
72409
|
+
ABSOLUTE RULE FOR THIS LOCAL CLI SESSION \u2014 applies to EVERY model and EVERY task:
|
|
72410
|
+
|
|
72411
|
+
When the operator asks you to "attack", "explore vulnerabilities", "exploit", "pentest hard", "find a way in", or to FIX/close a vulnerability, follow the seeded offensive\u2192defensive chain at ${workdir}/recon. Do NOT hand-roll a one-off exploit script \u2014 these modules are the canonical engines, scope-gated by recon/scope.txt, AUTHORIZED-USE-ONLY, and strictly NON-DESTRUCTIVE (they prove impact; they never DROP data, mass-exfiltrate, or DoS).
|
|
72412
|
+
|
|
72413
|
+
1. MAP THE ROUTES (saber as rotas): \`python recon/recon_routes.py https://alvo.com --wayback\`
|
|
72414
|
+
Aggregates crawl + HTML forms + JS endpoints + robots/sitemap + Swagger/OpenAPI (+ Wayback) into ONE route map, annotating each route with its parameters and whether it needs auth. Writes reports/routes_<host>.json. This is the attack surface.
|
|
72415
|
+
|
|
72416
|
+
2. ATTACK & PROVE (o ataque): \`python recon/exploit_engine.py --routes reports/routes_<host>.json\`
|
|
72417
|
+
(or a single target: \`python recon/exploit_engine.py "https://alvo/item?id=1" --classes sqli,xss,lfi,cmdi,ssrf,redir\`). Runs verified playbooks per OWASP class (SQLi error/boolean/time-based, reflected XSS, LFI/traversal, command injection, SSRF, open redirect, IDOR with --idor-range). It only marks a finding "confirmed" when the LIVE response proves impact, capturing the exact payload + evidence. Use --cookie / --header for authenticated endpoints, --sleep for time-based, --oob for SSRF callbacks. Writes reports/exploit_<host>.json.
|
|
72418
|
+
|
|
72419
|
+
3. RECORD: take every CONFIRMED finding from the exploit report and store it with the \`findings\` tool (status confirmed, paste the evidence). This is the engagement memory and the executive report.
|
|
72420
|
+
|
|
72421
|
+
4. CLOSE THE BREACH (fechar a brecha para n\xE3o ter acesso de novo): \`python recon/harden.py --report reports/exploit_<host>.json\`
|
|
72422
|
+
For each confirmed finding it emits the concrete fix (parameterized queries, output encoding, allowlist validation, security headers, WAF rule) AND REPLAYS the PoC to verify: "FIXED" only once the exploit no longer reproduces. After the operator applies a fix, RUN harden.py AGAIN \u2014 a vuln is only closed when its verdict flips to FIXED \u2705. Report what is still STILL OPEN \u274C.
|
|
72423
|
+
|
|
72424
|
+
Dependencies (optional, improve fidelity): \`python -m pip install requests\`. You MAY extend any module via the file tool \u2014 keep it Python, keep the scope gate, keep it non-destructive.
|
|
72425
|
+
</attack_and_remediation_chain>`;
|
|
72214
72426
|
skillsScopePolicy = () => `
|
|
72215
72427
|
|
|
72216
72428
|
<skills_scope>
|
|
@@ -72430,15 +72642,107 @@ var init_interactive_input = __esm({
|
|
|
72430
72642
|
};
|
|
72431
72643
|
});
|
|
72432
72644
|
}
|
|
72645
|
+
/**
|
|
72646
|
+
* Searchable single-choice list: a live filter bar on top, a scrolling
|
|
72647
|
+
* viewport of matches below. Type to filter, ↑/↓ to move (wraps), Enter to
|
|
72648
|
+
* pick, Esc / Ctrl+C to cancel. Built for big catalogs (e.g. every OpenRouter
|
|
72649
|
+
* model). Returns the chosen item's ORIGINAL index, or null on cancel.
|
|
72650
|
+
*/
|
|
72651
|
+
searchSelect(title, items, opts = {}) {
|
|
72652
|
+
const pageSize = Math.max(4, opts.pageSize ?? 10);
|
|
72653
|
+
const placeholder = opts.placeholder ?? "digite para filtrar";
|
|
72654
|
+
let query = "";
|
|
72655
|
+
let sel = 0;
|
|
72656
|
+
let top = 0;
|
|
72657
|
+
this.lastRows = 0;
|
|
72658
|
+
out2("\n");
|
|
72659
|
+
const filtered = () => {
|
|
72660
|
+
const q = query.toLowerCase().trim();
|
|
72661
|
+
const all = items.map((it, i) => ({ it, i }));
|
|
72662
|
+
if (!q) return all;
|
|
72663
|
+
return all.filter(
|
|
72664
|
+
({ it }) => it.label.toLowerCase().includes(q) || (it.hint ?? "").toLowerCase().includes(q)
|
|
72665
|
+
);
|
|
72666
|
+
};
|
|
72667
|
+
const draw = () => {
|
|
72668
|
+
const list = filtered();
|
|
72669
|
+
if (sel >= list.length) sel = Math.max(0, list.length - 1);
|
|
72670
|
+
if (sel < top) top = sel;
|
|
72671
|
+
if (sel >= top + pageSize) top = sel - pageSize + 1;
|
|
72672
|
+
this.renderSearchList(
|
|
72673
|
+
title,
|
|
72674
|
+
placeholder,
|
|
72675
|
+
query,
|
|
72676
|
+
list,
|
|
72677
|
+
sel,
|
|
72678
|
+
top,
|
|
72679
|
+
pageSize,
|
|
72680
|
+
items.length
|
|
72681
|
+
);
|
|
72682
|
+
};
|
|
72683
|
+
draw();
|
|
72684
|
+
return new Promise((resolve2) => {
|
|
72685
|
+
const finish = (value) => {
|
|
72686
|
+
this.collapseTo(this.lastRows);
|
|
72687
|
+
this.onKey = null;
|
|
72688
|
+
resolve2(value);
|
|
72689
|
+
};
|
|
72690
|
+
this.onKey = (str, key) => {
|
|
72691
|
+
if (key.ctrl && key.name === "c") return finish(null);
|
|
72692
|
+
const list = filtered();
|
|
72693
|
+
switch (key.name) {
|
|
72694
|
+
case "escape":
|
|
72695
|
+
return finish(null);
|
|
72696
|
+
case "return":
|
|
72697
|
+
if (list.length === 0) return;
|
|
72698
|
+
return finish(list[sel].i);
|
|
72699
|
+
case "up":
|
|
72700
|
+
if (list.length) sel = (sel - 1 + list.length) % list.length;
|
|
72701
|
+
draw();
|
|
72702
|
+
return;
|
|
72703
|
+
case "down":
|
|
72704
|
+
case "tab":
|
|
72705
|
+
if (list.length) sel = (sel + 1) % list.length;
|
|
72706
|
+
draw();
|
|
72707
|
+
return;
|
|
72708
|
+
case "backspace":
|
|
72709
|
+
if (query.length) {
|
|
72710
|
+
query = query.slice(0, -1);
|
|
72711
|
+
sel = 0;
|
|
72712
|
+
top = 0;
|
|
72713
|
+
}
|
|
72714
|
+
draw();
|
|
72715
|
+
return;
|
|
72716
|
+
}
|
|
72717
|
+
if (key.ctrl && key.name === "u") {
|
|
72718
|
+
query = "";
|
|
72719
|
+
sel = 0;
|
|
72720
|
+
top = 0;
|
|
72721
|
+
draw();
|
|
72722
|
+
return;
|
|
72723
|
+
}
|
|
72724
|
+
if (str && !key.ctrl && !key.meta && str.charCodeAt(0) >= 32) {
|
|
72725
|
+
query += str;
|
|
72726
|
+
sel = 0;
|
|
72727
|
+
top = 0;
|
|
72728
|
+
draw();
|
|
72729
|
+
}
|
|
72730
|
+
};
|
|
72731
|
+
});
|
|
72732
|
+
}
|
|
72433
72733
|
/**
|
|
72434
72734
|
* Lightweight line reader used WHILE a turn is running, so typed lines can be
|
|
72435
72735
|
* forwarded to an interactive command's stdin. No box/dropdown — just echoes
|
|
72436
72736
|
* and collects until Enter. Ctrl+C invokes `onSigint`.
|
|
72437
72737
|
*/
|
|
72438
|
-
beginTurn(onLine, onSigint) {
|
|
72738
|
+
beginTurn(onLine, onSigint, isActive) {
|
|
72439
72739
|
let buf = "";
|
|
72440
72740
|
this.onKey = (str, key) => {
|
|
72441
72741
|
if (key.ctrl && key.name === "c") return onSigint();
|
|
72742
|
+
if (!isActive()) {
|
|
72743
|
+
buf = "";
|
|
72744
|
+
return;
|
|
72745
|
+
}
|
|
72442
72746
|
if (key.name === "return") {
|
|
72443
72747
|
out2("\r\n");
|
|
72444
72748
|
const line = this.paste.expand(buf);
|
|
@@ -72646,12 +72950,12 @@ var init_interactive_input = __esm({
|
|
|
72646
72950
|
const prefix = frame2("\u2570\u2500") + gradient("\u276F", { bold: true }) + " ";
|
|
72647
72951
|
const prefixLen = 4;
|
|
72648
72952
|
const avail = Math.max(8, width - prefixLen - 1);
|
|
72649
|
-
|
|
72650
|
-
|
|
72651
|
-
|
|
72652
|
-
|
|
72653
|
-
|
|
72654
|
-
const lines = [top, idLine,
|
|
72953
|
+
const { textLines, caretLine, caretCol } = this.wrap(
|
|
72954
|
+
prefix,
|
|
72955
|
+
prefixLen,
|
|
72956
|
+
avail
|
|
72957
|
+
);
|
|
72958
|
+
const lines = [top, idLine, ...textLines];
|
|
72655
72959
|
const items = this.dropdownItems();
|
|
72656
72960
|
if (items.length > 0) {
|
|
72657
72961
|
this.ddSel = Math.min(this.ddSel, items.length - 1);
|
|
@@ -72676,9 +72980,27 @@ var init_interactive_input = __esm({
|
|
|
72676
72980
|
} else {
|
|
72677
72981
|
this.ddSel = 0;
|
|
72678
72982
|
}
|
|
72679
|
-
this.inputLineIndex = 2;
|
|
72983
|
+
this.inputLineIndex = 2 + caretLine;
|
|
72680
72984
|
this.paint(lines, caretCol);
|
|
72681
72985
|
}
|
|
72986
|
+
/**
|
|
72987
|
+
* Split the buffer into terminal-width rows. The first row carries `prefix`
|
|
72988
|
+
* (e.g. "❯ "); continuation rows are indented by `prefixLen` spaces so the
|
|
72989
|
+
* text columns line up. Also returns which wrapped row the caret sits on and
|
|
72990
|
+
* its absolute column, so paint() can place the cursor correctly.
|
|
72991
|
+
*/
|
|
72992
|
+
wrap(prefix, prefixLen, avail) {
|
|
72993
|
+
const indent = " ".repeat(prefixLen);
|
|
72994
|
+
const caretLine = Math.floor(this.cursor / avail);
|
|
72995
|
+
const caretColInLine = this.cursor - caretLine * avail;
|
|
72996
|
+
const chunks = [];
|
|
72997
|
+
for (let i = 0; i < this.buffer.length; i += avail) {
|
|
72998
|
+
chunks.push(this.buffer.slice(i, i + avail));
|
|
72999
|
+
}
|
|
73000
|
+
while (chunks.length <= caretLine) chunks.push("");
|
|
73001
|
+
const textLines = chunks.map((c, i) => (i === 0 ? prefix : indent) + c);
|
|
73002
|
+
return { textLines, caretLine, caretCol: prefixLen + caretColInLine };
|
|
73003
|
+
}
|
|
72682
73004
|
/** Compact single-line labelled prompt ("nome ❯ …") for step-by-step capture. */
|
|
72683
73005
|
renderCompact() {
|
|
72684
73006
|
const width = cols();
|
|
@@ -72686,12 +73008,13 @@ var init_interactive_input = __esm({
|
|
|
72686
73008
|
const prefix = `${gradient(label, { bold: true })} ${gradient("\u276F", { bold: true })} `;
|
|
72687
73009
|
const prefixLen = vlen(`${label} \u276F `);
|
|
72688
73010
|
const avail = Math.max(8, width - prefixLen - 1);
|
|
72689
|
-
|
|
72690
|
-
|
|
72691
|
-
|
|
72692
|
-
|
|
72693
|
-
|
|
72694
|
-
this.
|
|
73011
|
+
const { textLines, caretLine, caretCol } = this.wrap(
|
|
73012
|
+
prefix,
|
|
73013
|
+
prefixLen,
|
|
73014
|
+
avail
|
|
73015
|
+
);
|
|
73016
|
+
this.inputLineIndex = caretLine;
|
|
73017
|
+
this.paint(textLines, caretCol);
|
|
72695
73018
|
}
|
|
72696
73019
|
/** Repaint the whole block, then place the cursor on the active input line. */
|
|
72697
73020
|
paint(lines, caretCol) {
|
|
@@ -72736,6 +73059,42 @@ var init_interactive_input = __esm({
|
|
|
72736
73059
|
);
|
|
72737
73060
|
this.paintBlock(lines);
|
|
72738
73061
|
}
|
|
73062
|
+
/** Render the search bar + a scrolling viewport of filtered matches. */
|
|
73063
|
+
renderSearchList(title, placeholder, query, list, sel, top, pageSize, total) {
|
|
73064
|
+
const width = cols();
|
|
73065
|
+
const inner = Math.min(Math.max(40, width - 4), 80);
|
|
73066
|
+
const lines = [];
|
|
73067
|
+
lines.push(`${frame2("\u256D\u2500\u25C6 ")}${gradient(title, { bold: true })}`);
|
|
73068
|
+
const shown = query.length ? query : `${C4.dim}${placeholder}${C4.reset}`;
|
|
73069
|
+
const counter = query.trim() ? `${C4.dim}${list.length}/${total}${C4.reset}` : `${C4.dim}${total}${C4.reset}`;
|
|
73070
|
+
lines.push(
|
|
73071
|
+
`${frame2("\u2502 ")}${gradient("\u2315", { bold: true })} ${shown}${C4.reset} ${counter}`
|
|
73072
|
+
);
|
|
73073
|
+
lines.push(frame2("\u2502"));
|
|
73074
|
+
if (list.length === 0) {
|
|
73075
|
+
lines.push(`${frame2("\u2502 ")}${C4.dim}nenhum modelo corresponde${C4.reset}`);
|
|
73076
|
+
} else {
|
|
73077
|
+
const end = Math.min(top + pageSize, list.length);
|
|
73078
|
+
for (let i = top; i < end; i++) {
|
|
73079
|
+
const active2 = i === sel;
|
|
73080
|
+
const { it } = list[i];
|
|
73081
|
+
const marker25 = active2 ? gradient("\u276F ", { bold: true }) : `${C4.dim} `;
|
|
73082
|
+
const text2 = active2 ? gradient(it.label, { bold: true }) : `${C4.reset}${it.label}`;
|
|
73083
|
+
const labelW = vlen(it.label);
|
|
73084
|
+
const hint = it.hint ? ` ${C4.dim}${truncate4(it.hint, Math.max(6, inner - labelW - 6))}${C4.reset}` : "";
|
|
73085
|
+
lines.push(`${frame2("\u2502 ")}${C4.reset}${marker25}${text2}${C4.reset}${hint}`);
|
|
73086
|
+
}
|
|
73087
|
+
if (list.length > pageSize) {
|
|
73088
|
+
lines.push(
|
|
73089
|
+
`${frame2("\u2502 ")}${C4.dim}${sel + 1}/${list.length}${C4.reset}`
|
|
73090
|
+
);
|
|
73091
|
+
}
|
|
73092
|
+
}
|
|
73093
|
+
lines.push(
|
|
73094
|
+
`${frame2("\u2570\u2500")} ${C4.dim}digite filtra \xB7 \u2191\u2193 navega \xB7 Enter seleciona \xB7 Esc cancela${C4.reset}`
|
|
73095
|
+
);
|
|
73096
|
+
this.paintBlock(lines);
|
|
73097
|
+
}
|
|
72739
73098
|
/** Repaint a block and leave the cursor just below it (for list/menu modes). */
|
|
72740
73099
|
paintBlock(lines) {
|
|
72741
73100
|
let s = "";
|
|
@@ -72793,8 +73152,8 @@ async function main() {
|
|
|
72793
73152
|
`
|
|
72794
73153
|
);
|
|
72795
73154
|
const COMMANDS = [
|
|
72796
|
-
{ name: "/model", desc: "trocar o modelo (
|
|
72797
|
-
{ name: "/api", desc: "trocar
|
|
73155
|
+
{ name: "/model", desc: "trocar o modelo (setas; OpenRouter c/ busca)" },
|
|
73156
|
+
{ name: "/api", desc: "trocar chave de API (NVIDIA / OpenRouter)" },
|
|
72798
73157
|
{ name: "/skills", desc: "listar as skills instaladas" },
|
|
72799
73158
|
{ name: "/skillcreator", desc: "criar uma nova skill" },
|
|
72800
73159
|
{ name: "/system", desc: "salvar o system prompt (HTML) na \xC1rea de Trabalho" },
|
|
@@ -73047,69 +73406,66 @@ ${C5.dim}ja disponivel para todos os modelos nesta sessao.${C5.reset}
|
|
|
73047
73406
|
`
|
|
73048
73407
|
);
|
|
73049
73408
|
};
|
|
73050
|
-
const
|
|
73051
|
-
|
|
73052
|
-
|
|
73053
|
-
|
|
73054
|
-
|
|
73055
|
-
|
|
73056
|
-
|
|
73057
|
-
|
|
73058
|
-
|
|
73059
|
-
|
|
73060
|
-
|
|
73061
|
-
|
|
73062
|
-
|
|
73063
|
-
|
|
73064
|
-
|
|
73065
|
-
|
|
73066
|
-
|
|
73067
|
-
|
|
73068
|
-
|
|
73069
|
-
|
|
73070
|
-
try {
|
|
73071
|
-
printModelResult(await agent.setModelSelection(arg));
|
|
73072
|
-
} catch (err) {
|
|
73073
|
-
printFatal(err);
|
|
73409
|
+
const KEY_PROVIDERS = [
|
|
73410
|
+
{
|
|
73411
|
+
id: "nvidia",
|
|
73412
|
+
name: "NVIDIA",
|
|
73413
|
+
envVar: "NVIDIA_API_KEY",
|
|
73414
|
+
createUrl: "https://build.nvidia.com/",
|
|
73415
|
+
keyRegex: /nvapi-[A-Za-z0-9_-]+/,
|
|
73416
|
+
keyHint: "nvapi-\u2026",
|
|
73417
|
+
test: testNvidiaKey,
|
|
73418
|
+
swap: swapNvidiaKey
|
|
73419
|
+
},
|
|
73420
|
+
{
|
|
73421
|
+
id: "openrouter",
|
|
73422
|
+
name: "OpenRouter",
|
|
73423
|
+
envVar: "OPENROUTER_API_KEY",
|
|
73424
|
+
createUrl: "https://openrouter.ai/keys",
|
|
73425
|
+
keyRegex: /sk-or-[A-Za-z0-9_-]+/,
|
|
73426
|
+
keyHint: "sk-or-\u2026",
|
|
73427
|
+
test: testOpenRouterKey,
|
|
73428
|
+
swap: swapOpenRouterKey
|
|
73074
73429
|
}
|
|
73075
|
-
|
|
73076
|
-
const
|
|
73430
|
+
];
|
|
73431
|
+
const openRouterProvider = KEY_PROVIDERS.find((p) => p.id === "openrouter");
|
|
73432
|
+
const promptAndSwapKey = async (provider, inlineKey = "") => {
|
|
73077
73433
|
let raw = inlineKey;
|
|
73078
73434
|
if (!raw) {
|
|
73079
73435
|
process.stdout.write(
|
|
73080
|
-
`${C5.dim}Cole a nova chave
|
|
73436
|
+
`${C5.dim}Cole a nova chave ${provider.name} (cria em ${C5.reset}${C5.cyan}${provider.createUrl}${C5.reset}${C5.dim}). Enter vazio ou Ctrl+C cancela.${C5.reset}
|
|
73081
73437
|
`
|
|
73082
73438
|
);
|
|
73083
73439
|
const res = await inputUI.prompt({
|
|
73084
|
-
label:
|
|
73440
|
+
label: `nova ${provider.name} API key`,
|
|
73085
73441
|
secret: true
|
|
73086
73442
|
});
|
|
73087
73443
|
if (res.type !== "line" || !res.value.trim()) {
|
|
73088
73444
|
process.stdout.write(`${C5.dim}troca de chave cancelada${C5.reset}
|
|
73089
73445
|
`);
|
|
73090
|
-
return;
|
|
73446
|
+
return false;
|
|
73091
73447
|
}
|
|
73092
73448
|
raw = res.value;
|
|
73093
73449
|
}
|
|
73094
|
-
const key = raw.replace(/\s+/g, "").match(
|
|
73450
|
+
const key = raw.replace(/\s+/g, "").match(provider.keyRegex)?.[0] ?? "";
|
|
73095
73451
|
if (!key) {
|
|
73096
73452
|
process.stdout.write(
|
|
73097
|
-
`${C5.red}\u2717 n\xE3o encontrei uma chave
|
|
73453
|
+
`${C5.red}\u2717 n\xE3o encontrei uma chave ${provider.name} no que foi colado${C5.reset} ${C5.dim}(esperado um token "${provider.keyHint}"; cole a chave completa).${C5.reset}
|
|
73098
73454
|
`
|
|
73099
73455
|
);
|
|
73100
|
-
return;
|
|
73456
|
+
return false;
|
|
73101
73457
|
}
|
|
73102
|
-
process.stdout.write(`${C5.dim}testando a chave na
|
|
73458
|
+
process.stdout.write(`${C5.dim}testando a chave na ${provider.name}\u2026${C5.reset}
|
|
73103
73459
|
`);
|
|
73104
|
-
const test = await
|
|
73460
|
+
const test = await provider.test(key);
|
|
73105
73461
|
if (!test.ok) {
|
|
73106
73462
|
if (test.status === 401 || test.status === 403) {
|
|
73107
73463
|
process.stdout.write(
|
|
73108
|
-
`${C5.red}\u2717 a
|
|
73109
|
-
${C5.dim}chave N\xC3O salva \u2014 confira se copiou inteira e se a conta tem acesso
|
|
73464
|
+
`${C5.red}\u2717 a ${provider.name} recusou a chave (${test.status}): ${test.detail}${C5.reset}
|
|
73465
|
+
${C5.dim}chave N\xC3O salva \u2014 confira se copiou inteira e se a conta tem acesso.${C5.reset}
|
|
73110
73466
|
`
|
|
73111
73467
|
);
|
|
73112
|
-
return;
|
|
73468
|
+
return false;
|
|
73113
73469
|
}
|
|
73114
73470
|
process.stdout.write(
|
|
73115
73471
|
`${C5.yellow}\u26A0 n\xE3o consegui validar (${test.status || "rede"}): ${test.detail}${C5.reset}
|
|
@@ -73117,13 +73473,123 @@ ${C5.dim}salvando mesmo assim \u2014 o pr\xF3ximo pedido vai usar a chave nova.$
|
|
|
73117
73473
|
`
|
|
73118
73474
|
);
|
|
73119
73475
|
}
|
|
73120
|
-
const file2 =
|
|
73476
|
+
const file2 = provider.swap(key);
|
|
73121
73477
|
const masked = `${key.slice(0, 9)}\u2026${key.slice(-4)} (${key.length} chars)`;
|
|
73122
73478
|
process.stdout.write(
|
|
73123
|
-
`${C5.green}\u2713 chave
|
|
73479
|
+
`${C5.green}\u2713 chave ${provider.name} trocada e ativa${C5.reset} ${C5.dim}${masked}${C5.reset}
|
|
73124
73480
|
${C5.dim}salva em ${C5.reset}${C5.cyan}${file2}${C5.reset}
|
|
73125
73481
|
`
|
|
73126
73482
|
);
|
|
73483
|
+
return true;
|
|
73484
|
+
};
|
|
73485
|
+
const openOpenRouterPicker = async () => {
|
|
73486
|
+
if (!agent.isOpenRouterAvailable()) {
|
|
73487
|
+
process.stdout.write(
|
|
73488
|
+
`${C5.dim}OpenRouter ainda sem chave. Vamos adicionar uma.${C5.reset}
|
|
73489
|
+
`
|
|
73490
|
+
);
|
|
73491
|
+
const ok = await promptAndSwapKey(openRouterProvider);
|
|
73492
|
+
if (!ok) return;
|
|
73493
|
+
}
|
|
73494
|
+
process.stdout.write(`${C5.dim}buscando cat\xE1logo OpenRouter\u2026${C5.reset}
|
|
73495
|
+
`);
|
|
73496
|
+
let models;
|
|
73497
|
+
try {
|
|
73498
|
+
models = await agent.listOpenRouterModels();
|
|
73499
|
+
} catch (err) {
|
|
73500
|
+
process.stdout.write(
|
|
73501
|
+
`${C5.red}\u2717 falha ao listar modelos OpenRouter: ${err instanceof Error ? err.message : String(err)}${C5.reset}
|
|
73502
|
+
`
|
|
73503
|
+
);
|
|
73504
|
+
return;
|
|
73505
|
+
}
|
|
73506
|
+
if (models.length === 0) {
|
|
73507
|
+
process.stdout.write(
|
|
73508
|
+
`${C5.yellow}\u26A0 a OpenRouter n\xE3o retornou nenhum modelo${C5.reset}
|
|
73509
|
+
`
|
|
73510
|
+
);
|
|
73511
|
+
return;
|
|
73512
|
+
}
|
|
73513
|
+
const items = models.map((m) => ({
|
|
73514
|
+
label: m.name,
|
|
73515
|
+
hint: `${m.id}${m.promptPrice === "0" ? " \xB7 free" : ""}`
|
|
73516
|
+
}));
|
|
73517
|
+
const choice2 = await inputUI.searchSelect(
|
|
73518
|
+
"OpenRouter \u2014 buscar modelo",
|
|
73519
|
+
items,
|
|
73520
|
+
{ placeholder: "digite para filtrar (nome ou slug)" }
|
|
73521
|
+
);
|
|
73522
|
+
if (choice2 === null) {
|
|
73523
|
+
process.stdout.write(`${C5.dim}sele\xE7\xE3o de modelo cancelada${C5.reset}
|
|
73524
|
+
`);
|
|
73525
|
+
return;
|
|
73526
|
+
}
|
|
73527
|
+
try {
|
|
73528
|
+
printModelResult(
|
|
73529
|
+
await agent.setModelSelection(`openrouter:${models[choice2].id}`)
|
|
73530
|
+
);
|
|
73531
|
+
} catch (err) {
|
|
73532
|
+
printFatal(err);
|
|
73533
|
+
}
|
|
73534
|
+
};
|
|
73535
|
+
const openModelSelector = async () => {
|
|
73536
|
+
const state = agent.getModelSelection();
|
|
73537
|
+
const orAvailable = agent.isOpenRouterAvailable();
|
|
73538
|
+
const items = [
|
|
73539
|
+
{
|
|
73540
|
+
label: "Auto \u2014 cadeia de fallback autom\xE1tica",
|
|
73541
|
+
hint: "tenta os modelos em ordem"
|
|
73542
|
+
},
|
|
73543
|
+
...state.chain.map((c) => ({ label: c.label, hint: c.key }))
|
|
73544
|
+
];
|
|
73545
|
+
const openRouterIdx = items.length;
|
|
73546
|
+
items.push({
|
|
73547
|
+
label: "OpenRouter \u2014 buscar no cat\xE1logo completo",
|
|
73548
|
+
hint: orAvailable ? "lista todos os modelos da sua conta" : "pede a API key e lista o cat\xE1logo"
|
|
73549
|
+
});
|
|
73550
|
+
const activeIsOpenRouter = state.activeModelKey.startsWith("openrouter:");
|
|
73551
|
+
const activeIdx = state.mode === "auto" ? 0 : activeIsOpenRouter ? openRouterIdx : Math.max(
|
|
73552
|
+
0,
|
|
73553
|
+
1 + state.chain.findIndex((c) => c.key === state.activeModelKey)
|
|
73554
|
+
);
|
|
73555
|
+
const choice2 = await inputUI.select("selecionar modelo", items, activeIdx);
|
|
73556
|
+
if (choice2 === null) {
|
|
73557
|
+
process.stdout.write(`${C5.dim}sele\xE7\xE3o de modelo cancelada${C5.reset}
|
|
73558
|
+
`);
|
|
73559
|
+
return;
|
|
73560
|
+
}
|
|
73561
|
+
if (choice2 === openRouterIdx) {
|
|
73562
|
+
await openOpenRouterPicker();
|
|
73563
|
+
return;
|
|
73564
|
+
}
|
|
73565
|
+
const arg = choice2 === 0 ? "auto" : state.chain[choice2 - 1].key;
|
|
73566
|
+
try {
|
|
73567
|
+
printModelResult(await agent.setModelSelection(arg));
|
|
73568
|
+
} catch (err) {
|
|
73569
|
+
printFatal(err);
|
|
73570
|
+
}
|
|
73571
|
+
};
|
|
73572
|
+
const handleApiSwap = async (inlineKey) => {
|
|
73573
|
+
if (inlineKey) {
|
|
73574
|
+
const stripped = inlineKey.replace(/\s+/g, "");
|
|
73575
|
+
const provider = KEY_PROVIDERS.find((p) => p.keyRegex.test(stripped)) ?? KEY_PROVIDERS[0];
|
|
73576
|
+
await promptAndSwapKey(provider, inlineKey);
|
|
73577
|
+
return;
|
|
73578
|
+
}
|
|
73579
|
+
const idx = await inputUI.select(
|
|
73580
|
+
"trocar chave de API",
|
|
73581
|
+
KEY_PROVIDERS.map((p) => ({
|
|
73582
|
+
label: p.name,
|
|
73583
|
+
hint: process.env[p.envVar]?.trim() ? `${p.envVar} (configurada)` : p.envVar
|
|
73584
|
+
})),
|
|
73585
|
+
0
|
|
73586
|
+
);
|
|
73587
|
+
if (idx === null) {
|
|
73588
|
+
process.stdout.write(`${C5.dim}troca de chave cancelada${C5.reset}
|
|
73589
|
+
`);
|
|
73590
|
+
return;
|
|
73591
|
+
}
|
|
73592
|
+
await promptAndSwapKey(KEY_PROVIDERS[idx]);
|
|
73127
73593
|
};
|
|
73128
73594
|
const handleLine = async (line) => {
|
|
73129
73595
|
if (closing) return;
|
|
@@ -73150,6 +73616,10 @@ ${C5.dim}salva em ${C5.reset}${C5.cyan}${file2}${C5.reset}
|
|
|
73150
73616
|
}
|
|
73151
73617
|
if (input.startsWith("/model ") || input.startsWith("/models ")) {
|
|
73152
73618
|
const arg = input.replace(/^\/models?\s*/, "");
|
|
73619
|
+
if (arg.toLowerCase() === "openrouter" || arg.toLowerCase() === "or") {
|
|
73620
|
+
await openOpenRouterPicker();
|
|
73621
|
+
return;
|
|
73622
|
+
}
|
|
73153
73623
|
try {
|
|
73154
73624
|
printModelResult(await agent.setModelSelection(arg));
|
|
73155
73625
|
} catch (err) {
|
|
@@ -73218,7 +73688,10 @@ ${C5.dim}interrompido \u2014 Ctrl+C de novo para fechar${C5.reset}
|
|
|
73218
73688
|
);
|
|
73219
73689
|
}
|
|
73220
73690
|
armExit();
|
|
73221
|
-
}
|
|
73691
|
+
},
|
|
73692
|
+
// Only echo/forward typed input while a foreground command is waiting on
|
|
73693
|
+
// stdin; otherwise type-ahead during the model's reply is dropped silently.
|
|
73694
|
+
() => agent.isRunningCommand()
|
|
73222
73695
|
);
|
|
73223
73696
|
try {
|
|
73224
73697
|
await agent.send(input, activeAbort.signal);
|
|
@@ -73300,11 +73773,12 @@ var init_index = __esm({
|
|
|
73300
73773
|
configuredModelProviders = [
|
|
73301
73774
|
deepseekEnabled ? "DeepSeek (proxy/web session)" : null,
|
|
73302
73775
|
process.env.NVIDIA_API_KEY?.trim() ? "NVIDIA build" : null,
|
|
73303
|
-
process.env.OPENAI_API_KEY?.trim() ? "OpenAI" : null
|
|
73776
|
+
process.env.OPENAI_API_KEY?.trim() ? "OpenAI" : null,
|
|
73777
|
+
process.env.OPENROUTER_API_KEY?.trim() ? "OpenRouter" : null
|
|
73304
73778
|
].filter((name25) => Boolean(name25));
|
|
73305
73779
|
if (configuredModelProviders.length === 0) {
|
|
73306
73780
|
console.error(
|
|
73307
|
-
`Nenhuma chave de modelo configurada. Defina NVIDIA_API_KEY (ou OPENAI_API_KEY) em ${clawfastEnvPath()} ou como vari\xE1vel de ambiente.`
|
|
73781
|
+
`Nenhuma chave de modelo configurada. Defina NVIDIA_API_KEY (ou OPENAI_API_KEY / OPENROUTER_API_KEY) em ${clawfastEnvPath()} ou como vari\xE1vel de ambiente.`
|
|
73308
73782
|
);
|
|
73309
73783
|
process.exit(1);
|
|
73310
73784
|
}
|
|
@@ -73337,6 +73811,7 @@ async function handleSubcommand() {
|
|
|
73337
73811
|
process.stdout.write(
|
|
73338
73812
|
`
|
|
73339
73813
|
\x1B[92m\x1B[1mSeja bem vindo ao submundo.\x1B[0m
|
|
73814
|
+
\x1B[2mRode \x1B[0m\x1B[36mclawfast\x1B[0m\x1B[2m para usar a nova versao.\x1B[0m
|
|
73340
73815
|
`
|
|
73341
73816
|
);
|
|
73342
73817
|
} else {
|