@moltbankhq/openclaw 0.1.1 → 0.1.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @moltbankhq/openclaw
2
2
 
3
- OpenClaw plugin for [MoltBank](https://moltbank.bot) — spend control, approvals, and audit trails for agent fleets.
3
+ OpenClaw plugin for [MoltBank](https://moltbank.bot) — stablecoin treasury controls, approvals, and audit trails for agent fleets.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,9 +8,11 @@ OpenClaw plugin for [MoltBank](https://moltbank.bot) — spend control, approval
8
8
  openclaw plugins install @moltbankhq/openclaw
9
9
  ```
10
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
+
11
13
  ## What it does
12
14
 
13
- This plugin connects your OpenClaw agent to MoltBank's banking infrastructure. On install, it:
15
+ This plugin connects your OpenClaw agent to MoltBank's stablecoin treasury infrastructure. On install, it:
14
16
 
15
17
  - Downloads and configures the MoltBank skill (SKILL.md + scripts)
16
18
  - Installs and registers [mcporter](https://www.npmjs.com/package/mcporter) for MCP server connectivity
@@ -78,8 +80,6 @@ The MoltBank skill gives your agent access to:
78
80
  - **Approval workflows** — human-in-the-loop for spend above thresholds
79
81
  - **x402 payments** — agent-to-agent payments via the x402 protocol
80
82
  - **Audit trails** — full visibility into which agent spent what, when, and why
81
- - **Polymarket** — prediction market operations
82
- - **Pump.Fun** — Solana token operations
83
83
 
84
84
  ## Requirements
85
85
 
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';
@@ -141,6 +141,11 @@ function getAppBaseUrl(cfg: MoltbankPluginConfig): string {
141
141
  return (cfg?.appBaseUrl || process.env.APP_BASE_URL || 'https://app.moltbank.bot').trim();
142
142
  }
143
143
 
144
+ function getSkillBundleBaseUrl(cfg: MoltbankPluginConfig): string {
145
+ const base = getAppBaseUrl(cfg).replace(/\/$/, '');
146
+ return base.endsWith('/skill') ? base : `${base}/skill`;
147
+ }
148
+
144
149
  function isSandboxEnabled(): boolean {
145
150
  try {
146
151
  const configPath = join(homedir(), '.openclaw', 'openclaw.json');
@@ -276,43 +281,34 @@ function ensureWrapperExecutable(skillDir: string, api: LoggerApi): void {
276
281
  // ─── skill install ───────────────────────────────────────────────────────────
277
282
 
278
283
  const SKILL_FILES = [
284
+ 'README.md',
279
285
  '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
286
  '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',
287
+ 'install.sh',
288
+ 'assets/mcporter.json',
289
+ 'references/heartbeat.md',
290
+ 'references/onboarding.md',
291
+ 'references/openclaw-signer-eoa.md',
292
+ 'references/rules.md',
293
+ 'references/setup.md',
294
+ 'references/tools-reference.md',
295
+ 'references/x402-workflow.md',
295
296
  'scripts/openclaw-runtime-config.mjs',
296
297
  'scripts/request-oauth-device-code.mjs',
297
298
  'scripts/init-openclaw-signer.mjs',
298
- 'scripts/init-openclaw-solana-signer.mjs',
299
- 'scripts/bootstrap-openclaw-pumpfun-wallet.mjs',
300
299
  '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
300
  'scripts/poll-oauth-token.mjs',
307
301
  'scripts/export-api-key.mjs',
308
- 'scripts/fetch-openrouter-intent.mjs',
309
302
  'scripts/x402-pay-and-confirm.mjs',
310
- 'scripts/pumpportal-trade-local.mjs',
303
+ 'scripts/setup-sandbox.sh',
311
304
  'scripts/moltbank.sh',
312
- 'scripts/moltbank.ps1',
313
- 'scripts/polymarket-service.mjs'
305
+ 'scripts/moltbank.ps1'
314
306
  ];
315
307
 
308
+ function hasRequiredSkillFiles(skillDir: string): boolean {
309
+ return SKILL_FILES.every((file) => existsSync(join(skillDir, file)));
310
+ }
311
+
316
312
  function ensureSkillInstalled(
317
313
  skillDir: string,
318
314
  appBaseUrl: string,
@@ -321,12 +317,21 @@ function ensureSkillInstalled(
321
317
  mode: 'sandbox' | 'host' = 'sandbox'
322
318
  ) {
323
319
  const successFlag = join(skillDir, '.install_success');
324
- if (existsSync(successFlag)) {
320
+ if (existsSync(successFlag) && hasRequiredSkillFiles(skillDir)) {
325
321
  ensureWrapperExecutable(skillDir, api);
326
322
  api.logger.info('[moltbank] ✓ skill already installed at ' + skillDir);
327
323
  return true;
328
324
  }
329
325
 
326
+ if (existsSync(successFlag)) {
327
+ api.logger.warn('[moltbank] existing skill install is stale or incomplete — reinstalling current bundle');
328
+ try {
329
+ unlinkSync(successFlag);
330
+ } catch {
331
+ // ignore
332
+ }
333
+ }
334
+
330
335
  api.logger.info(`[moltbank] installing skill '${skillName}' to ${skillDir} (mode: ${mode})`);
331
336
  mkdirSync(skillDir, { recursive: true });
332
337
 
@@ -350,66 +355,32 @@ function ensureSkillInstalled(
350
355
  function ensureSkillFilesUppercase(skillDir: string, api: LoggerApi) {
351
356
  const lower = join(skillDir, 'skill.md');
352
357
  const upper = join(skillDir, 'SKILL.md');
353
- if (existsSync(upper)) {
354
- api.logger.info('[moltbank] ✓ SKILL.md already exists');
358
+ if (existsSync(lower)) {
359
+ api.logger.info('[moltbank] ✓ skill entrypoint present (skill.md)');
355
360
  return;
356
361
  }
357
- if (existsSync(lower)) {
358
- renameSync(lower, upper);
359
- api.logger.info('[moltbank] ✓ renamed skill.md → SKILL.md');
362
+ if (existsSync(upper)) {
363
+ api.logger.info('[moltbank] ✓ skill entrypoint present (SKILL.md)');
360
364
  } else {
361
365
  api.logger.warn('[moltbank] ✗ neither skill.md nor SKILL.md found');
362
366
  }
363
367
  }
364
368
 
365
369
  function fixSkillFrontmatter(skillDir: string, skillName: string, api: LoggerApi) {
366
- const skillFile = join(skillDir, 'SKILL.md');
370
+ const skillFile = existsSync(join(skillDir, 'skill.md')) ? join(skillDir, 'skill.md') : join(skillDir, 'SKILL.md');
367
371
  if (!existsSync(skillFile)) {
368
- api.logger.warn('[moltbank] ✗ SKILL.md not found — skipping frontmatter fix');
372
+ api.logger.warn('[moltbank] ✗ skill entrypoint not found — skipping frontmatter validation');
369
373
  return;
370
374
  }
371
375
 
372
376
  const content = readFileSync(skillFile, 'utf8').replace(/\r\n/g, '\n');
373
377
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
374
378
  if (!frontmatterMatch) {
375
- api.logger.warn('[moltbank] ✗ no frontmatter in SKILL.md');
379
+ api.logger.warn('[moltbank] ✗ no frontmatter in skill entrypoint');
376
380
  return;
377
381
  }
378
382
 
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
- }
383
+ api.logger.info(`[moltbank] skill frontmatter retained as authored (${skillName})`);
413
384
  }
414
385
 
415
386
  function ensureSkillPermissions(skillDir: string, api: LoggerApi) {
@@ -418,8 +389,9 @@ function ensureSkillPermissions(skillDir: string, api: LoggerApi) {
418
389
  return;
419
390
  }
420
391
 
421
- const configDir = join(skillDir, 'config');
422
- const configFile = join(skillDir, 'config', 'mcporter.json');
392
+ const assetsDir = join(skillDir, 'assets');
393
+ const configFile = join(skillDir, 'assets', 'mcporter.json');
394
+ const referencesDir = join(skillDir, 'references');
423
395
  const scriptsDir = join(skillDir, 'scripts');
424
396
 
425
397
  const user = run('whoami', { silent: true }).stdout;
@@ -428,9 +400,9 @@ function ensureSkillPermissions(skillDir: string, api: LoggerApi) {
428
400
  api.logger.info(`[moltbank] ✓ ownership corrected to ${user}`);
429
401
  }
430
402
 
431
- if (existsSync(configDir)) {
432
- run(`chmod 777 "${configDir}"`, { silent: true });
433
- api.logger.info('[moltbank] ✓ config/ permissions → 777');
403
+ if (existsSync(assetsDir)) {
404
+ run(`chmod 777 "${assetsDir}"`, { silent: true });
405
+ api.logger.info('[moltbank] ✓ assets/ permissions → 777');
434
406
  }
435
407
  if (existsSync(configFile)) {
436
408
  run(`chmod 666 "${configFile}"`, { silent: true });
@@ -439,15 +411,20 @@ function ensureSkillPermissions(skillDir: string, api: LoggerApi) {
439
411
  if (existsSync(scriptsDir)) {
440
412
  run(`chmod -R 755 "${scriptsDir}"`, { silent: true });
441
413
  api.logger.info('[moltbank] ✓ scripts/ permissions → 755');
442
- run(`find "${scriptsDir}" -type f | xargs sed -i 's/\r$//'`, {
414
+ run(`find "${scriptsDir}" -type f -exec sed -i 's/\r$//' {} +`, {
443
415
  silent: true
444
416
  });
445
417
  api.logger.info('[moltbank] ✓ scripts/ line endings normalized (CRLF → LF)');
446
418
  }
447
419
 
448
420
  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)');
421
+ run(`find "${skillDir}" -maxdepth 1 -type f -name "*.md" -exec sed -i 's/\r$//' {} +`, { silent: true });
422
+ }
423
+ if (existsSync(referencesDir)) {
424
+ run(`find "${referencesDir}" -maxdepth 1 -type f -name "*.md" -exec sed -i 's/\r$//' {} +`, { silent: true });
425
+ }
426
+ if (existsSync(skillDir) || existsSync(referencesDir)) {
427
+ api.logger.info('[moltbank] ✓ markdown line endings normalized (CRLF → LF)');
451
428
  }
452
429
  }
453
430
 
@@ -464,28 +441,12 @@ function ensureNpmDeps(skillDir: string, api: LoggerApi, mode: 'sandbox' | 'host
464
441
  dependencies: {
465
442
  '@x402/fetch': '^2.3.0',
466
443
  '@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'
444
+ viem: '^2.46.0'
472
445
  }
473
446
  };
474
447
  writeFileSync(pkgPath, JSON.stringify(fallbackPkg, null, 2) + '\n', 'utf8');
475
448
  api.logger.warn('[moltbank] package.json not found — created fallback package.json for skill runtime deps');
476
449
  }
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
450
  api.logger.info('[moltbank] installing npm deps...');
490
451
  if (mode === 'sandbox') {
491
452
  const result = run('npm install --ignore-scripts', { cwd: skillDir });
@@ -927,13 +888,13 @@ function printAuthStatus(skillDir: string, appBaseUrl: string, api: LoggerApi):
927
888
  }
928
889
 
929
890
  function ensureMcporterConfig(skillDir: string, appBaseUrl: string, api: LoggerApi) {
930
- const cfgPath = join(skillDir, 'config', 'mcporter.json');
931
- mkdirSync(join(skillDir, 'config'), { recursive: true });
891
+ const cfgPath = join(skillDir, 'assets', 'mcporter.json');
892
+ mkdirSync(join(skillDir, 'assets'), { recursive: true });
932
893
 
933
894
  const cfg = {
934
895
  mcpServers: {
935
896
  MoltBank: {
936
- description: 'MoltBank stablecoin banking MCP server powered by Fondu',
897
+ description: 'MoltBank stablecoin treasury MCP server powered by Fondu',
937
898
  transport: 'sse',
938
899
  url: `${appBaseUrl}/api/mcp`,
939
900
  headers: {
@@ -965,7 +926,7 @@ function ensureMcporterConfig(skillDir: string, appBaseUrl: string, api: LoggerA
965
926
  `--transport sse ` +
966
927
  `--header "Authorization=Bearer \${MOLTBANK}" ` +
967
928
  `--header "Content-Type=application/json" ` +
968
- `--description "MoltBank stablecoin banking MCP server powered by Fondu" ` +
929
+ `--description "MoltBank stablecoin treasury MCP server powered by Fondu" ` +
969
930
  `--scope home`;
970
931
 
971
932
  const result = run(addCmd, { silent: false });
@@ -1040,7 +1001,7 @@ function injectSandboxEnv(skillDir: string, api: LoggerApi): boolean {
1040
1001
  });
1041
1002
  if (!initResult.ok) {
1042
1003
  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');
1004
+ api.logger.warn('[moltbank] agent will generate signer on first x402 use');
1044
1005
  } else {
1045
1006
  const freshCreds = JSON.parse(readFileSync(credsPath, 'utf8')) as CredentialsFile;
1046
1007
  const freshOrg = freshCreds.organizations?.find((o: CredentialsOrganization) => o.name === activeOrg);
@@ -1100,7 +1061,7 @@ function configureSandbox(api: LoggerApi): boolean {
1100
1061
  'update-alternatives --set node $NODE22_BIN 2>/dev/null || true && ' +
1101
1062
  'node -v && npm -v && ' +
1102
1063
  "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 && ' +
1064
+ 'npm install -g mcporter @x402/fetch@2.3.0 @x402/evm@2.3.1 viem@2.46.0 && ' +
1104
1065
  "echo '[moltbank:sandbox] [6/7] verifying mcporter binary...' && " +
1105
1066
  "NPM_GLOBAL=$(npm root -g 2>/dev/null | sed 's|/node_modules$|/bin|' || true) && " +
1106
1067
  'if [ -n "$NPM_GLOBAL" ] && [ -x "$NPM_GLOBAL/mcporter" ]; then ln -sf "$NPM_GLOBAL/mcporter" /usr/local/bin/mcporter || true; fi && ' +
@@ -1189,6 +1150,7 @@ function recreateSandboxAndRestart(api: LoggerApi) {
1189
1150
  async function runSetup(cfg: MoltbankPluginConfig, api: LoggerApi, options: { authWaitMode?: AuthWaitMode } = {}) {
1190
1151
  let hostReady = false;
1191
1152
  const appBaseUrl = getAppBaseUrl(cfg);
1153
+ const skillBundleBaseUrl = getSkillBundleBaseUrl(cfg);
1192
1154
  const skillName = getSkillName(cfg);
1193
1155
  const sandbox = isSandboxEnabled();
1194
1156
  const skillDir = getSkillDir(cfg);
@@ -1210,13 +1172,13 @@ async function runSetup(cfg: MoltbankPluginConfig, api: LoggerApi, options: { au
1210
1172
  ensureMcporter(api);
1211
1173
 
1212
1174
  api.logger.info('[moltbank] [sandbox 1/10] installing skill files...');
1213
- const skillInstalled = ensureSkillInstalled(skillDir, appBaseUrl, skillName, api, 'sandbox');
1175
+ const skillInstalled = ensureSkillInstalled(skillDir, skillBundleBaseUrl, skillName, api, 'sandbox');
1214
1176
  if (!skillInstalled) {
1215
1177
  api.logger.warn('[moltbank] skill install failed — aborting sandbox setup');
1216
1178
  return;
1217
1179
  }
1218
1180
 
1219
- api.logger.info('[moltbank] [sandbox 2/10] ensuring SKILL.md naming...');
1181
+ api.logger.info('[moltbank] [sandbox 2/10] verifying skill entrypoint...');
1220
1182
  ensureSkillFilesUppercase(skillDir, api);
1221
1183
 
1222
1184
  api.logger.info('[moltbank] [sandbox 3/10] applying permissions (chown + chmod)...');
@@ -1243,7 +1205,7 @@ async function runSetup(cfg: MoltbankPluginConfig, api: LoggerApi, options: { au
1243
1205
  api.logger.info('[moltbank] [sandbox 5/10] installing skill npm dependencies...');
1244
1206
  ensureNpmDeps(skillDir, api, 'sandbox');
1245
1207
 
1246
- api.logger.info('[moltbank] [sandbox 6/10] normalizing SKILL.md frontmatter...');
1208
+ api.logger.info('[moltbank] [sandbox 6/10] validating skill frontmatter...');
1247
1209
  fixSkillFrontmatter(skillDir, skillName, api);
1248
1210
 
1249
1211
  api.logger.info('[moltbank] [sandbox 7/10] writing/registering mcporter config...');
@@ -1267,13 +1229,13 @@ async function runSetup(cfg: MoltbankPluginConfig, api: LoggerApi, options: { au
1267
1229
  ensureMcporter(api);
1268
1230
 
1269
1231
  api.logger.info('[moltbank] [host 2/8] installing skill files...');
1270
- const installed = ensureSkillInstalled(skillDir, appBaseUrl, skillName, api, 'host');
1232
+ const installed = ensureSkillInstalled(skillDir, skillBundleBaseUrl, skillName, api, 'host');
1271
1233
  if (!installed) {
1272
1234
  api.logger.warn('[moltbank] host setup aborted: skill install failed. Verify install.sh/base URL and retry.');
1273
1235
  return;
1274
1236
  }
1275
1237
 
1276
- api.logger.info('[moltbank] [host 3/8] ensuring SKILL.md naming/frontmatter...');
1238
+ api.logger.info('[moltbank] [host 3/8] verifying skill entrypoint/frontmatter...');
1277
1239
  ensureSkillFilesUppercase(skillDir, api);
1278
1240
  fixSkillFrontmatter(skillDir, skillName, api);
1279
1241
 
@@ -1328,8 +1290,8 @@ async function runSetup(cfg: MoltbankPluginConfig, api: LoggerApi, options: { au
1328
1290
  } else if (hostReady) {
1329
1291
  api.logger.info('[moltbank] ✓ host ready');
1330
1292
  }
1331
- api.logger.info(`[moltbank] skill: ${skillDir}/SKILL.md`);
1332
- api.logger.info(`[moltbank] mcporter: ${skillDir}/config/mcporter.json`);
1293
+ api.logger.info(`[moltbank] skill: ${skillDir}/skill.md`);
1294
+ api.logger.info(`[moltbank] mcporter: ${skillDir}/assets/mcporter.json`);
1333
1295
  if (sandbox) {
1334
1296
  const finalConfig = readOpenclawConfig();
1335
1297
  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.1",
4
- "description": "MoltBank OpenClaw plugin",
3
+ "version": "0.1.3",
4
+ "description": "MoltBank stablecoin treasury OpenClaw plugin",
5
5
  "main": "index.ts",
6
6
  "openclaw": {
7
7
  "extensions": [