@moltbankhq/openclaw 0.1.3 → 0.1.4

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,20 +1,30 @@
1
1
  # @moltbankhq/openclaw
2
2
 
3
- OpenClaw plugin for [MoltBank](https://moltbank.bot) — stablecoin treasury controls, approvals, and audit trails for agent fleets.
3
+ Standalone CLI and optional OpenClaw plugin for [MoltBank](https://moltbank.bot) — stablecoin treasury controls, approvals, and audit trails for agent fleets.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- openclaw plugins install @moltbankhq/openclaw
8
+ npm install -g @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.
11
+ You can also run it without a global install:
12
+
13
+ ```bash
14
+ npx @moltbankhq/openclaw setup
15
+ ```
16
+
17
+ OpenClaw plugin mode still exists in this pass for compatibility:
18
+
19
+ ```bash
20
+ openclaw plugins install @moltbankhq/openclaw
21
+ ```
12
22
 
13
23
  ## What it does
14
24
 
15
- This plugin connects your OpenClaw agent to MoltBank's stablecoin treasury infrastructure. On install, it:
25
+ This package connects your OpenClaw agent to MoltBank's stablecoin treasury infrastructure. During setup, it:
16
26
 
17
- - Downloads and configures the MoltBank skill (SKILL.md + scripts)
27
+ - Downloads and configures the MoltBank skill (`skill.md` + scripts)
18
28
  - Installs and registers [mcporter](https://www.npmjs.com/package/mcporter) for MCP server connectivity
19
29
  - Handles OAuth device-code authentication to link your agent to your MoltBank account
20
30
  - Configures sandbox (Docker) or host mode automatically based on your OpenClaw setup
@@ -27,24 +37,33 @@ Once set up, your agent can manage treasury operations, set per-agent spending l
27
37
  After installing, run:
28
38
 
29
39
  ```bash
30
- openclaw moltbank setup
40
+ moltbank setup
31
41
  ```
32
42
 
33
- The plugin will guide you through linking your MoltBank account via browser-based OAuth. Once approved, setup finalizes automatically.
43
+ The CLI will guide you through linking your MoltBank account via browser-based OAuth. Once approved, setup finalizes automatically.
34
44
 
35
45
  ## CLI Commands
36
46
 
37
47
  | Command | Description |
38
48
  |---------|-------------|
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 |
49
+ | `moltbank setup` | Run full setup (nonblocking auth by default) |
50
+ | `moltbank setup --blocking` | Run setup and wait for OAuth approval |
51
+ | `moltbank setup-blocking` | Run setup and wait for OAuth approval |
52
+ | `moltbank auth-status` | Check current auth state |
53
+ | `moltbank sandbox-setup` | Reconfigure sandbox Docker settings |
54
+ | `moltbank inject-key` | Re-inject env vars from credentials |
55
+ | `moltbank register` | Re-register mcporter MCP server |
45
56
 
46
57
  ## Configuration
47
58
 
59
+ CLI flags:
60
+
61
+ ```bash
62
+ moltbank setup --app-base-url https://app.moltbank.bot --skill-name MoltBank
63
+ ```
64
+
65
+ Plugin mode can still read optional config from your `openclaw.json`:
66
+
48
67
  Optional config in your `openclaw.json`:
49
68
 
50
69
  ```json
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from 'child_process';
4
+ import { dirname, resolve } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const binDir = dirname(fileURLToPath(import.meta.url));
8
+ const cliPath = resolve(binDir, '../cli.ts');
9
+ const result = spawnSync(process.execPath, ['--import', 'tsx/esm', cliPath, ...process.argv.slice(2)], {
10
+ stdio: 'inherit',
11
+ env: process.env
12
+ });
13
+
14
+ if (result.error) {
15
+ console.error(`[moltbank] failed to start CLI: ${result.error.message}`);
16
+ process.exit(1);
17
+ }
18
+
19
+ process.exit(result.status ?? 1);
package/cli.ts ADDED
@@ -0,0 +1,203 @@
1
+ import { readFileSync } from 'fs';
2
+ import process from 'process';
3
+ import {
4
+ configureSandbox,
5
+ ensureMcporterConfig,
6
+ getAppBaseUrl,
7
+ getSetupAuthWaitMode,
8
+ getSkillDir,
9
+ injectSandboxEnv,
10
+ printAuthStatus,
11
+ recreateSandboxAndRestart,
12
+ runSetup
13
+ } from './index.ts';
14
+
15
+ type CliConfig = {
16
+ appBaseUrl?: string;
17
+ skillName?: string;
18
+ };
19
+
20
+ type ParsedArgs = {
21
+ command?: string;
22
+ config: CliConfig;
23
+ help: boolean;
24
+ version: boolean;
25
+ blocking: boolean;
26
+ };
27
+
28
+ const HELP_TEXT = `MoltBank CLI
29
+
30
+ Usage:
31
+ moltbank <command> [options]
32
+
33
+ Commands:
34
+ setup Run MoltBank setup (nonblocking auth by default)
35
+ setup-blocking Run MoltBank setup and wait for OAuth approval
36
+ auth-status Show current MoltBank auth state
37
+ sandbox-setup Reconfigure sandbox Docker settings in openclaw.json
38
+ inject-key Re-inject sandbox env vars from credentials.json
39
+ register Re-register mcporter MCP config
40
+
41
+ Options:
42
+ --app-base-url <url> Override MoltBank deployment URL
43
+ --skill-name <name> Override skill folder name
44
+ --blocking Alias for blocking auth mode with setup
45
+ -h, --help Show help
46
+ -v, --version Show package version
47
+
48
+ Examples:
49
+ moltbank setup
50
+ moltbank setup --blocking
51
+ moltbank auth-status
52
+ moltbank register --app-base-url https://app.moltbank.bot
53
+ `;
54
+
55
+ function readVersion(): string {
56
+ const packageUrl = new URL('./package.json', import.meta.url);
57
+ const packageJson = JSON.parse(readFileSync(packageUrl, 'utf8')) as { version?: string };
58
+ return packageJson.version ?? '0.0.0';
59
+ }
60
+
61
+ function parseArgs(argv: string[]): ParsedArgs {
62
+ const config: CliConfig = {};
63
+ const positional: string[] = [];
64
+ let help = false;
65
+ let version = false;
66
+ let blocking = false;
67
+
68
+ for (let index = 0; index < argv.length; index += 1) {
69
+ const arg = argv[index];
70
+
71
+ if (arg === '-h' || arg === '--help') {
72
+ help = true;
73
+ continue;
74
+ }
75
+
76
+ if (arg === '-v' || arg === '--version') {
77
+ version = true;
78
+ continue;
79
+ }
80
+
81
+ if (arg === '--blocking') {
82
+ blocking = true;
83
+ continue;
84
+ }
85
+
86
+ if (arg === '--app-base-url') {
87
+ const value = argv[index + 1];
88
+ if (!value) {
89
+ throw new Error('missing value for --app-base-url');
90
+ }
91
+ config.appBaseUrl = value;
92
+ index += 1;
93
+ continue;
94
+ }
95
+
96
+ if (arg.startsWith('--app-base-url=')) {
97
+ config.appBaseUrl = arg.slice('--app-base-url='.length);
98
+ continue;
99
+ }
100
+
101
+ if (arg === '--skill-name') {
102
+ const value = argv[index + 1];
103
+ if (!value) {
104
+ throw new Error('missing value for --skill-name');
105
+ }
106
+ config.skillName = value;
107
+ index += 1;
108
+ continue;
109
+ }
110
+
111
+ if (arg.startsWith('--skill-name=')) {
112
+ config.skillName = arg.slice('--skill-name='.length);
113
+ continue;
114
+ }
115
+
116
+ if (arg.startsWith('-')) {
117
+ throw new Error(`unknown option: ${arg}`);
118
+ }
119
+
120
+ positional.push(arg);
121
+ }
122
+
123
+ return {
124
+ command: positional[0],
125
+ config,
126
+ help,
127
+ version,
128
+ blocking
129
+ };
130
+ }
131
+
132
+ async function runCommand(parsed: ParsedArgs): Promise<void> {
133
+ const cfg = parsed.config;
134
+ const logger = { logger: console };
135
+
136
+ switch (parsed.command) {
137
+ case 'setup': {
138
+ const authWaitMode = parsed.blocking ? 'blocking' : getSetupAuthWaitMode('nonblocking');
139
+ await runSetup(cfg, logger, { authWaitMode });
140
+ return;
141
+ }
142
+
143
+ case 'setup-blocking': {
144
+ await runSetup(cfg, logger, { authWaitMode: 'blocking' });
145
+ return;
146
+ }
147
+
148
+ case 'auth-status': {
149
+ printAuthStatus(getSkillDir(cfg), getAppBaseUrl(cfg), logger);
150
+ return;
151
+ }
152
+
153
+ case 'sandbox-setup': {
154
+ const changed = configureSandbox(logger);
155
+ if (changed) {
156
+ recreateSandboxAndRestart(logger);
157
+ } else {
158
+ console.log('[moltbank] No sandbox docker changes — not scheduling teardown');
159
+ }
160
+ return;
161
+ }
162
+
163
+ case 'inject-key': {
164
+ const changed = injectSandboxEnv(getSkillDir(cfg), logger);
165
+ if (changed) {
166
+ recreateSandboxAndRestart(logger);
167
+ } else {
168
+ console.log('[moltbank] No env changes — not scheduling teardown');
169
+ }
170
+ return;
171
+ }
172
+
173
+ case 'register': {
174
+ ensureMcporterConfig(getSkillDir(cfg), getAppBaseUrl(cfg), logger);
175
+ return;
176
+ }
177
+
178
+ default:
179
+ throw new Error(`unknown command: ${parsed.command}`);
180
+ }
181
+ }
182
+
183
+ async function main(): Promise<void> {
184
+ const parsed = parseArgs(process.argv.slice(2));
185
+
186
+ if (parsed.version) {
187
+ console.log(readVersion());
188
+ return;
189
+ }
190
+
191
+ if (parsed.help || !parsed.command) {
192
+ console.log(HELP_TEXT);
193
+ return;
194
+ }
195
+
196
+ await runCommand(parsed);
197
+ }
198
+
199
+ main().catch((error: unknown) => {
200
+ const message = error instanceof Error ? error.message : String(error);
201
+ console.error(`[moltbank] ${message}`);
202
+ process.exit(1);
203
+ });
package/index.ts CHANGED
@@ -7,7 +7,7 @@ const IS_WIN = process.platform === 'win32';
7
7
  type OpenclawConfig = Record<string, unknown>;
8
8
  type ParsedJsonObject = Record<string, unknown>;
9
9
 
10
- interface MoltbankPluginConfig {
10
+ export interface MoltbankPluginConfig {
11
11
  skillName?: string;
12
12
  appBaseUrl?: string;
13
13
  }
@@ -62,7 +62,7 @@ interface PluginApi extends LoggerApi {
62
62
  registerCli(handler: (args: { program: CliCommandLike }) => void, options: { commands: string[] }): void;
63
63
  }
64
64
 
65
- type AuthWaitMode = 'blocking' | 'nonblocking';
65
+ export type AuthWaitMode = 'blocking' | 'nonblocking';
66
66
  const oauthPollers = new Map<string, ReturnType<typeof spawn>>();
67
67
  const backgroundFinalizers = new Map<string, ReturnType<typeof spawn>>();
68
68
 
@@ -86,7 +86,7 @@ function asStringRecord(value: unknown): Record<string, string> {
86
86
  return out;
87
87
  }
88
88
 
89
- function getSetupAuthWaitMode(defaultMode: AuthWaitMode): AuthWaitMode {
89
+ export function getSetupAuthWaitMode(defaultMode: AuthWaitMode): AuthWaitMode {
90
90
  const raw = asString(process.env.MOLTBANK_SETUP_AUTH_WAIT_MODE).trim().toLowerCase();
91
91
  if (raw === 'blocking' || raw === 'wait') return 'blocking';
92
92
  if (raw === 'nonblocking' || raw === 'nowait') return 'nonblocking';
@@ -133,15 +133,15 @@ function getWorkspace(): string {
133
133
  return process.env.OPENCLAW_WORKSPACE || join(homedir(), '.openclaw', 'workspace');
134
134
  }
135
135
 
136
- function getSkillName(cfg: MoltbankPluginConfig): string {
136
+ export function getSkillName(cfg: MoltbankPluginConfig): string {
137
137
  return cfg?.skillName || process.env.MOLTBANK_SKILL_NAME || 'MoltBank';
138
138
  }
139
139
 
140
- function getAppBaseUrl(cfg: MoltbankPluginConfig): string {
140
+ export 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 {
144
+ export function getSkillBundleBaseUrl(cfg: MoltbankPluginConfig): string {
145
145
  const base = getAppBaseUrl(cfg).replace(/\/$/, '');
146
146
  return base.endsWith('/skill') ? base : `${base}/skill`;
147
147
  }
@@ -157,7 +157,7 @@ function isSandboxEnabled(): boolean {
157
157
  }
158
158
  }
159
159
 
160
- function getSkillDir(cfg: MoltbankPluginConfig): string {
160
+ export function getSkillDir(cfg: MoltbankPluginConfig): string {
161
161
  const skillName = getSkillName(cfg);
162
162
  return join(getWorkspace(), 'skills', skillName);
163
163
  }
@@ -855,7 +855,7 @@ function ensureMoltbankAuth(skillDir: string, appBaseUrl: string, api: LoggerApi
855
855
  return true;
856
856
  }
857
857
 
858
- function printAuthStatus(skillDir: string, appBaseUrl: string, api: LoggerApi): void {
858
+ export function printAuthStatus(skillDir: string, appBaseUrl: string, api: LoggerApi): void {
859
859
  const now = Math.floor(Date.now() / 1000);
860
860
  const existing = parseActiveTokenFromCredentials();
861
861
  const pending = readPendingOauthCode(skillDir);
@@ -883,11 +883,11 @@ function printAuthStatus(skillDir: string, appBaseUrl: string, api: LoggerApi):
883
883
  api.logger.info(`[moltbank] background poll: ${pollerRunning ? 'running' : 'idle'}`);
884
884
  api.logger.info(`[moltbank] background finalize: ${finalizerRunning ? 'running' : 'idle'}`);
885
885
  if (!existing.ok && !pending) {
886
- api.logger.info('[moltbank] hint: run `openclaw moltbank setup` to start onboarding');
886
+ api.logger.info('[moltbank] hint: run `moltbank setup` to start onboarding');
887
887
  }
888
888
  }
889
889
 
890
- function ensureMcporterConfig(skillDir: string, appBaseUrl: string, api: LoggerApi) {
890
+ export function ensureMcporterConfig(skillDir: string, appBaseUrl: string, api: LoggerApi) {
891
891
  const cfgPath = join(skillDir, 'assets', 'mcporter.json');
892
892
  mkdirSync(join(skillDir, 'assets'), { recursive: true });
893
893
 
@@ -942,7 +942,7 @@ function ensureMcporterConfig(skillDir: string, appBaseUrl: string, api: LoggerA
942
942
 
943
943
  // ─── sandbox env vars ────────────────────────────────────────────────────────
944
944
 
945
- function injectSandboxEnv(skillDir: string, api: LoggerApi): boolean {
945
+ export function injectSandboxEnv(skillDir: string, api: LoggerApi): boolean {
946
946
  const credsPath = getCredentialsPath();
947
947
 
948
948
  if (!existsSync(credsPath)) {
@@ -1040,7 +1040,7 @@ function injectSandboxEnv(skillDir: string, api: LoggerApi): boolean {
1040
1040
 
1041
1041
  // ─── sandbox docker config ────────────────────────────────────────────────────
1042
1042
 
1043
- function configureSandbox(api: LoggerApi): boolean {
1043
+ export function configureSandbox(api: LoggerApi): boolean {
1044
1044
  const SETUP_CMD =
1045
1045
  'echo \'APT::Sandbox::User "root";\' > /etc/apt/apt.conf.d/99sandbox && ' +
1046
1046
  'apt-get update -qq && ' +
@@ -1112,7 +1112,7 @@ function configureSandbox(api: LoggerApi): boolean {
1112
1112
 
1113
1113
  // ─── sandbox recreate + gateway restart ──────────────────────────────────────
1114
1114
 
1115
- function recreateSandboxAndRestart(api: LoggerApi) {
1115
+ export function recreateSandboxAndRestart(api: LoggerApi) {
1116
1116
  api.logger.info('[moltbank] recreating sandbox containers...');
1117
1117
  api.logger.info('[moltbank] ⏳ waiting 8s before recreate (hot container protection)...');
1118
1118
  setTimeout(() => {
@@ -1147,7 +1147,11 @@ function recreateSandboxAndRestart(api: LoggerApi) {
1147
1147
 
1148
1148
  // ─── main setup ───────────────────────────────────────────────────────────────
1149
1149
 
1150
- async function runSetup(cfg: MoltbankPluginConfig, api: LoggerApi, options: { authWaitMode?: AuthWaitMode } = {}) {
1150
+ export async function runSetup(
1151
+ cfg: MoltbankPluginConfig,
1152
+ api: LoggerApi,
1153
+ options: { authWaitMode?: AuthWaitMode } = {}
1154
+ ) {
1151
1155
  let hostReady = false;
1152
1156
  const appBaseUrl = getAppBaseUrl(cfg);
1153
1157
  const skillBundleBaseUrl = getSkillBundleBaseUrl(cfg);
@@ -1157,7 +1161,7 @@ async function runSetup(cfg: MoltbankPluginConfig, api: LoggerApi, options: { au
1157
1161
  const waitForAuth = (options.authWaitMode ?? 'blocking') === 'blocking';
1158
1162
 
1159
1163
  api.logger.info(`[moltbank] ══════════════════════════════════════`);
1160
- api.logger.info(`[moltbank] MoltBank plugin setup starting`);
1164
+ api.logger.info(`[moltbank] MoltBank setup starting`);
1161
1165
  api.logger.info(`[moltbank] mode: ${sandbox ? 'sandbox (Docker)' : 'host (direct)'}`);
1162
1166
  api.logger.info(`[moltbank] skill dir: ${skillDir}`);
1163
1167
  api.logger.info(`[moltbank] base url: ${appBaseUrl}`);
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@moltbankhq/openclaw",
3
- "version": "0.1.3",
4
- "description": "MoltBank stablecoin treasury OpenClaw plugin",
3
+ "version": "0.1.4",
4
+ "description": "MoltBank stablecoin treasury CLI and OpenClaw plugin",
5
5
  "main": "index.ts",
6
+ "bin": {
7
+ "moltbank": "./bin/moltbank.mjs"
8
+ },
6
9
  "openclaw": {
7
10
  "extensions": [
8
11
  "./index.ts"
@@ -12,6 +15,9 @@
12
15
  "author": "",
13
16
  "license": "ISC",
14
17
  "type": "module",
18
+ "dependencies": {
19
+ "tsx": "^4.20.6"
20
+ },
15
21
  "devDependencies": {
16
22
  "@types/node": "^25.5.0"
17
23
  }