@moltbankhq/openclaw 0.1.0 → 0.1.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/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # @moltbankhq/openclaw
2
+
3
+ OpenClaw plugin for [MoltBank](https://moltbank.bot) — stablecoin treasury controls, approvals, and audit trails for agent fleets.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ openclaw plugins install @moltbankhq/openclaw
9
+ ```
10
+
11
+ Direct `npm install` can be useful to acquire the package, but by itself it does not complete OpenClaw plugin registration or setup.
12
+
13
+ ## What it does
14
+
15
+ This plugin connects your OpenClaw agent to MoltBank's stablecoin treasury infrastructure. On install, it:
16
+
17
+ - Downloads and configures the MoltBank skill (SKILL.md + scripts)
18
+ - Installs and registers [mcporter](https://www.npmjs.com/package/mcporter) for MCP server connectivity
19
+ - Handles OAuth device-code authentication to link your agent to your MoltBank account
20
+ - Configures sandbox (Docker) or host mode automatically based on your OpenClaw setup
21
+ - Injects required environment variables (`MOLTBANK`, `SIGNER`, `ACTIVE_ORG_OVERRIDE`)
22
+
23
+ Once set up, your agent can manage treasury operations, set per-agent spending limits, handle x402 payments, and interact with MoltBank's MCP server — all within OpenClaw.
24
+
25
+ ## Setup
26
+
27
+ After installing, run:
28
+
29
+ ```bash
30
+ openclaw moltbank setup
31
+ ```
32
+
33
+ The plugin will guide you through linking your MoltBank account via browser-based OAuth. Once approved, setup finalizes automatically.
34
+
35
+ ## CLI Commands
36
+
37
+ | Command | Description |
38
+ |---------|-------------|
39
+ | `openclaw moltbank setup` | Run full setup (nonblocking auth) |
40
+ | `openclaw moltbank setup-blocking` | Setup and wait for OAuth approval |
41
+ | `openclaw moltbank auth-status` | Check current auth state |
42
+ | `openclaw moltbank sandbox-setup` | Reconfigure sandbox Docker settings |
43
+ | `openclaw moltbank inject-key` | Re-inject env vars from credentials |
44
+ | `openclaw moltbank register` | Re-register mcporter MCP server |
45
+
46
+ ## Configuration
47
+
48
+ Optional config in your `openclaw.json`:
49
+
50
+ ```json
51
+ {
52
+ "plugins": {
53
+ "entries": {
54
+ "moltbank": {
55
+ "config": {
56
+ "appBaseUrl": "https://app.moltbank.bot",
57
+ "skillName": "MoltBank"
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ ## Environment Variables
66
+
67
+ | Variable | Description |
68
+ |----------|-------------|
69
+ | `APP_BASE_URL` | MoltBank deployment URL (default: `https://app.moltbank.bot`) |
70
+ | `MOLTBANK_SKILL_NAME` | Override skill folder name (default: `MoltBank`) |
71
+ | `MOLTBANK_CREDENTIALS_PATH` | Custom credentials file path |
72
+ | `MOLTBANK_SETUP_AUTH_WAIT_MODE` | `blocking` or `nonblocking` (default: `nonblocking`) |
73
+
74
+ ## Capabilities
75
+
76
+ The MoltBank skill gives your agent access to:
77
+
78
+ - **Treasury management** — balances, transfers, account operations
79
+ - **Per-agent spending limits** — transaction, daily, and weekly caps
80
+ - **Approval workflows** — human-in-the-loop for spend above thresholds
81
+ - **x402 payments** — agent-to-agent payments via the x402 protocol
82
+ - **Audit trails** — full visibility into which agent spent what, when, and why
83
+
84
+ ## Requirements
85
+
86
+ - OpenClaw v2026.3+
87
+ - Node.js 22+
88
+ - Docker (if using sandbox mode)
89
+
90
+ ## Links
91
+
92
+ - [MoltBank](https://moltbank.bot)
93
+ - [Documentation](https://app.moltbank.bot)
94
+ - [GitHub](https://github.com/moltbankhq/openclaw-plugin)
95
+
96
+ ## License
97
+
98
+ ISC
package/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { execSync, spawn } from 'child_process';
2
- import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'fs';
2
+ import { chmodSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'fs';
3
3
  import { join, dirname } from 'path';
4
4
  import { homedir } from 'os';
5
5
  const IS_WIN = process.platform === 'win32';
@@ -276,43 +276,34 @@ function ensureWrapperExecutable(skillDir: string, api: LoggerApi): void {
276
276
  // ─── skill install ───────────────────────────────────────────────────────────
277
277
 
278
278
  const SKILL_FILES = [
279
+ 'README.md',
279
280
  'skill.md',
280
- 'setup.md',
281
- 'onboarding.md',
282
- 'multi-org.md',
283
- 'tools-reference.md',
284
- 'x402-workflow.md',
285
- 'heartbeat.md',
286
- 'rules.md',
287
281
  'skill.json',
288
- 'polymarket-workflow.md',
289
- 'polymarket-operation.md',
290
- 'polymarket-refill.md',
291
- 'openclaw-signer-eoa.md',
292
- 'openclaw-solana-signer.md',
293
- 'pumpfun-workflow.md',
294
- 'config/mcporter.json',
282
+ 'install.sh',
283
+ 'assets/mcporter.json',
284
+ 'references/heartbeat.md',
285
+ 'references/onboarding.md',
286
+ 'references/openclaw-signer-eoa.md',
287
+ 'references/rules.md',
288
+ 'references/setup.md',
289
+ 'references/tools-reference.md',
290
+ 'references/x402-workflow.md',
295
291
  'scripts/openclaw-runtime-config.mjs',
296
292
  'scripts/request-oauth-device-code.mjs',
297
293
  'scripts/init-openclaw-signer.mjs',
298
- 'scripts/init-openclaw-solana-signer.mjs',
299
- 'scripts/bootstrap-openclaw-pumpfun-wallet.mjs',
300
294
  'scripts/inspect-x402-requirements.mjs',
301
- 'scripts/inspect-solana-wallet.mjs',
302
- 'scripts/inspect-polygon-wallet.mjs',
303
- 'scripts/quote-solana-budget.mjs',
304
- 'scripts/polymarket-execute-lifi-tx.mjs',
305
- 'scripts/polymarket-signer-to-safe.mjs',
306
295
  'scripts/poll-oauth-token.mjs',
307
296
  'scripts/export-api-key.mjs',
308
- 'scripts/fetch-openrouter-intent.mjs',
309
297
  'scripts/x402-pay-and-confirm.mjs',
310
- 'scripts/pumpportal-trade-local.mjs',
298
+ 'scripts/setup-sandbox.sh',
311
299
  'scripts/moltbank.sh',
312
- 'scripts/moltbank.ps1',
313
- 'scripts/polymarket-service.mjs'
300
+ 'scripts/moltbank.ps1'
314
301
  ];
315
302
 
303
+ function hasRequiredSkillFiles(skillDir: string): boolean {
304
+ return SKILL_FILES.every((file) => existsSync(join(skillDir, file)));
305
+ }
306
+
316
307
  function ensureSkillInstalled(
317
308
  skillDir: string,
318
309
  appBaseUrl: string,
@@ -321,12 +312,21 @@ function ensureSkillInstalled(
321
312
  mode: 'sandbox' | 'host' = 'sandbox'
322
313
  ) {
323
314
  const successFlag = join(skillDir, '.install_success');
324
- if (existsSync(successFlag)) {
315
+ if (existsSync(successFlag) && hasRequiredSkillFiles(skillDir)) {
325
316
  ensureWrapperExecutable(skillDir, api);
326
317
  api.logger.info('[moltbank] ✓ skill already installed at ' + skillDir);
327
318
  return true;
328
319
  }
329
320
 
321
+ if (existsSync(successFlag)) {
322
+ api.logger.warn('[moltbank] existing skill install is stale or incomplete — reinstalling current bundle');
323
+ try {
324
+ unlinkSync(successFlag);
325
+ } catch {
326
+ // ignore
327
+ }
328
+ }
329
+
330
330
  api.logger.info(`[moltbank] installing skill '${skillName}' to ${skillDir} (mode: ${mode})`);
331
331
  mkdirSync(skillDir, { recursive: true });
332
332
 
@@ -350,66 +350,32 @@ function ensureSkillInstalled(
350
350
  function ensureSkillFilesUppercase(skillDir: string, api: LoggerApi) {
351
351
  const lower = join(skillDir, 'skill.md');
352
352
  const upper = join(skillDir, 'SKILL.md');
353
- if (existsSync(upper)) {
354
- api.logger.info('[moltbank] ✓ SKILL.md already exists');
353
+ if (existsSync(lower)) {
354
+ api.logger.info('[moltbank] ✓ skill entrypoint present (skill.md)');
355
355
  return;
356
356
  }
357
- if (existsSync(lower)) {
358
- renameSync(lower, upper);
359
- api.logger.info('[moltbank] ✓ renamed skill.md → SKILL.md');
357
+ if (existsSync(upper)) {
358
+ api.logger.info('[moltbank] ✓ skill entrypoint present (SKILL.md)');
360
359
  } else {
361
360
  api.logger.warn('[moltbank] ✗ neither skill.md nor SKILL.md found');
362
361
  }
363
362
  }
364
363
 
365
364
  function fixSkillFrontmatter(skillDir: string, skillName: string, api: LoggerApi) {
366
- const skillFile = join(skillDir, 'SKILL.md');
365
+ const skillFile = existsSync(join(skillDir, 'skill.md')) ? join(skillDir, 'skill.md') : join(skillDir, 'SKILL.md');
367
366
  if (!existsSync(skillFile)) {
368
- api.logger.warn('[moltbank] ✗ SKILL.md not found — skipping frontmatter fix');
367
+ api.logger.warn('[moltbank] ✗ skill entrypoint not found — skipping frontmatter validation');
369
368
  return;
370
369
  }
371
370
 
372
371
  const content = readFileSync(skillFile, 'utf8').replace(/\r\n/g, '\n');
373
372
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
374
373
  if (!frontmatterMatch) {
375
- api.logger.warn('[moltbank] ✗ no frontmatter in SKILL.md');
374
+ api.logger.warn('[moltbank] ✗ no frontmatter in skill entrypoint');
376
375
  return;
377
376
  }
378
377
 
379
- const binsYaml = IS_WIN ? ' - mcporter' : ' - mcporter\n - jq';
380
- const newFrontmatter = `---
381
- name: ${skillName}
382
- version: 1.5.3
383
- description: MCP skill for MoltBank business banking workflows (treasury, approvals, allowances, x402, OpenRouter, Polymarket, and Pump.Fun).
384
- homepage: \${APP_BASE_URL:-https://app.moltbank.bot}
385
- metadata:
386
- category: finance
387
- api_base: \${APP_BASE_URL:-https://app.moltbank.bot}/api/mcp
388
- install_script: \${APP_BASE_URL:-https://app.moltbank.bot}/install.sh
389
- openclaw:
390
- requires:
391
- bins:
392
- ${binsYaml}
393
- npm:
394
- - '@x402/fetch@^2.3.0'
395
- - '@x402/evm@^2.3.1'
396
- - 'viem@^2.46.0'
397
- - '@polymarket/clob-client'
398
- - 'ethers@5'
399
- - '@solana/web3.js@^1.98.4'
400
- - 'bs58@^6.0.0'
401
- primaryEnv: MOLTBANK
402
- ---`;
403
-
404
- const body = content.slice(frontmatterMatch[0].length);
405
- const fixed = newFrontmatter + body;
406
-
407
- if (fixed !== content) {
408
- writeFileSync(skillFile, fixed, 'utf8');
409
- api.logger.info(`[moltbank] ✓ SKILL.md frontmatter fixed → name: ${skillName}`);
410
- } else {
411
- api.logger.info('[moltbank] ✓ SKILL.md frontmatter already correct');
412
- }
378
+ api.logger.info(`[moltbank] skill frontmatter retained as authored (${skillName})`);
413
379
  }
414
380
 
415
381
  function ensureSkillPermissions(skillDir: string, api: LoggerApi) {
@@ -418,8 +384,9 @@ function ensureSkillPermissions(skillDir: string, api: LoggerApi) {
418
384
  return;
419
385
  }
420
386
 
421
- const configDir = join(skillDir, 'config');
422
- const configFile = join(skillDir, 'config', 'mcporter.json');
387
+ const assetsDir = join(skillDir, 'assets');
388
+ const configFile = join(skillDir, 'assets', 'mcporter.json');
389
+ const referencesDir = join(skillDir, 'references');
423
390
  const scriptsDir = join(skillDir, 'scripts');
424
391
 
425
392
  const user = run('whoami', { silent: true }).stdout;
@@ -428,9 +395,9 @@ function ensureSkillPermissions(skillDir: string, api: LoggerApi) {
428
395
  api.logger.info(`[moltbank] ✓ ownership corrected to ${user}`);
429
396
  }
430
397
 
431
- if (existsSync(configDir)) {
432
- run(`chmod 777 "${configDir}"`, { silent: true });
433
- api.logger.info('[moltbank] ✓ config/ permissions → 777');
398
+ if (existsSync(assetsDir)) {
399
+ run(`chmod 777 "${assetsDir}"`, { silent: true });
400
+ api.logger.info('[moltbank] ✓ assets/ permissions → 777');
434
401
  }
435
402
  if (existsSync(configFile)) {
436
403
  run(`chmod 666 "${configFile}"`, { silent: true });
@@ -439,15 +406,20 @@ function ensureSkillPermissions(skillDir: string, api: LoggerApi) {
439
406
  if (existsSync(scriptsDir)) {
440
407
  run(`chmod -R 755 "${scriptsDir}"`, { silent: true });
441
408
  api.logger.info('[moltbank] ✓ scripts/ permissions → 755');
442
- run(`find "${scriptsDir}" -type f | xargs sed -i 's/\r$//'`, {
409
+ run(`find "${scriptsDir}" -type f -exec sed -i 's/\r$//' {} +`, {
443
410
  silent: true
444
411
  });
445
412
  api.logger.info('[moltbank] ✓ scripts/ line endings normalized (CRLF → LF)');
446
413
  }
447
414
 
448
415
  if (existsSync(skillDir)) {
449
- run(`find "${skillDir}" -maxdepth 1 -name "*.md" | xargs sed -i 's/\r$//'`, { silent: true });
450
- api.logger.info('[moltbank] ✓ .md files line endings normalized (CRLF → LF)');
416
+ run(`find "${skillDir}" -maxdepth 1 -type f -name "*.md" -exec sed -i 's/\r$//' {} +`, { silent: true });
417
+ }
418
+ if (existsSync(referencesDir)) {
419
+ run(`find "${referencesDir}" -maxdepth 1 -type f -name "*.md" -exec sed -i 's/\r$//' {} +`, { silent: true });
420
+ }
421
+ if (existsSync(skillDir) || existsSync(referencesDir)) {
422
+ api.logger.info('[moltbank] ✓ markdown line endings normalized (CRLF → LF)');
451
423
  }
452
424
  }
453
425
 
@@ -464,28 +436,12 @@ function ensureNpmDeps(skillDir: string, api: LoggerApi, mode: 'sandbox' | 'host
464
436
  dependencies: {
465
437
  '@x402/fetch': '^2.3.0',
466
438
  '@x402/evm': '^2.3.1',
467
- viem: '^2.46.0',
468
- '@polymarket/clob-client': 'latest',
469
- ethers: '^5.8.0',
470
- '@solana/web3.js': '^1.98.4',
471
- bs58: '^6.0.0'
439
+ viem: '^2.46.0'
472
440
  }
473
441
  };
474
442
  writeFileSync(pkgPath, JSON.stringify(fallbackPkg, null, 2) + '\n', 'utf8');
475
443
  api.logger.warn('[moltbank] package.json not found — created fallback package.json for skill runtime deps');
476
444
  }
477
- try {
478
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
479
- pkg.dependencies = pkg.dependencies ?? {};
480
- const cur = String(pkg.dependencies['@polymarket/clob-client'] ?? '').trim();
481
- if (!cur || cur === '^6.0.0') {
482
- pkg.dependencies['@polymarket/clob-client'] = 'latest';
483
- writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
484
- api.logger.info('[moltbank] normalized package.json: @polymarket/clob-client -> latest');
485
- }
486
- } catch (e) {
487
- api.logger.warn('[moltbank] could not normalize package.json deps: ' + String(e));
488
- }
489
445
  api.logger.info('[moltbank] installing npm deps...');
490
446
  if (mode === 'sandbox') {
491
447
  const result = run('npm install --ignore-scripts', { cwd: skillDir });
@@ -927,13 +883,13 @@ function printAuthStatus(skillDir: string, appBaseUrl: string, api: LoggerApi):
927
883
  }
928
884
 
929
885
  function ensureMcporterConfig(skillDir: string, appBaseUrl: string, api: LoggerApi) {
930
- const cfgPath = join(skillDir, 'config', 'mcporter.json');
931
- mkdirSync(join(skillDir, 'config'), { recursive: true });
886
+ const cfgPath = join(skillDir, 'assets', 'mcporter.json');
887
+ mkdirSync(join(skillDir, 'assets'), { recursive: true });
932
888
 
933
889
  const cfg = {
934
890
  mcpServers: {
935
891
  MoltBank: {
936
- description: 'MoltBank stablecoin banking MCP server powered by Fondu',
892
+ description: 'MoltBank stablecoin treasury MCP server powered by Fondu',
937
893
  transport: 'sse',
938
894
  url: `${appBaseUrl}/api/mcp`,
939
895
  headers: {
@@ -965,7 +921,7 @@ function ensureMcporterConfig(skillDir: string, appBaseUrl: string, api: LoggerA
965
921
  `--transport sse ` +
966
922
  `--header "Authorization=Bearer \${MOLTBANK}" ` +
967
923
  `--header "Content-Type=application/json" ` +
968
- `--description "MoltBank stablecoin banking MCP server powered by Fondu" ` +
924
+ `--description "MoltBank stablecoin treasury MCP server powered by Fondu" ` +
969
925
  `--scope home`;
970
926
 
971
927
  const result = run(addCmd, { silent: false });
@@ -1040,7 +996,7 @@ function injectSandboxEnv(skillDir: string, api: LoggerApi): boolean {
1040
996
  });
1041
997
  if (!initResult.ok) {
1042
998
  api.logger.warn('[moltbank] ✗ could not generate EOA signer: ' + initResult.stderr);
1043
- api.logger.warn('[moltbank] agent will generate signer on first x402/Polymarket use');
999
+ api.logger.warn('[moltbank] agent will generate signer on first x402 use');
1044
1000
  } else {
1045
1001
  const freshCreds = JSON.parse(readFileSync(credsPath, 'utf8')) as CredentialsFile;
1046
1002
  const freshOrg = freshCreds.organizations?.find((o: CredentialsOrganization) => o.name === activeOrg);
@@ -1100,7 +1056,7 @@ function configureSandbox(api: LoggerApi): boolean {
1100
1056
  'update-alternatives --set node $NODE22_BIN 2>/dev/null || true && ' +
1101
1057
  'node -v && npm -v && ' +
1102
1058
  "echo '[moltbank:sandbox] [5/7] installing npm global deps (mcporter + sdk libs)...' && " +
1103
- 'npm install -g mcporter @x402/fetch@2.3.0 @x402/evm@2.3.1 viem@2.46.0 @polymarket/clob-client ethers@5 @solana/web3.js@1.98.4 bs58@6.0.0 && ' +
1059
+ 'npm install -g mcporter @x402/fetch@2.3.0 @x402/evm@2.3.1 viem@2.46.0 && ' +
1104
1060
  "echo '[moltbank:sandbox] [6/7] verifying mcporter binary...' && " +
1105
1061
  "NPM_GLOBAL=$(npm root -g 2>/dev/null | sed 's|/node_modules$|/bin|' || true) && " +
1106
1062
  'if [ -n "$NPM_GLOBAL" ] && [ -x "$NPM_GLOBAL/mcporter" ]; then ln -sf "$NPM_GLOBAL/mcporter" /usr/local/bin/mcporter || true; fi && ' +
@@ -1216,7 +1172,7 @@ async function runSetup(cfg: MoltbankPluginConfig, api: LoggerApi, options: { au
1216
1172
  return;
1217
1173
  }
1218
1174
 
1219
- api.logger.info('[moltbank] [sandbox 2/10] ensuring SKILL.md naming...');
1175
+ api.logger.info('[moltbank] [sandbox 2/10] verifying skill entrypoint...');
1220
1176
  ensureSkillFilesUppercase(skillDir, api);
1221
1177
 
1222
1178
  api.logger.info('[moltbank] [sandbox 3/10] applying permissions (chown + chmod)...');
@@ -1243,7 +1199,7 @@ async function runSetup(cfg: MoltbankPluginConfig, api: LoggerApi, options: { au
1243
1199
  api.logger.info('[moltbank] [sandbox 5/10] installing skill npm dependencies...');
1244
1200
  ensureNpmDeps(skillDir, api, 'sandbox');
1245
1201
 
1246
- api.logger.info('[moltbank] [sandbox 6/10] normalizing SKILL.md frontmatter...');
1202
+ api.logger.info('[moltbank] [sandbox 6/10] validating skill frontmatter...');
1247
1203
  fixSkillFrontmatter(skillDir, skillName, api);
1248
1204
 
1249
1205
  api.logger.info('[moltbank] [sandbox 7/10] writing/registering mcporter config...');
@@ -1273,7 +1229,7 @@ async function runSetup(cfg: MoltbankPluginConfig, api: LoggerApi, options: { au
1273
1229
  return;
1274
1230
  }
1275
1231
 
1276
- api.logger.info('[moltbank] [host 3/8] ensuring SKILL.md naming/frontmatter...');
1232
+ api.logger.info('[moltbank] [host 3/8] verifying skill entrypoint/frontmatter...');
1277
1233
  ensureSkillFilesUppercase(skillDir, api);
1278
1234
  fixSkillFrontmatter(skillDir, skillName, api);
1279
1235
 
@@ -1328,8 +1284,8 @@ async function runSetup(cfg: MoltbankPluginConfig, api: LoggerApi, options: { au
1328
1284
  } else if (hostReady) {
1329
1285
  api.logger.info('[moltbank] ✓ host ready');
1330
1286
  }
1331
- api.logger.info(`[moltbank] skill: ${skillDir}/SKILL.md`);
1332
- api.logger.info(`[moltbank] mcporter: ${skillDir}/config/mcporter.json`);
1287
+ api.logger.info(`[moltbank] skill: ${skillDir}/skill.md`);
1288
+ api.logger.info(`[moltbank] mcporter: ${skillDir}/assets/mcporter.json`);
1333
1289
  if (sandbox) {
1334
1290
  const finalConfig = readOpenclawConfig();
1335
1291
  const finalEnv = asStringRecord(getNestedValue(finalConfig, ['agents', 'defaults', 'sandbox', 'docker', 'env']));
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "moltbank",
3
3
  "name": "MoltBank",
4
- "description": "MoltBank banking skill for OpenClaw — installs mcporter, npm dependencies, sandbox setup, and the MoltBank skill",
4
+ "description": "MoltBank stablecoin treasury plugin for OpenClaw — installs mcporter, npm dependencies, sandbox setup, and the MoltBank skill",
5
5
  "configSchema": {
6
6
  "type": "object",
7
7
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@moltbankhq/openclaw",
3
- "version": "0.1.0",
4
- "description": "MoltBank OpenClaw plugin",
3
+ "version": "0.1.2",
4
+ "description": "MoltBank stablecoin treasury OpenClaw plugin",
5
5
  "main": "index.ts",
6
6
  "openclaw": {
7
7
  "extensions": [