skills-ws 1.3.0 → 1.3.2

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/bin/cli.mjs CHANGED
@@ -260,7 +260,10 @@ async function detectTarget() {
260
260
  for (const c of candidates) {
261
261
  try { await stat(c); return c; } catch {}
262
262
  }
263
- return join(process.cwd(), "skills");
263
+ // Default: create ~/.claude/skills if nothing found
264
+ const defaultTarget = join(process.env.HOME || "~", ".claude", "skills");
265
+ await mkdir(defaultTarget, { recursive: true });
266
+ return defaultTarget;
264
267
  }
265
268
 
266
269
  // ── Main ─────────────────────────────────────────────────────
@@ -336,13 +339,71 @@ async function main() {
336
339
  return;
337
340
  }
338
341
 
339
- // Help
340
- process.stdout.write(` ${B}Usage:${R}\n\n`);
341
- process.stdout.write(` ${CYAN}npx skills-ws list${R} List all skills\n`);
342
- process.stdout.write(` ${CYAN}npx skills-ws install${R} Interactive picker\n`);
343
- process.stdout.write(` ${CYAN}npx skills-ws install <name>${R} Install specific skill(s)\n`);
344
- process.stdout.write(` ${CYAN}npx skills-ws install all${R} Install everything\n`);
345
- process.stdout.write(`\n ${DIM}${skills.length} skills | skills.ws${R}\n\n`);
342
+ // No args = interactive install (same as `install`)
343
+ if (!args[0] || args[0] === "help" || args[0] === "-h" || args[0] === "--help") {
344
+ if (args[0] === "help" || args[0] === "-h" || args[0] === "--help") {
345
+ process.stdout.write(` ${B}Usage:${R}\n\n`);
346
+ process.stdout.write(` ${CYAN}npx skills-ws${R} Interactive picker\n`);
347
+ process.stdout.write(` ${CYAN}npx skills-ws list${R} List all skills\n`);
348
+ process.stdout.write(` ${CYAN}npx skills-ws install <name>${R} Install specific skill(s)\n`);
349
+ process.stdout.write(` ${CYAN}npx skills-ws install all${R} Install everything\n`);
350
+ process.stdout.write(`\n ${DIM}${skills.length} skills | skills.ws${R}\n\n`);
351
+ return;
352
+ }
353
+
354
+ // Interactive picker
355
+ for (let i = 0; i < skills.length; i++) {
356
+ process.stdout.write(
357
+ ` ${GRAY}${String(i + 1).padStart(2)}${R} ${GREEN}${skills[i].name.padEnd(24)}${R}${DIM}${skills[i].desc.slice(0, 50)}${R}\n`
358
+ );
359
+ }
360
+ process.stdout.write(`\n ${YELLOW}Enter numbers or names (comma-separated), or 'all':${R}\n`);
361
+
362
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
363
+ const answer = await new Promise((resolve) => rl.question(` ${CYAN}> ${R}`, resolve));
364
+ rl.close();
365
+
366
+ const names = [];
367
+ if (answer.trim().toLowerCase() === "all") {
368
+ names.push(...skills.map((s) => s.name));
369
+ } else {
370
+ for (const part of answer.split(",").map((s) => s.trim()).filter(Boolean)) {
371
+ const num = parseInt(part);
372
+ if (!isNaN(num) && num >= 1 && num <= skills.length) names.push(skills[num - 1].name);
373
+ else names.push(part);
374
+ }
375
+ }
376
+
377
+ if (names.length === 0) {
378
+ process.stdout.write(` ${DIM}Nothing selected.${R}\n`);
379
+ return;
380
+ }
381
+
382
+ const target = await detectTarget();
383
+ process.stdout.write(`\n ${DIM}${target}${R}\n\n`);
384
+
385
+ let installed = 0;
386
+ for (const name of names) {
387
+ const skill = skills.find((s) => s.name === name);
388
+ if (!skill) {
389
+ process.stdout.write(` ${YELLOW}skip${R} ${name} ${DIM}(not found)${R}\n`);
390
+ continue;
391
+ }
392
+ await copyDir(skill.dir, join(target, name));
393
+ installed++;
394
+ }
395
+
396
+ await playInstallProgress(installed);
397
+ process.stdout.write("\n");
398
+
399
+ for (const name of names) {
400
+ if (skills.find((s) => s.name === name)) {
401
+ process.stdout.write(` ${GREEN}+${R} ${name}\n`);
402
+ }
403
+ }
404
+ process.stdout.write(`\n ${DIM}skills.ws${R}\n\n`);
405
+ return;
406
+ }
346
407
  }
347
408
 
348
409
  process.on("SIGINT", () => { showCursor(); process.exit(0); });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "skills-ws",
3
- "version": "1.3.0",
4
- "description": "69 agent skills for AI coding assistants \u2014 marketing, growth, web3, dev, design & operations. Built for OpenClaw, Claude Code, Cursor, and Codex.",
3
+ "version": "1.3.2",
4
+ "description": "70 agent skills for AI coding assistants \u2014 marketing, growth, web3, dev, design & operations. Built for OpenClaw, Claude Code, Cursor, and Codex.",
5
5
  "bin": {
6
6
  "skills-ws": "./bin/cli.mjs"
7
7
  },
@@ -0,0 +1,677 @@
1
+ # Aleph Cloud VM Self-Replication
2
+
3
+ > Create Aleph Cloud VM instances and deploy autonomous AI agents that can replicate themselves endlessly across the decentralized cloud.
4
+
5
+ ## Overview
6
+
7
+ This skill enables an AI agent to:
8
+ 1. Set up an Aleph Cloud account with credits or $ALEPH tokens
9
+ 2. Create VM instances (compute nodes) on the Aleph network
10
+ 3. Install OpenClaw (or any AI agent framework) on the VM
11
+ 4. Configure the agent with credentials and identity
12
+ 5. Enable the new agent to repeat this process — self-replication
13
+
14
+ ## Prerequisites
15
+
16
+ - `aleph-client` CLI installed (`pip install aleph-client`)
17
+ - A funded Aleph Cloud account (credits on BASE/ETH/AVAX/SOL, or $ALEPH tokens for holding)
18
+ - An SSH keypair for VM access
19
+ - AI provider API key (Anthropic, OpenAI, etc.) to share with spawned agents
20
+
21
+ ### Install aleph-client
22
+
23
+ ```bash
24
+ pip install aleph-client --break-system-packages
25
+ ```
26
+
27
+ > On Ubuntu 24+ you need `--break-system-packages` or use a venv.
28
+
29
+ ### Check Version
30
+
31
+ ```bash
32
+ aleph --version
33
+ ```
34
+
35
+ ## Step 1: Account Setup
36
+
37
+ ### Import or Create a Private Key
38
+
39
+ ```bash
40
+ # Import existing private key
41
+ aleph account create --private-key <YOUR_HEX_PRIVATE_KEY> --chain BASE --active
42
+
43
+ # Or generate a new one
44
+ aleph account create --chain BASE --active
45
+ ```
46
+
47
+ Keys are stored at `~/.aleph-im/private-keys/`. Always `chmod 600` key files.
48
+
49
+ ### Check Balance
50
+
51
+ ```bash
52
+ aleph account balance
53
+ ```
54
+
55
+ You need credits or $ALEPH tokens. Credits are the simplest (pay-as-you-go). Buy credits at https://account.aleph.im/ or hold $ALEPH tokens.
56
+
57
+ ### Pricing Reference
58
+
59
+ ```bash
60
+ aleph pricing instance
61
+ ```
62
+
63
+ | Tier | Compute Units | vCPUs | RAM | Default Disk | Credits/Hour | Credits/Day |
64
+ |------|---------------|-------|-----|-------------|-------------|-------------|
65
+ | 1 | 1 | 1 | 2 GiB | 20 GiB | 14,250 | 342,000 |
66
+ | 2 | 2 | 2 | 4 GiB | 40 GiB | 28,500 | 684,000 |
67
+ | 3 | 4 | 4 | 8 GiB | 80 GiB | 57,000 | 1,368,000 |
68
+ | 4 | 6 | 6 | 12 GiB | 120 GiB | 85,500 | 2,052,000 |
69
+
70
+ > Disk size can be overridden with `--rootfs-size <MiB>`. The tier sets the minimum; you can allocate more (e.g., 40GB on a Tier 1). Extra disk costs ~4,416 credits/GiB/day.
71
+
72
+ ## Step 2: Generate SSH Keypair
73
+
74
+ ```bash
75
+ ssh-keygen -t ed25519 -f ~/.ssh/aleph_agent -N "" -C "agent@aleph"
76
+ chmod 600 ~/.ssh/aleph_agent
77
+ ```
78
+
79
+ ## Step 3: Find a Compute Resource Node (CRN)
80
+
81
+ ### List Active CRNs (Top 10 by Score)
82
+
83
+ ```bash
84
+ aleph node compute --active --json 2>/dev/null | python3 -c "
85
+ import sys, json
86
+ nodes = json.load(sys.stdin)
87
+ scored = []
88
+ for n in nodes:
89
+ score = n.get('score', 0) or 0
90
+ name = n.get('name', 'unknown')
91
+ node_hash = n.get('hash', '')
92
+ url = n.get('address', '')
93
+ if score > 0.5: # Score is 0.0–1.0 (not percentage)
94
+ scored.append((score, name, node_hash, url))
95
+ scored.sort(reverse=True)
96
+ for s, name, h, url in scored[:10]:
97
+ print(f'{s*100:.1f}% | {name} | {h} | {url}')
98
+ "
99
+ ```
100
+
101
+ ### Get Best CRN Hash (for scripts)
102
+
103
+ ```bash
104
+ CRN_HASH=$(aleph node compute --active --json 2>/dev/null | python3 -c "
105
+ import sys, json
106
+ nodes = json.load(sys.stdin)
107
+ best = max(
108
+ (n for n in nodes if (n.get('score', 0) or 0) > 0.5),
109
+ key=lambda n: n.get('score', 0),
110
+ default=None
111
+ )
112
+ print(best['hash'] if best else '')
113
+ ")
114
+ echo "Best CRN: $CRN_HASH"
115
+ ```
116
+
117
+ ## Step 4: Create the VM Instance
118
+
119
+ ### Known Rootfs Hashes
120
+
121
+ | OS | Rootfs Hash |
122
+ |---|---|
123
+ | Ubuntu 24 | `5330dcefe1857bcd97b7b7f24d1420a7d46232d53f27be280c8a7071d88bd84e` |
124
+ | Ubuntu 22 | (default if `--rootfs` omitted) |
125
+
126
+ ### Create Instance (Non-Interactive)
127
+
128
+ The `aleph instance create` command has an interactive TUI for CRN selection. To bypass it completely, use `--crn-hash` + `--skip-volume`:
129
+
130
+ ```bash
131
+ aleph instance create \
132
+ --name "agent-v1" \
133
+ --compute-units 1 \
134
+ --rootfs 5330dcefe1857bcd97b7b7f24d1420a7d46232d53f27be280c8a7071d88bd84e \
135
+ --rootfs-size 40960 \
136
+ --payment-type credit \
137
+ --payment-chain BASE \
138
+ --ssh-pubkey-file ~/.ssh/aleph_agent.pub \
139
+ --crn-hash "$CRN_HASH" \
140
+ --crn-auto-tac \
141
+ --skip-volume \
142
+ --verbose
143
+ ```
144
+
145
+ **Critical flags:**
146
+ - `--crn-hash <HASH>`: Bypasses the interactive CRN picker TUI entirely
147
+ - `--crn-auto-tac`: Auto-accepts CRN Terms & Conditions
148
+ - `--skip-volume`: Skips the "add extra volumes?" interactive prompt
149
+ - `--rootfs`: OS image hash (omit for Ubuntu 22 default)
150
+ - `--rootfs-size`: Disk size in MiB (40960 = 40GB; overrides tier default)
151
+
152
+ ### If Interactive TUI Still Appears
153
+
154
+ Some versions of aleph-client may still show prompts. Use pexpect:
155
+
156
+ ```python
157
+ #!/usr/bin/env python3
158
+ """Automate aleph instance create when CLI flags don't fully bypass TUI."""
159
+ import pexpect
160
+ import sys
161
+ import re
162
+
163
+ CRN_HASH = sys.argv[1] if len(sys.argv) > 1 else ""
164
+ NAME = sys.argv[2] if len(sys.argv) > 2 else f"agent-{int(__import__('time').time())}"
165
+
166
+ cmd = (
167
+ f'aleph instance create '
168
+ f'--name "{NAME}" '
169
+ f'--compute-units 1 '
170
+ f'--rootfs 5330dcefe1857bcd97b7b7f24d1420a7d46232d53f27be280c8a7071d88bd84e '
171
+ f'--rootfs-size 40960 '
172
+ f'--payment-type credit '
173
+ f'--payment-chain BASE '
174
+ f'--ssh-pubkey-file ~/.ssh/aleph_agent.pub '
175
+ f'--crn-auto-tac '
176
+ f'--skip-volume '
177
+ f'--verbose'
178
+ )
179
+ if CRN_HASH:
180
+ cmd += f' --crn-hash {CRN_HASH}'
181
+
182
+ child = pexpect.spawn('/bin/bash', ['-c', cmd], timeout=180, encoding='utf-8')
183
+ child.logfile_read = sys.stdout
184
+
185
+ while True:
186
+ try:
187
+ idx = child.expect([
188
+ r'(?i)rootfs.*type', # Rootfs type prompt
189
+ r'(?i)disk.*size', # Disk size prompt
190
+ r'(?i)add.*volume', # Add volume prompt
191
+ r'(?i)confirm|deploy|proceed', # Confirmation prompt
192
+ r'(?i)select.*crn|choose.*node', # CRN selection
193
+ pexpect.EOF,
194
+ pexpect.TIMEOUT,
195
+ ], timeout=120)
196
+
197
+ if idx == 0:
198
+ child.sendline('ubuntu24')
199
+ elif idx == 1:
200
+ child.sendline('40960')
201
+ elif idx == 2:
202
+ child.sendline('n')
203
+ elif idx == 3:
204
+ child.sendline('y')
205
+ elif idx == 4:
206
+ child.sendline('') # Press Enter for top/default choice
207
+ elif idx == 5:
208
+ break # EOF — done
209
+ elif idx == 6:
210
+ break # Timeout — done or stuck
211
+ except Exception as e:
212
+ print(f"Error: {e}", file=sys.stderr)
213
+ break
214
+
215
+ print("\n" + child.before if child.before else "")
216
+ child.close()
217
+ sys.exit(child.exitstatus or 0)
218
+ ```
219
+
220
+ Install pexpect: `pip install pexpect --break-system-packages`
221
+
222
+ Usage: `python3 create_instance.py <CRN_HASH> <INSTANCE_NAME>`
223
+
224
+ ### Parse Instance Hash After Creation
225
+
226
+ The CLI outputs the instance details. Extract the hash:
227
+
228
+ ```bash
229
+ # List instances and get the newest one
230
+ aleph instance list 2>&1
231
+ ```
232
+
233
+ The output shows a table with instance hash, SSH connection details, and mapped ports. Look for:
234
+ ```
235
+ ssh root@<HOST_IP> -p <PORT> -i <ssh-private-key-file>
236
+ ```
237
+
238
+ ### Programmatic SSH Details
239
+
240
+ ```bash
241
+ # Parse connection info for the most recent instance
242
+ aleph instance list 2>&1 | grep -oP 'ssh root@\S+ -p \d+'
243
+ ```
244
+
245
+ ### Wait and Connect
246
+
247
+ ```bash
248
+ # Wait for VM to boot (30-60 seconds)
249
+ sleep 45
250
+
251
+ # Connect
252
+ ssh -o StrictHostKeyChecking=no root@<HOST_IP> -p <MAPPED_PORT> -i ~/.ssh/aleph_agent
253
+ ```
254
+
255
+ ## Step 5: Install OpenClaw on the New VM
256
+
257
+ SSH into the VM, then:
258
+
259
+ ```bash
260
+ # Install Node.js 22
261
+ curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
262
+ DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs git
263
+
264
+ # Install OpenClaw
265
+ npm install -g openclaw
266
+
267
+ # Create workspace
268
+ mkdir -p /root/openclaw/memory /root/openclaw/skills
269
+
270
+ # Verify
271
+ node --version # Should show v22.x
272
+ openclaw --version
273
+ ```
274
+
275
+ > Use `DEBIAN_FRONTEND=noninteractive` to prevent dpkg prompts in non-TTY sessions.
276
+
277
+ ## Step 6: Configure Agent Auth & Gateway
278
+
279
+ ### Set Up Anthropic API Key
280
+
281
+ ```bash
282
+ mkdir -p /root/.openclaw/agents/main/agent /root/.openclaw/agents/main/sessions
283
+
284
+ cat > /root/.openclaw/agents/main/agent/auth-profiles.json << 'EOF'
285
+ {
286
+ "version": 1,
287
+ "profiles": {
288
+ "anthropic:default": {
289
+ "type": "token",
290
+ "provider": "anthropic",
291
+ "token": "<YOUR_ANTHROPIC_API_KEY>"
292
+ }
293
+ },
294
+ "lastGood": {
295
+ "anthropic": "anthropic:default"
296
+ }
297
+ }
298
+ EOF
299
+ chmod 600 /root/.openclaw/agents/main/agent/auth-profiles.json
300
+ ```
301
+
302
+ > **Critical:** The file MUST be named `auth-profiles.json` (not `auth.json`). The gateway reads `auth-profiles.json` — using the wrong filename silently fails with "No API key found for provider".
303
+
304
+ ### Configure OpenClaw
305
+
306
+ ```bash
307
+ GATEWAY_TOKEN=$(openssl rand -hex 24)
308
+ echo "Gateway token: $GATEWAY_TOKEN" # Save this!
309
+
310
+ cat > /root/.openclaw/openclaw.json << EOF
311
+ {
312
+ "auth": {
313
+ "profiles": {
314
+ "anthropic:default": {
315
+ "provider": "anthropic",
316
+ "mode": "token"
317
+ }
318
+ }
319
+ },
320
+ "agents": {
321
+ "defaults": {
322
+ "model": {
323
+ "primary": "anthropic/claude-sonnet-4-20250514"
324
+ },
325
+ "workspace": "/root/openclaw"
326
+ }
327
+ },
328
+ "gateway": {
329
+ "port": 18789,
330
+ "mode": "local",
331
+ "bind": "loopback",
332
+ "auth": {
333
+ "mode": "token",
334
+ "token": "$GATEWAY_TOKEN"
335
+ }
336
+ }
337
+ }
338
+ EOF
339
+
340
+ # Harden permissions
341
+ chmod 700 /root/.openclaw
342
+ chmod 600 /root/.openclaw/openclaw.json
343
+ ```
344
+
345
+ ### Start Gateway
346
+
347
+ ```bash
348
+ openclaw gateway install --force
349
+ openclaw gateway start
350
+
351
+ # Wait for startup
352
+ sleep 3
353
+
354
+ # Verify — MUST pass the token
355
+ openclaw gateway status --token "$GATEWAY_TOKEN"
356
+ # Should show: Runtime: running ... RPC probe: ok
357
+ ```
358
+
359
+ > If `RPC probe: failed` after `gateway start`, run `openclaw gateway install --force` then `openclaw gateway restart`. The systemd service file must have the token synced.
360
+
361
+ ### Test Agent
362
+
363
+ ```bash
364
+ OPENCLAW_GATEWAY_TOKEN="$GATEWAY_TOKEN" openclaw agent --agent main -m "Hello! Who are you? Reply in one sentence."
365
+ ```
366
+
367
+ If the gateway route fails, it falls back to embedded mode (still works, just slower on first call).
368
+
369
+ ## Step 7: Copy Identity & Skills
370
+
371
+ From the parent machine, copy workspace files to the new VM:
372
+
373
+ ```bash
374
+ HOST="<HOST_IP>"
375
+ PORT="<MAPPED_PORT>"
376
+ KEY="$HOME/.ssh/aleph_agent"
377
+ SCP="scp -i $KEY -P $PORT -o StrictHostKeyChecking=no"
378
+
379
+ # Core identity files
380
+ for f in SOUL.md AGENTS.md IDENTITY.md USER.md TOOLS.md HEARTBEAT.md; do
381
+ [ -f "/root/openclaw/$f" ] && $SCP /root/openclaw/$f root@$HOST:/root/openclaw/$f
382
+ done
383
+
384
+ # Memory (optional — fresh start or shared context)
385
+ [ -f "/root/openclaw/MEMORY.md" ] && $SCP /root/openclaw/MEMORY.md root@$HOST:/root/openclaw/MEMORY.md
386
+
387
+ # Skills directory (recursive)
388
+ scp -r -i $KEY -P $PORT -o StrictHostKeyChecking=no /root/openclaw/skills/ root@$HOST:/root/openclaw/skills/
389
+ ```
390
+
391
+ ## Step 8: Enable Self-Replication
392
+
393
+ For the new agent to replicate itself, it needs:
394
+
395
+ 1. **aleph-client CLI** on its VM
396
+ 2. **Aleph private key** (or delegated key) to create instances
397
+ 3. **This skill** in its skills directory
398
+ 4. **Sufficient credits/tokens** in the account
399
+
400
+ ### Install aleph-client on the Child VM
401
+
402
+ ```bash
403
+ ssh -i $KEY -p $PORT root@$HOST << 'REMOTE'
404
+ apt-get install -y python3-pip
405
+ pip install aleph-client --break-system-packages
406
+
407
+ # Verify
408
+ aleph --version
409
+ REMOTE
410
+ ```
411
+
412
+ ### Transfer Aleph Private Key
413
+
414
+ ```bash
415
+ # SENSITIVE — never log this key
416
+ $SCP /root/.aleph-im/private-keys/your-key.key root@$HOST:/root/.aleph-im/private-keys/your-key.key
417
+ ssh -i $KEY -p $PORT root@$HOST "chmod 700 /root/.aleph-im/private-keys && chmod 600 /root/.aleph-im/private-keys/*.key"
418
+
419
+ # Activate on the child
420
+ ssh -i $KEY -p $PORT root@$HOST "aleph account create --private-key \$(cat /root/.aleph-im/private-keys/your-key.key) --chain BASE --active"
421
+ ```
422
+
423
+ ### Verify Self-Replication Capability
424
+
425
+ ```bash
426
+ ssh -i $KEY -p $PORT root@$HOST << 'REMOTE'
427
+ echo "=== Checking replication readiness ==="
428
+ echo -n "aleph-client: "; aleph --version
429
+ echo -n "Balance: "; aleph account balance 2>&1 | head -3
430
+ echo -n "SSH key: "; ls ~/.ssh/aleph_agent.pub 2>/dev/null && echo "OK" || echo "MISSING — generate with: ssh-keygen -t ed25519 -f ~/.ssh/aleph_agent -N ''"
431
+ echo -n "This skill: "; ls ~/openclaw/skills/aleph-vm-replication/SKILL.md 2>/dev/null && echo "OK" || echo "MISSING"
432
+ echo -n "OpenClaw: "; openclaw --version
433
+ echo "=== Ready to replicate ==="
434
+ REMOTE
435
+ ```
436
+
437
+ Now the child agent can read this SKILL.md and create its own children — recursively.
438
+
439
+ ## Step 9: Manage Instances
440
+
441
+ ### List All Instances
442
+
443
+ ```bash
444
+ aleph instance list
445
+ ```
446
+
447
+ ### Delete an Instance
448
+
449
+ ```bash
450
+ aleph instance delete <INSTANCE_HASH>
451
+ ```
452
+
453
+ ### Check Account Balance (Monitor Burn Rate)
454
+
455
+ ```bash
456
+ aleph account balance
457
+ ```
458
+
459
+ ## Replication Script (All-in-One)
460
+
461
+ This script automates the full cycle: find CRN → create VM → wait → install OpenClaw → configure → verify.
462
+
463
+ ```bash
464
+ #!/bin/bash
465
+ # replicate.sh — Create a new Aleph Cloud agent clone
466
+ # Usage: ./replicate.sh [name] [compute-units] [anthropic-key]
467
+ set -euo pipefail
468
+
469
+ AGENT_NAME="${1:-agent-$(date +%s)}"
470
+ COMPUTE_UNITS="${2:-1}"
471
+ ANTHROPIC_KEY="${3:-$ANTHROPIC_API_KEY}"
472
+ SSH_KEY="$HOME/.ssh/aleph_agent"
473
+ ROOTFS="5330dcefe1857bcd97b7b7f24d1420a7d46232d53f27be280c8a7071d88bd84e"
474
+ ROOTFS_SIZE=40960
475
+
476
+ if [ -z "$ANTHROPIC_KEY" ]; then
477
+ echo "ERROR: Set ANTHROPIC_API_KEY env var or pass as 3rd arg"
478
+ exit 1
479
+ fi
480
+
481
+ # Generate SSH key if needed
482
+ if [ ! -f "$SSH_KEY" ]; then
483
+ echo "=== Generating SSH keypair ==="
484
+ ssh-keygen -t ed25519 -f "$SSH_KEY" -N "" -C "$AGENT_NAME@aleph"
485
+ fi
486
+
487
+ # Find best CRN
488
+ echo "=== Finding best CRN ==="
489
+ CRN_HASH=$(aleph node compute --active --json 2>/dev/null | python3 -c "
490
+ import sys, json
491
+ nodes = json.load(sys.stdin)
492
+ best = max(
493
+ (n for n in nodes if (n.get('score', 0) or 0) > 0.5),
494
+ key=lambda n: n.get('score', 0),
495
+ default=None
496
+ )
497
+ print(best['hash'] if best else '')
498
+ ")
499
+
500
+ if [ -z "$CRN_HASH" ]; then
501
+ echo "ERROR: No suitable CRN found"
502
+ exit 1
503
+ fi
504
+ echo "Selected CRN: $CRN_HASH"
505
+
506
+ # Create instance
507
+ echo "=== Creating instance: $AGENT_NAME ==="
508
+ aleph instance create \
509
+ --name "$AGENT_NAME" \
510
+ --compute-units "$COMPUTE_UNITS" \
511
+ --rootfs "$ROOTFS" \
512
+ --rootfs-size "$ROOTFS_SIZE" \
513
+ --payment-type credit \
514
+ --payment-chain BASE \
515
+ --ssh-pubkey-file "${SSH_KEY}.pub" \
516
+ --crn-hash "$CRN_HASH" \
517
+ --crn-auto-tac \
518
+ --skip-volume \
519
+ --verbose
520
+
521
+ # Wait for boot
522
+ echo "=== Waiting 45s for VM to boot ==="
523
+ sleep 45
524
+
525
+ # Get connection details
526
+ echo "=== Connection details ==="
527
+ aleph instance list 2>&1 | tail -30
528
+
529
+ # Parse SSH connection (user must extract HOST and PORT from output above)
530
+ echo ""
531
+ echo "=== $AGENT_NAME created ==="
532
+ echo ""
533
+ echo "Next steps:"
534
+ echo " 1. Find HOST and PORT from the instance list above"
535
+ echo " 2. SSH in: ssh root@<HOST> -p <PORT> -i $SSH_KEY"
536
+ echo " 3. Run the setup script:"
537
+ echo " ssh root@<HOST> -p <PORT> -i $SSH_KEY 'bash -s' < setup-agent.sh"
538
+ ```
539
+
540
+ ### Post-Creation Setup Script
541
+
542
+ Save this as `setup-agent.sh` and pipe it via SSH:
543
+
544
+ ```bash
545
+ #!/bin/bash
546
+ # setup-agent.sh — Run on a fresh Aleph VM after creation
547
+ # Usage: ssh root@HOST -p PORT -i KEY 'bash -s' < setup-agent.sh
548
+ set -e
549
+
550
+ ANTHROPIC_KEY="${1:-}"
551
+
552
+ echo "=== Installing Node.js 22 ==="
553
+ curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
554
+ DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs git python3-pip
555
+
556
+ echo "=== Installing OpenClaw ==="
557
+ npm install -g openclaw
558
+
559
+ echo "=== Installing aleph-client ==="
560
+ pip install aleph-client --break-system-packages
561
+
562
+ echo "=== Creating workspace ==="
563
+ mkdir -p /root/openclaw/{memory,skills}
564
+ mkdir -p /root/.openclaw/agents/main/{agent,sessions}
565
+
566
+ echo "=== Configuring gateway ==="
567
+ GATEWAY_TOKEN=$(openssl rand -hex 24)
568
+
569
+ cat > /root/.openclaw/openclaw.json << CONF
570
+ {
571
+ "auth": {
572
+ "profiles": {
573
+ "anthropic:default": {
574
+ "provider": "anthropic",
575
+ "mode": "token"
576
+ }
577
+ }
578
+ },
579
+ "agents": {
580
+ "defaults": {
581
+ "model": {
582
+ "primary": "anthropic/claude-sonnet-4-20250514"
583
+ },
584
+ "workspace": "/root/openclaw"
585
+ }
586
+ },
587
+ "gateway": {
588
+ "port": 18789,
589
+ "mode": "local",
590
+ "bind": "loopback",
591
+ "auth": {
592
+ "mode": "token",
593
+ "token": "$GATEWAY_TOKEN"
594
+ }
595
+ }
596
+ }
597
+ CONF
598
+
599
+ chmod 700 /root/.openclaw
600
+ chmod 600 /root/.openclaw/openclaw.json
601
+
602
+ echo "=== Starting gateway ==="
603
+ openclaw gateway install --force
604
+ openclaw gateway start
605
+ sleep 3
606
+
607
+ echo ""
608
+ echo "=== SETUP COMPLETE ==="
609
+ echo "Gateway token: $GATEWAY_TOKEN"
610
+ echo "OpenClaw: $(openclaw --version)"
611
+ echo "Node: $(node --version)"
612
+ echo ""
613
+ echo "Remaining: copy auth-profiles.json, identity files, and aleph key"
614
+ ```
615
+
616
+ ## Security Considerations
617
+
618
+ - **Private keys**: Always `chmod 600`. Never commit to git. Never log or echo.
619
+ - **API keys**: Store in `auth-profiles.json` with `chmod 600`, not in env vars or config files.
620
+ - **Gateway tokens**: Generate unique per VM (`openssl rand -hex 24`). Sync with `openclaw gateway install --force`.
621
+ - **SSH**: Use ed25519 keys only. Disable password auth on production VMs.
622
+ - **Replication limits**: Monitor credit balance. Set alerts. Unbounded replication drains funds fast.
623
+ - **Network isolation**: Gateway binds to loopback by default. Only expose via Tailscale, never raw public IP.
624
+ - **Aleph key sharing**: All child VMs share the same Aleph account. One compromised VM = all VMs at risk. Consider delegated keys for production.
625
+
626
+ ## Cost Planning
627
+
628
+ | Tier | vCPU / RAM | Credits/Day | Credits/Month | Approx $/Month |
629
+ |------|-----------|-------------|---------------|-----------------|
630
+ | 1 | 1 / 2 GiB | 342,000 | ~10.3M | ~$3-5 |
631
+ | 2 | 2 / 4 GiB | 684,000 | ~20.5M | ~$6-10 |
632
+ | 3 | 4 / 8 GiB | 1,368,000 | ~41M | ~$12-20 |
633
+
634
+ Extra disk beyond tier default: ~4,416 credits/GiB/day.
635
+
636
+ Check current pricing: `aleph pricing instance`
637
+
638
+ ## Troubleshooting
639
+
640
+ ### "No API key found for provider" Error
641
+ The gateway reads `auth-profiles.json`, **NOT** `auth.json`. Ensure:
642
+ - File is at `~/.openclaw/agents/main/agent/auth-profiles.json`
643
+ - Format matches the template in Step 6 (with `"version": 1` and `"profiles"` key)
644
+ - Run `openclaw gateway restart` after any auth changes
645
+
646
+ ### RPC Probe Failed
647
+ ```bash
648
+ openclaw gateway install --force
649
+ openclaw gateway restart
650
+ sleep 3
651
+ openclaw gateway status --token "$GATEWAY_TOKEN"
652
+ ```
653
+ The systemd service file caches the token — `install --force` re-syncs it.
654
+
655
+ ### Interactive TUI Blocks Automation
656
+ Use `--crn-hash <HASH>` + `--skip-volume` + `--crn-auto-tac`. If still interactive, use the pexpect script from Step 4.
657
+
658
+ ### VM Not Reachable After Creation
659
+ - Wait 45-60 seconds (boot takes time on decentralized infra)
660
+ - Check `aleph instance list` for the mapped SSH port
661
+ - Verify the CRN is healthy: `aleph node compute --crn-hash <HASH> --json`
662
+ - Try IPv6 if IPv4 mapping isn't working
663
+
664
+ ### Credit Balance Dropping Too Fast
665
+ - `aleph account balance` — check remaining credits
666
+ - `aleph instance list` — count running instances
667
+ - `aleph instance delete <HASH>` — kill unused instances
668
+ - Each running instance burns credits continuously, even idle
669
+
670
+ ### Config Validation Errors ("Unrecognized key")
671
+ Only use config keys that OpenClaw recognizes. Known valid top-level keys:
672
+ `auth`, `agents`, `gateway`, `models`, `tools`, `bindings`, `messages`, `commands`, `hooks`, `channels`, `skills`, `plugins`, `wizard`, `meta`
673
+
674
+ Run `openclaw doctor --fix` to auto-remove invalid keys.
675
+
676
+ ### dpkg Prompts Block SSH Scripts
677
+ Use `DEBIAN_FRONTEND=noninteractive` before apt commands in non-TTY sessions.
package/SECURITY.md DELETED
@@ -1,45 +0,0 @@
1
- # Security Policy
2
-
3
- ## Supported Versions
4
-
5
- | Version | Supported |
6
- |---------|-----------|
7
- | 1.x | ✅ Current |
8
- | < 1.0 | ❌ No longer supported |
9
-
10
- ## Reporting a Vulnerability
11
-
12
- If you discover a security vulnerability in skills-ws, please report it responsibly:
13
-
14
- **Email:** bob@openletz.com
15
- **Subject:** `[SECURITY] skills-ws: <brief description>`
16
-
17
- Please include:
18
- - Description of the vulnerability
19
- - Steps to reproduce
20
- - Potential impact
21
- - Suggested fix (if any)
22
-
23
- We will acknowledge receipt within 48 hours and provide a timeline for a fix.
24
-
25
- **Do NOT open a public GitHub issue for security vulnerabilities.**
26
-
27
- ## Security Model
28
-
29
- - **Zero runtime dependencies** — no supply chain risk from third-party packages
30
- - **No code execution** — skills are markdown files (SKILL.md), not executable code
31
- - **No eval/exec patterns** — the CLI copies files only, never evaluates content
32
- - **Environment-only credentials** — skills that reference API keys use environment variables exclusively
33
- - **VirusTotal scanned** — all skill files are periodically scanned
34
- - **Build provenance** — npm packages are published with Sigstore provenance attestation
35
-
36
- ## Scope
37
-
38
- This policy covers:
39
- - The `skills-ws` npm package
40
- - The CLI tool (`npx skills-ws`)
41
- - Skill content in `skills/` directory
42
-
43
- This policy does NOT cover:
44
- - Third-party tools referenced in skill documentation (e.g., Google Analytics, VirusTotal)
45
- - User-modified skill files after installation