diff --git a/admin/build/index.asset.php b/admin/build/index.asset.php index b4dab81..75e65d2 100644 --- a/admin/build/index.asset.php +++ b/admin/build/index.asset.php @@ -1 +1 @@ - array('react', 'react-dom', 'wp-api-fetch', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '3a8e65d912ca9ca5e467'); + array('react', 'react-dom', 'wp-api-fetch', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '8404554d959f53432915'); diff --git a/admin/build/index.js b/admin/build/index.js index 3e7d281..e6a44b4 100644 --- a/admin/build/index.js +++ b/admin/build/index.js @@ -12,7 +12,7 @@ (0,Op.__)("Forget “%s”?","saddle"),e.key),description:(0,Op.__)("This cannot be undone.","saddle"),danger:!0,confirmLabel:(0,Op.__)("Forget","saddle"),cancelLabel:(0,Op.__)("Cancel","saddle")})&&w(`memory/${e.key}`,{method:"DELETE"})})(e)},(0,Op.__)("Delete","saddle")))}),p===e.key&&(0,a.createElement)(Qu,{className:"saddle-rows__body",code:e.text,copy:!1})))))),(0,a.createElement)(iu,null,(0,a.createElement)(lu,{title:(0,Op.__)("Add something to remember","saddle"),description:(0,Op.__)("Your own notes are told to every session automatically.","saddle")}),(0,a.createElement)(cu,null,(0,a.createElement)("div",{className:"saddle-memory__compose"},(0,a.createElement)(eu,{label:(0,Op.__)("What to remember","saddle")},e=>(0,a.createElement)(nu,{...e,value:g.text,onChange:e=>y(t=>({...t,text:e.target.value})),rows:3,placeholder:(0,Op.__)("e.g. The pricing page is “Plans” (page 42) — update it, never create a new one.","saddle")})),(0,a.createElement)(eu,{label:(0,Op.__)("Name (optional)","saddle")},e=>(0,a.createElement)(tu,{...e,value:g.key,onChange:e=>y(t=>({...t,key:e.target.value})),placeholder:(0,Op.__)("e.g. pricing-page","saddle")})),(0,a.createElement)(Jd,{variant:"secondary",onClick:()=>{g.text.trim()&&(b(!0),w("memory",{method:"POST",data:{key:g.key,text:g.text}}).finally(()=>{b(!1),y({key:"",text:""})}))},loading:v,disabled:v||!g.text.trim()},(0,Op.__)("Remember this","saddle"))))),s&&(0,a.createElement)(iu,null,(0,a.createElement)(lu,{title:(0,Op.__)("AI-written memory","saddle"),description:(0,Op.__)("Entries an AI saved on its own are only found when it searches — unless you pin them, or turn this on.","saddle")}),(0,a.createElement)(cu,null,(0,a.createElement)("div",{className:"saddle-guide__actions saddle-guide__actions--stack"},(0,a.createElement)("label",{className:"saddle-toggle-row",htmlFor:"saddle-memory-autoinject"},(0,a.createElement)(ru,{id:"saddle-memory-autoinject",checked:s.autoinject_agent,onChange:e=>w("memory-settings",{method:"POST",data:{autoinject_agent:e}}),"aria-label":(0,Op.__)("Auto-include AI-written memory","saddle")}),(0,a.createElement)("span",null,(0,Op.__)("Auto-include AI-written memory (off is safest — pin entries instead)","saddle"))),x>0&&(0,a.createElement)(Jd,{variant:"secondary",className:"saddle-link-danger",onClick:()=>(async e=>{await t({title:(0,Op.__)("Clear AI-written memory?","saddle"),description:(0,Op.sprintf)(/* translators: %d: entry count. */ /* translators: %d: entry count. */ (0,Op.__)("Delete all %d AI-written memory entries? Your own entries are kept.","saddle"),e),danger:!0,confirmLabel:(0,Op.__)("Clear it","saddle"),cancelLabel:(0,Op.__)("Cancel","saddle")})&&w("memory-clear-agent",{method:"POST"})})(x)},(0,Op.__)("Clear AI-written memory","saddle"))))),""!==l&&(0,a.createElement)(iu,null,(0,a.createElement)(lu,{title:(0,Op.__)("What every session is told","saddle"),description:(0,Op.__)("The exact memory block a new AI session starts with.","saddle")}),(0,a.createElement)(cu,null,(0,a.createElement)(ju,{trigger:(0,Op.__)("Show the exact text","saddle")},(0,a.createElement)(Qu,{className:"saddle-guide__system",code:l,copy:!1})))),(0,a.createElement)(dp,{open:h,onOpenChange:f,title:(0,Op.__)("How memory works","saddle"),size:"md"},(0,a.createElement)("div",{className:"saddle-doc saddle-doc--bare"},(0,a.createElement)("p",{className:"saddle-doc__p"},(0,Op.__)("Memory is background information your AI carries between sessions — saved by you on this page, or noted by an AI with its memory tools while it works. It saves you re-explaining your site every time.","saddle")),(0,a.createElement)("h3",{className:"saddle-doc__h"},(0,Op.__)("What every session is told","saddle")),(0,a.createElement)("ul",{className:"saddle-doc__list"},(0,a.createElement)("li",null,(0,Op.__)("Your own entries — always included.","saddle")),(0,a.createElement)("li",null,(0,Op.__)("Pinned entries — always included, whoever wrote them.","saddle")),(0,a.createElement)("li",null,(0,Op.__)("Everything else — only found when an AI searches its memory.","saddle"))),(0,a.createElement)("h3",{className:"saddle-doc__h"},(0,Op.__)("AI-written entries","saddle")),(0,a.createElement)("p",{className:"saddle-doc__p"},(0,Op.__)("Entries an AI saved on its own are never served to future sessions automatically — you stay in control of what becomes standing knowledge. Pin the ones worth keeping, or turn on auto-include if you trust the whole set (off is safest). “Clear AI-written memory” removes them all at once; your own entries are kept.","saddle")),(0,a.createElement)("h3",{className:"saddle-doc__h"},(0,Op.__)("Safety","saddle")),(0,a.createElement)("p",{className:"saddle-doc__p"},(0,Op.__)("Memory is background information only. It can never change what your AI is allowed to do — the access level and per-tool permissions always win.","saddle")))))}function Nm(){const[e,t]=(0,i.useState)(null),[n,r]=(0,i.useState)(!0),[s,o]=(0,i.useState)(!1),[l,c]=(0,i.useState)(null),[d,u]=(0,i.useState)(!1),p=()=>{r(!0),Gp("self-check").then(t).catch(()=>t({status:"unknown"})).finally(()=>r(!1))};if((0,i.useEffect)(p,[]),n&&!e)return(0,a.createElement)("p",{className:"saddle-health saddle-health--checking"},(0,a.createElement)(Nu,null),(0,Op.__)("Checking your server setup…","saddle"));if(!e||"app_passwords_off"===e.status)return null;if("ok"===e.status||"fixed"===l)return(0,a.createElement)("p",{className:"saddle-health saddle-health--ok"},"fixed"===l?(0,Op.__)("✓ Fixed — sign-in details now reach WordPress. Your AI apps can connect.","saddle"):(0,Op.__)("✓ Server check passed — sign-in details reach WordPress correctly.","saddle"));if("unknown"===e.status)return(0,a.createElement)("p",{className:"saddle-health saddle-health--muted"},(0,Op.__)("We couldn’t verify your server automatically. If AI apps report “unauthorized” even with the right password, ask your host to pass the Authorization header through to WordPress.","saddle"));if("nonce_header_stripped"===e.status)return(0,a.createElement)(Eu,{className:"saddle-health",tone:"default",title:(0,Op.__)("Your host is removing one of WordPress’s headers","saddle"),description:(0,Op.__)("Something on your hosting — usually a security or firewall layer — strips the X-WP-Nonce header from requests. Saddle works around it, so this dashboard is fine. But the same layer may interfere with other plugins’ settings screens, so it’s worth asking your host to let that header through.","saddle")},(0,a.createElement)(Jd,{variant:"link",onClick:p,disabled:n},n?(0,Op.__)("Checking…","saddle"):(0,Op.__)("Check again","saddle")));const m=e.fix_snippet||{};return(0,a.createElement)(Eu,{className:"saddle-health",tone:"warning",title:(0,Op.__)("Your server is blocking app sign-ins","saddle"),description:(0,Op.__)("When an AI app connects, it sends its password in a sign-in header. Your web server removes that header before WordPress can see it, so every connection will fail as “unauthorized” — even with the right password. (The test above can still pass, because your browser signs in a different way.)","saddle")},e.htaccess_fixable&&"still_stripped"!==l&&(0,a.createElement)("div",{className:"saddle-health__actions"},(0,a.createElement)(Jd,{variant:"primary",onClick:()=>{o(!0),c(null),Gp("fix-auth-header",{method:"POST"}).then(e=>{"ok"===e.auth_header?c("fixed"):(c("still_stripped"),u(!0)),p()}).catch(e=>{c(e.message||(0,Op.__)("The automatic fix didn’t work.","saddle")),u(!0)}).finally(()=>o(!1))},loading:s,disabled:s},s?(0,Op.__)("Fixing…","saddle"):(0,Op.__)("Fix it for me","saddle")),(0,a.createElement)(Jd,{variant:"link",onClick:()=>u(!d)},d?(0,Op.__)("Hide the rule","saddle"):(0,Op.__)("See what this adds","saddle"))),"still_stripped"===l&&(0,a.createElement)("p",{className:"saddle-health__body"},(0,Op.__)("Saddle added the rule, but the header still isn’t arriving — something earlier in the chain (a proxy or your host’s own config) is removing it. Send the rule below to your hosting support and ask them to allow the Authorization header.","saddle")),"string"==typeof l&&"fixed"!==l&&"still_stripped"!==l&&(0,a.createElement)("p",{className:"saddle-health__body saddle-health__error"},l),!e.htaccess_fixable&&(0,a.createElement)("p",{className:"saddle-health__body"},(0,Op.__)("Saddle can’t edit this server’s configuration automatically. Add the matching rule below yourself, or send it to your hosting support.","saddle")),(d||!e.htaccess_fixable)&&(0,a.createElement)(a.Fragment,null,m.apache&&(0,a.createElement)(Qu,{dark:!0,label:(0,Op.__)("Apache / LiteSpeed — add to .htaccess","saddle"),code:m.apache}),m.nginx&&(0,a.createElement)(Qu,{dark:!0,label:(0,Op.__)("nginx — add to the PHP location block","saddle"),code:m.nginx})),(0,a.createElement)(Jd,{variant:"link",onClick:p,disabled:n},n?(0,Op.__)("Checking…","saddle"):(0,Op.__)("Check again","saddle")))}function Mm(){const[e,t]=(0,i.useState)(null),[n,r]=(0,i.useState)(!1),[s,o]=(0,i.useState)(!1),[l,c]=(0,i.useState)(!1),d=()=>Gp("mcp-diagnostics").then(t).catch(()=>t(null));(0,i.useEffect)(()=>{d()},[]),(0,i.useEffect)(()=>{if(!e?.recording)return;const t=setInterval(d,5e3);return()=>clearInterval(t)},[e?.recording]);const u=e=>{r(!0),Gp("mcp-diagnostics",{method:"POST",data:e}).then(t).catch(()=>{}).finally(()=>r(!1))};if(!e)return(0,a.createElement)("p",{className:"saddle-health saddle-health--checking"},(0,a.createElement)(Nu,null),(0,Op.__)("Loading client traffic…","saddle"));const p=e.health||{},m=e.entries||[];return(0,a.createElement)(iu,null,(0,a.createElement)(lu,{title:(0,Op.__)("Client traffic","saddle"),description:(0,Op.__)("If a connected app says it can’t see any tools, record its next attempt here — this shows what it asked for and what Saddle sent back.","saddle")}),(0,a.createElement)(cu,null,(0,a.createElement)("p",{className:"saddle-mcp-diag__health"},void 0===p.registered?(0,Op.__)("No app has asked for the tool list yet. Requests are recorded below either way.","saddle"):(0,Op.sprintf)(/* translators: 1: number of tools that loaded, 2: number installed. */ /* translators: 1: number of tools that loaded, 2: number installed. */ (0,Op.__)("%1$d of the %2$d tools installed here loaded correctly. How many an app is offered depends on its access level — the requests below show that number.","saddle"),p.registered,p.expected)),p.degraded&&(0,a.createElement)("p",{className:"saddle-mcp-diag__degraded"},p.missing?.length?(0,Op.sprintf)(/* translators: %s: comma-separated tool names. */ /* translators: %s: comma-separated tool names. */ -(0,Op.__)("These tools didn’t load, so apps can’t call them: %s. Reload this page; if they’re still missing, send the report below.","saddle"),p.missing.join(", ")):(0,Op.__)("No tools loaded at all, which is never normal. Reload this page; if it persists, send the report below.","saddle")),(0,a.createElement)("div",{className:"saddle-mcp-diag__actions"},(0,a.createElement)(Jd,{variant:e.recording?"secondary":"primary",disabled:n,onClick:()=>u({recording:!e.recording})},e.recording?(0,Op.__)("Stop recording","saddle"):(0,Op.__)("Record the next hour","saddle")),m.length>0&&(0,a.createElement)(a.Fragment,null,(0,a.createElement)(Jd,{variant:"link",onClick:()=>{e?.report&&window.navigator.clipboard?.writeText(e.report).then(()=>{o(!0),window.setTimeout(()=>o(!1),2e3)})}},s?(0,Op.__)("Copied","saddle"):(0,Op.__)("Copy report","saddle")),(0,a.createElement)(Jd,{variant:"link",onClick:()=>c(!l)},l?(0,Op.__)("Hide report","saddle"):(0,Op.__)("Show report","saddle")),(0,a.createElement)(Jd,{variant:"link",disabled:n,onClick:()=>u({clear:!0})},(0,Op.__)("Clear","saddle")))),e.recording&&(0,a.createElement)("p",{className:"saddle-mcp-diag__hint"},(0,Op.__)("Recording. Now ask the app to refresh its actions, or run any request from it — then come back here.","saddle")),0===m.length?(0,a.createElement)("p",{className:"saddle-mcp-diag__empty"},e.recording?(0,Op.__)("Nothing has arrived yet.","saddle"):(0,Op.__)("Nothing recorded. If an app is misbehaving, start recording and then retry it from the app.","saddle")):(0,a.createElement)("table",{className:"saddle-mcp-diag__table"},(0,a.createElement)("thead",null,(0,a.createElement)("tr",null,(0,a.createElement)("th",null,(0,Op.__)("When","saddle")),(0,a.createElement)("th",null,(0,Op.__)("Asked for","saddle")),(0,a.createElement)("th",null,(0,Op.__)("Result","saddle")),(0,a.createElement)("th",null,(0,Op.__)("App","saddle")))),(0,a.createElement)("tbody",null,m.map((e,t)=>(0,a.createElement)("tr",{key:`${e.time}-${t}`},(0,a.createElement)("td",null,new Date(1e3*e.time).toLocaleTimeString()),(0,a.createElement)("td",null,(e.methods||[]).join(", ")||"—"),(0,a.createElement)("td",null,e.status>=200&&e.status<300?(0,a.createElement)(mu,{tone:"success"},void 0!==e.tools?(0,Op.sprintf)(/* translators: %d: number of tools sent. */ /* translators: %d: number of tools sent. */ +(0,Op.__)("These tools didn’t load, so apps can’t call them: %s. Reload this page; if they’re still missing, send the report below.","saddle"),p.missing.join(", ")):(0,Op.__)("No tools loaded at all, which is never normal. Reload this page; if it persists, send the report below.","saddle")),(0,a.createElement)("div",{className:"saddle-mcp-diag__actions"},(0,a.createElement)(Jd,{variant:e.recording?"secondary":"primary",disabled:n,onClick:()=>u({recording:!e.recording})},e.recording?(0,Op.__)("Stop recording","saddle"):(0,Op.__)("Record the next hour","saddle")),m.length>0&&(0,a.createElement)(a.Fragment,null,(0,a.createElement)(Jd,{variant:"link",onClick:()=>{e?.report&&window.navigator.clipboard?.writeText(e.report).then(()=>{o(!0),window.setTimeout(()=>o(!1),2e3)})}},s?(0,Op.__)("Copied","saddle"):(0,Op.__)("Copy report","saddle")),(0,a.createElement)(Jd,{variant:"link",onClick:()=>c(!l)},l?(0,Op.__)("Hide report","saddle"):(0,Op.__)("Show report","saddle")),(0,a.createElement)(Jd,{variant:"link",disabled:n,onClick:()=>u({clear:!0})},(0,Op.__)("Clear","saddle")))),e.recording&&(0,a.createElement)("p",{className:"saddle-mcp-diag__hint"},(0,Op.__)("Recording. Now ask the app to refresh its actions, or run any request from it — then come back here.","saddle")),0===m.length?(0,a.createElement)("p",{className:"saddle-mcp-diag__empty"},e.recording?(0,Op.__)("Nothing has arrived yet.","saddle"):(0,Op.__)("Nothing recorded. If an app is misbehaving, start recording and then retry it from the app.","saddle")):(0,a.createElement)("table",{className:"saddle-mcp-diag__table"},(0,a.createElement)("thead",null,(0,a.createElement)("tr",null,(0,a.createElement)("th",null,(0,Op.__)("When","saddle")),(0,a.createElement)("th",null,(0,Op.__)("Asked for","saddle")),(0,a.createElement)("th",null,(0,Op.__)("Signed in with","saddle")),(0,a.createElement)("th",null,(0,Op.__)("Result","saddle")),(0,a.createElement)("th",null,(0,Op.__)("App","saddle")))),(0,a.createElement)("tbody",null,m.map((e,t)=>(0,a.createElement)("tr",{key:`${e.time}-${t}`},(0,a.createElement)("td",null,new Date(1e3*e.time).toLocaleTimeString()),(0,a.createElement)("td",null,(e.methods||[]).join(", ")||e.method||"—"),(0,a.createElement)("td",null,"absent"===e.auth?(0,a.createElement)(mu,{tone:"warning"},(0,Op.__)("nothing sent","saddle")):e.scheme||"—"),(0,a.createElement)("td",null,e.status>=200&&e.status<300?(0,a.createElement)(mu,{tone:"success"},void 0!==e.tools?(0,Op.sprintf)(/* translators: %d: number of tools sent. */ /* translators: %d: number of tools sent. */ (0,Op.__)("%d tools sent","saddle"),e.tools):(0,Op.__)("OK","saddle")):(0,a.createElement)(mu,{tone:"danger"},(0,Op.sprintf)(/* translators: %d: HTTP status code. */ /* translators: %d: HTTP status code. */ (0,Op.__)("refused (%d)","saddle"),e.status))),(0,a.createElement)("td",null,e.client||"—"))))),l&&e.report&&(0,a.createElement)(Qu,null,e.report)))}const km=Zp.mcpUrl||"",Im=Zp.user||"",Em=Zp.serverSlug||"saddle",Cm=/\s/g,Sm=(0,Op.__)("What can you see on my WordPress site?","saddle"),Lm=[{key:"claude",label:(0,Op.__)("Claude","saddle"),kind:(0,Op.__)("Desktop app","saddle"),how:(0,Op.__)("In Claude: Settings → Developer → Edit Config. Paste this inside, save, and restart the app.","saddle"),next:(0,Op.__)("Restart Claude, then ask it about your site.","saddle")},{key:"chatgpt",label:(0,Op.__)("ChatGPT","saddle"),kind:(0,Op.__)("Chat and Work","saddle"),auth:"oauth",how:(0,Op.__)("In ChatGPT on the web: turn on Developer mode (Settings → Apps & Connectors → Advanced settings), then create a connector. Paste the address, choose OAuth, and leave the client ID and secret blank. ChatGPT sends you here to approve it — the connector then works in the desktop app too.","saddle"),next:(0,Op.__)("Enable the connector in a ChatGPT chat and ask it about your site. If it can read but refuses to change anything, check your ChatGPT plan — write-capable custom connectors have been limited to Business, Enterprise and Edu workspaces.","saddle")},{key:"codex",label:(0,Op.__)("Codex","saddle"),kind:(0,Op.__)("In the ChatGPT app","saddle"),how:(0,Op.__)("Codex reads a settings file. Open ~/.codex/config.toml, paste this at the end, save, then restart the app. The codex terminal command reads the same file.","saddle"),next:(0,Op.__)("Open Codex in the ChatGPT app and ask it about your site.","saddle")},{key:"claude-code",label:(0,Op.__)("Claude Code","saddle"),kind:(0,Op.__)("Terminal","saddle"),how:(0,Op.__)("Paste this into your terminal and press Enter. That’s the whole setup.","saddle"),next:(0,Op.__)("Run claude in any folder and ask it about your site.","saddle")},{key:"cursor",label:(0,Op.__)("Cursor","saddle"),kind:(0,Op.__)("Code editor","saddle"),how:(0,Op.__)("In Cursor: Settings → MCP → Add new server. Paste this (or save it as .cursor/mcp.json).","saddle"),next:(0,Op.__)("Open Cursor’s chat and ask it about your site.","saddle")},{key:"gemini-cli",label:(0,Op.__)("Gemini CLI","saddle"),kind:(0,Op.__)("Terminal","saddle"),how:(0,Op.__)("Paste this into your terminal and press Enter. That’s the whole setup.","saddle"),next:(0,Op.__)("Run gemini in any folder and ask it about your site.","saddle")},{key:"vscode",label:(0,Op.__)("VS Code","saddle"),kind:(0,Op.__)("Copilot (agent mode)","saddle"),how:(0,Op.__)("Save this as .vscode/mcp.json in your project, then start the server from the MCP: List Servers command.","saddle"),next:(0,Op.__)("Open Copilot Chat in agent mode and ask it about your site.","saddle")},{key:"other",label:(0,Op.__)("Any MCP app","saddle"),kind:(0,Op.__)("Everything else","saddle"),how:(0,Op.__)("Most AI apps accept this standard setup — look for “Add MCP server” in their settings and paste it there.","saddle"),next:(0,Op.__)("Open your app and ask it about your site.","saddle")}];function Dm(e,t){const n=`Authorization: Basic ${t}`;switch(e){case"claude-code":return`claude mcp add ${Em} --scope user --transport http ${km} \\\n --header "${n}"`;case"gemini-cli":return`gemini mcp add --scope user --transport http ${Em} ${km} \\\n --header "${n}"`;case"vscode":return JSON.stringify({servers:{[Em]:{type:"http",url:km,headers:{Authorization:`Basic ${t}`}}}},null,2);case"chatgpt":return[`${(0,Op.__)("Name","saddle")}: ${Em}`,`${(0,Op.__)("Address","saddle")}: ${km}`,`${(0,Op.__)("Authentication","saddle")}: ${(0,Op.__)("OAuth (leave client ID and secret blank)","saddle")}`].join("\n");case"codex":return[`[mcp_servers.${Em}]`,`url = "${km}"`,`http_headers = { Authorization = "Basic ${t}" }`,"startup_timeout_sec = 30"].join("\n");case"cursor":case"other":return JSON.stringify({mcpServers:{[Em]:{url:km,headers:{Authorization:`Basic ${t}`}}}},null,2);default:return JSON.stringify({mcpServers:{[Em]:{command:"npx",args:["-y","mcp-remote",km,"--header",n]}}},null,2)}}function Tm(e,t){return t?Dm(e,btoa(`${Im}:${t.replace(Cm,"")}`)):Am(e)}function Am(e){return Dm(e,"PASTE-YOUR-KEY-HERE")}function Rm({open:e,onOpenChange:t,app:n,label:r,password:s}){const{copied:o,copy:i}=Ru(),l=Lm.find(e=>e.key===n)||Lm[Lm.length-1],c=!!s,d=c?Tm(l.key,s):Am(l.key);return(0,a.createElement)(dp,{open:e,onOpenChange:t,title:(0,Op.sprintf)(/* translators: %s: the app name. */ /* translators: %s: the app name. */ (0,Op.__)("Set up %s","saddle"),r||l.label),size:"lg"},(0,a.createElement)("div",{className:"saddle-setup-guide"},(0,a.createElement)("div",{className:"saddle-setup-guide__app"},(0,a.createElement)(im,{app:l.key}),(0,a.createElement)("span",null,l.label)),c?(0,a.createElement)(Tu,{tone:"warning"},(0,Op.__)("This fresh key appears only this once — paste it into the app before closing. Saddle keeps just its last four characters.","saddle")):(0,a.createElement)(Eu,{title:(0,Op.__)("Keys are shown only once","saddle"),description:(0,Op.__)("This guide uses a placeholder where the key goes. To get a real, ready-to-paste setup, use “Rotate key” on the connection — the old key stops working and a fresh one appears here.","saddle")}),(0,a.createElement)("section",{className:"saddle-setup-guide__step"},(0,a.createElement)("h3",null,(0,Op.__)("1. Where it goes","saddle")),(0,a.createElement)("p",null,l.how)),(0,a.createElement)("section",{className:"saddle-setup-guide__step"},(0,a.createElement)("h3",null,(0,Op.__)("2. The setup","saddle")),(0,a.createElement)(Qu,{dark:!0,copy:!1,label:c?(0,Op.__)("Ready to paste","saddle"):(0,Op.__)("Template — replace the placeholder with your key","saddle"),code:d}),c&&(0,a.createElement)(Jd,{variant:"primary",onClick:()=>i(d)},o?(0,Op.__)("Copied ✓","saddle"):(0,Op.__)("Copy setup","saddle")),(0,a.createElement)(Hu,{label:(0,Op.__)("Endpoint","saddle"),value:km})),(0,a.createElement)("section",{className:"saddle-setup-guide__step"},(0,a.createElement)("h3",null,(0,Op.__)("3. Say hello","saddle")),(0,a.createElement)("p",null,l.next),(0,a.createElement)(Hu,{label:(0,Op.__)("Try asking","saddle"),value:Sm}))))}const zm=Zp.mcpUrl||"",Pm=({children:e,help:t})=>(0,a.createElement)("span",{className:"saddle-apps__titled"},e,t&&(0,a.createElement)(Bu,null,t)),Om=new Intl.DateTimeFormat(void 0,{dateStyle:"medium",timeStyle:"short"}),Zm=e=>!e.last_used&&e.created&&Date.now()/1e3-e.created>172800;function Wm({clients:e,loading:t,onConnect:n,onClientsChanged:r,onClientRemoved:s,siteTier:l}){const c=tp(),[d,u]=(0,i.useState)(!1),[p,m]=(0,i.useState)(null),[h,f]=(0,i.useState)(null),[g,y]=(0,i.useState)([]),v=()=>Gp("oauth-connections").then(e=>y(e||[])).catch(()=>y([]));(0,i.useEffect)(()=>{v()},[]);return(0,a.createElement)("div",{className:"saddle-apps"},(0,a.createElement)(_p,{title:(0,a.createElement)(Pm,{help:(0,Op.__)("Every app here has its own sign-in you can take away at any moment, and they all follow the same rules you set on Permissions.","saddle")},(0,Op.__)("Connected apps","saddle")),description:(0,Op.__)("Apps you’ve let talk to this site.","saddle"),actions:(0,a.createElement)(Jd,{variant:"primary",onClick:n},(0,Op.__)("Connect an app","saddle"))}),t&&(0,a.createElement)(Nu,null),!t&&0===e.length&&0===g.length&&(0,a.createElement)(gu,{icon:(0,a.createElement)(am,null),title:(0,Op.__)("Nothing connected yet","saddle"),description:(0,Op.__)("Connect Claude, Cursor, or another AI app — it takes about a minute.","saddle"),actions:(0,a.createElement)(Jd,{variant:"primary",onClick:n},(0,Op.__)("Connect an app","saddle"))}),!t&&(e.length>0||g.length>0)&&(0,a.createElement)(iu,{className:"saddle-apps__card"},(0,a.createElement)(cu,{className:"saddle-apps__cardbody"},e.length>0&&(0,a.createElement)(_u,null,e.map(e=>{return(0,a.createElement)(wu,{key:e.uuid,icon:(0,a.createElement)(im,{app:lm(e.label||e.name)}),title:(0,a.createElement)(a.Fragment,null,(0,a.createElement)(hu,{tone:e.last_used?"success":"neutral"})," ",e.label||e.name),description:(e.last_used?(0,Op.sprintf)(/* translators: %s: date and time. */ /* translators: %s: date and time. */ diff --git a/admin/src/components/McpDiagnostics.jsx b/admin/src/components/McpDiagnostics.jsx index c85081a..f79996e 100644 --- a/admin/src/components/McpDiagnostics.jsx +++ b/admin/src/components/McpDiagnostics.jsx @@ -187,6 +187,7 @@ export default function McpDiagnostics() {