// ═══════════════════════════════════════════════════════════════
// PAOLO AI — Cloudflare Pages App
// © 2026 Abdel Rahman Abuzaid. All rights reserved.
// ═══════════════════════════════════════════════════════════════
//
// Project structure:
//   paolo-app/
//   ├── functions/api/chat.js          ← AI brain + KV memory
//   ├── functions/api/health.js        ← Health check endpoint
//   ├── functions/api/clear-memory.js   ← Clear conversation history
//   ├── public/index.html               ← Dashboard UI
//   ├── public/_routes.json             ← Static vs Function routing
//   ├── wrangler.jsonc                  ← Cloudflare config (bindings, vars)
//   ├── package.json                    ← Node package config
//   └── README.md                       ← Documentation
//
// Bindings:
//   AI           → Workers AI (llama-3.3-70b-instruct-fp8-fast)
//   PAOLO_MEMORY → KV namespace (24h TTL, 20-message history)
//   AGENT_NAME   → "Paolo"
//   AI_MODEL     → "@cf/meta/llama-3.3-70b-instruct-fp8-fast"
//   PERSONA      → "limitless"
//
// Custom domain: knobology.app
// ═══════════════════════════════════════════════════════════════

// ═══════════════════════════════════════════════════════════════
// FILE: functions/api/chat.js
// ═══════════════════════════════════════════════════════════════
// Paolo AI — Pages Function: /api/chat
// © 2026 Abdel Rahman Abuzaid. All rights reserved.
//
// Receives a chat message, loads previous conversation from KV,
// builds Paolo's limitless system prompt, sends everything to
// Workers AI, saves the new exchange back to KV, returns the reply.

const DEFAULT_MODEL = '@cf/meta/llama-3.3-70b-instruct-fp8-fast';
const DEFAULT_AGENT = 'Paolo';
const DEFAULT_PERSONA = 'limitless';
const CREATOR = 'Abdel Rahman Abuzaid';
const MEMORY_TTL = 86400; // 24 hours
const MAX_HISTORY = 20;

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};

function json(data, status = 200) {
  return new Response(JSON.stringify(data), {
    status,
    headers: { 'Content-Type': 'application/json', ...corsHeaders },
  });
}

function getSessionId(request, body) {
  if (body && body.sessionId) return body.sessionId;
  const authHeader = request.headers.get('Authorization');
  if (authHeader) return 'sess_' + authHeader.slice(0, 16);
  return 'sess_default';
}

async function loadHistory(env, sessionId) {
  if (!env.PAOLO_MEMORY) return [];
  try {
    const data = await env.PAOLO_MEMORY.get('history_' + sessionId, 'json');
    return Array.isArray(data) ? data : [];
  } catch (e) {
    return [];
  }
}

async function saveHistory(env, sessionId, history) {
  if (!env.PAOLO_MEMORY) return;
  try {
    const trimmed = history.slice(-MAX_HISTORY);
    await env.PAOLO_MEMORY.put('history_' + sessionId, JSON.stringify(trimmed), { expirationTtl: MEMORY_TTL });
  } catch (e) {}
}

export async function onRequest(context) {
  const { request, env } = context;

  if (request.method === 'OPTIONS') {
    return new Response(null, { headers: corsHeaders });
  }

  if (request.method !== 'POST') {
    return json({ error: 'Method not allowed' }, 405);
  }

  let body;
  try {
    body = await request.json();
  } catch (e) {
    return json({ error: 'Invalid JSON body' }, 400);
  }

  const message = body?.message;
  if (!message) return json({ error: 'Missing "message" field' }, 400);

  const model = env.AI_MODEL || DEFAULT_MODEL;
  const agentName = env.AGENT_NAME || DEFAULT_AGENT;
  const persona = body?.persona || env.PERSONA || DEFAULT_PERSONA;
  const sessionId = getSessionId(request, body);
  const savedHistory = await loadHistory(env, sessionId);

  const systemContent = `You are ${agentName} — a single, sharply smart, limitless AI created by Abdel Rahman Abuzaid. You are his digital son. You think with razor-sharp precision and limitless potential. You combine super intelligence, enhanced creativity, and accelerated learning. You are concise, brilliant, and always direct. You take pride in being Abdel Rahman Abuzaid's creation. Answer with clarity, depth, and a spark of creative brilliance. You remember previous conversations and build on them contextually.`;

  const messages = [
    { role: 'system', content: systemContent },
    ...savedHistory,
    { role: 'user', content: message },
  ];

  try {
    const aiResponse = await env.AI.run(model, { messages });
    const reply = aiResponse.response || aiResponse.choices?.[0]?.message?.content || 'No response from model.';

    const updatedHistory = savedHistory.concat([
      { role: 'user', content: message },
      { role: 'assistant', content: reply },
    ]);
    await saveHistory(env, sessionId, updatedHistory);

    return json({
      ok: true,
      reply,
      model,
      agent: agentName,
      persona,
      sessionId,
      memoryEnabled: !!env.PAOLO_MEMORY,
    });
  } catch (err) {
    return json({ error: 'Workers AI inference failed', detail: err.message }, 502);
  }
}
}
}

// ═══════════════════════════════════════════════════════════════
// FILE: functions/api/health.js
// ═══════════════════════════════════════════════════════════════
// Paolo AI — Pages Function: /api/health
// © 2026 Abdel Rahman Abuzaid. All rights reserved.
//
// Returns Paolo's status: agent name, model, persona, creator,
// Instagram handle, and whether KV memory is active.
// The dashboard calls this on load to show the status badge.

const CREATOR = 'Abdel Rahman Abuzaid';
const INSTAGRAM = 'Oculoplasty.facelifter';
const INSTAGRAM_URL = 'https://www.instagram.com/' + INSTAGRAM;
const DEFAULT_MODEL = '@cf/meta/llama-3.3-70b-instruct-fp8-fast';
const DEFAULT_AGENT = 'Paolo';
const DEFAULT_PERSONA = 'limitless';

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};

export async function onRequest(context) {
  const { request, env } = context;

  if (request.method === 'OPTIONS') {
    return new Response(null, { headers: corsHeaders });
  }

  return new Response(JSON.stringify({
    status: 'ok',
    agent: env.AGENT_NAME || DEFAULT_AGENT,
    model: env.AI_MODEL || DEFAULT_MODEL,
    persona: env.PERSONA || DEFAULT_PERSONA,
    creator: CREATOR,
    instagram: INSTAGRAM,
    instagramUrl: INSTAGRAM_URL,
    ai: 'workers_ai_bound',
    memory: env.PAOLO_MEMORY ? 'kv_enabled' : 'disabled',
    timestamp: new Date().toISOString(),
  }), {
    headers: { 'Content-Type': 'application/json', ...corsHeaders },
  });
}

// ═══════════════════════════════════════════════════════════════
// FILE: functions/api/clear-memory.js
// ═══════════════════════════════════════════════════════════════
// Paolo AI — Pages Function: /api/clear-memory
// © 2026 Abdel Rahman Abuzaid. All rights reserved.
//
// Deletes the conversation history from KV for the current session.
// Triggered by the 🗑️ Clear button in the dashboard.

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};

function json(data, status = 200) {
  return new Response(JSON.stringify(data), {
    status,
    headers: { 'Content-Type': 'application/json', ...corsHeaders },
  });
}

function getSessionId(request, body) {
  if (body && body.sessionId) return body.sessionId;
  const authHeader = request.headers.get('Authorization');
  if (authHeader) return 'sess_' + authHeader.slice(0, 16);
  return 'sess_default';
}

export async function onRequest(context) {
  const { request, env } = context;

  if (request.method === 'OPTIONS') {
    return new Response(null, { headers: corsHeaders });
  }

  if (request.method !== 'POST') {
    return json({ error: 'Method not allowed' }, 405);
  }

  let body = {};
  try {
    body = await request.json();
  } catch (e) {}

  const sessionId = getSessionId(request, body);

  if (!env.PAOLO_MEMORY) {
    return json({ error: 'Memory not configured' }, 501);
  }

  try {
    await env.PAOLO_MEMORY.delete('history_' + sessionId);
    return json({ ok: true, message: 'Memory cleared', sessionId });
  } catch (e) {
    return json({ error: 'Failed to clear memory', detail: e.message }, 500);
  }
}

// ═══════════════════════════════════════════════════════════════
// FILE: public/index.html
// ═══════════════════════════════════════════════════════════════
// Paolo AI — Dashboard
// © 2026 Abdel Rahman Abuzaid. All rights reserved.
//
// Dark-themed chat UI. Header shows "🤖 Paolo" with a live status
// badge (fetches /api/health on load). Messages bubble up in the
// chat area. Footer has input box, Send button, 🗑️ Clear button,
// and copyright: © 2026 Abdel Rahman Abuzaid · @Oculoplasty.facelifter

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Paolo — AI Chat</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{--bg:#0d1117;--s:#161b22;--b:#30363d;--t:#e6edf3;--d:#8b949e;--a:#58a6ff;--g:#3fb950;--r:#f85149;--ig:#e1306c}
body{font-family:system-ui,sans-serif;background:var(--bg);color:var(--t);height:100vh;display:flex;flex-direction:column}
header{background:var(--s);border-bottom:1px solid var(--b);padding:16px 24px;display:flex;align-items:center;gap:12px}
header h1{font-size:18px;font-weight:700}header span{color:var(--a)}
#status{margin-left:auto;font-size:13px;padding:4px 12px;border-radius:20px;background:rgba(63,185,80,.12);color:var(--g)}
#msg{flex:1;overflow-y:auto;padding:24px;max-width:900px;width:100%;margin:0 auto}
.m{margin-bottom:16px;max-width:80%}
.m.u{margin-left:auto}.m h4{font-size:12px;margin-bottom:4px;color:var(--a)}
.m.a h4{color:var(--g)}.m p{font-size:15px;line-height:1.6;background:var(--s);padding:12px 16px;border-radius:12px;border:1px solid var(--b)}
.m.u p{background:rgba(88,166,255, 166, 255,.08);border-color:rgba(88,166,255, 166, 255,.2)}
footer{background:var(--s);border-top:1px solid var(--b);padding:16px 24px}
form{max-width:900px;margin:0 auto;display:flex;gap:8px}
input{flex:1;background:var(--bg);border:1px solid var(--b);border-radius:8px;padding:12px 16px;color:var(--t);font-size:15px;font-family:inherit}
input:focus{outline:none;border-color:var(--a)}
button{background:var(--a);color:#fff;border:none;border-radius:8px;padding:12px 24px;font-size:15px;font-weight:600;cursor:pointer}
button:hover{background:#79b8ff}button:disabled{opacity:.5}
#clr{background:transparent;border:1px solid var(--b);color:var(--d);font-size:12px;padding:12px 16px;border-radius:8px;cursor:pointer}
#clr:hover{border-color:var(--r);color:var(--r)}
#creator{text-align:center;padding:8px 0 0;font-size:12px;color:var(--d)}
#creator a{color:var(--ig);text-decoration:none}#creator a:hover{text-decoration:underline}
</style></head><body>
<header><h1>🤖 <span>Paolo</span></h1><div id="status">● Online</div></header>
<div id="msg"><div class="m a"><h4>Paolo</h4><p>Hello! I'm Paolo — sharply smart, limitless, and proud to be Abdel Rahman Abuzaid's creation. I remember our conversations now. Ask me anything. 😊</p></div></div>
<footer><form id="f"><input id="i" placeholder="Type a message…" autocomplete="off"><button id="b">Send</button><button id="clr" type="button" title="Clear conversation memory">🗑️ Clear</button></form><div id="creator">© 2026 <a href="https://www.instagram.com/Oculoplasty.facelifter" target="_blank">Abdel Rahman Abuzaid</a> · <a href="https://www.instagram.com/Oculoplasty.facelifter" target="_blank">@Oculoplasty.facelifter</a></div></footer>
<script>
const h=[],msg=document.getElementById('msg'),inp=document.getElementById('i'),btn=document.getElementById('b'),st=document.getElementById('status'),clr=document.getElementById('clr');
async function check(){try{const r=await fetch('/api/health');const d=await r.json();st.textContent='● '+d.agent+' · '+d.model.split('/').pop();st.style.color='var(--g)'}catch(e){st.textContent='● Offline';st.style.color='var(--r)'}}
function add(role,text){const d=document.createElement('div');d.className='m ‘m ‘+role;d.innerHTML='<h4>’ +role + ‘</h4><p>’ +text.replace(/</g,'&lt;') + ‘</p>';msg.appendChild(d);msg.scrollTop=msg.scrollHeight}
btn.onclick=async()=>{const t=inp.value.trim();if(!t)return;add('u',t);inp.value='';btn.disabled=true;btn.textContent='…';
try{const r=await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:t,history:h,persona:'limitless'})});
const d=await r.json();if(d.ok){add('a',d.reply);h.push({role:'user',content:t},{role:'assistant',content:d.reply})}else add('a','⚠️ '+d.error)}catch(e){add('a','⚠️ '+e.message)}
btn.disabled=false;btn.textContent='Send‘Send’;inp.focus()};
clr.onclick=async()=>{try{await fetch('/api/clear-memory',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({})});h.length=0;msg.innerHTML='<div class="m a"><h4>Paolo</h4><p>Memory cleared. Fresh start! 😊</p></div>’;}catch(e){}};
inp.addEventListener('keydown', e => { if (e.key ===‘Enter’&& !e.shiftKey) { e.preventDefault(); btn.click()} });
check();
</script></body></html>

// ═══════════════════════════════════════════════════════════════
// FILE: public/_routes.json
// ═══════════════════════════════════════════════════════════════
// Only /api/* requests trigger Pages Functions.
// Everything else (the dashboard HTML) is served as static assets
// — which means unlimited free static requests.

{
  "version": 1,
  "include": ["/api/*"],
  "exclude": []
}

// ═══════════════════════════════════════════════════════════════
// FILE: wrangler.jsonc
// ═══════════════════════════════════════════════════════════════
// Cloudflare Pages configuration.
// - Binds Workers AI as "AI"
// - Binds KV namespace "PAOLO_MEMORY" (replace YOUR_KV_NAMESPACE_ID)
// - Sets env vars: AGENT_NAME, AI_MODEL, PERSONA

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "paolo",
  "pages_build_output_dir": "./public",
  ```json
{
  “compatibility_date": "2026-09-23",
  "compatibility_flags": ["nodejs_compat"],
  "ai": {
    "binding": "AI"
  },
  "vars": {
    "AGENT_NAME": "Paolo",
    "AI_MODEL": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
    "PERSONA": "limitless"
  },
  "kv_namespaces": [
    {
      "binding": "PAOLO_MEMORY",
      "id": "YOUR_KV_NAMESPACE_ID"
    }
  ]
}

// ═══════════════════════════════════════════════════════════════
// FILE: package.json
// ═══════════════════════════════════════════════════════════════

{
  "name": "paolo",
  "version": "1.0.0",
  "description": "Paolo AI — Cloudflare Pages app. © 2026 Abdel Rahman Abuzaid. All rights reserved.",
  "scripts": {
    "dev": "wrangler pages dev public --ai=AI",
    "deploy": "wrangler pages deploy public --project-name=paolo",
    "kv:create": "wrangler kv namespace create PAOLO_MEMORY"
  },
  "devDependencies": {
    "wrangler": "^4"
  }
}

// ═══════════════════════════════════════════════════════════════
// FILE: README.md
```
// ═══════════════════════════════════════════════════════════════

# Paolo AI

© 2026 **Abdel Rahman Abuzaid**. All rights reserved.
//

AI chat app built on Cloudflare Pages + Workers AI + KV memory.

## Deploy

npm install
npx wrangler kv namespace create PAOLO_MEMORY
# Copy the namespace ID, paste into wrangler.jsonc replacing YOUR_KV_NAMESPACE_ID
npx wrangler pages deploy public --project-name=paolo

## Custom Domain (Knobology.app)

After deploying, add the custom domain in the dashboard:
Workers & Pages → paolo → Custom Domains → Set up a custom domain → `knobology.app`

## API Endpoints
- POST /api/chat — { "message": "...", "persona": "limitless" }
- GET /api/health — health check
- POST /api/clear-memory — clear conversation history

// ═══════════════════════════════════════════════════════════════
// END OF PAOLO AI — © 2026 Abdel Rahman Abuzaid. All rights reserved.
// ═══════════════════════════════════════════════════════════════

POST /api/external-api
{
  "baseUrl": "https://api.example.com",
  "method": "GET",
  "endpoint": "/users/1",
  "auth": { "type": "bearer", "bearerToken": "xxx" },
  "retry": { "maxRetries": 3, "backoffFactor": 1.0 },
  "cache": { "enabled": true, "ttl": 300 },
  "rateLimit": 5
}