mirror of
https://github.com/radareorg/r2ai.git
synced 2026-11-10 04:01:27 -10:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9edd45188e | ||
|
|
66919dc065 |
No files matched your search
@@ -212,3 +212,21 @@ void daemonize() {
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
## WASM HTTP
|
||||
|
||||
With radare2's WASI HTTP build, decai uses `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. Older/native runtimes retain their existing curl path.
|
||||
|
||||
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.
|
||||
+31
-30
@@ -1,7 +1,7 @@
|
||||
📦
|
||||
27147 /main.js
|
||||
27221 /main.js
|
||||
✄
|
||||
var te="1.5.0",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
|
||||
var ie="1.5.2",w="decai",P="~/.config/r2ai",H=P+"/apikeys.txt",T=P+"/decai.txt",D="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.",Y={decai:`# Using Decai
|
||||
|
||||
Decai is the radare2 plugin for decompiling functions with the help of language models.
|
||||
|
||||
@@ -117,40 +117,41 @@ Use radare2 to resolve user requests.
|
||||
### Special cases
|
||||
|
||||
* On Swift binaries run...
|
||||
`};var A={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,think:"",apitype:"chat",useFiles:!1,contextFile:"",model:"",cache:!1,maxInputTokens:-1,prompt:T,lastOutput:""},o={...A,decopipe:{...A.decopipe},extraHeaders:[...A.extraHeaders]};function ne(e){return(r2.cmd("-e dir.tmp").trim()??".")+"/"+e}function K(e,t){r2.cmd("'mkdir -p "+e),r2.cmd("'touch "+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 re(e){let t=e.match(/```json\s*([\s\S]*?)```/);return t&&t[1]?t[1].trim():e}function ie(e){let t=e,n=t.indexOf("{");n!==-1&&(t=t.slice(n));let r=t.lastIndexOf("}");return r!==-1&&(t=t.slice(0,r+1)),t}function $(e){return btoa(e)}function ae(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(`
|
||||
`};var S={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,think:"",apitype:"chat",useFiles:!1,contextFile:"",model:"",cache:!1,maxInputTokens:-1,prompt:D,lastOutput:""},o={...S,decopipe:{...S.decopipe},extraHeaders:[...S.extraHeaders]};function ae(e){return(r2.cmd("-e dir.tmp").trim()??".")+"/"+e}function j(e,t){r2.cmd("'mkdir -p "+e),r2.cmd("'touch "+t)}function K(e){return(r2.cmd2("test -h").logs?.[0]?.message??"").includes("-fdx")?!0:r2.cmd("'test -vf "+e).startsWith("found")}function W(e,t){return e+" ".repeat(Math.max(0,t-e.length))}function x(e,t){return e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,"")}function se(e){return e.replace(/\x1b\[[0-9;]*m/g,"")}function le(e){let t=e.match(/```json\s*([\s\S]*?)```/);return t&&t[1]?t[1].trim():e}function ce(e){let t=e,n=t.indexOf("{");n!==-1&&(t=t.slice(n));let r=t.lastIndexOf("}");return r!==-1&&(t=t.slice(0,r+1)),t}function z(e){return btoa(e)}function ue(e,t){let n=z(t);r2.cmd("p6ds "+n+" > "+e)}function E(e){let t=e;return t=t.replace(/<think>[\s\S]*?<\/think>/gi,""),t.split(`
|
||||
`).filter(n=>!n.trim().startsWith("```")).join(`
|
||||
`)}function J(e){o.debug&&console.log(e)}function se(e){let t={};for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;let[i,...a]=r.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 Re=["DECAI_HEADERS","R2AI_HEADERS"];function Ue(e){let t=r2.cmd("'%"+e).trim();return t!==""&&!t.includes("=")?t:""}function _e(e){return e.trim().toLowerCase()}function Me(e){let t=e.indexOf(":"),n=e.indexOf("="),r=t!==-1&&(n===-1||t<n)?t:n;if(r===-1)return null;let i=e.slice(0,r).trim();return i===""?null:{name:i,value:e.slice(r+1).trim()}}function Ne(e){return e.value===""?`${e.name}:`:`${e.name}: ${e.value}`}function le(e,t){for(let n of e){let r=Me(n.trim());r&&t.set(_e(r.name),Ne(r))}}function Y(e){let t=new Map,n=e.replace(/\\n/g,`
|
||||
`).split(/\r?\n/g).filter(r=>{let i=r.trim();return i&&!i.startsWith("#")});return le(n,t),Array.from(t.values())}function ce(e){return e.join("\\n")}function E(...e){let t=new Map;for(let n of e)le(n,t);return Array.from(t.values())}function Te(){for(let e of Re){let t=Ue(e);if(t)return Y(t)}return[]}function ue(){return E(Te(),o.extraHeaders)}function M(e){return`'${e.replace(/'/g,`'"'"'`)}'`}function Ke(e){let{method:t,url:n,headers:r,payload:i}=e,a=["curl","-s"];if(o.timeout>0&&a.push("--max-time",String(o.timeout)),E(["Content-Type: application/json"],r).forEach(l=>a.push("-H",M(l))),t==="POST"){if(!i)throw new Error("Payload required for POST requests");let l=r2.fdump(i);a.push("--data-binary","@"+M(l),M(n));let u=a.join(" ")+"; rm "+M(l);return J(u),r2.syscmds(u)}else{a.push(M(n));let l=a.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="",r=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),r=a}catch{return null}return r?(r.message={role:"assistant",content:n},r):null}function de(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 de({method:"GET",url:e,headers:t})}function pe(e,t,n){return de({method:"POST",url:e,headers:t,payload:n})}function z(e){return e.split("_")[0].toLowerCase()}function fe(e){if(!e.authKey)return;let t=Array.from(new Set([e.keyName,z(e.authKey)].filter(Boolean))),n;for(let r of t){let i=q(r,e.authKey);if(i[0])return i;!n&&i[2]!=="nope"&&(n=i)}return n||q(z(e.authKey),e.authKey)}function S(e){if(e)return typeof e=="string"?e:e.message||JSON.stringify(e)}function me(e){return o.baseurl||e.defaultBaseUrl}function he(e,t=ue()){return E(e,t)}function He(e){return e?["Authorization: Bearer "+e]:[]}function De(e){let t=["anthropic-version: 2023-06-01"];return e?E(t,["x-api-key: "+e]):t}function ge(e,t){switch(e.authStyle||"none"){case"bearer":return He(t);case"anthropic":return De(t);case"none":default:return[]}}function je(e,t,n){let r=O(e,t),i=S(r.error);return i?(console.error(i),"error invalid response"):r.data?.map(n).join(`
|
||||
`)||""}function Be(e,t){let n=new Set;return e.filter(r=>{let i=t(r);return n.has(i)?!1:(n.add(i),!0)})}function V(e){let t=fe(e)?.[0]||null;return{baseUrl:me(e),headers:he(ge(e,t))}}function Fe(e){let{baseUrl:t,headers:n}=V(e),r=h(t,"v1/models");return je(r,n,i=>i.id)}function Le(e){let{baseUrl:t,headers:n}=V(e),r=O(h(t,"api/tags"),n),i=S(r.error);return i?(console.error(i),"error invalid response"):r.models?.map(a=>a.name).join(`
|
||||
`)||""}function Ge(e){let{baseUrl:t,headers:n}=V(e),r=O(h(t,"v1/models"),n);return r.data?Be(r.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 $e={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 W()&&(n.thinking={type:"enabled",budget_tokens:4096},n.max_tokens=16e3),o.deterministic&&!W()&&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 o.apitype==="generate"?n.prompt=t:n.messages=[{role:"user",content:t}],W()?o.think==="true"||o.think==="1"?n.think=!0:n.think=o.think:n.think=!1,o.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/"+o.apitype)},gemini:{buildPayload:(e,t)=>{let n={contents:[{parts:[{text:t}]}]},r={};return o.deterministic&&Object.assign(r,{temperature:0,topP:1,topK:1}),o.think!==""&&(X()?r.thinkingConfig={thinkingBudget:0}:r.thinkingConfig={thinkingBudget:8192}),Object.keys(r).length>0&&(n.generationConfig=r),n},parseResponse:e=>{if(e.candidates&&e.candidates[0]?.content?.parts){let n=e.candidates[0].content.parts.filter(r=>!r.thought&&r.text).map(r=>r.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}},H={anthropic:{defaultModel:"claude-3-7-sonnet-20250219",defaultBaseUrl:"https://api.anthropic.com",authKey:"ANTHROPIC_API_KEY",keyName:"anthropic",authStyle:"anthropic",apiStyle:"anthropic"},claude:{defaultModel:"claude-3-7-sonnet-20250219",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 H[e]}function Q(){return Object.keys(H)}function ye(e){let t=D(e);if(!t)throw new Error(`Unknown provider: ${e}`);if(e==="mistral")return Ge(t);switch(t.apiStyle){case"openai":case"anthropic":return Fe(t);case"ollama":return Le(t);case"gemini":return"";default:return""}}function X(){return o.think==="false"||o.think==="0"}function W(){return o.think!==""&&!X()}function Je(e,t,n){let r=e;return o.think!==""&&n==="openai"&&(X()?(r+=' Answers directly and concisely, without showing any thinking steps or internal reasoning. Never include phrases like "Let me think".',r+=" /no_think"):r="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."+r),t?r:o.prompt+Ye()+r}function Ye(){return`
|
||||
`)}function V(e){o.debug&&console.log(e)}function de(e){let t={};for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;let[i,...a]=r.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 Ne=["DECAI_HEADERS","R2AI_HEADERS"];function Te(e){let t=r2.cmd("'%"+e).trim();return t!==""&&!t.includes("=")?t:""}function Ke(e){return e.trim().toLowerCase()}function qe(e){let t=e.indexOf(":"),n=e.indexOf("="),r=t!==-1&&(n===-1||t<n)?t:n;if(r===-1)return null;let i=e.slice(0,r).trim();return i===""?null:{name:i,value:e.slice(r+1).trim()}}function He(e){return e.value===""?`${e.name}:`:`${e.name}: ${e.value}`}function pe(e,t){for(let n of e){let r=qe(n.trim());r&&t.set(Ke(r.name),He(r))}}function Q(e){let t=new Map,n=e.replace(/\\n/g,`
|
||||
`).split(/\r?\n/g).filter(r=>{let i=r.trim();return i&&!i.startsWith("#")});return pe(n,t),Array.from(t.values())}function fe(e){return e.join("\\n")}function C(...e){let t=new Map;for(let n of e)pe(n,t);return Array.from(t.values())}function De(){for(let e of Ne){let t=Te(e);if(t)return Q(t)}return[]}function me(){return C(De(),o.extraHeaders)}function q(e){return`'${e.replace(/'/g,`'"'"'`)}'`}function je(e){let{method:t,url:n,headers:r,payload:i}=e,a=C(["Content-Type: application/json"],r),s=r2.http;if(s)return s(t,n,a.join(`
|
||||
`),i??"").body;let l=["curl","-s"];if(o.timeout>0&&l.push("--max-time",String(o.timeout)),a.forEach(c=>l.push("-H",q(c))),t==="POST"){if(!i)throw new Error("Payload required for POST requests");let c=r2.fdump(i);l.push("--data-binary","@"+q(c),q(n));let k=l.join(" ")+"; rm "+q(c);return V(k),r2.syscmds(k)}else{l.push(q(n));let c=l.join(" ");return V(c),r2.syscmds(c)}}function Be(e){let t=e.split(`
|
||||
`).filter(i=>i.trim()!=="");if(t.length<=1)return null;let n="",r=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),r=a}catch{return null}return r?(r.message={role:"assistant",content:n},r):null}function he(e){try{let t=je(e).trim();if(t==="")return{error:"empty response"};try{return JSON.parse(t)}catch{let n=Be(t);return n||{error:t,rawOutput:t}}}catch(t){return{error:t.message}}}function I(e,t){return he({method:"GET",url:e,headers:t})}function ge(e,t,n){return he({method:"POST",url:e,headers:t,payload:n})}function Z(e){return e.split("_")[0].toLowerCase()}function ye(e){if(!e.authKey)return;let t=Array.from(new Set([e.keyName,Z(e.authKey)].filter(Boolean))),n;for(let r of t){let i=B(r,e.authKey);if(i[0])return i;!n&&i[2]!=="nope"&&(n=i)}return n||B(Z(e.authKey),e.authKey)}function R(e){if(e)return typeof e=="string"?e:e.message||JSON.stringify(e)}function be(e){return o.baseurl||e.defaultBaseUrl}function ke(e,t=me()){return C(e,t)}function Fe(e){return e?["Authorization: Bearer "+e]:[]}function Le(e){let t=["anthropic-version: 2023-06-01"];return e?C(t,["x-api-key: "+e]):t}function xe(e,t){switch(e.authStyle||"none"){case"bearer":return Fe(t);case"anthropic":return Le(t);case"none":default:return[]}}function Ge(e,t,n){let r=I(e,t),i=R(r.error);return i?(console.error(i),"error invalid response"):r.data?.map(n).join(`
|
||||
`)||""}function $e(e,t){let n=new Set;return e.filter(r=>{let i=t(r);return n.has(i)?!1:(n.add(i),!0)})}function ee(e){let t=ye(e)?.[0]||null;return{baseUrl:be(e),headers:ke(xe(e,t))}}function Je(e){let{baseUrl:t,headers:n}=ee(e),r=x(t,"v1/models");return Ge(r,n,i=>i.id)}function Ye(e){let{baseUrl:t,headers:n}=ee(e),r=I(x(t,"api/tags"),n),i=R(r.error);return i?(console.error(i),"error invalid response"):r.models?.map(a=>a.name).join(`
|
||||
`)||""}function We(e){let{baseUrl:t,headers:n}=ee(e),r=I(x(t,"v1/models"),n);return r.data?$e(r.data,i=>i.name||i.id).map(i=>[W(i.name||i.id,30),W(""+(i.max_context_length||""),10),i.description||""].join(" ")).join(`
|
||||
`):""}var ze={openai:{buildPayload:(e,t)=>({stream:!1,model:e,messages:[{role:"user",content:t}]}),parseResponse:e=>{if(e.error)throw new Error(R(e.error)||"Unknown error");if(e.choices&&e.choices[0]?.message?.content)return E(e.choices[0].message.content);throw new Error("Invalid response format")},buildUrl:e=>x(e,"v1/chat/completions")},anthropic:{buildPayload:(e,t)=>{let n={model:e,max_tokens:5128,messages:[{role:"user",content:t}]};return X()&&(n.thinking={type:"enabled",budget_tokens:4096},n.max_tokens=16e3),o.deterministic&&!X()&&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 E(t.join(`
|
||||
`))}throw e.error?new Error(R(e.error)||"Unknown error"):new Error("Invalid response format")},buildUrl:e=>x(e,"v1/messages")},ollama:{buildPayload:(e,t)=>{let n={stream:!1,model:e};return o.apitype==="generate"?n.prompt=t:n.messages=[{role:"user",content:t}],X()?o.think==="true"||o.think==="1"?n.think=!0:n.think=o.think:n.think=!1,o.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(R(e.error));if(typeof e.response=="string")return E(e.response);if(e.message?.content)return E(e.message.content);throw new Error(JSON.stringify(e))},buildUrl:e=>x(e,"api/"+o.apitype)},gemini:{buildPayload:(e,t)=>{let n={contents:[{parts:[{text:t}]}]},r={};return o.deterministic&&Object.assign(r,{temperature:0,topP:1,topK:1}),o.think!==""&&(ne()?r.thinkingConfig={thinkingBudget:0}:r.thinkingConfig={thinkingBudget:8192}),Object.keys(r).length>0&&(n.generationConfig=r),n},parseResponse:e=>{if(e.candidates&&e.candidates[0]?.content?.parts){let n=e.candidates[0].content.parts.filter(r=>!r.thought&&r.text).map(r=>r.text);if(n.length>0)return E(n.join(`
|
||||
`))}throw e.error?new Error(R(e.error)):(console.log(JSON.stringify(e)),new Error("Invalid response format"))},buildUrl:(e,t,n)=>x(e,`v1beta/models/${t}:generateContent?key=${n}`),requiresUrlApiKey:!0}},F={anthropic:{defaultModel:"claude-3-7-sonnet-20250219",defaultBaseUrl:"https://api.anthropic.com",authKey:"ANTHROPIC_API_KEY",keyName:"anthropic",authStyle:"anthropic",apiStyle:"anthropic"},claude:{defaultModel:"claude-3-7-sonnet-20250219",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 L(e){return F[e]}function te(){return Object.keys(F)}function ve(e){let t=L(e);if(!t)throw new Error(`Unknown provider: ${e}`);if(e==="mistral")return We(t);switch(t.apiStyle){case"openai":case"anthropic":return Je(t);case"ollama":return Ye(t);case"gemini":return"";default:return""}}function ne(){return o.think==="false"||o.think==="0"}function X(){return o.think!==""&&!ne()}function Ve(e,t,n){let r=e;return o.think!==""&&n==="openai"&&(ne()?(r+=' Answers directly and concisely, without showing any thinking steps or internal reasoning. Never include phrases like "Let me think".',r+=" /no_think"):r="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."+r),t?r:o.prompt+Qe()+r}function Qe(){return`
|
||||
.Translate the code into `+o.language+` programming language
|
||||
`}function We(e,t,n,r){let i=o.model||e.defaultModel,a=Je(n,r,e.apiStyle),s=fe(e);if(t.requiresUrlApiKey&&s?.[1]&&e.authKey)return`Cannot read ~/.r2ai.${z(e.authKey)}-key`;let l=t.buildPayload(i,a),u=ge(e,s?.[0]||null);e.apiStyle==="ollama"&&u.push("Accept: application/x-ndjson");let v=he(u),c=t.buildUrl(me(e),i,s?.[0]||void 0);try{return t.parseResponse(pe(c,v,JSON.stringify(l)))}catch(d){return"ERROR: "+d.message}}function be(e,t){let n=D(o.api);return n?We(n,$e[n.apiStyle],e,t):`Unknown value for 'decai -e api'. Available: ${Q().join(", ")}`}function ze(){return Object.fromEntries(Object.entries(H).filter(([,e])=>e.authKey).map(([e,t])=>[e,t.authKey]))}function q(e,t){let n=r2.cmd("'%"+t).trim();if(!n.includes("=")&&n!=="")return[n.trim(),null,"env"];let r=e.toLowerCase(),i=N;if(_(i)){let s=r2.cmd("'cat "+i),l=se(s);if(r in l)return[l[r],null,"txt"]}let a="~/.r2ai."+r+"-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 ke(){K(w,N),r2.cmd("'ed "+N)}function Z(){Object.entries(ze()).forEach(([e,t])=>{let n=q(e,t)[2];console.log(n," ",e)})}function xe(e){let t=D(e);if(!t){console.error(`Unknown provider: ${e}`);return}try{let n=ye(e);n&&console.log(n),t.hardcodedModels&&t.hardcodedModels.forEach(r=>console.log(r)),e==="mistral"&&console.log("ministral-8b-latest"),!n&&!t.hardcodedModels&&console.log(t.defaultModel)}catch(n){let r=n;console.error(`Error listing models for ${e}:`,r.message),console.log(t.defaultModel)}}var ee=!1;function Ve(e){return e==="true"||e==="1"}function P(e){let t=o;return{get:()=>t[e],set:n=>{t[e]=Ve(n)}}}function C(e){let t=o;return{get:()=>t[e],set:n=>{t[e]=n}}}function Qe(e){let t=e.trim();return t==="chat"?"chat":t==="generate"?"generate":null}var ve={pipeline:{get:()=>o.pipeline,set:e=>{o.pipeline=e;try{o.decopipe=JSON.parse(r2.cmd("cat "+e))}catch(t){console.error(t)}}},model:{get:()=>o.model,set:e=>{e==="?"?xe(o.api):o.model=e.trim()}},deterministic:P("deterministic"),files:P("useFiles"),think:{get:()=>o.think||"false",set:e=>{o.think=e}},apitype:{get:()=>o.apitype,set:e=>{let t=Qe(e);t?o.apitype=t:console.error("Invalid apitype. Use chat or generate.")}},debug:P("debug"),timeout:{get:()=>o.timeout,set:e=>{o.timeout=Math.max(0,parseInt(e,10)||0)}},api:{get:()=>o.api,set:e=>{e==="?"?console.error(Q().join(`
|
||||
`)):o.api=e}},lang:C("language"),hlang:C("humanLanguage"),cache:P("cache"),cmds:C("commands"),tts:P("tts"),yolo:P("yolo"),prompt:C("prompt"),ctxfile:C("contextFile"),baseurl:C("baseurl"),headers:{get:()=>ce(o.extraHeaders),set:e=>{o.extraHeaders=Y(e)}},maxtokens:{get:()=>o.maxInputTokens,set:e=>{o.maxInputTokens=parseInt(e,10)||-1}}};function Xe(){let e={host:o.host,port:o.port,lastOutput:o.lastOutput};Object.assign(o,A,e),o.decopipe={...A.decopipe},o.extraHeaders=[...A.extraHeaders]}function Ze(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),r=t===-1?void 0:e.slice(t+1),i=ve[n];if(!i){console.error("Unknown config key");return}typeof r<"u"?i.set(r):console.log(i.get())}function Ae(){Object.entries(ve).forEach(([e,t])=>{let n=t.get();console.log("decai -e "+e+"="+n)})}function we(){if(Xe(),!_(U))return;let e=r2.call("cat "+U);for(let t of e.split(/\r?\n/)){let n=Ze(t);n&&n[0]!="#"&&x(n)}}function Ee(){ee||(we(),ee=!0)}function Oe(){K(w,U),r2.cmd("'ed "+U),we(),ee=!0}function p(e,t,n=!1){let r=(t||"").replace(/`/g,""),i=e.replace(/'/g,"");if(o.api==="r2"||o.api==="r2ai"){let s=ne(".pdc.txt");ae(s,r);let l=i.startsWith("-")?i:["-i",s,i].join(" "),u=o.baseurl||o.host+":"+o.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+`:
|
||||
`+r;return o.maxInputTokens>0&&a.length>o.maxInputTokens&&(a=a.slice(0,o.maxInputTokens)),be(a,n)}function Pe(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 Se(){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,r){if(n){let s=r2.cmd("anos").trim();if(s.length>0)return s}let i="";if(r){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="";o.contextFile!==""&&r2.cmd2("test -f "+o.contextFile).value===0&&(l+=`## Context:
|
||||
`}function Xe(e,t,n,r){let i=o.model||e.defaultModel,a=Ve(n,r,e.apiStyle),s=ye(e);if(t.requiresUrlApiKey&&s?.[1]&&e.authKey)return`Cannot read ~/.r2ai.${Z(e.authKey)}-key`;let l=t.buildPayload(i,a),c=xe(e,s?.[0]||null);e.apiStyle==="ollama"&&c.push("Accept: application/x-ndjson");let k=ke(c),u=t.buildUrl(be(e),i,s?.[0]||void 0);try{return t.parseResponse(ge(u,k,JSON.stringify(l)))}catch(d){return"ERROR: "+d.message}}function Ae(e,t){let n=L(o.api);return n?Xe(n,ze[n.apiStyle],e,t):`Unknown value for 'decai -e api'. Available: ${te().join(", ")}`}function Ze(){return Object.fromEntries(Object.entries(F).filter(([,e])=>e.authKey).map(([e,t])=>[e,t.authKey]))}function B(e,t){let n=r2.cmd("'%"+t).trim();if(!n.includes("=")&&n!=="")return[n.trim(),null,"env"];let r=e.toLowerCase(),i=H;if(K(i)){let s=r2.cmd("'cat "+i),l=de(s);if(r in l)return[l[r],null,"txt"]}let a="~/.r2ai."+r+"-key";if(K(a)){let s=r2.cmd("'cat "+a);return s===""?[null,"Cannot read "+a,"no"]:[s.trim(),null,"file"]}return[null,"Not available","nope"]}function we(){j(P,H),r2.cmd("'ed "+H)}function oe(){Object.entries(Ze()).forEach(([e,t])=>{let n=B(e,t)[2];console.log(n," ",e)})}function Ee(e){let t=L(e);if(!t){console.error(`Unknown provider: ${e}`);return}try{let n=ve(e);n&&console.log(n),t.hardcodedModels&&t.hardcodedModels.forEach(r=>console.log(r)),e==="mistral"&&console.log("ministral-8b-latest"),!n&&!t.hardcodedModels&&console.log(t.defaultModel)}catch(n){let r=n;console.error(`Error listing models for ${e}:`,r.message),console.log(t.defaultModel)}}var re=!1;function et(e){return e==="true"||e==="1"}function U(e){let t=o;return{get:()=>t[e],set:n=>{t[e]=et(n)}}}function _(e){let t=o;return{get:()=>t[e],set:n=>{t[e]=n}}}function tt(e){let t=e.trim();return t==="chat"?"chat":t==="generate"?"generate":null}var Oe={pipeline:{get:()=>o.pipeline,set:e=>{o.pipeline=e;try{o.decopipe=JSON.parse(r2.cmd("cat "+e))}catch(t){console.error(t)}}},model:{get:()=>o.model,set:e=>{e==="?"?Ee(o.api):o.model=e.trim()}},deterministic:U("deterministic"),files:U("useFiles"),think:{get:()=>o.think||"false",set:e=>{o.think=e}},apitype:{get:()=>o.apitype,set:e=>{let t=tt(e);t?o.apitype=t:console.error("Invalid apitype. Use chat or generate.")}},debug:U("debug"),timeout:{get:()=>o.timeout,set:e=>{o.timeout=Math.max(0,parseInt(e,10)||0)}},api:{get:()=>o.api,set:e=>{e==="?"?console.error(te().join(`
|
||||
`)):o.api=e}},lang:_("language"),hlang:_("humanLanguage"),cache:U("cache"),cmds:_("commands"),tts:U("tts"),yolo:U("yolo"),prompt:_("prompt"),ctxfile:_("contextFile"),baseurl:_("baseurl"),headers:{get:()=>fe(o.extraHeaders),set:e=>{o.extraHeaders=Q(e)}},maxtokens:{get:()=>o.maxInputTokens,set:e=>{o.maxInputTokens=parseInt(e,10)||-1}}};function nt(){let e={host:o.host,port:o.port,lastOutput:o.lastOutput};Object.assign(o,S,e),o.decopipe={...S.decopipe},o.extraHeaders=[...S.extraHeaders]}function ot(e){let t=e.trim();return!t||t.startsWith("#")?null:t.startsWith("decai -e ")?t.slice(9).trim():t}function O(e){let t=e.indexOf("="),n=t===-1?e:e.slice(0,t),r=t===-1?void 0:e.slice(t+1),i=Oe[n];if(!i){console.error("Unknown config key");return}typeof r<"u"?i.set(r):console.log(i.get())}function Se(){Object.entries(Oe).forEach(([e,t])=>{let n=t.get();console.log("decai -e "+e+"="+n)})}function Pe(){if(nt(),!K(T))return;let e=r2.call("cat "+T);for(let t of e.split(/\r?\n/)){let n=ot(t);n&&n[0]!="#"&&O(n)}}function Ce(){re||(Pe(),re=!0)}function Ie(){j(P,T),r2.cmd("'ed "+T),Pe(),re=!0}function p(e,t,n=!1){let r=(t||"").replace(/`/g,""),i=e.replace(/'/g,"");if(o.api==="r2"||o.api==="r2ai"){let s=ae(".pdc.txt");ue(s,r);let l=i.startsWith("-")?i:["-i",s,i].join(" "),c=o.baseurl||o.host+":"+o.port,k=x(c,"cmd/"+encodeURIComponent(l)),u=I(k,[]);return u.error?`Error: ${u.error}`:u.result||JSON.stringify(u)||"Cannot curl, use r2ai-server or r2ai -w"}if(i.startsWith("-"))return"";let a=i+`:
|
||||
`+r;return o.maxInputTokens>0&&a.length>o.maxInputTokens&&(a=a.slice(0,o.maxInputTokens)),Ae(a,n)}function Ue(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 Re(){let e="",t=n=>e+=" "+w+" "+n+`
|
||||
`;e+="Usage: "+w+` (-h) ...
|
||||
`,e+="Version: "+ie+`
|
||||
`,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 G(e,t,n,r){if(n){let s=r2.cmd("anos").trim();if(s.length>0)return s}let i="";if(r){let s=r2.cmd("s");i+=`## Context functions:
|
||||
`;let l=r2.cmdAt("axff~^C[2]~$$",s);for(let c of l.split(/\n/g))i+=r2.cmd("pdc@"+c);r2.cmd("s "+s)}let a=t?" "+e:"";try{let s=e.slice(2).trim(),l="";o.contextFile!==""&&r2.cmd2("test -f "+o.contextFile).value===0&&(l+=`## Context:
|
||||
[START]
|
||||
`+r2.cmd("cat "+o.contextFile)+`
|
||||
[END]
|
||||
`);let{body:u,count:v}=Pe(()=>{let d=`## Before:
|
||||
`,y=0;for(let g of o.commands.split(",")){if(g.trim()==="")continue;let I=t||s.trim().length===0?g:g+"@@= "+s,R=r2.cmd(I);R.length>5&&(d+="Output of "+g+`:
|
||||
`);let{body:c,count:k}=Ue(()=>{let d=`## Before:
|
||||
`,A=0;for(let v of o.commands.split(",")){if(v.trim()==="")continue;let M=t||s.trim().length===0?v:v+"@@= "+s,N=r2.cmd(M);N.length>5&&(d+="Output of "+v+`:
|
||||
[START]
|
||||
`+R+`
|
||||
`+N+`
|
||||
[END]
|
||||
`,y++)}return d+=`## After:
|
||||
`,{body:d,count:y}});if(v===0){console.error("Nothing to do.");return}let c="";if(o.decopipe.use){let d=o.model;try{let y=o.decopipe[o.decopipe.default],g=l+u;for(let I of y.pipeline){I.model&&(o.model=I.model);let R=I.query+". "+y.globalQuery;c=p(R,g,!0),o.debug&&console.log(`QUERY
|
||||
`,R,`
|
||||
`,A++)}return d+=`## After:
|
||||
`,{body:d,count:A}});if(k===0){console.error("Nothing to do.");return}let u="";if(o.decopipe.use){let d=o.model;try{let A=o.decopipe[o.decopipe.default],v=l+c;for(let M of A.pipeline){M.model&&(o.model=M.model);let N=M.query+". "+A.globalQuery;u=p(N,v,!0),o.debug&&console.log(`QUERY
|
||||
`,N,`
|
||||
INPUT
|
||||
`,g,`
|
||||
`,v,`
|
||||
OUTPUT
|
||||
`,c),g=c}c=g}finally{o.model=d}}else{let d=a;l+=u+i,c=p(d,l,!1),o.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 et(){let e=Pe(()=>"[START]"+o.commands.split(",").map(r=>r2.cmd(r)).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 "+o.humanLanguage,e,!0).trim().split(/\n/g);return n[n.length-1].trim()}function tt(){let e=o.language,t=r2.cmd("afv;pdc");o.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),r=n.indexOf("{");return r!==-1&&(n=n.substring(0,r)),o.language=e,n}function nt(e,t){let n=[];for(;;){let r=L.auto;if(n.length>0){r+=`## Command Results
|
||||
`,u),v=u}u=v}finally{o.model=d}}else{let d=a;l+=c+i,u=p(d,l,!1),o.lastOutput=u}return n&&u.length>1&&r2.call("ano=base64:"+z(u)),u.startsWith("```")&&(u=u.replace(/```.*\n/,"").replace(/```$/,"")),u.trim()}catch(s){let l=s;console.error(l,l.stack);return}}function rt(){let e=Ue(()=>"[START]"+o.commands.split(",").map(r=>r2.cmd(r)).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 "+o.humanLanguage,e,!0).trim().split(/\n/g);return n[n.length-1].trim()}function it(){let e=o.language,t=r2.cmd("afv;pdc");o.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),r=n.indexOf("{");return r!==-1&&(n=n.substring(0,r)),o.language=e,n}function at(e,t){let n=[];for(;;){let r=Y.auto;if(n.length>0){r+=`## Command Results
|
||||
|
||||
`;for(let a of n){let s=JSON.parse(a);r+="### "+s.command+"\n\n```\n"+s.response+"\n```\n"}}r+=`
|
||||
|
||||
@@ -160,10 +161,10 @@ OUTPUT
|
||||
`,r,`
|
||||
#### /input`),console.log("Thinking...");let i=p("",r,!0);o.debug&&console.log(`#### output
|
||||
`,i,`
|
||||
#### /output`);try{let a=JSON.parse(ie(re(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),o.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;o.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);o.debug&&console.log(`<r2output>
|
||||
#### /output`);try{let a=JSON.parse(ce(le(E(i))));if(a.action==="r2cmd"||a.action==="response"||a.action===a.command){let s=a.command||"";a.reason&&(console.log("[r2cmd] Reasoning: "+a.reason),o.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;o.yolo||(l=st(s,t)),console.log("[r2cmd] Running: "+l);let c=r2.cmd2(l),k=c.logs?c.logs.map(A=>A.type+": "+A.message).join(`
|
||||
`):"",u=(c.res+k).trim();console.log(u);let d=se(u);o.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==="?"){rt();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 r=n.indexOf("#");return r!==-1?n.slice(0,r).trim():n}else return e}}}return e}function rt(){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 it(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("-")){Se();return}let n="",r=e[1],i=it(e);switch(r){case"H":console.log(L.decai);break;case"a":nt(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?o.prompt=i:console.log(o.prompt);break}case"R":o.prompt=T;break;case"s":if(!B()){n=F;break}n=tt();break;case"V":n=p("find vulnerabilities, dont show the code, only show the response, provide a sample exploit",o.lastOutput,!1);break;case"K":ke();break;case"E":Oe();break;case"k":Z();break;case"b":{i?x("baseurl="+i):console.log(o.baseurl);break}case"e":{i?x(i):Ae();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,o.lastOutput,!1);break;case"x":if(!B()){n=F;break}n=et(),(e[2]==="*"||e[2]==="r")&&(n="'CC "+n);break;case"d":e[2]==="r"?n=j(e.slice(2),!0,o.cache,!0)||"":e[2]==="d"?n=j(e,!1,!1,!1)||"":e[2]==="D"?n=j(e,!0,!1,!1)||"":n=j(e,!1,o.cache,!1)||"";break;default:Se();break}return n||void 0}function Ie(e){Ee();let t=Ce(e,Ie);return t&&r2.log(t),!0}function at(){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}})}at();
|
||||
`).replace(/\\/g,"");console.log(l)}else console.log(i),console.error(a);break}}}function st(e,t){for(;;){let n=r2.cmd("'?ie Tweak command? ('?' for help)").trim();if(n==="q!"){console.error("Break!");break}if(n==="?"){lt();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 r=n.indexOf("#");return r!==-1?n.slice(0,r).trim():n}else return e}}}return e}function lt(){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 ct(e){return e.slice(2).trim()}function $(){return r2.cmd("afi.").trim().length>0}var J="Cannot find function at current offset";function _e(e,t){if(e===""||!e.startsWith("-")){Re();return}let n="",r=e[1],i=ct(e);switch(r){case"H":console.log(Y.decai);break;case"a":at(i,t);break;case"m":{i==="="?O("model="):i?O("model="+i):O("model");break}case"n":case"f":{if(!$()){n=J;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(!$()){n=J;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(" "),c=r2.cmd("cat "+s);console.log(p(l,c,!0))}else console.log("Use: decai -i [file] [query ...]");break}case"p":{i?O("api="+i):oe();break}case"r":{i?o.prompt=i:console.log(o.prompt);break}case"R":o.prompt=D;break;case"s":if(!$()){n=J;break}n=it();break;case"V":n=p("find vulnerabilities, dont show the code, only show the response, provide a sample exploit",o.lastOutput,!1);break;case"K":we();break;case"E":Ie();break;case"k":oe();break;case"b":{i?O("baseurl="+i):console.log(o.baseurl);break}case"e":{i?O(i):Se();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,o.lastOutput,!1);break;case"x":if(!$()){n=J;break}n=rt(),(e[2]==="*"||e[2]==="r")&&(n="'CC "+n);break;case"d":e[2]==="r"?n=G(e.slice(2),!0,o.cache,!0)||"":e[2]==="d"?n=G(e,!1,!1,!1)||"":e[2]==="D"?n=G(e,!0,!1,!1)||"":n=G(e,!1,o.cache,!1)||"";break;default:Re();break}return n||void 0}function Me(e){Ce();let t=_e(e,Me);return t&&r2.log(t),!0}function ut(){r2.unload("core",w),r2.plugin("core",function(){function e(t){if(t.startsWith(w)){let n=t.slice(w.length).trim();return Me(n)}return!1}return{name:w,license:"MIT",desc:"r2 decompiler based on r2ai",call:e}})}ut();
|
||||
+2
-1
@@ -1,10 +1,11 @@
|
||||
PLUGIN_NAME=decai
|
||||
R2F_C_FLAGS=-c -S
|
||||
R2FRIDA_COMPILE?=r2pm -r r2frida-compile
|
||||
|
||||
all: build
|
||||
|
||||
build:
|
||||
r2pm -r r2frida-compile $(R2F_C_FLAGS) -o ../$(PLUGIN_NAME).r2.js main.ts
|
||||
$(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
|
||||
|
||||
+11
-4
@@ -16,14 +16,21 @@ function shellEscape(arg: string): string {
|
||||
|
||||
function executeCurl(options: HttpRequestOptions): string {
|
||||
const { method, url, headers, payload } = options;
|
||||
const cmdParts = ["curl", "-s"];
|
||||
if (state.timeout > 0) {
|
||||
cmdParts.push("--max-time", String(state.timeout));
|
||||
}
|
||||
const requestHeaders = mergeHeaders(
|
||||
["Content-Type: application/json"],
|
||||
headers,
|
||||
);
|
||||
// WASI provides HTTP through the host; never fall back after a denied request.
|
||||
const http = (r2 as unknown as { http?: (
|
||||
method: string, url: string, headers: string, data?: string,
|
||||
) => { status: number; body: string } }).http;
|
||||
if (http) {
|
||||
return http(method, url, requestHeaders.join("\n"), payload ?? "").body;
|
||||
}
|
||||
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") {
|
||||
|
||||
@@ -16,3 +16,25 @@ For meson builds, place this repository at `libr/xps/p/r2ai` and make
|
||||
```meson
|
||||
subdir('r2ai/r2plugin')
|
||||
```
|
||||
|
||||
## WASI HTTP build
|
||||
|
||||
Use a radare2 checkout with the host HTTP bridge and place this repository at
|
||||
`libr/xps/p/r2ai`. From that radare2 checkout:
|
||||
|
||||
```sh
|
||||
echo 'EXTERNAL_PLUGINS+=r2ai' >> libr/xps/config.mk
|
||||
make -C libr/xps
|
||||
WASI_HTTP=1 sys/wasi-api.sh
|
||||
```
|
||||
|
||||
This links the C plugin into the reactor; it does not load a native shared
|
||||
library at runtime. Enable HTTP in the host adapter. The `auto` backend selects
|
||||
`r_socket_http_get/post`, preserving provider authentication headers and JSON
|
||||
bodies, subject to the radare2 sandbox. `--disable-network` also denies these calls.
|
||||
|
||||
WASI excludes POSIX signal/alarm handling; the host controls request timeouts,
|
||||
not `r2ai.http.timeout`. Normal queries and model listing use buffered HTTP.
|
||||
Vertex's automatic `gcloud` authentication and background tasks requiring
|
||||
threads are unavailable. Explicit shell backends (`system`/`curl`) cannot work;
|
||||
use `auto` or `socket`. Browser CORS and restricted-header rules still apply.
|
||||
+6
-4
@@ -29,7 +29,7 @@ static R2AI_HttpConfig get_http_config(RCore *core) {
|
||||
return config;
|
||||
}
|
||||
|
||||
#if R2__UNIX__
|
||||
#if R2__UNIX__ && !__wasi__
|
||||
// Signal handler for timeout (SIGALRM)
|
||||
static void r2ai_http_sigint_handler(int sig) {
|
||||
if (sig == SIGALRM) {
|
||||
@@ -66,7 +66,7 @@ static HttpResponse r2ai_http_request_with_retry(HttpRequestFunc func, const HTT
|
||||
// Reset interrupt flag
|
||||
r2ai_http_interrupted = 0;
|
||||
|
||||
#if R2__UNIX__
|
||||
#if R2__UNIX__ && !__wasi__
|
||||
// Ignore SIGWINCH to prevent window resize from interrupting child processes
|
||||
struct sigaction old_winch_act;
|
||||
sigaction (SIGWINCH, &(struct sigaction){ .sa_handler = SIG_IGN }, &old_winch_act);
|
||||
@@ -129,7 +129,7 @@ static HttpResponse r2ai_http_request_with_retry(HttpRequestFunc func, const HTT
|
||||
r_cons_break_pop (core->cons);
|
||||
}
|
||||
|
||||
#if R2__UNIX__
|
||||
#if R2__UNIX__ && !__wasi__
|
||||
// Restore SIGWINCH handler
|
||||
sigaction (SIGWINCH, &old_winch_act, NULL);
|
||||
#endif
|
||||
@@ -145,7 +145,9 @@ static HttpResponse r2ai_http_request_with_retry(HttpRequestFunc func, const HTT
|
||||
static HttpRequestFunc select_backend(const char *backend, bool is_post) {
|
||||
HttpRequestFunc func = NULL;
|
||||
if (!strcmp (backend, "auto")) {
|
||||
#if R2__WINDOWS__
|
||||
#if __wasi__
|
||||
backend = "socket";
|
||||
#elif R2__WINDOWS__
|
||||
// TODO: we should use pwsh or r2 by default, but for not it seems the only backend that works on Windows is r2curl+system
|
||||
r_sys_setenv ("R2_CURL", "1");
|
||||
backend = "system";
|
||||
|
||||
+4
-4
@@ -6,14 +6,14 @@ static HttpResponse socket_http_post_with_interrupt(const HTTPRequest *request)
|
||||
#if R2__WINDOWS__
|
||||
r_sys_setenv ("R2_CURL", "1");
|
||||
#endif
|
||||
#if R2__UNIX__
|
||||
#if R2__UNIX__ && !__wasi__
|
||||
signal (SIGALRM, r2ai_http_sigint_handler);
|
||||
alarm (request->config.timeout); // Use configured timeout
|
||||
#endif
|
||||
int code = 0;
|
||||
int rlen = 0;
|
||||
char *result = r_socket_http_post (request->url, request->headers, request->data, &code, &rlen);
|
||||
#if R2__UNIX__
|
||||
#if R2__UNIX__ && !__wasi__
|
||||
alarm (0);
|
||||
#endif
|
||||
if (r2ai_http_interrupted) {
|
||||
@@ -32,14 +32,14 @@ static HttpResponse socket_http_get_with_interrupt(const HTTPRequest *request) {
|
||||
#if R2__WINDOWS__
|
||||
r_sys_setenv ("R2_CURL", "1");
|
||||
#endif
|
||||
#if R2__UNIX__
|
||||
#if R2__UNIX__ && !__wasi__
|
||||
signal (SIGALRM, r2ai_http_sigint_handler);
|
||||
alarm (request->config.timeout); // Use configured timeout
|
||||
#endif
|
||||
int code = 0;
|
||||
int rlen = 0;
|
||||
char *result = r_socket_http_get (request->url, request->headers, &code, &rlen);
|
||||
#if R2__UNIX__
|
||||
#if R2__UNIX__ && !__wasi__
|
||||
alarm (0);
|
||||
#endif
|
||||
if (r2ai_http_interrupted) {
|
||||
|
||||
Reference in new issue
Block a user