sessioncast-cli 2.2.0 → 2.2.2
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.
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LlmService = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
4
5
|
class LlmService {
|
|
5
6
|
constructor(config) {
|
|
6
7
|
this.config = config || {
|
|
@@ -32,6 +33,10 @@ class LlmService {
|
|
|
32
33
|
return await this.callOllama(actualModel, messages || [], temperature, maxTokens);
|
|
33
34
|
case 'openai':
|
|
34
35
|
return await this.callOpenAi(actualModel, messages || [], temperature, maxTokens);
|
|
36
|
+
case 'anthropic':
|
|
37
|
+
return await this.callAnthropic(actualModel, messages || [], temperature, maxTokens);
|
|
38
|
+
case 'claude-code':
|
|
39
|
+
return await this.callClaudeCode(actualModel, messages || [], maxTokens);
|
|
35
40
|
default:
|
|
36
41
|
return {
|
|
37
42
|
id: '',
|
|
@@ -152,5 +157,202 @@ class LlmService {
|
|
|
152
157
|
}
|
|
153
158
|
return await response.json();
|
|
154
159
|
}
|
|
160
|
+
async callAnthropic(model, messages, temperature, maxTokens) {
|
|
161
|
+
const baseUrl = this.config.baseUrl || 'https://api.anthropic.com';
|
|
162
|
+
const apiKey = this.config.apiKey;
|
|
163
|
+
if (!apiKey) {
|
|
164
|
+
return {
|
|
165
|
+
id: '',
|
|
166
|
+
object: 'error',
|
|
167
|
+
created: Date.now(),
|
|
168
|
+
model: '',
|
|
169
|
+
choices: [],
|
|
170
|
+
error: {
|
|
171
|
+
message: 'Anthropic API key not configured',
|
|
172
|
+
type: 'configuration_error'
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
// Separate system message from user/assistant messages
|
|
177
|
+
let systemPrompt;
|
|
178
|
+
const chatMessages = [];
|
|
179
|
+
for (const msg of messages) {
|
|
180
|
+
if (msg.role === 'system') {
|
|
181
|
+
systemPrompt = msg.content;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
chatMessages.push({ role: msg.role, content: msg.content });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const requestBody = {
|
|
188
|
+
model,
|
|
189
|
+
messages: chatMessages,
|
|
190
|
+
max_tokens: maxTokens || 1024
|
|
191
|
+
};
|
|
192
|
+
if (systemPrompt) {
|
|
193
|
+
requestBody.system = systemPrompt;
|
|
194
|
+
}
|
|
195
|
+
if (temperature !== undefined) {
|
|
196
|
+
requestBody.temperature = temperature;
|
|
197
|
+
}
|
|
198
|
+
const response = await fetch(`${baseUrl}/v1/messages`, {
|
|
199
|
+
method: 'POST',
|
|
200
|
+
headers: {
|
|
201
|
+
'Content-Type': 'application/json',
|
|
202
|
+
'x-api-key': apiKey,
|
|
203
|
+
'anthropic-version': '2023-06-01'
|
|
204
|
+
},
|
|
205
|
+
body: JSON.stringify(requestBody)
|
|
206
|
+
});
|
|
207
|
+
if (!response.ok) {
|
|
208
|
+
const errorBody = await response.text();
|
|
209
|
+
throw new Error(`Anthropic returned status ${response.status}: ${errorBody}`);
|
|
210
|
+
}
|
|
211
|
+
const anthropicResponse = await response.json();
|
|
212
|
+
return this.convertAnthropicToOpenAiFormat(anthropicResponse, model);
|
|
213
|
+
}
|
|
214
|
+
async callClaudeCode(model, messages, maxTokens) {
|
|
215
|
+
// Build prompt from messages
|
|
216
|
+
const parts = [];
|
|
217
|
+
for (const msg of messages) {
|
|
218
|
+
if (msg.role === 'system') {
|
|
219
|
+
parts.push(`[System] ${msg.content}`);
|
|
220
|
+
}
|
|
221
|
+
else if (msg.role === 'user') {
|
|
222
|
+
parts.push(msg.content);
|
|
223
|
+
}
|
|
224
|
+
else if (msg.role === 'assistant') {
|
|
225
|
+
parts.push(`[Assistant] ${msg.content}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const prompt = parts.join('\n\n');
|
|
229
|
+
const args = ['-p', '--output-format', 'json', '--no-session-persistence'];
|
|
230
|
+
// Map model names: 'sonnet', 'opus', 'haiku' or full model ID
|
|
231
|
+
if (model && model !== 'default') {
|
|
232
|
+
args.push('--model', model);
|
|
233
|
+
}
|
|
234
|
+
return new Promise((resolve) => {
|
|
235
|
+
const env = { ...process.env };
|
|
236
|
+
delete env.CLAUDECODE;
|
|
237
|
+
delete env.CLAUDE_CODE;
|
|
238
|
+
const proc = (0, child_process_1.spawn)('claude', args, {
|
|
239
|
+
env,
|
|
240
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
241
|
+
});
|
|
242
|
+
// Send prompt via stdin
|
|
243
|
+
proc.stdin.write(prompt);
|
|
244
|
+
proc.stdin.end();
|
|
245
|
+
let stdout = '';
|
|
246
|
+
let stderr = '';
|
|
247
|
+
proc.stdout.on('data', (data) => { stdout += data.toString(); });
|
|
248
|
+
proc.stderr.on('data', (data) => { stderr += data.toString(); });
|
|
249
|
+
const timer = setTimeout(() => {
|
|
250
|
+
proc.kill();
|
|
251
|
+
resolve({
|
|
252
|
+
id: '',
|
|
253
|
+
object: 'error',
|
|
254
|
+
created: Math.floor(Date.now() / 1000),
|
|
255
|
+
model,
|
|
256
|
+
choices: [],
|
|
257
|
+
error: { message: 'Claude Code timed out after 120s', type: 'timeout' }
|
|
258
|
+
});
|
|
259
|
+
}, 120000);
|
|
260
|
+
proc.on('close', (code) => {
|
|
261
|
+
clearTimeout(timer);
|
|
262
|
+
if (code !== 0) {
|
|
263
|
+
resolve({
|
|
264
|
+
id: '',
|
|
265
|
+
object: 'error',
|
|
266
|
+
created: Math.floor(Date.now() / 1000),
|
|
267
|
+
model,
|
|
268
|
+
choices: [],
|
|
269
|
+
error: {
|
|
270
|
+
message: `Claude Code exited with code ${code}${stderr ? `: ${stderr.trim()}` : ''}`,
|
|
271
|
+
type: 'internal_error'
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
// claude --output-format json returns a JSON array of events
|
|
278
|
+
// Find the "result" event which contains the final response
|
|
279
|
+
const events = JSON.parse(stdout);
|
|
280
|
+
const resultEvent = Array.isArray(events)
|
|
281
|
+
? events.find((e) => e.type === 'result')
|
|
282
|
+
: events;
|
|
283
|
+
const content = resultEvent?.result || '';
|
|
284
|
+
const sessionId = resultEvent?.session_id || '';
|
|
285
|
+
const usage = resultEvent?.usage || {};
|
|
286
|
+
resolve({
|
|
287
|
+
id: sessionId || `claude-${Math.random().toString(36).substring(2, 10)}`,
|
|
288
|
+
object: 'chat.completion',
|
|
289
|
+
created: Math.floor(Date.now() / 1000),
|
|
290
|
+
model: model || 'claude-code',
|
|
291
|
+
choices: [{
|
|
292
|
+
index: 0,
|
|
293
|
+
message: {
|
|
294
|
+
role: 'assistant',
|
|
295
|
+
content
|
|
296
|
+
},
|
|
297
|
+
finish_reason: 'stop'
|
|
298
|
+
}],
|
|
299
|
+
usage: {
|
|
300
|
+
prompt_tokens: usage.input_tokens || 0,
|
|
301
|
+
completion_tokens: usage.output_tokens || 0,
|
|
302
|
+
total_tokens: (usage.input_tokens || 0) + (usage.output_tokens || 0)
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
catch (parseError) {
|
|
307
|
+
// Fallback: treat stdout as plain text
|
|
308
|
+
resolve({
|
|
309
|
+
id: `claude-${Math.random().toString(36).substring(2, 10)}`,
|
|
310
|
+
object: 'chat.completion',
|
|
311
|
+
created: Math.floor(Date.now() / 1000),
|
|
312
|
+
model: model || 'claude-code',
|
|
313
|
+
choices: [{
|
|
314
|
+
index: 0,
|
|
315
|
+
message: {
|
|
316
|
+
role: 'assistant',
|
|
317
|
+
content: stdout.trim()
|
|
318
|
+
},
|
|
319
|
+
finish_reason: 'stop'
|
|
320
|
+
}],
|
|
321
|
+
usage: {
|
|
322
|
+
prompt_tokens: 0,
|
|
323
|
+
completion_tokens: 0,
|
|
324
|
+
total_tokens: 0
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
convertAnthropicToOpenAiFormat(anthropicResponse, model) {
|
|
332
|
+
const content = (anthropicResponse.content || [])
|
|
333
|
+
.filter((block) => block.type === 'text')
|
|
334
|
+
.map((block) => block.text)
|
|
335
|
+
.join('');
|
|
336
|
+
const usage = anthropicResponse.usage || {};
|
|
337
|
+
return {
|
|
338
|
+
id: anthropicResponse.id || `msg-${Math.random().toString(36).substring(2, 10)}`,
|
|
339
|
+
object: 'chat.completion',
|
|
340
|
+
created: Math.floor(Date.now() / 1000),
|
|
341
|
+
model: anthropicResponse.model || model,
|
|
342
|
+
choices: [{
|
|
343
|
+
index: 0,
|
|
344
|
+
message: {
|
|
345
|
+
role: 'assistant',
|
|
346
|
+
content
|
|
347
|
+
},
|
|
348
|
+
finish_reason: anthropicResponse.stop_reason === 'end_turn' ? 'stop' : (anthropicResponse.stop_reason || 'stop')
|
|
349
|
+
}],
|
|
350
|
+
usage: {
|
|
351
|
+
prompt_tokens: usage.input_tokens || 0,
|
|
352
|
+
completion_tokens: usage.output_tokens || 0,
|
|
353
|
+
total_tokens: (usage.input_tokens || 0) + (usage.output_tokens || 0)
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
}
|
|
155
357
|
}
|
|
156
358
|
exports.LlmService = LlmService;
|
|
@@ -93,6 +93,7 @@ class TmuxSessionHandler {
|
|
|
93
93
|
});
|
|
94
94
|
this.wsClient.on('connected', () => {
|
|
95
95
|
console.log(`[${this.tmuxSession}] Connected to relay`);
|
|
96
|
+
this.currentMetaJson = ''; // Force resend meta on reconnect
|
|
96
97
|
this.startScreenCapture();
|
|
97
98
|
this.startMetaTracking();
|
|
98
99
|
});
|
package/dist/agent/types.d.ts
CHANGED