Spaces:
Paused
Paused
File size: 6,057 Bytes
c59bca4 bfcb19a c59bca4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | 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
};
}
}
|