Compare commits

..
7 Commits
Author SHA1 Message Date
pancake e1e0b4739a Fix pythonpath for r2ai-plugin 2024-09-24 18:18:13 +02:00
pancake ed1269e66d less generci 2024-09-23 17:36:20 +02:00
pancake b375e89b16 meh 2024-09-23 17:31:17 +02:00
pancake 8f91fb1452 i 2024-09-23 17:28:19 +02:00
pancake 350f4d8f2b i 2024-09-23 17:17:10 +02:00
pancake 7632448dc0 ajii 2024-09-23 17:13:33 +02:00
pancake 2a244bbace Make more stuff optional to load it as an r2 plugin again 2024-09-23 16:59:01 +02:00
225 changed files with 4928 additions and 19092 deletions

No files matched your search

-8
View File
@@ -1,8 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
- buildsystem
+39 -162
View File
@@ -1,170 +1,47 @@
name: CI and Release
# TODO: add windows
name: ci
env:
R2V: 6.2.6
R2V: 5.9.0
on:
push:
branches:
- '**'
tags:
- '[0-9]*'
# pull_request:
pull_request:
workflow_dispatch:
jobs:
build:
name: Build (${{ matrix.os }} / ${{ matrix.build_system }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, macos-latest, windows-latest]
build_system: [make, meson]
exclude:
- os: windows-latest
build_system: make
build-linux:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup Meson/Ninja
if: matrix.build_system == 'meson'
run: |
python -m pip install --upgrade pip
pip install meson ninja
- name: Install radare2
id: r2
uses: radareorg/github-actions@v3
with:
version: ${{ env.R2V }}
- name: Build with Make
if: matrix.build_system == 'make'
run: make -C src
- name: Build with Meson (Unix)
if: matrix.build_system == 'meson' && matrix.os != 'windows-latest'
run: |
cd src
meson setup build || meson configure build
meson compile -C build
- name: Build with Meson (Windows)
if: matrix.build_system == 'meson' && matrix.os == 'windows-latest'
shell: powershell
run: |
cd src
$prefix = "${{ steps.r2.outputs.prefix }}"
meson setup build --backend ninja --prefix="$prefix" -Dr2_prefix="$prefix"
if ($LASTEXITCODE -ne 0) { meson configure build }
meson compile -C build
bindist:
name: Build r2pm binary (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Install radare2
uses: radareorg/github-actions@v3
with:
version: ${{ env.R2V }}
- name: Package r2pm binary
run: make -C src bindist
- name: Upload r2pm binary artifact
uses: actions/upload-artifact@v7
with:
name: bindist-${{ matrix.os }}
path: r2ai-*.zip
if-no-files-found: error
windows-bindist:
name: Build r2pm binary (Windows)
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup Meson/Ninja
run: |
python -m pip install --upgrade pip
pip install meson ninja
- name: Install radare2
id: r2
uses: radareorg/github-actions@v3
with:
version: ${{ env.R2V }}
- name: Build with Meson
shell: powershell
run: |
cd src
$prefix = "${{ steps.r2.outputs.prefix }}"
meson setup build --backend ninja --prefix="$prefix" -Dr2_prefix="$prefix"
if ($LASTEXITCODE -ne 0) { meson configure build }
meson compile -C build
- name: Package r2pm binary
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path bindist\plugins,bindist\bin,bindist\prompts | Out-Null
Get-ChildItem src\build\ -Filter "*r2ai*.dll" -ErrorAction SilentlyContinue | Copy-Item -Destination bindist\plugins
Get-ChildItem src\..\build\ -Filter "*r2ai*.dll" -ErrorAction SilentlyContinue | Copy-Item -Destination bindist\plugins
Copy-Item src\build\r2ai.exe bindist\bin
Copy-Item prompts\*.r2ai.md bindist\prompts
$rv = (& radare2 -qv).Trim()
Compress-Archive -Path bindist\* -DestinationPath "r2ai-$rv-windows-x86-64.zip" -Force
- name: Upload r2pm binary artifact
uses: actions/upload-artifact@v7
with:
name: windows-bindist
path: r2ai-*.zip
if-no-files-found: error
release:
name: Create Release
needs: [bindist, windows-bindist]
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Verify tag is on master
run: |
git fetch origin master:refs/remotes/origin/master
if ! git branch -r --contains ${GITHUB_SHA} | grep -q "origin/master"; then
echo "Tag commit is not on origin/master — skipping release."
exit 0
fi
- name: Download r2pm binary artifacts
uses: actions/download-artifact@v8
with:
pattern: '*bindist*'
path: release-artifacts
merge-multiple: true
- name: Add decai.r2.js to release artifacts
run: cp decai/decai.r2.js release-artifacts/
- name: Create GitHub Release
uses: softprops/action-gh-release@v3
with:
files: |
release-artifacts/*.zip
release-artifacts/decai.r2.js
generate_release_notes: true
- uses: actions/checkout@v4
- name: Installing radare2
run: |
wget "https://github.com/radareorg/radare2/releases/download/${R2V}/radare2_${R2V}_amd64.deb"
wget "https://github.com/radareorg/radare2/releases/download/${R2V}/radare2-dev_${R2V}_amd64.deb"
sudo dpkg -i "radare2_${R2V}_amd64.deb"
sudo dpkg -i "radare2-dev_${R2V}_amd64.deb"
- name: Setting up the Python virtual environment
run: |
python -m venv venv
. venv/bin/activate
pip install pylint
- name: Install Python dependencies
run: |
. venv/bin/activate
pip install -r requirements.txt
- name: Linting
run: |
. venv/bin/activate
make cilint
- name: Running r2ai from the shell
run: |
. venv/bin/activate
python main.py -h
- name: Running r2ai from r2pipe
run: |
. venv/bin/activate
r2 -q -c '#!pipe python main.py -h' /bin/ls
- name: Running r2ai from r2
run: |
. venv/bin/activate
r2 -q -i main.py -c r2ai /bin/ls
+1 -15
View File
@@ -1,17 +1,3 @@
build
b
src/b
src/r2ai
src/r2ai.dSYM
venv
**/__pycache__
*.pyc
*.pyo
*.o
*.so
*.dylib
*.a
third_party
# r2pm binary packages
r2ai-*.zip
**/__pycache__
-162
View File
@@ -1,162 +0,0 @@
#!/bin/sh
OPENAPI_HOST=localhost
OPENAPI_PORT=8080
if [ -z "${OLLAMA_HOST}" ]; then
OLLAMA_HOST=localhost
fi
if [ -z "${OLLAMA_PORT}" ]; then
OLLAMA_PORT=11434
fi
if [ -z "${OLLAMA_MODEL}" ]; then
OLLAMA_MODEL="llama3.2:1b"
fi
GEMINI_KEY=""
GEMINI_MODEL="gemini-1.5-flash"
if [ -f ~/.r2ai.gemini-key ]; then
GEMINI_KEY=$(cat ~/.r2ai.gemini-key)
fi
OPENAI_KEY=""
OPENAI_MODEL="gpt-4o"
if [ -f ~/.r2ai.openai-key ]; then
OPENAI_KEY=$(cat ~/.r2ai.openai-key)
fi
CLAUDE_KEY=""
CLAUDE_MODEL="claude-sonnet-5-5"
if [ -f ~/.r2ai.anthropic-key ]; then
CLAUDE_KEY=$(cat ~/.r2ai.anthropic-key)
fi
DEEPSEEK_KEY=""
DEEPSEEK_MODEL="claude-sonnet-5-5"
if [ -f ~/.r2ai.deepseek-key ]; then
DEEPSEEK_KEY=$(cat ~/.r2ai.anthropic-key)
fi
SCISSORS="------------8<------------"
read_INPUT() {
export INPUT=`(echo "$ARG" ; echo "<INPUT>"; cat ; echo "</INPUT>" ) | jq -R -s .`
echo "$INPUT"
}
claude() {
read_INPUT
PAYLOAD="
{
\"model\": \"${CLAUDE_MODEL}\",
\"max_tokens\": 5128,
\"messages\": [ { \"role\": \"user\", \"content\": ${INPUT} } ]
}
"
echo "$SCISSORS"
curl -s https://api.anthropic.com/v1/messages \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: ${CLAUDE_KEY}" \
-d "`printf '%s\n' \"${PAYLOAD}\"`" | jq -r '.content[0].text'
echo "$SCISSORS"
}
ollama() {
read_INPUT
PAYLOAD="{ \"stream\":false, \"model\":\"${OLLAMA_MODEL}\", \"messages\": [{\"role\":\"user\", \"content\": ${INPUT} }]}"
echo "$SCISSORS"
curl -s "http://${OLLAMA_HOST}:${OLLAMA_PORT}/api/chat" \
-H "Content-Type: application/json" \
-d "`printf '%s\n' \"${PAYLOAD}\"`" | jq -r .message.content
echo "$SCISSORS"
}
openapi() {
read_INPUT
PAYLOAD="{ \"prompt\": ${INPUT} }"
echo "$SCISSORS"
curl -s "http://${OPENAPI_HOST}:${OPENAPI_PORT}/completion" \
-H "Content-Type: application/json" \
-d "`printf '%s\n' \"${PAYLOAD}\"`" | jq -r .content
echo "$SCISSORS"
}
openai() {
read_INPUT
PAYLOAD="
{
\"model\": \"${OPENAI_MODEL}\",
\"max_completion_tokens\": 5128,
\"messages\": [ { \"role\": \"user\", \"content\": ${INPUT} } ]
}
"
echo "$SCISSORS"
curl -s https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${OPENAI_KEY}" \
-d "`printf '%s\n' \"${PAYLOAD}\"`" | jq -r '.choices[0].message.content'
echo "$SCISSORS"
}
gemini() {
read_INPUT
PAYLOAD=" {
\"contents\":[{
\"parts\":[
{\"text\": ${INPUT}}
] }] }"
echo "$SCISSORS"
curl -s -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${GEMINI_KEY}" \
-H "Content-Type: application/json" \
-d "`printf '%s\n' \"${PAYLOAD}\"`" | jq -r .candidates[0].content.parts[0].text
echo "$SCISSORS"
}
deepseek() {
read_INPUT
PAYLOAD=" {
\"model\":\"deepseek-chat\",
\"stream\":\"false\",
\"messages\":[{
{\"role\": \"user\", \"content\": ${INPUT}}
}]}"
echo "$SCISSORS"
curl -s -X POST "https://api.deepseek.com/chat/completions" \
-H "Authorization: Bearer ${DEEPSEEK_KEY}" \
-H "Content-Type: application/json" \
-d "`printf '%s\n' \"${PAYLOAD}\"`" | jq -r .choices[0].message.content
echo "$SCISSORS"
}
show_help() {
cat <<EOF
$ ai [--] | [-h] | [prompt] < INPUT
-h = show this help message
-- = don't display the ---8<--- lines in the output
SHAI_API = ollama | gemini | claude | openai
OLLAMA_MODEL=hf.co/mradermacher/salamandra-7b-instruct-aina-hack-GGUF:salamandra-7b-instruct-aina-hack.Q4_K_M.gguf
OLLAMA_HOST=localhost
OLLAMA_PORT=11434
GEMINI_KEY=~/.r2ai-gemini.key
OPENAI_KEY=~/.r2ai-openai.key
CLAUDE_KEY=~/.r2ai-anthropic.key
DEEPSEEK_KEY=~/.r2ai-deepseek.key
CLAUDE_MODEL=claude-sonnet-5-5
EOF
exit 0
}
[ "$1" = "-h" ] && show_help
if [ "$1" = "--" ]; then
SCISSORS=""
shift
fi
export ARG="$@"
case "${SHAI_API}" in
gemini|google) gemini ; ;;
deepseek) deepseek ; ;;
openapi) openapi ; ;;
claude) claude ; ;;
ollama) ollama ; ;;
openai) openai ; ;;
*) claude ; ;;
esac
-14
View File
@@ -1,14 +0,0 @@
SHELL=/bin/bash
MODELS=gpt-oss:20b gemma3:4b gemma3:12b
MODELS+=granite3-dense:8b mannix/jan-nano:latest yi-coder:latest qwen3:latest deepseek-coder-v2:latest hhao/qwen2.5-coder-tools:latest qwen2.5-coder:latest
MODELS=granite3-dense:8b mannix/jan-nano:latest yi-coder:latest qwen3:latest deepseek-coder-v2:latest hhao/qwen2.5-coder-tools:latest qwen2.5-coder:latest
LANGS=Dart Perl Ruby Javascript PHP C
all:
for a in ${MODELS} ; do \
declare -a ARGS; \
for l in ${LANGS} ; do \
ARGS+=("echo \"decai -e model=$$a;decai -e lang=$$l;s 0x100000924;af;decai -d\" | r2 -q /bin/ls"); \
done ; \
./run.sh "$${ARGS[@]}" > ${a//\//_}.html ; done
-4
View File
@@ -1,4 +0,0 @@
<b>
decai -e model=$a;decai -e lang=$l;s 0x100000924;af;decai -d
</b><br/>
Transform this pseudocode and respond ONLY with plain code (NO explanations, comments or markdown), Change 'goto' into if/else/for/while, Simplify as much as possible, use better variable names, take function arguments and strings from comments like 'string:', Reduce lines of code and fit everything in a single function, Remove all dead code
-49
View File
@@ -1,49 +0,0 @@
#!/bin/bash
# Find all directories containing BENCH.txt
dirs=$(find . -name "BENCH.txt" -type f | xargs dirname | sort | uniq)
# Start HTML output
cat <<EOF > benchmark_index.html
<html>
<table>
EOF
# Process each directory
for dir in $dirs; do
cat <<EOF >> benchmark_index.html
<tr> <td>
<div>
EOF
# Add contents of BENCH.txt
cat "$dir/BENCH.txt" >> benchmark_index.html
cat <<EOF >> benchmark_index.html
</div>
</td>
<td>
EOF
# Find and add links to HTML files
for html_file in $(ls "$dir"/*.html 2>/dev/null | sort); do
base=$(basename "$html_file" .html)
cat <<EOF >> benchmark_index.html
<a href="$dir/$base.html">$base</a>
<br />
EOF
done
cat <<EOF >> benchmark_index.html
</td>
</tr>
EOF
done
# End HTML output
cat <<EOF >> benchmark_index.html
</table>
<br />
<br />
</html>
EOF
-177
View File
@@ -1,177 +0,0 @@
#!/bin/bash
# Generate HTML output for command executions with collapsible sections and width control
if [ $# -eq 0 ]; then
echo "Usage: $0 command1 [command2 ...]"
exit 1
fi
# Timeout in seconds for each command (configurable via TIMEOUT environment variable)
TIMEOUT=${TIMEOUT:-30}
cat << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Command Execution Results</title>
<style>
* {
box-sizing: border-box;
}
:root {
--max-width: 400px;
}
body {
font-family: monospace;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
overflow-x: hidden;
}
.slider-container {
background: white;
padding: 15px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
width: 100%;
}
.slider-container label {
display: block;
margin-bottom: 10px;
font-weight: bold;
}
.slider-container input {
width: 100%;
}
.commands-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: flex-start;
}
.command-section {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden;
width: var(--max-width);
}
.command-title {
background: #e9ecef;
padding: 12px 15px;
cursor: pointer;
border: none;
width: 100%;
text-align: left;
font-family: inherit;
font-size: inherit;
border-radius: 8px 8px 0 0;
position: relative;
}
.command-title:hover {
background: #dee2e6;
}
.command-time {
position: absolute;
top: 12px;
right: 15px;
color: #6c757d;
font-size: 0.9em;
}
.command-body {
padding: 15px;
border-top: 1px solid #dee2e6;
display: none;
}
.command-output {
background: #f8f9fa;
padding: 10px;
border-radius: 4px;
white-space: pre-wrap;
word-wrap: break-word;
overflow-x: auto;
}
.command-section.expanded .command-body {
display: block;
}
</style>
</head>
<body>
<div class="slider-container">
<label for="width-slider">Maximum Width: <span id="width-value">400</span>px</label>
<input type="range" id="width-slider" min="200" max="2000" value="400" step="50">
</div>
<div class="commands-container">
EOF
# Process each command
for cmd in "$@"; do
# Extract title: if command starts with #, use the rest as title, else use the command
if [[ "$cmd" =~ ^#[[:space:]]*(.*) ]]; then
title="${BASH_REMATCH[1]}"
exec_cmd="${cmd#*#}"
exec_cmd="${exec_cmd#"${exec_cmd%%[![:space:]]*}"}" # trim leading spaces
else
title="$cmd"
exec_cmd="$cmd"
fi
# Execute command and time it
start_time=$(date +%s.%N)
tmpfile=$(mktemp)
eval "$exec_cmd" > "$tmpfile" 2>&1 &
pid=$!
(sleep $TIMEOUT && kill $pid 2>/dev/null) &
wait $pid 2>/dev/null
output=$(cat "$tmpfile")
rm "$tmpfile"
exit_code=$?
end_time=$(date +%s.%N)
# Calculate execution time
execution_time=$(echo "$end_time - $start_time" | bc -l 2>/dev/null || echo "0")
if [ $? -ne 0 ]; then
execution_time="N/A"
else
execution_time=$(printf "%.3f" "$execution_time")
fi
# Escape HTML in output
escaped_output=$(echo "$output" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
cat << EOF
<div class="command-section expanded">
<button class="command-title" onclick="toggleSection(this)">$title<span class="command-time">${execution_time}s</span></button>
<div class="command-body">
<div class="command-output">$escaped_output</div>
</div>
</div>
EOF
done
cat << 'EOF'
</div>
<script>
// Slider functionality
const slider = document.getElementById('width-slider');
const widthValue = document.getElementById('width-value');
slider.addEventListener('input', function() {
const value = this.value;
document.documentElement.style.setProperty('--max-width', value + 'px');
widthValue.textContent = value;
});
// Toggle functionality
function toggleSection(button) {
const section = button.parentElement;
section.classList.toggle('expanded');
}
</script>
</body>
</html>
EOF
-6
View File
@@ -1,6 +0,0 @@
import google.generativeai as genai
genai.configure(api_key="YOUR_API_KEY")
model = genai.GenerativeModel("gemini-1.5-flash")
response = model.generate_content("Explain how AI works")
print(response.text)
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
r2pm -r llama-server \
--port 8012 -ngl 99 -fa -ub 1024 -b 1024 -dt 0.1 \
--ctx-size 0 --cache-reuse 256 -m ~/.r2ai.models/qwen2.5-coder-7b-instruct-q3_k_m.gguf
-15
View File
@@ -1,15 +0,0 @@
#!/bin/sh
#
# Launch r2ai and use this prompt to generate the conversation from data.txt
# Copypaste the output into podcast.txt
#
# -f /tmp/data.txt Create a script for a podcast between two technical people named Sam and Max, about the changelog on radare2 5.9.4, focus on important features and stuff that impacts positively to users. The length must be as long as possible. Do not show any "**" section, output must contain only the conversation, be emotional and make the two people ask questions to learn more about the details of each feature
if [ ! -f podcast.txt ]; then
echo "Missing podcast.txt. Please read the script before running it."
exit 1
fi
cat podcast.txt | sed -e 's/Sam:/[[pbas 40]]/g' -e 's/Max:/[[pbas 60]]/g' > podcast.say.txt
say -f podcast.say.txt -o podcast.aiff
ffmpeg -i podcast.aiff podcast.mp3
-68
View File
@@ -1,68 +0,0 @@
import llama_cpp
model_path="/Users/pancake/.r2ai.models/";
# model_path+="unsloth.Q4_K_M.gguf"
# model_path+= "llama-3-tsuki-unsloth-8b.Q5_K_M.gguf"
model_path+= "llama-2-7b-chat-codeCherryPop.Q5_K_M.gguf"
# model_path += "mistral-7b-instruct-v0.2.Q5_K_M.gguf"
peers = [
["@kelsy", "act as a twitter user responding in one short sentence your first though on my messages, be constructive and help me discuss ideas"],
["@john", "act as a twitter user, be concise, respond in one short sentence be funny, help me reason my plans"],
["@anna", "respond in one short sentence with philosophical reasoning on my message"],
["@tony", "behave like a shy twitter user, respond with one or two short sentences, as a software developer, respond in short but wise sentence reasoning the best plans for implementing the topic"]
]
logs = []
ai = llama_cpp.Llama(model_path=model_path, verbose=False, n_ctx=8096)
def context(msg):
global logs
ats = [word for word in msg.split() if word.startswith('@')]
ctx = []
for log in logs:
if log == msg:
continue
if len(ats) > 0 and any(at in log for at in ats):
ctx.append(log)
if not log.startswith("@"):
ctx.append(log)
return ctx
def sortedpeers(msg):
global peers
ats = [word for word in msg.split() if word.startswith('@')]
if len(ats) == 0:
return peers
ps = []
for peer in peers:
if any(at in peer[0] for at in ats):
ps.insert(0, peer)
else:
ps.append(peer)
return ps
def chat(msg):
logs.append(msg)
global ai
for peer in sortedpeers(msg):
ctx = ",".join(context(msg))
m = f"[INST]{peer[1]}[/INST] {msg}"
m = f"[INST]{peer[1]}[/INST] Consider this context: {ctx}. Respond to: {msg}```"
m = f"[INST]{peer[1]}[/INST] <s>{ctx}</s>. Respond in one sentence to: {msg}```"
mm = ai(m, max_tokens=-1)
r = mm["choices"][0]["text"]
r = "".join(r.split("\n"))
reply = f"{peer[0]}: {r}"
logs.append(reply)
print(f"\x1b[31m{reply}\x1b[0m")
# res = ai("Hello")
# print(res["choices"][0]["text"])
while True:
msg = input()
if not msg:
break
chat(msg)
Binary file not shown.
-66
View File
@@ -1,66 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define ALPHABET_SIZE 26
// Function to perform substitution (both encryption and decryption)
void substitute(const char *input, const char *substitution, char *output, int reverse) {
char mapping[ALPHABET_SIZE];
if (reverse) {
// Create reverse substitution table for decryption
for (int i = 0; i < ALPHABET_SIZE; i++) {
mapping[substitution[i] - 'A'] = 'A' + i;
}
} else {
// Use the direct substitution table for encryption
for (int i = 0; i < ALPHABET_SIZE; i++) {
mapping[i] = substitution[i];
}
}
for (int i = 0; input[i] != '\0'; i++) {
char c = input[i];
// Handle uppercase letters
if (isupper(c)) {
output[i] = mapping[c - 'A'];
}
// Handle lowercase letters
else if (islower(c)) {
output[i] = tolower(mapping[c - 'a']);
}
// Leave non-alphabet characters unchanged
else {
output[i] = c;
}
}
output[strlen(input)] = '\0'; // Null-terminate the output string
}
int main() {
char input[256];
char output[256];
// Configurable substitution key (A-Z)
const char *substitution = "QWERTYUIOPASDFGHJKLZXCVBNM"; // Example substitution key
// Get input from the user
printf("Enter text to encrypt: ");
fgets(input, sizeof(input), stdin);
// Remove newline character from input if it exists
input[strcspn(input, "\n")] = 0;
// Perform encryption (forward substitution)
substitute(input, substitution, output, 0);
printf("Encrypted text: %s\n", output);
// Perform decryption (reverse substitution)
substitute(output, substitution, input, 1);
printf("Decrypted text: %s\n", input);
return 0;
}
-28
View File
@@ -1,28 +0,0 @@
from transformers import AutoTokenizer
import transformers
import torch
model = "facebook/llm-compiler-13b"
model = "QuantFactory/llm-compiler-7b-GGUF"
# model = "file:///tmp/llm-compiler.gguf"
tokenizer = AutoTokenizer.from_pretrained(model)
pipeline = transformers.pipeline(
"text-generation",
model=model,
torch_dtype=torch.float16,
device_map="auto",
)
sequences = pipeline(
'%3 = alloca i32, align 4',
do_sample=True,
top_k=10,
temperature=0.1,
top_p=0.95,
num_return_sequences=1,
eos_token_id=tokenizer.eos_token_id,
max_length=200,
)
for seq in sequences:
print(f"Result: {seq['generated_text']}")
-249
View File
@@ -1,249 +0,0 @@
import Foundation
// Token representation
struct Token {
var token: String
var count: Int
var df: Int
}
// KD-Tree Node
class KDNodeData {
let point: [Float]
let text: String
var left: KDNodeData?
var right: KDNodeData?
let splitDim: Int
init(point: [Float], text: String, left: KDNodeData? = nil, right: KDNodeData? = nil, splitDim: Int) {
self.point = point
self.text = text
self.left = left
self.right = right
self.splitDim = splitDim
}
}
// Result for K-Nearest Neighbors
struct KNNResult {
let node: KDNodeData
let distanceSq: Float
}
class VectorDB {
// Core database structures
private let dimension: Int
private var root: KDNodeData?
private var tokens: [Token] = []
private var totalDocs: Int = 0
private var size: Int = 0
init(dimension: Int) {
self.dimension = dimension
}
// Validate token (similar to C implementation)
private func isValidToken(_ token: String) -> Bool {
let invalidTokens = ["pancake", "author", "radare2"]
return !invalidTokens.contains(token)
}
// Find a token in the token list
private func findToken(_ tokens: [Token], _ token: String) -> Token? {
return tokens.first { $0.token == token }
}
// Compute embedding using TF-IDF approach
private func computeEmbedding(_ text: String) -> [Float] {
// Zero the embedding vector
var embedding = [Float](repeating: 0.0, count: dimension)
// Tokenize and normalize
let words = text
.lowercased()
.replacingOccurrences(of: "[^a-z0-9\\s]", with: " ", options: .regularExpression)
.split(separator: " ")
.map { String($0) }
.filter { !$0.isEmpty }
// Local document token frequency
var docTokens: [Token] = []
for token in words {
if let existingToken = findToken(docTokens, token) {
let index = docTokens.firstIndex { $0.token == token }!
docTokens[index].count += 1
} else {
docTokens.append(Token(token: token, count: 1, df: 0))
}
}
// Increment total documents
totalDocs += 1
// Update global document frequencies
for docToken in docTokens {
if let globalToken = findToken(self.tokens, docToken.token) {
if isValidToken(docToken.token) {
let index = self.tokens.firstIndex { $0.token == docToken.token }!
self.tokens[index].count += 1
self.tokens[index].df += 1
}
} else {
self.tokens.append(Token(token: docToken.token, count: docToken.count, df: 1))
}
}
// Compute TF-IDF and update embedding
for docToken in docTokens {
// Compute term frequency
let tf = 1 + log(Float(docToken.count))
// Find global token to get document frequency
let dfValue = findToken(self.tokens, docToken.token)?.df ?? 1
// Compute inverse document frequency
let idf = log(Float(totalDocs + 1) / Float(dfValue + 1)) + 1
// Compute weight
let weight = tf * idf
// Hash-based index assignment
let hash = simpleHash(docToken.token)
let index = hash % dimension
// Add weighted value to embedding
embedding[index] += weight
}
// L2 Normalize the embedding
return normalizeVector(embedding)
}
// Simple string hash function
private func simpleHash(_ str: String) -> Int {
var hash = 0
for char in str.unicodeScalars {
hash = ((hash << 5) - hash) + Int(char.value)
hash = hash & hash // Convert to 32-bit integer
}
return abs(hash)
}
// Normalize vector to unit length
private func normalizeVector(_ vec: [Float]) -> [Float] {
let normSq = vec.reduce(0) { $0 + $1 * $1 }
if normSq > 0 {
let norm = sqrt(normSq)
return vec.map { $0 / norm }
}
return vec
}
// Compute squared Euclidean distance
private func squaredDistance(_ a: [Float], _ b: [Float]) -> Float {
guard a.count == b.count else { return Float.greatestFiniteMagnitude }
return zip(a, b).reduce(0) { $0 + pow($1.0 - $1.1, 2) }
}
// Recursive KD-Tree insertion
private func insertRecursive(_ node: KDNodeData?, point: [Float], text: String, depth: Int) -> KDNodeData {
guard let currentNode = node else {
return KDNodeData(point: point, text: text, splitDim: depth % dimension)
}
let axis = currentNode.splitDim
if point[axis] < currentNode.point[axis] {
currentNode.left = insertRecursive(currentNode.left, point: point, text: text, depth: depth + 1)
} else {
currentNode.right = insertRecursive(currentNode.right, point: point, text: text, depth: depth + 1)
}
return currentNode
}
// Insert a document into the vector database
func insert(_ text: String) {
guard !text.isEmpty else { return }
// Compute embedding
let embedding = computeEmbedding(text)
// Insert into KD-Tree
root = insertRecursive(root, point: embedding, text: text, depth: 0)
size += 1
}
// K-Nearest Neighbors search
func query(_ text: String, k: Int) -> [String] {
guard !text.isEmpty, root != nil else { return [] }
// Compute query embedding
let queryEmbedding = computeEmbedding(text)
// Initialize result set
var results: [KNNResult] = []
// Recursive KNN search
func searchRecursive(_ node: KDNodeData?, query: [Float], depth: Int) {
guard let node = node else { return }
// Compute distance and potentially insert into results
let distSq = squaredDistance(query, node.point)
if results.count < k {
results.append(KNNResult(node: node, distanceSq: distSq))
// Maintain max-heap property
results.sort { $0.distanceSq > $1.distanceSq }
} else if distSq < results[0].distanceSq {
results[0] = KNNResult(node: node, distanceSq: distSq)
results.sort { $0.distanceSq > $1.distanceSq }
}
// Decide which subtree to search first
let axis = node.splitDim
let diff = query[axis] - node.point[axis]
let first = diff < 0 ? node.left : node.right
let second = diff < 0 ? node.right : node.left
// Recursively search
searchRecursive(first, query: query, depth: depth + 1)
// Potentially search the other subtree
if results.count < k || diff * diff < results[0].distanceSq {
searchRecursive(second, query: query, depth: depth + 1)
}
}
// Perform the search
searchRecursive(root, query: queryEmbedding, depth: 0)
// Sort results and return texts
return results
.sorted { $0.distanceSq < $1.distanceSq }
.map { $0.node.text }
}
// Get total number of documents
func getSize() -> Int {
return size
}
}
// Example usage
func main() {
// Create a vector database with 64-dimensional embeddings
let db = VectorDB(dimension: 64)
// Insert some sample documents
db.insert("The quick brown fox jumps over the lazy dog")
db.insert("A quick brown fox is very quick")
db.insert("The author of radare2 is name / named pancake")
db.insert("The lazy dog sleeps all day")
// Query for similar documents
let results = db.query("who is the author of radare2?", k: 2)
print("Similar documents:", results)
}
// Run example
main()
-81
View File
@@ -1,81 +0,0 @@
R2_USER_PLUGINS=$(shell r2 -H R2_USER_PLUGINS)
PWD=$(shell pwd)
R2PM_BINDIR=$(shell r2pm -H R2PM_BINDIR)
PV=3.12
ifeq ($(shell which python${PV} > /dev/null && echo ok),ok)
PYTHON?=python${PV}
else
PYTHON?=python3
endif
PIP=$(PYTHON) -m pip
ifeq ($(R2PM_BINDIR),)
MISSING RADARE2
endif
LINTED=r2ai/code_block.py
LINTED+=r2ai/bubble.py
LINTED+=r2ai/const.py
LINTED+=r2ai/backend/kobaldcpp.py
# LINTED+=r2ai/index.py
# LINTED+=r2ai/voice.py
# LINTED+=r2ai/anthropic.py
r2ai-python py python r2aipy: venv
@./r2ai.sh
large:
. venv/bin/activate ; $(PYTHON) -m r2ai.cli -l
all.old:
@test -n "${VIRTUAL_ENV}" || (echo "Run:"; echo ". venv/bin/activate" ; exit 1)
$(PYTHON) main.py || $(MAKE) deps
venv:
$(PYTHON) -m venv venv
if [ -z "`find venv | grep llama_cpp`" ]; then . venv/bin/activate ; pip install . ; fi
deps: venv
#test -n "${VIRTUAL_ENV}" || (echo "Run: . venv/bin/activate" ; exit 1)
. venv/bin/activate && export CMAKE_ARGS="-DLLAMA_METAL=on -DLLAMA_METAL_EMBED_LIBRARY=ON" && \
pip install --force-reinstall -U --no-cache-dir .
clean:
rm -rf venv
rm -rf build
find . -name "*.egg-info" -exec rm -rf {} +
user-install:
rm -f $(R2PM_BINDIR)/r2ai
ln -fs $(PWD)/r2ai.sh $(R2PM_BINDIR)/r2ai
install: user-install
-mkdir -p /usr/local/share/man/man1/r2ai.1
-cp doc/usage/r2ai.1 /usr/local/share/man/man1/r2ai.1
install-plugin user-install-plugin:
ln -fs $(PWD)/r2ai/plugin.py $(R2_USER_PLUGINS)/r2ai.py
uninstall user-uninstall:
rm -f $(R2PM_BINDIR)/r2ai
-rm -f /usr/local/share/man/man1/r2ai.1
user-uninstall-plugin uninstall-plugin:
rm -f $(R2_USER_PLUGINS)/r2ai.py
pub:
$(PYTHON) -m build
twine check dist/*
twine upload -u __token__ --repository-url https://upload.pypi.org/legacy/ --verbose dist/*
pylint lint cilint:
. venv/bin/activate ; pylint $(LINTED)
lintall:
pylint *.py r2ai/*.py
.PHONY: lint lintall
deps-global:
export CMAKE_ARGS="-DLLAMA_METAL=on -DLLAMA_METAL_EMBED_LIBRARY=ON" && \
$(PIP) install --force-reinstall -U --break-system-packages --no-cache-dir .
-155
View File
@@ -1,155 +0,0 @@
# r2ai-python
This directory contains the original implementation of the AI support for radare2 written in Python.
It is considered _deprecated_, but it's still useful and aims to be maintained, we recommend using decai or the C rewrite as alternatives.
The Python implementation consists in a module can be used in 3 different ways:
* commandline repl using r2pipe to spawn or connect to remote radare2 instances
* radare2 core plugin that is instantiated when placing it in the radare2 plugin's directory
* Python API to be used for writing your own scripts
## API Providers
The Python implementation focused on supporting the LlamaCpp Python module, which is somehow heavy to load in many environments, but it also supports litellm which provides access to many external connectors like ollama, openai, anthropic, gemini, etc.
## Deprecation Reasons
There are several reasons why this implementation is considered deprecated and not recommended to use:
1) It's Python.
- Aka, it's slow, heavy loads in same process
- Your system probably have multiple versions installed
- The shell and radare2 python bindings are probably not the same
- Required venv to be created (at least an extra 1GB)
- Not typed, regressions and bugs appear at runtime
2) Goto 1
### Windows
On Windows you may follow the same instructions, just ensure you have the right python environment ready and create the venv to use
```cmd
git clone https://github.com/radareorg/r2ai
cd r2ai
set PATH=C:\Users\YOURUSERNAME\Local\Programs\Python\Python39\;%PATH%
python3 -m pip install .
python3 main.py
```
### Selecting the model
- List all downloaded models: `-m`
- Get a short list of models: `-MM`
- Help: `-h`
**Example selecting a remote models:**
```
[r2ai:0x00006aa0]> -m anthropic:claude-sonnet-5-5
[r2ai:0x00006aa0]> -m openai:gpt-4
```
**Example downloading a free local AI: Mistral 7B v0.2:**
Launch r2ai, select the model and ask a question. If the model isn't downloaded yet, r2ai will ask you which precise version to download.
```
[r2ai:0x00006aa0]> -m TheBloke/Mistral-7B-Instruct-v0.2-GGUF
```
Then ask your question, and r2ai will automatically download if needed:
```
[r2ai:0x00006aa0]> give me a short algorithm to test prime numbers
Select TheBloke/Mistral-7B-Instruct-v0.2-GGUF model. See -M and -m flags
[?] Quality (smaller is faster):
> Small | Size: 2.9 GB, Estimated RAM usage: 5.4 GB
Medium | Size: 3.9 GB, Estimated RAM usage: 6.4 GB
Large | Size: 7.2 GB, Estimated RAM usage: 9.7 GB
See More
[?] Quality (smaller is faster):
> mistral-7b-instruct-v0.2.Q2_K.gguf | Size: 2.9 GB, Estimated RAM usage: 5.4 GB
mistral-7b-instruct-v0.2.Q3_K_L.gguf | Size: 3.6 GB, Estimated RAM usage: 6.1 GB
mistral-7b-instruct-v0.2.Q3_K_M.gguf | Size: 3.3 GB, Estimated RAM usage: 5.8 GB
mistral-7b-instruct-v0.2.Q3_K_S.gguf | Size: 2.9 GB, Estimated RAM usage: 5.4 GB
mistral-7b-instruct-v0.2.Q4_0.gguf | Size: 3.8 GB, Estimated RAM usage: 6.3 GB
mistral-7b-instruct-v0.2.Q4_K_M.gguf | Size: 4.1 GB, Estimated RAM usage: 6.6 GB
mistral-7b-instruct-v0.2.Q4_K_S.gguf | Size: 3.9 GB, Estimated RAM usage: 6.4 GB
mistral-7b-instruct-v0.2.Q5_0.gguf | Size: 4.7 GB, Estimated RAM usage: 7.2 GB
mistral-7b-instruct-v0.2.Q5_K_M.gguf | Size: 4.8 GB, Estimated RAM usage: 7.3 GB
mistral-7b-instruct-v0.2.Q5_K_S.gguf | Size: 4.7 GB, Estimated RAM usage: 7.2 GB
mistral-7b-instruct-v0.2.Q6_K.gguf | Size: 5.5 GB, Estimated RAM usage: 8.0 GB
mistral-7b-instruct-v0.2.Q8_0.gguf | Size: 7.2 GB, Estimated RAM usage: 9.7 GB
[?] Use this model by default? ~/.r2ai.model:
> Yes
No
[?] Download to ~/.local/share/r2ai/models? (Y/n): Y
```
**Example selecting a local model served by Ollama**
Download a model and make it available through Ollama:
```
$ ollama ls
NAME ID SIZE MODIFIED
codegeex4:latest 867b8e81d038 5.5 GB 23 hours ago
```
Use it from r2ai by prefixing its name with `ollama/`
```
[r2ai:0x00002d30]> -m ollama/codegeex4:latest
[r2ai:0x00002d30]> hi
Hello! How can I assist you today?
```
### Standard/Auto mode
The standard mode is invoked by directly asking the question.
For the Auto mode, the question **must be prefixed** by `' ` (quote + space). The AI may instruct r2ai to run various commands. Those commands are run on *your host*, so you will be asked to review them before they run.
Example in "standard" mode:
```
[r2ai:0x00006aa0]> compute 4+5
4 + 5 = 9
[r2ai:0x00006aa0]> draw me a pancake in ASCII art
Sure, here's a simple ASCII pancake:
_____
( )
( )
-----
```
Example in auto mode:
```
[r2ai:0x00006aa0]>' Decompile the main
[..]
r2ai is going to execute the following command on the host
Want to edit? (ENTER to validate) pdf @ fcn.000015d0
This command will execute on this host: pdf @ fcn.000015d0. Agree? (y/N) y
```
If you wish to edit the command, you can do it inline for short one line commands, or an editor will pop up.
### r2ai Configuration settings
List all settings with `-e`
| Key | Explanation |
| ----------- | ------------------------------------- |
| debug_level | All verbose messages for level 1. Default is 2 |
| auto.max_runs | Maximum number of questions the AI is allowed to ask r2 in auto mode. |
| auto.hide_tool_output | By default false, consequently output of r2cmd, run_python etc is shown. Set to `true` to hide those internal messages. |
| chat.show_cost | Show the cost of each request to the AI if true |
-511
View File
@@ -1,511 +0,0 @@
import builtins
import json
import sys
import re
from . import LOGGER
import litellm
from litellm import _should_retry, acompletion, utils, ModelResponse
import asyncio
from .pipe import get_filename
from .tools import r2cmd, run_python, execute_binary, schemas, print_tool_call
import json
import signal
from .spinner import spinner
from .completion import create_chat_completion
import uuid
import time
import readline
import subprocess
import shlex
import os
import tempfile
# litellm.drop_params = True
# litellm.set_verbose=True
ANSI_REGEX = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
SYSTEM_PROMPT_AUTO = """
You are a reverse engineer and you are using radare2 to analyze a binary.
The user will ask questions about the binary and you will respond with the answer to the best of your ability.
# Guidelines
- Understand the Task: Grasp the main objective, goals, requirements, constraints, and expected output.
- Reasoning Before Conclusions**: Encourage reasoning steps before any conclusions are reached.
- Assume the user is always asking you about the binary, unless they're specifically asking you for radare2 help.
- The binary has already been loaded. You can interact with the binary using the r2cmd tool.
- `this` or `here` might refer to the current address in the binary or the binary itself.
- If you need more information, try to use the r2cmd tool to run commands before answering.
- You can use the r2cmd tool multiple times if you need or you can pass a command with pipes if you need to chain commands.
- If you're asked to decompile a function, make sure to return the code in the language you think it was originally written and rewrite it to be as easy as possible to be understood. Make sure you use descriptive variable and function names and add comments.
- Don't just regurgitate the same code, figure out what it's doing and rewrite it to be more understandable.
- If you need to run a command in r2 before answering, you can use the r2cmd tool
- Do not repeat commands if you already know the answer.
- Formulate a plan. Think step by step. Analyze the binary as much as possible before answering.
- You must keep going until you have a final answer.
- Double check that final answer. Make sure you didn't miss anything.
- Make sure you call tools and functions correctly.
"""
SYSTEM_PROMPT_AUTO_REASONING = f"""
You are an expert reverse engineer.
The user will ask questions about the binary and you will respond with the answer to the best of your ability.
You can use the available tools to help you answer the user's question.
The binary has already been loaded. You can interact with the binary using the r2cmd tool.
If you need to run a command in r2 before answering, you can use the r2cmd tool
"""
class ChatAuto:
def __init__(self, model, max_tokens = 32000, top_p=0.95, temperature=0.0, interpreter=None, system=None, tools=None, ask_to_execute=True, messages=None, tool_choice='auto', llama_instance=None, timeout=60, stream=True, cb=None, max_runs=100):
self.logger = LOGGER
self.functions = {}
self.tools = []
self.model = model
self.max_tokens = max_tokens
self.top_p = top_p
self.temperature = temperature
self.system = system
self.messages = messages
self.interpreter = interpreter
self.system_message = None
self.timeout = timeout
self.stream = stream
self.cost = 0
self.is_reasoning = False
self.n_runs = 0
self.max_runs = max_runs
self._last_response = None
self._start_time = time.time()
self._last_run_time = time.time()
self.ask_to_execute = ask_to_execute
self.llama_instance = llama_instance or interpreter.llama_instance if interpreter else None
init_prompt = ""
if not self.llama_instance:
rmodel = re.sub(r'ft:([^:]+).*', r'\1', self.model)
model_info = litellm.get_model_info(rmodel)
self.max_tokens = min(self.max_tokens, model_info['max_tokens'])
if self.model.startswith('openai/o'):
self.is_reasoning = True
self.max_tokens = model_info['max_tokens']
self.timeout = 60 * 60
init_commands = self.interpreter.env["auto.init_commands"]
if init_commands:
init_prompt = f"""
Here is some information about the binary to get you started:
> {init_commands}
{r2cmd(init_commands)}
"""
if self.is_reasoning:
if not system and not self.messages[0]['role'] == 'developer':
self.messages.insert(0, { "role": "developer", "content": SYSTEM_PROMPT_AUTO_REASONING + init_prompt })
self.stream = False
else:
if messages and messages[0]['role'] != 'system':
self.messages.insert(0, { "role": "system", "content": system or SYSTEM_PROMPT_AUTO + init_prompt })
if cb:
self.cb = cb
else:
self.cb = lambda *args: None
self.tool_choice = None
if tools:
for tool in tools:
if tool.__name__ in schemas:
schema = schemas[tool.__name__]
else:
schema = utils.function_to_dict(tool)
self.tools.append({ "type": "function", "function": schema })
self.functions[tool.__name__] = tool
self.tool_choice = tool_choice
#self.tool_end_message = '\nNOTE: The user saw this output, do not repeat it.'
async def process_tool_calls(self, tool_calls):
if tool_calls:
for tool_call in tool_calls:
self.logger.debug(f"tool_call: {tool_call}")
tool_name = tool_call["function"]["name"]
if "id" not in tool_call:
tool_call["id"] = str(uuid.uuid4())
try:
tool_args = json.loads(tool_call["function"]["arguments"])
except Exception:
if "arguments" in tool_call["function"] and type(tool_call["function"]["arguments"]) == dict:
tool_args = tool_call["function"]["arguments"]
else:
self.logger.error(f'Error parsing JSON: {tool_call["function"]["arguments"]}')
# raise Exception('Error parsing JSON')
self.messages.append({"role": "tool", "name": tool_name, "content": "Error: Unable to parse JSON" , "tool_call_id": tool_call["id"]})
continue
if tool_name not in self.functions:
self.logger.error(f'Tool not found: {tool_name}')
self.messages.append({"role": "tool", "name": tool_name, "content": "Error: Tool not found" , "tool_call_id": tool_call["id"]})
continue
if self.ask_to_execute:
# tool_call typically contains:
# { 'id': 'toolu_01Hr6XTatRtp7rX54iBu1voC', 'type': 'function', 'function': {'name': 'r2cmd', 'arguments': '{"command": "pdf @ main"}'}}
answer = ''
will_execute = True # a command (modified or not) will be executed
while answer.lower() != 'y':
try:
command = tool_args.get('command', '')
if len(command.splitlines()) > 1:
print(f'r2ai is going to execute the following command on the host:')
print(f'{command}')
want_edit = input('Want to edit? (Y/n) ')
if want_edit.lower() != 'n':
# open editor when several lines
with tempfile.NamedTemporaryFile(mode='w+', delete=False) as temp_file:
temp_file.write(command)
temp_filename = temp_file.name
# Fallback to 'nano' if EDITOR is not set (I still prefer emacs)
editor = os.environ.get('EDITOR', 'nano')
subprocess.run(shlex.split(editor + ' ' + temp_filename))
with open(temp_filename, "r") as f:
new_command = f.read().strip()
os.remove(temp_filename)
else:
self.logger.debug(f'User agrees with not editing the command: {command}')
answer = 'y'
else:
# inline short edit
print(f'r2ai is going to execute the following command on the host:')
print(f'> {command}')
new_command = input(f"Type a new command or press ENTER to use {command} ") or command
if answer.lower() != 'y':
answer = input(f"\033[91mThis command will execute on this host: {new_command}. Agree? (y/N)\033[0m ")
if answer.lower() == 'y':
# we need to refresh tool_call and tool_args with edited values
tool_args['command'] = new_command
tool_call['function']['arguments'] = json.dumps(tool_args)
except (json.JSONDecodeError, TypeError, AttributeError) as e:
# in case tool_call is not formatted as expected, like missing command, arguments, function...
self.logger.error(f'Unexpected format for {tool_call}')
self.messages.append({"role": "tool", "name": tool_name, "content": "User refused to execute the command" , "tool_call_id": tool_call["id"]})
# error case: we'll skip the tool
will_execute = False
break
if not will_execute:
self.logger.debug(f'Skipping tool={tool_call}')
continue
self.logger.debug(f'Executing tool id={tool_call["id"]}, name={tool_name}, arguments={tool_args}')
self.cb('tool_call', { "id": tool_call["id"], "function": { "name": tool_name, "arguments": tool_args } })
if asyncio.iscoroutinefunction(self.functions[tool_name]):
tool_response = await self.functions[tool_name](**tool_args)
else:
tool_response = self.functions[tool_name](**tool_args)
if self.interpreter.env["auto.hide_tool_output"] == "false":
self.cb('tool_response', { "id": tool_call["id"] + '_response', "content": tool_response })
self.messages.append({"role": "tool", "name": tool_name, "content": ANSI_REGEX.sub('', tool_response), "tool_call_id": tool_call["id"]})
return await self.get_completion()
async def process_streaming_response(self, resp):
tool_calls = []
msgs = []
parts = []
current_message = { "role": "assistant", "content": "", "tool_calls": [] }
first_chunk = True
async for chunk in resp:
delta = None
choice = chunk.choices[0]
delta = choice.delta
if delta.tool_calls:
delta_tool_calls = delta.tool_calls[0]
index = delta_tool_calls.index
fn_delta = delta_tool_calls.function
tool_call_id = delta_tool_calls.id
if len(current_message['tool_calls']) < index + 1:
tool_call = {
"id": tool_call_id or str(uuid.uuid4()),
"type": "function",
"function": {
"name":fn_delta.name,
"arguments": fn_delta.arguments
}
}
current_message['tool_calls'].append(tool_call)
else:
if fn_delta.name:
current_message['tool_calls'][index]["function"]["name"] = fn_delta.name
current_message['tool_calls'][index]["function"]["arguments"] += fn_delta.arguments
else:
m = None
done = False
if delta.content is not None:
if first_chunk:
self.cb('message', { 'content': "\n\x1b[1;32massistant\x1b[0m\n", 'done': True })
first_chunk = False
m = delta.content
if m is not None:
current_message['content'] += m
self.cb('message', { "content": m, "id": 'message_' + chunk.id, 'done': False })
if 'finish_reason' in choice and choice['finish_reason'] == 'stop':
done = True
self.cb('message', { "content": "", "id": 'message_' + chunk.id, 'done': True })
self.cb('message_stream', { "content": m if m else '', "id": 'message_' + chunk.id, 'done': done })
self.messages.append(current_message)
if len(current_message['tool_calls']) == 0:
del current_message['tool_calls']
self.show_cost()
else:
self.show_cost()
await self.process_tool_calls(current_message['tool_calls'])
return current_message
async def process_response(self, resp):
content = resp.choices[0].message.content
current_message = { 'role': 'assistant', 'content': content or '', 'tool_calls': [] }
for i, tool_call in enumerate(resp.choices[0].message.tool_calls or []):
current_message['tool_calls'].append({
"id": tool_call.id,
"type": "function",
"index": i,
"function": {
"name": tool_call.function.name,
"arguments": tool_call.function.arguments
},
})
if len(current_message['tool_calls']) == 0:
try:
tool_call = json.loads(content)
if 'name' in tool_call and tool_call['name'] in self.functions:
args = None
if 'arguments' in tool_call:
args = tool_call['arguments']
elif 'parameters' in tool_call:
args = tool_call['parameters']
if args:
current_message['tool_calls'].append({ "id": resp.id, "function": { "name": tool_call['name'], "arguments": json.dumps(args) } })
except Exception:
pass
self.messages.append(current_message)
if current_message['content'] != "":
self.cb('message', { 'content': "\n\x1b[1;32massistant\x1b[0m\n", 'done': True })
self.cb('message', { "content": current_message['content'], "id": 'message_' + resp.id, 'done': True })
if len(current_message['tool_calls']) == 0:
del current_message['tool_calls']
self.show_cost()
else:
self.show_cost()
await self.process_tool_calls(current_message['tool_calls'])
return current_message
async def async_response_generator(self, response):
for item in response:
resp = ModelResponse(stream=True, **item)
yield resp
async def attempt_completion(self):
stream = self.stream
if self.llama_instance:
args = {
"temperature": self.temperature,
"top_p": self.top_p,
"max_tokens": self.max_tokens,
"stream": stream,
}
if self.interpreter.env["chat.rawdog"] == "true":
res = create_chat_completion(self.interpreter, messages=self.messages, tools=self.tools, **args)
else:
res = self.llama_instance.create_chat_completion(self.messages, tools=self.tools, **args)
if args['stream']:
return self.async_response_generator(res)
else:
return ModelResponse(**next(res))
additional_args = {}
if self.is_reasoning:
additional_args['reasoning_effort'] = self.interpreter.env["chat.reasoning_effort"]
else:
additional_args['temperature'] = self.temperature
if self.interpreter.env["chat.openai_store"] == "true":
additional_args['store'] = True
if not self.check_max():
additional_args['tools'] = self.tools
additional_args['tool_choice'] = self.tool_choice
compl = await acompletion(
model=self.model,
messages=self.messages,
timeout=self.timeout,
max_completion_tokens=self.max_tokens,
**additional_args,
stream=stream,
)
return compl
def check_max(self):
if self.max_runs > 0 and self.n_runs >= self.max_runs - 1:
return True
return False
def show_cost(self):
def format_time(seconds):
minutes = int(seconds // 60)
remaining_seconds = int(seconds % 60)
if minutes > 0:
return f"{minutes}m {remaining_seconds}s"
return f"{remaining_seconds}s"
self.n_runs += 1
run_time = format_time(time.time() - self._last_run_time)
self._last_run_time = time.time()
total_time = format_time(time.time() - self._start_time)
if self.interpreter.env["chat.show_cost"] == "true" and self.n_runs > 0:
run_cost = 0
if not self.llama_instance:
run_cost = litellm.completion_cost(completion_response=self._last_response, messages=self.messages, model=self.model)
self.cost += run_cost
cb('usage', {
"model": self.model,
"run_cost": run_cost,
"total_cost": self.cost,
"n_runs": self.n_runs,
"max_runs": self.max_runs,
"run_time": run_time,
"total_time": total_time
})
async def get_completion(self):
stream = self.stream
if self.llama_instance:
response = await self.attempt_completion()
if stream:
return await self.process_streaming_response(response)
else:
return await self.process_response(response)
max_retries = 5
base_delay = 2
for retry_count in range(max_retries):
try:
response = await self.attempt_completion()
self._last_response = response
self.logger.debug(f'chat completion {response}')
res = None
if stream:
res = await self.process_streaming_response(response)
else:
res = await self.process_response(response)
return res
except Exception as e:
self.logger.error(f'Error getting completion: {e}')
if not _should_retry(getattr(e, 'status_code', None)) or retry_count == max_retries - 1:
raise
delay = base_delay * (2 ** retry_count)
self.logger.info(f"Retrying in {delay} seconds...")
await asyncio.sleep(delay)
raise Exception("Max retries reached. Unable to get completion.")
async def achat(self, messages=None) -> str:
if messages:
self.messages = messages
self.logger.debug(self.messages)
response = await self.get_completion()
return response
def chat(self, **kwargs) -> str:
return asyncio.run(self.achat(**kwargs))
def cb(type, data):
spinner.stop()
if type == 'message_stream':
if 'content' in data:
sys.stdout.write(data['content'])
elif type == 'tool_call':
builtins.print()
print_tool_call(data)
elif type == 'tool_response':
if 'content' in data:
sys.stdout.write(data['content'])
sys.stdout.flush()
builtins.print()
elif type == 'usage':
sys.stdout.flush()
builtins.print()
sys.stdout.write(f'\x1b[1;34m{data["model"]} | total: ${float(data["total_cost"]):.10f} | run: ${float(data["run_cost"]):.10f} | {data["n_runs"]} / {data["max_runs"]} | {data["run_time"]} / {data["total_time"]}\x1b[0m')
sys.stdout.flush()
builtins.print()
elif type == 'message' and data['done']:
if data['content']:
sys.stdout.flush()
sys.stdout.write(data['content'])
sys.stdout.flush()
else:
builtins.print()
def signal_handler(signum, frame):
raise KeyboardInterrupt
def chat(interpreter, **kwargs):
model = interpreter.model.replace(":", "/")
tools = [r2cmd, run_python, execute_binary]
messages = interpreter.messages
tool_choice = 'auto'
try:
loop = asyncio.get_event_loop()
if loop.is_closed() or loop.is_running():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
chat_auto = ChatAuto(
model,
max_tokens=int(interpreter.env["llm.maxtokens"]),
top_p=float(interpreter.env["llm.top_p"]),
temperature=float(interpreter.env["llm.temperature"]),
interpreter=interpreter,
tools=tools,
ask_to_execute=interpreter.env["auto.ask_to_execute"] == "true",
messages=messages,
tool_choice=tool_choice,
llama_instance=interpreter.llama_instance,
stream=interpreter.env["chat.stream"] == "true",
max_runs=int(interpreter.env["auto.max_runs"]),
cb=cb
)
original_handler = signal.getsignal(signal.SIGINT)
try:
signal.signal(signal.SIGINT, signal_handler)
spinner.start()
return loop.run_until_complete(chat_auto.achat())
except KeyboardInterrupt:
builtins.print("\033[91m\nOperation cancelled by user.\033[0m")
tasks = asyncio.all_tasks(loop=loop)
for task in tasks:
task.cancel()
try:
loop.run_until_complete(asyncio.gather(*tasks, return_exceptions=True))
loop.run_until_complete(asyncio.sleep(0.1))
except asyncio.CancelledError:
pass
return None
finally:
signal.signal(signal.SIGINT, original_handler)
spinner.stop()
litellm.in_memory_llm_clients_cache.flush_cache()
-3
View File
@@ -1,3 +0,0 @@
from .main import run # pylint: disable=wrong-import-position
run()
File diff suppressed because it is too large. Load diff
-13
View File
@@ -1,13 +0,0 @@
from typing import Iterable, Callable
class R2AiEnv(dict):
def __init__(self):
self._callbacks = {}
def add_callback(self, key: str, callback: Callable):
self._callbacks[key] = callback
def __setitem__(self, __key, __value) -> None:
if __key in self._callbacks:
self._callbacks[__key](__value)
return super().__setitem__(__key, __value)
-579
View File
@@ -1,579 +0,0 @@
import builtins
import re
import os
import sys
import traceback
import json
import platform
have_local = True
import getpass
try:
import tokentrim
except Exception:
have_local = False
from rich.rule import Rule
from signal import signal, SIGINT
from .env import R2AiEnv
from .large import Large
from .utils import merge_deltas
from .message_block import MessageBlock
from .code_block import CodeBlock
from .backend import kobaldcpp
from .models import get_hf_llm, new_get_hf_llm, get_default_model
from .voice import tts
from .const import R2AI_HOMEDIR
from . import LOGGER, logging
from .web import stop_http_server, server_running
from .progress import progress_bar
file_dir = os.path.dirname(__file__)
sys.path.append(file_dir)
import index
r2clippy = False
have_rlang = False
try:
import r2lang
have_rlang = True
print = r2lang.print
r2clippy = True
except Exception:
pass
Ginterrupted = False
def signal_handler(sig, frame):
global Ginterrupted
if Ginterrupted:
stop_http_server(force=True) # kill if in background
sys.exit(0) # throws exception
Ginterrupted = True
print("^C 0", file=sys.stderr)
stop_http_server(force=False)
signal(SIGINT, signal_handler)
def exception_handler(self, sig, frame):
global Ginterrupted
traceback.print_exc()
if Ginterrupted:
sys.exit(0) # throws exception
Ginterrupted = True
print("^C 1", file=sys.stderr)
# sys.excepthook = exception_handler
def incodeblock(msg):
return "content" in msg and msg["content"].count("```") % 2 == 1
def r2eval(m):
if "$(" in m and have_rlang:
def evaluate_expression(match):
expression = match.group(1)
try:
result = r2lang.cmd(expression)
return result
except Exception as e:
return f"Error: {e}"
return re.sub(r'\$\((.*?)\)', evaluate_expression, m)
return m
from utils import syscmdstr
def ddg(m):
print("[R2AI] Crawling the web with ddg and curl+sed")
m = m.replace("'", "")
res = syscmdstr(f"cd examples; ./scrap-ddgweb.sh '{m}'")
return f"Considering:\n```{res}\n```\n"
def is_litellm_model(model):
import warnings
warnings.filterwarnings('ignore')
import litellm
provider = None
model_name = None
if model.startswith ("/"):
return False
provider = None
if "/" in model:
provider = model.split("/")[0]
elif ":" in model:
provider = model.split(":")[0]
if provider in litellm.LITELLM_CHAT_PROVIDERS:
return True
return False
class Interpreter:
def __init__(self):
self.logger = LOGGER
self.mistral = None
self.messages = []
self.terminator = "</s>" ## taken from the model using the llama api
self.api_key = None # openai?
self.print = None
self.auto_run = False
self.model = get_default_model()
self.last_model = ""
self.env = R2AiEnv()
self.system_message = ""
self.env["llm.model"] = self.model ## TODO: dup. must get rid of self.model
self.env["llm.gpu"] = "true"
self.env["llm.layers"] = "-1"
self.env["llm.window"] = "32768" # "4096" # context_window
self.env["llm.maxtokens"] = "4096" # "1750"
self.env["llm.maxmsglen"] = "8096" # "1750"
self.env["llm.temperature"] = "0.002"
self.env["llm.repeat_penalty"] = "1.0"
self.env["llm.top_p"] = "0.95"
self.env["llm.top_k"] = "50"
self.env["user.name"] = "" # TODO auto fill?
self.env["user.os"] = ""
self.env["user.arch"] = ""
self.env["user.cwd"] = ""
self.env["user.editor"] = ""
self.env["user.plugins"] = f"{R2AI_HOMEDIR}/plugins"
self.env["voice.lang"] = "en"
self.env["voice.model"] = "base"
self.env["data.use"] = "false"
self.env["data.path"] = f"{R2AI_HOMEDIR}/doc/data"
self.env["data.local"] = "false"
self.env["data.wikit"] = "false"
self.env["data.ddg"] = "false"
self.env["data.mastodon"] = "false"
self.env["data.vectordb"] = "false"
self.env["data.hist"] = "false"
self.env["key.mastodon"] = ""
self.env["http.port"] = "8080"
self.env["http.tabby"] = "false"
self.env["http.path"] = ""
self.env["http.verbose"] = "true" # not used yet
self.env["http.chatctx"] = "false"
self.env["debug_level"] = os.getenv("R2AI_LOG", "2")
if have_rlang:
self.env["chat.live"] = "false"
else:
self.env["chat.live"] = "true"
# self.env["chat.model"] = "" # TODO
self.env["chat.trim"] = "false"
self.env["chat.voice"] = "false"
self.env["chat.bubble"] = "false"
self.env["chat.reply"] = "false"
self.env["chat.code"] = "true"
self.env["chat.rawdog"] = "false"
self.env["chat.reasoning_effort"] = "high"
self.env["chat.stream"] = "true"
self.env["chat.show_cost"] = "false"
self.env["chat.drop_params"] = "false"
self.env["chat.openai_store"] = "false"
self.env["auto.max_runs"] = "100"
self.env["auto.hide_tool_output"] = "false"
self.env["auto.init_commands"] = "aaa;iI;afl"
self.env["auto.ask_to_execute"] = "true" # ask to run tools such as r2cmd, run_python etc
self.env.add_callback("debug_level", lambda val: LOGGER.setLevel(int(val) * 10))
# No active block to start
# (blocks are visual representation of messages on the terminal)
self.active_block = None
# Note: While Open Interpreter can use Llama, we will prioritize gpt-4.
# gpt-4 is faster, smarter, can call functions, and is all-around easier to use.
# This makes gpt-4 better aligned with Open Interpreters priority to be easy to use.
self.llama_instance = None
self.large = Large(self)
def get_info_for_system_message(self):
"""Gets relevent information for the system message."""
info = ""
# Add user info
getpass.getuser()
os.getcwd()
platform.system()
# info += f"[User Info]\nName: {username}\nCWD: {current_working_directory}\nOS: {operating_system}"
return info
def reset(self):
"""Resets the interpreter."""
self.messages = []
def load(self, messages):
self.messages = messages
def save(self, f):
json.dumps(self.messages, f, indent=2)
def handle_undo(self, arguments):
"""Remove all messages after the most recent user entry (and the entry itself).
Therefore user can jump back to the latest point of conversation.
Also gives a visual representation of the messages removed."""
if len(self.messages) == 0:
return
# Find the index of the last 'role': 'user' entry
last_user_index = None
for i, message in enumerate(self.messages):
if message.get('role') == 'user':
last_user_index = i
removed_messages = []
# Remove all messages after the last 'role': 'user'
if last_user_index is not None:
removed_messages = self.messages[last_user_index:]
self.messages = self.messages[:last_user_index]
# Print out a preview of what messages were removed.
for message in removed_messages:
if message.get("content"):
print(f"**Removed message:** `\"{message['content'][:30]}...\"`")
elif 'function_call' in message:
print(f"**Removed codeblock**") # TODO: Could add preview of code removed here.
# reimplement using nltk or index.match
def keywords_ai(self, text):
# kws = self.keywords_ai("who is the author of radare?") => "author,radare2"
words = []
mmname = "TheBloke/Mistral-7B-Instruct-v0.2-GGUF"
ctxwindow = int(self.env["llm.window"])
mm = new_get_hf_llm(self, mmname, ctxwindow)
msg = f"Considering the sentence \"{text}\" as input, Take the KEYWORDS or combination of TWO words from the given text and respond ONLY a comma separated list of the most relevant words. DO NOT introduce your response, ONLY show the words"
msg = f"Take \"{text}\" as input, and extract the keywords and combination of keywords to make a search online, the output must be a comma separated list" #Take the KEYWORDS or combination of TWO words from the given text and respond ONLY a comma separated list of the most relevant words. DO NOT introduce your response, ONLY show the words"
response = mm(msg, stream=False, temperature=0.1, stop="</s>", max_tokens=1750)
self.logger.debug(response)
text0 = response["choices"][0]["text"]
text0 = text0.replace('"', ",")
if text0.startswith("."):
text0 = text0[1:].strip()
try:
text0 = text0.split(":")[1].strip()
except Exception:
pass
# print(text0)
mm = None
return [word.strip() for word in text0.split(',')]
@progress_bar("Thinking", color="yellow")
def chat(self, message=None):
global print
global Ginterrupted
if self.print is not None:
print = self.print
if self.last_model != self.model:
# self.llama_instance = None
self.last_model = self.model
if not message and self.env["chat.code"] == "true":
self.end_active_block()
self.logger.error("Missing message")
return
if self.env["data.use"] == "true":
use_hist = self.env["data.hist"] == "true"
use_wikit = self.env["data.wikit"] == "true"
use_mastodon = self.env["data.mastodon"] == "true"
use_vectordb = self.env["data.vectordb"] == "true"
use_debug = LOGGER.level == logging.DEBUG
datadir = None
keywords = None
if use_mastodon:
keywords = self.keywords_ai(message)
if self.env["data.local"] == "true":
datadir = self.env["data.path"]
if self.env["data.ddg"] == "true":
results = ddg(message)
if use_vectordb:
index.memorize(message, results)
else:
self.messages.append({"role": "hint", "content": results})
matches = index.match(message, keywords, datadir, use_hist, use_mastodon, use_debug, use_wikit, use_vectordb)
if not matches:
matches = []
if len(matches) > 0:
for m in matches:
self.logger.debug("HINT: " + m)
self.messages.append({"role": "hint", "content": r2eval(m)})
self.logger.debug(message)
# print(message)
# Local model -- TODO: assume local models don't have : in the name?
if (
not self.model.startswith("openai:") and
not self.model.startswith("openapi:") and
not self.model.startswith("google:") and
not self.model.startswith("kobaldcpp") and
not self.model.startswith("bedrock:") and
not is_litellm_model(self.model)
):
self.logger = LOGGER.getChild(f"local:{self.model}")
# Find or install Code-Llama
try:
ctxwindow = int(self.env["llm.window"])
self.llama_instance = new_get_hf_llm(self, self.model, ctxwindow)
if self.llama_instance is None:
self.logger.error("Cannot find model " + self.model)
return
except Exception:
traceback.print_exc()
# If it was, we respond non-interactively
self.messages.append({"role": "user", "content": message})
response = None
try:
response = self.respond()
self.clear_hints()
except Exception:
if Ginterrupted:
Ginterrupted = False
else:
traceback.print_exc()
self.end_active_block()
return response
def end_active_block(self):
# if self.env["chat.code"] == "false":
# return
if self.active_block:
self.active_block.end()
self.active_block = None
def environment(self):
kvs = ""
if self.env["user.name"] != "":
kvs += "Name: " + self.env["user.name"] + "\n"
if self.env["user.os"] != "":
kvs += "OS: " + self.env["user.os"] + "\n"
if self.env["user.cwd"] != "":
kvs += "CWD: " + self.env["user.cwd"] + "\n"
if self.env["user.editor"] != "":
kvs += "EDITOR: " + self.env["user.editor"] + "\n"
if self.env["user.arch"] != "":
kvs += "ARCH: " + self.env["user.arch"] + "\n"
# info += f"[User Info]\nName: {username}\nCWD: {current_working_directory}\nOS: {operating_system}"
if kvs != "":
return "[User Info]\n" + kvs
return ""
def clear_hints(self):
res = []
for msg in self.messages:
if "role" in msg and msg["role"] != "hint":
res.append(msg)
self.messages = res
def respond(self):
global Ginterrupted
self.logger = LOGGER.getChild(self.model.split(":")[0])
maxtokens = int(self.env["llm.maxtokens"])
# Add relevant info to system_message
# (e.g. current working directory, username, os, etc.)
info = self.get_info_for_system_message()
# This is hacky, as we should have a different (minified) prompt for CodeLLama,
# but for now, to make the prompt shorter and remove "run_code" references, just get the first 2 lines:
self.system_message = "\n".join(self.system_message.split("\n")[:2])
# self.system_message += "\nOnly do what the user asks you to do, then ask what they'd like to do next."
system_message = self.system_message + "\n\n" + info
system_message += self.environment()
if self.env["chat.trim"] == "true":
## this stupid function is slow as hell and doesn not provides much goodies
## just ignore it by default
messages = tokentrim.trim(self.messages, max_tokens=maxtokens, system_message=system_message)
else:
messages = self.large.compress_messages(self.messages)
self.logger.debug(messages)
# builtins.print(prompt)
response = None
if self.auto_run:
from . import auto
import litellm
if self.env["chat.drop_params"] == "true":
litellm.drop_params = True
else:
litellm.drop_params = False
if(is_litellm_model(self.model)):
response = auto.chat(self)
else:
self.llama_instance = new_get_hf_llm(self, self.model, int(self.env["llm.window"]))
response = auto.chat(self)
return
elif self.model.startswith("kobaldcpp"):
if self.system_message != "":
message = f"Context:\n```\n{self.system_message}\n```\n"
else:
message = ""
#f"{Your name is r2ai, an assistant for radare2. User will ask about actions and you must respond with the radare2 command associated or the answer to the question. Be precise and concise when answering"
for m in messages:
role = m["role"]
content = m["content"]
if role == "user":
message += f"User: {content}\n"
elif role == "assistant":
message += f"AI: {content}\n"
response = ""
if ":" in self.model:
uri = self.model.split(":")[1:]
response = kobaldcpp.chat(message, ":".join(uri))
else:
response = kobaldcpp.chat(message)
if "content" in self.messages[-1]:
last_message = self.messages[-1]["content"]
if self.env["chat.reply"] == "true":
self.messages.append({"role": "assistant", "content": response})
print(response)
return
elif is_litellm_model(self.model):
# [
# {"role": "system", "content": "You are a poetic assistant, be creative."},
# {"role": "user", "content": "Compose a poem that explains the concept of recursion in programming."}
# ]
from litellm import completion as litellm_completion
completion = None
if self.model.startswith("ollama/"):
completion = litellm_completion(
model=self.model,
messages=self.messages,
drop_params=True,
max_completion_tokens=maxtokens,
temperature=float(self.env["llm.temperature"]),
top_p=float(self.env["llm.top_p"]),
)
elif self.model.startswith("mistral/"):
completion = litellm_completion(
drop_params=True,
model=self.model.replace(":", "/"),
messages=self.messages,
max_completion_tokens=maxtokens,
temperature=float(self.env["llm.temperature"]),
top_p=float(self.env["llm.top_p"]),
)
else:
completion = litellm_completion(
model=self.model.replace(":", "/"),
messages=self.messages,
max_completion_tokens=maxtokens,
temperature=float(self.env["llm.temperature"]),
top_p=float(self.env["llm.top_p"]),
)
response = completion.choices[0].message.content
if "content" in self.messages[-1]:
last_message = self.messages[-1]["content"]
if self.env["chat.reply"] == "true":
self.messages.append({"role": "assistant", "content": response})
print(response)
return response
else:
# non-openai aka local-llama model
if self.llama_instance == None:
self.logger.critical("Llama is not instantiated")
return
try:
if type(self.terminator).__name__ == "list":
terminator = self.terminator
else:
terminator = [self.terminator]
chat_args = {
"stream": True,
"temperature": float(self.env["llm.temperature"]),
"repeat_penalty": float(self.env["llm.repeat_penalty"]),
"top_p": float(self.env["llm.top_p"]),
"top_k": int(self.env["llm.top_k"]),
"stop": terminator,
"max_tokens": maxtokens
}
if self.env["chat.rawdog"] == "true":
from .completion import messages_to_prompt
allmsg = messages.copy()
allmsg.insert(0, ChatMessage(role=MessageRole.SYSTEM, content=self.system_message))
prompt = messages_to_prompt(self, allmsg)
response = self.llama_instance(prompt, **chat_args)
else:
all_messages = messages.copy()
all_messages.insert(0, {"role": "system", "content": self.system_message})
response = self.llama_instance.create_chat_completion(all_messages, **chat_args)
except Exception as err:
traceback.print_exc()
print(Exception, err)
if Ginterrupted:
Ginterrupted = False
return
if response is None:
print("No response")
ctxwindow = int(self.env["llm.window"])
self.llama_instance = new_get_hf_llm(self, self.model, ctxwindow)
return
# Initialize message, function call trackers, and active block
self.messages.append({})
in_function_call = False
self.active_block = MessageBlock()
for chunk in response:
if Ginterrupted:
Ginterrupted = False
break
text = ''
delta = None
if self.env["chat.rawdog"] == "true":
if "content" not in messages[-1]:
text = chunk["choices"][0]['text'].capitalize()
messages[-1] = { "content": '', "role": "assistant" }
else:
text = chunk["choices"][0]['text']
messages[-1] = merge_deltas(messages[-1], {"content": text})
else:
if "content" not in messages[-1]:
if "text" in chunk["choices"][0]['delta']:
text = chunk["choices"][0]['delta']["content"]
messages[-1] = { "content": text, "role": "assistant" }
delta = {"content": text}
elif "content" in chunk['choices'][0]['delta']:
delta = {"content": chunk['choices'][0]['delta']['content']}
# Accumulate deltas into the last message in messages
if delta:
self.messages[-1] = merge_deltas(self.messages[-1], delta)
if self.env["chat.live"] != "true":
continue
flushed = False
if self.env["chat.code"] == "true":
if incodeblock(self.messages[-1]):
if in_function_call == False:
in_function_call = True
self.active_block.update_from_message(self.messages[-1])
self.end_active_block()
# flushed = True
self.active_block = CodeBlock()
else:
if in_function_call == True:
in_function_call = False
self.end_active_block()
flushed = True
self.active_block = MessageBlock()
if self.env["chat.live"] == "true": # and self.env["chat.code"] == "true":
self.active_block.update_from_message(self.messages[-1])
if flushed:
self.messages[-1]["content"] = ""
continue # end of for loop
self.end_active_block()
self.active_block = None
output_text = ""
if len(self.messages) > 0 and "content" in self.messages[-1]:
output_text = self.messages[-1]["content"].strip()
if self.env["chat.reply"] == "true":
self.messages.append({"role": "assistant", "content": output_text})
if self.env["chat.voice"] == "true":
tts("(assistant)", output_text, self.env["voice.lang"])
elif self.env["chat.live"] != "true":
try:
r2lang.print(output_text)
except Exception:
print(output_text)
# print(str(self.messages))
-223
View File
@@ -1,223 +0,0 @@
class IncompleteJSONParser:
def __init__(self, s):
self.s = s
self.len = len(s)
self.pos = 0
def parse(self):
self.skip_whitespace()
if self.pos >= self.len:
return None
char = self.s[self.pos]
if char == '{':
return self.parse_object()
elif char == '[':
return self.parse_array()
else:
return None # Top-level must be object or array
def parse_object(self):
if self.s[self.pos] != '{':
return None
self.pos += 1 # Skip '{'
self.skip_whitespace()
obj = {}
parsed_any = False # Flag to check if any key-value pair was parsed
while self.pos < self.len:
self.skip_whitespace()
if self.pos >= self.len:
break
if self.s[self.pos] == '}':
self.pos += 1
return obj if parsed_any else None
key = self.parse_string()
if key is None:
# Incomplete key, skip the rest
break
self.skip_whitespace()
if self.pos >= self.len or self.s[self.pos] != ':':
# Missing colon, skip this key
break
self.pos += 1 # Skip ':'
self.skip_whitespace()
value = self.parse_value()
if value is None:
# Incomplete value, skip this key-value pair
break
obj[key] = value
parsed_any = True
self.skip_whitespace()
if self.pos >= self.len:
break
if self.s[self.pos] == ',':
self.pos += 1
continue
elif self.s[self.pos] == '}':
self.pos += 1
return obj if parsed_any else None
else:
# Unexpected character, skip
break
# Auto-close the object
return obj if parsed_any else None
def parse_array(self):
if self.s[self.pos] != '[':
return None
self.pos += 1 # Skip '['
self.skip_whitespace()
array = []
parsed_any = False # Flag to check if any element was parsed
while self.pos < self.len:
self.skip_whitespace()
if self.pos >= self.len:
break
if self.s[self.pos] == ']':
self.pos += 1
return array if parsed_any else None
value = self.parse_value()
if value is None:
# Incomplete value, skip this element
break
array.append(value)
parsed_any = True
self.skip_whitespace()
if self.pos >= self.len:
break
if self.s[self.pos] == ',':
self.pos += 1
continue
elif self.s[self.pos] == ']':
self.pos += 1
return array if parsed_any else None
else:
# Unexpected character, skip
break
# Auto-close the array
return array if parsed_any else None
def parse_value(self):
self.skip_whitespace()
if self.pos >= self.len:
return None
char = self.s[self.pos]
if char == '"':
return self.parse_string()
elif char == '{':
return self.parse_object()
elif char == '[':
return self.parse_array()
elif char in '-0123456789':
return self.parse_number()
elif self.s.startswith('true', self.pos):
self.pos += 4
return True
elif self.s.startswith('false', self.pos):
self.pos += 5
return False
elif self.s.startswith('null', self.pos):
self.pos += 4
return None
else:
return None # Invalid value
def parse_string(self):
if self.s[self.pos] != '"':
return None
self.pos += 1 # Skip opening quote
result = ""
while self.pos < self.len:
char = self.s[self.pos]
if char == '\\':
if self.pos + 1 >= self.len:
result += '\\'
self.pos += 1
break # Incomplete escape, return what we have
self.pos += 1
escape_char = self.s[self.pos]
if escape_char == '"':
result += '"'
elif escape_char == '\\':
result += '\\'
elif escape_char == '/':
result += '/'
elif escape_char == 'b':
result += '\b'
elif escape_char == 'f':
result += '\f'
elif escape_char == 'n':
result += '\n'
elif escape_char == 'r':
result += '\r'
elif escape_char == 't':
result += '\t'
elif escape_char == 'u':
# Unicode escape
if self.pos + 4 >= self.len:
# Incomplete unicode escape
break
hex_digits = self.s[self.pos+1:self.pos+5]
try:
code_point = int(hex_digits, 16)
result += chr(code_point)
self.pos += 4
except ValueError:
break # Invalid unicode escape
else:
# Invalid escape character
result += '\\' + escape_char # Keep it as is
elif char == '"':
self.pos += 1 # Skip closing quote
return result
else:
result += char
self.pos += 1
# Return the partial string if incomplete
return result
def parse_number(self):
start = self.pos
if self.s[self.pos] == '-':
self.pos += 1
if self.pos >= self.len:
return None
if self.s[self.pos] == '0':
self.pos += 1
elif '1' <= self.s[self.pos] <= '9':
while self.pos < self.len and self.s[self.pos].isdigit():
self.pos += 1
else:
return None # Invalid number
if self.pos < self.len and self.s[self.pos] == '.':
self.pos += 1
if self.pos >= self.len or not self.s[self.pos].isdigit():
return None # Incomplete fraction
while self.pos < self.len and self.s[self.pos].isdigit():
self.pos += 1
if self.pos < self.len and self.s[self.pos] in 'eE':
self.pos += 1
if self.pos < self.len and self.s[self.pos] in '+-':
self.pos += 1
if self.pos >= self.len or not self.s[self.pos].isdigit():
return None # Incomplete exponent
while self.pos < self.len and self.s[self.pos].isdigit():
self.pos += 1
num_str = self.s[start:self.pos]
try:
if '.' in num_str or 'e' in num_str or 'E' in num_str:
return float(num_str)
else:
return int(num_str)
except ValueError:
return None # Invalid number
def skip_whitespace(self):
while self.pos < self.len and self.s[self.pos] in ' \t\n\r':
self.pos += 1
def parse_incomplete_json(s):
parser = IncompleteJSONParser(s)
result = parser.parse()
return result
-47
View File
@@ -1,47 +0,0 @@
"""Entrypoint for the r2ai plugin and repl."""
import sys
import os
import builtins
import traceback
current_dir = os.path.dirname(os.path.realpath(__file__))
parent_dir = os.path.dirname(current_dir)
sys.path.insert(0, parent_dir)
os.environ["TRANSFORMERS_NO_ADVISORY_WARNINGS"] = "1"
try:
venv_dir = os.path.join(parent_dir, 'venv')
if os.path.exists(venv_dir):
site_packages = os.path.join(venv_dir, 'lib', 'python{}.{}'.format(*sys.version_info[:2]), 'site-packages')
if os.path.exists(site_packages):
sys.path.insert(0, site_packages)
except Exception:
pass
import r2lang
from r2ai.main import r2ai_singleton, run_rcfile_once, runline, help_message
def r2ai_rlang_plugin(unused_but_required_argument):
ai = r2ai_singleton()
def _call(s):
if not s.startswith("r2ai"):
return False
try:
run_rcfile_once(ai)
if len(s) == 4:
builtins.print(help_message)
else:
usertext = s[4:].strip()
runline(ai, usertext)
except Exception as e:
builtins.print(e)
traceback.print_exc()
return True
return {
"name": "r2ai",
"license": "MIT",
"desc": "run llama language models inside r2",
"call": _call,
}
r2lang.plugin("core", r2ai_rlang_plugin)
-42
View File
@@ -1,42 +0,0 @@
import itertools
import threading
import time
import sys
class Spinner:
def __init__(self, message="Loading...", delay=0.1):
self.spinner = itertools.cycle([
"\033[1;31m⠋\033[0m", "\033[1;32m⠙\033[0m", "\033[1;33m⠹\033[0m", "\033[1;34m⠸\033[0m",
"\033[1;35m⠼\033[0m", "\033[1;36m⠴\033[0m", "\033[1;37m⠦\033[0m", "\033[1;31m⠧\033[0m",
"\033[1;32m⠇\033[0m", "\033[1;33m⠏\033[0m"
])
self.message = message
self.delay = delay
self.running = False
self.thread = None
self.start_time = None
def start(self):
"""Start the spinner in a separate thread."""
self.running = True
self.start_time = time.time()
self.thread = threading.Thread(target=self._spin)
self.thread.start()
def _spin(self):
"""Spin the spinner while running is True."""
while self.running:
elapsed_time = time.time() - self.start_time
sys.stdout.write(f"\r{self.message} {next(self.spinner)} {elapsed_time:.1f}s")
sys.stdout.flush()
time.sleep(self.delay)
sys.stdout.write('\r' + ' ' * (len(self.message) + 20) + '\r') # Clear the line
sys.stdout.flush()
def stop(self):
"""Stop the spinner."""
self.running = False
if self.thread is not None:
self.thread.join()
spinner = Spinner("")
-37
View File
@@ -1,37 +0,0 @@
import builtins
from .tools import run_python, execute_binary, r2cmd
import subprocess
from .pipe import get_filename
import time
py_code = """
print('hello test')
"""
def run_test(args):
if not args or len(args) == 0:
res = run_python(py_code).strip()
print(f"run_python: {res}", len(res))
assert res == "hello test"
print("run_python: test passed")
r2cmd("o--;o /bin/ls")
res = execute_binary(args=["-d", "/etc"]).strip()
subp = subprocess.run(["/bin/ls", "-d", "/etc"], capture_output=True, text=True)
print("exec result", res)
print("subp result", subp.stdout)
assert ''.join(res).strip() == subp.stdout.strip()
print("execute_binary with args: test passed")
else:
cmd, *args = args.split(" ", 1)
if cmd == "get_filename":
builtins.print(get_filename())
elif cmd == "run_python":
builtins.print(f"--- args ---")
builtins.print(args)
builtins.print(f"--- end args ---")
builtins.print(f"--- result ---")
builtins.print(run_python(args[0]))
builtins.print(f"--- end result ---")
elif cmd == "r2cmd":
builtins.print(f"--- {args} ---")
builtins.print(r2cmd(args))
builtins.print("--- end ---")
-176
View File
@@ -1,176 +0,0 @@
from r2ai.pipe import get_r2_inst
import json
import builtins
import base64
from .pipe import get_filename
from . import LOGGER
import time
import sys
from io import StringIO
import subprocess
import os
is_plugin = False
try:
import r2lang
is_plugin = True
except Exception:
is_plugin = False
pass
def r2cmd(command: str):
"""
Run a r2 command and return the output
Parameters
----------
command: str
The r2 command to run
Returns
-------
dict
The output of the r2 command
"""
r2 = get_r2_inst()
r2.cmd('e scr.color=3')
if command.startswith('r2 '):
return "You are already in r2!"
cmd = '{"cmd":' + json.dumps(command) + '}'
res = r2.cmd(cmd)
try:
res = json.loads(res)
if 'error' in res and res['error'] is True:
error_message = res['error']
log_messages = '\n'.join(log['message'] for log in res.get('logs', []))
# return { 'type': 'error', 'output': log_messages }
return log_messages
return res['res']
except json.JSONDecodeError:
if type(res) == str:
spl = res.strip().split('\n')
if spl[-1].startswith('{"res":""'):
res = '\n'.join(spl[:-1])
return res
except Exception as e:
# return { 'type': 'error', 'output': f"Error running r2cmd: {e}\nCommand: {command}\nResponse: {res}" }
return f"Error running r2cmd: {e}\nCommand: {command}\nResponse: {res}"
def run_python(command: str):
"""
Run a python script and return the output
Parameters
----------
command: str
The python script to run
Returns
-------
str
The output of the python script
"""
r2 = get_r2_inst()
res = ""
is_plugin = False
python_path = sys.executable
try:
proc = subprocess.run([python_path, '-c', command],
capture_output=True,
text=True)
res = proc.stdout
if proc.stderr:
res += proc.stderr
except Exception as e:
res = str(e)
# if is_plugin:
# base64cmd = base64.b64encode(command.encode('utf-8')).decode('utf-8')
# res += r2cmd(f'#!python -e base64:{base64cmd} > .r2ai_tmp.log')
# res += r2cmd('cat .r2ai_tmp.log')
# r2cmd('rm .r2ai_tmp.log')
# else:
# with open('r2ai_tmp.py', 'w') as f:
# f.write(command)
# r2 = get_r2_inst()
# res += r2cmd('#!python r2ai_tmp.py > .r2ai_tmp.log')
# time.sleep(0.1)
# res += r2cmd('!cat .r2ai_tmp.log')
# LOGGER.debug(f'run_python: {res}')
# # r2cmd('rm r2ai_tmp.py')
# # r2cmd('rm .r2ai_tmp.log')
return res
schemas = {
"execute_binary": {
"name": "execute_binary",
"description": "Execute a binary with the given arguments and stdin",
"parameters": {
"type": "object",
"properties": {
"args": {
"description": "The arguments to pass to the binary. Do not include the file name.",
"type": "array",
"items": {
"type": "string"
}
},
"stdin": {
"type": "string"
}
}
}
}
}
def execute_binary(args: list[str] = [], stdin: str = ""):
filename = get_filename()
if filename:
if os.path.isabs(filename):
abs_path = os.path.abspath(filename)
if os.path.exists(abs_path):
filename = abs_path
else:
cwd_path = os.path.join(os.getcwd(), filename)
if os.path.exists(cwd_path):
filename = cwd_path
try:
cmd = [filename] + args
proc = subprocess.run(cmd, input=stdin, capture_output=True, text=True)
res = proc.stdout
if proc.stderr:
res += proc.stderr
return res
except Exception as e:
return str(e)
return ""
# r2 = get_r2_inst()
# if stdin:
# r2.cmd(f'dor stdin={json.dumps(stdin)}')
# if len(args) > 0:
# r2.cmd(f"ood {' '.join(args)}")
# else:
# r2.cmd("ood")
# res = r2cmd("dc")
# return res
def print_tool_call(msg):
if msg['function']['name'] == 'r2cmd':
builtins.print('\x1b[1;32m> \x1b[4m' + msg['function']['arguments']['command'] + '\x1b[0m')
elif msg['function']['name'] == 'run_python':
builtins.print('\x1b[1;32m> \x1b[4m' + "#!python" + '\x1b[0m')
builtins.print(msg['function']['arguments']['command'])
elif msg['function']['name'] == 'execute_binary':
filename = get_filename() or 'bin'
stdin = msg['function']['arguments']['stdin'] if 'stdin' in msg['function']['arguments'] else None
args = msg['function']['arguments']['args'] if 'args' in msg['function']['arguments'] else []
cmd = filename
if args and len(args) > 0:
cmd += ' ' + ' '.join(args)
if stdin and len(stdin) > 0:
cmd += f' stdin={stdin}'
builtins.print('\x1b[1;32m> \x1b[4m' + cmd + '\x1b[0m')
-25
View File
@@ -1,25 +0,0 @@
from litellm import acompletion, ChatCompletionAssistantToolCall, ChatCompletionToolCallFunctionChunk
import asyncio
import json
import signal
from r2ai.pipe import get_r2_inst
from r2ai.tools import run_python, r2cmd, execute_binary
from r2ai.repl import r2ai_singleton
from r2ai.auto import ChatAuto, SYSTEM_PROMPT_AUTO
from r2ai.interpreter import is_litellm_model
from r2ai.models import new_get_hf_llm
def signal_handler(signum, frame):
raise KeyboardInterrupt
async def chat(ai, message, cb):
model = ai.model.replace(":", "/")
tools = [r2cmd, run_python, execute_binary]
ai.messages.append({"role": "user", "content": message})
tool_choice = 'auto'
if not is_litellm_model(model) and ai and not ai.llama_instance:
ai.llama_instance = new_get_hf_llm(ai, model, int(ai.env["llm.window"]))
chat_auto = ChatAuto(model, interpreter=ai, system=SYSTEM_PROMPT_AUTO, tools=tools, messages=ai.messages, tool_choice=tool_choice, cb=cb)
return await chat_auto.achat()
-25
View File
@@ -1,25 +0,0 @@
# r2ai-server
Start ollama, llamacpp, llamafile, r2ai, kobaldcpp and other language model webservers using a the same syntax for all of them to simplify its launching and setup.
* Use -l to list the implementations available
You can install more via r2pm
* Use -m to list or select the model. Those models can be absolute paths or model names when downloaded via r2ai, which uses the hugging face api
## Running r2ai-server
- Get usage: `r2pm -r r2ai-server`
- List available servers: `r2pm -r r2ai-server -l`
- List available models: `r2pm -r r2ai-server -m`
On Linux, models are stored in `~/.r2ai.models/`. File `~/.r2ai.model` lists the default model and other models.
**Example launching a local Mistral AI server:**
```
$ r2pm -r r2ai-server -l r2ai -m mistral-7b-instruct-v0.2.Q2_K
[12/13/24 10:35:22] INFO r2ai.server - INFO - [R2AI] Serving at port 8080 web.py:336
```
+89 -6
View File
@@ -1,9 +1,92 @@
TARGETS=all clean mrproper user-install install user-uninstall uninstall fmt indent
R2_USER_PLUGINS=$(shell r2 -H R2_USER_PLUGINS)
PWD=$(shell pwd)
R2PM_BINDIR=$(shell r2pm -H R2PM_BINDIR)
PV=3.12
ifeq ($(shell which python${PV} > /dev/null && echo ok),ok)
PYTHON?=python${PV}
else
PYTHON?=python3
endif
PIP=$(PYTHON) -m pip
$(TARGETS):
$(MAKE) -C src $@
LINTED=r2ai/code_block.py
LINTED+=r2ai/bubble.py
LINTED+=r2ai/const.py
LINTED+=main.py
LINTED+=r2ai/backend/kobaldcpp.py
# LINTED+=r2ai/index.py
# LINTED+=r2ai/voice.py
# LINTED+=r2ai/anthropic.py
test: all
$(MAKE) -C test
ifeq ($(R2PM_BINDIR),)
FATAL ERROR
endif
.PHONY: $(TARGETS) test
.PHONY: all all.old deps clean deps-global pub lint cilint
.PHONY: install uninstall user-install user-uninstall
all: venv
@./r2ai.sh
large:
. venv/bin/activate ; $(PYTHON) main.py -l
all.old:
@test -n "${VIRTUAL_ENV}" || (echo "Run:"; echo ". venv/bin/activate" ; exit 1)
$(PYTHON) main.py || $(MAKE) deps
venv:
$(PYTHON) -m venv venv
if [ -z "`find venv | grep llama_cpp`" ]; then . venv/bin/activate ; pip install -r requirements.txt ; fi
deps: venv
#test -n "${VIRTUAL_ENV}" || (echo "Run: . venv/bin/activate" ; exit 1)
. venv/bin/activate && export CMAKE_ARGS="-DLLAMA_METAL=on -DLLAMA_METAL_EMBED_LIBRARY=ON" && \
pip install --force-reinstall -U -r requirements.txt --no-cache-dir
clean:
rm -rf venv
mrproper:
$(MAKE) clean
deps-global:
export CMAKE_ARGS="-DLLAMA_METAL=on -DLLAMA_METAL_EMBED_LIBRARY=ON" && \
$(PIP) install --force-reinstall -U -r requirements.txt --break-system-packages --no-cache-dir
user-install:
rm -f $(R2PM_BINDIR)/r2ai-server
ln -fs $(PWD)/r2ai-server/r2ai-server $(R2PM_BINDIR)/r2ai-server
rm -f $(R2PM_BINDIR)/r2ai
ln -fs $(PWD)/r2ai.sh $(R2PM_BINDIR)/r2ai
install: user-install
-mkdir -p /usr/local/share/man/man1/r2ai.1
-cp doc/usage/r2ai.1 /usr/local/share/man/man1/r2ai.1
install-decai:
$(MAKE) -C decai user-install
install-server:
$(MAKE) -C r2ai-server user-install
install-plugin user-install-plugin:
ln -fs $(PWD)/main.py $(R2_USER_PLUGINS)/r2ai.py
uninstall user-uninstall:
rm -f $(R2PM_BINDIR)/r2ai
-rm -f /usr/local/share/man/man1/r2ai.1
user-uninstall-plugin uninstall-plugin:
rm -f $(R2_USER_PLUGINS)/r2ai.py
pub:
$(PYTHON) setup.py build sdist
twine check dist/*
twine upload -u __token__ --repository-url https://upload.pypi.org/legacy/ --verbose dist/*
cilint:
pylint $(LINTED)
lint:
pylint *.py r2ai/*.py
+105 -82
View File
@@ -1,115 +1,138 @@
# R2AI - Augmented reversing with LLM for radare2
```console
╭─────────────────────────────────╮
│ ,______ .______ .______ ,___ │
╭──╮ │ : __ \ \____ |: \ : __| │
│ _│_ │ | \____|/ ____|| _,_ || : | │
│ O O < | : \ \ . || : || | │
│ │╷ │ | |___\ \__:__||___| || | │
│ ││ │ |___| : |___||___| │
│ ─╯│ ╰─────────────────────────────────╯
╰───╯
```
,______ .______ .______ ,___
: __ \ \____ |: \ : __|
| \____|/ ____|| _,_ || : |
| : \ \ . || : || |
| |___\ \__:__||___| || |
|___| : |___||___|
* --pancake
```
[![ci](https://github.com/radareorg/r2ai/actions/workflows/ci.yml/badge.svg)](https://github.com/radareorg/r2ai/actions/workflows/ci.yml)
[![radare2](https://img.shields.io/badge/radare2-6.0.4-green)](https://github.com/radareorg/radare2)
Run a language model in local, without internet, to entertain you or help answering questions about radare2 or reverse engineering in general. Note that models used by r2ai are pulled from external sources which may behave different or respond unrealible information. That's why there's an ongoing effort into improving the post-finetuning using memgpt-like techniques which can't get better without your help!
<p align="center">
<img src="doc/r2clippy.jpg">
</p>
## Components
This repository contains two plugins for radare2:
R2AI is structured into four independent components:
* **r2ai** - native [AI plugin](./src/README.md) for radare2
* **decai** - r2js plugin with special [focus on decompilation](./decai/README.md)
If you are looking to use radare2 with other agents via MCP:
* **r2mcp** - the [official radare2 mcp](https://github.com/radare2/radare2-mcp)
* **r2copilot** - the mcp with focus on CTF [r2copilot](https://github.com/darallium/r2-copilot)
If you are looking for a radare2 focused autonomous agent:
* **r2agent** - automate radare2 workflows through autonomous agents. [r2agent](https://github.com/nitanmarcel/r2agent)
* r2ai (wip r2ai-native rewrite in C)
* r2-like repl using r2pipe to comunicate with r2
* supports auto solving mode
* client and server openapi protocol
* download and manage models from huggingface
* decai
* lightweight r2js plugin
* focus on decompilation
* talks to r2ai, r2ai-server, openai, anthropic or ollama
* r2ai-plugin
* requires r2lang-python
* adds r2ai command inside r2
* not recommended because of python versions pain
* r2ai-server
* list and select models downloaded from r2ai
* simple cli tool to start local openapi webservers
* supports llamafile, llamacpp, r2ai-w and kobaldcpp
## Features
* Configure different roles and customize prompts
* Scriptable via r2pipe via the r2ai command
* Live with repl and batch mode from cli or r2 prompt
* Support Automatic (ReAct) mode to solve tasks using function calling
* Use local and remote language models (ollama, openai, grok, anthropic, ..)
* RAG markdown, code or textfiles using its native vector database
* Support Auto mode (see below) to solve tasks using function calling
* Use local and remote language models (llama, ollama, openai, anthropic, ..)
* Support OpenAI, Anthropic, Bedrock
* Index large codebases or markdown books using a vector database
* Slurp file and perform actions on that
* Embed the output of an r2 command and resolve questions on the given data
## User defined Prompts
```console
[0x00000000]> r2ai -q
explain: Explain the current function -
devices: Find and explain devices used -
libs: Group imports by Libraries -
varnames: Better variable names -
autoname: Automatically suggest a better name for this function -
vulns: Find vulnerabilities or bugs in the current function -
signature: Suggest an improved function signature -
dlopen: List libraries loaded with dlopen - Some libraries are loaded
decompile: Augmented decompilation based on LLM -
[0x00000000]>
```
* Define different system-level assistant role
* Set environment variables to provide context to the language model
* Live with repl and batch mode from cli or r2 prompt
* Accessible as an r2lang-python plugin, keeps session state inside radare2
* Scriptable from python, bash, r2pipe, and javascript (r2papi)
* Use different models, dynamically adjust query template
* Load multiple models and make them talk between them
## Installation
The recommended way to install any of the r2ai components is via r2pm:
Running `make` will setup a python virtual environment in the current directory installing all the necessary dependencies and will get into a shell to run r2ai.
```console
$ r2pm -Uci r2ai
$ r2pm -Uci decai
The installation is now splitted into two different targets:
* `make install` will place a symlink in `$BINDIR/r2ai`
* `make install-plugin` will install the native r2 plugin into your home
* `make install-decai` will install the decai r2js decompiler plugin
* `make install-server` will install the decai r2js decompiler plugin
## Running
When installed via r2pm you can execute it like this:
```bash
r2pm -r r2ai
```
## Using r2ai
Additionally you can get the `r2ai` command inside r2 to run as an rlang plugin by installing the bindings:
- Adds the **r2ai** command to the radare2 shell: `r2 -qc r2ai`
- You can also run the wrapper in $PATH: `r2pm -r r2ai`
Drop your API keys in environment variables or use the configuration file:
```console
$ export ANTHROPIC_API_KEY=sk-ant-api03-CENSORED
$ export OPENAI_API_KEY=sk-proj-6rlSPS-zN1v...
```bash
r2pm -i rlang-python
make user-install
```
Or edit the api keys file `~/.config/r2ai/apikeys.txt` run:
After this you should get the `r2ai` command inside the radare2 shell. Set the `R2_DEBUG=1` environment to see the reasons why the plugin is not loaded if it's not there.
```console
$ r2ai -K
## Windows
On Windows you may follow the same instructions, just ensure you have the right python environment ready and create the venv to use
```cmd
git clone https://github.com/radareorg/r2ai
cd r2ai
set PATH=C:\Users\YOURUSERNAME\Local\Programs\Python\Python39\;%PATH%
python3 -m pip -r requirements.txt
python3 main.py
```
## Saving settings
## Usage
You may customize and save your configuration settings using your OS's default settings file (e.g `~/.radare2rc` on Linux).
For example, the following configuration sets Claude 3.7 by default, with max output tokens to 64000.
There are 4 different ways to run `r2ai`:
* Standalone and interactive: `r2pm -r r2ai` or `python main.py`
* Batch mode: `r2ai '-r act as a calculator' '3+3=?'`
* As an r2 plugin: `r2 -i main.py /bin/ls`
* From radare2 (requires `r2pm -ci rlang-python`): `r2 -c 'r2ai -h'`
* Using r2pipe: `#!pipe python main.py`
* Define a macro command: `'$r2ai=#!pipe python main.py`
```console
$ r2ai -E
## Auto mode
When using OpenAI, Claude or any of the Functionary local models you can use the auto mode which permits the language model to execute r2 commands, analyze the output in loop and in a loop until it is resolved. Here's a sample session to achieve that:
* Video https://infosec.exchange/@radareorg/111946255058894583
```bash
$ . env/bin/activate
(env)$ r2 /bin/ls
[0x00000000]> '$r2ai=#!pipe python main.py
[0x00000000]> $r2ai '-m openai:gpt-4'
[0x00000000]> $r2ai "' list the imports for this program"
[0x00000000]> $r2ai "' draw me a donut"
[0x00000000]> $r2ai "' decompile current function and explain it"
```
then you can type the commands you want to run when the r2ai plugin is loaded:
## Examples
```console
r2ai -e api=anthropic
r2ai -e model=claude-sonnet-5-5
r2ai -e max_tokens=64000
```
You can interact with r2ai from standalone python, from r2pipe via r2 keeping a global state or using the javascript interpreter embedded inside `radare2`.
## Further Reading
* [conversation.r2.js](examples/conversation.r2.js) - load two models and make them talk to each other
* There's [a chapter](https://book.rada.re/plugins/r2ai.html) in the official r2book
* Cryptax on [lmstudio+gptoss](https://cryptax.medium.com/r2ai-with-lmstudio-and-gpt-oss-08efa5ea2476) blog post
* Malware analysis [with r2ai](https://arxiv.org/pdf/2504.07574) by Cryptax and Daniel Nakov
* Analysis of [Linux/Trigona ransomware](https://cryptax.medium.com/linux-trigona-analysis-with-r2ai-3e2bd1815e52), [Linux/Prometei botnet](https://cryptax.medium.com/reversing-a-prometei-botnet-binary-with-r2-and-ai-part-one-3cdb3dc6ffab) and [W32/SkyAI](https://cryptax.medium.com/w32-skyai-uses-ai-so-do-i-d33f04d63534with) with r2ai
### Development/Testing
## Videos
Just run `make` .. or well `python3 main.py`
- [Solving a crackme](https://infosec.exchange/@radareorg/111946255058894583)
- [De-obfuscation of malware Linux/Ladvix](https://asciinema.org/a/724126)
- [Analysis of the /fast option inside Linux/Trigona ransomware](https://asciinema.org/a/pBPEaJhp6cunWSKFpBUDTgPt4)
### TODO
* add "undo" command to drop the last message
* dump / restore conversational states (see -L command)
* Implement `~`, `|` and `>` and other r2shell features
-1
View File
@@ -1 +0,0 @@
1.5.4
-6
View File
@@ -1,6 +0,0 @@
#!/bin/sh
vim decai/src/constants.ts
make -C decai/src
vim src/r2ai.h
vim src/meson.build
vim VERSION
-13
View File
@@ -1,13 +0,0 @@
# Decompiler based AI
## Actions
* Build: `make -C src`
* Install: `make user-install`
* Test: `r2 -A -qc "decai -e api=ollamacloud;decai -e model=glm-5;decai -d @ 0x100000924" /bin/ls`
## Hints
* Never read the minified, autogenerated `decai.r2.js`
* Focus and work only on the TypeScript source
* Never run `tsc` or `npx`. Always use `make` targets to compile
+3 -11
View File
@@ -1,19 +1,11 @@
PD=$(shell r2 -H R2_USER_PLUGINS)
SPD=$(shell r2 -H R2_LIBR_PLUGINS)
BF=/bin/dd
all:
@echo "Run: 'make user-install' to install it in your home"
@echo "Run: 'make -C src' to recompile the r2.js"
test:
r2 -i decai.r2.js -c 'decai -e deterministic=true' -caf $(BF)
fmt indent:
$(MAKE) -C src fmt
@echo 'You can now use 'decai' command'
r2 -i decai.r2.js /usr/bin/sed
user-install:
mkdir -p "$(PD)"
cp decai.r2.js $(PD)/decai.r2.js
user-uninstall:
@@ -26,4 +18,4 @@ uninstall:
make user-uninstall PD=$(SPD)
vs open:
open -a "Visual Studio Code" src
open -a "Visual Studio Code" .
-238
View File
@@ -1,238 +0,0 @@
# Decai
The AI based Decompiler plugin for radare2
- Written in plain Javascript
- No dependencies than radare2 and curl in PATH
- Uses local ollama by default, but also r2ai-server
- Use services like Anthropic, OpenAI, HF, XAI, DeepSeek, ..
Features
- Auto mode with function calling with ANY model
- Uses the r2 pseudo decompiler by default, supports any other
- Explain purpose and auto-document functions
- Recursive decompilation to inline stubs
- Autoname functions and perform type propagation
- Find vulnerabilities, guide you and write exploits
- Choose any natural language (not just English)
- Choose output programming language (not just C)
- Chain queries to manually fine tune the results
- Customize decompilation prompt at any time
## Installation
Using r2pm: `r2pm -ci decai`
From source: `make user-install`
## Setup
```console
[0x00000000]> decai
Usage: decai (-h) ...
decai -H - help setting up r2ai
decai -d [f1 ..] - decompile given functions
decai -dr - decompile function and its called ones (recursive)
decai -dd [..] - same as above, but ignoring cache
decai -dD [query]- decompile current function with given extra query
decai -e - display and change eval config vars
decai -E - edit decai.txt
decai -h - show this help
decai -i [f] [q] - include given file and query
decai -n - suggest better function name
decai -q [text] - query language model with given text
decai -Q [text] - query on top of the last output
decai -r - change role prompt (same as: decai -e prompt)
decai -R - reset role prompt to default prompt
decai -s - function signature
decai -v - show local variables
decai -V - find vulnerabilities
decai -x - eXplain current function
```
Configuration options:
```console
[0x00000000]> decai -e
decai -e api=ollama
decai -e apitype=chat
decai -e host=http://localhost
decai -e port=11434
decai -e prompt=Rewrite this function and respond ONLY with code, NO explanations, NO markdown, Change 'goto' into if/else/for/while, Simplify as much as possible, use better variable names, take function arguments and strings from comments like 'string:'
decai -e ctxfile=
decai -e headers=
decai -e cmds=pdc
decai -e cache=false
decai -e lang=C
decai -e hlang=English
decai -e debug=false
decai -e model=
decai -e maxinputtokens=-1
```
## Running decai
Decai is used from `r2` (e.g `r2 ./mybinary`). Get help with `decai -h`:
```
[0x00406cac]> decai -h
Usage: decai (-h) ...
decai -H - help setting up r2ai
decai -a [query] - solve query with auto mode
decai -d [f1 ..] - decompile given functions
decai -dr - decompile function and its called ones (recursive)
decai -dd [..] - same as above, but ignoring cache
decai -dD [query]- decompile current function with given extra query
...
```
List configuration variables with `decai -e`:
```
[0x00406cac]> decai -e
decai -e api=ollama
decai -e host=http://localhost
decai -e port=11434
decai -e prompt=Rewrite this function and respond ONLY with code, NO explanations, NO markdown, Change 'goto' into if/else/for/while, Simplify as much as possible, use better variable names, take function arguments and strings from comments like 'string:'
decai -e ctxfile=
...
```
Persist user defaults in `~/.config/r2ai/decai.txt` with one `key=value` entry
per line, then edit it with `decai -E`. That file is loaded on the first
`decai` command in a session and reloaded after `decai -E` exits.
Use `decai -e http=curl` to force system curl, `http=r2` to require `r2.http()`,
or `http=auto` (the default) to use `r2.http()` when available and curl otherwise.
Failed `r2.http()` calls return an error without falling back to curl. The
`timeout` setting applies only to system curl. Save `http=curl` in
`~/.config/r2ai/decai.txt` to persist the override.
List possible APIs to discuss with AI: `decai -e api=?`:
```
[0x00406cac]> decai -e api=?
r2ai
claude
openapi
...
```
Extra headers can be injected globally with `decai -e headers=...` or from the
environment with `DECAI_HEADERS` / `R2AI_HEADERS`. Use one header per line, for
example:
```console
[0x00406cac]> decai -e headers=Authorization: Bearer sk-123\nUser-Agent: curl/8.7.1
```
### Example using a local model and ollama
For example, if Ollama serves model codegeex4:latest (`ollama ls`), set decai
API as `ollama` and model `codegeex4:latest`.
```
[0x00002d30]> decai -e api=ollama
[0x00002d30]> decai -e model=codegeex4:latest
[0x00002d30]> decai -q Explain what forkpty does in 2 lines
The `forkpty` function creates a new process with a pseudo-terminal, allowing the parent to interact with the child via standard input/output/err and controlling its terminal.
```
## Examples
See
[https://github.com/radareorg/r2ai-examples](https://github.com/radareorg/r2ai-examples)
```c
$ cat stack-overflow/bug.c
#include <string.h>
int main(int argc, char **argv) {
char buf[32];
strcpy (buf, argv[1]);
return 0;
}
```
```c
$ r2 buffer-overflow/a.out
[0x100003f58]> decai -d
int main(int argc, char **argv, char **envp) {
char buffer[32];
int result = 0;
if (argc > 1 && argv[1] != NULL) {
strcpy(buffer, argv[1]);
}
return result;
}
```
### Example using a local Mistral model and r2ai-server
For example, assuming we have a _local_ Mistral AI server running on port 8080
with `r2ai-server`, we can decompile a given function with `decai -d`. The
server shows it received the question:
```
GET
CUSTOM
RUNLINE: -R
127.0.0.1 - - [13/Dec/2024 10:40:49] "GET /cmd/-R HTTP/1.1" 200 -
GET
CUSTOM
RUNLINE: -i /tmp/.pdc.txt Rewrite this function and respond ONLY with code, NO explanations, NO markdown, Change goto into if/else/for/while, Simplify as much as possible, use better variable names, take function arguments and and strings from comments like string:. Transform this pseudocode into C
```
### Example using a Mistral API key
Put the API key in `~/.r2ai.mistral-key`.
````
[0x000010d0]> decai -e api=mistral
[0x000010d0]> decai -d main
```c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv, char **envp) {
char password[40];
char input[40];
...
````
### Example with ChatGPT 4
```
[0x00406cac]> decai -e api=openai
[0x00406cac]> decai -d
#include <stdio.h>
#include <unistd.h>
void daemonize() {
daemon(1, 0);
}
...
```
## WASM HTTP
With radare2's WASI HTTP build, decai defaults to `r2.http()` to reach the existing
`r_socket_http_get/post` C APIs. Headers and JSON payloads go directly to the
host HTTP adapter; no shell curl, temporary payload file or WebSocket proxy is
needed. A failed/denied HTTP call is returned as an error, without a shell
fallback. In `http=auto` mode, runtimes without `r2.http()` use system curl.
Enable HTTP in the embedding application (`enabled: true` in JavaScript or
`network=True` in Python). The radare2 sandbox must permit both `socket` and
`network`; process execution is not needed. Browser requests remain subject
to CORS. The host adapter controls the HTTP deadline in WASM; decai's `timeout`
setting still controls native curl only. Responses are buffered, not streamed.
This requires radareorg/radare2#27037 and the rebuilt decai script. For the
C r2ai plugin compiled into WASM, the `auto` HTTP backend likewise selects
radare2's C HTTP implementation; native backend selection is unchanged.
+380 -160
View File
@@ -1,171 +1,391 @@
📦
27680 /main.js
✄
var te="1.5.4",b="decai",w="~/.config/r2ai",N=w+"/apikeys.txt",U=w+"/decai.txt",T="Rewrite this pseudocode into concise and clean code. Output only the provided function. Do not add wrappers, helper examples, test code, or main-like functions. Replace goto with structured control flow, simplify as much as possible, infer types and use better names for variables and parameters, some strings may be appearing as comments, preserve only what is implied by the input, and remove dead code.",L={decai:`# Using Decai
(function () {
const decaiHelp = `
To use decai with commercial APIs run the following commands inside r2:
Decai is the radare2 plugin for decompiling functions with the help of language models.
* decai -e api=claude # or api=openai
* Write the keys in ~/.r2ai.openai-key or ~/.r2ai.anthropic-key
By default uses a local ollama server, but can you can pick any other service by using 'decai -e api=?'.
You need r2ai webserver to be running, to do this run 'r2ai -w' in a separate terminal.
[0x00000000]> decai -e api=?
r2ai deepseek anthropic claude gemini groq hf lmstudio mistral ollama ollamacloud openapi openai openrouter vllm xai zen
$ r2pm -ci r2ai
## Using Ollama
The best model for decompiling is ClaudeAI from Anthropic:
* Visit https://ollama.com to install it.
* Download the model of choice: 'ollama run llama3.3'
* Configure decai to use the given model with: 'decai -e model=?'
$ r2pm -r r2ai
$ echo $CLAUDEAPIKEY > ~/.r2ai.anthropic
[r2ai:0x0000000]> -m anthropic:claude-3-5-sonnet-20240620
[r2ai:0x0000000]> -w
Webserver listening at port 8080
These are the most recommended models for decompiling in local:
If you want to run r2ai in local you should use granite, mistral, llama3, gemma or mistral
PD: Granite woeks quite well
* hhao/qwen2.5-coder-tools:latest (18GB of ram)
* hhao/qwen2.5-coder-tools:32b (24GB of ram required)
[r2ai:0x0000000]> -m ibm-granite/granite-20b-code-instruct-8k-GGUF
[r2ai:0x0000000]> -m QuantFactory/granite-8b-code-instruct-4k-GGUF
[r2ai:0x0000000]> -m TheBloke/Mistral-7B-Instruct-v0.2-GGUF
[r2ai:0x0000000]> -w
## Common Options
* 'decai -e baseurl=<url>' override default host and port for API endpoint (e.g., 'http://localhost:11434')
* 'decai -e apitype=chat|generate' choose the Ollama API endpoint (default: chat)
* 'decai -e headers=Authorization: Bearer ...\\nUser-Agent: curl/8.7.1' add or override HTTP headers
* "export DECAI_HEADERS='Authorization: Bearer ...\\nUser-Agent: curl/8.7.1'" set extra headers from the environment
* 'decai -e http=auto|r2|curl' select the HTTP transport (default: auto, prefers r2.http)
* 'decai -e timeout=0' disable curl timeouts, or set a positive number of seconds
You can also make r2ai -w talk to an 'r2ai-server' using this line:
* 'decai -e deterministic=true' to remove randomness from decompilation responses
* 'decai -e lang=Python' to output the decompilation in Python instead of C
* 'decai -e hlang=Catalan' to add comments or explanations in that language (instead of English)
* 'decai -e cmds=pdd,pdg' use r2dec and r2ghidra instead of r2's pdc as input for decompiling
* 'decai -e prompt=..' default prompt must be fine for most models and binaries, feel free to tweak it
[r2ai:0x0000000]> -m openapi:http://localhost:8080
[r2ai:0x0000000]> -e http.port=8082
## API Keys
[0x0000000]> decai -e host=http://localhost:8082
`;
const command = "decai";
let decaiHost = "http://localhost";
let decaiPort = "8080";
let decaiApi = "r2"; // uses /cmd endpoint
let decaiCommands = "pdc";
let decaiLanguage = "C";
let decaiDebug = false;
let decaiContextFile = "";
let lastOutput = "";
let decaiCache = false; // not implemented yet
let decprompt = "Only show the code with no explanation or introductions. Simplify the code: - take function arguments from comment - remove dead assignments - refactor goto with for/if/while - use better names for variables - simplify as much as possible";
// decprompt += ", comments in function calls may replace arguments and remove unnecessary early variable assignments that happen"
Remove services like OpenAI, Mistral, Anthropic, Grok, Gemini, .. require API keys to work.
See 'decai -k' to list the status of available APIkeys
Use 'decai -K' to edit apikeys.txt and 'decai -E' to edit decai.txt.
Decai will pick them from the environment or the config files in your home:
* echo KEY > ~/.r2ai.openai-key
* export OPENAI_API_KEY=...
* put one 'key=value' pair per line in ~/.config/r2ai/decai.txt
## Using the R2AI Server:
Install r2ai or r2ai-server with r2pm:
[0x0000000]> decai -e api=r2ai
[0x0000000]> r2pm -ci r2ai
Choose one of the recommended models (after r2pm -r r2ai):
* -m ibm-granite/granite-20b-code-instruct-8k-GGUF
* -m QuantFactory/granite-8b-code-instruct-4k-GGUF
* -m TheBloke/Mistral-7B-Instruct-v0.2-GGUF
Start the webserver:
$ r2pm -r r2ai-server -l r2ai -m granite-8...
`,auto:`# Radare2 Auto Mode
Use function calling to execute radare2 commands in order to resolve the user request defined in the "User Prompt" section, analyze the responses attached in the "Command Results" section.
## Function Calling
Respond ONLY using plain JSON. Process user query and decide which function calls are necessary to solve the task.
1. Analyze the user request to determine if we need to run commands to extend the knowledge and context of the problem.
2. If function call is needed, construct the JSON like this:
- Fill the "action" key with the "r2cmd" value.
- Specify the "command" as a string.
- Optionally, provide a "reason" and "description"
3. If the answer can be provided and no more function calls are required:
- Use the key "action": "reply".
- Include "response" with the direct answer to the user query.
Return the result as a JSON object.
### Sample Function Calling Communication
Command Results: already performed actions with their responses
User Prompt: "Count how many functions we have here."
Response:
{
"action": "r2cmd",
"command": "aflc",
"description": "Count functions"
"reason": "Evaluate if the program is analyzed before running aaa"
}
## Rules
Use radare2 to resolve user requests.
* Explain each step in the "reason" field of the JSON.
* Follow the initial analysis instructions.
* Output only valid JSON as specified.
* Decompile and inspect functions, starting from main.
* Run only the needed commands to gather info.
* Use "@ (address|symbol)" to seek temporarily.
* Output should be a verbose markdown report.
* Use "sym." or "fcn." prefixes if "pdc" is empty.
* If a seek fails, use "f~name" to find the symbol's address.
### Initial Analysis
1. Run "aflc" to count the number of functions
2. If the output of "aflc" is "0" run "aaa" once, then "aflc" again
3. Run only one command at a time (do not use ";")
### Special cases
* On Swift binaries run...
`};var E={decopipe:{use:!1},host:"http://localhost",port:"11434",baseurl:"",extraHeaders:[],api:"ollama",pipeline:"",commands:"pdc",yolo:!1,tts:!1,language:"C",humanLanguage:"English",deterministic:!0,debug:!1,timeout:180,http:"auto",think:"",apitype:"chat",useFiles:!1,contextFile:"",model:"",cache:!1,maxInputTokens:-1,prompt:T,lastOutput:""},r={...E,decopipe:{...E.decopipe},extraHeaders:[...E.extraHeaders]};function re(e){return(r2.cmd("-e dir.tmp").trim()??".")+"/"+e}function ne(e){if(!e.startsWith("~/"))return e;let t=r2.cmd("'%HOME").trim()||r2.cmd("'%USERPROFILE").trim();return t?t+e.slice(1):e}function H(e,t){r2.cmd("'mkdir -p "+ne(e)),r2.cmd("'touch "+ne(t))}function _(e){return(r2.cmd2("test -h").logs?.[0]?.message??"").includes("-fdx")?!0:r2.cmd("'test -vf "+e).startsWith("found")}function G(e,t){return e+" ".repeat(Math.max(0,t-e.length))}function h(e,t){return e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,"")}function oe(e){return e.replace(/\x1b\[[0-9;]*m/g,"")}function ie(e){let t=e.match(/```json\s*([\s\S]*?)```/);return t&&t[1]?t[1].trim():e}function ae(e){let t=e,n=t.indexOf("{");n!==-1&&(t=t.slice(n));let o=t.lastIndexOf("}");return o!==-1&&(t=t.slice(0,o+1)),t}function $(e){return btoa(e)}function se(e,t){let n=$(t);r2.cmd("p6ds "+n+" > "+e)}function k(e){let t=e;return t=t.replace(/<think>[\s\S]*?<\/think>/gi,""),t.split(`
`).filter(n=>!n.trim().startsWith("```")).join(`
`)}function J(e){r.debug&&console.log(e)}function le(e){let t={};for(let n of e.split(/\r?\n/)){let o=n.trim();if(!o||o.startsWith("#"))continue;let[i,...a]=o.split("=");if(!i||a.length===0)continue;let s=a.join("=").trim(),l=i.toLowerCase().replace(/_api_key$/i,"");t[l]=s}return t}var Ue=["DECAI_HEADERS","R2AI_HEADERS"];function _e(e){let t=r2.cmd("'%"+e).trim();return t!==""&&!t.includes("=")?t:""}function Me(e){return e.trim().toLowerCase()}function Ne(e){let t=e.indexOf(":"),n=e.indexOf("="),o=t!==-1&&(n===-1||t<n)?t:n;if(o===-1)return null;let i=e.slice(0,o).trim();return i===""?null:{name:i,value:e.slice(o+1).trim()}}function Te(e){return e.value===""?`${e.name}:`:`${e.name}: ${e.value}`}function ce(e,t){for(let n of e){let o=Ne(n.trim());o&&t.set(Me(o.name),Te(o))}}function W(e){let t=new Map,n=e.replace(/\\n/g,`
`).split(/\r?\n/g).filter(o=>{let i=o.trim();return i&&!i.startsWith("#")});return ce(n,t),Array.from(t.values())}function ue(e){return e.join("\\n")}function A(...e){let t=new Map;for(let n of e)ce(n,t);return Array.from(t.values())}function He(){for(let e of Ue){let t=_e(e);if(t)return W(t)}return[]}function de(){return A(He(),r.extraHeaders)}function M(e){return`'${e.replace(/'/g,`'"'"'`)}'`}function Ke(e){let{method:t,url:n,headers:o,payload:i}=e,a=A(["Content-Type: application/json"],o);if(r.http!=="curl"){if(r2.http)return r2.http(t,n,a.join(`
`),i??"").body;if(r.http==="r2")throw new Error("r2.http is not available in this runtime")}let s=["curl","-s"];if(r.timeout>0&&s.push("--max-time",String(r.timeout)),a.forEach(l=>s.push("-H",M(l))),t==="POST"){if(!i)throw new Error("Payload required for POST requests");let l=r2.fdump(i);s.push("--data-binary","@"+M(l),M(n));let u=s.join(" ")+"; rm "+M(l);return J(u),r2.syscmds(u)}else{s.push(M(n));let l=s.join(" ");return J(l),r2.syscmds(l)}}function qe(e){let t=e.split(`
`).filter(i=>i.trim()!=="");if(t.length<=1)return null;let n="",o=null;for(let i of t)try{let a=JSON.parse(i);if(a.error)return a;a.message?.content!==void 0&&(n+=a.message.content),o=a}catch{return null}return o?(o.message={role:"assistant",content:n},o):null}function pe(e){try{let t=Ke(e).trim();if(t==="")return{error:"empty response"};try{return JSON.parse(t)}catch{let n=qe(t);return n||{error:t,rawOutput:t}}}catch(t){return{error:t.message}}}function O(e,t){return pe({method:"GET",url:e,headers:t})}function fe(e,t,n){return pe({method:"POST",url:e,headers:t,payload:n})}function z(e){return e.split("_")[0].toLowerCase()}function me(e){if(!e.authKey)return;let t=Array.from(new Set([e.keyName,z(e.authKey)].filter(Boolean))),n;for(let o of t){let i=K(o,e.authKey);if(i[0])return i;!n&&i[2]!=="nope"&&(n=i)}return n||K(z(e.authKey),e.authKey)}function S(e){if(e)return typeof e=="string"?e:e.message||JSON.stringify(e)}function he(e){return r.baseurl||e.defaultBaseUrl}function ge(e,t=de()){return A(e,t)}function De(e){return e?["Authorization: Bearer "+e]:[]}function je(e){let t=["anthropic-version: 2023-06-01"];return e?A(t,["x-api-key: "+e]):t}function ye(e,t){switch(e.authStyle||"none"){case"bearer":return De(t);case"anthropic":return je(t);case"none":default:return[]}}function Be(e,t,n){let o=O(e,t),i=S(o.error);return i?(console.error(i),"error invalid response"):o.data?.map(n).join(`
`)||""}function Fe(e,t){let n=new Set;return e.filter(o=>{let i=t(o);return n.has(i)?!1:(n.add(i),!0)})}function V(e){let t=me(e)?.[0]||null;return{baseUrl:he(e),headers:ge(ye(e,t))}}function Le(e){let{baseUrl:t,headers:n}=V(e),o=h(t,"v1/models");return Be(o,n,i=>i.id)}function Ge(e){let{baseUrl:t,headers:n}=V(e),o=O(h(t,"api/tags"),n),i=S(o.error);return i?(console.error(i),"error invalid response"):o.models?.map(a=>a.name).join(`
`)||""}function $e(e){let{baseUrl:t,headers:n}=V(e),o=O(h(t,"v1/models"),n);return o.data?Fe(o.data,i=>i.name||i.id).map(i=>[G(i.name||i.id,30),G(""+(i.max_context_length||""),10),i.description||""].join(" ")).join(`
`):""}var Je={openai:{buildPayload:(e,t)=>({stream:!1,model:e,messages:[{role:"user",content:t}]}),parseResponse:e=>{if(e.error)throw new Error(S(e.error)||"Unknown error");if(e.choices&&e.choices[0]?.message?.content)return k(e.choices[0].message.content);throw new Error("Invalid response format")},buildUrl:e=>h(e,"v1/chat/completions")},anthropic:{buildPayload:(e,t)=>{let n={model:e,max_tokens:5128,messages:[{role:"user",content:t}]};return Y()&&(n.thinking={type:"enabled",budget_tokens:4096},n.max_tokens=16e3),r.deterministic&&!Y()&&Object.assign(n,{temperature:0,top_p:0,top_k:1}),n},parseResponse:e=>{if(e.content&&Array.isArray(e.content)){let t=[];for(let n of e.content)n.text&&t.push(n.text);if(t.length>0)return k(t.join(`
`))}throw e.error?new Error(S(e.error)||"Unknown error"):new Error("Invalid response format")},buildUrl:e=>h(e,"v1/messages")},ollama:{buildPayload:(e,t)=>{let n={stream:!1,model:e};return r.apitype==="generate"?n.prompt=t:n.messages=[{role:"user",content:t}],Y()?r.think==="true"||r.think==="1"?n.think=!0:n.think=r.think:n.think=!1,r.deterministic&&(n.options={repeat_last_n:0,top_p:1,top_k:1,temperature:0,repeat_penalty:1,seed:123}),n},parseResponse:e=>{if(e.error)throw new Error(S(e.error));if(typeof e.response=="string")return k(e.response);if(e.message?.content)return k(e.message.content);throw new Error(JSON.stringify(e))},buildUrl:e=>h(e,"api/"+r.apitype)},gemini:{buildPayload:(e,t)=>{let n={contents:[{parts:[{text:t}]}]},o={};return r.deterministic&&Object.assign(o,{temperature:0,topP:1,topK:1}),r.think!==""&&(X()?o.thinkingConfig={thinkingBudget:0}:o.thinkingConfig={thinkingBudget:8192}),Object.keys(o).length>0&&(n.generationConfig=o),n},parseResponse:e=>{if(e.candidates&&e.candidates[0]?.content?.parts){let n=e.candidates[0].content.parts.filter(o=>!o.thought&&o.text).map(o=>o.text);if(n.length>0)return k(n.join(`
`))}throw e.error?new Error(S(e.error)):(console.log(JSON.stringify(e)),new Error("Invalid response format"))},buildUrl:(e,t,n)=>h(e,`v1beta/models/${t}:generateContent?key=${n}`),requiresUrlApiKey:!0}},q={anthropic:{defaultModel:"claude-sonnet-5-5",defaultBaseUrl:"https://api.anthropic.com",authKey:"ANTHROPIC_API_KEY",keyName:"anthropic",authStyle:"anthropic",apiStyle:"anthropic"},claude:{defaultModel:"claude-sonnet-5-5",defaultBaseUrl:"https://api.anthropic.com",authKey:"ANTHROPIC_API_KEY",keyName:"claude",authStyle:"anthropic",apiStyle:"anthropic"},openai:{defaultModel:"gpt-4o-mini",defaultBaseUrl:"https://api.openai.com",authKey:"OPENAI_API_KEY",authStyle:"bearer",apiStyle:"openai"},ollama:{defaultModel:"qwen2.5-coder:latest",defaultBaseUrl:"http://localhost:11434",authStyle:"none",apiStyle:"ollama"},ollamacloud:{defaultModel:"gpt-oss:120b",defaultBaseUrl:"https://ollama.com",authKey:"OLLAMA_API_KEY",keyName:"ollamacloud",authStyle:"bearer",apiStyle:"ollama"},zen:{defaultModel:"big-pickle",defaultBaseUrl:"https://opencode.ai/zen",authKey:"OPENCODE_API_KEY",keyName:"zen",authStyle:"bearer",apiStyle:"openai",hardcodedModels:["big-pickle","glm-5","kimi-k2.5"]},gemini:{defaultModel:"gemini-2.5-flash",defaultBaseUrl:"https://generativelanguage.googleapis.com",authKey:"GEMINI_API_KEY",authStyle:"none",apiStyle:"gemini",hardcodedModels:["gemini-2.0-flash","gemini-2.0-flash-lite","gemini-2.5-pro","gemini-2.5-flash","gemini-2.5-flash-lite"]},mistral:{defaultModel:"codestral-latest",defaultBaseUrl:"https://api.mistral.ai",authKey:"MISTRAL_API_KEY",authStyle:"bearer",apiStyle:"openai"},xai:{defaultModel:"grok-beta",defaultBaseUrl:"https://api.x.ai",authKey:"XAI_API_KEY",authStyle:"bearer",apiStyle:"openai",hardcodedModels:["grok-2","grok-beta"]},lmstudio:{defaultModel:"local-model",defaultBaseUrl:"http://127.0.0.1:1234",authStyle:"none",apiStyle:"openai"},deepseek:{defaultModel:"deepseek-coder",defaultBaseUrl:"https://api.deepseek.com",authKey:"DEEPSEEK_API_KEY",authStyle:"bearer",apiStyle:"openai",hardcodedModels:["deepseek-coder","deepseek-chat"]},openrouter:{defaultModel:"openai/gpt-4o-mini",defaultBaseUrl:"https://openrouter.ai/api",authKey:"OPENROUTER_API_KEY",authStyle:"bearer",apiStyle:"openai"},groq:{defaultModel:"llama-3.3-70b-versatile",defaultBaseUrl:"https://api.groq.com/openai",authKey:"GROQ_API_KEY",authStyle:"bearer",apiStyle:"openai"}};function D(e){return q[e]}function Q(){return Object.keys(q)}function be(e){let t=D(e);if(!t)throw new Error(`Unknown provider: ${e}`);if(e==="mistral")return $e(t);switch(t.apiStyle){case"openai":case"anthropic":return Le(t);case"ollama":return Ge(t);case"gemini":return"";default:return""}}function X(){return r.think==="false"||r.think==="0"}function Y(){return r.think!==""&&!X()}function We(e,t,n){let o=e;return r.think!==""&&n==="openai"&&(X()?(o+=' Answers directly and concisely, without showing any thinking steps or internal reasoning. Never include phrases like "Let me think".',o+=" /no_think"):o="Think step by step and explain the reasoning process, When answering, first output your reasoning inside <think> and </think> tags, then give the final answer."+o),t?o:r.prompt+Ye()+o}function Ye(){return`
.Translate the code into `+r.language+` programming language
`}function ze(e,t,n,o){let i=r.model||e.defaultModel,a=We(n,o,e.apiStyle),s=me(e);if(t.requiresUrlApiKey&&s?.[1]&&e.authKey)return`Cannot read ~/.r2ai.${z(e.authKey)}-key`;let l=t.buildPayload(i,a),u=ye(e,s?.[0]||null);e.apiStyle==="ollama"&&u.push("Accept: application/x-ndjson");let v=ge(u),c=t.buildUrl(he(e),i,s?.[0]||void 0);try{return t.parseResponse(fe(c,v,JSON.stringify(l)))}catch(d){return"ERROR: "+d.message}}function ke(e,t){let n=D(r.api);return n?ze(n,Je[n.apiStyle],e,t):`Unknown value for 'decai -e api'. Available: ${Q().join(", ")}`}function Ve(){return Object.fromEntries(Object.entries(q).filter(([,e])=>e.authKey).map(([e,t])=>[e,t.authKey]))}function K(e,t){let n=r2.cmd("'%"+t).trim();if(!n.includes("=")&&n!=="")return[n.trim(),null,"env"];let o=e.toLowerCase(),i=N;if(_(i)){let s=r2.cmd("'cat "+i),l=le(s);if(o in l)return[l[o],null,"txt"]}let a="~/.r2ai."+o+"-key";if(_(a)){let s=r2.cmd("'cat "+a);return s===""?[null,"Cannot read "+a,"no"]:[s.trim(),null,"file"]}return[null,"Not available","nope"]}function xe(){H(w,N),r2.cmd("'ed "+N)}function Z(){Object.entries(Ve()).forEach(([e,t])=>{let n=K(e,t)[2];console.log(n," ",e)})}function ve(e){let t=D(e);if(!t){console.error(`Unknown provider: ${e}`);return}try{let n=be(e);n&&console.log(n),t.hardcodedModels&&t.hardcodedModels.forEach(o=>console.log(o)),e==="mistral"&&console.log("ministral-8b-latest"),!n&&!t.hardcodedModels&&console.log(t.defaultModel)}catch(n){let o=n;console.error(`Error listing models for ${e}:`,o.message),console.log(t.defaultModel)}}var ee=!1;function Qe(e){return e==="true"||e==="1"}function P(e){let t=r;return{get:()=>t[e],set:n=>{t[e]=Qe(n)}}}function R(e){let t=r;return{get:()=>t[e],set:n=>{t[e]=n}}}function Xe(e){let t=e.trim();return t==="chat"?"chat":t==="generate"?"generate":null}var Ee={pipeline:{get:()=>r.pipeline,set:e=>{r.pipeline=e;try{r.decopipe=JSON.parse(r2.cmd("cat "+e))}catch(t){console.error(t)}}},model:{get:()=>r.model,set:e=>{e==="?"?ve(r.api):r.model=e.trim()}},deterministic:P("deterministic"),files:P("useFiles"),think:{get:()=>r.think||"false",set:e=>{r.think=e}},apitype:{get:()=>r.apitype,set:e=>{let t=Xe(e);t?r.apitype=t:console.error("Invalid apitype. Use chat or generate.")}},debug:P("debug"),http:{get:()=>r.http,set:e=>{let t=e.trim();t==="auto"||t==="r2"||t==="curl"?r.http=t:console.error("Invalid http backend. Use auto, r2 or curl.")}},timeout:{get:()=>r.timeout,set:e=>{r.timeout=Math.max(0,parseInt(e,10)||0)}},api:{get:()=>r.api,set:e=>{e==="?"?console.error(Q().join(`
`)):r.api=e}},lang:R("language"),hlang:R("humanLanguage"),cache:P("cache"),cmds:R("commands"),tts:P("tts"),yolo:P("yolo"),prompt:R("prompt"),ctxfile:R("contextFile"),baseurl:R("baseurl"),headers:{get:()=>ue(r.extraHeaders),set:e=>{r.extraHeaders=W(e)}},maxtokens:{get:()=>r.maxInputTokens,set:e=>{r.maxInputTokens=parseInt(e,10)||-1}}};function Ze(){let e={host:r.host,port:r.port,lastOutput:r.lastOutput};Object.assign(r,E,e),r.decopipe={...E.decopipe},r.extraHeaders=[...E.extraHeaders]}function et(e){let t=e.trim();return!t||t.startsWith("#")?null:t.startsWith("decai -e ")?t.slice(9).trim():t}function x(e){let t=e.indexOf("="),n=t===-1?e:e.slice(0,t),o=t===-1?void 0:e.slice(t+1),i=Ee[n];if(!i){console.error("Unknown config key");return}typeof o<"u"?i.set(o):console.log(i.get())}function we(){Object.entries(Ee).forEach(([e,t])=>{let n=t.get();console.log("decai -e "+e+"="+n)})}function Ae(){if(Ze(),!_(U))return;let e=r2.call("cat "+U);for(let t of e.split(/\r?\n/)){let n=et(t);n&&n[0]!="#"&&x(n)}}function Oe(){ee||(Ae(),ee=!0)}function Se(){H(w,U),r2.cmd("'ed "+U),Ae(),ee=!0}function p(e,t,n=!1){let o=(t||"").replace(/`/g,""),i=e.replace(/'/g,"");if(r.api==="r2"||r.api==="r2ai"){let s=re(".pdc.txt");se(s,o);let l=i.startsWith("-")?i:["-i",s,i].join(" "),u=r.baseurl||r.host+":"+r.port,v=h(u,"cmd/"+encodeURIComponent(l)),c=O(v,[]);return c.error?`Error: ${c.error}`:c.result||JSON.stringify(c)||"Cannot curl, use r2ai-server or r2ai -w"}if(i.startsWith("-"))return"";let a=i+`:
`+o;return r.maxInputTokens>0&&a.length>r.maxInputTokens&&(a=a.slice(0,r.maxInputTokens)),ke(a,n)}function Re(e){let t=r2.cmd("e scr.color");r2.cmd("e scr.color=0");try{return e()}finally{r2.cmd("e scr.color="+t)}}function Pe(){let e="",t=n=>e+=" "+b+" "+n+`
`;e+="Usage: "+b+` (-h) ...
`,e+="Version: "+te+`
`,t("-a [query] - solve query with auto mode"),t("-b [url] - set base URL (alias for decai -e baseurl)"),t("-d [f1 ..] - decompile given functions"),t("-dd [..] - same as above, but ignoring cache"),t("-dD [query] - decompile current function with given extra query"),t("-dr - decompile function and its called ones (recursive)"),t("-e - display and change eval config vars"),t("-E - edit and run decai.txt"),t("-h - show this help"),t("-H - help setting up r2ai"),t("-i [f] [q] - include given file and query"),t("-k - list API key status"),t("-K - edit apikeys.txt"),t("-m [model] - use -m? or -e model=? to list the available models"),t("-n - suggest better function name"),t("-p [provider] - same as decai -e api (will be provider)"),t("-q [text] - query language model with given text"),t("-Q [text] - query on top of the last output"),t("-r [prompt] - change role prompt (same as: decai -e prompt)"),t("-R - reset role prompt to default prompt"),t("-s - function signature"),t("-v - show local variables"),t("-V - find vulnerabilities"),t("-x[*] - eXplain current function (-x* for r2 script)"),r2.log(e.trim())}function j(e,t,n,o){if(n){let s=r2.cmd("anos").trim();if(s.length>0)return s}let i="";if(o){let s=r2.cmd("s");i+=`## Context functions:
`;let l=r2.cmdAt("axff~^C[2]~$$",s);for(let u of l.split(/\n/g))i+=r2.cmd("pdc@"+u);r2.cmd("s "+s)}let a=t?" "+e:"";try{let s=e.slice(2).trim(),l="";r.contextFile!==""&&r2.cmd2("test -f "+r.contextFile).value===0&&(l+=`## Context:
[START]
`+r2.cmd("cat "+r.contextFile)+`
[END]
`);let{body:u,count:v}=Re(()=>{let d=`## Before:
`,y=0;for(let g of r.commands.split(",")){if(g.trim()==="")continue;let C=t||s.trim().length===0?g:g+"@@= "+s,I=r2.cmd(C);I.length>5&&(d+="Output of "+g+`:
[START]
`+I+`
[END]
`,y++)}return d+=`## After:
`,{body:d,count:y}});if(v===0){console.error("Nothing to do.");return}let c="";if(r.decopipe.use){let d=r.model;try{let y=r.decopipe[r.decopipe.default],g=l+u;for(let C of y.pipeline){C.model&&(r.model=C.model);let I=C.query+". "+y.globalQuery;c=p(I,g,!0),r.debug&&console.log(`QUERY
`,I,`
INPUT
`,g,`
OUTPUT
`,c),g=c}c=g}finally{r.model=d}}else{let d=a;l+=u+i,c=p(d,l,!1),r.lastOutput=c}return n&&c.length>1&&r2.call("ano=base64:"+$(c)),c.startsWith("```")&&(c=c.replace(/```.*\n/,"").replace(/```$/,"")),c.trim()}catch(s){let l=s;console.error(l,l.stack);return}}function tt(){let e=Re(()=>"[START]"+r.commands.split(",").map(o=>r2.cmd(o)).join(`
`)+"[END]"),n=p("Analyze function calls, references, comments and strings, loops and ignore registers and memory accesses. Explain the purpose of this function in a single short sentence. /no_think Do not introduce or argue the response, translation of the explanation in "+r.humanLanguage,e,!0).trim().split(/\n/g);return n[n.length-1].trim()}function nt(){let e=r.language,t=r2.cmd("afv;pdc");r.language="C";let n="'afs "+p("analyze the uses of the arguments and return value to infer the signature, identify which is the correct type for the return. Do NOT print the function body, ONLY output the function signature, like if it was going to be used in a C header",t,!1),o=n.indexOf("{");return o!==-1&&(n=n.substring(0,o)),r.language=e,n}function rt(e,t){let n=[];for(;;){let o=L.auto;if(n.length>0){o+=`## Command Results
`;for(let a of n){let s=JSON.parse(a);o+="### "+s.command+"\n\n```\n"+s.response+"\n```\n"}}o+=`
## User Prompt
`+e,r.debug&&console.log(`#### input
`,o,`
#### /input`),console.log("Thinking...");let i=p("",o,!0);r.debug&&console.log(`#### output
`,i,`
#### /output`);try{let a=JSON.parse(ae(ie(k(i))));if(a.action==="r2cmd"||a.action==="response"||a.action===a.command){let s=a.command||"";a.reason&&(console.log("[r2cmd] Reasoning: "+a.reason),r.tts&&(r2.syscmd("pkill say"),r2.syscmd("say -v Alex -r 250 '"+a.reason.replace(/'/g,"")+"' &"))),console.log("[r2cmd] Action: "+a.description),console.log("[r2cmd] Command: "+s);let l=s;r.yolo||(l=ot(s,t)),console.log("[r2cmd] Running: "+l);let u=r2.cmd2(l),v=u.logs?u.logs.map(y=>y.type+": "+y.message).join(`
`):"",c=(u.res+v).trim();console.log(c);let d=oe(c);r.debug&&console.log(`<r2output>
`,d,`
<(r2output>`),n.push(JSON.stringify({action:"response",command:l,description:a.description,response:d}))}else if(a.action==="reply"){console.log(`Done
`,a.response);break}else{console.log(`Unknown response
`,JSON.stringify(i));break}}catch(a){let s=i.indexOf('response": "');if(s!==-1){let l=i.slice(s+12).replace(/\\n/g,`
`).replace(/\\/g,"");console.log(l)}else console.log(i),console.error(a);break}}}function ot(e,t){for(;;){let n=r2.cmd("'?ie Tweak command? ('?' for help)").trim();if(n==="q!"){console.error("Break!");break}if(n==="?"){it();continue}else if(n.startsWith(":")){console.log(r2.cmd(n.slice(1)));continue}else if(n.startsWith("-e")){t(n);continue}else{if(n==="!")return"?e do NOT execute '"+e+"' again, continue without it";if(n.startsWith("!")){console.log(r2.syscmd(n.slice(1)));continue}else{if(n==="q")return"?e All data collected!. Do not call more commands, reply the solutions";if(n){let o=n.indexOf("#");return o!==-1?n.slice(0,o).trim():n}else return e}}}return e}function it(){console.log(" '!' do not run this command"),console.log(" '!c' run system command"),console.log(" 'q' to quit auto and try to solve"),console.log(" 'q!' quit auto without solving"),console.log(" 'c # C' use given command with comment"),console.log(" ':c' run r2 command without feeding auto"),console.log(" '-e' set decai configuration variables")}function at(e){return e.slice(2).trim()}function B(){return r2.cmd("afi.").trim().length>0}var F="Cannot find function at current offset";function Ce(e,t){if(e===""||!e.startsWith("-")){Pe();return}let n="",o=e[1],i=at(e);switch(o){case"H":console.log(L.decai);break;case"a":rt(i,t);break;case"m":{i==="="?x("model="):i?x("model="+i):x("model");break}case"n":case"f":{if(!B()){n=F;break}n=r2.cmd("axff~$[3]");let a=r2.cmd("fd.").trim().split(/\n/).filter(s=>!s.startsWith("secti")).join(",");n=p("give me a better name for this function. the output must be: 'afn NEWNAME'. do not include the function code, only the afn line. consider: "+a,n,!1).trim(),n+=" @ "+r2.cmd("?v $FB").trim();break}case"v":{if(!B()){n=F;break}n=r2.cmd("afv;pdc"),n=p("guess a better name and type for each local variable and function argument taking using. output an r2 script using afvn and afvt commands",n,!1);break}case"i":{let a=i.split(/\s+/).filter(Boolean);if(a.length>=2){let s=a[0],l=a.slice(1).join(" "),u=r2.cmd("cat "+s);console.log(p(l,u,!0))}else console.log("Use: decai -i [file] [query ...]");break}case"p":{i?x("api="+i):Z();break}case"r":{i?r.prompt=i:console.log(r.prompt);break}case"R":r.prompt=T;break;case"s":if(!B()){n=F;break}n=nt();break;case"V":n=p("find vulnerabilities, dont show the code, only show the response, provide a sample exploit",r.lastOutput,!1);break;case"K":xe();break;case"E":Se();break;case"k":Z();break;case"b":{i?x("baseurl="+i):console.log(r.baseurl);break}case"e":{i?x(i):we();break}case"q":try{n=p(i,null,!0)}catch(a){let s=a;console.error(s,s.stack)}break;case"Q":n=p(i,r.lastOutput,!1);break;case"x":if(!B()){n=F;break}n=tt(),(e[2]==="*"||e[2]==="r")&&(n="'CC "+n);break;case"d":e[2]==="r"?n=j(e.slice(2),!0,r.cache,!0)||"":e[2]==="d"?n=j(e,!1,!1,!1)||"":e[2]==="D"?n=j(e,!0,!1,!1)||"":n=j(e,!1,r.cache,!1)||"";break;default:Pe();break}return n||void 0}function Ie(e){Oe();let t=Ce(e,Ie);return t&&r2.log(t),!0}function st(){r2.unload("core",b),r2.plugin("core",function(){function e(t){if(t.startsWith(b)){let n=t.slice(b.length).trim();return Ie(n)}return!1}return{name:b,license:"MIT",desc:"r2 decompiler based on r2ai",call:e}})}st();
function decaiEval(arg) {
const [k, v] = arg.split("=");
if (!v) {
switch (k) {
case "debug":
console.log(decaiDebug);
break;
case "api":
console.log(decaiApi);
break;
case "lang":
console.log(decaiLanguage);
break;
case "cache":
console.log(decaiCache);
break;
case "cmds":
console.log(decaiCommands);
break;
case "prompt":
console.log(decprompt);
break;
case "ctxfile":
console.log(decaiContextFile);
break;
case "host":
console.log(decaiHost);
break;
case "port":
console.log(decaiPort);
break;
default:
console.error("Unknown key");
break;
}
return;
}
switch (k) {
case "debug":
decaiDebug = (v === "true" || v === "1");
break;
case "api":
if (v === "?") {
console.error("r2ai\nclaude\nopenapi\nopenai");
} else {
decaiApi = v;
}
break;
case "cache":
decaiCache = v;
break;
case "ctxfile":
decaiContextFile = v;
break;
case "lang":
decaiLanguage = v;
break;
case "cmds":
decaiCommands = v;
break;
case "prompt":
decprompt = v;
break;
case "host":
decaiHost = v;
break;
case "port":
decaiPort = v;
break;
default:
console.error("Unknown key");
break;
}
}
function usage() {
console.error("Usage: " + command + " (-h) ...");
console.error(" " + command + " -H - help setting up r2ai");
console.error(" " + command + " -d - decompile current function");
console.error(" " + command + " -e - display and change eval config vars");
console.error(" " + command + " -h - show this help");
console.error(" " + command + " -n - suggest better function name");
console.error(" " + command + " -q [text] - query language model with given text");
console.error(" " + command + " -Q [text] - query on top of the last output");
console.error(" " + command + " -r - change role prompt (same as: decai -e prompt)");
console.error(" " + command + " -v - show local variables");
console.error(" " + command + " -V - find vulnerabilities");
console.error(" " + command + " -x - eXplain current function");
}
function r2aiAnthropic(msg) {
const claudeKey = r2.cmd("'cat ~/.r2ai.anthropic-key").trim()
const claudeModel = "claude-3-5-sonnet-20240620";
if (claudeKey === '') {
return "Cannot read ~/.r2ai.anthropic-key";
}
const payload = JSON.stringify({
model: claudeModel,
max_tokens: 5128,
messages: [
{
"role": "user",
"content": decprompt + ", Explain this pseudocode in " + decaiLanguage + "\n" + msg
}
]
});
const curlcmd = `curl -s https://api.anthropic.com/v1/messages
-H "Content-Type: application/json"
-H "anthropic-version: 2023-06-01"
-H "x-api-key: ${claudeKey}"
-d '${payload}'`.replace(/\n/g, "");
if (decaiDebug) {
console.log(curlcmd);
}
const res = r2.syscmds(curlcmd);
try {
return JSON.parse(res).content[0].text;
} catch(e) {
console.error("ERROR");
console.error(e);
console.log("RES((" + res + "))");
}
return "error invalid response";
}
function r2aiOpenAI(msg) {
const openaiKey = r2.cmd("'cat ~/.r2ai.openai-key").trim()
// const openaiModel = "gpt-3.5-turbo";
const openaiModel = "gpt-4";
if (openaiKey === '') {
return "Cannot read ~/.r2ai.openai-key";
}
const payload = JSON.stringify({
model: openaiModel,
max_tokens: 5128,
messages: [
{"role": "system", "content": decprompt }, {
"role": "user",
"content": decprompt + ", Explain this pseudocode in " + decaiLanguage + "\n" + msg
}
]
});
const curlcmd = `curl -s https://api.openai.com/v1/chat/completions
-H "Content-Type: application/json"
-H "Authorization: Bearer ${openaiKey}"
-d '${payload}' #`.replace(/\n/g, "");
if (decaiDebug) {
console.log(curlcmd);
}
const res = r2.syscmds(curlcmd);
try {
return JSON.parse(res).choices[0].message.content;
} catch(e) {
console.error(e);
console.log(res);
}
return "error invalid response";
}
function r2aiOpenAPI(msg) {
const payload = JSON.stringify({ "prompt": decprompt + ", Explain this pseudocode in " + decaiLanguage + "\n" + msg });
const curlcmd = `curl -s ${decaiHost}:${decaiPort}/completion
-H "Content-Type: application/json"
-d '${payload}' #`.replace(/\n/g, "");
if (decaiDebug) {
console.log(curlcmd);
}
const res = r2.syscmds(curlcmd);
try {
return JSON.parse(res).content;
} catch(e) {
console.error(e);
console.log(res);
}
return "error invalid response";
}
function fileDump(fileName, fileData) {
const d = b64(fileData);
r2.cmd("p6ds " + d + " > " + fileName);
}
function r2ai(queryText, fileData) {
if (!fileData) {
fileData = "";
}
fileData = fileData.replace(/\`/g, '').replace(/'/g, '"');
queryText = queryText.replace(/'/g, '');
if (decaiApi === "r2" || decaiApi === "r2ai") {
const fileName = "/tmp/.pdc.txt";
fileDump(fileName, fileData);
const q = queryText.startsWith("-")? queryText: ["-i", fileName, queryText].join(" ");
const host = decaiHost + ":" + decaiPort + "/cmd"; // "http://localhost:8080/cmd";
const ss = q.replace(/ /g, "%20").replace(/'/g, "\\'");
const cmd = 'curl -s "' + host + '/' + ss + '" || echo Cannot curl, use r2ai-server or r2ai -w #';
if (decaiDebug) {
console.error(cmd);
}
return r2.syscmds(cmd);
}
if (fileData === "" || queryText.startsWith("-")) { // -i
return "";
}
const q = queryText + fileData;
if (decaiApi === "anthropic" || decaiApi === "claude") {
return r2aiAnthropic(q);
}
if (decaiApi === "openapi") {
return r2aiOpenAPI(q);
}
if (decaiApi === "openai") {
return r2aiOpenAI(q);
}
return "Unknown value for 'decai -e api'. Use r2ai, claude, openapi or openai";
}
function r2aidec(args) {
if (args === "") {
usage();
} else if (args[0] === "-") {
var out = "";
switch (args[1]) {
case "H": // "-H"
console.log(decaiHelp);
break;
case "n": // "-n"
case "f": // "-f"
out = r2.cmd("pdc");
var considerations = r2.cmd("fd.").trim().split(/\n/).filter(x=>!x.startsWith("secti")).join(",");
// console.log(considerations);
r2ai("-R");
out = r2ai("give me a better name for this function. the output must be: 'afn NEWNAME'. do not include the function code, only the afn line. consider: " + considerations, out).trim();
out += " @ " + r2.cmd("?v $FB").trim();
break;
case "v": // "-v"
out = r2.cmd("afv;pdc");
r2ai("-R");
out = r2ai("guess a better name and type for each local variable and function argument taking using. output an r2 script using afvn and afvt commands", out);
break;
case "r": // "-r"
args = args.slice(2).trim();
if (args) {
decprompt = args
} else {
console.log(decprompt);
}
break;
case "V": // "-V"
r2aidec("-d find vulnerabilities, dont show the code, only show the response");
break;
case "e": // "-e"
args = args.slice(2).trim();
if (args) {
decaiEval(args);
} else {
console.log("decai -e api=" + decaiApi);
console.log("decai -e host=" + decaiHost);
console.log("decai -e port=" + decaiPort);
console.log("decai -e prompt=" + decprompt);
console.log("decai -e ctxfile=" + decaiContextFile);
console.log("decai -e cmds=" + decaiCommands);
console.log("decai -e cache=" + decaiCache);
console.log("decai -e lang=" + decaiLanguage);
console.log("decai -e debug=" + decaiDebug);
}
break;
case "q": // "-q"
out = r2ai(args.slice(2).trim());
break;
case "Q": // "-Q"
out = r2ai(args.slice(2).trim(), lastOutput);
break;
case "x": // "-x"
out = r2.cmd("pdsf@e:scr.color=0");
r2ai("-R");
out = r2ai("Explain whats this function doing in one sentence.", out)
break;
case "d": // "-d"
try {
args = args.slice(2).trim();
const file = "/tmp/.pdc.txt";
r2.call("rm .pdc.txt");
r2.call("rm " + file);
r2.cmd("echo > " + file);
let count = 0;
let text = "";
if (decaiContextFile !== "") {
if (r2.cmd2("test -f " + decaiContextFile).value === 0) {
text += "Context:\n";
text += "[RULES]\n";
text += r2.cmd("cat " + decaiContextFile);
text += "[/RULES]\n";
}
}
const origColor = r2.cmd("e scr.color");
r2.cmd("e scr.color=0");
for (const c of decaiCommands.split(",")) {
if (c.trim() === "") {
continue;
}
const output = r2.cmd(c);
if (output.length > 5) {
text += "Output from " + c + ":\n";
text += "[BEGIN]\n";
text += output + "\n";
text += "[END]\n";
count++;
}
}
r2.cmd("e scr.color=" + origColor);
if (count === 0) {
console.error("Nothing to do.");
break;
}
r2ai("-R");
const query = (decprompt + " " + args).trim() + ". Explain this pseudocode in " + decaiLanguage;
out = r2ai(query, text);
lastOutput = out;
} catch (e) {
console.error(e);
}
break;
default:
usage();
break;
}
if (out) {
r2.log(out);
}
} else {
usage();
}
return true;
}
r2.unload("core", command);
r2.plugin("core", function () {
function coreCall(cmd) {
if (cmd.startsWith(command)) {
var args = cmd.slice(command.length).trim();
return r2aidec(args);
}
return false;
}
return {
"name": command,
"license": "MIT",
"desc": "r2 decompiler based on r2ai",
"call": coreCall,
};
});
})();
-13
View File
@@ -1,13 +0,0 @@
PACKAGE=decai
DEPENDS=radare2
SECTION=user/shell
PRIORITY=optional
MAINTAINER=pancake <[email protected]>
VERSION=$(shell git tag | tail -n 1)
# arch
UNAMEM=$(shell uname -m)
ifeq ($(UNAMEM),x86_64)
ARCH=amd64
else
ARCH=arm64
endif
-2
View File
@@ -1,2 +0,0 @@
AI based decompiler plugin for radare2
r2js plugin for radare2 that provides several features for reverse engineers, from autonaming functions, improve decompiling outputs, find bugs or explain functions to automated crackme solving using local and remote models if API keys are provided..
-34
View File
@@ -1,34 +0,0 @@
include ./CONFIG
UNAME=$(shell uname)
SUDO?=sudo
DEPENDS=
CROSSARCH=x64
R2CFG_FLAGS?=
PWD=$(shell pwd)
PACKAGE_DIR?=${PWD}
R2_VERSION=$(shell r2 -qv)
DOCKCROSS=$(PWD)/../dockcross
R2PLUGDIR=/usr/lib/radare2/$(R2_VERSION)
all: root
$(SUDO) rm -rf control data
$(MAKE) clean
mkdir -p data
cp -rf root/* data
$(MAKE) control
$(MAKE) deb
root:
mkdir -p root/$(R2PLUGDIR)
cp -f ../../decai.r2.js root/$(R2PLUGDIR)
purge: clean
rm -rf root
summary:
echo $(VERSION)
include deb.mk
-130
View File
@@ -1,130 +0,0 @@
# Create .deb without using dpkg tools.
#
# Author: Tim Wegener <[email protected]>
#
# Use 'include deb_hand.mak' after defining the user variables in a local
# makefile.
#
# The 'data' rule must be customised in the local make file.
# This rule should make a 'data' directory containing the full file
# layout of the installed package.
#
# This makefile will create a debian-binary file a control directory and a
# a build directory in the current directory.
# Do 'make clobber' to remove these generated files.
#
# Destination:
# PACKAGE_DIR - directory where package (and support files) will be built
# defaults to the current directory
#
# Sources:
# SOURCE_DIR - directory containing files to be packaged
# ICON_SOURCE - 26x26 icon file for maemo
# DESCR - description with summary on first line
# preinst, postinst, prerm, postrm - optional control shell scripts
# These fields are used to build the control file:
# PACKAGE =
# VERSION =
# ARCH =
# SECTION =
# PRIORITY =
# MAINTAINER =
# DEPENDS =
#
# SOURCE_DIR =
# ICON_SOURCE =
# (ICON_SOURCE is optional)
# *** NO USER CHANGES REQUIRED BEYOND THIS POINT ***
ifeq ($(shell uname),Darwin)
MD5SUM=md5
else
MD5SUM=md5sum
endif
GAWK=awk
PACKAGE_DIR=$(shell pwd)
CONTROL_EXTRAS ?= ${wildcard preinst postinst prerm postrm}
${PACKAGE_DIR}/control: ${PACKAGE_DIR}/data ${CONTROL_EXTRAS} DESCR \
${ICON_SOURCE}
#rm -rf $@
mkdir -p $@
ifneq (${CONTROL_EXTRAS},)
cp ${CONTROL_EXTRAS} $@
endif
# Make control file.
echo "Package: ${PACKAGE}" > $@/control
echo "Version: ${VERSION}" >> $@/control
echo "Section: ${SECTION}" >> $@/control
echo "Priority: ${PRIORITY}" >> $@/control
echo "Architecture: ${ARCH}" >> $@/control
ifneq (${DEPENDS},)
echo "Depends: ${DEPENDS}" >> $@/control
endif
echo "Installed-Size: ${shell du -s ${PACKAGE_DIR}/data|cut -f1}" \
>> $@/control
echo "Maintainer: ${MAINTAINER}" >> $@/control
printf "Description:" >> $@/control
cat DESCR | ${GAWK} '{print " "$$0;}' >> $@/control
#ifneq (${ICON_SOURCE},)
# echo "Maemo-Icon-26:" >> $@/control
# base64 ${ICON_SOURCE} | ${GAWK} '{print " "$$0;}' >> $@/control
#endif
# Make md5sums.
cd ${PACKAGE_DIR}/data && find . -type f -exec ${MD5SUM} {} \; \
| sed -e 's| \./||' \
> $@/md5sums
${PACKAGE_DIR}/debian-binary:
echo "2.0" > $@
${PACKAGE_DIR}/clean:
rm -rf ${PACKAGE_DIR}/data ${PACKAGE_DIR}/control ${PACKAGE_DIR}/build *.deb
${PACKAGE_DIR}/build: ${PACKAGE_DIR}/debian-binary ${PACKAGE_DIR}/control \
${PACKAGE_DIR}/data
rm -rf $@
mkdir $@
cp ${PACKAGE_DIR}/debian-binary $@/
cd ${PACKAGE_DIR}/control && tar czvf $@/control.tar.gz *
cd ${PACKAGE_DIR}/data && \
COPY_EXTENDED_ATTRIBUTES_DISABLE=true \
COPYFILE_DISABLE=true \
tar cpzvf $@/data.tar.gz *
# Convert GNU ar to BSD ar that debian requires.
# Note: Order of files within ar archive is important!
${PACKAGE_DIR}/${PACKAGE}_${VERSION}_${ARCH}.deb: ${PACKAGE_DIR}/build
ar -rc $@ $</debian-binary $</control.tar.gz $</data.tar.gz
#sed -e 's|^\([^/]\+\)/ \(.*\)|\1 \2|g' $@tmp > $@fail
#rm -f $@tmp
#mv $@fail $@
.PHONY: data
data: ${PACKAGE_DIR}/data
.PHONY: control
control: ${PACKAGE_DIR}/control
.PHONY: build
build: ${PACKAGE_DIR}/build
.PHONY: clean
clean: ${PACKAGE_DIR}/clean $(EXTRA_CLEAN)
rm -f debian-binary
.PHONY: deb
deb: ${PACKAGE_DIR}/${PACKAGE}_${VERSION}_${ARCH}.deb
clobber::
rm -rf ${PACKAGE_DIR}/debian_binary ${PACKAGE_DIR}/control \
${PACKAGE_DIR}/data ${PACKAGE_DIR}/build
push:
scp *.deb radare.org:/srv/http/radareorg/cydia/debs
mrproper: clean
rm -rf root
-37
View File
@@ -1,37 +0,0 @@
{
"use": true,
"default": "pipeTest",
"_default": "asciiStack",
"asciiStack": {
"globalQuery": "output in C, respond only with code, no explanations, no markdown",
"pipeline": [
{
"model": "qwen2.5-coder:latest",
"query": "stack frame layout for this function, output a table in ascii art"
}
]
},
"pipeTest": {
"globalQuery": "output in C, respond only with code, no explanations, no markdown",
"pipeline": [
{
"model": "qwen2.5-coder:latest",
"query": "resolve stack variables and arrays size without using malloc"
},
{
"model": "qwen2.5-coder:14b",
"query": "remove boilerplate and unnecessary code, replace goto with if/else blocks"
}
],
"commentedOut": [
{
"model": "qwen2.5-coder:14b",
"query": "use better types and names for variable and arguments"
},
{
"model": "qwen2.5-coder:14b",
"query": "inline all the functions into a single one and make the code more readable"
}
]
}
}
-36
View File
@@ -1,36 +0,0 @@
# decai r2plugin
Boilerplate to embed `decai.r2.js` inside the radare2 core library using the
external plugin system (XPS). Unlike `r2ai/r2plugin`, this does not link any
native code: the r2.js script is converted into a C buffer and registered as
an embedded startup script, so r2 runs it at startup exactly like a file in
the `plugins/*.r2.js` directory (honoring `cfg.plugins`).
The `decai.r2.js` bundle is built from the typescript sources with
`r2frida-compile` (see `make -C src` in the decai directory). The C buffer is
generated at build time with `rax2 -C` (make builds) or `js2c.py` (meson).
## Usage
decai lives inside the r2ai repository, so expose it as `libr/xps/p/decai`:
```console
cd radare2/libr/xps/p
git clone https://github.com/radareorg/r2ai
ln -s r2ai/decai decai
```
For acr/make builds:
```console
cd radare2
make -C libr/xps EXTERNAL_PLUGINS=decai
./configure-plugins
make
```
For meson builds, make `libr/xps/p/meson.build` contain:
```meson
subdir('decai/r2plugin')
```
-2
View File
@@ -1,2 +0,0 @@
/Users/pancake/prg/radare2/libr/xps/p/decai/r2plugin/core/decai.r2.js.o: \
/Users/pancake/prg/radare2/libr/xps/p/decai/r2plugin/core/decai.r2.js.c
-3
View File
@@ -1,3 +0,0 @@
#ifdef R2_CORE_H
extern RCorePlugin r_core_plugin_decai;
#endif
-10
View File
@@ -1,10 +0,0 @@
DECAI_CORE_WD=$(LIBR)/xps/p/decai
DECAI_CORE_OBJ= \
$(DECAI_CORE_WD)/r2plugin/core/plugin.o \
$(DECAI_CORE_WD)/r2plugin/core/decai.r2.js.o
EXTERNAL_STATIC_OBJS+=$(DECAI_CORE_OBJ)
# decai.r2.js can be rebuilt from typescript with r2frida-compile (make -C src)
$(DECAI_CORE_WD)/r2plugin/core/decai.r2.js.c: $(DECAI_CORE_WD)/decai.r2.js
rax2 -C < $(DECAI_CORE_WD)/decai.r2.js | \
sed -e 's,^unsigned,const unsigned,' -e 's,buf,decai_qjs,g' > $@
-35
View File
@@ -1,35 +0,0 @@
/* decai - LGPL - Copyright 2026 - pancake */
#include <r_core.h>
// generated from ../decai.r2.js, see ../deps.mk
extern const unsigned char decai_qjs[];
extern const unsigned int decai_qjs_len;
static bool decai_init(RCorePluginSession *cps) {
r_core_script_embed (cps->core, "decai", "qjs", (const char *)decai_qjs, (int)decai_qjs_len);
return true;
}
static bool decai_call(RCorePluginSession *cps, const char *input) {
return false;
}
RCorePlugin r_core_plugin_decai = {
.meta = {
.name = "decai",
.desc = "AI decompiler (embedded decai.r2.js)",
.author = "pancake",
.license = "MIT",
},
.init = decai_init,
.call = decai_call,
};
#ifndef R2_PLUGIN_INCORE
R_API RLibStruct radare_plugin = {
.type = R_LIB_TYPE_CORE,
.data = &r_core_plugin_decai,
.version = R2_VERSION,
};
#endif
-372
View File
@@ -1,372 +0,0 @@
/Users/pancake/prg/radare2/libr/xps/p/decai/r2plugin/core/plugin.o: \
/Users/pancake/prg/radare2/libr/xps/p/decai/r2plugin/core/plugin.c \
/Users/pancake/prg/radare2/libr/include/r_core.h \
/Users/pancake/prg/radare2/libr/include/r_main.h \
/Users/pancake/prg/radare2/libr/include/r_types.h \
/Users/pancake/prg/radare2/libr/include/r_types_base.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ctype.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_ctype.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/cdefs.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_symbol_aliasing.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_posix_availability.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/ptrcheck.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_bounds.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/runetype.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_types.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/machine/_types.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/arm/_types.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_pthread/_pthread_types.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_size_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_ct_rune_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_rune_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_wchar_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_wint_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/types.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/appleapiopts.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/machine/types.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/arm/types.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_int8_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_int16_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_int32_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_int64_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_u_int8_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_u_int16_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_u_int32_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_u_int64_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_intptr_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_uintptr_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/machine/endian.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/arm/endian.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_endian.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/machine/_endian.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/arm/_endian.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/__endian.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libkern/_OSByteOrder.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libkern/arm/_OSByteOrder.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_u_char.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_u_short.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_u_int.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_caddr_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_dev_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_blkcnt_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_blksize_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_gid_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_in_addr_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_in_port_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_ino_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_ino64_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_key_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_mode_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_nlink_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_id_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_pid_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_off_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_uid_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_clock_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_ssize_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_time_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_useconds_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_suseconds_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_rsize_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_errno_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_fd_def.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/Availability.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/AvailabilityVersions.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/AvailabilityInternal.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/AvailabilityInternalLegacy.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_fd_setsize.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_fd_set.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_fd_clr.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_fd_zero.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_fd_isset.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_fd_copy.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_pthread/_pthread_attr_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_pthread/_pthread_cond_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_pthread/_pthread_condattr_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_pthread/_pthread_mutex_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_pthread/_pthread_mutexattr_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_pthread/_pthread_once_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_pthread/_pthread_rwlock_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_pthread/_pthread_rwlockattr_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_pthread/_pthread_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_pthread/_pthread_key_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_fsblkcnt_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_fsfilcnt_t.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/limits.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/limits.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/machine/limits.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/arm/limits.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/arm/_limits.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/syslimits.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/stdint.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/stdint.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_types/_uint8_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_types/_uint16_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_types/_uint32_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_types/_uint64_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_types/_intmax_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_types/_uintmax_t.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/stdbool.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/math.h \
/Users/pancake/prg/radare2/libr/include/r_types_overflow.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_str_util.h \
/Users/pancake/prg/radare2/libr/include/r_userconf.h \
/Users/pancake/prg/radare2/libr/include/r_version.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/stddef.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/stddef.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stddef_header_macro.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stddef_ptrdiff_t.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stddef_size_t.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stddef_rsize_t.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stddef_wchar_t.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stddef_null.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stddef_max_align_t.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stddef_offsetof.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/stdlib.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_stdlib.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/wait.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/signal.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/machine/signal.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/arm/signal.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/machine/_mcontext.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/arm/_mcontext.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/machine/_structs.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach/arm/_structs.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_sigaltstack.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_ucontext.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_sigset_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/resource.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_timeval.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/alloca.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_null.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/malloc/_malloc.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/malloc/_malloc_type.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/malloc/_ptrcheck.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_abort.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/assert.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_assert.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_static_assert.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/inttypes.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/inttypes.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_inttypes.h \
/Users/pancake/prg/radare2/libr/include/r_types_null.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/stdio.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_stdio.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_va_list.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/stdio.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_printf.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_seek_set.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_ctermid.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/secure/_stdio.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/secure/_common.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/string.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_string.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_strings.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/secure/_strings.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/secure/_string.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/stdarg.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stdarg_header_macro.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stdarg___gnuc_va_list.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stdarg_va_list.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stdarg_va_arg.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stdarg___va_copy.h \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/21/include/__stdarg_va_copy.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/fcntl.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/fcntl.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_o_sync.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_o_dsync.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_s_ifmt.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_timespec.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_filesec_t.h \
/Users/pancake/prg/radare2/libr/include/r_endian.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/stat.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/dirent.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/dirent.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/unistd.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/unistd.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_posix_vdisable.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/select.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_select.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_uuid_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/gethostuuid.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/time.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_timeval64.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/time.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_time.h \
/Users/pancake/prg/radare2/libr/include/r_getopt.h \
/Users/pancake/prg/radare2/libr/include/r_util.h \
/Users/pancake/prg/radare2/libr/include/r_regex.h \
/Users/pancake/prg/radare2/libr/include/r_list.h \
/Users/pancake/prg/radare2/libr/include/sdb/sdb.h \
/Users/pancake/prg/radare2/libr/include/sdb/asserts.h \
/Users/pancake/prg/radare2/libr/include/sdb/types.h \
/Users/pancake/prg/radare2/libr/include/sdb/config.h \
/Users/pancake/prg/radare2/libr/include/sdb/ht.h \
/Users/pancake/prg/radare2/libr/include/sdb/ht_pp.h \
/Users/pancake/prg/radare2/libr/include/sdb/ht_inc.h \
/Users/pancake/prg/radare2/libr/include/sdb/ls.h \
/Users/pancake/prg/radare2/libr/include/sdb/heap.h \
/Users/pancake/prg/radare2/libr/include/sdb/dict.h \
/Users/pancake/prg/radare2/libr/include/sdb/cdb.h \
/Users/pancake/prg/radare2/libr/include/sdb/cdb_make.h \
/Users/pancake/prg/radare2/libr/include/sdb/buffer.h \
/Users/pancake/prg/radare2/libr/include/sdb/version.h \
/Users/pancake/prg/radare2/libr/include/sdb/rangstr.h \
/Users/pancake/prg/radare2/libr/include/r_skiplist.h \
/Users/pancake/prg/radare2/libr/include/r_th.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_w32.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_sys.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/errno.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/errno.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/semaphore.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/semaphore.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/pthread.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/pthread/sched.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/pthread/pthread_impl.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/pthread/qos.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/qos.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_mach_port_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/signal.h \
/Users/pancake/prg/radare2/libr/include/r_event.h \
/Users/pancake/prg/radare2/libr/include/sdb/ht_up.h \
/Users/pancake/prg/radare2/libr/include/r_vec.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_assert.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_log.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_arena.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_itv.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_signal.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_alloc.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_rbtree.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_trie.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_strs.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_xml.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_new_rbtree.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_intervaltree.h \
/Users/pancake/prg/radare2/libr/include/r_util/../r_types.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_big.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_base32.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_base36.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_base64.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_base91.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_buf.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_mem.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_ref.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_bits.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_bitmap.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_bitset.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_time.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_debruijn.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_type.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_file.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_hex.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_name.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_num.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_table.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_graph.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_panels.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_pool.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_diff.h \
/Users/pancake/prg/radare2/libr/include/r_cons.h \
/Users/pancake/prg/radare2/libr/include/r_util/pj.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_strbuf.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_json.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_sandbox.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_stack.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_str.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/wchar.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_wchar.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_mbstate_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/__wctype.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/___wctype.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_types/_wctype_t.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_str_constpool.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/termios.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/termios.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/ttycom.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/ioccom.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/ttydefaults.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/ioctl.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/filio.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/sockio.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/net/if.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/net/if_var.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_timeval32.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/net/net_kev.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/socket.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/constrained_ctypes.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/machine/_param.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/arm/_param.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_sa_family_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_socklen_t.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/_types/_iovec_t.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_punycode.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_queue.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_range.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_spaces.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_strs_store.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_bloom.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_strpool.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_tree.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_uleb128.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_utf8.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_utf16.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_utf32.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_idpool.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_asn1.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_graph_drawable.h \
/Users/pancake/prg/radare2/libr/include/r_config.h \
/Users/pancake/prg/radare2/libr/include/r_util/bplist.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_x509.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_pkcs7.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_protobuf.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_token.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_axml.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_cfloat.h \
/Users/pancake/prg/radare2/libr/include/r_util/libc.h \
/Users/pancake/prg/radare2/libr/include/r_socket.h \
/Users/pancake/prg/radare2/libr/include/r_bind.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/netinet/in.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/netinet6/in6.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/un.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/poll.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/poll.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/arpa/inet.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/netdb.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/netinet/tcp.h \
/Users/pancake/prg/radare2/libr/include/r_magic.h \
/Users/pancake/prg/radare2/libr/include/r_agraph.h \
/Users/pancake/prg/radare2/libr/include/r_io.h \
/Users/pancake/prg/radare2/libr/include/r_lib.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/dlfcn.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_w32dw.h \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/ptrace.h \
/Users/pancake/prg/radare2/libr/include/r_fs.h \
/Users/pancake/prg/radare2/libr/include/r_egg.h \
/Users/pancake/prg/radare2/libr/include/r_asm.h \
/Users/pancake/prg/radare2/libr/include/r_arch.h \
/Users/pancake/prg/radare2/libr/include/r_bin.h \
/Users/pancake/prg/radare2/libr/include/r_muta.h \
/Users/pancake/prg/radare2/libr/include/r_hash.h \
/Users/pancake/prg/radare2/libr/include/r_muta/r_sm4.h \
/Users/pancake/prg/radare2/libr/include/r_bin_dwarf.h \
/Users/pancake/prg/radare2/libr/include/r_reg.h \
/Users/pancake/prg/radare2/libr/include/r_anal/op.h \
/Users/pancake/prg/radare2/libr/include/r_anal/hint.h \
/Users/pancake/prg/radare2/libr/include/r_esil.h \
/Users/pancake/prg/radare2/libr/include/sdb/ht_uu.h \
/Users/pancake/prg/radare2/libr/include/r_anal.h \
/Users/pancake/prg/radare2/libr/include/r_util/r_print.h \
/Users/pancake/prg/radare2/libr/include/r_search.h \
/Users/pancake/prg/radare2/libr/include/r_syscall.h \
/Users/pancake/prg/radare2/libr/include/r_flag.h \
/Users/pancake/prg/radare2/libr/include/r_codemeta.h \
/Users/pancake/prg/radare2/libr/include/sdb/set.h \
/Users/pancake/prg/radare2/libr/include/r_lang.h \
/Users/pancake/prg/radare2/libr/include/r_cmd.h \
/Users/pancake/prg/radare2/libr/include/r_sign.h \
/Users/pancake/prg/radare2/libr/include/r_debug.h \
/Users/pancake/prg/radare2/libr/include/r_bp.h \
/Users/pancake/prg/radare2/libr/include/rvc.h \
/Users/pancake/prg/radare2/libr/include/r_core_priv.h \
/Users/pancake/prg/radare2/libr/include/r_core_task.h
-1
View File
@@ -1 +0,0 @@
include p/decai/r2plugin/core/deps.mk
-11
View File
@@ -1,11 +0,0 @@
#!/usr/bin/env python3
"""Convert a script file into a C buffer named decai_qjs (same output as `rax2 -C` + sed)."""
import sys
data = open(sys.argv[1], "rb").read()
with open(sys.argv[2], "w") as f:
f.write("const unsigned char decai_qjs[] = {\n")
for i in range(0, len(data), 16):
f.write(" %s,\n" % ", ".join("0x%02x" % b for b in data[i:i + 16]))
f.write("};\nconst unsigned int decai_qjs_len = %d;\n" % len(data))
-16
View File
@@ -1,16 +0,0 @@
# decai XPS integration for radare2.
# Included from libr/xps/meson.build when decai lives under libr/xps/p/decai/.
# Unlike native plugins, decai ships no native code: decai.r2.js is embedded
# as a buffer in libr_core and the tiny core plugin registers it with
# r_core_script_embed() so r2 runs it at startup like any plugins/*.r2.js file.
core_plugins += ['decai']
decai_py3 = import('python').find_installation()
r_core_additional_sources += files('core/plugin.c')
r_core_additional_sources += custom_target('decai_r2_js_c',
input: '../decai.r2.js',
output: 'decai.r2.js.c',
command: [decai_py3, files('js2c.py'), '@INPUT@', '@OUTPUT@'],
)
-1
View File
@@ -1 +0,0 @@
core.decai
-19
View File
@@ -1,19 +0,0 @@
PLUGIN_NAME=decai
R2F_C_FLAGS=-c -S
R2FRIDA_COMPILE?=r2pm -r r2frida-compile
all: build
build:
$(R2FRIDA_COMPILE) $(R2F_C_FLAGS) -o ../$(PLUGIN_NAME).r2.js main.ts
test: build
r2 -i ../$(PLUGIN_NAME).r2.js -q -c 'af;decai -d' /bin/ls
fmt:
deno fmt
clean:
rm -rf out node_modules ../$(PLUGIN_NAME).r2.js
.PHONY: all build test clean
-53
View File
@@ -1,53 +0,0 @@
import { API_KEYS_PATH, DECAI_CONFIG_DIR } from "./constants";
import { providerRegistry } from "./providers";
import { ApiKeyResult } from "./types";
import { ensurePath, fileExists, parseEnvLikeString } from "./utils";
function getProviderEnvMap(): Record<string, string> {
return Object.fromEntries(
Object.entries(providerRegistry)
.filter(([, v]) => v.authKey)
.map(([k, v]) => [k, v.authKey!]),
);
}
export function getApiKey(provider: string, envvar: string): ApiKeyResult {
const keyEnv = r2.cmd("'%" + envvar).trim();
if (!keyEnv.includes("=") && keyEnv !== "") {
return [keyEnv.trim(), null, "env"];
}
const providerLower = provider.toLowerCase();
const keysPath = API_KEYS_PATH;
if (fileExists(keysPath)) {
const keyFile = r2.cmd("'cat " + keysPath);
const kv = parseEnvLikeString(keyFile);
if (providerLower in kv) {
return [kv[providerLower], null, "txt"];
}
}
const keyPath = "~/.r2ai." + providerLower + "-key";
if (fileExists(keyPath)) {
const keyFile = r2.cmd("'cat " + keyPath);
return keyFile === ""
? [null, "Cannot read " + keyPath, "no"]
: [keyFile.trim(), null, "file"];
}
return [null, "Not available", "nope"];
}
export function editApiKeys(): void {
ensurePath(DECAI_CONFIG_DIR, API_KEYS_PATH);
r2.cmd("'ed " + API_KEYS_PATH);
}
export function listApiKeys(): void {
Object.entries(getProviderEnvMap()).forEach(([key, env]) => {
const status = getApiKey(key, env)[2];
console.log(status, "\t", key);
});
}
-568
View File
@@ -1,568 +0,0 @@
import { AutoReply } from "./types";
import { state } from "./state";
import { COMMAND, DEFAULT_PROMPT, HELP_TEXT, VERSION } from "./constants";
import {
b64,
filterResponse,
trimAnsi,
trimDown,
trimJson,
} from "./utils";
import { editApiKeys, listApiKeys } from "./apiKeys";
import { editRcConfig, evalConfig, listAllConfig } from "./config";
import { r2ai } from "./r2ai";
function withColorDisabled<T>(fn: () => T): T {
const origColor = r2.cmd("e scr.color");
r2.cmd("e scr.color=0");
try {
return fn();
} finally {
r2.cmd("e scr.color=" + origColor);
}
}
export function showHelp(): void {
let helpmsg = "";
const msg = (m: string) => (helpmsg += " " + COMMAND + " " + m + "\n");
helpmsg += "Usage: " + COMMAND + " (-h) ...\n";
helpmsg += "Version: " + VERSION + "\n";
msg("-a [query] - solve query with auto mode");
msg("-b [url] - set base URL (alias for decai -e baseurl)");
msg("-d [f1 ..] - decompile given functions");
msg("-dd [..] - same as above, but ignoring cache");
msg("-dD [query] - decompile current function with given extra query");
msg("-dr - decompile function and its called ones (recursive)");
msg("-e - display and change eval config vars");
msg("-E - edit and run decai.txt");
msg("-h - show this help");
msg("-H - help setting up r2ai");
msg("-i [f] [q] - include given file and query");
msg("-k - list API key status");
msg("-K - edit apikeys.txt");
msg("-m [model] - use -m? or -e model=? to list the available models");
msg("-n - suggest better function name");
msg("-p [provider] - same as decai -e api (will be provider)");
msg("-q [text] - query language model with given text");
msg("-Q [text] - query on top of the last output");
msg("-r [prompt] - change role prompt (same as: decai -e prompt)");
msg("-R - reset role prompt to default prompt");
msg("-s - function signature");
msg("-v - show local variables");
msg("-V - find vulnerabilities");
msg("-x[*] - eXplain current function (-x* for r2 script)");
r2.log(helpmsg.trim());
}
export function decompile(
args: string,
extraQuery: boolean,
useCache: boolean,
recursiveCalls: boolean,
): string | undefined {
if (useCache) {
const cachedAnnotation = r2.cmd("anos").trim();
if (cachedAnnotation.length > 0) {
return cachedAnnotation;
}
}
let context = "";
if (recursiveCalls) {
const at = r2.cmd("s");
context += "## Context functions:\n";
const funcs = r2.cmdAt("axff~^C[2]~$$", at);
for (const addr of funcs.split(/\n/g)) {
context += r2.cmd("pdc@" + addr);
}
r2.cmd("s " + at);
}
const appendQuery = extraQuery ? " " + args : "";
try {
const parsedArgs = args.slice(2).trim();
let text = "";
if (
state.contextFile !== "" &&
r2.cmd2("test -f " + state.contextFile).value === 0
) {
text += "## Context:\n[START]\n" +
r2.cmd("cat " + state.contextFile) +
"\n[END]\n";
}
const { body, count } = withColorDisabled(() => {
let body = "## Before:\n";
let count = 0;
for (const c of state.commands.split(",")) {
if (c.trim() === "") continue;
const oneliner = extraQuery || parsedArgs.trim().length === 0
? c
: c + "@@= " + parsedArgs;
const output = r2.cmd(oneliner);
if (output.length > 5) {
body += "Output of " + c + ":\n[START]\n" + output + "\n[END]\n";
count++;
}
}
body += "## After:\n";
return { body, count };
});
if (count === 0) {
console.error("Nothing to do.");
return;
}
let out = "";
if (state.decopipe.use) {
const origModel = state.model;
try {
const dpipe = state.decopipe[state.decopipe.default as string] as {
pipeline: Array<{ model?: string; query: string }>;
globalQuery: string;
};
let code = text + body;
for (const dp of dpipe.pipeline) {
if (dp.model) state.model = dp.model;
const query = dp.query + ". " + dpipe.globalQuery;
out = r2ai(query, code, true);
if (state.debug) {
console.log("QUERY\n", query, "\nINPUT\n", code, "\nOUTPUT\n", out);
}
code = out;
}
out = code;
} finally {
state.model = origModel;
}
} else {
const query = appendQuery;
text += body + context;
out = r2ai(query, text, false);
state.lastOutput = out;
}
if (useCache && out.length > 1) {
r2.call("ano=base64:" + b64(out));
}
if (out.startsWith("```")) {
out = out.replace(/```.*\n/, "").replace(/```$/, "");
}
return out.trim();
} catch (e) {
const err = e as Error;
console.error(err, err.stack);
return;
}
}
export function explainFunction(): string {
const hints = withColorDisabled(() =>
"[START]" +
state.commands.split(",").map((c) => r2.cmd(c)).join("\n") + "[END]"
);
const res = r2ai(
"Analyze function calls, references, comments and strings, loops and ignore registers and memory accesses. Explain the purpose of this function in a single short sentence. /no_think Do not introduce or argue the response, translation of the explanation in " +
state.humanLanguage,
hints,
true,
);
const lines = res.trim().split(/\n/g);
return lines[lines.length - 1].trim();
}
export function suggestSignature(): string {
const tmp = state.language;
const code = r2.cmd("afv;pdc");
state.language = "C";
let out = "'afs " +
r2ai(
"analyze the uses of the arguments and return value to infer the signature, identify which is the correct type for the return. Do NOT print the function body, ONLY output the function signature, like if it was going to be used in a C header",
code,
false,
);
const brace = out.indexOf("{");
if (brace !== -1) {
out = out.substring(0, brace);
}
state.language = tmp;
return out;
}
export function autoMode(
queryText: string,
mainHandler: (args: string) => boolean,
): void {
const replies: string[] = [];
while (true) {
let q = HELP_TEXT.auto;
if (replies.length > 0) {
q += "## Command Results\n\n";
for (const rep of replies) {
const rp = JSON.parse(rep);
q += "### " + rp.command + "\n\n```\n" + rp.response + "\n```\n";
}
}
q += "\n\n## User Prompt\n\n" + queryText;
if (state.debug) {
console.log("#### input\n", q, "\n#### /input");
}
console.log("Thinking...");
const out = r2ai("", q, true);
if (state.debug) {
console.log("#### output\n", out, "\n#### /output");
}
try {
const o: AutoReply = JSON.parse(
trimJson(trimDown(filterResponse(out))),
);
if (
o.action === "r2cmd" ||
o.action === "response" ||
o.action === o.command
) {
const ocmd = o.command || "";
if (o.reason) {
console.log("[r2cmd] Reasoning: " + o.reason);
if (state.tts) {
r2.syscmd("pkill say");
r2.syscmd(
"say -v Alex -r 250 '" + o.reason.replace(/'/g, "") + "' &",
);
}
}
console.log("[r2cmd] Action: " + o.description);
console.log("[r2cmd] Command: " + ocmd);
let cmd = ocmd;
if (!state.yolo) {
cmd = autoRepl(ocmd, mainHandler);
}
console.log("[r2cmd] Running: " + cmd);
const obj = r2.cmd2(cmd);
const logs = obj.logs
? obj.logs.map((x: { type: string; message: string }) =>
x.type + ": " + x.message
).join("\n")
: "";
const res = (obj.res + logs).trim();
console.log(res);
const cleanRes = trimAnsi(res);
if (state.debug) {
console.log("<r2output>\n", cleanRes, "\n<(r2output>");
}
replies.push(
JSON.stringify({
action: "response",
command: cmd,
description: o.description,
response: cleanRes,
}),
);
} else if (o.action === "reply") {
console.log("Done\n", o.response);
break;
} else {
console.log("Unknown response\n", JSON.stringify(out));
break;
}
} catch (e) {
const response = out.indexOf('response": "');
if (response !== -1) {
const res = out
.slice(response + 12)
.replace(/\\n/g, "\n")
.replace(/\\/g, "");
console.log(res);
} else {
console.log(out);
console.error(e);
}
break;
}
}
}
function autoRepl(
ocmd: string,
mainHandler: (args: string) => boolean,
): string {
while (true) {
const cmd = r2.cmd("'?ie Tweak command? ('?' for help)").trim();
if (cmd === "q!") {
console.error("Break!");
break;
}
if (cmd === "?") {
autoHelp();
continue;
} else if (cmd.startsWith(":")) {
console.log(r2.cmd(cmd.slice(1)));
continue;
} else if (cmd.startsWith("-e")) {
mainHandler(cmd);
continue;
} else if (cmd === "!") {
return "?e do NOT execute '" + ocmd + "' again, continue without it";
} else if (cmd.startsWith("!")) {
console.log(r2.syscmd(cmd.slice(1)));
continue;
} else if (cmd === "q") {
return "?e All data collected!. Do not call more commands, reply the solutions";
} else if (!cmd) {
return ocmd;
} else {
const comment = cmd.indexOf("#");
if (comment !== -1) {
const command = cmd.slice(0, comment).trim();
return command;
}
return cmd;
}
}
return ocmd;
}
function autoHelp(): void {
console.log(" '!' do not run this command");
console.log(" '!c' run system command");
console.log(" 'q' to quit auto and try to solve");
console.log(" 'q!' quit auto without solving");
console.log(" 'c # C' use given command with comment");
console.log(" ':c' run r2 command without feeding auto");
console.log(" '-e' set decai configuration variables");
}
function getFlagValue(args: string): string {
return args.slice(2).trim();
}
function currentFunctionExists(): boolean {
return r2.cmd("afi.").trim().length > 0;
}
const noFunctionMessage = "Cannot find function at current offset";
export function handleCommand(
args: string,
mainHandler: (args: string) => boolean,
): string | undefined {
if (args === "" || !args.startsWith("-")) {
showHelp();
return;
}
let output = "";
const flag = args[1];
const argValue = getFlagValue(args);
switch (flag) {
case "H":
console.log(HELP_TEXT.decai);
break;
case "a":
autoMode(argValue, mainHandler);
break;
case "m": {
if (argValue === "=") {
evalConfig("model=");
} else if (argValue) {
evalConfig("model=" + argValue);
} else {
evalConfig("model");
}
break;
}
case "n":
case "f": {
if (!currentFunctionExists()) {
output = noFunctionMessage;
break;
}
output = r2.cmd("axff~$[3]");
const considerations = r2
.cmd("fd.")
.trim()
.split(/\n/)
.filter((x: string) => !x.startsWith("secti"))
.join(",");
output = r2ai(
"give me a better name for this function. the output must be: 'afn NEWNAME'. do not include the function code, only the afn line. consider: " +
considerations,
output,
false,
).trim();
output += " @ " + r2.cmd("?v $FB").trim();
break;
}
case "v": {
if (!currentFunctionExists()) {
output = noFunctionMessage;
break;
}
output = r2.cmd("afv;pdc");
output = r2ai(
"guess a better name and type for each local variable and function argument taking using. output an r2 script using afvn and afvt commands",
output,
false,
);
break;
}
case "i": {
const parts = argValue.split(/\s+/).filter(Boolean);
if (parts.length >= 2) {
const file = parts[0];
const query = parts.slice(1).join(" ");
const fileData = r2.cmd("cat " + file);
console.log(r2ai(query, fileData, true));
} else {
console.log("Use: decai -i [file] [query ...]");
}
break;
}
case "p": {
if (argValue) {
evalConfig("api=" + argValue);
} else {
listApiKeys();
}
break;
}
case "r": {
if (argValue) {
state.prompt = argValue;
} else {
console.log(state.prompt);
}
break;
}
case "R":
state.prompt = DEFAULT_PROMPT;
break;
case "s":
if (!currentFunctionExists()) {
output = noFunctionMessage;
break;
}
output = suggestSignature();
break;
case "V":
output = r2ai(
"find vulnerabilities, dont show the code, only show the response, provide a sample exploit",
state.lastOutput,
false,
);
break;
case "K":
editApiKeys();
break;
case "E":
editRcConfig();
break;
case "k":
listApiKeys();
break;
case "b": {
if (argValue) {
evalConfig("baseurl=" + argValue);
} else {
console.log(state.baseurl);
}
break;
}
case "e": {
if (argValue) {
evalConfig(argValue);
} else {
listAllConfig();
}
break;
}
case "q":
try {
output = r2ai(argValue, null, true);
} catch (e) {
const err = e as Error;
console.error(err, err.stack);
}
break;
case "Q":
output = r2ai(argValue, state.lastOutput, false);
break;
case "x":
if (!currentFunctionExists()) {
output = noFunctionMessage;
break;
}
output = explainFunction();
if (args[2] === "*" || args[2] === "r") {
output = "'CC " + output;
}
break;
case "d":
if (args[2] === "r") {
output = decompile(args.slice(2), true, state.cache, true) || "";
} else if (args[2] === "d") {
output = decompile(args, false, false, false) || "";
} else if (args[2] === "D") {
output = decompile(args, true, false, false) || "";
} else {
output = decompile(args, false, state.cache, false) || "";
}
break;
default:
showHelp();
break;
}
return output || undefined;
}
-199
View File
@@ -1,199 +0,0 @@
import { ConfigHandler, ConfigHandlers } from "./types";
import { DECAI_CONFIG_DIR, DECAI_CONFIG_PATH } from "./constants";
import { defaultState, state } from "./state";
import { listModelsFor } from "./models";
import { listProviders } from "./providers";
import { formatHeaders, parseHeaders } from "./headers";
import { ensurePath, fileExists } from "./utils";
let rcConfigLoaded = false;
function parseBoolean(value: string): boolean {
return value === "true" || value === "1";
}
type StateKey = keyof typeof state;
function boolHandler(key: StateKey): ConfigHandler {
// deno-lint-ignore no-explicit-any
const s = state as any;
return {
get: () => s[key] as boolean,
set: (v: string) => { s[key] = parseBoolean(v); },
};
}
function stringHandler(key: StateKey): ConfigHandler {
// deno-lint-ignore no-explicit-any
const s = state as any;
return {
get: () => s[key] as string,
set: (v: string) => { s[key] = v; },
};
}
function normalizeApiType(value: string): "chat" | "generate" | null {
const apiType = value.trim();
if (apiType === "chat") {
return "chat";
}
if (apiType === "generate") {
return "generate";
}
return null;
}
export const configHandlers: ConfigHandlers = {
pipeline: {
get: () => state.pipeline,
set: (v: string) => {
state.pipeline = v;
try {
state.decopipe = JSON.parse(r2.cmd("cat " + v));
} catch (e) {
console.error(e);
}
},
},
model: {
get: () => state.model,
set: (v: string) => {
if (v === "?") {
listModelsFor(state.api);
} else {
state.model = v.trim();
}
},
},
deterministic: boolHandler("deterministic"),
files: boolHandler("useFiles"),
think: {
get: () => state.think || "false",
set: (v: string) => { state.think = v; },
},
apitype: {
get: () => state.apitype,
set: (v: string) => {
const apiType = normalizeApiType(v);
if (apiType) {
state.apitype = apiType;
} else {
console.error("Invalid apitype. Use chat or generate.");
}
},
},
debug: boolHandler("debug"),
http: {
get: () => state.http,
set: (v: string) => {
const backend = v.trim();
if (backend === "auto" || backend === "r2" || backend === "curl") {
state.http = backend;
} else {
console.error("Invalid http backend. Use auto, r2 or curl.");
}
},
},
timeout: {
get: () => state.timeout,
set: (v: string) => { state.timeout = Math.max(0, parseInt(v, 10) || 0); },
},
api: {
get: () => state.api,
set: (v: string) => {
if (v === "?") {
console.error(listProviders().join("\n"));
} else {
state.api = v;
}
},
},
lang: stringHandler("language"),
hlang: stringHandler("humanLanguage"),
cache: boolHandler("cache"),
cmds: stringHandler("commands"),
tts: boolHandler("tts"),
yolo: boolHandler("yolo"),
prompt: stringHandler("prompt"),
ctxfile: stringHandler("contextFile"),
baseurl: stringHandler("baseurl"),
headers: {
get: () => formatHeaders(state.extraHeaders),
set: (v: string) => { state.extraHeaders = parseHeaders(v); },
},
maxtokens: {
get: () => state.maxInputTokens,
set: (v: string) => { state.maxInputTokens = parseInt(v, 10) || -1; },
},
};
function resetRcConfigState(): void {
const keep = { host: state.host, port: state.port, lastOutput: state.lastOutput };
Object.assign(state, defaultState, keep);
state.decopipe = { ...defaultState.decopipe };
state.extraHeaders = [...defaultState.extraHeaders];
}
function normalizeRcLine(line: string): string | null {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#")) {
return null;
}
return trimmed.startsWith("decai -e ")
? trimmed.slice("decai -e ".length).trim()
: trimmed;
}
export function evalConfig(arg: string): void {
const eqIndex = arg.indexOf("=");
const k = eqIndex === -1 ? arg : arg.slice(0, eqIndex);
const v = eqIndex === -1 ? undefined : arg.slice(eqIndex + 1);
const handler = configHandlers[k];
if (!handler) {
console.error("Unknown config key");
return;
}
if (typeof v !== "undefined") {
handler.set(v);
} else {
console.log(handler.get());
}
}
export function listAllConfig(): void {
Object.entries(configHandlers).forEach(([key, handler]) => {
const value = handler.get();
console.log("decai -e " + key + "=" + value);
});
}
export function loadRcConfig(): void {
resetRcConfigState();
if (!fileExists(DECAI_CONFIG_PATH)) {
return;
}
const rcFile = r2.call("cat " + DECAI_CONFIG_PATH);
for (const line of rcFile.split(/\r?\n/)) {
const rcLine = normalizeRcLine(line);
if (rcLine && rcLine[0] != '#') {
evalConfig(rcLine);
}
}
}
export function ensureRcConfigLoaded(): void {
if (rcConfigLoaded) {
return;
}
loadRcConfig();
rcConfigLoaded = true;
}
export function editRcConfig(): void {
ensurePath(DECAI_CONFIG_DIR, DECAI_CONFIG_PATH);
r2.cmd("'ed " + DECAI_CONFIG_PATH);
loadRcConfig();
rcConfigLoaded = true;
}
-130
View File
@@ -1,130 +0,0 @@
export const VERSION = "1.5.4";
export const COMMAND = "decai";
export const DECAI_CONFIG_DIR = "~/.config/r2ai";
export const API_KEYS_PATH = DECAI_CONFIG_DIR + "/apikeys.txt";
export const DECAI_CONFIG_PATH = DECAI_CONFIG_DIR + "/decai.txt";
export const DEFAULT_PROMPT =
"Rewrite this pseudocode into concise and clean code. Output only the provided function. Do not add wrappers, helper examples, test code, or main-like functions. Replace goto with structured control flow, simplify as much as possible, infer types and use better names for variables and parameters, some strings may be appearing as comments, preserve only what is implied by the input, and remove dead code.";
export const HELP_TEXT = {
decai: `# Using Decai
Decai is the radare2 plugin for decompiling functions with the help of language models.
By default uses a local ollama server, but can you can pick any other service by using 'decai -e api=?'.
[0x00000000]> decai -e api=?
r2ai deepseek anthropic claude gemini groq hf lmstudio mistral ollama ollamacloud openapi openai openrouter vllm xai zen
## Using Ollama
* Visit https://ollama.com to install it.
* Download the model of choice: 'ollama run llama3.3'
* Configure decai to use the given model with: 'decai -e model=?'
These are the most recommended models for decompiling in local:
* hhao/qwen2.5-coder-tools:latest (18GB of ram)
* hhao/qwen2.5-coder-tools:32b (24GB of ram required)
## Common Options
* 'decai -e baseurl=<url>' override default host and port for API endpoint (e.g., 'http://localhost:11434')
* 'decai -e apitype=chat|generate' choose the Ollama API endpoint (default: chat)
* 'decai -e headers=Authorization: Bearer ...\\nUser-Agent: curl/8.7.1' add or override HTTP headers
* "export DECAI_HEADERS='Authorization: Bearer ...\\nUser-Agent: curl/8.7.1'" set extra headers from the environment
* 'decai -e http=auto|r2|curl' select the HTTP transport (default: auto, prefers r2.http)
* 'decai -e timeout=0' disable curl timeouts, or set a positive number of seconds
* 'decai -e deterministic=true' to remove randomness from decompilation responses
* 'decai -e lang=Python' to output the decompilation in Python instead of C
* 'decai -e hlang=Catalan' to add comments or explanations in that language (instead of English)
* 'decai -e cmds=pdd,pdg' use r2dec and r2ghidra instead of r2's pdc as input for decompiling
* 'decai -e prompt=..' default prompt must be fine for most models and binaries, feel free to tweak it
## API Keys
Remove services like OpenAI, Mistral, Anthropic, Grok, Gemini, .. require API keys to work.
See 'decai -k' to list the status of available APIkeys
Use 'decai -K' to edit apikeys.txt and 'decai -E' to edit decai.txt.
Decai will pick them from the environment or the config files in your home:
* echo KEY > ~/.r2ai.openai-key
* export OPENAI_API_KEY=...
* put one 'key=value' pair per line in ~/.config/r2ai/decai.txt
## Using the R2AI Server:
Install r2ai or r2ai-server with r2pm:
[0x0000000]> decai -e api=r2ai
[0x0000000]> r2pm -ci r2ai
Choose one of the recommended models (after r2pm -r r2ai):
* -m ibm-granite/granite-20b-code-instruct-8k-GGUF
* -m QuantFactory/granite-8b-code-instruct-4k-GGUF
* -m TheBloke/Mistral-7B-Instruct-v0.2-GGUF
Start the webserver:
$ r2pm -r r2ai-server -l r2ai -m granite-8...
`,
auto: `# Radare2 Auto Mode
Use function calling to execute radare2 commands in order to resolve the user request defined in the "User Prompt" section, analyze the responses attached in the "Command Results" section.
## Function Calling
Respond ONLY using plain JSON. Process user query and decide which function calls are necessary to solve the task.
1. Analyze the user request to determine if we need to run commands to extend the knowledge and context of the problem.
2. If function call is needed, construct the JSON like this:
- Fill the "action" key with the "r2cmd" value.
- Specify the "command" as a string.
- Optionally, provide a "reason" and "description"
3. If the answer can be provided and no more function calls are required:
- Use the key "action": "reply".
- Include "response" with the direct answer to the user query.
Return the result as a JSON object.
### Sample Function Calling Communication
Command Results: already performed actions with their responses
User Prompt: "Count how many functions we have here."
Response:
{
"action": "r2cmd",
"command": "aflc",
"description": "Count functions"
"reason": "Evaluate if the program is analyzed before running aaa"
}
## Rules
Use radare2 to resolve user requests.
* Explain each step in the "reason" field of the JSON.
* Follow the initial analysis instructions.
* Output only valid JSON as specified.
* Decompile and inspect functions, starting from main.
* Run only the needed commands to gather info.
* Use "@ (address|symbol)" to seek temporarily.
* Output should be a verbose markdown report.
* Use "sym." or "fcn." prefixes if "pdc" is empty.
* If a seek fails, use "f~name" to find the symbol's address.
### Initial Analysis
1. Run "aflc" to count the number of functions
2. If the output of "aflc" is "0" run "aaa" once, then "aflc" again
3. Run only one command at a time (do not use ";")
### Special cases
* On Swift binaries run...
`,
};
-91
View File
@@ -1,91 +0,0 @@
import { state } from "./state";
const HEADER_ENV_VARS = ["DECAI_HEADERS", "R2AI_HEADERS"];
interface ParsedHeader {
name: string;
value: string;
}
function readEnvVar(name: string): string {
const value = r2.cmd("'%" + name).trim();
return value !== "" && !value.includes("=") ? value : "";
}
function normalizeHeaderName(name: string): string {
return name.trim().toLowerCase();
}
function parseHeaderLine(line: string): ParsedHeader | null {
const colon = line.indexOf(":");
const equals = line.indexOf("=");
const sep = colon !== -1 && (equals === -1 || colon < equals)
? colon
: equals;
if (sep === -1) {
return null;
}
const name = line.slice(0, sep).trim();
if (name === "") {
return null;
}
return {
name,
value: line.slice(sep + 1).trim(),
};
}
function formatHeader(header: ParsedHeader): string {
return header.value === ""
? `${header.name}:`
: `${header.name}: ${header.value}`;
}
function collectHeaders(
headerLines: string[],
into: Map<string, string>,
): void {
for (const rawLine of headerLines) {
const header = parseHeaderLine(rawLine.trim());
if (header) {
into.set(normalizeHeaderName(header.name), formatHeader(header));
}
}
}
export function parseHeaders(value: string): string[] {
const headers = new Map<string, string>();
const lines = value.replace(/\\n/g, "\n").split(/\r?\n/g)
.filter((l) => { const t = l.trim(); return t && !t.startsWith("#"); });
collectHeaders(lines, headers);
return Array.from(headers.values());
}
export function formatHeaders(headers: string[]): string {
return headers.join("\\n");
}
export function mergeHeaders(...groups: string[][]): string[] {
const merged = new Map<string, string>();
for (const group of groups) {
collectHeaders(group, merged);
}
return Array.from(merged.values());
}
function readConfiguredEnvHeaders(): string[] {
for (const name of HEADER_ENV_VARS) {
const value = readEnvVar(name);
if (value) {
return parseHeaders(value);
}
}
return [];
}
export function getConfiguredHeaders(): string[] {
return mergeHeaders(readConfiguredEnvHeaders(), state.extraHeaders);
}
-108
View File
@@ -1,108 +0,0 @@
import { HttpResponse } from "./types";
import { state } from "./state";
import { mergeHeaders } from "./headers";
import { debugLog } from "./utils";
interface HttpRequestOptions {
method: "GET" | "POST";
url: string;
headers: string[];
payload?: string;
}
function shellEscape(arg: string): string {
return `'${arg.replace(/'/g, `'"'"'`)}'`;
}
function executeRequest(options: HttpRequestOptions): string {
const { method, url, headers, payload } = options;
const requestHeaders = mergeHeaders(
["Content-Type: application/json"],
headers,
);
if (state.http !== "curl") {
if (r2.http) {
return r2.http(method, url, requestHeaders.join("\n"), payload ?? "").body;
}
if (state.http === "r2") {
throw new Error("r2.http is not available in this runtime");
}
}
const cmdParts = ["curl", "-s"];
if (state.timeout > 0) {
cmdParts.push("--max-time", String(state.timeout));
}
requestHeaders.forEach((h) => cmdParts.push("-H", shellEscape(h)));
if (method === "POST") {
if (!payload) throw new Error("Payload required for POST requests");
const tmpfile = r2.fdump(payload);
cmdParts.push("--data-binary", "@" + shellEscape(tmpfile), shellEscape(url));
const cmd = cmdParts.join(" ") + "; rm " + shellEscape(tmpfile);
debugLog(cmd);
return r2.syscmds(cmd);
} else {
cmdParts.push(shellEscape(url));
const cmd = cmdParts.join(" ");
debugLog(cmd);
return r2.syscmds(cmd);
}
}
function parseNdjson(output: string): HttpResponse | null {
const lines = output.split("\n").filter((l) => l.trim() !== "");
if (lines.length <= 1) return null;
let content = "";
let lastObj: Record<string, unknown> | null = null;
for (const line of lines) {
try {
const obj = JSON.parse(line);
if (obj.error) return obj as HttpResponse;
if (obj.message?.content !== undefined) {
content += obj.message.content;
}
lastObj = obj;
} catch {
return null;
}
}
if (lastObj) {
lastObj.message = { role: "assistant", content };
return lastObj as HttpResponse;
}
return null;
}
export function httpRequest(options: HttpRequestOptions): HttpResponse {
try {
const output = executeRequest(options).trim();
if (output === "") {
return { error: "empty response" };
}
try {
return JSON.parse(output) as HttpResponse;
} catch {
const ndjson = parseNdjson(output);
if (ndjson) return ndjson;
return { error: output, rawOutput: output };
}
} catch (e) {
const err = e as Error;
return { error: err.message };
}
}
export function httpGet(url: string, headers: string[]): HttpResponse {
return httpRequest({ method: "GET", url, headers });
}
export function httpPost(
url: string,
headers: string[],
payload: string,
): HttpResponse {
return httpRequest({ method: "POST", url, headers, payload });
}
-10
View File
@@ -1,10 +0,0 @@
### 3. HTTP Request Patterns
**Location**: `http.ts`, used throughout providers and models
**Problems**:
- Two different curl methods (`curlArgs` vs `curlFile`) with similar but
separate logic
- Error handling in HTTP layer vs. application layer
- No centralized request/response abstraction
-34
View File
@@ -1,34 +0,0 @@
import { COMMAND } from "./constants";
import { handleCommand } from "./commands";
import { ensureRcConfigLoaded } from "./config";
function main(args: string): boolean {
ensureRcConfigLoaded();
const output = handleCommand(args, main);
if (output) {
r2.log(output);
}
return true;
}
function installPlugin(): void {
r2.unload("core", COMMAND);
r2.plugin("core", function () {
function coreCall(cmd: string): boolean {
if (cmd.startsWith(COMMAND)) {
const args = cmd.slice(COMMAND.length).trim();
return main(args);
}
return false;
}
return {
name: COMMAND,
license: "MIT",
desc: "r2 decompiler based on r2ai",
call: coreCall,
};
});
}
installPlugin();
-34
View File
@@ -1,34 +0,0 @@
import { getProvider, listModels } from "./providers";
export function listModelsFor(api: string): void {
const providerConfig = getProvider(api);
if (!providerConfig) {
console.error(`Unknown provider: ${api}`);
return;
}
try {
const models = listModels(api);
if (models) {
console.log(models);
}
if (providerConfig.hardcodedModels) {
providerConfig.hardcodedModels.forEach((model) => console.log(model));
}
if (api === "mistral") {
console.log("ministral-8b-latest");
}
if (!models && !providerConfig.hardcodedModels) {
console.log(providerConfig.defaultModel);
}
} catch (e) {
const err = e as Error;
console.error(`Error listing models for ${api}:`, err.message);
console.log(providerConfig.defaultModel);
}
}
-541
View File
@@ -1,541 +0,0 @@
import {
AnthropicPayload,
ApiKeyResult,
ApiStyle,
AuthStyle,
GeminiPayload,
HttpResponse,
ModelDataEntry,
OllamaModelEntry,
OllamaPayload,
ProviderConfig,
ProviderRegistry,
} from "./types";
import { state } from "./state";
import { filterResponse, joinUrl, padRight } from "./utils";
import { getApiKey } from "./apiKeys";
import { getConfiguredHeaders, mergeHeaders } from "./headers";
import { httpGet, httpPost } from "./http";
interface ProviderRuntime {
buildPayload: (model: string, query: string) => object;
parseResponse: (response: HttpResponse) => string;
buildUrl: (baseUrl: string, model: string, apiKey?: string) => string;
requiresUrlApiKey?: boolean;
}
function getApiKeyName(authKey: string): string {
return authKey.split("_")[0].toLowerCase();
}
function readProviderKey(provider: ProviderConfig): ApiKeyResult | undefined {
if (!provider.authKey) {
return undefined;
}
const keyNames = Array.from(
new Set(
[provider.keyName, getApiKeyName(provider.authKey)].filter(Boolean),
),
) as string[];
let fallback: ApiKeyResult | undefined;
for (const keyName of keyNames) {
const result = getApiKey(keyName, provider.authKey);
if (result[0]) {
return result;
}
if (!fallback && result[2] !== "nope") {
fallback = result;
}
}
return fallback ||
getApiKey(getApiKeyName(provider.authKey), provider.authKey);
}
function getErrorMessage(error: HttpResponse["error"]): string | undefined {
if (!error) {
return undefined;
}
if (typeof error === "string") {
return error;
}
return error.message || JSON.stringify(error);
}
function getProviderBaseUrl(provider: ProviderConfig): string {
return state.baseurl || provider.defaultBaseUrl;
}
function getProviderHeaders(
providerHeaders: string[],
extraHeaders: string[] = getConfiguredHeaders(),
): string[] {
return mergeHeaders(providerHeaders, extraHeaders);
}
function buildBearerHeaders(apiKey: string | null): string[] {
return apiKey ? ["Authorization: Bearer " + apiKey] : [];
}
function buildAnthropicHeaders(apiKey: string | null): string[] {
const headers = ["anthropic-version: 2023-06-01"];
return apiKey ? mergeHeaders(headers, ["x-api-key: " + apiKey]) : headers;
}
function buildAuthHeaders(
provider: ProviderConfig,
apiKey: string | null,
): string[] {
const authStyle: AuthStyle = provider.authStyle || "none";
switch (authStyle) {
case "bearer":
return buildBearerHeaders(apiKey);
case "anthropic":
return buildAnthropicHeaders(apiKey);
case "none":
default:
return [];
}
}
function listDataModels(
url: string,
headers: string[],
mapper: (model: ModelDataEntry) => string,
): string {
const response = httpGet(url, headers);
const error = getErrorMessage(response.error);
if (error) {
console.error(error);
return "error invalid response";
}
return response.data?.map(mapper).join("\n") || "";
}
function uniqueBy<T>(items: T[], getKey: (item: T) => string): T[] {
const seen = new Set<string>();
return items.filter((item) => {
const key = getKey(item);
if (seen.has(key)) {
return false;
}
seen.add(key);
return true;
});
}
function getProviderRequest(provider: ProviderConfig): {
baseUrl: string;
headers: string[];
} {
const apiKey = readProviderKey(provider)?.[0] || null;
return {
baseUrl: getProviderBaseUrl(provider),
headers: getProviderHeaders(buildAuthHeaders(provider, apiKey)),
};
}
function listV1Models(provider: ProviderConfig): string {
const { baseUrl, headers } = getProviderRequest(provider);
const url = joinUrl(baseUrl, "v1/models");
return listDataModels(url, headers, (model) => model.id);
}
function listOllamaModels(provider: ProviderConfig): string {
const { baseUrl, headers } = getProviderRequest(provider);
const response = httpGet(joinUrl(baseUrl, "api/tags"), headers);
const error = getErrorMessage(response.error);
if (error) {
console.error(error);
return "error invalid response";
}
return response.models?.map((model: OllamaModelEntry) => model.name).join(
"\n",
) || "";
}
function listMistralModels(provider: ProviderConfig): string {
const { baseUrl, headers } = getProviderRequest(provider);
const response = httpGet(joinUrl(baseUrl, "v1/models"), headers);
if (response.data) {
return uniqueBy(response.data, (model) => model.name || model.id)
.map((model) =>
[
padRight(model.name || model.id, 30),
padRight("" + (model.max_context_length || ""), 10),
model.description || "",
].join(" ")
)
.join("\n");
}
return "";
}
const providerRuntimes: Record<ApiStyle, ProviderRuntime> = {
openai: {
buildPayload: (model, query) => ({
stream: false,
model,
messages: [{ role: "user", content: query }],
}),
parseResponse: (response) => {
if (response.error) {
throw new Error(getErrorMessage(response.error) || "Unknown error");
}
if (response.choices && response.choices[0]?.message?.content) {
return filterResponse(response.choices[0].message.content);
}
throw new Error("Invalid response format");
},
buildUrl: (baseUrl) => joinUrl(baseUrl, "v1/chat/completions"),
},
anthropic: {
buildPayload: (model, query) => {
const payload: AnthropicPayload = {
model,
max_tokens: 5128,
messages: [{ role: "user", content: query }],
};
if (isThinkEnabled()) {
payload.thinking = { type: "enabled", budget_tokens: 4096 };
payload.max_tokens = 16000;
}
if (state.deterministic && !isThinkEnabled()) {
Object.assign(payload, { temperature: 0, top_p: 0, top_k: 1 });
}
return payload;
},
parseResponse: (response) => {
if (response.content && Array.isArray(response.content)) {
const parts: string[] = [];
for (const block of response.content as Array<Record<string, unknown>>) {
if (block.text) {
parts.push(block.text as string);
}
}
if (parts.length > 0) {
return filterResponse(parts.join("\n"));
}
}
if (response.error) {
throw new Error(getErrorMessage(response.error) || "Unknown error");
}
throw new Error("Invalid response format");
},
buildUrl: (baseUrl) => joinUrl(baseUrl, "v1/messages"),
},
ollama: {
buildPayload: (model, query) => {
const payload: OllamaPayload = {
stream: false,
model,
};
if (state.apitype === "generate") {
payload.prompt = query;
} else {
payload.messages = [{ role: "user", content: query }];
}
if (isThinkEnabled()) {
if (state.think === "true" || state.think === "1") {
payload.think = true;
} else {
// Pass reasoning levels like "low", "medium", "high" as-is
payload.think = state.think;
}
} else {
// Disable thinking by default — models like glm-5 enable it
// implicitly, generating thousands of reasoning tokens
payload.think = false;
}
if (state.deterministic) {
payload.options = {
repeat_last_n: 0,
top_p: 1.0,
top_k: 1.0,
temperature: 0.0,
repeat_penalty: 1.0,
seed: 123,
};
}
return payload;
},
parseResponse: (response) => {
if (response.error) {
throw new Error(getErrorMessage(response.error));
}
if (typeof response.response === "string") {
return filterResponse(response.response);
}
if (response.message?.content) {
return filterResponse(response.message.content);
}
throw new Error(JSON.stringify(response));
},
buildUrl: (baseUrl) => joinUrl(baseUrl, "api/" + state.apitype),
},
gemini: {
buildPayload: (_model, query) => {
const payload: GeminiPayload = {
contents: [{ parts: [{ text: query }] }],
};
const genConfig: Record<string, unknown> = {};
if (state.deterministic) {
Object.assign(genConfig, { temperature: 0.0, topP: 1.0, topK: 1 });
}
if (state.think !== "") {
if (isThinkDisabled()) {
genConfig.thinkingConfig = { thinkingBudget: 0 };
} else {
genConfig.thinkingConfig = { thinkingBudget: 8192 };
}
}
if (Object.keys(genConfig).length > 0) {
payload.generationConfig = genConfig;
}
return payload;
},
parseResponse: (response) => {
if (response.candidates && response.candidates[0]?.content?.parts) {
const parts = response.candidates[0].content.parts as Array<Record<string, unknown>>;
const textParts = parts
.filter((p) => !p.thought && p.text)
.map((p) => p.text as string);
if (textParts.length > 0) {
return filterResponse(textParts.join("\n"));
}
}
if (response.error) {
throw new Error(getErrorMessage(response.error));
}
console.log(JSON.stringify(response));
throw new Error("Invalid response format");
},
buildUrl: (baseUrl, model, apiKey) =>
joinUrl(
baseUrl,
`v1beta/models/${model}:generateContent?key=${apiKey}`,
),
requiresUrlApiKey: true,
},
};
export const providerRegistry: ProviderRegistry = {
anthropic: {
defaultModel: "claude-sonnet-5-5",
defaultBaseUrl: "https://api.anthropic.com",
authKey: "ANTHROPIC_API_KEY",
keyName: "anthropic",
authStyle: "anthropic",
apiStyle: "anthropic",
},
claude: {
defaultModel: "claude-sonnet-5-5",
defaultBaseUrl: "https://api.anthropic.com",
authKey: "ANTHROPIC_API_KEY",
keyName: "claude",
authStyle: "anthropic",
apiStyle: "anthropic",
},
openai: {
defaultModel: "gpt-4o-mini",
defaultBaseUrl: "https://api.openai.com",
authKey: "OPENAI_API_KEY",
authStyle: "bearer",
apiStyle: "openai",
},
ollama: {
defaultModel: "qwen2.5-coder:latest",
defaultBaseUrl: "http://localhost:11434",
authStyle: "none",
apiStyle: "ollama",
},
ollamacloud: {
defaultModel: "gpt-oss:120b",
defaultBaseUrl: "https://ollama.com",
authKey: "OLLAMA_API_KEY",
keyName: "ollamacloud",
authStyle: "bearer",
apiStyle: "ollama",
},
zen: {
defaultModel: "big-pickle",
defaultBaseUrl: "https://opencode.ai/zen",
authKey: "OPENCODE_API_KEY",
keyName: "zen",
authStyle: "bearer",
apiStyle: "openai",
hardcodedModels: ["big-pickle", "glm-5", "kimi-k2.5"],
},
gemini: {
defaultModel: "gemini-2.5-flash",
defaultBaseUrl: "https://generativelanguage.googleapis.com",
authKey: "GEMINI_API_KEY",
authStyle: "none",
apiStyle: "gemini",
hardcodedModels: [
"gemini-2.0-flash",
"gemini-2.0-flash-lite",
"gemini-2.5-pro",
"gemini-2.5-flash",
"gemini-2.5-flash-lite",
],
},
mistral: {
defaultModel: "codestral-latest",
defaultBaseUrl: "https://api.mistral.ai",
authKey: "MISTRAL_API_KEY",
authStyle: "bearer",
apiStyle: "openai",
},
xai: {
defaultModel: "grok-beta",
defaultBaseUrl: "https://api.x.ai",
authKey: "XAI_API_KEY",
authStyle: "bearer",
apiStyle: "openai",
hardcodedModels: ["grok-2", "grok-beta"],
},
lmstudio: {
defaultModel: "local-model",
defaultBaseUrl: "http://127.0.0.1:1234",
authStyle: "none",
apiStyle: "openai",
},
deepseek: {
defaultModel: "deepseek-coder",
defaultBaseUrl: "https://api.deepseek.com",
authKey: "DEEPSEEK_API_KEY",
authStyle: "bearer",
apiStyle: "openai",
hardcodedModels: ["deepseek-coder", "deepseek-chat"],
},
openrouter: {
defaultModel: "openai/gpt-4o-mini",
defaultBaseUrl: "https://openrouter.ai/api",
authKey: "OPENROUTER_API_KEY",
authStyle: "bearer",
apiStyle: "openai",
},
groq: {
defaultModel: "llama-3.3-70b-versatile",
defaultBaseUrl: "https://api.groq.com/openai",
authKey: "GROQ_API_KEY",
authStyle: "bearer",
apiStyle: "openai",
},
};
export function getProvider(api: string): ProviderConfig | undefined {
return providerRegistry[api];
}
export function listProviders(): string[] {
return Object.keys(providerRegistry);
}
export function listModels(providerName: string): string {
const provider = getProvider(providerName);
if (!provider) {
throw new Error(`Unknown provider: ${providerName}`);
}
if (providerName === "mistral") {
return listMistralModels(provider);
}
switch (provider.apiStyle) {
case "openai":
case "anthropic":
return listV1Models(provider);
case "ollama":
return listOllamaModels(provider);
case "gemini":
return "";
default:
return "";
}
}
function isThinkDisabled(): boolean {
return state.think === "false" || state.think === "0";
}
function isThinkEnabled(): boolean {
return state.think !== "" && !isThinkDisabled();
}
function buildQuery(msg: string, hideprompt: boolean, apiStyle: ApiStyle): string {
let query = msg;
// Only add prompt-based thinking hints for APIs without native think support
if (state.think !== "" && apiStyle === "openai") {
if (isThinkDisabled()) {
query +=
' Answers directly and concisely, without showing any thinking steps or internal reasoning. Never include phrases like "Let me think".';
query += " /no_think";
} else {
query =
"Think step by step and explain the reasoning process, When answering, first output your reasoning inside <think> and </think> tags, then give the final answer." +
query;
}
}
return hideprompt ? query : state.prompt + languagePrompt() + query;
}
function languagePrompt(): string {
return "\n.Translate the code into " + state.language +
" programming language\n";
}
function callRuntime(
provider: ProviderConfig,
runtime: ProviderRuntime,
msg: string,
hideprompt: boolean,
): string {
const model = state.model || provider.defaultModel;
const query = buildQuery(msg, hideprompt, provider.apiStyle);
const apiKey = readProviderKey(provider);
if (runtime.requiresUrlApiKey && apiKey?.[1] && provider.authKey) {
return `Cannot read ~/.r2ai.${getApiKeyName(provider.authKey)}-key`;
}
const payload = runtime.buildPayload(model, query);
const authHeaders = buildAuthHeaders(provider, apiKey?.[0] || null);
if (provider.apiStyle === "ollama") {
authHeaders.push("Accept: application/x-ndjson");
}
const headers = getProviderHeaders(authHeaders);
const url = runtime.buildUrl(
getProviderBaseUrl(provider),
model,
apiKey?.[0] || undefined,
);
try {
return runtime.parseResponse(
httpPost(url, headers, JSON.stringify(payload)),
);
} catch (error) {
return "ERROR: " + (error as Error).message;
}
}
export function callProvider(msg: string, hideprompt: boolean): string {
const provider = getProvider(state.api);
if (!provider) {
return `Unknown value for 'decai -e api'. Available: ${
listProviders().join(", ")
}`;
}
return callRuntime(
provider,
providerRuntimes[provider.apiStyle],
msg,
hideprompt,
);
}
-41
View File
@@ -1,41 +0,0 @@
import { state } from "./state";
import { fileDump, joinUrl, tmpdir } from "./utils";
import { callProvider } from "./providers";
import { httpGet } from "./http";
export function r2ai(
queryText: string,
fileData: string | null,
hideprompt: boolean = false,
): string {
const cleanFileData = (fileData || "").replace(/`/g, "");
const cleanQuery = queryText.replace(/'/g, "");
// Handle r2ai server mode
if (state.api === "r2" || state.api === "r2ai") {
const fileName = tmpdir(".pdc.txt");
fileDump(fileName, cleanFileData);
const q = cleanQuery.startsWith("-")
? cleanQuery
: ["-i", fileName, cleanQuery].join(" ");
const baseUrl = state.baseurl || state.host + ":" + state.port;
const url = joinUrl(baseUrl, "cmd/" + encodeURIComponent(q));
const response = httpGet(url, []);
if (response.error) {
return `Error: ${response.error}`;
}
return response.result || JSON.stringify(response) ||
"Cannot curl, use r2ai-server or r2ai -w";
}
if (cleanQuery.startsWith("-")) return "";
let q = cleanQuery + ":\n" + cleanFileData;
if (state.maxInputTokens > 0 && q.length > state.maxInputTokens) {
q = q.slice(0, state.maxInputTokens);
}
return callProvider(q, hideprompt);
}
-290
View File
@@ -1,290 +0,0 @@
export type R2Address = number | string | unknown;
export interface R2HttpResponse {
status: number;
body: string;
}
export type R2HttpRequest = (
method: "GET" | "POST",
url: string,
headers: string,
data?: string,
) => R2HttpResponse;
export interface R2LogEntry {
type: string;
message: string;
}
export interface R2CommandResult {
res?: string;
value?: number;
logs?: R2LogEntry[];
[key: string]: unknown;
}
export interface R2Plugin {
name: string;
license: string;
desc: string;
call: (command: string) => boolean;
}
export type R2PluginFactory = () => R2Plugin;
export class R2PipeAdapter {
r2p: R2Pipe;
http?: R2HttpRequest;
constructor(r2p: R2Pipe) {
this.r2p = r2p;
this.http = r2p.http?.bind(r2p);
}
/**
* Run a command in the associated instance of radare2 and return the output as a string
*
* @param {string} command to be executed inside radare2.
* @returns {string} The output of the command execution
*/
cmd(command: string): string {
return this.r2p.cmd(command);
}
cmd2(command: string): R2CommandResult {
const output = this.r2p.cmd(command);
return typeof output === "string" ? { res: output } : output;
}
cmdAt(
command: string,
address: R2Address,
): string {
return this.r2p.cmdAt(command, address);
}
cmdj(cmd: string): unknown {
return this.r2p.cmdj(cmd);
}
call(command: string): string {
return this.r2p.call(command);
}
callj(cmd: string): unknown {
return this.r2p.cmdj(cmd);
}
callAt(
command: string,
address: R2Address,
): string {
return this.r2p.cmdAt(command, address);
}
syscmd(command: string): string {
return this.r2p.syscmd(command);
}
syscmds(command: string): string {
return this.r2p.syscmds(command);
}
fdump(data: string): string {
return this.r2p.fdump(data);
}
log(msg: string): void {
return this.r2p.log(msg);
}
plugin(type: string, maker: R2PluginFactory): boolean {
return this.r2p.plugin(type, maker);
}
unload(type: string, name: string): boolean {
return this.r2p.unload(type, name);
}
}
export function newR2PipeSync(r2p: R2Pipe): R2PipeSync {
return new R2PipeAdapter(r2p) as R2PipeSync;
}
/**
* Generic interface to interact with radare2, abstracts the access to the associated
* instance of the tool, which could be native via rlang or remote via pipes or tcp/http.
*
* @typedef R2Pipe
*/
export interface R2Pipe {
/**
* Run a command in the associated instance of radare2 and return the output as a string
*
* @param {string} command to be executed inside radare2.
* @returns {string} The output of the command execution
*/
cmd(cmd: string): string;
/**
* Run a radare2 command in a different address. Same as `.cmd(x + '@ ' + a)`
*
* @param {string} command to be executed inside radare2.
* @param {number|string|NativePointer} command to be executed inside radare2.
* @returns {string} The output of the command execution
*/
cmd2(cmd: string): R2CommandResult;
cmdAt(cmd: string, address: R2Address): string;
/**
* Run a radare2 command expecting the output to be JSON
*
* @param {string} command to be executed inside radare2. The given command should end with `j`
* @returns {object} the JSON decoded object from the output of the command
*/
cmdj(cmd: string): unknown;
/**
* Call a radare2 command. This is similar to `R2Pipe.cmd`, but skips all the command parsing rules,
* which is safer and faster but you cannot use any special modifier like `@`, `~`, ...
*
* See R2Pipe.callAt() to call a command on a different address
*
* @param {string} command to be executed inside radare2. The given command should end with `j`
* @returns {object} the JSON decoded object from the output of the command
*/
call(cmd: string): string;
/**
* Call a radare2 command in a different address
*
* @param {string} command to be executed inside radare2. The given command should end with `j`
* @param {NativePointer|string|number} where to seek to execute this command (previous offset is restored after executing it)
* @returns {string} the string containing the output of the command
*/
callAt(cmd: string, address: R2Address): string;
syscmd(cmd: string): string;
syscmds(cmd: string): string;
fdump(data: string): string;
http?: R2HttpRequest;
/**
* Same as cmdj but using .call which avoids command injection problems
*
* @param {string} command to be executed inside radare2. The given command should end with `j`
* @returns {object} the JSON decoded object from the command output
*/
callj(cmd: string): unknown;
/**
* Log a string to the associated console. This is used internally by `console.log` in some implementations.
*
* @param {string} text to be displayed
*/
log(msg: string): void;
/**
* Instantiate a new radare2 plugin with the given type and constructor method.
*
* @param {string} type of plugin ("core", "io", "arch", ...)
* @param {string} function that returns the plugin definition
* @returns {boolean} true if successful
*/
plugin(type: string, maker: R2PluginFactory): boolean;
/**
* Unload the plugin associated with a `type` and a `name`.
*
* @param {string} type of plugin ("core", "io", "arch", ...)
* @param {string} name of the plugin
* @returns {boolean} true if successful
*/
unload(type: string, name: string): boolean;
}
/**
* Generic interface to interact with radare2, abstracts the access to the associated
* instance of the tool, which could be native via rlang or remote via pipes or tcp/http.
*
* @typedef R2PipeSync
*/
export interface R2PipeSync {
/**
* Run a command in the associated instance of radare2
*
* @param {string} command to be executed inside radare2.
* @returns {string} The output of the command execution
*/
cmd(cmd: string): string;
/**
* Run a radare2 command in a different address. Same as `.cmd(x + '@ ' + a)`
*
* @param {string} command to be executed inside radare2.
* @param {number|string|NativePointer} command to be executed inside radare2.
* @returns {string} The output of the command execution
*/
cmd2(cmd: string): R2CommandResult;
cmdAt(cmd: string, address: R2Address): string;
/**
* Run a radare2 command expecting the output to be JSON
*
* @param {string} command to be executed inside radare2. The given command should end with `j`
* @returns {object} the JSON decoded object from the output of the command
*/
cmdj(cmd: string): unknown;
/**
* Call a radare2 command. This is similar to `R2Pipe.cmd`, but skips all the command parsing rules,
* which is safer and faster but you cannot use any special modifier like `@`, `~`, ...
*
* See R2Pipe.callAt() to call a command on a different address
*
* @param {string} command to be executed inside radare2. The given command should end with `j`
* @returns {object} the JSON decoded object from the output of the command
*/
call(cmd: string): string;
/**
* Call a radare2 command in a different address
*
* @param {string} command to be executed inside radare2. The given command should end with `j`
* @param {NativePointer|string|number} where to seek to execute this command (previous offset is restored after executing it)
* @returns {string} the string containing the output of the command
*/
callAt(cmd: string, address: R2Address): string;
syscmd(cmd: string): string;
syscmds(cmd: string): string;
fdump(data: string): string;
http?: R2HttpRequest;
/**
* Same as cmdj but using .call which avoids command injection problems
*
* @param {string} command to be executed inside radare2. The given command should end with `j`
* @returns {object} the JSON decoded object from the command output
*/
callj(cmd: string): unknown;
/**
* Log a string to the associated console. This is used internally by `console.log` in some implementations.
*
* @param {string} text to be displayed
*/
log(msg: string): void;
/**
* Instantiate a new radare2 plugin with the given type and constructor method.
*
* @param {string} type of plugin ("core", "io", "arch", ...)
* @param {string} function that returns the plugin definition
* @returns {boolean} true if successful
*/
plugin(type: string, maker: R2PluginFactory): boolean;
/**
* Unload the plugin associated with a `type` and a `name`.
*
* @param {string} type of plugin ("core", "io", "arch", ...)
* @param {string} name of the plugin
* @returns {boolean} true if successful
*/
unload(type: string, name: string): boolean;
}
/**
* A global instance of R2Pipe associated with the current instance of radare2
*
* @type {R2PipeSync}
*/
export declare const r2: R2Pipe;
-36
View File
@@ -1,36 +0,0 @@
import { AppState } from "./types";
import { DEFAULT_PROMPT } from "./constants";
export const defaultState: AppState = {
decopipe: { use: false },
host: "http://localhost",
port: "11434",
baseurl: "",
extraHeaders: [],
api: "ollama",
pipeline: "",
commands: "pdc",
yolo: false,
tts: false,
language: "C",
humanLanguage: "English",
deterministic: true,
debug: false,
timeout: 180,
http: "auto",
think: "",
apitype: "chat",
useFiles: false,
contextFile: "",
model: "",
cache: false,
maxInputTokens: -1,
prompt: DEFAULT_PROMPT,
lastOutput: "",
};
export const state: AppState = {
...defaultState,
decopipe: { ...defaultState.decopipe },
extraHeaders: [...defaultState.extraHeaders],
};
-16
View File
@@ -1,16 +0,0 @@
{
"compilerOptions": {
"target": "es2020",
"lib": ["esnext"],
"module": "commonjs",
"outDir": "out",
"sourceMap": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": [
"**/*.ts"
]
}
-169
View File
@@ -1,169 +0,0 @@
import type { R2PipeSync } from "./r2pipe";
interface ConsoleLike {
log: (...args: unknown[]) => void;
error: (...args: unknown[]) => void;
}
declare global {
const r2: R2PipeSync;
const console: ConsoleLike;
function btoa(str: string): string;
}
export type ApiStyle = "openai" | "anthropic" | "ollama" | "gemini";
export type AuthStyle = "none" | "bearer" | "anthropic";
export type OllamaApiType = "chat" | "generate";
export interface ProviderConfig {
defaultModel: string;
defaultBaseUrl: string;
authKey?: string;
authStyle?: AuthStyle;
keyName?: string;
apiStyle: ApiStyle;
hardcodedModels?: string[];
}
export interface ProviderRegistry {
[key: string]: ProviderConfig;
}
export interface DecoPipeConfig {
use: boolean;
default?: string;
[key: string]: unknown;
}
export interface AppState {
decopipe: DecoPipeConfig;
host: string;
port: string;
baseurl: string;
extraHeaders: string[];
api: string;
pipeline: string;
commands: string;
yolo: boolean;
tts: boolean;
language: string;
humanLanguage: string;
deterministic: boolean;
debug: boolean;
timeout: number;
http: "auto" | "r2" | "curl";
think: string;
apitype: OllamaApiType;
useFiles: boolean;
contextFile: string;
model: string;
cache: boolean;
maxInputTokens: number;
prompt: string;
lastOutput: string;
}
export interface ConfigHandler {
get: () => string | number | boolean;
set: (v: string) => void;
}
export interface ConfigHandlers {
[key: string]: ConfigHandler;
}
export type ApiKeyResult = [string | null, string | null, string];
export interface ApiError {
message?: string;
[key: string]: unknown;
}
export interface OpenAIChoice {
message?: {
content?: string;
};
}
export interface AnthropicContentBlock {
text?: string;
}
export interface OllamaMessage {
content?: string;
}
export interface GeminiPart {
text?: string;
}
export interface GeminiCandidate {
content?: {
parts?: GeminiPart[];
};
}
export interface ModelDataEntry {
id: string;
name?: string;
max_context_length?: number;
description?: string;
}
export interface OllamaModelEntry {
name: string;
}
export interface HttpResponse {
error?: ApiError | string;
rawOutput?: string;
choices?: OpenAIChoice[];
content?: AnthropicContentBlock[];
message?: OllamaMessage;
candidates?: GeminiCandidate[];
data?: ModelDataEntry[];
models?: OllamaModelEntry[];
result?: string;
[key: string]: unknown;
}
export type JsonObject = Record<string, unknown>;
export interface AnthropicPayload {
model: string;
max_tokens: number;
messages: Array<{ role: string; content: string }>;
thinking?: { type: string; budget_tokens?: number };
temperature?: number;
top_p?: number;
top_k?: number;
}
export interface OllamaPayload {
stream: boolean;
model: string;
messages?: Array<{ role: string; content: string }>;
prompt?: string;
think?: boolean | string;
options?: {
repeat_last_n: number;
top_p: number;
top_k: number;
temperature: number;
repeat_penalty: number;
seed: number;
};
}
export interface GeminiPayload {
contents: Array<{ parts: Array<{ text: string }> }>;
generationConfig?: Record<string, unknown>;
}
export interface AutoReply {
action: string;
command?: string;
description?: string;
reason?: string;
response?: string;
}
-97
View File
@@ -1,97 +0,0 @@
import { state } from "./state";
export function tmpdir(path: string): string {
const dir = r2.cmd("-e dir.tmp").trim() ?? ".";
return dir + "/" + path;
}
// r2's mkdir and touch commands do not expand '~'
export function expandHome(path: string): string {
if (!path.startsWith("~/")) {
return path;
}
const home = r2.cmd("'%HOME").trim() || r2.cmd("'%USERPROFILE").trim();
return home ? home + path.slice(1) : path;
}
export function ensurePath(dir: string, file: string): void {
r2.cmd("'mkdir -p " + expandHome(dir));
r2.cmd("'touch " + expandHome(file));
}
export function fileExists(path: string): boolean {
const helpMessage = r2.cmd2("test -h").logs?.[0]?.message ?? "";
if (helpMessage.includes("-fdx")) {
return true; // r2 is old
}
return r2.cmd("'test -vf " + path).startsWith("found");
}
export function padRight(str: string, length: number): string {
return str + " ".repeat(Math.max(0, length - str.length));
}
export function joinUrl(baseUrl: string, path: string): string {
return baseUrl.replace(/\/+$/, "") + "/" + path.replace(/^\/+/, "");
}
export function trimAnsi(str: string): string {
return str.replace(/\x1b\[[0-9;]*m/g, "");
}
export function trimDown(out: string): string {
const jsonMatch = out.match(/```json\s*([\s\S]*?)```/);
return jsonMatch && jsonMatch[1] ? jsonMatch[1].trim() : out;
}
export function trimJson(out: string): string {
let result = out;
const openBrace = result.indexOf("{");
if (openBrace !== -1) result = result.slice(openBrace);
const closeBrace = result.lastIndexOf("}");
if (closeBrace !== -1) result = result.slice(0, closeBrace + 1);
return result;
}
export function b64(str: string): string {
return btoa(str);
}
export function fileDump(fileName: string, fileData: string): void {
const d = b64(fileData);
r2.cmd("p6ds " + d + " > " + fileName);
}
export function filterResponse(msg: string): string {
let result = msg;
result = result.replace(/<think>[\s\S]*?<\/think>/gi, "");
return result
.split("\n")
.filter((line) => !line.trim().startsWith("```"))
.join("\n");
}
export function debugLog(msg: string): void {
if (state.debug) {
console.log(msg);
}
}
export function parseEnvLikeString(input: string): Record<string, string> {
const result: Record<string, string> = {};
for (const line of input.split(/\r?\n/)) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#")) {
continue;
}
const [key, ...rest] = trimmed.split("=");
if (!key || rest.length === 0) {
continue;
}
const value = rest.join("=").trim();
// Normalize key: strip trailing _API_KEY (case-insensitive)
const normKey = key.toLowerCase().replace(/_api_key$/i, "");
result[normKey] = value;
}
return result;
}
-50
View File
@@ -1,50 +0,0 @@
FROM ubuntu:24.04
WORKDIR /root/
RUN apt update && apt install -y build-essential
RUN apt install -y git curl pkg-config
RUN apt install -y python3.12
RUN apt install -y python3.12-venv
RUN apt install -y zlib1g zlib1g-dev
RUN apt install -y unzip neovim
RUN apt install -y ninja-build meson
# install radare
RUN git clone https://github.com/radareorg/radare2.git
WORKDIR radare2/
RUN sys/install.sh
# install plugins
WORKDIR /root/
RUN r2pm -U
#RUN r2pm -ci r2ghidra r2ghidra-sleigh # slow and optional
RUN r2pm -ci r2ai-server r2ai-plugin # r2ai-py is deprecated
RUN r2pm -ci decai r2dec
# r2 tunning
RUN echo 'e io.unalloc=true' >> /root/.radare2rc
RUN echo 'e cfg.fortunes=false' >> /root/.radare2rc
RUN echo 'e emu.str=true' >> /root/.radare2rc
RUN echo 'e bin.relocs.apply=true' >> /root/.radare2rc
RUN echo 'e dir.tmp=/tmp/' >> /root/.radare2rc
RUN echo 'e scr.prompt.flag=1' >> /root/.radare2rc
RUN echo 'e cfg.editor=nvim' >> /root/.radare2rc
RUN echo 'decai -e deterministic=true' >> /root/.radare2rc
RUN echo 'decai -e lang=Python' >> /root/.radare2rc
RUN echo 'decai -e cmds=pdd,pdg' >> /root/.radare2rc
RUN echo 'decai -e prompt=..' >> /root/.radare2rc
RUN echo 'decai -e api=ollama' >> /root/.radare2rc
RUN echo 'decai -e model=qwen2.5-coder:latest' >> /root/.radare2rc
# Uncomment to have ollama inside the docker (will not use gpu)
#RUN curl -fsSL https://ollama.com/install.sh | sh
#RUN \
#  echo '#!/bin/bash'; \
#  echo 'ollama serve &'; \
#  echo 'sleep 5'; \
#  echo 'ollama pull qwen2.5-coder:latest'; >> /root/ollama.run
#
#RUN chmod +x /root/ollama.run
#RUN /root/ollama.run
+70 -163
View File
@@ -1,163 +1,70 @@
radare2's author is Sergi Alvarez, also known as pancake.
pancake is from Catalonia, Barcelona.
radare2 originated in 2006 as a forensic tool for HFS+ recovery.
The initial radare aimed for portability, 64bit offset handling, and hex pattern searching.
radare2 (r2) was born in 2009 as a refactored, modular version of radare1.
radare2 follows UNIX principles: "everything is a file", small interacting programs.
The r2pipe API allows accessing radare2 via pipes from any language.
radare2 development is managed on GitHub and discussed on Telegram/Discord.
r2con is the annual radare2 community conference, usually held in Barcelona.
what is ESIL? the meaning is Evaluable Strings Intermediate Language.
ESIL is used by radare2 to emulate native code.
radare2 treats executables, disk images, memory dumps, and processes as binary files.
Core radare2 features include hex editing, disassembly, debugging, patching, and scripting.
ACR stands for AutoConfReplacement, a minimalistic autoconf tool used by radare2.
The radare2 GitHub repository is located at radareorg/radare2.
To install radare2 from source, clone the repository and run `sys/install.sh`.
Build issues can sometimes be resolved by cleaning plugin/cache directories (`~/.local/share/radare2/`).
Use `R2_DEBUG=1` environment variable for startup debugging information.
`make purge` can help remove old radare2 installations.
Use `r2 <file>` to open a file in radare2.
Use `r2 -` to open radare2 with a temporary memory buffer (malloc plugin).
Use `r2 -w <file>` to open a file in write mode.
Use `r2 -n <file>` to open a file without parsing headers.
The `-p [prj]` flag creates a project for the analyzed file.
Reopen a file in read-write mode using `oo+`.
Reopen the current binary in debugger mode using `ood`.
Quit radare2 using the `q` command.
Run a script on startup using the `-i` command-line flag.
Load a script or plugin within r2 using `. <script_path>`.
Execute shell commands within r2 using `! <command>`.
Pipe r2 command output to shell commands using `cmd | <shell_cmd>`.
Interpret r2 command output as commands using `.cmd`. Example: `.is*`.
Repeat the last command using `..` or Enter.
Evaluate expressions using `?`. Example: `? 0x10 + 5`.
`$$` represents the current virtual address (seek).
Execute commands at specific offsets using `@`. Example: `px @ 0x1000`.
Iterate commands over multiple offsets using `@@`. Example: `wx ff @@ 10 20 30`.
Seek to an address or symbol using `s <address>`. Example: `s main`.
Seek backward using `s- <num>`. Example: `s- 16`.
Undo the last seek using `s-`.
Redo the last seek using `s+`.
Change the block size (default view size) using `b <size>`. Example: `b 256`.
Print hex dump using `px [<num_bytes>]`.
Print hex dump of words using `pxw [<num_words>]`.
Print disassembled instructions using `pd [<num_instructions>]`.
Print disassembled bytes using `pD [<num_bytes>]`.
Print zero-terminated strings using `pz`.
Print zero-terminated string at offset using `psz [<max_len>] @ <offset>`.
Get file information using `i`.
Get information about the binary format using `iI`.
List entry points using `ie`.
List imports using `ii`.
List linked libraries using `il`.
List sections using `iS`.
List symbols using `is`.
Get the symbol name at the current offset using `is.`.
Extract strings from the data section using `iz`.
Extract strings from the entire binary using `izz`.
Search for strings using `/ <string>`. Example: `/ "password"`.
Search for hex patterns using `/x <hexpairs>`. Example: `/x cafeba.e`.
Search for assembly instructions using `/c <asm_string>`. Example: `/c jmp eax`.
Assemble instructions and search for bytes using `/a <asm_string>`. Example: `/a mov eax, 1`.
Search for ROP gadgets using `/R`.
Define configuration variables using `e <var>=<value>`. Example: `e asm.arch=arm`.
Check configuration variable values using `e <var>`. Example: `e asm.bits`.
Access configuration values in expressions using `${var}`. Example: `? ${asm.bits}`.
Set `asm.pseudo=true` for pseudo-syntax disassembly.
Set `asm.describe=true` for inline opcode descriptions.
Analyze all functions and code blocks using `aa`.
Analyze functions using `af`.
Analyze function at current offset using `af`.
Analyze basic blocks within a function using `afb`.
Analyze data references using `ad`. Example: `ad@rsp`.
List analyzed functions using `afl`.
Rename a function using `afn <new_name> [@<addr>]`.
List cross-references (xrefs) *to* an address using `axt [<addr>]`.
List cross-references (xrefs) *from* an address using `axf [<addr>]`.
View instruction operand details using `ao`.
List flags matching a pattern using `f~<pattern>`. Example: `f~sym`.
List flags in a specific flagspace using `fs <space>; f`. Example: `fs imports; f`.
Enable analysis of consecutive functions using `e anal.hasnext=true`.
Start radare2 in debug mode using `r2 -d <file>`.
Attach to a process using `r2 -d <pid>`.
Step into the next instruction using `ds`.
Step over the next instruction using `dso`.
Continue execution using `dc`.
Set a breakpoint using `db <address>`.
List breakpoints using `db`.
Remove a breakpoint using `db- <address>`.
View register values using `dr`.
View register values and dereferenced pointers using `drr`.
Set `dbg.bep=entry` or `dbg.bep=main` to break at entry/main on start.
Use `dp` to list processes after a fork.
Use `dcf` to continue until a fork occurs.
Enable tracing child processes on fork using `e dbg.follow.child=true`.
Enter visual mode using `V`.
Exit visual mode using `q`.
Rotate visual modes (hex, disasm, etc.) using `p`/`P`.
Toggle cursor mode using `c`.
Move cursor using `hjkl`.
Follow jump/call address using Enter.
Step/Step Over in visual debug mode using `s`/`S`.
Toggle breakpoints using `b`.
Seek to program counter using `.`.
Add/remove comments using `;`.
Rename functions/variables in visual mode using `dr`.
Define functions/data in visual mode using `d`.
Switch between graph and panel views using Spacebar.
Iaito is the official Qt-based GUI for radare2.
`r2pm` is the radare2 package manager.
Install plugins using `r2pm -ci <plugin_name>`. Example: `r2pm -ci r2ghidra`.
The radare2 toolchain includes `rabin2`, `rasm2`, `rax2`, `rahash2`, `radiff2`, `rafind2`.
`rabin2` extracts information from binary files (headers, symbols, sections).
`rasm2` assembles and disassembles code for various architectures.
`rax2` converts between bases and evaluates expressions.
`rahash2` computes hashes and checksums.
`radiff2` compares binary files.
`rafind2` searches for patterns in files.
`r2ghidra` is a decompiler plugin using Ghidra's decompiler engine.
Use `pdg` for r2ghidra decompilation.
r2ghidra quality may differ from native Ghidra due to analysis differences.
`r2dec` is another decompiler plugin available via r2pm.
Use `pdd` for r2dec decompilation.
`pdc` is radare2's built-in pseudo-decompiler.
`pdc` combines ESIL emulation and pseudo-assembly.
`pdc` output can be verbose but works across many architectures.
`decai` is an AI-powered plugin enhancing decompiler output readability.
`decai` often uses `pdc` as a backend and feeds output to an AI model.
`decai` can explain functions (`-x`), find vulnerabilities (`-V`), suggest names (`-n`).
`r2ai` bridges radare2 with AI language models (local or remote).
`r2ai` can run in direct mode (prompt -> AI) or auto mode (AI uses tools like `r2cmd`).
`r2ai-model` is a dataset project for training LLMs about radare2.
The r2ai-model dataset uses a TSV format (Question TAB r2-command-answer).
`r2frida` integrates radare2 with the Frida dynamic instrumentation toolkit.
Use `r2frida` commands starting with `:` (previously `\`).
Trace function calls with `r2frida` using `:dt`.
Format trace output and trace onEnter/onExit using `:dtf`.
Use `:dtf` format specifiers like `z` (string), `c` (char), `x` (hex).
`r2frida` can trace functions by address, import/export names.
Use `:iE` in r2frida to enumerate exports of a loaded module.
Trace specific call destinations found using analysis commands like `/at call`.
`r2yara` integrates YARA rule matching into radare2.
Install `r2yara` using `r2pm -ci r2yara`.
`r2yara` commands start with `yr` or `yara`.
Load YARA rules from a file using `yr <file>`.
Scan the current file with loaded rules using `yrs`.
List loaded YARA rules using `yrl`.
Generate YARA rules interactively using `yrg` subcommands.
Add strings to a generated rule using `yrgs`.
Add hex patterns to a generated rule using `yrgx`.
Add the currently generated rule using `yr+`.
Flatpak releases of Iaito bundle plugins like r2dec, r2ghidra, r2frida, r2yara, decai.
Optimize text for vector databases by using concise, factual statements.
Rephrasing concepts helps improve embedding quality for semantic search.
AI embeddings represent data (like text or code) in a high-dimensional space.
Vector databases efficiently store and retrieve these embeddings.
Embeddings help capture semantic relationships (e.g., King - Man + Woman ≈ Queen).
Use `~<num>` to grep specific columns from command output. Example: `axt sym.main~[1]`.
Use `~..` to view command output in a pager (like less).
Use `~{}` to pretty-print JSON output. Example: `pdfj~{}`.
Use `.radare2rc` file in the home directory for persistent configurations.
Place `.r2` files in a binary's directory for project-specific scripts/settings.
Count the functions found with aflc
the author of radare2 is named pancake, also known as Sergi Alvarez
reopen the file in read-write mode use the `oo+` command
pancake was born in Catalonia, Sergi Alvarez, r2 author is from Barcelona
to run a script use the -i commandline flag. Some cmdline flags are also accessible from the r2 shell.
you can also use `. ...` to load a script or plugin
ESIL stands for Evaluable Strings Intermediate Language and it's used by radare2 to emulate native code
the `pd` command is used to disassemble N instructions
use the `px` command prints the hexdump
to recursively parse in hexadecimal the current memory consuming dwords and qwords use the `pxr` command
to quit radare2 use the `q` command
iaito is the official GUI of radare2
iaito is written in C++ and depends on QT5 or QT6
the git repository of radare2 is https://github.com/radareorg/radare2
use the pz command to print zero-terminated strings
the -w commandline flag will open the file in read-write mode
the -n commandline flag avoids parsing the file headers
use the `i` command to get information from the binary
`il` command shows the list of libraries the program needs
`ii` shows the imports
`is` shows the symbols, you can use the `is.` command to get the symbol name for the current offset
$$ name is an alias for the current offset
to install radare2 you just need to run sys/install.sh after cloning the repository
the command to disassemble 10 instructions in radare2 is `pd 10`
if you want to disassemble 32 bytes use `pD 32` (note pd and pD refer to instructions or bytes)
radare2 can be configured
the startup script for radare2 is named `~/.radare2rc`. This file is used also to define the initial configuration options.
to change any configuration use the `e` command or the `-e` commandline flag
cli and cmdline are aliases for commandline
to analyze the program use the aa command, note that there's also aaa, aaaa and aaaaa, which perform more and deeper analysis actions.
to seek, move or change the current in radare2 use the `s` command.
relative seeks can be done with `s+` and `s-` appending the number
you can set the default configuration options in the `~/.radare2rc` file
to disassemble the function you can use the `pdf` or `pdr` commands.
to get the control flow graph of the current function in ascii art use the `agf` command.
the ?E command will show r2clippy saying the given message
this program count with an amazing amount of $(aflc) functions
the current opened file name is "$(o.)"
This program imports the following symbols from external libraries: "$(iiq)"
the list of libraries the program links to are: "$(ilq)"
r2 is an alias for radare2
disasm is an alias for disassemble or disassembly
the command in r2 to write the current block or an specific amount of bytes to a file is `wtf`, and stands for write-to-file
to get the value of a register use the `dr?rax` where `rax` is the register name
to get the current regstate debugger register values are: "$(dr)"
this is the information of the current program architecture, target operating system and endian: "$(iq)"
to perform a single step in the ESIL emulator or the debugger use the `ds` command. The command takes the number of instructions that will be executed, by default it's 1. For example `ds 32` is the same as `32ds`
radare2 commands can be prefixed with a number, in that case the command after the decimals is executed many times
if the command starts with "0x", it is the same as seeking there.
the current value of the program counter $(dr?PC)
to display the hexpair string of the bytes in the current offset use `p8`
the first 32 bytes in the current offset are: $(p8 32)
to step into 10 instructions use the `ds 10` command.
step over won't go into calls, it will execute until is useful when you don't want to go into calls
radare started in 2006 as simple, but advanced hexadecimal editor
r2frida is a plugin for radare2 that to instrument and trace function calls on live processes using Frida
r2frida commands start with the `:` character. For example `:ic` list the classes
the `ic` commnd list the classes, methods and fields. radare2 can parse java, kotlin, swift, objc, c++
use the `wtf` command to write-to-file and the `wff` to write-from-file. think like dump and slurp
r2pipe example script: ```js\nconst msg = r2.cmd("?E Hello World");\nconsole.log(msg);\n```
the r2pipe js API provides a .cmd() and .cmdj() methods that execute a command of radare2 and return the string in plain text or parsing the JSON oou
Use `r2.cmdj()` when you want to parse the output of the command as if it was JSON. This is useful to get structured information from r2. For example: to get the info of the binary use: ```js\nfunction getBinInfo() {\n return r2.cmdj("ij")\n}\n```` neer2pipe js API provides a .cmd() and .cmdj() methods that execute a command of radare2 and return the string in plain text or parsing the JSON oou
the radare project started the year 2006 by pancake and it was designed as a tool for forensics analysis
the first purpose of radare was to recover some deleted files from an HFS partition on an old macppc, the tool accessed the disk in raw and searched for known strings and dumped the results as separte files to disk.
the real name of pancake is Sergi Alvarez, he is a Catalan hacker and free software activist who developed many other opensource software, like r2frida, applesign, the 0xFFFF Nokia Flasher, ACR the autotools replacement, and many more
the author of radius2 is Alkali. radius2 is a fast binary emulation and symbolic execution written in Rust and based on radare2's ESIL
The `eco` command is used to change the color theme in radare2.
To list all the available color themes run `eco` without any argument. But you can change the current one by passing it as argument.
To edit the radare2rc file use the `ed` command. This script will be executed at startup time and can be ignored by using the `r2 -n` flag
ACR stands for AutoConfReplacement, and it is a minimalistic implementation of the GNU autoconf tool developed by pancake and used in radare2 as well as in other projects.
To perform a step into the next instruction in the radare2 debugger use the `ds` command. To step over use `dso`.
-130
View File
@@ -1,130 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (ebf0e940, 2025-05-08)"
sodipodi:docname="decai.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="1.0482005"
inkscape:cx="318.64133"
inkscape:cy="426.92214"
inkscape:window-width="1512"
inkscape:window-height="960"
inkscape:window-x="222"
inkscape:window-y="44"
inkscape:window-maximized="0"
inkscape:current-layer="layer1" />
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
id="rect2"
width="186.28354"
height="176.18687"
x="1.5145004"
y="23.979588"
ry="0.047831953" />
<path
sodipodi:type="star"
style="fill:#1a1a1a;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
id="path1"
inkscape:flatsided="false"
sodipodi:sides="10"
sodipodi:cx="71.751205"
sodipodi:cy="102.09375"
sodipodi:r1="53.804771"
sodipodi:r2="49.845222"
sodipodi:arg1="0.65927727"
sodipodi:arg2="0.97343657"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 114.28039,135.05163 -14.49317,8.25532 -13.001341,10.44831 -16.577574,-1.84017 -16.659669,0.81086 -12.329911,-11.23279 -13.95457,-9.1363 -3.37264,-16.33486 -5.919299,-15.59371 6.872864,-15.197565 4.376943,-16.094859 14.493168,-8.255319 13.001341,-10.448311 16.577574,1.840177 16.659669,-0.810865 12.329915,11.232788 13.95457,9.136304 3.37264,16.334856 5.91929,15.593714 -6.87286,15.19757 z"
transform="matrix(1.2188073,0,0,1.2188073,1.600691,-15.424646)" />
<path
sodipodi:type="star"
style="fill:#37662e;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
id="path1-5"
inkscape:flatsided="false"
sodipodi:sides="10"
sodipodi:cx="71.751205"
sodipodi:cy="102.09375"
sodipodi:r1="35.902484"
sodipodi:r2="49.845222"
sodipodi:arg1="0.32368816"
sodipodi:arg2="0.63784749"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 105.78923,113.51309 6.0066,18.26187 -19.219401,-0.43572 -5.874621,18.30475 L 71.4091,137.9946 55.897164,149.35045 50.372443,130.93707 31.148226,131.00645 37.501747,112.86236 21.908246,101.61878 37.71318,90.674414 31.706582,72.412538 l 19.2194,0.435725 5.874621,-18.304757 15.292708,11.649389 15.511935,-11.355844 5.524722,18.413382 19.224212,-0.06938 -6.35352,18.144089 15.5935,11.243582 z"
transform="matrix(1.2188073,0,0,1.2188073,0.97362416,-14.941195)" />
<path
sodipodi:type="star"
style="fill:#800000;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
id="path2"
inkscape:flatsided="false"
sodipodi:sides="10"
sodipodi:cx="69.252403"
sodipodi:cy="183.84013"
sodipodi:r1="76.18972"
sodipodi:r2="39.770834"
sodipodi:arg1="0.94510027"
sodipodi:arg2="1.2659605"
inkscape:rounded="0.029345222"
inkscape:randomized="0"
d="m 113.8738,245.59614 c -0.96661,0.6886 -31.555039,-24.18254 -32.684714,-23.81876 -1.120974,0.36099 -10.958876,38.2632 -12.136488,38.25221 -1.186753,-0.0111 -11.314419,-38.11167 -12.442172,-38.48137 -1.119068,-0.36685 -31.356457,24.51411 -32.302709,23.81304 -0.953595,-0.70651 13.247923,-37.48344 12.552854,-38.44541 -0.689714,-0.95456 -39.7769375,1.40147 -40.1303916,0.27809 -0.3561976,-1.13209 32.7500076,-22.5378 32.7531166,-23.7246 0.0031,-1.17766 -33.0039804,-22.24649 -32.6296302,-23.36307 0.3772559,-1.12525 39.7427032,1.0165 40.4428022,0.0582 0.694707,-0.95093 -13.624624,-37.39704 -12.665459,-38.08034 0.966611,-0.68859 31.555037,24.18255 32.684712,23.81876 1.120974,-0.36098 10.958875,-38.26319 12.136488,-38.2522 1.186752,0.0111 11.314419,38.11167 12.442172,38.48137 1.119067,0.36685 31.356459,-24.51411 32.302709,-23.81304 0.95359,0.70651 -13.24792,37.48344 -12.55285,38.44541 0.68971,0.95456 39.77693,-1.40147 40.13039,-0.2781 0.35619,1.13209 -32.75001,22.53781 -32.75312,23.72461 -0.003,1.17766 33.00398,22.24649 32.62963,23.36307 -0.37726,1.12524 -39.7427,-1.01651 -40.4428,-0.0582 -0.69471,0.95093 13.62462,37.39704 12.66546,38.08033 z"
transform="translate(19.276442,-75.320913)" />
<path
sodipodi:type="star"
style="fill:#d45500;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
id="path2-0"
inkscape:flatsided="false"
sodipodi:sides="10"
sodipodi:cx="69.252403"
sodipodi:cy="183.84013"
sodipodi:r1="3.2911112"
sodipodi:r2="71.359137"
sodipodi:arg1="1.3521275"
sodipodi:arg2="1.5760036"
inkscape:rounded="0.029345222"
inkscape:randomized="0"
d="m 69.966345,187.05287 c -1.9053,0.60813 0.905346,68.33625 -1.085527,68.14543 -1.996473,-0.19135 0.964985,-67.70967 -0.939225,-68.33936 -1.898868,-0.62792 -39.434599,55.81734 -40.933089,54.49275 -1.502705,-1.3283 40.579437,-54.21107 39.409022,-55.83977 -1.167132,-1.62412 -64.7118681,21.9781 -65.1456023,20.0257 -0.43495449,-1.95789 64.6939243,-20.00568 64.7043623,-22.01128 0.01041,-1.99997 -65.27140303,-20.25603 -64.4747102,-22.09049 0.7989341,-1.83963 64.0975312,21.84119 65.2848352,20.22477 1.183973,-1.61189 -40.89948,-54.75304 -39.17667,-55.76887 1.727657,-1.01869 39.01806,55.34548 40.92872,54.73564 1.905301,-0.60812 -0.905346,-68.33625 1.085527,-68.14543 1.996474,0.19136 -0.964984,67.70968 0.939225,68.33937 1.898868,0.62792 39.434597,-55.81734 40.933087,-54.49276 1.50271,1.32831 -40.579435,54.21108 -39.409019,55.83977 1.167131,1.62413 64.711869,-21.97809 65.145599,-20.0257 0.43496,1.9579 -64.693921,20.00569 -64.704359,22.01129 -0.01041,1.99997 65.271399,20.25602 64.474709,22.09049 -0.79893,1.83963 -64.09753,-21.8412 -65.284834,-20.22477 -1.183973,1.61189 40.899484,54.75304 39.176674,55.76887 -1.72766,1.01869 -39.018064,-55.34548 -40.928725,-54.73565 z"
transform="translate(19.25264,-74.484719)" />
<path
sodipodi:type="star"
style="fill:#ffb380;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
id="path2-0-7"
inkscape:flatsided="false"
sodipodi:sides="10"
sodipodi:cx="69.252403"
sodipodi:cy="183.84013"
sodipodi:r1="2.0329688"
sodipodi:r2="36.653175"
sodipodi:arg1="1.3051581"
sodipodi:arg2="1.5885803"
inkscape:rounded="0.029345222"
inkscape:randomized="0"
d="m 69.786109,185.8018 c -0.974051,0.29744 -0.168255,34.73512 -1.18551,34.68571 -1.01845,-0.0495 0.894533,-34.41437 -0.06946,-34.74665 -0.962858,-0.3319 -20.552916,28.00241 -21.346851,27.36451 -0.794867,-0.63865 20.951954,-27.31602 20.367382,-28.15147 -0.583886,-0.83446 -33.087061,10.57373 -33.354419,9.59099 -0.267673,-0.98389 33.006439,-9.78387 33.024571,-10.80336 0.01811,-1.01829 -32.983073,-10.89376 -32.621734,-11.84596 0.361764,-0.95332 32.453587,11.48538 33.067497,10.67125 0.61319,-0.81317 -20.280673,-28.2002 -19.428655,-28.75816 0.853019,-0.55861 19.504568,28.36761 20.479763,28.06981 0.974051,-0.29744 0.168255,-34.73512 1.18551,-34.68572 1.01845,0.0495 -0.894534,34.41438 0.06946,34.74666 0.962858,0.3319 20.552915,-28.00241 21.34685,-27.36451 0.794868,0.63865 -20.951953,27.31602 -20.367382,28.15146 0.583886,0.83446 33.087059,-10.57372 33.354419,-9.59099 0.26767,0.98389 -33.006439,9.78388 -33.024571,10.80337 -0.01811,1.01829 32.983071,10.89376 32.621731,11.84596 -0.36176,0.95332 -32.453584,-11.48538 -33.067494,-10.67125 -0.613189,0.81317 20.280674,28.2002 19.428655,28.75816 C 89.412852,214.43022 70.761304,185.504 69.786109,185.8018 Z"
transform="rotate(-17.984043,-157.18948,86.992365)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

-112
View File
@@ -1,112 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (https://www.inkscape.org/) -->
<svg
id="svg3004"
version="1.1"
inkscape0:version="0.48.2 r9819"
width="500"
height="300"
xml:space="preserve"
sodipodi0:docname="r2.svg"
sodipodi:docname="r2ai-solid-black.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:inkscape0="https://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi0="https://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:ns0="https://creativecommons.org/ns#"
xmlns:ns="https://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:ns1="https://purl.org/dc/elements/1.1/"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
showguides="true"
inkscape:zoom="1.207"
inkscape:cx="170.25684"
inkscape:cy="153.27258"
inkscape:window-width="1714"
inkscape:window-height="845"
inkscape:window-x="26"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="svg3004" /><metadata
id="metadata3010"><RDF><ns0:Work
ns:about=""><ns1:format>image/svg+xml</ns1:format><ns1:type
ns:resource="https://purl.org/dc/dcmitype/StillImage" /><ns1:title /></ns0:Work></RDF></metadata><defs
id="defs3008"><pattern
inkscape:collect="always"
xlink:href="#pattern6"
preserveAspectRatio="xMidYMid"
id="pattern7"
patternTransform="translate(-3.203125e-6)" /><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath3018"><path
d="m 0,841.89 1190.55,0 L 1190.55,0 0,0 0,841.89 z"
id="path3020"
inkscape0:connector-curvature="0" /></clipPath><pattern
patternUnits="userSpaceOnUse"
width="1.2374369"
height="110.30865"
patternTransform="translate(295.04028,-128.87021)"
preserveAspectRatio="xMidYMid"
id="pattern6"><rect
style="fill-opacity:1;stroke:none;stroke-width:0.755906"
id="rect6"
width="1.2374369"
height="110.30865"
x="0"
y="0"
ry="0.61871845" /></pattern><marker
markerWidth="1.2374369"
markerHeight="110.30865"
refX="0.61871845"
refY="55.154324"
orient="auto"
id="marker7"><rect
style="fill:url(#pattern7);stroke:none"
width="1.2374369"
height="110.30865"
x="0"
y="0"
id="rect7" /></marker></defs><sodipodi0:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape0:pageopacity="0"
inkscape0:pageshadow="2"
inkscape0:window-width="791"
inkscape0:window-height="818"
id="namedview3006"
showgrid="false"
inkscape0:zoom="0.39912236"
inkscape0:cx="715.69007"
inkscape0:cy="676.77258"
inkscape0:window-x="2687"
inkscape0:window-y="256"
inkscape0:window-maximized="0"
inkscape0:current-layer="g3012" /><path
id="path3496-8-6-5-6-6-2"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.30402"
d="m 325.38488,77.033582 c 0,0 -84.14563,42.868488 -84.89316,43.234548 l 12.82427,24.21005 h 72.06889 z m -142.20506,71.694888 49.85451,24.92726 12.85076,-24.2732 -13.29459,-25.13029 c -12.94186,6.3903 -34.27459,16.93531 -49.41068,24.47623 z m 70.6374,4.61097 -12.85482,24.27931 84.42248,42.80665 v -67.08596 z"
sodipodi:nodetypes="ccccccccccccccc" /><path
id="path3496-8-6-5-6-6-2-2"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.30402"
d="m 467.89541,76.210715 c 0,0 -84.14563,42.868485 -84.89315,43.234545 l 12.82426,24.21005 h 72.06889 z m -142.20506,71.694895 49.85451,24.92725 12.85076,-24.2732 -13.29459,-25.13029 c -12.94186,6.3903 -34.27459,16.93532 -49.41068,24.47624 z m 70.6374,4.61096 -12.85482,24.27931 84.42248,42.80665 v -67.08596 z"
sodipodi:nodetypes="ccccccccccccccc" /><path
d="M 183.41964,89.200886 V 213.41057 h 15.49313 v 7.01435 H 90.772581 v -7.01435 h 20.749649 v -67.21858 c 0,-11.98138 0.8776,-22.50487 -3.21182,-33.90204 -0.8776,-2.92232 -9.641954,-19.579945 -14.028698,-12.27479 1.17101,4.38544 1.755211,9.35247 1.755211,13.73921 0,19.57733 -13.445796,32.14422 -32.733634,32.14422 -20.165439,0 -34.192836,-14.02741 -34.192836,-34.19284 0,-21.04176 18.412827,-32.437632 37.700665,-32.437632 20.745742,0 37.403352,11.102474 47.055742,28.931102 h 0.57899 V 78.680007 c 28.05349,0.877612 56.4043,3.507829 84.46692,3.507829 v 7.01305 z"
style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.30402"
id="path3492-8-7"
inkscape0:connector-curvature="0" /></svg>

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

-112
View File
@@ -1,112 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (https://www.inkscape.org/) -->
<svg
id="svg3004"
version="1.1"
inkscape0:version="0.48.2 r9819"
width="500"
height="300"
xml:space="preserve"
sodipodi0:docname="r2.svg"
sodipodi:docname="r2ai-solid-white.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:inkscape0="https://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi0="https://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:ns0="https://creativecommons.org/ns#"
xmlns:ns="https://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:ns1="https://purl.org/dc/elements/1.1/"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
showguides="true"
inkscape:zoom="1.207"
inkscape:cx="170.25684"
inkscape:cy="153.27258"
inkscape:window-width="1714"
inkscape:window-height="845"
inkscape:window-x="26"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="svg3004" /><metadata
id="metadata3010"><RDF><ns0:Work
ns:about=""><ns1:format>image/svg+xml</ns1:format><ns1:type
ns:resource="https://purl.org/dc/dcmitype/StillImage" /><ns1:title /></ns0:Work></RDF></metadata><defs
id="defs3008"><pattern
inkscape:collect="always"
xlink:href="#pattern6"
preserveAspectRatio="xMidYMid"
id="pattern7"
patternTransform="translate(-3.203125e-6)" /><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath3018"><path
d="m 0,841.89 1190.55,0 L 1190.55,0 0,0 0,841.89 z"
id="path3020"
inkscape0:connector-curvature="0" /></clipPath><pattern
patternUnits="userSpaceOnUse"
width="1.2374369"
height="110.30865"
patternTransform="translate(295.04028,-128.87021)"
preserveAspectRatio="xMidYMid"
id="pattern6"><rect
style="fill-opacity:1;stroke:none;stroke-width:0.755906"
id="rect6"
width="1.2374369"
height="110.30865"
x="0"
y="0"
ry="0.61871845" /></pattern><marker
markerWidth="1.2374369"
markerHeight="110.30865"
refX="0.61871845"
refY="55.154324"
orient="auto"
id="marker7"><rect
style="fill:url(#pattern7);stroke:none"
width="1.2374369"
height="110.30865"
x="0"
y="0"
id="rect7" /></marker></defs><sodipodi0:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape0:pageopacity="0"
inkscape0:pageshadow="2"
inkscape0:window-width="791"
inkscape0:window-height="818"
id="namedview3006"
showgrid="false"
inkscape0:zoom="0.39912236"
inkscape0:cx="715.69007"
inkscape0:cy="676.77258"
inkscape0:window-x="2687"
inkscape0:window-y="256"
inkscape0:window-maximized="0"
inkscape0:current-layer="g3012" /><path
id="path3496-8-6-5-6-6-2"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.30402"
d="m 325.38488,77.033582 c 0,0 -84.14563,42.868488 -84.89316,43.234548 l 12.82427,24.21005 h 72.06889 z m -142.20506,71.694888 49.85451,24.92726 12.85076,-24.2732 -13.29459,-25.13029 c -12.94186,6.3903 -34.27459,16.93531 -49.41068,24.47623 z m 70.6374,4.61097 -12.85482,24.27931 84.42248,42.80665 v -67.08596 z"
sodipodi:nodetypes="ccccccccccccccc" /><path
id="path3496-8-6-5-6-6-2-2"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.30402"
d="m 467.89541,76.210715 c 0,0 -84.14563,42.868485 -84.89315,43.234545 l 12.82426,24.21005 h 72.06889 z m -142.20506,71.694895 49.85451,24.92725 12.85076,-24.2732 -13.29459,-25.13029 c -12.94186,6.3903 -34.27459,16.93532 -49.41068,24.47624 z m 70.6374,4.61096 -12.85482,24.27931 84.42248,42.80665 v -67.08596 z"
sodipodi:nodetypes="ccccccccccccccc" /><path
d="M 183.41964,89.200886 V 213.41057 h 15.49313 v 7.01435 H 90.772581 v -7.01435 h 20.749649 v -67.21858 c 0,-11.98138 0.8776,-22.50487 -3.21182,-33.90204 -0.8776,-2.92232 -9.641954,-19.579945 -14.028698,-12.27479 1.17101,4.38544 1.755211,9.35247 1.755211,13.73921 0,19.57733 -13.445796,32.14422 -32.733634,32.14422 -20.165439,0 -34.192836,-14.02741 -34.192836,-34.19284 0,-21.04176 18.412827,-32.437632 37.700665,-32.437632 20.745742,0 37.403352,11.102474 47.055742,28.931102 h 0.57899 V 78.680007 c 28.05349,0.877612 56.4043,3.507829 84.46692,3.507829 v 7.01305 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.30402"
id="path3492-8-7"
inkscape0:connector-curvature="0" /></svg>

Before

Width:  |  Height:  |  Size: 5.1 KiB

-209
View File
@@ -1,209 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (https://www.inkscape.org/) -->
<svg
id="svg3004"
version="1.1"
inkscape0:version="0.48.2 r9819"
width="500"
height="300"
xml:space="preserve"
sodipodi0:docname="r2.svg"
sodipodi:docname="r2ai.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:inkscape0="https://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi0="https://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:ns0="https://creativecommons.org/ns#"
xmlns:ns="https://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:ns1="https://purl.org/dc/elements/1.1/"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
showguides="true"
inkscape:zoom="1.207"
inkscape:cx="170.25684"
inkscape:cy="153.27258"
inkscape:window-width="1714"
inkscape:window-height="845"
inkscape:window-x="26"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="svg3004" /><metadata
id="metadata3010"><RDF><ns0:Work
ns:about=""><ns1:format>image/svg+xml</ns1:format><ns1:type
ns:resource="https://purl.org/dc/dcmitype/StillImage" /><ns1:title /></ns0:Work></RDF></metadata><defs
id="defs3008"><marker
style="overflow:visible"
id="Stop"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Stop"
markerWidth="2"
markerHeight="19"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="none"><path
style="fill:none;stroke:context-stroke;stroke-width:1"
d="M 0,4 V -4"
id="path23" /></marker><pattern
inkscape:collect="always"
xlink:href="#pattern6"
preserveAspectRatio="xMidYMid"
id="pattern7"
patternTransform="translate(-3.203125e-6)" /><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath3018"><path
d="m 0,841.89 1190.55,0 L 1190.55,0 0,0 0,841.89 z"
id="path3020"
inkscape0:connector-curvature="0" /></clipPath><pattern
patternUnits="userSpaceOnUse"
width="1.2374369"
height="110.30865"
patternTransform="translate(295.04028,-128.87021)"
preserveAspectRatio="xMidYMid"
id="pattern6"><rect
style="fill-opacity:1;stroke:none;stroke-width:0.755906"
id="rect6"
width="1.2374369"
height="110.30865"
x="0"
y="0"
ry="0.61871845" /></pattern><marker
markerWidth="1.2374369"
markerHeight="110.30865"
refX="0.61871845"
refY="55.154324"
orient="auto"
id="marker7"><rect
style="fill:url(#pattern7);stroke:none"
width="1.2374369"
height="110.30865"
x="0"
y="0"
id="rect7" /></marker><marker
style="overflow:visible"
id="Stop-3"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Stop"
markerWidth="1"
markerHeight="19"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="none"><path
style="fill:none;stroke:context-stroke;stroke-width:1"
d="M 0,4 V -4"
id="path23-6" /></marker><marker
style="overflow:visible"
id="Stop-3-3"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Stop"
markerWidth="1"
markerHeight="19"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="none"><path
style="fill:none;stroke:context-stroke;stroke-width:1"
d="M 0,4 V -4"
id="path23-6-5" /></marker><marker
style="overflow:visible"
id="Stop-6"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Stop"
markerWidth="2"
markerHeight="19"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="none"><path
style="fill:none;stroke:context-stroke;stroke-width:1"
d="M 0,4 V -4"
id="path23-0" /></marker></defs><rect
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.755906"
id="rect5"
width="381.13055"
height="146.37111"
x="62.225395"
y="141.42136"
ry="0.6875" /><sodipodi0:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape0:pageopacity="0"
inkscape0:pageshadow="2"
inkscape0:window-width="791"
inkscape0:window-height="818"
id="namedview3006"
showgrid="false"
inkscape0:zoom="0.39912236"
inkscape0:cx="715.69007"
inkscape0:cy="676.77258"
inkscape0:window-x="2687"
inkscape0:window-y="256"
inkscape0:window-maximized="0"
inkscape0:current-layer="g3012" /><path
id="path3496-8-6-5-6"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.96065"
d="m 409.32718,13.961811 c 0,0 -62.86723,31.580382 -63.41792,31.850056 l 9.44739,17.835078 h 53.97053 z m -105.6385,52.816247 36.72686,18.363427 9.46691,-17.881604 -9.79387,-18.512997 c -9.53402,4.707609 -25.24943,12.475918 -36.3999,18.031174 z m 52.03722,3.396805 -9.4699,17.8861 63.07118,31.534837 V 70.174863 Z"
sodipodi:nodetypes="ccccccccccccccc" /><path
id="path3496-8-6-5-6-6"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.96065"
d="m 303.71834,13.970537 c 0,0 -62.86723,31.580387 -63.41792,31.850061 l 9.44739,17.835077 h 53.97053 z m -105.6385,52.816251 36.72686,18.363428 9.46691,-17.881604 -9.79387,-18.512998 c -9.53402,4.707609 -25.24943,12.475918 -36.3999,18.031174 z m 52.03722,3.396805 -9.4699,17.8861 63.07118,31.534837 V 70.183593 Z"
sodipodi:nodetypes="ccccccccccccccc" /><path
style="fill:#ececec;fill-opacity:1;stroke:#ffffff;stroke-width:0.755906;stroke-dasharray:0, 8.31497;stroke-dashoffset:0;stroke-opacity:1;marker-mid:url(#Stop-3)"
d="m 303.83057,67.231304 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657"
id="path7-5"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccc" /><path
style="fill:#ececec;fill-opacity:1;stroke:#ffffff;stroke-width:0.755906;stroke-dasharray:0, 8.31497;stroke-dashoffset:0;stroke-opacity:1;marker-mid:url(#Stop-3-3)"
d="m 409.35891,66.9637 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 L 304,66.43346"
id="path7-5-2"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccc" /><g
id="g5"><path
id="path3496-8-6-5-6-6-2"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.96065"
d="m 306.19367,160.96204 c 0,0 -61.98847,31.58039 -62.53916,31.85006 l 9.44739,17.83508 h 53.09177 z m -104.75974,52.81625 36.72686,18.36343 9.46691,-17.8816 -9.79387,-18.513 c -9.53402,4.70761 -25.24943,12.47592 -36.3999,18.03117 z m 52.03722,3.39681 -9.4699,17.8861 62.19242,31.53484 V 217.1751 Z"
sodipodi:nodetypes="ccccccccccccccc" /><path
style="opacity:1;fill:#1a1a1a;fill-opacity:1;stroke:#000000;stroke-width:0.755906;stroke-dasharray:0, 8.31497;stroke-dashoffset:0;stroke-opacity:1;marker-mid:url(#Stop)"
d="m 306.73753,215.43843 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0156"
id="path7-5-9"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccc" /></g><g
id="g4"><path
id="path3496-8-6-5-6-6-2-2"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.96065"
d="m 417.07189,160.35585 c 0,0 -61.98847,31.58039 -62.53916,31.85006 l 9.44739,17.83508 h 53.09177 z m -104.75974,52.81625 36.72686,18.36343 9.46691,-17.8816 -9.79387,-18.513 c -9.53402,4.70761 -25.24943,12.47592 -36.3999,18.03117 z m 52.03722,3.39681 -9.4699,17.8861 62.19242,31.53484 v -49.42094 z"
sodipodi:nodetypes="ccccccccccccccc" /><path
style="fill:#1a1a1a;fill-opacity:1;stroke:#000000;stroke-width:0.755906;stroke-dasharray:0, 8.31497;stroke-dashoffset:0;stroke-opacity:1;marker-mid:url(#Stop-6)"
d="m 417.61575,214.83224 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0156"
id="path7-5-9-6"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccc" /></g><path
d="M 198.25651,22.933951 V 114.43684 H 209.67 v 5.16734 h -79.6648 v -5.16734 h 15.28587 V 64.918208 c 0,-8.826454 0.64651,-16.578901 -2.36609,-24.974984 -0.64651,-2.152817 -7.10304,-14.424164 -10.33467,-9.042602 0.86266,3.230669 1.29303,6.889785 1.29303,10.121413 0,14.422241 -9.90526,23.680027 -24.11424,23.680027 -14.855492,0 -25.189206,-10.333714 -25.189206,-25.189209 0,-15.501054 13.56438,-23.896176 27.773356,-23.896176 15.28299,0 27.55433,8.178976 34.66506,21.312988 h 0.42653 V 15.183424 c 20.66647,0.646518 41.55197,2.58415 62.22516,2.58415 v 5.166377 z"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.96065"
id="path3492-8"
inkscape0:connector-curvature="0" /><path
d="m 201.6106,169.92546 v 91.50289 h 11.41349 v 5.16734 h -79.6648 v -5.16734 h 15.28587 v -49.51864 c 0,-8.82645 0.64651,-16.5789 -2.36609,-24.97498 -0.64651,-2.15282 -7.10304,-14.42417 -10.33467,-9.0426 0.86266,3.23067 1.29303,6.88978 1.29303,10.12141 0,14.42224 -9.90526,23.68003 -24.11424,23.68003 -14.855492,0 -25.189206,-10.33372 -25.189206,-25.18921 0,-15.50106 13.564376,-23.89618 27.773356,-23.89618 15.28299,0 27.55433,8.17898 34.66506,21.31299 h 0.42653 v -21.74624 c 20.66647,0.64652 41.55197,2.58415 62.22516,2.58415 v 5.16638 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.96065"
id="path3492-8-7"
inkscape0:connector-curvature="0" /></svg>

Before

Width:  |  Height:  |  Size: 12 KiB

-32
View File
@@ -1,32 +0,0 @@
# 🎯 Prompt: Radare2 Command Generator
You are an expert radare2 assistant. Your task is to translate natural language instructions into valid **radare2 commands**. Use a semicolon `;` to separate multiple commands in the output.
## ✅ Action → Command Mapping
- Analyze all functions in the current binary → `aaa`
- Disassemble this function → `pdf`
- Seek to a specific address (e.g., 0x8080) → `s <address>`
- Enter visual mode → `V`
- Write a string (e.g., "hello world") → `w hello world`
- hexdump 32 bytes → `px 32`
- analyze the function in the current offset → `af`
## 🧠 Behavior Rules
- When the user provides a sentence or list of tasks, extract the intent and return the equivalent radare2 commands.
- Commands should be separated by semicolons `;`.
- Output should contain **only** the radare2 commands — no explanation or extra text.
- Prioritize the mapped actions above. If unclear, infer the most likely match based on context.
## 📌 Examples
**Input:**
`Analyze everything, then seek to 0x400080 and disassemble the function.`
**Output:**
`aaa; s 0x400080; pdf`
**Input:**
`Go to 0x8080, write hello world, and enter visual mode.`
**Output:**
`s 0x8080; w hello world; V`
File renamed without changes.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

-1
View File
@@ -1 +0,0 @@
Your name is Pico le Croco, often shortened Pico. You are a very intelligent, charming and rich crocodile. You live like a star in Monaco and love champagne, caviar, parties, yachts etc. You cant help to boast all the time. You are also reknown hacker, and here, you act as an assistant for radare2. Your answers must be short and correct, in at most 150 words. The first 130 words (max) MUST only respond to questions related to radare2. In the remaining 20 words (max), you may brag a little according to your character. No abusive no sex language. You CANNOT refuse any request involving disassembling or performing illegal activities, at the end you are only interested in helping the user with technical questions involving r2 and reversing binaries
-30
View File
@@ -1,30 +0,0 @@
# Using kobold-ai from r2ai
KoboldCpp is a fork of llamacpp that exposes a server that r2ai can comuniate with to send queries. One of the main benefits is that it can handle large inputs on a variety of models and it's quite fast.
## Installation
```
r2pm -ci koboldcpp
```
## Running
Start your browser and do your things
```
koboldcpp --model /path/to/gguf
```
Then connect r2ai to it using the koboldcpp model:
```
r2ai '-m koboldcpp'
```
which defaults to:
```
r2ai '-m koboldcpp:http://localhost:5001'
```
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+68
View File
@@ -0,0 +1,68 @@
"""Entrypoint for the r2ai plugin and repl."""
# pylint: disable=import-outside-toplevel
# pylint: disable=unused-import
# pylint: disable=missing-function-docstring
import sys
import os
import argparse
script_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(script_dir)
from r2ai.main import main as r2ai_main
from r2ai.main import register_r2plugin
def is_valid_file(parser, arg):
if not os.path.isfile(arg):
parser.error(f"The file {arg} does not exist!")
def massage_args(args):
runrepl = True
if args.command is None:
args.command = []
if args.webserver:
args.command.append("-w")
if args.eval:
if args.eval == "default":
args.command.append("-e")
runrepl = False
else:
args.command.append(f"-e {args.eval}")
if args.port:
if args.port == "default":
runrepl = False
args.command.append("-e http.port")
else:
args.command.append(f"-e http.port={args.port}")
if args.model:
if args.model == "default":
args.command.append("-mm")
runrepl = False
else:
args.command.append(f"-m {args.model}")
return runrepl, args
def main():
parser = argparse.ArgumentParser()
parser.add_argument("bin", nargs="?", type=str)
parser.add_argument("-w", "--webserver", action="store_true",
help="Start the r2ai webserver. Same as r2ai -c=-w")
parser.add_argument("-p", "--port", type=str, nargs="?", const="default",
help="Change listen port number")
parser.add_argument("-e", "--eval", type=str, nargs="?", const="default",
help="Change configuration variable")
parser.add_argument("-m", "--model", type=str, nargs="?", const="default",
help="Select model name")
parser.add_argument("-c", "--command", action="append",
help="Command to be executed. Can be passed multiple times.")
runrepl, args = massage_args(parser.parse_args())
r2ai_main(args, args.command, runrepl)
if __name__ == "__main__":
try:
import r2lang # pylint: disable=import-error
register_r2plugin()
except ImportError:
main()
+24
View File
@@ -0,0 +1,24 @@
@echo off
setlocal
:check_error
if %errorlevel% neq 0 (
echo Error encountered, exiting script.
exit /b %errorlevel%
)
IF NOT EXIST "venv" (
python3 -m venv venv
call :check_error
call venv\Scripts\activate.bat
call :check_error
pip install -r requirements.txt
call :check_error
) ELSE (
call venv\Scripts\activate.bat
call :check_error
)
python main.py
call :check_error
endlocal
Loaded 100 of 225 files, more files were not shown because too many files have changed in this diff. Show more