Skip to content

Commit d7c2dac

Browse files
committed
Add decai timeout, zen provider, improve prompt, custom headers and more
1 parent d6c32f4 commit d7c2dac

12 files changed

Lines changed: 458 additions & 381 deletions

File tree

decai/AGENTS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Decompiler based AI
2+
3+
## Actions
4+
5+
* Build: `make -C src`
6+
* Install: `make user-install`
7+
* Test: `r2 -A -qc "decai -e api=ollamacloud;decai -e model=glm-5;decai -d @ 0x100000924" /bin/ls`

decai/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ decai -e host=http://localhost
5959
decai -e port=11434
6060
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:'
6161
decai -e ctxfile=
62+
decai -e headers=
6263
decai -e cmds=pdc
6364
decai -e cache=false
6465
decai -e lang=C
@@ -106,6 +107,14 @@ openapi
106107
...
107108
```
108109

110+
Extra headers can be injected globally with `decai -e headers=...` or from the
111+
environment with `DECAI_HEADERS` / `R2AI_HEADERS`. Use one header per line, for
112+
example:
113+
114+
```console
115+
[0x00406cac]> decai -e headers=Authorization: Bearer sk-123\nUser-Agent: curl/8.7.1
116+
```
117+
109118
### Example using a local model and ollama
110119

111120
For example, if Ollama serves model codegeex4:latest (`ollama ls`), set decai

decai/decai.r2.js

Lines changed: 40 additions & 35 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

decai/src/apiKeys.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const PROVIDER_ENV_MAP: Record<string, string> = {
1111
xai: "XAI_API_KEY",
1212
ollama: "OLLAMA_API_KEY",
1313
ollamacloud: "OLLAMA_API_KEY",
14+
opencode: "OPENCODE_API_KEY",
15+
zen: "OPENCODE_API_KEY",
1416
};
1517

1618
export function getApiKey(provider: string, envvar: string): ApiKeyResult {

decai/src/config.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ConfigHandlers } from "./types";
22
import { state } from "./state";
33
import { listModelsFor } from "./models";
44
import { listProviders } from "./providers";
5+
import { formatHeaders, parseHeaders } from "./headers";
56

67
function parseBoolean(value: string): boolean {
78
return value === "true" || value === "1";
@@ -53,6 +54,12 @@ export const configHandlers: ConfigHandlers = {
5354
state.debug = parseBoolean(v);
5455
},
5556
},
57+
timeout: {
58+
get: () => state.timeout,
59+
set: (v: string) => {
60+
state.timeout = Math.max(0, parseInt(v, 10) || 0);
61+
},
62+
},
5663
api: {
5764
get: () => state.api,
5865
set: (v: string) => {
@@ -118,6 +125,12 @@ export const configHandlers: ConfigHandlers = {
118125
state.baseurl = v;
119126
},
120127
},
128+
headers: {
129+
get: () => formatHeaders(state.extraHeaders),
130+
set: (v: string) => {
131+
state.extraHeaders = parseHeaders(v);
132+
},
133+
},
121134
maxtokens: {
122135
get: () => state.maxInputTokens,
123136
set: (v: string) => {

decai/src/constants.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ export const VERSION = "1.3.2";
22
export const COMMAND = "decai";
33

44
export const DEFAULT_PROMPT =
5-
"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";
5+
"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.";
66

77
export const HELP_TEXT = {
88
decai: `# Using Decai
@@ -27,6 +27,9 @@ These are the most recommended models for decompiling in local:
2727
2828
## Common Options
2929
* 'decai -e baseurl=<url>' override default host and port for API endpoint (e.g., 'http://localhost:11434')
30+
* 'decai -e headers=Authorization: Bearer ...\\nUser-Agent: curl/8.7.1' add or override HTTP headers
31+
* "export DECAI_HEADERS='Authorization: Bearer ...\\nUser-Agent: curl/8.7.1'" set extra headers from the environment
32+
* 'decai -e timeout=0' disable curl timeouts, or set a positive number of seconds
3033
3134
* 'decai -e deterministic=true' to remove randomness from decompilation responses
3235
* 'decai -e lang=Python' to output the decompilation in Python instead of C

decai/src/http.ts

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { HttpResponse } from "./types";
2+
import { state } from "./state";
3+
import { mergeHeaders } from "./headers";
24
import { debugLog } from "./utils";
35

46
interface HttpRequestOptions {
@@ -14,45 +16,42 @@ function shellEscape(arg: string): string {
1416

1517
function executeCurl(options: HttpRequestOptions): string {
1618
const { method, url, headers, payload } = options;
17-
const cmdParts = ["curl", "-s", shellEscape(url)];
18-
headers.forEach((h) => cmdParts.push("-H", shellEscape(h)));
19-
cmdParts.push("-H", shellEscape("Content-Type: application/json"));
19+
const cmdParts = ["curl", "-s"];
20+
if (state.timeout > 0) {
21+
cmdParts.push("--max-time", String(state.timeout));
22+
}
23+
const requestHeaders = mergeHeaders(
24+
["Content-Type: application/json"],
25+
headers,
26+
);
27+
requestHeaders.forEach((h) => cmdParts.push("-H", shellEscape(h)));
2028

2129
if (method === "POST") {
2230
if (!payload) throw new Error("Payload required for POST requests");
2331
const tmpfile = r2.fdump(payload);
24-
cmdParts.push("-d", shellEscape(`@${tmpfile}`));
25-
const cmd = cmdParts.join(" ") + " && rm " + shellEscape(tmpfile);
32+
cmdParts.push("--data-binary", "@-", shellEscape(url));
33+
const cmd = cmdParts.join(" ") + " < " + shellEscape(tmpfile) +
34+
" && rm " + shellEscape(tmpfile);
2635
debugLog(cmd);
2736
return r2.syscmds(cmd);
2837
} else {
38+
cmdParts.push(shellEscape(url));
2939
const cmd = cmdParts.join(" ");
3040
debugLog(cmd);
3141
return r2.syscmds(cmd);
3242
}
3343
}
3444

35-
function parseJson<T>(output: string): T {
36-
if (output === "") {
37-
throw new Error("empty response");
38-
}
39-
try {
40-
return JSON.parse(output) as T;
41-
} catch (e) {
42-
const err = e as Error;
43-
console.error("output:", output);
44-
console.error(err, err.stack);
45-
throw new Error(err.message || "JSON parse error");
46-
}
47-
}
48-
4945
export function httpRequest(options: HttpRequestOptions): HttpResponse {
5046
try {
51-
const output = executeCurl(options);
47+
const output = executeCurl(options).trim();
48+
if (output === "") {
49+
return { error: "empty response" };
50+
}
5251
try {
53-
return parseJson<HttpResponse>(output);
54-
} catch (e) {
55-
return { error: (e as Error).message };
52+
return JSON.parse(output) as HttpResponse;
53+
} catch {
54+
return { error: output, rawOutput: output };
5655
}
5756
} catch (e) {
5857
const err = e as Error;

decai/src/httptask.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
### 3. HTTP Request Patterns
2+
3+
**Location**: `http.ts`, used throughout providers and models
4+
5+
**Problems**:
6+
7+
- Two different curl methods (`curlArgs` vs `curlFile`) with similar but
8+
separate logic
9+
- Error handling in HTTP layer vs. application layer
10+
- No centralized request/response abstraction

decai/src/models.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getProvider } from "./providers";
1+
import { getProvider, listModels } from "./providers";
22

33
export function listModelsFor(api: string): void {
44
const providerConfig = getProvider(api);
@@ -9,10 +9,7 @@ export function listModelsFor(api: string): void {
99
}
1010

1111
try {
12-
let models = "";
13-
if (providerConfig.listModelsCallback) {
14-
models = providerConfig.listModelsCallback(providerConfig);
15-
}
12+
const models = listModels(api);
1613

1714
if (models) {
1815
console.log(models);

0 commit comments

Comments
 (0)