agentgui 1.0.11 → 1.0.13
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/acp-launcher.js +58 -61
- package/package.json +1 -1
package/acp-launcher.js
CHANGED
|
@@ -3,9 +3,35 @@ import fs from 'fs';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import os from 'os';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
6
|
+
// Common paths where claude-code-acp might be installed
|
|
7
|
+
const CLAUDE_CODE_ACP_PATHS = [
|
|
8
|
+
'/config/.gmweb/npm-global/bin/claude-code-acp',
|
|
9
|
+
'/usr/local/bin/claude-code-acp',
|
|
10
|
+
'/usr/bin/claude-code-acp',
|
|
11
|
+
path.join(os.homedir(), '.local/bin/claude-code-acp'),
|
|
12
|
+
path.join(os.homedir(), '.gmweb/npm-global/bin/claude-code-acp'),
|
|
13
|
+
'claude-code-acp', // fallback to PATH
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
// Common paths where opencode might be installed
|
|
17
|
+
const OPENCODE_PATHS = [
|
|
18
|
+
'/usr/local/bin/opencode',
|
|
19
|
+
'/usr/bin/opencode',
|
|
20
|
+
path.join(os.homedir(), '.local/bin/opencode'),
|
|
21
|
+
'opencode', // fallback to PATH
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
function findBinary(paths) {
|
|
25
|
+
for (const p of paths) {
|
|
26
|
+
try {
|
|
27
|
+
fs.accessSync(p, fs.constants.X_OK);
|
|
28
|
+
return p;
|
|
29
|
+
} catch (_) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
9
35
|
|
|
10
36
|
const RIPPLEUI_SYSTEM_PROMPT = `ALWAYS respond with HTML using RippleUI components. The chat renders HTML. Use: cards (class='card'), alerts (class='alert alert-info'), tables (class='table table-zebra'), badges (class='badge badge-primary'), buttons (class='btn btn-primary'). Wrap all responses in styled HTML with Tailwind CSS utility classes for layout.
|
|
11
37
|
|
|
@@ -75,9 +101,35 @@ export default class ACPConnection {
|
|
|
75
101
|
delete env.NODE_INSPECT;
|
|
76
102
|
delete env.NODE_DEBUG;
|
|
77
103
|
|
|
104
|
+
// Ensure npm global bin directories are in PATH
|
|
105
|
+
const npmGlobalBins = [
|
|
106
|
+
'/config/.gmweb/npm-global/bin',
|
|
107
|
+
path.join(os.homedir(), '.gmweb/npm-global/bin'),
|
|
108
|
+
path.join(os.homedir(), '.local/bin'),
|
|
109
|
+
'/usr/local/bin',
|
|
110
|
+
];
|
|
111
|
+
const currentPath = env.PATH || '';
|
|
112
|
+
const newPathEntries = npmGlobalBins.filter(p => !currentPath.includes(p));
|
|
113
|
+
if (newPathEntries.length > 0) {
|
|
114
|
+
env.PATH = [...newPathEntries, currentPath].join(':');
|
|
115
|
+
}
|
|
116
|
+
|
|
78
117
|
try {
|
|
79
|
-
|
|
80
|
-
|
|
118
|
+
let cmd;
|
|
119
|
+
let args;
|
|
120
|
+
if (agentType === 'opencode') {
|
|
121
|
+
cmd = findBinary(OPENCODE_PATHS);
|
|
122
|
+
args = ['acp'];
|
|
123
|
+
} else {
|
|
124
|
+
cmd = findBinary(CLAUDE_CODE_ACP_PATHS);
|
|
125
|
+
args = [];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!cmd) {
|
|
129
|
+
reject(new Error(`Could not find ${agentType} ACP binary. Please ensure ${agentType === 'opencode' ? 'opencode' : 'claude-code-acp'} is installed and in your PATH.`));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
81
133
|
this.child = spawn(cmd, args, { cwd, stdio: ['pipe', 'pipe', 'pipe'], env, shell: false });
|
|
82
134
|
} catch (err) {
|
|
83
135
|
reject(new Error(`Failed to spawn ACP: ${err.message}`));
|
|
@@ -219,62 +271,7 @@ export default class ACPConnection {
|
|
|
219
271
|
}
|
|
220
272
|
|
|
221
273
|
async _sendPrintPrompt(prompt) {
|
|
222
|
-
|
|
223
|
-
let apiKey;
|
|
224
|
-
try { apiKey = fs.readFileSync(API_KEY_PATH, 'utf-8').trim(); }
|
|
225
|
-
catch (e) { throw new Error('No API key found at ' + API_KEY_PATH); }
|
|
226
|
-
|
|
227
|
-
const body = JSON.stringify({
|
|
228
|
-
model: 'claude-sonnet-4-20250514',
|
|
229
|
-
max_tokens: 4096,
|
|
230
|
-
system: RIPPLEUI_SYSTEM_PROMPT,
|
|
231
|
-
messages: [{ role: 'user', content: text }],
|
|
232
|
-
stream: true,
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
const res = await fetch(API_URL, {
|
|
236
|
-
method: 'POST',
|
|
237
|
-
headers: {
|
|
238
|
-
'Content-Type': 'application/json',
|
|
239
|
-
'x-api-key': apiKey,
|
|
240
|
-
'anthropic-version': '2023-06-01',
|
|
241
|
-
},
|
|
242
|
-
body,
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
if (!res.ok) {
|
|
246
|
-
const errText = await res.text();
|
|
247
|
-
throw new Error(`Anthropic API ${res.status}: ${errText.substring(0, 200)}`);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
let fullText = '';
|
|
251
|
-
const reader = res.body.getReader();
|
|
252
|
-
const decoder = new TextDecoder();
|
|
253
|
-
let buf = '';
|
|
254
|
-
|
|
255
|
-
while (true) {
|
|
256
|
-
const { done, value } = await reader.read();
|
|
257
|
-
if (done) break;
|
|
258
|
-
buf += decoder.decode(value, { stream: true });
|
|
259
|
-
const lines = buf.split('\n');
|
|
260
|
-
buf = lines.pop() || '';
|
|
261
|
-
for (const line of lines) {
|
|
262
|
-
if (!line.startsWith('data: ')) continue;
|
|
263
|
-
const data = line.slice(6);
|
|
264
|
-
if (data === '[DONE]') continue;
|
|
265
|
-
try {
|
|
266
|
-
const evt = JSON.parse(data);
|
|
267
|
-
if (evt.type === 'content_block_delta' && evt.delta?.text) {
|
|
268
|
-
fullText += evt.delta.text;
|
|
269
|
-
if (this.onUpdate) {
|
|
270
|
-
this.onUpdate({ update: { sessionUpdate: 'agent_message_chunk', content: { text: evt.delta.text } } });
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
} catch (_) {}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return { stopReason: 'end_turn', result: fullText };
|
|
274
|
+
throw new Error('Claude Code uses OAuth and requires the ACP bridge. The fallback to direct API calls is not supported because OAuth tokens cannot be used with the Anthropic API directly. Please ensure claude-code-acp is available in your PATH.');
|
|
278
275
|
}
|
|
279
276
|
|
|
280
277
|
isRunning() {
|