json-object-editor
Version:
JOE the Json Object Editor | Platform Edition
117 lines (107 loc) • 4.54 kB
HTML
<html>
<head>
<meta charset="utf-8">
<title>JOE MCP Prompt</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;margin:20px;}
textarea{width:100%;height:360px;font-family:ui-monospace,Menlo,Consolas,monospace}
.small{font-size:12px;color:#666}
</style>
</head>
<body>
<div id="mcp-nav"></div>
<script src="/JsonObjectEditor/_www/mcp-nav.js"></script>
<h1>Starter Agent Instructions</h1>
<div class="small">Copy into your Custom GPT or Assistant system prompt. This text is loaded from docs/joe_agent_custom_gpt_instructions_v_2.md (source of truth).</div>
<h3>Downloads</h3>
<div class="small">Quickly export JSON helpful for agent setup and offline review.</div>
<div class="row" style="display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin:8px 0 16px;">
<label for="base" style="margin:0">Base URL</label>
<input id="base" value="" placeholder="http://localhost:2025" style="min-width:280px"/>
<button id="downloadApps">Download apps.json</button>
<button id="downloadHydrate">Download hydrate-<date>.json</button>
<span id="dlStatus" class="small"></span>
</div>
<textarea readonly id="prompt" placeholder="Loading instructions from docs…"></textarea>
<script>
(function(){
var $ = function(id){ return document.getElementById(id); };
var base = $('base');
var dlBtn = $('downloadApps');
var dlHydrateBtn = $('downloadHydrate');
var dlStatus = $('dlStatus');
var promptBox = $('prompt');
base.value = base.value || location.origin;
function setStatus(msg, ok){
dlStatus.textContent = msg||'';
dlStatus.className = 'small ' + (ok===true?'good': ok===false?'bad':'');
}
async function fetchJSON(url, opts){
const res = await fetch(url, opts);
const ct = res.headers.get('content-type')||'';
const isJSON = ct.indexOf('application/json') >= 0;
if(!res.ok){
let detail = isJSON ? await res.json().catch(function(){return {};}) : await res.text();
throw new Error('HTTP '+res.status+': '+(isJSON?JSON.stringify(detail):detail));
}
return isJSON ? res.json() : res.text();
}
async function callMCP(method, params){
const url = base.value.replace(/\/$/,'') + '/mcp';
const body = { jsonrpc: '2.0', id: String(Date.now()), method: method, params: params||{} };
return fetchJSON(url, { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(body) });
}
dlBtn.onclick = async function(){
setStatus('Fetching apps...', null);
try{
const resp = await callMCP('listApps', {});
const data = (resp && (resp.result||resp)) || {};
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'apps.json';
document.body.appendChild(a);
a.click();
setTimeout(function(){ URL.revokeObjectURL(url); a.remove(); }, 0);
setStatus('Downloaded', true);
} catch(e){
setStatus(e.message||String(e), false);
}
};
// Load system instructions from docs (source of truth)
(function loadDocs(){
var path = '/JsonObjectEditor/docs/joe_agent_custom_gpt_instructions_v_3.md';
fetch(path).then(function(r){ if(!r.ok) throw new Error('HTTP '+r.status); return r.text(); })
.then(function(text){ promptBox.value = text; })
.catch(function(e){ promptBox.value = 'Failed to load instructions: '+(e.message||String(e)); });
})();
dlHydrateBtn.onclick = async function(){
setStatus('Fetching hydrate...', null);
try{
const resp = await callMCP('hydrate', {});
const data = (resp && (resp.result||resp)) || {};
const now = new Date();
const yyyy = now.getFullYear();
const mm = String(now.getMonth()+1).padStart(2,'0');
const dd = String(now.getDate()).padStart(2,'0');
const fname = `hydrate_${yyyy}-${mm}-${dd}.json`;
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fname;
document.body.appendChild(a);
a.click();
setTimeout(function(){ URL.revokeObjectURL(url); a.remove(); }, 0);
setStatus('Downloaded', true);
} catch(e){
setStatus(e.message||String(e), false);
}
};
})();
</script>
</body>
</html>