Point an agent at this instance with a per-user bearer token (hashed at rest; shown once). POST ${esc(base)}/remember · GET ${esc(base)}/recall
Your tokens
`;renderTokList()}catch(e){el.innerHTML='
'+esc(e.message)+'
'}}
+async function createApiToken(){const label=(document.getElementById('tok-label').value||'').trim();try{const d=await api('/auth/token',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({label})});document.getElementById('tok-created').innerHTML=`
Copy this token now — it won't be shown again:
${esc(d.token)}
Add to your agent config: Authorization: Bearer ${esc(d.token)}
`;document.getElementById('tok-label').value='';renderTokList()}catch(e){toast('Token: '+e.message,'err')}}
+async function revokeApiToken(id){if(!confirm('Revoke this token? Agents using it will stop working immediately.'))return;try{await api('/auth/token/'+id,{method:'DELETE'});toast('Token revoked','ok');renderTokList()}catch(e){toast(e.message,'err')}}
async function loadSyncStatus(){try{const d=await api('/sync/status');renderSync(d);if(d&&d.available)loadAutoSync()}catch(e){const el=document.getElementById('sync-body');if(el)el.textContent='Cloud sync is unavailable right now.'}}
function renderSync(d){const el=document.getElementById('sync-body');if(!el)return;if(!d||!d.available){el.innerHTML=`
Sync your memories to the cloud and pull them on any device — just sign in with your license key.
`;return}const last=d.last;let status='Not synced yet on this device.';if(last){const when=new Date((last.at||0)*1000).toLocaleString();status='Last synced '+when+' — pushed '+(last.exported||0)+', +'+(last.added||0)+' new from your other devices'+((last.errors&&last.errors.length)?' · '+last.errors.length+' issue(s)':'')+'.'}el.innerHTML=`
Status${esc((d.plan||'pro').toUpperCase())} · sync on
Your memories sync to the cloud. On another device: install Engraphis, open this dashboard, sign in with the same license key, and click Sync — everything downloads automatically.
${esc(status)}
`}
async function signInSync(){const i=document.getElementById('sync-key');const k=i?i.value.trim():'';if(!k){toast('Paste your license key','err');return}try{const d=await api('/license/activate',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({key:k})});LIC=d;try{updateLicBadge();updateFeatureLocks()}catch(e){}if((!d.plan||d.plan==='free')&&d.error){toast('Key accepted but not active — '+d.error,'err')}else{toast('Signed in — '+(d.plan||'').toUpperCase()+' plan','ok')}await loadSyncStatus();try{loadLicense()}catch(e){}}catch(e){toast('Sign-in failed: '+e.message,'err')}}