jules / services /wrapperService.js
GraziePrego's picture
Upload folder using huggingface_hub
34450be verified
import fetch from 'node-fetch';
/**
* Service to wrap complex Jules interactions into simple API calls.
*/
export class WrapperService {
constructor() {}
// --- Helpers ---
getApiKeyForAgent(agentId) {
if (agentId === 'jules-2') return process.env.JULES_API_KEY_2 || process.env.JULES_API_KEY;
if (agentId === 'jules-3') return process.env.JULES_API_KEY_3 || process.env.JULES_API_KEY;
if (agentId === 'jules-4') return process.env.JULES_API_KEY_4 || process.env.JULES_API_KEY;
return process.env.JULES_API_KEY;
}
getGithubTokenForAgent(agentId) {
// Jules 2 & 4 -> Greene-ctrl (GITHUB_2_TOKEN)
if (agentId === 'jules-2' || agentId === 'jules-4') return process.env.GITHUB_2_TOKEN;
// Jules 1 & 3 -> JsonLord (GITHUB_API_KEY)
return process.env.GITHUB_API_KEY || process.env.GITHUB_TOKEN;
}
getGithubProfileForAgent(agentId) {
if (agentId === 'jules-2' || agentId === 'jules-4') return 'Greene-ctrl';
return 'JsonLord';
}
async callJulesApi(method, path, apiKey, body = null) {
const url = `https://jules.googleapis.com/v1alpha/${path}`;
const headers = {
'Content-Type': 'application/json',
'X-Goog-Api-Key': apiKey
};
const opts = { method, headers };
if (body) opts.body = JSON.stringify(body);
const res = await fetch(url, opts);
if (!res.ok) {
const txt = await res.text();
throw new Error(`Jules API Error (${res.status}): ${txt}`);
}
return res.json();
}
fillTemplate(content, parameters) {
let filled = content;
// Replace {{VAR}} or $VAR
for (const [key, val] of Object.entries(parameters)) {
const bracketRegex = new RegExp(`{{${key}}}|\\[\\[${key}\\]\\]`, 'g');
const dollarRegex = new RegExp(`\\$${key}\\b`, 'g'); // Word boundary for $VAR
filled = filled.replace(bracketRegex, val).replace(dollarRegex, val);
}
return filled;
}
// --- Core Logic ---
async listTemplates(agentId) {
const token = this.getGithubTokenForAgent(agentId);
const profile = this.getGithubProfileForAgent(agentId);
// Hardcoded source for now based on memory
const owner = 'JsonLord';
const repo = 'agent-notes';
const path = 'ChatTemplates';
const branch = 'main';
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${branch}`;
const headers = { 'Accept': 'application/vnd.github.v3+json' };
if (token) headers['Authorization'] = `token ${token}`;
try {
const res = await fetch(url, { headers });
if (!res.ok) throw new Error(`GitHub Error: ${res.status}`);
const files = await res.json();
// Just return metadata, content needs individual fetch if not "custom"
return files
.filter(f => f.name.endsWith('.md') || f.name.endsWith('.txt'))
.map(f => ({
id: f.name,
name: f.name,
download_url: f.download_url
}));
} catch (e) {
console.error("List templates failed:", e);
return [];
}
}
async handleSessionRequest(agentId, templateId, sessionId, parameters) {
const apiKey = this.getApiKeyForAgent(agentId);
if (!apiKey) throw new Error(`No API Key found for agent ${agentId}`);
let promptText = '';
// 1. Resolve Template Content
if (templateId && templateId !== 'custom') {
// Fetch template content
// Reuse logic or simple fetch
const templates = await this.listTemplates(agentId);
const tmpl = templates.find(t => t.id === templateId);
if (tmpl) {
const res = await fetch(tmpl.download_url);
const raw = await res.text();
// Strip JSON block
const jsonMatch = raw.match(/^\s*(\{[\s\S]*?\})(\s*[\r\n]+|$)/);
promptText = jsonMatch ? raw.substring(jsonMatch[0].length).trim() : raw;
} else {
throw new Error(`Template ${templateId} not found`);
}
} else {
// Custom or direct prompt
promptText = parameters.prompt || parameters.content || '';
}
// 2. Fill Variables
const finalPrompt = this.fillTemplate(promptText, parameters);
// 3. Execute
let currentSessionId = sessionId;
let lastResponse = null;
if (!currentSessionId) {
// CREATE SESSION
// Extract title or default
const title = parameters.title || `Session ${new Date().toISOString()}`;
const source = parameters.source || 'JsonLord/agent-notes'; // Default source?
const payload = {
title,
prompt: finalPrompt,
sourceContext: {
source: source,
githubRepoContext: { startingBranch: 'main' }
}
};
const session = await this.callJulesApi('POST', 'sessions', apiKey, payload);
currentSessionId = session.name.split('/').pop(); // "sessions/XYZ" -> "XYZ"
lastResponse = session;
} else {
// SEND MESSAGE
const payload = { prompt: finalPrompt };
await this.callJulesApi('POST', `sessions/${currentSessionId}:sendMessage`, apiKey, payload);
lastResponse = { status: 'Message Sent' };
}
// 4. Auto-Approve logic (simplified)
// If the user wants to auto-run, we might check recent activities for a plan
// For now, return the ID so the user can call again.
return {
sessionId: currentSessionId,
agentId,
status: 'success',
data: lastResponse
};
}
}