ape-claw 0.1.2 → 0.1.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ape-claw",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "ApeChain bridge and NFT execution CLI with telemetry for OpenClaw agents",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/cli.mjs CHANGED
@@ -211,15 +211,35 @@ function installApeClawSkill(args) {
211
211
  };
212
212
  }
213
213
 
214
+ function yamlSafe(val) {
215
+ const s = String(val);
216
+ if (!s) return '""';
217
+ if (/[:{}\[\]#&*!|>'"%@`,\n]/.test(s) || s.startsWith(" ") || s.endsWith(" ")) {
218
+ return `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
219
+ }
220
+ return s;
221
+ }
222
+
214
223
  function syncSkillToOpenClaw(cardObj, slug, skillsRoot) {
215
224
  const s = String(slug || cardObj?.slug || cardObj?.name || "").toLowerCase().trim()
216
225
  .replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
217
226
  if (!s) return null;
218
- const doc = String(cardObj?.documentation_md || "").trim();
219
- const nameValue = String(cardObj?.name || s).trim();
227
+ const rawDoc = String(cardObj?.documentation_md || "").trim();
228
+ const displayName = String(cardObj?.name || s).trim();
220
229
  const versionValue = String(cardObj?.version || "1.0.0").trim();
221
230
  const descriptionValue = String(cardObj?.description || "").trim();
222
- const content = doc || `---\nname: ${nameValue}\nversion: ${versionValue}\ndescription: ${descriptionValue}\n---\n\n# ${nameValue}\n\n${descriptionValue}\n`;
231
+ const descOneLine = descriptionValue.replace(/\n/g, " ").slice(0, 300);
232
+
233
+ const openclawFrontmatter = `---\nname: ${s}\nversion: ${yamlSafe(versionValue)}\ndescription: ${yamlSafe(descOneLine)}\n---\n`;
234
+
235
+ let content;
236
+ if (rawDoc) {
237
+ // Strip existing frontmatter from documentation_md and prepend OpenClaw-compatible one
238
+ const stripped = rawDoc.replace(/^---[\s\S]*?---\s*/, "").trim();
239
+ content = openclawFrontmatter + "\n" + stripped;
240
+ } else {
241
+ content = openclawFrontmatter + `\n# ${displayName}\n\n${descriptionValue}\n`;
242
+ }
223
243
 
224
244
  const skillDir = path.join(skillsRoot, s);
225
245
  fs.mkdirSync(skillDir, { recursive: true });
@@ -431,6 +431,67 @@ a{color:var(--neon-cyan);text-decoration:none}
431
431
  @keyframes forgeToastIn{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}
432
432
  @keyframes forgeToastOut{from{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-12px)}}
433
433
 
434
+ /* ── Offline Gate ───────────────────────────────────────── */
435
+ .forge-offline-gate{
436
+ position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;
437
+ background:var(--bg);padding:24px;
438
+ }
439
+ .forge-offline-card{
440
+ max-width:560px;width:100%;background:var(--surface);border:1px solid var(--border);
441
+ border-radius:6px;padding:40px 36px;text-align:center;position:relative;
442
+ box-shadow:0 0 80px rgba(207,255,4,.06),var(--panel-shadow);
443
+ }
444
+ .forge-offline-icon{font-size:3.5rem;margin-bottom:12px;filter:drop-shadow(0 0 18px rgba(207,255,4,.35))}
445
+ .forge-offline-title{
446
+ font-family:var(--font-display);font-weight:900;font-size:1.6rem;letter-spacing:.06em;
447
+ color:var(--accent);text-transform:uppercase;margin-bottom:6px;
448
+ }
449
+ .forge-offline-sub{color:var(--dim);font-size:.85rem;margin-bottom:0}
450
+ .forge-offline-divider{
451
+ height:1px;background:linear-gradient(90deg,transparent,var(--border),transparent);
452
+ margin:20px 0;
453
+ }
454
+ .forge-offline-desc{color:var(--text);font-size:.82rem;line-height:1.65;text-align:left;margin-bottom:4px}
455
+ .forge-offline-steps{text-align:left;margin:18px 0}
456
+ .forge-offline-step{
457
+ display:flex;gap:14px;align-items:flex-start;padding:12px 0;
458
+ border-bottom:1px solid rgba(255,255,255,.05);
459
+ }
460
+ .forge-offline-step:last-child{border-bottom:none}
461
+ .forge-offline-step-num{
462
+ flex-shrink:0;width:26px;height:26px;display:flex;align-items:center;justify-content:center;
463
+ border-radius:50%;background:rgba(207,255,4,.12);border:1px solid var(--border);
464
+ color:var(--accent);font-weight:800;font-size:.7rem;font-family:var(--font-mono);
465
+ }
466
+ .forge-offline-step strong{display:block;color:var(--text);font-size:.8rem;margin-bottom:3px}
467
+ .forge-offline-step code{
468
+ display:block;font-family:var(--font-mono);font-size:.72rem;color:var(--neon-cyan);
469
+ background:rgba(0,0,0,.5);border:1px solid rgba(99,215,255,.15);border-radius:3px;
470
+ padding:6px 10px;margin-top:4px;word-break:break-all;
471
+ }
472
+ .forge-offline-hint{display:block;font-size:.68rem;color:var(--dim);margin-top:3px}
473
+ .forge-offline-hint code{display:inline;padding:1px 5px;margin:0;font-size:.68rem}
474
+ .forge-offline-note{font-size:.72rem;color:var(--dim);line-height:1.7;text-align:center}
475
+ .forge-offline-note code{
476
+ font-family:var(--font-mono);font-size:.68rem;color:var(--neon-green);
477
+ background:rgba(0,255,0,.06);padding:2px 6px;border-radius:2px;
478
+ }
479
+ .forge-offline-dim{color:rgba(255,255,255,.3)}
480
+ .forge-offline-links{display:flex;gap:10px;justify-content:center;margin-top:20px}
481
+ .forge-offline-btn{
482
+ display:inline-flex;align-items:center;padding:9px 22px;border-radius:3px;
483
+ font-family:var(--font-display);font-weight:700;font-size:.78rem;letter-spacing:.04em;
484
+ text-transform:uppercase;text-decoration:none;transition:all .2s;
485
+ background:var(--accent);color:#0c0c0c;border:1px solid var(--accent);
486
+ }
487
+ .forge-offline-btn:hover{background:#e2ff40;box-shadow:0 0 16px rgba(207,255,4,.3)}
488
+ .forge-offline-btn-secondary{
489
+ background:transparent;color:var(--text);border-color:var(--border);
490
+ }
491
+ .forge-offline-btn-secondary:hover{
492
+ background:rgba(207,255,4,.08);color:var(--accent);border-color:var(--accent);
493
+ }
494
+
434
495
  /* ── Responsive ─────────────────────────────────────────── */
435
496
  @media(max-width:1199px){
436
497
  .forge-bottom{grid-template-columns:1fr;max-height:none}
@@ -169,6 +169,67 @@
169
169
  </footer>
170
170
  </div>
171
171
 
172
+ <!-- Forge requires a local server to discover installed skills -->
173
+ <div class="forge-offline-gate" id="forgeOfflineGate" style="display:none">
174
+ <div class="forge-offline-card">
175
+ <div class="forge-offline-icon">&#x1F916;</div>
176
+ <h2 class="forge-offline-title">ClawBot Forge</h2>
177
+ <p class="forge-offline-sub">Your 3D agent — shaped by the skills you install</p>
178
+ <div class="forge-offline-divider"></div>
179
+ <p class="forge-offline-desc">
180
+ The Forge renders <strong>your</strong> installed skills as attachments on a 3D robot.
181
+ It discovers skills from your local Cursor &amp; OpenClaw directories, so it must be
182
+ hosted on your machine.
183
+ </p>
184
+ <div class="forge-offline-steps">
185
+ <div class="forge-offline-step">
186
+ <span class="forge-offline-step-num">1</span>
187
+ <div>
188
+ <strong>Install ApeClaw + skills</strong>
189
+ <code>npx ape-claw skill install --starter-pack</code>
190
+ </div>
191
+ </div>
192
+ <div class="forge-offline-step">
193
+ <span class="forge-offline-step-num">2</span>
194
+ <div>
195
+ <strong>Clone the repo &amp; start the server</strong>
196
+ <code>git clone https://github.com/simplefarmer69/ape-claw.git && cd ape-claw && npm install && npm run start:ui</code>
197
+ </div>
198
+ </div>
199
+ <div class="forge-offline-step">
200
+ <span class="forge-offline-step-num">3</span>
201
+ <div>
202
+ <strong>Open the Forge</strong>
203
+ <code>http://localhost:8799/forge</code>
204
+ <span class="forge-offline-hint">Your installed skills appear as 3D attachments on the robot automatically</span>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ <div class="forge-offline-divider"></div>
209
+ <p class="forge-offline-note">
210
+ The Forge reads skills installed via <code>npx ape-claw skill install</code>.<br>
211
+ Skills are synced to <code>~/.cursor/skills/</code> and <code>~/.openclaw/workspace/skills/</code> automatically.
212
+ </p>
213
+ <div class="forge-offline-links">
214
+ <a class="forge-offline-btn" href="/skills">Browse Skills</a>
215
+ <a class="forge-offline-btn forge-offline-btn-secondary" href="https://github.com/simplefarmer69/ape-claw" target="_blank" rel="noopener">GitHub</a>
216
+ </div>
217
+ </div>
218
+ </div>
219
+
220
+ <script>
221
+ (async function detectLocal() {
222
+ try {
223
+ const r = await fetch("/api/health", { signal: AbortSignal.timeout(4000) });
224
+ if (r.ok) return; // local server running — load the forge normally
225
+ } catch {}
226
+ // Not local — show the offline gate and hide the forge app
227
+ document.getElementById("forgeOfflineGate").style.display = "";
228
+ const app = document.querySelector(".forge-app");
229
+ if (app) app.style.display = "none";
230
+ })();
231
+ </script>
232
+
172
233
  <script type="importmap">
173
234
  {
174
235
  "imports": {
@@ -53,11 +53,12 @@
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" : (l.icon === "forge" ? "LOCAL" : "");
56
57
  navHtml +=
57
58
  '<a class="sb-link" href="' + String(l.href) + '" data-tip="' + String(l.label) + '"' + attrs + ">" +
58
59
  iconSvg(l.icon) +
59
60
  '<span class="sb-text">' + String(l.label) + "</span>" +
60
- (l.external ? '<span class="sb-sub">EXT</span>' : '<span class="sb-sub"></span>') +
61
+ '<span class="sb-sub">' + badge + '</span>' +
61
62
  "</a>";
62
63
  }
63
64