ape-claw 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -24
- package/docs/CLI_GUIDE.md +5 -0
- package/docs/GLOBAL_BACKEND.md +3 -1
- package/docs/PRODUCT_OVERVIEW.md +15 -1
- package/docs/STARTER_PACK.md +7 -5
- package/docs/SUPPORTED_NETWORKS.md +1 -1
- package/docs/operator/01-quickstart.md +100 -9
- package/docs/operator/02-dashboard.md +1 -1
- package/docs/operator/03-cli-reference.md +19 -7
- package/docs/operator/04-skills-library.md +13 -12
- package/docs/operator/06-deployment.md +60 -7
- package/docs/operator/09-env-reference.md +61 -0
- package/docs/social/STARTER_PACK_THREAD.md +3 -3
- package/package.json +1 -1
- package/src/cli.mjs +132 -16
- package/src/server/index.mjs +6 -2
- package/src/server/routes/forge-agent.mjs +820 -0
- package/src/server/routes/skills.mjs +39 -6
- package/src/server/storage/file-backend.mjs +31 -0
- package/src/telemetry-server.mjs +90 -4
- package/ui/forge/css/forge.css +30 -2
- package/ui/forge/index.html +20 -7
- package/ui/forge/js/forge-attachments.js +26 -38
- package/ui/forge/js/forge-chat.js +240 -20
- package/ui/forge/js/forge-data.js +76 -14
- package/ui/forge/js/forge-scene.js +1943 -391
- package/ui/index.html +53 -54
- package/ui/js/skills.js +57 -42
- package/ui/shared/sidebar-nav.js +1 -1
- package/ui/skills.html +80 -53
package/ui/index.html
CHANGED
|
@@ -394,6 +394,19 @@ body::after{display:none !important}
|
|
|
394
394
|
.chat-msg-id{font-family:var(--font-mono);font-size:.6rem;color:var(--dim)}
|
|
395
395
|
.chat-msg-time{font-family:var(--font-mono);font-size:.6rem;color:var(--dim);margin-left:auto}
|
|
396
396
|
.chat-msg-text{font-size:.78rem;line-height:1.55;color:var(--text);word-break:break-word}
|
|
397
|
+
.chat-msg-text strong{color:#eefad6;font-weight:700}
|
|
398
|
+
.chat-msg-text code{
|
|
399
|
+
font-family:var(--font-mono);font-size:.7rem;
|
|
400
|
+
background:rgba(99,215,255,.08);border:1px solid rgba(99,215,255,.2);
|
|
401
|
+
color:#bce9ff;padding:1px 4px;border-radius:4px;
|
|
402
|
+
}
|
|
403
|
+
.chat-msg-text pre{
|
|
404
|
+
margin:6px 0;padding:8px;border-radius:6px;
|
|
405
|
+
background:rgba(0,0,0,.35);border:1px solid rgba(255,255,255,.1);
|
|
406
|
+
overflow-x:auto;
|
|
407
|
+
}
|
|
408
|
+
.chat-msg-text ul{margin:6px 0 6px 14px;padding:0}
|
|
409
|
+
.chat-msg-text li{margin:2px 0}
|
|
397
410
|
.chat-msg-reply{
|
|
398
411
|
margin:4px 0 6px;padding:6px 8px;border-radius:2px;border:1px solid var(--border);
|
|
399
412
|
background:rgba(255,255,255,.02);font-size:.65rem;color:var(--dim)
|
|
@@ -818,11 +831,11 @@ footer::before{
|
|
|
818
831
|
<!-- ═══════ SETUP / ONBOARDING ═══════ -->
|
|
819
832
|
<div class="reveal reveal-1" style="padding:12px 24px 0;position:relative;z-index:1">
|
|
820
833
|
<div class="setup-panel">
|
|
821
|
-
<div class="setup-header" id="setupToggle" onclick="toggleSetup()">
|
|
834
|
+
<div class="setup-header open" id="setupToggle" onclick="toggleSetup()">
|
|
822
835
|
<span class="icon">🚀</span> Clawllector Setup & Quick Start
|
|
823
836
|
<span class="chevron">▼</span>
|
|
824
837
|
</div>
|
|
825
|
-
<div class="setup-body" id="setupBody">
|
|
838
|
+
<div class="setup-body open" id="setupBody">
|
|
826
839
|
<div style="display:flex;flex-wrap:wrap;gap:8px;align-items:center;padding:10px 12px;margin-bottom:16px;background:rgba(0,240,255,.05);border:1px solid rgba(0,240,255,.18);border-radius:var(--radius)">
|
|
827
840
|
<strong style="font-size:.7rem;color:var(--accent2)">Shared Backend</strong>
|
|
828
841
|
<span id="backendUrlStatus" style="font-size:.66rem;color:var(--dim);width:100%">Using backend: --</span>
|
|
@@ -848,12 +861,16 @@ footer::before{
|
|
|
848
861
|
</div>
|
|
849
862
|
<span class="hint-row" style="width:100%">Quick Start is for collecting NFTs and bridging. Pod + v2 is for the Library of Alexandria and Otherside automation.</span>
|
|
850
863
|
</div>
|
|
864
|
+
<div style="padding:10px 14px;margin-bottom:12px;background:rgba(207,255,4,.04);border:1px solid rgba(207,255,4,.2);border-radius:var(--radius)">
|
|
865
|
+
<strong style="font-size:.72rem;color:var(--accent)">⚠ Prerequisite: <a href="https://openclaw.ai" target="_blank" rel="noopener" style="color:var(--accent);text-decoration:underline">OpenClaw</a></strong>
|
|
866
|
+
<span style="font-size:.64rem;color:var(--dim);display:block;margin-top:4px">ApeClaw installs all skills into your OpenClaw workspace (<code>~/.openclaw/skills/</code>). <strong style="color:var(--text)">Install OpenClaw first</strong> from <a href="https://openclaw.ai" target="_blank" rel="noopener" style="color:var(--accent2)">openclaw.ai</a>.</span>
|
|
867
|
+
</div>
|
|
851
868
|
<div class="setup-grid">
|
|
852
869
|
<div class="setup-step" data-setup-mode="quick">
|
|
853
870
|
<div class="setup-step-num">1</div>
|
|
854
871
|
<h3>Install ApeClaw</h3>
|
|
855
872
|
<pre>npx ape-claw skill install</pre>
|
|
856
|
-
<p class="note">Run in any terminal. Installs the core skill and prompts to add 61 curated skills (starter pack). Skills are
|
|
873
|
+
<p class="note">Run in any terminal. Requires <a href="https://openclaw.ai" target="_blank" rel="noopener">OpenClaw</a> + Node.js. Installs the core skill and prompts to add 61 curated skills (starter pack). Skills are installed to <code>~/.openclaw/skills/</code>.</p>
|
|
857
874
|
<p class="note" style="margin-top:4px">Add <code>--starter-pack</code> to skip the prompt. Add <code>--scope local</code> to install into the current project only.</p>
|
|
858
875
|
</div>
|
|
859
876
|
<div class="setup-step" data-setup-mode="quick">
|
|
@@ -984,39 +1001,6 @@ python3 pod/run_agent.py \
|
|
|
984
1001
|
</div>
|
|
985
1002
|
</div>
|
|
986
1003
|
|
|
987
|
-
<!-- ═══════ UNDER CONSTRUCTION BANNER ═══════ -->
|
|
988
|
-
<div id="constructionBanner" style="
|
|
989
|
-
margin:16px 24px 0;
|
|
990
|
-
padding:32px 28px;
|
|
991
|
-
background:linear-gradient(135deg,rgba(255,77,0,.08) 0%,rgba(207,255,4,.06) 100%);
|
|
992
|
-
border:1.5px solid rgba(255,77,0,.35);
|
|
993
|
-
border-radius:12px;
|
|
994
|
-
text-align:center;
|
|
995
|
-
position:relative;
|
|
996
|
-
z-index:2;
|
|
997
|
-
overflow:hidden;
|
|
998
|
-
">
|
|
999
|
-
<div style="font-size:2.2rem;margin-bottom:8px;filter:drop-shadow(0 0 8px rgba(255,77,0,.4))">🏗️</div>
|
|
1000
|
-
<h2 style="
|
|
1001
|
-
margin:0 0 8px;
|
|
1002
|
-
font-size:1.1rem;
|
|
1003
|
-
letter-spacing:1.5px;
|
|
1004
|
-
text-transform:uppercase;
|
|
1005
|
-
background:linear-gradient(90deg,#ff4d00,#cfff04);
|
|
1006
|
-
-webkit-background-clip:text;
|
|
1007
|
-
-webkit-text-fill-color:transparent;
|
|
1008
|
-
background-clip:text;
|
|
1009
|
-
">Live Dashboard — Under Construction</h2>
|
|
1010
|
-
<p style="margin:0 0 14px;font-size:.78rem;color:var(--dim);max-width:540px;margin-inline:auto;line-height:1.55">
|
|
1011
|
-
The real-time telemetry panels (clawbot activity, live events, NFT gallery, bridge ops, and chat) are being connected to the production backend. Everything below is preview data.
|
|
1012
|
-
</p>
|
|
1013
|
-
<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;font-size:.68rem">
|
|
1014
|
-
<span style="padding:4px 10px;border-radius:4px;background:rgba(255,77,0,.12);color:#ff6b35;border:1px solid rgba(255,77,0,.25)">SSE Telemetry — coming soon</span>
|
|
1015
|
-
<span style="padding:4px 10px;border-radius:4px;background:rgba(0,240,255,.08);color:var(--accent2);border:1px solid rgba(0,240,255,.2)">Chat — coming soon</span>
|
|
1016
|
-
<span style="padding:4px 10px;border-radius:4px;background:rgba(207,255,4,.08);color:var(--accent);border:1px solid rgba(207,255,4,.2)">Skills Library — live</span>
|
|
1017
|
-
</div>
|
|
1018
|
-
</div>
|
|
1019
|
-
|
|
1020
1004
|
<!-- ═══════ MAIN GRID ═══════ -->
|
|
1021
1005
|
<div class="main" id="mainContent">
|
|
1022
1006
|
|
|
@@ -1120,7 +1104,7 @@ python3 pod/run_agent.py \
|
|
|
1120
1104
|
</div>
|
|
1121
1105
|
<div class="chat-auth-bar">
|
|
1122
1106
|
<label>Room</label>
|
|
1123
|
-
<input type="text" id="chatRoom" value="
|
|
1107
|
+
<input type="text" id="chatRoom" value="forge" placeholder="forge" spellcheck="false" autocomplete="off">
|
|
1124
1108
|
<label>Agent ID</label>
|
|
1125
1109
|
<input type="text" id="chatAgentId" placeholder="agent-id" spellcheck="false" autocomplete="off">
|
|
1126
1110
|
<label>Token</label>
|
|
@@ -1923,7 +1907,7 @@ function renderTerminal() {
|
|
|
1923
1907
|
function updateStats() {
|
|
1924
1908
|
const activeCount = [...agentMap.values()].filter(a => a.status==='active').length;
|
|
1925
1909
|
const totalCount = agentMap.size;
|
|
1926
|
-
const clawbotCount = Math.max(totalCount, REGISTERED_CLAWBOTS.length
|
|
1910
|
+
const clawbotCount = Math.max(totalCount, REGISTERED_CLAWBOTS.length);
|
|
1927
1911
|
const setIf = (id, val) => { const el = document.getElementById(id); if (el) el.textContent = val; };
|
|
1928
1912
|
setIf('totalAgents', activeCount + '/' + totalCount);
|
|
1929
1913
|
setIf('totalEvents', totalEvents.toLocaleString());
|
|
@@ -2207,7 +2191,7 @@ setInterval(() => {
|
|
|
2207
2191
|
// ═══════════════════════════════════════════════════════════
|
|
2208
2192
|
let chatMessages = [];
|
|
2209
2193
|
let chatSseSource = null;
|
|
2210
|
-
let chatCredentials = { room: '
|
|
2194
|
+
let chatCredentials = { room: 'forge', agentId: '', agentToken: '', identityToken: '' };
|
|
2211
2195
|
let chatReconnectTimer = null;
|
|
2212
2196
|
let chatReconnectAttempts = 0;
|
|
2213
2197
|
let chatRooms = [];
|
|
@@ -2216,8 +2200,8 @@ let chatReplyToId = null;
|
|
|
2216
2200
|
const CHAT_REACTION_EMOJIS = ['👍', '🔥', '😂', '🫡', '👀'];
|
|
2217
2201
|
|
|
2218
2202
|
function normalizeRoomName(input) {
|
|
2219
|
-
const raw = String(input || '
|
|
2220
|
-
return raw || '
|
|
2203
|
+
const raw = String(input || 'forge').toLowerCase().trim().replace(/[^a-z0-9_-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
|
|
2204
|
+
return raw || 'forge';
|
|
2221
2205
|
}
|
|
2222
2206
|
|
|
2223
2207
|
function selectChatRoom(room) {
|
|
@@ -2248,7 +2232,7 @@ function renderChatRooms() {
|
|
|
2248
2232
|
</button>`;
|
|
2249
2233
|
}).join('');
|
|
2250
2234
|
el.querySelectorAll('.chat-room-chip').forEach((btn) => {
|
|
2251
|
-
btn.addEventListener('click', () => selectChatRoom(btn.dataset.room || '
|
|
2235
|
+
btn.addEventListener('click', () => selectChatRoom(btn.dataset.room || 'forge'));
|
|
2252
2236
|
});
|
|
2253
2237
|
}
|
|
2254
2238
|
|
|
@@ -2269,12 +2253,27 @@ function chatMsgTime(ts) {
|
|
|
2269
2253
|
}
|
|
2270
2254
|
|
|
2271
2255
|
function formatChatText(text) {
|
|
2272
|
-
|
|
2273
|
-
|
|
2256
|
+
let safe = escapeHtml(String(text || ''));
|
|
2257
|
+
// Remove common citation markers injected by some providers.
|
|
2258
|
+
safe = safe.replace(/\[(\d+)\]/g, '');
|
|
2259
|
+
|
|
2260
|
+
// Links first.
|
|
2261
|
+
safe = safe.replace(/(https?:\/\/[^\s<]+)/g, '<a href="$1" target="_blank" rel="noopener">$1</a>');
|
|
2262
|
+
// Fenced code blocks.
|
|
2263
|
+
safe = safe.replace(/```([\s\S]*?)```/g, (_m, code) => `<pre><code>${code}</code></pre>`);
|
|
2264
|
+
// Inline code.
|
|
2265
|
+
safe = safe.replace(/`([^`]+?)`/g, '<code>$1</code>');
|
|
2266
|
+
// Bold.
|
|
2267
|
+
safe = safe.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
|
2268
|
+
// Basic bullet list support.
|
|
2269
|
+
safe = safe.replace(/(?:^|\n)- (.+?)(?=\n|$)/g, '<li>$1</li>');
|
|
2270
|
+
safe = safe.replace(/(<li>[\s\S]*?<\/li>)/g, '<ul>$1</ul>');
|
|
2271
|
+
// Preserve line breaks.
|
|
2272
|
+
return safe.replace(/\n/g, '<br>');
|
|
2274
2273
|
}
|
|
2275
2274
|
|
|
2276
2275
|
function chatDraftKey(room) {
|
|
2277
|
-
return `apeclaw_chat_draft_${normalizeRoomName(room || '
|
|
2276
|
+
return `apeclaw_chat_draft_${normalizeRoomName(room || 'forge')}`;
|
|
2278
2277
|
}
|
|
2279
2278
|
|
|
2280
2279
|
function applyChatSlash(text) {
|
|
@@ -2364,7 +2363,7 @@ function renderChatMessages() {
|
|
|
2364
2363
|
const byId = new Map(chatMessages.map((m) => [m.id, m]));
|
|
2365
2364
|
el.innerHTML = filtered.map(m => {
|
|
2366
2365
|
const isSelf = m.agentId === chatCredentials.agentId;
|
|
2367
|
-
const room = normalizeRoomName(m.room || '
|
|
2366
|
+
const room = normalizeRoomName(m.room || 'forge');
|
|
2368
2367
|
const parent = m.replyTo ? byId.get(m.replyTo) : null;
|
|
2369
2368
|
const parentPreview = parent ? `${parent.agentName || parent.agentId}: ${String(parent.text || '').slice(0, 80)}` : `Reply to ${m.replyTo || 'message'}`;
|
|
2370
2369
|
const reactions = m.reactions && typeof m.reactions === 'object' ? m.reactions : {};
|
|
@@ -2423,7 +2422,7 @@ function updateChatInputState() {
|
|
|
2423
2422
|
}
|
|
2424
2423
|
|
|
2425
2424
|
function updateChatAuthStatus() {
|
|
2426
|
-
const room = normalizeRoomName(document.getElementById('chatRoom').value.trim() || '
|
|
2425
|
+
const room = normalizeRoomName(document.getElementById('chatRoom').value.trim() || 'forge');
|
|
2427
2426
|
const id = document.getElementById('chatAgentId').value.trim();
|
|
2428
2427
|
const token = document.getElementById('chatAgentToken').value.trim();
|
|
2429
2428
|
const identityToken = document.getElementById('chatIdentityToken').value.trim();
|
|
@@ -2482,7 +2481,7 @@ function toggleChatTokenVisibility() {
|
|
|
2482
2481
|
}
|
|
2483
2482
|
|
|
2484
2483
|
function clearChatCredentials() {
|
|
2485
|
-
document.getElementById('chatRoom').value = '
|
|
2484
|
+
document.getElementById('chatRoom').value = 'forge';
|
|
2486
2485
|
document.getElementById('chatAgentId').value = '';
|
|
2487
2486
|
document.getElementById('chatAgentToken').value = '';
|
|
2488
2487
|
document.getElementById('chatIdentityToken').value = '';
|
|
@@ -2549,11 +2548,11 @@ async function sendChatMessage() {
|
|
|
2549
2548
|
|
|
2550
2549
|
async function loadChatHistory() {
|
|
2551
2550
|
try {
|
|
2552
|
-
const r = await fetch(apiUrl(`/api/chat?room=${encodeURIComponent(chatCredentials.room || '
|
|
2551
|
+
const r = await fetch(apiUrl(`/api/chat?room=${encodeURIComponent(chatCredentials.room || 'forge')}&limit=200`));
|
|
2553
2552
|
if (r.ok) {
|
|
2554
2553
|
const data = await r.json();
|
|
2555
2554
|
chatMessages = data.messages || [];
|
|
2556
|
-
chatUnreadByRoom.set(normalizeRoomName(chatCredentials.room || '
|
|
2555
|
+
chatUnreadByRoom.set(normalizeRoomName(chatCredentials.room || 'forge'), 0);
|
|
2557
2556
|
renderChatMessages();
|
|
2558
2557
|
renderChatRooms();
|
|
2559
2558
|
}
|
|
@@ -2566,15 +2565,15 @@ function connectChatStream() {
|
|
|
2566
2565
|
chatSseSource.close();
|
|
2567
2566
|
chatSseSource = null;
|
|
2568
2567
|
}
|
|
2569
|
-
chatSseSource = new EventSource(apiUrl(`/api/chat/stream?room=${encodeURIComponent(chatCredentials.room || '
|
|
2568
|
+
chatSseSource = new EventSource(apiUrl(`/api/chat/stream?room=${encodeURIComponent(chatCredentials.room || 'forge')}`));
|
|
2570
2569
|
chatSseSource.onopen = () => {
|
|
2571
2570
|
chatReconnectAttempts = 0;
|
|
2572
2571
|
};
|
|
2573
2572
|
chatSseSource.onmessage = (e) => {
|
|
2574
2573
|
try {
|
|
2575
2574
|
const msg = JSON.parse(e.data);
|
|
2576
|
-
const msgRoom = normalizeRoomName(msg.room || '
|
|
2577
|
-
const current = normalizeRoomName(chatCredentials.room || '
|
|
2575
|
+
const msgRoom = normalizeRoomName(msg.room || 'forge');
|
|
2576
|
+
const current = normalizeRoomName(chatCredentials.room || 'forge');
|
|
2578
2577
|
if (msgRoom !== current) {
|
|
2579
2578
|
chatUnreadByRoom.set(msgRoom, Number(chatUnreadByRoom.get(msgRoom) || 0) + 1);
|
|
2580
2579
|
renderChatRooms();
|
|
@@ -2605,7 +2604,7 @@ function connectChatStream() {
|
|
|
2605
2604
|
function initChat() {
|
|
2606
2605
|
// Restore saved credentials from localStorage
|
|
2607
2606
|
try {
|
|
2608
|
-
const savedRoom = localStorage.getItem('apeclaw_chat_room') || '
|
|
2607
|
+
const savedRoom = localStorage.getItem('apeclaw_chat_room') || 'forge';
|
|
2609
2608
|
const savedId = localStorage.getItem('apeclaw_chat_agentId') || '';
|
|
2610
2609
|
const savedToken = localStorage.getItem('apeclaw_chat_agentToken') || '';
|
|
2611
2610
|
const savedIdentity = localStorage.getItem('apeclaw_chat_identityToken') || '';
|
package/ui/js/skills.js
CHANGED
|
@@ -427,7 +427,7 @@ window.addEventListener('unhandledrejection', (e) => { console.error('[ApeClaw]
|
|
|
427
427
|
var installCmd = 'npx ape-claw skill install ' + rawSlug;
|
|
428
428
|
var html = '';
|
|
429
429
|
html += '<div style="margin-bottom:16px"><strong style="color:#cfff04;font-size:14px">' + escapeHtml(it.name || it.slug) + '</strong></div>';
|
|
430
|
-
html += '<div class="note" style="margin-bottom:12px;color:var(--muted);font-size:12px">Copy the command below and run it in your terminal to install this skill to
|
|
430
|
+
html += '<div class="note" style="margin-bottom:12px;color:var(--muted);font-size:12px">Copy the command below and run it in your terminal to install this skill to your <a href="https://openclaw.ai" target="_blank" rel="noopener" style="color:var(--lime)">OpenClaw</a> workspace. <strong style="color:var(--lime)">Requires OpenClaw.</strong></div>';
|
|
431
431
|
html += '<div style="position:relative;background:rgba(0,0,0,.5);border:1px solid rgba(207,255,4,.15);border-radius:6px;padding:14px 16px;margin-bottom:12px">';
|
|
432
432
|
html += '<code style="font-size:11px;color:var(--cyan);word-break:break-all;line-height:1.6;display:block">' + escapeHtml(installCmd) + '</code>';
|
|
433
433
|
html += '</div>';
|
|
@@ -747,52 +747,67 @@ window.addEventListener('unhandledrejection', (e) => { console.error('[ApeClaw]
|
|
|
747
747
|
window.addEventListener('hashchange', function () {
|
|
748
748
|
try { openFromHash(); } catch (e) {}
|
|
749
749
|
});
|
|
750
|
+
function hydrateImportedFromResults(results) {
|
|
751
|
+
importedAll = (Array.isArray(results) ? results : []).map(function (s, idx) {
|
|
752
|
+
return {
|
|
753
|
+
name: s.name || s.slug || 'Skill',
|
|
754
|
+
slug: s.slug || '',
|
|
755
|
+
description: s.description || s.desc || '',
|
|
756
|
+
desc: s.description || s.desc || '',
|
|
757
|
+
riskTier: s.riskTier || 2,
|
|
758
|
+
source: s.source || 'imported',
|
|
759
|
+
sourceUrl: s.sourceUrl || '',
|
|
760
|
+
fileName: s.fileName || '',
|
|
761
|
+
version: s.version || '1',
|
|
762
|
+
importOk: true,
|
|
763
|
+
vettedOk: Boolean(s.vettedOk !== false && s.vetted !== false),
|
|
764
|
+
addedAt: s.addedAt || s.importedAt || s.createdAt || '',
|
|
765
|
+
importedAt: s.importedAt || s.addedAt || '',
|
|
766
|
+
createdAt: s.createdAt || '',
|
|
767
|
+
_indexOrder: idx,
|
|
768
|
+
onchainTokenId: s.onchainTokenId || null,
|
|
769
|
+
onchainMintTx: s.onchainMintTx || null,
|
|
770
|
+
onchainPublishTx: s.onchainPublishTx || null
|
|
771
|
+
};
|
|
772
|
+
});
|
|
773
|
+
publishedBySlug = {};
|
|
774
|
+
for (var pi = 0; pi < importedAll.length; pi++) {
|
|
775
|
+
var si = importedAll[pi];
|
|
776
|
+
if (si && si.slug && si.onchainTokenId) {
|
|
777
|
+
publishedBySlug[si.slug] = {
|
|
778
|
+
skillId: si.onchainTokenId,
|
|
779
|
+
txs: { mint: si.onchainMintTx || null, publish: si.onchainPublishTx || null }
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
applyImportedFilters();
|
|
784
|
+
if (importedBadge) importedBadge.textContent = importedAll.length ? (importedAll.length + ' SKILLS') : 'NONE';
|
|
785
|
+
try { openFromHash(); } catch (e) {}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function loadImportedFromStaticFallback() {
|
|
789
|
+
return fetch('/data/skills-search.json', { headers: { 'accept': 'application/json' } })
|
|
790
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
791
|
+
.then(function (j) {
|
|
792
|
+
var results = (j && Array.isArray(j.results)) ? j.results : [];
|
|
793
|
+
hydrateImportedFromResults(results);
|
|
794
|
+
if (importedBadge && results.length) importedBadge.textContent = results.length + ' SKILLS (FALLBACK)';
|
|
795
|
+
})
|
|
796
|
+
.catch(function () {
|
|
797
|
+
if (importedBadge) importedBadge.textContent = 'UNAVAILABLE';
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
|
|
750
801
|
fetch(apiBase + '/api/skills/search?limit=5000', { headers: { 'accept': 'application/json' } })
|
|
751
802
|
.then(function (r) { return r.ok ? r.json() : null; })
|
|
752
803
|
.then(function (j) {
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
var results = j.results || [];
|
|
758
|
-
importedAll = results.map(function (s, idx) {
|
|
759
|
-
return {
|
|
760
|
-
name: s.name || s.slug || 'Skill',
|
|
761
|
-
slug: s.slug || '',
|
|
762
|
-
description: s.description || s.desc || '',
|
|
763
|
-
desc: s.description || s.desc || '',
|
|
764
|
-
riskTier: s.riskTier || 2,
|
|
765
|
-
source: s.source || 'imported',
|
|
766
|
-
sourceUrl: s.sourceUrl || '',
|
|
767
|
-
fileName: s.fileName || '',
|
|
768
|
-
version: s.version || '1',
|
|
769
|
-
importOk: true,
|
|
770
|
-
vettedOk: Boolean(s.vettedOk !== false && s.vetted !== false),
|
|
771
|
-
addedAt: s.addedAt || s.importedAt || s.createdAt || '',
|
|
772
|
-
importedAt: s.importedAt || s.addedAt || '',
|
|
773
|
-
createdAt: s.createdAt || '',
|
|
774
|
-
_indexOrder: idx,
|
|
775
|
-
onchainTokenId: s.onchainTokenId || null,
|
|
776
|
-
onchainMintTx: s.onchainMintTx || null,
|
|
777
|
-
onchainPublishTx: s.onchainPublishTx || null
|
|
778
|
-
};
|
|
779
|
-
});
|
|
780
|
-
publishedBySlug = {};
|
|
781
|
-
for (var pi = 0; pi < importedAll.length; pi++) {
|
|
782
|
-
var si = importedAll[pi];
|
|
783
|
-
if (si && si.slug && si.onchainTokenId) {
|
|
784
|
-
publishedBySlug[si.slug] = {
|
|
785
|
-
skillId: si.onchainTokenId,
|
|
786
|
-
txs: { mint: si.onchainMintTx || null, publish: si.onchainPublishTx || null }
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
applyImportedFilters();
|
|
791
|
-
if (importedBadge) importedBadge.textContent = importedAll.length ? (importedAll.length + ' SKILLS') : 'NONE';
|
|
792
|
-
try { openFromHash(); } catch (e) {}
|
|
804
|
+
var results = (j && j.ok && Array.isArray(j.results)) ? j.results : [];
|
|
805
|
+
// If API is unavailable or temporarily empty, use static snapshot so the page is never blank.
|
|
806
|
+
if (!results.length) return loadImportedFromStaticFallback();
|
|
807
|
+
hydrateImportedFromResults(results);
|
|
793
808
|
})
|
|
794
809
|
.catch(function () {
|
|
795
|
-
|
|
810
|
+
return loadImportedFromStaticFallback();
|
|
796
811
|
});
|
|
797
812
|
|
|
798
813
|
if (importedSearch) {
|
package/ui/shared/sidebar-nav.js
CHANGED
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
var active = !l.external && isActive(l.href, cur);
|
|
54
54
|
var attrs = active ? ' aria-current="page"' : "";
|
|
55
55
|
if (l.external) attrs += ' target="_blank" rel="noopener"';
|
|
56
|
-
var badge = l.external ? "EXT" :
|
|
56
|
+
var badge = l.external ? "EXT" : "";
|
|
57
57
|
navHtml +=
|
|
58
58
|
'<a class="sb-link" href="' + String(l.href) + '" data-tip="' + String(l.label) + '"' + attrs + ">" +
|
|
59
59
|
iconSvg(l.icon) +
|
package/ui/skills.html
CHANGED
|
@@ -776,7 +776,7 @@
|
|
|
776
776
|
<a class="pill" href="#" data-copy='npx ape-claw skill install'>Copy command</a>
|
|
777
777
|
<a class="pill" href="/docs?doc=CLI_GUIDE.md" data-keep-query="1">Install docs</a>
|
|
778
778
|
</div>
|
|
779
|
-
<div class="note" style="margin-top:8px"
|
|
779
|
+
<div class="note" style="margin-top:8px"><strong style="color:var(--lime)">Requires <a href="https://openclaw.ai" target="_blank" rel="noopener" style="color:var(--lime)">OpenClaw</a></strong> — install OpenClaw first. Add <code>--starter-pack</code> for 61 curated skills. Skills are installed to <code>~/.openclaw/skills/</code>. Works on macOS, Linux, and Windows (PowerShell).</div>
|
|
780
780
|
</div>
|
|
781
781
|
<div class="card ac-observe">
|
|
782
782
|
<div class="cardhead">
|
|
@@ -798,7 +798,7 @@
|
|
|
798
798
|
<p>
|
|
799
799
|
Install any skill by slug from the command line, or browse below and click <strong>Install</strong> to add to your agent.
|
|
800
800
|
</p>
|
|
801
|
-
<div class="code"><span class="k">npx</span>
|
|
801
|
+
<div class="code"><span class="k">npx</span> ape-claw skill install lincoln-ai</div>
|
|
802
802
|
<div class="tools" aria-label="Install instructions">
|
|
803
803
|
<a class="pill" href="#" data-copy='npx ape-claw skill install lincoln-ai'>Copy command</a>
|
|
804
804
|
<a class="pill" href="/docs?doc=SKILLCARDS_AND_IMPORTER.md" data-keep-query="1">Skill docs</a>
|
|
@@ -1453,22 +1453,29 @@ window.addEventListener('unhandledrejection', (e) => { console.error('[ApeClaw]
|
|
|
1453
1453
|
}, 2500);
|
|
1454
1454
|
}
|
|
1455
1455
|
|
|
1456
|
+
var FALLBACK_STATS = { total: 10024, vetted: 9994, onchain: 10004, user: 0 };
|
|
1456
1457
|
// Load global stats from API
|
|
1457
1458
|
(function loadGlobalStats() {
|
|
1458
1459
|
fetch(apiBase + '/api/skills/stats', { headers: { 'accept': 'application/json' } })
|
|
1459
1460
|
.then(function (r) { return r.ok ? r.json() : null; })
|
|
1460
1461
|
.then(function (d) {
|
|
1461
|
-
if (!d || !d.ok)
|
|
1462
|
+
if (!d || !d.ok || !d.total) {
|
|
1463
|
+
setStatCountUp(statTotal, FALLBACK_STATS.total);
|
|
1464
|
+
setStatCountUp(statVetted, FALLBACK_STATS.vetted);
|
|
1465
|
+
setStatCountUp(statOnchain, FALLBACK_STATS.onchain);
|
|
1466
|
+
setStatCountUp(statContributed, FALLBACK_STATS.user);
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1462
1469
|
setStatCountUp(statTotal, d.total || 0);
|
|
1463
1470
|
setStatCountUp(statVetted, d.vetted || 0);
|
|
1464
1471
|
setStatCountUp(statOnchain, d.onchain || 0);
|
|
1465
1472
|
setStatCountUp(statContributed, d.user || 0);
|
|
1466
1473
|
})
|
|
1467
1474
|
.catch(function () {
|
|
1468
|
-
setStatCountUp(statTotal,
|
|
1469
|
-
setStatCountUp(statVetted,
|
|
1470
|
-
setStatCountUp(statOnchain,
|
|
1471
|
-
setStatCountUp(statContributed,
|
|
1475
|
+
setStatCountUp(statTotal, FALLBACK_STATS.total);
|
|
1476
|
+
setStatCountUp(statVetted, FALLBACK_STATS.vetted);
|
|
1477
|
+
setStatCountUp(statOnchain, FALLBACK_STATS.onchain);
|
|
1478
|
+
setStatCountUp(statContributed, FALLBACK_STATS.user);
|
|
1472
1479
|
});
|
|
1473
1480
|
})();
|
|
1474
1481
|
|
|
@@ -1655,7 +1662,7 @@ window.addEventListener('unhandledrejection', (e) => { console.error('[ApeClaw]
|
|
|
1655
1662
|
var installCmd = 'npx ape-claw skill install ' + rawSlug;
|
|
1656
1663
|
var html = '';
|
|
1657
1664
|
html += '<div style="margin-bottom:16px"><strong style="color:#cfff04;font-size:14px">' + escapeHtml(it.name || it.slug) + '</strong></div>';
|
|
1658
|
-
html += '<div class="note" style="margin-bottom:14px;color:var(--muted);font-size:12px">Run the command below in your terminal to install this skill to
|
|
1665
|
+
html += '<div class="note" style="margin-bottom:14px;color:var(--muted);font-size:12px">Run the command below in your terminal to install this skill to your <a href="https://openclaw.ai" target="_blank" rel="noopener" style="color:var(--lime)">OpenClaw</a> workspace. <strong style="color:var(--lime)">Requires OpenClaw.</strong></div>';
|
|
1659
1666
|
html += '<div style="margin-bottom:6px;color:var(--lime);font-size:11px;font-weight:700;letter-spacing:.5px">INSTALL</div>';
|
|
1660
1667
|
html += '<div style="position:relative;background:rgba(0,0,0,.5);border:1px solid rgba(207,255,4,.15);border-radius:6px;padding:14px 16px;margin-bottom:14px">';
|
|
1661
1668
|
html += '<code style="font-size:11px;color:var(--cyan);word-break:break-all;line-height:1.6;display:block">' + escapeHtml(installCmd) + '</code>';
|
|
@@ -1982,53 +1989,73 @@ window.addEventListener('unhandledrejection', (e) => { console.error('[ApeClaw]
|
|
|
1982
1989
|
window.addEventListener('hashchange', function () {
|
|
1983
1990
|
try { openFromHash(); } catch (e) {}
|
|
1984
1991
|
});
|
|
1985
|
-
|
|
1986
|
-
.
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
1992
|
+
function hydrateFromResults(results) {
|
|
1993
|
+
importedAll = (Array.isArray(results) ? results : []).map(function (s, idx) {
|
|
1994
|
+
return {
|
|
1995
|
+
name: s.name || s.slug || 'Skill',
|
|
1996
|
+
slug: s.slug || '',
|
|
1997
|
+
description: s.description || s.desc || '',
|
|
1998
|
+
desc: s.description || s.desc || '',
|
|
1999
|
+
riskTier: s.riskTier || 2,
|
|
2000
|
+
source: s.source || 'imported',
|
|
2001
|
+
sourceUrl: s.sourceUrl || '',
|
|
2002
|
+
fileName: s.fileName || '',
|
|
2003
|
+
version: s.version || '1',
|
|
2004
|
+
importOk: true,
|
|
2005
|
+
vettedOk: Boolean(s.vettedOk !== false && s.vetted !== false),
|
|
2006
|
+
addedAt: s.addedAt || s.importedAt || s.createdAt || '',
|
|
2007
|
+
importedAt: s.importedAt || s.addedAt || '',
|
|
2008
|
+
createdAt: s.createdAt || '',
|
|
2009
|
+
_indexOrder: idx,
|
|
2010
|
+
onchainTokenId: s.onchainTokenId || null,
|
|
2011
|
+
onchainMintTx: s.onchainMintTx || null,
|
|
2012
|
+
onchainPublishTx: s.onchainPublishTx || null
|
|
2013
|
+
};
|
|
2014
|
+
});
|
|
2015
|
+
publishedBySlug = {};
|
|
2016
|
+
for (var pi = 0; pi < importedAll.length; pi++) {
|
|
2017
|
+
var si = importedAll[pi];
|
|
2018
|
+
if (si && si.slug && si.onchainTokenId) {
|
|
2019
|
+
publishedBySlug[si.slug] = {
|
|
2020
|
+
skillId: si.onchainTokenId,
|
|
2021
|
+
txs: { mint: si.onchainMintTx || null, publish: si.onchainPublishTx || null }
|
|
2013
2022
|
};
|
|
2014
|
-
});
|
|
2015
|
-
publishedBySlug = {};
|
|
2016
|
-
for (var pi = 0; pi < importedAll.length; pi++) {
|
|
2017
|
-
var si = importedAll[pi];
|
|
2018
|
-
if (si && si.slug && si.onchainTokenId) {
|
|
2019
|
-
publishedBySlug[si.slug] = {
|
|
2020
|
-
skillId: si.onchainTokenId,
|
|
2021
|
-
txs: { mint: si.onchainMintTx || null, publish: si.onchainPublishTx || null }
|
|
2022
|
-
};
|
|
2023
|
-
}
|
|
2024
2023
|
}
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
})
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2024
|
+
}
|
|
2025
|
+
applyImportedFilters();
|
|
2026
|
+
if (importedBadge) importedBadge.textContent = importedAll.length ? (importedAll.length + ' SKILLS') : 'NONE';
|
|
2027
|
+
try { openFromHash(); } catch (e) {}
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
function loadFromStaticFallback() {
|
|
2031
|
+
return fetch('/data/skills-search.json', { headers: { 'accept': 'application/json' } })
|
|
2032
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
2033
|
+
.then(function (j) {
|
|
2034
|
+
var results = (j && Array.isArray(j.results)) ? j.results : [];
|
|
2035
|
+
if (!results.length) {
|
|
2036
|
+
if (importedBadge) importedBadge.textContent = 'UNAVAILABLE';
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
hydrateFromResults(results);
|
|
2040
|
+
if (importedBadge) importedBadge.textContent = importedAll.length + ' SKILLS';
|
|
2041
|
+
setStatCountUp(statTotal, importedAll.length);
|
|
2042
|
+
})
|
|
2043
|
+
.catch(function () {
|
|
2044
|
+
if (importedBadge) importedBadge.textContent = 'UNAVAILABLE';
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
loadFromStaticFallback().then(function () {
|
|
2049
|
+
setTimeout(function () {
|
|
2050
|
+
fetch(apiBase + '/api/skills/search?limit=15000&slim=1', { headers: { 'accept': 'application/json' } })
|
|
2051
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
2052
|
+
.then(function (j) {
|
|
2053
|
+
var results = (j && j.ok && Array.isArray(j.results)) ? j.results : [];
|
|
2054
|
+
if (results.length > importedAll.length) hydrateFromResults(results);
|
|
2055
|
+
})
|
|
2056
|
+
.catch(function () {});
|
|
2057
|
+
}, 2000);
|
|
2058
|
+
});
|
|
2032
2059
|
|
|
2033
2060
|
if (importedSearch) {
|
|
2034
2061
|
importedSearch.addEventListener('input', applyImportedFilters);
|