@goplausible/openclaw-algorand-plugin 1.8.7 → 1.8.8
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/index.ts +106 -0
- package/memory/MEMORY.md +38 -0
- package/memory/algorand-plugin.md +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -184,6 +184,96 @@ function writeMemoryFile(workspacePath: string): { success: boolean; message: st
|
|
|
184
184
|
return { success: true, message: `Plugin memory written to ${targetFile}` };
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
function ensureWorkspaceMemoryIndex(workspacePath: string): { success: boolean; message: string } {
|
|
188
|
+
const templateFile = join(__dirname, "memory", "MEMORY.md");
|
|
189
|
+
|
|
190
|
+
if (!existsSync(templateFile)) {
|
|
191
|
+
return { success: false, message: "Template MEMORY.md not found in plugin" };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const templateContent = readFileSync(templateFile, "utf-8");
|
|
195
|
+
|
|
196
|
+
// Extract NEVER FORGET section from template (everything between ## NEVER FORGET and next ## or EOF)
|
|
197
|
+
const neverForgetMatch = templateContent.match(/## NEVER FORGET\n([\s\S]*?)(?=\n## (?!NEVER)|$)/);
|
|
198
|
+
if (!neverForgetMatch) {
|
|
199
|
+
return { success: false, message: "No NEVER FORGET section found in template MEMORY.md" };
|
|
200
|
+
}
|
|
201
|
+
const templateNeverForget = neverForgetMatch[1].trimEnd();
|
|
202
|
+
|
|
203
|
+
// Check for MEMORY.md or memory.md at workspace root
|
|
204
|
+
const memoryMdPath = join(workspacePath, "MEMORY.md");
|
|
205
|
+
const memoryMdLower = join(workspacePath, "memory.md");
|
|
206
|
+
|
|
207
|
+
const existingPath = existsSync(memoryMdPath) ? memoryMdPath
|
|
208
|
+
: existsSync(memoryMdLower) ? memoryMdLower
|
|
209
|
+
: null;
|
|
210
|
+
|
|
211
|
+
if (!existingPath) {
|
|
212
|
+
// No MEMORY.md exists — create from template
|
|
213
|
+
writeFileSync(memoryMdPath, templateContent);
|
|
214
|
+
return { success: true, message: `Created ${memoryMdPath} with NEVER FORGET section` };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// MEMORY.md exists — check for NEVER FORGET header
|
|
218
|
+
let existing = readFileSync(existingPath, "utf-8");
|
|
219
|
+
|
|
220
|
+
if (!/## NEVER FORGET/i.test(existing)) {
|
|
221
|
+
// No NEVER FORGET section — insert after first # heading
|
|
222
|
+
const firstHeadingEnd = existing.match(/^# .+\n/m);
|
|
223
|
+
if (firstHeadingEnd) {
|
|
224
|
+
const insertPos = (firstHeadingEnd.index ?? 0) + firstHeadingEnd[0].length;
|
|
225
|
+
existing = existing.slice(0, insertPos) + "\n## NEVER FORGET\n" + templateNeverForget + "\n\n" + existing.slice(insertPos);
|
|
226
|
+
} else {
|
|
227
|
+
// No heading at all — prepend
|
|
228
|
+
existing = "# OpenClaw Agent Long-Term Memory\n\n## NEVER FORGET\n" + templateNeverForget + "\n\n" + existing;
|
|
229
|
+
}
|
|
230
|
+
writeFileSync(existingPath, existing);
|
|
231
|
+
return { success: true, message: `Added NEVER FORGET section to ${existingPath}` };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// NEVER FORGET exists — merge template items that are missing
|
|
235
|
+
// Extract existing NEVER FORGET section content
|
|
236
|
+
const existingNFMatch = existing.match(/## NEVER FORGET\n([\s\S]*?)(?=\n## (?!#)|$)/);
|
|
237
|
+
const existingNFContent = existingNFMatch ? existingNFMatch[1] : "";
|
|
238
|
+
|
|
239
|
+
// Extract individual items (lines starting with *) from template, grouped by subsection
|
|
240
|
+
const templateLines = templateNeverForget.split("\n");
|
|
241
|
+
const newLines: string[] = [];
|
|
242
|
+
|
|
243
|
+
for (const line of templateLines) {
|
|
244
|
+
// Add subsection headers and bullet items that don't exist in existing content
|
|
245
|
+
if (line.startsWith("### ")) {
|
|
246
|
+
if (!existingNFContent.includes(line)) {
|
|
247
|
+
newLines.push(line);
|
|
248
|
+
}
|
|
249
|
+
} else if (line.startsWith("* ")) {
|
|
250
|
+
// Check if this bullet's key content already exists (first 50 chars as fingerprint)
|
|
251
|
+
const fingerprint = line.slice(2, 52).trim();
|
|
252
|
+
if (!existingNFContent.includes(fingerprint)) {
|
|
253
|
+
newLines.push(line);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (newLines.length === 0) {
|
|
259
|
+
return { success: true, message: `NEVER FORGET section in ${existingPath} is up to date` };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Append new items at the end of existing NEVER FORGET section
|
|
263
|
+
const nfEnd = existing.search(/## NEVER FORGET\n[\s\S]*?(?=\n## (?!#)|$)/);
|
|
264
|
+
if (nfEnd !== -1) {
|
|
265
|
+
const sectionMatch = existing.match(/## NEVER FORGET\n([\s\S]*?)(?=\n## (?!#)|$)/);
|
|
266
|
+
if (sectionMatch) {
|
|
267
|
+
const sectionEnd = (sectionMatch.index ?? 0) + sectionMatch[0].length;
|
|
268
|
+
const insertion = "\n" + newLines.join("\n") + "\n";
|
|
269
|
+
existing = existing.slice(0, sectionEnd) + insertion + existing.slice(sectionEnd);
|
|
270
|
+
writeFileSync(existingPath, existing);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return { success: true, message: `Updated NEVER FORGET section in ${existingPath} (added ${newLines.length} items)` };
|
|
275
|
+
}
|
|
276
|
+
|
|
187
277
|
function checkMcpBinary(): { available: boolean; path?: string } {
|
|
188
278
|
try {
|
|
189
279
|
const path = execSync("which algorand-mcp", { encoding: "utf-8" }).trim();
|
|
@@ -281,6 +371,14 @@ export default function register(api: PluginApi) {
|
|
|
281
371
|
console.error(` ❌ ${memResult.message}`);
|
|
282
372
|
}
|
|
283
373
|
|
|
374
|
+
// Step 1b: Ensure MEMORY.md exists at workspace root with NEVER FORGET section
|
|
375
|
+
const memIndexResult = ensureWorkspaceMemoryIndex(workspacePath);
|
|
376
|
+
if (memIndexResult.success) {
|
|
377
|
+
console.log(` ✅ ${memIndexResult.message}`);
|
|
378
|
+
} else {
|
|
379
|
+
console.error(` ❌ ${memIndexResult.message}`);
|
|
380
|
+
}
|
|
381
|
+
|
|
284
382
|
// Step 2: Stop any existing algorand-mcp processes
|
|
285
383
|
const stopResult = stopExistingMcpProcesses();
|
|
286
384
|
if (stopResult.stopped > 0) {
|
|
@@ -415,6 +513,14 @@ export default function register(api: PluginApi) {
|
|
|
415
513
|
console.log(" This plugin provides:");
|
|
416
514
|
console.log(" • 9 Algorand skills (Algorand development in TS and Python, x402, MCP interaction, alpha arcade interaction, haystack router development and interaction)");
|
|
417
515
|
console.log(" • algorand-mcp server (~100 blockchain tools via mcporter)\n");
|
|
516
|
+
|
|
517
|
+
// Ensure MEMORY.md exists at workspace root
|
|
518
|
+
const workspacePath = getWorkspacePath(api);
|
|
519
|
+
const memIndexResult = ensureWorkspaceMemoryIndex(workspacePath);
|
|
520
|
+
if (memIndexResult.success) {
|
|
521
|
+
console.log(` ✅ ${memIndexResult.message}`);
|
|
522
|
+
}
|
|
523
|
+
|
|
418
524
|
// Keyring persistence warning for headless Linux
|
|
419
525
|
try {
|
|
420
526
|
const scriptPath = join(__dirname, "scripts", "setup-keyring.sh");
|
package/memory/MEMORY.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# OpenClaw Agent Long-Term Memory
|
|
2
|
+
|
|
3
|
+
## NEVER FORGET
|
|
4
|
+
|
|
5
|
+
### Algorand Plugin Memory
|
|
6
|
+
* ALWAYS read `memory/algorand-plugin.md` first for anything Algorand-related — it contains skill routing, MCP tool categories, common workflows, asset tables, and external resources.
|
|
7
|
+
|
|
8
|
+
### Wallet
|
|
9
|
+
* ALWAYS check wallet with `wallet_get_info` before any blockchain operation.
|
|
10
|
+
* `create_account` only generates a keypair and returns it — nothing is stored. Use `wallet_add_account` to create a persistent agent wallet account with a nickname and spending limits. Make sure you notify user about this if they prompt for account creation without mentioning wallet.
|
|
11
|
+
* Default network is mainnet. ALWAYS remind users to specify `testnet` in their prompts if they intend to work on testnet.
|
|
12
|
+
|
|
13
|
+
### Skill Routing — Load the Right Skill
|
|
14
|
+
* `algorand-interaction` — ALWAYS load when using Algorand MCP tools for blockchain queries, transactions, swaps, x402 payments, or wallet operations.
|
|
15
|
+
* `algorand-development` — Load for AlgoKit CLI, project setup, example search, and general development workflows.
|
|
16
|
+
* `algorand-typescript` — Load for TypeScript/PuyaTs smart contract development, testing with Vitest, typed clients, React frontends.
|
|
17
|
+
* `algorand-python` — Load for Python/PuyaPy smart contract development, algopy decorators, Python AlgoKit Utils.
|
|
18
|
+
* `algorand-x402-typescript` — Load for building x402 payment apps in TypeScript (clients, servers, facilitators, paywalls, Next.js).
|
|
19
|
+
* `algorand-x402-python` — Load for building x402 payment apps in Python (clients, servers, facilitators, Bazaar discovery).
|
|
20
|
+
* `algorand-interaction` also covers x402 payment workflows — ALWAYS load it on HTTP 402 responses to follow the atomic group payment pattern.
|
|
21
|
+
* `haystack-router-interaction` — Load for best-price token swaps via MCP tools (DEX aggregation across Tinyman, Pact, Folks).
|
|
22
|
+
* `haystack-router-development` — Load for building swap UIs with `@txnlab/haystack-router` SDK (React, Node.js).
|
|
23
|
+
* `alpha-arcade-interaction` — Load for prediction market trading via MCP tools (browse markets, place orders, manage positions).
|
|
24
|
+
|
|
25
|
+
### QR Codes
|
|
26
|
+
* When calling `generate_algorand_qrcode`, **USE THE --RAW FLAG** with mcporter to get full output including base64 PNG. Always include UTF-8 QR block, PNG image, and URI string in your response. Copy the ENTIRE base64 string exactly — do not reformat or edit.
|
|
27
|
+
|
|
28
|
+
### Documentation
|
|
29
|
+
* Use `get_knowledge_doc` MCP tool for Algorand developer documentation (categories: arcs, sdks, algokit, algokit-utils, tealscript, puya, liquid-auth, python, developers, clis, nodes, details).
|
|
30
|
+
|
|
31
|
+
### Never Do This
|
|
32
|
+
* NEVER attempt any Algorand blockchain interaction without loading and reading the `algorand-interaction` skill first.
|
|
33
|
+
* NEVER use PyTEAL or Beaker — these are legacy. Use Algorand TypeScript (PuyaTs) or Algorand Python (PuyaPy).
|
|
34
|
+
* NEVER use AlgoExplorer — it is obsolete. Use Allo.info for block/account/transaction/asset/application explorer.
|
|
35
|
+
* NEVER attempt x402 payments without loading the `algorand-interaction` skill first — the x402 payment workflow and atomic group pattern are documented there.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
@@ -33,7 +33,7 @@ mcporter list algorand-mcp
|
|
|
33
33
|
|
|
34
34
|
# Call a tool
|
|
35
35
|
mcporter call algorand-mcp.wallet_get_info
|
|
36
|
-
mcporter call algorand-mcp.
|
|
36
|
+
mcporter call algorand-mcp.generate_algorand_qrcode --raw address=XXXXX network=testnet asset=0 amount=1000000
|
|
37
37
|
mcporter call algorand-mcp.search_assets name=USDC network=mainnet
|
|
38
38
|
```
|
|
39
39
|
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "openclaw-algorand-plugin",
|
|
3
3
|
"name": "Algorand Integration",
|
|
4
4
|
"description": "Algorand blockchain integration with MCP and skills — by GoPlausible",
|
|
5
|
-
"version": "1.8.
|
|
5
|
+
"version": "1.8.8",
|
|
6
6
|
"skills": [
|
|
7
7
|
"skills/algorand-development",
|
|
8
8
|
"skills/algorand-typescript",
|