UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

358 lines (349 loc) 17.3 kB
import{a as e,m as t,o as n,p as r,r as i}from"./lit-runtime-BImxIzGR.js";import{o as a,t as o}from"./string-coerce-Dt6Kt5Fx.js";import{P as s,R as c,_ as l,g as u}from"./index-B6YtUsIL.js";import{i as d,n as f,r as p,t as m}from"./skills-shared-CNlkuf6o.js";function h(e){return e?u(e,window.location.href):null}function g(e){!(e instanceof HTMLDialogElement)||e.open||(e.isConnected?e.showModal():queueMicrotask(()=>{e.isConnected&&!e.open&&e.showModal()}))}var _=[{id:`all`,label:`All`},{id:`ready`,label:`Ready`},{id:`needs-setup`,label:`Needs Setup`},{id:`disabled`,label:`Disabled`}];function v(e,t){switch(t){case`all`:return!0;case`ready`:return!e.disabled&&e.eligible;case`needs-setup`:return!e.disabled&&!e.eligible;case`disabled`:return e.disabled}throw Error(`Unsupported skills status filter`)}function y(e){return e.disabled?`muted`:e.eligible?`ok`:`warn`}function b(e,t){let n=e.clawhub;return!n||n.status!==`linked`||!n.valid?null:t[s({registry:n.registry,slug:n.slug,version:n.installedVersion})]??null}function x(e){if(!e)return`Unavailable`;let t=e.securityStatus?.trim()||null;return e.ok&&e.decision===`pass`?t===`clean`||!t?`Clean`:t:t===`pending`||t===`not-run`?`Pending`:t===`malicious`?`Blocked`:t===`suspicious`?`Review`:`Unavailable`}function S(e){if(!e)return`chip-warn`;if(e.ok&&e.decision===`pass`)return`chip-ok`;let t=e.securityStatus?.trim()||null;return t===`pending`||t===`not-run`?`chip`:`chip-warn`}function C(n){let i=n.report?.skills??[],s={all:i.length,ready:0,"needs-setup":0,disabled:0};for(let e of i)e.disabled?s.disabled++:e.eligible?s.ready++:s[`needs-setup`]++;let c=n.statusFilter===`all`?i:i.filter(e=>v(e,n.statusFilter)),l=o(n.filter),u=l?c.filter(e=>o([e.name,e.description,e.source].join(` `)).includes(l)):c,f=d(u),p=n.detailKey?i.find(e=>e.skillKey===n.detailKey)??null:null;return t` <section class="card"> <div class="row" style="justify-content: space-between;"> <div> <div class="card-title">Skills</div> <div class="card-sub">Installed skills and their status.</div> </div> <button class="btn" ?disabled=${n.loading||!n.connected} @click=${n.onRefresh} > ${n.loading?a(`common.loading`):a(`common.refresh`)} </button> </div> <div class="agent-tabs" style="margin-top: 14px;"> ${_.map(e=>t` <button class="agent-tab ${n.statusFilter===e.id?`active`:``}" @click=${()=>n.onStatusFilterChange(e.id)} > ${e.label}<span class="agent-tab-count">${s[e.id]}</span> </button> `)} </div> <div class="filters" style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 12px;" > <label class="field" style="flex: 1; min-width: 180px;"> <input .value=${n.filter} @input=${e=>n.onFilterChange(e.target.value)} placeholder="Filter installed skills" autocomplete="off" name="skills-filter" /> </label> <div class="muted">${u.length} shown</div> </div> <div style="margin-top: 16px; border-top: 1px solid var(--border); padding-top: 16px;"> <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 12px;"> <div style="font-weight: 600;">ClawHub</div> <div class="muted" style="font-size: 13px;"> Search and install skills from the registry </div> </div> <div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap;"> <label class="field" style="flex: 1; min-width: 180px;"> <input .value=${n.clawhubQuery} @input=${e=>n.onClawHubQueryChange(e.target.value)} placeholder="Search ClawHub skills…" autocomplete="off" name="clawhub-search" /> </label> ${n.clawhubSearchLoading?t`<span class="muted">Searching…</span>`:r} </div> ${n.clawhubSearchError?t`<div class="callout danger" style="margin-top: 8px;"> ${n.clawhubSearchError} </div>`:r} ${n.clawhubInstallMessage?t`<div class="callout ${n.clawhubInstallMessage.kind===`error`?`danger`:`success`}" style="margin-top: 8px;" > ${n.clawhubInstallMessage.text} </div>`:r} ${w(n)} </div> ${n.error?t`<div class="callout danger" style="margin-top: 12px;">${n.error}</div>`:r} ${u.length===0?t` <div class="muted" style="margin-top: 16px"> ${!n.connected&&!n.report?`Not connected to gateway.`:`No skills found.`} </div> `:t` <div class="agent-skills-groups" style="margin-top: 16px;"> ${f.map(r=>t` <details class="agent-skills-group" open> <summary class="agent-skills-header"> <span>${r.label}</span> <span class="muted">${r.skills.length}</span> </summary> <div class="list skills-grid"> ${e(r.skills,e=>e.skillKey,e=>E(e,n))} </div> </details> `)} </div> `} </section> ${p?D(p,n):r} ${n.clawhubDetailSlug?T(n):r} `}function w(e){let n=e.clawhubResults;return n?n.length===0?t`<div class="muted" style="margin-top: 8px;">No skills found on ClawHub.</div>`:t` <div class="list" style="margin-top: 8px;"> ${n.map(n=>t` <div class="list-item list-item-clickable" @click=${()=>e.onClawHubDetailOpen(n.slug)} > <div class="list-main"> <div class="list-title">${n.displayName}</div> <div class="list-sub">${n.summary?c(n.summary,120):n.slug}</div> </div> <div class="list-meta" style="display: flex; align-items: center; gap: 8px;"> ${n.version?t`<span class="muted" style="font-size: 12px;">v${n.version}</span>`:r} <button class="btn btn--sm" ?disabled=${e.clawhubInstallSlug!==null} @click=${t=>{t.stopPropagation(),e.onClawHubInstall(n.slug)}} > ${e.clawhubInstallSlug===n.slug?`Installing…`:`Install`} </button> </div> </div> `)} </div> `:r}function T(e){let i=e.clawhubDetail;return t` <dialog class="md-preview-dialog" ${n(g)} @click=${e=>{let t=e.currentTarget;e.target===t&&t.close()}} @close=${e.onClawHubDetailClose} > <div class="md-preview-dialog__panel"> <div class="md-preview-dialog__header"> <div class="md-preview-dialog__title"> ${i?.skill?.displayName??e.clawhubDetailSlug} </div> <button class="btn btn--sm" @click=${e=>{e.currentTarget.closest(`dialog`)?.close()}} > Close </button> </div> <div class="md-preview-dialog__body" style="display: grid; gap: 16px;"> ${e.clawhubDetailLoading?t`<div class="muted">${a(`common.loading`)}</div>`:e.clawhubDetailError?t`<div class="callout danger">${e.clawhubDetailError}</div>`:i?.skill?t` <div style="font-size: 14px; line-height: 1.5;"> ${i.skill.summary??``} </div> ${i.owner?.displayName?t`<div class="muted" style="font-size: 13px;"> By ${i.owner.displayName}${i.owner.handle?t` (@${i.owner.handle})`:r} </div>`:r} ${i.latestVersion?t`<div class="muted" style="font-size: 13px;"> Latest: v${i.latestVersion.version} </div>`:r} ${i.latestVersion?.changelog?t`<div style="font-size: 13px; border-top: 1px solid var(--border); padding-top: 12px; white-space: pre-wrap;" > ${i.latestVersion.changelog} </div>`:r} ${i.metadata?.os?t`<div class="muted" style="font-size: 12px;"> Platforms: ${i.metadata.os.join(`, `)} </div>`:r} <button class="btn primary" ?disabled=${e.clawhubInstallSlug!==null} @click=${()=>{e.clawhubDetailSlug&&e.onClawHubInstall(e.clawhubDetailSlug)}} > ${e.clawhubInstallSlug===e.clawhubDetailSlug?`Installing…`:`Install ${i.skill.displayName}`} </button> `:t`<div class="muted">Skill not found.</div>`} </div> </div> </dialog> `}function E(e,n){let i=n.busyKey===e.skillKey,a=y(e),o=b(e,n.clawhubVerdicts);return t` <div class="list-item list-item-clickable" @click=${()=>n.onDetailOpen(e.skillKey)}> <div class="list-main"> <div class="list-title" style="display: flex; align-items: center; gap: 8px;"> <span class="statusDot ${a}"></span> ${e.emoji?t`<span>${e.emoji}</span>`:r} <span>${e.name}</span> </div> <div class="list-sub">${c(e.description,140)}</div> </div> <div class="list-meta" style="display: flex; align-items: center; justify-content: flex-end; gap: 10px;" > ${e.clawhub?.status===`linked`?t`<span class="chip ${S(o)}">${x(o)}</span>`:e.clawhub?.status===`invalid`?t`<span class="chip chip-warn">ClawHub link invalid</span>`:r} <label class="skill-toggle-wrap" @click=${e=>e.stopPropagation()}> <input type="checkbox" class="skill-toggle" .checked=${!e.disabled} ?disabled=${i} @change=${t=>{t.stopPropagation(),n.onToggle(e.skillKey,e.disabled)}} /> </label> </div> </div> `}function D(e,i){let a=i.busyKey===e.skillKey,o=i.edits[e.skillKey]??``,s=i.messages[e.skillKey]??null,c=e.install.length>0&&e.missing.bins.length>0,l=!!(e.bundled&&e.source!==`openclaw-bundled`),u=m(e),d=f(e),_=b(e,i.clawhubVerdicts),v=i.detailTab===`card`&&e.skillCard?.present?`card`:`overview`;return t` <dialog class="md-preview-dialog" ${n(g)} @click=${e=>{let t=e.currentTarget;e.target===t&&t.close()}} @close=${i.onDetailClose} > <div class="md-preview-dialog__panel"> <div class="md-preview-dialog__header"> <div class="md-preview-dialog__title" style="display: flex; align-items: center; gap: 8px;" > <span class="statusDot ${y(e)}"></span> ${e.emoji?t`<span style="font-size: 18px;">${e.emoji}</span>`:r} <span>${e.name}</span> </div> <button class="btn btn--sm" @click=${e=>{e.currentTarget.closest(`dialog`)?.close()}} > Close </button> </div> <div class="md-preview-dialog__body" style="display: grid; gap: 16px;"> <div> <div style="font-size: 14px; line-height: 1.5; color: var(--text);"> ${e.description} </div> ${p({skill:e,showBundledBadge:l})} </div> ${e.clawhub||e.skillCard?.present?t` <div class="agent-tabs"> <button class="agent-tab ${v===`overview`?`active`:``}" @click=${()=>i.onDetailTabChange(`overview`)} > Overview </button> ${e.skillCard?.present?t`<button class="agent-tab ${v===`card`?`active`:``}" @click=${()=>i.onDetailTabChange(`card`)} > Skill Card </button>`:r} </div> `:r} ${v===`overview`?O(e,i,_):k(e,i)} ${u.length>0?t` <div class="callout" style="border-color: var(--warn-subtle); background: var(--warn-subtle); color: var(--warn);" > <div style="font-weight: 600; margin-bottom: 4px;">Missing requirements</div> <div>${u.join(`, `)}</div> </div> `:r} ${d.length>0?t` <div class="muted" style="font-size: 13px;">Reason: ${d.join(`, `)}</div> `:r} <div style="display: flex; align-items: center; gap: 12px;"> <label class="skill-toggle-wrap"> <input type="checkbox" class="skill-toggle" .checked=${!e.disabled} ?disabled=${a} @change=${()=>i.onToggle(e.skillKey,e.disabled)} /> </label> <span style="font-size: 13px; font-weight: 500;"> ${e.disabled?`Disabled`:`Enabled`} </span> ${c?t`<button class="btn" ?disabled=${a} @click=${()=>i.onInstall(e.skillKey,e.name,e.install[0].id)} > ${a?`Installing…`:e.install[0].label} </button>`:r} </div> ${s?t`<div class="callout ${s.kind===`error`?`danger`:`success`}"> ${s.message} </div>`:r} ${e.primaryEnv?t` <div style="display: grid; gap: 8px;"> <div class="field"> <span >API key <span class="muted" style="font-weight: normal; font-size: 0.88em;" >(${e.primaryEnv})</span ></span > <input type="password" .value=${o} @input=${t=>i.onEdit(e.skillKey,t.target.value)} /> </div> ${(()=>{let n=h(e.homepage);return n?t`<div class="muted" style="font-size: 13px;"> Get your key: <a href="${n}" target="_blank" rel="noopener noreferrer" >${e.homepage}</a > </div>`:r})()} <button class="btn primary" ?disabled=${a} @click=${()=>i.onSaveKey(e.skillKey)} > Save key </button> </div> `:r} <div style="border-top: 1px solid var(--border); padding-top: 12px; display: grid; gap: 6px; font-size: 12px; color: var(--muted);" > <div><span style="font-weight: 600;">Source:</span> ${e.source}</div> <div style="font-family: var(--mono); word-break: break-all;">${e.filePath}</div> ${(()=>{let n=h(e.homepage);return n?t`<div> <a href="${n}" target="_blank" rel="noopener noreferrer" >${e.homepage}</a > </div>`:r})()} </div> </div> </div> </dialog> `}function O(e,n,i){let a=e.clawhub;if(!a)return r;if(a.status===`invalid`)return t`<div class="callout danger"> <div style="font-weight: 600; margin-bottom: 4px;">ClawHub link invalid</div> <div>${a.reason}</div> </div>`;let o=h(i?.securityAuditUrl??void 0),s=i?.reasons?.length?i.reasons.join(`, `):null;return t` <div class="callout" style="display: grid; gap: 8px; border-color: var(--border); background: var(--panel-2);" > <div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;"> <span class="chip ${S(i)}">${x(i)}</span> <span class="muted" style="font-size: 12px;">${a.slug}@${a.installedVersion}</span> ${n.clawhubVerdictsLoading?t`<span class="muted">Refreshing…</span>`:r} </div> ${n.clawhubVerdictsError?t`<div class="muted" style="font-size: 13px;">${n.clawhubVerdictsError}</div>`:s?t`<div class="muted" style="font-size: 13px;">${s}</div>`:r} ${o?t`<div style="font-size: 13px;"> <a href="${o}" target="_blank" rel="noopener noreferrer" >Full security report</a > </div>`:r} </div> `}function k(e,n){if(!e.skillCard?.present)return r;let a=n.skillCardContents[e.skillKey];if(a===void 0){let r=n.skillCardErrors[e.skillKey];return r?t`<div class="callout danger">${r}</div>`:t`<div class="muted" style="font-size: 13px;"> ${n.skillCardLoadingKey===e.skillKey?`Loading Skill Card...`:`Skill Card not loaded.`} </div>`}return t` <article class="sidebar-markdown" style="max-width: 100%; overflow-wrap: anywhere;"> ${i(l(a))} </article> `}export{C as renderSkills}; //# sourceMappingURL=skills-CzxvTp7B.js.map