qafai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/qaf.js +2 -0
- package/dist/agent.d.ts +24 -0
- package/dist/agent.js +254 -0
- package/dist/agent.js.map +1 -0
- package/dist/client.d.ts +28 -0
- package/dist/client.js +140 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.js +63 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +272 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/fileEdit.d.ts +4 -0
- package/dist/tools/fileEdit.js +57 -0
- package/dist/tools/fileEdit.js.map +1 -0
- package/dist/tools/fileRead.d.ts +4 -0
- package/dist/tools/fileRead.js +53 -0
- package/dist/tools/fileRead.js.map +1 -0
- package/dist/tools/fileSearch.d.ts +4 -0
- package/dist/tools/fileSearch.js +113 -0
- package/dist/tools/fileSearch.js.map +1 -0
- package/dist/tools/fileWrite.d.ts +4 -0
- package/dist/tools/fileWrite.js +47 -0
- package/dist/tools/fileWrite.js.map +1 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.js +13 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +28 -0
- package/dist/tools/registry.js +46 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/shellExec.d.ts +4 -0
- package/dist/tools/shellExec.js +85 -0
- package/dist/tools/shellExec.js.map +1 -0
- package/dist/ui.d.ts +17 -0
- package/dist/ui.js +156 -0
- package/dist/ui.js.map +1 -0
- package/package.json +47 -0
package/bin/qaf.js
ADDED
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side agent loop for QafAI CLI.
|
|
3
|
+
* Mirrors backend/agent.py: tool_call parsing, result formatting, context compression.
|
|
4
|
+
* All tool execution happens locally on the user's machine.
|
|
5
|
+
*/
|
|
6
|
+
import { QafClient } from './client.js';
|
|
7
|
+
export declare class AgentLoop {
|
|
8
|
+
private client;
|
|
9
|
+
private model;
|
|
10
|
+
private temperature;
|
|
11
|
+
private contextLimit;
|
|
12
|
+
private messages;
|
|
13
|
+
private iteration;
|
|
14
|
+
private totalToolCalls;
|
|
15
|
+
private toolsUsed;
|
|
16
|
+
private autoConfirm;
|
|
17
|
+
constructor(client: QafClient, model?: string, temperature?: number, contextLimit?: number);
|
|
18
|
+
setModel(model: string): void;
|
|
19
|
+
clearHistory(): void;
|
|
20
|
+
run(userMessage: string): Promise<void>;
|
|
21
|
+
private confirmTool;
|
|
22
|
+
private estimateTokens;
|
|
23
|
+
private compressIfNeeded;
|
|
24
|
+
}
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side agent loop for QafAI CLI.
|
|
3
|
+
* Mirrors backend/agent.py: tool_call parsing, result formatting, context compression.
|
|
4
|
+
* All tool execution happens locally on the user's machine.
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
import { loadConfig } from './config.js';
|
|
9
|
+
import { executeTool, getToolsPrompt, toolRequiresConfirmation } from './tools/index.js';
|
|
10
|
+
import * as ui from './ui.js';
|
|
11
|
+
// Same regex as backend/agent.py
|
|
12
|
+
const TOOL_CALL_PATTERN = /<tool_call>\s*([\s\S]*?)\s*<\/tool_call>/g;
|
|
13
|
+
// Agent limits
|
|
14
|
+
const MAX_ITERATIONS = 10;
|
|
15
|
+
const MAX_TOOL_CALLS = 30;
|
|
16
|
+
const TIMEOUT = 180; // seconds
|
|
17
|
+
function parseToolCalls(text) {
|
|
18
|
+
const calls = [];
|
|
19
|
+
TOOL_CALL_PATTERN.lastIndex = 0;
|
|
20
|
+
let match;
|
|
21
|
+
while ((match = TOOL_CALL_PATTERN.exec(text)) !== null) {
|
|
22
|
+
try {
|
|
23
|
+
const data = JSON.parse(match[1]);
|
|
24
|
+
if (data.name) {
|
|
25
|
+
calls.push({
|
|
26
|
+
name: data.name,
|
|
27
|
+
arguments: data.arguments || {},
|
|
28
|
+
raw: match[0],
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return calls;
|
|
37
|
+
}
|
|
38
|
+
function buildSystemPrompt() {
|
|
39
|
+
return `You are QafAI Agent, an AI coding assistant running on the user's local machine.
|
|
40
|
+
You can read, write, and edit files, search the codebase, and run shell commands.
|
|
41
|
+
|
|
42
|
+
Working directory: ${process.cwd()}
|
|
43
|
+
|
|
44
|
+
## Available Tools
|
|
45
|
+
|
|
46
|
+
To use a tool, write a tool call in this exact format:
|
|
47
|
+
<tool_call>{"name": "tool_name", "arguments": {"param": "value"}}</tool_call>
|
|
48
|
+
|
|
49
|
+
${getToolsPrompt()}
|
|
50
|
+
|
|
51
|
+
## Rules
|
|
52
|
+
1. Use tools when needed to accomplish the user's request
|
|
53
|
+
2. You can use multiple tools in one response
|
|
54
|
+
3. Wait for tool results before giving your final answer
|
|
55
|
+
4. After getting tool results, provide a helpful summary
|
|
56
|
+
5. Don't repeat tool results verbatim — summarize and explain
|
|
57
|
+
6. If a tool fails, inform the user and try another approach
|
|
58
|
+
7. For file edits, provide enough context in old_string to ensure uniqueness
|
|
59
|
+
8. Always use absolute paths or paths relative to the working directory
|
|
60
|
+
9. Prefer reading files before editing them to understand context`;
|
|
61
|
+
}
|
|
62
|
+
export class AgentLoop {
|
|
63
|
+
client;
|
|
64
|
+
model;
|
|
65
|
+
temperature;
|
|
66
|
+
contextLimit;
|
|
67
|
+
messages;
|
|
68
|
+
iteration = 0;
|
|
69
|
+
totalToolCalls = 0;
|
|
70
|
+
toolsUsed = [];
|
|
71
|
+
autoConfirm;
|
|
72
|
+
constructor(client, model, temperature = 0.7, contextLimit = 131072) {
|
|
73
|
+
this.client = client;
|
|
74
|
+
this.model = model;
|
|
75
|
+
this.temperature = temperature;
|
|
76
|
+
this.contextLimit = contextLimit;
|
|
77
|
+
this.autoConfirm = loadConfig().auto_confirm;
|
|
78
|
+
this.messages = [{ role: 'system', content: buildSystemPrompt() }];
|
|
79
|
+
}
|
|
80
|
+
setModel(model) {
|
|
81
|
+
this.model = model;
|
|
82
|
+
}
|
|
83
|
+
clearHistory() {
|
|
84
|
+
const system = this.messages[0];
|
|
85
|
+
this.messages = system ? [system] : [{ role: 'system', content: buildSystemPrompt() }];
|
|
86
|
+
this.iteration = 0;
|
|
87
|
+
this.totalToolCalls = 0;
|
|
88
|
+
this.toolsUsed = [];
|
|
89
|
+
}
|
|
90
|
+
async run(userMessage) {
|
|
91
|
+
this.messages.push({ role: 'user', content: userMessage });
|
|
92
|
+
const startTime = Date.now();
|
|
93
|
+
this.iteration = 0;
|
|
94
|
+
this.totalToolCalls = 0;
|
|
95
|
+
this.toolsUsed = [];
|
|
96
|
+
while (this.iteration < MAX_ITERATIONS) {
|
|
97
|
+
// Timeout
|
|
98
|
+
if ((Date.now() - startTime) / 1000 >= TIMEOUT) {
|
|
99
|
+
ui.showError('Agent timeout reached.');
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
// Compress context if needed
|
|
103
|
+
this.compressIfNeeded();
|
|
104
|
+
this.iteration++;
|
|
105
|
+
// Call model and stream response
|
|
106
|
+
ui.startThinking();
|
|
107
|
+
let fullResponse = '';
|
|
108
|
+
let hadError = false;
|
|
109
|
+
try {
|
|
110
|
+
for await (const event of this.client.streamInference(this.messages, this.model, this.temperature)) {
|
|
111
|
+
if (event.chunk) {
|
|
112
|
+
fullResponse += event.chunk;
|
|
113
|
+
ui.writeChunk(event.chunk);
|
|
114
|
+
}
|
|
115
|
+
else if (event.error) {
|
|
116
|
+
ui.stopThinking();
|
|
117
|
+
ui.showError(event.error);
|
|
118
|
+
hadError = true;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
else if (event.done) {
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
ui.stopThinking();
|
|
128
|
+
ui.showError(err instanceof Error ? err.message : String(err));
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
ui.finishResponse();
|
|
132
|
+
if (hadError)
|
|
133
|
+
break;
|
|
134
|
+
// Parse tool calls
|
|
135
|
+
const toolCalls = parseToolCalls(fullResponse);
|
|
136
|
+
if (toolCalls.length === 0) {
|
|
137
|
+
// No tools — final answer
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
// Append assistant message
|
|
141
|
+
this.messages.push({ role: 'assistant', content: fullResponse });
|
|
142
|
+
// Execute tools
|
|
143
|
+
const toolResultTexts = [];
|
|
144
|
+
for (const call of toolCalls) {
|
|
145
|
+
if (this.totalToolCalls >= MAX_TOOL_CALLS) {
|
|
146
|
+
ui.showError('Max tool calls reached.');
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
ui.showToolCall(call.name, call.arguments);
|
|
150
|
+
// Confirmation flow
|
|
151
|
+
if (toolRequiresConfirmation(call.name) && !this.autoConfirm) {
|
|
152
|
+
const approved = await this.confirmTool(call.name, call.arguments);
|
|
153
|
+
if (!approved) {
|
|
154
|
+
const result = { success: false, error: 'User rejected this action.' };
|
|
155
|
+
ui.showToolResult(call.name, result);
|
|
156
|
+
toolResultTexts.push(`<tool_result name="${call.name}">\n${JSON.stringify(result)}\n</tool_result>`);
|
|
157
|
+
this.totalToolCalls++;
|
|
158
|
+
this.toolsUsed.push(call.name);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Execute
|
|
163
|
+
this.totalToolCalls++;
|
|
164
|
+
this.toolsUsed.push(call.name);
|
|
165
|
+
const result = await executeTool(call.name, call.arguments);
|
|
166
|
+
ui.showToolResult(call.name, result);
|
|
167
|
+
// Clean internal fields
|
|
168
|
+
const clean = {};
|
|
169
|
+
for (const [k, v] of Object.entries(result)) {
|
|
170
|
+
if (!k.startsWith('_'))
|
|
171
|
+
clean[k] = v;
|
|
172
|
+
}
|
|
173
|
+
let resultText = JSON.stringify(clean);
|
|
174
|
+
if (resultText.length > 4000)
|
|
175
|
+
resultText = resultText.slice(0, 4000) + '...';
|
|
176
|
+
toolResultTexts.push(`<tool_result name="${call.name}">\n${resultText}\n</tool_result>`);
|
|
177
|
+
}
|
|
178
|
+
// Feed results back
|
|
179
|
+
if (toolResultTexts.length > 0) {
|
|
180
|
+
this.messages.push({
|
|
181
|
+
role: 'user',
|
|
182
|
+
content: toolResultTexts.join('\n\n')
|
|
183
|
+
+ '\n\nBased on the tool results above, provide your response. Only use another tool if necessary.',
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
if (this.totalToolCalls >= MAX_TOOL_CALLS)
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
const duration = (Date.now() - startTime) / 1000;
|
|
190
|
+
if (this.totalToolCalls > 0) {
|
|
191
|
+
ui.showAgentDone(this.iteration, this.totalToolCalls, duration);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async confirmTool(name, args) {
|
|
195
|
+
if (name === 'shell_exec') {
|
|
196
|
+
return ui.showCommandConfirm(String(args.command || ''));
|
|
197
|
+
}
|
|
198
|
+
if (name === 'file_write' || name === 'file_edit') {
|
|
199
|
+
const path = String(args.path || '');
|
|
200
|
+
if (!path)
|
|
201
|
+
return ui.confirm(`Allow ${name}?`);
|
|
202
|
+
const absPath = resolve(path);
|
|
203
|
+
let oldContent = null;
|
|
204
|
+
if (existsSync(absPath)) {
|
|
205
|
+
try {
|
|
206
|
+
oldContent = readFileSync(absPath, 'utf-8');
|
|
207
|
+
}
|
|
208
|
+
catch { /* */ }
|
|
209
|
+
}
|
|
210
|
+
if (name === 'file_write') {
|
|
211
|
+
return ui.showFileDiff(absPath, oldContent, String(args.content || ''));
|
|
212
|
+
}
|
|
213
|
+
if (name === 'file_edit' && oldContent !== null) {
|
|
214
|
+
const oldStr = String(args.old_string || '');
|
|
215
|
+
const newStr = String(args.new_string || '');
|
|
216
|
+
if (oldContent.includes(oldStr)) {
|
|
217
|
+
const preview = oldContent.replace(oldStr, newStr);
|
|
218
|
+
return ui.showFileDiff(absPath, oldContent, preview);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return ui.confirm(`Allow ${name} on ${path}?`);
|
|
222
|
+
}
|
|
223
|
+
return ui.confirm(`Allow ${name}?`);
|
|
224
|
+
}
|
|
225
|
+
estimateTokens(text) {
|
|
226
|
+
let arabic = 0;
|
|
227
|
+
for (const c of text) {
|
|
228
|
+
if (c >= '\u0600' && c <= '\u06FF')
|
|
229
|
+
arabic++;
|
|
230
|
+
}
|
|
231
|
+
return arabic + Math.floor((text.length - arabic) / 4);
|
|
232
|
+
}
|
|
233
|
+
compressIfNeeded() {
|
|
234
|
+
const threshold = Math.floor(this.contextLimit * 0.7);
|
|
235
|
+
let total = 0;
|
|
236
|
+
for (const m of this.messages) {
|
|
237
|
+
total += this.estimateTokens(m.content);
|
|
238
|
+
}
|
|
239
|
+
if (total <= threshold || this.messages.length <= 5)
|
|
240
|
+
return;
|
|
241
|
+
const system = this.messages[0];
|
|
242
|
+
const middle = this.messages.slice(1, -4);
|
|
243
|
+
const recent = this.messages.slice(-4);
|
|
244
|
+
const summaryParts = middle.slice(-6).map(m => `[${m.role}]: ${m.content.slice(0, 200)}`);
|
|
245
|
+
const summary = 'Previous conversation summary:\n' + summaryParts.join('\n');
|
|
246
|
+
this.messages = [
|
|
247
|
+
system,
|
|
248
|
+
{ role: 'user', content: summary },
|
|
249
|
+
{ role: 'assistant', content: 'Understood. I\'ll continue helping.' },
|
|
250
|
+
...recent,
|
|
251
|
+
];
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for QafAI inference API.
|
|
3
|
+
* SSE streaming via native fetch, manual event parsing.
|
|
4
|
+
*/
|
|
5
|
+
export declare class QafClientError extends Error {
|
|
6
|
+
statusCode: number;
|
|
7
|
+
constructor(message: string, statusCode?: number);
|
|
8
|
+
}
|
|
9
|
+
export interface SSEEvent {
|
|
10
|
+
chunk?: string;
|
|
11
|
+
done?: boolean;
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class QafClient {
|
|
15
|
+
private serverUrl;
|
|
16
|
+
private apiKey;
|
|
17
|
+
constructor(serverUrl?: string, apiKey?: string);
|
|
18
|
+
private get headers();
|
|
19
|
+
streamInference(messages: Array<{
|
|
20
|
+
role: string;
|
|
21
|
+
content: string;
|
|
22
|
+
}>, model?: string, temperature?: number, maxTokens?: number): AsyncGenerator<SSEEvent>;
|
|
23
|
+
listModels(): Promise<{
|
|
24
|
+
models: Array<Record<string, unknown>>;
|
|
25
|
+
default: string;
|
|
26
|
+
}>;
|
|
27
|
+
healthCheck(): Promise<boolean>;
|
|
28
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for QafAI inference API.
|
|
3
|
+
* SSE streaming via native fetch, manual event parsing.
|
|
4
|
+
*/
|
|
5
|
+
import { getApiKey, getServerUrl } from './config.js';
|
|
6
|
+
export class QafClientError extends Error {
|
|
7
|
+
statusCode;
|
|
8
|
+
constructor(message, statusCode = 0) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.statusCode = statusCode;
|
|
11
|
+
this.name = 'QafClientError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class QafClient {
|
|
15
|
+
serverUrl;
|
|
16
|
+
apiKey;
|
|
17
|
+
constructor(serverUrl, apiKey) {
|
|
18
|
+
this.serverUrl = serverUrl || getServerUrl();
|
|
19
|
+
this.apiKey = apiKey || getApiKey();
|
|
20
|
+
}
|
|
21
|
+
get headers() {
|
|
22
|
+
return {
|
|
23
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
'User-Agent': 'qaf-agent/0.1.0',
|
|
26
|
+
'Accept': 'text/event-stream',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
async *streamInference(messages, model, temperature = 0.7, maxTokens) {
|
|
30
|
+
const payload = { messages, temperature };
|
|
31
|
+
if (model)
|
|
32
|
+
payload.model = model;
|
|
33
|
+
if (maxTokens)
|
|
34
|
+
payload.max_tokens = maxTokens;
|
|
35
|
+
let response;
|
|
36
|
+
try {
|
|
37
|
+
response = await fetch(`${this.serverUrl}/api/inference`, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: this.headers,
|
|
40
|
+
body: JSON.stringify(payload),
|
|
41
|
+
signal: AbortSignal.timeout(120_000),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
46
|
+
if (msg.includes('fetch failed') || msg.includes('ECONNREFUSED')) {
|
|
47
|
+
throw new QafClientError(`Cannot connect to ${this.serverUrl}. Check your server URL and network.`);
|
|
48
|
+
}
|
|
49
|
+
if (msg.includes('TimeoutError') || msg.includes('aborted')) {
|
|
50
|
+
throw new QafClientError('Request timed out. The server may be overloaded.');
|
|
51
|
+
}
|
|
52
|
+
throw new QafClientError(`Connection error: ${msg}`);
|
|
53
|
+
}
|
|
54
|
+
if (response.status === 401) {
|
|
55
|
+
throw new QafClientError("Invalid API key. Run 'qaf init' to configure.", 401);
|
|
56
|
+
}
|
|
57
|
+
if (response.status === 429) {
|
|
58
|
+
throw new QafClientError('Rate limit exceeded. Please wait.', 429);
|
|
59
|
+
}
|
|
60
|
+
if (response.status >= 400) {
|
|
61
|
+
let detail = 'Server error';
|
|
62
|
+
try {
|
|
63
|
+
const body = await response.json();
|
|
64
|
+
detail = body.detail || detail;
|
|
65
|
+
}
|
|
66
|
+
catch { /* ignore */ }
|
|
67
|
+
throw new QafClientError(`HTTP ${response.status}: ${detail}`, response.status);
|
|
68
|
+
}
|
|
69
|
+
if (!response.body) {
|
|
70
|
+
throw new QafClientError('Empty response body');
|
|
71
|
+
}
|
|
72
|
+
const reader = response.body.getReader();
|
|
73
|
+
const decoder = new TextDecoder();
|
|
74
|
+
let buffer = '';
|
|
75
|
+
try {
|
|
76
|
+
while (true) {
|
|
77
|
+
const { done, value } = await reader.read();
|
|
78
|
+
if (done)
|
|
79
|
+
break;
|
|
80
|
+
buffer += decoder.decode(value, { stream: true });
|
|
81
|
+
while (buffer.includes('\n\n')) {
|
|
82
|
+
const idx = buffer.indexOf('\n\n');
|
|
83
|
+
const eventStr = buffer.slice(0, idx);
|
|
84
|
+
buffer = buffer.slice(idx + 2);
|
|
85
|
+
for (const line of eventStr.split('\n')) {
|
|
86
|
+
const trimmed = line.trim();
|
|
87
|
+
if (!trimmed.startsWith('data: '))
|
|
88
|
+
continue;
|
|
89
|
+
const dataStr = trimmed.slice(6);
|
|
90
|
+
if (dataStr === '[DONE]') {
|
|
91
|
+
yield { done: true };
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const data = JSON.parse(dataStr);
|
|
96
|
+
yield data;
|
|
97
|
+
if (data.done)
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
reader.releaseLock();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async listModels() {
|
|
112
|
+
let response;
|
|
113
|
+
try {
|
|
114
|
+
response = await fetch(`${this.serverUrl}/api/inference/models`, {
|
|
115
|
+
headers: this.headers,
|
|
116
|
+
signal: AbortSignal.timeout(10_000),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
throw new QafClientError(`Cannot connect to ${this.serverUrl}`);
|
|
121
|
+
}
|
|
122
|
+
if (response.status === 401)
|
|
123
|
+
throw new QafClientError('Invalid API key.', 401);
|
|
124
|
+
if (response.status >= 400)
|
|
125
|
+
throw new QafClientError(`HTTP ${response.status}`, response.status);
|
|
126
|
+
return response.json();
|
|
127
|
+
}
|
|
128
|
+
async healthCheck() {
|
|
129
|
+
try {
|
|
130
|
+
const res = await fetch(`${this.serverUrl}/api/health`, {
|
|
131
|
+
signal: AbortSignal.timeout(5_000),
|
|
132
|
+
});
|
|
133
|
+
return res.status === 200;
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config management for QafAI CLI Agent.
|
|
3
|
+
* Manages ~/.qafai/config.json with server URL, API key, model preferences.
|
|
4
|
+
*/
|
|
5
|
+
export interface Config {
|
|
6
|
+
server_url: string;
|
|
7
|
+
api_key: string;
|
|
8
|
+
model: string;
|
|
9
|
+
temperature: number;
|
|
10
|
+
max_iterations: number;
|
|
11
|
+
timeout: number;
|
|
12
|
+
auto_confirm: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare const HISTORY_FILE: string;
|
|
15
|
+
export declare function loadConfig(): Config;
|
|
16
|
+
export declare function saveConfig(config: Partial<Config>): void;
|
|
17
|
+
export declare function getApiKey(): string;
|
|
18
|
+
export declare function getServerUrl(): string;
|
|
19
|
+
export declare function getModel(): string;
|
|
20
|
+
export declare function isConfigured(): boolean;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config management for QafAI CLI Agent.
|
|
3
|
+
* Manages ~/.qafai/config.json with server URL, API key, model preferences.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from 'node:fs';
|
|
6
|
+
import { homedir } from 'node:os';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
const CONFIG_DIR = join(homedir(), '.qafai');
|
|
9
|
+
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
10
|
+
export const HISTORY_FILE = join(CONFIG_DIR, 'history.json');
|
|
11
|
+
const DEFAULTS = {
|
|
12
|
+
server_url: 'https://qafai.net',
|
|
13
|
+
api_key: '',
|
|
14
|
+
model: '',
|
|
15
|
+
temperature: 0.7,
|
|
16
|
+
max_iterations: 10,
|
|
17
|
+
timeout: 180,
|
|
18
|
+
auto_confirm: false,
|
|
19
|
+
};
|
|
20
|
+
function ensureConfigDir() {
|
|
21
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
22
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function loadConfig() {
|
|
26
|
+
const config = { ...DEFAULTS };
|
|
27
|
+
if (existsSync(CONFIG_FILE)) {
|
|
28
|
+
try {
|
|
29
|
+
const stored = JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'));
|
|
30
|
+
Object.assign(config, stored);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Ignore corrupt config
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return config;
|
|
37
|
+
}
|
|
38
|
+
export function saveConfig(config) {
|
|
39
|
+
ensureConfigDir();
|
|
40
|
+
const current = loadConfig();
|
|
41
|
+
const merged = { ...current, ...config };
|
|
42
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2), 'utf-8');
|
|
43
|
+
try {
|
|
44
|
+
chmodSync(CONFIG_FILE, 0o600);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Windows may not support chmod
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function getApiKey() {
|
|
51
|
+
return process.env['QAFAI_API_KEY'] || loadConfig().api_key;
|
|
52
|
+
}
|
|
53
|
+
export function getServerUrl() {
|
|
54
|
+
const url = process.env['QAFAI_SERVER_URL'] || loadConfig().server_url || DEFAULTS.server_url;
|
|
55
|
+
return url.replace(/\/+$/, '');
|
|
56
|
+
}
|
|
57
|
+
export function getModel() {
|
|
58
|
+
return process.env['QAFAI_MODEL'] || loadConfig().model;
|
|
59
|
+
}
|
|
60
|
+
export function isConfigured() {
|
|
61
|
+
return !!getApiKey();
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAYjC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AACpD,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;AAE7D,MAAM,QAAQ,GAAW;IACvB,UAAU,EAAE,mBAAmB;IAC/B,OAAO,EAAE,EAAE;IACX,KAAK,EAAE,EAAE;IACT,WAAW,EAAE,GAAG;IAChB,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE,GAAG;IACZ,YAAY,EAAE,KAAK;CACpB,CAAC;AAEF,SAAS,eAAe;IACtB,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC/B,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;YAC9D,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAuB;IAChD,eAAe,EAAE,CAAC;IAClB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,MAAM,EAAE,CAAC;IACzC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACrE,IAAI,CAAC;QACH,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,gCAAgC;IAClC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,UAAU,EAAE,CAAC,OAAO,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,UAAU,EAAE,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU,CAAC;IAC9F,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,UAAU,EAAE,CAAC,KAAK,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC;AACvB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI entry point for QafAI Agent.
|
|
3
|
+
*
|
|
4
|
+
* npm install -g qafai
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* qaf "fix the bug in main.py" — One-shot agent
|
|
8
|
+
* qaf — Interactive REPL
|
|
9
|
+
* qaf --model groq:kimi-k2 "..." — Override model
|
|
10
|
+
* qaf init — Setup wizard
|
|
11
|
+
* qaf models — List available models
|
|
12
|
+
*/
|
|
13
|
+
import './tools/index.js';
|