@goplausible/openclaw-algorand-plugin 1.9.5 → 2.0.0

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.
@@ -1,428 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -e
3
-
4
- MODE="${1:---detect}"
5
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
6
- PLUGIN_DIR="$(dirname "$SCRIPT_DIR")"
7
- NODE_MODULES="$PLUGIN_DIR/node_modules"
8
- BACKUP_SCRIPT="$SCRIPT_DIR/backup-keyring.js"
9
- WALLET_DB="$HOME/.algorand-mcp/wallet.db"
10
-
11
- # ═══════════════════════════════════════════════════════════
12
- # 1. OS Detection
13
- # ═══════════════════════════════════════════════════════════
14
- if [[ "$(uname -s)" != "Linux" ]]; then
15
- case "$(uname -s)" in
16
- Darwin) BACKEND="macOS Keychain" ;;
17
- *) BACKEND="OS Keychain" ;;
18
- esac
19
- if [ "$MODE" = "--detect" ]; then
20
- echo "PLATFORM=$(uname -s | tr '[:upper:]' '[:lower:]')"
21
- echo "BACKEND=$BACKEND"
22
- echo "PERSISTENT=true"
23
- echo "HEADLESS=false"
24
- else
25
- echo ""
26
- echo " ✅ $BACKEND — persistent by default. No setup needed."
27
- echo ""
28
- fi
29
- exit 0
30
- fi
31
-
32
- # ═══════════════════════════════════════════════════════════
33
- # 2. Linux Environment Detection
34
- # ═══════════════════════════════════════════════════════════
35
-
36
- PLATFORM="linux"
37
- KEYRING_DIR="$HOME/.local/share/keyrings"
38
-
39
- # Display (headless?)
40
- HAS_DISPLAY=false
41
- [[ -n "${DISPLAY:-}" || -n "${WAYLAND_DISPLAY:-}" ]] && HAS_DISPLAY=true
42
-
43
- # D-Bus session
44
- HAS_DBUS=false
45
- [[ -n "${DBUS_SESSION_BUS_ADDRESS:-}" ]] && HAS_DBUS=true
46
-
47
- # GNOME Keyring daemon running
48
- KEYRING_RUNNING=false
49
- pgrep -u "$USER" gnome-keyring-daemon >/dev/null 2>&1 && KEYRING_RUNNING=true
50
-
51
- # Keyring files on disk
52
- KEYRING_FILES=false
53
- [[ -d "$KEYRING_DIR" && "$(ls -A "$KEYRING_DIR" 2>/dev/null)" ]] && KEYRING_FILES=true
54
-
55
- # Wallet DB exists and account count (via Node.js)
56
- WALLET_DB_EXISTS=false
57
- WALLET_DB_COUNT=0
58
- [[ -f "$WALLET_DB" ]] && WALLET_DB_EXISTS=true
59
- if [[ "$WALLET_DB_EXISTS" == "true" && -f "$BACKUP_SCRIPT" ]]; then
60
- WALLET_DB_COUNT=$(NODE_PATH="$NODE_MODULES" node "$BACKUP_SCRIPT" count 2>/dev/null || echo "0")
61
- fi
62
-
63
- # Package manager
64
- PKG=""
65
- if command -v apt >/dev/null 2>&1; then
66
- PKG="apt"
67
- elif command -v dnf >/dev/null 2>&1; then
68
- PKG="dnf"
69
- elif command -v yum >/dev/null 2>&1; then
70
- PKG="yum"
71
- elif command -v pacman >/dev/null 2>&1; then
72
- PKG="pacman"
73
- elif command -v apk >/dev/null 2>&1; then
74
- PKG="apk"
75
- fi
76
-
77
- # Container detection
78
- IN_CONTAINER=false
79
- [[ -f /.dockerenv ]] && IN_CONTAINER=true
80
- grep -q 'docker\|lxc\|containerd' /proc/1/cgroup 2>/dev/null && IN_CONTAINER=true
81
-
82
- # Persistence verdict
83
- HEADLESS=false
84
- if [[ "$HAS_DBUS" == "true" && "$KEYRING_RUNNING" == "true" && "$KEYRING_FILES" == "true" ]]; then
85
- PERSISTENT="true"
86
- BACKEND="GNOME Keyring (persistent)"
87
- elif [[ "$HAS_DISPLAY" == "true" ]]; then
88
- PERSISTENT="true"
89
- BACKEND="Desktop Keyring (persistent)"
90
- else
91
- PERSISTENT="false"
92
- BACKEND="In-memory (volatile)"
93
- HEADLESS=true
94
- fi
95
-
96
- # ═══════════════════════════════════════════════════════════
97
- # 3. Detect-only mode
98
- # ═══════════════════════════════════════════════════════════
99
- if [ "$MODE" = "--detect" ]; then
100
- echo "PLATFORM=$PLATFORM"
101
- echo "BACKEND=$BACKEND"
102
- echo "PERSISTENT=$PERSISTENT"
103
- echo "HEADLESS=$HEADLESS"
104
- echo "HAS_DISPLAY=$HAS_DISPLAY"
105
- echo "HAS_DBUS=$HAS_DBUS"
106
- echo "KEYRING_RUNNING=$KEYRING_RUNNING"
107
- echo "KEYRING_FILES=$KEYRING_FILES"
108
- echo "WALLET_DB_EXISTS=$WALLET_DB_EXISTS"
109
- echo "WALLET_DB_COUNT=$WALLET_DB_COUNT"
110
- echo "PKG_MANAGER=$PKG"
111
- echo "IN_CONTAINER=$IN_CONTAINER"
112
- exit 0
113
- fi
114
-
115
- # ═══════════════════════════════════════════════════════════
116
- # 4. Setup mode
117
- # ═══════════════════════════════════════════════════════════
118
-
119
- echo ""
120
- echo " ── Algorand MCP Keyring Persistence Setup ──"
121
- echo ""
122
-
123
- # ─── 4a. Status display ───
124
- echo " Platform: Linux"
125
- echo " Display: $( [[ "$HAS_DISPLAY" == "true" ]] && echo "✅ Yes" || echo "❌ No (headless)" )"
126
- echo " D-Bus session: $( [[ "$HAS_DBUS" == "true" ]] && echo "✅ Active" || echo "❌ Not found" )"
127
- echo " Keyring daemon: $( [[ "$KEYRING_RUNNING" == "true" ]] && echo "✅ Running" || echo "❌ Not running" )"
128
- echo " Keyring files: $( [[ "$KEYRING_FILES" == "true" ]] && echo "✅ Found in $KEYRING_DIR" || echo "❌ None (in-memory only)" )"
129
- echo " Wallet DB: $( [[ "$WALLET_DB_EXISTS" == "true" ]] && echo "✅ $WALLET_DB ($WALLET_DB_COUNT account(s))" || echo "— Not found (fresh install)" )"
130
- echo " Package manager: ${PKG:-unknown}"
131
- echo " Container: $( [[ "$IN_CONTAINER" == "true" ]] && echo "Yes" || echo "No" )"
132
- echo ""
133
-
134
- # Already persistent with keyring files on disk?
135
- if [[ "$PERSISTENT" == "true" && "$KEYRING_FILES" == "true" ]]; then
136
- echo " ✅ Keyring is persistent and will survive reboots."
137
- echo ""
138
- exit 0
139
- fi
140
-
141
- # ─── 4b. Backup existing wallet mnemonics (UPDATE scenario only) ───
142
- BACKUP_FILE=""
143
- if [[ "$WALLET_DB_COUNT" -gt 0 && "$KEYRING_RUNNING" == "true" && -f "$BACKUP_SCRIPT" ]]; then
144
- echo " ── Step 1: Backup wallet mnemonics from current keyring ──"
145
- echo ""
146
- echo " Found $WALLET_DB_COUNT account(s) in wallet.db."
147
- echo " Reading mnemonics from current keyring before setup..."
148
- echo ""
149
-
150
- BACKUP_FILE=$(mktemp /tmp/algorand-mcp-keyring-backup.XXXXXX)
151
- chmod 600 "$BACKUP_FILE"
152
-
153
- BACKUP_RESULT=$(NODE_PATH="$NODE_MODULES" node "$BACKUP_SCRIPT" backup "$BACKUP_FILE" 2>&1)
154
- echo " $BACKUP_RESULT"
155
-
156
- # Check if backup actually has content
157
- if [[ ! -s "$BACKUP_FILE" ]]; then
158
- rm -f "$BACKUP_FILE"
159
- BACKUP_FILE=""
160
- echo " ℹ️ No mnemonics to backup (keyring may be empty after a reboot)."
161
- fi
162
- echo ""
163
- elif [[ "$WALLET_DB_COUNT" -gt 0 && "$KEYRING_RUNNING" != "true" ]]; then
164
- echo " ── Step 1: Backup ──"
165
- echo ""
166
- echo " ⚠️ Keyring daemon not running — cannot backup existing mnemonics."
167
- echo " If the system was rebooted, in-memory mnemonics are already lost."
168
- echo ""
169
- fi
170
-
171
- # ─── 4c. Install keyring packages ───
172
- echo " ── Step 2: Install keyring packages ──"
173
- echo ""
174
-
175
- if [[ -z "$PKG" ]]; then
176
- echo " Could not detect package manager. Install manually:"
177
- echo " gnome-keyring, libsecret-tools, dbus-user-session (or equivalent)"
178
- echo ""
179
- else
180
- case "$PKG" in
181
- apt)
182
- INSTALL_CMD="sudo DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt update && sudo DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt install -y gnome-keyring libsecret-tools dbus-user-session"
183
- ;;
184
- dnf)
185
- INSTALL_CMD="sudo dnf install -y gnome-keyring libsecret libsecret-tools"
186
- ;;
187
- yum)
188
- INSTALL_CMD="sudo yum install -y gnome-keyring libsecret libsecret-tools"
189
- ;;
190
- pacman)
191
- INSTALL_CMD="sudo pacman -Sy --noconfirm gnome-keyring libsecret"
192
- ;;
193
- apk)
194
- INSTALL_CMD="sudo apk add gnome-keyring libsecret dbus secret-tool"
195
- ;;
196
- esac
197
-
198
- echo " Run:"
199
- echo " $INSTALL_CMD"
200
- echo ""
201
- read -r -p " Install now? [y/N] " REPLY
202
- if [[ "$REPLY" =~ ^[Yy]$ ]]; then
203
- echo ""
204
- eval "$INSTALL_CMD"
205
- echo ""
206
- echo " ✅ Packages installed."
207
- else
208
- echo " Skipped. Install later with the command above."
209
- fi
210
- echo ""
211
- fi
212
-
213
- # ─── 4d. Enable lingering user session ───
214
- echo " ── Step 3: Enable user session lingering ──"
215
- echo ""
216
- echo " This keeps D-Bus and keyring daemon alive after SSH logout."
217
- echo ""
218
-
219
- if loginctl show-user "$USER" 2>/dev/null | grep -q "Linger=yes"; then
220
- echo " ✅ Lingering already enabled for $USER"
221
- else
222
- read -r -p " Enable lingering for $USER? [y/N] " REPLY
223
- if [[ "$REPLY" =~ ^[Yy]$ ]]; then
224
- loginctl enable-linger "$USER"
225
- echo " ✅ Lingering enabled."
226
- else
227
- echo " Skipped."
228
- fi
229
- fi
230
- echo ""
231
-
232
- # ─── 4e. Start D-Bus and keyring daemon ───
233
- echo " ── Step 4: Start D-Bus session and keyring daemon ──"
234
- echo ""
235
-
236
- # Ensure D-Bus is available
237
- if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then
238
- echo " Starting D-Bus session..."
239
- eval $(dbus-launch --sh-syntax)
240
- echo " ✅ D-Bus session started."
241
- else
242
- echo " ✅ D-Bus session already active."
243
- fi
244
-
245
- # Kill stale keyring daemons (leftover --unlock processes, etc.)
246
- STALE_COUNT=$(pgrep -u "$USER" gnome-keyring-daemon 2>/dev/null | wc -l)
247
- if [[ "$STALE_COUNT" -gt 1 ]]; then
248
- echo " Cleaning up $STALE_COUNT stale keyring daemon processes..."
249
- pkill -9 -u "$USER" gnome-keyring-daemon 2>/dev/null || true
250
- sleep 2
251
- fi
252
-
253
- # Start the daemon via --start (D-Bus activated, stays resident)
254
- # Note: on headless, the daemon may be D-Bus activated on demand rather than
255
- # staying resident. This is normal — secret-tool and @napi-rs/keyring will
256
- # trigger D-Bus activation automatically when they query the Secret Service.
257
- echo " Starting gnome-keyring-daemon..."
258
- eval $(gnome-keyring-daemon --start --components=secrets 2>&1 | grep -v '^\*\*') || true
259
- sleep 1
260
-
261
- if pgrep -u "$USER" gnome-keyring-daemon >/dev/null 2>&1; then
262
- echo " ✅ Keyring daemon running."
263
- else
264
- # On D-Bus activated systems, the daemon starts on demand — verify by querying
265
- if command -v secret-tool >/dev/null 2>&1; then
266
- secret-tool search --all service algorand-mcp 2>/dev/null && echo " ✅ Keyring daemon available (D-Bus activated)." || true
267
- fi
268
- echo " ℹ️ Daemon is D-Bus activated (starts on demand when queried)."
269
- fi
270
- echo ""
271
-
272
- # ─── 4f. Create persistent login keyring collection ───
273
- KEYRING_FILE="$KEYRING_DIR/login.keyring"
274
- mkdir -p "$KEYRING_DIR"
275
-
276
- echo " ── Step 5: Create persistent keyring ──"
277
- echo ""
278
-
279
- if [[ -f "$KEYRING_FILE" ]]; then
280
- echo " ✅ Keyring already exists at $KEYRING_FILE"
281
- else
282
- # On headless Linux, gnome-keyring-daemon refuses to create a collection
283
- # via normal prompts (no GUI). We use the D-Bus internal interface
284
- # CreateWithMasterPassword to create the "login" collection with an
285
- # empty master password — making it auto-unlocked and persistent on disk.
286
- echo " Creating login keyring collection via D-Bus..."
287
-
288
- python3 << 'PYEOF'
289
- import sys
290
- try:
291
- from jeepney import new_method_call, DBusAddress
292
- from jeepney.io.blocking import open_dbus_connection
293
-
294
- conn = open_dbus_connection(bus='SESSION')
295
-
296
- # Open a plain-text session with the Secret Service
297
- msg = new_method_call(
298
- DBusAddress('/org/freedesktop/secrets',
299
- bus_name='org.freedesktop.secrets',
300
- interface='org.freedesktop.Secret.Service'),
301
- 'OpenSession',
302
- 'sv',
303
- ('plain', ('s', ''))
304
- )
305
- reply = conn.send_and_get_reply(msg)
306
- session_path = reply.body[1]
307
-
308
- # Create the "login" collection with empty master password
309
- # Uses the internal gnome-keyring interface (bypasses GUI prompt)
310
- props = {
311
- 'org.freedesktop.Secret.Collection.Label': ('s', 'login')
312
- }
313
- master_password = (session_path, b'', b'', 'text/plain')
314
-
315
- msg = new_method_call(
316
- DBusAddress('/org/freedesktop/secrets',
317
- bus_name='org.gnome.keyring',
318
- interface='org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface'),
319
- 'CreateWithMasterPassword',
320
- 'a{sv}(oayays)',
321
- (props, master_password)
322
- )
323
- reply = conn.send_and_get_reply(msg)
324
- print(f" Collection created: {reply.body[0]}")
325
- conn.close()
326
- except Exception as e:
327
- print(f" Error: {e}", file=sys.stderr)
328
- sys.exit(1)
329
- PYEOF
330
-
331
- if [[ $? -eq 0 && -f "$KEYRING_FILE" ]]; then
332
- echo " ✅ Persistent keyring created at $KEYRING_FILE"
333
- else
334
- FOUND_FILE=$(ls "$KEYRING_DIR"/*.keyring 2>/dev/null | head -1 || true)
335
- if [[ -n "$FOUND_FILE" ]]; then
336
- echo " ✅ Persistent keyring created at $FOUND_FILE"
337
- else
338
- echo " ⚠️ Keyring file not created. Ensure python3 and jeepney are installed."
339
- echo " Install with: pip3 install jeepney"
340
- fi
341
- fi
342
- fi
343
- echo ""
344
-
345
- # ─── 4g. Restore backed-up wallet mnemonics (UPDATE scenario only) ───
346
- if [[ -n "$BACKUP_FILE" && -f "$BACKUP_FILE" && -s "$BACKUP_FILE" ]]; then
347
- echo " ── Step 6: Restore wallet mnemonics ──"
348
- echo ""
349
-
350
- RESTORE_RESULT=$(NODE_PATH="$NODE_MODULES" node "$BACKUP_SCRIPT" restore "$BACKUP_FILE" 2>&1)
351
- echo " $RESTORE_RESULT"
352
-
353
- # Securely delete backup
354
- shred -u "$BACKUP_FILE" 2>/dev/null || rm -f "$BACKUP_FILE"
355
- echo " ✅ Temporary backup securely deleted."
356
- echo ""
357
- fi
358
-
359
- # ─── 4h. Docker-specific guidance ───
360
- if [[ "$IN_CONTAINER" == "true" ]]; then
361
- echo " ── Docker / Container Notes ──"
362
- echo ""
363
- echo " Add to your Dockerfile:"
364
- echo " RUN apt-get update && apt-get install -y \\"
365
- echo " gnome-keyring libsecret-tools dbus-user-session \\"
366
- echo " && rm -rf /var/lib/apt/lists/*"
367
- echo ""
368
- echo " Entrypoint wrapper (entrypoint.sh):"
369
- echo ' #!/bin/bash'
370
- echo ' export $(dbus-launch)'
371
- echo ' eval $(gnome-keyring-daemon --start --components=secrets)'
372
- echo ' # Create login collection if not exists (headless — no GUI prompt)'
373
- echo ' if [ ! -f ~/.local/share/keyrings/login.keyring ]; then'
374
- echo ' python3 -c "'
375
- echo ' from jeepney import new_method_call, DBusAddress'
376
- echo ' from jeepney.io.blocking import open_dbus_connection'
377
- echo ' conn = open_dbus_connection(bus=\"SESSION\")'
378
- echo ' r = conn.send_and_get_reply(new_method_call(DBusAddress(\"/org/freedesktop/secrets\",bus_name=\"org.freedesktop.secrets\",interface=\"org.freedesktop.Secret.Service\"),\"OpenSession\",\"sv\",(\"plain\",(\"s\",\"\"))))'
379
- echo ' s = r.body[1]'
380
- echo ' conn.send_and_get_reply(new_method_call(DBusAddress(\"/org/freedesktop/secrets\",bus_name=\"org.gnome.keyring\",interface=\"org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface\"),\"CreateWithMasterPassword\",\"a{sv}(oayays)\",({\"org.freedesktop.Secret.Collection.Label\":(\"s\",\"login\")},(s,b\"\",b\"\",\"text/plain\"))))'
381
- echo ' conn.close()'
382
- echo ' "'
383
- echo ' fi'
384
- echo ' exec "$@"'
385
- echo ""
386
- echo " Persist keyring + wallet data (docker-compose.yml):"
387
- echo " volumes:"
388
- echo " - keyring-data:/home/user/.local/share/keyrings"
389
- echo " - wallet-data:/home/user/.algorand-mcp"
390
- echo ""
391
- fi
392
-
393
- # ─── 4i. Final verification ───
394
- echo " ── Results ──"
395
- echo ""
396
-
397
- # Re-check
398
- VERIFY_RUNNING=false
399
- pgrep -u "$USER" gnome-keyring-daemon >/dev/null 2>&1 && VERIFY_RUNNING=true
400
-
401
- VERIFY_FILES=false
402
- [[ -d "$KEYRING_DIR" && "$(ls -A "$KEYRING_DIR" 2>/dev/null)" ]] && VERIFY_FILES=true
403
-
404
- if [[ "$VERIFY_RUNNING" == "true" ]]; then
405
- echo " ✅ Daemon: Running"
406
- else
407
- echo " ✅ Daemon: D-Bus activated (starts on demand)"
408
- fi
409
-
410
- if [[ "$VERIFY_FILES" == "true" ]]; then
411
- echo " ✅ Persistent: Yes (files at $KEYRING_DIR)"
412
- else
413
- echo " ⚠️ Persistent: No keyring files yet"
414
- fi
415
-
416
- # Verify wallet mnemonics via Node.js
417
- if [[ -f "$BACKUP_SCRIPT" && "$WALLET_DB_EXISTS" == "true" ]]; then
418
- VERIFY_RESULT=$(NODE_PATH="$NODE_MODULES" node "$BACKUP_SCRIPT" verify 2>/dev/null | tail -1)
419
- echo " ✅ Wallets: $VERIFY_RESULT"
420
- fi
421
-
422
- echo ""
423
- echo " Behavior after setup:"
424
- echo " • Keyring stored on disk, auto-unlocked on boot via D-Bus activation"
425
- echo " • loginctl linger keeps D-Bus session alive between SSH sessions"
426
- echo " • Wallet keys persist across reboots — no user interaction needed"
427
- echo " • Keep agent wallet funds minimal — use QR code top-ups as needed"
428
- echo ""